From 8989317146f348ed14405184ef89767645b8c0c0 Mon Sep 17 00:00:00 2001 From: tacyarg Date: Wed, 16 Sep 2026 23:26:11 +0000 Subject: [PATCH 1/2] Fix nonce reuse across draws, validate inputs, drop runtime deps A Provable instance built its byte stream once, so repeated floats()/ints() calls kept the original nonce and the emitted state no longer reproduced the outcome. Every draw now opens a stream for the current nonce, then advances and emits; long-lived and re-created instances agree. Byte stream, float and integer conversions and rotation are unchanged and pinned by fixed vectors. Also: validate seeds and integers (canonical decimal strings accepted), always derive serverHash from serverSeed, stop mutating caller objects, fix HashChain asserts and bounds, document HashSeries as immutable, restore the LICENSE holder, replace lodash and uuid with built-ins, add a files field, and rewrite the README to match the algorithm. --- LICENSE | 2 +- README.md | 275 ++++++++++++++++++++++------------------------ hashChain.js | 59 +++++----- hashSeries.js | 27 +++-- package.json | 17 ++- provable.js | 68 +++++++----- test.js | 294 ++++++++++++++++++++++++++++++++++++++++---------- utils.js | 102 ++++++++++++------ 8 files changed, 530 insertions(+), 314 deletions(-) diff --git a/LICENSE b/LICENSE index 244229e..edeafb0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) +Copyright (c) 2012-2026 Provable.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0e7af90..98c04f2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # Provable.IO Core Library -This is a random number generator core library that uses various hashing functions, byte generation, and other utilities. The generator can produce both floating-point and integer numbers with configurable settings. +Provably fair random number generation. A server seed, a client seed and a +nonce are combined with HMAC-SHA256 into a byte stream, the bytes become +floats in `[0, 1)`, and the floats become game outcomes. Anyone holding the +three inputs can recompute every result. The package also ships a hash chain +and a hash series for games that commit to a sequence of hashes in advance. + +No runtime dependencies. Requires Node.js 14.17 or later. ## Install @@ -15,218 +21,191 @@ npm install @provableio/provable-core ## Example ```javascript -const { - Provable, - HashChain, - HashSeries, - utils, -} = require("@provableio/provable-core"); - -const config = { +const { Provable } = require("@provableio/provable-core"); + +const generator = Provable((state) => { + // called after every draw with the new state; persist it here + console.log(state); +})({ serverSeed: "your-server-seed", clientSeed: "your-client-seed", nonce: 0, - cursor: 0, -}; - -const generator = Provable((x) => { - // update database? - console.log(x) -})(config); +}); -// Generate 5 Floating point outcomes -const randomFloats = generator.floats(5); -console.log("floats:", randomFloats); +// 5 floats in [0, 1) +console.log("floats:", generator.floats(5)); -// generate 10 outcomes between 0 - 100 -const randomInts = generator.ints(10, 100, 0); -console.log("ints:", randomInts); +// 10 integers in [0, 99] +console.log("ints:", generator.ints(10, 100, 0)); ``` -The `config` object contains the following properties: -- `serverSeed` (string): The server seed used for generating random numbers. -- `clientSeed` (string): The client seed used for generating random numbers. -- `nonce` (number): The current nonce value. -- `cursor` (number): The current cursor value. +## How outcomes are generated -## Available methods +For a draw with nonce `N`: -The Provable instance has the following methods: +1. `round = 0, 1, 2, ...` Each round produces 32 bytes: + `HMAC-SHA256(key = serverSeed, message = "clientSeed:N:round")`. +2. Bytes are consumed four at a time. Each group becomes a float: + `b0/256 + b1/256^2 + b2/256^3 + b3/256^4`, a value in `[0, 1)` with 32 bits + of precision. +3. `ints(count, max, min)` maps each float to `floor(min + float * max)`, an + integer in `[min, min + max - 1]`. **`max` is the size of the range, not an + upper bound.** +4. After the draw the nonce becomes `N + 1` and the new state is emitted. -### next(salt, clientSeed) +Every draw uses the nonce as it stands and then advances it, so the outcome +recorded against nonce `N` is always reproducible from +`(serverSeed, clientSeed, N)` alone, whether the generator instance was reused +or re-created from persisted state before each call. -Returns an object containing the next `clientSeed` and `serverSeed` values based on the provided `salt` and `clientSeed` values. If `clientSeed` is not provided, it will be generated using the `md5` hashing function. +Before a seed pair is used, publish `serverHash = sha256(serverSeed)`. Reveal +`serverSeed` when the pair is rotated; players check the hash and recompute +their outcomes. -```javascript -const nextValues = generator.next("salt-value"); -console.log(nextValues); // { clientSeed: "generated-client-seed", serverSeed: "generated-server-seed" } -``` +## Provable(emit)(config) -### state() +`emit` is optional. It receives a copy of the state after every draw. -Returns the current configuration object. +`config` accepts: -```javascript -const currentConfig = generator.state(); -console.log(currentConfig); // { clientSeed: "your-client-seed", serverSeed: "your-server-seed", nonce: 0, emit: [Function: emit] } -``` +| key | default | notes | +| ------------ | ------------------- | ------------------------------------------------- | +| `serverSeed` | `sha256(uuid)` | non-empty string, kept private until rotation | +| `clientSeed` | `md5(uuid)` | non-empty string, chosen or visible to the player | +| `nonce` | `0` | non-negative safe integer | +| `cursor` | `0` | byte offset into the stream, normally `0` | +| `serverHash` | derived | always recomputed as `sha256(serverSeed)` | -### floats(count) +Integers may be passed as canonical decimal strings (`"400"`); anything else +throws. Extra keys (ids, timestamps) are carried through untouched. The config +object you pass in is never mutated. + +### state() -Generates `count` number of random floating-point numbers using the configured generator. The `count` argument is optional and defaults to `1`. +Returns a copy of the current state. ```javascript -const randomFloats = generator.floats(5); -console.log(randomFloats); // [0.123456789, 0.234567890, 0.345678901, 0.4567890123, 0.56789012345] +generator.state(); +// { serverSeed, clientSeed, serverHash, nonce, cursor } ``` -The generator's configuration is updated after generating the random numbers, and the `nonce` value is incremented. If an `emit` callback function is provided, it will be called with the updated configuration. +### floats(count = 1) -### ints(count, max, min) +Returns `count` floats in `[0, 1)` for the current nonce, then advances the +nonce and emits. -Generates `count` number of random integer numbers within the range of `min` and `max` (inclusive) using the configured generator. The `count`, `max`, and `min` arguments are optional and default to `1`, `100`, and `0`, respectively. +### ints(count, max, min = 0) + +Returns `count` integers in `[min, min + max - 1]` for the current nonce, then +advances the nonce and emits. `count` and `max` must be at least 1. ```javascript -const randomInts = generator.ints(10, 100, 0); -console.log(randomInts); // [100, 25, 23, 22, 21, 20, 19, 18, 17, 16] +generator.ints(1, 6, 1); // one die roll, 1..6 +generator.ints(1, 10001, 0); // one value 0..10000 ``` -The generator's configuration is updated after generating the random numbers, and the `nonce` value is incremented. If an `emit` callback function is provided, it will be called with the updated configuration. - ### tick() -Increments the `nonce` value and emits the updated configuration (if an `emit` callback function is provided). - -```javascript -generator.tick(); -``` +Advances the nonce without drawing, emits, and returns the new state. -## Additional Generators +### next(salt, clientSeed) -### HashSeries({ seed, salt, nonce }) +Returns the state for the next seed rotation. Nonce and cursor restart at 0. -This class is used to generate a series of hashes based on the provided `seed`, `salt`, and `nonce` values. It has the following methods: +- `serverSeed` becomes `sha256(":")`. +- `clientSeed` is the one supplied, or `md5(":")`. -- `getHash()`: Returns the current hash value. -- `next()`: Increments the `nonce` value and returns a new object with the updated `seed`, `salt`, and `nonce`. -- `peekHash()`: Returns the next hash value based on the updated `nonce` value. -- `calcHash(_seed, _salt, _nonce)`: Calculates the hash value using the provided `seed`, `salt`, and `nonce` values. -- `state()`: Returns an object containing the `seed`, `salt`, and `nonce` values. +Keep `salt` private. Because the next server seed is derived from the current +one, a player who knows the salt could predict the successor of a revealed seed. ```javascript -const hashSeries = new HashSeries({ seed: "your-seed", salt: "your-salt", nonce: 0 }); -console.log(hashSeries.getHash()); // Generated hash value -console.log(hashSeries.next()); // { seed: "your-seed", salt: "your-salt", nonce: 1 } -console.log(hashSeries.peekHash()); // Generated hash value based on nonce + 1 +const rotated = generator.next("private-salt", "player-chosen-seed"); +// { serverSeed, clientSeed, serverHash, nonce: 0, cursor: 0 } ``` -### HashChain({ seed, count, index }) - -This class is used to manage generating and iterating through a provable hash chain. It has the following methods: - -- `state()`: Returns an object containing the `count`, `seed`, and `index` values. -- `peek()`: Returns the next hash value in the chain based on the current `index`. -- `get()`: Returns the current hash value in the chain based on the current `index`. -- `next()`: Returns an object containing the next hash value in the chain, the `count`, and the updated `index`. -- `last()`: Returns the previous hash value in the chain based on the current `index`. +### Verifying an outcome ```javascript -const hashChain = new HashChain({ seed: "your-seed", count: 10 }); -console.log(hashChain.state()); // Generated state object -console.log(hashChain.peek()); // Generated next hash value in the chain -console.log(hashChain.get()); // Generated current hash value in the chain -console.log(hashChain.next()); // Generated next hash value in the chain, count, and updated index -console.log(hashChain.last()); // Generated previous hash value in the chain -``` - - -## Additional Utilities +const { Provable } = require("@provableio/provable-core"); -### defaults(state) - -This function sets default values for the `state` object and returns it. If the `state` object is missing some properties, they will be set to the default values. - -```javascript -const defaultState = defaults({}); -console.log(defaultState); // { serverSeed: sha256(), clientSeed: md5(), nonce: 0, cursor: 0, serverHash: sha256(serverSeed) } +function verify({ serverSeed, serverHash, clientSeed, nonce }) { + const generator = Provable()({ serverSeed, clientSeed, nonce }); + if (generator.state().serverHash !== serverHash) throw new Error("hash mismatch"); + return generator.ints(1, 10001, 0)[0]; // same call the game made +} ``` -### sha256(input) +## HashSeries({ seed, salt, nonce }) -This function generates a SHA-256 hash value for the provided `input` string. If no `input` is provided, it will generate a hash value for a UUID. +A series of hashes `HMAC-SHA256(key = seed, message = "salt:nonce")`. +Instances are immutable; `next()` returns the state for the following nonce. ```javascript -const hash = sha256("your-input"); -console.log(hash); // Generated SHA-256 hash value -``` - -### md5(input) +const { HashSeries } = require("@provableio/provable-core"); -This function generates an MD5 hash value for the provided `input` string. If no `input` is provided, it will generate a hash value for a UUID. - -```javascript -const hash = md5("your-input"); -console.log(hash); // Generated MD5 hash value +const series = HashSeries({ seed: "your-seed", salt: "your-salt", nonce: 0 }); +series.getHash(); // hash for nonce 0 +series.peekHash(); // hash for nonce 1 +series.next(); // { seed, salt, nonce: 1 } +HashSeries(series.next()).getHash(); // === series.peekHash() +series.calcHash(seed, salt, nonce); // hash for arbitrary inputs +series.state(); // { seed, salt, nonce } ``` -### ByteGenerator({ serverSeed, clientSeed, nonce, cursor }) - -This generator function produces a sequence of bytes based on the provided `serverSeed`, `clientSeed`, `nonce`, and `cursor` values. It uses the SHA-256 hashing function to generate bytes and yields them one by one. - -```javascript -const byteGenerator = new ByteGenerator({ serverSeed: "your-server-seed", clientSeed: "your-client-seed", nonce: 0, cursor: 0 }); -for (let byte of byteGenerator) { - console.log(byte); // Yielded bytes -} -``` +## HashChain({ seed, count = 1000000, index = 0 }) -### bytesToFloat(bytes) +Generates a chain of `count` SHA-256 hashes from `seed` and iterates it. +`chain[count - 1] = sha256(seed)` and `chain[i] = sha256(chain[i + 1])`, so +`chain[0]` is the terminating hash to publish before play, and every hash +handed out afterwards is the preimage of the one before it. A player checks +game `i` with `sha256(hash[i]) === hash[i - 1]`. -This function converts an array of bytes to a floating-point number. +Generating the default one-million-hash chain takes about two seconds and +roughly 90 MB. Build it once at start-up and resume from `state()`. ```javascript -const float = bytesToFloat([128, 64, 32, 0]); -console.log(float); // Generated float value -``` +const { HashChain } = require("@provableio/provable-core"); -### floatToInt(val, max, min) +const chain = HashChain({ seed: "your-secret-seed", count: 10 }); +chain.get(); // current hash (chain[index]) +chain.peek(); // next hash, or undefined at the end +chain.next(); // { hash, count, index } and advances; throws "chain has ended" at the end +chain.last(); // previous hash, or undefined at the start +chain.state(); // { count, seed, index } — includes the seed, persist privately -This function converts a floating-point number to an integer within the range of `min` and `max` (inclusive). - -```javascript -const int = floatToInt(0.5, 100, 0); -console.log(int); // Generated integer value +HashChain(chain.state()); // resumes the same chain at the same index ``` -### FloatGenerator(rng, count) +The chain does not renew itself. When `next()` throws, decide how to commit to +the next chain (for example, publish its terminating hash) before using it. -This generator function produces a sequence of floating-point numbers based on the provided `rng` (random number generator) and `count` values. It uses the `ByteGenerator` to generate bytes and converts them to floating-point numbers using the `bytesToFloat` function. +`HashChain.generateHashChain(count, seed)` returns the raw array. -```javascript -const floatGenerator = new FloatGenerator(byteGenerator, 10); -for (let float of floatGenerator) { - console.log(float); // Yielded floating-point numbers -} -``` - -### floats(rng, count) - -This function generates an array of `count` number of random floating-point numbers using the provided `rng` (random number generator). +## utils ```javascript -const randomFloats = floats(byteGenerator, 5); -console.log(randomFloats); // Generated array of floating-point numbers +const { utils } = require("@provableio/provable-core"); ``` -### ints(rng, count, max, min) - -This function generates an array of `count` number of random integer numbers within the range of `min` and `max` (inclusive) using the provided `rng` (random number generator). +- `sha256(input)`, `md5(input)`: hex digests. With no input, hash a random UUID. +- `defaults(state)`: normalise and validate a Provable state (see the config + table above). Returns a new object with `serverHash` derived. +- `ByteGenerator({ serverSeed, clientSeed, nonce, cursor })`: generator function + yielding the HMAC byte stream for a fixed nonce. Call it, do not `new` it. +- `FloatGenerator(rng, count)`: generator function turning four bytes at a time + from `rng` into floats. +- `floats(rng, count)`, `ints(rng, count, max, min = 0)`: array forms of the above. +- `bytesToFloat([b0, b1, b2, b3])`, `floatToInt(float, max, min = 0)`: the two + conversions described in "How outcomes are generated". +- `toInteger(value, name)`, `assertSeed(value, name)`: the validators used + throughout. ```javascript -const randomInts = ints(byteGenerator, 10, 50, -25); -console.log(randomInts); // Generated array of integer numbers +const rng = utils.ByteGenerator({ serverSeed, clientSeed, nonce: 0, cursor: 0 }); +utils.floats(rng, 2); // two floats from the start of the stream +utils.ints(rng, 3, 6, 1); // three dice rolls from the bytes that follow ``` ## License -This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. +MIT. See [LICENSE](LICENSE). diff --git a/hashChain.js b/hashChain.js index a6ec5db..974c5be 100644 --- a/hashChain.js +++ b/hashChain.js @@ -1,28 +1,37 @@ const assert = require("assert"); -const { v4: uuid } = require("uuid"); -const { sha256 } = require("./utils"); +const { sha256, toInteger, assertSeed, randomUUID } = require("./utils"); -// generate a provable hash chain count to -1 -function generateHashChain(count, seed) { - assert(seed, "requires seed"); - assert(seed, "requires count"); +function chainLength(count) { + count = toInteger(count, "count"); + assert(count >= 1, "count must be 1 or more"); + return count; +} - var result = Array(count); +// Builds the chain in reverse: result[count - 1] = sha256(seed) and +// result[i] = sha256(result[i + 1]). result[0] is the terminating hash to +// publish before play; every later hash is the preimage of the one before it, +// so a player verifies hash i with sha256(hash[i]) === hash[i - 1]. +function generateHashChain(count, seed) { + count = chainLength(count); + assertSeed(seed, "seed"); - // generate chain in revese order - for (var i = count - 1; i >= 0; i--) { + const result = Array(count); + for (let i = count - 1; i >= 0; i--) { seed = sha256(seed); result[i] = seed; } - return result; } -// manages generating and itteratng through a provable hashchain -// the last hash of the previous chain will be used to generate the next. -function HashChain({ seed = uuid(), count = 1000000, index = 0 }) { - assert(count >= 1, "requires count"); - assert(seed, "requires seed"); +// Generates and iterates a provable hash chain. `state()` is enough to resume +// the same chain later; it includes the seed, so persist it privately. +// The chain does not renew itself: when `next()` reports it has ended, the +// caller decides how to commit to and start the following chain. +function HashChain({ seed = randomUUID(), count = 1000000, index = 0 } = {}) { + count = chainLength(count); + index = toInteger(index, "index"); + assert(index >= 0 && index < count, "index must be within the chain"); + assertSeed(seed, "seed"); const chain = generateHashChain(count, seed); @@ -30,6 +39,7 @@ function HashChain({ seed = uuid(), count = 1000000, index = 0 }) { return { count, seed, index }; } + // Next hash, or undefined at the end of the chain. function peek() { return chain[index + 1]; } @@ -41,26 +51,17 @@ function HashChain({ seed = uuid(), count = 1000000, index = 0 }) { function next() { const hash = peek(); assert(hash, "chain has ended"); - - // increment index and return hash - return { - hash, - count, - index: ++index, - }; + index += 1; + return { hash, count, index }; } + // Previous hash, or undefined at the start of the chain. function last() { return chain[index - 1]; } - return { - state, - peek, - get, - next, - last, - }; + return { state, peek, get, next, last }; } module.exports = HashChain; +module.exports.generateHashChain = generateHashChain; diff --git a/hashSeries.js b/hashSeries.js index 8112683..191fe26 100644 --- a/hashSeries.js +++ b/hashSeries.js @@ -1,7 +1,16 @@ -const { v4: uuid } = require("uuid"); +const assert = require("assert"); const crypto = require("crypto"); +const { toInteger, assertSeed, randomUUID } = require("./utils"); + +// A series of hashes HMAC-SHA256(key = seed, message = `salt:nonce`). +// Instances are immutable: `next()` returns the state for the following +// nonce, which you pass to a new HashSeries. +function HashSeries({ seed = randomUUID(), salt = randomUUID(), nonce = 0 } = {}) { + assertSeed(seed, "seed"); + assertSeed(salt, "salt"); + nonce = toInteger(nonce, "nonce"); + assert(nonce >= 0, "nonce must be 0 or more"); -function HashSeries({ seed = uuid(), salt = uuid(), nonce = 0 } = {}) { function calcHash(_seed = seed, _salt = salt, _nonce = nonce) { return crypto .createHmac("sha256", _seed) @@ -15,23 +24,13 @@ function HashSeries({ seed = uuid(), salt = uuid(), nonce = 0 } = {}) { return { seed, salt, nonce }; } function next() { - return { - seed, - salt, - nonce: nonce + 1, - }; + return { seed, salt, nonce: nonce + 1 }; } function peekHash() { return calcHash(seed, salt, nonce + 1); } - return { - getHash, - next, - peekHash, - calcHash, - state, - }; + return { getHash, next, peekHash, calcHash, state }; } module.exports = HashSeries; diff --git a/package.json b/package.json index 403efd5..58a23c5 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,19 @@ "name": "@provableio/provable-core", "packageManager": "yarn@3.3.0", "version": "1.0.1", + "description": "Provably fair random number generation: HMAC-SHA256 byte streams, hash chains and hash series.", "main": "index.js", - "dependencies": { - "lodash": "^4.17.21", - "uuid": "^9.0.1" + "files": [ + "index.js", + "provable.js", + "hashChain.js", + "hashSeries.js", + "utils.js" + ], + "engines": { + "node": ">=14.17.0" }, + "dependencies": {}, "devDependencies": { "tape": "^5.0.1" }, @@ -21,8 +29,9 @@ "keywords": [ "rng", "provable", + "provably-fair", "random", - "reproducable", + "reproducible", "fair", "hash", "chain", diff --git a/provable.js b/provable.js index 661df5c..d46d03a 100644 --- a/provable.js +++ b/provable.js @@ -3,56 +3,70 @@ const { md5, sha256, ByteGenerator, - // bytesToFloat, floats, ints, defaults, + toInteger, + assertSeed, } = require("./utils"); +const MAX_NONCE = Number.MAX_SAFE_INTEGER; + +function positiveInteger(value, name) { + const result = toInteger(value, name); + assert(result >= 1, `${name} must be 1 or more`); + return result; +} + +// Provable(emit)(config) -> generator +// +// Every draw hashes with the nonce as it currently stands, then advances the +// nonce and emits the new state. A fresh byte stream is opened for each draw, +// so a long-lived instance yields exactly the same results as re-creating one +// from the emitted state before every call. Outcome for nonce N is therefore +// always reproducible from (serverSeed, clientSeed, N) alone. module.exports = (emit = (x) => x) => (config) => { config = defaults(config); - const generator = ByteGenerator(config); + + function draw(fn) { + assert(config.nonce + 1 < MAX_NONCE, "max nonce, rotate seed."); + const result = fn(ByteGenerator(config)); + config.nonce += 1; + emit({ ...config }); + return result; + } return { + // Derives the seeds for the next rotation. The new server seed depends + // on the current one and `salt`; keep `salt` private so a revealed seed + // does not disclose its successor. Nonce and cursor restart at 0. next(salt, clientSeed) { + assertSeed(salt, "salt"); + if (clientSeed) assertSeed(clientSeed, "clientSeed"); return defaults({ clientSeed: clientSeed || md5(`${config.clientSeed}:${salt}`), serverSeed: sha256(`${config.serverSeed}:${salt}`), }); }, state() { - return config; + return { ...config }; }, floats(count = 1) { - ++config.nonce; - assert( - Number.MAX_SAFE_INTEGER !== config.nonce, - "max nonce, rotate seed." - ); - const result = floats(generator, count); - emit(config); - return result; + count = positiveInteger(count, "count"); + return draw((rng) => floats(rng, count)); }, - ints(count, max, min) { - ++config.nonce; - assert( - Number.MAX_SAFE_INTEGER !== config.nonce, - "max nonce, rotate seed." - ); - const result = ints(generator, count, max, min); - emit(config); - return result; + // `count` integers in [min, min + max - 1]. `max` is the range size. + ints(count, max, min = 0) { + count = positiveInteger(count, "count"); + max = positiveInteger(max, "max"); + min = toInteger(min, "min"); + return draw((rng) => ints(rng, count, max, min)); }, tick() { - ++config.nonce; - assert( - Number.MAX_SAFE_INTEGER !== config.nonce, - "max nonce, rotate seed." - ); - emit(config); - return config; + draw(() => undefined); + return { ...config }; }, }; }; diff --git a/test.js b/test.js index abeeb91..1919f69 100644 --- a/test.js +++ b/test.js @@ -1,67 +1,217 @@ const test = require("tape"); -const { Provable, HashChain, HashSeries } = require("./index"); +const { Provable, HashChain, HashSeries, utils } = require("./index"); -console.log({ - Provable, - HashChain, - HashSeries, -}); - -let config = { +// Fixed vectors. These pin the byte stream, so any change here is a breaking +// change for every outcome ever produced with this library. +const base = { clientSeed: "bba625387fb64d772ff7da0ed6e71b16", - created: 1597620193906, - cursor: 0, - id: "d2026ad4-480c-4d30-b963-9e3a59c5e22d", + serverSeed: "aa47ddbf021afd16d64756e7f32b6cea2feebdfddc8fd57f1522641fdf372375", + serverHash: "158e995ed494f58c06d93e22f71554cf2126614174fde336aedf9a457dbf16f4", nonce: 400, - serverHash: - "158e995ed494f58c06d93e22f71554cf2126614174fde336aedf9a457dbf16f4", - serverSeed: - "aa47ddbf021afd16d64756e7f32b6cea2feebdfddc8fd57f1522641fdf372375", - updated: 1598719893207, + cursor: 0, }; +// ints(1, 10001, 0) for nonces 400, 401, 402, ... const answers = [ 3842, 9426, 5011, 9503, 1378, 9933, 9048, 9339, 4585, 7847, 2357, 9807, 8488, 5542, ]; + +test("exports", (t) => { + t.equal(typeof Provable, "function"); + t.equal(typeof HashChain, "function"); + t.equal(typeof HashSeries, "function"); + t.equal(typeof utils.ByteGenerator, "function"); + t.end(); +}); + +test("byte stream vectors", (t) => { + const gen = utils.ByteGenerator(base); + const bytes = Array.from({ length: 8 }, () => gen.next().value); + t.deepEqual(bytes, [98, 92, 27, 175, 16, 51, 213, 32]); + t.equal(utils.sha256(base.serverSeed), base.serverHash); + t.end(); +}); + test("provable", (t) => { - let provable; - t.test("init", (t) => { - provable = Provable((x) => console.log(x))(config); - t.ok(provable); + t.test("ints, fresh instance per nonce", (t) => { + let config = { ...base }; + answers.forEach((answer, i) => { + const provable = Provable((x) => (config = x))(config); + const [result] = provable.ints(1, 10001, 0); + t.equal(result, answer, `nonce ${base.nonce + i}`); + t.equal(config.nonce, base.nonce + i + 1, "emitted nonce advanced"); + }); t.end(); }); - t.test("floats", (t) => { - answers.forEach((answer) => { - let provable = Provable((x) => (config = x))(config); - const [result] = provable.ints(1, 10001, 0); - // const [result] = provable.floats(1) - // t.equal(Math.round(result * 10000),answer) - t.equal(result, answer); - console.log(result); + + t.test("ints and floats from one long-lived instance match replay", (t) => { + const emitted = []; + const provable = Provable((x) => emitted.push(x))(base); + const live = answers.map(() => provable.ints(1, 10001, 0)[0]); + t.deepEqual(live, answers, "long-lived instance advances the nonce"); + + emitted.forEach((state, i) => { + t.equal(state.nonce, base.nonce + i + 1, "emitted state describes the next draw"); + // the draw that produced live[i] used the nonce before increment + const replay = Provable()({ ...state, nonce: state.nonce - 1 }).ints(1, 10001, 0)[0]; + t.equal(replay, live[i], `replay of draw ${i} from emitted state`); }); t.end(); }); + + t.test("floats vectors", (t) => { + t.deepEqual(Provable()(base).floats(4), [ + 0.3842179586645216, 0.06329090148210526, 0.9394600219093263, + 0.015737906098365784, + ]); + t.deepEqual(Provable()({ ...base, nonce: 401 }).floats(1), [ + 0.9425638655666262, + ]); + t.deepEqual(Provable()(base).floats(), Provable()(base).floats(1), "count defaults to 1"); + t.end(); + }); + + t.test("ints range is [min, min + max - 1]", (t) => { + t.deepEqual(Provable()(base).ints(3, 6, 1), [3, 1, 6]); + const many = Provable()(base).ints(500, 6, 1); + t.ok(many.every((n) => n >= 1 && n <= 6), "within range"); + t.ok(many.includes(1) && many.includes(6), "hits both ends"); + t.deepEqual(Provable()(base).ints(1, 10001), Provable()(base).ints(1, 10001, 0), "min defaults to 0"); + t.deepEqual(Provable()(base).ints(2, 10, -5).map((n) => n >= -5 && n <= 4), [true, true]); + t.equal(utils.floatToInt(0.99999999, 6, 1), 6); + t.equal(utils.floatToInt(0, 6, 1), 1); + t.end(); + }); + + t.test("cursor offsets the byte stream", (t) => { + t.deepEqual(Provable()({ ...base, cursor: 4 }).ints(1, 10001, 0), [632]); + t.deepEqual(Provable()({ ...base, cursor: 32 }).ints(1, 10001, 0), Provable()(base).ints(9, 10001, 0).slice(8)); + t.end(); + }); + + t.test("integer-like strings are accepted, anything else is rejected", (t) => { + t.deepEqual(Provable()({ ...base, nonce: "400" }).ints(1, 10001, 0), [answers[0]]); + t.deepEqual(Provable()(base).ints("1", "10001", "0"), [answers[0]]); + t.equal(Provable()({ ...base, nonce: null }).state().nonce, 0, "null means missing"); + for (const nonce of ["abc", "1e3", 1.5, -1, NaN, {}, [], Infinity, true]) { + t.throws(() => Provable()({ ...base, nonce }), /nonce/, `nonce ${String(nonce)}`); + } + t.throws(() => Provable()({ ...base, cursor: -1 }), /cursor/); + t.throws(() => Provable()({ ...base, serverSeed: 123 }), /serverSeed/); + t.throws(() => Provable()({ ...base, clientSeed: "" }), /clientSeed/); + t.throws(() => Provable()({ ...base, clientSeed: {} }), /clientSeed/); + t.throws(() => Provable()(base).ints(), /count/); + t.throws(() => Provable()(base).ints(0, 10), /count/); + t.throws(() => Provable()(base).ints(1), /max/); + t.throws(() => Provable()(base).ints(1, 0), /max/); + t.throws(() => Provable()(base).ints(1, 10, 1.5), /min/); + t.throws(() => Provable()(base).floats(0), /count/); + t.throws(() => Provable()(base).floats(2.5), /count/); + t.end(); + }); + + t.test("serverHash is always derived from serverSeed", (t) => { + const state = Provable()({ ...base, serverHash: "bogus" }).state(); + t.equal(state.serverHash, base.serverHash); + t.equal(Provable()({ serverSeed: "s" }).state().serverHash, utils.sha256("s")); + t.end(); + }); + + t.test("defaults fill missing seeds and carry unknown keys", (t) => { + const state = Provable()({ id: "abc" }).state(); + t.equal(state.id, "abc"); + t.equal(state.nonce, 0); + t.equal(state.cursor, 0); + t.equal(state.serverSeed.length, 64); + t.equal(state.clientSeed.length, 32); + t.equal(state.serverHash, utils.sha256(state.serverSeed)); + t.notEqual(Provable()({}).state().serverSeed, Provable()({}).state().serverSeed); + t.end(); + }); + + t.test("input is not mutated and state() is a copy", (t) => { + const input = { ...base }; + const provable = Provable()(input); + provable.tick(); + t.equal(input.nonce, base.nonce, "input untouched"); + const snapshot = provable.state(); + snapshot.nonce = 0; + t.equal(provable.state().nonce, base.nonce + 1, "state() copy"); + t.end(); + }); + + t.test("tick advances the nonce and emits", (t) => { + let emitted; + const provable = Provable((x) => (emitted = x))(base); + const state = provable.tick(); + t.equal(state.nonce, base.nonce + 1); + t.equal(emitted.nonce, base.nonce + 1); + t.deepEqual(provable.floats(1), Provable()({ ...base, nonce: base.nonce + 1 }).floats(1)); + t.end(); + }); + + t.test("max nonce", (t) => { + const provable = Provable()({ ...base, nonce: Number.MAX_SAFE_INTEGER - 1 }); + t.throws(() => provable.floats(), /max nonce/); + t.equal(provable.state().nonce, Number.MAX_SAFE_INTEGER - 1, "state unchanged on failure"); + t.doesNotThrow(() => Provable()({ ...base, nonce: Number.MAX_SAFE_INTEGER - 2 }).floats()); + t.end(); + }); + + t.test("next derives the rotation deterministically", (t) => { + const next = Provable()(base).next("salt-1"); + t.deepEqual(next, { + clientSeed: "6d342ac2abade213ef60ecbb7eee012b", + serverSeed: "80ce19c2df995ac44e2882e13df00d0be5e4910968c6de323d09f60c0aafcf04", + serverHash: "0108879296bb4617d3b101e3fc1d71eae17bba095c962d22645e4f93012c72f0", + nonce: 0, + cursor: 0, + }); + t.equal(next.serverSeed, utils.sha256(`${base.serverSeed}:salt-1`)); + t.equal(next.clientSeed, utils.md5(`${base.clientSeed}:salt-1`)); + const custom = Provable()(base).next("salt-1", "myclient"); + t.equal(custom.clientSeed, "myclient"); + t.equal(custom.serverSeed, next.serverSeed); + t.equal(Provable()(base).next("salt-1", "").clientSeed, next.clientSeed, "empty client seed falls back"); + t.throws(() => Provable()(base).next(), /salt/); + t.throws(() => Provable()(base).next("salt-1", 5), /clientSeed/); + t.end(); + }); }); -test("utils", (t) => { - t.test("hashseries", (t) => { - const series = HashSeries(); - const hash = series.getHash(); - t.ok(hash); - const next = series.peekHash(); - const nextSeries = HashSeries(series.next()); - t.equal(next, nextSeries.getHash()); + +test("hashseries", (t) => { + t.test("vectors", (t) => { + const series = HashSeries({ seed: "seed", salt: "salt", nonce: 0 }); + t.equal(series.getHash(), "18e6774026b67fdf7687651023db21e4aa7259889871fbad4daed033699a5651"); + t.equal(series.peekHash(), "593210434386ec6d9cd6cef9bab11c57abe76ce0b46ff9cdde19051836bfe1d1"); + t.equal(series.calcHash("seed", "salt", 1), series.peekHash()); t.end(); }); - t.test("hashseries", (t) => { + + t.test("next returns the following state without mutating", (t) => { const series = HashSeries(); - const result = series.state(); - t.ok(result); + const peek = series.peekHash(); + const following = HashSeries(series.next()); + t.equal(following.getHash(), peek); + t.equal(series.state().nonce, 0); + t.equal(following.state().nonce, 1); + t.deepEqual(Object.keys(series.state()).sort(), ["nonce", "salt", "seed"]); + t.end(); + }); + + t.test("validation", (t) => { + t.doesNotThrow(() => HashSeries()); + t.equal(HashSeries({ seed: "a", salt: "b", nonce: "3" }).state().nonce, 3); + t.throws(() => HashSeries({ seed: "" }), /seed/); + t.throws(() => HashSeries({ salt: 1 }), /salt/); + t.throws(() => HashSeries({ nonce: -1 }), /nonce/); + t.throws(() => HashSeries({ nonce: 1.5 }), /nonce/); t.end(); }); }); -// hash chain seed "test" should match these +// hash chain with seed "test" and count 10 const chainHashes = [ "bc89c6f72947bcd2f783d342a46cafcfccfcc2e7884a34f1cfe8f55bad2d200e", "d36e4f43c5243135e038611e679adee4bf197290e84e0203727cb6761929e072", @@ -76,28 +226,58 @@ const chainHashes = [ ]; test("hashchain", (t) => { - t.test("init", (t) => { + t.test("vectors and iteration", (t) => { const chain = HashChain({ count: 10, seed: "test", index: 0 }); - - // tests - const hash = chain.get(); - t.ok(hash); - t.equal(hash, chainHashes[0]); - - const peek = chain.peek(); - t.notEqual(hash, peek); - t.equal(peek, chainHashes[1]); - + t.equal(chain.get(), chainHashes[0]); + t.equal(chain.peek(), chainHashes[1]); + t.equal(chain.last(), undefined, "nothing before the first hash"); const next = chain.next(); - t.equal(next.hash, peek); + t.deepEqual(next, { hash: chainHashes[1], count: 10, index: 1 }); + t.equal(chain.get(), chainHashes[1]); + t.equal(chain.last(), chainHashes[0]); + t.deepEqual(chain.state(), { count: 10, seed: "test", index: 1 }); + t.end(); + }); - const last = chain.last(); - t.equal(hash, last); + t.test("every hash is the preimage of the one before it", (t) => { + t.equal(chainHashes[9], utils.sha256("test")); + for (let i = 1; i < chainHashes.length; i++) { + t.equal(utils.sha256(chainHashes[i]), chainHashes[i - 1], `hash ${i}`); + } + t.deepEqual(HashChain.generateHashChain(10, "test"), chainHashes); + t.end(); + }); - const state = chain.state(); - console.log("chain state:", state); - t.ok(state); + t.test("resumes from state", (t) => { + const chain = HashChain({ count: 10, seed: "test" }); + chain.next(); + chain.next(); + const resumed = HashChain(chain.state()); + t.equal(resumed.get(), chain.get()); + t.equal(resumed.peek(), chain.peek()); + t.equal(resumed.next().hash, chainHashes[3]); + t.end(); + }); + + t.test("end of chain", (t) => { + const chain = HashChain({ count: 3, seed: "x", index: 1 }); + t.equal(chain.next().index, 2); + t.equal(chain.peek(), undefined); + t.throws(() => chain.next(), /chain has ended/); + t.equal(chain.state().index, 2, "index unchanged after failure"); + t.equal(chain.get(), utils.sha256("x")); + t.end(); + }); + t.test("validation", (t) => { + t.doesNotThrow(() => HashChain({ count: 1 })); + t.equal(HashChain({ count: "5", index: "2", seed: "x" }).state().index, 2); + t.throws(() => HashChain({ seed: "x", count: 0 }), /count/); + t.throws(() => HashChain({ seed: "x", count: 2.5 }), /count/); + t.throws(() => HashChain({ seed: "x", count: 3, index: 3 }), /index/); + t.throws(() => HashChain({ seed: "x", count: 3, index: -1 }), /index/); + t.throws(() => HashChain({ seed: "", count: 3 }), /seed/); + t.throws(() => HashChain({ seed: 5, count: 3 }), /seed/); t.end(); }); }); diff --git a/utils.js b/utils.js index b457106..e3921ff 100644 --- a/utils.js +++ b/utils.js @@ -1,45 +1,74 @@ -const { v4: uuid } = require("uuid"); -const lodash = require("lodash"); +const assert = require("assert"); const crypto = require("crypto"); -function defaults(state = {}) { - lodash.defaults(state, { - serverSeed: sha256(), - clientSeed: md5(), - nonce: 0, - cursor: 0, - }); - state.serverHash = state.serverHash || sha256(state.serverSeed); - return state; +const BYTES_PER_ROUND = 32; // one HMAC-SHA256 digest +const BYTES_PER_FLOAT = 4; + +function randomUUID() { + return crypto.randomUUID(); +} + +function sha256(input = randomUUID()) { + return crypto.createHash("sha256").update(input).digest("hex"); +} + +function md5(input = randomUUID()) { + return crypto.createHash("md5").update(input).digest("hex"); +} + +// Accepts a safe integer, or its canonical decimal string form because values +// read back from a database or a query string often arrive as strings. +// Anything else (floats, NaN, "1e3", objects) throws. +function toInteger(value, name = "value") { + if (typeof value === "string" && /^-?\d+$/.test(value)) value = Number(value); + assert(Number.isSafeInteger(value), `${name} must be a safe integer`); + return value; } -function sha256(input = uuid()) { - const hash = crypto.createHash("sha256"); - hash.update(input); - return hash.digest("hex"); +function assertSeed(value, name = "seed") { + assert( + typeof value === "string" && value.length > 0, + `${name} must be a non-empty string` + ); } -function md5(input = uuid()) { - const hash = crypto.createHash("md5"); - hash.update(input); - return hash.digest("hex"); + +// Normalises and validates a provable state. Returns a new object; the input +// is never mutated. Unknown keys (ids, timestamps) are carried through. +// `serverHash` is always derived from `serverSeed`: a supplied value is never +// trusted, so the commitment can not drift from the seed it commits to. +function defaults(state = {}) { + assert(state && typeof state === "object", "state must be an object"); + + const result = { + ...state, + serverSeed: state.serverSeed ?? sha256(), + clientSeed: state.clientSeed ?? md5(), + nonce: toInteger(state.nonce ?? 0, "nonce"), + cursor: toInteger(state.cursor ?? 0, "cursor"), + }; + + assertSeed(result.serverSeed, "serverSeed"); + assertSeed(result.clientSeed, "clientSeed"); + assert(result.nonce >= 0, "nonce must be 0 or more"); + assert(result.cursor >= 0, "cursor must be 0 or more"); + + result.serverHash = sha256(result.serverSeed); + return result; } -// Random number generation based on following inputs: serverSeed, clientSeed, nonce and cursor -function* ByteGenerator({ serverSeed, clientSeed, nonce, cursor }) { - // Setup curser variables - let currentRound = Math.floor(cursor / 32); - let currentRoundCursor = cursor; - currentRoundCursor -= currentRound * 32; +// Yields bytes from HMAC-SHA256(key = serverSeed, message = `clientSeed:nonce:round`). +// Each round yields 32 bytes; `cursor` is the byte offset to start from. +// The nonce is fixed for the life of the generator: open a new one per draw. +function* ByteGenerator({ serverSeed, clientSeed, nonce, cursor = 0 }) { + let currentRound = Math.floor(cursor / BYTES_PER_ROUND); + let currentRoundCursor = cursor - currentRound * BYTES_PER_ROUND; - // Generate outputs until cursor requirement fullfilled while (true) { - // HMAC function used to output provided inputs into bytes const hmac = crypto.createHmac("sha256", serverSeed); hmac.update(`${clientSeed}:${nonce}:${currentRound}`); const buffer = hmac.digest(); - // Update curser for next iteration of loop - while (currentRoundCursor < 32) { + while (currentRoundCursor < BYTES_PER_ROUND) { yield Number(buffer[currentRoundCursor]); currentRoundCursor += 1; } @@ -48,23 +77,25 @@ function* ByteGenerator({ serverSeed, clientSeed, nonce, cursor }) { } } +// Four bytes become a float in [0, 1) with 32 bits of precision: +// b0/256 + b1/256^2 + b2/256^3 + b3/256^4 function bytesToFloat(bytes) { return bytes.reduce((result, value, i) => { const divider = 256 ** (i + 1); - const partialResult = value / divider; - return result + partialResult; + return result + value / divider; }, 0); } +// Maps a float in [0, 1) onto the `max` integers starting at `min`, i.e. the +// range [min, min + max - 1]. `max` is a range size, not an upper bound. function floatToInt(val, max, min = 0) { return Math.floor(min + val * max); } -// Convert the hash output from the rng byteGenerator to floats function* FloatGenerator(rng, count) { for (let i = 0; i < count; i++) { const bytes = []; - for (let j = 0; j < 4; j++) { + for (let j = 0; j < BYTES_PER_FLOAT; j++) { bytes.push(rng.next().value); } yield bytesToFloat(bytes); @@ -75,7 +106,7 @@ function floats(rng, count) { return [...FloatGenerator(rng, count)]; } -function ints(rng, count, max, min) { +function ints(rng, count, max, min = 0) { const result = []; const gen = FloatGenerator(rng, count); for (let i = 0; i < count; i++) { @@ -94,4 +125,7 @@ module.exports = { FloatGenerator, floatToInt, defaults, + toInteger, + assertSeed, + randomUUID, }; From 10b66d502cb24ba30a3eda32074c49fb988cf520 Mon Sep 17 00:00:00 2001 From: tacyarg Date: Thu, 17 Sep 2026 17:26:05 +0000 Subject: [PATCH 2/2] Add release flow: npm version, changelog, CI and provenance publish npm version patch|minor|major is now the whole release: preversion checks the CHANGELOG has an Unreleased entry and runs the tests, version dates the entry and rewrites the compare links, postversion pushes commit and vX.Y.Z tag. publish.yml publishes on v* tags with npm provenance and creates the GitHub release from the changelog section; ci.yml runs tests on Node 16-24. Commit the Yarn 3.3.0 lockfile (node-modules linker, global cache), switch repository.url to https for provenance, backfill CHANGELOG for 1.0.0 and 1.0.1, document the versioning policy, and drop the old release script. --- .github/workflows/ci.yml | 22 + .github/workflows/publish.yml | 47 ++ .gitignore | 4 +- .npmrc | 1 + .yarnrc.yml | 3 + CHANGELOG.md | 79 ++ README.md | 27 + package.json | 12 +- scripts/release-changelog.js | 76 ++ yarn.lock | 1447 +++++++++++++++++++++++++++++++++ 10 files changed, 1713 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .npmrc create mode 100644 .yarnrc.yml create mode 100644 CHANGELOG.md create mode 100644 scripts/release-changelog.js create mode 100644 yarn.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c4dc9b5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: [16, 18, 20, 22, 24] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - run: corepack enable + - run: yarn install --immutable + - run: yarn test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..cf029a3 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,47 @@ +name: Publish + +# Cut a release with `npm version patch|minor|major` on master. That runs the +# tests, dates the CHANGELOG entry, commits, tags `vX.Y.Z` and pushes. This +# workflow then publishes to npm with provenance and creates the GitHub release. +# +# Needs one repository secret: NPM_TOKEN, a granular npm access token with +# read/write on @provableio/provable-core and bypass 2FA for automation. + +on: + push: + tags: ["v[0-9]+.[0-9]+.[0-9]+"] + +permissions: + contents: write + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + registry-url: https://registry.npmjs.org + - run: corepack enable + - run: yarn install --immutable + - run: yarn test + + - name: Tag must match package.json + run: | + version="v$(node -p "require('./package.json').version")" + test "$version" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME != package.json $version"; exit 1; } + + - name: Release notes from CHANGELOG.md + run: node scripts/release-changelog.js notes "$GITHUB_REF_NAME" > release-notes.md + + - name: Publish to npm + run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: GitHub release + run: gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes-file release-notes.md + env: + GH_TOKEN: ${{ github.token }} diff --git a/.gitignore b/.gitignore index bacf133..68fc02d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules .env *.log -dist \ No newline at end of file +dist +.yarn/ +.pnp.* diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e030e5b --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +message=v%s diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..22efd3d --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,3 @@ +nodeLinker: node-modules +enableGlobalCache: true +enableTelemetry: false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5529358 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,79 @@ +# Changelog + +All notable changes to this project are documented here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project +follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +Because this library exists to make results reproducible, **any change to the +bytes, floats or integers produced for a given input is a breaking change and +requires a major version**, however small it looks. + +## [Unreleased] + +### Breaking + +- Inputs are validated. Seeds must be non-empty strings; `nonce`, `cursor`, + `count`, `max`, `min`, hash-chain `count`/`index` and hash-series `nonce` + must be safe integers or their canonical decimal string form (`"400"`). + Calls that previously coerced or ignored bad input (`ints()` returned `[]`, + a `NaN` nonce was hashed as the text `NaN`) now throw. +- `serverHash` is always derived from `serverSeed`. A supplied value is + replaced rather than trusted. +- `Provable(...)(config)` no longer mutates `config`; `state()` and the + emitted state are copies. +- `next(salt)` requires a non-empty `salt`. +- `HashChain` rejects `index` outside `[0, count - 1]` and non-integer `count` + with clear errors instead of `RangeError: Invalid array length` or silent + `undefined`. +- Requires Node.js 14.17 or later (`crypto.randomUUID`). + +### Fixed + +- A long-lived `Provable` instance reused its first nonce for every draw, so + the emitted state did not reproduce the outcome it was recorded against. + Each draw now opens a fresh byte stream for the current nonce, then advances + it. Instances that were re-created from persisted state before every draw + produce identical results before and after this fix. +- The nonce guard runs before state changes, so a failed draw leaves the + instance untouched. +- `HashChain` count assertion checked the seed (copy-paste). +- `HashChain()` with no argument threw `TypeError`. +- LICENSE copyright holder was blank. + +### Changed + +- README rewritten to describe the actual algorithm (HMAC-SHA256, the + `[min, min + max - 1]` range of `ints`, immutable `HashSeries`, no `new` on + generator functions) and to add a verification recipe. +- Test suite pins the byte stream, float and integer conversions and the + rotation formula with fixed vectors. +- Releases are cut with `npm version` and published from GitHub Actions on + `v*` tags with npm provenance. + +### Added + +- `HashChain.generateHashChain(count, seed)`. +- `utils.toInteger`, `utils.assertSeed`, `utils.randomUUID`. +- `files` field so the published tarball ships only the library. + +### Removed + +- Runtime dependencies `lodash` and `uuid`. +- The `release` npm script. + +## [1.0.1] - 2025-09-27 + +### Changed + +- Nonce advances eagerly and is capped at `Number.MAX_SAFE_INTEGER`. +- Documentation and typo fixes. + +## [1.0.0] - 2024-05-03 + +### Added + +- `Provable`, `HashSeries`, `HashChain` and `utils`. + +[Unreleased]: https://github.com/provableio/provable-core/compare/v1.0.1...HEAD +[1.0.1]: https://github.com/provableio/provable-core/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/provableio/provable-core/releases/tag/v1.0.0 diff --git a/README.md b/README.md index 98c04f2..5cdf46e 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,33 @@ utils.floats(rng, 2); // two floats from the start of the stream utils.ints(rng, 3, 6, 1); // three dice rolls from the bytes that follow ``` +## Versioning and releases + +The package follows [semver](https://semver.org). Because its purpose is +reproducibility, the rules are stricter than usual: + +- **Major**: any change to the bytes, floats or integers produced for a given + input, any tightening of accepted input, or a higher Node.js requirement. +- **Minor**: new functions or options that leave existing outputs untouched. +- **Patch**: documentation, tests, tooling, internal refactors with identical + outputs. + +Every change goes under `Unreleased` in [CHANGELOG.md](CHANGELOG.md) with the +pull request that made it. + +To release, from an up-to-date `master`: + +``` +npm version patch # or minor / major +``` + +That runs the tests, moves `Unreleased` under the new version with today's +date, commits, tags `vX.Y.Z` and pushes. The +[publish workflow](.github/workflows/publish.yml) then verifies the tag +against `package.json`, publishes to npm with provenance and creates the +GitHub release from the changelog entry. It needs an `NPM_TOKEN` repository +secret; nothing is published from a laptop. + ## License MIT. See [LICENSE](LICENSE). diff --git a/package.json b/package.json index 58a23c5..254d27e 100644 --- a/package.json +++ b/package.json @@ -14,17 +14,18 @@ "engines": { "node": ">=14.17.0" }, - "dependencies": {}, "devDependencies": { "tape": "^5.0.1" }, "scripts": { "test": "node test.js", - "release": "yarn test && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish" + "preversion": "node scripts/release-changelog.js check && npm test", + "version": "node scripts/release-changelog.js release && git add CHANGELOG.md", + "postversion": "git push --follow-tags" }, "repository": { "type": "git", - "url": "git@github.com:provableio/provable-core.git" + "url": "git+https://github.com/provableio/provable-core.git" }, "keywords": [ "rng", @@ -39,5 +40,8 @@ ], "author": "tacyarg ", "license": "MIT", - "homepage": "https://provable.io" + "homepage": "https://provable.io", + "bugs": { + "url": "https://github.com/provableio/provable-core/issues" + } } diff --git a/scripts/release-changelog.js b/scripts/release-changelog.js new file mode 100644 index 0000000..ba1de9b --- /dev/null +++ b/scripts/release-changelog.js @@ -0,0 +1,76 @@ +#!/usr/bin/env node +// Keeps CHANGELOG.md in step with package.json. +// +// release-changelog.js check fail unless "Unreleased" has content +// release-changelog.js release move "Unreleased" under the current +// package.json version, dated today +// release-changelog.js notes print the section for (used by +// the publish workflow for the GitHub +// release body) +// +// `npm version` runs the release form through the "version" script, after it +// has bumped package.json and before it commits. +const fs = require("fs"); +const path = require("path"); + +const REPO = "https://github.com/provableio/provable-core"; +const file = path.join(__dirname, "..", "CHANGELOG.md"); +const [mode, arg] = process.argv.slice(2); + +const changelog = fs.readFileSync(file, "utf8"); +const heading = /^## \[([^\]]+)\](?: - (\d{4}-\d{2}-\d{2}))?$/gm; + +function sections() { + const result = []; + let match; + while ((match = heading.exec(changelog))) { + result.push({ name: match[1], start: match.index, bodyStart: match.index + match[0].length }); + } + result.forEach((s, i) => { + const end = i + 1 < result.length ? result[i + 1].start : changelog.search(/^\[Unreleased\]:/m); + s.body = changelog.slice(s.bodyStart, end === -1 ? undefined : end).trim(); + }); + return result; +} + +function fail(message) { + console.error(`release-changelog: ${message}`); + process.exit(1); +} + +function unreleased() { + const section = sections()[0]; + if (!section || section.name !== "Unreleased") fail("CHANGELOG.md must start with an [Unreleased] section"); + if (!section.body) fail("the [Unreleased] section is empty; describe the change before releasing"); + return section; +} + +if (mode === "check") { + unreleased(); +} else if (mode === "notes") { + const version = (arg || "").replace(/^v/, ""); + const section = sections().find((s) => s.name === version); + if (!section) fail(`no CHANGELOG section for ${version}`); + process.stdout.write(section.body + "\n"); +} else if (mode === "release") { + const { version } = require(path.join(__dirname, "..", "package.json")); + unreleased(); + const all = sections(); + if (all.some((s) => s.name === version)) fail(`CHANGELOG.md already has a ${version} section`); + const previous = all[1] && all[1].name; + const today = new Date().toISOString().slice(0, 10); + + let next = changelog.replace( + /^## \[Unreleased\]$/m, + `## [Unreleased]\n\n## [${version}] - ${today}` + ); + next = next.replace( + /^\[Unreleased\]: .*$/m, + `[Unreleased]: ${REPO}/compare/v${version}...HEAD\n[${version}]: ` + + (previous ? `${REPO}/compare/v${previous}...v${version}` : `${REPO}/releases/tag/v${version}`) + ); + fs.writeFileSync(file, next); + console.log(`CHANGELOG.md: released ${version} (${today})`); +} else { + fail("usage: release-changelog.js check | release | notes "); +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..de3c2ee --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1447 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"@ljharb/now@npm:^1.0.1": + version: 1.0.1 + resolution: "@ljharb/now@npm:1.0.1" + dependencies: + call-bind-apply-helpers: ^1.0.2 + call-bound: ^1.0.4 + get-intrinsic: ^1.3.0 + checksum: 922896dee6766007a9e1d82c18f0d1c40f7b709b764f617f5e8526fbf2341c8a16b030a18636f7e699710c540c8e5005ffc1ca5d319861973b43353f73be17a5 + languageName: node + linkType: hard + +"@ljharb/resumer@npm:^0.1.3": + version: 0.1.3 + resolution: "@ljharb/resumer@npm:0.1.3" + dependencies: + "@ljharb/through": ^2.3.13 + call-bind: ^1.0.7 + checksum: be3a3de2a0899a32e412426e0525f44b7be2b759d88d17fbe4291e3cfedd5ae14ec6aea9826d9ef033016db2d0697a022b2a75192de89704215ebb2a8678582b + languageName: node + linkType: hard + +"@ljharb/through@npm:^2.3.13, @ljharb/through@npm:^2.3.14": + version: 2.3.14 + resolution: "@ljharb/through@npm:2.3.14" + dependencies: + call-bind: ^1.0.8 + checksum: 70323a9d7a0d43a6867651770d2b965b6d774480eef0409454531ced499b625d16f7227886666bb9f52785c65c386686b9337f7f5c3e1182ce0d25bc4c81bc5f + languageName: node + linkType: hard + +"@provableio/provable-core@workspace:.": + version: 0.0.0-use.local + resolution: "@provableio/provable-core@workspace:." + dependencies: + tape: ^5.0.1 + languageName: unknown + linkType: soft + +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": + version: 1.0.2 + resolution: "array-buffer-byte-length@npm:1.0.2" + dependencies: + call-bound: ^1.0.3 + is-array-buffer: ^3.0.5 + checksum: 0ae3786195c3211b423e5be8dd93357870e6fb66357d81da968c2c39ef43583ef6eece1f9cb1caccdae4806739c65dea832b44b8593414313cd76a89795fca63 + languageName: node + linkType: hard + +"array.prototype.every@npm:^1.1.7": + version: 1.1.7 + resolution: "array.prototype.every@npm:1.1.7" + dependencies: + call-bound: ^1.0.2 + define-properties: ^1.2.1 + es-abstract: ^1.23.5 + es-object-atoms: ^1.0.0 + is-string: ^1.1.0 + checksum: 525ff981177a72afb140009e7cb29d24ece1416fbcb5396d0c40b58013f1adb7665b1e1b590a090b42716c350d9ecd64fb54992e2b58888cda9a0a89f4089030 + languageName: node + linkType: hard + +"array.prototype.flatmap@npm:^1.3.3": + version: 1.3.3 + resolution: "array.prototype.flatmap@npm:1.3.3" + dependencies: + call-bind: ^1.0.8 + define-properties: ^1.2.1 + es-abstract: ^1.23.5 + es-shim-unscopables: ^1.0.2 + checksum: 11b4de09b1cf008be6031bb507d997ad6f1892e57dc9153583de6ebca0f74ea403fffe0f203461d359de05048d609f3f480d9b46fed4099652d8b62cc972f284 + languageName: node + linkType: hard + +"arraybuffer.prototype.slice@npm:^1.0.4": + version: 1.0.4 + resolution: "arraybuffer.prototype.slice@npm:1.0.4" + dependencies: + array-buffer-byte-length: ^1.0.1 + call-bind: ^1.0.8 + define-properties: ^1.2.1 + es-abstract: ^1.23.5 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.6 + is-array-buffer: ^3.0.4 + checksum: b1d1fd20be4e972a3779b1569226f6740170dca10f07aa4421d42cefeec61391e79c557cda8e771f5baefe47d878178cd4438f60916ce831813c08132bced765 + languageName: node + linkType: hard + +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 9102e246d1ed9b37ac36f57f0a6ca55226876553251a31fc80677e71471f463a54c872dc78d5d7f80740c8ba624395cccbe8b60f7b690c4418f487d8e9fd1106 + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 74a71a4a2dd7afd06ebb612f6d612c7f4766a351bedffde466023bf6dae629e46b0d2cd38786239e0fbf245de0c7df76035465e16d1213774a0efb22fec0d713 + languageName: node + linkType: hard + +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" + dependencies: + possible-typed-array-names: ^1.0.0 + checksum: 1aa3ffbfe6578276996de660848b6e95669d9a95ad149e3dd0c0cda77db6ee1dbd9d1dd723b65b6d277b882dd0c4b91a654ae9d3cf9e1254b7e93e4908d78fd3 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.21 + resolution: "brace-expansion@npm:1.1.21" + dependencies: + balanced-match: ^1.0.0 + concat-map: 0.0.1 + checksum: b841b4198a0ab8a1b433920ed65b46a9b4368c0ad1f95b2ed229a682c7d7d5b5839620204b5644a05c967d29e66bdf0a59b1115e74e0f27761d0dc02f5e0d835 + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: ^1.3.0 + function-bind: ^1.1.2 + checksum: b2863d74fcf2a6948221f65d95b91b4b2d90cfe8927650b506141e669f7d5de65cea191bf788838bc40d13846b7886c5bc5c84ab96c3adbcf88ad69a72fcdc6b + languageName: node + linkType: hard + +"call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8, call-bind@npm:^1.0.9": + version: 1.0.9 + resolution: "call-bind@npm:1.0.9" + dependencies: + call-bind-apply-helpers: ^1.0.2 + es-define-property: ^1.0.1 + get-intrinsic: ^1.3.0 + set-function-length: ^1.2.2 + checksum: fb5a8037bd7e2417ebda428f7ba57cbb3152e92f355aa8a20a4b2be9657f67b84e3812502620047ccf12c6542584a7d5bfb8d080cb636eb178b270bec0bfc010 + languageName: node + linkType: hard + +"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3, call-bound@npm:^1.0.4": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: ^1.0.2 + get-intrinsic: ^1.3.0 + checksum: 2f6399488d1c272f56306ca60ff696575e2b7f31daf23bc11574798c84d9f2759dceb0cb1f471a85b77f28962a7ac6411f51d283ea2e45319009a19b6ccab3b2 + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af + languageName: node + linkType: hard + +"data-view-buffer@npm:^1.0.2": + version: 1.0.2 + resolution: "data-view-buffer@npm:1.0.2" + dependencies: + call-bound: ^1.0.3 + es-errors: ^1.3.0 + is-data-view: ^1.0.2 + checksum: 1e1cd509c3037ac0f8ba320da3d1f8bf1a9f09b0be09394b5e40781b8cc15ff9834967ba7c9f843a425b34f9fe14ce44cf055af6662c44263424c1eb8d65659b + languageName: node + linkType: hard + +"data-view-byte-length@npm:^1.0.2": + version: 1.0.2 + resolution: "data-view-byte-length@npm:1.0.2" + dependencies: + call-bound: ^1.0.3 + es-errors: ^1.3.0 + is-data-view: ^1.0.2 + checksum: 3600c91ced1cfa935f19ef2abae11029e01738de8d229354d3b2a172bf0d7e4ed08ff8f53294b715569fdf72dfeaa96aa7652f479c0f60570878d88e7e8bddf6 + languageName: node + linkType: hard + +"data-view-byte-offset@npm:^1.0.1": + version: 1.0.1 + resolution: "data-view-byte-offset@npm:1.0.1" + dependencies: + call-bound: ^1.0.2 + es-errors: ^1.3.0 + is-data-view: ^1.0.1 + checksum: 8dd492cd51d19970876626b5b5169fbb67ca31ec1d1d3238ee6a71820ca8b80cafb141c485999db1ee1ef02f2cc3b99424c5eda8d59e852d9ebb79ab290eb5ee + languageName: node + linkType: hard + +"deep-equal@npm:^2.2.3": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: ^1.0.0 + call-bind: ^1.0.5 + es-get-iterator: ^1.1.3 + get-intrinsic: ^1.2.2 + is-arguments: ^1.1.1 + is-array-buffer: ^3.0.2 + is-date-object: ^1.0.5 + is-regex: ^1.1.4 + is-shared-array-buffer: ^1.0.2 + isarray: ^2.0.5 + object-is: ^1.1.5 + object-keys: ^1.1.1 + object.assign: ^4.1.4 + regexp.prototype.flags: ^1.5.1 + side-channel: ^1.0.4 + which-boxed-primitive: ^1.0.2 + which-collection: ^1.0.1 + which-typed-array: ^1.1.13 + checksum: ee8852f23e4d20a5626c13b02f415ba443a1b30b4b3d39eaf366d59c4a85e6545d7ec917db44d476a85ae5a86064f7e5f7af7479f38f113995ba869f3a1ddc53 + languageName: node + linkType: hard + +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: ^1.0.0 + es-errors: ^1.3.0 + gopd: ^1.0.1 + checksum: 8068ee6cab694d409ac25936eb861eea704b7763f7f342adbdfe337fc27c78d7ae0eff2364b2917b58c508d723c7a074326d068eef2e45c4edcd85cf94d0313b + languageName: node + linkType: hard + +"define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" + dependencies: + define-data-property: ^1.0.1 + has-property-descriptors: ^1.0.0 + object-keys: ^1.1.1 + checksum: b4ccd00597dd46cb2d4a379398f5b19fca84a16f3374e2249201992f36b30f6835949a9429669ee6b41b6e837205a163eadd745e472069e70dfc10f03e5fcc12 + languageName: node + linkType: hard + +"defined@npm:^1.0.1": + version: 1.0.1 + resolution: "defined@npm:1.0.1" + checksum: b1a852300bdb57f297289b55eafdd0c517afaa3ec8190e78fce91b9d8d0c0369d4505ecbdacfd3d98372e664f4a267d9bd793938d4a8c76209c9d9516fbe2101 + languageName: node + linkType: hard + +"dotignore@npm:^0.1.2": + version: 0.1.2 + resolution: "dotignore@npm:0.1.2" + dependencies: + minimatch: ^3.0.4 + bin: + ignored: bin/ignored + checksum: 06bab15e2a2400c6f823a0edbcd73661180f6245a4041a3fe3b9fde4b22ae74b896604df4520a877093f05c656bd080087376c9f605bccdea847664c59910f37 + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: ^1.0.1 + es-errors: ^1.3.0 + gopd: ^1.2.0 + checksum: 149207e36f07bd4941921b0ca929e3a28f1da7bd6b6ff8ff7f4e2f2e460675af4576eeba359c635723dc189b64cdd4787e0255897d5b135ccc5d15cb8685fc90 + languageName: node + linkType: hard + +"es-abstract-get@npm:^1.0.0": + version: 1.0.0 + resolution: "es-abstract-get@npm:1.0.0" + dependencies: + es-errors: ^1.3.0 + es-object-atoms: ^1.1.2 + is-callable: ^1.2.7 + object-inspect: ^1.13.4 + checksum: 625bb67d41ebc22e7585875a6ebb90dc9c153998c9866dc7d50ff7b1b9e178e216c0d23914e5dbfd0addb38aab344c142ad27ff4375c6d2acf095432a4d85fe3 + languageName: node + linkType: hard + +"es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.2": + version: 1.24.2 + resolution: "es-abstract@npm:1.24.2" + dependencies: + array-buffer-byte-length: ^1.0.2 + arraybuffer.prototype.slice: ^1.0.4 + available-typed-arrays: ^1.0.7 + call-bind: ^1.0.8 + call-bound: ^1.0.4 + data-view-buffer: ^1.0.2 + data-view-byte-length: ^1.0.2 + data-view-byte-offset: ^1.0.1 + es-define-property: ^1.0.1 + es-errors: ^1.3.0 + es-object-atoms: ^1.1.1 + es-set-tostringtag: ^2.1.0 + es-to-primitive: ^1.3.0 + function.prototype.name: ^1.1.8 + get-intrinsic: ^1.3.0 + get-proto: ^1.0.1 + get-symbol-description: ^1.1.0 + globalthis: ^1.0.4 + gopd: ^1.2.0 + has-property-descriptors: ^1.0.2 + has-proto: ^1.2.0 + has-symbols: ^1.1.0 + hasown: ^2.0.2 + internal-slot: ^1.1.0 + is-array-buffer: ^3.0.5 + is-callable: ^1.2.7 + is-data-view: ^1.0.2 + is-negative-zero: ^2.0.3 + is-regex: ^1.2.1 + is-set: ^2.0.3 + is-shared-array-buffer: ^1.0.4 + is-string: ^1.1.1 + is-typed-array: ^1.1.15 + is-weakref: ^1.1.1 + math-intrinsics: ^1.1.0 + object-inspect: ^1.13.4 + object-keys: ^1.1.1 + object.assign: ^4.1.7 + own-keys: ^1.0.1 + regexp.prototype.flags: ^1.5.4 + safe-array-concat: ^1.1.3 + safe-push-apply: ^1.0.0 + safe-regex-test: ^1.1.0 + set-proto: ^1.0.0 + stop-iteration-iterator: ^1.1.0 + string.prototype.trim: ^1.2.10 + string.prototype.trimend: ^1.0.9 + string.prototype.trimstart: ^1.0.8 + typed-array-buffer: ^1.0.3 + typed-array-byte-length: ^1.0.3 + typed-array-byte-offset: ^1.0.4 + typed-array-length: ^1.0.7 + unbox-primitive: ^1.1.0 + which-typed-array: ^1.1.19 + checksum: 25ddb06725159050d896986a10df5351c658a35113dcfb328bc2e117557440cb956e2ebf61c1a977974c14551fac3bd43449c96e63cb876c5e72bde306714b98 + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 0512f4e5d564021c9e3a644437b0155af2679d10d80f21adaf868e64d30efdfbd321631956f20f42d655fedb2e3a027da479fad3fa6048f768eb453a80a5f80a + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: ec1414527a0ccacd7f15f4a3bc66e215f04f595ba23ca75cdae0927af099b5ec865f9f4d33e9d7e86f512f252876ac77d4281a7871531a50678132429b1271b5 + languageName: node + linkType: hard + +"es-get-iterator@npm:^1.1.3": + version: 1.1.3 + resolution: "es-get-iterator@npm:1.1.3" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.3 + has-symbols: ^1.0.3 + is-arguments: ^1.1.1 + is-map: ^2.0.2 + is-set: ^2.0.2 + is-string: ^1.0.7 + isarray: ^2.0.5 + stop-iteration-iterator: ^1.0.0 + checksum: 8fa118da42667a01a7c7529f8a8cca514feeff243feec1ce0bb73baaa3514560bd09d2b3438873cf8a5aaec5d52da248131de153b28e2638a061b6e4df13267d + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1, es-object-atoms@npm:^1.1.2": + version: 1.1.2 + resolution: "es-object-atoms@npm:1.1.2" + dependencies: + es-errors: ^1.3.0 + checksum: b821f39e4f48bd85b13fee80aea9068a674bcb78b95ff01267aa4da1111f83e6944049b3329b2ffdb3acaa266fb5b8b885476fe8cada05034dc7c87c8016c86d + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: ^1.3.0 + get-intrinsic: ^1.2.6 + has-tostringtag: ^1.0.2 + hasown: ^2.0.2 + checksum: 789f35de4be3dc8d11fdcb91bc26af4ae3e6d602caa93299a8c45cf05d36cc5081454ae2a6d3afa09cceca214b76c046e4f8151e092e6fc7feeb5efb9e794fc6 + languageName: node + linkType: hard + +"es-shim-unscopables@npm:^1.0.2": + version: 1.1.0 + resolution: "es-shim-unscopables@npm:1.1.0" + dependencies: + hasown: ^2.0.2 + checksum: 33cfb1ebcb2f869f0bf528be1a8660b4fe8b6cec8fc641f330e508db2284b58ee2980fad6d0828882d22858c759c0806076427a3673b6daa60f753e3b558ee15 + languageName: node + linkType: hard + +"es-to-primitive@npm:^1.3.0": + version: 1.3.4 + resolution: "es-to-primitive@npm:1.3.4" + dependencies: + es-abstract-get: ^1.0.0 + es-define-property: ^1.0.1 + es-errors: ^1.3.0 + is-callable: ^1.2.7 + is-date-object: ^1.1.0 + is-symbol: ^1.1.1 + checksum: b152ec48ee2f962760751c1c181ef09d2c4483af50fbdd08c0d41004d71014c90ad4339b14d0328a14209d9ee638d13b4190aadc066f19466071cf2af5785d05 + languageName: node + linkType: hard + +"for-each@npm:^0.3.3, for-each@npm:^0.3.5": + version: 0.3.5 + resolution: "for-each@npm:0.3.5" + dependencies: + is-callable: ^1.2.7 + checksum: 3c986d7e11f4381237cc98baa0a2f87eabe74719eee65ed7bed275163082b940ede19268c61d04c6260e0215983b12f8d885e3c8f9aa8c2113bf07c37051745c + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1 + languageName: node + linkType: hard + +"function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.8": + version: 1.2.0 + resolution: "function.prototype.name@npm:1.2.0" + dependencies: + call-bind: ^1.0.9 + call-bound: ^1.0.4 + es-define-property: ^1.0.1 + es-errors: ^1.3.0 + functions-have-names: ^1.2.3 + has-property-descriptors: ^1.0.2 + hasown: ^2.0.4 + is-callable: ^1.2.7 + is-document.all: ^1.0.0 + checksum: 297f6966f6fe1ff756ec7f51956420273d55186697769ff8e815578c10bb9cc27de5e4383cc0da703873b988ad634b74b167f1f9f8ef6a04f7f096f8088fde5d + languageName: node + linkType: hard + +"functions-have-names@npm:^1.2.3": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 + languageName: node + linkType: hard + +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 3bf87f7b0230de5d74529677e6c3ceb3b7b5d9618b5a22d92b45ce3876defbaf5a77791b25a61b0fa7d13f95675b5ff67a7769f3b9af33f096e34653519e873d + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: ^1.0.0 + async-generator-function: ^1.0.0 + call-bind-apply-helpers: ^1.0.2 + es-define-property: ^1.0.1 + es-errors: ^1.3.0 + es-object-atoms: ^1.1.1 + function-bind: ^1.1.2 + generator-function: ^2.0.0 + get-proto: ^1.0.1 + gopd: ^1.2.0 + has-symbols: ^1.1.0 + hasown: ^2.0.2 + math-intrinsics: ^1.1.0 + checksum: c02b3b6a445f9cd53e14896303794ac60f9751f58a69099127248abdb0251957174c6524245fc68579dc8e6a35161d3d94c93e665f808274716f4248b269436a + languageName: node + linkType: hard + +"get-package-type@npm:^0.1.0": + version: 0.1.0 + resolution: "get-package-type@npm:0.1.0" + checksum: bba0811116d11e56d702682ddef7c73ba3481f114590e705fc549f4d868972263896af313c57a25c076e3c0d567e11d919a64ba1b30c879be985fc9d44f96148 + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: ^1.0.1 + es-object-atoms: ^1.0.0 + checksum: 4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + +"get-symbol-description@npm:^1.1.0": + version: 1.1.0 + resolution: "get-symbol-description@npm:1.1.0" + dependencies: + call-bound: ^1.0.3 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.6 + checksum: 655ed04db48ee65ef2ddbe096540d4405e79ba0a7f54225775fef43a7e2afcb93a77d141c5f05fdef0afce2eb93bcbfb3597142189d562ac167ff183582683cd + languageName: node + linkType: hard + +"glob@npm:^7.2.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard + +"globalthis@npm:^1.0.4": + version: 1.0.4 + resolution: "globalthis@npm:1.0.4" + dependencies: + define-properties: ^1.2.1 + gopd: ^1.0.1 + checksum: 39ad667ad9f01476474633a1834a70842041f70a55571e8dcef5fb957980a92da5022db5430fca8aecc5d47704ae30618c0bc877a579c70710c904e9ef06108a + languageName: node + linkType: hard + +"gopd@npm:^1.0.1, gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: cc6d8e655e360955bdccaca51a12a474268f95bb793fc3e1f2bdadb075f28bfd1fd988dab872daf77a61d78cbaf13744bc8727a17cfb1d150d76047d805375f3 + languageName: node + linkType: hard + +"has-bigints@npm:^1.0.2": + version: 1.1.0 + resolution: "has-bigints@npm:1.1.0" + checksum: 79730518ae02c77e4af6a1d1a0b6a2c3e1509785532771f9baf0241e83e36329542c3d7a0e723df8cbc85f74eff4f177828a2265a01ba576adbdc2d40d86538b + languageName: node + linkType: hard + +"has-dynamic-import@npm:^2.1.1": + version: 2.1.1 + resolution: "has-dynamic-import@npm:2.1.1" + dependencies: + call-bind: ^1.0.8 + call-bound: ^1.0.3 + get-intrinsic: ^1.2.6 + checksum: 84cf235c445c9bd8e14c1d3b56e6dc075a3c1c32045a9b403131157d42bb6e72ca479267693b6ed5488d8d822a65270cddace44e50505871c69353ceefd76da7 + languageName: node + linkType: hard + +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: ^1.0.0 + checksum: fcbb246ea2838058be39887935231c6d5788babed499d0e9d0cc5737494c48aba4fe17ba1449e0d0fbbb1e36175442faa37f9c427ae357d6ccb1d895fbcd3de3 + languageName: node + linkType: hard + +"has-proto@npm:^1.2.0": + version: 1.2.0 + resolution: "has-proto@npm:1.2.0" + dependencies: + dunder-proto: ^1.0.0 + checksum: f55010cb94caa56308041d77967c72a02ffd71386b23f9afa8447e58bc92d49d15c19bf75173713468e92fe3fb1680b03b115da39c21c32c74886d1d50d3e7ff + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: b2316c7302a0e8ba3aaba215f834e96c22c86f192e7310bdf689dd0e6999510c89b00fbc5742571507cebf25764d68c988b3a0da217369a73596191ac0ce694b + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: ^1.0.3 + checksum: 999d60bb753ad714356b2c6c87b7fb74f32463b8426e159397da4bde5bca7e598ab1073f4d8d4deafac297f2eb311484cd177af242776bf05f0d11565680468d + languageName: node + linkType: hard + +"hasown@npm:^2.0.2, hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" + dependencies: + function-bind: ^1.1.2 + checksum: 4bd8f916b629e06324853593ffbdd45e200022952a85ad0c967f3bd4c2e4c7e1f9a9766fbe6186f60bd394e0afc73e719730caa1da15cd9bd832b7cdf53fd26c + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: ^1.3.0 + wrappy: 1 + checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd + languageName: node + linkType: hard + +"inherits@npm:2, inherits@npm:^2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 + languageName: node + linkType: hard + +"internal-slot@npm:^1.1.0": + version: 1.1.0 + resolution: "internal-slot@npm:1.1.0" + dependencies: + es-errors: ^1.3.0 + hasown: ^2.0.2 + side-channel: ^1.1.0 + checksum: 8e0991c2d048cc08dab0a91f573c99f6a4215075887517ea4fa32203ce8aea60fa03f95b177977fa27eb502e5168366d0f3e02c762b799691411d49900611861 + languageName: node + linkType: hard + +"is-arguments@npm:^1.1.1": + version: 1.2.0 + resolution: "is-arguments@npm:1.2.0" + dependencies: + call-bound: ^1.0.2 + has-tostringtag: ^1.0.2 + checksum: aae9307fedfe2e5be14aebd0f48a9eeedf6b8c8f5a0b66257b965146d1e94abdc3f08e3dce3b1d908e1fa23c70039a88810ee1d753905758b9b6eebbab0bafeb + languageName: node + linkType: hard + +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": + version: 3.0.5 + resolution: "is-array-buffer@npm:3.0.5" + dependencies: + call-bind: ^1.0.8 + call-bound: ^1.0.3 + get-intrinsic: ^1.2.6 + checksum: f137a2a6e77af682cdbffef1e633c140cf596f72321baf8bba0f4ef22685eb4339dde23dfe9e9ca430b5f961dee4d46577dcf12b792b68518c8449b134fb9156 + languageName: node + linkType: hard + +"is-async-function@npm:^2.0.0": + version: 2.1.1 + resolution: "is-async-function@npm:2.1.1" + dependencies: + async-function: ^1.0.0 + call-bound: ^1.0.3 + get-proto: ^1.0.1 + has-tostringtag: ^1.0.2 + safe-regex-test: ^1.1.0 + checksum: 9bece45133da26636488ca127d7686b85ad3ca18927e2850cff1937a650059e90be1c71a48623f8791646bb7a241b0cabf602a0b9252dcfa5ab273f2399000e6 + languageName: node + linkType: hard + +"is-bigint@npm:^1.1.0": + version: 1.1.0 + resolution: "is-bigint@npm:1.1.0" + dependencies: + has-bigints: ^1.0.2 + checksum: ee1544f0e664f253306786ed1dce494b8cf242ef415d6375d8545b4d8816b0f054bd9f948a8988ae2c6325d1c28260dd02978236b2f7b8fb70dfc4838a6c9fa7 + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.2.1": + version: 1.2.2 + resolution: "is-boolean-object@npm:1.2.2" + dependencies: + call-bound: ^1.0.3 + has-tostringtag: ^1.0.2 + checksum: 0415b181e8f1bfd5d3f8a20f8108e64d372a72131674eea9c2923f39d065b6ad08d654765553bdbffbd92c3746f1007986c34087db1bd89a31f71be8359ccdaa + languageName: node + linkType: hard + +"is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac + languageName: node + linkType: hard + +"is-core-module@npm:^2.16.2": + version: 2.17.0 + resolution: "is-core-module@npm:2.17.0" + dependencies: + hasown: ^2.0.4 + checksum: ab8a122dd92fc05b54e4f46c9859d6f8a9d9f20676a1b029605a4c33b151f07d8cd280e31f7806a29f29f8bfd8e3e647ba7b1cde7f07d60387c14be77774d346 + languageName: node + linkType: hard + +"is-data-view@npm:^1.0.1, is-data-view@npm:^1.0.2": + version: 1.0.2 + resolution: "is-data-view@npm:1.0.2" + dependencies: + call-bound: ^1.0.2 + get-intrinsic: ^1.2.6 + is-typed-array: ^1.1.13 + checksum: 31600dd19932eae7fd304567e465709ffbfa17fa236427c9c864148e1b54eb2146357fcf3aed9b686dee13c217e1bb5a649cb3b9c479e1004c0648e9febde1b2 + languageName: node + linkType: hard + +"is-date-object@npm:^1.0.5, is-date-object@npm:^1.1.0": + version: 1.1.0 + resolution: "is-date-object@npm:1.1.0" + dependencies: + call-bound: ^1.0.2 + has-tostringtag: ^1.0.2 + checksum: d6c36ab9d20971d65f3fc64cef940d57a4900a2ac85fb488a46d164c2072a33da1cb51eefcc039e3e5c208acbce343d3480b84ab5ff0983f617512da2742562a + languageName: node + linkType: hard + +"is-document.all@npm:^1.0.0": + version: 1.0.0 + resolution: "is-document.all@npm:1.0.0" + dependencies: + call-bound: ^1.0.4 + checksum: 383175789df98503dc0c15d39e80932b21cbec839b8840d7b75dc36db942e9dd2da0f36c464ea1aa2e751f5808c38e97bbf288b76d8114d5e570f49df26ed930 + languageName: node + linkType: hard + +"is-finalizationregistry@npm:^1.1.0": + version: 1.1.1 + resolution: "is-finalizationregistry@npm:1.1.1" + dependencies: + call-bound: ^1.0.3 + checksum: 38c646c506e64ead41a36c182d91639833311970b6b6c6268634f109eef0a1a9d2f1f2e499ef4cb43c744a13443c4cdd2f0812d5afdcee5e9b65b72b28c48557 + languageName: node + linkType: hard + +"is-generator-function@npm:^1.0.10": + version: 1.1.2 + resolution: "is-generator-function@npm:1.1.2" + dependencies: + call-bound: ^1.0.4 + generator-function: ^2.0.0 + get-proto: ^1.0.1 + has-tostringtag: ^1.0.2 + safe-regex-test: ^1.1.0 + checksum: 0b81c613752a5e534939e5b3835ff722446837a5b94c3a3934af5ded36a651d9aa31c3f11f8a3453884b9658bf26dbfb7eb855e744d920b07f084bd890a43414 + languageName: node + linkType: hard + +"is-map@npm:^2.0.2, is-map@npm:^2.0.3": + version: 2.0.3 + resolution: "is-map@npm:2.0.3" + checksum: e6ce5f6380f32b141b3153e6ba9074892bbbbd655e92e7ba5ff195239777e767a976dcd4e22f864accaf30e53ebf961ab1995424aef91af68788f0591b7396cc + languageName: node + linkType: hard + +"is-negative-zero@npm:^2.0.3": + version: 2.0.3 + resolution: "is-negative-zero@npm:2.0.3" + checksum: c1e6b23d2070c0539d7b36022d5a94407132411d01aba39ec549af824231f3804b1aea90b5e4e58e807a65d23ceb538ed6e355ce76b267bdd86edb757ffcbdcd + languageName: node + linkType: hard + +"is-number-object@npm:^1.1.1": + version: 1.1.1 + resolution: "is-number-object@npm:1.1.1" + dependencies: + call-bound: ^1.0.3 + has-tostringtag: ^1.0.2 + checksum: 6517f0a0e8c4b197a21afb45cd3053dc711e79d45d8878aa3565de38d0102b130ca8732485122c7b336e98c27dacd5236854e3e6526e0eb30cae64956535662f + languageName: node + linkType: hard + +"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1": + version: 1.2.1 + resolution: "is-regex@npm:1.2.1" + dependencies: + call-bound: ^1.0.2 + gopd: ^1.2.0 + has-tostringtag: ^1.0.2 + hasown: ^2.0.2 + checksum: 99ee0b6d30ef1bb61fa4b22fae7056c6c9b3c693803c0c284ff7a8570f83075a7d38cda53b06b7996d441215c27895ea5d1af62124562e13d91b3dbec41a5e13 + languageName: node + linkType: hard + +"is-set@npm:^2.0.2, is-set@npm:^2.0.3": + version: 2.0.3 + resolution: "is-set@npm:2.0.3" + checksum: 36e3f8c44bdbe9496c9689762cc4110f6a6a12b767c5d74c0398176aa2678d4467e3bf07595556f2dba897751bde1422480212b97d973c7b08a343100b0c0dfe + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4": + version: 1.0.4 + resolution: "is-shared-array-buffer@npm:1.0.4" + dependencies: + call-bound: ^1.0.3 + checksum: 1611fedc175796eebb88f4dfc393dd969a4a8e6c69cadaff424ee9d4464f9f026399a5f84a90f7c62d6d7ee04e3626a912149726de102b0bd6c1ee6a9868fa5a + languageName: node + linkType: hard + +"is-string@npm:^1.0.7, is-string@npm:^1.1.0, is-string@npm:^1.1.1": + version: 1.1.1 + resolution: "is-string@npm:1.1.1" + dependencies: + call-bound: ^1.0.3 + has-tostringtag: ^1.0.2 + checksum: 2eeaaff605250f5e836ea3500d33d1a5d3aa98d008641d9d42fb941e929ffd25972326c2ef912987e54c95b6f10416281aaf1b35cdf81992cfb7524c5de8e193 + languageName: node + linkType: hard + +"is-symbol@npm:^1.1.1": + version: 1.1.1 + resolution: "is-symbol@npm:1.1.1" + dependencies: + call-bound: ^1.0.2 + has-symbols: ^1.1.0 + safe-regex-test: ^1.1.0 + checksum: bfafacf037af6f3c9d68820b74be4ae8a736a658a3344072df9642a090016e281797ba8edbeb1c83425879aae55d1cb1f30b38bf132d703692b2570367358032 + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.15": + version: 1.1.15 + resolution: "is-typed-array@npm:1.1.15" + dependencies: + which-typed-array: ^1.1.16 + checksum: ea7cfc46c282f805d19a9ab2084fd4542fed99219ee9dbfbc26284728bd713a51eac66daa74eca00ae0a43b61322920ba334793607dc39907465913e921e0892 + languageName: node + linkType: hard + +"is-weakmap@npm:^2.0.2": + version: 2.0.2 + resolution: "is-weakmap@npm:2.0.2" + checksum: f36aef758b46990e0d3c37269619c0a08c5b29428c0bb11ecba7f75203442d6c7801239c2f31314bc79199217ef08263787f3837d9e22610ad1da62970d6616d + languageName: node + linkType: hard + +"is-weakref@npm:^1.0.2, is-weakref@npm:^1.1.1": + version: 1.1.1 + resolution: "is-weakref@npm:1.1.1" + dependencies: + call-bound: ^1.0.3 + checksum: 1769b9aed5d435a3a989ffc18fc4ad1947d2acdaf530eb2bd6af844861b545047ea51102f75901f89043bed0267ed61d914ee21e6e8b9aa734ec201cdfc0726f + languageName: node + linkType: hard + +"is-weakset@npm:^2.0.3": + version: 2.0.4 + resolution: "is-weakset@npm:2.0.4" + dependencies: + call-bound: ^1.0.3 + get-intrinsic: ^1.2.6 + checksum: 5c6c8415a06065d78bdd5e3a771483aa1cd928df19138aa73c4c51333226f203f22117b4325df55cc8b3085a6716870a320c2d757efee92d7a7091a039082041 + languageName: node + linkType: hard + +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: bd5bbe4104438c4196ba58a54650116007fa0262eccef13a4c55b2e09a5b36b59f1e75b9fcc49883dd9d4953892e6fc007eef9e9155648ceea036e184b0f930a + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 0e513b29d120f478c85a70f49da0b8b19bc638975eca466f2eeae0071f3ad00454c621bf66e16dd435896c208e719fc91ad79bbfba4e400fe0b372e7c1c9c9a2 + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: ^1.1.7 + checksum: 47ef6f412c08be045a7291d11b1c40777925accf7252dc6d3caa39b1bfbb3a7ea390ba7aba464d762d783265c644143d2c8a204e6b5763145024d52ee65a1941 + languageName: node + linkType: hard + +"minimist@npm:^1.2.8": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 + languageName: node + linkType: hard + +"mock-property@npm:^1.1.0": + version: 1.1.2 + resolution: "mock-property@npm:1.1.2" + dependencies: + define-data-property: ^1.1.4 + es-errors: ^1.3.0 + functions-have-names: ^1.2.3 + gopd: ^1.2.0 + has-property-descriptors: ^1.0.2 + hasown: ^2.0.4 + isarray: ^2.0.5 + object-inspect: ^1.13.4 + checksum: ffb31081367d85f8e3b95e8be32967db80cf2026a4fc368f2542a8f4855686e6800726126f24e2abae924ee8f61fe86ec19e67a12edea276d9335a429fc719e7 + languageName: node + linkType: hard + +"node-exports-info@npm:^1.6.0": + version: 1.6.2 + resolution: "node-exports-info@npm:1.6.2" + dependencies: + array.prototype.flatmap: ^1.3.3 + es-errors: ^1.3.0 + object.entries: ^1.1.9 + semver: ^6.3.1 + checksum: d8642f3dc0c03023a0249ab737ab052796b7ae6d51401b49cfcd0c8c41a22dc464e8aeb3207d6d2ea1f3807f37000bc88bcfad111e4ec191d0f9e1eafa768804 + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 582810c6a8d2ef988ea0a39e69e115a138dad8f42dd445383b394877e5816eb4268489f316a6f74ee9c4e0a984b3eab1028e3e79d62b1ed67c726661d55c7a8b + languageName: node + linkType: hard + +"object-is@npm:^1.1.5, object-is@npm:^1.1.6": + version: 1.1.6 + resolution: "object-is@npm:1.1.6" + dependencies: + call-bind: ^1.0.7 + define-properties: ^1.2.1 + checksum: 3ea22759967e6f2380a2cbbd0f737b42dc9ddb2dfefdb159a1b927fea57335e1b058b564bfa94417db8ad58cddab33621a035de6f5e5ad56d89f2dd03e66c6a1 + languageName: node + linkType: hard + +"object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a + languageName: node + linkType: hard + +"object.assign@npm:^4.1.4, object.assign@npm:^4.1.7": + version: 4.1.7 + resolution: "object.assign@npm:4.1.7" + dependencies: + call-bind: ^1.0.8 + call-bound: ^1.0.3 + define-properties: ^1.2.1 + es-object-atoms: ^1.0.0 + has-symbols: ^1.1.0 + object-keys: ^1.1.1 + checksum: 60e07d2651cf4f5528c485f1aa4dbded9b384c47d80e8187cefd11320abb1aebebf78df5483451dfa549059f8281c21f7b4bf7d19e9e5e97d8d617df0df298de + languageName: node + linkType: hard + +"object.entries@npm:^1.1.9": + version: 1.1.9 + resolution: "object.entries@npm:1.1.9" + dependencies: + call-bind: ^1.0.8 + call-bound: ^1.0.4 + define-properties: ^1.2.1 + es-object-atoms: ^1.1.1 + checksum: 0ab2ef331c4d6a53ff600a5d69182948d453107c3a1f7fd91bc29d387538c2aba21d04949a74f57c21907208b1f6fb175567fd1f39f1a7a4046ba1bca762fb41 + languageName: node + linkType: hard + +"once@npm:^1.3.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: 1 + checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"own-keys@npm:^1.0.1": + version: 1.0.2 + resolution: "own-keys@npm:1.0.2" + dependencies: + call-bound: ^1.0.4 + get-intrinsic: ^1.3.0 + object-keys: ^1.1.1 + safe-push-apply: ^1.0.0 + checksum: 9c5092be16b13391938458205ce2d6aea9184ce0d6179d9bd18c112d3be83fe90b5b958f4e99a2a0c9699e2ae04c655c5eea0cd33b7170ef3243430feea33a94 + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a + languageName: node + linkType: hard + +"possible-typed-array-names@npm:^1.0.0, possible-typed-array-names@npm:^1.1.0": + version: 1.1.0 + resolution: "possible-typed-array-names@npm:1.1.0" + checksum: cfcd4f05264eee8fd184cd4897a17890561d1d473434b43ab66ad3673d9c9128981ec01e0cb1d65a52cd6b1eebfb2eae1e53e39b2e0eca86afc823ede7a4f41b + languageName: node + linkType: hard + +"reflect.getprototypeof@npm:^1.0.10": + version: 1.0.10 + resolution: "reflect.getprototypeof@npm:1.0.10" + dependencies: + call-bind: ^1.0.8 + define-properties: ^1.2.1 + es-abstract: ^1.23.9 + es-errors: ^1.3.0 + es-object-atoms: ^1.0.0 + get-intrinsic: ^1.2.7 + get-proto: ^1.0.1 + which-builtin-type: ^1.2.1 + checksum: ccc5debeb66125e276ae73909cecb27e47c35d9bb79d9cc8d8d055f008c58010ab8cb401299786e505e4aab733a64cba9daf5f312a58e96a43df66adad221870 + languageName: node + linkType: hard + +"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.4": + version: 1.5.4 + resolution: "regexp.prototype.flags@npm:1.5.4" + dependencies: + call-bind: ^1.0.8 + define-properties: ^1.2.1 + es-errors: ^1.3.0 + get-proto: ^1.0.1 + gopd: ^1.2.0 + set-function-name: ^2.0.2 + checksum: 18cb667e56cb328d2dda569d7f04e3ea78f2683135b866d606538cf7b1d4271f7f749f09608c877527799e6cf350e531368f3c7a20ccd1bb41048a48926bdeeb + languageName: node + linkType: hard + +"resolve@npm:^2.0.0-next.7": + version: 2.0.0-next.7 + resolution: "resolve@npm:2.0.0-next.7" + dependencies: + es-errors: ^1.3.0 + is-core-module: ^2.16.2 + node-exports-info: ^1.6.0 + object-keys: ^1.1.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 62d58c4b2fcd820da8ef4e0f37f14daaac3f1dd1bdc73b4c1b750c4705676a28f30281784d340a60240206f3398a7001d0feca35735bacdc90e01de6ebf606b8 + languageName: node + linkType: hard + +"resolve@patch:resolve@^2.0.0-next.7#~builtin": + version: 2.0.0-next.7 + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.7#~builtin::version=2.0.0-next.7&hash=c3c19d" + dependencies: + es-errors: ^1.3.0 + is-core-module: ^2.16.2 + node-exports-info: ^1.6.0 + object-keys: ^1.1.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 6385a1797ae12043ecdbdeed8d39da9099417edf3504495df4f395889cc3548afc1f71b51ae05b3235e82cef3551215b5e5b89cf5c9fb6360cd804df519b76bf + languageName: node + linkType: hard + +"safe-array-concat@npm:^1.1.3": + version: 1.1.4 + resolution: "safe-array-concat@npm:1.1.4" + dependencies: + call-bind: ^1.0.9 + call-bound: ^1.0.4 + get-intrinsic: ^1.3.0 + has-symbols: ^1.1.0 + isarray: ^2.0.5 + checksum: fd59dbf79f5ab6b56eb1b07bc7fd38ebdb9cb0478e7639606cd7a7f423d2fd22dc81eaf2371f74b5f3332ce75327669e1667272b34b33d06d07514e3e5305cf8 + languageName: node + linkType: hard + +"safe-push-apply@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-push-apply@npm:1.0.0" + dependencies: + es-errors: ^1.3.0 + isarray: ^2.0.5 + checksum: 8c11cbee6dc8ff5cc0f3d95eef7052e43494591384015902e4292aef4ae9e539908288520ed97179cee17d6ffb450fe5f05a46ce7a1749685f7524fd568ab5db + languageName: node + linkType: hard + +"safe-regex-test@npm:^1.1.0": + version: 1.1.0 + resolution: "safe-regex-test@npm:1.1.0" + dependencies: + call-bound: ^1.0.2 + es-errors: ^1.3.0 + is-regex: ^1.2.1 + checksum: 3c809abeb81977c9ed6c869c83aca6873ea0f3ab0f806b8edbba5582d51713f8a6e9757d24d2b4b088f563801475ea946c8e77e7713e8c65cdd02305b6caedab + languageName: node + linkType: hard + +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 + languageName: node + linkType: hard + +"set-function-length@npm:^1.2.2": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: ^1.1.4 + es-errors: ^1.3.0 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.4 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.2 + checksum: a8248bdacdf84cb0fab4637774d9fb3c7a8e6089866d04c817583ff48e14149c87044ce683d7f50759a8c50fb87c7a7e173535b06169c87ef76f5fb276dfff72 + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.2": + version: 2.0.2 + resolution: "set-function-name@npm:2.0.2" + dependencies: + define-data-property: ^1.1.4 + es-errors: ^1.3.0 + functions-have-names: ^1.2.3 + has-property-descriptors: ^1.0.2 + checksum: d6229a71527fd0404399fc6227e0ff0652800362510822a291925c9d7b48a1ca1a468b11b281471c34cd5a2da0db4f5d7ff315a61d26655e77f6e971e6d0c80f + languageName: node + linkType: hard + +"set-proto@npm:^1.0.0": + version: 1.0.0 + resolution: "set-proto@npm:1.0.0" + dependencies: + dunder-proto: ^1.0.1 + es-errors: ^1.3.0 + es-object-atoms: ^1.0.0 + checksum: ec27cbbe334598547e99024403e96da32aca3e530583e4dba7f5db1c43cbc4affa9adfbd77c7b2c210b9b8b2e7b2e600bad2a6c44fd62e804d8233f96bbb62f4 + languageName: node + linkType: hard + +"side-channel-list@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-list@npm:1.0.1" + dependencies: + es-errors: ^1.3.0 + object-inspect: ^1.13.4 + checksum: 3499671cd52adaee739eac1e14d07530b8e3530192741aeb05e7fe4ad1b51d1368ceea2cd3c21b0f62b05410a5c70a7c4d997ba4b143303ef73d0c65dfd1c252 + languageName: node + linkType: hard + +"side-channel-map@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-map@npm:1.0.1" + dependencies: + call-bound: ^1.0.2 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.5 + object-inspect: ^1.13.3 + checksum: 42501371cdf71f4ccbbc9c9e2eb00aaaab80a4c1c429d5e8da713fd4d39ef3b8d4a4b37ed4f275798a65260a551a7131fd87fe67e922dba4ac18586d6aab8b06 + languageName: node + linkType: hard + +"side-channel-weakmap@npm:^1.0.2": + version: 1.0.2 + resolution: "side-channel-weakmap@npm:1.0.2" + dependencies: + call-bound: ^1.0.2 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.5 + object-inspect: ^1.13.3 + side-channel-map: ^1.0.1 + checksum: a815c89bc78c5723c714ea1a77c938377ea710af20d4fb886d362b0d1f8ac73a17816a5f6640f354017d7e292a43da9c5e876c22145bac00b76cfb3468001736 + languageName: node + linkType: hard + +"side-channel@npm:^1.0.4, side-channel@npm:^1.1.0": + version: 1.1.1 + resolution: "side-channel@npm:1.1.1" + dependencies: + es-errors: ^1.3.0 + object-inspect: ^1.13.4 + side-channel-list: ^1.0.1 + side-channel-map: ^1.0.1 + side-channel-weakmap: ^1.0.2 + checksum: e0f217140c463636ee556260bbc8f47ba2931f1248826f58e1502704135814c763b325dd9ab122a04176aa45b4a4f8cc556415bcab7a0179d85250fb7812f063 + languageName: node + linkType: hard + +"stop-iteration-iterator@npm:^1.0.0, stop-iteration-iterator@npm:^1.1.0": + version: 1.1.0 + resolution: "stop-iteration-iterator@npm:1.1.0" + dependencies: + es-errors: ^1.3.0 + internal-slot: ^1.1.0 + checksum: be944489d8829fb3bdec1a1cc4a2142c6b6eb317305eeace1ece978d286d6997778afa1ae8cb3bd70e2b274b9aa8c69f93febb1e15b94b1359b11058f9d3c3a1 + languageName: node + linkType: hard + +"string.prototype.trim@npm:^1.2.10, string.prototype.trim@npm:^1.2.11": + version: 1.2.11 + resolution: "string.prototype.trim@npm:1.2.11" + dependencies: + call-bind: ^1.0.9 + call-bound: ^1.0.4 + define-data-property: ^1.1.4 + define-properties: ^1.2.1 + es-abstract: ^1.24.2 + es-object-atoms: ^1.1.2 + has-property-descriptors: ^1.0.2 + safe-regex-test: ^1.1.0 + checksum: 1aa0868afe15a54e781cd7fdcc851af4b0d71522108006f136ba35ecfde65b042496ca6926f85192923e6e9287750b772afb13860db15574bf1da454343db0ca + languageName: node + linkType: hard + +"string.prototype.trimend@npm:^1.0.9": + version: 1.0.10 + resolution: "string.prototype.trimend@npm:1.0.10" + dependencies: + call-bind: ^1.0.9 + call-bound: ^1.0.4 + define-properties: ^1.2.1 + es-object-atoms: ^1.1.2 + checksum: 17684796ccd12accafaef0f7cafe7a88891e4a57ff8deb5a40665dbe627fb3bed2252f1b9f3b16da2229aa41ba24e082178b44afe91a0302d570149730be1ccb + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimstart@npm:1.0.8" + dependencies: + call-bind: ^1.0.7 + define-properties: ^1.2.1 + es-object-atoms: ^1.0.0 + checksum: df1007a7f580a49d692375d996521dc14fd103acda7f3034b3c558a60b82beeed3a64fa91e494e164581793a8ab0ae2f59578a49896a7af6583c1f20472bce96 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 53b1e247e68e05db7b3808b99b892bd36fb096e6fba213a06da7fab22045e97597db425c724f2bbd6c99a3c295e1e73f3e4de78592289f38431049e1277ca0ae + languageName: node + linkType: hard + +"tape@npm:^5.0.1": + version: 5.10.2 + resolution: "tape@npm:5.10.2" + dependencies: + "@ljharb/now": ^1.0.1 + "@ljharb/resumer": ^0.1.3 + "@ljharb/through": ^2.3.14 + array.prototype.every: ^1.1.7 + call-bind: ^1.0.9 + call-bound: ^1.0.4 + deep-equal: ^2.2.3 + defined: ^1.0.1 + dotignore: ^0.1.2 + es-object-atoms: ^1.1.2 + for-each: ^0.3.5 + get-package-type: ^0.1.0 + glob: ^7.2.3 + has-dynamic-import: ^2.1.1 + hasown: ^2.0.4 + inherits: ^2.0.4 + is-regex: ^1.2.1 + minimist: ^1.2.8 + mock-property: ^1.1.0 + object-inspect: ^1.13.4 + object-is: ^1.1.6 + object-keys: ^1.1.1 + object.assign: ^4.1.7 + resolve: ^2.0.0-next.7 + string.prototype.trim: ^1.2.11 + bin: + tape: bin/tape + checksum: c10ff16aae0490451dc0f35c8a82c2d0c657eff011ca47d62e6e608f52457c497fafcb8d9417231e63d1d4151fed5420cb81385874009f18719399c498bdd72f + languageName: node + linkType: hard + +"typed-array-buffer@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-buffer@npm:1.0.3" + dependencies: + call-bound: ^1.0.3 + es-errors: ^1.3.0 + is-typed-array: ^1.1.14 + checksum: 3fb91f0735fb413b2bbaaca9fabe7b8fc14a3fa5a5a7546bab8a57e755be0e3788d893195ad9c2b842620592de0e68d4c077d4c2c41f04ec25b8b5bb82fa9a80 + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-byte-length@npm:1.0.3" + dependencies: + call-bind: ^1.0.8 + for-each: ^0.3.3 + gopd: ^1.2.0 + has-proto: ^1.2.0 + is-typed-array: ^1.1.14 + checksum: cda9352178ebeab073ad6499b03e938ebc30c4efaea63a26839d89c4b1da9d2640b0d937fc2bd1f049eb0a38def6fbe8a061b601292ae62fe079a410ce56e3a6 + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.4": + version: 1.0.5 + resolution: "typed-array-byte-offset@npm:1.0.5" + dependencies: + available-typed-arrays: ^1.0.7 + call-bind: ^1.0.9 + for-each: ^0.3.5 + gopd: ^1.2.0 + is-typed-array: ^1.1.15 + reflect.getprototypeof: ^1.0.10 + checksum: b314f3f1779b1f978704f72ed9ded61e7c8235f2712cfebae169312638c9e4736015516fb55c0ee9fe2d861e6111a9e44a782add667164477397a929e08e5fee + languageName: node + linkType: hard + +"typed-array-length@npm:^1.0.7": + version: 1.0.8 + resolution: "typed-array-length@npm:1.0.8" + dependencies: + call-bind: ^1.0.9 + for-each: ^0.3.5 + gopd: ^1.2.0 + is-typed-array: ^1.1.15 + possible-typed-array-names: ^1.1.0 + reflect.getprototypeof: ^1.0.10 + checksum: 612a90b6c86fed3aa8de7be20e05fd1fcdf4a5a0f2ce7a04da664b8f5b1ff2fb74a50f91d67dea9dbf2e526fbaac4046093fc0314af4e28533a7d1052d37fbd6 + languageName: node + linkType: hard + +"unbox-primitive@npm:^1.1.0": + version: 1.1.0 + resolution: "unbox-primitive@npm:1.1.0" + dependencies: + call-bound: ^1.0.3 + has-bigints: ^1.0.2 + has-symbols: ^1.1.0 + which-boxed-primitive: ^1.1.1 + checksum: 729f13b84a5bfa3fead1d8139cee5c38514e63a8d6a437819a473e241ba87eeb593646568621c7fc7f133db300ef18d65d1a5a60dc9c7beb9000364d93c581df + languageName: node + linkType: hard + +"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": + version: 1.1.1 + resolution: "which-boxed-primitive@npm:1.1.1" + dependencies: + is-bigint: ^1.1.0 + is-boolean-object: ^1.2.1 + is-number-object: ^1.1.1 + is-string: ^1.1.1 + is-symbol: ^1.1.1 + checksum: ee41d0260e4fd39551ad77700c7047d3d281ec03d356f5e5c8393fe160ba0db53ef446ff547d05f76ffabfd8ad9df7c9a827e12d4cccdbc8fccf9239ff8ac21e + languageName: node + linkType: hard + +"which-builtin-type@npm:^1.2.1": + version: 1.2.1 + resolution: "which-builtin-type@npm:1.2.1" + dependencies: + call-bound: ^1.0.2 + function.prototype.name: ^1.1.6 + has-tostringtag: ^1.0.2 + is-async-function: ^2.0.0 + is-date-object: ^1.1.0 + is-finalizationregistry: ^1.1.0 + is-generator-function: ^1.0.10 + is-regex: ^1.2.1 + is-weakref: ^1.0.2 + isarray: ^2.0.5 + which-boxed-primitive: ^1.1.0 + which-collection: ^1.0.2 + which-typed-array: ^1.1.16 + checksum: 7a3617ba0e7cafb795f74db418df889867d12bce39a477f3ee29c6092aa64d396955bf2a64eae3726d8578440e26777695544057b373c45a8bcf5fbe920bf633 + languageName: node + linkType: hard + +"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2": + version: 1.0.2 + resolution: "which-collection@npm:1.0.2" + dependencies: + is-map: ^2.0.3 + is-set: ^2.0.3 + is-weakmap: ^2.0.2 + is-weakset: ^2.0.3 + checksum: c51821a331624c8197916598a738fc5aeb9a857f1e00d89f5e4c03dc7c60b4032822b8ec5696d28268bb83326456a8b8216344fb84270d18ff1d7628051879d9 + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19": + version: 1.1.23 + resolution: "which-typed-array@npm:1.1.23" + dependencies: + available-typed-arrays: ^1.0.7 + call-bind: ^1.0.9 + call-bound: ^1.0.4 + for-each: ^0.3.5 + get-proto: ^1.0.1 + gopd: ^1.2.0 + has-tostringtag: ^1.0.2 + checksum: 56b195916bbc59a3a91c3d2ab743a6d7dfa56a873ca7ce8dfcd629faf214bcb082fdcbc1a55e890948acc8c48d059b89ef9a7eea886beeb3e65067bc5182329a + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard