@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
@@ -26,6 +26,8 @@ export type TMarketplaceManifest = {
26
26
  workflows: TManifestWorkflow[];
27
27
  /** Patterns included in this package */
28
28
  patterns: TManifestPattern[];
29
+ /** Export targets provided by this package */
30
+ exportTargets?: TManifestExportTarget[];
29
31
  /** External dependency information */
30
32
  dependencies?: {
31
33
  /** Flow Weaver peer dependency constraints */
@@ -34,6 +36,17 @@ export type TMarketplaceManifest = {
34
36
  npm?: Record<string, string>;
35
37
  };
36
38
  };
39
+ /** An export target provided by a marketplace package. */
40
+ export type TManifestExportTarget = {
41
+ /** Target identifier (e.g. "lambda", "azure-pipelines") */
42
+ name: string;
43
+ /** Human-readable description */
44
+ description?: string;
45
+ /** Relative path to the compiled JS file that exports the target class */
46
+ file: string;
47
+ /** Named export from the file (default: "default") */
48
+ exportName?: string;
49
+ };
37
50
  export type TManifestPort = {
38
51
  dataType: TDataType;
39
52
  description?: string;
@@ -47,10 +47,29 @@ function validatePackageJson(pkg, directory) {
47
47
  // ── Manifest-level rules ─────────────────────────────────────────────────────
48
48
  function validateManifestContents(manifest) {
49
49
  const issues = [];
50
- const totalUnits = manifest.nodeTypes.length + manifest.workflows.length + manifest.patterns.length;
50
+ const totalUnits = manifest.nodeTypes.length + manifest.workflows.length + manifest.patterns.length +
51
+ (manifest.exportTargets?.length ?? 0);
51
52
  // PKG-006: Must contain at least one unit
52
53
  if (totalUnits === 0) {
53
- issues.push(issue('PKG-006', 'error', 'Package must contain at least one node type, workflow, or pattern'));
54
+ issues.push(issue('PKG-006', 'error', 'Package must contain at least one node type, workflow, pattern, or export target'));
55
+ }
56
+ // TGT-001: Export target entries must have name and file
57
+ for (const et of manifest.exportTargets ?? []) {
58
+ if (!et.name) {
59
+ issues.push(issue('TGT-001', 'error', 'Export target entry must have a "name"'));
60
+ }
61
+ if (!et.file) {
62
+ issues.push(issue('TGT-001', 'error', `Export target "${et.name || '(unnamed)'}" must have a "file"`));
63
+ }
64
+ }
65
+ // TGT-002: Export target names must be unique within the package
66
+ const etNames = new Set();
67
+ for (const et of manifest.exportTargets ?? []) {
68
+ if (et.name && etNames.has(et.name)) {
69
+ issues.push(issue('TGT-002', 'error', `Duplicate export target name: "${et.name}"`));
70
+ }
71
+ if (et.name)
72
+ etNames.add(et.name);
54
73
  }
55
74
  // UNIT-002: Node type names must be unique within the package
56
75
  const ntNames = new Set();
@@ -14,8 +14,8 @@ export function registerExportTools(mcp) {
14
14
  mcp.tool('fw_export', 'Export workflows as serverless deployments. Generates handler code, platform config, and deploy instructions.', {
15
15
  filePath: z.string().describe('Path to the workflow .ts file'),
16
16
  target: z
17
- .enum(['lambda', 'vercel', 'cloudflare', 'inngest'])
18
- .describe('Deployment target platform'),
17
+ .string()
18
+ .describe('Deployment target platform (e.g. lambda, vercel, cloudflare, inngest, github-actions, gitlab-ci). Run with --list-targets to see installed targets.'),
19
19
  outputDir: z.string().describe('Output directory for generated files'),
20
20
  serviceName: z
21
21
  .string()
@@ -54,7 +54,16 @@ export function registerExportTools(mcp) {
54
54
  catch {
55
55
  return makeErrorResult('FILE_NOT_FOUND', `File not found: ${filePath}`);
56
56
  }
57
- // 2. Parse the file to discover workflows and node types
57
+ // 2. Discover targets from installed packs
58
+ const registry = await createTargetRegistry(process.cwd());
59
+ const exportTarget = registry.get(args.target);
60
+ if (!exportTarget) {
61
+ const available = registry.getNames();
62
+ return makeErrorResult('INVALID_TARGET', available.length === 0
63
+ ? `No export targets installed. Install a target pack (e.g. npm install flowweaver-pack-${args.target})`
64
+ : `Unknown target: "${args.target}". Installed: ${available.join(', ')}`);
65
+ }
66
+ // 3. Parse the file to discover workflows and node types
58
67
  let parseResult;
59
68
  try {
60
69
  parseResult = await parseWorkflow(filePath, { nodeTypesOnly: false });
@@ -77,21 +86,50 @@ export function registerExportTools(mcp) {
77
86
  return makeErrorResult('PARSE_ERROR', `Parse errors in ${filePath}: ${parseResult.errors.join('; ')}`);
78
87
  }
79
88
  }
80
- // 3. Get the export target
81
- const registry = createTargetRegistry();
82
- const exportTarget = registry.get(args.target);
83
- if (!exportTarget) {
84
- return makeErrorResult('INVALID_TARGET', `Unknown target: ${args.target}. Supported: lambda, vercel, cloudflare, inngest`);
85
- }
89
+ // CI/CD targets use generate() directly — they read the AST, not compiled code
86
90
  if (!exportTarget.generateBundle) {
87
- return makeErrorResult('INVALID_TARGET', `Target ${args.target} does not support bundle export`);
91
+ const serviceName = args.serviceName || path.basename(filePath, '.ts').replace(/[^a-zA-Z0-9-]/g, '-');
92
+ const artifacts = await exportTarget.generate({
93
+ sourceFile: filePath,
94
+ workflowName: args.workflows?.[0] || serviceName,
95
+ displayName: serviceName,
96
+ outputDir,
97
+ production: true,
98
+ });
99
+ // Write files if not preview
100
+ if (!preview) {
101
+ await fs.promises.mkdir(outputDir, { recursive: true });
102
+ for (const file of artifacts.files) {
103
+ const fullPath = path.join(outputDir, file.relativePath);
104
+ await fs.promises.mkdir(path.dirname(fullPath), { recursive: true });
105
+ await fs.promises.writeFile(fullPath, file.content, 'utf-8');
106
+ }
107
+ }
108
+ const instructions = exportTarget.getDeployInstructions(artifacts);
109
+ return makeToolResult({
110
+ preview,
111
+ target: args.target,
112
+ serviceName,
113
+ outputDir,
114
+ files: artifacts.files.map((f) => ({
115
+ path: f.relativePath,
116
+ type: f.type,
117
+ size: f.content.length,
118
+ })),
119
+ instructions: {
120
+ title: instructions.title,
121
+ steps: instructions.steps,
122
+ prerequisites: instructions.prerequisites,
123
+ },
124
+ summary: preview
125
+ ? `Preview: ${artifacts.files.length} files would be generated in ${outputDir}`
126
+ : `Exported ${artifacts.files.length} files to ${outputDir}`,
127
+ });
88
128
  }
89
129
  // 4. Build workflow and node type lists
90
- // parseResult.allWorkflows has all TWorkflowAST[] from the file;
91
- // each workflow's nodeTypes are in workflow.nodeTypes (TNodeTypeAST[])
92
130
  const allWorkflows = parseResult.allWorkflows || [];
93
131
  const allNodeTypes = allWorkflows.flatMap((w) => w.nodeTypes || []);
94
- // Deduplicate node types by name (same type may appear in multiple workflows)
132
+ // Deduplicate node types by name
95
133
  const uniqueNodeTypes = [
96
134
  ...new Map(allNodeTypes.map((nt) => [nt.name, nt])).values(),
97
135
  ];
@@ -133,7 +171,11 @@ export function registerExportTools(mcp) {
133
171
  outputDir,
134
172
  production: true,
135
173
  includeDocs,
136
- targetOptions: args.durableSteps ? { durableSteps: true } : undefined,
174
+ targetOptions: {
175
+ ...(args.durableSteps && { durableSteps: true }),
176
+ // Pass @deploy config from workflow AST so targets can read their annotations
177
+ ...(allWorkflows[0]?.options?.deploy && { deploy: allWorkflows[0].options.deploy }),
178
+ },
137
179
  });
138
180
  // 6. Write files if not preview
139
181
  if (!preview) {
package/dist/parser.js CHANGED
@@ -612,6 +612,7 @@ export class AnnotationParser {
612
612
  label: portDef.label,
613
613
  expression: portDef.expression,
614
614
  ...(portDef.scope && { scope: portDef.scope }),
615
+ ...(portDef.hidden && { hidden: portDef.hidden }),
615
616
  ...(portDef.metadata && { metadata: portDef.metadata }),
616
617
  ...(portDef.tsType && { tsType: portDef.tsType }),
617
618
  };
@@ -624,6 +625,7 @@ export class AnnotationParser {
624
625
  dataType: portDef.type,
625
626
  label: portDef.label,
626
627
  ...(portDef.scope && { scope: portDef.scope }),
628
+ ...(portDef.hidden && { hidden: portDef.hidden }),
627
629
  ...(portDef.metadata && { metadata: portDef.metadata }),
628
630
  ...(portDef.tsType && { tsType: portDef.tsType }),
629
631
  };
@@ -745,6 +747,7 @@ export class AnnotationParser {
745
747
  tags: config.tags,
746
748
  }
747
749
  : undefined,
750
+ ...(config.deploy && { deploy: config.deploy }),
748
751
  sourceLocation: {
749
752
  file: sourceFile.getFilePath(),
750
753
  line: fn.getStartLineNumber(false),
@@ -896,6 +899,8 @@ export class AnnotationParser {
896
899
  ...(inst.sourceLocation && {
897
900
  sourceLocation: { file: filePath, ...inst.sourceLocation },
898
901
  }),
902
+ ...(inst.job && { job: inst.job }),
903
+ ...(inst.environment && { environment: inst.environment }),
899
904
  };
900
905
  });
901
906
  // Convert connections to ConnectionAST
@@ -985,7 +990,11 @@ export class AnnotationParser {
985
990
  ...(Object.keys(ui).length > 0 && { ui }),
986
991
  ...((config.strictTypes !== undefined || config.autoConnect ||
987
992
  config.trigger || config.cancelOn || config.retries !== undefined ||
988
- config.timeout || config.throttle) && {
993
+ config.timeout || config.throttle ||
994
+ config.secrets || config.runner || config.caches || config.artifacts ||
995
+ config.environments || config.matrix || config.services ||
996
+ config.concurrency || config.cicdTriggers ||
997
+ config.deploy) && {
989
998
  options: {
990
999
  ...(config.strictTypes !== undefined && { strictTypes: config.strictTypes }),
991
1000
  ...(config.autoConnect && { autoConnect: true }),
@@ -994,6 +1003,24 @@ export class AnnotationParser {
994
1003
  ...(config.retries !== undefined && { retries: config.retries }),
995
1004
  ...(config.timeout && { timeout: config.timeout }),
996
1005
  ...(config.throttle && { throttle: config.throttle }),
1006
+ // CI/CD domain options (grouped)
1007
+ ...((config.secrets || config.runner || config.caches || config.artifacts ||
1008
+ config.environments || config.matrix || config.services ||
1009
+ config.concurrency || config.cicdTriggers) && {
1010
+ cicd: {
1011
+ ...(config.secrets && { secrets: config.secrets }),
1012
+ ...(config.runner && { runner: config.runner }),
1013
+ ...(config.caches && { caches: config.caches }),
1014
+ ...(config.artifacts && { artifacts: config.artifacts }),
1015
+ ...(config.environments && { environments: config.environments }),
1016
+ ...(config.matrix && { matrix: config.matrix }),
1017
+ ...(config.services && { services: config.services }),
1018
+ ...(config.concurrency && { concurrency: config.concurrency }),
1019
+ ...(config.cicdTriggers && { triggers: config.cicdTriggers }),
1020
+ },
1021
+ }),
1022
+ // Per-target deployment config
1023
+ ...(config.deploy && { deploy: config.deploy }),
997
1024
  },
998
1025
  }),
999
1026
  });
@@ -0,0 +1,33 @@
1
+ /**
2
+ * CI/CD Workflow Detection
3
+ *
4
+ * Determines whether a workflow is a CI/CD pipeline based on annotations.
5
+ * A workflow is CI/CD if it has any CI/CD-specific annotations:
6
+ * @secret, @runner, @cache, @artifact, @environment, @matrix, @service,
7
+ * @concurrency, [job: "..."], or CI/CD trigger types (push, pull_request, etc.)
8
+ */
9
+ import type { TWorkflowAST } from '../ast/types.js';
10
+ /**
11
+ * Check if a workflow is a CI/CD pipeline.
12
+ *
13
+ * Detection signals (any one is sufficient):
14
+ * 1. Workflow options contain CI/CD fields (secrets, runner, caches, etc.)
15
+ * 2. Any node instance has a `job` attribute
16
+ * 3. Workflow has cicdTriggers
17
+ */
18
+ export declare function isCICDWorkflow(ast: TWorkflowAST): boolean;
19
+ /**
20
+ * Get all unique job names from a CI/CD workflow.
21
+ * Returns empty array if no jobs are defined.
22
+ */
23
+ export declare function getJobNames(ast: TWorkflowAST): string[];
24
+ /**
25
+ * Get all declared secret names from a CI/CD workflow.
26
+ */
27
+ export declare function getDeclaredSecrets(ast: TWorkflowAST): string[];
28
+ /**
29
+ * Get all secret:NAME references from connections.
30
+ * Returns array of secret names that are wired via @connect.
31
+ */
32
+ export declare function getReferencedSecrets(ast: TWorkflowAST): string[];
33
+ //# sourceMappingURL=cicd-detection.d.ts.map
@@ -0,0 +1,76 @@
1
+ /**
2
+ * CI/CD Workflow Detection
3
+ *
4
+ * Determines whether a workflow is a CI/CD pipeline based on annotations.
5
+ * A workflow is CI/CD if it has any CI/CD-specific annotations:
6
+ * @secret, @runner, @cache, @artifact, @environment, @matrix, @service,
7
+ * @concurrency, [job: "..."], or CI/CD trigger types (push, pull_request, etc.)
8
+ */
9
+ /**
10
+ * Check if a workflow is a CI/CD pipeline.
11
+ *
12
+ * Detection signals (any one is sufficient):
13
+ * 1. Workflow options contain CI/CD fields (secrets, runner, caches, etc.)
14
+ * 2. Any node instance has a `job` attribute
15
+ * 3. Workflow has cicdTriggers
16
+ */
17
+ export function isCICDWorkflow(ast) {
18
+ // Check CI/CD domain annotations
19
+ const cicd = ast.options?.cicd;
20
+ if (cicd) {
21
+ if (cicd.secrets && cicd.secrets.length > 0)
22
+ return true;
23
+ if (cicd.runner)
24
+ return true;
25
+ if (cicd.caches && cicd.caches.length > 0)
26
+ return true;
27
+ if (cicd.artifacts && cicd.artifacts.length > 0)
28
+ return true;
29
+ if (cicd.environments && cicd.environments.length > 0)
30
+ return true;
31
+ if (cicd.matrix)
32
+ return true;
33
+ if (cicd.services && cicd.services.length > 0)
34
+ return true;
35
+ if (cicd.concurrency)
36
+ return true;
37
+ if (cicd.triggers && cicd.triggers.length > 0)
38
+ return true;
39
+ }
40
+ // Check node-level CI/CD annotations
41
+ if (ast.instances.some((inst) => inst.job))
42
+ return true;
43
+ return false;
44
+ }
45
+ /**
46
+ * Get all unique job names from a CI/CD workflow.
47
+ * Returns empty array if no jobs are defined.
48
+ */
49
+ export function getJobNames(ast) {
50
+ const jobs = new Set();
51
+ for (const inst of ast.instances) {
52
+ if (inst.job)
53
+ jobs.add(inst.job);
54
+ }
55
+ return Array.from(jobs);
56
+ }
57
+ /**
58
+ * Get all declared secret names from a CI/CD workflow.
59
+ */
60
+ export function getDeclaredSecrets(ast) {
61
+ return (ast.options?.cicd?.secrets || []).map((s) => s.name);
62
+ }
63
+ /**
64
+ * Get all secret:NAME references from connections.
65
+ * Returns array of secret names that are wired via @connect.
66
+ */
67
+ export function getReferencedSecrets(ast) {
68
+ const secrets = new Set();
69
+ for (const conn of ast.connections) {
70
+ if (conn.from.node.startsWith('secret:')) {
71
+ secrets.add(conn.from.node.substring(7)); // strip "secret:" prefix
72
+ }
73
+ }
74
+ return Array.from(secrets);
75
+ }
76
+ //# sourceMappingURL=cicd-detection.js.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * CI/CD-Specific Validation Rules
3
+ *
4
+ * Custom TValidationRule implementations for CI/CD pipeline workflows.
5
+ * These run AFTER the built-in validator via the api/validate.ts custom rules injection.
6
+ *
7
+ * Rules:
8
+ * 1. CICD_SECRET_NOT_DECLARED - secret:X referenced but no @secret X declared
9
+ * 2. CICD_SECRET_UNUSED - @secret X declared but never wired
10
+ * 3. CICD_TRIGGER_MISSING - No trigger annotations — pipeline would never run
11
+ * 4. CICD_JOB_MISSING_RUNNER - Job has no runner (uses workflow default or none)
12
+ * 5. CICD_ARTIFACT_CROSS_JOB - Data flows between jobs without @artifact declaration
13
+ * 6. CICD_CIRCULAR_JOB_DEPS - Job dependency cycle detected
14
+ * 7. CICD_MATRIX_WITH_ENVIRONMENT - Matrix + environment = N approval prompts
15
+ */
16
+ import type { TValidationRule } from '../ast/types.js';
17
+ /**
18
+ * A `secret:X` pseudo-node is referenced in @connect but no `@secret X` is declared.
19
+ * This means the export target won't know about the secret and can't generate
20
+ * proper environment variable references.
21
+ */
22
+ export declare const secretNotDeclaredRule: TValidationRule;
23
+ /**
24
+ * A `@secret X` is declared but never wired via `@connect secret:X -> ...`.
25
+ * The secret might be intentional (used in a shell-command step) or a leftover.
26
+ */
27
+ export declare const secretUnusedRule: TValidationRule;
28
+ /**
29
+ * A CI/CD workflow with no trigger annotations would never run automatically.
30
+ * Needs at least one @trigger (push, pull_request, schedule, dispatch, or tag).
31
+ */
32
+ export declare const triggerMissingRule: TValidationRule;
33
+ /**
34
+ * A job (group of nodes with same [job: "name"]) has no explicit runner
35
+ * and the workflow has no default @runner. The export target will use a
36
+ * platform default, which may not be what the user expects.
37
+ */
38
+ export declare const jobMissingRunnerRule: TValidationRule;
39
+ /**
40
+ * Data flows between nodes in different jobs via connections, but no @artifact
41
+ * is declared. In CI/CD, each job runs in a fresh environment — data must be
42
+ * explicitly passed via artifacts.
43
+ */
44
+ export declare const artifactCrossJobRule: TValidationRule;
45
+ /**
46
+ * Job dependencies (derived from @path connections between jobs) form a cycle.
47
+ * CI/CD platforms reject circular job dependencies.
48
+ */
49
+ export declare const circularJobDepsRule: TValidationRule;
50
+ /**
51
+ * Using @matrix with @environment protection means each matrix combination
52
+ * triggers an approval prompt. For a 3x2 matrix, that's 6 prompts.
53
+ */
54
+ export declare const matrixWithEnvironmentRule: TValidationRule;
55
+ /** All CI/CD validation rules */
56
+ export declare const cicdValidationRules: TValidationRule[];
57
+ /**
58
+ * Get all CI/CD validation rules.
59
+ * Convenience function for passing to validateWorkflow().
60
+ */
61
+ export declare function getCICDValidationRules(): TValidationRule[];
62
+ //# sourceMappingURL=cicd-rules.d.ts.map