@smonn/ids 0.12.0 → 0.12.2

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/dist/cli.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as isIdsError } from "./error-Cp5qYZcv.mjs";
3
- import { t as createTimestampId } from "./timestamp-DBwVjDkg.mjs";
4
- import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-ayT0KdCt.mjs";
5
- import { t as createReverseTimestampId } from "./reverse-BRZRc1_U.mjs";
6
- import { i as importSigningKey, n as decodeSigningKey, r as encodeSigningKey, t as createSignedTimestampId } from "./signed-C8OMt3TJ.mjs";
7
- import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-CDTiPwNM.mjs";
3
+ import { t as createTimestampId } from "./timestamp-Cg9nRfnK.mjs";
4
+ import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-BQVNoIIh.mjs";
5
+ import { t as createReverseTimestampId } from "./reverse-DsPd7Lco.mjs";
6
+ import { i as importSigningKey, n as decodeSigningKey, r as encodeSigningKey, t as createSignedTimestampId } from "./signed-4h2BnlWx.mjs";
7
+ import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-BQ-lNECo.mjs";
8
8
  import { i as importDigestKey, n as decodeDigestKey, r as encodeDigestKey, t as createDigestId } from "./digest-CknNw2wa.mjs";
9
9
  //#region src/cli/key-io.ts
10
10
  function isKeyFormatError(result) {
@@ -220,10 +220,10 @@ function formatRelative(thenMs, nowMs) {
220
220
  return head === "" ? "just now" : `${head} ${suffix}`;
221
221
  }
222
222
  function headUnits(abs) {
223
- if (abs < msPerMinute) return "";
224
- if (abs < msPerHour) return unit(Math.round(abs / msPerMinute), "minute");
225
- if (abs < msPerDay) return unit(Math.round(abs / msPerHour), "hour");
226
- if (abs < msPerDay * daysPerMonth) return unit(Math.round(abs / msPerDay), "day");
223
+ if (abs < 6e4) return "";
224
+ if (abs < 36e5) return unit(Math.round(abs / msPerMinute), "minute");
225
+ if (abs < 864e5) return unit(Math.round(abs / msPerHour), "hour");
226
+ if (abs < 864e5 * daysPerMonth) return unit(Math.round(abs / msPerDay), "day");
227
227
  const totalMonths = Math.round(abs / (msPerDay * daysPerMonth));
228
228
  if (totalMonths < monthsPerYear) return unit(totalMonths, "month");
229
229
  const years = Math.floor(totalMonths / monthsPerYear);
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli/key-io.ts","../src/cli/codec-options.ts","../src/cli/constants.ts","../src/cli/flags.ts","../src/cli/format.ts","../src/cli/variants.ts","../src/cli/dispatch.ts","../src/cli/commands/generate.ts","../src/cli/usage.ts","../src/cli/commands/inspect.ts","../src/cli/commands/keygen.ts","../src/cli/index.ts","../bin/cli.ts"],"sourcesContent":["import type { RunOpts } from \"./types.js\";\n\nexport type KeyFormat = \"hex\" | \"base64url\";\n\nexport type KeyFacet<K> = {\n envVar: string;\n formatEnvVar: string;\n // Not yet consumed by any helper here; the keygen-delegation chunk wires it.\n encode: (bytes: Uint8Array, format: KeyFormat) => string;\n decode: (raw: string, format: KeyFormat) => Uint8Array;\n import: (bytes: Uint8Array) => K | Promise<K>;\n};\n\nexport function isKeyFormatError(result: KeyFormat | string): result is string {\n return result !== \"hex\" && result !== \"base64url\";\n}\n\nfunction parseKeyFormatFlag(values: Map<string, string>): KeyFormat | string | undefined {\n const fromFlag = values.get(\"--key-format\");\n if (fromFlag === undefined) return undefined;\n if (fromFlag === \"\") return \"--key-format requires a value\";\n if (fromFlag === \"hex\" || fromFlag === \"base64url\") return fromFlag;\n return `--key-format must be hex or base64url, got '${fromFlag}'`;\n}\n\nexport function parseKeyFormatFromFlag(values: Map<string, string>): KeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag === undefined) return \"hex\";\n return fromFlag;\n}\n\nexport function parseKeyFormat(\n values: Map<string, string>,\n opts: RunOpts,\n facet: Pick<KeyFacet<unknown>, \"formatEnvVar\">,\n): KeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag !== undefined) return fromFlag;\n const env = opts.env ?? process.env;\n const fromEnv = env[facet.formatEnvVar];\n if (fromEnv === undefined || fromEnv === \"\") return \"hex\";\n if (fromEnv === \"hex\" || fromEnv === \"base64url\") return fromEnv;\n return `${facet.formatEnvVar} must be hex or base64url, got '${fromEnv}'`;\n}\n\nexport async function loadKey<K>(\n opts: RunOpts,\n format: KeyFormat,\n facet: Pick<KeyFacet<K>, \"envVar\" | \"decode\" | \"import\">,\n): Promise<K | string> {\n const env = opts.env ?? process.env;\n const raw = env[facet.envVar];\n if (raw === undefined || raw === \"\") return `missing ${facet.envVar} environment variable`;\n try {\n return await facet.import(facet.decode(raw, format));\n } catch (err) {\n return (err as Error).message;\n }\n}\n","import type { TimestampOptions } from \"../codecs/timestamp/index.js\";\nimport type { RunOpts } from \"./types.js\";\n\nexport function codecOpts(opts: RunOpts): Partial<TimestampOptions> {\n // CLI invocations are intentionally ephemeral: one codec per run, never\n // retained, so this is not the duplicate-brand warning case.\n const o: Partial<TimestampOptions> = { allowDuplicateBrand: true };\n if (opts.now !== undefined) o.now = opts.now;\n if (opts.rng !== undefined) o.rng = opts.rng;\n return o;\n}\n","export const maxGenerateCount = 10_000;\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport type ParsedFlags = {\n flags: Set<string>;\n values: Map<string, string>;\n positionals: string[];\n errors: string[];\n};\n\nfunction splitFlagToken(arg: string): { flag: string; inlineValue: string | undefined } {\n const eq = arg.indexOf(\"=\");\n if (eq <= 0) return { flag: arg, inlineValue: undefined };\n return { flag: arg.slice(0, eq), inlineValue: arg.slice(eq + 1) };\n}\n\nexport function splitFlags(args: ReadonlyArray<string>, valueFlags: Set<string>): ParsedFlags {\n const flags = new Set<string>();\n const values = new Map<string, string>();\n const positionals: string[] = [];\n const errors: string[] = [];\n const seenFlags = new Set<string>();\n const addFlag = (flag: string) => {\n const canonical = canonicalFlag(flag);\n if (seenFlags.has(canonical)) errors.push(`duplicate flag: ${canonical}`);\n seenFlags.add(canonical);\n flags.add(flag);\n };\n for (let i = 0; i < args.length; i++) {\n const raw = args[i]!;\n const { flag, inlineValue } = splitFlagToken(raw);\n if (valueFlags.has(flag)) {\n if (inlineValue !== undefined) {\n addFlag(flag);\n values.set(flag, inlineValue);\n continue;\n }\n const value = args[i + 1];\n if (value === undefined || value.startsWith(\"-\")) {\n addFlag(flag);\n values.set(flag, \"\");\n continue;\n }\n addFlag(flag);\n values.set(flag, value);\n i++;\n continue;\n }\n if (flag.startsWith(\"-\")) {\n addFlag(flag);\n if (inlineValue !== undefined) errors.push(`flag does not take a value: ${flag}`);\n continue;\n }\n positionals.push(raw);\n }\n return { flags, values, positionals, errors };\n}\n\nfunction canonicalFlag(flag: string): string {\n if (flag === \"-c\") return \"--count\";\n return flag;\n}\n\nconst knownFlags = new Set([\n \"--opaque\",\n \"--wrapped\",\n \"--reverse\",\n \"--signed\",\n \"--digest\",\n \"--ns\",\n \"--kind\",\n \"--key-format\",\n \"--count\",\n \"-c\",\n \"--bits\",\n]);\n\nexport function unsupportedFlagForCommand(\n command: string,\n flags: Set<string>,\n allowed: Set<string>,\n): string | undefined {\n for (const flag of flags) {\n if (!allowed.has(flag)) {\n return knownFlags.has(flag)\n ? `unsupported flag for ${command}: ${flag}`\n : `unsupported flag: ${flag}`;\n }\n }\n return undefined;\n}\n\nexport function parseCount(values: Map<string, string>): number | string {\n const raw = values.get(\"--count\") ?? values.get(\"-c\");\n if (raw === undefined) return 1;\n if (raw === \"\") return \"--count requires a value\";\n if (!/^[1-9][0-9]*$/.test(raw)) return `--count must be a positive integer, got '${raw}'`;\n const count = Number.parseInt(raw, 10);\n if (!Number.isSafeInteger(count) || count > maxGenerateCount) {\n return `--count must be at most ${maxGenerateCount}, got '${raw}'`;\n }\n return count;\n}\n\nexport function parseBits(values: Map<string, string>): number | string {\n const raw = values.get(\"--bits\");\n if (raw === undefined) return 256;\n if (raw === \"\") return \"--bits requires a value\";\n if (raw === \"128\") return 128;\n if (raw === \"192\") return 192;\n if (raw === \"256\") return 256;\n return `--bits must be 128, 192, or 256, got '${raw}'`;\n}\n\nexport type WrappedKindValue = \"u32\" | \"i32\" | \"u64\" | \"i64\";\n\nexport function parseKind(values: Map<string, string>): WrappedKindValue | string | undefined {\n const raw = values.get(\"--kind\");\n if (raw === undefined) return undefined;\n if (raw === \"\") return \"--kind requires a value\";\n if (raw === \"u32\" || raw === \"i32\" || raw === \"u64\" || raw === \"i64\") return raw;\n return `--kind must be u32, i32, u64, or i64, got '${raw}'`;\n}\n\nexport function isKindError(result: WrappedKindValue | string): result is string {\n return result !== \"u32\" && result !== \"i32\" && result !== \"u64\" && result !== \"i64\";\n}\n\nexport function parseNs(values: Map<string, string>): string | undefined {\n const raw = values.get(\"--ns\");\n if (raw === undefined) return undefined;\n if (raw === \"\") return \"--ns requires a value\";\n return raw;\n}\n\nexport function isNsError(result: string): boolean {\n return result === \"--ns requires a value\";\n}\n","import { isIdsError } from \"../error.js\";\nimport type { Id } from \"../types.js\";\n\ntype InspectOutput = {\n brand: string;\n timestamp: Date;\n canonical: Id<string>;\n input: string;\n nowMs: number;\n};\n\ntype SignedInspectOutput = InspectOutput & {\n verification: \"ok\" | \"failed\" | \"unavailable\";\n};\n\ntype WrappedInspectOutput = {\n brand: string;\n lookupKey: number | bigint;\n canonical: Id<string>;\n input: string;\n};\n\nexport function formatCliError(err: unknown): string {\n return isIdsError(err)\n ? `${err.code}: ${err.message}`\n : err instanceof Error\n ? err.message\n : String(err);\n}\n\nexport function formatWrappedInspectOutput(result: WrappedInspectOutput): string {\n const inputLine = describeInputForm(result.input, result.canonical);\n return [\n `brand: ${result.brand}`,\n `lookup-key: ${result.lookupKey.toString()}`,\n `canonical: ${result.canonical}`,\n `input: ${inputLine}`,\n \"\",\n ].join(\"\\n\");\n}\n\nexport function formatSignedInspectOutput(result: SignedInspectOutput): string {\n const relative = formatRelative(result.timestamp.getTime(), result.nowMs);\n const inputLine = describeInputForm(result.input, result.canonical);\n const lines = [\n `brand: ${result.brand}`,\n `timestamp: ${result.timestamp.toISOString()} (${relative})`,\n ];\n // \"verification:\" is the spec-mandated key name; the extra chars vs. other labels are intentional.\n lines.push(`verification: ${result.verification}`);\n lines.push(`canonical: ${result.canonical}`, `input: ${inputLine}`, \"\");\n return lines.join(\"\\n\");\n}\n\nexport function formatInspectOutput(result: InspectOutput): string {\n const relative = formatRelative(result.timestamp.getTime(), result.nowMs);\n const inputLine = describeInputForm(result.input, result.canonical);\n return [\n `brand: ${result.brand}`,\n `timestamp: ${result.timestamp.toISOString()} (${relative})`,\n `canonical: ${result.canonical}`,\n `input: ${inputLine}`,\n \"\",\n ].join(\"\\n\");\n}\n\nfunction describeInputForm(input: string, canonical: Id<string>): string {\n if (input === canonical) return \"canonical\";\n const notes: string[] = [];\n if (input !== input.toLowerCase()) notes.push(\"was uppercase\");\n if (/[ilo]/i.test(input.slice(4))) notes.push(\"used Crockford aliases\");\n return `not canonical (${notes.join(\" + \")})`;\n}\n\nconst msPerSecond = 1000;\nconst msPerMinute = 60 * msPerSecond;\nconst msPerHour = 60 * msPerMinute;\nconst msPerDay = 24 * msPerHour;\nconst daysPerMonth = 30.44;\nconst monthsPerYear = 12;\n\nfunction formatRelative(thenMs: number, nowMs: number): string {\n const diff = nowMs - thenMs;\n const abs = Math.abs(diff);\n const suffix = diff < 0 ? \"from now\" : \"ago\";\n\n const head = headUnits(abs);\n return head === \"\" ? \"just now\" : `${head} ${suffix}`;\n}\n\nfunction headUnits(abs: number): string {\n if (abs < msPerMinute) return \"\";\n if (abs < msPerHour) return unit(Math.round(abs / msPerMinute), \"minute\");\n if (abs < msPerDay) return unit(Math.round(abs / msPerHour), \"hour\");\n if (abs < msPerDay * daysPerMonth) return unit(Math.round(abs / msPerDay), \"day\");\n\n const totalMonths = Math.round(abs / (msPerDay * daysPerMonth));\n if (totalMonths < monthsPerYear) return unit(totalMonths, \"month\");\n\n const years = Math.floor(totalMonths / monthsPerYear);\n const months = totalMonths % monthsPerYear;\n return months === 0 ? unit(years, \"year\") : `${unit(years, \"year\")} ${unit(months, \"month\")}`;\n}\n\nfunction unit(n: number, name: string): string {\n return `${n} ${n === 1 ? name : `${name}s`}`;\n}\n","import {\n createDigestId,\n decodeDigestKey,\n encodeDigestKey,\n importDigestKey,\n type DigestKey,\n} from \"../codecs/digest/index.js\";\nimport {\n createOpaqueTimestampId,\n decodeOpaqueKey,\n encodeOpaqueKey,\n importOpaqueKey,\n type OpaqueKey,\n} from \"../codecs/opaque/index.js\";\nimport { createReverseTimestampId } from \"../codecs/reverse/index.js\";\nimport {\n createSignedTimestampId,\n decodeSigningKey,\n encodeSigningKey,\n importSigningKey,\n type SigningKey,\n} from \"../codecs/signed/index.js\";\nimport { createTimestampId } from \"../codecs/timestamp/index.js\";\nimport {\n createWrappedKeyId,\n decodeWrappingKey,\n encodeWrappingKey,\n importWrappingKey,\n type WrappingKey,\n} from \"../codecs/wrapped/index.js\";\nimport type { IdCodec } from \"../adapters/adapter-types.js\";\nimport { codecOpts } from \"./codec-options.js\";\nimport { isKindError, isNsError, parseKind, parseNs } from \"./flags.js\";\nimport { formatCliError } from \"./format.js\";\nimport type { KeyFacet } from \"./key-io.js\";\nimport type { RunOpts } from \"./types.js\";\n\ntype InspectMode = \"readable\" | \"keyed-readable\" | \"unwrap\" | \"verify\" | \"unsupported\";\n\nexport type Descriptor = {\n flag?: string;\n key?: KeyFacet<unknown>;\n construct: (\n brand: string,\n opts: RunOpts,\n key?: unknown,\n values?: Map<string, string>,\n ) => (IdCodec<string> & { generate?(): string | Promise<string> }) | string;\n inspectMode: InspectMode;\n extraFlags?: readonly string[];\n};\n\nexport type GeneratorDescriptor = {\n flag?: string;\n key?: KeyFacet<unknown>;\n construct: (\n brand: string,\n opts: RunOpts,\n key?: unknown,\n values?: Map<string, string>,\n ) => (IdCodec<string> & { generate(): string | Promise<string> }) | string;\n inspectMode: InspectMode;\n extraFlags?: readonly string[];\n};\n\nexport type Policy<D extends Descriptor = Descriptor> = {\n default: D;\n selectable: readonly D[];\n intrinsicFlags: readonly string[];\n};\n\nexport type GeneratePolicy = Policy<GeneratorDescriptor>;\n\nexport const timestampVariant: GeneratorDescriptor = {\n inspectMode: \"readable\",\n construct(brand, opts) {\n try {\n return createTimestampId(brand, codecOpts(opts));\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const opaqueVariant: GeneratorDescriptor = {\n flag: \"--opaque\",\n key: {\n envVar: \"IDS_KEY\",\n formatEnvVar: \"IDS_KEY_FORMAT\",\n encode: encodeOpaqueKey,\n decode: decodeOpaqueKey,\n import: importOpaqueKey,\n },\n inspectMode: \"keyed-readable\",\n construct(brand, opts, key) {\n try {\n return createOpaqueTimestampId(brand, { key: key as OpaqueKey, ...codecOpts(opts) });\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const reverseVariant: GeneratorDescriptor = {\n flag: \"--reverse\",\n inspectMode: \"readable\",\n construct(brand, opts) {\n try {\n return createReverseTimestampId(brand, codecOpts(opts));\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const wrappedVariant: Descriptor = {\n flag: \"--wrapped\",\n key: {\n envVar: \"IDS_WRAPPING_KEY\",\n formatEnvVar: \"IDS_WRAPPING_KEY_FORMAT\",\n encode: encodeWrappingKey,\n decode: decodeWrappingKey,\n import: importWrappingKey,\n },\n inspectMode: \"unwrap\",\n extraFlags: [\"--kind\"],\n construct(brand, _opts, key, values) {\n const kind = parseKind(values ?? new Map());\n if (kind === undefined) return \"--kind is required with --wrapped\";\n if (isKindError(kind)) return kind;\n try {\n return createWrappedKeyId(brand, {\n kind,\n keys: [key as WrappingKey],\n allowDuplicateBrand: true,\n });\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const signedVariant: GeneratorDescriptor = {\n flag: \"--signed\",\n key: {\n envVar: \"IDS_SIGNING_KEY\",\n formatEnvVar: \"IDS_SIGNING_KEY_FORMAT\",\n encode: encodeSigningKey,\n decode: decodeSigningKey,\n import: importSigningKey,\n },\n inspectMode: \"verify\",\n construct(brand, opts, key) {\n try {\n return createSignedTimestampId(brand, {\n keys: [key as SigningKey],\n ...codecOpts(opts),\n });\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const digestVariant: GeneratorDescriptor = {\n flag: \"--digest\",\n key: {\n envVar: \"IDS_DIGEST_KEY\",\n formatEnvVar: \"IDS_DIGEST_KEY_FORMAT\",\n encode: encodeDigestKey,\n decode: decodeDigestKey,\n import: importDigestKey,\n },\n // Digest is one-way: inspect --digest is unsupported by design, so digestVariant is omitted\n // from inspectPolicy.selectable. \"unsupported\" documents that there is no inspect path.\n inspectMode: \"unsupported\",\n extraFlags: [\"--ns\"],\n construct(brand, opts, key, values) {\n const ns = parseNs(values ?? new Map());\n if (ns === undefined) return \"--ns is required with --digest\";\n if (isNsError(ns)) return ns;\n try {\n const codec = createDigestId(brand, { ns, key: key as DigestKey, allowDuplicateBrand: true });\n return {\n safeParse: (v: unknown) => codec.safeParse(v),\n generate(): Promise<string> {\n const reader = opts.readStdin ?? (() => Promise.resolve(\"\"));\n return reader().then((material) => codec.digest(material));\n },\n };\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\n// Determines which flag name appears first in \"cannot use --A and --B together\"\n// messages when two selectable variant flags conflict. Signed always leads;\n// remaining follow registry insertion order (digest, reverse, wrapped, opaque).\nexport const conflictPriorityOrder: readonly Descriptor[] = [\n signedVariant,\n digestVariant,\n reverseVariant,\n wrappedVariant,\n opaqueVariant,\n];\n\nexport const generatePolicy: GeneratePolicy = {\n default: timestampVariant,\n selectable: [opaqueVariant, reverseVariant, signedVariant, digestVariant],\n intrinsicFlags: [\"--count\", \"-c\"],\n};\n\nexport const inspectPolicy: Policy = {\n default: timestampVariant,\n selectable: [reverseVariant, wrappedVariant, opaqueVariant, signedVariant],\n intrinsicFlags: [],\n};\n\nexport const keygenPolicy: Policy = {\n default: opaqueVariant,\n selectable: [wrappedVariant, signedVariant, digestVariant],\n intrinsicFlags: [\"--bits\"],\n};\n","import type { IdCodec } from \"../adapters/adapter-types.js\";\nimport { isKeyFormatError, loadKey, parseKeyFormat } from \"./key-io.js\";\nimport type { RunOpts } from \"./types.js\";\nimport {\n conflictPriorityOrder,\n type Descriptor,\n type GeneratorDescriptor,\n type Policy,\n} from \"./variants.js\";\n\nexport function deriveAllowedFlags(policy: Policy): Set<string> {\n const flags = new Set<string>(policy.intrinsicFlags);\n let hasKeyed = policy.default.key !== undefined;\n for (const v of policy.selectable) {\n if (v.flag !== undefined) flags.add(v.flag);\n if (v.key !== undefined) hasKeyed = true;\n if (v.extraFlags !== undefined) {\n for (const f of v.extraFlags) flags.add(f);\n }\n }\n if (hasKeyed) flags.add(\"--key-format\");\n return flags;\n}\n\nexport function resolveVariant<D extends Descriptor>(\n policy: Policy<D>,\n flags: Set<string>,\n): D | string {\n const selected = conflictPriorityOrder.filter(\n (v): v is D =>\n policy.selectable.some((d) => d === v) && v.flag !== undefined && flags.has(v.flag),\n );\n if (selected.length === 0) return policy.default;\n if (selected.length === 1) return selected[0]!;\n return `cannot use ${selected[0]!.flag} and ${selected[1]!.flag} together`;\n}\n\nexport async function buildCodec(\n variant: GeneratorDescriptor,\n brand: string,\n values: Map<string, string>,\n opts: RunOpts,\n): Promise<(IdCodec<string> & { generate(): string | Promise<string> }) | string>;\nexport async function buildCodec(\n variant: Descriptor,\n brand: string,\n values: Map<string, string>,\n opts: RunOpts,\n): Promise<IdCodec<string> | string>;\nexport async function buildCodec(\n variant: Descriptor,\n brand: string,\n values: Map<string, string>,\n opts: RunOpts,\n): Promise<(IdCodec<string> & { generate?(): string | Promise<string> }) | string> {\n let key: unknown;\n if (variant.key !== undefined) {\n const format = parseKeyFormat(values, opts, variant.key);\n if (isKeyFormatError(format)) return format;\n const keyResult = await loadKey(opts, format, variant.key);\n if (typeof keyResult === \"string\") return keyResult;\n key = keyResult;\n }\n return variant.construct(brand, opts, key, values);\n}\n","import { buildCodec, deriveAllowedFlags, resolveVariant } from \"../dispatch.js\";\nimport { parseCount, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { generatePolicy } from \"../variants.js\";\n\nlet stdinCache: Promise<string> | undefined;\n/* v8 ignore next 12 -- reads from process.stdin; not exercised in unit tests, only in the real binary */\nfunction readProcessStdin(): Promise<string> {\n if (stdinCache === undefined) {\n stdinCache = new Promise<string>((resolve) => {\n const chunks: string[] = [];\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk: string) => chunks.push(chunk));\n process.stdin.on(\"end\", () => resolve(chunks.join(\"\")));\n process.stdin.resume();\n });\n }\n return stdinCache;\n}\n\nexport async function runGenerate(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const allowedFlags = deriveAllowedFlags(generatePolicy);\n const selectorFlags = new Set(\n generatePolicy.selectable.map((v) => v.flag).filter((f): f is string => f !== undefined),\n );\n const valueFlags = new Set([...allowedFlags].filter((f) => !selectorFlags.has(f)));\n const { flags, values, positionals, errors } = splitFlags(args, valueFlags);\n const unsupported = unsupportedFlagForCommand(\"generate\", flags, allowedFlags);\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return 1;\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return 1;\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return 1;\n }\n const [brand] = positionals;\n const count = parseCount(values);\n if (typeof count === \"string\") {\n opts.stderr(count + \"\\n\");\n return 1;\n }\n const variant = resolveVariant(generatePolicy, flags);\n if (typeof variant === \"string\") {\n opts.stderr(variant + \"\\n\");\n return 1;\n }\n if (variant.key === undefined && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque, --signed, or --digest\\n\");\n return 1;\n }\n if (flags.has(\"--digest\") && count > 1) {\n opts.stderr(\n \"--count N > 1 is rejected with --digest: same material always produces the same ID\\n\",\n );\n return 1;\n }\n const optsWithStdin: RunOpts = { ...opts, readStdin: opts.readStdin ?? readProcessStdin };\n const codec = await buildCodec(variant, brand ?? \"\", values, optsWithStdin);\n if (typeof codec === \"string\") {\n opts.stderr(codec + \"\\n\");\n return 1;\n }\n for (let i = 0; i < count; i++) opts.stdout((await codec.generate()) + \"\\n\");\n return 0;\n}\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport function usage(): string {\n return [\n \"Usage: ids <subcommand> [args]\",\n \"\",\n \"Subcommands:\",\n \" inspect, i <id> [--opaque] [--wrapped --kind u32|i32|u64|i64] [--reverse] [--signed] [--key-format hex|base64url]\",\n \" Decode an ID and print brand, timestamp (or lookup key), and canonical form.\",\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" --wrapped reads the wrapping key from IDS_WRAPPING_KEY (hex by default; IDS_WRAPPING_KEY_FORMAT or --key-format).\",\n \" --kind is required with --wrapped: u32, i32, u64, or i64.\",\n \" --reverse decodes a Reverse Timestamp ID (newest-first sort order).\",\n \" --signed decodes a Signed Timestamp ID; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).\",\n \" Without IDS_SIGNING_KEY, --signed prints the timestamp only (no verification). With IDS_SIGNING_KEY, prints verification: ok or failed.\",\n \" Note: --digest is not supported for inspect (Digest IDs are one-way; there is no reverse path).\",\n \" generate, g <brand> [--count, -c N] [--opaque] [--reverse] [--signed] [--digest --ns <ns>] [--key-format hex|base64url]\",\n ` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" --reverse mints Reverse Timestamp IDs (newest-first sort order).\",\n \" --signed mints Signed Timestamp IDs; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).\",\n \" --digest mints a deterministic Digest ID from material read on stdin.\",\n \" --ns <ns> is required: the namespace domain separator (non-secret, non-empty).\",\n \" Reads the digest key from IDS_DIGEST_KEY (hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).\",\n \" Same material + ns + key always produces the same ID. Digest IDs are one-way.\",\n \" --count N > 1 is rejected: same material always produces the same ID.\",\n \" keygen, k [--wrapped] [--signed] [--digest] [--bits 128|192|256] [--key-format hex|base64url]\",\n \" Emit a random key for importOpaqueKey, importWrappingKey, importSigningKey, or importDigestKey (stdout only).\",\n \" --wrapped emits a wrapping key for importWrappingKey instead (IDS_WRAPPING_KEY).\",\n \" --signed emits a signing key for importSigningKey instead (IDS_SIGNING_KEY; hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).\",\n \" --digest emits a digest key for importDigestKey instead (IDS_DIGEST_KEY; hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).\",\n \"\",\n ].join(\"\\n\");\n}\n","import { createTimestampId } from \"../../codecs/timestamp/index.js\";\nimport type { Id, StandardSchemaProps } from \"../../types.js\";\nimport type { SafeVerifyResult } from \"../../codecs/signed/index.js\";\nimport { codecOpts } from \"../codec-options.js\";\nimport { buildCodec, deriveAllowedFlags, resolveVariant } from \"../dispatch.js\";\nimport {\n formatCliError,\n formatInspectOutput,\n formatSignedInspectOutput,\n formatWrappedInspectOutput,\n} from \"../format.js\";\nimport { splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, parseKeyFormat } from \"../key-io.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { usage } from \"../usage.js\";\nimport { inspectPolicy } from \"../variants.js\";\n\ntype WithValidate = { \"~standard\": StandardSchemaProps<string> };\ntype WithExtractTimestamp = { extractTimestamp(id: Id<string>): Date };\ntype WithAsyncExtractTimestamp = { extractTimestamp(id: Id<string>): Promise<Date> };\ntype WithUnwrap = { unwrap(id: Id<string>): Promise<number | bigint> };\ntype WithSafeVerify = { safeVerify(id: string): Promise<SafeVerifyResult<string>> };\n\nexport async function runInspect(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const allowedFlags = deriveAllowedFlags(inspectPolicy);\n const selectorFlags = new Set(\n inspectPolicy.selectable.map((v) => v.flag).filter((f): f is string => f !== undefined),\n );\n const valueFlags = new Set([...allowedFlags].filter((f) => !selectorFlags.has(f)));\n const { flags, values, positionals, errors } = splitFlags(args, valueFlags);\n\n const unsupported = unsupportedFlagForCommand(\"inspect\", flags, allowedFlags);\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return 1;\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return 1;\n }\n const [input] = positionals;\n if (input === undefined) {\n opts.stderr(usage());\n return 1;\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return 1;\n }\n\n const variant = resolveVariant(inspectPolicy, flags);\n if (typeof variant === \"string\") {\n opts.stderr(variant + \"\\n\");\n return 1;\n }\n if (variant.key === undefined && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque, --wrapped, or --signed\\n\");\n return 1;\n }\n\n const brand = input.slice(0, 3).toLowerCase();\n\n // \"verify\" (--signed) mode: the timestamp is plaintext and must be extractable even when\n // the signing key is unavailable. Structural parse happens before key loading so that:\n // bad key format → stderr only, stdout = \"\" (no timestamp shown)\n // invalid payload → stderr only, stdout = \"\" (no timestamp shown)\n // key missing/malformed → stdout has timestamp + \"verification: unavailable\"\n let verifyTimestamp: Date | undefined;\n let verifyCanonical: Id<string> | undefined;\n let verifyNowMs: number | undefined;\n if (variant.inspectMode === \"verify\") {\n const fmtCheck = parseKeyFormat(values, opts, variant.key!);\n if (isKeyFormatError(fmtCheck)) {\n opts.stderr(fmtCheck + \"\\n\");\n return 1;\n }\n let tsCodec: WithValidate & WithExtractTimestamp;\n try {\n tsCodec = createTimestampId(brand, codecOpts(opts)) as unknown as WithValidate &\n WithExtractTimestamp;\n } catch (err) {\n opts.stderr(formatCliError(err) + \"\\n\");\n return 1;\n }\n const structValidation = tsCodec[\"~standard\"].validate(input);\n if (structValidation.issues) {\n opts.stderr(structValidation.issues[0]!.message + \"\\n\");\n return 1;\n }\n verifyCanonical = structValidation.value;\n verifyTimestamp = tsCodec.extractTimestamp(verifyCanonical);\n verifyNowMs = (opts.now ?? Date.now)();\n }\n\n const codecOrError = await buildCodec(variant, brand, values, opts);\n if (typeof codecOrError === \"string\") {\n if (variant.inspectMode === \"verify\") {\n opts.stdout(\n formatSignedInspectOutput({\n brand,\n timestamp: verifyTimestamp!,\n canonical: verifyCanonical!,\n input,\n nowMs: verifyNowMs!,\n verification: \"unavailable\",\n }),\n );\n }\n opts.stderr(codecOrError + \"\\n\");\n return 1;\n }\n\n // Structural validation for non-verify cases (verify already validated above)\n let canonical: Id<string> | undefined;\n if (variant.inspectMode !== \"verify\") {\n const validation = (codecOrError as unknown as WithValidate)[\"~standard\"].validate(input);\n if (validation.issues) {\n opts.stderr(validation.issues[0]!.message + \"\\n\");\n return 1;\n }\n canonical = validation.value;\n }\n\n // Back half: switch on inspectMode for output shapes\n switch (variant.inspectMode) {\n case \"readable\": {\n const timestamp = (codecOrError as unknown as WithExtractTimestamp).extractTimestamp(\n canonical!,\n );\n const nowMs = (opts.now ?? Date.now)();\n opts.stderr(\n \"note: timestamp assumes a plaintext Timestamp ID; if this ID was Opaque-encoded, the timestamp is meaningless — re-run with --opaque and the correct IDS_KEY\\n\",\n );\n opts.stdout(formatInspectOutput({ brand, timestamp, canonical: canonical!, input, nowMs }));\n return 0;\n }\n case \"keyed-readable\": {\n const timestamp = await (\n codecOrError as unknown as WithAsyncExtractTimestamp\n ).extractTimestamp(canonical!);\n const nowMs = (opts.now ?? Date.now)();\n opts.stderr(\n \"note: timestamp assumes IDS_KEY matches the key used at generation; a wrong key yields a plausible but incorrect timestamp\\n\",\n );\n opts.stdout(formatInspectOutput({ brand, timestamp, canonical: canonical!, input, nowMs }));\n return 0;\n }\n case \"unwrap\": {\n let lookupKey: number | bigint;\n try {\n lookupKey = await (codecOrError as unknown as WithUnwrap).unwrap(canonical!);\n } catch (err) {\n opts.stderr(formatCliError(err) + \"\\n\");\n return 1;\n }\n opts.stdout(formatWrappedInspectOutput({ brand, lookupKey, canonical: canonical!, input }));\n return 0;\n }\n case \"verify\": {\n const verifyResult = await (codecOrError as unknown as WithSafeVerify).safeVerify(input);\n if (!verifyResult.ok) {\n /* v8 ignore next 4 -- defensive: both codecs share the same wire parse so ParseError\n is unreachable after the createTimestampId pre-validation above passes */\n if (verifyResult.error !== \"verification_failed\") {\n opts.stderr(verifyResult.error + \"\\n\");\n return 1;\n }\n opts.stdout(\n formatSignedInspectOutput({\n brand,\n timestamp: verifyTimestamp!,\n canonical: verifyCanonical!,\n input,\n nowMs: verifyNowMs!,\n verification: \"failed\",\n }),\n );\n opts.stderr(\"verification_failed: verification failed\\n\");\n return 1;\n }\n opts.stdout(\n formatSignedInspectOutput({\n brand,\n timestamp: verifyTimestamp!,\n canonical: verifyResult.id,\n input,\n nowMs: verifyNowMs!,\n verification: \"ok\",\n }),\n );\n return 0;\n }\n /* v8 ignore next 5 -- defensive: digestVariant is the only \"unsupported\" variant and it is\n excluded from inspectPolicy.selectable, so resolveVariant can never return it here. The\n branch exists for TypeScript exhaustiveness. */\n case \"unsupported\": {\n opts.stderr(\"unsupported flag for inspect: --digest\\n\");\n return 1;\n }\n }\n}\n","import { deriveAllowedFlags, resolveVariant } from \"../dispatch.js\";\nimport { parseBits, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, parseKeyFormatFromFlag } from \"../key-io.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { keygenPolicy } from \"../variants.js\";\n\nexport function runKeygen(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const allowedFlags = deriveAllowedFlags(keygenPolicy);\n const variantExtraFlags = new Set(keygenPolicy.selectable.flatMap((v) => v.extraFlags ?? []));\n const { flags, values, positionals, errors } = splitFlags(args, allowedFlags);\n const unsupported = unsupportedFlagForCommand(\n \"keygen\",\n flags,\n new Set([...allowedFlags].filter((f) => !variantExtraFlags.has(f))),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const extra = positionals[0];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const variant = resolveVariant(keygenPolicy, flags);\n if (typeof variant === \"string\") {\n opts.stderr(variant + \"\\n\");\n return Promise.resolve(1);\n }\n const bits = parseBits(values);\n if (typeof bits === \"string\") {\n opts.stderr(bits + \"\\n\");\n return Promise.resolve(1);\n }\n const format = parseKeyFormatFromFlag(values);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n /* v8 ignore next 4 -- defensive guard; all keygenPolicy variants have key defined */\n if (variant.key === undefined) {\n opts.stderr(\"internal: keygen policy variant has no key facet\\n\");\n return Promise.resolve(1);\n }\n const bytes = new Uint8Array(bits / 8);\n crypto.getRandomValues(bytes);\n opts.stdout(variant.key.encode(bytes, format) + \"\\n\");\n return Promise.resolve(0);\n}\n","import { runGenerate } from \"./commands/generate.js\";\nimport { runInspect } from \"./commands/inspect.js\";\nimport { runKeygen } from \"./commands/keygen.js\";\nimport type { CommandHandler, RunOpts } from \"./types.js\";\nimport { usage } from \"./usage.js\";\n\nexport type { RunOpts } from \"./types.js\";\n\ntype Command = {\n names: ReadonlyArray<string>;\n run: CommandHandler;\n};\n\nconst commands: ReadonlyArray<Command> = [\n { names: [\"generate\", \"g\"], run: runGenerate },\n { names: [\"inspect\", \"i\"], run: runInspect },\n { names: [\"keygen\", \"k\"], run: runKeygen },\n];\n\nexport async function run(opts: RunOpts): Promise<number> {\n const [subcommand, ...rest] = opts.argv;\n const command = commands.find((candidate) => candidate.names.includes(subcommand ?? \"\"));\n if (command !== undefined) return command.run(rest, opts);\n if (subcommand === undefined || subcommand === \"--help\" || subcommand === \"-h\") {\n opts.stdout(usage());\n return 0;\n }\n opts.stderr(usage());\n return 1;\n}\n","#!/usr/bin/env node\nimport { run } from \"../src/cli/index.js\";\n\nprocess.exitCode = await run({\n argv: process.argv.slice(2),\n stdout: (s) => process.stdout.write(s),\n stderr: (s) => process.stderr.write(s),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli/key-io.ts","../src/cli/codec-options.ts","../src/cli/constants.ts","../src/cli/flags.ts","../src/cli/format.ts","../src/cli/variants.ts","../src/cli/dispatch.ts","../src/cli/commands/generate.ts","../src/cli/usage.ts","../src/cli/commands/inspect.ts","../src/cli/commands/keygen.ts","../src/cli/index.ts","../bin/cli.ts"],"sourcesContent":["import type { RunOpts } from \"./types.js\";\n\nexport type KeyFormat = \"hex\" | \"base64url\";\n\nexport type KeyFacet<K> = {\n envVar: string;\n formatEnvVar: string;\n // Not yet consumed by any helper here; the keygen-delegation chunk wires it.\n encode: (bytes: Uint8Array, format: KeyFormat) => string;\n decode: (raw: string, format: KeyFormat) => Uint8Array;\n import: (bytes: Uint8Array) => K | Promise<K>;\n};\n\nexport function isKeyFormatError(result: KeyFormat | string): result is string {\n return result !== \"hex\" && result !== \"base64url\";\n}\n\nfunction parseKeyFormatFlag(values: Map<string, string>): KeyFormat | string | undefined {\n const fromFlag = values.get(\"--key-format\");\n if (fromFlag === undefined) return undefined;\n if (fromFlag === \"\") return \"--key-format requires a value\";\n if (fromFlag === \"hex\" || fromFlag === \"base64url\") return fromFlag;\n return `--key-format must be hex or base64url, got '${fromFlag}'`;\n}\n\nexport function parseKeyFormatFromFlag(values: Map<string, string>): KeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag === undefined) return \"hex\";\n return fromFlag;\n}\n\nexport function parseKeyFormat(\n values: Map<string, string>,\n opts: RunOpts,\n facet: Pick<KeyFacet<unknown>, \"formatEnvVar\">,\n): KeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag !== undefined) return fromFlag;\n const env = opts.env ?? process.env;\n const fromEnv = env[facet.formatEnvVar];\n if (fromEnv === undefined || fromEnv === \"\") return \"hex\";\n if (fromEnv === \"hex\" || fromEnv === \"base64url\") return fromEnv;\n return `${facet.formatEnvVar} must be hex or base64url, got '${fromEnv}'`;\n}\n\nexport async function loadKey<K>(\n opts: RunOpts,\n format: KeyFormat,\n facet: Pick<KeyFacet<K>, \"envVar\" | \"decode\" | \"import\">,\n): Promise<K | string> {\n const env = opts.env ?? process.env;\n const raw = env[facet.envVar];\n if (raw === undefined || raw === \"\") return `missing ${facet.envVar} environment variable`;\n try {\n return await facet.import(facet.decode(raw, format));\n } catch (err) {\n return (err as Error).message;\n }\n}\n","import type { TimestampOptions } from \"../codecs/timestamp/index.js\";\nimport type { RunOpts } from \"./types.js\";\n\nexport function codecOpts(opts: RunOpts): Partial<TimestampOptions> {\n // CLI invocations are intentionally ephemeral: one codec per run, never\n // retained, so this is not the duplicate-brand warning case.\n const o: Partial<TimestampOptions> = { allowDuplicateBrand: true };\n if (opts.now !== undefined) o.now = opts.now;\n if (opts.rng !== undefined) o.rng = opts.rng;\n return o;\n}\n","export const maxGenerateCount = 10_000;\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport type ParsedFlags = {\n flags: Set<string>;\n values: Map<string, string>;\n positionals: string[];\n errors: string[];\n};\n\nfunction splitFlagToken(arg: string): { flag: string; inlineValue: string | undefined } {\n const eq = arg.indexOf(\"=\");\n if (eq <= 0) return { flag: arg, inlineValue: undefined };\n return { flag: arg.slice(0, eq), inlineValue: arg.slice(eq + 1) };\n}\n\nexport function splitFlags(args: ReadonlyArray<string>, valueFlags: Set<string>): ParsedFlags {\n const flags = new Set<string>();\n const values = new Map<string, string>();\n const positionals: string[] = [];\n const errors: string[] = [];\n const seenFlags = new Set<string>();\n const addFlag = (flag: string) => {\n const canonical = canonicalFlag(flag);\n if (seenFlags.has(canonical)) errors.push(`duplicate flag: ${canonical}`);\n seenFlags.add(canonical);\n flags.add(flag);\n };\n for (let i = 0; i < args.length; i++) {\n const raw = args[i]!;\n const { flag, inlineValue } = splitFlagToken(raw);\n if (valueFlags.has(flag)) {\n if (inlineValue !== undefined) {\n addFlag(flag);\n values.set(flag, inlineValue);\n continue;\n }\n const value = args[i + 1];\n if (value === undefined || value.startsWith(\"-\")) {\n addFlag(flag);\n values.set(flag, \"\");\n continue;\n }\n addFlag(flag);\n values.set(flag, value);\n i++;\n continue;\n }\n if (flag.startsWith(\"-\")) {\n addFlag(flag);\n if (inlineValue !== undefined) errors.push(`flag does not take a value: ${flag}`);\n continue;\n }\n positionals.push(raw);\n }\n return { flags, values, positionals, errors };\n}\n\nfunction canonicalFlag(flag: string): string {\n if (flag === \"-c\") return \"--count\";\n return flag;\n}\n\nconst knownFlags = new Set([\n \"--opaque\",\n \"--wrapped\",\n \"--reverse\",\n \"--signed\",\n \"--digest\",\n \"--ns\",\n \"--kind\",\n \"--key-format\",\n \"--count\",\n \"-c\",\n \"--bits\",\n]);\n\nexport function unsupportedFlagForCommand(\n command: string,\n flags: Set<string>,\n allowed: Set<string>,\n): string | undefined {\n for (const flag of flags) {\n if (!allowed.has(flag)) {\n return knownFlags.has(flag)\n ? `unsupported flag for ${command}: ${flag}`\n : `unsupported flag: ${flag}`;\n }\n }\n return undefined;\n}\n\nexport function parseCount(values: Map<string, string>): number | string {\n const raw = values.get(\"--count\") ?? values.get(\"-c\");\n if (raw === undefined) return 1;\n if (raw === \"\") return \"--count requires a value\";\n if (!/^[1-9][0-9]*$/.test(raw)) return `--count must be a positive integer, got '${raw}'`;\n const count = Number.parseInt(raw, 10);\n if (!Number.isSafeInteger(count) || count > maxGenerateCount) {\n return `--count must be at most ${maxGenerateCount}, got '${raw}'`;\n }\n return count;\n}\n\nexport function parseBits(values: Map<string, string>): number | string {\n const raw = values.get(\"--bits\");\n if (raw === undefined) return 256;\n if (raw === \"\") return \"--bits requires a value\";\n if (raw === \"128\") return 128;\n if (raw === \"192\") return 192;\n if (raw === \"256\") return 256;\n return `--bits must be 128, 192, or 256, got '${raw}'`;\n}\n\nexport type WrappedKindValue = \"u32\" | \"i32\" | \"u64\" | \"i64\";\n\nexport function parseKind(values: Map<string, string>): WrappedKindValue | string | undefined {\n const raw = values.get(\"--kind\");\n if (raw === undefined) return undefined;\n if (raw === \"\") return \"--kind requires a value\";\n if (raw === \"u32\" || raw === \"i32\" || raw === \"u64\" || raw === \"i64\") return raw;\n return `--kind must be u32, i32, u64, or i64, got '${raw}'`;\n}\n\nexport function isKindError(result: WrappedKindValue | string): result is string {\n return result !== \"u32\" && result !== \"i32\" && result !== \"u64\" && result !== \"i64\";\n}\n\nexport function parseNs(values: Map<string, string>): string | undefined {\n const raw = values.get(\"--ns\");\n if (raw === undefined) return undefined;\n if (raw === \"\") return \"--ns requires a value\";\n return raw;\n}\n\nexport function isNsError(result: string): boolean {\n return result === \"--ns requires a value\";\n}\n","import { isIdsError } from \"../error.js\";\nimport type { Id } from \"../types.js\";\n\ntype InspectOutput = {\n brand: string;\n timestamp: Date;\n canonical: Id<string>;\n input: string;\n nowMs: number;\n};\n\ntype SignedInspectOutput = InspectOutput & {\n verification: \"ok\" | \"failed\" | \"unavailable\";\n};\n\ntype WrappedInspectOutput = {\n brand: string;\n lookupKey: number | bigint;\n canonical: Id<string>;\n input: string;\n};\n\nexport function formatCliError(err: unknown): string {\n return isIdsError(err)\n ? `${err.code}: ${err.message}`\n : err instanceof Error\n ? err.message\n : String(err);\n}\n\nexport function formatWrappedInspectOutput(result: WrappedInspectOutput): string {\n const inputLine = describeInputForm(result.input, result.canonical);\n return [\n `brand: ${result.brand}`,\n `lookup-key: ${result.lookupKey.toString()}`,\n `canonical: ${result.canonical}`,\n `input: ${inputLine}`,\n \"\",\n ].join(\"\\n\");\n}\n\nexport function formatSignedInspectOutput(result: SignedInspectOutput): string {\n const relative = formatRelative(result.timestamp.getTime(), result.nowMs);\n const inputLine = describeInputForm(result.input, result.canonical);\n const lines = [\n `brand: ${result.brand}`,\n `timestamp: ${result.timestamp.toISOString()} (${relative})`,\n ];\n // \"verification:\" is the spec-mandated key name; the extra chars vs. other labels are intentional.\n lines.push(`verification: ${result.verification}`);\n lines.push(`canonical: ${result.canonical}`, `input: ${inputLine}`, \"\");\n return lines.join(\"\\n\");\n}\n\nexport function formatInspectOutput(result: InspectOutput): string {\n const relative = formatRelative(result.timestamp.getTime(), result.nowMs);\n const inputLine = describeInputForm(result.input, result.canonical);\n return [\n `brand: ${result.brand}`,\n `timestamp: ${result.timestamp.toISOString()} (${relative})`,\n `canonical: ${result.canonical}`,\n `input: ${inputLine}`,\n \"\",\n ].join(\"\\n\");\n}\n\nfunction describeInputForm(input: string, canonical: Id<string>): string {\n if (input === canonical) return \"canonical\";\n const notes: string[] = [];\n if (input !== input.toLowerCase()) notes.push(\"was uppercase\");\n if (/[ilo]/i.test(input.slice(4))) notes.push(\"used Crockford aliases\");\n return `not canonical (${notes.join(\" + \")})`;\n}\n\nconst msPerSecond = 1000;\nexport const msPerMinute: number = 60 * msPerSecond;\nexport const msPerHour: number = 60 * msPerMinute;\nexport const msPerDay: number = 24 * msPerHour;\nconst daysPerMonth = 30.44;\nconst monthsPerYear = 12;\n\nfunction formatRelative(thenMs: number, nowMs: number): string {\n const diff = nowMs - thenMs;\n const abs = Math.abs(diff);\n const suffix = diff < 0 ? \"from now\" : \"ago\";\n\n const head = headUnits(abs);\n return head === \"\" ? \"just now\" : `${head} ${suffix}`;\n}\n\nfunction headUnits(abs: number): string {\n if (abs < msPerMinute) return \"\";\n if (abs < msPerHour) return unit(Math.round(abs / msPerMinute), \"minute\");\n if (abs < msPerDay) return unit(Math.round(abs / msPerHour), \"hour\");\n if (abs < msPerDay * daysPerMonth) return unit(Math.round(abs / msPerDay), \"day\");\n\n const totalMonths = Math.round(abs / (msPerDay * daysPerMonth));\n if (totalMonths < monthsPerYear) return unit(totalMonths, \"month\");\n\n const years = Math.floor(totalMonths / monthsPerYear);\n const months = totalMonths % monthsPerYear;\n return months === 0 ? unit(years, \"year\") : `${unit(years, \"year\")} ${unit(months, \"month\")}`;\n}\n\nfunction unit(n: number, name: string): string {\n return `${n} ${n === 1 ? name : `${name}s`}`;\n}\n","import {\n createDigestId,\n decodeDigestKey,\n encodeDigestKey,\n importDigestKey,\n type DigestKey,\n} from \"../codecs/digest/index.js\";\nimport {\n createOpaqueTimestampId,\n decodeOpaqueKey,\n encodeOpaqueKey,\n importOpaqueKey,\n type OpaqueKey,\n} from \"../codecs/opaque/index.js\";\nimport { createReverseTimestampId } from \"../codecs/reverse/index.js\";\nimport {\n createSignedTimestampId,\n decodeSigningKey,\n encodeSigningKey,\n importSigningKey,\n type SigningKey,\n} from \"../codecs/signed/index.js\";\nimport { createTimestampId } from \"../codecs/timestamp/index.js\";\nimport {\n createWrappedKeyId,\n decodeWrappingKey,\n encodeWrappingKey,\n importWrappingKey,\n type WrappingKey,\n} from \"../codecs/wrapped/index.js\";\nimport type { IdCodec } from \"../adapters/adapter-types.js\";\nimport { codecOpts } from \"./codec-options.js\";\nimport { isKindError, isNsError, parseKind, parseNs } from \"./flags.js\";\nimport { formatCliError } from \"./format.js\";\nimport type { KeyFacet } from \"./key-io.js\";\nimport type { RunOpts } from \"./types.js\";\n\ntype InspectMode = \"readable\" | \"keyed-readable\" | \"unwrap\" | \"verify\" | \"unsupported\";\n\nexport type Descriptor = {\n flag?: string;\n key?: KeyFacet<unknown>;\n construct: (\n brand: string,\n opts: RunOpts,\n key?: unknown,\n values?: Map<string, string>,\n ) => (IdCodec<string> & { generate?(): string | Promise<string> }) | string;\n inspectMode: InspectMode;\n extraFlags?: readonly string[];\n};\n\nexport type GeneratorDescriptor = {\n flag?: string;\n key?: KeyFacet<unknown>;\n construct: (\n brand: string,\n opts: RunOpts,\n key?: unknown,\n values?: Map<string, string>,\n ) => (IdCodec<string> & { generate(): string | Promise<string> }) | string;\n inspectMode: InspectMode;\n extraFlags?: readonly string[];\n};\n\nexport type Policy<D extends Descriptor = Descriptor> = {\n default: D;\n selectable: readonly D[];\n intrinsicFlags: readonly string[];\n};\n\nexport type GeneratePolicy = Policy<GeneratorDescriptor>;\n\nexport const timestampVariant: GeneratorDescriptor = {\n inspectMode: \"readable\",\n construct(brand, opts) {\n try {\n return createTimestampId(brand, codecOpts(opts));\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const opaqueVariant: GeneratorDescriptor = {\n flag: \"--opaque\",\n key: {\n envVar: \"IDS_KEY\",\n formatEnvVar: \"IDS_KEY_FORMAT\",\n encode: encodeOpaqueKey,\n decode: decodeOpaqueKey,\n import: importOpaqueKey,\n },\n inspectMode: \"keyed-readable\",\n construct(brand, opts, key) {\n try {\n return createOpaqueTimestampId(brand, { key: key as OpaqueKey, ...codecOpts(opts) });\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const reverseVariant: GeneratorDescriptor = {\n flag: \"--reverse\",\n inspectMode: \"readable\",\n construct(brand, opts) {\n try {\n return createReverseTimestampId(brand, codecOpts(opts));\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const wrappedVariant: Descriptor = {\n flag: \"--wrapped\",\n key: {\n envVar: \"IDS_WRAPPING_KEY\",\n formatEnvVar: \"IDS_WRAPPING_KEY_FORMAT\",\n encode: encodeWrappingKey,\n decode: decodeWrappingKey,\n import: importWrappingKey,\n },\n inspectMode: \"unwrap\",\n extraFlags: [\"--kind\"],\n construct(brand, _opts, key, values) {\n const kind = parseKind(values ?? new Map());\n if (kind === undefined) return \"--kind is required with --wrapped\";\n if (isKindError(kind)) return kind;\n try {\n return createWrappedKeyId(brand, {\n kind,\n keys: [key as WrappingKey],\n allowDuplicateBrand: true,\n });\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const signedVariant: GeneratorDescriptor = {\n flag: \"--signed\",\n key: {\n envVar: \"IDS_SIGNING_KEY\",\n formatEnvVar: \"IDS_SIGNING_KEY_FORMAT\",\n encode: encodeSigningKey,\n decode: decodeSigningKey,\n import: importSigningKey,\n },\n inspectMode: \"verify\",\n construct(brand, opts, key) {\n try {\n return createSignedTimestampId(brand, {\n keys: [key as SigningKey],\n ...codecOpts(opts),\n });\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\nexport const digestVariant: GeneratorDescriptor = {\n flag: \"--digest\",\n key: {\n envVar: \"IDS_DIGEST_KEY\",\n formatEnvVar: \"IDS_DIGEST_KEY_FORMAT\",\n encode: encodeDigestKey,\n decode: decodeDigestKey,\n import: importDigestKey,\n },\n // Digest is one-way: inspect --digest is unsupported by design, so digestVariant is omitted\n // from inspectPolicy.selectable. \"unsupported\" documents that there is no inspect path.\n inspectMode: \"unsupported\",\n extraFlags: [\"--ns\"],\n construct(brand, opts, key, values) {\n const ns = parseNs(values ?? new Map());\n if (ns === undefined) return \"--ns is required with --digest\";\n if (isNsError(ns)) return ns;\n try {\n const codec = createDigestId(brand, { ns, key: key as DigestKey, allowDuplicateBrand: true });\n return {\n safeParse: (v: unknown) => codec.safeParse(v),\n generate(): Promise<string> {\n const reader = opts.readStdin ?? (() => Promise.resolve(\"\"));\n return reader().then((material) => codec.digest(material));\n },\n };\n } catch (err) {\n return formatCliError(err);\n }\n },\n};\n\n// Determines which flag name appears first in \"cannot use --A and --B together\"\n// messages when two selectable variant flags conflict. Signed always leads;\n// remaining follow registry insertion order (digest, reverse, wrapped, opaque).\nexport const conflictPriorityOrder: readonly Descriptor[] = [\n signedVariant,\n digestVariant,\n reverseVariant,\n wrappedVariant,\n opaqueVariant,\n];\n\nexport const generatePolicy: GeneratePolicy = {\n default: timestampVariant,\n selectable: [opaqueVariant, reverseVariant, signedVariant, digestVariant],\n intrinsicFlags: [\"--count\", \"-c\"],\n};\n\nexport const inspectPolicy: Policy = {\n default: timestampVariant,\n selectable: [reverseVariant, wrappedVariant, opaqueVariant, signedVariant],\n intrinsicFlags: [],\n};\n\nexport const keygenPolicy: Policy = {\n default: opaqueVariant,\n selectable: [wrappedVariant, signedVariant, digestVariant],\n intrinsicFlags: [\"--bits\"],\n};\n","import type { IdCodec } from \"../adapters/adapter-types.js\";\nimport { isKeyFormatError, loadKey, parseKeyFormat } from \"./key-io.js\";\nimport type { RunOpts } from \"./types.js\";\nimport {\n conflictPriorityOrder,\n type Descriptor,\n type GeneratorDescriptor,\n type Policy,\n} from \"./variants.js\";\n\nexport function deriveAllowedFlags(policy: Policy): Set<string> {\n const flags = new Set<string>(policy.intrinsicFlags);\n let hasKeyed = policy.default.key !== undefined;\n for (const v of policy.selectable) {\n if (v.flag !== undefined) flags.add(v.flag);\n if (v.key !== undefined) hasKeyed = true;\n if (v.extraFlags !== undefined) {\n for (const f of v.extraFlags) flags.add(f);\n }\n }\n if (hasKeyed) flags.add(\"--key-format\");\n return flags;\n}\n\nexport function resolveVariant<D extends Descriptor>(\n policy: Policy<D>,\n flags: Set<string>,\n): D | string {\n const selected = conflictPriorityOrder.filter(\n (v): v is D =>\n policy.selectable.some((d) => d === v) && v.flag !== undefined && flags.has(v.flag),\n );\n if (selected.length === 0) return policy.default;\n if (selected.length === 1) return selected[0]!;\n return `cannot use ${selected[0]!.flag} and ${selected[1]!.flag} together`;\n}\n\nexport async function buildCodec(\n variant: GeneratorDescriptor,\n brand: string,\n values: Map<string, string>,\n opts: RunOpts,\n): Promise<(IdCodec<string> & { generate(): string | Promise<string> }) | string>;\nexport async function buildCodec(\n variant: Descriptor,\n brand: string,\n values: Map<string, string>,\n opts: RunOpts,\n): Promise<IdCodec<string> | string>;\nexport async function buildCodec(\n variant: Descriptor,\n brand: string,\n values: Map<string, string>,\n opts: RunOpts,\n): Promise<(IdCodec<string> & { generate?(): string | Promise<string> }) | string> {\n let key: unknown;\n if (variant.key !== undefined) {\n const format = parseKeyFormat(values, opts, variant.key);\n if (isKeyFormatError(format)) return format;\n const keyResult = await loadKey(opts, format, variant.key);\n if (typeof keyResult === \"string\") return keyResult;\n key = keyResult;\n }\n return variant.construct(brand, opts, key, values);\n}\n","import { buildCodec, deriveAllowedFlags, resolveVariant } from \"../dispatch.js\";\nimport { parseCount, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { generatePolicy } from \"../variants.js\";\n\nlet stdinCache: Promise<string> | undefined;\n/* v8 ignore next 12 -- reads from process.stdin; not exercised in unit tests, only in the real binary */\nfunction readProcessStdin(): Promise<string> {\n if (stdinCache === undefined) {\n stdinCache = new Promise<string>((resolve) => {\n const chunks: string[] = [];\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk: string) => chunks.push(chunk));\n process.stdin.on(\"end\", () => resolve(chunks.join(\"\")));\n process.stdin.resume();\n });\n }\n return stdinCache;\n}\n\nexport async function runGenerate(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const allowedFlags = deriveAllowedFlags(generatePolicy);\n const selectorFlags = new Set(\n generatePolicy.selectable.map((v) => v.flag).filter((f): f is string => f !== undefined),\n );\n const valueFlags = new Set([...allowedFlags].filter((f) => !selectorFlags.has(f)));\n const { flags, values, positionals, errors } = splitFlags(args, valueFlags);\n const unsupported = unsupportedFlagForCommand(\"generate\", flags, allowedFlags);\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return 1;\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return 1;\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return 1;\n }\n const [brand] = positionals;\n const count = parseCount(values);\n if (typeof count === \"string\") {\n opts.stderr(count + \"\\n\");\n return 1;\n }\n const variant = resolveVariant(generatePolicy, flags);\n if (typeof variant === \"string\") {\n opts.stderr(variant + \"\\n\");\n return 1;\n }\n if (variant.key === undefined && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque, --signed, or --digest\\n\");\n return 1;\n }\n if (flags.has(\"--digest\") && count > 1) {\n opts.stderr(\n \"--count N > 1 is rejected with --digest: same material always produces the same ID\\n\",\n );\n return 1;\n }\n const optsWithStdin: RunOpts = { ...opts, readStdin: opts.readStdin ?? readProcessStdin };\n const codec = await buildCodec(variant, brand ?? \"\", values, optsWithStdin);\n if (typeof codec === \"string\") {\n opts.stderr(codec + \"\\n\");\n return 1;\n }\n for (let i = 0; i < count; i++) opts.stdout((await codec.generate()) + \"\\n\");\n return 0;\n}\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport function usage(): string {\n return [\n \"Usage: ids <subcommand> [args]\",\n \"\",\n \"Subcommands:\",\n \" inspect, i <id> [--opaque] [--wrapped --kind u32|i32|u64|i64] [--reverse] [--signed] [--key-format hex|base64url]\",\n \" Decode an ID and print brand, timestamp (or lookup key), and canonical form.\",\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" --wrapped reads the wrapping key from IDS_WRAPPING_KEY (hex by default; IDS_WRAPPING_KEY_FORMAT or --key-format).\",\n \" --kind is required with --wrapped: u32, i32, u64, or i64.\",\n \" --reverse decodes a Reverse Timestamp ID (newest-first sort order).\",\n \" --signed decodes a Signed Timestamp ID; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).\",\n \" Without IDS_SIGNING_KEY, --signed prints the timestamp only (no verification). With IDS_SIGNING_KEY, prints verification: ok or failed.\",\n \" Note: --digest is not supported for inspect (Digest IDs are one-way; there is no reverse path).\",\n \" generate, g <brand> [--count, -c N] [--opaque] [--reverse] [--signed] [--digest --ns <ns>] [--key-format hex|base64url]\",\n ` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" --reverse mints Reverse Timestamp IDs (newest-first sort order).\",\n \" --signed mints Signed Timestamp IDs; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).\",\n \" --digest mints a deterministic Digest ID from material read on stdin.\",\n \" --ns <ns> is required: the namespace domain separator (non-secret, non-empty).\",\n \" Reads the digest key from IDS_DIGEST_KEY (hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).\",\n \" Same material + ns + key always produces the same ID. Digest IDs are one-way.\",\n \" --count N > 1 is rejected: same material always produces the same ID.\",\n \" keygen, k [--wrapped] [--signed] [--digest] [--bits 128|192|256] [--key-format hex|base64url]\",\n \" Emit a random key for importOpaqueKey, importWrappingKey, importSigningKey, or importDigestKey (stdout only).\",\n \" --wrapped emits a wrapping key for importWrappingKey instead (IDS_WRAPPING_KEY).\",\n \" --signed emits a signing key for importSigningKey instead (IDS_SIGNING_KEY; hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).\",\n \" --digest emits a digest key for importDigestKey instead (IDS_DIGEST_KEY; hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).\",\n \"\",\n ].join(\"\\n\");\n}\n","import { createTimestampId } from \"../../codecs/timestamp/index.js\";\nimport type { Id, StandardSchemaProps } from \"../../types.js\";\nimport type { SafeVerifyResult } from \"../../codecs/signed/index.js\";\nimport { codecOpts } from \"../codec-options.js\";\nimport { buildCodec, deriveAllowedFlags, resolveVariant } from \"../dispatch.js\";\nimport {\n formatCliError,\n formatInspectOutput,\n formatSignedInspectOutput,\n formatWrappedInspectOutput,\n} from \"../format.js\";\nimport { splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, parseKeyFormat } from \"../key-io.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { usage } from \"../usage.js\";\nimport { inspectPolicy } from \"../variants.js\";\n\ntype WithValidate = { \"~standard\": StandardSchemaProps<string> };\ntype WithExtractTimestamp = { extractTimestamp(id: Id<string>): Date };\ntype WithAsyncExtractTimestamp = { extractTimestamp(id: Id<string>): Promise<Date> };\ntype WithUnwrap = { unwrap(id: Id<string>): Promise<number | bigint> };\ntype WithSafeVerify = { safeVerify(id: string): Promise<SafeVerifyResult<string>> };\n\nexport async function runInspect(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const allowedFlags = deriveAllowedFlags(inspectPolicy);\n const selectorFlags = new Set(\n inspectPolicy.selectable.map((v) => v.flag).filter((f): f is string => f !== undefined),\n );\n const valueFlags = new Set([...allowedFlags].filter((f) => !selectorFlags.has(f)));\n const { flags, values, positionals, errors } = splitFlags(args, valueFlags);\n\n const unsupported = unsupportedFlagForCommand(\"inspect\", flags, allowedFlags);\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return 1;\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return 1;\n }\n const [input] = positionals;\n if (input === undefined) {\n opts.stderr(usage());\n return 1;\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return 1;\n }\n\n const variant = resolveVariant(inspectPolicy, flags);\n if (typeof variant === \"string\") {\n opts.stderr(variant + \"\\n\");\n return 1;\n }\n if (variant.key === undefined && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque, --wrapped, or --signed\\n\");\n return 1;\n }\n\n const brand = input.slice(0, 3).toLowerCase();\n\n // \"verify\" (--signed) mode: the timestamp is plaintext and must be extractable even when\n // the signing key is unavailable. Structural parse happens before key loading so that:\n // bad key format → stderr only, stdout = \"\" (no timestamp shown)\n // invalid payload → stderr only, stdout = \"\" (no timestamp shown)\n // key missing/malformed → stdout has timestamp + \"verification: unavailable\"\n let verifyTimestamp: Date | undefined;\n let verifyCanonical: Id<string> | undefined;\n let verifyNowMs: number | undefined;\n if (variant.inspectMode === \"verify\") {\n const fmtCheck = parseKeyFormat(values, opts, variant.key!);\n if (isKeyFormatError(fmtCheck)) {\n opts.stderr(fmtCheck + \"\\n\");\n return 1;\n }\n let tsCodec: WithValidate & WithExtractTimestamp;\n try {\n tsCodec = createTimestampId(brand, codecOpts(opts)) as unknown as WithValidate &\n WithExtractTimestamp;\n } catch (err) {\n opts.stderr(formatCliError(err) + \"\\n\");\n return 1;\n }\n const structValidation = tsCodec[\"~standard\"].validate(input);\n if (structValidation.issues) {\n opts.stderr(structValidation.issues[0]!.message + \"\\n\");\n return 1;\n }\n verifyCanonical = structValidation.value;\n verifyTimestamp = tsCodec.extractTimestamp(verifyCanonical);\n verifyNowMs = (opts.now ?? Date.now)();\n }\n\n const codecOrError = await buildCodec(variant, brand, values, opts);\n if (typeof codecOrError === \"string\") {\n if (variant.inspectMode === \"verify\") {\n opts.stdout(\n formatSignedInspectOutput({\n brand,\n timestamp: verifyTimestamp!,\n canonical: verifyCanonical!,\n input,\n nowMs: verifyNowMs!,\n verification: \"unavailable\",\n }),\n );\n }\n opts.stderr(codecOrError + \"\\n\");\n return 1;\n }\n\n // Structural validation for non-verify cases (verify already validated above)\n let canonical: Id<string> | undefined;\n if (variant.inspectMode !== \"verify\") {\n const validation = (codecOrError as unknown as WithValidate)[\"~standard\"].validate(input);\n if (validation.issues) {\n opts.stderr(validation.issues[0]!.message + \"\\n\");\n return 1;\n }\n canonical = validation.value;\n }\n\n // Back half: switch on inspectMode for output shapes\n switch (variant.inspectMode) {\n case \"readable\": {\n const timestamp = (codecOrError as unknown as WithExtractTimestamp).extractTimestamp(\n canonical!,\n );\n const nowMs = (opts.now ?? Date.now)();\n opts.stderr(\n \"note: timestamp assumes a plaintext Timestamp ID; if this ID was Opaque-encoded, the timestamp is meaningless — re-run with --opaque and the correct IDS_KEY\\n\",\n );\n opts.stdout(formatInspectOutput({ brand, timestamp, canonical: canonical!, input, nowMs }));\n return 0;\n }\n case \"keyed-readable\": {\n const timestamp = await (\n codecOrError as unknown as WithAsyncExtractTimestamp\n ).extractTimestamp(canonical!);\n const nowMs = (opts.now ?? Date.now)();\n opts.stderr(\n \"note: timestamp assumes IDS_KEY matches the key used at generation; a wrong key yields a plausible but incorrect timestamp\\n\",\n );\n opts.stdout(formatInspectOutput({ brand, timestamp, canonical: canonical!, input, nowMs }));\n return 0;\n }\n case \"unwrap\": {\n let lookupKey: number | bigint;\n try {\n lookupKey = await (codecOrError as unknown as WithUnwrap).unwrap(canonical!);\n } catch (err) {\n opts.stderr(formatCliError(err) + \"\\n\");\n return 1;\n }\n opts.stdout(formatWrappedInspectOutput({ brand, lookupKey, canonical: canonical!, input }));\n return 0;\n }\n case \"verify\": {\n const verifyResult = await (codecOrError as unknown as WithSafeVerify).safeVerify(input);\n if (!verifyResult.ok) {\n /* v8 ignore next 4 -- defensive: both codecs share the same wire parse so ParseError\n is unreachable after the createTimestampId pre-validation above passes */\n if (verifyResult.error !== \"verification_failed\") {\n opts.stderr(verifyResult.error + \"\\n\");\n return 1;\n }\n opts.stdout(\n formatSignedInspectOutput({\n brand,\n timestamp: verifyTimestamp!,\n canonical: verifyCanonical!,\n input,\n nowMs: verifyNowMs!,\n verification: \"failed\",\n }),\n );\n opts.stderr(\"verification_failed: verification failed\\n\");\n return 1;\n }\n opts.stdout(\n formatSignedInspectOutput({\n brand,\n timestamp: verifyTimestamp!,\n canonical: verifyResult.id,\n input,\n nowMs: verifyNowMs!,\n verification: \"ok\",\n }),\n );\n return 0;\n }\n /* v8 ignore next 5 -- defensive: digestVariant is the only \"unsupported\" variant and it is\n excluded from inspectPolicy.selectable, so resolveVariant can never return it here. The\n branch exists for TypeScript exhaustiveness. */\n case \"unsupported\": {\n opts.stderr(\"unsupported flag for inspect: --digest\\n\");\n return 1;\n }\n }\n}\n","import { deriveAllowedFlags, resolveVariant } from \"../dispatch.js\";\nimport { parseBits, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, parseKeyFormatFromFlag } from \"../key-io.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { keygenPolicy } from \"../variants.js\";\n\nexport function runKeygen(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const allowedFlags = deriveAllowedFlags(keygenPolicy);\n const variantExtraFlags = new Set(keygenPolicy.selectable.flatMap((v) => v.extraFlags ?? []));\n const { flags, values, positionals, errors } = splitFlags(args, allowedFlags);\n const unsupported = unsupportedFlagForCommand(\n \"keygen\",\n flags,\n new Set([...allowedFlags].filter((f) => !variantExtraFlags.has(f))),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const extra = positionals[0];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const variant = resolveVariant(keygenPolicy, flags);\n if (typeof variant === \"string\") {\n opts.stderr(variant + \"\\n\");\n return Promise.resolve(1);\n }\n const bits = parseBits(values);\n if (typeof bits === \"string\") {\n opts.stderr(bits + \"\\n\");\n return Promise.resolve(1);\n }\n const format = parseKeyFormatFromFlag(values);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n /* v8 ignore next 4 -- defensive guard; all keygenPolicy variants have key defined */\n if (variant.key === undefined) {\n opts.stderr(\"internal: keygen policy variant has no key facet\\n\");\n return Promise.resolve(1);\n }\n const bytes = new Uint8Array(bits / 8);\n crypto.getRandomValues(bytes);\n opts.stdout(variant.key.encode(bytes, format) + \"\\n\");\n return Promise.resolve(0);\n}\n","import { runGenerate } from \"./commands/generate.js\";\nimport { runInspect } from \"./commands/inspect.js\";\nimport { runKeygen } from \"./commands/keygen.js\";\nimport type { CommandHandler, RunOpts } from \"./types.js\";\nimport { usage } from \"./usage.js\";\n\nexport type { RunOpts } from \"./types.js\";\n\ntype Command = {\n names: ReadonlyArray<string>;\n run: CommandHandler;\n};\n\nconst commands: ReadonlyArray<Command> = [\n { names: [\"generate\", \"g\"], run: runGenerate },\n { names: [\"inspect\", \"i\"], run: runInspect },\n { names: [\"keygen\", \"k\"], run: runKeygen },\n];\n\nexport async function run(opts: RunOpts): Promise<number> {\n const [subcommand, ...rest] = opts.argv;\n const command = commands.find((candidate) => candidate.names.includes(subcommand ?? \"\"));\n if (command !== undefined) return command.run(rest, opts);\n if (subcommand === undefined || subcommand === \"--help\" || subcommand === \"-h\") {\n opts.stdout(usage());\n return 0;\n }\n opts.stderr(usage());\n return 1;\n}\n","#!/usr/bin/env node\nimport { run } from \"../src/cli/index.js\";\n\nprocess.exitCode = await run({\n argv: process.argv.slice(2),\n stdout: (s) => process.stdout.write(s),\n stderr: (s) => process.stderr.write(s),\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/codecs/timestamp/index.ts"],"mappings":";;;;;;;KASY,gBAAA;EAAA,6EAEV,GAAA,iBAEe;EAAf,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;;;AAAA;AAeF;;;;;;KAAY,cAAA;uEAEV,QAAA,IAAY,EAAA,CAAG,KAAA;EAEf,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;EAK3B,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;;;EAIhC,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;;;;EAI1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;EAIvC,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,IAAA;EAEjC,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;EAE7B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;EAE7B,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;;;;;;;iBAiD5B,iBAAA,uBACd,KAAA,EAAO,KAAA,EACP,IAAA,GAAM,gBAAA,GACL,cAAA,CAAe,KAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/codecs/timestamp/index.ts"],"mappings":";;;;;;;KAUY,gBAAA;EAAA,6EAEV,GAAA,iBAEe;EAAf,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;;;AAAA;AAeF;;;;;;KAAY,cAAA;uEAEV,QAAA,IAAY,EAAA,CAAG,KAAA;EAEf,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;EAK3B,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;;;EAIhC,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;;;;EAI1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;EAIvC,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,IAAA;EAEjC,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;EAE7B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;EAE7B,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;;;;;;;iBAiB5B,iBAAA,uBACd,KAAA,EAAO,KAAA,EACP,IAAA,GAAM,gBAAA,GACL,cAAA,CAAe,KAAA"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { t as createTimestampId } from "./timestamp-DBwVjDkg.mjs";
2
+ import { t as createTimestampId } from "./timestamp-Cg9nRfnK.mjs";
3
3
  export { IdsError, createTimestampId, isIdsError };
@@ -1,7 +1,6 @@
1
1
  import { a as toWireId, i as payloadBytesFromId, n as registerBrand, r as payloadBase32Length, s as validateBrand, t as wireMethods } from "./codec-shell-DvrTDa65.mjs";
2
- import { r as writeTimestamp, t as readTimestampMs } from "./timestamp-bytes-DvhWHDa-.mjs";
2
+ import { a as writeTimestamp, r as readTimestampMs, t as defaultRng } from "./rng-Clos6uC0.mjs";
3
3
  import { i as encodeKeyMaterial, r as decodeKeyMaterial, t as assertValidKeyMaterialByteLength } from "./key-material-f29JIyrz.mjs";
4
- import { t as defaultRng } from "./rng-DHxioKyI.mjs";
5
4
  //#region src/codecs/opaque/layout.ts
6
5
  const zeroIv = /* @__PURE__ */ new Uint8Array(16);
7
6
  const pkcsPad = 16;
@@ -128,4 +127,4 @@ function createOpaqueTimestampId(brand, opts) {
128
127
  //#endregion
129
128
  export { importOpaqueKey as i, decodeOpaqueKey as n, encodeOpaqueKey as r, createOpaqueTimestampId as t };
130
129
 
131
- //# sourceMappingURL=opaque-ayT0KdCt.mjs.map
130
+ //# sourceMappingURL=opaque-BQVNoIIh.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"opaque-ayT0KdCt.mjs","names":[],"sources":["../src/codecs/opaque/layout.ts","../src/codecs/opaque/key.ts","../src/codecs/opaque/index.ts"],"sourcesContent":["import type { webcrypto } from \"node:crypto\";\nimport type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadBase32Length, payloadByteLength } from \"../../wire/invariants.js\";\nimport {\n readTimestampMs,\n timestampByteLength,\n writeTimestamp,\n} from \"../../wire/timestamp-bytes.js\";\n\nconst zeroIv = new Uint8Array(payloadByteLength);\nconst pkcsPad = 0x10;\n\nfunction buildPlaintext(ms: number, rng: (target: Uint8Array) => void): Uint8Array {\n const plaintext = new Uint8Array(payloadByteLength);\n writeTimestamp(ms, plaintext);\n rng(plaintext.subarray(timestampByteLength, payloadByteLength));\n return plaintext;\n}\n\nasync function encryptPayload(\n key: webcrypto.CryptoKey,\n plaintext: Uint8Array,\n): Promise<Uint8Array> {\n const encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n plaintext as Uint8Array<ArrayBuffer>,\n ),\n );\n return encrypted.subarray(0, payloadByteLength);\n}\n\n// AES-CBC strip-and-reconstruct decrypt (ADR-0004). The wire carries only C1\n// (16 bytes); C2 = AES_K(P2 XOR C1) where P2 is the PKCS#7 pad block (0x10×16).\n// Recompute C2 via CBC encrypt of (P2 XOR C1) with IV=0, then decrypt C1‖C2.\nasync function decryptPayload(key: webcrypto.CryptoKey, c1: Uint8Array): Promise<Uint8Array> {\n const c2Input = new Uint8Array(payloadByteLength);\n for (let i = 0; i < payloadByteLength; i++) c2Input[i] = pkcsPad ^ c1[i]!;\n const c2Encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n c2Input as Uint8Array<ArrayBuffer>,\n ),\n );\n const ciphertext = new Uint8Array(payloadByteLength * 2);\n ciphertext.set(c1, 0);\n ciphertext.set(c2Encrypted.subarray(0, payloadByteLength), payloadByteLength);\n return new Uint8Array(\n await crypto.subtle.decrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n ciphertext as Uint8Array<ArrayBuffer>,\n ),\n );\n}\n\nasync function extractTimestampFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n id: Id<Brand>,\n): Promise<Date> {\n const plaintext = await decryptPayload(key, payloadBytesFromId(prefix, id));\n return new Date(readTimestampMs(plaintext));\n}\n\n/** Produces a canonical encrypted wire ID. Per-call plaintext/ciphertext buffers —\n * subtle dominates this path; reuse would be safe but not worth pinning to spec detail. */\nasync function generateWireId<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n rng: (target: Uint8Array) => void,\n ms: number,\n): Promise<Id<Brand>> {\n const plaintext = buildPlaintext(ms, rng);\n const encrypted = await encryptPayload(key, plaintext);\n return toWireId(prefix, encrypted);\n}\n\n/** Structural placeholder for JSON Schema (encrypt is async). */\nfunction schemaExample<Brand extends string>(prefix: Prefix<Brand>): string {\n return prefix + \"0\".repeat(payloadBase32Length);\n}\n\n/** Layout ops binder for the Opaque Timestamp variant. `extractTimestampFromId` is module-private; the binder exposes `extractTimestamp` for the codec constructor. */\nexport function createOpaqueLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n rng: (target: Uint8Array) => void,\n) {\n return {\n generateAt: (ms: number): Promise<Id<Brand>> => generateWireId(prefix, key, rng, ms),\n extractTimestamp: (id: Id<Brand>): Promise<Date> => extractTimestampFromId(prefix, key, id),\n exampleWireId: (): Id<Brand> => schemaExample(prefix) as Id<Brand>,\n };\n}\n","import type { webcrypto } from \"node:crypto\";\nimport {\n assertValidKeyMaterialByteLength,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\n/** Wire encoding for opaque AES key material (not Crockford base32). */\nexport type OpaqueKeyFormat = \"hex\" | \"base64url\";\n\ndeclare const opaqueKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for one AES key used by the Opaque Timestamp codec.\n *\n * Holds the underlying `webcrypto.CryptoKey` internally; callers never access it directly.\n * Obtain handles via {@link importOpaqueKey} and pass them to\n * `createOpaqueTimestampId` as the `key` option.\n *\n * Distinct from the `WrappingKey` used by `@smonn/ids/wrapped` — one raw\n * secret must not silently serve both codecs without an explicit import.\n */\nexport type OpaqueKey = {\n readonly [opaqueKeyBrand]: \"OpaqueKey\";\n};\n\nconst opaqueKeyInternals = new WeakMap<OpaqueKey, webcrypto.CryptoKey>();\n\n/**\n * Imports raw AES key bytes into an {@link OpaqueKey} handle for the Opaque\n * Timestamp codec.\n *\n * Accepts 16, 24, or 32 bytes (AES-128 / AES-192 / AES-256 strength).\n * To store or transport key material, use {@link encodeOpaqueKey} /\n * {@link decodeOpaqueKey} (`\"hex\"` or `\"base64url\"` — not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 raw key bytes.\n */\nexport async function importOpaqueKey(bytes: Uint8Array): Promise<OpaqueKey> {\n assertValidKeyMaterialByteLength(bytes.length, \"AES\");\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"AES-CBC\",\n false,\n [\"encrypt\", \"decrypt\"],\n );\n const key = Object.freeze({}) as OpaqueKey;\n opaqueKeyInternals.set(key, cryptoKey);\n return key;\n}\n\nexport function getOpaqueKeyCryptoKey(key: OpaqueKey): webcrypto.CryptoKey {\n const cryptoKey = opaqueKeyInternals.get(key);\n if (cryptoKey === undefined) {\n throw new Error(\"invalid opaque key\");\n }\n return cryptoKey;\n}\n\n/**\n * Encodes raw AES key bytes for storage in env vars or secret managers.\n *\n * @param bytes - 16, 24, or 32 raw key bytes (AES-128/192/256).\n * @param format - `hex` (lowercase) or `base64url`.\n */\nexport function encodeOpaqueKey(bytes: Uint8Array, format: OpaqueKeyFormat): string {\n return encodeKeyMaterial(bytes, format, \"opaque\", \"AES\");\n}\n\n/**\n * Decodes key material emitted by `encodeOpaqueKey` (or `ids keygen`) back to raw bytes.\n *\n * @param encoded - Hex or base64url string.\n * @param format - Must match how the string was encoded.\n */\nexport function decodeOpaqueKey(encoded: string, format: OpaqueKeyFormat): Uint8Array {\n return decodeKeyMaterial(encoded, format, \"opaque\", \"AES\");\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { createOpaqueLayoutOps } from \"./layout.js\";\nimport { getOpaqueKeyCryptoKey, type OpaqueKey } from \"./key.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { defaultRng } from \"../_kernel/rng.js\";\nimport type { Id, JsonSchema, ParseResult, Prefix, StandardSchemaProps } from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nexport {\n decodeOpaqueKey,\n encodeOpaqueKey,\n importOpaqueKey,\n type OpaqueKey,\n type OpaqueKeyFormat,\n} from \"./key.js\";\n\n/**\n * Configuration options for an Opaque Timestamp codec instance.\n */\nexport type OpaqueTimestampOptions = {\n /**\n * {@link OpaqueKey} handle for AES-CBC encryption and decryption.\n * Obtain via {@link importOpaqueKey}.\n *\n * A single key, not a ring: rotation is forward-only and caller-tracked —\n * hold one codec per key epoch and select it from your own records. The\n * library cannot trial keys (the payload is unauthenticated). See ADR-0013.\n */\n key: OpaqueKey;\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes random bytes into `target` for ID generation. Defaults to `crypto.getRandomValues`. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * A brand-scoped codec for generating and validating Opaque Timestamp IDs.\n *\n * Same wire shape as the Timestamp codec (`{brand}_` + 26 base32 chars) but the\n * payload is AES-CBC encrypted. `generate`, `generateAt`, and `extractTimestamp`\n * are async; parsing methods are sync. No `minIdForTime` / `maxIdForTime` —\n * encrypted payloads do not sort by creation time.\n */\nexport type OpaqueTimestampCodec<Brand extends string> = {\n /** Produces a new canonical encrypted ID using the codec's `now` and `rng`. */\n generate(): Promise<Id<Brand>>;\n /** Produces a new canonical encrypted ID with timestamp bytes from `date`. Throws on invalid dates. */\n generateAt(date: Date): Promise<Id<Brand>>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decrypts and decodes the creation `Date` from an `Id<Brand>`. Trusts the type — use `safeParse()` at boundaries first. See ADR-0002.\n *\n * Requires the same key used at generation; a wrong key returns a plausible\n * but wrong `Date`, never an error. With rotation, select the codec for the\n * ID's key epoch from your own records — the library cannot. See ADR-0013.\n */\n extractTimestamp(id: Id<Brand>): Promise<Date>;\n /** JSON Schema for the canonical wire form (`example` is a structural placeholder). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/**\n * Creates an Opaque Timestamp codec for `brand` (three lowercase a–z characters).\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Required `key` (an {@link OpaqueKey} from {@link importOpaqueKey}) plus\n * optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n */\nexport function createOpaqueTimestampId<Brand extends string>(\n brand: Brand,\n opts: OpaqueTimestampOptions,\n): OpaqueTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const cryptoKey = getOpaqueKeyCryptoKey(opts.key);\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? defaultRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createOpaqueLayoutOps(prefix, cryptoKey, rng);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":";;;;;AAUA,MAAM,yBAAS,IAAI,WAAA,EAA4B;AAC/C,MAAM,UAAU;AAEhB,SAAS,eAAe,IAAY,KAA+C;CACjF,MAAM,4BAAY,IAAI,WAAA,EAA4B;CAClD,eAAe,IAAI,SAAS;CAC5B,IAAI,UAAU,SAAA,GAAA,EAA+C,CAAC;CAC9D,OAAO;AACT;AAEA,eAAe,eACb,KACA,WACqB;CAQrB,OAAO,IAPe,WACpB,MAAM,OAAO,OAAO,QAClB;EAAE,MAAM;EAAW,IAAI;CAAO,GAC9B,KACA,SACF,CAEa,CAAC,CAAC,SAAS,GAAA,EAAoB;AAChD;AAKA,eAAe,eAAe,KAA0B,IAAqC;CAC3F,MAAM,0BAAU,IAAI,WAAA,EAA4B;CAChD,KAAK,IAAI,IAAI,GAAG,IAAA,IAAuB,KAAK,QAAQ,KAAK,UAAU,GAAG;CACtE,MAAM,cAAc,IAAI,WACtB,MAAM,OAAO,OAAO,QAClB;EAAE,MAAM;EAAW,IAAI;CAAO,GAC9B,KACA,OACF,CACF;CACA,MAAM,6BAAa,IAAI,WAAA,EAAgC;CACvD,WAAW,IAAI,IAAI,CAAC;CACpB,WAAW,IAAI,YAAY,SAAS,GAAA,EAAoB,GAAA,EAAoB;CAC5E,OAAO,IAAI,WACT,MAAM,OAAO,OAAO,QAClB;EAAE,MAAM;EAAW,IAAI;CAAO,GAC9B,KACA,UACF,CACF;AACF;AAEA,eAAe,uBACb,QACA,KACA,IACe;CACf,MAAM,YAAY,MAAM,eAAe,KAAK,mBAAmB,QAAQ,EAAE,CAAC;CAC1E,OAAO,IAAI,KAAK,gBAAgB,SAAS,CAAC;AAC5C;;;AAIA,eAAe,eACb,QACA,KACA,KACA,IACoB;CAGpB,OAAO,SAAS,QAAQ,MADA,eAAe,KADrB,eAAe,IAAI,GACe,CAAC,CACpB;AACnC;;AAGA,SAAS,cAAoC,QAA+B;CAC1E,OAAO,SAAS,IAAI,OAAO,mBAAmB;AAChD;;AAGA,SAAgB,sBACd,QACA,KACA,KACA;CACA,OAAO;EACL,aAAa,OAAmC,eAAe,QAAQ,KAAK,KAAK,EAAE;EACnF,mBAAmB,OAAiC,uBAAuB,QAAQ,KAAK,EAAE;EAC1F,qBAAgC,cAAc,MAAM;CACtD;AACF;;;ACvEA,MAAM,qCAAqB,IAAI,QAAwC;;;;;;;;;;;AAYvE,eAAsB,gBAAgB,OAAuC;CAC3E,iCAAiC,MAAM,QAAQ,KAAK;CACpD,MAAM,YAAY,MAAM,OAAO,OAAO,UACpC,OACA,OACA,WACA,OACA,CAAC,WAAW,SAAS,CACvB;CACA,MAAM,MAAM,OAAO,OAAO,CAAC,CAAC;CAC5B,mBAAmB,IAAI,KAAK,SAAS;CACrC,OAAO;AACT;AAEA,SAAgB,sBAAsB,KAAqC;CACzE,MAAM,YAAY,mBAAmB,IAAI,GAAG;CAC5C,IAAI,cAAc,KAAA,GAChB,MAAM,IAAI,MAAM,oBAAoB;CAEtC,OAAO;AACT;;;;;;;AAQA,SAAgB,gBAAgB,OAAmB,QAAiC;CAClF,OAAO,kBAAkB,OAAO,QAAQ,UAAU,KAAK;AACzD;;;;;;;AAQA,SAAgB,gBAAgB,SAAiB,QAAqC;CACpF,OAAO,kBAAkB,SAAS,QAAQ,UAAU,KAAK;AAC3D;;;;;;;;;;ACQA,SAAgB,wBACd,OACA,MAC6B;CAC7B,cAAc,KAAK;CACnB,cAAc,OAAO,KAAK,mBAAmB;CAE7C,MAAM,YAAY,sBAAsB,KAAK,GAAG;CAChD,MAAM,MAAM,KAAK,OAAO,KAAK;CAC7B,MAAM,MAAM,KAAK,OAAO;CACxB,MAAM,SAAwB,GAAG,MAAM;CACvC,MAAM,OAAO,YAAY,MAAM;CAC/B,MAAM,SAAS,sBAAsB,QAAQ,WAAW,GAAG;CAE3D,OAAO;EACL,gBAAgB,OAAO,WAAW,IAAI,CAAC;EACvC,aAAa,SAAe,OAAO,WAAW,KAAK,QAAQ,CAAC;EAC5D,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,kBAAkB,OAAO;EACzB,oBAAoB,KAAK,aAAa,OAAO,OAAO,cAAc,CAAC;EACnE,aAAa,KAAK;CACpB;AACF"}
1
+ {"version":3,"file":"opaque-BQVNoIIh.mjs","names":[],"sources":["../src/codecs/opaque/layout.ts","../src/codecs/opaque/key.ts","../src/codecs/opaque/index.ts"],"sourcesContent":["import type { webcrypto } from \"node:crypto\";\nimport type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadBase32Length, payloadByteLength } from \"../../wire/invariants.js\";\nimport {\n readTimestampMs,\n timestampByteLength,\n writeTimestamp,\n} from \"../../wire/timestamp-bytes.js\";\n\nconst zeroIv = new Uint8Array(payloadByteLength);\nconst pkcsPad = 0x10;\n\nfunction buildPlaintext(ms: number, rng: (target: Uint8Array) => void): Uint8Array {\n const plaintext = new Uint8Array(payloadByteLength);\n writeTimestamp(ms, plaintext);\n rng(plaintext.subarray(timestampByteLength, payloadByteLength));\n return plaintext;\n}\n\nasync function encryptPayload(\n key: webcrypto.CryptoKey,\n plaintext: Uint8Array,\n): Promise<Uint8Array> {\n const encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n plaintext as Uint8Array<ArrayBuffer>,\n ),\n );\n return encrypted.subarray(0, payloadByteLength);\n}\n\n// AES-CBC strip-and-reconstruct decrypt (ADR-0004). The wire carries only C1\n// (16 bytes); C2 = AES_K(P2 XOR C1) where P2 is the PKCS#7 pad block (0x10×16).\n// Recompute C2 via CBC encrypt of (P2 XOR C1) with IV=0, then decrypt C1‖C2.\nasync function decryptPayload(key: webcrypto.CryptoKey, c1: Uint8Array): Promise<Uint8Array> {\n const c2Input = new Uint8Array(payloadByteLength);\n for (let i = 0; i < payloadByteLength; i++) c2Input[i] = pkcsPad ^ c1[i]!;\n const c2Encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n c2Input as Uint8Array<ArrayBuffer>,\n ),\n );\n const ciphertext = new Uint8Array(payloadByteLength * 2);\n ciphertext.set(c1, 0);\n ciphertext.set(c2Encrypted.subarray(0, payloadByteLength), payloadByteLength);\n return new Uint8Array(\n await crypto.subtle.decrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n ciphertext as Uint8Array<ArrayBuffer>,\n ),\n );\n}\n\nasync function extractTimestampFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n id: Id<Brand>,\n): Promise<Date> {\n const plaintext = await decryptPayload(key, payloadBytesFromId(prefix, id));\n return new Date(readTimestampMs(plaintext));\n}\n\n/** Produces a canonical encrypted wire ID. Per-call plaintext/ciphertext buffers —\n * subtle dominates this path; reuse would be safe but not worth pinning to spec detail. */\nasync function generateWireId<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n rng: (target: Uint8Array) => void,\n ms: number,\n): Promise<Id<Brand>> {\n const plaintext = buildPlaintext(ms, rng);\n const encrypted = await encryptPayload(key, plaintext);\n return toWireId(prefix, encrypted);\n}\n\n/** Structural placeholder for JSON Schema (encrypt is async). */\nfunction schemaExample<Brand extends string>(prefix: Prefix<Brand>): string {\n return prefix + \"0\".repeat(payloadBase32Length);\n}\n\n/** Layout ops binder for the Opaque Timestamp variant. `extractTimestampFromId` is module-private; the binder exposes `extractTimestamp` for the codec constructor. */\nexport function createOpaqueLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n rng: (target: Uint8Array) => void,\n) {\n return {\n generateAt: (ms: number): Promise<Id<Brand>> => generateWireId(prefix, key, rng, ms),\n extractTimestamp: (id: Id<Brand>): Promise<Date> => extractTimestampFromId(prefix, key, id),\n exampleWireId: (): Id<Brand> => schemaExample(prefix) as Id<Brand>,\n };\n}\n","import type { webcrypto } from \"node:crypto\";\nimport {\n assertValidKeyMaterialByteLength,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\n/** Wire encoding for opaque AES key material (not Crockford base32). */\nexport type OpaqueKeyFormat = \"hex\" | \"base64url\";\n\ndeclare const opaqueKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for one AES key used by the Opaque Timestamp codec.\n *\n * Holds the underlying `webcrypto.CryptoKey` internally; callers never access it directly.\n * Obtain handles via {@link importOpaqueKey} and pass them to\n * `createOpaqueTimestampId` as the `key` option.\n *\n * Distinct from the `WrappingKey` used by `@smonn/ids/wrapped` — one raw\n * secret must not silently serve both codecs without an explicit import.\n */\nexport type OpaqueKey = {\n readonly [opaqueKeyBrand]: \"OpaqueKey\";\n};\n\nconst opaqueKeyInternals = new WeakMap<OpaqueKey, webcrypto.CryptoKey>();\n\n/**\n * Imports raw AES key bytes into an {@link OpaqueKey} handle for the Opaque\n * Timestamp codec.\n *\n * Accepts 16, 24, or 32 bytes (AES-128 / AES-192 / AES-256 strength).\n * To store or transport key material, use {@link encodeOpaqueKey} /\n * {@link decodeOpaqueKey} (`\"hex\"` or `\"base64url\"` — not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 raw key bytes.\n */\nexport async function importOpaqueKey(bytes: Uint8Array): Promise<OpaqueKey> {\n assertValidKeyMaterialByteLength(bytes.length, \"AES\");\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"AES-CBC\",\n false,\n [\"encrypt\", \"decrypt\"],\n );\n const key = Object.freeze({}) as OpaqueKey;\n opaqueKeyInternals.set(key, cryptoKey);\n return key;\n}\n\nexport function getOpaqueKeyCryptoKey(key: OpaqueKey): webcrypto.CryptoKey {\n const cryptoKey = opaqueKeyInternals.get(key);\n if (cryptoKey === undefined) {\n throw new Error(\"invalid opaque key\");\n }\n return cryptoKey;\n}\n\n/**\n * Encodes raw AES key bytes for storage in env vars or secret managers.\n *\n * @param bytes - 16, 24, or 32 raw key bytes (AES-128/192/256).\n * @param format - `hex` (lowercase) or `base64url`.\n */\nexport function encodeOpaqueKey(bytes: Uint8Array, format: OpaqueKeyFormat): string {\n return encodeKeyMaterial(bytes, format, \"opaque\", \"AES\");\n}\n\n/**\n * Decodes key material emitted by `encodeOpaqueKey` (or `ids keygen`) back to raw bytes.\n *\n * @param encoded - Hex or base64url string.\n * @param format - Must match how the string was encoded.\n */\nexport function decodeOpaqueKey(encoded: string, format: OpaqueKeyFormat): Uint8Array {\n return decodeKeyMaterial(encoded, format, \"opaque\", \"AES\");\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { createOpaqueLayoutOps } from \"./layout.js\";\nimport { getOpaqueKeyCryptoKey, type OpaqueKey } from \"./key.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { defaultRng } from \"../_kernel/rng.js\";\nimport type { Id, JsonSchema, ParseResult, Prefix, StandardSchemaProps } from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nexport {\n decodeOpaqueKey,\n encodeOpaqueKey,\n importOpaqueKey,\n type OpaqueKey,\n type OpaqueKeyFormat,\n} from \"./key.js\";\n\n/**\n * Configuration options for an Opaque Timestamp codec instance.\n */\nexport type OpaqueTimestampOptions = {\n /**\n * {@link OpaqueKey} handle for AES-CBC encryption and decryption.\n * Obtain via {@link importOpaqueKey}.\n *\n * A single key, not a ring: rotation is forward-only and caller-tracked —\n * hold one codec per key epoch and select it from your own records. The\n * library cannot trial keys (the payload is unauthenticated). See ADR-0013.\n */\n key: OpaqueKey;\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes random bytes into `target` for ID generation. Defaults to `crypto.getRandomValues`. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * A brand-scoped codec for generating and validating Opaque Timestamp IDs.\n *\n * Same wire shape as the Timestamp codec (`{brand}_` + 26 base32 chars) but the\n * payload is AES-CBC encrypted. `generate`, `generateAt`, and `extractTimestamp`\n * are async; parsing methods are sync. No `minIdForTime` / `maxIdForTime` —\n * encrypted payloads do not sort by creation time.\n */\nexport type OpaqueTimestampCodec<Brand extends string> = {\n /** Produces a new canonical encrypted ID using the codec's `now` and `rng`. */\n generate(): Promise<Id<Brand>>;\n /** Produces a new canonical encrypted ID with timestamp bytes from `date`. Throws on invalid dates. */\n generateAt(date: Date): Promise<Id<Brand>>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decrypts and decodes the creation `Date` from an `Id<Brand>`. Trusts the type — use `safeParse()` at boundaries first. See ADR-0002.\n *\n * Requires the same key used at generation; a wrong key returns a plausible\n * but wrong `Date`, never an error. With rotation, select the codec for the\n * ID's key epoch from your own records — the library cannot. See ADR-0013.\n */\n extractTimestamp(id: Id<Brand>): Promise<Date>;\n /** JSON Schema for the canonical wire form (`example` is a structural placeholder). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/**\n * Creates an Opaque Timestamp codec for `brand` (three lowercase a–z characters).\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Required `key` (an {@link OpaqueKey} from {@link importOpaqueKey}) plus\n * optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n */\nexport function createOpaqueTimestampId<Brand extends string>(\n brand: Brand,\n opts: OpaqueTimestampOptions,\n): OpaqueTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const cryptoKey = getOpaqueKeyCryptoKey(opts.key);\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? defaultRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createOpaqueLayoutOps(prefix, cryptoKey, rng);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":"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"}
package/dist/opaque.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-ayT0KdCt.mjs";
2
+ import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-BQVNoIIh.mjs";
3
3
  export { IdsError, createOpaqueTimestampId, decodeOpaqueKey, encodeOpaqueKey, importOpaqueKey, isIdsError };
@@ -1,6 +1,5 @@
1
1
  import { a as toWireId, i as payloadBytesFromId, n as registerBrand, s as validateBrand, t as wireMethods } from "./codec-shell-DvrTDa65.mjs";
2
- import { r as writeTimestamp } from "./timestamp-bytes-DvhWHDa-.mjs";
3
- import { t as defaultRng } from "./rng-DHxioKyI.mjs";
2
+ import { a as writeTimestamp, n as fastTenByteRng } from "./rng-Clos6uC0.mjs";
4
3
  //#region src/codecs/reverse/layout.ts
5
4
  const randomByteLength = 10;
6
5
  /** Writes inverted timestamp bytes, then fills random portion. */
@@ -62,7 +61,7 @@ function createReverseTimestampId(brand, opts = {}) {
62
61
  validateBrand(brand);
63
62
  registerBrand(brand, opts.allowDuplicateBrand);
64
63
  const now = opts.now ?? Date.now;
65
- const rng = opts.rng ?? defaultRng;
64
+ const rng = opts.rng ?? fastTenByteRng;
66
65
  const prefix = `${brand}_`;
67
66
  const wire = wireMethods(prefix);
68
67
  const layout = createReverseTimestampLayoutOps(prefix, rng);
@@ -82,4 +81,4 @@ function createReverseTimestampId(brand, opts = {}) {
82
81
  //#endregion
83
82
  export { createReverseTimestampId as t };
84
83
 
85
- //# sourceMappingURL=reverse-BRZRc1_U.mjs.map
84
+ //# sourceMappingURL=reverse-DsPd7Lco.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverse-DsPd7Lco.mjs","names":[],"sources":["../src/codecs/reverse/layout.ts","../src/codecs/reverse/index.ts"],"sourcesContent":["import type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\nimport { timestampByteLength, writeTimestamp } from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength: number = payloadByteLength - timestampByteLength;\n\n/** Writes inverted timestamp bytes, then fills random portion. */\nfunction buildReversePayload(\n ms: number,\n rng: (target: Uint8Array) => void,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n rng(randomView);\n}\n\n/** Writes inverted timestamp bytes, then fills random portion with a sentinel. */\nfunction buildReverseSentinelPayload(\n ms: number,\n fill: number,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n randomView.fill(fill);\n}\n\n/** Decodes the original timestamp by inverting the first 6 payload bytes. */\nfunction extractReverseTimestampFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n id: Id<Brand>,\n): Date {\n const bytes = payloadBytesFromId(prefix, id);\n let ms = 0;\n for (let i = 0; i < timestampByteLength; i++) {\n ms = ms * 256 + (~bytes[i]! & 0xff);\n }\n return new Date(ms);\n}\n\n/** Layout ops binder for the Reverse Timestamp variant. */\nexport function createReverseTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n rng: (target: Uint8Array) => void,\n) {\n const buffer = new Uint8Array(payloadByteLength);\n const randomView = new Uint8Array(buffer.buffer, timestampByteLength, randomByteLength);\n\n return {\n generateAt: (ms: number): Id<Brand> => {\n buildReversePayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n extractTimestamp: (id: Id<Brand>): Date => extractReverseTimestampFromId(prefix, id),\n minIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0x00, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0xff, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n exampleWireId: (ms: number): Id<Brand> => {\n buildReversePayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n };\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nimport { createReverseTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { fastTenByteRng } from \"../_kernel/rng.js\";\nimport type { Id, JsonSchema, ParseResult, Prefix, StandardSchemaProps } from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode };\n\n/**\n * Configuration options for a Reverse Timestamp codec instance.\n */\nexport type ReverseTimestampOptions = {\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes the 10-byte random tail into `target`. Defaults to a `crypto.randomUUID` harvest fast path (same as the Timestamp codec). */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * A brand-scoped codec for generating and validating Reverse Timestamp IDs.\n *\n * Wire format: `{brand}_` plus 26 lowercase Crockford base32 characters encoding a\n * 16-byte payload (6-byte bitwise-inverted ms timestamp + 10 random bytes). IDs sort\n * by creation time in **descending** (newest-first) order.\n *\n * Range queries across a time interval [t_old, t_new] should scan from\n * `minIdForTime(t_new)` to `maxIdForTime(t_old)` — the reversed sort order means\n * newer timestamps produce lexicographically smaller IDs.\n *\n * Constructed via `createReverseTimestampId(brand)` from `@smonn/ids/reverse`.\n */\nexport type ReverseTimestampCodec<Brand extends string> = {\n /** Produces a new canonical ID using the codec's `now` and `rng`. */\n generate(): Id<Brand>;\n /** Produces a new canonical ID with timestamp bytes from `date` and a fresh random tail. Throws on invalid dates. */\n generateAt(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>` by inverting the timestamp bytes.\n * Trusts the type — use `safeParse()` at boundaries first.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /**\n * Lexicographically smallest ID for any ID generated at `date` (random portion `0x00`).\n * Because timestamps are inverted, a newer `date` yields a lexicographically smaller result —\n * use `minIdForTime(t_new)` as the lower bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n minIdForTime(date: Date): Id<Brand>;\n /**\n * Lexicographically largest ID for any ID generated at `date` (random portion `0xff`).\n * Because timestamps are inverted, an older `date` yields a lexicographically larger result —\n * use `maxIdForTime(t_old)` as the upper bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n maxIdForTime(date: Date): Id<Brand>;\n /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/**\n * Creates a Reverse Timestamp codec for `brand` (three lowercase a–z characters).\n *\n * IDs sort newest-first: the 48-bit timestamp field is bitwise-inverted before encoding,\n * so lexicographic ID order equals descending creation-time order. `extractTimestamp`\n * inverts back to recover the original millisecond.\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n */\nexport function createReverseTimestampId<Brand extends string>(\n brand: Brand,\n opts: ReverseTimestampOptions = {},\n): ReverseTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? fastTenByteRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createReverseTimestampLayoutOps(prefix, rng);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId(now())),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":";;;AAKA,MAAM,mBAAA;;AAGN,SAAS,oBACP,IACA,KACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,IAAI,UAAU;AAChB;;AAGA,SAAS,4BACP,IACA,MACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,WAAW,KAAK,IAAI;AACtB;;AAGA,SAAS,8BACP,QACA,IACM;CACN,MAAM,QAAQ,mBAAmB,QAAQ,EAAE;CAC3C,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,KAAK,KAAK,OAAO,CAAC,MAAM,KAAM;CAEhC,OAAO,IAAI,KAAK,EAAE;AACpB;;AAGA,SAAgB,gCACd,QACA,KACA;CACA,MAAM,yBAAS,IAAI,WAAA,EAA4B;CAC/C,MAAM,aAAa,IAAI,WAAW,OAAO,QAAA,GAA6B,gBAAgB;CAEtF,OAAO;EACL,aAAa,OAA0B;GACrC,oBAAoB,IAAI,KAAK,QAAQ,UAAU;GAC/C,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,mBAAmB,OAAwB,8BAA8B,QAAQ,EAAE;EACnF,eAAe,OAA0B;GACvC,4BAA4B,IAAI,GAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,eAAe,OAA0B;GACvC,4BAA4B,IAAI,KAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,gBAAgB,OAA0B;GACxC,oBAAoB,IAAI,KAAK,QAAQ,UAAU;GAC/C,OAAO,SAAS,QAAQ,MAAM;EAChC;CACF;AACF;;;;;;;;;;;;;ACcA,SAAgB,yBACd,OACA,OAAgC,CAAC,GACH;CAC9B,cAAc,KAAK;CACnB,cAAc,OAAO,KAAK,mBAAmB;CAE7C,MAAM,MAAM,KAAK,OAAO,KAAK;CAC7B,MAAM,MAAM,KAAK,OAAO;CACxB,MAAM,SAAwB,GAAG,MAAM;CACvC,MAAM,OAAO,YAAY,MAAM;CAC/B,MAAM,SAAS,gCAAgC,QAAQ,GAAG;CAE1D,OAAO;EACL,gBAAgB,OAAO,WAAW,IAAI,CAAC;EACvC,aAAa,SAAe,OAAO,WAAW,KAAK,QAAQ,CAAC;EAC5D,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,kBAAkB,OAAO;EACzB,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,oBAAoB,KAAK,aAAa,OAAO,OAAO,cAAc,IAAI,CAAC,CAAC;EACxE,aAAa,KAAK;CACpB;AACF"}
@@ -6,7 +6,7 @@ import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, t as Id }
6
6
  * Configuration options for a Reverse Timestamp codec instance.
7
7
  */
8
8
  type ReverseTimestampOptions = {
9
- /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */now?: () => number; /** Writes random bytes into `target` for ID generation. Defaults to `crypto.getRandomValues`. */
9
+ /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */now?: () => number; /** Writes the 10-byte random tail into `target`. Defaults to a `crypto.randomUUID` harvest fast path (same as the Timestamp codec). */
10
10
  rng?: (target: Uint8Array) => void; /** If true, silences the duplicate-brand warning in non-production environments. */
11
11
  allowDuplicateBrand?: boolean;
12
12
  };
package/dist/reverse.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { t as createReverseTimestampId } from "./reverse-BRZRc1_U.mjs";
2
+ import { t as createReverseTimestampId } from "./reverse-DsPd7Lco.mjs";
3
3
  export { IdsError, createReverseTimestampId, isIdsError };
@@ -0,0 +1,63 @@
1
+ import { o as decodeBase32 } from "./codec-shell-DvrTDa65.mjs";
2
+ const timestampBase32Length = Math.ceil(48 / 5);
3
+ /** Write the timestamp in big-endian; encoded via mod-256 to avoid 32-bit bitwise coercion. */
4
+ function writeTimestamp(ms, buffer) {
5
+ if (Number.isNaN(ms)) throw new Error("timestamp is not a number");
6
+ if (!Number.isInteger(ms)) throw new Error("timestamp is not an integer");
7
+ if (ms < 0) throw new Error("timestamp is negative");
8
+ if (ms >= 2 ** 48) throw new Error("timestamp exceeds 48-bit range");
9
+ for (let i = 5; i >= 0; i--) {
10
+ buffer[i] = ms % 256;
11
+ ms = Math.floor(ms / 256);
12
+ }
13
+ }
14
+ /** Decode the first `timestampByteLength` bytes of a buffer as a big-endian unsigned millisecond timestamp. */
15
+ function readTimestampMs(buffer) {
16
+ let ms = 0;
17
+ for (let i = 0; i < 6; i++) ms = ms * 256 + buffer[i];
18
+ return ms;
19
+ }
20
+ /** Decodes ms from the first 10 base32 chars of a payload suffix (partial decode). */
21
+ function readTimestampMsFromBase32Suffix(base32Suffix) {
22
+ return readTimestampMs(decodeBase32(base32Suffix.slice(0, timestampBase32Length)));
23
+ }
24
+ //#endregion
25
+ //#region src/codecs/_kernel/rng.ts
26
+ /** Default RNG: writes cryptographically random bytes via `crypto.getRandomValues`. */
27
+ function defaultRng(target) {
28
+ crypto.getRandomValues(target);
29
+ }
30
+ const hexCharCodeToNibble = /* @__PURE__ */ new Uint8Array(128);
31
+ for (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;
32
+ for (let i = 0; i < 6; i++) hexCharCodeToNibble[97 + i] = 10 + i;
33
+ /**
34
+ * Fast RNG for the 10-byte random tail shared by the plaintext timestamp layouts
35
+ * (Timestamp and Reverse Timestamp codecs). Writes exactly `target[0..9]`.
36
+ *
37
+ * `crypto.randomUUID()` is ~7× faster than `crypto.getRandomValues` in Node 24
38
+ * (~84 ns vs ~610 ns for a 16-byte fill — the UUID path has a tight fixed-format
39
+ * fast path). A UUIDv4 string carries 122 cryptographically-random bits; we
40
+ * harvest 10 fully-random bytes from positions where no version (hex 12) or
41
+ * variant (hex 16) bits sit. String layout: `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`
42
+ * — bytes 0–5 are string[0..7]+string[9..12], bytes 6–9 are string[24..31].
43
+ *
44
+ * Security-equivalent to `defaultRng` for a 10-byte tail (both are CSPRNG-backed,
45
+ * fully-random bytes); the only difference is throughput.
46
+ */
47
+ function fastTenByteRng(target) {
48
+ const s = crypto.randomUUID();
49
+ target[0] = hexCharCodeToNibble[s.charCodeAt(0)] << 4 | hexCharCodeToNibble[s.charCodeAt(1)];
50
+ target[1] = hexCharCodeToNibble[s.charCodeAt(2)] << 4 | hexCharCodeToNibble[s.charCodeAt(3)];
51
+ target[2] = hexCharCodeToNibble[s.charCodeAt(4)] << 4 | hexCharCodeToNibble[s.charCodeAt(5)];
52
+ target[3] = hexCharCodeToNibble[s.charCodeAt(6)] << 4 | hexCharCodeToNibble[s.charCodeAt(7)];
53
+ target[4] = hexCharCodeToNibble[s.charCodeAt(9)] << 4 | hexCharCodeToNibble[s.charCodeAt(10)];
54
+ target[5] = hexCharCodeToNibble[s.charCodeAt(11)] << 4 | hexCharCodeToNibble[s.charCodeAt(12)];
55
+ target[6] = hexCharCodeToNibble[s.charCodeAt(24)] << 4 | hexCharCodeToNibble[s.charCodeAt(25)];
56
+ target[7] = hexCharCodeToNibble[s.charCodeAt(26)] << 4 | hexCharCodeToNibble[s.charCodeAt(27)];
57
+ target[8] = hexCharCodeToNibble[s.charCodeAt(28)] << 4 | hexCharCodeToNibble[s.charCodeAt(29)];
58
+ target[9] = hexCharCodeToNibble[s.charCodeAt(30)] << 4 | hexCharCodeToNibble[s.charCodeAt(31)];
59
+ }
60
+ //#endregion
61
+ export { writeTimestamp as a, readTimestampMsFromBase32Suffix as i, fastTenByteRng as n, readTimestampMs as r, defaultRng as t };
62
+
63
+ //# sourceMappingURL=rng-Clos6uC0.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rng-Clos6uC0.mjs","names":[],"sources":["../src/wire/timestamp-bytes.ts","../src/codecs/_kernel/rng.ts"],"sourcesContent":["import { decodeBase32 } from \"./base32.js\";\n\n// Timestamp byte layout: first N bytes of the plaintext payload encode a\n// big-endian Unix-ms timestamp. Shared by timestamp-family layouts.\nexport const timestampByteLength: number = 6;\n\nconst timestampBase32Length: number = Math.ceil((timestampByteLength * 8) / 5);\n\n/** Write the timestamp in big-endian; encoded via mod-256 to avoid 32-bit bitwise coercion. */\nexport function writeTimestamp(ms: number, buffer: Uint8Array): void {\n if (Number.isNaN(ms)) throw new Error(\"timestamp is not a number\");\n if (!Number.isInteger(ms)) throw new Error(\"timestamp is not an integer\");\n if (ms < 0) throw new Error(\"timestamp is negative\");\n if (ms >= 2 ** (timestampByteLength * 8)) {\n throw new Error(\"timestamp exceeds 48-bit range\");\n }\n for (let i = timestampByteLength - 1; i >= 0; i--) {\n buffer[i] = ms % 256;\n ms = Math.floor(ms / 256);\n }\n}\n\n/** Decode the first `timestampByteLength` bytes of a buffer as a big-endian unsigned millisecond timestamp. */\nexport function readTimestampMs(buffer: Uint8Array): number {\n let ms = 0;\n for (let i = 0; i < timestampByteLength; i++) ms = ms * 256 + buffer[i]!;\n return ms;\n}\n\n/** Decodes ms from the first 10 base32 chars of a payload suffix (partial decode). */\nexport function readTimestampMsFromBase32Suffix(base32Suffix: string): number {\n return readTimestampMs(decodeBase32(base32Suffix.slice(0, timestampBase32Length)));\n}\n","/** Default RNG: writes cryptographically random bytes via `crypto.getRandomValues`. */\nexport function defaultRng(target: Uint8Array): void {\n crypto.getRandomValues(target as Uint8Array<ArrayBuffer>);\n}\n\n// hex charCode → 0–15 nibble, for harvesting bytes out of a UUIDv4 string.\n// Covers ['0'-'9' = 48–57] and ['a'-'f' = 97–102]; randomUUID is lowercase per spec.\nconst hexCharCodeToNibble = new Uint8Array(128);\nfor (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;\nfor (let i = 0; i < 6; i++) hexCharCodeToNibble[97 + i] = 10 + i;\n\n/**\n * Fast RNG for the 10-byte random tail shared by the plaintext timestamp layouts\n * (Timestamp and Reverse Timestamp codecs). Writes exactly `target[0..9]`.\n *\n * `crypto.randomUUID()` is ~7× faster than `crypto.getRandomValues` in Node 24\n * (~84 ns vs ~610 ns for a 16-byte fill — the UUID path has a tight fixed-format\n * fast path). A UUIDv4 string carries 122 cryptographically-random bits; we\n * harvest 10 fully-random bytes from positions where no version (hex 12) or\n * variant (hex 16) bits sit. String layout: `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`\n * — bytes 0–5 are string[0..7]+string[9..12], bytes 6–9 are string[24..31].\n *\n * Security-equivalent to `defaultRng` for a 10-byte tail (both are CSPRNG-backed,\n * fully-random bytes); the only difference is throughput.\n */\nexport function fastTenByteRng(target: Uint8Array): void {\n const s = crypto.randomUUID();\n target[0] = (hexCharCodeToNibble[s.charCodeAt(0)]! << 4) | hexCharCodeToNibble[s.charCodeAt(1)]!;\n target[1] = (hexCharCodeToNibble[s.charCodeAt(2)]! << 4) | hexCharCodeToNibble[s.charCodeAt(3)]!;\n target[2] = (hexCharCodeToNibble[s.charCodeAt(4)]! << 4) | hexCharCodeToNibble[s.charCodeAt(5)]!;\n target[3] = (hexCharCodeToNibble[s.charCodeAt(6)]! << 4) | hexCharCodeToNibble[s.charCodeAt(7)]!;\n target[4] = (hexCharCodeToNibble[s.charCodeAt(9)]! << 4) | hexCharCodeToNibble[s.charCodeAt(10)]!;\n target[5] =\n (hexCharCodeToNibble[s.charCodeAt(11)]! << 4) | hexCharCodeToNibble[s.charCodeAt(12)]!;\n target[6] =\n (hexCharCodeToNibble[s.charCodeAt(24)]! << 4) | hexCharCodeToNibble[s.charCodeAt(25)]!;\n target[7] =\n (hexCharCodeToNibble[s.charCodeAt(26)]! << 4) | hexCharCodeToNibble[s.charCodeAt(27)]!;\n target[8] =\n (hexCharCodeToNibble[s.charCodeAt(28)]! << 4) | hexCharCodeToNibble[s.charCodeAt(29)]!;\n target[9] =\n (hexCharCodeToNibble[s.charCodeAt(30)]! << 4) | hexCharCodeToNibble[s.charCodeAt(31)]!;\n}\n"],"mappings":";AAMA,MAAM,wBAAgC,KAAK,KAAA,KAAiC,CAAC;;AAG7E,SAAgB,eAAe,IAAY,QAA0B;CACnE,IAAI,OAAO,MAAM,EAAE,GAAG,MAAM,IAAI,MAAM,2BAA2B;CACjE,IAAI,CAAC,OAAO,UAAU,EAAE,GAAG,MAAM,IAAI,MAAM,6BAA6B;CACxE,IAAI,KAAK,GAAG,MAAM,IAAI,MAAM,uBAAuB;CACnD,IAAI,MAAM,KAAA,IACR,MAAM,IAAI,MAAM,gCAAgC;CAElD,KAAK,IAAI,IAAA,GAA6B,KAAK,GAAG,KAAK;EACjD,OAAO,KAAK,KAAK;EACjB,KAAK,KAAK,MAAM,KAAK,GAAG;CAC1B;AACF;;AAGA,SAAgB,gBAAgB,QAA4B;CAC1D,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KAAK,KAAK,KAAK,MAAM,OAAO;CACrE,OAAO;AACT;;AAGA,SAAgB,gCAAgC,cAA8B;CAC5E,OAAO,gBAAgB,aAAa,aAAa,MAAM,GAAG,qBAAqB,CAAC,CAAC;AACnF;;;;AC/BA,SAAgB,WAAW,QAA0B;CACnD,OAAO,gBAAgB,MAAiC;AAC1D;AAIA,MAAM,sCAAsB,IAAI,WAAW,GAAG;AAC9C,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,oBAAoB,KAAK,KAAK;AAC3D,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,oBAAoB,KAAK,KAAK,KAAK;;;;;;;;;;;;;;;AAgB/D,SAAgB,eAAe,QAA0B;CACvD,MAAM,IAAI,OAAO,WAAW;CAC5B,OAAO,KAAM,oBAAoB,EAAE,WAAW,CAAC,MAAO,IAAK,oBAAoB,EAAE,WAAW,CAAC;CAC7F,OAAO,KAAM,oBAAoB,EAAE,WAAW,CAAC,MAAO,IAAK,oBAAoB,EAAE,WAAW,CAAC;CAC7F,OAAO,KAAM,oBAAoB,EAAE,WAAW,CAAC,MAAO,IAAK,oBAAoB,EAAE,WAAW,CAAC;CAC7F,OAAO,KAAM,oBAAoB,EAAE,WAAW,CAAC,MAAO,IAAK,oBAAoB,EAAE,WAAW,CAAC;CAC7F,OAAO,KAAM,oBAAoB,EAAE,WAAW,CAAC,MAAO,IAAK,oBAAoB,EAAE,WAAW,EAAE;CAC9F,OAAO,KACJ,oBAAoB,EAAE,WAAW,EAAE,MAAO,IAAK,oBAAoB,EAAE,WAAW,EAAE;CACrF,OAAO,KACJ,oBAAoB,EAAE,WAAW,EAAE,MAAO,IAAK,oBAAoB,EAAE,WAAW,EAAE;CACrF,OAAO,KACJ,oBAAoB,EAAE,WAAW,EAAE,MAAO,IAAK,oBAAoB,EAAE,WAAW,EAAE;CACrF,OAAO,KACJ,oBAAoB,EAAE,WAAW,EAAE,MAAO,IAAK,oBAAoB,EAAE,WAAW,EAAE;CACrF,OAAO,KACJ,oBAAoB,EAAE,WAAW,EAAE,MAAO,IAAK,oBAAoB,EAAE,WAAW,EAAE;AACvF"}
@@ -1,8 +1,7 @@
1
1
  import { t as IdsError } from "./error-Cp5qYZcv.mjs";
2
2
  import { a as toWireId, i as payloadBytesFromId, n as registerBrand, r as payloadBase32Length, s as validateBrand, t as wireMethods } from "./codec-shell-DvrTDa65.mjs";
3
- import { n as readTimestampMsFromBase32Suffix, r as writeTimestamp } from "./timestamp-bytes-DvhWHDa-.mjs";
3
+ import { a as writeTimestamp, i as readTimestampMsFromBase32Suffix, t as defaultRng } from "./rng-Clos6uC0.mjs";
4
4
  import { i as encodeKeyMaterial, n as assertValidKeyring, r as decodeKeyMaterial, t as assertValidKeyMaterialByteLength } from "./key-material-f29JIyrz.mjs";
5
- import { t as defaultRng } from "./rng-DHxioKyI.mjs";
6
5
  const tagByteLength = 5;
7
6
  const randomOffset = 6;
8
7
  const tagOffset = 11;
@@ -204,4 +203,4 @@ function createSignedTimestampId(brand, opts) {
204
203
  //#endregion
205
204
  export { importSigningKey as i, decodeSigningKey as n, encodeSigningKey as r, createSignedTimestampId as t };
206
205
 
207
- //# sourceMappingURL=signed-C8OMt3TJ.mjs.map
206
+ //# sourceMappingURL=signed-4h2BnlWx.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"signed-C8OMt3TJ.mjs","names":[],"sources":["../src/codecs/signed/layout.ts","../src/codecs/signed/key.ts","../src/codecs/signed/index.ts"],"sourcesContent":["import type { webcrypto } from \"node:crypto\";\nimport type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadBase32Length, payloadByteLength } from \"../../wire/invariants.js\";\nimport {\n readTimestampMsFromBase32Suffix,\n timestampByteLength,\n writeTimestamp,\n} from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength = 5;\nconst tagByteLength = 5;\nconst randomOffset = timestampByteLength; // 6\nconst tagOffset = randomOffset + randomByteLength; // 11\nconst signedContentByteLength = randomOffset + randomByteLength; // 11 (ts6 ‖ rand5)\n\nasync function computeTag(\n hmacKey: webcrypto.CryptoKey,\n brandBytes: Uint8Array,\n signedContent: Uint8Array,\n): Promise<Uint8Array> {\n const message = new Uint8Array(brandBytes.length + signedContent.length);\n message.set(brandBytes, 0);\n message.set(signedContent, brandBytes.length);\n const signature = new Uint8Array(\n await crypto.subtle.sign(\"HMAC\", hmacKey, message as Uint8Array<ArrayBuffer>),\n );\n return signature.subarray(0, tagByteLength);\n}\n\nfunction tagsEqual(a: Uint8Array, b: Uint8Array): boolean {\n /* v8 ignore next -- defensive guard; both call sites always pass tagByteLength-byte arrays */\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i]! ^ b[i]!;\n return diff === 0;\n}\n\nexport function createSignedTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n brand: Brand,\n rng: (target: Uint8Array) => void,\n hmacKeys: readonly webcrypto.CryptoKey[],\n) {\n const signKey = hmacKeys[0]!;\n const brandBytes = new TextEncoder().encode(brand);\n const syncBuffer = new Uint8Array(payloadByteLength);\n\n return {\n generateAt: async (ms: number): Promise<Id<Brand>> => {\n const buffer = new Uint8Array(payloadByteLength);\n writeTimestamp(ms, buffer);\n rng(buffer.subarray(randomOffset, tagOffset));\n const tag = await computeTag(\n signKey,\n brandBytes,\n buffer.subarray(0, signedContentByteLength),\n );\n buffer.set(tag, tagOffset);\n return toWireId(prefix, buffer);\n },\n tryVerify: async (id: Id<Brand>): Promise<boolean> => {\n const payload = payloadBytesFromId(prefix, id);\n const storedTag = payload.subarray(tagOffset, payloadByteLength);\n const signedContent = payload.subarray(0, signedContentByteLength);\n for (const hmacKey of hmacKeys) {\n const expected = await computeTag(hmacKey, brandBytes, signedContent);\n if (tagsEqual(storedTag, expected)) return true;\n }\n return false;\n },\n extractTimestamp: (id: Id<Brand>): Date =>\n new Date(readTimestampMsFromBase32Suffix(id.slice(prefix.length))),\n minIdForTime: (ms: number): Id<Brand> => {\n writeTimestamp(ms, syncBuffer);\n syncBuffer.fill(0x00, randomOffset, payloadByteLength);\n return toWireId(prefix, syncBuffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n writeTimestamp(ms, syncBuffer);\n syncBuffer.fill(0xff, randomOffset, payloadByteLength);\n return toWireId(prefix, syncBuffer);\n },\n exampleWireId: (): Id<Brand> => (prefix + \"0\".repeat(payloadBase32Length)) as Id<Brand>,\n };\n}\n","import type { webcrypto } from \"node:crypto\";\nimport {\n assertValidKeyMaterialByteLength,\n assertValidKeyring,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\nexport { assertValidKeyring };\n\n/** Wire encoding for signing key raw key bytes (not Crockford base32). */\nexport type SigningKeyFormat = \"hex\" | \"base64url\";\n\nconst hmacInfo = new TextEncoder().encode(\"@smonn/ids/signed/hmac\");\n\nconst SHA256_DIGEST_BYTES = 32;\n\ndeclare const signingKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for one operator signing key.\n *\n * Holds a single HMAC-SHA-256 key derived via HKDF under the domain-separation\n * label `@smonn/ids/signed/hmac`. The underlying `webcrypto.CryptoKey` is held\n * internally and never exposed to callers. Obtain handles via\n * {@link importSigningKey} and pass them to `createSignedTimestampId` as the\n * `keys` signing keyring.\n *\n * Distinct from both the **Opaque key** and the **Wrapping key** — the same\n * raw key material must not silently serve multiple codecs without an explicit import.\n */\nexport type SigningKey = {\n readonly [signingKeyBrand]: \"SigningKey\";\n};\n\ntype SigningKeyInternals = {\n keyDigest: Uint8Array;\n hmacKey: webcrypto.CryptoKey;\n};\n\nconst internals = new WeakMap<SigningKey, SigningKeyInternals>();\n\n/**\n * Import raw operator key material into a {@link SigningKey} handle.\n *\n * Derives a single HMAC-SHA-256 key via HKDF under the domain-separation label\n * `@smonn/ids/signed/hmac`. Accepts 16, 24, or 32 bytes. To store or\n * transport key material, use {@link encodeSigningKey} / {@link decodeSigningKey}\n * (`\"hex\"` or `\"base64url\"` — not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 raw key bytes.\n * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.\n */\nexport async function importSigningKey(bytes: Uint8Array): Promise<SigningKey> {\n assertValidKeyMaterialByteLength(bytes.length, \"signing\");\n const [hmacKey, digestBuffer] = await Promise.all([\n deriveHmacKey(bytes),\n crypto.subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>),\n ]);\n const key = Object.freeze({}) as SigningKey;\n internals.set(key, { keyDigest: new Uint8Array(digestBuffer), hmacKey });\n return key;\n}\n\n/**\n * Encode raw signing operator key material for storage in env vars or secret managers.\n *\n * Supports `\"hex\"` (lowercase) and `\"base64url\"`. Output round-trips through\n * {@link decodeSigningKey} back to the original bytes.\n *\n * @throws {IdsError} `invalid_key_format` if `format` is not `\"hex\"` or `\"base64url\"`.\n * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.\n */\nexport function encodeSigningKey(bytes: Uint8Array, format: SigningKeyFormat): string {\n return encodeKeyMaterial(bytes, format, \"signing\", \"signing\");\n}\n\n/**\n * Decode key material emitted by {@link encodeSigningKey} back to raw bytes.\n *\n * The result can be passed directly to {@link importSigningKey}.\n *\n * @throws {IdsError} `invalid_key_format` if `format` is not `\"hex\"` or `\"base64url\"`.\n * @throws {IdsError} `invalid_key_encoding` if the string is malformed for its format.\n * @throws {IdsError} `invalid_key_length` if the decoded bytes are not 16, 24, or 32 bytes.\n */\nexport function decodeSigningKey(encoded: string, format: SigningKeyFormat): Uint8Array {\n return decodeKeyMaterial(encoded, format, \"signing\", \"signing\");\n}\n\n/**\n * Returns true when two handles were imported from the same raw key material.\n *\n * Uses a constant-time comparison so duplicate detection over key material does\n * not leak the position of the first differing byte through a timing side channel.\n */\nexport function signingKeysEqual(a: SigningKey, b: SigningKey): boolean {\n const aDigest = getSigningKeyInternals(a).keyDigest;\n const bDigest = getSigningKeyInternals(b).keyDigest;\n let diff = 0;\n for (let i = 0; i < SHA256_DIGEST_BYTES; i++) {\n diff |= aDigest[i]! ^ bDigest[i]!;\n }\n return diff === 0;\n}\n\n/**\n * Returns the derived HMAC webcrypto.CryptoKey held inside the handle.\n *\n * Intentional module-internal escape hatch for codec implementations (e.g. `createSignedTimestampId`).\n * Not re-exported from `@smonn/ids/signed`; external callers cannot reach this.\n */\nexport function getSigningKeyHmacKey(key: SigningKey): webcrypto.CryptoKey {\n return getSigningKeyInternals(key).hmacKey;\n}\n\nfunction getSigningKeyInternals(key: SigningKey): SigningKeyInternals {\n const keyInternals = internals.get(key);\n if (keyInternals === undefined) {\n throw new Error(\"invalid signing key\");\n }\n return keyInternals;\n}\n\nasync function deriveHmacKey(bytes: Uint8Array): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: new Uint8Array(), info: hmacInfo },\n base,\n { name: \"HMAC\", hash: \"SHA-256\", length: 256 },\n false,\n [\"sign\", \"verify\"],\n );\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nimport { createSignedTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { defaultRng } from \"../_kernel/rng.js\";\nimport type {\n Id,\n JsonSchema,\n ParseError,\n ParseResult,\n Prefix,\n StandardSchemaProps,\n} from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\nimport {\n assertValidKeyring,\n decodeSigningKey,\n encodeSigningKey,\n getSigningKeyHmacKey,\n importSigningKey,\n signingKeysEqual,\n type SigningKey,\n type SigningKeyFormat,\n} from \"./key.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode };\nexport {\n decodeSigningKey,\n encodeSigningKey,\n importSigningKey,\n type SigningKey,\n type SigningKeyFormat,\n};\n\n/**\n * Configuration options for a Signed Timestamp codec instance.\n */\nexport type SignedTimestampOptions = {\n /**\n * Non-empty ordered signing keyring. The first entry is current — the only one\n * `generate` / `generateAt` sign with. `verify` / `safeVerify` trial every entry\n * until the tag matches. Duplicate raw secrets are rejected at construction.\n */\n keys: [SigningKey, ...SigningKey[]];\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes 5 random bytes into `target` for the random tail. Defaults to `crypto.getRandomValues`. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * Result returned by {@link SignedTimestampCodec.safeVerify}.\n *\n * On success, `id` is the canonical {@link Id}.\n * On failure, `error` is a {@link ParseError} for structural problems or\n * `\"verification_failed\"` when the HMAC tag does not match any entry in the\n * signing keyring.\n */\nexport type SafeVerifyResult<Brand extends string> =\n | { ok: true; id: Id<Brand> }\n | { ok: false; error: ParseError | \"verification_failed\" };\n\n/**\n * Codec returned by {@link createSignedTimestampId}.\n *\n * Keeps the 6-byte millisecond timestamp **readable and sortable** like the\n * Timestamp codec, but replaces half of the 10-byte random tail with a truncated\n * HMAC tag, making IDs **tamper-evident and verifiable without a database lookup**.\n *\n * Byte layout: `ts6 ‖ rand5 ‖ tag5` where the 40-bit tag =\n * `trunc(HMAC-SHA256(hmacKey, brand ‖ ts6 ‖ rand5), 40)`.\n *\n * - Async (HMAC): `generate`, `generateAt`, `verify`, `safeVerify`.\n * - Sync (no key / plaintext timestamp): all other methods.\n */\nexport type SignedTimestampCodec<Brand extends string> = {\n /** Produces a canonical ID signed with the current (first) key. */\n generate(): Promise<Id<Brand>>;\n /**\n * Produces a canonical ID with timestamp from `date`, signed with the current key.\n * Throws on invalid dates.\n */\n generateAt(date: Date): Promise<Id<Brand>>;\n /**\n * Recomputes the HMAC tag across every keyring entry.\n *\n * Throws `IdsError` with `code: \"verification_failed\"` if no entry matches.\n * Tamper of the brand, timestamp bytes, or random bytes all fail here.\n */\n verify(id: Id<Brand>): Promise<void>;\n /**\n * Non-throwing path for untrusted input.\n *\n * Structurally parses `input` first (same rules as {@link safeParse}), then\n * verifies the HMAC tag. Returns `{ ok: false, error }` on any failure —\n * {@link ParseError} for structural problems or `\"verification_failed\"` for tag\n * mismatch — without throwing.\n */\n safeVerify(input: unknown): Promise<SafeVerifyResult<Brand>>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>`.\n * Sync — the 6-byte timestamp is plaintext. Trusts the type; use `safeParse()` at boundaries first.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /**\n * Tight lower bound sentinel for range scans (`ts(t) ‖ 0x00×10`).\n * **Not verifiable** — carries no valid tag.\n */\n minIdForTime(date: Date): Id<Brand>;\n /**\n * Tight upper bound sentinel for range scans (`ts(t) ‖ 0xff×10`).\n * **Not verifiable** — carries no valid tag.\n */\n maxIdForTime(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical `Id<Brand>` strings.\n * For untrusted input, use `safeParse()` or `safeVerify()` instead.\n */\n is(value: unknown): value is Id<Brand>;\n /** Normalise to canonical form, or throw on parse failure. */\n parse(value: unknown): Id<Brand>;\n /** Normalise to canonical form, or return `{ ok: false, error }`. */\n safeParse(value: unknown): ParseResult<Brand>;\n /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/**\n * Construct a {@link SignedTimestampCodec} for `brand`.\n *\n * `opts.keys` is a non-empty ordered signing keyring — the first entry is current\n * (used by `generate` / `generateAt`); all entries are tried on `verify` /\n * `safeVerify`; duplicate operator secrets are rejected at construction.\n *\n * @example\n * ```ts\n * const key = await importSigningKey(new Uint8Array(32));\n * const usr = createSignedTimestampId(\"usr\", { keys: [key] });\n *\n * const id = await usr.generate(); // Id<\"usr\">\n * await usr.verify(id); // passes\n * usr.extractTimestamp(id); // Date — sync, timestamp is plaintext\n * ```\n */\nexport function createSignedTimestampId<Brand extends string>(\n brand: Brand,\n opts: SignedTimestampOptions,\n): SignedTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n assertValidKeyring(opts.keys, signingKeysEqual, \"signing\");\n\n const hmacKeys = opts.keys.map(getSigningKeyHmacKey);\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? defaultRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createSignedTimestampLayoutOps(prefix, brand, rng, hmacKeys);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n verify: async (id) => {\n const ok = await layout.tryVerify(id);\n if (!ok) throw new IdsError(\"verification_failed\", \"verification failed\");\n },\n safeVerify: async (input) => {\n const parsed = wire.safeParse(input);\n if (!parsed.ok) return parsed;\n const ok = await layout.tryVerify(parsed.id);\n if (!ok) return { ok: false, error: \"verification_failed\" };\n return { ok: true, id: parsed.id };\n },\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"signed-4h2BnlWx.mjs","names":[],"sources":["../src/codecs/signed/layout.ts","../src/codecs/signed/key.ts","../src/codecs/signed/index.ts"],"sourcesContent":["import type { webcrypto } from \"node:crypto\";\nimport type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadBase32Length, payloadByteLength } from \"../../wire/invariants.js\";\nimport {\n readTimestampMsFromBase32Suffix,\n timestampByteLength,\n writeTimestamp,\n} from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength = 5;\nconst tagByteLength = 5;\nconst randomOffset = timestampByteLength; // 6\nconst tagOffset = randomOffset + randomByteLength; // 11\nconst signedContentByteLength = randomOffset + randomByteLength; // 11 (ts6 ‖ rand5)\n\nasync function computeTag(\n hmacKey: webcrypto.CryptoKey,\n brandBytes: Uint8Array,\n signedContent: Uint8Array,\n): Promise<Uint8Array> {\n const message = new Uint8Array(brandBytes.length + signedContent.length);\n message.set(brandBytes, 0);\n message.set(signedContent, brandBytes.length);\n const signature = new Uint8Array(\n await crypto.subtle.sign(\"HMAC\", hmacKey, message as Uint8Array<ArrayBuffer>),\n );\n return signature.subarray(0, tagByteLength);\n}\n\nfunction tagsEqual(a: Uint8Array, b: Uint8Array): boolean {\n /* v8 ignore next -- defensive guard; both call sites always pass tagByteLength-byte arrays */\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i]! ^ b[i]!;\n return diff === 0;\n}\n\nexport function createSignedTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n brand: Brand,\n rng: (target: Uint8Array) => void,\n hmacKeys: readonly webcrypto.CryptoKey[],\n) {\n const signKey = hmacKeys[0]!;\n const brandBytes = new TextEncoder().encode(brand);\n const syncBuffer = new Uint8Array(payloadByteLength);\n\n return {\n generateAt: async (ms: number): Promise<Id<Brand>> => {\n const buffer = new Uint8Array(payloadByteLength);\n writeTimestamp(ms, buffer);\n rng(buffer.subarray(randomOffset, tagOffset));\n const tag = await computeTag(\n signKey,\n brandBytes,\n buffer.subarray(0, signedContentByteLength),\n );\n buffer.set(tag, tagOffset);\n return toWireId(prefix, buffer);\n },\n tryVerify: async (id: Id<Brand>): Promise<boolean> => {\n const payload = payloadBytesFromId(prefix, id);\n const storedTag = payload.subarray(tagOffset, payloadByteLength);\n const signedContent = payload.subarray(0, signedContentByteLength);\n for (const hmacKey of hmacKeys) {\n const expected = await computeTag(hmacKey, brandBytes, signedContent);\n if (tagsEqual(storedTag, expected)) return true;\n }\n return false;\n },\n extractTimestamp: (id: Id<Brand>): Date =>\n new Date(readTimestampMsFromBase32Suffix(id.slice(prefix.length))),\n minIdForTime: (ms: number): Id<Brand> => {\n writeTimestamp(ms, syncBuffer);\n syncBuffer.fill(0x00, randomOffset, payloadByteLength);\n return toWireId(prefix, syncBuffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n writeTimestamp(ms, syncBuffer);\n syncBuffer.fill(0xff, randomOffset, payloadByteLength);\n return toWireId(prefix, syncBuffer);\n },\n exampleWireId: (): Id<Brand> => (prefix + \"0\".repeat(payloadBase32Length)) as Id<Brand>,\n };\n}\n","import type { webcrypto } from \"node:crypto\";\nimport {\n assertValidKeyMaterialByteLength,\n assertValidKeyring,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\nexport { assertValidKeyring };\n\n/** Wire encoding for signing key raw key bytes (not Crockford base32). */\nexport type SigningKeyFormat = \"hex\" | \"base64url\";\n\nconst hmacInfo = new TextEncoder().encode(\"@smonn/ids/signed/hmac\");\n\nconst SHA256_DIGEST_BYTES = 32;\n\ndeclare const signingKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for one operator signing key.\n *\n * Holds a single HMAC-SHA-256 key derived via HKDF under the domain-separation\n * label `@smonn/ids/signed/hmac`. The underlying `webcrypto.CryptoKey` is held\n * internally and never exposed to callers. Obtain handles via\n * {@link importSigningKey} and pass them to `createSignedTimestampId` as the\n * `keys` signing keyring.\n *\n * Distinct from both the **Opaque key** and the **Wrapping key** — the same\n * raw key material must not silently serve multiple codecs without an explicit import.\n */\nexport type SigningKey = {\n readonly [signingKeyBrand]: \"SigningKey\";\n};\n\ntype SigningKeyInternals = {\n keyDigest: Uint8Array;\n hmacKey: webcrypto.CryptoKey;\n};\n\nconst internals = new WeakMap<SigningKey, SigningKeyInternals>();\n\n/**\n * Import raw operator key material into a {@link SigningKey} handle.\n *\n * Derives a single HMAC-SHA-256 key via HKDF under the domain-separation label\n * `@smonn/ids/signed/hmac`. Accepts 16, 24, or 32 bytes. To store or\n * transport key material, use {@link encodeSigningKey} / {@link decodeSigningKey}\n * (`\"hex\"` or `\"base64url\"` — not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 raw key bytes.\n * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.\n */\nexport async function importSigningKey(bytes: Uint8Array): Promise<SigningKey> {\n assertValidKeyMaterialByteLength(bytes.length, \"signing\");\n const [hmacKey, digestBuffer] = await Promise.all([\n deriveHmacKey(bytes),\n crypto.subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>),\n ]);\n const key = Object.freeze({}) as SigningKey;\n internals.set(key, { keyDigest: new Uint8Array(digestBuffer), hmacKey });\n return key;\n}\n\n/**\n * Encode raw signing operator key material for storage in env vars or secret managers.\n *\n * Supports `\"hex\"` (lowercase) and `\"base64url\"`. Output round-trips through\n * {@link decodeSigningKey} back to the original bytes.\n *\n * @throws {IdsError} `invalid_key_format` if `format` is not `\"hex\"` or `\"base64url\"`.\n * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.\n */\nexport function encodeSigningKey(bytes: Uint8Array, format: SigningKeyFormat): string {\n return encodeKeyMaterial(bytes, format, \"signing\", \"signing\");\n}\n\n/**\n * Decode key material emitted by {@link encodeSigningKey} back to raw bytes.\n *\n * The result can be passed directly to {@link importSigningKey}.\n *\n * @throws {IdsError} `invalid_key_format` if `format` is not `\"hex\"` or `\"base64url\"`.\n * @throws {IdsError} `invalid_key_encoding` if the string is malformed for its format.\n * @throws {IdsError} `invalid_key_length` if the decoded bytes are not 16, 24, or 32 bytes.\n */\nexport function decodeSigningKey(encoded: string, format: SigningKeyFormat): Uint8Array {\n return decodeKeyMaterial(encoded, format, \"signing\", \"signing\");\n}\n\n/**\n * Returns true when two handles were imported from the same raw key material.\n *\n * Uses a constant-time comparison so duplicate detection over key material does\n * not leak the position of the first differing byte through a timing side channel.\n */\nexport function signingKeysEqual(a: SigningKey, b: SigningKey): boolean {\n const aDigest = getSigningKeyInternals(a).keyDigest;\n const bDigest = getSigningKeyInternals(b).keyDigest;\n let diff = 0;\n for (let i = 0; i < SHA256_DIGEST_BYTES; i++) {\n diff |= aDigest[i]! ^ bDigest[i]!;\n }\n return diff === 0;\n}\n\n/**\n * Returns the derived HMAC webcrypto.CryptoKey held inside the handle.\n *\n * Intentional module-internal escape hatch for codec implementations (e.g. `createSignedTimestampId`).\n * Not re-exported from `@smonn/ids/signed`; external callers cannot reach this.\n */\nexport function getSigningKeyHmacKey(key: SigningKey): webcrypto.CryptoKey {\n return getSigningKeyInternals(key).hmacKey;\n}\n\nfunction getSigningKeyInternals(key: SigningKey): SigningKeyInternals {\n const keyInternals = internals.get(key);\n if (keyInternals === undefined) {\n throw new Error(\"invalid signing key\");\n }\n return keyInternals;\n}\n\nasync function deriveHmacKey(bytes: Uint8Array): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: new Uint8Array(), info: hmacInfo },\n base,\n { name: \"HMAC\", hash: \"SHA-256\", length: 256 },\n false,\n [\"sign\", \"verify\"],\n );\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nimport { createSignedTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { defaultRng } from \"../_kernel/rng.js\";\nimport type {\n Id,\n JsonSchema,\n ParseError,\n ParseResult,\n Prefix,\n StandardSchemaProps,\n} from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\nimport {\n assertValidKeyring,\n decodeSigningKey,\n encodeSigningKey,\n getSigningKeyHmacKey,\n importSigningKey,\n signingKeysEqual,\n type SigningKey,\n type SigningKeyFormat,\n} from \"./key.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode };\nexport {\n decodeSigningKey,\n encodeSigningKey,\n importSigningKey,\n type SigningKey,\n type SigningKeyFormat,\n};\n\n/**\n * Configuration options for a Signed Timestamp codec instance.\n */\nexport type SignedTimestampOptions = {\n /**\n * Non-empty ordered signing keyring. The first entry is current — the only one\n * `generate` / `generateAt` sign with. `verify` / `safeVerify` trial every entry\n * until the tag matches. Duplicate raw secrets are rejected at construction.\n */\n keys: [SigningKey, ...SigningKey[]];\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes 5 random bytes into `target` for the random tail. Defaults to `crypto.getRandomValues`. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * Result returned by {@link SignedTimestampCodec.safeVerify}.\n *\n * On success, `id` is the canonical {@link Id}.\n * On failure, `error` is a {@link ParseError} for structural problems or\n * `\"verification_failed\"` when the HMAC tag does not match any entry in the\n * signing keyring.\n */\nexport type SafeVerifyResult<Brand extends string> =\n | { ok: true; id: Id<Brand> }\n | { ok: false; error: ParseError | \"verification_failed\" };\n\n/**\n * Codec returned by {@link createSignedTimestampId}.\n *\n * Keeps the 6-byte millisecond timestamp **readable and sortable** like the\n * Timestamp codec, but replaces half of the 10-byte random tail with a truncated\n * HMAC tag, making IDs **tamper-evident and verifiable without a database lookup**.\n *\n * Byte layout: `ts6 ‖ rand5 ‖ tag5` where the 40-bit tag =\n * `trunc(HMAC-SHA256(hmacKey, brand ‖ ts6 ‖ rand5), 40)`.\n *\n * - Async (HMAC): `generate`, `generateAt`, `verify`, `safeVerify`.\n * - Sync (no key / plaintext timestamp): all other methods.\n */\nexport type SignedTimestampCodec<Brand extends string> = {\n /** Produces a canonical ID signed with the current (first) key. */\n generate(): Promise<Id<Brand>>;\n /**\n * Produces a canonical ID with timestamp from `date`, signed with the current key.\n * Throws on invalid dates.\n */\n generateAt(date: Date): Promise<Id<Brand>>;\n /**\n * Recomputes the HMAC tag across every keyring entry.\n *\n * Throws `IdsError` with `code: \"verification_failed\"` if no entry matches.\n * Tamper of the brand, timestamp bytes, or random bytes all fail here.\n */\n verify(id: Id<Brand>): Promise<void>;\n /**\n * Non-throwing path for untrusted input.\n *\n * Structurally parses `input` first (same rules as {@link safeParse}), then\n * verifies the HMAC tag. Returns `{ ok: false, error }` on any failure —\n * {@link ParseError} for structural problems or `\"verification_failed\"` for tag\n * mismatch — without throwing.\n */\n safeVerify(input: unknown): Promise<SafeVerifyResult<Brand>>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>`.\n * Sync — the 6-byte timestamp is plaintext. Trusts the type; use `safeParse()` at boundaries first.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /**\n * Tight lower bound sentinel for range scans (`ts(t) ‖ 0x00×10`).\n * **Not verifiable** — carries no valid tag.\n */\n minIdForTime(date: Date): Id<Brand>;\n /**\n * Tight upper bound sentinel for range scans (`ts(t) ‖ 0xff×10`).\n * **Not verifiable** — carries no valid tag.\n */\n maxIdForTime(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical `Id<Brand>` strings.\n * For untrusted input, use `safeParse()` or `safeVerify()` instead.\n */\n is(value: unknown): value is Id<Brand>;\n /** Normalise to canonical form, or throw on parse failure. */\n parse(value: unknown): Id<Brand>;\n /** Normalise to canonical form, or return `{ ok: false, error }`. */\n safeParse(value: unknown): ParseResult<Brand>;\n /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/**\n * Construct a {@link SignedTimestampCodec} for `brand`.\n *\n * `opts.keys` is a non-empty ordered signing keyring — the first entry is current\n * (used by `generate` / `generateAt`); all entries are tried on `verify` /\n * `safeVerify`; duplicate operator secrets are rejected at construction.\n *\n * @example\n * ```ts\n * const key = await importSigningKey(new Uint8Array(32));\n * const usr = createSignedTimestampId(\"usr\", { keys: [key] });\n *\n * const id = await usr.generate(); // Id<\"usr\">\n * await usr.verify(id); // passes\n * usr.extractTimestamp(id); // Date — sync, timestamp is plaintext\n * ```\n */\nexport function createSignedTimestampId<Brand extends string>(\n brand: Brand,\n opts: SignedTimestampOptions,\n): SignedTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n assertValidKeyring(opts.keys, signingKeysEqual, \"signing\");\n\n const hmacKeys = opts.keys.map(getSigningKeyHmacKey);\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? defaultRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createSignedTimestampLayoutOps(prefix, brand, rng, hmacKeys);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n verify: async (id) => {\n const ok = await layout.tryVerify(id);\n if (!ok) throw new IdsError(\"verification_failed\", \"verification failed\");\n },\n safeVerify: async (input) => {\n const parsed = wire.safeParse(input);\n if (!parsed.ok) return parsed;\n const ok = await layout.tryVerify(parsed.id);\n if (!ok) return { ok: false, error: \"verification_failed\" };\n return { ok: true, id: parsed.id };\n },\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":"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"}
package/dist/signed.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { i as importSigningKey, n as decodeSigningKey, r as encodeSigningKey, t as createSignedTimestampId } from "./signed-C8OMt3TJ.mjs";
2
+ import { i as importSigningKey, n as decodeSigningKey, r as encodeSigningKey, t as createSignedTimestampId } from "./signed-4h2BnlWx.mjs";
3
3
  export { IdsError, createSignedTimestampId, decodeSigningKey, encodeSigningKey, importSigningKey, isIdsError };
@@ -1,5 +1,5 @@
1
1
  import { a as toWireId, n as registerBrand, s as validateBrand, t as wireMethods } from "./codec-shell-DvrTDa65.mjs";
2
- import { n as readTimestampMsFromBase32Suffix, r as writeTimestamp } from "./timestamp-bytes-DvhWHDa-.mjs";
2
+ import { a as writeTimestamp, i as readTimestampMsFromBase32Suffix, n as fastTenByteRng } from "./rng-Clos6uC0.mjs";
3
3
  //#region src/codecs/timestamp/layout.ts
4
4
  const randomByteLength = 10;
5
5
  /** Writes a 16-byte timestamp-layout payload into codec-owned scratch. */
@@ -42,24 +42,9 @@ function createTimestampLayoutOps(prefix, rng) {
42
42
  }
43
43
  //#endregion
44
44
  //#region src/codecs/timestamp/index.ts
45
- const hexCharCodeToNibble = /* @__PURE__ */ new Uint8Array(128);
46
- for (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;
47
- for (let i = 0; i < 6; i++) hexCharCodeToNibble[97 + i] = 10 + i;
48
45
  const defaultTimestampOptions = {
49
46
  now: Date.now,
50
- rng: (target) => {
51
- const s = crypto.randomUUID();
52
- target[0] = hexCharCodeToNibble[s.charCodeAt(0)] << 4 | hexCharCodeToNibble[s.charCodeAt(1)];
53
- target[1] = hexCharCodeToNibble[s.charCodeAt(2)] << 4 | hexCharCodeToNibble[s.charCodeAt(3)];
54
- target[2] = hexCharCodeToNibble[s.charCodeAt(4)] << 4 | hexCharCodeToNibble[s.charCodeAt(5)];
55
- target[3] = hexCharCodeToNibble[s.charCodeAt(6)] << 4 | hexCharCodeToNibble[s.charCodeAt(7)];
56
- target[4] = hexCharCodeToNibble[s.charCodeAt(9)] << 4 | hexCharCodeToNibble[s.charCodeAt(10)];
57
- target[5] = hexCharCodeToNibble[s.charCodeAt(11)] << 4 | hexCharCodeToNibble[s.charCodeAt(12)];
58
- target[6] = hexCharCodeToNibble[s.charCodeAt(24)] << 4 | hexCharCodeToNibble[s.charCodeAt(25)];
59
- target[7] = hexCharCodeToNibble[s.charCodeAt(26)] << 4 | hexCharCodeToNibble[s.charCodeAt(27)];
60
- target[8] = hexCharCodeToNibble[s.charCodeAt(28)] << 4 | hexCharCodeToNibble[s.charCodeAt(29)];
61
- target[9] = hexCharCodeToNibble[s.charCodeAt(30)] << 4 | hexCharCodeToNibble[s.charCodeAt(31)];
62
- }
47
+ rng: fastTenByteRng
63
48
  };
64
49
  /**
65
50
  * Creates a codec for `brand` (three lowercase a–z characters).
@@ -93,4 +78,4 @@ function createTimestampId(brand, opts = {}) {
93
78
  //#endregion
94
79
  export { createTimestampId as t };
95
80
 
96
- //# sourceMappingURL=timestamp-DBwVjDkg.mjs.map
81
+ //# sourceMappingURL=timestamp-Cg9nRfnK.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp-Cg9nRfnK.mjs","names":[],"sources":["../src/codecs/timestamp/layout.ts","../src/codecs/timestamp/index.ts"],"sourcesContent":["import type { Id, Prefix } from \"../../types.js\";\nimport { toWireId } from \"../../wire/envelope.js\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\nimport {\n readTimestampMsFromBase32Suffix,\n timestampByteLength,\n writeTimestamp,\n} from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength: number = payloadByteLength - timestampByteLength;\n\n/** Writes a 16-byte timestamp-layout payload into codec-owned scratch. */\nfunction buildPayload(\n ms: number,\n rng: (target: Uint8Array) => void,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n rng(randomView);\n}\n\n/** Writes sentinel min/max random bytes into codec-owned scratch. */\nfunction buildSentinelPayload(\n ms: number,\n fill: number,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n randomView.fill(fill);\n}\n\n/** Decodes the creation timestamp from a trusted wire ID. */\nfunction extractTimestampFromId<Brand extends string>(prefix: Prefix<Brand>, id: Id<Brand>): Date {\n return new Date(readTimestampMsFromBase32Suffix(id.slice(prefix.length)));\n}\n\n/** Layout ops binder for the Timestamp variant. `extractTimestampFromId` is module-private; the binder exposes `extractTimestamp` for the codec constructor. */\nexport function createTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n rng: (target: Uint8Array) => void,\n) {\n // Per-codec scratch buffer. Shared across generateAt(), minIdForTime(),\n // maxIdForTime(), and exampleWireId() — all are synchronous and overwrite both\n // the timestamp and random slices before encoding, so successive callers see\n // their own freshly-written bytes. toWireId reads the buffer and returns an\n // independent string, so the caller never sees the buffer itself.\n const buffer = new Uint8Array(payloadByteLength);\n const randomView = new Uint8Array(buffer.buffer, timestampByteLength, randomByteLength);\n\n return {\n generateAt: (ms: number): Id<Brand> => {\n buildPayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n extractTimestamp: (id: Id<Brand>): Date => extractTimestampFromId(prefix, id),\n minIdForTime: (ms: number): Id<Brand> => {\n buildSentinelPayload(ms, 0x00, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n buildSentinelPayload(ms, 0xff, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n exampleWireId: (ms: number): Id<Brand> => {\n buildPayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n };\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { createTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { fastTenByteRng } from \"../_kernel/rng.js\";\nimport type { Id, JsonSchema, ParseResult, Prefix, StandardSchemaProps } from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\n\n/**\n * Configuration options for a codec instance.\n */\nexport type TimestampOptions = {\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes random bytes into `target` for ID generation. Defaults to a `crypto.randomUUID` fast path. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\ntype ResolvedTimestampOptions = Required<Pick<TimestampOptions, \"now\" | \"rng\">> &\n Pick<TimestampOptions, \"allowDuplicateBrand\">;\n\n/**\n * A brand-scoped codec for generating and validating public-facing IDs.\n *\n * Wire format: `{brand}_` plus 26 lowercase Crockford base32 characters encoding a\n * 16-byte payload (6-byte ms timestamp + 10 random bytes). IDs sort by creation\n * time in ascending order.\n *\n * For encrypted IDs, use `createOpaqueTimestampId` from `@smonn/ids/opaque`.\n */\nexport type TimestampCodec<Brand extends string> = {\n /** Produces a new canonical ID using the codec's `now` and `rng`. */\n generate(): Id<Brand>;\n /** Produces a new canonical ID with timestamp bytes from `date` and a fresh random tail. Throws on invalid dates. */\n generateAt(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>`. Trusts the type — use `safeParse()` at boundaries first. See ADR-0002.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /** Tight lower bound for any ID generated at `date` (random portion `0x00`). Throws on invalid dates. */\n minIdForTime(date: Date): Id<Brand>;\n /** Tight upper bound for any ID generated at `date` (random portion `0xff`). Throws on invalid dates. */\n maxIdForTime(date: Date): Id<Brand>;\n /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\nconst defaultTimestampOptions: ResolvedTimestampOptions = {\n now: Date.now,\n // crypto.randomUUID harvest fast path (~7× faster than crypto.getRandomValues);\n // see fastTenByteRng. The Reverse Timestamp codec shares the identical 10-byte\n // random tail and the same default.\n rng: fastTenByteRng,\n};\n\n/**\n * Creates a codec for `brand` (three lowercase a–z characters).\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n */\nexport function createTimestampId<Brand extends string>(\n brand: Brand,\n opts: TimestampOptions = {},\n): TimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const options = {\n now: opts.now ?? defaultTimestampOptions.now,\n rng: opts.rng ?? defaultTimestampOptions.rng,\n } satisfies ResolvedTimestampOptions;\n\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createTimestampLayoutOps(prefix, options.rng);\n\n return {\n generate: () => layout.generateAt(options.now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId(options.now())),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":";;;AASA,MAAM,mBAAA;;AAGN,SAAS,aACP,IACA,KACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,IAAI,UAAU;AAChB;;AAGA,SAAS,qBACP,IACA,MACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,WAAW,KAAK,IAAI;AACtB;;AAGA,SAAS,uBAA6C,QAAuB,IAAqB;CAChG,OAAO,IAAI,KAAK,gCAAgC,GAAG,MAAM,OAAO,MAAM,CAAC,CAAC;AAC1E;;AAGA,SAAgB,yBACd,QACA,KACA;CAMA,MAAM,yBAAS,IAAI,WAAA,EAA4B;CAC/C,MAAM,aAAa,IAAI,WAAW,OAAO,QAAA,GAA6B,gBAAgB;CAEtF,OAAO;EACL,aAAa,OAA0B;GACrC,aAAa,IAAI,KAAK,QAAQ,UAAU;GACxC,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,mBAAmB,OAAwB,uBAAuB,QAAQ,EAAE;EAC5E,eAAe,OAA0B;GACvC,qBAAqB,IAAI,GAAM,QAAQ,UAAU;GACjD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,eAAe,OAA0B;GACvC,qBAAqB,IAAI,KAAM,QAAQ,UAAU;GACjD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,gBAAgB,OAA0B;GACxC,aAAa,IAAI,KAAK,QAAQ,UAAU;GACxC,OAAO,SAAS,QAAQ,MAAM;EAChC;CACF;AACF;;;ACPA,MAAM,0BAAoD;CACxD,KAAK,KAAK;CAIV,KAAK;AACP;;;;;;;AAQA,SAAgB,kBACd,OACA,OAAyB,CAAC,GACH;CACvB,cAAc,KAAK;CACnB,cAAc,OAAO,KAAK,mBAAmB;CAE7C,MAAM,UAAU;EACd,KAAK,KAAK,OAAO,wBAAwB;EACzC,KAAK,KAAK,OAAO,wBAAwB;CAC3C;CAEA,MAAM,SAAwB,GAAG,MAAM;CACvC,MAAM,OAAO,YAAY,MAAM;CAC/B,MAAM,SAAS,yBAAyB,QAAQ,QAAQ,GAAG;CAE3D,OAAO;EACL,gBAAgB,OAAO,WAAW,QAAQ,IAAI,CAAC;EAC/C,aAAa,SAAe,OAAO,WAAW,KAAK,QAAQ,CAAC;EAC5D,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,kBAAkB,OAAO;EACzB,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,oBAAoB,KAAK,aAAa,OAAO,OAAO,cAAc,QAAQ,IAAI,CAAC,CAAC;EAChF,aAAa,KAAK;CACpB;AACF"}
@@ -67,26 +67,33 @@ function writeLen32(value, target, offset) {
67
67
  target[offset + 2] = value >>> 8 & 255;
68
68
  target[offset + 3] = value & 255;
69
69
  }
70
- function hmacMessage(brand, kind, lane) {
70
+ function createHmacMessageTemplate(brand, kind) {
71
71
  const encoder = new TextEncoder();
72
72
  const brandBytes = encoder.encode(brand);
73
73
  const kindBytes = encoder.encode(kind);
74
- const msgLen = 4 + brandBytes.length + 4 + kindBytes.length + lane.length;
75
- const message = new Uint8Array(msgLen);
74
+ const laneOffset = 4 + brandBytes.length + 4 + kindBytes.length;
75
+ const buffer = new Uint8Array(laneOffset + laneByteLength);
76
76
  let offset = 0;
77
- writeLen32(brandBytes.length, message, offset);
77
+ writeLen32(brandBytes.length, buffer, offset);
78
78
  offset += 4;
79
- message.set(brandBytes, offset);
79
+ buffer.set(brandBytes, offset);
80
80
  offset += brandBytes.length;
81
- writeLen32(kindBytes.length, message, offset);
81
+ writeLen32(kindBytes.length, buffer, offset);
82
82
  offset += 4;
83
- message.set(kindBytes, offset);
84
- offset += kindBytes.length;
85
- message.set(lane, offset);
83
+ buffer.set(kindBytes, offset);
84
+ return {
85
+ buffer,
86
+ laneOffset
87
+ };
88
+ }
89
+ /** Materialise the HMAC message for `lane`. Fresh buffer per call → safe under concurrent async signs. */
90
+ function hmacMessage(template, lane) {
91
+ const message = template.buffer.slice();
92
+ message.set(lane, template.laneOffset);
86
93
  return message;
87
94
  }
88
- async function computeTag(key, brand, kind, lane) {
89
- return new Uint8Array(await crypto.subtle.sign("HMAC", key.hmacKey, hmacMessage(brand, kind, lane))).subarray(0, tagByteLength);
95
+ async function computeTag(key, template, lane) {
96
+ return new Uint8Array(await crypto.subtle.sign("HMAC", key.hmacKey, hmacMessage(template, lane))).subarray(0, tagByteLength);
90
97
  }
91
98
  function tagsEqual(a, b) {
92
99
  /* v8 ignore next -- defensive guard; both call sites always pass tagByteLength-byte arrays */
@@ -122,15 +129,15 @@ function buildPlaintext(lane, tag) {
122
129
  plaintext.set(tag, laneByteLength);
123
130
  return plaintext;
124
131
  }
125
- async function wrapLookupKey(prefix, brand, key, kind, lookupKey) {
132
+ async function wrapLookupKey(prefix, template, key, kind, lookupKey) {
126
133
  const lane = new Uint8Array(laneByteLength);
127
134
  writeLane(kind, lookupKey, lane);
128
- return toWireId(prefix, await encryptPayload(key, buildPlaintext(lane, await computeTag(key, brand, kind, lane))));
135
+ return toWireId(prefix, await encryptPayload(key, buildPlaintext(lane, await computeTag(key, template, lane))));
129
136
  }
130
- async function tryUnwrapLookupKey(prefix, brand, key, kind, id) {
137
+ async function tryUnwrapLookupKey(prefix, template, key, kind, id) {
131
138
  const plaintext = await decryptPayload(key, payloadBytesFromId(prefix, id));
132
139
  const lane = plaintext.subarray(0, laneByteLength);
133
- if (!tagsEqual(plaintext.subarray(laneByteLength, 16), await computeTag(key, brand, kind, lane))) return null;
140
+ if (!tagsEqual(plaintext.subarray(laneByteLength, 16), await computeTag(key, template, lane))) return null;
134
141
  return readLane(kind, lane);
135
142
  }
136
143
  function schemaExample(prefix) {
@@ -138,11 +145,12 @@ function schemaExample(prefix) {
138
145
  }
139
146
  function createWrappedLayoutOps(prefix, brand, kind, keys) {
140
147
  const wrapKey = keys[0];
148
+ const template = createHmacMessageTemplate(brand, kind);
141
149
  return {
142
- wrap: (lookupKey) => wrapLookupKey(prefix, brand, wrapKey, kind, lookupKey),
150
+ wrap: (lookupKey) => wrapLookupKey(prefix, template, wrapKey, kind, lookupKey),
143
151
  tryUnwrap: async (id) => {
144
152
  for (const key of keys) {
145
- const lookupKey = await tryUnwrapLookupKey(prefix, brand, key, kind, id);
153
+ const lookupKey = await tryUnwrapLookupKey(prefix, template, key, kind, id);
146
154
  if (lookupKey !== null) return lookupKey;
147
155
  }
148
156
  return null;
@@ -346,4 +354,4 @@ function createWrappedKeyId(brand, opts) {
346
354
  //#endregion
347
355
  export { importWrappingKey as i, decodeWrappingKey as n, encodeWrappingKey as r, createWrappedKeyId as t };
348
356
 
349
- //# sourceMappingURL=wrapped-CDTiPwNM.mjs.map
357
+ //# sourceMappingURL=wrapped-BQ-lNECo.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapped-BQ-lNECo.mjs","names":[],"sources":["../src/codecs/wrapped/layout.ts","../src/codecs/wrapped/key.ts","../src/codecs/wrapped/index.ts"],"sourcesContent":["import type { webcrypto } from \"node:crypto\";\nimport type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadBase32Length, payloadByteLength } from \"../../wire/invariants.js\";\n\nconst zeroIv = new Uint8Array(payloadByteLength);\nconst pkcsPad = 0x10;\nconst laneByteLength = 8;\nconst tagByteLength = 8;\n\ntype LayoutWrappingKey = {\n aesKey: webcrypto.CryptoKey;\n hmacKey: webcrypto.CryptoKey;\n};\n\ntype LayoutWrappedKind = \"u32\" | \"i32\" | \"u64\" | \"i64\";\ntype LayoutLookupKey<K extends LayoutWrappedKind> = K extends \"u32\" | \"i32\" ? number : bigint;\n\nfunction writeU32Lane(value: number, lane: Uint8Array): void {\n lane[0] = 0;\n lane[1] = 0;\n lane[2] = 0;\n lane[3] = 0;\n lane[4] = (value >>> 24) & 0xff;\n lane[5] = (value >>> 16) & 0xff;\n lane[6] = (value >>> 8) & 0xff;\n lane[7] = value & 0xff;\n}\n\nfunction readU32Lane(lane: Uint8Array): number | null {\n for (let i = 0; i < 4; i++) {\n if (lane[i] !== 0) return null;\n }\n return ((lane[4]! << 24) | (lane[5]! << 16) | (lane[6]! << 8) | lane[7]!) >>> 0;\n}\n\nfunction writeI32Lane(value: number, lane: Uint8Array): void {\n lane.fill(value < 0 ? 0xff : 0x00, 0, 4);\n new DataView(lane.buffer, lane.byteOffset, lane.byteLength).setInt32(4, value, false);\n}\n\nfunction readI32Lane(lane: Uint8Array): number | null {\n const signExtension = (lane[4]! & 0x80) === 0 ? 0x00 : 0xff;\n for (let i = 0; i < 4; i++) {\n if (lane[i] !== signExtension) return null;\n }\n return new DataView(lane.buffer, lane.byteOffset, lane.byteLength).getInt32(4, false);\n}\n\nfunction writeU64Lane(value: bigint, lane: Uint8Array): void {\n new DataView(lane.buffer, lane.byteOffset, lane.byteLength).setBigUint64(0, value, false);\n}\n\nfunction readU64Lane(lane: Uint8Array): bigint {\n return new DataView(lane.buffer, lane.byteOffset, lane.byteLength).getBigUint64(0, false);\n}\n\nfunction writeI64Lane(value: bigint, lane: Uint8Array): void {\n new DataView(lane.buffer, lane.byteOffset, lane.byteLength).setBigInt64(0, value, false);\n}\n\nfunction readI64Lane(lane: Uint8Array): bigint {\n return new DataView(lane.buffer, lane.byteOffset, lane.byteLength).getBigInt64(0, false);\n}\n\nfunction writeLane<K extends LayoutWrappedKind>(\n kind: K,\n value: LayoutLookupKey<K>,\n lane: Uint8Array,\n): void {\n if (kind === \"i32\") {\n writeI32Lane(value as number, lane);\n return;\n }\n if (kind === \"u64\") {\n writeU64Lane(value as bigint, lane);\n return;\n }\n if (kind === \"i64\") {\n writeI64Lane(value as bigint, lane);\n return;\n }\n writeU32Lane(value as number, lane);\n}\n\nfunction readLane<K extends LayoutWrappedKind>(\n kind: K,\n lane: Uint8Array,\n): LayoutLookupKey<K> | null {\n if (kind === \"u64\") return readU64Lane(lane) as LayoutLookupKey<K>;\n if (kind === \"i64\") return readI64Lane(lane) as LayoutLookupKey<K>;\n const value = kind === \"i32\" ? readI32Lane(lane) : readU32Lane(lane);\n return value as LayoutLookupKey<K> | null;\n}\n\nfunction writeLen32(value: number, target: Uint8Array, offset: number): void {\n target[offset] = (value >>> 24) & 0xff;\n target[offset + 1] = (value >>> 16) & 0xff;\n target[offset + 2] = (value >>> 8) & 0xff;\n target[offset + 3] = value & 0xff;\n}\n\n/**\n * Precomputed HMAC-message template for a fixed (brand, kind) pair.\n *\n * The message is `len32(brand) ‖ brand ‖ len32(kind) ‖ kind ‖ lane`. Everything\n * except the trailing 8-byte lane is constant for the life of the codec, so we\n * build it once at construction. `brand`/`kind` are never re-encoded and no\n * `TextEncoder` is allocated on the `wrap` / `unwrap` hot paths.\n */\ntype HmacMessageTemplate = {\n /** Full-length buffer with the constant prefix written and the lane region zeroed. */\n readonly buffer: Uint8Array;\n /** Byte offset where the lane is copied in on each call. */\n readonly laneOffset: number;\n};\n\nfunction createHmacMessageTemplate(brand: string, kind: LayoutWrappedKind): HmacMessageTemplate {\n const encoder = new TextEncoder();\n const brandBytes = encoder.encode(brand);\n const kindBytes = encoder.encode(kind);\n const laneOffset = 4 + brandBytes.length + 4 + kindBytes.length;\n const buffer = new Uint8Array(laneOffset + laneByteLength);\n let offset = 0;\n writeLen32(brandBytes.length, buffer, offset);\n offset += 4;\n buffer.set(brandBytes, offset);\n offset += brandBytes.length;\n writeLen32(kindBytes.length, buffer, offset);\n offset += 4;\n buffer.set(kindBytes, offset);\n return { buffer, laneOffset };\n}\n\n/** Materialise the HMAC message for `lane`. Fresh buffer per call → safe under concurrent async signs. */\nfunction hmacMessage(template: HmacMessageTemplate, lane: Uint8Array): Uint8Array {\n const message = template.buffer.slice();\n message.set(lane, template.laneOffset);\n return message;\n}\n\nasync function computeTag(\n key: LayoutWrappingKey,\n template: HmacMessageTemplate,\n lane: Uint8Array,\n): Promise<Uint8Array> {\n const signature = new Uint8Array(\n await crypto.subtle.sign(\n \"HMAC\",\n key.hmacKey,\n hmacMessage(template, lane) as Uint8Array<ArrayBuffer>,\n ),\n );\n return signature.subarray(0, tagByteLength);\n}\n\nfunction tagsEqual(a: Uint8Array, b: Uint8Array): boolean {\n /* v8 ignore next -- defensive guard; both call sites always pass tagByteLength-byte arrays */\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i]! ^ b[i]!;\n return diff === 0;\n}\n\nasync function encryptPayload(key: LayoutWrappingKey, plaintext: Uint8Array): Promise<Uint8Array> {\n const encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key.aesKey,\n plaintext as Uint8Array<ArrayBuffer>,\n ),\n );\n return encrypted.subarray(0, payloadByteLength);\n}\n\nasync function decryptPayload(key: LayoutWrappingKey, c1: Uint8Array): Promise<Uint8Array> {\n const c2Input = new Uint8Array(payloadByteLength);\n for (let i = 0; i < payloadByteLength; i++) c2Input[i] = pkcsPad ^ c1[i]!;\n const c2Encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key.aesKey,\n c2Input as Uint8Array<ArrayBuffer>,\n ),\n );\n const ciphertext = new Uint8Array(payloadByteLength * 2);\n ciphertext.set(c1, 0);\n ciphertext.set(c2Encrypted.subarray(0, payloadByteLength), payloadByteLength);\n return new Uint8Array(\n await crypto.subtle.decrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key.aesKey,\n ciphertext as Uint8Array<ArrayBuffer>,\n ),\n );\n}\n\nfunction buildPlaintext(lane: Uint8Array, tag: Uint8Array): Uint8Array {\n const plaintext = new Uint8Array(payloadByteLength);\n plaintext.set(lane, 0);\n plaintext.set(tag, laneByteLength);\n return plaintext;\n}\n\nasync function wrapLookupKey<Brand extends string, Kind extends LayoutWrappedKind>(\n prefix: Prefix<Brand>,\n template: HmacMessageTemplate,\n key: LayoutWrappingKey,\n kind: Kind,\n lookupKey: LayoutLookupKey<Kind>,\n): Promise<Id<Brand>> {\n const lane = new Uint8Array(laneByteLength);\n writeLane(kind, lookupKey, lane);\n const tag = await computeTag(key, template, lane);\n const encrypted = await encryptPayload(key, buildPlaintext(lane, tag));\n return toWireId(prefix, encrypted);\n}\n\nasync function tryUnwrapLookupKey<Brand extends string, Kind extends LayoutWrappedKind>(\n prefix: Prefix<Brand>,\n template: HmacMessageTemplate,\n key: LayoutWrappingKey,\n kind: Kind,\n id: Id<Brand>,\n): Promise<LayoutLookupKey<Kind> | null> {\n const plaintext = await decryptPayload(key, payloadBytesFromId(prefix, id));\n const lane = plaintext.subarray(0, laneByteLength);\n const tag = plaintext.subarray(laneByteLength, payloadByteLength);\n const expected = await computeTag(key, template, lane);\n if (!tagsEqual(tag, expected)) return null;\n return readLane(kind, lane);\n}\n\nfunction schemaExample<Brand extends string>(prefix: Prefix<Brand>): string {\n return prefix + \"0\".repeat(payloadBase32Length);\n}\n\nexport function createWrappedLayoutOps<Brand extends string, Kind extends LayoutWrappedKind>(\n prefix: Prefix<Brand>,\n brand: Brand,\n kind: Kind,\n keys: readonly LayoutWrappingKey[],\n) {\n const wrapKey = keys[0]!;\n // brand + kind are fixed for the codec's lifetime; encode them and build the\n // HMAC-message prefix once instead of on every wrap / unwrap-trial.\n const template = createHmacMessageTemplate(brand, kind);\n return {\n wrap: (lookupKey: LayoutLookupKey<Kind>): Promise<Id<Brand>> =>\n wrapLookupKey(prefix, template, wrapKey, kind, lookupKey),\n tryUnwrap: async (id: Id<Brand>): Promise<LayoutLookupKey<Kind> | null> => {\n for (const key of keys) {\n const lookupKey = await tryUnwrapLookupKey(prefix, template, key, kind, id);\n if (lookupKey !== null) return lookupKey;\n }\n return null;\n },\n exampleWireId: (): Id<Brand> => schemaExample(prefix) as Id<Brand>,\n };\n}\n","import type { webcrypto } from \"node:crypto\";\nimport {\n assertValidKeyMaterialByteLength,\n assertValidKeyring,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\nexport { assertValidKeyring };\n\n/** Wire encoding for wrapping operator secret bytes (not Crockford base32). */\nexport type WrappingKeyFormat = \"hex\" | \"base64url\";\n\nconst aesInfo = new TextEncoder().encode(\"@smonn/ids/wrapped/aes\");\nconst hmacInfo = new TextEncoder().encode(\"@smonn/ids/wrapped/hmac\");\n\nconst SHA256_DIGEST_BYTES = 32;\n\ndeclare const wrappingKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for one operator wrapping secret.\n *\n * Holds derived AES and HMAC subkeys internally; callers never access subkeys\n * or raw `webcrypto.CryptoKey` values directly. Obtain handles via {@link importWrappingKey}\n * and pass them to `createWrappedKeyId` as the `keys` wrapping keyring.\n *\n * Distinct from the **Opaque key** used by `@smonn/ids/opaque` — one raw\n * secret must not silently serve both codecs without an explicit import.\n */\nexport type WrappingKey = {\n readonly [wrappingKeyBrand]: \"WrappingKey\";\n};\n\ntype WrappingKeyInternals = {\n keyDigest: Uint8Array;\n aesKey: webcrypto.CryptoKey;\n hmacKey: webcrypto.CryptoKey;\n};\n\nexport type WrappingKeyMaterial = {\n aesKey: webcrypto.CryptoKey;\n hmacKey: webcrypto.CryptoKey;\n};\n\nconst internals = new WeakMap<WrappingKey, WrappingKeyInternals>();\n\n/**\n * Import raw operator secret bytes into a {@link WrappingKey} handle.\n *\n * One raw secret derives into AES and HMAC subkeys held inside the returned\n * handle. Accepts 16, 24, or 32 bytes (AES-128 / AES-192 / AES-256 strength).\n * To store or transport key material, use {@link encodeWrappingKey} /\n * {@link decodeWrappingKey} (`\"hex\"` or `\"base64url\"` — not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 raw key bytes.\n */\nexport async function importWrappingKey(bytes: Uint8Array): Promise<WrappingKey> {\n assertValidKeyMaterialByteLength(bytes.length, \"wrapping\");\n const [aesKey, hmacKey, digestBuffer] = await Promise.all([\n deriveAesKey(bytes),\n deriveHmacKey(bytes),\n crypto.subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>),\n ]);\n const key = Object.freeze({}) as WrappingKey;\n internals.set(key, {\n keyDigest: new Uint8Array(digestBuffer),\n aesKey,\n hmacKey,\n });\n return key;\n}\n\n/**\n * Encode raw wrapping operator secret bytes for storage in env vars or secret managers.\n *\n * Supports `\"hex\"` (lowercase) and `\"base64url\"`. Output round-trips through\n * {@link decodeWrappingKey} back to the original bytes.\n */\nexport function encodeWrappingKey(bytes: Uint8Array, format: WrappingKeyFormat): string {\n return encodeKeyMaterial(bytes, format, \"wrapping\", \"wrapping\");\n}\n\n/**\n * Decode key material emitted by {@link encodeWrappingKey} back to raw bytes.\n *\n * The result can be passed directly to {@link importWrappingKey}.\n */\nexport function decodeWrappingKey(encoded: string, format: WrappingKeyFormat): Uint8Array {\n return decodeKeyMaterial(encoded, format, \"wrapping\", \"wrapping\");\n}\n\n/**\n * Returns true when two handles were imported from the same raw operator secret.\n *\n * Uses a constant-time comparison so duplicate detection over key material does\n * not leak the position of the first differing byte through a timing side channel.\n */\nexport function wrappingKeysEqual(a: WrappingKey, b: WrappingKey): boolean {\n const aDigest = getWrappingKeyInternals(a).keyDigest;\n const bDigest = getWrappingKeyInternals(b).keyDigest;\n let diff = 0;\n for (let i = 0; i < SHA256_DIGEST_BYTES; i++) {\n diff |= aDigest[i]! ^ bDigest[i]!;\n }\n return diff === 0;\n}\n\nexport function getWrappingKeyMaterial(key: WrappingKey): WrappingKeyMaterial {\n const keyInternals = getWrappingKeyInternals(key);\n return {\n aesKey: keyInternals.aesKey,\n hmacKey: keyInternals.hmacKey,\n };\n}\n\nfunction getWrappingKeyInternals(key: WrappingKey): WrappingKeyInternals {\n const keyInternals = internals.get(key);\n if (keyInternals === undefined) {\n throw new Error(\"invalid wrapping key\");\n }\n return keyInternals;\n}\n\nasync function deriveAesKey(bytes: Uint8Array): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: new Uint8Array(), info: aesInfo },\n base,\n { name: \"AES-CBC\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\nasync function deriveHmacKey(bytes: Uint8Array): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: new Uint8Array(), info: hmacInfo },\n base,\n { name: \"HMAC\", hash: \"SHA-256\", length: 256 },\n false,\n [\"sign\", \"verify\"],\n );\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nimport { createWrappedLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport type {\n Id,\n JsonSchema,\n ParseError,\n ParseResult,\n Prefix,\n StandardSchemaProps,\n} from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\nimport {\n assertValidKeyring,\n decodeWrappingKey,\n encodeWrappingKey,\n getWrappingKeyMaterial,\n importWrappingKey,\n type WrappingKey,\n type WrappingKeyFormat,\n wrappingKeysEqual,\n} from \"./key.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode };\nexport {\n decodeWrappingKey,\n encodeWrappingKey,\n importWrappingKey,\n type WrappingKey,\n type WrappingKeyFormat,\n};\n\nexport type WrappedKind = \"u32\" | \"i32\" | \"u64\" | \"i64\";\n\ntype LookupKeyForKind<K extends WrappedKind> = K extends \"u32\" | \"i32\" ? number : bigint;\n\n/**\n * Result returned by {@link WrappedKeyCodec.safeUnwrap}.\n *\n * On success, `id` is the canonical {@link Id} and `lookupKey` is the recovered\n * integer (`number` for 32-bit kinds, `bigint` for 64-bit kinds).\n * On failure, `error` is a {@link ParseError} for structural problems or\n * `\"verification_failed\"` when the payload is structurally valid but the\n * verification tag does not match any entry in the wrapping keyring.\n */\nexport type UnwrapResult<Brand extends string, Kind extends WrappedKind> =\n | { ok: true; id: Id<Brand>; lookupKey: LookupKeyForKind<Kind> }\n | { ok: false; error: ParseError | \"verification_failed\" };\n\n/**\n * Codec returned by {@link createWrappedKeyId}.\n *\n * Wraps a caller-owned integer **lookup key** into a public {@link Id} and\n * recovers it on unwrap. The codec is deterministic under fixed key material:\n * the same lookup key always yields the same public ID (**equality leakage**).\n *\n * - `wrap` / `unwrap` / `safeUnwrap` are async (WebCrypto).\n * - `is`, `parse`, `safeParse`, and `toJsonSchema` are synchronous and require\n * no key material — they validate prefix and base32 shape only.\n * - The `Kind` type parameter drives value types at the TypeScript boundary:\n * `u32` / `i32` → `number`; `u64` / `i64` → `bigint`.\n */\nexport type WrappedKeyCodec<Brand extends string, Kind extends WrappedKind> = {\n /**\n * Wrap `lookupKey` into a public ID using the current (first) wrapping key.\n *\n * Throws if `lookupKey` is out of range or the wrong JS type for `Kind`.\n */\n wrap(lookupKey: LookupKeyForKind<Kind>): Promise<Id<Brand>>;\n /**\n * Verify the payload of a trusted `Id<Brand>` and return the lookup key.\n *\n * Throws `IdsError` with `code: \"verification_failed\"` if no entry in the\n * wrapping keyring matches the payload tag. Use {@link safeUnwrap} for\n * untrusted input.\n */\n unwrap(id: Id<Brand>): Promise<LookupKeyForKind<Kind>>;\n /**\n * Non-throwing path for untrusted input.\n *\n * Structurally parses `input` first (same rules as {@link safeParse}), then\n * verifies the payload. Returns `{ ok: false, error }` on any failure —\n * `ParseError` for structural problems or `\"verification_failed\"` for tag\n * mismatch — without throwing. Tamper, wrong keyring, and revoked-key cases\n * all surface as `\"verification_failed\"`.\n */\n safeUnwrap(input: unknown): Promise<UnwrapResult<Brand, Kind>>;\n /** Strict type guard: `true` only for already-canonical `Id<Brand>` strings. */\n is(value: unknown): value is Id<Brand>;\n /** Normalise to canonical form, or throw on parse failure. */\n parse(value: unknown): Id<Brand>;\n /** Normalise to canonical form, or return `{ ok: false, error }`. */\n safeParse(value: unknown): ParseResult<Brand>;\n toJsonSchema(): JsonSchema;\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\nexport type WrappedKeyOptions<K extends WrappedKind> = {\n kind: K;\n keys: [WrappingKey, ...WrappingKey[]];\n allowDuplicateBrand?: boolean;\n};\n\nconst u32Max = 0xffff_ffff;\nconst i32Min = -0x8000_0000;\nconst i32Max = 0x7fff_ffff;\nconst u64Max = 0xffff_ffff_ffff_ffffn;\nconst i64Min = -(1n << 63n);\nconst i64Max = (1n << 63n) - 1n;\n\nfunction assertSupportedKind(kind: WrappedKind): asserts kind is WrappedKind {\n if (kind !== \"u32\" && kind !== \"i32\" && kind !== \"u64\" && kind !== \"i64\") {\n throw new IdsError(\"invalid_kind\", \"invalid wrapped key kind: expected u32, i32, u64, or i64\");\n }\n}\n\nfunction assertU32LookupKey(lookupKey: unknown): asserts lookupKey is number {\n if (\n typeof lookupKey !== \"number\" ||\n !Number.isInteger(lookupKey) ||\n Object.is(lookupKey, -0) ||\n lookupKey < 0 ||\n lookupKey > u32Max\n ) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid u32 lookup key: expected integer in [0, ${u32Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertI32LookupKey(lookupKey: unknown): asserts lookupKey is number {\n if (\n typeof lookupKey !== \"number\" ||\n !Number.isInteger(lookupKey) ||\n Object.is(lookupKey, -0) ||\n lookupKey < i32Min ||\n lookupKey > i32Max\n ) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid i32 lookup key: expected integer in [${i32Min}, ${i32Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertU64LookupKey(lookupKey: unknown): asserts lookupKey is bigint {\n if (typeof lookupKey !== \"bigint\" || lookupKey < 0n || lookupKey > u64Max) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid u64 lookup key: expected bigint in [0, ${u64Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertI64LookupKey(lookupKey: unknown): asserts lookupKey is bigint {\n if (typeof lookupKey !== \"bigint\" || lookupKey < i64Min || lookupKey > i64Max) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid i64 lookup key: expected bigint in [${i64Min}, ${i64Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertLookupKey<Kind extends WrappedKind>(\n kind: Kind,\n lookupKey: unknown,\n): asserts lookupKey is LookupKeyForKind<Kind> {\n if (kind === \"i32\") {\n assertI32LookupKey(lookupKey);\n return;\n }\n if (kind === \"u64\") {\n assertU64LookupKey(lookupKey);\n return;\n }\n if (kind === \"i64\") {\n assertI64LookupKey(lookupKey);\n return;\n }\n assertU32LookupKey(lookupKey);\n}\n\n/**\n * Construct a {@link WrappedKeyCodec} for `brand` and the given `kind`.\n *\n * `opts.kind` fixes the integer type at construction time — one brand, one\n * kind. `opts.keys` is a non-empty ordered wrapping keyring: the first entry\n * is current (used by `wrap`); all entries are tried on `unwrap`; duplicate\n * operator secrets are rejected at construction.\n *\n * @example\n * ```ts\n * const key = await importWrappingKey(new Uint8Array(32));\n * const invoices = createWrappedKeyId(\"inv\", { kind: \"u32\", keys: [key] });\n *\n * const id = await invoices.wrap(42); // Id<\"inv\">\n * await invoices.unwrap(id); // 42\n * ```\n */\nexport function createWrappedKeyId<Brand extends string, Kind extends WrappedKind>(\n brand: Brand,\n opts: WrappedKeyOptions<Kind>,\n): WrappedKeyCodec<Brand, Kind> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n assertSupportedKind(opts.kind);\n assertValidKeyring(opts.keys, wrappingKeysEqual, \"wrapping\");\n const layoutKeys = opts.keys.map(getWrappingKeyMaterial);\n\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createWrappedLayoutOps(prefix, brand, opts.kind, layoutKeys);\n\n return {\n wrap: async (lookupKey) => {\n assertLookupKey(opts.kind, lookupKey);\n return layout.wrap(lookupKey);\n },\n unwrap: async (id) => {\n const lookupKey = await layout.tryUnwrap(id);\n if (lookupKey === null) {\n throw new IdsError(\"verification_failed\", \"verification failed\");\n }\n return lookupKey;\n },\n safeUnwrap: async (input) => {\n const parsed = wire.safeParse(input);\n if (!parsed.ok) return parsed;\n const lookupKey = await layout.tryUnwrap(parsed.id);\n if (lookupKey === null) return { ok: false, error: \"verification_failed\" };\n return { ok: true, id: parsed.id, lookupKey };\n },\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":"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"}
package/dist/wrapped.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-CDTiPwNM.mjs";
2
+ import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-BQ-lNECo.mjs";
3
3
  export { IdsError, createWrappedKeyId, decodeWrappingKey, encodeWrappingKey, importWrappingKey, isIdsError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smonn/ids",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "license": "MIT",
5
5
  "author": "Simon Ingeson (https://github.com/smonn)",
6
6
  "repository": {
@@ -38,12 +38,14 @@
38
38
  "@mikro-orm/core": "^7.1.4",
39
39
  "@nestjs/common": "^11.1.27",
40
40
  "@prisma/client": ">=5.0.0",
41
+ "@total-typescript/shoehorn": "^0.1.2",
41
42
  "@types/express": "^5.0.6",
42
43
  "@types/node": "24.13.2",
43
44
  "@vitest/coverage-v8": "4.1.8",
44
45
  "dependency-cruiser": "17.4.3",
45
46
  "drizzle-orm": "^0.45.2",
46
47
  "express": "^5.2.1",
48
+ "fast-check": "^4.8.0",
47
49
  "fastify": "^5.8.5",
48
50
  "graphql": "^17.0.1",
49
51
  "hono": "^4.12.26",
@@ -1 +0,0 @@
1
- {"version":3,"file":"reverse-BRZRc1_U.mjs","names":[],"sources":["../src/codecs/reverse/layout.ts","../src/codecs/reverse/index.ts"],"sourcesContent":["import type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\nimport { timestampByteLength, writeTimestamp } from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength: number = payloadByteLength - timestampByteLength;\n\n/** Writes inverted timestamp bytes, then fills random portion. */\nfunction buildReversePayload(\n ms: number,\n rng: (target: Uint8Array) => void,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n rng(randomView);\n}\n\n/** Writes inverted timestamp bytes, then fills random portion with a sentinel. */\nfunction buildReverseSentinelPayload(\n ms: number,\n fill: number,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n randomView.fill(fill);\n}\n\n/** Decodes the original timestamp by inverting the first 6 payload bytes. */\nfunction extractReverseTimestampFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n id: Id<Brand>,\n): Date {\n const bytes = payloadBytesFromId(prefix, id);\n let ms = 0;\n for (let i = 0; i < timestampByteLength; i++) {\n ms = ms * 256 + (~bytes[i]! & 0xff);\n }\n return new Date(ms);\n}\n\n/** Layout ops binder for the Reverse Timestamp variant. */\nexport function createReverseTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n rng: (target: Uint8Array) => void,\n) {\n const buffer = new Uint8Array(payloadByteLength);\n const randomView = new Uint8Array(buffer.buffer, timestampByteLength, randomByteLength);\n\n return {\n generateAt: (ms: number): Id<Brand> => {\n buildReversePayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n extractTimestamp: (id: Id<Brand>): Date => extractReverseTimestampFromId(prefix, id),\n minIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0x00, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0xff, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n exampleWireId: (ms: number): Id<Brand> => {\n buildReversePayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n };\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nimport { createReverseTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { defaultRng } from \"../_kernel/rng.js\";\nimport type { Id, JsonSchema, ParseResult, Prefix, StandardSchemaProps } from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode };\n\n/**\n * Configuration options for a Reverse Timestamp codec instance.\n */\nexport type ReverseTimestampOptions = {\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes random bytes into `target` for ID generation. Defaults to `crypto.getRandomValues`. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * A brand-scoped codec for generating and validating Reverse Timestamp IDs.\n *\n * Wire format: `{brand}_` plus 26 lowercase Crockford base32 characters encoding a\n * 16-byte payload (6-byte bitwise-inverted ms timestamp + 10 random bytes). IDs sort\n * by creation time in **descending** (newest-first) order.\n *\n * Range queries across a time interval [t_old, t_new] should scan from\n * `minIdForTime(t_new)` to `maxIdForTime(t_old)` — the reversed sort order means\n * newer timestamps produce lexicographically smaller IDs.\n *\n * Constructed via `createReverseTimestampId(brand)` from `@smonn/ids/reverse`.\n */\nexport type ReverseTimestampCodec<Brand extends string> = {\n /** Produces a new canonical ID using the codec's `now` and `rng`. */\n generate(): Id<Brand>;\n /** Produces a new canonical ID with timestamp bytes from `date` and a fresh random tail. Throws on invalid dates. */\n generateAt(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>` by inverting the timestamp bytes.\n * Trusts the type — use `safeParse()` at boundaries first.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /**\n * Lexicographically smallest ID for any ID generated at `date` (random portion `0x00`).\n * Because timestamps are inverted, a newer `date` yields a lexicographically smaller result —\n * use `minIdForTime(t_new)` as the lower bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n minIdForTime(date: Date): Id<Brand>;\n /**\n * Lexicographically largest ID for any ID generated at `date` (random portion `0xff`).\n * Because timestamps are inverted, an older `date` yields a lexicographically larger result —\n * use `maxIdForTime(t_old)` as the upper bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n maxIdForTime(date: Date): Id<Brand>;\n /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/**\n * Creates a Reverse Timestamp codec for `brand` (three lowercase a–z characters).\n *\n * IDs sort newest-first: the 48-bit timestamp field is bitwise-inverted before encoding,\n * so lexicographic ID order equals descending creation-time order. `extractTimestamp`\n * inverts back to recover the original millisecond.\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n */\nexport function createReverseTimestampId<Brand extends string>(\n brand: Brand,\n opts: ReverseTimestampOptions = {},\n): ReverseTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? defaultRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createReverseTimestampLayoutOps(prefix, rng);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId(now())),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":";;;;AAKA,MAAM,mBAAA;;AAGN,SAAS,oBACP,IACA,KACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,IAAI,UAAU;AAChB;;AAGA,SAAS,4BACP,IACA,MACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,WAAW,KAAK,IAAI;AACtB;;AAGA,SAAS,8BACP,QACA,IACM;CACN,MAAM,QAAQ,mBAAmB,QAAQ,EAAE;CAC3C,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,KAAK,KAAK,OAAO,CAAC,MAAM,KAAM;CAEhC,OAAO,IAAI,KAAK,EAAE;AACpB;;AAGA,SAAgB,gCACd,QACA,KACA;CACA,MAAM,yBAAS,IAAI,WAAA,EAA4B;CAC/C,MAAM,aAAa,IAAI,WAAW,OAAO,QAAA,GAA6B,gBAAgB;CAEtF,OAAO;EACL,aAAa,OAA0B;GACrC,oBAAoB,IAAI,KAAK,QAAQ,UAAU;GAC/C,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,mBAAmB,OAAwB,8BAA8B,QAAQ,EAAE;EACnF,eAAe,OAA0B;GACvC,4BAA4B,IAAI,GAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,eAAe,OAA0B;GACvC,4BAA4B,IAAI,KAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,gBAAgB,OAA0B;GACxC,oBAAoB,IAAI,KAAK,QAAQ,UAAU;GAC/C,OAAO,SAAS,QAAQ,MAAM;EAChC;CACF;AACF;;;;;;;;;;;;;ACcA,SAAgB,yBACd,OACA,OAAgC,CAAC,GACH;CAC9B,cAAc,KAAK;CACnB,cAAc,OAAO,KAAK,mBAAmB;CAE7C,MAAM,MAAM,KAAK,OAAO,KAAK;CAC7B,MAAM,MAAM,KAAK,OAAO;CACxB,MAAM,SAAwB,GAAG,MAAM;CACvC,MAAM,OAAO,YAAY,MAAM;CAC/B,MAAM,SAAS,gCAAgC,QAAQ,GAAG;CAE1D,OAAO;EACL,gBAAgB,OAAO,WAAW,IAAI,CAAC;EACvC,aAAa,SAAe,OAAO,WAAW,KAAK,QAAQ,CAAC;EAC5D,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,kBAAkB,OAAO;EACzB,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,oBAAoB,KAAK,aAAa,OAAO,OAAO,cAAc,IAAI,CAAC,CAAC;EACxE,aAAa,KAAK;CACpB;AACF"}
@@ -1,9 +0,0 @@
1
- //#region src/codecs/_kernel/rng.ts
2
- /** Default RNG: writes cryptographically random bytes via `crypto.getRandomValues`. */
3
- function defaultRng(target) {
4
- crypto.getRandomValues(target);
5
- }
6
- //#endregion
7
- export { defaultRng as t };
8
-
9
- //# sourceMappingURL=rng-DHxioKyI.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rng-DHxioKyI.mjs","names":[],"sources":["../src/codecs/_kernel/rng.ts"],"sourcesContent":["/** Default RNG: writes cryptographically random bytes via `crypto.getRandomValues`. */\nexport function defaultRng(target: Uint8Array): void {\n crypto.getRandomValues(target as Uint8Array<ArrayBuffer>);\n}\n"],"mappings":";;AACA,SAAgB,WAAW,QAA0B;CACnD,OAAO,gBAAgB,MAAiC;AAC1D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"timestamp-DBwVjDkg.mjs","names":[],"sources":["../src/codecs/timestamp/layout.ts","../src/codecs/timestamp/index.ts"],"sourcesContent":["import type { Id, Prefix } from \"../../types.js\";\nimport { toWireId } from \"../../wire/envelope.js\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\nimport {\n readTimestampMsFromBase32Suffix,\n timestampByteLength,\n writeTimestamp,\n} from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength: number = payloadByteLength - timestampByteLength;\n\n/** Writes a 16-byte timestamp-layout payload into codec-owned scratch. */\nfunction buildPayload(\n ms: number,\n rng: (target: Uint8Array) => void,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n rng(randomView);\n}\n\n/** Writes sentinel min/max random bytes into codec-owned scratch. */\nfunction buildSentinelPayload(\n ms: number,\n fill: number,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n randomView.fill(fill);\n}\n\n/** Decodes the creation timestamp from a trusted wire ID. */\nfunction extractTimestampFromId<Brand extends string>(prefix: Prefix<Brand>, id: Id<Brand>): Date {\n return new Date(readTimestampMsFromBase32Suffix(id.slice(prefix.length)));\n}\n\n/** Layout ops binder for the Timestamp variant. `extractTimestampFromId` is module-private; the binder exposes `extractTimestamp` for the codec constructor. */\nexport function createTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n rng: (target: Uint8Array) => void,\n) {\n // Per-codec scratch buffer. Shared across generateAt(), minIdForTime(),\n // maxIdForTime(), and exampleWireId() — all are synchronous and overwrite both\n // the timestamp and random slices before encoding, so successive callers see\n // their own freshly-written bytes. toWireId reads the buffer and returns an\n // independent string, so the caller never sees the buffer itself.\n const buffer = new Uint8Array(payloadByteLength);\n const randomView = new Uint8Array(buffer.buffer, timestampByteLength, randomByteLength);\n\n return {\n generateAt: (ms: number): Id<Brand> => {\n buildPayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n extractTimestamp: (id: Id<Brand>): Date => extractTimestampFromId(prefix, id),\n minIdForTime: (ms: number): Id<Brand> => {\n buildSentinelPayload(ms, 0x00, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n buildSentinelPayload(ms, 0xff, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n exampleWireId: (ms: number): Id<Brand> => {\n buildPayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n };\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { createTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport type { Id, JsonSchema, ParseResult, Prefix, StandardSchemaProps } from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\n\n/**\n * Configuration options for a codec instance.\n */\nexport type TimestampOptions = {\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes random bytes into `target` for ID generation. Defaults to a `crypto.randomUUID` fast path. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\ntype ResolvedTimestampOptions = Required<Pick<TimestampOptions, \"now\" | \"rng\">> &\n Pick<TimestampOptions, \"allowDuplicateBrand\">;\n\n/**\n * A brand-scoped codec for generating and validating public-facing IDs.\n *\n * Wire format: `{brand}_` plus 26 lowercase Crockford base32 characters encoding a\n * 16-byte payload (6-byte ms timestamp + 10 random bytes). IDs sort by creation\n * time in ascending order.\n *\n * For encrypted IDs, use `createOpaqueTimestampId` from `@smonn/ids/opaque`.\n */\nexport type TimestampCodec<Brand extends string> = {\n /** Produces a new canonical ID using the codec's `now` and `rng`. */\n generate(): Id<Brand>;\n /** Produces a new canonical ID with timestamp bytes from `date` and a fresh random tail. Throws on invalid dates. */\n generateAt(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>`. Trusts the type — use `safeParse()` at boundaries first. See ADR-0002.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /** Tight lower bound for any ID generated at `date` (random portion `0x00`). Throws on invalid dates. */\n minIdForTime(date: Date): Id<Brand>;\n /** Tight upper bound for any ID generated at `date` (random portion `0xff`). Throws on invalid dates. */\n maxIdForTime(date: Date): Id<Brand>;\n /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n// hex charCode → 0–15 nibble, for decoding UUIDv4 strings into bytes.\n// Covers ['0'-'9' = 48–57] and ['a'-'f' = 97–102]; UUIDs are lowercase per spec.\nconst hexCharCodeToNibble = new Uint8Array(128);\nfor (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;\nfor (let i = 0; i < 6; i++) hexCharCodeToNibble[97 + i] = 10 + i;\n\nconst defaultTimestampOptions: ResolvedTimestampOptions = {\n now: Date.now,\n // crypto.randomUUID is ~7× faster than crypto.getRandomValues in Node 24\n // (~84 ns vs ~610 ns for a 16-byte fill — likely because the UUID path has\n // a tight fixed-format fast path). We use the 122 random bits of a UUIDv4\n // string as our entropy source, harvesting 10 fully-random bytes from\n // positions where no version (hex 12) or variant (hex 16) bits sit.\n // String layout: \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\" — bytes 0–5 are\n // string[0..7]+string[9..12], bytes 6–9 are string[24..31].\n rng: (target) => {\n const s = crypto.randomUUID();\n target[0] =\n (hexCharCodeToNibble[s.charCodeAt(0)]! << 4) | hexCharCodeToNibble[s.charCodeAt(1)]!;\n target[1] =\n (hexCharCodeToNibble[s.charCodeAt(2)]! << 4) | hexCharCodeToNibble[s.charCodeAt(3)]!;\n target[2] =\n (hexCharCodeToNibble[s.charCodeAt(4)]! << 4) | hexCharCodeToNibble[s.charCodeAt(5)]!;\n target[3] =\n (hexCharCodeToNibble[s.charCodeAt(6)]! << 4) | hexCharCodeToNibble[s.charCodeAt(7)]!;\n target[4] =\n (hexCharCodeToNibble[s.charCodeAt(9)]! << 4) | hexCharCodeToNibble[s.charCodeAt(10)]!;\n target[5] =\n (hexCharCodeToNibble[s.charCodeAt(11)]! << 4) | hexCharCodeToNibble[s.charCodeAt(12)]!;\n target[6] =\n (hexCharCodeToNibble[s.charCodeAt(24)]! << 4) | hexCharCodeToNibble[s.charCodeAt(25)]!;\n target[7] =\n (hexCharCodeToNibble[s.charCodeAt(26)]! << 4) | hexCharCodeToNibble[s.charCodeAt(27)]!;\n target[8] =\n (hexCharCodeToNibble[s.charCodeAt(28)]! << 4) | hexCharCodeToNibble[s.charCodeAt(29)]!;\n target[9] =\n (hexCharCodeToNibble[s.charCodeAt(30)]! << 4) | hexCharCodeToNibble[s.charCodeAt(31)]!;\n },\n};\n\n/**\n * Creates a codec for `brand` (three lowercase a–z characters).\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n */\nexport function createTimestampId<Brand extends string>(\n brand: Brand,\n opts: TimestampOptions = {},\n): TimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const options = {\n now: opts.now ?? defaultTimestampOptions.now,\n rng: opts.rng ?? defaultTimestampOptions.rng,\n } satisfies ResolvedTimestampOptions;\n\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createTimestampLayoutOps(prefix, options.rng);\n\n return {\n generate: () => layout.generateAt(options.now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId(options.now())),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":"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"}
@@ -1,27 +0,0 @@
1
- import { o as decodeBase32 } from "./codec-shell-DvrTDa65.mjs";
2
- const timestampBase32Length = Math.ceil(48 / 5);
3
- /** Write the timestamp in big-endian; encoded via mod-256 to avoid 32-bit bitwise coercion. */
4
- function writeTimestamp(ms, buffer) {
5
- if (Number.isNaN(ms)) throw new Error("timestamp is not a number");
6
- if (!Number.isInteger(ms)) throw new Error("timestamp is not an integer");
7
- if (ms < 0) throw new Error("timestamp is negative");
8
- if (ms >= 2 ** 48) throw new Error("timestamp exceeds 48-bit range");
9
- for (let i = 5; i >= 0; i--) {
10
- buffer[i] = ms % 256;
11
- ms = Math.floor(ms / 256);
12
- }
13
- }
14
- /** Decode the first `timestampByteLength` bytes of a buffer as a big-endian unsigned millisecond timestamp. */
15
- function readTimestampMs(buffer) {
16
- let ms = 0;
17
- for (let i = 0; i < 6; i++) ms = ms * 256 + buffer[i];
18
- return ms;
19
- }
20
- /** Decodes ms from the first 10 base32 chars of a payload suffix (partial decode). */
21
- function readTimestampMsFromBase32Suffix(base32Suffix) {
22
- return readTimestampMs(decodeBase32(base32Suffix.slice(0, timestampBase32Length)));
23
- }
24
- //#endregion
25
- export { readTimestampMsFromBase32Suffix as n, writeTimestamp as r, readTimestampMs as t };
26
-
27
- //# sourceMappingURL=timestamp-bytes-DvhWHDa-.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"timestamp-bytes-DvhWHDa-.mjs","names":[],"sources":["../src/wire/timestamp-bytes.ts"],"sourcesContent":["import { decodeBase32 } from \"./base32.js\";\n\n// Timestamp byte layout: first N bytes of the plaintext payload encode a\n// big-endian Unix-ms timestamp. Shared by timestamp-family layouts.\nexport const timestampByteLength: number = 6;\n\nconst timestampBase32Length: number = Math.ceil((timestampByteLength * 8) / 5);\n\n/** Write the timestamp in big-endian; encoded via mod-256 to avoid 32-bit bitwise coercion. */\nexport function writeTimestamp(ms: number, buffer: Uint8Array): void {\n if (Number.isNaN(ms)) throw new Error(\"timestamp is not a number\");\n if (!Number.isInteger(ms)) throw new Error(\"timestamp is not an integer\");\n if (ms < 0) throw new Error(\"timestamp is negative\");\n if (ms >= 2 ** (timestampByteLength * 8)) {\n throw new Error(\"timestamp exceeds 48-bit range\");\n }\n for (let i = timestampByteLength - 1; i >= 0; i--) {\n buffer[i] = ms % 256;\n ms = Math.floor(ms / 256);\n }\n}\n\n/** Decode the first `timestampByteLength` bytes of a buffer as a big-endian unsigned millisecond timestamp. */\nexport function readTimestampMs(buffer: Uint8Array): number {\n let ms = 0;\n for (let i = 0; i < timestampByteLength; i++) ms = ms * 256 + buffer[i]!;\n return ms;\n}\n\n/** Decodes ms from the first 10 base32 chars of a payload suffix (partial decode). */\nexport function readTimestampMsFromBase32Suffix(base32Suffix: string): number {\n return readTimestampMs(decodeBase32(base32Suffix.slice(0, timestampBase32Length)));\n}\n"],"mappings":";AAMA,MAAM,wBAAgC,KAAK,KAAA,KAAiC,CAAC;;AAG7E,SAAgB,eAAe,IAAY,QAA0B;CACnE,IAAI,OAAO,MAAM,EAAE,GAAG,MAAM,IAAI,MAAM,2BAA2B;CACjE,IAAI,CAAC,OAAO,UAAU,EAAE,GAAG,MAAM,IAAI,MAAM,6BAA6B;CACxE,IAAI,KAAK,GAAG,MAAM,IAAI,MAAM,uBAAuB;CACnD,IAAI,MAAM,KAAA,IACR,MAAM,IAAI,MAAM,gCAAgC;CAElD,KAAK,IAAI,IAAA,GAA6B,KAAK,GAAG,KAAK;EACjD,OAAO,KAAK,KAAK;EACjB,KAAK,KAAK,MAAM,KAAK,GAAG;CAC1B;AACF;;AAGA,SAAgB,gBAAgB,QAA4B;CAC1D,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KAAK,KAAK,KAAK,MAAM,OAAO;CACrE,OAAO;AACT;;AAGA,SAAgB,gCAAgC,cAA8B;CAC5E,OAAO,gBAAgB,aAAa,aAAa,MAAM,GAAG,qBAAqB,CAAC,CAAC;AACnF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapped-CDTiPwNM.mjs","names":[],"sources":["../src/codecs/wrapped/layout.ts","../src/codecs/wrapped/key.ts","../src/codecs/wrapped/index.ts"],"sourcesContent":["import type { webcrypto } from \"node:crypto\";\nimport type { Id, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadBase32Length, payloadByteLength } from \"../../wire/invariants.js\";\n\nconst zeroIv = new Uint8Array(payloadByteLength);\nconst pkcsPad = 0x10;\nconst laneByteLength = 8;\nconst tagByteLength = 8;\n\ntype LayoutWrappingKey = {\n aesKey: webcrypto.CryptoKey;\n hmacKey: webcrypto.CryptoKey;\n};\n\ntype LayoutWrappedKind = \"u32\" | \"i32\" | \"u64\" | \"i64\";\ntype LayoutLookupKey<K extends LayoutWrappedKind> = K extends \"u32\" | \"i32\" ? number : bigint;\n\nfunction writeU32Lane(value: number, lane: Uint8Array): void {\n lane[0] = 0;\n lane[1] = 0;\n lane[2] = 0;\n lane[3] = 0;\n lane[4] = (value >>> 24) & 0xff;\n lane[5] = (value >>> 16) & 0xff;\n lane[6] = (value >>> 8) & 0xff;\n lane[7] = value & 0xff;\n}\n\nfunction readU32Lane(lane: Uint8Array): number | null {\n for (let i = 0; i < 4; i++) {\n if (lane[i] !== 0) return null;\n }\n return ((lane[4]! << 24) | (lane[5]! << 16) | (lane[6]! << 8) | lane[7]!) >>> 0;\n}\n\nfunction writeI32Lane(value: number, lane: Uint8Array): void {\n lane.fill(value < 0 ? 0xff : 0x00, 0, 4);\n new DataView(lane.buffer, lane.byteOffset, lane.byteLength).setInt32(4, value, false);\n}\n\nfunction readI32Lane(lane: Uint8Array): number | null {\n const signExtension = (lane[4]! & 0x80) === 0 ? 0x00 : 0xff;\n for (let i = 0; i < 4; i++) {\n if (lane[i] !== signExtension) return null;\n }\n return new DataView(lane.buffer, lane.byteOffset, lane.byteLength).getInt32(4, false);\n}\n\nfunction writeU64Lane(value: bigint, lane: Uint8Array): void {\n new DataView(lane.buffer, lane.byteOffset, lane.byteLength).setBigUint64(0, value, false);\n}\n\nfunction readU64Lane(lane: Uint8Array): bigint {\n return new DataView(lane.buffer, lane.byteOffset, lane.byteLength).getBigUint64(0, false);\n}\n\nfunction writeI64Lane(value: bigint, lane: Uint8Array): void {\n new DataView(lane.buffer, lane.byteOffset, lane.byteLength).setBigInt64(0, value, false);\n}\n\nfunction readI64Lane(lane: Uint8Array): bigint {\n return new DataView(lane.buffer, lane.byteOffset, lane.byteLength).getBigInt64(0, false);\n}\n\nfunction writeLane<K extends LayoutWrappedKind>(\n kind: K,\n value: LayoutLookupKey<K>,\n lane: Uint8Array,\n): void {\n if (kind === \"i32\") {\n writeI32Lane(value as number, lane);\n return;\n }\n if (kind === \"u64\") {\n writeU64Lane(value as bigint, lane);\n return;\n }\n if (kind === \"i64\") {\n writeI64Lane(value as bigint, lane);\n return;\n }\n writeU32Lane(value as number, lane);\n}\n\nfunction readLane<K extends LayoutWrappedKind>(\n kind: K,\n lane: Uint8Array,\n): LayoutLookupKey<K> | null {\n if (kind === \"u64\") return readU64Lane(lane) as LayoutLookupKey<K>;\n if (kind === \"i64\") return readI64Lane(lane) as LayoutLookupKey<K>;\n const value = kind === \"i32\" ? readI32Lane(lane) : readU32Lane(lane);\n return value as LayoutLookupKey<K> | null;\n}\n\nfunction writeLen32(value: number, target: Uint8Array, offset: number): void {\n target[offset] = (value >>> 24) & 0xff;\n target[offset + 1] = (value >>> 16) & 0xff;\n target[offset + 2] = (value >>> 8) & 0xff;\n target[offset + 3] = value & 0xff;\n}\n\nfunction hmacMessage(brand: string, kind: LayoutWrappedKind, lane: Uint8Array): Uint8Array {\n const encoder = new TextEncoder();\n const brandBytes = encoder.encode(brand);\n const kindBytes = encoder.encode(kind);\n const msgLen = 4 + brandBytes.length + 4 + kindBytes.length + lane.length;\n const message = new Uint8Array(msgLen);\n let offset = 0;\n writeLen32(brandBytes.length, message, offset);\n offset += 4;\n message.set(brandBytes, offset);\n offset += brandBytes.length;\n writeLen32(kindBytes.length, message, offset);\n offset += 4;\n message.set(kindBytes, offset);\n offset += kindBytes.length;\n message.set(lane, offset);\n return message;\n}\n\nasync function computeTag(\n key: LayoutWrappingKey,\n brand: string,\n kind: LayoutWrappedKind,\n lane: Uint8Array,\n): Promise<Uint8Array> {\n const signature = new Uint8Array(\n await crypto.subtle.sign(\n \"HMAC\",\n key.hmacKey,\n hmacMessage(brand, kind, lane) as Uint8Array<ArrayBuffer>,\n ),\n );\n return signature.subarray(0, tagByteLength);\n}\n\nfunction tagsEqual(a: Uint8Array, b: Uint8Array): boolean {\n /* v8 ignore next -- defensive guard; both call sites always pass tagByteLength-byte arrays */\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i]! ^ b[i]!;\n return diff === 0;\n}\n\nasync function encryptPayload(key: LayoutWrappingKey, plaintext: Uint8Array): Promise<Uint8Array> {\n const encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key.aesKey,\n plaintext as Uint8Array<ArrayBuffer>,\n ),\n );\n return encrypted.subarray(0, payloadByteLength);\n}\n\nasync function decryptPayload(key: LayoutWrappingKey, c1: Uint8Array): Promise<Uint8Array> {\n const c2Input = new Uint8Array(payloadByteLength);\n for (let i = 0; i < payloadByteLength; i++) c2Input[i] = pkcsPad ^ c1[i]!;\n const c2Encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key.aesKey,\n c2Input as Uint8Array<ArrayBuffer>,\n ),\n );\n const ciphertext = new Uint8Array(payloadByteLength * 2);\n ciphertext.set(c1, 0);\n ciphertext.set(c2Encrypted.subarray(0, payloadByteLength), payloadByteLength);\n return new Uint8Array(\n await crypto.subtle.decrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key.aesKey,\n ciphertext as Uint8Array<ArrayBuffer>,\n ),\n );\n}\n\nfunction buildPlaintext(lane: Uint8Array, tag: Uint8Array): Uint8Array {\n const plaintext = new Uint8Array(payloadByteLength);\n plaintext.set(lane, 0);\n plaintext.set(tag, laneByteLength);\n return plaintext;\n}\n\nasync function wrapLookupKey<Brand extends string, Kind extends LayoutWrappedKind>(\n prefix: Prefix<Brand>,\n brand: string,\n key: LayoutWrappingKey,\n kind: Kind,\n lookupKey: LayoutLookupKey<Kind>,\n): Promise<Id<Brand>> {\n const lane = new Uint8Array(laneByteLength);\n writeLane(kind, lookupKey, lane);\n const tag = await computeTag(key, brand, kind, lane);\n const encrypted = await encryptPayload(key, buildPlaintext(lane, tag));\n return toWireId(prefix, encrypted);\n}\n\nasync function tryUnwrapLookupKey<Brand extends string, Kind extends LayoutWrappedKind>(\n prefix: Prefix<Brand>,\n brand: string,\n key: LayoutWrappingKey,\n kind: Kind,\n id: Id<Brand>,\n): Promise<LayoutLookupKey<Kind> | null> {\n const plaintext = await decryptPayload(key, payloadBytesFromId(prefix, id));\n const lane = plaintext.subarray(0, laneByteLength);\n const tag = plaintext.subarray(laneByteLength, payloadByteLength);\n const expected = await computeTag(key, brand, kind, lane);\n if (!tagsEqual(tag, expected)) return null;\n return readLane(kind, lane);\n}\n\nfunction schemaExample<Brand extends string>(prefix: Prefix<Brand>): string {\n return prefix + \"0\".repeat(payloadBase32Length);\n}\n\nexport function createWrappedLayoutOps<Brand extends string, Kind extends LayoutWrappedKind>(\n prefix: Prefix<Brand>,\n brand: Brand,\n kind: Kind,\n keys: readonly LayoutWrappingKey[],\n) {\n const wrapKey = keys[0]!;\n return {\n wrap: (lookupKey: LayoutLookupKey<Kind>): Promise<Id<Brand>> =>\n wrapLookupKey(prefix, brand, wrapKey, kind, lookupKey),\n tryUnwrap: async (id: Id<Brand>): Promise<LayoutLookupKey<Kind> | null> => {\n for (const key of keys) {\n const lookupKey = await tryUnwrapLookupKey(prefix, brand, key, kind, id);\n if (lookupKey !== null) return lookupKey;\n }\n return null;\n },\n exampleWireId: (): Id<Brand> => schemaExample(prefix) as Id<Brand>,\n };\n}\n","import type { webcrypto } from \"node:crypto\";\nimport {\n assertValidKeyMaterialByteLength,\n assertValidKeyring,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\nexport { assertValidKeyring };\n\n/** Wire encoding for wrapping operator secret bytes (not Crockford base32). */\nexport type WrappingKeyFormat = \"hex\" | \"base64url\";\n\nconst aesInfo = new TextEncoder().encode(\"@smonn/ids/wrapped/aes\");\nconst hmacInfo = new TextEncoder().encode(\"@smonn/ids/wrapped/hmac\");\n\nconst SHA256_DIGEST_BYTES = 32;\n\ndeclare const wrappingKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for one operator wrapping secret.\n *\n * Holds derived AES and HMAC subkeys internally; callers never access subkeys\n * or raw `webcrypto.CryptoKey` values directly. Obtain handles via {@link importWrappingKey}\n * and pass them to `createWrappedKeyId` as the `keys` wrapping keyring.\n *\n * Distinct from the **Opaque key** used by `@smonn/ids/opaque` — one raw\n * secret must not silently serve both codecs without an explicit import.\n */\nexport type WrappingKey = {\n readonly [wrappingKeyBrand]: \"WrappingKey\";\n};\n\ntype WrappingKeyInternals = {\n keyDigest: Uint8Array;\n aesKey: webcrypto.CryptoKey;\n hmacKey: webcrypto.CryptoKey;\n};\n\nexport type WrappingKeyMaterial = {\n aesKey: webcrypto.CryptoKey;\n hmacKey: webcrypto.CryptoKey;\n};\n\nconst internals = new WeakMap<WrappingKey, WrappingKeyInternals>();\n\n/**\n * Import raw operator secret bytes into a {@link WrappingKey} handle.\n *\n * One raw secret derives into AES and HMAC subkeys held inside the returned\n * handle. Accepts 16, 24, or 32 bytes (AES-128 / AES-192 / AES-256 strength).\n * To store or transport key material, use {@link encodeWrappingKey} /\n * {@link decodeWrappingKey} (`\"hex\"` or `\"base64url\"` — not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 raw key bytes.\n */\nexport async function importWrappingKey(bytes: Uint8Array): Promise<WrappingKey> {\n assertValidKeyMaterialByteLength(bytes.length, \"wrapping\");\n const [aesKey, hmacKey, digestBuffer] = await Promise.all([\n deriveAesKey(bytes),\n deriveHmacKey(bytes),\n crypto.subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>),\n ]);\n const key = Object.freeze({}) as WrappingKey;\n internals.set(key, {\n keyDigest: new Uint8Array(digestBuffer),\n aesKey,\n hmacKey,\n });\n return key;\n}\n\n/**\n * Encode raw wrapping operator secret bytes for storage in env vars or secret managers.\n *\n * Supports `\"hex\"` (lowercase) and `\"base64url\"`. Output round-trips through\n * {@link decodeWrappingKey} back to the original bytes.\n */\nexport function encodeWrappingKey(bytes: Uint8Array, format: WrappingKeyFormat): string {\n return encodeKeyMaterial(bytes, format, \"wrapping\", \"wrapping\");\n}\n\n/**\n * Decode key material emitted by {@link encodeWrappingKey} back to raw bytes.\n *\n * The result can be passed directly to {@link importWrappingKey}.\n */\nexport function decodeWrappingKey(encoded: string, format: WrappingKeyFormat): Uint8Array {\n return decodeKeyMaterial(encoded, format, \"wrapping\", \"wrapping\");\n}\n\n/**\n * Returns true when two handles were imported from the same raw operator secret.\n *\n * Uses a constant-time comparison so duplicate detection over key material does\n * not leak the position of the first differing byte through a timing side channel.\n */\nexport function wrappingKeysEqual(a: WrappingKey, b: WrappingKey): boolean {\n const aDigest = getWrappingKeyInternals(a).keyDigest;\n const bDigest = getWrappingKeyInternals(b).keyDigest;\n let diff = 0;\n for (let i = 0; i < SHA256_DIGEST_BYTES; i++) {\n diff |= aDigest[i]! ^ bDigest[i]!;\n }\n return diff === 0;\n}\n\nexport function getWrappingKeyMaterial(key: WrappingKey): WrappingKeyMaterial {\n const keyInternals = getWrappingKeyInternals(key);\n return {\n aesKey: keyInternals.aesKey,\n hmacKey: keyInternals.hmacKey,\n };\n}\n\nfunction getWrappingKeyInternals(key: WrappingKey): WrappingKeyInternals {\n const keyInternals = internals.get(key);\n if (keyInternals === undefined) {\n throw new Error(\"invalid wrapping key\");\n }\n return keyInternals;\n}\n\nasync function deriveAesKey(bytes: Uint8Array): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: new Uint8Array(), info: aesInfo },\n base,\n { name: \"AES-CBC\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\nasync function deriveHmacKey(bytes: Uint8Array): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: new Uint8Array(), info: hmacInfo },\n base,\n { name: \"HMAC\", hash: \"SHA-256\", length: 256 },\n false,\n [\"sign\", \"verify\"],\n );\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nimport { createWrappedLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport type {\n Id,\n JsonSchema,\n ParseError,\n ParseResult,\n Prefix,\n StandardSchemaProps,\n} from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\nimport {\n assertValidKeyring,\n decodeWrappingKey,\n encodeWrappingKey,\n getWrappingKeyMaterial,\n importWrappingKey,\n type WrappingKey,\n type WrappingKeyFormat,\n wrappingKeysEqual,\n} from \"./key.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode };\nexport {\n decodeWrappingKey,\n encodeWrappingKey,\n importWrappingKey,\n type WrappingKey,\n type WrappingKeyFormat,\n};\n\nexport type WrappedKind = \"u32\" | \"i32\" | \"u64\" | \"i64\";\n\ntype LookupKeyForKind<K extends WrappedKind> = K extends \"u32\" | \"i32\" ? number : bigint;\n\n/**\n * Result returned by {@link WrappedKeyCodec.safeUnwrap}.\n *\n * On success, `id` is the canonical {@link Id} and `lookupKey` is the recovered\n * integer (`number` for 32-bit kinds, `bigint` for 64-bit kinds).\n * On failure, `error` is a {@link ParseError} for structural problems or\n * `\"verification_failed\"` when the payload is structurally valid but the\n * verification tag does not match any entry in the wrapping keyring.\n */\nexport type UnwrapResult<Brand extends string, Kind extends WrappedKind> =\n | { ok: true; id: Id<Brand>; lookupKey: LookupKeyForKind<Kind> }\n | { ok: false; error: ParseError | \"verification_failed\" };\n\n/**\n * Codec returned by {@link createWrappedKeyId}.\n *\n * Wraps a caller-owned integer **lookup key** into a public {@link Id} and\n * recovers it on unwrap. The codec is deterministic under fixed key material:\n * the same lookup key always yields the same public ID (**equality leakage**).\n *\n * - `wrap` / `unwrap` / `safeUnwrap` are async (WebCrypto).\n * - `is`, `parse`, `safeParse`, and `toJsonSchema` are synchronous and require\n * no key material — they validate prefix and base32 shape only.\n * - The `Kind` type parameter drives value types at the TypeScript boundary:\n * `u32` / `i32` → `number`; `u64` / `i64` → `bigint`.\n */\nexport type WrappedKeyCodec<Brand extends string, Kind extends WrappedKind> = {\n /**\n * Wrap `lookupKey` into a public ID using the current (first) wrapping key.\n *\n * Throws if `lookupKey` is out of range or the wrong JS type for `Kind`.\n */\n wrap(lookupKey: LookupKeyForKind<Kind>): Promise<Id<Brand>>;\n /**\n * Verify the payload of a trusted `Id<Brand>` and return the lookup key.\n *\n * Throws `IdsError` with `code: \"verification_failed\"` if no entry in the\n * wrapping keyring matches the payload tag. Use {@link safeUnwrap} for\n * untrusted input.\n */\n unwrap(id: Id<Brand>): Promise<LookupKeyForKind<Kind>>;\n /**\n * Non-throwing path for untrusted input.\n *\n * Structurally parses `input` first (same rules as {@link safeParse}), then\n * verifies the payload. Returns `{ ok: false, error }` on any failure —\n * `ParseError` for structural problems or `\"verification_failed\"` for tag\n * mismatch — without throwing. Tamper, wrong keyring, and revoked-key cases\n * all surface as `\"verification_failed\"`.\n */\n safeUnwrap(input: unknown): Promise<UnwrapResult<Brand, Kind>>;\n /** Strict type guard: `true` only for already-canonical `Id<Brand>` strings. */\n is(value: unknown): value is Id<Brand>;\n /** Normalise to canonical form, or throw on parse failure. */\n parse(value: unknown): Id<Brand>;\n /** Normalise to canonical form, or return `{ ok: false, error }`. */\n safeParse(value: unknown): ParseResult<Brand>;\n toJsonSchema(): JsonSchema;\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\nexport type WrappedKeyOptions<K extends WrappedKind> = {\n kind: K;\n keys: [WrappingKey, ...WrappingKey[]];\n allowDuplicateBrand?: boolean;\n};\n\nconst u32Max = 0xffff_ffff;\nconst i32Min = -0x8000_0000;\nconst i32Max = 0x7fff_ffff;\nconst u64Max = 0xffff_ffff_ffff_ffffn;\nconst i64Min = -(1n << 63n);\nconst i64Max = (1n << 63n) - 1n;\n\nfunction assertSupportedKind(kind: WrappedKind): asserts kind is WrappedKind {\n if (kind !== \"u32\" && kind !== \"i32\" && kind !== \"u64\" && kind !== \"i64\") {\n throw new IdsError(\"invalid_kind\", \"invalid wrapped key kind: expected u32, i32, u64, or i64\");\n }\n}\n\nfunction assertU32LookupKey(lookupKey: unknown): asserts lookupKey is number {\n if (\n typeof lookupKey !== \"number\" ||\n !Number.isInteger(lookupKey) ||\n Object.is(lookupKey, -0) ||\n lookupKey < 0 ||\n lookupKey > u32Max\n ) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid u32 lookup key: expected integer in [0, ${u32Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertI32LookupKey(lookupKey: unknown): asserts lookupKey is number {\n if (\n typeof lookupKey !== \"number\" ||\n !Number.isInteger(lookupKey) ||\n Object.is(lookupKey, -0) ||\n lookupKey < i32Min ||\n lookupKey > i32Max\n ) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid i32 lookup key: expected integer in [${i32Min}, ${i32Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertU64LookupKey(lookupKey: unknown): asserts lookupKey is bigint {\n if (typeof lookupKey !== \"bigint\" || lookupKey < 0n || lookupKey > u64Max) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid u64 lookup key: expected bigint in [0, ${u64Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertI64LookupKey(lookupKey: unknown): asserts lookupKey is bigint {\n if (typeof lookupKey !== \"bigint\" || lookupKey < i64Min || lookupKey > i64Max) {\n throw new IdsError(\n \"invalid_lookup_key\",\n `invalid i64 lookup key: expected bigint in [${i64Min}, ${i64Max}], got ${lookupKey}`,\n );\n }\n}\n\nfunction assertLookupKey<Kind extends WrappedKind>(\n kind: Kind,\n lookupKey: unknown,\n): asserts lookupKey is LookupKeyForKind<Kind> {\n if (kind === \"i32\") {\n assertI32LookupKey(lookupKey);\n return;\n }\n if (kind === \"u64\") {\n assertU64LookupKey(lookupKey);\n return;\n }\n if (kind === \"i64\") {\n assertI64LookupKey(lookupKey);\n return;\n }\n assertU32LookupKey(lookupKey);\n}\n\n/**\n * Construct a {@link WrappedKeyCodec} for `brand` and the given `kind`.\n *\n * `opts.kind` fixes the integer type at construction time — one brand, one\n * kind. `opts.keys` is a non-empty ordered wrapping keyring: the first entry\n * is current (used by `wrap`); all entries are tried on `unwrap`; duplicate\n * operator secrets are rejected at construction.\n *\n * @example\n * ```ts\n * const key = await importWrappingKey(new Uint8Array(32));\n * const invoices = createWrappedKeyId(\"inv\", { kind: \"u32\", keys: [key] });\n *\n * const id = await invoices.wrap(42); // Id<\"inv\">\n * await invoices.unwrap(id); // 42\n * ```\n */\nexport function createWrappedKeyId<Brand extends string, Kind extends WrappedKind>(\n brand: Brand,\n opts: WrappedKeyOptions<Kind>,\n): WrappedKeyCodec<Brand, Kind> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n assertSupportedKind(opts.kind);\n assertValidKeyring(opts.keys, wrappingKeysEqual, \"wrapping\");\n const layoutKeys = opts.keys.map(getWrappingKeyMaterial);\n\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createWrappedLayoutOps(prefix, brand, opts.kind, layoutKeys);\n\n return {\n wrap: async (lookupKey) => {\n assertLookupKey(opts.kind, lookupKey);\n return layout.wrap(lookupKey);\n },\n unwrap: async (id) => {\n const lookupKey = await layout.tryUnwrap(id);\n if (lookupKey === null) {\n throw new IdsError(\"verification_failed\", \"verification failed\");\n }\n return lookupKey;\n },\n safeUnwrap: async (input) => {\n const parsed = wire.safeParse(input);\n if (!parsed.ok) return parsed;\n const lookupKey = await layout.tryUnwrap(parsed.id);\n if (lookupKey === null) return { ok: false, error: \"verification_failed\" };\n return { ok: true, id: parsed.id, lookupKey };\n },\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":"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"}