@synergenius/flow-weaver 0.27.5 → 0.29.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.
@@ -5987,7 +5987,7 @@ var VERSION;
5987
5987
  var init_generated_version = __esm({
5988
5988
  "src/generated-version.ts"() {
5989
5989
  "use strict";
5990
- VERSION = "0.27.5";
5990
+ VERSION = "0.29.0";
5991
5991
  }
5992
5992
  });
5993
5993
 
@@ -6645,18 +6645,28 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
6645
6645
  }
6646
6646
  const safeChildId = toValidIdentifier(child.id);
6647
6647
  const awaitPrefix = isAsync2 ? "await " : "";
6648
+ const emitDebugHooks = !production;
6649
+ let childIndent = " ";
6648
6650
  lines.push(``);
6649
6651
  lines.push(` // Execute: ${child.id} (${child.nodeType})`);
6650
- lines.push(` scopedCtx.checkAborted('${child.id}');`);
6651
- lines.push(` const ${safeChildId}Idx = scopedCtx.addExecution('${child.id}');`);
6652
- lines.push(` if (typeof globalThis !== 'undefined') (globalThis as unknown as { __fw_current_node_id__?: string }).__fw_current_node_id__ = '${child.id}';`);
6653
- lines.push(` ${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
6654
- lines.push(` nodeTypeName: '${child.nodeType}',`);
6655
- lines.push(` id: '${child.id}',`);
6656
- lines.push(` executionIndex: ${safeChildId}Idx,`);
6657
- lines.push(` status: 'RUNNING',`);
6658
- lines.push(` });`);
6659
- lines.push(` try {`);
6652
+ if (emitDebugHooks) {
6653
+ const awaitHook = isAsync2 ? "await " : "";
6654
+ lines.push(` let ${safeChildId}Idx: number = -1;`);
6655
+ lines.push(` if (${awaitHook}__ctrl__.beforeNode('${child.id}', scopedCtx)) {`);
6656
+ childIndent = " ";
6657
+ }
6658
+ lines.push(`${childIndent}scopedCtx.checkAborted('${child.id}');`);
6659
+ const idxDecl = emitDebugHooks ? "" : "const ";
6660
+ lines.push(`${childIndent}${idxDecl}${safeChildId}Idx = scopedCtx.addExecution('${child.id}');`);
6661
+ lines.push(`${childIndent}if (typeof globalThis !== 'undefined') (globalThis as unknown as { __fw_current_node_id__?: string }).__fw_current_node_id__ = '${child.id}';`);
6662
+ lines.push(`${childIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
6663
+ lines.push(`${childIndent} nodeTypeName: '${child.nodeType}',`);
6664
+ lines.push(`${childIndent} id: '${child.id}',`);
6665
+ lines.push(`${childIndent} executionIndex: ${safeChildId}Idx,`);
6666
+ lines.push(`${childIndent} status: 'RUNNING',`);
6667
+ lines.push(`${childIndent}});`);
6668
+ lines.push(`${childIndent}try {`);
6669
+ const tryIndent = `${childIndent} `;
6660
6670
  const argLines = [];
6661
6671
  const getCall = isAsync2 ? "await scopedCtx.getVariable" : "scopedCtx.getVariable";
6662
6672
  const childSetCall = isAsync2 ? `await scopedCtx.setVariable` : `scopedCtx.setVariable`;
@@ -6673,10 +6683,10 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
6673
6683
  const targetPortDef = childNodeType.inputs[targetPort];
6674
6684
  const portType = targetPortDef ? mapToTypeScript(targetPortDef.dataType, targetPortDef.tsType) : "unknown";
6675
6685
  argLines.push(
6676
- ` const ${varName} = ${getCall}({ id: '${parentNodeId}', portName: '${conn.from.port}', executionIndex: ${scopeParamIdxVar} }) as ${portType};`
6686
+ `${tryIndent}const ${varName} = ${getCall}({ id: '${parentNodeId}', portName: '${conn.from.port}', executionIndex: ${scopeParamIdxVar} }) as ${portType};`
6677
6687
  );
6678
6688
  argLines.push(
6679
- ` ${childSetCall}({ id: '${child.id}', portName: '${targetPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${varName});`
6689
+ `${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${targetPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${varName});`
6680
6690
  );
6681
6691
  preHandledPorts.add(targetPort);
6682
6692
  }
@@ -6686,7 +6696,7 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
6686
6696
  workflow: scopeWorkflow,
6687
6697
  id: child.id,
6688
6698
  lines: argLines,
6689
- indent: ` `,
6699
+ indent: tryIndent,
6690
6700
  getCall,
6691
6701
  isAsync: isAsync2,
6692
6702
  instanceParent: child.parent ? `${child.parent.id}.${child.parent.scope}` : void 0,
@@ -6699,11 +6709,11 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
6699
6709
  argLines.forEach((line) => lines.push(line));
6700
6710
  if (childNodeType.expression) {
6701
6711
  lines.push(
6702
- ` const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(", ")});`
6712
+ `${tryIndent}const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(", ")});`
6703
6713
  );
6704
6714
  } else {
6705
6715
  lines.push(
6706
- ` const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(", ")});`
6716
+ `${tryIndent}const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(", ")});`
6707
6717
  );
6708
6718
  }
6709
6719
  if (childNodeType.expression) {
@@ -6711,49 +6721,56 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
6711
6721
  const portDef = childNodeType.outputs[outPort];
6712
6722
  if (portDef.failure || isFailurePort(outPort)) {
6713
6723
  lines.push(
6714
- ` ${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, false);`
6724
+ `${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, false);`
6715
6725
  );
6716
6726
  } else if (portDef.isControlFlow || isSuccessPort(outPort)) {
6717
6727
  lines.push(
6718
- ` ${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, true);`
6728
+ `${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, true);`
6719
6729
  );
6720
6730
  } else {
6721
6731
  lines.push(
6722
- ` ${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`
6732
+ `${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`
6723
6733
  );
6724
6734
  }
6725
6735
  });
6726
6736
  } else {
6727
6737
  Object.keys(childNodeType.outputs || {}).forEach((outPort) => {
6728
6738
  lines.push(
6729
- ` ${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`
6739
+ `${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`
6730
6740
  );
6731
6741
  });
6732
6742
  }
6733
- lines.push(` ${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
6734
- lines.push(` nodeTypeName: '${child.nodeType}',`);
6735
- lines.push(` id: '${child.id}',`);
6736
- lines.push(` executionIndex: ${safeChildId}Idx,`);
6737
- lines.push(` status: 'SUCCEEDED',`);
6738
- lines.push(` });`);
6739
- lines.push(` } catch (error: unknown) {`);
6740
- lines.push(` const isCancellation = CancellationError.isCancellationError(error);`);
6741
- lines.push(` ${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
6742
- lines.push(` nodeTypeName: '${child.nodeType}',`);
6743
- lines.push(` id: '${child.id}',`);
6744
- lines.push(` executionIndex: ${safeChildId}Idx,`);
6745
- lines.push(` status: isCancellation ? 'CANCELLED' : 'FAILED',`);
6746
- lines.push(` });`);
6747
- lines.push(` if (!isCancellation) {`);
6748
- lines.push(` scopedCtx.sendLogErrorEvent({`);
6749
- lines.push(` nodeTypeName: '${child.nodeType}',`);
6750
- lines.push(` id: '${child.id}',`);
6751
- lines.push(` executionIndex: ${safeChildId}Idx,`);
6752
- lines.push(` error: error instanceof Error ? error.message : String(error),`);
6753
- lines.push(` });`);
6754
- lines.push(` }`);
6755
- lines.push(` throw error;`);
6756
- lines.push(` }`);
6743
+ lines.push(`${tryIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
6744
+ lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
6745
+ lines.push(`${tryIndent} id: '${child.id}',`);
6746
+ lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
6747
+ lines.push(`${tryIndent} status: 'SUCCEEDED',`);
6748
+ lines.push(`${tryIndent}});`);
6749
+ if (emitDebugHooks) {
6750
+ const awaitHook = isAsync2 ? "await " : "";
6751
+ lines.push(`${tryIndent}${awaitHook}__ctrl__.afterNode('${child.id}', scopedCtx);`);
6752
+ }
6753
+ lines.push(`${childIndent}} catch (error: unknown) {`);
6754
+ lines.push(`${tryIndent}const isCancellation = CancellationError.isCancellationError(error);`);
6755
+ lines.push(`${tryIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
6756
+ lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
6757
+ lines.push(`${tryIndent} id: '${child.id}',`);
6758
+ lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
6759
+ lines.push(`${tryIndent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
6760
+ lines.push(`${tryIndent}});`);
6761
+ lines.push(`${tryIndent}if (!isCancellation) {`);
6762
+ lines.push(`${tryIndent} scopedCtx.sendLogErrorEvent({`);
6763
+ lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
6764
+ lines.push(`${tryIndent} id: '${child.id}',`);
6765
+ lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
6766
+ lines.push(`${tryIndent} error: error instanceof Error ? error.message : String(error),`);
6767
+ lines.push(`${tryIndent} });`);
6768
+ lines.push(`${tryIndent}}`);
6769
+ lines.push(`${tryIndent}throw error;`);
6770
+ lines.push(`${childIndent}}`);
6771
+ if (emitDebugHooks) {
6772
+ lines.push(` }`);
6773
+ }
6757
6774
  });
6758
6775
  lines.push(``);
6759
6776
  }
@@ -14022,8 +14039,9 @@ function generateCode(ast, options) {
14022
14039
  }
14023
14040
  }
14024
14041
  const npmPackageNodes = ast.nodeTypes.filter((n7) => n7.importSource);
14042
+ const referencedNodeTypes = new Set(ast.instances.map((i) => i.nodeType));
14025
14043
  const localNodes = ast.nodeTypes.filter(
14026
- (n7) => !n7.importSource && n7.sourceLocation?.file === ast.sourceFile
14044
+ (n7) => !n7.importSource && (n7.sourceLocation?.file === ast.sourceFile || !n7.sourceLocation && n7.functionText && n7.helperText != null && referencedNodeTypes.has(n7.name))
14027
14045
  );
14028
14046
  const importedNodes = ast.nodeTypes.filter(
14029
14047
  (n7) => !n7.importSource && n7.sourceLocation?.file !== ast.sourceFile
@@ -14134,9 +14152,21 @@ function generateCode(ast, options) {
14134
14152
  if (inlineFunctions.length > 0) {
14135
14153
  lines.push("");
14136
14154
  addLine();
14155
+ const emittedHelpers = /* @__PURE__ */ new Set();
14156
+ inlineFunctions.forEach((node) => {
14157
+ if (node.importSource) return;
14158
+ const helperText = production ? node.helperTextProduction ?? null : node.helperText ?? null;
14159
+ if (helperText && !emittedHelpers.has(helperText)) {
14160
+ emittedHelpers.add(helperText);
14161
+ lines.push(helperText);
14162
+ helperText.split("\n").forEach(() => addLine());
14163
+ lines.push("");
14164
+ addLine();
14165
+ }
14166
+ });
14137
14167
  inlineFunctions.forEach((node) => {
14138
14168
  if (node.importSource) return;
14139
- const functionText = node.functionText;
14169
+ const functionText = production && node.functionTextProduction != null ? node.functionTextProduction : node.functionText;
14140
14170
  if (functionText) {
14141
14171
  const functionWithoutDecorators = removeDecorators(functionText);
14142
14172
  if (node.sourceLocation) {
@@ -15258,12 +15288,62 @@ function generateInPlace(sourceCode, ast, options = {}) {
15258
15288
  if (nodeType.sourceLocation?.file && path2.resolve(nodeType.sourceLocation.file) !== path2.resolve(ast.sourceFile)) {
15259
15289
  continue;
15260
15290
  }
15291
+ if (!nodeType.sourceLocation && nodeType.helperText != null) {
15292
+ continue;
15293
+ }
15261
15294
  const nodeTypeResult = replaceNodeTypeJSDoc(result, nodeType);
15262
15295
  if (nodeTypeResult.changed) {
15263
15296
  result = nodeTypeResult.code;
15264
15297
  hasChanges = true;
15265
15298
  }
15266
15299
  }
15300
+ const usedNodeTypes = new Set(ast.instances.map((i) => i.nodeType));
15301
+ const builtInNodes = ast.nodeTypes.filter(
15302
+ (nt2) => !nt2.sourceLocation && nt2.functionText && nt2.helperText != null && usedNodeTypes.has(nt2.name)
15303
+ );
15304
+ if (builtInNodes.length > 0) {
15305
+ const workflowFnPattern = new RegExp(
15306
+ `(/\\*\\*[\\s\\S]*?@flowWeaver\\s+workflow[\\s\\S]*?\\*/)\\s*\\n\\s*(?:export\\s+)?(?:async\\s+)?function\\s+${ast.functionName}\\b`
15307
+ );
15308
+ const match = result.match(workflowFnPattern);
15309
+ if (match && match.index !== void 0) {
15310
+ const insertionLines = [];
15311
+ const emittedHelpers = /* @__PURE__ */ new Set();
15312
+ for (const node of builtInNodes) {
15313
+ const helperText = production ? node.helperTextProduction ?? null : node.helperText ?? null;
15314
+ if (helperText && !emittedHelpers.has(helperText)) {
15315
+ emittedHelpers.add(helperText);
15316
+ insertionLines.push(helperText);
15317
+ insertionLines.push("");
15318
+ }
15319
+ }
15320
+ for (const node of builtInNodes) {
15321
+ const funcText = production && node.functionTextProduction != null ? node.functionTextProduction : node.functionText;
15322
+ if (funcText) {
15323
+ const portAnnotations = [];
15324
+ portAnnotations.push("/**");
15325
+ portAnnotations.push(` * @flowWeaver nodeType`);
15326
+ for (const [name, port] of Object.entries(node.inputs)) {
15327
+ if (name === "execute") continue;
15328
+ const optPrefix = port.optional ? "[" : "";
15329
+ const optSuffix = port.optional ? "]" : "";
15330
+ portAnnotations.push(` * @input ${optPrefix}${name}${optSuffix} - ${port.label || name}`);
15331
+ }
15332
+ for (const [name, port] of Object.entries(node.outputs)) {
15333
+ if (name === "onSuccess" || name === "onFailure") continue;
15334
+ portAnnotations.push(` * @output ${name} - ${port.label || name}`);
15335
+ }
15336
+ portAnnotations.push(" */");
15337
+ insertionLines.push(portAnnotations.join("\n"));
15338
+ insertionLines.push(funcText);
15339
+ insertionLines.push("");
15340
+ }
15341
+ }
15342
+ const insertionCode = insertionLines.join("\n");
15343
+ result = result.slice(0, match.index) + insertionCode + "\n" + result.slice(match.index);
15344
+ hasChanges = true;
15345
+ }
15346
+ }
15267
15347
  const cleanupResult = removeOrphanedNodeTypeFunctions(result, ast, allWorkflows);
15268
15348
  if (cleanupResult.changed) {
15269
15349
  result = cleanupResult.code;
@@ -27243,6 +27323,306 @@ var init_coercion_types = __esm({
27243
27323
  }
27244
27324
  });
27245
27325
 
27326
+ // src/built-in-nodes/generated-registry.ts
27327
+ var BUILT_IN_NODE_TYPES;
27328
+ var init_generated_registry = __esm({
27329
+ "src/built-in-nodes/generated-registry.ts"() {
27330
+ "use strict";
27331
+ BUILT_IN_NODE_TYPES = [
27332
+ {
27333
+ type: "NodeType",
27334
+ name: "delay",
27335
+ functionName: "delay",
27336
+ isAsync: true,
27337
+ hasSuccessPort: true,
27338
+ hasFailurePort: true,
27339
+ executeWhen: "CONJUNCTION",
27340
+ variant: "FUNCTION",
27341
+ inputs: {
27342
+ execute: { dataType: "STEP", label: "Execute" },
27343
+ duration: { dataType: "STRING", label: 'Duration to sleep (e.g. "30s", "5m", "1h", "2d")', tsType: "string" }
27344
+ },
27345
+ outputs: {
27346
+ onSuccess: { dataType: "STEP", label: "On Success", isControlFlow: true },
27347
+ onFailure: { dataType: "STEP", label: "On Failure", isControlFlow: true, failure: true },
27348
+ elapsed: { dataType: "BOOLEAN", label: "Always true after sleep completes", tsType: "boolean" }
27349
+ },
27350
+ helperText: `
27351
+ function __fw_getMockConfig() {
27352
+ return globalThis.__fw_mocks__;
27353
+ }
27354
+
27355
+ function __fw_lookupMock(section, key) {
27356
+ if (!section)
27357
+ return undefined;
27358
+ const nodeId = globalThis.__fw_current_node_id__;
27359
+ if (nodeId) {
27360
+ const qualified = section[\`\${nodeId}:\${key}\`];
27361
+ if (qualified !== undefined)
27362
+ return qualified;
27363
+ }
27364
+ return section[key];
27365
+ }
27366
+ `.trim(),
27367
+ helperTextProduction: void 0,
27368
+ functionText: `
27369
+ async function delay(execute, duration) {
27370
+ if (!execute)
27371
+ return { onSuccess: false, onFailure: false, elapsed: false };
27372
+ const mocks = __fw_getMockConfig();
27373
+ if (mocks?.fast) {
27374
+ await new Promise((resolve) => setTimeout(resolve, 1));
27375
+ }
27376
+ else {
27377
+ const ms = __fw_parseDuration(duration);
27378
+ await new Promise((resolve) => setTimeout(resolve, ms));
27379
+ }
27380
+ return { onSuccess: true, onFailure: false, elapsed: true };
27381
+ }
27382
+ function __fw_parseDuration(duration) {
27383
+ const match = duration.match(/^(\\d+)(ms|s|m|h|d)$/);
27384
+ if (!match)
27385
+ return 0;
27386
+ const [, value, unit] = match;
27387
+ const multipliers = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
27388
+ return parseInt(value) * (multipliers[unit] || 0);
27389
+ }
27390
+ `.trim(),
27391
+ functionTextProduction: `
27392
+ async function delay(execute, duration) {
27393
+ if (!execute)
27394
+ return { onSuccess: false, onFailure: false, elapsed: false };
27395
+ const ms = __fw_parseDuration(duration);
27396
+ await new Promise((resolve) => setTimeout(resolve, ms));
27397
+ return { onSuccess: true, onFailure: false, elapsed: true };
27398
+ }
27399
+ function __fw_parseDuration(duration) {
27400
+ const match = duration.match(/^(\\d+)(ms|s|m|h|d)$/);
27401
+ if (!match)
27402
+ return 0;
27403
+ const [, value, unit] = match;
27404
+ const multipliers = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
27405
+ return parseInt(value) * (multipliers[unit] || 0);
27406
+ }
27407
+ `.trim()
27408
+ },
27409
+ {
27410
+ type: "NodeType",
27411
+ name: "waitForEvent",
27412
+ functionName: "waitForEvent",
27413
+ isAsync: true,
27414
+ hasSuccessPort: true,
27415
+ hasFailurePort: true,
27416
+ executeWhen: "CONJUNCTION",
27417
+ variant: "FUNCTION",
27418
+ inputs: {
27419
+ execute: { dataType: "STEP", label: "Execute" },
27420
+ eventName: { dataType: "STRING", label: 'Event name to wait for (e.g. "app/approval.received")', tsType: "string" },
27421
+ match: { dataType: "STRING", label: 'Field to match between trigger and waited event (e.g. "data.requestId")', tsType: "string", optional: true },
27422
+ timeout: { dataType: "STRING", label: 'Max wait time (e.g. "24h", "7d"). Empty = no timeout', tsType: "string", optional: true }
27423
+ },
27424
+ outputs: {
27425
+ onSuccess: { dataType: "STEP", label: "On Success", isControlFlow: true },
27426
+ onFailure: { dataType: "STEP", label: "On Failure", isControlFlow: true, failure: true },
27427
+ eventData: { dataType: "OBJECT", label: "The received event's data payload", tsType: "object" }
27428
+ },
27429
+ helperText: `
27430
+ function __fw_getMockConfig() {
27431
+ return globalThis.__fw_mocks__;
27432
+ }
27433
+
27434
+ function __fw_lookupMock(section, key) {
27435
+ if (!section)
27436
+ return undefined;
27437
+ const nodeId = globalThis.__fw_current_node_id__;
27438
+ if (nodeId) {
27439
+ const qualified = section[\`\${nodeId}:\${key}\`];
27440
+ if (qualified !== undefined)
27441
+ return qualified;
27442
+ }
27443
+ return section[key];
27444
+ }
27445
+ `.trim(),
27446
+ helperTextProduction: void 0,
27447
+ functionText: `
27448
+ async function waitForEvent(execute, eventName, match, timeout) {
27449
+ if (!execute)
27450
+ return { onSuccess: false, onFailure: false, eventData: {} };
27451
+ const mocks = __fw_getMockConfig();
27452
+ if (mocks) {
27453
+ const mockData = __fw_lookupMock(mocks.events, eventName);
27454
+ if (mockData !== undefined) {
27455
+ return { onSuccess: true, onFailure: false, eventData: mockData };
27456
+ }
27457
+ return { onSuccess: false, onFailure: true, eventData: {} };
27458
+ }
27459
+ return { onSuccess: true, onFailure: false, eventData: {} };
27460
+ }
27461
+ `.trim(),
27462
+ functionTextProduction: `
27463
+ async function waitForEvent(execute, eventName, match, timeout) {
27464
+ if (!execute)
27465
+ return { onSuccess: false, onFailure: false, eventData: {} };
27466
+ return { onSuccess: true, onFailure: false, eventData: {} };
27467
+ }
27468
+ `.trim()
27469
+ },
27470
+ {
27471
+ type: "NodeType",
27472
+ name: "invokeWorkflow",
27473
+ functionName: "invokeWorkflow",
27474
+ isAsync: true,
27475
+ hasSuccessPort: true,
27476
+ hasFailurePort: true,
27477
+ executeWhen: "CONJUNCTION",
27478
+ variant: "FUNCTION",
27479
+ inputs: {
27480
+ execute: { dataType: "STEP", label: "Execute" },
27481
+ functionId: { dataType: "STRING", label: 'Function ID of the workflow to invoke (e.g. "my-service/sub-workflow")', tsType: "string" },
27482
+ payload: { dataType: "OBJECT", label: "Data to pass as event.data to the invoked function", tsType: "object" },
27483
+ timeout: { dataType: "STRING", label: 'Max wait time (e.g. "1h")', tsType: "string", optional: true }
27484
+ },
27485
+ outputs: {
27486
+ onSuccess: { dataType: "STEP", label: "On Success", isControlFlow: true },
27487
+ onFailure: { dataType: "STEP", label: "On Failure", isControlFlow: true, failure: true },
27488
+ result: { dataType: "OBJECT", label: "Return value from the invoked function", tsType: "object" }
27489
+ },
27490
+ helperText: `
27491
+ function __fw_getMockConfig() {
27492
+ return globalThis.__fw_mocks__;
27493
+ }
27494
+
27495
+ function __fw_lookupMock(section, key) {
27496
+ if (!section)
27497
+ return undefined;
27498
+ const nodeId = globalThis.__fw_current_node_id__;
27499
+ if (nodeId) {
27500
+ const qualified = section[\`\${nodeId}:\${key}\`];
27501
+ if (qualified !== undefined)
27502
+ return qualified;
27503
+ }
27504
+ return section[key];
27505
+ }
27506
+ `.trim(),
27507
+ helperTextProduction: void 0,
27508
+ functionText: `
27509
+ async function invokeWorkflow(execute, functionId, payload, timeout) {
27510
+ if (!execute)
27511
+ return { onSuccess: false, onFailure: false, result: {} };
27512
+ const mocks = __fw_getMockConfig();
27513
+ if (mocks) {
27514
+ const mockResult = __fw_lookupMock(mocks.invocations, functionId);
27515
+ if (mockResult !== undefined) {
27516
+ return { onSuccess: true, onFailure: false, result: mockResult };
27517
+ }
27518
+ return { onSuccess: false, onFailure: true, result: {} };
27519
+ }
27520
+ const registry = globalThis.__fw_workflow_registry__;
27521
+ if (registry?.[functionId]) {
27522
+ try {
27523
+ const result = await registry[functionId](true, payload);
27524
+ return { onSuccess: true, onFailure: false, result: result ?? {} };
27525
+ }
27526
+ catch {
27527
+ return { onSuccess: false, onFailure: true, result: {} };
27528
+ }
27529
+ }
27530
+ return { onSuccess: true, onFailure: false, result: {} };
27531
+ }
27532
+ `.trim(),
27533
+ functionTextProduction: `
27534
+ async function invokeWorkflow(execute, functionId, payload, timeout) {
27535
+ if (!execute)
27536
+ return { onSuccess: false, onFailure: false, result: {} };
27537
+ const registry = globalThis.__fw_workflow_registry__;
27538
+ if (registry?.[functionId]) {
27539
+ try {
27540
+ const result = await registry[functionId](true, payload);
27541
+ return { onSuccess: true, onFailure: false, result: result ?? {} };
27542
+ }
27543
+ catch {
27544
+ return { onSuccess: false, onFailure: true, result: {} };
27545
+ }
27546
+ }
27547
+ return { onSuccess: true, onFailure: false, result: {} };
27548
+ }
27549
+ `.trim()
27550
+ },
27551
+ {
27552
+ type: "NodeType",
27553
+ name: "waitForAgent",
27554
+ functionName: "waitForAgent",
27555
+ isAsync: true,
27556
+ hasSuccessPort: true,
27557
+ hasFailurePort: true,
27558
+ executeWhen: "CONJUNCTION",
27559
+ variant: "FUNCTION",
27560
+ inputs: {
27561
+ execute: { dataType: "STEP", label: "Execute" },
27562
+ agentId: { dataType: "STRING", label: "Agent/task identifier", tsType: "string" },
27563
+ context: { dataType: "OBJECT", label: "Context data to send to the agent", tsType: "object" },
27564
+ prompt: { dataType: "STRING", label: "Message to display when requesting input", tsType: "string", optional: true }
27565
+ },
27566
+ outputs: {
27567
+ onSuccess: { dataType: "STEP", label: "On Success", isControlFlow: true },
27568
+ onFailure: { dataType: "STEP", label: "On Failure", isControlFlow: true, failure: true },
27569
+ agentResult: { dataType: "OBJECT", label: "Result returned by the agent", tsType: "object" }
27570
+ },
27571
+ helperText: `
27572
+ function __fw_getMockConfig() {
27573
+ return globalThis.__fw_mocks__;
27574
+ }
27575
+
27576
+ function __fw_lookupMock(section, key) {
27577
+ if (!section)
27578
+ return undefined;
27579
+ const nodeId = globalThis.__fw_current_node_id__;
27580
+ if (nodeId) {
27581
+ const qualified = section[\`\${nodeId}:\${key}\`];
27582
+ if (qualified !== undefined)
27583
+ return qualified;
27584
+ }
27585
+ return section[key];
27586
+ }
27587
+ `.trim(),
27588
+ helperTextProduction: void 0,
27589
+ functionText: `
27590
+ async function waitForAgent(execute, agentId, context, prompt) {
27591
+ if (!execute)
27592
+ return { onSuccess: false, onFailure: false, agentResult: {} };
27593
+ const mocks = __fw_getMockConfig();
27594
+ const mockResult = __fw_lookupMock(mocks?.agents, agentId);
27595
+ if (mockResult !== undefined) {
27596
+ return { onSuccess: true, onFailure: false, agentResult: mockResult };
27597
+ }
27598
+ if (mocks?.agents) {
27599
+ return { onSuccess: false, onFailure: true, agentResult: {} };
27600
+ }
27601
+ const channel = globalThis.__fw_agent_channel__;
27602
+ if (channel) {
27603
+ const result = await channel.request({ agentId, context, prompt });
27604
+ return { onSuccess: true, onFailure: false, agentResult: result };
27605
+ }
27606
+ return { onSuccess: true, onFailure: false, agentResult: {} };
27607
+ }
27608
+ `.trim(),
27609
+ functionTextProduction: `
27610
+ async function waitForAgent(execute, agentId, context, prompt) {
27611
+ if (!execute)
27612
+ return { onSuccess: false, onFailure: false, agentResult: {} };
27613
+ const channel = globalThis.__fw_agent_channel__;
27614
+ if (channel) {
27615
+ const result = await channel.request({ agentId, context, prompt });
27616
+ return { onSuccess: true, onFailure: false, agentResult: result };
27617
+ }
27618
+ return { onSuccess: true, onFailure: false, agentResult: {} };
27619
+ }
27620
+ `.trim()
27621
+ }
27622
+ ];
27623
+ }
27624
+ });
27625
+
27246
27626
  // src/parser/tag-registry.ts
27247
27627
  var TagHandlerRegistry, tagHandlerRegistry;
27248
27628
  var init_tag_registry = __esm({
@@ -27571,6 +27951,7 @@ var init_parser2 = __esm({
27571
27951
  init_shared_project();
27572
27952
  init_lru_cache();
27573
27953
  init_coercion_types();
27954
+ init_generated_registry();
27574
27955
  init_tag_registry();
27575
27956
  AnnotationParser = class {
27576
27957
  project;
@@ -27714,7 +28095,7 @@ var init_parser2 = __esm({
27714
28095
  }
27715
28096
  }
27716
28097
  }
27717
- const inferredNodeTypes = this.inferNodeTypesFromUnannotated(sourceFile, nodeTypes);
28098
+ const inferredNodeTypes = this.inferNodeTypesFromUnannotated(sourceFile, nodeTypes, localNodeTypes, warnings);
27718
28099
  nodeTypes.push(...inferredNodeTypes);
27719
28100
  const workflows = this.extractWorkflows(sourceFile, nodeTypes, filePath, errors2, warnings);
27720
28101
  const patterns = this.extractPatterns(sourceFile, nodeTypes, filePath, errors2, warnings);
@@ -27754,7 +28135,7 @@ var init_parser2 = __esm({
27754
28135
  const workflowSignatures = this.extractWorkflowSignatures(sourceFile, virtualPath, warnings);
27755
28136
  const sameFileWorkflowNodeTypes = workflowSignatures.map((wf) => this.workflowToNodeType(wf));
27756
28137
  const nodeTypes = [...localNodeTypes, ...sameFileWorkflowNodeTypes];
27757
- const inferredNodeTypes = this.inferNodeTypesFromUnannotated(sourceFile, nodeTypes);
28138
+ const inferredNodeTypes = this.inferNodeTypesFromUnannotated(sourceFile, nodeTypes, localNodeTypes, warnings);
27758
28139
  nodeTypes.push(...inferredNodeTypes);
27759
28140
  const workflows = this.extractWorkflows(sourceFile, nodeTypes, virtualPath, errors2, warnings);
27760
28141
  const patterns = this.extractPatterns(sourceFile, nodeTypes, virtualPath, errors2, warnings);
@@ -27986,7 +28367,7 @@ var init_parser2 = __esm({
27986
28367
  if (imp.importSource.startsWith(".")) {
27987
28368
  return this.resolveLocalImportAnnotation(imp, currentDir, warnings);
27988
28369
  } else {
27989
- return this.resolveNpmImportAnnotation(imp, currentDir);
28370
+ return this.resolveNpmImportAnnotation(imp, currentDir, warnings);
27990
28371
  }
27991
28372
  }
27992
28373
  /**
@@ -28031,7 +28412,7 @@ var init_parser2 = __esm({
28031
28412
  /**
28032
28413
  * Resolve an npm package @fwImport to a node type by reading .d.ts declarations.
28033
28414
  */
28034
- resolveNpmImportAnnotation(imp, currentDir) {
28415
+ resolveNpmImportAnnotation(imp, currentDir, warnings) {
28035
28416
  const cacheKey = `npm:${imp.importSource}`;
28036
28417
  if (this.importCache.has(cacheKey)) {
28037
28418
  const cached2 = this.importCache.get(cacheKey);
@@ -28055,6 +28436,9 @@ var init_parser2 = __esm({
28055
28436
  }
28056
28437
  const dtsPath = resolvePackageTypesPath(imp.importSource, currentDir);
28057
28438
  if (!dtsPath) {
28439
+ warnings.push(
28440
+ `@fwImport: Package "${imp.importSource}" has no type declarations (.d.ts). Install @types/${imp.importSource} or add a local wrapper with @flowWeaver nodeType annotations.`
28441
+ );
28058
28442
  return this.createImportStub(imp);
28059
28443
  }
28060
28444
  try {
@@ -28084,7 +28468,13 @@ var init_parser2 = __esm({
28084
28468
  if (found) {
28085
28469
  return { ...found, name: imp.name, importSource: imp.importSource };
28086
28470
  }
28087
- } catch {
28471
+ warnings.push(
28472
+ `@fwImport: Function "${imp.functionName}" not found in type declarations for "${imp.importSource}". Available exports: ${allNodeTypes.map((nt2) => nt2.functionName).join(", ") || "(none)"}.`
28473
+ );
28474
+ } catch (err) {
28475
+ warnings.push(
28476
+ `@fwImport: Failed to parse type declarations for "${imp.importSource}": ${err.message}. Node "${imp.name}" will use a generic stub.`
28477
+ );
28088
28478
  }
28089
28479
  return this.createImportStub(imp);
28090
28480
  }
@@ -28383,6 +28773,18 @@ ${fn.getText()}` : fn.getText();
28383
28773
  const importedNpmNodeTypes = (config2.imports || []).map(
28384
28774
  (imp) => this.resolveImportAnnotation(imp, filePath, warnings)
28385
28775
  );
28776
+ for (const nt2 of importedNpmNodeTypes) {
28777
+ const dataInputs = Object.keys(nt2.inputs).filter((p) => p !== "execute");
28778
+ const nonControlOutputs = Object.keys(nt2.outputs).filter(
28779
+ (p) => p !== "onSuccess" && p !== "onFailure"
28780
+ );
28781
+ const isStubOnly = nonControlOutputs.length === 1 && nonControlOutputs[0] === "result" && nt2.outputs.result?.dataType === "ANY";
28782
+ if (dataInputs.length === 0 && isStubOnly) {
28783
+ warnings.push(
28784
+ `Could not infer ports for "${nt2.functionName}" from "${nt2.importSource}". Wrap it in a local function with @flowWeaver nodeType annotations instead.`
28785
+ );
28786
+ }
28787
+ }
28386
28788
  const allAvailableNodeTypes = [...availableNodeTypes, ...importedNpmNodeTypes];
28387
28789
  const instances = (config2.instances || []).map((inst) => {
28388
28790
  const nodeTypeExists = allAvailableNodeTypes.some(
@@ -28733,7 +29135,7 @@ ${fn.getText()}` : fn.getText();
28733
29135
  * nodeType annotation, we infer an expression node type from its TypeScript
28734
29136
  * signature. Phase 1: same-file functions only.
28735
29137
  */
28736
- inferNodeTypesFromUnannotated(sourceFile, existingNodeTypes) {
29138
+ inferNodeTypesFromUnannotated(sourceFile, existingNodeTypes, localNodeTypes, warnings) {
28737
29139
  const allFunctions = extractFunctionLikes(sourceFile);
28738
29140
  const referencedTypes = /* @__PURE__ */ new Set();
28739
29141
  for (const fn of allFunctions) {
@@ -28757,8 +29159,24 @@ ${fn.getText()}` : fn.getText();
28757
29159
  if (unresolvedTypes.size === 0) return [];
28758
29160
  const inferredNodeTypes = [];
28759
29161
  const alreadyInferred = /* @__PURE__ */ new Set();
29162
+ const builtInByName = new Map(BUILT_IN_NODE_TYPES.map((nt2) => [nt2.name, nt2]));
29163
+ const annotatedNames = new Set(localNodeTypes.map((nt2) => nt2.functionName));
28760
29164
  for (const unresolvedType of unresolvedTypes) {
28761
29165
  if (alreadyInferred.has(unresolvedType)) continue;
29166
+ const builtIn = builtInByName.get(unresolvedType);
29167
+ if (builtIn) {
29168
+ inferredNodeTypes.push(builtIn);
29169
+ alreadyInferred.add(unresolvedType);
29170
+ if (!annotatedNames.has(unresolvedType)) {
29171
+ const shadowFn = allFunctions.find((fn) => fn.getName() === unresolvedType && !this.hasFlowWeaverAnnotation(fn));
29172
+ if (shadowFn) {
29173
+ warnings.push(
29174
+ `Function '${unresolvedType}' exists in this file but is not annotated with @flowWeaver nodeType. The built-in '${unresolvedType}' will be used instead. Add @flowWeaver nodeType to use your version.`
29175
+ );
29176
+ }
29177
+ }
29178
+ continue;
29179
+ }
28762
29180
  const matchedFn = allFunctions.find((fn) => {
28763
29181
  if (fn.getName() !== unresolvedType) return false;
28764
29182
  return !this.hasFlowWeaverAnnotation(fn);
@@ -29005,6 +29423,10 @@ ${fn.getText()}` : fn.getText();
29005
29423
  const nextInputs = getInputPorts(nextId);
29006
29424
  for (const [inputName] of Object.entries(nextInputs)) {
29007
29425
  if (isControlFlowPort(inputName)) continue;
29426
+ const alreadyConnected = connections.some(
29427
+ (c) => c.to.node === nextId && c.to.port === inputName
29428
+ );
29429
+ if (alreadyConnected) continue;
29008
29430
  for (let j2 = i; j2 >= 0; j2--) {
29009
29431
  const ancestorId = steps[j2].node;
29010
29432
  const ancestorOutputs = getOutputPorts(ancestorId);
@@ -37919,7 +38341,7 @@ function getDependents(ast, nodeId) {
37919
38341
  });
37920
38342
  return Array.from(dependents);
37921
38343
  }
37922
- function getTopologicalOrder(ast) {
38344
+ function getTopologicalOrder(ast, options) {
37923
38345
  const mainInstances = getMainFlowInstances(ast);
37924
38346
  const mainConnections = getMainFlowConnections(ast);
37925
38347
  const inDegree = /* @__PURE__ */ new Map();
@@ -37958,6 +38380,30 @@ function getTopologicalOrder(ast) {
37958
38380
  if (result.length !== mainInstances.length) {
37959
38381
  throw new Error("Cannot compute topological order: workflow contains cycles");
37960
38382
  }
38383
+ if (options?.includeScopedChildren) {
38384
+ const scopedChildren = ast.instances.filter(
38385
+ (inst) => isPerPortScopedChild(inst, ast, ast.nodeTypes)
38386
+ );
38387
+ const expanded = [];
38388
+ const scopedByParent = /* @__PURE__ */ new Map();
38389
+ for (const child of scopedChildren) {
38390
+ if (child.parent) {
38391
+ const parentId = child.parent.id;
38392
+ if (!scopedByParent.has(parentId)) {
38393
+ scopedByParent.set(parentId, []);
38394
+ }
38395
+ scopedByParent.get(parentId).push(child.id);
38396
+ }
38397
+ }
38398
+ for (const nodeId of result) {
38399
+ expanded.push(nodeId);
38400
+ const children = scopedByParent.get(nodeId);
38401
+ if (children) {
38402
+ expanded.push(...children);
38403
+ }
38404
+ }
38405
+ return expanded;
38406
+ }
37961
38407
  return result;
37962
38408
  }
37963
38409
  function findIsolatedNodes(ast) {
@@ -58608,9 +59054,9 @@ function printNocodeGuidance(_projectName) {
58608
59054
  logger.newline();
58609
59055
  logger.log(` ${logger.bold("Useful commands")}`);
58610
59056
  logger.newline();
58611
- logger.log(` fw run src/*.ts ${logger.dim("Run your workflow")}`);
58612
- logger.log(` fw diagram src/*.ts ${logger.dim("See a visual diagram")}`);
58613
- logger.log(` fw mcp-setup ${logger.dim("Connect more AI editors")}`);
59057
+ logger.log(` npx fw run src/*.ts ${logger.dim("Run your workflow")}`);
59058
+ logger.log(` npx fw diagram src/*.ts ${logger.dim("See a visual diagram")}`);
59059
+ logger.log(` npx fw mcp-setup ${logger.dim("Connect more AI editors")}`);
58614
59060
  }
58615
59061
  function printVibecoderGuidance() {
58616
59062
  logger.newline();
@@ -58628,17 +59074,17 @@ function printLowcodeGuidance() {
58628
59074
  logger.newline();
58629
59075
  logger.log(` ${logger.bold("Explore and customize")}`);
58630
59076
  logger.newline();
58631
- logger.log(` fw templates ${logger.dim("List all 16 workflow templates")}`);
58632
- logger.log(` fw describe src/*.ts ${logger.dim("See the workflow structure")}`);
58633
- logger.log(` fw docs annotations ${logger.dim("Annotation reference")}`);
59077
+ logger.log(` npx fw templates ${logger.dim("List all 16 workflow templates")}`);
59078
+ logger.log(` npx fw describe src/*.ts ${logger.dim("See the workflow structure")}`);
59079
+ logger.log(` npx fw docs annotations ${logger.dim("Annotation reference")}`);
58634
59080
  logger.newline();
58635
59081
  logger.log(` Your project includes an example in ${logger.highlight("examples/")} to study.`);
58636
59082
  logger.log(` With MCP connected, AI can help modify nodes and connections.`);
58637
59083
  }
58638
59084
  function printExpertGuidance() {
58639
59085
  logger.newline();
58640
- logger.log(` fw mcp-setup ${logger.dim("Connect AI editors (Claude, Cursor, VS Code)")}`);
58641
- logger.log(` fw docs ${logger.dim("Browse reference docs")}`);
59086
+ logger.log(` npx fw mcp-setup ${logger.dim("Connect AI editors (Claude, Cursor, VS Code)")}`);
59087
+ logger.log(` npx fw docs ${logger.dim("Browse reference docs")}`);
58642
59088
  }
58643
59089
  function pad(displayName, width) {
58644
59090
  const padding = Math.max(1, width - displayName.length);
@@ -59588,16 +60034,19 @@ ${workflowCode}`;
59588
60034
  "",
59589
60035
  `import { ${workflowName} } from './${workflowJsFile}';`,
59590
60036
  "",
59591
- "try {",
59592
- ` const result = ${workflowName}(true, { data: { message: 'hello world' } });`,
59593
- " console.log(result);",
59594
- "} catch (e) {",
60037
+ "async function main() {",
60038
+ ` const result = await ${workflowName}(true, { data: { message: 'hello world' } });`,
60039
+ " console.log(JSON.stringify(result, null, 2));",
60040
+ "}",
60041
+ "",
60042
+ "main().catch((e) => {",
59595
60043
  " if (e instanceof Error && e.message.startsWith('Compile with:')) {",
59596
60044
  " console.error('Workflow not compiled yet. Run: npm run dev');",
59597
60045
  " process.exit(1);",
59598
60046
  " }",
59599
- " throw e;",
59600
- "}",
60047
+ " console.error(e);",
60048
+ " process.exit(1);",
60049
+ "});",
59601
60050
  ""
59602
60051
  ].join("\n");
59603
60052
  } else {
@@ -59613,16 +60062,19 @@ ${workflowCode}`;
59613
60062
  "",
59614
60063
  `const { ${workflowName} } = require('./${workflowJsFile}');`,
59615
60064
  "",
59616
- "try {",
59617
- ` const result = ${workflowName}(true, { data: { message: 'hello world' } });`,
59618
- " console.log(result);",
59619
- "} catch (e) {",
60065
+ "async function main() {",
60066
+ ` const result = await ${workflowName}(true, { data: { message: 'hello world' } });`,
60067
+ " console.log(JSON.stringify(result, null, 2));",
60068
+ "}",
60069
+ "",
60070
+ "main().catch((e) => {",
59620
60071
  " if (e instanceof Error && e.message.startsWith('Compile with:')) {",
59621
60072
  " console.error('Workflow not compiled yet. Run: npm run dev');",
59622
60073
  " process.exit(1);",
59623
60074
  " }",
59624
- " throw e;",
59625
- "}",
60075
+ " console.error(e);",
60076
+ " process.exit(1);",
60077
+ "});",
59626
60078
  ""
59627
60079
  ].join("\n");
59628
60080
  }
@@ -84560,6 +85012,13 @@ async function runCommandInner(input, options) {
84560
85012
  logger.newline();
84561
85013
  logger.section("Result");
84562
85014
  logger.log(JSON.stringify(result.result, null, 2));
85015
+ const resultObj = result.result;
85016
+ if (resultObj?.onFailure === true && !options.params && !options.paramsFile) {
85017
+ logger.newline();
85018
+ logger.warn(
85019
+ "Tip: use --params to provide input. Run `fw describe <file>` to see expected inputs."
85020
+ );
85021
+ }
84563
85022
  if (options.trace && !options.stream && result.trace && result.trace.length > 0) {
84564
85023
  logger.newline();
84565
85024
  logger.section("Trace");
@@ -88468,7 +88927,7 @@ function parseIntStrict(value) {
88468
88927
  // src/cli/index.ts
88469
88928
  init_logger();
88470
88929
  init_error_utils();
88471
- var version2 = true ? "0.27.5" : "0.0.0-dev";
88930
+ var version2 = true ? "0.29.0" : "0.0.0-dev";
88472
88931
  var program2 = new Command();
88473
88932
  program2.name("fw").description("Flow Weaver Annotations - Compile and validate workflow files").option("-v, --version", "Output the current version").option("--no-color", "Disable colors").option("--color", "Force colors").on("option:version", () => {
88474
88933
  logger.banner(version2);