@smonn/ids 0.5.0 → 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.
Files changed (42) hide show
  1. package/README.md +400 -18
  2. package/dist/cli.mjs +195 -16
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/drizzle-CeSni5PB.d.mts +44 -0
  5. package/dist/drizzle-CeSni5PB.d.mts.map +1 -0
  6. package/dist/drizzle.d.mts +2 -0
  7. package/dist/drizzle.mjs +42 -0
  8. package/dist/drizzle.mjs.map +1 -0
  9. package/dist/express.d.mts +92 -0
  10. package/dist/express.d.mts.map +1 -0
  11. package/dist/express.mjs +90 -0
  12. package/dist/express.mjs.map +1 -0
  13. package/dist/hono.d.mts +75 -0
  14. package/dist/hono.d.mts.map +1 -0
  15. package/dist/hono.mjs +63 -0
  16. package/dist/hono.mjs.map +1 -0
  17. package/dist/kysely.d.mts +55 -0
  18. package/dist/kysely.d.mts.map +1 -0
  19. package/dist/kysely.mjs +42 -0
  20. package/dist/kysely.mjs.map +1 -0
  21. package/dist/{opaque-B4ps7Pqk.mjs → opaque-goLnFoo7.mjs} +29 -13
  22. package/dist/opaque-goLnFoo7.mjs.map +1 -0
  23. package/dist/opaque.d.mts +33 -9
  24. package/dist/opaque.d.mts.map +1 -1
  25. package/dist/opaque.mjs +1 -1
  26. package/dist/prisma.d.mts +84 -0
  27. package/dist/prisma.d.mts.map +1 -0
  28. package/dist/prisma.mjs +53 -0
  29. package/dist/prisma.mjs.map +1 -0
  30. package/dist/reverse--n4D2yxu.mjs +87 -0
  31. package/dist/reverse--n4D2yxu.mjs.map +1 -0
  32. package/dist/reverse.d.mts +76 -0
  33. package/dist/reverse.d.mts.map +1 -0
  34. package/dist/reverse.mjs +2 -0
  35. package/dist/wrapped-Dw5mHQhn.mjs +363 -0
  36. package/dist/wrapped-Dw5mHQhn.mjs.map +1 -0
  37. package/dist/wrapped.d.mts +86 -8
  38. package/dist/wrapped.d.mts.map +1 -1
  39. package/dist/wrapped.mjs +1 -335
  40. package/package.json +38 -3
  41. package/dist/opaque-B4ps7Pqk.mjs.map +0 -1
  42. package/dist/wrapped.mjs.map +0 -1
package/dist/cli.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as createTimestampId } from "./timestamp-Bgzxx8bE.mjs";
3
- import { i as encodeOpaqueKey, n as importOpaqueKey, r as decodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-B4ps7Pqk.mjs";
3
+ import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-goLnFoo7.mjs";
4
+ import { t as createReverseTimestampId } from "./reverse--n4D2yxu.mjs";
5
+ import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-Dw5mHQhn.mjs";
4
6
  //#region src/cli/codec-options.ts
5
7
  function codecOpts(opts) {
6
8
  const o = { allowDuplicateBrand: true };
@@ -34,7 +36,8 @@ function splitFlags(args) {
34
36
  "--count",
35
37
  "-c",
36
38
  "--bits",
37
- "--key-format"
39
+ "--key-format",
40
+ "--kind"
38
41
  ]);
39
42
  const addFlag = (flag) => {
40
43
  const canonical = canonicalFlag(flag);
@@ -45,9 +48,9 @@ function splitFlags(args) {
45
48
  for (let i = 0; i < args.length; i++) {
46
49
  const raw = args[i];
47
50
  const { flag, inlineValue } = splitFlagToken(raw);
48
- if (flag === "--opaque") {
51
+ if (flag === "--opaque" || flag === "--wrapped" || flag === "--reverse") {
49
52
  addFlag(flag);
50
- if (inlineValue !== void 0) errors.push("flag does not take a value: --opaque");
53
+ if (inlineValue !== void 0) errors.push(`flag does not take a value: ${flag}`);
51
54
  continue;
52
55
  }
53
56
  if (valueFlags.has(flag)) {
@@ -86,6 +89,9 @@ function canonicalFlag(flag) {
86
89
  }
87
90
  const knownFlags = new Set([
88
91
  "--opaque",
92
+ "--wrapped",
93
+ "--reverse",
94
+ "--kind",
89
95
  "--key-format",
90
96
  "--count",
91
97
  "-c",
@@ -112,12 +118,22 @@ function parseBits(values) {
112
118
  if (raw === "256") return 256;
113
119
  return `--bits must be 128, 192, or 256, got '${raw}'`;
114
120
  }
121
+ function parseKind(values) {
122
+ const raw = values.get("--kind");
123
+ if (raw === void 0) return void 0;
124
+ if (raw === "") return "--kind requires a value";
125
+ if (raw === "u32" || raw === "i32" || raw === "u64" || raw === "i64") return raw;
126
+ return `--kind must be u32, i32, u64, or i64, got '${raw}'`;
127
+ }
128
+ function isKindError(result) {
129
+ return result !== "u32" && result !== "i32" && result !== "u64" && result !== "i64";
130
+ }
115
131
  //#endregion
116
132
  //#region src/cli/opaque-key.ts
117
133
  function isKeyFormatError(result) {
118
134
  return result !== "hex" && result !== "base64url";
119
135
  }
120
- function parseKeyFormatFlag(values) {
136
+ function parseKeyFormatFlag$1(values) {
121
137
  const fromFlag = values.get("--key-format");
122
138
  if (fromFlag === void 0) return void 0;
123
139
  if (fromFlag === "") return "--key-format requires a value";
@@ -125,12 +141,12 @@ function parseKeyFormatFlag(values) {
125
141
  return `--key-format must be hex or base64url, got '${fromFlag}'`;
126
142
  }
127
143
  function parseKeygenFormat(values) {
128
- const fromFlag = parseKeyFormatFlag(values);
144
+ const fromFlag = parseKeyFormatFlag$1(values);
129
145
  if (fromFlag === void 0) return "hex";
130
146
  return fromFlag;
131
147
  }
132
148
  function parseOpaqueKeyFormat(values, opts) {
133
- const fromFlag = parseKeyFormatFlag(values);
149
+ const fromFlag = parseKeyFormatFlag$1(values);
134
150
  if (fromFlag !== void 0) return fromFlag;
135
151
  const fromEnv = (opts.env ?? process.env).IDS_KEY_FORMAT;
136
152
  if (fromEnv === void 0 || fromEnv === "") return "hex";
@@ -154,6 +170,7 @@ function runGenerate(args, opts) {
154
170
  "--count",
155
171
  "-c",
156
172
  "--opaque",
173
+ "--reverse",
157
174
  "--key-format"
158
175
  ]));
159
176
  if (unsupported !== void 0) {
@@ -176,6 +193,11 @@ function runGenerate(args, opts) {
176
193
  return Promise.resolve(1);
177
194
  }
178
195
  const opaque = flags.has("--opaque");
196
+ const reverse = flags.has("--reverse");
197
+ if (reverse && opaque) {
198
+ opts.stderr("cannot use --reverse and --opaque together\n");
199
+ return Promise.resolve(1);
200
+ }
179
201
  if (!opaque && flags.has("--key-format")) {
180
202
  opts.stderr("--key-format requires --opaque\n");
181
203
  return Promise.resolve(1);
@@ -188,6 +210,17 @@ function runGenerate(args, opts) {
188
210
  }
189
211
  return runOpaqueGenerate(brand ?? "", count, format, opts);
190
212
  }
213
+ if (reverse) {
214
+ let codec;
215
+ try {
216
+ codec = createReverseTimestampId(brand ?? "", codecOpts(opts));
217
+ } catch (err) {
218
+ opts.stderr(err.message + "\n");
219
+ return Promise.resolve(1);
220
+ }
221
+ for (let i = 0; i < count; i++) opts.stdout(codec.generate() + "\n");
222
+ return Promise.resolve(0);
223
+ }
191
224
  let codec;
192
225
  try {
193
226
  codec = createTimestampId(brand ?? "", codecOpts(opts));
@@ -219,6 +252,16 @@ async function runOpaqueGenerate(brand, count, format, opts) {
219
252
  }
220
253
  //#endregion
221
254
  //#region src/cli/format.ts
255
+ function formatWrappedInspectOutput(result) {
256
+ const inputLine = describeInputForm(result.input, result.canonical);
257
+ return [
258
+ `brand: ${result.brand}`,
259
+ `lookup-key: ${result.lookupKey.toString()}`,
260
+ `canonical: ${result.canonical}`,
261
+ `input: ${inputLine}`,
262
+ ""
263
+ ].join("\n");
264
+ }
222
265
  function formatInspectOutput(result) {
223
266
  const relative = formatRelative(result.timestamp.getTime(), result.nowMs);
224
267
  const inputLine = describeInputForm(result.input, result.canonical);
@@ -264,20 +307,51 @@ function unit(n, name) {
264
307
  return `${n} ${n === 1 ? name : `${name}s`}`;
265
308
  }
266
309
  //#endregion
310
+ //#region src/cli/wrapping-key.ts
311
+ function parseKeyFormatFlag(values) {
312
+ const fromFlag = values.get("--key-format");
313
+ if (fromFlag === void 0) return void 0;
314
+ if (fromFlag === "") return "--key-format requires a value";
315
+ if (fromFlag === "hex" || fromFlag === "base64url") return fromFlag;
316
+ return `--key-format must be hex or base64url, got '${fromFlag}'`;
317
+ }
318
+ function parseWrappingKeyFormat(values, opts) {
319
+ const fromFlag = parseKeyFormatFlag(values);
320
+ if (fromFlag !== void 0) return fromFlag;
321
+ const fromEnv = (opts.env ?? process.env).IDS_WRAPPING_KEY_FORMAT;
322
+ if (fromEnv === void 0 || fromEnv === "") return "hex";
323
+ if (fromEnv === "hex" || fromEnv === "base64url") return fromEnv;
324
+ return `IDS_WRAPPING_KEY_FORMAT must be hex or base64url, got '${fromEnv}'`;
325
+ }
326
+ async function loadWrappingKey(opts, format) {
327
+ const raw = (opts.env ?? process.env).IDS_WRAPPING_KEY;
328
+ if (raw === void 0 || raw === "") return "missing IDS_WRAPPING_KEY environment variable";
329
+ try {
330
+ return importWrappingKey(decodeWrappingKey(raw, format));
331
+ } catch (err) {
332
+ return err.message;
333
+ }
334
+ }
335
+ //#endregion
267
336
  //#region src/cli/usage.ts
268
337
  function usage() {
269
338
  return [
270
339
  "Usage: ids <subcommand> [args]",
271
340
  "",
272
341
  "Subcommands:",
273
- " inspect, i <id> [--opaque] [--key-format hex|base64url]",
274
- " Decode an ID and print brand, timestamp, and canonical form.",
342
+ " inspect, i <id> [--opaque] [--wrapped --kind u32|i32|u64|i64] [--reverse] [--key-format hex|base64url]",
343
+ " Decode an ID and print brand, timestamp (or lookup key), and canonical form.",
275
344
  " --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
276
- " generate, g <brand> [--count, -c N] [--opaque] [--key-format hex|base64url]",
345
+ " --wrapped reads the wrapping key from IDS_WRAPPING_KEY (hex by default; IDS_WRAPPING_KEY_FORMAT or --key-format).",
346
+ " --kind is required with --wrapped: u32, i32, u64, or i64.",
347
+ " --reverse decodes a Reverse Timestamp ID (newest-first sort order).",
348
+ " generate, g <brand> [--count, -c N] [--opaque] [--reverse] [--key-format hex|base64url]",
277
349
  ` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,
278
350
  " --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
279
- " keygen, k [--bits 128|192|256] [--key-format hex|base64url]",
351
+ " --reverse mints Reverse Timestamp IDs (newest-first sort order).",
352
+ " keygen, k [--wrapped] [--bits 128|192|256] [--key-format hex|base64url]",
280
353
  " Emit a random AES key for importOpaqueKey (stdout only).",
354
+ " --wrapped emits a wrapping key for importWrappingKey instead (IDS_WRAPPING_KEY).",
281
355
  ""
282
356
  ].join("\n");
283
357
  }
@@ -285,7 +359,13 @@ function usage() {
285
359
  //#region src/cli/commands/inspect.ts
286
360
  function runInspect(args, opts) {
287
361
  const { flags, values, positionals, errors } = splitFlags(args);
288
- const unsupported = unsupportedFlagForCommand("inspect", flags, new Set(["--opaque", "--key-format"]));
362
+ const unsupported = unsupportedFlagForCommand("inspect", flags, new Set([
363
+ "--opaque",
364
+ "--wrapped",
365
+ "--reverse",
366
+ "--kind",
367
+ "--key-format"
368
+ ]));
289
369
  if (unsupported !== void 0) {
290
370
  opts.stderr(unsupported + "\n");
291
371
  return Promise.resolve(1);
@@ -305,11 +385,42 @@ function runInspect(args, opts) {
305
385
  return Promise.resolve(1);
306
386
  }
307
387
  const opaque = flags.has("--opaque");
308
- if (!opaque && flags.has("--key-format")) {
309
- opts.stderr("--key-format requires --opaque\n");
388
+ const wrapped = flags.has("--wrapped");
389
+ const reverse = flags.has("--reverse");
390
+ if (opaque && wrapped) {
391
+ opts.stderr("cannot use --wrapped and --opaque together\n");
392
+ return Promise.resolve(1);
393
+ }
394
+ if (reverse && opaque) {
395
+ opts.stderr("cannot use --reverse and --opaque together\n");
396
+ return Promise.resolve(1);
397
+ }
398
+ if (reverse && wrapped) {
399
+ opts.stderr("cannot use --reverse and --wrapped together\n");
400
+ return Promise.resolve(1);
401
+ }
402
+ if (!opaque && !wrapped && flags.has("--key-format")) {
403
+ opts.stderr("--key-format requires --opaque or --wrapped\n");
310
404
  return Promise.resolve(1);
311
405
  }
312
406
  const brand = input.slice(0, 3).toLowerCase();
407
+ if (wrapped) {
408
+ const kind = parseKind(values);
409
+ if (kind === void 0) {
410
+ opts.stderr("--kind is required with --wrapped\n");
411
+ return Promise.resolve(1);
412
+ }
413
+ if (isKindError(kind)) {
414
+ opts.stderr(kind + "\n");
415
+ return Promise.resolve(1);
416
+ }
417
+ const format = parseWrappingKeyFormat(values, opts);
418
+ if (isKeyFormatError(format)) {
419
+ opts.stderr(format + "\n");
420
+ return Promise.resolve(1);
421
+ }
422
+ return runWrappedInspect(brand, input, kind, format, opts);
423
+ }
313
424
  if (opaque) {
314
425
  const format = parseOpaqueKeyFormat(values, opts);
315
426
  if (isKeyFormatError(format)) {
@@ -318,6 +429,31 @@ function runInspect(args, opts) {
318
429
  }
319
430
  return runOpaqueInspect(brand, input, format, opts);
320
431
  }
432
+ if (reverse) {
433
+ let reverseCodec;
434
+ try {
435
+ reverseCodec = createReverseTimestampId(brand, codecOpts(opts));
436
+ } catch (err) {
437
+ opts.stderr(err.message + "\n");
438
+ return Promise.resolve(1);
439
+ }
440
+ const reverseValidation = reverseCodec["~standard"].validate(input);
441
+ if (reverseValidation.issues) {
442
+ opts.stderr(reverseValidation.issues[0].message + "\n");
443
+ return Promise.resolve(1);
444
+ }
445
+ const reverseCanonical = reverseValidation.value;
446
+ const reverseTimestamp = reverseCodec.extractTimestamp(reverseCanonical);
447
+ const reverseNowMs = (opts.now ?? Date.now)();
448
+ opts.stdout(formatInspectOutput({
449
+ brand,
450
+ timestamp: reverseTimestamp,
451
+ canonical: reverseCanonical,
452
+ input,
453
+ nowMs: reverseNowMs
454
+ }));
455
+ return Promise.resolve(0);
456
+ }
321
457
  let codec;
322
458
  try {
323
459
  codec = createTimestampId(brand, codecOpts(opts));
@@ -342,6 +478,44 @@ function runInspect(args, opts) {
342
478
  }));
343
479
  return Promise.resolve(0);
344
480
  }
481
+ async function runWrappedInspect(brand, input, kind, format, opts) {
482
+ const keyResult = await loadWrappingKey(opts, format);
483
+ if (typeof keyResult === "string") {
484
+ opts.stderr(keyResult + "\n");
485
+ return 1;
486
+ }
487
+ let codec;
488
+ try {
489
+ codec = createWrappedKeyId(brand, {
490
+ kind,
491
+ keys: [keyResult],
492
+ allowDuplicateBrand: true
493
+ });
494
+ } catch (err) {
495
+ opts.stderr(err.message + "\n");
496
+ return 1;
497
+ }
498
+ const validation = codec["~standard"].validate(input);
499
+ if (validation.issues) {
500
+ opts.stderr(validation.issues[0].message + "\n");
501
+ return 1;
502
+ }
503
+ const canonical = validation.value;
504
+ let lookupKey;
505
+ try {
506
+ lookupKey = await codec.unwrap(canonical);
507
+ } catch (err) {
508
+ opts.stderr(err.message + "\n");
509
+ return 1;
510
+ }
511
+ opts.stdout(formatWrappedInspectOutput({
512
+ brand,
513
+ lookupKey,
514
+ canonical,
515
+ input
516
+ }));
517
+ return 0;
518
+ }
345
519
  async function runOpaqueInspect(brand, input, format, opts) {
346
520
  const keyResult = await loadOpaqueKey(opts, format);
347
521
  if (typeof keyResult === "string") {
@@ -380,7 +554,11 @@ async function runOpaqueInspect(brand, input, format, opts) {
380
554
  //#region src/cli/commands/keygen.ts
381
555
  function runKeygen(args, opts) {
382
556
  const { flags, values, positionals, errors } = splitFlags(args);
383
- const unsupported = unsupportedFlagForCommand("keygen", flags, new Set(["--bits", "--key-format"]));
557
+ const unsupported = unsupportedFlagForCommand("keygen", flags, new Set([
558
+ "--wrapped",
559
+ "--bits",
560
+ "--key-format"
561
+ ]));
384
562
  if (unsupported !== void 0) {
385
563
  opts.stderr(unsupported + "\n");
386
564
  return Promise.resolve(1);
@@ -406,7 +584,8 @@ function runKeygen(args, opts) {
406
584
  }
407
585
  const bytes = new Uint8Array(bits / 8);
408
586
  crypto.getRandomValues(bytes);
409
- opts.stdout(encodeOpaqueKey(bytes, format) + "\n");
587
+ const wrapped = flags.has("--wrapped");
588
+ opts.stdout((wrapped ? encodeWrappingKey(bytes, format) : encodeOpaqueKey(bytes, format)) + "\n");
410
589
  return Promise.resolve(0);
411
590
  }
412
591
  //#endregion
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli/codec-options.ts","../src/cli/constants.ts","../src/cli/flags.ts","../src/cli/opaque-key.ts","../src/cli/commands/generate.ts","../src/cli/format.ts","../src/cli/usage.ts","../src/cli/commands/inspect.ts","../src/cli/commands/keygen.ts","../src/cli.ts","../bin/cli.ts"],"sourcesContent":["import type { TimestampOptions } from \"../timestamp.js\";\nimport type { RunOpts } from \"./types.js\";\n\nexport function codecOpts(opts: RunOpts): Partial<TimestampOptions> {\n // CLI invocations are intentionally ephemeral: one codec per run, never\n // retained, so this is not the duplicate-brand warning case.\n const o: Partial<TimestampOptions> = { allowDuplicateBrand: true };\n if (opts.now !== undefined) o.now = opts.now;\n if (opts.rng !== undefined) o.rng = opts.rng;\n return o;\n}\n","export const maxGenerateCount = 10_000;\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport type ParsedFlags = {\n flags: Set<string>;\n values: Map<string, string>;\n positionals: string[];\n errors: string[];\n};\n\nfunction splitFlagToken(arg: string): { flag: string; inlineValue: string | undefined } {\n const eq = arg.indexOf(\"=\");\n if (eq <= 0) return { flag: arg, inlineValue: undefined };\n return { flag: arg.slice(0, eq), inlineValue: arg.slice(eq + 1) };\n}\n\nexport function splitFlags(args: ReadonlyArray<string>): ParsedFlags {\n const flags = new Set<string>();\n const values = new Map<string, string>();\n const positionals: string[] = [];\n const errors: string[] = [];\n const seenFlags = new Set<string>();\n const valueFlags = new Set([\"--count\", \"-c\", \"--bits\", \"--key-format\"]);\n const addFlag = (flag: string) => {\n const canonical = canonicalFlag(flag);\n if (seenFlags.has(canonical)) errors.push(`duplicate flag: ${canonical}`);\n seenFlags.add(canonical);\n flags.add(flag);\n };\n for (let i = 0; i < args.length; i++) {\n const raw = args[i]!;\n const { flag, inlineValue } = splitFlagToken(raw);\n if (flag === \"--opaque\") {\n addFlag(flag);\n if (inlineValue !== undefined) errors.push(\"flag does not take a value: --opaque\");\n continue;\n }\n if (valueFlags.has(flag)) {\n if (inlineValue !== undefined) {\n addFlag(flag);\n values.set(flag, inlineValue);\n continue;\n }\n const value = args[i + 1];\n if (value === undefined || value.startsWith(\"-\")) {\n addFlag(flag);\n values.set(flag, \"\");\n continue;\n }\n addFlag(flag);\n values.set(flag, value);\n i++;\n continue;\n }\n if (flag.startsWith(\"-\")) {\n addFlag(flag);\n continue;\n }\n positionals.push(raw);\n }\n return { flags, values, positionals, errors };\n}\n\nfunction canonicalFlag(flag: string): string {\n if (flag === \"-c\") return \"--count\";\n return flag;\n}\n\nconst knownFlags = new Set([\"--opaque\", \"--key-format\", \"--count\", \"-c\", \"--bits\"]);\n\nexport function unsupportedFlagForCommand(\n command: string,\n flags: Set<string>,\n allowed: Set<string>,\n): string | undefined {\n for (const flag of flags) {\n if (!allowed.has(flag)) {\n return knownFlags.has(flag)\n ? `unsupported flag for ${command}: ${flag}`\n : `unsupported flag: ${flag}`;\n }\n }\n return undefined;\n}\n\nexport function parseCount(values: Map<string, string>): number | string {\n const raw = values.get(\"--count\") ?? values.get(\"-c\");\n if (raw === undefined) return 1;\n if (raw === \"\") return \"--count requires a value\";\n if (!/^[1-9][0-9]*$/.test(raw)) return `--count must be a positive integer, got '${raw}'`;\n const count = Number.parseInt(raw, 10);\n if (!Number.isSafeInteger(count) || count > maxGenerateCount) {\n return `--count must be at most ${maxGenerateCount}, got '${raw}'`;\n }\n return count;\n}\n\nexport function parseBits(values: Map<string, string>): number | string {\n const raw = values.get(\"--bits\");\n if (raw === undefined) return 256;\n if (raw === \"\") return \"--bits requires a value\";\n if (raw === \"128\") return 128;\n if (raw === \"192\") return 192;\n if (raw === \"256\") return 256;\n return `--bits must be 128, 192, or 256, got '${raw}'`;\n}\n","import { decodeOpaqueKey, importOpaqueKey, type OpaqueKeyFormat } from \"../opaque.js\";\nimport type { RunOpts } from \"./types.js\";\n\nexport function isKeyFormatError(result: OpaqueKeyFormat | string): result is string {\n return result !== \"hex\" && result !== \"base64url\";\n}\n\nfunction parseKeyFormatFlag(values: Map<string, string>): OpaqueKeyFormat | string | undefined {\n const fromFlag = values.get(\"--key-format\");\n if (fromFlag === undefined) return undefined;\n if (fromFlag === \"\") return \"--key-format requires a value\";\n if (fromFlag === \"hex\" || fromFlag === \"base64url\") return fromFlag;\n return `--key-format must be hex or base64url, got '${fromFlag}'`;\n}\n\nexport function parseKeygenFormat(values: Map<string, string>): OpaqueKeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag === undefined) return \"hex\";\n return fromFlag;\n}\n\nexport function parseOpaqueKeyFormat(\n values: Map<string, string>,\n opts: RunOpts,\n): OpaqueKeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag !== undefined) return fromFlag;\n const env = opts.env ?? process.env;\n const fromEnv = env.IDS_KEY_FORMAT;\n if (fromEnv === undefined || fromEnv === \"\") return \"hex\";\n if (fromEnv === \"hex\" || fromEnv === \"base64url\") return fromEnv;\n return `IDS_KEY_FORMAT must be hex or base64url, got '${fromEnv}'`;\n}\n\nexport async function loadOpaqueKey(\n opts: RunOpts,\n format: OpaqueKeyFormat,\n): Promise<CryptoKey | string> {\n const env = opts.env ?? process.env;\n const raw = env.IDS_KEY;\n if (raw === undefined || raw === \"\") return \"missing IDS_KEY environment variable\";\n try {\n return importOpaqueKey(decodeOpaqueKey(raw, format));\n } catch (err) {\n return (err as Error).message;\n }\n}\n","import { createTimestampId } from \"../../timestamp.js\";\nimport { createOpaqueTimestampId, type OpaqueKeyFormat } from \"../../opaque.js\";\nimport { codecOpts } from \"../codec-options.js\";\nimport { parseCount, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, loadOpaqueKey, parseOpaqueKeyFormat } from \"../opaque-key.js\";\nimport type { RunOpts } from \"../types.js\";\n\nexport function runGenerate(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const { flags, values, positionals, errors } = splitFlags(args);\n const unsupported = unsupportedFlagForCommand(\n \"generate\",\n flags,\n new Set([\"--count\", \"-c\", \"--opaque\", \"--key-format\"]),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const [brand] = positionals;\n const count = parseCount(values);\n if (typeof count === \"string\") {\n opts.stderr(count + \"\\n\");\n return Promise.resolve(1);\n }\n const opaque = flags.has(\"--opaque\");\n if (!opaque && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque\\n\");\n return Promise.resolve(1);\n }\n if (opaque) {\n const format = parseOpaqueKeyFormat(values, opts);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n return runOpaqueGenerate(brand ?? \"\", count, format, opts);\n }\n let codec;\n try {\n codec = createTimestampId(brand ?? \"\", codecOpts(opts));\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return Promise.resolve(1);\n }\n for (let i = 0; i < count; i++) opts.stdout(codec.generate() + \"\\n\");\n return Promise.resolve(0);\n}\n\nasync function runOpaqueGenerate(\n brand: string,\n count: number,\n format: OpaqueKeyFormat,\n opts: RunOpts,\n): Promise<number> {\n const keyResult = await loadOpaqueKey(opts, format);\n if (typeof keyResult === \"string\") {\n opts.stderr(keyResult + \"\\n\");\n return 1;\n }\n let codec;\n try {\n codec = createOpaqueTimestampId(brand, { key: keyResult, ...codecOpts(opts) });\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return 1;\n }\n for (let i = 0; i < count; i++) opts.stdout((await codec.generate()) + \"\\n\");\n return 0;\n}\n","import type { Id } from \"../types.js\";\n\ntype InspectOutput = {\n brand: string;\n timestamp: Date;\n canonical: Id<string>;\n input: string;\n nowMs: number;\n};\n\nexport function formatInspectOutput(result: InspectOutput): string {\n const relative = formatRelative(result.timestamp.getTime(), result.nowMs);\n const inputLine = describeInputForm(result.input, result.canonical);\n return [\n `brand: ${result.brand}`,\n `timestamp: ${result.timestamp.toISOString()} (${relative})`,\n `canonical: ${result.canonical}`,\n `input: ${inputLine}`,\n \"\",\n ].join(\"\\n\");\n}\n\nexport function describeInputForm(input: string, canonical: Id<string>): string {\n if (input === canonical) return \"canonical\";\n const notes: string[] = [];\n if (input !== input.toLowerCase()) notes.push(\"was uppercase\");\n if (/[ilo]/i.test(input.slice(4))) notes.push(\"used Crockford aliases\");\n return `not canonical (${notes.join(\" + \")})`;\n}\n\nconst msPerSecond = 1000;\nconst msPerMinute = 60 * msPerSecond;\nconst msPerHour = 60 * msPerMinute;\nconst msPerDay = 24 * msPerHour;\nconst daysPerMonth = 30.44;\nconst monthsPerYear = 12;\n\nexport function formatRelative(thenMs: number, nowMs: number): string {\n const diff = nowMs - thenMs;\n const abs = Math.abs(diff);\n const suffix = diff < 0 ? \"from now\" : \"ago\";\n\n const head = headUnits(abs);\n return head === \"\" ? \"just now\" : `${head} ${suffix}`;\n}\n\nfunction headUnits(abs: number): string {\n if (abs < msPerMinute) return \"\";\n if (abs < msPerHour) return unit(Math.round(abs / msPerMinute), \"minute\");\n if (abs < msPerDay) return unit(Math.round(abs / msPerHour), \"hour\");\n if (abs < msPerDay * daysPerMonth) return unit(Math.round(abs / msPerDay), \"day\");\n\n const totalMonths = Math.round(abs / (msPerDay * daysPerMonth));\n if (totalMonths < monthsPerYear) return unit(totalMonths, \"month\");\n\n const years = Math.floor(totalMonths / monthsPerYear);\n const months = totalMonths % monthsPerYear;\n return months === 0 ? unit(years, \"year\") : `${unit(years, \"year\")} ${unit(months, \"month\")}`;\n}\n\nfunction unit(n: number, name: string): string {\n return `${n} ${n === 1 ? name : `${name}s`}`;\n}\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport function usage(): string {\n return [\n \"Usage: ids <subcommand> [args]\",\n \"\",\n \"Subcommands:\",\n \" inspect, i <id> [--opaque] [--key-format hex|base64url]\",\n \" Decode an ID and print brand, timestamp, and canonical form.\",\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" generate, g <brand> [--count, -c N] [--opaque] [--key-format hex|base64url]\",\n ` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" keygen, k [--bits 128|192|256] [--key-format hex|base64url]\",\n \" Emit a random AES key for importOpaqueKey (stdout only).\",\n \"\",\n ].join(\"\\n\");\n}\n","import { createTimestampId } from \"../../timestamp.js\";\nimport { createOpaqueTimestampId, type OpaqueKeyFormat } from \"../../opaque.js\";\nimport { codecOpts } from \"../codec-options.js\";\nimport { formatInspectOutput } from \"../format.js\";\nimport { splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, loadOpaqueKey, parseOpaqueKeyFormat } from \"../opaque-key.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { usage } from \"../usage.js\";\n\nexport function runInspect(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const { flags, values, positionals, errors } = splitFlags(args);\n const unsupported = unsupportedFlagForCommand(\n \"inspect\",\n flags,\n new Set([\"--opaque\", \"--key-format\"]),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const [input] = positionals;\n if (input === undefined) {\n opts.stderr(usage());\n return Promise.resolve(1);\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const opaque = flags.has(\"--opaque\");\n if (!opaque && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque\\n\");\n return Promise.resolve(1);\n }\n const brand = input.slice(0, 3).toLowerCase();\n if (opaque) {\n const format = parseOpaqueKeyFormat(values, opts);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n return runOpaqueInspect(brand, input, format, opts);\n }\n let codec;\n try {\n codec = createTimestampId(brand, codecOpts(opts));\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return Promise.resolve(1);\n }\n const validation = codec[\"~standard\"].validate(input);\n if (validation.issues) {\n opts.stderr(validation.issues[0]!.message + \"\\n\");\n return Promise.resolve(1);\n }\n const canonical = validation.value;\n const timestamp = codec.extractTimestamp(canonical);\n const nowMs = (opts.now ?? Date.now)();\n opts.stdout(\n formatInspectOutput({\n brand,\n timestamp,\n canonical,\n input,\n nowMs,\n }),\n );\n return Promise.resolve(0);\n}\n\nasync function runOpaqueInspect(\n brand: string,\n input: string,\n format: OpaqueKeyFormat,\n opts: RunOpts,\n): Promise<number> {\n const keyResult = await loadOpaqueKey(opts, format);\n if (typeof keyResult === \"string\") {\n opts.stderr(keyResult + \"\\n\");\n return 1;\n }\n let codec;\n try {\n codec = createOpaqueTimestampId(brand, { key: keyResult, ...codecOpts(opts) });\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return 1;\n }\n const validation = codec[\"~standard\"].validate(input);\n if (validation.issues) {\n opts.stderr(validation.issues[0]!.message + \"\\n\");\n return 1;\n }\n const canonical = validation.value;\n const timestamp = await codec.extractTimestamp(canonical);\n const nowMs = (opts.now ?? Date.now)();\n opts.stderr(\n \"note: timestamp assumes IDS_KEY matches the key used at generation; a wrong key yields a plausible but incorrect timestamp\\n\",\n );\n opts.stdout(\n formatInspectOutput({\n brand,\n timestamp,\n canonical,\n input,\n nowMs,\n }),\n );\n return 0;\n}\n","import { encodeOpaqueKey } from \"../../opaque.js\";\nimport { parseBits, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, parseKeygenFormat } from \"../opaque-key.js\";\nimport type { RunOpts } from \"../types.js\";\n\nexport function runKeygen(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const { flags, values, positionals, errors } = splitFlags(args);\n const unsupported = unsupportedFlagForCommand(\n \"keygen\",\n flags,\n new Set([\"--bits\", \"--key-format\"]),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const extra = positionals[0];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const bits = parseBits(values);\n if (typeof bits === \"string\") {\n opts.stderr(bits + \"\\n\");\n return Promise.resolve(1);\n }\n const format = parseKeygenFormat(values);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n const bytes = new Uint8Array(bits / 8);\n crypto.getRandomValues(bytes);\n opts.stdout(encodeOpaqueKey(bytes, format) + \"\\n\");\n return Promise.resolve(0);\n}\n","import { runGenerate } from \"./cli/commands/generate.js\";\nimport { runInspect } from \"./cli/commands/inspect.js\";\nimport { runKeygen } from \"./cli/commands/keygen.js\";\nimport type { CommandHandler, RunOpts } from \"./cli/types.js\";\nimport { usage } from \"./cli/usage.js\";\n\nexport type { RunOpts } from \"./cli/types.js\";\n\ntype Command = {\n names: ReadonlyArray<string>;\n run: CommandHandler;\n};\n\nconst commands: ReadonlyArray<Command> = [\n { names: [\"generate\", \"g\"], run: runGenerate },\n { names: [\"inspect\", \"i\"], run: runInspect },\n { names: [\"keygen\", \"k\"], run: runKeygen },\n];\n\nexport async function run(opts: RunOpts): Promise<number> {\n const [subcommand, ...rest] = opts.argv;\n const command = commands.find((candidate) => candidate.names.includes(subcommand ?? \"\"));\n if (command !== undefined) return command.run(rest, opts);\n if (subcommand === undefined || subcommand === \"--help\" || subcommand === \"-h\") {\n opts.stdout(usage());\n return 0;\n }\n opts.stderr(usage());\n return 1;\n}\n","#!/usr/bin/env node\nimport { run } from \"../src/cli.js\";\n\nprocess.exitCode = await run({\n argv: process.argv.slice(2),\n stdout: (s) => process.stdout.write(s),\n stderr: (s) => process.stderr.write(s),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["parseKeyFormatFlag"],"sources":["../src/cli/codec-options.ts","../src/cli/constants.ts","../src/cli/flags.ts","../src/cli/opaque-key.ts","../src/cli/commands/generate.ts","../src/cli/format.ts","../src/cli/wrapping-key.ts","../src/cli/usage.ts","../src/cli/commands/inspect.ts","../src/cli/commands/keygen.ts","../src/cli.ts","../bin/cli.ts"],"sourcesContent":["import type { TimestampOptions } from \"../timestamp.js\";\nimport type { RunOpts } from \"./types.js\";\n\nexport function codecOpts(opts: RunOpts): Partial<TimestampOptions> {\n // CLI invocations are intentionally ephemeral: one codec per run, never\n // retained, so this is not the duplicate-brand warning case.\n const o: Partial<TimestampOptions> = { allowDuplicateBrand: true };\n if (opts.now !== undefined) o.now = opts.now;\n if (opts.rng !== undefined) o.rng = opts.rng;\n return o;\n}\n","export const maxGenerateCount = 10_000;\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport type ParsedFlags = {\n flags: Set<string>;\n values: Map<string, string>;\n positionals: string[];\n errors: string[];\n};\n\nfunction splitFlagToken(arg: string): { flag: string; inlineValue: string | undefined } {\n const eq = arg.indexOf(\"=\");\n if (eq <= 0) return { flag: arg, inlineValue: undefined };\n return { flag: arg.slice(0, eq), inlineValue: arg.slice(eq + 1) };\n}\n\nexport function splitFlags(args: ReadonlyArray<string>): ParsedFlags {\n const flags = new Set<string>();\n const values = new Map<string, string>();\n const positionals: string[] = [];\n const errors: string[] = [];\n const seenFlags = new Set<string>();\n const valueFlags = new Set([\"--count\", \"-c\", \"--bits\", \"--key-format\", \"--kind\"]);\n const addFlag = (flag: string) => {\n const canonical = canonicalFlag(flag);\n if (seenFlags.has(canonical)) errors.push(`duplicate flag: ${canonical}`);\n seenFlags.add(canonical);\n flags.add(flag);\n };\n for (let i = 0; i < args.length; i++) {\n const raw = args[i]!;\n const { flag, inlineValue } = splitFlagToken(raw);\n if (flag === \"--opaque\" || flag === \"--wrapped\" || flag === \"--reverse\") {\n addFlag(flag);\n if (inlineValue !== undefined) errors.push(`flag does not take a value: ${flag}`);\n continue;\n }\n if (valueFlags.has(flag)) {\n if (inlineValue !== undefined) {\n addFlag(flag);\n values.set(flag, inlineValue);\n continue;\n }\n const value = args[i + 1];\n if (value === undefined || value.startsWith(\"-\")) {\n addFlag(flag);\n values.set(flag, \"\");\n continue;\n }\n addFlag(flag);\n values.set(flag, value);\n i++;\n continue;\n }\n if (flag.startsWith(\"-\")) {\n addFlag(flag);\n continue;\n }\n positionals.push(raw);\n }\n return { flags, values, positionals, errors };\n}\n\nfunction canonicalFlag(flag: string): string {\n if (flag === \"-c\") return \"--count\";\n return flag;\n}\n\nconst knownFlags = new Set([\n \"--opaque\",\n \"--wrapped\",\n \"--reverse\",\n \"--kind\",\n \"--key-format\",\n \"--count\",\n \"-c\",\n \"--bits\",\n]);\n\nexport function unsupportedFlagForCommand(\n command: string,\n flags: Set<string>,\n allowed: Set<string>,\n): string | undefined {\n for (const flag of flags) {\n if (!allowed.has(flag)) {\n return knownFlags.has(flag)\n ? `unsupported flag for ${command}: ${flag}`\n : `unsupported flag: ${flag}`;\n }\n }\n return undefined;\n}\n\nexport function parseCount(values: Map<string, string>): number | string {\n const raw = values.get(\"--count\") ?? values.get(\"-c\");\n if (raw === undefined) return 1;\n if (raw === \"\") return \"--count requires a value\";\n if (!/^[1-9][0-9]*$/.test(raw)) return `--count must be a positive integer, got '${raw}'`;\n const count = Number.parseInt(raw, 10);\n if (!Number.isSafeInteger(count) || count > maxGenerateCount) {\n return `--count must be at most ${maxGenerateCount}, got '${raw}'`;\n }\n return count;\n}\n\nexport function parseBits(values: Map<string, string>): number | string {\n const raw = values.get(\"--bits\");\n if (raw === undefined) return 256;\n if (raw === \"\") return \"--bits requires a value\";\n if (raw === \"128\") return 128;\n if (raw === \"192\") return 192;\n if (raw === \"256\") return 256;\n return `--bits must be 128, 192, or 256, got '${raw}'`;\n}\n\nexport type WrappedKindValue = \"u32\" | \"i32\" | \"u64\" | \"i64\";\n\nexport function parseKind(values: Map<string, string>): WrappedKindValue | string | undefined {\n const raw = values.get(\"--kind\");\n if (raw === undefined) return undefined;\n if (raw === \"\") return \"--kind requires a value\";\n if (raw === \"u32\" || raw === \"i32\" || raw === \"u64\" || raw === \"i64\") return raw;\n return `--kind must be u32, i32, u64, or i64, got '${raw}'`;\n}\n\nexport function isKindError(result: WrappedKindValue | string): result is string {\n return result !== \"u32\" && result !== \"i32\" && result !== \"u64\" && result !== \"i64\";\n}\n","import {\n decodeOpaqueKey,\n importOpaqueKey,\n type OpaqueKey,\n type OpaqueKeyFormat,\n} from \"../opaque.js\";\nimport type { RunOpts } from \"./types.js\";\n\nexport function isKeyFormatError(result: OpaqueKeyFormat | string): result is string {\n return result !== \"hex\" && result !== \"base64url\";\n}\n\nfunction parseKeyFormatFlag(values: Map<string, string>): OpaqueKeyFormat | string | undefined {\n const fromFlag = values.get(\"--key-format\");\n if (fromFlag === undefined) return undefined;\n if (fromFlag === \"\") return \"--key-format requires a value\";\n if (fromFlag === \"hex\" || fromFlag === \"base64url\") return fromFlag;\n return `--key-format must be hex or base64url, got '${fromFlag}'`;\n}\n\nexport function parseKeygenFormat(values: Map<string, string>): OpaqueKeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag === undefined) return \"hex\";\n return fromFlag;\n}\n\nexport function parseOpaqueKeyFormat(\n values: Map<string, string>,\n opts: RunOpts,\n): OpaqueKeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag !== undefined) return fromFlag;\n const env = opts.env ?? process.env;\n const fromEnv = env.IDS_KEY_FORMAT;\n if (fromEnv === undefined || fromEnv === \"\") return \"hex\";\n if (fromEnv === \"hex\" || fromEnv === \"base64url\") return fromEnv;\n return `IDS_KEY_FORMAT must be hex or base64url, got '${fromEnv}'`;\n}\n\nexport async function loadOpaqueKey(\n opts: RunOpts,\n format: OpaqueKeyFormat,\n): Promise<OpaqueKey | string> {\n const env = opts.env ?? process.env;\n const raw = env.IDS_KEY;\n if (raw === undefined || raw === \"\") return \"missing IDS_KEY environment variable\";\n try {\n return importOpaqueKey(decodeOpaqueKey(raw, format));\n } catch (err) {\n return (err as Error).message;\n }\n}\n","import { createTimestampId } from \"../../timestamp.js\";\nimport { createOpaqueTimestampId, type OpaqueKeyFormat } from \"../../opaque.js\";\nimport { createReverseTimestampId } from \"../../reverse.js\";\nimport { codecOpts } from \"../codec-options.js\";\nimport { parseCount, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, loadOpaqueKey, parseOpaqueKeyFormat } from \"../opaque-key.js\";\nimport type { RunOpts } from \"../types.js\";\n\nexport function runGenerate(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const { flags, values, positionals, errors } = splitFlags(args);\n const unsupported = unsupportedFlagForCommand(\n \"generate\",\n flags,\n new Set([\"--count\", \"-c\", \"--opaque\", \"--reverse\", \"--key-format\"]),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const [brand] = positionals;\n const count = parseCount(values);\n if (typeof count === \"string\") {\n opts.stderr(count + \"\\n\");\n return Promise.resolve(1);\n }\n const opaque = flags.has(\"--opaque\");\n const reverse = flags.has(\"--reverse\");\n if (reverse && opaque) {\n opts.stderr(\"cannot use --reverse and --opaque together\\n\");\n return Promise.resolve(1);\n }\n if (!opaque && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque\\n\");\n return Promise.resolve(1);\n }\n if (opaque) {\n const format = parseOpaqueKeyFormat(values, opts);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n return runOpaqueGenerate(brand ?? \"\", count, format, opts);\n }\n if (reverse) {\n let codec;\n try {\n codec = createReverseTimestampId(brand ?? \"\", codecOpts(opts));\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return Promise.resolve(1);\n }\n for (let i = 0; i < count; i++) opts.stdout(codec.generate() + \"\\n\");\n return Promise.resolve(0);\n }\n let codec;\n try {\n codec = createTimestampId(brand ?? \"\", codecOpts(opts));\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return Promise.resolve(1);\n }\n for (let i = 0; i < count; i++) opts.stdout(codec.generate() + \"\\n\");\n return Promise.resolve(0);\n}\n\nasync function runOpaqueGenerate(\n brand: string,\n count: number,\n format: OpaqueKeyFormat,\n opts: RunOpts,\n): Promise<number> {\n const keyResult = await loadOpaqueKey(opts, format);\n if (typeof keyResult === \"string\") {\n opts.stderr(keyResult + \"\\n\");\n return 1;\n }\n let codec;\n try {\n codec = createOpaqueTimestampId(brand, { key: keyResult, ...codecOpts(opts) });\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return 1;\n }\n for (let i = 0; i < count; i++) opts.stdout((await codec.generate()) + \"\\n\");\n return 0;\n}\n","import type { Id } from \"../types.js\";\n\ntype InspectOutput = {\n brand: string;\n timestamp: Date;\n canonical: Id<string>;\n input: string;\n nowMs: number;\n};\n\ntype WrappedInspectOutput = {\n brand: string;\n lookupKey: number | bigint;\n canonical: Id<string>;\n input: string;\n};\n\nexport function formatWrappedInspectOutput(result: WrappedInspectOutput): string {\n const inputLine = describeInputForm(result.input, result.canonical);\n return [\n `brand: ${result.brand}`,\n `lookup-key: ${result.lookupKey.toString()}`,\n `canonical: ${result.canonical}`,\n `input: ${inputLine}`,\n \"\",\n ].join(\"\\n\");\n}\n\nexport function formatInspectOutput(result: InspectOutput): string {\n const relative = formatRelative(result.timestamp.getTime(), result.nowMs);\n const inputLine = describeInputForm(result.input, result.canonical);\n return [\n `brand: ${result.brand}`,\n `timestamp: ${result.timestamp.toISOString()} (${relative})`,\n `canonical: ${result.canonical}`,\n `input: ${inputLine}`,\n \"\",\n ].join(\"\\n\");\n}\n\nexport function describeInputForm(input: string, canonical: Id<string>): string {\n if (input === canonical) return \"canonical\";\n const notes: string[] = [];\n if (input !== input.toLowerCase()) notes.push(\"was uppercase\");\n if (/[ilo]/i.test(input.slice(4))) notes.push(\"used Crockford aliases\");\n return `not canonical (${notes.join(\" + \")})`;\n}\n\nconst msPerSecond = 1000;\nconst msPerMinute = 60 * msPerSecond;\nconst msPerHour = 60 * msPerMinute;\nconst msPerDay = 24 * msPerHour;\nconst daysPerMonth = 30.44;\nconst monthsPerYear = 12;\n\nexport function formatRelative(thenMs: number, nowMs: number): string {\n const diff = nowMs - thenMs;\n const abs = Math.abs(diff);\n const suffix = diff < 0 ? \"from now\" : \"ago\";\n\n const head = headUnits(abs);\n return head === \"\" ? \"just now\" : `${head} ${suffix}`;\n}\n\nfunction headUnits(abs: number): string {\n if (abs < msPerMinute) return \"\";\n if (abs < msPerHour) return unit(Math.round(abs / msPerMinute), \"minute\");\n if (abs < msPerDay) return unit(Math.round(abs / msPerHour), \"hour\");\n if (abs < msPerDay * daysPerMonth) return unit(Math.round(abs / msPerDay), \"day\");\n\n const totalMonths = Math.round(abs / (msPerDay * daysPerMonth));\n if (totalMonths < monthsPerYear) return unit(totalMonths, \"month\");\n\n const years = Math.floor(totalMonths / monthsPerYear);\n const months = totalMonths % monthsPerYear;\n return months === 0 ? unit(years, \"year\") : `${unit(years, \"year\")} ${unit(months, \"month\")}`;\n}\n\nfunction unit(n: number, name: string): string {\n return `${n} ${n === 1 ? name : `${name}s`}`;\n}\n","import {\n decodeWrappingKey,\n importWrappingKey,\n type WrappingKey,\n type WrappingKeyFormat,\n} from \"../wrapped.js\";\nimport type { RunOpts } from \"./types.js\";\n\nfunction parseKeyFormatFlag(values: Map<string, string>): WrappingKeyFormat | string | undefined {\n const fromFlag = values.get(\"--key-format\");\n if (fromFlag === undefined) return undefined;\n if (fromFlag === \"\") return \"--key-format requires a value\";\n if (fromFlag === \"hex\" || fromFlag === \"base64url\") return fromFlag;\n return `--key-format must be hex or base64url, got '${fromFlag}'`;\n}\n\nexport function parseWrappingKeyFormat(\n values: Map<string, string>,\n opts: RunOpts,\n): WrappingKeyFormat | string {\n const fromFlag = parseKeyFormatFlag(values);\n if (fromFlag !== undefined) return fromFlag;\n const env = opts.env ?? process.env;\n const fromEnv = env.IDS_WRAPPING_KEY_FORMAT;\n if (fromEnv === undefined || fromEnv === \"\") return \"hex\";\n if (fromEnv === \"hex\" || fromEnv === \"base64url\") return fromEnv;\n return `IDS_WRAPPING_KEY_FORMAT must be hex or base64url, got '${fromEnv}'`;\n}\n\nexport async function loadWrappingKey(\n opts: RunOpts,\n format: WrappingKeyFormat,\n): Promise<WrappingKey | string> {\n const env = opts.env ?? process.env;\n const raw = env.IDS_WRAPPING_KEY;\n if (raw === undefined || raw === \"\") return \"missing IDS_WRAPPING_KEY environment variable\";\n try {\n return importWrappingKey(decodeWrappingKey(raw, format));\n } catch (err) {\n return (err as Error).message;\n }\n}\n","import { maxGenerateCount } from \"./constants.js\";\n\nexport function usage(): string {\n return [\n \"Usage: ids <subcommand> [args]\",\n \"\",\n \"Subcommands:\",\n \" inspect, i <id> [--opaque] [--wrapped --kind u32|i32|u64|i64] [--reverse] [--key-format hex|base64url]\",\n \" Decode an ID and print brand, timestamp (or lookup key), and canonical form.\",\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" --wrapped reads the wrapping key from IDS_WRAPPING_KEY (hex by default; IDS_WRAPPING_KEY_FORMAT or --key-format).\",\n \" --kind is required with --wrapped: u32, i32, u64, or i64.\",\n \" --reverse decodes a Reverse Timestamp ID (newest-first sort order).\",\n \" generate, g <brand> [--count, -c N] [--opaque] [--reverse] [--key-format hex|base64url]\",\n ` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,\n \" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).\",\n \" --reverse mints Reverse Timestamp IDs (newest-first sort order).\",\n \" keygen, k [--wrapped] [--bits 128|192|256] [--key-format hex|base64url]\",\n \" Emit a random AES key for importOpaqueKey (stdout only).\",\n \" --wrapped emits a wrapping key for importWrappingKey instead (IDS_WRAPPING_KEY).\",\n \"\",\n ].join(\"\\n\");\n}\n","import { createTimestampId } from \"../../timestamp.js\";\nimport { createOpaqueTimestampId, type OpaqueKeyFormat } from \"../../opaque.js\";\nimport { createReverseTimestampId } from \"../../reverse.js\";\nimport { createWrappedKeyId, type WrappingKey } from \"../../wrapped.js\";\nimport { codecOpts } from \"../codec-options.js\";\nimport { formatInspectOutput, formatWrappedInspectOutput } from \"../format.js\";\nimport {\n isKindError,\n parseKind,\n splitFlags,\n unsupportedFlagForCommand,\n type WrappedKindValue,\n} from \"../flags.js\";\nimport { isKeyFormatError, loadOpaqueKey, parseOpaqueKeyFormat } from \"../opaque-key.js\";\nimport { loadWrappingKey, parseWrappingKeyFormat } from \"../wrapping-key.js\";\nimport type { RunOpts } from \"../types.js\";\nimport { usage } from \"../usage.js\";\n\nexport function runInspect(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const { flags, values, positionals, errors } = splitFlags(args);\n const unsupported = unsupportedFlagForCommand(\n \"inspect\",\n flags,\n new Set([\"--opaque\", \"--wrapped\", \"--reverse\", \"--kind\", \"--key-format\"]),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const [input] = positionals;\n if (input === undefined) {\n opts.stderr(usage());\n return Promise.resolve(1);\n }\n const extra = positionals[1];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const opaque = flags.has(\"--opaque\");\n const wrapped = flags.has(\"--wrapped\");\n const reverse = flags.has(\"--reverse\");\n if (opaque && wrapped) {\n opts.stderr(\"cannot use --wrapped and --opaque together\\n\");\n return Promise.resolve(1);\n }\n if (reverse && opaque) {\n opts.stderr(\"cannot use --reverse and --opaque together\\n\");\n return Promise.resolve(1);\n }\n if (reverse && wrapped) {\n opts.stderr(\"cannot use --reverse and --wrapped together\\n\");\n return Promise.resolve(1);\n }\n if (!opaque && !wrapped && flags.has(\"--key-format\")) {\n opts.stderr(\"--key-format requires --opaque or --wrapped\\n\");\n return Promise.resolve(1);\n }\n const brand = input.slice(0, 3).toLowerCase();\n if (wrapped) {\n const kind = parseKind(values);\n if (kind === undefined) {\n opts.stderr(\"--kind is required with --wrapped\\n\");\n return Promise.resolve(1);\n }\n if (isKindError(kind)) {\n opts.stderr(kind + \"\\n\");\n return Promise.resolve(1);\n }\n const format = parseWrappingKeyFormat(values, opts);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n return runWrappedInspect(brand, input, kind, format, opts);\n }\n if (opaque) {\n const format = parseOpaqueKeyFormat(values, opts);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n return runOpaqueInspect(brand, input, format, opts);\n }\n if (reverse) {\n let reverseCodec;\n try {\n reverseCodec = createReverseTimestampId(brand, codecOpts(opts));\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return Promise.resolve(1);\n }\n const reverseValidation = reverseCodec[\"~standard\"].validate(input);\n if (reverseValidation.issues) {\n opts.stderr(reverseValidation.issues[0]!.message + \"\\n\");\n return Promise.resolve(1);\n }\n const reverseCanonical = reverseValidation.value;\n const reverseTimestamp = reverseCodec.extractTimestamp(reverseCanonical);\n const reverseNowMs = (opts.now ?? Date.now)();\n opts.stdout(\n formatInspectOutput({\n brand,\n timestamp: reverseTimestamp,\n canonical: reverseCanonical,\n input,\n nowMs: reverseNowMs,\n }),\n );\n return Promise.resolve(0);\n }\n let codec;\n try {\n codec = createTimestampId(brand, codecOpts(opts));\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return Promise.resolve(1);\n }\n const validation = codec[\"~standard\"].validate(input);\n if (validation.issues) {\n opts.stderr(validation.issues[0]!.message + \"\\n\");\n return Promise.resolve(1);\n }\n const canonical = validation.value;\n const timestamp = codec.extractTimestamp(canonical);\n const nowMs = (opts.now ?? Date.now)();\n opts.stdout(\n formatInspectOutput({\n brand,\n timestamp,\n canonical,\n input,\n nowMs,\n }),\n );\n return Promise.resolve(0);\n}\n\nasync function runWrappedInspect(\n brand: string,\n input: string,\n kind: WrappedKindValue,\n format: string,\n opts: RunOpts,\n): Promise<number> {\n const keyResult = await loadWrappingKey(opts, format as \"hex\" | \"base64url\");\n if (typeof keyResult === \"string\") {\n opts.stderr(keyResult + \"\\n\");\n return 1;\n }\n let codec;\n try {\n codec = createWrappedKeyId(brand, {\n kind,\n keys: [keyResult as WrappingKey],\n allowDuplicateBrand: true,\n });\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return 1;\n }\n const validation = codec[\"~standard\"].validate(input);\n if (validation.issues) {\n opts.stderr(validation.issues[0]!.message + \"\\n\");\n return 1;\n }\n const canonical = validation.value;\n let lookupKey;\n try {\n lookupKey = await codec.unwrap(canonical);\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return 1;\n }\n opts.stdout(\n formatWrappedInspectOutput({\n brand,\n lookupKey,\n canonical,\n input,\n }),\n );\n return 0;\n}\n\nasync function runOpaqueInspect(\n brand: string,\n input: string,\n format: OpaqueKeyFormat,\n opts: RunOpts,\n): Promise<number> {\n const keyResult = await loadOpaqueKey(opts, format);\n if (typeof keyResult === \"string\") {\n opts.stderr(keyResult + \"\\n\");\n return 1;\n }\n let codec;\n try {\n codec = createOpaqueTimestampId(brand, { key: keyResult, ...codecOpts(opts) });\n } catch (err) {\n opts.stderr((err as Error).message + \"\\n\");\n return 1;\n }\n const validation = codec[\"~standard\"].validate(input);\n if (validation.issues) {\n opts.stderr(validation.issues[0]!.message + \"\\n\");\n return 1;\n }\n const canonical = validation.value;\n const timestamp = await codec.extractTimestamp(canonical);\n const nowMs = (opts.now ?? Date.now)();\n opts.stderr(\n \"note: timestamp assumes IDS_KEY matches the key used at generation; a wrong key yields a plausible but incorrect timestamp\\n\",\n );\n opts.stdout(\n formatInspectOutput({\n brand,\n timestamp,\n canonical,\n input,\n nowMs,\n }),\n );\n return 0;\n}\n","import { encodeOpaqueKey } from \"../../opaque.js\";\nimport { encodeWrappingKey } from \"../../wrapped.js\";\nimport { parseBits, splitFlags, unsupportedFlagForCommand } from \"../flags.js\";\nimport { isKeyFormatError, parseKeygenFormat } from \"../opaque-key.js\";\nimport type { RunOpts } from \"../types.js\";\n\nexport function runKeygen(args: ReadonlyArray<string>, opts: RunOpts): Promise<number> {\n const { flags, values, positionals, errors } = splitFlags(args);\n const unsupported = unsupportedFlagForCommand(\n \"keygen\",\n flags,\n new Set([\"--wrapped\", \"--bits\", \"--key-format\"]),\n );\n if (unsupported !== undefined) {\n opts.stderr(unsupported + \"\\n\");\n return Promise.resolve(1);\n }\n if (errors[0] !== undefined) {\n opts.stderr(errors[0] + \"\\n\");\n return Promise.resolve(1);\n }\n const extra = positionals[0];\n if (extra !== undefined) {\n opts.stderr(`unexpected argument: ${extra}\\n`);\n return Promise.resolve(1);\n }\n const bits = parseBits(values);\n if (typeof bits === \"string\") {\n opts.stderr(bits + \"\\n\");\n return Promise.resolve(1);\n }\n const format = parseKeygenFormat(values);\n if (isKeyFormatError(format)) {\n opts.stderr(format + \"\\n\");\n return Promise.resolve(1);\n }\n const bytes = new Uint8Array(bits / 8);\n crypto.getRandomValues(bytes);\n const wrapped = flags.has(\"--wrapped\");\n opts.stdout((wrapped ? encodeWrappingKey(bytes, format) : encodeOpaqueKey(bytes, format)) + \"\\n\");\n return Promise.resolve(0);\n}\n","import { runGenerate } from \"./cli/commands/generate.js\";\nimport { runInspect } from \"./cli/commands/inspect.js\";\nimport { runKeygen } from \"./cli/commands/keygen.js\";\nimport type { CommandHandler, RunOpts } from \"./cli/types.js\";\nimport { usage } from \"./cli/usage.js\";\n\nexport type { RunOpts } from \"./cli/types.js\";\n\ntype Command = {\n names: ReadonlyArray<string>;\n run: CommandHandler;\n};\n\nconst commands: ReadonlyArray<Command> = [\n { names: [\"generate\", \"g\"], run: runGenerate },\n { names: [\"inspect\", \"i\"], run: runInspect },\n { names: [\"keygen\", \"k\"], run: runKeygen },\n];\n\nexport async function run(opts: RunOpts): Promise<number> {\n const [subcommand, ...rest] = opts.argv;\n const command = commands.find((candidate) => candidate.names.includes(subcommand ?? \"\"));\n if (command !== undefined) return command.run(rest, opts);\n if (subcommand === undefined || subcommand === \"--help\" || subcommand === \"-h\") {\n opts.stdout(usage());\n return 0;\n }\n opts.stderr(usage());\n return 1;\n}\n","#!/usr/bin/env node\nimport { run } from \"../src/cli.js\";\n\nprocess.exitCode = await run({\n argv: process.argv.slice(2),\n stdout: (s) => process.stdout.write(s),\n stderr: (s) => process.stderr.write(s),\n});\n"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { i as ParseResult, t as Id } from "./types-g7CiQDyE.mjs";
2
+ import { ConvertCustomConfig, PgCustomColumnBuilder } from "drizzle-orm/pg-core";
3
+
4
+ //#region src/drizzle.d.ts
5
+ /**
6
+ * Minimum codec interface required by the Drizzle adapter.
7
+ *
8
+ * Any codec variant satisfies this type — TimestampCodec, OpaqueTimestampCodec,
9
+ * and WrappedKeyCodec all expose `safeParse`. The adapter never calls
10
+ * `extractTimestamp`, `wrap`/`unwrap`, or any key-dependent method.
11
+ *
12
+ * Kysely and Prisma adapter issues should use this same codec contract shape.
13
+ */
14
+ type IdColumnCodec<Brand extends string> = {
15
+ safeParse(value: unknown): ParseResult<Brand>;
16
+ };
17
+ /**
18
+ * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value.
19
+ *
20
+ * **Write path:** passes the `Id<Brand>` directly to the driver — it is already
21
+ * the canonical string form.
22
+ *
23
+ * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict
24
+ * `is()`. Data at rest should already be canonical per ADR-0003, but `safeParse`
25
+ * is a safe boundary in case stale non-canonical values exist. Throws if the
26
+ * value from the database does not parse as a valid `Id<Brand>`.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { idColumn } from "@smonn/ids/drizzle";
31
+ * import { createTimestampId } from "@smonn/ids";
32
+ *
33
+ * const usr = createTimestampId("usr");
34
+ * export const users = pgTable("users", { id: idColumn(usr).primaryKey() });
35
+ * // users.id is Id<"usr"> end-to-end
36
+ * ```
37
+ */
38
+ declare function idColumn<Brand extends string>(codec: IdColumnCodec<Brand>): PgCustomColumnBuilder<ConvertCustomConfig<"", {
39
+ data: Id<Brand>;
40
+ driverData: string;
41
+ }>>;
42
+ //#endregion
43
+ export { idColumn as n, IdColumnCodec as t };
44
+ //# sourceMappingURL=drizzle-CeSni5PB.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle-CeSni5PB.d.mts","names":[],"sources":["../src/drizzle.ts"],"mappings":";;;;;;AAgBA;;;;;;;KAAY,aAAA;EACV,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;AAAA;AAwBzC;;;;;;;;;;;;;;;;;;;;AAxByC,iBAwBzB,QAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,qBAAA,CAAsB,mBAAA;EAA0B,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA"}
@@ -0,0 +1,2 @@
1
+ import { n as idColumn, t as IdColumnCodec } from "./drizzle-CeSni5PB.mjs";
2
+ export { IdColumnCodec, idColumn };
@@ -0,0 +1,42 @@
1
+ import { customType } from "drizzle-orm/pg-core";
2
+ //#region src/drizzle.ts
3
+ /**
4
+ * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value.
5
+ *
6
+ * **Write path:** passes the `Id<Brand>` directly to the driver — it is already
7
+ * the canonical string form.
8
+ *
9
+ * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict
10
+ * `is()`. Data at rest should already be canonical per ADR-0003, but `safeParse`
11
+ * is a safe boundary in case stale non-canonical values exist. Throws if the
12
+ * value from the database does not parse as a valid `Id<Brand>`.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { idColumn } from "@smonn/ids/drizzle";
17
+ * import { createTimestampId } from "@smonn/ids";
18
+ *
19
+ * const usr = createTimestampId("usr");
20
+ * export const users = pgTable("users", { id: idColumn(usr).primaryKey() });
21
+ * // users.id is Id<"usr"> end-to-end
22
+ * ```
23
+ */
24
+ function idColumn(codec) {
25
+ return customType({
26
+ dataType() {
27
+ return "text";
28
+ },
29
+ toDriver(value) {
30
+ return value;
31
+ },
32
+ fromDriver(value) {
33
+ const result = codec.safeParse(value);
34
+ if (!result.ok) throw new Error(`[ids] invalid ID from database: ${result.error}`);
35
+ return result.id;
36
+ }
37
+ })();
38
+ }
39
+ //#endregion
40
+ export { idColumn };
41
+
42
+ //# sourceMappingURL=drizzle.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle.mjs","names":[],"sources":["../src/drizzle.ts"],"sourcesContent":["import {\n customType,\n type ConvertCustomConfig,\n type PgCustomColumnBuilder,\n} from \"drizzle-orm/pg-core\";\nimport type { Id, ParseResult } from \"./types.js\";\n\n/**\n * Minimum codec interface required by the Drizzle adapter.\n *\n * Any codec variant satisfies this type — TimestampCodec, OpaqueTimestampCodec,\n * and WrappedKeyCodec all expose `safeParse`. The adapter never calls\n * `extractTimestamp`, `wrap`/`unwrap`, or any key-dependent method.\n *\n * Kysely and Prisma adapter issues should use this same codec contract shape.\n */\nexport type IdColumnCodec<Brand extends string> = {\n safeParse(value: unknown): ParseResult<Brand>;\n};\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Data at rest should already be canonical per ADR-0003, but `safeParse`\n * is a safe boundary in case stale non-canonical values exist. Throws if the\n * value from the database does not parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = pgTable(\"users\", { id: idColumn(usr).primaryKey() });\n * // users.id is Id<\"usr\"> end-to-end\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): PgCustomColumnBuilder<ConvertCustomConfig<\"\", { data: Id<Brand>; driverData: string }>> {\n return customType<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n const result = codec.safeParse(value);\n if (!result.ok) {\n throw new Error(`[ids] invalid ID from database: ${result.error}`);\n }\n return result.id;\n },\n })();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,SACd,OACyF;CACzF,OAAO,WAAoD;EACzD,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,MAAM,SAAS,MAAM,UAAU,KAAK;GACpC,IAAI,CAAC,OAAO,IACV,MAAM,IAAI,MAAM,mCAAmC,OAAO,OAAO;GAEnE,OAAO,OAAO;EAChB;CACF,CAAC,CAAC,CAAC;AACL"}