@synergenius/flow-weaver 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +41 -2
  2. package/dist/api/validate.js +8 -2
  3. package/dist/ast/types.d.ts +120 -0
  4. package/dist/chevrotain-parser/node-parser.d.ts +4 -0
  5. package/dist/chevrotain-parser/node-parser.js +41 -1
  6. package/dist/chevrotain-parser/port-parser.d.ts +1 -0
  7. package/dist/chevrotain-parser/port-parser.js +22 -2
  8. package/dist/chevrotain-parser/tokens.d.ts +3 -0
  9. package/dist/chevrotain-parser/tokens.js +15 -0
  10. package/dist/cli/commands/export.js +25 -38
  11. package/dist/cli/flow-weaver.mjs +63703 -54297
  12. package/dist/cli/templates/index.js +9 -0
  13. package/dist/cli/templates/workflows/cicd-docker.d.ts +9 -0
  14. package/dist/cli/templates/workflows/cicd-docker.js +110 -0
  15. package/dist/cli/templates/workflows/cicd-matrix.d.ts +9 -0
  16. package/dist/cli/templates/workflows/cicd-matrix.js +112 -0
  17. package/dist/cli/templates/workflows/cicd-multi-env.d.ts +9 -0
  18. package/dist/cli/templates/workflows/cicd-multi-env.js +118 -0
  19. package/dist/cli/templates/workflows/cicd-test-deploy.d.ts +11 -0
  20. package/dist/cli/templates/workflows/cicd-test-deploy.js +149 -0
  21. package/dist/constants.js +7 -0
  22. package/dist/deployment/index.d.ts +14 -7
  23. package/dist/deployment/index.js +29 -17
  24. package/dist/deployment/targets/base.d.ts +27 -2
  25. package/dist/deployment/targets/base.js +38 -6
  26. package/dist/deployment/targets/cicd-base.d.ts +111 -0
  27. package/dist/deployment/targets/cicd-base.js +357 -0
  28. package/dist/deployment/targets/cloudflare.d.ts +6 -0
  29. package/dist/deployment/targets/cloudflare.js +3 -0
  30. package/dist/deployment/targets/github-actions.d.ts +54 -0
  31. package/dist/deployment/targets/github-actions.js +366 -0
  32. package/dist/deployment/targets/gitlab-ci.d.ts +65 -0
  33. package/dist/deployment/targets/gitlab-ci.js +374 -0
  34. package/dist/deployment/targets/inngest.d.ts +25 -0
  35. package/dist/deployment/targets/inngest.js +10 -1
  36. package/dist/deployment/targets/lambda.d.ts +17 -0
  37. package/dist/deployment/targets/lambda.js +5 -0
  38. package/dist/deployment/targets/vercel.d.ts +16 -0
  39. package/dist/deployment/targets/vercel.js +5 -0
  40. package/dist/diagram/geometry.js +13 -5
  41. package/dist/export/index.d.ts +13 -9
  42. package/dist/export/index.js +129 -997
  43. package/dist/generated-version.d.ts +1 -1
  44. package/dist/generated-version.js +1 -1
  45. package/dist/jsdoc-parser.d.ts +130 -0
  46. package/dist/jsdoc-parser.js +408 -4
  47. package/dist/marketplace/index.d.ts +1 -1
  48. package/dist/marketplace/types.d.ts +13 -0
  49. package/dist/marketplace/validator.js +21 -2
  50. package/dist/mcp/tools-export.js +56 -14
  51. package/dist/parser.js +28 -1
  52. package/dist/validation/cicd-detection.d.ts +33 -0
  53. package/dist/validation/cicd-detection.js +76 -0
  54. package/dist/validation/cicd-rules.d.ts +62 -0
  55. package/dist/validation/cicd-rules.js +284 -0
  56. package/docs/reference/scaffold.md +4 -0
  57. package/package.json +4 -3
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.13.2";
1
+ export declare const VERSION = "0.14.0";
2
2
  //# sourceMappingURL=generated-version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.ts — do not edit manually
2
- export const VERSION = '0.13.2';
2
+ export const VERSION = '0.14.0';
3
3
  //# sourceMappingURL=generated-version.js.map
@@ -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
  */
@@ -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';