@synergenius/flow-weaver 0.17.2 → 0.17.4

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.
@@ -156,6 +156,12 @@ export function parseTriggerLine(input, warnings) {
156
156
  return null;
157
157
  }
158
158
  const result = visitorInstance.visit(cst);
159
+ // Empty result means the parser consumed @trigger but found no event=/cron= assignments.
160
+ // Return null so the caller can delegate to domain-specific handlers (e.g. CI/CD triggers
161
+ // like @trigger push, @trigger pull_request, etc.)
162
+ if (!result.event && !result.cron) {
163
+ return null;
164
+ }
159
165
  // Validate cron expression
160
166
  if (result.cron && !CRON_REGEX.test(result.cron)) {
161
167
  warnings.push(`Invalid cron expression: "${result.cron}". Expected 5 fields (minute hour day month weekday).`);
@@ -103,7 +103,9 @@ export async function exportCommand(input, options) {
103
103
  logger.section('Handler Preview');
104
104
  const handlerFile = result.files.find((f) => f.path.endsWith('handler.ts') ||
105
105
  f.path.endsWith(`${result.workflow}.ts`) ||
106
- f.path.endsWith('index.ts'));
106
+ f.path.endsWith('index.ts') ||
107
+ f.path.endsWith('.yml') ||
108
+ f.path.endsWith('.yaml'));
107
109
  if (handlerFile) {
108
110
  // Show first 40 lines of handler
109
111
  const lines = handlerFile.content.split('\n');
@@ -114,6 +116,14 @@ export async function exportCommand(input, options) {
114
116
  }
115
117
  }
116
118
  }
119
+ // Show warnings about unsupported annotations
120
+ if (result.warnings && result.warnings.length > 0) {
121
+ logger.newline();
122
+ logger.section('Warnings');
123
+ for (const warning of result.warnings) {
124
+ logger.warn(warning);
125
+ }
126
+ }
117
127
  // Get deploy instructions from the target
118
128
  logger.newline();
119
129
  logger.section('Next Steps');
@@ -17061,6 +17061,9 @@ function parseTriggerLine(input, warnings) {
17061
17061
  return null;
17062
17062
  }
17063
17063
  const result = visitorInstance8.visit(cst);
17064
+ if (!result.event && !result.cron) {
17065
+ return null;
17066
+ }
17064
17067
  if (result.cron && !CRON_REGEX.test(result.cron)) {
17065
17068
  warnings.push(`Invalid cron expression: "${result.cron}". Expected 5 fields (minute hour day month weekday).`);
17066
17069
  }
@@ -25611,7 +25614,7 @@ var VERSION2;
25611
25614
  var init_generated_version = __esm({
25612
25615
  "src/generated-version.ts"() {
25613
25616
  "use strict";
25614
- VERSION2 = "0.17.2";
25617
+ VERSION2 = "0.17.4";
25615
25618
  }
25616
25619
  });
25617
25620
 
@@ -71463,7 +71466,7 @@ var init_base_target = __esm({
71463
71466
  },
71464
71467
  "slack-notify": {
71465
71468
  githubAction: "slackapi/slack-github-action@v1",
71466
- gitlabScript: ['curl -X POST -H "Content-type: application/json" --data "{\\"text\\":\\"Pipeline complete\\"}" $SLACK_WEBHOOK_URL'],
71469
+ gitlabScript: [`curl -X POST -H 'Content-type: application/json' --data '{"text":"Pipeline complete"}' $SLACK_WEBHOOK_URL`],
71467
71470
  label: "Send Slack notification"
71468
71471
  },
71469
71472
  "health-check": {
@@ -71575,11 +71578,10 @@ var init_base_target = __esm({
71575
71578
  const stages = ast.options?.cicd?.stages;
71576
71579
  if (stages && stages.length > 0) {
71577
71580
  const depthMap = this.computeJobDepths(jobs);
71578
- for (const jc of jobConfigs || []) {
71579
- const job = jobs.find((j) => j.id === jc.id);
71580
- if (job && !job.stage) {
71581
+ for (const job of jobs) {
71582
+ if (!job.stage) {
71581
71583
  for (const s of stages) {
71582
- if (jc.id === s.name || jc.id.startsWith(s.name + "-") || jc.id.startsWith(s.name + "_")) {
71584
+ if (job.id === s.name || job.id.startsWith(s.name + "-") || job.id.startsWith(s.name + "_")) {
71583
71585
  job.stage = s.name;
71584
71586
  break;
71585
71587
  }
@@ -73354,6 +73356,18 @@ function parseJob(text, d, warnings) {
73354
73356
  jc.rules = jc.rules || [];
73355
73357
  jc.rules.push({ if: value2 });
73356
73358
  break;
73359
+ case "when": {
73360
+ jc.rules = jc.rules || [];
73361
+ if (jc.rules.length === 0) jc.rules.push({});
73362
+ jc.rules[jc.rules.length - 1].when = value2;
73363
+ break;
73364
+ }
73365
+ case "changes": {
73366
+ jc.rules = jc.rules || [];
73367
+ if (jc.rules.length === 0) jc.rules.push({});
73368
+ jc.rules[jc.rules.length - 1].changes = value2.split(",").map((s) => s.trim()).filter(Boolean);
73369
+ break;
73370
+ }
73357
73371
  case "reports": {
73358
73372
  jc.reports = jc.reports || [];
73359
73373
  for (const pair of value2.split(",")) {
@@ -73794,6 +73808,18 @@ function deploySsh(sshKey: string = ''): { result: string } { return { result: '
73794
73808
  */
73795
73809
  function deployS3(accessKey: string = '', secretKey: string = ''): { result: string } { return { result: 'deployed' }; }
73796
73810
  ` : "";
73811
+ const stageAnnotations = hasDeploy ? ` * @stage test
73812
+ * @stage build
73813
+ * @stage deploy
73814
+ ` : ` * @stage test
73815
+ * @stage build
73816
+ `;
73817
+ const jobAnnotations = hasDeploy ? ` * @job test retry=1
73818
+ * @job build timeout="10m"
73819
+ * @job deploy allow_failure=false
73820
+ ` : ` * @job test retry=1
73821
+ * @job build timeout="10m"
73822
+ `;
73797
73823
  return `/** @flowWeaver nodeType
73798
73824
  * @expression
73799
73825
  * @label Checkout code
@@ -73832,6 +73858,7 @@ ${deployStub}
73832
73858
  * @secret NPM_TOKEN - npm auth token${deploySecret}
73833
73859
  * @cache npm key="package-lock.json"
73834
73860
  *
73861
+ ${stageAnnotations}${jobAnnotations} *
73835
73862
  * @node co checkout [job: "test"] [position: 270 0]
73836
73863
  * @node setup setupNode [job: "test"] [position: 540 0]
73837
73864
  * @node install npmInstall [job: "test"] [position: 810 0]
@@ -73996,6 +74023,15 @@ var cicdMultiEnvTemplate = {
73996
74023
  const name = opts.workflowName || "multiEnvPipeline";
73997
74024
  const envs = (opts.config?.environments || "staging,production").split(",").map((e) => e.trim());
73998
74025
  const envAnnotations = envs.map((env) => ` * @environment ${env} url="https://${env}.example.com"`).join("\n");
74026
+ const stageAnnotations = ` * @stage test
74027
+ * @stage build
74028
+ * @stage deploy
74029
+ `;
74030
+ const jobAnnotations = [
74031
+ ` * @job test retry=1`,
74032
+ ` * @job build timeout="10m"`,
74033
+ ...envs.map((env) => ` * @job deploy-${env} allow_failure=${env === "staging" ? "true" : "false"}`)
74034
+ ].join("\n");
73999
74035
  let x = 270;
74000
74036
  const nodeAnnotations = [];
74001
74037
  const pathParts = ["Start"];
@@ -74048,6 +74084,8 @@ ${envAnnotations}
74048
74084
  * @cache npm key="package-lock.json"
74049
74085
  * @artifact dist path="dist/" retention=3
74050
74086
  *
74087
+ ${stageAnnotations}${jobAnnotations}
74088
+ *
74051
74089
  ${nodeAnnotations.join("\n")}
74052
74090
  *
74053
74091
  * @path ${pathParts.join(" -> ")}
@@ -107919,7 +107957,8 @@ async function exportSingleWorkflowViaRegistry(target, inputPath, outputDir, isD
107919
107957
  target: options.target,
107920
107958
  files,
107921
107959
  workflow: workflow.name,
107922
- description: workflow.description
107960
+ description: workflow.description,
107961
+ warnings: artifacts.warnings
107923
107962
  };
107924
107963
  }
107925
107964
  async function exportMultiWorkflowViaRegistry(target, inputPath, outputDir, isDryRun, options) {
@@ -107981,7 +108020,8 @@ async function exportMultiWorkflowViaRegistry(target, inputPath, outputDir, isDr
107981
108020
  target: options.target,
107982
108021
  files,
107983
108022
  workflow: serviceName,
107984
- workflows: selectedWorkflows.map((w) => w.name)
108023
+ workflows: selectedWorkflows.map((w) => w.name),
108024
+ warnings: artifacts.warnings
107985
108025
  };
107986
108026
  }
107987
108027
  async function compileToOutput(inputPath, functionName, outputDir, production) {
@@ -108070,7 +108110,7 @@ async function exportCommand(input, options) {
108070
108110
  logger.newline();
108071
108111
  logger.section("Handler Preview");
108072
108112
  const handlerFile = result.files.find(
108073
- (f) => f.path.endsWith("handler.ts") || f.path.endsWith(`${result.workflow}.ts`) || f.path.endsWith("index.ts")
108113
+ (f) => f.path.endsWith("handler.ts") || f.path.endsWith(`${result.workflow}.ts`) || f.path.endsWith("index.ts") || f.path.endsWith(".yml") || f.path.endsWith(".yaml")
108074
108114
  );
108075
108115
  if (handlerFile) {
108076
108116
  const lines = handlerFile.content.split("\n");
@@ -108081,6 +108121,13 @@ async function exportCommand(input, options) {
108081
108121
  }
108082
108122
  }
108083
108123
  }
108124
+ if (result.warnings && result.warnings.length > 0) {
108125
+ logger.newline();
108126
+ logger.section("Warnings");
108127
+ for (const warning of result.warnings) {
108128
+ logger.warn(warning);
108129
+ }
108130
+ }
108084
108131
  logger.newline();
108085
108132
  logger.section("Next Steps");
108086
108133
  const { createTargetRegistry: createTargetRegistry2 } = await Promise.resolve().then(() => (init_deployment(), deployment_exports));
@@ -109257,7 +109304,7 @@ function displayInstalledPackage(pkg) {
109257
109304
 
109258
109305
  // src/cli/index.ts
109259
109306
  init_error_utils();
109260
- var version2 = true ? "0.17.2" : "0.0.0-dev";
109307
+ var version2 = true ? "0.17.4" : "0.0.0-dev";
109261
109308
  var program2 = new Command();
109262
109309
  program2.name("flow-weaver").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", () => {
109263
109310
  logger.banner(version2);
@@ -67,6 +67,8 @@ export interface ExportArtifacts {
67
67
  workflowName: string;
68
68
  /** Entry point file */
69
69
  entryPoint: string;
70
+ /** Warnings about unsupported or dropped annotations */
71
+ warnings?: string[];
70
72
  }
71
73
  /**
72
74
  * Deployment instructions
@@ -55,6 +55,8 @@ export interface ExportResult {
55
55
  workflows?: string[];
56
56
  /** OpenAPI spec (if generated) */
57
57
  openApiSpec?: object;
58
+ /** Warnings about unsupported or dropped annotations */
59
+ warnings?: string[];
58
60
  }
59
61
  /**
60
62
  * Export a workflow for deployment.
@@ -112,6 +112,7 @@ async function exportSingleWorkflowViaRegistry(target, inputPath, outputDir, isD
112
112
  files,
113
113
  workflow: workflow.name,
114
114
  description: workflow.description,
115
+ warnings: artifacts.warnings,
115
116
  };
116
117
  }
117
118
  /**
@@ -177,6 +178,7 @@ async function exportMultiWorkflowViaRegistry(target, inputPath, outputDir, isDr
177
178
  files,
178
179
  workflow: serviceName,
179
180
  workflows: selectedWorkflows.map((w) => w.name),
181
+ warnings: artifacts.warnings,
180
182
  };
181
183
  }
182
184
  /**
@@ -79,7 +79,7 @@ export const NODE_ACTION_MAP = {
79
79
  },
80
80
  'slack-notify': {
81
81
  githubAction: 'slackapi/slack-github-action@v1',
82
- gitlabScript: ['curl -X POST -H "Content-type: application/json" --data "{\\\"text\\\":\\\"Pipeline complete\\\"}" $SLACK_WEBHOOK_URL'],
82
+ gitlabScript: ["curl -X POST -H 'Content-type: application/json' --data '{\"text\":\"Pipeline complete\"}' $SLACK_WEBHOOK_URL"],
83
83
  label: 'Send Slack notification',
84
84
  },
85
85
  'health-check': {
@@ -224,11 +224,11 @@ export class BaseCICDTarget extends BaseExportTarget {
224
224
  const stages = ast.options?.cicd?.stages;
225
225
  if (stages && stages.length > 0) {
226
226
  const depthMap = this.computeJobDepths(jobs);
227
- for (const jc of jobConfigs || []) {
228
- const job = jobs.find(j => j.id === jc.id);
229
- if (job && !job.stage) {
227
+ // Match ALL jobs by name prefix to stages, not just configured ones
228
+ for (const job of jobs) {
229
+ if (!job.stage) {
230
230
  for (const s of stages) {
231
- if (jc.id === s.name || jc.id.startsWith(s.name + '-') || jc.id.startsWith(s.name + '_')) {
231
+ if (job.id === s.name || job.id.startsWith(s.name + '-') || job.id.startsWith(s.name + '_')) {
232
232
  job.stage = s.name;
233
233
  break;
234
234
  }
@@ -326,6 +326,22 @@ function parseJob(text, d, warnings) {
326
326
  jc.rules = jc.rules || [];
327
327
  jc.rules.push({ if: value });
328
328
  break;
329
+ case 'when': {
330
+ // Modifier: sets `when` on the last rule, or creates a standalone rule
331
+ jc.rules = jc.rules || [];
332
+ if (jc.rules.length === 0)
333
+ jc.rules.push({});
334
+ jc.rules[jc.rules.length - 1].when = value;
335
+ break;
336
+ }
337
+ case 'changes': {
338
+ // Modifier: sets `changes` on the last rule
339
+ jc.rules = jc.rules || [];
340
+ if (jc.rules.length === 0)
341
+ jc.rules.push({});
342
+ jc.rules[jc.rules.length - 1].changes = value.split(',').map(s => s.trim()).filter(Boolean);
343
+ break;
344
+ }
329
345
  case 'reports': {
330
346
  jc.reports = jc.reports || [];
331
347
  for (const pair of value.split(',')) {
@@ -35,6 +35,12 @@ export const cicdMultiEnvTemplate = {
35
35
  const envAnnotations = envs
36
36
  .map((env) => ` * @environment ${env} url="https://${env}.example.com"`)
37
37
  .join('\n');
38
+ const stageAnnotations = ` * @stage test\n * @stage build\n * @stage deploy\n`;
39
+ const jobAnnotations = [
40
+ ` * @job test retry=1`,
41
+ ` * @job build timeout="10m"`,
42
+ ...envs.map((env) => ` * @job deploy-${env} allow_failure=${env === 'staging' ? 'true' : 'false'}`),
43
+ ].join('\n');
38
44
  let x = 270;
39
45
  const nodeAnnotations = [];
40
46
  const pathParts = ['Start'];
@@ -89,6 +95,8 @@ ${envAnnotations}
89
95
  * @cache npm key="package-lock.json"
90
96
  * @artifact dist path="dist/" retention=3
91
97
  *
98
+ ${stageAnnotations}${jobAnnotations}
99
+ *
92
100
  ${nodeAnnotations.join('\n')}
93
101
  *
94
102
  * @path ${pathParts.join(' -> ')}
@@ -78,6 +78,12 @@ function deploySsh(sshKey: string = ''): { result: string } { return { result: '
78
78
  function deployS3(accessKey: string = '', secretKey: string = ''): { result: string } { return { result: 'deployed' }; }
79
79
  `
80
80
  : '';
81
+ const stageAnnotations = hasDeploy
82
+ ? ` * @stage test\n * @stage build\n * @stage deploy\n`
83
+ : ` * @stage test\n * @stage build\n`;
84
+ const jobAnnotations = hasDeploy
85
+ ? ` * @job test retry=1\n * @job build timeout="10m"\n * @job deploy allow_failure=false\n`
86
+ : ` * @job test retry=1\n * @job build timeout="10m"\n`;
81
87
  return `/** @flowWeaver nodeType
82
88
  * @expression
83
89
  * @label Checkout code
@@ -116,6 +122,7 @@ ${deployStub}
116
122
  * @secret NPM_TOKEN - npm auth token${deploySecret}
117
123
  * @cache npm key="package-lock.json"
118
124
  *
125
+ ${stageAnnotations}${jobAnnotations} *
119
126
  * @node co checkout [job: "test"] [position: 270 0]
120
127
  * @node setup setupNode [job: "test"] [position: 540 0]
121
128
  * @node install npmInstall [job: "test"] [position: 810 0]
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.17.2";
1
+ export declare const VERSION = "0.17.4";
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.17.2';
2
+ export const VERSION = '0.17.4';
3
3
  //# sourceMappingURL=generated-version.js.map
@@ -572,7 +572,9 @@ Configures per-job settings. The name must match a `[job: "name"]` attribute use
572
572
  jobTag ::= "@job" IDENTIFIER { IDENTIFIER "=" ( STRING | IDENTIFIER | INTEGER ) }
573
573
  ```
574
574
 
575
- Recognized keys: `retry` (number), `allow_failure` (boolean), `timeout` (string), `runner` (string), `tags` (comma-list), `coverage` (string), `reports` (comma-list of type=path), `rules` (string), `extends` (string), `before_script` (comma-list), `variables` (comma-list of KEY=VALUE).
575
+ Recognized keys: `retry` (number), `allow_failure` (boolean), `timeout` (string), `runner` (string), `tags` (comma-list), `coverage` (string), `reports` (comma-list of type=path), `rules` (string), `when` (rule modifier), `changes` (rule modifier, comma-list), `extends` (string), `before_script` (comma-list), `variables` (comma-list of KEY=VALUE).
576
+
577
+ The `when` and `changes` keys are rule modifiers: they apply to the most recently declared `rules` entry for that job. If no `rules` entry exists yet, one is created automatically.
576
578
 
577
579
  **Examples:**
578
580
 
@@ -580,6 +582,8 @@ Recognized keys: `retry` (number), `allow_failure` (boolean), `timeout` (string)
580
582
  @job build retry=2 timeout="10m"
581
583
  @job test-unit coverage='/Coverage: (\d+)%/' reports="junit=test-results.xml"
582
584
  @job deploy allow_failure=true rules="$CI_COMMIT_BRANCH == main"
585
+ @job deploy rules="$CI_COMMIT_BRANCH == main" when=manual
586
+ @job deploy rules="$CI_COMMIT_TAG" changes="src/**,lib/**"
583
587
  @job lint tags="docker,linux" extends=".base-lint"
584
588
  ```
585
589
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver",
3
- "version": "0.17.2",
3
+ "version": "0.17.4",
4
4
  "description": "Deterministic workflow compiler for AI agents. Compiles to standalone TypeScript, no runtime dependencies.",
5
5
  "private": false,
6
6
  "type": "module",