@getstrata/core 0.5.40 → 0.5.42
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/core/cache/simpleCache.d.ts +1 -0
- package/dist/core/cache/simpleCacheStore.d.ts +1 -0
- package/dist/core/database/baseRepository.d.ts +1 -0
- package/dist/core/queue/failedJobRepository.d.ts +1 -0
- package/dist/core/queue/failedJobService.d.ts +1 -0
- package/dist/entries/audit/exportAuditLogs.js +18 -0
- package/dist/entries/auth/sessionGuard.js +443 -0
- package/dist/entries/cache/simpleCache.js +178 -0
- package/dist/entries/cache/simpleCacheStore.js +42 -0
- package/dist/entries/database/baseRepository.js +1388 -0
- package/dist/entries/database/bindConnection.js +22 -0
- package/dist/entries/database/boundConnection.js +19 -0
- package/dist/entries/database/connection.js +12 -0
- package/dist/entries/database/errors.js +128 -0
- package/dist/entries/database/model.js +948 -0
- package/dist/entries/database/query.js +436 -0
- package/dist/entries/database/relationships.js +162 -0
- package/dist/entries/database/table.js +8 -0
- package/dist/entries/database/transaction.js +129 -0
- package/dist/entries/http/authMiddleware.js +47 -0
- package/dist/entries/http/authorizeMiddleware.js +104 -0
- package/dist/entries/http/metricsMiddleware.js +91 -0
- package/dist/entries/http/parseMultipartUpload.js +144 -0
- package/dist/entries/http/securedRouteModelBinding.js +6 -0
- package/dist/entries/http/webErrorResponse.js +443 -0
- package/dist/entries/http/webFormRequest.js +6 -0
- package/dist/entries/jobs/dispatchWebhookJob.js +18 -0
- package/dist/entries/queue/createAppQueue.js +449 -0
- package/dist/entries/queue/failedJobRepository.js +2306 -0
- package/dist/entries/queue/failedJobService.js +3 -0
- package/dist/entries/queue/publicQueue.js +449 -0
- package/dist/entries/queue/queueMetrics.js +449 -0
- package/dist/entries/queue/redisQueue.js +232 -0
- package/dist/entries/security/scimTenantTokens.js +51 -0
- package/dist/entries/tenant/tenantDatabaseScope.js +113 -0
- package/dist/entries/view.js +443 -0
- package/package.json +102 -2
|
@@ -291,6 +291,14 @@ function appendWhereParts(tableName, where, params) {
|
|
|
291
291
|
}
|
|
292
292
|
return clauses.join(" AND ");
|
|
293
293
|
}
|
|
294
|
+
function buildWhereClause(tableName, where = {}) {
|
|
295
|
+
const params = [];
|
|
296
|
+
const body = appendWhereParts(tableName, where, params);
|
|
297
|
+
return {
|
|
298
|
+
clause: body.length > 0 ? ` WHERE ${body}` : "",
|
|
299
|
+
params
|
|
300
|
+
};
|
|
301
|
+
}
|
|
294
302
|
function buildWhereNodeClause(tableName, node, params) {
|
|
295
303
|
if ("where" in node) {
|
|
296
304
|
return appendWhereParts(tableName, node.where, params);
|
|
@@ -593,6 +601,30 @@ function buildDeleteByIdQuery(table, id) {
|
|
|
593
601
|
}
|
|
594
602
|
|
|
595
603
|
// ../../src/core/database/relationships.ts
|
|
604
|
+
function hasMany(definition) {
|
|
605
|
+
return {
|
|
606
|
+
type: "hasMany",
|
|
607
|
+
...definition
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function hasOne(definition) {
|
|
611
|
+
return {
|
|
612
|
+
type: "hasOne",
|
|
613
|
+
...definition
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function belongsTo(definition) {
|
|
617
|
+
return {
|
|
618
|
+
type: "belongsTo",
|
|
619
|
+
...definition
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function belongsToMany(definition) {
|
|
623
|
+
return {
|
|
624
|
+
type: "belongsToMany",
|
|
625
|
+
...definition
|
|
626
|
+
};
|
|
627
|
+
}
|
|
596
628
|
function indexHasManyRelation(parents, children, relation) {
|
|
597
629
|
const groups = new Map;
|
|
598
630
|
for (const parent of parents) {
|
|
@@ -608,6 +640,15 @@ function indexHasManyRelation(parents, children, relation) {
|
|
|
608
640
|
}
|
|
609
641
|
return groups;
|
|
610
642
|
}
|
|
643
|
+
function indexHasOneRelation(parents, children, relation) {
|
|
644
|
+
const grouped = indexHasManyRelation(parents, children, relation);
|
|
645
|
+
const result = new Map;
|
|
646
|
+
for (const parent of parents) {
|
|
647
|
+
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
648
|
+
result.set(parent[relation.localKey], matches[0]);
|
|
649
|
+
}
|
|
650
|
+
return result;
|
|
651
|
+
}
|
|
611
652
|
function indexBelongsToRelation(children, parents, relation) {
|
|
612
653
|
const parentsById = new Map;
|
|
613
654
|
for (const parent of parents) {
|
|
@@ -623,6 +664,45 @@ function indexBelongsToRelation(children, parents, relation) {
|
|
|
623
664
|
}
|
|
624
665
|
return result;
|
|
625
666
|
}
|
|
667
|
+
function indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation) {
|
|
668
|
+
const relatedById = new Map;
|
|
669
|
+
for (const related of relatedRows) {
|
|
670
|
+
relatedById.set(related[relation.relatedKey], related);
|
|
671
|
+
}
|
|
672
|
+
const groups = new Map;
|
|
673
|
+
for (const parent of parents) {
|
|
674
|
+
groups.set(parent[relation.parentKey], []);
|
|
675
|
+
}
|
|
676
|
+
for (const pivot of pivotRows) {
|
|
677
|
+
const parentId = pivot[relation.foreignPivotKey];
|
|
678
|
+
const relatedId = pivot[relation.relatedPivotKey];
|
|
679
|
+
const group = groups.get(parentId);
|
|
680
|
+
const related = relatedById.get(relatedId);
|
|
681
|
+
if (!group || !related) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
group.push(related);
|
|
685
|
+
}
|
|
686
|
+
return groups;
|
|
687
|
+
}
|
|
688
|
+
function morphMany(definition) {
|
|
689
|
+
return {
|
|
690
|
+
type: "morphMany",
|
|
691
|
+
...definition
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function morphOne(definition) {
|
|
695
|
+
return {
|
|
696
|
+
type: "morphOne",
|
|
697
|
+
...definition
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function morphTo(definition) {
|
|
701
|
+
return {
|
|
702
|
+
type: "morphTo",
|
|
703
|
+
...definition
|
|
704
|
+
};
|
|
705
|
+
}
|
|
626
706
|
function indexMorphManyRelation(parents, children, relation) {
|
|
627
707
|
const groups = new Map;
|
|
628
708
|
for (const parent of parents) {
|
|
@@ -641,6 +721,15 @@ function indexMorphManyRelation(parents, children, relation) {
|
|
|
641
721
|
}
|
|
642
722
|
return groups;
|
|
643
723
|
}
|
|
724
|
+
function indexMorphOneRelation(parents, children, relation) {
|
|
725
|
+
const grouped = indexMorphManyRelation(parents, children, relation);
|
|
726
|
+
const result = new Map;
|
|
727
|
+
for (const parent of parents) {
|
|
728
|
+
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
729
|
+
result.set(parent[relation.localKey], matches[0]);
|
|
730
|
+
}
|
|
731
|
+
return result;
|
|
732
|
+
}
|
|
644
733
|
function indexMorphToRelation(children, parentsByType, relation) {
|
|
645
734
|
const result = new Map;
|
|
646
735
|
for (const child of children) {
|
|
@@ -661,9 +750,15 @@ function indexMorphToRelation(children, parentsByType, relation) {
|
|
|
661
750
|
var boundConnectionHolder = {
|
|
662
751
|
connection: null
|
|
663
752
|
};
|
|
753
|
+
function bindDatabaseConnection(connection) {
|
|
754
|
+
boundConnectionHolder.connection = connection;
|
|
755
|
+
}
|
|
664
756
|
function getBoundDatabaseConnection() {
|
|
665
757
|
return boundConnectionHolder.connection;
|
|
666
758
|
}
|
|
759
|
+
function resetBoundDatabaseConnection() {
|
|
760
|
+
boundConnectionHolder.connection = null;
|
|
761
|
+
}
|
|
667
762
|
|
|
668
763
|
// ../../src/core/runtime/asyncContextStore.ts
|
|
669
764
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -681,9 +776,15 @@ function createAsyncContextStore(key) {
|
|
|
681
776
|
|
|
682
777
|
// ../../src/core/database/connectionContext.ts
|
|
683
778
|
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
779
|
+
function runWithDatabaseConnection(connection, callback) {
|
|
780
|
+
return activeConnection.run(connection, callback);
|
|
781
|
+
}
|
|
684
782
|
function getActiveDatabaseConnection(fallback) {
|
|
685
783
|
return activeConnection.getStore() ?? fallback;
|
|
686
784
|
}
|
|
785
|
+
function hasActiveDatabaseConnection() {
|
|
786
|
+
return activeConnection.getStore() !== undefined;
|
|
787
|
+
}
|
|
687
788
|
|
|
688
789
|
// ../../src/core/database/queryProxy.ts
|
|
689
790
|
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
@@ -720,6 +821,12 @@ function registerDefaultDatabasePool(connection) {
|
|
|
720
821
|
defaultPool.connection = connection;
|
|
721
822
|
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
722
823
|
}
|
|
824
|
+
function getDefaultDatabasePool() {
|
|
825
|
+
if (!defaultPool.connection) {
|
|
826
|
+
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
827
|
+
}
|
|
828
|
+
return defaultPool.connection;
|
|
829
|
+
}
|
|
723
830
|
function getDefaultDatabaseQuery() {
|
|
724
831
|
if (!defaultQuery.connection) {
|
|
725
832
|
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
@@ -1297,10 +1404,327 @@ class BaseRepository {
|
|
|
1297
1404
|
}
|
|
1298
1405
|
}
|
|
1299
1406
|
var baseRepository_default = BaseRepository;
|
|
1407
|
+
// ../../src/core/database/connection.ts
|
|
1408
|
+
function createDatabaseConnection(source) {
|
|
1409
|
+
return {
|
|
1410
|
+
async unsafe(query, params = []) {
|
|
1411
|
+
return await source.unsafe(query, params);
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1300
1415
|
// ../../src/core/database/model.ts
|
|
1301
1416
|
var modelRepositories = new WeakMap;
|
|
1302
1417
|
var modelGlobalScopes = new WeakMap;
|
|
1303
1418
|
var modelBooted = new WeakSet;
|
|
1419
|
+
function resolveModelRepository(model) {
|
|
1420
|
+
const repository = modelRepositories.get(model);
|
|
1421
|
+
if (!repository) {
|
|
1422
|
+
throw new Error(`${model.name}.repository() is not implemented.`);
|
|
1423
|
+
}
|
|
1424
|
+
return repository;
|
|
1425
|
+
}
|
|
1426
|
+
function modelStatics(model) {
|
|
1427
|
+
return model;
|
|
1428
|
+
}
|
|
1429
|
+
function ensureBooted(model) {
|
|
1430
|
+
if (modelBooted.has(model)) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
modelBooted.add(model);
|
|
1434
|
+
const boot = model.boot;
|
|
1435
|
+
if (typeof boot === "function") {
|
|
1436
|
+
boot.call(model);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
function getGlobalScopes(model) {
|
|
1440
|
+
return modelGlobalScopes.get(model) ?? [];
|
|
1441
|
+
}
|
|
1442
|
+
function hydrateValue(value, cast) {
|
|
1443
|
+
if (value === null || value === undefined) {
|
|
1444
|
+
return value;
|
|
1445
|
+
}
|
|
1446
|
+
switch (cast) {
|
|
1447
|
+
case "date":
|
|
1448
|
+
case "datetime":
|
|
1449
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
1450
|
+
case "json":
|
|
1451
|
+
return typeof value === "string" ? JSON.parse(value) : value;
|
|
1452
|
+
case "bool":
|
|
1453
|
+
case "boolean":
|
|
1454
|
+
return value === true || value === 1 || value === "1" || value === "true";
|
|
1455
|
+
default:
|
|
1456
|
+
return value;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
function dehydrateValue(value, cast) {
|
|
1460
|
+
if (value === null || value === undefined) {
|
|
1461
|
+
return value;
|
|
1462
|
+
}
|
|
1463
|
+
switch (cast) {
|
|
1464
|
+
case "date":
|
|
1465
|
+
case "datetime":
|
|
1466
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
1467
|
+
case "json":
|
|
1468
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
1469
|
+
case "bool":
|
|
1470
|
+
case "boolean":
|
|
1471
|
+
return Boolean(value);
|
|
1472
|
+
default:
|
|
1473
|
+
return value;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
function filterMassAssignable(fillable, guarded, input) {
|
|
1477
|
+
const resolvedGuarded = guarded ?? true;
|
|
1478
|
+
if (fillable && fillable.length > 0) {
|
|
1479
|
+
const allowed = new Set(fillable);
|
|
1480
|
+
return Object.fromEntries(Object.entries(input).filter(([key]) => allowed.has(key)));
|
|
1481
|
+
}
|
|
1482
|
+
if (resolvedGuarded === true || resolvedGuarded.includes("*")) {
|
|
1483
|
+
return {};
|
|
1484
|
+
}
|
|
1485
|
+
const blocked = new Set(resolvedGuarded);
|
|
1486
|
+
return Object.fromEntries(Object.entries(input).filter(([key]) => !blocked.has(key)));
|
|
1487
|
+
}
|
|
1488
|
+
function applyCasts(values, casts, direction) {
|
|
1489
|
+
if (Object.keys(casts).length === 0) {
|
|
1490
|
+
return values;
|
|
1491
|
+
}
|
|
1492
|
+
const result = { ...values };
|
|
1493
|
+
const castFn = direction === "hydrate" ? hydrateValue : dehydrateValue;
|
|
1494
|
+
for (const [key, cast] of Object.entries(casts)) {
|
|
1495
|
+
if (key in result && cast) {
|
|
1496
|
+
result[key] = castFn(result[key], cast);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return result;
|
|
1500
|
+
}
|
|
1501
|
+
function applyTimestampsOnCreate(columns, values, enabled) {
|
|
1502
|
+
if (!enabled) {
|
|
1503
|
+
return values;
|
|
1504
|
+
}
|
|
1505
|
+
const now = new Date;
|
|
1506
|
+
const result = { ...values };
|
|
1507
|
+
if (columns.includes("created_at")) {
|
|
1508
|
+
result.created_at = now;
|
|
1509
|
+
}
|
|
1510
|
+
if (columns.includes("updated_at")) {
|
|
1511
|
+
result.updated_at = now;
|
|
1512
|
+
}
|
|
1513
|
+
return result;
|
|
1514
|
+
}
|
|
1515
|
+
function applyTimestampsOnUpdate(columns, values, enabled) {
|
|
1516
|
+
if (!enabled) {
|
|
1517
|
+
return values;
|
|
1518
|
+
}
|
|
1519
|
+
const result = { ...values };
|
|
1520
|
+
if (columns.includes("updated_at")) {
|
|
1521
|
+
result.updated_at = new Date;
|
|
1522
|
+
}
|
|
1523
|
+
return result;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
class Model {
|
|
1527
|
+
attributes;
|
|
1528
|
+
repository;
|
|
1529
|
+
static $fillable;
|
|
1530
|
+
static $guarded;
|
|
1531
|
+
static $casts = {};
|
|
1532
|
+
static $timestamps = true;
|
|
1533
|
+
_exists;
|
|
1534
|
+
constructor(attributes, repository, exists = true) {
|
|
1535
|
+
this.attributes = attributes;
|
|
1536
|
+
this.repository = repository;
|
|
1537
|
+
this._exists = exists;
|
|
1538
|
+
}
|
|
1539
|
+
get $exists() {
|
|
1540
|
+
return this._exists;
|
|
1541
|
+
}
|
|
1542
|
+
get(key) {
|
|
1543
|
+
return this.attributes[key];
|
|
1544
|
+
}
|
|
1545
|
+
get id() {
|
|
1546
|
+
return this.attributes[this.primaryKey()];
|
|
1547
|
+
}
|
|
1548
|
+
toObject() {
|
|
1549
|
+
return { ...this.attributes };
|
|
1550
|
+
}
|
|
1551
|
+
primaryKey() {
|
|
1552
|
+
throw new Error(`${this.constructor.name}.primaryKey() is not implemented.`);
|
|
1553
|
+
}
|
|
1554
|
+
static primaryKeyField() {
|
|
1555
|
+
return resolveModelRepository(this).getTable().primaryKey;
|
|
1556
|
+
}
|
|
1557
|
+
static hydrateAttributes(attributes) {
|
|
1558
|
+
const casts = modelStatics(this).$casts ?? {};
|
|
1559
|
+
return applyCasts(attributes, casts, "hydrate");
|
|
1560
|
+
}
|
|
1561
|
+
static dehydrateAttributes(attributes) {
|
|
1562
|
+
const casts = modelStatics(this).$casts ?? {};
|
|
1563
|
+
return applyCasts(attributes, casts, "dehydrate");
|
|
1564
|
+
}
|
|
1565
|
+
static fromRecord(record, repository, exists = true) {
|
|
1566
|
+
const statics = modelStatics(this);
|
|
1567
|
+
const hydrated = statics.hydrateAttributes(record);
|
|
1568
|
+
return new statics(hydrated, repository, exists);
|
|
1569
|
+
}
|
|
1570
|
+
static boot() {}
|
|
1571
|
+
static addGlobalScope(_name, scope) {
|
|
1572
|
+
ensureBooted(this);
|
|
1573
|
+
const existing = modelGlobalScopes.get(this) ?? [];
|
|
1574
|
+
modelGlobalScopes.set(this, [
|
|
1575
|
+
...existing,
|
|
1576
|
+
scope
|
|
1577
|
+
]);
|
|
1578
|
+
}
|
|
1579
|
+
static repository() {
|
|
1580
|
+
return resolveModelRepository(this);
|
|
1581
|
+
}
|
|
1582
|
+
static query() {
|
|
1583
|
+
ensureBooted(this);
|
|
1584
|
+
const repository = resolveModelRepository(this);
|
|
1585
|
+
let query = repository.query();
|
|
1586
|
+
for (const scope of getGlobalScopes(this)) {
|
|
1587
|
+
query = scope(query);
|
|
1588
|
+
}
|
|
1589
|
+
return query;
|
|
1590
|
+
}
|
|
1591
|
+
static async create(attributes) {
|
|
1592
|
+
const statics = modelStatics(this);
|
|
1593
|
+
ensureBooted(this);
|
|
1594
|
+
const repository = resolveModelRepository(this);
|
|
1595
|
+
const table = repository.getTable();
|
|
1596
|
+
const timestamps = statics.$timestamps ?? true;
|
|
1597
|
+
const assignable = filterMassAssignable(statics.$fillable, statics.$guarded, attributes);
|
|
1598
|
+
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1599
|
+
const payload = statics.dehydrateAttributes(withTimestamps);
|
|
1600
|
+
const record = await repository.create(payload);
|
|
1601
|
+
return statics.fromRecord(record, repository, true);
|
|
1602
|
+
}
|
|
1603
|
+
static async find(id) {
|
|
1604
|
+
const statics = modelStatics(this);
|
|
1605
|
+
const repository = resolveModelRepository(this);
|
|
1606
|
+
const primaryKey = repository.getTable().primaryKey;
|
|
1607
|
+
const record = await Model.query.call(this).where({ [primaryKey]: id }).first();
|
|
1608
|
+
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1609
|
+
}
|
|
1610
|
+
static async findOrFail(id, errorFactory) {
|
|
1611
|
+
const model = await Model.find.call(this, id);
|
|
1612
|
+
if (model) {
|
|
1613
|
+
return model;
|
|
1614
|
+
}
|
|
1615
|
+
throw errorFactory?.(id) ?? new NotFoundError(`${this.name} ${String(id)} not found.`);
|
|
1616
|
+
}
|
|
1617
|
+
static async all(options = {}) {
|
|
1618
|
+
const statics = modelStatics(this);
|
|
1619
|
+
const repository = resolveModelRepository(this);
|
|
1620
|
+
let query = Model.query.call(this);
|
|
1621
|
+
if (options.orderBy) {
|
|
1622
|
+
query = query.orderBy(options.orderBy);
|
|
1623
|
+
}
|
|
1624
|
+
if (options.limit !== undefined) {
|
|
1625
|
+
query = query.limit(options.limit);
|
|
1626
|
+
}
|
|
1627
|
+
const rows = await query.get();
|
|
1628
|
+
return rows.map((row) => statics.fromRecord(row, repository, true));
|
|
1629
|
+
}
|
|
1630
|
+
static async firstWhere(where, options = {}) {
|
|
1631
|
+
const statics = modelStatics(this);
|
|
1632
|
+
const repository = resolveModelRepository(this);
|
|
1633
|
+
let query = Model.query.call(this).where(where);
|
|
1634
|
+
if (options.orderBy) {
|
|
1635
|
+
query = query.orderBy(options.orderBy);
|
|
1636
|
+
}
|
|
1637
|
+
const record = await query.first();
|
|
1638
|
+
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1639
|
+
}
|
|
1640
|
+
async save() {
|
|
1641
|
+
const ModelClass = modelStatics(this.constructor);
|
|
1642
|
+
const timestamps = ModelClass.$timestamps ?? true;
|
|
1643
|
+
const casts = ModelClass.$casts ?? {};
|
|
1644
|
+
const table = this.repository.getTable();
|
|
1645
|
+
if (this.$exists) {
|
|
1646
|
+
const changes = applyTimestampsOnUpdate(table.columns, applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
1647
|
+
const record2 = await this.repository.updateByIdOrThrow(this.id, changes);
|
|
1648
|
+
this.attributes = ModelClass.hydrateAttributes(record2);
|
|
1649
|
+
return this;
|
|
1650
|
+
}
|
|
1651
|
+
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
|
|
1652
|
+
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1653
|
+
const payload = ModelClass.dehydrateAttributes(withTimestamps);
|
|
1654
|
+
const record = await this.repository.create(payload);
|
|
1655
|
+
this.attributes = ModelClass.hydrateAttributes(record);
|
|
1656
|
+
this._exists = true;
|
|
1657
|
+
return this;
|
|
1658
|
+
}
|
|
1659
|
+
async update(changes) {
|
|
1660
|
+
const ModelClass = modelStatics(this.constructor);
|
|
1661
|
+
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, changes);
|
|
1662
|
+
Object.assign(this.attributes, assignable);
|
|
1663
|
+
return await this.save();
|
|
1664
|
+
}
|
|
1665
|
+
async delete() {
|
|
1666
|
+
if (resolveSoftDeleteColumn(this.repository.getTable())) {
|
|
1667
|
+
return await this.repository.deleteById(this.id);
|
|
1668
|
+
}
|
|
1669
|
+
return await this.repository.forceDeleteById(this.id);
|
|
1670
|
+
}
|
|
1671
|
+
async forceDelete() {
|
|
1672
|
+
return await this.repository.forceDeleteById(this.id);
|
|
1673
|
+
}
|
|
1674
|
+
async restore() {
|
|
1675
|
+
const ModelClass = modelStatics(this.constructor);
|
|
1676
|
+
const record = await this.repository.restoreById(this.id);
|
|
1677
|
+
if (!record) {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
this.attributes = ModelClass.hydrateAttributes(record);
|
|
1681
|
+
return this;
|
|
1682
|
+
}
|
|
1683
|
+
async loadHasMany(as, relation, childRepository, options = {}) {
|
|
1684
|
+
const grouped = await childRepository.withConnection(this.repository.getConnection()).loadHasManyForParents([this.attributes], relation, options);
|
|
1685
|
+
const loaded = grouped.get(this.attributes[relation.localKey]) ?? [];
|
|
1686
|
+
return Object.assign(this, { [as]: loaded });
|
|
1687
|
+
}
|
|
1688
|
+
async loadHasOne(as, relation, childRepository, options = {}) {
|
|
1689
|
+
const loaded = await this.loadHasMany(as, relation, childRepository, { ...options, limit: 1 });
|
|
1690
|
+
const value = loaded[as]?.[0];
|
|
1691
|
+
return Object.assign(this, { [as]: value });
|
|
1692
|
+
}
|
|
1693
|
+
async loadBelongsTo(as, relation, parentRepository, options = {}) {
|
|
1694
|
+
const grouped = await this.repository.loadBelongsToForParents([this.attributes], relation, parentRepository, options);
|
|
1695
|
+
const loaded = grouped.get(this.attributes[relation.foreignKey]);
|
|
1696
|
+
return Object.assign(this, { [as]: loaded });
|
|
1697
|
+
}
|
|
1698
|
+
async loadBelongsToMany(as, relation, relatedRepository, options = {}) {
|
|
1699
|
+
const connection = this.repository.getConnection();
|
|
1700
|
+
const parentId = this.attributes[relation.parentKey];
|
|
1701
|
+
const pivotRows = await connection.unsafe(`SELECT * FROM ${relation.pivotTable} WHERE ${String(relation.foreignPivotKey)} = $1`, [parentId]);
|
|
1702
|
+
if (pivotRows.length === 0) {
|
|
1703
|
+
return Object.assign(this, { [as]: [] });
|
|
1704
|
+
}
|
|
1705
|
+
const relatedIds = [
|
|
1706
|
+
...new Set(pivotRows.map((row) => row[relation.relatedPivotKey]))
|
|
1707
|
+
];
|
|
1708
|
+
const relatedRows = await relatedRepository.withConnection(connection).findAll({
|
|
1709
|
+
...options,
|
|
1710
|
+
where: {
|
|
1711
|
+
[relation.relatedKey]: relatedIds
|
|
1712
|
+
}
|
|
1713
|
+
});
|
|
1714
|
+
const grouped = indexBelongsToManyRelation([this.attributes], pivotRows, relatedRows, relation);
|
|
1715
|
+
const loaded = grouped.get(parentId) ?? [];
|
|
1716
|
+
return Object.assign(this, { [as]: loaded });
|
|
1717
|
+
}
|
|
1718
|
+
mergeAttributes(patch) {
|
|
1719
|
+
Object.assign(this.attributes, patch);
|
|
1720
|
+
return this;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
function registerModelRepository(model, repository) {
|
|
1724
|
+
modelRepositories.set(model, repository);
|
|
1725
|
+
ensureBooted(model);
|
|
1726
|
+
return model;
|
|
1727
|
+
}
|
|
1304
1728
|
// ../../src/core/database/schema/columnDefinition.ts
|
|
1305
1729
|
class ColumnDefinition {
|
|
1306
1730
|
name;
|
|
@@ -1870,6 +2294,19 @@ class SchemaBuilder {
|
|
|
1870
2294
|
function defineTable(definition) {
|
|
1871
2295
|
return definition;
|
|
1872
2296
|
}
|
|
2297
|
+
// ../../src/core/database/transaction.ts
|
|
2298
|
+
function supportsTransactions(connection) {
|
|
2299
|
+
return typeof connection.begin === "function";
|
|
2300
|
+
}
|
|
2301
|
+
async function runInTransaction(operation) {
|
|
2302
|
+
const pool = resolveRepositoryConnection();
|
|
2303
|
+
if (!supportsTransactions(pool)) {
|
|
2304
|
+
throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
|
|
2305
|
+
}
|
|
2306
|
+
return await pool.begin(async (transaction) => {
|
|
2307
|
+
return await operation(createDatabaseConnection(transaction));
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
1873
2310
|
// ../../src/core/queue/failedJobTable.ts
|
|
1874
2311
|
var failedJobTable = defineTable({
|
|
1875
2312
|
name: "failed_job",
|
|
@@ -2119,6 +2556,18 @@ class QueueWorker {
|
|
|
2119
2556
|
this.client.close();
|
|
2120
2557
|
}
|
|
2121
2558
|
}
|
|
2559
|
+
async function countPendingQueueJobs(redisUrl) {
|
|
2560
|
+
const client = new RedisClient(redisUrl);
|
|
2561
|
+
try {
|
|
2562
|
+
let total = 0;
|
|
2563
|
+
for (const queueKey of QUEUE_KEYS) {
|
|
2564
|
+
total += await client.llen(queueKey);
|
|
2565
|
+
}
|
|
2566
|
+
return total;
|
|
2567
|
+
} finally {
|
|
2568
|
+
client.close();
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2122
2571
|
|
|
2123
2572
|
// ../../src/core/queue/resilientQueue.ts
|
|
2124
2573
|
class ResilientQueue {
|