@dudousxd/nestjs-durable-store-mikro-orm 0.15.1 → 0.16.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 +49 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +49 -9
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -63,6 +63,7 @@ var WorkflowRunEntity = class {
|
|
|
63
63
|
tags;
|
|
64
64
|
searchAttributes;
|
|
65
65
|
priority;
|
|
66
|
+
namespace;
|
|
66
67
|
createdAt;
|
|
67
68
|
updatedAt;
|
|
68
69
|
};
|
|
@@ -104,6 +105,7 @@ var SignalWaiterEntity = class {
|
|
|
104
105
|
token;
|
|
105
106
|
runId;
|
|
106
107
|
seq;
|
|
108
|
+
parallelGroup;
|
|
107
109
|
};
|
|
108
110
|
var BufferedSignalEntity = class {
|
|
109
111
|
static {
|
|
@@ -136,6 +138,14 @@ function durableEntities(options = {}) {
|
|
|
136
138
|
"workflow",
|
|
137
139
|
"status"
|
|
138
140
|
]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "durable_workflow_runs_namespace_status_idx",
|
|
144
|
+
properties: [
|
|
145
|
+
"namespace",
|
|
146
|
+
"status",
|
|
147
|
+
"createdAt"
|
|
148
|
+
]
|
|
139
149
|
}
|
|
140
150
|
],
|
|
141
151
|
properties: {
|
|
@@ -206,6 +216,11 @@ function durableEntities(options = {}) {
|
|
|
206
216
|
nullable: true,
|
|
207
217
|
fieldName: col("priority")
|
|
208
218
|
},
|
|
219
|
+
namespace: {
|
|
220
|
+
type: "string",
|
|
221
|
+
default: "default",
|
|
222
|
+
fieldName: col("namespace")
|
|
223
|
+
},
|
|
209
224
|
createdAt: {
|
|
210
225
|
type: "Date",
|
|
211
226
|
fieldName: col("createdAt")
|
|
@@ -365,6 +380,14 @@ function durableEntities(options = {}) {
|
|
|
365
380
|
seq: {
|
|
366
381
|
type: "integer",
|
|
367
382
|
fieldName: col("seq")
|
|
383
|
+
},
|
|
384
|
+
// A ctx.gather_children/ctx.all child fan-out tags every awaited child with the same group; the
|
|
385
|
+
// engine threads it onto the waiter so the resolving `signal:child:` checkpoint carries it and the
|
|
386
|
+
// dashboard renders the fan as one parallel group. Nullable: only fan-out child waiters carry it.
|
|
387
|
+
parallelGroup: {
|
|
388
|
+
type: "string",
|
|
389
|
+
nullable: true,
|
|
390
|
+
fieldName: col("parallelGroup")
|
|
368
391
|
}
|
|
369
392
|
}
|
|
370
393
|
});
|
|
@@ -630,7 +653,7 @@ var MikroOrmStateStore = class {
|
|
|
630
653
|
}, "saveCheckpoint")
|
|
631
654
|
}));
|
|
632
655
|
}
|
|
633
|
-
async listIncompleteRuns() {
|
|
656
|
+
async listIncompleteRuns(namespace) {
|
|
634
657
|
const em = this.orm.em.fork();
|
|
635
658
|
const rows = await em.find(WorkflowRunEntity, {
|
|
636
659
|
status: {
|
|
@@ -638,14 +661,20 @@ var MikroOrmStateStore = class {
|
|
|
638
661
|
"running",
|
|
639
662
|
"cancelling"
|
|
640
663
|
]
|
|
641
|
-
}
|
|
664
|
+
},
|
|
665
|
+
...namespace !== void 0 ? {
|
|
666
|
+
namespace
|
|
667
|
+
} : {}
|
|
642
668
|
});
|
|
643
669
|
return rows.map(fromRunEntity);
|
|
644
670
|
}
|
|
645
|
-
async listPendingRuns(limit) {
|
|
671
|
+
async listPendingRuns(limit, namespace) {
|
|
646
672
|
const em = this.orm.em.fork();
|
|
647
673
|
const rows = await em.find(WorkflowRunEntity, {
|
|
648
|
-
status: "pending"
|
|
674
|
+
status: "pending",
|
|
675
|
+
...namespace !== void 0 ? {
|
|
676
|
+
namespace
|
|
677
|
+
} : {}
|
|
649
678
|
}, {
|
|
650
679
|
orderBy: {
|
|
651
680
|
createdAt: "asc"
|
|
@@ -654,14 +683,17 @@ var MikroOrmStateStore = class {
|
|
|
654
683
|
});
|
|
655
684
|
return rows.map(fromRunEntity);
|
|
656
685
|
}
|
|
657
|
-
async listDueTimers(nowMs) {
|
|
686
|
+
async listDueTimers(nowMs, namespace) {
|
|
658
687
|
const em = this.orm.em.fork();
|
|
659
688
|
const rows = await em.find(WorkflowRunEntity, {
|
|
660
689
|
status: "suspended",
|
|
661
690
|
wakeAt: {
|
|
662
691
|
$ne: null,
|
|
663
692
|
$lte: new Date(nowMs)
|
|
664
|
-
}
|
|
693
|
+
},
|
|
694
|
+
...namespace !== void 0 ? {
|
|
695
|
+
namespace
|
|
696
|
+
} : {}
|
|
665
697
|
});
|
|
666
698
|
return rows.map(fromRunEntity);
|
|
667
699
|
}
|
|
@@ -708,6 +740,7 @@ var MikroOrmStateStore = class {
|
|
|
708
740
|
const em = this.orm.em.fork();
|
|
709
741
|
const where = {};
|
|
710
742
|
if (query.workflow) where.workflow = query.workflow;
|
|
743
|
+
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
711
744
|
if (query.status && query.statuses) {
|
|
712
745
|
where.$and = [
|
|
713
746
|
{
|
|
@@ -854,7 +887,10 @@ var MikroOrmStateStore = class {
|
|
|
854
887
|
async putSignalWaiter(waiter) {
|
|
855
888
|
const em = this.orm.em.fork();
|
|
856
889
|
await em.upsert(SignalWaiterEntity, {
|
|
857
|
-
|
|
890
|
+
token: waiter.token,
|
|
891
|
+
runId: waiter.runId,
|
|
892
|
+
seq: waiter.seq,
|
|
893
|
+
parallelGroup: waiter.parallelGroup ?? null
|
|
858
894
|
});
|
|
859
895
|
await em.flush();
|
|
860
896
|
}
|
|
@@ -867,7 +903,8 @@ var MikroOrmStateStore = class {
|
|
|
867
903
|
const waiter = {
|
|
868
904
|
token: entity.token,
|
|
869
905
|
runId: entity.runId,
|
|
870
|
-
seq: entity.seq
|
|
906
|
+
seq: entity.seq,
|
|
907
|
+
parallelGroup: entity.parallelGroup ?? void 0
|
|
871
908
|
};
|
|
872
909
|
await em.remove(entity).flush();
|
|
873
910
|
return waiter;
|
|
@@ -882,7 +919,8 @@ var MikroOrmStateStore = class {
|
|
|
882
919
|
return rows.map((e) => ({
|
|
883
920
|
token: e.token,
|
|
884
921
|
runId: e.runId,
|
|
885
|
-
seq: e.seq
|
|
922
|
+
seq: e.seq,
|
|
923
|
+
parallelGroup: e.parallelGroup ?? void 0
|
|
886
924
|
}));
|
|
887
925
|
}
|
|
888
926
|
async bufferSignal(token, payload) {
|
|
@@ -926,6 +964,7 @@ function toRunEntity(run) {
|
|
|
926
964
|
e.tags = run.tags ?? null;
|
|
927
965
|
e.searchAttributes = run.searchAttributes ?? null;
|
|
928
966
|
e.priority = run.priority ?? null;
|
|
967
|
+
e.namespace = run.namespace ?? "default";
|
|
929
968
|
e.createdAt = run.createdAt;
|
|
930
969
|
e.updatedAt = run.updatedAt;
|
|
931
970
|
return e;
|
|
@@ -947,6 +986,7 @@ function fromRunEntity(e) {
|
|
|
947
986
|
tags: e.tags ?? void 0,
|
|
948
987
|
searchAttributes: e.searchAttributes ?? void 0,
|
|
949
988
|
priority: e.priority ?? void 0,
|
|
989
|
+
namespace: e.namespace,
|
|
950
990
|
createdAt: e.createdAt,
|
|
951
991
|
updatedAt: e.updatedAt
|
|
952
992
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/entities.ts","../src/mikro-orm-state-store.ts","../src/schema.ts"],"sourcesContent":["export * from './entities';\nexport * from './mikro-orm-state-store';\nexport * from './schema';\n","import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema } from '@mikro-orm/core';\n\n/**\n * How property names map to physical column names. The durable tables are adapter-agnostic: a run\n * written by one store adapter must be readable by another, so every adapter has to agree on the\n * physical column names. The default is `'snake_case'` (the canonical, matched by the TypeORM,\n * Prisma and Drizzle adapters). `'preserve'` keeps the camelCase property name verbatim (what the\n * old TypeORM/Prisma adapters produced before they were pinned). A function lets you supply any\n * custom mapping.\n *\n * The choice is pinned EXPLICITLY onto the entity schema rather than left to the host ORM's naming\n * strategy — depending on the host strategy is what silently diverged the adapters and broke a store\n * swap against an existing table.\n */\nexport type DurableColumnNaming = 'snake_case' | 'preserve' | ((property: string) => string);\n\nfunction snakeCase(property: string): string {\n return property.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nfunction columnNamer(naming: DurableColumnNaming): (property: string) => string {\n if (naming === 'snake_case') return snakeCase;\n if (naming === 'preserve') return (property) => property;\n return naming;\n}\n\n// Plain classes (no decorators): the schema/column mapping lives on the EntitySchema built by\n// `durableEntities`, so the same class can be registered under different column names. The store\n// still references these classes for `em.create`/`getMetadata`.\n\nexport class WorkflowRunEntity {\n id!: string;\n workflow!: string;\n workflowVersion!: string;\n status!: RunStatus;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n wakeAt?: Date;\n lockedBy?: string;\n lockedUntil?: Date;\n recoveryAttempts?: number;\n tags?: string[] | null;\n searchAttributes?: Record<string, string | number | boolean> | null;\n priority?: number | null;\n createdAt!: Date;\n updatedAt!: Date;\n}\n\nexport class StepCheckpointEntity {\n runId!: string;\n seq!: number;\n name!: string;\n kind!: StepKind;\n stepId!: string;\n status!: 'pending' | 'running' | 'completed' | 'failed';\n input?: unknown;\n output?: unknown;\n error?: unknown;\n events?: unknown;\n attempts!: number;\n workerGroup?: string;\n parallelGroup?: string;\n wakeAt?: Date;\n enqueuedAt?: Date;\n startedAt!: Date;\n finishedAt!: Date;\n}\n\n/**\n * Normalized side-table for search attributes: one row per (run, key), so range/equality predicates\n * push DOWN into SQL (an EXISTS join indexed on `(key, numValue)` / `(key, strValue)`) instead of a\n * coarse scan + in-process filter. Maintained on every createRun/updateRun. Numbers land in\n * `numValue`, strings/booleans in `strValue` (booleans as \"true\"/\"false\"); see normalizeAttributeRows.\n */\nexport class RunAttributeEntity {\n runId!: string;\n key!: string;\n strValue?: string | null;\n numValue?: number | null;\n}\n\nexport class SignalWaiterEntity {\n token!: string;\n runId!: string;\n seq!: number;\n}\n\nexport class BufferedSignalEntity {\n id!: number;\n token!: string;\n payload?: unknown;\n}\n\n/**\n * Build the durable MikroORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your MikroORM config / `MikroOrmModule.forFeature`.\n *\n * ```ts\n * entities: durableEntities({ naming: 'snake_case' })\n * ```\n */\nexport function durableEntities(options: { naming?: DurableColumnNaming } = {}): EntitySchema[] {\n const col = columnNamer(options.naming ?? 'snake_case');\n\n const workflowRuns = new EntitySchema<WorkflowRunEntity>({\n class: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n // Indexes mirror the Prisma adapter so a store swap keeps the same plan. The poller hits these\n // every tick: `(status, wakeAt)` serves both the due-timer scan (status='suspended' AND wakeAt<=now)\n // and, via its `status` prefix, the pending/incomplete status lookups; `(workflow, status)` serves\n // the per-workflow timeout sweep. Without them every poll is a full scan of a table that only grows.\n indexes: [\n { name: 'durable_workflow_runs_status_wake_at_idx', properties: ['status', 'wakeAt'] },\n { name: 'durable_workflow_runs_workflow_status_idx', properties: ['workflow', 'status'] },\n ],\n properties: {\n id: { type: 'string', primary: true, fieldName: col('id') },\n workflow: { type: 'string', fieldName: col('workflow') },\n workflowVersion: { type: 'string', fieldName: col('workflowVersion') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n lockedBy: { type: 'string', nullable: true, fieldName: col('lockedBy') },\n lockedUntil: { type: 'Date', nullable: true, fieldName: col('lockedUntil') },\n recoveryAttempts: { type: 'integer', nullable: true, fieldName: col('recoveryAttempts') },\n tags: { type: 'json', nullable: true, fieldName: col('tags') },\n searchAttributes: { type: 'json', nullable: true, fieldName: col('searchAttributes') },\n priority: { type: 'integer', nullable: true, fieldName: col('priority') },\n createdAt: { type: 'Date', fieldName: col('createdAt') },\n updatedAt: { type: 'Date', fieldName: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n class: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n seq: { type: 'integer', primary: true, fieldName: col('seq') },\n name: { type: 'string', fieldName: col('name') },\n kind: { type: 'string', fieldName: col('kind') },\n stepId: { type: 'string', fieldName: col('stepId') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n events: { type: 'json', nullable: true, fieldName: col('events') },\n attempts: { type: 'integer', fieldName: col('attempts') },\n workerGroup: { type: 'string', nullable: true, fieldName: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group string so the dashboard\n // renders them as one \"ran in parallel\" group. The core engine sets it (incl. from a remote\n // worker's recordStep), but it was never persisted here — so the fan always rendered as N\n // sequential singles. Nullable: only fan-out steps carry it.\n parallelGroup: { type: 'string', nullable: true, fieldName: col('parallelGroup') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n enqueuedAt: { type: 'Date', nullable: true, fieldName: col('enqueuedAt') },\n startedAt: { type: 'Date', fieldName: col('startedAt') },\n finishedAt: { type: 'Date', fieldName: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n class: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n // The search-attribute EXISTS join (see the class docstring) pushes equality/range predicates down\n // onto `(key, numValue)` / `(key, strValue)`; mirror the Prisma adapter so those scans stay indexed.\n indexes: [\n { name: 'durable_run_attributes_key_num_idx', properties: ['key', 'numValue'] },\n { name: 'durable_run_attributes_key_str_idx', properties: ['key', 'strValue'] },\n ],\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n key: { type: 'string', primary: true, fieldName: col('key') },\n strValue: { type: 'string', nullable: true, fieldName: col('strValue') },\n // `float`/`double` is portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, fieldName: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n class: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n properties: {\n token: { type: 'string', primary: true, fieldName: col('token') },\n runId: { type: 'string', fieldName: col('runId') },\n seq: { type: 'integer', fieldName: col('seq') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n class: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n properties: {\n id: { type: 'integer', primary: true, autoincrement: true, fieldName: col('id') },\n token: { type: 'string', index: true, fieldName: col('token') },\n payload: { type: 'json', nullable: true, fieldName: col('payload') },\n },\n });\n\n return [workflowRuns, stepCheckpoints, runAttributes, signalWaiters, bufferedSignals];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import {\n type AttributeFilter,\n type RetentionPolicy,\n type RunQuery,\n type SignalWaiter,\n type StateStore,\n type StepCheckpoint,\n type StepError,\n type StepEvent,\n type WorkflowRun,\n attributeColumnFor,\n attributeOperand,\n normalizeAttributeRows,\n parseDuration,\n sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport type { EntityManager, MikroORM } from '@mikro-orm/core';\n\n/** Minimal structural surface of the SQL EntityManager's QueryBuilder (from `@mikro-orm/knex`),\n * typed here so the adapter needn't add a hard dependency on the knex types — a real SQL-driver EM\n * satisfies it at runtime. */\ninterface DurableQueryBuilder<T> {\n where(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n andWhere(cond: string): DurableQueryBuilder<T>;\n orderBy(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n limit(n: number): DurableQueryBuilder<T>;\n offset(n: number): DurableQueryBuilder<T>;\n getResultList(): Promise<T[]>;\n}\ntype SqlEm = EntityManager & {\n createQueryBuilder<T>(entity: unknown, alias: string): DurableQueryBuilder<T>;\n};\n\n/** Resolved side-table column names (per the active naming strategy) for the raw EXISTS pushdown. */\ninterface AttributeColumns {\n table: string;\n /** Side-table FK column (the run id on `durable_run_attributes`). */\n runId: string;\n /** Run table PK column (the `id` on `durable_workflow_runs`) used for the outer correlation. */\n runPk: string;\n key: string;\n strValue: string;\n numValue: string;\n}\nimport {\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { ensureMikroOrmDurableSchema } from './schema';\n\n/**\n * MikroORM-backed `StateStore`. Works on any MikroORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps and `wakeAt` use native datetime columns. Each operation runs on a forked\n * EntityManager so it owns its unit of work.\n */\nexport class MikroOrmStateStore implements StateStore {\n constructor(private readonly orm: MikroORM) {}\n\n async ensureSchema(): Promise<void> {\n await ensureMikroOrmDurableSchema(this.orm);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n const em = this.orm.em.fork();\n em.create(WorkflowRunEntity, toRunEntity(run));\n await em.flush();\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n const em = this.orm.em.fork();\n const entity = await em.findOneOrFail(WorkflowRunEntity, { id: runId });\n Object.assign(entity, toRunEntity({ ...fromRunEntity(entity), ...patch } as WorkflowRun));\n await em.flush();\n // Keep the side-table in step with the run's attributes whenever they're patched.\n if ('searchAttributes' in patch) await this.reindexAttributes(runId, patch.searchAttributes);\n }\n\n /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors\n * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */\n private async reindexAttributes(\n runId: string,\n attributes: WorkflowRun['searchAttributes'],\n forked?: EntityManager,\n ): Promise<void> {\n const em = forked ?? this.orm.em.fork();\n await em.nativeDelete(RunAttributeEntity, { runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await em.insertMany(RunAttributeEntity, rows);\n }\n\n async getRun(runId: string): Promise<WorkflowRun | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(WorkflowRunEntity, { id: runId });\n return entity ? fromRunEntity(entity) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n // Children first, then the run row — checkpoints, signal waiters, and the normalized\n // attribute side-table, so nothing dangles after the run is gone.\n await em.nativeDelete(StepCheckpointEntity, { runId });\n await em.nativeDelete(SignalWaiterEntity, { runId });\n await em.nativeDelete(RunAttributeEntity, { runId });\n await em.nativeDelete(WorkflowRunEntity, { id: runId });\n }\n\n async pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number> {\n if (policy.statuses.length === 0 || limit <= 0) return 0;\n const em = this.orm.em.fork();\n const status = { $in: policy.statuses };\n // Collect ids that violate EITHER bound (most-restrictive keep): too old, or past the count cap.\n const ids = new Set<string>();\n\n if (policy.maxAge != null) {\n const cutoff = new Date(nowMs - parseDuration(policy.maxAge));\n const rows = await em.find(\n WorkflowRunEntity,\n { status, updatedAt: { $lt: cutoff } },\n { fields: ['id'], orderBy: { updatedAt: 'asc' }, limit }, // oldest first\n );\n for (const r of rows) ids.add(r.id);\n }\n\n if (policy.maxCount != null && ids.size < limit) {\n // Everything beyond the newest `maxCount` rows in the status set — skip the kept window via offset.\n const rows = await em.find(\n WorkflowRunEntity,\n { status },\n {\n fields: ['id'],\n orderBy: { updatedAt: 'desc', id: 'desc' },\n limit,\n offset: policy.maxCount,\n },\n );\n for (const r of rows) {\n ids.add(r.id);\n if (ids.size >= limit) break;\n }\n }\n\n if (ids.size === 0) return 0;\n const idList = [...ids].slice(0, limit);\n const runId = { $in: idList };\n // Cascade children then runs (mirrors deleteRun) in one transaction so a pruned run never dangles.\n await em.transactional(async (tem) => {\n await tem.nativeDelete(StepCheckpointEntity, { runId });\n await tem.nativeDelete(SignalWaiterEntity, { runId });\n await tem.nativeDelete(RunAttributeEntity, { runId });\n await tem.nativeDelete(WorkflowRunEntity, { id: runId });\n });\n return idList.length;\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(StepCheckpointEntity, { runId, seq });\n return entity ? fromCheckpointEntity(entity) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(checkpoint));\n await em.flush();\n }\n\n async transaction<T>(\n work: (tx: {\n raw: unknown;\n saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;\n }) => Promise<T>,\n ): Promise<T> {\n return this.orm.em.fork().transactional(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, { status: { $in: ['running', 'cancelling'] } });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(\n WorkflowRunEntity,\n { status: 'pending' },\n { orderBy: { createdAt: 'asc' }, limit }, // FIFO dispatch\n );\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, {\n status: 'suspended',\n wakeAt: { $ne: null, $lte: new Date(nowMs) },\n });\n return rows.map(fromRunEntity);\n }\n\n async tryLockRun(\n runId: string,\n owner: string,\n leaseUntilMs: number,\n nowMs: number,\n ): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, $or: [{ lockedUntil: null }, { lockedUntil: { $lte: new Date(nowMs) } }] },\n { lockedBy: owner, lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n await em.nativeUpdate(WorkflowRunEntity, { id: runId }, { lockedBy: null, lockedUntil: null });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, lockedBy: owner },\n { lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const where: Record<string, unknown> = {};\n if (query.workflow) where.workflow = query.workflow;\n // `status IN (...)`; an empty set matches nothing (mirrors the in-memory store). When both the\n // single `status` and `statuses` are set, AND them so the narrower set wins.\n if (query.status && query.statuses) {\n where.$and = [{ status: query.status }, { status: { $in: query.statuses } }];\n } else if (query.status) {\n where.status = query.status;\n } else if (query.statuses) {\n where.status = { $in: query.statuses };\n }\n const orderBy = { createdAt: 'desc' as const }; // newest first — recent runs on top in the dashboard\n // `tags`/attributes need raw SQL pushed through the QueryBuilder:\n // - tag: `tags` is a JSON column (native json/jsonb on PG/MySQL), and MikroORM JSON-serializes a\n // `{ $like }` operand (wrapping/escaping the LIKE pattern) so a plain where corrupts it to no\n // matches. A raw LIKE on the column-as-text matches the quoted token so `etl` doesn't match\n // `etl-foo`. Postgres `jsonb` rejects `LIKE` directly, so cast to text per dialect.\n // - attributes: each filter becomes a raw EXISTS against the normalized side-table, so the DB\n // filters AND paginates — no full scan + in-process filter (ANDed: one EXISTS per filter).\n // Use a QueryBuilder with a fixed root alias `r` so the raw correlations are stable across drivers.\n if (query.tag || query.attributes?.length) {\n const quote = this.idQuote(em);\n const cols = this.attributeColumns(em);\n const qb = (em as SqlEm)\n .createQueryBuilder<WorkflowRunEntity>(WorkflowRunEntity, 'r')\n .where(where)\n .orderBy(orderBy);\n if (query.tag) {\n const tagsCol = this.tagsColumn(em);\n const pattern = `%\"${query.tag.replace(/'/g, \"''\")}\"%`;\n const colExpr = this.jsonAsText(em, `${quote('r')}.${quote(tagsCol)}`);\n qb.andWhere(`${colExpr} LIKE '${pattern}'`);\n }\n for (const f of query.attributes ?? []) {\n qb.andWhere(this.attributeExistsSql(f, quote, 'r', cols));\n }\n if (query.limit != null) qb.limit(query.limit);\n if (query.offset != null) qb.offset(query.offset);\n const rows = await qb.getResultList();\n return rows.map(fromRunEntity);\n }\n const rows = await em.find(WorkflowRunEntity, where, {\n ...(query.limit != null ? { limit: query.limit } : {}),\n ...(query.offset != null ? { offset: query.offset } : {}),\n orderBy,\n });\n return rows.map(fromRunEntity);\n }\n\n /** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run\n * alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):\n * EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric\n * operands compare the num column, everything else the str column. Identifiers are quoted per\n * driver and operands are inlined as literals (string operands are escaped by doubling quotes). */\n private attributeExistsSql(\n f: AttributeFilter,\n quote: (id: string) => string,\n alias: string,\n cols: AttributeColumns,\n ): string {\n const col = attributeColumnFor(f) === 'numValue' ? cols.numValue : cols.strValue;\n const cmp = sqlComparator(f.op);\n const operand = attributeOperand(f);\n const literal =\n typeof operand === 'number' ? String(operand) : `'${String(operand).replace(/'/g, \"''\")}'`;\n const a = quote(cols.table);\n const sub = quote('a');\n const outerId = `${quote(alias)}.${quote(cols.runPk)}`; // run PK column on the outer alias\n return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, \"''\")}' AND ${sub}.${quote(col)} ${cmp} ${literal})`;\n }\n\n /** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches\n * the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is\n * reused for BOTH the outer correlation and the side-table FK (they share the same fieldName). */\n private attributeColumns(em: EntityManager): AttributeColumns {\n const meta = em.getMetadata().get(RunAttributeEntity);\n const field = (prop: string) =>\n meta.props.find((p) => p.name === prop)?.fieldNames?.[0] ?? prop;\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n const runPk = runMeta.properties.id?.fieldNames?.[0] ?? 'id';\n return {\n table: meta.tableName,\n runId: field('runId'),\n runPk,\n key: field('key'),\n strValue: field('strValue'),\n numValue: field('numValue'),\n };\n }\n\n /** Resolve the `tags` column name from metadata (per the active naming strategy), for the raw LIKE. */\n private tagsColumn(em: EntityManager): string {\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n return runMeta.properties.tags?.fieldNames?.[0] ?? 'tags';\n }\n\n /** Wrap a JSON column reference so it can be `LIKE`d as text. Postgres `jsonb`/`json` rejects `LIKE`\n * directly (`operator does not exist: jsonb ~~ unknown`) — cast to `text`; MySQL JSON needs an\n * explicit `CHAR` cast; SQLite stores JSON as text already, so no cast. */\n private jsonAsText(em: EntityManager, colRef: string): string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n if (platform.includes('postgre')) return `${colRef}::text`;\n if (platform.includes('mysql') || platform.includes('mariadb'))\n return `CAST(${colRef} AS CHAR)`;\n return colRef;\n }\n\n /** Quote an identifier per the active SQL driver (MySQL/MariaDB backtick, others double-quote). */\n private idQuote(em: EntityManager): (id: string) => string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n const isMysql = platform.includes('mysql') || platform.includes('mariadb');\n const ch = isMysql ? '`' : '\"';\n return (id: string) => `${ch}${id}${ch}`;\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(StepCheckpointEntity, { runId }, { orderBy: { seq: 'asc' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(\n StepCheckpointEntity,\n { runId, name },\n { orderBy: { seq: 'desc' } },\n );\n return entity ? fromCheckpointEntity(entity) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const em = this.orm.em.fork();\n const rows = await em.find(\n StepCheckpointEntity,\n { runId, $or: prefixes.map((p) => ({ name: { $like: `${p}%` } })) },\n { orderBy: { seq: 'asc' } },\n );\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(SignalWaiterEntity, { ...waiter });\n await em.flush();\n }\n\n async takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(SignalWaiterEntity, { token });\n if (!entity) return null;\n const waiter: SignalWaiter = { token: entity.token, runId: entity.runId, seq: entity.seq };\n await em.remove(entity).flush();\n return waiter;\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(SignalWaiterEntity, { token: { $like: `${prefix}%` } });\n return rows.map((e) => ({ token: e.token, runId: e.runId, seq: e.seq }));\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n const em = this.orm.em.fork();\n const e = new BufferedSignalEntity();\n e.token = token;\n e.payload = payload ?? null;\n em.persist(e);\n await em.flush();\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(BufferedSignalEntity, { token }, { orderBy: { id: 'asc' } });\n if (!entity) return null;\n const payload = entity.payload ?? undefined;\n await em.remove(entity).flush();\n return { payload };\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n const e = new WorkflowRunEntity();\n e.id = run.id;\n e.workflow = run.workflow;\n e.workflowVersion = run.workflowVersion;\n e.status = run.status;\n e.input = run.input ?? null;\n e.output = run.output ?? null;\n e.error = run.error ?? null;\n if (run.wakeAt != null) e.wakeAt = new Date(run.wakeAt);\n if (run.lockedBy !== undefined) e.lockedBy = run.lockedBy;\n if (run.lockedUntil != null) e.lockedUntil = new Date(run.lockedUntil);\n if (run.recoveryAttempts !== undefined) e.recoveryAttempts = run.recoveryAttempts;\n e.tags = run.tags ?? null;\n e.searchAttributes = run.searchAttributes ?? null;\n e.priority = run.priority ?? null;\n e.createdAt = run.createdAt;\n e.updatedAt = run.updatedAt;\n return e;\n}\n\nfunction fromRunEntity(e: WorkflowRunEntity): WorkflowRun {\n return {\n id: e.id,\n workflow: e.workflow,\n workflowVersion: e.workflowVersion,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n wakeAt: e.wakeAt?.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil?.getTime(),\n recoveryAttempts: e.recoveryAttempts ?? undefined,\n tags: e.tags ?? undefined,\n searchAttributes: e.searchAttributes ?? undefined,\n priority: e.priority ?? undefined,\n createdAt: e.createdAt,\n updatedAt: e.updatedAt,\n };\n}\n\nfunction toCheckpointEntity(cp: StepCheckpoint): StepCheckpointEntity {\n const e = new StepCheckpointEntity();\n e.runId = cp.runId;\n e.seq = cp.seq;\n e.name = cp.name;\n e.kind = cp.kind;\n e.stepId = cp.stepId;\n e.status = cp.status;\n e.input = cp.input ?? null;\n e.output = cp.output ?? null;\n e.error = cp.error ?? null;\n e.events = cp.events ?? null;\n e.attempts = cp.attempts;\n if (cp.workerGroup !== undefined) e.workerGroup = cp.workerGroup;\n if (cp.parallelGroup !== undefined) e.parallelGroup = cp.parallelGroup;\n if (cp.wakeAt != null) e.wakeAt = new Date(cp.wakeAt);\n e.enqueuedAt = cp.enqueuedAt;\n e.startedAt = cp.startedAt;\n e.finishedAt = cp.finishedAt;\n return e;\n}\n\nfunction fromCheckpointEntity(e: StepCheckpointEntity): StepCheckpoint {\n return {\n runId: e.runId,\n seq: e.seq,\n name: e.name,\n kind: e.kind,\n stepId: e.stepId,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n events: (e.events ?? undefined) as StepEvent[] | undefined,\n attempts: e.attempts,\n workerGroup: e.workerGroup ?? undefined,\n parallelGroup: e.parallelGroup ?? undefined,\n wakeAt: e.wakeAt?.getTime(),\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n","import type { MikroORM } from '@mikro-orm/core';\n\n/**\n * The tables this store owns. Kept in sync with the `tableName`s in `./entities`. Used to scope\n * schema management to our own tables (see {@link ensureMikroOrmDurableSchema}).\n */\nconst DURABLE_TABLE_NAMES = new Set([\n 'durable_workflow_runs',\n 'durable_step_checkpoints',\n 'durable_run_attributes',\n 'durable_signal_waiters',\n 'durable_buffered_signals',\n]);\n\n/**\n * The table a DDL statement targets, lower-cased; undefined otherwise. Recognizes `create table` /\n * `alter table` (the table name follows immediately) and standalone `create [unique] index ... on\n * <table>` (the table follows `on`). MikroORM adds an index to an EXISTING table as `alter table ...\n * add index` on MySQL but as a standalone `create index ... on ...` on Postgres/SQLite — without the\n * second form those index statements would be dropped by the durable-table filter and never applied.\n */\nfunction targetTable(statement: string): string | undefined {\n const match = statement.match(\n /^(?:create\\s+(?:unique\\s+)?index\\s+.+?\\s+on\\s+|(?:create|alter)\\s+table\\s+(?:if\\s+not\\s+exists\\s+)?)[`\"']?([\\w$]+)[`\"']?/i,\n );\n return match?.[1]?.toLowerCase();\n}\n\n/**\n * Whether a statement adds structure the store REQUIRES — a failure here is fatal (the store can't\n * run without the table/column/index). A column TYPE alignment (`modify`/`alter column ... type`)\n * is best-effort: the column already holds the data (the store serializes JSON to/from it regardless\n * of the declared type), so failing to converge its type — e.g. a legacy `longtext` value that won't\n * cast to `json` because it was truncated under an older `text` column — must NOT crash boot.\n */\nfunction isRequiredStructure(statement: string): boolean {\n return /\\b(?:create\\s+table|create\\s+(?:unique\\s+)?index|add\\s+(?:column|index|constraint|key|unique|fulltext))\\b/i.test(\n statement,\n );\n}\n\n/**\n * Idempotently create/extend ONLY the durable tables (additive — never drops). Safe to run on every\n * boot (auto-schema) and from a MikroORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables extends Migration {\n * async up() { await ensureMikroOrmDurableSchema(this.getEntityManager().getOrm()); }\n * }\n * ```\n *\n * It does NOT run a whole-ORM `schema.update()`. When the store shares the host app's ORM (the\n * recommended setup — a single ORM avoids MikroORM's global-metadata clobber between instances),\n * a full `schema.update()` would try to reconcile EVERY app table to the entity metadata: on a\n * migration-managed schema that means recreating the migrations table and dropping app foreign keys.\n * Instead we compute the safe additive diff and execute only the statements that target our own\n * `durable_*` tables. `getUpdateSchemaSQL` already emits a `create table` only for a missing table\n * and `alter table ... add` only for missing columns, so existing tables — and the entire rest of\n * the host schema — are left untouched.\n */\nexport async function ensureMikroOrmDurableSchema(orm: MikroORM): Promise<void> {\n const sql = await orm.schema.getUpdateSchemaSQL({ safe: true });\n const ours = sql\n .split(';')\n .map((statement) => statement.trim())\n .filter((statement) => {\n const table = targetTable(statement);\n return table !== undefined && DURABLE_TABLE_NAMES.has(table);\n });\n const connection = orm.em.getConnection();\n for (const statement of ours) {\n try {\n await connection.execute(statement);\n } catch (err) {\n // Required structure (create table / add column / add index) must succeed — rethrow.\n if (isRequiredStructure(statement)) {\n throw err;\n }\n // A best-effort type alignment (e.g. legacy `longtext` → `json`) failed, typically because an\n // existing column holds a value that can't cast to the target type (a relic of an older column\n // type). The column already stores the data and the store reads/writes it via serialization, so\n // leave it as-is and continue rather than crashing boot. Surfaced so the data can be repaired\n // out of band (e.g. `UPDATE … SET col = NULL WHERE JSON_VALID(col) = 0` for a disposable column).\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] skipped a non-structural durable-schema statement that failed; the column is left as-is (functional) — repair the data out of band to converge its type. Statement: ${statement} — ${message}`,\n );\n }\n }\n\n // Converge collation last, once the tables exist (whether just created above or pre-existing).\n await alignDurableCollation(orm);\n}\n\n/** A MySQL identifier we are willing to interpolate into DDL (charset / collation names). */\nfunction isSafeSqlIdentifier(value: string): boolean {\n return /^[A-Za-z0-9_]+$/.test(value);\n}\n\n/**\n * Align each durable table's collation to the ORM's configured `collate` (MySQL/MariaDB only).\n *\n * MikroORM's auto-schema (`getUpdateSchemaSQL`) creates tables with the server's DEFAULT collation —\n * on MySQL 8.4 that's `utf8mb4_0900_ai_ci` — and ignores the `collate` config option. When the host\n * app pins a different collation on its own tables (commonly `utf8mb4_unicode_ci` via migrations), a\n * JOIN between a durable table and an app table throws \"Illegal mix of collations\". We converge it\n * here: read the configured collation and `CONVERT` only the durable tables whose collation differs.\n *\n * Idempotent (matching tables are skipped), non-fatal (a CONVERT failure is warned, never crashes\n * boot), and a no-op when no `collate` is configured or the platform isn't MySQL/MariaDB.\n */\nasync function alignDurableCollation(orm: MikroORM): Promise<void> {\n const platform = String(orm.em.getPlatform().constructor.name).toLowerCase();\n if (!platform.includes('mysql') && !platform.includes('maria')) {\n return;\n }\n const collate = orm.config.get('collate');\n if (typeof collate !== 'string' || !isSafeSqlIdentifier(collate)) {\n return;\n }\n // utf8mb4_unicode_ci → utf8mb4. CONVERT TO needs the charset that owns the collation.\n const charset = collate.split('_')[0];\n if (!charset || !isSafeSqlIdentifier(charset)) {\n return;\n }\n const connection = orm.em.getConnection();\n for (const table of DURABLE_TABLE_NAMES) {\n try {\n const rows = (await connection.execute(\n 'select table_collation as collation from information_schema.tables where table_schema = database() and table_name = ? limit 1',\n [table],\n )) as Array<{ collation?: string | null }>;\n const current = rows[0]?.collation;\n // Table absent (nothing to align) or already at the target collation — skip.\n if (current == null || current === collate) {\n continue;\n }\n await connection.execute(\n `alter table \\`${table}\\` convert to character set ${charset} collate ${collate}`,\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] could not align collation for \\`${table}\\` to ${collate} (left as-is): ${message}`,\n );\n }\n }\n}\n"],"mappings":"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","names":["snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","recoveryAttempts","tags","searchAttributes","priority","createdAt","updatedAt","StepCheckpointEntity","runId","seq","name","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt","RunAttributeEntity","key","strValue","numValue","SignalWaiterEntity","token","BufferedSignalEntity","payload","durableEntities","options","col","workflowRuns","EntitySchema","class","tableName","indexes","properties","type","primary","fieldName","nullable","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","autoincrement","index","ENTITIES","DURABLE_TABLE_NAMES","Set","targetTable","statement","match","toLowerCase","isRequiredStructure","test","ensureMikroOrmDurableSchema","orm","sql","schema","getUpdateSchemaSQL","safe","ours","split","map","trim","filter","table","undefined","has","connection","em","getConnection","execute","err","message","Error","String","console","warn","alignDurableCollation","isSafeSqlIdentifier","value","platform","getPlatform","name","includes","collate","config","get","charset","rows","current","collation","MikroOrmStateStore","orm","ensureSchema","ensureMikroOrmDurableSchema","createRun","run","em","fork","create","WorkflowRunEntity","toRunEntity","flush","reindexAttributes","id","searchAttributes","updateRun","runId","patch","entity","findOneOrFail","Object","assign","fromRunEntity","attributes","forked","nativeDelete","RunAttributeEntity","rows","normalizeAttributeRows","length","insertMany","getRun","findOne","deleteRun","StepCheckpointEntity","SignalWaiterEntity","pruneTerminalRuns","policy","nowMs","limit","statuses","status","$in","ids","Set","maxAge","cutoff","Date","parseDuration","find","updatedAt","$lt","fields","orderBy","r","add","maxCount","size","offset","idList","slice","transactional","tem","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","upsert","toCheckpointEntity","transaction","work","raw","cp","listIncompleteRuns","map","listPendingRuns","createdAt","listDueTimers","wakeAt","$ne","$lte","tryLockRun","owner","leaseUntilMs","affected","nativeUpdate","$or","lockedUntil","lockedBy","releaseRunLock","renewRunLock","listRuns","query","where","workflow","$and","tag","quote","idQuote","cols","attributeColumns","qb","createQueryBuilder","tagsCol","tagsColumn","pattern","replace","colExpr","jsonAsText","andWhere","f","attributeExistsSql","getResultList","alias","col","attributeColumnFor","numValue","strValue","cmp","sqlComparator","op","operand","attributeOperand","literal","String","a","table","sub","outerId","runPk","key","meta","getMetadata","get","field","prop","props","p","name","fieldNames","runMeta","properties","tableName","tags","colRef","platform","getPlatform","toLowerCase","includes","isMysql","ch","listCheckpoints","getLatestCheckpointByName","undefined","listCheckpointsByNamePrefix","prefixes","$like","putSignalWaiter","waiter","takeSignalWaiter","token","remove","listSignalWaiters","prefix","e","bufferSignal","payload","BufferedSignalEntity","persist","takeBufferedSignal","workflowVersion","input","output","error","recoveryAttempts","priority","getTime","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/entities.ts","../src/mikro-orm-state-store.ts","../src/schema.ts"],"sourcesContent":["export * from './entities';\nexport * from './mikro-orm-state-store';\nexport * from './schema';\n","import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema } from '@mikro-orm/core';\n\n/**\n * How property names map to physical column names. The durable tables are adapter-agnostic: a run\n * written by one store adapter must be readable by another, so every adapter has to agree on the\n * physical column names. The default is `'snake_case'` (the canonical, matched by the TypeORM,\n * Prisma and Drizzle adapters). `'preserve'` keeps the camelCase property name verbatim (what the\n * old TypeORM/Prisma adapters produced before they were pinned). A function lets you supply any\n * custom mapping.\n *\n * The choice is pinned EXPLICITLY onto the entity schema rather than left to the host ORM's naming\n * strategy — depending on the host strategy is what silently diverged the adapters and broke a store\n * swap against an existing table.\n */\nexport type DurableColumnNaming = 'snake_case' | 'preserve' | ((property: string) => string);\n\nfunction snakeCase(property: string): string {\n return property.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nfunction columnNamer(naming: DurableColumnNaming): (property: string) => string {\n if (naming === 'snake_case') return snakeCase;\n if (naming === 'preserve') return (property) => property;\n return naming;\n}\n\n// Plain classes (no decorators): the schema/column mapping lives on the EntitySchema built by\n// `durableEntities`, so the same class can be registered under different column names. The store\n// still references these classes for `em.create`/`getMetadata`.\n\nexport class WorkflowRunEntity {\n id!: string;\n workflow!: string;\n workflowVersion!: string;\n status!: RunStatus;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n wakeAt?: Date;\n lockedBy?: string;\n lockedUntil?: Date;\n recoveryAttempts?: number;\n tags?: string[] | null;\n searchAttributes?: Record<string, string | number | boolean> | null;\n priority?: number | null;\n namespace!: string;\n createdAt!: Date;\n updatedAt!: Date;\n}\n\nexport class StepCheckpointEntity {\n runId!: string;\n seq!: number;\n name!: string;\n kind!: StepKind;\n stepId!: string;\n status!: 'pending' | 'running' | 'completed' | 'failed';\n input?: unknown;\n output?: unknown;\n error?: unknown;\n events?: unknown;\n attempts!: number;\n workerGroup?: string;\n parallelGroup?: string;\n wakeAt?: Date;\n enqueuedAt?: Date;\n startedAt!: Date;\n finishedAt!: Date;\n}\n\n/**\n * Normalized side-table for search attributes: one row per (run, key), so range/equality predicates\n * push DOWN into SQL (an EXISTS join indexed on `(key, numValue)` / `(key, strValue)`) instead of a\n * coarse scan + in-process filter. Maintained on every createRun/updateRun. Numbers land in\n * `numValue`, strings/booleans in `strValue` (booleans as \"true\"/\"false\"); see normalizeAttributeRows.\n */\nexport class RunAttributeEntity {\n runId!: string;\n key!: string;\n strValue?: string | null;\n numValue?: number | null;\n}\n\nexport class SignalWaiterEntity {\n token!: string;\n runId!: string;\n seq!: number;\n parallelGroup?: string | null;\n}\n\nexport class BufferedSignalEntity {\n id!: number;\n token!: string;\n payload?: unknown;\n}\n\n/**\n * Build the durable MikroORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your MikroORM config / `MikroOrmModule.forFeature`.\n *\n * ```ts\n * entities: durableEntities({ naming: 'snake_case' })\n * ```\n */\nexport function durableEntities(options: { naming?: DurableColumnNaming } = {}): EntitySchema[] {\n const col = columnNamer(options.naming ?? 'snake_case');\n\n const workflowRuns = new EntitySchema<WorkflowRunEntity>({\n class: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n // Indexes mirror the Prisma adapter so a store swap keeps the same plan. The poller hits these\n // every tick: `(status, wakeAt)` serves both the due-timer scan (status='suspended' AND wakeAt<=now)\n // and, via its `status` prefix, the pending/incomplete status lookups; `(workflow, status)` serves\n // the per-workflow timeout sweep. Without them every poll is a full scan of a table that only grows.\n indexes: [\n { name: 'durable_workflow_runs_status_wake_at_idx', properties: ['status', 'wakeAt'] },\n { name: 'durable_workflow_runs_workflow_status_idx', properties: ['workflow', 'status'] },\n {\n name: 'durable_workflow_runs_namespace_status_idx',\n properties: ['namespace', 'status', 'createdAt'],\n },\n ],\n properties: {\n id: { type: 'string', primary: true, fieldName: col('id') },\n workflow: { type: 'string', fieldName: col('workflow') },\n workflowVersion: { type: 'string', fieldName: col('workflowVersion') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n lockedBy: { type: 'string', nullable: true, fieldName: col('lockedBy') },\n lockedUntil: { type: 'Date', nullable: true, fieldName: col('lockedUntil') },\n recoveryAttempts: { type: 'integer', nullable: true, fieldName: col('recoveryAttempts') },\n tags: { type: 'json', nullable: true, fieldName: col('tags') },\n searchAttributes: { type: 'json', nullable: true, fieldName: col('searchAttributes') },\n priority: { type: 'integer', nullable: true, fieldName: col('priority') },\n namespace: { type: 'string', default: 'default', fieldName: col('namespace') },\n createdAt: { type: 'Date', fieldName: col('createdAt') },\n updatedAt: { type: 'Date', fieldName: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n class: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n seq: { type: 'integer', primary: true, fieldName: col('seq') },\n name: { type: 'string', fieldName: col('name') },\n kind: { type: 'string', fieldName: col('kind') },\n stepId: { type: 'string', fieldName: col('stepId') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n events: { type: 'json', nullable: true, fieldName: col('events') },\n attempts: { type: 'integer', fieldName: col('attempts') },\n workerGroup: { type: 'string', nullable: true, fieldName: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group string so the dashboard\n // renders them as one \"ran in parallel\" group. The core engine sets it (incl. from a remote\n // worker's recordStep), but it was never persisted here — so the fan always rendered as N\n // sequential singles. Nullable: only fan-out steps carry it.\n parallelGroup: { type: 'string', nullable: true, fieldName: col('parallelGroup') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n enqueuedAt: { type: 'Date', nullable: true, fieldName: col('enqueuedAt') },\n startedAt: { type: 'Date', fieldName: col('startedAt') },\n finishedAt: { type: 'Date', fieldName: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n class: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n // The search-attribute EXISTS join (see the class docstring) pushes equality/range predicates down\n // onto `(key, numValue)` / `(key, strValue)`; mirror the Prisma adapter so those scans stay indexed.\n indexes: [\n { name: 'durable_run_attributes_key_num_idx', properties: ['key', 'numValue'] },\n { name: 'durable_run_attributes_key_str_idx', properties: ['key', 'strValue'] },\n ],\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n key: { type: 'string', primary: true, fieldName: col('key') },\n strValue: { type: 'string', nullable: true, fieldName: col('strValue') },\n // `float`/`double` is portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, fieldName: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n class: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n properties: {\n token: { type: 'string', primary: true, fieldName: col('token') },\n runId: { type: 'string', fieldName: col('runId') },\n seq: { type: 'integer', fieldName: col('seq') },\n // A ctx.gather_children/ctx.all child fan-out tags every awaited child with the same group; the\n // engine threads it onto the waiter so the resolving `signal:child:` checkpoint carries it and the\n // dashboard renders the fan as one parallel group. Nullable: only fan-out child waiters carry it.\n parallelGroup: { type: 'string', nullable: true, fieldName: col('parallelGroup') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n class: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n properties: {\n id: { type: 'integer', primary: true, autoincrement: true, fieldName: col('id') },\n token: { type: 'string', index: true, fieldName: col('token') },\n payload: { type: 'json', nullable: true, fieldName: col('payload') },\n },\n });\n\n return [workflowRuns, stepCheckpoints, runAttributes, signalWaiters, bufferedSignals];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import {\n type AttributeFilter,\n type RetentionPolicy,\n type RunQuery,\n type SignalWaiter,\n type StateStore,\n type StepCheckpoint,\n type StepError,\n type StepEvent,\n type WorkflowRun,\n attributeColumnFor,\n attributeOperand,\n normalizeAttributeRows,\n parseDuration,\n sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport type { EntityManager, MikroORM } from '@mikro-orm/core';\n\n/** Minimal structural surface of the SQL EntityManager's QueryBuilder (from `@mikro-orm/knex`),\n * typed here so the adapter needn't add a hard dependency on the knex types — a real SQL-driver EM\n * satisfies it at runtime. */\ninterface DurableQueryBuilder<T> {\n where(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n andWhere(cond: string): DurableQueryBuilder<T>;\n orderBy(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n limit(n: number): DurableQueryBuilder<T>;\n offset(n: number): DurableQueryBuilder<T>;\n getResultList(): Promise<T[]>;\n}\ntype SqlEm = EntityManager & {\n createQueryBuilder<T>(entity: unknown, alias: string): DurableQueryBuilder<T>;\n};\n\n/** Resolved side-table column names (per the active naming strategy) for the raw EXISTS pushdown. */\ninterface AttributeColumns {\n table: string;\n /** Side-table FK column (the run id on `durable_run_attributes`). */\n runId: string;\n /** Run table PK column (the `id` on `durable_workflow_runs`) used for the outer correlation. */\n runPk: string;\n key: string;\n strValue: string;\n numValue: string;\n}\nimport {\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { ensureMikroOrmDurableSchema } from './schema';\n\n/**\n * MikroORM-backed `StateStore`. Works on any MikroORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps and `wakeAt` use native datetime columns. Each operation runs on a forked\n * EntityManager so it owns its unit of work.\n */\nexport class MikroOrmStateStore implements StateStore {\n constructor(private readonly orm: MikroORM) {}\n\n async ensureSchema(): Promise<void> {\n await ensureMikroOrmDurableSchema(this.orm);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n const em = this.orm.em.fork();\n em.create(WorkflowRunEntity, toRunEntity(run));\n await em.flush();\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n const em = this.orm.em.fork();\n const entity = await em.findOneOrFail(WorkflowRunEntity, { id: runId });\n Object.assign(entity, toRunEntity({ ...fromRunEntity(entity), ...patch } as WorkflowRun));\n await em.flush();\n // Keep the side-table in step with the run's attributes whenever they're patched.\n if ('searchAttributes' in patch) await this.reindexAttributes(runId, patch.searchAttributes);\n }\n\n /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors\n * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */\n private async reindexAttributes(\n runId: string,\n attributes: WorkflowRun['searchAttributes'],\n forked?: EntityManager,\n ): Promise<void> {\n const em = forked ?? this.orm.em.fork();\n await em.nativeDelete(RunAttributeEntity, { runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await em.insertMany(RunAttributeEntity, rows);\n }\n\n async getRun(runId: string): Promise<WorkflowRun | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(WorkflowRunEntity, { id: runId });\n return entity ? fromRunEntity(entity) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n // Children first, then the run row — checkpoints, signal waiters, and the normalized\n // attribute side-table, so nothing dangles after the run is gone.\n await em.nativeDelete(StepCheckpointEntity, { runId });\n await em.nativeDelete(SignalWaiterEntity, { runId });\n await em.nativeDelete(RunAttributeEntity, { runId });\n await em.nativeDelete(WorkflowRunEntity, { id: runId });\n }\n\n async pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number> {\n if (policy.statuses.length === 0 || limit <= 0) return 0;\n const em = this.orm.em.fork();\n const status = { $in: policy.statuses };\n // Collect ids that violate EITHER bound (most-restrictive keep): too old, or past the count cap.\n const ids = new Set<string>();\n\n if (policy.maxAge != null) {\n const cutoff = new Date(nowMs - parseDuration(policy.maxAge));\n const rows = await em.find(\n WorkflowRunEntity,\n { status, updatedAt: { $lt: cutoff } },\n { fields: ['id'], orderBy: { updatedAt: 'asc' }, limit }, // oldest first\n );\n for (const r of rows) ids.add(r.id);\n }\n\n if (policy.maxCount != null && ids.size < limit) {\n // Everything beyond the newest `maxCount` rows in the status set — skip the kept window via offset.\n const rows = await em.find(\n WorkflowRunEntity,\n { status },\n {\n fields: ['id'],\n orderBy: { updatedAt: 'desc', id: 'desc' },\n limit,\n offset: policy.maxCount,\n },\n );\n for (const r of rows) {\n ids.add(r.id);\n if (ids.size >= limit) break;\n }\n }\n\n if (ids.size === 0) return 0;\n const idList = [...ids].slice(0, limit);\n const runId = { $in: idList };\n // Cascade children then runs (mirrors deleteRun) in one transaction so a pruned run never dangles.\n await em.transactional(async (tem) => {\n await tem.nativeDelete(StepCheckpointEntity, { runId });\n await tem.nativeDelete(SignalWaiterEntity, { runId });\n await tem.nativeDelete(RunAttributeEntity, { runId });\n await tem.nativeDelete(WorkflowRunEntity, { id: runId });\n });\n return idList.length;\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(StepCheckpointEntity, { runId, seq });\n return entity ? fromCheckpointEntity(entity) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(checkpoint));\n await em.flush();\n }\n\n async transaction<T>(\n work: (tx: {\n raw: unknown;\n saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;\n }) => Promise<T>,\n ): Promise<T> {\n return this.orm.em.fork().transactional(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, {\n status: { $in: ['running', 'cancelling'] },\n ...(namespace !== undefined ? { namespace } : {}),\n });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(\n WorkflowRunEntity,\n { status: 'pending', ...(namespace !== undefined ? { namespace } : {}) },\n { orderBy: { createdAt: 'asc' }, limit }, // FIFO dispatch\n );\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, {\n status: 'suspended',\n wakeAt: { $ne: null, $lte: new Date(nowMs) },\n ...(namespace !== undefined ? { namespace } : {}),\n });\n return rows.map(fromRunEntity);\n }\n\n async tryLockRun(\n runId: string,\n owner: string,\n leaseUntilMs: number,\n nowMs: number,\n ): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, $or: [{ lockedUntil: null }, { lockedUntil: { $lte: new Date(nowMs) } }] },\n { lockedBy: owner, lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n await em.nativeUpdate(WorkflowRunEntity, { id: runId }, { lockedBy: null, lockedUntil: null });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, lockedBy: owner },\n { lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const where: Record<string, unknown> = {};\n if (query.workflow) where.workflow = query.workflow;\n if (query.namespace !== undefined) where.namespace = query.namespace;\n // `status IN (...)`; an empty set matches nothing (mirrors the in-memory store). When both the\n // single `status` and `statuses` are set, AND them so the narrower set wins.\n if (query.status && query.statuses) {\n where.$and = [{ status: query.status }, { status: { $in: query.statuses } }];\n } else if (query.status) {\n where.status = query.status;\n } else if (query.statuses) {\n where.status = { $in: query.statuses };\n }\n const orderBy = { createdAt: 'desc' as const }; // newest first — recent runs on top in the dashboard\n // `tags`/attributes need raw SQL pushed through the QueryBuilder:\n // - tag: `tags` is a JSON column (native json/jsonb on PG/MySQL), and MikroORM JSON-serializes a\n // `{ $like }` operand (wrapping/escaping the LIKE pattern) so a plain where corrupts it to no\n // matches. A raw LIKE on the column-as-text matches the quoted token so `etl` doesn't match\n // `etl-foo`. Postgres `jsonb` rejects `LIKE` directly, so cast to text per dialect.\n // - attributes: each filter becomes a raw EXISTS against the normalized side-table, so the DB\n // filters AND paginates — no full scan + in-process filter (ANDed: one EXISTS per filter).\n // Use a QueryBuilder with a fixed root alias `r` so the raw correlations are stable across drivers.\n if (query.tag || query.attributes?.length) {\n const quote = this.idQuote(em);\n const cols = this.attributeColumns(em);\n const qb = (em as SqlEm)\n .createQueryBuilder<WorkflowRunEntity>(WorkflowRunEntity, 'r')\n .where(where)\n .orderBy(orderBy);\n if (query.tag) {\n const tagsCol = this.tagsColumn(em);\n const pattern = `%\"${query.tag.replace(/'/g, \"''\")}\"%`;\n const colExpr = this.jsonAsText(em, `${quote('r')}.${quote(tagsCol)}`);\n qb.andWhere(`${colExpr} LIKE '${pattern}'`);\n }\n for (const f of query.attributes ?? []) {\n qb.andWhere(this.attributeExistsSql(f, quote, 'r', cols));\n }\n if (query.limit != null) qb.limit(query.limit);\n if (query.offset != null) qb.offset(query.offset);\n const rows = await qb.getResultList();\n return rows.map(fromRunEntity);\n }\n const rows = await em.find(WorkflowRunEntity, where, {\n ...(query.limit != null ? { limit: query.limit } : {}),\n ...(query.offset != null ? { offset: query.offset } : {}),\n orderBy,\n });\n return rows.map(fromRunEntity);\n }\n\n /** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run\n * alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):\n * EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric\n * operands compare the num column, everything else the str column. Identifiers are quoted per\n * driver and operands are inlined as literals (string operands are escaped by doubling quotes). */\n private attributeExistsSql(\n f: AttributeFilter,\n quote: (id: string) => string,\n alias: string,\n cols: AttributeColumns,\n ): string {\n const col = attributeColumnFor(f) === 'numValue' ? cols.numValue : cols.strValue;\n const cmp = sqlComparator(f.op);\n const operand = attributeOperand(f);\n const literal =\n typeof operand === 'number' ? String(operand) : `'${String(operand).replace(/'/g, \"''\")}'`;\n const a = quote(cols.table);\n const sub = quote('a');\n const outerId = `${quote(alias)}.${quote(cols.runPk)}`; // run PK column on the outer alias\n return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, \"''\")}' AND ${sub}.${quote(col)} ${cmp} ${literal})`;\n }\n\n /** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches\n * the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is\n * reused for BOTH the outer correlation and the side-table FK (they share the same fieldName). */\n private attributeColumns(em: EntityManager): AttributeColumns {\n const meta = em.getMetadata().get(RunAttributeEntity);\n const field = (prop: string) =>\n meta.props.find((p) => p.name === prop)?.fieldNames?.[0] ?? prop;\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n const runPk = runMeta.properties.id?.fieldNames?.[0] ?? 'id';\n return {\n table: meta.tableName,\n runId: field('runId'),\n runPk,\n key: field('key'),\n strValue: field('strValue'),\n numValue: field('numValue'),\n };\n }\n\n /** Resolve the `tags` column name from metadata (per the active naming strategy), for the raw LIKE. */\n private tagsColumn(em: EntityManager): string {\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n return runMeta.properties.tags?.fieldNames?.[0] ?? 'tags';\n }\n\n /** Wrap a JSON column reference so it can be `LIKE`d as text. Postgres `jsonb`/`json` rejects `LIKE`\n * directly (`operator does not exist: jsonb ~~ unknown`) — cast to `text`; MySQL JSON needs an\n * explicit `CHAR` cast; SQLite stores JSON as text already, so no cast. */\n private jsonAsText(em: EntityManager, colRef: string): string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n if (platform.includes('postgre')) return `${colRef}::text`;\n if (platform.includes('mysql') || platform.includes('mariadb'))\n return `CAST(${colRef} AS CHAR)`;\n return colRef;\n }\n\n /** Quote an identifier per the active SQL driver (MySQL/MariaDB backtick, others double-quote). */\n private idQuote(em: EntityManager): (id: string) => string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n const isMysql = platform.includes('mysql') || platform.includes('mariadb');\n const ch = isMysql ? '`' : '\"';\n return (id: string) => `${ch}${id}${ch}`;\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(StepCheckpointEntity, { runId }, { orderBy: { seq: 'asc' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(\n StepCheckpointEntity,\n { runId, name },\n { orderBy: { seq: 'desc' } },\n );\n return entity ? fromCheckpointEntity(entity) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const em = this.orm.em.fork();\n const rows = await em.find(\n StepCheckpointEntity,\n { runId, $or: prefixes.map((p) => ({ name: { $like: `${p}%` } })) },\n { orderBy: { seq: 'asc' } },\n );\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(SignalWaiterEntity, {\n token: waiter.token,\n runId: waiter.runId,\n seq: waiter.seq,\n parallelGroup: waiter.parallelGroup ?? null,\n });\n await em.flush();\n }\n\n async takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(SignalWaiterEntity, { token });\n if (!entity) return null;\n const waiter: SignalWaiter = {\n token: entity.token,\n runId: entity.runId,\n seq: entity.seq,\n parallelGroup: entity.parallelGroup ?? undefined,\n };\n await em.remove(entity).flush();\n return waiter;\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(SignalWaiterEntity, { token: { $like: `${prefix}%` } });\n return rows.map((e) => ({\n token: e.token,\n runId: e.runId,\n seq: e.seq,\n parallelGroup: e.parallelGroup ?? undefined,\n }));\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n const em = this.orm.em.fork();\n const e = new BufferedSignalEntity();\n e.token = token;\n e.payload = payload ?? null;\n em.persist(e);\n await em.flush();\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(BufferedSignalEntity, { token }, { orderBy: { id: 'asc' } });\n if (!entity) return null;\n const payload = entity.payload ?? undefined;\n await em.remove(entity).flush();\n return { payload };\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n const e = new WorkflowRunEntity();\n e.id = run.id;\n e.workflow = run.workflow;\n e.workflowVersion = run.workflowVersion;\n e.status = run.status;\n e.input = run.input ?? null;\n e.output = run.output ?? null;\n e.error = run.error ?? null;\n if (run.wakeAt != null) e.wakeAt = new Date(run.wakeAt);\n if (run.lockedBy !== undefined) e.lockedBy = run.lockedBy;\n if (run.lockedUntil != null) e.lockedUntil = new Date(run.lockedUntil);\n if (run.recoveryAttempts !== undefined) e.recoveryAttempts = run.recoveryAttempts;\n e.tags = run.tags ?? null;\n e.searchAttributes = run.searchAttributes ?? null;\n e.priority = run.priority ?? null;\n e.namespace = run.namespace ?? 'default';\n e.createdAt = run.createdAt;\n e.updatedAt = run.updatedAt;\n return e;\n}\n\nfunction fromRunEntity(e: WorkflowRunEntity): WorkflowRun {\n return {\n id: e.id,\n workflow: e.workflow,\n workflowVersion: e.workflowVersion,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n wakeAt: e.wakeAt?.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil?.getTime(),\n recoveryAttempts: e.recoveryAttempts ?? undefined,\n tags: e.tags ?? undefined,\n searchAttributes: e.searchAttributes ?? undefined,\n priority: e.priority ?? undefined,\n namespace: e.namespace,\n createdAt: e.createdAt,\n updatedAt: e.updatedAt,\n };\n}\n\nfunction toCheckpointEntity(cp: StepCheckpoint): StepCheckpointEntity {\n const e = new StepCheckpointEntity();\n e.runId = cp.runId;\n e.seq = cp.seq;\n e.name = cp.name;\n e.kind = cp.kind;\n e.stepId = cp.stepId;\n e.status = cp.status;\n e.input = cp.input ?? null;\n e.output = cp.output ?? null;\n e.error = cp.error ?? null;\n e.events = cp.events ?? null;\n e.attempts = cp.attempts;\n if (cp.workerGroup !== undefined) e.workerGroup = cp.workerGroup;\n if (cp.parallelGroup !== undefined) e.parallelGroup = cp.parallelGroup;\n if (cp.wakeAt != null) e.wakeAt = new Date(cp.wakeAt);\n e.enqueuedAt = cp.enqueuedAt;\n e.startedAt = cp.startedAt;\n e.finishedAt = cp.finishedAt;\n return e;\n}\n\nfunction fromCheckpointEntity(e: StepCheckpointEntity): StepCheckpoint {\n return {\n runId: e.runId,\n seq: e.seq,\n name: e.name,\n kind: e.kind,\n stepId: e.stepId,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n events: (e.events ?? undefined) as StepEvent[] | undefined,\n attempts: e.attempts,\n workerGroup: e.workerGroup ?? undefined,\n parallelGroup: e.parallelGroup ?? undefined,\n wakeAt: e.wakeAt?.getTime(),\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n","import type { MikroORM } from '@mikro-orm/core';\n\n/**\n * The tables this store owns. Kept in sync with the `tableName`s in `./entities`. Used to scope\n * schema management to our own tables (see {@link ensureMikroOrmDurableSchema}).\n */\nconst DURABLE_TABLE_NAMES = new Set([\n 'durable_workflow_runs',\n 'durable_step_checkpoints',\n 'durable_run_attributes',\n 'durable_signal_waiters',\n 'durable_buffered_signals',\n]);\n\n/**\n * The table a DDL statement targets, lower-cased; undefined otherwise. Recognizes `create table` /\n * `alter table` (the table name follows immediately) and standalone `create [unique] index ... on\n * <table>` (the table follows `on`). MikroORM adds an index to an EXISTING table as `alter table ...\n * add index` on MySQL but as a standalone `create index ... on ...` on Postgres/SQLite — without the\n * second form those index statements would be dropped by the durable-table filter and never applied.\n */\nfunction targetTable(statement: string): string | undefined {\n const match = statement.match(\n /^(?:create\\s+(?:unique\\s+)?index\\s+.+?\\s+on\\s+|(?:create|alter)\\s+table\\s+(?:if\\s+not\\s+exists\\s+)?)[`\"']?([\\w$]+)[`\"']?/i,\n );\n return match?.[1]?.toLowerCase();\n}\n\n/**\n * Whether a statement adds structure the store REQUIRES — a failure here is fatal (the store can't\n * run without the table/column/index). A column TYPE alignment (`modify`/`alter column ... type`)\n * is best-effort: the column already holds the data (the store serializes JSON to/from it regardless\n * of the declared type), so failing to converge its type — e.g. a legacy `longtext` value that won't\n * cast to `json` because it was truncated under an older `text` column — must NOT crash boot.\n */\nfunction isRequiredStructure(statement: string): boolean {\n return /\\b(?:create\\s+table|create\\s+(?:unique\\s+)?index|add\\s+(?:column|index|constraint|key|unique|fulltext))\\b/i.test(\n statement,\n );\n}\n\n/**\n * Idempotently create/extend ONLY the durable tables (additive — never drops). Safe to run on every\n * boot (auto-schema) and from a MikroORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables extends Migration {\n * async up() { await ensureMikroOrmDurableSchema(this.getEntityManager().getOrm()); }\n * }\n * ```\n *\n * It does NOT run a whole-ORM `schema.update()`. When the store shares the host app's ORM (the\n * recommended setup — a single ORM avoids MikroORM's global-metadata clobber between instances),\n * a full `schema.update()` would try to reconcile EVERY app table to the entity metadata: on a\n * migration-managed schema that means recreating the migrations table and dropping app foreign keys.\n * Instead we compute the safe additive diff and execute only the statements that target our own\n * `durable_*` tables. `getUpdateSchemaSQL` already emits a `create table` only for a missing table\n * and `alter table ... add` only for missing columns, so existing tables — and the entire rest of\n * the host schema — are left untouched.\n */\nexport async function ensureMikroOrmDurableSchema(orm: MikroORM): Promise<void> {\n const sql = await orm.schema.getUpdateSchemaSQL({ safe: true });\n const ours = sql\n .split(';')\n .map((statement) => statement.trim())\n .filter((statement) => {\n const table = targetTable(statement);\n return table !== undefined && DURABLE_TABLE_NAMES.has(table);\n });\n const connection = orm.em.getConnection();\n for (const statement of ours) {\n try {\n await connection.execute(statement);\n } catch (err) {\n // Required structure (create table / add column / add index) must succeed — rethrow.\n if (isRequiredStructure(statement)) {\n throw err;\n }\n // A best-effort type alignment (e.g. legacy `longtext` → `json`) failed, typically because an\n // existing column holds a value that can't cast to the target type (a relic of an older column\n // type). The column already stores the data and the store reads/writes it via serialization, so\n // leave it as-is and continue rather than crashing boot. Surfaced so the data can be repaired\n // out of band (e.g. `UPDATE … SET col = NULL WHERE JSON_VALID(col) = 0` for a disposable column).\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] skipped a non-structural durable-schema statement that failed; the column is left as-is (functional) — repair the data out of band to converge its type. Statement: ${statement} — ${message}`,\n );\n }\n }\n\n // Converge collation last, once the tables exist (whether just created above or pre-existing).\n await alignDurableCollation(orm);\n}\n\n/** A MySQL identifier we are willing to interpolate into DDL (charset / collation names). */\nfunction isSafeSqlIdentifier(value: string): boolean {\n return /^[A-Za-z0-9_]+$/.test(value);\n}\n\n/**\n * Align each durable table's collation to the ORM's configured `collate` (MySQL/MariaDB only).\n *\n * MikroORM's auto-schema (`getUpdateSchemaSQL`) creates tables with the server's DEFAULT collation —\n * on MySQL 8.4 that's `utf8mb4_0900_ai_ci` — and ignores the `collate` config option. When the host\n * app pins a different collation on its own tables (commonly `utf8mb4_unicode_ci` via migrations), a\n * JOIN between a durable table and an app table throws \"Illegal mix of collations\". We converge it\n * here: read the configured collation and `CONVERT` only the durable tables whose collation differs.\n *\n * Idempotent (matching tables are skipped), non-fatal (a CONVERT failure is warned, never crashes\n * boot), and a no-op when no `collate` is configured or the platform isn't MySQL/MariaDB.\n */\nasync function alignDurableCollation(orm: MikroORM): Promise<void> {\n const platform = String(orm.em.getPlatform().constructor.name).toLowerCase();\n if (!platform.includes('mysql') && !platform.includes('maria')) {\n return;\n }\n const collate = orm.config.get('collate');\n if (typeof collate !== 'string' || !isSafeSqlIdentifier(collate)) {\n return;\n }\n // utf8mb4_unicode_ci → utf8mb4. CONVERT TO needs the charset that owns the collation.\n const charset = collate.split('_')[0];\n if (!charset || !isSafeSqlIdentifier(charset)) {\n return;\n }\n const connection = orm.em.getConnection();\n for (const table of DURABLE_TABLE_NAMES) {\n try {\n const rows = (await connection.execute(\n 'select table_collation as collation from information_schema.tables where table_schema = database() and table_name = ? limit 1',\n [table],\n )) as Array<{ collation?: string | null }>;\n const current = rows[0]?.collation;\n // Table absent (nothing to align) or already at the target collation — skip.\n if (current == null || current === collate) {\n continue;\n }\n await connection.execute(\n `alter table \\`${table}\\` convert to character set ${charset} collate ${collate}`,\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] could not align collation for \\`${table}\\` to ${collate} (left as-is): ${message}`,\n );\n }\n }\n}\n"],"mappings":"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","names":["snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","recoveryAttempts","tags","searchAttributes","priority","namespace","createdAt","updatedAt","StepCheckpointEntity","runId","seq","name","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt","RunAttributeEntity","key","strValue","numValue","SignalWaiterEntity","token","BufferedSignalEntity","payload","durableEntities","options","col","workflowRuns","EntitySchema","class","tableName","indexes","properties","type","primary","fieldName","nullable","default","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","autoincrement","index","ENTITIES","DURABLE_TABLE_NAMES","Set","targetTable","statement","match","toLowerCase","isRequiredStructure","test","ensureMikroOrmDurableSchema","orm","sql","schema","getUpdateSchemaSQL","safe","ours","split","map","trim","filter","table","undefined","has","connection","em","getConnection","execute","err","message","Error","String","console","warn","alignDurableCollation","isSafeSqlIdentifier","value","platform","getPlatform","name","includes","collate","config","get","charset","rows","current","collation","MikroOrmStateStore","orm","ensureSchema","ensureMikroOrmDurableSchema","createRun","run","em","fork","create","WorkflowRunEntity","toRunEntity","flush","reindexAttributes","id","searchAttributes","updateRun","runId","patch","entity","findOneOrFail","Object","assign","fromRunEntity","attributes","forked","nativeDelete","RunAttributeEntity","rows","normalizeAttributeRows","length","insertMany","getRun","findOne","deleteRun","StepCheckpointEntity","SignalWaiterEntity","pruneTerminalRuns","policy","nowMs","limit","statuses","status","$in","ids","Set","maxAge","cutoff","Date","parseDuration","find","updatedAt","$lt","fields","orderBy","r","add","maxCount","size","offset","idList","slice","transactional","tem","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","upsert","toCheckpointEntity","transaction","work","raw","cp","listIncompleteRuns","namespace","undefined","map","listPendingRuns","createdAt","listDueTimers","wakeAt","$ne","$lte","tryLockRun","owner","leaseUntilMs","affected","nativeUpdate","$or","lockedUntil","lockedBy","releaseRunLock","renewRunLock","listRuns","query","where","workflow","$and","tag","quote","idQuote","cols","attributeColumns","qb","createQueryBuilder","tagsCol","tagsColumn","pattern","replace","colExpr","jsonAsText","andWhere","f","attributeExistsSql","getResultList","alias","col","attributeColumnFor","numValue","strValue","cmp","sqlComparator","op","operand","attributeOperand","literal","String","a","table","sub","outerId","runPk","key","meta","getMetadata","get","field","prop","props","p","name","fieldNames","runMeta","properties","tableName","tags","colRef","platform","getPlatform","toLowerCase","includes","isMysql","ch","listCheckpoints","getLatestCheckpointByName","listCheckpointsByNamePrefix","prefixes","$like","putSignalWaiter","waiter","token","parallelGroup","takeSignalWaiter","remove","listSignalWaiters","prefix","e","bufferSignal","payload","BufferedSignalEntity","persist","takeBufferedSignal","workflowVersion","input","output","error","recoveryAttempts","priority","getTime","kind","stepId","events","attempts","workerGroup","enqueuedAt","startedAt","finishedAt"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -30,6 +30,7 @@ declare class WorkflowRunEntity {
|
|
|
30
30
|
tags?: string[] | null;
|
|
31
31
|
searchAttributes?: Record<string, string | number | boolean> | null;
|
|
32
32
|
priority?: number | null;
|
|
33
|
+
namespace: string;
|
|
33
34
|
createdAt: Date;
|
|
34
35
|
updatedAt: Date;
|
|
35
36
|
}
|
|
@@ -68,6 +69,7 @@ declare class SignalWaiterEntity {
|
|
|
68
69
|
token: string;
|
|
69
70
|
runId: string;
|
|
70
71
|
seq: number;
|
|
72
|
+
parallelGroup?: string | null;
|
|
71
73
|
}
|
|
72
74
|
declare class BufferedSignalEntity {
|
|
73
75
|
id: number;
|
|
@@ -111,9 +113,9 @@ declare class MikroOrmStateStore implements StateStore {
|
|
|
111
113
|
raw: unknown;
|
|
112
114
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
113
115
|
}) => Promise<T>): Promise<T>;
|
|
114
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
115
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
116
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
116
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
117
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
118
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
117
119
|
tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
|
|
118
120
|
releaseRunLock(runId: string): Promise<void>;
|
|
119
121
|
renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ declare class WorkflowRunEntity {
|
|
|
30
30
|
tags?: string[] | null;
|
|
31
31
|
searchAttributes?: Record<string, string | number | boolean> | null;
|
|
32
32
|
priority?: number | null;
|
|
33
|
+
namespace: string;
|
|
33
34
|
createdAt: Date;
|
|
34
35
|
updatedAt: Date;
|
|
35
36
|
}
|
|
@@ -68,6 +69,7 @@ declare class SignalWaiterEntity {
|
|
|
68
69
|
token: string;
|
|
69
70
|
runId: string;
|
|
70
71
|
seq: number;
|
|
72
|
+
parallelGroup?: string | null;
|
|
71
73
|
}
|
|
72
74
|
declare class BufferedSignalEntity {
|
|
73
75
|
id: number;
|
|
@@ -111,9 +113,9 @@ declare class MikroOrmStateStore implements StateStore {
|
|
|
111
113
|
raw: unknown;
|
|
112
114
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
113
115
|
}) => Promise<T>): Promise<T>;
|
|
114
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
115
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
116
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
116
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
117
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
118
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
117
119
|
tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
|
|
118
120
|
releaseRunLock(runId: string): Promise<void>;
|
|
119
121
|
renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var WorkflowRunEntity = class {
|
|
|
31
31
|
tags;
|
|
32
32
|
searchAttributes;
|
|
33
33
|
priority;
|
|
34
|
+
namespace;
|
|
34
35
|
createdAt;
|
|
35
36
|
updatedAt;
|
|
36
37
|
};
|
|
@@ -72,6 +73,7 @@ var SignalWaiterEntity = class {
|
|
|
72
73
|
token;
|
|
73
74
|
runId;
|
|
74
75
|
seq;
|
|
76
|
+
parallelGroup;
|
|
75
77
|
};
|
|
76
78
|
var BufferedSignalEntity = class {
|
|
77
79
|
static {
|
|
@@ -104,6 +106,14 @@ function durableEntities(options = {}) {
|
|
|
104
106
|
"workflow",
|
|
105
107
|
"status"
|
|
106
108
|
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "durable_workflow_runs_namespace_status_idx",
|
|
112
|
+
properties: [
|
|
113
|
+
"namespace",
|
|
114
|
+
"status",
|
|
115
|
+
"createdAt"
|
|
116
|
+
]
|
|
107
117
|
}
|
|
108
118
|
],
|
|
109
119
|
properties: {
|
|
@@ -174,6 +184,11 @@ function durableEntities(options = {}) {
|
|
|
174
184
|
nullable: true,
|
|
175
185
|
fieldName: col("priority")
|
|
176
186
|
},
|
|
187
|
+
namespace: {
|
|
188
|
+
type: "string",
|
|
189
|
+
default: "default",
|
|
190
|
+
fieldName: col("namespace")
|
|
191
|
+
},
|
|
177
192
|
createdAt: {
|
|
178
193
|
type: "Date",
|
|
179
194
|
fieldName: col("createdAt")
|
|
@@ -333,6 +348,14 @@ function durableEntities(options = {}) {
|
|
|
333
348
|
seq: {
|
|
334
349
|
type: "integer",
|
|
335
350
|
fieldName: col("seq")
|
|
351
|
+
},
|
|
352
|
+
// A ctx.gather_children/ctx.all child fan-out tags every awaited child with the same group; the
|
|
353
|
+
// engine threads it onto the waiter so the resolving `signal:child:` checkpoint carries it and the
|
|
354
|
+
// dashboard renders the fan as one parallel group. Nullable: only fan-out child waiters carry it.
|
|
355
|
+
parallelGroup: {
|
|
356
|
+
type: "string",
|
|
357
|
+
nullable: true,
|
|
358
|
+
fieldName: col("parallelGroup")
|
|
336
359
|
}
|
|
337
360
|
}
|
|
338
361
|
});
|
|
@@ -598,7 +621,7 @@ var MikroOrmStateStore = class {
|
|
|
598
621
|
}, "saveCheckpoint")
|
|
599
622
|
}));
|
|
600
623
|
}
|
|
601
|
-
async listIncompleteRuns() {
|
|
624
|
+
async listIncompleteRuns(namespace) {
|
|
602
625
|
const em = this.orm.em.fork();
|
|
603
626
|
const rows = await em.find(WorkflowRunEntity, {
|
|
604
627
|
status: {
|
|
@@ -606,14 +629,20 @@ var MikroOrmStateStore = class {
|
|
|
606
629
|
"running",
|
|
607
630
|
"cancelling"
|
|
608
631
|
]
|
|
609
|
-
}
|
|
632
|
+
},
|
|
633
|
+
...namespace !== void 0 ? {
|
|
634
|
+
namespace
|
|
635
|
+
} : {}
|
|
610
636
|
});
|
|
611
637
|
return rows.map(fromRunEntity);
|
|
612
638
|
}
|
|
613
|
-
async listPendingRuns(limit) {
|
|
639
|
+
async listPendingRuns(limit, namespace) {
|
|
614
640
|
const em = this.orm.em.fork();
|
|
615
641
|
const rows = await em.find(WorkflowRunEntity, {
|
|
616
|
-
status: "pending"
|
|
642
|
+
status: "pending",
|
|
643
|
+
...namespace !== void 0 ? {
|
|
644
|
+
namespace
|
|
645
|
+
} : {}
|
|
617
646
|
}, {
|
|
618
647
|
orderBy: {
|
|
619
648
|
createdAt: "asc"
|
|
@@ -622,14 +651,17 @@ var MikroOrmStateStore = class {
|
|
|
622
651
|
});
|
|
623
652
|
return rows.map(fromRunEntity);
|
|
624
653
|
}
|
|
625
|
-
async listDueTimers(nowMs) {
|
|
654
|
+
async listDueTimers(nowMs, namespace) {
|
|
626
655
|
const em = this.orm.em.fork();
|
|
627
656
|
const rows = await em.find(WorkflowRunEntity, {
|
|
628
657
|
status: "suspended",
|
|
629
658
|
wakeAt: {
|
|
630
659
|
$ne: null,
|
|
631
660
|
$lte: new Date(nowMs)
|
|
632
|
-
}
|
|
661
|
+
},
|
|
662
|
+
...namespace !== void 0 ? {
|
|
663
|
+
namespace
|
|
664
|
+
} : {}
|
|
633
665
|
});
|
|
634
666
|
return rows.map(fromRunEntity);
|
|
635
667
|
}
|
|
@@ -676,6 +708,7 @@ var MikroOrmStateStore = class {
|
|
|
676
708
|
const em = this.orm.em.fork();
|
|
677
709
|
const where = {};
|
|
678
710
|
if (query.workflow) where.workflow = query.workflow;
|
|
711
|
+
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
679
712
|
if (query.status && query.statuses) {
|
|
680
713
|
where.$and = [
|
|
681
714
|
{
|
|
@@ -822,7 +855,10 @@ var MikroOrmStateStore = class {
|
|
|
822
855
|
async putSignalWaiter(waiter) {
|
|
823
856
|
const em = this.orm.em.fork();
|
|
824
857
|
await em.upsert(SignalWaiterEntity, {
|
|
825
|
-
|
|
858
|
+
token: waiter.token,
|
|
859
|
+
runId: waiter.runId,
|
|
860
|
+
seq: waiter.seq,
|
|
861
|
+
parallelGroup: waiter.parallelGroup ?? null
|
|
826
862
|
});
|
|
827
863
|
await em.flush();
|
|
828
864
|
}
|
|
@@ -835,7 +871,8 @@ var MikroOrmStateStore = class {
|
|
|
835
871
|
const waiter = {
|
|
836
872
|
token: entity.token,
|
|
837
873
|
runId: entity.runId,
|
|
838
|
-
seq: entity.seq
|
|
874
|
+
seq: entity.seq,
|
|
875
|
+
parallelGroup: entity.parallelGroup ?? void 0
|
|
839
876
|
};
|
|
840
877
|
await em.remove(entity).flush();
|
|
841
878
|
return waiter;
|
|
@@ -850,7 +887,8 @@ var MikroOrmStateStore = class {
|
|
|
850
887
|
return rows.map((e) => ({
|
|
851
888
|
token: e.token,
|
|
852
889
|
runId: e.runId,
|
|
853
|
-
seq: e.seq
|
|
890
|
+
seq: e.seq,
|
|
891
|
+
parallelGroup: e.parallelGroup ?? void 0
|
|
854
892
|
}));
|
|
855
893
|
}
|
|
856
894
|
async bufferSignal(token, payload) {
|
|
@@ -894,6 +932,7 @@ function toRunEntity(run) {
|
|
|
894
932
|
e.tags = run.tags ?? null;
|
|
895
933
|
e.searchAttributes = run.searchAttributes ?? null;
|
|
896
934
|
e.priority = run.priority ?? null;
|
|
935
|
+
e.namespace = run.namespace ?? "default";
|
|
897
936
|
e.createdAt = run.createdAt;
|
|
898
937
|
e.updatedAt = run.updatedAt;
|
|
899
938
|
return e;
|
|
@@ -915,6 +954,7 @@ function fromRunEntity(e) {
|
|
|
915
954
|
tags: e.tags ?? void 0,
|
|
916
955
|
searchAttributes: e.searchAttributes ?? void 0,
|
|
917
956
|
priority: e.priority ?? void 0,
|
|
957
|
+
namespace: e.namespace,
|
|
918
958
|
createdAt: e.createdAt,
|
|
919
959
|
updatedAt: e.updatedAt
|
|
920
960
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/entities.ts","../src/mikro-orm-state-store.ts","../src/schema.ts"],"sourcesContent":["import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema } from '@mikro-orm/core';\n\n/**\n * How property names map to physical column names. The durable tables are adapter-agnostic: a run\n * written by one store adapter must be readable by another, so every adapter has to agree on the\n * physical column names. The default is `'snake_case'` (the canonical, matched by the TypeORM,\n * Prisma and Drizzle adapters). `'preserve'` keeps the camelCase property name verbatim (what the\n * old TypeORM/Prisma adapters produced before they were pinned). A function lets you supply any\n * custom mapping.\n *\n * The choice is pinned EXPLICITLY onto the entity schema rather than left to the host ORM's naming\n * strategy — depending on the host strategy is what silently diverged the adapters and broke a store\n * swap against an existing table.\n */\nexport type DurableColumnNaming = 'snake_case' | 'preserve' | ((property: string) => string);\n\nfunction snakeCase(property: string): string {\n return property.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nfunction columnNamer(naming: DurableColumnNaming): (property: string) => string {\n if (naming === 'snake_case') return snakeCase;\n if (naming === 'preserve') return (property) => property;\n return naming;\n}\n\n// Plain classes (no decorators): the schema/column mapping lives on the EntitySchema built by\n// `durableEntities`, so the same class can be registered under different column names. The store\n// still references these classes for `em.create`/`getMetadata`.\n\nexport class WorkflowRunEntity {\n id!: string;\n workflow!: string;\n workflowVersion!: string;\n status!: RunStatus;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n wakeAt?: Date;\n lockedBy?: string;\n lockedUntil?: Date;\n recoveryAttempts?: number;\n tags?: string[] | null;\n searchAttributes?: Record<string, string | number | boolean> | null;\n priority?: number | null;\n createdAt!: Date;\n updatedAt!: Date;\n}\n\nexport class StepCheckpointEntity {\n runId!: string;\n seq!: number;\n name!: string;\n kind!: StepKind;\n stepId!: string;\n status!: 'pending' | 'running' | 'completed' | 'failed';\n input?: unknown;\n output?: unknown;\n error?: unknown;\n events?: unknown;\n attempts!: number;\n workerGroup?: string;\n parallelGroup?: string;\n wakeAt?: Date;\n enqueuedAt?: Date;\n startedAt!: Date;\n finishedAt!: Date;\n}\n\n/**\n * Normalized side-table for search attributes: one row per (run, key), so range/equality predicates\n * push DOWN into SQL (an EXISTS join indexed on `(key, numValue)` / `(key, strValue)`) instead of a\n * coarse scan + in-process filter. Maintained on every createRun/updateRun. Numbers land in\n * `numValue`, strings/booleans in `strValue` (booleans as \"true\"/\"false\"); see normalizeAttributeRows.\n */\nexport class RunAttributeEntity {\n runId!: string;\n key!: string;\n strValue?: string | null;\n numValue?: number | null;\n}\n\nexport class SignalWaiterEntity {\n token!: string;\n runId!: string;\n seq!: number;\n}\n\nexport class BufferedSignalEntity {\n id!: number;\n token!: string;\n payload?: unknown;\n}\n\n/**\n * Build the durable MikroORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your MikroORM config / `MikroOrmModule.forFeature`.\n *\n * ```ts\n * entities: durableEntities({ naming: 'snake_case' })\n * ```\n */\nexport function durableEntities(options: { naming?: DurableColumnNaming } = {}): EntitySchema[] {\n const col = columnNamer(options.naming ?? 'snake_case');\n\n const workflowRuns = new EntitySchema<WorkflowRunEntity>({\n class: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n // Indexes mirror the Prisma adapter so a store swap keeps the same plan. The poller hits these\n // every tick: `(status, wakeAt)` serves both the due-timer scan (status='suspended' AND wakeAt<=now)\n // and, via its `status` prefix, the pending/incomplete status lookups; `(workflow, status)` serves\n // the per-workflow timeout sweep. Without them every poll is a full scan of a table that only grows.\n indexes: [\n { name: 'durable_workflow_runs_status_wake_at_idx', properties: ['status', 'wakeAt'] },\n { name: 'durable_workflow_runs_workflow_status_idx', properties: ['workflow', 'status'] },\n ],\n properties: {\n id: { type: 'string', primary: true, fieldName: col('id') },\n workflow: { type: 'string', fieldName: col('workflow') },\n workflowVersion: { type: 'string', fieldName: col('workflowVersion') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n lockedBy: { type: 'string', nullable: true, fieldName: col('lockedBy') },\n lockedUntil: { type: 'Date', nullable: true, fieldName: col('lockedUntil') },\n recoveryAttempts: { type: 'integer', nullable: true, fieldName: col('recoveryAttempts') },\n tags: { type: 'json', nullable: true, fieldName: col('tags') },\n searchAttributes: { type: 'json', nullable: true, fieldName: col('searchAttributes') },\n priority: { type: 'integer', nullable: true, fieldName: col('priority') },\n createdAt: { type: 'Date', fieldName: col('createdAt') },\n updatedAt: { type: 'Date', fieldName: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n class: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n seq: { type: 'integer', primary: true, fieldName: col('seq') },\n name: { type: 'string', fieldName: col('name') },\n kind: { type: 'string', fieldName: col('kind') },\n stepId: { type: 'string', fieldName: col('stepId') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n events: { type: 'json', nullable: true, fieldName: col('events') },\n attempts: { type: 'integer', fieldName: col('attempts') },\n workerGroup: { type: 'string', nullable: true, fieldName: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group string so the dashboard\n // renders them as one \"ran in parallel\" group. The core engine sets it (incl. from a remote\n // worker's recordStep), but it was never persisted here — so the fan always rendered as N\n // sequential singles. Nullable: only fan-out steps carry it.\n parallelGroup: { type: 'string', nullable: true, fieldName: col('parallelGroup') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n enqueuedAt: { type: 'Date', nullable: true, fieldName: col('enqueuedAt') },\n startedAt: { type: 'Date', fieldName: col('startedAt') },\n finishedAt: { type: 'Date', fieldName: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n class: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n // The search-attribute EXISTS join (see the class docstring) pushes equality/range predicates down\n // onto `(key, numValue)` / `(key, strValue)`; mirror the Prisma adapter so those scans stay indexed.\n indexes: [\n { name: 'durable_run_attributes_key_num_idx', properties: ['key', 'numValue'] },\n { name: 'durable_run_attributes_key_str_idx', properties: ['key', 'strValue'] },\n ],\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n key: { type: 'string', primary: true, fieldName: col('key') },\n strValue: { type: 'string', nullable: true, fieldName: col('strValue') },\n // `float`/`double` is portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, fieldName: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n class: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n properties: {\n token: { type: 'string', primary: true, fieldName: col('token') },\n runId: { type: 'string', fieldName: col('runId') },\n seq: { type: 'integer', fieldName: col('seq') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n class: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n properties: {\n id: { type: 'integer', primary: true, autoincrement: true, fieldName: col('id') },\n token: { type: 'string', index: true, fieldName: col('token') },\n payload: { type: 'json', nullable: true, fieldName: col('payload') },\n },\n });\n\n return [workflowRuns, stepCheckpoints, runAttributes, signalWaiters, bufferedSignals];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import {\n type AttributeFilter,\n type RetentionPolicy,\n type RunQuery,\n type SignalWaiter,\n type StateStore,\n type StepCheckpoint,\n type StepError,\n type StepEvent,\n type WorkflowRun,\n attributeColumnFor,\n attributeOperand,\n normalizeAttributeRows,\n parseDuration,\n sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport type { EntityManager, MikroORM } from '@mikro-orm/core';\n\n/** Minimal structural surface of the SQL EntityManager's QueryBuilder (from `@mikro-orm/knex`),\n * typed here so the adapter needn't add a hard dependency on the knex types — a real SQL-driver EM\n * satisfies it at runtime. */\ninterface DurableQueryBuilder<T> {\n where(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n andWhere(cond: string): DurableQueryBuilder<T>;\n orderBy(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n limit(n: number): DurableQueryBuilder<T>;\n offset(n: number): DurableQueryBuilder<T>;\n getResultList(): Promise<T[]>;\n}\ntype SqlEm = EntityManager & {\n createQueryBuilder<T>(entity: unknown, alias: string): DurableQueryBuilder<T>;\n};\n\n/** Resolved side-table column names (per the active naming strategy) for the raw EXISTS pushdown. */\ninterface AttributeColumns {\n table: string;\n /** Side-table FK column (the run id on `durable_run_attributes`). */\n runId: string;\n /** Run table PK column (the `id` on `durable_workflow_runs`) used for the outer correlation. */\n runPk: string;\n key: string;\n strValue: string;\n numValue: string;\n}\nimport {\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { ensureMikroOrmDurableSchema } from './schema';\n\n/**\n * MikroORM-backed `StateStore`. Works on any MikroORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps and `wakeAt` use native datetime columns. Each operation runs on a forked\n * EntityManager so it owns its unit of work.\n */\nexport class MikroOrmStateStore implements StateStore {\n constructor(private readonly orm: MikroORM) {}\n\n async ensureSchema(): Promise<void> {\n await ensureMikroOrmDurableSchema(this.orm);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n const em = this.orm.em.fork();\n em.create(WorkflowRunEntity, toRunEntity(run));\n await em.flush();\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n const em = this.orm.em.fork();\n const entity = await em.findOneOrFail(WorkflowRunEntity, { id: runId });\n Object.assign(entity, toRunEntity({ ...fromRunEntity(entity), ...patch } as WorkflowRun));\n await em.flush();\n // Keep the side-table in step with the run's attributes whenever they're patched.\n if ('searchAttributes' in patch) await this.reindexAttributes(runId, patch.searchAttributes);\n }\n\n /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors\n * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */\n private async reindexAttributes(\n runId: string,\n attributes: WorkflowRun['searchAttributes'],\n forked?: EntityManager,\n ): Promise<void> {\n const em = forked ?? this.orm.em.fork();\n await em.nativeDelete(RunAttributeEntity, { runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await em.insertMany(RunAttributeEntity, rows);\n }\n\n async getRun(runId: string): Promise<WorkflowRun | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(WorkflowRunEntity, { id: runId });\n return entity ? fromRunEntity(entity) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n // Children first, then the run row — checkpoints, signal waiters, and the normalized\n // attribute side-table, so nothing dangles after the run is gone.\n await em.nativeDelete(StepCheckpointEntity, { runId });\n await em.nativeDelete(SignalWaiterEntity, { runId });\n await em.nativeDelete(RunAttributeEntity, { runId });\n await em.nativeDelete(WorkflowRunEntity, { id: runId });\n }\n\n async pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number> {\n if (policy.statuses.length === 0 || limit <= 0) return 0;\n const em = this.orm.em.fork();\n const status = { $in: policy.statuses };\n // Collect ids that violate EITHER bound (most-restrictive keep): too old, or past the count cap.\n const ids = new Set<string>();\n\n if (policy.maxAge != null) {\n const cutoff = new Date(nowMs - parseDuration(policy.maxAge));\n const rows = await em.find(\n WorkflowRunEntity,\n { status, updatedAt: { $lt: cutoff } },\n { fields: ['id'], orderBy: { updatedAt: 'asc' }, limit }, // oldest first\n );\n for (const r of rows) ids.add(r.id);\n }\n\n if (policy.maxCount != null && ids.size < limit) {\n // Everything beyond the newest `maxCount` rows in the status set — skip the kept window via offset.\n const rows = await em.find(\n WorkflowRunEntity,\n { status },\n {\n fields: ['id'],\n orderBy: { updatedAt: 'desc', id: 'desc' },\n limit,\n offset: policy.maxCount,\n },\n );\n for (const r of rows) {\n ids.add(r.id);\n if (ids.size >= limit) break;\n }\n }\n\n if (ids.size === 0) return 0;\n const idList = [...ids].slice(0, limit);\n const runId = { $in: idList };\n // Cascade children then runs (mirrors deleteRun) in one transaction so a pruned run never dangles.\n await em.transactional(async (tem) => {\n await tem.nativeDelete(StepCheckpointEntity, { runId });\n await tem.nativeDelete(SignalWaiterEntity, { runId });\n await tem.nativeDelete(RunAttributeEntity, { runId });\n await tem.nativeDelete(WorkflowRunEntity, { id: runId });\n });\n return idList.length;\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(StepCheckpointEntity, { runId, seq });\n return entity ? fromCheckpointEntity(entity) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(checkpoint));\n await em.flush();\n }\n\n async transaction<T>(\n work: (tx: {\n raw: unknown;\n saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;\n }) => Promise<T>,\n ): Promise<T> {\n return this.orm.em.fork().transactional(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, { status: { $in: ['running', 'cancelling'] } });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(\n WorkflowRunEntity,\n { status: 'pending' },\n { orderBy: { createdAt: 'asc' }, limit }, // FIFO dispatch\n );\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, {\n status: 'suspended',\n wakeAt: { $ne: null, $lte: new Date(nowMs) },\n });\n return rows.map(fromRunEntity);\n }\n\n async tryLockRun(\n runId: string,\n owner: string,\n leaseUntilMs: number,\n nowMs: number,\n ): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, $or: [{ lockedUntil: null }, { lockedUntil: { $lte: new Date(nowMs) } }] },\n { lockedBy: owner, lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n await em.nativeUpdate(WorkflowRunEntity, { id: runId }, { lockedBy: null, lockedUntil: null });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, lockedBy: owner },\n { lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const where: Record<string, unknown> = {};\n if (query.workflow) where.workflow = query.workflow;\n // `status IN (...)`; an empty set matches nothing (mirrors the in-memory store). When both the\n // single `status` and `statuses` are set, AND them so the narrower set wins.\n if (query.status && query.statuses) {\n where.$and = [{ status: query.status }, { status: { $in: query.statuses } }];\n } else if (query.status) {\n where.status = query.status;\n } else if (query.statuses) {\n where.status = { $in: query.statuses };\n }\n const orderBy = { createdAt: 'desc' as const }; // newest first — recent runs on top in the dashboard\n // `tags`/attributes need raw SQL pushed through the QueryBuilder:\n // - tag: `tags` is a JSON column (native json/jsonb on PG/MySQL), and MikroORM JSON-serializes a\n // `{ $like }` operand (wrapping/escaping the LIKE pattern) so a plain where corrupts it to no\n // matches. A raw LIKE on the column-as-text matches the quoted token so `etl` doesn't match\n // `etl-foo`. Postgres `jsonb` rejects `LIKE` directly, so cast to text per dialect.\n // - attributes: each filter becomes a raw EXISTS against the normalized side-table, so the DB\n // filters AND paginates — no full scan + in-process filter (ANDed: one EXISTS per filter).\n // Use a QueryBuilder with a fixed root alias `r` so the raw correlations are stable across drivers.\n if (query.tag || query.attributes?.length) {\n const quote = this.idQuote(em);\n const cols = this.attributeColumns(em);\n const qb = (em as SqlEm)\n .createQueryBuilder<WorkflowRunEntity>(WorkflowRunEntity, 'r')\n .where(where)\n .orderBy(orderBy);\n if (query.tag) {\n const tagsCol = this.tagsColumn(em);\n const pattern = `%\"${query.tag.replace(/'/g, \"''\")}\"%`;\n const colExpr = this.jsonAsText(em, `${quote('r')}.${quote(tagsCol)}`);\n qb.andWhere(`${colExpr} LIKE '${pattern}'`);\n }\n for (const f of query.attributes ?? []) {\n qb.andWhere(this.attributeExistsSql(f, quote, 'r', cols));\n }\n if (query.limit != null) qb.limit(query.limit);\n if (query.offset != null) qb.offset(query.offset);\n const rows = await qb.getResultList();\n return rows.map(fromRunEntity);\n }\n const rows = await em.find(WorkflowRunEntity, where, {\n ...(query.limit != null ? { limit: query.limit } : {}),\n ...(query.offset != null ? { offset: query.offset } : {}),\n orderBy,\n });\n return rows.map(fromRunEntity);\n }\n\n /** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run\n * alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):\n * EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric\n * operands compare the num column, everything else the str column. Identifiers are quoted per\n * driver and operands are inlined as literals (string operands are escaped by doubling quotes). */\n private attributeExistsSql(\n f: AttributeFilter,\n quote: (id: string) => string,\n alias: string,\n cols: AttributeColumns,\n ): string {\n const col = attributeColumnFor(f) === 'numValue' ? cols.numValue : cols.strValue;\n const cmp = sqlComparator(f.op);\n const operand = attributeOperand(f);\n const literal =\n typeof operand === 'number' ? String(operand) : `'${String(operand).replace(/'/g, \"''\")}'`;\n const a = quote(cols.table);\n const sub = quote('a');\n const outerId = `${quote(alias)}.${quote(cols.runPk)}`; // run PK column on the outer alias\n return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, \"''\")}' AND ${sub}.${quote(col)} ${cmp} ${literal})`;\n }\n\n /** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches\n * the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is\n * reused for BOTH the outer correlation and the side-table FK (they share the same fieldName). */\n private attributeColumns(em: EntityManager): AttributeColumns {\n const meta = em.getMetadata().get(RunAttributeEntity);\n const field = (prop: string) =>\n meta.props.find((p) => p.name === prop)?.fieldNames?.[0] ?? prop;\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n const runPk = runMeta.properties.id?.fieldNames?.[0] ?? 'id';\n return {\n table: meta.tableName,\n runId: field('runId'),\n runPk,\n key: field('key'),\n strValue: field('strValue'),\n numValue: field('numValue'),\n };\n }\n\n /** Resolve the `tags` column name from metadata (per the active naming strategy), for the raw LIKE. */\n private tagsColumn(em: EntityManager): string {\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n return runMeta.properties.tags?.fieldNames?.[0] ?? 'tags';\n }\n\n /** Wrap a JSON column reference so it can be `LIKE`d as text. Postgres `jsonb`/`json` rejects `LIKE`\n * directly (`operator does not exist: jsonb ~~ unknown`) — cast to `text`; MySQL JSON needs an\n * explicit `CHAR` cast; SQLite stores JSON as text already, so no cast. */\n private jsonAsText(em: EntityManager, colRef: string): string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n if (platform.includes('postgre')) return `${colRef}::text`;\n if (platform.includes('mysql') || platform.includes('mariadb'))\n return `CAST(${colRef} AS CHAR)`;\n return colRef;\n }\n\n /** Quote an identifier per the active SQL driver (MySQL/MariaDB backtick, others double-quote). */\n private idQuote(em: EntityManager): (id: string) => string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n const isMysql = platform.includes('mysql') || platform.includes('mariadb');\n const ch = isMysql ? '`' : '\"';\n return (id: string) => `${ch}${id}${ch}`;\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(StepCheckpointEntity, { runId }, { orderBy: { seq: 'asc' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(\n StepCheckpointEntity,\n { runId, name },\n { orderBy: { seq: 'desc' } },\n );\n return entity ? fromCheckpointEntity(entity) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const em = this.orm.em.fork();\n const rows = await em.find(\n StepCheckpointEntity,\n { runId, $or: prefixes.map((p) => ({ name: { $like: `${p}%` } })) },\n { orderBy: { seq: 'asc' } },\n );\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(SignalWaiterEntity, { ...waiter });\n await em.flush();\n }\n\n async takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(SignalWaiterEntity, { token });\n if (!entity) return null;\n const waiter: SignalWaiter = { token: entity.token, runId: entity.runId, seq: entity.seq };\n await em.remove(entity).flush();\n return waiter;\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(SignalWaiterEntity, { token: { $like: `${prefix}%` } });\n return rows.map((e) => ({ token: e.token, runId: e.runId, seq: e.seq }));\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n const em = this.orm.em.fork();\n const e = new BufferedSignalEntity();\n e.token = token;\n e.payload = payload ?? null;\n em.persist(e);\n await em.flush();\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(BufferedSignalEntity, { token }, { orderBy: { id: 'asc' } });\n if (!entity) return null;\n const payload = entity.payload ?? undefined;\n await em.remove(entity).flush();\n return { payload };\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n const e = new WorkflowRunEntity();\n e.id = run.id;\n e.workflow = run.workflow;\n e.workflowVersion = run.workflowVersion;\n e.status = run.status;\n e.input = run.input ?? null;\n e.output = run.output ?? null;\n e.error = run.error ?? null;\n if (run.wakeAt != null) e.wakeAt = new Date(run.wakeAt);\n if (run.lockedBy !== undefined) e.lockedBy = run.lockedBy;\n if (run.lockedUntil != null) e.lockedUntil = new Date(run.lockedUntil);\n if (run.recoveryAttempts !== undefined) e.recoveryAttempts = run.recoveryAttempts;\n e.tags = run.tags ?? null;\n e.searchAttributes = run.searchAttributes ?? null;\n e.priority = run.priority ?? null;\n e.createdAt = run.createdAt;\n e.updatedAt = run.updatedAt;\n return e;\n}\n\nfunction fromRunEntity(e: WorkflowRunEntity): WorkflowRun {\n return {\n id: e.id,\n workflow: e.workflow,\n workflowVersion: e.workflowVersion,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n wakeAt: e.wakeAt?.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil?.getTime(),\n recoveryAttempts: e.recoveryAttempts ?? undefined,\n tags: e.tags ?? undefined,\n searchAttributes: e.searchAttributes ?? undefined,\n priority: e.priority ?? undefined,\n createdAt: e.createdAt,\n updatedAt: e.updatedAt,\n };\n}\n\nfunction toCheckpointEntity(cp: StepCheckpoint): StepCheckpointEntity {\n const e = new StepCheckpointEntity();\n e.runId = cp.runId;\n e.seq = cp.seq;\n e.name = cp.name;\n e.kind = cp.kind;\n e.stepId = cp.stepId;\n e.status = cp.status;\n e.input = cp.input ?? null;\n e.output = cp.output ?? null;\n e.error = cp.error ?? null;\n e.events = cp.events ?? null;\n e.attempts = cp.attempts;\n if (cp.workerGroup !== undefined) e.workerGroup = cp.workerGroup;\n if (cp.parallelGroup !== undefined) e.parallelGroup = cp.parallelGroup;\n if (cp.wakeAt != null) e.wakeAt = new Date(cp.wakeAt);\n e.enqueuedAt = cp.enqueuedAt;\n e.startedAt = cp.startedAt;\n e.finishedAt = cp.finishedAt;\n return e;\n}\n\nfunction fromCheckpointEntity(e: StepCheckpointEntity): StepCheckpoint {\n return {\n runId: e.runId,\n seq: e.seq,\n name: e.name,\n kind: e.kind,\n stepId: e.stepId,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n events: (e.events ?? undefined) as StepEvent[] | undefined,\n attempts: e.attempts,\n workerGroup: e.workerGroup ?? undefined,\n parallelGroup: e.parallelGroup ?? undefined,\n wakeAt: e.wakeAt?.getTime(),\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n","import type { MikroORM } from '@mikro-orm/core';\n\n/**\n * The tables this store owns. Kept in sync with the `tableName`s in `./entities`. Used to scope\n * schema management to our own tables (see {@link ensureMikroOrmDurableSchema}).\n */\nconst DURABLE_TABLE_NAMES = new Set([\n 'durable_workflow_runs',\n 'durable_step_checkpoints',\n 'durable_run_attributes',\n 'durable_signal_waiters',\n 'durable_buffered_signals',\n]);\n\n/**\n * The table a DDL statement targets, lower-cased; undefined otherwise. Recognizes `create table` /\n * `alter table` (the table name follows immediately) and standalone `create [unique] index ... on\n * <table>` (the table follows `on`). MikroORM adds an index to an EXISTING table as `alter table ...\n * add index` on MySQL but as a standalone `create index ... on ...` on Postgres/SQLite — without the\n * second form those index statements would be dropped by the durable-table filter and never applied.\n */\nfunction targetTable(statement: string): string | undefined {\n const match = statement.match(\n /^(?:create\\s+(?:unique\\s+)?index\\s+.+?\\s+on\\s+|(?:create|alter)\\s+table\\s+(?:if\\s+not\\s+exists\\s+)?)[`\"']?([\\w$]+)[`\"']?/i,\n );\n return match?.[1]?.toLowerCase();\n}\n\n/**\n * Whether a statement adds structure the store REQUIRES — a failure here is fatal (the store can't\n * run without the table/column/index). A column TYPE alignment (`modify`/`alter column ... type`)\n * is best-effort: the column already holds the data (the store serializes JSON to/from it regardless\n * of the declared type), so failing to converge its type — e.g. a legacy `longtext` value that won't\n * cast to `json` because it was truncated under an older `text` column — must NOT crash boot.\n */\nfunction isRequiredStructure(statement: string): boolean {\n return /\\b(?:create\\s+table|create\\s+(?:unique\\s+)?index|add\\s+(?:column|index|constraint|key|unique|fulltext))\\b/i.test(\n statement,\n );\n}\n\n/**\n * Idempotently create/extend ONLY the durable tables (additive — never drops). Safe to run on every\n * boot (auto-schema) and from a MikroORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables extends Migration {\n * async up() { await ensureMikroOrmDurableSchema(this.getEntityManager().getOrm()); }\n * }\n * ```\n *\n * It does NOT run a whole-ORM `schema.update()`. When the store shares the host app's ORM (the\n * recommended setup — a single ORM avoids MikroORM's global-metadata clobber between instances),\n * a full `schema.update()` would try to reconcile EVERY app table to the entity metadata: on a\n * migration-managed schema that means recreating the migrations table and dropping app foreign keys.\n * Instead we compute the safe additive diff and execute only the statements that target our own\n * `durable_*` tables. `getUpdateSchemaSQL` already emits a `create table` only for a missing table\n * and `alter table ... add` only for missing columns, so existing tables — and the entire rest of\n * the host schema — are left untouched.\n */\nexport async function ensureMikroOrmDurableSchema(orm: MikroORM): Promise<void> {\n const sql = await orm.schema.getUpdateSchemaSQL({ safe: true });\n const ours = sql\n .split(';')\n .map((statement) => statement.trim())\n .filter((statement) => {\n const table = targetTable(statement);\n return table !== undefined && DURABLE_TABLE_NAMES.has(table);\n });\n const connection = orm.em.getConnection();\n for (const statement of ours) {\n try {\n await connection.execute(statement);\n } catch (err) {\n // Required structure (create table / add column / add index) must succeed — rethrow.\n if (isRequiredStructure(statement)) {\n throw err;\n }\n // A best-effort type alignment (e.g. legacy `longtext` → `json`) failed, typically because an\n // existing column holds a value that can't cast to the target type (a relic of an older column\n // type). The column already stores the data and the store reads/writes it via serialization, so\n // leave it as-is and continue rather than crashing boot. Surfaced so the data can be repaired\n // out of band (e.g. `UPDATE … SET col = NULL WHERE JSON_VALID(col) = 0` for a disposable column).\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] skipped a non-structural durable-schema statement that failed; the column is left as-is (functional) — repair the data out of band to converge its type. Statement: ${statement} — ${message}`,\n );\n }\n }\n\n // Converge collation last, once the tables exist (whether just created above or pre-existing).\n await alignDurableCollation(orm);\n}\n\n/** A MySQL identifier we are willing to interpolate into DDL (charset / collation names). */\nfunction isSafeSqlIdentifier(value: string): boolean {\n return /^[A-Za-z0-9_]+$/.test(value);\n}\n\n/**\n * Align each durable table's collation to the ORM's configured `collate` (MySQL/MariaDB only).\n *\n * MikroORM's auto-schema (`getUpdateSchemaSQL`) creates tables with the server's DEFAULT collation —\n * on MySQL 8.4 that's `utf8mb4_0900_ai_ci` — and ignores the `collate` config option. When the host\n * app pins a different collation on its own tables (commonly `utf8mb4_unicode_ci` via migrations), a\n * JOIN between a durable table and an app table throws \"Illegal mix of collations\". We converge it\n * here: read the configured collation and `CONVERT` only the durable tables whose collation differs.\n *\n * Idempotent (matching tables are skipped), non-fatal (a CONVERT failure is warned, never crashes\n * boot), and a no-op when no `collate` is configured or the platform isn't MySQL/MariaDB.\n */\nasync function alignDurableCollation(orm: MikroORM): Promise<void> {\n const platform = String(orm.em.getPlatform().constructor.name).toLowerCase();\n if (!platform.includes('mysql') && !platform.includes('maria')) {\n return;\n }\n const collate = orm.config.get('collate');\n if (typeof collate !== 'string' || !isSafeSqlIdentifier(collate)) {\n return;\n }\n // utf8mb4_unicode_ci → utf8mb4. CONVERT TO needs the charset that owns the collation.\n const charset = collate.split('_')[0];\n if (!charset || !isSafeSqlIdentifier(charset)) {\n return;\n }\n const connection = orm.em.getConnection();\n for (const table of DURABLE_TABLE_NAMES) {\n try {\n const rows = (await connection.execute(\n 'select table_collation as collation from information_schema.tables where table_schema = database() and table_name = ? limit 1',\n [table],\n )) as Array<{ collation?: string | null }>;\n const current = rows[0]?.collation;\n // Table absent (nothing to align) or already at the target collation — skip.\n if (current == null || current === collate) {\n continue;\n }\n await connection.execute(\n `alter table \\`${table}\\` convert to character set ${charset} collate ${collate}`,\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] could not align collation for \\`${table}\\` to ${collate} (left as-is): ${message}`,\n );\n }\n }\n}\n"],"mappings":";;;;AACA,SAASA,oBAAoB;AAgB7B,SAASC,UAAUC,UAAgB;AACjC,SAAOA,SAASC,QAAQ,UAAU,CAACC,MAAM,IAAIA,EAAEC,YAAW,CAAA,EAAI;AAChE;AAFSJ;AAIT,SAASK,YAAYC,QAA2B;AAC9C,MAAIA,WAAW,aAAc,QAAON;AACpC,MAAIM,WAAW,WAAY,QAAO,CAACL,aAAaA;AAChD,SAAOK;AACT;AAJSD;AAUF,IAAME,oBAAN,MAAMA;EA9Bb,OA8BaA;;;EACXC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;AACF;AAEO,IAAMC,uBAAN,MAAMA;EAjDb,OAiDaA;;;EACXC;EACAC;EACAC;EACAC;EACAC;EACAlB;EACAC;EACAC;EACAC;EACAgB;EACAC;EACAC;EACAC;EACAlB;EACAmB;EACAC;EACAC;AACF;AAQO,IAAMC,qBAAN,MAAMA;EA3Eb,OA2EaA;;;EACXZ;EACAa;EACAC;EACAC;AACF;AAEO,IAAMC,qBAAN,MAAMA;EAlFb,OAkFaA;;;EACXC;EACAjB;EACAC;AACF;AAEO,IAAMiB,uBAAN,MAAMA;EAxFb,OAwFaA;;;EACXnC;EACAkC;EACAE;AACF;AAUO,SAASC,gBAAgBC,UAA4C,CAAC,GAAC;AAC5E,QAAMC,MAAM1C,YAAYyC,QAAQxC,UAAU,YAAA;AAE1C,QAAM0C,eAAe,IAAIC,aAAgC;IACvDC,OAAO3C;IACP4C,WAAW;;;;;IAKXC,SAAS;MACP;QAAEzB,MAAM;QAA4C0B,YAAY;UAAC;UAAU;;MAAU;MACrF;QAAE1B,MAAM;QAA6C0B,YAAY;UAAC;UAAY;;MAAU;;IAE1FA,YAAY;MACV7C,IAAI;QAAE8C,MAAM;QAAUC,SAAS;QAAMC,WAAWT,IAAI,IAAA;MAAM;MAC1DtC,UAAU;QAAE6C,MAAM;QAAUE,WAAWT,IAAI,UAAA;MAAY;MACvDrC,iBAAiB;QAAE4C,MAAM;QAAUE,WAAWT,IAAI,iBAAA;MAAmB;MACrEpC,QAAQ;QAAE2C,MAAM;QAAUE,WAAWT,IAAI,QAAA;MAAU;MACnDnC,OAAO;QAAE0C,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,OAAA;MAAS;MAC/DlC,QAAQ;QAAEyC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,QAAA;MAAU;MACjEjC,OAAO;QAAEwC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,OAAA;MAAS;MAC/DhC,QAAQ;QAAEuC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,QAAA;MAAU;MACjE/B,UAAU;QAAEsC,MAAM;QAAUG,UAAU;QAAMD,WAAWT,IAAI,UAAA;MAAY;MACvE9B,aAAa;QAAEqC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,aAAA;MAAe;MAC3E7B,kBAAkB;QAAEoC,MAAM;QAAWG,UAAU;QAAMD,WAAWT,IAAI,kBAAA;MAAoB;MACxF5B,MAAM;QAAEmC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,MAAA;MAAQ;MAC7D3B,kBAAkB;QAAEkC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,kBAAA;MAAoB;MACrF1B,UAAU;QAAEiC,MAAM;QAAWG,UAAU;QAAMD,WAAWT,IAAI,UAAA;MAAY;MACxEzB,WAAW;QAAEgC,MAAM;QAAQE,WAAWT,IAAI,WAAA;MAAa;MACvDxB,WAAW;QAAE+B,MAAM;QAAQE,WAAWT,IAAI,WAAA;MAAa;IACzD;EACF,CAAA;AAEA,QAAMW,kBAAkB,IAAIT,aAAmC;IAC7DC,OAAO1B;IACP2B,WAAW;IACXE,YAAY;MACV5B,OAAO;QAAE6B,MAAM;QAAUC,SAAS;QAAMC,WAAWT,IAAI,OAAA;MAAS;MAChErB,KAAK;QAAE4B,MAAM;QAAWC,SAAS;QAAMC,WAAWT,IAAI,KAAA;MAAO;MAC7DpB,MAAM;QAAE2B,MAAM;QAAUE,WAAWT,IAAI,MAAA;MAAQ;MAC/CnB,MAAM;QAAE0B,MAAM;QAAUE,WAAWT,IAAI,MAAA;MAAQ;MAC/ClB,QAAQ;QAAEyB,MAAM;QAAUE,WAAWT,IAAI,QAAA;MAAU;MACnDpC,QAAQ;QAAE2C,MAAM;QAAUE,WAAWT,IAAI,QAAA;MAAU;MACnDnC,OAAO;QAAE0C,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,OAAA;MAAS;MAC/DlC,QAAQ;QAAEyC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,QAAA;MAAU;MACjEjC,OAAO;QAAEwC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,OAAA;MAAS;MAC/DjB,QAAQ;QAAEwB,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,QAAA;MAAU;MACjEhB,UAAU;QAAEuB,MAAM;QAAWE,WAAWT,IAAI,UAAA;MAAY;MACxDf,aAAa;QAAEsB,MAAM;QAAUG,UAAU;QAAMD,WAAWT,IAAI,aAAA;MAAe;;;;;MAK7Ed,eAAe;QAAEqB,MAAM;QAAUG,UAAU;QAAMD,WAAWT,IAAI,eAAA;MAAiB;MACjFhC,QAAQ;QAAEuC,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,QAAA;MAAU;MACjEb,YAAY;QAAEoB,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,YAAA;MAAc;MACzEZ,WAAW;QAAEmB,MAAM;QAAQE,WAAWT,IAAI,WAAA;MAAa;MACvDX,YAAY;QAAEkB,MAAM;QAAQE,WAAWT,IAAI,YAAA;MAAc;IAC3D;EACF,CAAA;AAEA,QAAMY,gBAAgB,IAAIV,aAAiC;IACzDC,OAAOb;IACPc,WAAW;;;IAGXC,SAAS;MACP;QAAEzB,MAAM;QAAsC0B,YAAY;UAAC;UAAO;;MAAY;MAC9E;QAAE1B,MAAM;QAAsC0B,YAAY;UAAC;UAAO;;MAAY;;IAEhFA,YAAY;MACV5B,OAAO;QAAE6B,MAAM;QAAUC,SAAS;QAAMC,WAAWT,IAAI,OAAA;MAAS;MAChET,KAAK;QAAEgB,MAAM;QAAUC,SAAS;QAAMC,WAAWT,IAAI,KAAA;MAAO;MAC5DR,UAAU;QAAEe,MAAM;QAAUG,UAAU;QAAMD,WAAWT,IAAI,UAAA;MAAY;;MAEvEP,UAAU;QAAEc,MAAM;QAASG,UAAU;QAAMD,WAAWT,IAAI,UAAA;MAAY;IACxE;EACF,CAAA;AAEA,QAAMa,gBAAgB,IAAIX,aAAiC;IACzDC,OAAOT;IACPU,WAAW;IACXE,YAAY;MACVX,OAAO;QAAEY,MAAM;QAAUC,SAAS;QAAMC,WAAWT,IAAI,OAAA;MAAS;MAChEtB,OAAO;QAAE6B,MAAM;QAAUE,WAAWT,IAAI,OAAA;MAAS;MACjDrB,KAAK;QAAE4B,MAAM;QAAWE,WAAWT,IAAI,KAAA;MAAO;IAChD;EACF,CAAA;AAEA,QAAMc,kBAAkB,IAAIZ,aAAmC;IAC7DC,OAAOP;IACPQ,WAAW;IACXE,YAAY;MACV7C,IAAI;QAAE8C,MAAM;QAAWC,SAAS;QAAMO,eAAe;QAAMN,WAAWT,IAAI,IAAA;MAAM;MAChFL,OAAO;QAAEY,MAAM;QAAUS,OAAO;QAAMP,WAAWT,IAAI,OAAA;MAAS;MAC9DH,SAAS;QAAEU,MAAM;QAAQG,UAAU;QAAMD,WAAWT,IAAI,SAAA;MAAW;IACrE;EACF,CAAA;AAEA,SAAO;IAACC;IAAcU;IAAiBC;IAAeC;IAAeC;;AACvE;AArGgBhB;AAwGT,IAAMmB,WAAWnB,gBAAAA;;;AC/MxB,SAUEoB,oBACAC,kBACAC,wBACAC,eACAC,qBACK;;;ACTP,IAAMC,sBAAsB,oBAAIC,IAAI;EAClC;EACA;EACA;EACA;EACA;CACD;AASD,SAASC,YAAYC,WAAiB;AACpC,QAAMC,QAAQD,UAAUC,MACtB,2HAAA;AAEF,SAAOA,QAAQ,CAAA,GAAIC,YAAAA;AACrB;AALSH;AAcT,SAASI,oBAAoBH,WAAiB;AAC5C,SAAO,6GAA6GI,KAClHJ,SAAAA;AAEJ;AAJSG;AAyBT,eAAsBE,4BAA4BC,KAAa;AAC7D,QAAMC,MAAM,MAAMD,IAAIE,OAAOC,mBAAmB;IAAEC,MAAM;EAAK,CAAA;AAC7D,QAAMC,OAAOJ,IACVK,MAAM,GAAA,EACNC,IAAI,CAACb,cAAcA,UAAUc,KAAI,CAAA,EACjCC,OAAO,CAACf,cAAAA;AACP,UAAMgB,QAAQjB,YAAYC,SAAAA;AAC1B,WAAOgB,UAAUC,UAAapB,oBAAoBqB,IAAIF,KAAAA;EACxD,CAAA;AACF,QAAMG,aAAab,IAAIc,GAAGC,cAAa;AACvC,aAAWrB,aAAaW,MAAM;AAC5B,QAAI;AACF,YAAMQ,WAAWG,QAAQtB,SAAAA;IAC3B,SAASuB,KAAK;AAEZ,UAAIpB,oBAAoBH,SAAAA,GAAY;AAClC,cAAMuB;MACR;AAMA,YAAMC,UAAUD,eAAeE,QAAQF,IAAIC,UAAUE,OAAOH,GAAAA;AAC5DI,cAAQC,KACN,6MAAwM5B,SAAAA,WAAewB,OAAAA,EAAS;IAEpO;EACF;AAGA,QAAMK,sBAAsBvB,GAAAA;AAC9B;AAhCsBD;AAmCtB,SAASyB,oBAAoBC,OAAa;AACxC,SAAO,kBAAkB3B,KAAK2B,KAAAA;AAChC;AAFSD;AAgBT,eAAeD,sBAAsBvB,KAAa;AAChD,QAAM0B,WAAWN,OAAOpB,IAAIc,GAAGa,YAAW,EAAG,YAAYC,IAAI,EAAEhC,YAAW;AAC1E,MAAI,CAAC8B,SAASG,SAAS,OAAA,KAAY,CAACH,SAASG,SAAS,OAAA,GAAU;AAC9D;EACF;AACA,QAAMC,UAAU9B,IAAI+B,OAAOC,IAAI,SAAA;AAC/B,MAAI,OAAOF,YAAY,YAAY,CAACN,oBAAoBM,OAAAA,GAAU;AAChE;EACF;AAEA,QAAMG,UAAUH,QAAQxB,MAAM,GAAA,EAAK,CAAA;AACnC,MAAI,CAAC2B,WAAW,CAACT,oBAAoBS,OAAAA,GAAU;AAC7C;EACF;AACA,QAAMpB,aAAab,IAAIc,GAAGC,cAAa;AACvC,aAAWL,SAASnB,qBAAqB;AACvC,QAAI;AACF,YAAM2C,OAAQ,MAAMrB,WAAWG,QAC7B,iIACA;QAACN;OAAM;AAET,YAAMyB,UAAUD,KAAK,CAAA,GAAIE;AAEzB,UAAID,WAAW,QAAQA,YAAYL,SAAS;AAC1C;MACF;AACA,YAAMjB,WAAWG,QACf,iBAAiBN,KAAAA,+BAAoCuB,OAAAA,YAAmBH,OAAAA,EAAS;IAErF,SAASb,KAAK;AACZ,YAAMC,UAAUD,eAAeE,QAAQF,IAAIC,UAAUE,OAAOH,GAAAA;AAC5DI,cAAQC,KACN,oEAAoEZ,KAAAA,SAAcoB,OAAAA,kBAAyBZ,OAAAA,EAAS;IAExH;EACF;AACF;AApCeK;;;ADrDR,IAAMc,qBAAN,MAAMA;EA1Db,OA0DaA;;;;EACX,YAA6BC,KAAe;SAAfA,MAAAA;EAAgB;EAE7C,MAAMC,eAA8B;AAClC,UAAMC,4BAA4B,KAAKF,GAAG;EAC5C;EAEA,MAAMG,UAAUC,KAAiC;AAC/C,UAAMC,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3BD,OAAGE,OAAOC,mBAAmBC,YAAYL,GAAAA,CAAAA;AACzC,UAAMC,GAAGK,MAAK;AACd,UAAM,KAAKC,kBAAkBP,IAAIQ,IAAIR,IAAIS,gBAAgB;EAC3D;EAEA,MAAMC,UAAUC,OAAeC,OAA4C;AACzE,UAAMX,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMW,SAAS,MAAMZ,GAAGa,cAAcV,mBAAmB;MAAEI,IAAIG;IAAM,CAAA;AACrEI,WAAOC,OAAOH,QAAQR,YAAY;MAAE,GAAGY,cAAcJ,MAAAA;MAAS,GAAGD;IAAM,CAAA,CAAA;AACvE,UAAMX,GAAGK,MAAK;AAEd,QAAI,sBAAsBM,MAAO,OAAM,KAAKL,kBAAkBI,OAAOC,MAAMH,gBAAgB;EAC7F;;;EAIA,MAAcF,kBACZI,OACAO,YACAC,QACe;AACf,UAAMlB,KAAKkB,UAAU,KAAKvB,IAAIK,GAAGC,KAAI;AACrC,UAAMD,GAAGmB,aAAaC,oBAAoB;MAAEV;IAAM,CAAA;AAClD,UAAMW,OAAOC,uBAAuBZ,OAAOO,UAAAA;AAC3C,QAAII,KAAKE,OAAQ,OAAMvB,GAAGwB,WAAWJ,oBAAoBC,IAAAA;EAC3D;EAEA,MAAMI,OAAOf,OAA4C;AACvD,UAAMV,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMW,SAAS,MAAMZ,GAAG0B,QAAQvB,mBAAmB;MAAEI,IAAIG;IAAM,CAAA;AAC/D,WAAOE,SAASI,cAAcJ,MAAAA,IAAU;EAC1C;EAEA,MAAMe,UAAUjB,OAA8B;AAC5C,UAAMV,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAG3B,UAAMD,GAAGmB,aAAaS,sBAAsB;MAAElB;IAAM,CAAA;AACpD,UAAMV,GAAGmB,aAAaU,oBAAoB;MAAEnB;IAAM,CAAA;AAClD,UAAMV,GAAGmB,aAAaC,oBAAoB;MAAEV;IAAM,CAAA;AAClD,UAAMV,GAAGmB,aAAahB,mBAAmB;MAAEI,IAAIG;IAAM,CAAA;EACvD;EAEA,MAAMoB,kBAAkBC,QAAyBC,OAAeC,OAAgC;AAC9F,QAAIF,OAAOG,SAASX,WAAW,KAAKU,SAAS,EAAG,QAAO;AACvD,UAAMjC,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMkC,SAAS;MAAEC,KAAKL,OAAOG;IAAS;AAEtC,UAAMG,MAAM,oBAAIC,IAAAA;AAEhB,QAAIP,OAAOQ,UAAU,MAAM;AACzB,YAAMC,SAAS,IAAIC,KAAKT,QAAQU,cAAcX,OAAOQ,MAAM,CAAA;AAC3D,YAAMlB,OAAO,MAAMrB,GAAG2C,KACpBxC,mBACA;QAAEgC;QAAQS,WAAW;UAAEC,KAAKL;QAAO;MAAE,GACrC;QAAEM,QAAQ;UAAC;;QAAOC,SAAS;UAAEH,WAAW;QAAM;QAAGX;MAAM,CAAA;AAEzD,iBAAWe,KAAK3B,KAAMgB,KAAIY,IAAID,EAAEzC,EAAE;IACpC;AAEA,QAAIwB,OAAOmB,YAAY,QAAQb,IAAIc,OAAOlB,OAAO;AAE/C,YAAMZ,OAAO,MAAMrB,GAAG2C,KACpBxC,mBACA;QAAEgC;MAAO,GACT;QACEW,QAAQ;UAAC;;QACTC,SAAS;UAAEH,WAAW;UAAQrC,IAAI;QAAO;QACzC0B;QACAmB,QAAQrB,OAAOmB;MACjB,CAAA;AAEF,iBAAWF,KAAK3B,MAAM;AACpBgB,YAAIY,IAAID,EAAEzC,EAAE;AACZ,YAAI8B,IAAIc,QAAQlB,MAAO;MACzB;IACF;AAEA,QAAII,IAAIc,SAAS,EAAG,QAAO;AAC3B,UAAME,SAAS;SAAIhB;MAAKiB,MAAM,GAAGrB,KAAAA;AACjC,UAAMvB,QAAQ;MAAE0B,KAAKiB;IAAO;AAE5B,UAAMrD,GAAGuD,cAAc,OAAOC,QAAAA;AAC5B,YAAMA,IAAIrC,aAAaS,sBAAsB;QAAElB;MAAM,CAAA;AACrD,YAAM8C,IAAIrC,aAAaU,oBAAoB;QAAEnB;MAAM,CAAA;AACnD,YAAM8C,IAAIrC,aAAaC,oBAAoB;QAAEV;MAAM,CAAA;AACnD,YAAM8C,IAAIrC,aAAahB,mBAAmB;QAAEI,IAAIG;MAAM,CAAA;IACxD,CAAA;AACA,WAAO2C,OAAO9B;EAChB;EAEA,MAAMkC,cAAc/C,OAAegD,KAA6C;AAC9E,UAAM1D,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMW,SAAS,MAAMZ,GAAG0B,QAAQE,sBAAsB;MAAElB;MAAOgD;IAAI,CAAA;AACnE,WAAO9C,SAAS+C,qBAAqB/C,MAAAA,IAAU;EACjD;EAEA,MAAMgD,eAAeC,YAA2C;AAC9D,UAAM7D,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMD,GAAG8D,OAAOlC,sBAAsBmC,mBAAmBF,UAAAA,CAAAA;AACzD,UAAM7D,GAAGK,MAAK;EAChB;EAEA,MAAM2D,YACJC,MAIY;AACZ,WAAO,KAAKtE,IAAIK,GAAGC,KAAI,EAAGsD,cAAc,OAAOvD,OAC7CiE,KAAK;MACHC,KAAKlE;MACL4D,gBAAgB,8BAAOO,OAAAA;AACrB,cAAMnE,GAAG8D,OAAOlC,sBAAsBmC,mBAAmBI,EAAAA,CAAAA;MAC3D,GAFgB;IAGlB,CAAA,CAAA;EAEJ;EAEA,MAAMC,qBAA6C;AACjD,UAAMpE,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMoB,OAAO,MAAMrB,GAAG2C,KAAKxC,mBAAmB;MAAEgC,QAAQ;QAAEC,KAAK;UAAC;UAAW;;MAAc;IAAE,CAAA;AAC3F,WAAOf,KAAKgD,IAAIrD,aAAAA;EAClB;EAEA,MAAMsD,gBAAgBrC,OAAuC;AAC3D,UAAMjC,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMoB,OAAO,MAAMrB,GAAG2C,KACpBxC,mBACA;MAAEgC,QAAQ;IAAU,GACpB;MAAEY,SAAS;QAAEwB,WAAW;MAAM;MAAGtC;IAAM,CAAA;AAEzC,WAAOZ,KAAKgD,IAAIrD,aAAAA;EAClB;EAEA,MAAMwD,cAAcxC,OAAuC;AACzD,UAAMhC,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMoB,OAAO,MAAMrB,GAAG2C,KAAKxC,mBAAmB;MAC5CgC,QAAQ;MACRsC,QAAQ;QAAEC,KAAK;QAAMC,MAAM,IAAIlC,KAAKT,KAAAA;MAAO;IAC7C,CAAA;AACA,WAAOX,KAAKgD,IAAIrD,aAAAA;EAClB;EAEA,MAAM4D,WACJlE,OACAmE,OACAC,cACA9C,OACkB;AAClB,UAAMhC,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAM8E,WAAW,MAAM/E,GAAGgF,aACxB7E,mBACA;MAAEI,IAAIG;MAAOuE,KAAK;QAAC;UAAEC,aAAa;QAAK;QAAG;UAAEA,aAAa;YAAEP,MAAM,IAAIlC,KAAKT,KAAAA;UAAO;QAAE;;IAAG,GACtF;MAAEmD,UAAUN;MAAOK,aAAa,IAAIzC,KAAKqC,YAAAA;IAAc,CAAA;AAEzD,WAAOC,aAAa;EACtB;EAEA,MAAMK,eAAe1E,OAA8B;AACjD,UAAMV,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMD,GAAGgF,aAAa7E,mBAAmB;MAAEI,IAAIG;IAAM,GAAG;MAAEyE,UAAU;MAAMD,aAAa;IAAK,CAAA;EAC9F;EAEA,MAAMG,aAAa3E,OAAemE,OAAeC,cAAwC;AACvF,UAAM9E,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAM8E,WAAW,MAAM/E,GAAGgF,aACxB7E,mBACA;MAAEI,IAAIG;MAAOyE,UAAUN;IAAM,GAC7B;MAAEK,aAAa,IAAIzC,KAAKqC,YAAAA;IAAc,CAAA;AAExC,WAAOC,aAAa;EACtB;EAEA,MAAMO,SAASC,OAAyC;AACtD,UAAMvF,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMuF,QAAiC,CAAC;AACxC,QAAID,MAAME,SAAUD,OAAMC,WAAWF,MAAME;AAG3C,QAAIF,MAAMpD,UAAUoD,MAAMrD,UAAU;AAClCsD,YAAME,OAAO;QAAC;UAAEvD,QAAQoD,MAAMpD;QAAO;QAAG;UAAEA,QAAQ;YAAEC,KAAKmD,MAAMrD;UAAS;QAAE;;IAC5E,WAAWqD,MAAMpD,QAAQ;AACvBqD,YAAMrD,SAASoD,MAAMpD;IACvB,WAAWoD,MAAMrD,UAAU;AACzBsD,YAAMrD,SAAS;QAAEC,KAAKmD,MAAMrD;MAAS;IACvC;AACA,UAAMa,UAAU;MAAEwB,WAAW;IAAgB;AAS7C,QAAIgB,MAAMI,OAAOJ,MAAMtE,YAAYM,QAAQ;AACzC,YAAMqE,QAAQ,KAAKC,QAAQ7F,EAAAA;AAC3B,YAAM8F,OAAO,KAAKC,iBAAiB/F,EAAAA;AACnC,YAAMgG,KAAMhG,GACTiG,mBAAsC9F,mBAAmB,GAAA,EACzDqF,MAAMA,KAAAA,EACNzC,QAAQA,OAAAA;AACX,UAAIwC,MAAMI,KAAK;AACb,cAAMO,UAAU,KAAKC,WAAWnG,EAAAA;AAChC,cAAMoG,UAAU,KAAKb,MAAMI,IAAIU,QAAQ,MAAM,IAAA,CAAA;AAC7C,cAAMC,UAAU,KAAKC,WAAWvG,IAAI,GAAG4F,MAAM,GAAA,CAAA,IAAQA,MAAMM,OAAAA,CAAAA,EAAU;AACrEF,WAAGQ,SAAS,GAAGF,OAAAA,UAAiBF,OAAAA,GAAU;MAC5C;AACA,iBAAWK,KAAKlB,MAAMtE,cAAc,CAAA,GAAI;AACtC+E,WAAGQ,SAAS,KAAKE,mBAAmBD,GAAGb,OAAO,KAAKE,IAAAA,CAAAA;MACrD;AACA,UAAIP,MAAMtD,SAAS,KAAM+D,IAAG/D,MAAMsD,MAAMtD,KAAK;AAC7C,UAAIsD,MAAMnC,UAAU,KAAM4C,IAAG5C,OAAOmC,MAAMnC,MAAM;AAChD,YAAM/B,QAAO,MAAM2E,GAAGW,cAAa;AACnC,aAAOtF,MAAKgD,IAAIrD,aAAAA;IAClB;AACA,UAAMK,OAAO,MAAMrB,GAAG2C,KAAKxC,mBAAmBqF,OAAO;MACnD,GAAID,MAAMtD,SAAS,OAAO;QAAEA,OAAOsD,MAAMtD;MAAM,IAAI,CAAC;MACpD,GAAIsD,MAAMnC,UAAU,OAAO;QAAEA,QAAQmC,MAAMnC;MAAO,IAAI,CAAC;MACvDL;IACF,CAAA;AACA,WAAO1B,KAAKgD,IAAIrD,aAAAA;EAClB;;;;;;EAOQ0F,mBACND,GACAb,OACAgB,OACAd,MACQ;AACR,UAAMe,MAAMC,mBAAmBL,CAAAA,MAAO,aAAaX,KAAKiB,WAAWjB,KAAKkB;AACxE,UAAMC,MAAMC,cAAcT,EAAEU,EAAE;AAC9B,UAAMC,UAAUC,iBAAiBZ,CAAAA;AACjC,UAAMa,UACJ,OAAOF,YAAY,WAAWG,OAAOH,OAAAA,IAAW,IAAIG,OAAOH,OAAAA,EAASf,QAAQ,MAAM,IAAA,CAAA;AACpF,UAAMmB,IAAI5B,MAAME,KAAK2B,KAAK;AAC1B,UAAMC,MAAM9B,MAAM,GAAA;AAClB,UAAM+B,UAAU,GAAG/B,MAAMgB,KAAAA,CAAAA,IAAUhB,MAAME,KAAK8B,KAAK,CAAA;AACnD,WAAO,yBAAyBJ,CAAAA,IAAKE,GAAAA,UAAaA,GAAAA,IAAO9B,MAAME,KAAKpF,KAAK,CAAA,MAAOiH,OAAAA,QAAeD,GAAAA,IAAO9B,MAAME,KAAK+B,GAAG,CAAA,OAAQpB,EAAEoB,IAAIxB,QAAQ,MAAM,IAAA,CAAA,SAAcqB,GAAAA,IAAO9B,MAAMiB,GAAAA,CAAAA,IAAQI,GAAAA,IAAOK,OAAAA;EAC5L;;;;EAKQvB,iBAAiB/F,IAAqC;AAC5D,UAAM8H,OAAO9H,GAAG+H,YAAW,EAAGC,IAAI5G,kBAAAA;AAClC,UAAM6G,QAAQ,wBAACC,SACbJ,KAAKK,MAAMxF,KAAK,CAACyF,MAAMA,EAAEC,SAASH,IAAAA,GAAOI,aAAa,CAAA,KAAMJ,MADhD;AAEd,UAAMK,UAAUvI,GAAG+H,YAAW,EAAGC,IAAI7H,iBAAAA;AACrC,UAAMyH,QAAQW,QAAQC,WAAWjI,IAAI+H,aAAa,CAAA,KAAM;AACxD,WAAO;MACLb,OAAOK,KAAKW;MACZ/H,OAAOuH,MAAM,OAAA;MACbL;MACAC,KAAKI,MAAM,KAAA;MACXjB,UAAUiB,MAAM,UAAA;MAChBlB,UAAUkB,MAAM,UAAA;IAClB;EACF;;EAGQ9B,WAAWnG,IAA2B;AAC5C,UAAMuI,UAAUvI,GAAG+H,YAAW,EAAGC,IAAI7H,iBAAAA;AACrC,WAAOoI,QAAQC,WAAWE,MAAMJ,aAAa,CAAA,KAAM;EACrD;;;;EAKQ/B,WAAWvG,IAAmB2I,QAAwB;AAC5D,UAAMC,WAAWrB,OAAOvH,GAAG6I,YAAW,EAAG,YAAYR,IAAI,EAAES,YAAW;AACtE,QAAIF,SAASG,SAAS,SAAA,EAAY,QAAO,GAAGJ,MAAAA;AAC5C,QAAIC,SAASG,SAAS,OAAA,KAAYH,SAASG,SAAS,SAAA,EAClD,QAAO,QAAQJ,MAAAA;AACjB,WAAOA;EACT;;EAGQ9C,QAAQ7F,IAA2C;AACzD,UAAM4I,WAAWrB,OAAOvH,GAAG6I,YAAW,EAAG,YAAYR,IAAI,EAAES,YAAW;AACtE,UAAME,UAAUJ,SAASG,SAAS,OAAA,KAAYH,SAASG,SAAS,SAAA;AAChE,UAAME,KAAKD,UAAU,MAAM;AAC3B,WAAO,CAACzI,OAAe,GAAG0I,EAAAA,GAAK1I,EAAAA,GAAK0I,EAAAA;EACtC;EAEA,MAAMC,gBAAgBxI,OAA0C;AAC9D,UAAMV,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMoB,OAAO,MAAMrB,GAAG2C,KAAKf,sBAAsB;MAAElB;IAAM,GAAG;MAAEqC,SAAS;QAAEW,KAAK;MAAM;IAAE,CAAA;AACtF,WAAOrC,KAAKgD,IAAIV,oBAAAA;EAClB;EAEA,MAAMwF,0BACJzI,OACA2H,MACqC;AACrC,UAAMrI,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMW,SAAS,MAAMZ,GAAG0B,QACtBE,sBACA;MAAElB;MAAO2H;IAAK,GACd;MAAEtF,SAAS;QAAEW,KAAK;MAAO;IAAE,CAAA;AAE7B,WAAO9C,SAAS+C,qBAAqB/C,MAAAA,IAAUwI;EACjD;EAEA,MAAMC,4BAA4B3I,OAAe4I,UAA+C;AAC9F,QAAIA,SAAS/H,WAAW,EAAG,QAAO,CAAA;AAClC,UAAMvB,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMoB,OAAO,MAAMrB,GAAG2C,KACpBf,sBACA;MAAElB;MAAOuE,KAAKqE,SAASjF,IAAI,CAAC+D,OAAO;QAAEC,MAAM;UAAEkB,OAAO,GAAGnB,CAAAA;QAAK;MAAE,EAAA;IAAI,GAClE;MAAErF,SAAS;QAAEW,KAAK;MAAM;IAAE,CAAA;AAE5B,WAAOrC,KAAKgD,IAAIV,oBAAAA;EAClB;EAEA,MAAM6F,gBAAgBC,QAAqC;AACzD,UAAMzJ,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMD,GAAG8D,OAAOjC,oBAAoB;MAAE,GAAG4H;IAAO,CAAA;AAChD,UAAMzJ,GAAGK,MAAK;EAChB;EAEA,MAAMqJ,iBAAiBC,OAA6C;AAClE,UAAM3J,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMW,SAAS,MAAMZ,GAAG0B,QAAQG,oBAAoB;MAAE8H;IAAM,CAAA;AAC5D,QAAI,CAAC/I,OAAQ,QAAO;AACpB,UAAM6I,SAAuB;MAAEE,OAAO/I,OAAO+I;MAAOjJ,OAAOE,OAAOF;MAAOgD,KAAK9C,OAAO8C;IAAI;AACzF,UAAM1D,GAAG4J,OAAOhJ,MAAAA,EAAQP,MAAK;AAC7B,WAAOoJ;EACT;EAEA,MAAMI,kBAAkBC,QAAyC;AAC/D,UAAM9J,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMoB,OAAO,MAAMrB,GAAG2C,KAAKd,oBAAoB;MAAE8H,OAAO;QAAEJ,OAAO,GAAGO,MAAAA;MAAU;IAAE,CAAA;AAChF,WAAOzI,KAAKgD,IAAI,CAAC0F,OAAO;MAAEJ,OAAOI,EAAEJ;MAAOjJ,OAAOqJ,EAAErJ;MAAOgD,KAAKqG,EAAErG;IAAI,EAAA;EACvE;EAEA,MAAMsG,aAAaL,OAAeM,SAAiC;AACjE,UAAMjK,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAM8J,IAAI,IAAIG,qBAAAA;AACdH,MAAEJ,QAAQA;AACVI,MAAEE,UAAUA,WAAW;AACvBjK,OAAGmK,QAAQJ,CAAAA;AACX,UAAM/J,GAAGK,MAAK;EAChB;EAEA,MAAM+J,mBAAmBT,OAAqD;AAC5E,UAAM3J,KAAK,KAAKL,IAAIK,GAAGC,KAAI;AAC3B,UAAMW,SAAS,MAAMZ,GAAG0B,QAAQwI,sBAAsB;MAAEP;IAAM,GAAG;MAAE5G,SAAS;QAAExC,IAAI;MAAM;IAAE,CAAA;AAC1F,QAAI,CAACK,OAAQ,QAAO;AACpB,UAAMqJ,UAAUrJ,OAAOqJ,WAAWb;AAClC,UAAMpJ,GAAG4J,OAAOhJ,MAAAA,EAAQP,MAAK;AAC7B,WAAO;MAAE4J;IAAQ;EACnB;AACF;AAEA,SAAS7J,YAAYL,KAAgB;AACnC,QAAMgK,IAAI,IAAI5J,kBAAAA;AACd4J,IAAExJ,KAAKR,IAAIQ;AACXwJ,IAAEtE,WAAW1F,IAAI0F;AACjBsE,IAAEM,kBAAkBtK,IAAIsK;AACxBN,IAAE5H,SAASpC,IAAIoC;AACf4H,IAAEO,QAAQvK,IAAIuK,SAAS;AACvBP,IAAEQ,SAASxK,IAAIwK,UAAU;AACzBR,IAAES,QAAQzK,IAAIyK,SAAS;AACvB,MAAIzK,IAAI0E,UAAU,KAAMsF,GAAEtF,SAAS,IAAIhC,KAAK1C,IAAI0E,MAAM;AACtD,MAAI1E,IAAIoF,aAAaiE,OAAWW,GAAE5E,WAAWpF,IAAIoF;AACjD,MAAIpF,IAAImF,eAAe,KAAM6E,GAAE7E,cAAc,IAAIzC,KAAK1C,IAAImF,WAAW;AACrE,MAAInF,IAAI0K,qBAAqBrB,OAAWW,GAAEU,mBAAmB1K,IAAI0K;AACjEV,IAAErB,OAAO3I,IAAI2I,QAAQ;AACrBqB,IAAEvJ,mBAAmBT,IAAIS,oBAAoB;AAC7CuJ,IAAEW,WAAW3K,IAAI2K,YAAY;AAC7BX,IAAExF,YAAYxE,IAAIwE;AAClBwF,IAAEnH,YAAY7C,IAAI6C;AAClB,SAAOmH;AACT;AAnBS3J;AAqBT,SAASY,cAAc+I,GAAoB;AACzC,SAAO;IACLxJ,IAAIwJ,EAAExJ;IACNkF,UAAUsE,EAAEtE;IACZ4E,iBAAiBN,EAAEM;IACnBlI,QAAQ4H,EAAE5H;IACVmI,OAAOP,EAAEO,SAASlB;IAClBmB,QAAQR,EAAEQ,UAAUnB;IACpBoB,OAAQT,EAAES,SAASpB;IACnB3E,QAAQsF,EAAEtF,QAAQkG,QAAAA;IAClBxF,UAAU4E,EAAE5E,YAAYiE;IACxBlE,aAAa6E,EAAE7E,aAAayF,QAAAA;IAC5BF,kBAAkBV,EAAEU,oBAAoBrB;IACxCV,MAAMqB,EAAErB,QAAQU;IAChB5I,kBAAkBuJ,EAAEvJ,oBAAoB4I;IACxCsB,UAAUX,EAAEW,YAAYtB;IACxB7E,WAAWwF,EAAExF;IACb3B,WAAWmH,EAAEnH;EACf;AACF;AAnBS5B;AAqBT,SAAS+C,mBAAmBI,IAAkB;AAC5C,QAAM4F,IAAI,IAAInI,qBAAAA;AACdmI,IAAErJ,QAAQyD,GAAGzD;AACbqJ,IAAErG,MAAMS,GAAGT;AACXqG,IAAE1B,OAAOlE,GAAGkE;AACZ0B,IAAEa,OAAOzG,GAAGyG;AACZb,IAAEc,SAAS1G,GAAG0G;AACdd,IAAE5H,SAASgC,GAAGhC;AACd4H,IAAEO,QAAQnG,GAAGmG,SAAS;AACtBP,IAAEQ,SAASpG,GAAGoG,UAAU;AACxBR,IAAES,QAAQrG,GAAGqG,SAAS;AACtBT,IAAEe,SAAS3G,GAAG2G,UAAU;AACxBf,IAAEgB,WAAW5G,GAAG4G;AAChB,MAAI5G,GAAG6G,gBAAgB5B,OAAWW,GAAEiB,cAAc7G,GAAG6G;AACrD,MAAI7G,GAAG8G,kBAAkB7B,OAAWW,GAAEkB,gBAAgB9G,GAAG8G;AACzD,MAAI9G,GAAGM,UAAU,KAAMsF,GAAEtF,SAAS,IAAIhC,KAAK0B,GAAGM,MAAM;AACpDsF,IAAEmB,aAAa/G,GAAG+G;AAClBnB,IAAEoB,YAAYhH,GAAGgH;AACjBpB,IAAEqB,aAAajH,GAAGiH;AAClB,SAAOrB;AACT;AApBShG;AAsBT,SAASJ,qBAAqBoG,GAAuB;AACnD,SAAO;IACLrJ,OAAOqJ,EAAErJ;IACTgD,KAAKqG,EAAErG;IACP2E,MAAM0B,EAAE1B;IACRuC,MAAMb,EAAEa;IACRC,QAAQd,EAAEc;IACV1I,QAAQ4H,EAAE5H;IACVmI,OAAOP,EAAEO,SAASlB;IAClBmB,QAAQR,EAAEQ,UAAUnB;IACpBoB,OAAQT,EAAES,SAASpB;IACnB0B,QAASf,EAAEe,UAAU1B;IACrB2B,UAAUhB,EAAEgB;IACZC,aAAajB,EAAEiB,eAAe5B;IAC9B6B,eAAelB,EAAEkB,iBAAiB7B;IAClC3E,QAAQsF,EAAEtF,QAAQkG,QAAAA;IAClBO,YAAYnB,EAAEmB,cAAcnB,EAAEoB;IAC9BA,WAAWpB,EAAEoB;IACbC,YAAYrB,EAAEqB;EAChB;AACF;AApBSzH;","names":["EntitySchema","snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","recoveryAttempts","tags","searchAttributes","priority","createdAt","updatedAt","StepCheckpointEntity","runId","seq","name","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt","RunAttributeEntity","key","strValue","numValue","SignalWaiterEntity","token","BufferedSignalEntity","payload","durableEntities","options","col","workflowRuns","EntitySchema","class","tableName","indexes","properties","type","primary","fieldName","nullable","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","autoincrement","index","ENTITIES","attributeColumnFor","attributeOperand","normalizeAttributeRows","parseDuration","sqlComparator","DURABLE_TABLE_NAMES","Set","targetTable","statement","match","toLowerCase","isRequiredStructure","test","ensureMikroOrmDurableSchema","orm","sql","schema","getUpdateSchemaSQL","safe","ours","split","map","trim","filter","table","undefined","has","connection","em","getConnection","execute","err","message","Error","String","console","warn","alignDurableCollation","isSafeSqlIdentifier","value","platform","getPlatform","name","includes","collate","config","get","charset","rows","current","collation","MikroOrmStateStore","orm","ensureSchema","ensureMikroOrmDurableSchema","createRun","run","em","fork","create","WorkflowRunEntity","toRunEntity","flush","reindexAttributes","id","searchAttributes","updateRun","runId","patch","entity","findOneOrFail","Object","assign","fromRunEntity","attributes","forked","nativeDelete","RunAttributeEntity","rows","normalizeAttributeRows","length","insertMany","getRun","findOne","deleteRun","StepCheckpointEntity","SignalWaiterEntity","pruneTerminalRuns","policy","nowMs","limit","statuses","status","$in","ids","Set","maxAge","cutoff","Date","parseDuration","find","updatedAt","$lt","fields","orderBy","r","add","maxCount","size","offset","idList","slice","transactional","tem","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","upsert","toCheckpointEntity","transaction","work","raw","cp","listIncompleteRuns","map","listPendingRuns","createdAt","listDueTimers","wakeAt","$ne","$lte","tryLockRun","owner","leaseUntilMs","affected","nativeUpdate","$or","lockedUntil","lockedBy","releaseRunLock","renewRunLock","listRuns","query","where","workflow","$and","tag","quote","idQuote","cols","attributeColumns","qb","createQueryBuilder","tagsCol","tagsColumn","pattern","replace","colExpr","jsonAsText","andWhere","f","attributeExistsSql","getResultList","alias","col","attributeColumnFor","numValue","strValue","cmp","sqlComparator","op","operand","attributeOperand","literal","String","a","table","sub","outerId","runPk","key","meta","getMetadata","get","field","prop","props","p","name","fieldNames","runMeta","properties","tableName","tags","colRef","platform","getPlatform","toLowerCase","includes","isMysql","ch","listCheckpoints","getLatestCheckpointByName","undefined","listCheckpointsByNamePrefix","prefixes","$like","putSignalWaiter","waiter","takeSignalWaiter","token","remove","listSignalWaiters","prefix","e","bufferSignal","payload","BufferedSignalEntity","persist","takeBufferedSignal","workflowVersion","input","output","error","recoveryAttempts","priority","getTime","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt"]}
|
|
1
|
+
{"version":3,"sources":["../src/entities.ts","../src/mikro-orm-state-store.ts","../src/schema.ts"],"sourcesContent":["import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema } from '@mikro-orm/core';\n\n/**\n * How property names map to physical column names. The durable tables are adapter-agnostic: a run\n * written by one store adapter must be readable by another, so every adapter has to agree on the\n * physical column names. The default is `'snake_case'` (the canonical, matched by the TypeORM,\n * Prisma and Drizzle adapters). `'preserve'` keeps the camelCase property name verbatim (what the\n * old TypeORM/Prisma adapters produced before they were pinned). A function lets you supply any\n * custom mapping.\n *\n * The choice is pinned EXPLICITLY onto the entity schema rather than left to the host ORM's naming\n * strategy — depending on the host strategy is what silently diverged the adapters and broke a store\n * swap against an existing table.\n */\nexport type DurableColumnNaming = 'snake_case' | 'preserve' | ((property: string) => string);\n\nfunction snakeCase(property: string): string {\n return property.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nfunction columnNamer(naming: DurableColumnNaming): (property: string) => string {\n if (naming === 'snake_case') return snakeCase;\n if (naming === 'preserve') return (property) => property;\n return naming;\n}\n\n// Plain classes (no decorators): the schema/column mapping lives on the EntitySchema built by\n// `durableEntities`, so the same class can be registered under different column names. The store\n// still references these classes for `em.create`/`getMetadata`.\n\nexport class WorkflowRunEntity {\n id!: string;\n workflow!: string;\n workflowVersion!: string;\n status!: RunStatus;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n wakeAt?: Date;\n lockedBy?: string;\n lockedUntil?: Date;\n recoveryAttempts?: number;\n tags?: string[] | null;\n searchAttributes?: Record<string, string | number | boolean> | null;\n priority?: number | null;\n namespace!: string;\n createdAt!: Date;\n updatedAt!: Date;\n}\n\nexport class StepCheckpointEntity {\n runId!: string;\n seq!: number;\n name!: string;\n kind!: StepKind;\n stepId!: string;\n status!: 'pending' | 'running' | 'completed' | 'failed';\n input?: unknown;\n output?: unknown;\n error?: unknown;\n events?: unknown;\n attempts!: number;\n workerGroup?: string;\n parallelGroup?: string;\n wakeAt?: Date;\n enqueuedAt?: Date;\n startedAt!: Date;\n finishedAt!: Date;\n}\n\n/**\n * Normalized side-table for search attributes: one row per (run, key), so range/equality predicates\n * push DOWN into SQL (an EXISTS join indexed on `(key, numValue)` / `(key, strValue)`) instead of a\n * coarse scan + in-process filter. Maintained on every createRun/updateRun. Numbers land in\n * `numValue`, strings/booleans in `strValue` (booleans as \"true\"/\"false\"); see normalizeAttributeRows.\n */\nexport class RunAttributeEntity {\n runId!: string;\n key!: string;\n strValue?: string | null;\n numValue?: number | null;\n}\n\nexport class SignalWaiterEntity {\n token!: string;\n runId!: string;\n seq!: number;\n parallelGroup?: string | null;\n}\n\nexport class BufferedSignalEntity {\n id!: number;\n token!: string;\n payload?: unknown;\n}\n\n/**\n * Build the durable MikroORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your MikroORM config / `MikroOrmModule.forFeature`.\n *\n * ```ts\n * entities: durableEntities({ naming: 'snake_case' })\n * ```\n */\nexport function durableEntities(options: { naming?: DurableColumnNaming } = {}): EntitySchema[] {\n const col = columnNamer(options.naming ?? 'snake_case');\n\n const workflowRuns = new EntitySchema<WorkflowRunEntity>({\n class: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n // Indexes mirror the Prisma adapter so a store swap keeps the same plan. The poller hits these\n // every tick: `(status, wakeAt)` serves both the due-timer scan (status='suspended' AND wakeAt<=now)\n // and, via its `status` prefix, the pending/incomplete status lookups; `(workflow, status)` serves\n // the per-workflow timeout sweep. Without them every poll is a full scan of a table that only grows.\n indexes: [\n { name: 'durable_workflow_runs_status_wake_at_idx', properties: ['status', 'wakeAt'] },\n { name: 'durable_workflow_runs_workflow_status_idx', properties: ['workflow', 'status'] },\n {\n name: 'durable_workflow_runs_namespace_status_idx',\n properties: ['namespace', 'status', 'createdAt'],\n },\n ],\n properties: {\n id: { type: 'string', primary: true, fieldName: col('id') },\n workflow: { type: 'string', fieldName: col('workflow') },\n workflowVersion: { type: 'string', fieldName: col('workflowVersion') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n lockedBy: { type: 'string', nullable: true, fieldName: col('lockedBy') },\n lockedUntil: { type: 'Date', nullable: true, fieldName: col('lockedUntil') },\n recoveryAttempts: { type: 'integer', nullable: true, fieldName: col('recoveryAttempts') },\n tags: { type: 'json', nullable: true, fieldName: col('tags') },\n searchAttributes: { type: 'json', nullable: true, fieldName: col('searchAttributes') },\n priority: { type: 'integer', nullable: true, fieldName: col('priority') },\n namespace: { type: 'string', default: 'default', fieldName: col('namespace') },\n createdAt: { type: 'Date', fieldName: col('createdAt') },\n updatedAt: { type: 'Date', fieldName: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n class: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n seq: { type: 'integer', primary: true, fieldName: col('seq') },\n name: { type: 'string', fieldName: col('name') },\n kind: { type: 'string', fieldName: col('kind') },\n stepId: { type: 'string', fieldName: col('stepId') },\n status: { type: 'string', fieldName: col('status') },\n input: { type: 'json', nullable: true, fieldName: col('input') },\n output: { type: 'json', nullable: true, fieldName: col('output') },\n error: { type: 'json', nullable: true, fieldName: col('error') },\n events: { type: 'json', nullable: true, fieldName: col('events') },\n attempts: { type: 'integer', fieldName: col('attempts') },\n workerGroup: { type: 'string', nullable: true, fieldName: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group string so the dashboard\n // renders them as one \"ran in parallel\" group. The core engine sets it (incl. from a remote\n // worker's recordStep), but it was never persisted here — so the fan always rendered as N\n // sequential singles. Nullable: only fan-out steps carry it.\n parallelGroup: { type: 'string', nullable: true, fieldName: col('parallelGroup') },\n wakeAt: { type: 'Date', nullable: true, fieldName: col('wakeAt') },\n enqueuedAt: { type: 'Date', nullable: true, fieldName: col('enqueuedAt') },\n startedAt: { type: 'Date', fieldName: col('startedAt') },\n finishedAt: { type: 'Date', fieldName: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n class: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n // The search-attribute EXISTS join (see the class docstring) pushes equality/range predicates down\n // onto `(key, numValue)` / `(key, strValue)`; mirror the Prisma adapter so those scans stay indexed.\n indexes: [\n { name: 'durable_run_attributes_key_num_idx', properties: ['key', 'numValue'] },\n { name: 'durable_run_attributes_key_str_idx', properties: ['key', 'strValue'] },\n ],\n properties: {\n runId: { type: 'string', primary: true, fieldName: col('runId') },\n key: { type: 'string', primary: true, fieldName: col('key') },\n strValue: { type: 'string', nullable: true, fieldName: col('strValue') },\n // `float`/`double` is portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, fieldName: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n class: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n properties: {\n token: { type: 'string', primary: true, fieldName: col('token') },\n runId: { type: 'string', fieldName: col('runId') },\n seq: { type: 'integer', fieldName: col('seq') },\n // A ctx.gather_children/ctx.all child fan-out tags every awaited child with the same group; the\n // engine threads it onto the waiter so the resolving `signal:child:` checkpoint carries it and the\n // dashboard renders the fan as one parallel group. Nullable: only fan-out child waiters carry it.\n parallelGroup: { type: 'string', nullable: true, fieldName: col('parallelGroup') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n class: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n properties: {\n id: { type: 'integer', primary: true, autoincrement: true, fieldName: col('id') },\n token: { type: 'string', index: true, fieldName: col('token') },\n payload: { type: 'json', nullable: true, fieldName: col('payload') },\n },\n });\n\n return [workflowRuns, stepCheckpoints, runAttributes, signalWaiters, bufferedSignals];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import {\n type AttributeFilter,\n type RetentionPolicy,\n type RunQuery,\n type SignalWaiter,\n type StateStore,\n type StepCheckpoint,\n type StepError,\n type StepEvent,\n type WorkflowRun,\n attributeColumnFor,\n attributeOperand,\n normalizeAttributeRows,\n parseDuration,\n sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport type { EntityManager, MikroORM } from '@mikro-orm/core';\n\n/** Minimal structural surface of the SQL EntityManager's QueryBuilder (from `@mikro-orm/knex`),\n * typed here so the adapter needn't add a hard dependency on the knex types — a real SQL-driver EM\n * satisfies it at runtime. */\ninterface DurableQueryBuilder<T> {\n where(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n andWhere(cond: string): DurableQueryBuilder<T>;\n orderBy(cond: Record<string, unknown>): DurableQueryBuilder<T>;\n limit(n: number): DurableQueryBuilder<T>;\n offset(n: number): DurableQueryBuilder<T>;\n getResultList(): Promise<T[]>;\n}\ntype SqlEm = EntityManager & {\n createQueryBuilder<T>(entity: unknown, alias: string): DurableQueryBuilder<T>;\n};\n\n/** Resolved side-table column names (per the active naming strategy) for the raw EXISTS pushdown. */\ninterface AttributeColumns {\n table: string;\n /** Side-table FK column (the run id on `durable_run_attributes`). */\n runId: string;\n /** Run table PK column (the `id` on `durable_workflow_runs`) used for the outer correlation. */\n runPk: string;\n key: string;\n strValue: string;\n numValue: string;\n}\nimport {\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { ensureMikroOrmDurableSchema } from './schema';\n\n/**\n * MikroORM-backed `StateStore`. Works on any MikroORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps and `wakeAt` use native datetime columns. Each operation runs on a forked\n * EntityManager so it owns its unit of work.\n */\nexport class MikroOrmStateStore implements StateStore {\n constructor(private readonly orm: MikroORM) {}\n\n async ensureSchema(): Promise<void> {\n await ensureMikroOrmDurableSchema(this.orm);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n const em = this.orm.em.fork();\n em.create(WorkflowRunEntity, toRunEntity(run));\n await em.flush();\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n const em = this.orm.em.fork();\n const entity = await em.findOneOrFail(WorkflowRunEntity, { id: runId });\n Object.assign(entity, toRunEntity({ ...fromRunEntity(entity), ...patch } as WorkflowRun));\n await em.flush();\n // Keep the side-table in step with the run's attributes whenever they're patched.\n if ('searchAttributes' in patch) await this.reindexAttributes(runId, patch.searchAttributes);\n }\n\n /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors\n * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */\n private async reindexAttributes(\n runId: string,\n attributes: WorkflowRun['searchAttributes'],\n forked?: EntityManager,\n ): Promise<void> {\n const em = forked ?? this.orm.em.fork();\n await em.nativeDelete(RunAttributeEntity, { runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await em.insertMany(RunAttributeEntity, rows);\n }\n\n async getRun(runId: string): Promise<WorkflowRun | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(WorkflowRunEntity, { id: runId });\n return entity ? fromRunEntity(entity) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n // Children first, then the run row — checkpoints, signal waiters, and the normalized\n // attribute side-table, so nothing dangles after the run is gone.\n await em.nativeDelete(StepCheckpointEntity, { runId });\n await em.nativeDelete(SignalWaiterEntity, { runId });\n await em.nativeDelete(RunAttributeEntity, { runId });\n await em.nativeDelete(WorkflowRunEntity, { id: runId });\n }\n\n async pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number> {\n if (policy.statuses.length === 0 || limit <= 0) return 0;\n const em = this.orm.em.fork();\n const status = { $in: policy.statuses };\n // Collect ids that violate EITHER bound (most-restrictive keep): too old, or past the count cap.\n const ids = new Set<string>();\n\n if (policy.maxAge != null) {\n const cutoff = new Date(nowMs - parseDuration(policy.maxAge));\n const rows = await em.find(\n WorkflowRunEntity,\n { status, updatedAt: { $lt: cutoff } },\n { fields: ['id'], orderBy: { updatedAt: 'asc' }, limit }, // oldest first\n );\n for (const r of rows) ids.add(r.id);\n }\n\n if (policy.maxCount != null && ids.size < limit) {\n // Everything beyond the newest `maxCount` rows in the status set — skip the kept window via offset.\n const rows = await em.find(\n WorkflowRunEntity,\n { status },\n {\n fields: ['id'],\n orderBy: { updatedAt: 'desc', id: 'desc' },\n limit,\n offset: policy.maxCount,\n },\n );\n for (const r of rows) {\n ids.add(r.id);\n if (ids.size >= limit) break;\n }\n }\n\n if (ids.size === 0) return 0;\n const idList = [...ids].slice(0, limit);\n const runId = { $in: idList };\n // Cascade children then runs (mirrors deleteRun) in one transaction so a pruned run never dangles.\n await em.transactional(async (tem) => {\n await tem.nativeDelete(StepCheckpointEntity, { runId });\n await tem.nativeDelete(SignalWaiterEntity, { runId });\n await tem.nativeDelete(RunAttributeEntity, { runId });\n await tem.nativeDelete(WorkflowRunEntity, { id: runId });\n });\n return idList.length;\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(StepCheckpointEntity, { runId, seq });\n return entity ? fromCheckpointEntity(entity) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(checkpoint));\n await em.flush();\n }\n\n async transaction<T>(\n work: (tx: {\n raw: unknown;\n saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;\n }) => Promise<T>,\n ): Promise<T> {\n return this.orm.em.fork().transactional(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.upsert(StepCheckpointEntity, toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, {\n status: { $in: ['running', 'cancelling'] },\n ...(namespace !== undefined ? { namespace } : {}),\n });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(\n WorkflowRunEntity,\n { status: 'pending', ...(namespace !== undefined ? { namespace } : {}) },\n { orderBy: { createdAt: 'asc' }, limit }, // FIFO dispatch\n );\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(WorkflowRunEntity, {\n status: 'suspended',\n wakeAt: { $ne: null, $lte: new Date(nowMs) },\n ...(namespace !== undefined ? { namespace } : {}),\n });\n return rows.map(fromRunEntity);\n }\n\n async tryLockRun(\n runId: string,\n owner: string,\n leaseUntilMs: number,\n nowMs: number,\n ): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, $or: [{ lockedUntil: null }, { lockedUntil: { $lte: new Date(nowMs) } }] },\n { lockedBy: owner, lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n const em = this.orm.em.fork();\n await em.nativeUpdate(WorkflowRunEntity, { id: runId }, { lockedBy: null, lockedUntil: null });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const em = this.orm.em.fork();\n const affected = await em.nativeUpdate(\n WorkflowRunEntity,\n { id: runId, lockedBy: owner },\n { lockedUntil: new Date(leaseUntilMs) },\n );\n return affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n const em = this.orm.em.fork();\n const where: Record<string, unknown> = {};\n if (query.workflow) where.workflow = query.workflow;\n if (query.namespace !== undefined) where.namespace = query.namespace;\n // `status IN (...)`; an empty set matches nothing (mirrors the in-memory store). When both the\n // single `status` and `statuses` are set, AND them so the narrower set wins.\n if (query.status && query.statuses) {\n where.$and = [{ status: query.status }, { status: { $in: query.statuses } }];\n } else if (query.status) {\n where.status = query.status;\n } else if (query.statuses) {\n where.status = { $in: query.statuses };\n }\n const orderBy = { createdAt: 'desc' as const }; // newest first — recent runs on top in the dashboard\n // `tags`/attributes need raw SQL pushed through the QueryBuilder:\n // - tag: `tags` is a JSON column (native json/jsonb on PG/MySQL), and MikroORM JSON-serializes a\n // `{ $like }` operand (wrapping/escaping the LIKE pattern) so a plain where corrupts it to no\n // matches. A raw LIKE on the column-as-text matches the quoted token so `etl` doesn't match\n // `etl-foo`. Postgres `jsonb` rejects `LIKE` directly, so cast to text per dialect.\n // - attributes: each filter becomes a raw EXISTS against the normalized side-table, so the DB\n // filters AND paginates — no full scan + in-process filter (ANDed: one EXISTS per filter).\n // Use a QueryBuilder with a fixed root alias `r` so the raw correlations are stable across drivers.\n if (query.tag || query.attributes?.length) {\n const quote = this.idQuote(em);\n const cols = this.attributeColumns(em);\n const qb = (em as SqlEm)\n .createQueryBuilder<WorkflowRunEntity>(WorkflowRunEntity, 'r')\n .where(where)\n .orderBy(orderBy);\n if (query.tag) {\n const tagsCol = this.tagsColumn(em);\n const pattern = `%\"${query.tag.replace(/'/g, \"''\")}\"%`;\n const colExpr = this.jsonAsText(em, `${quote('r')}.${quote(tagsCol)}`);\n qb.andWhere(`${colExpr} LIKE '${pattern}'`);\n }\n for (const f of query.attributes ?? []) {\n qb.andWhere(this.attributeExistsSql(f, quote, 'r', cols));\n }\n if (query.limit != null) qb.limit(query.limit);\n if (query.offset != null) qb.offset(query.offset);\n const rows = await qb.getResultList();\n return rows.map(fromRunEntity);\n }\n const rows = await em.find(WorkflowRunEntity, where, {\n ...(query.limit != null ? { limit: query.limit } : {}),\n ...(query.offset != null ? { offset: query.offset } : {}),\n orderBy,\n });\n return rows.map(fromRunEntity);\n }\n\n /** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run\n * alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):\n * EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric\n * operands compare the num column, everything else the str column. Identifiers are quoted per\n * driver and operands are inlined as literals (string operands are escaped by doubling quotes). */\n private attributeExistsSql(\n f: AttributeFilter,\n quote: (id: string) => string,\n alias: string,\n cols: AttributeColumns,\n ): string {\n const col = attributeColumnFor(f) === 'numValue' ? cols.numValue : cols.strValue;\n const cmp = sqlComparator(f.op);\n const operand = attributeOperand(f);\n const literal =\n typeof operand === 'number' ? String(operand) : `'${String(operand).replace(/'/g, \"''\")}'`;\n const a = quote(cols.table);\n const sub = quote('a');\n const outerId = `${quote(alias)}.${quote(cols.runPk)}`; // run PK column on the outer alias\n return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, \"''\")}' AND ${sub}.${quote(col)} ${cmp} ${literal})`;\n }\n\n /** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches\n * the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is\n * reused for BOTH the outer correlation and the side-table FK (they share the same fieldName). */\n private attributeColumns(em: EntityManager): AttributeColumns {\n const meta = em.getMetadata().get(RunAttributeEntity);\n const field = (prop: string) =>\n meta.props.find((p) => p.name === prop)?.fieldNames?.[0] ?? prop;\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n const runPk = runMeta.properties.id?.fieldNames?.[0] ?? 'id';\n return {\n table: meta.tableName,\n runId: field('runId'),\n runPk,\n key: field('key'),\n strValue: field('strValue'),\n numValue: field('numValue'),\n };\n }\n\n /** Resolve the `tags` column name from metadata (per the active naming strategy), for the raw LIKE. */\n private tagsColumn(em: EntityManager): string {\n const runMeta = em.getMetadata().get(WorkflowRunEntity);\n return runMeta.properties.tags?.fieldNames?.[0] ?? 'tags';\n }\n\n /** Wrap a JSON column reference so it can be `LIKE`d as text. Postgres `jsonb`/`json` rejects `LIKE`\n * directly (`operator does not exist: jsonb ~~ unknown`) — cast to `text`; MySQL JSON needs an\n * explicit `CHAR` cast; SQLite stores JSON as text already, so no cast. */\n private jsonAsText(em: EntityManager, colRef: string): string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n if (platform.includes('postgre')) return `${colRef}::text`;\n if (platform.includes('mysql') || platform.includes('mariadb'))\n return `CAST(${colRef} AS CHAR)`;\n return colRef;\n }\n\n /** Quote an identifier per the active SQL driver (MySQL/MariaDB backtick, others double-quote). */\n private idQuote(em: EntityManager): (id: string) => string {\n const platform = String(em.getPlatform().constructor.name).toLowerCase();\n const isMysql = platform.includes('mysql') || platform.includes('mariadb');\n const ch = isMysql ? '`' : '\"';\n return (id: string) => `${ch}${id}${ch}`;\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(StepCheckpointEntity, { runId }, { orderBy: { seq: 'asc' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(\n StepCheckpointEntity,\n { runId, name },\n { orderBy: { seq: 'desc' } },\n );\n return entity ? fromCheckpointEntity(entity) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const em = this.orm.em.fork();\n const rows = await em.find(\n StepCheckpointEntity,\n { runId, $or: prefixes.map((p) => ({ name: { $like: `${p}%` } })) },\n { orderBy: { seq: 'asc' } },\n );\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n const em = this.orm.em.fork();\n await em.upsert(SignalWaiterEntity, {\n token: waiter.token,\n runId: waiter.runId,\n seq: waiter.seq,\n parallelGroup: waiter.parallelGroup ?? null,\n });\n await em.flush();\n }\n\n async takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(SignalWaiterEntity, { token });\n if (!entity) return null;\n const waiter: SignalWaiter = {\n token: entity.token,\n runId: entity.runId,\n seq: entity.seq,\n parallelGroup: entity.parallelGroup ?? undefined,\n };\n await em.remove(entity).flush();\n return waiter;\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const em = this.orm.em.fork();\n const rows = await em.find(SignalWaiterEntity, { token: { $like: `${prefix}%` } });\n return rows.map((e) => ({\n token: e.token,\n runId: e.runId,\n seq: e.seq,\n parallelGroup: e.parallelGroup ?? undefined,\n }));\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n const em = this.orm.em.fork();\n const e = new BufferedSignalEntity();\n e.token = token;\n e.payload = payload ?? null;\n em.persist(e);\n await em.flush();\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const em = this.orm.em.fork();\n const entity = await em.findOne(BufferedSignalEntity, { token }, { orderBy: { id: 'asc' } });\n if (!entity) return null;\n const payload = entity.payload ?? undefined;\n await em.remove(entity).flush();\n return { payload };\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n const e = new WorkflowRunEntity();\n e.id = run.id;\n e.workflow = run.workflow;\n e.workflowVersion = run.workflowVersion;\n e.status = run.status;\n e.input = run.input ?? null;\n e.output = run.output ?? null;\n e.error = run.error ?? null;\n if (run.wakeAt != null) e.wakeAt = new Date(run.wakeAt);\n if (run.lockedBy !== undefined) e.lockedBy = run.lockedBy;\n if (run.lockedUntil != null) e.lockedUntil = new Date(run.lockedUntil);\n if (run.recoveryAttempts !== undefined) e.recoveryAttempts = run.recoveryAttempts;\n e.tags = run.tags ?? null;\n e.searchAttributes = run.searchAttributes ?? null;\n e.priority = run.priority ?? null;\n e.namespace = run.namespace ?? 'default';\n e.createdAt = run.createdAt;\n e.updatedAt = run.updatedAt;\n return e;\n}\n\nfunction fromRunEntity(e: WorkflowRunEntity): WorkflowRun {\n return {\n id: e.id,\n workflow: e.workflow,\n workflowVersion: e.workflowVersion,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n wakeAt: e.wakeAt?.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil?.getTime(),\n recoveryAttempts: e.recoveryAttempts ?? undefined,\n tags: e.tags ?? undefined,\n searchAttributes: e.searchAttributes ?? undefined,\n priority: e.priority ?? undefined,\n namespace: e.namespace,\n createdAt: e.createdAt,\n updatedAt: e.updatedAt,\n };\n}\n\nfunction toCheckpointEntity(cp: StepCheckpoint): StepCheckpointEntity {\n const e = new StepCheckpointEntity();\n e.runId = cp.runId;\n e.seq = cp.seq;\n e.name = cp.name;\n e.kind = cp.kind;\n e.stepId = cp.stepId;\n e.status = cp.status;\n e.input = cp.input ?? null;\n e.output = cp.output ?? null;\n e.error = cp.error ?? null;\n e.events = cp.events ?? null;\n e.attempts = cp.attempts;\n if (cp.workerGroup !== undefined) e.workerGroup = cp.workerGroup;\n if (cp.parallelGroup !== undefined) e.parallelGroup = cp.parallelGroup;\n if (cp.wakeAt != null) e.wakeAt = new Date(cp.wakeAt);\n e.enqueuedAt = cp.enqueuedAt;\n e.startedAt = cp.startedAt;\n e.finishedAt = cp.finishedAt;\n return e;\n}\n\nfunction fromCheckpointEntity(e: StepCheckpointEntity): StepCheckpoint {\n return {\n runId: e.runId,\n seq: e.seq,\n name: e.name,\n kind: e.kind,\n stepId: e.stepId,\n status: e.status,\n input: e.input ?? undefined,\n output: e.output ?? undefined,\n error: (e.error ?? undefined) as StepError | undefined,\n events: (e.events ?? undefined) as StepEvent[] | undefined,\n attempts: e.attempts,\n workerGroup: e.workerGroup ?? undefined,\n parallelGroup: e.parallelGroup ?? undefined,\n wakeAt: e.wakeAt?.getTime(),\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n","import type { MikroORM } from '@mikro-orm/core';\n\n/**\n * The tables this store owns. Kept in sync with the `tableName`s in `./entities`. Used to scope\n * schema management to our own tables (see {@link ensureMikroOrmDurableSchema}).\n */\nconst DURABLE_TABLE_NAMES = new Set([\n 'durable_workflow_runs',\n 'durable_step_checkpoints',\n 'durable_run_attributes',\n 'durable_signal_waiters',\n 'durable_buffered_signals',\n]);\n\n/**\n * The table a DDL statement targets, lower-cased; undefined otherwise. Recognizes `create table` /\n * `alter table` (the table name follows immediately) and standalone `create [unique] index ... on\n * <table>` (the table follows `on`). MikroORM adds an index to an EXISTING table as `alter table ...\n * add index` on MySQL but as a standalone `create index ... on ...` on Postgres/SQLite — without the\n * second form those index statements would be dropped by the durable-table filter and never applied.\n */\nfunction targetTable(statement: string): string | undefined {\n const match = statement.match(\n /^(?:create\\s+(?:unique\\s+)?index\\s+.+?\\s+on\\s+|(?:create|alter)\\s+table\\s+(?:if\\s+not\\s+exists\\s+)?)[`\"']?([\\w$]+)[`\"']?/i,\n );\n return match?.[1]?.toLowerCase();\n}\n\n/**\n * Whether a statement adds structure the store REQUIRES — a failure here is fatal (the store can't\n * run without the table/column/index). A column TYPE alignment (`modify`/`alter column ... type`)\n * is best-effort: the column already holds the data (the store serializes JSON to/from it regardless\n * of the declared type), so failing to converge its type — e.g. a legacy `longtext` value that won't\n * cast to `json` because it was truncated under an older `text` column — must NOT crash boot.\n */\nfunction isRequiredStructure(statement: string): boolean {\n return /\\b(?:create\\s+table|create\\s+(?:unique\\s+)?index|add\\s+(?:column|index|constraint|key|unique|fulltext))\\b/i.test(\n statement,\n );\n}\n\n/**\n * Idempotently create/extend ONLY the durable tables (additive — never drops). Safe to run on every\n * boot (auto-schema) and from a MikroORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables extends Migration {\n * async up() { await ensureMikroOrmDurableSchema(this.getEntityManager().getOrm()); }\n * }\n * ```\n *\n * It does NOT run a whole-ORM `schema.update()`. When the store shares the host app's ORM (the\n * recommended setup — a single ORM avoids MikroORM's global-metadata clobber between instances),\n * a full `schema.update()` would try to reconcile EVERY app table to the entity metadata: on a\n * migration-managed schema that means recreating the migrations table and dropping app foreign keys.\n * Instead we compute the safe additive diff and execute only the statements that target our own\n * `durable_*` tables. `getUpdateSchemaSQL` already emits a `create table` only for a missing table\n * and `alter table ... add` only for missing columns, so existing tables — and the entire rest of\n * the host schema — are left untouched.\n */\nexport async function ensureMikroOrmDurableSchema(orm: MikroORM): Promise<void> {\n const sql = await orm.schema.getUpdateSchemaSQL({ safe: true });\n const ours = sql\n .split(';')\n .map((statement) => statement.trim())\n .filter((statement) => {\n const table = targetTable(statement);\n return table !== undefined && DURABLE_TABLE_NAMES.has(table);\n });\n const connection = orm.em.getConnection();\n for (const statement of ours) {\n try {\n await connection.execute(statement);\n } catch (err) {\n // Required structure (create table / add column / add index) must succeed — rethrow.\n if (isRequiredStructure(statement)) {\n throw err;\n }\n // A best-effort type alignment (e.g. legacy `longtext` → `json`) failed, typically because an\n // existing column holds a value that can't cast to the target type (a relic of an older column\n // type). The column already stores the data and the store reads/writes it via serialization, so\n // leave it as-is and continue rather than crashing boot. Surfaced so the data can be repaired\n // out of band (e.g. `UPDATE … SET col = NULL WHERE JSON_VALID(col) = 0` for a disposable column).\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] skipped a non-structural durable-schema statement that failed; the column is left as-is (functional) — repair the data out of band to converge its type. Statement: ${statement} — ${message}`,\n );\n }\n }\n\n // Converge collation last, once the tables exist (whether just created above or pre-existing).\n await alignDurableCollation(orm);\n}\n\n/** A MySQL identifier we are willing to interpolate into DDL (charset / collation names). */\nfunction isSafeSqlIdentifier(value: string): boolean {\n return /^[A-Za-z0-9_]+$/.test(value);\n}\n\n/**\n * Align each durable table's collation to the ORM's configured `collate` (MySQL/MariaDB only).\n *\n * MikroORM's auto-schema (`getUpdateSchemaSQL`) creates tables with the server's DEFAULT collation —\n * on MySQL 8.4 that's `utf8mb4_0900_ai_ci` — and ignores the `collate` config option. When the host\n * app pins a different collation on its own tables (commonly `utf8mb4_unicode_ci` via migrations), a\n * JOIN between a durable table and an app table throws \"Illegal mix of collations\". We converge it\n * here: read the configured collation and `CONVERT` only the durable tables whose collation differs.\n *\n * Idempotent (matching tables are skipped), non-fatal (a CONVERT failure is warned, never crashes\n * boot), and a no-op when no `collate` is configured or the platform isn't MySQL/MariaDB.\n */\nasync function alignDurableCollation(orm: MikroORM): Promise<void> {\n const platform = String(orm.em.getPlatform().constructor.name).toLowerCase();\n if (!platform.includes('mysql') && !platform.includes('maria')) {\n return;\n }\n const collate = orm.config.get('collate');\n if (typeof collate !== 'string' || !isSafeSqlIdentifier(collate)) {\n return;\n }\n // utf8mb4_unicode_ci → utf8mb4. CONVERT TO needs the charset that owns the collation.\n const charset = collate.split('_')[0];\n if (!charset || !isSafeSqlIdentifier(charset)) {\n return;\n }\n const connection = orm.em.getConnection();\n for (const table of DURABLE_TABLE_NAMES) {\n try {\n const rows = (await connection.execute(\n 'select table_collation as collation from information_schema.tables where table_schema = database() and table_name = ? limit 1',\n [table],\n )) as Array<{ collation?: string | null }>;\n const current = rows[0]?.collation;\n // Table absent (nothing to align) or already at the target collation — skip.\n if (current == null || current === collate) {\n continue;\n }\n await connection.execute(\n `alter table \\`${table}\\` convert to character set ${charset} collate ${collate}`,\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n `[nestjs-durable-store-mikro-orm] could not align collation for \\`${table}\\` to ${collate} (left as-is): ${message}`,\n );\n }\n }\n}\n"],"mappings":"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","names":["EntitySchema","snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","recoveryAttempts","tags","searchAttributes","priority","namespace","createdAt","updatedAt","StepCheckpointEntity","runId","seq","name","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt","RunAttributeEntity","key","strValue","numValue","SignalWaiterEntity","token","BufferedSignalEntity","payload","durableEntities","options","col","workflowRuns","EntitySchema","class","tableName","indexes","properties","type","primary","fieldName","nullable","default","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","autoincrement","index","ENTITIES","attributeColumnFor","attributeOperand","normalizeAttributeRows","parseDuration","sqlComparator","DURABLE_TABLE_NAMES","Set","targetTable","statement","match","toLowerCase","isRequiredStructure","test","ensureMikroOrmDurableSchema","orm","sql","schema","getUpdateSchemaSQL","safe","ours","split","map","trim","filter","table","undefined","has","connection","em","getConnection","execute","err","message","Error","String","console","warn","alignDurableCollation","isSafeSqlIdentifier","value","platform","getPlatform","name","includes","collate","config","get","charset","rows","current","collation","MikroOrmStateStore","orm","ensureSchema","ensureMikroOrmDurableSchema","createRun","run","em","fork","create","WorkflowRunEntity","toRunEntity","flush","reindexAttributes","id","searchAttributes","updateRun","runId","patch","entity","findOneOrFail","Object","assign","fromRunEntity","attributes","forked","nativeDelete","RunAttributeEntity","rows","normalizeAttributeRows","length","insertMany","getRun","findOne","deleteRun","StepCheckpointEntity","SignalWaiterEntity","pruneTerminalRuns","policy","nowMs","limit","statuses","status","$in","ids","Set","maxAge","cutoff","Date","parseDuration","find","updatedAt","$lt","fields","orderBy","r","add","maxCount","size","offset","idList","slice","transactional","tem","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","upsert","toCheckpointEntity","transaction","work","raw","cp","listIncompleteRuns","namespace","undefined","map","listPendingRuns","createdAt","listDueTimers","wakeAt","$ne","$lte","tryLockRun","owner","leaseUntilMs","affected","nativeUpdate","$or","lockedUntil","lockedBy","releaseRunLock","renewRunLock","listRuns","query","where","workflow","$and","tag","quote","idQuote","cols","attributeColumns","qb","createQueryBuilder","tagsCol","tagsColumn","pattern","replace","colExpr","jsonAsText","andWhere","f","attributeExistsSql","getResultList","alias","col","attributeColumnFor","numValue","strValue","cmp","sqlComparator","op","operand","attributeOperand","literal","String","a","table","sub","outerId","runPk","key","meta","getMetadata","get","field","prop","props","p","name","fieldNames","runMeta","properties","tableName","tags","colRef","platform","getPlatform","toLowerCase","includes","isMysql","ch","listCheckpoints","getLatestCheckpointByName","listCheckpointsByNamePrefix","prefixes","$like","putSignalWaiter","waiter","token","parallelGroup","takeSignalWaiter","remove","listSignalWaiters","prefix","e","bufferSignal","payload","BufferedSignalEntity","persist","takeBufferedSignal","workflowVersion","input","output","error","recoveryAttempts","priority","getTime","kind","stepId","events","attempts","workerGroup","enqueuedAt","startedAt","finishedAt"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudousxd/nestjs-durable-store-mikro-orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "MikroORM adapter for the nestjs-durable StateStore (reference ORM adapter)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Davide Carvalho",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"reflect-metadata": "0.2.2",
|
|
32
32
|
"tsup": "8.3.5",
|
|
33
33
|
"typescript": "5.9.3",
|
|
34
|
-
"@dudousxd/nestjs-durable-core": "^0.
|
|
35
|
-
"@dudousxd/nestjs-durable-testing": "^0.7.
|
|
34
|
+
"@dudousxd/nestjs-durable-core": "^0.40.1",
|
|
35
|
+
"@dudousxd/nestjs-durable-testing": "^0.7.1"
|
|
36
36
|
},
|
|
37
37
|
"module": "./dist/index.js",
|
|
38
38
|
"exports": {
|