@dudousxd/nestjs-durable-store-mikro-orm 0.26.0 → 0.27.1
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 +283 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -3
- package/dist/index.d.ts +31 -3
- package/dist/index.js +282 -51
- 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 [
|
|
@@ -845,13 +934,50 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
845
934
|
async ensureSchema() {
|
|
846
935
|
await ensureMikroOrmDurableSchema(this.orm);
|
|
847
936
|
}
|
|
937
|
+
async tryAdmitSingleton(runId, tag, workflow, limit, retryWakeAt) {
|
|
938
|
+
return this.fork().transactional(async (em) => {
|
|
939
|
+
const where = {
|
|
940
|
+
workflow,
|
|
941
|
+
status: {
|
|
942
|
+
$in: import_nestjs_durable_core.SINGLETON_ACTIVE_STATUSES
|
|
943
|
+
},
|
|
944
|
+
...this.scopeNamespace !== void 0 ? {
|
|
945
|
+
namespace: this.scopeNamespace
|
|
946
|
+
} : {}
|
|
947
|
+
};
|
|
948
|
+
await em.nativeUpdate(WorkflowRunEntity, where, {
|
|
949
|
+
workflow
|
|
950
|
+
});
|
|
951
|
+
const rows = await em.find(WorkflowRunEntity, where);
|
|
952
|
+
const runs = rows.map(fromRunEntity);
|
|
953
|
+
const tags = (0, import_nestjs_durable_core.singletonAdmissionTags)(runs, runId, tag, workflow, limit);
|
|
954
|
+
if (!tags) {
|
|
955
|
+
const patch = (0, import_nestjs_durable_core.singletonAdmissionDenialPatch)(runs, runId, tag, workflow, retryWakeAt);
|
|
956
|
+
if (patch) await em.nativeUpdate(WorkflowRunEntity, {
|
|
957
|
+
id: runId
|
|
958
|
+
}, {
|
|
959
|
+
status: "suspended",
|
|
960
|
+
wakeAt: new Date(patch.wakeAt),
|
|
961
|
+
updatedAt: patch.updatedAt
|
|
962
|
+
});
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
await em.nativeUpdate(WorkflowRunEntity, {
|
|
966
|
+
id: runId
|
|
967
|
+
}, {
|
|
968
|
+
tags
|
|
969
|
+
});
|
|
970
|
+
return true;
|
|
971
|
+
});
|
|
972
|
+
}
|
|
848
973
|
async createRun(run) {
|
|
849
974
|
const em = this.fork();
|
|
850
975
|
em.create(WorkflowRunEntity, toRunEntity(run));
|
|
851
976
|
await em.flush();
|
|
852
977
|
await this.reindexAttributes(run.id, run.searchAttributes);
|
|
853
978
|
}
|
|
854
|
-
async updateRun(runId,
|
|
979
|
+
async updateRun(runId, inputPatch) {
|
|
980
|
+
const patch = inputPatch.status && import_nestjs_durable_core.TERMINAL_RUN_STATUSES.includes(inputPatch.status) ? await (0, import_nestjs_durable_core.clearTerminalSingletonAdmission)(runId, inputPatch, (id) => this.withScope({}).getRun(id)) : inputPatch;
|
|
855
981
|
const em = this.fork();
|
|
856
982
|
const entity = await em.findOneOrFail(WorkflowRunEntity, {
|
|
857
983
|
id: runId
|
|
@@ -895,71 +1021,69 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
895
1021
|
id: runId
|
|
896
1022
|
});
|
|
897
1023
|
}
|
|
1024
|
+
async deleteRuns(runIds) {
|
|
1025
|
+
if (runIds.length === 0) return;
|
|
1026
|
+
const runId = {
|
|
1027
|
+
$in: runIds
|
|
1028
|
+
};
|
|
1029
|
+
await this.fork().transactional(async (tem) => {
|
|
1030
|
+
await tem.nativeDelete(StepCheckpointEntity, {
|
|
1031
|
+
runId
|
|
1032
|
+
});
|
|
1033
|
+
await tem.nativeDelete(SignalWaiterEntity, {
|
|
1034
|
+
runId
|
|
1035
|
+
});
|
|
1036
|
+
await tem.nativeDelete(RunAttributeEntity, {
|
|
1037
|
+
runId
|
|
1038
|
+
});
|
|
1039
|
+
await tem.nativeDelete(WorkflowRunEntity, {
|
|
1040
|
+
id: runId
|
|
1041
|
+
});
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
898
1044
|
async pruneTerminalRuns(policy, nowMs, limit) {
|
|
899
|
-
|
|
1045
|
+
const statuses = policy.statuses.filter((s) => import_nestjs_durable_core.TERMINAL_RUN_STATUSES.includes(s));
|
|
1046
|
+
if (statuses.length === 0 || limit <= 0) return 0;
|
|
1047
|
+
if (policy.maxAge == null && policy.maxCount == null) return 0;
|
|
900
1048
|
const em = this.fork();
|
|
901
|
-
const
|
|
902
|
-
|
|
1049
|
+
const meta = em.getMetadata().get(WorkflowRunEntity);
|
|
1050
|
+
const idCol = meta.properties.id?.fieldNames?.[0] ?? "id";
|
|
1051
|
+
const query = {
|
|
1052
|
+
...policy.scope ?? {},
|
|
1053
|
+
statuses
|
|
903
1054
|
};
|
|
1055
|
+
const scoped = /* @__PURE__ */ __name((extra) => this.runQueryBuilder(em, query, {
|
|
1056
|
+
...this.runWhere(query),
|
|
1057
|
+
...extra ?? {}
|
|
1058
|
+
}).select([
|
|
1059
|
+
`r.${idCol}`
|
|
1060
|
+
]), "scoped");
|
|
904
1061
|
const ids = /* @__PURE__ */ new Set();
|
|
905
1062
|
if (policy.maxAge != null) {
|
|
906
1063
|
const cutoff = new Date(nowMs - (0, import_nestjs_durable_core.parseDuration)(policy.maxAge));
|
|
907
|
-
const rows = await
|
|
908
|
-
status,
|
|
1064
|
+
const rows = await scoped({
|
|
909
1065
|
updatedAt: {
|
|
910
1066
|
$lt: cutoff
|
|
911
1067
|
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
orderBy: {
|
|
917
|
-
updatedAt: "asc"
|
|
918
|
-
},
|
|
919
|
-
limit
|
|
920
|
-
});
|
|
921
|
-
for (const r of rows) ids.add(r.id);
|
|
1068
|
+
}).orderBy({
|
|
1069
|
+
"r.updatedAt": "asc"
|
|
1070
|
+
}).limit(limit).execute();
|
|
1071
|
+
for (const r of rows) ids.add(String(r[idCol] ?? r.id));
|
|
922
1072
|
}
|
|
923
1073
|
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
|
-
});
|
|
1074
|
+
const rows = await scoped().orderBy({
|
|
1075
|
+
"r.updatedAt": "desc",
|
|
1076
|
+
"r.id": "desc"
|
|
1077
|
+
}).limit(limit).offset(policy.maxCount).execute();
|
|
937
1078
|
for (const r of rows) {
|
|
938
|
-
ids.add(r.id);
|
|
939
1079
|
if (ids.size >= limit) break;
|
|
1080
|
+
ids.add(String(r[idCol] ?? r.id));
|
|
940
1081
|
}
|
|
941
1082
|
}
|
|
942
|
-
if (ids.size === 0) return 0;
|
|
943
1083
|
const idList = [
|
|
944
1084
|
...ids
|
|
945
1085
|
].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
|
-
});
|
|
1086
|
+
await this.deleteRuns(idList);
|
|
963
1087
|
return idList.length;
|
|
964
1088
|
}
|
|
965
1089
|
async getCheckpoint(runId, seq) {
|
|
@@ -1171,6 +1295,11 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1171
1295
|
const colExpr = this.jsonAsText(em, `${quote("r")}.${quote(this.tagsColumn(em))}`);
|
|
1172
1296
|
return `${colExpr} LIKE '${pattern}'`;
|
|
1173
1297
|
}
|
|
1298
|
+
async countRuns(query) {
|
|
1299
|
+
const em = this.fork();
|
|
1300
|
+
const rows = await this.runQueryBuilder(em, query, this.runWhere(query)).select([]).addSelect((0, import_core3.raw)("count(*) as count")).execute();
|
|
1301
|
+
return Number(rows[0]?.count ?? 0);
|
|
1302
|
+
}
|
|
1174
1303
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
1175
1304
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
1176
1305
|
* count them in the browser. */
|
|
@@ -1362,6 +1491,76 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1362
1491
|
const ch = isMysql ? "`" : '"';
|
|
1363
1492
|
return (id) => `${ch}${id}${ch}`;
|
|
1364
1493
|
}
|
|
1494
|
+
// ---- persisted schedules ----------------------------------------------------------------------
|
|
1495
|
+
async saveSchedule(record) {
|
|
1496
|
+
const em = this.fork();
|
|
1497
|
+
await em.upsert(ScheduleEntity, toScheduleEntity(record));
|
|
1498
|
+
}
|
|
1499
|
+
async getSchedule(id) {
|
|
1500
|
+
const entity = await this.fork().findOne(ScheduleEntity, {
|
|
1501
|
+
id
|
|
1502
|
+
});
|
|
1503
|
+
return entity ? fromScheduleEntity(entity) : null;
|
|
1504
|
+
}
|
|
1505
|
+
/** One conditional `nativeUpdate`: the compare-and-set on `next_fire_at` (an exact bigint) is a
|
|
1506
|
+
* single statement. */
|
|
1507
|
+
async updateSchedule(id, patch, expectedNextFireAt) {
|
|
1508
|
+
const data = {};
|
|
1509
|
+
if (patch.namespace !== void 0) data.namespace = patch.namespace;
|
|
1510
|
+
if (patch.workflow !== void 0) data.workflow = patch.workflow;
|
|
1511
|
+
if (patch.paused !== void 0) data.paused = patch.paused;
|
|
1512
|
+
if ("nextFireAt" in patch) data.nextFireAt = patch.nextFireAt ?? null;
|
|
1513
|
+
if ("tags" in patch) data.tags = patch.tags ?? null;
|
|
1514
|
+
if (patch.spec !== void 0) data.spec = patch.spec;
|
|
1515
|
+
if (patch.state !== void 0) data.state = patch.state;
|
|
1516
|
+
if (patch.updatedAt !== void 0) data.updatedAt = patch.updatedAt;
|
|
1517
|
+
if (!Object.keys(data).length) return await this.getSchedule(id) !== null;
|
|
1518
|
+
const where = {
|
|
1519
|
+
id
|
|
1520
|
+
};
|
|
1521
|
+
if (expectedNextFireAt !== void 0) where.nextFireAt = expectedNextFireAt;
|
|
1522
|
+
const affected = await this.fork().nativeUpdate(ScheduleEntity, where, data);
|
|
1523
|
+
return affected > 0;
|
|
1524
|
+
}
|
|
1525
|
+
async deleteSchedule(id) {
|
|
1526
|
+
return await this.fork().nativeDelete(ScheduleEntity, {
|
|
1527
|
+
id
|
|
1528
|
+
}) > 0;
|
|
1529
|
+
}
|
|
1530
|
+
async listSchedules(query) {
|
|
1531
|
+
const em = this.fork();
|
|
1532
|
+
const where = {};
|
|
1533
|
+
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
1534
|
+
if (query.workflow !== void 0) where.workflow = query.workflow;
|
|
1535
|
+
if (query.paused !== void 0) where.paused = query.paused;
|
|
1536
|
+
if (query.dueBy !== void 0) {
|
|
1537
|
+
where.$and = [
|
|
1538
|
+
{
|
|
1539
|
+
paused: false
|
|
1540
|
+
},
|
|
1541
|
+
{
|
|
1542
|
+
nextFireAt: {
|
|
1543
|
+
$lte: query.dueBy
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
];
|
|
1547
|
+
}
|
|
1548
|
+
const quote = this.idQuote(em);
|
|
1549
|
+
const meta = em.getMetadata().get(ScheduleEntity);
|
|
1550
|
+
const tagsCol = meta.properties.tags?.fieldNames?.[0] ?? "tags";
|
|
1551
|
+
const qb = em.createQueryBuilder(ScheduleEntity, "r").where(where);
|
|
1552
|
+
if (query.tag !== void 0) {
|
|
1553
|
+
const pattern = `%"${query.tag.replace(/'/g, "''")}"%`;
|
|
1554
|
+
qb.andWhere(`${this.jsonAsText(em, `${quote("r")}.${quote(tagsCol)}`)} LIKE '${pattern}'`);
|
|
1555
|
+
}
|
|
1556
|
+
qb.orderBy({
|
|
1557
|
+
"r.nextFireAt": "asc nulls last",
|
|
1558
|
+
"r.id": "asc"
|
|
1559
|
+
});
|
|
1560
|
+
if (query.limit != null) qb.limit(query.limit);
|
|
1561
|
+
if (query.offset != null) qb.offset(query.offset);
|
|
1562
|
+
return (await qb.getResultList()).map(fromScheduleEntity);
|
|
1563
|
+
}
|
|
1365
1564
|
async listCheckpoints(runId) {
|
|
1366
1565
|
const em = this.fork();
|
|
1367
1566
|
const rows = await em.find(StepCheckpointEntity, {
|
|
@@ -1600,6 +1799,38 @@ function fromCheckpointEntity(e) {
|
|
|
1600
1799
|
};
|
|
1601
1800
|
}
|
|
1602
1801
|
__name(fromCheckpointEntity, "fromCheckpointEntity");
|
|
1802
|
+
function toScheduleEntity(r) {
|
|
1803
|
+
const e = new ScheduleEntity();
|
|
1804
|
+
e.id = r.id;
|
|
1805
|
+
e.namespace = r.namespace;
|
|
1806
|
+
e.workflow = r.workflow;
|
|
1807
|
+
e.paused = r.paused;
|
|
1808
|
+
e.nextFireAt = r.nextFireAt;
|
|
1809
|
+
e.tags = r.tags ?? null;
|
|
1810
|
+
e.spec = r.spec;
|
|
1811
|
+
e.state = r.state;
|
|
1812
|
+
e.createdAt = r.createdAt;
|
|
1813
|
+
e.updatedAt = r.updatedAt;
|
|
1814
|
+
return e;
|
|
1815
|
+
}
|
|
1816
|
+
__name(toScheduleEntity, "toScheduleEntity");
|
|
1817
|
+
function fromScheduleEntity(e) {
|
|
1818
|
+
return {
|
|
1819
|
+
id: e.id,
|
|
1820
|
+
namespace: e.namespace,
|
|
1821
|
+
workflow: e.workflow,
|
|
1822
|
+
paused: Boolean(e.paused),
|
|
1823
|
+
nextFireAt: e.nextFireAt == null ? null : Number(e.nextFireAt),
|
|
1824
|
+
...e.tags?.length ? {
|
|
1825
|
+
tags: e.tags
|
|
1826
|
+
} : {},
|
|
1827
|
+
spec: e.spec ?? {},
|
|
1828
|
+
state: e.state ?? {},
|
|
1829
|
+
createdAt: e.createdAt,
|
|
1830
|
+
updatedAt: e.updatedAt
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
__name(fromScheduleEntity, "fromScheduleEntity");
|
|
1603
1834
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1604
1835
|
0 && (module.exports = {
|
|
1605
1836
|
BufferedEventEntity,
|
|
@@ -1610,6 +1841,8 @@ __name(fromCheckpointEntity, "fromCheckpointEntity");
|
|
|
1610
1841
|
MikroOrmStateStore,
|
|
1611
1842
|
RunAttributeEntity,
|
|
1612
1843
|
RunAttributeRepository,
|
|
1844
|
+
ScheduleEntity,
|
|
1845
|
+
ScheduleRepository,
|
|
1613
1846
|
SignalWaiterEntity,
|
|
1614
1847
|
SignalWaiterRepository,
|
|
1615
1848
|
StepCheckpointEntity,
|