@defai.digital/workflow-engine 13.0.3

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.
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Workflow Loader
3
+ *
4
+ * Loads workflow definitions from YAML/JSON files.
5
+ * Similar to AgentLoader but for workflow files.
6
+ *
7
+ * Invariants:
8
+ * - INV-WF-LDR-001: All loaded workflows must pass schema validation
9
+ * - INV-WF-LDR-002: File extensions must match configured extensions
10
+ * - INV-WF-LDR-003: Workflow IDs must be unique across all loaded files
11
+ */
12
+ import { type Workflow } from '@defai.digital/contracts';
13
+ /**
14
+ * Workflow loader configuration
15
+ */
16
+ export interface WorkflowLoaderConfig {
17
+ /**
18
+ * Directory containing workflow files
19
+ */
20
+ workflowsDir: string;
21
+ /**
22
+ * File extensions to load (default: ['.yaml', '.yml', '.json'])
23
+ */
24
+ extensions?: string[];
25
+ /**
26
+ * Watch for file changes (default: false)
27
+ */
28
+ watch?: boolean;
29
+ }
30
+ /**
31
+ * Workflow loader interface
32
+ */
33
+ export interface WorkflowLoader {
34
+ /**
35
+ * Load a workflow by ID
36
+ */
37
+ load(workflowId: string): Promise<Workflow | undefined>;
38
+ /**
39
+ * Load all workflows from the directory
40
+ */
41
+ loadAll(): Promise<Workflow[]>;
42
+ /**
43
+ * Check if a workflow exists
44
+ */
45
+ exists(workflowId: string): Promise<boolean>;
46
+ /**
47
+ * Reload all workflows
48
+ */
49
+ reload(): Promise<void>;
50
+ /**
51
+ * Get workflow count
52
+ */
53
+ count(): number;
54
+ }
55
+ /**
56
+ * Workflow info for listing (lightweight representation)
57
+ */
58
+ export interface WorkflowInfo {
59
+ id: string;
60
+ name: string;
61
+ version: string;
62
+ description: string | undefined;
63
+ stepCount: number;
64
+ status: 'active' | 'inactive' | 'draft';
65
+ filePath: string;
66
+ }
67
+ /**
68
+ * Loads workflow definitions from the file system
69
+ */
70
+ export declare class FileSystemWorkflowLoader implements WorkflowLoader {
71
+ private readonly config;
72
+ private cache;
73
+ private filePathMap;
74
+ private loaded;
75
+ constructor(config: WorkflowLoaderConfig);
76
+ /**
77
+ * Load a workflow by ID
78
+ */
79
+ load(workflowId: string): Promise<Workflow | undefined>;
80
+ /**
81
+ * Load all workflows from the directory
82
+ */
83
+ loadAll(): Promise<Workflow[]>;
84
+ /**
85
+ * Check if a workflow exists
86
+ */
87
+ exists(workflowId: string): Promise<boolean>;
88
+ /**
89
+ * Reload all workflows
90
+ */
91
+ reload(): Promise<void>;
92
+ /**
93
+ * Get workflow count
94
+ */
95
+ count(): number;
96
+ /**
97
+ * Get all workflow infos (lightweight listing)
98
+ */
99
+ listAll(): Promise<WorkflowInfo[]>;
100
+ /**
101
+ * Get file path for a workflow
102
+ */
103
+ getFilePath(workflowId: string): string | undefined;
104
+ /**
105
+ * Load a single workflow from a file
106
+ */
107
+ private loadFile;
108
+ /**
109
+ * Infer workflow status from metadata
110
+ */
111
+ private inferStatus;
112
+ }
113
+ /**
114
+ * Creates a file system workflow loader
115
+ */
116
+ export declare function createWorkflowLoader(config: WorkflowLoaderConfig): FileSystemWorkflowLoader;
117
+ /**
118
+ * Default workflow directories to search
119
+ */
120
+ export declare const DEFAULT_WORKFLOW_DIRS: string[];
121
+ /**
122
+ * Finds the first existing workflow directory
123
+ */
124
+ export declare function findWorkflowDir(basePath: string): string | undefined;
125
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAMzE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAExD;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE/B;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7C;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAExB;;OAEG;IACH,KAAK,IAAI,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAYD;;GAEG;AACH,qBAAa,wBAAyB,YAAW,cAAc;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IACxD,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,oBAAoB;IAQxC;;OAEG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAO7D;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAkDpC;;OAEG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOlD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7B;;OAEG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAsBxC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAInD;;OAEG;YACW,QAAQ;IA6BtB;;OAEG;IACH,OAAO,CAAC,WAAW;CAapB;AAMD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,wBAAwB,CAE3F;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,UAIjC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQpE"}
package/dist/loader.js ADDED
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Workflow Loader
3
+ *
4
+ * Loads workflow definitions from YAML/JSON files.
5
+ * Similar to AgentLoader but for workflow files.
6
+ *
7
+ * Invariants:
8
+ * - INV-WF-LDR-001: All loaded workflows must pass schema validation
9
+ * - INV-WF-LDR-002: File extensions must match configured extensions
10
+ * - INV-WF-LDR-003: Workflow IDs must be unique across all loaded files
11
+ */
12
+ import * as fs from 'node:fs';
13
+ import * as path from 'node:path';
14
+ import { parse as parseYaml } from 'yaml';
15
+ import { WorkflowSchema } from '@defai.digital/contracts';
16
+ // ============================================================================
17
+ // Constants
18
+ // ============================================================================
19
+ const DEFAULT_EXTENSIONS = ['.yaml', '.yml', '.json'];
20
+ // ============================================================================
21
+ // File System Workflow Loader
22
+ // ============================================================================
23
+ /**
24
+ * Loads workflow definitions from the file system
25
+ */
26
+ export class FileSystemWorkflowLoader {
27
+ config;
28
+ cache = new Map();
29
+ filePathMap = new Map();
30
+ loaded = false;
31
+ constructor(config) {
32
+ this.config = {
33
+ workflowsDir: config.workflowsDir,
34
+ extensions: config.extensions ?? DEFAULT_EXTENSIONS,
35
+ watch: config.watch ?? false,
36
+ };
37
+ }
38
+ /**
39
+ * Load a workflow by ID
40
+ */
41
+ async load(workflowId) {
42
+ if (!this.loaded) {
43
+ await this.loadAll();
44
+ }
45
+ return this.cache.get(workflowId);
46
+ }
47
+ /**
48
+ * Load all workflows from the directory
49
+ */
50
+ async loadAll() {
51
+ this.cache.clear();
52
+ this.filePathMap.clear();
53
+ const dirPath = this.config.workflowsDir;
54
+ // Check if directory exists
55
+ if (!fs.existsSync(dirPath)) {
56
+ this.loaded = true;
57
+ return [];
58
+ }
59
+ const files = fs.readdirSync(dirPath);
60
+ const workflows = [];
61
+ for (const file of files) {
62
+ const ext = path.extname(file).toLowerCase();
63
+ if (!this.config.extensions.includes(ext)) {
64
+ continue;
65
+ }
66
+ const filePath = path.join(dirPath, file);
67
+ // Skip directories
68
+ const stat = fs.statSync(filePath);
69
+ if (stat.isDirectory()) {
70
+ continue;
71
+ }
72
+ const workflow = await this.loadFile(filePath);
73
+ if (workflow) {
74
+ // INV-WF-LDR-003: Check for duplicate workflow IDs
75
+ if (this.cache.has(workflow.workflowId)) {
76
+ console.warn(`Duplicate workflow ID "${workflow.workflowId}" found in ${file}, skipping`);
77
+ continue;
78
+ }
79
+ this.cache.set(workflow.workflowId, workflow);
80
+ this.filePathMap.set(workflow.workflowId, filePath);
81
+ workflows.push(workflow);
82
+ }
83
+ }
84
+ this.loaded = true;
85
+ return workflows;
86
+ }
87
+ /**
88
+ * Check if a workflow exists
89
+ */
90
+ async exists(workflowId) {
91
+ if (!this.loaded) {
92
+ await this.loadAll();
93
+ }
94
+ return this.cache.has(workflowId);
95
+ }
96
+ /**
97
+ * Reload all workflows
98
+ */
99
+ async reload() {
100
+ this.loaded = false;
101
+ await this.loadAll();
102
+ }
103
+ /**
104
+ * Get workflow count
105
+ */
106
+ count() {
107
+ return this.cache.size;
108
+ }
109
+ /**
110
+ * Get all workflow infos (lightweight listing)
111
+ */
112
+ async listAll() {
113
+ if (!this.loaded) {
114
+ await this.loadAll();
115
+ }
116
+ const infos = [];
117
+ for (const [workflowId, workflow] of this.cache) {
118
+ infos.push({
119
+ id: workflowId,
120
+ name: workflow.name ?? workflowId,
121
+ version: workflow.version,
122
+ description: workflow.description,
123
+ stepCount: workflow.steps.length,
124
+ status: this.inferStatus(workflow),
125
+ filePath: this.filePathMap.get(workflowId) ?? '',
126
+ });
127
+ }
128
+ return infos;
129
+ }
130
+ /**
131
+ * Get file path for a workflow
132
+ */
133
+ getFilePath(workflowId) {
134
+ return this.filePathMap.get(workflowId);
135
+ }
136
+ /**
137
+ * Load a single workflow from a file
138
+ */
139
+ async loadFile(filePath) {
140
+ try {
141
+ const content = fs.readFileSync(filePath, 'utf-8');
142
+ const ext = path.extname(filePath).toLowerCase();
143
+ let data;
144
+ if (ext === '.json') {
145
+ data = JSON.parse(content);
146
+ }
147
+ else {
148
+ data = parseYaml(content);
149
+ }
150
+ // INV-WF-LDR-001: Validate against schema
151
+ const result = WorkflowSchema.safeParse(data);
152
+ if (!result.success) {
153
+ const errors = result.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ');
154
+ console.warn(`Invalid workflow in ${filePath}: ${errors}`);
155
+ return undefined;
156
+ }
157
+ return result.data;
158
+ }
159
+ catch (error) {
160
+ const message = error instanceof Error ? error.message : 'Unknown error';
161
+ console.warn(`Failed to load workflow from ${filePath}: ${message}`);
162
+ return undefined;
163
+ }
164
+ }
165
+ /**
166
+ * Infer workflow status from metadata
167
+ */
168
+ inferStatus(workflow) {
169
+ // Check metadata for explicit status
170
+ const metadata = workflow.metadata;
171
+ if (metadata?.status === 'inactive')
172
+ return 'inactive';
173
+ if (metadata?.status === 'draft')
174
+ return 'draft';
175
+ // Check if name contains draft indicator
176
+ if (workflow.name?.toLowerCase().includes('draft'))
177
+ return 'draft';
178
+ if (workflow.name?.toLowerCase().includes('wip'))
179
+ return 'draft';
180
+ // Default to active
181
+ return 'active';
182
+ }
183
+ }
184
+ // ============================================================================
185
+ // Factory Functions
186
+ // ============================================================================
187
+ /**
188
+ * Creates a file system workflow loader
189
+ */
190
+ export function createWorkflowLoader(config) {
191
+ return new FileSystemWorkflowLoader(config);
192
+ }
193
+ /**
194
+ * Default workflow directories to search
195
+ */
196
+ export const DEFAULT_WORKFLOW_DIRS = [
197
+ 'examples/workflows',
198
+ 'workflows',
199
+ '.automatosx/workflows',
200
+ ];
201
+ /**
202
+ * Finds the first existing workflow directory
203
+ */
204
+ export function findWorkflowDir(basePath) {
205
+ for (const dir of DEFAULT_WORKFLOW_DIRS) {
206
+ const fullPath = path.join(basePath, dir);
207
+ if (fs.existsSync(fullPath)) {
208
+ return fullPath;
209
+ }
210
+ }
211
+ return undefined;
212
+ }
213
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAiB,MAAM,0BAA0B,CAAC;AAqEzE,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,+EAA+E;AAC/E,8BAA8B;AAC9B,+EAA+E;AAE/E;;GAEG;AACH,MAAM,OAAO,wBAAwB;IAClB,MAAM,CAAiC;IAChD,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IACpC,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,GAAG,KAAK,CAAC;IAEvB,YAAY,MAA4B;QACtC,IAAI,CAAC,MAAM,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,kBAAkB;YACnD,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK;SAC7B,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,UAAkB;QAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;QAEzC,4BAA4B;QAC5B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,SAAS,GAAe,EAAE,CAAC;QAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1C,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAE1C,mBAAmB;YACnB,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAE/C,IAAI,QAAQ,EAAE,CAAC;gBACb,mDAAmD;gBACnD,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;oBACxC,OAAO,CAAC,IAAI,CACV,0BAA0B,QAAQ,CAAC,UAAU,cAAc,IAAI,YAAY,CAC5E,CAAC;oBACF,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;gBAC9C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;gBACpD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,UAAkB;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;QAED,MAAM,KAAK,GAAmB,EAAE,CAAC;QAEjC,KAAK,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,UAAU;gBACd,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,UAAU;gBACjC,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM;gBAChC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;gBAClC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE;aACjD,CAAC,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,UAAkB;QAC5B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,QAAQ,CAAC,QAAgB;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;YAEjD,IAAI,IAAa,CAAC;YAClB,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;gBACpB,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;YAC5B,CAAC;YAED,0CAA0C;YAC1C,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAE9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC5F,OAAO,CAAC,IAAI,CAAC,uBAAuB,QAAQ,KAAK,MAAM,EAAE,CAAC,CAAC;gBAC3D,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACzE,OAAO,CAAC,IAAI,CAAC,gCAAgC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;YACrE,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,QAAkB;QACpC,qCAAqC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACnC,IAAI,QAAQ,EAAE,MAAM,KAAK,UAAU;YAAE,OAAO,UAAU,CAAC;QACvD,IAAI,QAAQ,EAAE,MAAM,KAAK,OAAO;YAAE,OAAO,OAAO,CAAC;QAEjD,yCAAyC;QACzC,IAAI,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QACnE,IAAI,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QAEjE,oBAAoB;QACpB,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAA4B;IAC/D,OAAO,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,oBAAoB;IACpB,WAAW;IACX,uBAAuB;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,KAAK,MAAM,GAAG,IAAI,qBAAqB,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC1C,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type RetryPolicy, DEFAULT_RETRY_POLICY } from '@defai.digital/contracts';
2
+ import type { StepError } from './types.js';
3
+ export { DEFAULT_RETRY_POLICY };
4
+ /**
5
+ * Merges a partial retry policy with defaults
6
+ */
7
+ export declare function mergeRetryPolicy(policy?: RetryPolicy): Required<RetryPolicy>;
8
+ /**
9
+ * Determines if an error should trigger a retry
10
+ * INV-WF-002: Retries are scoped to the current step only
11
+ */
12
+ export declare function shouldRetry(error: StepError, policy: Required<RetryPolicy>, currentAttempt: number): boolean;
13
+ /**
14
+ * Calculates the backoff delay for a retry attempt
15
+ */
16
+ export declare function calculateBackoff(policy: Required<RetryPolicy>, attempt: number): number;
17
+ /**
18
+ * Sleeps for a specified duration
19
+ */
20
+ export declare function sleep(ms: number): Promise<void>;
21
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,oBAAoB,EAErB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,QAAQ,CAAC,WAAW,CAAC,CAcvB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC7B,cAAc,EAAE,MAAM,GACrB,OAAO,CAuBT;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC7B,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAkCD;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
package/dist/retry.js ADDED
@@ -0,0 +1,82 @@
1
+ import { DEFAULT_RETRY_POLICY, DEFAULT_BACKOFF_CAP_MS, } from '@defai.digital/contracts';
2
+ // Re-export for backwards compatibility
3
+ export { DEFAULT_RETRY_POLICY };
4
+ /**
5
+ * Merges a partial retry policy with defaults
6
+ */
7
+ export function mergeRetryPolicy(policy) {
8
+ if (policy === undefined) {
9
+ return DEFAULT_RETRY_POLICY;
10
+ }
11
+ // Cast to partial to allow null coalescing
12
+ // Runtime objects may be incomplete even if TypeScript says otherwise
13
+ const partial = policy;
14
+ return {
15
+ maxAttempts: partial.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts,
16
+ backoffMs: partial.backoffMs ?? DEFAULT_RETRY_POLICY.backoffMs,
17
+ backoffMultiplier: partial.backoffMultiplier ?? DEFAULT_RETRY_POLICY.backoffMultiplier,
18
+ retryOn: partial.retryOn ?? DEFAULT_RETRY_POLICY.retryOn,
19
+ };
20
+ }
21
+ /**
22
+ * Determines if an error should trigger a retry
23
+ * INV-WF-002: Retries are scoped to the current step only
24
+ */
25
+ export function shouldRetry(error, policy, currentAttempt) {
26
+ // Check if we have attempts remaining
27
+ if (currentAttempt >= policy.maxAttempts) {
28
+ return false;
29
+ }
30
+ // Check if the error is retryable
31
+ if (!error.retryable) {
32
+ return false;
33
+ }
34
+ // Check if the error code matches retry conditions
35
+ const errorType = mapErrorCodeToRetryType(error.code);
36
+ if (errorType === null) {
37
+ return false;
38
+ }
39
+ // If retryOn is not defined, allow retry for any retryable error
40
+ if (policy.retryOn === undefined) {
41
+ return true;
42
+ }
43
+ return policy.retryOn.includes(errorType);
44
+ }
45
+ /**
46
+ * Calculates the backoff delay for a retry attempt
47
+ */
48
+ export function calculateBackoff(policy, attempt) {
49
+ // Exponential backoff: baseDelay * multiplier^(attempt-1)
50
+ const delay = policy.backoffMs * Math.pow(policy.backoffMultiplier, attempt - 1);
51
+ return Math.min(delay, DEFAULT_BACKOFF_CAP_MS); // Cap at contract-defined max
52
+ }
53
+ /**
54
+ * Maps error codes to retry types
55
+ */
56
+ function mapErrorCodeToRetryType(code) {
57
+ const codeUpper = code.toUpperCase();
58
+ if (codeUpper.includes('TIMEOUT')) {
59
+ return 'timeout';
60
+ }
61
+ if (codeUpper.includes('RATE_LIMIT') || codeUpper.includes('RATE_LIMITED')) {
62
+ return 'rate_limit';
63
+ }
64
+ if (codeUpper.includes('SERVER_ERROR') ||
65
+ codeUpper.includes('INTERNAL_ERROR') ||
66
+ codeUpper.startsWith('5')) {
67
+ return 'server_error';
68
+ }
69
+ if (codeUpper.includes('NETWORK') ||
70
+ codeUpper.includes('CONNECTION') ||
71
+ codeUpper.includes('ECONNREFUSED')) {
72
+ return 'network_error';
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Sleeps for a specified duration
78
+ */
79
+ export function sleep(ms) {
80
+ return new Promise((resolve) => setTimeout(resolve, ms));
81
+ }
82
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAGlC,wCAAwC;AACxC,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAoB;IAEpB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,2CAA2C;IAC3C,sEAAsE;IACtE,MAAM,OAAO,GAAG,MAA8B,CAAC;IAC/C,OAAO;QACL,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,oBAAoB,CAAC,WAAW;QACpE,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,oBAAoB,CAAC,SAAS;QAC9D,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,IAAI,oBAAoB,CAAC,iBAAiB;QACtF,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,oBAAoB,CAAC,OAAO;KACzD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAgB,EAChB,MAA6B,EAC7B,cAAsB;IAEtB,sCAAsC;IACtC,IAAI,cAAc,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,kCAAkC;IAClC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,mDAAmD;IACnD,MAAM,SAAS,GAAG,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iEAAiE;IACjE,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAA6B,EAC7B,OAAe;IAEf,0DAA0D;IAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,8BAA8B;AAChF,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAC9B,IAAY;IAEZ,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAErC,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAC3E,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IACE,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC;QAClC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QACpC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EACzB,CAAC;QACD,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,IACE,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC7B,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC;QAChC,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,EAClC,CAAC;QACD,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { WorkflowResult, WorkflowRunnerConfig } from './types.js';
2
+ /**
3
+ * Workflow runner that executes workflows following contract invariants
4
+ *
5
+ * Invariants enforced:
6
+ * - INV-WF-001: Step execution order matches definition exactly
7
+ * - INV-WF-002: Retries are scoped to the current step only
8
+ * - INV-WF-003: Schema strictness (via validation)
9
+ * - INV-WF-004: Step ID uniqueness (via validation)
10
+ * - INV-WF-005: Immutable definition (via freezing)
11
+ */
12
+ export declare class WorkflowRunner {
13
+ private readonly config;
14
+ constructor(config?: WorkflowRunnerConfig);
15
+ /**
16
+ * Executes a workflow
17
+ * INV-WF-001: Steps are executed in definition order
18
+ */
19
+ run(workflowData: unknown, input?: unknown): Promise<WorkflowResult>;
20
+ /**
21
+ * Executes a single step with retry logic
22
+ * INV-WF-002: Retries are scoped to current step only
23
+ */
24
+ private executeStepWithRetry;
25
+ /**
26
+ * Executes a step with optional timeout
27
+ */
28
+ private executeStepWithTimeout;
29
+ /**
30
+ * Creates an error result
31
+ */
32
+ private createErrorResult;
33
+ }
34
+ /**
35
+ * Creates a workflow runner with the given configuration
36
+ */
37
+ export declare function createWorkflowRunner(config?: WorkflowRunnerConfig): WorkflowRunner;
38
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EAKrB,MAAM,YAAY,CAAC;AAqBpB;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;gBAE5B,MAAM,GAAE,oBAAyB;IAc7C;;;OAGG;IACG,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IA6E1E;;;OAGG;YACW,oBAAoB;IAsElC;;OAEG;YACW,sBAAsB;IAsCpC;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAqB1B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,CAAC,EAAE,oBAAoB,GAC5B,cAAc,CAEhB"}