db4ai 0.3.0 → 0.3.2

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 (89) hide show
  1. package/dist/chunk-3DWAMVV5.js +305 -0
  2. package/dist/chunk-3DWAMVV5.js.map +1 -0
  3. package/dist/chunk-COTPYBYM.js +618 -0
  4. package/dist/chunk-COTPYBYM.js.map +1 -0
  5. package/dist/chunk-EERD6CDF.js +735 -0
  6. package/dist/chunk-EERD6CDF.js.map +1 -0
  7. package/dist/chunk-FUF4HJTC.js +758 -0
  8. package/dist/chunk-FUF4HJTC.js.map +1 -0
  9. package/dist/chunk-JLL6FH5L.js +16 -0
  10. package/dist/chunk-JLL6FH5L.js.map +1 -0
  11. package/dist/chunk-JXFW6AIT.js +192 -0
  12. package/dist/chunk-JXFW6AIT.js.map +1 -0
  13. package/dist/chunk-XLSYCQPG.js +854 -0
  14. package/dist/chunk-XLSYCQPG.js.map +1 -0
  15. package/dist/chunk-Y5IXAS7F.js +569 -0
  16. package/dist/chunk-Y5IXAS7F.js.map +1 -0
  17. package/dist/cli/bin.d.ts +13 -12
  18. package/dist/cli/bin.js +277 -307
  19. package/dist/cli/bin.js.map +1 -1
  20. package/dist/cli/dashboard/index.d.ts +295 -14
  21. package/dist/cli/dashboard/index.js +60 -15
  22. package/dist/cli/dashboard/index.js.map +1 -1
  23. package/dist/cli/index.d.ts +10 -16
  24. package/dist/cli/index.js +94 -47
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/runtime/index.d.ts +52 -23
  27. package/dist/cli/runtime/index.js +10 -704
  28. package/dist/cli/runtime/index.js.map +1 -1
  29. package/dist/cli/scanner/index.d.ts +17 -15
  30. package/dist/cli/scanner/index.js +8 -639
  31. package/dist/cli/scanner/index.js.map +1 -1
  32. package/dist/cli/seed/index.d.ts +16 -12
  33. package/dist/cli/seed/index.js +12 -773
  34. package/dist/cli/seed/index.js.map +1 -1
  35. package/dist/cli/sync/index.d.ts +54 -53
  36. package/dist/cli/sync/index.js +23 -704
  37. package/dist/cli/sync/index.js.map +1 -1
  38. package/dist/cli/terminal.d.ts +9 -8
  39. package/dist/cli/terminal.js +6 -209
  40. package/dist/cli/terminal.js.map +1 -1
  41. package/dist/cli/workflow/index.d.ts +18 -10
  42. package/dist/cli/workflow/index.js +6 -307
  43. package/dist/cli/workflow/index.js.map +1 -1
  44. package/dist/handlers.d.ts +10 -9
  45. package/dist/handlers.js +6 -38
  46. package/dist/handlers.js.map +1 -1
  47. package/dist/index.d.ts +120 -118
  48. package/dist/index.js +1972 -3125
  49. package/dist/index.js.map +1 -1
  50. package/package.json +3 -4
  51. package/dist/cli/bin.d.ts.map +0 -1
  52. package/dist/cli/dashboard/App.d.ts +0 -16
  53. package/dist/cli/dashboard/App.d.ts.map +0 -1
  54. package/dist/cli/dashboard/App.js +0 -116
  55. package/dist/cli/dashboard/App.js.map +0 -1
  56. package/dist/cli/dashboard/components/index.d.ts +0 -70
  57. package/dist/cli/dashboard/components/index.d.ts.map +0 -1
  58. package/dist/cli/dashboard/components/index.js +0 -192
  59. package/dist/cli/dashboard/components/index.js.map +0 -1
  60. package/dist/cli/dashboard/hooks/index.d.ts +0 -76
  61. package/dist/cli/dashboard/hooks/index.d.ts.map +0 -1
  62. package/dist/cli/dashboard/hooks/index.js +0 -201
  63. package/dist/cli/dashboard/hooks/index.js.map +0 -1
  64. package/dist/cli/dashboard/index.d.ts.map +0 -1
  65. package/dist/cli/dashboard/types.d.ts +0 -84
  66. package/dist/cli/dashboard/types.d.ts.map +0 -1
  67. package/dist/cli/dashboard/types.js +0 -5
  68. package/dist/cli/dashboard/types.js.map +0 -1
  69. package/dist/cli/dashboard/views/index.d.ts +0 -51
  70. package/dist/cli/dashboard/views/index.d.ts.map +0 -1
  71. package/dist/cli/dashboard/views/index.js +0 -72
  72. package/dist/cli/dashboard/views/index.js.map +0 -1
  73. package/dist/cli/index.d.ts.map +0 -1
  74. package/dist/cli/runtime/index.d.ts.map +0 -1
  75. package/dist/cli/scanner/index.d.ts.map +0 -1
  76. package/dist/cli/seed/index.d.ts.map +0 -1
  77. package/dist/cli/sync/index.d.ts.map +0 -1
  78. package/dist/cli/terminal.d.ts.map +0 -1
  79. package/dist/cli/workflow/index.d.ts.map +0 -1
  80. package/dist/errors.d.ts +0 -43
  81. package/dist/errors.d.ts.map +0 -1
  82. package/dist/errors.js +0 -47
  83. package/dist/errors.js.map +0 -1
  84. package/dist/handlers.d.ts.map +0 -1
  85. package/dist/index.d.ts.map +0 -1
  86. package/dist/types.d.ts +0 -276
  87. package/dist/types.d.ts.map +0 -1
  88. package/dist/types.js +0 -12
  89. package/dist/types.js.map +0 -1
@@ -0,0 +1,305 @@
1
+ // cli/workflow/index.ts
2
+ import { randomUUID } from "crypto";
3
+ var matchesPattern = (pattern, type, action) => {
4
+ const [patternType, patternAction] = pattern.split(".");
5
+ const typeMatches = patternType === "*" || patternType === type;
6
+ const actionMatches = patternAction === "*" || patternAction === action;
7
+ return typeMatches && actionMatches;
8
+ };
9
+ function createWorkflowRunner(db, eventsAPI) {
10
+ const workflows = /* @__PURE__ */ new Map();
11
+ const executions = /* @__PURE__ */ new Map();
12
+ const abortControllers = /* @__PURE__ */ new Map();
13
+ const eventUnsubscribers = [];
14
+ const runQueues = /* @__PURE__ */ new Map();
15
+ const runningCounts = /* @__PURE__ */ new Map();
16
+ const scheduleTimers = /* @__PURE__ */ new Map();
17
+ let isWatching = false;
18
+ const internalHandlers = /* @__PURE__ */ new Map();
19
+ const originalEmit = eventsAPI.emit;
20
+ const triggerInternalHandlers = async (event) => {
21
+ for (const [pattern, handlers] of internalHandlers.entries()) {
22
+ if (matchesPattern(pattern, event.type, event.action)) {
23
+ for (const handler of handlers) {
24
+ await handler(event);
25
+ }
26
+ }
27
+ }
28
+ };
29
+ const originalEmitFn = eventsAPI.emit;
30
+ const emitProxy = new Proxy(originalEmitFn, {
31
+ apply: async (target, thisArg, args) => {
32
+ const eventOrString = args[0];
33
+ if (typeof eventOrString !== "string" && eventOrString?.type && eventOrString?.action) {
34
+ await triggerInternalHandlers(eventOrString);
35
+ }
36
+ return Reflect.apply(target, thisArg, args);
37
+ },
38
+ get: (target, prop, receiver) => {
39
+ return Reflect.get(target, prop, receiver);
40
+ }
41
+ });
42
+ eventsAPI.emit = emitProxy;
43
+ const createLogger = (runId, workflowName) => ({
44
+ info: (message, data) => {
45
+ console.info(`[${workflowName}:${runId}] ${message}`, data ?? "");
46
+ },
47
+ warn: (message, data) => {
48
+ console.warn(`[${workflowName}:${runId}] ${message}`, data ?? "");
49
+ },
50
+ error: (message, data) => {
51
+ console.error(`[${workflowName}:${runId}] ${message}`, data ?? "");
52
+ },
53
+ debug: (message, data) => {
54
+ console.debug(`[${workflowName}:${runId}] ${message}`, data ?? "");
55
+ }
56
+ });
57
+ const executeWorkflow = async (config, entity, runId, attempt, input) => {
58
+ const abortController = new AbortController();
59
+ abortControllers.set(runId, abortController);
60
+ const execution = executions.get(runId);
61
+ execution.status = "running";
62
+ execution.attempt = attempt;
63
+ try {
64
+ const context = {
65
+ entity,
66
+ db: {
67
+ generate: db.generate,
68
+ relationships: db.relationships,
69
+ get: db.get,
70
+ list: db.list,
71
+ update: db.update
72
+ },
73
+ emit: (event, data) => {
74
+ ;
75
+ eventsAPI.emit(event, data);
76
+ },
77
+ log: createLogger(runId, config.name),
78
+ workflow: {
79
+ name: config.name,
80
+ runId,
81
+ attempt
82
+ },
83
+ signal: abortController.signal,
84
+ input
85
+ };
86
+ const timeout = config.timeout ?? 3e4;
87
+ let timeoutId;
88
+ const timeoutPromise = new Promise((_, reject) => {
89
+ timeoutId = setTimeout(() => {
90
+ abortController.abort();
91
+ reject(new Error("Workflow timeout"));
92
+ }, timeout);
93
+ });
94
+ try {
95
+ await Promise.race([config.handler(context), timeoutPromise]);
96
+ } finally {
97
+ clearTimeout(timeoutId);
98
+ }
99
+ execution.status = "completed";
100
+ execution.completedAt = Date.now();
101
+ } catch (err) {
102
+ const maxRetries = config.maxRetries ?? 0;
103
+ if (attempt < maxRetries && !abortController.signal.aborted) {
104
+ const retryDelay = config.retryDelay ?? 1e3;
105
+ await new Promise((r) => setTimeout(r, retryDelay));
106
+ return executeWorkflow(config, entity, runId, attempt + 1, input);
107
+ }
108
+ execution.status = abortController.signal.aborted ? "cancelled" : "failed";
109
+ execution.error = err instanceof Error ? err.message : String(err);
110
+ execution.completedAt = Date.now();
111
+ } finally {
112
+ abortControllers.delete(runId);
113
+ const count = runningCounts.get(config.name) ?? 0;
114
+ runningCounts.set(config.name, Math.max(0, count - 1));
115
+ const queue = runQueues.get(config.name) ?? [];
116
+ if (queue.length > 0) {
117
+ const next = queue.shift();
118
+ runQueues.set(config.name, queue);
119
+ next();
120
+ }
121
+ }
122
+ };
123
+ const scheduleExecution = async (config, entity, runId, input) => {
124
+ const concurrency = config.concurrency ?? 1;
125
+ const currentRunning = runningCounts.get(config.name) ?? 0;
126
+ if (currentRunning >= concurrency) {
127
+ return new Promise((resolve) => {
128
+ const queue = runQueues.get(config.name) ?? [];
129
+ queue.push(async () => {
130
+ await scheduleExecution(config, entity, runId, input);
131
+ resolve();
132
+ });
133
+ runQueues.set(config.name, queue);
134
+ });
135
+ }
136
+ runningCounts.set(config.name, currentRunning + 1);
137
+ await executeWorkflow(config, entity, runId, 1, input);
138
+ };
139
+ return {
140
+ register(config) {
141
+ if (workflows.has(config.name)) {
142
+ throw new Error(`Workflow '${config.name}' already exists`);
143
+ }
144
+ workflows.set(config.name, config);
145
+ },
146
+ registerAll(configs) {
147
+ for (const config of configs) {
148
+ this.register(config);
149
+ }
150
+ },
151
+ unregister(name) {
152
+ workflows.delete(name);
153
+ },
154
+ async watch() {
155
+ if (isWatching) return;
156
+ isWatching = true;
157
+ for (const [, config] of workflows) {
158
+ if (typeof config.trigger === "string" && config.trigger !== "manual") {
159
+ const handler = async (event) => {
160
+ const entity = event.data;
161
+ if (config.filter) {
162
+ const shouldProcess = await config.filter(entity);
163
+ if (!shouldProcess) return;
164
+ }
165
+ const runId = randomUUID();
166
+ executions.set(runId, {
167
+ runId,
168
+ workflowName: config.name,
169
+ status: "pending",
170
+ startedAt: Date.now(),
171
+ entityId: event.entityId,
172
+ entityType: event.type,
173
+ attempt: 0
174
+ });
175
+ await scheduleExecution(
176
+ config,
177
+ {
178
+ ...entity,
179
+ $id: entity.$id ?? event.entityId,
180
+ $rowid: entity.$rowid ?? event.entityRowid
181
+ },
182
+ runId
183
+ );
184
+ };
185
+ const existingHandlers = internalHandlers.get(config.trigger) ?? [];
186
+ internalHandlers.set(config.trigger, [...existingHandlers, handler]);
187
+ }
188
+ }
189
+ },
190
+ async stop() {
191
+ isWatching = false;
192
+ for (const unsubscribe of eventUnsubscribers) {
193
+ unsubscribe();
194
+ }
195
+ eventUnsubscribers.length = 0;
196
+ internalHandlers.clear();
197
+ },
198
+ async run(name, options) {
199
+ const config = workflows.get(name);
200
+ if (!config) {
201
+ throw new Error(`Workflow '${name}' not found`);
202
+ }
203
+ const runId = randomUUID();
204
+ let entity = {};
205
+ if (options?.target) {
206
+ const [type, id] = options.target.split(":");
207
+ const fetched = await db.get(type, id);
208
+ entity = fetched;
209
+ }
210
+ executions.set(runId, {
211
+ runId,
212
+ workflowName: name,
213
+ status: "pending",
214
+ startedAt: Date.now(),
215
+ entityId: entity.$id,
216
+ entityType: options?.target?.split(":")[0],
217
+ attempt: 0
218
+ });
219
+ const executePromise = scheduleExecution(
220
+ config,
221
+ { ...entity, $id: entity.$id ?? "manual", $rowid: entity.$rowid ?? 0 },
222
+ runId,
223
+ options?.input
224
+ );
225
+ if (options?.wait) {
226
+ await executePromise;
227
+ }
228
+ return runId;
229
+ },
230
+ active() {
231
+ return Array.from(executions.values());
232
+ },
233
+ get(name) {
234
+ return workflows.get(name);
235
+ },
236
+ list() {
237
+ return Array.from(workflows.values());
238
+ },
239
+ stats() {
240
+ const all = Array.from(executions.values());
241
+ return {
242
+ total: all.length,
243
+ running: all.filter((e) => e.status === "running").length,
244
+ completed: all.filter((e) => e.status === "completed").length,
245
+ failed: all.filter((e) => e.status === "failed").length,
246
+ cancelled: all.filter((e) => e.status === "cancelled").length
247
+ };
248
+ },
249
+ async cancel(runId) {
250
+ const controller = abortControllers.get(runId);
251
+ if (controller) {
252
+ controller.abort();
253
+ }
254
+ const execution = executions.get(runId);
255
+ if (execution) {
256
+ execution.status = "cancelled";
257
+ execution.completedAt = Date.now();
258
+ }
259
+ },
260
+ async cancelAll(name) {
261
+ for (const [runId, execution] of executions) {
262
+ if (execution.workflowName === name && execution.status === "running") {
263
+ await this.cancel(runId);
264
+ }
265
+ }
266
+ runQueues.delete(name);
267
+ },
268
+ async waitFor(runId, timeout = 3e4) {
269
+ const start = Date.now();
270
+ while (Date.now() - start < timeout) {
271
+ const execution = executions.get(runId);
272
+ if (execution && (execution.status === "completed" || execution.status === "failed" || execution.status === "cancelled")) {
273
+ return execution;
274
+ }
275
+ await new Promise((r) => setTimeout(r, 50));
276
+ }
277
+ throw new Error(`Timeout waiting for workflow ${runId}`);
278
+ },
279
+ history(filter) {
280
+ let results = Array.from(executions.values());
281
+ if (filter?.workflowName) {
282
+ results = results.filter((e) => e.workflowName === filter.workflowName);
283
+ }
284
+ if (filter?.status) {
285
+ results = results.filter((e) => e.status === filter.status);
286
+ }
287
+ if (filter?.since) {
288
+ results = results.filter((e) => e.startedAt >= filter.since);
289
+ }
290
+ if (filter?.until) {
291
+ results = results.filter((e) => e.startedAt <= filter.until);
292
+ }
293
+ results.sort((a, b) => b.startedAt - a.startedAt);
294
+ if (filter?.limit && filter.limit > 0) {
295
+ results = results.slice(0, filter.limit);
296
+ }
297
+ return results;
298
+ }
299
+ };
300
+ }
301
+
302
+ export {
303
+ createWorkflowRunner
304
+ };
305
+ //# sourceMappingURL=chunk-3DWAMVV5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../cli/workflow/index.ts"],"sourcesContent":["/**\n * Module 16: Workflow Runner\n *\n * Executes event-triggered and manual workflows with integration\n * to the Events API and support for concurrency control.\n */\n\nimport { randomUUID } from 'node:crypto'\n\n// =============================================================================\n// Type Definitions\n// =============================================================================\n\nexport type WorkflowTrigger = string | { schedule: string } | 'manual'\n\nexport interface WorkflowContext<T = unknown> {\n entity: T & { $id: string; $rowid: number }\n db: {\n generate: (opts: { type: string; prompt?: string; id?: string }) => Promise<unknown>\n relationships: {\n create: (opts: { from: number; to: number; type: string; label?: string }) => Promise<void>\n list: (opts: { from?: number; to?: number; type?: string }) => Promise<unknown[]>\n }\n get: (type: string, id: string) => Promise<unknown>\n list: (type: string, opts?: { where?: Record<string, unknown>; limit?: number }) => Promise<unknown[]>\n update: (type: string, id: string, data: Record<string, unknown>) => Promise<unknown>\n }\n emit: (event: string, data?: Record<string, unknown>) => void\n log: {\n info: (message: string, data?: Record<string, unknown>) => void\n warn: (message: string, data?: Record<string, unknown>) => void\n error: (message: string, data?: Record<string, unknown>) => void\n debug: (message: string, data?: Record<string, unknown>) => void\n }\n workflow: {\n name: string\n runId: string\n attempt: number\n }\n signal: AbortSignal\n input?: Record<string, unknown>\n}\n\nexport interface WorkflowConfig<T = unknown> {\n name: string\n trigger: WorkflowTrigger\n concurrency?: number\n timeout?: number\n maxRetries?: number\n retryDelay?: number\n handler: (ctx: WorkflowContext<T>) => Promise<void>\n filter?: (entity: T) => boolean | Promise<boolean>\n description?: string\n}\n\nexport interface RunOptions {\n target?: string\n input?: Record<string, unknown>\n concurrency?: number\n wait?: boolean\n}\n\nexport interface WorkflowExecution {\n runId: string\n workflowName: string\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n startedAt: number\n completedAt?: number\n entityId?: string\n entityType?: string\n attempt: number\n error?: string\n}\n\nexport interface WorkflowStats {\n total: number\n running: number\n completed: number\n failed: number\n cancelled: number\n}\n\nexport interface WorkflowHistoryFilter {\n workflowName?: string\n status?: WorkflowExecution['status']\n since?: number\n until?: number\n limit?: number\n}\n\nexport interface WorkflowRunner {\n register(config: WorkflowConfig): void\n registerAll(configs: WorkflowConfig[]): void\n unregister(name: string): void\n watch(): Promise<void>\n stop(): Promise<void>\n run(name: string, options?: RunOptions): Promise<string>\n active(): WorkflowExecution[]\n get(name: string): WorkflowConfig | undefined\n list(): WorkflowConfig[]\n stats(): WorkflowStats\n cancel(runId: string): Promise<void>\n cancelAll(name: string): Promise<void>\n waitFor(runId: string, timeout?: number): Promise<WorkflowExecution>\n history(filter?: WorkflowHistoryFilter): WorkflowExecution[]\n}\n\ninterface MockDB {\n generate: (opts: { type: string; prompt?: string; id?: string }) => Promise<unknown>\n relationships: {\n create: (opts: { from: number; to: number; type: string; label?: string }) => Promise<void>\n list: (opts: { from?: number; to?: number; type?: string }) => Promise<unknown[]>\n }\n get: (type: string, id: string) => Promise<unknown>\n list: (type: string, opts?: { where?: Record<string, unknown>; limit?: number }) => Promise<unknown[]>\n update: (type: string, id: string, data: Record<string, unknown>) => Promise<unknown>\n}\n\ninterface DBEvent {\n id: string\n type: string\n action: 'created' | 'updated' | 'deleted' | 'generated'\n entityId: string\n entityRowid: number\n data: unknown\n timestamp: number\n}\n\ninterface MockEventsAPI {\n on: (pattern: string, handler: (event: DBEvent) => Promise<void>) => () => void\n off: (pattern: string) => void\n emit: (event: DBEvent) => Promise<void>\n replay: (opts: { since?: number; pattern?: string }) => Promise<void>\n}\n\n// =============================================================================\n// Workflow Runner Implementation\n// =============================================================================\n\n// Pattern matching helper - matches event patterns like 'Company.created', '*.updated', etc.\nconst matchesPattern = (pattern: string, type: string, action: string): boolean => {\n const [patternType, patternAction] = pattern.split('.')\n const typeMatches = patternType === '*' || patternType === type\n const actionMatches = patternAction === '*' || patternAction === action\n return typeMatches && actionMatches\n}\n\n// Cron parser helper - parses cron expression and calculates next run time\n// Format: minute hour day-of-month month day-of-week\n// Supports: * (any), */n (every n), n (exact value)\nconst parseCronField = (field: string, min: number, max: number): number[] => {\n if (field === '*') {\n return Array.from({ length: max - min + 1 }, (_, i) => min + i)\n }\n if (field.startsWith('*/')) {\n const step = parseInt(field.slice(2), 10)\n const values: number[] = []\n for (let i = min; i <= max; i += step) {\n values.push(i)\n }\n return values\n }\n return [parseInt(field, 10)]\n}\n\nconst getNextCronRun = (cronExpr: string): number => {\n const parts = cronExpr.split(' ')\n if (parts.length !== 5) return Date.now() + 60000 // Default to 1 minute if invalid\n\n const [minuteField, hourField, dayField, monthField, weekdayField] = parts\n const minutes = parseCronField(minuteField, 0, 59)\n const hours = parseCronField(hourField, 0, 23)\n const days = parseCronField(dayField, 1, 31)\n const months = parseCronField(monthField, 1, 12)\n const weekdays = parseCronField(weekdayField, 0, 6)\n\n const now = new Date()\n const candidate = new Date(now)\n candidate.setSeconds(0)\n candidate.setMilliseconds(0)\n\n // Try to find the next matching time (up to 1 year ahead)\n for (let i = 0; i < 525600; i++) { // Max iterations: 1 year of minutes\n candidate.setMinutes(candidate.getMinutes() + 1)\n\n const m = candidate.getMinutes()\n const h = candidate.getHours()\n const d = candidate.getDate()\n const mo = candidate.getMonth() + 1\n const wd = candidate.getDay()\n\n if (\n minutes.includes(m) &&\n hours.includes(h) &&\n days.includes(d) &&\n months.includes(mo) &&\n weekdays.includes(wd)\n ) {\n return candidate.getTime()\n }\n }\n\n // Fallback: return 1 hour from now if no match found\n return Date.now() + 3600000\n}\n\nexport function createWorkflowRunner(db: MockDB, eventsAPI: MockEventsAPI): WorkflowRunner {\n const workflows = new Map<string, WorkflowConfig>()\n const executions = new Map<string, WorkflowExecution>()\n const abortControllers = new Map<string, AbortController>()\n const eventUnsubscribers: (() => void)[] = []\n const runQueues = new Map<string, (() => Promise<void>)[]>()\n const runningCounts = new Map<string, number>()\n const scheduleTimers = new Map<string, ReturnType<typeof setTimeout>>()\n let isWatching = false\n\n // Store internal handlers for event triggering\n const internalHandlers = new Map<string, ((event: DBEvent) => Promise<void>)[]>()\n\n // Store reference to original emit function\n const originalEmit = eventsAPI.emit\n\n // Create a function that triggers internal handlers and calls the current emit\n const triggerInternalHandlers = async (event: DBEvent) => {\n for (const [pattern, handlers] of internalHandlers.entries()) {\n if (matchesPattern(pattern, event.type, event.action)) {\n for (const handler of handlers) {\n await handler(event)\n }\n }\n }\n }\n\n // Intercept emit calls by wrapping the vi.fn()\n // We use Object.defineProperty to intercept calls while preserving vi.fn() methods\n const originalEmitFn = eventsAPI.emit\n const emitProxy = new Proxy(originalEmitFn, {\n apply: async (target, thisArg, args) => {\n const eventOrString = args[0]\n // If called with a DBEvent object, trigger internal handlers first\n if (typeof eventOrString !== 'string' && eventOrString?.type && eventOrString?.action) {\n await triggerInternalHandlers(eventOrString as DBEvent)\n }\n // Call the original function (which might have been replaced by mockImplementation)\n return Reflect.apply(target, thisArg, args)\n },\n get: (target, prop, receiver) => {\n // Pass through all properties (including mockImplementation, mock, etc.)\n return Reflect.get(target, prop, receiver)\n }\n })\n\n ;(eventsAPI as any).emit = emitProxy\n\n const createLogger = (runId: string, workflowName: string) => ({\n info: (message: string, data?: Record<string, unknown>) => {\n console.info(`[${workflowName}:${runId}] ${message}`, data ?? '')\n },\n warn: (message: string, data?: Record<string, unknown>) => {\n console.warn(`[${workflowName}:${runId}] ${message}`, data ?? '')\n },\n error: (message: string, data?: Record<string, unknown>) => {\n console.error(`[${workflowName}:${runId}] ${message}`, data ?? '')\n },\n debug: (message: string, data?: Record<string, unknown>) => {\n console.debug(`[${workflowName}:${runId}] ${message}`, data ?? '')\n },\n })\n\n const executeWorkflow = async (\n config: WorkflowConfig,\n entity: unknown,\n runId: string,\n attempt: number,\n input?: Record<string, unknown>\n ): Promise<void> => {\n const abortController = new AbortController()\n abortControllers.set(runId, abortController)\n\n const execution = executions.get(runId)!\n execution.status = 'running'\n execution.attempt = attempt\n\n try {\n const context: WorkflowContext = {\n entity: entity as WorkflowContext['entity'],\n db: {\n generate: db.generate,\n relationships: db.relationships,\n get: db.get,\n list: db.list,\n update: db.update,\n },\n emit: (event: string, data?: Record<string, unknown>) => {\n // Call eventsAPI.emit with the string event name first, then data\n // This allows tests to capture the emitted event in string format\n ;(eventsAPI.emit as unknown as (event: string, data?: Record<string, unknown>) => void)(event, data)\n },\n log: createLogger(runId, config.name),\n workflow: {\n name: config.name,\n runId,\n attempt,\n },\n signal: abortController.signal,\n input,\n }\n\n // Apply timeout\n const timeout = config.timeout ?? 30000\n let timeoutId: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n abortController.abort()\n reject(new Error('Workflow timeout'))\n }, timeout)\n })\n\n try {\n await Promise.race([config.handler(context), timeoutPromise])\n } finally {\n clearTimeout(timeoutId!)\n }\n\n execution.status = 'completed'\n execution.completedAt = Date.now()\n } catch (err) {\n const maxRetries = config.maxRetries ?? 0\n if (attempt < maxRetries && !abortController.signal.aborted) {\n const retryDelay = config.retryDelay ?? 1000\n await new Promise((r) => setTimeout(r, retryDelay))\n return executeWorkflow(config, entity, runId, attempt + 1, input)\n }\n\n execution.status = abortController.signal.aborted ? 'cancelled' : 'failed'\n execution.error = err instanceof Error ? err.message : String(err)\n execution.completedAt = Date.now()\n } finally {\n abortControllers.delete(runId)\n const count = runningCounts.get(config.name) ?? 0\n runningCounts.set(config.name, Math.max(0, count - 1))\n\n // Process next in queue\n const queue = runQueues.get(config.name) ?? []\n if (queue.length > 0) {\n const next = queue.shift()!\n runQueues.set(config.name, queue)\n next()\n }\n }\n }\n\n const scheduleExecution = async (\n config: WorkflowConfig,\n entity: unknown,\n runId: string,\n input?: Record<string, unknown>\n ): Promise<void> => {\n const concurrency = config.concurrency ?? 1\n const currentRunning = runningCounts.get(config.name) ?? 0\n\n if (currentRunning >= concurrency) {\n // Queue the execution\n return new Promise((resolve) => {\n const queue = runQueues.get(config.name) ?? []\n queue.push(async () => {\n await scheduleExecution(config, entity, runId, input)\n resolve()\n })\n runQueues.set(config.name, queue)\n })\n }\n\n runningCounts.set(config.name, currentRunning + 1)\n await executeWorkflow(config, entity, runId, 1, input)\n }\n\n return {\n register(config: WorkflowConfig): void {\n if (workflows.has(config.name)) {\n throw new Error(`Workflow '${config.name}' already exists`)\n }\n workflows.set(config.name, config)\n },\n\n registerAll(configs: WorkflowConfig[]): void {\n for (const config of configs) {\n this.register(config)\n }\n },\n\n unregister(name: string): void {\n workflows.delete(name)\n },\n\n async watch(): Promise<void> {\n if (isWatching) return\n isWatching = true\n\n for (const [, config] of workflows) {\n if (typeof config.trigger === 'string' && config.trigger !== 'manual') {\n const handler = async (event: DBEvent) => {\n const entity = event.data as Record<string, unknown>\n\n // Apply filter if present\n if (config.filter) {\n const shouldProcess = await config.filter(entity)\n if (!shouldProcess) return\n }\n\n const runId = randomUUID()\n executions.set(runId, {\n runId,\n workflowName: config.name,\n status: 'pending',\n startedAt: Date.now(),\n entityId: event.entityId,\n entityType: event.type,\n attempt: 0,\n })\n\n await scheduleExecution(\n config,\n {\n ...entity,\n $id: (entity as { $id?: string }).$id ?? event.entityId,\n $rowid: (entity as { $rowid?: number }).$rowid ?? event.entityRowid\n },\n runId\n )\n }\n\n // Store handler in internal map for direct triggering\n // (We don't use eventsAPI.on to avoid double-triggering when test doesn't override mock)\n const existingHandlers = internalHandlers.get(config.trigger) ?? []\n internalHandlers.set(config.trigger, [...existingHandlers, handler])\n }\n }\n },\n\n async stop(): Promise<void> {\n isWatching = false\n for (const unsubscribe of eventUnsubscribers) {\n unsubscribe()\n }\n eventUnsubscribers.length = 0\n internalHandlers.clear()\n },\n\n async run(name: string, options?: RunOptions): Promise<string> {\n const config = workflows.get(name)\n if (!config) {\n throw new Error(`Workflow '${name}' not found`)\n }\n\n const runId = randomUUID()\n let entity: Record<string, unknown> = {}\n\n // Parse target if provided\n if (options?.target) {\n const [type, id] = options.target.split(':')\n const fetched = await db.get(type, id)\n entity = fetched as Record<string, unknown>\n }\n\n executions.set(runId, {\n runId,\n workflowName: name,\n status: 'pending',\n startedAt: Date.now(),\n entityId: (entity as { $id?: string }).$id,\n entityType: options?.target?.split(':')[0],\n attempt: 0,\n })\n\n const executePromise = scheduleExecution(\n config,\n { ...entity, $id: entity.$id ?? 'manual', $rowid: entity.$rowid ?? 0 },\n runId,\n options?.input\n )\n\n if (options?.wait) {\n await executePromise\n }\n\n return runId\n },\n\n active(): WorkflowExecution[] {\n return Array.from(executions.values())\n },\n\n get(name: string): WorkflowConfig | undefined {\n return workflows.get(name)\n },\n\n list(): WorkflowConfig[] {\n return Array.from(workflows.values())\n },\n\n stats(): WorkflowStats {\n const all = Array.from(executions.values())\n return {\n total: all.length,\n running: all.filter((e) => e.status === 'running').length,\n completed: all.filter((e) => e.status === 'completed').length,\n failed: all.filter((e) => e.status === 'failed').length,\n cancelled: all.filter((e) => e.status === 'cancelled').length,\n }\n },\n\n async cancel(runId: string): Promise<void> {\n const controller = abortControllers.get(runId)\n if (controller) {\n controller.abort()\n }\n const execution = executions.get(runId)\n if (execution) {\n execution.status = 'cancelled'\n execution.completedAt = Date.now()\n }\n },\n\n async cancelAll(name: string): Promise<void> {\n for (const [runId, execution] of executions) {\n if (execution.workflowName === name && execution.status === 'running') {\n await this.cancel(runId)\n }\n }\n // Clear queue\n runQueues.delete(name)\n },\n\n async waitFor(runId: string, timeout = 30000): Promise<WorkflowExecution> {\n const start = Date.now()\n while (Date.now() - start < timeout) {\n const execution = executions.get(runId)\n if (execution && (execution.status === 'completed' || execution.status === 'failed' || execution.status === 'cancelled')) {\n return execution\n }\n await new Promise((r) => setTimeout(r, 50))\n }\n throw new Error(`Timeout waiting for workflow ${runId}`)\n },\n\n history(filter?: WorkflowHistoryFilter): WorkflowExecution[] {\n let results = Array.from(executions.values())\n\n // Filter by workflow name\n if (filter?.workflowName) {\n results = results.filter((e) => e.workflowName === filter.workflowName)\n }\n\n // Filter by status\n if (filter?.status) {\n results = results.filter((e) => e.status === filter.status)\n }\n\n // Filter by time range\n if (filter?.since) {\n results = results.filter((e) => e.startedAt >= filter.since!)\n }\n if (filter?.until) {\n results = results.filter((e) => e.startedAt <= filter.until!)\n }\n\n // Sort by startedAt descending (most recent first)\n results.sort((a, b) => b.startedAt - a.startedAt)\n\n // Apply limit\n if (filter?.limit && filter.limit > 0) {\n results = results.slice(0, filter.limit)\n }\n\n return results\n },\n }\n}\n"],"mappings":"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","names":[]}