@venturekit/data 0.0.24 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/index.js.map +1 -1
- package/dist/query/tenant.d.ts +61 -0
- package/dist/query/tenant.d.ts.map +1 -1
- package/dist/query/tenant.js +128 -6
- package/dist/query/tenant.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export { createRdsConfig, DEFAULT_RDS_CONFIG, buildRdsConfig, } from './rds/inde
|
|
|
8
8
|
export type { RdsOutputs, RdsInfraConfig } from './rds/index.js';
|
|
9
9
|
export { createMigrationConfig, DEFAULT_MIGRATION_CONFIG, getFlywayEnv, runMigrations, getMigrationStatus, runSeeds, getSeedStatus, MIGRATIONS_TRACKING_TABLE, SEEDS_TRACKING_TABLE, MigrationToolNotImplementedError, MigrationHashMismatchError, MigrationCollisionError, applyDatabaseUrlToEnv, } from './migrations/index.js';
|
|
10
10
|
export type { RunResult, StatusEntry, RunOptions, SeedRunOptions, } from './migrations/index.js';
|
|
11
|
-
export { query, getPool, mapResults, mapRow, configureMapper, getMapperConfig, beginTransaction, withTransaction, buildTransaction, applyDbSecretToEnv, TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './query/index.js';
|
|
11
|
+
export { query, getPool, mapResults, mapRow, configureMapper, getMapperConfig, beginTransaction, withTransaction, buildTransaction, applyDbSecretToEnv, TENANT_GUC, TENANT_SCOPE_GUC, runWithTenant, runWithTenantScope, runWithoutTenant, getCurrentTenantId, getCurrentTenantScope, isTenancyEngaged, } from './query/index.js';
|
|
12
12
|
export type { Transaction, ResultsMapper, Querier, MapperOptions, } from './query/index.js';
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKjE,OAAO,EAEL,qBAAqB,EACrB,wBAAwB,EACxB,YAAY,EAEZ,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB,EAEpB,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB,EAGvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,GACf,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAKhB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKjE,OAAO,EAEL,qBAAqB,EACrB,wBAAwB,EACxB,YAAY,EAEZ,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB,EAEpB,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB,EAGvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,GACf,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAKhB,kBAAkB,EAUlB,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,WAAW,EACX,aAAa,EACb,OAAO,EACP,aAAa,GACd,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,10 @@ applyDbSecretToEnv,
|
|
|
31
31
|
// `runWithTenant(id, fn)` and every query inside it — pooled or
|
|
32
32
|
// transactional — carries the `app.tenant_id` GUC, so RLS policies
|
|
33
33
|
// enforce isolation the application SQL may have forgotten.
|
|
34
|
+
// `runWithTenantScope([parent, ...children], fn)` additionally
|
|
35
|
+
// publishes `app.tenant_ids` for hierarchies where one request may
|
|
36
|
+
// read several tenants.
|
|
34
37
|
// See ./query/tenant.ts for the required policy shape and the
|
|
35
38
|
// database-role split RLS depends on.
|
|
36
|
-
TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './query/index.js';
|
|
39
|
+
TENANT_GUC, TENANT_SCOPE_GUC, runWithTenant, runWithTenantScope, runWithoutTenant, getCurrentTenantId, getCurrentTenantScope, isTenancyEngaged, } from './query/index.js';
|
|
37
40
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,MAAM;AACN,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,uEAAuE;AACvE,iEAAiE;AACjE,gDAAgD;AAChD,OAAO;AACL,4BAA4B;AAC5B,qBAAqB,EACrB,wBAAwB,EACxB,YAAY;AACZ,sEAAsE;AACtE,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB;AACpB,SAAS;AACT,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB;AACvB,uEAAuE;AACvE,qEAAqE;AACrE,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAQ/B,kDAAkD;AAClD,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB;AAChB,0DAA0D;AAC1D,mEAAmE;AACnE,iEAAiE;AACjE,gCAAgC;AAChC,kBAAkB;AAClB,2DAA2D;AAC3D,gEAAgE;AAChE,mEAAmE;AACnE,4DAA4D;AAC5D,8DAA8D;AAC9D,sCAAsC;AACtC,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,MAAM;AACN,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,uEAAuE;AACvE,iEAAiE;AACjE,gDAAgD;AAChD,OAAO;AACL,4BAA4B;AAC5B,qBAAqB,EACrB,wBAAwB,EACxB,YAAY;AACZ,sEAAsE;AACtE,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,yBAAyB,EACzB,oBAAoB;AACpB,SAAS;AACT,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB;AACvB,uEAAuE;AACvE,qEAAqE;AACrE,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAQ/B,kDAAkD;AAClD,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB;AAChB,0DAA0D;AAC1D,mEAAmE;AACnE,iEAAiE;AACjE,gCAAgC;AAChC,kBAAkB;AAClB,2DAA2D;AAC3D,gEAAgE;AAChE,mEAAmE;AACnE,4DAA4D;AAC5D,+DAA+D;AAC/D,mEAAmE;AACnE,wBAAwB;AACxB,8DAA8D;AAC9D,sCAAsC;AACtC,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
|
package/dist/query/index.d.ts
CHANGED
|
@@ -56,7 +56,7 @@ import type { ResultsMapper } from './transaction.js';
|
|
|
56
56
|
export declare const DEFAULT_SLOW_QUERY_THRESHOLD_MS = 1000;
|
|
57
57
|
export { getPool, applyDatabaseUrlToEnv } from './pool.js';
|
|
58
58
|
export { applyDbSecretToEnv } from './secret.js';
|
|
59
|
-
export { TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './tenant.js';
|
|
59
|
+
export { TENANT_GUC, TENANT_SCOPE_GUC, runWithTenant, runWithTenantScope, runWithoutTenant, getCurrentTenantId, getCurrentTenantScope, isTenancyEngaged, } from './tenant.js';
|
|
60
60
|
export { mapResults, mapRow, configureMapper, getMapperConfig, } from './mapper.js';
|
|
61
61
|
export type { MapperOptions } from './mapper.js';
|
|
62
62
|
export { beginTransaction, withTransaction, buildTransaction, } from './transaction.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtD;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAqBrD,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AA+B5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACvD,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EAAE,EACvB,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,EAChC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CA6CZ"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtD;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAqBrD,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AA+B5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACvD,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EAAE,EACvB,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,EAChC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CA6CZ"}
|
package/dist/query/index.js
CHANGED
|
@@ -77,7 +77,7 @@ function previewSql(sql) {
|
|
|
77
77
|
}
|
|
78
78
|
export { getPool, applyDatabaseUrlToEnv } from './pool.js';
|
|
79
79
|
export { applyDbSecretToEnv } from './secret.js';
|
|
80
|
-
export { TENANT_GUC, runWithTenant, runWithoutTenant, getCurrentTenantId, isTenancyEngaged, } from './tenant.js';
|
|
80
|
+
export { TENANT_GUC, TENANT_SCOPE_GUC, runWithTenant, runWithTenantScope, runWithoutTenant, getCurrentTenantId, getCurrentTenantScope, isTenancyEngaged, } from './tenant.js';
|
|
81
81
|
export { mapResults, mapRow, configureMapper, getMapperConfig, } from './mapper.js';
|
|
82
82
|
export { beginTransaction, withTransaction, buildTransaction, } from './transaction.js';
|
|
83
83
|
/**
|
package/dist/query/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAA8B,MAAM,aAAa,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAErD,SAAS,2BAA2B;IAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IACnD,IAAI,CAAC,GAAG;QAAE,OAAO,+BAA+B,CAAC;IACjD,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,+BAA+B,CAAC;AAC5E,CAAC;AAED;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AACrE,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAG1B;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,OAAO,CACpB,IAAU,EACV,QAAgB,EAChB,WAAuB;IAEvB,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,QAAgB,EAChB,WAAuB,EACvB,aAAgC,EAChC,aAA6B;IAE7B,sEAAsE;IACtE,0EAA0E;IAC1E,8EAA8E;IAC9E,6CAA6C;IAC7C,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,GAAgB,CAAC;IACrB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,mEAAmE;QACnE,gEAAgE;QAChE,mEAAmE;QACnE,mEAAmE;QACnE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAC1C,MAAM,MAAM,GAAG,GAA6D,CAAC;QAC7E,KAAK,CAAC,KAAK,CAAC,uBAAuB,EAAE;YACnC,UAAU;YACV,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC;YAChC,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;YACpC,SAAS,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;YACtD,YAAY,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YAC9D,MAAM,EAAE,MAAM,CAAC,IAAI;YACnB,UAAU,EAAE,MAAM,CAAC,QAAQ;YAC3B,SAAS,EAAE,MAAM,CAAC,OAAO;SAC1B,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IAC1C,MAAM,MAAM,GAAG,2BAA2B,EAAE,CAAC;IAC7C,IAAI,MAAM,GAAG,CAAC,IAAI,UAAU,IAAI,MAAM,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,qBAAqB,EAAE;YAChC,UAAU;YACV,WAAW,EAAE,MAAM;YACnB,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM;YACzC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC;SACjC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACnD,OAAO,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAsB,CAAC;AACxE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"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"}
|
package/dist/query/tenant.d.ts
CHANGED
|
@@ -35,6 +35,32 @@
|
|
|
35
35
|
* this wrong (e.g. omit the NULLIF and cast `''::uuid`) and you
|
|
36
36
|
* trade a leak for a runtime error on every unscoped query.
|
|
37
37
|
*
|
|
38
|
+
* # Hierarchies: one request, several readable tenants
|
|
39
|
+
*
|
|
40
|
+
* Plenty of products let one tenant read others: a parent organization
|
|
41
|
+
* over its subsidiaries, a franchise group over its outlets, a reseller
|
|
42
|
+
* over the accounts it manages. A single-valued GUC cannot express that,
|
|
43
|
+
* and the usual workaround — dropping RLS for those requests and
|
|
44
|
+
* rebuilding the filter in application SQL — removes the guarantee
|
|
45
|
+
* exactly where the access rules are most intricate.
|
|
46
|
+
*
|
|
47
|
+
* {@link runWithTenantScope} therefore publishes a SECOND GUC,
|
|
48
|
+
* `app.tenant_ids`, holding every tenant the request may read. The
|
|
49
|
+
* first element is also published as `app.tenant_id`, so it remains the
|
|
50
|
+
* "acting" tenant that writes are attributed to.
|
|
51
|
+
*
|
|
52
|
+
* ```sql
|
|
53
|
+
* CREATE POLICY tenant_isolation ON posts
|
|
54
|
+
* USING (tenant_id::text = ANY (
|
|
55
|
+
* string_to_array(NULLIF(current_setting('app.tenant_ids', true), ''), ',')
|
|
56
|
+
* ));
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* `string_to_array(NULL, ',')` is NULL and `x = ANY (NULL)` is NULL, so
|
|
60
|
+
* an unset GUC filters every row — the same fail-closed default as the
|
|
61
|
+
* single-tenant shape. {@link runWithTenant} sets both GUCs, so this one
|
|
62
|
+
* policy shape serves flat and hierarchical apps alike.
|
|
63
|
+
*
|
|
38
64
|
* RLS does not apply to a table's OWNER unless `FORCE ROW LEVEL
|
|
39
65
|
* SECURITY` is set, and is skipped entirely for roles with `BYPASSRLS`.
|
|
40
66
|
* Run the application as a role that owns nothing and has neither
|
|
@@ -68,6 +94,13 @@ import type { PoolClient } from 'pg';
|
|
|
68
94
|
* needed for the setting to exist.
|
|
69
95
|
*/
|
|
70
96
|
export declare const TENANT_GUC = "app.tenant_id";
|
|
97
|
+
/**
|
|
98
|
+
* The GUC carrying every tenant readable by the current request, as a
|
|
99
|
+
* comma-separated list. Always a superset of {@link TENANT_GUC}: a flat
|
|
100
|
+
* {@link runWithTenant} scope publishes the single id here too, so a
|
|
101
|
+
* policy written against this GUC alone is correct in both cases.
|
|
102
|
+
*/
|
|
103
|
+
export declare const TENANT_SCOPE_GUC = "app.tenant_ids";
|
|
71
104
|
/** True once tenancy has been engaged. Read by `query()`. */
|
|
72
105
|
export declare function isTenancyEngaged(): boolean;
|
|
73
106
|
/**
|
|
@@ -75,6 +108,11 @@ export declare function isTenancyEngaged(): boolean;
|
|
|
75
108
|
* scope / inside a {@link runWithoutTenant} block.
|
|
76
109
|
*/
|
|
77
110
|
export declare function getCurrentTenantId(): string | null;
|
|
111
|
+
/**
|
|
112
|
+
* Every tenant readable in the current async scope, acting tenant
|
|
113
|
+
* first. Empty outside any scope / inside {@link runWithoutTenant}.
|
|
114
|
+
*/
|
|
115
|
+
export declare function getCurrentTenantScope(): string[];
|
|
78
116
|
/**
|
|
79
117
|
* Run `fn` with `tenantId` applied to every query it issues.
|
|
80
118
|
*
|
|
@@ -92,6 +130,29 @@ export declare function getCurrentTenantId(): string | null;
|
|
|
92
130
|
* ```
|
|
93
131
|
*/
|
|
94
132
|
export declare function runWithTenant<T>(tenantId: string, fn: () => Promise<T>): Promise<T>;
|
|
133
|
+
/**
|
|
134
|
+
* Run `fn` with a scope spanning SEVERAL tenants — a parent and its
|
|
135
|
+
* children, a group and its members.
|
|
136
|
+
*
|
|
137
|
+
* `tenantIds[0]` is the acting tenant: it is published as
|
|
138
|
+
* {@link TENANT_GUC}, so writes and audit stay attributed to one
|
|
139
|
+
* subject, while reads may span the whole list via
|
|
140
|
+
* {@link TENANT_SCOPE_GUC}.
|
|
141
|
+
*
|
|
142
|
+
* The caller owns the expansion. This module deliberately does not walk
|
|
143
|
+
* a hierarchy: which tenants a principal may read is an authorization
|
|
144
|
+
* decision, and inferring it from a table here would put that decision
|
|
145
|
+
* out of reach of review. Resolve the list once, from your own closure
|
|
146
|
+
* table or membership rows, then hand it over.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* // A group administrator reads across the group's schools.
|
|
151
|
+
* const scope = [group.id, ...descendantIds];
|
|
152
|
+
* await runWithTenantScope(scope, async () => handler(body, ctx));
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export declare function runWithTenantScope<T>(tenantIds: string[], fn: () => Promise<T>): Promise<T>;
|
|
95
156
|
/**
|
|
96
157
|
* Run `fn` with NO tenant applied, clearing any inherited scope.
|
|
97
158
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/query/tenant.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/query/tenant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,mBAAmB,CAAC;AA6CjD,6DAA6D;AAC7D,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAElD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CASnF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA2B3F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGpE;AAkCD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB3E;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAShF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
package/dist/query/tenant.js
CHANGED
|
@@ -35,6 +35,32 @@
|
|
|
35
35
|
* this wrong (e.g. omit the NULLIF and cast `''::uuid`) and you
|
|
36
36
|
* trade a leak for a runtime error on every unscoped query.
|
|
37
37
|
*
|
|
38
|
+
* # Hierarchies: one request, several readable tenants
|
|
39
|
+
*
|
|
40
|
+
* Plenty of products let one tenant read others: a parent organization
|
|
41
|
+
* over its subsidiaries, a franchise group over its outlets, a reseller
|
|
42
|
+
* over the accounts it manages. A single-valued GUC cannot express that,
|
|
43
|
+
* and the usual workaround — dropping RLS for those requests and
|
|
44
|
+
* rebuilding the filter in application SQL — removes the guarantee
|
|
45
|
+
* exactly where the access rules are most intricate.
|
|
46
|
+
*
|
|
47
|
+
* {@link runWithTenantScope} therefore publishes a SECOND GUC,
|
|
48
|
+
* `app.tenant_ids`, holding every tenant the request may read. The
|
|
49
|
+
* first element is also published as `app.tenant_id`, so it remains the
|
|
50
|
+
* "acting" tenant that writes are attributed to.
|
|
51
|
+
*
|
|
52
|
+
* ```sql
|
|
53
|
+
* CREATE POLICY tenant_isolation ON posts
|
|
54
|
+
* USING (tenant_id::text = ANY (
|
|
55
|
+
* string_to_array(NULLIF(current_setting('app.tenant_ids', true), ''), ',')
|
|
56
|
+
* ));
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* `string_to_array(NULL, ',')` is NULL and `x = ANY (NULL)` is NULL, so
|
|
60
|
+
* an unset GUC filters every row — the same fail-closed default as the
|
|
61
|
+
* single-tenant shape. {@link runWithTenant} sets both GUCs, so this one
|
|
62
|
+
* policy shape serves flat and hierarchical apps alike.
|
|
63
|
+
*
|
|
38
64
|
* RLS does not apply to a table's OWNER unless `FORCE ROW LEVEL
|
|
39
65
|
* SECURITY` is set, and is skipped entirely for roles with `BYPASSRLS`.
|
|
40
66
|
* Run the application as a role that owns nothing and has neither
|
|
@@ -68,6 +94,19 @@ import { AsyncLocalStorage } from 'node:async_hooks';
|
|
|
68
94
|
* needed for the setting to exist.
|
|
69
95
|
*/
|
|
70
96
|
export const TENANT_GUC = 'app.tenant_id';
|
|
97
|
+
/**
|
|
98
|
+
* The GUC carrying every tenant readable by the current request, as a
|
|
99
|
+
* comma-separated list. Always a superset of {@link TENANT_GUC}: a flat
|
|
100
|
+
* {@link runWithTenant} scope publishes the single id here too, so a
|
|
101
|
+
* policy written against this GUC alone is correct in both cases.
|
|
102
|
+
*/
|
|
103
|
+
export const TENANT_SCOPE_GUC = 'app.tenant_ids';
|
|
104
|
+
/**
|
|
105
|
+
* Separator for {@link TENANT_SCOPE_GUC}. A tenant id containing this
|
|
106
|
+
* character would split into two ids and silently widen the scope, so
|
|
107
|
+
* {@link runWithTenantScope} rejects such ids rather than encoding them.
|
|
108
|
+
*/
|
|
109
|
+
const SCOPE_SEPARATOR = ',';
|
|
71
110
|
/**
|
|
72
111
|
* Value written when a scope is explicitly un-tenanted
|
|
73
112
|
* ({@link runWithoutTenant}). The empty string — rather than
|
|
@@ -102,6 +141,13 @@ export function isTenancyEngaged() {
|
|
|
102
141
|
export function getCurrentTenantId() {
|
|
103
142
|
return storage.getStore()?.tenantId ?? null;
|
|
104
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Every tenant readable in the current async scope, acting tenant
|
|
146
|
+
* first. Empty outside any scope / inside {@link runWithoutTenant}.
|
|
147
|
+
*/
|
|
148
|
+
export function getCurrentTenantScope() {
|
|
149
|
+
return storage.getStore()?.tenantIds ?? [];
|
|
150
|
+
}
|
|
105
151
|
/**
|
|
106
152
|
* Run `fn` with `tenantId` applied to every query it issues.
|
|
107
153
|
*
|
|
@@ -124,7 +170,51 @@ export function runWithTenant(tenantId, fn) {
|
|
|
124
170
|
'deliberately un-scoped work.');
|
|
125
171
|
}
|
|
126
172
|
engaged = true;
|
|
127
|
-
return storage.run({ tenantId }, fn);
|
|
173
|
+
return storage.run({ tenantId, tenantIds: [tenantId] }, fn);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Run `fn` with a scope spanning SEVERAL tenants — a parent and its
|
|
177
|
+
* children, a group and its members.
|
|
178
|
+
*
|
|
179
|
+
* `tenantIds[0]` is the acting tenant: it is published as
|
|
180
|
+
* {@link TENANT_GUC}, so writes and audit stay attributed to one
|
|
181
|
+
* subject, while reads may span the whole list via
|
|
182
|
+
* {@link TENANT_SCOPE_GUC}.
|
|
183
|
+
*
|
|
184
|
+
* The caller owns the expansion. This module deliberately does not walk
|
|
185
|
+
* a hierarchy: which tenants a principal may read is an authorization
|
|
186
|
+
* decision, and inferring it from a table here would put that decision
|
|
187
|
+
* out of reach of review. Resolve the list once, from your own closure
|
|
188
|
+
* table or membership rows, then hand it over.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* // A group administrator reads across the group's schools.
|
|
193
|
+
* const scope = [group.id, ...descendantIds];
|
|
194
|
+
* await runWithTenantScope(scope, async () => handler(body, ctx));
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export function runWithTenantScope(tenantIds, fn) {
|
|
198
|
+
if (tenantIds.length === 0) {
|
|
199
|
+
throw new Error('runWithTenantScope requires at least one tenantId. Use runWithoutTenant() for ' +
|
|
200
|
+
'deliberately un-scoped work.');
|
|
201
|
+
}
|
|
202
|
+
for (const id of tenantIds) {
|
|
203
|
+
if (!id) {
|
|
204
|
+
throw new Error('runWithTenantScope requires non-empty tenant ids.');
|
|
205
|
+
}
|
|
206
|
+
if (id.includes(SCOPE_SEPARATOR)) {
|
|
207
|
+
// Encoding this would split one id into two and grant read access
|
|
208
|
+
// to a tenant nobody authorized.
|
|
209
|
+
throw new Error(`runWithTenantScope: tenant id ${JSON.stringify(id)} contains ${JSON.stringify(SCOPE_SEPARATOR)}, which is the scope separator.`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// De-duplicate while preserving order so the acting tenant stays first
|
|
213
|
+
// and a caller passing [parent, ...children] that already contains the
|
|
214
|
+
// parent does not write it twice.
|
|
215
|
+
const unique = [...new Set(tenantIds)];
|
|
216
|
+
engaged = true;
|
|
217
|
+
return storage.run({ tenantId: unique[0], tenantIds: unique }, fn);
|
|
128
218
|
}
|
|
129
219
|
/**
|
|
130
220
|
* Run `fn` with NO tenant applied, clearing any inherited scope.
|
|
@@ -141,7 +231,18 @@ export function runWithTenant(tenantId, fn) {
|
|
|
141
231
|
*/
|
|
142
232
|
export function runWithoutTenant(fn) {
|
|
143
233
|
engaged = true;
|
|
144
|
-
return storage.run({ tenantId: null }, fn);
|
|
234
|
+
return storage.run({ tenantId: null, tenantIds: [] }, fn);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The pair of values to publish for the current scope. Both are written
|
|
238
|
+
* together so a connection can never carry one request's acting tenant
|
|
239
|
+
* beside another's readable set.
|
|
240
|
+
*/
|
|
241
|
+
function currentGucValues() {
|
|
242
|
+
return {
|
|
243
|
+
tenant: getCurrentTenantId() ?? NO_TENANT,
|
|
244
|
+
scope: getCurrentTenantScope().join(SCOPE_SEPARATOR),
|
|
245
|
+
};
|
|
145
246
|
}
|
|
146
247
|
/**
|
|
147
248
|
* Last value applied to each physical connection.
|
|
@@ -153,6 +254,14 @@ export function runWithoutTenant(fn) {
|
|
|
153
254
|
* the GUC.
|
|
154
255
|
*/
|
|
155
256
|
const appliedByClient = new WeakMap();
|
|
257
|
+
/**
|
|
258
|
+
* Memo key for the applied pair. `\u0000` cannot appear in either value
|
|
259
|
+
* (Postgres rejects NUL in text, and the scope separator is a comma), so
|
|
260
|
+
* distinct pairs cannot collide onto one key.
|
|
261
|
+
*/
|
|
262
|
+
function memoKey(tenant, scope) {
|
|
263
|
+
return `${tenant}\u0000${scope}`;
|
|
264
|
+
}
|
|
156
265
|
/**
|
|
157
266
|
* Ensure `client`'s session GUC matches the current scope, issuing
|
|
158
267
|
* `set_config` only when it differs.
|
|
@@ -162,13 +271,21 @@ const appliedByClient = new WeakMap();
|
|
|
162
271
|
* only injection-safe way to write one.
|
|
163
272
|
*/
|
|
164
273
|
export async function applyTenantToClient(client) {
|
|
165
|
-
const
|
|
274
|
+
const { tenant, scope } = currentGucValues();
|
|
275
|
+
const want = memoKey(tenant, scope);
|
|
166
276
|
if (appliedByClient.get(client) === want)
|
|
167
277
|
return;
|
|
168
278
|
// Record BEFORE awaiting: a rejected set_config must not leave the
|
|
169
279
|
// memo claiming a value the server never accepted.
|
|
170
280
|
appliedByClient.delete(client);
|
|
171
|
-
|
|
281
|
+
// One statement, so the acting tenant and the readable set can never
|
|
282
|
+
// be left half-applied by a connection dropped between two queries.
|
|
283
|
+
await client.query(`SELECT set_config($1, $2, false), set_config($3, $4, false)`, [
|
|
284
|
+
TENANT_GUC,
|
|
285
|
+
tenant,
|
|
286
|
+
TENANT_SCOPE_GUC,
|
|
287
|
+
scope,
|
|
288
|
+
]);
|
|
172
289
|
appliedByClient.set(client, want);
|
|
173
290
|
}
|
|
174
291
|
/**
|
|
@@ -184,8 +301,13 @@ export async function applyTenantToClient(client) {
|
|
|
184
301
|
export async function applyTenantToTransaction(client) {
|
|
185
302
|
if (!engaged)
|
|
186
303
|
return;
|
|
187
|
-
const
|
|
188
|
-
await client.query(`SELECT set_config($1, $2, true)
|
|
304
|
+
const { tenant, scope } = currentGucValues();
|
|
305
|
+
await client.query(`SELECT set_config($1, $2, true), set_config($3, $4, true)`, [
|
|
306
|
+
TENANT_GUC,
|
|
307
|
+
tenant,
|
|
308
|
+
TENANT_SCOPE_GUC,
|
|
309
|
+
scope,
|
|
310
|
+
]);
|
|
189
311
|
}
|
|
190
312
|
/**
|
|
191
313
|
* Reset module state. Test-only — production code has no reason to
|
package/dist/query/tenant.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/query/tenant.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/query/tenant.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@venturekit/data",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "Database and data layer for VentureKit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@aws-sdk/client-secrets-manager": "^3.1091.0",
|
|
33
|
-
"@venturekit/core": "0.0.
|
|
33
|
+
"@venturekit/core": "0.0.26",
|
|
34
34
|
"pg": "^8.12.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|