@smonn/ids 0.13.1 → 0.14.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 (59) hide show
  1. package/README.md +17 -5
  2. package/dist/{adapter-types-CIc-4O-P.d.mts → adapter-types-Bia_w9sg.d.mts} +2 -2
  3. package/dist/{adapter-types-CIc-4O-P.d.mts.map → adapter-types-Bia_w9sg.d.mts.map} +1 -1
  4. package/dist/cli.mjs +82 -15
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/{codec-shell-C2NKQEx2.mjs → codec-shell-BRZkuQeP.mjs} +89 -7
  7. package/dist/codec-shell-BRZkuQeP.mjs.map +1 -0
  8. package/dist/{digest-DsGeXfk3.mjs → digest-CLJEGBxo.mjs} +7 -4
  9. package/dist/{digest-DsGeXfk3.mjs.map → digest-CLJEGBxo.mjs.map} +1 -1
  10. package/dist/digest.d.mts +19 -2
  11. package/dist/digest.d.mts.map +1 -1
  12. package/dist/digest.mjs +1 -1
  13. package/dist/drizzle.d.mts +3 -3
  14. package/dist/{error-Dqyho9vp.d.mts → error-CifcKKOG.d.mts} +2 -2
  15. package/dist/{error-Dqyho9vp.d.mts.map → error-CifcKKOG.d.mts.map} +1 -1
  16. package/dist/express.d.mts +2 -2
  17. package/dist/fastify.d.mts +2 -2
  18. package/dist/graphql.d.mts +2 -2
  19. package/dist/hono.d.mts +2 -2
  20. package/dist/index.d.mts +19 -2
  21. package/dist/index.d.mts.map +1 -1
  22. package/dist/index.mjs +1 -1
  23. package/dist/{key-material-DvjACe89.mjs → key-material-1wOKJ1o-.mjs} +2 -2
  24. package/dist/{key-material-DvjACe89.mjs.map → key-material-1wOKJ1o-.mjs.map} +1 -1
  25. package/dist/kysely.d.mts +3 -3
  26. package/dist/mikro-orm.d.mts +3 -3
  27. package/dist/nestjs.d.mts +2 -2
  28. package/dist/{opaque-BW3Uzeeb.mjs → opaque-COAcIIY4.mjs} +14 -5
  29. package/dist/opaque-COAcIIY4.mjs.map +1 -0
  30. package/dist/opaque.d.mts +26 -2
  31. package/dist/opaque.d.mts.map +1 -1
  32. package/dist/opaque.mjs +1 -1
  33. package/dist/prisma.d.mts +3 -3
  34. package/dist/{reverse-BW8g_cln.mjs → reverse-CT-El3hi.mjs} +7 -4
  35. package/dist/{reverse-BW8g_cln.mjs.map → reverse-CT-El3hi.mjs.map} +1 -1
  36. package/dist/reverse.d.mts +19 -2
  37. package/dist/reverse.d.mts.map +1 -1
  38. package/dist/reverse.mjs +1 -1
  39. package/dist/{rng-BHFxX1Fc.mjs → rng-6GyNT4zS.mjs} +2 -2
  40. package/dist/{rng-BHFxX1Fc.mjs.map → rng-6GyNT4zS.mjs.map} +1 -1
  41. package/dist/{signed-BTz3ZFYE.mjs → signed-Dkdteu1y.mjs} +8 -5
  42. package/dist/{signed-BTz3ZFYE.mjs.map → signed-Dkdteu1y.mjs.map} +1 -1
  43. package/dist/signed.d.mts +19 -2
  44. package/dist/signed.d.mts.map +1 -1
  45. package/dist/signed.mjs +1 -1
  46. package/dist/{timestamp-CleAIdZI.mjs → timestamp-RXXwHfHO.mjs} +7 -4
  47. package/dist/{timestamp-CleAIdZI.mjs.map → timestamp-RXXwHfHO.mjs.map} +1 -1
  48. package/dist/typeorm.d.mts +2 -2
  49. package/dist/{types-wplmOgOK.d.mts → types-hGBnCpJj.d.mts} +3 -3
  50. package/dist/{types-wplmOgOK.d.mts.map → types-hGBnCpJj.d.mts.map} +1 -1
  51. package/dist/{wrapped-DPlsv1x-.mjs → wrapped-Oj2hC1vB.mjs} +15 -4
  52. package/dist/wrapped-Oj2hC1vB.mjs.map +1 -0
  53. package/dist/wrapped.d.mts +27 -2
  54. package/dist/wrapped.d.mts.map +1 -1
  55. package/dist/wrapped.mjs +1 -1
  56. package/package.json +1 -1
  57. package/dist/codec-shell-C2NKQEx2.mjs.map +0 -1
  58. package/dist/opaque-BW3Uzeeb.mjs.map +0 -1
  59. package/dist/wrapped-DPlsv1x-.mjs.map +0 -1
package/dist/digest.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
3
3
 
4
4
  //#region src/codecs/digest/key.d.ts
5
5
  /** Wire encoding for digest operator key material (not Crockford base32). */
@@ -101,6 +101,23 @@ type DigestCodec<Brand extends string> = {
101
101
  safeParse(value: unknown): ParseResult<Brand>; /** JSON Schema for the canonical wire form (`pattern` is canonical-only). */
102
102
  toJsonSchema(): JsonSchema; /** Standard Schema validate entry point. */
103
103
  readonly "~standard": StandardSchemaProps<Brand>;
104
+ /**
105
+ * Converts a trusted `Id<Brand>` to an RFC 9562 canonical (lowercase, hyphenated)
106
+ * UUID string by reinterpreting the 16-byte payload verbatim. Total — cannot fail.
107
+ * Returns a plain `string` (brand is shed). See ADR-0024.
108
+ */
109
+ toUUID(id: Id<Brand>): string;
110
+ /**
111
+ * Parses a UUID string into an `Id<Brand>`. Accepts case-insensitive `8-4-4-4-12`
112
+ * hyphenated form only. Throws `IdsError` with `code: "invalid_id"` on bad input.
113
+ * See ADR-0024.
114
+ */
115
+ fromUUID(value: string): Id<Brand>;
116
+ /**
117
+ * Non-throwing UUID parse. Returns `{ ok: true, id }` or
118
+ * `{ ok: false, error: "not_string" | "invalid_uuid" }`. See ADR-0024.
119
+ */
120
+ safeFromUUID(value: unknown): ParseResult<Brand>;
104
121
  };
105
122
  /**
106
123
  * Construct a {@link DigestCodec} for `brand`.
@@ -1 +1 @@
1
- {"version":3,"file":"digest.d.mts","names":[],"sources":["../src/codecs/digest/key.ts","../src/codecs/digest/index.ts"],"mappings":";;;;;KASY,eAAA;AAAA,cAIE,cAAA;AAJd;;;;AAAY;AAA0B;;;;AAIxB;AAkBd;;;;AACY;AAoBZ;AA3CA,KAsBY,SAAA;EAAA,UACA,cAAA;AAAA;;;;;;;;;;AAoBsD;AAmBlE;iBAnBsB,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,SAAA;;;;;;;;AAmBP;AAa3D;iBAbgB,eAAA,CAAgB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,eAAA;;;;;;;;AAagB;;iBAA3D,eAAA,CAAgB,OAAA,UAAiB,MAAA,EAAQ,eAAA,GAAkB,UAAA;;;;;;KCvD/D,aAAA;EDpB0B;;;;AAIxB;ECsBZ,EAAA;EDJU;;;AACA;ECQV,GAAA,EAAK,SAAA,EDYe;ECVpB,mBAAA;AAAA;;;;;;;;;;ADUgE;AAmBlE;;;KCbY,WAAA;;;;;;ADa+C;ECNzD,MAAA,CAAO,QAAA,WAAmB,UAAA,GAAa,OAAA,CAAQ,EAAA,CAAG,KAAA,IDmBpC;ECjBd,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA,GDiByC;ECfzE,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAE1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;EAEvC,YAAA,IAAgB,UAAA,EDWyD;EAAA,SCThE,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;;AA9C5C;;;;;;;;;AAaE;AAgBF;;;;;iBAoCgB,cAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,EAAM,aAAA,GACL,WAAA,CAAY,KAAA"}
1
+ {"version":3,"file":"digest.d.mts","names":[],"sources":["../src/codecs/digest/key.ts","../src/codecs/digest/index.ts"],"mappings":";;;;;KASY,eAAA;AAAA,cAIE,cAAA;AAJd;;;;AAAY;AAA0B;;;;AAIxB;AAkBd;;;;AACY;AAoBZ;AA3CA,KAsBY,SAAA;EAAA,UACA,cAAA;AAAA;;;;;;;;;;AAoBsD;AAmBlE;iBAnBsB,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,SAAA;;;;;;;;AAmBP;AAa3D;iBAbgB,eAAA,CAAgB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,eAAA;;;;;;;;AAagB;;iBAA3D,eAAA,CAAgB,OAAA,UAAiB,MAAA,EAAQ,eAAA,GAAkB,UAAA;;;;;;KCvD/D,aAAA;EDpB0B;;;;AAIxB;ECsBZ,EAAA;EDJU;;;AACA;ECQV,GAAA,EAAK,SAAA,EDYe;ECVpB,mBAAA;AAAA;;;;;;;;;;ADUgE;AAmBlE;;;KCbY,WAAA;;;;;;ADa+C;ECNzD,MAAA,CAAO,QAAA,WAAmB,UAAA,GAAa,OAAA,CAAQ,EAAA,CAAG,KAAA,IDmBpC;ECjBd,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA,GDiByC;ECfzE,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAE1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;EAEvC,YAAA,IAAgB,UAAA,EDWyD;EAAA,SCThE,WAAA,EAAa,mBAAA,CAAoB,KAAA;;;AA9C5C;;;EAoDE,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA;;;;;;EAMd,QAAA,CAAS,KAAA,WAAgB,EAAA,CAAG,KAAA;EA7B9B;;;;EAkCE,YAAA,CAAa,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;;;;;;;;;;;;;;;;iBAmB5B,cAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,EAAM,aAAA,GACL,WAAA,CAAY,KAAA"}
package/dist/digest.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { i as importDigestKey, n as decodeDigestKey, r as encodeDigestKey, t as createDigestId } from "./digest-DsGeXfk3.mjs";
2
+ import { i as importDigestKey, n as decodeDigestKey, r as encodeDigestKey, t as createDigestId } from "./digest-CLJEGBxo.mjs";
3
3
  export { IdsError, createDigestId, decodeDigestKey, encodeDigestKey, importDigestKey, isIdsError };
@@ -1,6 +1,6 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdColumnCodec } from "./adapter-types-CIc-4O-P.mjs";
3
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdColumnCodec } from "./adapter-types-Bia_w9sg.mjs";
3
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
4
4
  import { ConvertCustomConfig, PgCustomColumnBuilder } from "drizzle-orm/pg-core";
5
5
 
6
6
  //#region src/adapters/drizzle.d.ts
@@ -1,4 +1,4 @@
1
- import { r as ParseError } from "./types-wplmOgOK.mjs";
1
+ import { r as ParseError } from "./types-hGBnCpJj.mjs";
2
2
 
3
3
  //#region src/error.d.ts
4
4
  /**
@@ -46,4 +46,4 @@ declare class IdsError extends Error {
46
46
  declare function isIdsError(value: unknown): value is IdsError;
47
47
  //#endregion
48
48
  export { IdsErrorCode as n, isIdsError as r, IdsError as t };
49
- //# sourceMappingURL=error-Dqyho9vp.d.mts.map
49
+ //# sourceMappingURL=error-CifcKKOG.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-Dqyho9vp.d.mts","names":[],"sources":["../src/error.ts"],"mappings":";;;;;AASA;;;KAAY,YAAA;AAAA;AA4BZ;;;;;;;;;;;;;;AA5BY,cA4BC,QAAA,SAAiB,KAAA;EAAA,SACnB,IAAA,EAAM,YAAA;EAAA,SACE,KAAA,GAAQ,UAAA;EAEzB,WAAA,CAAY,IAAA,EAAM,YAAA,EAAc,OAAA,UAAiB,OAAA;IAAY,KAAA,GAAQ,UAAA;EAAA;AAAA;;;AAAW;AA2BlF;;;;;;;;AAAqD;;;iBAArC,UAAA,CAAW,KAAA,YAAiB,KAAA,IAAS,QAAA"}
1
+ {"version":3,"file":"error-CifcKKOG.d.mts","names":[],"sources":["../src/error.ts"],"mappings":";;;;;AASA;;;KAAY,YAAA;AAAA;AA4BZ;;;;;;;;;;;;;;AA5BY,cA4BC,QAAA,SAAiB,KAAA;EAAA,SACnB,IAAA,EAAM,YAAA;EAAA,SACE,KAAA,GAAQ,UAAA;EAEzB,WAAA,CAAY,IAAA,EAAM,YAAA,EAAc,OAAA,UAAiB,OAAA;IAAY,KAAA,GAAQ,UAAA;EAAA;AAAA;;;AAAW;AA2BlF;;;;;;;;AAAqD;;;iBAArC,UAAA,CAAW,KAAA,YAAiB,KAAA,IAAS,QAAA"}
@@ -1,5 +1,5 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { r as IdParamFailure, t as IdCodec } from "./adapter-types-CIc-4O-P.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { r as IdParamFailure, t as IdCodec } from "./adapter-types-Bia_w9sg.mjs";
3
3
  import { NextFunction, Request, Response } from "express";
4
4
 
5
5
  //#region src/adapters/express.d.ts
@@ -1,5 +1,5 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { r as IdParamFailure, t as IdCodec } from "./adapter-types-CIc-4O-P.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { r as IdParamFailure, t as IdCodec } from "./adapter-types-Bia_w9sg.mjs";
3
3
  import { FastifyReply, FastifyRequest } from "fastify";
4
4
 
5
5
  //#region src/adapters/fastify.d.ts
@@ -1,5 +1,5 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { t as IdCodec } from "./adapter-types-CIc-4O-P.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { t as IdCodec } from "./adapter-types-Bia_w9sg.mjs";
3
3
  import { GraphQLScalarType } from "graphql";
4
4
 
5
5
  //#region src/adapters/graphql.d.ts
package/dist/hono.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { r as IdParamFailure, t as IdCodec } from "./adapter-types-CIc-4O-P.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { r as IdParamFailure, t as IdCodec } from "./adapter-types-Bia_w9sg.mjs";
3
3
  import { ContentfulStatusCode } from "hono/utils/http-status";
4
4
  import { Context, MiddlewareHandler } from "hono";
5
5
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, r as ParseError, t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, r as ParseError, t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
3
3
 
4
4
  //#region src/codecs/timestamp/index.d.ts
5
5
  /**
@@ -52,6 +52,23 @@ type TimestampCodec<Brand extends string> = {
52
52
  */
53
53
  toJsonSchema(): JsonSchema; /** Standard Schema validate entry point. */
54
54
  readonly "~standard": StandardSchemaProps<Brand>;
55
+ /**
56
+ * Converts a trusted `Id<Brand>` to an RFC 9562 canonical (lowercase, hyphenated)
57
+ * UUID string by reinterpreting the 16-byte payload verbatim. Total — cannot fail.
58
+ * Returns a plain `string` (brand is shed). See ADR-0024.
59
+ */
60
+ toUUID(id: Id<Brand>): string;
61
+ /**
62
+ * Parses a UUID string into an `Id<Brand>`. Accepts case-insensitive `8-4-4-4-12`
63
+ * hyphenated form only. Throws `IdsError` with `code: "invalid_id"` on bad input.
64
+ * See ADR-0024.
65
+ */
66
+ fromUUID(value: string): Id<Brand>;
67
+ /**
68
+ * Non-throwing UUID parse. Returns `{ ok: true, id }` or
69
+ * `{ ok: false, error: "not_string" | "invalid_uuid" }`. See ADR-0024.
70
+ */
71
+ safeFromUUID(value: unknown): ParseResult<Brand>;
55
72
  };
56
73
  /**
57
74
  * Creates a codec for `brand` (three lowercase a–z characters).
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/codecs/timestamp/index.ts"],"mappings":";;;;;;;KAiBY,gBAAA;EAAA,6EAEV,GAAA,iBAEe;EAAf,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;;;AAAA;AAeF;;;;;;KAAY,cAAA;uEAEV,QAAA,IAAY,EAAA,CAAG,KAAA;EAEf,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;EAK3B,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;;;EAIhC,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;;;;EAI1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;;;;;EAQvC,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,IAAA;EAEjC,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;EAE7B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;;EAM7B,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;;;;;;;;;;;;;;iBAwB5B,iBAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,GAAM,gBAAA,GACL,cAAA,CAAe,KAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/codecs/timestamp/index.ts"],"mappings":";;;;;;;KAiBY,gBAAA;EAAA,6EAEV,GAAA,iBAEe;EAAf,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;;;AAAA;AAeF;;;;;;KAAY,cAAA;uEAEV,QAAA,IAAY,EAAA,CAAG,KAAA;EAEf,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;EAK3B,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;;;EAIhC,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;;;;EAI1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;;;;;EAQvC,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,IAAA,EA6BH;EA3B9B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;EAE7B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;;EAM7B,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;;;;;;EAM1C,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA;;;;;;EAMd,QAAA,CAAS,KAAA,WAAgB,EAAA,CAAG,KAAA;;;;;EAK5B,YAAA,CAAa,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;;;;;;;;;;;;;iBAwB5B,iBAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,GAAM,gBAAA,GACL,cAAA,CAAe,KAAA"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { t as createTimestampId } from "./timestamp-CleAIdZI.mjs";
2
+ import { t as createTimestampId } from "./timestamp-RXXwHfHO.mjs";
3
3
  export { IdsError, createTimestampId, isIdsError };
@@ -1,5 +1,5 @@
1
1
  import { t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import "./codec-shell-C2NKQEx2.mjs";
2
+ import "./codec-shell-BRZkuQeP.mjs";
3
3
  //#region src/codecs/_kernel/bytes.ts
4
4
  const hexDigits = "0123456789abcdef";
5
5
  const invalidNibble = 255;
@@ -185,4 +185,4 @@ function decodeKeyMaterial(encoded, format, formatNoun, lengthNoun) {
185
185
  //#endregion
186
186
  export { decryptPayload as a, timingSafeEqual as c, encodeKeyMaterial as i, writeLen32 as l, assertValidKeyring as n, deriveKey as o, decodeKeyMaterial as r, encryptPayload as s, assertValidKeyMaterialByteLength as t };
187
187
 
188
- //# sourceMappingURL=key-material-DvjACe89.mjs.map
188
+ //# sourceMappingURL=key-material-1wOKJ1o-.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"key-material-DvjACe89.mjs","names":[],"sources":["../src/codecs/_kernel/bytes.ts","../src/codecs/_kernel/crypto.ts","../src/codecs/_kernel/key-material.ts"],"sourcesContent":["const hexDigits = \"0123456789abcdef\";\n\nconst invalidNibble = 0xff;\nconst hexCharCodeToNibble = new Uint8Array(128).fill(invalidNibble);\nfor (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;\nfor (let i = 0; i < 6; i++) {\n hexCharCodeToNibble[97 + i] = 10 + i;\n hexCharCodeToNibble[65 + i] = 10 + i;\n}\n\n/** Lowercase hex encoding of raw bytes. */\nexport function encodeHex(bytes: Uint8Array): string {\n // oxlint-disable-next-line no-new-array\n const codes = new Array<number>(bytes.length * 2);\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n codes[i * 2] = hexDigits.charCodeAt(b >>> 4);\n codes[i * 2 + 1] = hexDigits.charCodeAt(b & 0x0f);\n }\n return String.fromCharCode(...codes);\n}\n\n/** Decodes a hex string to raw bytes. Throws on non-hex input. */\nexport function decodeHex(encoded: string): Uint8Array {\n if (encoded.length % 2 !== 0) throw new Error(\"invalid hex\");\n const out = new Uint8Array(encoded.length / 2);\n for (let i = 0; i < out.length; i++) {\n const hiCode = encoded.charCodeAt(i * 2);\n const loCode = encoded.charCodeAt(i * 2 + 1);\n if (hiCode >= hexCharCodeToNibble.length || loCode >= hexCharCodeToNibble.length) {\n throw new Error(\"invalid hex\");\n }\n const hi = hexCharCodeToNibble[hiCode]!;\n const lo = hexCharCodeToNibble[loCode]!;\n if (hi === invalidNibble || lo === invalidNibble) {\n throw new Error(\"invalid hex\");\n }\n out[i] = (hi << 4) | lo;\n }\n return out;\n}\n\n/** Base64url encoding without padding. */\nexport function encodeBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Writes a 32-bit integer as four big-endian bytes into target[offset..offset+3]. */\nexport function writeLen32(value: number, target: Uint8Array, offset: number): void {\n target[offset] = (value >>> 24) & 0xff;\n target[offset + 1] = (value >>> 16) & 0xff;\n target[offset + 2] = (value >>> 8) & 0xff;\n target[offset + 3] = value & 0xff;\n}\n\n/** Decodes a base64url string to raw bytes. Throws on invalid input. */\nexport function decodeBase64Url(encoded: string): Uint8Array {\n const base64 = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = (4 - (base64.length % 4)) % 4;\n const binary = atob(base64 + \"=\".repeat(pad));\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n","import type { webcrypto } from \"node:crypto\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\n\nconst zeroIv = new Uint8Array(payloadByteLength);\nconst pkcsPad = 0x10;\n\nexport function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i]! ^ b[i]!;\n return diff === 0;\n}\n\nexport async function encryptPayload(\n key: webcrypto.CryptoKey,\n plaintext: Uint8Array,\n): Promise<Uint8Array> {\n const encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n plaintext as Uint8Array<ArrayBuffer>,\n ),\n );\n return encrypted.subarray(0, payloadByteLength);\n}\n\n// AES-CBC strip-and-reconstruct decrypt (ADR-0004). The wire carries only C1\n// (16 bytes); C2 = AES_K(P2 XOR C1) where P2 is the PKCS#7 pad block (0x10×16).\n// Recompute C2 via CBC encrypt of (P2 XOR C1) with IV=0, then decrypt C1‖C2.\nexport async function decryptPayload(\n key: webcrypto.CryptoKey,\n c1: Uint8Array,\n): Promise<Uint8Array> {\n const c2Input = new Uint8Array(payloadByteLength);\n for (let i = 0; i < payloadByteLength; i++) c2Input[i] = pkcsPad ^ c1[i]!;\n const c2Encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n c2Input as Uint8Array<ArrayBuffer>,\n ),\n );\n const ciphertext = new Uint8Array(payloadByteLength * 2);\n ciphertext.set(c1, 0);\n ciphertext.set(c2Encrypted.subarray(0, payloadByteLength), payloadByteLength);\n return new Uint8Array(\n await crypto.subtle.decrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n ciphertext as Uint8Array<ArrayBuffer>,\n ),\n );\n}\n\nexport { writeLen32 } from \"./bytes.js\";\n\n/**\n * @param info - codec-specific HKDF domain-separation label; see ADR-0019.\n */\nexport async function deriveKey(\n bytes: Uint8Array,\n info: Uint8Array,\n keySpec: webcrypto.AesDerivedKeyParams | webcrypto.HmacImportParams,\n keyUsages: webcrypto.KeyUsage[],\n): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n {\n name: \"HKDF\",\n hash: \"SHA-256\",\n salt: new Uint8Array(), // empty salt: IKM entropy floor is operator key size (128/192/256-bit); see ADR-0019\n info: info as Uint8Array<ArrayBuffer>,\n },\n base,\n keySpec,\n false,\n keyUsages,\n );\n}\n","import { decodeBase64Url, decodeHex, encodeBase64Url, encodeHex } from \"./bytes.js\";\nimport { IdsError } from \"../../error.js\";\n\ntype KeyMaterialFormat = \"hex\" | \"base64url\";\n\nconst validByteLengths = new Set([16, 24, 32]);\n\nfunction formatForError(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"[unprintable]\";\n }\n}\n\nfunction assertKeyMaterialFormat(\n format: unknown,\n noun: string,\n): asserts format is KeyMaterialFormat {\n if (format !== \"hex\" && format !== \"base64url\") {\n throw new IdsError(\n \"invalid_key_format\",\n `invalid ${noun} key format: expected hex or base64url, got '${formatForError(format)}'`,\n );\n }\n}\n\n/**\n * Throws `empty_keyring` when `keys` is empty.\n * `noun` appears in the message (e.g. `\"signing\"` → \"signing keyring must contain at least one key\").\n */\nfunction assertNonEmptyKeyring<K = unknown>(keys: readonly K[], noun: string): void {\n if (keys.length === 0) {\n throw new IdsError(\"empty_keyring\", `${noun} keyring must contain at least one key`);\n }\n}\n\n/**\n * Throws `duplicate_keyring_entry` when any two entries in `keys` compare equal.\n * Uses the caller-supplied constant-time `keysEqual` comparator.\n */\nfunction assertNoDuplicateKeyringEntries<K>(\n keys: readonly K[],\n keysEqual: (a: K, b: K) => boolean,\n noun: string,\n): void {\n for (let i = 0; i < keys.length; i++) {\n for (let j = i + 1; j < keys.length; j++) {\n if (keysEqual(keys[i]!, keys[j]!)) {\n throw new IdsError(\"duplicate_keyring_entry\", `duplicate ${noun} key in keyring`);\n }\n }\n }\n}\n\n/**\n * Asserts that `keys` is non-empty and contains no pairwise duplicates.\n *\n * Combines {@link assertNonEmptyKeyring} and {@link assertNoDuplicateKeyringEntries}\n * into a single call for codec constructors that validate a keyring at construction.\n *\n * @param keys - The keyring to validate.\n * @param keysEqual - Constant-time comparator (e.g. `wrappingKeysEqual`, `signingKeysEqual`).\n * @param noun - Noun used in error messages (e.g. `\"wrapping\"`, `\"signing\"`).\n */\nexport function assertValidKeyring<K>(\n keys: readonly K[],\n keysEqual: (a: K, b: K) => boolean,\n noun: string,\n): void {\n assertNonEmptyKeyring(keys, noun);\n assertNoDuplicateKeyringEntries(keys, keysEqual, noun);\n}\n\n/** Throws `invalid_key_length` when `byteLength` is not 16, 24, or 32. */\nexport function assertValidKeyMaterialByteLength(byteLength: number, noun: string): void {\n if (!validByteLengths.has(byteLength)) {\n throw new IdsError(\n \"invalid_key_length\",\n `invalid ${noun} key length: expected 16, 24, or 32 bytes, got ${byteLength}`,\n );\n }\n}\n\n/**\n * Encodes raw key bytes as hex or base64url.\n *\n * `formatNoun` appears in format error messages; `lengthNoun` in length error messages.\n * For most key types both are the same (e.g. `\"wrapping\"`, `\"signing\"`). For the\n * Opaque key, they differ (`\"opaque\"` and `\"AES\"` respectively) to preserve the\n * original human-readable messages.\n */\nexport function encodeKeyMaterial(\n bytes: Uint8Array,\n format: KeyMaterialFormat,\n formatNoun: string,\n lengthNoun: string,\n): string {\n assertKeyMaterialFormat(format, formatNoun);\n assertValidKeyMaterialByteLength(bytes.length, lengthNoun);\n if (format === \"hex\") return encodeHex(bytes);\n return encodeBase64Url(bytes);\n}\n\n/**\n * Decodes a hex or base64url-encoded key string back to raw bytes.\n *\n * `formatNoun` appears in format error messages; `lengthNoun` in length error messages.\n */\nexport function decodeKeyMaterial(\n encoded: string,\n format: KeyMaterialFormat,\n formatNoun: string,\n lengthNoun: string,\n): Uint8Array {\n assertKeyMaterialFormat(format, formatNoun);\n let bytes: Uint8Array;\n if (format === \"hex\") {\n if (encoded.length === 0 || encoded.length % 2 !== 0) {\n throw new IdsError(\n \"invalid_key_encoding\",\n \"invalid hex key: length must be a positive even number of characters\",\n );\n }\n if (!/^[0-9a-fA-F]+$/.test(encoded)) {\n throw new IdsError(\"invalid_key_encoding\", \"invalid hex key: expected [0-9a-fA-F] only\");\n }\n bytes = decodeHex(encoded);\n } else {\n try {\n bytes = decodeBase64Url(encoded);\n } catch {\n throw new IdsError(\"invalid_key_encoding\", \"invalid base64url key\");\n }\n }\n assertValidKeyMaterialByteLength(bytes.length, lengthNoun);\n return bytes;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"key-material-1wOKJ1o-.mjs","names":[],"sources":["../src/codecs/_kernel/bytes.ts","../src/codecs/_kernel/crypto.ts","../src/codecs/_kernel/key-material.ts"],"sourcesContent":["const hexDigits = \"0123456789abcdef\";\n\nconst invalidNibble = 0xff;\nconst hexCharCodeToNibble = new Uint8Array(128).fill(invalidNibble);\nfor (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;\nfor (let i = 0; i < 6; i++) {\n hexCharCodeToNibble[97 + i] = 10 + i;\n hexCharCodeToNibble[65 + i] = 10 + i;\n}\n\n/** Lowercase hex encoding of raw bytes. */\nexport function encodeHex(bytes: Uint8Array): string {\n // oxlint-disable-next-line no-new-array\n const codes = new Array<number>(bytes.length * 2);\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n codes[i * 2] = hexDigits.charCodeAt(b >>> 4);\n codes[i * 2 + 1] = hexDigits.charCodeAt(b & 0x0f);\n }\n return String.fromCharCode(...codes);\n}\n\n/** Decodes a hex string to raw bytes. Throws on non-hex input. */\nexport function decodeHex(encoded: string): Uint8Array {\n if (encoded.length % 2 !== 0) throw new Error(\"invalid hex\");\n const out = new Uint8Array(encoded.length / 2);\n for (let i = 0; i < out.length; i++) {\n const hiCode = encoded.charCodeAt(i * 2);\n const loCode = encoded.charCodeAt(i * 2 + 1);\n if (hiCode >= hexCharCodeToNibble.length || loCode >= hexCharCodeToNibble.length) {\n throw new Error(\"invalid hex\");\n }\n const hi = hexCharCodeToNibble[hiCode]!;\n const lo = hexCharCodeToNibble[loCode]!;\n if (hi === invalidNibble || lo === invalidNibble) {\n throw new Error(\"invalid hex\");\n }\n out[i] = (hi << 4) | lo;\n }\n return out;\n}\n\n/** Base64url encoding without padding. */\nexport function encodeBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Writes a 32-bit integer as four big-endian bytes into target[offset..offset+3]. */\nexport function writeLen32(value: number, target: Uint8Array, offset: number): void {\n target[offset] = (value >>> 24) & 0xff;\n target[offset + 1] = (value >>> 16) & 0xff;\n target[offset + 2] = (value >>> 8) & 0xff;\n target[offset + 3] = value & 0xff;\n}\n\n/** Decodes a base64url string to raw bytes. Throws on invalid input. */\nexport function decodeBase64Url(encoded: string): Uint8Array {\n const base64 = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = (4 - (base64.length % 4)) % 4;\n const binary = atob(base64 + \"=\".repeat(pad));\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n","import type { webcrypto } from \"node:crypto\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\n\nconst zeroIv = new Uint8Array(payloadByteLength);\nconst pkcsPad = 0x10;\n\nexport function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i]! ^ b[i]!;\n return diff === 0;\n}\n\nexport async function encryptPayload(\n key: webcrypto.CryptoKey,\n plaintext: Uint8Array,\n): Promise<Uint8Array> {\n const encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n plaintext as Uint8Array<ArrayBuffer>,\n ),\n );\n return encrypted.subarray(0, payloadByteLength);\n}\n\n// AES-CBC strip-and-reconstruct decrypt (ADR-0004). The wire carries only C1\n// (16 bytes); C2 = AES_K(P2 XOR C1) where P2 is the PKCS#7 pad block (0x10×16).\n// Recompute C2 via CBC encrypt of (P2 XOR C1) with IV=0, then decrypt C1‖C2.\nexport async function decryptPayload(\n key: webcrypto.CryptoKey,\n c1: Uint8Array,\n): Promise<Uint8Array> {\n const c2Input = new Uint8Array(payloadByteLength);\n for (let i = 0; i < payloadByteLength; i++) c2Input[i] = pkcsPad ^ c1[i]!;\n const c2Encrypted = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n c2Input as Uint8Array<ArrayBuffer>,\n ),\n );\n const ciphertext = new Uint8Array(payloadByteLength * 2);\n ciphertext.set(c1, 0);\n ciphertext.set(c2Encrypted.subarray(0, payloadByteLength), payloadByteLength);\n return new Uint8Array(\n await crypto.subtle.decrypt(\n { name: \"AES-CBC\", iv: zeroIv },\n key,\n ciphertext as Uint8Array<ArrayBuffer>,\n ),\n );\n}\n\nexport { writeLen32 } from \"./bytes.js\";\n\n/**\n * @param info - codec-specific HKDF domain-separation label; see ADR-0019.\n */\nexport async function deriveKey(\n bytes: Uint8Array,\n info: Uint8Array,\n keySpec: webcrypto.AesDerivedKeyParams | webcrypto.HmacImportParams,\n keyUsages: webcrypto.KeyUsage[],\n): Promise<webcrypto.CryptoKey> {\n const base = await crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"HKDF\",\n false,\n [\"deriveKey\"],\n );\n return crypto.subtle.deriveKey(\n {\n name: \"HKDF\",\n hash: \"SHA-256\",\n salt: new Uint8Array(), // empty salt: IKM entropy floor is operator key size (128/192/256-bit); see ADR-0019\n info: info as Uint8Array<ArrayBuffer>,\n },\n base,\n keySpec,\n false,\n keyUsages,\n );\n}\n","import { decodeBase64Url, decodeHex, encodeBase64Url, encodeHex } from \"./bytes.js\";\nimport { IdsError } from \"../../error.js\";\n\ntype KeyMaterialFormat = \"hex\" | \"base64url\";\n\nconst validByteLengths = new Set([16, 24, 32]);\n\nfunction formatForError(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"[unprintable]\";\n }\n}\n\nfunction assertKeyMaterialFormat(\n format: unknown,\n noun: string,\n): asserts format is KeyMaterialFormat {\n if (format !== \"hex\" && format !== \"base64url\") {\n throw new IdsError(\n \"invalid_key_format\",\n `invalid ${noun} key format: expected hex or base64url, got '${formatForError(format)}'`,\n );\n }\n}\n\n/**\n * Throws `empty_keyring` when `keys` is empty.\n * `noun` appears in the message (e.g. `\"signing\"` → \"signing keyring must contain at least one key\").\n */\nfunction assertNonEmptyKeyring<K = unknown>(keys: readonly K[], noun: string): void {\n if (keys.length === 0) {\n throw new IdsError(\"empty_keyring\", `${noun} keyring must contain at least one key`);\n }\n}\n\n/**\n * Throws `duplicate_keyring_entry` when any two entries in `keys` compare equal.\n * Uses the caller-supplied constant-time `keysEqual` comparator.\n */\nfunction assertNoDuplicateKeyringEntries<K>(\n keys: readonly K[],\n keysEqual: (a: K, b: K) => boolean,\n noun: string,\n): void {\n for (let i = 0; i < keys.length; i++) {\n for (let j = i + 1; j < keys.length; j++) {\n if (keysEqual(keys[i]!, keys[j]!)) {\n throw new IdsError(\"duplicate_keyring_entry\", `duplicate ${noun} key in keyring`);\n }\n }\n }\n}\n\n/**\n * Asserts that `keys` is non-empty and contains no pairwise duplicates.\n *\n * Combines {@link assertNonEmptyKeyring} and {@link assertNoDuplicateKeyringEntries}\n * into a single call for codec constructors that validate a keyring at construction.\n *\n * @param keys - The keyring to validate.\n * @param keysEqual - Constant-time comparator (e.g. `wrappingKeysEqual`, `signingKeysEqual`).\n * @param noun - Noun used in error messages (e.g. `\"wrapping\"`, `\"signing\"`).\n */\nexport function assertValidKeyring<K>(\n keys: readonly K[],\n keysEqual: (a: K, b: K) => boolean,\n noun: string,\n): void {\n assertNonEmptyKeyring(keys, noun);\n assertNoDuplicateKeyringEntries(keys, keysEqual, noun);\n}\n\n/** Throws `invalid_key_length` when `byteLength` is not 16, 24, or 32. */\nexport function assertValidKeyMaterialByteLength(byteLength: number, noun: string): void {\n if (!validByteLengths.has(byteLength)) {\n throw new IdsError(\n \"invalid_key_length\",\n `invalid ${noun} key length: expected 16, 24, or 32 bytes, got ${byteLength}`,\n );\n }\n}\n\n/**\n * Encodes raw key bytes as hex or base64url.\n *\n * `formatNoun` appears in format error messages; `lengthNoun` in length error messages.\n * For most key types both are the same (e.g. `\"wrapping\"`, `\"signing\"`). For the\n * Opaque key, they differ (`\"opaque\"` and `\"AES\"` respectively) to preserve the\n * original human-readable messages.\n */\nexport function encodeKeyMaterial(\n bytes: Uint8Array,\n format: KeyMaterialFormat,\n formatNoun: string,\n lengthNoun: string,\n): string {\n assertKeyMaterialFormat(format, formatNoun);\n assertValidKeyMaterialByteLength(bytes.length, lengthNoun);\n if (format === \"hex\") return encodeHex(bytes);\n return encodeBase64Url(bytes);\n}\n\n/**\n * Decodes a hex or base64url-encoded key string back to raw bytes.\n *\n * `formatNoun` appears in format error messages; `lengthNoun` in length error messages.\n */\nexport function decodeKeyMaterial(\n encoded: string,\n format: KeyMaterialFormat,\n formatNoun: string,\n lengthNoun: string,\n): Uint8Array {\n assertKeyMaterialFormat(format, formatNoun);\n let bytes: Uint8Array;\n if (format === \"hex\") {\n if (encoded.length === 0 || encoded.length % 2 !== 0) {\n throw new IdsError(\n \"invalid_key_encoding\",\n \"invalid hex key: length must be a positive even number of characters\",\n );\n }\n if (!/^[0-9a-fA-F]+$/.test(encoded)) {\n throw new IdsError(\"invalid_key_encoding\", \"invalid hex key: expected [0-9a-fA-F] only\");\n }\n bytes = decodeHex(encoded);\n } else {\n try {\n bytes = decodeBase64Url(encoded);\n } catch {\n throw new IdsError(\"invalid_key_encoding\", \"invalid base64url key\");\n }\n }\n assertValidKeyMaterialByteLength(bytes.length, lengthNoun);\n return bytes;\n}\n"],"mappings":"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"}
package/dist/kysely.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdColumnCodec } from "./adapter-types-CIc-4O-P.mjs";
3
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdColumnCodec } from "./adapter-types-Bia_w9sg.mjs";
3
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
4
4
  import { ColumnType } from "kysely";
5
5
 
6
6
  //#region src/adapters/kysely.d.ts
@@ -1,6 +1,6 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdColumnCodec } from "./adapter-types-CIc-4O-P.mjs";
3
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdColumnCodec } from "./adapter-types-Bia_w9sg.mjs";
3
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
4
4
  import { Type } from "@mikro-orm/core";
5
5
 
6
6
  //#region src/adapters/mikro-orm.d.ts
package/dist/nestjs.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { r as IdParamFailure, t as IdCodec } from "./adapter-types-CIc-4O-P.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { r as IdParamFailure, t as IdCodec } from "./adapter-types-Bia_w9sg.mjs";
3
3
  import { ArgumentMetadata, PipeTransform } from "@nestjs/common";
4
4
 
5
5
  //#region src/adapters/nestjs.d.ts
@@ -1,6 +1,6 @@
1
- import { a as toWireId, i as payloadBytesFromId, n as registerBrand, r as payloadBase32Length, s as validateBrand, t as wireMethods } from "./codec-shell-C2NKQEx2.mjs";
2
- import { a as writeTimestamp, r as readTimestampMs, t as defaultRng } from "./rng-BHFxX1Fc.mjs";
3
- import { a as decryptPayload, i as encodeKeyMaterial, r as decodeKeyMaterial, s as encryptPayload, t as assertValidKeyMaterialByteLength } from "./key-material-DvjACe89.mjs";
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
+ 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";
4
4
  //#region src/codecs/opaque/layout.ts
5
5
  function buildPlaintext(ms, rng) {
6
6
  const plaintext = /* @__PURE__ */ new Uint8Array(16);
@@ -41,6 +41,7 @@ const opaqueKeyInternals = /* @__PURE__ */ new WeakMap();
41
41
  * {@link decodeOpaqueKey} (`"hex"` or `"base64url"` — not Crockford base32).
42
42
  *
43
43
  * @param bytes - 16, 24, or 32 raw key bytes.
44
+ * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
44
45
  */
45
46
  async function importOpaqueKey(bytes) {
46
47
  assertValidKeyMaterialByteLength(bytes.length, "AES");
@@ -59,6 +60,8 @@ function getOpaqueKeyCryptoKey(key) {
59
60
  *
60
61
  * @param bytes - 16, 24, or 32 raw key bytes (AES-128/192/256).
61
62
  * @param format - `hex` (lowercase) or `base64url`.
63
+ * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
64
+ * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
62
65
  */
63
66
  function encodeOpaqueKey(bytes, format) {
64
67
  return encodeKeyMaterial(bytes, format, "opaque", "AES");
@@ -68,6 +71,9 @@ function encodeOpaqueKey(bytes, format) {
68
71
  *
69
72
  * @param encoded - Hex or base64url string.
70
73
  * @param format - Must match how the string was encoded.
74
+ * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
75
+ * @throws {IdsError} `invalid_key_encoding` if the string is malformed for its format.
76
+ * @throws {IdsError} `invalid_key_length` if the decoded bytes are not 16, 24, or 32 bytes.
71
77
  */
72
78
  function decodeOpaqueKey(encoded, format) {
73
79
  return decodeKeyMaterial(encoded, format, "opaque", "AES");
@@ -98,10 +104,13 @@ function createOpaqueTimestampId(brand, opts) {
98
104
  safeParse: wire.safeParse,
99
105
  extractTimestamp: layout.extractTimestamp,
100
106
  toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),
101
- "~standard": wire["~standard"]
107
+ "~standard": wire["~standard"],
108
+ toUUID: wire.toUUID,
109
+ fromUUID: wire.fromUUID,
110
+ safeFromUUID: wire.safeFromUUID
102
111
  };
103
112
  }
104
113
  //#endregion
105
114
  export { importOpaqueKey as i, decodeOpaqueKey as n, encodeOpaqueKey as r, createOpaqueTimestampId as t };
106
115
 
107
- //# sourceMappingURL=opaque-BW3Uzeeb.mjs.map
116
+ //# sourceMappingURL=opaque-COAcIIY4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opaque-COAcIIY4.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 {\n assertValidKeyMaterialByteLength,\n decodeKeyMaterial,\n encodeKeyMaterial,\n} from \"../_kernel/key-material.js\";\n\n/** Wire encoding for opaque AES key material (not Crockford base32). */\nexport type OpaqueKeyFormat = \"hex\" | \"base64url\";\n\ndeclare const opaqueKeyBrand: unique symbol;\n\n/**\n * Opaque imported handle for one AES key used by the Opaque Timestamp codec.\n *\n * Holds the underlying `webcrypto.CryptoKey` internally; callers never access it directly.\n * Obtain handles via {@link importOpaqueKey} and pass them to\n * `createOpaqueTimestampId` as the `key` option.\n *\n * Distinct from the `WrappingKey` used by `@smonn/ids/wrapped` — one raw\n * secret must not silently serve both codecs without an explicit import.\n */\nexport type OpaqueKey = {\n readonly [opaqueKeyBrand]: \"OpaqueKey\";\n};\n\nconst opaqueKeyInternals = new WeakMap<OpaqueKey, webcrypto.CryptoKey>();\n\n/**\n * Imports raw AES key bytes into an {@link OpaqueKey} handle for the Opaque\n * Timestamp codec.\n *\n * Accepts 16, 24, or 32 bytes (AES-128 / AES-192 / AES-256 strength).\n * To store or transport key material, use {@link encodeOpaqueKey} /\n * {@link decodeOpaqueKey} (`\"hex\"` or `\"base64url\"` — not Crockford base32).\n *\n * @param bytes - 16, 24, or 32 raw key bytes.\n * @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 crypto.subtle.importKey(\n \"raw\",\n bytes as Uint8Array<ArrayBuffer>,\n \"AES-CBC\",\n false,\n [\"encrypt\", \"decrypt\"],\n );\n const key = Object.freeze({}) as OpaqueKey;\n opaqueKeyInternals.set(key, cryptoKey);\n return key;\n}\n\nexport function getOpaqueKeyCryptoKey(key: OpaqueKey): webcrypto.CryptoKey {\n const cryptoKey = opaqueKeyInternals.get(key);\n if (cryptoKey === undefined) {\n throw new Error(\"invalid opaque key\");\n }\n return cryptoKey;\n}\n\n/**\n * Encodes raw AES key bytes for storage in env vars or secret managers.\n *\n * @param bytes - 16, 24, or 32 raw key bytes (AES-128/192/256).\n * @param format - `hex` (lowercase) or `base64url`.\n * @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;;;ACjCA,MAAM,qCAAqB,IAAI,QAAwC;;;;;;;;;;;;AAavE,eAAsB,gBAAgB,OAAuC;CAC3E,iCAAiC,MAAM,QAAQ,KAAK;CACpD,MAAM,YAAY,MAAM,OAAO,OAAO,UACpC,OACA,OACA,WACA,OACA,CAAC,WAAW,SAAS,CACvB;CACA,MAAM,MAAM,OAAO,OAAO,CAAC,CAAC;CAC5B,mBAAmB,IAAI,KAAK,SAAS;CACrC,OAAO;AACT;AAEA,SAAgB,sBAAsB,KAAqC;CACzE,MAAM,YAAY,mBAAmB,IAAI,GAAG;CAC5C,IAAI,cAAc,KAAA,GAChB,MAAM,IAAI,MAAM,oBAAoB;CAEtC,OAAO;AACT;;;;;;;;;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;;;;;;;;;;AC4CA,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
@@ -1,5 +1,5 @@
1
- import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
3
3
 
4
4
  //#region src/codecs/opaque/key.d.ts
5
5
  /** Wire encoding for opaque AES key material (not Crockford base32). */
@@ -27,6 +27,7 @@ type OpaqueKey = {
27
27
  * {@link decodeOpaqueKey} (`"hex"` or `"base64url"` — not Crockford base32).
28
28
  *
29
29
  * @param bytes - 16, 24, or 32 raw key bytes.
30
+ * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
30
31
  */
31
32
  declare function importOpaqueKey(bytes: Uint8Array): Promise<OpaqueKey>;
32
33
  /**
@@ -34,6 +35,8 @@ declare function importOpaqueKey(bytes: Uint8Array): Promise<OpaqueKey>;
34
35
  *
35
36
  * @param bytes - 16, 24, or 32 raw key bytes (AES-128/192/256).
36
37
  * @param format - `hex` (lowercase) or `base64url`.
38
+ * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
39
+ * @throws {IdsError} `invalid_key_length` if `bytes.length` is not 16, 24, or 32.
37
40
  */
38
41
  declare function encodeOpaqueKey(bytes: Uint8Array, format: OpaqueKeyFormat): string;
39
42
  /**
@@ -41,6 +44,9 @@ declare function encodeOpaqueKey(bytes: Uint8Array, format: OpaqueKeyFormat): st
41
44
  *
42
45
  * @param encoded - Hex or base64url string.
43
46
  * @param format - Must match how the string was encoded.
47
+ * @throws {IdsError} `invalid_key_format` if `format` is not `"hex"` or `"base64url"`.
48
+ * @throws {IdsError} `invalid_key_encoding` if the string is malformed for its format.
49
+ * @throws {IdsError} `invalid_key_length` if the decoded bytes are not 16, 24, or 32 bytes.
44
50
  */
45
51
  declare function decodeOpaqueKey(encoded: string, format: OpaqueKeyFormat): Uint8Array;
46
52
  //#endregion
@@ -114,6 +120,24 @@ type OpaqueTimestampCodec<Brand extends string> = {
114
120
  */
115
121
  toJsonSchema(): JsonSchema; /** Standard Schema validate entry point. */
116
122
  readonly "~standard": StandardSchemaProps<Brand>;
123
+ /**
124
+ * Converts a trusted `Id<Brand>` to an RFC 9562 canonical (lowercase, hyphenated)
125
+ * UUID string by reinterpreting the 16-byte payload verbatim. The payload is the
126
+ * encrypted ciphertext — `toUUID` does not decrypt it. Total — cannot fail.
127
+ * Returns a plain `string` (brand is shed). See ADR-0024.
128
+ */
129
+ toUUID(id: Id<Brand>): string;
130
+ /**
131
+ * Parses a UUID string into an `Id<Brand>`. Accepts case-insensitive `8-4-4-4-12`
132
+ * hyphenated form only. Throws `IdsError` with `code: "invalid_id"` on bad input.
133
+ * See ADR-0024.
134
+ */
135
+ fromUUID(value: string): Id<Brand>;
136
+ /**
137
+ * Non-throwing UUID parse. Returns `{ ok: true, id }` or
138
+ * `{ ok: false, error: "not_string" | "invalid_uuid" }`. See ADR-0024.
139
+ */
140
+ safeFromUUID(value: unknown): ParseResult<Brand>;
117
141
  };
118
142
  /**
119
143
  * Creates an Opaque Timestamp codec for `brand` (three lowercase a–z characters).
@@ -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;AAeZ;;;;;;;;iBAAsB,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,SAAA;;;;AAAA;AA4BlE;;iBAAgB,eAAA,CAAgB,KAAA,EAAO,UAAA,EAAY,MAAA,EAAQ,eAAA;;;;;;;iBAU3C,eAAA,CAAgB,OAAA,UAAiB,MAAA,EAAQ,eAAA,GAAkB,UAAA;;;;;;KChD/D,sBAAA;EDpB0B;;;;AAExB;AAYd;;;ECeE,GAAA,EAAK,SAAA,EDdK;ECgBV,GAAA,iBDDoB;ECGpB,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;;;;;;;;ADLgE;AA4BlE;;;;;;;;;AAA2D;AAU3D;;KCVY,oBAAA;EDU+D,+ECRzE,QAAA,IAAY,OAAA,CAAQ,EAAA,CAAG,KAAA;EAEvB,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,OAAA,CAAQ,EAAA,CAAG,KAAA;;;ADMsC;;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;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":";;;;;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"}
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-BW3Uzeeb.mjs";
2
+ import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-COAcIIY4.mjs";
3
3
  export { IdsError, createOpaqueTimestampId, decodeOpaqueKey, encodeOpaqueKey, importOpaqueKey, isIdsError };
package/dist/prisma.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdColumnCodec } from "./adapter-types-CIc-4O-P.mjs";
3
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdColumnCodec } from "./adapter-types-Bia_w9sg.mjs";
3
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
4
4
 
5
5
  //#region src/adapters/prisma.d.ts
6
6
  /**
@@ -1,5 +1,5 @@
1
- import { a as toWireId, i as payloadBytesFromId, n as registerBrand, s as validateBrand, t as wireMethods } from "./codec-shell-C2NKQEx2.mjs";
2
- import { a as writeTimestamp, n as fastTenByteRng } from "./rng-BHFxX1Fc.mjs";
1
+ import { a as toWireId, i as payloadBytesFromId, n as registerBrand, s as validateBrand, t as wireMethods } from "./codec-shell-BRZkuQeP.mjs";
2
+ import { a as writeTimestamp, n as fastTenByteRng } from "./rng-6GyNT4zS.mjs";
3
3
  //#region src/codecs/reverse/layout.ts
4
4
  const randomByteLength = 10;
5
5
  /** Writes inverted timestamp bytes, then fills random portion. */
@@ -82,10 +82,13 @@ function createReverseTimestampId(brand, opts = {}) {
82
82
  minIdForTime: (date) => layout.minIdForTime(date.getTime()),
83
83
  maxIdForTime: (date) => layout.maxIdForTime(date.getTime()),
84
84
  toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),
85
- "~standard": wire["~standard"]
85
+ "~standard": wire["~standard"],
86
+ toUUID: wire.toUUID,
87
+ fromUUID: wire.fromUUID,
88
+ safeFromUUID: wire.safeFromUUID
86
89
  };
87
90
  }
88
91
  //#endregion
89
92
  export { createReverseTimestampId as t };
90
93
 
91
- //# sourceMappingURL=reverse-BW8g_cln.mjs.map
94
+ //# sourceMappingURL=reverse-CT-El3hi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"reverse-BW8g_cln.mjs","names":[],"sources":["../src/codecs/reverse/layout.ts","../src/codecs/reverse/index.ts"],"sourcesContent":["import type { Id, LayoutOps, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\nimport { timestampByteLength, writeTimestamp } from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength: number = payloadByteLength - timestampByteLength;\n\n/** Writes inverted timestamp bytes, then fills random portion. */\nfunction buildReversePayload(\n ms: number,\n rng: (target: Uint8Array) => void,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n rng(randomView);\n}\n\n/** Writes inverted timestamp bytes, then fills random portion with a sentinel. */\nfunction buildReverseSentinelPayload(\n ms: number,\n fill: number,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n randomView.fill(fill);\n}\n\n/** Decodes the original timestamp by inverting the first 6 payload bytes. */\nfunction extractReverseTimestampFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n id: Id<Brand>,\n): Date {\n const bytes = payloadBytesFromId(prefix, id);\n let ms = 0;\n for (let i = 0; i < timestampByteLength; i++) {\n ms = ms * 256 + (~bytes[i]! & 0xff);\n }\n return new Date(ms);\n}\n\n/** Layout ops binder for the Reverse Timestamp variant. */\nexport function createReverseTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n rng: (target: Uint8Array) => void,\n): LayoutOps<Brand> & {\n generateAt(ms: number): Id<Brand>;\n extractTimestamp(id: Id<Brand>): Date;\n minIdForTime(ms: number): Id<Brand>;\n maxIdForTime(ms: number): Id<Brand>;\n} {\n const buffer = new Uint8Array(payloadByteLength);\n const randomView = new Uint8Array(buffer.buffer, timestampByteLength, randomByteLength);\n\n return {\n generateAt: (ms: number): Id<Brand> => {\n buildReversePayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n extractTimestamp: (id: Id<Brand>): Date => extractReverseTimestampFromId(prefix, id),\n minIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0x00, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0xff, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n exampleWireId: (ms?: number): Id<Brand> => {\n buildReversePayload(ms ?? Date.now(), rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n };\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { createReverseTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { fastTenByteRng } 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\";\n\n/**\n * Configuration options for a Reverse Timestamp codec instance.\n */\nexport type ReverseTimestampOptions = {\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes the 10-byte random tail into `target`. Defaults to a `crypto.randomUUID` harvest fast path (same as the Timestamp codec). */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * A brand-scoped codec for generating and validating Reverse Timestamp IDs.\n *\n * Wire format: `{brand}_` plus 26 lowercase Crockford base32 characters encoding a\n * 16-byte payload (6-byte bitwise-inverted ms timestamp + 10 random bytes). IDs sort\n * by creation time in **descending** (newest-first) order.\n *\n * Range queries across a time interval [t_old, t_new] should scan from\n * `minIdForTime(t_new)` to `maxIdForTime(t_old)` — the reversed sort order means\n * newer timestamps produce lexicographically smaller IDs.\n *\n * Constructed via `createReverseTimestampId(brand)` from `@smonn/ids/reverse`.\n */\nexport type ReverseTimestampCodec<Brand extends string> = {\n /** Produces a new canonical ID using the codec's `now` and `rng`. */\n generate(): Id<Brand>;\n /** Produces a new canonical ID with timestamp bytes from `date` and a fresh random tail. Throws on invalid dates. */\n generateAt(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>` by inverting the timestamp bytes.\n * Trusts the type — use `safeParse()` at boundaries first.\n *\n * Best-effort: inverts and decodes the timestamp bytes in the payload without any\n * additional verification. An ID that bypassed `safeParse()` (e.g. via a type\n * assertion) may return a plausible-looking but incorrect `Date`.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /**\n * Lexicographically smallest ID for any ID generated at `date` (random portion `0x00`).\n * Because timestamps are inverted, a newer `date` yields a lexicographically smaller result —\n * use `minIdForTime(t_new)` as the lower bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n minIdForTime(date: Date): Id<Brand>;\n /**\n * Lexicographically largest ID for any ID generated at `date` (random portion `0xff`).\n * Because timestamps are inverted, an older `date` yields a lexicographically larger result —\n * use `maxIdForTime(t_old)` as the upper bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n maxIdForTime(date: Date): Id<Brand>;\n /**\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 */\n toJsonSchema(): JsonSchema;\n /** Standard Schema validate entry point. */\n readonly \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/**\n * Creates a Reverse Timestamp codec for `brand` (three lowercase a–z characters).\n *\n * IDs sort newest-first: the 48-bit timestamp field is bitwise-inverted before encoding,\n * so lexicographic ID order equals descending creation-time order. `extractTimestamp`\n * inverts back to recover the original millisecond.\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n * @example\n * ```ts\n * const posts = createReverseTimestampId(\"pst\");\n *\n * const id = posts.generate(); // Id<\"pst\"> (newest-first sort)\n * posts.extractTimestamp(id); // Date\n * ```\n */\nexport function createReverseTimestampId<Brand extends string>(\n brand: Brand & ValidBrand<Brand>,\n opts: ReverseTimestampOptions = {},\n): ReverseTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? fastTenByteRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createReverseTimestampLayoutOps(prefix, rng);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n };\n}\n"],"mappings":";;;AAKA,MAAM,mBAAA;;AAGN,SAAS,oBACP,IACA,KACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,IAAI,UAAU;AAChB;;AAGA,SAAS,4BACP,IACA,MACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,WAAW,KAAK,IAAI;AACtB;;AAGA,SAAS,8BACP,QACA,IACM;CACN,MAAM,QAAQ,mBAAmB,QAAQ,EAAE;CAC3C,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,KAAK,KAAK,OAAO,CAAC,MAAM,KAAM;CAEhC,OAAO,IAAI,KAAK,EAAE;AACpB;;AAGA,SAAgB,gCACd,QACA,KAMA;CACA,MAAM,yBAAS,IAAI,WAAA,EAA4B;CAC/C,MAAM,aAAa,IAAI,WAAW,OAAO,QAAA,GAA6B,gBAAgB;CAEtF,OAAO;EACL,aAAa,OAA0B;GACrC,oBAAoB,IAAI,KAAK,QAAQ,UAAU;GAC/C,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,mBAAmB,OAAwB,8BAA8B,QAAQ,EAAE;EACnF,eAAe,OAA0B;GACvC,4BAA4B,IAAI,GAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,eAAe,OAA0B;GACvC,4BAA4B,IAAI,KAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,gBAAgB,OAA2B;GACzC,oBAAoB,MAAM,KAAK,IAAI,GAAG,KAAK,QAAQ,UAAU;GAC7D,OAAO,SAAS,QAAQ,MAAM;EAChC;CACF;AACF;;;;;;;;;;;;;;;;;;;;AC8BA,SAAgB,yBACd,OACA,OAAgC,CAAC,GACH;CAC9B,cAAc,KAAK;CACnB,cAAc,OAAO,KAAK,mBAAmB;CAE7C,MAAM,MAAM,KAAK,OAAO,KAAK;CAC7B,MAAM,MAAM,KAAK,OAAO;CACxB,MAAM,SAAwB,GAAG,MAAM;CACvC,MAAM,OAAO,YAAY,MAAM;CAC/B,MAAM,SAAS,gCAAgC,QAAQ,GAAG;CAE1D,OAAO;EACL,gBAAgB,OAAO,WAAW,IAAI,CAAC;EACvC,aAAa,SAAe,OAAO,WAAW,KAAK,QAAQ,CAAC;EAC5D,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,kBAAkB,OAAO;EACzB,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,oBAAoB,KAAK,aAAa,OAAO,OAAO,cAAc,CAAC;EACnE,aAAa,KAAK;CACpB;AACF"}
1
+ {"version":3,"file":"reverse-CT-El3hi.mjs","names":[],"sources":["../src/codecs/reverse/layout.ts","../src/codecs/reverse/index.ts"],"sourcesContent":["import type { Id, LayoutOps, Prefix } from \"../../types.js\";\nimport { payloadBytesFromId, toWireId } from \"../../wire/envelope.js\";\nimport { payloadByteLength } from \"../../wire/invariants.js\";\nimport { timestampByteLength, writeTimestamp } from \"../../wire/timestamp-bytes.js\";\n\nconst randomByteLength: number = payloadByteLength - timestampByteLength;\n\n/** Writes inverted timestamp bytes, then fills random portion. */\nfunction buildReversePayload(\n ms: number,\n rng: (target: Uint8Array) => void,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n rng(randomView);\n}\n\n/** Writes inverted timestamp bytes, then fills random portion with a sentinel. */\nfunction buildReverseSentinelPayload(\n ms: number,\n fill: number,\n buffer: Uint8Array,\n randomView: Uint8Array,\n): void {\n writeTimestamp(ms, buffer);\n for (let i = 0; i < timestampByteLength; i++) {\n buffer[i] = ~buffer[i]! & 0xff;\n }\n randomView.fill(fill);\n}\n\n/** Decodes the original timestamp by inverting the first 6 payload bytes. */\nfunction extractReverseTimestampFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n id: Id<Brand>,\n): Date {\n const bytes = payloadBytesFromId(prefix, id);\n let ms = 0;\n for (let i = 0; i < timestampByteLength; i++) {\n ms = ms * 256 + (~bytes[i]! & 0xff);\n }\n return new Date(ms);\n}\n\n/** Layout ops binder for the Reverse Timestamp variant. */\nexport function createReverseTimestampLayoutOps<Brand extends string>(\n prefix: Prefix<Brand>,\n rng: (target: Uint8Array) => void,\n): LayoutOps<Brand> & {\n generateAt(ms: number): Id<Brand>;\n extractTimestamp(id: Id<Brand>): Date;\n minIdForTime(ms: number): Id<Brand>;\n maxIdForTime(ms: number): Id<Brand>;\n} {\n const buffer = new Uint8Array(payloadByteLength);\n const randomView = new Uint8Array(buffer.buffer, timestampByteLength, randomByteLength);\n\n return {\n generateAt: (ms: number): Id<Brand> => {\n buildReversePayload(ms, rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n extractTimestamp: (id: Id<Brand>): Date => extractReverseTimestampFromId(prefix, id),\n minIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0x00, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n maxIdForTime: (ms: number): Id<Brand> => {\n buildReverseSentinelPayload(ms, 0xff, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n exampleWireId: (ms?: number): Id<Brand> => {\n buildReversePayload(ms ?? Date.now(), rng, buffer, randomView);\n return toWireId(prefix, buffer);\n },\n };\n}\n","import { validateBrand } from \"../_kernel/brand.js\";\nimport { createReverseTimestampLayoutOps } from \"./layout.js\";\nimport { registerBrand } from \"../_kernel/registry.js\";\nimport { fastTenByteRng } 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\";\n\n/**\n * Configuration options for a Reverse Timestamp codec instance.\n */\nexport type ReverseTimestampOptions = {\n /** Returns the current timestamp in milliseconds. Defaults to `Date.now`. */\n now?: () => number;\n /** Writes the 10-byte random tail into `target`. Defaults to a `crypto.randomUUID` harvest fast path (same as the Timestamp codec). */\n rng?: (target: Uint8Array) => void;\n /** If true, silences the duplicate-brand warning in non-production environments. */\n allowDuplicateBrand?: boolean;\n};\n\n/**\n * A brand-scoped codec for generating and validating Reverse Timestamp IDs.\n *\n * Wire format: `{brand}_` plus 26 lowercase Crockford base32 characters encoding a\n * 16-byte payload (6-byte bitwise-inverted ms timestamp + 10 random bytes). IDs sort\n * by creation time in **descending** (newest-first) order.\n *\n * Range queries across a time interval [t_old, t_new] should scan from\n * `minIdForTime(t_new)` to `maxIdForTime(t_old)` — the reversed sort order means\n * newer timestamps produce lexicographically smaller IDs.\n *\n * Constructed via `createReverseTimestampId(brand)` from `@smonn/ids/reverse`.\n */\nexport type ReverseTimestampCodec<Brand extends string> = {\n /** Produces a new canonical ID using the codec's `now` and `rng`. */\n generate(): Id<Brand>;\n /** Produces a new canonical ID with timestamp bytes from `date` and a fresh random tail. Throws on invalid dates. */\n generateAt(date: Date): Id<Brand>;\n /**\n * Strict type guard: `true` only for already-canonical strings for this brand.\n * For untrusted input, use `safeParse()` or `parse()` instead. See ADR-0003.\n */\n is(value: unknown): value is Id<Brand>;\n /**\n * Lenient parse: normalises case and Crockford aliases, returns canonical `Id<Brand>`, or throws.\n */\n parse(value: unknown): Id<Brand>;\n /**\n * Lenient parse without throwing: normalises to canonical form, or returns `{ ok: false, error }`.\n */\n safeParse(value: unknown): ParseResult<Brand>;\n /**\n * Decodes the creation `Date` from an `Id<Brand>` by inverting the timestamp bytes.\n * Trusts the type — use `safeParse()` at boundaries first.\n *\n * Best-effort: inverts and decodes the timestamp bytes in the payload without any\n * additional verification. An ID that bypassed `safeParse()` (e.g. via a type\n * assertion) may return a plausible-looking but incorrect `Date`.\n */\n extractTimestamp(id: Id<Brand>): Date;\n /**\n * Lexicographically smallest ID for any ID generated at `date` (random portion `0x00`).\n * Because timestamps are inverted, a newer `date` yields a lexicographically smaller result —\n * use `minIdForTime(t_new)` as the lower bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n minIdForTime(date: Date): Id<Brand>;\n /**\n * Lexicographically largest ID for any ID generated at `date` (random portion `0xff`).\n * Because timestamps are inverted, an older `date` yields a lexicographically larger result —\n * use `maxIdForTime(t_old)` as the upper bound when scanning [t_old, t_new].\n * Throws on invalid dates.\n */\n maxIdForTime(date: Date): Id<Brand>;\n /**\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 */\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. 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 a Reverse Timestamp codec for `brand` (three lowercase a–z characters).\n *\n * IDs sort newest-first: the 48-bit timestamp field is bitwise-inverted before encoding,\n * so lexicographic ID order equals descending creation-time order. `extractTimestamp`\n * inverts back to recover the original millisecond.\n *\n * @param brand - Entity type brand validated once at construction.\n * @param opts - Optional `now`, `rng`, and `allowDuplicateBrand` overrides.\n * @example\n * ```ts\n * const posts = createReverseTimestampId(\"pst\");\n *\n * const id = posts.generate(); // Id<\"pst\"> (newest-first sort)\n * posts.extractTimestamp(id); // Date\n * ```\n */\nexport function createReverseTimestampId<Brand extends string>(\n brand: Brand & ValidBrand<Brand>,\n opts: ReverseTimestampOptions = {},\n): ReverseTimestampCodec<Brand> {\n validateBrand(brand);\n registerBrand(brand, opts.allowDuplicateBrand);\n\n const now = opts.now ?? Date.now;\n const rng = opts.rng ?? fastTenByteRng;\n const prefix: Prefix<Brand> = `${brand}_`;\n const wire = wireMethods(prefix);\n const layout = createReverseTimestampLayoutOps(prefix, rng);\n\n return {\n generate: () => layout.generateAt(now()),\n generateAt: (date: Date) => layout.generateAt(date.getTime()),\n is: wire.is,\n parse: wire.parse,\n safeParse: wire.safeParse,\n extractTimestamp: layout.extractTimestamp,\n minIdForTime: (date: Date) => layout.minIdForTime(date.getTime()),\n maxIdForTime: (date: Date) => layout.maxIdForTime(date.getTime()),\n toJsonSchema: () => wire.toJsonSchema(brand, layout.exampleWireId()),\n \"~standard\": wire[\"~standard\"],\n toUUID: wire.toUUID,\n fromUUID: wire.fromUUID,\n safeFromUUID: wire.safeFromUUID,\n };\n}\n"],"mappings":";;;AAKA,MAAM,mBAAA;;AAGN,SAAS,oBACP,IACA,KACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,IAAI,UAAU;AAChB;;AAGA,SAAS,4BACP,IACA,MACA,QACA,YACM;CACN,eAAe,IAAI,MAAM;CACzB,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,OAAO,KAAK,CAAC,OAAO,KAAM;CAE5B,WAAW,KAAK,IAAI;AACtB;;AAGA,SAAS,8BACP,QACA,IACM;CACN,MAAM,QAAQ,mBAAmB,QAAQ,EAAE;CAC3C,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAA,GAAyB,KACvC,KAAK,KAAK,OAAO,CAAC,MAAM,KAAM;CAEhC,OAAO,IAAI,KAAK,EAAE;AACpB;;AAGA,SAAgB,gCACd,QACA,KAMA;CACA,MAAM,yBAAS,IAAI,WAAA,EAA4B;CAC/C,MAAM,aAAa,IAAI,WAAW,OAAO,QAAA,GAA6B,gBAAgB;CAEtF,OAAO;EACL,aAAa,OAA0B;GACrC,oBAAoB,IAAI,KAAK,QAAQ,UAAU;GAC/C,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,mBAAmB,OAAwB,8BAA8B,QAAQ,EAAE;EACnF,eAAe,OAA0B;GACvC,4BAA4B,IAAI,GAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,eAAe,OAA0B;GACvC,4BAA4B,IAAI,KAAM,QAAQ,UAAU;GACxD,OAAO,SAAS,QAAQ,MAAM;EAChC;EACA,gBAAgB,OAA2B;GACzC,oBAAoB,MAAM,KAAK,IAAI,GAAG,KAAK,QAAQ,UAAU;GAC7D,OAAO,SAAS,QAAQ,MAAM;EAChC;CACF;AACF;;;;;;;;;;;;;;;;;;;;AC+CA,SAAgB,yBACd,OACA,OAAgC,CAAC,GACH;CAC9B,cAAc,KAAK;CACnB,cAAc,OAAO,KAAK,mBAAmB;CAE7C,MAAM,MAAM,KAAK,OAAO,KAAK;CAC7B,MAAM,MAAM,KAAK,OAAO;CACxB,MAAM,SAAwB,GAAG,MAAM;CACvC,MAAM,OAAO,YAAY,MAAM;CAC/B,MAAM,SAAS,gCAAgC,QAAQ,GAAG;CAE1D,OAAO;EACL,gBAAgB,OAAO,WAAW,IAAI,CAAC;EACvC,aAAa,SAAe,OAAO,WAAW,KAAK,QAAQ,CAAC;EAC5D,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,kBAAkB,OAAO;EACzB,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,eAAe,SAAe,OAAO,aAAa,KAAK,QAAQ,CAAC;EAChE,oBAAoB,KAAK,aAAa,OAAO,OAAO,cAAc,CAAC;EACnE,aAAa,KAAK;EAClB,QAAQ,KAAK;EACb,UAAU,KAAK;EACf,cAAc,KAAK;CACrB;AACF"}
@@ -1,5 +1,5 @@
1
- import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, t as Id } from "./types-wplmOgOK.mjs";
2
- import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-Dqyho9vp.mjs";
1
+ import { a as StandardSchemaProps, i as ParseResult, n as JsonSchema, o as ValidBrand, t as Id } from "./types-hGBnCpJj.mjs";
2
+ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
3
3
 
4
4
  //#region src/codecs/reverse/index.d.ts
5
5
  /**
@@ -69,6 +69,23 @@ type ReverseTimestampCodec<Brand extends string> = {
69
69
  */
70
70
  toJsonSchema(): JsonSchema; /** Standard Schema validate entry point. */
71
71
  readonly "~standard": StandardSchemaProps<Brand>;
72
+ /**
73
+ * Converts a trusted `Id<Brand>` to an RFC 9562 canonical (lowercase, hyphenated)
74
+ * UUID string by reinterpreting the 16-byte payload verbatim. Total — cannot fail.
75
+ * Returns a plain `string` (brand is shed). See ADR-0024.
76
+ */
77
+ toUUID(id: Id<Brand>): string;
78
+ /**
79
+ * Parses a UUID string into an `Id<Brand>`. Accepts case-insensitive `8-4-4-4-12`
80
+ * hyphenated form only. Throws `IdsError` with `code: "invalid_id"` on bad input.
81
+ * See ADR-0024.
82
+ */
83
+ fromUUID(value: string): Id<Brand>;
84
+ /**
85
+ * Non-throwing UUID parse. Returns `{ ok: true, id }` or
86
+ * `{ ok: false, error: "not_string" | "invalid_uuid" }`. See ADR-0024.
87
+ */
88
+ safeFromUUID(value: unknown): ParseResult<Brand>;
72
89
  };
73
90
  /**
74
91
  * Creates a Reverse Timestamp codec for `brand` (three lowercase a–z characters).
@@ -1 +1 @@
1
- {"version":3,"file":"reverse.d.mts","names":[],"sources":["../src/codecs/reverse/index.ts"],"mappings":";;;;;AAoBA;;KAAY,uBAAA;EAIK,6EAFf,GAAA;EAEA,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;AAAA;AAgBF;;;;;;;;;;;;AAhBE,KAgBU,qBAAA;uEAEV,QAAA,IAAY,EAAA,CAAG,KAAA;EAEf,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;EAK3B,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;;;EAIhC,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;EAmCJ;;;EA/BtB,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;;;;;;EASvC,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,IAAA;;;;;;;EAOjC,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;;;EAO7B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;;EAM7B,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;AAAA;;;;;;;;;;AAAA;AAoB5C;;;;;;;iBAAgB,wBAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,GAAM,uBAAA,GACL,qBAAA,CAAsB,KAAA"}
1
+ {"version":3,"file":"reverse.d.mts","names":[],"sources":["../src/codecs/reverse/index.ts"],"mappings":";;;;;AAoBA;;KAAY,uBAAA;EAIK,6EAFf,GAAA;EAEA,GAAA,IAAO,MAAA,EAAQ,UAAA;EAEf,mBAAA;AAAA;AAAA;AAgBF;;;;;;;;;;;;AAhBE,KAgBU,qBAAA;uEAEV,QAAA,IAAY,EAAA,CAAG,KAAA;EAEf,UAAA,CAAW,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;EAK3B,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,EAAA,CAAG,KAAA;;;;EAIhC,KAAA,CAAM,KAAA,YAAiB,EAAA,CAAG,KAAA;;;;EAI1B,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;;;;;;;;;EASvC,gBAAA,CAAiB,EAAA,EAAI,EAAA,CAAG,KAAA,IAAS,IAAA;;;;;;;EAOjC,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;;;EAO7B,YAAA,CAAa,IAAA,EAAM,IAAA,GAAO,EAAA,CAAG,KAAA;;;;;;EAM7B,YAAA,IAAgB,UAAA;WAEP,WAAA,EAAa,mBAAA,CAAoB,KAAA;;;;;;EAM1C,MAAA,CAAO,EAAA,EAAI,EAAA,CAAG,KAAA;;;;;;EAMd,QAAA,CAAS,KAAA,WAAgB,EAAA,CAAG,KAAA;;;;;EAK5B,YAAA,CAAa,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;;;;;;;;;;AAAA;AAoB5C;;;;;;iBAAgB,wBAAA,uBACd,KAAA,EAAO,KAAA,GAAQ,UAAA,CAAW,KAAA,GAC1B,IAAA,GAAM,uBAAA,GACL,qBAAA,CAAsB,KAAA"}
package/dist/reverse.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
- import { t as createReverseTimestampId } from "./reverse-BW8g_cln.mjs";
2
+ import { t as createReverseTimestampId } from "./reverse-CT-El3hi.mjs";
3
3
  export { IdsError, createReverseTimestampId, isIdsError };
@@ -1,4 +1,4 @@
1
- import { o as decodeBase32 } from "./codec-shell-C2NKQEx2.mjs";
1
+ import { o as decodeBase32 } from "./codec-shell-BRZkuQeP.mjs";
2
2
  const timestampBase32Length = Math.ceil(48 / 5);
3
3
  /** Write the timestamp in big-endian; encoded via mod-256 to avoid 32-bit bitwise coercion. */
4
4
  function writeTimestamp(ms, buffer) {
@@ -60,4 +60,4 @@ function fastTenByteRng(target) {
60
60
  //#endregion
61
61
  export { writeTimestamp as a, readTimestampMsFromBase32Suffix as i, fastTenByteRng as n, readTimestampMs as r, defaultRng as t };
62
62
 
63
- //# sourceMappingURL=rng-BHFxX1Fc.mjs.map
63
+ //# sourceMappingURL=rng-6GyNT4zS.mjs.map