@cloudbitmaps/core 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -8
- package/dist/chunk-7GT632UF.js +641 -0
- package/dist/chunk-7GT632UF.js.map +7 -0
- package/dist/chunk-CGILDXGP.js +73 -0
- package/dist/chunk-CGILDXGP.js.map +7 -0
- package/dist/chunk-GKHERVVQ.js +121 -0
- package/dist/chunk-GKHERVVQ.js.map +7 -0
- package/dist/{chunk-IGLIZBR7.js → chunk-MVUD4R72.js} +253 -765
- package/dist/chunk-MVUD4R72.js.map +7 -0
- package/dist/core/audit.d.ts +53 -15
- package/dist/core/bit-route.d.ts +9 -0
- package/dist/core/blob.d.ts +4 -0
- package/dist/core/budget.d.ts +8 -3
- package/dist/core/check.d.ts +27 -0
- package/dist/core/codec.d.ts +1 -1
- package/dist/core/combine-expr.d.ts +9 -5
- package/dist/core/combine-feed.d.ts +11 -3
- package/dist/core/combine-many.d.ts +28 -13
- package/dist/core/consistency.d.ts +11 -2
- package/dist/core/cooperative.d.ts +7 -0
- package/dist/core/crbm-storage-source.d.ts +7 -4
- package/dist/core/crypto.d.ts +5 -0
- package/dist/core/determinism.d.ts +2 -1
- package/dist/core/discover.d.ts +5 -3
- package/dist/core/engine.d.ts +4 -4
- package/dist/core/erase-id.d.ts +52 -19
- package/dist/core/erasure.d.ts +62 -28
- package/dist/core/errors.d.ts +83 -9
- package/dist/core/leases.d.ts +2 -0
- package/dist/core/load-input.d.ts +48 -0
- package/dist/core/load.d.ts +71 -16
- package/dist/core/metrics.d.ts +22 -1
- package/dist/core/pinned-storage-source.d.ts +27 -8
- package/dist/core/pointer-id.d.ts +9 -0
- package/dist/core/ports.d.ts +107 -13
- package/dist/core/preflight.d.ts +80 -0
- package/dist/core/registry-scan.d.ts +0 -5
- package/dist/core/retention-sweep.d.ts +61 -16
- package/dist/core/retention.d.ts +18 -7
- package/dist/core/retry.d.ts +5 -2
- package/dist/core/rollback.d.ts +23 -12
- package/dist/core/validate.d.ts +33 -2
- package/dist/driver-kit.d.ts +9 -4
- package/dist/driver-kit.js +63 -27
- package/dist/driver-kit.js.map +2 -2
- package/dist/drivers/_shared/object-registry.d.ts +24 -1
- package/dist/drivers/_shared/options.d.ts +15 -7
- package/dist/drivers/backends.d.ts +19 -2
- package/dist/drivers/crypto.d.ts +18 -3
- package/dist/export/index.d.ts +25 -10
- package/dist/index.d.ts +19 -17
- package/dist/index.js +2457 -2283
- package/dist/index.js.map +4 -4
- package/dist/node.d.ts +14 -0
- package/dist/node.js +1064 -0
- package/dist/node.js.map +7 -0
- package/package.json +16 -2
- package/dist/chunk-IGLIZBR7.js.map +0 -7
- package/dist/core/absent-operands.d.ts +0 -8
- package/dist/core/bulk-load.d.ts +0 -148
- package/dist/core/cas-retry.d.ts +0 -27
- package/dist/core/chunk-checks.d.ts +0 -39
- package/dist/core/chunk-stream.d.ts +0 -22
- package/dist/core/chunk-window.d.ts +0 -37
- package/dist/core/combine-ledger.d.ts +0 -43
- package/dist/core/crbm/crc32c.d.ts +0 -10
- package/dist/core/crbm/fingerprint.d.ts +0 -23
- package/dist/core/crbm/format.d.ts +0 -150
- package/dist/core/crbm/frame.d.ts +0 -10
- package/dist/core/crbm/plan-reads.d.ts +0 -50
- package/dist/core/crbm/varint.d.ts +0 -14
- package/dist/core/crbm/writer.d.ts +0 -72
- package/dist/core/crbm-open.d.ts +0 -154
- package/dist/core/crbm-write.d.ts +0 -75
- package/dist/core/due-index.d.ts +0 -53
- package/dist/core/generation-gc.d.ts +0 -293
- package/dist/core/generation-list.d.ts +0 -11
- package/dist/core/id-window.d.ts +0 -46
- package/dist/core/invalidation-scope.d.ts +0 -77
- package/dist/core/item-pull.d.ts +0 -29
- package/dist/core/known-generations-shape.d.ts +0 -13
- package/dist/core/known-generations.d.ts +0 -147
- package/dist/core/ledger-seed.d.ts +0 -17
- package/dist/core/metadata.d.ts +0 -42
- package/dist/core/order.d.ts +0 -2
- package/dist/core/publish.d.ts +0 -207
- package/dist/core/reader-defaults.d.ts +0 -29
- package/dist/core/summary.d.ts +0 -110
- package/dist/core/token.d.ts +0 -41
- package/dist/drivers/_shared/entropy.d.ts +0 -5
- package/dist/drivers/_shared/registry.d.ts +0 -107
- package/dist/drivers/localfs/exact-case.d.ts +0 -34
- package/dist/drivers/localfs/fs-util.d.ts +0 -55
- package/dist/drivers/localfs/paths.d.ts +0 -40
- package/dist/drivers/localfs/registry.d.ts +0 -50
- package/dist/drivers/localfs/storage.d.ts +0 -28
- package/dist/drivers/memory.d.ts +0 -64
- package/dist/testing/fuzz-core.d.ts +0 -15
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/drivers/_shared/options.ts", "../src/drivers/_shared/keys.ts", "../src/drivers/_shared/registry.ts", "../src/drivers/_shared/entropy.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Option-bag checks shared by the backends and the drivers, so each states \"refuse what can never be honoured\" once:\n * an options value that is not an object, a key the backend does not take (named, with the keys it does), a `now` that\n * is not a function, a size that is not a positive safe integer and a switch that is not a boolean. Each throws\n * `ValidationError`, so a wiring mistake fails where it is made rather than at the first read.\n */\nimport {\n refuseUnknownOptions,\n requireBool,\n requireNonEmptyString,\n requirePositiveInteger,\n shown,\n} from '@/core/check';\n\nexport { refuseUnknownOptions, requireBool, requireNonEmptyString };\nimport { ValidationError } from '@/core/errors';\n\n/** Refuse a `now` that is present and not a function. */\nexport function checkNow(name: string, now: unknown): void {\n if (now !== undefined && typeof now !== 'function') {\n throw new ValidationError(`${name}: \\`now\\` must be a function returning epoch milliseconds`);\n }\n}\n\n/**\n * The option `name`'s `value`, refused with `ValidationError` unless it is absent or a positive safe integer (a whole\n * number from 1 to 2^53 - 1): `0`, a negative, a fraction, `NaN`, an infinity and a string are refused by name. An\n * absent value comes back `undefined`, for the caller's default.\n */\nexport function requirePositiveInt(name: string, value: unknown): number | undefined {\n return value === undefined ? undefined : requirePositiveInteger(value, name);\n}\n\nconst DELETE_OPTION_KEYS: readonly string[] = ['ifVersion'];\nconst WRITE_OPTION_KEYS: readonly string[] = ['held', 'onToken'];\n\n/**\n * The per-call options of a storage port's `delete`, checked: absent, or an object holding only `ifVersion`, a string.\n * A misspelt key would otherwise read as no condition and delete whatever is under the key. Returns the `ifVersion`.\n */\nexport function checkDeleteOptions(options: unknown): string | undefined {\n if (options === undefined) return undefined;\n refuseUnknownOptions('delete options', options, DELETE_OPTION_KEYS);\n const { ifVersion } = options as { ifVersion?: unknown };\n if (ifVersion !== undefined && typeof ifVersion !== 'string') {\n throw new ValidationError(\n `delete options: \\`ifVersion\\` must be a string; got ${shown(ifVersion)}`,\n );\n }\n return ifVersion;\n}\n\n/**\n * The per-call options of a registry port's `create` and `compareAndSwap`, checked: absent, or an object holding only\n * `held` (a record or `null`) and `onToken` (a function). A misspelt key would otherwise drop the hint or the token report\n * a publish relies on.\n */\nexport function checkRegistryWriteOptions(options: unknown): void {\n if (options === undefined) return;\n refuseUnknownOptions('write options', options, WRITE_OPTION_KEYS);\n const { held, onToken } = options as { held?: unknown; onToken?: unknown };\n if (held !== undefined && held !== null && typeof held !== 'object') {\n throw new ValidationError(\n `write options: \\`held\\` must be a registry record or null; got ${shown(held)}`,\n );\n }\n if (onToken !== undefined && typeof onToken !== 'function') {\n throw new ValidationError(\n `write options: \\`onToken\\` must be a function; got ${shown(onToken)}`,\n );\n }\n}\n", "/**\n * Shared key-grammar fragments used by every driver's logical-ref \u2192 physical-key mapping.\n *\n * One copy of the `_default` namespace sentinel for every driver that maps a namespace to a key or a path. Pure\n * string logic, no SDK, no I/O \u2014 it lives in the SDK-free `_shared` bundle so any driver may import it.\n */\n\n/**\n * The physical stand-in for an **absent** namespace. A caller MAY name a namespace `_default`; it simply does not collide, because\n * `namespaceKeyPart`/`namespacePathPart` encode the caller's namespace (to `%5Fdefault`) and emit this\n * sentinel literally. The separation is a property of the encoding, not of a grammar; reading it as a grammar is\n * exactly what would make the collision easy to reintroduce.\n */\nexport const DEFAULT_NAMESPACE = '_default';\n\n/**\n * The physical namespace component of an **object key**: the caller's namespace encoded, or the sentinel.\n *\n * The sentinel is emitted **literally, never encoded**, and that asymmetry is the whole point. Encoding it too\n * would send an absent namespace to `%5Fdefault` \u2014 exactly where a caller who names their namespace `_default`\n * already goes, since the encoder escapes their leading underscore. Both would land in one place and read each\n * other's data. Encoding only the caller's side means `_default` (ours) and `%5Fdefault` (theirs) are two\n * different strings, and no caller can produce the first.\n */\nexport function namespaceKeyPart(namespace: string | undefined): string {\n return namespace === undefined ? DEFAULT_NAMESPACE : encodeNameForKey(namespace);\n}\n\n/** As {@link namespaceKeyPart}, for a filesystem path component. */\nexport function namespacePathPart(namespace: string | undefined): string {\n return namespace === undefined ? DEFAULT_NAMESPACE : encodeNameForPath(namespace);\n}\n\n// The name codec lives in `core/` (pure string logic, and `validate.ts` needs it); imported here so the\n// drivers keep one import for key shaping, and re-exported for the same reason.\nimport { encodeNameForKey, encodeNameForPath } from '@/core/name-codec';\n\nexport {\n encodeNameForKey,\n decodeNameFromKey,\n encodeNameForPath,\n decodeNameFromPath,\n} from '@/core/name-codec';\n", "/**\n * Shared, SDK-free helpers for assembling + validating {@link RegistryRecord}s.\n *\n * The registry's record-construction, patch-application, and field-validation are identical across every\n * backend (memory / LocalFs / object store) \u2014 only the *storage* + OCC mechanics differ. This is the one home\n * for that logic so the three drivers can't drift. Pure: no I/O, no SDK, no clock (the caller passes `now`), and no\n * ambient randomness (the caller passes its `Entropy`; the Web Crypto default is in `entropy.ts`).\n */\nimport { IntegrityError, UnsupportedError, ValidationError } from '@/core/errors';\nimport type { Entropy } from '@/core/determinism';\nimport { canonicalMetadataJson, MAX_METADATA_BYTES } from '@/core/metadata';\nimport { fingerprintParts } from '@/core/crbm/fingerprint';\nimport { INCARNATION_TOKEN, incarnationOf } from '@/core/token';\nimport { renewsPointer } from '@/core/pointer-id';\nimport { MAX_SUMMARY_CARDINALITY, tailBytesFit } from '@/core/summary';\nimport { MAX_LEASES_PER_SEGMENT, MAX_STORED_LEASES } from '@/core/leases';\nimport { knownGensShapeFault } from '@/core/known-generations-shape';\nimport {\n MAX_KNOWN_GENERATIONS,\n MAX_STORED_KNOWN_GENERATIONS,\n usableKnownGens,\n} from '@/core/known-generations';\nimport type {\n KnownGeneration,\n LeaseEntry,\n NewRegistryRecord,\n RegistryPatch,\n RegistryRecord,\n RegistrySummary,\n SegmentRef,\n Token,\n} from '@/core/ports';\n\n/** The valid {@link RegistryStatus} values \u2014 used to validate both caller input and stored bytes. */\nconst STATUSES: readonly string[] = ['active', 'destroyed'];\n/**\n * The fields a stored record may carry: {@link RegistryRecord}'s, and nothing else. A field no reader resolves\n * through is refused on read-back like any other corruption (invariant 5), rather than ignored. A field the record\n * gains is listed here with its validator; the registry test fails to compile when this list and the record disagree.\n */\nexport const RECORD_FIELDS = [\n 'namespace',\n 'segment',\n 'currentGen',\n 'wrappedDeks',\n 'keyId',\n 'status',\n 'retention',\n 'residency',\n 'summary',\n 'createdAt',\n 'updatedAt',\n 'token',\n 'knownGens',\n 'leases',\n 'pointerId',\n] as const;\n/** The fields the registry sets on a row and a caller's write never does. */\nconst DRIVER_MANAGED: readonly string[] = ['token', 'pointerId'];\n/** The fields of the persisted envelope around a record. */\nconst ENVELOPE_FIELDS: readonly string[] = ['schemaVersion', 'deleted', 'record'];\n/** Cap on a serialized governance blob (retention/residency) \u2014 bounds row size so a row can't be bricked. */\nconst MAX_GOVERNANCE_BYTES = 64 * 1024;\n/** Bounds on the wrapped-DEK list (one DEK wrapped under a few KEKs) \u2014 keeps the row small + rejects abuse. */\nconst MAX_WRAPPED_DEKS = 8;\nconst MAX_WRAPPED_DEK_BYTES = 4 * 1024;\n/**\n * A sealed summary's fixed part: a 12-byte nonce, the u64 count, the object's fingerprint (a u64 size and a u32 footer\n * checksum), the u32 `tailBytes`, and a 16-byte tag.\n */\nconst SEALED_SUMMARY_MIN_BYTES = 12 + 8 + 8 + 4 + 4 + 16;\n/** The fixed part plus metadata at its cap. */\nconst SEALED_SUMMARY_MAX_BYTES = SEALED_SUMMARY_MIN_BYTES + MAX_METADATA_BYTES;\n/** Padded standard base64; canonical once its pad bits are checked too (see {@link base64PadBitsAreZero}). */\nconst BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;\n\n/** `null` is legal and meaningful: the segment has no Storage generation yet (see `RegistryRecord.currentGen`). */\nfunction validateGeneration(gen: number | null): void {\n if (gen === null) return;\n if (!Number.isSafeInteger(gen) || gen < 0) {\n throw new ValidationError(\n `currentGen must be a non-negative safe integer or null (no Storage generation yet); got ${gen}`,\n );\n }\n}\n\n/**\n * A patch that *mentions* `currentGen` must give it a real value \u2014 `null` to clear the pointer, or a generation.\n * `{ currentGen: undefined }` is refused rather than coerced. `{ currentGen: maybeUndefined }` is what `strict`\n * alone permits, since `exactOptionalPropertyTypes` is off, and a caller writing it means to leave the pointer\n * alone. Under presence-based merging the same call would silently *un-publish* a live segment: every Storage\n * generation goes invisible, and the collection then refuses to collect the objects (no pointer \u21D2 nothing\n * to compare against), so they are stranded and billed forever. Fail fast at the boundary instead.\n */\nfunction validatePatchGeneration(patch: RegistryPatch): void {\n if (!('currentGen' in patch)) return;\n // `validateGeneration(undefined)` fails the integer test on its own, because this check reads the raw patch\n // before `applyRegistryPatch` turns an undefined `currentGen` into `null`; this branch exists to make the\n // *message* say which value to pass instead, because the generic \"must be a non-negative integer or null\"\n // reads like a type error rather than the trap it is.\n if (patch.currentGen === undefined) {\n throw new ValidationError(\n `currentGen was present in the patch but undefined. Pass \\`null\\` to clear the pointer (the segment has no ` +\n `Storage generation), or omit the key to leave it unchanged \u2014 an undefined value is refused because it ` +\n `would un-publish the segment's Storage data.`,\n );\n }\n validateGeneration(patch.currentGen);\n}\n\nfunction validateStatus(status: string): void {\n if (!STATUSES.includes(status)) {\n throw new ValidationError(`status must be one of ${STATUSES.join('/')}; got ${status}`);\n }\n}\n\n/**\n * Validate a governance blob (retention/residency) at the write boundary: it must be JSON-serializable\n * (a `BigInt`/function/circular value fails fast with a typed error instead of a cryptic one deep in a\n * driver) and within a size cap (so an oversized blob can't write a row that's later unreadable \u2014 a brick).\n */\nfunction validateGovernance(meta: unknown, field: string): void {\n if (meta === undefined) return;\n // A plain object, not `null`/an array/a primitive. `JSON.stringify(null)` is 4 valid bytes, so without this the\n // size + serializability checks below happily store `\"retention\": null` \u2014 and `retention.expiresAt` is read with\n // an `in` test, which throws an untyped `TypeError` on a non-object and would abort a whole fleet retention\n // sweep rather than becoming one ledger entry. Reject at the write boundary; the read boundary\n // ({@link assertStoredRecordShape}) rejects it too, for a row edited by hand or written by another writer.\n if (meta === null || typeof meta !== 'object' || Array.isArray(meta)) {\n throw new ValidationError(\n `${field} must be a plain object (got ${meta === null ? 'null' : typeof meta})`,\n );\n }\n let json: string;\n try {\n json = JSON.stringify(meta);\n } catch {\n throw new ValidationError(`${field} must be JSON-serializable`);\n }\n if (json.length > MAX_GOVERNANCE_BYTES) {\n throw new ValidationError(`${field} is ${json.length}B, exceeds cap ${MAX_GOVERNANCE_BYTES}B`);\n }\n}\n\n/**\n * Whose value a validator is looking at: one a `caller` is writing (a bad one is a ValidationError, a list or summary\n * it returns is a frozen copy, and `knownGens` is held to {@link MAX_KNOWN_GENERATIONS}), or a row read back from\n * `stored` bytes (a bad one is an IntegrityError, `knownGens` is held to {@link MAX_STORED_KNOWN_GENERATIONS}, and the\n * parsed value is kept).\n */\ntype RecordOrigin = 'caller' | 'stored';\n\n/**\n * Validate the wrapped-DEK list at the write/read boundary: a bounded array of `{ keyId, wrapped }` with\n * non-empty string fields and a per-entry size cap (so a row can't be bricked, and corrupt bytes are rejected\n * rather than reaching the keystore). `origin` toggles the error class (write = ValidationError; read =\n * IntegrityError, invariant 5).\n */\nfunction validateWrappedDeks(value: unknown, origin: RecordOrigin): void {\n if (value === undefined) return;\n const fail = (msg: string): never => {\n throw origin === 'stored' ? new IntegrityError(msg) : new ValidationError(msg);\n };\n if (!Array.isArray(value)) fail('wrappedDeks must be an array');\n const list = value as unknown[];\n if (list.length === 0) fail('wrappedDeks must be non-empty when present');\n if (list.length > MAX_WRAPPED_DEKS)\n fail(`wrappedDeks has ${list.length} entries, cap ${MAX_WRAPPED_DEKS}`);\n for (const w of list) {\n if (w === null || typeof w !== 'object') fail('wrappedDeks entry must be an object');\n const { keyId, wrapped } = w as { keyId?: unknown; wrapped?: unknown };\n if (typeof keyId !== 'string' || keyId.length === 0)\n fail('wrappedDeks entry needs a non-empty keyId');\n if (typeof wrapped !== 'string' || wrapped.length === 0)\n fail('wrappedDeks entry needs a non-empty wrapped blob');\n if ((wrapped as string).length > MAX_WRAPPED_DEK_BYTES)\n fail(`wrappedDeks entry is ${(wrapped as string).length}B, cap ${MAX_WRAPPED_DEK_BYTES}B`);\n }\n}\n\n/** The standard base64 alphabet, whose index of a character is the six bits it carries. */\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/**\n * Whether a padded standard base64 string is canonical: the bits of its last character past the data are zero.\n * Without this two strings would decode to the same bytes.\n */\nfunction base64PadBitsAreZero(text: string): boolean {\n const pad = text.endsWith('==') ? 2 : text.endsWith('=') ? 1 : 0;\n if (pad === 0) return true;\n const last = BASE64_ALPHABET.indexOf(text[text.length - pad - 1]!);\n return pad === 2 ? (last & 0b1111) === 0 : (last & 0b11) === 0;\n}\n\n/**\n * Validate a {@link RegistrySummary}'s shape at the write or read boundary, and return a frozen copy built from the\n * checked fields only: exactly one of the two shapes, a non-negative safe-integer generation, a cardinality from 0 to\n * 2^32, the fingerprint of an object (`<size>:<footer checksum>`, a size no smaller than a `.crbm` preamble and footer\n * and a 32-bit checksum), `tailBytes` (an integer from a footer's size to the object's size less its preamble, and at\n * most the cap a reader fetches for one open), metadata by the metadata rules (never the empty object, which is never stored) with its keys\n * in canonical order, or a sealed blob in canonical base64 whose length fits a sealed count and fingerprint plus\n * metadata at its cap. A writer\n * stores the copy, so a caller that changes its object after the call, while the driver awaits the row, changes\n * nothing that is written. Whether a summary may be *used* is the reader's rule, not this one: this only bounds what a\n * row can hold. `origin` picks the error class (write = ValidationError; read = IntegrityError, invariant 5), and a\n * read names the row by `ctx`.\n */\nfunction validateSummary(\n value: unknown,\n origin: RecordOrigin,\n ctx?: string,\n): RegistrySummary | undefined {\n if (value === undefined) return undefined;\n const fail = (msg: string): never => {\n const where = ctx === undefined ? '' : `: ${ctx}`;\n throw origin === 'stored'\n ? new IntegrityError(`registry record summary: ${msg}${where}`)\n : new ValidationError(`summary: ${msg}${where}`);\n };\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n fail(\n `must be an object (got ${value === null ? 'null' : Array.isArray(value) ? 'an array' : typeof value})`,\n );\n }\n const s = value as Record<string, unknown>;\n const keys = Object.keys(s);\n const isSealed = keys.includes('sealed');\n const allowed = isSealed\n ? ['generation', 'sealed']\n : ['generation', 'cardinality', 'fingerprint', 'tailBytes', 'metadata'];\n const extra = keys.filter((k) => !allowed.includes(k));\n if (extra.length > 0) {\n fail(\n `has fields its ${isSealed ? 'sealed' : 'clear'} shape does not declare (${extra.join(', ')})`,\n );\n }\n const generation = s.generation;\n if (!Number.isSafeInteger(generation) || (generation as number) < 0) {\n fail(`generation must be a non-negative safe integer (got ${String(generation)})`);\n }\n if (isSealed) {\n const sealed = s.sealed;\n if (typeof sealed !== 'string' || !BASE64.test(sealed) || !base64PadBitsAreZero(sealed)) {\n fail('sealed must be canonical, padded standard base64');\n }\n const text = sealed as string;\n const bytes = (text.length / 4) * 3 - (text.endsWith('==') ? 2 : text.endsWith('=') ? 1 : 0);\n if (bytes < SEALED_SUMMARY_MIN_BYTES || bytes > SEALED_SUMMARY_MAX_BYTES) {\n fail(\n `sealed is ${bytes}B, outside ${SEALED_SUMMARY_MIN_BYTES}..${SEALED_SUMMARY_MAX_BYTES}B ` +\n '(a nonce, a fixed-width count, fingerprint and tail size, a tag, and metadata up to its cap)',\n );\n }\n return Object.freeze({ generation: generation as number, sealed: text });\n }\n const n = s.cardinality;\n if (!Number.isInteger(n) || (n as number) < 0 || (n as number) > MAX_SUMMARY_CARDINALITY) {\n fail(`cardinality must be an integer from 0 to 2^32 (got ${String(n)})`);\n }\n const fingerprint = s.fingerprint;\n const parts = fingerprintParts(fingerprint);\n if (parts === undefined) {\n fail(\n `fingerprint must be an object's, <size>:<footer checksum> in decimal (got ${shown(JSON.stringify(fingerprint))})`,\n );\n }\n const tailBytes = s.tailBytes;\n if (!tailBytesFit(tailBytes, parts!.size)) {\n fail(\n `tailBytes must be an integer from the footer's size to the object's size less its preamble, and at most ` +\n `the cap of one open's tail read (got ${shown(JSON.stringify(tailBytes))} for an object of ${parts!.size} bytes)`,\n );\n }\n if (s.metadata === undefined) {\n return Object.freeze({\n generation: generation as number,\n cardinality: n as number,\n fingerprint: fingerprint as string,\n tailBytes: tailBytes as number,\n });\n }\n const canonical = canonicalMetadataJson(s.metadata, fail);\n if (canonical === '{}') fail('metadata is empty; a generation without metadata carries none');\n // A read only checks the stored shape and keeps the object it parsed, so it builds no copy.\n if (origin === 'stored') return s as unknown as RegistrySummary;\n // Rebuilt from the canonical JSON, so its keys are in canonical order (JavaScript still lists integer-like keys\n // first, in numeric order, as it does in every object) and nothing of the caller's object is kept.\n const metadata = Object.freeze(JSON.parse(canonical) as Record<string, string | number>);\n return Object.freeze({\n generation: generation as number,\n cardinality: n as number,\n fingerprint: fingerprint as string,\n tailBytes: tailBytes as number,\n metadata,\n });\n}\n\n/** A summary given at a write must describe the generation the row will point at. */\nfunction validateSummaryNames(\n summary: RegistrySummary | undefined,\n currentGen: number | null,\n): void {\n if (summary !== undefined && summary.generation !== currentGen) {\n throw new ValidationError(\n `summary describes generation ${summary.generation}, but the row points at ${String(currentGen)}`,\n );\n }\n}\n\n/**\n * Whether a summary's shape agrees with the row's encryption: sealed on a row with wrapped keys, clear on one without.\n * A clear count or metadata beside a key would leak what the segment's objects hide; a sealed one on a cleartext row\n * could never be opened.\n */\nfunction summaryAgreesWithKeys(\n summary: RegistrySummary,\n wrappedDeks: RegistryRecord['wrappedDeks'],\n): boolean {\n return 'sealed' in summary === (wrappedDeks !== undefined);\n}\n\n/** A summary given at a write must agree with the row's encryption (see {@link summaryAgreesWithKeys}). */\nfunction validateSummaryKeys(\n summary: RegistrySummary | undefined,\n wrappedDeks: RegistryRecord['wrappedDeks'],\n): void {\n if (summary !== undefined && !summaryAgreesWithKeys(summary, wrappedDeks)) {\n throw new ValidationError(\n wrappedDeks === undefined\n ? 'summary is sealed, but the row has no wrapped keys to open it with'\n : \"summary is in the clear, but the row is encrypted: an encrypted segment's summary must be sealed\",\n );\n }\n}\n\n/**\n * Validate a {@link RegistryRecord.knownGens} list's shape at the write or read boundary: an array of at most `max`\n * entries (192 written, 384 read), each exactly `{ generation, footerCrc }`, the generation a non-negative safe integer\n * and unique, the checksum an integer from 0 to 2^32 - 1. The order is the order the generations were made current and\n * is kept as given: it cannot be checked, since it is not derivable from the numbers. A write returns a frozen copy, so\n * a caller that changes its array afterwards changes nothing that is written. `origin` picks the error class (write =\n * ValidationError; read = IntegrityError, invariant 5). Whether the last entry is the pointer, and whether its checksum\n * is the summary's, is not checked here on a read: a ledger that disagrees with its row is one its reader does not use,\n * not a row to refuse.\n */\nfunction validateKnownGens(\n value: unknown,\n origin: RecordOrigin,\n ctx?: string,\n): readonly KnownGeneration[] | undefined {\n if (value === undefined) return undefined;\n const fail = (msg: string): never => {\n const where = ctx === undefined ? '' : `: ${ctx}`;\n throw origin === 'stored'\n ? new IntegrityError(`registry record knownGens: ${msg}${where}`)\n : new ValidationError(`knownGens: ${msg}`);\n };\n const max = origin === 'stored' ? MAX_STORED_KNOWN_GENERATIONS : MAX_KNOWN_GENERATIONS;\n const fault = knownGensShapeFault(value, max, shown);\n if (fault !== undefined) fail(fault);\n const out: KnownGeneration[] = [];\n for (const entry of value as readonly KnownGeneration[]) {\n out.push(\n origin === 'stored'\n ? entry\n : Object.freeze({ generation: entry.generation, footerCrc: entry.footerCrc }),\n );\n }\n return origin === 'stored' ? out : Object.freeze(out);\n}\n\nconst HOLDER = /^[0-9a-f]{16}$/;\nconst LEASE_ENTRY_KEYS: readonly string[] = ['holder', 'generation', 'until'];\n\n/**\n * Validate a {@link RegistryRecord.leases} list at the write or read boundary: an array of at most\n * {@link MAX_STORED_LEASES} entries whatever the origin (a write's own, smaller cap is applied against the row it\n * patches), each entry exactly `{ holder, generation, until }`, the holder 16 lowercase hex digits\n * and unique, the two numbers non-negative safe integers. A write returns a frozen copy, and an empty list as\n * `undefined`, so a row that never held a lease is the row it was before leases. `origin` picks the error class\n * (write = ValidationError; read = IntegrityError, invariant 5). Whether an entry is still live, or its generation\n * exists, is not checked here: a collector reads that, and a row is not refused for it.\n */\nfunction validateLeases(\n value: unknown,\n origin: RecordOrigin,\n ctx?: string,\n): readonly LeaseEntry[] | undefined {\n if (value === undefined) return undefined;\n const fail = (msg: string): never => {\n const where = ctx === undefined ? '' : `: ${ctx}`;\n throw origin === 'stored'\n ? new IntegrityError(`registry record leases: ${msg}${where}`)\n : new ValidationError(`leases: ${msg}`);\n };\n if (!Array.isArray(value)) fail('must be an array');\n const list = value as unknown[];\n const max = MAX_STORED_LEASES; // a write's own cap is applied against the row it patches\n if (list.length > max) fail(`has ${list.length} entries, cap ${max}`);\n const seen = new Set<string>();\n const out: LeaseEntry[] = [];\n for (const e of list) {\n if (typeof e !== 'object' || e === null || Array.isArray(e)) fail('an entry must be an object');\n const entry = e as Record<string, unknown>;\n const extra = Object.keys(entry).filter((k) => !LEASE_ENTRY_KEYS.includes(k));\n if (extra.length > 0) fail(`an entry carries an undeclared field (${extra.join(', ')})`);\n const { holder, generation, until } = entry;\n if (typeof holder !== 'string' || !HOLDER.test(holder)) {\n fail('a holder must be 16 lowercase hex digits');\n }\n if (seen.has(holder as string)) fail('holders must be unique');\n seen.add(holder as string);\n for (const [name, n] of [\n ['generation', generation],\n ['until', until],\n ] as const) {\n if (!Number.isSafeInteger(n) || (n as number) < 0) {\n fail(`${name} must be a non-negative safe integer (got ${String(n)})`);\n }\n }\n out.push(\n origin === 'stored'\n ? (entry as unknown as LeaseEntry)\n : Object.freeze({\n holder: holder as string,\n generation: generation as number,\n until: until as number,\n }),\n );\n }\n if (origin === 'stored') return out;\n return out.length === 0 ? undefined : Object.freeze(out);\n}\n\n/** A non-empty list given at a write needs a pointer to hold generations of. */\nfunction validateLeasesPointer(\n leases: readonly LeaseEntry[] | undefined,\n currentGen: number | null,\n): void {\n if (leases !== undefined && leases.length > 0 && currentGen === null) {\n throw new ValidationError('leases: a row with no pointer has no generation to lease');\n }\n}\n\n/**\n * A ledger given at a write must fit the row it makes: none on a row with no pointer, its last entry the row's resulting\n * `currentGen`, and, where the row's resulting summary is in the clear and describes that generation, its checksum the\n * one the summary's fingerprint carries. A registry cannot check a sealed summary's: the library's writers take both\n * values from one object.\n */\nfunction validateKnownGensFit(\n knownGens: readonly KnownGeneration[] | undefined,\n currentGen: number | null,\n summary: RegistrySummary | undefined,\n): void {\n if (knownGens === undefined) return;\n if (currentGen === null) {\n throw new ValidationError('knownGens: a row with no pointer has no generation to know');\n }\n const last = knownGens[knownGens.length - 1];\n if (last === undefined || last.generation !== currentGen) {\n throw new ValidationError(\n `knownGens must end with the row's pointer ${currentGen}, in the order the generations were made current ` +\n `(its last entry is ${last === undefined ? 'missing' : last.generation})`,\n );\n }\n if (\n summary !== undefined &&\n summary.generation === currentGen &&\n 'fingerprint' in summary &&\n fingerprintParts(summary.fingerprint)?.footerCrc !== last.footerCrc\n ) {\n throw new ValidationError(\n `knownGens records footerCrc ${last.footerCrc} for generation ${currentGen}, but the summary's ` +\n `fingerprint ${summary.fingerprint} carries another: both describe the one current object`,\n );\n }\n}\n\n/**\n * A write that names a field the registry sets (`token`, `pointerId`) is refused rather than stored or ignored: the\n * type does not allow either, so one arrives only from a caller in JavaScript, or a whole record passed as a patch, and\n * either would read as if it had set the row's identity.\n */\nfunction refuseDriverManaged(write: object, what: string): void {\n const named = DRIVER_MANAGED.filter((field) => field in write);\n if (named.length > 0) {\n throw new ValidationError(\n `${what} names ${named.join(' and ')}, which the registry sets on every write and a caller never does`,\n );\n }\n}\n\n/**\n * A `keyId` is absent or a non-empty string. A read accepts any string, so this is stricter: an empty id names no key,\n * and a value of another type would make the row unreadable.\n */\nfunction validateKeyId(keyId: unknown): void {\n if (keyId !== undefined && (typeof keyId !== 'string' || keyId.length === 0)) {\n throw new ValidationError('keyId must be a non-empty string, or absent');\n }\n}\n\n/**\n * Validate the caller-settable fields at `create`, and return the record to store: the caller's, with its summary\n * replaced by the checked, frozen copy {@link validateSummary} builds.\n */\nexport function validateNewRegistryRecord(rec: NewRegistryRecord): NewRegistryRecord {\n refuseDriverManaged(rec, 'a new record');\n validateKeyId(rec.keyId);\n validateGeneration(rec.currentGen);\n if (rec.status !== undefined) validateStatus(rec.status);\n validateWrappedDeks(rec.wrappedDeks, 'caller');\n validateGovernance(rec.retention, 'retention');\n validateGovernance(rec.residency, 'residency');\n const knownGens = validateKnownGens(rec.knownGens, 'caller');\n const withKnown = knownGens === undefined ? rec : { ...rec, knownGens };\n const summary = 'summary' in rec ? validateSummary(rec.summary, 'caller') : undefined;\n validateKnownGensFit(knownGens, rec.currentGen, summary);\n if (!('summary' in rec)) return withKnown;\n validateSummaryNames(summary, rec.currentGen);\n validateSummaryKeys(summary, rec.wrappedDeks);\n return { ...withKnown, summary };\n}\n\n/**\n * Validate the caller-settable fields in a `compareAndSwap` patch, and return the patch to apply: the caller's, with\n * its summary replaced by the checked, frozen copy. That a given `summary` names the row's resulting `currentGen`,\n * and agrees with its resulting keys, needs the stored row, so {@link applyRegistryPatch} checks both.\n */\nexport function validateRegistryPatch(patch: RegistryPatch): RegistryPatch {\n refuseDriverManaged(patch, 'a patch');\n if ('keyId' in patch) validateKeyId(patch.keyId);\n validatePatchGeneration(patch);\n if (patch.status !== undefined) validateStatus(patch.status);\n if ('wrappedDeks' in patch) validateWrappedDeks(patch.wrappedDeks, 'caller');\n if ('retention' in patch) validateGovernance(patch.retention, 'retention');\n if ('residency' in patch) validateGovernance(patch.residency, 'residency');\n const withKnown =\n patch.knownGens === undefined\n ? patch\n : { ...patch, knownGens: validateKnownGens(patch.knownGens, 'caller') };\n const withLeases =\n 'leases' in withKnown\n ? { ...withKnown, leases: validateLeases(withKnown.leases, 'caller') }\n : withKnown;\n if (!('summary' in patch)) return withLeases;\n return { ...withLeases, summary: validateSummary(patch.summary, 'caller') };\n}\n\n/**\n * The persisted envelope for a registry row: the record plus a tombstone flag. A **deleted** row keeps its\n * record (with an advanced counter) rather than being removed, so a re-create carries the counter on and the two\n * incarnations' tokens are apart by construction, as well as by their random parts \u2014 ABA-safety. A row removed by a\n * conditional delete leaves no counter to carry on, so a re-create starts at 0 and only the random parts keep its\n * tokens apart from the old ones. Shared by the\n * persistent drivers (LocalFs file, S3 object).\n *\n * `schemaVersion` is a wire-only concern \u2014 it stamps the persisted bytes, not the in-memory domain object \u2014\n * so it lives on the serialize/parse boundary ({@link serializeRegistryEnvelope} /\n * {@link parseRegistryEnvelope}), never on this interface.\n */\nexport interface RegistryEnvelope {\n readonly deleted: boolean;\n readonly record: RegistryRecord;\n}\n\n/**\n * Current registry-row schema version \u2014 a pre-1.0 format-freeze prerequisite. Every persistent\n * registry driver \u2014 every one of which persists the `{ deleted, record }` envelope \u2014 stamps its rows\n * with this so a reader can fail-closed on a layout it does not read instead of misparsing it. Bump only\n * on a backward-incompatible change. Policy: any stamp but this one \u2192 `UnsupportedError` (fail-closed); an\n * **absent** or malformed stamp \u2192 `IntegrityError`, since every row this build writes has one.\n *\n * Schema 5 is the record with `knownGens`, the ledger of the generations a row made current (each with its object's footer\n * checksum, in the order they were made current), and a summary that records `tailBytes`. This build reads schema 5 and\n * nothing else: a row stamped lower carries no ledger and, below 4, no `pointerId` either, so nothing could say which\n * generations the row published or which write set what it resolves to.\n */\nexport const REGISTRY_SCHEMA_VERSION = 5;\n\n/**\n * Validate a persisted registry row's `schemaVersion` (untrusted bytes, invariant 5) and return it: an absent or\n * malformed value \u2192 `IntegrityError`; any version but {@link REGISTRY_SCHEMA_VERSION} \u2192 `UnsupportedError`\n * (fail-closed rather than misread a format this build does not read), naming what to do for an earlier one. The steps\n * are in the changelog's entry for the release that introduced the schema; the message names that entry and no anchor,\n * which cannot go stale.\n */\nexport function assertRegistrySchemaVersion(raw: unknown, ctx: string): number {\n if (raw === undefined) {\n throw new IntegrityError(`registry row has no schemaVersion: ${ctx}`);\n }\n if (typeof raw !== 'number' || !Number.isInteger(raw) || raw < 1) {\n throw new IntegrityError(`registry row has a malformed schemaVersion (${shown(raw)}): ${ctx}`);\n }\n if (raw < REGISTRY_SCHEMA_VERSION) {\n throw new UnsupportedError(\n `registry row schemaVersion ${raw} is one this build does not read: it reads schema ` +\n `${REGISTRY_SCHEMA_VERSION} only. Export the segments with the release that wrote the row and load them ` +\n `with this one into a new prefix (steps: ` +\n `https://github.com/cloudbitmaps/cloudbitmaps/blob/main/CHANGELOG.md, the Breaking entry on registry schema 5): ${ctx}`,\n );\n }\n if (raw > REGISTRY_SCHEMA_VERSION) {\n throw new UnsupportedError(\n `registry row schemaVersion ${raw} is newer than this build reads (v${REGISTRY_SCHEMA_VERSION}): ${ctx}`,\n );\n }\n return raw;\n}\n\n/** Serialize a registry envelope for persistence, stamping the current {@link REGISTRY_SCHEMA_VERSION}. */\nexport function serializeRegistryEnvelope(env: RegistryEnvelope): string {\n return JSON.stringify({\n schemaVersion: REGISTRY_SCHEMA_VERSION,\n deleted: env.deleted,\n record: env.record,\n });\n}\n\n// \u2500\u2500 The OCC token \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A token is `<incarnation>.<counter>.<write>`. The incarnation is 32 lowercase hex digits drawn from injected\n// entropy when the row is created; it keeps a re-created name from ever being taken for an earlier incarnation of\n// it, even once the earlier row's record is gone and nothing is left to continue a counter from. The counter is a\n// canonical decimal that every write of the row advances by one, and carries on across a tombstone, so while a row's\n// history is intact its tokens are apart by construction. The write part is 16 lowercase hex digits drawn afresh for\n// every write: a row restored from a backup is back at an older counter, and without it the writes after the restore\n// would be given the tokens the writes after the backup already had. Only a create starts a new incarnation. Tokens\n// are compared only by equality; nothing orders them. A row's `pointerId` is the token of one of its writes, so it has\n// the same form, the row's incarnation, and a counter no higher than the token's.\n\n/** The incarnation id's width, in bytes. */\nconst INCARNATION_BYTES = 16;\n/** The write part's width, in bytes. */\nconst WRITE_BYTES = 8;\n\n/** A token taken apart: its incarnation and its counter. */\ninterface TokenParts {\n readonly incarnation: string;\n readonly counter: number;\n}\n\n/**\n * Take a token apart, refusing anything that is not the form a shipped driver writes (invariant 5): `\"7\"`, `\"1e3\"`,\n * `\"0x10\"`, `\" 5 \"`, `\"\"`, a counter past 2^53, upper-case hex. `what` names the field in the message.\n */\nfunction tokenParts(token: unknown, ctx: string, what = 'token'): TokenParts {\n const match = typeof token === 'string' ? INCARNATION_TOKEN.exec(token) : null;\n if (match === null) {\n throw new IntegrityError(\n `registry row ${what} is not one a shipped registry writes (${shown(JSON.stringify(token))}): ${ctx}`,\n );\n }\n const counter = Number(match[2]);\n if (!Number.isSafeInteger(counter)) {\n throw new IntegrityError(`registry row ${what}'s counter is out of safe-integer range: ${ctx}`);\n }\n return { incarnation: match[1]!, counter };\n}\n\n/**\n * Refuse a stored `pointerId` a shipped registry cannot have written (invariant 5): one in another form, of another\n * incarnation than the row's token, or with a higher counter than the token's. One of another incarnation was spliced\n * from another row, and a reader trusting it would key a cache on another incarnation's identity.\n */\nfunction checkPointerId(pointerId: unknown, token: TokenParts, ctx: string): void {\n const parts = tokenParts(pointerId, ctx, 'pointerId');\n if (parts.incarnation !== token.incarnation) {\n throw new IntegrityError(\n `registry row pointerId names another incarnation than its token: ${ctx}`,\n );\n }\n if (parts.counter > token.counter) {\n throw new IntegrityError(`registry row pointerId is a write after its token: ${ctx}`);\n }\n}\n\nexport { usableKnownGens };\n\n// `incarnationOf` is defined in `core/token`, where the publish reads it too, and is the same function here.\nexport { incarnationOf };\n\n/** `bytes` fresh bytes from `entropy`, as lowercase hex, refusing a source that does not give them. */\nfunction drawHex(entropy: Entropy, bytes: number): string {\n const drawn = entropy(bytes);\n if (!(drawn instanceof Uint8Array) || drawn.length !== bytes) {\n throw new ValidationError(\n `the registry's entropy source must return ${bytes} bytes as a Uint8Array`,\n );\n }\n let hex = '';\n for (const b of drawn) hex += b.toString(16).padStart(2, '0');\n return hex;\n}\n\n/** A fresh incarnation id from `entropy`: 128 bits as 32 lowercase hex digits. */\nexport function drawIncarnation(entropy: Entropy): string {\n return drawHex(entropy, INCARNATION_BYTES);\n}\n\n/** A fresh write part from `entropy`: 64 bits as 16 lowercase hex digits. */\nexport function drawWrite(entropy: Entropy): string {\n return drawHex(entropy, WRITE_BYTES);\n}\n\n/**\n * The token for the write after `record`'s: the same incarnation, the counter one on, and a fresh write part. For a\n * compare-and-swap or a tombstone.\n */\nexport function nextRegistryToken(record: RegistryRecord, entropy: Entropy): Token {\n const { incarnation, counter } = tokenParts(record.token, record.segment);\n return `${incarnation}.${counter + 1}.${drawWrite(entropy)}`;\n}\n\n/**\n * The token of a new row: a fresh incarnation and write part from `entropy`, and a counter that continues\n * `tombstone`'s when the row is created over one, else starts at 0.\n */\nexport function newIncarnationToken(\n entropy: Entropy,\n tombstone: RegistryRecord | undefined,\n): Token {\n const counter =\n tombstone === undefined ? 0 : tokenParts(tombstone.token, tombstone.segment).counter + 1;\n return `${drawIncarnation(entropy)}.${counter}.${drawWrite(entropy)}`;\n}\n\n/** A stored value as a message shows it: at most 64 characters, so a hostile row cannot fill every log line it reaches. */\nfunction shown(v: unknown): string {\n const text = String(v);\n return text.length > 64 ? `${text.slice(0, 64)}\u2026` : text;\n}\n\n/**\n * Parse + structurally validate a persisted `{ deleted, record }` envelope from stored bytes. A published row\n * is always whole (atomic write), so a parse failure or a missing/mistyped field means corruption/tampering \u2014\n * fail fast (invariant 5), never silently report \"absent\". `ctx` names the source (path/key) for the message.\n * `expected` is the segment the row was read under; a row that names another is refused.\n */\nexport function parseRegistryEnvelope(\n text: string,\n ctx: string,\n expected?: SegmentRef,\n): RegistryEnvelope {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new IntegrityError(`registry row is not valid JSON: ${ctx}`);\n }\n // Guard before any property access: `JSON.parse` yields `null`/primitives for hostile bytes (e.g. `\"null\"`,\n // `\"5\"`), which would throw an *untyped* TypeError on the dereference below (invariant 5 wants a typed error).\n if (parsed === null || typeof parsed !== 'object') {\n throw new IntegrityError(`registry row has a malformed envelope: ${ctx}`);\n }\n const env = parsed as { schemaVersion?: unknown; deleted?: unknown; record?: unknown };\n assertRegistrySchemaVersion(env.schemaVersion, ctx);\n const extra = Object.keys(env).filter((k) => !ENVELOPE_FIELDS.includes(k));\n if (extra.length > 0) {\n throw new IntegrityError(\n `registry row has fields its envelope does not declare (${shown(extra.join(', '))}): ${ctx}`,\n );\n }\n if (typeof env.deleted !== 'boolean' || env.record === null || typeof env.record !== 'object') {\n throw new IntegrityError(`registry row has a malformed envelope: ${ctx}`);\n }\n const r = env.record as Record<string, unknown>;\n assertStoredRecordShape(r, ctx);\n if (typeof r.token !== 'string') {\n throw new IntegrityError(`registry row is missing its token: ${ctx}`);\n }\n checkPointerId(r.pointerId, tokenParts(r.token, ctx), ctx);\n // A row is the row of the name it is stored under. One copied or restored under another name would read as that\n // segment while naming this one, and every sweep that acts on the name it carries (an erasure, a report, a retention\n // pass) would act on the wrong segment, or on this one twice.\n if (\n expected !== undefined &&\n (r.segment !== expected.segment ||\n (r.namespace ?? undefined) !== (expected.namespace ?? undefined))\n ) {\n throw new IntegrityError(\n `registry row names another segment than the one it is stored for: ${ctx}`,\n );\n }\n return { deleted: env.deleted, record: env.record as RegistryRecord };\n}\n\n/**\n * Build a full record from a {@link NewRegistryRecord} plus identity, audit timestamps, and an OCC token. The create\n * sets what the row resolves to, so its `pointerId` is its own token.\n */\nexport function recordFromNew(\n ref: SegmentRef,\n rec: NewRegistryRecord,\n now: number,\n token: Token,\n): RegistryRecord {\n return {\n namespace: ref.namespace,\n segment: ref.segment,\n currentGen: rec.currentGen,\n wrappedDeks: rec.wrappedDeks,\n keyId: rec.keyId,\n status: rec.status ?? 'active',\n retention: rec.retention,\n residency: rec.residency,\n summary: rec.summary,\n knownGens: rec.knownGens,\n // A new row has no holder: a lease is written only against a row that already exists.\n leases: undefined,\n createdAt: now,\n updatedAt: now,\n token,\n pointerId: token,\n };\n}\n\n/**\n * Fail-fast structural check on a record read back from a persistent tier (untrusted bytes, invariant 5):\n * a published row is always whole, so a missing/mistyped required field means corruption/tampering \u2014 reject\n * it rather than silently report \"absent\". The form of `token` and `pointerId`, and how the two relate, is checked by\n * the caller.\n */\nexport function assertStoredRecordShape(r: Record<string, unknown>, ctx: string): void {\n if (\n typeof r.segment !== 'string' ||\n typeof r.pointerId !== 'string' ||\n (r.namespace !== undefined && typeof r.namespace !== 'string') ||\n (r.currentGen !== null && typeof r.currentGen !== 'number') ||\n typeof r.status !== 'string' ||\n typeof r.createdAt !== 'number' ||\n typeof r.updatedAt !== 'number'\n ) {\n throw new IntegrityError(`registry record is missing required fields: ${ctx}`);\n }\n // Enforce the same value invariants the write path checks, so corrupt/tampered bytes are rejected at the\n // read boundary (invariant 5) rather than leaking a bad currentGen/status downstream.\n if (r.currentGen !== null && (!Number.isSafeInteger(r.currentGen) || r.currentGen < 0)) {\n throw new IntegrityError(\n `registry record has an invalid currentGen (${shown(r.currentGen)}): ${ctx}`,\n );\n }\n if (!STATUSES.includes(r.status)) {\n throw new IntegrityError(`registry record has an unknown status (${shown(r.status)}): ${ctx}`);\n }\n const declared: readonly string[] = RECORD_FIELDS;\n const extra = Object.keys(r).filter((k) => !declared.includes(k));\n if (extra.length > 0) {\n throw new IntegrityError(\n `registry record has fields a schema-${REGISTRY_SCHEMA_VERSION} row does not declare (${shown(extra.join(', '))}): ${ctx}`,\n );\n }\n if (r.keyId !== undefined && typeof r.keyId !== 'string') {\n throw new IntegrityError(`registry record has an invalid keyId: ${ctx}`);\n }\n validateWrappedDeks(r.wrappedDeks, 'stored'); // invariant 5: reject a corrupt wrapped-DEK list on read-back\n // The governance blobs' SHAPE is checked on read-back too, because one of them carries semantics:\n // `retention.expiresAt` is read with an `in` test, which throws an untyped `TypeError` on a stored\n // `null`/string/number. That is reachable \u2014 the write boundary only checks\n // JSON-serializability and size, so `\"retention\": null` round-trips through every driver from a hand-edit,\n // another writer, or a restore \u2014 and it would abort a whole fleet retention sweep rather than becoming one\n // ledger entry. Reject it here, typed, at the trust boundary (invariant 5).\n for (const field of ['retention', 'residency'] as const) {\n const meta = r[field];\n if (meta !== undefined && (meta === null || typeof meta !== 'object' || Array.isArray(meta))) {\n throw new IntegrityError(`registry record has a non-object ${field}: ${ctx}`);\n }\n }\n // Shape only. Whether the summary agrees with the row's keys and names its `currentGen` is not checked here: a\n // stored row that disagrees is a summary its reader must not use, not a row to refuse, and refusing it would let\n // one row stop every listing that reaches it.\n validateSummary(r.summary, 'stored', ctx);\n validateKnownGens(r.knownGens, 'stored', ctx);\n validateLeases(r.leases, 'stored', ctx);\n}\n\n/**\n * Apply a patch to an existing record, returning a new one with a fresh `updatedAt` + `token` (identity and\n * `createdAt` are preserved). Optional fields use `'k' in patch` so a patch can *clear* them (set to\n * `undefined`, e.g. dropping `keyId` on crypto-shred); required fields use `??` (they always have a value). A patch\n * that names a resolved field ({@link renewsPointer}) sets `pointerId` to this write's token; any other keeps it.\n *\n * `summary` describes the current generation, so it follows the pointer and the keys: a patch that moves\n * `currentGen`, or changes `wrappedDeks` so the summary's shape no longer agrees, without mentioning `summary` drops\n * the old one; and a summary the patch gives must name the resulting `currentGen` and agree with the resulting keys\n * (`ValidationError`, before anything is written).\n */\nexport function applyRegistryPatch(\n prev: RegistryRecord,\n patch: RegistryPatch,\n now: number,\n token: Token,\n): RegistryRecord {\n // `'currentGen' in patch`, NOT `patch.currentGen ?? prev.currentGen`. `null` is a legal, meaningful value\n // here (no Storage generation yet), and `??` treats it as absent \u2014 so the nullish form would silently ignore a\n // patch that clears the pointer and leave the old generation in place. The same reason `wrappedDeks` and\n // `keyId` below use presence: any field whose null is a *value* cannot be merged with `??`.\n const currentGen = 'currentGen' in patch ? (patch.currentGen ?? null) : prev.currentGen;\n const wrappedDeks = 'wrappedDeks' in patch ? patch.wrappedDeks : prev.wrappedDeks;\n let summary: RegistrySummary | undefined;\n if ('summary' in patch) {\n validateSummaryNames(patch.summary, currentGen);\n validateSummaryKeys(patch.summary, wrappedDeks);\n summary = patch.summary;\n } else {\n // Kept only while it still describes the row: the same generation, and the same encryption.\n const kept = currentGen === prev.currentGen ? prev.summary : undefined;\n summary = kept !== undefined && summaryAgreesWithKeys(kept, wrappedDeks) ? kept : undefined;\n }\n // The ledger ends with the pointer, so it follows the pointer as the summary does: kept while the pointer stays,\n // dropped when it moves without the patch naming the ledger, and checked against the pointer and summary it gets.\n let knownGens: readonly KnownGeneration[] | undefined;\n if ('knownGens' in patch) {\n validateKnownGensFit(patch.knownGens, currentGen, summary);\n knownGens = patch.knownGens;\n } else {\n knownGens = currentGen === prev.currentGen ? prev.knownGens : undefined;\n }\n // Leases name generations a holder pinned, and the pointer moving does not end them: kept unless the patch names the list.\n let leases: readonly LeaseEntry[] | undefined;\n if ('leases' in patch) {\n leases = patch.leases;\n validateLeasesPointer(leases, currentGen);\n // A writer writes at most the cap, except that a list may always shrink: a release over a row that holds more\n // (written by another build) writes that list less one, and so can never be stuck behind the cap.\n const most = Math.max(MAX_LEASES_PER_SEGMENT, prev.leases?.length ?? 0);\n if (leases !== undefined && leases.length > most) {\n throw new ValidationError(`leases: has ${leases.length} entries, cap ${most}`);\n }\n } else {\n leases = prev.leases;\n }\n return {\n namespace: prev.namespace,\n segment: prev.segment,\n currentGen,\n wrappedDeks,\n keyId: 'keyId' in patch ? patch.keyId : prev.keyId,\n status: patch.status ?? prev.status,\n retention: 'retention' in patch ? patch.retention : prev.retention,\n residency: 'residency' in patch ? patch.residency : prev.residency,\n summary,\n knownGens,\n leases,\n createdAt: prev.createdAt,\n updatedAt: now,\n token,\n pointerId: renewsPointer(patch) ? token : prev.pointerId,\n };\n}\n", "/**\n * The default entropy a shipped registry draws its tokens' random parts from: the platform's Web Crypto.\n *\n * Kept apart from the record helpers, which are pure, because this reads an ambient global. It is checked when a\n * row is written, not when a registry is built, so a process that only reads runs on a runtime without it.\n */\nimport { UnsupportedError } from '@/core/errors';\nimport type { Entropy } from '@/core/determinism';\n\ninterface WebCrypto {\n getRandomValues(array: Uint8Array): Uint8Array;\n}\n\n/** The platform's Web Crypto, or `undefined` when this runtime has none. */\nfunction webCrypto(): WebCrypto | undefined {\n const c = (globalThis as { crypto?: { getRandomValues?: unknown } }).crypto;\n return c !== undefined && typeof c.getRandomValues === 'function' ? (c as WebCrypto) : undefined;\n}\n\n/** Draws from the platform's Web Crypto; `UnsupportedError` on a runtime without it. */\nexport const webCryptoEntropy: Entropy = (length) => {\n const c = webCrypto();\n if (c === undefined) {\n throw new UnsupportedError(\n 'this runtime has no Web Crypto (crypto.getRandomValues), which a registry needs for the token of every ' +\n 'row it writes: run on a runtime that has it',\n );\n }\n return c.getRandomValues(new Uint8Array(length));\n};\n\n/** Whether `entropy` can draw here: an injected source always, the default only where Web Crypto exists. */\nexport function entropyIsAvailable(entropy: Entropy): boolean {\n return entropy !== webCryptoEntropy || webCrypto() !== undefined;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAkBO,SAAS,SAAS,MAAc,KAAoB;AACzD,MAAI,QAAQ,UAAa,OAAO,QAAQ,YAAY;AAClD,UAAM,IAAI,gBAAgB,GAAG,IAAI,2DAA2D;AAAA,EAC9F;AACF;AAOO,SAAS,mBAAmB,MAAc,OAAoC;AACnF,SAAO,UAAU,SAAY,SAAY,uBAAuB,OAAO,IAAI;AAC7E;AAEA,IAAM,qBAAwC,CAAC,WAAW;AAC1D,IAAM,oBAAuC,CAAC,QAAQ,SAAS;AAMxD,SAAS,mBAAmB,SAAsC;AACvE,MAAI,YAAY,OAAW,QAAO;AAClC,uBAAqB,kBAAkB,SAAS,kBAAkB;AAClE,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI,cAAc,UAAa,OAAO,cAAc,UAAU;AAC5D,UAAM,IAAI;AAAA,MACR,uDAAuD,MAAM,SAAS,CAAC;AAAA,IACzE;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,0BAA0B,SAAwB;AAChE,MAAI,YAAY,OAAW;AAC3B,uBAAqB,iBAAiB,SAAS,iBAAiB;AAChE,QAAM,EAAE,MAAM,QAAQ,IAAI;AAC1B,MAAI,SAAS,UAAa,SAAS,QAAQ,OAAO,SAAS,UAAU;AACnE,UAAM,IAAI;AAAA,MACR,kEAAkE,MAAM,IAAI,CAAC;AAAA,IAC/E;AAAA,EACF;AACA,MAAI,YAAY,UAAa,OAAO,YAAY,YAAY;AAC1D,UAAM,IAAI;AAAA,MACR,sDAAsD,MAAM,OAAO,CAAC;AAAA,IACtE;AAAA,EACF;AACF;;;AC1DO,IAAM,oBAAoB;AAW1B,SAAS,iBAAiB,WAAuC;AACtE,SAAO,cAAc,SAAY,oBAAoB,iBAAiB,SAAS;AACjF;AAGO,SAAS,kBAAkB,WAAuC;AACvE,SAAO,cAAc,SAAY,oBAAoB,kBAAkB,SAAS;AAClF;;;ACGA,IAAM,WAA8B,CAAC,UAAU,WAAW;AAMnD,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,iBAAoC,CAAC,SAAS,WAAW;AAE/D,IAAM,kBAAqC,CAAC,iBAAiB,WAAW,QAAQ;AAEhF,IAAM,uBAAuB,KAAK;AAElC,IAAM,mBAAmB;AACzB,IAAM,wBAAwB,IAAI;AAKlC,IAAM,2BAA2B,KAAK,IAAI,IAAI,IAAI,IAAI;AAEtD,IAAM,2BAA2B,2BAA2B;AAE5D,IAAM,SAAS;AAGf,SAAS,mBAAmB,KAA0B;AACpD,MAAI,QAAQ,KAAM;AAClB,MAAI,CAAC,OAAO,cAAc,GAAG,KAAK,MAAM,GAAG;AACzC,UAAM,IAAI;AAAA,MACR,2FAA2F,GAAG;AAAA,IAChG;AAAA,EACF;AACF;AAUA,SAAS,wBAAwB,OAA4B;AAC3D,MAAI,EAAE,gBAAgB,OAAQ;AAK9B,MAAI,MAAM,eAAe,QAAW;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,qBAAmB,MAAM,UAAU;AACrC;AAEA,SAAS,eAAe,QAAsB;AAC5C,MAAI,CAAC,SAAS,SAAS,MAAM,GAAG;AAC9B,UAAM,IAAI,gBAAgB,yBAAyB,SAAS,KAAK,GAAG,CAAC,SAAS,MAAM,EAAE;AAAA,EACxF;AACF;AAOA,SAAS,mBAAmB,MAAe,OAAqB;AAC9D,MAAI,SAAS,OAAW;AAMxB,MAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACpE,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,gCAAgC,SAAS,OAAO,SAAS,OAAO,IAAI;AAAA,IAC9E;AAAA,EACF;AACA,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B,QAAQ;AACN,UAAM,IAAI,gBAAgB,GAAG,KAAK,4BAA4B;AAAA,EAChE;AACA,MAAI,KAAK,SAAS,sBAAsB;AACtC,UAAM,IAAI,gBAAgB,GAAG,KAAK,OAAO,KAAK,MAAM,kBAAkB,oBAAoB,GAAG;AAAA,EAC/F;AACF;AAgBA,SAAS,oBAAoB,OAAgB,QAA4B;AACvE,MAAI,UAAU,OAAW;AACzB,QAAM,OAAO,CAAC,QAAuB;AACnC,UAAM,WAAW,WAAW,IAAI,eAAe,GAAG,IAAI,IAAI,gBAAgB,GAAG;AAAA,EAC/E;AACA,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,MAAK,8BAA8B;AAC9D,QAAM,OAAO;AACb,MAAI,KAAK,WAAW,EAAG,MAAK,4CAA4C;AACxE,MAAI,KAAK,SAAS;AAChB,SAAK,mBAAmB,KAAK,MAAM,iBAAiB,gBAAgB,EAAE;AACxE,aAAW,KAAK,MAAM;AACpB,QAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,MAAK,qCAAqC;AACnF,UAAM,EAAE,OAAO,QAAQ,IAAI;AAC3B,QAAI,OAAO,UAAU,YAAY,MAAM,WAAW;AAChD,WAAK,2CAA2C;AAClD,QAAI,OAAO,YAAY,YAAY,QAAQ,WAAW;AACpD,WAAK,kDAAkD;AACzD,QAAK,QAAmB,SAAS;AAC/B,WAAK,wBAAyB,QAAmB,MAAM,UAAU,qBAAqB,GAAG;AAAA,EAC7F;AACF;AAGA,IAAM,kBAAkB;AAMxB,SAAS,qBAAqB,MAAuB;AACnD,QAAM,MAAM,KAAK,SAAS,IAAI,IAAI,IAAI,KAAK,SAAS,GAAG,IAAI,IAAI;AAC/D,MAAI,QAAQ,EAAG,QAAO;AACtB,QAAM,OAAO,gBAAgB,QAAQ,KAAK,KAAK,SAAS,MAAM,CAAC,CAAE;AACjE,SAAO,QAAQ,KAAK,OAAO,QAAY,KAAK,OAAO,OAAU;AAC/D;AAeA,SAAS,gBACP,OACA,QACA,KAC6B;AAC7B,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,OAAO,CAAC,QAAuB;AACnC,UAAM,QAAQ,QAAQ,SAAY,KAAK,KAAK,GAAG;AAC/C,UAAM,WAAW,WACb,IAAI,eAAe,4BAA4B,GAAG,GAAG,KAAK,EAAE,IAC5D,IAAI,gBAAgB,YAAY,GAAG,GAAG,KAAK,EAAE;AAAA,EACnD;AACA,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACvE;AAAA,MACE,0BAA0B,UAAU,OAAO,SAAS,MAAM,QAAQ,KAAK,IAAI,aAAa,OAAO,KAAK;AAAA,IACtG;AAAA,EACF;AACA,QAAM,IAAI;AACV,QAAM,OAAO,OAAO,KAAK,CAAC;AAC1B,QAAM,WAAW,KAAK,SAAS,QAAQ;AACvC,QAAM,UAAU,WACZ,CAAC,cAAc,QAAQ,IACvB,CAAC,cAAc,eAAe,eAAe,aAAa,UAAU;AACxE,QAAM,QAAQ,KAAK,OAAO,CAAC,MAAM,CAAC,QAAQ,SAAS,CAAC,CAAC;AACrD,MAAI,MAAM,SAAS,GAAG;AACpB;AAAA,MACE,kBAAkB,WAAW,WAAW,OAAO,4BAA4B,MAAM,KAAK,IAAI,CAAC;AAAA,IAC7F;AAAA,EACF;AACA,QAAM,aAAa,EAAE;AACrB,MAAI,CAAC,OAAO,cAAc,UAAU,KAAM,aAAwB,GAAG;AACnE,SAAK,uDAAuD,OAAO,UAAU,CAAC,GAAG;AAAA,EACnF;AACA,MAAI,UAAU;AACZ,UAAM,SAAS,EAAE;AACjB,QAAI,OAAO,WAAW,YAAY,CAAC,OAAO,KAAK,MAAM,KAAK,CAAC,qBAAqB,MAAM,GAAG;AACvF,WAAK,kDAAkD;AAAA,IACzD;AACA,UAAM,OAAO;AACb,UAAM,QAAS,KAAK,SAAS,IAAK,KAAK,KAAK,SAAS,IAAI,IAAI,IAAI,KAAK,SAAS,GAAG,IAAI,IAAI;AAC1F,QAAI,QAAQ,4BAA4B,QAAQ,0BAA0B;AACxE;AAAA,QACE,aAAa,KAAK,cAAc,wBAAwB,KAAK,wBAAwB;AAAA,MAEvF;AAAA,IACF;AACA,WAAO,OAAO,OAAO,EAAE,YAAkC,QAAQ,KAAK,CAAC;AAAA,EACzE;AACA,QAAM,IAAI,EAAE;AACZ,MAAI,CAAC,OAAO,UAAU,CAAC,KAAM,IAAe,KAAM,IAAe,yBAAyB;AACxF,SAAK,sDAAsD,OAAO,CAAC,CAAC,GAAG;AAAA,EACzE;AACA,QAAM,cAAc,EAAE;AACtB,QAAM,QAAQ,iBAAiB,WAAW;AAC1C,MAAI,UAAU,QAAW;AACvB;AAAA,MACE,6EAA6EA,OAAM,KAAK,UAAU,WAAW,CAAC,CAAC;AAAA,IACjH;AAAA,EACF;AACA,QAAM,YAAY,EAAE;AACpB,MAAI,CAAC,aAAa,WAAW,MAAO,IAAI,GAAG;AACzC;AAAA,MACE,gJAC0CA,OAAM,KAAK,UAAU,SAAS,CAAC,CAAC,qBAAqB,MAAO,IAAI;AAAA,IAC5G;AAAA,EACF;AACA,MAAI,EAAE,aAAa,QAAW;AAC5B,WAAO,OAAO,OAAO;AAAA,MACnB;AAAA,MACA,aAAa;AAAA,MACb;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACA,QAAM,YAAY,sBAAsB,EAAE,UAAU,IAAI;AACxD,MAAI,cAAc,KAAM,MAAK,+DAA+D;AAE5F,MAAI,WAAW,SAAU,QAAO;AAGhC,QAAM,WAAW,OAAO,OAAO,KAAK,MAAM,SAAS,CAAoC;AACvF,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAGA,SAAS,qBACP,SACA,YACM;AACN,MAAI,YAAY,UAAa,QAAQ,eAAe,YAAY;AAC9D,UAAM,IAAI;AAAA,MACR,gCAAgC,QAAQ,UAAU,2BAA2B,OAAO,UAAU,CAAC;AAAA,IACjG;AAAA,EACF;AACF;AAOA,SAAS,sBACP,SACA,aACS;AACT,SAAO,YAAY,aAAa,gBAAgB;AAClD;AAGA,SAAS,oBACP,SACA,aACM;AACN,MAAI,YAAY,UAAa,CAAC,sBAAsB,SAAS,WAAW,GAAG;AACzE,UAAM,IAAI;AAAA,MACR,gBAAgB,SACZ,uEACA;AAAA,IACN;AAAA,EACF;AACF;AAYA,SAAS,kBACP,OACA,QACA,KACwC;AACxC,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,OAAO,CAAC,QAAuB;AACnC,UAAM,QAAQ,QAAQ,SAAY,KAAK,KAAK,GAAG;AAC/C,UAAM,WAAW,WACb,IAAI,eAAe,8BAA8B,GAAG,GAAG,KAAK,EAAE,IAC9D,IAAI,gBAAgB,cAAc,GAAG,EAAE;AAAA,EAC7C;AACA,QAAM,MAAM,WAAW,WAAW,+BAA+B;AACjE,QAAM,QAAQ,oBAAoB,OAAO,KAAKA,MAAK;AACnD,MAAI,UAAU,OAAW,MAAK,KAAK;AACnC,QAAM,MAAyB,CAAC;AAChC,aAAW,SAAS,OAAqC;AACvD,QAAI;AAAA,MACF,WAAW,WACP,QACA,OAAO,OAAO,EAAE,YAAY,MAAM,YAAY,WAAW,MAAM,UAAU,CAAC;AAAA,IAChF;AAAA,EACF;AACA,SAAO,WAAW,WAAW,MAAM,OAAO,OAAO,GAAG;AACtD;AAEA,IAAM,SAAS;AACf,IAAM,mBAAsC,CAAC,UAAU,cAAc,OAAO;AAW5E,SAAS,eACP,OACA,QACA,KACmC;AACnC,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,OAAO,CAAC,QAAuB;AACnC,UAAM,QAAQ,QAAQ,SAAY,KAAK,KAAK,GAAG;AAC/C,UAAM,WAAW,WACb,IAAI,eAAe,2BAA2B,GAAG,GAAG,KAAK,EAAE,IAC3D,IAAI,gBAAgB,WAAW,GAAG,EAAE;AAAA,EAC1C;AACA,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,MAAK,kBAAkB;AAClD,QAAM,OAAO;AACb,QAAM,MAAM;AACZ,MAAI,KAAK,SAAS,IAAK,MAAK,OAAO,KAAK,MAAM,iBAAiB,GAAG,EAAE;AACpE,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,MAAoB,CAAC;AAC3B,aAAW,KAAK,MAAM;AACpB,QAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,QAAQ,CAAC,EAAG,MAAK,4BAA4B;AAC9F,UAAM,QAAQ;AACd,UAAM,QAAQ,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,SAAS,CAAC,CAAC;AAC5E,QAAI,MAAM,SAAS,EAAG,MAAK,yCAAyC,MAAM,KAAK,IAAI,CAAC,GAAG;AACvF,UAAM,EAAE,QAAQ,YAAY,MAAM,IAAI;AACtC,QAAI,OAAO,WAAW,YAAY,CAAC,OAAO,KAAK,MAAM,GAAG;AACtD,WAAK,0CAA0C;AAAA,IACjD;AACA,QAAI,KAAK,IAAI,MAAgB,EAAG,MAAK,wBAAwB;AAC7D,SAAK,IAAI,MAAgB;AACzB,eAAW,CAAC,MAAM,CAAC,KAAK;AAAA,MACtB,CAAC,cAAc,UAAU;AAAA,MACzB,CAAC,SAAS,KAAK;AAAA,IACjB,GAAY;AACV,UAAI,CAAC,OAAO,cAAc,CAAC,KAAM,IAAe,GAAG;AACjD,aAAK,GAAG,IAAI,6CAA6C,OAAO,CAAC,CAAC,GAAG;AAAA,MACvE;AAAA,IACF;AACA,QAAI;AAAA,MACF,WAAW,WACN,QACD,OAAO,OAAO;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACP;AAAA,EACF;AACA,MAAI,WAAW,SAAU,QAAO;AAChC,SAAO,IAAI,WAAW,IAAI,SAAY,OAAO,OAAO,GAAG;AACzD;AAGA,SAAS,sBACP,QACA,YACM;AACN,MAAI,WAAW,UAAa,OAAO,SAAS,KAAK,eAAe,MAAM;AACpE,UAAM,IAAI,gBAAgB,0DAA0D;AAAA,EACtF;AACF;AAQA,SAAS,qBACP,WACA,YACA,SACM;AACN,MAAI,cAAc,OAAW;AAC7B,MAAI,eAAe,MAAM;AACvB,UAAM,IAAI,gBAAgB,4DAA4D;AAAA,EACxF;AACA,QAAM,OAAO,UAAU,UAAU,SAAS,CAAC;AAC3C,MAAI,SAAS,UAAa,KAAK,eAAe,YAAY;AACxD,UAAM,IAAI;AAAA,MACR,6CAA6C,UAAU,uEAC/B,SAAS,SAAY,YAAY,KAAK,UAAU;AAAA,IAC1E;AAAA,EACF;AACA,MACE,YAAY,UACZ,QAAQ,eAAe,cACvB,iBAAiB,WACjB,iBAAiB,QAAQ,WAAW,GAAG,cAAc,KAAK,WAC1D;AACA,UAAM,IAAI;AAAA,MACR,+BAA+B,KAAK,SAAS,mBAAmB,UAAU,mCACzD,QAAQ,WAAW;AAAA,IACtC;AAAA,EACF;AACF;AAOA,SAAS,oBAAoB,OAAe,MAAoB;AAC9D,QAAM,QAAQ,eAAe,OAAO,CAAC,UAAU,SAAS,KAAK;AAC7D,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI;AAAA,MACR,GAAG,IAAI,UAAU,MAAM,KAAK,OAAO,CAAC;AAAA,IACtC;AAAA,EACF;AACF;AAMA,SAAS,cAAc,OAAsB;AAC3C,MAAI,UAAU,WAAc,OAAO,UAAU,YAAY,MAAM,WAAW,IAAI;AAC5E,UAAM,IAAI,gBAAgB,6CAA6C;AAAA,EACzE;AACF;AAMO,SAAS,0BAA0B,KAA2C;AACnF,sBAAoB,KAAK,cAAc;AACvC,gBAAc,IAAI,KAAK;AACvB,qBAAmB,IAAI,UAAU;AACjC,MAAI,IAAI,WAAW,OAAW,gBAAe,IAAI,MAAM;AACvD,sBAAoB,IAAI,aAAa,QAAQ;AAC7C,qBAAmB,IAAI,WAAW,WAAW;AAC7C,qBAAmB,IAAI,WAAW,WAAW;AAC7C,QAAM,YAAY,kBAAkB,IAAI,WAAW,QAAQ;AAC3D,QAAM,YAAY,cAAc,SAAY,MAAM,EAAE,GAAG,KAAK,UAAU;AACtE,QAAM,UAAU,aAAa,MAAM,gBAAgB,IAAI,SAAS,QAAQ,IAAI;AAC5E,uBAAqB,WAAW,IAAI,YAAY,OAAO;AACvD,MAAI,EAAE,aAAa,KAAM,QAAO;AAChC,uBAAqB,SAAS,IAAI,UAAU;AAC5C,sBAAoB,SAAS,IAAI,WAAW;AAC5C,SAAO,EAAE,GAAG,WAAW,QAAQ;AACjC;AAOO,SAAS,sBAAsB,OAAqC;AACzE,sBAAoB,OAAO,SAAS;AACpC,MAAI,WAAW,MAAO,eAAc,MAAM,KAAK;AAC/C,0BAAwB,KAAK;AAC7B,MAAI,MAAM,WAAW,OAAW,gBAAe,MAAM,MAAM;AAC3D,MAAI,iBAAiB,MAAO,qBAAoB,MAAM,aAAa,QAAQ;AAC3E,MAAI,eAAe,MAAO,oBAAmB,MAAM,WAAW,WAAW;AACzE,MAAI,eAAe,MAAO,oBAAmB,MAAM,WAAW,WAAW;AACzE,QAAM,YACJ,MAAM,cAAc,SAChB,QACA,EAAE,GAAG,OAAO,WAAW,kBAAkB,MAAM,WAAW,QAAQ,EAAE;AAC1E,QAAM,aACJ,YAAY,YACR,EAAE,GAAG,WAAW,QAAQ,eAAe,UAAU,QAAQ,QAAQ,EAAE,IACnE;AACN,MAAI,EAAE,aAAa,OAAQ,QAAO;AAClC,SAAO,EAAE,GAAG,YAAY,SAAS,gBAAgB,MAAM,SAAS,QAAQ,EAAE;AAC5E;AA+BO,IAAM,0BAA0B;AAShC,SAAS,4BAA4B,KAAc,KAAqB;AAC7E,MAAI,QAAQ,QAAW;AACrB,UAAM,IAAI,eAAe,sCAAsC,GAAG,EAAE;AAAA,EACtE;AACA,MAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,GAAG;AAChE,UAAM,IAAI,eAAe,+CAA+CA,OAAM,GAAG,CAAC,MAAM,GAAG,EAAE;AAAA,EAC/F;AACA,MAAI,MAAM,yBAAyB;AACjC,UAAM,IAAI;AAAA,MACR,8BAA8B,GAAG,qDAC5B,uBAAuB,uOAEwF,GAAG;AAAA,IACzH;AAAA,EACF;AACA,MAAI,MAAM,yBAAyB;AACjC,UAAM,IAAI;AAAA,MACR,8BAA8B,GAAG,qCAAqC,uBAAuB,MAAM,GAAG;AAAA,IACxG;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,0BAA0B,KAA+B;AACvE,SAAO,KAAK,UAAU;AAAA,IACpB,eAAe;AAAA,IACf,SAAS,IAAI;AAAA,IACb,QAAQ,IAAI;AAAA,EACd,CAAC;AACH;AAeA,IAAM,oBAAoB;AAE1B,IAAM,cAAc;AAYpB,SAAS,WAAW,OAAgB,KAAa,OAAO,SAAqB;AAC3E,QAAM,QAAQ,OAAO,UAAU,WAAW,kBAAkB,KAAK,KAAK,IAAI;AAC1E,MAAI,UAAU,MAAM;AAClB,UAAM,IAAI;AAAA,MACR,gBAAgB,IAAI,0CAA0CA,OAAM,KAAK,UAAU,KAAK,CAAC,CAAC,MAAM,GAAG;AAAA,IACrG;AAAA,EACF;AACA,QAAM,UAAU,OAAO,MAAM,CAAC,CAAC;AAC/B,MAAI,CAAC,OAAO,cAAc,OAAO,GAAG;AAClC,UAAM,IAAI,eAAe,gBAAgB,IAAI,4CAA4C,GAAG,EAAE;AAAA,EAChG;AACA,SAAO,EAAE,aAAa,MAAM,CAAC,GAAI,QAAQ;AAC3C;AAOA,SAAS,eAAe,WAAoB,OAAmB,KAAmB;AAChF,QAAM,QAAQ,WAAW,WAAW,KAAK,WAAW;AACpD,MAAI,MAAM,gBAAgB,MAAM,aAAa;AAC3C,UAAM,IAAI;AAAA,MACR,oEAAoE,GAAG;AAAA,IACzE;AAAA,EACF;AACA,MAAI,MAAM,UAAU,MAAM,SAAS;AACjC,UAAM,IAAI,eAAe,sDAAsD,GAAG,EAAE;AAAA,EACtF;AACF;AAQA,SAAS,QAAQ,SAAkB,OAAuB;AACxD,QAAM,QAAQ,QAAQ,KAAK;AAC3B,MAAI,EAAE,iBAAiB,eAAe,MAAM,WAAW,OAAO;AAC5D,UAAM,IAAI;AAAA,MACR,6CAA6C,KAAK;AAAA,IACpD;AAAA,EACF;AACA,MAAI,MAAM;AACV,aAAW,KAAK,MAAO,QAAO,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC5D,SAAO;AACT;AAGO,SAAS,gBAAgB,SAA0B;AACxD,SAAO,QAAQ,SAAS,iBAAiB;AAC3C;AAGO,SAAS,UAAU,SAA0B;AAClD,SAAO,QAAQ,SAAS,WAAW;AACrC;AAMO,SAAS,kBAAkB,QAAwB,SAAyB;AACjF,QAAM,EAAE,aAAa,QAAQ,IAAI,WAAW,OAAO,OAAO,OAAO,OAAO;AACxE,SAAO,GAAG,WAAW,IAAI,UAAU,CAAC,IAAI,UAAU,OAAO,CAAC;AAC5D;AAMO,SAAS,oBACd,SACA,WACO;AACP,QAAM,UACJ,cAAc,SAAY,IAAI,WAAW,UAAU,OAAO,UAAU,OAAO,EAAE,UAAU;AACzF,SAAO,GAAG,gBAAgB,OAAO,CAAC,IAAI,OAAO,IAAI,UAAU,OAAO,CAAC;AACrE;AAGA,SAASC,OAAM,GAAoB;AACjC,QAAM,OAAO,OAAO,CAAC;AACrB,SAAO,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC,WAAM;AACtD;AAQO,SAAS,sBACd,MACA,KACA,UACkB;AAClB,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,UAAM,IAAI,eAAe,mCAAmC,GAAG,EAAE;AAAA,EACnE;AAGA,MAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;AACjD,UAAM,IAAI,eAAe,0CAA0C,GAAG,EAAE;AAAA,EAC1E;AACA,QAAM,MAAM;AACZ,8BAA4B,IAAI,eAAe,GAAG;AAClD,QAAM,QAAQ,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,SAAS,CAAC,CAAC;AACzE,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI;AAAA,MACR,0DAA0DA,OAAM,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG;AAAA,IAC5F;AAAA,EACF;AACA,MAAI,OAAO,IAAI,YAAY,aAAa,IAAI,WAAW,QAAQ,OAAO,IAAI,WAAW,UAAU;AAC7F,UAAM,IAAI,eAAe,0CAA0C,GAAG,EAAE;AAAA,EAC1E;AACA,QAAM,IAAI,IAAI;AACd,0BAAwB,GAAG,GAAG;AAC9B,MAAI,OAAO,EAAE,UAAU,UAAU;AAC/B,UAAM,IAAI,eAAe,sCAAsC,GAAG,EAAE;AAAA,EACtE;AACA,iBAAe,EAAE,WAAW,WAAW,EAAE,OAAO,GAAG,GAAG,GAAG;AAIzD,MACE,aAAa,WACZ,EAAE,YAAY,SAAS,YACrB,EAAE,aAAa,aAAgB,SAAS,aAAa,UACxD;AACA,UAAM,IAAI;AAAA,MACR,qEAAqE,GAAG;AAAA,IAC1E;AAAA,EACF;AACA,SAAO,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,OAAyB;AACtE;AAMO,SAAS,cACd,KACA,KACA,KACA,OACgB;AAChB,SAAO;AAAA,IACL,WAAW,IAAI;AAAA,IACf,SAAS,IAAI;AAAA,IACb,YAAY,IAAI;AAAA,IAChB,aAAa,IAAI;AAAA,IACjB,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI,UAAU;AAAA,IACtB,WAAW,IAAI;AAAA,IACf,WAAW,IAAI;AAAA,IACf,SAAS,IAAI;AAAA,IACb,WAAW,IAAI;AAAA;AAAA,IAEf,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW;AAAA,IACX;AAAA,IACA,WAAW;AAAA,EACb;AACF;AAQO,SAAS,wBAAwB,GAA4B,KAAmB;AACrF,MACE,OAAO,EAAE,YAAY,YACrB,OAAO,EAAE,cAAc,YACtB,EAAE,cAAc,UAAa,OAAO,EAAE,cAAc,YACpD,EAAE,eAAe,QAAQ,OAAO,EAAE,eAAe,YAClD,OAAO,EAAE,WAAW,YACpB,OAAO,EAAE,cAAc,YACvB,OAAO,EAAE,cAAc,UACvB;AACA,UAAM,IAAI,eAAe,+CAA+C,GAAG,EAAE;AAAA,EAC/E;AAGA,MAAI,EAAE,eAAe,SAAS,CAAC,OAAO,cAAc,EAAE,UAAU,KAAK,EAAE,aAAa,IAAI;AACtF,UAAM,IAAI;AAAA,MACR,8CAA8CA,OAAM,EAAE,UAAU,CAAC,MAAM,GAAG;AAAA,IAC5E;AAAA,EACF;AACA,MAAI,CAAC,SAAS,SAAS,EAAE,MAAM,GAAG;AAChC,UAAM,IAAI,eAAe,0CAA0CA,OAAM,EAAE,MAAM,CAAC,MAAM,GAAG,EAAE;AAAA,EAC/F;AACA,QAAM,WAA8B;AACpC,QAAM,QAAQ,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,SAAS,CAAC,CAAC;AAChE,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI;AAAA,MACR,uCAAuC,uBAAuB,0BAA0BA,OAAM,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG;AAAA,IAC1H;AAAA,EACF;AACA,MAAI,EAAE,UAAU,UAAa,OAAO,EAAE,UAAU,UAAU;AACxD,UAAM,IAAI,eAAe,yCAAyC,GAAG,EAAE;AAAA,EACzE;AACA,sBAAoB,EAAE,aAAa,QAAQ;AAO3C,aAAW,SAAS,CAAC,aAAa,WAAW,GAAY;AACvD,UAAM,OAAO,EAAE,KAAK;AACpB,QAAI,SAAS,WAAc,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,IAAI;AAC5F,YAAM,IAAI,eAAe,oCAAoC,KAAK,KAAK,GAAG,EAAE;AAAA,IAC9E;AAAA,EACF;AAIA,kBAAgB,EAAE,SAAS,UAAU,GAAG;AACxC,oBAAkB,EAAE,WAAW,UAAU,GAAG;AAC5C,iBAAe,EAAE,QAAQ,UAAU,GAAG;AACxC;AAaO,SAAS,mBACd,MACA,OACA,KACA,OACgB;AAKhB,QAAM,aAAa,gBAAgB,QAAS,MAAM,cAAc,OAAQ,KAAK;AAC7E,QAAM,cAAc,iBAAiB,QAAQ,MAAM,cAAc,KAAK;AACtE,MAAI;AACJ,MAAI,aAAa,OAAO;AACtB,yBAAqB,MAAM,SAAS,UAAU;AAC9C,wBAAoB,MAAM,SAAS,WAAW;AAC9C,cAAU,MAAM;AAAA,EAClB,OAAO;AAEL,UAAM,OAAO,eAAe,KAAK,aAAa,KAAK,UAAU;AAC7D,cAAU,SAAS,UAAa,sBAAsB,MAAM,WAAW,IAAI,OAAO;AAAA,EACpF;AAGA,MAAI;AACJ,MAAI,eAAe,OAAO;AACxB,yBAAqB,MAAM,WAAW,YAAY,OAAO;AACzD,gBAAY,MAAM;AAAA,EACpB,OAAO;AACL,gBAAY,eAAe,KAAK,aAAa,KAAK,YAAY;AAAA,EAChE;AAEA,MAAI;AACJ,MAAI,YAAY,OAAO;AACrB,aAAS,MAAM;AACf,0BAAsB,QAAQ,UAAU;AAGxC,UAAM,OAAO,KAAK,IAAI,wBAAwB,KAAK,QAAQ,UAAU,CAAC;AACtE,QAAI,WAAW,UAAa,OAAO,SAAS,MAAM;AAChD,YAAM,IAAI,gBAAgB,eAAe,OAAO,MAAM,iBAAiB,IAAI,EAAE;AAAA,IAC/E;AAAA,EACF,OAAO;AACL,aAAS,KAAK;AAAA,EAChB;AACA,SAAO;AAAA,IACL,WAAW,KAAK;AAAA,IAChB,SAAS,KAAK;AAAA,IACd;AAAA,IACA;AAAA,IACA,OAAO,WAAW,QAAQ,MAAM,QAAQ,KAAK;AAAA,IAC7C,QAAQ,MAAM,UAAU,KAAK;AAAA,IAC7B,WAAW,eAAe,QAAQ,MAAM,YAAY,KAAK;AAAA,IACzD,WAAW,eAAe,QAAQ,MAAM,YAAY,KAAK;AAAA,IACzD;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,WAAW;AAAA,IACX;AAAA,IACA,WAAW,cAAc,KAAK,IAAI,QAAQ,KAAK;AAAA,EACjD;AACF;;;ACx6BA,SAAS,YAAmC;AAC1C,QAAM,IAAK,WAA0D;AACrE,SAAO,MAAM,UAAa,OAAO,EAAE,oBAAoB,aAAc,IAAkB;AACzF;AAGO,IAAM,mBAA4B,CAAC,WAAW;AACnD,QAAM,IAAI,UAAU;AACpB,MAAI,MAAM,QAAW;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO,EAAE,gBAAgB,IAAI,WAAW,MAAM,CAAC;AACjD;AAGO,SAAS,mBAAmB,SAA2B;AAC5D,SAAO,YAAY,oBAAoB,UAAU,MAAM;AACzD;",
|
|
6
|
+
"names": ["shown", "shown"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requirePositiveInteger
|
|
3
|
+
} from "./chunk-MVUD4R72.js";
|
|
4
|
+
|
|
5
|
+
// src/core/concurrency.ts
|
|
6
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
7
|
+
requirePositiveInteger(limit, "concurrency limit");
|
|
8
|
+
const results = new Array(items.length);
|
|
9
|
+
let next = 0;
|
|
10
|
+
let failed = false;
|
|
11
|
+
let firstError;
|
|
12
|
+
const worker = async () => {
|
|
13
|
+
while (!failed) {
|
|
14
|
+
const i = next++;
|
|
15
|
+
if (i >= items.length) return;
|
|
16
|
+
try {
|
|
17
|
+
results[i] = await fn(items[i], i);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
if (!failed) {
|
|
20
|
+
failed = true;
|
|
21
|
+
firstError = err;
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
|
|
28
|
+
await Promise.all(workers);
|
|
29
|
+
if (failed) throw firstError;
|
|
30
|
+
return results;
|
|
31
|
+
}
|
|
32
|
+
function boundedAll(items, limit, fn) {
|
|
33
|
+
try {
|
|
34
|
+
requirePositiveInteger(limit, "concurrency limit");
|
|
35
|
+
} catch (err) {
|
|
36
|
+
return Promise.reject(err);
|
|
37
|
+
}
|
|
38
|
+
if (items.length > limit) return inWaves(items, limit, fn);
|
|
39
|
+
const calls = [];
|
|
40
|
+
for (const item of items) {
|
|
41
|
+
try {
|
|
42
|
+
calls.push(fn(item));
|
|
43
|
+
} catch (err) {
|
|
44
|
+
void Promise.allSettled(calls);
|
|
45
|
+
return Promise.reject(err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return Promise.all(calls);
|
|
49
|
+
}
|
|
50
|
+
async function inWaves(items, limit, fn) {
|
|
51
|
+
const results = new Array(items.length);
|
|
52
|
+
let next = 0;
|
|
53
|
+
let failed = false;
|
|
54
|
+
const worker = async () => {
|
|
55
|
+
while (!failed && next < items.length) {
|
|
56
|
+
const i = next++;
|
|
57
|
+
try {
|
|
58
|
+
results[i] = await fn(items[i]);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
failed = true;
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
await Promise.all(Array.from({ length: limit }, () => worker()));
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
mapWithConcurrency,
|
|
71
|
+
boundedAll
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=chunk-CGILDXGP.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/concurrency.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Bounded-concurrency fan-out \u2014 the missing primitive for running async work over many items without either\n * a thundering herd (unbounded `Promise.all` \u2192 N simultaneous object-store calls) or full serial latency\n * (`for await`). Pure + I/O-free, so it lives in `core/`; callers inject the async work.\n */\n\nimport { requirePositiveInteger } from './check';\n\n/**\n * Run `fn` over `items` with at most `limit` promises in flight at once, returning results in **input order**\n * (regardless of completion order). If any `fn` rejects, the pool stops scheduling new work, lets the in-flight\n * tasks settle, and rejects with the **first** error \u2014 callers that must not abort on one failure (e.g. an\n * erasure ledger that records per-item faults) should catch inside `fn` so it never rejects.\n *\n * `items` is a materialized array on purpose: every caller here already enumerates its source (registry list,\n * S3 page, chunk map), so an array keeps the pool trivial and race-free (no shared-iterator hazard).\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n requirePositiveInteger(limit, 'concurrency limit');\n const results = new Array<R>(items.length);\n let next = 0;\n let failed = false;\n let firstError: unknown;\n const worker = async (): Promise<void> => {\n while (!failed) {\n const i = next++;\n if (i >= items.length) return;\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n if (!failed) {\n failed = true; // first failure wins; stop scheduling new items (in-flight ones still settle)\n firstError = err;\n }\n return;\n }\n }\n };\n const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());\n await Promise.all(workers);\n if (failed) throw firstError;\n return results;\n}\n\n/**\n * `Promise.all` over `items` with at most `limit` calls of `fn` in flight: the results in input order, or the first\n * failure, at once. Unlike {@link mapWithConcurrency} it does not wait for the calls still in flight before it rejects,\n * as `Promise.all` does not; it starts no call after a failure, and what the calls in flight answer is dropped. For reads\n * a caller abandons on the first fault, never for work that must finish or be accounted for once begun. When every item\n * fits under `limit` it is `Promise.all` itself, so a small fan-out costs and interleaves exactly as `Promise.all` does.\n */\nexport function boundedAll<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n try {\n requirePositiveInteger(limit, 'concurrency limit');\n } catch (err) {\n return Promise.reject(err);\n }\n if (items.length > limit) return inWaves(items, limit, fn);\n const calls: Promise<R>[] = [];\n for (const item of items) {\n try {\n calls.push(fn(item));\n } catch (err) {\n // The calls already made still run; settle them, so one that rejects later is not left unhandled.\n void Promise.allSettled(calls);\n return Promise.reject(err);\n }\n }\n return Promise.all(calls);\n}\n\n/** {@link boundedAll} over more items than `limit`: `limit` workers, each taking the next item as its last settles. */\nasync function inWaves<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n let failed = false;\n const worker = async (): Promise<void> => {\n while (!failed && next < items.length) {\n const i = next++;\n try {\n results[i] = await fn(items[i]!);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n };\n await Promise.all(Array.from({ length: limit }, () => worker()));\n return results;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;AAiBA,eAAsB,mBACpB,OACA,OACA,IACc;AACd,yBAAuB,OAAO,mBAAmB;AACjD,QAAM,UAAU,IAAI,MAAS,MAAM,MAAM;AACzC,MAAI,OAAO;AACX,MAAI,SAAS;AACb,MAAI;AACJ,QAAM,SAAS,YAA2B;AACxC,WAAO,CAAC,QAAQ;AACd,YAAM,IAAI;AACV,UAAI,KAAK,MAAM,OAAQ;AACvB,UAAI;AACF,gBAAQ,CAAC,IAAI,MAAM,GAAG,MAAM,CAAC,GAAI,CAAC;AAAA,MACpC,SAAS,KAAK;AACZ,YAAI,CAAC,QAAQ;AACX,mBAAS;AACT,uBAAa;AAAA,QACf;AACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,OAAO,MAAM,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC;AACpF,QAAM,QAAQ,IAAI,OAAO;AACzB,MAAI,OAAQ,OAAM;AAClB,SAAO;AACT;AASO,SAAS,WACd,OACA,OACA,IACc;AACd,MAAI;AACF,2BAAuB,OAAO,mBAAmB;AAAA,EACnD,SAAS,KAAK;AACZ,WAAO,QAAQ,OAAO,GAAG;AAAA,EAC3B;AACA,MAAI,MAAM,SAAS,MAAO,QAAO,QAAQ,OAAO,OAAO,EAAE;AACzD,QAAM,QAAsB,CAAC;AAC7B,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,YAAM,KAAK,GAAG,IAAI,CAAC;AAAA,IACrB,SAAS,KAAK;AAEZ,WAAK,QAAQ,WAAW,KAAK;AAC7B,aAAO,QAAQ,OAAO,GAAG;AAAA,IAC3B;AAAA,EACF;AACA,SAAO,QAAQ,IAAI,KAAK;AAC1B;AAGA,eAAe,QACb,OACA,OACA,IACc;AACd,QAAM,UAAU,IAAI,MAAS,MAAM,MAAM;AACzC,MAAI,OAAO;AACX,MAAI,SAAS;AACb,QAAM,SAAS,YAA2B;AACxC,WAAO,CAAC,UAAU,OAAO,MAAM,QAAQ;AACrC,YAAM,IAAI;AACV,UAAI;AACF,gBAAQ,CAAC,IAAI,MAAM,GAAG,MAAM,CAAC,CAAE;AAAA,MACjC,SAAS,KAAK;AACZ,iBAAS;AACT,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC;AAC/D,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationError,
|
|
3
|
+
encodeNameForKey
|
|
4
|
+
} from "./chunk-MVUD4R72.js";
|
|
5
|
+
|
|
6
|
+
// src/core/keys.ts
|
|
7
|
+
var FIELD = " ";
|
|
8
|
+
var NO_NAMESPACE = "/";
|
|
9
|
+
function segmentKey(ref) {
|
|
10
|
+
const ns = ref.namespace === void 0 ? NO_NAMESPACE : encodeNameForKey(ref.namespace);
|
|
11
|
+
return `${ns}${FIELD}${encodeNameForKey(ref.segment)}`;
|
|
12
|
+
}
|
|
13
|
+
function segmentPrefix(ref) {
|
|
14
|
+
return `${segmentKey(ref)}${FIELD}`;
|
|
15
|
+
}
|
|
16
|
+
var KEPT_SEGMENT_KEYS = 1024;
|
|
17
|
+
var KeptSegmentKeys = class {
|
|
18
|
+
constructor(encode, max = KEPT_SEGMENT_KEYS) {
|
|
19
|
+
this.encode = encode;
|
|
20
|
+
this.max = max;
|
|
21
|
+
}
|
|
22
|
+
encode;
|
|
23
|
+
max;
|
|
24
|
+
byNamespace = /* @__PURE__ */ new Map();
|
|
25
|
+
kept = 0;
|
|
26
|
+
/** The segment's encoded key. */
|
|
27
|
+
of(ref) {
|
|
28
|
+
let names = this.byNamespace.get(ref.namespace);
|
|
29
|
+
const found = names?.get(ref.segment);
|
|
30
|
+
if (found !== void 0) return found;
|
|
31
|
+
const encoded = this.encode(ref);
|
|
32
|
+
if (this.kept >= this.max) {
|
|
33
|
+
this.byNamespace.clear();
|
|
34
|
+
this.kept = 0;
|
|
35
|
+
names = void 0;
|
|
36
|
+
}
|
|
37
|
+
if (names === void 0) {
|
|
38
|
+
names = /* @__PURE__ */ new Map();
|
|
39
|
+
this.byNamespace.set(ref.namespace, names);
|
|
40
|
+
}
|
|
41
|
+
names.set(ref.segment, encoded);
|
|
42
|
+
this.kept += 1;
|
|
43
|
+
return encoded;
|
|
44
|
+
}
|
|
45
|
+
/** How many encoded keys are kept. */
|
|
46
|
+
get size() {
|
|
47
|
+
return this.kept;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function chunkKeyUnder(prefix, chunkKey, version) {
|
|
51
|
+
return version === void 0 ? `${prefix}${chunkKey}` : `${prefix}${chunkKey}${FIELD}${version}`;
|
|
52
|
+
}
|
|
53
|
+
function shardOf(key, totalShards) {
|
|
54
|
+
let h = 2166136261;
|
|
55
|
+
for (let i = 0; i < key.length; i++) {
|
|
56
|
+
h ^= key.charCodeAt(i);
|
|
57
|
+
h = Math.imul(h, 16777619);
|
|
58
|
+
}
|
|
59
|
+
return (h >>> 0) % totalShards;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/core/blob.ts
|
|
63
|
+
function checkTailLength(maxBytes) {
|
|
64
|
+
if (!Number.isSafeInteger(maxBytes)) {
|
|
65
|
+
throw new ValidationError(`invalid tail length ${String(maxBytes)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
var BufferSink = class {
|
|
69
|
+
chunks = [];
|
|
70
|
+
length = 0;
|
|
71
|
+
write(bytes) {
|
|
72
|
+
this.chunks.push(new Uint8Array(bytes));
|
|
73
|
+
this.length += bytes.length;
|
|
74
|
+
return Promise.resolve();
|
|
75
|
+
}
|
|
76
|
+
bytes() {
|
|
77
|
+
const out = new Uint8Array(this.length);
|
|
78
|
+
let offset = 0;
|
|
79
|
+
for (const chunk of this.chunks) {
|
|
80
|
+
out.set(chunk, offset);
|
|
81
|
+
offset += chunk.length;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var BufferReader = class {
|
|
87
|
+
constructor(buffer) {
|
|
88
|
+
this.buffer = buffer;
|
|
89
|
+
}
|
|
90
|
+
buffer;
|
|
91
|
+
getRange(offset, length) {
|
|
92
|
+
if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0 || offset + length > this.buffer.length) {
|
|
93
|
+
throw new ValidationError(
|
|
94
|
+
`range [${offset}, ${offset + length}) out of bounds for ${this.buffer.length}-byte blob`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return Promise.resolve(this.buffer.subarray(offset, offset + length));
|
|
98
|
+
}
|
|
99
|
+
getTail(maxBytes) {
|
|
100
|
+
try {
|
|
101
|
+
checkTailLength(maxBytes);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
return Promise.reject(err);
|
|
104
|
+
}
|
|
105
|
+
const size = this.buffer.length;
|
|
106
|
+
const take = Math.min(Math.max(maxBytes, 0), size);
|
|
107
|
+
return Promise.resolve({ bytes: this.buffer.subarray(size - take), size });
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
segmentKey,
|
|
113
|
+
segmentPrefix,
|
|
114
|
+
KeptSegmentKeys,
|
|
115
|
+
chunkKeyUnder,
|
|
116
|
+
shardOf,
|
|
117
|
+
checkTailLength,
|
|
118
|
+
BufferSink,
|
|
119
|
+
BufferReader
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=chunk-GKHERVVQ.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/keys.ts", "../src/core/blob.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Canonical key encoding for a segment / chunk \u2014 one source of truth, used by both the storage\n * drivers and the cache (DRY). The encoding is collision-proof and injection-proof.\n *\n * **The name parts are encoded, and that is what makes the delimiters safe.** A name is any non-empty string,\n * so it may contain a space or a `/`, and with the parts left literal two distinct segments could produce one\n * key:\n *\n * ```\n * segmentKey({ segment: 's' }) \u2192 \"/ s\"\n * segmentKey({ namespace: '/', segment: 's' }) \u2192 \"/ s\" \u2190 the same key\n * segmentKey({ namespace: 'a', segment: 'b c' }) \u2192 \"a b c\"\n * segmentKey({ namespace: 'a b', segment: 'c' }) \u2192 \"a b c\" \u2190 the same key\n * ```\n *\n * These keys reach the reader LRU and the decoded-chunk cache, so a collision is one segment serving\n * another's data on the read path, on every backend \u2014 invariant 3, and a tenant-isolation break.\n *\n * `encodeNameForKey` escapes both delimiters (`/` \u2192 `%2F`, space \u2192 `%20`) along with control characters, so\n * neither can be injected across a field \u2014 a property of the encoding, not of what a caller chooses to name\n * things. The absent-namespace sentinel keeps the same ours-vs-theirs asymmetry the\n * storage keys use: it is emitted literally while a caller's namespace is encoded, so a namespace actually\n * named `/` encodes to `%2F` and cannot impersonate it.\n *\n * These keys are in-memory and driver-internal; nothing on disk is named by them.\n */\nimport { encodeNameForKey } from './name-codec';\nimport type { ChunkRef, SegmentRef } from './ports';\n\nconst FIELD = ' ';\nconst NO_NAMESPACE = '/';\n\nexport function segmentKey(ref: SegmentRef): string {\n const ns = ref.namespace === undefined ? NO_NAMESPACE : encodeNameForKey(ref.namespace);\n return `${ns}${FIELD}${encodeNameForKey(ref.segment)}`;\n}\n\nexport function segmentPrefix(ref: SegmentRef): string {\n return `${segmentKey(ref)}${FIELD}`;\n}\n\n/**\n * How many segments' encoded keys a {@link KeptSegmentKeys} holds before it empties: the reader cache's default\n * segment count, a working set a long-lived reader keeps open. With 30-character names that is about 150 KB.\n */\nexport const KEPT_SEGMENT_KEYS = 1024;\n\n/**\n * Segments' encoded keys ({@link segmentKey}, or {@link segmentPrefix}: whichever `encode` gives), kept so a read that\n * looks up chunk after chunk compares names rather than encoding them again, which costs about a third of a warm\n * `has()`. Held by namespace and then by segment, so segments of one name in several namespaces sit side by side, and\n * no number of segments read in turn makes every lookup encode again. Bounded: at `max` entries it empties and fills\n * again. An encoding is the names' and nothing else, so a kept one is the one an encoding would give now.\n */\nexport class KeptSegmentKeys {\n private readonly byNamespace = new Map<string | undefined, Map<string, string>>();\n private kept = 0;\n\n constructor(\n private readonly encode: (ref: SegmentRef) => string,\n private readonly max: number = KEPT_SEGMENT_KEYS,\n ) {}\n\n /** The segment's encoded key. */\n of(ref: SegmentRef): string {\n let names = this.byNamespace.get(ref.namespace);\n const found = names?.get(ref.segment);\n if (found !== undefined) return found;\n const encoded = this.encode(ref);\n if (this.kept >= this.max) {\n this.byNamespace.clear();\n this.kept = 0;\n names = undefined;\n }\n if (names === undefined) {\n names = new Map();\n this.byNamespace.set(ref.namespace, names);\n }\n names.set(ref.segment, encoded);\n this.kept += 1;\n return encoded;\n }\n\n /** How many encoded keys are kept. */\n get size(): number {\n return this.kept;\n }\n}\n\nexport function chunkRefKey(ref: ChunkRef): string {\n return chunkKeyUnder(segmentPrefix(ref), ref.chunkKey);\n}\n\n/**\n * {@link chunkRefKey}, or {@link chunkGenKey} with a `version`, from the segment's {@link segmentPrefix} already\n * encoded: for a caller that looks up many chunks of a few segments and keeps their prefixes.\n */\nexport function chunkKeyUnder(prefix: string, chunkKey: number, version?: string | number): string {\n return version === undefined ? `${prefix}${chunkKey}` : `${prefix}${chunkKey}${FIELD}${version}`;\n}\n\n/**\n * Cache key for a chunk scoped to a specific **version** of the segment \u2014 {@link chunkRefKey} plus that\n * version (space-delimited, injection-proof exactly as above \u2014 the name parts are already encoded). A publish bumps the version, so it naturally\n * misses the cache instead of serving a stale superseded chunk, and the superseded entries age out under the\n * LRU ceiling (no active purge needed).\n *\n * `version` is whatever the Storage source reports as identifying the bytes a read will see \u2014 its\n * `currentVersion` where it has one, otherwise the bare generation number. **A generation number alone is not\n * an identity**: a load's generation number restarts at 0 once a registry row is purged and the bucket emptied, so a\n * retired-and-re-created name serves different data at the same `currentGen`, and a key built on the number\n * hands the new incarnation the old one's decoded chunks.\n */\nexport function chunkGenKey(ref: ChunkRef, version: string | number): string {\n return chunkKeyUnder(segmentPrefix(ref), ref.chunkKey, version);\n}\n\n/**\n * Deterministic FNV-1a shard assignment for a segment key \u2014 dependency-free, stable across workers and\n * restarts. One definition, so every sharded fleet operation (today the retention sweep) agrees on it exactly:\n * two definitions that disagreed would make the union across workers neither disjoint nor complete.\n */\nexport function shardOf(key: string, totalShards: number): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i);\n h = Math.imul(h, 0x0100_0193);\n }\n return (h >>> 0) % totalShards;\n}\n", "/**\n * The byte-IO seam the `.crbm` codec reads/writes through.\n *\n * Deliberately tiny and driver-independent: a `BlobSink` is a streaming append target (the writer needs\n * no random access), a `BlobReader` supports the speculative tail read + bounded range reads. An\n * `IStorageDriver` adapts a `GenKey` to these; the in-memory impls below let the format be\n * golden-tested and let the in-memory storage tier hold a real `.crbm` with no filesystem.\n */\nimport { ValidationError } from './errors';\n\n/**\n * Streaming append target for a single object being written. A caller may reuse the buffer it passed to `write` once\n * the write resolves, so a sink that holds bytes past that copies them.\n */\nexport interface BlobSink {\n /**\n * Appends `bytes` to the object. Once it resolves the caller may reuse the buffer, so a sink that keeps the bytes\n * copies them.\n */\n write(bytes: Uint8Array): Promise<void>;\n}\n\n/**\n * Random/tail read access to a single finished object. The total size comes back from `getTail` (the\n * codec's entry point), so there's no separate `size()` \u2014 keeping the seam minimal for driver authors.\n */\nexport interface BlobReader {\n /** Exactly `length` bytes starting at `offset`. Out-of-range is a `ValidationError`, never a short read. */\n getRange(offset: number, length: number): Promise<Uint8Array>;\n /**\n * The last `min(maxBytes, size)` bytes plus the total size \u2014 the one-GET footer+index path. `maxBytes` is a safe\n * integer, zero or less for an empty tail; any other length is refused with `ValidationError`.\n */\n getTail(maxBytes: number): Promise<{ bytes: Uint8Array; size: number }>;\n}\n\n/**\n * Check the length a tail read asks for, the one rule every tail read holds to (each storage driver's `getTail` and\n * {@link BufferReader.getTail}): a safe integer. Zero or less answers an empty tail with the size, and a length past\n * the object's size answers the whole object. Anything else is refused with `ValidationError` before anything is read:\n * `NaN`, a fraction (a negative one too), an infinity of either sign, and a whole number past the safe range (2^53 or\n * more, or -2^53 or less), so no reader takes one for some other length.\n */\nexport function checkTailLength(maxBytes: number): void {\n if (!Number.isSafeInteger(maxBytes)) {\n throw new ValidationError(`invalid tail length ${String(maxBytes)}`);\n }\n}\n\n/** In-memory `BlobSink` that accumulates written bytes; `bytes()` returns the concatenation. */\nexport class BufferSink implements BlobSink {\n private readonly chunks: Uint8Array[] = [];\n private length = 0;\n\n write(bytes: Uint8Array): Promise<void> {\n // Copy into a plain Uint8Array: callers may reuse their buffer after write() resolves, and on a Node Buffer\n // `slice()` is a view of it, not a copy.\n this.chunks.push(new Uint8Array(bytes));\n this.length += bytes.length;\n return Promise.resolve();\n }\n\n bytes(): Uint8Array {\n const out = new Uint8Array(this.length);\n let offset = 0;\n for (const chunk of this.chunks) {\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n }\n}\n\n/** In-memory `BlobReader` over a fixed byte buffer. */\nexport class BufferReader implements BlobReader {\n constructor(private readonly buffer: Uint8Array) {}\n\n getRange(offset: number, length: number): Promise<Uint8Array> {\n if (\n !Number.isInteger(offset) ||\n !Number.isInteger(length) ||\n offset < 0 ||\n length < 0 ||\n offset + length > this.buffer.length\n ) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds for ${this.buffer.length}-byte blob`,\n );\n }\n return Promise.resolve(this.buffer.subarray(offset, offset + length));\n }\n\n getTail(maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n try {\n checkTailLength(maxBytes);\n } catch (err) {\n return Promise.reject(err);\n }\n const size = this.buffer.length;\n const take = Math.min(Math.max(maxBytes, 0), size);\n return Promise.resolve({ bytes: this.buffer.subarray(size - take), size });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|