@synergenius/flow-weaver 0.13.2 → 0.14.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.
Files changed (57) hide show
  1. package/README.md +41 -2
  2. package/dist/api/validate.js +8 -2
  3. package/dist/ast/types.d.ts +120 -0
  4. package/dist/chevrotain-parser/node-parser.d.ts +4 -0
  5. package/dist/chevrotain-parser/node-parser.js +41 -1
  6. package/dist/chevrotain-parser/port-parser.d.ts +1 -0
  7. package/dist/chevrotain-parser/port-parser.js +22 -2
  8. package/dist/chevrotain-parser/tokens.d.ts +3 -0
  9. package/dist/chevrotain-parser/tokens.js +15 -0
  10. package/dist/cli/commands/export.js +25 -38
  11. package/dist/cli/flow-weaver.mjs +63703 -54297
  12. package/dist/cli/templates/index.js +9 -0
  13. package/dist/cli/templates/workflows/cicd-docker.d.ts +9 -0
  14. package/dist/cli/templates/workflows/cicd-docker.js +110 -0
  15. package/dist/cli/templates/workflows/cicd-matrix.d.ts +9 -0
  16. package/dist/cli/templates/workflows/cicd-matrix.js +112 -0
  17. package/dist/cli/templates/workflows/cicd-multi-env.d.ts +9 -0
  18. package/dist/cli/templates/workflows/cicd-multi-env.js +118 -0
  19. package/dist/cli/templates/workflows/cicd-test-deploy.d.ts +11 -0
  20. package/dist/cli/templates/workflows/cicd-test-deploy.js +149 -0
  21. package/dist/constants.js +7 -0
  22. package/dist/deployment/index.d.ts +14 -7
  23. package/dist/deployment/index.js +29 -17
  24. package/dist/deployment/targets/base.d.ts +27 -2
  25. package/dist/deployment/targets/base.js +38 -6
  26. package/dist/deployment/targets/cicd-base.d.ts +111 -0
  27. package/dist/deployment/targets/cicd-base.js +357 -0
  28. package/dist/deployment/targets/cloudflare.d.ts +6 -0
  29. package/dist/deployment/targets/cloudflare.js +3 -0
  30. package/dist/deployment/targets/github-actions.d.ts +54 -0
  31. package/dist/deployment/targets/github-actions.js +366 -0
  32. package/dist/deployment/targets/gitlab-ci.d.ts +65 -0
  33. package/dist/deployment/targets/gitlab-ci.js +374 -0
  34. package/dist/deployment/targets/inngest.d.ts +25 -0
  35. package/dist/deployment/targets/inngest.js +10 -1
  36. package/dist/deployment/targets/lambda.d.ts +17 -0
  37. package/dist/deployment/targets/lambda.js +5 -0
  38. package/dist/deployment/targets/vercel.d.ts +16 -0
  39. package/dist/deployment/targets/vercel.js +5 -0
  40. package/dist/diagram/geometry.js +13 -5
  41. package/dist/export/index.d.ts +13 -9
  42. package/dist/export/index.js +129 -997
  43. package/dist/generated-version.d.ts +1 -1
  44. package/dist/generated-version.js +1 -1
  45. package/dist/jsdoc-parser.d.ts +130 -0
  46. package/dist/jsdoc-parser.js +408 -4
  47. package/dist/marketplace/index.d.ts +1 -1
  48. package/dist/marketplace/types.d.ts +13 -0
  49. package/dist/marketplace/validator.js +21 -2
  50. package/dist/mcp/tools-export.js +56 -14
  51. package/dist/parser.js +28 -1
  52. package/dist/validation/cicd-detection.d.ts +33 -0
  53. package/dist/validation/cicd-detection.js +76 -0
  54. package/dist/validation/cicd-rules.d.ts +62 -0
  55. package/dist/validation/cicd-rules.js +284 -0
  56. package/docs/reference/scaffold.md +4 -0
  57. package/package.json +4 -3
@@ -0,0 +1,366 @@
1
+ /**
2
+ * GitHub Actions Export Target
3
+ *
4
+ * Generates .github/workflows/<name>.yml from a Flow Weaver CI/CD workflow.
5
+ * No FW runtime dependency — outputs native GitHub Actions YAML.
6
+ *
7
+ * Mapping:
8
+ * - FW Node → GitHub Actions step (uses: or run:)
9
+ * - FW [job: "name"] → GitHub Actions job
10
+ * - FW @path → job `needs:` dependencies
11
+ * - FW @secret → ${{ secrets.NAME }}
12
+ * - FW @cache → actions/cache@v4
13
+ * - FW @artifact → actions/upload-artifact@v4 / actions/download-artifact@v4
14
+ * - FW @trigger → `on:` event configuration
15
+ */
16
+ import { stringify as yamlStringify } from 'yaml';
17
+ import { isCICDWorkflow } from '../../validation/cicd-detection.js';
18
+ import { BaseCICDTarget, } from './cicd-base.js';
19
+ import { parseWorkflow } from '../../api/index.js';
20
+ import * as path from 'path';
21
+ export class GitHubActionsTarget extends BaseCICDTarget {
22
+ name = 'github-actions';
23
+ description = 'GitHub Actions workflow YAML (.github/workflows/)';
24
+ deploySchema = {
25
+ runner: { type: 'string', description: 'GitHub runner label', default: 'ubuntu-latest' },
26
+ };
27
+ nodeTypeDeploySchema = {
28
+ action: { type: 'string', description: 'GitHub Action uses: value (e.g. actions/checkout@v4)' },
29
+ with: { type: 'string', description: 'JSON object for with: parameters' },
30
+ label: { type: 'string', description: 'Step display name' },
31
+ };
32
+ async generate(options) {
33
+ const filePath = path.resolve(options.sourceFile);
34
+ const outputDir = path.resolve(options.outputDir);
35
+ // Parse the workflow file to get AST
36
+ const parseResult = await parseWorkflow(filePath, { nodeTypesOnly: false });
37
+ if (parseResult.errors.length > 0) {
38
+ throw new Error(`Parse errors: ${parseResult.errors.join('; ')}`);
39
+ }
40
+ const allWorkflows = parseResult.allWorkflows || [];
41
+ const targetWorkflows = options.workflowName
42
+ ? allWorkflows.filter((w) => w.name === options.workflowName || w.functionName === options.workflowName)
43
+ : allWorkflows.filter((w) => isCICDWorkflow(w));
44
+ if (targetWorkflows.length === 0) {
45
+ throw new Error('No CI/CD workflows found. Ensure workflow has CI/CD annotations (@secret, @runner, @trigger, [job:], etc.)');
46
+ }
47
+ const files = [];
48
+ for (const ast of targetWorkflows) {
49
+ // Build job graph
50
+ const jobs = this.buildJobGraph(ast);
51
+ // Resolve secrets
52
+ this.resolveJobSecrets(jobs, ast, (name) => `\${{ secrets.${name} }}`);
53
+ // Inject artifacts
54
+ const artifacts = ast.options?.cicd?.artifacts || [];
55
+ this.injectArtifactSteps(jobs, artifacts);
56
+ // Apply cache, services, matrix from workflow options
57
+ this.applyWorkflowOptions(jobs, ast);
58
+ // Generate YAML
59
+ const yamlContent = this.renderWorkflowYAML(ast, jobs);
60
+ // Output path: .github/workflows/<name>.yml
61
+ const yamlFileName = `.github/workflows/${ast.functionName}.yml`;
62
+ files.push(this.createFile(outputDir, yamlFileName, yamlContent, 'config'));
63
+ // Generate secrets doc if secrets exist
64
+ const secrets = ast.options?.cicd?.secrets || [];
65
+ if (secrets.length > 0) {
66
+ const secretsDoc = this.generateSecretsDoc(secrets, 'github-actions');
67
+ files.push(this.createFile(outputDir, 'SECRETS_SETUP.md', secretsDoc, 'other'));
68
+ }
69
+ }
70
+ return {
71
+ files,
72
+ target: this.name,
73
+ workflowName: options.displayName || targetWorkflows[0].name,
74
+ entryPoint: files[0].relativePath,
75
+ };
76
+ }
77
+ getDeployInstructions(_artifacts) {
78
+ return {
79
+ title: 'Deploy GitHub Actions Workflow',
80
+ prerequisites: [
81
+ 'GitHub repository',
82
+ 'Repository secrets configured (see SECRETS_SETUP.md)',
83
+ ],
84
+ steps: [
85
+ 'Copy the .github/workflows/ directory to your repository root',
86
+ 'Configure required secrets in GitHub (Settings > Secrets > Actions)',
87
+ 'Push to trigger the workflow',
88
+ ],
89
+ localTestSteps: [
90
+ 'Install act: brew install act (or see https://github.com/nektos/act)',
91
+ 'Run locally: act push',
92
+ ],
93
+ links: [
94
+ { label: 'GitHub Actions Docs', url: 'https://docs.github.com/en/actions' },
95
+ { label: 'act - Local Testing', url: 'https://github.com/nektos/act' },
96
+ ],
97
+ };
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // Private: YAML Rendering
101
+ // ---------------------------------------------------------------------------
102
+ renderWorkflowYAML(ast, jobs) {
103
+ const doc = {};
104
+ // name
105
+ doc.name = ast.name;
106
+ // on: triggers
107
+ doc.on = this.renderTriggers(ast.options?.cicd?.triggers || []);
108
+ // concurrency
109
+ if (ast.options?.cicd?.concurrency) {
110
+ doc.concurrency = {
111
+ group: ast.options.cicd.concurrency.group,
112
+ 'cancel-in-progress': ast.options.cicd.concurrency.cancelInProgress ?? false,
113
+ };
114
+ }
115
+ // jobs
116
+ const jobsObj = {};
117
+ for (const job of jobs) {
118
+ jobsObj[job.id] = this.renderJob(job, ast);
119
+ }
120
+ doc.jobs = jobsObj;
121
+ return yamlStringify(doc, {
122
+ lineWidth: 120,
123
+ defaultStringType: 'PLAIN',
124
+ defaultKeyType: 'PLAIN',
125
+ });
126
+ }
127
+ renderTriggers(triggers) {
128
+ if (triggers.length === 0) {
129
+ // Default: manual dispatch
130
+ return { workflow_dispatch: {} };
131
+ }
132
+ const on = {};
133
+ for (const trigger of triggers) {
134
+ switch (trigger.type) {
135
+ case 'push': {
136
+ const pushConfig = {};
137
+ if (trigger.branches)
138
+ pushConfig.branches = trigger.branches;
139
+ if (trigger.paths)
140
+ pushConfig.paths = trigger.paths;
141
+ if (trigger.pathsIgnore)
142
+ pushConfig['paths-ignore'] = trigger.pathsIgnore;
143
+ on.push = Object.keys(pushConfig).length > 0 ? pushConfig : null;
144
+ break;
145
+ }
146
+ case 'pull_request': {
147
+ const prConfig = {};
148
+ if (trigger.branches)
149
+ prConfig.branches = trigger.branches;
150
+ if (trigger.types)
151
+ prConfig.types = trigger.types;
152
+ if (trigger.paths)
153
+ prConfig.paths = trigger.paths;
154
+ if (trigger.pathsIgnore)
155
+ prConfig['paths-ignore'] = trigger.pathsIgnore;
156
+ on.pull_request = Object.keys(prConfig).length > 0 ? prConfig : null;
157
+ break;
158
+ }
159
+ case 'schedule':
160
+ on.schedule = on.schedule || [];
161
+ if (trigger.cron) {
162
+ on.schedule.push({ cron: trigger.cron });
163
+ }
164
+ break;
165
+ case 'dispatch':
166
+ on.workflow_dispatch = trigger.inputs
167
+ ? { inputs: trigger.inputs }
168
+ : {};
169
+ break;
170
+ case 'tag': {
171
+ // Tags are a filter on push
172
+ if (!on.push)
173
+ on.push = {};
174
+ on.push.tags = trigger.pattern
175
+ ? [trigger.pattern]
176
+ : ['*'];
177
+ break;
178
+ }
179
+ }
180
+ }
181
+ return on;
182
+ }
183
+ renderJob(job, ast) {
184
+ const jobObj = {};
185
+ // runs-on
186
+ jobObj['runs-on'] = job.runner || 'ubuntu-latest';
187
+ // needs
188
+ if (job.needs.length > 0) {
189
+ jobObj.needs = job.needs;
190
+ }
191
+ // environment
192
+ if (job.environment) {
193
+ const envConfig = ast.options?.cicd?.environments?.find((e) => e.name === job.environment);
194
+ if (envConfig?.url) {
195
+ jobObj.environment = { name: job.environment, url: envConfig.url };
196
+ }
197
+ else {
198
+ jobObj.environment = job.environment;
199
+ }
200
+ }
201
+ // matrix strategy
202
+ if (job.matrix) {
203
+ const strategy = {};
204
+ if (job.matrix.dimensions) {
205
+ strategy.matrix = { ...job.matrix.dimensions };
206
+ }
207
+ if (job.matrix.include) {
208
+ strategy.matrix = strategy.matrix || {};
209
+ strategy.matrix.include = job.matrix.include;
210
+ }
211
+ if (job.matrix.exclude) {
212
+ strategy.matrix = strategy.matrix || {};
213
+ strategy.matrix.exclude = job.matrix.exclude;
214
+ }
215
+ jobObj.strategy = strategy;
216
+ }
217
+ // services
218
+ if (job.services && job.services.length > 0) {
219
+ const services = {};
220
+ for (const svc of job.services) {
221
+ const svcObj = { image: svc.image };
222
+ if (svc.ports)
223
+ svcObj.ports = svc.ports;
224
+ if (svc.env)
225
+ svcObj.env = svc.env;
226
+ services[svc.name] = svcObj;
227
+ }
228
+ jobObj.services = services;
229
+ }
230
+ // steps
231
+ const steps = [];
232
+ // Download artifacts first
233
+ if (job.downloadArtifacts && job.downloadArtifacts.length > 0) {
234
+ for (const artifactName of job.downloadArtifacts) {
235
+ const artifact = ast.options?.cicd?.artifacts?.find((a) => a.name === artifactName);
236
+ steps.push({
237
+ uses: 'actions/download-artifact@v4',
238
+ with: {
239
+ name: artifactName,
240
+ ...(artifact?.path && { path: artifact.path }),
241
+ },
242
+ });
243
+ }
244
+ }
245
+ // Cache step
246
+ if (job.cache) {
247
+ steps.push(this.renderCacheStep(job.cache));
248
+ }
249
+ // Node steps
250
+ for (const step of job.steps) {
251
+ steps.push(this.renderStep(step));
252
+ }
253
+ // Upload artifacts last
254
+ if (job.uploadArtifacts && job.uploadArtifacts.length > 0) {
255
+ for (const artifact of job.uploadArtifacts) {
256
+ const uploadStep = {
257
+ uses: 'actions/upload-artifact@v4',
258
+ with: {
259
+ name: artifact.name,
260
+ path: artifact.path,
261
+ },
262
+ };
263
+ if (artifact.retention) {
264
+ uploadStep.with['retention-days'] = artifact.retention;
265
+ }
266
+ steps.push(uploadStep);
267
+ }
268
+ }
269
+ jobObj.steps = steps;
270
+ return jobObj;
271
+ }
272
+ renderStep(step) {
273
+ const mapping = this.resolveActionMapping(step, 'github-actions');
274
+ if (mapping?.githubAction) {
275
+ // Use a pre-built action
276
+ const stepObj = {
277
+ name: mapping.label || step.name,
278
+ uses: mapping.githubAction,
279
+ };
280
+ if (mapping.githubWith) {
281
+ stepObj.with = { ...mapping.githubWith };
282
+ }
283
+ if (step.env && Object.keys(step.env).length > 0) {
284
+ stepObj.env = step.env;
285
+ }
286
+ return stepObj;
287
+ }
288
+ if (mapping?.gitlabScript) {
289
+ // Known node type but no GitHub action — use run:
290
+ const stepObj = {
291
+ name: mapping.label || step.name,
292
+ run: mapping.gitlabScript.join('\n'),
293
+ };
294
+ if (step.env && Object.keys(step.env).length > 0) {
295
+ stepObj.env = step.env;
296
+ }
297
+ return stepObj;
298
+ }
299
+ // Unknown node type — generate TODO placeholder
300
+ const stepObj = {
301
+ name: step.name,
302
+ run: `echo "TODO: Implement step '${step.id}' (node type: ${step.nodeType})"`,
303
+ };
304
+ if (step.env && Object.keys(step.env).length > 0) {
305
+ stepObj.env = step.env;
306
+ }
307
+ return stepObj;
308
+ }
309
+ renderCacheStep(cache) {
310
+ const cacheConfig = {};
311
+ switch (cache.strategy) {
312
+ case 'npm':
313
+ cacheConfig.path = cache.path || '~/.npm';
314
+ cacheConfig.key = cache.key
315
+ ? `npm-\${{ hashFiles('${cache.key}') }}`
316
+ : "npm-${{ hashFiles('**/package-lock.json') }}";
317
+ break;
318
+ case 'pip':
319
+ cacheConfig.path = cache.path || '~/.cache/pip';
320
+ cacheConfig.key = cache.key
321
+ ? `pip-\${{ hashFiles('${cache.key}') }}`
322
+ : "pip-${{ hashFiles('**/requirements.txt') }}";
323
+ break;
324
+ default:
325
+ cacheConfig.path = cache.path || '.cache';
326
+ cacheConfig.key = cache.key
327
+ ? `${cache.strategy}-\${{ hashFiles('${cache.key}') }}`
328
+ : `${cache.strategy}-\${{ github.sha }}`;
329
+ }
330
+ return {
331
+ name: `Cache ${cache.strategy}`,
332
+ uses: 'actions/cache@v4',
333
+ with: cacheConfig,
334
+ };
335
+ }
336
+ /**
337
+ * Apply workflow-level options (cache, services, matrix) to jobs.
338
+ */
339
+ applyWorkflowOptions(jobs, ast) {
340
+ const cicd = ast.options?.cicd;
341
+ if (!cicd)
342
+ return;
343
+ // Apply cache to all jobs (or first job if only one)
344
+ if (cicd.caches && cicd.caches.length > 0) {
345
+ const targetJobs = jobs.length === 1 ? jobs : jobs.filter((j) => j.needs.length === 0);
346
+ for (const job of targetJobs) {
347
+ job.cache = cicd.caches[0]; // Primary cache
348
+ }
349
+ }
350
+ // Apply services to all jobs
351
+ if (cicd.services && cicd.services.length > 0) {
352
+ for (const job of jobs) {
353
+ job.services = cicd.services;
354
+ }
355
+ }
356
+ // Apply matrix to first job (or specific jobs based on convention)
357
+ if (cicd.matrix) {
358
+ // Apply to all jobs that don't have dependencies (root jobs)
359
+ const rootJobs = jobs.filter((j) => j.needs.length === 0);
360
+ for (const job of rootJobs) {
361
+ job.matrix = cicd.matrix;
362
+ }
363
+ }
364
+ }
365
+ }
366
+ //# sourceMappingURL=github-actions.js.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * GitLab CI Export Target
3
+ *
4
+ * Generates .gitlab-ci.yml from a Flow Weaver CI/CD workflow.
5
+ * No FW runtime dependency — outputs native GitLab CI YAML.
6
+ *
7
+ * Key differences from GitHub Actions:
8
+ * - `stage:` instead of job `needs:` (generates `stages:` list)
9
+ * - `$CI_VARIABLE` instead of `${{ secrets.NAME }}`
10
+ * - `cache:` and `artifacts:` as native YAML keywords (no separate actions)
11
+ * - `services:` as native keyword
12
+ * - `rules:` for conditional execution
13
+ * - `environment:` as native keyword with `url:` and `when: manual` for approval
14
+ */
15
+ import { BaseCICDTarget } from './cicd-base.js';
16
+ import type { ExportOptions, ExportArtifacts, DeployInstructions } from './base.js';
17
+ export declare class GitLabCITarget extends BaseCICDTarget {
18
+ readonly name = "gitlab-ci";
19
+ readonly description = "GitLab CI/CD pipeline (.gitlab-ci.yml)";
20
+ readonly deploySchema: {
21
+ runner: {
22
+ type: "string";
23
+ description: string;
24
+ default: string;
25
+ };
26
+ };
27
+ readonly nodeTypeDeploySchema: {
28
+ script: {
29
+ type: "string[]";
30
+ description: string;
31
+ };
32
+ image: {
33
+ type: "string";
34
+ description: string;
35
+ };
36
+ label: {
37
+ type: "string";
38
+ description: string;
39
+ };
40
+ };
41
+ generate(options: ExportOptions): Promise<ExportArtifacts>;
42
+ getDeployInstructions(_artifacts: ExportArtifacts): DeployInstructions;
43
+ private renderPipelineYAML;
44
+ /**
45
+ * Derive stages from job dependency order.
46
+ * Jobs with no deps → stage 1, jobs depending on stage 1 → stage 2, etc.
47
+ */
48
+ private deriveStages;
49
+ /**
50
+ * Derive default image from @deploy annotations or built-in mappings.
51
+ */
52
+ private deriveDefaultImage;
53
+ /**
54
+ * Convert CI/CD triggers to GitLab CI workflow rules.
55
+ */
56
+ private renderWorkflowRules;
57
+ private renderJob;
58
+ private renderStepScript;
59
+ private renderCache;
60
+ /**
61
+ * Apply workflow-level options to jobs.
62
+ */
63
+ private applyWorkflowOptions;
64
+ }
65
+ //# sourceMappingURL=gitlab-ci.d.ts.map