@stratum-hq/mysql 0.4.0 → 0.6.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/README.md +37 -9
- package/dist/adapters/table.d.ts +3 -0
- package/dist/adapters/table.d.ts.map +1 -1
- package/dist/adapters/table.js +8 -1
- package/dist/adapters/table.js.map +1 -1
- package/dist/integrations/knex.d.ts +9 -0
- package/dist/integrations/knex.d.ts.map +1 -1
- package/dist/integrations/knex.js +38 -5
- package/dist/integrations/knex.js.map +1 -1
- package/dist/integrations/sequelize.d.ts +29 -7
- package/dist/integrations/sequelize.d.ts.map +1 -1
- package/dist/integrations/sequelize.js +400 -9
- package/dist/integrations/sequelize.js.map +1 -1
- package/dist/integrations/typeorm-subscriber.d.ts +99 -7
- package/dist/integrations/typeorm-subscriber.d.ts.map +1 -1
- package/dist/integrations/typeorm-subscriber.js +471 -7
- package/dist/integrations/typeorm-subscriber.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -3
|
@@ -2,12 +2,40 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StratumTypeOrmSubscriber = void 0;
|
|
4
4
|
exports.registerStratumSubscriber = registerStratumSubscriber;
|
|
5
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
5
6
|
const sdk_1 = require("@stratum-hq/sdk");
|
|
6
7
|
const utils_js_1 = require("../utils.js");
|
|
7
8
|
// TypeORM writes an upsert on MySQL as INSERT ... ON DUPLICATE KEY UPDATE and
|
|
8
9
|
// quotes each column with backticks.
|
|
9
10
|
const UPSERT_CLAUSE = /\bON\s+DUPLICATE\s+KEY\s+UPDATE\b/i;
|
|
10
11
|
const TENANT_ASSIGNMENT = /`tenant_id`\s*=/i;
|
|
12
|
+
// The target of an INSERT, as TypeORM writes it: `table` or `schema`.`table`.
|
|
13
|
+
const INSERT_TARGET = /^\s*INSERT\s+(?:IGNORE\s+)?INTO\s+(?:`((?:[^`]|``)+)`\.)?`((?:[^`]|``)+)`/i;
|
|
14
|
+
const UPDATE_OR_DELETE = /^\s*(?:UPDATE|DELETE)\b/i;
|
|
15
|
+
// The target of a TRUNCATE, as TypeORM writes it: `table` or `schema`.`table`.
|
|
16
|
+
const TRUNCATE_TARGET = /^\s*TRUNCATE\s+(?:TABLE\s+)?(?:`((?:[^`]|``)+)`\.)?`?((?:[^`\s;]|``)+)`?/i;
|
|
17
|
+
const TENANT_COLUMN_SQL = "SELECT COUNT(*) AS n FROM information_schema.COLUMNS " +
|
|
18
|
+
"WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ? AND LOWER(COLUMN_NAME) = 'tenant_id'";
|
|
19
|
+
const TENANT_PARAMETER = "stratumTenantId";
|
|
20
|
+
const SCOPED_EXECUTE = Symbol("stratum.tenantScopedExecute");
|
|
21
|
+
const SCOPED_GET_QUERY = Symbol("stratum.tenantScopedGetQuery");
|
|
22
|
+
const SCOPED_INSERT = Symbol("stratum.tenantScopedInsert");
|
|
23
|
+
const EXEMPT_VIEW_EXPRESSION = Symbol("stratum.exemptViewExpression");
|
|
24
|
+
/** Inserted values whose primary key was already checked. */
|
|
25
|
+
const keyChecked = new WeakSet();
|
|
26
|
+
/** Values of an upsert while its builder runs; their keys are checked in beforeQuery instead. */
|
|
27
|
+
const upsertValues = new WeakSet();
|
|
28
|
+
/** True while a view entity's query builder expression is being built. */
|
|
29
|
+
const buildingViewExpression = new node_async_hooks_1.AsyncLocalStorage();
|
|
30
|
+
/** Select query builders returned by a view entity's expression. */
|
|
31
|
+
const viewExpressionBuilders = new WeakSet();
|
|
32
|
+
/** The WHERE addition that addTenantCondition set on each write builder's expression map. */
|
|
33
|
+
const scopedWriteConditions = new WeakMap();
|
|
34
|
+
/** Data sources whose update and delete query builders are tenant-scoped. */
|
|
35
|
+
const scopedDataSources = new WeakSet();
|
|
36
|
+
const UNIQUE_KEYS_SQL = "SELECT INDEX_NAME AS index_name, COLUMN_NAME AS column_name " +
|
|
37
|
+
"FROM information_schema.STATISTICS " +
|
|
38
|
+
"WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ? AND NON_UNIQUE = 0";
|
|
11
39
|
/**
|
|
12
40
|
* TypeORM subscriber that injects tenant_id into inserted entities via ALS and
|
|
13
41
|
* never lets an update change tenant_id.
|
|
@@ -15,34 +43,94 @@ const TENANT_ASSIGNMENT = /`tenant_id`\s*=/i;
|
|
|
15
43
|
* Register it with `registerStratumSubscriber(dataSource)` after
|
|
16
44
|
* `dataSource.initialize()`. TypeORM's `subscribers` option only loads classes
|
|
17
45
|
* decorated with `@EventSubscriber()`, so it does not load this class.
|
|
46
|
+
* Registration also scopes TypeORM's update, delete and soft-delete query
|
|
47
|
+
* builders (repository update(), delete(), save() of an existing row, remove(),
|
|
48
|
+
* softDelete(), restore()) to the current tenant: `tenant_id = <tenant>` is
|
|
49
|
+
* ANDed to their WHERE clause, and they are refused outside a tenant context.
|
|
18
50
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
51
|
+
* Registration scopes reads the same way. Every SQL statement that TypeORM's
|
|
52
|
+
* select query builder produces (repository find*, findOne*, count*, exists*,
|
|
53
|
+
* sum/average/min/max, preload(), query builder getMany/getOne/getRawMany/
|
|
54
|
+
* getRawOne/getCount/getManyAndCount/getExists/stream, relation loading, the
|
|
55
|
+
* row that save() loads, and the count and pagination queries TypeORM builds
|
|
56
|
+
* internally) ANDs `tenant_id = <tenant>` for the FROM entity to its WHERE
|
|
57
|
+
* clause and adds it to the ON condition of each joined entity, so a LEFT JOIN
|
|
58
|
+
* still returns the parent row. Reads of an entity with a tenant_id column are
|
|
59
|
+
* refused outside a tenant context.
|
|
60
|
+
*
|
|
61
|
+
* Not scoped: raw SQL (`dataSource.query()`, `queryRunner.query()`), SQL taken
|
|
62
|
+
* from a builder's getQuery() / getQueryAndParameters() and run by hand, reads
|
|
63
|
+
* from a table that has no entity on the data source, and many-to-many junction
|
|
64
|
+
* tables. Add the tenant condition to those yourself, or use the shared-table
|
|
65
|
+
* adapter's structured methods.
|
|
22
66
|
*/
|
|
23
67
|
class StratumTypeOrmSubscriber {
|
|
24
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Injects the current tenant's ID into the entity before insert, into the
|
|
70
|
+
* property mapped to the tenant_id column.
|
|
71
|
+
*
|
|
72
|
+
* When the entity supplies its whole primary key and the insert builder has
|
|
73
|
+
* not checked it yet (save() runs this hook before the builder), the key is
|
|
74
|
+
* checked against the table first, so a save() with another tenant's key is
|
|
75
|
+
* refused rather than inserted. See checkKeyIsFree.
|
|
76
|
+
*
|
|
77
|
+
* @returns A promise that rejects when the key belongs to another tenant's row.
|
|
78
|
+
*/
|
|
25
79
|
beforeInsert(event) {
|
|
26
80
|
const context = (0, sdk_1.getTenantContext)();
|
|
27
81
|
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
28
|
-
|
|
82
|
+
let key;
|
|
83
|
+
if (event.metadata && !keyChecked.has(event.entity) && !upsertValues.has(event.entity)) {
|
|
84
|
+
keyChecked.add(event.entity);
|
|
85
|
+
key = primaryKeyOf(event.metadata, event.entity);
|
|
86
|
+
}
|
|
87
|
+
setTenant(event.entity, event.metadata, context.tenant_id);
|
|
88
|
+
if (key && event.metadata && event.queryRunner) {
|
|
89
|
+
return checkKeyIsFree(event.queryRunner, event.metadata, key, context.tenant_id);
|
|
90
|
+
}
|
|
29
91
|
}
|
|
30
92
|
/**
|
|
31
93
|
* Keeps tenant_id out of an update. On save() the loaded value is restored,
|
|
32
94
|
* so the column is unchanged; on a repository or query builder update the
|
|
33
95
|
* column is dropped from the SET values (MySQL column names are
|
|
34
96
|
* case-insensitive, so any letter case is dropped).
|
|
97
|
+
*
|
|
98
|
+
* @throws Error when save() loaded a row whose tenant_id is not the current
|
|
99
|
+
* tenant's. The loaded tenant_id is not copied onto the entity.
|
|
35
100
|
*/
|
|
36
101
|
beforeUpdate(event) {
|
|
37
102
|
const entity = event.entity;
|
|
38
103
|
if (!entity)
|
|
39
104
|
return;
|
|
105
|
+
const loaded = event.databaseEntity;
|
|
106
|
+
if (loaded) {
|
|
107
|
+
const loadedProps = new Set(Object.keys(loaded).filter((key) => key.toLowerCase() === "tenant_id"));
|
|
108
|
+
for (const column of event.metadata?.columns ?? []) {
|
|
109
|
+
if (column.databaseName.toLowerCase() === "tenant_id" && column.propertyName in loaded) {
|
|
110
|
+
loadedProps.add(column.propertyName);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (loadedProps.size > 0) {
|
|
114
|
+
const context = (0, sdk_1.getTenantContext)();
|
|
115
|
+
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
116
|
+
for (const prop of loadedProps) {
|
|
117
|
+
if (loaded[prop] !== context.tenant_id) {
|
|
118
|
+
throw new Error("Stratum: save() refused, because the row belongs to another tenant.");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
40
123
|
const tenantProps = new Set(Object.keys(entity).filter((key) => key.toLowerCase() === "tenant_id"));
|
|
41
124
|
for (const column of event.metadata?.columns ?? []) {
|
|
42
125
|
if (column.databaseName.toLowerCase() === "tenant_id" && column.propertyName in entity) {
|
|
43
126
|
tenantProps.add(column.propertyName);
|
|
44
127
|
}
|
|
45
128
|
}
|
|
129
|
+
// A relation whose join column is tenant_id writes that column too.
|
|
130
|
+
for (const prop of tenantRelationProps(event.metadata)) {
|
|
131
|
+
if (prop in entity)
|
|
132
|
+
tenantProps.add(prop);
|
|
133
|
+
}
|
|
46
134
|
for (const prop of tenantProps) {
|
|
47
135
|
const loaded = event.databaseEntity;
|
|
48
136
|
if (loaded && prop in loaded) {
|
|
@@ -54,29 +142,401 @@ class StratumTypeOrmSubscriber {
|
|
|
54
142
|
}
|
|
55
143
|
}
|
|
56
144
|
/**
|
|
57
|
-
* Rejects an upsert that
|
|
145
|
+
* Rejects an upsert that could update another tenant's row.
|
|
58
146
|
*
|
|
59
147
|
* A conflict update must never change the tenant of an existing row. A
|
|
60
148
|
* subscriber cannot remove one column from the conflict update, so the
|
|
61
149
|
* statement fails before it runs.
|
|
62
150
|
*
|
|
151
|
+
* MySQL runs ON DUPLICATE KEY UPDATE on a conflict with any unique key of
|
|
152
|
+
* the table, whatever conflict columns were passed to upsert() or
|
|
153
|
+
* orUpdate(). The upsert is therefore allowed only when every unique key
|
|
154
|
+
* (including the primary key) of the target table contains tenant_id, so a
|
|
155
|
+
* conflict can only ever be with a row of the same tenant. The keys are read
|
|
156
|
+
* from information_schema through the same query runner.
|
|
157
|
+
*
|
|
63
158
|
* @throws Error when the ON DUPLICATE KEY UPDATE clause assigns tenant_id.
|
|
159
|
+
* @returns A promise that rejects when a unique key of the table does not
|
|
160
|
+
* include tenant_id, or when the table cannot be determined.
|
|
64
161
|
*/
|
|
65
162
|
beforeQuery(event) {
|
|
163
|
+
if (event.dataSource &&
|
|
164
|
+
!scopedDataSources.has(event.dataSource) &&
|
|
165
|
+
UPDATE_OR_DELETE.test(event.query)) {
|
|
166
|
+
throw new Error("Stratum: register the subscriber with registerStratumSubscriber(dataSource), " +
|
|
167
|
+
"which scopes updates and deletes to the current tenant.");
|
|
168
|
+
}
|
|
169
|
+
if (/^\s*TRUNCATE\b/i.test(event.query))
|
|
170
|
+
return assertNotTenantTable(event);
|
|
66
171
|
const clause = event.query.split(UPSERT_CLAUSE)[1];
|
|
67
|
-
if (clause
|
|
172
|
+
if (clause === undefined)
|
|
173
|
+
return;
|
|
174
|
+
if (TENANT_ASSIGNMENT.test(clause)) {
|
|
68
175
|
throw new Error("Stratum: an upsert must not update tenant_id on conflict. " +
|
|
69
176
|
"Remove tenant_id from the entity values or from the orUpdate() columns.");
|
|
70
177
|
}
|
|
178
|
+
return assertUniqueKeysIncludeTenant(event);
|
|
71
179
|
}
|
|
72
180
|
}
|
|
73
181
|
exports.StratumTypeOrmSubscriber = StratumTypeOrmSubscriber;
|
|
182
|
+
/**
|
|
183
|
+
* ANDs `tenant_id = <current tenant>` to the WHERE clause of an update, delete
|
|
184
|
+
* or soft-delete builder whose data source has the subscriber. Entities without
|
|
185
|
+
* a tenant_id column are left alone; a target without metadata (a table name)
|
|
186
|
+
* is treated as a tenant table.
|
|
187
|
+
*/
|
|
188
|
+
function addTenantCondition(builder) {
|
|
189
|
+
if (!builder.connection.subscribers.some((s) => s instanceof StratumTypeOrmSubscriber))
|
|
190
|
+
return;
|
|
191
|
+
const alias = builder.expressionMap.mainAlias;
|
|
192
|
+
let column = "tenant_id";
|
|
193
|
+
if (alias?.hasMetadata) {
|
|
194
|
+
const tenantColumn = alias.metadata.columns.find((c) => c.databaseName.toLowerCase() === "tenant_id");
|
|
195
|
+
if (!tenantColumn)
|
|
196
|
+
return;
|
|
197
|
+
column = tenantColumn.databaseName;
|
|
198
|
+
}
|
|
199
|
+
const context = (0, sdk_1.getTenantContext)();
|
|
200
|
+
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
201
|
+
stripTenantFromUpdateValues(builder);
|
|
202
|
+
const qualified = builder.expressionMap.aliasNamePrefixingEnabled && alias
|
|
203
|
+
? `${builder.escape(alias.name)}.${builder.escape(column)}`
|
|
204
|
+
: builder.escape(column);
|
|
205
|
+
const condition = `${qualified} = :${TENANT_PARAMETER}`;
|
|
206
|
+
const existing = builder.expressionMap.extraAppendedAndWhereCondition;
|
|
207
|
+
// A builder that is executed again already carries the condition set here.
|
|
208
|
+
if (scopedWriteConditions.get(builder.expressionMap) !== existing) {
|
|
209
|
+
// TypeORM ANDs this condition, in its own parentheses, to the caller's
|
|
210
|
+
// WHERE clause, so an orWhere() cannot widen the statement past the tenant.
|
|
211
|
+
builder.expressionMap.extraAppendedAndWhereCondition = existing
|
|
212
|
+
? `(${existing}) AND ${condition}`
|
|
213
|
+
: condition;
|
|
214
|
+
scopedWriteConditions.set(builder.expressionMap, builder.expressionMap.extraAppendedAndWhereCondition);
|
|
215
|
+
}
|
|
216
|
+
builder.setParameter(TENANT_PARAMETER, context.tenant_id);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Drops tenant_id from the SET values of an update builder, so an update never
|
|
220
|
+
* moves a row to another tenant, with or without listeners (beforeUpdate).
|
|
221
|
+
*/
|
|
222
|
+
function stripTenantFromUpdateValues(builder) {
|
|
223
|
+
const map = builder.expressionMap;
|
|
224
|
+
const values = map.valuesSet;
|
|
225
|
+
if (map.queryType !== "update" || !values || typeof values !== "object")
|
|
226
|
+
return;
|
|
227
|
+
const alias = map.mainAlias;
|
|
228
|
+
const metadata = alias?.hasMetadata ? alias.metadata : undefined;
|
|
229
|
+
const tenantProps = new Set([
|
|
230
|
+
...(metadata?.columns ?? []).filter((c) => c.databaseName.toLowerCase() === "tenant_id").map((c) => c.propertyName),
|
|
231
|
+
...tenantRelationProps(metadata),
|
|
232
|
+
]);
|
|
233
|
+
const keys = Object.keys(values);
|
|
234
|
+
const isTenantKey = (key) => key.toLowerCase() === "tenant_id" || tenantProps.has(key);
|
|
235
|
+
if (!keys.some(isTenantKey))
|
|
236
|
+
return;
|
|
237
|
+
map.valuesSet = Object.fromEntries(keys.filter((key) => !isTenantKey(key)).map((key) => [key, values[key]]));
|
|
238
|
+
}
|
|
239
|
+
/** Wraps execute() of TypeORM's update, delete and soft-delete query builder classes, once. */
|
|
240
|
+
function scopeWriteBuilders(dataSource) {
|
|
241
|
+
const builders = [
|
|
242
|
+
dataSource.createQueryBuilder().update(),
|
|
243
|
+
dataSource.createQueryBuilder().delete(),
|
|
244
|
+
dataSource.createQueryBuilder().softDelete(),
|
|
245
|
+
];
|
|
246
|
+
for (const builder of builders) {
|
|
247
|
+
const proto = Object.getPrototypeOf(builder);
|
|
248
|
+
if (proto[SCOPED_EXECUTE])
|
|
249
|
+
continue;
|
|
250
|
+
const original = proto.execute;
|
|
251
|
+
proto.execute = function () {
|
|
252
|
+
addTenantCondition(this);
|
|
253
|
+
return original.call(this);
|
|
254
|
+
};
|
|
255
|
+
proto[SCOPED_EXECUTE] = true;
|
|
256
|
+
}
|
|
257
|
+
scopedDataSources.add(dataSource);
|
|
258
|
+
}
|
|
259
|
+
/** Returns the tenant_id column of an entity's metadata, if it has one. */
|
|
260
|
+
function tenantColumnIn(metadata) {
|
|
261
|
+
return metadata?.columns.find((c) => c.databaseName.toLowerCase() === "tenant_id");
|
|
262
|
+
}
|
|
263
|
+
/** Returns the properties of relations whose join columns include tenant_id. */
|
|
264
|
+
function tenantRelationProps(metadata) {
|
|
265
|
+
return (metadata?.relations ?? [])
|
|
266
|
+
.filter((r) => (r.joinColumns ?? []).some((c) => c.databaseName.toLowerCase() === "tenant_id"))
|
|
267
|
+
.map((r) => r.propertyName);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Writes the tenant into the property mapped to the tenant_id column, and
|
|
271
|
+
* removes any relation whose join column is tenant_id, because TypeORM reads
|
|
272
|
+
* the column's value from such a relation first.
|
|
273
|
+
*/
|
|
274
|
+
function setTenant(entity, metadata, tenantId) {
|
|
275
|
+
for (const prop of tenantRelationProps(metadata))
|
|
276
|
+
delete entity[prop];
|
|
277
|
+
const column = tenantColumnIn(metadata);
|
|
278
|
+
if (!column) {
|
|
279
|
+
entity["tenant_id"] = tenantId;
|
|
280
|
+
}
|
|
281
|
+
else if (column.setEntityValue) {
|
|
282
|
+
column.setEntityValue(entity, tenantId);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
entity[column.propertyName] = tenantId;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Returns the primary key values that an entity supplies, keyed by column
|
|
290
|
+
* name. Returns undefined when the entity has no tenant_id column or any part
|
|
291
|
+
* of its primary key is missing or computed by SQL.
|
|
292
|
+
*/
|
|
293
|
+
function primaryKeyOf(metadata, entity) {
|
|
294
|
+
if (metadata.primaryColumns.length === 0 || !tenantColumnIn(metadata))
|
|
295
|
+
return undefined;
|
|
296
|
+
const key = new Map();
|
|
297
|
+
for (const column of metadata.primaryColumns) {
|
|
298
|
+
const value = column.getEntityValue ? column.getEntityValue(entity) : entity[column.propertyName];
|
|
299
|
+
if (value === undefined || value === null || typeof value === "function")
|
|
300
|
+
return undefined;
|
|
301
|
+
key.set(column.databaseName, value);
|
|
302
|
+
}
|
|
303
|
+
return key;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Refuses an insert whose primary key belongs to another tenant's row. The
|
|
307
|
+
* lookup is raw SQL, so it is not tenant-scoped, and its result is used only
|
|
308
|
+
* for this decision.
|
|
309
|
+
*/
|
|
310
|
+
async function checkKeyIsFree(runner, metadata, key, tenantId) {
|
|
311
|
+
const quote = (name) => "`" + name.replace(/`/g, "``") + "`";
|
|
312
|
+
const tenantColumn = tenantColumnIn(metadata);
|
|
313
|
+
const table = metadata.tablePath.split(".").map(quote).join(".");
|
|
314
|
+
const where = [...key.keys()].map((column) => `${quote(column)} = ?`).join(" AND ");
|
|
315
|
+
const rows = (await runner.query(`SELECT ${quote(tenantColumn.databaseName)} AS tenant_id FROM ${table} WHERE ${where} LIMIT 1`, [...key.values()]));
|
|
316
|
+
if (rows.length > 0 && rows[0].tenant_id !== tenantId) {
|
|
317
|
+
throw new Error("Stratum: insert refused, because a row with this primary key belongs to another tenant.");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Prepares an insert builder whose data source has the subscriber.
|
|
322
|
+
*
|
|
323
|
+
* - INSERT ... SELECT into a tenant entity is refused, because the tenant of
|
|
324
|
+
* the selected rows cannot be set. Into other tables, the tenant parameter
|
|
325
|
+
* of a scoped SELECT is copied to the insert.
|
|
326
|
+
* - Each value of a tenant entity gets the current tenant, whether or not
|
|
327
|
+
* listeners (beforeInsert) run.
|
|
328
|
+
* - A plain insert whose value supplies its whole primary key is refused when
|
|
329
|
+
* a row with that key belongs to another tenant. The lookup is raw SQL, so it
|
|
330
|
+
* is not tenant-scoped, and its result is used only for this decision.
|
|
331
|
+
* Upserts are checked in beforeQuery against the table's unique keys.
|
|
332
|
+
*/
|
|
333
|
+
async function prepareInsert(builder) {
|
|
334
|
+
if (!builder.connection.subscribers.some((s) => s instanceof StratumTypeOrmSubscriber))
|
|
335
|
+
return [];
|
|
336
|
+
const map = builder.expressionMap;
|
|
337
|
+
const metadata = map.mainAlias?.hasMetadata ? map.mainAlias.metadata : undefined;
|
|
338
|
+
const tenantColumn = tenantColumnIn(metadata);
|
|
339
|
+
if (map.insertFromSelect) {
|
|
340
|
+
if (tenantColumn) {
|
|
341
|
+
throw new Error("Stratum: INSERT ... SELECT into a tenant table is refused, because the tenant of the selected rows " +
|
|
342
|
+
"cannot be set. Load the rows and insert them as entities instead.");
|
|
343
|
+
}
|
|
344
|
+
if (map.insertFromSelect.getQuery().includes(`:${TENANT_PARAMETER}`)) {
|
|
345
|
+
const context = (0, sdk_1.getTenantContext)();
|
|
346
|
+
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
347
|
+
builder.setParameter(TENANT_PARAMETER, context.tenant_id);
|
|
348
|
+
}
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
if (!metadata || !tenantColumn)
|
|
352
|
+
return [];
|
|
353
|
+
const context = (0, sdk_1.getTenantContext)();
|
|
354
|
+
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
355
|
+
const values = (Array.isArray(map.valuesSet) ? map.valuesSet : [map.valuesSet]).filter((value) => !!value && typeof value === "object");
|
|
356
|
+
const runner = builder.queryRunner ?? builder.connection;
|
|
357
|
+
for (const value of values) {
|
|
358
|
+
if (map.onUpdate || map.onIgnore) {
|
|
359
|
+
upsertValues.add(value);
|
|
360
|
+
}
|
|
361
|
+
else if (!keyChecked.has(value)) {
|
|
362
|
+
keyChecked.add(value);
|
|
363
|
+
const key = primaryKeyOf(metadata, value);
|
|
364
|
+
if (key)
|
|
365
|
+
await checkKeyIsFree(runner, metadata, key, context.tenant_id);
|
|
366
|
+
}
|
|
367
|
+
setTenant(value, metadata, context.tenant_id);
|
|
368
|
+
}
|
|
369
|
+
return values;
|
|
370
|
+
}
|
|
371
|
+
/** Returns the tenant_id column of an entity alias, or undefined when it has none. */
|
|
372
|
+
function tenantColumnOf(alias) {
|
|
373
|
+
if (!alias || alias.subQuery || !alias.hasMetadata)
|
|
374
|
+
return undefined;
|
|
375
|
+
return alias.metadata.columns.find((c) => c.databaseName.toLowerCase() === "tenant_id")?.databaseName;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Builds the SQL of a select query builder whose data source has the
|
|
379
|
+
* subscriber, with `tenant_id = <current tenant>` ANDed to the WHERE clause for
|
|
380
|
+
* the FROM entity and to the ON condition of each joined entity that has a
|
|
381
|
+
* tenant_id column. The conditions are added only while the SQL is built and
|
|
382
|
+
* removed afterwards, so a builder that is built again, cloned, or used as a
|
|
383
|
+
* subquery never carries the condition twice.
|
|
384
|
+
*/
|
|
385
|
+
function buildScopedSelect(builder, build) {
|
|
386
|
+
if (!builder.connection.subscribers.some((s) => s instanceof StratumTypeOrmSubscriber))
|
|
387
|
+
return build();
|
|
388
|
+
// A view's definition is a schema object, built by synchronize() outside any
|
|
389
|
+
// tenant context. Reads from the view entity are scoped like any entity.
|
|
390
|
+
if (buildingViewExpression.getStore() || viewExpressionBuilders.has(builder))
|
|
391
|
+
return build();
|
|
392
|
+
const map = builder.expressionMap;
|
|
393
|
+
const condition = (alias, column) => `${builder.escape(alias.name)}.${builder.escape(column)} = :${TENANT_PARAMETER}`;
|
|
394
|
+
const mainColumn = tenantColumnOf(map.mainAlias);
|
|
395
|
+
const joins = map.joinAttributes
|
|
396
|
+
.map((join) => ({ join, column: tenantColumnOf(join.alias) }))
|
|
397
|
+
.filter((entry) => entry.column !== undefined);
|
|
398
|
+
const setTenantParameter = () => {
|
|
399
|
+
const context = (0, sdk_1.getTenantContext)();
|
|
400
|
+
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
401
|
+
builder.setParameter(TENANT_PARAMETER, context.tenant_id);
|
|
402
|
+
};
|
|
403
|
+
if (mainColumn === undefined && joins.length === 0) {
|
|
404
|
+
const sql = build();
|
|
405
|
+
// SQL built by another scoped builder can be embedded here, for example
|
|
406
|
+
// TypeORM's pagination query, which selects from a clone of the builder.
|
|
407
|
+
if (sql.includes(`:${TENANT_PARAMETER}`))
|
|
408
|
+
setTenantParameter();
|
|
409
|
+
return sql;
|
|
410
|
+
}
|
|
411
|
+
setTenantParameter();
|
|
412
|
+
const extra = map.extraAppendedAndWhereCondition;
|
|
413
|
+
const joinConditions = joins.map(({ join }) => join.condition);
|
|
414
|
+
try {
|
|
415
|
+
if (mainColumn !== undefined) {
|
|
416
|
+
const main = condition(map.mainAlias, mainColumn);
|
|
417
|
+
// TypeORM ANDs this condition, in its own parentheses, to the caller's
|
|
418
|
+
// WHERE clause, so an orWhere() cannot widen the read past the tenant.
|
|
419
|
+
map.extraAppendedAndWhereCondition = extra ? `(${extra}) AND ${main}` : main;
|
|
420
|
+
}
|
|
421
|
+
for (const { join, column } of joins) {
|
|
422
|
+
// In the ON condition rather than the WHERE clause, so a LEFT JOIN keeps
|
|
423
|
+
// the parent row and only drops the other tenant's joined row.
|
|
424
|
+
const own = condition(join.alias, column);
|
|
425
|
+
join.condition = join.condition ? `(${join.condition}) AND ${own}` : own;
|
|
426
|
+
}
|
|
427
|
+
return build();
|
|
428
|
+
}
|
|
429
|
+
finally {
|
|
430
|
+
map.extraAppendedAndWhereCondition = extra;
|
|
431
|
+
joins.forEach(({ join }, i) => {
|
|
432
|
+
join.condition = joinConditions[i];
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Wraps getQuery() of TypeORM's select query builder class, once. Every read
|
|
438
|
+
* the builder runs (loadRawResults, stream, the count, exists and pagination
|
|
439
|
+
* queries, and subqueries) builds its SQL through getQuery().
|
|
440
|
+
*/
|
|
441
|
+
function scopeSelectBuilder(dataSource) {
|
|
442
|
+
const proto = Object.getPrototypeOf(dataSource.createQueryBuilder());
|
|
443
|
+
if (proto[SCOPED_GET_QUERY])
|
|
444
|
+
return;
|
|
445
|
+
if (!Object.prototype.hasOwnProperty.call(proto, "getQuery") || typeof proto.getQuery !== "function") {
|
|
446
|
+
throw new Error("Stratum: this TypeORM version's select query builder is not supported, so reads cannot be tenant-scoped.");
|
|
447
|
+
}
|
|
448
|
+
const original = proto.getQuery;
|
|
449
|
+
proto.getQuery = function () {
|
|
450
|
+
return buildScopedSelect(this, () => original.call(this));
|
|
451
|
+
};
|
|
452
|
+
proto[SCOPED_GET_QUERY] = true;
|
|
453
|
+
}
|
|
454
|
+
/** Wraps execute() of TypeORM's insert query builder class, once (see prepareInsert). */
|
|
455
|
+
function scopeInsertBuilder(dataSource) {
|
|
456
|
+
const proto = Object.getPrototypeOf(dataSource.createQueryBuilder().insert());
|
|
457
|
+
if (proto[SCOPED_INSERT])
|
|
458
|
+
return;
|
|
459
|
+
const original = proto.execute;
|
|
460
|
+
proto.execute = async function () {
|
|
461
|
+
const values = await prepareInsert(this);
|
|
462
|
+
try {
|
|
463
|
+
return await original.call(this);
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
// An upsert's values are exempt from the key check only while it runs.
|
|
467
|
+
for (const value of values)
|
|
468
|
+
upsertValues.delete(value);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
proto[SCOPED_INSERT] = true;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Marks the query builders that view entity expressions return, so that
|
|
475
|
+
* synchronize() can build the view definition without the read scope.
|
|
476
|
+
*/
|
|
477
|
+
function exemptViewExpressions(dataSource) {
|
|
478
|
+
for (const metadata of dataSource.entityMetadatas ?? []) {
|
|
479
|
+
const expression = metadata.expression;
|
|
480
|
+
if (typeof expression !== "function" || expression[EXEMPT_VIEW_EXPRESSION])
|
|
481
|
+
continue;
|
|
482
|
+
const exempt = Object.assign((source) => buildingViewExpression.run(true, () => {
|
|
483
|
+
const builder = expression(source);
|
|
484
|
+
viewExpressionBuilders.add(builder);
|
|
485
|
+
return builder;
|
|
486
|
+
}), { [EXEMPT_VIEW_EXPRESSION]: true });
|
|
487
|
+
metadata.expression = exempt;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Refuses a TRUNCATE (Repository.clear(), QueryRunner.clearTable()) of a table
|
|
492
|
+
* with a tenant_id column, because it would empty the table for every tenant.
|
|
493
|
+
* A TRUNCATE whose table cannot be checked is refused too.
|
|
494
|
+
*/
|
|
495
|
+
async function assertNotTenantTable(event) {
|
|
496
|
+
const target = TRUNCATE_TARGET.exec(event.query);
|
|
497
|
+
const unquote = (name) => (name === undefined ? null : name.replace(/``/g, "`"));
|
|
498
|
+
const table = target ? unquote(target[2]) : null;
|
|
499
|
+
let isTenantTable = true;
|
|
500
|
+
if (table && event.queryRunner) {
|
|
501
|
+
const rows = (await event.queryRunner.query(TENANT_COLUMN_SQL, [unquote(target?.[1]), table]));
|
|
502
|
+
isTenantTable = Number(rows[0]?.n ?? 1) > 0;
|
|
503
|
+
}
|
|
504
|
+
if (isTenantTable) {
|
|
505
|
+
throw new Error(`Stratum: TRUNCATE of "${table ?? "unknown table"}" is refused, because it would remove every tenant's rows. ` +
|
|
506
|
+
"Delete the current tenant's rows instead.");
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function assertUniqueKeysIncludeTenant(event) {
|
|
510
|
+
const target = INSERT_TARGET.exec(event.query);
|
|
511
|
+
if (!target || !event.queryRunner) {
|
|
512
|
+
throw new Error("Stratum: an upsert is refused because its table's unique keys cannot be checked for tenant_id.");
|
|
513
|
+
}
|
|
514
|
+
const unquote = (name) => (name === undefined ? null : name.replace(/``/g, "`"));
|
|
515
|
+
const table = unquote(target[2]);
|
|
516
|
+
const rows = (await event.queryRunner.query(UNIQUE_KEYS_SQL, [unquote(target[1]), table]));
|
|
517
|
+
const keys = new Map();
|
|
518
|
+
for (const row of rows) {
|
|
519
|
+
const hasTenant = row.column_name.toLowerCase() === "tenant_id";
|
|
520
|
+
keys.set(row.index_name, (keys.get(row.index_name) ?? false) || hasTenant);
|
|
521
|
+
}
|
|
522
|
+
for (const [key, hasTenant] of keys) {
|
|
523
|
+
if (!hasTenant) {
|
|
524
|
+
throw new Error(`Stratum: an upsert on "${table}" is refused, because its unique key "${key}" does not include tenant_id, ` +
|
|
525
|
+
"so a conflict could update another tenant's row. Add tenant_id to every unique key of the table, " +
|
|
526
|
+
"or look the row up by tenant before writing it.");
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
74
530
|
/**
|
|
75
531
|
* Returns the one StratumTypeOrmSubscriber on the data source, and adds it first when it is missing.
|
|
76
532
|
*
|
|
77
533
|
* A second call returns the subscriber that the first call added, so the
|
|
78
534
|
* data source never runs the subscriber twice.
|
|
79
535
|
*
|
|
536
|
+
* It also scopes TypeORM's select, update, delete and soft-delete query
|
|
537
|
+
* builders to the current tenant (see StratumTypeOrmSubscriber). The subscriber refuses an
|
|
538
|
+
* UPDATE or DELETE on a data source that was not registered this way.
|
|
539
|
+
*
|
|
80
540
|
* @param dataSource - An initialized TypeORM `DataSource`.
|
|
81
541
|
* @throws Error when the data source is not initialized. `initialize()` replaces
|
|
82
542
|
* the subscriber list, so an earlier registration has no effect.
|
|
@@ -86,6 +546,10 @@ function registerStratumSubscriber(dataSource) {
|
|
|
86
546
|
throw new Error("Stratum: call registerStratumSubscriber() after dataSource.initialize(). " +
|
|
87
547
|
"initialize() replaces the subscriber list.");
|
|
88
548
|
}
|
|
549
|
+
scopeSelectBuilder(dataSource);
|
|
550
|
+
scopeWriteBuilders(dataSource);
|
|
551
|
+
scopeInsertBuilder(dataSource);
|
|
552
|
+
exemptViewExpressions(dataSource);
|
|
89
553
|
const existing = dataSource.subscribers.find((subscriber) => subscriber instanceof StratumTypeOrmSubscriber);
|
|
90
554
|
if (existing)
|
|
91
555
|
return existing;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeorm-subscriber.js","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":";;;AAsHA,8DAkBC;AAxID,yCAAmD;AACnD,0CAA6C;AAiC7C,8EAA8E;AAC9E,qCAAqC;AACrC,MAAM,aAAa,GAAG,oCAAoC,CAAC;AAC3D,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,MAAa,wBAAwB;IACnC,qEAAqE;IACrE,YAAY,CAAC,KAAkB;QAC7B,MAAM,OAAO,GAAG,IAAA,sBAAgB,GAAE,CAAC;QACnC,IAAA,yBAAc,EAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAChD,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,KAAkB;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC,CACvE,CAAC;QACF,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YACnD,IAAI,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,WAAW,IAAI,MAAM,CAAC,YAAY,IAAI,MAAM,EAAE,CAAC;gBACvF,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;YACpC,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,WAAW,CAAC,KAAuB;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,MAAM,KAAK,SAAS,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CACb,4DAA4D;gBAC1D,yEAAyE,CAC5E,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAvDD,4DAuDC;AAED;;;;;;;;;GASG;AACH,SAAgB,yBAAyB,CACvC,UAAiC;IAEjC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,2EAA2E;YACzE,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,CAC1C,CAAC,UAAU,EAA0C,EAAE,CACrD,UAAU,YAAY,wBAAwB,CACjD,CAAC;IACF,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,UAAU,GAAG,IAAI,wBAAwB,EAAE,CAAC;IAClD,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
1
|
+
{"version":3,"file":"typeorm-subscriber.js","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -39,8 +39,9 @@ export interface MysqlTableAdapterOptions {
|
|
|
39
39
|
databaseName: string;
|
|
40
40
|
/**
|
|
41
41
|
* Every base table name that has a per-tenant copy. Required by
|
|
42
|
+
* scopedTable, which only accepts these base names, and by
|
|
42
43
|
* purgeTenantData, which drops exactly `{base}_{slug}` for each entry and
|
|
43
|
-
* nothing else.
|
|
44
|
+
* nothing else.
|
|
44
45
|
* No entry may be another entry followed by `_`, since `{base}_{slug}` would
|
|
45
46
|
* then be ambiguous between two tenants.
|
|
46
47
|
*/
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,OAAO,IAAI,IAAI,CAAC;IAChB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAID,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,QAAQ,IAAI,YAAY,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID,MAAM,WAAW,yBAAyB;IACxC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,OAAO,IAAI,IAAI,CAAC;IAChB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAID,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,QAAQ,IAAI,YAAY,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID,MAAM,WAAW,yBAAyB;IACxC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,kEAAkE;IAClE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stratum-hq/mysql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "MySQL tenant isolation for Stratum — shared table, table-per-tenant, and database-per-tenant strategies",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"multi-tenancy",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"clean": "rm -rf dist"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@stratum-hq/core": "^1.
|
|
42
|
-
"@stratum-hq/sdk": "^1.
|
|
41
|
+
"@stratum-hq/core": "^1.5.0",
|
|
42
|
+
"@stratum-hq/sdk": "^1.3.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"mysql2": "^3.0.0",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"knex": "^3.3.0",
|
|
63
63
|
"mysql2": "^3.24.4",
|
|
64
|
+
"sequelize": "^6.37.8",
|
|
64
65
|
"typeorm": "^1.1.1",
|
|
65
66
|
"typescript": "^5.9.0",
|
|
66
67
|
"vitest": "^4.1.11"
|