@pikku/kysely 0.12.17 → 0.12.18
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/CHANGELOG.md +25 -0
- package/dist/src/kysely-workflow-mirror.js +10 -0
- package/dist/src/kysely-workflow-run-service.js +2 -0
- package/dist/src/kysely-workflow-service.js +3 -3
- package/package.json +2 -2
- package/src/kysely-workflow-mirror.test.ts +23 -0
- package/src/kysely-workflow-mirror.ts +11 -0
- package/src/kysely-workflow-run-service.ts +2 -0
- package/src/kysely-workflow-service.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
## 0.12.18
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 5c0ff0f: Fix `getRunHistory` dropping step provenance (`fromStepName`). The value was persisted on the step row and used by the graph planner, but `getRunHistory` built its rows from the per-attempt history and never carried `fromStepName` through — so run history (and any timeline reconstructed from it) reported no predecessors. Redis and Kysely `getRunHistory` now return `fromStepName`. Also adds the missing `from_step_name` column (+ backfill) to the Kysely workflow mirror's `workflow_step` schema and persists it on mirror inserts, so a mirror-side history has identical provenance.
|
|
6
|
+
- 72694f6: feat(workflow): expose per-step attempt count + record running/succeeded/failed timestamps
|
|
7
|
+
|
|
8
|
+
`getRunStatus` now returns `attempts` (the latest attempt count) per step, so
|
|
9
|
+
consumers can show retry counts without a second history query. It already
|
|
10
|
+
computed `duration` from `runningAt`/`succeededAt`, but the kysely and mongodb
|
|
11
|
+
workflow stores only stamped those timestamps on the _insert_ path — the
|
|
12
|
+
`running` / `succeeded` / `failed` status transitions updated the history row's
|
|
13
|
+
status without setting `runningAt` / `succeededAt` / `failedAt`, so `duration`
|
|
14
|
+
was always undefined. The transitions now stamp the matching timestamp, so step
|
|
15
|
+
duration is populated for kysely- and mongodb-backed runs. (Redis already
|
|
16
|
+
stamped on transition.) A shared service-suite test guards both behaviours.
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [4be205f]
|
|
19
|
+
- Updated dependencies [061c717]
|
|
20
|
+
- Updated dependencies [2c55e13]
|
|
21
|
+
- Updated dependencies [c745c26]
|
|
22
|
+
- Updated dependencies [57900b5]
|
|
23
|
+
- Updated dependencies [72694f6]
|
|
24
|
+
- @pikku/core@0.12.39
|
|
25
|
+
|
|
1
26
|
## 0.12.17
|
|
2
27
|
|
|
3
28
|
### Patch Changes
|
|
@@ -61,6 +61,7 @@ export class KyselyWorkflowMirror {
|
|
|
61
61
|
.addColumn('branch_taken', 'text')
|
|
62
62
|
.addColumn('retries', 'integer')
|
|
63
63
|
.addColumn('retry_delay', 'text')
|
|
64
|
+
.addColumn('from_step_name', 'text')
|
|
64
65
|
.addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
65
66
|
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
66
67
|
.addUniqueConstraint('workflow_step_run_name_unique', [
|
|
@@ -68,6 +69,14 @@ export class KyselyWorkflowMirror {
|
|
|
68
69
|
'step_name',
|
|
69
70
|
])
|
|
70
71
|
.execute();
|
|
72
|
+
// Additive: backfill the column on mirror tables created before from_step_name.
|
|
73
|
+
await this.db.schema
|
|
74
|
+
.alterTable('workflow_step')
|
|
75
|
+
.addColumn('from_step_name', 'text')
|
|
76
|
+
.execute()
|
|
77
|
+
.catch(() => {
|
|
78
|
+
// Column already exists (fresh tables include it above) — nothing to do.
|
|
79
|
+
});
|
|
71
80
|
await this.db.schema
|
|
72
81
|
.createTable('workflow_step_history')
|
|
73
82
|
.ifNotExists()
|
|
@@ -144,6 +153,7 @@ export class KyselyWorkflowMirror {
|
|
|
144
153
|
status: step.status,
|
|
145
154
|
retries: step.retries ?? null,
|
|
146
155
|
retryDelay: step.retryDelay?.toString() ?? null,
|
|
156
|
+
fromStepName: step.fromStepName ?? null,
|
|
147
157
|
createdAt: now,
|
|
148
158
|
updatedAt: now,
|
|
149
159
|
})
|
|
@@ -137,6 +137,7 @@ export class KyselyWorkflowRunService {
|
|
|
137
137
|
.select([
|
|
138
138
|
's.workflowStepId',
|
|
139
139
|
's.stepName',
|
|
140
|
+
's.fromStepName',
|
|
140
141
|
's.retries',
|
|
141
142
|
's.retryDelay',
|
|
142
143
|
'h.status',
|
|
@@ -158,6 +159,7 @@ export class KyselyWorkflowRunService {
|
|
|
158
159
|
return {
|
|
159
160
|
stepId,
|
|
160
161
|
stepName: row.stepName,
|
|
162
|
+
fromStepName: row.fromStepName ?? undefined,
|
|
161
163
|
status: row.status,
|
|
162
164
|
result: parseJson(row.result),
|
|
163
165
|
error: parseJson(row.error),
|
|
@@ -231,7 +231,7 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
231
231
|
if (latestHistory) {
|
|
232
232
|
await this.db
|
|
233
233
|
.updateTable('workflowStepHistory')
|
|
234
|
-
.set({ status: 'running' })
|
|
234
|
+
.set({ status: 'running', runningAt: new Date() })
|
|
235
235
|
.where('historyId', '=', latestHistory.historyId)
|
|
236
236
|
.execute();
|
|
237
237
|
}
|
|
@@ -308,7 +308,7 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
308
308
|
if (latestHistory) {
|
|
309
309
|
await this.db
|
|
310
310
|
.updateTable('workflowStepHistory')
|
|
311
|
-
.set({ status: 'succeeded', result: resultJson })
|
|
311
|
+
.set({ status: 'succeeded', result: resultJson, succeededAt: new Date() })
|
|
312
312
|
.where('historyId', '=', latestHistory.historyId)
|
|
313
313
|
.execute();
|
|
314
314
|
}
|
|
@@ -340,7 +340,7 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
340
340
|
if (latestHistory) {
|
|
341
341
|
await this.db
|
|
342
342
|
.updateTable('workflowStepHistory')
|
|
343
|
-
.set({ status: 'failed', error: errorJson })
|
|
343
|
+
.set({ status: 'failed', error: errorJson, failedAt: new Date() })
|
|
344
344
|
.where('historyId', '=', latestHistory.historyId)
|
|
345
345
|
.execute();
|
|
346
346
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/kysely",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.18",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"prepublishOnly": "yarn build"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@pikku/core": "^0.12.
|
|
23
|
+
"@pikku/core": "^0.12.39"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"kysely": "^0.29.0"
|
|
@@ -103,6 +103,29 @@ describe('KyselyWorkflowMirror — step lifecycle', () => {
|
|
|
103
103
|
assert.equal(history[0]!.status, 'pending')
|
|
104
104
|
})
|
|
105
105
|
|
|
106
|
+
test('getRunHistory surfaces step provenance (fromStepName)', async () => {
|
|
107
|
+
// The mirror persists fromStepName and getRunHistory reads it back, so the
|
|
108
|
+
// run history reconstructs the walked path (entry has none, the next step
|
|
109
|
+
// records its predecessor).
|
|
110
|
+
await seedStep(STEP_ID, 'begin')
|
|
111
|
+
await mirror.insertStepState(RUN_ID, {
|
|
112
|
+
stepId: `${STEP_ID}-2`,
|
|
113
|
+
stepName: 'next',
|
|
114
|
+
rpcName: 'rpc.fn',
|
|
115
|
+
data: { x: 2 },
|
|
116
|
+
status: 'pending',
|
|
117
|
+
attemptCount: 1,
|
|
118
|
+
fromStepName: 'begin',
|
|
119
|
+
createdAt: new Date(),
|
|
120
|
+
updatedAt: new Date(),
|
|
121
|
+
} as any)
|
|
122
|
+
|
|
123
|
+
const history = await runService.getRunHistory(RUN_ID)
|
|
124
|
+
const from = new Map(history.map((h) => [h.stepName, h.fromStepName]))
|
|
125
|
+
assert.equal(from.get('begin'), undefined, 'entry step has no predecessor')
|
|
126
|
+
assert.equal(from.get('next'), 'begin', 'next records its predecessor')
|
|
127
|
+
})
|
|
128
|
+
|
|
106
129
|
test('running → succeeded updates the in-place history row', async () => {
|
|
107
130
|
await seedStep()
|
|
108
131
|
await mirror.setStepRunning(STEP_ID)
|
|
@@ -79,6 +79,7 @@ export class KyselyWorkflowMirror implements WorkflowRunMirror {
|
|
|
79
79
|
.addColumn('branch_taken', 'text')
|
|
80
80
|
.addColumn('retries', 'integer')
|
|
81
81
|
.addColumn('retry_delay', 'text')
|
|
82
|
+
.addColumn('from_step_name', 'text')
|
|
82
83
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
83
84
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
84
85
|
)
|
|
@@ -91,6 +92,15 @@ export class KyselyWorkflowMirror implements WorkflowRunMirror {
|
|
|
91
92
|
])
|
|
92
93
|
.execute()
|
|
93
94
|
|
|
95
|
+
// Additive: backfill the column on mirror tables created before from_step_name.
|
|
96
|
+
await this.db.schema
|
|
97
|
+
.alterTable('workflow_step')
|
|
98
|
+
.addColumn('from_step_name', 'text')
|
|
99
|
+
.execute()
|
|
100
|
+
.catch(() => {
|
|
101
|
+
// Column already exists (fresh tables include it above) — nothing to do.
|
|
102
|
+
})
|
|
103
|
+
|
|
94
104
|
await this.db.schema
|
|
95
105
|
.createTable('workflow_step_history')
|
|
96
106
|
.ifNotExists()
|
|
@@ -202,6 +212,7 @@ export class KyselyWorkflowMirror implements WorkflowRunMirror {
|
|
|
202
212
|
status: step.status,
|
|
203
213
|
retries: step.retries ?? null,
|
|
204
214
|
retryDelay: step.retryDelay?.toString() ?? null,
|
|
215
|
+
fromStepName: step.fromStepName ?? null,
|
|
205
216
|
createdAt: now,
|
|
206
217
|
updatedAt: now,
|
|
207
218
|
})
|
|
@@ -192,6 +192,7 @@ export class KyselyWorkflowRunService implements WorkflowRunService {
|
|
|
192
192
|
.select([
|
|
193
193
|
's.workflowStepId',
|
|
194
194
|
's.stepName',
|
|
195
|
+
's.fromStepName',
|
|
195
196
|
's.retries',
|
|
196
197
|
's.retryDelay',
|
|
197
198
|
'h.status',
|
|
@@ -215,6 +216,7 @@ export class KyselyWorkflowRunService implements WorkflowRunService {
|
|
|
215
216
|
return {
|
|
216
217
|
stepId,
|
|
217
218
|
stepName: row.stepName,
|
|
219
|
+
fromStepName: row.fromStepName ?? undefined,
|
|
218
220
|
status: row.status as StepState['status'],
|
|
219
221
|
result: parseJson(row.result),
|
|
220
222
|
error: parseJson(row.error),
|
|
@@ -319,7 +319,7 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
319
319
|
if (latestHistory) {
|
|
320
320
|
await this.db
|
|
321
321
|
.updateTable('workflowStepHistory')
|
|
322
|
-
.set({ status: 'running' })
|
|
322
|
+
.set({ status: 'running', runningAt: new Date() })
|
|
323
323
|
.where('historyId', '=', latestHistory.historyId)
|
|
324
324
|
.execute()
|
|
325
325
|
}
|
|
@@ -417,7 +417,7 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
417
417
|
if (latestHistory) {
|
|
418
418
|
await this.db
|
|
419
419
|
.updateTable('workflowStepHistory')
|
|
420
|
-
.set({ status: 'succeeded', result: resultJson })
|
|
420
|
+
.set({ status: 'succeeded', result: resultJson, succeededAt: new Date() })
|
|
421
421
|
.where('historyId', '=', latestHistory.historyId)
|
|
422
422
|
.execute()
|
|
423
423
|
}
|
|
@@ -456,7 +456,7 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
456
456
|
if (latestHistory) {
|
|
457
457
|
await this.db
|
|
458
458
|
.updateTable('workflowStepHistory')
|
|
459
|
-
.set({ status: 'failed', error: errorJson })
|
|
459
|
+
.set({ status: 'failed', error: errorJson, failedAt: new Date() })
|
|
460
460
|
.where('historyId', '=', latestHistory.historyId)
|
|
461
461
|
.execute()
|
|
462
462
|
}
|