@ingram-tech/nk-db 1.3.0 → 1.4.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.
package/README.md CHANGED
@@ -136,6 +136,50 @@ UUIDv7 minting (`uuidGenerateId`) plus a base58 "skin" for wire ids
136
136
  (`team_3nX…` ↔ stored UUID): `toPrefixedId` / `fromPrefixedId` / `base58Id`,
137
137
  and `createIdRegistry` for typed per-entity helpers.
138
138
 
139
+ **This module is isomorphic** — zero imports, randomness from Web Crypto — so a
140
+ Drizzle `schema.ts`, a client component or an edge runtime can all use it. Keep
141
+ it that way: a single `node:crypto` import makes every module that touches an id
142
+ node-only (a test enforces this).
143
+
144
+ Store the raw `uuid` and skin at the edge. The prefixed id is presentation, not
145
+ identity: inside the DB, "which entity is this?" is already carried by the
146
+ column, so storing the prefix duplicates schema metadata into every row and puts
147
+ codec bugs permanently on disk.
148
+
149
+ **Ids are self-describing, uuids are not.** A prefix names its entity, so
150
+ decoding needs no context — `entityOf(registry, id)` and `decodeAnyId(registry,
151
+ id)` resolve one from the value alone, which is what makes polymorphic FKs and
152
+ raw-SQL bindings work. The inverse does not hold: encoding always needs to know
153
+ which entity you meant. Automate decode; enforce encode with the `Id<E>` brand.
154
+
155
+ ### Drizzle bindings (`@ingram-tech/nk-db/id/drizzle`)
156
+
157
+ Decode public ids at the **column**, so a skinned id reaching a query stops being
158
+ a failure mode:
159
+
160
+ ```ts
161
+ export const ids = createIdRegistry({ invoice: "inv", account: "acct" });
162
+ export const { idColumn, polymorphicIdColumn, sqlUuid, sqlUuidArray } =
163
+ createIdColumns(ids);
164
+
165
+ export const invoices = pgTable("invoices", {
166
+ id: idColumn("invoice")().primaryKey(), // eq(invoices.id, "inv_…") just works
167
+ account_id: idColumn("account")().notNull(),
168
+ entity_id: polymorphicIdColumn(), // decodes any entity's id
169
+ });
170
+ ```
171
+
172
+ Drizzle runs `toDriver` on WHERE values (`eq`, `inArray`) as well as insert/update
173
+ `SET`, and `dataType` stays `uuid` — so there is **no DDL and no migration**, and
174
+ `drizzle-kit generate` reports no diff. There is deliberately no `fromDriver`
175
+ (see the encode asymmetry above).
176
+
177
+ Raw SQL and RPC args bypass the column layer, so bind those with `sqlUuid(id)` /
178
+ `sqlUuidArray(ids)` rather than a bare `${id}::uuid`.
179
+
180
+ This subpath pulls only `drizzle-orm` and the codec — never `pg` — because it is
181
+ imported by `schema.ts`.
182
+
139
183
  ## PGlite dev & test (`@ingram-tech/nk-db/pglite`)
140
184
 
141
185
  `nk dev` runs the `nk-pglite-dev` bin automatically when this package is
@@ -0,0 +1,82 @@
1
+ import { type SQL } from "drizzle-orm";
2
+ import { customType } from "drizzle-orm/pg-core";
3
+ import { type IdHelper } from "./id.js";
4
+ /**
5
+ * Drizzle bindings for the id codec (`@ingram-tech/nk-db/id/drizzle`).
6
+ *
7
+ * Deliberately a separate subpath from the package root: this is imported by a
8
+ * site's `schema.ts`, so it pulls only `drizzle-orm` and the (isomorphic) codec
9
+ * — never `pg`, the pool, or the RLS helpers that the root export carries.
10
+ *
11
+ * The problem it solves: when a site stores raw `uuid` but exposes prefixed
12
+ * public ids (`inv_…`), any id that reaches a query still skinned blows up with
13
+ * `invalid input syntax for type uuid`. Rather than decoding at every call site
14
+ * and hoping none are missed, decode once at the column.
15
+ */
16
+ type IdColumnBindings<K extends string> = {
17
+ /**
18
+ * A `uuid` column that decodes a public, prefixed id to its raw uuid *before*
19
+ * the value reaches Postgres.
20
+ *
21
+ * Drizzle runs `toDriver` on every JS→driver value: insert/update `SET`
22
+ * values **and** WHERE-clause comparison values (`eq`, `inArray`, …), so
23
+ * `eq(invoices.id, "inv_…")` is decoded transparently and feeding a skinned id
24
+ * into a typed query stops being a failure mode.
25
+ *
26
+ * `dataType` is unchanged (`uuid`), so this is a TypeScript-only mapping: no
27
+ * DDL, no migration, and `drizzle-kit generate` reports no schema diff.
28
+ *
29
+ * Decode is tolerant — a raw uuid or a non-id sentinel passes through, so
30
+ * adopting it never regresses a working call site. There is intentionally no
31
+ * `fromDriver`: reads stay raw uuids. Encoding is the caller's job, because
32
+ * (unlike decode) it cannot be inferred from the value — see `entityOf`.
33
+ */
34
+ idColumn: (entity: K) => ReturnType<typeof customType<IdColumnConfig>>;
35
+ /**
36
+ * A `uuid` column for a **polymorphic** FK — an `entity_id` whose target is
37
+ * named by a sibling `*_type` column — where no single-entity decoder applies.
38
+ * Decodes a prefixed id of any entity in the registry (ids are
39
+ * self-describing).
40
+ *
41
+ * A column cannot see its sibling, so this accepts an id of the *wrong*
42
+ * entity. Where both halves are in hand, check them (`entityOf(registry, id)
43
+ * === expectedType`) before writing, or the row is silently mislabelled.
44
+ */
45
+ polymorphicIdColumn: ReturnType<typeof customType<IdColumnConfig>>;
46
+ /**
47
+ * Bind an id into raw SQL as a `uuid`. Raw SQL and RPC args bypass the column
48
+ * layer entirely, so this is the sanctioned binding for them:
49
+ * `sql\`select f(${sqlUuid(id)})\``. Needs no entity because ids are
50
+ * self-describing; a raw uuid (the common case) passes through, and
51
+ * null/undefined binds as `null::uuid`.
52
+ */
53
+ sqlUuid: (value: string | null | undefined) => SQL;
54
+ /**
55
+ * Bind a list of ids as a `uuid[]`, for RPCs taking a `uuid[]` arg. Built
56
+ * element-wise on purpose: pg binds a JS array as `text[]`, and
57
+ * `text[]::uuid[]` is not a legal direct cast (SQLSTATE 42846).
58
+ */
59
+ sqlUuidArray: (values: readonly string[]) => SQL;
60
+ };
61
+ type IdColumnConfig = {
62
+ data: string;
63
+ driverData: string;
64
+ };
65
+ /**
66
+ * Build the Drizzle id bindings for a site's registry:
67
+ *
68
+ * ```ts
69
+ * // ids.ts
70
+ * export const ids = createIdRegistry({ invoice: "inv", account: "acct" });
71
+ * export const { idColumn, polymorphicIdColumn, sqlUuid } = createIdColumns(ids);
72
+ *
73
+ * // schema.ts
74
+ * export const invoices = pgTable("invoices", {
75
+ * id: idColumn("invoice")().primaryKey(),
76
+ * account_id: idColumn("account")().notNull(),
77
+ * });
78
+ * ```
79
+ */
80
+ export declare function createIdColumns<K extends string>(registry: Record<K, IdHelper>): IdColumnBindings<K>;
81
+ export {};
82
+ //# sourceMappingURL=id-drizzle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-drizzle.d.ts","sourceRoot":"","sources":["../src/id-drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAErD;;;;;;;;;;;GAWG;AAEH,KAAK,gBAAgB,CAAC,CAAC,SAAS,MAAM,IAAI;IACzC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,UAAU,CAAC,OAAO,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;IACvE;;;;;;;;;OASG;IACH,mBAAmB,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;IACnE;;;;;;OAMG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,GAAG,CAAC;IACnD;;;;OAIG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,KAAK,GAAG,CAAC;CACjD,CAAC;AAEF,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAC/C,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,GAC3B,gBAAgB,CAAC,CAAC,CAAC,CA0BrB"}
@@ -0,0 +1,37 @@
1
+ import { sql } from "drizzle-orm";
2
+ import { customType } from "drizzle-orm/pg-core";
3
+ import { decodeAnyId } from "./id.js";
4
+ /**
5
+ * Build the Drizzle id bindings for a site's registry:
6
+ *
7
+ * ```ts
8
+ * // ids.ts
9
+ * export const ids = createIdRegistry({ invoice: "inv", account: "acct" });
10
+ * export const { idColumn, polymorphicIdColumn, sqlUuid } = createIdColumns(ids);
11
+ *
12
+ * // schema.ts
13
+ * export const invoices = pgTable("invoices", {
14
+ * id: idColumn("invoice")().primaryKey(),
15
+ * account_id: idColumn("account")().notNull(),
16
+ * });
17
+ * ```
18
+ */
19
+ export function createIdColumns(registry) {
20
+ const decodeAny = (value) => decodeAnyId(registry, value);
21
+ const sqlUuid = (value) => sql `${value == null ? null : decodeAny(value)}::uuid`;
22
+ return {
23
+ idColumn: (entity) => customType({
24
+ dataType: () => "uuid",
25
+ toDriver: (value) => typeof value === "string"
26
+ ? (registry[entity].decodeOrNull(value) ?? value)
27
+ : value,
28
+ }),
29
+ polymorphicIdColumn: customType({
30
+ dataType: () => "uuid",
31
+ toDriver: (value) => (typeof value === "string" ? decodeAny(value) : value),
32
+ }),
33
+ sqlUuid,
34
+ sqlUuidArray: (values) => sql `array[${sql.join(values.map((value) => sqlUuid(value)), sql `, `)}]::uuid[]`,
35
+ };
36
+ }
37
+ //# sourceMappingURL=id-drizzle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-drizzle.js","sourceRoot":"","sources":["../src/id-drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAY,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAiB,MAAM,SAAS,CAAC;AA+DrD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAC9B,QAA6B;IAE7B,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAElE,MAAM,OAAO,GAAG,CAAC,KAAgC,EAAO,EAAE,CACzD,GAAG,CAAA,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC;IAEvD,OAAO;QACN,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CACpB,UAAU,CAAiB;YAC1B,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM;YACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CACnB,OAAO,KAAK,KAAK,QAAQ;gBACxB,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;gBACjD,CAAC,CAAC,KAAK;SACT,CAAC;QACH,mBAAmB,EAAE,UAAU,CAAiB;YAC/C,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM;YACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;SAC3E,CAAC;QACF,OAAO;QACP,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CACxB,GAAG,CAAA,SAAS,GAAG,CAAC,IAAI,CACnB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EACrC,GAAG,CAAA,IAAI,CACP,WAAW;KACb,CAAC;AACH,CAAC"}
package/dist/id.d.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  /**
2
2
  * The Ingram id codec — a UUIDv7 and its base58 skin. It lives here in nk-db
3
3
  * (the lowest package in the graph) behind the `@ingram-tech/nk-db/id` subpath,
4
- * so it stays `node:crypto`-only — no `pg` / `drizzle` — and any site can mint
5
- * ids without pulling a heavier slice.
4
+ * so it stays dependency-free — no `pg` / `drizzle` — and any site can mint ids
5
+ * without pulling a heavier slice.
6
+ *
7
+ * **This module is isomorphic and must stay that way.** It has no imports at
8
+ * all: randomness comes from Web Crypto (`crypto.getRandomValues`, a global on
9
+ * Node 19+, Bun, Deno, edge runtimes and browsers), not `node:crypto`. That is
10
+ * load-bearing, not incidental — a `node:crypto` import here makes every module
11
+ * that touches an id node-only, which in practice means a Drizzle `schema.ts`
12
+ * cannot encode/decode without dragging `node:crypto` into client bundles, and
13
+ * sites end up dependency-injecting the codec to route around it. Keep the
14
+ * import list empty.
6
15
  *
7
16
  * When a service has a non-JS twin of this codec (e.g. a Python API), keep its
8
17
  * byte → string vectors identical to the ones in `id.test.ts`, so a stored
@@ -23,7 +32,7 @@
23
32
  * `advanced.database.generateId`. Node/Bun's `randomUUID` is v4-only, so we lay
24
33
  * the bytes out by hand.
25
34
  */
26
- export declare const uuidGenerateId: () => string;
35
+ export declare const uuidGenerateId: () => Uuid;
27
36
  /** Skin a stored hyphenated UUIDv7 as a prefixed base58 id, e.g. `team_…`. */
28
37
  export declare function toPrefixedId(uuid: string, prefix: string): string;
29
38
  /** Inverse of {@link toPrefixedId}: recover the hyphenated UUIDv7. */
@@ -49,6 +58,14 @@ export type Id<E extends string> = string & {
49
58
  export type Uuid = string & {
50
59
  readonly [UUID_BRAND]: "Uuid";
51
60
  };
61
+ /**
62
+ * Whether `value` is a hyphenated uuid — the sanctioned string→{@link Uuid}
63
+ * bless: narrowing through the shape check is never a blind cast. Use at trust
64
+ * boundaries (env/config, external payloads, `crypto.randomUUID()`).
65
+ */
66
+ export declare const isUuid: (value: string | null | undefined) => value is Uuid;
67
+ /** The throwing {@link isUuid}: bless a string you require to be a uuid. */
68
+ export declare function asUuid(value: string): Uuid;
52
69
  /** Typed helpers for one entity's prefixed ids — built by {@link createIdRegistry}. */
53
70
  export interface IdHelper<E extends string = string> {
54
71
  /** This entity's prefix, e.g. `"agt"` (no trailing underscore). */
@@ -87,5 +104,31 @@ export type IdRegistry<K extends string> = {
87
104
  export declare function createIdRegistry<const T extends Record<string, string>>(prefixes: T): {
88
105
  [K in keyof T]: IdHelper;
89
106
  };
107
+ /**
108
+ * Which entity a prefixed id belongs to, or `null` if no helper in `registry`
109
+ * recognises it (a raw uuid, a sentinel, a foreign prefix).
110
+ *
111
+ * A public id is **self-describing**: its prefix names its entity, so resolving
112
+ * one needs no surrounding context. That is the property behind every
113
+ * entity-agnostic decode — a polymorphic FK whose target is named by a sibling
114
+ * `*_type` column, a raw-SQL binding that only has a value, a webhook payload.
115
+ * The inverse does **not** hold: a bare uuid carries no entity, so encoding
116
+ * always needs to know which entity you meant.
117
+ *
118
+ * Standalone rather than a registry method: the registry is keyed by entity
119
+ * name, so a method would collide with an entity literally called `entityOf`.
120
+ */
121
+ export declare function entityOf<K extends string>(registry: Record<K, IdHelper>, value: string): K | null;
122
+ /**
123
+ * Decode a prefixed id belonging to **any** entity in `registry` (see
124
+ * {@link entityOf}), for the boundaries that can't name one: polymorphic FKs,
125
+ * raw-SQL id bindings, generic audit/event payloads.
126
+ *
127
+ * Tolerant by design — a value no helper recognises is returned unchanged, so an
128
+ * already-decoded uuid passes straight through and this is safe to apply to a
129
+ * value that may or may not be skinned. Use a helper's `decode`/`decodeOrNull`
130
+ * when you know the entity and want a wrong prefix rejected.
131
+ */
132
+ export declare function decodeAnyId<K extends string>(registry: Record<K, IdHelper>, value: string): Uuid;
90
133
  export {};
91
134
  //# sourceMappingURL=id.d.ts.map
package/dist/id.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,QAAO,MAYjC,CAAC;AA0DF,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,OAAO,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;AACtC,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AAExC;;;;;GAKG;AACH,MAAM,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,uFAAuF;AACvF,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAClD,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5B,iFAAiF;IACjF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACtC,yFAAyF;IACzF,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CAC5B;AAED,kFAAkF;AAClF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI;KAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;CAAE,CAAC;AAmBrE;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtE,QAAQ,EAAE,CAAC,GACT;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ;CAAE,CAM9B"}
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,QAAO,IAYjC,CAAC;AA0DF,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,OAAO,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;AACtC,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AAExC;;;;;GAKG;AACH,MAAM,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAM9D;;;;GAIG;AACH,eAAO,MAAM,MAAM,UAAW,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,KAAK,IAAI,IAClB,CAAC;AAElD,4EAA4E;AAC5E,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG1C;AAED,uFAAuF;AACvF,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAClD,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5B,iFAAiF;IACjF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACtC,yFAAyF;IACzF,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CAC5B;AAED,kFAAkF;AAClF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI;KAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;CAAE,CAAC;AAmBrE;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtE,QAAQ,EAAE,CAAC,GACT;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ;CAAE,CAM9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EACxC,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,EAC7B,KAAK,EAAE,MAAM,GACX,CAAC,GAAG,IAAI,CAOV;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC3C,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,EAC7B,KAAK,EAAE,MAAM,GACX,IAAI,CAGN"}
package/dist/id.js CHANGED
@@ -1,9 +1,17 @@
1
- import { randomBytes } from "node:crypto";
2
1
  /**
3
2
  * The Ingram id codec — a UUIDv7 and its base58 skin. It lives here in nk-db
4
3
  * (the lowest package in the graph) behind the `@ingram-tech/nk-db/id` subpath,
5
- * so it stays `node:crypto`-only — no `pg` / `drizzle` — and any site can mint
6
- * ids without pulling a heavier slice.
4
+ * so it stays dependency-free — no `pg` / `drizzle` — and any site can mint ids
5
+ * without pulling a heavier slice.
6
+ *
7
+ * **This module is isomorphic and must stay that way.** It has no imports at
8
+ * all: randomness comes from Web Crypto (`crypto.getRandomValues`, a global on
9
+ * Node 19+, Bun, Deno, edge runtimes and browsers), not `node:crypto`. That is
10
+ * load-bearing, not incidental — a `node:crypto` import here makes every module
11
+ * that touches an id node-only, which in practice means a Drizzle `schema.ts`
12
+ * cannot encode/decode without dragging `node:crypto` into client bundles, and
13
+ * sites end up dependency-injecting the codec to route around it. Keep the
14
+ * import list empty.
7
15
  *
8
16
  * When a service has a non-JS twin of this codec (e.g. a Python API), keep its
9
17
  * byte → string vectors identical to the ones in `id.test.ts`, so a stored
@@ -25,7 +33,7 @@ import { randomBytes } from "node:crypto";
25
33
  * the bytes out by hand.
26
34
  */
27
35
  export const uuidGenerateId = () => {
28
- const bytes = randomBytes(16);
36
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
29
37
  const ts = Date.now();
30
38
  bytes[0] = Math.floor(ts / 2 ** 40) & 0xff;
31
39
  bytes[1] = Math.floor(ts / 2 ** 32) & 0xff;
@@ -35,7 +43,7 @@ export const uuidGenerateId = () => {
35
43
  bytes[5] = ts & 0xff;
36
44
  bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x70; // version 7
37
45
  bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80; // variant 10
38
- return bytesToUuid(bytes);
46
+ return bytesToUuid(bytes); // mint site
39
47
  };
40
48
  const B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
41
49
  // ceil(128 / log2(58)): a 16-byte value never needs more than 22 digits. We
@@ -105,6 +113,20 @@ export function fromPrefixedId(id) {
105
113
  export function base58Id(prefix) {
106
114
  return toPrefixedId(uuidGenerateId(), prefix);
107
115
  }
116
+ // Any RFC 9562 version (1-8): sites mint v7, but migrated rows are often v4.
117
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
118
+ /**
119
+ * Whether `value` is a hyphenated uuid — the sanctioned string→{@link Uuid}
120
+ * bless: narrowing through the shape check is never a blind cast. Use at trust
121
+ * boundaries (env/config, external payloads, `crypto.randomUUID()`).
122
+ */
123
+ export const isUuid = (value) => typeof value === "string" && UUID_RE.test(value);
124
+ /** The throwing {@link isUuid}: bless a string you require to be a uuid. */
125
+ export function asUuid(value) {
126
+ if (!isUuid(value))
127
+ throw new Error(`not a uuid: ${value}`);
128
+ return value;
129
+ }
108
130
  function makeHelper(prefix) {
109
131
  // Prefixes are developer-controlled identifier constants, never user input.
110
132
  const matcher = new RegExp(`^${prefix}_${BODY}$`);
@@ -141,4 +163,42 @@ export function createIdRegistry(prefixes) {
141
163
  }
142
164
  return reg;
143
165
  }
166
+ /**
167
+ * Which entity a prefixed id belongs to, or `null` if no helper in `registry`
168
+ * recognises it (a raw uuid, a sentinel, a foreign prefix).
169
+ *
170
+ * A public id is **self-describing**: its prefix names its entity, so resolving
171
+ * one needs no surrounding context. That is the property behind every
172
+ * entity-agnostic decode — a polymorphic FK whose target is named by a sibling
173
+ * `*_type` column, a raw-SQL binding that only has a value, a webhook payload.
174
+ * The inverse does **not** hold: a bare uuid carries no entity, so encoding
175
+ * always needs to know which entity you meant.
176
+ *
177
+ * Standalone rather than a registry method: the registry is keyed by entity
178
+ * name, so a method would collide with an entity literally called `entityOf`.
179
+ */
180
+ export function entityOf(registry, value) {
181
+ // Cheap reject: every prefixed id has one, and raw uuids never do.
182
+ if (!value.includes("_"))
183
+ return null;
184
+ for (const entity of Object.keys(registry)) {
185
+ if (registry[entity].is(value))
186
+ return entity;
187
+ }
188
+ return null;
189
+ }
190
+ /**
191
+ * Decode a prefixed id belonging to **any** entity in `registry` (see
192
+ * {@link entityOf}), for the boundaries that can't name one: polymorphic FKs,
193
+ * raw-SQL id bindings, generic audit/event payloads.
194
+ *
195
+ * Tolerant by design — a value no helper recognises is returned unchanged, so an
196
+ * already-decoded uuid passes straight through and this is safe to apply to a
197
+ * value that may or may not be skinned. Use a helper's `decode`/`decodeOrNull`
198
+ * when you know the entity and want a wrong prefix rejected.
199
+ */
200
+ export function decodeAnyId(registry, value) {
201
+ const entity = entityOf(registry, value);
202
+ return entity === null ? value : registry[entity].decode(value);
203
+ }
144
204
  //# sourceMappingURL=id.js.map
package/dist/id.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAS,EAAE;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;IAC1C,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,YAAY;IACxD,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,aAAa;IACzD,OAAO,WAAW,CAAC,KAAK,CAAS,CAAC,CAAC,YAAY;AAChD,CAAC,CAAC;AAEF,MAAM,GAAG,GAAG,4DAA4D,CAAC;AACzE,4EAA4E;AAC5E,uEAAuE;AACvE,0EAA0E;AAC1E,MAAM,KAAK,GAAG,EAAE,CAAC;AACjB,qEAAqE;AACrE,MAAM,IAAI,GAAG,0BAA0B,CAAC;AAExC,gFAAgF;AAChF,SAAS,QAAQ,CAAC,KAAiB;IAClC,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QACf,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;QACxC,CAAC,IAAI,GAAG,CAAC;IACV,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,mEAAmE;AACnE,SAAS,QAAQ,CAAC,IAAY;IAC7B,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1B,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;QACzD,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,6EAA6E;IAC7E,4EAA4E;IAC5E,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;IAChF,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC7B,CAAC,KAAK,EAAE,CAAC;IACV,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,mDAAmD;AACnD,SAAS,WAAW,CAAC,IAAY;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,yDAAyD;AACzD,SAAS,WAAW,CAAC,KAAiB;IACrC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/E,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5G,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,MAAc;IACxD,OAAO,GAAG,MAAM,IAAI,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACnD,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAAC,EAAU;IACxC,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,QAAQ,CAAC,MAAc;IACtC,OAAO,YAAY,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC;AAoBD,6EAA6E;AAC7E,MAAM,OAAO,GACZ,4EAA4E,CAAC;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAgC,EAAiB,EAAE,CACzE,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAElD,4EAA4E;AAC5E,MAAM,UAAU,MAAM,CAAC,KAAa;IACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC;IAC5D,OAAO,KAAK,CAAC;AACd,CAAC;AAyBD,SAAS,UAAU,CAAmB,MAAc;IACnD,4EAA4E;IAC5E,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,IAAI,MAAM,IAAI,IAAI,GAAG,CAAC,CAAC;IAClD,yEAAyE;IACzE,OAAO;QACN,MAAM;QACN,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAU;QACrC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAU;QACrD,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE;YACd,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,SAAS,MAAM,SAAS,EAAE,EAAE,CAAC,CAAC;YACrE,OAAO,cAAc,CAAC,EAAE,CAAS,CAAC;QACnC,CAAC;QACD,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,cAAc,CAAC,EAAE,CAAU,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9E,EAAE,EAAE,CAAC,EAAE,EAAe,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;KACzC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC/B,QAAW;IAEX,MAAM,GAAG,GAA6B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtD,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,GAAmC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,QAAQ,CACvB,QAA6B,EAC7B,KAAa;IAEb,mEAAmE;IACnE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAQ,EAAE,CAAC;QACnD,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAC1B,QAA6B,EAC7B,KAAa;IAEb,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAE,KAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC"}
File without changes
File without changes
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAC;AAG3C,MAAM,WAAW,gBAAgB;IAChC,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAcD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,WAAY,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SACN,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,UAAU,YAAY,gBAAgB,KAAQ,IAiC1D,CAAC"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAC;AAG3C,MAAM,WAAW,gBAAgB;IAChC,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAcD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,WAAY,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SACN,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,UAAU,YAAY,gBAAgB,KAAQ,IA6C1D,CAAC"}
package/dist/pool.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Pool } from "pg";
2
- import { dbEnv } from "./keys.js";
2
+ import { dbEnv, getDatabaseUrl } from "./keys.js";
3
3
  const LOCAL_HOSTS = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
4
4
  const isLocal = (connectionString) => {
5
5
  // Parse the URL: a substring check would misclassify a remote URL whose
@@ -49,10 +49,21 @@ export const normalizeCaCert = (caCert) => caCert?.includes("\\n") ? caCert.repl
49
49
  * app-side `src/lib/db.ts` pattern in docs/db-package.md).
50
50
  */
51
51
  export const createPool = (config = {}) => {
52
- const env = config.connectionString ? undefined : dbEnv();
52
+ // Resolve the connection string WITHOUT throwing when it is simply absent.
53
+ // createPool runs at module load in every app's `lib/db`, so importing that
54
+ // module — and therefore `next build` collecting page data, a unit test, or
55
+ // a CLI tool that never queries — must stay side-effect-free. Validate the
56
+ // full env (which *does* throw fast on a present-but-invalid value) only once
57
+ // a URL is known to exist; when nothing is configured, defer the error to
58
+ // first use via `unconfiguredPool`.
59
+ const env = config.connectionString !== undefined
60
+ ? undefined
61
+ : getDatabaseUrl() !== undefined
62
+ ? dbEnv()
63
+ : undefined;
53
64
  const connectionString = config.connectionString ?? env?.connectionString;
54
65
  if (!connectionString) {
55
- throw new Error("@ingram-tech/nk-db: createPool needs a connection string.");
66
+ return unconfiguredPool();
56
67
  }
57
68
  const caCert = normalizeCaCert(config.caCert ?? env?.caCert);
58
69
  const local = isLocal(connectionString);
@@ -80,4 +91,33 @@ export const createPool = (config = {}) => {
80
91
  ssl: { rejectUnauthorized: false },
81
92
  });
82
93
  };
94
+ /**
95
+ * A pool that constructs cleanly but rejects any real operation with the clear
96
+ * "set DATABASE_URL" error. Returned by {@link createPool} when no connection
97
+ * string is configured.
98
+ *
99
+ * Constructing the pool — which happens at module load in every app's `lib/db`,
100
+ * and therefore during `next build`'s page-data collection and in unit tests —
101
+ * must not throw just because a database isn't wired up. A process that then
102
+ * runs a real query without a URL is genuinely misconfigured and fails fast and
103
+ * legibly here; the env is fixed at process start, so this is never a transient
104
+ * miss. `query` and `connect` are the only entry points that reach the wire
105
+ * (Drizzle and `createQueries` go through them), so guarding both is sufficient.
106
+ *
107
+ * The rejection is deferred to a macrotask, not returned already-rejected, so it
108
+ * mimics real connection I/O: an async framework that inspects the in-flight
109
+ * promise before it settles — e.g. Next.js partial prerendering deciding a
110
+ * segment is dynamic — sees a pending promise and postpones it, exactly as it
111
+ * would for a real (reachable or not) pool. An already-rejected promise would
112
+ * instead surface as a synchronous render error at build.
113
+ */
114
+ const unconfiguredPool = () => {
115
+ const pool = new Pool();
116
+ const reject = () => new Promise((_resolve, rej) => {
117
+ setTimeout(() => rej(new Error("@ingram-tech/nk-db: no database connection string — set DATABASE_URL.")), 0);
118
+ });
119
+ pool.query = reject;
120
+ pool.connect = reject;
121
+ return pool;
122
+ };
83
123
  //# sourceMappingURL=pool.js.map
package/dist/pool.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAWlC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAExE,MAAM,OAAO,GAAG,CAAC,gBAAwB,EAAW,EAAE;IACrD,wEAAwE;IACxE,4DAA4D;IAC5D,IAAI,CAAC;QACJ,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAA0B,EAAsB,EAAE,CACjF,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAAM,GAAqB,EAAE,EAAQ,EAAE;IACjE,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IAC1D,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,GAAG,EAAE,gBAAgB,CAAC;IAC1E,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxC,8EAA8E;IAC9E,0EAA0E;IAC1E,8EAA8E;IAC9E,sCAAsC;IACtC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAErD,MAAM,IAAI,GAAe,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;IAE1D,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,IAAI,CAAC;YACf,GAAG,IAAI;YACP,gBAAgB;YAChB,GAAG,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE;SAC7C,CAAC,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;QACf,GAAG,IAAI;QACP,gBAAgB,EAAE,GAAG,CAAC,QAAQ,EAAE;QAChC,GAAG,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE;KAClC,CAAC,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Opt-in: keep `timestamptz` / `timestamp` columns as the raw ISO **strings**
3
+ * Postgres sends, instead of letting `pg` parse them into JS `Date`s.
4
+ *
5
+ * Why this exists: supabase-js / PostgREST returned timestamps as strings, and
6
+ * Supabase-generated row types declare them `string`. After moving to direct
7
+ * `pg`, the same columns come back as `Date`, which silently breaks any code
8
+ * doing string ops on them (and mismatches the generated types). Call this once
9
+ * at startup if your row types expect strings.
10
+ *
11
+ * Note: with Drizzle you usually express this per-column instead
12
+ * (`timestamp(..., { mode: "string" })`), so reach for that first on the golden
13
+ * path; this global switch is for the raw-helper / legacy-types case.
14
+ */
15
+ export declare const configureTimestampsAsStrings: () => void;
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,QAAO,IAI/C,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,24 @@
1
+ import pg from "pg";
2
+ /** Postgres type OIDs we care about. */
3
+ const TIMESTAMPTZ_OID = 1184;
4
+ const TIMESTAMP_OID = 1114;
5
+ /**
6
+ * Opt-in: keep `timestamptz` / `timestamp` columns as the raw ISO **strings**
7
+ * Postgres sends, instead of letting `pg` parse them into JS `Date`s.
8
+ *
9
+ * Why this exists: supabase-js / PostgREST returned timestamps as strings, and
10
+ * Supabase-generated row types declare them `string`. After moving to direct
11
+ * `pg`, the same columns come back as `Date`, which silently breaks any code
12
+ * doing string ops on them (and mismatches the generated types). Call this once
13
+ * at startup if your row types expect strings.
14
+ *
15
+ * Note: with Drizzle you usually express this per-column instead
16
+ * (`timestamp(..., { mode: "string" })`), so reach for that first on the golden
17
+ * path; this global switch is for the raw-helper / legacy-types case.
18
+ */
19
+ export const configureTimestampsAsStrings = () => {
20
+ const keep = (value) => value;
21
+ pg.types.setTypeParser(TIMESTAMPTZ_OID, keep);
22
+ pg.types.setTypeParser(TIMESTAMP_OID, keep);
23
+ };
24
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,wCAAwC;AACxC,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAS,EAAE;IACtD,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC;IAC9C,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IAC9C,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingram-tech/nk-db",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "The Ingram Postgres data layer: one TLS-aware pg pool, raw-SQL helpers, Drizzle wiring, and a PGlite (no-Docker) dev/test harness for Next.js sites.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -22,23 +22,27 @@
22
22
  "exports": {
23
23
  ".": {
24
24
  "types": "./dist/index.d.ts",
25
- "import": "./dist/index.js"
25
+ "default": "./dist/index.js"
26
26
  },
27
27
  "./id": {
28
28
  "types": "./dist/id.d.ts",
29
- "import": "./dist/id.js"
29
+ "default": "./dist/id.js"
30
+ },
31
+ "./id/drizzle": {
32
+ "types": "./dist/id-drizzle.d.ts",
33
+ "default": "./dist/id-drizzle.js"
30
34
  },
31
35
  "./migrate": {
32
36
  "types": "./dist/migrate.d.ts",
33
- "import": "./dist/migrate.js"
37
+ "default": "./dist/migrate.js"
34
38
  },
35
39
  "./pglite": {
36
40
  "types": "./dist/pglite/index.d.ts",
37
- "import": "./dist/pglite/index.js"
41
+ "default": "./dist/pglite/index.js"
38
42
  },
39
43
  "./pglite/reset": {
40
44
  "types": "./dist/pglite/reset.d.ts",
41
- "import": "./dist/pglite/reset.js"
45
+ "default": "./dist/pglite/reset.js"
42
46
  }
43
47
  },
44
48
  "scripts": {