@smonn/ids 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +129 -20
  2. package/dist/adapter-types-BY-wrYYB.mjs +27 -0
  3. package/dist/adapter-types-BY-wrYYB.mjs.map +1 -0
  4. package/dist/adapter-types-unUcmMXC.d.mts +20 -0
  5. package/dist/adapter-types-unUcmMXC.d.mts.map +1 -0
  6. package/dist/cli.mjs +213 -47
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{codec-shell-DH-UO4UR.mjs → codec-shell-C7_B4oum.mjs} +4 -3
  9. package/dist/codec-shell-C7_B4oum.mjs.map +1 -0
  10. package/dist/{drizzle-CeSni5PB.d.mts → drizzle-CHtyDXpv.d.mts} +4 -15
  11. package/dist/drizzle-CHtyDXpv.d.mts.map +1 -0
  12. package/dist/drizzle.d.mts +3 -2
  13. package/dist/drizzle.mjs +2 -3
  14. package/dist/drizzle.mjs.map +1 -1
  15. package/dist/express.d.mts +2 -5
  16. package/dist/express.d.mts.map +1 -1
  17. package/dist/express.mjs +3 -8
  18. package/dist/express.mjs.map +1 -1
  19. package/dist/fastify.d.mts +12 -6
  20. package/dist/fastify.d.mts.map +1 -1
  21. package/dist/fastify.mjs +10 -8
  22. package/dist/fastify.mjs.map +1 -1
  23. package/dist/hono.d.mts +2 -5
  24. package/dist/hono.d.mts.map +1 -1
  25. package/dist/hono.mjs +3 -8
  26. package/dist/hono.mjs.map +1 -1
  27. package/dist/index.mjs +1 -1
  28. package/dist/key-material-DUHhmMq-.mjs +137 -0
  29. package/dist/key-material-DUHhmMq-.mjs.map +1 -0
  30. package/dist/kysely.d.mts +1 -1
  31. package/dist/kysely.d.mts.map +1 -1
  32. package/dist/{opaque-uvjOFY_0.mjs → opaque-BQOlZ2oD.mjs} +8 -44
  33. package/dist/opaque-BQOlZ2oD.mjs.map +1 -0
  34. package/dist/opaque.d.mts +8 -0
  35. package/dist/opaque.d.mts.map +1 -1
  36. package/dist/opaque.mjs +1 -1
  37. package/dist/prisma.d.mts +3 -16
  38. package/dist/prisma.d.mts.map +1 -1
  39. package/dist/prisma.mjs +2 -3
  40. package/dist/prisma.mjs.map +1 -1
  41. package/dist/{reverse-BgFU6JHw.mjs → reverse-C12D1btB.mjs} +4 -6
  42. package/dist/reverse-C12D1btB.mjs.map +1 -0
  43. package/dist/reverse.d.mts.map +1 -1
  44. package/dist/reverse.mjs +1 -1
  45. package/dist/rng-CPJOx_nE.mjs +9 -0
  46. package/dist/rng-CPJOx_nE.mjs.map +1 -0
  47. package/dist/signed-CwqKTFaQ.mjs +207 -0
  48. package/dist/signed-CwqKTFaQ.mjs.map +1 -0
  49. package/dist/signed.d.mts +112 -1
  50. package/dist/signed.d.mts.map +1 -1
  51. package/dist/signed.mjs +2 -99
  52. package/dist/{timestamp-B5_UCzc6.mjs → timestamp-BjIMQkJf.mjs} +3 -3
  53. package/dist/{timestamp-B5_UCzc6.mjs.map → timestamp-BjIMQkJf.mjs.map} +1 -1
  54. package/dist/{timestamp-bytes-BBY7JI33.mjs → timestamp-bytes-Bbg6Y66Z.mjs} +2 -2
  55. package/dist/{timestamp-bytes-BBY7JI33.mjs.map → timestamp-bytes-Bbg6Y66Z.mjs.map} +1 -1
  56. package/dist/{wrapped-0vL72Nje.mjs → wrapped-DKOsN_dq.mjs} +16 -50
  57. package/dist/wrapped-DKOsN_dq.mjs.map +1 -0
  58. package/dist/wrapped.d.mts.map +1 -1
  59. package/dist/wrapped.mjs +1 -1
  60. package/package.json +1 -1
  61. package/dist/adapter-types-oHCCSgOO.d.mts +0 -12
  62. package/dist/adapter-types-oHCCSgOO.d.mts.map +0 -1
  63. package/dist/bytes-lhzKVaBV.mjs +0 -53
  64. package/dist/bytes-lhzKVaBV.mjs.map +0 -1
  65. package/dist/codec-shell-DH-UO4UR.mjs.map +0 -1
  66. package/dist/drizzle-CeSni5PB.d.mts.map +0 -1
  67. package/dist/opaque-uvjOFY_0.mjs.map +0 -1
  68. package/dist/reverse-BgFU6JHw.mjs.map +0 -1
  69. package/dist/signed.mjs.map +0 -1
  70. package/dist/wrapped-0vL72Nje.mjs.map +0 -1
package/dist/signed.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-DTr4i6Ic.mjs";
2
+ import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, r as ParseError, t as Id } from "./types-g7CiQDyE.mjs";
2
3
 
3
4
  //#region src/signing-key.d.ts
4
5
  /** Wire encoding for signing key raw key bytes (not Crockford base32). */
@@ -52,5 +53,115 @@ declare function encodeSigningKey(bytes: Uint8Array, format: SigningKeyFormat):
52
53
  */
53
54
  declare function decodeSigningKey(encoded: string, format: SigningKeyFormat): Uint8Array;
54
55
  //#endregion
55
- export { IdsError, type IdsErrorCode, type SigningKey, type SigningKeyFormat, decodeSigningKey, encodeSigningKey, importSigningKey, isIdsError };
56
+ //#region src/signed.d.ts
57
+ /**
58
+ * Configuration options for a Signed Timestamp codec instance.
59
+ */
60
+ type SignedTimestampOptions = {
61
+ /**
62
+ * Non-empty ordered signing keyring. The first entry is current — the only one
63
+ * `generate` / `generateAt` sign with. `verify` / `safeVerify` trial every entry
64
+ * until the tag matches. Duplicate raw secrets are rejected at construction.
65
+ */
66
+ keys: [SigningKey, ...SigningKey[]]; /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */
67
+ now?: () => number; /** Writes 5 random bytes into `target` for the random tail. Defaults to `crypto.getRandomValues`. */
68
+ rng?: (target: Uint8Array) => void; /** If true, silences the duplicate-brand warning in non-production environments. */
69
+ allowDuplicateBrand?: boolean;
70
+ };
71
+ /**
72
+ * Result returned by {@link SignedTimestampCodec.safeVerify}.
73
+ *
74
+ * On success, `id` is the canonical {@link Id}.
75
+ * On failure, `error` is a {@link ParseError} for structural problems or
76
+ * `"verification_failed"` when the HMAC tag does not match any entry in the
77
+ * signing keyring.
78
+ */
79
+ type SafeVerifyResult<Brand extends string> = {
80
+ ok: true;
81
+ id: Id<Brand>;
82
+ } | {
83
+ ok: false;
84
+ error: ParseError | "verification_failed";
85
+ };
86
+ /**
87
+ * Codec returned by {@link createSignedTimestampId}.
88
+ *
89
+ * Keeps the 6-byte millisecond timestamp **readable and sortable** like the
90
+ * Timestamp codec, but replaces half of the 10-byte random tail with a truncated
91
+ * HMAC tag, making IDs **tamper-evident and verifiable without a database lookup**.
92
+ *
93
+ * Byte layout: `ts6 ‖ rand5 ‖ tag5` where the 40-bit tag =
94
+ * `trunc(HMAC-SHA256(hmacKey, brand ‖ ts6 ‖ rand5), 40)`.
95
+ *
96
+ * - Async (HMAC): `generate`, `generateAt`, `verify`, `safeVerify`.
97
+ * - Sync (no key / plaintext timestamp): all other methods.
98
+ */
99
+ type SignedTimestampCodec<Brand extends string> = {
100
+ /** Produces a canonical ID signed with the current (first) key. */generate(): Promise<Id<Brand>>;
101
+ /**
102
+ * Produces a canonical ID with timestamp from `date`, signed with the current key.
103
+ * Throws on invalid dates.
104
+ */
105
+ generateAt(date: Date): Promise<Id<Brand>>;
106
+ /**
107
+ * Recomputes the HMAC tag across every keyring entry.
108
+ *
109
+ * Throws `IdsError` with `code: "verification_failed"` if no entry matches.
110
+ * Tamper of the brand, timestamp bytes, or random bytes all fail here.
111
+ */
112
+ verify(id: Id<Brand>): Promise<void>;
113
+ /**
114
+ * Non-throwing path for untrusted input.
115
+ *
116
+ * Structurally parses `input` first (same rules as {@link safeParse}), then
117
+ * verifies the HMAC tag. Returns `{ ok: false, error }` on any failure —
118
+ * {@link ParseError} for structural problems or `"verification_failed"` for tag
119
+ * mismatch — without throwing.
120
+ */
121
+ safeVerify(input: unknown): Promise<SafeVerifyResult<Brand>>;
122
+ /**
123
+ * Decodes the creation `Date` from an `Id<Brand>`.
124
+ * Sync — the 6-byte timestamp is plaintext. Trusts the type; use `safeParse()` at boundaries first.
125
+ */
126
+ extractTimestamp(id: Id<Brand>): Date;
127
+ /**
128
+ * Tight lower bound sentinel for range scans (`ts(t) ‖ 0x00×10`).
129
+ * **Not verifiable** — carries no valid tag.
130
+ */
131
+ minIdForTime(date: Date): Id<Brand>;
132
+ /**
133
+ * Tight upper bound sentinel for range scans (`ts(t) ‖ 0xff×10`).
134
+ * **Not verifiable** — carries no valid tag.
135
+ */
136
+ maxIdForTime(date: Date): Id<Brand>;
137
+ /**
138
+ * Strict type guard: `true` only for already-canonical `Id<Brand>` strings.
139
+ * For untrusted input, use `safeParse()` or `safeVerify()` instead.
140
+ */
141
+ is(value: unknown): value is Id<Brand>; /** Normalise to canonical form, or throw on parse failure. */
142
+ parse(value: unknown): Id<Brand>; /** Normalise to canonical form, or return `{ ok: false, error }`. */
143
+ safeParse(value: unknown): ParseResult<Brand>; /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */
144
+ toJsonSchema(): JsonSchema; /** Standard Schema validate entry point. */
145
+ readonly "~standard": StandardSchemaProps<Brand>;
146
+ };
147
+ /**
148
+ * Construct a {@link SignedTimestampCodec} for `brand`.
149
+ *
150
+ * `opts.keys` is a non-empty ordered signing keyring — the first entry is current
151
+ * (used by `generate` / `generateAt`); all entries are tried on `verify` /
152
+ * `safeVerify`; duplicate operator secrets are rejected at construction.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * const key = await importSigningKey(new Uint8Array(32));
157
+ * const usr = createSignedTimestampId("usr", { keys: [key] });
158
+ *
159
+ * const id = await usr.generate(); // Id<"usr">
160
+ * await usr.verify(id); // passes
161
+ * usr.extractTimestamp(id); // Date — sync, timestamp is plaintext
162
+ * ```
163
+ */
164
+ declare function createSignedTimestampId<Brand extends string>(brand: Brand, opts: SignedTimestampOptions): SignedTimestampCodec<Brand>;
165
+ //#endregion
166
+ export { IdsError, type IdsErrorCode, SafeVerifyResult, SignedTimestampCodec, SignedTimestampOptions, type SigningKey, type SigningKeyFormat, createSignedTimestampId, decodeSigningKey, encodeSigningKey, importSigningKey, isIdsError };
56
167
  //# sourceMappingURL=signed.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"signed.d.mts","names":[],"sources":["../src/signing-key.ts"],"mappings":";;;;KAIY,gBAAA;AAAA,cAME,eAAA;AANd;;;;AAAY;AAA2B;;;;AAMzB;AAcd;;AApBA,KAoBY,UAAA;EAAA,UACA,eAAA;AAAA;AAqBZ;;;;;;;;;;;AAAA,iBAAsB,gBAAA,CAAiB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,UAAA;;AAAA;AAiBnE;;;;;;;iBAAgB,gBAAA,CAAiB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,gBAAA;;AAAA;AAgB5D;;;;;;;iBAAgB,gBAAA,CAAiB,OAAA,UAAiB,MAAA,EAAQ,gBAAA,GAAmB,UAAA"}
1
+ {"version":3,"file":"signed.d.mts","names":[],"sources":["../src/signing-key.ts","../src/signed.ts"],"mappings":";;;;;KAUY,gBAAA;AAAA,cAIE,eAAA;;;;AAJF;AAA2B;;;;AAIzB;AAcd;;;KAAY,UAAA;EAAA,UACA,eAAA;AAAA;;;;;;;;;;;;iBAqBU,gBAAA,CAAiB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,UAAA;AAAA;AAiBnE;;;;;;;;AAjBmE,iBAiBnD,gBAAA,CAAiB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,gBAAA;AAAA;AAa5D;;;;;;;;AAb4D,iBAa5C,gBAAA,CAAiB,OAAA,UAAiB,MAAA,EAAQ,gBAAA,GAAmB,UAAA;;;;;AAtEjE;KC4BA,sBAAA;EDxBE;;;AAAA;AAcd;ECgBE,IAAA,GAAO,UAAA,KAAe,UAAA;EAEtB,GAAA,iBDjBU;ECmBV,GAAA,IAAO,MAAA,EAAQ,UAAA,WDEK;ECApB,mBAAA;AAAA;;;;;;;;;KAWU,gBAAA;EACN,EAAA;EAAU,EAAA,EAAI,EAAA,CAAG,KAAA;AAAA;EACjB,EAAA;EAAW,KAAA,EAAO,UAAA;AAAA;;;;;ADIoC;AAa5D;;;;;;;;KCFY,oBAAA;EDEiE,mECA3E,QAAA,IAAY,OAAA,CAAQ,EAAA,CAAG,KAAA;;;AA1CzB;;EA+CE,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,OAAA,CAAQ,EAAA,CAAG,KAAA;;;;;;;EAOnC,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,OAAA;;;;;;;;AA1CvB;EAmDA,UAAA,CAAW,KAAA,YAAiB,OAAA,CAAQ,gBAAA,CAAiB,KAAA;EAxC3C;;;;EA6CV,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,IAAA;EA3CX;;;;EAgDtB,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;EAK7B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;EArDP;AAAA;AAexB;;EA2CE,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;EAEhC,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAE1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;EAEvC,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;;;;;;;;;;;;;;;;;;iBAoB5B,uBAAA,uBACd,KAAA,EAAO,KAAA,EACP,IAAA,EAAM,sBAAA,GACL,oBAAA,CAAqB,KAAA"}
package/dist/signed.mjs CHANGED
@@ -1,100 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { i as encodeHex, n as decodeHex, r as encodeBase64Url, t as decodeBase64Url } from "./bytes-lhzKVaBV.mjs";
3
- //#region src/signing-key.ts
4
- const validKeyByteLengths = new Set([
5
- 16,
6
- 24,
7
- 32
8
- ]);
9
- const hmacInfo = new TextEncoder().encode("ids/signed-timestamp/hmac");
10
- const internals = /* @__PURE__ */ new WeakMap();
11
- /**
12
- * Import raw operator key material into a {@link SigningKey} handle.
13
- *
14
- * Derives a single HMAC-SHA-256 key via HKDF under the domain-separation label
15
- * `ids/signed-timestamp/hmac`. Accepts 16, 24, or 32 bytes. To store or
16
- * transport key material, use {@link encodeSigningKey} / {@link decodeSigningKey}
17
- * (`"hex"` or `"base64url"` — not Crockford base32).
18
- *
19
- * @param bytes - 16, 24, or 32 raw key bytes.
20
- * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
21
- */
22
- async function importSigningKey(bytes) {
23
- assertValidKeyByteLength(bytes.length);
24
- const hmacKey = await deriveHmacKey(bytes);
25
- const key = Object.freeze({});
26
- internals.set(key, {
27
- rawBytes: bytes.slice(),
28
- hmacKey
29
- });
30
- return key;
31
- }
32
- /**
33
- * Encode raw signing operator key material for storage in env vars or secret managers.
34
- *
35
- * Supports `"hex"` (lowercase) and `"base64url"`. Output round-trips through
36
- * {@link decodeSigningKey} back to the original bytes.
37
- *
38
- * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
39
- * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
40
- */
41
- function encodeSigningKey(bytes, format) {
42
- assertSigningKeyFormat(format);
43
- assertValidKeyByteLength(bytes.length);
44
- if (format === "hex") return encodeHex(bytes);
45
- return encodeBase64Url(bytes);
46
- }
47
- /**
48
- * Decode key material emitted by {@link encodeSigningKey} back to raw bytes.
49
- *
50
- * The result can be passed directly to {@link importSigningKey}.
51
- *
52
- * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
53
- * @throws {IdsError} `invalid_key_encoding` if the string is malformed for its format.
54
- * @throws {IdsError} `invalid_key_length` if the decoded bytes are not 16, 24, or 32 bytes.
55
- */
56
- function decodeSigningKey(encoded, format) {
57
- assertSigningKeyFormat(format);
58
- let bytes;
59
- if (format === "hex") {
60
- if (encoded.length === 0 || encoded.length % 2 !== 0) throw new IdsError("invalid_key_encoding", "invalid hex key: length must be a positive even number of characters");
61
- if (!/^[0-9a-fA-F]+$/.test(encoded)) throw new IdsError("invalid_key_encoding", "invalid hex key: expected [0-9a-fA-F] only");
62
- bytes = decodeHex(encoded);
63
- } else try {
64
- bytes = decodeBase64Url(encoded);
65
- } catch {
66
- throw new IdsError("invalid_key_encoding", "invalid base64url key");
67
- }
68
- assertValidKeyByteLength(bytes.length);
69
- return bytes;
70
- }
71
- async function deriveHmacKey(bytes) {
72
- const base = await crypto.subtle.importKey("raw", bytes, "HKDF", false, ["deriveKey"]);
73
- return crypto.subtle.deriveKey({
74
- name: "HKDF",
75
- hash: "SHA-256",
76
- salt: new Uint8Array(),
77
- info: hmacInfo
78
- }, base, {
79
- name: "HMAC",
80
- hash: "SHA-256",
81
- length: 256
82
- }, false, ["sign", "verify"]);
83
- }
84
- function assertValidKeyByteLength(byteLength) {
85
- if (!validKeyByteLengths.has(byteLength)) throw new IdsError("invalid_key_length", `invalid signing key length: expected 16, 24, or 32 bytes, got ${byteLength}`);
86
- }
87
- function assertSigningKeyFormat(format) {
88
- if (format !== "hex" && format !== "base64url") throw new IdsError("invalid_key_format", `invalid signing key format: expected hex or base64url, got '${formatForError(format)}'`);
89
- }
90
- function formatForError(value) {
91
- try {
92
- return String(value);
93
- } catch {
94
- return "[unprintable]";
95
- }
96
- }
97
- //#endregion
98
- export { IdsError, decodeSigningKey, encodeSigningKey, importSigningKey, isIdsError };
99
-
100
- //# sourceMappingURL=signed.mjs.map
2
+ import { i as importSigningKey, n as decodeSigningKey, r as encodeSigningKey, t as createSignedTimestampId } from "./signed-CwqKTFaQ.mjs";
3
+ export { IdsError, createSignedTimestampId, decodeSigningKey, encodeSigningKey, importSigningKey, isIdsError };
@@ -1,5 +1,5 @@
1
- import { a as toWireId, n as registerBrand, s as validateBrand, t as wireMethods } from "./codec-shell-DH-UO4UR.mjs";
2
- import { n as readTimestampMsFromBase32Suffix, r as writeTimestamp } from "./timestamp-bytes-BBY7JI33.mjs";
1
+ import { a as toWireId, n as registerBrand, s as validateBrand, t as wireMethods } from "./codec-shell-C7_B4oum.mjs";
2
+ import { n as readTimestampMsFromBase32Suffix, r as writeTimestamp } from "./timestamp-bytes-Bbg6Y66Z.mjs";
3
3
  //#region src/layouts/timestamp.ts
4
4
  const randomByteLength = 10;
5
5
  /** Writes a 16-byte timestamp-layout payload into codec-owned scratch. */
@@ -93,4 +93,4 @@ function createTimestampId(brand, opts = {}) {
93
93
  //#endregion
94
94
  export { createTimestampId as t };
95
95
 
96
- //# sourceMappingURL=timestamp-B5_UCzc6.mjs.map
96
+ //# sourceMappingURL=timestamp-BjIMQkJf.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"timestamp-B5_UCzc6.mjs","names":[],"sources":["../src/layouts/timestamp.ts","../src/timestamp.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 \"./brand.js\";\nimport { createTimestampLayoutOps } from \"./layouts/timestamp.js\";\nimport { registerBrand } from \"./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
+ {"version":3,"file":"timestamp-BjIMQkJf.mjs","names":[],"sources":["../src/layouts/timestamp.ts","../src/timestamp.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 \"./brand.js\";\nimport { createTimestampLayoutOps } from \"./layouts/timestamp.js\";\nimport { registerBrand } from \"./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,4 +1,4 @@
1
- import { o as decodeBase32 } from "./codec-shell-DH-UO4UR.mjs";
1
+ import { o as decodeBase32 } from "./codec-shell-C7_B4oum.mjs";
2
2
  const timestampBase32Length = Math.ceil(48 / 5);
3
3
  /** Write the timestamp in big-endian; encoded via mod-256 to avoid 32-bit bitwise coercion. */
4
4
  function writeTimestamp(ms, buffer) {
@@ -23,4 +23,4 @@ function readTimestampMsFromBase32Suffix(base32Suffix) {
23
23
  //#endregion
24
24
  export { readTimestampMsFromBase32Suffix as n, writeTimestamp as r, readTimestampMs as t };
25
25
 
26
- //# sourceMappingURL=timestamp-bytes-BBY7JI33.mjs.map
26
+ //# sourceMappingURL=timestamp-bytes-Bbg6Y66Z.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"timestamp-bytes-BBY7JI33.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 (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,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
+ {"version":3,"file":"timestamp-bytes-Bbg6Y66Z.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 (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,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,6 +1,6 @@
1
1
  import { t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { a as toWireId, i as payloadBytesFromId, n as registerBrand, r as payloadBase32Length, s as validateBrand, t as wireMethods } from "./codec-shell-DH-UO4UR.mjs";
3
- import { i as encodeHex, n as decodeHex, r as encodeBase64Url, t as decodeBase64Url } from "./bytes-lhzKVaBV.mjs";
2
+ import { a as toWireId, i as payloadBytesFromId, n as registerBrand, r as payloadBase32Length, s as validateBrand, t as wireMethods } from "./codec-shell-C7_B4oum.mjs";
3
+ import { i as encodeKeyMaterial, n as assertValidKeyring, r as decodeKeyMaterial, t as assertValidKeyMaterialByteLength } from "./key-material-DUHhmMq-.mjs";
4
4
  //#region src/layouts/wrapped.ts
5
5
  const zeroIv = new Uint8Array(16);
6
6
  const pkcsPad = 16;
@@ -133,11 +133,6 @@ function createWrappedLayoutOps(prefix, brand, kind, keys) {
133
133
  }
134
134
  //#endregion
135
135
  //#region src/wrapping-key.ts
136
- const validKeyByteLengths = new Set([
137
- 16,
138
- 24,
139
- 32
140
- ]);
141
136
  const aesInfo = new TextEncoder().encode("@smonn/ids/wrapped/aes/v1");
142
137
  const hmacInfo = new TextEncoder().encode("@smonn/ids/wrapped/hmac/v1");
143
138
  const internals = /* @__PURE__ */ new WeakMap();
@@ -152,7 +147,7 @@ const internals = /* @__PURE__ */ new WeakMap();
152
147
  * @param bytes - 16, 24, or 32 raw key bytes.
153
148
  */
154
149
  async function importWrappingKey(bytes) {
155
- assertValidKeyByteLength(bytes.length);
150
+ assertValidKeyMaterialByteLength(bytes.length, "wrapping");
156
151
  const aesKey = await deriveAesKey(bytes);
157
152
  const hmacKey = await deriveHmacKey(bytes);
158
153
  const key = Object.freeze({});
@@ -170,10 +165,7 @@ async function importWrappingKey(bytes) {
170
165
  * {@link decodeWrappingKey} back to the original bytes.
171
166
  */
172
167
  function encodeWrappingKey(bytes, format) {
173
- assertWrappingKeyFormat(format);
174
- assertValidKeyByteLength(bytes.length);
175
- if (format === "hex") return encodeHex(bytes);
176
- return encodeBase64Url(bytes);
168
+ return encodeKeyMaterial(bytes, format, "wrapping", "wrapping");
177
169
  }
178
170
  /**
179
171
  * Decode key material emitted by {@link encodeWrappingKey} back to raw bytes.
@@ -181,27 +173,21 @@ function encodeWrappingKey(bytes, format) {
181
173
  * The result can be passed directly to {@link importWrappingKey}.
182
174
  */
183
175
  function decodeWrappingKey(encoded, format) {
184
- assertWrappingKeyFormat(format);
185
- let bytes;
186
- if (format === "hex") {
187
- if (encoded.length === 0 || encoded.length % 2 !== 0) throw new IdsError("invalid_key_encoding", "invalid hex key: length must be a positive even number of characters");
188
- if (!/^[0-9a-fA-F]+$/.test(encoded)) throw new IdsError("invalid_key_encoding", "invalid hex key: expected [0-9a-fA-F] only");
189
- bytes = decodeHex(encoded);
190
- } else try {
191
- bytes = decodeBase64Url(encoded);
192
- } catch {
193
- throw new IdsError("invalid_key_encoding", "invalid base64url key");
194
- }
195
- assertValidKeyByteLength(bytes.length);
196
- return bytes;
176
+ return decodeKeyMaterial(encoded, format, "wrapping", "wrapping");
197
177
  }
198
- /** Returns true when two handles were imported from the same raw operator secret. */
178
+ /**
179
+ * Returns true when two handles were imported from the same raw operator secret.
180
+ *
181
+ * Uses a constant-time comparison so duplicate detection over key material does
182
+ * not leak the position of the first differing byte through a timing side channel.
183
+ */
199
184
  function wrappingKeysEqual(a, b) {
200
185
  const aInternals = getWrappingKeyInternals(a);
201
186
  const bInternals = getWrappingKeyInternals(b);
202
187
  if (aInternals.rawBytes.length !== bInternals.rawBytes.length) return false;
203
- for (let i = 0; i < aInternals.rawBytes.length; i++) if (aInternals.rawBytes[i] !== bInternals.rawBytes[i]) return false;
204
- return true;
188
+ let diff = 0;
189
+ for (let i = 0; i < aInternals.rawBytes.length; i++) diff |= aInternals.rawBytes[i] ^ bInternals.rawBytes[i];
190
+ return diff === 0;
205
191
  }
206
192
  function getWrappingKeyMaterial(key) {
207
193
  const keyInternals = getWrappingKeyInternals(key);
@@ -240,19 +226,6 @@ async function deriveHmacKey(bytes) {
240
226
  length: 256
241
227
  }, false, ["sign", "verify"]);
242
228
  }
243
- function assertValidKeyByteLength(byteLength) {
244
- if (!validKeyByteLengths.has(byteLength)) throw new IdsError("invalid_key_length", `invalid wrapping key length: expected 16, 24, or 32 bytes, got ${byteLength}`);
245
- }
246
- function assertWrappingKeyFormat(format) {
247
- if (format !== "hex" && format !== "base64url") throw new IdsError("invalid_key_format", `invalid wrapping key format: expected hex or base64url, got '${formatForError(format)}'`);
248
- }
249
- function formatForError(value) {
250
- try {
251
- return String(value);
252
- } catch {
253
- return "[unprintable]";
254
- }
255
- }
256
229
  //#endregion
257
230
  //#region src/wrapped.ts
258
231
  const u32Max = 4294967295;
@@ -264,12 +237,6 @@ const i64Max = (1n << 63n) - 1n;
264
237
  function assertSupportedKind(kind) {
265
238
  if (kind !== "u32" && kind !== "i32" && kind !== "u64" && kind !== "i64") throw new IdsError("invalid_kind", "invalid wrapped key kind: expected u32, i32, u64, or i64");
266
239
  }
267
- function assertNonEmptyKeyring(keys) {
268
- if (keys.length === 0) throw new IdsError("empty_keyring", "wrapped keyring must contain at least one key");
269
- }
270
- function assertNonDuplicateKeys(keys) {
271
- for (let i = 0; i < keys.length; i++) for (let j = i + 1; j < keys.length; j++) if (wrappingKeysEqual(keys[i], keys[j])) throw new IdsError("duplicate_keyring_entry", "duplicate wrapping key in keyring");
272
- }
273
240
  function assertU32LookupKey(lookupKey) {
274
241
  if (typeof lookupKey !== "number" || !Number.isInteger(lookupKey) || Object.is(lookupKey, -0) || lookupKey < 0 || lookupKey > u32Max) throw new IdsError("invalid_lookup_key", `invalid u32 lookup key: expected integer in [0, ${u32Max}], got ${lookupKey}`);
275
242
  }
@@ -318,9 +285,8 @@ function createWrappedKeyId(brand, opts) {
318
285
  validateBrand(brand);
319
286
  registerBrand(brand, opts.allowDuplicateBrand);
320
287
  assertSupportedKind(opts.kind);
321
- assertNonEmptyKeyring(opts.keys);
288
+ assertValidKeyring(opts.keys, wrappingKeysEqual, "wrapping");
322
289
  const layoutKeys = opts.keys.map(getWrappingKeyMaterial);
323
- assertNonDuplicateKeys(opts.keys);
324
290
  const prefix = `${brand}_`;
325
291
  const wire = wireMethods(prefix);
326
292
  const layout = createWrappedLayoutOps(prefix, brand, opts.kind, layoutKeys);
@@ -358,4 +324,4 @@ function createWrappedKeyId(brand, opts) {
358
324
  //#endregion
359
325
  export { importWrappingKey as i, decodeWrappingKey as n, encodeWrappingKey as r, createWrappedKeyId as t };
360
326
 
361
- //# sourceMappingURL=wrapped-0vL72Nje.mjs.map
327
+ //# sourceMappingURL=wrapped-DKOsN_dq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapped-DKOsN_dq.mjs","names":[],"sources":["../src/layouts/wrapped.ts","../src/wrapping-key.ts","../src/wrapped.ts"],"sourcesContent":["import 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: CryptoKey;\n hmacKey: 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 hmacMessage(brand: string, kind: LayoutWrappedKind, lane: Uint8Array): Uint8Array {\n const prefix = new TextEncoder().encode(`${brand}:${kind}:`);\n const message = new Uint8Array(prefix.length + lane.length);\n message.set(prefix, 0);\n message.set(lane, prefix.length);\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 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 {\n assertValidKeyMaterialByteLength,\n assertValidKeyring,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"./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/v1\");\nconst hmacInfo = new TextEncoder().encode(\"@smonn/ids/wrapped/hmac/v1\");\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 `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 rawBytes: Uint8Array;\n aesKey: CryptoKey;\n hmacKey: CryptoKey;\n};\n\nexport type WrappingKeyMaterial = {\n aesKey: CryptoKey;\n hmacKey: 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 = await deriveAesKey(bytes);\n const hmacKey = await deriveHmacKey(bytes);\n const key = Object.freeze({}) as WrappingKey;\n internals.set(key, {\n rawBytes: bytes.slice(),\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 aInternals = getWrappingKeyInternals(a);\n const bInternals = getWrappingKeyInternals(b);\n if (aInternals.rawBytes.length !== bInternals.rawBytes.length) return false;\n let diff = 0;\n for (let i = 0; i < aInternals.rawBytes.length; i++) {\n diff |= aInternals.rawBytes[i]! ^ bInternals.rawBytes[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<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<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 \"./brand.js\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"./error.js\";\nimport { createWrappedLayoutOps } from \"./layouts/wrapped.js\";\nimport { registerBrand } from \"./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 \"./wrapping-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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrapped.d.mts","names":[],"sources":["../src/wrapping-key.ts","../src/wrapped.ts"],"mappings":";;;;;KAIY,iBAAA;AAAA,cAOE,gBAAA;;AAPd;;;;AAAY;AAA4B;;;;KAmB5B,WAAA;EAAA,UACA,gBAAA;AAAA;;;;;;;;;;;iBA0BU,iBAAA,CAAkB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,WAAA;;AAAA;AAmBpE;;;;iBAAgB,iBAAA,CAAkB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,iBAAA;;;;;AAAA;iBAY7C,iBAAA,CAAkB,OAAA,UAAiB,MAAA,EAAQ,iBAAA,GAAoB,UAAA;;;KChDnE,WAAA;AAAA,KAEP,gBAAA,WAA2B,WAAA,IAAe,CAAA;AD/BnC;AAA4B;;;;AAO1B;AAYd;;;AAnBY,KC0CA,YAAA,oCAAgD,WAAA;EACtD,EAAA;EAAU,EAAA,EAAI,EAAA,CAAG,KAAA;EAAQ,SAAA,EAAW,gBAAA,CAAiB,IAAA;AAAA;EACrD,EAAA;EAAW,KAAA,EAAO,UAAA;AAAA;;;;;ADE4C;AAmBpE;;;;;;;;KCNY,eAAA,oCAAmD,WAAA;;;;;;EAM7D,IAAA,CAAK,SAAA,EAAW,gBAAA,CAAiB,IAAA,IAAQ,OAAA,CAAQ,EAAA,CAAG,KAAA;EDYyB;AAAA;;;;AChD/E;;EA4CE,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,OAAA,CAAQ,gBAAA,CAAiB,IAAA;EA5CtC;AAAA;AAAsC;;;;;;;EAsDhD,UAAA,CAAW,KAAA,YAAiB,OAAA,CAAQ,YAAA,CAAa,KAAA,EAAO,IAAA,IApDX;EAsD7C,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA,GA3CtB;EA6CV,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAE1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;EACvC,YAAA,IAAgB,UAAA;EAAA,SACP,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;AAAA,KAGhC,iBAAA,WAA4B,WAAA;EACtC,IAAA,EAAM,CAAA;EACN,IAAA,GAAO,WAAA,KAAgB,WAAA;EACvB,mBAAA;AAAA;;;;;;;;;;;;;AArDsB;AAexB;;;;iBA0JgB,kBAAA,oCAAsD,WAAA,EACpE,KAAA,EAAO,KAAA,EACP,IAAA,EAAM,iBAAA,CAAkB,IAAA,IACvB,eAAA,CAAgB,KAAA,EAAO,IAAA"}
1
+ {"version":3,"file":"wrapped.d.mts","names":[],"sources":["../src/wrapping-key.ts","../src/wrapped.ts"],"mappings":";;;;;KAUY,iBAAA;AAAA,cAKE,gBAAA;;;;AALF;AAA4B;;;;AAK1B;AAYd;KAAY,WAAA;EAAA,UACA,gBAAA;AAAA;;;;;;;;;;;iBA0BU,iBAAA,CAAkB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,WAAA;AAmBpE;;;;;;AAAA,iBAAgB,iBAAA,CAAkB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,iBAAA;;;AAAA;AAS7D;;iBAAgB,iBAAA,CAAkB,OAAA,UAAiB,MAAA,EAAQ,iBAAA,GAAoB,UAAA;;;KChDnE,WAAA;AAAA,KAEP,gBAAA,WAA2B,WAAA,IAAe,CAAA;AD1BnC;AAA4B;;;;AAK1B;AAYd;;;AAjBY,KCqCA,YAAA,oCAAgD,WAAA;EACtD,EAAA;EAAU,EAAA,EAAI,EAAA,CAAG,KAAA;EAAQ,SAAA,EAAW,gBAAA,CAAiB,IAAA;AAAA;EACrD,EAAA;EAAW,KAAA,EAAO,UAAA;AAAA;;;;;ADK4C;AAmBpE;;;;;;;;KCTY,eAAA,oCAAmD,WAAA;;;;;;EAM7D,IAAA,CAAK,SAAA,EAAW,gBAAA,CAAiB,IAAA,IAAQ,OAAA,CAAQ,EAAA,CAAG,KAAA;EDYyB;AAAA;;;;AChD/E;;EA4CE,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,OAAA,CAAQ,gBAAA,CAAiB,IAAA;EA5CtC;AAAA;AAAsC;;;;;;;EAsDhD,UAAA,CAAW,KAAA,YAAiB,OAAA,CAAQ,YAAA,CAAa,KAAA,EAAO,IAAA,IApDX;EAsD7C,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA,GA3CtB;EA6CV,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAE1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;EACvC,YAAA,IAAgB,UAAA;EAAA,SACP,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;AAAA,KAGhC,iBAAA,WAA4B,WAAA;EACtC,IAAA,EAAM,CAAA;EACN,IAAA,GAAO,WAAA,KAAgB,WAAA;EACvB,mBAAA;AAAA;;;;;;;;;;;;;AArDsB;AAexB;;;;iBA0IgB,kBAAA,oCAAsD,WAAA,EACpE,KAAA,EAAO,KAAA,EACP,IAAA,EAAM,iBAAA,CAAkB,IAAA,IACvB,eAAA,CAAgB,KAAA,EAAO,IAAA"}
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-0vL72Nje.mjs";
2
+ import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-DKOsN_dq.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.7.0",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "author": "Simon Ingeson (https://github.com/smonn)",
6
6
  "repository": {
@@ -1,12 +0,0 @@
1
- //#region src/adapter-types.d.ts
2
- /** Discriminated failure value passed to `onError` and emitted to the framework's error handler. */
3
- type IdParamFailure = {
4
- readonly reason: "brand_mismatch";
5
- readonly status: number;
6
- } | {
7
- readonly reason: "malformed";
8
- readonly status: number;
9
- };
10
- //#endregion
11
- export { IdParamFailure as t };
12
- //# sourceMappingURL=adapter-types-oHCCSgOO.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter-types-oHCCSgOO.d.mts","names":[],"sources":["../src/adapter-types.ts"],"mappings":";;KACY,cAAA;EAAA,SACG,MAAA;EAAA,SAAmC,MAAA;AAAA;EAAA,SACnC,MAAA;EAAA,SAA8B,MAAA;AAAA"}
@@ -1,53 +0,0 @@
1
- //#region src/bytes.ts
2
- const hexDigits = "0123456789abcdef";
3
- const invalidNibble = 255;
4
- const hexCharCodeToNibble = new Uint8Array(128).fill(invalidNibble);
5
- for (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;
6
- for (let i = 0; i < 6; i++) {
7
- hexCharCodeToNibble[97 + i] = 10 + i;
8
- hexCharCodeToNibble[65 + i] = 10 + i;
9
- }
10
- /** Lowercase hex encoding of raw bytes. */
11
- function encodeHex(bytes) {
12
- const codes = new Array(bytes.length * 2);
13
- for (let i = 0; i < bytes.length; i++) {
14
- const b = bytes[i];
15
- codes[i * 2] = hexDigits.charCodeAt(b >>> 4);
16
- codes[i * 2 + 1] = hexDigits.charCodeAt(b & 15);
17
- }
18
- return String.fromCharCode(...codes);
19
- }
20
- /** Decodes a hex string to raw bytes. Throws on non-hex input. */
21
- function decodeHex(encoded) {
22
- if (encoded.length % 2 !== 0) throw new Error("invalid hex");
23
- const out = new Uint8Array(encoded.length / 2);
24
- for (let i = 0; i < out.length; i++) {
25
- const hiCode = encoded.charCodeAt(i * 2);
26
- const loCode = encoded.charCodeAt(i * 2 + 1);
27
- if (hiCode >= hexCharCodeToNibble.length || loCode >= hexCharCodeToNibble.length) throw new Error("invalid hex");
28
- const hi = hexCharCodeToNibble[hiCode];
29
- const lo = hexCharCodeToNibble[loCode];
30
- if (hi === invalidNibble || lo === invalidNibble) throw new Error("invalid hex");
31
- out[i] = hi << 4 | lo;
32
- }
33
- return out;
34
- }
35
- /** Base64url encoding without padding. */
36
- function encodeBase64Url(bytes) {
37
- let binary = "";
38
- for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
39
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
40
- }
41
- /** Decodes a base64url string to raw bytes. Throws on invalid input. */
42
- function decodeBase64Url(encoded) {
43
- const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
44
- const pad = (4 - base64.length % 4) % 4;
45
- const binary = atob(base64 + "=".repeat(pad));
46
- const out = new Uint8Array(binary.length);
47
- for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
48
- return out;
49
- }
50
- //#endregion
51
- export { encodeHex as i, decodeHex as n, encodeBase64Url as r, decodeBase64Url as t };
52
-
53
- //# sourceMappingURL=bytes-lhzKVaBV.mjs.map