@dudousxd/nestjs-durable-store-mikro-orm 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +245 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -2
- package/dist/index.d.ts +29 -2
- package/dist/index.js +244 -50
- package/dist/index.js.map +1 -1
- package/package.json +14 -3
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,8 @@ __export(index_exports, {
|
|
|
29
29
|
MikroOrmStateStore: () => MikroOrmStateStore,
|
|
30
30
|
RunAttributeEntity: () => RunAttributeEntity,
|
|
31
31
|
RunAttributeRepository: () => RunAttributeRepository,
|
|
32
|
+
ScheduleEntity: () => ScheduleEntity,
|
|
33
|
+
ScheduleRepository: () => ScheduleRepository,
|
|
32
34
|
SignalWaiterEntity: () => SignalWaiterEntity,
|
|
33
35
|
SignalWaiterRepository: () => SignalWaiterRepository,
|
|
34
36
|
StepCheckpointEntity: () => StepCheckpointEntity,
|
|
@@ -76,6 +78,11 @@ var BufferedEventRepository = class extends import_core.EntityRepository {
|
|
|
76
78
|
__name(this, "BufferedEventRepository");
|
|
77
79
|
}
|
|
78
80
|
};
|
|
81
|
+
var ScheduleRepository = class extends import_core.EntityRepository {
|
|
82
|
+
static {
|
|
83
|
+
__name(this, "ScheduleRepository");
|
|
84
|
+
}
|
|
85
|
+
};
|
|
79
86
|
|
|
80
87
|
// src/entities.ts
|
|
81
88
|
function snakeCase(property) {
|
|
@@ -175,6 +182,24 @@ var BufferedEventEntity = class {
|
|
|
175
182
|
payload;
|
|
176
183
|
publishedAt;
|
|
177
184
|
};
|
|
185
|
+
var ScheduleEntity = class {
|
|
186
|
+
static {
|
|
187
|
+
__name(this, "ScheduleEntity");
|
|
188
|
+
}
|
|
189
|
+
[import_core2.EntityRepositoryType];
|
|
190
|
+
id;
|
|
191
|
+
namespace;
|
|
192
|
+
workflow;
|
|
193
|
+
paused;
|
|
194
|
+
/** Epoch ms as a `bigint` column — exact, so `updateSchedule`'s compare-and-set can't be defeated
|
|
195
|
+
* by a DATETIME's truncated milliseconds. Drivers may hand it back as a string. */
|
|
196
|
+
nextFireAt;
|
|
197
|
+
tags;
|
|
198
|
+
spec;
|
|
199
|
+
state;
|
|
200
|
+
createdAt;
|
|
201
|
+
updatedAt;
|
|
202
|
+
};
|
|
178
203
|
function namespaceFilterCond(args) {
|
|
179
204
|
if (args?.namespace === void 0) return {};
|
|
180
205
|
return {
|
|
@@ -574,13 +599,76 @@ function durableEntities(options = {}) {
|
|
|
574
599
|
}
|
|
575
600
|
}
|
|
576
601
|
});
|
|
602
|
+
const schedules = new import_core2.EntitySchema({
|
|
603
|
+
class: ScheduleEntity,
|
|
604
|
+
tableName: "durable_schedules",
|
|
605
|
+
repository: /* @__PURE__ */ __name(() => ScheduleRepository, "repository"),
|
|
606
|
+
// The poll's "due by T" scan: `paused = false AND next_fire_at <= T ORDER BY next_fire_at`.
|
|
607
|
+
indexes: [
|
|
608
|
+
{
|
|
609
|
+
name: "durable_schedules_due_idx",
|
|
610
|
+
properties: [
|
|
611
|
+
"paused",
|
|
612
|
+
"nextFireAt"
|
|
613
|
+
]
|
|
614
|
+
}
|
|
615
|
+
],
|
|
616
|
+
properties: {
|
|
617
|
+
id: {
|
|
618
|
+
type: "string",
|
|
619
|
+
primary: true,
|
|
620
|
+
fieldName: col("id")
|
|
621
|
+
},
|
|
622
|
+
namespace: {
|
|
623
|
+
type: "string",
|
|
624
|
+
default: "default",
|
|
625
|
+
fieldName: col("namespace")
|
|
626
|
+
},
|
|
627
|
+
workflow: {
|
|
628
|
+
type: "string",
|
|
629
|
+
fieldName: col("workflow")
|
|
630
|
+
},
|
|
631
|
+
paused: {
|
|
632
|
+
type: "boolean",
|
|
633
|
+
default: false,
|
|
634
|
+
fieldName: col("paused")
|
|
635
|
+
},
|
|
636
|
+
nextFireAt: {
|
|
637
|
+
type: "bigint",
|
|
638
|
+
nullable: true,
|
|
639
|
+
fieldName: col("nextFireAt")
|
|
640
|
+
},
|
|
641
|
+
tags: {
|
|
642
|
+
type: "json",
|
|
643
|
+
nullable: true,
|
|
644
|
+
fieldName: col("tags")
|
|
645
|
+
},
|
|
646
|
+
spec: {
|
|
647
|
+
type: "json",
|
|
648
|
+
fieldName: col("spec")
|
|
649
|
+
},
|
|
650
|
+
state: {
|
|
651
|
+
type: "json",
|
|
652
|
+
fieldName: col("state")
|
|
653
|
+
},
|
|
654
|
+
createdAt: {
|
|
655
|
+
type: "Date",
|
|
656
|
+
fieldName: col("createdAt")
|
|
657
|
+
},
|
|
658
|
+
updatedAt: {
|
|
659
|
+
type: "Date",
|
|
660
|
+
fieldName: col("updatedAt")
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
});
|
|
577
664
|
return [
|
|
578
665
|
workflowRuns,
|
|
579
666
|
stepCheckpoints,
|
|
580
667
|
runAttributes,
|
|
581
668
|
signalWaiters,
|
|
582
669
|
bufferedSignals,
|
|
583
|
-
bufferedEvents
|
|
670
|
+
bufferedEvents,
|
|
671
|
+
schedules
|
|
584
672
|
];
|
|
585
673
|
}
|
|
586
674
|
__name(durableEntities, "durableEntities");
|
|
@@ -598,7 +686,8 @@ var DURABLE_TABLE_NAMES = /* @__PURE__ */ new Set([
|
|
|
598
686
|
"durable_run_attributes",
|
|
599
687
|
"durable_signal_waiters",
|
|
600
688
|
"durable_buffered_signals",
|
|
601
|
-
"durable_buffered_events"
|
|
689
|
+
"durable_buffered_events",
|
|
690
|
+
"durable_schedules"
|
|
602
691
|
]);
|
|
603
692
|
function durableManagedTables() {
|
|
604
693
|
return [
|
|
@@ -895,71 +984,69 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
895
984
|
id: runId
|
|
896
985
|
});
|
|
897
986
|
}
|
|
987
|
+
async deleteRuns(runIds) {
|
|
988
|
+
if (runIds.length === 0) return;
|
|
989
|
+
const runId = {
|
|
990
|
+
$in: runIds
|
|
991
|
+
};
|
|
992
|
+
await this.fork().transactional(async (tem) => {
|
|
993
|
+
await tem.nativeDelete(StepCheckpointEntity, {
|
|
994
|
+
runId
|
|
995
|
+
});
|
|
996
|
+
await tem.nativeDelete(SignalWaiterEntity, {
|
|
997
|
+
runId
|
|
998
|
+
});
|
|
999
|
+
await tem.nativeDelete(RunAttributeEntity, {
|
|
1000
|
+
runId
|
|
1001
|
+
});
|
|
1002
|
+
await tem.nativeDelete(WorkflowRunEntity, {
|
|
1003
|
+
id: runId
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
898
1007
|
async pruneTerminalRuns(policy, nowMs, limit) {
|
|
899
|
-
|
|
1008
|
+
const statuses = policy.statuses.filter((s) => import_nestjs_durable_core.TERMINAL_RUN_STATUSES.includes(s));
|
|
1009
|
+
if (statuses.length === 0 || limit <= 0) return 0;
|
|
1010
|
+
if (policy.maxAge == null && policy.maxCount == null) return 0;
|
|
900
1011
|
const em = this.fork();
|
|
901
|
-
const
|
|
902
|
-
|
|
1012
|
+
const meta = em.getMetadata().get(WorkflowRunEntity);
|
|
1013
|
+
const idCol = meta.properties.id?.fieldNames?.[0] ?? "id";
|
|
1014
|
+
const query = {
|
|
1015
|
+
...policy.scope ?? {},
|
|
1016
|
+
statuses
|
|
903
1017
|
};
|
|
1018
|
+
const scoped = /* @__PURE__ */ __name((extra) => this.runQueryBuilder(em, query, {
|
|
1019
|
+
...this.runWhere(query),
|
|
1020
|
+
...extra ?? {}
|
|
1021
|
+
}).select([
|
|
1022
|
+
`r.${idCol}`
|
|
1023
|
+
]), "scoped");
|
|
904
1024
|
const ids = /* @__PURE__ */ new Set();
|
|
905
1025
|
if (policy.maxAge != null) {
|
|
906
1026
|
const cutoff = new Date(nowMs - (0, import_nestjs_durable_core.parseDuration)(policy.maxAge));
|
|
907
|
-
const rows = await
|
|
908
|
-
status,
|
|
1027
|
+
const rows = await scoped({
|
|
909
1028
|
updatedAt: {
|
|
910
1029
|
$lt: cutoff
|
|
911
1030
|
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
orderBy: {
|
|
917
|
-
updatedAt: "asc"
|
|
918
|
-
},
|
|
919
|
-
limit
|
|
920
|
-
});
|
|
921
|
-
for (const r of rows) ids.add(r.id);
|
|
1031
|
+
}).orderBy({
|
|
1032
|
+
"r.updatedAt": "asc"
|
|
1033
|
+
}).limit(limit).execute();
|
|
1034
|
+
for (const r of rows) ids.add(String(r[idCol] ?? r.id));
|
|
922
1035
|
}
|
|
923
1036
|
if (policy.maxCount != null && ids.size < limit) {
|
|
924
|
-
const rows = await
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
"id"
|
|
929
|
-
],
|
|
930
|
-
orderBy: {
|
|
931
|
-
updatedAt: "desc",
|
|
932
|
-
id: "desc"
|
|
933
|
-
},
|
|
934
|
-
limit,
|
|
935
|
-
offset: policy.maxCount
|
|
936
|
-
});
|
|
1037
|
+
const rows = await scoped().orderBy({
|
|
1038
|
+
"r.updatedAt": "desc",
|
|
1039
|
+
"r.id": "desc"
|
|
1040
|
+
}).limit(limit).offset(policy.maxCount).execute();
|
|
937
1041
|
for (const r of rows) {
|
|
938
|
-
ids.add(r.id);
|
|
939
1042
|
if (ids.size >= limit) break;
|
|
1043
|
+
ids.add(String(r[idCol] ?? r.id));
|
|
940
1044
|
}
|
|
941
1045
|
}
|
|
942
|
-
if (ids.size === 0) return 0;
|
|
943
1046
|
const idList = [
|
|
944
1047
|
...ids
|
|
945
1048
|
].slice(0, limit);
|
|
946
|
-
|
|
947
|
-
$in: idList
|
|
948
|
-
};
|
|
949
|
-
await em.transactional(async (tem) => {
|
|
950
|
-
await tem.nativeDelete(StepCheckpointEntity, {
|
|
951
|
-
runId
|
|
952
|
-
});
|
|
953
|
-
await tem.nativeDelete(SignalWaiterEntity, {
|
|
954
|
-
runId
|
|
955
|
-
});
|
|
956
|
-
await tem.nativeDelete(RunAttributeEntity, {
|
|
957
|
-
runId
|
|
958
|
-
});
|
|
959
|
-
await tem.nativeDelete(WorkflowRunEntity, {
|
|
960
|
-
id: runId
|
|
961
|
-
});
|
|
962
|
-
});
|
|
1049
|
+
await this.deleteRuns(idList);
|
|
963
1050
|
return idList.length;
|
|
964
1051
|
}
|
|
965
1052
|
async getCheckpoint(runId, seq) {
|
|
@@ -1171,6 +1258,11 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1171
1258
|
const colExpr = this.jsonAsText(em, `${quote("r")}.${quote(this.tagsColumn(em))}`);
|
|
1172
1259
|
return `${colExpr} LIKE '${pattern}'`;
|
|
1173
1260
|
}
|
|
1261
|
+
async countRuns(query) {
|
|
1262
|
+
const em = this.fork();
|
|
1263
|
+
const rows = await this.runQueryBuilder(em, query, this.runWhere(query)).select([]).addSelect((0, import_core3.raw)("count(*) as count")).execute();
|
|
1264
|
+
return Number(rows[0]?.count ?? 0);
|
|
1265
|
+
}
|
|
1174
1266
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
1175
1267
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
1176
1268
|
* count them in the browser. */
|
|
@@ -1362,6 +1454,76 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1362
1454
|
const ch = isMysql ? "`" : '"';
|
|
1363
1455
|
return (id) => `${ch}${id}${ch}`;
|
|
1364
1456
|
}
|
|
1457
|
+
// ---- persisted schedules ----------------------------------------------------------------------
|
|
1458
|
+
async saveSchedule(record) {
|
|
1459
|
+
const em = this.fork();
|
|
1460
|
+
await em.upsert(ScheduleEntity, toScheduleEntity(record));
|
|
1461
|
+
}
|
|
1462
|
+
async getSchedule(id) {
|
|
1463
|
+
const entity = await this.fork().findOne(ScheduleEntity, {
|
|
1464
|
+
id
|
|
1465
|
+
});
|
|
1466
|
+
return entity ? fromScheduleEntity(entity) : null;
|
|
1467
|
+
}
|
|
1468
|
+
/** One conditional `nativeUpdate`: the compare-and-set on `next_fire_at` (an exact bigint) is a
|
|
1469
|
+
* single statement. */
|
|
1470
|
+
async updateSchedule(id, patch, expectedNextFireAt) {
|
|
1471
|
+
const data = {};
|
|
1472
|
+
if (patch.namespace !== void 0) data.namespace = patch.namespace;
|
|
1473
|
+
if (patch.workflow !== void 0) data.workflow = patch.workflow;
|
|
1474
|
+
if (patch.paused !== void 0) data.paused = patch.paused;
|
|
1475
|
+
if ("nextFireAt" in patch) data.nextFireAt = patch.nextFireAt ?? null;
|
|
1476
|
+
if ("tags" in patch) data.tags = patch.tags ?? null;
|
|
1477
|
+
if (patch.spec !== void 0) data.spec = patch.spec;
|
|
1478
|
+
if (patch.state !== void 0) data.state = patch.state;
|
|
1479
|
+
if (patch.updatedAt !== void 0) data.updatedAt = patch.updatedAt;
|
|
1480
|
+
if (!Object.keys(data).length) return await this.getSchedule(id) !== null;
|
|
1481
|
+
const where = {
|
|
1482
|
+
id
|
|
1483
|
+
};
|
|
1484
|
+
if (expectedNextFireAt !== void 0) where.nextFireAt = expectedNextFireAt;
|
|
1485
|
+
const affected = await this.fork().nativeUpdate(ScheduleEntity, where, data);
|
|
1486
|
+
return affected > 0;
|
|
1487
|
+
}
|
|
1488
|
+
async deleteSchedule(id) {
|
|
1489
|
+
return await this.fork().nativeDelete(ScheduleEntity, {
|
|
1490
|
+
id
|
|
1491
|
+
}) > 0;
|
|
1492
|
+
}
|
|
1493
|
+
async listSchedules(query) {
|
|
1494
|
+
const em = this.fork();
|
|
1495
|
+
const where = {};
|
|
1496
|
+
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
1497
|
+
if (query.workflow !== void 0) where.workflow = query.workflow;
|
|
1498
|
+
if (query.paused !== void 0) where.paused = query.paused;
|
|
1499
|
+
if (query.dueBy !== void 0) {
|
|
1500
|
+
where.$and = [
|
|
1501
|
+
{
|
|
1502
|
+
paused: false
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
nextFireAt: {
|
|
1506
|
+
$lte: query.dueBy
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
];
|
|
1510
|
+
}
|
|
1511
|
+
const quote = this.idQuote(em);
|
|
1512
|
+
const meta = em.getMetadata().get(ScheduleEntity);
|
|
1513
|
+
const tagsCol = meta.properties.tags?.fieldNames?.[0] ?? "tags";
|
|
1514
|
+
const qb = em.createQueryBuilder(ScheduleEntity, "r").where(where);
|
|
1515
|
+
if (query.tag !== void 0) {
|
|
1516
|
+
const pattern = `%"${query.tag.replace(/'/g, "''")}"%`;
|
|
1517
|
+
qb.andWhere(`${this.jsonAsText(em, `${quote("r")}.${quote(tagsCol)}`)} LIKE '${pattern}'`);
|
|
1518
|
+
}
|
|
1519
|
+
qb.orderBy({
|
|
1520
|
+
"r.nextFireAt": "asc nulls last",
|
|
1521
|
+
"r.id": "asc"
|
|
1522
|
+
});
|
|
1523
|
+
if (query.limit != null) qb.limit(query.limit);
|
|
1524
|
+
if (query.offset != null) qb.offset(query.offset);
|
|
1525
|
+
return (await qb.getResultList()).map(fromScheduleEntity);
|
|
1526
|
+
}
|
|
1365
1527
|
async listCheckpoints(runId) {
|
|
1366
1528
|
const em = this.fork();
|
|
1367
1529
|
const rows = await em.find(StepCheckpointEntity, {
|
|
@@ -1600,6 +1762,38 @@ function fromCheckpointEntity(e) {
|
|
|
1600
1762
|
};
|
|
1601
1763
|
}
|
|
1602
1764
|
__name(fromCheckpointEntity, "fromCheckpointEntity");
|
|
1765
|
+
function toScheduleEntity(r) {
|
|
1766
|
+
const e = new ScheduleEntity();
|
|
1767
|
+
e.id = r.id;
|
|
1768
|
+
e.namespace = r.namespace;
|
|
1769
|
+
e.workflow = r.workflow;
|
|
1770
|
+
e.paused = r.paused;
|
|
1771
|
+
e.nextFireAt = r.nextFireAt;
|
|
1772
|
+
e.tags = r.tags ?? null;
|
|
1773
|
+
e.spec = r.spec;
|
|
1774
|
+
e.state = r.state;
|
|
1775
|
+
e.createdAt = r.createdAt;
|
|
1776
|
+
e.updatedAt = r.updatedAt;
|
|
1777
|
+
return e;
|
|
1778
|
+
}
|
|
1779
|
+
__name(toScheduleEntity, "toScheduleEntity");
|
|
1780
|
+
function fromScheduleEntity(e) {
|
|
1781
|
+
return {
|
|
1782
|
+
id: e.id,
|
|
1783
|
+
namespace: e.namespace,
|
|
1784
|
+
workflow: e.workflow,
|
|
1785
|
+
paused: Boolean(e.paused),
|
|
1786
|
+
nextFireAt: e.nextFireAt == null ? null : Number(e.nextFireAt),
|
|
1787
|
+
...e.tags?.length ? {
|
|
1788
|
+
tags: e.tags
|
|
1789
|
+
} : {},
|
|
1790
|
+
spec: e.spec ?? {},
|
|
1791
|
+
state: e.state ?? {},
|
|
1792
|
+
createdAt: e.createdAt,
|
|
1793
|
+
updatedAt: e.updatedAt
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
__name(fromScheduleEntity, "fromScheduleEntity");
|
|
1603
1797
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1604
1798
|
0 && (module.exports = {
|
|
1605
1799
|
BufferedEventEntity,
|
|
@@ -1610,6 +1804,8 @@ __name(fromCheckpointEntity, "fromCheckpointEntity");
|
|
|
1610
1804
|
MikroOrmStateStore,
|
|
1611
1805
|
RunAttributeEntity,
|
|
1612
1806
|
RunAttributeRepository,
|
|
1807
|
+
ScheduleEntity,
|
|
1808
|
+
ScheduleRepository,
|
|
1613
1809
|
SignalWaiterEntity,
|
|
1614
1810
|
SignalWaiterRepository,
|
|
1615
1811
|
StepCheckpointEntity,
|