webpipe-js 2.0.11 → 2.0.13

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/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  formatPipelineStep: () => formatPipelineStep,
26
26
  formatStepConfig: () => formatStepConfig,
27
27
  formatTag: () => formatTag,
28
+ formatTagExpr: () => formatTagExpr,
28
29
  formatTags: () => formatTags,
29
30
  formatWhen: () => formatWhen,
30
31
  getPipelineRanges: () => getPipelineRanges,
@@ -492,10 +493,39 @@ var Parser = class {
492
493
  this.expect(":");
493
494
  this.skipInlineSpaces();
494
495
  const { config, configType } = this.parseStepConfig();
495
- const tags = this.parseTags();
496
+ const condition = this.parseStepCondition();
496
497
  const parsedJoinTargets = name === "join" ? this.parseJoinTaskNames(config) : void 0;
497
498
  this.skipWhitespaceOnly();
498
- return { kind: "Regular", name, config, configType, tags, parsedJoinTargets };
499
+ return { kind: "Regular", name, config, configType, condition, parsedJoinTargets };
500
+ }
501
+ /**
502
+ * Parse optional step condition (tag expression after the config)
503
+ * Supports:
504
+ * - @tag (single tag)
505
+ * - @tag @tag2 (implicit AND for backwards compatibility)
506
+ * - @tag and @tag2 (explicit AND)
507
+ * - @tag or @tag2 (explicit OR)
508
+ * - (@tag or @tag2) and @tag3 (grouping)
509
+ */
510
+ parseStepCondition() {
511
+ this.skipInlineSpaces();
512
+ if (this.cur() !== "@") {
513
+ return void 0;
514
+ }
515
+ let expr = this.parseTagExpr();
516
+ while (true) {
517
+ this.skipInlineSpaces();
518
+ const ch = this.cur();
519
+ if (ch === "\n" || ch === "\r" || ch === "#" || this.text.startsWith("//", this.pos)) {
520
+ break;
521
+ }
522
+ if (ch !== "@") {
523
+ break;
524
+ }
525
+ const nextTag = this.parseTag();
526
+ expr = { kind: "And", left: expr, right: { kind: "Tag", tag: nextTag } };
527
+ }
528
+ return expr;
499
529
  }
500
530
  /**
501
531
  * Pre-parse join config into task names at parse time.
@@ -609,11 +639,69 @@ var Parser = class {
609
639
  this.skipSpaces();
610
640
  this.expect("case");
611
641
  this.skipInlineSpaces();
612
- const tag = this.parseTag();
642
+ const condition = this.parseTagExpr();
643
+ this.skipInlineSpaces();
613
644
  this.expect(":");
614
645
  this.skipSpaces();
615
646
  const pipeline = this.parseIfPipeline("case", "default:", "end");
616
- return { tag, pipeline };
647
+ return { condition, pipeline };
648
+ }
649
+ /**
650
+ * Parse a tag expression with boolean operators (and, or) and grouping
651
+ * Grammar (precedence: AND > OR):
652
+ * tag_expr := or_expr
653
+ * or_expr := and_expr ("or" and_expr)*
654
+ * and_expr := primary ("and" primary)*
655
+ * primary := tag | "(" tag_expr ")"
656
+ */
657
+ parseTagExpr() {
658
+ return this.parseOrExpr();
659
+ }
660
+ parseOrExpr() {
661
+ let left = this.parseAndExpr();
662
+ while (true) {
663
+ const saved = this.pos;
664
+ this.skipInlineSpaces();
665
+ if (this.text.startsWith("or", this.pos) && !this.isIdentCont(this.text[this.pos + 2] || "")) {
666
+ this.pos += 2;
667
+ this.skipInlineSpaces();
668
+ const right = this.parseAndExpr();
669
+ left = { kind: "Or", left, right };
670
+ } else {
671
+ this.pos = saved;
672
+ break;
673
+ }
674
+ }
675
+ return left;
676
+ }
677
+ parseAndExpr() {
678
+ let left = this.parseTagPrimary();
679
+ while (true) {
680
+ const saved = this.pos;
681
+ this.skipInlineSpaces();
682
+ if (this.text.startsWith("and", this.pos) && !this.isIdentCont(this.text[this.pos + 3] || "")) {
683
+ this.pos += 3;
684
+ this.skipInlineSpaces();
685
+ const right = this.parseTagPrimary();
686
+ left = { kind: "And", left, right };
687
+ } else {
688
+ this.pos = saved;
689
+ break;
690
+ }
691
+ }
692
+ return left;
693
+ }
694
+ parseTagPrimary() {
695
+ if (this.cur() === "(") {
696
+ this.pos++;
697
+ this.skipInlineSpaces();
698
+ const expr = this.parseTagExpr();
699
+ this.skipInlineSpaces();
700
+ this.expect(")");
701
+ return expr;
702
+ }
703
+ const tag = this.parseTag();
704
+ return { kind: "Tag", tag };
617
705
  }
618
706
  parseIfPipeline(...stopKeywords) {
619
707
  const steps = [];
@@ -1136,8 +1224,8 @@ function formatConfigValue(value) {
1136
1224
  function formatPipelineStep(step, indent = " ") {
1137
1225
  if (step.kind === "Regular") {
1138
1226
  const configPart = formatStepConfig(step.config, step.configType);
1139
- const tagsPart = step.tags.length > 0 ? " " + formatTags(step.tags) : "";
1140
- return `${indent}|> ${step.name}: ${configPart}${tagsPart}`;
1227
+ const conditionPart = step.condition ? " " + formatTagExpr(step.condition) : "";
1228
+ return `${indent}|> ${step.name}: ${configPart}${conditionPart}`;
1141
1229
  } else if (step.kind === "Result") {
1142
1230
  const lines = [`${indent}|> result`];
1143
1231
  step.branches.forEach((branch) => {
@@ -1167,7 +1255,7 @@ function formatPipelineStep(step, indent = " ") {
1167
1255
  } else if (step.kind === "Dispatch") {
1168
1256
  const lines = [`${indent}|> dispatch`];
1169
1257
  step.branches.forEach((branch) => {
1170
- lines.push(`${indent} case ${formatTag(branch.tag)}:`);
1258
+ lines.push(`${indent} case ${formatTagExpr(branch.condition)}:`);
1171
1259
  branch.pipeline.steps.forEach((branchStep) => {
1172
1260
  lines.push(formatPipelineStep(branchStep, indent + " "));
1173
1261
  });
@@ -1206,6 +1294,19 @@ function formatTag(tag) {
1206
1294
  const args = tag.args.length > 0 ? `(${tag.args.join(",")})` : "";
1207
1295
  return `@${negation}${tag.name}${args}`;
1208
1296
  }
1297
+ function formatTagExpr(expr) {
1298
+ switch (expr.kind) {
1299
+ case "Tag":
1300
+ return formatTag(expr.tag);
1301
+ case "And": {
1302
+ const leftStr = expr.left.kind === "Or" ? `(${formatTagExpr(expr.left)})` : formatTagExpr(expr.left);
1303
+ const rightStr = expr.right.kind === "Or" ? `(${formatTagExpr(expr.right)})` : formatTagExpr(expr.right);
1304
+ return `${leftStr} and ${rightStr}`;
1305
+ }
1306
+ case "Or":
1307
+ return `${formatTagExpr(expr.left)} or ${formatTagExpr(expr.right)}`;
1308
+ }
1309
+ }
1209
1310
  function formatPipelineRef(ref) {
1210
1311
  if (ref.kind === "Named") {
1211
1312
  return [` |> pipeline: ${ref.name}`];
@@ -1234,6 +1335,7 @@ function formatWhen(when) {
1234
1335
  formatPipelineStep,
1235
1336
  formatStepConfig,
1236
1337
  formatTag,
1338
+ formatTagExpr,
1237
1339
  formatTags,
1238
1340
  formatWhen,
1239
1341
  getPipelineRanges,
package/dist/index.d.cts CHANGED
@@ -93,12 +93,25 @@ interface Tag {
93
93
  negated: boolean;
94
94
  args: string[];
95
95
  }
96
+ /** A boolean expression of tags for dispatch routing */
97
+ type TagExpr = {
98
+ kind: 'Tag';
99
+ tag: Tag;
100
+ } | {
101
+ kind: 'And';
102
+ left: TagExpr;
103
+ right: TagExpr;
104
+ } | {
105
+ kind: 'Or';
106
+ left: TagExpr;
107
+ right: TagExpr;
108
+ };
96
109
  type PipelineStep = {
97
110
  kind: 'Regular';
98
111
  name: string;
99
112
  config: string;
100
113
  configType: ConfigType;
101
- tags: Tag[];
114
+ condition?: TagExpr;
102
115
  parsedJoinTargets?: string[];
103
116
  } | {
104
117
  kind: 'Result';
@@ -118,7 +131,7 @@ type PipelineStep = {
118
131
  pipeline: Pipeline;
119
132
  };
120
133
  interface DispatchBranch {
121
- tag: Tag;
134
+ condition: TagExpr;
122
135
  pipeline: Pipeline;
123
136
  }
124
137
  interface ResultBranch {
@@ -209,7 +222,8 @@ declare function formatPipelineStep(step: PipelineStep, indent?: string): string
209
222
  declare function formatStepConfig(config: string, configType: ConfigType): string;
210
223
  declare function formatTags(tags: Tag[]): string;
211
224
  declare function formatTag(tag: Tag): string;
225
+ declare function formatTagExpr(expr: TagExpr): string;
212
226
  declare function formatPipelineRef(ref: PipelineRef): string[];
213
227
  declare function formatWhen(when: When): string;
214
228
 
215
- export { type Comment, type Condition, type Config, type ConfigProperty, type ConfigType, type ConfigValue, type Describe, type DiagnosticSeverity, type DispatchBranch, type GraphQLSchema, type It, type Mock, type MutationResolver, type NamedPipeline, type ParseDiagnostic, type Pipeline, type PipelineRef, type PipelineStep, type Program, type QueryResolver, type ResultBranch, type ResultBranchType, type Route, type Tag, type Variable, type When, formatConfigValue, formatPipelineRef, formatPipelineStep, formatStepConfig, formatTag, formatTags, formatWhen, getPipelineRanges, getVariableRanges, parseProgram, parseProgramWithDiagnostics, prettyPrint, printComment, printCondition, printConfig, printDescribe, printGraphQLSchema, printMock, printMutationResolver, printPipeline, printQueryResolver, printRoute, printTest, printVariable };
229
+ export { type Comment, type Condition, type Config, type ConfigProperty, type ConfigType, type ConfigValue, type Describe, type DiagnosticSeverity, type DispatchBranch, type GraphQLSchema, type It, type Mock, type MutationResolver, type NamedPipeline, type ParseDiagnostic, type Pipeline, type PipelineRef, type PipelineStep, type Program, type QueryResolver, type ResultBranch, type ResultBranchType, type Route, type Tag, type TagExpr, type Variable, type When, formatConfigValue, formatPipelineRef, formatPipelineStep, formatStepConfig, formatTag, formatTagExpr, formatTags, formatWhen, getPipelineRanges, getVariableRanges, parseProgram, parseProgramWithDiagnostics, prettyPrint, printComment, printCondition, printConfig, printDescribe, printGraphQLSchema, printMock, printMutationResolver, printPipeline, printQueryResolver, printRoute, printTest, printVariable };
package/dist/index.d.ts CHANGED
@@ -93,12 +93,25 @@ interface Tag {
93
93
  negated: boolean;
94
94
  args: string[];
95
95
  }
96
+ /** A boolean expression of tags for dispatch routing */
97
+ type TagExpr = {
98
+ kind: 'Tag';
99
+ tag: Tag;
100
+ } | {
101
+ kind: 'And';
102
+ left: TagExpr;
103
+ right: TagExpr;
104
+ } | {
105
+ kind: 'Or';
106
+ left: TagExpr;
107
+ right: TagExpr;
108
+ };
96
109
  type PipelineStep = {
97
110
  kind: 'Regular';
98
111
  name: string;
99
112
  config: string;
100
113
  configType: ConfigType;
101
- tags: Tag[];
114
+ condition?: TagExpr;
102
115
  parsedJoinTargets?: string[];
103
116
  } | {
104
117
  kind: 'Result';
@@ -118,7 +131,7 @@ type PipelineStep = {
118
131
  pipeline: Pipeline;
119
132
  };
120
133
  interface DispatchBranch {
121
- tag: Tag;
134
+ condition: TagExpr;
122
135
  pipeline: Pipeline;
123
136
  }
124
137
  interface ResultBranch {
@@ -209,7 +222,8 @@ declare function formatPipelineStep(step: PipelineStep, indent?: string): string
209
222
  declare function formatStepConfig(config: string, configType: ConfigType): string;
210
223
  declare function formatTags(tags: Tag[]): string;
211
224
  declare function formatTag(tag: Tag): string;
225
+ declare function formatTagExpr(expr: TagExpr): string;
212
226
  declare function formatPipelineRef(ref: PipelineRef): string[];
213
227
  declare function formatWhen(when: When): string;
214
228
 
215
- export { type Comment, type Condition, type Config, type ConfigProperty, type ConfigType, type ConfigValue, type Describe, type DiagnosticSeverity, type DispatchBranch, type GraphQLSchema, type It, type Mock, type MutationResolver, type NamedPipeline, type ParseDiagnostic, type Pipeline, type PipelineRef, type PipelineStep, type Program, type QueryResolver, type ResultBranch, type ResultBranchType, type Route, type Tag, type Variable, type When, formatConfigValue, formatPipelineRef, formatPipelineStep, formatStepConfig, formatTag, formatTags, formatWhen, getPipelineRanges, getVariableRanges, parseProgram, parseProgramWithDiagnostics, prettyPrint, printComment, printCondition, printConfig, printDescribe, printGraphQLSchema, printMock, printMutationResolver, printPipeline, printQueryResolver, printRoute, printTest, printVariable };
229
+ export { type Comment, type Condition, type Config, type ConfigProperty, type ConfigType, type ConfigValue, type Describe, type DiagnosticSeverity, type DispatchBranch, type GraphQLSchema, type It, type Mock, type MutationResolver, type NamedPipeline, type ParseDiagnostic, type Pipeline, type PipelineRef, type PipelineStep, type Program, type QueryResolver, type ResultBranch, type ResultBranchType, type Route, type Tag, type TagExpr, type Variable, type When, formatConfigValue, formatPipelineRef, formatPipelineStep, formatStepConfig, formatTag, formatTagExpr, formatTags, formatWhen, getPipelineRanges, getVariableRanges, parseProgram, parseProgramWithDiagnostics, prettyPrint, printComment, printCondition, printConfig, printDescribe, printGraphQLSchema, printMock, printMutationResolver, printPipeline, printQueryResolver, printRoute, printTest, printVariable };
package/dist/index.mjs CHANGED
@@ -443,10 +443,39 @@ var Parser = class {
443
443
  this.expect(":");
444
444
  this.skipInlineSpaces();
445
445
  const { config, configType } = this.parseStepConfig();
446
- const tags = this.parseTags();
446
+ const condition = this.parseStepCondition();
447
447
  const parsedJoinTargets = name === "join" ? this.parseJoinTaskNames(config) : void 0;
448
448
  this.skipWhitespaceOnly();
449
- return { kind: "Regular", name, config, configType, tags, parsedJoinTargets };
449
+ return { kind: "Regular", name, config, configType, condition, parsedJoinTargets };
450
+ }
451
+ /**
452
+ * Parse optional step condition (tag expression after the config)
453
+ * Supports:
454
+ * - @tag (single tag)
455
+ * - @tag @tag2 (implicit AND for backwards compatibility)
456
+ * - @tag and @tag2 (explicit AND)
457
+ * - @tag or @tag2 (explicit OR)
458
+ * - (@tag or @tag2) and @tag3 (grouping)
459
+ */
460
+ parseStepCondition() {
461
+ this.skipInlineSpaces();
462
+ if (this.cur() !== "@") {
463
+ return void 0;
464
+ }
465
+ let expr = this.parseTagExpr();
466
+ while (true) {
467
+ this.skipInlineSpaces();
468
+ const ch = this.cur();
469
+ if (ch === "\n" || ch === "\r" || ch === "#" || this.text.startsWith("//", this.pos)) {
470
+ break;
471
+ }
472
+ if (ch !== "@") {
473
+ break;
474
+ }
475
+ const nextTag = this.parseTag();
476
+ expr = { kind: "And", left: expr, right: { kind: "Tag", tag: nextTag } };
477
+ }
478
+ return expr;
450
479
  }
451
480
  /**
452
481
  * Pre-parse join config into task names at parse time.
@@ -560,11 +589,69 @@ var Parser = class {
560
589
  this.skipSpaces();
561
590
  this.expect("case");
562
591
  this.skipInlineSpaces();
563
- const tag = this.parseTag();
592
+ const condition = this.parseTagExpr();
593
+ this.skipInlineSpaces();
564
594
  this.expect(":");
565
595
  this.skipSpaces();
566
596
  const pipeline = this.parseIfPipeline("case", "default:", "end");
567
- return { tag, pipeline };
597
+ return { condition, pipeline };
598
+ }
599
+ /**
600
+ * Parse a tag expression with boolean operators (and, or) and grouping
601
+ * Grammar (precedence: AND > OR):
602
+ * tag_expr := or_expr
603
+ * or_expr := and_expr ("or" and_expr)*
604
+ * and_expr := primary ("and" primary)*
605
+ * primary := tag | "(" tag_expr ")"
606
+ */
607
+ parseTagExpr() {
608
+ return this.parseOrExpr();
609
+ }
610
+ parseOrExpr() {
611
+ let left = this.parseAndExpr();
612
+ while (true) {
613
+ const saved = this.pos;
614
+ this.skipInlineSpaces();
615
+ if (this.text.startsWith("or", this.pos) && !this.isIdentCont(this.text[this.pos + 2] || "")) {
616
+ this.pos += 2;
617
+ this.skipInlineSpaces();
618
+ const right = this.parseAndExpr();
619
+ left = { kind: "Or", left, right };
620
+ } else {
621
+ this.pos = saved;
622
+ break;
623
+ }
624
+ }
625
+ return left;
626
+ }
627
+ parseAndExpr() {
628
+ let left = this.parseTagPrimary();
629
+ while (true) {
630
+ const saved = this.pos;
631
+ this.skipInlineSpaces();
632
+ if (this.text.startsWith("and", this.pos) && !this.isIdentCont(this.text[this.pos + 3] || "")) {
633
+ this.pos += 3;
634
+ this.skipInlineSpaces();
635
+ const right = this.parseTagPrimary();
636
+ left = { kind: "And", left, right };
637
+ } else {
638
+ this.pos = saved;
639
+ break;
640
+ }
641
+ }
642
+ return left;
643
+ }
644
+ parseTagPrimary() {
645
+ if (this.cur() === "(") {
646
+ this.pos++;
647
+ this.skipInlineSpaces();
648
+ const expr = this.parseTagExpr();
649
+ this.skipInlineSpaces();
650
+ this.expect(")");
651
+ return expr;
652
+ }
653
+ const tag = this.parseTag();
654
+ return { kind: "Tag", tag };
568
655
  }
569
656
  parseIfPipeline(...stopKeywords) {
570
657
  const steps = [];
@@ -1087,8 +1174,8 @@ function formatConfigValue(value) {
1087
1174
  function formatPipelineStep(step, indent = " ") {
1088
1175
  if (step.kind === "Regular") {
1089
1176
  const configPart = formatStepConfig(step.config, step.configType);
1090
- const tagsPart = step.tags.length > 0 ? " " + formatTags(step.tags) : "";
1091
- return `${indent}|> ${step.name}: ${configPart}${tagsPart}`;
1177
+ const conditionPart = step.condition ? " " + formatTagExpr(step.condition) : "";
1178
+ return `${indent}|> ${step.name}: ${configPart}${conditionPart}`;
1092
1179
  } else if (step.kind === "Result") {
1093
1180
  const lines = [`${indent}|> result`];
1094
1181
  step.branches.forEach((branch) => {
@@ -1118,7 +1205,7 @@ function formatPipelineStep(step, indent = " ") {
1118
1205
  } else if (step.kind === "Dispatch") {
1119
1206
  const lines = [`${indent}|> dispatch`];
1120
1207
  step.branches.forEach((branch) => {
1121
- lines.push(`${indent} case ${formatTag(branch.tag)}:`);
1208
+ lines.push(`${indent} case ${formatTagExpr(branch.condition)}:`);
1122
1209
  branch.pipeline.steps.forEach((branchStep) => {
1123
1210
  lines.push(formatPipelineStep(branchStep, indent + " "));
1124
1211
  });
@@ -1157,6 +1244,19 @@ function formatTag(tag) {
1157
1244
  const args = tag.args.length > 0 ? `(${tag.args.join(",")})` : "";
1158
1245
  return `@${negation}${tag.name}${args}`;
1159
1246
  }
1247
+ function formatTagExpr(expr) {
1248
+ switch (expr.kind) {
1249
+ case "Tag":
1250
+ return formatTag(expr.tag);
1251
+ case "And": {
1252
+ const leftStr = expr.left.kind === "Or" ? `(${formatTagExpr(expr.left)})` : formatTagExpr(expr.left);
1253
+ const rightStr = expr.right.kind === "Or" ? `(${formatTagExpr(expr.right)})` : formatTagExpr(expr.right);
1254
+ return `${leftStr} and ${rightStr}`;
1255
+ }
1256
+ case "Or":
1257
+ return `${formatTagExpr(expr.left)} or ${formatTagExpr(expr.right)}`;
1258
+ }
1259
+ }
1160
1260
  function formatPipelineRef(ref) {
1161
1261
  if (ref.kind === "Named") {
1162
1262
  return [` |> pipeline: ${ref.name}`];
@@ -1184,6 +1284,7 @@ export {
1184
1284
  formatPipelineStep,
1185
1285
  formatStepConfig,
1186
1286
  formatTag,
1287
+ formatTagExpr,
1187
1288
  formatTags,
1188
1289
  formatWhen,
1189
1290
  getPipelineRanges,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpipe-js",
3
- "version": "2.0.11",
3
+ "version": "2.0.13",
4
4
  "description": "Web Pipe parser",
5
5
  "license": "ISC",
6
6
  "author": "William Cotton",