@venturekit/data 0.0.0-dev.20260721010549 → 0.0.0-dev.20260725205100

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/dist/index.d.ts CHANGED
@@ -8,6 +8,6 @@ export { createRdsConfig, DEFAULT_RDS_CONFIG, buildRdsConfig, } from './rds/inde
8
8
  export type { RdsOutputs, RdsInfraConfig } from './rds/index.js';
9
9
  export { createMigrationConfig, DEFAULT_MIGRATION_CONFIG, getFlywayEnv, runMigrations, getMigrationStatus, runSeeds, getSeedStatus, MIGRATIONS_TRACKING_TABLE, SEEDS_TRACKING_TABLE, MigrationToolNotImplementedError, MigrationHashMismatchError, MigrationCollisionError, applyDatabaseUrlToEnv, } from './migrations/index.js';
10
10
  export type { RunResult, StatusEntry, RunOptions, SeedRunOptions, } from './migrations/index.js';
11
- export { query, getPool, mapResults, mapRow, configureMapper, getMapperConfig, beginTransaction, withTransaction, buildTransaction, applyDbSecretToEnv, } from './query/index.js';
11
+ export { query, getPool, mapResults, mapRow, configureMapper, getMapperConfig, beginTransaction, withTransaction, buildTransaction, applyDbSecretToEnv, TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './query/index.js';
12
12
  export type { Transaction, ResultsMapper, Querier, MapperOptions, } from './query/index.js';
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKjE,OAAO,EAEL,qBAAqB,EACrB,wBAAwB,EACxB,YAAY,EAEZ,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB,EAEpB,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB,EAGvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,GACf,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAKhB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,WAAW,EACX,aAAa,EACb,OAAO,EACP,aAAa,GACd,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKjE,OAAO,EAEL,qBAAqB,EACrB,wBAAwB,EACxB,YAAY,EAEZ,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB,EAEpB,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB,EAGvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,GACf,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAKhB,kBAAkB,EAOlB,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,WAAW,EACX,aAAa,EACb,OAAO,EACP,aAAa,GACd,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -26,5 +26,12 @@ export { query, getPool, mapResults, mapRow, configureMapper, getMapperConfig, b
26
26
  // Secrets Manager when `DB_SECRET_ARN` is set. Auto-applied at the
27
27
  // top of `query()` and `beginTransaction()`, so consumers rarely
28
28
  // need to call this themselves.
29
- applyDbSecretToEnv, } from './query/index.js';
29
+ applyDbSecretToEnv,
30
+ // Tenant context for row-level security. Wrap a request in
31
+ // `runWithTenant(id, fn)` and every query inside it — pooled or
32
+ // transactional — carries the `app.tenant_id` GUC, so RLS policies
33
+ // enforce isolation the application SQL may have forgotten.
34
+ // See ./query/tenant.ts for the required policy shape and the
35
+ // database-role split RLS depends on.
36
+ TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './query/index.js';
30
37
  //# 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;;;;GAIG;AAEH,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,MAAM;AACN,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,uEAAuE;AACvE,iEAAiE;AACjE,gDAAgD;AAChD,OAAO;AACL,4BAA4B;AAC5B,qBAAqB,EACrB,wBAAwB,EACxB,YAAY;AACZ,sEAAsE;AACtE,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB;AACpB,SAAS;AACT,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB;AACvB,uEAAuE;AACvE,qEAAqE;AACrE,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAQ/B,kDAAkD;AAClD,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB;AAChB,0DAA0D;AAC1D,mEAAmE;AACnE,iEAAiE;AACjE,gCAAgC;AAChC,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,MAAM;AACN,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,uEAAuE;AACvE,iEAAiE;AACjE,gDAAgD;AAChD,OAAO;AACL,4BAA4B;AAC5B,qBAAqB,EACrB,wBAAwB,EACxB,YAAY;AACZ,sEAAsE;AACtE,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB;AACpB,SAAS;AACT,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB;AACvB,uEAAuE;AACvE,qEAAqE;AACrE,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAQ/B,kDAAkD;AAClD,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB;AAChB,0DAA0D;AAC1D,mEAAmE;AACnE,iEAAiE;AACjE,gCAAgC;AAChC,kBAAkB;AAClB,2DAA2D;AAC3D,gEAAgE;AAChE,mEAAmE;AACnE,4DAA4D;AAC5D,8DAA8D;AAC9D,sCAAsC;AACtC,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
@@ -56,6 +56,7 @@ import type { ResultsMapper } from './transaction.js';
56
56
  export declare const DEFAULT_SLOW_QUERY_THRESHOLD_MS = 1000;
57
57
  export { getPool, applyDatabaseUrlToEnv } from './pool.js';
58
58
  export { applyDbSecretToEnv } from './secret.js';
59
+ export { TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './tenant.js';
59
60
  export { mapResults, mapRow, configureMapper, getMapperConfig, } from './mapper.js';
60
61
  export type { MapperOptions } from './mapper.js';
61
62
  export { beginTransaction, withTransaction, buildTransaction, } from './transaction.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAItD;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAqBrD,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACvD,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EAAE,EACvB,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,EAChC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CA6CZ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtD;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAqBrD,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AA+B5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACvD,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EAAE,EACvB,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,EAChC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CA6CZ"}
@@ -47,6 +47,7 @@
47
47
  import { getPool } from './pool.js';
48
48
  import { mapResults } from './mapper.js';
49
49
  import { applyDbSecretToEnv } from './secret.js';
50
+ import { applyTenantToClient, isTenancyEngaged } from './tenant.js';
50
51
  import { dbLog } from '../internal/logger.js';
51
52
  /**
52
53
  * Default threshold above which a successful query is logged as slow.
@@ -76,8 +77,34 @@ function previewSql(sql) {
76
77
  }
77
78
  export { getPool, applyDatabaseUrlToEnv } from './pool.js';
78
79
  export { applyDbSecretToEnv } from './secret.js';
80
+ export { TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './tenant.js';
79
81
  export { mapResults, mapRow, configureMapper, getMapperConfig, } from './mapper.js';
80
82
  export { beginTransaction, withTransaction, buildTransaction, } from './transaction.js';
83
+ /**
84
+ * Run one statement, ensuring the tenant GUC is in place when tenancy
85
+ * is engaged.
86
+ *
87
+ * `pool.query()` borrows a connection for the duration of a single
88
+ * statement, which is why the tenant-scoped path checks out the
89
+ * connection EXPLICITLY: `set_config` and the statement itself must
90
+ * land on the same physical connection, and only an explicit checkout
91
+ * guarantees that. When tenancy has never been engaged we keep the
92
+ * original `pool.query()` call so existing consumers see no behavioural
93
+ * or performance change at all.
94
+ */
95
+ async function execute(pool, sqlQuery, queryParams) {
96
+ if (!isTenancyEngaged()) {
97
+ return pool.query(sqlQuery, queryParams);
98
+ }
99
+ const client = await pool.connect();
100
+ try {
101
+ await applyTenantToClient(client);
102
+ return await client.query(sqlQuery, queryParams);
103
+ }
104
+ finally {
105
+ client.release();
106
+ }
107
+ }
81
108
  /**
82
109
  * Execute a SQL query against the connection pool.
83
110
  *
@@ -122,7 +149,7 @@ export async function query(sqlQuery, queryParams, resultsMapper, mapperOptions)
122
149
  const startedAt = Date.now();
123
150
  let res;
124
151
  try {
125
- res = await pool.query(sqlQuery, queryParams);
152
+ res = await execute(pool, sqlQuery, queryParams);
126
153
  }
127
154
  catch (err) {
128
155
  // Capture pg's structured fields when present (`code`, `severity`,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAA8B,MAAM,aAAa,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAErD,SAAS,2BAA2B;IAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IACnD,IAAI,CAAC,GAAG;QAAE,OAAO,+BAA+B,CAAC;IACjD,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,+BAA+B,CAAC;AAC5E,CAAC;AAED;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AACrE,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAG1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,QAAgB,EAChB,WAAuB,EACvB,aAAgC,EAChC,aAA6B;IAE7B,sEAAsE;IACtE,0EAA0E;IAC1E,8EAA8E;IAC9E,6CAA6C;IAC7C,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,GAAgB,CAAC;IACrB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,mEAAmE;QACnE,gEAAgE;QAChE,mEAAmE;QACnE,mEAAmE;QACnE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAC1C,MAAM,MAAM,GAAG,GAA6D,CAAC;QAC7E,KAAK,CAAC,KAAK,CAAC,uBAAuB,EAAE;YACnC,UAAU;YACV,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC;YAChC,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;YACpC,SAAS,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;YACtD,YAAY,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YAC9D,MAAM,EAAE,MAAM,CAAC,IAAI;YACnB,UAAU,EAAE,MAAM,CAAC,QAAQ;YAC3B,SAAS,EAAE,MAAM,CAAC,OAAO;SAC1B,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IAC1C,MAAM,MAAM,GAAG,2BAA2B,EAAE,CAAC;IAC7C,IAAI,MAAM,GAAG,CAAC,IAAI,UAAU,IAAI,MAAM,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,qBAAqB,EAAE;YAChC,UAAU;YACV,WAAW,EAAE,MAAM;YACnB,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM;YACzC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC;SACjC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACnD,OAAO,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAsB,CAAC;AACxE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,8 @@
15
15
  * - DB_SSL_INSECURE ("true" to skip certificate verification — NOT recommended)
16
16
  * - DB_POOL_MAX (default 10)
17
17
  * - DB_CONNECT_TIMEOUT_MS (default 5000)
18
+ * - DB_QUERY_TIMEOUT_MS (client-side per-query ceiling; default 0 = disabled)
19
+ * - DB_KEEPALIVE_INITIAL_DELAY_MS (TCP keepalive first-probe delay; default 10000)
18
20
  *
19
21
  * The connection-timeout default is intentionally well below the 10 s
20
22
  * Lambda timeout used by the `nano` / `micro` presets: an unreachable
@@ -31,6 +33,44 @@ import { Pool, type PoolConfig } from 'pg';
31
33
  * to RDS Postgres) don't get false-positive aborts.
32
34
  */
33
35
  export declare const DEFAULT_CONNECT_TIMEOUT_MS = 5000;
36
+ /**
37
+ * Default `pg.Client#query_timeout` — a CLIENT-side per-query ceiling, in ms.
38
+ *
39
+ * `0` (the default) leaves it disabled, preserving pre-existing behavior.
40
+ * Unlike a server-side `statement_timeout`, a client-side timeout fires
41
+ * even when the TCP connection is a black hole — the server never receives
42
+ * the query and so can never cancel it. That is exactly the failure mode of
43
+ * a pooled connection reused after a Lambda freeze/thaw, where the socket
44
+ * died mid-freeze: without a client ceiling the query hangs until the OS TCP
45
+ * retransmit budget is exhausted (minutes), which is how a fire-and-forget
46
+ * write can surface as a ~300 s "connection timeout".
47
+ *
48
+ * It is pool-wide (inherited by every pooled client, transaction clients
49
+ * included), so it is DISABLED by default and must be opted into via
50
+ * `DB_QUERY_TIMEOUT_MS` on runtime Lambdas ONLY — never in the migration
51
+ * runner, whose long index builds run with `statementTimeoutMs: 0` and would
52
+ * be aborted client-side by it.
53
+ */
54
+ export declare const DEFAULT_QUERY_TIMEOUT_MS = 0;
55
+ /**
56
+ * Resolve the client-side `query_timeout` (ms) from `DB_QUERY_TIMEOUT_MS`.
57
+ * Falls back to the (disabled) default on unset / non-finite / negative
58
+ * values, matching `resolveConnectionTimeoutMs`. Exported for the unit tests.
59
+ */
60
+ export declare function resolveQueryTimeoutMs(): number;
61
+ /**
62
+ * Default TCP-keepalive first-probe delay (ms). `keepAlive` is enabled
63
+ * unconditionally on the pool so a peer that vanished while the socket sat
64
+ * idle (e.g. an RDS connection dropped during a Lambda freeze) is detected
65
+ * by the OS rather than lingering as a half-open connection. This value maps
66
+ * to TCP_KEEPIDLE — how long the socket is idle before the first probe.
67
+ */
68
+ export declare const DEFAULT_KEEPALIVE_INITIAL_DELAY_MS = 10000;
69
+ /**
70
+ * Resolve the TCP-keepalive first-probe delay (ms) from
71
+ * `DB_KEEPALIVE_INITIAL_DELAY_MS`. Exported for the unit tests.
72
+ */
73
+ export declare function resolveKeepAliveInitialDelayMs(): number;
34
74
  /**
35
75
  * Bridge `DATABASE_URL` into the granular `DB_*` slots `getPool()` reads.
36
76
  * Idempotent — only fills slots that aren't already set, so the granular
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/query/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAM3C;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAShD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAoC5C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAG3D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IACvB,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,oBAAoB,GAAG,qBAAqB,GAAG,oBAAoB,CAAC;CAChG,CAAC;AAEF,8DAA8D;AAC9D,wBAAgB,UAAU,IAAI,WAAW,CA8CxC;AAED,wBAAgB,OAAO,IAAI,IAAI,CAsD9B"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/query/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAM3C;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAShD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kCAAkC,QAAS,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,8BAA8B,IAAI,MAAM,CAKvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAoC5C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAG3D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IACvB,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,oBAAoB,GAAG,qBAAqB,GAAG,oBAAoB,CAAC;CAChG,CAAC;AAEF,8DAA8D;AAC9D,wBAAgB,UAAU,IAAI,WAAW,CA8CxC;AAED,wBAAgB,OAAO,IAAI,IAAI,CAqE9B"}
@@ -15,6 +15,8 @@
15
15
  * - DB_SSL_INSECURE ("true" to skip certificate verification — NOT recommended)
16
16
  * - DB_POOL_MAX (default 10)
17
17
  * - DB_CONNECT_TIMEOUT_MS (default 5000)
18
+ * - DB_QUERY_TIMEOUT_MS (client-side per-query ceiling; default 0 = disabled)
19
+ * - DB_KEEPALIVE_INITIAL_DELAY_MS (TCP keepalive first-probe delay; default 10000)
18
20
  *
19
21
  * The connection-timeout default is intentionally well below the 10 s
20
22
  * Lambda timeout used by the `nano` / `micro` presets: an unreachable
@@ -41,6 +43,56 @@ function resolveConnectionTimeoutMs() {
41
43
  const n = Number.parseInt(raw, 10);
42
44
  return Number.isFinite(n) && n >= 0 ? n : DEFAULT_CONNECT_TIMEOUT_MS;
43
45
  }
46
+ /**
47
+ * Default `pg.Client#query_timeout` — a CLIENT-side per-query ceiling, in ms.
48
+ *
49
+ * `0` (the default) leaves it disabled, preserving pre-existing behavior.
50
+ * Unlike a server-side `statement_timeout`, a client-side timeout fires
51
+ * even when the TCP connection is a black hole — the server never receives
52
+ * the query and so can never cancel it. That is exactly the failure mode of
53
+ * a pooled connection reused after a Lambda freeze/thaw, where the socket
54
+ * died mid-freeze: without a client ceiling the query hangs until the OS TCP
55
+ * retransmit budget is exhausted (minutes), which is how a fire-and-forget
56
+ * write can surface as a ~300 s "connection timeout".
57
+ *
58
+ * It is pool-wide (inherited by every pooled client, transaction clients
59
+ * included), so it is DISABLED by default and must be opted into via
60
+ * `DB_QUERY_TIMEOUT_MS` on runtime Lambdas ONLY — never in the migration
61
+ * runner, whose long index builds run with `statementTimeoutMs: 0` and would
62
+ * be aborted client-side by it.
63
+ */
64
+ export const DEFAULT_QUERY_TIMEOUT_MS = 0;
65
+ /**
66
+ * Resolve the client-side `query_timeout` (ms) from `DB_QUERY_TIMEOUT_MS`.
67
+ * Falls back to the (disabled) default on unset / non-finite / negative
68
+ * values, matching `resolveConnectionTimeoutMs`. Exported for the unit tests.
69
+ */
70
+ export function resolveQueryTimeoutMs() {
71
+ const raw = process.env.DB_QUERY_TIMEOUT_MS;
72
+ if (!raw)
73
+ return DEFAULT_QUERY_TIMEOUT_MS;
74
+ const n = Number.parseInt(raw, 10);
75
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_QUERY_TIMEOUT_MS;
76
+ }
77
+ /**
78
+ * Default TCP-keepalive first-probe delay (ms). `keepAlive` is enabled
79
+ * unconditionally on the pool so a peer that vanished while the socket sat
80
+ * idle (e.g. an RDS connection dropped during a Lambda freeze) is detected
81
+ * by the OS rather than lingering as a half-open connection. This value maps
82
+ * to TCP_KEEPIDLE — how long the socket is idle before the first probe.
83
+ */
84
+ export const DEFAULT_KEEPALIVE_INITIAL_DELAY_MS = 10_000;
85
+ /**
86
+ * Resolve the TCP-keepalive first-probe delay (ms) from
87
+ * `DB_KEEPALIVE_INITIAL_DELAY_MS`. Exported for the unit tests.
88
+ */
89
+ export function resolveKeepAliveInitialDelayMs() {
90
+ const raw = process.env.DB_KEEPALIVE_INITIAL_DELAY_MS;
91
+ if (!raw)
92
+ return DEFAULT_KEEPALIVE_INITIAL_DELAY_MS;
93
+ const n = Number.parseInt(raw, 10);
94
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_KEEPALIVE_INITIAL_DELAY_MS;
95
+ }
44
96
  /**
45
97
  * Bridge `DATABASE_URL` into the granular `DB_*` slots `getPool()` reads.
46
98
  * Idempotent — only fills slots that aren't already set, so the granular
@@ -157,6 +209,8 @@ export function getPool() {
157
209
  const { ssl, mode: sslMode } = resolveSsl();
158
210
  const max = Number(process.env.DB_POOL_MAX ?? 10);
159
211
  const connectionTimeoutMillis = resolveConnectionTimeoutMs();
212
+ const queryTimeoutMillis = resolveQueryTimeoutMs();
213
+ const keepAliveInitialDelayMillis = resolveKeepAliveInitialDelayMs();
160
214
  const host = process.env.DB_HOST;
161
215
  const port = Number(process.env.DB_PORT ?? 5432);
162
216
  const database = process.env.DB_NAME;
@@ -180,6 +234,9 @@ export function getPool() {
180
234
  sslMode,
181
235
  max,
182
236
  connectionTimeoutMillis,
237
+ // 0 = disabled; any positive value is a hard client-side ceiling.
238
+ queryTimeoutMillis,
239
+ keepAliveInitialDelayMillis,
183
240
  });
184
241
  pool = new Pool({
185
242
  host,
@@ -190,6 +247,16 @@ export function getPool() {
190
247
  ssl,
191
248
  max,
192
249
  connectionTimeoutMillis,
250
+ // Enable TCP keepalive so a connection whose peer disappeared while
251
+ // this Lambda was frozen is detected by the OS instead of looking
252
+ // healthy until the next query hangs on the dead socket.
253
+ keepAlive: true,
254
+ keepAliveInitialDelayMillis,
255
+ // Only set query_timeout when explicitly enabled (> 0). Left off by
256
+ // default so the migration runner (long index builds via
257
+ // beginTransaction({ statementTimeoutMs: 0 })) is never aborted
258
+ // client-side. Enable via DB_QUERY_TIMEOUT_MS on runtime Lambdas.
259
+ ...(queryTimeoutMillis > 0 ? { query_timeout: queryTimeoutMillis } : {}),
193
260
  });
194
261
  // Async pool errors (e.g. an idle client losing its TCP connection)
195
262
  // would otherwise crash the Lambda with no breadcrumb. Capture them
@@ -1 +1 @@
1
- {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/query/pool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/query/pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Tenant context for row-level security (RLS).
3
+ *
4
+ * # What this is for
5
+ *
6
+ * Multi-tenant apps filter nearly every query by a tenant column. That
7
+ * filter is applied by hand, in application SQL, hundreds of times —
8
+ * and a single omission silently returns another tenant's rows. Neither
9
+ * the type system nor code review reliably catches it: the usual shape
10
+ * is "resolve the row by primary key after checking the tenant
11
+ * separately", so the dangerous query looks identical to the safe one.
12
+ *
13
+ * This module moves the guarantee into Postgres. The caller wraps a
14
+ * request in {@link runWithTenant}; every query issued inside that
15
+ * scope — pooled or transactional — runs with the `app.tenant_id` GUC
16
+ * set, so RLS policies can filter rows the application forgot to.
17
+ * A missing predicate then yields ZERO rows instead of a cross-tenant
18
+ * leak.
19
+ *
20
+ * # The policy shape this expects
21
+ *
22
+ * ```sql
23
+ * ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
24
+ * ALTER TABLE posts FORCE ROW LEVEL SECURITY;
25
+ * CREATE POLICY tenant_isolation ON posts
26
+ * USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid);
27
+ * ```
28
+ *
29
+ * Two details matter:
30
+ * - the `true` second argument to `current_setting` makes an UNSET
31
+ * GUC return NULL rather than raising;
32
+ * - `NULLIF(..., '')` maps the "no tenant" sentinel this module
33
+ * writes onto NULL, so the comparison is NULL, so the row is
34
+ * filtered. **Unset means nothing is visible** — fail-closed. Get
35
+ * this wrong (e.g. omit the NULLIF and cast `''::uuid`) and you
36
+ * trade a leak for a runtime error on every unscoped query.
37
+ *
38
+ * RLS does not apply to a table's OWNER unless `FORCE ROW LEVEL
39
+ * SECURITY` is set, and is skipped entirely for roles with `BYPASSRLS`.
40
+ * Run the application as a role that owns nothing and has neither
41
+ * attribute; keep migrations and platform-level jobs on a separate
42
+ * privileged role. Enabling RLS without that split gives you the
43
+ * overhead and none of the protection.
44
+ *
45
+ * # Why the GUC is set per connection, not per query
46
+ *
47
+ * `query()` borrows a connection from the pool for the duration of one
48
+ * statement, so a session-level `SET` would not survive to the next
49
+ * call, and `SET LOCAL` requires an open transaction. Instead the
50
+ * applied value is memoized per physical connection: the extra
51
+ * round-trip is paid only when a connection's tenant actually changes.
52
+ * A warm Lambda container serving one tenant pays it once.
53
+ *
54
+ * Transactions take the cheaper path — `set_config(..., is_local =>
55
+ * true)`, which reverts on COMMIT/ROLLBACK and therefore never
56
+ * disturbs the memoized session value.
57
+ *
58
+ * # Backwards compatibility
59
+ *
60
+ * Consumers that never call {@link runWithTenant} are untouched: the
61
+ * module stays dormant until the first call, and `query()` keeps using
62
+ * `pool.query()` directly with no added statements.
63
+ */
64
+ import type { PoolClient } from 'pg';
65
+ /**
66
+ * The GUC that carries the current tenant into Postgres. `app.` is a
67
+ * customizable-options prefix, so no superuser configuration is
68
+ * needed for the setting to exist.
69
+ */
70
+ export declare const TENANT_GUC = "app.tenant_id";
71
+ /** True once tenancy has been engaged. Read by `query()`. */
72
+ export declare function isTenancyEngaged(): boolean;
73
+ /**
74
+ * The tenant id for the current async scope, or `null` outside any
75
+ * scope / inside a {@link runWithoutTenant} block.
76
+ */
77
+ export declare function getCurrentTenantId(): string | null;
78
+ /**
79
+ * Run `fn` with `tenantId` applied to every query it issues.
80
+ *
81
+ * Wrap the whole request here — typically from tenant-resolution
82
+ * middleware, once `ctx.tenant` is known — so repos and helpers
83
+ * inherit the scope without threading an argument through every
84
+ * signature.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * await runWithTenant(ctx.tenant.id, async () => {
89
+ * // Any query in here, at any depth, is tenant-scoped by Postgres.
90
+ * return handler(body, ctx);
91
+ * });
92
+ * ```
93
+ */
94
+ export declare function runWithTenant<T>(tenantId: string, fn: () => Promise<T>): Promise<T>;
95
+ /**
96
+ * Run `fn` with NO tenant applied, clearing any inherited scope.
97
+ *
98
+ * For work that is legitimately cross-tenant: resolving which tenant a
99
+ * hostname belongs to, platform-operator queries, cron jobs sweeping
100
+ * every tenant. Queries inside still execute against RLS with an
101
+ * unset GUC, so tenant-scoped tables return nothing — the caller is
102
+ * expected to be reading un-scoped tables, or to be connected as a
103
+ * privileged role that bypasses RLS.
104
+ *
105
+ * Deliberately verbose to write. An escape hatch should be visible in
106
+ * review.
107
+ */
108
+ export declare function runWithoutTenant<T>(fn: () => Promise<T>): Promise<T>;
109
+ /**
110
+ * Ensure `client`'s session GUC matches the current scope, issuing
111
+ * `set_config` only when it differs.
112
+ *
113
+ * Uses a bound parameter — GUC values are user-controlled tenant ids
114
+ * and `SET` does not accept placeholders, so `set_config()` is the
115
+ * only injection-safe way to write one.
116
+ */
117
+ export declare function applyTenantToClient(client: PoolClient): Promise<void>;
118
+ /**
119
+ * Apply the current scope's tenant to an open transaction.
120
+ *
121
+ * `is_local => true` scopes the value to the transaction, so it
122
+ * reverts on COMMIT/ROLLBACK and leaves the connection's session
123
+ * value — and therefore {@link appliedByClient} — untouched.
124
+ *
125
+ * No-op until tenancy is engaged, so existing transactional consumers
126
+ * are unaffected.
127
+ */
128
+ export declare function applyTenantToTransaction(client: PoolClient): Promise<void>;
129
+ /**
130
+ * Reset module state. Test-only — production code has no reason to
131
+ * disengage tenancy once engaged.
132
+ */
133
+ export declare function __resetTenancyForTests(): void;
134
+ //# sourceMappingURL=tenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/query/tenant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAiC1C,6DAA6D;AAC7D,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAElD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CASnF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGpE;AAaD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3E;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Tenant context for row-level security (RLS).
3
+ *
4
+ * # What this is for
5
+ *
6
+ * Multi-tenant apps filter nearly every query by a tenant column. That
7
+ * filter is applied by hand, in application SQL, hundreds of times —
8
+ * and a single omission silently returns another tenant's rows. Neither
9
+ * the type system nor code review reliably catches it: the usual shape
10
+ * is "resolve the row by primary key after checking the tenant
11
+ * separately", so the dangerous query looks identical to the safe one.
12
+ *
13
+ * This module moves the guarantee into Postgres. The caller wraps a
14
+ * request in {@link runWithTenant}; every query issued inside that
15
+ * scope — pooled or transactional — runs with the `app.tenant_id` GUC
16
+ * set, so RLS policies can filter rows the application forgot to.
17
+ * A missing predicate then yields ZERO rows instead of a cross-tenant
18
+ * leak.
19
+ *
20
+ * # The policy shape this expects
21
+ *
22
+ * ```sql
23
+ * ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
24
+ * ALTER TABLE posts FORCE ROW LEVEL SECURITY;
25
+ * CREATE POLICY tenant_isolation ON posts
26
+ * USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid);
27
+ * ```
28
+ *
29
+ * Two details matter:
30
+ * - the `true` second argument to `current_setting` makes an UNSET
31
+ * GUC return NULL rather than raising;
32
+ * - `NULLIF(..., '')` maps the "no tenant" sentinel this module
33
+ * writes onto NULL, so the comparison is NULL, so the row is
34
+ * filtered. **Unset means nothing is visible** — fail-closed. Get
35
+ * this wrong (e.g. omit the NULLIF and cast `''::uuid`) and you
36
+ * trade a leak for a runtime error on every unscoped query.
37
+ *
38
+ * RLS does not apply to a table's OWNER unless `FORCE ROW LEVEL
39
+ * SECURITY` is set, and is skipped entirely for roles with `BYPASSRLS`.
40
+ * Run the application as a role that owns nothing and has neither
41
+ * attribute; keep migrations and platform-level jobs on a separate
42
+ * privileged role. Enabling RLS without that split gives you the
43
+ * overhead and none of the protection.
44
+ *
45
+ * # Why the GUC is set per connection, not per query
46
+ *
47
+ * `query()` borrows a connection from the pool for the duration of one
48
+ * statement, so a session-level `SET` would not survive to the next
49
+ * call, and `SET LOCAL` requires an open transaction. Instead the
50
+ * applied value is memoized per physical connection: the extra
51
+ * round-trip is paid only when a connection's tenant actually changes.
52
+ * A warm Lambda container serving one tenant pays it once.
53
+ *
54
+ * Transactions take the cheaper path — `set_config(..., is_local =>
55
+ * true)`, which reverts on COMMIT/ROLLBACK and therefore never
56
+ * disturbs the memoized session value.
57
+ *
58
+ * # Backwards compatibility
59
+ *
60
+ * Consumers that never call {@link runWithTenant} are untouched: the
61
+ * module stays dormant until the first call, and `query()` keeps using
62
+ * `pool.query()` directly with no added statements.
63
+ */
64
+ import { AsyncLocalStorage } from 'node:async_hooks';
65
+ /**
66
+ * The GUC that carries the current tenant into Postgres. `app.` is a
67
+ * customizable-options prefix, so no superuser configuration is
68
+ * needed for the setting to exist.
69
+ */
70
+ export const TENANT_GUC = 'app.tenant_id';
71
+ /**
72
+ * Value written when a scope is explicitly un-tenanted
73
+ * ({@link runWithoutTenant}). The empty string — rather than
74
+ * unsetting — so the reset is an ordinary value assignment that the
75
+ * per-connection memo can track, and so policies distinguish "cleared"
76
+ * from "never set" identically via `NULLIF`.
77
+ */
78
+ const NO_TENANT = '';
79
+ const storage = new AsyncLocalStorage();
80
+ /**
81
+ * Whether any caller has engaged tenancy in this process.
82
+ *
83
+ * Gates the whole feature so existing consumers see byte-identical
84
+ * behaviour: until someone calls {@link runWithTenant} or
85
+ * {@link runWithoutTenant}, `query()` must not issue `set_config` or
86
+ * take the explicit connect/release path.
87
+ *
88
+ * One-way latch — once engaged, every subsequent query participates,
89
+ * including those OUTSIDE any tenant scope. That is the fail-closed
90
+ * half of the design: a request that forgets to establish context does
91
+ * not silently inherit whichever tenant last used its connection.
92
+ */
93
+ let engaged = false;
94
+ /** True once tenancy has been engaged. Read by `query()`. */
95
+ export function isTenancyEngaged() {
96
+ return engaged;
97
+ }
98
+ /**
99
+ * The tenant id for the current async scope, or `null` outside any
100
+ * scope / inside a {@link runWithoutTenant} block.
101
+ */
102
+ export function getCurrentTenantId() {
103
+ return storage.getStore()?.tenantId ?? null;
104
+ }
105
+ /**
106
+ * Run `fn` with `tenantId` applied to every query it issues.
107
+ *
108
+ * Wrap the whole request here — typically from tenant-resolution
109
+ * middleware, once `ctx.tenant` is known — so repos and helpers
110
+ * inherit the scope without threading an argument through every
111
+ * signature.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * await runWithTenant(ctx.tenant.id, async () => {
116
+ * // Any query in here, at any depth, is tenant-scoped by Postgres.
117
+ * return handler(body, ctx);
118
+ * });
119
+ * ```
120
+ */
121
+ export function runWithTenant(tenantId, fn) {
122
+ if (!tenantId) {
123
+ throw new Error('runWithTenant requires a non-empty tenantId. Use runWithoutTenant() for ' +
124
+ 'deliberately un-scoped work.');
125
+ }
126
+ engaged = true;
127
+ return storage.run({ tenantId }, fn);
128
+ }
129
+ /**
130
+ * Run `fn` with NO tenant applied, clearing any inherited scope.
131
+ *
132
+ * For work that is legitimately cross-tenant: resolving which tenant a
133
+ * hostname belongs to, platform-operator queries, cron jobs sweeping
134
+ * every tenant. Queries inside still execute against RLS with an
135
+ * unset GUC, so tenant-scoped tables return nothing — the caller is
136
+ * expected to be reading un-scoped tables, or to be connected as a
137
+ * privileged role that bypasses RLS.
138
+ *
139
+ * Deliberately verbose to write. An escape hatch should be visible in
140
+ * review.
141
+ */
142
+ export function runWithoutTenant(fn) {
143
+ engaged = true;
144
+ return storage.run({ tenantId: null }, fn);
145
+ }
146
+ /**
147
+ * Last value applied to each physical connection.
148
+ *
149
+ * WeakMap so entries vanish with the connection and nothing is
150
+ * retained across warm invocations. Absent means "never set on this
151
+ * connection", which is distinct from `NO_TENANT` (explicitly
152
+ * cleared) and forces the first query on a fresh connection to write
153
+ * the GUC.
154
+ */
155
+ const appliedByClient = new WeakMap();
156
+ /**
157
+ * Ensure `client`'s session GUC matches the current scope, issuing
158
+ * `set_config` only when it differs.
159
+ *
160
+ * Uses a bound parameter — GUC values are user-controlled tenant ids
161
+ * and `SET` does not accept placeholders, so `set_config()` is the
162
+ * only injection-safe way to write one.
163
+ */
164
+ export async function applyTenantToClient(client) {
165
+ const want = getCurrentTenantId() ?? NO_TENANT;
166
+ if (appliedByClient.get(client) === want)
167
+ return;
168
+ // Record BEFORE awaiting: a rejected set_config must not leave the
169
+ // memo claiming a value the server never accepted.
170
+ appliedByClient.delete(client);
171
+ await client.query(`SELECT set_config($1, $2, false)`, [TENANT_GUC, want]);
172
+ appliedByClient.set(client, want);
173
+ }
174
+ /**
175
+ * Apply the current scope's tenant to an open transaction.
176
+ *
177
+ * `is_local => true` scopes the value to the transaction, so it
178
+ * reverts on COMMIT/ROLLBACK and leaves the connection's session
179
+ * value — and therefore {@link appliedByClient} — untouched.
180
+ *
181
+ * No-op until tenancy is engaged, so existing transactional consumers
182
+ * are unaffected.
183
+ */
184
+ export async function applyTenantToTransaction(client) {
185
+ if (!engaged)
186
+ return;
187
+ const want = getCurrentTenantId() ?? NO_TENANT;
188
+ await client.query(`SELECT set_config($1, $2, true)`, [TENANT_GUC, want]);
189
+ }
190
+ /**
191
+ * Reset module state. Test-only — production code has no reason to
192
+ * disengage tenancy once engaged.
193
+ */
194
+ export function __resetTenancyForTests() {
195
+ engaged = false;
196
+ }
197
+ //# sourceMappingURL=tenant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/query/tenant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,EAAE,CAAC;AAOrB,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAe,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,IAAI,OAAO,GAAG,KAAK,CAAC;AAEpB,6DAA6D;AAC7D,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,OAAO,CAAC,QAAQ,EAAE,EAAE,QAAQ,IAAI,IAAI,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,aAAa,CAAI,QAAgB,EAAE,EAAoB;IACrE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,0EAA0E;YACxE,8BAA8B,CACjC,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,IAAI,CAAC;IACf,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAI,EAAoB;IACtD,OAAO,GAAG,IAAI,CAAC;IACf,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,eAAe,GAAG,IAAI,OAAO,EAAsB,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAkB;IAC1D,MAAM,IAAI,GAAG,kBAAkB,EAAE,IAAI,SAAS,CAAC;IAC/C,IAAI,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI;QAAE,OAAO;IACjD,mEAAmE;IACnE,mDAAmD;IACnD,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;IAC3E,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,MAAkB;IAC/D,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,MAAM,IAAI,GAAG,kBAAkB,EAAE,IAAI,SAAS,CAAC;IAC/C,MAAM,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,GAAG,KAAK,CAAC;AAClB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/query/transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,IAAI,CAAC;AAElD,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAc7D,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACjC,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EAAE,EACvB,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,EAChC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,6BAA6B;IAC7B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,gCAAgC;IAChC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAUnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAwCzF;AAaD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAAC;AACd,wBAAsB,eAAe,CAAC,CAAC,EACrC,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAAC;AAkDd;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,CAkDhE"}
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/query/transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,IAAI,CAAC;AAElD,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAe7D,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACjC,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EAAE,EACvB,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,EAChC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,6BAA6B;IAC7B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,gCAAgC;IAChC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAUnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CA8CzF;AAaD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAAC;AACd,wBAAsB,eAAe,CAAC,CAAC,EACrC,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAAC;AAkDd;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,CAkDhE"}
@@ -9,6 +9,7 @@
9
9
  import { getPool } from './pool.js';
10
10
  import { mapResults } from './mapper.js';
11
11
  import { applyDbSecretToEnv } from './secret.js';
12
+ import { applyTenantToTransaction } from './tenant.js';
12
13
  import { dbLog } from '../internal/logger.js';
13
14
  /**
14
15
  * Same single-line clip used by `query()`'s logger. Duplicated here
@@ -90,6 +91,11 @@ export async function beginTransaction(options) {
90
91
  const ms = Math.floor(Number(timeoutMs));
91
92
  await client.query(`SET LOCAL statement_timeout = ${ms}`);
92
93
  }
94
+ // Publish the ambient tenant scope to this transaction so RLS policies
95
+ // see it. Transaction-local, so it reverts on COMMIT/ROLLBACK and never
96
+ // leaks to the next borrower of this pooled connection. No-op until a
97
+ // consumer engages tenancy via runWithTenant().
98
+ await applyTenantToTransaction(client);
93
99
  return buildTransaction(client);
94
100
  }
95
101
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../src/query/transaction.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../src/query/transaction.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venturekit/data",
3
- "version": "0.0.0-dev.20260721010549",
3
+ "version": "0.0.0-dev.20260725205100",
4
4
  "description": "Database and data layer for VentureKit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,13 +24,13 @@
24
24
  "license": "Apache-2.0",
25
25
  "exports": {
26
26
  ".": {
27
- "import": "./dist/index.js",
28
- "types": "./dist/index.d.ts"
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
32
  "@aws-sdk/client-secrets-manager": "^3.1068.0",
33
- "@venturekit/core": "0.0.0-dev.20260721010549",
33
+ "@venturekit/core": "0.0.0-dev.20260725205100",
34
34
  "pg": "^8.12.0"
35
35
  },
36
36
  "devDependencies": {