@stratum-hq/db-adapters 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/base-adapter.test.d.ts +2 -0
- package/dist/__tests__/base-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/base-adapter.test.js +194 -0
- package/dist/__tests__/base-adapter.test.js.map +1 -0
- package/dist/__tests__/database-manager.test.d.ts +2 -0
- package/dist/__tests__/database-manager.test.d.ts.map +1 -0
- package/dist/__tests__/database-manager.test.js +110 -0
- package/dist/__tests__/database-manager.test.js.map +1 -0
- package/dist/__tests__/pool-manager.test.d.ts +2 -0
- package/dist/__tests__/pool-manager.test.d.ts.map +1 -0
- package/dist/__tests__/pool-manager.test.js +174 -0
- package/dist/__tests__/pool-manager.test.js.map +1 -0
- package/dist/__tests__/rls-manager.test.d.ts +2 -0
- package/dist/__tests__/rls-manager.test.d.ts.map +1 -0
- package/dist/__tests__/rls-manager.test.js +180 -0
- package/dist/__tests__/rls-manager.test.js.map +1 -0
- package/dist/__tests__/rls-session.test.d.ts +2 -0
- package/dist/__tests__/rls-session.test.d.ts.map +1 -0
- package/dist/__tests__/rls-session.test.js +102 -0
- package/dist/__tests__/rls-session.test.js.map +1 -0
- package/dist/__tests__/schema-manager.test.d.ts +2 -0
- package/dist/__tests__/schema-manager.test.d.ts.map +1 -0
- package/dist/__tests__/schema-manager.test.js +116 -0
- package/dist/__tests__/schema-manager.test.js.map +1 -0
- package/dist/adapters/database-prisma.d.ts +50 -0
- package/dist/adapters/database-prisma.d.ts.map +1 -0
- package/dist/adapters/database-prisma.js +66 -0
- package/dist/adapters/database-prisma.js.map +1 -0
- package/dist/adapters/database-raw.d.ts +23 -0
- package/dist/adapters/database-raw.d.ts.map +1 -0
- package/dist/adapters/database-raw.js +52 -0
- package/dist/adapters/database-raw.js.map +1 -0
- package/dist/adapters/prisma.d.ts +14 -0
- package/dist/adapters/prisma.d.ts.map +1 -0
- package/dist/adapters/prisma.js +29 -0
- package/dist/adapters/prisma.js.map +1 -0
- package/dist/adapters/raw.d.ts +8 -0
- package/dist/adapters/raw.d.ts.map +1 -0
- package/dist/adapters/raw.js +26 -0
- package/dist/adapters/raw.js.map +1 -0
- package/dist/adapters/schema-prisma.d.ts +11 -0
- package/dist/adapters/schema-prisma.d.ts.map +1 -0
- package/dist/adapters/schema-prisma.js +35 -0
- package/dist/adapters/schema-prisma.js.map +1 -0
- package/dist/adapters/schema-raw.d.ts +13 -0
- package/dist/adapters/schema-raw.d.ts.map +1 -0
- package/dist/adapters/schema-raw.js +63 -0
- package/dist/adapters/schema-raw.js.map +1 -0
- package/dist/base-adapter.d.ts +7 -0
- package/dist/base-adapter.d.ts.map +1 -0
- package/dist/base-adapter.js +32 -0
- package/dist/base-adapter.js.map +1 -0
- package/dist/database/manager.d.ts +27 -0
- package/dist/database/manager.d.ts.map +1 -0
- package/dist/database/manager.js +68 -0
- package/dist/database/manager.js.map +1 -0
- package/dist/database/pool-manager.d.ts +44 -0
- package/dist/database/pool-manager.d.ts.map +1 -0
- package/dist/database/pool-manager.js +102 -0
- package/dist/database/pool-manager.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/migration-helpers.d.ts +6 -0
- package/dist/migration-helpers.d.ts.map +1 -0
- package/dist/migration-helpers.js +34 -0
- package/dist/migration-helpers.js.map +1 -0
- package/dist/rls/manager.d.ts +7 -0
- package/dist/rls/manager.d.ts.map +1 -0
- package/dist/rls/manager.js +46 -0
- package/dist/rls/manager.js.map +1 -0
- package/dist/rls/session.d.ts +5 -0
- package/dist/rls/session.d.ts.map +1 -0
- package/dist/rls/session.js +18 -0
- package/dist/rls/session.js.map +1 -0
- package/dist/schema/manager.d.ts +8 -0
- package/dist/schema/manager.d.ts.map +1 -0
- package/dist/schema/manager.js +49 -0
- package/dist/schema/manager.js.map +1 -0
- package/dist/schema/session.d.ts +5 -0
- package/dist/schema/session.d.ts.map +1 -0
- package/dist/schema/session.js +21 -0
- package/dist/schema/session.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DatabaseRawAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Raw SQL adapter for DB_PER_TENANT isolation.
|
|
6
|
+
*
|
|
7
|
+
* Each query is routed to the dedicated database for the given tenant slug
|
|
8
|
+
* by obtaining a connection from the tenant's pool via DatabasePoolManager.
|
|
9
|
+
*/
|
|
10
|
+
class DatabaseRawAdapter {
|
|
11
|
+
poolManager;
|
|
12
|
+
constructor(poolManager) {
|
|
13
|
+
this.poolManager = poolManager;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Executes a single query against the tenant's dedicated database.
|
|
17
|
+
* The connection is acquired and released automatically.
|
|
18
|
+
*/
|
|
19
|
+
async query(tenantSlug, text, values) {
|
|
20
|
+
const pool = this.poolManager.getPool(tenantSlug);
|
|
21
|
+
const client = await pool.connect();
|
|
22
|
+
try {
|
|
23
|
+
return await client.query(text, values);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
client.release();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Executes a callback inside an explicit transaction against the tenant's dedicated database.
|
|
31
|
+
* The transaction is automatically committed on success and rolled back on error.
|
|
32
|
+
*/
|
|
33
|
+
async executeWithTenantContext(tenantSlug, queryFn) {
|
|
34
|
+
const pool = this.poolManager.getPool(tenantSlug);
|
|
35
|
+
const client = await pool.connect();
|
|
36
|
+
try {
|
|
37
|
+
await client.query("BEGIN");
|
|
38
|
+
const result = await queryFn(client);
|
|
39
|
+
await client.query("COMMIT");
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
await client.query("ROLLBACK");
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
client.release();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.DatabaseRawAdapter = DatabaseRawAdapter;
|
|
52
|
+
//# sourceMappingURL=database-raw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-raw.js","sourceRoot":"","sources":["../../src/adapters/database-raw.ts"],"names":[],"mappings":";;;AAGA;;;;;GAKG;AACH,MAAa,kBAAkB;IACA;IAA7B,YAA6B,WAAgC;QAAhC,gBAAW,GAAX,WAAW,CAAqB;IAAG,CAAC;IAEjE;;;OAGG;IACH,KAAK,CAAC,KAAK,CACT,UAAkB,EAClB,IAAY,EACZ,MAAkB;QAElB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,MAAM,CAAC,KAAK,CAAI,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,wBAAwB,CAC5B,UAAkB,EAClB,OAA8C;QAE9C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;YACrC,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,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;CACF;AA3CD,gDA2CC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseAdapter } from "../base-adapter.js";
|
|
2
|
+
import pg from "pg";
|
|
3
|
+
interface PrismaClientLike {
|
|
4
|
+
$extends: (extension: unknown) => PrismaClientLike;
|
|
5
|
+
$executeRaw: (query: TemplateStringsArray, ...values: unknown[]) => Promise<number>;
|
|
6
|
+
$transaction: <T>(fn: (tx: PrismaClientLike) => Promise<T>) => Promise<T>;
|
|
7
|
+
}
|
|
8
|
+
export declare class PrismaAdapter extends BaseAdapter {
|
|
9
|
+
constructor(pool: pg.Pool);
|
|
10
|
+
withTenant(prisma: PrismaClientLike, contextFn: () => string): PrismaClientLike;
|
|
11
|
+
}
|
|
12
|
+
export declare function withTenant(prisma: PrismaClientLike, contextFn: () => string, pool: pg.Pool): PrismaClientLike;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=prisma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,UAAU,gBAAgB;IACxB,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,gBAAgB,CAAC;IACnD,WAAW,EAAE,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpF,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3E;AAED,qBAAa,aAAc,SAAQ,WAAW;gBAChC,IAAI,EAAE,EAAE,CAAC,IAAI;IAIzB,UAAU,CAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,MAAM,GAAG,gBAAgB;CAahF;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,MAAM,EACvB,IAAI,EAAE,EAAE,CAAC,IAAI,GACZ,gBAAgB,CAGlB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withTenant = exports.PrismaAdapter = void 0;
|
|
4
|
+
const base_adapter_js_1 = require("../base-adapter.js");
|
|
5
|
+
class PrismaAdapter extends base_adapter_js_1.BaseAdapter {
|
|
6
|
+
constructor(pool) {
|
|
7
|
+
super(pool);
|
|
8
|
+
}
|
|
9
|
+
withTenant(prisma, contextFn) {
|
|
10
|
+
return prisma.$extends({
|
|
11
|
+
query: {
|
|
12
|
+
$allOperations({ args, query }) {
|
|
13
|
+
const tenantId = contextFn();
|
|
14
|
+
return prisma.$transaction(async (tx) => {
|
|
15
|
+
await tx.$executeRaw `SELECT set_config('app.current_tenant_id', ${tenantId}, true)`;
|
|
16
|
+
return query(args);
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.PrismaAdapter = PrismaAdapter;
|
|
24
|
+
function withTenant(prisma, contextFn, pool) {
|
|
25
|
+
const adapter = new PrismaAdapter(pool);
|
|
26
|
+
return adapter.withTenant(prisma, contextFn);
|
|
27
|
+
}
|
|
28
|
+
exports.withTenant = withTenant;
|
|
29
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../../src/adapters/prisma.ts"],"names":[],"mappings":";;;AAAA,wDAAiD;AAWjD,MAAa,aAAc,SAAQ,6BAAW;IAC5C,YAAY,IAAa;QACvB,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAED,UAAU,CAAC,MAAwB,EAAE,SAAuB;QAC1D,OAAO,MAAM,CAAC,QAAQ,CAAC;YACrB,KAAK,EAAE;gBACL,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,EAAiE;oBAC3F,MAAM,QAAQ,GAAG,SAAS,EAAE,CAAC;oBAC7B,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAoB,EAAE,EAAE;wBACxD,MAAM,EAAE,CAAC,WAAW,CAAA,8CAA8C,QAAQ,SAAS,CAAC;wBACpF,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;oBACrB,CAAC,CAAC,CAAC;gBACL,CAAC;aACF;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAlBD,sCAkBC;AAED,SAAgB,UAAU,CACxB,MAAwB,EACxB,SAAuB,EACvB,IAAa;IAEb,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC;AAPD,gCAOC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
import { BaseAdapter } from "../base-adapter.js";
|
|
3
|
+
export declare class RawAdapter extends BaseAdapter {
|
|
4
|
+
constructor(pool: pg.Pool);
|
|
5
|
+
query<T extends pg.QueryResultRow = pg.QueryResultRow>(tenantId: string, text: string, values?: unknown[]): Promise<pg.QueryResult<T>>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createTenantPool(pool: pg.Pool, contextFn: () => string): RawAdapter;
|
|
8
|
+
//# sourceMappingURL=raw.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/adapters/raw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,qBAAa,UAAW,SAAQ,WAAW;gBAC7B,IAAI,EAAE,EAAE,CAAC,IAAI;IAInB,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,cAAc,EACzD,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;CAK9B;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,SAAS,EAAE,MAAM,MAAM,GACtB,UAAU,CAWZ"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTenantPool = exports.RawAdapter = void 0;
|
|
4
|
+
const base_adapter_js_1 = require("../base-adapter.js");
|
|
5
|
+
class RawAdapter extends base_adapter_js_1.BaseAdapter {
|
|
6
|
+
constructor(pool) {
|
|
7
|
+
super(pool);
|
|
8
|
+
}
|
|
9
|
+
async query(tenantId, text, values) {
|
|
10
|
+
return this.executeWithTenantContext(tenantId, (client) => client.query(text, values));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.RawAdapter = RawAdapter;
|
|
14
|
+
function createTenantPool(pool, contextFn) {
|
|
15
|
+
const adapter = new RawAdapter(pool);
|
|
16
|
+
return new Proxy(adapter, {
|
|
17
|
+
get(target, prop) {
|
|
18
|
+
if (prop === "query") {
|
|
19
|
+
return (text, values) => target.query(contextFn(), text, values);
|
|
20
|
+
}
|
|
21
|
+
return target[prop];
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
exports.createTenantPool = createTenantPool;
|
|
26
|
+
//# sourceMappingURL=raw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw.js","sourceRoot":"","sources":["../../src/adapters/raw.ts"],"names":[],"mappings":";;;AACA,wDAAiD;AAEjD,MAAa,UAAW,SAAQ,6BAAW;IACzC,YAAY,IAAa;QACvB,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAED,KAAK,CAAC,KAAK,CACT,QAAgB,EAChB,IAAY,EACZ,MAAkB;QAElB,OAAO,IAAI,CAAC,wBAAwB,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CACxD,MAAM,CAAC,KAAK,CAAI,IAAI,EAAE,MAAM,CAAC,CAC9B,CAAC;IACJ,CAAC;CACF;AAdD,gCAcC;AAED,SAAgB,gBAAgB,CAC9B,IAAa,EACb,SAAuB;IAEvB,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACrC,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,CAAe,CAAC;AACnB,CAAC;AAdD,4CAcC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface PrismaClientLike {
|
|
2
|
+
$extends: (extension: unknown) => PrismaClientLike;
|
|
3
|
+
$executeRawUnsafe: (query: string, ...values: unknown[]) => Promise<number>;
|
|
4
|
+
$transaction: <T>(fn: (tx: PrismaClientLike) => Promise<T>) => Promise<T>;
|
|
5
|
+
}
|
|
6
|
+
export declare class SchemaPrismaAdapter {
|
|
7
|
+
withTenant(prisma: PrismaClientLike, contextFn: () => string): PrismaClientLike;
|
|
8
|
+
}
|
|
9
|
+
export declare function withSchemaTenant(prisma: PrismaClientLike, contextFn: () => string): PrismaClientLike;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=schema-prisma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-prisma.d.ts","sourceRoot":"","sources":["../../src/adapters/schema-prisma.ts"],"names":[],"mappings":"AAIA,UAAU,gBAAgB;IACxB,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,gBAAgB,CAAC;IACnD,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5E,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3E;AAUD,qBAAa,mBAAmB;IAC9B,UAAU,CACR,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,MAAM,GACtB,gBAAgB;CAuBpB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,MAAM,GACtB,gBAAgB,CAGlB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withSchemaTenant = exports.SchemaPrismaAdapter = void 0;
|
|
4
|
+
const manager_js_1 = require("../schema/manager.js");
|
|
5
|
+
// Validate slug to prevent SQL injection — slugs follow /^[a-z][a-z0-9_]{0,62}$/
|
|
6
|
+
function validateSlug(slug) {
|
|
7
|
+
if (!/^[a-z][a-z0-9_]{0,62}$/.test(slug)) {
|
|
8
|
+
throw new Error(`Invalid tenant slug: ${slug}`);
|
|
9
|
+
}
|
|
10
|
+
return slug;
|
|
11
|
+
}
|
|
12
|
+
class SchemaPrismaAdapter {
|
|
13
|
+
withTenant(prisma, contextFn) {
|
|
14
|
+
return prisma.$extends({
|
|
15
|
+
query: {
|
|
16
|
+
$allOperations({ args, query, }) {
|
|
17
|
+
const tenantSlug = validateSlug(contextFn());
|
|
18
|
+
const schemaName = (0, manager_js_1.tenantSchemaName)(tenantSlug);
|
|
19
|
+
return prisma.$transaction(async (tx) => {
|
|
20
|
+
// schemaName is derived from a validated slug — safe for interpolation.
|
|
21
|
+
await tx.$executeRawUnsafe(`SET LOCAL search_path TO ${schemaName}, public`);
|
|
22
|
+
return query(args);
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.SchemaPrismaAdapter = SchemaPrismaAdapter;
|
|
30
|
+
function withSchemaTenant(prisma, contextFn) {
|
|
31
|
+
const adapter = new SchemaPrismaAdapter();
|
|
32
|
+
return adapter.withTenant(prisma, contextFn);
|
|
33
|
+
}
|
|
34
|
+
exports.withSchemaTenant = withSchemaTenant;
|
|
35
|
+
//# sourceMappingURL=schema-prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-prisma.js","sourceRoot":"","sources":["../../src/adapters/schema-prisma.ts"],"names":[],"mappings":";;;AAAA,qDAAwD;AAUxD,iFAAiF;AACjF,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAa,mBAAmB;IAC9B,UAAU,CACR,MAAwB,EACxB,SAAuB;QAEvB,OAAO,MAAM,CAAC,QAAQ,CAAC;YACrB,KAAK,EAAE;gBACL,cAAc,CAAC,EACb,IAAI,EACJ,KAAK,GAIN;oBACC,MAAM,UAAU,GAAG,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC;oBAC7C,MAAM,UAAU,GAAG,IAAA,6BAAgB,EAAC,UAAU,CAAC,CAAC;oBAChD,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAoB,EAAE,EAAE;wBACxD,wEAAwE;wBACxE,MAAM,EAAE,CAAC,iBAAiB,CACxB,4BAA4B,UAAU,UAAU,CACjD,CAAC;wBACF,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;oBACrB,CAAC,CAAC,CAAC;gBACL,CAAC;aACF;SACF,CAAC,CAAC;IACL,CAAC;CACF;AA3BD,kDA2BC;AAED,SAAgB,gBAAgB,CAC9B,MAAwB,EACxB,SAAuB;IAEvB,MAAM,OAAO,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAC1C,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC;AAND,4CAMC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
export declare class SchemaRawAdapter {
|
|
3
|
+
private pool;
|
|
4
|
+
constructor(pool: pg.Pool);
|
|
5
|
+
query<T extends pg.QueryResultRow = pg.QueryResultRow>(tenantSlug: string, text: string, values?: unknown[]): Promise<pg.QueryResult<T>>;
|
|
6
|
+
/**
|
|
7
|
+
* Executes a callback within a transaction scoped to the tenant's schema.
|
|
8
|
+
* Sets `search_path` to `tenant_{slug}, public` for the duration of the transaction.
|
|
9
|
+
*/
|
|
10
|
+
executeWithTenantContext<T>(tenantSlug: string, fn: (client: pg.PoolClient) => Promise<T>): Promise<T>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createSchemaTenantPool(pool: pg.Pool, contextFn: () => string): SchemaRawAdapter;
|
|
13
|
+
//# sourceMappingURL=schema-raw.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-raw.d.ts","sourceRoot":"","sources":["../../src/adapters/schema-raw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAapB,qBAAa,gBAAgB;IACf,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,EAAE,CAAC,IAAI;IAE3B,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,cAAc,EACzD,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAM7B;;;OAGG;IACG,wBAAwB,CAAC,CAAC,EAC9B,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,CAAC,CAAC;CAmBd;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,SAAS,EAAE,MAAM,MAAM,GACtB,gBAAgB,CAWlB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSchemaTenantPool = exports.SchemaRawAdapter = void 0;
|
|
4
|
+
const manager_js_1 = require("../schema/manager.js");
|
|
5
|
+
const session_js_1 = require("../schema/session.js");
|
|
6
|
+
// Validate slug to prevent SQL injection — slugs follow /^[a-z][a-z0-9_]{0,62}$/
|
|
7
|
+
// so tenant_{slug} is always safe, but we verify the prefix is not tampered.
|
|
8
|
+
function validateSlug(slug) {
|
|
9
|
+
if (!/^[a-z][a-z0-9_]{0,62}$/.test(slug)) {
|
|
10
|
+
throw new Error(`Invalid tenant slug: ${slug}`);
|
|
11
|
+
}
|
|
12
|
+
return slug;
|
|
13
|
+
}
|
|
14
|
+
class SchemaRawAdapter {
|
|
15
|
+
pool;
|
|
16
|
+
constructor(pool) {
|
|
17
|
+
this.pool = pool;
|
|
18
|
+
}
|
|
19
|
+
async query(tenantSlug, text, values) {
|
|
20
|
+
return this.executeWithTenantContext(tenantSlug, async (client) => {
|
|
21
|
+
return client.query(text, values);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Executes a callback within a transaction scoped to the tenant's schema.
|
|
26
|
+
* Sets `search_path` to `tenant_{slug}, public` for the duration of the transaction.
|
|
27
|
+
*/
|
|
28
|
+
async executeWithTenantContext(tenantSlug, fn) {
|
|
29
|
+
const safe = validateSlug(tenantSlug);
|
|
30
|
+
const schemaName = (0, manager_js_1.tenantSchemaName)(safe);
|
|
31
|
+
const client = await this.pool.connect();
|
|
32
|
+
try {
|
|
33
|
+
await client.query("BEGIN");
|
|
34
|
+
// SET LOCAL is transaction-scoped; schemaName is derived from a validated slug.
|
|
35
|
+
await client.query(`SET LOCAL search_path TO ${schemaName}, public`);
|
|
36
|
+
const result = await fn(client);
|
|
37
|
+
await client.query("COMMIT");
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
await client.query("ROLLBACK");
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await (0, session_js_1.resetSearchPath)(client);
|
|
46
|
+
client.release();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.SchemaRawAdapter = SchemaRawAdapter;
|
|
51
|
+
function createSchemaTenantPool(pool, contextFn) {
|
|
52
|
+
const adapter = new SchemaRawAdapter(pool);
|
|
53
|
+
return new Proxy(adapter, {
|
|
54
|
+
get(target, prop) {
|
|
55
|
+
if (prop === "query") {
|
|
56
|
+
return (text, values) => target.query(contextFn(), text, values);
|
|
57
|
+
}
|
|
58
|
+
return target[prop];
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
exports.createSchemaTenantPool = createSchemaTenantPool;
|
|
63
|
+
//# sourceMappingURL=schema-raw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-raw.js","sourceRoot":"","sources":["../../src/adapters/schema-raw.ts"],"names":[],"mappings":";;;AACA,qDAAwD;AACxD,qDAAuD;AAEvD,iFAAiF;AACjF,6EAA6E;AAC7E,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,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,YAAY,CAAC,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;AAdD,wDAcC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
export declare abstract class BaseAdapter {
|
|
3
|
+
protected pool: pg.Pool;
|
|
4
|
+
constructor(pool: pg.Pool);
|
|
5
|
+
executeWithTenantContext<T>(tenantId: string, queryFn: (client: pg.PoolClient) => Promise<T>): Promise<T>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=base-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../src/base-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,8BAAsB,WAAW;IAC/B,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;gBAEZ,IAAI,EAAE,EAAE,CAAC,IAAI;IAInB,wBAAwB,CAAC,CAAC,EAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,CAAC;CAgBd"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseAdapter = void 0;
|
|
4
|
+
class BaseAdapter {
|
|
5
|
+
pool;
|
|
6
|
+
constructor(pool) {
|
|
7
|
+
this.pool = pool;
|
|
8
|
+
}
|
|
9
|
+
async executeWithTenantContext(tenantId, queryFn) {
|
|
10
|
+
const client = await this.pool.connect();
|
|
11
|
+
try {
|
|
12
|
+
await client.query("BEGIN");
|
|
13
|
+
await client.query("SELECT set_config('app.current_tenant_id', $1, true)", [tenantId]);
|
|
14
|
+
const result = await queryFn(client);
|
|
15
|
+
await client.query("COMMIT");
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
await client.query("ROLLBACK");
|
|
20
|
+
throw err;
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
try {
|
|
24
|
+
await client.query("RESET app.current_tenant_id");
|
|
25
|
+
}
|
|
26
|
+
catch { /* connection already broken */ }
|
|
27
|
+
client.release();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.BaseAdapter = BaseAdapter;
|
|
32
|
+
//# sourceMappingURL=base-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-adapter.js","sourceRoot":"","sources":["../src/base-adapter.ts"],"names":[],"mappings":";;;AAEA,MAAsB,WAAW;IACrB,IAAI,CAAU;IAExB,YAAY,IAAa;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,wBAAwB,CAC5B,QAAgB,EAChB,OAA8C;QAE9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,MAAM,MAAM,CAAC,KAAK,CAAC,sDAAsD,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;YACvF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;YACrC,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,IAAI,CAAC;gBAAC,MAAM,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,+BAA+B,CAAC,CAAC;YACpG,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;CACF;AA1BD,kCA0BC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the database name for a given tenant slug.
|
|
4
|
+
* Format: stratum_tenant_{slug}
|
|
5
|
+
*/
|
|
6
|
+
export declare function getDatabaseName(tenantSlug: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Checks whether the per-tenant database exists.
|
|
9
|
+
* Safe to run inside a transaction (uses pg_database catalog).
|
|
10
|
+
*/
|
|
11
|
+
export declare function databaseExists(client: pg.PoolClient | pg.Client, tenantSlug: string): Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* Creates the per-tenant database.
|
|
14
|
+
*
|
|
15
|
+
* IMPORTANT: CREATE DATABASE cannot run inside a transaction block.
|
|
16
|
+
* The caller MUST provide a pg.Client (not a pool client inside a transaction)
|
|
17
|
+
* with autocommit behavior. Typically obtained via pool.connect() without BEGIN.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createDatabase(client: pg.PoolClient | pg.Client, tenantSlug: string, templateDb?: string): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Drops the per-tenant database if it exists.
|
|
22
|
+
*
|
|
23
|
+
* IMPORTANT: DROP DATABASE cannot run inside a transaction block.
|
|
24
|
+
* Same constraint as createDatabase — use a standalone client.
|
|
25
|
+
*/
|
|
26
|
+
export declare function dropDatabase(client: pg.PoolClient | pg.Client, tenantSlug: string): Promise<void>;
|
|
27
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/database/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAkBpB;;;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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dropDatabase = exports.createDatabase = exports.databaseExists = exports.getDatabaseName = void 0;
|
|
4
|
+
const SLUG_REGEX = /^[a-z][a-z0-9_]{0,62}$/;
|
|
5
|
+
const DB_PREFIX = "stratum_tenant_";
|
|
6
|
+
/**
|
|
7
|
+
* Validates a tenant slug for use in a database name.
|
|
8
|
+
* Throws if invalid to prevent SQL injection.
|
|
9
|
+
*/
|
|
10
|
+
function validateSlug(slug) {
|
|
11
|
+
if (!SLUG_REGEX.test(slug)) {
|
|
12
|
+
throw new Error(`Invalid tenant slug: "${slug}". Must match ^[a-z][a-z0-9_]{0,62}$`);
|
|
13
|
+
}
|
|
14
|
+
return slug;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns the database name for a given tenant slug.
|
|
18
|
+
* Format: stratum_tenant_{slug}
|
|
19
|
+
*/
|
|
20
|
+
function getDatabaseName(tenantSlug) {
|
|
21
|
+
validateSlug(tenantSlug);
|
|
22
|
+
return `${DB_PREFIX}${tenantSlug}`;
|
|
23
|
+
}
|
|
24
|
+
exports.getDatabaseName = getDatabaseName;
|
|
25
|
+
/**
|
|
26
|
+
* Checks whether the per-tenant database exists.
|
|
27
|
+
* Safe to run inside a transaction (uses pg_database catalog).
|
|
28
|
+
*/
|
|
29
|
+
async function databaseExists(client, tenantSlug) {
|
|
30
|
+
const dbName = getDatabaseName(tenantSlug);
|
|
31
|
+
const res = await client.query(`SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1) AS exists`, [dbName]);
|
|
32
|
+
return res.rows[0].exists;
|
|
33
|
+
}
|
|
34
|
+
exports.databaseExists = databaseExists;
|
|
35
|
+
/**
|
|
36
|
+
* Creates the per-tenant database.
|
|
37
|
+
*
|
|
38
|
+
* IMPORTANT: CREATE DATABASE cannot run inside a transaction block.
|
|
39
|
+
* The caller MUST provide a pg.Client (not a pool client inside a transaction)
|
|
40
|
+
* with autocommit behavior. Typically obtained via pool.connect() without BEGIN.
|
|
41
|
+
*/
|
|
42
|
+
async function createDatabase(client, tenantSlug, templateDb) {
|
|
43
|
+
const dbName = getDatabaseName(tenantSlug);
|
|
44
|
+
// Identifiers validated by regex — no user-supplied interpolation outside of validated values.
|
|
45
|
+
// pg does not support parameterized DDL identifiers, so we construct the SQL string directly.
|
|
46
|
+
let sql = `CREATE DATABASE "${dbName}"`;
|
|
47
|
+
if (templateDb) {
|
|
48
|
+
// Template DB name must only contain safe identifier characters.
|
|
49
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(templateDb)) {
|
|
50
|
+
throw new Error(`Invalid template database name: "${templateDb}"`);
|
|
51
|
+
}
|
|
52
|
+
sql += ` TEMPLATE "${templateDb}"`;
|
|
53
|
+
}
|
|
54
|
+
await client.query(sql);
|
|
55
|
+
}
|
|
56
|
+
exports.createDatabase = createDatabase;
|
|
57
|
+
/**
|
|
58
|
+
* Drops the per-tenant database if it exists.
|
|
59
|
+
*
|
|
60
|
+
* IMPORTANT: DROP DATABASE cannot run inside a transaction block.
|
|
61
|
+
* Same constraint as createDatabase — use a standalone client.
|
|
62
|
+
*/
|
|
63
|
+
async function dropDatabase(client, tenantSlug) {
|
|
64
|
+
const dbName = getDatabaseName(tenantSlug);
|
|
65
|
+
await client.query(`DROP DATABASE IF EXISTS "${dbName}"`);
|
|
66
|
+
}
|
|
67
|
+
exports.dropDatabase = dropDatabase;
|
|
68
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/database/manager.ts"],"names":[],"mappings":";;;AAEA,MAAM,UAAU,GAAG,wBAAwB,CAAC;AAC5C,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAEpC;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,yBAAyB,IAAI,sCAAsC,CACpE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,UAAkB;IAChD,YAAY,CAAC,UAAU,CAAC,CAAC;IACzB,OAAO,GAAG,SAAS,GAAG,UAAU,EAAE,CAAC;AACrC,CAAC;AAHD,0CAGC;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;AAVD,wCAUC;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;AAjBD,wCAiBC;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;AAND,oCAMC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
export interface DatabasePoolManagerOptions {
|
|
3
|
+
/** Template connection config (host, port, user, password, ssl, etc.) — database name is overridden per tenant. */
|
|
4
|
+
baseConnectionConfig: pg.PoolConfig;
|
|
5
|
+
/** Maximum number of tenant pools to keep open simultaneously. Default: 50. */
|
|
6
|
+
maxPools?: number;
|
|
7
|
+
/** Milliseconds of inactivity before a pool is eligible for LRU eviction. Default: 30000. */
|
|
8
|
+
idleTimeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Manages a collection of per-tenant pg.Pool instances.
|
|
12
|
+
*
|
|
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.
|
|
15
|
+
*/
|
|
16
|
+
export declare class DatabasePoolManager {
|
|
17
|
+
private readonly pools;
|
|
18
|
+
private readonly baseConfig;
|
|
19
|
+
private readonly maxPools;
|
|
20
|
+
private readonly idleTimeoutMs;
|
|
21
|
+
constructor(options: DatabasePoolManagerOptions);
|
|
22
|
+
/** Validates tenant slug to prevent injection in database names. */
|
|
23
|
+
private validateSlug;
|
|
24
|
+
/**
|
|
25
|
+
* Returns an existing pool for the tenant slug, or creates a new one.
|
|
26
|
+
* Evicts the LRU pool if the pool limit is reached.
|
|
27
|
+
*
|
|
28
|
+
* When a regionId is provided, the pool is keyed as `regionId:slug` to support
|
|
29
|
+
* multi-region deployments where the same slug may exist in different regions.
|
|
30
|
+
*/
|
|
31
|
+
getPool(tenantSlug: string, regionId?: string): pg.Pool;
|
|
32
|
+
/** Closes and removes the pool for the given tenant slug. No-op if not found. */
|
|
33
|
+
closePool(tenantSlug: string): Promise<void>;
|
|
34
|
+
/** Closes all managed pools. Call during application shutdown. */
|
|
35
|
+
closeAll(): Promise<void>;
|
|
36
|
+
/** Returns a snapshot of current pool statistics. */
|
|
37
|
+
getStats(): {
|
|
38
|
+
poolCount: number;
|
|
39
|
+
activeConnections: number;
|
|
40
|
+
};
|
|
41
|
+
/** Evicts the pool that has been idle the longest. */
|
|
42
|
+
private evictLRU;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=pool-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool-manager.d.ts","sourceRoot":"","sources":["../../src/database/pool-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,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,oEAAoE;IACpE,OAAO,CAAC,YAAY;IAWpB;;;;;;OAMG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI;IAyBvD,iFAAiF;IAC3E,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlD,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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DatabasePoolManager = void 0;
|
|
7
|
+
const pg_1 = __importDefault(require("pg"));
|
|
8
|
+
/**
|
|
9
|
+
* Manages a collection of per-tenant pg.Pool instances.
|
|
10
|
+
*
|
|
11
|
+
* Pools are keyed by tenant slug and created on first access.
|
|
12
|
+
* When the pool count exceeds maxPools, the least-recently-used pool is evicted.
|
|
13
|
+
*/
|
|
14
|
+
class DatabasePoolManager {
|
|
15
|
+
pools = new Map();
|
|
16
|
+
baseConfig;
|
|
17
|
+
maxPools;
|
|
18
|
+
idleTimeoutMs;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.baseConfig = options.baseConnectionConfig;
|
|
21
|
+
this.maxPools = options.maxPools ?? 50;
|
|
22
|
+
this.idleTimeoutMs = options.idleTimeoutMs ?? 30_000;
|
|
23
|
+
}
|
|
24
|
+
/** Validates tenant slug to prevent injection in database names. */
|
|
25
|
+
validateSlug(slug) {
|
|
26
|
+
if (!/^[a-z][a-z0-9_]*$/.test(slug)) {
|
|
27
|
+
throw new Error(`Invalid tenant slug: "${slug}". Slugs must start with a lowercase letter and contain only lowercase alphanumeric characters and underscores.`);
|
|
28
|
+
}
|
|
29
|
+
if (slug.length > 63) {
|
|
30
|
+
throw new Error(`Tenant slug too long (max 63 chars): "${slug}"`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Returns an existing pool for the tenant slug, or creates a new one.
|
|
35
|
+
* Evicts the LRU pool if the pool limit is reached.
|
|
36
|
+
*
|
|
37
|
+
* When a regionId is provided, the pool is keyed as `regionId:slug` to support
|
|
38
|
+
* multi-region deployments where the same slug may exist in different regions.
|
|
39
|
+
*/
|
|
40
|
+
getPool(tenantSlug, regionId) {
|
|
41
|
+
this.validateSlug(tenantSlug);
|
|
42
|
+
const poolKey = regionId ? `${regionId}:${tenantSlug}` : tenantSlug;
|
|
43
|
+
const existing = this.pools.get(poolKey);
|
|
44
|
+
if (existing) {
|
|
45
|
+
existing.lastUsed = Date.now();
|
|
46
|
+
return existing.pool;
|
|
47
|
+
}
|
|
48
|
+
// Evict before adding so we never exceed maxPools.
|
|
49
|
+
if (this.pools.size >= this.maxPools) {
|
|
50
|
+
void this.evictLRU();
|
|
51
|
+
}
|
|
52
|
+
const dbName = `stratum_tenant_${tenantSlug}`;
|
|
53
|
+
const pool = new pg_1.default.Pool({
|
|
54
|
+
...this.baseConfig,
|
|
55
|
+
database: dbName,
|
|
56
|
+
idleTimeoutMillis: this.idleTimeoutMs,
|
|
57
|
+
});
|
|
58
|
+
this.pools.set(poolKey, { pool, lastUsed: Date.now() });
|
|
59
|
+
return pool;
|
|
60
|
+
}
|
|
61
|
+
/** Closes and removes the pool for the given tenant slug. No-op if not found. */
|
|
62
|
+
async closePool(tenantSlug) {
|
|
63
|
+
const entry = this.pools.get(tenantSlug);
|
|
64
|
+
if (!entry)
|
|
65
|
+
return;
|
|
66
|
+
this.pools.delete(tenantSlug);
|
|
67
|
+
await entry.pool.end();
|
|
68
|
+
}
|
|
69
|
+
/** Closes all managed pools. Call during application shutdown. */
|
|
70
|
+
async closeAll() {
|
|
71
|
+
const entries = Array.from(this.pools.entries());
|
|
72
|
+
this.pools.clear();
|
|
73
|
+
await Promise.all(entries.map(([, entry]) => entry.pool.end()));
|
|
74
|
+
}
|
|
75
|
+
/** Returns a snapshot of current pool statistics. */
|
|
76
|
+
getStats() {
|
|
77
|
+
let activeConnections = 0;
|
|
78
|
+
for (const entry of this.pools.values()) {
|
|
79
|
+
// pg.Pool exposes totalCount (all clients) and idleCount; active = total - idle
|
|
80
|
+
activeConnections +=
|
|
81
|
+
entry.pool.totalCount -
|
|
82
|
+
entry.pool.idleCount;
|
|
83
|
+
}
|
|
84
|
+
return { poolCount: this.pools.size, activeConnections };
|
|
85
|
+
}
|
|
86
|
+
/** Evicts the pool that has been idle the longest. */
|
|
87
|
+
async evictLRU() {
|
|
88
|
+
let oldestKey;
|
|
89
|
+
let oldestTime = Infinity;
|
|
90
|
+
for (const [key, entry] of this.pools.entries()) {
|
|
91
|
+
if (entry.lastUsed < oldestTime) {
|
|
92
|
+
oldestTime = entry.lastUsed;
|
|
93
|
+
oldestKey = key;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (oldestKey !== undefined) {
|
|
97
|
+
await this.closePool(oldestKey);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.DatabasePoolManager = DatabasePoolManager;
|
|
102
|
+
//# sourceMappingURL=pool-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { BaseAdapter } from "./base-adapter.js";
|
|
2
|
+
export { RawAdapter, createTenantPool } from "./adapters/raw.js";
|
|
3
|
+
export { PrismaAdapter, withTenant } from "./adapters/prisma.js";
|
|
4
|
+
export { SchemaRawAdapter, createSchemaTenantPool, } from "./adapters/schema-raw.js";
|
|
5
|
+
export { SchemaPrismaAdapter, withSchemaTenant, } from "./adapters/schema-prisma.js";
|
|
6
|
+
export { createPolicy, dropPolicy, enableRLS, disableRLS, isRLSEnabled, } from "./rls/manager.js";
|
|
7
|
+
export { setTenantContext, resetTenantContext, getCurrentTenantId, } from "./rls/session.js";
|
|
8
|
+
export { createSchema, dropSchema, schemaExists, listTenantSchemas, replicateTableToSchema, tenantSchemaName, } from "./schema/manager.js";
|
|
9
|
+
export { setSchemaSearchPath, resetSearchPath, getCurrentSearchPath, } from "./schema/session.js";
|
|
10
|
+
export { addTenantColumn, enableRLS as enableRLSMigration, createIsolationPolicy, migrateTable, } from "./migration-helpers.js";
|
|
11
|
+
export { getDatabaseName, databaseExists, createDatabase, dropDatabase, } from "./database/manager.js";
|
|
12
|
+
export { DatabasePoolManager } from "./database/pool-manager.js";
|
|
13
|
+
export type { DatabasePoolManagerOptions } from "./database/pool-manager.js";
|
|
14
|
+
export { DatabaseRawAdapter } from "./adapters/database-raw.js";
|
|
15
|
+
export { DatabasePrismaAdapter } from "./adapters/database-prisma.js";
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,EAAE,MAAM,sBAAsB,CAAC;AACjE,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,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,kBAAkB,EAC/B,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"}
|