@ingram-tech/nk-db 0.3.0 → 0.5.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 +50 -1
- package/dist/drizzle.d.ts +18 -0
- package/dist/drizzle.d.ts.map +1 -1
- package/dist/drizzle.js +20 -0
- package/dist/drizzle.js.map +1 -1
- package/dist/id.d.ts +63 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +138 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/pglite/dev.js +0 -0
- package/dist/queries.d.ts +10 -0
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +12 -2
- package/dist/queries.js.map +1 -1
- package/dist/rls.d.ts +82 -0
- package/dist/rls.d.ts.map +1 -0
- package/dist/rls.js +49 -0
- package/dist/rls.js.map +1 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ import * as schema from "./schema";
|
|
|
39
39
|
|
|
40
40
|
export const pool = createPool(); // TLS-aware; local socket → max:1
|
|
41
41
|
export const db = createDb(pool, schema); // Drizzle — the default query path
|
|
42
|
-
export const { query, one, maybeOne, execute, withTx } = createQueries(pool);
|
|
42
|
+
export const { query, one, maybeOne, execute, withTx, withRls } = createQueries(pool);
|
|
43
43
|
export { schema };
|
|
44
44
|
```
|
|
45
45
|
|
|
@@ -55,6 +55,55 @@ same pool: `betterAuth({ database: pool, … })`.
|
|
|
55
55
|
`timestamptz` as ISO strings (on the golden path, prefer Drizzle's
|
|
56
56
|
`timestamp(..., { mode: "string" })` per column).
|
|
57
57
|
|
|
58
|
+
## Keeping RLS on a direct connection (`withRls` / `withRlsTransaction`)
|
|
59
|
+
|
|
60
|
+
A direct `pg`/Drizzle connection has no PostgREST, so the `SET ROLE authenticated`
|
|
61
|
+
+ `request.jwt.claims` setup that made `auth.uid()` policies fire is gone — a
|
|
62
|
+
plain query runs as the connection's role with no claims. These helpers reproduce
|
|
63
|
+
that setup **per transaction**, so your **existing RLS policies keep working
|
|
64
|
+
unchanged**, whether you're still on Supabase Postgres or already on DO. It's
|
|
65
|
+
pure Postgres and behaves identically on both.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { withRlsTransaction } from "@ingram-tech/nk-db";
|
|
69
|
+
import { auth } from "@/lib/auth"; // your Better Auth instance
|
|
70
|
+
import { db } from "@/lib/db";
|
|
71
|
+
|
|
72
|
+
const session = await auth.api.getSession({ headers });
|
|
73
|
+
// scoped: sets request.jwt.claims + SET LOCAL ROLE authenticated, then runs fn
|
|
74
|
+
const notes = await withRlsTransaction(db, { sub: session.user.id }, (tx) =>
|
|
75
|
+
tx.select().from(schema.notes), // returns only this user's rows
|
|
76
|
+
);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The claims come **straight from the Better Auth session** (`sub` = `user.id`) — no
|
|
80
|
+
JWT minting, no JWKS issuer, no `supabase.auth`. The raw helpers expose the same
|
|
81
|
+
thing as `withRls` (sibling of `withTx`):
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const { withRls } = createQueries(pool);
|
|
85
|
+
const rows = await withRls({ sub: userId }, (tx) =>
|
|
86
|
+
tx.query<Note>("select * from notes"),
|
|
87
|
+
);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Two requirements **you** own (they can't be enforced from the library):
|
|
91
|
+
|
|
92
|
+
- **Connect as a role that doesn't bypass RLS** for user-facing rows — not the
|
|
93
|
+
table owner, not a `BYPASSRLS` superuser. After `SET ROLE authenticated`, RLS
|
|
94
|
+
applies even if the underlying connection is `postgres` (exactly what PostgREST
|
|
95
|
+
relied on). Service-role/admin paths keep using plain `db` / `query` and bypass
|
|
96
|
+
RLS as before.
|
|
97
|
+
- **The connecting role must be allowed to `SET ROLE`** to the target
|
|
98
|
+
(Supabase's `authenticator`/`postgres` already can; on DO, `GRANT app_user TO …`).
|
|
99
|
+
|
|
100
|
+
Override the role / claims GUC when your DB role name differs from the JWT claim:
|
|
101
|
+
`withRlsTransaction(db, { sub }, fn, { role: "app_user" })`. Both helpers set the
|
|
102
|
+
GUCs **transaction-locally** (`is_local = true`), so they reset at
|
|
103
|
+
commit/rollback and never leak across pooled connections. See
|
|
104
|
+
[`docs/db-package.md` §RLS](https://github.com/ingram-technologies/nextkit/blob/main/docs/db-package.md)
|
|
105
|
+
and [`docs/better-auth-migration.md`](https://github.com/ingram-technologies/nextkit/blob/main/docs/better-auth-migration.md).
|
|
106
|
+
|
|
58
107
|
## PGlite dev & test (`@ingram-tech/nk-db/pglite`)
|
|
59
108
|
|
|
60
109
|
`nk dev` runs the `nk-pglite-dev` bin automatically when this package is
|
package/dist/drizzle.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
2
|
import type { Pool } from "pg";
|
|
3
|
+
import { type RlsClaims, type RlsOptions } from "./rls.js";
|
|
3
4
|
/**
|
|
4
5
|
* Build the Drizzle instance on the shared pool. This is the default query path
|
|
5
6
|
* for app tables — schema-first, with `drizzle-kit` generating the migrations
|
|
@@ -11,4 +12,21 @@ import type { Pool } from "pg";
|
|
|
11
12
|
* export const db = createDb(pool, schema);
|
|
12
13
|
*/
|
|
13
14
|
export declare const createDb: <TSchema extends Record<string, unknown>>(pool: Pool, schema: TSchema) => NodePgDatabase<TSchema>;
|
|
15
|
+
/** The transaction handle Drizzle hands its `db.transaction(fn)` callback. */
|
|
16
|
+
type DrizzleTx<TSchema extends Record<string, unknown>> = Parameters<Parameters<NodePgDatabase<TSchema>["transaction"]>[0]>[0];
|
|
17
|
+
/**
|
|
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` +
|
|
20
|
+
* `SET LOCAL ROLE` (default `authenticated`) as the first statement, so existing
|
|
21
|
+
* `auth.uid()` policies fire unchanged; the `tx` passed to `fn` is a normal
|
|
22
|
+
* Drizzle transaction (full query builder + `tx.execute`). Use for user-facing
|
|
23
|
+
* reads/writes; use plain `db` for service-role paths. The connection role must
|
|
24
|
+
* not bypass RLS for the rows touched — see `rls.ts`.
|
|
25
|
+
*
|
|
26
|
+
* const notes = await withRlsTransaction(db, { sub: user.id }, (tx) =>
|
|
27
|
+
* tx.select().from(schema.notes),
|
|
28
|
+
* );
|
|
29
|
+
*/
|
|
30
|
+
export declare const withRlsTransaction: <TSchema extends Record<string, unknown>, T>(db: NodePgDatabase<TSchema>, claims: RlsClaims, fn: (tx: DrizzleTx<TSchema>) => Promise<T>, options?: RlsOptions) => Promise<T>;
|
|
31
|
+
export {};
|
|
14
32
|
//# sourceMappingURL=drizzle.d.ts.map
|
package/dist/drizzle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAoB,MAAM,UAAU,CAAC;AAE7E;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,MAAM,IAAI,EACV,QAAQ,OAAO,KACb,cAAc,CAAC,OAAO,CAA8B,CAAC;AAExD,8EAA8E;AAC9E,KAAK,SAAS,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CACnE,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CACrD,CAAC,CAAC,CAAC,CAAC;AAEL;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,EAC5E,IAAI,cAAc,CAAC,OAAO,CAAC,EAC3B,QAAQ,SAAS,EACjB,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,UAAS,UAAe,KACtB,OAAO,CAAC,CAAC,CAOT,CAAC"}
|
package/dist/drizzle.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
1
2
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
3
|
+
import { resolveRlsConfig } from "./rls.js";
|
|
2
4
|
/**
|
|
3
5
|
* Build the Drizzle instance on the shared pool. This is the default query path
|
|
4
6
|
* for app tables — schema-first, with `drizzle-kit` generating the migrations
|
|
@@ -10,4 +12,22 @@ import { drizzle } from "drizzle-orm/node-postgres";
|
|
|
10
12
|
* export const db = createDb(pool, schema);
|
|
11
13
|
*/
|
|
12
14
|
export const createDb = (pool, schema) => drizzle(pool, { schema });
|
|
15
|
+
/**
|
|
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` +
|
|
18
|
+
* `SET LOCAL ROLE` (default `authenticated`) as the first statement, so existing
|
|
19
|
+
* `auth.uid()` policies fire unchanged; the `tx` passed to `fn` is a normal
|
|
20
|
+
* Drizzle transaction (full query builder + `tx.execute`). Use for user-facing
|
|
21
|
+
* reads/writes; use plain `db` for service-role paths. The connection role must
|
|
22
|
+
* not bypass RLS for the rows touched — see `rls.ts`.
|
|
23
|
+
*
|
|
24
|
+
* const notes = await withRlsTransaction(db, { sub: user.id }, (tx) =>
|
|
25
|
+
* tx.select().from(schema.notes),
|
|
26
|
+
* );
|
|
27
|
+
*/
|
|
28
|
+
export const withRlsTransaction = (db, claims, fn, options = {}) => db.transaction(async (tx) => {
|
|
29
|
+
const { role, claimsSetting, claimsJson } = resolveRlsConfig(claims, options);
|
|
30
|
+
await tx.execute(sql `select set_config(${claimsSetting}, ${claimsJson}, true), set_config('role', ${role}, true)`);
|
|
31
|
+
return fn(tx);
|
|
32
|
+
});
|
|
13
33
|
//# sourceMappingURL=drizzle.js.map
|
package/dist/drizzle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.js","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAuB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"drizzle.js","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,OAAO,EAAuB,MAAM,2BAA2B,CAAC;AAEzE,OAAO,EAAmC,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE7E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CACvB,IAAU,EACV,MAAe,EACW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AAOxD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CACjC,EAA2B,EAC3B,MAAiB,EACjB,EAA0C,EAC1C,UAAsB,EAAE,EACX,EAAE,CACf,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;IAC3B,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9E,MAAM,EAAE,CAAC,OAAO,CACf,GAAG,CAAA,qBAAqB,aAAa,KAAK,UAAU,+BAA+B,IAAI,SAAS,CAChG,CAAC;IACF,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;AACf,CAAC,CAAC,CAAC"}
|
package/dist/id.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Ingram id codec — a UUIDv7 and its base58 skin. It lives here in nk-db
|
|
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.
|
|
6
|
+
*
|
|
7
|
+
* The Python twin lives in cloud.ingram.tech's `v1/core.py` (`new_id`); the
|
|
8
|
+
* byte → string vectors in `id.test.ts` and that repo's `tests/test_ids.py` are
|
|
9
|
+
* kept identical, so a stored UUIDv7 and an `agt_` / `smt_` id from the API are
|
|
10
|
+
* the same encoding of the same 16 bytes. That cross-impl contract is the most
|
|
11
|
+
* important property of this file — keep the vectors in lockstep.
|
|
12
|
+
*
|
|
13
|
+
* Store the hyphenated UUIDv7 at rest (uuid columns stay native and time-ordered
|
|
14
|
+
* for index locality) and use {@link toPrefixedId} to skin it as a prefixed
|
|
15
|
+
* base58 id for the wire / display, {@link fromPrefixedId} to recover it.
|
|
16
|
+
* {@link base58Id} mints a fresh one directly; {@link createIdRegistry} builds a
|
|
17
|
+
* typed, prefix-validated set of helpers for a project's entities.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Mint a **UUIDv7** (RFC 9562): a 48-bit Unix-ms timestamp prefix + random tail,
|
|
21
|
+
* version `7`, variant `10`. UUID-shaped (drops straight into a `uuid` column)
|
|
22
|
+
* while staying time-ordered for index locality. Used as Better Auth's
|
|
23
|
+
* `advanced.database.generateId`. Node/Bun's `randomUUID` is v4-only, so we lay
|
|
24
|
+
* the bytes out by hand.
|
|
25
|
+
*/
|
|
26
|
+
export declare const uuidGenerateId: () => string;
|
|
27
|
+
/** Skin a stored hyphenated UUIDv7 as a prefixed base58 id, e.g. `team_…`. */
|
|
28
|
+
export declare function toPrefixedId(uuid: string, prefix: string): string;
|
|
29
|
+
/** Inverse of {@link toPrefixedId}: recover the hyphenated UUIDv7. */
|
|
30
|
+
export declare function fromPrefixedId(id: string): string;
|
|
31
|
+
/** Mint a fresh prefixed base58 id (UUIDv7 core), for text-id sites / API parity. */
|
|
32
|
+
export declare function base58Id(prefix: string): string;
|
|
33
|
+
/** Typed helpers for one entity's prefixed ids — built by {@link createIdRegistry}. */
|
|
34
|
+
export interface IdHelper {
|
|
35
|
+
/** This entity's prefix, e.g. `"agt"` (no trailing underscore). */
|
|
36
|
+
readonly prefix: string;
|
|
37
|
+
/** Mint a fresh prefixed id (new UUIDv7 core). */
|
|
38
|
+
mint(): string;
|
|
39
|
+
/** Skin a stored hyphenated UUIDv7 as this entity's prefixed id. */
|
|
40
|
+
encode(uuid: string): string;
|
|
41
|
+
/** Recover the hyphenated UUIDv7; throws if the prefix / shape doesn't match. */
|
|
42
|
+
decode(id: string): string;
|
|
43
|
+
/** Whether `id` is a well-formed prefixed id for this entity. */
|
|
44
|
+
is(id: string): boolean;
|
|
45
|
+
}
|
|
46
|
+
/** The map returned by {@link createIdRegistry}: one {@link IdHelper} per key. */
|
|
47
|
+
export type IdRegistry<K extends string> = Record<K, IdHelper>;
|
|
48
|
+
/**
|
|
49
|
+
* Build a typed, prefix-validated id registry for a project's entities — one
|
|
50
|
+
* place to declare every prefix instead of hand-rolling encode/decode wrappers:
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* const ids = createIdRegistry({ org: "org", agent: "agt", session: "cs" });
|
|
54
|
+
* ids.org.mint(); // "org_3k9…"
|
|
55
|
+
* ids.org.encode(uuid); // "org_…"
|
|
56
|
+
* ids.org.decode("org_…"); // uuid — throws on a wrong / missing prefix
|
|
57
|
+
* ids.agent.is(someId); // boolean
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function createIdRegistry<const T extends Record<string, string>>(prefixes: T): {
|
|
61
|
+
[K in keyof T]: IdHelper;
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=id.d.ts.map
|
package/dist/id.d.ts.map
ADDED
|
@@ -0,0 +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;AAuDF,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,uFAAuF;AACvF,MAAM,WAAW,QAAQ;IACxB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,IAAI,IAAI,MAAM,CAAC;IACf,oEAAoE;IACpE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,iFAAiF;IACjF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,iEAAiE;IACjE,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,kFAAkF;AAClF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAiB/D;;;;;;;;;;;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"}
|
package/dist/id.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* The Ingram id codec — a UUIDv7 and its base58 skin. It lives here in nk-db
|
|
4
|
+
* (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.
|
|
7
|
+
*
|
|
8
|
+
* The Python twin lives in cloud.ingram.tech's `v1/core.py` (`new_id`); the
|
|
9
|
+
* byte → string vectors in `id.test.ts` and that repo's `tests/test_ids.py` are
|
|
10
|
+
* kept identical, so a stored UUIDv7 and an `agt_` / `smt_` id from the API are
|
|
11
|
+
* the same encoding of the same 16 bytes. That cross-impl contract is the most
|
|
12
|
+
* important property of this file — keep the vectors in lockstep.
|
|
13
|
+
*
|
|
14
|
+
* Store the hyphenated UUIDv7 at rest (uuid columns stay native and time-ordered
|
|
15
|
+
* for index locality) and use {@link toPrefixedId} to skin it as a prefixed
|
|
16
|
+
* base58 id for the wire / display, {@link fromPrefixedId} to recover it.
|
|
17
|
+
* {@link base58Id} mints a fresh one directly; {@link createIdRegistry} builds a
|
|
18
|
+
* typed, prefix-validated set of helpers for a project's entities.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Mint a **UUIDv7** (RFC 9562): a 48-bit Unix-ms timestamp prefix + random tail,
|
|
22
|
+
* version `7`, variant `10`. UUID-shaped (drops straight into a `uuid` column)
|
|
23
|
+
* while staying time-ordered for index locality. Used as Better Auth's
|
|
24
|
+
* `advanced.database.generateId`. Node/Bun's `randomUUID` is v4-only, so we lay
|
|
25
|
+
* the bytes out by hand.
|
|
26
|
+
*/
|
|
27
|
+
export const uuidGenerateId = () => {
|
|
28
|
+
const bytes = randomBytes(16);
|
|
29
|
+
const ts = Date.now();
|
|
30
|
+
bytes[0] = Math.floor(ts / 2 ** 40) & 0xff;
|
|
31
|
+
bytes[1] = Math.floor(ts / 2 ** 32) & 0xff;
|
|
32
|
+
bytes[2] = Math.floor(ts / 2 ** 24) & 0xff;
|
|
33
|
+
bytes[3] = Math.floor(ts / 2 ** 16) & 0xff;
|
|
34
|
+
bytes[4] = Math.floor(ts / 2 ** 8) & 0xff;
|
|
35
|
+
bytes[5] = ts & 0xff;
|
|
36
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x70; // version 7
|
|
37
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80; // variant 10
|
|
38
|
+
return bytesToUuid(bytes);
|
|
39
|
+
};
|
|
40
|
+
const B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
41
|
+
// ceil(128 / log2(58)): a 16-byte value never needs more than 22 digits. We
|
|
42
|
+
// left-pad to it so every body is uniform width and sorts lexically ==
|
|
43
|
+
// chronologically (UUIDv7's ms-timestamp prefix lives in the high bytes).
|
|
44
|
+
const WIDTH = 22;
|
|
45
|
+
// A bare base58 body: 22 chars, Bitcoin alphabet (no 0 / I / O / l).
|
|
46
|
+
const BODY = "[1-9A-HJ-NP-Za-km-z]{22}";
|
|
47
|
+
/** Big-endian base58 (Bitcoin alphabet) of 16 bytes, left-padded to `WIDTH`. */
|
|
48
|
+
function encode58(bytes) {
|
|
49
|
+
let n = 0n;
|
|
50
|
+
for (const b of bytes)
|
|
51
|
+
n = (n << 8n) | BigInt(b);
|
|
52
|
+
let out = "";
|
|
53
|
+
while (n > 0n) {
|
|
54
|
+
out = B58.charAt(Number(n % 58n)) + out;
|
|
55
|
+
n /= 58n;
|
|
56
|
+
}
|
|
57
|
+
return out.padStart(WIDTH, B58.charAt(0));
|
|
58
|
+
}
|
|
59
|
+
/** Inverse of {@link encode58}: a base58 body back to 16 bytes. */
|
|
60
|
+
function decode58(body) {
|
|
61
|
+
let n = 0n;
|
|
62
|
+
for (const ch of body) {
|
|
63
|
+
const v = B58.indexOf(ch);
|
|
64
|
+
if (v < 0)
|
|
65
|
+
throw new Error(`invalid base58 char: ${ch}`);
|
|
66
|
+
n = n * 58n + BigInt(v);
|
|
67
|
+
}
|
|
68
|
+
const bytes = new Uint8Array(16);
|
|
69
|
+
for (let i = 15; i >= 0; i--) {
|
|
70
|
+
bytes[i] = Number(n & 0xffn);
|
|
71
|
+
n >>= 8n;
|
|
72
|
+
}
|
|
73
|
+
return bytes;
|
|
74
|
+
}
|
|
75
|
+
/** A hyphenated UUID string → its 16 raw bytes. */
|
|
76
|
+
function uuidToBytes(uuid) {
|
|
77
|
+
const hex = uuid.replace(/-/g, "");
|
|
78
|
+
if (!/^[0-9a-fA-F]{32}$/.test(hex))
|
|
79
|
+
throw new Error(`not a uuid: ${uuid}`);
|
|
80
|
+
const bytes = new Uint8Array(16);
|
|
81
|
+
for (let i = 0; i < 16; i++) {
|
|
82
|
+
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
83
|
+
}
|
|
84
|
+
return bytes;
|
|
85
|
+
}
|
|
86
|
+
/** 16 raw bytes → a canonical hyphenated UUID string. */
|
|
87
|
+
function bytesToUuid(bytes) {
|
|
88
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
89
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
90
|
+
}
|
|
91
|
+
/** Skin a stored hyphenated UUIDv7 as a prefixed base58 id, e.g. `team_…`. */
|
|
92
|
+
export function toPrefixedId(uuid, prefix) {
|
|
93
|
+
return `${prefix}_${encode58(uuidToBytes(uuid))}`;
|
|
94
|
+
}
|
|
95
|
+
/** Inverse of {@link toPrefixedId}: recover the hyphenated UUIDv7. */
|
|
96
|
+
export function fromPrefixedId(id) {
|
|
97
|
+
const body = id.slice(id.indexOf("_") + 1);
|
|
98
|
+
return bytesToUuid(decode58(body));
|
|
99
|
+
}
|
|
100
|
+
/** Mint a fresh prefixed base58 id (UUIDv7 core), for text-id sites / API parity. */
|
|
101
|
+
export function base58Id(prefix) {
|
|
102
|
+
return toPrefixedId(uuidGenerateId(), prefix);
|
|
103
|
+
}
|
|
104
|
+
function makeHelper(prefix) {
|
|
105
|
+
// Prefixes are developer-controlled identifier constants, never user input.
|
|
106
|
+
const matcher = new RegExp(`^${prefix}_${BODY}$`);
|
|
107
|
+
return {
|
|
108
|
+
prefix,
|
|
109
|
+
mint: () => base58Id(prefix),
|
|
110
|
+
encode: (uuid) => toPrefixedId(uuid, prefix),
|
|
111
|
+
decode: (id) => {
|
|
112
|
+
if (!matcher.test(id))
|
|
113
|
+
throw new Error(`not a ${prefix}_ id: ${id}`);
|
|
114
|
+
return fromPrefixedId(id);
|
|
115
|
+
},
|
|
116
|
+
is: (id) => matcher.test(id),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Build a typed, prefix-validated id registry for a project's entities — one
|
|
121
|
+
* place to declare every prefix instead of hand-rolling encode/decode wrappers:
|
|
122
|
+
*
|
|
123
|
+
* ```ts
|
|
124
|
+
* const ids = createIdRegistry({ org: "org", agent: "agt", session: "cs" });
|
|
125
|
+
* ids.org.mint(); // "org_3k9…"
|
|
126
|
+
* ids.org.encode(uuid); // "org_…"
|
|
127
|
+
* ids.org.decode("org_…"); // uuid — throws on a wrong / missing prefix
|
|
128
|
+
* ids.agent.is(someId); // boolean
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export function createIdRegistry(prefixes) {
|
|
132
|
+
const reg = {};
|
|
133
|
+
for (const [key, prefix] of Object.entries(prefixes)) {
|
|
134
|
+
reg[key] = makeHelper(prefix);
|
|
135
|
+
}
|
|
136
|
+
return reg;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=id.js.map
|
package/dist/id.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { createDb } from "./drizzle.js";
|
|
1
|
+
export { createDb, withRlsTransaction } from "./drizzle.js";
|
|
2
2
|
export { type DbEnv, dbEnv, getDatabaseUrl, isConfigured } from "./keys.js";
|
|
3
3
|
export { type CreatePoolConfig, createPool } from "./pool.js";
|
|
4
4
|
export { createQueries, type PoolQueries, type Queries } from "./queries.js";
|
|
5
|
+
export { RLS_CLAIMS_SETTING, RLS_DEFAULT_ROLE, type ResolvedRlsConfig, type RlsClaims, type RlsOptions, resolveRlsConfig, rlsPreamble, } from "./rls.js";
|
|
5
6
|
export { configureTimestampsAsStrings } from "./types.js";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5D,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,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7E,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"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// The Ingram Postgres data layer. The PGlite dev/test harness lives behind the
|
|
2
2
|
// "@ingram-tech/nk-db/pglite" subpath so its WASM/dev-only deps never reach a
|
|
3
3
|
// production bundle.
|
|
4
|
-
export { createDb } from "./drizzle.js";
|
|
4
|
+
export { createDb, withRlsTransaction } from "./drizzle.js";
|
|
5
5
|
export { dbEnv, getDatabaseUrl, isConfigured } from "./keys.js";
|
|
6
6
|
export { createPool } from "./pool.js";
|
|
7
7
|
export { createQueries } from "./queries.js";
|
|
8
|
+
export { RLS_CLAIMS_SETTING, RLS_DEFAULT_ROLE, resolveRlsConfig, rlsPreamble, } from "./rls.js";
|
|
8
9
|
export { configureTimestampsAsStrings } from "./types.js";
|
|
9
10
|
//# 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,QAAQ,EAAE,MAAM,cAAc,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,QAAQ,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAc,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAyB,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAkC,MAAM,cAAc,CAAC;AAC7E,OAAO,EACN,kBAAkB,EAClB,gBAAgB,EAIhB,gBAAgB,EAChB,WAAW,GACX,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/pglite/dev.js
CHANGED
|
File without changes
|
package/dist/queries.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Pool, QueryResultRow } from "pg";
|
|
2
|
+
import { type RlsClaims, type RlsOptions } from "./rls.js";
|
|
2
3
|
export interface Queries {
|
|
3
4
|
/** Run a query and return all rows. */
|
|
4
5
|
query: <T extends QueryResultRow = QueryResultRow>(text: string, params?: unknown[]) => Promise<T[]>;
|
|
@@ -17,6 +18,15 @@ export interface PoolQueries extends Queries {
|
|
|
17
18
|
* statements that must be atomic.
|
|
18
19
|
*/
|
|
19
20
|
withTx: <T>(fn: (tx: Queries) => Promise<T>) => Promise<T>;
|
|
21
|
+
/**
|
|
22
|
+
* Like `withTx`, but first **scopes the transaction to a user** so RLS
|
|
23
|
+
* policies apply: it sets `request.jwt.claims` + `SET LOCAL ROLE` (default
|
|
24
|
+
* `authenticated`) before `fn`, reproducing what PostgREST did. Use this for
|
|
25
|
+
* user-facing reads/writes on a direct connection; keep `withTx` / `query`
|
|
26
|
+
* for service-role paths that should bypass RLS. The connection role must not
|
|
27
|
+
* bypass RLS for the rows touched — see `rls.ts`.
|
|
28
|
+
*/
|
|
29
|
+
withRls: <T>(claims: RlsClaims, fn: (tx: Queries) => Promise<T>, options?: RlsOptions) => Promise<T>;
|
|
20
30
|
}
|
|
21
31
|
/**
|
|
22
32
|
* Bind the raw-SQL helpers (`query` / `one` / `maybeOne` / `execute` / `withTx`)
|
package/dist/queries.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,cAAc,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,cAAc,EAAE,MAAM,IAAI,CAAC;AAC3D,OAAO,EACN,KAAK,SAAS,EACd,KAAK,UAAU,EAGf,MAAM,UAAU,CAAC;AAKlB,MAAM,WAAW,OAAO;IACvB,uCAAuC;IACvC,KAAK,EAAE,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EAChD,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,KACd,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAClB,wEAAwE;IACxE,QAAQ,EAAE,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EACnD,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,KACd,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvB,sEAAsE;IACtE,GAAG,EAAE,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EAC9C,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,KACd,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,qDAAqD;IACrD,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/D;AAoCD,MAAM,WAAW,WAAY,SAAQ,OAAO;IAC3C;;;;;OAKG;IACH,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3D;;;;;;;OAOG;IACH,OAAO,EAAE,CAAC,CAAC,EACV,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,EAC/B,OAAO,CAAC,EAAE,UAAU,KAChB,OAAO,CAAC,CAAC,CAAC,CAAC;CAChB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI,KAAG,WAkC1C,CAAC"}
|
package/dist/queries.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveRlsConfig, rlsPreamble, } from "./rls.js";
|
|
1
2
|
const bind = (executor) => {
|
|
2
3
|
const query = async (text, params = []) => {
|
|
3
4
|
const result = await executor.query(text, params);
|
|
@@ -32,10 +33,14 @@ const bind = (executor) => {
|
|
|
32
33
|
*/
|
|
33
34
|
export const createQueries = (pool) => {
|
|
34
35
|
const base = bind(pool);
|
|
35
|
-
|
|
36
|
+
// One transaction runner; `setup` (if any) runs after `begin`, before `fn` —
|
|
37
|
+
// that's where withRls injects the claims/role GUCs.
|
|
38
|
+
const runTx = async (setup, fn) => {
|
|
36
39
|
const client = await pool.connect();
|
|
37
40
|
try {
|
|
38
41
|
await client.query("begin");
|
|
42
|
+
if (setup)
|
|
43
|
+
await setup(client);
|
|
39
44
|
const result = await fn(bind(client));
|
|
40
45
|
await client.query("commit");
|
|
41
46
|
return result;
|
|
@@ -48,6 +53,11 @@ export const createQueries = (pool) => {
|
|
|
48
53
|
client.release();
|
|
49
54
|
}
|
|
50
55
|
};
|
|
51
|
-
|
|
56
|
+
const withTx = (fn) => runTx(undefined, fn);
|
|
57
|
+
const withRls = (claims, fn, options) => runTx(async (client) => {
|
|
58
|
+
const { text, values } = rlsPreamble(resolveRlsConfig(claims, options));
|
|
59
|
+
await client.query(text, values);
|
|
60
|
+
}, fn);
|
|
61
|
+
return { ...base, withTx, withRls };
|
|
52
62
|
};
|
|
53
63
|
//# sourceMappingURL=queries.js.map
|
package/dist/queries.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"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"}
|
package/dist/rls.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row-Level Security for a **direct Postgres connection** (no PostgREST).
|
|
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`.
|
|
16
|
+
*
|
|
17
|
+
* Two requirements the caller owns (documented, not enforceable here):
|
|
18
|
+
* 1. The pool must connect as a role that **does not bypass RLS** for the rows
|
|
19
|
+
* it touches — i.e. not the table owner and not a `BYPASSRLS` superuser for
|
|
20
|
+
* user-facing reads. (After `SET ROLE authenticated`, RLS applies even if the
|
|
21
|
+
* underlying connection is `postgres`, exactly as PostgREST relied on.)
|
|
22
|
+
* 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 …`).
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The JWT-style claims to scope a transaction by. `sub` becomes `auth.uid()`;
|
|
27
|
+
* `role` (default `"authenticated"`) is the Postgres role assumed. Any further
|
|
28
|
+
* keys are written into the claims GUC for policies that read them
|
|
29
|
+
* (`request.jwt.claims ->> 'org_id'`, etc.).
|
|
30
|
+
*/
|
|
31
|
+
export interface RlsClaims {
|
|
32
|
+
/**
|
|
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.
|
|
35
|
+
*/
|
|
36
|
+
sub: string;
|
|
37
|
+
/** The Postgres role to assume (the JWT `role` claim). Defaults to `"authenticated"`. */
|
|
38
|
+
role?: string;
|
|
39
|
+
/** Any further claims your policies read from `request.jwt.claims`. */
|
|
40
|
+
[claim: string]: unknown;
|
|
41
|
+
}
|
|
42
|
+
/** Overrides for {@link resolveRlsConfig} (and the helpers that build on it). */
|
|
43
|
+
export interface RlsOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Force the Postgres role to `SET LOCAL`, ignoring `claims.role`. Use when the
|
|
46
|
+
* DB role name differs from the JWT `role` claim (e.g. a dedicated `app_user`
|
|
47
|
+
* on DO while the claim stays `"authenticated"`).
|
|
48
|
+
*/
|
|
49
|
+
role?: string;
|
|
50
|
+
/** The GUC the claims JSON is written to. Defaults to `"request.jwt.claims"`. */
|
|
51
|
+
claimsSetting?: string;
|
|
52
|
+
}
|
|
53
|
+
/** The role assumed when neither `options.role` nor `claims.role` is set. */
|
|
54
|
+
export declare const RLS_DEFAULT_ROLE = "authenticated";
|
|
55
|
+
/** The GUC `auth.uid()` / `auth.role()` read; what PostgREST populated. */
|
|
56
|
+
export declare const RLS_CLAIMS_SETTING = "request.jwt.claims";
|
|
57
|
+
/** The concrete values a transaction is scoped with. */
|
|
58
|
+
export interface ResolvedRlsConfig {
|
|
59
|
+
/** Postgres role to assume for the transaction. */
|
|
60
|
+
role: string;
|
|
61
|
+
/** GUC the claims JSON is written to. */
|
|
62
|
+
claimsSetting: string;
|
|
63
|
+
/** The claims serialized for `set_config` (a JSON string). */
|
|
64
|
+
claimsJson: string;
|
|
65
|
+
}
|
|
66
|
+
/** Resolve claims + options into the role, GUC name, and serialized claims. */
|
|
67
|
+
export declare const resolveRlsConfig: (claims: RlsClaims, options?: RlsOptions) => ResolvedRlsConfig;
|
|
68
|
+
/**
|
|
69
|
+
* The single parameterized statement that scopes a transaction: writes the
|
|
70
|
+
* claims GUC and the `role` GUC, **both transaction-local** (`is_local = true`,
|
|
71
|
+
* so they reset at `commit`/`rollback` and never leak across pooled
|
|
72
|
+
* connections). Everything — including the GUC name and role — is bound, never
|
|
73
|
+
* interpolated, so it's injection-safe.
|
|
74
|
+
*
|
|
75
|
+
* Exposed for callers that manage their own connection/transaction (e.g. a
|
|
76
|
+
* framework middleware): run it as the first statement after `begin`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const rlsPreamble: (config: ResolvedRlsConfig) => {
|
|
79
|
+
text: string;
|
|
80
|
+
values: string[];
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=rls.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/rls.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row-Level Security for a **direct Postgres connection** (no PostgREST).
|
|
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`.
|
|
16
|
+
*
|
|
17
|
+
* Two requirements the caller owns (documented, not enforceable here):
|
|
18
|
+
* 1. The pool must connect as a role that **does not bypass RLS** for the rows
|
|
19
|
+
* it touches — i.e. not the table owner and not a `BYPASSRLS` superuser for
|
|
20
|
+
* user-facing reads. (After `SET ROLE authenticated`, RLS applies even if the
|
|
21
|
+
* underlying connection is `postgres`, exactly as PostgREST relied on.)
|
|
22
|
+
* 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 …`).
|
|
24
|
+
*/
|
|
25
|
+
/** The role assumed when neither `options.role` nor `claims.role` is set. */
|
|
26
|
+
export const RLS_DEFAULT_ROLE = "authenticated";
|
|
27
|
+
/** The GUC `auth.uid()` / `auth.role()` read; what PostgREST populated. */
|
|
28
|
+
export const RLS_CLAIMS_SETTING = "request.jwt.claims";
|
|
29
|
+
/** Resolve claims + options into the role, GUC name, and serialized claims. */
|
|
30
|
+
export const resolveRlsConfig = (claims, options = {}) => ({
|
|
31
|
+
role: options.role ?? claims.role ?? RLS_DEFAULT_ROLE,
|
|
32
|
+
claimsSetting: options.claimsSetting ?? RLS_CLAIMS_SETTING,
|
|
33
|
+
claimsJson: JSON.stringify(claims),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* The single parameterized statement that scopes a transaction: writes the
|
|
37
|
+
* claims GUC and the `role` GUC, **both transaction-local** (`is_local = true`,
|
|
38
|
+
* so they reset at `commit`/`rollback` and never leak across pooled
|
|
39
|
+
* connections). Everything — including the GUC name and role — is bound, never
|
|
40
|
+
* interpolated, so it's injection-safe.
|
|
41
|
+
*
|
|
42
|
+
* Exposed for callers that manage their own connection/transaction (e.g. a
|
|
43
|
+
* framework middleware): run it as the first statement after `begin`.
|
|
44
|
+
*/
|
|
45
|
+
export const rlsPreamble = (config) => ({
|
|
46
|
+
text: "select set_config($1, $2, true), set_config('role', $3, true)",
|
|
47
|
+
values: [config.claimsSetting, config.claimsJson, config.role],
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=rls.js.map
|
package/dist/rls.js.map
ADDED
|
@@ -0,0 +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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-tech/nk-db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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",
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
"types": "./dist/index.d.ts",
|
|
24
24
|
"import": "./dist/index.js"
|
|
25
25
|
},
|
|
26
|
+
"./id": {
|
|
27
|
+
"types": "./dist/id.d.ts",
|
|
28
|
+
"import": "./dist/id.js"
|
|
29
|
+
},
|
|
26
30
|
"./pglite": {
|
|
27
31
|
"types": "./dist/pglite/index.d.ts",
|
|
28
32
|
"import": "./dist/pglite/index.js"
|
|
@@ -57,7 +61,7 @@
|
|
|
57
61
|
"devDependencies": {
|
|
58
62
|
"@electric-sql/pglite": "^0.5.0",
|
|
59
63
|
"@electric-sql/pglite-socket": "^0.2.4",
|
|
60
|
-
"@ingram-tech/typescript-config": "
|
|
64
|
+
"@ingram-tech/typescript-config": "0.1.0",
|
|
61
65
|
"@types/node": "^25.0.0",
|
|
62
66
|
"@types/pg": "^8.11.0",
|
|
63
67
|
"drizzle-orm": "^0.45.0",
|