@dudousxd/nestjs-durable-store-typeorm 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +30 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +30 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -90,6 +90,7 @@ var WorkflowRunEntity = class {
|
|
|
90
90
|
tags;
|
|
91
91
|
searchAttributes;
|
|
92
92
|
priority;
|
|
93
|
+
origin;
|
|
93
94
|
createdAt;
|
|
94
95
|
updatedAt;
|
|
95
96
|
};
|
|
@@ -236,6 +237,18 @@ function durableEntities(options = {}) {
|
|
|
236
237
|
nullable: true,
|
|
237
238
|
name: col("priority")
|
|
238
239
|
},
|
|
240
|
+
// Which library/module registered the workflow this run belongs to (e.g.
|
|
241
|
+
// `@dudousxd/nestjs-catalog-pipeline`), derived at registration time — never caller input.
|
|
242
|
+
// Nullable with NO default: an origin cannot be reconstructed for a row written before this
|
|
243
|
+
// column existed, so it stays NULL and reads back as `undefined` = "unknown". Defaulting it to
|
|
244
|
+
// a real-looking string would be indistinguishable from a genuine registration in the
|
|
245
|
+
// dashboard's facet. `ensureTypeOrmDurableSchema` back-fills the COLUMN onto pre-existing
|
|
246
|
+
// tables (see the `additive` map in ./schema), never its value.
|
|
247
|
+
origin: {
|
|
248
|
+
type: "text",
|
|
249
|
+
nullable: true,
|
|
250
|
+
name: col("origin")
|
|
251
|
+
},
|
|
239
252
|
createdAt: {
|
|
240
253
|
type: Date,
|
|
241
254
|
name: col("createdAt")
|
|
@@ -552,7 +565,7 @@ async function ensureTypeOrmDurableSchema(dataSource) {
|
|
|
552
565
|
${runsCol("wakeAt")} ${ts}, ${runsCol("lockedBy")} ${str}, ${runsCol("lockedUntil")} ${ts},
|
|
553
566
|
${runsCol("awaitingDecisionTaskId")} ${str},
|
|
554
567
|
${runsCol("recoveryAttempts")} ${int}, ${runsCol("tags")} ${txt}, ${runsCol("searchAttributes")} ${txt},
|
|
555
|
-
${runsCol("priority")} ${int},
|
|
568
|
+
${runsCol("priority")} ${int}, ${runsCol("origin")} ${str},
|
|
556
569
|
${runsCol("createdAt")} ${ts} NOT NULL, ${runsCol("updatedAt")} ${ts} NOT NULL
|
|
557
570
|
)`,
|
|
558
571
|
`CREATE TABLE IF NOT EXISTS ${checkpoints} (
|
|
@@ -618,6 +631,14 @@ async function ensureTypeOrmDurableSchema(dataSource) {
|
|
|
618
631
|
[
|
|
619
632
|
"priority",
|
|
620
633
|
int
|
|
634
|
+
],
|
|
635
|
+
// Nullable and NOT back-filled with a value: every row that predates the column keeps a NULL
|
|
636
|
+
// origin, which the store reads as `undefined` = "unknown origin". There is no honest value to
|
|
637
|
+
// back-fill — the origin is derived from the registration that created the run, which is long
|
|
638
|
+
// gone by the time this ALTER runs.
|
|
639
|
+
[
|
|
640
|
+
"origin",
|
|
641
|
+
str
|
|
621
642
|
]
|
|
622
643
|
],
|
|
623
644
|
durable_step_checkpoints: [
|
|
@@ -797,6 +818,7 @@ var TypeOrmStateStore = class {
|
|
|
797
818
|
if ("tags" in patch) update.tags = patch.tags ?? null;
|
|
798
819
|
if ("searchAttributes" in patch) update.searchAttributes = patch.searchAttributes ?? null;
|
|
799
820
|
if ("priority" in patch) update.priority = patch.priority ?? null;
|
|
821
|
+
if ("origin" in patch) update.origin = patch.origin ?? null;
|
|
800
822
|
if ("createdAt" in patch) update.createdAt = patch.createdAt;
|
|
801
823
|
if ("updatedAt" in patch) update.updatedAt = patch.updatedAt;
|
|
802
824
|
const result = await this.runs().createQueryBuilder().update().set(update).where({
|
|
@@ -910,6 +932,9 @@ var TypeOrmStateStore = class {
|
|
|
910
932
|
if (query.workflow) qb.andWhere("r.workflow = :workflow", {
|
|
911
933
|
workflow: query.workflow
|
|
912
934
|
});
|
|
935
|
+
if (query.origin !== void 0) qb.andWhere("r.origin = :origin", {
|
|
936
|
+
origin: query.origin
|
|
937
|
+
});
|
|
913
938
|
if (query.status) qb.andWhere("r.status = :status", {
|
|
914
939
|
status: query.status
|
|
915
940
|
});
|
|
@@ -1112,6 +1137,8 @@ function toRunEntity(run) {
|
|
|
1112
1137
|
tags: run.tags ?? null,
|
|
1113
1138
|
searchAttributes: run.searchAttributes ?? null,
|
|
1114
1139
|
priority: run.priority ?? null,
|
|
1140
|
+
// Absent origin stays SQL NULL — "unknown", never coerced into a real-looking library name.
|
|
1141
|
+
origin: run.origin ?? null,
|
|
1115
1142
|
createdAt: run.createdAt,
|
|
1116
1143
|
updatedAt: run.updatedAt
|
|
1117
1144
|
};
|
|
@@ -1134,6 +1161,8 @@ function fromRunEntity(e) {
|
|
|
1134
1161
|
tags: e.tags ?? void 0,
|
|
1135
1162
|
searchAttributes: e.searchAttributes ?? void 0,
|
|
1136
1163
|
priority: e.priority ?? void 0,
|
|
1164
|
+
// NULL (a row written before the column existed) surfaces as `undefined` = unknown origin.
|
|
1165
|
+
origin: e.origin ?? void 0,
|
|
1137
1166
|
createdAt: e.createdAt,
|
|
1138
1167
|
updatedAt: e.updatedAt
|
|
1139
1168
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/entities.ts","../src/schema.ts","../src/typeorm-state-store.ts"],"sourcesContent":["export * from './entities';\nexport * from './schema';\nexport * from './typeorm-state-store';\n","import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema, type ValueTransformer } from 'typeorm';\n\n/**\n * Safely deserialize a JSON-text column back to an object on READ.\n *\n * A column truncated by the old MySQL `text` 64KB limit (or otherwise corrupt) makes `JSON.parse`\n * throw \"Unterminated string in JSON\" — which, without this guard, would 500 the *entire* run-detail\n * read just because one blob is damaged. Instead, log once and degrade the bad field to `undefined`\n * so the rest of the run still renders. Valid JSON is unchanged.\n */\nexport function parseJsonColumn<T = unknown>(raw: unknown, field: string): T | undefined {\n if (raw == null) return undefined;\n // Some drivers (e.g. a native `json` column) already hand back a parsed object.\n if (typeof raw !== 'string') return raw as T;\n try {\n return JSON.parse(raw) as T;\n } catch (err) {\n console.warn(\n `durable store: failed to parse ${field} column, degrading to undefined: ${\n (err as Error).message\n }`,\n );\n return undefined;\n }\n}\n\n/**\n * TypeORM transformer for the free-form JSON-blob columns. Mirrors the built-in `simple-json`\n * serialization on WRITE (`JSON.stringify`, SQL NULL stays NULL) but uses {@link parseJsonColumn}\n * on READ so a single corrupt/truncated row degrades to `undefined` instead of throwing.\n */\nfunction jsonColumnTransformer(field: string): ValueTransformer {\n return {\n to: (value: unknown) => (value == null ? null : JSON.stringify(value)),\n from: (value: unknown) => parseJsonColumn(value, field),\n };\n}\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 MikroORM,\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\n/** Resolve a `DurableColumnNaming` to a `(property) => columnName` function. */\nexport function 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 `getRepository`/`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 | null;\n lockedUntil?: Date;\n awaitingDecisionTaskId?: string | null;\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 | null;\n parallelGroup?: string | null;\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/** A published event that matched no live waiter (see `engine.publishEvent`'s buffering). Keyed by a\n * caller-minted `id` (a uuid, not autoincrement — `removeBufferedEvent` targets it directly), not by\n * `runId`: events are name-based pub/sub, not tied to any one run. */\nexport class BufferedEventEntity {\n id!: string;\n name!: string;\n payload?: unknown;\n publishedAt!: Date;\n}\n\n/**\n * Build the durable TypeORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your `DataSource`/`TypeOrmModule.forFeature`. The JSON-blob\n * columns keep the `jsonColumnTransformer` (corruption-tolerant read), the primary keys / auto-\n * increment / nullability are preserved, and the `id` of `durable_buffered_signals` stays a driver-\n * native auto-increment column.\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 name: 'WorkflowRunEntity',\n target: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n columns: {\n id: { type: 'text', primary: true, name: col('id') },\n workflow: { type: 'text', name: col('workflow') },\n workflowVersion: { type: 'text', name: col('workflowVersion') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('runs.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('runs.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('runs.error'),\n },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n lockedBy: { type: 'text', nullable: true, name: col('lockedBy') },\n lockedUntil: { type: Date, nullable: true, name: col('lockedUntil') },\n // REMOTE turn the engine suspended on awaiting a decision; matched by completeRemoteDecision so\n // only the currently-awaited turn's decision is applied. Nullable: cleared when a decision lands.\n awaitingDecisionTaskId: {\n type: 'text',\n nullable: true,\n name: col('awaitingDecisionTaskId'),\n },\n recoveryAttempts: { type: 'integer', nullable: true, name: col('recoveryAttempts') },\n tags: {\n type: 'text',\n nullable: true,\n name: col('tags'),\n transformer: jsonColumnTransformer('runs.tags'),\n },\n searchAttributes: {\n type: 'text',\n nullable: true,\n name: col('searchAttributes'),\n transformer: jsonColumnTransformer('runs.searchAttributes'),\n },\n priority: { type: 'integer', nullable: true, name: col('priority') },\n createdAt: { type: Date, name: col('createdAt') },\n updatedAt: { type: Date, name: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n name: 'StepCheckpointEntity',\n target: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n seq: { type: 'integer', primary: true, name: col('seq') },\n name: { type: 'text', name: col('name') },\n kind: { type: 'text', name: col('kind') },\n stepId: { type: 'text', name: col('stepId') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('checkpoints.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('checkpoints.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('checkpoints.error'),\n },\n events: {\n type: 'text',\n nullable: true,\n name: col('events'),\n transformer: jsonColumnTransformer('checkpoints.events'),\n },\n attempts: { type: 'integer', name: col('attempts') },\n workerGroup: { type: 'text', nullable: true, name: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group so the dashboard renders\n // them as one parallel group; the core engine sets it (incl. from a remote worker's recordStep).\n parallelGroup: { type: 'text', nullable: true, name: col('parallelGroup') },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n enqueuedAt: { type: Date, nullable: true, name: col('enqueuedAt') },\n startedAt: { type: Date, name: col('startedAt') },\n finishedAt: { type: Date, name: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n name: 'RunAttributeEntity',\n target: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n key: { type: 'text', primary: true, name: col('key') },\n strValue: { type: 'text', nullable: true, name: col('strValue') },\n // `float`/`double` portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, name: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n name: 'SignalWaiterEntity',\n target: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n columns: {\n token: { type: 'text', primary: true, name: col('token') },\n runId: { type: 'text', name: col('runId') },\n seq: { type: 'integer', name: 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: 'text', nullable: true, name: col('parallelGroup') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n name: 'BufferedSignalEntity',\n target: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n columns: {\n // Auto-increment PK for FIFO ordering. Left as TypeORM's default increment generation so it\n // maps to the driver-native auto-increment column (INTEGER on SQLite, BIGINT/SERIAL elsewhere).\n id: { type: Number, primary: true, generated: 'increment', name: col('id') },\n token: { type: 'text', name: col('token') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.payload'),\n },\n },\n });\n\n const bufferedEvents = new EntitySchema<BufferedEventEntity>({\n name: 'BufferedEventEntity',\n target: BufferedEventEntity,\n tableName: 'durable_buffered_events',\n // No `indices` here (unlike MikroORM's EntitySchema, which does declare one): this store manages\n // its DDL — including the `(name, publishedAt)` index — via raw SQL in `schema.ts`'s\n // `ensureTypeOrmDurableSchema`, the same as every other durable_* table's indexes.\n columns: {\n // Caller-minted uuid (engine.publishEvent), not autoincrement — removeBufferedEvent targets it\n // directly and needs to be handed the SAME id it was buffered with.\n id: { type: 'text', primary: true, name: col('id') },\n name: { type: 'text', name: col('name') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.eventPayload'),\n },\n publishedAt: { type: Date, name: col('publishedAt') },\n },\n });\n\n return [\n workflowRuns,\n stepCheckpoints,\n runAttributes,\n signalWaiters,\n bufferedSignals,\n bufferedEvents,\n ];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import type { DataSource } from 'typeorm';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\n\n/** The JSON-blob columns per table (keyed by table name) that hold free-form payloads and so must\n * be the unbounded text type — `longtext` on MySQL, `text` elsewhere. Keyed by entity PROPERTY name;\n * the physical column is resolved from the DataSource metadata so it tracks the configured naming. */\nexport const JSON_BLOB_COLUMNS: Record<string, string[]> = {\n durable_workflow_runs: ['input', 'output', 'error', 'tags', 'searchAttributes'],\n durable_step_checkpoints: ['input', 'output', 'error', 'events'],\n // NOTE: durable_buffered_signals/durable_buffered_events intentionally omitted here, same as the\n // existing buffered-signals table — their `payload` column is created with the widened `txt` type\n // (see `${bufCol('payload')} ${txt}` / the new table's `${bevCol('payload')} ${txt}` below) directly\n // at CREATE TABLE time on a fresh install, so there's no pre-existing narrower column to widen.\n};\n\n/** Each durable table → its backing entity class, so a column resolver can look up the physical\n * column for any (table, property) from the DataSource metadata (which reflects the configured\n * `DurableColumnNaming`). Keeps the raw DDL/SQL in lock-step with the entity column mapping. */\nconst TABLE_TO_ENTITY = {\n durable_workflow_runs: WorkflowRunEntity,\n durable_step_checkpoints: StepCheckpointEntity,\n durable_run_attributes: RunAttributeEntity,\n durable_signal_waiters: SignalWaiterEntity,\n durable_buffered_signals: BufferedSignalEntity,\n durable_buffered_events: BufferedEventEntity,\n} as const;\n\n/**\n * Tables this store creates and manages at boot (autoSchema, see {@link ensureTypeOrmDurableSchema}).\n * Feed to your ORM's migration differ exclude/skipTables so it never tries to drop them.\n *\n * ```ts\n * // MikroORM-flavoured example (TypeORM has no built-in equivalent option); adapt to whichever\n * // migration tool generates your diffs — e.g. exclude these names from a typeorm-generated\n * // migration, or filter them out of `SELECT table_name FROM information_schema.tables` before\n * // diffing against your own entities.\n * const skipTables = new Set(durableManagedTables());\n * ```\n *\n * Derived from the same {@link TABLE_TO_ENTITY} map the raw-SQL heal keys its column resolution off\n * — one list, so a consumer's denylist can never drift from what this store actually owns.\n */\nexport function durableManagedTables(): string[] {\n return Object.keys(TABLE_TO_ENTITY);\n}\n\n/**\n * Resolve `(table, property) => physical column name` from the DataSource's entity metadata. The\n * column names are NOT hardcoded here: they come from whatever the registered `durableEntities`\n * mapped (canonical `snake_case` by default, `'preserve'` for the legacy camelCase, or a custom\n * mapping), so the raw `CREATE TABLE`/index/EXISTS SQL always matches the entities and can never\n * silently diverge.\n */\nexport function durableColumnResolver(\n dataSource: DataSource,\n): (table: string, property: string) => string {\n return (table, property) => {\n const entity = TABLE_TO_ENTITY[table as keyof typeof TABLE_TO_ENTITY];\n if (!entity) throw new Error(`durable store: unknown table \"${table}\"`);\n const meta = dataSource.getMetadata(entity);\n const column = meta.findColumnWithPropertyName(property);\n if (!column) {\n throw new Error(`durable store: no column for ${table}.${property} in entity metadata`);\n }\n return column.databaseName;\n };\n}\n\n/** The SQL type for free-form JSON-blob columns. MySQL `text` caps at 64KB and silently truncates,\n * so use `longtext` (4GB) there; Postgres/SQLite `text` is already unbounded. */\nexport function jsonBlobColumnType(isMysql: boolean): 'longtext' | 'text' {\n return isMysql ? 'longtext' : 'text';\n}\n\n/** Idempotent `MODIFY COLUMN ... longtext` statements that widen pre-existing MySQL `text` JSON-blob\n * columns. Returns `[]` on non-MySQL dialects (their `text` is already unbounded — nothing to do).\n * `quote` quotes an identifier per the active driver; `col` resolves a property to its physical\n * column name so the widen tracks the configured naming. */\nexport function buildWidenStatements(\n isMysql: boolean,\n quote: (id: string) => string,\n col: (table: string, property: string) => string,\n): string[] {\n if (!isMysql) return [];\n const out: string[] = [];\n for (const [table, props] of Object.entries(JSON_BLOB_COLUMNS)) {\n for (const prop of props) {\n out.push(`ALTER TABLE ${quote(table)} MODIFY COLUMN ${quote(col(table, prop))} longtext`);\n }\n }\n return out;\n}\n\n/**\n * Idempotently create the durable tables. Safe to run on every boot (auto-schema) and the\n * exact function to call from your own TypeORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables implements MigrationInterface {\n * async up(q: QueryRunner) { await ensureTypeOrmDurableSchema(q.connection); }\n * }\n * ```\n *\n * Dialect-aware (MySQL / MariaDB / Postgres / SQLite): identifiers are quoted per driver, keyed\n * string columns are `varchar` (MySQL can't key a `text` column), and the index is best-effort.\n * Only ever adds the three durable tables — it never touches your other tables.\n */\nexport async function ensureTypeOrmDurableSchema(dataSource: DataSource): Promise<void> {\n const q = (id: string) => dataSource.driver.escape(id);\n // Physical column names come from the entity metadata (canonical snake_case by default), so this\n // raw DDL always matches the registered `durableEntities` instead of hardcoding camelCase.\n const resolve = durableColumnResolver(dataSource);\n const type = String(dataSource.options.type);\n const isPg = type === 'postgres' || type === 'aurora-postgres';\n const isMysql = type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql';\n const isSqlite = type === 'sqlite' || type === 'better-sqlite3' || type === 'expo';\n\n // Keyed/short strings must be varchar on MySQL (a `text` PK/index needs a key length); `text`\n // for the free-form JSON payloads. Dates use the dialect's native timestamp type.\n const str = 'varchar(191)';\n // Free-form JSON payloads (`input`/`output`/`error`/`events`/`tags`/`searchAttributes`). MySQL's\n // `text` caps at 64KB and *silently truncates* — a large fan-out step's `events` then fails to\n // parse on read (\"Unterminated string in JSON\"). Use `longtext` (4GB) on MySQL; Postgres `text`\n // is already unbounded and SQLite `text` has no length limit.\n const txt = jsonBlobColumnType(isMysql);\n const int = isMysql ? 'int' : 'integer';\n const ts = isPg ? 'timestamptz' : 'datetime';\n\n const runs = q('durable_workflow_runs');\n const checkpoints = q('durable_step_checkpoints');\n const runAttributes = q('durable_run_attributes');\n const waiters = q('durable_signal_waiters');\n const buffered = q('durable_buffered_signals');\n const bufferedEvents = q('durable_buffered_events');\n // Per-table quoted-column helpers: resolve an entity property to its physical column then quote it.\n const runsCol = (property: string) => q(resolve('durable_workflow_runs', property));\n const cpCol = (property: string) => q(resolve('durable_step_checkpoints', property));\n const attrCol = (property: string) => q(resolve('durable_run_attributes', property));\n const waiterCol = (property: string) => q(resolve('durable_signal_waiters', property));\n const bufCol = (property: string) => q(resolve('durable_buffered_signals', property));\n const bevCol = (property: string) => q(resolve('durable_buffered_events', property));\n // Numeric side-table column for attribute range scans. `double precision` on Postgres, `double` on\n // MySQL, `real` on SQLite (all hold JS numbers without precision loss for the typical attribute).\n const num = isPg ? 'double precision' : isMysql ? 'double' : 'real';\n\n // Auto-increment PK syntax differs per dialect: SQLite wants `INTEGER PRIMARY KEY AUTOINCREMENT`,\n // MySQL `BIGINT AUTO_INCREMENT PRIMARY KEY`, Postgres `BIGSERIAL PRIMARY KEY`.\n const bufferedId = isSqlite\n ? `${bufCol('id')} integer PRIMARY KEY AUTOINCREMENT`\n : isMysql\n ? `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`\n : isPg\n ? `${bufCol('id')} bigserial PRIMARY KEY`\n : `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`;\n\n const tables = [\n `CREATE TABLE IF NOT EXISTS ${runs} (\n ${runsCol('id')} ${str} PRIMARY KEY,\n ${runsCol('workflow')} ${str} NOT NULL,\n ${runsCol('workflowVersion')} ${str} NOT NULL,\n ${runsCol('status')} ${str} NOT NULL,\n ${runsCol('input')} ${txt}, ${runsCol('output')} ${txt}, ${runsCol('error')} ${txt},\n ${runsCol('wakeAt')} ${ts}, ${runsCol('lockedBy')} ${str}, ${runsCol('lockedUntil')} ${ts},\n ${runsCol('awaitingDecisionTaskId')} ${str},\n ${runsCol('recoveryAttempts')} ${int}, ${runsCol('tags')} ${txt}, ${runsCol('searchAttributes')} ${txt},\n ${runsCol('priority')} ${int},\n ${runsCol('createdAt')} ${ts} NOT NULL, ${runsCol('updatedAt')} ${ts} NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS ${checkpoints} (\n ${cpCol('runId')} ${str} NOT NULL, ${cpCol('seq')} ${int} NOT NULL,\n ${cpCol('name')} ${str} NOT NULL, ${cpCol('kind')} ${str} NOT NULL, ${cpCol('stepId')} ${str} NOT NULL,\n ${cpCol('status')} ${str} NOT NULL, ${cpCol('input')} ${txt}, ${cpCol('output')} ${txt}, ${cpCol('error')} ${txt}, ${cpCol('events')} ${txt},\n ${cpCol('attempts')} ${int} NOT NULL, ${cpCol('workerGroup')} ${str}, ${cpCol('parallelGroup')} ${str},\n ${cpCol('wakeAt')} ${ts}, ${cpCol('enqueuedAt')} ${ts},\n ${cpCol('startedAt')} ${ts} NOT NULL, ${cpCol('finishedAt')} ${ts} NOT NULL,\n PRIMARY KEY (${cpCol('runId')}, ${cpCol('seq')})\n )`,\n // Normalized search-attribute side-table: one row per (run, key) so attribute predicates push\n // DOWN into SQL via an EXISTS join (indexed below). `key` is keyed → must be varchar on MySQL.\n `CREATE TABLE IF NOT EXISTS ${runAttributes} (\n ${attrCol('runId')} ${str} NOT NULL, ${attrCol('key')} ${str} NOT NULL,\n ${attrCol('strValue')} ${str}, ${attrCol('numValue')} ${num},\n PRIMARY KEY (${attrCol('runId')}, ${attrCol('key')})\n )`,\n `CREATE TABLE IF NOT EXISTS ${waiters} (\n ${waiterCol('token')} ${str} PRIMARY KEY, ${waiterCol('runId')} ${str} NOT NULL, ${waiterCol('seq')} ${int} NOT NULL,\n ${waiterCol('parallelGroup')} ${str}\n )`,\n `CREATE TABLE IF NOT EXISTS ${buffered} (\n ${bufferedId}, ${bufCol('token')} ${str} NOT NULL, ${bufCol('payload')} ${txt}\n )`,\n // Caller-minted uuid PK (engine.publishEvent), not auto-increment — unlike durable_buffered_signals\n // above, removeBufferedEvent needs to target the EXACT id it was buffered with.\n `CREATE TABLE IF NOT EXISTS ${bufferedEvents} (\n ${bevCol('id')} ${str} PRIMARY KEY, ${bevCol('name')} ${str} NOT NULL,\n ${bevCol('payload')} ${txt}, ${bevCol('publishedAt')} ${ts} NOT NULL\n )`,\n ];\n\n // Additive nullable columns gained across versions (e.g. `input`, `events`, `enqueuedAt`). On a\n // table that predates them, `CREATE TABLE IF NOT EXISTS` above is a no-op, so back-fill the ones\n // that are actually missing — the auto-schema self-heals instead of needing a manual migration.\n // We check the live columns first (rather than ALTER-and-ignore) so a *real* ALTER failure\n // surfaces instead of being swallowed as a presumed \"column already exists\".\n // Keyed by entity PROPERTY name; the physical column is resolved per-table so the ADD COLUMN\n // tracks the configured naming.\n const additive: Record<string, Array<[string, string]>> = {\n durable_workflow_runs: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['awaitingDecisionTaskId', str],\n ['recoveryAttempts', int],\n ['tags', txt],\n ['searchAttributes', txt],\n ['priority', int],\n ],\n durable_step_checkpoints: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['events', txt],\n ['workerGroup', str],\n ['parallelGroup', str],\n ['enqueuedAt', ts],\n ],\n // Back-fill the child-await fan group onto signal-waiter tables that predate it (a remote\n // `gather_children` fan-out threads its group here so the resolved `signal:child:` checkpoint carries it).\n durable_signal_waiters: [['parallelGroup', str]],\n };\n\n const runner = dataSource.createQueryRunner();\n try {\n for (const sql of tables) await runner.query(sql);\n for (const [table, props] of Object.entries(additive)) {\n const have = await existingColumns(runner, table, { isSqlite, isMysql });\n for (const [property, colType] of props) {\n const column = resolve(table, property);\n if (!have.has(column)) {\n await runner.query(`ALTER TABLE ${q(table)} ADD COLUMN ${q(column)} ${colType}`);\n }\n }\n }\n // Widen pre-existing MySQL `text` JSON-blob columns to `longtext`. A table created before this\n // fix has these as `text` (64KB) and silently truncates large `events`/`output` payloads. MODIFY\n // to `longtext` is idempotent (no-op if already longtext), so it's safe to run on every boot.\n // MySQL-only: Postgres/SQLite `text` is already unbounded, so there's nothing to widen.\n for (const sql of buildWidenStatements(isMysql, q, resolve)) {\n try {\n await runner.query(sql);\n } catch {\n /* column already longtext, or table/column not present on this deploy */\n }\n }\n // MySQL has no `CREATE INDEX IF NOT EXISTS`; create each best-effort and ignore \"already exists\".\n // - status/wakeAt: the timer poller (listDueTimers) and recovery (listIncompleteRuns) scans.\n // - workflow/status: the dashboard's listRuns filters. (Checkpoints are keyed by the PK prefix\n // `(runId, seq)`, so listCheckpoints/getEvent already hit an index — no extra one needed.)\n const indexes: Array<[string, string]> = [\n ['durable_runs_status_idx', `${runs} (${runsCol('status')}, ${runsCol('wakeAt')})`],\n [\n 'durable_runs_workflow_status_idx',\n `${runs} (${runsCol('workflow')}, ${runsCol('status')})`,\n ],\n // buffered signals are taken FIFO per token (smallest id) — index the token for the scan.\n ['durable_buffered_signals_token_idx', `${buffered} (${bufCol('token')})`],\n // buffered events are scanned oldest-first per name — one composite index serves both.\n [\n 'durable_buffered_events_name_published_at_idx',\n `${bufferedEvents} (${bevCol('name')}, ${bevCol('publishedAt')})`,\n ],\n // Search-attribute pushdown: equality + range predicates probe by (key, value). Two composite\n // indexes — one per typed column — so a numeric range or a string equality is an index scan.\n [\n 'durable_run_attributes_num_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('numValue')})`,\n ],\n [\n 'durable_run_attributes_str_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('strValue')})`,\n ],\n ];\n for (const [name, target] of indexes) {\n try {\n await runner.query(`CREATE INDEX ${q(name)} ON ${target}`);\n } catch {\n /* index already exists */\n }\n }\n } finally {\n await runner.release();\n }\n}\n\n/** The columns that currently exist on `table`, so the schema can add only the missing ones\n * (instead of ALTER-and-swallow, which would also hide a genuine ALTER failure). */\nasync function existingColumns(\n runner: { query: (sql: string, params?: unknown[]) => Promise<unknown> },\n table: string,\n d: { isSqlite: boolean; isMysql: boolean },\n): Promise<Set<string>> {\n if (d.isSqlite) {\n const rows = (await runner.query(`PRAGMA table_info(\"${table}\")`)) as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n }\n // Postgres + MySQL both expose information_schema; scope to the connection's own schema/db.\n // Bind-param placeholder differs: Postgres uses `$1`, MySQL uses `?` (TypeORM only rewrites `?`\n // inside the query builder, not for these raw `runner.query` calls), so pick per dialect.\n const scope = d.isMysql ? 'TABLE_SCHEMA = DATABASE()' : 'table_schema = current_schema()';\n const placeholder = d.isMysql ? '?' : '$1';\n const rows = (await runner.query(\n `SELECT column_name AS name FROM information_schema.columns WHERE table_name = ${placeholder} AND ${scope}`,\n [table],\n )) as Array<{ name?: string; COLUMN_NAME?: string }>;\n return new Set(rows.map((r) => (r.name ?? r.COLUMN_NAME) as string));\n}\n","import {\n type AttributeFilter,\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 sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n Brackets,\n type DataSource,\n type EntityManager,\n In,\n IsNull,\n LessThanOrEqual,\n Like,\n type SelectQueryBuilder,\n} from 'typeorm';\nimport type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { durableColumnResolver, ensureTypeOrmDurableSchema } from './schema';\n\n/**\n * TypeORM-backed `StateStore`. Works on any TypeORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps use native datetime columns and `wakeAt` is stored as a datetime too.\n */\nexport class TypeOrmStateStore implements StateStore {\n constructor(private readonly dataSource: DataSource) {}\n\n async ensureSchema(): Promise<void> {\n await ensureTypeOrmDurableSchema(this.dataSource);\n }\n\n private runs() {\n return this.dataSource.getRepository(WorkflowRunEntity);\n }\n private checkpoints() {\n return this.dataSource.getRepository(StepCheckpointEntity);\n }\n private waiters() {\n return this.dataSource.getRepository(SignalWaiterEntity);\n }\n private buffered() {\n return this.dataSource.getRepository(BufferedSignalEntity);\n }\n private bufferedEvents() {\n return this.dataSource.getRepository(BufferedEventEntity);\n }\n private attributes() {\n return this.dataSource.getRepository(RunAttributeEntity);\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 em?: EntityManager,\n ): Promise<void> {\n const repo = em ? em.getRepository(RunAttributeEntity) : this.attributes();\n await repo.delete({ runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await repo.insert(rows);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n await this.runs().save(toRunEntity(run));\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n // Single UPDATE (no pre-SELECT + save round-trips): map only the patched fields. The query\n // builder still applies the entities' JSON column transformers to the `set` values (as in\n // listRuns/tryLockRun). Preserve the not-found throw any caller may rely on.\n const update: Record<string, unknown> = {};\n if ('workflow' in patch) update.workflow = patch.workflow;\n if ('workflowVersion' in patch) update.workflowVersion = patch.workflowVersion;\n if ('status' in patch) update.status = patch.status;\n if ('input' in patch) update.input = patch.input ?? null;\n if ('output' in patch) update.output = patch.output ?? null;\n if ('error' in patch) update.error = patch.error ?? null;\n if ('wakeAt' in patch) update.wakeAt = patch.wakeAt == null ? null : new Date(patch.wakeAt);\n if ('lockedBy' in patch) update.lockedBy = patch.lockedBy ?? null;\n if ('lockedUntil' in patch)\n update.lockedUntil = patch.lockedUntil == null ? null : new Date(patch.lockedUntil);\n if ('awaitingDecisionTaskId' in patch)\n update.awaitingDecisionTaskId = patch.awaitingDecisionTaskId ?? null;\n if ('recoveryAttempts' in patch) update.recoveryAttempts = patch.recoveryAttempts;\n if ('tags' in patch) update.tags = patch.tags ?? null;\n if ('searchAttributes' in patch) update.searchAttributes = patch.searchAttributes ?? null;\n if ('priority' in patch) update.priority = patch.priority ?? null;\n if ('createdAt' in patch) update.createdAt = patch.createdAt;\n if ('updatedAt' in patch) update.updatedAt = patch.updatedAt;\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set(update)\n .where({ id: runId })\n .execute();\n if (!result.affected) throw new Error(`run ${runId} not found`);\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 async getRun(runId: string): Promise<WorkflowRun | null> {\n const e = await this.runs().findOneBy({ id: runId });\n return e ? fromRunEntity(e) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n // Child rows first, then the run — checkpoints, signal waiters, attribute rows.\n await this.checkpoints().delete({ runId });\n await this.waiters().delete({ runId });\n await this.attributes().delete({ runId });\n await this.runs().delete({ id: runId });\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const e = await this.checkpoints().findOneBy({ runId, seq });\n return e ? fromCheckpointEntity(e) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n // upsert = INSERT ... ON CONFLICT DO UPDATE on the (runId, seq) key — drops the pre-SELECT that\n // `.save()` does on each write. JSON column transformers still apply for upsert.\n const entity = toCheckpointEntity(checkpoint) as QueryDeepPartialEntity<StepCheckpointEntity>;\n await this.checkpoints().upsert(entity, ['runId', 'seq']);\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.dataSource.transaction(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.getRepository(StepCheckpointEntity).save(toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({ status: In(['running', 'cancelling']) });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().find({\n where: { status: 'pending' },\n order: { createdAt: 'ASC' }, // FIFO dispatch\n take: limit,\n });\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({\n status: 'suspended',\n wakeAt: LessThanOrEqual(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 result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedBy: owner, lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId })\n .andWhere(\n new Brackets((qb) =>\n qb\n .where({ lockedUntil: IsNull() })\n .orWhere({ lockedUntil: LessThanOrEqual(new Date(nowMs)) }),\n ),\n )\n .execute();\n return result.affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n await this.runs().update({ id: runId }, { lockedBy: null, lockedUntil: () => 'NULL' });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId, lockedBy: owner })\n .execute();\n return result.affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n // `tags` carries a custom JSON transformer, and TypeORM applies a column transformer's `to()` to\n // FindOperator values too — so a `Like('%\"etl\"%')` in a plain `where` would be JSON-stringified\n // and corrupt the LIKE pattern. Use the query builder with a raw parameter to bypass that.\n const qb = this.runs().createQueryBuilder('r');\n if (query.workflow) qb.andWhere('r.workflow = :workflow', { workflow: query.workflow });\n if (query.status) qb.andWhere('r.status = :status', { status: query.status });\n if (query.statuses)\n qb.andWhere(\n query.statuses.length ? 'r.status IN (:...statuses)' : '1 = 0',\n query.statuses.length ? { statuses: query.statuses } : {},\n );\n // `tags` is a JSON-text column; match the quoted token so `etl` doesn't match `etl-foo`. NOTE: a\n // leading-wildcard LIKE on JSON text is NOT index-friendly (no B-tree index can serve `%\"x\"%`), so\n // tag-filtered scans (e.g. singleton admission) are sequential over `durable_runs_workflow_status_idx`'s\n // result set. The `statuses`/`workflow` predicates above bound that scan; if tag scans ever dominate,\n // promote tags to a normalized join table or a JSON/GIN index — a schema change, intentionally not done here.\n if (query.tag) qb.andWhere('r.tags LIKE :tagPattern', { tagPattern: `%\"${query.tag}\"%` });\n // Typed/range attribute predicates push DOWN into SQL: each filter becomes an EXISTS against the\n // normalized `durable_run_attributes` side-table (indexed on (key, numValue)/(key, strValue)), so\n // the DB does the filtering AND the LIMIT/OFFSET — no full scan + in-process filter. ANDed: a run\n // must satisfy every filter, so one EXISTS per filter.\n if (query.attributes?.length) {\n query.attributes.forEach((f, i) => this.applyAttributeExists(qb, f, i));\n }\n qb.orderBy('r.createdAt', 'DESC'); // newest first — recent runs on top in the dashboard\n if (query.limit != null) qb.take(query.limit);\n if (query.offset != null) qb.skip(query.offset);\n const rows = await qb.getMany();\n return rows.map(fromRunEntity);\n }\n\n /** Add one attribute predicate to `qb` as an EXISTS subquery on the side-table. `i` namespaces the\n * bind params so multiple ANDed filters don't collide. */\n private applyAttributeExists(\n qb: SelectQueryBuilder<WorkflowRunEntity>,\n f: AttributeFilter,\n i: number,\n ): void {\n const valueProperty = attributeColumnFor(f); // 'numValue' | 'strValue' (entity property)\n const cmp = sqlComparator(f.op);\n const keyParam = `attrKey${i}`;\n const valParam = `attrVal${i}`;\n // Resolve the PHYSICAL column names from the entity metadata so this raw subquery tracks the\n // configured naming (canonical snake_case by default) instead of hardcoding camelCase — which\n // would break the EXISTS pushdown the moment the table is snake_case.\n const resolve = durableColumnResolver(this.dataSource);\n const q = this.idQuote();\n const wrap = (name: string) => `${q}${name}${q}`;\n const runIdCol = wrap(resolve('durable_run_attributes', 'runId'));\n const keyCol = wrap(resolve('durable_run_attributes', 'key'));\n const valueCol = wrap(resolve('durable_run_attributes', valueProperty));\n const runPkCol = wrap(resolve('durable_workflow_runs', 'id'));\n const table = wrap('durable_run_attributes');\n // `<>` (ne) must also exclude rows where the attribute is absent: the missing-key-never-matches\n // contract (see core matchesAttributes). EXISTS already requires the key row to be present, and a\n // row only exists when the value is non-null, so EXISTS(... <> ...) gives exactly ne-with-present.\n const a = `a${i}`;\n const sql = `EXISTS (SELECT 1 FROM ${table} ${a} WHERE ${a}.${runIdCol} = r.${runPkCol} AND ${a}.${keyCol} = :${keyParam} AND ${a}.${valueCol} ${cmp} :${valParam})`;\n qb.andWhere(sql, {\n [keyParam]: f.key,\n [valParam]: attributeOperand(f),\n });\n }\n\n /** The identifier quote char for the active driver (MySQL backtick, others double-quote). */\n private idQuote(): string {\n const type = String(this.dataSource.options.type);\n return type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql' ? '`' : '\"';\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const rows = await this.checkpoints().find({ where: { runId }, order: { seq: 'ASC' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const e = await this.checkpoints().findOne({ where: { runId, name }, order: { seq: 'DESC' } });\n return e ? fromCheckpointEntity(e) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const rows = await this.checkpoints().find({\n where: prefixes.map((p) => ({ runId, name: Like(`${p}%`) })),\n order: { seq: 'ASC' },\n });\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n await this.waiters().save({\n token: waiter.token,\n runId: waiter.runId,\n seq: waiter.seq,\n parallelGroup: waiter.parallelGroup ?? null,\n });\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const rows = await this.waiters().find({ where: { 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 takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const e = await this.waiters().findOneBy({ token });\n if (!e) return null;\n const waiter: SignalWaiter = {\n token: e.token,\n runId: e.runId,\n seq: e.seq,\n parallelGroup: e.parallelGroup ?? undefined,\n };\n await this.waiters().delete({ token });\n return waiter;\n }\n\n async removeSignalWaiter(waiter: SignalWaiter): Promise<void> {\n // Exact-match delete (token AND runId AND seq) — a plain `delete({ token })` would remove\n // whatever row currently owns the token, even if a different run has since claimed it.\n await this.waiters().delete({ token: waiter.token, runId: waiter.runId, seq: waiter.seq });\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n await this.buffered().save({ token, payload: payload ?? null });\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const e = await this.buffered().findOne({ where: { token }, order: { id: 'ASC' } });\n if (!e) return null;\n await this.buffered().delete({ id: e.id });\n return { payload: e.payload ?? undefined };\n }\n\n async bufferEvent(input: {\n name: string;\n payload: unknown;\n id: string;\n publishedAt: number;\n }): Promise<void> {\n await this.bufferedEvents().save({\n id: input.id,\n name: input.name,\n payload: input.payload ?? null,\n publishedAt: new Date(input.publishedAt),\n });\n }\n\n async listBufferedEvents(\n name: string,\n limit: number,\n ): Promise<Array<{ id: string; payload: unknown; publishedAt: number }>> {\n const rows = await this.bufferedEvents().find({\n where: { name },\n order: { publishedAt: 'ASC' }, // oldest first\n take: limit,\n });\n return rows.map((e) => ({\n id: e.id,\n payload: e.payload ?? undefined,\n publishedAt: e.publishedAt.getTime(),\n }));\n }\n\n async removeBufferedEvent(id: string): Promise<boolean> {\n const result = await this.bufferedEvents().delete({ id });\n return result.affected === 1;\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n return {\n id: run.id,\n workflow: run.workflow,\n workflowVersion: run.workflowVersion,\n status: run.status,\n input: run.input ?? null,\n output: run.output ?? null,\n error: run.error ?? null,\n ...(run.wakeAt == null ? {} : { wakeAt: new Date(run.wakeAt) }),\n lockedBy: run.lockedBy ?? null,\n ...(run.lockedUntil == null ? {} : { lockedUntil: new Date(run.lockedUntil) }),\n awaitingDecisionTaskId: run.awaitingDecisionTaskId ?? null,\n ...(run.recoveryAttempts === undefined ? {} : { recoveryAttempts: run.recoveryAttempts }),\n tags: run.tags ?? null,\n searchAttributes: run.searchAttributes ?? null,\n priority: run.priority ?? null,\n createdAt: run.createdAt,\n updatedAt: run.updatedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil == null ? undefined : e.lockedUntil.getTime(),\n awaitingDecisionTaskId: e.awaitingDecisionTaskId ?? undefined,\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 return {\n runId: cp.runId,\n seq: cp.seq,\n name: cp.name,\n kind: cp.kind,\n stepId: cp.stepId,\n status: cp.status,\n input: cp.input ?? null,\n output: cp.output ?? null,\n error: cp.error ?? null,\n events: cp.events ?? null,\n attempts: cp.attempts,\n workerGroup: cp.workerGroup ?? null,\n parallelGroup: cp.parallelGroup ?? null,\n ...(cp.wakeAt == null ? {} : { wakeAt: new Date(cp.wakeAt) }),\n enqueuedAt: cp.enqueuedAt,\n startedAt: cp.startedAt,\n finishedAt: cp.finishedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n // Older rows predate enqueuedAt; treat the worker start as enqueue time (queue-wait reads zero).\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n"],"mappings":"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","names":["parseJsonColumn","raw","field","undefined","JSON","parse","err","console","warn","message","jsonColumnTransformer","to","value","stringify","from","snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","awaitingDecisionTaskId","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","BufferedEventEntity","publishedAt","durableEntities","options","col","workflowRuns","EntitySchema","target","tableName","columns","type","primary","nullable","transformer","Date","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","Number","generated","bufferedEvents","ENTITIES","JSON_BLOB_COLUMNS","durable_workflow_runs","durable_step_checkpoints","TABLE_TO_ENTITY","WorkflowRunEntity","StepCheckpointEntity","durable_run_attributes","RunAttributeEntity","durable_signal_waiters","SignalWaiterEntity","durable_buffered_signals","BufferedSignalEntity","durable_buffered_events","BufferedEventEntity","durableManagedTables","Object","keys","durableColumnResolver","dataSource","table","property","entity","Error","meta","getMetadata","column","findColumnWithPropertyName","databaseName","jsonBlobColumnType","isMysql","buildWidenStatements","quote","col","out","props","entries","prop","push","ensureTypeOrmDurableSchema","q","id","driver","escape","resolve","type","String","options","isPg","isSqlite","str","txt","int","ts","runs","checkpoints","runAttributes","waiters","buffered","bufferedEvents","runsCol","cpCol","attrCol","waiterCol","bufCol","bevCol","num","bufferedId","tables","additive","runner","createQueryRunner","sql","query","have","existingColumns","colType","has","indexes","name","target","release","d","rows","Set","map","r","scope","placeholder","COLUMN_NAME","import_typeorm","TypeOrmStateStore","dataSource","ensureSchema","ensureTypeOrmDurableSchema","runs","getRepository","WorkflowRunEntity","checkpoints","StepCheckpointEntity","waiters","SignalWaiterEntity","buffered","BufferedSignalEntity","bufferedEvents","BufferedEventEntity","attributes","RunAttributeEntity","reindexAttributes","runId","em","repo","delete","rows","normalizeAttributeRows","length","insert","createRun","run","save","toRunEntity","id","searchAttributes","updateRun","patch","update","workflow","workflowVersion","status","input","output","error","wakeAt","Date","lockedBy","lockedUntil","awaitingDecisionTaskId","recoveryAttempts","tags","priority","createdAt","updatedAt","result","createQueryBuilder","set","where","execute","affected","Error","getRun","e","findOneBy","fromRunEntity","deleteRun","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","entity","toCheckpointEntity","upsert","transaction","work","raw","cp","listIncompleteRuns","findBy","In","map","listPendingRuns","limit","find","order","take","listDueTimers","nowMs","LessThanOrEqual","tryLockRun","owner","leaseUntilMs","andWhere","Brackets","qb","IsNull","orWhere","releaseRunLock","renewRunLock","listRuns","query","statuses","tag","tagPattern","forEach","f","i","applyAttributeExists","orderBy","offset","skip","getMany","valueProperty","attributeColumnFor","cmp","sqlComparator","op","keyParam","valParam","resolve","durableColumnResolver","q","idQuote","wrap","name","runIdCol","keyCol","valueCol","runPkCol","table","a","sql","key","attributeOperand","type","String","options","listCheckpoints","getLatestCheckpointByName","findOne","undefined","listCheckpointsByNamePrefix","prefixes","p","Like","putSignalWaiter","waiter","token","parallelGroup","listSignalWaiters","prefix","takeSignalWaiter","removeSignalWaiter","bufferSignal","payload","takeBufferedSignal","bufferEvent","publishedAt","listBufferedEvents","getTime","removeBufferedEvent","kind","stepId","events","attempts","workerGroup","enqueuedAt","startedAt","finishedAt"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/entities.ts","../src/schema.ts","../src/typeorm-state-store.ts"],"sourcesContent":["export * from './entities';\nexport * from './schema';\nexport * from './typeorm-state-store';\n","import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema, type ValueTransformer } from 'typeorm';\n\n/**\n * Safely deserialize a JSON-text column back to an object on READ.\n *\n * A column truncated by the old MySQL `text` 64KB limit (or otherwise corrupt) makes `JSON.parse`\n * throw \"Unterminated string in JSON\" — which, without this guard, would 500 the *entire* run-detail\n * read just because one blob is damaged. Instead, log once and degrade the bad field to `undefined`\n * so the rest of the run still renders. Valid JSON is unchanged.\n */\nexport function parseJsonColumn<T = unknown>(raw: unknown, field: string): T | undefined {\n if (raw == null) return undefined;\n // Some drivers (e.g. a native `json` column) already hand back a parsed object.\n if (typeof raw !== 'string') return raw as T;\n try {\n return JSON.parse(raw) as T;\n } catch (err) {\n console.warn(\n `durable store: failed to parse ${field} column, degrading to undefined: ${\n (err as Error).message\n }`,\n );\n return undefined;\n }\n}\n\n/**\n * TypeORM transformer for the free-form JSON-blob columns. Mirrors the built-in `simple-json`\n * serialization on WRITE (`JSON.stringify`, SQL NULL stays NULL) but uses {@link parseJsonColumn}\n * on READ so a single corrupt/truncated row degrades to `undefined` instead of throwing.\n */\nfunction jsonColumnTransformer(field: string): ValueTransformer {\n return {\n to: (value: unknown) => (value == null ? null : JSON.stringify(value)),\n from: (value: unknown) => parseJsonColumn(value, field),\n };\n}\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 MikroORM,\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\n/** Resolve a `DurableColumnNaming` to a `(property) => columnName` function. */\nexport function 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 `getRepository`/`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 | null;\n lockedUntil?: Date;\n awaitingDecisionTaskId?: string | null;\n recoveryAttempts?: number;\n tags?: string[] | null;\n searchAttributes?: Record<string, string | number | boolean> | null;\n priority?: number | null;\n origin?: string | 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 | null;\n parallelGroup?: string | null;\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/** A published event that matched no live waiter (see `engine.publishEvent`'s buffering). Keyed by a\n * caller-minted `id` (a uuid, not autoincrement — `removeBufferedEvent` targets it directly), not by\n * `runId`: events are name-based pub/sub, not tied to any one run. */\nexport class BufferedEventEntity {\n id!: string;\n name!: string;\n payload?: unknown;\n publishedAt!: Date;\n}\n\n/**\n * Build the durable TypeORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your `DataSource`/`TypeOrmModule.forFeature`. The JSON-blob\n * columns keep the `jsonColumnTransformer` (corruption-tolerant read), the primary keys / auto-\n * increment / nullability are preserved, and the `id` of `durable_buffered_signals` stays a driver-\n * native auto-increment column.\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 name: 'WorkflowRunEntity',\n target: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n columns: {\n id: { type: 'text', primary: true, name: col('id') },\n workflow: { type: 'text', name: col('workflow') },\n workflowVersion: { type: 'text', name: col('workflowVersion') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('runs.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('runs.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('runs.error'),\n },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n lockedBy: { type: 'text', nullable: true, name: col('lockedBy') },\n lockedUntil: { type: Date, nullable: true, name: col('lockedUntil') },\n // REMOTE turn the engine suspended on awaiting a decision; matched by completeRemoteDecision so\n // only the currently-awaited turn's decision is applied. Nullable: cleared when a decision lands.\n awaitingDecisionTaskId: {\n type: 'text',\n nullable: true,\n name: col('awaitingDecisionTaskId'),\n },\n recoveryAttempts: { type: 'integer', nullable: true, name: col('recoveryAttempts') },\n tags: {\n type: 'text',\n nullable: true,\n name: col('tags'),\n transformer: jsonColumnTransformer('runs.tags'),\n },\n searchAttributes: {\n type: 'text',\n nullable: true,\n name: col('searchAttributes'),\n transformer: jsonColumnTransformer('runs.searchAttributes'),\n },\n priority: { type: 'integer', nullable: true, name: col('priority') },\n // Which library/module registered the workflow this run belongs to (e.g.\n // `@dudousxd/nestjs-catalog-pipeline`), derived at registration time — never caller input.\n // Nullable with NO default: an origin cannot be reconstructed for a row written before this\n // column existed, so it stays NULL and reads back as `undefined` = \"unknown\". Defaulting it to\n // a real-looking string would be indistinguishable from a genuine registration in the\n // dashboard's facet. `ensureTypeOrmDurableSchema` back-fills the COLUMN onto pre-existing\n // tables (see the `additive` map in ./schema), never its value.\n origin: { type: 'text', nullable: true, name: col('origin') },\n createdAt: { type: Date, name: col('createdAt') },\n updatedAt: { type: Date, name: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n name: 'StepCheckpointEntity',\n target: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n seq: { type: 'integer', primary: true, name: col('seq') },\n name: { type: 'text', name: col('name') },\n kind: { type: 'text', name: col('kind') },\n stepId: { type: 'text', name: col('stepId') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('checkpoints.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('checkpoints.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('checkpoints.error'),\n },\n events: {\n type: 'text',\n nullable: true,\n name: col('events'),\n transformer: jsonColumnTransformer('checkpoints.events'),\n },\n attempts: { type: 'integer', name: col('attempts') },\n workerGroup: { type: 'text', nullable: true, name: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group so the dashboard renders\n // them as one parallel group; the core engine sets it (incl. from a remote worker's recordStep).\n parallelGroup: { type: 'text', nullable: true, name: col('parallelGroup') },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n enqueuedAt: { type: Date, nullable: true, name: col('enqueuedAt') },\n startedAt: { type: Date, name: col('startedAt') },\n finishedAt: { type: Date, name: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n name: 'RunAttributeEntity',\n target: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n key: { type: 'text', primary: true, name: col('key') },\n strValue: { type: 'text', nullable: true, name: col('strValue') },\n // `float`/`double` portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, name: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n name: 'SignalWaiterEntity',\n target: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n columns: {\n token: { type: 'text', primary: true, name: col('token') },\n runId: { type: 'text', name: col('runId') },\n seq: { type: 'integer', name: 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: 'text', nullable: true, name: col('parallelGroup') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n name: 'BufferedSignalEntity',\n target: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n columns: {\n // Auto-increment PK for FIFO ordering. Left as TypeORM's default increment generation so it\n // maps to the driver-native auto-increment column (INTEGER on SQLite, BIGINT/SERIAL elsewhere).\n id: { type: Number, primary: true, generated: 'increment', name: col('id') },\n token: { type: 'text', name: col('token') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.payload'),\n },\n },\n });\n\n const bufferedEvents = new EntitySchema<BufferedEventEntity>({\n name: 'BufferedEventEntity',\n target: BufferedEventEntity,\n tableName: 'durable_buffered_events',\n // No `indices` here (unlike MikroORM's EntitySchema, which does declare one): this store manages\n // its DDL — including the `(name, publishedAt)` index — via raw SQL in `schema.ts`'s\n // `ensureTypeOrmDurableSchema`, the same as every other durable_* table's indexes.\n columns: {\n // Caller-minted uuid (engine.publishEvent), not autoincrement — removeBufferedEvent targets it\n // directly and needs to be handed the SAME id it was buffered with.\n id: { type: 'text', primary: true, name: col('id') },\n name: { type: 'text', name: col('name') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.eventPayload'),\n },\n publishedAt: { type: Date, name: col('publishedAt') },\n },\n });\n\n return [\n workflowRuns,\n stepCheckpoints,\n runAttributes,\n signalWaiters,\n bufferedSignals,\n bufferedEvents,\n ];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import type { DataSource } from 'typeorm';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\n\n/** The JSON-blob columns per table (keyed by table name) that hold free-form payloads and so must\n * be the unbounded text type — `longtext` on MySQL, `text` elsewhere. Keyed by entity PROPERTY name;\n * the physical column is resolved from the DataSource metadata so it tracks the configured naming. */\nexport const JSON_BLOB_COLUMNS: Record<string, string[]> = {\n durable_workflow_runs: ['input', 'output', 'error', 'tags', 'searchAttributes'],\n durable_step_checkpoints: ['input', 'output', 'error', 'events'],\n // NOTE: durable_buffered_signals/durable_buffered_events intentionally omitted here, same as the\n // existing buffered-signals table — their `payload` column is created with the widened `txt` type\n // (see `${bufCol('payload')} ${txt}` / the new table's `${bevCol('payload')} ${txt}` below) directly\n // at CREATE TABLE time on a fresh install, so there's no pre-existing narrower column to widen.\n};\n\n/** Each durable table → its backing entity class, so a column resolver can look up the physical\n * column for any (table, property) from the DataSource metadata (which reflects the configured\n * `DurableColumnNaming`). Keeps the raw DDL/SQL in lock-step with the entity column mapping. */\nconst TABLE_TO_ENTITY = {\n durable_workflow_runs: WorkflowRunEntity,\n durable_step_checkpoints: StepCheckpointEntity,\n durable_run_attributes: RunAttributeEntity,\n durable_signal_waiters: SignalWaiterEntity,\n durable_buffered_signals: BufferedSignalEntity,\n durable_buffered_events: BufferedEventEntity,\n} as const;\n\n/**\n * Tables this store creates and manages at boot (autoSchema, see {@link ensureTypeOrmDurableSchema}).\n * Feed to your ORM's migration differ exclude/skipTables so it never tries to drop them.\n *\n * ```ts\n * // MikroORM-flavoured example (TypeORM has no built-in equivalent option); adapt to whichever\n * // migration tool generates your diffs — e.g. exclude these names from a typeorm-generated\n * // migration, or filter them out of `SELECT table_name FROM information_schema.tables` before\n * // diffing against your own entities.\n * const skipTables = new Set(durableManagedTables());\n * ```\n *\n * Derived from the same {@link TABLE_TO_ENTITY} map the raw-SQL heal keys its column resolution off\n * — one list, so a consumer's denylist can never drift from what this store actually owns.\n */\nexport function durableManagedTables(): string[] {\n return Object.keys(TABLE_TO_ENTITY);\n}\n\n/**\n * Resolve `(table, property) => physical column name` from the DataSource's entity metadata. The\n * column names are NOT hardcoded here: they come from whatever the registered `durableEntities`\n * mapped (canonical `snake_case` by default, `'preserve'` for the legacy camelCase, or a custom\n * mapping), so the raw `CREATE TABLE`/index/EXISTS SQL always matches the entities and can never\n * silently diverge.\n */\nexport function durableColumnResolver(\n dataSource: DataSource,\n): (table: string, property: string) => string {\n return (table, property) => {\n const entity = TABLE_TO_ENTITY[table as keyof typeof TABLE_TO_ENTITY];\n if (!entity) throw new Error(`durable store: unknown table \"${table}\"`);\n const meta = dataSource.getMetadata(entity);\n const column = meta.findColumnWithPropertyName(property);\n if (!column) {\n throw new Error(`durable store: no column for ${table}.${property} in entity metadata`);\n }\n return column.databaseName;\n };\n}\n\n/** The SQL type for free-form JSON-blob columns. MySQL `text` caps at 64KB and silently truncates,\n * so use `longtext` (4GB) there; Postgres/SQLite `text` is already unbounded. */\nexport function jsonBlobColumnType(isMysql: boolean): 'longtext' | 'text' {\n return isMysql ? 'longtext' : 'text';\n}\n\n/** Idempotent `MODIFY COLUMN ... longtext` statements that widen pre-existing MySQL `text` JSON-blob\n * columns. Returns `[]` on non-MySQL dialects (their `text` is already unbounded — nothing to do).\n * `quote` quotes an identifier per the active driver; `col` resolves a property to its physical\n * column name so the widen tracks the configured naming. */\nexport function buildWidenStatements(\n isMysql: boolean,\n quote: (id: string) => string,\n col: (table: string, property: string) => string,\n): string[] {\n if (!isMysql) return [];\n const out: string[] = [];\n for (const [table, props] of Object.entries(JSON_BLOB_COLUMNS)) {\n for (const prop of props) {\n out.push(`ALTER TABLE ${quote(table)} MODIFY COLUMN ${quote(col(table, prop))} longtext`);\n }\n }\n return out;\n}\n\n/**\n * Idempotently create the durable tables. Safe to run on every boot (auto-schema) and the\n * exact function to call from your own TypeORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables implements MigrationInterface {\n * async up(q: QueryRunner) { await ensureTypeOrmDurableSchema(q.connection); }\n * }\n * ```\n *\n * Dialect-aware (MySQL / MariaDB / Postgres / SQLite): identifiers are quoted per driver, keyed\n * string columns are `varchar` (MySQL can't key a `text` column), and the index is best-effort.\n * Only ever adds the three durable tables — it never touches your other tables.\n */\nexport async function ensureTypeOrmDurableSchema(dataSource: DataSource): Promise<void> {\n const q = (id: string) => dataSource.driver.escape(id);\n // Physical column names come from the entity metadata (canonical snake_case by default), so this\n // raw DDL always matches the registered `durableEntities` instead of hardcoding camelCase.\n const resolve = durableColumnResolver(dataSource);\n const type = String(dataSource.options.type);\n const isPg = type === 'postgres' || type === 'aurora-postgres';\n const isMysql = type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql';\n const isSqlite = type === 'sqlite' || type === 'better-sqlite3' || type === 'expo';\n\n // Keyed/short strings must be varchar on MySQL (a `text` PK/index needs a key length); `text`\n // for the free-form JSON payloads. Dates use the dialect's native timestamp type.\n const str = 'varchar(191)';\n // Free-form JSON payloads (`input`/`output`/`error`/`events`/`tags`/`searchAttributes`). MySQL's\n // `text` caps at 64KB and *silently truncates* — a large fan-out step's `events` then fails to\n // parse on read (\"Unterminated string in JSON\"). Use `longtext` (4GB) on MySQL; Postgres `text`\n // is already unbounded and SQLite `text` has no length limit.\n const txt = jsonBlobColumnType(isMysql);\n const int = isMysql ? 'int' : 'integer';\n const ts = isPg ? 'timestamptz' : 'datetime';\n\n const runs = q('durable_workflow_runs');\n const checkpoints = q('durable_step_checkpoints');\n const runAttributes = q('durable_run_attributes');\n const waiters = q('durable_signal_waiters');\n const buffered = q('durable_buffered_signals');\n const bufferedEvents = q('durable_buffered_events');\n // Per-table quoted-column helpers: resolve an entity property to its physical column then quote it.\n const runsCol = (property: string) => q(resolve('durable_workflow_runs', property));\n const cpCol = (property: string) => q(resolve('durable_step_checkpoints', property));\n const attrCol = (property: string) => q(resolve('durable_run_attributes', property));\n const waiterCol = (property: string) => q(resolve('durable_signal_waiters', property));\n const bufCol = (property: string) => q(resolve('durable_buffered_signals', property));\n const bevCol = (property: string) => q(resolve('durable_buffered_events', property));\n // Numeric side-table column for attribute range scans. `double precision` on Postgres, `double` on\n // MySQL, `real` on SQLite (all hold JS numbers without precision loss for the typical attribute).\n const num = isPg ? 'double precision' : isMysql ? 'double' : 'real';\n\n // Auto-increment PK syntax differs per dialect: SQLite wants `INTEGER PRIMARY KEY AUTOINCREMENT`,\n // MySQL `BIGINT AUTO_INCREMENT PRIMARY KEY`, Postgres `BIGSERIAL PRIMARY KEY`.\n const bufferedId = isSqlite\n ? `${bufCol('id')} integer PRIMARY KEY AUTOINCREMENT`\n : isMysql\n ? `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`\n : isPg\n ? `${bufCol('id')} bigserial PRIMARY KEY`\n : `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`;\n\n const tables = [\n `CREATE TABLE IF NOT EXISTS ${runs} (\n ${runsCol('id')} ${str} PRIMARY KEY,\n ${runsCol('workflow')} ${str} NOT NULL,\n ${runsCol('workflowVersion')} ${str} NOT NULL,\n ${runsCol('status')} ${str} NOT NULL,\n ${runsCol('input')} ${txt}, ${runsCol('output')} ${txt}, ${runsCol('error')} ${txt},\n ${runsCol('wakeAt')} ${ts}, ${runsCol('lockedBy')} ${str}, ${runsCol('lockedUntil')} ${ts},\n ${runsCol('awaitingDecisionTaskId')} ${str},\n ${runsCol('recoveryAttempts')} ${int}, ${runsCol('tags')} ${txt}, ${runsCol('searchAttributes')} ${txt},\n ${runsCol('priority')} ${int}, ${runsCol('origin')} ${str},\n ${runsCol('createdAt')} ${ts} NOT NULL, ${runsCol('updatedAt')} ${ts} NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS ${checkpoints} (\n ${cpCol('runId')} ${str} NOT NULL, ${cpCol('seq')} ${int} NOT NULL,\n ${cpCol('name')} ${str} NOT NULL, ${cpCol('kind')} ${str} NOT NULL, ${cpCol('stepId')} ${str} NOT NULL,\n ${cpCol('status')} ${str} NOT NULL, ${cpCol('input')} ${txt}, ${cpCol('output')} ${txt}, ${cpCol('error')} ${txt}, ${cpCol('events')} ${txt},\n ${cpCol('attempts')} ${int} NOT NULL, ${cpCol('workerGroup')} ${str}, ${cpCol('parallelGroup')} ${str},\n ${cpCol('wakeAt')} ${ts}, ${cpCol('enqueuedAt')} ${ts},\n ${cpCol('startedAt')} ${ts} NOT NULL, ${cpCol('finishedAt')} ${ts} NOT NULL,\n PRIMARY KEY (${cpCol('runId')}, ${cpCol('seq')})\n )`,\n // Normalized search-attribute side-table: one row per (run, key) so attribute predicates push\n // DOWN into SQL via an EXISTS join (indexed below). `key` is keyed → must be varchar on MySQL.\n `CREATE TABLE IF NOT EXISTS ${runAttributes} (\n ${attrCol('runId')} ${str} NOT NULL, ${attrCol('key')} ${str} NOT NULL,\n ${attrCol('strValue')} ${str}, ${attrCol('numValue')} ${num},\n PRIMARY KEY (${attrCol('runId')}, ${attrCol('key')})\n )`,\n `CREATE TABLE IF NOT EXISTS ${waiters} (\n ${waiterCol('token')} ${str} PRIMARY KEY, ${waiterCol('runId')} ${str} NOT NULL, ${waiterCol('seq')} ${int} NOT NULL,\n ${waiterCol('parallelGroup')} ${str}\n )`,\n `CREATE TABLE IF NOT EXISTS ${buffered} (\n ${bufferedId}, ${bufCol('token')} ${str} NOT NULL, ${bufCol('payload')} ${txt}\n )`,\n // Caller-minted uuid PK (engine.publishEvent), not auto-increment — unlike durable_buffered_signals\n // above, removeBufferedEvent needs to target the EXACT id it was buffered with.\n `CREATE TABLE IF NOT EXISTS ${bufferedEvents} (\n ${bevCol('id')} ${str} PRIMARY KEY, ${bevCol('name')} ${str} NOT NULL,\n ${bevCol('payload')} ${txt}, ${bevCol('publishedAt')} ${ts} NOT NULL\n )`,\n ];\n\n // Additive nullable columns gained across versions (e.g. `input`, `events`, `enqueuedAt`). On a\n // table that predates them, `CREATE TABLE IF NOT EXISTS` above is a no-op, so back-fill the ones\n // that are actually missing — the auto-schema self-heals instead of needing a manual migration.\n // We check the live columns first (rather than ALTER-and-ignore) so a *real* ALTER failure\n // surfaces instead of being swallowed as a presumed \"column already exists\".\n // Keyed by entity PROPERTY name; the physical column is resolved per-table so the ADD COLUMN\n // tracks the configured naming.\n const additive: Record<string, Array<[string, string]>> = {\n durable_workflow_runs: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['awaitingDecisionTaskId', str],\n ['recoveryAttempts', int],\n ['tags', txt],\n ['searchAttributes', txt],\n ['priority', int],\n // Nullable and NOT back-filled with a value: every row that predates the column keeps a NULL\n // origin, which the store reads as `undefined` = \"unknown origin\". There is no honest value to\n // back-fill — the origin is derived from the registration that created the run, which is long\n // gone by the time this ALTER runs.\n ['origin', str],\n ],\n durable_step_checkpoints: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['events', txt],\n ['workerGroup', str],\n ['parallelGroup', str],\n ['enqueuedAt', ts],\n ],\n // Back-fill the child-await fan group onto signal-waiter tables that predate it (a remote\n // `gather_children` fan-out threads its group here so the resolved `signal:child:` checkpoint carries it).\n durable_signal_waiters: [['parallelGroup', str]],\n };\n\n const runner = dataSource.createQueryRunner();\n try {\n for (const sql of tables) await runner.query(sql);\n for (const [table, props] of Object.entries(additive)) {\n const have = await existingColumns(runner, table, { isSqlite, isMysql });\n for (const [property, colType] of props) {\n const column = resolve(table, property);\n if (!have.has(column)) {\n await runner.query(`ALTER TABLE ${q(table)} ADD COLUMN ${q(column)} ${colType}`);\n }\n }\n }\n // Widen pre-existing MySQL `text` JSON-blob columns to `longtext`. A table created before this\n // fix has these as `text` (64KB) and silently truncates large `events`/`output` payloads. MODIFY\n // to `longtext` is idempotent (no-op if already longtext), so it's safe to run on every boot.\n // MySQL-only: Postgres/SQLite `text` is already unbounded, so there's nothing to widen.\n for (const sql of buildWidenStatements(isMysql, q, resolve)) {\n try {\n await runner.query(sql);\n } catch {\n /* column already longtext, or table/column not present on this deploy */\n }\n }\n // MySQL has no `CREATE INDEX IF NOT EXISTS`; create each best-effort and ignore \"already exists\".\n // - status/wakeAt: the timer poller (listDueTimers) and recovery (listIncompleteRuns) scans.\n // - workflow/status: the dashboard's listRuns filters. (Checkpoints are keyed by the PK prefix\n // `(runId, seq)`, so listCheckpoints/getEvent already hit an index — no extra one needed.)\n const indexes: Array<[string, string]> = [\n ['durable_runs_status_idx', `${runs} (${runsCol('status')}, ${runsCol('wakeAt')})`],\n [\n 'durable_runs_workflow_status_idx',\n `${runs} (${runsCol('workflow')}, ${runsCol('status')})`,\n ],\n // buffered signals are taken FIFO per token (smallest id) — index the token for the scan.\n ['durable_buffered_signals_token_idx', `${buffered} (${bufCol('token')})`],\n // buffered events are scanned oldest-first per name — one composite index serves both.\n [\n 'durable_buffered_events_name_published_at_idx',\n `${bufferedEvents} (${bevCol('name')}, ${bevCol('publishedAt')})`,\n ],\n // Search-attribute pushdown: equality + range predicates probe by (key, value). Two composite\n // indexes — one per typed column — so a numeric range or a string equality is an index scan.\n [\n 'durable_run_attributes_num_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('numValue')})`,\n ],\n [\n 'durable_run_attributes_str_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('strValue')})`,\n ],\n ];\n for (const [name, target] of indexes) {\n try {\n await runner.query(`CREATE INDEX ${q(name)} ON ${target}`);\n } catch {\n /* index already exists */\n }\n }\n } finally {\n await runner.release();\n }\n}\n\n/** The columns that currently exist on `table`, so the schema can add only the missing ones\n * (instead of ALTER-and-swallow, which would also hide a genuine ALTER failure). */\nasync function existingColumns(\n runner: { query: (sql: string, params?: unknown[]) => Promise<unknown> },\n table: string,\n d: { isSqlite: boolean; isMysql: boolean },\n): Promise<Set<string>> {\n if (d.isSqlite) {\n const rows = (await runner.query(`PRAGMA table_info(\"${table}\")`)) as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n }\n // Postgres + MySQL both expose information_schema; scope to the connection's own schema/db.\n // Bind-param placeholder differs: Postgres uses `$1`, MySQL uses `?` (TypeORM only rewrites `?`\n // inside the query builder, not for these raw `runner.query` calls), so pick per dialect.\n const scope = d.isMysql ? 'TABLE_SCHEMA = DATABASE()' : 'table_schema = current_schema()';\n const placeholder = d.isMysql ? '?' : '$1';\n const rows = (await runner.query(\n `SELECT column_name AS name FROM information_schema.columns WHERE table_name = ${placeholder} AND ${scope}`,\n [table],\n )) as Array<{ name?: string; COLUMN_NAME?: string }>;\n return new Set(rows.map((r) => (r.name ?? r.COLUMN_NAME) as string));\n}\n","import {\n type AttributeFilter,\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 sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n Brackets,\n type DataSource,\n type EntityManager,\n In,\n IsNull,\n LessThanOrEqual,\n Like,\n type SelectQueryBuilder,\n} from 'typeorm';\nimport type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { durableColumnResolver, ensureTypeOrmDurableSchema } from './schema';\n\n/**\n * TypeORM-backed `StateStore`. Works on any TypeORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps use native datetime columns and `wakeAt` is stored as a datetime too.\n */\nexport class TypeOrmStateStore implements StateStore {\n constructor(private readonly dataSource: DataSource) {}\n\n async ensureSchema(): Promise<void> {\n await ensureTypeOrmDurableSchema(this.dataSource);\n }\n\n private runs() {\n return this.dataSource.getRepository(WorkflowRunEntity);\n }\n private checkpoints() {\n return this.dataSource.getRepository(StepCheckpointEntity);\n }\n private waiters() {\n return this.dataSource.getRepository(SignalWaiterEntity);\n }\n private buffered() {\n return this.dataSource.getRepository(BufferedSignalEntity);\n }\n private bufferedEvents() {\n return this.dataSource.getRepository(BufferedEventEntity);\n }\n private attributes() {\n return this.dataSource.getRepository(RunAttributeEntity);\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 em?: EntityManager,\n ): Promise<void> {\n const repo = em ? em.getRepository(RunAttributeEntity) : this.attributes();\n await repo.delete({ runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await repo.insert(rows);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n await this.runs().save(toRunEntity(run));\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n // Single UPDATE (no pre-SELECT + save round-trips): map only the patched fields. The query\n // builder still applies the entities' JSON column transformers to the `set` values (as in\n // listRuns/tryLockRun). Preserve the not-found throw any caller may rely on.\n const update: Record<string, unknown> = {};\n if ('workflow' in patch) update.workflow = patch.workflow;\n if ('workflowVersion' in patch) update.workflowVersion = patch.workflowVersion;\n if ('status' in patch) update.status = patch.status;\n if ('input' in patch) update.input = patch.input ?? null;\n if ('output' in patch) update.output = patch.output ?? null;\n if ('error' in patch) update.error = patch.error ?? null;\n if ('wakeAt' in patch) update.wakeAt = patch.wakeAt == null ? null : new Date(patch.wakeAt);\n if ('lockedBy' in patch) update.lockedBy = patch.lockedBy ?? null;\n if ('lockedUntil' in patch)\n update.lockedUntil = patch.lockedUntil == null ? null : new Date(patch.lockedUntil);\n if ('awaitingDecisionTaskId' in patch)\n update.awaitingDecisionTaskId = patch.awaitingDecisionTaskId ?? null;\n if ('recoveryAttempts' in patch) update.recoveryAttempts = patch.recoveryAttempts;\n if ('tags' in patch) update.tags = patch.tags ?? null;\n if ('searchAttributes' in patch) update.searchAttributes = patch.searchAttributes ?? null;\n if ('priority' in patch) update.priority = patch.priority ?? null;\n if ('origin' in patch) update.origin = patch.origin ?? null;\n if ('createdAt' in patch) update.createdAt = patch.createdAt;\n if ('updatedAt' in patch) update.updatedAt = patch.updatedAt;\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set(update)\n .where({ id: runId })\n .execute();\n if (!result.affected) throw new Error(`run ${runId} not found`);\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 async getRun(runId: string): Promise<WorkflowRun | null> {\n const e = await this.runs().findOneBy({ id: runId });\n return e ? fromRunEntity(e) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n // Child rows first, then the run — checkpoints, signal waiters, attribute rows.\n await this.checkpoints().delete({ runId });\n await this.waiters().delete({ runId });\n await this.attributes().delete({ runId });\n await this.runs().delete({ id: runId });\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const e = await this.checkpoints().findOneBy({ runId, seq });\n return e ? fromCheckpointEntity(e) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n // upsert = INSERT ... ON CONFLICT DO UPDATE on the (runId, seq) key — drops the pre-SELECT that\n // `.save()` does on each write. JSON column transformers still apply for upsert.\n const entity = toCheckpointEntity(checkpoint) as QueryDeepPartialEntity<StepCheckpointEntity>;\n await this.checkpoints().upsert(entity, ['runId', 'seq']);\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.dataSource.transaction(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.getRepository(StepCheckpointEntity).save(toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({ status: In(['running', 'cancelling']) });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().find({\n where: { status: 'pending' },\n order: { createdAt: 'ASC' }, // FIFO dispatch\n take: limit,\n });\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({\n status: 'suspended',\n wakeAt: LessThanOrEqual(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 result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedBy: owner, lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId })\n .andWhere(\n new Brackets((qb) =>\n qb\n .where({ lockedUntil: IsNull() })\n .orWhere({ lockedUntil: LessThanOrEqual(new Date(nowMs)) }),\n ),\n )\n .execute();\n return result.affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n await this.runs().update({ id: runId }, { lockedBy: null, lockedUntil: () => 'NULL' });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId, lockedBy: owner })\n .execute();\n return result.affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n // `tags` carries a custom JSON transformer, and TypeORM applies a column transformer's `to()` to\n // FindOperator values too — so a `Like('%\"etl\"%')` in a plain `where` would be JSON-stringified\n // and corrupt the LIKE pattern. Use the query builder with a raw parameter to bypass that.\n const qb = this.runs().createQueryBuilder('r');\n if (query.workflow) qb.andWhere('r.workflow = :workflow', { workflow: query.workflow });\n // Which library registered the workflow. Plain equality, so a run whose origin is NULL (created\n // before the column existed, or registered through a path the derivation could not classify)\n // matches NO origin value — it is never folded into a bucket to make the facet look complete.\n // Unknown-origin runs are reachable only with the filter OFF, so \"all origins\" must stay the\n // default view; a dashboard that filtered by default would make those runs look deleted.\n if (query.origin !== undefined) qb.andWhere('r.origin = :origin', { origin: query.origin });\n if (query.status) qb.andWhere('r.status = :status', { status: query.status });\n if (query.statuses)\n qb.andWhere(\n query.statuses.length ? 'r.status IN (:...statuses)' : '1 = 0',\n query.statuses.length ? { statuses: query.statuses } : {},\n );\n // `tags` is a JSON-text column; match the quoted token so `etl` doesn't match `etl-foo`. NOTE: a\n // leading-wildcard LIKE on JSON text is NOT index-friendly (no B-tree index can serve `%\"x\"%`), so\n // tag-filtered scans (e.g. singleton admission) are sequential over `durable_runs_workflow_status_idx`'s\n // result set. The `statuses`/`workflow` predicates above bound that scan; if tag scans ever dominate,\n // promote tags to a normalized join table or a JSON/GIN index — a schema change, intentionally not done here.\n if (query.tag) qb.andWhere('r.tags LIKE :tagPattern', { tagPattern: `%\"${query.tag}\"%` });\n // Typed/range attribute predicates push DOWN into SQL: each filter becomes an EXISTS against the\n // normalized `durable_run_attributes` side-table (indexed on (key, numValue)/(key, strValue)), so\n // the DB does the filtering AND the LIMIT/OFFSET — no full scan + in-process filter. ANDed: a run\n // must satisfy every filter, so one EXISTS per filter.\n if (query.attributes?.length) {\n query.attributes.forEach((f, i) => this.applyAttributeExists(qb, f, i));\n }\n qb.orderBy('r.createdAt', 'DESC'); // newest first — recent runs on top in the dashboard\n if (query.limit != null) qb.take(query.limit);\n if (query.offset != null) qb.skip(query.offset);\n const rows = await qb.getMany();\n return rows.map(fromRunEntity);\n }\n\n /** Add one attribute predicate to `qb` as an EXISTS subquery on the side-table. `i` namespaces the\n * bind params so multiple ANDed filters don't collide. */\n private applyAttributeExists(\n qb: SelectQueryBuilder<WorkflowRunEntity>,\n f: AttributeFilter,\n i: number,\n ): void {\n const valueProperty = attributeColumnFor(f); // 'numValue' | 'strValue' (entity property)\n const cmp = sqlComparator(f.op);\n const keyParam = `attrKey${i}`;\n const valParam = `attrVal${i}`;\n // Resolve the PHYSICAL column names from the entity metadata so this raw subquery tracks the\n // configured naming (canonical snake_case by default) instead of hardcoding camelCase — which\n // would break the EXISTS pushdown the moment the table is snake_case.\n const resolve = durableColumnResolver(this.dataSource);\n const q = this.idQuote();\n const wrap = (name: string) => `${q}${name}${q}`;\n const runIdCol = wrap(resolve('durable_run_attributes', 'runId'));\n const keyCol = wrap(resolve('durable_run_attributes', 'key'));\n const valueCol = wrap(resolve('durable_run_attributes', valueProperty));\n const runPkCol = wrap(resolve('durable_workflow_runs', 'id'));\n const table = wrap('durable_run_attributes');\n // `<>` (ne) must also exclude rows where the attribute is absent: the missing-key-never-matches\n // contract (see core matchesAttributes). EXISTS already requires the key row to be present, and a\n // row only exists when the value is non-null, so EXISTS(... <> ...) gives exactly ne-with-present.\n const a = `a${i}`;\n const sql = `EXISTS (SELECT 1 FROM ${table} ${a} WHERE ${a}.${runIdCol} = r.${runPkCol} AND ${a}.${keyCol} = :${keyParam} AND ${a}.${valueCol} ${cmp} :${valParam})`;\n qb.andWhere(sql, {\n [keyParam]: f.key,\n [valParam]: attributeOperand(f),\n });\n }\n\n /** The identifier quote char for the active driver (MySQL backtick, others double-quote). */\n private idQuote(): string {\n const type = String(this.dataSource.options.type);\n return type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql' ? '`' : '\"';\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const rows = await this.checkpoints().find({ where: { runId }, order: { seq: 'ASC' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const e = await this.checkpoints().findOne({ where: { runId, name }, order: { seq: 'DESC' } });\n return e ? fromCheckpointEntity(e) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const rows = await this.checkpoints().find({\n where: prefixes.map((p) => ({ runId, name: Like(`${p}%`) })),\n order: { seq: 'ASC' },\n });\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n await this.waiters().save({\n token: waiter.token,\n runId: waiter.runId,\n seq: waiter.seq,\n parallelGroup: waiter.parallelGroup ?? null,\n });\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const rows = await this.waiters().find({ where: { 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 takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const e = await this.waiters().findOneBy({ token });\n if (!e) return null;\n const waiter: SignalWaiter = {\n token: e.token,\n runId: e.runId,\n seq: e.seq,\n parallelGroup: e.parallelGroup ?? undefined,\n };\n await this.waiters().delete({ token });\n return waiter;\n }\n\n async removeSignalWaiter(waiter: SignalWaiter): Promise<void> {\n // Exact-match delete (token AND runId AND seq) — a plain `delete({ token })` would remove\n // whatever row currently owns the token, even if a different run has since claimed it.\n await this.waiters().delete({ token: waiter.token, runId: waiter.runId, seq: waiter.seq });\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n await this.buffered().save({ token, payload: payload ?? null });\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const e = await this.buffered().findOne({ where: { token }, order: { id: 'ASC' } });\n if (!e) return null;\n await this.buffered().delete({ id: e.id });\n return { payload: e.payload ?? undefined };\n }\n\n async bufferEvent(input: {\n name: string;\n payload: unknown;\n id: string;\n publishedAt: number;\n }): Promise<void> {\n await this.bufferedEvents().save({\n id: input.id,\n name: input.name,\n payload: input.payload ?? null,\n publishedAt: new Date(input.publishedAt),\n });\n }\n\n async listBufferedEvents(\n name: string,\n limit: number,\n ): Promise<Array<{ id: string; payload: unknown; publishedAt: number }>> {\n const rows = await this.bufferedEvents().find({\n where: { name },\n order: { publishedAt: 'ASC' }, // oldest first\n take: limit,\n });\n return rows.map((e) => ({\n id: e.id,\n payload: e.payload ?? undefined,\n publishedAt: e.publishedAt.getTime(),\n }));\n }\n\n async removeBufferedEvent(id: string): Promise<boolean> {\n const result = await this.bufferedEvents().delete({ id });\n return result.affected === 1;\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n return {\n id: run.id,\n workflow: run.workflow,\n workflowVersion: run.workflowVersion,\n status: run.status,\n input: run.input ?? null,\n output: run.output ?? null,\n error: run.error ?? null,\n ...(run.wakeAt == null ? {} : { wakeAt: new Date(run.wakeAt) }),\n lockedBy: run.lockedBy ?? null,\n ...(run.lockedUntil == null ? {} : { lockedUntil: new Date(run.lockedUntil) }),\n awaitingDecisionTaskId: run.awaitingDecisionTaskId ?? null,\n ...(run.recoveryAttempts === undefined ? {} : { recoveryAttempts: run.recoveryAttempts }),\n tags: run.tags ?? null,\n searchAttributes: run.searchAttributes ?? null,\n priority: run.priority ?? null,\n // Absent origin stays SQL NULL — \"unknown\", never coerced into a real-looking library name.\n origin: run.origin ?? null,\n createdAt: run.createdAt,\n updatedAt: run.updatedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil == null ? undefined : e.lockedUntil.getTime(),\n awaitingDecisionTaskId: e.awaitingDecisionTaskId ?? undefined,\n recoveryAttempts: e.recoveryAttempts ?? undefined,\n tags: e.tags ?? undefined,\n searchAttributes: e.searchAttributes ?? undefined,\n priority: e.priority ?? undefined,\n // NULL (a row written before the column existed) surfaces as `undefined` = unknown origin.\n origin: e.origin ?? undefined,\n createdAt: e.createdAt,\n updatedAt: e.updatedAt,\n };\n}\n\nfunction toCheckpointEntity(cp: StepCheckpoint): StepCheckpointEntity {\n return {\n runId: cp.runId,\n seq: cp.seq,\n name: cp.name,\n kind: cp.kind,\n stepId: cp.stepId,\n status: cp.status,\n input: cp.input ?? null,\n output: cp.output ?? null,\n error: cp.error ?? null,\n events: cp.events ?? null,\n attempts: cp.attempts,\n workerGroup: cp.workerGroup ?? null,\n parallelGroup: cp.parallelGroup ?? null,\n ...(cp.wakeAt == null ? {} : { wakeAt: new Date(cp.wakeAt) }),\n enqueuedAt: cp.enqueuedAt,\n startedAt: cp.startedAt,\n finishedAt: cp.finishedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n // Older rows predate enqueuedAt; treat the worker start as enqueue time (queue-wait reads zero).\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n"],"mappings":"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","names":["parseJsonColumn","raw","field","undefined","JSON","parse","err","console","warn","message","jsonColumnTransformer","to","value","stringify","from","snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","awaitingDecisionTaskId","recoveryAttempts","tags","searchAttributes","priority","origin","createdAt","updatedAt","StepCheckpointEntity","runId","seq","name","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt","RunAttributeEntity","key","strValue","numValue","SignalWaiterEntity","token","BufferedSignalEntity","payload","BufferedEventEntity","publishedAt","durableEntities","options","col","workflowRuns","EntitySchema","target","tableName","columns","type","primary","nullable","transformer","Date","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","Number","generated","bufferedEvents","ENTITIES","JSON_BLOB_COLUMNS","durable_workflow_runs","durable_step_checkpoints","TABLE_TO_ENTITY","WorkflowRunEntity","StepCheckpointEntity","durable_run_attributes","RunAttributeEntity","durable_signal_waiters","SignalWaiterEntity","durable_buffered_signals","BufferedSignalEntity","durable_buffered_events","BufferedEventEntity","durableManagedTables","Object","keys","durableColumnResolver","dataSource","table","property","entity","Error","meta","getMetadata","column","findColumnWithPropertyName","databaseName","jsonBlobColumnType","isMysql","buildWidenStatements","quote","col","out","props","entries","prop","push","ensureTypeOrmDurableSchema","q","id","driver","escape","resolve","type","String","options","isPg","isSqlite","str","txt","int","ts","runs","checkpoints","runAttributes","waiters","buffered","bufferedEvents","runsCol","cpCol","attrCol","waiterCol","bufCol","bevCol","num","bufferedId","tables","additive","runner","createQueryRunner","sql","query","have","existingColumns","colType","has","indexes","name","target","release","d","rows","Set","map","r","scope","placeholder","COLUMN_NAME","import_typeorm","TypeOrmStateStore","dataSource","ensureSchema","ensureTypeOrmDurableSchema","runs","getRepository","WorkflowRunEntity","checkpoints","StepCheckpointEntity","waiters","SignalWaiterEntity","buffered","BufferedSignalEntity","bufferedEvents","BufferedEventEntity","attributes","RunAttributeEntity","reindexAttributes","runId","em","repo","delete","rows","normalizeAttributeRows","length","insert","createRun","run","save","toRunEntity","id","searchAttributes","updateRun","patch","update","workflow","workflowVersion","status","input","output","error","wakeAt","Date","lockedBy","lockedUntil","awaitingDecisionTaskId","recoveryAttempts","tags","priority","origin","createdAt","updatedAt","result","createQueryBuilder","set","where","execute","affected","Error","getRun","e","findOneBy","fromRunEntity","deleteRun","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","entity","toCheckpointEntity","upsert","transaction","work","raw","cp","listIncompleteRuns","findBy","In","map","listPendingRuns","limit","find","order","take","listDueTimers","nowMs","LessThanOrEqual","tryLockRun","owner","leaseUntilMs","andWhere","Brackets","qb","IsNull","orWhere","releaseRunLock","renewRunLock","listRuns","query","undefined","statuses","tag","tagPattern","forEach","f","i","applyAttributeExists","orderBy","offset","skip","getMany","valueProperty","attributeColumnFor","cmp","sqlComparator","op","keyParam","valParam","resolve","durableColumnResolver","q","idQuote","wrap","name","runIdCol","keyCol","valueCol","runPkCol","table","a","sql","key","attributeOperand","type","String","options","listCheckpoints","getLatestCheckpointByName","findOne","listCheckpointsByNamePrefix","prefixes","p","Like","putSignalWaiter","waiter","token","parallelGroup","listSignalWaiters","prefix","takeSignalWaiter","removeSignalWaiter","bufferSignal","payload","takeBufferedSignal","bufferEvent","publishedAt","listBufferedEvents","getTime","removeBufferedEvent","kind","stepId","events","attempts","workerGroup","enqueuedAt","startedAt","finishedAt"]}
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ var WorkflowRunEntity = class {
|
|
|
50
50
|
tags;
|
|
51
51
|
searchAttributes;
|
|
52
52
|
priority;
|
|
53
|
+
origin;
|
|
53
54
|
createdAt;
|
|
54
55
|
updatedAt;
|
|
55
56
|
};
|
|
@@ -196,6 +197,18 @@ function durableEntities(options = {}) {
|
|
|
196
197
|
nullable: true,
|
|
197
198
|
name: col("priority")
|
|
198
199
|
},
|
|
200
|
+
// Which library/module registered the workflow this run belongs to (e.g.
|
|
201
|
+
// `@dudousxd/nestjs-catalog-pipeline`), derived at registration time — never caller input.
|
|
202
|
+
// Nullable with NO default: an origin cannot be reconstructed for a row written before this
|
|
203
|
+
// column existed, so it stays NULL and reads back as `undefined` = "unknown". Defaulting it to
|
|
204
|
+
// a real-looking string would be indistinguishable from a genuine registration in the
|
|
205
|
+
// dashboard's facet. `ensureTypeOrmDurableSchema` back-fills the COLUMN onto pre-existing
|
|
206
|
+
// tables (see the `additive` map in ./schema), never its value.
|
|
207
|
+
origin: {
|
|
208
|
+
type: "text",
|
|
209
|
+
nullable: true,
|
|
210
|
+
name: col("origin")
|
|
211
|
+
},
|
|
199
212
|
createdAt: {
|
|
200
213
|
type: Date,
|
|
201
214
|
name: col("createdAt")
|
|
@@ -512,7 +525,7 @@ async function ensureTypeOrmDurableSchema(dataSource) {
|
|
|
512
525
|
${runsCol("wakeAt")} ${ts}, ${runsCol("lockedBy")} ${str}, ${runsCol("lockedUntil")} ${ts},
|
|
513
526
|
${runsCol("awaitingDecisionTaskId")} ${str},
|
|
514
527
|
${runsCol("recoveryAttempts")} ${int}, ${runsCol("tags")} ${txt}, ${runsCol("searchAttributes")} ${txt},
|
|
515
|
-
${runsCol("priority")} ${int},
|
|
528
|
+
${runsCol("priority")} ${int}, ${runsCol("origin")} ${str},
|
|
516
529
|
${runsCol("createdAt")} ${ts} NOT NULL, ${runsCol("updatedAt")} ${ts} NOT NULL
|
|
517
530
|
)`,
|
|
518
531
|
`CREATE TABLE IF NOT EXISTS ${checkpoints} (
|
|
@@ -578,6 +591,14 @@ async function ensureTypeOrmDurableSchema(dataSource) {
|
|
|
578
591
|
[
|
|
579
592
|
"priority",
|
|
580
593
|
int
|
|
594
|
+
],
|
|
595
|
+
// Nullable and NOT back-filled with a value: every row that predates the column keeps a NULL
|
|
596
|
+
// origin, which the store reads as `undefined` = "unknown origin". There is no honest value to
|
|
597
|
+
// back-fill — the origin is derived from the registration that created the run, which is long
|
|
598
|
+
// gone by the time this ALTER runs.
|
|
599
|
+
[
|
|
600
|
+
"origin",
|
|
601
|
+
str
|
|
581
602
|
]
|
|
582
603
|
],
|
|
583
604
|
durable_step_checkpoints: [
|
|
@@ -757,6 +778,7 @@ var TypeOrmStateStore = class {
|
|
|
757
778
|
if ("tags" in patch) update.tags = patch.tags ?? null;
|
|
758
779
|
if ("searchAttributes" in patch) update.searchAttributes = patch.searchAttributes ?? null;
|
|
759
780
|
if ("priority" in patch) update.priority = patch.priority ?? null;
|
|
781
|
+
if ("origin" in patch) update.origin = patch.origin ?? null;
|
|
760
782
|
if ("createdAt" in patch) update.createdAt = patch.createdAt;
|
|
761
783
|
if ("updatedAt" in patch) update.updatedAt = patch.updatedAt;
|
|
762
784
|
const result = await this.runs().createQueryBuilder().update().set(update).where({
|
|
@@ -870,6 +892,9 @@ var TypeOrmStateStore = class {
|
|
|
870
892
|
if (query.workflow) qb.andWhere("r.workflow = :workflow", {
|
|
871
893
|
workflow: query.workflow
|
|
872
894
|
});
|
|
895
|
+
if (query.origin !== void 0) qb.andWhere("r.origin = :origin", {
|
|
896
|
+
origin: query.origin
|
|
897
|
+
});
|
|
873
898
|
if (query.status) qb.andWhere("r.status = :status", {
|
|
874
899
|
status: query.status
|
|
875
900
|
});
|
|
@@ -1072,6 +1097,8 @@ function toRunEntity(run) {
|
|
|
1072
1097
|
tags: run.tags ?? null,
|
|
1073
1098
|
searchAttributes: run.searchAttributes ?? null,
|
|
1074
1099
|
priority: run.priority ?? null,
|
|
1100
|
+
// Absent origin stays SQL NULL — "unknown", never coerced into a real-looking library name.
|
|
1101
|
+
origin: run.origin ?? null,
|
|
1075
1102
|
createdAt: run.createdAt,
|
|
1076
1103
|
updatedAt: run.updatedAt
|
|
1077
1104
|
};
|
|
@@ -1094,6 +1121,8 @@ function fromRunEntity(e) {
|
|
|
1094
1121
|
tags: e.tags ?? void 0,
|
|
1095
1122
|
searchAttributes: e.searchAttributes ?? void 0,
|
|
1096
1123
|
priority: e.priority ?? void 0,
|
|
1124
|
+
// NULL (a row written before the column existed) surfaces as `undefined` = unknown origin.
|
|
1125
|
+
origin: e.origin ?? void 0,
|
|
1097
1126
|
createdAt: e.createdAt,
|
|
1098
1127
|
updatedAt: e.updatedAt
|
|
1099
1128
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/entities.ts","../src/schema.ts","../src/typeorm-state-store.ts"],"sourcesContent":["import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema, type ValueTransformer } from 'typeorm';\n\n/**\n * Safely deserialize a JSON-text column back to an object on READ.\n *\n * A column truncated by the old MySQL `text` 64KB limit (or otherwise corrupt) makes `JSON.parse`\n * throw \"Unterminated string in JSON\" — which, without this guard, would 500 the *entire* run-detail\n * read just because one blob is damaged. Instead, log once and degrade the bad field to `undefined`\n * so the rest of the run still renders. Valid JSON is unchanged.\n */\nexport function parseJsonColumn<T = unknown>(raw: unknown, field: string): T | undefined {\n if (raw == null) return undefined;\n // Some drivers (e.g. a native `json` column) already hand back a parsed object.\n if (typeof raw !== 'string') return raw as T;\n try {\n return JSON.parse(raw) as T;\n } catch (err) {\n console.warn(\n `durable store: failed to parse ${field} column, degrading to undefined: ${\n (err as Error).message\n }`,\n );\n return undefined;\n }\n}\n\n/**\n * TypeORM transformer for the free-form JSON-blob columns. Mirrors the built-in `simple-json`\n * serialization on WRITE (`JSON.stringify`, SQL NULL stays NULL) but uses {@link parseJsonColumn}\n * on READ so a single corrupt/truncated row degrades to `undefined` instead of throwing.\n */\nfunction jsonColumnTransformer(field: string): ValueTransformer {\n return {\n to: (value: unknown) => (value == null ? null : JSON.stringify(value)),\n from: (value: unknown) => parseJsonColumn(value, field),\n };\n}\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 MikroORM,\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\n/** Resolve a `DurableColumnNaming` to a `(property) => columnName` function. */\nexport function 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 `getRepository`/`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 | null;\n lockedUntil?: Date;\n awaitingDecisionTaskId?: string | null;\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 | null;\n parallelGroup?: string | null;\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/** A published event that matched no live waiter (see `engine.publishEvent`'s buffering). Keyed by a\n * caller-minted `id` (a uuid, not autoincrement — `removeBufferedEvent` targets it directly), not by\n * `runId`: events are name-based pub/sub, not tied to any one run. */\nexport class BufferedEventEntity {\n id!: string;\n name!: string;\n payload?: unknown;\n publishedAt!: Date;\n}\n\n/**\n * Build the durable TypeORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your `DataSource`/`TypeOrmModule.forFeature`. The JSON-blob\n * columns keep the `jsonColumnTransformer` (corruption-tolerant read), the primary keys / auto-\n * increment / nullability are preserved, and the `id` of `durable_buffered_signals` stays a driver-\n * native auto-increment column.\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 name: 'WorkflowRunEntity',\n target: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n columns: {\n id: { type: 'text', primary: true, name: col('id') },\n workflow: { type: 'text', name: col('workflow') },\n workflowVersion: { type: 'text', name: col('workflowVersion') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('runs.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('runs.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('runs.error'),\n },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n lockedBy: { type: 'text', nullable: true, name: col('lockedBy') },\n lockedUntil: { type: Date, nullable: true, name: col('lockedUntil') },\n // REMOTE turn the engine suspended on awaiting a decision; matched by completeRemoteDecision so\n // only the currently-awaited turn's decision is applied. Nullable: cleared when a decision lands.\n awaitingDecisionTaskId: {\n type: 'text',\n nullable: true,\n name: col('awaitingDecisionTaskId'),\n },\n recoveryAttempts: { type: 'integer', nullable: true, name: col('recoveryAttempts') },\n tags: {\n type: 'text',\n nullable: true,\n name: col('tags'),\n transformer: jsonColumnTransformer('runs.tags'),\n },\n searchAttributes: {\n type: 'text',\n nullable: true,\n name: col('searchAttributes'),\n transformer: jsonColumnTransformer('runs.searchAttributes'),\n },\n priority: { type: 'integer', nullable: true, name: col('priority') },\n createdAt: { type: Date, name: col('createdAt') },\n updatedAt: { type: Date, name: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n name: 'StepCheckpointEntity',\n target: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n seq: { type: 'integer', primary: true, name: col('seq') },\n name: { type: 'text', name: col('name') },\n kind: { type: 'text', name: col('kind') },\n stepId: { type: 'text', name: col('stepId') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('checkpoints.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('checkpoints.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('checkpoints.error'),\n },\n events: {\n type: 'text',\n nullable: true,\n name: col('events'),\n transformer: jsonColumnTransformer('checkpoints.events'),\n },\n attempts: { type: 'integer', name: col('attempts') },\n workerGroup: { type: 'text', nullable: true, name: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group so the dashboard renders\n // them as one parallel group; the core engine sets it (incl. from a remote worker's recordStep).\n parallelGroup: { type: 'text', nullable: true, name: col('parallelGroup') },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n enqueuedAt: { type: Date, nullable: true, name: col('enqueuedAt') },\n startedAt: { type: Date, name: col('startedAt') },\n finishedAt: { type: Date, name: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n name: 'RunAttributeEntity',\n target: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n key: { type: 'text', primary: true, name: col('key') },\n strValue: { type: 'text', nullable: true, name: col('strValue') },\n // `float`/`double` portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, name: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n name: 'SignalWaiterEntity',\n target: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n columns: {\n token: { type: 'text', primary: true, name: col('token') },\n runId: { type: 'text', name: col('runId') },\n seq: { type: 'integer', name: 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: 'text', nullable: true, name: col('parallelGroup') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n name: 'BufferedSignalEntity',\n target: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n columns: {\n // Auto-increment PK for FIFO ordering. Left as TypeORM's default increment generation so it\n // maps to the driver-native auto-increment column (INTEGER on SQLite, BIGINT/SERIAL elsewhere).\n id: { type: Number, primary: true, generated: 'increment', name: col('id') },\n token: { type: 'text', name: col('token') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.payload'),\n },\n },\n });\n\n const bufferedEvents = new EntitySchema<BufferedEventEntity>({\n name: 'BufferedEventEntity',\n target: BufferedEventEntity,\n tableName: 'durable_buffered_events',\n // No `indices` here (unlike MikroORM's EntitySchema, which does declare one): this store manages\n // its DDL — including the `(name, publishedAt)` index — via raw SQL in `schema.ts`'s\n // `ensureTypeOrmDurableSchema`, the same as every other durable_* table's indexes.\n columns: {\n // Caller-minted uuid (engine.publishEvent), not autoincrement — removeBufferedEvent targets it\n // directly and needs to be handed the SAME id it was buffered with.\n id: { type: 'text', primary: true, name: col('id') },\n name: { type: 'text', name: col('name') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.eventPayload'),\n },\n publishedAt: { type: Date, name: col('publishedAt') },\n },\n });\n\n return [\n workflowRuns,\n stepCheckpoints,\n runAttributes,\n signalWaiters,\n bufferedSignals,\n bufferedEvents,\n ];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import type { DataSource } from 'typeorm';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\n\n/** The JSON-blob columns per table (keyed by table name) that hold free-form payloads and so must\n * be the unbounded text type — `longtext` on MySQL, `text` elsewhere. Keyed by entity PROPERTY name;\n * the physical column is resolved from the DataSource metadata so it tracks the configured naming. */\nexport const JSON_BLOB_COLUMNS: Record<string, string[]> = {\n durable_workflow_runs: ['input', 'output', 'error', 'tags', 'searchAttributes'],\n durable_step_checkpoints: ['input', 'output', 'error', 'events'],\n // NOTE: durable_buffered_signals/durable_buffered_events intentionally omitted here, same as the\n // existing buffered-signals table — their `payload` column is created with the widened `txt` type\n // (see `${bufCol('payload')} ${txt}` / the new table's `${bevCol('payload')} ${txt}` below) directly\n // at CREATE TABLE time on a fresh install, so there's no pre-existing narrower column to widen.\n};\n\n/** Each durable table → its backing entity class, so a column resolver can look up the physical\n * column for any (table, property) from the DataSource metadata (which reflects the configured\n * `DurableColumnNaming`). Keeps the raw DDL/SQL in lock-step with the entity column mapping. */\nconst TABLE_TO_ENTITY = {\n durable_workflow_runs: WorkflowRunEntity,\n durable_step_checkpoints: StepCheckpointEntity,\n durable_run_attributes: RunAttributeEntity,\n durable_signal_waiters: SignalWaiterEntity,\n durable_buffered_signals: BufferedSignalEntity,\n durable_buffered_events: BufferedEventEntity,\n} as const;\n\n/**\n * Tables this store creates and manages at boot (autoSchema, see {@link ensureTypeOrmDurableSchema}).\n * Feed to your ORM's migration differ exclude/skipTables so it never tries to drop them.\n *\n * ```ts\n * // MikroORM-flavoured example (TypeORM has no built-in equivalent option); adapt to whichever\n * // migration tool generates your diffs — e.g. exclude these names from a typeorm-generated\n * // migration, or filter them out of `SELECT table_name FROM information_schema.tables` before\n * // diffing against your own entities.\n * const skipTables = new Set(durableManagedTables());\n * ```\n *\n * Derived from the same {@link TABLE_TO_ENTITY} map the raw-SQL heal keys its column resolution off\n * — one list, so a consumer's denylist can never drift from what this store actually owns.\n */\nexport function durableManagedTables(): string[] {\n return Object.keys(TABLE_TO_ENTITY);\n}\n\n/**\n * Resolve `(table, property) => physical column name` from the DataSource's entity metadata. The\n * column names are NOT hardcoded here: they come from whatever the registered `durableEntities`\n * mapped (canonical `snake_case` by default, `'preserve'` for the legacy camelCase, or a custom\n * mapping), so the raw `CREATE TABLE`/index/EXISTS SQL always matches the entities and can never\n * silently diverge.\n */\nexport function durableColumnResolver(\n dataSource: DataSource,\n): (table: string, property: string) => string {\n return (table, property) => {\n const entity = TABLE_TO_ENTITY[table as keyof typeof TABLE_TO_ENTITY];\n if (!entity) throw new Error(`durable store: unknown table \"${table}\"`);\n const meta = dataSource.getMetadata(entity);\n const column = meta.findColumnWithPropertyName(property);\n if (!column) {\n throw new Error(`durable store: no column for ${table}.${property} in entity metadata`);\n }\n return column.databaseName;\n };\n}\n\n/** The SQL type for free-form JSON-blob columns. MySQL `text` caps at 64KB and silently truncates,\n * so use `longtext` (4GB) there; Postgres/SQLite `text` is already unbounded. */\nexport function jsonBlobColumnType(isMysql: boolean): 'longtext' | 'text' {\n return isMysql ? 'longtext' : 'text';\n}\n\n/** Idempotent `MODIFY COLUMN ... longtext` statements that widen pre-existing MySQL `text` JSON-blob\n * columns. Returns `[]` on non-MySQL dialects (their `text` is already unbounded — nothing to do).\n * `quote` quotes an identifier per the active driver; `col` resolves a property to its physical\n * column name so the widen tracks the configured naming. */\nexport function buildWidenStatements(\n isMysql: boolean,\n quote: (id: string) => string,\n col: (table: string, property: string) => string,\n): string[] {\n if (!isMysql) return [];\n const out: string[] = [];\n for (const [table, props] of Object.entries(JSON_BLOB_COLUMNS)) {\n for (const prop of props) {\n out.push(`ALTER TABLE ${quote(table)} MODIFY COLUMN ${quote(col(table, prop))} longtext`);\n }\n }\n return out;\n}\n\n/**\n * Idempotently create the durable tables. Safe to run on every boot (auto-schema) and the\n * exact function to call from your own TypeORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables implements MigrationInterface {\n * async up(q: QueryRunner) { await ensureTypeOrmDurableSchema(q.connection); }\n * }\n * ```\n *\n * Dialect-aware (MySQL / MariaDB / Postgres / SQLite): identifiers are quoted per driver, keyed\n * string columns are `varchar` (MySQL can't key a `text` column), and the index is best-effort.\n * Only ever adds the three durable tables — it never touches your other tables.\n */\nexport async function ensureTypeOrmDurableSchema(dataSource: DataSource): Promise<void> {\n const q = (id: string) => dataSource.driver.escape(id);\n // Physical column names come from the entity metadata (canonical snake_case by default), so this\n // raw DDL always matches the registered `durableEntities` instead of hardcoding camelCase.\n const resolve = durableColumnResolver(dataSource);\n const type = String(dataSource.options.type);\n const isPg = type === 'postgres' || type === 'aurora-postgres';\n const isMysql = type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql';\n const isSqlite = type === 'sqlite' || type === 'better-sqlite3' || type === 'expo';\n\n // Keyed/short strings must be varchar on MySQL (a `text` PK/index needs a key length); `text`\n // for the free-form JSON payloads. Dates use the dialect's native timestamp type.\n const str = 'varchar(191)';\n // Free-form JSON payloads (`input`/`output`/`error`/`events`/`tags`/`searchAttributes`). MySQL's\n // `text` caps at 64KB and *silently truncates* — a large fan-out step's `events` then fails to\n // parse on read (\"Unterminated string in JSON\"). Use `longtext` (4GB) on MySQL; Postgres `text`\n // is already unbounded and SQLite `text` has no length limit.\n const txt = jsonBlobColumnType(isMysql);\n const int = isMysql ? 'int' : 'integer';\n const ts = isPg ? 'timestamptz' : 'datetime';\n\n const runs = q('durable_workflow_runs');\n const checkpoints = q('durable_step_checkpoints');\n const runAttributes = q('durable_run_attributes');\n const waiters = q('durable_signal_waiters');\n const buffered = q('durable_buffered_signals');\n const bufferedEvents = q('durable_buffered_events');\n // Per-table quoted-column helpers: resolve an entity property to its physical column then quote it.\n const runsCol = (property: string) => q(resolve('durable_workflow_runs', property));\n const cpCol = (property: string) => q(resolve('durable_step_checkpoints', property));\n const attrCol = (property: string) => q(resolve('durable_run_attributes', property));\n const waiterCol = (property: string) => q(resolve('durable_signal_waiters', property));\n const bufCol = (property: string) => q(resolve('durable_buffered_signals', property));\n const bevCol = (property: string) => q(resolve('durable_buffered_events', property));\n // Numeric side-table column for attribute range scans. `double precision` on Postgres, `double` on\n // MySQL, `real` on SQLite (all hold JS numbers without precision loss for the typical attribute).\n const num = isPg ? 'double precision' : isMysql ? 'double' : 'real';\n\n // Auto-increment PK syntax differs per dialect: SQLite wants `INTEGER PRIMARY KEY AUTOINCREMENT`,\n // MySQL `BIGINT AUTO_INCREMENT PRIMARY KEY`, Postgres `BIGSERIAL PRIMARY KEY`.\n const bufferedId = isSqlite\n ? `${bufCol('id')} integer PRIMARY KEY AUTOINCREMENT`\n : isMysql\n ? `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`\n : isPg\n ? `${bufCol('id')} bigserial PRIMARY KEY`\n : `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`;\n\n const tables = [\n `CREATE TABLE IF NOT EXISTS ${runs} (\n ${runsCol('id')} ${str} PRIMARY KEY,\n ${runsCol('workflow')} ${str} NOT NULL,\n ${runsCol('workflowVersion')} ${str} NOT NULL,\n ${runsCol('status')} ${str} NOT NULL,\n ${runsCol('input')} ${txt}, ${runsCol('output')} ${txt}, ${runsCol('error')} ${txt},\n ${runsCol('wakeAt')} ${ts}, ${runsCol('lockedBy')} ${str}, ${runsCol('lockedUntil')} ${ts},\n ${runsCol('awaitingDecisionTaskId')} ${str},\n ${runsCol('recoveryAttempts')} ${int}, ${runsCol('tags')} ${txt}, ${runsCol('searchAttributes')} ${txt},\n ${runsCol('priority')} ${int},\n ${runsCol('createdAt')} ${ts} NOT NULL, ${runsCol('updatedAt')} ${ts} NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS ${checkpoints} (\n ${cpCol('runId')} ${str} NOT NULL, ${cpCol('seq')} ${int} NOT NULL,\n ${cpCol('name')} ${str} NOT NULL, ${cpCol('kind')} ${str} NOT NULL, ${cpCol('stepId')} ${str} NOT NULL,\n ${cpCol('status')} ${str} NOT NULL, ${cpCol('input')} ${txt}, ${cpCol('output')} ${txt}, ${cpCol('error')} ${txt}, ${cpCol('events')} ${txt},\n ${cpCol('attempts')} ${int} NOT NULL, ${cpCol('workerGroup')} ${str}, ${cpCol('parallelGroup')} ${str},\n ${cpCol('wakeAt')} ${ts}, ${cpCol('enqueuedAt')} ${ts},\n ${cpCol('startedAt')} ${ts} NOT NULL, ${cpCol('finishedAt')} ${ts} NOT NULL,\n PRIMARY KEY (${cpCol('runId')}, ${cpCol('seq')})\n )`,\n // Normalized search-attribute side-table: one row per (run, key) so attribute predicates push\n // DOWN into SQL via an EXISTS join (indexed below). `key` is keyed → must be varchar on MySQL.\n `CREATE TABLE IF NOT EXISTS ${runAttributes} (\n ${attrCol('runId')} ${str} NOT NULL, ${attrCol('key')} ${str} NOT NULL,\n ${attrCol('strValue')} ${str}, ${attrCol('numValue')} ${num},\n PRIMARY KEY (${attrCol('runId')}, ${attrCol('key')})\n )`,\n `CREATE TABLE IF NOT EXISTS ${waiters} (\n ${waiterCol('token')} ${str} PRIMARY KEY, ${waiterCol('runId')} ${str} NOT NULL, ${waiterCol('seq')} ${int} NOT NULL,\n ${waiterCol('parallelGroup')} ${str}\n )`,\n `CREATE TABLE IF NOT EXISTS ${buffered} (\n ${bufferedId}, ${bufCol('token')} ${str} NOT NULL, ${bufCol('payload')} ${txt}\n )`,\n // Caller-minted uuid PK (engine.publishEvent), not auto-increment — unlike durable_buffered_signals\n // above, removeBufferedEvent needs to target the EXACT id it was buffered with.\n `CREATE TABLE IF NOT EXISTS ${bufferedEvents} (\n ${bevCol('id')} ${str} PRIMARY KEY, ${bevCol('name')} ${str} NOT NULL,\n ${bevCol('payload')} ${txt}, ${bevCol('publishedAt')} ${ts} NOT NULL\n )`,\n ];\n\n // Additive nullable columns gained across versions (e.g. `input`, `events`, `enqueuedAt`). On a\n // table that predates them, `CREATE TABLE IF NOT EXISTS` above is a no-op, so back-fill the ones\n // that are actually missing — the auto-schema self-heals instead of needing a manual migration.\n // We check the live columns first (rather than ALTER-and-ignore) so a *real* ALTER failure\n // surfaces instead of being swallowed as a presumed \"column already exists\".\n // Keyed by entity PROPERTY name; the physical column is resolved per-table so the ADD COLUMN\n // tracks the configured naming.\n const additive: Record<string, Array<[string, string]>> = {\n durable_workflow_runs: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['awaitingDecisionTaskId', str],\n ['recoveryAttempts', int],\n ['tags', txt],\n ['searchAttributes', txt],\n ['priority', int],\n ],\n durable_step_checkpoints: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['events', txt],\n ['workerGroup', str],\n ['parallelGroup', str],\n ['enqueuedAt', ts],\n ],\n // Back-fill the child-await fan group onto signal-waiter tables that predate it (a remote\n // `gather_children` fan-out threads its group here so the resolved `signal:child:` checkpoint carries it).\n durable_signal_waiters: [['parallelGroup', str]],\n };\n\n const runner = dataSource.createQueryRunner();\n try {\n for (const sql of tables) await runner.query(sql);\n for (const [table, props] of Object.entries(additive)) {\n const have = await existingColumns(runner, table, { isSqlite, isMysql });\n for (const [property, colType] of props) {\n const column = resolve(table, property);\n if (!have.has(column)) {\n await runner.query(`ALTER TABLE ${q(table)} ADD COLUMN ${q(column)} ${colType}`);\n }\n }\n }\n // Widen pre-existing MySQL `text` JSON-blob columns to `longtext`. A table created before this\n // fix has these as `text` (64KB) and silently truncates large `events`/`output` payloads. MODIFY\n // to `longtext` is idempotent (no-op if already longtext), so it's safe to run on every boot.\n // MySQL-only: Postgres/SQLite `text` is already unbounded, so there's nothing to widen.\n for (const sql of buildWidenStatements(isMysql, q, resolve)) {\n try {\n await runner.query(sql);\n } catch {\n /* column already longtext, or table/column not present on this deploy */\n }\n }\n // MySQL has no `CREATE INDEX IF NOT EXISTS`; create each best-effort and ignore \"already exists\".\n // - status/wakeAt: the timer poller (listDueTimers) and recovery (listIncompleteRuns) scans.\n // - workflow/status: the dashboard's listRuns filters. (Checkpoints are keyed by the PK prefix\n // `(runId, seq)`, so listCheckpoints/getEvent already hit an index — no extra one needed.)\n const indexes: Array<[string, string]> = [\n ['durable_runs_status_idx', `${runs} (${runsCol('status')}, ${runsCol('wakeAt')})`],\n [\n 'durable_runs_workflow_status_idx',\n `${runs} (${runsCol('workflow')}, ${runsCol('status')})`,\n ],\n // buffered signals are taken FIFO per token (smallest id) — index the token for the scan.\n ['durable_buffered_signals_token_idx', `${buffered} (${bufCol('token')})`],\n // buffered events are scanned oldest-first per name — one composite index serves both.\n [\n 'durable_buffered_events_name_published_at_idx',\n `${bufferedEvents} (${bevCol('name')}, ${bevCol('publishedAt')})`,\n ],\n // Search-attribute pushdown: equality + range predicates probe by (key, value). Two composite\n // indexes — one per typed column — so a numeric range or a string equality is an index scan.\n [\n 'durable_run_attributes_num_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('numValue')})`,\n ],\n [\n 'durable_run_attributes_str_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('strValue')})`,\n ],\n ];\n for (const [name, target] of indexes) {\n try {\n await runner.query(`CREATE INDEX ${q(name)} ON ${target}`);\n } catch {\n /* index already exists */\n }\n }\n } finally {\n await runner.release();\n }\n}\n\n/** The columns that currently exist on `table`, so the schema can add only the missing ones\n * (instead of ALTER-and-swallow, which would also hide a genuine ALTER failure). */\nasync function existingColumns(\n runner: { query: (sql: string, params?: unknown[]) => Promise<unknown> },\n table: string,\n d: { isSqlite: boolean; isMysql: boolean },\n): Promise<Set<string>> {\n if (d.isSqlite) {\n const rows = (await runner.query(`PRAGMA table_info(\"${table}\")`)) as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n }\n // Postgres + MySQL both expose information_schema; scope to the connection's own schema/db.\n // Bind-param placeholder differs: Postgres uses `$1`, MySQL uses `?` (TypeORM only rewrites `?`\n // inside the query builder, not for these raw `runner.query` calls), so pick per dialect.\n const scope = d.isMysql ? 'TABLE_SCHEMA = DATABASE()' : 'table_schema = current_schema()';\n const placeholder = d.isMysql ? '?' : '$1';\n const rows = (await runner.query(\n `SELECT column_name AS name FROM information_schema.columns WHERE table_name = ${placeholder} AND ${scope}`,\n [table],\n )) as Array<{ name?: string; COLUMN_NAME?: string }>;\n return new Set(rows.map((r) => (r.name ?? r.COLUMN_NAME) as string));\n}\n","import {\n type AttributeFilter,\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 sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n Brackets,\n type DataSource,\n type EntityManager,\n In,\n IsNull,\n LessThanOrEqual,\n Like,\n type SelectQueryBuilder,\n} from 'typeorm';\nimport type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { durableColumnResolver, ensureTypeOrmDurableSchema } from './schema';\n\n/**\n * TypeORM-backed `StateStore`. Works on any TypeORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps use native datetime columns and `wakeAt` is stored as a datetime too.\n */\nexport class TypeOrmStateStore implements StateStore {\n constructor(private readonly dataSource: DataSource) {}\n\n async ensureSchema(): Promise<void> {\n await ensureTypeOrmDurableSchema(this.dataSource);\n }\n\n private runs() {\n return this.dataSource.getRepository(WorkflowRunEntity);\n }\n private checkpoints() {\n return this.dataSource.getRepository(StepCheckpointEntity);\n }\n private waiters() {\n return this.dataSource.getRepository(SignalWaiterEntity);\n }\n private buffered() {\n return this.dataSource.getRepository(BufferedSignalEntity);\n }\n private bufferedEvents() {\n return this.dataSource.getRepository(BufferedEventEntity);\n }\n private attributes() {\n return this.dataSource.getRepository(RunAttributeEntity);\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 em?: EntityManager,\n ): Promise<void> {\n const repo = em ? em.getRepository(RunAttributeEntity) : this.attributes();\n await repo.delete({ runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await repo.insert(rows);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n await this.runs().save(toRunEntity(run));\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n // Single UPDATE (no pre-SELECT + save round-trips): map only the patched fields. The query\n // builder still applies the entities' JSON column transformers to the `set` values (as in\n // listRuns/tryLockRun). Preserve the not-found throw any caller may rely on.\n const update: Record<string, unknown> = {};\n if ('workflow' in patch) update.workflow = patch.workflow;\n if ('workflowVersion' in patch) update.workflowVersion = patch.workflowVersion;\n if ('status' in patch) update.status = patch.status;\n if ('input' in patch) update.input = patch.input ?? null;\n if ('output' in patch) update.output = patch.output ?? null;\n if ('error' in patch) update.error = patch.error ?? null;\n if ('wakeAt' in patch) update.wakeAt = patch.wakeAt == null ? null : new Date(patch.wakeAt);\n if ('lockedBy' in patch) update.lockedBy = patch.lockedBy ?? null;\n if ('lockedUntil' in patch)\n update.lockedUntil = patch.lockedUntil == null ? null : new Date(patch.lockedUntil);\n if ('awaitingDecisionTaskId' in patch)\n update.awaitingDecisionTaskId = patch.awaitingDecisionTaskId ?? null;\n if ('recoveryAttempts' in patch) update.recoveryAttempts = patch.recoveryAttempts;\n if ('tags' in patch) update.tags = patch.tags ?? null;\n if ('searchAttributes' in patch) update.searchAttributes = patch.searchAttributes ?? null;\n if ('priority' in patch) update.priority = patch.priority ?? null;\n if ('createdAt' in patch) update.createdAt = patch.createdAt;\n if ('updatedAt' in patch) update.updatedAt = patch.updatedAt;\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set(update)\n .where({ id: runId })\n .execute();\n if (!result.affected) throw new Error(`run ${runId} not found`);\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 async getRun(runId: string): Promise<WorkflowRun | null> {\n const e = await this.runs().findOneBy({ id: runId });\n return e ? fromRunEntity(e) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n // Child rows first, then the run — checkpoints, signal waiters, attribute rows.\n await this.checkpoints().delete({ runId });\n await this.waiters().delete({ runId });\n await this.attributes().delete({ runId });\n await this.runs().delete({ id: runId });\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const e = await this.checkpoints().findOneBy({ runId, seq });\n return e ? fromCheckpointEntity(e) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n // upsert = INSERT ... ON CONFLICT DO UPDATE on the (runId, seq) key — drops the pre-SELECT that\n // `.save()` does on each write. JSON column transformers still apply for upsert.\n const entity = toCheckpointEntity(checkpoint) as QueryDeepPartialEntity<StepCheckpointEntity>;\n await this.checkpoints().upsert(entity, ['runId', 'seq']);\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.dataSource.transaction(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.getRepository(StepCheckpointEntity).save(toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({ status: In(['running', 'cancelling']) });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().find({\n where: { status: 'pending' },\n order: { createdAt: 'ASC' }, // FIFO dispatch\n take: limit,\n });\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({\n status: 'suspended',\n wakeAt: LessThanOrEqual(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 result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedBy: owner, lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId })\n .andWhere(\n new Brackets((qb) =>\n qb\n .where({ lockedUntil: IsNull() })\n .orWhere({ lockedUntil: LessThanOrEqual(new Date(nowMs)) }),\n ),\n )\n .execute();\n return result.affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n await this.runs().update({ id: runId }, { lockedBy: null, lockedUntil: () => 'NULL' });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId, lockedBy: owner })\n .execute();\n return result.affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n // `tags` carries a custom JSON transformer, and TypeORM applies a column transformer's `to()` to\n // FindOperator values too — so a `Like('%\"etl\"%')` in a plain `where` would be JSON-stringified\n // and corrupt the LIKE pattern. Use the query builder with a raw parameter to bypass that.\n const qb = this.runs().createQueryBuilder('r');\n if (query.workflow) qb.andWhere('r.workflow = :workflow', { workflow: query.workflow });\n if (query.status) qb.andWhere('r.status = :status', { status: query.status });\n if (query.statuses)\n qb.andWhere(\n query.statuses.length ? 'r.status IN (:...statuses)' : '1 = 0',\n query.statuses.length ? { statuses: query.statuses } : {},\n );\n // `tags` is a JSON-text column; match the quoted token so `etl` doesn't match `etl-foo`. NOTE: a\n // leading-wildcard LIKE on JSON text is NOT index-friendly (no B-tree index can serve `%\"x\"%`), so\n // tag-filtered scans (e.g. singleton admission) are sequential over `durable_runs_workflow_status_idx`'s\n // result set. The `statuses`/`workflow` predicates above bound that scan; if tag scans ever dominate,\n // promote tags to a normalized join table or a JSON/GIN index — a schema change, intentionally not done here.\n if (query.tag) qb.andWhere('r.tags LIKE :tagPattern', { tagPattern: `%\"${query.tag}\"%` });\n // Typed/range attribute predicates push DOWN into SQL: each filter becomes an EXISTS against the\n // normalized `durable_run_attributes` side-table (indexed on (key, numValue)/(key, strValue)), so\n // the DB does the filtering AND the LIMIT/OFFSET — no full scan + in-process filter. ANDed: a run\n // must satisfy every filter, so one EXISTS per filter.\n if (query.attributes?.length) {\n query.attributes.forEach((f, i) => this.applyAttributeExists(qb, f, i));\n }\n qb.orderBy('r.createdAt', 'DESC'); // newest first — recent runs on top in the dashboard\n if (query.limit != null) qb.take(query.limit);\n if (query.offset != null) qb.skip(query.offset);\n const rows = await qb.getMany();\n return rows.map(fromRunEntity);\n }\n\n /** Add one attribute predicate to `qb` as an EXISTS subquery on the side-table. `i` namespaces the\n * bind params so multiple ANDed filters don't collide. */\n private applyAttributeExists(\n qb: SelectQueryBuilder<WorkflowRunEntity>,\n f: AttributeFilter,\n i: number,\n ): void {\n const valueProperty = attributeColumnFor(f); // 'numValue' | 'strValue' (entity property)\n const cmp = sqlComparator(f.op);\n const keyParam = `attrKey${i}`;\n const valParam = `attrVal${i}`;\n // Resolve the PHYSICAL column names from the entity metadata so this raw subquery tracks the\n // configured naming (canonical snake_case by default) instead of hardcoding camelCase — which\n // would break the EXISTS pushdown the moment the table is snake_case.\n const resolve = durableColumnResolver(this.dataSource);\n const q = this.idQuote();\n const wrap = (name: string) => `${q}${name}${q}`;\n const runIdCol = wrap(resolve('durable_run_attributes', 'runId'));\n const keyCol = wrap(resolve('durable_run_attributes', 'key'));\n const valueCol = wrap(resolve('durable_run_attributes', valueProperty));\n const runPkCol = wrap(resolve('durable_workflow_runs', 'id'));\n const table = wrap('durable_run_attributes');\n // `<>` (ne) must also exclude rows where the attribute is absent: the missing-key-never-matches\n // contract (see core matchesAttributes). EXISTS already requires the key row to be present, and a\n // row only exists when the value is non-null, so EXISTS(... <> ...) gives exactly ne-with-present.\n const a = `a${i}`;\n const sql = `EXISTS (SELECT 1 FROM ${table} ${a} WHERE ${a}.${runIdCol} = r.${runPkCol} AND ${a}.${keyCol} = :${keyParam} AND ${a}.${valueCol} ${cmp} :${valParam})`;\n qb.andWhere(sql, {\n [keyParam]: f.key,\n [valParam]: attributeOperand(f),\n });\n }\n\n /** The identifier quote char for the active driver (MySQL backtick, others double-quote). */\n private idQuote(): string {\n const type = String(this.dataSource.options.type);\n return type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql' ? '`' : '\"';\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const rows = await this.checkpoints().find({ where: { runId }, order: { seq: 'ASC' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const e = await this.checkpoints().findOne({ where: { runId, name }, order: { seq: 'DESC' } });\n return e ? fromCheckpointEntity(e) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const rows = await this.checkpoints().find({\n where: prefixes.map((p) => ({ runId, name: Like(`${p}%`) })),\n order: { seq: 'ASC' },\n });\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n await this.waiters().save({\n token: waiter.token,\n runId: waiter.runId,\n seq: waiter.seq,\n parallelGroup: waiter.parallelGroup ?? null,\n });\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const rows = await this.waiters().find({ where: { 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 takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const e = await this.waiters().findOneBy({ token });\n if (!e) return null;\n const waiter: SignalWaiter = {\n token: e.token,\n runId: e.runId,\n seq: e.seq,\n parallelGroup: e.parallelGroup ?? undefined,\n };\n await this.waiters().delete({ token });\n return waiter;\n }\n\n async removeSignalWaiter(waiter: SignalWaiter): Promise<void> {\n // Exact-match delete (token AND runId AND seq) — a plain `delete({ token })` would remove\n // whatever row currently owns the token, even if a different run has since claimed it.\n await this.waiters().delete({ token: waiter.token, runId: waiter.runId, seq: waiter.seq });\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n await this.buffered().save({ token, payload: payload ?? null });\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const e = await this.buffered().findOne({ where: { token }, order: { id: 'ASC' } });\n if (!e) return null;\n await this.buffered().delete({ id: e.id });\n return { payload: e.payload ?? undefined };\n }\n\n async bufferEvent(input: {\n name: string;\n payload: unknown;\n id: string;\n publishedAt: number;\n }): Promise<void> {\n await this.bufferedEvents().save({\n id: input.id,\n name: input.name,\n payload: input.payload ?? null,\n publishedAt: new Date(input.publishedAt),\n });\n }\n\n async listBufferedEvents(\n name: string,\n limit: number,\n ): Promise<Array<{ id: string; payload: unknown; publishedAt: number }>> {\n const rows = await this.bufferedEvents().find({\n where: { name },\n order: { publishedAt: 'ASC' }, // oldest first\n take: limit,\n });\n return rows.map((e) => ({\n id: e.id,\n payload: e.payload ?? undefined,\n publishedAt: e.publishedAt.getTime(),\n }));\n }\n\n async removeBufferedEvent(id: string): Promise<boolean> {\n const result = await this.bufferedEvents().delete({ id });\n return result.affected === 1;\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n return {\n id: run.id,\n workflow: run.workflow,\n workflowVersion: run.workflowVersion,\n status: run.status,\n input: run.input ?? null,\n output: run.output ?? null,\n error: run.error ?? null,\n ...(run.wakeAt == null ? {} : { wakeAt: new Date(run.wakeAt) }),\n lockedBy: run.lockedBy ?? null,\n ...(run.lockedUntil == null ? {} : { lockedUntil: new Date(run.lockedUntil) }),\n awaitingDecisionTaskId: run.awaitingDecisionTaskId ?? null,\n ...(run.recoveryAttempts === undefined ? {} : { recoveryAttempts: run.recoveryAttempts }),\n tags: run.tags ?? null,\n searchAttributes: run.searchAttributes ?? null,\n priority: run.priority ?? null,\n createdAt: run.createdAt,\n updatedAt: run.updatedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil == null ? undefined : e.lockedUntil.getTime(),\n awaitingDecisionTaskId: e.awaitingDecisionTaskId ?? undefined,\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 return {\n runId: cp.runId,\n seq: cp.seq,\n name: cp.name,\n kind: cp.kind,\n stepId: cp.stepId,\n status: cp.status,\n input: cp.input ?? null,\n output: cp.output ?? null,\n error: cp.error ?? null,\n events: cp.events ?? null,\n attempts: cp.attempts,\n workerGroup: cp.workerGroup ?? null,\n parallelGroup: cp.parallelGroup ?? null,\n ...(cp.wakeAt == null ? {} : { wakeAt: new Date(cp.wakeAt) }),\n enqueuedAt: cp.enqueuedAt,\n startedAt: cp.startedAt,\n finishedAt: cp.finishedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n // Older rows predate enqueuedAt; treat the worker start as enqueue time (queue-wait reads zero).\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n"],"mappings":"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","names":["EntitySchema","parseJsonColumn","raw","field","undefined","JSON","parse","err","console","warn","message","jsonColumnTransformer","to","value","stringify","from","snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","awaitingDecisionTaskId","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","BufferedEventEntity","publishedAt","durableEntities","options","col","workflowRuns","EntitySchema","target","tableName","columns","type","primary","nullable","transformer","Date","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","Number","generated","bufferedEvents","ENTITIES","JSON_BLOB_COLUMNS","durable_workflow_runs","durable_step_checkpoints","TABLE_TO_ENTITY","WorkflowRunEntity","StepCheckpointEntity","durable_run_attributes","RunAttributeEntity","durable_signal_waiters","SignalWaiterEntity","durable_buffered_signals","BufferedSignalEntity","durable_buffered_events","BufferedEventEntity","durableManagedTables","Object","keys","durableColumnResolver","dataSource","table","property","entity","Error","meta","getMetadata","column","findColumnWithPropertyName","databaseName","jsonBlobColumnType","isMysql","buildWidenStatements","quote","col","out","props","entries","prop","push","ensureTypeOrmDurableSchema","q","id","driver","escape","resolve","type","String","options","isPg","isSqlite","str","txt","int","ts","runs","checkpoints","runAttributes","waiters","buffered","bufferedEvents","runsCol","cpCol","attrCol","waiterCol","bufCol","bevCol","num","bufferedId","tables","additive","runner","createQueryRunner","sql","query","have","existingColumns","colType","has","indexes","name","target","release","d","rows","Set","map","r","scope","placeholder","COLUMN_NAME","attributeColumnFor","attributeOperand","normalizeAttributeRows","sqlComparator","Brackets","In","IsNull","LessThanOrEqual","Like","TypeOrmStateStore","dataSource","ensureSchema","ensureTypeOrmDurableSchema","runs","getRepository","WorkflowRunEntity","checkpoints","StepCheckpointEntity","waiters","SignalWaiterEntity","buffered","BufferedSignalEntity","bufferedEvents","BufferedEventEntity","attributes","RunAttributeEntity","reindexAttributes","runId","em","repo","delete","rows","normalizeAttributeRows","length","insert","createRun","run","save","toRunEntity","id","searchAttributes","updateRun","patch","update","workflow","workflowVersion","status","input","output","error","wakeAt","Date","lockedBy","lockedUntil","awaitingDecisionTaskId","recoveryAttempts","tags","priority","createdAt","updatedAt","result","createQueryBuilder","set","where","execute","affected","Error","getRun","e","findOneBy","fromRunEntity","deleteRun","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","entity","toCheckpointEntity","upsert","transaction","work","raw","cp","listIncompleteRuns","findBy","In","map","listPendingRuns","limit","find","order","take","listDueTimers","nowMs","LessThanOrEqual","tryLockRun","owner","leaseUntilMs","andWhere","Brackets","qb","IsNull","orWhere","releaseRunLock","renewRunLock","listRuns","query","statuses","tag","tagPattern","forEach","f","i","applyAttributeExists","orderBy","offset","skip","getMany","valueProperty","attributeColumnFor","cmp","sqlComparator","op","keyParam","valParam","resolve","durableColumnResolver","q","idQuote","wrap","name","runIdCol","keyCol","valueCol","runPkCol","table","a","sql","key","attributeOperand","type","String","options","listCheckpoints","getLatestCheckpointByName","findOne","undefined","listCheckpointsByNamePrefix","prefixes","p","Like","putSignalWaiter","waiter","token","parallelGroup","listSignalWaiters","prefix","takeSignalWaiter","removeSignalWaiter","bufferSignal","payload","takeBufferedSignal","bufferEvent","publishedAt","listBufferedEvents","getTime","removeBufferedEvent","kind","stepId","events","attempts","workerGroup","enqueuedAt","startedAt","finishedAt"]}
|
|
1
|
+
{"version":3,"sources":["../src/entities.ts","../src/schema.ts","../src/typeorm-state-store.ts"],"sourcesContent":["import type { RunStatus, StepKind } from '@dudousxd/nestjs-durable-core';\nimport { EntitySchema, type ValueTransformer } from 'typeorm';\n\n/**\n * Safely deserialize a JSON-text column back to an object on READ.\n *\n * A column truncated by the old MySQL `text` 64KB limit (or otherwise corrupt) makes `JSON.parse`\n * throw \"Unterminated string in JSON\" — which, without this guard, would 500 the *entire* run-detail\n * read just because one blob is damaged. Instead, log once and degrade the bad field to `undefined`\n * so the rest of the run still renders. Valid JSON is unchanged.\n */\nexport function parseJsonColumn<T = unknown>(raw: unknown, field: string): T | undefined {\n if (raw == null) return undefined;\n // Some drivers (e.g. a native `json` column) already hand back a parsed object.\n if (typeof raw !== 'string') return raw as T;\n try {\n return JSON.parse(raw) as T;\n } catch (err) {\n console.warn(\n `durable store: failed to parse ${field} column, degrading to undefined: ${\n (err as Error).message\n }`,\n );\n return undefined;\n }\n}\n\n/**\n * TypeORM transformer for the free-form JSON-blob columns. Mirrors the built-in `simple-json`\n * serialization on WRITE (`JSON.stringify`, SQL NULL stays NULL) but uses {@link parseJsonColumn}\n * on READ so a single corrupt/truncated row degrades to `undefined` instead of throwing.\n */\nfunction jsonColumnTransformer(field: string): ValueTransformer {\n return {\n to: (value: unknown) => (value == null ? null : JSON.stringify(value)),\n from: (value: unknown) => parseJsonColumn(value, field),\n };\n}\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 MikroORM,\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\n/** Resolve a `DurableColumnNaming` to a `(property) => columnName` function. */\nexport function 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 `getRepository`/`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 | null;\n lockedUntil?: Date;\n awaitingDecisionTaskId?: string | null;\n recoveryAttempts?: number;\n tags?: string[] | null;\n searchAttributes?: Record<string, string | number | boolean> | null;\n priority?: number | null;\n origin?: string | 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 | null;\n parallelGroup?: string | null;\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/** A published event that matched no live waiter (see `engine.publishEvent`'s buffering). Keyed by a\n * caller-minted `id` (a uuid, not autoincrement — `removeBufferedEvent` targets it directly), not by\n * `runId`: events are name-based pub/sub, not tied to any one run. */\nexport class BufferedEventEntity {\n id!: string;\n name!: string;\n payload?: unknown;\n publishedAt!: Date;\n}\n\n/**\n * Build the durable TypeORM entity schemas with column names pinned per `naming` (default\n * `'snake_case'`). Register the result in your `DataSource`/`TypeOrmModule.forFeature`. The JSON-blob\n * columns keep the `jsonColumnTransformer` (corruption-tolerant read), the primary keys / auto-\n * increment / nullability are preserved, and the `id` of `durable_buffered_signals` stays a driver-\n * native auto-increment column.\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 name: 'WorkflowRunEntity',\n target: WorkflowRunEntity,\n tableName: 'durable_workflow_runs',\n columns: {\n id: { type: 'text', primary: true, name: col('id') },\n workflow: { type: 'text', name: col('workflow') },\n workflowVersion: { type: 'text', name: col('workflowVersion') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('runs.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('runs.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('runs.error'),\n },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n lockedBy: { type: 'text', nullable: true, name: col('lockedBy') },\n lockedUntil: { type: Date, nullable: true, name: col('lockedUntil') },\n // REMOTE turn the engine suspended on awaiting a decision; matched by completeRemoteDecision so\n // only the currently-awaited turn's decision is applied. Nullable: cleared when a decision lands.\n awaitingDecisionTaskId: {\n type: 'text',\n nullable: true,\n name: col('awaitingDecisionTaskId'),\n },\n recoveryAttempts: { type: 'integer', nullable: true, name: col('recoveryAttempts') },\n tags: {\n type: 'text',\n nullable: true,\n name: col('tags'),\n transformer: jsonColumnTransformer('runs.tags'),\n },\n searchAttributes: {\n type: 'text',\n nullable: true,\n name: col('searchAttributes'),\n transformer: jsonColumnTransformer('runs.searchAttributes'),\n },\n priority: { type: 'integer', nullable: true, name: col('priority') },\n // Which library/module registered the workflow this run belongs to (e.g.\n // `@dudousxd/nestjs-catalog-pipeline`), derived at registration time — never caller input.\n // Nullable with NO default: an origin cannot be reconstructed for a row written before this\n // column existed, so it stays NULL and reads back as `undefined` = \"unknown\". Defaulting it to\n // a real-looking string would be indistinguishable from a genuine registration in the\n // dashboard's facet. `ensureTypeOrmDurableSchema` back-fills the COLUMN onto pre-existing\n // tables (see the `additive` map in ./schema), never its value.\n origin: { type: 'text', nullable: true, name: col('origin') },\n createdAt: { type: Date, name: col('createdAt') },\n updatedAt: { type: Date, name: col('updatedAt') },\n },\n });\n\n const stepCheckpoints = new EntitySchema<StepCheckpointEntity>({\n name: 'StepCheckpointEntity',\n target: StepCheckpointEntity,\n tableName: 'durable_step_checkpoints',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n seq: { type: 'integer', primary: true, name: col('seq') },\n name: { type: 'text', name: col('name') },\n kind: { type: 'text', name: col('kind') },\n stepId: { type: 'text', name: col('stepId') },\n status: { type: 'text', name: col('status') },\n input: {\n type: 'text',\n nullable: true,\n name: col('input'),\n transformer: jsonColumnTransformer('checkpoints.input'),\n },\n output: {\n type: 'text',\n nullable: true,\n name: col('output'),\n transformer: jsonColumnTransformer('checkpoints.output'),\n },\n error: {\n type: 'text',\n nullable: true,\n name: col('error'),\n transformer: jsonColumnTransformer('checkpoints.error'),\n },\n events: {\n type: 'text',\n nullable: true,\n name: col('events'),\n transformer: jsonColumnTransformer('checkpoints.events'),\n },\n attempts: { type: 'integer', name: col('attempts') },\n workerGroup: { type: 'text', nullable: true, name: col('workerGroup') },\n // A ctx.gather/ctx.all fan tags every sibling step with the same group so the dashboard renders\n // them as one parallel group; the core engine sets it (incl. from a remote worker's recordStep).\n parallelGroup: { type: 'text', nullable: true, name: col('parallelGroup') },\n wakeAt: { type: Date, nullable: true, name: col('wakeAt') },\n enqueuedAt: { type: Date, nullable: true, name: col('enqueuedAt') },\n startedAt: { type: Date, name: col('startedAt') },\n finishedAt: { type: Date, name: col('finishedAt') },\n },\n });\n\n const runAttributes = new EntitySchema<RunAttributeEntity>({\n name: 'RunAttributeEntity',\n target: RunAttributeEntity,\n tableName: 'durable_run_attributes',\n columns: {\n runId: { type: 'text', primary: true, name: col('runId') },\n key: { type: 'text', primary: true, name: col('key') },\n strValue: { type: 'text', nullable: true, name: col('strValue') },\n // `float`/`double` portable across SQLite/MySQL/Postgres for numeric range scans.\n numValue: { type: 'float', nullable: true, name: col('numValue') },\n },\n });\n\n const signalWaiters = new EntitySchema<SignalWaiterEntity>({\n name: 'SignalWaiterEntity',\n target: SignalWaiterEntity,\n tableName: 'durable_signal_waiters',\n columns: {\n token: { type: 'text', primary: true, name: col('token') },\n runId: { type: 'text', name: col('runId') },\n seq: { type: 'integer', name: 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: 'text', nullable: true, name: col('parallelGroup') },\n },\n });\n\n const bufferedSignals = new EntitySchema<BufferedSignalEntity>({\n name: 'BufferedSignalEntity',\n target: BufferedSignalEntity,\n tableName: 'durable_buffered_signals',\n columns: {\n // Auto-increment PK for FIFO ordering. Left as TypeORM's default increment generation so it\n // maps to the driver-native auto-increment column (INTEGER on SQLite, BIGINT/SERIAL elsewhere).\n id: { type: Number, primary: true, generated: 'increment', name: col('id') },\n token: { type: 'text', name: col('token') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.payload'),\n },\n },\n });\n\n const bufferedEvents = new EntitySchema<BufferedEventEntity>({\n name: 'BufferedEventEntity',\n target: BufferedEventEntity,\n tableName: 'durable_buffered_events',\n // No `indices` here (unlike MikroORM's EntitySchema, which does declare one): this store manages\n // its DDL — including the `(name, publishedAt)` index — via raw SQL in `schema.ts`'s\n // `ensureTypeOrmDurableSchema`, the same as every other durable_* table's indexes.\n columns: {\n // Caller-minted uuid (engine.publishEvent), not autoincrement — removeBufferedEvent targets it\n // directly and needs to be handed the SAME id it was buffered with.\n id: { type: 'text', primary: true, name: col('id') },\n name: { type: 'text', name: col('name') },\n payload: {\n type: 'text',\n nullable: true,\n name: col('payload'),\n transformer: jsonColumnTransformer('buffered.eventPayload'),\n },\n publishedAt: { type: Date, name: col('publishedAt') },\n },\n });\n\n return [\n workflowRuns,\n stepCheckpoints,\n runAttributes,\n signalWaiters,\n bufferedSignals,\n bufferedEvents,\n ];\n}\n\n/** Durable entity schemas with the canonical `'snake_case'` column names. */\nexport const ENTITIES = durableEntities();\n","import type { DataSource } from 'typeorm';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\n\n/** The JSON-blob columns per table (keyed by table name) that hold free-form payloads and so must\n * be the unbounded text type — `longtext` on MySQL, `text` elsewhere. Keyed by entity PROPERTY name;\n * the physical column is resolved from the DataSource metadata so it tracks the configured naming. */\nexport const JSON_BLOB_COLUMNS: Record<string, string[]> = {\n durable_workflow_runs: ['input', 'output', 'error', 'tags', 'searchAttributes'],\n durable_step_checkpoints: ['input', 'output', 'error', 'events'],\n // NOTE: durable_buffered_signals/durable_buffered_events intentionally omitted here, same as the\n // existing buffered-signals table — their `payload` column is created with the widened `txt` type\n // (see `${bufCol('payload')} ${txt}` / the new table's `${bevCol('payload')} ${txt}` below) directly\n // at CREATE TABLE time on a fresh install, so there's no pre-existing narrower column to widen.\n};\n\n/** Each durable table → its backing entity class, so a column resolver can look up the physical\n * column for any (table, property) from the DataSource metadata (which reflects the configured\n * `DurableColumnNaming`). Keeps the raw DDL/SQL in lock-step with the entity column mapping. */\nconst TABLE_TO_ENTITY = {\n durable_workflow_runs: WorkflowRunEntity,\n durable_step_checkpoints: StepCheckpointEntity,\n durable_run_attributes: RunAttributeEntity,\n durable_signal_waiters: SignalWaiterEntity,\n durable_buffered_signals: BufferedSignalEntity,\n durable_buffered_events: BufferedEventEntity,\n} as const;\n\n/**\n * Tables this store creates and manages at boot (autoSchema, see {@link ensureTypeOrmDurableSchema}).\n * Feed to your ORM's migration differ exclude/skipTables so it never tries to drop them.\n *\n * ```ts\n * // MikroORM-flavoured example (TypeORM has no built-in equivalent option); adapt to whichever\n * // migration tool generates your diffs — e.g. exclude these names from a typeorm-generated\n * // migration, or filter them out of `SELECT table_name FROM information_schema.tables` before\n * // diffing against your own entities.\n * const skipTables = new Set(durableManagedTables());\n * ```\n *\n * Derived from the same {@link TABLE_TO_ENTITY} map the raw-SQL heal keys its column resolution off\n * — one list, so a consumer's denylist can never drift from what this store actually owns.\n */\nexport function durableManagedTables(): string[] {\n return Object.keys(TABLE_TO_ENTITY);\n}\n\n/**\n * Resolve `(table, property) => physical column name` from the DataSource's entity metadata. The\n * column names are NOT hardcoded here: they come from whatever the registered `durableEntities`\n * mapped (canonical `snake_case` by default, `'preserve'` for the legacy camelCase, or a custom\n * mapping), so the raw `CREATE TABLE`/index/EXISTS SQL always matches the entities and can never\n * silently diverge.\n */\nexport function durableColumnResolver(\n dataSource: DataSource,\n): (table: string, property: string) => string {\n return (table, property) => {\n const entity = TABLE_TO_ENTITY[table as keyof typeof TABLE_TO_ENTITY];\n if (!entity) throw new Error(`durable store: unknown table \"${table}\"`);\n const meta = dataSource.getMetadata(entity);\n const column = meta.findColumnWithPropertyName(property);\n if (!column) {\n throw new Error(`durable store: no column for ${table}.${property} in entity metadata`);\n }\n return column.databaseName;\n };\n}\n\n/** The SQL type for free-form JSON-blob columns. MySQL `text` caps at 64KB and silently truncates,\n * so use `longtext` (4GB) there; Postgres/SQLite `text` is already unbounded. */\nexport function jsonBlobColumnType(isMysql: boolean): 'longtext' | 'text' {\n return isMysql ? 'longtext' : 'text';\n}\n\n/** Idempotent `MODIFY COLUMN ... longtext` statements that widen pre-existing MySQL `text` JSON-blob\n * columns. Returns `[]` on non-MySQL dialects (their `text` is already unbounded — nothing to do).\n * `quote` quotes an identifier per the active driver; `col` resolves a property to its physical\n * column name so the widen tracks the configured naming. */\nexport function buildWidenStatements(\n isMysql: boolean,\n quote: (id: string) => string,\n col: (table: string, property: string) => string,\n): string[] {\n if (!isMysql) return [];\n const out: string[] = [];\n for (const [table, props] of Object.entries(JSON_BLOB_COLUMNS)) {\n for (const prop of props) {\n out.push(`ALTER TABLE ${quote(table)} MODIFY COLUMN ${quote(col(table, prop))} longtext`);\n }\n }\n return out;\n}\n\n/**\n * Idempotently create the durable tables. Safe to run on every boot (auto-schema) and the\n * exact function to call from your own TypeORM migration when you disable auto-schema:\n *\n * ```ts\n * export class AddDurableTables implements MigrationInterface {\n * async up(q: QueryRunner) { await ensureTypeOrmDurableSchema(q.connection); }\n * }\n * ```\n *\n * Dialect-aware (MySQL / MariaDB / Postgres / SQLite): identifiers are quoted per driver, keyed\n * string columns are `varchar` (MySQL can't key a `text` column), and the index is best-effort.\n * Only ever adds the three durable tables — it never touches your other tables.\n */\nexport async function ensureTypeOrmDurableSchema(dataSource: DataSource): Promise<void> {\n const q = (id: string) => dataSource.driver.escape(id);\n // Physical column names come from the entity metadata (canonical snake_case by default), so this\n // raw DDL always matches the registered `durableEntities` instead of hardcoding camelCase.\n const resolve = durableColumnResolver(dataSource);\n const type = String(dataSource.options.type);\n const isPg = type === 'postgres' || type === 'aurora-postgres';\n const isMysql = type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql';\n const isSqlite = type === 'sqlite' || type === 'better-sqlite3' || type === 'expo';\n\n // Keyed/short strings must be varchar on MySQL (a `text` PK/index needs a key length); `text`\n // for the free-form JSON payloads. Dates use the dialect's native timestamp type.\n const str = 'varchar(191)';\n // Free-form JSON payloads (`input`/`output`/`error`/`events`/`tags`/`searchAttributes`). MySQL's\n // `text` caps at 64KB and *silently truncates* — a large fan-out step's `events` then fails to\n // parse on read (\"Unterminated string in JSON\"). Use `longtext` (4GB) on MySQL; Postgres `text`\n // is already unbounded and SQLite `text` has no length limit.\n const txt = jsonBlobColumnType(isMysql);\n const int = isMysql ? 'int' : 'integer';\n const ts = isPg ? 'timestamptz' : 'datetime';\n\n const runs = q('durable_workflow_runs');\n const checkpoints = q('durable_step_checkpoints');\n const runAttributes = q('durable_run_attributes');\n const waiters = q('durable_signal_waiters');\n const buffered = q('durable_buffered_signals');\n const bufferedEvents = q('durable_buffered_events');\n // Per-table quoted-column helpers: resolve an entity property to its physical column then quote it.\n const runsCol = (property: string) => q(resolve('durable_workflow_runs', property));\n const cpCol = (property: string) => q(resolve('durable_step_checkpoints', property));\n const attrCol = (property: string) => q(resolve('durable_run_attributes', property));\n const waiterCol = (property: string) => q(resolve('durable_signal_waiters', property));\n const bufCol = (property: string) => q(resolve('durable_buffered_signals', property));\n const bevCol = (property: string) => q(resolve('durable_buffered_events', property));\n // Numeric side-table column for attribute range scans. `double precision` on Postgres, `double` on\n // MySQL, `real` on SQLite (all hold JS numbers without precision loss for the typical attribute).\n const num = isPg ? 'double precision' : isMysql ? 'double' : 'real';\n\n // Auto-increment PK syntax differs per dialect: SQLite wants `INTEGER PRIMARY KEY AUTOINCREMENT`,\n // MySQL `BIGINT AUTO_INCREMENT PRIMARY KEY`, Postgres `BIGSERIAL PRIMARY KEY`.\n const bufferedId = isSqlite\n ? `${bufCol('id')} integer PRIMARY KEY AUTOINCREMENT`\n : isMysql\n ? `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`\n : isPg\n ? `${bufCol('id')} bigserial PRIMARY KEY`\n : `${bufCol('id')} bigint NOT NULL AUTO_INCREMENT PRIMARY KEY`;\n\n const tables = [\n `CREATE TABLE IF NOT EXISTS ${runs} (\n ${runsCol('id')} ${str} PRIMARY KEY,\n ${runsCol('workflow')} ${str} NOT NULL,\n ${runsCol('workflowVersion')} ${str} NOT NULL,\n ${runsCol('status')} ${str} NOT NULL,\n ${runsCol('input')} ${txt}, ${runsCol('output')} ${txt}, ${runsCol('error')} ${txt},\n ${runsCol('wakeAt')} ${ts}, ${runsCol('lockedBy')} ${str}, ${runsCol('lockedUntil')} ${ts},\n ${runsCol('awaitingDecisionTaskId')} ${str},\n ${runsCol('recoveryAttempts')} ${int}, ${runsCol('tags')} ${txt}, ${runsCol('searchAttributes')} ${txt},\n ${runsCol('priority')} ${int}, ${runsCol('origin')} ${str},\n ${runsCol('createdAt')} ${ts} NOT NULL, ${runsCol('updatedAt')} ${ts} NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS ${checkpoints} (\n ${cpCol('runId')} ${str} NOT NULL, ${cpCol('seq')} ${int} NOT NULL,\n ${cpCol('name')} ${str} NOT NULL, ${cpCol('kind')} ${str} NOT NULL, ${cpCol('stepId')} ${str} NOT NULL,\n ${cpCol('status')} ${str} NOT NULL, ${cpCol('input')} ${txt}, ${cpCol('output')} ${txt}, ${cpCol('error')} ${txt}, ${cpCol('events')} ${txt},\n ${cpCol('attempts')} ${int} NOT NULL, ${cpCol('workerGroup')} ${str}, ${cpCol('parallelGroup')} ${str},\n ${cpCol('wakeAt')} ${ts}, ${cpCol('enqueuedAt')} ${ts},\n ${cpCol('startedAt')} ${ts} NOT NULL, ${cpCol('finishedAt')} ${ts} NOT NULL,\n PRIMARY KEY (${cpCol('runId')}, ${cpCol('seq')})\n )`,\n // Normalized search-attribute side-table: one row per (run, key) so attribute predicates push\n // DOWN into SQL via an EXISTS join (indexed below). `key` is keyed → must be varchar on MySQL.\n `CREATE TABLE IF NOT EXISTS ${runAttributes} (\n ${attrCol('runId')} ${str} NOT NULL, ${attrCol('key')} ${str} NOT NULL,\n ${attrCol('strValue')} ${str}, ${attrCol('numValue')} ${num},\n PRIMARY KEY (${attrCol('runId')}, ${attrCol('key')})\n )`,\n `CREATE TABLE IF NOT EXISTS ${waiters} (\n ${waiterCol('token')} ${str} PRIMARY KEY, ${waiterCol('runId')} ${str} NOT NULL, ${waiterCol('seq')} ${int} NOT NULL,\n ${waiterCol('parallelGroup')} ${str}\n )`,\n `CREATE TABLE IF NOT EXISTS ${buffered} (\n ${bufferedId}, ${bufCol('token')} ${str} NOT NULL, ${bufCol('payload')} ${txt}\n )`,\n // Caller-minted uuid PK (engine.publishEvent), not auto-increment — unlike durable_buffered_signals\n // above, removeBufferedEvent needs to target the EXACT id it was buffered with.\n `CREATE TABLE IF NOT EXISTS ${bufferedEvents} (\n ${bevCol('id')} ${str} PRIMARY KEY, ${bevCol('name')} ${str} NOT NULL,\n ${bevCol('payload')} ${txt}, ${bevCol('publishedAt')} ${ts} NOT NULL\n )`,\n ];\n\n // Additive nullable columns gained across versions (e.g. `input`, `events`, `enqueuedAt`). On a\n // table that predates them, `CREATE TABLE IF NOT EXISTS` above is a no-op, so back-fill the ones\n // that are actually missing — the auto-schema self-heals instead of needing a manual migration.\n // We check the live columns first (rather than ALTER-and-ignore) so a *real* ALTER failure\n // surfaces instead of being swallowed as a presumed \"column already exists\".\n // Keyed by entity PROPERTY name; the physical column is resolved per-table so the ADD COLUMN\n // tracks the configured naming.\n const additive: Record<string, Array<[string, string]>> = {\n durable_workflow_runs: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['awaitingDecisionTaskId', str],\n ['recoveryAttempts', int],\n ['tags', txt],\n ['searchAttributes', txt],\n ['priority', int],\n // Nullable and NOT back-filled with a value: every row that predates the column keeps a NULL\n // origin, which the store reads as `undefined` = \"unknown origin\". There is no honest value to\n // back-fill — the origin is derived from the registration that created the run, which is long\n // gone by the time this ALTER runs.\n ['origin', str],\n ],\n durable_step_checkpoints: [\n ['input', txt],\n ['output', txt],\n ['error', txt],\n ['events', txt],\n ['workerGroup', str],\n ['parallelGroup', str],\n ['enqueuedAt', ts],\n ],\n // Back-fill the child-await fan group onto signal-waiter tables that predate it (a remote\n // `gather_children` fan-out threads its group here so the resolved `signal:child:` checkpoint carries it).\n durable_signal_waiters: [['parallelGroup', str]],\n };\n\n const runner = dataSource.createQueryRunner();\n try {\n for (const sql of tables) await runner.query(sql);\n for (const [table, props] of Object.entries(additive)) {\n const have = await existingColumns(runner, table, { isSqlite, isMysql });\n for (const [property, colType] of props) {\n const column = resolve(table, property);\n if (!have.has(column)) {\n await runner.query(`ALTER TABLE ${q(table)} ADD COLUMN ${q(column)} ${colType}`);\n }\n }\n }\n // Widen pre-existing MySQL `text` JSON-blob columns to `longtext`. A table created before this\n // fix has these as `text` (64KB) and silently truncates large `events`/`output` payloads. MODIFY\n // to `longtext` is idempotent (no-op if already longtext), so it's safe to run on every boot.\n // MySQL-only: Postgres/SQLite `text` is already unbounded, so there's nothing to widen.\n for (const sql of buildWidenStatements(isMysql, q, resolve)) {\n try {\n await runner.query(sql);\n } catch {\n /* column already longtext, or table/column not present on this deploy */\n }\n }\n // MySQL has no `CREATE INDEX IF NOT EXISTS`; create each best-effort and ignore \"already exists\".\n // - status/wakeAt: the timer poller (listDueTimers) and recovery (listIncompleteRuns) scans.\n // - workflow/status: the dashboard's listRuns filters. (Checkpoints are keyed by the PK prefix\n // `(runId, seq)`, so listCheckpoints/getEvent already hit an index — no extra one needed.)\n const indexes: Array<[string, string]> = [\n ['durable_runs_status_idx', `${runs} (${runsCol('status')}, ${runsCol('wakeAt')})`],\n [\n 'durable_runs_workflow_status_idx',\n `${runs} (${runsCol('workflow')}, ${runsCol('status')})`,\n ],\n // buffered signals are taken FIFO per token (smallest id) — index the token for the scan.\n ['durable_buffered_signals_token_idx', `${buffered} (${bufCol('token')})`],\n // buffered events are scanned oldest-first per name — one composite index serves both.\n [\n 'durable_buffered_events_name_published_at_idx',\n `${bufferedEvents} (${bevCol('name')}, ${bevCol('publishedAt')})`,\n ],\n // Search-attribute pushdown: equality + range predicates probe by (key, value). Two composite\n // indexes — one per typed column — so a numeric range or a string equality is an index scan.\n [\n 'durable_run_attributes_num_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('numValue')})`,\n ],\n [\n 'durable_run_attributes_str_idx',\n `${runAttributes} (${attrCol('key')}, ${attrCol('strValue')})`,\n ],\n ];\n for (const [name, target] of indexes) {\n try {\n await runner.query(`CREATE INDEX ${q(name)} ON ${target}`);\n } catch {\n /* index already exists */\n }\n }\n } finally {\n await runner.release();\n }\n}\n\n/** The columns that currently exist on `table`, so the schema can add only the missing ones\n * (instead of ALTER-and-swallow, which would also hide a genuine ALTER failure). */\nasync function existingColumns(\n runner: { query: (sql: string, params?: unknown[]) => Promise<unknown> },\n table: string,\n d: { isSqlite: boolean; isMysql: boolean },\n): Promise<Set<string>> {\n if (d.isSqlite) {\n const rows = (await runner.query(`PRAGMA table_info(\"${table}\")`)) as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n }\n // Postgres + MySQL both expose information_schema; scope to the connection's own schema/db.\n // Bind-param placeholder differs: Postgres uses `$1`, MySQL uses `?` (TypeORM only rewrites `?`\n // inside the query builder, not for these raw `runner.query` calls), so pick per dialect.\n const scope = d.isMysql ? 'TABLE_SCHEMA = DATABASE()' : 'table_schema = current_schema()';\n const placeholder = d.isMysql ? '?' : '$1';\n const rows = (await runner.query(\n `SELECT column_name AS name FROM information_schema.columns WHERE table_name = ${placeholder} AND ${scope}`,\n [table],\n )) as Array<{ name?: string; COLUMN_NAME?: string }>;\n return new Set(rows.map((r) => (r.name ?? r.COLUMN_NAME) as string));\n}\n","import {\n type AttributeFilter,\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 sqlComparator,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n Brackets,\n type DataSource,\n type EntityManager,\n In,\n IsNull,\n LessThanOrEqual,\n Like,\n type SelectQueryBuilder,\n} from 'typeorm';\nimport type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';\nimport {\n BufferedEventEntity,\n BufferedSignalEntity,\n RunAttributeEntity,\n SignalWaiterEntity,\n StepCheckpointEntity,\n WorkflowRunEntity,\n} from './entities';\nimport { durableColumnResolver, ensureTypeOrmDurableSchema } from './schema';\n\n/**\n * TypeORM-backed `StateStore`. Works on any TypeORM driver — Postgres, MySQL, SQLite (tested);\n * timestamps use native datetime columns and `wakeAt` is stored as a datetime too.\n */\nexport class TypeOrmStateStore implements StateStore {\n constructor(private readonly dataSource: DataSource) {}\n\n async ensureSchema(): Promise<void> {\n await ensureTypeOrmDurableSchema(this.dataSource);\n }\n\n private runs() {\n return this.dataSource.getRepository(WorkflowRunEntity);\n }\n private checkpoints() {\n return this.dataSource.getRepository(StepCheckpointEntity);\n }\n private waiters() {\n return this.dataSource.getRepository(SignalWaiterEntity);\n }\n private buffered() {\n return this.dataSource.getRepository(BufferedSignalEntity);\n }\n private bufferedEvents() {\n return this.dataSource.getRepository(BufferedEventEntity);\n }\n private attributes() {\n return this.dataSource.getRepository(RunAttributeEntity);\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 em?: EntityManager,\n ): Promise<void> {\n const repo = em ? em.getRepository(RunAttributeEntity) : this.attributes();\n await repo.delete({ runId });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await repo.insert(rows);\n }\n\n async createRun(run: WorkflowRun): Promise<void> {\n await this.runs().save(toRunEntity(run));\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n // Single UPDATE (no pre-SELECT + save round-trips): map only the patched fields. The query\n // builder still applies the entities' JSON column transformers to the `set` values (as in\n // listRuns/tryLockRun). Preserve the not-found throw any caller may rely on.\n const update: Record<string, unknown> = {};\n if ('workflow' in patch) update.workflow = patch.workflow;\n if ('workflowVersion' in patch) update.workflowVersion = patch.workflowVersion;\n if ('status' in patch) update.status = patch.status;\n if ('input' in patch) update.input = patch.input ?? null;\n if ('output' in patch) update.output = patch.output ?? null;\n if ('error' in patch) update.error = patch.error ?? null;\n if ('wakeAt' in patch) update.wakeAt = patch.wakeAt == null ? null : new Date(patch.wakeAt);\n if ('lockedBy' in patch) update.lockedBy = patch.lockedBy ?? null;\n if ('lockedUntil' in patch)\n update.lockedUntil = patch.lockedUntil == null ? null : new Date(patch.lockedUntil);\n if ('awaitingDecisionTaskId' in patch)\n update.awaitingDecisionTaskId = patch.awaitingDecisionTaskId ?? null;\n if ('recoveryAttempts' in patch) update.recoveryAttempts = patch.recoveryAttempts;\n if ('tags' in patch) update.tags = patch.tags ?? null;\n if ('searchAttributes' in patch) update.searchAttributes = patch.searchAttributes ?? null;\n if ('priority' in patch) update.priority = patch.priority ?? null;\n if ('origin' in patch) update.origin = patch.origin ?? null;\n if ('createdAt' in patch) update.createdAt = patch.createdAt;\n if ('updatedAt' in patch) update.updatedAt = patch.updatedAt;\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set(update)\n .where({ id: runId })\n .execute();\n if (!result.affected) throw new Error(`run ${runId} not found`);\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 async getRun(runId: string): Promise<WorkflowRun | null> {\n const e = await this.runs().findOneBy({ id: runId });\n return e ? fromRunEntity(e) : null;\n }\n\n async deleteRun(runId: string): Promise<void> {\n // Child rows first, then the run — checkpoints, signal waiters, attribute rows.\n await this.checkpoints().delete({ runId });\n await this.waiters().delete({ runId });\n await this.attributes().delete({ runId });\n await this.runs().delete({ id: runId });\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const e = await this.checkpoints().findOneBy({ runId, seq });\n return e ? fromCheckpointEntity(e) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n // upsert = INSERT ... ON CONFLICT DO UPDATE on the (runId, seq) key — drops the pre-SELECT that\n // `.save()` does on each write. JSON column transformers still apply for upsert.\n const entity = toCheckpointEntity(checkpoint) as QueryDeepPartialEntity<StepCheckpointEntity>;\n await this.checkpoints().upsert(entity, ['runId', 'seq']);\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.dataSource.transaction(async (em) =>\n work({\n raw: em,\n saveCheckpoint: async (cp) => {\n await em.getRepository(StepCheckpointEntity).save(toCheckpointEntity(cp));\n },\n }),\n );\n }\n\n async listIncompleteRuns(): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({ status: In(['running', 'cancelling']) });\n return rows.map(fromRunEntity);\n }\n\n async listPendingRuns(limit: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().find({\n where: { status: 'pending' },\n order: { createdAt: 'ASC' }, // FIFO dispatch\n take: limit,\n });\n return rows.map(fromRunEntity);\n }\n\n async listDueTimers(nowMs: number): Promise<WorkflowRun[]> {\n const rows = await this.runs().findBy({\n status: 'suspended',\n wakeAt: LessThanOrEqual(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 result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedBy: owner, lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId })\n .andWhere(\n new Brackets((qb) =>\n qb\n .where({ lockedUntil: IsNull() })\n .orWhere({ lockedUntil: LessThanOrEqual(new Date(nowMs)) }),\n ),\n )\n .execute();\n return result.affected === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n await this.runs().update({ id: runId }, { lockedBy: null, lockedUntil: () => 'NULL' });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const result = await this.runs()\n .createQueryBuilder()\n .update()\n .set({ lockedUntil: new Date(leaseUntilMs) })\n .where({ id: runId, lockedBy: owner })\n .execute();\n return result.affected === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n // `tags` carries a custom JSON transformer, and TypeORM applies a column transformer's `to()` to\n // FindOperator values too — so a `Like('%\"etl\"%')` in a plain `where` would be JSON-stringified\n // and corrupt the LIKE pattern. Use the query builder with a raw parameter to bypass that.\n const qb = this.runs().createQueryBuilder('r');\n if (query.workflow) qb.andWhere('r.workflow = :workflow', { workflow: query.workflow });\n // Which library registered the workflow. Plain equality, so a run whose origin is NULL (created\n // before the column existed, or registered through a path the derivation could not classify)\n // matches NO origin value — it is never folded into a bucket to make the facet look complete.\n // Unknown-origin runs are reachable only with the filter OFF, so \"all origins\" must stay the\n // default view; a dashboard that filtered by default would make those runs look deleted.\n if (query.origin !== undefined) qb.andWhere('r.origin = :origin', { origin: query.origin });\n if (query.status) qb.andWhere('r.status = :status', { status: query.status });\n if (query.statuses)\n qb.andWhere(\n query.statuses.length ? 'r.status IN (:...statuses)' : '1 = 0',\n query.statuses.length ? { statuses: query.statuses } : {},\n );\n // `tags` is a JSON-text column; match the quoted token so `etl` doesn't match `etl-foo`. NOTE: a\n // leading-wildcard LIKE on JSON text is NOT index-friendly (no B-tree index can serve `%\"x\"%`), so\n // tag-filtered scans (e.g. singleton admission) are sequential over `durable_runs_workflow_status_idx`'s\n // result set. The `statuses`/`workflow` predicates above bound that scan; if tag scans ever dominate,\n // promote tags to a normalized join table or a JSON/GIN index — a schema change, intentionally not done here.\n if (query.tag) qb.andWhere('r.tags LIKE :tagPattern', { tagPattern: `%\"${query.tag}\"%` });\n // Typed/range attribute predicates push DOWN into SQL: each filter becomes an EXISTS against the\n // normalized `durable_run_attributes` side-table (indexed on (key, numValue)/(key, strValue)), so\n // the DB does the filtering AND the LIMIT/OFFSET — no full scan + in-process filter. ANDed: a run\n // must satisfy every filter, so one EXISTS per filter.\n if (query.attributes?.length) {\n query.attributes.forEach((f, i) => this.applyAttributeExists(qb, f, i));\n }\n qb.orderBy('r.createdAt', 'DESC'); // newest first — recent runs on top in the dashboard\n if (query.limit != null) qb.take(query.limit);\n if (query.offset != null) qb.skip(query.offset);\n const rows = await qb.getMany();\n return rows.map(fromRunEntity);\n }\n\n /** Add one attribute predicate to `qb` as an EXISTS subquery on the side-table. `i` namespaces the\n * bind params so multiple ANDed filters don't collide. */\n private applyAttributeExists(\n qb: SelectQueryBuilder<WorkflowRunEntity>,\n f: AttributeFilter,\n i: number,\n ): void {\n const valueProperty = attributeColumnFor(f); // 'numValue' | 'strValue' (entity property)\n const cmp = sqlComparator(f.op);\n const keyParam = `attrKey${i}`;\n const valParam = `attrVal${i}`;\n // Resolve the PHYSICAL column names from the entity metadata so this raw subquery tracks the\n // configured naming (canonical snake_case by default) instead of hardcoding camelCase — which\n // would break the EXISTS pushdown the moment the table is snake_case.\n const resolve = durableColumnResolver(this.dataSource);\n const q = this.idQuote();\n const wrap = (name: string) => `${q}${name}${q}`;\n const runIdCol = wrap(resolve('durable_run_attributes', 'runId'));\n const keyCol = wrap(resolve('durable_run_attributes', 'key'));\n const valueCol = wrap(resolve('durable_run_attributes', valueProperty));\n const runPkCol = wrap(resolve('durable_workflow_runs', 'id'));\n const table = wrap('durable_run_attributes');\n // `<>` (ne) must also exclude rows where the attribute is absent: the missing-key-never-matches\n // contract (see core matchesAttributes). EXISTS already requires the key row to be present, and a\n // row only exists when the value is non-null, so EXISTS(... <> ...) gives exactly ne-with-present.\n const a = `a${i}`;\n const sql = `EXISTS (SELECT 1 FROM ${table} ${a} WHERE ${a}.${runIdCol} = r.${runPkCol} AND ${a}.${keyCol} = :${keyParam} AND ${a}.${valueCol} ${cmp} :${valParam})`;\n qb.andWhere(sql, {\n [keyParam]: f.key,\n [valParam]: attributeOperand(f),\n });\n }\n\n /** The identifier quote char for the active driver (MySQL backtick, others double-quote). */\n private idQuote(): string {\n const type = String(this.dataSource.options.type);\n return type === 'mysql' || type === 'mariadb' || type === 'aurora-mysql' ? '`' : '\"';\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const rows = await this.checkpoints().find({ where: { runId }, order: { seq: 'ASC' } });\n return rows.map(fromCheckpointEntity);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const e = await this.checkpoints().findOne({ where: { runId, name }, order: { seq: 'DESC' } });\n return e ? fromCheckpointEntity(e) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const rows = await this.checkpoints().find({\n where: prefixes.map((p) => ({ runId, name: Like(`${p}%`) })),\n order: { seq: 'ASC' },\n });\n return rows.map(fromCheckpointEntity);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n await this.waiters().save({\n token: waiter.token,\n runId: waiter.runId,\n seq: waiter.seq,\n parallelGroup: waiter.parallelGroup ?? null,\n });\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const rows = await this.waiters().find({ where: { 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 takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const e = await this.waiters().findOneBy({ token });\n if (!e) return null;\n const waiter: SignalWaiter = {\n token: e.token,\n runId: e.runId,\n seq: e.seq,\n parallelGroup: e.parallelGroup ?? undefined,\n };\n await this.waiters().delete({ token });\n return waiter;\n }\n\n async removeSignalWaiter(waiter: SignalWaiter): Promise<void> {\n // Exact-match delete (token AND runId AND seq) — a plain `delete({ token })` would remove\n // whatever row currently owns the token, even if a different run has since claimed it.\n await this.waiters().delete({ token: waiter.token, runId: waiter.runId, seq: waiter.seq });\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n await this.buffered().save({ token, payload: payload ?? null });\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const e = await this.buffered().findOne({ where: { token }, order: { id: 'ASC' } });\n if (!e) return null;\n await this.buffered().delete({ id: e.id });\n return { payload: e.payload ?? undefined };\n }\n\n async bufferEvent(input: {\n name: string;\n payload: unknown;\n id: string;\n publishedAt: number;\n }): Promise<void> {\n await this.bufferedEvents().save({\n id: input.id,\n name: input.name,\n payload: input.payload ?? null,\n publishedAt: new Date(input.publishedAt),\n });\n }\n\n async listBufferedEvents(\n name: string,\n limit: number,\n ): Promise<Array<{ id: string; payload: unknown; publishedAt: number }>> {\n const rows = await this.bufferedEvents().find({\n where: { name },\n order: { publishedAt: 'ASC' }, // oldest first\n take: limit,\n });\n return rows.map((e) => ({\n id: e.id,\n payload: e.payload ?? undefined,\n publishedAt: e.publishedAt.getTime(),\n }));\n }\n\n async removeBufferedEvent(id: string): Promise<boolean> {\n const result = await this.bufferedEvents().delete({ id });\n return result.affected === 1;\n }\n}\n\nfunction toRunEntity(run: WorkflowRun): WorkflowRunEntity {\n return {\n id: run.id,\n workflow: run.workflow,\n workflowVersion: run.workflowVersion,\n status: run.status,\n input: run.input ?? null,\n output: run.output ?? null,\n error: run.error ?? null,\n ...(run.wakeAt == null ? {} : { wakeAt: new Date(run.wakeAt) }),\n lockedBy: run.lockedBy ?? null,\n ...(run.lockedUntil == null ? {} : { lockedUntil: new Date(run.lockedUntil) }),\n awaitingDecisionTaskId: run.awaitingDecisionTaskId ?? null,\n ...(run.recoveryAttempts === undefined ? {} : { recoveryAttempts: run.recoveryAttempts }),\n tags: run.tags ?? null,\n searchAttributes: run.searchAttributes ?? null,\n priority: run.priority ?? null,\n // Absent origin stays SQL NULL — \"unknown\", never coerced into a real-looking library name.\n origin: run.origin ?? null,\n createdAt: run.createdAt,\n updatedAt: run.updatedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n lockedBy: e.lockedBy ?? undefined,\n lockedUntil: e.lockedUntil == null ? undefined : e.lockedUntil.getTime(),\n awaitingDecisionTaskId: e.awaitingDecisionTaskId ?? undefined,\n recoveryAttempts: e.recoveryAttempts ?? undefined,\n tags: e.tags ?? undefined,\n searchAttributes: e.searchAttributes ?? undefined,\n priority: e.priority ?? undefined,\n // NULL (a row written before the column existed) surfaces as `undefined` = unknown origin.\n origin: e.origin ?? undefined,\n createdAt: e.createdAt,\n updatedAt: e.updatedAt,\n };\n}\n\nfunction toCheckpointEntity(cp: StepCheckpoint): StepCheckpointEntity {\n return {\n runId: cp.runId,\n seq: cp.seq,\n name: cp.name,\n kind: cp.kind,\n stepId: cp.stepId,\n status: cp.status,\n input: cp.input ?? null,\n output: cp.output ?? null,\n error: cp.error ?? null,\n events: cp.events ?? null,\n attempts: cp.attempts,\n workerGroup: cp.workerGroup ?? null,\n parallelGroup: cp.parallelGroup ?? null,\n ...(cp.wakeAt == null ? {} : { wakeAt: new Date(cp.wakeAt) }),\n enqueuedAt: cp.enqueuedAt,\n startedAt: cp.startedAt,\n finishedAt: cp.finishedAt,\n };\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 == null ? undefined : e.wakeAt.getTime(),\n // Older rows predate enqueuedAt; treat the worker start as enqueue time (queue-wait reads zero).\n enqueuedAt: e.enqueuedAt ?? e.startedAt,\n startedAt: e.startedAt,\n finishedAt: e.finishedAt,\n };\n}\n"],"mappings":"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","names":["EntitySchema","parseJsonColumn","raw","field","undefined","JSON","parse","err","console","warn","message","jsonColumnTransformer","to","value","stringify","from","snakeCase","property","replace","c","toLowerCase","columnNamer","naming","WorkflowRunEntity","id","workflow","workflowVersion","status","input","output","error","wakeAt","lockedBy","lockedUntil","awaitingDecisionTaskId","recoveryAttempts","tags","searchAttributes","priority","origin","createdAt","updatedAt","StepCheckpointEntity","runId","seq","name","kind","stepId","events","attempts","workerGroup","parallelGroup","enqueuedAt","startedAt","finishedAt","RunAttributeEntity","key","strValue","numValue","SignalWaiterEntity","token","BufferedSignalEntity","payload","BufferedEventEntity","publishedAt","durableEntities","options","col","workflowRuns","EntitySchema","target","tableName","columns","type","primary","nullable","transformer","Date","stepCheckpoints","runAttributes","signalWaiters","bufferedSignals","Number","generated","bufferedEvents","ENTITIES","JSON_BLOB_COLUMNS","durable_workflow_runs","durable_step_checkpoints","TABLE_TO_ENTITY","WorkflowRunEntity","StepCheckpointEntity","durable_run_attributes","RunAttributeEntity","durable_signal_waiters","SignalWaiterEntity","durable_buffered_signals","BufferedSignalEntity","durable_buffered_events","BufferedEventEntity","durableManagedTables","Object","keys","durableColumnResolver","dataSource","table","property","entity","Error","meta","getMetadata","column","findColumnWithPropertyName","databaseName","jsonBlobColumnType","isMysql","buildWidenStatements","quote","col","out","props","entries","prop","push","ensureTypeOrmDurableSchema","q","id","driver","escape","resolve","type","String","options","isPg","isSqlite","str","txt","int","ts","runs","checkpoints","runAttributes","waiters","buffered","bufferedEvents","runsCol","cpCol","attrCol","waiterCol","bufCol","bevCol","num","bufferedId","tables","additive","runner","createQueryRunner","sql","query","have","existingColumns","colType","has","indexes","name","target","release","d","rows","Set","map","r","scope","placeholder","COLUMN_NAME","attributeColumnFor","attributeOperand","normalizeAttributeRows","sqlComparator","Brackets","In","IsNull","LessThanOrEqual","Like","TypeOrmStateStore","dataSource","ensureSchema","ensureTypeOrmDurableSchema","runs","getRepository","WorkflowRunEntity","checkpoints","StepCheckpointEntity","waiters","SignalWaiterEntity","buffered","BufferedSignalEntity","bufferedEvents","BufferedEventEntity","attributes","RunAttributeEntity","reindexAttributes","runId","em","repo","delete","rows","normalizeAttributeRows","length","insert","createRun","run","save","toRunEntity","id","searchAttributes","updateRun","patch","update","workflow","workflowVersion","status","input","output","error","wakeAt","Date","lockedBy","lockedUntil","awaitingDecisionTaskId","recoveryAttempts","tags","priority","origin","createdAt","updatedAt","result","createQueryBuilder","set","where","execute","affected","Error","getRun","e","findOneBy","fromRunEntity","deleteRun","getCheckpoint","seq","fromCheckpointEntity","saveCheckpoint","checkpoint","entity","toCheckpointEntity","upsert","transaction","work","raw","cp","listIncompleteRuns","findBy","In","map","listPendingRuns","limit","find","order","take","listDueTimers","nowMs","LessThanOrEqual","tryLockRun","owner","leaseUntilMs","andWhere","Brackets","qb","IsNull","orWhere","releaseRunLock","renewRunLock","listRuns","query","undefined","statuses","tag","tagPattern","forEach","f","i","applyAttributeExists","orderBy","offset","skip","getMany","valueProperty","attributeColumnFor","cmp","sqlComparator","op","keyParam","valParam","resolve","durableColumnResolver","q","idQuote","wrap","name","runIdCol","keyCol","valueCol","runPkCol","table","a","sql","key","attributeOperand","type","String","options","listCheckpoints","getLatestCheckpointByName","findOne","listCheckpointsByNamePrefix","prefixes","p","Like","putSignalWaiter","waiter","token","parallelGroup","listSignalWaiters","prefix","takeSignalWaiter","removeSignalWaiter","bufferSignal","payload","takeBufferedSignal","bufferEvent","publishedAt","listBufferedEvents","getTime","removeBufferedEvent","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-typeorm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "TypeORM adapter for the nestjs-durable StateStore",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Davide Carvalho",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"typeorm": "0.3.30",
|
|
30
30
|
"typescript": "5.9.3",
|
|
31
31
|
"tsup": "8.3.5",
|
|
32
|
-
"@dudousxd/nestjs-durable-core": "^0.
|
|
33
|
-
"@dudousxd/nestjs-durable-testing": "^0.
|
|
32
|
+
"@dudousxd/nestjs-durable-core": "^0.63.0",
|
|
33
|
+
"@dudousxd/nestjs-durable-testing": "^0.10.0"
|
|
34
34
|
},
|
|
35
35
|
"module": "./dist/index.js",
|
|
36
36
|
"exports": {
|