@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
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.14.0";
|
|
2
2
|
//# sourceMappingURL=generated-version.d.ts.map
|
package/dist/jsdoc-parser.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export interface JSDocNodeTypeConfig {
|
|
|
33
33
|
expression?: string;
|
|
34
34
|
scope?: string;
|
|
35
35
|
mergeStrategy?: TMergeStrategy;
|
|
36
|
+
hidden?: boolean;
|
|
36
37
|
metadata?: {
|
|
37
38
|
order?: number;
|
|
38
39
|
};
|
|
@@ -42,11 +43,14 @@ export interface JSDocNodeTypeConfig {
|
|
|
42
43
|
type: TDataType;
|
|
43
44
|
label?: string;
|
|
44
45
|
scope?: string;
|
|
46
|
+
hidden?: boolean;
|
|
45
47
|
metadata?: {
|
|
46
48
|
order?: number;
|
|
47
49
|
};
|
|
48
50
|
tsType?: string;
|
|
49
51
|
}>;
|
|
52
|
+
/** Per-target deploy config from @deploy annotations on nodeType */
|
|
53
|
+
deploy?: Record<string, Record<string, unknown>>;
|
|
50
54
|
}
|
|
51
55
|
export interface JSDocWorkflowConfig {
|
|
52
56
|
name?: string;
|
|
@@ -79,6 +83,10 @@ export interface JSDocWorkflowConfig {
|
|
|
79
83
|
line: number;
|
|
80
84
|
column: number;
|
|
81
85
|
};
|
|
86
|
+
/** CI/CD job group */
|
|
87
|
+
job?: string;
|
|
88
|
+
/** CI/CD deployment environment */
|
|
89
|
+
environment?: string;
|
|
82
90
|
}>;
|
|
83
91
|
connections?: Array<{
|
|
84
92
|
from: {
|
|
@@ -192,6 +200,69 @@ export interface JSDocWorkflowConfig {
|
|
|
192
200
|
limit: number;
|
|
193
201
|
period?: string;
|
|
194
202
|
};
|
|
203
|
+
/** @secret declarations */
|
|
204
|
+
secrets?: Array<{
|
|
205
|
+
name: string;
|
|
206
|
+
description?: string;
|
|
207
|
+
platform?: 'github' | 'gitlab' | 'all';
|
|
208
|
+
scope?: string;
|
|
209
|
+
}>;
|
|
210
|
+
/** @runner default execution environment */
|
|
211
|
+
runner?: string;
|
|
212
|
+
/** @cache configurations */
|
|
213
|
+
caches?: Array<{
|
|
214
|
+
strategy: string;
|
|
215
|
+
path?: string;
|
|
216
|
+
key?: string;
|
|
217
|
+
}>;
|
|
218
|
+
/** @artifact declarations */
|
|
219
|
+
artifacts?: Array<{
|
|
220
|
+
name: string;
|
|
221
|
+
path: string;
|
|
222
|
+
retention?: number;
|
|
223
|
+
}>;
|
|
224
|
+
/** @environment declarations */
|
|
225
|
+
environments?: Array<{
|
|
226
|
+
name: string;
|
|
227
|
+
url?: string;
|
|
228
|
+
reviewers?: number;
|
|
229
|
+
}>;
|
|
230
|
+
/** @matrix strategy */
|
|
231
|
+
matrix?: {
|
|
232
|
+
dimensions: Record<string, string[]>;
|
|
233
|
+
include?: Record<string, string>[];
|
|
234
|
+
exclude?: Record<string, string>[];
|
|
235
|
+
};
|
|
236
|
+
/** @service containers */
|
|
237
|
+
services?: Array<{
|
|
238
|
+
name: string;
|
|
239
|
+
image: string;
|
|
240
|
+
env?: Record<string, string>;
|
|
241
|
+
ports?: string[];
|
|
242
|
+
}>;
|
|
243
|
+
/** @concurrency control */
|
|
244
|
+
concurrency?: {
|
|
245
|
+
group: string;
|
|
246
|
+
cancelInProgress?: boolean;
|
|
247
|
+
};
|
|
248
|
+
/** Extended CI/CD triggers from @trigger push/pull_request/dispatch/tag */
|
|
249
|
+
cicdTriggers?: Array<{
|
|
250
|
+
type: 'push' | 'pull_request' | 'schedule' | 'dispatch' | 'tag';
|
|
251
|
+
branches?: string[];
|
|
252
|
+
paths?: string[];
|
|
253
|
+
pathsIgnore?: string[];
|
|
254
|
+
pattern?: string;
|
|
255
|
+
types?: string[];
|
|
256
|
+
cron?: string;
|
|
257
|
+
inputs?: Record<string, {
|
|
258
|
+
description?: string;
|
|
259
|
+
required?: boolean;
|
|
260
|
+
default?: string;
|
|
261
|
+
type?: string;
|
|
262
|
+
}>;
|
|
263
|
+
}>;
|
|
264
|
+
/** @deploy target key=value pairs */
|
|
265
|
+
deploy?: Record<string, Record<string, unknown>>;
|
|
195
266
|
}
|
|
196
267
|
export interface JSDocPatternConfig {
|
|
197
268
|
name?: string;
|
|
@@ -331,6 +402,10 @@ export declare class JSDocParser {
|
|
|
331
402
|
* Parse @trigger tag using Chevrotain parser.
|
|
332
403
|
*/
|
|
333
404
|
private parseTriggerTag;
|
|
405
|
+
/**
|
|
406
|
+
* Parse a CI/CD-specific trigger type (push, pull_request, dispatch, tag, schedule).
|
|
407
|
+
*/
|
|
408
|
+
private parseCICDTrigger;
|
|
334
409
|
/**
|
|
335
410
|
* Parse @cancelOn tag using Chevrotain parser.
|
|
336
411
|
*/
|
|
@@ -339,6 +414,61 @@ export declare class JSDocParser {
|
|
|
339
414
|
* Parse @throttle tag using Chevrotain parser.
|
|
340
415
|
*/
|
|
341
416
|
private parseThrottleTag;
|
|
417
|
+
/**
|
|
418
|
+
* Dispatch a CI/CD or @deploy tag to the appropriate parser.
|
|
419
|
+
* Consolidates all deployment-related cases from the main switch.
|
|
420
|
+
*/
|
|
421
|
+
private handleDeploymentTag;
|
|
422
|
+
/**
|
|
423
|
+
* Parse @deploy tag.
|
|
424
|
+
* Format: @deploy <target> key=value key2="value with spaces" key3=123 key4=true
|
|
425
|
+
*
|
|
426
|
+
* Examples:
|
|
427
|
+
* @deploy github-actions action="actions/checkout@v4"
|
|
428
|
+
* @deploy inngest durableSteps=true framework="next" retries=3
|
|
429
|
+
* @deploy lambda memory=256 timeout=30
|
|
430
|
+
*
|
|
431
|
+
* Values are auto-coerced: "true"/"false" → boolean, numeric strings → number.
|
|
432
|
+
*/
|
|
433
|
+
private parseDeployTag;
|
|
434
|
+
/**
|
|
435
|
+
* Parse @secret tag.
|
|
436
|
+
* Format: @secret NAME - description
|
|
437
|
+
* Optional: @secret NAME scope="deploy" platform="github" - description
|
|
438
|
+
*/
|
|
439
|
+
private parseSecretTag;
|
|
440
|
+
/**
|
|
441
|
+
* Parse @cache tag.
|
|
442
|
+
* Format: @cache strategy [key="file"] [path="dir"]
|
|
443
|
+
*/
|
|
444
|
+
private parseCacheTag;
|
|
445
|
+
/**
|
|
446
|
+
* Parse @artifact tag.
|
|
447
|
+
* Format: @artifact name path="dist/" [retention=5]
|
|
448
|
+
*/
|
|
449
|
+
private parseArtifactTag;
|
|
450
|
+
/**
|
|
451
|
+
* Parse @environment tag.
|
|
452
|
+
* Format: @environment name [url="..."] [reviewers=N]
|
|
453
|
+
*/
|
|
454
|
+
private parseEnvironmentTag;
|
|
455
|
+
/**
|
|
456
|
+
* Parse @matrix tag.
|
|
457
|
+
* Format: @matrix dim1="val1,val2" dim2="val3,val4"
|
|
458
|
+
* Or: @matrix include dim1="val1" dim2="val2"
|
|
459
|
+
* Or: @matrix exclude dim1="val1" dim2="val2"
|
|
460
|
+
*/
|
|
461
|
+
private parseMatrixTag;
|
|
462
|
+
/**
|
|
463
|
+
* Parse @service tag.
|
|
464
|
+
* Format: @service name image="..." [env="K=V,K2=V2"] [ports="5432:5432"]
|
|
465
|
+
*/
|
|
466
|
+
private parseServiceTag;
|
|
467
|
+
/**
|
|
468
|
+
* Parse @concurrency tag.
|
|
469
|
+
* Format: @concurrency group="name" [cancel-in-progress=true]
|
|
470
|
+
*/
|
|
471
|
+
private parseConcurrencyTag;
|
|
342
472
|
/**
|
|
343
473
|
* Parse default value from string
|
|
344
474
|
*/
|
package/dist/jsdoc-parser.js
CHANGED
|
@@ -195,6 +195,10 @@ export class JSDocParser {
|
|
|
195
195
|
case 'step':
|
|
196
196
|
this.parseStepTag(tag, config, func, warnings);
|
|
197
197
|
break;
|
|
198
|
+
case 'deploy':
|
|
199
|
+
config.deploy = config.deploy || {};
|
|
200
|
+
this.parseDeployTag(tag, config.deploy);
|
|
201
|
+
break;
|
|
198
202
|
default:
|
|
199
203
|
// D: Context validation - tags that belong to other block types
|
|
200
204
|
if (tagName === 'param' || tagName === 'returns' || tagName === 'return') {
|
|
@@ -316,6 +320,18 @@ export class JSDocParser {
|
|
|
316
320
|
case 'throttle':
|
|
317
321
|
this.parseThrottleTag(tag, config, warnings);
|
|
318
322
|
break;
|
|
323
|
+
// ── CI/CD + deployment annotations (delegated to helper) ──
|
|
324
|
+
case 'deploy':
|
|
325
|
+
case 'secret':
|
|
326
|
+
case 'runner':
|
|
327
|
+
case 'cache':
|
|
328
|
+
case 'artifact':
|
|
329
|
+
case 'environment':
|
|
330
|
+
case 'matrix':
|
|
331
|
+
case 'service':
|
|
332
|
+
case 'concurrency':
|
|
333
|
+
this.handleDeploymentTag(tagName, tag, config, warnings);
|
|
334
|
+
break;
|
|
319
335
|
case 'param':
|
|
320
336
|
this.parseParamTag(tag, config, func, warnings);
|
|
321
337
|
break;
|
|
@@ -493,7 +509,7 @@ export class JSDocParser {
|
|
|
493
509
|
if (!result) {
|
|
494
510
|
return;
|
|
495
511
|
}
|
|
496
|
-
const { name, defaultValue, isOptional, scope, order, mergeStrategy, description } = result;
|
|
512
|
+
const { name, defaultValue, isOptional, scope, order, mergeStrategy, hidden, description } = result;
|
|
497
513
|
// Infer type from signature or scope callback return type
|
|
498
514
|
let type;
|
|
499
515
|
let tsType;
|
|
@@ -569,6 +585,7 @@ export class JSDocParser {
|
|
|
569
585
|
...(expression && { expression }),
|
|
570
586
|
...(scope && { scope }),
|
|
571
587
|
...(mergeStrategy && { mergeStrategy: mergeStrategy }),
|
|
588
|
+
...(hidden && { hidden }),
|
|
572
589
|
...(order !== undefined && { metadata: { order } }),
|
|
573
590
|
...(tsType && { tsType }),
|
|
574
591
|
};
|
|
@@ -583,7 +600,7 @@ export class JSDocParser {
|
|
|
583
600
|
if (!result) {
|
|
584
601
|
return;
|
|
585
602
|
}
|
|
586
|
-
const { name, scope, order, description } = result;
|
|
603
|
+
const { name, scope, order, hidden, description } = result;
|
|
587
604
|
// Infer type from return type or scope callback parameter
|
|
588
605
|
let type;
|
|
589
606
|
let tsType;
|
|
@@ -652,6 +669,7 @@ export class JSDocParser {
|
|
|
652
669
|
type,
|
|
653
670
|
label: description?.trim(),
|
|
654
671
|
...(scope && { scope }),
|
|
672
|
+
...(hidden && { hidden }),
|
|
655
673
|
...(order !== undefined && { metadata: { order } }),
|
|
656
674
|
...(tsType && { tsType }),
|
|
657
675
|
};
|
|
@@ -813,7 +831,7 @@ export class JSDocParser {
|
|
|
813
831
|
if (!result) {
|
|
814
832
|
return;
|
|
815
833
|
}
|
|
816
|
-
const { instanceId, nodeType, parentScope, label, expressions, portOrder, portLabel, minimized, pullExecution, size, position, color, icon, tags, } = result;
|
|
834
|
+
const { instanceId, nodeType, parentScope, label, expressions, portOrder, portLabel, minimized, pullExecution, size, position, color, icon, tags, job, environment, } = result;
|
|
817
835
|
// Capture source location from tag
|
|
818
836
|
const line = tag.getStartLineNumber();
|
|
819
837
|
// Build portConfigs from portOrder, portLabel, and expressions
|
|
@@ -861,6 +879,8 @@ export class JSDocParser {
|
|
|
861
879
|
...(tags && tags.length > 0 && { tags }),
|
|
862
880
|
...(size && { width: size.width, height: size.height }),
|
|
863
881
|
...(position && { x: position.x, y: position.y }),
|
|
882
|
+
...(job && { job }),
|
|
883
|
+
...(environment && { environment }),
|
|
864
884
|
sourceLocation: { line, column: 0 },
|
|
865
885
|
});
|
|
866
886
|
}
|
|
@@ -1017,9 +1037,26 @@ export class JSDocParser {
|
|
|
1017
1037
|
* Parse @trigger tag using Chevrotain parser.
|
|
1018
1038
|
*/
|
|
1019
1039
|
parseTriggerTag(tag, config, warnings) {
|
|
1020
|
-
const comment = tag.getCommentText() || '';
|
|
1040
|
+
const comment = (tag.getCommentText() || '').trim();
|
|
1041
|
+
// Check for CI/CD trigger types first (push, pull_request, dispatch, tag, schedule)
|
|
1042
|
+
const cicdTriggerMatch = comment.match(/^(push|pull_request|dispatch|tag|schedule)\b(.*)?$/);
|
|
1043
|
+
if (cicdTriggerMatch) {
|
|
1044
|
+
this.parseCICDTrigger(cicdTriggerMatch[1], cicdTriggerMatch[2]?.trim() || '', config, warnings);
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
// Also detect cron="..." as a schedule trigger for CI/CD
|
|
1048
|
+
const cronOnlyMatch = comment.match(/^cron\s*=\s*"([^"]+)"/);
|
|
1049
|
+
if (cronOnlyMatch && !comment.includes('event=')) {
|
|
1050
|
+
// Could be CI/CD schedule or existing FW cron — store both
|
|
1051
|
+
config.cicdTriggers = config.cicdTriggers || [];
|
|
1052
|
+
config.cicdTriggers.push({ type: 'schedule', cron: cronOnlyMatch[1] });
|
|
1053
|
+
}
|
|
1054
|
+
// Existing FW trigger parsing (event= and/or cron=)
|
|
1021
1055
|
const result = parseTriggerLine(`@trigger ${comment}`, warnings);
|
|
1022
1056
|
if (!result) {
|
|
1057
|
+
// If it was a CI/CD trigger we already handled, don't warn
|
|
1058
|
+
if (cicdTriggerMatch)
|
|
1059
|
+
return;
|
|
1023
1060
|
warnings.push(`Invalid @trigger format: @trigger ${comment}`);
|
|
1024
1061
|
return;
|
|
1025
1062
|
}
|
|
@@ -1030,6 +1067,43 @@ export class JSDocParser {
|
|
|
1030
1067
|
if (result.cron)
|
|
1031
1068
|
config.trigger.cron = result.cron;
|
|
1032
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Parse a CI/CD-specific trigger type (push, pull_request, dispatch, tag, schedule).
|
|
1072
|
+
*/
|
|
1073
|
+
parseCICDTrigger(type, rest, config, _warnings) {
|
|
1074
|
+
config.cicdTriggers = config.cicdTriggers || [];
|
|
1075
|
+
const triggerType = type;
|
|
1076
|
+
const trigger = { type: triggerType };
|
|
1077
|
+
// Parse key="value" pairs from rest
|
|
1078
|
+
const branchesMatch = rest.match(/branches\s*=\s*"([^"]+)"/);
|
|
1079
|
+
if (branchesMatch)
|
|
1080
|
+
trigger.branches = branchesMatch[1].split(',').map(b => b.trim());
|
|
1081
|
+
const pathsMatch = rest.match(/(?<![a-z])paths\s*=\s*"([^"]+)"/);
|
|
1082
|
+
if (pathsMatch)
|
|
1083
|
+
trigger.paths = pathsMatch[1].split(',').map(p => p.trim());
|
|
1084
|
+
const pathsIgnoreMatch = rest.match(/paths-ignore\s*=\s*"([^"]+)"/);
|
|
1085
|
+
if (pathsIgnoreMatch)
|
|
1086
|
+
trigger.pathsIgnore = pathsIgnoreMatch[1].split(',').map(p => p.trim());
|
|
1087
|
+
const typesMatch = rest.match(/types\s*=\s*"([^"]+)"/);
|
|
1088
|
+
if (typesMatch)
|
|
1089
|
+
trigger.types = typesMatch[1].split(',').map(t => t.trim());
|
|
1090
|
+
const patternMatch = rest.match(/pattern\s*=\s*"([^"]+)"/);
|
|
1091
|
+
if (patternMatch)
|
|
1092
|
+
trigger.pattern = patternMatch[1];
|
|
1093
|
+
const cronMatch = rest.match(/cron\s*=\s*"([^"]+)"/);
|
|
1094
|
+
if (cronMatch)
|
|
1095
|
+
trigger.cron = cronMatch[1];
|
|
1096
|
+
const inputsMatch = rest.match(/inputs\s*=\s*"([^"]+)"/);
|
|
1097
|
+
if (inputsMatch) {
|
|
1098
|
+
trigger.inputs = {};
|
|
1099
|
+
for (const input of inputsMatch[1].split(',')) {
|
|
1100
|
+
const name = input.trim();
|
|
1101
|
+
if (name)
|
|
1102
|
+
trigger.inputs[name] = {};
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
config.cicdTriggers.push(trigger);
|
|
1106
|
+
}
|
|
1033
1107
|
/**
|
|
1034
1108
|
* Parse @cancelOn tag using Chevrotain parser.
|
|
1035
1109
|
*/
|
|
@@ -1054,6 +1128,336 @@ export class JSDocParser {
|
|
|
1054
1128
|
}
|
|
1055
1129
|
config.throttle = result;
|
|
1056
1130
|
}
|
|
1131
|
+
// ── Deployment dispatch + @deploy parser ───────────────────────────
|
|
1132
|
+
/**
|
|
1133
|
+
* Dispatch a CI/CD or @deploy tag to the appropriate parser.
|
|
1134
|
+
* Consolidates all deployment-related cases from the main switch.
|
|
1135
|
+
*/
|
|
1136
|
+
handleDeploymentTag(tagName, tag, config, warnings) {
|
|
1137
|
+
switch (tagName) {
|
|
1138
|
+
case 'deploy':
|
|
1139
|
+
config.deploy = config.deploy || {};
|
|
1140
|
+
this.parseDeployTag(tag, config.deploy);
|
|
1141
|
+
break;
|
|
1142
|
+
case 'secret':
|
|
1143
|
+
this.parseSecretTag(tag, config, warnings);
|
|
1144
|
+
break;
|
|
1145
|
+
case 'runner': {
|
|
1146
|
+
const comment = (tag.getCommentText() || '').trim().replace(/^["']|["']$/g, '');
|
|
1147
|
+
if (comment)
|
|
1148
|
+
config.runner = comment;
|
|
1149
|
+
break;
|
|
1150
|
+
}
|
|
1151
|
+
case 'cache':
|
|
1152
|
+
this.parseCacheTag(tag, config, warnings);
|
|
1153
|
+
break;
|
|
1154
|
+
case 'artifact':
|
|
1155
|
+
this.parseArtifactTag(tag, config, warnings);
|
|
1156
|
+
break;
|
|
1157
|
+
case 'environment':
|
|
1158
|
+
this.parseEnvironmentTag(tag, config, warnings);
|
|
1159
|
+
break;
|
|
1160
|
+
case 'matrix':
|
|
1161
|
+
this.parseMatrixTag(tag, config, warnings);
|
|
1162
|
+
break;
|
|
1163
|
+
case 'service':
|
|
1164
|
+
this.parseServiceTag(tag, config, warnings);
|
|
1165
|
+
break;
|
|
1166
|
+
case 'concurrency':
|
|
1167
|
+
this.parseConcurrencyTag(tag, config, warnings);
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Parse @deploy tag.
|
|
1173
|
+
* Format: @deploy <target> key=value key2="value with spaces" key3=123 key4=true
|
|
1174
|
+
*
|
|
1175
|
+
* Examples:
|
|
1176
|
+
* @deploy github-actions action="actions/checkout@v4"
|
|
1177
|
+
* @deploy inngest durableSteps=true framework="next" retries=3
|
|
1178
|
+
* @deploy lambda memory=256 timeout=30
|
|
1179
|
+
*
|
|
1180
|
+
* Values are auto-coerced: "true"/"false" → boolean, numeric strings → number.
|
|
1181
|
+
*/
|
|
1182
|
+
parseDeployTag(tag, deployMap) {
|
|
1183
|
+
const text = (tag.getCommentText() || '').trim();
|
|
1184
|
+
if (!text)
|
|
1185
|
+
return;
|
|
1186
|
+
// Split target name from remaining key=value pairs
|
|
1187
|
+
const spaceIdx = text.indexOf(' ');
|
|
1188
|
+
const targetName = spaceIdx === -1 ? text : text.substring(0, spaceIdx);
|
|
1189
|
+
const rest = spaceIdx === -1 ? '' : text.substring(spaceIdx + 1);
|
|
1190
|
+
if (!targetName)
|
|
1191
|
+
return;
|
|
1192
|
+
if (!deployMap[targetName])
|
|
1193
|
+
deployMap[targetName] = {};
|
|
1194
|
+
if (!rest)
|
|
1195
|
+
return;
|
|
1196
|
+
// Parse key=value and key="value with spaces" pairs
|
|
1197
|
+
const kvRegex = /(\w[\w-]*)\s*=\s*(?:"([^"]*)"|([\S]+))/g;
|
|
1198
|
+
let match;
|
|
1199
|
+
while ((match = kvRegex.exec(rest)) !== null) {
|
|
1200
|
+
const key = match[1];
|
|
1201
|
+
const quotedVal = match[2]; // captured from "..."
|
|
1202
|
+
const bareVal = match[3]; // captured from unquoted
|
|
1203
|
+
if (quotedVal !== undefined) {
|
|
1204
|
+
// Quoted values: check if comma-separated list → string[]
|
|
1205
|
+
if (quotedVal.includes(',')) {
|
|
1206
|
+
deployMap[targetName][key] = quotedVal.split(',').map(v => v.trim());
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
deployMap[targetName][key] = quotedVal;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
else if (bareVal !== undefined) {
|
|
1213
|
+
// Bare values: auto-coerce boolean and number
|
|
1214
|
+
if (bareVal === 'true') {
|
|
1215
|
+
deployMap[targetName][key] = true;
|
|
1216
|
+
}
|
|
1217
|
+
else if (bareVal === 'false') {
|
|
1218
|
+
deployMap[targetName][key] = false;
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1221
|
+
const num = Number(bareVal);
|
|
1222
|
+
deployMap[targetName][key] = isNaN(num) ? bareVal : num;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
// ── CI/CD annotation parsers ───────────────────────────────────────
|
|
1228
|
+
/**
|
|
1229
|
+
* Parse @secret tag.
|
|
1230
|
+
* Format: @secret NAME - description
|
|
1231
|
+
* Optional: @secret NAME scope="deploy" platform="github" - description
|
|
1232
|
+
*/
|
|
1233
|
+
parseSecretTag(_tag, config, warnings) {
|
|
1234
|
+
const comment = (_tag.getCommentText() || '').trim();
|
|
1235
|
+
if (!comment) {
|
|
1236
|
+
warnings.push('Empty @secret tag. Expected: @secret SECRET_NAME - description');
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
// Parse: NAME [key=value ...] [- description]
|
|
1240
|
+
const match = comment.match(/^(\S+)(.*?)(?:\s+-\s+(.*))?$/);
|
|
1241
|
+
if (!match) {
|
|
1242
|
+
warnings.push(`Invalid @secret format: @secret ${comment}`);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
const name = match[1];
|
|
1246
|
+
const attrs = match[2] || '';
|
|
1247
|
+
const description = match[3]?.trim();
|
|
1248
|
+
const secret = { name };
|
|
1249
|
+
if (description)
|
|
1250
|
+
secret.description = description;
|
|
1251
|
+
// Parse optional key=value attributes
|
|
1252
|
+
const scopeMatch = attrs.match(/scope\s*=\s*"([^"]+)"/);
|
|
1253
|
+
if (scopeMatch)
|
|
1254
|
+
secret.scope = scopeMatch[1];
|
|
1255
|
+
const platformMatch = attrs.match(/platform\s*=\s*"([^"]+)"/);
|
|
1256
|
+
if (platformMatch) {
|
|
1257
|
+
const validPlatforms = ['github', 'gitlab', 'all'];
|
|
1258
|
+
const p = platformMatch[1];
|
|
1259
|
+
if (validPlatforms.includes(p)) {
|
|
1260
|
+
secret.platform = p;
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
warnings.push(`Invalid @secret platform "${platformMatch[1]}". Must be: github, gitlab, or all`);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
config.secrets = config.secrets || [];
|
|
1267
|
+
config.secrets.push(secret);
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Parse @cache tag.
|
|
1271
|
+
* Format: @cache strategy [key="file"] [path="dir"]
|
|
1272
|
+
*/
|
|
1273
|
+
parseCacheTag(_tag, config, warnings) {
|
|
1274
|
+
const comment = (_tag.getCommentText() || '').trim();
|
|
1275
|
+
if (!comment) {
|
|
1276
|
+
warnings.push('Empty @cache tag. Expected: @cache npm key="package-lock.json"');
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
const parts = comment.match(/^(\S+)(.*)?$/);
|
|
1280
|
+
if (!parts) {
|
|
1281
|
+
warnings.push(`Invalid @cache format: @cache ${comment}`);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
const cache = { strategy: parts[1] };
|
|
1285
|
+
const rest = parts[2] || '';
|
|
1286
|
+
const keyMatch = rest.match(/key\s*=\s*"([^"]+)"/);
|
|
1287
|
+
if (keyMatch)
|
|
1288
|
+
cache.key = keyMatch[1];
|
|
1289
|
+
const pathMatch = rest.match(/path\s*=\s*"([^"]+)"/);
|
|
1290
|
+
if (pathMatch)
|
|
1291
|
+
cache.path = pathMatch[1];
|
|
1292
|
+
config.caches = config.caches || [];
|
|
1293
|
+
config.caches.push(cache);
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Parse @artifact tag.
|
|
1297
|
+
* Format: @artifact name path="dist/" [retention=5]
|
|
1298
|
+
*/
|
|
1299
|
+
parseArtifactTag(_tag, config, warnings) {
|
|
1300
|
+
const comment = (_tag.getCommentText() || '').trim();
|
|
1301
|
+
if (!comment) {
|
|
1302
|
+
warnings.push('Empty @artifact tag. Expected: @artifact name path="dist/"');
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
const nameMatch = comment.match(/^(\S+)/);
|
|
1306
|
+
if (!nameMatch) {
|
|
1307
|
+
warnings.push(`Invalid @artifact format: @artifact ${comment}`);
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
const pathMatch = comment.match(/path\s*=\s*"([^"]+)"/);
|
|
1311
|
+
if (!pathMatch) {
|
|
1312
|
+
warnings.push(`@artifact requires path="...": @artifact ${comment}`);
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
const artifact = {
|
|
1316
|
+
name: nameMatch[1],
|
|
1317
|
+
path: pathMatch[1],
|
|
1318
|
+
};
|
|
1319
|
+
const retentionMatch = comment.match(/retention\s*=\s*(\d+)/);
|
|
1320
|
+
if (retentionMatch)
|
|
1321
|
+
artifact.retention = parseInt(retentionMatch[1], 10);
|
|
1322
|
+
config.artifacts = config.artifacts || [];
|
|
1323
|
+
config.artifacts.push(artifact);
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Parse @environment tag.
|
|
1327
|
+
* Format: @environment name [url="..."] [reviewers=N]
|
|
1328
|
+
*/
|
|
1329
|
+
parseEnvironmentTag(_tag, config, warnings) {
|
|
1330
|
+
const comment = (_tag.getCommentText() || '').trim();
|
|
1331
|
+
if (!comment) {
|
|
1332
|
+
warnings.push('Empty @environment tag. Expected: @environment production url="https://app.com"');
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
const nameMatch = comment.match(/^(\S+)/);
|
|
1336
|
+
if (!nameMatch) {
|
|
1337
|
+
warnings.push(`Invalid @environment format: @environment ${comment}`);
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
const env = { name: nameMatch[1] };
|
|
1341
|
+
const urlMatch = comment.match(/url\s*=\s*"([^"]+)"/);
|
|
1342
|
+
if (urlMatch)
|
|
1343
|
+
env.url = urlMatch[1];
|
|
1344
|
+
const reviewersMatch = comment.match(/reviewers\s*=\s*(\d+)/);
|
|
1345
|
+
if (reviewersMatch)
|
|
1346
|
+
env.reviewers = parseInt(reviewersMatch[1], 10);
|
|
1347
|
+
config.environments = config.environments || [];
|
|
1348
|
+
config.environments.push(env);
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Parse @matrix tag.
|
|
1352
|
+
* Format: @matrix dim1="val1,val2" dim2="val3,val4"
|
|
1353
|
+
* Or: @matrix include dim1="val1" dim2="val2"
|
|
1354
|
+
* Or: @matrix exclude dim1="val1" dim2="val2"
|
|
1355
|
+
*/
|
|
1356
|
+
parseMatrixTag(_tag, config, warnings) {
|
|
1357
|
+
const comment = (_tag.getCommentText() || '').trim();
|
|
1358
|
+
if (!comment) {
|
|
1359
|
+
warnings.push('Empty @matrix tag. Expected: @matrix node="18,20,22" os="ubuntu-latest"');
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
config.matrix = config.matrix || { dimensions: {} };
|
|
1363
|
+
// Check for include/exclude prefix
|
|
1364
|
+
const isInclude = comment.startsWith('include ');
|
|
1365
|
+
const isExclude = comment.startsWith('exclude ');
|
|
1366
|
+
if (isInclude || isExclude) {
|
|
1367
|
+
const rest = comment.slice(isInclude ? 8 : 8);
|
|
1368
|
+
const entry = {};
|
|
1369
|
+
const kvRegex = /(\w[\w-]*)\s*=\s*"([^"]+)"/g;
|
|
1370
|
+
let m;
|
|
1371
|
+
while ((m = kvRegex.exec(rest)) !== null) {
|
|
1372
|
+
entry[m[1]] = m[2];
|
|
1373
|
+
}
|
|
1374
|
+
if (Object.keys(entry).length > 0) {
|
|
1375
|
+
if (isInclude) {
|
|
1376
|
+
config.matrix.include = config.matrix.include || [];
|
|
1377
|
+
config.matrix.include.push(entry);
|
|
1378
|
+
}
|
|
1379
|
+
else {
|
|
1380
|
+
config.matrix.exclude = config.matrix.exclude || [];
|
|
1381
|
+
config.matrix.exclude.push(entry);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
// Regular dimensions: key="val1,val2"
|
|
1387
|
+
const kvRegex = /(\w[\w-]*)\s*=\s*"([^"]+)"/g;
|
|
1388
|
+
let m;
|
|
1389
|
+
while ((m = kvRegex.exec(comment)) !== null) {
|
|
1390
|
+
config.matrix.dimensions[m[1]] = m[2].split(',').map(v => v.trim());
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Parse @service tag.
|
|
1395
|
+
* Format: @service name image="..." [env="K=V,K2=V2"] [ports="5432:5432"]
|
|
1396
|
+
*/
|
|
1397
|
+
parseServiceTag(_tag, config, warnings) {
|
|
1398
|
+
const comment = (_tag.getCommentText() || '').trim();
|
|
1399
|
+
if (!comment) {
|
|
1400
|
+
warnings.push('Empty @service tag. Expected: @service postgres image="postgres:16"');
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
const nameMatch = comment.match(/^(\S+)/);
|
|
1404
|
+
if (!nameMatch) {
|
|
1405
|
+
warnings.push(`Invalid @service format: @service ${comment}`);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
const imageMatch = comment.match(/image\s*=\s*"([^"]+)"/);
|
|
1409
|
+
if (!imageMatch) {
|
|
1410
|
+
warnings.push(`@service requires image="...": @service ${comment}`);
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
const svc = {
|
|
1414
|
+
name: nameMatch[1],
|
|
1415
|
+
image: imageMatch[1],
|
|
1416
|
+
};
|
|
1417
|
+
const envMatch = comment.match(/env\s*=\s*"([^"]+)"/);
|
|
1418
|
+
if (envMatch) {
|
|
1419
|
+
svc.env = {};
|
|
1420
|
+
for (const pair of envMatch[1].split(',')) {
|
|
1421
|
+
const [k, ...vParts] = pair.split('=');
|
|
1422
|
+
if (k && vParts.length > 0)
|
|
1423
|
+
svc.env[k.trim()] = vParts.join('=').trim();
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
const portsMatch = comment.match(/ports\s*=\s*"([^"]+)"/);
|
|
1427
|
+
if (portsMatch) {
|
|
1428
|
+
svc.ports = portsMatch[1].split(',').map(p => p.trim());
|
|
1429
|
+
}
|
|
1430
|
+
config.services = config.services || [];
|
|
1431
|
+
config.services.push(svc);
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Parse @concurrency tag.
|
|
1435
|
+
* Format: @concurrency group="name" [cancel-in-progress=true]
|
|
1436
|
+
*/
|
|
1437
|
+
parseConcurrencyTag(_tag, config, warnings) {
|
|
1438
|
+
const comment = (_tag.getCommentText() || '').trim();
|
|
1439
|
+
if (!comment) {
|
|
1440
|
+
warnings.push('Empty @concurrency tag. Expected: @concurrency group="deploy"');
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const groupMatch = comment.match(/group\s*=\s*"([^"]+)"/);
|
|
1444
|
+
if (!groupMatch) {
|
|
1445
|
+
// Try bare word: @concurrency deploy
|
|
1446
|
+
const bareMatch = comment.match(/^(\S+)/);
|
|
1447
|
+
if (bareMatch) {
|
|
1448
|
+
config.concurrency = { group: bareMatch[1], cancelInProgress: false };
|
|
1449
|
+
}
|
|
1450
|
+
else {
|
|
1451
|
+
warnings.push(`Invalid @concurrency format: @concurrency ${comment}`);
|
|
1452
|
+
}
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const cancelMatch = comment.match(/cancel-in-progress\s*=\s*(true|false)/);
|
|
1456
|
+
config.concurrency = {
|
|
1457
|
+
group: groupMatch[1],
|
|
1458
|
+
cancelInProgress: cancelMatch ? cancelMatch[1] === 'true' : false,
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1057
1461
|
/**
|
|
1058
1462
|
* Parse default value from string
|
|
1059
1463
|
*/
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Flow Weaver Marketplace — discover, install, and publish reusable
|
|
5
5
|
* node types, workflows, and patterns via npm.
|
|
6
6
|
*/
|
|
7
|
-
export type { TMarketplaceManifest, TManifestNodeType, TManifestWorkflow, TManifestPattern, TManifestPort, TValidationIssue, TValidationSeverity, TPackageValidationResult, TMarketplacePackageInfo, TInstalledPackage, TMarketInitConfig, } from './types.js';
|
|
7
|
+
export type { TMarketplaceManifest, TManifestNodeType, TManifestWorkflow, TManifestPattern, TManifestExportTarget, TManifestPort, TValidationIssue, TValidationSeverity, TPackageValidationResult, TMarketplacePackageInfo, TInstalledPackage, TMarketInitConfig, } from './types.js';
|
|
8
8
|
export { generateManifest, writeManifest, readManifest, type GenerateManifestOptions, type GenerateManifestResult, } from './manifest.js';
|
|
9
9
|
export { validatePackage } from './validator.js';
|
|
10
10
|
export { searchPackages, listInstalledPackages, getInstalledPackageManifest, type SearchOptions, } from './registry.js';
|