@getstrata/core 2.2.0 → 2.2.2
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/CHANGELOG.md +11 -0
- package/dist/core/contracts/serviceTokens.d.ts +3 -2
- package/dist/core/database/baseRepository.d.ts +5 -0
- package/dist/core/database/model.d.ts +3 -1
- package/dist/core/database/query.d.ts +6 -1
- package/dist/core/database/repositoryQuery.d.ts +7 -1
- package/dist/core/database/schema/blueprint.d.ts +2 -0
- package/dist/core/database/schema/columnDefinition.d.ts +1 -1
- package/dist/core/database/types.d.ts +10 -1
- package/dist/core/http/requestMetaContext.d.ts +2 -0
- package/dist/core/queue/createAppQueue.d.ts +2 -1
- package/dist/core/queue/failedJobService.d.ts +3 -1
- package/dist/core/queue/index.d.ts +10 -2
- package/dist/core/queue/jobRunner.d.ts +7 -1
- package/dist/core/queue/publicQueue.d.ts +2 -1
- package/dist/core/queue/queueConfig.d.ts +3 -0
- package/dist/core/queue/redisQueue.d.ts +5 -7
- package/dist/core/queue/redisQueueKeys.d.ts +8 -0
- package/dist/core/queue/redisStreams.d.ts +73 -0
- package/dist/core/queue/types.d.ts +1 -0
- package/dist/core/tenant/publicTenantHost.d.ts +11 -0
- package/dist/core/tenant/tenantMiddleware.d.ts +4 -0
- package/dist/entries/auth/basicAuthGuard.js +1 -0
- package/dist/entries/auth/jwtGuard.js +1 -0
- package/dist/entries/auth/sessionGuard.js +1 -0
- package/dist/entries/contracts/serviceTokens.js +2 -0
- package/dist/entries/database/query.js +70 -1
- package/dist/entries/database/repositoryQuery.js +99 -1
- package/dist/entries/database/schema.js +9 -0
- package/dist/entries/database/sqliteConnection.js +1 -1
- package/dist/entries/queue/createAppQueue.js +681 -66
- package/dist/entries/queue/failedJobRepository.js +1 -1
- package/dist/entries/queue/failedJobService.js +5 -1
- package/dist/entries/queue/jobRunner.js +67 -2
- package/dist/entries/queue/publicQueue.js +679 -65
- package/dist/entries/queue/queueMetrics.js +694 -72
- package/dist/entries/queue/redisQueue.js +765 -148
- package/dist/framework/public-api.d.ts +2 -2
- package/dist/index.js +855 -69
- package/package.json +1 -1
|
@@ -355,6 +355,31 @@ function getDefinedColumnEntries(table, values, options = {}) {
|
|
|
355
355
|
return [[column, value]];
|
|
356
356
|
});
|
|
357
357
|
}
|
|
358
|
+
function buildSelectLock(table, options) {
|
|
359
|
+
const lock = options.lock;
|
|
360
|
+
if (!lock)
|
|
361
|
+
return "";
|
|
362
|
+
const driver = currentSqlDialect().driver;
|
|
363
|
+
if (driver === "sqlite")
|
|
364
|
+
throw new Error("Row locks are not supported on SQLite; use an explicit write transaction.");
|
|
365
|
+
if (lock.mode !== "update" && lock.mode !== "share")
|
|
366
|
+
throw new TypeError("Invalid row lock mode.");
|
|
367
|
+
if (lock.wait !== undefined && !["wait", "nowait", "skipLocked"].includes(lock.wait))
|
|
368
|
+
throw new TypeError("Invalid row lock wait policy.");
|
|
369
|
+
if (options.groupBy || options.having)
|
|
370
|
+
throw new Error("Row locks cannot be combined with grouped/aggregate queries.");
|
|
371
|
+
let target = "";
|
|
372
|
+
if (lock.of !== undefined) {
|
|
373
|
+
if (driver !== "pgsql")
|
|
374
|
+
throw new Error("Row lock targets are supported only on PostgreSQL.");
|
|
375
|
+
const tables = [table.name, ...(options.joins ?? []).map((join) => join.table)];
|
|
376
|
+
if (!lock.of.length || lock.of.some((name) => !tables.includes(name)))
|
|
377
|
+
throw new TypeError("Row lock targets must name tables in this query.");
|
|
378
|
+
target = ` OF ${lock.of.map(quoteIdentifier).join(", ")}`;
|
|
379
|
+
}
|
|
380
|
+
const wait = lock.wait === "nowait" ? " NOWAIT" : lock.wait === "skipLocked" ? " SKIP LOCKED" : "";
|
|
381
|
+
return ` FOR ${lock.mode === "update" ? "UPDATE" : "SHARE"}${target}${wait}`;
|
|
382
|
+
}
|
|
358
383
|
function buildSelectQuery(table, options = {}, whereNodes = []) {
|
|
359
384
|
const params = [];
|
|
360
385
|
const columns = buildSelectList(table, options.select, params);
|
|
@@ -365,8 +390,9 @@ function buildSelectQuery(table, options = {}, whereNodes = []) {
|
|
|
365
390
|
const orderBy = buildOrderByClause(table.name, options.orderBy ?? table.defaultOrderBy);
|
|
366
391
|
const limit = buildLimitClause(options.limit);
|
|
367
392
|
const offset = buildOffsetClause(options.offset);
|
|
393
|
+
const lock = buildSelectLock(table, options);
|
|
368
394
|
return {
|
|
369
|
-
text: `SELECT ${columns} FROM ${quoteIdentifier(table.name)}${joins}${clause}${groupBy}${havingClause}${orderBy}${limit}${offset}`,
|
|
395
|
+
text: `SELECT ${columns} FROM ${quoteIdentifier(table.name)}${joins}${clause}${groupBy}${havingClause}${orderBy}${limit}${offset}${lock}`,
|
|
370
396
|
params
|
|
371
397
|
};
|
|
372
398
|
}
|
|
@@ -535,6 +561,48 @@ function buildDeleteByIdQuery(table, id) {
|
|
|
535
561
|
params
|
|
536
562
|
};
|
|
537
563
|
}
|
|
564
|
+
function buildConditionalWriteQuery(table, operation, changes, options, returning) {
|
|
565
|
+
for (const key of [
|
|
566
|
+
"joins",
|
|
567
|
+
"groupBy",
|
|
568
|
+
"having",
|
|
569
|
+
"select",
|
|
570
|
+
"orderBy",
|
|
571
|
+
"limit",
|
|
572
|
+
"offset",
|
|
573
|
+
"lock"
|
|
574
|
+
]) {
|
|
575
|
+
if (options[key] !== undefined)
|
|
576
|
+
throw new Error(`Conditional writes do not support ${key}.`);
|
|
577
|
+
}
|
|
578
|
+
const params = [];
|
|
579
|
+
const predicate = buildAdvancedWhereClause(table.name, options.where ?? {}, options.whereNodes ?? []);
|
|
580
|
+
if (!predicate.clause)
|
|
581
|
+
throw new Error("Conditional writes require an explicit predicate.");
|
|
582
|
+
if (returning !== undefined && currentSqlDialect().driver === "mysql")
|
|
583
|
+
throw new Error("Mutation RETURNING is not supported on MySQL.");
|
|
584
|
+
const columns = returning ?? [table.primaryKey];
|
|
585
|
+
if (!columns.length || columns.some((column) => !table.columns.includes(column)))
|
|
586
|
+
throw new Error("RETURNING columns must be nonempty declared table columns.");
|
|
587
|
+
const deletedAt = resolveSoftDeleteColumn(table);
|
|
588
|
+
const values = operation === "delete" && deletedAt ? { [deletedAt]: new Date } : changes;
|
|
589
|
+
let prefix;
|
|
590
|
+
if (operation === "update" || deletedAt) {
|
|
591
|
+
const entries = Object.entries(values).filter(([, value]) => value !== undefined);
|
|
592
|
+
if (!entries.length)
|
|
593
|
+
throw new Error("Conditional updates require at least one value.");
|
|
594
|
+
if (entries.some(([column]) => column === table.primaryKey || !table.columns.includes(column)))
|
|
595
|
+
throw new Error("Conditional updates require declared non-primary-key columns.");
|
|
596
|
+
const assignments = entries.map(([column, value]) => `${quoteIdentifier(column)} = ${pushParam(params, value)}`);
|
|
597
|
+
prefix = `UPDATE ${quoteIdentifier(table.name)} SET ${assignments.join(", ")}`;
|
|
598
|
+
} else
|
|
599
|
+
prefix = `DELETE FROM ${quoteIdentifier(table.name)}`;
|
|
600
|
+
const { clause } = buildQueryWhereClause(table, options, options.whereNodes ?? [], params);
|
|
601
|
+
return {
|
|
602
|
+
text: prefix + clause + returningSuffix(columns.map(quoteIdentifier).join(", ")),
|
|
603
|
+
params
|
|
604
|
+
};
|
|
605
|
+
}
|
|
538
606
|
|
|
539
607
|
// ../../src/core/database/relationships.ts
|
|
540
608
|
function hasMany(definition) {
|
|
@@ -856,6 +924,20 @@ class RepositoryQuery {
|
|
|
856
924
|
this.whereNodes.push({ kind: "or", where: input });
|
|
857
925
|
return this;
|
|
858
926
|
}
|
|
927
|
+
lockForUpdate(options = {}) {
|
|
928
|
+
this.queryOptions = {
|
|
929
|
+
...this.queryOptions,
|
|
930
|
+
lock: { ...options, of: options.of ? [...options.of] : undefined, mode: "update" }
|
|
931
|
+
};
|
|
932
|
+
return this;
|
|
933
|
+
}
|
|
934
|
+
sharedLock(options = {}) {
|
|
935
|
+
this.queryOptions = {
|
|
936
|
+
...this.queryOptions,
|
|
937
|
+
lock: { ...options, of: options.of ? [...options.of] : undefined, mode: "share" }
|
|
938
|
+
};
|
|
939
|
+
return this;
|
|
940
|
+
}
|
|
859
941
|
orderBy(orderBy) {
|
|
860
942
|
this.queryOptions = { ...this.queryOptions, orderBy };
|
|
861
943
|
return this;
|
|
@@ -995,6 +1077,22 @@ class RepositoryQuery {
|
|
|
995
1077
|
this.queryOptions = { ...this.queryOptions, onlyTrashed: true };
|
|
996
1078
|
return this;
|
|
997
1079
|
}
|
|
1080
|
+
async update(changes) {
|
|
1081
|
+
this.assertNoMutationEagerLoads();
|
|
1082
|
+
return this.repository.updateWhere(changes, this.buildOptions());
|
|
1083
|
+
}
|
|
1084
|
+
async updateReturning(changes, ...columns) {
|
|
1085
|
+
this.assertNoMutationEagerLoads();
|
|
1086
|
+
return this.repository.updateWhereReturning(changes, columns, this.buildOptions());
|
|
1087
|
+
}
|
|
1088
|
+
async delete() {
|
|
1089
|
+
this.assertNoMutationEagerLoads();
|
|
1090
|
+
return this.repository.deleteWhere(this.buildOptions());
|
|
1091
|
+
}
|
|
1092
|
+
assertNoMutationEagerLoads() {
|
|
1093
|
+
if (this.eagerLoads.length)
|
|
1094
|
+
throw new Error("Conditional writes do not support eager loading.");
|
|
1095
|
+
}
|
|
998
1096
|
async get() {
|
|
999
1097
|
const rows = await this.repository.findAll(this.buildOptions());
|
|
1000
1098
|
return await this.attach(rows);
|
|
@@ -340,6 +340,13 @@ class Blueprint {
|
|
|
340
340
|
this.columns.push(column);
|
|
341
341
|
return column;
|
|
342
342
|
}
|
|
343
|
+
bigId(name = "id") {
|
|
344
|
+
const column = new ColumnDefinition(name, "bigId");
|
|
345
|
+
column.primary();
|
|
346
|
+
column.autoIncrement = true;
|
|
347
|
+
this.columns.push(column);
|
|
348
|
+
return column;
|
|
349
|
+
}
|
|
343
350
|
string(name, length) {
|
|
344
351
|
if (length !== undefined && (!Number.isSafeInteger(length) || length <= 0)) {
|
|
345
352
|
throw new Error("String length must be a positive safe integer.");
|
|
@@ -486,6 +493,8 @@ init_dialect();
|
|
|
486
493
|
// ../../src/core/database/schema/grammars/grammar.ts
|
|
487
494
|
function compileColumnType(driver, column) {
|
|
488
495
|
switch (column.kind) {
|
|
496
|
+
case "bigId":
|
|
497
|
+
return driver === "pgsql" ? "BIGSERIAL" : compileIdType(driver);
|
|
489
498
|
case "id":
|
|
490
499
|
return compileIdType(driver);
|
|
491
500
|
case "string":
|
|
@@ -69,7 +69,7 @@ function createSqliteConnection(filename) {
|
|
|
69
69
|
const transactions = new WeakSet;
|
|
70
70
|
function execute(query, params) {
|
|
71
71
|
const statement = db.query(query);
|
|
72
|
-
const args =
|
|
72
|
+
const args = params.map((value) => value instanceof Date ? value.toISOString() : value);
|
|
73
73
|
if (isRowReturning(query))
|
|
74
74
|
return statement.all(...args);
|
|
75
75
|
statement.run(...args);
|