@smonn/ids 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +129 -20
  2. package/dist/adapter-types-BY-wrYYB.mjs +27 -0
  3. package/dist/adapter-types-BY-wrYYB.mjs.map +1 -0
  4. package/dist/adapter-types-unUcmMXC.d.mts +20 -0
  5. package/dist/adapter-types-unUcmMXC.d.mts.map +1 -0
  6. package/dist/cli.mjs +213 -47
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{codec-shell-DH-UO4UR.mjs → codec-shell-C7_B4oum.mjs} +4 -3
  9. package/dist/codec-shell-C7_B4oum.mjs.map +1 -0
  10. package/dist/{drizzle-CeSni5PB.d.mts → drizzle-CHtyDXpv.d.mts} +4 -15
  11. package/dist/drizzle-CHtyDXpv.d.mts.map +1 -0
  12. package/dist/drizzle.d.mts +3 -2
  13. package/dist/drizzle.mjs +2 -3
  14. package/dist/drizzle.mjs.map +1 -1
  15. package/dist/express.d.mts +2 -5
  16. package/dist/express.d.mts.map +1 -1
  17. package/dist/express.mjs +3 -8
  18. package/dist/express.mjs.map +1 -1
  19. package/dist/fastify.d.mts +12 -6
  20. package/dist/fastify.d.mts.map +1 -1
  21. package/dist/fastify.mjs +10 -8
  22. package/dist/fastify.mjs.map +1 -1
  23. package/dist/hono.d.mts +2 -5
  24. package/dist/hono.d.mts.map +1 -1
  25. package/dist/hono.mjs +3 -8
  26. package/dist/hono.mjs.map +1 -1
  27. package/dist/index.mjs +1 -1
  28. package/dist/key-material-DUHhmMq-.mjs +137 -0
  29. package/dist/key-material-DUHhmMq-.mjs.map +1 -0
  30. package/dist/kysely.d.mts +1 -1
  31. package/dist/kysely.d.mts.map +1 -1
  32. package/dist/{opaque-uvjOFY_0.mjs → opaque-BQOlZ2oD.mjs} +8 -44
  33. package/dist/opaque-BQOlZ2oD.mjs.map +1 -0
  34. package/dist/opaque.d.mts +8 -0
  35. package/dist/opaque.d.mts.map +1 -1
  36. package/dist/opaque.mjs +1 -1
  37. package/dist/prisma.d.mts +3 -16
  38. package/dist/prisma.d.mts.map +1 -1
  39. package/dist/prisma.mjs +2 -3
  40. package/dist/prisma.mjs.map +1 -1
  41. package/dist/{reverse-BgFU6JHw.mjs → reverse-C12D1btB.mjs} +4 -6
  42. package/dist/reverse-C12D1btB.mjs.map +1 -0
  43. package/dist/reverse.d.mts.map +1 -1
  44. package/dist/reverse.mjs +1 -1
  45. package/dist/rng-CPJOx_nE.mjs +9 -0
  46. package/dist/rng-CPJOx_nE.mjs.map +1 -0
  47. package/dist/signed-CwqKTFaQ.mjs +207 -0
  48. package/dist/signed-CwqKTFaQ.mjs.map +1 -0
  49. package/dist/signed.d.mts +112 -1
  50. package/dist/signed.d.mts.map +1 -1
  51. package/dist/signed.mjs +2 -99
  52. package/dist/{timestamp-B5_UCzc6.mjs → timestamp-BjIMQkJf.mjs} +3 -3
  53. package/dist/{timestamp-B5_UCzc6.mjs.map → timestamp-BjIMQkJf.mjs.map} +1 -1
  54. package/dist/{timestamp-bytes-BBY7JI33.mjs → timestamp-bytes-Bbg6Y66Z.mjs} +2 -2
  55. package/dist/{timestamp-bytes-BBY7JI33.mjs.map → timestamp-bytes-Bbg6Y66Z.mjs.map} +1 -1
  56. package/dist/{wrapped-0vL72Nje.mjs → wrapped-DKOsN_dq.mjs} +16 -50
  57. package/dist/wrapped-DKOsN_dq.mjs.map +1 -0
  58. package/dist/wrapped.d.mts.map +1 -1
  59. package/dist/wrapped.mjs +1 -1
  60. package/package.json +1 -1
  61. package/dist/adapter-types-oHCCSgOO.d.mts +0 -12
  62. package/dist/adapter-types-oHCCSgOO.d.mts.map +0 -1
  63. package/dist/bytes-lhzKVaBV.mjs +0 -53
  64. package/dist/bytes-lhzKVaBV.mjs.map +0 -1
  65. package/dist/codec-shell-DH-UO4UR.mjs.map +0 -1
  66. package/dist/drizzle-CeSni5PB.d.mts.map +0 -1
  67. package/dist/opaque-uvjOFY_0.mjs.map +0 -1
  68. package/dist/reverse-BgFU6JHw.mjs.map +0 -1
  69. package/dist/signed.mjs.map +0 -1
  70. package/dist/wrapped-0vL72Nje.mjs.map +0 -1
@@ -64,6 +64,7 @@ function payloadBytesFromId(prefix, id) {
64
64
  }
65
65
  const payloadBase32Length = Math.ceil(128 / 5);
66
66
  const base32CharClass = "[0-9a-hjkmnp-tv-z]";
67
+ const base32FinalCharClass = "[048cgmrw]";
67
68
  //#endregion
68
69
  //#region src/registry.ts
69
70
  const registeredBrands = /* @__PURE__ */ new Set();
@@ -82,7 +83,7 @@ function registerBrand(brand, allowDuplicateBrand) {
82
83
  const replacePattern = /[ilo]/g;
83
84
  const aliasTestPattern = /[ilo]/;
84
85
  const replacer = (match) => match === "o" ? "0" : "1";
85
- const base32Pattern = new RegExp(`^[${alphabet}]{${payloadBase32Length}}$`);
86
+ const base32Pattern = new RegExp(`^[${alphabet}]{${payloadBase32Length - 1}}${base32FinalCharClass}$`);
86
87
  function safeParse(prefix, value) {
87
88
  if (typeof value !== "string") return {
88
89
  ok: false,
@@ -135,7 +136,7 @@ function wireMethods(prefix) {
135
136
  safeParse: (value) => safeParse(prefix, value),
136
137
  toJsonSchema: (brand, example) => ({
137
138
  type: "string",
138
- pattern: `^${prefix}${base32CharClass}{${payloadBase32Length}}$`,
139
+ pattern: `^${prefix}${base32CharClass}{${payloadBase32Length - 1}}${base32FinalCharClass}$`,
139
140
  description: `Branded ID for '${brand}'`,
140
141
  example
141
142
  }),
@@ -149,4 +150,4 @@ function wireMethods(prefix) {
149
150
  //#endregion
150
151
  export { toWireId as a, payloadBytesFromId as i, registerBrand as n, decodeBase32 as o, payloadBase32Length as r, validateBrand as s, wireMethods as t };
151
152
 
152
- //# sourceMappingURL=codec-shell-DH-UO4UR.mjs.map
153
+ //# sourceMappingURL=codec-shell-C7_B4oum.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec-shell-C7_B4oum.mjs","names":[],"sources":["../src/brand.ts","../src/base32.ts","../src/wire/envelope.ts","../src/wire/invariants.ts","../src/registry.ts","../src/wire/parse.ts","../src/wire/codec-shell.ts"],"sourcesContent":["import { IdsError } from \"./error.js\";\n\nconst brandPattern = /^[a-z]{3}$/;\n\n/** Validates a three-character lowercase brand. Throws on invalid input. */\nexport function validateBrand(brand: string): void {\n if (!brandPattern.test(brand)) {\n throw new IdsError(\"invalid_brand\", \"invalid brand: expected three lowercase a-z characters\");\n }\n}\n","/*\n This is based on Crockford's Base32 spec: https://www.crockford.com/base32.html\n One difference is that it uses lowercase instead of uppercase when encoding.\n\n These functions are internal: codec constructors guarantee that input is a\n 16-byte buffer for encode, or a string of characters drawn from the alphabet\n for decode. Invalid input produces silent garbage rather than a thrown error,\n consistent with the trust-the-type rule in ADR-0003.\n*/\n\nexport const alphabet = \"0123456789abcdefghjkmnpqrstvwxyz\";\n\n// 0–31 → ASCII char code, for write-into-codes-then-fromCharCode encoding.\nconst valueToCharCode = new Uint8Array(32);\nfor (let i = 0; i < 32; i++) valueToCharCode[i] = alphabet.charCodeAt(i);\n\n// charCode → 0–31 value. Canonical lowercase only; upstream resolves case and\n// o/i/l aliases before any string reaches decodeBase32.\nconst INVALID = 0xff;\nconst charCodeToValue = new Uint8Array(256).fill(INVALID);\nfor (let i = 0; i < alphabet.length; i++) charCodeToValue[alphabet.charCodeAt(i)] = i;\n\nexport function encodeBase32(bytes: Uint8Array): string {\n // Build an Array<number> of char codes and pass it to fromCharCode.apply.\n // Faster than `result += char` (avoids cons-string overhead) and than\n // Uint8Array variants (apply has a fast path for plain Arrays).\n // oxlint-disable-next-line no-new-array\n const codes = new Array<number>(Math.floor((bytes.length * 8) / 5) + 1);\n let chi = 0;\n let bits = 0;\n let value = 0;\n\n for (let i = 0; i < bytes.length; i++) {\n value = (value << 8) | bytes[i]!;\n bits += 8;\n while (bits >= 5) {\n bits -= 5;\n codes[chi++] = valueToCharCode[(value >>> bits) & 0x1f]!;\n }\n }\n codes[chi] = valueToCharCode[(value << (5 - bits)) & 0x1f]!;\n return String.fromCharCode.apply(null, codes);\n}\n\nexport function decodeBase32(str: string): Uint8Array {\n const result = new Uint8Array(Math.floor((str.length * 5) / 8));\n let bits = 0;\n let value = 0;\n let index = 0;\n\n for (let i = 0; i < str.length; i++) {\n const v = charCodeToValue[str.charCodeAt(i)]!;\n value = (value << 5) | v;\n bits += 5;\n if (bits >= 8) {\n bits -= 8;\n result[index++] = (value >>> bits) & 0xff;\n }\n }\n return result;\n}\n","import { decodeBase32, encodeBase32 } from \"../base32.js\";\nimport type { Id, Prefix } from \"../types.js\";\n\n/** Encodes a 16-byte payload as lowercase Crockford base32 (26 chars). */\nfunction encodePayload(bytes: Uint8Array): string {\n return encodeBase32(bytes);\n}\n\n/** Decodes a 26-char base32 payload suffix to 16 bytes. Trust-the-type. */\nfunction decodePayload(base32: string): Uint8Array {\n return decodeBase32(base32);\n}\n\n/** Composes a canonical wire ID from a prefix and 16-byte payload. */\nexport function toWireId<Brand extends string>(\n prefix: Prefix<Brand>,\n payload: Uint8Array,\n): Id<Brand> {\n return (prefix + encodePayload(payload)) as Id<Brand>;\n}\n\n/** Decodes the full 16-byte payload from a trusted wire ID. */\nexport function payloadBytesFromId<Brand extends string>(\n prefix: Prefix<Brand>,\n id: Id<Brand>,\n): Uint8Array {\n return decodePayload(id.slice(prefix.length));\n}\n","// Payload is always 16 bytes on the wire (every codec). 16 bytes → 26 Crockford\n// base32 chars. ADR-0002 codifies this as the shared wire-format invariant.\nexport const payloadByteLength: number = 16;\nexport const payloadBase32Length: number = Math.ceil((payloadByteLength * 8) / 5);\n\n// Compact regex character class for the canonical lowercase Crockford alphabet\n// (`0123456789abcdefghjkmnpqrstvwxyz` — excludes i, l, o, u). Used in the JSON\n// Schema `pattern`, which describes the canonical wire form only (ADR-0003).\nexport const base32CharClass: string = \"[0-9a-hjkmnp-tv-z]\";\n\n// The 8 alphabet values at indices divisible by 4 (low 2 bits = 00) that satisfy the\n// canonical padding-bit constraint for a 130-bit encoding of 16 bytes. ADR-0003.\nexport const base32FinalCharClass: string = \"[048cgmrw]\";\n","const registeredBrands = new Set<string>();\nconst warnedBrands = new Set<string>();\n\nexport function registerBrand(brand: string, allowDuplicateBrand: boolean | undefined): void {\n if (\n typeof process === \"undefined\" ||\n process.env.NODE_ENV === \"production\" ||\n allowDuplicateBrand\n ) {\n return;\n }\n\n if (registeredBrands.has(brand)) {\n if (!warnedBrands.has(brand)) {\n console.warn(\n `[@smonn/ids] brand \"${brand}\" was registered more than once — this usually indicates a bundling or import bug, or that more than one codec variant is using the same brand. Pass { allowDuplicateBrand: true } to silence.`,\n );\n warnedBrands.add(brand);\n }\n } else {\n registeredBrands.add(brand);\n }\n}\n","import { alphabet } from \"../base32.js\";\nimport type { Id, ParseError, ParseResult, Prefix } from \"../types.js\";\nimport { base32FinalCharClass, payloadBase32Length } from \"./invariants.js\";\n\nconst replacePattern = /[ilo]/g;\nconst aliasTestPattern = /[ilo]/;\nconst replacer = (match: string): string => (match === \"o\" ? \"0\" : \"1\");\nconst base32Pattern = new RegExp(\n `^[${alphabet}]{${payloadBase32Length - 1}}${base32FinalCharClass}$`,\n);\n\nexport function safeParse<Brand extends string>(\n prefix: Prefix<Brand>,\n value: unknown,\n): ParseResult<Brand> {\n if (typeof value !== \"string\") return { ok: false, error: \"not_string\" };\n const lowercase = value.toLowerCase();\n if (!lowercase.startsWith(prefix)) return { ok: false, error: \"invalid_prefix\" };\n\n const sliced = lowercase.slice(prefix.length);\n const base32 = aliasTestPattern.test(sliced)\n ? sliced.replaceAll(replacePattern, replacer)\n : sliced;\n\n if (!base32Pattern.test(base32)) return { ok: false, error: \"invalid_base32\" };\n\n const id = (prefix + base32) as Id<Brand>;\n return { ok: true, id };\n}\n\nexport function is<Brand extends string>(\n prefix: Prefix<Brand>,\n value: unknown,\n): value is Id<Brand> {\n if (typeof value !== \"string\") return false;\n if (!value.startsWith(prefix)) return false;\n return base32Pattern.test(value.slice(prefix.length));\n}\n\nfunction errorMessage<Brand extends string>(prefix: Prefix<Brand>, error: ParseError): string {\n switch (error) {\n case \"not_string\":\n return \"expected string\";\n case \"invalid_prefix\":\n return `expected prefix '${prefix}'`;\n case \"invalid_base32\":\n return \"invalid base32 payload\";\n }\n}\n\nexport function standardValidate<Brand extends string>(\n prefix: Prefix<Brand>,\n value: unknown,\n):\n | { readonly value: Id<Brand>; readonly issues?: undefined }\n | { readonly issues: ReadonlyArray<{ readonly message: string }> } {\n const result = safeParse(prefix, value);\n if (result.ok) return { value: result.id };\n return { issues: [{ message: errorMessage(prefix, result.error) }] };\n}\n","import { IdsError } from \"../error.js\";\nimport type { Id, JsonSchema, ParseResult, Prefix, StandardSchemaProps } from \"../types.js\";\nimport { base32CharClass, base32FinalCharClass, payloadBase32Length } from \"./invariants.js\";\nimport { is, safeParse, standardValidate } from \"./parse.js\";\n\ntype WireMethods<Brand extends string> = {\n is: (value: unknown) => value is Id<Brand>;\n parse: (value: unknown) => Id<Brand>;\n safeParse: (value: unknown) => ParseResult<Brand>;\n toJsonSchema: (brand: Brand, example: string) => JsonSchema;\n \"~standard\": StandardSchemaProps<Brand>;\n};\n\n/** Wire-only methods shared by every codec variant for a fixed prefix. */\nexport function wireMethods<Brand extends string>(prefix: Prefix<Brand>): WireMethods<Brand> {\n const standard: StandardSchemaProps<Brand> = {\n version: 1,\n vendor: \"@smonn/ids\",\n validate: (value: unknown) => standardValidate(prefix, value),\n };\n return {\n is: (value: unknown): value is Id<Brand> => is(prefix, value),\n parse: (value: unknown): Id<Brand> => {\n const result = safeParse(prefix, value);\n if (result.ok) return result.id;\n throw new IdsError(\"invalid_id\", `invalid ID: ${result.error}`, { cause: result.error });\n },\n safeParse: (value: unknown): ParseResult<Brand> => safeParse(prefix, value),\n toJsonSchema: (brand: Brand, example: string): JsonSchema => ({\n type: \"string\",\n pattern: `^${prefix}${base32CharClass}{${payloadBase32Length - 1}}${base32FinalCharClass}$`,\n description: `Branded ID for '${brand}'`,\n example,\n }),\n \"~standard\": standard,\n };\n}\n"],"mappings":"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"}
@@ -1,20 +1,9 @@
1
- import { i as ParseResult, t as Id } from "./types-g7CiQDyE.mjs";
1
+ import { t as Id } from "./types-g7CiQDyE.mjs";
2
+ import { n as IdColumnCodec } from "./adapter-types-unUcmMXC.mjs";
2
3
  import { ConvertCustomConfig, PgCustomColumnBuilder } from "drizzle-orm/pg-core";
3
4
 
4
5
  //#region src/drizzle.d.ts
5
6
  /**
6
- * Minimum codec interface required by the Drizzle adapter.
7
- *
8
- * Any codec variant satisfies this type — TimestampCodec, OpaqueTimestampCodec,
9
- * and WrappedKeyCodec all expose `safeParse`. The adapter never calls
10
- * `extractTimestamp`, `wrap`/`unwrap`, or any key-dependent method.
11
- *
12
- * Kysely and Prisma adapter issues should use this same codec contract shape.
13
- */
14
- type IdColumnCodec<Brand extends string> = {
15
- safeParse(value: unknown): ParseResult<Brand>;
16
- };
17
- /**
18
7
  * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value.
19
8
  *
20
9
  * **Write path:** passes the `Id<Brand>` directly to the driver — it is already
@@ -40,5 +29,5 @@ declare function idColumn<Brand extends string>(codec: IdColumnCodec<Brand>): Pg
40
29
  driverData: string;
41
30
  }>>;
42
31
  //#endregion
43
- export { idColumn as n, IdColumnCodec as t };
44
- //# sourceMappingURL=drizzle-CeSni5PB.d.mts.map
32
+ export { idColumn as t };
33
+ //# sourceMappingURL=drizzle-CHtyDXpv.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle-CHtyDXpv.d.mts","names":[],"sources":["../src/drizzle.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqCoE;iBAFpD,QAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,qBAAA,CAAsB,mBAAA;EAA0B,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA"}
@@ -1,3 +1,4 @@
1
1
  import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-DTr4i6Ic.mjs";
2
- import { n as idColumn, t as IdColumnCodec } from "./drizzle-CeSni5PB.mjs";
3
- export { IdColumnCodec, IdsError, type IdsErrorCode, idColumn, isIdsError };
2
+ import { n as IdColumnCodec } from "./adapter-types-unUcmMXC.mjs";
3
+ import { t as idColumn } from "./drizzle-CHtyDXpv.mjs";
4
+ export { type IdColumnCodec, IdsError, type IdsErrorCode, idColumn, isIdsError };
package/dist/drizzle.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
2
+ import { t as readIdColumn } from "./adapter-types-BY-wrYYB.mjs";
2
3
  import { customType } from "drizzle-orm/pg-core";
3
4
  //#region src/drizzle.ts
4
5
  /**
@@ -31,9 +32,7 @@ function idColumn(codec) {
31
32
  return value;
32
33
  },
33
34
  fromDriver(value) {
34
- const result = codec.safeParse(value);
35
- if (!result.ok) throw new IdsError("invalid_id", `invalid ID from database: ${result.error}`, { cause: result.error });
36
- return result.id;
35
+ return readIdColumn(codec, value);
37
36
  }
38
37
  })();
39
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle.mjs","names":[],"sources":["../src/drizzle.ts"],"sourcesContent":["import {\n customType,\n type ConvertCustomConfig,\n type PgCustomColumnBuilder,\n} from \"drizzle-orm/pg-core\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"./error.js\";\nimport type { Id, ParseResult } 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 };\n\n/**\n * Minimum codec interface required by the Drizzle adapter.\n *\n * Any codec variant satisfies this type — TimestampCodec, OpaqueTimestampCodec,\n * and WrappedKeyCodec all expose `safeParse`. The adapter never calls\n * `extractTimestamp`, `wrap`/`unwrap`, or any key-dependent method.\n *\n * Kysely and Prisma adapter issues should use this same codec contract shape.\n */\nexport type IdColumnCodec<Brand extends string> = {\n safeParse(value: unknown): ParseResult<Brand>;\n};\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Data at rest should already be canonical per ADR-0003, but `safeParse`\n * is a safe boundary in case stale non-canonical values exist. Throws if the\n * value from the database does not parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = pgTable(\"users\", { id: idColumn(usr).primaryKey() });\n * // users.id is Id<\"usr\"> end-to-end\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): PgCustomColumnBuilder<ConvertCustomConfig<\"\", { data: Id<Brand>; driverData: string }>> {\n return customType<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n const result = codec.safeParse(value);\n if (!result.ok) {\n throw new IdsError(\"invalid_id\", `invalid ID from database: ${result.error}`, {\n cause: result.error,\n });\n }\n return result.id;\n },\n })();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,SACd,OACyF;CACzF,OAAO,WAAoD;EACzD,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,MAAM,SAAS,MAAM,UAAU,KAAK;GACpC,IAAI,CAAC,OAAO,IACV,MAAM,IAAI,SAAS,cAAc,6BAA6B,OAAO,SAAS,EAC5E,OAAO,OAAO,MAChB,CAAC;GAEH,OAAO,OAAO;EAChB;CACF,CAAC,CAAC,CAAC;AACL"}
1
+ {"version":3,"file":"drizzle.mjs","names":[],"sources":["../src/drizzle.ts"],"sourcesContent":["import {\n customType,\n type ConvertCustomConfig,\n type PgCustomColumnBuilder,\n} from \"drizzle-orm/pg-core\";\nimport { IdsError, isIdsError, type IdsErrorCode } from \"./error.js\";\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 };\n\nexport type { IdColumnCodec };\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Data at rest should already be canonical per ADR-0003, but `safeParse`\n * is a safe boundary in case stale non-canonical values exist. Throws if the\n * value from the database does not parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = pgTable(\"users\", { id: idColumn(usr).primaryKey() });\n * // users.id is Id<\"usr\"> end-to-end\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): PgCustomColumnBuilder<ConvertCustomConfig<\"\", { data: Id<Brand>; driverData: string }>> {\n return customType<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,SACd,OACyF;CACzF,OAAO,WAAoD;EACzD,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC;AACL"}
@@ -1,11 +1,8 @@
1
- import { i as ParseResult, t as Id } from "./types-g7CiQDyE.mjs";
2
- import { t as IdParamFailure } from "./adapter-types-oHCCSgOO.mjs";
1
+ import { t as Id } from "./types-g7CiQDyE.mjs";
2
+ import { r as IdParamFailure, t as IdCodec } from "./adapter-types-unUcmMXC.mjs";
3
3
  import { NextFunction, Request, Response } from "express";
4
4
 
5
5
  //#region src/express.d.ts
6
- type IdCodec<Brand extends string> = {
7
- safeParse(value: unknown): ParseResult<Brand>;
8
- };
9
6
  /**
10
7
  * Typed error forwarded to Express's error pipeline (`next(err)`) on validation failure.
11
8
  * Inspect `err.reason` and `err.status` in error-handling middleware.
@@ -1 +1 @@
1
- {"version":3,"file":"express.d.mts","names":[],"sources":["../src/express.ts"],"mappings":";;;;;KAMK,OAAA;EACH,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;;;;cAO5B,YAAA,SAAqB,KAAA;EAAA,SACvB,MAAA;EAAA,SACA,MAAA;EAET,WAAA,CAAY,MAAA,kCAAwC,MAAA;AAAA;AAJtD;AAAA,KAaY,cAAA;;;;;EAKV,OAAA,IAAW,OAAA,EAAS,cAAA,EAAgB,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;;;EAKvE,MAAA;IAAW,cAAA;IAAyB,SAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;AAAA;AAmDtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,OAAA,gDACd,SAAA,EAAW,QAAA,EACX,KAAA,EAAO,OAAA,CAAQ,KAAA,GACf,OAAA,GAAU,cAAA,IACR,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,UAAkB,MAAA,CAAO,QAAA,EAAU,EAAA,CAAG,KAAA,KAAU,IAAA,EAAM,YAAA"}
1
+ {"version":3,"file":"express.d.mts","names":[],"sources":["../src/express.ts"],"mappings":";;;;;;AAUA;;;cAAa,YAAA,SAAqB,KAAA;EAAA,SACvB,MAAA;EAAA,SACA,MAAA;EAET,WAAA,CAAY,MAAA,kCAAwC,MAAA;AAAA;;KAS1C,cAAA;EAT0C;AAAA;AAStD;;EAKE,OAAA,IAAW,OAAA,EAAS,cAAA,EAAgB,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;;;EAKvE,MAAA;IAAW,cAAA;IAAyB,SAAA;EAAA;AAAA;;;;;;;;;;;AAAA;AAmDtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAI6E;iBAJ7D,OAAA,gDACd,SAAA,EAAW,QAAA,EACX,KAAA,EAAO,OAAA,CAAQ,KAAA,GACf,OAAA,GAAU,cAAA,IACR,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,UAAkB,MAAA,CAAO,QAAA,EAAU,EAAA,CAAG,KAAA,KAAU,IAAA,EAAM,YAAA"}
package/dist/express.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { n as resolveIdParamFailure } from "./adapter-types-BY-wrYYB.mjs";
1
2
  //#region src/express.ts
2
3
  /**
3
4
  * Typed error forwarded to Express's error pipeline (`next(err)`) on validation failure.
@@ -66,18 +67,12 @@ function idParam(paramName, codec, options) {
66
67
  const raw = req.params[paramName];
67
68
  const result = codec.safeParse(raw);
68
69
  if (!result.ok) {
69
- const reason = result.error === "invalid_prefix" ? "brand_mismatch" : "malformed";
70
- const defaultStatus = reason === "brand_mismatch" ? 404 : 400;
71
- const status = options?.status?.[reason] ?? defaultStatus;
72
- const failure = {
73
- reason,
74
- status
75
- };
70
+ const failure = resolveIdParamFailure(result.error, options);
76
71
  if (options?.onError) {
77
72
  options.onError(failure, req, res, next);
78
73
  return;
79
74
  }
80
- next(new IdParamError(reason, status));
75
+ next(new IdParamError(failure.reason, failure.status));
81
76
  return;
82
77
  }
83
78
  res.locals[paramName] = result.id;
@@ -1 +1 @@
1
- {"version":3,"file":"express.mjs","names":[],"sources":["../src/express.ts"],"sourcesContent":["import type { NextFunction, Request, Response } from \"express\";\nimport type { IdParamFailure } from \"./adapter-types.js\";\nimport type { Id, ParseResult } from \"./types.js\";\n\nexport type { IdParamFailure };\n\ntype IdCodec<Brand extends string> = {\n safeParse(value: unknown): ParseResult<Brand>;\n};\n\n/**\n * Typed error forwarded to Express's error pipeline (`next(err)`) on validation failure.\n * Inspect `err.reason` and `err.status` in error-handling middleware.\n */\nexport class IdParamError extends Error {\n readonly status: number;\n readonly reason: \"brand_mismatch\" | \"malformed\";\n\n constructor(reason: \"brand_mismatch\" | \"malformed\", status: number) {\n super(`ID validation failed: ${reason}`);\n this.name = \"IdParamError\";\n this.reason = reason;\n this.status = status;\n }\n}\n\n/** Options for `idParam`. All fields are optional. */\nexport type IdParamOptions = {\n /**\n * Called instead of forwarding to `next(err)` when provided. The hook owns the response\n * entirely — the adapter does not call `next(err)` itself.\n */\n onError?: (failure: IdParamFailure, req: Request, res: Response, next: NextFunction) => void;\n /**\n * Remap the default HTTP status for a failure reason without a full handler.\n * e.g. `{ brand_mismatch: 400 }` treats both failure kinds as 400.\n */\n status?: { brand_mismatch?: number; malformed?: number };\n};\n\n/**\n * Express middleware that validates a named route param against a codec via `safeParse`.\n *\n * **Default (no options):** calls `next(err)` with an `IdParamError` carrying `status` and `reason`,\n * so the app's existing error-handling middleware controls rendering. The adapter does not write\n * a response body itself.\n *\n * **`options.onError`:** when provided, the hook owns the response entirely — the adapter does\n * not call `next(err)`.\n *\n * **`options.status`:** remaps the default HTTP status for a reason without a full handler.\n *\n * - **Brand mismatch (`invalid_prefix`) → `reason: \"brand_mismatch\"`, default 404**\n * - **Malformed or missing ID → `reason: \"malformed\"`, default 400**\n *\n * On success, stores the canonical `Id<Brand>` in `res.locals` under `paramName`\n * and calls `next()`.\n *\n * @example\n * ```ts\n * import { idParam, IdParamError } from \"@smonn/ids/express\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // Default: forwards error to app error-handling middleware\n * app.get(\"/users/:id\", idParam(\"id\", usr), (req, res) => {\n * const id = res.locals.id; // Id<\"usr\">, canonical\n * });\n *\n * // Error-handling middleware receives the typed error\n * app.use((err, req, res, next) => {\n * if (err instanceof IdParamError) {\n * res.status(err.status).json({ error: err.reason });\n * return;\n * }\n * next(err);\n * });\n *\n * // Override: consumer fully owns the response\n * app.get(\"/orgs/:id\", idParam(\"id\", org, {\n * onError: (failure, req, res) => res.status(failure.status).json({ error: failure.reason }),\n * }), handler);\n *\n * // Or a lightweight status remap without a full handler\n * app.get(\"/things/:id\", idParam(\"id\", thing, { status: { brand_mismatch: 400 } }), handler);\n * ```\n */\nexport function idParam<ParamKey extends string, Brand extends string>(\n paramName: ParamKey,\n codec: IdCodec<Brand>,\n options?: IdParamOptions,\n): (req: Request, res: Response<unknown, Record<ParamKey, Id<Brand>>>, next: NextFunction) => void {\n return (req, res, next): void => {\n const raw = req.params[paramName];\n const result = codec.safeParse(raw);\n if (!result.ok) {\n const reason =\n result.error === \"invalid_prefix\" ? (\"brand_mismatch\" as const) : (\"malformed\" as const);\n const defaultStatus = reason === \"brand_mismatch\" ? 404 : 400;\n const status = options?.status?.[reason] ?? defaultStatus;\n const failure: IdParamFailure = { reason, status };\n if (options?.onError) {\n options.onError(failure, req, res, next);\n return;\n }\n next(new IdParamError(reason, status));\n return;\n }\n (res.locals as Record<string, unknown>)[paramName] = result.id;\n next();\n };\n}\n"],"mappings":";;;;;AAcA,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CAEA,YAAY,QAAwC,QAAgB;EAClE,MAAM,yBAAyB,QAAQ;EACvC,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,SAAS;CAChB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAgB,QACd,WACA,OACA,SACiG;CACjG,QAAQ,KAAK,KAAK,SAAe;EAC/B,MAAM,MAAM,IAAI,OAAO;EACvB,MAAM,SAAS,MAAM,UAAU,GAAG;EAClC,IAAI,CAAC,OAAO,IAAI;GACd,MAAM,SACJ,OAAO,UAAU,mBAAoB,mBAA8B;GACrE,MAAM,gBAAgB,WAAW,mBAAmB,MAAM;GAC1D,MAAM,SAAS,SAAS,SAAS,WAAW;GAC5C,MAAM,UAA0B;IAAE;IAAQ;GAAO;GACjD,IAAI,SAAS,SAAS;IACpB,QAAQ,QAAQ,SAAS,KAAK,KAAK,IAAI;IACvC;GACF;GACA,KAAK,IAAI,aAAa,QAAQ,MAAM,CAAC;GACrC;EACF;EACA,IAAK,OAAmC,aAAa,OAAO;EAC5D,KAAK;CACP;AACF"}
1
+ {"version":3,"file":"express.mjs","names":[],"sources":["../src/express.ts"],"sourcesContent":["import type { NextFunction, Request, Response } from \"express\";\nimport { type IdCodec, type IdParamFailure, resolveIdParamFailure } from \"./adapter-types.js\";\nimport type { Id } from \"./types.js\";\n\nexport type { IdParamFailure };\n\n/**\n * Typed error forwarded to Express's error pipeline (`next(err)`) on validation failure.\n * Inspect `err.reason` and `err.status` in error-handling middleware.\n */\nexport class IdParamError extends Error {\n readonly status: number;\n readonly reason: \"brand_mismatch\" | \"malformed\";\n\n constructor(reason: \"brand_mismatch\" | \"malformed\", status: number) {\n super(`ID validation failed: ${reason}`);\n this.name = \"IdParamError\";\n this.reason = reason;\n this.status = status;\n }\n}\n\n/** Options for `idParam`. All fields are optional. */\nexport type IdParamOptions = {\n /**\n * Called instead of forwarding to `next(err)` when provided. The hook owns the response\n * entirely — the adapter does not call `next(err)` itself.\n */\n onError?: (failure: IdParamFailure, req: Request, res: Response, next: NextFunction) => void;\n /**\n * Remap the default HTTP status for a failure reason without a full handler.\n * e.g. `{ brand_mismatch: 400 }` treats both failure kinds as 400.\n */\n status?: { brand_mismatch?: number; malformed?: number };\n};\n\n/**\n * Express middleware that validates a named route param against a codec via `safeParse`.\n *\n * **Default (no options):** calls `next(err)` with an `IdParamError` carrying `status` and `reason`,\n * so the app's existing error-handling middleware controls rendering. The adapter does not write\n * a response body itself.\n *\n * **`options.onError`:** when provided, the hook owns the response entirely — the adapter does\n * not call `next(err)`.\n *\n * **`options.status`:** remaps the default HTTP status for a reason without a full handler.\n *\n * - **Brand mismatch (`invalid_prefix`) → `reason: \"brand_mismatch\"`, default 404**\n * - **Malformed or missing ID → `reason: \"malformed\"`, default 400**\n *\n * On success, stores the canonical `Id<Brand>` in `res.locals` under `paramName`\n * and calls `next()`.\n *\n * @example\n * ```ts\n * import { idParam, IdParamError } from \"@smonn/ids/express\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // Default: forwards error to app error-handling middleware\n * app.get(\"/users/:id\", idParam(\"id\", usr), (req, res) => {\n * const id = res.locals.id; // Id<\"usr\">, canonical\n * });\n *\n * // Error-handling middleware receives the typed error\n * app.use((err, req, res, next) => {\n * if (err instanceof IdParamError) {\n * res.status(err.status).json({ error: err.reason });\n * return;\n * }\n * next(err);\n * });\n *\n * // Override: consumer fully owns the response\n * app.get(\"/orgs/:id\", idParam(\"id\", org, {\n * onError: (failure, req, res) => res.status(failure.status).json({ error: failure.reason }),\n * }), handler);\n *\n * // Or a lightweight status remap without a full handler\n * app.get(\"/things/:id\", idParam(\"id\", thing, { status: { brand_mismatch: 400 } }), handler);\n * ```\n */\nexport function idParam<ParamKey extends string, Brand extends string>(\n paramName: ParamKey,\n codec: IdCodec<Brand>,\n options?: IdParamOptions,\n): (req: Request, res: Response<unknown, Record<ParamKey, Id<Brand>>>, next: NextFunction) => void {\n return (req, res, next): void => {\n const raw = req.params[paramName];\n const result = codec.safeParse(raw);\n if (!result.ok) {\n const failure = resolveIdParamFailure(result.error, options);\n if (options?.onError) {\n options.onError(failure, req, res, next);\n return;\n }\n next(new IdParamError(failure.reason, failure.status));\n return;\n }\n (res.locals as Record<string, unknown>)[paramName] = result.id;\n next();\n };\n}\n"],"mappings":";;;;;;AAUA,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CAEA,YAAY,QAAwC,QAAgB;EAClE,MAAM,yBAAyB,QAAQ;EACvC,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,SAAS;CAChB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAgB,QACd,WACA,OACA,SACiG;CACjG,QAAQ,KAAK,KAAK,SAAe;EAC/B,MAAM,MAAM,IAAI,OAAO;EACvB,MAAM,SAAS,MAAM,UAAU,GAAG;EAClC,IAAI,CAAC,OAAO,IAAI;GACd,MAAM,UAAU,sBAAsB,OAAO,OAAO,OAAO;GAC3D,IAAI,SAAS,SAAS;IACpB,QAAQ,QAAQ,SAAS,KAAK,KAAK,IAAI;IACvC;GACF;GACA,KAAK,IAAI,aAAa,QAAQ,QAAQ,QAAQ,MAAM,CAAC;GACrD;EACF;EACA,IAAK,OAAmC,aAAa,OAAO;EAC5D,KAAK;CACP;AACF"}
@@ -1,11 +1,8 @@
1
- import { i as ParseResult } from "./types-g7CiQDyE.mjs";
2
- import { t as IdParamFailure } from "./adapter-types-oHCCSgOO.mjs";
1
+ import { t as Id } from "./types-g7CiQDyE.mjs";
2
+ import { r as IdParamFailure, t as IdCodec } from "./adapter-types-unUcmMXC.mjs";
3
3
  import { FastifyReply, FastifyRequest } from "fastify";
4
4
 
5
5
  //#region src/fastify.d.ts
6
- type IdCodec<Brand extends string> = {
7
- safeParse(value: unknown): ParseResult<Brand>;
8
- };
9
6
  /**
10
7
  * Typed error thrown into Fastify's `setErrorHandler` on validation failure.
11
8
  * Inspect `err.reason` and `err.statusCode` in your error handler.
@@ -47,6 +44,13 @@ type IdParamOptions = {
47
44
  *
48
45
  * On success, stores the canonical `Id<Brand>` in `request.params` under `paramName`.
49
46
  *
47
+ * **Return type note:** the returned hook is typed as
48
+ * `(request: FastifyRequest<{ Params: Record<string, Id<Brand>> }>, reply: FastifyReply) => Promise<void>`.
49
+ * Assigning it to a Fastify `preHandler` slot is backward-compatible (method-signature bivariance applies).
50
+ * However, a locally-annotated variable typed as the bare `(request: FastifyRequest, reply: FastifyReply) => Promise<void>`
51
+ * will produce a TypeScript error under `--strictFunctionTypes` because function parameter types are contravariant.
52
+ * Use `preHandler` assignment or let TypeScript infer the type to avoid this.
53
+ *
50
54
  * @example
51
55
  * ```ts
52
56
  * import { idParam, IdParamError } from "@smonn/ids/fastify";
@@ -82,7 +86,9 @@ type IdParamOptions = {
82
86
  * }, handler);
83
87
  * ```
84
88
  */
85
- declare function idParam<ParamKey extends string, Brand extends string>(paramName: ParamKey, codec: IdCodec<Brand>, options?: IdParamOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
89
+ declare function idParam<ParamKey extends string, Brand extends string>(paramName: ParamKey, codec: IdCodec<Brand>, options?: IdParamOptions): (request: FastifyRequest<{
90
+ Params: Record<string, Id<Brand>>;
91
+ }>, reply: FastifyReply) => Promise<void>;
86
92
  //#endregion
87
93
  export { IdParamError, type IdParamFailure, IdParamOptions, idParam };
88
94
  //# sourceMappingURL=fastify.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.d.mts","names":[],"sources":["../src/fastify.ts"],"mappings":";;;;;KAMK,OAAA;EACH,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;;;;cAO5B,YAAA,SAAqB,KAAA;EAAA,SACvB,UAAA;EAAA,SACA,MAAA;EAET,WAAA,CAAY,MAAA,kCAAwC,UAAA;AAAA;AAJtD;AAAA,KAaY,cAAA;;;;;EAKV,OAAA,IACE,OAAA,EAAS,cAAA,EACT,OAAA,EAAS,cAAA,EACT,KAAA,EAAO,YAAA,YACG,OAAA;;;;;EAKZ,MAAA;IAAW,cAAA;IAAyB,SAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;AAAA;AAsDtC;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIqD;;;;;;iBAJrC,OAAA,gDACd,SAAA,EAAW,QAAA,EACX,KAAA,EAAO,OAAA,CAAQ,KAAA,GACf,OAAA,GAAU,cAAA,IACR,OAAA,EAAS,cAAA,EAAgB,KAAA,EAAO,YAAA,KAAiB,OAAA"}
1
+ {"version":3,"file":"fastify.d.mts","names":[],"sources":["../src/fastify.ts"],"mappings":";;;;;;AAUA;;;cAAa,YAAA,SAAqB,KAAA;EAAA,SACvB,UAAA;EAAA,SACA,MAAA;EAET,WAAA,CAAY,MAAA,kCAAwC,UAAA;AAAA;;KAS1C,cAAA;EAT0C;AAAA;AAStD;;EAKE,OAAA,IACE,OAAA,EAAS,cAAA,EACT,OAAA,EAAS,cAAA,EACT,KAAA,EAAO,YAAA,YACG,OAAA;;;;;EAKZ,MAAA;IAAW,cAAA;IAAyB,SAAA;EAAA;AAAA;;;;;;;;;;AAAA;AA6DtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOK;;;;;;;;;;;;;;iBAPW,OAAA,gDACd,SAAA,EAAW,QAAA,EACX,KAAA,EAAO,OAAA,CAAQ,KAAA,GACf,OAAA,GAAU,cAAA,IAEV,OAAA,EAAS,cAAA;EAAiB,MAAA,EAAQ,MAAA,SAAe,EAAA,CAAG,KAAA;AAAA,IACpD,KAAA,EAAO,YAAA,KACJ,OAAA"}
package/dist/fastify.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { n as resolveIdParamFailure } from "./adapter-types-BY-wrYYB.mjs";
1
2
  //#region src/fastify.ts
2
3
  /**
3
4
  * Typed error thrown into Fastify's `setErrorHandler` on validation failure.
@@ -29,6 +30,13 @@ var IdParamError = class extends Error {
29
30
  *
30
31
  * On success, stores the canonical `Id<Brand>` in `request.params` under `paramName`.
31
32
  *
33
+ * **Return type note:** the returned hook is typed as
34
+ * `(request: FastifyRequest<{ Params: Record<string, Id<Brand>> }>, reply: FastifyReply) => Promise<void>`.
35
+ * Assigning it to a Fastify `preHandler` slot is backward-compatible (method-signature bivariance applies).
36
+ * However, a locally-annotated variable typed as the bare `(request: FastifyRequest, reply: FastifyReply) => Promise<void>`
37
+ * will produce a TypeScript error under `--strictFunctionTypes` because function parameter types are contravariant.
38
+ * Use `preHandler` assignment or let TypeScript infer the type to avoid this.
39
+ *
32
40
  * @example
33
41
  * ```ts
34
42
  * import { idParam, IdParamError } from "@smonn/ids/fastify";
@@ -69,18 +77,12 @@ function idParam(paramName, codec, options) {
69
77
  const raw = request.params[paramName];
70
78
  const result = codec.safeParse(raw);
71
79
  if (!result.ok) {
72
- const reason = result.error === "invalid_prefix" ? "brand_mismatch" : "malformed";
73
- const defaultStatus = reason === "brand_mismatch" ? 404 : 400;
74
- const status = options?.status?.[reason] ?? defaultStatus;
75
- const failure = {
76
- reason,
77
- status
78
- };
80
+ const failure = resolveIdParamFailure(result.error, options);
79
81
  if (options?.onError) {
80
82
  await options.onError(failure, request, reply);
81
83
  return;
82
84
  }
83
- throw new IdParamError(reason, status);
85
+ throw new IdParamError(failure.reason, failure.status);
84
86
  }
85
87
  request.params[paramName] = result.id;
86
88
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.mjs","names":[],"sources":["../src/fastify.ts"],"sourcesContent":["import type { FastifyReply, FastifyRequest } from \"fastify\";\nimport type { IdParamFailure } from \"./adapter-types.js\";\nimport type { Id, ParseResult } from \"./types.js\";\n\nexport type { IdParamFailure };\n\ntype IdCodec<Brand extends string> = {\n safeParse(value: unknown): ParseResult<Brand>;\n};\n\n/**\n * Typed error thrown into Fastify's `setErrorHandler` on validation failure.\n * Inspect `err.reason` and `err.statusCode` in your error handler.\n */\nexport class IdParamError extends Error {\n readonly statusCode: number;\n readonly reason: \"brand_mismatch\" | \"malformed\";\n\n constructor(reason: \"brand_mismatch\" | \"malformed\", statusCode: number) {\n super(`ID validation failed: ${reason}`);\n this.name = \"IdParamError\";\n this.reason = reason;\n this.statusCode = statusCode;\n }\n}\n\n/** Options for `idParam`. All fields are optional. */\nexport type IdParamOptions = {\n /**\n * Called instead of throwing when provided. The hook owns the response entirely —\n * the adapter does not throw.\n */\n onError?: (\n failure: IdParamFailure,\n request: FastifyRequest,\n reply: FastifyReply,\n ) => void | Promise<void>;\n /**\n * Remap the default HTTP status for a failure reason without a full handler.\n * e.g. `{ brand_mismatch: 400 }` treats both failure kinds as 400.\n */\n status?: { brand_mismatch?: number; malformed?: number };\n};\n\n/**\n * Fastify `preHandler` hook factory that validates a named route param against a codec via `safeParse`.\n *\n * **Default (no options):** throws `IdParamError` carrying `statusCode` and `reason` so the app's\n * existing `setErrorHandler` controls rendering. The adapter does not write a response body itself.\n *\n * **`options.onError`:** when provided, the hook calls `onError` and does not throw; the consumer\n * fully owns the response via `reply`.\n *\n * **`options.status`:** remaps the default HTTP status for a reason without a full handler.\n *\n * - **Brand mismatch (`invalid_prefix`) → `reason: \"brand_mismatch\"`, default 404**\n * - **Malformed or missing ID → `reason: \"malformed\"`, default 400**\n *\n * On success, stores the canonical `Id<Brand>` in `request.params` under `paramName`.\n *\n * @example\n * ```ts\n * import { idParam, IdParamError } from \"@smonn/ids/fastify\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // Default: throws IdParamError → setErrorHandler renders it\n * fastify.get(\"/users/:id\", { preHandler: idParam(\"id\", usr) }, (request, reply) => {\n * const id = request.params.id; // Id<\"usr\">, canonical\n * });\n *\n * // Error handler receives the typed error\n * fastify.setErrorHandler((err, request, reply) => {\n * if (err instanceof IdParamError) {\n * reply.status(err.statusCode).send({ error: err.reason });\n * return;\n * }\n * reply.send(err);\n * });\n *\n * // Override: consumer fully owns the error response\n * fastify.get(\"/orgs/:id\", {\n * preHandler: idParam(\"id\", org, {\n * onError: (failure, request, reply) =>\n * reply.status(failure.status).send({ error: failure.reason }),\n * }),\n * }, handler);\n *\n * // Or a lightweight status remap without a full handler\n * fastify.get(\"/things/:id\", {\n * preHandler: idParam(\"id\", thing, { status: { brand_mismatch: 400 } }),\n * }, handler);\n * ```\n */\nexport function idParam<ParamKey extends string, Brand extends string>(\n paramName: ParamKey,\n codec: IdCodec<Brand>,\n options?: IdParamOptions,\n): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {\n return async (request, reply): Promise<void> => {\n const raw = (request.params as Record<ParamKey, unknown>)[paramName];\n const result = codec.safeParse(raw);\n if (!result.ok) {\n const reason =\n result.error === \"invalid_prefix\" ? (\"brand_mismatch\" as const) : (\"malformed\" as const);\n const defaultStatus = reason === \"brand_mismatch\" ? 404 : 400;\n const status = options?.status?.[reason] ?? defaultStatus;\n const failure: IdParamFailure = { reason, status };\n if (options?.onError) {\n await options.onError(failure, request, reply);\n return;\n }\n throw new IdParamError(reason, status);\n }\n (request.params as Record<ParamKey, Id<Brand>>)[paramName] = result.id;\n };\n}\n"],"mappings":";;;;;AAcA,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CAEA,YAAY,QAAwC,YAAoB;EACtE,MAAM,yBAAyB,QAAQ;EACvC,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,aAAa;CACpB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuEA,SAAgB,QACd,WACA,OACA,SACiE;CACjE,OAAO,OAAO,SAAS,UAAyB;EAC9C,MAAM,MAAO,QAAQ,OAAqC;EAC1D,MAAM,SAAS,MAAM,UAAU,GAAG;EAClC,IAAI,CAAC,OAAO,IAAI;GACd,MAAM,SACJ,OAAO,UAAU,mBAAoB,mBAA8B;GACrE,MAAM,gBAAgB,WAAW,mBAAmB,MAAM;GAC1D,MAAM,SAAS,SAAS,SAAS,WAAW;GAC5C,MAAM,UAA0B;IAAE;IAAQ;GAAO;GACjD,IAAI,SAAS,SAAS;IACpB,MAAM,QAAQ,QAAQ,SAAS,SAAS,KAAK;IAC7C;GACF;GACA,MAAM,IAAI,aAAa,QAAQ,MAAM;EACvC;EACA,QAAS,OAAuC,aAAa,OAAO;CACtE;AACF"}
1
+ {"version":3,"file":"fastify.mjs","names":[],"sources":["../src/fastify.ts"],"sourcesContent":["import type { FastifyReply, FastifyRequest } from \"fastify\";\nimport { type IdCodec, type IdParamFailure, resolveIdParamFailure } from \"./adapter-types.js\";\nimport type { Id } from \"./types.js\";\n\nexport type { IdParamFailure };\n\n/**\n * Typed error thrown into Fastify's `setErrorHandler` on validation failure.\n * Inspect `err.reason` and `err.statusCode` in your error handler.\n */\nexport class IdParamError extends Error {\n readonly statusCode: number;\n readonly reason: \"brand_mismatch\" | \"malformed\";\n\n constructor(reason: \"brand_mismatch\" | \"malformed\", statusCode: number) {\n super(`ID validation failed: ${reason}`);\n this.name = \"IdParamError\";\n this.reason = reason;\n this.statusCode = statusCode;\n }\n}\n\n/** Options for `idParam`. All fields are optional. */\nexport type IdParamOptions = {\n /**\n * Called instead of throwing when provided. The hook owns the response entirely —\n * the adapter does not throw.\n */\n onError?: (\n failure: IdParamFailure,\n request: FastifyRequest,\n reply: FastifyReply,\n ) => void | Promise<void>;\n /**\n * Remap the default HTTP status for a failure reason without a full handler.\n * e.g. `{ brand_mismatch: 400 }` treats both failure kinds as 400.\n */\n status?: { brand_mismatch?: number; malformed?: number };\n};\n\n/**\n * Fastify `preHandler` hook factory that validates a named route param against a codec via `safeParse`.\n *\n * **Default (no options):** throws `IdParamError` carrying `statusCode` and `reason` so the app's\n * existing `setErrorHandler` controls rendering. The adapter does not write a response body itself.\n *\n * **`options.onError`:** when provided, the hook calls `onError` and does not throw; the consumer\n * fully owns the response via `reply`.\n *\n * **`options.status`:** remaps the default HTTP status for a reason without a full handler.\n *\n * - **Brand mismatch (`invalid_prefix`) → `reason: \"brand_mismatch\"`, default 404**\n * - **Malformed or missing ID → `reason: \"malformed\"`, default 400**\n *\n * On success, stores the canonical `Id<Brand>` in `request.params` under `paramName`.\n *\n * **Return type note:** the returned hook is typed as\n * `(request: FastifyRequest<{ Params: Record<string, Id<Brand>> }>, reply: FastifyReply) => Promise<void>`.\n * Assigning it to a Fastify `preHandler` slot is backward-compatible (method-signature bivariance applies).\n * However, a locally-annotated variable typed as the bare `(request: FastifyRequest, reply: FastifyReply) => Promise<void>`\n * will produce a TypeScript error under `--strictFunctionTypes` because function parameter types are contravariant.\n * Use `preHandler` assignment or let TypeScript infer the type to avoid this.\n *\n * @example\n * ```ts\n * import { idParam, IdParamError } from \"@smonn/ids/fastify\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // Default: throws IdParamError → setErrorHandler renders it\n * fastify.get(\"/users/:id\", { preHandler: idParam(\"id\", usr) }, (request, reply) => {\n * const id = request.params.id; // Id<\"usr\">, canonical\n * });\n *\n * // Error handler receives the typed error\n * fastify.setErrorHandler((err, request, reply) => {\n * if (err instanceof IdParamError) {\n * reply.status(err.statusCode).send({ error: err.reason });\n * return;\n * }\n * reply.send(err);\n * });\n *\n * // Override: consumer fully owns the error response\n * fastify.get(\"/orgs/:id\", {\n * preHandler: idParam(\"id\", org, {\n * onError: (failure, request, reply) =>\n * reply.status(failure.status).send({ error: failure.reason }),\n * }),\n * }, handler);\n *\n * // Or a lightweight status remap without a full handler\n * fastify.get(\"/things/:id\", {\n * preHandler: idParam(\"id\", thing, { status: { brand_mismatch: 400 } }),\n * }, handler);\n * ```\n */\nexport function idParam<ParamKey extends string, Brand extends string>(\n paramName: ParamKey,\n codec: IdCodec<Brand>,\n options?: IdParamOptions,\n): (\n request: FastifyRequest<{ Params: Record<string, Id<Brand>> }>,\n reply: FastifyReply,\n) => Promise<void> {\n return async (request, reply): Promise<void> => {\n const raw = request.params[paramName];\n const result = codec.safeParse(raw);\n if (!result.ok) {\n const failure = resolveIdParamFailure(result.error, options);\n if (options?.onError) {\n await options.onError(failure, request, reply);\n return;\n }\n throw new IdParamError(failure.reason, failure.status);\n }\n request.params[paramName] = result.id;\n };\n}\n"],"mappings":";;;;;;AAUA,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CAEA,YAAY,QAAwC,YAAoB;EACtE,MAAM,yBAAyB,QAAQ;EACvC,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,aAAa;CACpB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8EA,SAAgB,QACd,WACA,OACA,SAIiB;CACjB,OAAO,OAAO,SAAS,UAAyB;EAC9C,MAAM,MAAM,QAAQ,OAAO;EAC3B,MAAM,SAAS,MAAM,UAAU,GAAG;EAClC,IAAI,CAAC,OAAO,IAAI;GACd,MAAM,UAAU,sBAAsB,OAAO,OAAO,OAAO;GAC3D,IAAI,SAAS,SAAS;IACpB,MAAM,QAAQ,QAAQ,SAAS,SAAS,KAAK;IAC7C;GACF;GACA,MAAM,IAAI,aAAa,QAAQ,QAAQ,QAAQ,MAAM;EACvD;EACA,QAAQ,OAAO,aAAa,OAAO;CACrC;AACF"}
package/dist/hono.d.mts CHANGED
@@ -1,11 +1,8 @@
1
- import { i as ParseResult, t as Id } from "./types-g7CiQDyE.mjs";
2
- import { t as IdParamFailure } from "./adapter-types-oHCCSgOO.mjs";
1
+ import { t as Id } from "./types-g7CiQDyE.mjs";
2
+ import { r as IdParamFailure, t as IdCodec } from "./adapter-types-unUcmMXC.mjs";
3
3
  import { Context, MiddlewareHandler } from "hono";
4
4
 
5
5
  //#region src/hono.d.ts
6
- type IdCodec<Brand extends string> = {
7
- safeParse(value: unknown): ParseResult<Brand>;
8
- };
9
6
  /** Options for `idParam`. All fields are optional. */
10
7
  type IdParamOptions = {
11
8
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"hono.d.mts","names":[],"sources":["../src/hono.ts"],"mappings":";;;;;KAOK,OAAA;EACH,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;;KAI7B,cAAA;;;;;EAKV,OAAA,IAAW,OAAA,EAAS,cAAA,EAAgB,CAAA,EAAG,OAAA,KAAY,QAAA,GAAW,OAAA,CAAQ,QAAA;EAT/B;AAAA;AAIzC;;EAUE,MAAA;IAAW,cAAA;IAAyB,SAAA;EAAA;AAAA;;;;;;;;;;;;;;;;AAAA;AAyCtC;;;;;;;;;;;;;;;;;;;;;;iBAAgB,OAAA,gDACd,SAAA,EAAW,QAAA,EACX,KAAA,EAAO,OAAA,CAAQ,KAAA,GACf,OAAA,GAAU,cAAA,GACT,iBAAA;EAAoB,SAAA,EAAW,MAAA,CAAO,QAAA,EAAU,EAAA,CAAG,KAAA;AAAA"}
1
+ {"version":3,"file":"hono.d.mts","names":[],"sources":["../src/hono.ts"],"mappings":";;;;;;KAQY,cAAA;EAAA;;;;EAKV,OAAA,IAAW,OAAA,EAAS,cAAA,EAAgB,CAAA,EAAG,OAAA,KAAY,QAAA,GAAW,OAAA,CAAQ,QAAA;;;;;EAKtE,MAAA;IAAW,cAAA;IAAyB,SAAA;EAAA;AAAA;;;;;;;;AAAA;AAyCtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,OAAA,gDACd,SAAA,EAAW,QAAA,EACX,KAAA,EAAO,OAAA,CAAQ,KAAA,GACf,OAAA,GAAU,cAAA,GACT,iBAAA;EAAoB,SAAA,EAAW,MAAA,CAAO,QAAA,EAAU,EAAA,CAAG,KAAA;AAAA"}
package/dist/hono.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { n as resolveIdParamFailure } from "./adapter-types-BY-wrYYB.mjs";
1
2
  import { HTTPException } from "hono/http-exception";
2
3
  //#region src/hono.ts
3
4
  /**
@@ -43,15 +44,9 @@ function idParam(paramName, codec, options) {
43
44
  const raw = c.req.param(paramName);
44
45
  const result = codec.safeParse(raw);
45
46
  if (!result.ok) {
46
- const reason = result.error === "invalid_prefix" ? "brand_mismatch" : "malformed";
47
- const defaultStatus = reason === "brand_mismatch" ? 404 : 400;
48
- const status = options?.status?.[reason] ?? defaultStatus;
49
- const failure = {
50
- reason,
51
- status
52
- };
47
+ const failure = resolveIdParamFailure(result.error, options);
53
48
  if (options?.onError) return options.onError(failure, c);
54
- throw new HTTPException(status);
49
+ throw new HTTPException(failure.status);
55
50
  }
56
51
  c.set(paramName, result.id);
57
52
  await next();
package/dist/hono.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hono.mjs","names":[],"sources":["../src/hono.ts"],"sourcesContent":["import { HTTPException } from \"hono/http-exception\";\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport type { IdParamFailure } from \"./adapter-types.js\";\nimport type { Id, ParseResult } from \"./types.js\";\n\nexport type { IdParamFailure };\n\ntype IdCodec<Brand extends string> = {\n safeParse(value: unknown): ParseResult<Brand>;\n};\n\n/** Options for `idParam`. All fields are optional. */\nexport type IdParamOptions = {\n /**\n * Called instead of throwing when provided. The hook owns the response entirely —\n * the adapter neither throws nor writes a body.\n */\n onError?: (failure: IdParamFailure, c: Context) => Response | Promise<Response>;\n /**\n * Remap the default HTTP status for a failure reason without a full handler.\n * e.g. `{ brand_mismatch: 400 }` treats both failure kinds as 400.\n */\n status?: { brand_mismatch?: number; malformed?: number };\n};\n\n/**\n * Hono middleware that validates a named route param against a codec via `safeParse`.\n *\n * **Default (no options):** throws `HTTPException(status)` so the app's existing `onError` handler\n * controls rendering and content negotiation. The adapter does not write a response body itself.\n *\n * **`options.onError`:** when provided, the hook owns the response entirely — the adapter neither\n * throws nor writes a response.\n *\n * **`options.status`:** remaps the default HTTP status for a reason without a full handler.\n *\n * - **Brand mismatch (`invalid_prefix`) → `reason: \"brand_mismatch\"`, default 404**\n * - **Malformed or missing ID → `reason: \"malformed\"`, default 400**\n *\n * On success, stores the canonical `Id<Brand>` in the Hono context under `paramName`\n * and calls `next()`.\n *\n * @example\n * ```ts\n * import { idParam } from \"@smonn/ids/hono\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // Default: throws HTTPException → app.onError renders it\n * app.get(\"/users/:id\", idParam(\"id\", usr), (c) => {\n * const id = c.get(\"id\"); // Id<\"usr\">, canonical\n * });\n *\n * // Override: consumer fully owns the response\n * app.get(\"/orgs/:id\", idParam(\"id\", org, {\n * onError: (failure, c) => c.json({ error: failure.reason }, failure.status),\n * }), handler);\n *\n * // Or a lightweight status remap without a full handler\n * app.get(\"/things/:id\", idParam(\"id\", thing, { status: { brand_mismatch: 400 } }), handler);\n * ```\n */\nexport function idParam<ParamKey extends string, Brand extends string>(\n paramName: ParamKey,\n codec: IdCodec<Brand>,\n options?: IdParamOptions,\n): MiddlewareHandler<{ Variables: Record<ParamKey, Id<Brand>> }> {\n return async (c, next) => {\n const raw = c.req.param(paramName);\n const result = codec.safeParse(raw);\n if (!result.ok) {\n const reason =\n result.error === \"invalid_prefix\" ? (\"brand_mismatch\" as const) : (\"malformed\" as const);\n const defaultStatus = reason === \"brand_mismatch\" ? 404 : 400;\n const status = options?.status?.[reason] ?? defaultStatus;\n const failure: IdParamFailure = { reason, status };\n if (options?.onError) {\n return options.onError(failure, c);\n }\n throw new HTTPException(status as ConstructorParameters<typeof HTTPException>[0]);\n }\n c.set(paramName, result.id);\n await next();\n return;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,QACd,WACA,OACA,SAC+D;CAC/D,OAAO,OAAO,GAAG,SAAS;EACxB,MAAM,MAAM,EAAE,IAAI,MAAM,SAAS;EACjC,MAAM,SAAS,MAAM,UAAU,GAAG;EAClC,IAAI,CAAC,OAAO,IAAI;GACd,MAAM,SACJ,OAAO,UAAU,mBAAoB,mBAA8B;GACrE,MAAM,gBAAgB,WAAW,mBAAmB,MAAM;GAC1D,MAAM,SAAS,SAAS,SAAS,WAAW;GAC5C,MAAM,UAA0B;IAAE;IAAQ;GAAO;GACjD,IAAI,SAAS,SACX,OAAO,QAAQ,QAAQ,SAAS,CAAC;GAEnC,MAAM,IAAI,cAAc,MAAwD;EAClF;EACA,EAAE,IAAI,WAAW,OAAO,EAAE;EAC1B,MAAM,KAAK;CAEb;AACF"}
1
+ {"version":3,"file":"hono.mjs","names":[],"sources":["../src/hono.ts"],"sourcesContent":["import { HTTPException } from \"hono/http-exception\";\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport { type IdCodec, type IdParamFailure, resolveIdParamFailure } from \"./adapter-types.js\";\nimport type { Id } from \"./types.js\";\n\nexport type { IdParamFailure };\n\n/** Options for `idParam`. All fields are optional. */\nexport type IdParamOptions = {\n /**\n * Called instead of throwing when provided. The hook owns the response entirely —\n * the adapter neither throws nor writes a body.\n */\n onError?: (failure: IdParamFailure, c: Context) => Response | Promise<Response>;\n /**\n * Remap the default HTTP status for a failure reason without a full handler.\n * e.g. `{ brand_mismatch: 400 }` treats both failure kinds as 400.\n */\n status?: { brand_mismatch?: number; malformed?: number };\n};\n\n/**\n * Hono middleware that validates a named route param against a codec via `safeParse`.\n *\n * **Default (no options):** throws `HTTPException(status)` so the app's existing `onError` handler\n * controls rendering and content negotiation. The adapter does not write a response body itself.\n *\n * **`options.onError`:** when provided, the hook owns the response entirely — the adapter neither\n * throws nor writes a response.\n *\n * **`options.status`:** remaps the default HTTP status for a reason without a full handler.\n *\n * - **Brand mismatch (`invalid_prefix`) → `reason: \"brand_mismatch\"`, default 404**\n * - **Malformed or missing ID → `reason: \"malformed\"`, default 400**\n *\n * On success, stores the canonical `Id<Brand>` in the Hono context under `paramName`\n * and calls `next()`.\n *\n * @example\n * ```ts\n * import { idParam } from \"@smonn/ids/hono\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // Default: throws HTTPException → app.onError renders it\n * app.get(\"/users/:id\", idParam(\"id\", usr), (c) => {\n * const id = c.get(\"id\"); // Id<\"usr\">, canonical\n * });\n *\n * // Override: consumer fully owns the response\n * app.get(\"/orgs/:id\", idParam(\"id\", org, {\n * onError: (failure, c) => c.json({ error: failure.reason }, failure.status),\n * }), handler);\n *\n * // Or a lightweight status remap without a full handler\n * app.get(\"/things/:id\", idParam(\"id\", thing, { status: { brand_mismatch: 400 } }), handler);\n * ```\n */\nexport function idParam<ParamKey extends string, Brand extends string>(\n paramName: ParamKey,\n codec: IdCodec<Brand>,\n options?: IdParamOptions,\n): MiddlewareHandler<{ Variables: Record<ParamKey, Id<Brand>> }> {\n return async (c, next) => {\n const raw = c.req.param(paramName);\n const result = codec.safeParse(raw);\n if (!result.ok) {\n const failure = resolveIdParamFailure(result.error, options);\n if (options?.onError) {\n return options.onError(failure, c);\n }\n throw new HTTPException(failure.status as ConstructorParameters<typeof HTTPException>[0]);\n }\n c.set(paramName, result.id);\n await next();\n return;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,SAAgB,QACd,WACA,OACA,SAC+D;CAC/D,OAAO,OAAO,GAAG,SAAS;EACxB,MAAM,MAAM,EAAE,IAAI,MAAM,SAAS;EACjC,MAAM,SAAS,MAAM,UAAU,GAAG;EAClC,IAAI,CAAC,OAAO,IAAI;GACd,MAAM,UAAU,sBAAsB,OAAO,OAAO,OAAO;GAC3D,IAAI,SAAS,SACX,OAAO,QAAQ,QAAQ,SAAS,CAAC;GAEnC,MAAM,IAAI,cAAc,QAAQ,MAAwD;EAC1F;EACA,EAAE,IAAI,WAAW,OAAO,EAAE;EAC1B,MAAM,KAAK;CAEb;AACF"}
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-B5_UCzc6.mjs";
2
+ import { t as createTimestampId } from "./timestamp-BjIMQkJf.mjs";
3
3
  export { IdsError, createTimestampId, isIdsError };
@@ -0,0 +1,137 @@
1
+ import { t as IdsError } from "./error-Cp5qYZcv.mjs";
2
+ //#region src/bytes.ts
3
+ const hexDigits = "0123456789abcdef";
4
+ const invalidNibble = 255;
5
+ const hexCharCodeToNibble = new Uint8Array(128).fill(invalidNibble);
6
+ for (let i = 0; i < 10; i++) hexCharCodeToNibble[48 + i] = i;
7
+ for (let i = 0; i < 6; i++) {
8
+ hexCharCodeToNibble[97 + i] = 10 + i;
9
+ hexCharCodeToNibble[65 + i] = 10 + i;
10
+ }
11
+ /** Lowercase hex encoding of raw bytes. */
12
+ function encodeHex(bytes) {
13
+ const codes = new Array(bytes.length * 2);
14
+ for (let i = 0; i < bytes.length; i++) {
15
+ const b = bytes[i];
16
+ codes[i * 2] = hexDigits.charCodeAt(b >>> 4);
17
+ codes[i * 2 + 1] = hexDigits.charCodeAt(b & 15);
18
+ }
19
+ return String.fromCharCode(...codes);
20
+ }
21
+ /** Decodes a hex string to raw bytes. Throws on non-hex input. */
22
+ function decodeHex(encoded) {
23
+ if (encoded.length % 2 !== 0) throw new Error("invalid hex");
24
+ const out = new Uint8Array(encoded.length / 2);
25
+ for (let i = 0; i < out.length; i++) {
26
+ const hiCode = encoded.charCodeAt(i * 2);
27
+ const loCode = encoded.charCodeAt(i * 2 + 1);
28
+ if (hiCode >= hexCharCodeToNibble.length || loCode >= hexCharCodeToNibble.length) throw new Error("invalid hex");
29
+ const hi = hexCharCodeToNibble[hiCode];
30
+ const lo = hexCharCodeToNibble[loCode];
31
+ if (hi === invalidNibble || lo === invalidNibble) throw new Error("invalid hex");
32
+ out[i] = hi << 4 | lo;
33
+ }
34
+ return out;
35
+ }
36
+ /** Base64url encoding without padding. */
37
+ function encodeBase64Url(bytes) {
38
+ let binary = "";
39
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
40
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
41
+ }
42
+ /** Decodes a base64url string to raw bytes. Throws on invalid input. */
43
+ function decodeBase64Url(encoded) {
44
+ const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
45
+ const pad = (4 - base64.length % 4) % 4;
46
+ const binary = atob(base64 + "=".repeat(pad));
47
+ const out = new Uint8Array(binary.length);
48
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
49
+ return out;
50
+ }
51
+ //#endregion
52
+ //#region src/key-material.ts
53
+ const validByteLengths = new Set([
54
+ 16,
55
+ 24,
56
+ 32
57
+ ]);
58
+ function formatForError(value) {
59
+ try {
60
+ return String(value);
61
+ } catch {
62
+ return "[unprintable]";
63
+ }
64
+ }
65
+ function assertKeyMaterialFormat(format, noun) {
66
+ if (format !== "hex" && format !== "base64url") throw new IdsError("invalid_key_format", `invalid ${noun} key format: expected hex or base64url, got '${formatForError(format)}'`);
67
+ }
68
+ /**
69
+ * Throws `empty_keyring` when `keys` is empty.
70
+ * `noun` appears in the message (e.g. `"signing"` → "signing keyring must contain at least one key").
71
+ */
72
+ function assertNonEmptyKeyring(keys, noun) {
73
+ if (keys.length === 0) throw new IdsError("empty_keyring", `${noun} keyring must contain at least one key`);
74
+ }
75
+ /**
76
+ * Throws `duplicate_keyring_entry` when any two entries in `keys` compare equal.
77
+ * Uses the caller-supplied constant-time `keysEqual` comparator.
78
+ */
79
+ function assertNoDuplicateKeyringEntries(keys, keysEqual, noun) {
80
+ for (let i = 0; i < keys.length; i++) for (let j = i + 1; j < keys.length; j++) if (keysEqual(keys[i], keys[j])) throw new IdsError("duplicate_keyring_entry", `duplicate ${noun} key in keyring`);
81
+ }
82
+ /**
83
+ * Asserts that `keys` is non-empty and contains no pairwise duplicates.
84
+ *
85
+ * Combines {@link assertNonEmptyKeyring} and {@link assertNoDuplicateKeyringEntries}
86
+ * into a single call for codec constructors that validate a keyring at construction.
87
+ *
88
+ * @param keys - The keyring to validate.
89
+ * @param keysEqual - Constant-time comparator (e.g. `wrappingKeysEqual`, `signingKeysEqual`).
90
+ * @param noun - Noun used in error messages (e.g. `"wrapping"`, `"signing"`).
91
+ */
92
+ function assertValidKeyring(keys, keysEqual, noun) {
93
+ assertNonEmptyKeyring(keys, noun);
94
+ assertNoDuplicateKeyringEntries(keys, keysEqual, noun);
95
+ }
96
+ /** Throws `invalid_key_length` when `byteLength` is not 16, 24, or 32. */
97
+ function assertValidKeyMaterialByteLength(byteLength, noun) {
98
+ if (!validByteLengths.has(byteLength)) throw new IdsError("invalid_key_length", `invalid ${noun} key length: expected 16, 24, or 32 bytes, got ${byteLength}`);
99
+ }
100
+ /**
101
+ * Encodes raw key bytes as hex or base64url.
102
+ *
103
+ * `formatNoun` appears in format error messages; `lengthNoun` in length error messages.
104
+ * For most key types both are the same (e.g. `"wrapping"`, `"signing"`). For the
105
+ * Opaque key, they differ (`"opaque"` and `"AES"` respectively) to preserve the
106
+ * original human-readable messages.
107
+ */
108
+ function encodeKeyMaterial(bytes, format, formatNoun, lengthNoun) {
109
+ assertKeyMaterialFormat(format, formatNoun);
110
+ assertValidKeyMaterialByteLength(bytes.length, lengthNoun);
111
+ if (format === "hex") return encodeHex(bytes);
112
+ return encodeBase64Url(bytes);
113
+ }
114
+ /**
115
+ * Decodes a hex or base64url-encoded key string back to raw bytes.
116
+ *
117
+ * `formatNoun` appears in format error messages; `lengthNoun` in length error messages.
118
+ */
119
+ function decodeKeyMaterial(encoded, format, formatNoun, lengthNoun) {
120
+ assertKeyMaterialFormat(format, formatNoun);
121
+ let bytes;
122
+ if (format === "hex") {
123
+ if (encoded.length === 0 || encoded.length % 2 !== 0) throw new IdsError("invalid_key_encoding", "invalid hex key: length must be a positive even number of characters");
124
+ if (!/^[0-9a-fA-F]+$/.test(encoded)) throw new IdsError("invalid_key_encoding", "invalid hex key: expected [0-9a-fA-F] only");
125
+ bytes = decodeHex(encoded);
126
+ } else try {
127
+ bytes = decodeBase64Url(encoded);
128
+ } catch {
129
+ throw new IdsError("invalid_key_encoding", "invalid base64url key");
130
+ }
131
+ assertValidKeyMaterialByteLength(bytes.length, lengthNoun);
132
+ return bytes;
133
+ }
134
+ //#endregion
135
+ export { encodeKeyMaterial as i, assertValidKeyring as n, decodeKeyMaterial as r, assertValidKeyMaterialByteLength as t };
136
+
137
+ //# sourceMappingURL=key-material-DUHhmMq-.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-material-DUHhmMq-.mjs","names":[],"sources":["../src/bytes.ts","../src/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/** 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 { 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
1
  import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-DTr4i6Ic.mjs";
2
2
  import { t as Id } from "./types-g7CiQDyE.mjs";
3
- import { t as IdColumnCodec } from "./drizzle-CeSni5PB.mjs";
3
+ import { n as IdColumnCodec } from "./adapter-types-unUcmMXC.mjs";
4
4
  import { ColumnType } from "kysely";
5
5
 
6
6
  //#region src/kysely.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"kysely.d.mts","names":[],"sources":["../src/kysely.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;KA0BY,YAAA,yBAAqC,UAAA,CAAW,EAAA,CAAG,KAAA,GAAQ,EAAA,CAAG,KAAA,GAAQ,EAAA,CAAG,KAAA;;;AAAA;AA2BrF;;;;;;;;;;;;;;;;;;;;;;iBAAgB,QAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA;EAErB,QAAA,CAAS,KAAA,EAAO,EAAA,CAAG,KAAA;EACnB,UAAA,CAAW,KAAA,WAAgB,EAAA,CAAG,KAAA;AAAA"}
1
+ {"version":3,"file":"kysely.d.mts","names":[],"sources":["../src/kysely.ts"],"mappings":";;;;;;AA0BA;;;;;;;;;;;;;;;;;AAAA,KAAY,YAAA,yBAAqC,UAAA,CAAW,EAAA,CAAG,KAAA,GAAQ,EAAA,CAAG,KAAA,GAAQ,EAAA,CAAG,KAAA;;;;AAAA;AA2BrF;;;;;;;;;;;;;;;;;;;;;iBAAgB,QAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA;EAErB,QAAA,CAAS,KAAA,EAAO,EAAA,CAAG,KAAA;EACnB,UAAA,CAAW,KAAA,WAAgB,EAAA,CAAG,KAAA;AAAA"}