@synergenius/flow-weaver 0.13.3 → 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 (56) hide show
  1. package/dist/api/validate.js +8 -2
  2. package/dist/ast/types.d.ts +120 -0
  3. package/dist/chevrotain-parser/node-parser.d.ts +4 -0
  4. package/dist/chevrotain-parser/node-parser.js +41 -1
  5. package/dist/chevrotain-parser/port-parser.d.ts +1 -0
  6. package/dist/chevrotain-parser/port-parser.js +22 -2
  7. package/dist/chevrotain-parser/tokens.d.ts +3 -0
  8. package/dist/chevrotain-parser/tokens.js +15 -0
  9. package/dist/cli/commands/export.js +25 -38
  10. package/dist/cli/flow-weaver.mjs +63703 -54297
  11. package/dist/cli/templates/index.js +9 -0
  12. package/dist/cli/templates/workflows/cicd-docker.d.ts +9 -0
  13. package/dist/cli/templates/workflows/cicd-docker.js +110 -0
  14. package/dist/cli/templates/workflows/cicd-matrix.d.ts +9 -0
  15. package/dist/cli/templates/workflows/cicd-matrix.js +112 -0
  16. package/dist/cli/templates/workflows/cicd-multi-env.d.ts +9 -0
  17. package/dist/cli/templates/workflows/cicd-multi-env.js +118 -0
  18. package/dist/cli/templates/workflows/cicd-test-deploy.d.ts +11 -0
  19. package/dist/cli/templates/workflows/cicd-test-deploy.js +149 -0
  20. package/dist/constants.js +7 -0
  21. package/dist/deployment/index.d.ts +14 -7
  22. package/dist/deployment/index.js +29 -17
  23. package/dist/deployment/targets/base.d.ts +27 -2
  24. package/dist/deployment/targets/base.js +38 -6
  25. package/dist/deployment/targets/cicd-base.d.ts +111 -0
  26. package/dist/deployment/targets/cicd-base.js +357 -0
  27. package/dist/deployment/targets/cloudflare.d.ts +6 -0
  28. package/dist/deployment/targets/cloudflare.js +3 -0
  29. package/dist/deployment/targets/github-actions.d.ts +54 -0
  30. package/dist/deployment/targets/github-actions.js +366 -0
  31. package/dist/deployment/targets/gitlab-ci.d.ts +65 -0
  32. package/dist/deployment/targets/gitlab-ci.js +374 -0
  33. package/dist/deployment/targets/inngest.d.ts +25 -0
  34. package/dist/deployment/targets/inngest.js +10 -1
  35. package/dist/deployment/targets/lambda.d.ts +17 -0
  36. package/dist/deployment/targets/lambda.js +5 -0
  37. package/dist/deployment/targets/vercel.d.ts +16 -0
  38. package/dist/deployment/targets/vercel.js +5 -0
  39. package/dist/diagram/geometry.js +13 -5
  40. package/dist/export/index.d.ts +13 -9
  41. package/dist/export/index.js +129 -997
  42. package/dist/generated-version.d.ts +1 -1
  43. package/dist/generated-version.js +1 -1
  44. package/dist/jsdoc-parser.d.ts +130 -0
  45. package/dist/jsdoc-parser.js +408 -4
  46. package/dist/marketplace/index.d.ts +1 -1
  47. package/dist/marketplace/types.d.ts +13 -0
  48. package/dist/marketplace/validator.js +21 -2
  49. package/dist/mcp/tools-export.js +56 -14
  50. package/dist/parser.js +28 -1
  51. package/dist/validation/cicd-detection.d.ts +33 -0
  52. package/dist/validation/cicd-detection.js +76 -0
  53. package/dist/validation/cicd-rules.d.ts +62 -0
  54. package/dist/validation/cicd-rules.js +284 -0
  55. package/docs/reference/scaffold.md +4 -0
  56. package/package.json +4 -3
@@ -169,6 +169,20 @@ export interface BundleArtifacts extends ExportArtifacts {
169
169
  /** OpenAPI spec if generated */
170
170
  openApiSpec?: object;
171
171
  }
172
+ /**
173
+ * Deploy schema field definition.
174
+ * Describes a single key that a target accepts via @deploy annotations.
175
+ */
176
+ export interface DeploySchemaField {
177
+ type: 'string' | 'number' | 'boolean' | 'string[]';
178
+ description: string;
179
+ default?: unknown;
180
+ }
181
+ /**
182
+ * Deploy schema — describes all @deploy keys a target accepts.
183
+ * Used for validation, Studio autocomplete, and documentation.
184
+ */
185
+ export type DeploySchema = Record<string, DeploySchemaField>;
172
186
  /**
173
187
  * Export target interface
174
188
  */
@@ -177,6 +191,10 @@ export interface ExportTarget {
177
191
  readonly name: string;
178
192
  /** Human-readable description */
179
193
  readonly description: string;
194
+ /** Schema for @deploy keys this target accepts on workflows */
195
+ readonly deploySchema?: DeploySchema;
196
+ /** Schema for @deploy keys this target accepts on node types */
197
+ readonly nodeTypeDeploySchema?: DeploySchema;
180
198
  /**
181
199
  * Generate deployment artifacts for single workflow
182
200
  */
@@ -307,10 +325,17 @@ export declare abstract class BaseExportTarget implements ExportTarget {
307
325
  * Registry of available export targets
308
326
  */
309
327
  export declare class ExportTargetRegistry {
310
- private targets;
311
- register(target: ExportTarget): void;
328
+ private factories;
329
+ private instances;
330
+ /**
331
+ * Register a target factory. The target is only instantiated on first use.
332
+ * Also accepts a pre-instantiated target for backwards compatibility.
333
+ */
334
+ register(nameOrTarget: string | ExportTarget, factory?: () => ExportTarget): void;
312
335
  get(name: string): ExportTarget | undefined;
313
336
  getAll(): ExportTarget[];
314
337
  getNames(): string[];
338
+ /** Get deploy schemas from all registered targets (for validation/autocomplete) */
339
+ getDeploySchemas(): Record<string, DeploySchema>;
315
340
  }
316
341
  //# sourceMappingURL=base.d.ts.map
@@ -806,18 +806,50 @@ export const functionRegistry = {
806
806
  * Registry of available export targets
807
807
  */
808
808
  export class ExportTargetRegistry {
809
- targets = new Map();
810
- register(target) {
811
- this.targets.set(target.name, target);
809
+ factories = new Map();
810
+ instances = new Map();
811
+ /**
812
+ * Register a target factory. The target is only instantiated on first use.
813
+ * Also accepts a pre-instantiated target for backwards compatibility.
814
+ */
815
+ register(nameOrTarget, factory) {
816
+ if (typeof nameOrTarget === 'string') {
817
+ // New lazy factory API: register(name, factory)
818
+ this.factories.set(nameOrTarget, factory);
819
+ }
820
+ else {
821
+ // Legacy API: register(target) — wrap in factory
822
+ const target = nameOrTarget;
823
+ this.instances.set(target.name, target);
824
+ this.factories.set(target.name, () => target);
825
+ }
812
826
  }
813
827
  get(name) {
814
- return this.targets.get(name);
828
+ if (!this.instances.has(name)) {
829
+ const factory = this.factories.get(name);
830
+ if (factory)
831
+ this.instances.set(name, factory());
832
+ }
833
+ return this.instances.get(name);
815
834
  }
816
835
  getAll() {
817
- return Array.from(this.targets.values());
836
+ for (const [name, factory] of this.factories) {
837
+ if (!this.instances.has(name))
838
+ this.instances.set(name, factory());
839
+ }
840
+ return Array.from(this.instances.values());
818
841
  }
819
842
  getNames() {
820
- return Array.from(this.targets.keys());
843
+ return Array.from(this.factories.keys());
844
+ }
845
+ /** Get deploy schemas from all registered targets (for validation/autocomplete) */
846
+ getDeploySchemas() {
847
+ const schemas = {};
848
+ for (const target of this.getAll()) {
849
+ if (target.deploySchema)
850
+ schemas[target.name] = target.deploySchema;
851
+ }
852
+ return schemas;
821
853
  }
822
854
  }
823
855
  //# sourceMappingURL=base.js.map
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Base CI/CD Export Target
3
+ *
4
+ * Shared logic for generating CI/CD pipeline files (GitHub Actions, GitLab CI).
5
+ * Unlike serverless targets, CI/CD targets produce native YAML that runs without
6
+ * any FW runtime dependency.
7
+ *
8
+ * Key responsibilities:
9
+ * - Build job graph from AST (group nodes by @job, compute dependencies)
10
+ * - Resolve secret wiring (secret: pseudo-node connections → job env vars)
11
+ * - Inject artifact upload/download steps between jobs
12
+ * - Generate SECRETS_SETUP.md documentation
13
+ */
14
+ import type { TWorkflowAST, TCICDSecret, TCICDCache, TCICDArtifact, TCICDService, TCICDMatrix } from '../../ast/types.js';
15
+ import { BaseExportTarget, type ExportOptions, type MultiWorkflowArtifacts, type CompiledWorkflow, type NodeTypeArtifacts, type NodeTypeInfo, type NodeTypeExportOptions, type BundleArtifacts, type BundleWorkflow, type BundleNodeType } from './base.js';
16
+ export interface CICDStep {
17
+ /** Node instance ID */
18
+ id: string;
19
+ /** Human-readable step name */
20
+ name: string;
21
+ /** Node type (used for action mapping) */
22
+ nodeType: string;
23
+ /** Environment variables for this step */
24
+ env?: Record<string, string>;
25
+ /** Per-target deploy config from @deploy annotations on the node type */
26
+ nodeTypeDeploy?: Record<string, Record<string, unknown>>;
27
+ }
28
+ export interface CICDJob {
29
+ /** Job identifier (from [job: "name"]) */
30
+ id: string;
31
+ /** Human-readable job name */
32
+ name: string;
33
+ /** Runner label (from @runner or job-level override) */
34
+ runner?: string;
35
+ /** Jobs that must complete before this one */
36
+ needs: string[];
37
+ /** Steps in execution order */
38
+ steps: CICDStep[];
39
+ /** Deployment environment (from [environment: "name"]) */
40
+ environment?: string;
41
+ /** Secret names used by this job */
42
+ secrets: string[];
43
+ /** Matrix strategy */
44
+ matrix?: TCICDMatrix;
45
+ /** Sidecar services */
46
+ services?: TCICDService[];
47
+ /** Cache configuration */
48
+ cache?: TCICDCache;
49
+ /** Artifacts to upload after this job */
50
+ uploadArtifacts?: TCICDArtifact[];
51
+ /** Artifact names to download before this job */
52
+ downloadArtifacts?: string[];
53
+ }
54
+ export interface ActionMapping {
55
+ /** GitHub Actions `uses:` value */
56
+ githubAction?: string;
57
+ /** GitHub Actions `with:` defaults */
58
+ githubWith?: Record<string, string>;
59
+ /** GitLab CI script commands */
60
+ gitlabScript?: string[];
61
+ /** GitLab CI image override */
62
+ gitlabImage?: string;
63
+ /** Human-readable step name */
64
+ label?: string;
65
+ }
66
+ /**
67
+ * Default mapping from FW node types to CI/CD platform actions.
68
+ * Unknown node types fall back to a TODO placeholder.
69
+ */
70
+ export declare const NODE_ACTION_MAP: Record<string, ActionMapping>;
71
+ export declare abstract class BaseCICDTarget extends BaseExportTarget {
72
+ /**
73
+ * CI/CD targets don't compile workflows — they read the AST and generate YAML.
74
+ * The generate() method must be implemented by each platform target.
75
+ */
76
+ generateMultiWorkflow(_workflows: CompiledWorkflow[], _options: ExportOptions): Promise<MultiWorkflowArtifacts>;
77
+ generateNodeTypeService(_nodeTypes: NodeTypeInfo[], _options: NodeTypeExportOptions): Promise<NodeTypeArtifacts>;
78
+ generateBundle(_workflows: BundleWorkflow[], _nodeTypes: BundleNodeType[], _options: ExportOptions): Promise<BundleArtifacts>;
79
+ /**
80
+ * Resolve the action mapping for a node type.
81
+ * Priority: @deploy annotations on the node type → NODE_ACTION_MAP fallback → undefined.
82
+ *
83
+ * This makes marketplace packages self-describing: a node type with
84
+ * `@deploy github-actions action="actions/checkout@v4"` contributes its own mapping.
85
+ */
86
+ protected resolveActionMapping(step: CICDStep, targetName: string): ActionMapping | undefined;
87
+ /**
88
+ * Build the job graph from the workflow AST.
89
+ * Groups nodes by their [job: "name"] attribute and computes dependencies
90
+ * from connections between nodes in different jobs.
91
+ */
92
+ protected buildJobGraph(ast: TWorkflowAST): CICDJob[];
93
+ /**
94
+ * Topologically sort jobs so that dependencies come first.
95
+ */
96
+ private topoSortJobs;
97
+ /**
98
+ * Map secret:NAME connections to the jobs that need them.
99
+ * Populates job.secrets and step.env for each secret connection.
100
+ */
101
+ protected resolveJobSecrets(jobs: CICDJob[], ast: TWorkflowAST, renderSecretRef: (name: string) => string): void;
102
+ /**
103
+ * Add artifact upload/download steps between jobs.
104
+ */
105
+ protected injectArtifactSteps(jobs: CICDJob[], artifacts: TCICDArtifact[]): void;
106
+ /**
107
+ * Generate SECRETS_SETUP.md with per-secret setup instructions.
108
+ */
109
+ protected generateSecretsDoc(secrets: TCICDSecret[], platform: string): string;
110
+ }
111
+ //# sourceMappingURL=cicd-base.d.ts.map
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Base CI/CD Export Target
3
+ *
4
+ * Shared logic for generating CI/CD pipeline files (GitHub Actions, GitLab CI).
5
+ * Unlike serverless targets, CI/CD targets produce native YAML that runs without
6
+ * any FW runtime dependency.
7
+ *
8
+ * Key responsibilities:
9
+ * - Build job graph from AST (group nodes by @job, compute dependencies)
10
+ * - Resolve secret wiring (secret: pseudo-node connections → job env vars)
11
+ * - Inject artifact upload/download steps between jobs
12
+ * - Generate SECRETS_SETUP.md documentation
13
+ */
14
+ import { BaseExportTarget, } from './base.js';
15
+ /**
16
+ * Default mapping from FW node types to CI/CD platform actions.
17
+ * Unknown node types fall back to a TODO placeholder.
18
+ */
19
+ export const NODE_ACTION_MAP = {
20
+ checkout: {
21
+ githubAction: 'actions/checkout@v4',
22
+ gitlabScript: ['echo "Checkout handled by GitLab CI runner"'],
23
+ label: 'Checkout code',
24
+ },
25
+ 'setup-node': {
26
+ githubAction: 'actions/setup-node@v4',
27
+ githubWith: { 'node-version': '20' },
28
+ gitlabImage: 'node:20',
29
+ label: 'Setup Node.js',
30
+ },
31
+ 'setup-python': {
32
+ githubAction: 'actions/setup-python@v5',
33
+ githubWith: { 'python-version': '3.12' },
34
+ gitlabImage: 'python:3.12',
35
+ label: 'Setup Python',
36
+ },
37
+ 'npm-install': {
38
+ githubAction: undefined,
39
+ gitlabScript: ['npm ci'],
40
+ label: 'Install dependencies',
41
+ },
42
+ 'npm-test': {
43
+ githubAction: undefined,
44
+ gitlabScript: ['npm test'],
45
+ label: 'Run tests',
46
+ },
47
+ 'npm-build': {
48
+ githubAction: undefined,
49
+ gitlabScript: ['npm run build'],
50
+ label: 'Build',
51
+ },
52
+ 'docker-build': {
53
+ githubAction: 'docker/build-push-action@v6',
54
+ githubWith: { push: 'false' },
55
+ gitlabScript: ['docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .'],
56
+ label: 'Build Docker image',
57
+ },
58
+ 'docker-push': {
59
+ githubAction: 'docker/build-push-action@v6',
60
+ githubWith: { push: 'true' },
61
+ gitlabScript: ['docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA'],
62
+ label: 'Push Docker image',
63
+ },
64
+ 'docker-login': {
65
+ githubAction: 'docker/login-action@v3',
66
+ gitlabScript: ['echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY'],
67
+ label: 'Docker login',
68
+ },
69
+ 'shell-command': {
70
+ githubAction: undefined,
71
+ gitlabScript: ['echo "TODO: Add shell command"'],
72
+ label: 'Run command',
73
+ },
74
+ 'deploy-ssh': {
75
+ githubAction: undefined,
76
+ gitlabScript: ['echo "TODO: Configure SSH deployment"'],
77
+ label: 'Deploy via SSH',
78
+ },
79
+ 'deploy-s3': {
80
+ githubAction: 'aws-actions/configure-aws-credentials@v4',
81
+ gitlabScript: ['aws s3 sync dist/ s3://$S3_BUCKET/'],
82
+ label: 'Deploy to S3',
83
+ },
84
+ 'slack-notify': {
85
+ githubAction: 'slackapi/slack-github-action@v1',
86
+ gitlabScript: ['curl -X POST -H "Content-type: application/json" --data "{\"text\":\"Pipeline complete\"}" $SLACK_WEBHOOK_URL'],
87
+ label: 'Send Slack notification',
88
+ },
89
+ 'health-check': {
90
+ githubAction: undefined,
91
+ gitlabScript: ['curl --retry 10 --retry-delay 5 --retry-all-errors $HEALTH_CHECK_URL'],
92
+ label: 'Health check',
93
+ },
94
+ 'wait-for-url': {
95
+ githubAction: undefined,
96
+ gitlabScript: ['for i in $(seq 1 30); do curl -sf $WAIT_URL && exit 0; sleep 10; done; exit 1'],
97
+ label: 'Wait for URL',
98
+ },
99
+ };
100
+ // ---------------------------------------------------------------------------
101
+ // Base CI/CD Target
102
+ // ---------------------------------------------------------------------------
103
+ export class BaseCICDTarget extends BaseExportTarget {
104
+ /**
105
+ * CI/CD targets don't compile workflows — they read the AST and generate YAML.
106
+ * The generate() method must be implemented by each platform target.
107
+ */
108
+ // Not used by CI/CD targets (they don't produce serverless handlers)
109
+ async generateMultiWorkflow(_workflows, _options) {
110
+ throw new Error('CI/CD targets use generate() with AST, not generateMultiWorkflow()');
111
+ }
112
+ async generateNodeTypeService(_nodeTypes, _options) {
113
+ throw new Error('CI/CD targets do not export node types as services');
114
+ }
115
+ async generateBundle(_workflows, _nodeTypes, _options) {
116
+ throw new Error('CI/CD targets use generate() with AST, not generateBundle()');
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Shared CI/CD Logic
120
+ // ---------------------------------------------------------------------------
121
+ /**
122
+ * Resolve the action mapping for a node type.
123
+ * Priority: @deploy annotations on the node type → NODE_ACTION_MAP fallback → undefined.
124
+ *
125
+ * This makes marketplace packages self-describing: a node type with
126
+ * `@deploy github-actions action="actions/checkout@v4"` contributes its own mapping.
127
+ */
128
+ resolveActionMapping(step, targetName) {
129
+ // 1. Check @deploy annotations on the node type
130
+ const deployConfig = step.nodeTypeDeploy?.[targetName];
131
+ if (deployConfig) {
132
+ return {
133
+ githubAction: deployConfig.action,
134
+ githubWith: deployConfig.with
135
+ ? (typeof deployConfig.with === 'string'
136
+ ? JSON.parse(deployConfig.with)
137
+ : deployConfig.with)
138
+ : undefined,
139
+ gitlabScript: deployConfig.script
140
+ ? (Array.isArray(deployConfig.script)
141
+ ? deployConfig.script
142
+ : [deployConfig.script])
143
+ : undefined,
144
+ gitlabImage: deployConfig.image,
145
+ label: deployConfig.label || step.name,
146
+ };
147
+ }
148
+ // 2. Fall back to built-in NODE_ACTION_MAP
149
+ return NODE_ACTION_MAP[step.nodeType];
150
+ }
151
+ /**
152
+ * Build the job graph from the workflow AST.
153
+ * Groups nodes by their [job: "name"] attribute and computes dependencies
154
+ * from connections between nodes in different jobs.
155
+ */
156
+ buildJobGraph(ast) {
157
+ // Build node type lookup for @deploy annotations
158
+ const nodeTypeLookup = new Map();
159
+ for (const nt of ast.nodeTypes) {
160
+ nodeTypeLookup.set(nt.name, nt);
161
+ if (nt.functionName !== nt.name)
162
+ nodeTypeLookup.set(nt.functionName, nt);
163
+ }
164
+ // Group instances by job
165
+ const jobMap = new Map();
166
+ const defaultRunner = ast.options?.cicd?.runner;
167
+ for (const inst of ast.instances) {
168
+ const jobName = inst.job || 'default';
169
+ if (!jobMap.has(jobName))
170
+ jobMap.set(jobName, []);
171
+ jobMap.get(jobName).push(inst);
172
+ }
173
+ // Build node -> job lookup
174
+ const nodeJob = new Map();
175
+ for (const inst of ast.instances) {
176
+ nodeJob.set(inst.id, inst.job || 'default');
177
+ }
178
+ // Build job dependency graph from connections
179
+ const jobDeps = new Map();
180
+ for (const conn of ast.connections) {
181
+ if (conn.from.node.startsWith('secret:'))
182
+ continue;
183
+ if (conn.from.node === 'Start' || conn.to.node === 'Exit')
184
+ continue;
185
+ const fromJob = nodeJob.get(conn.from.node);
186
+ const toJob = nodeJob.get(conn.to.node);
187
+ if (fromJob && toJob && fromJob !== toJob) {
188
+ if (!jobDeps.has(toJob))
189
+ jobDeps.set(toJob, new Set());
190
+ jobDeps.get(toJob).add(fromJob);
191
+ }
192
+ }
193
+ // Convert to CICDJob array
194
+ const jobs = [];
195
+ for (const [jobId, instances] of jobMap) {
196
+ // Determine environment from first instance with one
197
+ const environment = instances.find((i) => i.environment)?.environment;
198
+ const steps = instances.map((inst) => {
199
+ const nt = nodeTypeLookup.get(inst.nodeType);
200
+ return {
201
+ id: inst.id,
202
+ name: inst.config?.label || inst.id,
203
+ nodeType: inst.nodeType,
204
+ ...(nt?.deploy && { nodeTypeDeploy: nt.deploy }),
205
+ };
206
+ });
207
+ const needs = jobDeps.get(jobId)
208
+ ? Array.from(jobDeps.get(jobId))
209
+ : [];
210
+ jobs.push({
211
+ id: jobId,
212
+ name: jobId,
213
+ runner: defaultRunner,
214
+ needs,
215
+ steps,
216
+ environment,
217
+ secrets: [],
218
+ });
219
+ }
220
+ // Topologically sort jobs so dependencies come first
221
+ return this.topoSortJobs(jobs);
222
+ }
223
+ /**
224
+ * Topologically sort jobs so that dependencies come first.
225
+ */
226
+ topoSortJobs(jobs) {
227
+ const jobMap = new Map(jobs.map((j) => [j.id, j]));
228
+ const visited = new Set();
229
+ const sorted = [];
230
+ function visit(id) {
231
+ if (visited.has(id))
232
+ return;
233
+ visited.add(id);
234
+ const job = jobMap.get(id);
235
+ if (!job)
236
+ return;
237
+ for (const dep of job.needs) {
238
+ visit(dep);
239
+ }
240
+ sorted.push(job);
241
+ }
242
+ for (const job of jobs) {
243
+ visit(job.id);
244
+ }
245
+ return sorted;
246
+ }
247
+ /**
248
+ * Map secret:NAME connections to the jobs that need them.
249
+ * Populates job.secrets and step.env for each secret connection.
250
+ */
251
+ resolveJobSecrets(jobs, ast, renderSecretRef) {
252
+ // Build node -> job lookup
253
+ const nodeJob = new Map();
254
+ for (const inst of ast.instances) {
255
+ nodeJob.set(inst.id, inst.job || 'default');
256
+ }
257
+ // Build step lookup
258
+ const stepMap = new Map();
259
+ for (const job of jobs) {
260
+ for (const step of job.steps) {
261
+ stepMap.set(step.id, step);
262
+ }
263
+ }
264
+ // Process secret connections
265
+ for (const conn of ast.connections) {
266
+ if (!conn.from.node.startsWith('secret:'))
267
+ continue;
268
+ const secretName = conn.from.node.substring(7);
269
+ const targetNode = conn.to.node;
270
+ const targetPort = conn.to.port;
271
+ const jobId = nodeJob.get(targetNode);
272
+ if (!jobId)
273
+ continue;
274
+ const job = jobs.find((j) => j.id === jobId);
275
+ if (!job)
276
+ continue;
277
+ // Add secret to job
278
+ if (!job.secrets.includes(secretName)) {
279
+ job.secrets.push(secretName);
280
+ }
281
+ // Add env var to the step
282
+ const step = stepMap.get(targetNode);
283
+ if (step) {
284
+ step.env = step.env || {};
285
+ // Convert port name to env var (e.g., npmToken -> NPM_TOKEN or use secret name directly)
286
+ step.env[targetPort.replace(/([A-Z])/g, '_$1').toUpperCase().replace(/^_/, '')] =
287
+ renderSecretRef(secretName);
288
+ }
289
+ }
290
+ }
291
+ /**
292
+ * Add artifact upload/download steps between jobs.
293
+ */
294
+ injectArtifactSteps(jobs, artifacts) {
295
+ if (artifacts.length === 0)
296
+ return;
297
+ // For each job that has dependencies, check if any dependency produces artifacts
298
+ for (const job of jobs) {
299
+ if (job.needs.length === 0)
300
+ continue;
301
+ // Check if any needed job produces artifacts
302
+ const neededJobs = jobs.filter((j) => job.needs.includes(j.id));
303
+ for (const needed of neededJobs) {
304
+ // Find artifacts that match the producing job
305
+ const jobArtifacts = artifacts.filter((a) => !a.name || needed.steps.some((s) => s.nodeType === a.name));
306
+ if (jobArtifacts.length > 0) {
307
+ needed.uploadArtifacts = (needed.uploadArtifacts || []).concat(jobArtifacts);
308
+ job.downloadArtifacts = (job.downloadArtifacts || []).concat(jobArtifacts.map((a) => a.name));
309
+ }
310
+ }
311
+ }
312
+ }
313
+ /**
314
+ * Generate SECRETS_SETUP.md with per-secret setup instructions.
315
+ */
316
+ generateSecretsDoc(secrets, platform) {
317
+ if (secrets.length === 0)
318
+ return '';
319
+ const lines = [
320
+ '# Secrets Setup Guide',
321
+ '',
322
+ `This workflow requires ${secrets.length} secret(s) to be configured.`,
323
+ '',
324
+ ];
325
+ for (const secret of secrets) {
326
+ lines.push(`## ${secret.name}`);
327
+ if (secret.description) {
328
+ lines.push(`> ${secret.description}`);
329
+ }
330
+ lines.push('');
331
+ if (platform === 'github-actions' || secret.platform === 'all' || secret.platform === 'github' || !secret.platform) {
332
+ lines.push('**GitHub Actions:**');
333
+ lines.push('1. Go to your repository on GitHub');
334
+ lines.push('2. Navigate to Settings > Secrets and variables > Actions');
335
+ lines.push('3. Click "New repository secret"');
336
+ lines.push(`4. Name: \`${secret.name}\``);
337
+ lines.push('5. Paste your secret value and click "Add secret"');
338
+ lines.push('');
339
+ }
340
+ if (platform === 'gitlab-ci' || secret.platform === 'all' || secret.platform === 'gitlab' || !secret.platform) {
341
+ lines.push('**GitLab CI:**');
342
+ lines.push('1. Go to your project on GitLab');
343
+ lines.push('2. Navigate to Settings > CI/CD > Variables');
344
+ lines.push('3. Click "Add variable"');
345
+ lines.push(`4. Key: \`${secret.name}\``);
346
+ lines.push('5. Paste your secret value');
347
+ lines.push('6. Check "Mask variable" and optionally "Protect variable"');
348
+ lines.push('7. Click "Add variable"');
349
+ lines.push('');
350
+ }
351
+ lines.push('---');
352
+ lines.push('');
353
+ }
354
+ return lines.join('\n');
355
+ }
356
+ }
357
+ //# sourceMappingURL=cicd-base.js.map
@@ -10,6 +10,12 @@ import { BaseExportTarget, type ExportOptions, type ExportArtifacts, type Deploy
10
10
  export declare class CloudflareTarget extends BaseExportTarget {
11
11
  readonly name = "cloudflare";
12
12
  readonly description = "Cloudflare Workers";
13
+ readonly deploySchema: {
14
+ compatDate: {
15
+ type: "string";
16
+ description: string;
17
+ };
18
+ };
13
19
  generate(options: ExportOptions): Promise<ExportArtifacts>;
14
20
  /**
15
21
  * Generate OpenAPI specification for the workflow
@@ -687,6 +687,9 @@ export default {
687
687
  export class CloudflareTarget extends BaseExportTarget {
688
688
  name = 'cloudflare';
689
689
  description = 'Cloudflare Workers';
690
+ deploySchema = {
691
+ compatDate: { type: 'string', description: 'Compatibility date' },
692
+ };
690
693
  async generate(options) {
691
694
  const files = [];
692
695
  const includeDocs = options.includeDocs ?? false;
@@ -0,0 +1,54 @@
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 { BaseCICDTarget } from './cicd-base.js';
17
+ import type { ExportOptions, ExportArtifacts, DeployInstructions } from './base.js';
18
+ export declare class GitHubActionsTarget extends BaseCICDTarget {
19
+ readonly name = "github-actions";
20
+ readonly description = "GitHub Actions workflow YAML (.github/workflows/)";
21
+ readonly deploySchema: {
22
+ runner: {
23
+ type: "string";
24
+ description: string;
25
+ default: string;
26
+ };
27
+ };
28
+ readonly nodeTypeDeploySchema: {
29
+ action: {
30
+ type: "string";
31
+ description: string;
32
+ };
33
+ with: {
34
+ type: "string";
35
+ description: string;
36
+ };
37
+ label: {
38
+ type: "string";
39
+ description: string;
40
+ };
41
+ };
42
+ generate(options: ExportOptions): Promise<ExportArtifacts>;
43
+ getDeployInstructions(_artifacts: ExportArtifacts): DeployInstructions;
44
+ private renderWorkflowYAML;
45
+ private renderTriggers;
46
+ private renderJob;
47
+ private renderStep;
48
+ private renderCacheStep;
49
+ /**
50
+ * Apply workflow-level options (cache, services, matrix) to jobs.
51
+ */
52
+ private applyWorkflowOptions;
53
+ }
54
+ //# sourceMappingURL=github-actions.d.ts.map