@nodii/db-rls 0.2.1 → 0.3.1

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 (44) hide show
  1. package/dist/configure.d.ts +25 -8
  2. package/dist/configure.d.ts.map +1 -1
  3. package/dist/configure.js +48 -17
  4. package/dist/configure.js.map +1 -1
  5. package/dist/context.d.ts +8 -6
  6. package/dist/context.d.ts.map +1 -1
  7. package/dist/context.js +27 -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-policies.d.ts +23 -0
  14. package/dist/drizzle-policies.d.ts.map +1 -0
  15. package/dist/drizzle-policies.js +114 -0
  16. package/dist/drizzle-policies.js.map +1 -0
  17. package/dist/index.d.ts +8 -5
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +21 -9
  20. package/dist/index.js.map +1 -1
  21. package/dist/init.d.ts +14 -5
  22. package/dist/init.d.ts.map +1 -1
  23. package/dist/init.js +20 -4
  24. package/dist/init.js.map +1 -1
  25. package/dist/metrics.d.ts +28 -0
  26. package/dist/metrics.d.ts.map +1 -0
  27. package/dist/metrics.js +263 -0
  28. package/dist/metrics.js.map +1 -0
  29. package/dist/migrate-guard.d.ts +14 -0
  30. package/dist/migrate-guard.d.ts.map +1 -0
  31. package/dist/migrate-guard.js +33 -0
  32. package/dist/migrate-guard.js.map +1 -0
  33. package/dist/pg-pool-adapter.d.ts.map +1 -1
  34. package/dist/pg-pool-adapter.js +78 -25
  35. package/dist/pg-pool-adapter.js.map +1 -1
  36. package/dist/postgres-js-client.d.ts +60 -0
  37. package/dist/postgres-js-client.d.ts.map +1 -0
  38. package/dist/postgres-js-client.js +223 -0
  39. package/dist/postgres-js-client.js.map +1 -0
  40. package/dist/types.d.ts +29 -0
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/types.js +30 -0
  43. package/dist/types.js.map +1 -1
  44. package/package.json +20 -5
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,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAMrB,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;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,22 @@
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
+ */
2
7
  export interface InitDbRlsConfig {
3
8
  owner: PgPool;
4
- services: PgPool;
5
- authenticated: PgPool;
9
+ services?: PgPool;
10
+ authenticated?: PgPool;
11
+ /** Defaults to `"3-pool"`. Set `"2-pool"` for nodii-auth-shape services
12
+ * per 08-rls § 7 — owner + (optional) services only, no authenticated. */
13
+ mode?: DbRlsMode;
6
14
  }
7
15
  export interface ResolvedDbRls {
8
16
  owner: PgPool;
9
- services: PgPool;
10
- authenticated: PgPool;
17
+ services: PgPool | null;
18
+ authenticated: PgPool | null;
19
+ mode: DbRlsMode;
11
20
  }
12
21
  export declare function initDbRls(config: InitDbRlsConfig): void;
13
22
  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;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;+EAC2E;IAC3E,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,IAAI,EAAE,SAAS,CAAC;CACjB;AAID,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CA8BvD;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,34 @@
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
+ }
23
+ // 2-pool: services optional, authenticated forbidden (the whole point
24
+ // of the exception is that nodii-auth has no user-context surface yet).
25
+ if (mode === "2-pool" && config.authenticated) {
26
+ 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.");
27
+ }
13
28
  resolved = {
14
29
  owner: config.owner,
15
- services: config.services,
16
- authenticated: config.authenticated,
30
+ services: config.services ?? null,
31
+ authenticated: config.authenticated ?? null,
32
+ mode,
17
33
  };
18
34
  }
19
35
  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;AAuBjB,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;IACH,CAAC;IACD,sEAAsE;IACtE,wEAAwE;IACxE,IAAI,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,IAAI,gBAAgB,CACxB,kMAAkM,CACnM,CAAC;IACJ,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,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"}
@@ -13,6 +13,7 @@
13
13
  // Pool-leak guard: client.release() ALWAYS runs in the finally block,
14
14
  // regardless of commit/rollback outcome. The acquire-timer fires a
15
15
  // telemetry span when acquisition exceeds 50ms (§ 5.10).
16
+ import { emitSlowAcquireSpan, recordAcquisition, recordAcquisitionDuration, recordGucSetFailure, recordPoolSize, recordTxDuration, withTxSpan, } from "./metrics";
16
17
  class PgClientTx {
17
18
  poolKind;
18
19
  client;
@@ -26,7 +27,13 @@ class PgClientTx {
26
27
  async setConfig(key, value) {
27
28
  // set_config(name, value, is_local=true) — tx-local per § 5.3.
28
29
  // Never expose the false variant; library refuses session-scoped GUCs.
29
- await this.client.query("SELECT set_config($1, $2, true)", [key, value]);
30
+ try {
31
+ await this.client.query("SELECT set_config($1, $2, true)", [key, value]);
32
+ }
33
+ catch (err) {
34
+ recordGucSetFailure(key);
35
+ throw err;
36
+ }
30
37
  }
31
38
  }
32
39
  /**
@@ -56,48 +63,94 @@ export class PgClientPool {
56
63
  }
57
64
  async withTransaction(fn) {
58
65
  const acquireStart = Date.now();
59
- const client = await this.pool.connect();
66
+ let client;
67
+ try {
68
+ client = await this.pool.connect();
69
+ }
70
+ catch (err) {
71
+ recordAcquisition(this.kind, "failure");
72
+ throw err;
73
+ }
60
74
  const acquireMs = Date.now() - acquireStart;
61
- if (acquireMs >= this.slowAcquireMs && this.onSlowAcquire) {
62
- try {
63
- this.onSlowAcquire({ pool: this.kind, durationMs: acquireMs });
64
- }
65
- catch {
66
- /* hook errors are non-fatal */
75
+ recordAcquisition(this.kind, "success");
76
+ recordAcquisitionDuration(this.kind, acquireMs);
77
+ if (acquireMs >= this.slowAcquireMs) {
78
+ emitSlowAcquireSpan(this.kind, acquireMs);
79
+ if (this.onSlowAcquire) {
80
+ try {
81
+ this.onSlowAcquire({ pool: this.kind, durationMs: acquireMs });
82
+ }
83
+ catch {
84
+ /* hook errors are non-fatal */
85
+ }
67
86
  }
68
87
  }
69
88
  const txStart = Date.now();
70
89
  let outcome = "error";
71
90
  try {
72
- await client.query("BEGIN");
73
- const tx = new PgClientTx(this.kind, client);
74
- let result;
75
- try {
76
- result = await fn(tx);
77
- }
78
- catch (err) {
79
- // Rollback then re-throw. Swallow rollback failure (don't double-throw).
91
+ return await withTxSpan(this.kind, async (setOutcome) => {
80
92
  try {
81
- await client.query("ROLLBACK");
93
+ await client.query("BEGIN");
94
+ const tx = new PgClientTx(this.kind, client);
95
+ let result;
96
+ try {
97
+ result = await fn(tx);
98
+ }
99
+ catch (err) {
100
+ // Rollback then re-throw. Swallow rollback failure (don't
101
+ // double-throw).
102
+ try {
103
+ await client.query("ROLLBACK");
104
+ }
105
+ catch {
106
+ /* swallow per § 5.9 */
107
+ }
108
+ outcome = "rolled_back";
109
+ setOutcome("rolled_back");
110
+ throw err;
111
+ }
112
+ await client.query("COMMIT");
113
+ outcome = "committed";
114
+ setOutcome("committed");
115
+ return result;
82
116
  }
83
- catch {
84
- /* swallow per § 5.9 */
117
+ catch (err) {
118
+ // BEGIN failure (or other pre-fn throw) — record error path.
119
+ if (outcome === "error") {
120
+ setOutcome("error");
121
+ }
122
+ throw err;
85
123
  }
86
- outcome = "rolled_back";
87
- throw err;
88
- }
89
- await client.query("COMMIT");
90
- outcome = "committed";
91
- return result;
124
+ });
92
125
  }
93
126
  finally {
94
127
  const durationMs = Date.now() - txStart;
128
+ recordTxDuration(this.kind, outcome, durationMs);
95
129
  try {
96
130
  this.onTxFinish?.({ pool: this.kind, outcome, durationMs });
97
131
  }
98
132
  catch {
99
133
  /* hook errors are non-fatal */
100
134
  }
135
+ // Sample pool-size gauge at tx end. pg.Pool exposes totalCount /
136
+ // idleCount / waitingCount; if absent (mocked or wrapped pool),
137
+ // skip silently.
138
+ try {
139
+ // biome-ignore lint/suspicious/noExplicitAny: pg.Pool stats
140
+ const stats = this.pool;
141
+ if (typeof stats.totalCount === "number") {
142
+ const total = stats.totalCount;
143
+ const idle = stats.idleCount ?? 0;
144
+ const waiting = stats.waitingCount ?? 0;
145
+ const active = Math.max(0, total - idle);
146
+ recordPoolSize(this.kind, "active", active);
147
+ recordPoolSize(this.kind, "idle", idle);
148
+ recordPoolSize(this.kind, "waiting", waiting);
149
+ }
150
+ }
151
+ catch {
152
+ /* gauge sampling errors are non-fatal */
153
+ }
101
154
  // Pool-leak guard — release ALWAYS runs, even on commit/rollback failure.
102
155
  try {
103
156
  client.release();
@@ -1 +1 @@
1
- {"version":3,"file":"pg-pool-adapter.js","sourceRoot":"","sources":["../src/pg-pool-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pg-pool-adapter.js","sourceRoot":"","sources":["../src/pg-pool-adapter.ts"],"names":[],"mappings":"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"}