@nodii/db-rls 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/configure.d.ts +42 -8
  2. package/dist/configure.d.ts.map +1 -1
  3. package/dist/configure.js +87 -16
  4. package/dist/configure.js.map +1 -1
  5. package/dist/context.d.ts +36 -7
  6. package/dist/context.d.ts.map +1 -1
  7. package/dist/context.js +67 -6
  8. package/dist/context.js.map +1 -1
  9. package/dist/drizzle-client.d.ts +9 -2
  10. package/dist/drizzle-client.d.ts.map +1 -1
  11. package/dist/drizzle-client.js +28 -2
  12. package/dist/drizzle-client.js.map +1 -1
  13. package/dist/drizzle-grants.d.ts +203 -0
  14. package/dist/drizzle-grants.d.ts.map +1 -0
  15. package/dist/drizzle-grants.js +68 -0
  16. package/dist/drizzle-grants.js.map +1 -0
  17. package/dist/drizzle-policies.d.ts +90 -0
  18. package/dist/drizzle-policies.d.ts.map +1 -0
  19. package/dist/drizzle-policies.js +223 -0
  20. package/dist/drizzle-policies.js.map +1 -0
  21. package/dist/index.d.ts +9 -6
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +22 -10
  24. package/dist/index.js.map +1 -1
  25. package/dist/init.d.ts +23 -5
  26. package/dist/init.d.ts.map +1 -1
  27. package/dist/init.js +38 -4
  28. package/dist/init.js.map +1 -1
  29. package/dist/metrics.d.ts +28 -0
  30. package/dist/metrics.d.ts.map +1 -0
  31. package/dist/metrics.js +263 -0
  32. package/dist/metrics.js.map +1 -0
  33. package/dist/migrate-guard.d.ts +14 -0
  34. package/dist/migrate-guard.d.ts.map +1 -0
  35. package/dist/migrate-guard.js +33 -0
  36. package/dist/migrate-guard.js.map +1 -0
  37. package/dist/pg-pool-adapter.d.ts.map +1 -1
  38. package/dist/pg-pool-adapter.js +78 -25
  39. package/dist/pg-pool-adapter.js.map +1 -1
  40. package/dist/postgres-js-client.d.ts +60 -0
  41. package/dist/postgres-js-client.d.ts.map +1 -0
  42. package/dist/postgres-js-client.js +223 -0
  43. package/dist/postgres-js-client.js.map +1 -0
  44. package/dist/types.d.ts +68 -2
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/types.js +31 -1
  47. package/dist/types.js.map +1 -1
  48. package/package.json +24 -5
package/dist/index.js CHANGED
@@ -1,32 +1,43 @@
1
- // @nodii/db-rls — v0.1.1 public barrel.
1
+ // @nodii/db-rls — v0.2.3 public barrel.
2
2
  //
3
3
  // Spec: planning hub feature_doc serviceId=nodii-libs docKey=db-rls
4
- // (A1 posture — RLS on every tenant-scoped table — locked 2026-05-25).
4
+ // + global doctrine `08-rls-doctrine.md` (A1 posture — RLS on every
5
+ // tenant-scoped table — operator-confirmed 2026-06-02).
5
6
  // Polyglot ship: TS + Python + Go in parity.
6
7
  //
7
- // v0.1.1 surface:
8
- // - configureDbRls (real pg.Pool-aware boot helper) + initDbRls (raw)
8
+ // v0.2.3 surface (additive over v0.1.1):
9
+ // - configureDbRls (3-pool) + configureDbRls2Pool (2-pool exception
10
+ // per 08-rls § 7) + initDbRls (raw)
9
11
  // - PgClientPool (wraps pg.Pool → PgPool interface)
10
12
  // - InMemoryPgPool (still exported — used by upstream tests)
11
13
  // - withUserContext / withSystemContext / withOwnerContext / autoDispatch
12
14
  // (operates on the substrate-agnostic PgTx)
13
15
  // - withUserContextDb / withSystemContextDb / withOwnerContextDb
14
16
  // (Drizzle-bound; require a real PgClientPool)
17
+ // - withOwnerContext now enforces NODII_ENV=migrate per 08-rls § 10.2
15
18
  // - applyAuthSchemaMigrations + AUTH_SCHEMA_SQL + AUTH_SCHEMA_GRANTS_SQL
16
- // - withRls() (RLS-policy DDL generator per § 5.5)
19
+ // - withRls() (legacy DDL-string generator per § 5.5; keeps *_policy suffix)
20
+ // - withRls() Drizzle table-builder (subpath `@nodii/db-rls/drizzle`)
21
+ // per doctrine § 4.2 — emits canonical 5-policy DDL via Drizzle's
22
+ // pgPolicy constructs, naming follows doctrine § 4 (no `_policy` suffix)
23
+ // - postgres-js adapter (subpath `@nodii/db-rls/postgres-js`) — nodii-auth
24
+ // and other postgres-js consumers
25
+ // - DB_RLS_METRIC_VOCABULARY + 9 locked metrics + 2 spans per § 5.10
17
26
  // - generateRlsPolicy / authSchemaHelpers (deprecated aliases, kept for
18
27
  // parity-fence + parity with the v0.1.0 surface; no NotImplemented paths)
19
28
  // - migrateGen + runMigrateGenCli + userTenantIdsRealSql + userTenantIdsStubSql
20
29
  // - Typed errors: DbRlsError + DbRlsNotInitialized + PoolUnavailable +
21
30
  // MissingTenantContext + TransactionRolledBack +
22
- // PoolCredentialMismatch
31
+ // PoolCredentialMismatch + OwnerContextOutsideMigrate +
32
+ // DbRlsConfigError
23
33
  export const LIB_NAME = "db-rls";
24
- export const VERSION = "0.1.1";
34
+ export const VERSION = "0.4.0";
25
35
  export { initDbRls, requireDbRls, getDbRlsOrNull, requirePool, _resetForTests, } from "./init";
26
- export { configureDbRls, PoolCredentialMismatch, _resetConfigureForTests, } from "./configure";
36
+ export { configureDbRls, configureDbRls2Pool, configureDbRls4Pool, PoolCredentialMismatch, _resetConfigureForTests, } from "./configure";
27
37
  export { PgClientPool } from "./pg-pool-adapter";
28
- export { withUserContext, withSystemContext, withOwnerContext, autoDispatch, } from "./context";
38
+ export { withUserContext, withSystemContext, withOwnerContext, withPlatformOps, autoDispatch, } from "./context";
29
39
  export { withUserContextDb, withSystemContextDb, withOwnerContextDb, } from "./drizzle-client";
40
+ export { assertMigrateAllowed } from "./migrate-guard";
30
41
  export { AUTH_SCHEMA_SQL, AUTH_SCHEMA_GRANTS_SQL, applyAuthSchemaMigrations, authSchemaFullSql, } from "./auth-schema";
31
42
  export { withRls, generateTableGrants } from "./rls-policy-gen";
32
43
  export { migrateGen, runMigrateGenCli, userTenantIdsRealSql, userTenantIdsStubSql, } from "./migrate-gen";
@@ -34,5 +45,6 @@ export { migrateGen, runMigrateGenCli, userTenantIdsRealSql, userTenantIdsStubSq
34
45
  // names. Both are byte-equal SQL helpers — no NotImplemented paths.
35
46
  export { generateRlsPolicy, authSchemaHelpers } from "./rls-policy";
36
47
  export { InMemoryPgPool } from "./in-memory-pool";
37
- export { DbRlsError, DbRlsNotInitialized, PoolUnavailable, MissingTenantContext, TransactionRolledBack, } from "./types";
48
+ export { DB_RLS_METRIC_VOCABULARY, _resetMetricsWarnLedgerForTests, } from "./metrics";
49
+ export { DbRlsConfigError, DbRlsError, DbRlsNotInitialized, MissingTenantContext, OwnerContextOutsideMigrate, PoolUnavailable, TransactionRolledBack, } from "./types";
38
50
  //# 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,wCAAwC;AACxC,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,6CAA6C;AAC7C,EAAE;AACF,kBAAkB;AAClB,wEAAwE;AACxE,sDAAsD;AACtD,+DAA+D;AAC/D,4EAA4E;AAC5E,gDAAgD;AAChD,mEAAmE;AACnE,mDAAmD;AACnD,2EAA2E;AAC3E,qDAAqD;AACrD,0EAA0E;AAC1E,8EAA8E;AAC9E,kFAAkF;AAClF,yEAAyE;AACzE,mEAAmE;AACnE,2CAA2C;AAE3C,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC;AACjC,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EACL,SAAS,EACT,YAAY,EACZ,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,QAAQ,CAAC;AAGhB,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAGvB,qEAAqE;AACrE,oEAAoE;AACpE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,oEAAoE;AACpE,oEAAoE;AACpE,wDAAwD;AACxD,6CAA6C;AAC7C,EAAE;AACF,yCAAyC;AACzC,sEAAsE;AACtE,wCAAwC;AACxC,sDAAsD;AACtD,+DAA+D;AAC/D,4EAA4E;AAC5E,gDAAgD;AAChD,mEAAmE;AACnE,mDAAmD;AACnD,wEAAwE;AACxE,2EAA2E;AAC3E,+EAA+E;AAC/E,wEAAwE;AACxE,sEAAsE;AACtE,6EAA6E;AAC7E,6EAA6E;AAC7E,sCAAsC;AACtC,uEAAuE;AACvE,0EAA0E;AAC1E,8EAA8E;AAC9E,kFAAkF;AAClF,yEAAyE;AACzE,mEAAmE;AACnE,0EAA0E;AAC1E,qCAAqC;AAErC,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC;AACjC,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EACL,SAAS,EACT,YAAY,EACZ,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,QAAQ,CAAC;AAGhB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAGvB,qEAAqE;AACrE,oEAAoE;AACpE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EACL,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,eAAe,EACf,qBAAqB,GACtB,MAAM,SAAS,CAAC"}
package/dist/init.d.ts CHANGED
@@ -1,13 +1,31 @@
1
- import { type PgPool, type PoolKind } from "./types";
1
+ import { type DbRlsMode, type PgPool, type PoolKind } from "./types";
2
+ /**
3
+ * Process-local config. `services` and `authenticated` are optional ONLY
4
+ * in 2-pool mode; in 3-pool mode (the default) the type guard runs at
5
+ * init time and throws `DbRlsConfigError` if either is missing.
6
+ *
7
+ * v0.4.0 (D185): `platformOps` is REQUIRED when `mode:
8
+ * "3-pool-with-platform-ops"` and FORBIDDEN otherwise.
9
+ */
2
10
  export interface InitDbRlsConfig {
3
11
  owner: PgPool;
4
- services: PgPool;
5
- authenticated: PgPool;
12
+ services?: PgPool;
13
+ authenticated?: PgPool;
14
+ /** D185 platform-ops pool. Required in 4-pool mode; forbidden in
15
+ * 3-pool / 2-pool modes. RLS-bound (NOT BYPASSRLS); policies use
16
+ * the grants-lookup predicate against `platform_ops_grants_replica`. */
17
+ platformOps?: PgPool;
18
+ /** Defaults to `"3-pool"`. Set `"2-pool"` for nodii-auth-shape services
19
+ * per 08-rls § 7. Set `"3-pool-with-platform-ops"` for services with a
20
+ * platform-ops surface per D185 (nodii-tenant-service, etc.). */
21
+ mode?: DbRlsMode;
6
22
  }
7
23
  export interface ResolvedDbRls {
8
24
  owner: PgPool;
9
- services: PgPool;
10
- authenticated: PgPool;
25
+ services: PgPool | null;
26
+ authenticated: PgPool | null;
27
+ platform_ops: PgPool | null;
28
+ mode: DbRlsMode;
11
29
  }
12
30
  export declare function initDbRls(config: InitDbRlsConfig): void;
13
31
  export declare function requireDbRls(): ResolvedDbRls;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,MAAM,EAEX,KAAK,QAAQ,EACd,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAcvD;AAED,wBAAgB,YAAY,IAAI,aAAa,CAK5C;AAED,wBAAgB,cAAc,IAAI,aAAa,GAAG,IAAI,CAErD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAOlD;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,MAAM,EAEX,KAAK,QAAQ,EACd,MAAM,SAAS,CAAC;AAEjB;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;6EAEyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;sEAEkE;IAClE,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,SAAS,CAAC;CACjB;AAID,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAwDvD;AAED,wBAAgB,YAAY,IAAI,aAAa,CAK5C;AAED,wBAAgB,cAAc,IAAI,aAAa,GAAG,IAAI,CAErD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAOlD;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
package/dist/init.js CHANGED
@@ -2,18 +2,52 @@
2
2
  //
3
3
  // Spec § 5.2. Caller supplies one PgPool per role. Subsequent calls warn +
4
4
  // no-op (first-init wins). Public APIs throw `DbRlsNotInitialized` before init.
5
- import { DbRlsNotInitialized, PoolUnavailable, } from "./types";
5
+ //
6
+ // v0.2.x adds the 2-pool exception per `08-rls-doctrine § 7` — nodii-auth
7
+ // boots without `services`/`authenticated` until its admin portal lands.
8
+ // Mode is explicit (`"3-pool"` | `"2-pool"`) so a missing pool is never
9
+ // silent.
10
+ import { DbRlsConfigError, DbRlsNotInitialized, PoolUnavailable, } from "./types";
6
11
  let resolved = null;
7
12
  export function initDbRls(config) {
8
13
  if (resolved) {
9
- // eslint-disable-next-line no-console
10
14
  console.warn("[@nodii/db-rls] initDbRls() called more than once; ignoring subsequent call.");
11
15
  return;
12
16
  }
17
+ const mode = config.mode ?? "3-pool";
18
+ if (mode === "3-pool") {
19
+ if (!config.services || !config.authenticated) {
20
+ throw new DbRlsConfigError("initDbRls: 3-pool mode requires owner + services + authenticated pools. Either supply both, or set mode: '2-pool' (nodii-auth shape per 08-rls § 7).");
21
+ }
22
+ if (config.platformOps) {
23
+ throw new DbRlsConfigError("initDbRls: 3-pool mode must not supply a 'platformOps' pool — use mode: '3-pool-with-platform-ops' per hub decision D185 if you have a platform-ops surface.");
24
+ }
25
+ }
26
+ // 2-pool: services optional, authenticated forbidden (the whole point
27
+ // of the exception is that nodii-auth has no user-context surface yet).
28
+ if (mode === "2-pool") {
29
+ if (config.authenticated) {
30
+ throw new DbRlsConfigError("initDbRls: 2-pool mode must not supply an 'authenticated' pool — the 2-pool exception per 08-rls § 7 covers services without user-context. Use mode: '3-pool' if you have an authenticated pool.");
31
+ }
32
+ if (config.platformOps) {
33
+ throw new DbRlsConfigError("initDbRls: 2-pool mode must not supply a 'platformOps' pool — the 2-pool exception per 08-rls § 7 has no platform-ops surface. Use mode: '3-pool-with-platform-ops' per D185.");
34
+ }
35
+ }
36
+ // 4-pool (D185): owner + services + authenticated + platformOps all required.
37
+ if (mode === "3-pool-with-platform-ops") {
38
+ if (!config.services || !config.authenticated) {
39
+ throw new DbRlsConfigError("initDbRls: '3-pool-with-platform-ops' mode requires owner + services + authenticated + platformOps pools (D185). All four are mandatory.");
40
+ }
41
+ if (!config.platformOps) {
42
+ throw new DbRlsConfigError("initDbRls: '3-pool-with-platform-ops' mode requires a 'platformOps' pool wired against the nodii_platform_ops Postgres role (D185 § 1). Did you mean mode: '3-pool'?");
43
+ }
44
+ }
13
45
  resolved = {
14
46
  owner: config.owner,
15
- services: config.services,
16
- authenticated: config.authenticated,
47
+ services: config.services ?? null,
48
+ authenticated: config.authenticated ?? null,
49
+ platform_ops: config.platformOps ?? null,
50
+ mode,
17
51
  };
18
52
  }
19
53
  export function requireDbRls() {
package/dist/init.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAEhF,OAAO,EACL,mBAAmB,EAEnB,eAAe,GAEhB,MAAM,SAAS,CAAC;AAcjB,IAAI,QAAQ,GAAyB,IAAI,CAAC;AAE1C,MAAM,UAAU,SAAS,CAAC,MAAuB;IAC/C,IAAI,QAAQ,EAAE,CAAC;QACb,sCAAsC;QACtC,OAAO,CAAC,IAAI,CACV,8EAA8E,CAC/E,CAAC;QACF,OAAO;IACT,CAAC;IAED,QAAQ,GAAG;QACT,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,mBAAmB,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAc;IACxC,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,cAAc;IAC5B,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,wEAAwE;AACxE,UAAU;AAEV,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EAGnB,eAAe,GAEhB,MAAM,SAAS,CAAC;AAgCjB,IAAI,QAAQ,GAAyB,IAAI,CAAC;AAE1C,MAAM,UAAU,SAAS,CAAC,MAAuB;IAC/C,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CACV,8EAA8E,CAC/E,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAc,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC;IAChD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC9C,MAAM,IAAI,gBAAgB,CACxB,sJAAsJ,CACvJ,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,MAAM,IAAI,gBAAgB,CACxB,8JAA8J,CAC/J,CAAC;QACJ,CAAC;IACH,CAAC;IACD,sEAAsE;IACtE,wEAAwE;IACxE,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACzB,MAAM,IAAI,gBAAgB,CACxB,kMAAkM,CACnM,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,MAAM,IAAI,gBAAgB,CACxB,+KAA+K,CAChL,CAAC;QACJ,CAAC;IACH,CAAC;IACD,8EAA8E;IAC9E,IAAI,IAAI,KAAK,0BAA0B,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC9C,MAAM,IAAI,gBAAgB,CACxB,0IAA0I,CAC3I,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,gBAAgB,CACxB,sKAAsK,CACvK,CAAC;QACJ,CAAC;IACH,CAAC;IAED,QAAQ,GAAG;QACT,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI;QACjC,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,IAAI;QAC3C,YAAY,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI;QACxC,IAAI;KACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,mBAAmB,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAc;IACxC,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,cAAc;IAC5B,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { type MetricVocabularyRegistry } from "@nodii/telemetry";
2
+ import type { PoolKind } from "./types";
3
+ export declare const DB_RLS_METRIC_VOCABULARY: MetricVocabularyRegistry;
4
+ export declare function recordAcquisition(pool: PoolKind, outcome: "success" | "failure"): void;
5
+ export declare function recordAcquisitionDuration(pool: PoolKind, durationMs: number): void;
6
+ export declare function recordPoolSize(pool: PoolKind, state: "active" | "idle" | "waiting", size: number): void;
7
+ export declare function recordTxDuration(pool: PoolKind, outcome: "committed" | "rolled_back" | "error", durationMs: number): void;
8
+ export declare function recordCrossPoolNesting(outer: PoolKind, inner: PoolKind, reason: string): void;
9
+ export declare function recordCrossPoolViolation(outer: PoolKind, attemptedInner: PoolKind): void;
10
+ export declare function recordOwnerContextInvocation(outcome: "allowed" | "refused"): void;
11
+ export declare function recordGucSetFailure(guc: string): void;
12
+ /**
13
+ * Wraps `fn` in a `db.rls.tx` span scoped to `pool`. The span stamps
14
+ * the final outcome attribute (`committed` / `rolled_back` / `error`)
15
+ * via the `setOutcome` callback the caller passes through. On thrown
16
+ * exceptions the tracer.span lifecycle marks status=ERROR + records
17
+ * the exception — no extra work needed here.
18
+ */
19
+ export declare function withTxSpan<T>(pool: PoolKind, fn: (setOutcome: (o: "committed" | "rolled_back" | "error") => void) => Promise<T>): Promise<T>;
20
+ /**
21
+ * Emits a one-shot `db.rls.pool.acquire` span. Caller invokes this only
22
+ * when acquisition time exceeds the slow threshold (default 50ms per
23
+ * § 5.10) — keeps span volume bounded.
24
+ */
25
+ export declare function emitSlowAcquireSpan(pool: PoolKind, durationMs: number): void;
26
+ /** Test-only: reset the one-shot warn ledger so each test runs clean. */
27
+ export declare function _resetMetricsWarnLedgerForTests(): void;
28
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAmBA,OAAO,EAEL,KAAK,wBAAwB,EAI9B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAWxC,eAAO,MAAM,wBAAwB,EAAE,wBAsDtC,CAAC;AAuFF,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,SAAS,GAAG,SAAS,GAC7B,IAAI,CAKN;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,GACjB,IAAI,CAMN;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,EACpC,IAAI,EAAE,MAAM,GACX,IAAI,CAMN;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,WAAW,GAAG,aAAa,GAAG,OAAO,EAC9C,UAAU,EAAE,MAAM,GACjB,IAAI,CAUN;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,MAAM,GACb,IAAI,CAKN;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,QAAQ,EACf,cAAc,EAAE,QAAQ,GACvB,IAAI,CAKN;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,SAAS,GAAG,SAAS,GAC7B,IAAI,CAKN;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAKrD;AAQD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,IAAI,EAAE,QAAQ,EACd,EAAE,EAAE,CACF,UAAU,EAAE,CAAC,CAAC,EAAE,WAAW,GAAG,aAAa,GAAG,OAAO,KAAK,IAAI,KAC3D,OAAO,CAAC,CAAC,CAAC,GACd,OAAO,CAAC,CAAC,CAAC,CA8BZ;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAgB5E;AAKD,yEAAyE;AACzE,wBAAgB,+BAA+B,IAAI,IAAI,CAGtD"}
@@ -0,0 +1,263 @@
1
+ // 9 locked db-rls metrics + 2 spans per `db-rls.md § 5.10` + doctrine
2
+ // `08-rls-doctrine.md § 12.4`. Wired through `@nodii/telemetry`'s
3
+ // vocabulary-bound `metrics.counter` / `metrics.histogram` API.
4
+ //
5
+ // Behavioral discipline:
6
+ // - Each metric is registered lazily on first emit. If the consumer
7
+ // hasn't merged `DB_RLS_METRIC_VOCABULARY` into their
8
+ // `initTelemetry({ vocabularyRegistry })` call, the registration
9
+ // throws `MetricVocabularyError`. We catch + `console.warn` once per
10
+ // metric so adopters see the gap; subsequent emissions are silently
11
+ // no-op for that name. Mirrors the pattern in
12
+ // `ts/approval/src/telemetry-shim.ts`.
13
+ // - Consumers wire `DB_RLS_METRIC_VOCABULARY` into their telemetry init
14
+ // so the registrations succeed; from then on the metrics emit
15
+ // normally.
16
+ // - Spans are emitted via `@nodii/telemetry`'s `tracer.startSpan`. Same
17
+ // try/swallow pattern — `TelemetryNotInitialized` is caught + the
18
+ // fn runs without a span.
19
+ import { MetricVocabularyError, metrics, tracer, TelemetryNotInitialized, } from "@nodii/telemetry";
20
+ /**
21
+ * Vocabulary registration. Adopters merge this into their `initTelemetry({
22
+ * vocabularyRegistry })` call so the metrics emitted by `@nodii/db-rls`
23
+ * are recognized.
24
+ */
25
+ // All 9 metrics map to D18 family `pool` + the doctrine § 12.4 decision
26
+ // `D-rls-metrics-v1` (recorded in the locked db-rls.md § 5.10 table).
27
+ const DOCTRINE_DECISION = "D-rls-metrics-v1";
28
+ export const DB_RLS_METRIC_VOCABULARY = {
29
+ "db_rls.pool.acquisitions_total": {
30
+ family: "pool",
31
+ description: "Pool connection acquisitions per pool + outcome (success|failure).",
32
+ decision: DOCTRINE_DECISION,
33
+ },
34
+ "db_rls.pool.acquisition_duration_seconds": {
35
+ family: "pool",
36
+ description: "Duration (s) from pool.connect() request to client ready.",
37
+ decision: DOCTRINE_DECISION,
38
+ },
39
+ "db_rls.pool.size": {
40
+ family: "pool",
41
+ description: "Pool size sampled at tx finish; state ∈ {active, idle, waiting}.",
42
+ decision: DOCTRINE_DECISION,
43
+ },
44
+ "db_rls.tx.duration_seconds": {
45
+ family: "pool",
46
+ description: "Per-transaction duration (s) from BEGIN to COMMIT/ROLLBACK, per pool + outcome.",
47
+ decision: DOCTRINE_DECISION,
48
+ },
49
+ "db_rls.tx.outcomes_total": {
50
+ family: "pool",
51
+ description: "Transaction outcomes per pool. outcome ∈ {committed, rolled_back, error}.",
52
+ decision: DOCTRINE_DECISION,
53
+ },
54
+ "db_rls.cross_pool_nesting_total": {
55
+ family: "pool",
56
+ description: "Sanctioned cross-pool nesting events ({outer}→{inner}, with reason).",
57
+ decision: DOCTRINE_DECISION,
58
+ },
59
+ "db_rls.cross_pool_violations_total": {
60
+ family: "pool",
61
+ description: "Refused cross-pool nesting attempts ({outer}→{attempted_inner}).",
62
+ decision: DOCTRINE_DECISION,
63
+ },
64
+ "db_rls.owner_context_invocations_total": {
65
+ family: "pool",
66
+ description: "withOwnerContext entry events. Should be 0 in non-migration prod runtime.",
67
+ decision: DOCTRINE_DECISION,
68
+ },
69
+ "db_rls.guc_set_failures_total": {
70
+ family: "pool",
71
+ description: "GUC set_config failures by GUC name — indicates auth.* helper drift.",
72
+ decision: DOCTRINE_DECISION,
73
+ },
74
+ };
75
+ /**
76
+ * One-shot warn ledger for metric names whose registration failed (e.g.
77
+ * because the consumer didn't merge `DB_RLS_METRIC_VOCABULARY`). Each
78
+ * name warn-logs once; subsequent emissions of that name are silent.
79
+ */
80
+ const WARNED = new Set();
81
+ let TELEMETRY_WARNED = false;
82
+ function warnOnce(metric, err) {
83
+ if (WARNED.has(metric))
84
+ return;
85
+ WARNED.add(metric);
86
+ const msg = err instanceof Error ? err.message : String(err);
87
+ console.warn(`[@nodii/db-rls] metric '${metric}' swallowed: ${msg}. Merge DB_RLS_METRIC_VOCABULARY into your initTelemetry({ vocabularyRegistry }) call so db-rls metrics emit. Further failures for '${metric}' will be silent.`);
88
+ }
89
+ function warnTelemetryOnce(err) {
90
+ if (TELEMETRY_WARNED)
91
+ return;
92
+ TELEMETRY_WARNED = true;
93
+ const msg = err instanceof Error ? err.message : String(err);
94
+ console.warn(`[@nodii/db-rls] telemetry not initialized: ${msg}. Call initTelemetry({ vocabularyRegistry: { ...DB_RLS_METRIC_VOCABULARY, ... } }) at process start. Further telemetry failures will be silent.`);
95
+ }
96
+ function inc(args) {
97
+ try {
98
+ const handle = metrics.counter(args.name, {
99
+ family: "pool",
100
+ description: DB_RLS_METRIC_VOCABULARY[args.name]?.description ?? "",
101
+ labelKeys: args.labels ? Object.keys(args.labels) : [],
102
+ });
103
+ handle.inc(args.by ?? 1, args.labels ?? {});
104
+ }
105
+ catch (err) {
106
+ if (err instanceof MetricVocabularyError) {
107
+ warnOnce(args.name, err);
108
+ return;
109
+ }
110
+ if (err instanceof TelemetryNotInitialized) {
111
+ warnTelemetryOnce(err);
112
+ return;
113
+ }
114
+ // Anything else: also one-shot warn, never throw out of the metric path.
115
+ warnOnce(args.name, err);
116
+ }
117
+ }
118
+ function observe(args) {
119
+ try {
120
+ const handle = metrics.histogram(args.name, {
121
+ family: "pool",
122
+ description: DB_RLS_METRIC_VOCABULARY[args.name]?.description ?? "",
123
+ labelKeys: args.labels ? Object.keys(args.labels) : [],
124
+ });
125
+ handle.record(args.value, args.labels ?? {});
126
+ }
127
+ catch (err) {
128
+ if (err instanceof MetricVocabularyError) {
129
+ warnOnce(args.name, err);
130
+ return;
131
+ }
132
+ if (err instanceof TelemetryNotInitialized) {
133
+ warnTelemetryOnce(err);
134
+ return;
135
+ }
136
+ warnOnce(args.name, err);
137
+ }
138
+ }
139
+ // ---------------------------------------------------------------------------
140
+ // Public emission helpers — called by PgClientPool / PgPostgresJsPool /
141
+ // context.ts. Each helper is a thin shim so the call sites stay readable.
142
+ export function recordAcquisition(pool, outcome) {
143
+ inc({
144
+ name: "db_rls.pool.acquisitions_total",
145
+ labels: { pool, outcome },
146
+ });
147
+ }
148
+ export function recordAcquisitionDuration(pool, durationMs) {
149
+ observe({
150
+ name: "db_rls.pool.acquisition_duration_seconds",
151
+ value: durationMs / 1000,
152
+ labels: { pool },
153
+ });
154
+ }
155
+ export function recordPoolSize(pool, state, size) {
156
+ observe({
157
+ name: "db_rls.pool.size",
158
+ value: size,
159
+ labels: { pool, state },
160
+ });
161
+ }
162
+ export function recordTxDuration(pool, outcome, durationMs) {
163
+ observe({
164
+ name: "db_rls.tx.duration_seconds",
165
+ value: durationMs / 1000,
166
+ labels: { pool, outcome },
167
+ });
168
+ inc({
169
+ name: "db_rls.tx.outcomes_total",
170
+ labels: { pool, outcome },
171
+ });
172
+ }
173
+ export function recordCrossPoolNesting(outer, inner, reason) {
174
+ inc({
175
+ name: "db_rls.cross_pool_nesting_total",
176
+ labels: { outer, inner, reason },
177
+ });
178
+ }
179
+ export function recordCrossPoolViolation(outer, attemptedInner) {
180
+ inc({
181
+ name: "db_rls.cross_pool_violations_total",
182
+ labels: { outer, attempted_inner: attemptedInner },
183
+ });
184
+ }
185
+ export function recordOwnerContextInvocation(outcome) {
186
+ inc({
187
+ name: "db_rls.owner_context_invocations_total",
188
+ labels: { outcome },
189
+ });
190
+ }
191
+ export function recordGucSetFailure(guc) {
192
+ inc({
193
+ name: "db_rls.guc_set_failures_total",
194
+ labels: { guc },
195
+ });
196
+ }
197
+ // ---------------------------------------------------------------------------
198
+ // Spans — `db.rls.tx{pool}` per § 5.10 wraps the tx fn so the span end
199
+ // runs deterministically on success + throw. `db.rls.pool.acquire{pool}`
200
+ // is emitted as a one-shot span only when acquisition exceeds the slow
201
+ // threshold (default 50ms) per the doctrine.
202
+ /**
203
+ * Wraps `fn` in a `db.rls.tx` span scoped to `pool`. The span stamps
204
+ * the final outcome attribute (`committed` / `rolled_back` / `error`)
205
+ * via the `setOutcome` callback the caller passes through. On thrown
206
+ * exceptions the tracer.span lifecycle marks status=ERROR + records
207
+ * the exception — no extra work needed here.
208
+ */
209
+ export async function withTxSpan(pool, fn) {
210
+ try {
211
+ return await tracer.span("db.rls.tx", async (span) => {
212
+ let outcome = "error";
213
+ try {
214
+ const out = await fn((o) => {
215
+ outcome = o;
216
+ });
217
+ span.setAttribute("outcome", outcome);
218
+ return out;
219
+ }
220
+ catch (err) {
221
+ span.setAttribute("outcome", "error");
222
+ throw err;
223
+ }
224
+ }, { attributes: { pool } });
225
+ }
226
+ catch (err) {
227
+ if (err instanceof TelemetryNotInitialized) {
228
+ warnTelemetryOnce(err);
229
+ // Telemetry not initialized — degrade to a plain pass-through.
230
+ let outcome = "error";
231
+ return await fn((o) => {
232
+ outcome = o;
233
+ }).finally(() => void outcome);
234
+ }
235
+ throw err;
236
+ }
237
+ }
238
+ /**
239
+ * Emits a one-shot `db.rls.pool.acquire` span. Caller invokes this only
240
+ * when acquisition time exceeds the slow threshold (default 50ms per
241
+ * § 5.10) — keeps span volume bounded.
242
+ */
243
+ export function emitSlowAcquireSpan(pool, durationMs) {
244
+ // Fire-and-forget single-tick span — no fn body to wrap.
245
+ void tracer
246
+ .span("db.rls.pool.acquire", async (_span) => {
247
+ /* attributes set via opts; no inner work. */
248
+ }, { attributes: { pool, "duration.ms": durationMs } })
249
+ .catch((err) => {
250
+ if (err instanceof TelemetryNotInitialized) {
251
+ warnTelemetryOnce(err);
252
+ }
253
+ /* otherwise swallow */
254
+ });
255
+ }
256
+ // ---------------------------------------------------------------------------
257
+ // Test hooks.
258
+ /** Test-only: reset the one-shot warn ledger so each test runs clean. */
259
+ export function _resetMetricsWarnLedgerForTests() {
260
+ WARNED.clear();
261
+ TELEMETRY_WARNED = false;
262
+ }
263
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface OwnerContextOpts {
2
+ /** Bypass the `NODII_ENV=migrate` guard. Requires `reason` to be a
3
+ * non-empty string so the bypass is at least intentional. v0.2.x:
4
+ * the reason is captured for log lines + future audit emission. */
5
+ allowOutsideMigrate?: boolean;
6
+ /** Operator-attested reason, required iff `allowOutsideMigrate` is true. */
7
+ reason?: string;
8
+ }
9
+ /**
10
+ * Asserts the caller can legitimately enter `withOwnerContext`. Throws
11
+ * `OwnerContextOutsideMigrate` if the guard is not satisfied.
12
+ */
13
+ export declare function assertMigrateAllowed(opts?: OwnerContextOpts): void;
14
+ //# sourceMappingURL=migrate-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-guard.d.ts","sourceRoot":"","sources":["../src/migrate-guard.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,gBAAgB;IAC/B;;wEAEoE;IACpE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,gBAAqB,GAAG,IAAI,CAWtE"}
@@ -0,0 +1,33 @@
1
+ // migrate-guard — per `08-rls-doctrine § 10.2`.
2
+ //
3
+ // `withOwnerContext` / `withOwnerContextDb` connect via `nodii_owner`
4
+ // which is BYPASSRLS. Invoking it outside a migration is a tenant-
5
+ // isolation escape: an owner-pool query against the live request path
6
+ // returns ALL tenants' rows. The guard refuses by default unless:
7
+ //
8
+ // 1. `NODII_ENV=migrate` (migration scripts opt in via env), OR
9
+ // 2. The caller explicitly passes `{ allowOutsideMigrate: true,
10
+ // reason: "..." }` with an operator-attested reason.
11
+ //
12
+ // Both paths are first-class — option 2 exists for legitimate ops
13
+ // tooling (DSAR fulfillment, support-ticket data extraction) where the
14
+ // caller has attested authorization. v0.2.x ships the guard; the
15
+ // audit-row-on-bypass extension lands in a follow-up slot.
16
+ import { OwnerContextOutsideMigrate } from "./types";
17
+ /**
18
+ * Asserts the caller can legitimately enter `withOwnerContext`. Throws
19
+ * `OwnerContextOutsideMigrate` if the guard is not satisfied.
20
+ */
21
+ export function assertMigrateAllowed(opts = {}) {
22
+ if (opts.allowOutsideMigrate) {
23
+ if (!opts.reason || opts.reason.trim() === "") {
24
+ throw new OwnerContextOutsideMigrate(process.env.NODII_ENV ?? "(unset)");
25
+ }
26
+ return;
27
+ }
28
+ const env = process.env.NODII_ENV ?? "";
29
+ if (env !== "migrate") {
30
+ throw new OwnerContextOutsideMigrate(env === "" ? "(unset)" : env);
31
+ }
32
+ }
33
+ //# sourceMappingURL=migrate-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-guard.js","sourceRoot":"","sources":["../src/migrate-guard.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,sEAAsE;AACtE,mEAAmE;AACnE,sEAAsE;AACtE,kEAAkE;AAClE,EAAE;AACF,kEAAkE;AAClE,kEAAkE;AAClE,0DAA0D;AAC1D,EAAE;AACF,kEAAkE;AAClE,uEAAuE;AACvE,iEAAiE;AACjE,2DAA2D;AAE3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAWrD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAyB,EAAE;IAC9D,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC9C,MAAM,IAAI,0BAA0B,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;IACxC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,0BAA0B,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrE,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-pool-adapter.d.ts","sourceRoot":"","sources":["../src/pg-pool-adapter.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,IAAI,EAAE,IAAI,CAAC;IACX,sDAAsD;IACtD,IAAI,EAAE,QAAQ,CAAC;IACf;uDACmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;qDACiD;IACjD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE;qDACiD;IACjD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,IAAI,EAAE,QAAQ,CAAC;QACf,OAAO,EAAE,WAAW,GAAG,aAAa,GAAG,OAAO,CAAC;QAC/C,UAAU,EAAE,MAAM,CAAC;KACpB,KAAK,IAAI,CAAC;CACZ;AAsBD;;;;GAIG;AACH,qBAAa,YAAa,YAAW,MAAM;IACzC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAqC;IACpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAkC;gBAElD,IAAI,EAAE,iBAAiB;IAQnC;;;sEAGkE;IAClE,IAAI,OAAO,IAAI,IAAI,CAElB;IAEK,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAgDnE"}
1
+ {"version":3,"file":"pg-pool-adapter.d.ts","sourceRoot":"","sources":["../src/pg-pool-adapter.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAC;AAU3C,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,IAAI,EAAE,IAAI,CAAC;IACX,sDAAsD;IACtD,IAAI,EAAE,QAAQ,CAAC;IACf;uDACmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;qDACiD;IACjD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE;qDACiD;IACjD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,IAAI,EAAE,QAAQ,CAAC;QACf,OAAO,EAAE,WAAW,GAAG,aAAa,GAAG,OAAO,CAAC;QAC/C,UAAU,EAAE,MAAM,CAAC;KACpB,KAAK,IAAI,CAAC;CACZ;AA2BD;;;;GAIG;AACH,qBAAa,YAAa,YAAW,MAAM;IACzC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAqC;IACpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAkC;gBAElD,IAAI,EAAE,iBAAiB;IAQnC;;;sEAGkE;IAClE,IAAI,OAAO,IAAI,IAAI,CAElB;IAEK,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CA2FnE"}