@hlos-ai/schemas 0.4.2 → 0.6.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.
@@ -1,18 +1,13 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/ids.ts
9
2
  import { z } from "zod";
3
+ var DispatchIdBrand = /* @__PURE__ */ Symbol("DispatchIdBrand");
10
4
  var WalletIdSchema = z.string().regex(/^wallet_[a-z0-9]{20,}$/i, "Invalid WalletId format").transform((s) => s);
11
5
  var PassportIdSchema = z.string().regex(/^passport_[a-z0-9]{20,}$/i, "Invalid PassportId format").transform((s) => s);
12
6
  var ReceiptIdSchema = z.string().regex(/^rcpt_[0-9A-HJKMNP-TV-Z]{26}$/i, "Invalid ReceiptId format").transform((s) => s);
13
7
  var CorrelationIdSchema = z.string().min(8).max(128).transform((s) => s);
14
8
  var IdempotencyKeySchema = z.string().min(1).max(256).regex(/^[\x20-\x7E]+$/, "IdempotencyKey must be printable ASCII").transform((s) => s);
15
9
  var CrossingIdSchema = z.string().regex(/^cross_[a-z0-9]{20,}$/i, "Invalid CrossingId format").transform((s) => s);
10
+ var DispatchIdSchema = z.string().regex(/^dispatch_[a-z0-9]{20,}$/, "Invalid DispatchId format").transform((s) => s);
16
11
  function generateId(prefix) {
17
12
  const timestamp = Date.now().toString(36);
18
13
  const random = crypto.randomUUID().replace(/-/g, "").slice(0, 16);
@@ -22,19 +17,22 @@ var generateWalletId = () => generateId("wallet");
22
17
  var generatePassportId = () => generateId("passport");
23
18
  var generateReceiptId = () => generateId("rcpt");
24
19
  var generateCrossingId = () => generateId("cross");
20
+ var generateDispatchId = () => generateId("dispatch");
25
21
 
26
22
  export {
27
- __require,
23
+ DispatchIdBrand,
28
24
  WalletIdSchema,
29
25
  PassportIdSchema,
30
26
  ReceiptIdSchema,
31
27
  CorrelationIdSchema,
32
28
  IdempotencyKeySchema,
33
29
  CrossingIdSchema,
30
+ DispatchIdSchema,
34
31
  generateId,
35
32
  generateWalletId,
36
33
  generatePassportId,
37
34
  generateReceiptId,
38
- generateCrossingId
35
+ generateCrossingId,
36
+ generateDispatchId
39
37
  };
40
- //# sourceMappingURL=chunk-RQP6MVT3.js.map
38
+ //# sourceMappingURL=chunk-ADIQW3HO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ids.ts"],"sourcesContent":["/**\n * @hlos-ai/schemas - Branded ID Types\n *\n * Strongly-typed ID strings to prevent cross-surface mistakes.\n * Use Zod for runtime validation.\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// ID Patterns\n// =============================================================================\n\n/**\n * ULID pattern (26 chars, Crockford base32).\n */\nconst ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/i;\n\n/**\n * CUID pattern (starts with 'c', 25+ chars).\n */\nconst CUID_PATTERN = /^c[a-z0-9]{24,}$/;\n\n/**\n * UUID v4 pattern.\n */\nconst UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n// =============================================================================\n// Branded Types (TypeScript compile-time safety)\n// =============================================================================\n\ndeclare const WalletIdBrand: unique symbol;\ndeclare const PassportIdBrand: unique symbol;\ndeclare const ReceiptIdBrand: unique symbol;\ndeclare const CorrelationIdBrand: unique symbol;\ndeclare const IdempotencyKeyBrand: unique symbol;\ndeclare const CrossingIdBrand: unique symbol;\n/** Runtime symbol export keeps DTS declarations nameable across entrypoints. */\nexport const DispatchIdBrand = Symbol('DispatchIdBrand');\n\nexport type WalletId = string & { readonly [WalletIdBrand]: true };\nexport type PassportId = string & { readonly [PassportIdBrand]: true };\nexport type ReceiptId = string & { readonly [ReceiptIdBrand]: true };\nexport type CorrelationId = string & { readonly [CorrelationIdBrand]: true };\nexport type IdempotencyKey = string & { readonly [IdempotencyKeyBrand]: true };\nexport type CrossingId = string & { readonly [CrossingIdBrand]: true };\nexport type DispatchId = string & { readonly [DispatchIdBrand]: true };\n\n// =============================================================================\n// Zod Schemas (runtime validation)\n// =============================================================================\n\n/**\n * WalletId: prefixed CUID or ULID.\n */\nexport const WalletIdSchema = z.string()\n .regex(/^wallet_[a-z0-9]{20,}$/i, 'Invalid WalletId format')\n .transform((s) => s as WalletId);\n\n/**\n * PassportId: prefixed CUID or ULID.\n */\nexport const PassportIdSchema = z.string()\n .regex(/^passport_[a-z0-9]{20,}$/i, 'Invalid PassportId format')\n .transform((s) => s as PassportId);\n\n/**\n * ReceiptId: prefixed ULID.\n */\nexport const ReceiptIdSchema = z.string()\n .regex(/^rcpt_[0-9A-HJKMNP-TV-Z]{26}$/i, 'Invalid ReceiptId format')\n .transform((s) => s as ReceiptId);\n\n/**\n * CorrelationId: UUID v4 or custom format.\n */\nexport const CorrelationIdSchema = z.string()\n .min(8)\n .max(128)\n .transform((s) => s as CorrelationId);\n\n/**\n * IdempotencyKey: client-provided, opaque.\n * Max 256 chars, printable ASCII.\n */\nexport const IdempotencyKeySchema = z.string()\n .min(1)\n .max(256)\n .regex(/^[\\x20-\\x7E]+$/, 'IdempotencyKey must be printable ASCII')\n .transform((s) => s as IdempotencyKey);\n\n/**\n * CrossingId: prefixed CUID.\n */\nexport const CrossingIdSchema = z.string()\n .regex(/^cross_[a-z0-9]{20,}$/i, 'Invalid CrossingId format')\n .transform((s) => s as CrossingId);\n\n/**\n * DispatchId: prefixed CUID, canonical lowercase.\n * No /i flag — deterministic for cryptographic contexts.\n */\nexport const DispatchIdSchema = z.string()\n .regex(/^dispatch_[a-z0-9]{20,}$/, 'Invalid DispatchId format')\n .transform((s) => s as DispatchId);\n\n// =============================================================================\n// ID Generation Helpers\n// =============================================================================\n\n/**\n * Generate a prefixed CUID-style ID.\n * Uses crypto.randomUUID() stripped of dashes + timestamp prefix.\n */\nexport function generateId(prefix: string): string {\n const timestamp = Date.now().toString(36);\n const random = crypto.randomUUID().replace(/-/g, '').slice(0, 16);\n return `${prefix}_${timestamp}${random}`;\n}\n\nexport const generateWalletId = (): WalletId => generateId('wallet') as WalletId;\nexport const generatePassportId = (): PassportId => generateId('passport') as PassportId;\nexport const generateReceiptId = (): ReceiptId => generateId('rcpt') as ReceiptId;\nexport const generateCrossingId = (): CrossingId => generateId('cross') as CrossingId;\nexport const generateDispatchId = (): DispatchId => generateId('dispatch') as DispatchId;\n"],"mappings":";AAOA,SAAS,SAAS;AAgCX,IAAM,kBAAkB,uBAAO,iBAAiB;AAiBhD,IAAM,iBAAiB,EAAE,OAAO,EAClC,MAAM,2BAA2B,yBAAyB,EAC1D,UAAU,CAAC,MAAM,CAAa;AAK5B,IAAM,mBAAmB,EAAE,OAAO,EACpC,MAAM,6BAA6B,2BAA2B,EAC9D,UAAU,CAAC,MAAM,CAAe;AAK9B,IAAM,kBAAkB,EAAE,OAAO,EACnC,MAAM,kCAAkC,0BAA0B,EAClE,UAAU,CAAC,MAAM,CAAc;AAK7B,IAAM,sBAAsB,EAAE,OAAO,EACvC,IAAI,CAAC,EACL,IAAI,GAAG,EACP,UAAU,CAAC,MAAM,CAAkB;AAMjC,IAAM,uBAAuB,EAAE,OAAO,EACxC,IAAI,CAAC,EACL,IAAI,GAAG,EACP,MAAM,kBAAkB,wCAAwC,EAChE,UAAU,CAAC,MAAM,CAAmB;AAKlC,IAAM,mBAAmB,EAAE,OAAO,EACpC,MAAM,0BAA0B,2BAA2B,EAC3D,UAAU,CAAC,MAAM,CAAe;AAM9B,IAAM,mBAAmB,EAAE,OAAO,EACpC,MAAM,4BAA4B,2BAA2B,EAC7D,UAAU,CAAC,MAAM,CAAe;AAU9B,SAAS,WAAW,QAAwB;AAC/C,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,SAAS,OAAO,WAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,EAAE;AAChE,SAAO,GAAG,MAAM,IAAI,SAAS,GAAG,MAAM;AAC1C;AAEO,IAAM,mBAAmB,MAAgB,WAAW,QAAQ;AAC5D,IAAM,qBAAqB,MAAkB,WAAW,UAAU;AAClE,IAAM,oBAAoB,MAAiB,WAAW,MAAM;AAC5D,IAAM,qBAAqB,MAAkB,WAAW,OAAO;AAC/D,IAAM,qBAAqB,MAAkB,WAAW,UAAU;","names":[]}
@@ -0,0 +1,11 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
11
+ //# sourceMappingURL=chunk-DGUM43GV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,156 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-DGUM43GV.js";
4
+
5
+ // src/encoding.ts
6
+ import { z } from "zod";
7
+ function bytesToBase64url(bytes) {
8
+ let binary = "";
9
+ for (let i = 0; i < bytes.length; i++) {
10
+ binary += String.fromCharCode(bytes[i]);
11
+ }
12
+ const base64 = btoa(binary);
13
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
14
+ }
15
+ function base64urlToBytes(str) {
16
+ const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
17
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
18
+ const binary = atob(padded);
19
+ const bytes = new Uint8Array(binary.length);
20
+ for (let i = 0; i < binary.length; i++) {
21
+ bytes[i] = binary.charCodeAt(i);
22
+ }
23
+ return bytes;
24
+ }
25
+ var BASE64URL_SHA256_REGEX = /^[A-Za-z0-9_-]{43}$/;
26
+ var Base64urlSha256Schema = z.string().regex(
27
+ BASE64URL_SHA256_REGEX,
28
+ "Must be 43-char base64url (32-byte SHA-256 digest)"
29
+ );
30
+ var Base64urlEd25519SigSchema = z.string().regex(
31
+ /^[A-Za-z0-9_-]{86}$/,
32
+ "Must be 86-char base64url (64-byte Ed25519 signature)"
33
+ );
34
+
35
+ // src/receipt-hash.ts
36
+ var cachedSha256 = null;
37
+ function sortKeysDeep(value) {
38
+ if (Array.isArray(value)) return value.map(sortKeysDeep);
39
+ if (value !== null && typeof value === "object") {
40
+ const obj = value;
41
+ const out = {};
42
+ for (const key of Object.keys(obj).sort()) {
43
+ out[key] = sortKeysDeep(obj[key]);
44
+ }
45
+ return out;
46
+ }
47
+ return value;
48
+ }
49
+ function hasLoneSurrogate(s) {
50
+ for (let i = 0; i < s.length; i++) {
51
+ const code = s.charCodeAt(i);
52
+ if (code >= 55296 && code <= 56319) {
53
+ const next = s.charCodeAt(i + 1);
54
+ if (isNaN(next) || next < 56320 || next > 57343) return true;
55
+ i++;
56
+ } else if (code >= 56320 && code <= 57343) {
57
+ return true;
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+ function jcsCanonicalize(value) {
63
+ return JSON.stringify(sortKeysDeep(value), (_key, v) => {
64
+ if (v === void 0) throw new Error("JCS: undefined is not allowed");
65
+ if (typeof v === "function") throw new Error("JCS: function is not allowed");
66
+ if (typeof v === "symbol") throw new Error("JCS: symbol is not allowed");
67
+ if (typeof v === "bigint") throw new Error("JCS: bigint is not allowed");
68
+ if (typeof v === "number" && !Number.isFinite(v))
69
+ throw new Error("JCS: non-finite number is not allowed");
70
+ if (typeof v === "string" && hasLoneSurrogate(v))
71
+ throw new Error("JCS: lone surrogate (U+D800\u2013U+DFFF) is not allowed");
72
+ return v;
73
+ });
74
+ }
75
+ function computeReceiptHash(receipt) {
76
+ const sha256 = loadSha256();
77
+ const canonical = jcsCanonicalize(receipt);
78
+ const digest = sha256(new TextEncoder().encode(canonical));
79
+ return bytesToBase64url(digest);
80
+ }
81
+ function computeContentHash(content) {
82
+ const sha256 = loadSha256();
83
+ const canonical = jcsCanonicalize(content);
84
+ const digest = sha256(new TextEncoder().encode(canonical));
85
+ return bytesToBase64url(digest);
86
+ }
87
+ function loadSha256() {
88
+ if (cachedSha256) return cachedSha256;
89
+ const moduleApi = typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : (
90
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
91
+ __require("module")
92
+ );
93
+ const createRequire = moduleApi.createRequire;
94
+ const requireBase = typeof __filename === "string" ? __filename : `${process.cwd()}/package.json`;
95
+ const _require = createRequire(requireBase);
96
+ let sha256;
97
+ try {
98
+ sha256 = _require("@noble/hashes/sha2.js").sha256;
99
+ } catch {
100
+ try {
101
+ sha256 = _require("@noble/hashes/sha2").sha256;
102
+ } catch {
103
+ throw new Error(
104
+ "computeReceiptHash/computeContentHash requires @noble/hashes as a peer dependency. Install it: npm install @noble/hashes"
105
+ );
106
+ }
107
+ }
108
+ cachedSha256 = sha256;
109
+ return sha256;
110
+ }
111
+ var ReceiptHashSchema = Base64urlSha256Schema.describe(
112
+ "receipt_hash: base64url(SHA-256(JCS(receipt)))"
113
+ );
114
+ var RECEIPT_HASH_GOLDEN_FIXTURE = {
115
+ receipt: {
116
+ "@type": "https://hlos.ai/schema/SignedReceiptV0",
117
+ version: 0,
118
+ receipt_id: "rcpt_01HZGOLDENTEST000000000000",
119
+ content: {
120
+ type: "CrossingSettled",
121
+ crossingId: "cross_test123abc456def789ghi",
122
+ settlement_authority: "hlos.ai"
123
+ },
124
+ // Placeholder bytes (NOT a valid cryptographic signature)
125
+ content_hash: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
126
+ signature: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
127
+ key_id: "hlos-v2-1",
128
+ issued_at: "2026-01-27T00:00:00.000Z"
129
+ },
130
+ /**
131
+ * Expected JCS canonical form of the receipt.
132
+ * Keys are lexicographically sorted at all levels.
133
+ */
134
+ expectedJcs: '{"@type":"https://hlos.ai/schema/SignedReceiptV0","content":{"crossingId":"cross_test123abc456def789ghi","settlement_authority":"hlos.ai","type":"CrossingSettled"},"content_hash":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","issued_at":"2026-01-27T00:00:00.000Z","key_id":"hlos-v2-1","receipt_id":"rcpt_01HZGOLDENTEST000000000000","signature":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","version":0}',
135
+ /**
136
+ * Expected receipt_hash (FROZEN).
137
+ * Compute: JCS canonicalize receipt → SHA-256 → base64url (no padding)
138
+ *
139
+ * DO NOT CHANGE unless the receipt fixture above changes.
140
+ */
141
+ expectedReceiptHash: "uLiQVUeVKcE35Rdje2fArZQfTcECDgwK6UbmQB_36Pg"
142
+ };
143
+
144
+ export {
145
+ bytesToBase64url,
146
+ base64urlToBytes,
147
+ BASE64URL_SHA256_REGEX,
148
+ Base64urlSha256Schema,
149
+ Base64urlEd25519SigSchema,
150
+ jcsCanonicalize,
151
+ computeReceiptHash,
152
+ computeContentHash,
153
+ ReceiptHashSchema,
154
+ RECEIPT_HASH_GOLDEN_FIXTURE
155
+ };
156
+ //# sourceMappingURL=chunk-I4QGTJTP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/encoding.ts","../src/receipt-hash.ts"],"sourcesContent":["/**\n * @hlos-ai/schemas - Base64url Encoding Utilities\n *\n * RFC 4648 §5 compliant base64url encoding/decoding.\n * Zero external dependencies.\n *\n * Used for:\n * - receipt_hash: base64url(SHA-256(JCS(receipt)))\n * - content_hash: base64url(SHA-256(JCS(content)))\n * - crossing_hash: base64url(SHA-256(JCS(CrossingHashInputV0)))\n * - Ed25519 signatures and public keys\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Encoding\n// =============================================================================\n\n/**\n * Encode Uint8Array to base64url (RFC 4648 §5, no padding).\n */\nexport function bytesToBase64url(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Decode base64url string to Uint8Array.\n */\nexport function base64urlToBytes(str: string): Uint8Array {\n const base64 = str.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n// =============================================================================\n// Schemas\n// =============================================================================\n\n/**\n * Regex for base64url-encoded SHA-256 hash (32 bytes = 43 chars, no padding).\n */\nexport const BASE64URL_SHA256_REGEX = /^[A-Za-z0-9_-]{43}$/;\n\n/**\n * Zod schema for a base64url-encoded SHA-256 hash.\n * 32 bytes = 43 chars base64url (no padding).\n */\nexport const Base64urlSha256Schema = z.string().regex(\n BASE64URL_SHA256_REGEX,\n 'Must be 43-char base64url (32-byte SHA-256 digest)'\n);\n\n/**\n * Zod schema for a base64url-encoded Ed25519 signature.\n * 64 bytes = 86 chars base64url (no padding).\n */\nexport const Base64urlEd25519SigSchema = z.string().regex(\n /^[A-Za-z0-9_-]{86}$/,\n 'Must be 86-char base64url (64-byte Ed25519 signature)'\n);\n","/**\n * @hlos-ai/schemas - Receipt Hash Computation\n *\n * Canonical hash computation for HLOS receipts.\n *\n * receipt_hash = base64url(SHA-256(JCS(receipt)))\n *\n * This is the hash that:\n * - BazaarReceiptEnvelope references (hash-first, per BRIDGE_SPEC.md §1.1)\n * - Notary Attestation (NA) binds to (per BRIDGE_SPEC.md §4.3)\n * - Surfaces use to reference kernel receipts unambiguously\n *\n * IMPORTANT: receipt_hash is computed over the ENTIRE SignedReceiptV0 envelope\n * (including content, content_hash, signature, key_id, etc.), NOT just content.\n * This binds the hash to a specific signed receipt version.\n *\n * Requires @noble/hashes as peer dependency.\n *\n * @see BRIDGE_SPEC.md §1.3 for canonical hashing requirements\n */\n\nimport { z } from 'zod';\nimport { bytesToBase64url, Base64urlSha256Schema } from './encoding';\nimport type { SignedReceiptV0 } from './receipt-v0';\n\nlet cachedSha256: ((data: Uint8Array) => Uint8Array) | null = null;\n\n// =============================================================================\n// JCS Canonicalization (RFC 8785)\n// =============================================================================\n\n/**\n * Recursively sort object keys lexicographically (RFC 8785 requirement).\n * @internal\n */\nfunction sortKeysDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeysDeep);\n if (value !== null && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n out[key] = sortKeysDeep(obj[key]);\n }\n return out;\n }\n return value;\n}\n\n/**\n * Detect lone surrogates (U+D800–U+DFFF) that are NOT part of a valid pair.\n * A high surrogate (D800–DBFF) must be followed by a low surrogate (DC00–DFFF).\n * Lone surrogates are invalid in well-formed UTF-8 / RFC 8259 JSON and can\n * cause parser differentials between JCS implementations.\n */\nfunction hasLoneSurrogate(s: string): boolean {\n for (let i = 0; i < s.length; i++) {\n const code = s.charCodeAt(i);\n if (code >= 0xD800 && code <= 0xDBFF) {\n const next = s.charCodeAt(i + 1);\n if (isNaN(next) || next < 0xDC00 || next > 0xDFFF) return true;\n i++;\n } else if (code >= 0xDC00 && code <= 0xDFFF) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * JCS (RFC 8785) canonical JSON serialization.\n *\n * Per RFC 8785:\n * - Object keys are lexicographically sorted\n * - Numbers use ES6 serialization\n * - Rejects: undefined, functions, symbols, bigint, non-finite numbers\n * - Rejects: strings containing lone surrogates (U+D800–U+DFFF)\n *\n * @throws Error if value contains non-canonicalizable types\n */\nexport function jcsCanonicalize(value: unknown): string {\n return JSON.stringify(sortKeysDeep(value), (_key, v) => {\n if (v === undefined) throw new Error('JCS: undefined is not allowed');\n if (typeof v === 'function') throw new Error('JCS: function is not allowed');\n if (typeof v === 'symbol') throw new Error('JCS: symbol is not allowed');\n if (typeof v === 'bigint') throw new Error('JCS: bigint is not allowed');\n if (typeof v === 'number' && !Number.isFinite(v))\n throw new Error('JCS: non-finite number is not allowed');\n if (typeof v === 'string' && hasLoneSurrogate(v))\n throw new Error('JCS: lone surrogate (U+D800–U+DFFF) is not allowed');\n return v;\n });\n}\n\n// =============================================================================\n// Hash Computation\n// =============================================================================\n\n/**\n * Compute receipt_hash for a SignedReceiptV0.\n *\n * receipt_hash = base64url(SHA-256(JCS(receipt)))\n *\n * The hash is over the ENTIRE receipt envelope (content, content_hash,\n * signature, key_id, @type, version, receipt_id, issued_at).\n *\n * This is the canonical hash format per BRIDGE_SPEC.md §1.3:\n * 1. Canonicalize the receipt using RFC 8785 JCS\n * 2. Hash canonical bytes using SHA-256\n * 3. Encode as base64url (no padding)\n *\n * @param receipt - A fully-formed SignedReceiptV0 (must already be signed)\n * @returns 43-char base64url string (32-byte SHA-256 digest)\n *\n * @example\n * ```typescript\n * const receipt = issueReceiptV0(content);\n * const hash = computeReceiptHash(receipt);\n * // hash is e.g. \"z8A2kCNck4rPL4ugNe-Fbxputdi3PkjkVpSrIBhojU0\"\n * ```\n */\nexport function computeReceiptHash(receipt: SignedReceiptV0): string {\n // Lazy-load to keep this module usable without @noble/hashes installed\n // (only fails at call time if peer dep is missing)\n const sha256 = loadSha256();\n const canonical = jcsCanonicalize(receipt);\n const digest = sha256(new TextEncoder().encode(canonical));\n return bytesToBase64url(digest);\n}\n\n/**\n * Compute content_hash for arbitrary content.\n *\n * content_hash = base64url(SHA-256(JCS(content)))\n *\n * This is the hash stored in SignedReceiptV0.content_hash.\n * It hashes only the content field, NOT the entire receipt.\n *\n * @param content - The content to hash (will be JCS-canonicalized)\n * @returns 43-char base64url string (32-byte SHA-256 digest)\n */\nexport function computeContentHash(content: unknown): string {\n const sha256 = loadSha256();\n const canonical = jcsCanonicalize(content);\n const digest = sha256(new TextEncoder().encode(canonical));\n return bytesToBase64url(digest);\n}\n\n/**\n * Lazy-load SHA-256 from @noble/hashes (optional peer dependency).\n * @internal\n */\nfunction loadSha256(): (data: Uint8Array) => Uint8Array {\n if (cachedSha256) return cachedSha256;\n\n // Resolve require for both ESM and CJS contexts without import.meta so\n // CommonJS test runners can parse this file directly.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const moduleApi = typeof process.getBuiltinModule === 'function'\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ? (process.getBuiltinModule('module') as any)\n : // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('node:module');\n const createRequire = moduleApi.createRequire as (filename: string) => NodeRequire;\n const requireBase = typeof __filename === 'string'\n ? __filename\n : `${process.cwd()}/package.json`;\n const _require = createRequire(requireBase);\n\n let sha256: (data: Uint8Array) => Uint8Array;\n try {\n // @noble/hashes v2.x exports from sha2.js\n sha256 = _require('@noble/hashes/sha2.js').sha256;\n } catch {\n try {\n // Fallback for bundlers that resolve without .js extension\n sha256 = _require('@noble/hashes/sha2').sha256;\n } catch {\n throw new Error(\n 'computeReceiptHash/computeContentHash requires @noble/hashes as a peer dependency. ' +\n 'Install it: npm install @noble/hashes'\n );\n }\n }\n cachedSha256 = sha256;\n return sha256;\n}\n\n// =============================================================================\n// Schemas\n// =============================================================================\n\n/**\n * Zod schema for receipt_hash field.\n * base64url(SHA-256(JCS(receipt))) — always 43 chars.\n */\nexport const ReceiptHashSchema = Base64urlSha256Schema.describe(\n 'receipt_hash: base64url(SHA-256(JCS(receipt)))'\n);\n\n// =============================================================================\n// Golden Fixtures\n// =============================================================================\n\n/**\n * Golden receipt fixture for hash conformance testing.\n *\n * This is a deterministic SignedReceiptV0 with fixed field values.\n * The receipt_hash computed over this fixture is FROZEN.\n *\n * If your computeReceiptHash() produces a different result for this input,\n * your implementation is non-conformant. DO NOT CHANGE these values.\n */\nexport const RECEIPT_HASH_GOLDEN_FIXTURE = {\n receipt: {\n '@type': 'https://hlos.ai/schema/SignedReceiptV0' as const,\n version: 0 as const,\n receipt_id: 'rcpt_01HZGOLDENTEST000000000000',\n content: {\n type: 'CrossingSettled' as const,\n crossingId: 'cross_test123abc456def789ghi',\n settlement_authority: 'hlos.ai',\n },\n // Placeholder bytes (NOT a valid cryptographic signature)\n content_hash: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',\n signature:\n 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',\n key_id: 'hlos-v2-1',\n issued_at: '2026-01-27T00:00:00.000Z',\n } satisfies SignedReceiptV0,\n\n /**\n * Expected JCS canonical form of the receipt.\n * Keys are lexicographically sorted at all levels.\n */\n expectedJcs: '{\"@type\":\"https://hlos.ai/schema/SignedReceiptV0\",\"content\":{\"crossingId\":\"cross_test123abc456def789ghi\",\"settlement_authority\":\"hlos.ai\",\"type\":\"CrossingSettled\"},\"content_hash\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"issued_at\":\"2026-01-27T00:00:00.000Z\",\"key_id\":\"hlos-v2-1\",\"receipt_id\":\"rcpt_01HZGOLDENTEST000000000000\",\"signature\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"version\":0}',\n\n /**\n * Expected receipt_hash (FROZEN).\n * Compute: JCS canonicalize receipt → SHA-256 → base64url (no padding)\n *\n * DO NOT CHANGE unless the receipt fixture above changes.\n */\n expectedReceiptHash: 'uLiQVUeVKcE35Rdje2fArZQfTcECDgwK6UbmQB_36Pg',\n} as const;\n"],"mappings":";;;;;AAaA,SAAS,SAAS;AASX,SAAS,iBAAiB,OAA2B;AACxD,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,cAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,EAC1C;AACA,QAAM,SAAS,KAAK,MAAM;AAC1B,SAAO,OAAO,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC3E;AAKO,SAAS,iBAAiB,KAAyB;AACtD,QAAM,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACvD,QAAM,SAAS,SAAS,IAAI,QAAQ,IAAK,OAAO,SAAS,KAAM,CAAC;AAChE,QAAM,SAAS,KAAK,MAAM;AAC1B,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACpC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAClC;AACA,SAAO;AACX;AASO,IAAM,yBAAyB;AAM/B,IAAM,wBAAwB,EAAE,OAAO,EAAE;AAAA,EAC5C;AAAA,EACA;AACJ;AAMO,IAAM,4BAA4B,EAAE,OAAO,EAAE;AAAA,EAChD;AAAA,EACA;AACJ;;;AC7CA,IAAI,eAA0D;AAU9D,SAAS,aAAa,OAAyB;AAC3C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,YAAY;AACvD,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC7C,UAAM,MAAM;AACZ,UAAM,MAA+B,CAAC;AACtC,eAAW,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK,GAAG;AACvC,UAAI,GAAG,IAAI,aAAa,IAAI,GAAG,CAAC;AAAA,IACpC;AACA,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAQA,SAAS,iBAAiB,GAAoB;AAC1C,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAC/B,UAAM,OAAO,EAAE,WAAW,CAAC;AAC3B,QAAI,QAAQ,SAAU,QAAQ,OAAQ;AAClC,YAAM,OAAO,EAAE,WAAW,IAAI,CAAC;AAC/B,UAAI,MAAM,IAAI,KAAK,OAAO,SAAU,OAAO,MAAQ,QAAO;AAC1D;AAAA,IACJ,WAAW,QAAQ,SAAU,QAAQ,OAAQ;AACzC,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAaO,SAAS,gBAAgB,OAAwB;AACpD,SAAO,KAAK,UAAU,aAAa,KAAK,GAAG,CAAC,MAAM,MAAM;AACpD,QAAI,MAAM,OAAW,OAAM,IAAI,MAAM,+BAA+B;AACpE,QAAI,OAAO,MAAM,WAAY,OAAM,IAAI,MAAM,8BAA8B;AAC3E,QAAI,OAAO,MAAM,SAAU,OAAM,IAAI,MAAM,4BAA4B;AACvE,QAAI,OAAO,MAAM,SAAU,OAAM,IAAI,MAAM,4BAA4B;AACvE,QAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC;AAC3C,YAAM,IAAI,MAAM,uCAAuC;AAC3D,QAAI,OAAO,MAAM,YAAY,iBAAiB,CAAC;AAC3C,YAAM,IAAI,MAAM,yDAAoD;AACxE,WAAO;AAAA,EACX,CAAC;AACL;AA6BO,SAAS,mBAAmB,SAAkC;AAGjE,QAAM,SAAS,WAAW;AAC1B,QAAM,YAAY,gBAAgB,OAAO;AACzC,QAAM,SAAS,OAAO,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AACzD,SAAO,iBAAiB,MAAM;AAClC;AAaO,SAAS,mBAAmB,SAA0B;AACzD,QAAM,SAAS,WAAW;AAC1B,QAAM,YAAY,gBAAgB,OAAO;AACzC,QAAM,SAAS,OAAO,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AACzD,SAAO,iBAAiB,MAAM;AAClC;AAMA,SAAS,aAA+C;AACpD,MAAI,aAAc,QAAO;AAKzB,QAAM,YAAY,OAAO,QAAQ,qBAAqB,aAE/C,QAAQ,iBAAiB,QAAQ;AAAA;AAAA,IAElC,UAAQ,QAAa;AAAA;AAC3B,QAAM,gBAAgB,UAAU;AAChC,QAAM,cAAc,OAAO,eAAe,WACpC,aACA,GAAG,QAAQ,IAAI,CAAC;AACtB,QAAM,WAAW,cAAc,WAAW;AAE1C,MAAI;AACJ,MAAI;AAEA,aAAS,SAAS,uBAAuB,EAAE;AAAA,EAC/C,QAAQ;AACJ,QAAI;AAEA,eAAS,SAAS,oBAAoB,EAAE;AAAA,IAC5C,QAAQ;AACJ,YAAM,IAAI;AAAA,QACN;AAAA,MAEJ;AAAA,IACJ;AAAA,EACJ;AACA,iBAAe;AACf,SAAO;AACX;AAUO,IAAM,oBAAoB,sBAAsB;AAAA,EACnD;AACJ;AAeO,IAAM,8BAA8B;AAAA,EACvC,SAAS;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,MACL,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,sBAAsB;AAAA,IAC1B;AAAA;AAAA,IAEA,cAAc;AAAA,IACd,WACI;AAAA,IACJ,QAAQ;AAAA,IACR,WAAW;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQb,qBAAqB;AACzB;","names":[]}
package/dist/ids.cjs CHANGED
@@ -22,11 +22,14 @@ var ids_exports = {};
22
22
  __export(ids_exports, {
23
23
  CorrelationIdSchema: () => CorrelationIdSchema,
24
24
  CrossingIdSchema: () => CrossingIdSchema,
25
+ DispatchIdBrand: () => DispatchIdBrand,
26
+ DispatchIdSchema: () => DispatchIdSchema,
25
27
  IdempotencyKeySchema: () => IdempotencyKeySchema,
26
28
  PassportIdSchema: () => PassportIdSchema,
27
29
  ReceiptIdSchema: () => ReceiptIdSchema,
28
30
  WalletIdSchema: () => WalletIdSchema,
29
31
  generateCrossingId: () => generateCrossingId,
32
+ generateDispatchId: () => generateDispatchId,
30
33
  generateId: () => generateId,
31
34
  generatePassportId: () => generatePassportId,
32
35
  generateReceiptId: () => generateReceiptId,
@@ -34,12 +37,14 @@ __export(ids_exports, {
34
37
  });
35
38
  module.exports = __toCommonJS(ids_exports);
36
39
  var import_zod = require("zod");
40
+ var DispatchIdBrand = /* @__PURE__ */ Symbol("DispatchIdBrand");
37
41
  var WalletIdSchema = import_zod.z.string().regex(/^wallet_[a-z0-9]{20,}$/i, "Invalid WalletId format").transform((s) => s);
38
42
  var PassportIdSchema = import_zod.z.string().regex(/^passport_[a-z0-9]{20,}$/i, "Invalid PassportId format").transform((s) => s);
39
43
  var ReceiptIdSchema = import_zod.z.string().regex(/^rcpt_[0-9A-HJKMNP-TV-Z]{26}$/i, "Invalid ReceiptId format").transform((s) => s);
40
44
  var CorrelationIdSchema = import_zod.z.string().min(8).max(128).transform((s) => s);
41
45
  var IdempotencyKeySchema = import_zod.z.string().min(1).max(256).regex(/^[\x20-\x7E]+$/, "IdempotencyKey must be printable ASCII").transform((s) => s);
42
46
  var CrossingIdSchema = import_zod.z.string().regex(/^cross_[a-z0-9]{20,}$/i, "Invalid CrossingId format").transform((s) => s);
47
+ var DispatchIdSchema = import_zod.z.string().regex(/^dispatch_[a-z0-9]{20,}$/, "Invalid DispatchId format").transform((s) => s);
43
48
  function generateId(prefix) {
44
49
  const timestamp = Date.now().toString(36);
45
50
  const random = crypto.randomUUID().replace(/-/g, "").slice(0, 16);
@@ -49,15 +54,19 @@ var generateWalletId = () => generateId("wallet");
49
54
  var generatePassportId = () => generateId("passport");
50
55
  var generateReceiptId = () => generateId("rcpt");
51
56
  var generateCrossingId = () => generateId("cross");
57
+ var generateDispatchId = () => generateId("dispatch");
52
58
  // Annotate the CommonJS export names for ESM import in node:
53
59
  0 && (module.exports = {
54
60
  CorrelationIdSchema,
55
61
  CrossingIdSchema,
62
+ DispatchIdBrand,
63
+ DispatchIdSchema,
56
64
  IdempotencyKeySchema,
57
65
  PassportIdSchema,
58
66
  ReceiptIdSchema,
59
67
  WalletIdSchema,
60
68
  generateCrossingId,
69
+ generateDispatchId,
61
70
  generateId,
62
71
  generatePassportId,
63
72
  generateReceiptId,
package/dist/ids.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ids.ts"],"sourcesContent":["/**\n * @hlos-ai/schemas - Branded ID Types\n *\n * Strongly-typed ID strings to prevent cross-surface mistakes.\n * Use Zod for runtime validation.\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// ID Patterns\n// =============================================================================\n\n/**\n * ULID pattern (26 chars, Crockford base32).\n */\nconst ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/i;\n\n/**\n * CUID pattern (starts with 'c', 25+ chars).\n */\nconst CUID_PATTERN = /^c[a-z0-9]{24,}$/;\n\n/**\n * UUID v4 pattern.\n */\nconst UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n// =============================================================================\n// Branded Types (TypeScript compile-time safety)\n// =============================================================================\n\ndeclare const WalletIdBrand: unique symbol;\ndeclare const PassportIdBrand: unique symbol;\ndeclare const ReceiptIdBrand: unique symbol;\ndeclare const CorrelationIdBrand: unique symbol;\ndeclare const IdempotencyKeyBrand: unique symbol;\ndeclare const CrossingIdBrand: unique symbol;\n\nexport type WalletId = string & { readonly [WalletIdBrand]: true };\nexport type PassportId = string & { readonly [PassportIdBrand]: true };\nexport type ReceiptId = string & { readonly [ReceiptIdBrand]: true };\nexport type CorrelationId = string & { readonly [CorrelationIdBrand]: true };\nexport type IdempotencyKey = string & { readonly [IdempotencyKeyBrand]: true };\nexport type CrossingId = string & { readonly [CrossingIdBrand]: true };\n\n// =============================================================================\n// Zod Schemas (runtime validation)\n// =============================================================================\n\n/**\n * WalletId: prefixed CUID or ULID.\n */\nexport const WalletIdSchema = z.string()\n .regex(/^wallet_[a-z0-9]{20,}$/i, 'Invalid WalletId format')\n .transform((s) => s as WalletId);\n\n/**\n * PassportId: prefixed CUID or ULID.\n */\nexport const PassportIdSchema = z.string()\n .regex(/^passport_[a-z0-9]{20,}$/i, 'Invalid PassportId format')\n .transform((s) => s as PassportId);\n\n/**\n * ReceiptId: prefixed ULID.\n */\nexport const ReceiptIdSchema = z.string()\n .regex(/^rcpt_[0-9A-HJKMNP-TV-Z]{26}$/i, 'Invalid ReceiptId format')\n .transform((s) => s as ReceiptId);\n\n/**\n * CorrelationId: UUID v4 or custom format.\n */\nexport const CorrelationIdSchema = z.string()\n .min(8)\n .max(128)\n .transform((s) => s as CorrelationId);\n\n/**\n * IdempotencyKey: client-provided, opaque.\n * Max 256 chars, printable ASCII.\n */\nexport const IdempotencyKeySchema = z.string()\n .min(1)\n .max(256)\n .regex(/^[\\x20-\\x7E]+$/, 'IdempotencyKey must be printable ASCII')\n .transform((s) => s as IdempotencyKey);\n\n/**\n * CrossingId: prefixed CUID.\n */\nexport const CrossingIdSchema = z.string()\n .regex(/^cross_[a-z0-9]{20,}$/i, 'Invalid CrossingId format')\n .transform((s) => s as CrossingId);\n\n// =============================================================================\n// ID Generation Helpers\n// =============================================================================\n\n/**\n * Generate a prefixed CUID-style ID.\n * Uses crypto.randomUUID() stripped of dashes + timestamp prefix.\n */\nexport function generateId(prefix: string): string {\n const timestamp = Date.now().toString(36);\n const random = crypto.randomUUID().replace(/-/g, '').slice(0, 16);\n return `${prefix}_${timestamp}${random}`;\n}\n\nexport const generateWalletId = (): WalletId => generateId('wallet') as WalletId;\nexport const generatePassportId = (): PassportId => generateId('passport') as PassportId;\nexport const generateReceiptId = (): ReceiptId => generateId('rcpt') as ReceiptId;\nexport const generateCrossingId = (): CrossingId => generateId('cross') as CrossingId;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,iBAAkB;AA8CX,IAAM,iBAAiB,aAAE,OAAO,EAClC,MAAM,2BAA2B,yBAAyB,EAC1D,UAAU,CAAC,MAAM,CAAa;AAK5B,IAAM,mBAAmB,aAAE,OAAO,EACpC,MAAM,6BAA6B,2BAA2B,EAC9D,UAAU,CAAC,MAAM,CAAe;AAK9B,IAAM,kBAAkB,aAAE,OAAO,EACnC,MAAM,kCAAkC,0BAA0B,EAClE,UAAU,CAAC,MAAM,CAAc;AAK7B,IAAM,sBAAsB,aAAE,OAAO,EACvC,IAAI,CAAC,EACL,IAAI,GAAG,EACP,UAAU,CAAC,MAAM,CAAkB;AAMjC,IAAM,uBAAuB,aAAE,OAAO,EACxC,IAAI,CAAC,EACL,IAAI,GAAG,EACP,MAAM,kBAAkB,wCAAwC,EAChE,UAAU,CAAC,MAAM,CAAmB;AAKlC,IAAM,mBAAmB,aAAE,OAAO,EACpC,MAAM,0BAA0B,2BAA2B,EAC3D,UAAU,CAAC,MAAM,CAAe;AAU9B,SAAS,WAAW,QAAwB;AAC/C,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,SAAS,OAAO,WAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,EAAE;AAChE,SAAO,GAAG,MAAM,IAAI,SAAS,GAAG,MAAM;AAC1C;AAEO,IAAM,mBAAmB,MAAgB,WAAW,QAAQ;AAC5D,IAAM,qBAAqB,MAAkB,WAAW,UAAU;AAClE,IAAM,oBAAoB,MAAiB,WAAW,MAAM;AAC5D,IAAM,qBAAqB,MAAkB,WAAW,OAAO;","names":[]}
1
+ {"version":3,"sources":["../src/ids.ts"],"sourcesContent":["/**\n * @hlos-ai/schemas - Branded ID Types\n *\n * Strongly-typed ID strings to prevent cross-surface mistakes.\n * Use Zod for runtime validation.\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// ID Patterns\n// =============================================================================\n\n/**\n * ULID pattern (26 chars, Crockford base32).\n */\nconst ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/i;\n\n/**\n * CUID pattern (starts with 'c', 25+ chars).\n */\nconst CUID_PATTERN = /^c[a-z0-9]{24,}$/;\n\n/**\n * UUID v4 pattern.\n */\nconst UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n// =============================================================================\n// Branded Types (TypeScript compile-time safety)\n// =============================================================================\n\ndeclare const WalletIdBrand: unique symbol;\ndeclare const PassportIdBrand: unique symbol;\ndeclare const ReceiptIdBrand: unique symbol;\ndeclare const CorrelationIdBrand: unique symbol;\ndeclare const IdempotencyKeyBrand: unique symbol;\ndeclare const CrossingIdBrand: unique symbol;\n/** Runtime symbol export keeps DTS declarations nameable across entrypoints. */\nexport const DispatchIdBrand = Symbol('DispatchIdBrand');\n\nexport type WalletId = string & { readonly [WalletIdBrand]: true };\nexport type PassportId = string & { readonly [PassportIdBrand]: true };\nexport type ReceiptId = string & { readonly [ReceiptIdBrand]: true };\nexport type CorrelationId = string & { readonly [CorrelationIdBrand]: true };\nexport type IdempotencyKey = string & { readonly [IdempotencyKeyBrand]: true };\nexport type CrossingId = string & { readonly [CrossingIdBrand]: true };\nexport type DispatchId = string & { readonly [DispatchIdBrand]: true };\n\n// =============================================================================\n// Zod Schemas (runtime validation)\n// =============================================================================\n\n/**\n * WalletId: prefixed CUID or ULID.\n */\nexport const WalletIdSchema = z.string()\n .regex(/^wallet_[a-z0-9]{20,}$/i, 'Invalid WalletId format')\n .transform((s) => s as WalletId);\n\n/**\n * PassportId: prefixed CUID or ULID.\n */\nexport const PassportIdSchema = z.string()\n .regex(/^passport_[a-z0-9]{20,}$/i, 'Invalid PassportId format')\n .transform((s) => s as PassportId);\n\n/**\n * ReceiptId: prefixed ULID.\n */\nexport const ReceiptIdSchema = z.string()\n .regex(/^rcpt_[0-9A-HJKMNP-TV-Z]{26}$/i, 'Invalid ReceiptId format')\n .transform((s) => s as ReceiptId);\n\n/**\n * CorrelationId: UUID v4 or custom format.\n */\nexport const CorrelationIdSchema = z.string()\n .min(8)\n .max(128)\n .transform((s) => s as CorrelationId);\n\n/**\n * IdempotencyKey: client-provided, opaque.\n * Max 256 chars, printable ASCII.\n */\nexport const IdempotencyKeySchema = z.string()\n .min(1)\n .max(256)\n .regex(/^[\\x20-\\x7E]+$/, 'IdempotencyKey must be printable ASCII')\n .transform((s) => s as IdempotencyKey);\n\n/**\n * CrossingId: prefixed CUID.\n */\nexport const CrossingIdSchema = z.string()\n .regex(/^cross_[a-z0-9]{20,}$/i, 'Invalid CrossingId format')\n .transform((s) => s as CrossingId);\n\n/**\n * DispatchId: prefixed CUID, canonical lowercase.\n * No /i flag — deterministic for cryptographic contexts.\n */\nexport const DispatchIdSchema = z.string()\n .regex(/^dispatch_[a-z0-9]{20,}$/, 'Invalid DispatchId format')\n .transform((s) => s as DispatchId);\n\n// =============================================================================\n// ID Generation Helpers\n// =============================================================================\n\n/**\n * Generate a prefixed CUID-style ID.\n * Uses crypto.randomUUID() stripped of dashes + timestamp prefix.\n */\nexport function generateId(prefix: string): string {\n const timestamp = Date.now().toString(36);\n const random = crypto.randomUUID().replace(/-/g, '').slice(0, 16);\n return `${prefix}_${timestamp}${random}`;\n}\n\nexport const generateWalletId = (): WalletId => generateId('wallet') as WalletId;\nexport const generatePassportId = (): PassportId => generateId('passport') as PassportId;\nexport const generateReceiptId = (): ReceiptId => generateId('rcpt') as ReceiptId;\nexport const generateCrossingId = (): CrossingId => generateId('cross') as CrossingId;\nexport const generateDispatchId = (): DispatchId => generateId('dispatch') as DispatchId;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,iBAAkB;AAgCX,IAAM,kBAAkB,uBAAO,iBAAiB;AAiBhD,IAAM,iBAAiB,aAAE,OAAO,EAClC,MAAM,2BAA2B,yBAAyB,EAC1D,UAAU,CAAC,MAAM,CAAa;AAK5B,IAAM,mBAAmB,aAAE,OAAO,EACpC,MAAM,6BAA6B,2BAA2B,EAC9D,UAAU,CAAC,MAAM,CAAe;AAK9B,IAAM,kBAAkB,aAAE,OAAO,EACnC,MAAM,kCAAkC,0BAA0B,EAClE,UAAU,CAAC,MAAM,CAAc;AAK7B,IAAM,sBAAsB,aAAE,OAAO,EACvC,IAAI,CAAC,EACL,IAAI,GAAG,EACP,UAAU,CAAC,MAAM,CAAkB;AAMjC,IAAM,uBAAuB,aAAE,OAAO,EACxC,IAAI,CAAC,EACL,IAAI,GAAG,EACP,MAAM,kBAAkB,wCAAwC,EAChE,UAAU,CAAC,MAAM,CAAmB;AAKlC,IAAM,mBAAmB,aAAE,OAAO,EACpC,MAAM,0BAA0B,2BAA2B,EAC3D,UAAU,CAAC,MAAM,CAAe;AAM9B,IAAM,mBAAmB,aAAE,OAAO,EACpC,MAAM,4BAA4B,2BAA2B,EAC7D,UAAU,CAAC,MAAM,CAAe;AAU9B,SAAS,WAAW,QAAwB;AAC/C,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,SAAS,OAAO,WAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,EAAE;AAChE,SAAO,GAAG,MAAM,IAAI,SAAS,GAAG,MAAM;AAC1C;AAEO,IAAM,mBAAmB,MAAgB,WAAW,QAAQ;AAC5D,IAAM,qBAAqB,MAAkB,WAAW,UAAU;AAClE,IAAM,oBAAoB,MAAiB,WAAW,MAAM;AAC5D,IAAM,qBAAqB,MAAkB,WAAW,OAAO;AAC/D,IAAM,qBAAqB,MAAkB,WAAW,UAAU;","names":[]}
package/dist/ids.d.cts CHANGED
@@ -13,6 +13,8 @@ declare const ReceiptIdBrand: unique symbol;
13
13
  declare const CorrelationIdBrand: unique symbol;
14
14
  declare const IdempotencyKeyBrand: unique symbol;
15
15
  declare const CrossingIdBrand: unique symbol;
16
+ /** Runtime symbol export keeps DTS declarations nameable across entrypoints. */
17
+ declare const DispatchIdBrand: unique symbol;
16
18
  type WalletId = string & {
17
19
  readonly [WalletIdBrand]: true;
18
20
  };
@@ -31,6 +33,9 @@ type IdempotencyKey = string & {
31
33
  type CrossingId = string & {
32
34
  readonly [CrossingIdBrand]: true;
33
35
  };
36
+ type DispatchId = string & {
37
+ readonly [DispatchIdBrand]: true;
38
+ };
34
39
  /**
35
40
  * WalletId: prefixed CUID or ULID.
36
41
  */
@@ -56,6 +61,11 @@ declare const IdempotencyKeySchema: z.ZodEffects<z.ZodString, IdempotencyKey, st
56
61
  * CrossingId: prefixed CUID.
57
62
  */
58
63
  declare const CrossingIdSchema: z.ZodEffects<z.ZodString, CrossingId, string>;
64
+ /**
65
+ * DispatchId: prefixed CUID, canonical lowercase.
66
+ * No /i flag — deterministic for cryptographic contexts.
67
+ */
68
+ declare const DispatchIdSchema: z.ZodEffects<z.ZodString, DispatchId, string>;
59
69
  /**
60
70
  * Generate a prefixed CUID-style ID.
61
71
  * Uses crypto.randomUUID() stripped of dashes + timestamp prefix.
@@ -65,5 +75,6 @@ declare const generateWalletId: () => WalletId;
65
75
  declare const generatePassportId: () => PassportId;
66
76
  declare const generateReceiptId: () => ReceiptId;
67
77
  declare const generateCrossingId: () => CrossingId;
78
+ declare const generateDispatchId: () => DispatchId;
68
79
 
69
- export { type CorrelationId, CorrelationIdSchema, type CrossingId, CrossingIdSchema, type IdempotencyKey, IdempotencyKeySchema, type PassportId, PassportIdSchema, type ReceiptId, ReceiptIdSchema, type WalletId, WalletIdSchema, generateCrossingId, generateId, generatePassportId, generateReceiptId, generateWalletId };
80
+ export { type CorrelationId, CorrelationIdSchema, type CrossingId, CrossingIdSchema, type DispatchId, DispatchIdBrand, DispatchIdSchema, type IdempotencyKey, IdempotencyKeySchema, type PassportId, PassportIdSchema, type ReceiptId, ReceiptIdSchema, type WalletId, WalletIdSchema, generateCrossingId, generateDispatchId, generateId, generatePassportId, generateReceiptId, generateWalletId };
package/dist/ids.d.ts CHANGED
@@ -13,6 +13,8 @@ declare const ReceiptIdBrand: unique symbol;
13
13
  declare const CorrelationIdBrand: unique symbol;
14
14
  declare const IdempotencyKeyBrand: unique symbol;
15
15
  declare const CrossingIdBrand: unique symbol;
16
+ /** Runtime symbol export keeps DTS declarations nameable across entrypoints. */
17
+ declare const DispatchIdBrand: unique symbol;
16
18
  type WalletId = string & {
17
19
  readonly [WalletIdBrand]: true;
18
20
  };
@@ -31,6 +33,9 @@ type IdempotencyKey = string & {
31
33
  type CrossingId = string & {
32
34
  readonly [CrossingIdBrand]: true;
33
35
  };
36
+ type DispatchId = string & {
37
+ readonly [DispatchIdBrand]: true;
38
+ };
34
39
  /**
35
40
  * WalletId: prefixed CUID or ULID.
36
41
  */
@@ -56,6 +61,11 @@ declare const IdempotencyKeySchema: z.ZodEffects<z.ZodString, IdempotencyKey, st
56
61
  * CrossingId: prefixed CUID.
57
62
  */
58
63
  declare const CrossingIdSchema: z.ZodEffects<z.ZodString, CrossingId, string>;
64
+ /**
65
+ * DispatchId: prefixed CUID, canonical lowercase.
66
+ * No /i flag — deterministic for cryptographic contexts.
67
+ */
68
+ declare const DispatchIdSchema: z.ZodEffects<z.ZodString, DispatchId, string>;
59
69
  /**
60
70
  * Generate a prefixed CUID-style ID.
61
71
  * Uses crypto.randomUUID() stripped of dashes + timestamp prefix.
@@ -65,5 +75,6 @@ declare const generateWalletId: () => WalletId;
65
75
  declare const generatePassportId: () => PassportId;
66
76
  declare const generateReceiptId: () => ReceiptId;
67
77
  declare const generateCrossingId: () => CrossingId;
78
+ declare const generateDispatchId: () => DispatchId;
68
79
 
69
- export { type CorrelationId, CorrelationIdSchema, type CrossingId, CrossingIdSchema, type IdempotencyKey, IdempotencyKeySchema, type PassportId, PassportIdSchema, type ReceiptId, ReceiptIdSchema, type WalletId, WalletIdSchema, generateCrossingId, generateId, generatePassportId, generateReceiptId, generateWalletId };
80
+ export { type CorrelationId, CorrelationIdSchema, type CrossingId, CrossingIdSchema, type DispatchId, DispatchIdBrand, DispatchIdSchema, type IdempotencyKey, IdempotencyKeySchema, type PassportId, PassportIdSchema, type ReceiptId, ReceiptIdSchema, type WalletId, WalletIdSchema, generateCrossingId, generateDispatchId, generateId, generatePassportId, generateReceiptId, generateWalletId };
package/dist/ids.js CHANGED
@@ -1,24 +1,31 @@
1
1
  import {
2
2
  CorrelationIdSchema,
3
3
  CrossingIdSchema,
4
+ DispatchIdBrand,
5
+ DispatchIdSchema,
4
6
  IdempotencyKeySchema,
5
7
  PassportIdSchema,
6
8
  ReceiptIdSchema,
7
9
  WalletIdSchema,
8
10
  generateCrossingId,
11
+ generateDispatchId,
9
12
  generateId,
10
13
  generatePassportId,
11
14
  generateReceiptId,
12
15
  generateWalletId
13
- } from "./chunk-RQP6MVT3.js";
16
+ } from "./chunk-ADIQW3HO.js";
17
+ import "./chunk-DGUM43GV.js";
14
18
  export {
15
19
  CorrelationIdSchema,
16
20
  CrossingIdSchema,
21
+ DispatchIdBrand,
22
+ DispatchIdSchema,
17
23
  IdempotencyKeySchema,
18
24
  PassportIdSchema,
19
25
  ReceiptIdSchema,
20
26
  WalletIdSchema,
21
27
  generateCrossingId,
28
+ generateDispatchId,
22
29
  generateId,
23
30
  generatePassportId,
24
31
  generateReceiptId,