yaml-flow 1.0.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.js ADDED
@@ -0,0 +1,731 @@
1
+ // src/stores/memory.ts
2
+ var MemoryStore = class {
3
+ runs = /* @__PURE__ */ new Map();
4
+ data = /* @__PURE__ */ new Map();
5
+ async saveRunState(runId, state) {
6
+ this.runs.set(runId, { ...state });
7
+ }
8
+ async loadRunState(runId) {
9
+ const state = this.runs.get(runId);
10
+ return state ? { ...state } : null;
11
+ }
12
+ async deleteRunState(runId) {
13
+ this.runs.delete(runId);
14
+ this.data.delete(runId);
15
+ }
16
+ async setData(runId, key, value) {
17
+ if (!this.data.has(runId)) {
18
+ this.data.set(runId, {});
19
+ }
20
+ const runData = this.data.get(runId);
21
+ runData[key] = value;
22
+ }
23
+ async getData(runId, key) {
24
+ return this.data.get(runId)?.[key];
25
+ }
26
+ async getAllData(runId) {
27
+ return { ...this.data.get(runId) ?? {} };
28
+ }
29
+ async clearData(runId) {
30
+ this.data.delete(runId);
31
+ }
32
+ async listRuns() {
33
+ return Array.from(this.runs.keys());
34
+ }
35
+ /**
36
+ * Clear all data (useful for testing)
37
+ */
38
+ clear() {
39
+ this.runs.clear();
40
+ this.data.clear();
41
+ }
42
+ };
43
+
44
+ // src/core/engine.ts
45
+ function generateRunId() {
46
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
47
+ return crypto.randomUUID();
48
+ }
49
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
50
+ const r = Math.random() * 16 | 0;
51
+ const v = c === "x" ? r : r & 3 | 8;
52
+ return v.toString(16);
53
+ });
54
+ }
55
+ var FlowEngine = class {
56
+ flow;
57
+ handlers;
58
+ store;
59
+ components;
60
+ options;
61
+ listeners = /* @__PURE__ */ new Map();
62
+ aborted = false;
63
+ constructor(flow, handlers, options = {}) {
64
+ this.flow = flow;
65
+ this.handlers = new Map(Object.entries(handlers));
66
+ this.store = options.store ?? new MemoryStore();
67
+ this.components = options.components ?? {};
68
+ this.options = options;
69
+ if (options.signal) {
70
+ options.signal.addEventListener("abort", () => {
71
+ this.aborted = true;
72
+ });
73
+ }
74
+ this.validateFlow();
75
+ }
76
+ /**
77
+ * Validate the flow configuration
78
+ */
79
+ validateFlow() {
80
+ const { settings, steps, terminal_states } = this.flow;
81
+ if (!settings?.start_step) {
82
+ throw new Error("Flow must have settings.start_step defined");
83
+ }
84
+ if (!steps || Object.keys(steps).length === 0) {
85
+ throw new Error("Flow must have at least one step defined");
86
+ }
87
+ if (!terminal_states || Object.keys(terminal_states).length === 0) {
88
+ throw new Error("Flow must have at least one terminal_state defined");
89
+ }
90
+ if (!steps[settings.start_step] && !terminal_states[settings.start_step]) {
91
+ throw new Error(`Start step "${settings.start_step}" not found in steps or terminal_states`);
92
+ }
93
+ for (const [stepName, stepConfig] of Object.entries(steps)) {
94
+ for (const [result, target] of Object.entries(stepConfig.transitions)) {
95
+ if (!steps[target] && !terminal_states[target]) {
96
+ throw new Error(
97
+ `Step "${stepName}" transition "${result}" points to unknown step "${target}"`
98
+ );
99
+ }
100
+ }
101
+ }
102
+ }
103
+ /**
104
+ * Run the flow from the start
105
+ */
106
+ async run(initialData) {
107
+ const runId = generateRunId();
108
+ const startedAt = Date.now();
109
+ const runState = {
110
+ runId,
111
+ flowId: this.flow.id ?? "unnamed",
112
+ currentStep: this.flow.settings.start_step,
113
+ status: "running",
114
+ stepHistory: [],
115
+ iterationCounts: {},
116
+ retryCounts: {},
117
+ startedAt,
118
+ updatedAt: startedAt
119
+ };
120
+ await this.store.saveRunState(runId, runState);
121
+ if (initialData) {
122
+ for (const [key, value] of Object.entries(initialData)) {
123
+ await this.store.setData(runId, key, value);
124
+ }
125
+ }
126
+ this.emit({
127
+ type: "flow:start",
128
+ runId,
129
+ timestamp: startedAt,
130
+ data: { initialData }
131
+ });
132
+ try {
133
+ return await this.executeLoop(runId, runState, startedAt);
134
+ } catch (error) {
135
+ const err = error instanceof Error ? error : new Error(String(error));
136
+ this.emit({
137
+ type: "flow:error",
138
+ runId,
139
+ timestamp: Date.now(),
140
+ data: { error: err.message }
141
+ });
142
+ this.options.onError?.(err);
143
+ runState.status = "failed";
144
+ runState.updatedAt = Date.now();
145
+ await this.store.saveRunState(runId, runState);
146
+ return {
147
+ runId,
148
+ status: "failed",
149
+ data: await this.store.getAllData(runId),
150
+ finalStep: runState.currentStep,
151
+ stepHistory: runState.stepHistory,
152
+ durationMs: Date.now() - startedAt,
153
+ error: err
154
+ };
155
+ }
156
+ }
157
+ /**
158
+ * Resume a paused or interrupted flow
159
+ */
160
+ async resume(runId) {
161
+ const runState = await this.store.loadRunState(runId);
162
+ if (!runState) {
163
+ throw new Error(`No run found with ID: ${runId}`);
164
+ }
165
+ if (runState.status === "completed" || runState.status === "failed") {
166
+ throw new Error(`Cannot resume a ${runState.status} run`);
167
+ }
168
+ const startedAt = runState.startedAt;
169
+ runState.status = "running";
170
+ runState.pausedAt = void 0;
171
+ runState.updatedAt = Date.now();
172
+ await this.store.saveRunState(runId, runState);
173
+ this.emit({
174
+ type: "flow:resumed",
175
+ runId,
176
+ timestamp: Date.now(),
177
+ data: { currentStep: runState.currentStep }
178
+ });
179
+ return this.executeLoop(runId, runState, startedAt);
180
+ }
181
+ /**
182
+ * Pause a running flow
183
+ */
184
+ async pause(runId) {
185
+ const runState = await this.store.loadRunState(runId);
186
+ if (!runState) {
187
+ throw new Error(`No run found with ID: ${runId}`);
188
+ }
189
+ runState.status = "paused";
190
+ runState.pausedAt = Date.now();
191
+ runState.updatedAt = Date.now();
192
+ await this.store.saveRunState(runId, runState);
193
+ this.emit({
194
+ type: "flow:paused",
195
+ runId,
196
+ timestamp: Date.now(),
197
+ data: { currentStep: runState.currentStep }
198
+ });
199
+ }
200
+ /**
201
+ * Main execution loop
202
+ */
203
+ async executeLoop(runId, runState, startedAt) {
204
+ const maxSteps = this.flow.settings.max_total_steps ?? 100;
205
+ const timeoutMs = this.flow.settings.timeout_ms;
206
+ let iterations = 0;
207
+ while (iterations < maxSteps) {
208
+ if (this.aborted) {
209
+ runState.status = "cancelled";
210
+ runState.updatedAt = Date.now();
211
+ await this.store.saveRunState(runId, runState);
212
+ return {
213
+ runId,
214
+ status: "cancelled",
215
+ data: await this.store.getAllData(runId),
216
+ finalStep: runState.currentStep,
217
+ stepHistory: runState.stepHistory,
218
+ durationMs: Date.now() - startedAt
219
+ };
220
+ }
221
+ if (timeoutMs && Date.now() - startedAt > timeoutMs) {
222
+ runState.status = "completed";
223
+ runState.updatedAt = Date.now();
224
+ await this.store.saveRunState(runId, runState);
225
+ return {
226
+ runId,
227
+ status: "timeout",
228
+ intent: "timeout",
229
+ data: await this.store.getAllData(runId),
230
+ finalStep: runState.currentStep,
231
+ stepHistory: runState.stepHistory,
232
+ durationMs: Date.now() - startedAt
233
+ };
234
+ }
235
+ const currentStep = runState.currentStep;
236
+ const terminalState = this.flow.terminal_states[currentStep];
237
+ if (terminalState) {
238
+ runState.status = "completed";
239
+ runState.updatedAt = Date.now();
240
+ await this.store.saveRunState(runId, runState);
241
+ const allData = await this.store.getAllData(runId);
242
+ const returnData = this.extractReturnData(terminalState.return_artifacts, allData);
243
+ const result = {
244
+ runId,
245
+ status: "completed",
246
+ intent: terminalState.return_intent,
247
+ data: returnData,
248
+ finalStep: currentStep,
249
+ stepHistory: runState.stepHistory,
250
+ durationMs: Date.now() - startedAt
251
+ };
252
+ this.emit({
253
+ type: "flow:complete",
254
+ runId,
255
+ timestamp: Date.now(),
256
+ data: { ...result }
257
+ });
258
+ this.options.onComplete?.(result);
259
+ return result;
260
+ }
261
+ const stepConfig = this.flow.steps[currentStep];
262
+ if (!stepConfig) {
263
+ throw new Error(`Step "${currentStep}" not found in flow configuration`);
264
+ }
265
+ if (stepConfig.circuit_breaker) {
266
+ const count = runState.iterationCounts[currentStep] ?? 0;
267
+ if (count >= stepConfig.circuit_breaker.max_iterations) {
268
+ runState.currentStep = stepConfig.circuit_breaker.on_open;
269
+ runState.updatedAt = Date.now();
270
+ await this.store.saveRunState(runId, runState);
271
+ iterations++;
272
+ continue;
273
+ }
274
+ }
275
+ runState.iterationCounts[currentStep] = (runState.iterationCounts[currentStep] ?? 0) + 1;
276
+ const stepResult = await this.executeStep(runId, currentStep, stepConfig);
277
+ if (stepResult.result === "failure" && stepConfig.retry) {
278
+ const retryCount = runState.retryCounts[currentStep] ?? 0;
279
+ if (retryCount < stepConfig.retry.max_attempts) {
280
+ runState.retryCounts[currentStep] = retryCount + 1;
281
+ if (stepConfig.retry.delay_ms) {
282
+ const delay = stepConfig.retry.backoff_multiplier ? stepConfig.retry.delay_ms * Math.pow(stepConfig.retry.backoff_multiplier, retryCount) : stepConfig.retry.delay_ms;
283
+ await this.sleep(delay);
284
+ }
285
+ iterations++;
286
+ continue;
287
+ }
288
+ }
289
+ const nextStep = stepConfig.transitions[stepResult.result];
290
+ if (!nextStep) {
291
+ throw new Error(
292
+ `No transition defined for result "${stepResult.result}" in step "${currentStep}"`
293
+ );
294
+ }
295
+ runState.stepHistory.push(currentStep);
296
+ runState.currentStep = nextStep;
297
+ runState.updatedAt = Date.now();
298
+ runState.retryCounts[currentStep] = 0;
299
+ await this.store.saveRunState(runId, runState);
300
+ this.emit({
301
+ type: "transition",
302
+ runId,
303
+ timestamp: Date.now(),
304
+ data: { from: currentStep, to: nextStep, result: stepResult.result }
305
+ });
306
+ this.options.onTransition?.(currentStep, nextStep);
307
+ iterations++;
308
+ }
309
+ runState.status = "completed";
310
+ runState.updatedAt = Date.now();
311
+ await this.store.saveRunState(runId, runState);
312
+ return {
313
+ runId,
314
+ status: "max_iterations",
315
+ intent: "max_iterations",
316
+ data: await this.store.getAllData(runId),
317
+ finalStep: runState.currentStep,
318
+ stepHistory: runState.stepHistory,
319
+ durationMs: Date.now() - startedAt
320
+ };
321
+ }
322
+ /**
323
+ * Execute a single step
324
+ */
325
+ async executeStep(runId, stepName, stepConfig) {
326
+ const handler = this.handlers.get(stepName);
327
+ if (!handler) {
328
+ throw new Error(`No handler registered for step "${stepName}"`);
329
+ }
330
+ const allData = await this.store.getAllData(runId);
331
+ const input = {};
332
+ if (stepConfig.expects_data) {
333
+ for (const key of stepConfig.expects_data) {
334
+ input[key] = allData[key];
335
+ }
336
+ } else {
337
+ Object.assign(input, allData);
338
+ }
339
+ const context = {
340
+ runId,
341
+ stepName,
342
+ components: this.components,
343
+ store: this.store,
344
+ signal: this.options.signal,
345
+ emit: (event, data) => {
346
+ this.emit({
347
+ type: "step:complete",
348
+ // Custom events map to step:complete
349
+ runId,
350
+ timestamp: Date.now(),
351
+ data: { event, payload: data }
352
+ });
353
+ }
354
+ };
355
+ this.emit({
356
+ type: "step:start",
357
+ runId,
358
+ timestamp: Date.now(),
359
+ data: { step: stepName, input }
360
+ });
361
+ try {
362
+ const result = await handler(input, context);
363
+ if (result.data) {
364
+ for (const [key, value] of Object.entries(result.data)) {
365
+ await this.store.setData(runId, key, value);
366
+ }
367
+ }
368
+ this.emit({
369
+ type: "step:complete",
370
+ runId,
371
+ timestamp: Date.now(),
372
+ data: { step: stepName, result: result.result, outputKeys: Object.keys(result.data ?? {}) }
373
+ });
374
+ this.options.onStep?.(stepName, result);
375
+ return result;
376
+ } catch (error) {
377
+ const err = error instanceof Error ? error : new Error(String(error));
378
+ this.emit({
379
+ type: "step:error",
380
+ runId,
381
+ timestamp: Date.now(),
382
+ data: { step: stepName, error: err.message }
383
+ });
384
+ return { result: "failure", data: { error: err.message } };
385
+ }
386
+ }
387
+ /**
388
+ * Extract data to return based on return_artifacts configuration
389
+ */
390
+ extractReturnData(returnArtifacts, allData) {
391
+ if (returnArtifacts === false || returnArtifacts === void 0) {
392
+ return {};
393
+ }
394
+ if (typeof returnArtifacts === "string") {
395
+ return { [returnArtifacts]: allData[returnArtifacts] };
396
+ }
397
+ if (Array.isArray(returnArtifacts)) {
398
+ const result = {};
399
+ for (const key of returnArtifacts) {
400
+ result[key] = allData[key];
401
+ }
402
+ return result;
403
+ }
404
+ return allData;
405
+ }
406
+ /**
407
+ * Sleep helper
408
+ */
409
+ sleep(ms) {
410
+ return new Promise((resolve) => setTimeout(resolve, ms));
411
+ }
412
+ /**
413
+ * Subscribe to flow events
414
+ */
415
+ on(type, listener) {
416
+ if (!this.listeners.has(type)) {
417
+ this.listeners.set(type, /* @__PURE__ */ new Set());
418
+ }
419
+ this.listeners.get(type).add(listener);
420
+ return () => {
421
+ this.listeners.get(type)?.delete(listener);
422
+ };
423
+ }
424
+ /**
425
+ * Emit an event
426
+ */
427
+ emit(event) {
428
+ const typeListeners = this.listeners.get(event.type);
429
+ if (typeListeners) {
430
+ for (const listener of typeListeners) {
431
+ try {
432
+ listener(event);
433
+ } catch {
434
+ }
435
+ }
436
+ }
437
+ }
438
+ /**
439
+ * Get the current store instance
440
+ */
441
+ getStore() {
442
+ return this.store;
443
+ }
444
+ };
445
+ function createEngine(flow, handlers, options) {
446
+ return new FlowEngine(flow, handlers, options);
447
+ }
448
+
449
+ // src/core/loader.ts
450
+ async function parseYaml(yamlString) {
451
+ const yaml = await import('yaml');
452
+ return yaml.parse(yamlString);
453
+ }
454
+ async function loadFlowFromUrl(url) {
455
+ const response = await fetch(url);
456
+ if (!response.ok) {
457
+ throw new Error(`Failed to load flow from ${url}: ${response.statusText}`);
458
+ }
459
+ const contentType = response.headers.get("content-type") ?? "";
460
+ const text = await response.text();
461
+ if (contentType.includes("json") || url.endsWith(".json")) {
462
+ return JSON.parse(text);
463
+ }
464
+ return parseYaml(text);
465
+ }
466
+ async function loadFlowFromFile(filePath) {
467
+ const fs = await import('fs/promises');
468
+ const text = await fs.readFile(filePath, "utf-8");
469
+ if (filePath.endsWith(".json")) {
470
+ return JSON.parse(text);
471
+ }
472
+ return parseYaml(text);
473
+ }
474
+ function validateFlowConfig(flow) {
475
+ const errors = [];
476
+ if (!flow || typeof flow !== "object") {
477
+ return ["Flow must be an object"];
478
+ }
479
+ const f = flow;
480
+ if (!f.settings || typeof f.settings !== "object") {
481
+ errors.push('Flow must have a "settings" object');
482
+ } else {
483
+ const settings = f.settings;
484
+ if (typeof settings.start_step !== "string") {
485
+ errors.push("settings.start_step must be a string");
486
+ }
487
+ if (settings.max_total_steps !== void 0 && typeof settings.max_total_steps !== "number") {
488
+ errors.push("settings.max_total_steps must be a number");
489
+ }
490
+ if (settings.timeout_ms !== void 0 && typeof settings.timeout_ms !== "number") {
491
+ errors.push("settings.timeout_ms must be a number");
492
+ }
493
+ }
494
+ if (!f.steps || typeof f.steps !== "object") {
495
+ errors.push('Flow must have a "steps" object');
496
+ } else {
497
+ const steps = f.steps;
498
+ for (const [stepName, stepConfig] of Object.entries(steps)) {
499
+ if (!stepConfig || typeof stepConfig !== "object") {
500
+ errors.push(`Step "${stepName}" must be an object`);
501
+ continue;
502
+ }
503
+ const step = stepConfig;
504
+ if (!step.transitions || typeof step.transitions !== "object") {
505
+ errors.push(`Step "${stepName}" must have a "transitions" object`);
506
+ }
507
+ }
508
+ }
509
+ if (!f.terminal_states || typeof f.terminal_states !== "object") {
510
+ errors.push('Flow must have a "terminal_states" object');
511
+ } else {
512
+ const terminals = f.terminal_states;
513
+ for (const [name, config] of Object.entries(terminals)) {
514
+ if (!config || typeof config !== "object") {
515
+ errors.push(`Terminal state "${name}" must be an object`);
516
+ continue;
517
+ }
518
+ const terminal = config;
519
+ if (typeof terminal.return_intent !== "string") {
520
+ errors.push(`Terminal state "${name}" must have a "return_intent" string`);
521
+ }
522
+ }
523
+ }
524
+ return errors;
525
+ }
526
+ async function loadFlow(source) {
527
+ let flow;
528
+ if (typeof source === "string") {
529
+ if (source.startsWith("http://") || source.startsWith("https://")) {
530
+ flow = await loadFlowFromUrl(source);
531
+ } else if (source.includes("{")) {
532
+ flow = JSON.parse(source);
533
+ } else {
534
+ flow = await loadFlowFromFile(source);
535
+ }
536
+ } else {
537
+ flow = source;
538
+ }
539
+ const errors = validateFlowConfig(flow);
540
+ if (errors.length > 0) {
541
+ throw new Error(`Invalid flow configuration:
542
+ - ${errors.join("\n- ")}`);
543
+ }
544
+ return flow;
545
+ }
546
+
547
+ // src/stores/localStorage.ts
548
+ var LocalStorageStore = class {
549
+ prefix;
550
+ constructor(options = {}) {
551
+ this.prefix = options.prefix ?? "yamlflow";
552
+ if (typeof localStorage === "undefined") {
553
+ throw new Error("LocalStorageStore requires localStorage (browser environment)");
554
+ }
555
+ }
556
+ runKey(runId) {
557
+ return `${this.prefix}:run:${runId}`;
558
+ }
559
+ dataKey(runId) {
560
+ return `${this.prefix}:data:${runId}`;
561
+ }
562
+ indexKey() {
563
+ return `${this.prefix}:runs`;
564
+ }
565
+ async saveRunState(runId, state) {
566
+ localStorage.setItem(this.runKey(runId), JSON.stringify(state));
567
+ const runs = await this.listRuns();
568
+ if (!runs.includes(runId)) {
569
+ runs.push(runId);
570
+ localStorage.setItem(this.indexKey(), JSON.stringify(runs));
571
+ }
572
+ }
573
+ async loadRunState(runId) {
574
+ const raw = localStorage.getItem(this.runKey(runId));
575
+ return raw ? JSON.parse(raw) : null;
576
+ }
577
+ async deleteRunState(runId) {
578
+ localStorage.removeItem(this.runKey(runId));
579
+ localStorage.removeItem(this.dataKey(runId));
580
+ const runs = await this.listRuns();
581
+ const filtered = runs.filter((id) => id !== runId);
582
+ localStorage.setItem(this.indexKey(), JSON.stringify(filtered));
583
+ }
584
+ async setData(runId, key, value) {
585
+ const allData = await this.getAllData(runId);
586
+ allData[key] = value;
587
+ localStorage.setItem(this.dataKey(runId), JSON.stringify(allData));
588
+ }
589
+ async getData(runId, key) {
590
+ const allData = await this.getAllData(runId);
591
+ return allData[key];
592
+ }
593
+ async getAllData(runId) {
594
+ const raw = localStorage.getItem(this.dataKey(runId));
595
+ return raw ? JSON.parse(raw) : {};
596
+ }
597
+ async clearData(runId) {
598
+ localStorage.removeItem(this.dataKey(runId));
599
+ }
600
+ async listRuns() {
601
+ const raw = localStorage.getItem(this.indexKey());
602
+ return raw ? JSON.parse(raw) : [];
603
+ }
604
+ /**
605
+ * Clear all flow data from localStorage
606
+ */
607
+ clearAll() {
608
+ const keysToRemove = [];
609
+ for (let i = 0; i < localStorage.length; i++) {
610
+ const key = localStorage.key(i);
611
+ if (key?.startsWith(this.prefix + ":")) {
612
+ keysToRemove.push(key);
613
+ }
614
+ }
615
+ keysToRemove.forEach((key) => localStorage.removeItem(key));
616
+ }
617
+ };
618
+
619
+ // src/stores/file.ts
620
+ var FileStore = class {
621
+ directory;
622
+ fs = null;
623
+ path = null;
624
+ constructor(options) {
625
+ this.directory = options.directory;
626
+ }
627
+ async ensureModules() {
628
+ if (!this.fs || !this.path) {
629
+ this.fs = await import('fs/promises');
630
+ this.path = await import('path');
631
+ await this.fs.mkdir(this.directory, { recursive: true });
632
+ }
633
+ }
634
+ runPath(runId) {
635
+ return this.path.join(this.directory, `${runId}.run.json`);
636
+ }
637
+ dataPath(runId) {
638
+ return this.path.join(this.directory, `${runId}.data.json`);
639
+ }
640
+ async saveRunState(runId, state) {
641
+ await this.ensureModules();
642
+ await this.fs.writeFile(
643
+ this.runPath(runId),
644
+ JSON.stringify(state, null, 2),
645
+ "utf-8"
646
+ );
647
+ }
648
+ async loadRunState(runId) {
649
+ await this.ensureModules();
650
+ try {
651
+ const raw = await this.fs.readFile(this.runPath(runId), "utf-8");
652
+ return JSON.parse(raw);
653
+ } catch (err) {
654
+ if (err.code === "ENOENT") {
655
+ return null;
656
+ }
657
+ throw err;
658
+ }
659
+ }
660
+ async deleteRunState(runId) {
661
+ await this.ensureModules();
662
+ try {
663
+ await this.fs.unlink(this.runPath(runId));
664
+ } catch (err) {
665
+ if (err.code !== "ENOENT") throw err;
666
+ }
667
+ try {
668
+ await this.fs.unlink(this.dataPath(runId));
669
+ } catch (err) {
670
+ if (err.code !== "ENOENT") throw err;
671
+ }
672
+ }
673
+ async setData(runId, key, value) {
674
+ await this.ensureModules();
675
+ const allData = await this.getAllData(runId);
676
+ allData[key] = value;
677
+ await this.fs.writeFile(
678
+ this.dataPath(runId),
679
+ JSON.stringify(allData, null, 2),
680
+ "utf-8"
681
+ );
682
+ }
683
+ async getData(runId, key) {
684
+ const allData = await this.getAllData(runId);
685
+ return allData[key];
686
+ }
687
+ async getAllData(runId) {
688
+ await this.ensureModules();
689
+ try {
690
+ const raw = await this.fs.readFile(this.dataPath(runId), "utf-8");
691
+ return JSON.parse(raw);
692
+ } catch (err) {
693
+ if (err.code === "ENOENT") {
694
+ return {};
695
+ }
696
+ throw err;
697
+ }
698
+ }
699
+ async clearData(runId) {
700
+ await this.ensureModules();
701
+ try {
702
+ await this.fs.unlink(this.dataPath(runId));
703
+ } catch (err) {
704
+ if (err.code !== "ENOENT") throw err;
705
+ }
706
+ }
707
+ async listRuns() {
708
+ await this.ensureModules();
709
+ try {
710
+ const files = await this.fs.readdir(this.directory);
711
+ return files.filter((f) => f.endsWith(".run.json")).map((f) => f.replace(".run.json", ""));
712
+ } catch (err) {
713
+ if (err.code === "ENOENT") {
714
+ return [];
715
+ }
716
+ throw err;
717
+ }
718
+ }
719
+ /**
720
+ * Clear all flow data from directory
721
+ */
722
+ async clearAll() {
723
+ await this.ensureModules();
724
+ const runs = await this.listRuns();
725
+ await Promise.all(runs.map((runId) => this.deleteRunState(runId)));
726
+ }
727
+ };
728
+
729
+ export { FileStore, FlowEngine, LocalStorageStore, MemoryStore, createEngine, loadFlow, loadFlowFromFile, loadFlowFromUrl, parseYaml, validateFlowConfig };
730
+ //# sourceMappingURL=index.js.map
731
+ //# sourceMappingURL=index.js.map