webpipe-js 2.0.10 → 2.0.12

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,
@@ -461,8 +462,29 @@ var Parser = class {
461
462
  if (ifStep) return ifStep;
462
463
  const dispatchStep = this.tryParse(() => this.parseDispatchStep());
463
464
  if (dispatchStep) return dispatchStep;
465
+ const foreachStep = this.tryParse(() => this.parseForeachStep());
466
+ if (foreachStep) return foreachStep;
464
467
  return this.parseRegularStep();
465
468
  }
469
+ parseForeachStep() {
470
+ this.skipWhitespaceOnly();
471
+ this.expect("|>");
472
+ this.skipInlineSpaces();
473
+ this.expect("foreach");
474
+ if (this.cur() !== " " && this.cur() !== " ") {
475
+ throw new ParseFailure("space after foreach", this.pos);
476
+ }
477
+ this.skipInlineSpaces();
478
+ const selector = this.consumeWhile((c) => c !== "\n" && c !== "#").trim();
479
+ if (selector.length === 0) {
480
+ throw new ParseFailure("foreach selector", this.pos);
481
+ }
482
+ this.skipSpaces();
483
+ const pipeline = this.parseIfPipeline("end");
484
+ this.skipSpaces();
485
+ this.expect("end");
486
+ return { kind: "Foreach", selector, pipeline };
487
+ }
466
488
  parseRegularStep() {
467
489
  this.skipWhitespaceOnly();
468
490
  this.expect("|>");
@@ -588,11 +610,69 @@ var Parser = class {
588
610
  this.skipSpaces();
589
611
  this.expect("case");
590
612
  this.skipInlineSpaces();
591
- const tag = this.parseTag();
613
+ const condition = this.parseTagExpr();
614
+ this.skipInlineSpaces();
592
615
  this.expect(":");
593
616
  this.skipSpaces();
594
617
  const pipeline = this.parseIfPipeline("case", "default:", "end");
595
- return { tag, pipeline };
618
+ return { condition, pipeline };
619
+ }
620
+ /**
621
+ * Parse a tag expression with boolean operators (and, or) and grouping
622
+ * Grammar (precedence: AND > OR):
623
+ * tag_expr := or_expr
624
+ * or_expr := and_expr ("or" and_expr)*
625
+ * and_expr := primary ("and" primary)*
626
+ * primary := tag | "(" tag_expr ")"
627
+ */
628
+ parseTagExpr() {
629
+ return this.parseOrExpr();
630
+ }
631
+ parseOrExpr() {
632
+ let left = this.parseAndExpr();
633
+ while (true) {
634
+ const saved = this.pos;
635
+ this.skipInlineSpaces();
636
+ if (this.text.startsWith("or", this.pos) && !this.isIdentCont(this.text[this.pos + 2] || "")) {
637
+ this.pos += 2;
638
+ this.skipInlineSpaces();
639
+ const right = this.parseAndExpr();
640
+ left = { kind: "Or", left, right };
641
+ } else {
642
+ this.pos = saved;
643
+ break;
644
+ }
645
+ }
646
+ return left;
647
+ }
648
+ parseAndExpr() {
649
+ let left = this.parseTagPrimary();
650
+ while (true) {
651
+ const saved = this.pos;
652
+ this.skipInlineSpaces();
653
+ if (this.text.startsWith("and", this.pos) && !this.isIdentCont(this.text[this.pos + 3] || "")) {
654
+ this.pos += 3;
655
+ this.skipInlineSpaces();
656
+ const right = this.parseTagPrimary();
657
+ left = { kind: "And", left, right };
658
+ } else {
659
+ this.pos = saved;
660
+ break;
661
+ }
662
+ }
663
+ return left;
664
+ }
665
+ parseTagPrimary() {
666
+ if (this.cur() === "(") {
667
+ this.pos++;
668
+ this.skipInlineSpaces();
669
+ const expr = this.parseTagExpr();
670
+ this.skipInlineSpaces();
671
+ this.expect(")");
672
+ return expr;
673
+ }
674
+ const tag = this.parseTag();
675
+ return { kind: "Tag", tag };
596
676
  }
597
677
  parseIfPipeline(...stopKeywords) {
598
678
  const steps = [];
@@ -1143,10 +1223,10 @@ function formatPipelineStep(step, indent = " ") {
1143
1223
  });
1144
1224
  }
1145
1225
  return lines.join("\n");
1146
- } else {
1226
+ } else if (step.kind === "Dispatch") {
1147
1227
  const lines = [`${indent}|> dispatch`];
1148
1228
  step.branches.forEach((branch) => {
1149
- lines.push(`${indent} case ${formatTag(branch.tag)}:`);
1229
+ lines.push(`${indent} case ${formatTagExpr(branch.condition)}:`);
1150
1230
  branch.pipeline.steps.forEach((branchStep) => {
1151
1231
  lines.push(formatPipelineStep(branchStep, indent + " "));
1152
1232
  });
@@ -1158,6 +1238,13 @@ function formatPipelineStep(step, indent = " ") {
1158
1238
  });
1159
1239
  }
1160
1240
  return lines.join("\n");
1241
+ } else {
1242
+ const lines = [`${indent}|> foreach ${step.selector}`];
1243
+ step.pipeline.steps.forEach((innerStep) => {
1244
+ lines.push(formatPipelineStep(innerStep, indent + " "));
1245
+ });
1246
+ lines.push(`${indent}end`);
1247
+ return lines.join("\n");
1161
1248
  }
1162
1249
  }
1163
1250
  function formatStepConfig(config, configType) {
@@ -1178,6 +1265,19 @@ function formatTag(tag) {
1178
1265
  const args = tag.args.length > 0 ? `(${tag.args.join(",")})` : "";
1179
1266
  return `@${negation}${tag.name}${args}`;
1180
1267
  }
1268
+ function formatTagExpr(expr) {
1269
+ switch (expr.kind) {
1270
+ case "Tag":
1271
+ return formatTag(expr.tag);
1272
+ case "And": {
1273
+ const leftStr = expr.left.kind === "Or" ? `(${formatTagExpr(expr.left)})` : formatTagExpr(expr.left);
1274
+ const rightStr = expr.right.kind === "Or" ? `(${formatTagExpr(expr.right)})` : formatTagExpr(expr.right);
1275
+ return `${leftStr} and ${rightStr}`;
1276
+ }
1277
+ case "Or":
1278
+ return `${formatTagExpr(expr.left)} or ${formatTagExpr(expr.right)}`;
1279
+ }
1280
+ }
1181
1281
  function formatPipelineRef(ref) {
1182
1282
  if (ref.kind === "Named") {
1183
1283
  return [` |> pipeline: ${ref.name}`];
@@ -1206,6 +1306,7 @@ function formatWhen(when) {
1206
1306
  formatPipelineStep,
1207
1307
  formatStepConfig,
1208
1308
  formatTag,
1309
+ formatTagExpr,
1209
1310
  formatTags,
1210
1311
  formatWhen,
1211
1312
  getPipelineRanges,
package/dist/index.d.cts CHANGED
@@ -93,6 +93,19 @@ 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;
@@ -112,9 +125,13 @@ type PipelineStep = {
112
125
  kind: 'Dispatch';
113
126
  branches: DispatchBranch[];
114
127
  default?: Pipeline;
128
+ } | {
129
+ kind: 'Foreach';
130
+ selector: string;
131
+ pipeline: Pipeline;
115
132
  };
116
133
  interface DispatchBranch {
117
- tag: Tag;
134
+ condition: TagExpr;
118
135
  pipeline: Pipeline;
119
136
  }
120
137
  interface ResultBranch {
@@ -205,7 +222,8 @@ declare function formatPipelineStep(step: PipelineStep, indent?: string): string
205
222
  declare function formatStepConfig(config: string, configType: ConfigType): string;
206
223
  declare function formatTags(tags: Tag[]): string;
207
224
  declare function formatTag(tag: Tag): string;
225
+ declare function formatTagExpr(expr: TagExpr): string;
208
226
  declare function formatPipelineRef(ref: PipelineRef): string[];
209
227
  declare function formatWhen(when: When): string;
210
228
 
211
- 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,6 +93,19 @@ 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;
@@ -112,9 +125,13 @@ type PipelineStep = {
112
125
  kind: 'Dispatch';
113
126
  branches: DispatchBranch[];
114
127
  default?: Pipeline;
128
+ } | {
129
+ kind: 'Foreach';
130
+ selector: string;
131
+ pipeline: Pipeline;
115
132
  };
116
133
  interface DispatchBranch {
117
- tag: Tag;
134
+ condition: TagExpr;
118
135
  pipeline: Pipeline;
119
136
  }
120
137
  interface ResultBranch {
@@ -205,7 +222,8 @@ declare function formatPipelineStep(step: PipelineStep, indent?: string): string
205
222
  declare function formatStepConfig(config: string, configType: ConfigType): string;
206
223
  declare function formatTags(tags: Tag[]): string;
207
224
  declare function formatTag(tag: Tag): string;
225
+ declare function formatTagExpr(expr: TagExpr): string;
208
226
  declare function formatPipelineRef(ref: PipelineRef): string[];
209
227
  declare function formatWhen(when: When): string;
210
228
 
211
- 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
@@ -412,8 +412,29 @@ var Parser = class {
412
412
  if (ifStep) return ifStep;
413
413
  const dispatchStep = this.tryParse(() => this.parseDispatchStep());
414
414
  if (dispatchStep) return dispatchStep;
415
+ const foreachStep = this.tryParse(() => this.parseForeachStep());
416
+ if (foreachStep) return foreachStep;
415
417
  return this.parseRegularStep();
416
418
  }
419
+ parseForeachStep() {
420
+ this.skipWhitespaceOnly();
421
+ this.expect("|>");
422
+ this.skipInlineSpaces();
423
+ this.expect("foreach");
424
+ if (this.cur() !== " " && this.cur() !== " ") {
425
+ throw new ParseFailure("space after foreach", this.pos);
426
+ }
427
+ this.skipInlineSpaces();
428
+ const selector = this.consumeWhile((c) => c !== "\n" && c !== "#").trim();
429
+ if (selector.length === 0) {
430
+ throw new ParseFailure("foreach selector", this.pos);
431
+ }
432
+ this.skipSpaces();
433
+ const pipeline = this.parseIfPipeline("end");
434
+ this.skipSpaces();
435
+ this.expect("end");
436
+ return { kind: "Foreach", selector, pipeline };
437
+ }
417
438
  parseRegularStep() {
418
439
  this.skipWhitespaceOnly();
419
440
  this.expect("|>");
@@ -539,11 +560,69 @@ var Parser = class {
539
560
  this.skipSpaces();
540
561
  this.expect("case");
541
562
  this.skipInlineSpaces();
542
- const tag = this.parseTag();
563
+ const condition = this.parseTagExpr();
564
+ this.skipInlineSpaces();
543
565
  this.expect(":");
544
566
  this.skipSpaces();
545
567
  const pipeline = this.parseIfPipeline("case", "default:", "end");
546
- return { tag, pipeline };
568
+ return { condition, pipeline };
569
+ }
570
+ /**
571
+ * Parse a tag expression with boolean operators (and, or) and grouping
572
+ * Grammar (precedence: AND > OR):
573
+ * tag_expr := or_expr
574
+ * or_expr := and_expr ("or" and_expr)*
575
+ * and_expr := primary ("and" primary)*
576
+ * primary := tag | "(" tag_expr ")"
577
+ */
578
+ parseTagExpr() {
579
+ return this.parseOrExpr();
580
+ }
581
+ parseOrExpr() {
582
+ let left = this.parseAndExpr();
583
+ while (true) {
584
+ const saved = this.pos;
585
+ this.skipInlineSpaces();
586
+ if (this.text.startsWith("or", this.pos) && !this.isIdentCont(this.text[this.pos + 2] || "")) {
587
+ this.pos += 2;
588
+ this.skipInlineSpaces();
589
+ const right = this.parseAndExpr();
590
+ left = { kind: "Or", left, right };
591
+ } else {
592
+ this.pos = saved;
593
+ break;
594
+ }
595
+ }
596
+ return left;
597
+ }
598
+ parseAndExpr() {
599
+ let left = this.parseTagPrimary();
600
+ while (true) {
601
+ const saved = this.pos;
602
+ this.skipInlineSpaces();
603
+ if (this.text.startsWith("and", this.pos) && !this.isIdentCont(this.text[this.pos + 3] || "")) {
604
+ this.pos += 3;
605
+ this.skipInlineSpaces();
606
+ const right = this.parseTagPrimary();
607
+ left = { kind: "And", left, right };
608
+ } else {
609
+ this.pos = saved;
610
+ break;
611
+ }
612
+ }
613
+ return left;
614
+ }
615
+ parseTagPrimary() {
616
+ if (this.cur() === "(") {
617
+ this.pos++;
618
+ this.skipInlineSpaces();
619
+ const expr = this.parseTagExpr();
620
+ this.skipInlineSpaces();
621
+ this.expect(")");
622
+ return expr;
623
+ }
624
+ const tag = this.parseTag();
625
+ return { kind: "Tag", tag };
547
626
  }
548
627
  parseIfPipeline(...stopKeywords) {
549
628
  const steps = [];
@@ -1094,10 +1173,10 @@ function formatPipelineStep(step, indent = " ") {
1094
1173
  });
1095
1174
  }
1096
1175
  return lines.join("\n");
1097
- } else {
1176
+ } else if (step.kind === "Dispatch") {
1098
1177
  const lines = [`${indent}|> dispatch`];
1099
1178
  step.branches.forEach((branch) => {
1100
- lines.push(`${indent} case ${formatTag(branch.tag)}:`);
1179
+ lines.push(`${indent} case ${formatTagExpr(branch.condition)}:`);
1101
1180
  branch.pipeline.steps.forEach((branchStep) => {
1102
1181
  lines.push(formatPipelineStep(branchStep, indent + " "));
1103
1182
  });
@@ -1109,6 +1188,13 @@ function formatPipelineStep(step, indent = " ") {
1109
1188
  });
1110
1189
  }
1111
1190
  return lines.join("\n");
1191
+ } else {
1192
+ const lines = [`${indent}|> foreach ${step.selector}`];
1193
+ step.pipeline.steps.forEach((innerStep) => {
1194
+ lines.push(formatPipelineStep(innerStep, indent + " "));
1195
+ });
1196
+ lines.push(`${indent}end`);
1197
+ return lines.join("\n");
1112
1198
  }
1113
1199
  }
1114
1200
  function formatStepConfig(config, configType) {
@@ -1129,6 +1215,19 @@ function formatTag(tag) {
1129
1215
  const args = tag.args.length > 0 ? `(${tag.args.join(",")})` : "";
1130
1216
  return `@${negation}${tag.name}${args}`;
1131
1217
  }
1218
+ function formatTagExpr(expr) {
1219
+ switch (expr.kind) {
1220
+ case "Tag":
1221
+ return formatTag(expr.tag);
1222
+ case "And": {
1223
+ const leftStr = expr.left.kind === "Or" ? `(${formatTagExpr(expr.left)})` : formatTagExpr(expr.left);
1224
+ const rightStr = expr.right.kind === "Or" ? `(${formatTagExpr(expr.right)})` : formatTagExpr(expr.right);
1225
+ return `${leftStr} and ${rightStr}`;
1226
+ }
1227
+ case "Or":
1228
+ return `${formatTagExpr(expr.left)} or ${formatTagExpr(expr.right)}`;
1229
+ }
1230
+ }
1132
1231
  function formatPipelineRef(ref) {
1133
1232
  if (ref.kind === "Named") {
1134
1233
  return [` |> pipeline: ${ref.name}`];
@@ -1156,6 +1255,7 @@ export {
1156
1255
  formatPipelineStep,
1157
1256
  formatStepConfig,
1158
1257
  formatTag,
1258
+ formatTagExpr,
1159
1259
  formatTags,
1160
1260
  formatWhen,
1161
1261
  getPipelineRanges,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpipe-js",
3
- "version": "2.0.10",
3
+ "version": "2.0.12",
4
4
  "description": "Web Pipe parser",
5
5
  "license": "ISC",
6
6
  "author": "William Cotton",