@smonn/ids 0.15.0 → 1.0.0-rc.1

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 (46) hide show
  1. package/README.md +3 -3
  2. package/dist/{adapter-types-7wWdELSh.mjs → adapter-types-CjzFNDcJ.mjs} +7 -2
  3. package/dist/adapter-types-CjzFNDcJ.mjs.map +1 -0
  4. package/dist/cli.mjs +30 -22
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/drizzle.d.mts +202 -3
  7. package/dist/drizzle.d.mts.map +1 -1
  8. package/dist/drizzle.mjs +245 -5
  9. package/dist/drizzle.mjs.map +1 -1
  10. package/dist/express.d.mts +44 -2
  11. package/dist/express.d.mts.map +1 -1
  12. package/dist/express.mjs +60 -2
  13. package/dist/express.mjs.map +1 -1
  14. package/dist/fastify.d.mts +49 -2
  15. package/dist/fastify.d.mts.map +1 -1
  16. package/dist/fastify.mjs +61 -2
  17. package/dist/fastify.mjs.map +1 -1
  18. package/dist/hono.d.mts +44 -2
  19. package/dist/hono.d.mts.map +1 -1
  20. package/dist/hono.mjs +54 -2
  21. package/dist/hono.mjs.map +1 -1
  22. package/dist/kysely.d.mts +103 -2
  23. package/dist/kysely.d.mts.map +1 -1
  24. package/dist/kysely.mjs +105 -2
  25. package/dist/kysely.mjs.map +1 -1
  26. package/dist/mikro-orm.d.mts +81 -3
  27. package/dist/mikro-orm.d.mts.map +1 -1
  28. package/dist/mikro-orm.mjs +86 -4
  29. package/dist/mikro-orm.mjs.map +1 -1
  30. package/dist/nestjs.mjs +1 -1
  31. package/dist/{opaque-COAcIIY4.mjs → opaque-Dle3CmSE.mjs} +18 -10
  32. package/dist/opaque-Dle3CmSE.mjs.map +1 -0
  33. package/dist/opaque.d.mts +16 -10
  34. package/dist/opaque.d.mts.map +1 -1
  35. package/dist/opaque.mjs +1 -1
  36. package/dist/prisma.d.mts +135 -3
  37. package/dist/prisma.d.mts.map +1 -1
  38. package/dist/prisma.mjs +141 -3
  39. package/dist/prisma.mjs.map +1 -1
  40. package/dist/typeorm.d.mts +84 -1
  41. package/dist/typeorm.d.mts.map +1 -1
  42. package/dist/typeorm.mjs +87 -2
  43. package/dist/typeorm.mjs.map +1 -1
  44. package/package.json +1 -1
  45. package/dist/adapter-types-7wWdELSh.mjs.map +0 -1
  46. package/dist/opaque-COAcIIY4.mjs.map +0 -1
package/dist/kysely.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { t as readIdColumn } from "./adapter-types-7wWdELSh.mjs";
2
+ import { n as readIdColumnNullable, t as readIdColumn } from "./adapter-types-CjzFNDcJ.mjs";
3
3
  //#region src/adapters/kysely.ts
4
4
  /**
5
5
  * Kysely column adapter bound to a codec.
@@ -36,7 +36,110 @@ function idColumn(codec) {
36
36
  }
37
37
  };
38
38
  }
39
+ /**
40
+ * Generates a fresh `Id<Brand>` for use at a Kysely insert call site.
41
+ *
42
+ * Requires a codec variant that exposes a synchronous `generate()` — see
43
+ * {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse
44
+ * Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs
45
+ * are rejected at compile time.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { insertId } from "@smonn/ids/kysely";
50
+ * import { createTimestampId } from "@smonn/ids";
51
+ *
52
+ * const usr = createTimestampId("usr");
53
+ *
54
+ * await db.insertInto("users").values({ id: insertId(usr), name: "Alice" }).execute();
55
+ * ```
56
+ */
57
+ function insertId(codec) {
58
+ return codec.generate();
59
+ }
60
+ /**
61
+ * Kysely plugin that automatically transforms result columns using the provided codec map.
62
+ *
63
+ * Keys in `map` are plain column names (`"id"`) or `"table.column"` qualified names
64
+ * (`"users.id"`). Plain names match any result column with that name; qualified names
65
+ * match by the column-name part (after the last `.`). If both a plain key and a qualified
66
+ * key resolve to the same column name, the qualified key takes precedence.
67
+ *
68
+ * `transformResult` calls `readIdColumn(codec, rawValue)` for each matched column, returning
69
+ * a branded `Id<Brand>` on success and throwing `IdsError("invalid_id")` on parse failure.
70
+ * `transformQuery` is a no-op identity pass-through.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * import { idPlugin } from "@smonn/ids/kysely";
75
+ * import { createTimestampId } from "@smonn/ids";
76
+ * import { Kysely } from "kysely";
77
+ *
78
+ * const usr = createTimestampId("usr");
79
+ *
80
+ * const db = new Kysely<Database>({
81
+ * // ...
82
+ * plugins: [idPlugin({ "users.id": usr })],
83
+ * });
84
+ *
85
+ * // result.id is automatically validated and branded as Id<"usr">
86
+ * const row = await db.selectFrom("users").selectAll().executeTakeFirstOrThrow();
87
+ * ```
88
+ */
89
+ function idPlugin(map) {
90
+ const lookup = /* @__PURE__ */ new Map();
91
+ for (const [key, codec] of Object.entries(map)) if (!key.includes(".")) lookup.set(key, codec);
92
+ for (const [key, codec] of Object.entries(map)) if (key.includes(".")) lookup.set(key.slice(key.lastIndexOf(".") + 1), codec);
93
+ return {
94
+ transformQuery(args) {
95
+ return args.node;
96
+ },
97
+ async transformResult(args) {
98
+ const rows = args.result.rows.map((row) => {
99
+ const newRow = {};
100
+ for (const [colName, value] of Object.entries(row)) {
101
+ const codec = lookup.get(colName);
102
+ newRow[colName] = codec !== void 0 ? readIdColumn(codec, value) : value;
103
+ }
104
+ return newRow;
105
+ });
106
+ return {
107
+ ...args.result,
108
+ rows
109
+ };
110
+ }
111
+ };
112
+ }
113
+ /**
114
+ * Kysely column adapter for a **nullable** `Id<Brand>` column.
115
+ *
116
+ * Behaves like {@link idColumn} but `fromDriver` returns `null` for `null` /
117
+ * `undefined` driver values and `toDriver` passes `null` through unchanged.
118
+ * Use for optional foreign keys and `LEFT JOIN` results.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import { nullableIdColumn } from "@smonn/ids/kysely";
123
+ * import { createTimestampId } from "@smonn/ids";
124
+ *
125
+ * const usr = createTimestampId("usr");
126
+ * const authorCol = nullableIdColumn(usr);
127
+ *
128
+ * // In a query result handler:
129
+ * const authorId = authorCol.fromDriver(row.author_id); // Id<"usr"> | null
130
+ * ```
131
+ */
132
+ function nullableIdColumn(codec) {
133
+ return {
134
+ toDriver(value) {
135
+ return value;
136
+ },
137
+ fromDriver(value) {
138
+ return readIdColumnNullable(codec, value);
139
+ }
140
+ };
141
+ }
39
142
  //#endregion
40
- export { IdsError, idColumn, isIdsError };
143
+ export { IdsError, idColumn, idPlugin, insertId, isIdsError, nullableIdColumn };
41
144
 
42
145
  //# sourceMappingURL=kysely.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"kysely.mjs","names":[],"sources":["../src/adapters/kysely.ts"],"sourcesContent":["import type { ColumnType } from \"kysely\";\nimport { readIdColumn, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\nexport type { IdColumnCodec } from \"./adapter-types.js\";\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\n/**\n * Kysely column type mapping for `Id<Brand>`.\n *\n * Use this in your Kysely `Database` interface to type a column as `Id<Brand>` at\n * the TypeScript level. Pair it with `idColumn(codec)` for runtime read/write\n * transformation.\n *\n * @example\n * ```ts\n * import type { IdColumnType } from \"@smonn/ids/kysely\";\n * import type { Id } from \"@smonn/ids\";\n *\n * interface Database {\n * users: { id: IdColumnType<\"usr\"> };\n * }\n * ```\n */\nexport type IdColumnType<Brand extends string> = ColumnType<Id<Brand>, Id<Brand>, Id<Brand>>;\n\n/**\n * Kysely column adapter bound to a codec.\n *\n * Returns an object with `fromDriver` / `toDriver` helpers that mirror the read/write\n * contract of the Drizzle adapter — same error message, same strictness (safeParse on\n * read, identity on write).\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()`. Throws if\n * the value does not parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const usrCol = idColumn(usr);\n *\n * // In a query result handler:\n * const id = usrCol.fromDriver(row.id);\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): {\n toDriver(value: Id<Brand>): string;\n fromDriver(value: string): Id<Brand>;\n} {\n return {\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAgB,SACd,OAIA;CACA,OAAO;EACL,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF;AACF"}
1
+ {"version":3,"file":"kysely.mjs","names":[],"sources":["../src/adapters/kysely.ts"],"sourcesContent":["import type {\n ColumnType,\n KyselyPlugin,\n PluginTransformQueryArgs,\n PluginTransformResultArgs,\n QueryResult,\n UnknownRow,\n} from \"kysely\";\nimport { readIdColumn, readIdColumnNullable, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\nexport type { IdColumnCodec } from \"./adapter-types.js\";\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\n/**\n * Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.\n * Required by {@link insertId} so that the insert-time call site can produce a\n * fresh `Id<Brand>` with a compile-time constraint enforcing codec capability.\n * Only the **Timestamp codec** and **Reverse Timestamp codec** satisfy this;\n * async-generate codecs (Opaque, Signed, Wrapped, Digest) do not and are\n * therefore rejected at the TypeScript level.\n */\nexport type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {\n generate(): Id<Brand>;\n};\n\n/**\n * Kysely column type mapping for `Id<Brand>`.\n *\n * Use this in your Kysely `Database` interface to type a column as `Id<Brand>` at\n * the TypeScript level. Pair it with `idColumn(codec)` for runtime read/write\n * transformation.\n *\n * @example\n * ```ts\n * import type { IdColumnType } from \"@smonn/ids/kysely\";\n * import type { Id } from \"@smonn/ids\";\n *\n * interface Database {\n * users: { id: IdColumnType<\"usr\"> };\n * }\n * ```\n */\nexport type IdColumnType<Brand extends string> = ColumnType<Id<Brand>, Id<Brand>, Id<Brand>>;\n\n/**\n * Kysely column type mapping for a nullable `Id<Brand>` column.\n *\n * Use in your Kysely `Database` interface for optional foreign keys or any\n * column that can be `NULL`. Pair with `nullableIdColumn(codec)` for runtime\n * transformation.\n *\n * @example\n * ```ts\n * import type { NullableIdColumnType } from \"@smonn/ids/kysely\";\n * import type { Id } from \"@smonn/ids\";\n *\n * interface Database {\n * posts: { authorId: NullableIdColumnType<\"usr\"> };\n * }\n * ```\n */\nexport type NullableIdColumnType<Brand extends string> = ColumnType<\n Id<Brand> | null,\n Id<Brand> | null,\n Id<Brand> | null\n>;\n\n/**\n * Kysely column adapter bound to a codec.\n *\n * Returns an object with `fromDriver` / `toDriver` helpers that mirror the read/write\n * contract of the Drizzle adapter — same error message, same strictness (safeParse on\n * read, identity on write).\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()`. Throws if\n * the value does not parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const usrCol = idColumn(usr);\n *\n * // In a query result handler:\n * const id = usrCol.fromDriver(row.id);\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): {\n toDriver(value: Id<Brand>): string;\n fromDriver(value: string): Id<Brand>;\n} {\n return {\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n };\n}\n\n/**\n * Generates a fresh `Id<Brand>` for use at a Kysely insert call site.\n *\n * Requires a codec variant that exposes a synchronous `generate()` — see\n * {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse\n * Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs\n * are rejected at compile time.\n *\n * @example\n * ```ts\n * import { insertId } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * await db.insertInto(\"users\").values({ id: insertId(usr), name: \"Alice\" }).execute();\n * ```\n */\nexport function insertId<Brand extends string>(codec: IdGeneratingCodec<Brand>): Id<Brand> {\n return codec.generate();\n}\n\n/**\n * Kysely plugin that automatically transforms result columns using the provided codec map.\n *\n * Keys in `map` are plain column names (`\"id\"`) or `\"table.column\"` qualified names\n * (`\"users.id\"`). Plain names match any result column with that name; qualified names\n * match by the column-name part (after the last `.`). If both a plain key and a qualified\n * key resolve to the same column name, the qualified key takes precedence.\n *\n * `transformResult` calls `readIdColumn(codec, rawValue)` for each matched column, returning\n * a branded `Id<Brand>` on success and throwing `IdsError(\"invalid_id\")` on parse failure.\n * `transformQuery` is a no-op identity pass-through.\n *\n * @example\n * ```ts\n * import { idPlugin } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import { Kysely } from \"kysely\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * const db = new Kysely<Database>({\n * // ...\n * plugins: [idPlugin({ \"users.id\": usr })],\n * });\n *\n * // result.id is automatically validated and branded as Id<\"usr\">\n * const row = await db.selectFrom(\"users\").selectAll().executeTakeFirstOrThrow();\n * ```\n */\nexport function idPlugin(map: Record<string, IdColumnCodec<string>>): KyselyPlugin {\n // Build a lookup keyed by the bare column name.\n // Plain keys (\"id\") are added first; qualified keys (\"users.id\") are added second\n // so they override the plain key when both resolve to the same column name.\n const lookup = new Map<string, IdColumnCodec<string>>();\n for (const [key, codec] of Object.entries(map)) {\n if (!key.includes(\".\")) {\n lookup.set(key, codec);\n }\n }\n for (const [key, codec] of Object.entries(map)) {\n if (key.includes(\".\")) {\n lookup.set(key.slice(key.lastIndexOf(\".\") + 1), codec);\n }\n }\n\n return {\n transformQuery(args: PluginTransformQueryArgs) {\n return args.node;\n },\n async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {\n const rows = args.result.rows.map((row) => {\n const newRow: Record<string, unknown> = {};\n for (const [colName, value] of Object.entries(row)) {\n const codec = lookup.get(colName);\n newRow[colName] = codec !== undefined ? readIdColumn(codec, value) : value;\n }\n return newRow;\n });\n return { ...args.result, rows };\n },\n };\n}\n\n/**\n * Kysely column adapter for a **nullable** `Id<Brand>` column.\n *\n * Behaves like {@link idColumn} but `fromDriver` returns `null` for `null` /\n * `undefined` driver values and `toDriver` passes `null` through unchanged.\n * Use for optional foreign keys and `LEFT JOIN` results.\n *\n * @example\n * ```ts\n * import { nullableIdColumn } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const authorCol = nullableIdColumn(usr);\n *\n * // In a query result handler:\n * const authorId = authorCol.fromDriver(row.author_id); // Id<\"usr\"> | null\n * ```\n */\nexport function nullableIdColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): {\n toDriver(value: Id<Brand> | null): string | null;\n fromDriver(value: string | null): Id<Brand> | null;\n} {\n return {\n toDriver(value: Id<Brand> | null): string | null {\n return value;\n },\n fromDriver(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FA,SAAgB,SACd,OAIA;CACA,OAAO;EACL,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,SAA+B,OAA4C;CACzF,OAAO,MAAM,SAAS;AACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,SAAS,KAA0D;CAIjF,MAAM,yBAAS,IAAI,IAAmC;CACtD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,CAAC,IAAI,SAAS,GAAG,GACnB,OAAO,IAAI,KAAK,KAAK;CAGzB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,IAAI,SAAS,GAAG,GAClB,OAAO,IAAI,IAAI,MAAM,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,KAAK;CAIzD,OAAO;EACL,eAAe,MAAgC;GAC7C,OAAO,KAAK;EACd;EACA,MAAM,gBAAgB,MAAmE;GACvF,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,QAAQ;IACzC,MAAM,SAAkC,CAAC;IACzC,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,GAAG,GAAG;KAClD,MAAM,QAAQ,OAAO,IAAI,OAAO;KAChC,OAAO,WAAW,UAAU,KAAA,IAAY,aAAa,OAAO,KAAK,IAAI;IACvE;IACA,OAAO;GACT,CAAC;GACD,OAAO;IAAE,GAAG,KAAK;IAAQ;GAAK;EAChC;CACF;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,iBACd,OAIA;CACA,OAAO;EACL,SAAS,OAAwC;GAC/C,OAAO;EACT;EACA,WAAW,OAAwC;GACjD,OAAO,qBAAqB,OAAO,KAAK;EAC1C;CACF;AACF"}
@@ -5,6 +5,45 @@ import { Type } from "@mikro-orm/core";
5
5
 
6
6
  //#region src/adapters/mikro-orm.d.ts
7
7
  /**
8
+ * Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.
9
+ * Required by {@link idField} so that the MikroORM `onCreate` hook can produce
10
+ * IDs at persist time. Only the **Timestamp codec** and **Reverse Timestamp codec**
11
+ * satisfy this; async-generate codecs (Opaque, Signed, Wrapped, Digest) do not.
12
+ */
13
+ type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {
14
+ generate(): Id<Brand>;
15
+ };
16
+ /**
17
+ * Returns a MikroORM property option object that wires `codec.generate()` into the
18
+ * `onCreate` lifecycle hook, so the field auto-fills on first persist.
19
+ *
20
+ * Requires a codec variant that exposes a synchronous `generate()` —
21
+ * see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse
22
+ * Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot
23
+ * be passed here.
24
+ *
25
+ * Pass the result as options to `@PrimaryKey()` (the typical case for auto-generated primary keys) or any other MikroORM property decorator:
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { PrimaryKey } from "@mikro-orm/core";
30
+ * import { idField } from "@smonn/ids/mikro-orm";
31
+ * import { createTimestampId } from "@smonn/ids";
32
+ * import type { Id } from "@smonn/ids";
33
+ *
34
+ * const usr = createTimestampId("usr");
35
+ *
36
+ * class User {
37
+ * @PrimaryKey(idField(usr))
38
+ * id!: Id<"usr">;
39
+ * }
40
+ * ```
41
+ */
42
+ declare function idField<Brand extends string>(codec: IdGeneratingCodec<Brand>): {
43
+ type: new () => Type<Id<Brand>, string>;
44
+ onCreate: () => Id<Brand>;
45
+ };
46
+ /**
8
47
  * Factory that returns a MikroORM `Type` subclass bound to a codec.
9
48
  *
10
49
  * **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through
@@ -14,7 +53,14 @@ import { Type } from "@mikro-orm/core";
14
53
  * `codec.safeParse()`. Throws `IdsError("invalid_id")` if the stored value
15
54
  * does not parse as a valid `Id<Brand>`.
16
55
  *
17
- * **Column type** (`getColumnType`): returns `"text"`.
56
+ * **Column type** (`getColumnType`): returns `"text"` by default, or the
57
+ * `options.columnType` override when provided.
58
+ *
59
+ * @param codec - The brand-scoped codec used to parse values read from the database.
60
+ * @param options - Optional column configuration.
61
+ * @param options.columnType - SQL column type to use (default: `"text"`). Pass
62
+ * `"varchar(30)"` or `"char(26)"` to match an existing DDL or index strategy.
63
+ * The value is passed through verbatim — no validation is performed.
18
64
  *
19
65
  * @example
20
66
  * ```ts
@@ -25,13 +71,45 @@ import { Type } from "@mikro-orm/core";
25
71
  *
26
72
  * const usr = createTimestampId("usr");
27
73
  *
74
+ * // default: text column
28
75
  * class User {
29
76
  * @PrimaryKey({ type: idType(usr) })
30
77
  * id!: Id<"usr">;
31
78
  * }
79
+ *
80
+ * // explicit varchar column
81
+ * class Org {
82
+ * @PrimaryKey({ type: idType(usr, { columnType: "varchar(30)" }) })
83
+ * id!: Id<"usr">;
84
+ * }
85
+ * ```
86
+ */
87
+ declare function idType<Brand extends string>(codec: IdColumnCodec<Brand>, options?: {
88
+ columnType?: string;
89
+ }): new () => Type<Id<Brand>, string>;
90
+ /**
91
+ * Factory that returns a MikroORM `Type` subclass for a **nullable** `Id<Brand>` column.
92
+ *
93
+ * Behaves like {@link idType} but `convertToJSValue` returns `null` for `null` /
94
+ * `undefined` database values and `convertToDatabaseValue` passes `null` through
95
+ * unchanged. Use for optional foreign keys.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * import { Property } from "@mikro-orm/core";
100
+ * import { nullableIdType } from "@smonn/ids/mikro-orm";
101
+ * import { createTimestampId } from "@smonn/ids";
102
+ * import type { Id } from "@smonn/ids";
103
+ *
104
+ * const usr = createTimestampId("usr");
105
+ *
106
+ * class Post {
107
+ * @Property({ type: nullableIdType(usr), nullable: true })
108
+ * authorId!: Id<"usr"> | null;
109
+ * }
32
110
  * ```
33
111
  */
34
- declare function idType<Brand extends string>(codec: IdColumnCodec<Brand>): new () => Type<Id<Brand>, string>;
112
+ declare function nullableIdType<Brand extends string>(codec: IdColumnCodec<Brand>): new () => Type<Id<Brand> | null, string | null>;
35
113
  //#endregion
36
- export { type IdColumnCodec, IdsError, type IdsErrorCode, idType, isIdsError };
114
+ export { type IdColumnCodec, IdGeneratingCodec, IdsError, type IdsErrorCode, idField, idType, isIdsError, nullableIdType };
37
115
  //# sourceMappingURL=mikro-orm.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mikro-orm.d.mts","names":[],"sources":["../src/adapters/mikro-orm.ts"],"mappings":";;;;;;AAoCA;;;;;;;;;;;;;;;;;;AAEqB;;;;;;;;;AAFrB,iBAAgB,MAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,cACV,IAAA,CAAK,EAAA,CAAG,KAAA"}
1
+ {"version":3,"file":"mikro-orm.d.mts","names":[],"sources":["../src/adapters/mikro-orm.ts"],"mappings":";;;;;;AAeA;;;;;;AAAA,KAAY,iBAAA,yBAA0C,aAAA,CAAc,KAAA;EAClE,QAAA,IAAY,EAAA,CAAG,KAAA;AAAA;;;;;;;;AAAA;AA6BjB;;;;;;;;;;;;;;;;;;iBAAgB,OAAA,uBACd,KAAA,EAAO,iBAAA,CAAkB,KAAA;EAEzB,IAAA,YAAgB,IAAA,CAAK,EAAA,CAAG,KAAA;EACxB,QAAA,QAAgB,EAAA,CAAG,KAAA;AAAA;;;AAAA;AAiDrB;;;;;;;;;;;;;;;;;;;;AAGqB;AAqCrB;;;;;;;;;;;;;;;;;iBAxCgB,MAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,GACrB,OAAA;EAAY,UAAA;AAAA,cACD,IAAA,CAAK,EAAA,CAAG,KAAA;;;;;;;;;;;;;;;;;;;;;;;iBAqCL,cAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,cACV,IAAA,CAAK,EAAA,CAAG,KAAA"}
@@ -1,8 +1,40 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { t as readIdColumn } from "./adapter-types-7wWdELSh.mjs";
2
+ import { n as readIdColumnNullable, t as readIdColumn } from "./adapter-types-CjzFNDcJ.mjs";
3
3
  import { Type } from "@mikro-orm/core";
4
4
  //#region src/adapters/mikro-orm.ts
5
5
  /**
6
+ * Returns a MikroORM property option object that wires `codec.generate()` into the
7
+ * `onCreate` lifecycle hook, so the field auto-fills on first persist.
8
+ *
9
+ * Requires a codec variant that exposes a synchronous `generate()` —
10
+ * see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse
11
+ * Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot
12
+ * be passed here.
13
+ *
14
+ * Pass the result as options to `@PrimaryKey()` (the typical case for auto-generated primary keys) or any other MikroORM property decorator:
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { PrimaryKey } from "@mikro-orm/core";
19
+ * import { idField } from "@smonn/ids/mikro-orm";
20
+ * import { createTimestampId } from "@smonn/ids";
21
+ * import type { Id } from "@smonn/ids";
22
+ *
23
+ * const usr = createTimestampId("usr");
24
+ *
25
+ * class User {
26
+ * @PrimaryKey(idField(usr))
27
+ * id!: Id<"usr">;
28
+ * }
29
+ * ```
30
+ */
31
+ function idField(codec) {
32
+ return {
33
+ type: idType(codec),
34
+ onCreate: () => codec.generate()
35
+ };
36
+ }
37
+ /**
6
38
  * Factory that returns a MikroORM `Type` subclass bound to a codec.
7
39
  *
8
40
  * **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through
@@ -12,7 +44,14 @@ import { Type } from "@mikro-orm/core";
12
44
  * `codec.safeParse()`. Throws `IdsError("invalid_id")` if the stored value
13
45
  * does not parse as a valid `Id<Brand>`.
14
46
  *
15
- * **Column type** (`getColumnType`): returns `"text"`.
47
+ * **Column type** (`getColumnType`): returns `"text"` by default, or the
48
+ * `options.columnType` override when provided.
49
+ *
50
+ * @param codec - The brand-scoped codec used to parse values read from the database.
51
+ * @param options - Optional column configuration.
52
+ * @param options.columnType - SQL column type to use (default: `"text"`). Pass
53
+ * `"varchar(30)"` or `"char(26)"` to match an existing DDL or index strategy.
54
+ * The value is passed through verbatim — no validation is performed.
16
55
  *
17
56
  * @example
18
57
  * ```ts
@@ -23,13 +62,21 @@ import { Type } from "@mikro-orm/core";
23
62
  *
24
63
  * const usr = createTimestampId("usr");
25
64
  *
65
+ * // default: text column
26
66
  * class User {
27
67
  * @PrimaryKey({ type: idType(usr) })
28
68
  * id!: Id<"usr">;
29
69
  * }
70
+ *
71
+ * // explicit varchar column
72
+ * class Org {
73
+ * @PrimaryKey({ type: idType(usr, { columnType: "varchar(30)" }) })
74
+ * id!: Id<"usr">;
75
+ * }
30
76
  * ```
31
77
  */
32
- function idType(codec) {
78
+ function idType(codec, options) {
79
+ const columnType = options?.columnType ?? "text";
33
80
  return class extends Type {
34
81
  convertToDatabaseValue(value) {
35
82
  return value;
@@ -37,12 +84,47 @@ function idType(codec) {
37
84
  convertToJSValue(value) {
38
85
  return readIdColumn(codec, value);
39
86
  }
87
+ getColumnType() {
88
+ return columnType;
89
+ }
90
+ };
91
+ }
92
+ /**
93
+ * Factory that returns a MikroORM `Type` subclass for a **nullable** `Id<Brand>` column.
94
+ *
95
+ * Behaves like {@link idType} but `convertToJSValue` returns `null` for `null` /
96
+ * `undefined` database values and `convertToDatabaseValue` passes `null` through
97
+ * unchanged. Use for optional foreign keys.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * import { Property } from "@mikro-orm/core";
102
+ * import { nullableIdType } from "@smonn/ids/mikro-orm";
103
+ * import { createTimestampId } from "@smonn/ids";
104
+ * import type { Id } from "@smonn/ids";
105
+ *
106
+ * const usr = createTimestampId("usr");
107
+ *
108
+ * class Post {
109
+ * @Property({ type: nullableIdType(usr), nullable: true })
110
+ * authorId!: Id<"usr"> | null;
111
+ * }
112
+ * ```
113
+ */
114
+ function nullableIdType(codec) {
115
+ return class extends Type {
116
+ convertToDatabaseValue(value) {
117
+ return value;
118
+ }
119
+ convertToJSValue(value) {
120
+ return readIdColumnNullable(codec, value);
121
+ }
40
122
  getColumnType() {
41
123
  return "text";
42
124
  }
43
125
  };
44
126
  }
45
127
  //#endregion
46
- export { IdsError, idType, isIdsError };
128
+ export { IdsError, idField, idType, isIdsError, nullableIdType };
47
129
 
48
130
  //# sourceMappingURL=mikro-orm.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mikro-orm.mjs","names":[],"sources":["../src/adapters/mikro-orm.ts"],"sourcesContent":["import { Type } from \"@mikro-orm/core\";\nimport { readIdColumn, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\nexport type { IdColumnCodec };\n\n/**\n * Factory that returns a MikroORM `Type` subclass bound to a codec.\n *\n * **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through\n * unchanged — it is already the canonical string form.\n *\n * **Read path** (`convertToJSValue`): normalises the raw DB value via\n * `codec.safeParse()`. Throws `IdsError(\"invalid_id\")` if the stored value\n * does not parse as a valid `Id<Brand>`.\n *\n * **Column type** (`getColumnType`): returns `\"text\"`.\n *\n * @example\n * ```ts\n * import { PrimaryKey } from \"@mikro-orm/core\";\n * import { idType } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * class User {\n * @PrimaryKey({ type: idType(usr) })\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idType<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): new () => Type<Id<Brand>, string> {\n return class extends Type<Id<Brand>, string> {\n override convertToDatabaseValue(value: Id<Brand>): string {\n return value;\n }\n override convertToJSValue(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n }\n override getColumnType(): string {\n return \"text\";\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,OACd,OACmC;CACnC,OAAO,cAAc,KAAwB;EAC3C,uBAAgC,OAA0B;GACxD,OAAO;EACT;EACA,iBAA0B,OAA0B;GAClD,OAAO,aAAa,OAAO,KAAK;EAClC;EACA,gBAAiC;GAC/B,OAAO;EACT;CACF;AACF"}
1
+ {"version":3,"file":"mikro-orm.mjs","names":[],"sources":["../src/adapters/mikro-orm.ts"],"sourcesContent":["import { Type } from \"@mikro-orm/core\";\nimport { readIdColumn, readIdColumnNullable, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\nexport type { IdColumnCodec };\n\n/**\n * Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.\n * Required by {@link idField} so that the MikroORM `onCreate` hook can produce\n * IDs at persist time. Only the **Timestamp codec** and **Reverse Timestamp codec**\n * satisfy this; async-generate codecs (Opaque, Signed, Wrapped, Digest) do not.\n */\nexport type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {\n generate(): Id<Brand>;\n};\n\n/**\n * Returns a MikroORM property option object that wires `codec.generate()` into the\n * `onCreate` lifecycle hook, so the field auto-fills on first persist.\n *\n * Requires a codec variant that exposes a synchronous `generate()` —\n * see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse\n * Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot\n * be passed here.\n *\n * Pass the result as options to `@PrimaryKey()` (the typical case for auto-generated primary keys) or any other MikroORM property decorator:\n *\n * @example\n * ```ts\n * import { PrimaryKey } from \"@mikro-orm/core\";\n * import { idField } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * class User {\n * @PrimaryKey(idField(usr))\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idField<Brand extends string>(\n codec: IdGeneratingCodec<Brand>,\n): {\n type: new () => Type<Id<Brand>, string>;\n onCreate: () => Id<Brand>;\n} {\n return {\n type: idType(codec),\n onCreate: () => codec.generate(),\n };\n}\n\n/**\n * Factory that returns a MikroORM `Type` subclass bound to a codec.\n *\n * **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through\n * unchanged — it is already the canonical string form.\n *\n * **Read path** (`convertToJSValue`): normalises the raw DB value via\n * `codec.safeParse()`. Throws `IdsError(\"invalid_id\")` if the stored value\n * does not parse as a valid `Id<Brand>`.\n *\n * **Column type** (`getColumnType`): returns `\"text\"` by default, or the\n * `options.columnType` override when provided.\n *\n * @param codec - The brand-scoped codec used to parse values read from the database.\n * @param options - Optional column configuration.\n * @param options.columnType - SQL column type to use (default: `\"text\"`). Pass\n * `\"varchar(30)\"` or `\"char(26)\"` to match an existing DDL or index strategy.\n * The value is passed through verbatim — no validation is performed.\n *\n * @example\n * ```ts\n * import { PrimaryKey } from \"@mikro-orm/core\";\n * import { idType } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // default: text column\n * class User {\n * @PrimaryKey({ type: idType(usr) })\n * id!: Id<\"usr\">;\n * }\n *\n * // explicit varchar column\n * class Org {\n * @PrimaryKey({ type: idType(usr, { columnType: \"varchar(30)\" }) })\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idType<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n options?: { columnType?: string },\n): new () => Type<Id<Brand>, string> {\n const columnType = options?.columnType ?? \"text\";\n return class extends Type<Id<Brand>, string> {\n override convertToDatabaseValue(value: Id<Brand>): string {\n return value;\n }\n override convertToJSValue(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n }\n override getColumnType(): string {\n return columnType;\n }\n };\n}\n\n/**\n * Factory that returns a MikroORM `Type` subclass for a **nullable** `Id<Brand>` column.\n *\n * Behaves like {@link idType} but `convertToJSValue` returns `null` for `null` /\n * `undefined` database values and `convertToDatabaseValue` passes `null` through\n * unchanged. Use for optional foreign keys.\n *\n * @example\n * ```ts\n * import { Property } from \"@mikro-orm/core\";\n * import { nullableIdType } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * class Post {\n * @Property({ type: nullableIdType(usr), nullable: true })\n * authorId!: Id<\"usr\"> | null;\n * }\n * ```\n */\nexport function nullableIdType<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): new () => Type<Id<Brand> | null, string | null> {\n return class extends Type<Id<Brand> | null, string | null> {\n override convertToDatabaseValue(value: Id<Brand> | null): string | null {\n return value;\n }\n override convertToJSValue(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n }\n override getColumnType(): string {\n return \"text\";\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,QACd,OAIA;CACA,OAAO;EACL,MAAM,OAAO,KAAK;EAClB,gBAAgB,MAAM,SAAS;CACjC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,OACd,OACA,SACmC;CACnC,MAAM,aAAa,SAAS,cAAc;CAC1C,OAAO,cAAc,KAAwB;EAC3C,uBAAgC,OAA0B;GACxD,OAAO;EACT;EACA,iBAA0B,OAA0B;GAClD,OAAO,aAAa,OAAO,KAAK;EAClC;EACA,gBAAiC;GAC/B,OAAO;EACT;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,eACd,OACiD;CACjD,OAAO,cAAc,KAAsC;EACzD,uBAAgC,OAAwC;GACtE,OAAO;EACT;EACA,iBAA0B,OAAwC;GAChE,OAAO,qBAAqB,OAAO,KAAK;EAC1C;EACA,gBAAiC;GAC/B,OAAO;EACT;CACF;AACF"}
package/dist/nestjs.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as resolveIdParamFailure } from "./adapter-types-7wWdELSh.mjs";
1
+ import { r as resolveIdParamFailure } from "./adapter-types-CjzFNDcJ.mjs";
2
2
  import { BadRequestException, HttpException, Injectable, NotFoundException } from "@nestjs/common";
3
3
  //#region src/adapters/nestjs.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  import { a as toWireId, i as payloadBytesFromId, n as registerBrand, r as payloadBase32Length, s as validateBrand, t as wireMethods } from "./codec-shell-BRZkuQeP.mjs";
2
2
  import { a as writeTimestamp, r as readTimestampMs, t as defaultRng } from "./rng-6GyNT4zS.mjs";
3
- import { a as decryptPayload, i as encodeKeyMaterial, r as decodeKeyMaterial, s as encryptPayload, t as assertValidKeyMaterialByteLength } from "./key-material-1wOKJ1o-.mjs";
3
+ import { a as decryptPayload, i as encodeKeyMaterial, o as deriveKey, r as decodeKeyMaterial, s as encryptPayload, t as assertValidKeyMaterialByteLength } from "./key-material-1wOKJ1o-.mjs";
4
4
  //#region src/codecs/opaque/layout.ts
5
5
  function buildPlaintext(ms, rng) {
6
6
  const plaintext = /* @__PURE__ */ new Uint8Array(16);
@@ -31,21 +31,29 @@ function createOpaqueLayoutOps(prefix, key, rng) {
31
31
  }
32
32
  //#endregion
33
33
  //#region src/codecs/opaque/key.ts
34
+ const aesInfo = new TextEncoder().encode("@smonn/ids/opaque/aes");
34
35
  const opaqueKeyInternals = /* @__PURE__ */ new WeakMap();
35
36
  /**
36
- * Imports raw AES key bytes into an {@link OpaqueKey} handle for the Opaque
37
+ * Imports operator key material into an {@link OpaqueKey} handle for the Opaque
37
38
  * Timestamp codec.
38
39
  *
39
- * Accepts 16, 24, or 32 bytes (AES-128 / AES-192 / AES-256 strength).
40
- * To store or transport key material, use {@link encodeOpaqueKey} /
41
- * {@link decodeOpaqueKey} (`"hex"` or `"base64url"` not Crockford base32).
40
+ * The bytes are HKDF **input keying material**, not the AES key itself: the
41
+ * codec derives an **AES-256** key from them via HKDF under the label
42
+ * `@smonn/ids/opaque/aes` (ADR-0027). Accepts 16, 24, or 32 bytes; the input
43
+ * size sets the entropy floor only — a 16-byte handle still yields AES-256 with
44
+ * a 128-bit entropy floor. To store or transport key material, use
45
+ * {@link encodeOpaqueKey} / {@link decodeOpaqueKey} (`"hex"` or `"base64url"` —
46
+ * not Crockford base32).
42
47
  *
43
- * @param bytes - 16, 24, or 32 raw key bytes.
48
+ * @param bytes - 16, 24, or 32 bytes of raw key material.
44
49
  * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
45
50
  */
46
51
  async function importOpaqueKey(bytes) {
47
52
  assertValidKeyMaterialByteLength(bytes.length, "AES");
48
- const cryptoKey = await crypto.subtle.importKey("raw", bytes, "AES-CBC", false, ["encrypt", "decrypt"]);
53
+ const cryptoKey = await deriveKey(bytes, aesInfo, {
54
+ name: "AES-CBC",
55
+ length: 256
56
+ }, ["encrypt", "decrypt"]);
49
57
  const key = Object.freeze({});
50
58
  opaqueKeyInternals.set(key, cryptoKey);
51
59
  return key;
@@ -56,9 +64,9 @@ function getOpaqueKeyCryptoKey(key) {
56
64
  return cryptoKey;
57
65
  }
58
66
  /**
59
- * Encodes raw AES key bytes for storage in env vars or secret managers.
67
+ * Encodes raw Opaque key material bytes for storage in env vars or secret managers.
60
68
  *
61
- * @param bytes - 16, 24, or 32 raw key bytes (AES-128/192/256).
69
+ * @param bytes - 16, 24, or 32 raw Opaque key material bytes.
62
70
  * @param format - `hex` (lowercase) or `base64url`.
63
71
  * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
64
72
  * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
@@ -113,4 +121,4 @@ function createOpaqueTimestampId(brand, opts) {
113
121
  //#endregion
114
122
  export { importOpaqueKey as i, decodeOpaqueKey as n, encodeOpaqueKey as r, createOpaqueTimestampId as t };
115
123
 
116
- //# sourceMappingURL=opaque-COAcIIY4.mjs.map
124
+ //# sourceMappingURL=opaque-Dle3CmSE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opaque-Dle3CmSE.mjs","names":[],"sources":["../src/codecs/opaque/layout.ts","../src/codecs/opaque/key.ts","../src/codecs/opaque/index.ts"],"sourcesContent":["import type { webcrypto } from \"node:crypto\";\nimport type { Id, LayoutOps, Prefix } from \"../../types.js\";\nimport { decryptPayload, encryptPayload } from \"../_kernel/crypto.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadBase32Length, payloadByteLength } from \"../../wire/invariants.js\";\nimport {\n readTimestampMs,\n timestampByteLength,\n writeTimestamp,\n} from \"../../wire/timestamp-bytes.js\";\n\nfunction buildPlaintext(ms: number, rng: (target: Uint8Array) => void): Uint8Array {\n const plaintext = new Uint8Array(payloadByteLength);\n writeTimestamp(ms, plaintext);\n rng(plaintext.subarray(timestampByteLength, payloadByteLength));\n return plaintext;\n}\n\nasync function extractTimestampFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n id: Id<Brand>,\n): Promise<Date> {\n const plaintext = await decryptPayload(key, payloadBytesFromId(prefix, id));\n return new Date(readTimestampMs(plaintext));\n}\n\n/** Produces a canonical encrypted wire ID. Per-call plaintext/ciphertext buffers —\n * subtle dominates this path; reuse would be safe but not worth pinning to spec detail. */\nasync function generateWireId<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n rng: (target: Uint8Array) => void,\n ms: number,\n): Promise<Id<Brand>> {\n const plaintext = buildPlaintext(ms, rng);\n const encrypted = await encryptPayload(key, plaintext);\n return toWireId(prefix, encrypted);\n}\n\n/** Structural placeholder for JSON Schema (encrypt is async). */\nfunction schemaExample<Brand extends string>(prefix: Prefix<Brand>): string {\n return prefix + \"0\".repeat(payloadBase32Length);\n}\n\n/** Layout ops binder for the Opaque Timestamp variant. `extractTimestampFromId` is module-private; the binder exposes `extractTimestamp` for the codec constructor. */\nexport function createOpaqueLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n key: webcrypto.CryptoKey,\n rng: (target: Uint8Array) => void,\n): LayoutOps<Brand> & {\n generateAt(ms: number): Promise<Id<Brand>>;\n extractTimestamp(id: Id<Brand>): Promise<Date>;\n} {\n return {\n generateAt: (ms: number): Promise<Id<Brand>> => generateWireId(prefix, key, rng, ms),\n extractTimestamp: (id: Id<Brand>): Promise<Date> => extractTimestampFromId(prefix, key, id),\n exampleWireId: (_ms?: number): Id<Brand> => schemaExample(prefix) as Id<Brand>,\n };\n}\n","import type { webcrypto } from \"node:crypto\";\nimport { deriveKey } from \"../_kernel/crypto.js\";\nimport {\n assertValidKeyMaterialByteLength,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\n/** Wire encoding for opaque AES key material (not Crockford base32). */\nexport type OpaqueKeyFormat = \"hex\" | \"base64url\";\n\n// HKDF domain-separation label for the Opaque AES key; see ADR-0019 / ADR-0027.\nconst aesInfo = new TextEncoder().encode(\"@smonn/ids/opaque/aes\");\n\ndeclare const opaqueKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for the Opaque Timestamp codec's AES-256 key.\n *\n * Holds the underlying `webcrypto.CryptoKey` internally; callers never access it directly.\n * Obtain handles via {@link importOpaqueKey} and pass them to\n * `createOpaqueTimestampId` as the `key` option.\n *\n * The same raw secret may safely back an `OpaqueKey` and any other codec's\n * handle (a **primary secret**): each codec derives its key under a distinct\n * HKDF label, so the derived keys are independent — but each codec needs its\n * own explicit import. See ADR-0027.\n */\nexport type OpaqueKey = {\n readonly [opaqueKeyBrand]: \"OpaqueKey\";\n};\n\nconst opaqueKeyInternals = new WeakMap<OpaqueKey, webcrypto.CryptoKey>();\n\n/**\n * Imports operator key material into an {@link OpaqueKey} handle for the Opaque\n * Timestamp codec.\n *\n * The bytes are HKDF **input keying material**, not the AES key itself: the\n * codec derives an **AES-256** key from them via HKDF under the label\n * `@smonn/ids/opaque/aes` (ADR-0027). Accepts 16, 24, or 32 bytes; the input\n * size sets the entropy floor only — a 16-byte handle still yields AES-256 with\n * a 128-bit entropy floor. To store or transport key material, use\n * {@link encodeOpaqueKey} / {@link decodeOpaqueKey} (`\"hex\"` or `\"base64url\"` —\n * not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 bytes of raw key material.\n * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.\n */\nexport async function importOpaqueKey(bytes: Uint8Array): Promise<OpaqueKey> {\n assertValidKeyMaterialByteLength(bytes.length, \"AES\");\n const cryptoKey = await deriveKey(bytes, aesInfo, { name: \"AES-CBC\", length: 256 }, [\n \"encrypt\",\n \"decrypt\",\n ]);\n const key = Object.freeze({}) as OpaqueKey;\n opaqueKeyInternals.set(key, cryptoKey);\n return key;\n}\n\nexport function getOpaqueKeyCryptoKey(key: OpaqueKey): webcrypto.CryptoKey {\n const cryptoKey = opaqueKeyInternals.get(key);\n if (cryptoKey === undefined) {\n throw new Error(\"invalid opaque key\");\n }\n return cryptoKey;\n}\n\n/**\n * Encodes raw Opaque key material bytes for storage in env vars or secret managers.\n *\n * @param bytes - 16, 24, or 32 raw Opaque key material bytes.\n * @param format - `hex` (lowercase) or `base64url`.\n * @throws {IdsError} `invalid_key_format` if `format` is not `\"hex\"` or `\"base64url\"`.\n * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.\n */\nexport function encodeOpaqueKey(bytes: Uint8Array, format: OpaqueKeyFormat): string {\n return encodeKeyMaterial(bytes, format, \"opaque\", \"AES\");\n}\n\n/**\n * Decodes key material emitted by `encodeOpaqueKey` (or `ids keygen`) back to raw bytes.\n *\n * @param encoded - Hex or base64url string.\n * @param format - Must match how the string was encoded.\n * @throws {IdsError} `invalid_key_format` if `format` is not `\"hex\"` or `\"base64url\"`.\n * @throws {IdsError} `invalid_key_encoding` if the string is malformed for its format.\n * @throws {IdsError} `invalid_key_length` if the decoded bytes are not 16, 24, or 32 bytes.\n */\nexport function decodeOpaqueKey(encoded: string, format: OpaqueKeyFormat): Uint8Array {\n return decodeKeyMaterial(encoded, format, \"opaque\", \"AES\");\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { createOpaqueLayoutOps } from \"./layout.js\";\nimport { getOpaqueKeyCryptoKey, type OpaqueKey } from \"./key.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { defaultRng } from \"../_kernel/rng.js\";\nimport type {\n Id,\n JsonSchema,\n ParseResult,\n Prefix,\n StandardSchemaProps,\n ValidBrand,\n} from \"../../types.js\";\nimport { wireMethods } from \"../../wire/codec-shell.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../../error.js\";\nexport {\n decodeOpaqueKey,\n encodeOpaqueKey,\n importOpaqueKey,\n type OpaqueKey,\n type OpaqueKeyFormat,\n} from \"./key.js\";\n\n/**\n * Configuration options for an Opaque Timestamp codec instance.\n */\nexport type OpaqueTimestampOptions = {\n /**\n * {@link OpaqueKey} handle for AES-CBC encryption and decryption.\n * Obtain via {@link importOpaqueKey}.\n *\n * A single key, not a ring: rotation is forward-only and caller-tracked —\n * hold one codec per key epoch and select it from your own records. The\n * library cannot trial keys (the payload is unauthenticated). See ADR-0013.\n */\n key: OpaqueKey;\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes random bytes into `target` for ID generation. Defaults to `crypto.getRandomValues`. */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * A brand-scoped codec for generating and validating Opaque Timestamp IDs.\n *\n * Same wire shape as the Timestamp codec (`{brand}_` + 26 base32 chars) but the\n * payload is AES-CBC encrypted. `generate`, `generateAt`, and `extractTimestamp`\n * are async; parsing methods are sync. No `minIdForTime` / `maxIdForTime` —\n * encrypted payloads do not sort by creation time.\n *\n * @remarks\n * **Security properties (unauthenticated, deterministic, and malleable by design):**\n *\n * - The payload is AES-CBC encrypted but **unauthenticated** — there is no\n * integrity tag. A tampered or wrong-key payload decrypts to garbage bytes\n * without throwing.\n * - Opaque IDs must be treated as **opaque handles**, not as trusted or\n * authenticated tokens.\n * - `extractTimestamp` is best-effort on untrusted input: a wrong or tampered\n * key returns a plausible-looking `Date` without error, not a verification\n * failure. Do not treat the returned timestamp as proof of origin.\n */\nexport type OpaqueTimestampCodec<Brand extends string> = {\n /** Produces a new canonical encrypted ID using the codec's `now` and `rng`. */\n generate(): Promise<Id<Brand>>;\n /** Produces a new canonical encrypted ID with timestamp bytes from `date`. Throws on invalid dates. */\n generateAt(date: Date): Promise<Id<Brand>>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decrypts and decodes the creation `Date` from an `Id<Brand>`. Trusts the type — use `safeParse()` at boundaries first. See ADR-0002.\n *\n * Requires the same key used at generation; a wrong key returns a plausible\n * but wrong `Date`, never an error. With rotation, select the codec for the\n * ID's key epoch from your own records — the library cannot. See ADR-0013.\n */\n extractTimestamp(id: Id<Brand>): Promise<Date>;\n /**\n * JSON Schema for the canonical wire form. The `pattern` matches the canonical stored\n * form only and is deliberately stricter than `parse()`/`safeParse()`, which accept\n * uppercase letters and Crockford aliases (`o`/`i`/`l`) before normalising. See ADR-0003.\n * The `example` is a structural placeholder (generated at construction time).\n */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n /**\n * Converts a trusted `Id<Brand>` to an RFC 9562 canonical (lowercase, hyphenated)\n * UUID string by reinterpreting the 16-byte payload verbatim. The payload is the\n * encrypted ciphertext — `toUUID` does not decrypt it. Total — cannot fail.\n * Returns a plain `string` (brand is shed). See ADR-0024.\n */\n toUUID(id: Id<Brand>): string;\n /**\n * Parses a UUID string into an `Id<Brand>`. Accepts case-insensitive `8-4-4-4-12`\n * hyphenated form only. Throws `IdsError` with `code: \"invalid_id\"` on bad input.\n * See ADR-0024.\n */\n fromUUID(value: string): Id<Brand>;\n /**\n * Non-throwing UUID parse. Returns `{ ok: true, id }` or\n * `{ ok: false, error: \"not_string\" | \"invalid_uuid\" }`. See ADR-0024.\n */\n safeFromUUID(value: unknown): ParseResult<Brand>;\n};\n\n/**\n * Creates an Opaque Timestamp codec for `brand` (three lowercase a–z characters).\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Required `key` (an {@link OpaqueKey} from {@link importOpaqueKey}) plus\n * optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n */\nexport function createOpaqueTimestampId<Brand extends string>(\n brand: Brand & ValidBrand<Brand>,\n opts: OpaqueTimestampOptions,\n): OpaqueTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const cryptoKey = getOpaqueKeyCryptoKey(opts.key);\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? defaultRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createOpaqueLayoutOps(prefix, cryptoKey, rng);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n toUUID: wire.toUUID,\n fromUUID: wire.fromUUID,\n safeFromUUID: wire.safeFromUUID,\n };\n}\n"],"mappings":";;;;AAWA,SAAS,eAAe,IAAY,KAA+C;CACjF,MAAM,4BAAY,IAAI,WAAA,EAA4B;CAClD,eAAe,IAAI,SAAS;CAC5B,IAAI,UAAU,SAAA,GAAA,EAA+C,CAAC;CAC9D,OAAO;AACT;AAEA,eAAe,uBACb,QACA,KACA,IACe;CACf,MAAM,YAAY,MAAM,eAAe,KAAK,mBAAmB,QAAQ,EAAE,CAAC;CAC1E,OAAO,IAAI,KAAK,gBAAgB,SAAS,CAAC;AAC5C;;;AAIA,eAAe,eACb,QACA,KACA,KACA,IACoB;CAGpB,OAAO,SAAS,QAAQ,MADA,eAAe,KADrB,eAAe,IAAI,GACe,CAAC,CACpB;AACnC;;AAGA,SAAS,cAAoC,QAA+B;CAC1E,OAAO,SAAS,IAAI,OAAO,mBAAmB;AAChD;;AAGA,SAAgB,sBACd,QACA,KACA,KAIA;CACA,OAAO;EACL,aAAa,OAAmC,eAAe,QAAQ,KAAK,KAAK,EAAE;EACnF,mBAAmB,OAAiC,uBAAuB,QAAQ,KAAK,EAAE;EAC1F,gBAAgB,QAA4B,cAAc,MAAM;CAClE;AACF;;;AC/CA,MAAM,UAAU,IAAI,YAAY,CAAC,CAAC,OAAO,uBAAuB;AAoBhE,MAAM,qCAAqB,IAAI,QAAwC;;;;;;;;;;;;;;;;AAiBvE,eAAsB,gBAAgB,OAAuC;CAC3E,iCAAiC,MAAM,QAAQ,KAAK;CACpD,MAAM,YAAY,MAAM,UAAU,OAAO,SAAS;EAAE,MAAM;EAAW,QAAQ;CAAI,GAAG,CAClF,WACA,SACF,CAAC;CACD,MAAM,MAAM,OAAO,OAAO,CAAC,CAAC;CAC5B,mBAAmB,IAAI,KAAK,SAAS;CACrC,OAAO;AACT;AAEA,SAAgB,sBAAsB,KAAqC;CACzE,MAAM,YAAY,mBAAmB,IAAI,GAAG;CAC5C,IAAI,cAAc,KAAA,GAChB,MAAM,IAAI,MAAM,oBAAoB;CAEtC,OAAO;AACT;;;;;;;;;AAUA,SAAgB,gBAAgB,OAAmB,QAAiC;CAClF,OAAO,kBAAkB,OAAO,QAAQ,UAAU,KAAK;AACzD;;;;;;;;;;AAWA,SAAgB,gBAAgB,SAAiB,QAAqC;CACpF,OAAO,kBAAkB,SAAS,QAAQ,UAAU,KAAK;AAC3D;;;;;;;;;;ACqCA,SAAgB,wBACd,OACA,MAC6B;CAC7B,cAAc,KAAK;CACnB,cAAc,OAAO,KAAK,mBAAmB;CAE7C,MAAM,YAAY,sBAAsB,KAAK,GAAG;CAChD,MAAM,MAAM,KAAK,OAAO,KAAK;CAC7B,MAAM,MAAM,KAAK,OAAO;CACxB,MAAM,SAAwB,GAAG,MAAM;CACvC,MAAM,OAAO,YAAY,MAAM;CAC/B,MAAM,SAAS,sBAAsB,QAAQ,WAAW,GAAG;CAE3D,OAAO;EACL,gBAAgB,OAAO,WAAW,IAAI,CAAC;EACvC,aAAa,SAAe,OAAO,WAAW,KAAK,QAAQ,CAAC;EAC5D,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,kBAAkB,OAAO;EACzB,oBAAoB,KAAK,aAAa,OAAO,OAAO,cAAc,CAAC;EACnE,aAAa,KAAK;EAClB,QAAQ,KAAK;EACb,UAAU,KAAK;EACf,cAAc,KAAK;CACrB;AACF"}
package/dist/opaque.d.mts CHANGED
@@ -6,34 +6,40 @@ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcK
6
6
  type OpaqueKeyFormat = "hex" | "base64url";
7
7
  declare const opaqueKeyBrand: unique symbol;
8
8
  /**
9
- * Opaque imported handle for one AES key used by the Opaque Timestamp codec.
9
+ * Opaque imported handle for the Opaque Timestamp codec's AES-256 key.
10
10
  *
11
11
  * Holds the underlying `webcrypto.CryptoKey` internally; callers never access it directly.
12
12
  * Obtain handles via {@link importOpaqueKey} and pass them to
13
13
  * `createOpaqueTimestampId` as the `key` option.
14
14
  *
15
- * Distinct from the `WrappingKey` used by `@smonn/ids/wrapped` one raw
16
- * secret must not silently serve both codecs without an explicit import.
15
+ * The same raw secret may safely back an `OpaqueKey` and any other codec's
16
+ * handle (a **primary secret**): each codec derives its key under a distinct
17
+ * HKDF label, so the derived keys are independent — but each codec needs its
18
+ * own explicit import. See ADR-0027.
17
19
  */
18
20
  type OpaqueKey = {
19
21
  readonly [opaqueKeyBrand]: "OpaqueKey";
20
22
  };
21
23
  /**
22
- * Imports raw AES key bytes into an {@link OpaqueKey} handle for the Opaque
24
+ * Imports operator key material into an {@link OpaqueKey} handle for the Opaque
23
25
  * Timestamp codec.
24
26
  *
25
- * Accepts 16, 24, or 32 bytes (AES-128 / AES-192 / AES-256 strength).
26
- * To store or transport key material, use {@link encodeOpaqueKey} /
27
- * {@link decodeOpaqueKey} (`"hex"` or `"base64url"` not Crockford base32).
27
+ * The bytes are HKDF **input keying material**, not the AES key itself: the
28
+ * codec derives an **AES-256** key from them via HKDF under the label
29
+ * `@smonn/ids/opaque/aes` (ADR-0027). Accepts 16, 24, or 32 bytes; the input
30
+ * size sets the entropy floor only — a 16-byte handle still yields AES-256 with
31
+ * a 128-bit entropy floor. To store or transport key material, use
32
+ * {@link encodeOpaqueKey} / {@link decodeOpaqueKey} (`"hex"` or `"base64url"` —
33
+ * not Crockford base32).
28
34
  *
29
- * @param bytes - 16, 24, or 32 raw key bytes.
35
+ * @param bytes - 16, 24, or 32 bytes of raw key material.
30
36
  * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
31
37
  */
32
38
  declare function importOpaqueKey(bytes: Uint8Array): Promise<OpaqueKey>;
33
39
  /**
34
- * Encodes raw AES key bytes for storage in env vars or secret managers.
40
+ * Encodes raw Opaque key material bytes for storage in env vars or secret managers.
35
41
  *
36
- * @param bytes - 16, 24, or 32 raw key bytes (AES-128/192/256).
42
+ * @param bytes - 16, 24, or 32 raw Opaque key material bytes.
37
43
  * @param format - `hex` (lowercase) or `base64url`.
38
44
  * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
39
45
  * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
@@ -1 +1 @@
1
- {"version":3,"file":"opaque.d.mts","names":[],"sources":["../src/codecs/opaque/key.ts","../src/codecs/opaque/index.ts"],"mappings":";;;;;KAQY,eAAA;AAAA,cAEE,cAAA;AAFd;;;;AAAY;AAA0B;;;;AAExB;AAFd,KAcY,SAAA;EAAA,UACA,cAAA;AAAA;;AAAA;AAgBZ;;;;;;;;;iBAAsB,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,SAAA;;;AAAA;AA8BlE;;;;;iBAAgB,eAAA,CAAgB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,eAAA;;;;AAAA;AAa3D;;;;;iBAAgB,eAAA,CAAgB,OAAA,UAAiB,MAAA,EAAQ,eAAA,GAAkB,UAAA;;;;;;KCtD/D,sBAAA;EDpB0B;;;;AAExB;AAYd;;;ECeE,GAAA,EAAK,SAAA,EDdK;ECgBV,GAAA,iBDAoB;ECEpB,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;;;;;;;;ADJgE;AA8BlE;;;;;;;;;AAA2D;AAa3D;;KChBY,oBAAA;EDgB+D,+ECdzE,QAAA,IAAY,OAAA,CAAQ,EAAA,CAAG,KAAA;EAEvB,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,OAAA,CAAQ,EAAA,CAAG,KAAA;;;ADYsC;;ECPzE,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;AA/ClC;;EAmDE,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAtCX;;;EA0Cf,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;;;AAxCvC;AAuBF;EAyBE,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,OAAA,CAAQ,IAAA;;;;;;;EAOzC,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;;;;;;;EAO1C,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA;;;;;;EAMd,QAAA,CAAS,KAAA,WAAgB,EAAA,CAAG,KAAA;;;;;EAK5B,YAAA,CAAa,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;;;;;;;iBAU5B,uBAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,EAAM,sBAAA,GACL,oBAAA,CAAqB,KAAA"}
1
+ {"version":3,"file":"opaque.d.mts","names":[],"sources":["../src/codecs/opaque/key.ts","../src/codecs/opaque/index.ts"],"mappings":";;;;;KASY,eAAA;AAAA,cAKE,cAAA;AALd;;;;AAAY;AAA0B;;;;AAKxB;AAcd;;AAnBA,KAmBY,SAAA;EAAA,UACA,cAAA;AAAA;AAoBZ;;;;;;;;;;;;;AAAkE;AA2BlE;AA3BA,iBAAsB,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,SAAA;;;;;;;AA2BP;AAa3D;iBAbgB,eAAA,CAAgB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,eAAA;;;;;;;;AAagB;;iBAA3D,eAAA,CAAgB,OAAA,UAAiB,MAAA,EAAQ,eAAA,GAAkB,UAAA;;;;;;KC7D/D,sBAAA;EDnB0B;;;;AAKxB;AAcd;;;ECSE,GAAA,EAAK,SAAA,EDRK;ECUV,GAAA,iBDUoB;ECRpB,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;;;;;;;;ADMgE;AA2BlE;;;;;;;;;AAA2D;AAa3D;;KCvBY,oBAAA;EDuB+D,+ECrBzE,QAAA,IAAY,OAAA,CAAQ,EAAA,CAAG,KAAA;EAEvB,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,OAAA,CAAQ,EAAA,CAAG,KAAA;;;ADmBsC;;ECdzE,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;AA/ClC;;EAmDE,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAtCX;;;EA0Cf,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;;;AAxCvC;AAuBF;EAyBE,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,OAAA,CAAQ,IAAA;;;;;;;EAOzC,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;;;;;;;EAO1C,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA;;;;;;EAMd,QAAA,CAAS,KAAA,WAAgB,EAAA,CAAG,KAAA;;;;;EAK5B,YAAA,CAAa,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;;;;;;;iBAU5B,uBAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,EAAM,sBAAA,GACL,oBAAA,CAAqB,KAAA"}
package/dist/opaque.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-COAcIIY4.mjs";
2
+ import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-Dle3CmSE.mjs";
3
3
  export { IdsError, createOpaqueTimestampId, decodeOpaqueKey, encodeOpaqueKey, importOpaqueKey, isIdsError };