@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,374 @@
|
|
|
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 { stringify as yamlStringify } from 'yaml';
|
|
16
|
+
import { isCICDWorkflow } from '../../validation/cicd-detection.js';
|
|
17
|
+
import { BaseCICDTarget, } from './cicd-base.js';
|
|
18
|
+
import { parseWorkflow } from '../../api/index.js';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
export class GitLabCITarget extends BaseCICDTarget {
|
|
21
|
+
name = 'gitlab-ci';
|
|
22
|
+
description = 'GitLab CI/CD pipeline (.gitlab-ci.yml)';
|
|
23
|
+
deploySchema = {
|
|
24
|
+
runner: { type: 'string', description: 'Default Docker image', default: 'ubuntu:latest' },
|
|
25
|
+
};
|
|
26
|
+
nodeTypeDeploySchema = {
|
|
27
|
+
script: { type: 'string[]', description: 'GitLab CI script commands' },
|
|
28
|
+
image: { type: 'string', description: 'Docker image override' },
|
|
29
|
+
label: { type: 'string', description: 'Step display name' },
|
|
30
|
+
};
|
|
31
|
+
async generate(options) {
|
|
32
|
+
const filePath = path.resolve(options.sourceFile);
|
|
33
|
+
const outputDir = path.resolve(options.outputDir);
|
|
34
|
+
// Parse the workflow file to get AST
|
|
35
|
+
const parseResult = await parseWorkflow(filePath, { nodeTypesOnly: false });
|
|
36
|
+
if (parseResult.errors.length > 0) {
|
|
37
|
+
throw new Error(`Parse errors: ${parseResult.errors.join('; ')}`);
|
|
38
|
+
}
|
|
39
|
+
const allWorkflows = parseResult.allWorkflows || [];
|
|
40
|
+
const targetWorkflows = options.workflowName
|
|
41
|
+
? allWorkflows.filter((w) => w.name === options.workflowName || w.functionName === options.workflowName)
|
|
42
|
+
: allWorkflows.filter((w) => isCICDWorkflow(w));
|
|
43
|
+
if (targetWorkflows.length === 0) {
|
|
44
|
+
throw new Error('No CI/CD workflows found. Ensure workflow has CI/CD annotations (@secret, @runner, @trigger, [job:], etc.)');
|
|
45
|
+
}
|
|
46
|
+
const files = [];
|
|
47
|
+
for (const ast of targetWorkflows) {
|
|
48
|
+
// Build job graph
|
|
49
|
+
const jobs = this.buildJobGraph(ast);
|
|
50
|
+
// Resolve secrets
|
|
51
|
+
this.resolveJobSecrets(jobs, ast, (name) => `$${name}`);
|
|
52
|
+
// Inject artifacts
|
|
53
|
+
const artifacts = ast.options?.cicd?.artifacts || [];
|
|
54
|
+
this.injectArtifactSteps(jobs, artifacts);
|
|
55
|
+
// Apply workflow options
|
|
56
|
+
this.applyWorkflowOptions(jobs, ast);
|
|
57
|
+
// Generate YAML
|
|
58
|
+
const yamlContent = this.renderPipelineYAML(ast, jobs);
|
|
59
|
+
files.push(this.createFile(outputDir, '.gitlab-ci.yml', yamlContent, 'config'));
|
|
60
|
+
// Generate secrets doc if secrets exist
|
|
61
|
+
const secrets = ast.options?.cicd?.secrets || [];
|
|
62
|
+
if (secrets.length > 0) {
|
|
63
|
+
const secretsDoc = this.generateSecretsDoc(secrets, 'gitlab-ci');
|
|
64
|
+
files.push(this.createFile(outputDir, 'SECRETS_SETUP.md', secretsDoc, 'other'));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
files,
|
|
69
|
+
target: this.name,
|
|
70
|
+
workflowName: options.displayName || targetWorkflows[0].name,
|
|
71
|
+
entryPoint: files[0].relativePath,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
getDeployInstructions(_artifacts) {
|
|
75
|
+
return {
|
|
76
|
+
title: 'Deploy GitLab CI Pipeline',
|
|
77
|
+
prerequisites: [
|
|
78
|
+
'GitLab repository',
|
|
79
|
+
'CI/CD variables configured (see SECRETS_SETUP.md)',
|
|
80
|
+
'GitLab Runner available (shared or project-specific)',
|
|
81
|
+
],
|
|
82
|
+
steps: [
|
|
83
|
+
'Copy .gitlab-ci.yml to your repository root',
|
|
84
|
+
'Configure required variables in GitLab (Settings > CI/CD > Variables)',
|
|
85
|
+
'Push to trigger the pipeline',
|
|
86
|
+
],
|
|
87
|
+
localTestSteps: [
|
|
88
|
+
'Install gitlab-runner: brew install gitlab-runner',
|
|
89
|
+
'Run locally: gitlab-runner exec docker <job-name>',
|
|
90
|
+
],
|
|
91
|
+
links: [
|
|
92
|
+
{ label: 'GitLab CI/CD Docs', url: 'https://docs.gitlab.com/ee/ci/' },
|
|
93
|
+
{ label: 'GitLab CI Lint', url: 'https://docs.gitlab.com/ee/ci/lint.html' },
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Private: YAML Rendering
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
renderPipelineYAML(ast, jobs) {
|
|
101
|
+
const doc = {};
|
|
102
|
+
// stages (derived from job dependency order)
|
|
103
|
+
const stages = this.deriveStages(jobs);
|
|
104
|
+
doc.stages = stages;
|
|
105
|
+
// Default image
|
|
106
|
+
const defaultImage = this.deriveDefaultImage(ast, jobs);
|
|
107
|
+
if (defaultImage) {
|
|
108
|
+
doc.default = { image: defaultImage };
|
|
109
|
+
}
|
|
110
|
+
// Workflow-level rules (from triggers)
|
|
111
|
+
const rules = this.renderWorkflowRules(ast.options?.cicd?.triggers || []);
|
|
112
|
+
if (rules.length > 0) {
|
|
113
|
+
doc.workflow = { rules };
|
|
114
|
+
}
|
|
115
|
+
// Job definitions
|
|
116
|
+
for (const job of jobs) {
|
|
117
|
+
doc[job.id] = this.renderJob(job, ast, stages);
|
|
118
|
+
}
|
|
119
|
+
return yamlStringify(doc, {
|
|
120
|
+
lineWidth: 120,
|
|
121
|
+
defaultStringType: 'PLAIN',
|
|
122
|
+
defaultKeyType: 'PLAIN',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Derive stages from job dependency order.
|
|
127
|
+
* Jobs with no deps → stage 1, jobs depending on stage 1 → stage 2, etc.
|
|
128
|
+
*/
|
|
129
|
+
deriveStages(jobs) {
|
|
130
|
+
const stages = [];
|
|
131
|
+
const assigned = new Map();
|
|
132
|
+
// Assign stages based on dependency depth
|
|
133
|
+
function getStage(jobId, jobs) {
|
|
134
|
+
if (assigned.has(jobId))
|
|
135
|
+
return assigned.get(jobId);
|
|
136
|
+
const job = jobs.find((j) => j.id === jobId);
|
|
137
|
+
if (!job || job.needs.length === 0) {
|
|
138
|
+
assigned.set(jobId, jobId);
|
|
139
|
+
return jobId;
|
|
140
|
+
}
|
|
141
|
+
// Stage is one after the latest dependency
|
|
142
|
+
const depStages = job.needs.map((dep) => getStage(dep, jobs));
|
|
143
|
+
assigned.set(jobId, jobId);
|
|
144
|
+
return jobId;
|
|
145
|
+
}
|
|
146
|
+
// Simple: use job IDs as stage names, ordered by dependency
|
|
147
|
+
for (const job of jobs) {
|
|
148
|
+
getStage(job.id, jobs);
|
|
149
|
+
if (!stages.includes(job.id)) {
|
|
150
|
+
stages.push(job.id);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return stages;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Derive default image from @deploy annotations or built-in mappings.
|
|
157
|
+
*/
|
|
158
|
+
deriveDefaultImage(_ast, jobs) {
|
|
159
|
+
// Check steps for @deploy gitlab-ci image or built-in mapping
|
|
160
|
+
for (const job of jobs) {
|
|
161
|
+
for (const step of job.steps) {
|
|
162
|
+
const mapping = this.resolveActionMapping(step, 'gitlab-ci');
|
|
163
|
+
if (mapping?.gitlabImage)
|
|
164
|
+
return mapping.gitlabImage;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Convert CI/CD triggers to GitLab CI workflow rules.
|
|
171
|
+
*/
|
|
172
|
+
renderWorkflowRules(triggers) {
|
|
173
|
+
if (triggers.length === 0)
|
|
174
|
+
return [];
|
|
175
|
+
const rules = [];
|
|
176
|
+
for (const trigger of triggers) {
|
|
177
|
+
switch (trigger.type) {
|
|
178
|
+
case 'push':
|
|
179
|
+
if (trigger.branches) {
|
|
180
|
+
for (const branch of trigger.branches) {
|
|
181
|
+
rules.push({
|
|
182
|
+
if: `$CI_COMMIT_BRANCH == "${branch}"`,
|
|
183
|
+
when: 'always',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
rules.push({ if: '$CI_PIPELINE_SOURCE == "push"', when: 'always' });
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
case 'pull_request':
|
|
192
|
+
rules.push({
|
|
193
|
+
if: '$CI_PIPELINE_SOURCE == "merge_request_event"',
|
|
194
|
+
when: 'always',
|
|
195
|
+
});
|
|
196
|
+
break;
|
|
197
|
+
case 'schedule':
|
|
198
|
+
rules.push({
|
|
199
|
+
if: '$CI_PIPELINE_SOURCE == "schedule"',
|
|
200
|
+
when: 'always',
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
case 'dispatch':
|
|
204
|
+
rules.push({
|
|
205
|
+
if: '$CI_PIPELINE_SOURCE == "web" || $CI_PIPELINE_SOURCE == "api"',
|
|
206
|
+
when: 'always',
|
|
207
|
+
});
|
|
208
|
+
break;
|
|
209
|
+
case 'tag':
|
|
210
|
+
rules.push({ if: '$CI_COMMIT_TAG', when: 'always' });
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return rules;
|
|
215
|
+
}
|
|
216
|
+
renderJob(job, ast, stages) {
|
|
217
|
+
const jobObj = {};
|
|
218
|
+
// stage
|
|
219
|
+
jobObj.stage = job.id;
|
|
220
|
+
// image (from runner or default)
|
|
221
|
+
if (job.runner && job.runner !== 'ubuntu-latest') {
|
|
222
|
+
// GitLab uses Docker images, not runner labels
|
|
223
|
+
// Map common GitHub runners to Docker images
|
|
224
|
+
const imageMap = {
|
|
225
|
+
'ubuntu-latest': 'ubuntu:latest',
|
|
226
|
+
'ubuntu-22.04': 'ubuntu:22.04',
|
|
227
|
+
'ubuntu-20.04': 'ubuntu:20.04',
|
|
228
|
+
};
|
|
229
|
+
const image = imageMap[job.runner] || job.runner;
|
|
230
|
+
jobObj.image = image;
|
|
231
|
+
}
|
|
232
|
+
// needs (for DAG mode instead of stage-based ordering)
|
|
233
|
+
if (job.needs.length > 0) {
|
|
234
|
+
jobObj.needs = job.needs;
|
|
235
|
+
}
|
|
236
|
+
// environment
|
|
237
|
+
if (job.environment) {
|
|
238
|
+
const envConfig = ast.options?.cicd?.environments?.find((e) => e.name === job.environment);
|
|
239
|
+
const envObj = { name: job.environment };
|
|
240
|
+
if (envConfig?.url)
|
|
241
|
+
envObj.url = envConfig.url;
|
|
242
|
+
if (envConfig?.reviewers)
|
|
243
|
+
envObj.deployment_tier = 'production';
|
|
244
|
+
jobObj.environment = envObj;
|
|
245
|
+
// Protected environments require manual approval in GitLab
|
|
246
|
+
if (envConfig?.reviewers) {
|
|
247
|
+
jobObj.when = 'manual';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// services
|
|
251
|
+
if (job.services && job.services.length > 0) {
|
|
252
|
+
jobObj.services = job.services.map((svc) => {
|
|
253
|
+
const svcObj = { name: svc.image };
|
|
254
|
+
if (svc.ports) {
|
|
255
|
+
// GitLab services expose the first port automatically
|
|
256
|
+
// Additional port mapping needs alias
|
|
257
|
+
}
|
|
258
|
+
return svcObj;
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// variables (from secrets)
|
|
262
|
+
if (job.secrets.length > 0) {
|
|
263
|
+
// In GitLab, CI/CD variables are automatically available
|
|
264
|
+
// But we document them for clarity
|
|
265
|
+
const variables = {};
|
|
266
|
+
for (const secret of job.secrets) {
|
|
267
|
+
variables[secret] = `$${secret}`;
|
|
268
|
+
}
|
|
269
|
+
jobObj.variables = variables;
|
|
270
|
+
}
|
|
271
|
+
// cache
|
|
272
|
+
if (job.cache) {
|
|
273
|
+
jobObj.cache = this.renderCache(job.cache);
|
|
274
|
+
}
|
|
275
|
+
// artifacts (upload)
|
|
276
|
+
if (job.uploadArtifacts && job.uploadArtifacts.length > 0) {
|
|
277
|
+
const paths = job.uploadArtifacts.map((a) => a.path);
|
|
278
|
+
const expiry = job.uploadArtifacts[0].retention
|
|
279
|
+
? `${job.uploadArtifacts[0].retention} days`
|
|
280
|
+
: '1 week';
|
|
281
|
+
jobObj.artifacts = {
|
|
282
|
+
paths,
|
|
283
|
+
expire_in: expiry,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// script (the actual steps)
|
|
287
|
+
const script = [];
|
|
288
|
+
// Download artifacts (GitLab handles this automatically via `needs:`)
|
|
289
|
+
// but we add a comment for clarity if explicit artifacts are expected
|
|
290
|
+
if (job.downloadArtifacts && job.downloadArtifacts.length > 0) {
|
|
291
|
+
script.push(`# Artifacts from: ${job.downloadArtifacts.join(', ')} (downloaded automatically via needs:)`);
|
|
292
|
+
}
|
|
293
|
+
// Step scripts
|
|
294
|
+
for (const step of job.steps) {
|
|
295
|
+
const stepScript = this.renderStepScript(step);
|
|
296
|
+
script.push(...stepScript);
|
|
297
|
+
}
|
|
298
|
+
jobObj.script = script;
|
|
299
|
+
return jobObj;
|
|
300
|
+
}
|
|
301
|
+
renderStepScript(step) {
|
|
302
|
+
const mapping = this.resolveActionMapping(step, 'gitlab-ci');
|
|
303
|
+
const lines = [];
|
|
304
|
+
// Add env vars as export statements if present
|
|
305
|
+
if (step.env) {
|
|
306
|
+
for (const [key, value] of Object.entries(step.env)) {
|
|
307
|
+
lines.push(`export ${key}="${value}"`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (mapping?.gitlabScript) {
|
|
311
|
+
lines.push(`# ${mapping.label || step.name}`);
|
|
312
|
+
lines.push(...mapping.gitlabScript);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
// Unknown node type — generate TODO
|
|
316
|
+
lines.push(`# TODO: Implement '${step.id}' (node type: ${step.nodeType})`);
|
|
317
|
+
lines.push(`echo "Step: ${step.name}"`);
|
|
318
|
+
}
|
|
319
|
+
return lines;
|
|
320
|
+
}
|
|
321
|
+
renderCache(cache) {
|
|
322
|
+
const cacheObj = {};
|
|
323
|
+
switch (cache.strategy) {
|
|
324
|
+
case 'npm':
|
|
325
|
+
cacheObj.key = {
|
|
326
|
+
files: [cache.key || 'package-lock.json'],
|
|
327
|
+
};
|
|
328
|
+
cacheObj.paths = [cache.path || 'node_modules/'];
|
|
329
|
+
break;
|
|
330
|
+
case 'pip':
|
|
331
|
+
cacheObj.key = {
|
|
332
|
+
files: [cache.key || 'requirements.txt'],
|
|
333
|
+
};
|
|
334
|
+
cacheObj.paths = [cache.path || '.pip-cache/'];
|
|
335
|
+
break;
|
|
336
|
+
default:
|
|
337
|
+
cacheObj.key = cache.key || '$CI_COMMIT_REF_SLUG';
|
|
338
|
+
cacheObj.paths = [cache.path || '.cache/'];
|
|
339
|
+
}
|
|
340
|
+
return cacheObj;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Apply workflow-level options to jobs.
|
|
344
|
+
*/
|
|
345
|
+
applyWorkflowOptions(jobs, ast) {
|
|
346
|
+
const cicd = ast.options?.cicd;
|
|
347
|
+
if (!cicd)
|
|
348
|
+
return;
|
|
349
|
+
// Apply cache to all jobs
|
|
350
|
+
if (cicd.caches && cicd.caches.length > 0) {
|
|
351
|
+
for (const job of jobs) {
|
|
352
|
+
if (!job.cache) {
|
|
353
|
+
job.cache = cicd.caches[0];
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Apply services to all jobs
|
|
358
|
+
if (cicd.services && cicd.services.length > 0) {
|
|
359
|
+
for (const job of jobs) {
|
|
360
|
+
if (!job.services) {
|
|
361
|
+
job.services = cicd.services;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Apply matrix (GitLab uses `parallel: matrix:`)
|
|
366
|
+
if (cicd.matrix) {
|
|
367
|
+
const rootJobs = jobs.filter((j) => j.needs.length === 0);
|
|
368
|
+
for (const job of rootJobs) {
|
|
369
|
+
job.matrix = cicd.matrix;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=gitlab-ci.js.map
|
|
@@ -12,6 +12,31 @@ import { BaseExportTarget, type ExportOptions, type ExportArtifacts, type Deploy
|
|
|
12
12
|
export declare class InngestTarget extends BaseExportTarget {
|
|
13
13
|
readonly name = "inngest";
|
|
14
14
|
readonly description = "Inngest \u2014 durable, event-driven workflow functions";
|
|
15
|
+
readonly deploySchema: {
|
|
16
|
+
durableSteps: {
|
|
17
|
+
type: "boolean";
|
|
18
|
+
description: string;
|
|
19
|
+
default: boolean;
|
|
20
|
+
};
|
|
21
|
+
framework: {
|
|
22
|
+
type: "string";
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
serve: {
|
|
26
|
+
type: "boolean";
|
|
27
|
+
description: string;
|
|
28
|
+
default: boolean;
|
|
29
|
+
};
|
|
30
|
+
retries: {
|
|
31
|
+
type: "number";
|
|
32
|
+
description: string;
|
|
33
|
+
default: number;
|
|
34
|
+
};
|
|
35
|
+
triggerEvent: {
|
|
36
|
+
type: "string";
|
|
37
|
+
description: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
15
40
|
/**
|
|
16
41
|
* Sanitize a name into a valid Inngest ID (lowercase, alphanumeric + hyphens)
|
|
17
42
|
*/
|
|
@@ -394,6 +394,13 @@ export default app;
|
|
|
394
394
|
export class InngestTarget extends BaseExportTarget {
|
|
395
395
|
name = 'inngest';
|
|
396
396
|
description = 'Inngest — durable, event-driven workflow functions';
|
|
397
|
+
deploySchema = {
|
|
398
|
+
durableSteps: { type: 'boolean', description: 'Per-node step.run() for durability', default: false },
|
|
399
|
+
framework: { type: 'string', description: 'Framework adapter: next, express, hono, fastify, remix' },
|
|
400
|
+
serve: { type: 'boolean', description: 'Generate serve() handler export', default: false },
|
|
401
|
+
retries: { type: 'number', description: 'Retries per function', default: 3 },
|
|
402
|
+
triggerEvent: { type: 'string', description: 'Custom trigger event name' },
|
|
403
|
+
};
|
|
397
404
|
/**
|
|
398
405
|
* Sanitize a name into a valid Inngest ID (lowercase, alphanumeric + hyphens)
|
|
399
406
|
*/
|
|
@@ -413,7 +420,9 @@ export class InngestTarget extends BaseExportTarget {
|
|
|
413
420
|
async generate(options) {
|
|
414
421
|
const files = [];
|
|
415
422
|
const includeDocs = options.includeDocs ?? false;
|
|
416
|
-
|
|
423
|
+
// @deploy inngest annotations take precedence over CLI flags
|
|
424
|
+
const deployConfig = options.targetOptions?.deploy?.inngest;
|
|
425
|
+
const durableSteps = deployConfig?.durableSteps === true || options.targetOptions?.durableSteps === true;
|
|
417
426
|
const serviceId = this.toInngestId(options.displayName);
|
|
418
427
|
const functionId = this.toInngestId(options.workflowName);
|
|
419
428
|
const functionVar = `fn_${this.toVarName(options.workflowName)}`;
|
|
@@ -10,6 +10,23 @@ import { BaseExportTarget, type ExportOptions, type ExportArtifacts, type Deploy
|
|
|
10
10
|
export declare class LambdaTarget extends BaseExportTarget {
|
|
11
11
|
readonly name = "lambda";
|
|
12
12
|
readonly description = "AWS Lambda with SAM (Serverless Application Model)";
|
|
13
|
+
readonly deploySchema: {
|
|
14
|
+
memory: {
|
|
15
|
+
type: "number";
|
|
16
|
+
description: string;
|
|
17
|
+
default: number;
|
|
18
|
+
};
|
|
19
|
+
runtime: {
|
|
20
|
+
type: "string";
|
|
21
|
+
description: string;
|
|
22
|
+
default: string;
|
|
23
|
+
};
|
|
24
|
+
timeout: {
|
|
25
|
+
type: "number";
|
|
26
|
+
description: string;
|
|
27
|
+
default: number;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
13
30
|
generate(options: ExportOptions): Promise<ExportArtifacts>;
|
|
14
31
|
/**
|
|
15
32
|
* Generate OpenAPI specification for the workflow
|
|
@@ -873,6 +873,11 @@ Outputs:
|
|
|
873
873
|
export class LambdaTarget extends BaseExportTarget {
|
|
874
874
|
name = 'lambda';
|
|
875
875
|
description = 'AWS Lambda with SAM (Serverless Application Model)';
|
|
876
|
+
deploySchema = {
|
|
877
|
+
memory: { type: 'number', description: 'Lambda memory in MB', default: 256 },
|
|
878
|
+
runtime: { type: 'string', description: 'Lambda runtime', default: 'nodejs20.x' },
|
|
879
|
+
timeout: { type: 'number', description: 'Function timeout in seconds', default: 30 },
|
|
880
|
+
};
|
|
876
881
|
async generate(options) {
|
|
877
882
|
const files = [];
|
|
878
883
|
const includeDocs = options.includeDocs ?? false;
|
|
@@ -10,6 +10,22 @@ import { BaseExportTarget, type ExportOptions, type ExportArtifacts, type Deploy
|
|
|
10
10
|
export declare class VercelTarget extends BaseExportTarget {
|
|
11
11
|
readonly name = "vercel";
|
|
12
12
|
readonly description = "Vercel Serverless Functions";
|
|
13
|
+
readonly deploySchema: {
|
|
14
|
+
maxDuration: {
|
|
15
|
+
type: "number";
|
|
16
|
+
description: string;
|
|
17
|
+
default: number;
|
|
18
|
+
};
|
|
19
|
+
memory: {
|
|
20
|
+
type: "number";
|
|
21
|
+
description: string;
|
|
22
|
+
default: number;
|
|
23
|
+
};
|
|
24
|
+
regions: {
|
|
25
|
+
type: "string[]";
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
13
29
|
generate(options: ExportOptions): Promise<ExportArtifacts>;
|
|
14
30
|
/**
|
|
15
31
|
* Generate OpenAPI specification for the workflow
|
|
@@ -519,6 +519,11 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|
|
519
519
|
export class VercelTarget extends BaseExportTarget {
|
|
520
520
|
name = 'vercel';
|
|
521
521
|
description = 'Vercel Serverless Functions';
|
|
522
|
+
deploySchema = {
|
|
523
|
+
maxDuration: { type: 'number', description: 'Max function duration in seconds', default: 60 },
|
|
524
|
+
memory: { type: 'number', description: 'Memory in MB', default: 1024 },
|
|
525
|
+
regions: { type: 'string[]', description: 'Deployment regions' },
|
|
526
|
+
};
|
|
522
527
|
async generate(options) {
|
|
523
528
|
const files = [];
|
|
524
529
|
const maxDuration = options.targetOptions?.maxDuration || 60;
|
package/dist/diagram/geometry.js
CHANGED
|
@@ -176,19 +176,19 @@ function orderedPorts(ports, direction) {
|
|
|
176
176
|
function buildInstanceNode(instId, instNodeType, instConfig, nodeTypeMap, theme = 'dark') {
|
|
177
177
|
const nt = nodeTypeMap.get(instNodeType);
|
|
178
178
|
const allInputs = nt
|
|
179
|
-
?
|
|
179
|
+
? filterHiddenPorts(filterNonScopedPorts(nt.inputs))
|
|
180
180
|
: {};
|
|
181
181
|
const allOutputs = nt
|
|
182
|
-
?
|
|
182
|
+
? filterHiddenPorts(filterNonScopedPorts(nt.outputs))
|
|
183
183
|
: {};
|
|
184
184
|
if (nt && !nt.expression) {
|
|
185
|
-
if (!allInputs.execute)
|
|
185
|
+
if (!allInputs.execute && !nt.inputs.execute?.hidden)
|
|
186
186
|
allInputs.execute = { dataType: 'STEP' };
|
|
187
187
|
}
|
|
188
|
-
if (nt && nt.hasSuccessPort && !allOutputs.onSuccess) {
|
|
188
|
+
if (nt && nt.hasSuccessPort && !allOutputs.onSuccess && !nt.outputs.onSuccess?.hidden) {
|
|
189
189
|
allOutputs.onSuccess = { dataType: 'STEP', isControlFlow: true };
|
|
190
190
|
}
|
|
191
|
-
if (nt && nt.hasFailurePort && !allOutputs.onFailure) {
|
|
191
|
+
if (nt && nt.hasFailurePort && !allOutputs.onFailure && !nt.outputs.onFailure?.hidden) {
|
|
192
192
|
allOutputs.onFailure = { dataType: 'STEP', isControlFlow: true, failure: true };
|
|
193
193
|
}
|
|
194
194
|
// Resolve icon: instance config → node type visuals → auto-detection
|
|
@@ -1084,6 +1084,14 @@ function findScopeParent(childId, diagramNodes) {
|
|
|
1084
1084
|
}
|
|
1085
1085
|
return undefined;
|
|
1086
1086
|
}
|
|
1087
|
+
function filterHiddenPorts(ports) {
|
|
1088
|
+
const result = {};
|
|
1089
|
+
for (const [name, def] of Object.entries(ports)) {
|
|
1090
|
+
if (!def.hidden)
|
|
1091
|
+
result[name] = def;
|
|
1092
|
+
}
|
|
1093
|
+
return result;
|
|
1094
|
+
}
|
|
1087
1095
|
function filterNonScopedPorts(ports) {
|
|
1088
1096
|
const result = {};
|
|
1089
1097
|
for (const [name, def] of Object.entries(ports)) {
|
package/dist/export/index.d.ts
CHANGED
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
* Generates deployment-ready handlers for various platforms
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Export target identifier.
|
|
7
|
+
*
|
|
8
|
+
* Widened to `string` so marketplace packs can register arbitrary targets
|
|
9
|
+
* without modifying core. Well-known built-in names (when packs are installed):
|
|
10
|
+
* 'lambda', 'vercel', 'cloudflare', 'inngest', 'github-actions', 'gitlab-ci'
|
|
7
11
|
*/
|
|
8
|
-
export type ExportTarget =
|
|
12
|
+
export type ExportTarget = string;
|
|
9
13
|
/**
|
|
10
14
|
* Options for exporting a workflow
|
|
11
15
|
*/
|
|
@@ -54,15 +58,15 @@ export interface ExportResult {
|
|
|
54
58
|
openApiSpec?: object;
|
|
55
59
|
}
|
|
56
60
|
/**
|
|
57
|
-
* Export
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*
|
|
61
|
+
* Export a workflow for deployment.
|
|
62
|
+
*
|
|
63
|
+
* Delegates to the target registry — every target is a plugin discovered
|
|
64
|
+
* from installed marketplace packs. The legacy template system has been
|
|
65
|
+
* replaced by target classes that generate their own handler + config.
|
|
62
66
|
*/
|
|
63
67
|
export declare function exportWorkflow(options: ExportOptions): Promise<ExportResult>;
|
|
64
68
|
/**
|
|
65
|
-
* List
|
|
69
|
+
* List installed export targets by querying the registry.
|
|
66
70
|
*/
|
|
67
|
-
export declare function getSupportedTargets():
|
|
71
|
+
export declare function getSupportedTargets(projectDir?: string): Promise<string[]>;
|
|
68
72
|
//# sourceMappingURL=index.d.ts.map
|