@synergenius/flow-weaver 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api/parse.js CHANGED
@@ -32,6 +32,7 @@ export async function parseWorkflow(filePath, options) {
32
32
  // Parse the file to extract nodes and workflows
33
33
  const parsed = parser.parse(filePath);
34
34
  warnings.push(...parsed.warnings);
35
+ errors.push(...parsed.errors);
35
36
  // Get available workflow names
36
37
  availableWorkflows = parsed.workflows.map((w) => w.functionName);
37
38
  // Determine which workflow to use
@@ -82,7 +83,7 @@ export async function parseWorkflow(filePath, options) {
82
83
  }
83
84
  return {
84
85
  ast: workflow,
85
- errors: [],
86
+ errors,
86
87
  warnings,
87
88
  availableWorkflows,
88
89
  allWorkflows: parsed.workflows,
@@ -65,14 +65,13 @@ class NodeParser extends CstParser {
65
65
  this.CONSUME(LabelPrefix);
66
66
  this.CONSUME(StringLiteral, { LABEL: 'labelValue' });
67
67
  });
68
- // expr: port="value", port2="value2"
68
+ // expr: port="value", port2="value2" (comma optional between assignments)
69
69
  exprAttr = this.RULE('exprAttr', () => {
70
70
  this.CONSUME(ExprPrefix);
71
- this.AT_LEAST_ONE_SEP({
72
- SEP: Comma,
73
- DEF: () => {
74
- this.SUBRULE(this.exprAssignment);
75
- },
71
+ this.SUBRULE(this.exprAssignment);
72
+ this.MANY(() => {
73
+ this.OPTION(() => this.CONSUME(Comma));
74
+ this.SUBRULE2(this.exprAssignment);
76
75
  });
77
76
  });
78
77
  // port="value"
@@ -524,6 +523,10 @@ const visitorInstance = new NodeVisitor();
524
523
  export function parseNodeLine(input, warnings) {
525
524
  const lexResult = JSDocLexer.tokenize(input);
526
525
  if (lexResult.errors.length > 0) {
526
+ const truncatedInput = input.length > 60 ? input.substring(0, 60) + '...' : input;
527
+ warnings.push(`Failed to tokenize node line: "${truncatedInput}"\n` +
528
+ ` Error: ${lexResult.errors[0].message}\n` +
529
+ ` Expected format: @node instanceId NodeType`);
527
530
  return null;
528
531
  }
529
532
  // Check if starts with @node
@@ -137,48 +137,50 @@ export async function compileCommand(input, options = {}) {
137
137
  errorCount++;
138
138
  continue;
139
139
  }
140
- // Validate the AST (especially for --strict mode)
141
- if (strict) {
142
- const validation = validator.validate(parseResult.ast, { strictMode: true });
143
- if (validation.errors.length > 0) {
144
- logger.error(` ${fileName}`);
145
- validation.errors.forEach((err) => {
146
- const friendly = getFriendlyError(err);
147
- if (friendly) {
148
- const loc = err.location ? `[line ${err.location.line}] ` : '';
149
- logger.error(` ${loc}${friendly.title}: ${friendly.explanation}`);
150
- logger.warn(` How to fix: ${friendly.fix}`);
151
- if (err.docUrl) {
152
- logger.warn(` See: ${err.docUrl}`);
153
- }
140
+ // Validate the AST
141
+ const validation = validator.validate(parseResult.ast, { strictMode: strict });
142
+ // In strict mode, validation errors block compilation
143
+ if (strict && validation.errors.length > 0) {
144
+ logger.error(` ${fileName}`);
145
+ validation.errors.forEach((err) => {
146
+ const friendly = getFriendlyError(err);
147
+ if (friendly) {
148
+ const loc = err.location ? `[line ${err.location.line}] ` : '';
149
+ logger.error(` ${loc}${friendly.title}: ${friendly.explanation}`);
150
+ logger.warn(` How to fix: ${friendly.fix}`);
151
+ if (err.docUrl) {
152
+ logger.warn(` See: ${err.docUrl}`);
154
153
  }
155
- else {
156
- let msg = ` ${err.message}`;
157
- if (err.node) {
158
- msg += ` (node: ${err.node})`;
159
- }
160
- logger.error(msg);
161
- if (err.docUrl) {
162
- logger.warn(` See: ${err.docUrl}`);
163
- }
154
+ }
155
+ else {
156
+ let msg = ` ${err.message}`;
157
+ if (err.node) {
158
+ msg += ` (node: ${err.node})`;
164
159
  }
165
- });
166
- errorCount++;
167
- continue;
168
- }
169
- if (validation.warnings.length > 0 && verbose) {
170
- validation.warnings.forEach((warn) => {
171
- const friendly = getFriendlyError(warn);
172
- if (friendly) {
173
- const loc = warn.location ? `[line ${warn.location.line}] ` : '';
174
- logger.warn(` ${loc}${friendly.title}: ${friendly.explanation}`);
175
- logger.warn(` How to fix: ${friendly.fix}`);
160
+ logger.error(msg);
161
+ if (err.docUrl) {
162
+ logger.warn(` See: ${err.docUrl}`);
176
163
  }
177
- else {
178
- logger.warn(` ${warn.message}`);
164
+ }
165
+ });
166
+ errorCount++;
167
+ continue;
168
+ }
169
+ // Always show validation warnings (not just in verbose mode)
170
+ if (validation.warnings.length > 0) {
171
+ validation.warnings.forEach((warn) => {
172
+ const friendly = getFriendlyError(warn);
173
+ if (friendly) {
174
+ const loc = warn.location ? `[line ${warn.location.line}] ` : '';
175
+ logger.warn(` ${loc}${friendly.title}: ${friendly.explanation}`);
176
+ if (verbose) {
177
+ logger.warn(` How to fix: ${friendly.fix}`);
179
178
  }
180
- });
181
- }
179
+ }
180
+ else {
181
+ logger.warn(` ${warn.message}`);
182
+ }
183
+ });
182
184
  }
183
185
  // Read original source
184
186
  const sourceCode = fs.readFileSync(file, 'utf8');
@@ -13,6 +13,10 @@ export interface DevOptions extends DevModeOptions {
13
13
  clean?: boolean;
14
14
  /** Compilation target (default: typescript in-place) */
15
15
  target?: string;
16
+ /** Mock config for built-in nodes as JSON string */
17
+ mocks?: string;
18
+ /** Path to JSON file with mock config */
19
+ mocksFile?: string;
16
20
  }
17
21
  /**
18
22
  * Dev command: watch + compile + run in a single loop.
@@ -52,11 +52,38 @@ function parseParams(options) {
52
52
  }
53
53
  return {};
54
54
  }
55
+ /**
56
+ * Parse mock config from --mocks or --mocks-file.
57
+ */
58
+ function parseMocks(options) {
59
+ if (options.mocks) {
60
+ try {
61
+ return JSON.parse(options.mocks);
62
+ }
63
+ catch {
64
+ throw new Error(`Invalid JSON in --mocks: ${options.mocks}`);
65
+ }
66
+ }
67
+ if (options.mocksFile) {
68
+ const mocksFilePath = path.resolve(options.mocksFile);
69
+ if (!fs.existsSync(mocksFilePath)) {
70
+ throw new Error(`Mocks file not found: ${mocksFilePath}`);
71
+ }
72
+ try {
73
+ const content = fs.readFileSync(mocksFilePath, 'utf8');
74
+ return JSON.parse(content);
75
+ }
76
+ catch {
77
+ throw new Error(`Failed to parse mocks file: ${options.mocksFile}`);
78
+ }
79
+ }
80
+ return undefined;
81
+ }
55
82
  /**
56
83
  * Run a single compile + execute cycle.
57
84
  * Returns true if both compile and run succeeded.
58
85
  */
59
- async function compileAndRun(filePath, params, options) {
86
+ async function compileAndRun(filePath, params, mocks, options) {
60
87
  // Step 1: Compile
61
88
  const compileOpts = {
62
89
  format: options.format,
@@ -96,6 +123,7 @@ async function compileAndRun(filePath, params, options) {
96
123
  workflowName: options.workflow,
97
124
  production: options.production ?? false,
98
125
  includeTrace: !options.production,
126
+ mocks,
99
127
  });
100
128
  if (options.json) {
101
129
  process.stdout.write(JSON.stringify({
@@ -143,16 +171,20 @@ export async function devCommand(input, options = {}) {
143
171
  throw new Error(`Unknown dev target "${options.target}". ${available.length ? `Available: ${available.join(', ')}` : 'No dev mode providers registered. Install a pack that provides one.'}`);
144
172
  }
145
173
  const params = parseParams(options);
174
+ const mocks = parseMocks(options);
146
175
  if (!options.json) {
147
176
  logger.section('Dev Mode');
148
177
  logger.info(`File: ${path.basename(filePath)}`);
149
178
  if (Object.keys(params).length > 0) {
150
179
  logger.info(`Params: ${JSON.stringify(params)}`);
151
180
  }
181
+ if (mocks) {
182
+ logger.info(`Mocks: ${JSON.stringify(mocks)}`);
183
+ }
152
184
  logger.newline();
153
185
  }
154
186
  // Initial compile + run
155
- await compileAndRun(filePath, params, options);
187
+ await compileAndRun(filePath, params, mocks, options);
156
188
  // If --once, exit after first cycle
157
189
  if (options.once) {
158
190
  return;
@@ -173,7 +205,7 @@ export async function devCommand(input, options = {}) {
173
205
  if (!options.json) {
174
206
  cycleSeparator(file);
175
207
  }
176
- await compileAndRun(filePath, params, options);
208
+ await compileAndRun(filePath, params, mocks, options);
177
209
  });
178
210
  // Handle process termination
179
211
  const cleanup = () => {
@@ -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.29.0";
5990
+ VERSION = "0.30.0";
5991
5991
  }
5992
5992
  });
5993
5993
 
@@ -24365,6 +24365,12 @@ var init_port_parser = __esm({
24365
24365
  function parseNodeLine(input, warnings) {
24366
24366
  const lexResult = JSDocLexer.tokenize(input);
24367
24367
  if (lexResult.errors.length > 0) {
24368
+ const truncatedInput = input.length > 60 ? input.substring(0, 60) + "..." : input;
24369
+ warnings.push(
24370
+ `Failed to tokenize node line: "${truncatedInput}"
24371
+ Error: ${lexResult.errors[0].message}
24372
+ Expected format: @node instanceId NodeType`
24373
+ );
24368
24374
  return null;
24369
24375
  }
24370
24376
  if (lexResult.tokens.length === 0) {
@@ -24452,14 +24458,13 @@ var init_node_parser = __esm({
24452
24458
  this.CONSUME(LabelPrefix);
24453
24459
  this.CONSUME(StringLiteral, { LABEL: "labelValue" });
24454
24460
  });
24455
- // expr: port="value", port2="value2"
24461
+ // expr: port="value", port2="value2" (comma optional between assignments)
24456
24462
  exprAttr = this.RULE("exprAttr", () => {
24457
24463
  this.CONSUME(ExprPrefix);
24458
- this.AT_LEAST_ONE_SEP({
24459
- SEP: Comma,
24460
- DEF: () => {
24461
- this.SUBRULE(this.exprAssignment);
24462
- }
24464
+ this.SUBRULE(this.exprAssignment);
24465
+ this.MANY(() => {
24466
+ this.OPTION(() => this.CONSUME(Comma));
24467
+ this.SUBRULE2(this.exprAssignment);
24463
24468
  });
24464
24469
  });
24465
24470
  // port="value"
@@ -30075,6 +30080,7 @@ async function parseWorkflow(filePath, options) {
30075
30080
  }
30076
30081
  const parsed = parser.parse(filePath);
30077
30082
  warnings.push(...parsed.warnings);
30083
+ errors2.push(...parsed.errors);
30078
30084
  availableWorkflows = parsed.workflows.map((w) => w.functionName);
30079
30085
  let workflowName = options?.workflowName;
30080
30086
  if (!workflowName) {
@@ -30124,7 +30130,7 @@ async function parseWorkflow(filePath, options) {
30124
30130
  }
30125
30131
  return {
30126
30132
  ast: workflow,
30127
- errors: [],
30133
+ errors: errors2,
30128
30134
  warnings,
30129
30135
  availableWorkflows,
30130
30136
  allWorkflows: parsed.workflows
@@ -47596,45 +47602,45 @@ async function compileCommand(input, options = {}) {
47596
47602
  errorCount++;
47597
47603
  continue;
47598
47604
  }
47599
- if (strict) {
47600
- const validation = validator.validate(parseResult.ast, { strictMode: true });
47601
- if (validation.errors.length > 0) {
47602
- logger.error(` ${fileName}`);
47603
- validation.errors.forEach((err) => {
47604
- const friendly = getFriendlyError(err);
47605
- if (friendly) {
47606
- const loc = err.location ? `[line ${err.location.line}] ` : "";
47607
- logger.error(` ${loc}${friendly.title}: ${friendly.explanation}`);
47608
- logger.warn(` How to fix: ${friendly.fix}`);
47609
- if (err.docUrl) {
47610
- logger.warn(` See: ${err.docUrl}`);
47611
- }
47612
- } else {
47613
- let msg = ` ${err.message}`;
47614
- if (err.node) {
47615
- msg += ` (node: ${err.node})`;
47616
- }
47617
- logger.error(msg);
47618
- if (err.docUrl) {
47619
- logger.warn(` See: ${err.docUrl}`);
47620
- }
47605
+ const validation = validator.validate(parseResult.ast, { strictMode: strict });
47606
+ if (strict && validation.errors.length > 0) {
47607
+ logger.error(` ${fileName}`);
47608
+ validation.errors.forEach((err) => {
47609
+ const friendly = getFriendlyError(err);
47610
+ if (friendly) {
47611
+ const loc = err.location ? `[line ${err.location.line}] ` : "";
47612
+ logger.error(` ${loc}${friendly.title}: ${friendly.explanation}`);
47613
+ logger.warn(` How to fix: ${friendly.fix}`);
47614
+ if (err.docUrl) {
47615
+ logger.warn(` See: ${err.docUrl}`);
47621
47616
  }
47622
- });
47623
- errorCount++;
47624
- continue;
47625
- }
47626
- if (validation.warnings.length > 0 && verbose) {
47627
- validation.warnings.forEach((warn) => {
47628
- const friendly = getFriendlyError(warn);
47629
- if (friendly) {
47630
- const loc = warn.location ? `[line ${warn.location.line}] ` : "";
47631
- logger.warn(` ${loc}${friendly.title}: ${friendly.explanation}`);
47617
+ } else {
47618
+ let msg = ` ${err.message}`;
47619
+ if (err.node) {
47620
+ msg += ` (node: ${err.node})`;
47621
+ }
47622
+ logger.error(msg);
47623
+ if (err.docUrl) {
47624
+ logger.warn(` See: ${err.docUrl}`);
47625
+ }
47626
+ }
47627
+ });
47628
+ errorCount++;
47629
+ continue;
47630
+ }
47631
+ if (validation.warnings.length > 0) {
47632
+ validation.warnings.forEach((warn) => {
47633
+ const friendly = getFriendlyError(warn);
47634
+ if (friendly) {
47635
+ const loc = warn.location ? `[line ${warn.location.line}] ` : "";
47636
+ logger.warn(` ${loc}${friendly.title}: ${friendly.explanation}`);
47637
+ if (verbose) {
47632
47638
  logger.warn(` How to fix: ${friendly.fix}`);
47633
- } else {
47634
- logger.warn(` ${warn.message}`);
47635
47639
  }
47636
- });
47637
- }
47640
+ } else {
47641
+ logger.warn(` ${warn.message}`);
47642
+ }
47643
+ });
47638
47644
  }
47639
47645
  const sourceCode = fs11.readFileSync(file, "utf8");
47640
47646
  const result = generateInPlace(sourceCode, parseResult.ast, { production, moduleFormat, sourceFile: file, skipParamReturns: clean });
@@ -60756,7 +60762,29 @@ function parseParams(options) {
60756
60762
  }
60757
60763
  return {};
60758
60764
  }
60759
- async function compileAndRun(filePath, params, options) {
60765
+ function parseMocks(options) {
60766
+ if (options.mocks) {
60767
+ try {
60768
+ return JSON.parse(options.mocks);
60769
+ } catch {
60770
+ throw new Error(`Invalid JSON in --mocks: ${options.mocks}`);
60771
+ }
60772
+ }
60773
+ if (options.mocksFile) {
60774
+ const mocksFilePath = path21.resolve(options.mocksFile);
60775
+ if (!fs21.existsSync(mocksFilePath)) {
60776
+ throw new Error(`Mocks file not found: ${mocksFilePath}`);
60777
+ }
60778
+ try {
60779
+ const content = fs21.readFileSync(mocksFilePath, "utf8");
60780
+ return JSON.parse(content);
60781
+ } catch {
60782
+ throw new Error(`Failed to parse mocks file: ${options.mocksFile}`);
60783
+ }
60784
+ }
60785
+ return void 0;
60786
+ }
60787
+ async function compileAndRun(filePath, params, mocks, options) {
60760
60788
  const compileOpts = {
60761
60789
  format: options.format,
60762
60790
  clean: options.clean
@@ -60790,7 +60818,8 @@ async function compileAndRun(filePath, params, options) {
60790
60818
  const result = await executeWorkflowFromFile(filePath, params, {
60791
60819
  workflowName: options.workflow,
60792
60820
  production: options.production ?? false,
60793
- includeTrace: !options.production
60821
+ includeTrace: !options.production,
60822
+ mocks
60794
60823
  });
60795
60824
  if (options.json) {
60796
60825
  process.stdout.write(
@@ -60838,15 +60867,19 @@ async function devCommand(input, options = {}) {
60838
60867
  );
60839
60868
  }
60840
60869
  const params = parseParams(options);
60870
+ const mocks = parseMocks(options);
60841
60871
  if (!options.json) {
60842
60872
  logger.section("Dev Mode");
60843
60873
  logger.info(`File: ${path21.basename(filePath)}`);
60844
60874
  if (Object.keys(params).length > 0) {
60845
60875
  logger.info(`Params: ${JSON.stringify(params)}`);
60846
60876
  }
60877
+ if (mocks) {
60878
+ logger.info(`Mocks: ${JSON.stringify(mocks)}`);
60879
+ }
60847
60880
  logger.newline();
60848
60881
  }
60849
- await compileAndRun(filePath, params, options);
60882
+ await compileAndRun(filePath, params, mocks, options);
60850
60883
  if (options.once) {
60851
60884
  return;
60852
60885
  }
@@ -60864,7 +60897,7 @@ async function devCommand(input, options = {}) {
60864
60897
  if (!options.json) {
60865
60898
  cycleSeparator(file);
60866
60899
  }
60867
- await compileAndRun(filePath, params, options);
60900
+ await compileAndRun(filePath, params, mocks, options);
60868
60901
  });
60869
60902
  const cleanup = () => {
60870
60903
  if (!options.json) {
@@ -88927,7 +88960,7 @@ function parseIntStrict(value) {
88927
88960
  // src/cli/index.ts
88928
88961
  init_logger();
88929
88962
  init_error_utils();
88930
- var version2 = true ? "0.29.0" : "0.0.0-dev";
88963
+ var version2 = true ? "0.30.0" : "0.0.0-dev";
88931
88964
  var program2 = new Command();
88932
88965
  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", () => {
88933
88966
  logger.banner(version2);
@@ -89003,7 +89036,7 @@ program2.command("watch <input>").description("Watch workflow files and recompil
89003
89036
  if (options.workflow) options.workflowName = options.workflow;
89004
89037
  await watchCommand2(input, options);
89005
89038
  }));
89006
- program2.command("dev <input>").description("Watch, compile, and run workflow on changes").option("--params <json>", "Input parameters as JSON string").option("--params-file <path>", "Path to JSON file with input parameters").option("-w, --workflow <name>", "Specific workflow name to run").option("-p, --production", "Run in production mode (no trace events)", false).option("-f, --format <format>", "Module format: esm, cjs, or auto", "auto").option("--clean", "Omit redundant @param/@returns annotations", false).option("--once", "Run once then exit", false).option("--json", "Output result as JSON", false).option("--target <target>", "Compilation target (default: typescript)").action(wrapAction(async (input, options) => {
89039
+ program2.command("dev <input>").description("Watch, compile, and run workflow on changes").option("--params <json>", "Input parameters as JSON string").option("--params-file <path>", "Path to JSON file with input parameters").option("-w, --workflow <name>", "Specific workflow name to run").option("-p, --production", "Run in production mode (no trace events)", false).option("-f, --format <format>", "Module format: esm, cjs, or auto", "auto").option("--clean", "Omit redundant @param/@returns annotations", false).option("--once", "Run once then exit", false).option("--json", "Output result as JSON", false).option("--target <target>", "Compilation target (default: typescript)").option("--mocks <json>", "Mock config for built-in nodes (events, invocations, agents, fast) as JSON").option("--mocks-file <path>", "Path to JSON file with mock config for built-in nodes").action(wrapAction(async (input, options) => {
89007
89040
  const { devCommand: devCommand2 } = await Promise.resolve().then(() => (init_dev(), dev_exports));
89008
89041
  await devCommand2(input, options);
89009
89042
  }));
package/dist/cli/index.js CHANGED
@@ -223,6 +223,8 @@ program
223
223
  .option('--once', 'Run once then exit', false)
224
224
  .option('--json', 'Output result as JSON', false)
225
225
  .option('--target <target>', 'Compilation target (default: typescript)')
226
+ .option('--mocks <json>', 'Mock config for built-in nodes (events, invocations, agents, fast) as JSON')
227
+ .option('--mocks-file <path>', 'Path to JSON file with mock config for built-in nodes')
226
228
  .action(wrapAction(async (input, options) => {
227
229
  const { devCommand } = await import('./commands/dev.js');
228
230
  await devCommand(input, options);
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.29.0";
1
+ export declare const VERSION = "0.30.0";
2
2
  //# sourceMappingURL=generated-version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.ts — do not edit manually
2
- export const VERSION = '0.29.0';
2
+ export const VERSION = '0.30.0';
3
3
  //# sourceMappingURL=generated-version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Flow Weaver: deterministic TypeScript workflow compiler. Define workflows with JSDoc annotations, compile to standalone functions with zero runtime dependencies.",
5
5
  "private": false,
6
6
  "type": "module",