WIP: bootstrap and partial real Solana watcher implementation

This commit is contained in:
2026-08-16 09:17:45 +00:00
commit dc23412c3f
7232 changed files with 1687637 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
import arrayLikeToArray from "./arrayLikeToArray.js";
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? arrayLikeToArray(r, a) : void 0;
}
}
export { _unsupportedIterableToArray as default };

View File

@@ -0,0 +1,35 @@
{
"for + if": {
"name": "for + if",
"browser": "IE 9.0.0 (Windows 7 0.0.0)",
"suite": "itar-short",
"hz": 240965.2696072598,
"success": true,
"fastest": false,
"rme": 0.013595390361967868,
"rhz": 0.8402592586317352,
"sampleSize": 209
},
"while + if": {
"name": "while + if",
"browser": "IE 9.0.0 (Windows 7 0.0.0)",
"suite": "itar-short",
"hz": 230187.22948896923,
"success": true,
"fastest": false,
"rme": 0.01328665941718909,
"rhz": 0.8026756350080547,
"sampleSize": 217
},
"array join": {
"name": "array join",
"browser": "IE 9.0.0 (Windows 7 0.0.0)",
"suite": "itar-short",
"hz": 286774.90564000903,
"success": true,
"fastest": true,
"rme": 0.015580324289100318,
"rhz": 1,
"sampleSize": 198
}
}

View File

@@ -0,0 +1,364 @@
/**
* @fileoverview Rule to flag non-matching identifiers
* @author Matthieu Larcher
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../types').Rule.RuleModule} */
module.exports = {
meta: {
type: "suggestion",
defaultOptions: [
"^.+$",
{
classFields: false,
ignoreDestructuring: false,
onlyDeclarations: false,
properties: false,
},
],
docs: {
description:
"Require identifiers to match a specified regular expression",
recommended: false,
frozen: true,
url: "https://eslint.org/docs/latest/rules/id-match",
},
schema: [
{
type: "string",
},
{
type: "object",
properties: {
properties: {
type: "boolean",
},
classFields: {
type: "boolean",
},
onlyDeclarations: {
type: "boolean",
},
ignoreDestructuring: {
type: "boolean",
},
},
additionalProperties: false,
},
],
messages: {
notMatch:
"Identifier '{{name}}' does not match the pattern '{{pattern}}'.",
notMatchPrivate:
"Identifier '#{{name}}' does not match the pattern '{{pattern}}'.",
},
},
create(context) {
//--------------------------------------------------------------------------
// Options
//--------------------------------------------------------------------------
const [
pattern,
{
classFields: checkClassFields,
ignoreDestructuring,
onlyDeclarations,
properties: checkProperties,
},
] = context.options;
const regexp = new RegExp(pattern, "u");
const sourceCode = context.sourceCode;
let globalScope;
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
// contains reported nodes to avoid reporting twice on destructuring with shorthand notation
const reportedNodes = new Set();
const ALLOWED_PARENT_TYPES = new Set([
"CallExpression",
"NewExpression",
]);
const DECLARATION_TYPES = new Set([
"FunctionDeclaration",
"VariableDeclarator",
]);
const IMPORT_TYPES = new Set([
"ImportSpecifier",
"ImportNamespaceSpecifier",
"ImportDefaultSpecifier",
]);
/**
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a reference to a global variable.
*/
function isReferenceToGlobalVariable(node) {
const variable = globalScope.set.get(node.name);
return (
variable &&
variable.defs.length === 0 &&
variable.references.some(ref => ref.identifier === node)
);
}
/**
* Checks if a string matches the provided pattern
* @param {string} name The string to check.
* @returns {boolean} if the string is a match
* @private
*/
function isInvalid(name) {
return !regexp.test(name);
}
/**
* Checks if a parent of a node is an ObjectPattern.
* @param {ASTNode} node The node to check.
* @returns {boolean} if the node is inside an ObjectPattern
* @private
*/
function isInsideObjectPattern(node) {
let { parent } = node;
while (parent) {
if (parent.type === "ObjectPattern") {
return true;
}
parent = parent.parent;
}
return false;
}
/**
* Verifies if we should report an error or not based on the effective
* parent node and the identifier name.
* @param {ASTNode} effectiveParent The effective parent node of the node to be reported
* @param {string} name The identifier name of the identifier node
* @returns {boolean} whether an error should be reported or not
*/
function shouldReport(effectiveParent, name) {
return (
(!onlyDeclarations ||
DECLARATION_TYPES.has(effectiveParent.type)) &&
!ALLOWED_PARENT_TYPES.has(effectiveParent.type) &&
isInvalid(name)
);
}
/**
* Reports an AST node as a rule violation.
* @param {ASTNode} node The node to report.
* @returns {void}
* @private
*/
function report(node) {
/*
* We used the range instead of the node because it's possible
* for the same identifier to be represented by two different
* nodes, with the most clear example being shorthand properties:
* { foo }
* In this case, "foo" is represented by one node for the name
* and one for the value. The only way to know they are the same
* is to look at the range.
*/
if (!reportedNodes.has(node.range.toString())) {
const messageId =
node.type === "PrivateIdentifier"
? "notMatchPrivate"
: "notMatch";
context.report({
node,
messageId,
data: {
name: node.name,
pattern,
},
});
reportedNodes.add(node.range.toString());
}
}
return {
Program(node) {
globalScope = sourceCode.getScope(node);
},
Identifier(node) {
const name = node.name,
parent = node.parent,
effectiveParent =
parent.type === "MemberExpression"
? parent.parent
: parent;
if (
isReferenceToGlobalVariable(node) ||
astUtils.isImportAttributeKey(node) ||
parent.type === "MetaProperty"
) {
return;
}
if (parent.type === "MemberExpression") {
if (!checkProperties) {
return;
}
// Always check object names
if (
parent.object.type === "Identifier" &&
parent.object.name === name
) {
if (isInvalid(name)) {
report(node);
}
// Report AssignmentExpressions left side's assigned variable id
} else if (
effectiveParent.type === "AssignmentExpression" &&
effectiveParent.left.type === "MemberExpression" &&
effectiveParent.left.property.name === node.name
) {
if (isInvalid(name)) {
report(node);
}
// Report AssignmentExpressions only if they are the left side of the assignment
} else if (
effectiveParent.type === "AssignmentExpression" &&
effectiveParent.right.type !== "MemberExpression"
) {
if (isInvalid(name)) {
report(node);
}
}
// For https://github.com/eslint/eslint/issues/15123
} else if (
parent.type === "Property" &&
parent.parent.type === "ObjectExpression" &&
parent.key === node &&
!parent.computed
) {
if (checkProperties && isInvalid(name)) {
report(node);
}
/*
* Properties have their own rules, and
* AssignmentPattern nodes can be treated like Properties:
* e.g.: const { no_camelcased = false } = bar;
*/
} else if (
parent.type === "Property" ||
parent.type === "AssignmentPattern"
) {
if (
parent.parent &&
parent.parent.type === "ObjectPattern"
) {
if (
!ignoreDestructuring &&
parent.shorthand &&
parent.value.left &&
isInvalid(name)
) {
report(node);
}
const assignmentKeyEqualsValue =
parent.key.name === parent.value.name;
// prevent checking righthand side of destructured object
if (!assignmentKeyEqualsValue && parent.key === node) {
return;
}
const valueIsInvalid =
parent.value.name && isInvalid(name);
// ignore destructuring if the option is set, unless a new identifier is created
if (
valueIsInvalid &&
!(assignmentKeyEqualsValue && ignoreDestructuring)
) {
report(node);
}
}
// never check properties or always ignore destructuring
if (
(!checkProperties && !parent.computed) ||
(ignoreDestructuring && isInsideObjectPattern(node))
) {
return;
}
// don't check right hand side of AssignmentExpression to prevent duplicate warnings
if (
parent.right !== node &&
shouldReport(effectiveParent, name)
) {
report(node);
}
// Check if it's an import specifier
} else if (IMPORT_TYPES.has(parent.type)) {
// Report only if the local imported identifier is invalid
if (
parent.local &&
parent.local.name === node.name &&
isInvalid(name)
) {
report(node);
}
} else if (parent.type === "PropertyDefinition") {
if (checkClassFields && isInvalid(name)) {
report(node);
}
// Report anything that is invalid that isn't a CallExpression
} else if (shouldReport(effectiveParent, name)) {
report(node);
}
},
PrivateIdentifier(node) {
const isClassField = node.parent.type === "PropertyDefinition";
if (isClassField && !checkClassFields) {
return;
}
if (isInvalid(node.name)) {
report(node);
}
},
};
},
};

View File

@@ -0,0 +1,4 @@
function _classStaticPrivateMethodSet() {
throw new TypeError("attempted to set read only static private field");
}
export { _classStaticPrivateMethodSet as default };

View File

@@ -0,0 +1,2 @@
declare function md5(bytes: Uint8Array): Uint8Array;
export default md5;

View File

@@ -0,0 +1,22 @@
'use strict';
const none = Symbol.for('object-stream.none');
const finalSymbol = Symbol.for('object-stream.final');
const manySymbol = Symbol.for('object-stream.many');
const final = value => ({[finalSymbol]: value});
const many = values => ({[manySymbol]: values});
const isFinal = o => o && typeof o == 'object' && finalSymbol in o;
const isMany = o => o && typeof o == 'object' && manySymbol in o;
const getFinalValue = o => o[finalSymbol];
const getManyValues = o => o[manySymbol];
module.exports.none = none;
module.exports.final = final;
module.exports.isFinal = isFinal;
module.exports.getFinalValue = getFinalValue;
module.exports.many = many;
module.exports.isMany = isMany;
module.exports.getManyValues = getManyValues;

View File

@@ -0,0 +1,6 @@
function _object_destructuring_empty(o) {
if (o === null || o === void 0) throw new TypeError("Cannot destructure " + o);
return o;
}
export { _object_destructuring_empty as _ };

View File

@@ -0,0 +1 @@
{"version":3,"file":"_u64.d.ts","sourceRoot":"","sources":["src/_u64.ts"],"names":[],"mappings":"AAQA,iBAAS,OAAO,CACd,CAAC,EAAE,MAAM,EACT,EAAE,UAAQ,GACT;IACD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAGA;AAED,iBAAS,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,UAAQ,GAAG,WAAW,EAAE,CASvD;AAED,QAAA,MAAM,KAAK,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAqD,CAAC;AAE5F,QAAA,MAAM,KAAK,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,GAAG,MAAM,KAAG,MAAiB,CAAC;AACpE,QAAA,MAAM,KAAK,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAqC,CAAC;AAEvF,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAqC,CAAC;AACxF,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAqC,CAAC;AAExF,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAA4C,CAAC;AAC/F,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAA4C,CAAC;AAE/F,QAAA,MAAM,OAAO,GAAI,IAAI,MAAM,EAAE,GAAG,MAAM,KAAG,MAAW,CAAC;AACrD,QAAA,MAAM,OAAO,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,KAAG,MAAW,CAAC;AAErD,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAqC,CAAC;AACxF,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAqC,CAAC;AAExF,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAA4C,CAAC;AAC/F,QAAA,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAA4C,CAAC;AAI/F,iBAAS,GAAG,CACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT;IACD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAGA;AAED,QAAA,MAAM,KAAK,GAAI,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,KAAG,MAA8C,CAAC;AACnG,QAAA,MAAM,KAAK,GAAI,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,KAAG,MACrB,CAAC;AAC7C,QAAA,MAAM,KAAK,GAAI,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,KAAG,MACb,CAAC;AACpD,QAAA,MAAM,KAAK,GAAI,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,KAAG,MAC5B,CAAC;AAClD,QAAA,MAAM,KAAK,GAAI,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,KAAG,MACZ,CAAC;AACjE,QAAA,MAAM,KAAK,GAAI,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,KAAG,MACnC,CAAC;AAGvD,OAAO,EACL,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EACrK,CAAC;AAEF,QAAA,MAAM,GAAG,EAAE;IAAE,OAAO,EAAE,OAAO,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;CAOrpC,CAAC;AACF,eAAe,GAAG,CAAC"}

View File

@@ -0,0 +1,23 @@
/**
* Safer version of `Function` which should not be called.
* Every function should be assignable to this, but this should not be assignable to every function.
*/
export type AnyFunction = (...args: never[]) => void;
/**
* Type of objects whose values are all of the same type.
* The `in` and `for-in` operators can *not* be safely used,
* since `Object.prototype` may be modified by outside code.
*/
export interface MapLike<T> {
[index: string]: T;
}
/**
* Indicates whether a map-like contains an own property with the specified key.
*
* @param map A map-like.
* @param key A property key.
*/
export declare function hasProperty(map: MapLike<any>, key: string): boolean;
export declare function assertNever(member: never, message?: string, stackCrawlMark?: AnyFunction): never;
export declare function fail(message?: string, stackCrawlMark?: AnyFunction): never;
//# sourceMappingURL=utils.d.ts.map

View File

@@ -0,0 +1,34 @@
import { expectType, expectAssignable } from 'tsd'
import type { SonicBoom } from 'sonic-boom'
import pino, {
destination,
type LevelMapping,
levels,
type Logger,
multistream,
type MultiStreamRes,
type SerializedError,
stdSerializers,
stdTimeFunctions,
symbols,
transport,
version,
} from '../../pino'
expectType<SonicBoom>(destination(''))
expectType<LevelMapping>(levels)
expectType<MultiStreamRes>(multistream(process.stdout))
expectType<SerializedError>(stdSerializers.err({} as any))
expectType<string>(stdTimeFunctions.isoTime())
expectType<string>(stdTimeFunctions.isoTimeNano())
expectType<string>(version)
// Can't test against `unique symbol`, see https://github.com/SamVerschueren/tsd/issues/49
expectAssignable<Symbol>(symbols.endSym)
// TODO: currently returns (aliased) `any`, waiting for strong typed `thread-stream`
transport({
target: '#pino/pretty',
options: { some: 'options for', the: 'transport' }
})

View File

@@ -0,0 +1 @@
{"version":3,"file":"offset-codec.d.ts","sourceRoot":"","sources":["../../src/offset-codec.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAgC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAExF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,KAAK,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAE/B,KAAK,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAE/B,KAAK,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,KAAK,YAAY,GAAG;IAChB,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,SAAS,CAAC,EAAE,iBAAiB,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,KAAK,sBAAsB,GAAG;IAC1B,6BAA6B;IAC7B,KAAK,EAAE,kBAAkB,GAAG,UAAU,CAAC;IACvC,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,KAAK,iBAAiB,GAAG,CAAC,KAAK,EAAE,sBAAsB,KAAK,MAAM,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,KAAK,kBAAkB,GAAG,CACtB,KAAK,EAAE,sBAAsB,GAAG;IAC5B,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;CACtB,KACA,MAAM,CAAC;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,aAAa,CAAC,QAAQ,SAAS,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,QAAQ,CAe5G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,aAAa,CAAC,QAAQ,SAAS,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,QAAQ,CAe5G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,wBAAgB,WAAW,CAAC,MAAM,SAAS,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAEhG"}

View File

@@ -0,0 +1,914 @@
/**
* Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y².
* For design rationale of types / exports, see weierstrass module documentation.
* Untwisted Edwards curves exist, but they aren't used in real-world protocols.
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import {
_validateObject,
_abool2 as abool,
_abytes2 as abytes,
aInRange,
bytesToHex,
bytesToNumberLE,
concatBytes,
copyBytes,
ensureBytes,
isBytes,
memoized,
notImplemented,
randomBytes as randomBytesWeb,
type FHash,
type Hex,
} from '../utils.ts';
import {
_createCurveFields,
normalizeZ,
pippenger,
wNAF,
type AffinePoint,
type BasicCurve,
type CurveLengths,
type CurvePoint,
type CurvePointCons,
} from './curve.ts';
import { Field, type IField, type NLength } from './modular.ts';
// Be friendly to bad ECMAScript parsers by not using bigint literals
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
export type UVRatio = (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
/** Instance of Extended Point with coordinates in X, Y, Z, T. */
export interface EdwardsPoint extends CurvePoint<bigint, EdwardsPoint> {
/** extended X coordinate. Different from affine x. */
readonly X: bigint;
/** extended Y coordinate. Different from affine y. */
readonly Y: bigint;
/** extended Z coordinate */
readonly Z: bigint;
/** extended T coordinate */
readonly T: bigint;
/** @deprecated use `toBytes` */
toRawBytes(): Uint8Array;
/** @deprecated use `p.precompute(windowSize)` */
_setWindowSize(windowSize: number): void;
/** @deprecated use .X */
readonly ex: bigint;
/** @deprecated use .Y */
readonly ey: bigint;
/** @deprecated use .Z */
readonly ez: bigint;
/** @deprecated use .T */
readonly et: bigint;
}
/** Static methods of Extended Point with coordinates in X, Y, Z, T. */
export interface EdwardsPointCons extends CurvePointCons<EdwardsPoint> {
new (X: bigint, Y: bigint, Z: bigint, T: bigint): EdwardsPoint;
CURVE(): EdwardsOpts;
fromBytes(bytes: Uint8Array, zip215?: boolean): EdwardsPoint;
fromHex(hex: Hex, zip215?: boolean): EdwardsPoint;
/** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
msm(points: EdwardsPoint[], scalars: bigint[]): EdwardsPoint;
}
/** @deprecated use EdwardsPoint */
export type ExtPointType = EdwardsPoint;
/** @deprecated use EdwardsPointCons */
export type ExtPointConstructor = EdwardsPointCons;
/**
* Twisted Edwards curve options.
*
* * a: formula param
* * d: formula param
* * p: prime characteristic (order) of finite field, in which arithmetics is done
* * n: order of prime subgroup a.k.a total amount of valid curve points
* * h: cofactor. h*n is group order; n is subgroup order
* * Gx: x coordinate of generator point a.k.a. base point
* * Gy: y coordinate of generator point
*/
export type EdwardsOpts = Readonly<{
p: bigint;
n: bigint;
h: bigint;
a: bigint;
d: bigint;
Gx: bigint;
Gy: bigint;
}>;
/**
* Extra curve options for Twisted Edwards.
*
* * Fp: redefined Field over curve.p
* * Fn: redefined Field over curve.n
* * uvRatio: helper function for decompression, calculating √(u/v)
*/
export type EdwardsExtraOpts = Partial<{
Fp: IField<bigint>;
Fn: IField<bigint>;
FpFnLE: boolean;
uvRatio: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
}>;
/**
* EdDSA (Edwards Digital Signature algorithm) options.
*
* * hash: hash function used to hash secret keys and messages
* * adjustScalarBytes: clears bits to get valid field element
* * domain: Used for hashing
* * mapToCurve: for hash-to-curve standard
* * prehash: RFC 8032 pre-hashing of messages to sign() / verify()
* * randomBytes: function generating random bytes, used for randomSecretKey
*/
export type EdDSAOpts = Partial<{
adjustScalarBytes: (bytes: Uint8Array) => Uint8Array;
domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
mapToCurve: (scalar: bigint[]) => AffinePoint<bigint>;
prehash: FHash;
randomBytes: (bytesLength?: number) => Uint8Array;
}>;
/**
* EdDSA (Edwards Digital Signature algorithm) interface.
*
* Allows to create and verify signatures, create public and secret keys.
*/
export interface EdDSA {
keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
getPublicKey: (secretKey: Hex) => Uint8Array;
sign: (message: Hex, secretKey: Hex, options?: { context?: Hex }) => Uint8Array;
verify: (
sig: Hex,
message: Hex,
publicKey: Hex,
options?: { context?: Hex; zip215: boolean }
) => boolean;
Point: EdwardsPointCons;
utils: {
randomSecretKey: (seed?: Uint8Array) => Uint8Array;
isValidSecretKey: (secretKey: Uint8Array) => boolean;
isValidPublicKey: (publicKey: Uint8Array, zip215?: boolean) => boolean;
/**
* Converts ed public key to x public key.
*
* There is NO `fromMontgomery`:
* - There are 2 valid ed25519 points for every x25519, with flipped coordinate
* - Sometimes there are 0 valid ed25519 points, because x25519 *additionally*
* accepts inputs on the quadratic twist, which can't be moved to ed25519
*
* @example
* ```js
* const someonesPub = ed25519.getPublicKey(ed25519.utils.randomSecretKey());
* const aPriv = x25519.utils.randomSecretKey();
* x25519.getSharedSecret(aPriv, ed25519.utils.toMontgomery(someonesPub))
* ```
*/
toMontgomery: (publicKey: Uint8Array) => Uint8Array;
/**
* Converts ed secret key to x secret key.
* @example
* ```js
* const someonesPub = x25519.getPublicKey(x25519.utils.randomSecretKey());
* const aPriv = ed25519.utils.randomSecretKey();
* x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(aPriv), someonesPub)
* ```
*/
toMontgomerySecret: (privateKey: Uint8Array) => Uint8Array;
getExtendedPublicKey: (key: Hex) => {
head: Uint8Array;
prefix: Uint8Array;
scalar: bigint;
point: EdwardsPoint;
pointBytes: Uint8Array;
};
/** @deprecated use `randomSecretKey` */
randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
/** @deprecated use `point.precompute()` */
precompute: (windowSize?: number, point?: EdwardsPoint) => EdwardsPoint;
};
lengths: CurveLengths;
}
function isEdValidXY(Fp: IField<bigint>, CURVE: EdwardsOpts, x: bigint, y: bigint): boolean {
const x2 = Fp.sqr(x);
const y2 = Fp.sqr(y);
const left = Fp.add(Fp.mul(CURVE.a, x2), y2);
const right = Fp.add(Fp.ONE, Fp.mul(CURVE.d, Fp.mul(x2, y2)));
return Fp.eql(left, right);
}
export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}): EdwardsPointCons {
const validated = _createCurveFields('edwards', params, extraOpts, extraOpts.FpFnLE);
const { Fp, Fn } = validated;
let CURVE = validated.CURVE as EdwardsOpts;
const { h: cofactor } = CURVE;
_validateObject(extraOpts, {}, { uvRatio: 'function' });
// Important:
// There are some places where Fp.BYTES is used instead of nByteLength.
// So far, everything has been tested with curves of Fp.BYTES == nByteLength.
// TODO: test and find curves which behave otherwise.
const MASK = _2n << (BigInt(Fn.BYTES * 8) - _1n);
const modP = (n: bigint) => Fp.create(n); // Function overrides
// sqrt(u/v)
const uvRatio =
extraOpts.uvRatio ||
((u: bigint, v: bigint) => {
try {
return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
} catch (e) {
return { isValid: false, value: _0n };
}
});
// Validate whether the passed curve params are valid.
// equation ax² + y² = 1 + dx²y² should work for generator point.
if (!isEdValidXY(Fp, CURVE, CURVE.Gx, CURVE.Gy))
throw new Error('bad curve params: generator point');
/**
* Asserts coordinate is valid: 0 <= n < MASK.
* Coordinates >= Fp.ORDER are allowed for zip215.
*/
function acoord(title: string, n: bigint, banZero = false) {
const min = banZero ? _1n : _0n;
aInRange('coordinate ' + title, n, min, MASK);
return n;
}
function aextpoint(other: unknown) {
if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
const toAffineMemo = memoized((p: Point, iz?: bigint): AffinePoint<bigint> => {
const { X, Y, Z } = p;
const is0 = p.is0();
if (iz == null) iz = is0 ? _8n : (Fp.inv(Z) as bigint); // 8 was chosen arbitrarily
const x = modP(X * iz);
const y = modP(Y * iz);
const zz = Fp.mul(Z, iz);
if (is0) return { x: _0n, y: _1n };
if (zz !== _1n) throw new Error('invZ was invalid');
return { x, y };
});
const assertValidMemo = memoized((p: Point) => {
const { a, d } = CURVE;
if (p.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { X, Y, Z, T } = p;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right) throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
return true;
});
// Extended Point works in extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z, T=xy).
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
class Point implements EdwardsPoint {
// base / generator point
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
// zero / infinity / identity point
static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
// math field
static readonly Fp = Fp;
// scalar field
static readonly Fn = Fn;
readonly X: bigint;
readonly Y: bigint;
readonly Z: bigint;
readonly T: bigint;
constructor(X: bigint, Y: bigint, Z: bigint, T: bigint) {
this.X = acoord('x', X);
this.Y = acoord('y', Y);
this.Z = acoord('z', Z, true);
this.T = acoord('t', T);
Object.freeze(this);
}
static CURVE(): EdwardsOpts {
return CURVE;
}
static fromAffine(p: AffinePoint<bigint>): Point {
if (p instanceof Point) throw new Error('extended point not allowed');
const { x, y } = p || {};
acoord('x', x);
acoord('y', y);
return new Point(x, y, _1n, modP(x * y));
}
// Uses algo from RFC8032 5.1.3.
static fromBytes(bytes: Uint8Array, zip215 = false): Point {
const len = Fp.BYTES;
const { a, d } = CURVE;
bytes = copyBytes(abytes(bytes, len, 'point'));
abool(zip215, 'zip215');
const normed = copyBytes(bytes); // copy again, we'll manipulate it
const lastByte = bytes[len - 1]; // select last byte
normed[len - 1] = lastByte & ~0x80; // clear last bit
const y = bytesToNumberLE(normed);
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
// RFC8032 prohibits >= p, but ZIP215 doesn't
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
const max = zip215 ? MASK : Fp.ORDER;
aInRange('point.y', y, _0n, max);
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
const y2 = modP(y * y); // denominator is always non-0 mod p.
const u = modP(y2 - _1n); // u = y² - 1
const v = modP(d * y2 - a); // v = d y² + 1.
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
if (!isValid) throw new Error('bad point: invalid y coordinate');
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
if (!zip215 && x === _0n && isLastByteOdd)
// if x=0 and x_0 = 1, fail
throw new Error('bad point: x=0 and x_0=1');
if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
return Point.fromAffine({ x, y });
}
static fromHex(bytes: Uint8Array, zip215 = false): Point {
return Point.fromBytes(ensureBytes('point', bytes), zip215);
}
get x(): bigint {
return this.toAffine().x;
}
get y(): bigint {
return this.toAffine().y;
}
precompute(windowSize: number = 8, isLazy = true) {
wnaf.createCache(this, windowSize);
if (!isLazy) this.multiply(_2n); // random number
return this;
}
// Useful in fromAffine() - not for fromBytes(), which always created valid points.
assertValidity(): void {
assertValidMemo(this);
}
// Compare one point to another.
equals(other: Point): boolean {
aextpoint(other);
const { X: X1, Y: Y1, Z: Z1 } = this;
const { X: X2, Y: Y2, Z: Z2 } = other;
const X1Z2 = modP(X1 * Z2);
const X2Z1 = modP(X2 * Z1);
const Y1Z2 = modP(Y1 * Z2);
const Y2Z1 = modP(Y2 * Z1);
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
}
is0(): boolean {
return this.equals(Point.ZERO);
}
negate(): Point {
// Flips point sign to a negative one (-x, y in affine coords)
return new Point(modP(-this.X), this.Y, this.Z, modP(-this.T));
}
// Fast algo for doubling Extended Point.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
// Cost: 4M + 4S + 1*a + 6add + 1*2.
double(): Point {
const { a } = CURVE;
const { X: X1, Y: Y1, Z: Z1 } = this;
const A = modP(X1 * X1); // A = X12
const B = modP(Y1 * Y1); // B = Y12
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
const D = modP(a * A); // D = a*A
const x1y1 = X1 + Y1;
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
const G = D + B; // G = D+B
const F = G - C; // F = G-C
const H = D - B; // H = D-B
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
// Fast algo for adding 2 Extended Points.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
// Cost: 9M + 1*a + 1*d + 7add.
add(other: Point) {
aextpoint(other);
const { a, d } = CURVE;
const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;
const { X: X2, Y: Y2, Z: Z2, T: T2 } = other;
const A = modP(X1 * X2); // A = X1*X2
const B = modP(Y1 * Y2); // B = Y1*Y2
const C = modP(T1 * d * T2); // C = T1*d*T2
const D = modP(Z1 * Z2); // D = Z1*Z2
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
const F = D - C; // F = D-C
const G = D + C; // G = D+C
const H = modP(B - a * A); // H = B-a*A
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
subtract(other: Point): Point {
return this.add(other.negate());
}
// Constant-time multiplication.
multiply(scalar: bigint): Point {
// 1 <= scalar < L
if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: expected 1 <= sc < curve.n');
const { p, f } = wnaf.cached(this, scalar, (p) => normalizeZ(Point, p));
return normalizeZ(Point, [p, f])[0];
}
// Non-constant-time multiplication. Uses double-and-add algorithm.
// It's faster, but should only be used when you don't care about
// an exposed private key e.g. sig verification.
// Does NOT allow scalars higher than CURVE.n.
// Accepts optional accumulator to merge with multiply (important for sparse scalars)
multiplyUnsafe(scalar: bigint, acc = Point.ZERO): Point {
// 0 <= scalar < L
if (!Fn.isValid(scalar)) throw new Error('invalid scalar: expected 0 <= sc < curve.n');
if (scalar === _0n) return Point.ZERO;
if (this.is0() || scalar === _1n) return this;
return wnaf.unsafe(this, scalar, (p) => normalizeZ(Point, p), acc);
}
// Checks if point is of small order.
// If you add something to small order point, you will have "dirty"
// point with torsion component.
// Multiplies point by cofactor and checks if the result is 0.
isSmallOrder(): boolean {
return this.multiplyUnsafe(cofactor).is0();
}
// Multiplies point by curve order and checks if the result is 0.
// Returns `false` is the point is dirty.
isTorsionFree(): boolean {
return wnaf.unsafe(this, CURVE.n).is0();
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
toAffine(invertedZ?: bigint): AffinePoint<bigint> {
return toAffineMemo(this, invertedZ);
}
clearCofactor(): Point {
if (cofactor === _1n) return this;
return this.multiplyUnsafe(cofactor);
}
toBytes(): Uint8Array {
const { x, y } = this.toAffine();
// Fp.toBytes() allows non-canonical encoding of y (>= p).
const bytes = Fp.toBytes(y);
// Each y has 2 valid points: (x, y), (x,-y).
// When compressing, it's enough to store y and use the last byte to encode sign of x
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0;
return bytes;
}
toHex(): string {
return bytesToHex(this.toBytes());
}
toString() {
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
}
// TODO: remove
get ex(): bigint {
return this.X;
}
get ey(): bigint {
return this.Y;
}
get ez(): bigint {
return this.Z;
}
get et(): bigint {
return this.T;
}
static normalizeZ(points: Point[]): Point[] {
return normalizeZ(Point, points);
}
static msm(points: Point[], scalars: bigint[]): Point {
return pippenger(Point, Fn, points, scalars);
}
_setWindowSize(windowSize: number) {
this.precompute(windowSize);
}
toRawBytes(): Uint8Array {
return this.toBytes();
}
}
const wnaf = new wNAF(Point, Fn.BITS);
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
return Point;
}
/**
* Base class for prime-order points like Ristretto255 and Decaf448.
* These points eliminate cofactor issues by representing equivalence classes
* of Edwards curve points.
*/
export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
implements CurvePoint<bigint, T>
{
static BASE: PrimeEdwardsPoint<any>;
static ZERO: PrimeEdwardsPoint<any>;
static Fp: IField<bigint>;
static Fn: IField<bigint>;
protected readonly ep: EdwardsPoint;
constructor(ep: EdwardsPoint) {
this.ep = ep;
}
// Abstract methods that must be implemented by subclasses
abstract toBytes(): Uint8Array;
abstract equals(other: T): boolean;
// Static methods that must be implemented by subclasses
static fromBytes(_bytes: Uint8Array): any {
notImplemented();
}
static fromHex(_hex: Hex): any {
notImplemented();
}
get x(): bigint {
return this.toAffine().x;
}
get y(): bigint {
return this.toAffine().y;
}
// Common implementations
clearCofactor(): T {
// no-op for prime-order groups
return this as any;
}
assertValidity(): void {
this.ep.assertValidity();
}
toAffine(invertedZ?: bigint): AffinePoint<bigint> {
return this.ep.toAffine(invertedZ);
}
toHex(): string {
return bytesToHex(this.toBytes());
}
toString(): string {
return this.toHex();
}
isTorsionFree(): boolean {
return true;
}
isSmallOrder(): boolean {
return false;
}
add(other: T): T {
this.assertSame(other);
return this.init(this.ep.add(other.ep));
}
subtract(other: T): T {
this.assertSame(other);
return this.init(this.ep.subtract(other.ep));
}
multiply(scalar: bigint): T {
return this.init(this.ep.multiply(scalar));
}
multiplyUnsafe(scalar: bigint): T {
return this.init(this.ep.multiplyUnsafe(scalar));
}
double(): T {
return this.init(this.ep.double());
}
negate(): T {
return this.init(this.ep.negate());
}
precompute(windowSize?: number, isLazy?: boolean): T {
return this.init(this.ep.precompute(windowSize, isLazy));
}
// Helper methods
abstract is0(): boolean;
protected abstract assertSame(other: T): void;
protected abstract init(ep: EdwardsPoint): T;
/** @deprecated use `toBytes` */
toRawBytes(): Uint8Array {
return this.toBytes();
}
}
/**
* Initializes EdDSA signatures over given Edwards curve.
*/
export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpts = {}): EdDSA {
if (typeof cHash !== 'function') throw new Error('"hash" function param is required');
_validateObject(
eddsaOpts,
{},
{
adjustScalarBytes: 'function',
randomBytes: 'function',
domain: 'function',
prehash: 'function',
mapToCurve: 'function',
}
);
const { prehash } = eddsaOpts;
const { BASE, Fp, Fn } = Point;
const randomBytes = eddsaOpts.randomBytes || randomBytesWeb;
const adjustScalarBytes = eddsaOpts.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
const domain =
eddsaOpts.domain ||
((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
abool(phflag, 'phflag');
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
return data;
}); // NOOP
// Little-endian SHA512 with modulo n
function modN_LE(hash: Uint8Array): bigint {
return Fn.create(bytesToNumberLE(hash)); // Not Fn.fromBytes: it has length limit
}
// Get the hashed private scalar per RFC8032 5.1.5
function getPrivateScalar(key: Hex) {
const len = lengths.secretKey;
key = ensureBytes('private key', key, len);
// Hash private key with curve's hash function to produce uniformingly random input
// Check byte lengths: ensure(64, h(ensure(32, key)))
const hashed = ensureBytes('hashed private key', cHash(key), 2 * len);
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
const scalar = modN_LE(head); // The actual private scalar
return { head, prefix, scalar };
}
/** Convenience method that creates public key from scalar. RFC8032 5.1.5 */
function getExtendedPublicKey(secretKey: Hex) {
const { head, prefix, scalar } = getPrivateScalar(secretKey);
const point = BASE.multiply(scalar); // Point on Edwards curve aka public key
const pointBytes = point.toBytes();
return { head, prefix, scalar, point, pointBytes };
}
/** Calculates EdDSA pub key. RFC8032 5.1.5. */
function getPublicKey(secretKey: Hex): Uint8Array {
return getExtendedPublicKey(secretKey).pointBytes;
}
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
function hashDomainToScalar(context: Hex = Uint8Array.of(), ...msgs: Uint8Array[]) {
const msg = concatBytes(...msgs);
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!prehash)));
}
/** Signs message with privateKey. RFC8032 5.1.6 */
function sign(msg: Hex, secretKey: Hex, options: { context?: Hex } = {}): Uint8Array {
msg = ensureBytes('message', msg);
if (prehash) msg = prehash(msg); // for ed25519ph etc.
const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
const R = BASE.multiply(r).toBytes(); // R = rG
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
const s = Fn.create(r + k * scalar); // S = (r + k * s) mod L
if (!Fn.isValid(s)) throw new Error('sign failed: invalid s'); // 0 <= s < L
const rs = concatBytes(R, Fn.toBytes(s));
return abytes(rs, lengths.signature, 'result');
}
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
const verifyOpts: { context?: Hex; zip215?: boolean } = { zip215: true };
/**
* Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
* An extended group equation is checked.
*/
function verify(sig: Hex, msg: Hex, publicKey: Hex, options = verifyOpts): boolean {
const { context, zip215 } = options;
const len = lengths.signature;
sig = ensureBytes('signature', sig, len);
msg = ensureBytes('message', msg);
publicKey = ensureBytes('publicKey', publicKey, lengths.publicKey);
if (zip215 !== undefined) abool(zip215, 'zip215');
if (prehash) msg = prehash(msg); // for ed25519ph, etc
const mid = len / 2;
const r = sig.subarray(0, mid);
const s = bytesToNumberLE(sig.subarray(mid, len));
let A, R, SB;
try {
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
A = Point.fromBytes(publicKey, zip215);
R = Point.fromBytes(r, zip215);
SB = BASE.multiplyUnsafe(s); // 0 <= s < l is done inside
} catch (error) {
return false;
}
if (!zip215 && A.isSmallOrder()) return false; // zip215 allows public keys of small order
const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
const RkA = R.add(A.multiplyUnsafe(k));
// Extended group equation
// [8][S]B = [8]R + [8][k]A'
return RkA.subtract(SB).clearCofactor().is0();
}
const _size = Fp.BYTES; // 32 for ed25519, 57 for ed448
const lengths = {
secretKey: _size,
publicKey: _size,
signature: 2 * _size,
seed: _size,
};
function randomSecretKey(seed = randomBytes(lengths.seed)): Uint8Array {
return abytes(seed, lengths.seed, 'seed');
}
function keygen(seed?: Uint8Array) {
const secretKey = utils.randomSecretKey(seed);
return { secretKey, publicKey: getPublicKey(secretKey) };
}
function isValidSecretKey(key: Uint8Array): boolean {
return isBytes(key) && key.length === Fn.BYTES;
}
function isValidPublicKey(key: Uint8Array, zip215?: boolean): boolean {
try {
return !!Point.fromBytes(key, zip215);
} catch (error) {
return false;
}
}
const utils = {
getExtendedPublicKey,
randomSecretKey,
isValidSecretKey,
isValidPublicKey,
/**
* Converts ed public key to x public key. Uses formula:
* - ed25519:
* - `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
* - `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
* - ed448:
* - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
* - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
*/
toMontgomery(publicKey: Uint8Array): Uint8Array {
const { y } = Point.fromBytes(publicKey);
const size = lengths.publicKey;
const is25519 = size === 32;
if (!is25519 && size !== 57) throw new Error('only defined for 25519 and 448');
const u = is25519 ? Fp.div(_1n + y, _1n - y) : Fp.div(y - _1n, y + _1n);
return Fp.toBytes(u);
},
toMontgomerySecret(secretKey: Uint8Array): Uint8Array {
const size = lengths.secretKey;
abytes(secretKey, size);
const hashed = cHash(secretKey.subarray(0, size));
return adjustScalarBytes(hashed).subarray(0, size);
},
/** @deprecated */
randomPrivateKey: randomSecretKey,
/** @deprecated */
precompute(windowSize = 8, point: EdwardsPoint = Point.BASE): EdwardsPoint {
return point.precompute(windowSize, false);
},
};
return Object.freeze({
keygen,
getPublicKey,
sign,
verify,
utils,
Point,
lengths,
});
}
// TODO: remove everything below
export type CurveType = BasicCurve<bigint> & {
a: bigint; // curve param a
d: bigint; // curve param d
/** @deprecated the property will be removed in next release */
hash: FHash; // Hashing
randomBytes?: (bytesLength?: number) => Uint8Array; // CSPRNG
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
uvRatio?: UVRatio; // Ratio √(u/v)
prehash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
};
export type CurveTypeWithLength = Readonly<CurveType & Partial<NLength>>;
export type CurveFn = {
/** @deprecated the property will be removed in next release */
CURVE: CurveType;
keygen: EdDSA['keygen'];
getPublicKey: EdDSA['getPublicKey'];
sign: EdDSA['sign'];
verify: EdDSA['verify'];
Point: EdwardsPointCons;
/** @deprecated use `Point` */
ExtendedPoint: EdwardsPointCons;
utils: EdDSA['utils'];
lengths: CurveLengths;
};
export type EdComposed = {
CURVE: EdwardsOpts;
curveOpts: EdwardsExtraOpts;
hash: FHash;
eddsaOpts: EdDSAOpts;
};
function _eddsa_legacy_opts_to_new(c: CurveTypeWithLength): EdComposed {
const CURVE: EdwardsOpts = {
a: c.a,
d: c.d,
p: c.Fp.ORDER,
n: c.n,
h: c.h,
Gx: c.Gx,
Gy: c.Gy,
};
const Fp = c.Fp;
const Fn = Field(CURVE.n, c.nBitLength, true);
const curveOpts: EdwardsExtraOpts = { Fp, Fn, uvRatio: c.uvRatio };
const eddsaOpts: EdDSAOpts = {
randomBytes: c.randomBytes,
adjustScalarBytes: c.adjustScalarBytes,
domain: c.domain,
prehash: c.prehash,
mapToCurve: c.mapToCurve,
};
return { CURVE, curveOpts, hash: c.hash, eddsaOpts };
}
function _eddsa_new_output_to_legacy(c: CurveTypeWithLength, eddsa: EdDSA): CurveFn {
const Point = eddsa.Point;
const legacy = Object.assign({}, eddsa, {
ExtendedPoint: Point,
CURVE: c,
nBitLength: Point.Fn.BITS,
nByteLength: Point.Fn.BYTES,
});
return legacy;
}
// TODO: remove. Use eddsa
export function twistedEdwards(c: CurveTypeWithLength): CurveFn {
const { CURVE, curveOpts, hash, eddsaOpts } = _eddsa_legacy_opts_to_new(c);
const Point = edwards(CURVE, curveOpts);
const EDDSA = eddsa(Point, hash, eddsaOpts);
return _eddsa_new_output_to_legacy(c, EDDSA);
}

View File

@@ -0,0 +1,4 @@
{
"main": "../../cjs/_object_without_properties.cjs",
"module": "../../esm/_object_without_properties.js"
}

View File

@@ -0,0 +1,643 @@
# Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [Unreleased](https://github.com/motdotla/dotenv/compare/v17.4.2...master)
## [17.4.2](https://github.com/motdotla/dotenv/compare/v17.4.1...v17.4.2) (2026-04-12)
### Changed
* Improved skill files - tightened up details ([#1009](https://github.com/motdotla/dotenv/pull/1009))
## [17.4.1](https://github.com/motdotla/dotenv/compare/v17.4.0...v17.4.1) (2026-04-05)
### Changed
* Change text `injecting` to `injected` ([#1005](https://github.com/motdotla/dotenv/pull/1005))
## [17.4.0](https://github.com/motdotla/dotenv/compare/v17.3.1...v17.4.0) (2026-04-01)
### Added
* Add `skills/` folder with focused agent skills: `skills/dotenv/SKILL.md` (core usage) and `skills/dotenvx/SKILL.md` (encryption, multiple environments, variable expansion) for AI coding agent discovery via the skills.sh ecosystem (`npx skills add motdotla/dotenv`)
### Changed
* Tighten up logs: `◇ injecting env (14) from .env` ([#1003](https://github.com/motdotla/dotenv/pull/1003))
## [17.3.1](https://github.com/motdotla/dotenv/compare/v17.3.0...v17.3.1) (2026-02-12)
### Changed
* Fix as2 example command in README and update spanish README
## [17.3.0](https://github.com/motdotla/dotenv/compare/v17.2.4...v17.3.0) (2026-02-12)
### Added
* Add a new README section on dotenvs approach to the agentic future.
### Changed
* Rewrite README to get humans started more quickly with less noise while simultaneously making more accessible for llms and agents to go deeper into details.
## [17.2.4](https://github.com/motdotla/dotenv/compare/v17.2.3...v17.2.4) (2026-02-05)
### Changed
* Make `DotenvPopulateInput` accept `NodeJS.ProcessEnv` type ([#915](https://github.com/motdotla/dotenv/pull/915))
- Give back to dotenv by checking out my newest project [vestauth](https://github.com/vestauth/vestauth). It is auth for agents. Thank you for using my software.
## [17.2.3](https://github.com/motdotla/dotenv/compare/v17.2.2...v17.2.3) (2025-09-29)
### Changed
* Fixed typescript error definition ([#912](https://github.com/motdotla/dotenv/pull/912))
## [17.2.2](https://github.com/motdotla/dotenv/compare/v17.2.1...v17.2.2) (2025-09-02)
### Added
- 🙏 A big thank you to new sponsor [Tuple.app](https://tuple.app/dotenv) - *the premier screen sharing app for developers on macOS and Windows.* Go check them out. It's wonderful and generous of them to give back to open source by sponsoring dotenv. Give them some love back.
## [17.2.1](https://github.com/motdotla/dotenv/compare/v17.2.0...v17.2.1) (2025-07-24)
### Changed
* Fix clickable tip links by removing parentheses ([#897](https://github.com/motdotla/dotenv/pull/897))
## [17.2.0](https://github.com/motdotla/dotenv/compare/v17.1.0...v17.2.0) (2025-07-09)
### Added
* Optionally specify `DOTENV_CONFIG_QUIET=true` in your environment or `.env` file to quiet the runtime log ([#889](https://github.com/motdotla/dotenv/pull/889))
* Just like dotenv any `DOTENV_CONFIG_` environment variables take precedence over any code set options like `({quiet: false})`
```ini
# .env
DOTENV_CONFIG_QUIET=true
HELLO="World"
```
```js
// index.js
require('dotenv').config()
console.log(`Hello ${process.env.HELLO}`)
```
```sh
$ node index.js
Hello World
or
$ DOTENV_CONFIG_QUIET=true node index.js
```
## [17.1.0](https://github.com/motdotla/dotenv/compare/v17.0.1...v17.1.0) (2025-07-07)
### Added
* Add additional security and configuration tips to the runtime log ([#884](https://github.com/motdotla/dotenv/pull/884))
* Dim the tips text from the main injection information text
```js
const TIPS = [
'🔐 encrypt with dotenvx: https://dotenvx.com',
'🔐 prevent committing .env to code: https://dotenvx.com/precommit',
'🔐 prevent building .env in docker: https://dotenvx.com/prebuild',
'🛠️ run anywhere with `dotenvx run -- yourcommand`',
'⚙️ specify custom .env file path with { path: \'/custom/path/.env\' }',
'⚙️ enable debug logging with { debug: true }',
'⚙️ override existing env vars with { override: true }',
'⚙️ suppress all logs with { quiet: true }',
'⚙️ write to custom object with { processEnv: myObject }',
'⚙️ load multiple .env files with { path: [\'.env.local\', \'.env\'] }'
]
```
## [17.0.1](https://github.com/motdotla/dotenv/compare/v17.0.0...v17.0.1) (2025-07-01)
### Changed
* Patched injected log to count only populated/set keys to process.env ([#879](https://github.com/motdotla/dotenv/pull/879))
## [17.0.0](https://github.com/motdotla/dotenv/compare/v16.6.1...v17.0.0) (2025-06-27)
### Changed
- Default `quiet` to false - informational (file and keys count) runtime log message shows by default ([#875](https://github.com/motdotla/dotenv/pull/875))
## [16.6.1](https://github.com/motdotla/dotenv/compare/v16.6.0...v16.6.1) (2025-06-27)
### Changed
- Default `quiet` to true hiding the runtime log message ([#874](https://github.com/motdotla/dotenv/pull/874))
- NOTICE: 17.0.0 will be released with quiet defaulting to false. Use `config({ quiet: true })` to suppress.
- And check out the new [dotenvx](https://github.com/dotenvx/dotenvx). As coding workflows evolve and agents increasingly handle secrets, encrypted .env files offer a much safer way to deploy both agents and code together with secure secrets. Simply switch `require('dotenv').config()` for `require('@dotenvx/dotenvx').config()`.
## [16.6.0](https://github.com/motdotla/dotenv/compare/v16.5.0...v16.6.0) (2025-06-26)
### Added
- Default log helpful message `[dotenv@16.6.0] injecting env (1) from .env` ([#870](https://github.com/motdotla/dotenv/pull/870))
- Use `{ quiet: true }` to suppress
- Aligns dotenv more closely with [dotenvx](https://github.com/dotenvx/dotenvx).
## [16.5.0](https://github.com/motdotla/dotenv/compare/v16.4.7...v16.5.0) (2025-04-07)
### Added
- 🎉 Added new sponsor [Graphite](https://graphite.dev/?utm_source=github&utm_medium=repo&utm_campaign=dotenv) - *the AI developer productivity platform helping teams on GitHub ship higher quality software, faster*.
> [!TIP]
> **[Become a sponsor](https://github.com/sponsors/motdotla)**
>
> The dotenvx README is viewed thousands of times DAILY on GitHub and NPM.
> Sponsoring dotenv is a great way to get in front of developers and give back to the developer community at the same time.
### Changed
- Remove `_log` method. Use `_debug` [#862](https://github.com/motdotla/dotenv/pull/862)
## [16.4.7](https://github.com/motdotla/dotenv/compare/v16.4.6...v16.4.7) (2024-12-03)
### Changed
- Ignore `.tap` folder when publishing. (oops, sorry about that everyone. - @motdotla) [#848](https://github.com/motdotla/dotenv/pull/848)
## [16.4.6](https://github.com/motdotla/dotenv/compare/v16.4.5...v16.4.6) (2024-12-02)
### Changed
- Clean up stale dev dependencies [#847](https://github.com/motdotla/dotenv/pull/847)
- Various README updates clarifying usage and alternative solutions using [dotenvx](https://github.com/dotenvx/dotenvx)
## [16.4.5](https://github.com/motdotla/dotenv/compare/v16.4.4...v16.4.5) (2024-02-19)
### Changed
- 🐞 Fix recent regression when using `path` option. return to historical behavior: do not attempt to auto find `.env` if `path` set. (regression was introduced in `16.4.3`) [#814](https://github.com/motdotla/dotenv/pull/814)
## [16.4.4](https://github.com/motdotla/dotenv/compare/v16.4.3...v16.4.4) (2024-02-13)
### Changed
- 🐞 Replaced chaining operator `?.` with old school `&&` (fixing node 12 failures) [#812](https://github.com/motdotla/dotenv/pull/812)
## [16.4.3](https://github.com/motdotla/dotenv/compare/v16.4.2...v16.4.3) (2024-02-12)
### Changed
- Fixed processing of multiple files in `options.path` [#805](https://github.com/motdotla/dotenv/pull/805)
## [16.4.2](https://github.com/motdotla/dotenv/compare/v16.4.1...v16.4.2) (2024-02-10)
### Changed
- Changed funding link in package.json to [`dotenvx.com`](https://dotenvx.com)
## [16.4.1](https://github.com/motdotla/dotenv/compare/v16.4.0...v16.4.1) (2024-01-24)
- Patch support for array as `path` option [#797](https://github.com/motdotla/dotenv/pull/797)
## [16.4.0](https://github.com/motdotla/dotenv/compare/v16.3.2...v16.4.0) (2024-01-23)
- Add `error.code` to error messages around `.env.vault` decryption handling [#795](https://github.com/motdotla/dotenv/pull/795)
- Add ability to find `.env.vault` file when filename(s) passed as an array [#784](https://github.com/motdotla/dotenv/pull/784)
## [16.3.2](https://github.com/motdotla/dotenv/compare/v16.3.1...v16.3.2) (2024-01-18)
### Added
- Add debug message when no encoding set [#735](https://github.com/motdotla/dotenv/pull/735)
### Changed
- Fix output typing for `populate` [#792](https://github.com/motdotla/dotenv/pull/792)
- Use subarray instead of slice [#793](https://github.com/motdotla/dotenv/pull/793)
## [16.3.1](https://github.com/motdotla/dotenv/compare/v16.3.0...v16.3.1) (2023-06-17)
### Added
- Add missing type definitions for `processEnv` and `DOTENV_KEY` options. [#756](https://github.com/motdotla/dotenv/pull/756)
## [16.3.0](https://github.com/motdotla/dotenv/compare/v16.2.0...v16.3.0) (2023-06-16)
### Added
- Optionally pass `DOTENV_KEY` to options rather than relying on `process.env.DOTENV_KEY`. Defaults to `process.env.DOTENV_KEY` [#754](https://github.com/motdotla/dotenv/pull/754)
## [16.2.0](https://github.com/motdotla/dotenv/compare/v16.1.4...v16.2.0) (2023-06-15)
### Added
- Optionally write to your own target object rather than `process.env`. Defaults to `process.env`. [#753](https://github.com/motdotla/dotenv/pull/753)
- Add import type URL to types file [#751](https://github.com/motdotla/dotenv/pull/751)
## [16.1.4](https://github.com/motdotla/dotenv/compare/v16.1.3...v16.1.4) (2023-06-04)
### Added
- Added `.github/` to `.npmignore` [#747](https://github.com/motdotla/dotenv/pull/747)
## [16.1.3](https://github.com/motdotla/dotenv/compare/v16.1.2...v16.1.3) (2023-05-31)
### Removed
- Removed `browser` keys for `path`, `os`, and `crypto` in package.json. These were set to false incorrectly as of 16.1. Instead, if using dotenv on the front-end make sure to include polyfills for `path`, `os`, and `crypto`. [node-polyfill-webpack-plugin](https://github.com/Richienb/node-polyfill-webpack-plugin) provides these.
## [16.1.2](https://github.com/motdotla/dotenv/compare/v16.1.1...v16.1.2) (2023-05-31)
### Changed
- Exposed private function `_configDotenv` as `configDotenv`. [#744](https://github.com/motdotla/dotenv/pull/744)
## [16.1.1](https://github.com/motdotla/dotenv/compare/v16.1.0...v16.1.1) (2023-05-30)
### Added
- Added type definition for `decrypt` function
### Changed
- Fixed `{crypto: false}` in `packageJson.browser`
## [16.1.0](https://github.com/motdotla/dotenv/compare/v16.0.3...v16.1.0) (2023-05-30)
### Added
- Add `populate` convenience method [#733](https://github.com/motdotla/dotenv/pull/733)
- Accept URL as path option [#720](https://github.com/motdotla/dotenv/pull/720)
- Add dotenv to `npm fund` command
- Spanish language README [#698](https://github.com/motdotla/dotenv/pull/698)
- Add `.env.vault` support. 🎉 ([#730](https://github.com/motdotla/dotenv/pull/730))
`.env.vault` extends the `.env` file format standard with a localized encrypted vault file. Package it securely with your production code deploys. It's cloud agnostic so that you can deploy your secrets anywhere  without [risky third-party integrations](https://techcrunch.com/2023/01/05/circleci-breach/). [read more](https://github.com/motdotla/dotenv#-deploying)
### Changed
- Fixed "cannot resolve 'fs'" error on tools like Replit [#693](https://github.com/motdotla/dotenv/pull/693)
## [16.0.3](https://github.com/motdotla/dotenv/compare/v16.0.2...v16.0.3) (2022-09-29)
### Changed
- Added library version to debug logs ([#682](https://github.com/motdotla/dotenv/pull/682))
## [16.0.2](https://github.com/motdotla/dotenv/compare/v16.0.1...v16.0.2) (2022-08-30)
### Added
- Export `env-options.js` and `cli-options.js` in package.json for use with downstream [dotenv-expand](https://github.com/motdotla/dotenv-expand) module
## [16.0.1](https://github.com/motdotla/dotenv/compare/v16.0.0...v16.0.1) (2022-05-10)
### Changed
- Minor README clarifications
- Development ONLY: updated devDependencies as recommended for development only security risks ([#658](https://github.com/motdotla/dotenv/pull/658))
## [16.0.0](https://github.com/motdotla/dotenv/compare/v15.0.1...v16.0.0) (2022-02-02)
### Added
- _Breaking:_ Backtick support 🎉 ([#615](https://github.com/motdotla/dotenv/pull/615))
If you had values containing the backtick character, please quote those values with either single or double quotes.
## [15.0.1](https://github.com/motdotla/dotenv/compare/v15.0.0...v15.0.1) (2022-02-02)
### Changed
- Properly parse empty single or double quoted values 🐞 ([#614](https://github.com/motdotla/dotenv/pull/614))
## [15.0.0](https://github.com/motdotla/dotenv/compare/v14.3.2...v15.0.0) (2022-01-31)
`v15.0.0` is a major new release with some important breaking changes.
### Added
- _Breaking:_ Multiline parsing support (just works. no need for the flag.)
### Changed
- _Breaking:_ `#` marks the beginning of a comment (UNLESS the value is wrapped in quotes. Please update your `.env` files to wrap in quotes any values containing `#`. For example: `SECRET_HASH="something-with-a-#-hash"`).
..Understandably, (as some teams have noted) this is tedious to do across the entire team. To make it less tedious, we recommend using [dotenv cli](https://github.com/dotenv-org/cli) going forward. It's an optional plugin that will keep your `.env` files in sync between machines, environments, or team members.
### Removed
- _Breaking:_ Remove multiline option (just works out of the box now. no need for the flag.)
## [14.3.2](https://github.com/motdotla/dotenv/compare/v14.3.1...v14.3.2) (2022-01-25)
### Changed
- Preserve backwards compatibility on values containing `#` 🐞 ([#603](https://github.com/motdotla/dotenv/pull/603))
## [14.3.1](https://github.com/motdotla/dotenv/compare/v14.3.0...v14.3.1) (2022-01-25)
### Changed
- Preserve backwards compatibility on exports by re-introducing the prior in-place exports 🐞 ([#606](https://github.com/motdotla/dotenv/pull/606))
## [14.3.0](https://github.com/motdotla/dotenv/compare/v14.2.0...v14.3.0) (2022-01-24)
### Added
- Add `multiline` option 🎉 ([#486](https://github.com/motdotla/dotenv/pull/486))
## [14.2.0](https://github.com/motdotla/dotenv/compare/v14.1.1...v14.2.0) (2022-01-17)
### Added
- Add `dotenv_config_override` cli option
- Add `DOTENV_CONFIG_OVERRIDE` command line env option
## [14.1.1](https://github.com/motdotla/dotenv/compare/v14.1.0...v14.1.1) (2022-01-17)
### Added
- Add React gotcha to FAQ on README
## [14.1.0](https://github.com/motdotla/dotenv/compare/v14.0.1...v14.1.0) (2022-01-17)
### Added
- Add `override` option 🎉 ([#595](https://github.com/motdotla/dotenv/pull/595))
## [14.0.1](https://github.com/motdotla/dotenv/compare/v14.0.0...v14.0.1) (2022-01-16)
### Added
- Log error on failure to load `.env` file ([#594](https://github.com/motdotla/dotenv/pull/594))
## [14.0.0](https://github.com/motdotla/dotenv/compare/v13.0.1...v14.0.0) (2022-01-16)
### Added
- _Breaking:_ Support inline comments for the parser 🎉 ([#568](https://github.com/motdotla/dotenv/pull/568))
## [13.0.1](https://github.com/motdotla/dotenv/compare/v13.0.0...v13.0.1) (2022-01-16)
### Changed
* Hide comments and newlines from debug output ([#404](https://github.com/motdotla/dotenv/pull/404))
## [13.0.0](https://github.com/motdotla/dotenv/compare/v12.0.4...v13.0.0) (2022-01-16)
### Added
* _Breaking:_ Add type file for `config.js` ([#539](https://github.com/motdotla/dotenv/pull/539))
## [12.0.4](https://github.com/motdotla/dotenv/compare/v12.0.3...v12.0.4) (2022-01-16)
### Changed
* README updates
* Minor order adjustment to package json format
## [12.0.3](https://github.com/motdotla/dotenv/compare/v12.0.2...v12.0.3) (2022-01-15)
### Changed
* Simplified jsdoc for consistency across editors
## [12.0.2](https://github.com/motdotla/dotenv/compare/v12.0.1...v12.0.2) (2022-01-15)
### Changed
* Improve embedded jsdoc type documentation
## [12.0.1](https://github.com/motdotla/dotenv/compare/v12.0.0...v12.0.1) (2022-01-15)
### Changed
* README updates and clarifications
## [12.0.0](https://github.com/motdotla/dotenv/compare/v11.0.0...v12.0.0) (2022-01-15)
### Removed
- _Breaking:_ drop support for Flow static type checker ([#584](https://github.com/motdotla/dotenv/pull/584))
### Changed
- Move types/index.d.ts to lib/main.d.ts ([#585](https://github.com/motdotla/dotenv/pull/585))
- Typescript cleanup ([#587](https://github.com/motdotla/dotenv/pull/587))
- Explicit typescript inclusion in package.json ([#566](https://github.com/motdotla/dotenv/pull/566))
## [11.0.0](https://github.com/motdotla/dotenv/compare/v10.0.0...v11.0.0) (2022-01-11)
### Changed
- _Breaking:_ drop support for Node v10 ([#558](https://github.com/motdotla/dotenv/pull/558))
- Patch debug option ([#550](https://github.com/motdotla/dotenv/pull/550))
## [10.0.0](https://github.com/motdotla/dotenv/compare/v9.0.2...v10.0.0) (2021-05-20)
### Added
- Add generic support to parse function
- Allow for import "dotenv/config.js"
- Add support to resolve home directory in path via ~
## [9.0.2](https://github.com/motdotla/dotenv/compare/v9.0.1...v9.0.2) (2021-05-10)
### Changed
- Support windows newlines with debug mode
## [9.0.1](https://github.com/motdotla/dotenv/compare/v9.0.0...v9.0.1) (2021-05-08)
### Changed
- Updates to README
## [9.0.0](https://github.com/motdotla/dotenv/compare/v8.6.0...v9.0.0) (2021-05-05)
### Changed
- _Breaking:_ drop support for Node v8
## [8.6.0](https://github.com/motdotla/dotenv/compare/v8.5.1...v8.6.0) (2021-05-05)
### Added
- define package.json in exports
## [8.5.1](https://github.com/motdotla/dotenv/compare/v8.5.0...v8.5.1) (2021-05-05)
### Changed
- updated dev dependencies via npm audit
## [8.5.0](https://github.com/motdotla/dotenv/compare/v8.4.0...v8.5.0) (2021-05-05)
### Added
- allow for `import "dotenv/config"`
## [8.4.0](https://github.com/motdotla/dotenv/compare/v8.3.0...v8.4.0) (2021-05-05)
### Changed
- point to exact types file to work with VS Code
## [8.3.0](https://github.com/motdotla/dotenv/compare/v8.2.0...v8.3.0) (2021-05-05)
### Changed
- _Breaking:_ drop support for Node v8 (mistake to be released as minor bump. later bumped to 9.0.0. see above.)
## [8.2.0](https://github.com/motdotla/dotenv/compare/v8.1.0...v8.2.0) (2019-10-16)
### Added
- TypeScript types
## [8.1.0](https://github.com/motdotla/dotenv/compare/v8.0.0...v8.1.0) (2019-08-18)
### Changed
- _Breaking:_ drop support for Node v6 ([#392](https://github.com/motdotla/dotenv/issues/392))
# [8.0.0](https://github.com/motdotla/dotenv/compare/v7.0.0...v8.0.0) (2019-05-02)
### Changed
- _Breaking:_ drop support for Node v6 ([#302](https://github.com/motdotla/dotenv/issues/392))
## [7.0.0] - 2019-03-12
### Fixed
- Fix removing unbalanced quotes ([#376](https://github.com/motdotla/dotenv/pull/376))
### Removed
- Removed `load` alias for `config` for consistency throughout code and documentation.
## [6.2.0] - 2018-12-03
### Added
- Support preload configuration via environment variables ([#351](https://github.com/motdotla/dotenv/issues/351))
## [6.1.0] - 2018-10-08
### Added
- `debug` option for `config` and `parse` methods will turn on logging
## [6.0.0] - 2018-06-02
### Changed
- _Breaking:_ drop support for Node v4 ([#304](https://github.com/motdotla/dotenv/pull/304))
## [5.0.0] - 2018-01-29
### Added
- Testing against Node v8 and v9
- Documentation on trim behavior of values
- Documentation on how to use with `import`
### Changed
- _Breaking_: default `path` is now `path.resolve(process.cwd(), '.env')`
- _Breaking_: does not write over keys already in `process.env` if the key has a falsy value
- using `const` and `let` instead of `var`
### Removed
- Testing against Node v7
## [4.0.0] - 2016-12-23
### Changed
- Return Object with parsed content or error instead of false ([#165](https://github.com/motdotla/dotenv/pull/165)).
### Removed
- `verbose` option removed in favor of returning result.
## [3.0.0] - 2016-12-20
### Added
- `verbose` option will log any error messages. Off by default.
- parses email addresses correctly
- allow importing config method directly in ES6
### Changed
- Suppress error messages by default ([#154](https://github.com/motdotla/dotenv/pull/154))
- Ignoring more files for NPM to make package download smaller
### Fixed
- False positive test due to case-sensitive variable ([#124](https://github.com/motdotla/dotenv/pull/124))
### Removed
- `silent` option removed in favor of `verbose`
## [2.0.0] - 2016-01-20
### Added
- CHANGELOG to ["make it easier for users and contributors to see precisely what notable changes have been made between each release"](http://keepachangelog.com/). Linked to from README
- LICENSE to be more explicit about what was defined in `package.json`. Linked to from README
- Testing nodejs v4 on travis-ci
- added examples of how to use dotenv in different ways
- return parsed object on success rather than boolean true
### Changed
- README has shorter description not referencing ruby gem since we don't have or want feature parity
### Removed
- Variable expansion and escaping so environment variables are encouraged to be fully orthogonal
## [1.2.0] - 2015-06-20
### Added
- Preload hook to require dotenv without including it in your code
### Changed
- clarified license to be "BSD-2-Clause" in `package.json`
### Fixed
- retain spaces in string vars
## [1.1.0] - 2015-03-31
### Added
- Silent option to silence `console.log` when `.env` missing
## [1.0.0] - 2015-03-13
### Removed
- support for multiple `.env` files. should always use one `.env` file for the current environment
[7.0.0]: https://github.com/motdotla/dotenv/compare/v6.2.0...v7.0.0
[6.2.0]: https://github.com/motdotla/dotenv/compare/v6.1.0...v6.2.0
[6.1.0]: https://github.com/motdotla/dotenv/compare/v6.0.0...v6.1.0
[6.0.0]: https://github.com/motdotla/dotenv/compare/v5.0.0...v6.0.0
[5.0.0]: https://github.com/motdotla/dotenv/compare/v4.0.0...v5.0.0
[4.0.0]: https://github.com/motdotla/dotenv/compare/v3.0.0...v4.0.0
[3.0.0]: https://github.com/motdotla/dotenv/compare/v2.0.0...v3.0.0
[2.0.0]: https://github.com/motdotla/dotenv/compare/v1.2.0...v2.0.0
[1.2.0]: https://github.com/motdotla/dotenv/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/motdotla/dotenv/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/motdotla/dotenv/compare/v0.4.0...v1.0.0

View File

@@ -0,0 +1,33 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
export type MessageIds = 'incorrectGroupOrder' | 'incorrectOrder' | 'incorrectRequiredMembersOrder';
type ReadonlyType = 'readonly-field' | 'readonly-signature';
type MemberKind = 'accessor' | 'call-signature' | 'constructor' | 'field' | 'get' | 'method' | 'set' | 'signature' | 'static-initialization' | ReadonlyType;
type DecoratedMemberKind = 'accessor' | 'field' | 'get' | 'method' | 'set' | Exclude<ReadonlyType, 'readonly-signature'>;
type NonCallableMemberKind = Exclude<MemberKind, 'constructor' | 'readonly-signature' | 'signature'>;
type MemberScope = 'abstract' | 'instance' | 'static';
type Accessibility = '#private' | TSESTree.Accessibility;
type BaseMemberType = `${Accessibility}-${Exclude<MemberKind, 'readonly-signature' | 'signature' | 'static-initialization'>}` | `${Accessibility}-${MemberScope}-${NonCallableMemberKind}` | `${Accessibility}-decorated-${DecoratedMemberKind}` | `${MemberScope}-${NonCallableMemberKind}` | `decorated-${DecoratedMemberKind}` | MemberKind;
type MemberType = BaseMemberType | BaseMemberType[];
type AlphabeticalOrder = 'alphabetically' | 'alphabetically-case-insensitive' | 'natural' | 'natural-case-insensitive';
type Order = 'as-written' | AlphabeticalOrder;
interface SortedOrderConfig {
memberTypes?: 'never' | MemberType[];
optionalityOrder?: OptionalityOrder;
order?: Order;
}
type OrderConfig = 'never' | MemberType[] | SortedOrderConfig;
type OptionalityOrder = 'optional-first' | 'required-first';
export type Options = [
{
classes?: OrderConfig;
classExpressions?: OrderConfig;
default?: OrderConfig;
interfaces?: OrderConfig;
typeLiterals?: OrderConfig;
}
];
export declare const defaultOrder: MemberType[];
declare const _default: TSESLint.RuleModule<MessageIds, Options, import("../../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
name: string;
};
export default _default;

View File

@@ -0,0 +1,10 @@
import { a as Debugger, i as DebugOptions, n as enabled, o as Formatters, r as namespaces, s as InspectOptions, t as disable } from "./core.js";
//#region src/node.d.ts
declare function createDebug(namespace: string, options?: DebugOptions): Debugger;
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*/
declare function enable(namespaces: string): void;
//#endregion
export { type DebugOptions, type Debugger, type Formatters, type InspectOptions, createDebug, disable, enable, enabled, namespaces };

View File

@@ -0,0 +1,165 @@
import {MessageHeader, MessageAddressTableLookup} from './index';
import {AccountKeysFromLookups} from './account-keys';
import {AddressLookupTableAccount} from '../programs';
import {TransactionInstruction} from '../transaction';
import assert from '../utils/assert';
import {PublicKey} from '../publickey';
export type CompiledKeyMeta = {
isSigner: boolean;
isWritable: boolean;
isInvoked: boolean;
};
type KeyMetaMap = Map<string, CompiledKeyMeta>;
export class CompiledKeys {
payer: PublicKey;
keyMetaMap: KeyMetaMap;
constructor(payer: PublicKey, keyMetaMap: KeyMetaMap) {
this.payer = payer;
this.keyMetaMap = keyMetaMap;
}
static compile(
instructions: Array<TransactionInstruction>,
payer: PublicKey,
): CompiledKeys {
const keyMetaMap: KeyMetaMap = new Map();
const getOrInsertDefault = (pubkey: PublicKey): CompiledKeyMeta => {
const address = pubkey.toBase58();
let keyMeta = keyMetaMap.get(address);
if (keyMeta === undefined) {
keyMeta = {
isSigner: false,
isWritable: false,
isInvoked: false,
};
keyMetaMap.set(address, keyMeta);
}
return keyMeta;
};
const payerKeyMeta = getOrInsertDefault(payer);
payerKeyMeta.isSigner = true;
payerKeyMeta.isWritable = true;
for (const ix of instructions) {
getOrInsertDefault(ix.programId).isInvoked = true;
for (const accountMeta of ix.keys) {
const keyMeta = getOrInsertDefault(accountMeta.pubkey);
keyMeta.isSigner ||= accountMeta.isSigner;
keyMeta.isWritable ||= accountMeta.isWritable;
}
}
return new CompiledKeys(payer, keyMetaMap);
}
getMessageComponents(): [MessageHeader, Array<PublicKey>] {
const mapEntries = [...this.keyMetaMap.entries()];
assert(mapEntries.length <= 256, 'Max static account keys length exceeded');
const writableSigners = mapEntries.filter(
([, meta]) => meta.isSigner && meta.isWritable,
);
const readonlySigners = mapEntries.filter(
([, meta]) => meta.isSigner && !meta.isWritable,
);
const writableNonSigners = mapEntries.filter(
([, meta]) => !meta.isSigner && meta.isWritable,
);
const readonlyNonSigners = mapEntries.filter(
([, meta]) => !meta.isSigner && !meta.isWritable,
);
const header: MessageHeader = {
numRequiredSignatures: writableSigners.length + readonlySigners.length,
numReadonlySignedAccounts: readonlySigners.length,
numReadonlyUnsignedAccounts: readonlyNonSigners.length,
};
// sanity checks
{
assert(
writableSigners.length > 0,
'Expected at least one writable signer key',
);
const [payerAddress] = writableSigners[0];
assert(
payerAddress === this.payer.toBase58(),
'Expected first writable signer key to be the fee payer',
);
}
const staticAccountKeys = [
...writableSigners.map(([address]) => new PublicKey(address)),
...readonlySigners.map(([address]) => new PublicKey(address)),
...writableNonSigners.map(([address]) => new PublicKey(address)),
...readonlyNonSigners.map(([address]) => new PublicKey(address)),
];
return [header, staticAccountKeys];
}
extractTableLookup(
lookupTable: AddressLookupTableAccount,
): [MessageAddressTableLookup, AccountKeysFromLookups] | undefined {
const [writableIndexes, drainedWritableKeys] =
this.drainKeysFoundInLookupTable(
lookupTable.state.addresses,
keyMeta =>
!keyMeta.isSigner && !keyMeta.isInvoked && keyMeta.isWritable,
);
const [readonlyIndexes, drainedReadonlyKeys] =
this.drainKeysFoundInLookupTable(
lookupTable.state.addresses,
keyMeta =>
!keyMeta.isSigner && !keyMeta.isInvoked && !keyMeta.isWritable,
);
// Don't extract lookup if no keys were found
if (writableIndexes.length === 0 && readonlyIndexes.length === 0) {
return;
}
return [
{
accountKey: lookupTable.key,
writableIndexes,
readonlyIndexes,
},
{
writable: drainedWritableKeys,
readonly: drainedReadonlyKeys,
},
];
}
/** @internal */
private drainKeysFoundInLookupTable(
lookupTableEntries: Array<PublicKey>,
keyMetaFilter: (keyMeta: CompiledKeyMeta) => boolean,
): [Array<number>, Array<PublicKey>] {
const lookupTableIndexes = new Array();
const drainedKeys = new Array();
for (const [address, keyMeta] of this.keyMetaMap.entries()) {
if (keyMetaFilter(keyMeta)) {
const key = new PublicKey(address);
const lookupTableIndex = lookupTableEntries.findIndex(entry =>
entry.equals(key),
);
if (lookupTableIndex >= 0) {
assert(lookupTableIndex < 256, 'Max lookup table index exceeded');
lookupTableIndexes.push(lookupTableIndex);
drainedKeys.push(key);
this.keyMetaMap.delete(address);
}
}
}
return [lookupTableIndexes, drainedKeys];
}
}

View File

@@ -0,0 +1,75 @@
import { SolanaErrorCode, SolanaErrorCodeWithCause } from './codes';
import { SolanaErrorContext } from './context';
/**
* A type guard that returns `true` if the input is a {@link SolanaError}, optionally with a
* particular error code.
*
* When the `code` argument is supplied and the input is a {@link SolanaError}, TypeScript will
* refine the error's {@link SolanaError#context | `context`} property to the type associated with
* that error code. You can use that context to render useful error messages, or to make
* context-aware decisions that help your application to recover from the error.
*
* @example
* ```ts
* import {
* SOLANA_ERROR__TRANSACTION__MISSING_SIGNATURE,
* SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING,
* isSolanaError,
* } from '@solana/errors';
* import { assertIsFullySignedTransaction, getSignatureFromTransaction } from '@solana/transactions';
*
* try {
* const transactionSignature = getSignatureFromTransaction(tx);
* assertIsFullySignedTransaction(tx);
* /* ... *\/
* } catch (e) {
* if (isSolanaError(e, SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING)) {
* displayError(
* "We can't send this transaction without signatures for these addresses:\n- %s",
* // The type of the `context` object is now refined to contain `addresses`.
* e.context.addresses.join('\n- '),
* );
* return;
* } else if (isSolanaError(e, SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING)) {
* if (!tx.feePayer) {
* displayError('Choose a fee payer for this transaction before sending it');
* } else {
* displayError('The fee payer still needs to sign for this transaction');
* }
* return;
* }
* throw e;
* }
* ```
*/
export declare function isSolanaError<TErrorCode extends SolanaErrorCode>(e: unknown,
/**
* When supplied, this function will require that the input is a {@link SolanaError} _and_ that
* its error code is exactly this value.
*/
code?: TErrorCode): e is SolanaError<TErrorCode>;
type SolanaErrorCodedContext = Readonly<{
[P in SolanaErrorCode]: (SolanaErrorContext[P] extends undefined ? object : SolanaErrorContext[P]) & {
__code: P;
};
}>;
/**
* Encapsulates an error's stacktrace, a Solana-specific numeric code that indicates what went
* wrong, and optional context if the type of error indicated by the code supports it.
*/
export declare class SolanaError<TErrorCode extends SolanaErrorCode = SolanaErrorCode> extends Error {
/**
* Indicates the root cause of this {@link SolanaError}, if any.
*
* For example, a transaction error might have an instruction error as its root cause. In this
* case, you will be able to access the instruction error on the transaction error as `cause`.
*/
readonly cause?: TErrorCode extends SolanaErrorCodeWithCause ? SolanaError : unknown;
/**
* Contains context that can assist in understanding or recovering from a {@link SolanaError}.
*/
readonly context: SolanaErrorCodedContext[TErrorCode];
constructor(...[code, contextAndErrorOptions]: SolanaErrorContext[TErrorCode] extends undefined ? [code: TErrorCode, errorOptions?: ErrorOptions | undefined] : [code: TErrorCode, contextAndErrorOptions: SolanaErrorContext[TErrorCode] & (ErrorOptions | undefined)]);
}
export {};
//# sourceMappingURL=error.d.ts.map

View File

@@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-ignore `sass` may not be installed
import type DartSass from 'sass'
// @ts-ignore `sass-embedded` may not be installed
import type SassEmbedded from 'sass-embedded'
// @ts-ignore `less` may not be installed
import type Less from 'less'
// @ts-ignore `stylus` may not be installed
import type Stylus from 'stylus'
/* eslint-enable @typescript-eslint/ban-ts-comment */
// https://github.com/type-challenges/type-challenges/issues/29285
type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false
type DartSassStringOptionsAsync = DartSass.StringOptions<'async'>
type SassEmbeddedStringOptionsAsync = SassEmbedded.StringOptions<'async'>
type SassStringOptionsAsync =
IsAny<SassEmbeddedStringOptionsAsync> extends false
? SassEmbeddedStringOptionsAsync
: DartSassStringOptionsAsync
export type SassModernPreprocessBaseOptions = Omit<
SassStringOptionsAsync,
'url' | 'sourceMap'
>
export type LessPreprocessorBaseOptions = Omit<
Less.Options,
'sourceMap' | 'filename'
>
export type StylusPreprocessorBaseOptions = Omit<
Stylus.RenderOptions,
'filename'
> & { define?: Record<string, any> }
declare global {
// LESS' types somewhat references this which doesn't make sense in Node,
// so we have to shim it
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
interface HTMLLinkElement {}
}

View File

@@ -0,0 +1,18 @@
//#region src/client/env.ts
const context = (() => {
if (typeof globalThis !== "undefined") return globalThis;
else if (typeof self !== "undefined") return self;
else if (typeof window !== "undefined") return window;
else return Function("return this")();
})();
const defines = __DEFINES__;
Object.keys(defines).forEach((key) => {
const segments = key.split(".");
let target = context;
for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
if (i === segments.length - 1) target[segment] = defines[key];
else target = target[segment] || (target[segment] = {});
}
});
//#endregion

View File

@@ -0,0 +1,6 @@
export declare enum NewLineKind {
None = 0,
CRLF = 1,
LF = 2
}
//# sourceMappingURL=newLineKind.enum.d.ts.map

View File

@@ -0,0 +1,11 @@
"use strict";
module.exports = function (it) {
const { pluginName } = it;
return `
ESLint couldn't find the plugin "${pluginName}". because there is whitespace in the name. Please check your configuration and remove all whitespace from the plugin name.
If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.
`.trimStart();
};

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.visitorKeys = exports.getKeys = void 0;
var get_keys_1 = require("./get-keys");
Object.defineProperty(exports, "getKeys", { enumerable: true, get: function () { return get_keys_1.getKeys; } });
var visitor_keys_1 = require("./visitor-keys");
Object.defineProperty(exports, "visitorKeys", { enumerable: true, get: function () { return visitor_keys_1.visitorKeys; } });

View File

@@ -0,0 +1,10 @@
var toPropertyKey = require("./toPropertyKey.js");
function _defineProperty(e, r, t) {
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
module.exports = _defineProperty, module.exports.__esModule = true, module.exports["default"] = module.exports;

View File

@@ -0,0 +1,35 @@
import type * as core from "./core.js";
import type { $ZodType } from "./schemas.js";
export declare const $output: unique symbol;
export type $output = typeof $output;
export declare const $input: unique symbol;
export type $input = typeof $input;
export type $replace<Meta, S extends $ZodType> = Meta extends $output ? core.output<S> : Meta extends $input ? core.input<S> : Meta extends (infer M)[] ? $replace<M, S>[] : Meta extends (...args: infer P) => infer R ? (...args: {
[K in keyof P]: $replace<P[K], S>;
}) => $replace<R, S> : Meta extends object ? {
[K in keyof Meta]: $replace<Meta[K], S>;
} : Meta;
type MetadataType = object | undefined;
export declare class $ZodRegistry<Meta extends MetadataType = MetadataType, Schema extends $ZodType = $ZodType> {
_meta: Meta;
_schema: Schema;
_map: WeakMap<Schema, $replace<Meta, Schema>>;
_idmap: Map<string, Schema>;
add<S extends Schema>(schema: S, ..._meta: undefined extends Meta ? [$replace<Meta, S>?] : [$replace<Meta, S>]): this;
clear(): this;
remove(schema: Schema): this;
get<S extends Schema>(schema: S): $replace<Meta, S> | undefined;
has(schema: Schema): boolean;
}
export interface JSONSchemaMeta {
id?: string | undefined;
title?: string | undefined;
description?: string | undefined;
deprecated?: boolean | undefined;
[k: string]: unknown;
}
export interface GlobalMeta extends JSONSchemaMeta {
}
export declare function registry<T extends MetadataType = MetadataType, S extends $ZodType = $ZodType>(): $ZodRegistry<T, S>;
export declare const globalRegistry: $ZodRegistry<GlobalMeta>;
export {};

View File

@@ -0,0 +1,379 @@
import { beforeEach, describe, expect, test } from "vitest";
import { z } from "../../../../index.js";
import he from "../../../locales/he.js";
describe("Hebrew localization", () => {
beforeEach(() => {
z.config(he());
});
describe("too_small errors with definite article and gendered verbs", () => {
test("string type (feminine - צריכה)", () => {
const schema = z.string().min(3);
const result = schema.safeParse("ab");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קצר מדי: המחרוזת צריכה להכיל 3 תווים או יותר");
}
});
test("number type (masculine - צריך)", () => {
const schema = z.number().min(10);
const result = schema.safeParse(5);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קטן מדי: המספר צריך להיות גדול או שווה ל-10");
}
});
test("array type (masculine - צריך)", () => {
const schema = z.array(z.string()).min(1);
const result = schema.safeParse([]);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קטן מדי: המערך צריך להכיל לפחות פריט אחד");
}
});
test("set type (feminine - צריכה)", () => {
const schema = z.set(z.string()).min(2);
const result = schema.safeParse(new Set(["a"]));
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קטן מדי: הקבוצה (Set) צריכה להכיל 2 פריטים או יותר");
}
});
});
describe("too_big errors with definite article and gendered verbs", () => {
test("string type (feminine - צריכה)", () => {
const schema = z.string().max(3);
const result = schema.safeParse("abcde");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("ארוך מדי: המחרוזת צריכה להכיל 3 תווים או פחות");
}
});
test("number type (masculine - צריך)", () => {
const schema = z.number().max(365);
const result = schema.safeParse(400);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("גדול מדי: המספר צריך להיות קטן או שווה ל-365");
}
});
test("array max", () => {
const schema = z.array(z.string()).max(2);
const result = schema.safeParse(["a", "b", "c"]);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("גדול מדי: המערך צריך להכיל 2 פריטים או פחות");
}
});
});
describe("invalid_type errors with definite article and gendered verbs", () => {
test("string expected (feminine), number received", () => {
const schema = z.string();
const result = schema.safeParse(123);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין: צריך להיות מחרוזת, התקבל מספר");
}
});
test("number expected (masculine), string received", () => {
const schema = z.number();
const result = schema.safeParse("abc");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין: צריך להיות מספר, התקבל מחרוזת");
}
});
test("boolean expected (masculine), null received", () => {
const schema = z.boolean();
const result = schema.safeParse(null);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין: צריך להיות ערך בוליאני, התקבל ערך ריק (null)");
}
});
test("array expected (masculine), object received", () => {
const schema = z.array(z.string());
const result = schema.safeParse({});
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין: צריך להיות מערך, התקבל אובייקט");
}
});
test("object expected (masculine), array received", () => {
const schema = z.object({ a: z.string() });
const result = schema.safeParse([]);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין: צריך להיות אובייקט, התקבל מערך");
}
});
test("function expected (feminine), string received", () => {
const schema = z.function();
const result = schema.safeParse("not a function");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין: צריך להיות פונקציה, התקבל מחרוזת");
}
});
});
describe("gendered verbs consistency", () => {
test("feminine types use צריכה", () => {
const feminineTypes = [
{ schema: z.string().min(5), input: "abc" },
{ schema: z.set(z.string()).min(2), input: new Set(["a"]) },
];
for (const { schema, input } of feminineTypes) {
const result = schema.safeParse(input);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toContain("צריכה");
}
}
});
test("masculine types use צריך", () => {
const masculineTypes = [
{ schema: z.number().min(10), input: 5 },
{ schema: z.array(z.string()).min(2), input: ["a"] },
];
for (const { schema, input } of masculineTypes) {
const result = schema.safeParse(input);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toContain("צריך");
}
}
});
});
describe("invalid_value with enum", () => {
test("single value", () => {
const schema = z.enum(["a"]);
const result = schema.safeParse("b");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('ערך לא תקין: הערך חייב להיות "a"');
}
});
test("two values", () => {
const schema = z.enum(["a", "b"]);
const result = schema.safeParse("c");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('ערך לא תקין: האפשרויות המתאימות הן "a" או "b"');
}
});
test("multiple values", () => {
const schema = z.enum(["a", "b", "c"]);
const result = schema.safeParse("d");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('ערך לא תקין: האפשרויות המתאימות הן "a", "b" או "c"');
}
});
});
describe("other error types", () => {
test("not_multiple_of", () => {
const schema = z.number().multipleOf(3);
const result = schema.safeParse(10);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("מספר לא תקין: חייב להיות מכפלה של 3");
}
});
test("unrecognized_keys - single key", () => {
const schema = z.object({ a: z.string() }).strict();
const result = schema.safeParse({ a: "test", b: "extra" });
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('מפתח לא מזוהה: "b"');
}
});
test("unrecognized_keys - multiple keys", () => {
const schema = z.object({ a: z.string() }).strict();
const result = schema.safeParse({ a: "test", b: "extra", c: "more" });
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('מפתחות לא מזוהים: "b", "c"');
}
});
test("invalid_union", () => {
const schema = z.union([z.string(), z.number()]);
const result = schema.safeParse(true);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין");
}
});
test("invalid_key in object", () => {
const schema = z.record(z.number(), z.string());
const result = schema.safeParse({ notANumber: "value" });
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("שדה לא תקין באובייקט");
}
});
});
describe("invalid_format with string checks", () => {
test("startsWith", () => {
const schema = z.string().startsWith("hello");
const result = schema.safeParse("world");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('המחרוזת חייבת להתחיל ב "hello"');
}
});
test("endsWith", () => {
const schema = z.string().endsWith("world");
const result = schema.safeParse("hello");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('המחרוזת חייבת להסתיים ב "world"');
}
});
test("includes", () => {
const schema = z.string().includes("test");
const result = schema.safeParse("hello world");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe('המחרוזת חייבת לכלול "test"');
}
});
test("regex", () => {
const schema = z.string().regex(/^[a-z]+$/);
const result = schema.safeParse("ABC123");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("המחרוזת חייבת להתאים לתבנית /^[a-z]+$/");
}
});
});
describe("invalid_format with common formats", () => {
test("email", () => {
const schema = z.string().email();
const result = schema.safeParse("not-an-email");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("כתובת אימייל לא תקינה");
}
});
test("url", () => {
const schema = z.string().url();
const result = schema.safeParse("not-a-url");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("כתובת רשת לא תקינה");
}
});
test("uuid", () => {
const schema = z.string().uuid();
const result = schema.safeParse("not-a-uuid");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("UUID לא תקין");
}
});
});
describe("tuple validation", () => {
test("invalid element type in tuple shows full error message", () => {
const schema = z.tuple([z.string(), z.number()]);
const result = schema.safeParse(["abc", "not a number"]);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קלט לא תקין: צריך להיות מספר, התקבל מחרוזת");
}
});
});
describe("inclusive vs exclusive bounds", () => {
test("inclusive minimum (>=)", () => {
const schema = z.number().min(10);
const result = schema.safeParse(5);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קטן מדי: המספר צריך להיות גדול או שווה ל-10");
}
});
test("exclusive minimum (>)", () => {
const schema = z.number().gt(10);
const result = schema.safeParse(10);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("קטן מדי: המספר צריך להיות גדול מ-10");
}
});
test("inclusive maximum (<=)", () => {
const schema = z.number().max(10);
const result = schema.safeParse(15);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("גדול מדי: המספר צריך להיות קטן או שווה ל-10");
}
});
test("exclusive maximum (<)", () => {
const schema = z.number().lt(10);
const result = schema.safeParse(10);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toBe("גדול מדי: המספר צריך להיות קטן מ-10");
}
});
});
describe("all type names with definite article", () => {
test("verifies all type translations are correct", () => {
const types = [
{ schema: z.string(), expected: "מחרוזת", input: 123 },
{ schema: z.number(), expected: "מספר", input: "abc" },
{ schema: z.boolean(), expected: "ערך בוליאני", input: "abc" },
{ schema: z.bigint(), expected: "BigInt", input: "abc" },
{ schema: z.date(), expected: "תאריך", input: "abc" },
{ schema: z.array(z.any()), expected: "מערך", input: "abc" },
{ schema: z.object({}), expected: "אובייקט", input: "abc" },
{ schema: z.function(), expected: "פונקציה", input: "abc" },
];
for (const { schema, expected, input } of types) {
const result = schema.safeParse(input);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0].message).toContain(expected);
}
}
});
});
});

View File

@@ -0,0 +1,150 @@
# @eslint/config-helpers
## Description
Helper utilities for creating ESLint configuration.
## Installation
For Node.js and compatible runtimes:
```shell
npm install @eslint/config-helpers
# or
yarn add @eslint/config-helpers
# or
pnpm install @eslint/config-helpers
# or
bun add @eslint/config-helpers
```
For Deno:
```shell
deno add @eslint/config-helpers
```
## Usage
### `defineConfig()`
The `defineConfig()` function allows you to specify an ESLint configuration with full type checking and additional capabilities, such as `extends`. Here's an example:
```js
// eslint.config.js
import { defineConfig } from "@eslint/config-helpers";
import js from "@eslint/js";
export default defineConfig([
{
files: ["src/**/*.js"],
plugins: { js },
extends: ["js/recommended"],
rules: {
"no-var": "error",
"prefer-const": "error",
},
},
{
files: ["test/**/*.js"],
rules: {
"no-console": "off",
},
},
]);
```
### `globalIgnores()`
The `globalIgnores()` function allows you to specify patterns for files and directories that should be globally ignored by ESLint. This is useful for excluding files that you don't want to lint, such as build directories or third-party libraries. Here's an example:
```js
// eslint.config.js
import { defineConfig, globalIgnores } from "@eslint/config-helpers";
export default defineConfig([
{
files: ["src/**/*.js"],
rules: {
"no-var": "error",
"prefer-const": "error",
},
},
globalIgnores(["node_modules/", "dist/", "coverage/"]),
]);
```
### `includeIgnoreFile()`
The `includeIgnoreFile()` function reads a file with gitignore-style patterns (such as a `.gitignore`) and returns a config object with the patterns converted to a global ignores object. Pass the absolute path to the ignore file as the first argument:
```js
// eslint.config.js
import { defineConfig, includeIgnoreFile } from "@eslint/config-helpers";
import path from "node:path";
const ignorePath = path.join(import.meta.dirname, ".gitignore");
export default defineConfig([
includeIgnoreFile(ignorePath, {
gitignoreResolution: true,
}),
// ...
]);
```
#### Options
The second argument is an optional options object:
- **`gitignoreResolution`** (`boolean`): Controls how ignore patterns are interpreted.
- `false` (default) — patterns are resolved relative to the location of the configuration file.
- `true` — patterns are resolved relative to the location of the ignore file, matching the behavior of `.gitignore` files.
- **`name`** (`string`): A custom name for the resulting config object.
For backwards compatibility with `includeIgnoreFile()` from `@eslint/compat`, passing a string instead of an object as the second argument is treated as equivalent to providing a value for `name`.
#### Multiple files
You can also pass an array of absolute paths to include multiple ignore files at once. In this case an array of config objects is returned:
```js
// eslint.config.js
import { defineConfig, includeIgnoreFile } from "@eslint/config-helpers";
import path from "node:path";
export default defineConfig([
includeIgnoreFile(
[
path.join(import.meta.dirname, ".gitignore"),
path.join(import.meta.dirname, "packages/lib/.gitignore"),
],
{ gitignoreResolution: true },
),
// ...
]);
```
## License
Apache 2.0
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
<!--sponsorsstart-->
## Sponsors
The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
to get your logo on our READMEs and [website](https://eslint.org/sponsors).
<h3>Platinum Sponsors</h3>
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
<p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a> <a href="https://www.coderabbit.ai/?utm_source=cr_org&utm_medium=github"><img src="https://avatars.githubusercontent.com/u/132028505" alt="CodeRabbit" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://citadel-ai.com"><img src="https://avatars.githubusercontent.com/u/75781367" alt="Citadel AI" height="32"></a></p>
<h3>Technology Sponsors</h3>
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
<!--sponsorsend-->

View File

@@ -0,0 +1,46 @@
/**
* @fileoverview Rule to flag nested ternary expressions
* @author Ian Christian Myers
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../types').Rule.RuleModule} */
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "Disallow nested ternary expressions",
recommended: false,
frozen: true,
url: "https://eslint.org/docs/latest/rules/no-nested-ternary",
},
schema: [],
messages: {
noNestedTernary: "Do not nest ternary expressions.",
},
},
create(context) {
return {
ConditionalExpression(node) {
if (
node.alternate.type === "ConditionalExpression" ||
node.consequent.type === "ConditionalExpression"
) {
context.report({
node,
messageId: "noNestedTernary",
});
}
},
};
},
};

View File

@@ -0,0 +1,116 @@
'use strict'
var DATE_TIME = /(\d{1,})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?.*?( BC)?$/
var DATE = /^(\d{1,})-(\d{2})-(\d{2})( BC)?$/
var TIME_ZONE = /([Z+-])(\d{2})?:?(\d{2})?:?(\d{2})?/
var INFINITY = /^-?infinity$/
module.exports = function parseDate (isoDate) {
if (INFINITY.test(isoDate)) {
// Capitalize to Infinity before passing to Number
return Number(isoDate.replace('i', 'I'))
}
var matches = DATE_TIME.exec(isoDate)
if (!matches) {
// Force YYYY-MM-DD dates to be parsed as local time
return getDate(isoDate) || null
}
var isBC = !!matches[8]
var year = parseInt(matches[1], 10)
if (isBC) {
year = bcYearToNegativeYear(year)
}
var month = parseInt(matches[2], 10) - 1
var day = matches[3]
var hour = parseInt(matches[4], 10)
var minute = parseInt(matches[5], 10)
var second = parseInt(matches[6], 10)
var ms = matches[7]
ms = ms ? 1000 * parseFloat(ms) : 0
var date
var offset = timeZoneOffset(isoDate)
if (offset != null) {
date = new Date(Date.UTC(year, month, day, hour, minute, second, ms))
// Account for years from 0 to 99 being interpreted as 1900-1999
// by Date.UTC / the multi-argument form of the Date constructor
if (is0To99(year)) {
date.setUTCFullYear(year)
}
if (offset !== 0) {
date.setTime(date.getTime() - offset)
}
} else {
date = new Date(year, month, day, hour, minute, second, ms)
if (is0To99(year)) {
date.setFullYear(year)
}
}
return date
}
function getDate (isoDate) {
var matches = DATE.exec(isoDate)
if (!matches) {
return
}
var year = parseInt(matches[1], 10)
var isBC = !!matches[4]
if (isBC) {
year = bcYearToNegativeYear(year)
}
var month = parseInt(matches[2], 10) - 1
var day = matches[3]
// YYYY-MM-DD will be parsed as local time
var date = new Date(year, month, day)
if (is0To99(year)) {
date.setFullYear(year)
}
return date
}
// match timezones:
// Z (UTC)
// -05
// +06:30
function timeZoneOffset (isoDate) {
if (isoDate.endsWith('+00')) {
return 0
}
var zone = TIME_ZONE.exec(isoDate.split(' ')[1])
if (!zone) return
var type = zone[1]
if (type === 'Z') {
return 0
}
var sign = type === '-' ? -1 : 1
var offset = parseInt(zone[2], 10) * 3600 +
parseInt(zone[3] || 0, 10) * 60 +
parseInt(zone[4] || 0, 10)
return offset * sign * 1000
}
function bcYearToNegativeYear (year) {
// Account for numerical difference between representations of BC years
// See: https://github.com/bendrucker/postgres-date/issues/5
return -(year - 1)
}
function is0To99 (num) {
return num >= 0 && num < 100
}