@stratum-hq/db-adapters 1.0.0 → 1.2.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/README.md +39 -4
  2. package/dist/adapters/database-prisma.d.ts +4 -2
  3. package/dist/adapters/database-prisma.d.ts.map +1 -1
  4. package/dist/adapters/database-prisma.js +19 -6
  5. package/dist/adapters/database-prisma.js.map +1 -1
  6. package/dist/adapters/database-raw.d.ts +2 -0
  7. package/dist/adapters/database-raw.d.ts.map +1 -1
  8. package/dist/adapters/database-raw.js +36 -13
  9. package/dist/adapters/database-raw.js.map +1 -1
  10. package/dist/adapters/drizzle.d.ts +2 -2
  11. package/dist/adapters/drizzle.d.ts.map +1 -1
  12. package/dist/adapters/drizzle.js +9 -8
  13. package/dist/adapters/drizzle.js.map +1 -1
  14. package/dist/adapters/prisma.d.ts +1 -1
  15. package/dist/adapters/prisma.d.ts.map +1 -1
  16. package/dist/adapters/prisma.js +12 -5
  17. package/dist/adapters/prisma.js.map +1 -1
  18. package/dist/adapters/schema-prisma.d.ts +36 -4
  19. package/dist/adapters/schema-prisma.d.ts.map +1 -1
  20. package/dist/adapters/schema-prisma.js +55 -17
  21. package/dist/adapters/schema-prisma.js.map +1 -1
  22. package/dist/adapters/schema-raw.d.ts +14 -3
  23. package/dist/adapters/schema-raw.d.ts.map +1 -1
  24. package/dist/adapters/schema-raw.js +30 -8
  25. package/dist/adapters/schema-raw.js.map +1 -1
  26. package/dist/database/manager.d.ts.map +1 -1
  27. package/dist/database/manager.js +7 -1
  28. package/dist/database/manager.js.map +1 -1
  29. package/dist/database/pool-manager.d.ts +38 -7
  30. package/dist/database/pool-manager.d.ts.map +1 -1
  31. package/dist/database/pool-manager.js +103 -27
  32. package/dist/database/pool-manager.js.map +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/migration-helpers.js +1 -1
  37. package/dist/migration-helpers.js.map +1 -1
  38. package/dist/rls/manager.js +1 -1
  39. package/dist/rls/manager.js.map +1 -1
  40. package/dist/schema/manager.d.ts +1 -0
  41. package/dist/schema/manager.d.ts.map +1 -1
  42. package/dist/schema/manager.js +11 -2
  43. package/dist/schema/manager.js.map +1 -1
  44. package/dist/schema/session.d.ts +10 -1
  45. package/dist/schema/session.d.ts.map +1 -1
  46. package/dist/schema/session.js +24 -4
  47. package/dist/schema/session.js.map +1 -1
  48. package/package.json +4 -3
@@ -5,10 +5,16 @@ exports.createSchemaTenantPool = createSchemaTenantPool;
5
5
  const core_1 = require("@stratum-hq/core");
6
6
  const manager_js_1 = require("../schema/manager.js");
7
7
  const session_js_1 = require("../schema/session.js");
8
+ function toError(err) {
9
+ return err instanceof Error ? err : new Error(String(err));
10
+ }
8
11
  class SchemaRawAdapter {
9
12
  pool;
10
- constructor(pool) {
13
+ extraSearchPath;
14
+ constructor(pool, options = {}) {
11
15
  this.pool = pool;
16
+ // Validate up front so a bad entry fails at construction, not per query.
17
+ this.extraSearchPath = (options.extraSearchPath ?? []).map(manager_js_1.validateSchemaName);
12
18
  }
13
19
  async query(tenantSlug, text, values) {
14
20
  return this.executeWithTenantContext(tenantSlug, async (client) => {
@@ -17,33 +23,49 @@ class SchemaRawAdapter {
17
23
  }
18
24
  /**
19
25
  * Executes a callback within a transaction scoped to the tenant's schema.
20
- * Sets `search_path` to `tenant_{slug}, public` for the duration of the transaction.
26
+ * Sets `search_path` to `tenant_{slug}`, followed only by any `extraSearchPath`
27
+ * schemas, for the duration of the transaction.
21
28
  */
22
29
  async executeWithTenantContext(tenantSlug, fn) {
23
30
  const safe = (0, core_1.validateSlug)(tenantSlug);
24
31
  const schemaName = (0, manager_js_1.tenantSchemaName)(safe);
25
32
  const client = await this.pool.connect();
33
+ // A failed ROLLBACK or RESET must not replace the result or the caller's
34
+ // error. That error goes to client.release instead: a truthy argument makes
35
+ // pg-pool destroy the connection, so no later caller gets a connection in
36
+ // an unknown transaction state or with an unknown search_path.
37
+ let releaseErr;
26
38
  try {
27
39
  await client.query("BEGIN");
28
40
  // SET LOCAL is transaction-scoped; schemaName is derived from a validated slug.
29
- await client.query(`SET LOCAL search_path TO ${schemaName}, public`);
41
+ await client.query(`SET LOCAL search_path TO ${(0, session_js_1.tenantSearchPath)(schemaName, this.extraSearchPath)}`);
30
42
  const result = await fn(client);
31
43
  await client.query("COMMIT");
32
44
  return result;
33
45
  }
34
46
  catch (err) {
35
- await client.query("ROLLBACK");
47
+ try {
48
+ await client.query("ROLLBACK");
49
+ }
50
+ catch (rollbackErr) {
51
+ releaseErr = toError(rollbackErr);
52
+ }
36
53
  throw err;
37
54
  }
38
55
  finally {
39
- await (0, session_js_1.resetSearchPath)(client);
40
- client.release();
56
+ try {
57
+ await (0, session_js_1.resetSearchPath)(client);
58
+ }
59
+ catch (resetErr) {
60
+ releaseErr ??= toError(resetErr);
61
+ }
62
+ client.release(releaseErr);
41
63
  }
42
64
  }
43
65
  }
44
66
  exports.SchemaRawAdapter = SchemaRawAdapter;
45
- function createSchemaTenantPool(pool, contextFn) {
46
- const adapter = new SchemaRawAdapter(pool);
67
+ function createSchemaTenantPool(pool, contextFn, options = {}) {
68
+ const adapter = new SchemaRawAdapter(pool, options);
47
69
  return new Proxy(adapter, {
48
70
  get(target, prop) {
49
71
  if (prop === "query") {
@@ -1 +1 @@
1
- {"version":3,"file":"schema-raw.js","sourceRoot":"","sources":["../../src/adapters/schema-raw.ts"],"names":[],"mappings":";;;AA8CA,wDAcC;AA3DD,2CAAgD;AAChD,qDAAwD;AACxD,qDAAuD;AAEvD,MAAa,gBAAgB;IACP;IAApB,YAAoB,IAAa;QAAb,SAAI,GAAJ,IAAI,CAAS;IAAG,CAAC;IAErC,KAAK,CAAC,KAAK,CACT,UAAkB,EAClB,IAAY,EACZ,MAAkB;QAElB,OAAO,IAAI,CAAC,wBAAwB,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAChE,OAAO,MAAM,CAAC,KAAK,CAAI,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,wBAAwB,CAC5B,UAAkB,EAClB,EAAyC;QAEzC,MAAM,IAAI,GAAG,IAAA,mBAAY,EAAC,UAAU,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,IAAA,6BAAgB,EAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,gFAAgF;YAChF,MAAM,MAAM,CAAC,KAAK,CAAC,4BAA4B,UAAU,UAAU,CAAC,CAAC;YACrE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC7B,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC/B,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,MAAM,IAAA,4BAAe,EAAC,MAAM,CAAC,CAAC;YAC9B,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;CACF;AAvCD,4CAuCC;AAED,SAAgB,sBAAsB,CACpC,IAAa,EACb,SAAuB;IAEvB,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;QACxB,GAAG,CAAC,MAAM,EAAE,IAAI;YACd,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACrB,OAAO,CAAC,IAAY,EAAE,MAAkB,EAAE,EAAE,CAC1C,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAC5C,CAAC;YACD,OAAQ,MAAsD,CAAC,IAAI,CAAC,CAAC;QACvE,CAAC;KACF,CAAqB,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"schema-raw.js","sourceRoot":"","sources":["../../src/adapters/schema-raw.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/database/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAKpB;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,MAAM,EACjC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,MAAM,EACjC,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,MAAM,EACjC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/database/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAOpB;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAS1D;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,MAAM,EACjC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,MAAM,EACjC,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,MAAM,EACjC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -6,13 +6,19 @@ exports.createDatabase = createDatabase;
6
6
  exports.dropDatabase = dropDatabase;
7
7
  const core_1 = require("@stratum-hq/core");
8
8
  const DB_PREFIX = "stratum_tenant_";
9
+ // PostgreSQL silently truncates database names longer than this many bytes.
10
+ const MAX_IDENTIFIER_BYTES = 63;
9
11
  /**
10
12
  * Returns the database name for a given tenant slug.
11
13
  * Format: stratum_tenant_{slug}
12
14
  */
13
15
  function getDatabaseName(tenantSlug) {
14
16
  (0, core_1.validateSlug)(tenantSlug);
15
- return `${DB_PREFIX}${tenantSlug}`;
17
+ const dbName = `${DB_PREFIX}${tenantSlug}`;
18
+ if (Buffer.byteLength(dbName) > MAX_IDENTIFIER_BYTES) {
19
+ throw new Error(`Database name for tenant slug "${tenantSlug}" exceeds ${MAX_IDENTIFIER_BYTES} bytes`);
20
+ }
21
+ return dbName;
16
22
  }
17
23
  /**
18
24
  * Checks whether the per-tenant database exists.
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/database/manager.ts"],"names":[],"mappings":";;AASA,0CAGC;AAMD,wCAUC;AASD,wCAiBC;AAQD,oCAMC;AAnED,2CAAgD;AAEhD,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAEpC;;;GAGG;AACH,SAAgB,eAAe,CAAC,UAAkB;IAChD,IAAA,mBAAY,EAAC,UAAU,CAAC,CAAC;IACzB,OAAO,GAAG,SAAS,GAAG,UAAU,EAAE,CAAC;AACrC,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,cAAc,CAClC,MAAiC,EACjC,UAAkB;IAElB,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,uEAAuE,EACvE,CAAC,MAAM,CAAC,CACT,CAAC;IACF,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAC5B,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,cAAc,CAClC,MAAiC,EACjC,UAAkB,EAClB,UAAmB;IAEnB,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,+FAA+F;IAC/F,8FAA8F;IAC9F,IAAI,GAAG,GAAG,oBAAoB,MAAM,GAAG,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,iEAAiE;QACjE,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,oCAAoC,UAAU,GAAG,CAAC,CAAC;QACrE,CAAC;QACD,GAAG,IAAI,cAAc,UAAU,GAAG,CAAC;IACrC,CAAC;IACD,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,YAAY,CAChC,MAAiC,EACjC,UAAkB;IAElB,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,MAAM,CAAC,KAAK,CAAC,4BAA4B,MAAM,GAAG,CAAC,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/database/manager.ts"],"names":[],"mappings":";;AAWA,0CASC;AAMD,wCAUC;AASD,wCAiBC;AAQD,oCAMC;AA3ED,2CAAgD;AAEhD,MAAM,SAAS,GAAG,iBAAiB,CAAC;AACpC,4EAA4E;AAC5E,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC;;;GAGG;AACH,SAAgB,eAAe,CAAC,UAAkB;IAChD,IAAA,mBAAY,EAAC,UAAU,CAAC,CAAC;IACzB,MAAM,MAAM,GAAG,GAAG,SAAS,GAAG,UAAU,EAAE,CAAC;IAC3C,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,oBAAoB,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,kCAAkC,UAAU,aAAa,oBAAoB,QAAQ,CACtF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,cAAc,CAClC,MAAiC,EACjC,UAAkB;IAElB,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,uEAAuE,EACvE,CAAC,MAAM,CAAC,CACT,CAAC;IACF,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAC5B,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,cAAc,CAClC,MAAiC,EACjC,UAAkB,EAClB,UAAmB;IAEnB,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,+FAA+F;IAC/F,8FAA8F;IAC9F,IAAI,GAAG,GAAG,oBAAoB,MAAM,GAAG,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,iEAAiE;QACjE,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,oCAAoC,UAAU,GAAG,CAAC,CAAC;QACrE,CAAC;QACD,GAAG,IAAI,cAAc,UAAU,GAAG,CAAC;IACrC,CAAC;IACD,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,YAAY,CAChC,MAAiC,EACjC,UAAkB;IAElB,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,MAAM,CAAC,KAAK,CAAC,4BAA4B,MAAM,GAAG,CAAC,CAAC;AAC5D,CAAC"}
@@ -4,40 +4,71 @@ export interface DatabasePoolManagerOptions {
4
4
  baseConnectionConfig: pg.PoolConfig;
5
5
  /** Maximum number of tenant pools to keep open simultaneously. Default: 50. */
6
6
  maxPools?: number;
7
- /** Milliseconds of inactivity before a pool is eligible for LRU eviction. Default: 30000. */
7
+ /**
8
+ * Milliseconds an idle connection stays open inside a tenant pool before pg
9
+ * closes it. The manager passes it to pg.Pool as idleTimeoutMillis. It does
10
+ * not control LRU eviction. Default: 30000.
11
+ */
8
12
  idleTimeoutMs?: number;
9
13
  }
10
14
  /**
11
15
  * Manages a collection of per-tenant pg.Pool instances.
12
16
  *
13
- * Pools are keyed by tenant slug and created on first access.
14
- * When the pool count exceeds maxPools, the least-recently-used pool is evicted.
17
+ * Pools are keyed by tenant slug and created on first access. Each getPool call
18
+ * holds the pool until the caller calls releasePool. When the pool count reaches
19
+ * maxPools, the least-recently-used pool that no caller holds is evicted. A pool
20
+ * that a caller holds is never evicted, so the count can exceed maxPools while
21
+ * every pool is in use.
15
22
  */
16
23
  export declare class DatabasePoolManager {
17
24
  private readonly pools;
25
+ /** Holds that callers still have on pools that closePool or closeAll removed, by pool key. */
26
+ private readonly closedHolds;
18
27
  private readonly baseConfig;
19
28
  private readonly maxPools;
20
29
  private readonly idleTimeoutMs;
21
30
  constructor(options: DatabasePoolManagerOptions);
22
31
  /**
23
- * Returns an existing pool for the tenant slug, or creates a new one.
24
- * Evicts the LRU pool if the pool limit is reached.
32
+ * Returns the pool for the tenant slug, and creates it on first access.
33
+ * Each call holds the pool until the caller calls releasePool with the same
34
+ * arguments. A held pool is never evicted.
25
35
  *
26
36
  * When a regionId is provided, the pool is keyed as `regionId:slug` to support
27
37
  * multi-region deployments where the same slug may exist in different regions.
28
38
  */
29
39
  getPool(tenantSlug: string, regionId?: string): Promise<pg.Pool>;
40
+ /**
41
+ * Ends one hold on the pool that getPool returned for the same arguments.
42
+ * Call it once for each getPool call. No-op if the pool is not tracked.
43
+ *
44
+ * Without a regionId, a bare slug also matches one region-prefixed pool
45
+ * (`regionId:slug`), as closePool does. If the slug matches pools in more
46
+ * than one region, the call releases nothing, because the manager cannot
47
+ * tell which pool the caller holds. Pass the regionId in that case.
48
+ */
49
+ releasePool(tenantSlug: string, regionId?: string): void;
30
50
  /** Closes and removes the pool for the given tenant slug. No-op if not found.
31
51
  * Accepts either a bare slug or a region-prefixed key (`regionId:slug`). */
32
52
  closePool(tenantSlug: string, regionId?: string): Promise<void>;
33
53
  /** Closes all managed pools. Call during application shutdown. */
34
54
  closeAll(): Promise<void>;
55
+ /** Keeps the holds of a removed pool, so that their releases do not reach a later pool for the key. */
56
+ private recordClosedHolds;
57
+ /**
58
+ * Returns the key that releasePool acts on: the exact key when the manager
59
+ * tracks it, else the one region-prefixed key for a bare slug. Returns
60
+ * undefined when no key or more than one key matches.
61
+ */
62
+ private resolveHeldKey;
35
63
  /** Returns a snapshot of current pool statistics. */
36
64
  getStats(): {
37
65
  poolCount: number;
38
66
  activeConnections: number;
39
67
  };
40
- /** Evicts the pool that has been idle the longest. */
41
- private evictLRU;
68
+ /**
69
+ * Removes the least-recently-used pool that no caller holds from the map and
70
+ * returns it. The caller ends it. Returns undefined when every pool is held.
71
+ */
72
+ private takeLRU;
42
73
  }
43
74
  //# sourceMappingURL=pool-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pool-manager.d.ts","sourceRoot":"","sources":["../../src/database/pool-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpB,MAAM,WAAW,0BAA0B;IACzC,mHAAmH;IACnH,oBAAoB,EAAE,EAAE,CAAC,UAAU,CAAC;IACpC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAOD;;;;;GAKG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,OAAO,EAAE,0BAA0B;IAM/C;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC;IAyBtE;gFAC4E;IACtE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBrE,kEAAkE;IAC5D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B,qDAAqD;IACrD,QAAQ,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAW5D,sDAAsD;YACxC,QAAQ;CAevB"}
1
+ {"version":3,"file":"pool-manager.d.ts","sourceRoot":"","sources":["../../src/database/pool-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpB,MAAM,WAAW,0BAA0B;IACzC,mHAAmH;IACnH,oBAAoB,EAAE,EAAE,CAAC,UAAU,CAAC;IACpC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;GAQG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,8FAA8F;IAC9F,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,OAAO,EAAE,0BAA0B;IAM/C;;;;;;;OAOG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC;IAgCtE;;;;;;;;OAQG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAqBxD;gFAC4E;IACtE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBrE,kEAAkE;IAC5D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAO/B,uGAAuG;IACvG,OAAO,CAAC,iBAAiB;IAKzB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAWtB,qDAAqD;IACrD,QAAQ,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAW5D;;;OAGG;IACH,OAAO,CAAC,OAAO;CAgBhB"}
@@ -5,15 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DatabasePoolManager = void 0;
7
7
  const pg_1 = __importDefault(require("pg"));
8
- const core_1 = require("@stratum-hq/core");
8
+ const manager_js_1 = require("./manager.js");
9
9
  /**
10
10
  * Manages a collection of per-tenant pg.Pool instances.
11
11
  *
12
- * Pools are keyed by tenant slug and created on first access.
13
- * When the pool count exceeds maxPools, the least-recently-used pool is evicted.
12
+ * Pools are keyed by tenant slug and created on first access. Each getPool call
13
+ * holds the pool until the caller calls releasePool. When the pool count reaches
14
+ * maxPools, the least-recently-used pool that no caller holds is evicted. A pool
15
+ * that a caller holds is never evicted, so the count can exceed maxPools while
16
+ * every pool is in use.
14
17
  */
15
18
  class DatabasePoolManager {
16
19
  pools = new Map();
20
+ /** Holds that callers still have on pools that closePool or closeAll removed, by pool key. */
21
+ closedHolds = new Map();
17
22
  baseConfig;
18
23
  maxPools;
19
24
  idleTimeoutMs;
@@ -23,32 +28,73 @@ class DatabasePoolManager {
23
28
  this.idleTimeoutMs = options.idleTimeoutMs ?? 30_000;
24
29
  }
25
30
  /**
26
- * Returns an existing pool for the tenant slug, or creates a new one.
27
- * Evicts the LRU pool if the pool limit is reached.
31
+ * Returns the pool for the tenant slug, and creates it on first access.
32
+ * Each call holds the pool until the caller calls releasePool with the same
33
+ * arguments. A held pool is never evicted.
28
34
  *
29
35
  * When a regionId is provided, the pool is keyed as `regionId:slug` to support
30
36
  * multi-region deployments where the same slug may exist in different regions.
31
37
  */
32
38
  async getPool(tenantSlug, regionId) {
33
- (0, core_1.validateSlug)(tenantSlug);
39
+ const dbName = (0, manager_js_1.getDatabaseName)(tenantSlug);
34
40
  const poolKey = regionId ? `${regionId}:${tenantSlug}` : tenantSlug;
35
- const existing = this.pools.get(poolKey);
36
- if (existing) {
37
- existing.lastUsed = Date.now();
38
- return existing.pool;
41
+ // The lookup, the eviction choice and the insert run with no await between
42
+ // them. Thus a concurrent call for the same key always finds this entry.
43
+ let entry = this.pools.get(poolKey);
44
+ let victim;
45
+ if (!entry) {
46
+ if (this.pools.size >= this.maxPools)
47
+ victim = this.takeLRU();
48
+ entry = {
49
+ pool: new pg_1.default.Pool({
50
+ ...this.baseConfig,
51
+ database: dbName,
52
+ idleTimeoutMillis: this.idleTimeoutMs,
53
+ }),
54
+ lastUsed: Date.now(),
55
+ refCount: 0,
56
+ };
57
+ this.pools.set(poolKey, entry);
39
58
  }
40
- // Evict before adding so we never exceed maxPools.
41
- if (this.pools.size >= this.maxPools) {
42
- await this.evictLRU();
59
+ entry.lastUsed = Date.now();
60
+ entry.refCount++;
61
+ if (victim) {
62
+ // The evicted pool belongs to a different tenant. Its end error must not
63
+ // fail this request, which already has a usable pool.
64
+ await victim.end().catch(() => undefined);
43
65
  }
44
- const dbName = `stratum_tenant_${tenantSlug}`;
45
- const pool = new pg_1.default.Pool({
46
- ...this.baseConfig,
47
- database: dbName,
48
- idleTimeoutMillis: this.idleTimeoutMs,
49
- });
50
- this.pools.set(poolKey, { pool, lastUsed: Date.now() });
51
- return pool;
66
+ return entry.pool;
67
+ }
68
+ /**
69
+ * Ends one hold on the pool that getPool returned for the same arguments.
70
+ * Call it once for each getPool call. No-op if the pool is not tracked.
71
+ *
72
+ * Without a regionId, a bare slug also matches one region-prefixed pool
73
+ * (`regionId:slug`), as closePool does. If the slug matches pools in more
74
+ * than one region, the call releases nothing, because the manager cannot
75
+ * tell which pool the caller holds. Pass the regionId in that case.
76
+ */
77
+ releasePool(tenantSlug, regionId) {
78
+ const poolKey = this.resolveHeldKey(tenantSlug, regionId);
79
+ if (poolKey === undefined)
80
+ return;
81
+ // The caller passes a key, not the pool, so the manager cannot tell a
82
+ // hold on a closed pool from a hold on its replacement. It ends holds on
83
+ // closed pools first. The replacement then counts as held for longer than
84
+ // it is, which is safe: eviction never ends a pool that is in use.
85
+ const closed = this.closedHolds.get(poolKey);
86
+ if (closed !== undefined) {
87
+ if (closed === 1)
88
+ this.closedHolds.delete(poolKey);
89
+ else
90
+ this.closedHolds.set(poolKey, closed - 1);
91
+ return;
92
+ }
93
+ const entry = this.pools.get(poolKey);
94
+ if (!entry || entry.refCount === 0)
95
+ return;
96
+ entry.refCount--;
97
+ entry.lastUsed = Date.now();
52
98
  }
53
99
  /** Closes and removes the pool for the given tenant slug. No-op if not found.
54
100
  * Accepts either a bare slug or a region-prefixed key (`regionId:slug`). */
@@ -69,14 +115,39 @@ class DatabasePoolManager {
69
115
  if (!entry)
70
116
  return;
71
117
  this.pools.delete(resolvedKey);
118
+ this.recordClosedHolds(resolvedKey, entry);
72
119
  await entry.pool.end();
73
120
  }
74
121
  /** Closes all managed pools. Call during application shutdown. */
75
122
  async closeAll() {
76
123
  const entries = Array.from(this.pools.entries());
77
124
  this.pools.clear();
125
+ for (const [key, entry] of entries)
126
+ this.recordClosedHolds(key, entry);
78
127
  await Promise.all(entries.map(([, entry]) => entry.pool.end()));
79
128
  }
129
+ /** Keeps the holds of a removed pool, so that their releases do not reach a later pool for the key. */
130
+ recordClosedHolds(poolKey, entry) {
131
+ if (entry.refCount === 0)
132
+ return;
133
+ this.closedHolds.set(poolKey, (this.closedHolds.get(poolKey) ?? 0) + entry.refCount);
134
+ }
135
+ /**
136
+ * Returns the key that releasePool acts on: the exact key when the manager
137
+ * tracks it, else the one region-prefixed key for a bare slug. Returns
138
+ * undefined when no key or more than one key matches.
139
+ */
140
+ resolveHeldKey(tenantSlug, regionId) {
141
+ const poolKey = regionId ? `${regionId}:${tenantSlug}` : tenantSlug;
142
+ if (this.pools.has(poolKey) || this.closedHolds.has(poolKey) || regionId)
143
+ return poolKey;
144
+ const matches = new Set();
145
+ for (const key of [...this.pools.keys(), ...this.closedHolds.keys()]) {
146
+ if (key.endsWith(`:${tenantSlug}`))
147
+ matches.add(key);
148
+ }
149
+ return matches.size === 1 ? [...matches][0] : undefined;
150
+ }
80
151
  /** Returns a snapshot of current pool statistics. */
81
152
  getStats() {
82
153
  let activeConnections = 0;
@@ -88,19 +159,24 @@ class DatabasePoolManager {
88
159
  }
89
160
  return { poolCount: this.pools.size, activeConnections };
90
161
  }
91
- /** Evicts the pool that has been idle the longest. */
92
- async evictLRU() {
162
+ /**
163
+ * Removes the least-recently-used pool that no caller holds from the map and
164
+ * returns it. The caller ends it. Returns undefined when every pool is held.
165
+ */
166
+ takeLRU() {
93
167
  let oldestKey;
94
168
  let oldestTime = Infinity;
95
169
  for (const [key, entry] of this.pools.entries()) {
96
- if (entry.lastUsed < oldestTime) {
170
+ if (entry.refCount === 0 && entry.lastUsed < oldestTime) {
97
171
  oldestTime = entry.lastUsed;
98
172
  oldestKey = key;
99
173
  }
100
174
  }
101
- if (oldestKey !== undefined) {
102
- await this.closePool(oldestKey);
103
- }
175
+ if (oldestKey === undefined)
176
+ return undefined;
177
+ const entry = this.pools.get(oldestKey);
178
+ this.pools.delete(oldestKey);
179
+ return entry.pool;
104
180
  }
105
181
  }
106
182
  exports.DatabasePoolManager = DatabasePoolManager;
@@ -1 +1 @@
1
- {"version":3,"file":"pool-manager.js","sourceRoot":"","sources":["../../src/database/pool-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pool-manager.js","sourceRoot":"","sources":["../../src/database/pool-manager.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { BaseAdapter } from "./base-adapter.js";
2
2
  export { RawAdapter, createTenantPool } from "./adapters/raw.js";
3
3
  export { PrismaAdapter, withTenant as prismaWithTenant } from "./adapters/prisma.js";
4
4
  export { SchemaRawAdapter, createSchemaTenantPool, } from "./adapters/schema-raw.js";
5
+ export type { SchemaAdapterOptions } from "./adapters/schema-raw.js";
5
6
  export { SchemaPrismaAdapter, withSchemaTenant, } from "./adapters/schema-prisma.js";
6
7
  export { createPolicy, dropPolicy, enableRLS, disableRLS, isRLSEnabled, } from "./rls/manager.js";
7
8
  export { setTenantContext, resetTenantContext, withTenantContext, withRlsBypass, getCurrentTenantId, } from "./rls/session.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,eAAe,EACf,SAAS,IAAI,qBAAqB,EAClC,qBAAqB,EACrB,YAAY,GACb,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,GACb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,eAAe,IAAI,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxG,OAAO,EACL,cAAc,EACd,UAAU,IAAI,iBAAiB,EAC/B,eAAe,IAAI,sBAAsB,GAC1C,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,eAAe,EACf,SAAS,IAAI,qBAAqB,EAClC,qBAAqB,EACrB,YAAY,GACb,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,GACb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,eAAe,IAAI,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxG,OAAO,EACL,cAAc,EACd,UAAU,IAAI,iBAAiB,EAC/B,eAAe,IAAI,sBAAsB,GAC1C,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAgD;AAAvC,8GAAA,WAAW,OAAA;AAEpB,4CAAiE;AAAxD,oGAAA,UAAU,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AACrC,kDAAqF;AAA5E,0GAAA,aAAa,OAAA;AAAE,6GAAA,UAAU,OAAoB;AACtD,0DAGkC;AAFhC,iHAAA,gBAAgB,OAAA;AAChB,uHAAA,sBAAsB,OAAA;AAExB,gEAGqC;AAFnC,uHAAA,mBAAmB,OAAA;AACnB,oHAAA,gBAAgB,OAAA;AAGlB,+CAM0B;AALxB,0GAAA,YAAY,OAAA;AACZ,wGAAA,UAAU,OAAA;AACV,uGAAA,SAAS,OAAA;AACT,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AAGd,+CAM0B;AALxB,8GAAA,gBAAgB,OAAA;AAChB,gHAAA,kBAAkB,OAAA;AAClB,+GAAA,iBAAiB,OAAA;AACjB,2GAAA,aAAa,OAAA;AACb,gHAAA,kBAAkB,OAAA;AAGpB,kDAO6B;AAN3B,0GAAA,YAAY,OAAA;AACZ,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,+GAAA,iBAAiB,OAAA;AACjB,oHAAA,sBAAsB,OAAA;AACtB,8GAAA,gBAAgB,OAAA;AAGlB,kDAI6B;AAH3B,iHAAA,mBAAmB,OAAA;AACnB,6GAAA,eAAe,OAAA;AACf,kHAAA,oBAAoB,OAAA;AAGtB,+DAKgC;AAJ9B,uHAAA,eAAe,OAAA;AACf,6HAAA,SAAS,OAAyB;AAClC,6HAAA,qBAAqB,OAAA;AACrB,oHAAA,YAAY,OAAA;AAGd,0BAA0B;AAC1B,oDAK+B;AAJ7B,6GAAA,eAAe,OAAA;AACf,4GAAA,cAAc,OAAA;AACd,4GAAA,cAAc,OAAA;AACd,0GAAA,YAAY,OAAA;AAGd,8DAAiE;AAAxD,sHAAA,mBAAmB,OAAA;AAG5B,8DAAgE;AAAvD,qHAAA,kBAAkB,OAAA;AAC3B,oEAAsE;AAA7D,2HAAA,qBAAqB,OAAA;AAC9B,wDAAwG;AAA/F,gHAAA,gBAAgB,OAAA;AAAE,wHAAA,eAAe,OAA4B;AACtE,oDAI+B;AAH7B,4GAAA,cAAc,OAAA;AACd,+GAAA,UAAU,OAAqB;AAC/B,oHAAA,eAAe,OAA0B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAgD;AAAvC,8GAAA,WAAW,OAAA;AAEpB,4CAAiE;AAAxD,oGAAA,UAAU,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AACrC,kDAAqF;AAA5E,0GAAA,aAAa,OAAA;AAAE,6GAAA,UAAU,OAAoB;AACtD,0DAGkC;AAFhC,iHAAA,gBAAgB,OAAA;AAChB,uHAAA,sBAAsB,OAAA;AAGxB,gEAGqC;AAFnC,uHAAA,mBAAmB,OAAA;AACnB,oHAAA,gBAAgB,OAAA;AAGlB,+CAM0B;AALxB,0GAAA,YAAY,OAAA;AACZ,wGAAA,UAAU,OAAA;AACV,uGAAA,SAAS,OAAA;AACT,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AAGd,+CAM0B;AALxB,8GAAA,gBAAgB,OAAA;AAChB,gHAAA,kBAAkB,OAAA;AAClB,+GAAA,iBAAiB,OAAA;AACjB,2GAAA,aAAa,OAAA;AACb,gHAAA,kBAAkB,OAAA;AAGpB,kDAO6B;AAN3B,0GAAA,YAAY,OAAA;AACZ,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,+GAAA,iBAAiB,OAAA;AACjB,oHAAA,sBAAsB,OAAA;AACtB,8GAAA,gBAAgB,OAAA;AAGlB,kDAI6B;AAH3B,iHAAA,mBAAmB,OAAA;AACnB,6GAAA,eAAe,OAAA;AACf,kHAAA,oBAAoB,OAAA;AAGtB,+DAKgC;AAJ9B,uHAAA,eAAe,OAAA;AACf,6HAAA,SAAS,OAAyB;AAClC,6HAAA,qBAAqB,OAAA;AACrB,oHAAA,YAAY,OAAA;AAGd,0BAA0B;AAC1B,oDAK+B;AAJ7B,6GAAA,eAAe,OAAA;AACf,4GAAA,cAAc,OAAA;AACd,4GAAA,cAAc,OAAA;AACd,0GAAA,YAAY,OAAA;AAGd,8DAAiE;AAAxD,sHAAA,mBAAmB,OAAA;AAG5B,8DAAgE;AAAvD,qHAAA,kBAAkB,OAAA;AAC3B,oEAAsE;AAA7D,2HAAA,qBAAqB,OAAA;AAC9B,wDAAwG;AAA/F,gHAAA,gBAAgB,OAAA;AAAE,wHAAA,eAAe,OAA4B;AACtE,oDAI+B;AAH7B,4GAAA,cAAc,OAAA;AACd,+GAAA,UAAU,OAAqB;AAC/B,oHAAA,eAAe,OAA0B"}
@@ -23,7 +23,7 @@ async function enableRLS(client, tableName) {
23
23
  async function createIsolationPolicy(client, tableName) {
24
24
  const safe = validateTableName(tableName);
25
25
  await client.query(`CREATE POLICY tenant_isolation ON ${safe}
26
- USING (tenant_id = current_setting('app.current_tenant_id')::uuid)`);
26
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid)`);
27
27
  }
28
28
  async function migrateTable(client, tableName) {
29
29
  await addTenantColumn(client, tableName);
@@ -1 +1 @@
1
- {"version":3,"file":"migration-helpers.js","sourceRoot":"","sources":["../src/migration-helpers.ts"],"names":[],"mappings":";;AAUA,0CAQC;AAED,8BAOC;AAED,sDASC;AAED,oCAOC;AA7CD,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAEM,KAAK,UAAU,eAAe,CACnC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAChB,eAAe,IAAI,qCAAqC,CACzD,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,SAAS,CAC7B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,4BAA4B,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,2BAA2B,CAAC,CAAC;AACrE,CAAC;AAEM,KAAK,UAAU,qBAAqB,CACzC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAChB,qCAAqC,IAAI;wEAC2B,CACrE,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzC,MAAM,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnC,MAAM,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"migration-helpers.js","sourceRoot":"","sources":["../src/migration-helpers.ts"],"names":[],"mappings":";;AAUA,0CAQC;AAED,8BAOC;AAED,sDASC;AAED,oCAOC;AA7CD,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAEM,KAAK,UAAU,eAAe,CACnC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAChB,eAAe,IAAI,qCAAqC,CACzD,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,SAAS,CAC7B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,4BAA4B,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,2BAA2B,CAAC,CAAC;AACrE,CAAC;AAEM,KAAK,UAAU,qBAAqB,CACzC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAChB,qCAAqC,IAAI;0FAC6C,CACvF,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzC,MAAM,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnC,MAAM,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC"}
@@ -18,7 +18,7 @@ async function createPolicy(client, tableName) {
18
18
  // Table name is validated via allowlist regex above.
19
19
  const exists = await client.query(`SELECT count(*) FROM pg_policies WHERE tablename = $1 AND policyname = 'tenant_isolation'`, [safe]);
20
20
  if (parseInt(exists.rows[0].count, 10) === 0) {
21
- await client.query(`CREATE POLICY tenant_isolation ON ${safe} USING (tenant_id = current_setting('app.current_tenant_id')::uuid)`);
21
+ await client.query(`CREATE POLICY tenant_isolation ON ${safe} USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid)`);
22
22
  }
23
23
  }
24
24
  async function dropPolicy(client, tableName) {
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/rls/manager.ts"],"names":[],"mappings":";;AAUA,oCAgBC;AAED,gCAMC;AAED,8BAOC;AAED,gCAOC;AAED,oCAYC;AAhED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,qDAAqD;IACrD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B,2FAA2F,EAC3F,CAAC,IAAI,CAAC,CACP,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,KAAK,CAChB,qCAAqC,IAAI,qEAAqE,CAC/G,CAAC;IACJ,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC;AAEM,KAAK,UAAU,SAAS,CAC7B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,4BAA4B,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,2BAA2B,CAAC,CAAC;AACrE,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,8BAA8B,CAAC,CAAC;IACtE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,6BAA6B,CAAC,CAAC;AACvE,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,wDAAwD,EACxD,CAAC,SAAS,CAAC,CACZ,CAAC;IACF,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/rls/manager.ts"],"names":[],"mappings":";;AAUA,oCAgBC;AAED,gCAMC;AAED,8BAOC;AAED,gCAOC;AAED,oCAYC;AAhED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,qDAAqD;IACrD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B,2FAA2F,EAC3F,CAAC,IAAI,CAAC,CACP,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,KAAK,CAChB,qCAAqC,IAAI,uFAAuF,CACjI,CAAC;IACJ,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC;AAEM,KAAK,UAAU,SAAS,CAC7B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,4BAA4B,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,2BAA2B,CAAC,CAAC;AACrE,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,8BAA8B,CAAC,CAAC;IACtE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,6BAA6B,CAAC,CAAC;AACvE,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,wDAAwD,EACxD,CAAC,SAAS,CAAC,CACZ,CAAC;IACF,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;AACpC,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import pg from "pg";
2
+ export declare function validateSchemaName(schemaName: string): string;
2
3
  export declare function tenantSchemaName(tenantSlug: string): string;
3
4
  export declare function createSchema(client: pg.PoolClient, tenantSlug: string): Promise<void>;
4
5
  export declare function dropSchema(client: pg.PoolClient, tenantSlug: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAkBpB,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAMf"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpB,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK7D;AAaD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAMf"}
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateSchemaName = validateSchemaName;
3
4
  exports.tenantSchemaName = tenantSchemaName;
4
5
  exports.createSchema = createSchema;
5
6
  exports.dropSchema = dropSchema;
@@ -20,12 +21,20 @@ function validateTableName(tableName) {
20
21
  }
21
22
  return tableName;
22
23
  }
24
+ // PostgreSQL silently truncates identifiers longer than this many bytes.
25
+ const MAX_IDENTIFIER_BYTES = 63;
23
26
  function tenantSchemaName(tenantSlug) {
24
- return `tenant_${tenantSlug}`;
27
+ const schemaName = `tenant_${tenantSlug}`;
28
+ if (Buffer.byteLength(schemaName) > MAX_IDENTIFIER_BYTES) {
29
+ throw new Error(`Schema name for tenant slug "${tenantSlug}" exceeds ${MAX_IDENTIFIER_BYTES} bytes`);
30
+ }
31
+ return schemaName;
25
32
  }
26
33
  async function createSchema(client, tenantSlug) {
27
34
  const schemaName = validateSchemaName(tenantSchemaName(tenantSlug));
28
- await client.query(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);
35
+ // No IF NOT EXISTS: an existing schema may hold another tenant's data, so
36
+ // provisioning must fail rather than adopt it.
37
+ await client.query(`CREATE SCHEMA ${schemaName}`);
29
38
  }
30
39
  async function dropSchema(client, tenantSlug) {
31
40
  const schemaName = validateSchemaName(tenantSchemaName(tenantSlug));
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":";;AAkBA,4CAEC;AAED,oCAMC;AAED,gCAMC;AAED,oCAUC;AAED,8CAOC;AAED,wDAUC;AAnED,yFAAyF;AACzF,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,OAAO,UAAU,UAAU,EAAE,CAAC;AAChC,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAC;AAClE,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,yBAAyB,UAAU,UAAU,CAAC,CAAC;AACpE,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,sDAAsD,EACtD,CAAC,UAAU,CAAC,CACb,CAAC;IACF,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC;AAEM,KAAK,UAAU,iBAAiB,CACrC,MAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,iFAAiF,CAClF,CAAC;IACF,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC;AAEM,KAAK,UAAU,sBAAsB,CAC1C,MAAqB,EACrB,SAAiB,EACjB,UAAkB;IAElB,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAClD,MAAM,MAAM,CAAC,KAAK,CAChB,8BAA8B,UAAU,IAAI,SAAS,iBAAiB,SAAS,iBAAiB,CACjG,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":";;AAGA,gDAKC;AAaD,4CAQC;AAED,oCAQC;AAED,gCAMC;AAED,oCAUC;AAED,8CAOC;AAED,wDAUC;AA9ED,yFAAyF;AACzF,SAAgB,kBAAkB,CAAC,UAAkB;IACnD,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,yEAAyE;AACzE,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,MAAM,UAAU,GAAG,UAAU,UAAU,EAAE,CAAC;IAC1C,IAAI,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,oBAAoB,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,gCAAgC,UAAU,aAAa,oBAAoB,QAAQ,CACpF,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,0EAA0E;IAC1E,+CAA+C;IAC/C,MAAM,MAAM,CAAC,KAAK,CAAC,iBAAiB,UAAU,EAAE,CAAC,CAAC;AACpD,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,yBAAyB,UAAU,UAAU,CAAC,CAAC;AACpE,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,sDAAsD,EACtD,CAAC,UAAU,CAAC,CACb,CAAC;IACF,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC;AAEM,KAAK,UAAU,iBAAiB,CACrC,MAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,iFAAiF,CAClF,CAAC;IACF,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC;AAEM,KAAK,UAAU,sBAAsB,CAC1C,MAAqB,EACrB,SAAiB,EACjB,UAAkB;IAElB,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAClD,MAAM,MAAM,CAAC,KAAK,CAChB,8BAA8B,UAAU,IAAI,SAAS,iBAAiB,SAAS,iBAAiB,CACjG,CAAC;AACJ,CAAC"}
@@ -1,5 +1,14 @@
1
1
  import pg from "pg";
2
- export declare function setSchemaSearchPath(client: pg.PoolClient, tenantSlug: string): Promise<void>;
2
+ /**
3
+ * Builds the search_path list for a tenant: the tenant schema first, then any
4
+ * explicitly opted-in extra schemas (for example one holding extension
5
+ * functions). Every extra entry is validated as a plain identifier, the same
6
+ * way tenant schema names are. Note that an unqualified table missing from the
7
+ * tenant schema resolves in the extra schemas, so list only schemas that hold
8
+ * no tenant data.
9
+ */
10
+ export declare function tenantSearchPath(schemaName: string, extraSearchPath?: string[]): string;
11
+ export declare function setSchemaSearchPath(client: pg.PoolClient, tenantSlug: string, extraSearchPath?: string[]): Promise<void>;
3
12
  export declare function resetSearchPath(client: pg.PoolClient): Promise<void>;
4
13
  export declare function getCurrentSearchPath(client: pg.PoolClient): Promise<string>;
5
14
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/schema/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAGjB"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/schema/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,GAAE,MAAM,EAAO,GAAG,MAAM,CAE3F;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,EAClB,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAGjB"}
@@ -1,17 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tenantSearchPath = tenantSearchPath;
3
4
  exports.setSchemaSearchPath = setSchemaSearchPath;
4
5
  exports.resetSearchPath = resetSearchPath;
5
6
  exports.getCurrentSearchPath = getCurrentSearchPath;
6
7
  const core_1 = require("@stratum-hq/core");
7
8
  const manager_js_1 = require("./manager.js");
8
- async function setSchemaSearchPath(client, tenantSlug) {
9
+ /**
10
+ * Builds the search_path list for a tenant: the tenant schema first, then any
11
+ * explicitly opted-in extra schemas (for example one holding extension
12
+ * functions). Every extra entry is validated as a plain identifier, the same
13
+ * way tenant schema names are. Note that an unqualified table missing from the
14
+ * tenant schema resolves in the extra schemas, so list only schemas that hold
15
+ * no tenant data.
16
+ */
17
+ function tenantSearchPath(schemaName, extraSearchPath = []) {
18
+ return [schemaName, ...extraSearchPath.map(manager_js_1.validateSchemaName)].join(", ");
19
+ }
20
+ async function setSchemaSearchPath(client, tenantSlug, extraSearchPath = []) {
9
21
  // The schema name is interpolated (identifiers cannot be bound), so the slug
10
22
  // must be validated against the canonical slug charset first; validateSlug
11
23
  // throws on anything else. SET LOCAL only takes effect for the current
12
- // transaction.
13
- const schemaName = (0, manager_js_1.tenantSchemaName)((0, core_1.validateSlug)(tenantSlug));
14
- await client.query(`SET LOCAL search_path TO ${schemaName}, public`);
24
+ // transaction. The path is the tenant schema alone (pg_catalog is always
25
+ // searched), so a table missing from it errors instead of resolving to a
26
+ // shared table in public. Extra schemas are appended only when opted in.
27
+ const searchPath = tenantSearchPath((0, manager_js_1.tenantSchemaName)((0, core_1.validateSlug)(tenantSlug)), extraSearchPath);
28
+ await client.query(`SET LOCAL search_path TO ${searchPath}`);
29
+ // Outside a transaction SET LOCAL is a no-op with only a warning. SHOW
30
+ // reports unquoted identifiers folded to lower case.
31
+ const res = await client.query("SHOW search_path");
32
+ if (res.rows[0].search_path !== searchPath.toLowerCase()) {
33
+ throw new Error("setSchemaSearchPath must be called inside a transaction");
34
+ }
15
35
  }
16
36
  async function resetSearchPath(client) {
17
37
  await client.query("RESET search_path");