@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.
- package/README.md +41 -2
- package/dist/api/validate.js +8 -2
- package/dist/ast/types.d.ts +120 -0
- package/dist/chevrotain-parser/node-parser.d.ts +4 -0
- package/dist/chevrotain-parser/node-parser.js +41 -1
- package/dist/chevrotain-parser/port-parser.d.ts +1 -0
- package/dist/chevrotain-parser/port-parser.js +22 -2
- package/dist/chevrotain-parser/tokens.d.ts +3 -0
- package/dist/chevrotain-parser/tokens.js +15 -0
- package/dist/cli/commands/export.js +25 -38
- package/dist/cli/flow-weaver.mjs +63703 -54297
- package/dist/cli/templates/index.js +9 -0
- package/dist/cli/templates/workflows/cicd-docker.d.ts +9 -0
- package/dist/cli/templates/workflows/cicd-docker.js +110 -0
- package/dist/cli/templates/workflows/cicd-matrix.d.ts +9 -0
- package/dist/cli/templates/workflows/cicd-matrix.js +112 -0
- package/dist/cli/templates/workflows/cicd-multi-env.d.ts +9 -0
- package/dist/cli/templates/workflows/cicd-multi-env.js +118 -0
- package/dist/cli/templates/workflows/cicd-test-deploy.d.ts +11 -0
- package/dist/cli/templates/workflows/cicd-test-deploy.js +149 -0
- package/dist/constants.js +7 -0
- package/dist/deployment/index.d.ts +14 -7
- package/dist/deployment/index.js +29 -17
- package/dist/deployment/targets/base.d.ts +27 -2
- package/dist/deployment/targets/base.js +38 -6
- package/dist/deployment/targets/cicd-base.d.ts +111 -0
- package/dist/deployment/targets/cicd-base.js +357 -0
- package/dist/deployment/targets/cloudflare.d.ts +6 -0
- package/dist/deployment/targets/cloudflare.js +3 -0
- package/dist/deployment/targets/github-actions.d.ts +54 -0
- package/dist/deployment/targets/github-actions.js +366 -0
- package/dist/deployment/targets/gitlab-ci.d.ts +65 -0
- package/dist/deployment/targets/gitlab-ci.js +374 -0
- package/dist/deployment/targets/inngest.d.ts +25 -0
- package/dist/deployment/targets/inngest.js +10 -1
- package/dist/deployment/targets/lambda.d.ts +17 -0
- package/dist/deployment/targets/lambda.js +5 -0
- package/dist/deployment/targets/vercel.d.ts +16 -0
- package/dist/deployment/targets/vercel.js +5 -0
- package/dist/diagram/geometry.js +13 -5
- package/dist/export/index.d.ts +13 -9
- package/dist/export/index.js +129 -997
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/jsdoc-parser.d.ts +130 -0
- package/dist/jsdoc-parser.js +408 -4
- package/dist/marketplace/index.d.ts +1 -1
- package/dist/marketplace/types.d.ts +13 -0
- package/dist/marketplace/validator.js +21 -2
- package/dist/mcp/tools-export.js +56 -14
- package/dist/parser.js +28 -1
- package/dist/validation/cicd-detection.d.ts +33 -0
- package/dist/validation/cicd-detection.js +76 -0
- package/dist/validation/cicd-rules.d.ts +62 -0
- package/dist/validation/cicd-rules.js +284 -0
- package/docs/reference/scaffold.md +4 -0
- package/package.json +4 -3
|
@@ -0,0 +1,284 @@
|
|
|
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 { getDeclaredSecrets, getReferencedSecrets, getJobNames, } from './cicd-detection.js';
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Rule 1: Secret Not Declared
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* A `secret:X` pseudo-node is referenced in @connect but no `@secret X` is declared.
|
|
22
|
+
* This means the export target won't know about the secret and can't generate
|
|
23
|
+
* proper environment variable references.
|
|
24
|
+
*/
|
|
25
|
+
export const secretNotDeclaredRule = {
|
|
26
|
+
name: 'CICD_SECRET_NOT_DECLARED',
|
|
27
|
+
validate(ast) {
|
|
28
|
+
const errors = [];
|
|
29
|
+
const declared = new Set(getDeclaredSecrets(ast));
|
|
30
|
+
const referenced = getReferencedSecrets(ast);
|
|
31
|
+
for (const secretName of referenced) {
|
|
32
|
+
if (!declared.has(secretName)) {
|
|
33
|
+
errors.push({
|
|
34
|
+
type: 'error',
|
|
35
|
+
code: 'CICD_SECRET_NOT_DECLARED',
|
|
36
|
+
message: `Secret '${secretName}' is referenced via @connect but not declared with @secret. Add: @secret ${secretName} - description`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return errors;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Rule 2: Secret Unused
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* A `@secret X` is declared but never wired via `@connect secret:X -> ...`.
|
|
48
|
+
* The secret might be intentional (used in a shell-command step) or a leftover.
|
|
49
|
+
*/
|
|
50
|
+
export const secretUnusedRule = {
|
|
51
|
+
name: 'CICD_SECRET_UNUSED',
|
|
52
|
+
validate(ast) {
|
|
53
|
+
const errors = [];
|
|
54
|
+
const referenced = new Set(getReferencedSecrets(ast));
|
|
55
|
+
const declared = getDeclaredSecrets(ast);
|
|
56
|
+
for (const secretName of declared) {
|
|
57
|
+
if (!referenced.has(secretName)) {
|
|
58
|
+
errors.push({
|
|
59
|
+
type: 'warning',
|
|
60
|
+
code: 'CICD_SECRET_UNUSED',
|
|
61
|
+
message: `Secret '${secretName}' is declared but not wired to any node. If used in a shell command, this is fine. Otherwise, wire it with: @connect secret:${secretName} -> node.port`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return errors;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Rule 3: Trigger Missing
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* A CI/CD workflow with no trigger annotations would never run automatically.
|
|
73
|
+
* Needs at least one @trigger (push, pull_request, schedule, dispatch, or tag).
|
|
74
|
+
*/
|
|
75
|
+
export const triggerMissingRule = {
|
|
76
|
+
name: 'CICD_TRIGGER_MISSING',
|
|
77
|
+
validate(ast) {
|
|
78
|
+
const triggers = ast.options?.cicd?.triggers || [];
|
|
79
|
+
// Also check for FW-style triggers (event=, cron=)
|
|
80
|
+
const fwTrigger = ast.options?.trigger;
|
|
81
|
+
if (triggers.length === 0 && !fwTrigger) {
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
type: 'warning',
|
|
85
|
+
code: 'CICD_TRIGGER_MISSING',
|
|
86
|
+
message: 'No trigger annotations found. The pipeline will never run automatically. Add at least one: @trigger push branches="main" or @trigger dispatch',
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
return [];
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Rule 4: Job Missing Runner
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
/**
|
|
97
|
+
* A job (group of nodes with same [job: "name"]) has no explicit runner
|
|
98
|
+
* and the workflow has no default @runner. The export target will use a
|
|
99
|
+
* platform default, which may not be what the user expects.
|
|
100
|
+
*/
|
|
101
|
+
export const jobMissingRunnerRule = {
|
|
102
|
+
name: 'CICD_JOB_MISSING_RUNNER',
|
|
103
|
+
validate(ast) {
|
|
104
|
+
const errors = [];
|
|
105
|
+
const defaultRunner = ast.options?.cicd?.runner;
|
|
106
|
+
const jobNames = getJobNames(ast);
|
|
107
|
+
// If there's a default runner, all jobs are covered
|
|
108
|
+
if (defaultRunner)
|
|
109
|
+
return [];
|
|
110
|
+
// If there are jobs but no default runner, warn
|
|
111
|
+
if (jobNames.length > 0) {
|
|
112
|
+
errors.push({
|
|
113
|
+
type: 'warning',
|
|
114
|
+
code: 'CICD_JOB_MISSING_RUNNER',
|
|
115
|
+
message: `No @runner annotation found. Jobs (${jobNames.join(', ')}) will use platform defaults. Add: @runner ubuntu-latest`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return errors;
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Rule 5: Artifact Cross-Job
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
/**
|
|
125
|
+
* Data flows between nodes in different jobs via connections, but no @artifact
|
|
126
|
+
* is declared. In CI/CD, each job runs in a fresh environment — data must be
|
|
127
|
+
* explicitly passed via artifacts.
|
|
128
|
+
*/
|
|
129
|
+
export const artifactCrossJobRule = {
|
|
130
|
+
name: 'CICD_ARTIFACT_CROSS_JOB',
|
|
131
|
+
validate(ast) {
|
|
132
|
+
const errors = [];
|
|
133
|
+
const artifacts = ast.options?.cicd?.artifacts || [];
|
|
134
|
+
// Build node -> job map
|
|
135
|
+
const nodeJob = new Map();
|
|
136
|
+
for (const inst of ast.instances) {
|
|
137
|
+
if (inst.job)
|
|
138
|
+
nodeJob.set(inst.id, inst.job);
|
|
139
|
+
}
|
|
140
|
+
// Check connections between nodes in different jobs
|
|
141
|
+
const crossJobPairs = new Set();
|
|
142
|
+
for (const conn of ast.connections) {
|
|
143
|
+
// Skip secret: pseudo-nodes and Start/Exit
|
|
144
|
+
if (conn.from.node.startsWith('secret:'))
|
|
145
|
+
continue;
|
|
146
|
+
if (conn.from.node === 'Start' || conn.to.node === 'Exit')
|
|
147
|
+
continue;
|
|
148
|
+
const fromJob = nodeJob.get(conn.from.node);
|
|
149
|
+
const toJob = nodeJob.get(conn.to.node);
|
|
150
|
+
if (fromJob && toJob && fromJob !== toJob) {
|
|
151
|
+
const pairKey = `${fromJob}->${toJob}`;
|
|
152
|
+
if (!crossJobPairs.has(pairKey)) {
|
|
153
|
+
crossJobPairs.add(pairKey);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// If there are cross-job data flows but no artifacts declared, warn
|
|
158
|
+
if (crossJobPairs.size > 0 && artifacts.length === 0) {
|
|
159
|
+
const pairs = Array.from(crossJobPairs);
|
|
160
|
+
errors.push({
|
|
161
|
+
type: 'warning',
|
|
162
|
+
code: 'CICD_ARTIFACT_CROSS_JOB',
|
|
163
|
+
message: `Data flows between jobs (${pairs.join(', ')}) but no @artifact is declared. In CI/CD, each job runs in a fresh environment. Add @artifact declarations to pass data between jobs.`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return errors;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Rule 6: Circular Job Dependencies
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
/**
|
|
173
|
+
* Job dependencies (derived from @path connections between jobs) form a cycle.
|
|
174
|
+
* CI/CD platforms reject circular job dependencies.
|
|
175
|
+
*/
|
|
176
|
+
export const circularJobDepsRule = {
|
|
177
|
+
name: 'CICD_CIRCULAR_JOB_DEPS',
|
|
178
|
+
validate(ast) {
|
|
179
|
+
const errors = [];
|
|
180
|
+
// Build node -> job map
|
|
181
|
+
const nodeJob = new Map();
|
|
182
|
+
for (const inst of ast.instances) {
|
|
183
|
+
if (inst.job)
|
|
184
|
+
nodeJob.set(inst.id, inst.job);
|
|
185
|
+
}
|
|
186
|
+
// Build job dependency graph from connections
|
|
187
|
+
const jobDeps = new Map();
|
|
188
|
+
for (const conn of ast.connections) {
|
|
189
|
+
if (conn.from.node.startsWith('secret:'))
|
|
190
|
+
continue;
|
|
191
|
+
if (conn.from.node === 'Start' || conn.to.node === 'Exit')
|
|
192
|
+
continue;
|
|
193
|
+
const fromJob = nodeJob.get(conn.from.node);
|
|
194
|
+
const toJob = nodeJob.get(conn.to.node);
|
|
195
|
+
if (fromJob && toJob && fromJob !== toJob) {
|
|
196
|
+
if (!jobDeps.has(toJob))
|
|
197
|
+
jobDeps.set(toJob, new Set());
|
|
198
|
+
jobDeps.get(toJob).add(fromJob);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Detect cycles using DFS
|
|
202
|
+
const visited = new Set();
|
|
203
|
+
const inStack = new Set();
|
|
204
|
+
function hasCycle(job) {
|
|
205
|
+
if (inStack.has(job))
|
|
206
|
+
return true;
|
|
207
|
+
if (visited.has(job))
|
|
208
|
+
return false;
|
|
209
|
+
visited.add(job);
|
|
210
|
+
inStack.add(job);
|
|
211
|
+
const deps = jobDeps.get(job);
|
|
212
|
+
if (deps) {
|
|
213
|
+
for (const dep of deps) {
|
|
214
|
+
if (hasCycle(dep))
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
inStack.delete(job);
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
const allJobs = getJobNames(ast);
|
|
222
|
+
for (const job of allJobs) {
|
|
223
|
+
if (hasCycle(job)) {
|
|
224
|
+
errors.push({
|
|
225
|
+
type: 'error',
|
|
226
|
+
code: 'CICD_CIRCULAR_JOB_DEPS',
|
|
227
|
+
message: `Circular dependency detected involving job '${job}'. CI/CD platforms require a directed acyclic graph of job dependencies.`,
|
|
228
|
+
});
|
|
229
|
+
break; // Report once
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return errors;
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
// Rule 7: Matrix with Environment
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
/**
|
|
239
|
+
* Using @matrix with @environment protection means each matrix combination
|
|
240
|
+
* triggers an approval prompt. For a 3x2 matrix, that's 6 prompts.
|
|
241
|
+
*/
|
|
242
|
+
export const matrixWithEnvironmentRule = {
|
|
243
|
+
name: 'CICD_MATRIX_WITH_ENVIRONMENT',
|
|
244
|
+
validate(ast) {
|
|
245
|
+
const errors = [];
|
|
246
|
+
const matrix = ast.options?.cicd?.matrix;
|
|
247
|
+
const environments = ast.options?.cicd?.environments || [];
|
|
248
|
+
if (matrix && environments.length > 0) {
|
|
249
|
+
// Calculate matrix size
|
|
250
|
+
const dimensions = matrix.include
|
|
251
|
+
? matrix.include.length
|
|
252
|
+
: Object.values(matrix.dimensions || {}).reduce((acc, vals) => acc * vals.length, 1);
|
|
253
|
+
if (dimensions > 1) {
|
|
254
|
+
errors.push({
|
|
255
|
+
type: 'warning',
|
|
256
|
+
code: 'CICD_MATRIX_WITH_ENVIRONMENT',
|
|
257
|
+
message: `Using @matrix (${dimensions} combinations) with @environment protection will trigger ${dimensions} approval prompts per deployment. Consider separating the matrix job from the deploy job.`,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return errors;
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// Public API
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
/** All CI/CD validation rules */
|
|
268
|
+
export const cicdValidationRules = [
|
|
269
|
+
secretNotDeclaredRule,
|
|
270
|
+
secretUnusedRule,
|
|
271
|
+
triggerMissingRule,
|
|
272
|
+
jobMissingRunnerRule,
|
|
273
|
+
artifactCrossJobRule,
|
|
274
|
+
circularJobDepsRule,
|
|
275
|
+
matrixWithEnvironmentRule,
|
|
276
|
+
];
|
|
277
|
+
/**
|
|
278
|
+
* Get all CI/CD validation rules.
|
|
279
|
+
* Convenience function for passing to validateWorkflow().
|
|
280
|
+
*/
|
|
281
|
+
export function getCICDValidationRules() {
|
|
282
|
+
return cicdValidationRules;
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=cicd-rules.js.map
|
|
@@ -94,6 +94,10 @@ flow-weaver templates [--json]
|
|
|
94
94
|
| `error-handler` | Try/catch/retry pattern with error recovery |
|
|
95
95
|
| `ai-agent-durable` | Linear agent pipeline with durability — classify, tools, approval, respond |
|
|
96
96
|
| `ai-pipeline-durable` | Sequential data pipeline with durability — fetch, extract, validate, save |
|
|
97
|
+
| `cicd-test-deploy` | Standard test-and-deploy pipeline with checkout, setup, test, build, and deploy stages |
|
|
98
|
+
| `cicd-docker` | Build and push Docker images to a container registry |
|
|
99
|
+
| `cicd-multi-env` | Multi-environment deployment pipeline with staging and production stages |
|
|
100
|
+
| `cicd-matrix` | Test across multiple Node.js versions and operating systems |
|
|
97
101
|
<!-- AUTO:END workflow_templates_table -->
|
|
98
102
|
|
|
99
103
|
## Node Templates
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergenius/flow-weaver",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Deterministic workflow compiler for AI agents. Compiles to standalone TypeScript, no runtime dependencies.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -134,6 +134,7 @@
|
|
|
134
134
|
"chevrotain": "^11.0.3",
|
|
135
135
|
"chokidar": "^4.0.0",
|
|
136
136
|
"commander": "^11.1.0",
|
|
137
|
+
"esbuild": "^0.27.2",
|
|
137
138
|
"glob": "^10.3.10",
|
|
138
139
|
"immer": "^10.2.0",
|
|
139
140
|
"js-yaml": "^4.1.0",
|
|
@@ -141,8 +142,8 @@
|
|
|
141
142
|
"source-map": "^0.7.6",
|
|
142
143
|
"ts-morph": "^21.0.1",
|
|
143
144
|
"ws": "^8.19.0",
|
|
144
|
-
"
|
|
145
|
-
"
|
|
145
|
+
"yaml": "^2.8.2",
|
|
146
|
+
"zod": "^3.22.4"
|
|
146
147
|
},
|
|
147
148
|
"devDependencies": {
|
|
148
149
|
"@types/js-yaml": "^4.0.9",
|