@ingram-tech/nk-db 0.9.0 → 1.0.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
@@ -3,7 +3,7 @@
3
3
  The Ingram **Postgres data layer**: one TLS-aware `pg` pool, raw-SQL helpers,
4
4
  Drizzle wiring, and a **PGlite** (no-Docker) dev/test harness. It consolidates
5
5
  the `src/lib/db/` layer that several products each hand-rolled when they moved
6
- off Supabase. Design + rationale:
6
+ to self-hosted Postgres. Design + rationale:
7
7
  [`docs/db-package.md`](https://github.com/ingram-technologies/nextkit/blob/main/docs/db-package.md).
8
8
 
9
9
  `pg` and `drizzle-orm` are **peer dependencies** (one copy in the app).
@@ -17,12 +17,10 @@ bun add @ingram-tech/nk-db pg drizzle-orm
17
17
  bun add -d @electric-sql/pglite @electric-sql/pglite-socket # for PGlite dev/test
18
18
  ```
19
19
 
20
- Env contract (validated by `keys.ts`; resolves in precedence order):
20
+ Env contract (validated by `keys.ts`):
21
21
 
22
22
  ```dotenv
23
- DATABASE_URL=… # direct Postgres (session pooler / :5432), NOT PostgREST
24
- # fallbacks, for running on Supabase Postgres before the data moves:
25
- # POSTGRES_URL_NON_POOLING / POSTGRES_URL
23
+ DATABASE_URL=… # direct Postgres (session pooler / :5432), not a REST proxy
26
24
  DATABASE_CA_CERT=… # optional PEM CA → verify-full
27
25
  DATABASE_SSL=true # optional
28
26
  DATABASE_POOL_MAX=5 # optional; keep small on serverless
@@ -51,23 +49,21 @@ same pool: `betterAuth({ database: pool, … })`.
51
49
  - **Raw helpers** (`createQueries(pool)`) are the escape hatch — Postgres
52
50
  functions (`select fn($1,…)`), `pgmq` draining, `pg_trgm`. Signatures match the
53
51
  hand-rolled originals, so adopting is a find-and-replace of the import.
54
- - **`configureTimestampsAsStrings()`** — opt-in, for legacy row types that expect
55
- `timestamptz` as ISO strings (on the golden path, prefer Drizzle's
56
- `timestamp(..., { mode: "string" })` per column).
57
52
  - **`pgTimestampToIso(value)` / `pgNumericToNumber(value)`** — response-boundary
58
- coercions for schemas written against supabase-js. `pg`/Drizzle return
59
- `numeric` as a string and `timestamp(..., { mode: "string" })` as Postgres'
60
- text form; these convert to the `z.number()` / strict `z.iso.datetime()` shapes
61
- those schemas expect. Presentation only — keep money math on the decimal value.
53
+ coercions for strict schemas. `pg`/Drizzle return `numeric` as a string and
54
+ `timestamp(..., { mode: "string" })` as Postgres' text form; these convert to
55
+ the `z.number()` / strict `z.iso.datetime()` shapes such schemas expect.
56
+ Presentation only — keep money math on the decimal value. For string timestamps
57
+ prefer Drizzle's `timestamp(..., { mode: "string" })` per column.
62
58
 
63
59
  ## Keeping RLS on a direct connection (`withRls` / `withRlsTransaction`)
64
60
 
65
- A direct `pg`/Drizzle connection has no PostgREST, so the `SET ROLE authenticated`
66
- + `request.jwt.claims` setup that made `auth.uid()` policies fire is gone a
67
- plain query runs as the connection's role with no claims. These helpers reproduce
68
- that setup **per transaction**, so your **existing RLS policies keep working
69
- unchanged**, whether you're still on Supabase Postgres or already on DO. It's
70
- pure Postgres and behaves identically on both.
61
+ A plain `pg`/Drizzle connection runs as the connection's role with **no request
62
+ claims**, so `auth.uid()` policies can't fire nothing populates the
63
+ `request.jwt.claims` they read. These helpers set the claims GUC + `SET LOCAL
64
+ ROLE` **per transaction**, so your **existing RLS policies keep working
65
+ unchanged**, wherever the cluster lives. It's pure Postgres and behaves
66
+ identically everywhere.
71
67
 
72
68
  ```ts
73
69
  import { withRlsTransaction } from "@ingram-tech/nk-db";
@@ -82,7 +78,7 @@ const notes = await withRlsTransaction(db, { sub: session.user.id }, (tx) =>
82
78
  ```
83
79
 
84
80
  The claims come **straight from the Better Auth session** (`sub` = `user.id`) — no
85
- JWT minting, no JWKS issuer, no `supabase.auth`. The raw helpers expose the same
81
+ JWT minting, no JWKS issuer, no third-party auth bridge. The raw helpers expose the same
86
82
  thing as `withRls` (sibling of `withTx`):
87
83
 
88
84
  ```ts
@@ -96,18 +92,17 @@ Two requirements **you** own (they can't be enforced from the library):
96
92
 
97
93
  - **Connect as a role that doesn't bypass RLS** for user-facing rows — not the
98
94
  table owner, not a `BYPASSRLS` superuser. After `SET ROLE authenticated`, RLS
99
- applies even if the underlying connection is `postgres` (exactly what PostgREST
100
- relied on). Service-role/admin paths keep using plain `db` / `query` and bypass
101
- RLS as before.
95
+ applies even when the underlying connection is a superuser. Service-role/admin
96
+ paths keep using plain `db` / `query` and bypass RLS as before.
102
97
  - **The connecting role must be allowed to `SET ROLE`** to the target
103
- (Supabase's `authenticator`/`postgres` already can; on DO, `GRANT app_user TO …`).
98
+ (on DO, `GRANT app_user TO the_connecting_role`).
104
99
 
105
100
  Override the role / claims GUC when your DB role name differs from the JWT claim:
106
101
  `withRlsTransaction(db, { sub }, fn, { role: "app_user" })`. Both helpers set the
107
102
  GUCs **transaction-locally** (`is_local = true`), so they reset at
108
103
  commit/rollback and never leak across pooled connections. See
109
104
  [`docs/db-package.md` §RLS](https://github.com/ingram-technologies/nextkit/blob/main/docs/db-package.md)
110
- and [`docs/better-auth-migration.md`](https://github.com/ingram-technologies/nextkit/blob/main/docs/better-auth-migration.md).
105
+ and the [`@ingram-tech/nk-auth`](https://github.com/ingram-technologies/nextkit/blob/main/packages/nk-auth) README.
111
106
 
112
107
  ## PGlite dev & test (`@ingram-tech/nk-db/pglite`)
113
108
 
package/dist/coerce.d.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  * Normalize a Postgres timestamp string to a strict ISO-8601 UTC string
3
3
  * ("2026-06-21T18:22:08.331Z"), the form `z.iso.datetime()` (no offset) accepts.
4
4
  *
5
- * `Date` parses both Postgres' space-separated form and supabase-js's `+00:00`
6
- * offset form to the same instant, and `.toISOString()` emits the canonical
7
- * `T…Z`. `null` passes through.
5
+ * `Date` parses both Postgres' space-separated form and the `+00:00` offset
6
+ * form to the same instant, and `.toISOString()` emits the canonical `T…Z`.
7
+ * `null` passes through.
8
8
  */
9
9
  export declare function pgTimestampToIso(value: string): string;
10
10
  export declare function pgTimestampToIso(value: null): null;
@@ -1 +1 @@
1
- {"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../src/coerce.ts"],"names":[],"mappings":"AAcA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;AACpD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;AAKtE;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;AACzD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;AACrD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../src/coerce.ts"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;AACpD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;AAKtE;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;AACzD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;AACrD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC"}
package/dist/coerce.js CHANGED
@@ -1,12 +1,11 @@
1
1
  // Coerce Postgres value representations back to the JS types app code expects.
2
2
  //
3
- // Why these exist: response/validation schemas in nextkit sites are typically
4
- // written against supabase-js / PostgREST, which parsed `timestamptz` into ISO
5
- // strings and `numeric` into JS numbers. Direct `pg` (and Drizzle on top of it)
6
- // surfaces those columns differently `numeric` as a string (to avoid losing
7
- // precision), and `timestamp(..., { mode: "string" })` as Postgres' own text
8
- // form ("2026-06-21 18:22:08.331494+00", space separator, short offset). Neither
9
- // satisfies a schema expecting `z.number()` / strict `z.iso.datetime()`.
3
+ // Why these exist: `pg` (and Drizzle on top of it) surfaces some columns in a
4
+ // form a strict response/validation schema rejects `numeric` as a string (to
5
+ // avoid losing precision), and `timestamp(..., { mode: "string" })` as Postgres'
6
+ // own text form ("2026-06-21 18:22:08.331494+00", space separator, short
7
+ // offset). Neither satisfies a schema expecting `z.number()` / strict
8
+ // `z.iso.datetime()`.
10
9
  //
11
10
  // Use these at the read/response boundary when handing Drizzle/`pg` rows to such
12
11
  // a schema. They are presentation coercions, not domain math — keep money
@@ -1 +1 @@
1
- {"version":3,"file":"coerce.js","sourceRoot":"","sources":["../src/coerce.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,6EAA6E;AAC7E,iFAAiF;AACjF,yEAAyE;AACzE,EAAE;AACF,iFAAiF;AACjF,0EAA0E;AAC1E,uEAAuE;AAavE,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACpD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AAC9D,CAAC;AAaD,MAAM,UAAU,iBAAiB,CAAC,KAAoB;IACrD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"coerce.js","sourceRoot":"","sources":["../src/coerce.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,yEAAyE;AACzE,sEAAsE;AACtE,sBAAsB;AACtB,EAAE;AACF,iFAAiF;AACjF,0EAA0E;AAC1E,uEAAuE;AAavE,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACpD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AAC9D,CAAC;AAaD,MAAM,UAAU,iBAAiB,CAAC,KAAoB;IACrD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9C,CAAC"}
package/dist/drizzle.d.ts CHANGED
@@ -16,7 +16,7 @@ export declare const createDb: <TSchema extends Record<string, unknown>>(pool: P
16
16
  type DrizzleTx<TSchema extends Record<string, unknown>> = Parameters<Parameters<NodePgDatabase<TSchema>["transaction"]>[0]>[0];
17
17
  /**
18
18
  * Run `fn` inside a Drizzle transaction **scoped to a user**, so RLS policies
19
- * apply on a direct connection (no PostgREST). It sets `request.jwt.claims` +
19
+ * apply on a direct connection. It sets `request.jwt.claims` +
20
20
  * `SET LOCAL ROLE` (default `authenticated`) as the first statement, so existing
21
21
  * `auth.uid()` policies fire unchanged; the `tx` passed to `fn` is a normal
22
22
  * Drizzle transaction (full query builder + `tx.execute`). Use for user-facing
package/dist/drizzle.js CHANGED
@@ -14,7 +14,7 @@ import { resolveRlsConfig } from "./rls.js";
14
14
  export const createDb = (pool, schema) => drizzle(pool, { schema });
15
15
  /**
16
16
  * Run `fn` inside a Drizzle transaction **scoped to a user**, so RLS policies
17
- * apply on a direct connection (no PostgREST). It sets `request.jwt.claims` +
17
+ * apply on a direct connection. It sets `request.jwt.claims` +
18
18
  * `SET LOCAL ROLE` (default `authenticated`) as the first statement, so existing
19
19
  * `auth.uid()` policies fire unchanged; the `tx` passed to `fn` is a normal
20
20
  * Drizzle transaction (full query builder + `tx.execute`). Use for user-facing
package/dist/index.d.ts CHANGED
@@ -5,5 +5,4 @@ export { type DbEnv, dbEnv, getDatabaseUrl, isConfigured } from "./keys.js";
5
5
  export { type CreatePoolConfig, createPool } from "./pool.js";
6
6
  export { createQueries, parseMaybeRow, parseOneRow, parseRows, type PoolQueries, type Queries, type RowsResult, } from "./queries.js";
7
7
  export { RLS_CLAIMS_SETTING, RLS_DEFAULT_ROLE, type ResolvedRlsConfig, type RlsClaims, type RlsOptions, resolveRlsConfig, rlsPreamble, } from "./rls.js";
8
- export { configureTimestampsAsStrings } from "./types.js";
9
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,KAAK,gBAAgB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EACN,aAAa,EACb,aAAa,EACb,WAAW,EACX,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,UAAU,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,gBAAgB,EAChB,WAAW,GACX,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,KAAK,gBAAgB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EACN,aAAa,EACb,aAAa,EACb,WAAW,EACX,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,UAAU,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,gBAAgB,EAChB,WAAW,GACX,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -8,5 +8,4 @@ export { dbEnv, getDatabaseUrl, isConfigured } from "./keys.js";
8
8
  export { createPool } from "./pool.js";
9
9
  export { createQueries, parseMaybeRow, parseOneRow, parseRows, } from "./queries.js";
10
10
  export { RLS_CLAIMS_SETTING, RLS_DEFAULT_ROLE, resolveRlsConfig, rlsPreamble, } from "./rls.js";
11
- export { configureTimestampsAsStrings } from "./types.js";
12
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,qBAAqB;AAErB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAc,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAyB,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EACN,aAAa,EACb,aAAa,EACb,WAAW,EACX,SAAS,GAIT,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,kBAAkB,EAClB,gBAAgB,EAIhB,gBAAgB,EAChB,WAAW,GACX,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,qBAAqB;AAErB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAc,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAyB,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EACN,aAAa,EACb,aAAa,EACb,WAAW,EACX,SAAS,GAIT,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,kBAAkB,EAClB,gBAAgB,EAIhB,gBAAgB,EAChB,WAAW,GACX,MAAM,UAAU,CAAC"}
package/dist/keys.d.ts CHANGED
@@ -5,13 +5,8 @@
5
5
  * @ingram-tech/nk-auth). Env vars are external input, so we parse them with Zod
6
6
  * (per docs/code-style.md) rather than reading `process.env` ad hoc.
7
7
  *
8
- * The connection string resolves in a fixed precedence so an app can deploy the
9
- * direct-`pg` code **while still pointed at Supabase Postgres**, before any data
10
- * has moved (see docs/db-package.md and the Supabase->Postgres playbook):
11
- *
12
- * 1. DATABASE_URL — our DO cluster (or the local PGlite socket)
13
- * 2. POSTGRES_URL_NON_POOLING — Supabase integration's direct URL
14
- * 3. POSTGRES_URL — Supabase integration's pooled URL
8
+ * The connection string comes from `DATABASE_URL` our DO cluster, or the local
9
+ * PGlite socket in dev (see docs/db-package.md).
15
10
  *
16
11
  * Optional TLS / pool controls:
17
12
  * DATABASE_SSL — booleanish flag; only "true" sets DbEnv.ssl, but other
@@ -30,9 +25,9 @@ export interface DbEnv {
30
25
  poolMax?: number;
31
26
  }
32
27
  /**
33
- * Resolve the connection string from the environment by precedence, without
34
- * validating the rest of the contract. Returns `undefined` if none is set
35
- * useful for "is a database configured?" checks (e.g. degrade in local dev).
28
+ * Resolve the connection string from the environment, without validating the
29
+ * rest of the contract. Returns `undefined` if unset — useful for "is a database
30
+ * configured?" checks (e.g. degrade in local dev).
36
31
  */
37
32
  export declare const getDatabaseUrl: (env?: NodeJS.ProcessEnv) => string | undefined;
38
33
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgBH,MAAM,WAAW,KAAK;IACrB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAC1B,MAAK,MAAM,CAAC,UAAwB,KAClC,MAAM,GAAG,SACyD,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,KAAK,QAAO,KAqBxB,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,YAAY,QAAO,OAAyC,CAAC"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAcH,MAAM,WAAW,KAAK;IACrB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAC1B,MAAK,MAAM,CAAC,UAAwB,KAClC,MAAM,GAAG,SAA6B,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,KAAK,QAAO,KAoBxB,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,YAAY,QAAO,OAAyC,CAAC"}
package/dist/keys.js CHANGED
@@ -5,13 +5,8 @@
5
5
  * @ingram-tech/nk-auth). Env vars are external input, so we parse them with Zod
6
6
  * (per docs/code-style.md) rather than reading `process.env` ad hoc.
7
7
  *
8
- * The connection string resolves in a fixed precedence so an app can deploy the
9
- * direct-`pg` code **while still pointed at Supabase Postgres**, before any data
10
- * has moved (see docs/db-package.md and the Supabase->Postgres playbook):
11
- *
12
- * 1. DATABASE_URL — our DO cluster (or the local PGlite socket)
13
- * 2. POSTGRES_URL_NON_POOLING — Supabase integration's direct URL
14
- * 3. POSTGRES_URL — Supabase integration's pooled URL
8
+ * The connection string comes from `DATABASE_URL` our DO cluster, or the local
9
+ * PGlite socket in dev (see docs/db-package.md).
15
10
  *
16
11
  * Optional TLS / pool controls:
17
12
  * DATABASE_SSL — booleanish flag; only "true" sets DbEnv.ssl, but other
@@ -22,8 +17,6 @@
22
17
  import { z } from "zod";
23
18
  const schema = z.object({
24
19
  DATABASE_URL: z.string().min(1).optional(),
25
- POSTGRES_URL_NON_POOLING: z.string().min(1).optional(),
26
- POSTGRES_URL: z.string().min(1).optional(),
27
20
  // Booleanish, but tolerant: only "true" flips DbEnv.ssl, yet libpq-style
28
21
  // values (disable/require/prefer/…) are accepted rather than throwing —
29
22
  // createPool decides TLS from the CA cert + host, never from this flag.
@@ -32,11 +25,11 @@ const schema = z.object({
32
25
  DATABASE_POOL_MAX: z.coerce.number().int().positive().optional(),
33
26
  });
34
27
  /**
35
- * Resolve the connection string from the environment by precedence, without
36
- * validating the rest of the contract. Returns `undefined` if none is set
37
- * useful for "is a database configured?" checks (e.g. degrade in local dev).
28
+ * Resolve the connection string from the environment, without validating the
29
+ * rest of the contract. Returns `undefined` if unset — useful for "is a database
30
+ * configured?" checks (e.g. degrade in local dev).
38
31
  */
39
- export const getDatabaseUrl = (env = process.env) => env.DATABASE_URL ?? env.POSTGRES_URL_NON_POOLING ?? env.POSTGRES_URL;
32
+ export const getDatabaseUrl = (env = process.env) => env.DATABASE_URL;
40
33
  /**
41
34
  * Read and validate the nk-db env vars at once. Throws a single error listing
42
35
  * everything missing/invalid so a misconfigured site fails fast at startup
@@ -52,8 +45,7 @@ export const dbEnv = () => {
52
45
  }
53
46
  const connectionString = getDatabaseUrl();
54
47
  if (!connectionString) {
55
- throw new Error("@ingram-tech/nk-db: no database connection string — set DATABASE_URL " +
56
- "(or POSTGRES_URL_NON_POOLING / POSTGRES_URL).");
48
+ throw new Error("@ingram-tech/nk-db: no database connection string — set DATABASE_URL.");
57
49
  }
58
50
  return {
59
51
  connectionString,
package/dist/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,wBAAwB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,yEAAyE;IACzE,wEAAwE;IACxE,wEAAwE;IACxE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC9C,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAChE,CAAC,CAAC;AAaH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC7B,MAAyB,OAAO,CAAC,GAAG,EACf,EAAE,CACvB,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,wBAAwB,IAAI,GAAG,CAAC,YAAY,CAAC;AAEtE;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,GAAU,EAAE;IAChC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAChC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;aAC3D,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,6CAA6C,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,gBAAgB,GAAG,cAAc,EAAE,CAAC;IAC1C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACd,uEAAuE;YACtE,+CAA+C,CAChD,CAAC;IACH,CAAC;IACD,OAAO;QACN,gBAAgB;QAChB,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB;QACpC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,KAAK,MAAM;QACxC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB;KACtC,CAAC;AACH,CAAC,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE,CAAC,cAAc,EAAE,KAAK,SAAS,CAAC"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,yEAAyE;IACzE,wEAAwE;IACxE,wEAAwE;IACxE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC9C,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAChE,CAAC,CAAC;AAaH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC7B,MAAyB,OAAO,CAAC,GAAG,EACf,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,GAAU,EAAE;IAChC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAChC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;aAC3D,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,6CAA6C,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,gBAAgB,GAAG,cAAc,EAAE,CAAC;IAC1C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACd,uEAAuE,CACvE,CAAC;IACH,CAAC;IACD,OAAO;QACN,gBAAgB;QAChB,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB;QACpC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,KAAK,MAAM;QACxC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB;KACtC,CAAC;AACH,CAAC,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE,CAAC,cAAc,EAAE,KAAK,SAAS,CAAC"}
File without changes
@@ -70,7 +70,7 @@ export const startPgliteDev = async (options = {}) => {
70
70
  dataDir: options.dataDir ?? ".pglite",
71
71
  ...options,
72
72
  });
73
- console.log(`nk(pglite): Postgres 18 (WASM) ready on ${databaseUrl} — no Docker, no Supabase CLI`);
73
+ console.log(`nk(pglite): Postgres 18 (WASM) ready on ${databaseUrl} — no Docker, no external service`);
74
74
  const child = spawn("bunx", ["next", "dev", "--turbopack", ...(options.nextArgs ?? [])], { stdio: "inherit", env: { ...process.env, DATABASE_URL: databaseUrl } });
75
75
  let closing = false;
76
76
  const close = async (code) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pglite/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pglite/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,iFAAiF;AACjF,uEAAuE;AACvE,iFAAiF;AACjF,sBAAsB;AACtB,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,2CAA2C;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,MAAM,EAAmB,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgC/C;2EAC2E;AAC3E,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAmB,EAAE,CAClD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC/B,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE;QACxB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEJ,MAAM,cAAc,GACnB,CAAC,gBAAwB,EAAE,EAAE,CAC7B,KAAK,EAAE,EAAU,EAAiB,EAAE;IACnC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACvD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;IAChE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACtC,UAA+B,EAAE,EACT,EAAE;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;IACrE,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,SAAS,CAAC;IAC/D,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAE5B,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,eAAe,GAAG,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACzD,MAAM,EAAE,GAAG,OAAO;QACjB,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;QAClE,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAC3D,IAAI,eAAe,EAAE,CAAC;QACrB,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACrB,MAAM,WAAW,GAAG,kCAAkC,IAAI,IAAI,IAAI,WAAW,CAAC;IAC9E,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;QACtC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACpC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC;IACF,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAClC,UAAyD,EAAE,EAC3C,EAAE;IAClB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACrD,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,MAAM,kBAAkB,CAAC;QACtD,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,SAAS;QACrC,GAAG,OAAO;KACV,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CACV,2CAA2C,WAAW,mCAAmC,CACzF,CAAC;IAEF,MAAM,KAAK,GAAG,KAAK,CAClB,MAAM,EACN,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,EAC3D,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,CACxE,CAAC;IAEF,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,KAAK,GAAG,KAAK,EAAE,IAAY,EAAiB,EAAE;QACnD,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,MAAM,IAAI,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACnD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC;AAYF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAChC,UAA+B,EAAE,EACf,EAAE;IACpB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACzC,MAAM,IAAI,GACT,OAAO,CAAC,IAAI;QACZ,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;YACvB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;YACjC,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,MAAM,kBAAkB,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,GAAkB,EAAE,CACjC,iBAAiB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAwB,GAAG,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACvC,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,IAAI,EAAE,CAAC;IACd,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC,CAAC"}
package/dist/pool.d.ts CHANGED
@@ -7,6 +7,21 @@ export interface CreatePoolConfig {
7
7
  /** Pool size cap. Defaults to env DATABASE_POOL_MAX, or 1 for a local socket. */
8
8
  max?: number;
9
9
  }
10
+ /**
11
+ * Un-escape a PEM CA cert that arrived with literal `\n` instead of real
12
+ * newlines. A multiline secret survives intact in the app's runtime env (e.g.
13
+ * Vercel hands `process.env.DATABASE_CA_CERT` back with real newlines), but
14
+ * `vercel env pull` — and most `.env` serializers — collapse it to a single
15
+ * quoted line with `\n` escapes. A caller that then sources that file (the
16
+ * `nk-pg-migrate` runner, a CI job) gets the literal-`\n` form, which OpenSSL
17
+ * rejects with "self-signed certificate in certificate chain" even though the
18
+ * deployed app verifies the very same cert fine. Normalise here, the one place
19
+ * the cert reaches `pg`, so every caller gets verify-full regardless of how the
20
+ * env was loaded. A correctly-newlined PEM contains no literal `\n`, so this is
21
+ * a no-op there — idempotent and safe (base64 + the BEGIN/END lines never
22
+ * contain a backslash).
23
+ */
24
+ export declare const normalizeCaCert: (caCert: string | undefined) => string | undefined;
10
25
  /**
11
26
  * The one shared `pg.Pool`. Reuse this for everything — app queries (via
12
27
  * `createQueries` / Drizzle) AND Better Auth's adapter — so there's exactly one
@@ -18,8 +33,8 @@ export interface CreatePoolConfig {
18
33
  * - `caCert` set -> verify the server cert + hostname (`sslmode=verify-full`).
19
34
  * - local host -> no TLS, and cap at `max: 1` (the PGlite socket server is
20
35
  * single-connection/multiplexed; a larger pool breaks dev).
21
- * - otherwise -> TLS **without** chain verification. Managed certs (DO,
22
- * Supabase) aren't in Node's trust store, so full
36
+ * - otherwise -> TLS **without** chain verification. Managed certs (e.g.
37
+ * DigitalOcean) aren't in Node's trust store, so full
23
38
  * verification fails with "self-signed certificate in
24
39
  * certificate chain"; the link is still encrypted. `sslmode`
25
40
  * is stripped from the URL because `pg` ignores the `ssl`
@@ -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;AAKD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,UAAU,GAAI,SAAQ,gBAAqB,KAAG,IA6B1D,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;AAKD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SACN,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,UAAU,GAAI,SAAQ,gBAAqB,KAAG,IA6B1D,CAAC"}
package/dist/pool.js CHANGED
@@ -1,6 +1,21 @@
1
1
  import { Pool } from "pg";
2
2
  import { dbEnv } from "./keys.js";
3
3
  const isLocal = (connectionString) => connectionString.includes("127.0.0.1") || connectionString.includes("localhost");
4
+ /**
5
+ * Un-escape a PEM CA cert that arrived with literal `\n` instead of real
6
+ * newlines. A multiline secret survives intact in the app's runtime env (e.g.
7
+ * Vercel hands `process.env.DATABASE_CA_CERT` back with real newlines), but
8
+ * `vercel env pull` — and most `.env` serializers — collapse it to a single
9
+ * quoted line with `\n` escapes. A caller that then sources that file (the
10
+ * `nk-pg-migrate` runner, a CI job) gets the literal-`\n` form, which OpenSSL
11
+ * rejects with "self-signed certificate in certificate chain" even though the
12
+ * deployed app verifies the very same cert fine. Normalise here, the one place
13
+ * the cert reaches `pg`, so every caller gets verify-full regardless of how the
14
+ * env was loaded. A correctly-newlined PEM contains no literal `\n`, so this is
15
+ * a no-op there — idempotent and safe (base64 + the BEGIN/END lines never
16
+ * contain a backslash).
17
+ */
18
+ export const normalizeCaCert = (caCert) => caCert?.includes("\\n") ? caCert.replace(/\\n/g, "\n") : caCert;
4
19
  /**
5
20
  * The one shared `pg.Pool`. Reuse this for everything — app queries (via
6
21
  * `createQueries` / Drizzle) AND Better Auth's adapter — so there's exactly one
@@ -12,8 +27,8 @@ const isLocal = (connectionString) => connectionString.includes("127.0.0.1") ||
12
27
  * - `caCert` set -> verify the server cert + hostname (`sslmode=verify-full`).
13
28
  * - local host -> no TLS, and cap at `max: 1` (the PGlite socket server is
14
29
  * single-connection/multiplexed; a larger pool breaks dev).
15
- * - otherwise -> TLS **without** chain verification. Managed certs (DO,
16
- * Supabase) aren't in Node's trust store, so full
30
+ * - otherwise -> TLS **without** chain verification. Managed certs (e.g.
31
+ * DigitalOcean) aren't in Node's trust store, so full
17
32
  * verification fails with "self-signed certificate in
18
33
  * certificate chain"; the link is still encrypted. `sslmode`
19
34
  * is stripped from the URL because `pg` ignores the `ssl`
@@ -29,7 +44,7 @@ export const createPool = (config = {}) => {
29
44
  if (!connectionString) {
30
45
  throw new Error("@ingram-tech/nk-db: createPool needs a connection string.");
31
46
  }
32
- const caCert = config.caCert ?? env?.caCert;
47
+ const caCert = normalizeCaCert(config.caCert ?? env?.caCert);
33
48
  const local = isLocal(connectionString);
34
49
  const max = config.max ?? env?.poolMax ?? (local ? 1 : undefined);
35
50
  const base = max === undefined ? {} : { max };
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,OAAO,GAAG,CAAC,gBAAwB,EAAW,EAAE,CACrD,gBAAgB,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,SAA2B,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,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,MAAM,CAAC;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,EAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAElE,MAAM,IAAI,GAAe,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;IAE1D,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,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAChD,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":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAWlC,MAAM,OAAO,GAAG,CAAC,gBAAwB,EAAW,EAAE,CACrD,gBAAgB,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAElF;;;;;;;;;;;;;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,SAA2B,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,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,EAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAElE,MAAM,IAAI,GAAe,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;IAE1D,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,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAChD,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"}
package/dist/queries.d.ts CHANGED
@@ -59,8 +59,8 @@ export interface PoolQueries extends Queries {
59
59
  /**
60
60
  * Like `withTx`, but first **scopes the transaction to a user** so RLS
61
61
  * policies apply: it sets `request.jwt.claims` + `SET LOCAL ROLE` (default
62
- * `authenticated`) before `fn`, reproducing what PostgREST did. Use this for
63
- * user-facing reads/writes on a direct connection; keep `withTx` / `query`
62
+ * `authenticated`) before `fn`, so `auth.uid()`-style policies fire. Use this
63
+ * for user-facing reads/writes on a direct connection; keep `withTx` / `query`
64
64
  * for service-role paths that should bypass RLS. The connection role must not
65
65
  * bypass RLS for the rows touched — see `rls.ts`.
66
66
  */
package/dist/rls.d.ts CHANGED
@@ -1,26 +1,20 @@
1
1
  /**
2
- * Row-Level Security for a **direct Postgres connection** (no PostgREST).
2
+ * Row-Level Security for a **direct Postgres connection** (`pg` / Drizzle).
3
3
  *
4
- * On Supabase, PostgREST is what made RLS work: per request it did
5
- * `SET ROLE authenticated` and set `request.jwt.claims` from the user's JWT, so
6
- * policies written against `auth.uid()` fired. The moment a site queries Postgres
7
- * directly (`pg` / Drizzle) whether it's still on Supabase Postgres or has
8
- * moved to our DigitalOcean cluster — that setup is gone, and a plain connection
9
- * runs as the connection's role with **no claims**, so RLS is either bypassed
10
- * (privileged role) or denies everything.
11
- *
12
- * These helpers reproduce exactly what PostgREST did, per transaction: set the
13
- * claims GUC and `SET LOCAL ROLE`, so **existing `auth.uid()` policies keep
14
- * working unchanged**. It is pure Postgres, so it behaves identically on Supabase
15
- * and on DO. See `docs/db-package.md` (§RLS) and `docs/better-auth-migration.md`.
4
+ * A plain connection runs as the connection's role with **no request claims**, so
5
+ * RLS is either bypassed (privileged role) or denies everything. These helpers
6
+ * scope a transaction the way a JWT-claims RLS setup needs: per transaction they
7
+ * set the `request.jwt.claims` GUC and `SET LOCAL ROLE`, so policies written
8
+ * against `auth.uid()` (i.e. `current_setting('request.jwt.claims') ->> 'sub'`)
9
+ * fire correctly. It is pure Postgres. See `docs/db-package.md` RLS).
16
10
  *
17
11
  * Two requirements the caller owns (documented, not enforceable here):
18
12
  * 1. The pool must connect as a role that **does not bypass RLS** for the rows
19
13
  * it touches — i.e. not the table owner and not a `BYPASSRLS` superuser for
20
14
  * user-facing reads. (After `SET ROLE authenticated`, RLS applies even if the
21
- * underlying connection is `postgres`, exactly as PostgREST relied on.)
15
+ * underlying connection is a superuser.)
22
16
  * 2. The connecting role must be allowed to `SET ROLE` to the target role
23
- * (Supabase's `authenticator`/`postgres` can; on DO, `GRANT app_user TO …`).
17
+ * (e.g. `GRANT app_user TO the_connecting_role`).
24
18
  */
25
19
  /**
26
20
  * The JWT-style claims to scope a transaction by. `sub` becomes `auth.uid()`;
@@ -30,8 +24,8 @@
30
24
  */
31
25
  export interface RlsClaims {
32
26
  /**
33
- * The user id. Becomes the `sub` claim → `auth.uid()`. For Supabase-style
34
- * policies that cast `sub` to `uuid`, this MUST be a valid UUID.
27
+ * The user id. Becomes the `sub` claim → `auth.uid()`. For policies that cast
28
+ * `sub` to `uuid`, this MUST be a valid UUID.
35
29
  */
36
30
  sub: string;
37
31
  /** The Postgres role to assume (the JWT `role` claim). Defaults to `"authenticated"`. */
@@ -52,7 +46,7 @@ export interface RlsOptions {
52
46
  }
53
47
  /** The role assumed when neither `options.role` nor `claims.role` is set. */
54
48
  export declare const RLS_DEFAULT_ROLE = "authenticated";
55
- /** The GUC `auth.uid()` / `auth.role()` read; what PostgREST populated. */
49
+ /** The GUC `auth.uid()` / `auth.role()` policies read claims from. */
56
50
  export declare const RLS_CLAIMS_SETTING = "request.jwt.claims";
57
51
  /** The concrete values a transaction is scoped with. */
58
52
  export interface ResolvedRlsConfig {
package/dist/rls.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rls.d.ts","sourceRoot":"","sources":["../src/rls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAED,iFAAiF;AACjF,MAAM,WAAW,UAAU;IAC1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IACjC,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,GAC5B,QAAQ,SAAS,EACjB,UAAS,UAAe,KACtB,iBAID,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,GACvB,QAAQ,iBAAiB,KACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAGjC,CAAC"}
1
+ {"version":3,"file":"rls.d.ts","sourceRoot":"","sources":["../src/rls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAED,iFAAiF;AACjF,MAAM,WAAW,UAAU;IAC1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IACjC,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,GAC5B,QAAQ,SAAS,EACjB,UAAS,UAAe,KACtB,iBAID,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,GACvB,QAAQ,iBAAiB,KACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAGjC,CAAC"}
package/dist/rls.js CHANGED
@@ -1,30 +1,24 @@
1
1
  /**
2
- * Row-Level Security for a **direct Postgres connection** (no PostgREST).
2
+ * Row-Level Security for a **direct Postgres connection** (`pg` / Drizzle).
3
3
  *
4
- * On Supabase, PostgREST is what made RLS work: per request it did
5
- * `SET ROLE authenticated` and set `request.jwt.claims` from the user's JWT, so
6
- * policies written against `auth.uid()` fired. The moment a site queries Postgres
7
- * directly (`pg` / Drizzle) whether it's still on Supabase Postgres or has
8
- * moved to our DigitalOcean cluster — that setup is gone, and a plain connection
9
- * runs as the connection's role with **no claims**, so RLS is either bypassed
10
- * (privileged role) or denies everything.
11
- *
12
- * These helpers reproduce exactly what PostgREST did, per transaction: set the
13
- * claims GUC and `SET LOCAL ROLE`, so **existing `auth.uid()` policies keep
14
- * working unchanged**. It is pure Postgres, so it behaves identically on Supabase
15
- * and on DO. See `docs/db-package.md` (§RLS) and `docs/better-auth-migration.md`.
4
+ * A plain connection runs as the connection's role with **no request claims**, so
5
+ * RLS is either bypassed (privileged role) or denies everything. These helpers
6
+ * scope a transaction the way a JWT-claims RLS setup needs: per transaction they
7
+ * set the `request.jwt.claims` GUC and `SET LOCAL ROLE`, so policies written
8
+ * against `auth.uid()` (i.e. `current_setting('request.jwt.claims') ->> 'sub'`)
9
+ * fire correctly. It is pure Postgres. See `docs/db-package.md` RLS).
16
10
  *
17
11
  * Two requirements the caller owns (documented, not enforceable here):
18
12
  * 1. The pool must connect as a role that **does not bypass RLS** for the rows
19
13
  * it touches — i.e. not the table owner and not a `BYPASSRLS` superuser for
20
14
  * user-facing reads. (After `SET ROLE authenticated`, RLS applies even if the
21
- * underlying connection is `postgres`, exactly as PostgREST relied on.)
15
+ * underlying connection is a superuser.)
22
16
  * 2. The connecting role must be allowed to `SET ROLE` to the target role
23
- * (Supabase's `authenticator`/`postgres` can; on DO, `GRANT app_user TO …`).
17
+ * (e.g. `GRANT app_user TO the_connecting_role`).
24
18
  */
25
19
  /** The role assumed when neither `options.role` nor `claims.role` is set. */
26
20
  export const RLS_DEFAULT_ROLE = "authenticated";
27
- /** The GUC `auth.uid()` / `auth.role()` read; what PostgREST populated. */
21
+ /** The GUC `auth.uid()` / `auth.role()` policies read claims from. */
28
22
  export const RLS_CLAIMS_SETTING = "request.jwt.claims";
29
23
  /** Resolve claims + options into the role, GUC name, and serialized claims. */
30
24
  export const resolveRlsConfig = (claims, options = {}) => ({
package/dist/rls.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rls.js","sourceRoot":"","sources":["../src/rls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAgCH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC;AAChD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAYvD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,MAAiB,EACjB,UAAsB,EAAE,EACJ,EAAE,CAAC,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,gBAAgB;IACrD,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,kBAAkB;IAC1D,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;CAClC,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,MAAyB,EACY,EAAE,CAAC,CAAC;IACzC,IAAI,EAAE,+DAA+D;IACrE,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC;CAC9D,CAAC,CAAC"}
1
+ {"version":3,"file":"rls.js","sourceRoot":"","sources":["../src/rls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgCH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC;AAChD,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAYvD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,MAAiB,EACjB,UAAsB,EAAE,EACJ,EAAE,CAAC,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,gBAAgB;IACrD,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,kBAAkB;IAC1D,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;CAClC,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,MAAyB,EACY,EAAE,CAAC,CAAC;IACzC,IAAI,EAAE,+DAA+D;IACrE,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC;CAC9D,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingram-tech/nk-db",
3
- "version": "0.9.0",
3
+ "version": "1.0.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",
@@ -66,7 +66,7 @@
66
66
  "devDependencies": {
67
67
  "@electric-sql/pglite": "^0.5.0",
68
68
  "@electric-sql/pglite-socket": "^0.2.4",
69
- "@ingram-tech/nk-dev": "workspace:*",
69
+ "@ingram-tech/nk-dev": "0.2.4",
70
70
  "@types/node": "^25.0.0",
71
71
  "@types/pg": "^8.11.0",
72
72
  "drizzle-orm": "^0.45.0",
package/dist/types.d.ts DELETED
@@ -1,16 +0,0 @@
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
@@ -1 +0,0 @@
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 DELETED
@@ -1,24 +0,0 @@
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
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
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"}