@multiplier-labs/stepflow 0.3.4 → 0.3.8

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.
Files changed (109) hide show
  1. package/README.md +9 -0
  2. package/dist/{chunk-5XJBAPXF.js → chunk-4IDPWWGS.js} +15 -4
  3. package/dist/chunk-4IDPWWGS.js.map +1 -0
  4. package/dist/{chunk-U3PCVK6O.js → chunk-T6GAZBTQ.js} +70 -38
  5. package/dist/chunk-T6GAZBTQ.js.map +1 -0
  6. package/dist/{chunk-FMPJBK2N.js → chunk-TVSADUYU.js} +3 -5
  7. package/dist/{chunk-FMPJBK2N.js.map → chunk-TVSADUYU.js.map} +1 -1
  8. package/dist/events/index.d.ts +43 -3
  9. package/dist/events/index.js +7 -3
  10. package/dist/{index-Cd0IZ40L.d.ts → index-CdXDv30T.d.ts} +10 -2
  11. package/dist/index.d.ts +5 -4
  12. package/dist/index.js +159 -46
  13. package/dist/index.js.map +1 -1
  14. package/dist/scheduler/index.d.ts +2 -2
  15. package/dist/scheduler/index.js +1 -1
  16. package/dist/storage/index.d.ts +6 -4
  17. package/dist/storage/index.js +1 -1
  18. package/dist/{types-DSEjiLMj.d.ts → types-BfWe5yP6.d.ts} +2 -0
  19. package/package.json +26 -14
  20. package/.codekin/reports/code-review/2026-04-11_code-review-daily.md +0 -146
  21. package/.codekin/reports/code-review/2026-04-13_code-review-daily.md +0 -74
  22. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +0 -179
  23. package/.codekin/reports/comments/2026-04-11_comment-assessment.md +0 -161
  24. package/.codekin/reports/comments/2026-04-14_comment-assessment.md +0 -34
  25. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +0 -152
  26. package/.codekin/reports/complexity/2026-04-11_complexity-report.md +0 -152
  27. package/.codekin/reports/complexity/2026-04-14_complexity-report.md +0 -34
  28. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +0 -94
  29. package/.codekin/reports/dependencies/2026-04-11_dependency-health.md +0 -93
  30. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +0 -10
  31. package/.codekin/reports/docs-audit/2026-04-11_docs-audit.md +0 -138
  32. package/.codekin/reports/docs-audit/2026-04-13_docs-audit.md +0 -121
  33. package/.codekin/reports/security/2026-04-03_security-audit.md +0 -208
  34. package/.codekin/reports/security/2026-04-11_security-audit.md +0 -297
  35. package/.github/CODEOWNERS +0 -15
  36. package/.github/dependabot.yml +0 -23
  37. package/.github/workflows/ci.yml +0 -38
  38. package/.github/workflows/publish.yml +0 -42
  39. package/.github/workflows/security.yml +0 -29
  40. package/CHANGELOG.md +0 -34
  41. package/SECURITY.md +0 -40
  42. package/dist/chunk-5XJBAPXF.js.map +0 -1
  43. package/dist/chunk-U3PCVK6O.js.map +0 -1
  44. package/docs/github-packages-setup.md +0 -97
  45. package/docs/stepflow-api-reference.md +0 -1663
  46. package/src/core/engine/concurrency.ts +0 -229
  47. package/src/core/engine/lifecycle.ts +0 -176
  48. package/src/core/engine.test.ts +0 -1435
  49. package/src/core/engine.ts +0 -598
  50. package/src/core/index.ts +0 -7
  51. package/src/core/orchestrator/persistence.ts +0 -165
  52. package/src/core/orchestrator/plan-resolution.ts +0 -140
  53. package/src/core/orchestrator/state-transitions.ts +0 -109
  54. package/src/core/orchestrator/step-runner.ts +0 -427
  55. package/src/core/orchestrator.ts +0 -252
  56. package/src/core/types.ts +0 -220
  57. package/src/events/index.ts +0 -8
  58. package/src/events/memory.test.ts +0 -107
  59. package/src/events/memory.ts +0 -92
  60. package/src/events/socketio.test.ts +0 -378
  61. package/src/events/socketio.ts +0 -262
  62. package/src/events/types.ts +0 -118
  63. package/src/events/webhook.test.ts +0 -949
  64. package/src/events/webhook.ts +0 -807
  65. package/src/index.ts +0 -270
  66. package/src/planning/evaluate-condition.test.ts +0 -153
  67. package/src/planning/evaluate-condition.ts +0 -105
  68. package/src/planning/index.ts +0 -55
  69. package/src/planning/planner.ts +0 -494
  70. package/src/planning/planning.test.ts +0 -727
  71. package/src/planning/registry.test.ts +0 -398
  72. package/src/planning/registry.ts +0 -283
  73. package/src/planning/types.ts +0 -566
  74. package/src/scheduler/cron.test.ts +0 -479
  75. package/src/scheduler/cron.ts +0 -438
  76. package/src/scheduler/index.ts +0 -8
  77. package/src/scheduler/postgres-persistence.ts +0 -530
  78. package/src/scheduler/sqlite-persistence.test.ts +0 -235
  79. package/src/scheduler/sqlite-persistence.ts +0 -286
  80. package/src/scheduler/types.ts +0 -73
  81. package/src/storage/index.ts +0 -27
  82. package/src/storage/memory.test.ts +0 -349
  83. package/src/storage/memory.ts +0 -227
  84. package/src/storage/postgres-core.ts +0 -550
  85. package/src/storage/postgres-migration.test.ts +0 -69
  86. package/src/storage/postgres-transaction-pagination.test.ts +0 -107
  87. package/src/storage/postgres.ts +0 -835
  88. package/src/storage/sqlite-core.ts +0 -151
  89. package/src/storage/sqlite.test.ts +0 -642
  90. package/src/storage/sqlite.ts +0 -717
  91. package/src/storage/types.ts +0 -343
  92. package/src/utils/errors.test.ts +0 -150
  93. package/src/utils/errors.ts +0 -161
  94. package/src/utils/id.ts +0 -21
  95. package/src/utils/index.ts +0 -10
  96. package/src/utils/logger.test.ts +0 -121
  97. package/src/utils/logger.ts +0 -96
  98. package/src/utils/pinned-fetch.test.ts +0 -84
  99. package/src/utils/pinned-fetch.ts +0 -118
  100. package/src/utils/postgres-deps.ts +0 -79
  101. package/src/utils/retry.test.ts +0 -177
  102. package/src/utils/retry.ts +0 -121
  103. package/src/utils/safe-json.test.ts +0 -166
  104. package/src/utils/safe-json.ts +0 -107
  105. package/src/utils/webhook-signature.test.ts +0 -111
  106. package/src/utils/webhook-signature.ts +0 -74
  107. package/tsconfig.json +0 -21
  108. package/tsup.config.ts +0 -14
  109. package/vitest.config.ts +0 -9
@@ -1,235 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import Database from 'better-sqlite3';
3
- import { SQLiteSchedulePersistence } from './sqlite-persistence';
4
- import type { WorkflowSchedule } from './types';
5
-
6
- describe('SQLiteSchedulePersistence', () => {
7
- let db: Database.Database;
8
- let persistence: SQLiteSchedulePersistence;
9
-
10
- beforeEach(() => {
11
- // Use in-memory database for testing
12
- db = new Database(':memory:');
13
- persistence = new SQLiteSchedulePersistence({ db });
14
- });
15
-
16
- afterEach(() => {
17
- db.close();
18
- });
19
-
20
- describe('saveSchedule', () => {
21
- it('should save a cron schedule', async () => {
22
- const schedule: WorkflowSchedule = {
23
- id: 'test-1',
24
- workflowKind: 'test.workflow',
25
- triggerType: 'cron',
26
- cronExpression: '0 * * * *',
27
- timezone: 'UTC',
28
- enabled: true,
29
- input: { foo: 'bar' },
30
- metadata: { user: 'test' },
31
- };
32
-
33
- await persistence.saveSchedule(schedule);
34
-
35
- const loaded = await persistence.loadSchedules();
36
- expect(loaded).toHaveLength(1);
37
- expect(loaded[0].id).toBe('test-1');
38
- expect(loaded[0].cronExpression).toBe('0 * * * *');
39
- expect(loaded[0].input).toEqual({ foo: 'bar' });
40
- });
41
-
42
- it('should save a workflow completion trigger', async () => {
43
- const schedule: WorkflowSchedule = {
44
- id: 'test-2',
45
- workflowKind: 'notification.send',
46
- triggerType: 'workflow_completed',
47
- triggerOnWorkflowKind: 'order.process',
48
- triggerOnStatus: ['succeeded', 'failed'],
49
- enabled: true,
50
- };
51
-
52
- await persistence.saveSchedule(schedule);
53
-
54
- const loaded = await persistence.loadSchedules();
55
- expect(loaded).toHaveLength(1);
56
- expect(loaded[0].triggerType).toBe('workflow_completed');
57
- expect(loaded[0].triggerOnWorkflowKind).toBe('order.process');
58
- expect(loaded[0].triggerOnStatus).toEqual(['succeeded', 'failed']);
59
- });
60
-
61
- it('should save schedule with dates', async () => {
62
- const lastRunAt = new Date('2024-01-01T12:00:00Z');
63
- const nextRunAt = new Date('2024-01-01T13:00:00Z');
64
-
65
- const schedule: WorkflowSchedule = {
66
- id: 'test-3',
67
- workflowKind: 'test.workflow',
68
- triggerType: 'cron',
69
- cronExpression: '0 * * * *',
70
- enabled: true,
71
- lastRunAt,
72
- lastRunId: 'run-123',
73
- nextRunAt,
74
- };
75
-
76
- await persistence.saveSchedule(schedule);
77
-
78
- const loaded = await persistence.loadSchedules();
79
- expect(loaded[0].lastRunAt).toEqual(lastRunAt);
80
- expect(loaded[0].lastRunId).toBe('run-123');
81
- expect(loaded[0].nextRunAt).toEqual(nextRunAt);
82
- });
83
- });
84
-
85
- describe('loadSchedules', () => {
86
- it('should return empty array when no schedules', async () => {
87
- const schedules = await persistence.loadSchedules();
88
- expect(schedules).toHaveLength(0);
89
- });
90
-
91
- it('should load multiple schedules', async () => {
92
- await persistence.saveSchedule({
93
- id: 'schedule-1',
94
- workflowKind: 'workflow.a',
95
- triggerType: 'cron',
96
- cronExpression: '0 * * * *',
97
- enabled: true,
98
- });
99
-
100
- await persistence.saveSchedule({
101
- id: 'schedule-2',
102
- workflowKind: 'workflow.b',
103
- triggerType: 'manual',
104
- enabled: false,
105
- });
106
-
107
- const schedules = await persistence.loadSchedules();
108
- expect(schedules).toHaveLength(2);
109
- });
110
- });
111
-
112
- describe('updateSchedule', () => {
113
- it('should update enabled status', async () => {
114
- await persistence.saveSchedule({
115
- id: 'update-1',
116
- workflowKind: 'test.workflow',
117
- triggerType: 'cron',
118
- cronExpression: '0 * * * *',
119
- enabled: true,
120
- });
121
-
122
- await persistence.updateSchedule('update-1', { enabled: false });
123
-
124
- const loaded = await persistence.loadSchedules();
125
- expect(loaded[0].enabled).toBe(false);
126
- });
127
-
128
- it('should update cron expression', async () => {
129
- await persistence.saveSchedule({
130
- id: 'update-2',
131
- workflowKind: 'test.workflow',
132
- triggerType: 'cron',
133
- cronExpression: '0 * * * *',
134
- enabled: true,
135
- });
136
-
137
- await persistence.updateSchedule('update-2', {
138
- cronExpression: '30 * * * *',
139
- });
140
-
141
- const loaded = await persistence.loadSchedules();
142
- expect(loaded[0].cronExpression).toBe('30 * * * *');
143
- });
144
-
145
- it('should update lastRunAt and lastRunId', async () => {
146
- await persistence.saveSchedule({
147
- id: 'update-3',
148
- workflowKind: 'test.workflow',
149
- triggerType: 'cron',
150
- cronExpression: '0 * * * *',
151
- enabled: true,
152
- });
153
-
154
- const now = new Date();
155
- await persistence.updateSchedule('update-3', {
156
- lastRunAt: now,
157
- lastRunId: 'run-456',
158
- });
159
-
160
- const loaded = await persistence.loadSchedules();
161
- expect(loaded[0].lastRunAt).toEqual(now);
162
- expect(loaded[0].lastRunId).toBe('run-456');
163
- });
164
-
165
- it('should throw for non-existent schedule', async () => {
166
- await expect(
167
- persistence.updateSchedule('nonexistent', { enabled: false })
168
- ).rejects.toThrow('Schedule not found');
169
- });
170
- });
171
-
172
- describe('deleteSchedule', () => {
173
- it('should delete a schedule', async () => {
174
- await persistence.saveSchedule({
175
- id: 'delete-1',
176
- workflowKind: 'test.workflow',
177
- triggerType: 'cron',
178
- cronExpression: '0 * * * *',
179
- enabled: true,
180
- });
181
-
182
- await persistence.deleteSchedule('delete-1');
183
-
184
- const loaded = await persistence.loadSchedules();
185
- expect(loaded).toHaveLength(0);
186
- });
187
-
188
- it('should handle deleting non-existent schedule gracefully', async () => {
189
- // This should not throw
190
- await persistence.deleteSchedule('nonexistent');
191
- });
192
- });
193
-
194
- describe('data integrity', () => {
195
- it('should preserve all fields through save/load cycle', async () => {
196
- const schedule: WorkflowSchedule = {
197
- id: 'integrity-1',
198
- workflowKind: 'complex.workflow',
199
- triggerType: 'workflow_completed',
200
- triggerOnWorkflowKind: 'parent.workflow',
201
- triggerOnStatus: ['succeeded'],
202
- input: { nested: { value: 123 } },
203
- metadata: { tags: ['a', 'b'] },
204
- enabled: true,
205
- lastRunAt: new Date('2024-06-15T10:30:00Z'),
206
- lastRunId: 'run-789',
207
- nextRunAt: new Date('2024-06-15T11:00:00Z'),
208
- };
209
-
210
- await persistence.saveSchedule(schedule);
211
-
212
- const loaded = await persistence.loadSchedules();
213
- expect(loaded[0]).toEqual(schedule);
214
- });
215
-
216
- it('should handle null/undefined values correctly', async () => {
217
- const schedule: WorkflowSchedule = {
218
- id: 'nullable-1',
219
- workflowKind: 'test.workflow',
220
- triggerType: 'manual',
221
- enabled: false,
222
- // All other optional fields are undefined
223
- };
224
-
225
- await persistence.saveSchedule(schedule);
226
-
227
- const loaded = await persistence.loadSchedules();
228
- expect(loaded[0].cronExpression).toBeUndefined();
229
- expect(loaded[0].timezone).toBeUndefined();
230
- expect(loaded[0].input).toBeUndefined();
231
- expect(loaded[0].metadata).toBeUndefined();
232
- expect(loaded[0].lastRunAt).toBeUndefined();
233
- });
234
- });
235
- });
@@ -1,286 +0,0 @@
1
- /**
2
- * SQLite persistence adapter for schedules.
3
- *
4
- * Stores workflow schedules in a SQLite database table.
5
- */
6
-
7
- import type { Database, Statement } from 'better-sqlite3';
8
- import type { WorkflowSchedule } from './types';
9
- import type { SchedulePersistence } from './cron';
10
- import type { Logger, RunStatus } from '../core/types';
11
- import { generateId } from '../utils/id';
12
- import { safeJsonParse as sharedSafeJsonParse } from '../utils/safe-json';
13
-
14
- // ============================================================================
15
- // Configuration Types
16
- // ============================================================================
17
-
18
- /**
19
- * Configuration for SQLiteSchedulePersistence.
20
- */
21
- export interface SQLiteSchedulePersistenceConfig {
22
- /** SQLite database instance */
23
- db: Database;
24
-
25
- /** Table name for schedules (default: workflow_schedules) */
26
- tableName?: string;
27
-
28
- /**
29
- * Optional structured logger used to surface JSON corruption events.
30
- * Without one, parse failures still increment the corruption counter
31
- * exposed via `getJsonParseCorruptionCount()` but produce no log output.
32
- */
33
- logger?: Logger;
34
- }
35
-
36
- // ============================================================================
37
- // SQLiteSchedulePersistence Class
38
- // ============================================================================
39
-
40
- /**
41
- * SQLite-based persistence for workflow schedules.
42
- *
43
- * @example
44
- * ```typescript
45
- * import Database from 'better-sqlite3';
46
- *
47
- * const db = new Database('workflow.db');
48
- * const persistence = new SQLiteSchedulePersistence({ db });
49
- *
50
- * const scheduler = new CronScheduler({
51
- * engine,
52
- * persistence,
53
- * });
54
- * ```
55
- */
56
- export class SQLiteSchedulePersistence implements SchedulePersistence {
57
- private db: Database;
58
- private tableName: string;
59
- private logger: Logger | undefined;
60
- private stmts: {
61
- insert: Statement;
62
- update: Statement;
63
- delete: Statement;
64
- getAll: Statement;
65
- getById: Statement;
66
- } | null = null;
67
-
68
- constructor(config: SQLiteSchedulePersistenceConfig) {
69
- this.db = config.db;
70
- this.tableName = config.tableName ?? 'workflow_schedules';
71
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(this.tableName)) {
72
- throw new Error(`Invalid table name: ${this.tableName}`);
73
- }
74
- this.logger = config.logger;
75
- this.initializeDatabase();
76
- }
77
-
78
- // ============================================================================
79
- // Database Initialization
80
- // ============================================================================
81
-
82
- private initializeDatabase(): void {
83
- // Create schedules table.
84
- // Schema layout:
85
- // id — unique schedule identifier (TEXT PK)
86
- // workflow_kind — target workflow type to spawn
87
- // trigger_type — 'cron', 'workflow_completed', or 'manual'
88
- // cron_expression — cron expression for time-based triggers (nullable)
89
- // timezone — IANA timezone for cron evaluation (nullable)
90
- // trigger_on_* — fields for workflow-completion triggers
91
- // input / metadata — JSON-serialized payloads for spawned runs
92
- // enabled — whether this schedule is active (0/1)
93
- // last_run_at/id — most recent execution tracking
94
- // next_run_at — next scheduled execution time
95
- // created_at/updated_at — audit timestamps
96
- // No migration strategy is currently implemented; table is created idempotently.
97
- this.db.exec(`
98
- CREATE TABLE IF NOT EXISTS ${this.tableName} (
99
- id TEXT PRIMARY KEY,
100
- workflow_kind TEXT NOT NULL,
101
- trigger_type TEXT NOT NULL,
102
- cron_expression TEXT,
103
- timezone TEXT,
104
- trigger_on_workflow_kind TEXT,
105
- trigger_on_status TEXT,
106
- input TEXT,
107
- metadata TEXT,
108
- enabled INTEGER NOT NULL DEFAULT 1,
109
- last_run_at TEXT,
110
- last_run_id TEXT,
111
- next_run_at TEXT,
112
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
113
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
114
- )
115
- `);
116
-
117
- // Create indexes
118
- this.db.exec(`
119
- CREATE INDEX IF NOT EXISTS idx_${this.tableName}_workflow_kind
120
- ON ${this.tableName}(workflow_kind);
121
-
122
- CREATE INDEX IF NOT EXISTS idx_${this.tableName}_enabled
123
- ON ${this.tableName}(enabled);
124
-
125
- CREATE INDEX IF NOT EXISTS idx_${this.tableName}_trigger_type
126
- ON ${this.tableName}(trigger_type);
127
- `);
128
-
129
- // Prepare statements
130
- this.stmts = {
131
- insert: this.db.prepare(`
132
- INSERT INTO ${this.tableName} (
133
- id, workflow_kind, trigger_type, cron_expression, timezone,
134
- trigger_on_workflow_kind, trigger_on_status, input, metadata,
135
- enabled, last_run_at, last_run_id, next_run_at
136
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
137
- `),
138
- update: this.db.prepare(`
139
- UPDATE ${this.tableName}
140
- SET workflow_kind = COALESCE(?, workflow_kind),
141
- trigger_type = COALESCE(?, trigger_type),
142
- cron_expression = ?,
143
- timezone = ?,
144
- trigger_on_workflow_kind = ?,
145
- trigger_on_status = ?,
146
- input = ?,
147
- metadata = ?,
148
- enabled = COALESCE(?, enabled),
149
- last_run_at = ?,
150
- last_run_id = ?,
151
- next_run_at = ?,
152
- updated_at = datetime('now')
153
- WHERE id = ?
154
- `),
155
- delete: this.db.prepare(`
156
- DELETE FROM ${this.tableName} WHERE id = ?
157
- `),
158
- getAll: this.db.prepare(`
159
- SELECT * FROM ${this.tableName}
160
- `),
161
- getById: this.db.prepare(`
162
- SELECT * FROM ${this.tableName} WHERE id = ?
163
- `),
164
- };
165
- }
166
-
167
- // ============================================================================
168
- // SchedulePersistence Interface Implementation
169
- // ============================================================================
170
-
171
- async loadSchedules(): Promise<WorkflowSchedule[]> {
172
- const rows = this.stmts!.getAll.all() as ScheduleRow[];
173
- return rows.map(row => this.rowToSchedule(row));
174
- }
175
-
176
- async saveSchedule(schedule: WorkflowSchedule): Promise<void> {
177
- this.stmts!.insert.run(
178
- schedule.id,
179
- schedule.workflowKind,
180
- schedule.triggerType,
181
- schedule.cronExpression ?? null,
182
- schedule.timezone ?? null,
183
- schedule.triggerOnWorkflowKind ?? null,
184
- schedule.triggerOnStatus ? JSON.stringify(schedule.triggerOnStatus) : null,
185
- schedule.input ? JSON.stringify(schedule.input) : null,
186
- schedule.metadata ? JSON.stringify(schedule.metadata) : null,
187
- schedule.enabled ? 1 : 0,
188
- schedule.lastRunAt?.toISOString() ?? null,
189
- schedule.lastRunId ?? null,
190
- schedule.nextRunAt?.toISOString() ?? null
191
- );
192
- }
193
-
194
- async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {
195
- // Get existing schedule to merge with updates
196
- const existing = this.stmts!.getById.get(scheduleId) as ScheduleRow | undefined;
197
- if (!existing) {
198
- throw new Error(`Schedule not found: ${scheduleId}`);
199
- }
200
-
201
- const merged = {
202
- ...this.rowToSchedule(existing),
203
- ...updates,
204
- };
205
-
206
- this.stmts!.update.run(
207
- updates.workflowKind ?? null,
208
- updates.triggerType ?? null,
209
- merged.cronExpression ?? null,
210
- merged.timezone ?? null,
211
- merged.triggerOnWorkflowKind ?? null,
212
- merged.triggerOnStatus ? JSON.stringify(merged.triggerOnStatus) : null,
213
- merged.input ? JSON.stringify(merged.input) : null,
214
- merged.metadata ? JSON.stringify(merged.metadata) : null,
215
- updates.enabled !== undefined ? (updates.enabled ? 1 : 0) : null,
216
- merged.lastRunAt?.toISOString() ?? null,
217
- merged.lastRunId ?? null,
218
- merged.nextRunAt?.toISOString() ?? null,
219
- scheduleId
220
- );
221
- }
222
-
223
- async deleteSchedule(scheduleId: string): Promise<void> {
224
- this.stmts!.delete.run(scheduleId);
225
- }
226
-
227
- // ============================================================================
228
- // Helper Methods
229
- // ============================================================================
230
-
231
- private safeJsonParse(
232
- json: string,
233
- fallback: unknown,
234
- rowId: string,
235
- column: string,
236
- ): unknown {
237
- return sharedSafeJsonParse(json, fallback, {
238
- component: 'SQLiteSchedulePersistence',
239
- rowId,
240
- column,
241
- logger: this.logger,
242
- });
243
- }
244
-
245
- private rowToSchedule(row: ScheduleRow): WorkflowSchedule {
246
- return {
247
- id: row.id,
248
- workflowKind: row.workflow_kind,
249
- triggerType: row.trigger_type as WorkflowSchedule['triggerType'],
250
- cronExpression: row.cron_expression ?? undefined,
251
- timezone: row.timezone ?? undefined,
252
- triggerOnWorkflowKind: row.trigger_on_workflow_kind ?? undefined,
253
- triggerOnStatus: row.trigger_on_status
254
- ? this.safeJsonParse(row.trigger_on_status, undefined, row.id, 'trigger_on_status') as RunStatus[] | undefined
255
- : undefined,
256
- input: row.input ? this.safeJsonParse(row.input, undefined, row.id, 'input') as Record<string, unknown> | undefined : undefined,
257
- metadata: row.metadata ? this.safeJsonParse(row.metadata, undefined, row.id, 'metadata') as Record<string, unknown> | undefined : undefined,
258
- enabled: row.enabled === 1,
259
- lastRunAt: row.last_run_at ? new Date(row.last_run_at) : undefined,
260
- lastRunId: row.last_run_id ?? undefined,
261
- nextRunAt: row.next_run_at ? new Date(row.next_run_at) : undefined,
262
- };
263
- }
264
- }
265
-
266
- // ============================================================================
267
- // Types
268
- // ============================================================================
269
-
270
- interface ScheduleRow {
271
- id: string;
272
- workflow_kind: string;
273
- trigger_type: string;
274
- cron_expression: string | null;
275
- timezone: string | null;
276
- trigger_on_workflow_kind: string | null;
277
- trigger_on_status: string | null;
278
- input: string | null;
279
- metadata: string | null;
280
- enabled: number;
281
- last_run_at: string | null;
282
- last_run_id: string | null;
283
- next_run_at: string | null;
284
- created_at: string;
285
- updated_at: string;
286
- }
@@ -1,73 +0,0 @@
1
- /**
2
- * Scheduler types for the workflow engine.
3
- * Defines the type contracts for the cron and workflow-completion trigger
4
- * scheduler, implemented by {@link CronScheduler}, {@link SQLiteSchedulePersistence},
5
- * and {@link PostgresSchedulePersistence}.
6
- */
7
-
8
- import type { WorkflowKind, RunStatus } from '../core/types';
9
-
10
- /**
11
- * Schedule trigger types.
12
- */
13
- export type TriggerType = 'cron' | 'workflow_completed' | 'manual';
14
-
15
- /**
16
- * Schedule definition.
17
- */
18
- export interface WorkflowSchedule {
19
- id: string;
20
- workflowKind: WorkflowKind;
21
- triggerType: TriggerType;
22
-
23
- /** Cron expression for time-based triggers (e.g. `0 0 * * *` for daily). Only used when triggerType is 'cron'. */
24
- cronExpression?: string;
25
- /** IANA timezone for cron evaluation (e.g. 'America/New_York'). Defaults to 'UTC'. */
26
- timezone?: string;
27
-
28
- /** Workflow kind that triggers this schedule on completion. Only used when triggerType is 'workflow_completed'. */
29
- triggerOnWorkflowKind?: WorkflowKind;
30
- /** Run statuses that activate the completion trigger. If empty, any terminal status triggers. */
31
- triggerOnStatus?: RunStatus[];
32
-
33
- /** Input payload passed to the spawned workflow run. */
34
- input?: Record<string, unknown>;
35
- /** Arbitrary metadata attached to the spawned workflow run. */
36
- metadata?: Record<string, unknown>;
37
-
38
- /** Whether this schedule is active. Disabled schedules are not evaluated. */
39
- enabled: boolean;
40
- /** Timestamp of the most recent run spawned by this schedule. */
41
- lastRunAt?: Date;
42
- /** Run ID of the most recent run spawned by this schedule. */
43
- lastRunId?: string;
44
- /** Next scheduled execution time, computed from the cron expression and timezone. */
45
- nextRunAt?: Date;
46
- }
47
-
48
- /**
49
- * Scheduler interface.
50
- * Implement this to create custom schedulers.
51
- */
52
- export interface Scheduler {
53
- /** Start the scheduler */
54
- start(): Promise<void>;
55
-
56
- /** Stop the scheduler */
57
- stop(): Promise<void>;
58
-
59
- /** Add a schedule */
60
- addSchedule(schedule: Omit<WorkflowSchedule, 'id'>): Promise<WorkflowSchedule>;
61
-
62
- /** Remove a schedule */
63
- removeSchedule(scheduleId: string): Promise<void>;
64
-
65
- /** Update a schedule */
66
- updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void>;
67
-
68
- /** Get all schedules */
69
- getSchedules(): Promise<WorkflowSchedule[]>;
70
-
71
- /** Manually trigger a scheduled workflow */
72
- triggerNow(scheduleId: string): Promise<string>;
73
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Storage module exports.
3
- */
4
-
5
- export * from './types';
6
- export * from './memory';
7
- export * from './sqlite';
8
- export * from './postgres';
9
-
10
- // Re-export specific types for convenience
11
- export type {
12
- WorkflowStorage,
13
- WorkflowRunRecord,
14
- CreateRunInput,
15
- UpdateRunInput,
16
- ListRunsOptions,
17
- PaginatedResult,
18
- StepResult,
19
- StepRecord,
20
- ExtendedWorkflowRunRecord,
21
- ExtendedRunStatus,
22
- ExtendedStepStatus,
23
- ExtendedListRunsOptions,
24
- StepflowRunsTable,
25
- StepflowStepResultsTable,
26
- StepflowDatabase,
27
- } from './types';