0pflow 0.1.0-dev.4fd2ac2 → 0.1.0-dev.582d64d

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 (204) hide show
  1. package/dist/__tests__/discover.integration.test.d.ts +2 -0
  2. package/dist/__tests__/discover.integration.test.d.ts.map +1 -0
  3. package/dist/__tests__/discover.integration.test.js +137 -0
  4. package/dist/__tests__/discover.integration.test.js.map +1 -0
  5. package/dist/__tests__/factory.test.js +7 -0
  6. package/dist/__tests__/factory.test.js.map +1 -1
  7. package/dist/__tests__/integration.e2e.test.js +2 -1
  8. package/dist/__tests__/integration.e2e.test.js.map +1 -1
  9. package/dist/__tests__/integration.test.js +87 -82
  10. package/dist/__tests__/integration.test.js.map +1 -1
  11. package/dist/agent.d.ts +7 -0
  12. package/dist/agent.d.ts.map +1 -1
  13. package/dist/agent.js +59 -9
  14. package/dist/agent.js.map +1 -1
  15. package/dist/cli/__tests__/discovery.test.js +1 -1
  16. package/dist/cli/__tests__/discovery.test.js.map +1 -1
  17. package/dist/cli/app.d.ts +6 -0
  18. package/dist/cli/app.d.ts.map +1 -1
  19. package/dist/cli/app.js +27 -0
  20. package/dist/cli/app.js.map +1 -1
  21. package/dist/cli/discovery.d.ts +10 -0
  22. package/dist/cli/discovery.d.ts.map +1 -1
  23. package/dist/cli/discovery.js +42 -0
  24. package/dist/cli/discovery.js.map +1 -1
  25. package/dist/cli/env.js +1 -1
  26. package/dist/cli/env.js.map +1 -1
  27. package/dist/cli/index.d.ts.map +1 -1
  28. package/dist/cli/index.js +124 -11
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/cli/install.d.ts.map +1 -1
  31. package/dist/cli/install.js +39 -9
  32. package/dist/cli/install.js.map +1 -1
  33. package/dist/cli/mcp/lib/scaffolding.d.ts +33 -0
  34. package/dist/cli/mcp/lib/scaffolding.d.ts.map +1 -0
  35. package/dist/cli/mcp/lib/scaffolding.js +219 -0
  36. package/dist/cli/mcp/lib/scaffolding.js.map +1 -0
  37. package/dist/cli/mcp/lib/templates.d.ts +1 -0
  38. package/dist/cli/mcp/lib/templates.d.ts.map +1 -1
  39. package/dist/cli/mcp/lib/templates.js.map +1 -1
  40. package/dist/cli/mcp/tools/createApp.d.ts +1 -0
  41. package/dist/cli/mcp/tools/createApp.d.ts.map +1 -1
  42. package/dist/cli/mcp/tools/createApp.js +12 -55
  43. package/dist/cli/mcp/tools/createApp.js.map +1 -1
  44. package/dist/cli/mcp/tools/createDatabase.d.ts.map +1 -1
  45. package/dist/cli/mcp/tools/createDatabase.js +2 -41
  46. package/dist/cli/mcp/tools/createDatabase.js.map +1 -1
  47. package/dist/cli/mcp/tools/getConnectionInfo.d.ts +19 -0
  48. package/dist/cli/mcp/tools/getConnectionInfo.d.ts.map +1 -0
  49. package/dist/cli/mcp/tools/getConnectionInfo.js +111 -0
  50. package/dist/cli/mcp/tools/getConnectionInfo.js.map +1 -0
  51. package/dist/cli/mcp/tools/getRun.d.ts +22 -0
  52. package/dist/cli/mcp/tools/getRun.d.ts.map +1 -0
  53. package/dist/cli/mcp/tools/getRun.js +80 -0
  54. package/dist/cli/mcp/tools/getRun.js.map +1 -0
  55. package/dist/cli/mcp/tools/getTrace.d.ts +32 -0
  56. package/dist/cli/mcp/tools/getTrace.d.ts.map +1 -0
  57. package/dist/cli/mcp/tools/getTrace.js +104 -0
  58. package/dist/cli/mcp/tools/getTrace.js.map +1 -0
  59. package/dist/cli/mcp/tools/index.d.ts +94 -1
  60. package/dist/cli/mcp/tools/index.d.ts.map +1 -1
  61. package/dist/cli/mcp/tools/index.js +16 -0
  62. package/dist/cli/mcp/tools/index.js.map +1 -1
  63. package/dist/cli/mcp/tools/listIntegrations.d.ts +14 -0
  64. package/dist/cli/mcp/tools/listIntegrations.d.ts.map +1 -0
  65. package/dist/cli/mcp/tools/listIntegrations.js +53 -0
  66. package/dist/cli/mcp/tools/listIntegrations.js.map +1 -0
  67. package/dist/cli/mcp/tools/listRuns.d.ts +21 -0
  68. package/dist/cli/mcp/tools/listRuns.d.ts.map +1 -0
  69. package/dist/cli/mcp/tools/listRuns.js +72 -0
  70. package/dist/cli/mcp/tools/listRuns.js.map +1 -0
  71. package/dist/cli/mcp/tools/listWorkflows.d.ts +15 -0
  72. package/dist/cli/mcp/tools/listWorkflows.d.ts.map +1 -0
  73. package/dist/cli/mcp/tools/listWorkflows.js +45 -0
  74. package/dist/cli/mcp/tools/listWorkflows.js.map +1 -0
  75. package/dist/cli/mcp/tools/runNode.d.ts +17 -0
  76. package/dist/cli/mcp/tools/runNode.d.ts.map +1 -0
  77. package/dist/cli/mcp/tools/runNode.js +74 -0
  78. package/dist/cli/mcp/tools/runNode.js.map +1 -0
  79. package/dist/cli/mcp/tools/runWorkflow.d.ts +16 -0
  80. package/dist/cli/mcp/tools/runWorkflow.d.ts.map +1 -0
  81. package/dist/cli/mcp/tools/runWorkflow.js +66 -0
  82. package/dist/cli/mcp/tools/runWorkflow.js.map +1 -0
  83. package/dist/cli/mcp/tools/setupAppSchema.d.ts +1 -1
  84. package/dist/cli/mcp/tools/setupAppSchema.d.ts.map +1 -1
  85. package/dist/cli/mcp/tools/setupAppSchema.js +11 -132
  86. package/dist/cli/mcp/tools/setupAppSchema.js.map +1 -1
  87. package/dist/cli/mcp/tools/utils.d.ts +7 -0
  88. package/dist/cli/mcp/tools/utils.d.ts.map +1 -0
  89. package/dist/cli/mcp/tools/utils.js +28 -0
  90. package/dist/cli/mcp/tools/utils.js.map +1 -0
  91. package/dist/cli/run.d.ts +2 -0
  92. package/dist/cli/run.d.ts.map +1 -0
  93. package/dist/cli/run.js +391 -0
  94. package/dist/cli/run.js.map +1 -0
  95. package/dist/cli/trace.d.ts +5 -0
  96. package/dist/cli/trace.d.ts.map +1 -1
  97. package/dist/cli/trace.js +1 -1
  98. package/dist/cli/trace.js.map +1 -1
  99. package/dist/connections/cloud-auth.d.ts +46 -0
  100. package/dist/connections/cloud-auth.d.ts.map +1 -0
  101. package/dist/connections/cloud-auth.js +243 -0
  102. package/dist/connections/cloud-auth.js.map +1 -0
  103. package/dist/connections/cloud-client.d.ts +25 -0
  104. package/dist/connections/cloud-client.d.ts.map +1 -0
  105. package/dist/connections/cloud-client.js +59 -0
  106. package/dist/connections/cloud-client.js.map +1 -0
  107. package/dist/connections/cloud-integration-provider.d.ts +21 -0
  108. package/dist/connections/cloud-integration-provider.d.ts.map +1 -0
  109. package/dist/connections/cloud-integration-provider.js +26 -0
  110. package/dist/connections/cloud-integration-provider.js.map +1 -0
  111. package/dist/connections/index.d.ts +11 -0
  112. package/dist/connections/index.d.ts.map +1 -0
  113. package/dist/connections/index.js +9 -0
  114. package/dist/connections/index.js.map +1 -0
  115. package/dist/connections/integration-provider.d.ts +38 -0
  116. package/dist/connections/integration-provider.d.ts.map +1 -0
  117. package/dist/connections/integration-provider.js +20 -0
  118. package/dist/connections/integration-provider.js.map +1 -0
  119. package/dist/connections/local-integration-provider.d.ts +28 -0
  120. package/dist/connections/local-integration-provider.d.ts.map +1 -0
  121. package/dist/connections/local-integration-provider.js +54 -0
  122. package/dist/connections/local-integration-provider.js.map +1 -0
  123. package/dist/connections/nango-client.d.ts +14 -0
  124. package/dist/connections/nango-client.d.ts.map +1 -0
  125. package/dist/connections/nango-client.js +50 -0
  126. package/dist/connections/nango-client.js.map +1 -0
  127. package/dist/connections/resolver.d.ts +26 -0
  128. package/dist/connections/resolver.d.ts.map +1 -0
  129. package/dist/connections/resolver.js +48 -0
  130. package/dist/connections/resolver.js.map +1 -0
  131. package/dist/connections/schema.d.ts +8 -0
  132. package/dist/connections/schema.d.ts.map +1 -0
  133. package/dist/connections/schema.js +31 -0
  134. package/dist/connections/schema.js.map +1 -0
  135. package/dist/context.d.ts.map +1 -1
  136. package/dist/context.js +4 -0
  137. package/dist/context.js.map +1 -1
  138. package/dist/dev-ui/api.d.ts +16 -0
  139. package/dist/dev-ui/api.d.ts.map +1 -0
  140. package/dist/dev-ui/api.js +237 -0
  141. package/dist/dev-ui/api.js.map +1 -0
  142. package/dist/dev-ui/dag/extractor.d.ts +19 -0
  143. package/dist/dev-ui/dag/extractor.d.ts.map +1 -0
  144. package/dist/dev-ui/dag/extractor.js +716 -0
  145. package/dist/dev-ui/dag/extractor.js.map +1 -0
  146. package/dist/dev-ui/dag/types.d.ts +42 -0
  147. package/dist/dev-ui/dag/types.d.ts.map +1 -0
  148. package/dist/dev-ui/dag/types.js +2 -0
  149. package/dist/dev-ui/dag/types.js.map +1 -0
  150. package/dist/dev-ui/dev-server.d.ts +18 -0
  151. package/dist/dev-ui/dev-server.d.ts.map +1 -0
  152. package/dist/dev-ui/dev-server.js +222 -0
  153. package/dist/dev-ui/dev-server.js.map +1 -0
  154. package/dist/dev-ui/index.d.ts +3 -0
  155. package/dist/dev-ui/index.d.ts.map +1 -0
  156. package/dist/dev-ui/index.js +2 -0
  157. package/dist/dev-ui/index.js.map +1 -0
  158. package/dist/dev-ui/pty.d.ts +16 -0
  159. package/dist/dev-ui/pty.d.ts.map +1 -0
  160. package/dist/dev-ui/pty.js +87 -0
  161. package/dist/dev-ui/pty.js.map +1 -0
  162. package/dist/dev-ui/watcher.d.ts +12 -0
  163. package/dist/dev-ui/watcher.d.ts.map +1 -0
  164. package/dist/dev-ui/watcher.js +162 -0
  165. package/dist/dev-ui/watcher.js.map +1 -0
  166. package/dist/dev-ui/ws.d.ts +52 -0
  167. package/dist/dev-ui/ws.d.ts.map +1 -0
  168. package/dist/dev-ui/ws.js +32 -0
  169. package/dist/dev-ui/ws.js.map +1 -0
  170. package/dist/dev-ui-client/assets/index-C-LxzUII.css +32 -0
  171. package/dist/dev-ui-client/assets/index-DAKTQEvj.js +1 -0
  172. package/dist/dev-ui-client/assets/index-aAIwXl4O.js +127 -0
  173. package/dist/dev-ui-client/index.html +13 -0
  174. package/dist/discover.d.ts +15 -0
  175. package/dist/discover.d.ts.map +1 -0
  176. package/dist/discover.js +29 -0
  177. package/dist/discover.js.map +1 -0
  178. package/dist/factory.d.ts.map +1 -1
  179. package/dist/factory.js +25 -12
  180. package/dist/factory.js.map +1 -1
  181. package/dist/index.d.ts +5 -1
  182. package/dist/index.d.ts.map +1 -1
  183. package/dist/index.js +4 -0
  184. package/dist/index.js.map +1 -1
  185. package/dist/node.d.ts +1 -0
  186. package/dist/node.d.ts.map +1 -1
  187. package/dist/node.js +1 -0
  188. package/dist/node.js.map +1 -1
  189. package/dist/nodes/agent/executor.d.ts +2 -0
  190. package/dist/nodes/agent/executor.d.ts.map +1 -1
  191. package/dist/nodes/agent/executor.js +11 -1
  192. package/dist/nodes/agent/executor.js.map +1 -1
  193. package/dist/types.d.ts +21 -3
  194. package/dist/types.d.ts.map +1 -1
  195. package/dist/workflow.d.ts +22 -0
  196. package/dist/workflow.d.ts.map +1 -1
  197. package/dist/workflow.js +97 -2
  198. package/dist/workflow.js.map +1 -1
  199. package/package.json +26 -5
  200. package/templates/app/dbos-config.yaml +6 -0
  201. package/templates/app/package.json +4 -1
  202. package/templates/app/src/app/api/workflow/[name]/route.ts +37 -0
  203. package/templates/app/src/instrumentation.ts +6 -0
  204. package/templates/app/src/lib/pflow.ts +29 -0
@@ -0,0 +1,716 @@
1
+ import { createRequire } from "node:module";
2
+ import { resolve, dirname } from "node:path";
3
+ let parserReady = null;
4
+ async function getParser() {
5
+ if (parserReady)
6
+ return parserReady;
7
+ parserReady = (async () => {
8
+ const TreeSitter = (await import("web-tree-sitter")).default;
9
+ await TreeSitter.init();
10
+ // Locate the .wasm file from the tree-sitter-typescript package
11
+ // Use createRequire so Node's module resolution handles hoisted packages correctly
12
+ const require = createRequire(import.meta.url);
13
+ let wasmPath;
14
+ try {
15
+ const tsPkgJson = require.resolve("tree-sitter-typescript/package.json");
16
+ wasmPath = resolve(dirname(tsPkgJson), "tree-sitter-typescript.wasm");
17
+ }
18
+ catch {
19
+ throw new Error("Could not find tree-sitter-typescript.wasm. Make sure tree-sitter-typescript is installed.");
20
+ }
21
+ const TypeScript = await TreeSitter.Language.load(wasmPath);
22
+ const parser = new TreeSitter();
23
+ parser.setLanguage(TypeScript);
24
+ return parser;
25
+ })();
26
+ return parserReady;
27
+ }
28
+ /**
29
+ * Determine executable type from import path.
30
+ */
31
+ function inferType(importSource) {
32
+ if (importSource === "0pflow" || importSource === "0pflow/nodes")
33
+ return "node";
34
+ if (importSource.includes("agents/"))
35
+ return "agent";
36
+ if (importSource.includes("workflows/"))
37
+ return "workflow";
38
+ if (importSource.includes("nodes/"))
39
+ return "node";
40
+ return "node";
41
+ }
42
+ /**
43
+ * Convert a camelCase or PascalCase identifier to a human-readable label.
44
+ * e.g. "querySalesforceLeads" → "Query Salesforce Leads"
45
+ * "pageSummarizer" → "Page Summarizer"
46
+ * "webRead" → "Web Read"
47
+ */
48
+ function humanize(identifier) {
49
+ // Insert spaces before uppercase letters (camelCase → camel Case)
50
+ const spaced = identifier.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
51
+ // Capitalize first letter
52
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
53
+ }
54
+ function extractStringProperty(obj, propName) {
55
+ for (const prop of obj.namedChildren) {
56
+ if (prop.type === "pair" || prop.type === "property_assignment") {
57
+ const key = prop.namedChildren[0];
58
+ const value = prop.namedChildren[1];
59
+ if (key?.text === propName && value) {
60
+ if (value.type === "template_string") {
61
+ // Strip backticks
62
+ return value.text.replace(/^`|`$/g, "").trim();
63
+ }
64
+ if (value.type === "string") {
65
+ return value.text.replace(/^['"]|['"]$/g, "").trim();
66
+ }
67
+ }
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ /**
73
+ * Extract a string array property (e.g. integrations: ["salesforce", "hubspot"]) from an object node.
74
+ */
75
+ function extractStringArrayProperty(obj, propName) {
76
+ for (const prop of obj.namedChildren) {
77
+ if (prop.type === "pair" || prop.type === "property_assignment") {
78
+ const key = prop.namedChildren[0];
79
+ const value = prop.namedChildren[1];
80
+ if (key?.text === propName && value?.type === "array") {
81
+ const items = [];
82
+ for (const elem of value.namedChildren) {
83
+ if (elem.type === "string") {
84
+ items.push(elem.text.replace(/^['"]|['"]$/g, ""));
85
+ }
86
+ }
87
+ return items.length > 0 ? items : null;
88
+ }
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Parse a node source file and extract the integrations array from its .create() config.
95
+ */
96
+ export async function extractNodeIntegrations(source) {
97
+ const parser = await getParser();
98
+ const tree = parser.parse(source);
99
+ const root = tree.rootNode;
100
+ const calls = root.descendantsOfType("call_expression");
101
+ for (const call of calls) {
102
+ const fn = call.namedChildren[0];
103
+ if (!fn || fn.type !== "member_expression")
104
+ continue;
105
+ const prop = fn.namedChildren[1];
106
+ if (prop?.text !== "create")
107
+ continue;
108
+ const args = call.descendantsOfType("arguments")[0];
109
+ if (!args)
110
+ continue;
111
+ const obj = args.descendantsOfType("object")[0];
112
+ if (!obj)
113
+ continue;
114
+ const integrations = extractStringArrayProperty(obj, "integrations");
115
+ if (integrations)
116
+ return integrations;
117
+ }
118
+ return undefined;
119
+ }
120
+ /**
121
+ * Parse a node source file and extract the name from its .create() config.
122
+ */
123
+ export async function extractNodeName(source) {
124
+ const parser = await getParser();
125
+ const tree = parser.parse(source);
126
+ const root = tree.rootNode;
127
+ const calls = root.descendantsOfType("call_expression");
128
+ for (const call of calls) {
129
+ const fn = call.namedChildren[0];
130
+ if (!fn || fn.type !== "member_expression")
131
+ continue;
132
+ const prop = fn.namedChildren[1];
133
+ if (prop?.text !== "create")
134
+ continue;
135
+ const args = call.descendantsOfType("arguments")[0];
136
+ if (!args)
137
+ continue;
138
+ const obj = args.descendantsOfType("object")[0];
139
+ if (!obj)
140
+ continue;
141
+ const name = extractStringProperty(obj, "name");
142
+ if (name)
143
+ return name;
144
+ }
145
+ return undefined;
146
+ }
147
+ /**
148
+ * Parse a node source file and extract the description from its .create() config.
149
+ */
150
+ export async function extractNodeDescription(source) {
151
+ const parser = await getParser();
152
+ const tree = parser.parse(source);
153
+ const root = tree.rootNode;
154
+ // Find any .create() call
155
+ const calls = root.descendantsOfType("call_expression");
156
+ for (const call of calls) {
157
+ const fn = call.namedChildren[0];
158
+ if (!fn || fn.type !== "member_expression")
159
+ continue;
160
+ const prop = fn.namedChildren[1];
161
+ if (prop?.text !== "create")
162
+ continue;
163
+ const args = call.descendantsOfType("arguments")[0];
164
+ if (!args)
165
+ continue;
166
+ const obj = args.descendantsOfType("object")[0];
167
+ if (!obj)
168
+ continue;
169
+ const desc = extractStringProperty(obj, "description");
170
+ if (desc)
171
+ return desc;
172
+ }
173
+ return undefined;
174
+ }
175
+ function extractImports(root) {
176
+ const imports = new Map();
177
+ const importStatements = root.descendantsOfType("import_statement");
178
+ for (const stmt of importStatements) {
179
+ const sourceNode = stmt.descendantsOfType("string")[0];
180
+ if (!sourceNode)
181
+ continue;
182
+ const source = sourceNode.text.replace(/^['"]|['"]$/g, "");
183
+ const importSpecifiers = stmt.descendantsOfType("import_specifier");
184
+ for (const spec of importSpecifiers) {
185
+ const names = spec.namedChildren;
186
+ const identifier = names.length > 1 ? names[1].text : names[0]?.text;
187
+ if (identifier) {
188
+ imports.set(identifier, { identifier, source });
189
+ }
190
+ }
191
+ const identifiers = stmt.descendantsOfType("identifier");
192
+ for (const id of identifiers) {
193
+ if (id.parent?.type === "import_clause") {
194
+ imports.set(id.text, { identifier: id.text, source });
195
+ }
196
+ }
197
+ }
198
+ return imports;
199
+ }
200
+ function findWorkflowCreateCalls(root) {
201
+ const calls = root.descendantsOfType("call_expression");
202
+ const results = [];
203
+ for (const call of calls) {
204
+ const fn = call.namedChildren[0];
205
+ if (!fn)
206
+ continue;
207
+ if (fn.type === "member_expression") {
208
+ const obj = fn.namedChildren[0];
209
+ const prop = fn.namedChildren[1];
210
+ if (obj?.text === "Workflow" && prop?.text === "create") {
211
+ results.push(call);
212
+ }
213
+ }
214
+ }
215
+ return results;
216
+ }
217
+ function extractWorkflowName(createCall) {
218
+ const args = createCall.descendantsOfType("arguments")[0];
219
+ if (!args)
220
+ return null;
221
+ const obj = args.descendantsOfType("object")[0];
222
+ if (!obj)
223
+ return null;
224
+ for (const prop of obj.namedChildren) {
225
+ if (prop.type === "pair" || prop.type === "property_assignment") {
226
+ const key = prop.namedChildren[0];
227
+ const value = prop.namedChildren[1];
228
+ if (key?.text === "name" && value) {
229
+ return value.text.replace(/^['"]|['"]$/g, "");
230
+ }
231
+ }
232
+ }
233
+ return null;
234
+ }
235
+ function extractWorkflowVersion(createCall) {
236
+ const args = createCall.descendantsOfType("arguments")[0];
237
+ if (!args)
238
+ return 1;
239
+ const obj = args.descendantsOfType("object")[0];
240
+ if (!obj)
241
+ return 1;
242
+ for (const prop of obj.namedChildren) {
243
+ if (prop.type === "pair" || prop.type === "property_assignment") {
244
+ const key = prop.namedChildren[0];
245
+ const value = prop.namedChildren[1];
246
+ if (key?.text === "version" && value) {
247
+ const n = parseInt(value.text, 10);
248
+ return isNaN(n) ? 1 : n;
249
+ }
250
+ }
251
+ }
252
+ return 1;
253
+ }
254
+ /**
255
+ * Extract field names from a z.object({...}) schema variable.
256
+ * Given the schema variable name (e.g. "UrlSummarizerInputSchema"),
257
+ * find its declaration and extract the top-level keys from z.object({...}).
258
+ */
259
+ function extractSchemaFields(root, schemaVarName) {
260
+ // Find variable declaration: const <name> = z.object({...})
261
+ const declarations = root.descendantsOfType("variable_declarator");
262
+ for (const decl of declarations) {
263
+ const nameNode = decl.namedChildren[0];
264
+ if (nameNode?.text !== schemaVarName)
265
+ continue;
266
+ // Find the z.object call in the initializer
267
+ const calls = decl.descendantsOfType("call_expression");
268
+ for (const call of calls) {
269
+ const fn = call.namedChildren[0];
270
+ if (!fn || fn.type !== "member_expression")
271
+ continue;
272
+ if (fn.namedChildren[0]?.text !== "z" || fn.namedChildren[1]?.text !== "object")
273
+ continue;
274
+ // Found z.object(...) — extract keys from the object argument
275
+ const args = call.descendantsOfType("arguments")[0];
276
+ if (!args)
277
+ continue;
278
+ const obj = args.descendantsOfType("object")[0];
279
+ if (!obj)
280
+ continue;
281
+ const fields = [];
282
+ for (const prop of obj.namedChildren) {
283
+ if (prop.type === "pair" || prop.type === "property_assignment") {
284
+ const key = prop.namedChildren[0];
285
+ if (key)
286
+ fields.push(key.text);
287
+ }
288
+ if (prop.type === "shorthand_property_identifier" || prop.type === "shorthand_property_identifier_pattern") {
289
+ fields.push(prop.text);
290
+ }
291
+ }
292
+ return fields;
293
+ }
294
+ }
295
+ return [];
296
+ }
297
+ /**
298
+ * Get the schema variable name from the Workflow.create() config object.
299
+ * Looks for inputSchema/outputSchema property values.
300
+ */
301
+ function getSchemaVarName(createCall, propName) {
302
+ const args = createCall.descendantsOfType("arguments")[0];
303
+ if (!args)
304
+ return null;
305
+ const obj = args.descendantsOfType("object")[0];
306
+ if (!obj)
307
+ return null;
308
+ for (const prop of obj.namedChildren) {
309
+ if (prop.type === "pair" || prop.type === "property_assignment") {
310
+ const key = prop.namedChildren[0];
311
+ const value = prop.namedChildren[1];
312
+ if (key?.text === propName && value) {
313
+ return value.text;
314
+ }
315
+ }
316
+ }
317
+ return null;
318
+ }
319
+ function findRunMethod(createCall) {
320
+ const args = createCall.descendantsOfType("arguments")[0];
321
+ if (!args)
322
+ return null;
323
+ const obj = args.descendantsOfType("object")[0];
324
+ if (!obj)
325
+ return null;
326
+ for (const prop of obj.namedChildren) {
327
+ if (prop.type === "method_definition") {
328
+ const nameNode = prop.childForFieldName("name");
329
+ if (nameNode?.text === "run")
330
+ return prop;
331
+ }
332
+ if (prop.type === "pair" || prop.type === "property_assignment") {
333
+ const key = prop.namedChildren[0];
334
+ if (key?.text === "run") {
335
+ return prop;
336
+ }
337
+ }
338
+ }
339
+ return null;
340
+ }
341
+ function getContextParamName(runMethod) {
342
+ const params = runMethod.descendantsOfType("formal_parameters")[0];
343
+ if (!params)
344
+ return "ctx";
345
+ const firstParam = params.namedChildren[0];
346
+ if (!firstParam)
347
+ return "ctx";
348
+ if (firstParam.type === "required_parameter" || firstParam.type === "optional_parameter") {
349
+ const pattern = firstParam.childForFieldName("pattern");
350
+ return pattern?.text ?? firstParam.namedChildren[0]?.text ?? "ctx";
351
+ }
352
+ return firstParam.text ?? "ctx";
353
+ }
354
+ function extractCtxRunCalls(body, ctxName) {
355
+ const calls = [];
356
+ const allCalls = body.descendantsOfType("call_expression");
357
+ for (const call of allCalls) {
358
+ const fn = call.namedChildren[0];
359
+ if (!fn || fn.type !== "member_expression")
360
+ continue;
361
+ const obj = fn.namedChildren[0];
362
+ const prop = fn.namedChildren[1];
363
+ if (obj?.text !== ctxName || prop?.text !== "run")
364
+ continue;
365
+ const args = call.descendantsOfType("arguments")[0];
366
+ if (!args)
367
+ continue;
368
+ const firstArg = args.namedChildren[0];
369
+ if (!firstArg)
370
+ continue;
371
+ calls.push({
372
+ executableIdentifier: firstArg.text,
373
+ lineNumber: call.startPosition.row + 1,
374
+ node: call,
375
+ });
376
+ }
377
+ return calls;
378
+ }
379
+ const LOOP_TYPES = new Set([
380
+ "for_statement",
381
+ "for_in_statement",
382
+ "while_statement",
383
+ "do_statement",
384
+ ]);
385
+ function findEnclosingLoop(callNode, runMethodBody) {
386
+ let current = callNode;
387
+ while (current && current !== runMethodBody) {
388
+ if (LOOP_TYPES.has(current.type))
389
+ return current;
390
+ current = current.parent;
391
+ }
392
+ return null;
393
+ }
394
+ function extractLoopLabel(loopNode) {
395
+ if (loopNode.type === "for_in_statement") {
396
+ // Covers for...of and for...in
397
+ // Structure: for (const <var> of/in <iterable>) { ... }
398
+ const left = loopNode.childForFieldName("left");
399
+ const right = loopNode.childForFieldName("right");
400
+ // Determine if it's "of" or "in" by checking the text between left and right
401
+ const keyword = loopNode.text.includes(" of ") ? "of" : "in";
402
+ const varName = left?.text.replace(/^(const|let|var)\s+/, "") ?? "item";
403
+ const iterableName = right?.text ?? "items";
404
+ return `for each ${varName} ${keyword} ${iterableName}`;
405
+ }
406
+ if (loopNode.type === "while_statement") {
407
+ const condition = loopNode.childForFieldName("condition");
408
+ return `while ${condition?.text ?? "(...)"}`;
409
+ }
410
+ // for_statement, do_statement
411
+ return "for loop";
412
+ }
413
+ function getEnclosingIfCondition(callNode, runMethodBody) {
414
+ let current = callNode;
415
+ while (current && current !== runMethodBody) {
416
+ if (current.type === "if_statement") {
417
+ const condition = current.childForFieldName("condition");
418
+ const consequence = current.childForFieldName("consequence");
419
+ const alternative = current.childForFieldName("alternative");
420
+ const isInConsequence = consequence && isDescendantOf(callNode, consequence);
421
+ const isInAlternative = alternative && isDescendantOf(callNode, alternative);
422
+ return {
423
+ condition: condition?.text ?? "?",
424
+ isElseBranch: !isInConsequence && !!isInAlternative,
425
+ };
426
+ }
427
+ current = current.parent;
428
+ }
429
+ return null;
430
+ }
431
+ function isDescendantOf(node, ancestor) {
432
+ let current = node;
433
+ while (current) {
434
+ if (current === ancestor)
435
+ return true;
436
+ current = current.parent;
437
+ }
438
+ return false;
439
+ }
440
+ function isFollowedByReturn(callNode, runMethodBody) {
441
+ let stmt = callNode;
442
+ while (stmt && stmt.type !== "expression_statement" && stmt !== runMethodBody) {
443
+ stmt = stmt.parent;
444
+ }
445
+ if (!stmt || stmt === runMethodBody)
446
+ return false;
447
+ const parent = stmt.parent;
448
+ if (!parent)
449
+ return false;
450
+ const children = parent.namedChildren;
451
+ const idx = children.indexOf(stmt);
452
+ if (idx < 0 || idx >= children.length - 1)
453
+ return false;
454
+ const next = children[idx + 1];
455
+ return next?.type === "return_statement";
456
+ }
457
+ function findGuardClausesBetween(runBody, afterLine, beforeLine) {
458
+ const guards = [];
459
+ for (const child of runBody.namedChildren) {
460
+ const line = child.startPosition.row + 1;
461
+ if (line <= afterLine || line >= beforeLine)
462
+ continue;
463
+ if (child.type === "if_statement") {
464
+ const consequence = child.childForFieldName("consequence");
465
+ if (!consequence)
466
+ continue;
467
+ const returns = consequence.descendantsOfType("return_statement");
468
+ if (returns.length > 0) {
469
+ const condition = child.childForFieldName("condition");
470
+ guards.push({
471
+ condition: condition?.text ?? "?",
472
+ lineNumber: line,
473
+ });
474
+ }
475
+ }
476
+ }
477
+ return guards;
478
+ }
479
+ /**
480
+ * Parse a single TypeScript file and extract workflow DAGs from it.
481
+ * Fault-tolerant: returns whatever it can parse, even from broken files.
482
+ */
483
+ export async function extractDAGs(filePath, source) {
484
+ const parser = await getParser();
485
+ const tree = parser.parse(source);
486
+ const root = tree.rootNode;
487
+ const imports = extractImports(root);
488
+ const workflowCalls = findWorkflowCreateCalls(root);
489
+ const dags = [];
490
+ // Fallback for broken files: if no complete Workflow.create() call found,
491
+ // look for a Workflow.create member expression + any ctx.run() calls in the file
492
+ if (workflowCalls.length === 0) {
493
+ const memberExprs = root.descendantsOfType("member_expression");
494
+ const hasWorkflowCreate = memberExprs.some((m) => m.namedChildren[0]?.text === "Workflow" && m.namedChildren[1]?.text === "create");
495
+ if (hasWorkflowCreate) {
496
+ const allCalls = root.descendantsOfType("call_expression");
497
+ const ctxRunCalls = [];
498
+ for (const call of allCalls) {
499
+ const fn = call.namedChildren[0];
500
+ if (!fn || fn.type !== "member_expression")
501
+ continue;
502
+ const prop = fn.namedChildren[1];
503
+ if (prop?.text !== "run")
504
+ continue;
505
+ const obj = fn.namedChildren[0];
506
+ if (!obj || obj.text === "Workflow" || obj.text === "z")
507
+ continue;
508
+ const args = call.descendantsOfType("arguments")[0];
509
+ const firstArg = args?.namedChildren[0];
510
+ if (!firstArg)
511
+ continue;
512
+ ctxRunCalls.push({
513
+ executableIdentifier: firstArg.text,
514
+ lineNumber: call.startPosition.row + 1,
515
+ node: call,
516
+ });
517
+ }
518
+ if (ctxRunCalls.length > 0) {
519
+ const strings = root.descendantsOfType("string");
520
+ let workflowName = "partial";
521
+ for (const s of strings) {
522
+ const prev = s.parent?.namedChildren[0];
523
+ if (prev?.text === "name") {
524
+ workflowName = s.text.replace(/^['"]|['"]$/g, "");
525
+ break;
526
+ }
527
+ }
528
+ const nodes = [{ id: "input", label: "Input", type: "input" }];
529
+ const edges = [];
530
+ let prevId = "input";
531
+ for (let i = 0; i < ctxRunCalls.length; i++) {
532
+ const call = ctxRunCalls[i];
533
+ const importInfo = imports.get(call.executableIdentifier);
534
+ const nodeId = `step-${i}`;
535
+ nodes.push({
536
+ id: nodeId,
537
+ label: humanize(call.executableIdentifier),
538
+ type: importInfo ? inferType(importInfo.source) : "node",
539
+ executableName: call.executableIdentifier,
540
+ importPath: importInfo?.source,
541
+ lineNumber: call.lineNumber,
542
+ });
543
+ edges.push({ id: `${prevId}->${nodeId}`, source: prevId, target: nodeId });
544
+ prevId = nodeId;
545
+ }
546
+ nodes.push({ id: "output", label: "Output", type: "output" });
547
+ edges.push({ id: `${prevId}->output`, source: prevId, target: "output" });
548
+ dags.push({ workflowName, version: 1, filePath, nodes, edges });
549
+ }
550
+ }
551
+ return dags;
552
+ }
553
+ for (const createCall of workflowCalls) {
554
+ const workflowName = extractWorkflowName(createCall) ?? "unknown";
555
+ const version = extractWorkflowVersion(createCall);
556
+ // Extract schema fields for richer input/output nodes
557
+ const inputSchemaVar = getSchemaVarName(createCall, "inputSchema");
558
+ const outputSchemaVar = getSchemaVarName(createCall, "outputSchema");
559
+ const inputFields = inputSchemaVar ? extractSchemaFields(root, inputSchemaVar) : [];
560
+ const outputFields = outputSchemaVar ? extractSchemaFields(root, outputSchemaVar) : [];
561
+ const runMethod = findRunMethod(createCall);
562
+ if (!runMethod) {
563
+ dags.push({
564
+ workflowName,
565
+ version,
566
+ filePath,
567
+ nodes: [
568
+ { id: "input", label: "Input", type: "input", fields: inputFields.length > 0 ? inputFields : undefined },
569
+ { id: "output", label: "Output", type: "output", fields: outputFields.length > 0 ? outputFields : undefined },
570
+ ],
571
+ edges: [{ id: "input->output", source: "input", target: "output" }],
572
+ });
573
+ continue;
574
+ }
575
+ const ctxName = getContextParamName(runMethod);
576
+ const runBody = runMethod.descendantsOfType("statement_block")[0];
577
+ if (!runBody)
578
+ continue;
579
+ const ctxRunCalls = extractCtxRunCalls(runBody, ctxName);
580
+ const nodes = [];
581
+ const edges = [];
582
+ let condCounter = 0;
583
+ const loopGroupMap = new Map();
584
+ nodes.push({ id: "input", label: "Input", type: "input", fields: inputFields.length > 0 ? inputFields : undefined });
585
+ let prevNodeIds = ["input"];
586
+ let prevLine = 0;
587
+ for (let i = 0; i < ctxRunCalls.length; i++) {
588
+ const call = ctxRunCalls[i];
589
+ const importInfo = imports.get(call.executableIdentifier);
590
+ const execType = importInfo ? inferType(importInfo.source) : "node";
591
+ const nodeId = `step-${i}`;
592
+ const ifContext = getEnclosingIfCondition(call.node, runBody);
593
+ // Track loop group membership
594
+ const enclosingLoop = findEnclosingLoop(call.node, runBody);
595
+ if (enclosingLoop) {
596
+ if (!loopGroupMap.has(enclosingLoop)) {
597
+ loopGroupMap.set(enclosingLoop, {
598
+ label: extractLoopLabel(enclosingLoop),
599
+ stepIds: [],
600
+ });
601
+ }
602
+ loopGroupMap.get(enclosingLoop).stepIds.push(nodeId);
603
+ }
604
+ const guards = findGuardClausesBetween(runBody, prevLine, call.lineNumber);
605
+ for (const guard of guards) {
606
+ const condId = `condition-${condCounter++}`;
607
+ nodes.push({
608
+ id: condId,
609
+ label: guard.condition,
610
+ type: "condition",
611
+ lineNumber: guard.lineNumber,
612
+ });
613
+ for (const prev of prevNodeIds) {
614
+ edges.push({ id: `${prev}->${condId}`, source: prev, target: condId });
615
+ }
616
+ edges.push({ id: `${condId}->output-guard`, source: condId, target: "output", label: "yes" });
617
+ prevNodeIds = [condId];
618
+ }
619
+ if (ifContext) {
620
+ const condId = `condition-${condCounter++}`;
621
+ const conditionExists = nodes.some((n) => n.type === "condition" && n.label === ifContext.condition);
622
+ if (!conditionExists) {
623
+ nodes.push({
624
+ id: condId,
625
+ label: ifContext.condition,
626
+ type: "condition",
627
+ lineNumber: call.lineNumber,
628
+ });
629
+ for (const prev of prevNodeIds) {
630
+ edges.push({ id: `${prev}->${condId}`, source: prev, target: condId });
631
+ }
632
+ prevNodeIds = [condId];
633
+ }
634
+ nodes.push({
635
+ id: nodeId,
636
+ label: humanize(call.executableIdentifier),
637
+ type: execType,
638
+ executableName: call.executableIdentifier,
639
+ importPath: importInfo?.source,
640
+ lineNumber: call.lineNumber,
641
+ });
642
+ const sourceCondId = nodes.find((n) => n.type === "condition" && n.label === ifContext.condition)?.id ?? condId;
643
+ edges.push({
644
+ id: `${sourceCondId}->${nodeId}`,
645
+ source: sourceCondId,
646
+ target: nodeId,
647
+ label: ifContext.isElseBranch ? "else" : "then",
648
+ });
649
+ if (isFollowedByReturn(call.node, runBody)) {
650
+ edges.push({ id: `${nodeId}->output`, source: nodeId, target: "output" });
651
+ }
652
+ else {
653
+ prevNodeIds = [...prevNodeIds.filter((p) => p !== sourceCondId), nodeId];
654
+ }
655
+ }
656
+ else {
657
+ nodes.push({
658
+ id: nodeId,
659
+ label: humanize(call.executableIdentifier),
660
+ type: execType,
661
+ executableName: call.executableIdentifier,
662
+ importPath: importInfo?.source,
663
+ lineNumber: call.lineNumber,
664
+ });
665
+ for (const prev of prevNodeIds) {
666
+ edges.push({ id: `${prev}->${nodeId}`, source: prev, target: nodeId });
667
+ }
668
+ prevNodeIds = [nodeId];
669
+ }
670
+ prevLine = call.lineNumber;
671
+ }
672
+ const lastLine = ctxRunCalls.length > 0 ? ctxRunCalls[ctxRunCalls.length - 1].lineNumber : 0;
673
+ const trailingGuards = findGuardClausesBetween(runBody, lastLine, 999999);
674
+ for (const guard of trailingGuards) {
675
+ const condId = `condition-${condCounter++}`;
676
+ nodes.push({
677
+ id: condId,
678
+ label: guard.condition,
679
+ type: "condition",
680
+ lineNumber: guard.lineNumber,
681
+ });
682
+ for (const prev of prevNodeIds) {
683
+ edges.push({ id: `${prev}->${condId}`, source: prev, target: condId });
684
+ }
685
+ edges.push({ id: `${condId}->output-guard`, source: condId, target: "output", label: "yes" });
686
+ prevNodeIds = [condId];
687
+ }
688
+ nodes.push({ id: "output", label: "Output", type: "output", fields: outputFields.length > 0 ? outputFields : undefined });
689
+ for (const prev of prevNodeIds) {
690
+ const alreadyConnected = edges.some((e) => e.source === prev && e.target === "output");
691
+ if (!alreadyConnected) {
692
+ edges.push({ id: `${prev}->output`, source: prev, target: "output" });
693
+ }
694
+ }
695
+ // Build loop groups array
696
+ const loopGroups = [];
697
+ let loopCounter = 0;
698
+ for (const [, group] of loopGroupMap) {
699
+ loopGroups.push({
700
+ id: `loop-${loopCounter++}`,
701
+ label: group.label,
702
+ nodeIds: group.stepIds,
703
+ });
704
+ }
705
+ dags.push({
706
+ workflowName,
707
+ version,
708
+ filePath,
709
+ nodes,
710
+ edges,
711
+ loopGroups: loopGroups.length > 0 ? loopGroups : undefined,
712
+ });
713
+ }
714
+ return dags;
715
+ }
716
+ //# sourceMappingURL=extractor.js.map