webpipe-js 0.1.0 → 0.1.2

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.
@@ -1,219 +1,101 @@
1
- export interface Program {
2
- configs: Config[];
3
- pipelines: NamedPipeline[];
4
- variables: Variable[];
5
- routes: Route[];
6
- describes: Describe[];
7
- }
8
-
9
- export interface Config {
10
- name: string;
11
- properties: ConfigProperty[];
12
- }
13
-
14
- export interface ConfigProperty {
15
- key: string;
16
- value: ConfigValue;
17
- }
18
-
19
- export type ConfigValue =
20
- | { kind: 'String'; value: string }
21
- | { kind: 'EnvVar'; var: string; default?: string }
22
- | { kind: 'Boolean'; value: boolean }
23
- | { kind: 'Number'; value: number };
24
-
25
- export interface NamedPipeline {
26
- name: string;
27
- pipeline: Pipeline;
28
- }
29
-
30
- export interface Variable {
31
- varType: string;
32
- name: string;
33
- value: string;
34
- }
35
-
36
- export interface Route {
37
- method: string;
38
- path: string;
39
- pipeline: PipelineRef;
40
- }
41
-
42
- export type PipelineRef =
43
- | { kind: 'Inline'; pipeline: Pipeline }
44
- | { kind: 'Named'; name: string };
45
-
46
- export interface Pipeline {
47
- steps: PipelineStep[];
48
- }
49
-
50
- export type PipelineStep =
51
- | { kind: 'Regular'; name: string; config: string }
52
- | { kind: 'Result'; branches: ResultBranch[] };
53
-
54
- export interface ResultBranch {
55
- branchType: ResultBranchType;
56
- statusCode: number;
57
- pipeline: Pipeline;
58
- }
59
-
60
- export type ResultBranchType =
61
- | { kind: 'Ok' }
62
- | { kind: 'Custom'; name: string }
63
- | { kind: 'Default' };
64
-
65
- export interface Describe {
66
- name: string;
67
- mocks: Mock[];
68
- tests: It[];
69
- }
70
-
71
- export interface Mock {
72
- target: string;
73
- returnValue: string;
74
- }
75
-
76
- export interface It {
77
- name: string;
78
- mocks: Mock[];
79
- when: When;
80
- input?: string;
81
- conditions: Condition[];
82
- }
83
-
84
- export type When =
85
- | { kind: 'CallingRoute'; method: string; path: string }
86
- | { kind: 'ExecutingPipeline'; name: string }
87
- | { kind: 'ExecutingVariable'; varType: string; name: string };
88
-
89
- export interface Condition {
90
- conditionType: 'Then' | 'And';
91
- field: string;
92
- jqExpr?: string;
93
- comparison: string;
94
- value: string;
95
- }
96
-
97
- export type DiagnosticSeverity = 'error' | 'warning' | 'info';
98
- export interface ParseDiagnostic {
99
- message: string;
100
- start: number;
101
- end: number;
102
- severity: DiagnosticSeverity;
103
- }
104
-
105
- class Parser {
106
- private readonly text: string;
107
- private readonly len: number;
108
- private pos: number = 0;
109
- private diagnostics: ParseDiagnostic[] = [];
110
- private readonly pipelineRanges: Map<string, { start: number; end: number }> = new Map();
111
- private readonly variableRanges: Map<string, { start: number; end: number }> = new Map();
112
-
113
- constructor(text: string) {
1
+ // src/parser.ts
2
+ var Parser = class {
3
+ constructor(text) {
4
+ this.pos = 0;
5
+ this.diagnostics = [];
6
+ this.pipelineRanges = /* @__PURE__ */ new Map();
7
+ this.variableRanges = /* @__PURE__ */ new Map();
114
8
  this.text = text;
115
9
  this.len = text.length;
116
10
  }
117
-
118
- getDiagnostics(): ParseDiagnostic[] {
11
+ getDiagnostics() {
119
12
  return this.diagnostics.slice();
120
13
  }
121
-
122
- getPipelineRanges(): Map<string, { start: number; end: number }> {
14
+ getPipelineRanges() {
123
15
  return new Map(this.pipelineRanges);
124
16
  }
125
-
126
- getVariableRanges(): Map<string, { start: number; end: number }> {
17
+ getVariableRanges() {
127
18
  return new Map(this.variableRanges);
128
19
  }
129
-
130
- report(message: string, start: number, end: number, severity: DiagnosticSeverity): void {
20
+ report(message, start, end, severity) {
131
21
  this.diagnostics.push({ message, start, end, severity });
132
22
  }
133
-
134
- findLineStart(pos: number): number {
23
+ findLineStart(pos) {
135
24
  let i = Math.max(0, Math.min(pos, this.len));
136
- while (i > 0 && this.text[i - 1] !== '\n') i--;
25
+ while (i > 0 && this.text[i - 1] !== "\n") i--;
137
26
  return i;
138
27
  }
139
-
140
- findLineEnd(pos: number): number {
28
+ findLineEnd(pos) {
141
29
  let i = Math.max(0, Math.min(pos, this.len));
142
- while (i < this.text.length && this.text[i] !== '\n') i++;
30
+ while (i < this.text.length && this.text[i] !== "\n") i++;
143
31
  return i;
144
32
  }
145
-
146
- parseProgram(): Program {
33
+ parseProgram() {
147
34
  this.skipSpaces();
148
-
149
- const configs: Config[] = [];
150
- const pipelines: NamedPipeline[] = [];
151
- const variables: Variable[] = [];
152
- const routes: Route[] = [];
153
- const describes: Describe[] = [];
154
-
35
+ const configs = [];
36
+ const pipelines = [];
37
+ const variables = [];
38
+ const routes = [];
39
+ const describes = [];
155
40
  while (!this.eof()) {
156
41
  this.skipSpaces();
157
42
  if (this.eof()) break;
158
-
159
43
  const start = this.pos;
160
-
161
44
  const cfg = this.tryParse(() => this.parseConfig());
162
45
  if (cfg) {
163
46
  configs.push(cfg);
164
47
  continue;
165
48
  }
166
-
167
49
  const namedPipe = this.tryParse(() => this.parseNamedPipeline());
168
50
  if (namedPipe) {
169
51
  pipelines.push(namedPipe);
170
52
  continue;
171
53
  }
172
-
173
54
  const variable = this.tryParse(() => this.parseVariable());
174
55
  if (variable) {
175
56
  variables.push(variable);
176
57
  continue;
177
58
  }
178
-
179
59
  const route = this.tryParse(() => this.parseRoute());
180
60
  if (route) {
181
61
  routes.push(route);
182
62
  continue;
183
63
  }
184
-
185
64
  const describe = this.tryParse(() => this.parseDescribe());
186
65
  if (describe) {
187
66
  describes.push(describe);
188
67
  continue;
189
68
  }
190
-
191
69
  if (this.pos === start) {
192
70
  const lineStart = this.findLineStart(this.pos);
193
71
  const lineEnd = this.findLineEnd(this.pos);
194
- this.report('Unrecognized or unsupported syntax', lineStart, lineEnd, 'warning');
72
+ this.report("Unrecognized or unsupported syntax", lineStart, lineEnd, "warning");
195
73
  this.skipToEol();
196
- if (this.cur() === '\n') this.pos++;
197
- this.consumeWhile((c) => c === '\n');
74
+ if (this.cur() === "\n") this.pos++;
75
+ this.consumeWhile((c) => c === "\n");
198
76
  }
199
77
  }
200
-
201
78
  const backtickCount = (this.text.match(/`/g) || []).length;
202
79
  if (backtickCount % 2 === 1) {
203
- const idx = this.text.lastIndexOf('`');
80
+ const idx = this.text.lastIndexOf("`");
204
81
  const start = Math.max(0, idx);
205
- this.report('Unclosed backtick-delimited string', start, start + 1, 'warning');
82
+ this.report("Unclosed backtick-delimited string", start, start + 1, "warning");
206
83
  }
207
-
208
84
  return { configs, pipelines, variables, routes, describes };
209
85
  }
210
-
211
- private eof(): boolean { return this.pos >= this.len; }
212
- private peek(): string { return this.text[this.pos] ?? '\0'; }
213
- private cur(): string { return this.text[this.pos] ?? '\0'; }
214
- private ahead(n: number): string { return this.text[this.pos + n] ?? '\0'; }
215
-
216
- private tryParse<T>(fn: () => T): T | null {
86
+ eof() {
87
+ return this.pos >= this.len;
88
+ }
89
+ peek() {
90
+ return this.text[this.pos] ?? "\0";
91
+ }
92
+ cur() {
93
+ return this.text[this.pos] ?? "\0";
94
+ }
95
+ ahead(n) {
96
+ return this.text[this.pos + n] ?? "\0";
97
+ }
98
+ tryParse(fn) {
217
99
  const save = this.pos;
218
100
  try {
219
101
  const value = fn();
@@ -223,74 +105,63 @@ class Parser {
223
105
  return null;
224
106
  }
225
107
  }
226
-
227
- private skipSpaces(): void {
108
+ skipSpaces() {
228
109
  while (true) {
229
- this.consumeWhile((ch) => ch === ' ' || ch === '\t' || ch === '\r' || ch === '\n');
230
- if (this.text.startsWith('#', this.pos)) {
110
+ this.consumeWhile((ch) => ch === " " || ch === " " || ch === "\r" || ch === "\n");
111
+ if (this.text.startsWith("#", this.pos)) {
231
112
  this.skipToEol();
232
- if (this.cur() === '\n') this.pos++;
113
+ if (this.cur() === "\n") this.pos++;
233
114
  continue;
234
115
  }
235
- if (this.text.startsWith('//', this.pos)) {
116
+ if (this.text.startsWith("//", this.pos)) {
236
117
  this.skipToEol();
237
- if (this.cur() === '\n') this.pos++;
118
+ if (this.cur() === "\n") this.pos++;
238
119
  continue;
239
120
  }
240
121
  break;
241
122
  }
242
123
  }
243
-
244
- private skipInlineSpaces(): void {
245
- this.consumeWhile((ch) => ch === ' ' || ch === '\t' || ch === '\r');
124
+ skipInlineSpaces() {
125
+ this.consumeWhile((ch) => ch === " " || ch === " " || ch === "\r");
246
126
  }
247
-
248
- private consumeWhile(pred: (ch: string) => boolean): string {
127
+ consumeWhile(pred) {
249
128
  const start = this.pos;
250
129
  while (!this.eof() && pred(this.text[this.pos])) this.pos++;
251
130
  return this.text.slice(start, this.pos);
252
131
  }
253
-
254
- private match(str: string): boolean {
132
+ match(str) {
255
133
  if (this.text.startsWith(str, this.pos)) {
256
134
  this.pos += str.length;
257
135
  return true;
258
136
  }
259
137
  return false;
260
138
  }
261
-
262
- private expect(str: string): void {
139
+ expect(str) {
263
140
  if (!this.match(str)) throw new ParseFailure(`expected '${str}'`, this.pos);
264
141
  }
265
-
266
- private skipToEol(): void {
267
- while (!this.eof() && this.cur() !== '\n') this.pos++;
142
+ skipToEol() {
143
+ while (!this.eof() && this.cur() !== "\n") this.pos++;
268
144
  }
269
-
270
- private isIdentStart(ch: string): boolean {
145
+ isIdentStart(ch) {
271
146
  return /[A-Za-z_]/.test(ch);
272
147
  }
273
-
274
- private isIdentCont(ch: string): boolean {
148
+ isIdentCont(ch) {
275
149
  return /[A-Za-z0-9_\-]/.test(ch);
276
150
  }
277
-
278
- private parseIdentifier(): string {
279
- if (!this.isIdentStart(this.cur())) throw new ParseFailure('identifier', this.pos);
151
+ parseIdentifier() {
152
+ if (!this.isIdentStart(this.cur())) throw new ParseFailure("identifier", this.pos);
280
153
  const start = this.pos;
281
154
  this.pos++;
282
155
  while (!this.eof() && this.isIdentCont(this.cur())) this.pos++;
283
156
  return this.text.slice(start, this.pos);
284
157
  }
285
-
286
- private parseNumber(): number {
158
+ parseNumber() {
287
159
  const start = this.pos;
288
160
  const digits = this.consumeWhile((c) => /[0-9]/.test(c));
289
- if (digits.length === 0) throw new ParseFailure('number', this.pos);
161
+ if (digits.length === 0) throw new ParseFailure("number", this.pos);
290
162
  return parseInt(this.text.slice(start, this.pos), 10);
291
163
  }
292
-
293
- private parseQuotedString(): string {
164
+ parseQuotedString() {
294
165
  this.expect('"');
295
166
  const start = this.pos;
296
167
  while (!this.eof()) {
@@ -302,94 +173,83 @@ class Parser {
302
173
  this.expect('"');
303
174
  return content;
304
175
  }
305
-
306
- private parseBacktickString(): string {
307
- this.expect('`');
176
+ parseBacktickString() {
177
+ this.expect("`");
308
178
  const start = this.pos;
309
179
  while (!this.eof()) {
310
180
  const ch = this.cur();
311
- if (ch === '`') break;
181
+ if (ch === "`") break;
312
182
  this.pos++;
313
183
  }
314
184
  const content = this.text.slice(start, this.pos);
315
- this.expect('`');
185
+ this.expect("`");
316
186
  return content;
317
187
  }
318
-
319
- private parseMethod(): string {
320
- const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
188
+ parseMethod() {
189
+ const methods = ["GET", "POST", "PUT", "PATCH", "DELETE"];
321
190
  for (const m of methods) {
322
191
  if (this.text.startsWith(m, this.pos)) {
323
192
  this.pos += m.length;
324
193
  return m;
325
194
  }
326
195
  }
327
- throw new ParseFailure('method', this.pos);
196
+ throw new ParseFailure("method", this.pos);
328
197
  }
329
-
330
- private parseStepConfig(): string {
198
+ parseStepConfig() {
331
199
  const bt = this.tryParse(() => this.parseBacktickString());
332
200
  if (bt !== null) return bt;
333
201
  const dq = this.tryParse(() => this.parseQuotedString());
334
202
  if (dq !== null) return dq;
335
203
  const id = this.tryParse(() => this.parseIdentifier());
336
204
  if (id !== null) return id;
337
- throw new ParseFailure('step-config', this.pos);
205
+ throw new ParseFailure("step-config", this.pos);
338
206
  }
339
-
340
- private parseConfigValue(): ConfigValue {
207
+ parseConfigValue() {
341
208
  const envWithDefault = this.tryParse(() => {
342
- this.expect('$');
209
+ this.expect("$");
343
210
  const variable = this.parseIdentifier();
344
211
  this.skipInlineSpaces();
345
- this.expect('||');
212
+ this.expect("||");
346
213
  this.skipInlineSpaces();
347
214
  const def = this.parseQuotedString();
348
- return { kind: 'EnvVar', var: variable, default: def } as ConfigValue;
215
+ return { kind: "EnvVar", var: variable, default: def };
349
216
  });
350
217
  if (envWithDefault) return envWithDefault;
351
-
352
218
  const envNoDefault = this.tryParse(() => {
353
- this.expect('$');
219
+ this.expect("$");
354
220
  const variable = this.parseIdentifier();
355
- return { kind: 'EnvVar', var: variable } as ConfigValue;
221
+ return { kind: "EnvVar", var: variable };
356
222
  });
357
223
  if (envNoDefault) return envNoDefault;
358
-
359
224
  const str = this.tryParse(() => this.parseQuotedString());
360
- if (str !== null) return { kind: 'String', value: str };
361
-
225
+ if (str !== null) return { kind: "String", value: str };
362
226
  const bool = this.tryParse(() => {
363
- if (this.match('true')) return true;
364
- if (this.match('false')) return false;
365
- throw new ParseFailure('bool', this.pos);
227
+ if (this.match("true")) return true;
228
+ if (this.match("false")) return false;
229
+ throw new ParseFailure("bool", this.pos);
366
230
  });
367
- if (bool !== null) return { kind: 'Boolean', value: bool };
368
-
231
+ if (bool !== null) return { kind: "Boolean", value: bool };
369
232
  const num = this.tryParse(() => this.parseNumber());
370
- if (num !== null) return { kind: 'Number', value: num };
371
-
372
- throw new ParseFailure('config-value', this.pos);
233
+ if (num !== null) return { kind: "Number", value: num };
234
+ throw new ParseFailure("config-value", this.pos);
373
235
  }
374
-
375
- private parseConfigProperty(): ConfigProperty {
236
+ parseConfigProperty() {
376
237
  this.skipSpaces();
377
238
  const key = this.parseIdentifier();
378
239
  this.skipInlineSpaces();
379
- this.expect(':');
240
+ this.expect(":");
380
241
  this.skipInlineSpaces();
381
242
  const value = this.parseConfigValue();
382
243
  return { key, value };
383
244
  }
384
-
385
- private parseConfig(): Config {
386
- this.expect('config');
245
+ parseConfig() {
246
+ this.expect("config");
387
247
  this.skipInlineSpaces();
388
248
  const name = this.parseIdentifier();
389
249
  this.skipInlineSpaces();
390
- this.expect('{');
250
+ this.expect("{");
391
251
  this.skipSpaces();
392
- const properties: ConfigProperty[] = [];
252
+ const properties = [];
393
253
  while (true) {
394
254
  const prop = this.tryParse(() => this.parseConfigProperty());
395
255
  if (!prop) break;
@@ -397,72 +257,69 @@ class Parser {
397
257
  this.skipSpaces();
398
258
  }
399
259
  this.skipSpaces();
400
- this.expect('}');
260
+ this.expect("}");
401
261
  this.skipSpaces();
402
262
  return { name, properties };
403
263
  }
404
-
405
- private parsePipelineStep(): PipelineStep {
264
+ parsePipelineStep() {
406
265
  const result = this.tryParse(() => this.parseResultStep());
407
266
  if (result) return result;
408
267
  return this.parseRegularStep();
409
268
  }
410
-
411
- private parseRegularStep(): PipelineStep {
269
+ parseRegularStep() {
412
270
  this.skipSpaces();
413
- this.expect('|>');
271
+ this.expect("|>");
414
272
  this.skipInlineSpaces();
415
273
  const name = this.parseIdentifier();
416
- this.expect(':');
274
+ this.expect(":");
417
275
  this.skipInlineSpaces();
418
276
  const config = this.parseStepConfig();
419
277
  this.skipSpaces();
420
- return { kind: 'Regular', name, config };
278
+ return { kind: "Regular", name, config };
421
279
  }
422
-
423
- private parseResultStep(): PipelineStep {
280
+ parseResultStep() {
424
281
  this.skipSpaces();
425
- this.expect('|>');
282
+ this.expect("|>");
426
283
  this.skipInlineSpaces();
427
- this.expect('result');
284
+ this.expect("result");
428
285
  this.skipSpaces();
429
- const branches: ResultBranch[] = [];
286
+ const branches = [];
430
287
  while (true) {
431
288
  const br = this.tryParse(() => this.parseResultBranch());
432
289
  if (!br) break;
433
290
  branches.push(br);
434
291
  }
435
- return { kind: 'Result', branches };
292
+ return { kind: "Result", branches };
436
293
  }
437
-
438
- private parseResultBranch(): ResultBranch {
294
+ parseResultBranch() {
439
295
  this.skipSpaces();
440
296
  const branchIdent = this.parseIdentifier();
441
- let branchType: ResultBranchType;
442
- if (branchIdent === 'ok') branchType = { kind: 'Ok' };
443
- else if (branchIdent === 'default') branchType = { kind: 'Default' };
444
- else branchType = { kind: 'Custom', name: branchIdent };
445
- this.expect('(');
297
+ let branchType;
298
+ if (branchIdent === "ok") branchType = { kind: "Ok" };
299
+ else if (branchIdent === "default") branchType = { kind: "Default" };
300
+ else branchType = { kind: "Custom", name: branchIdent };
301
+ this.expect("(");
446
302
  const statusCode = this.parseNumber();
447
303
  if (statusCode < 100 || statusCode > 599) {
448
- this.report(`Invalid HTTP status code: ${statusCode}`,
304
+ this.report(
305
+ `Invalid HTTP status code: ${statusCode}`,
449
306
  this.pos - String(statusCode).length,
450
307
  this.pos,
451
- 'error');
308
+ "error"
309
+ );
452
310
  }
453
- this.expect(')');
454
- this.expect(':');
311
+ this.expect(")");
312
+ this.expect(":");
455
313
  this.skipSpaces();
456
314
  const pipeline = this.parsePipeline();
457
315
  return { branchType, statusCode, pipeline };
458
316
  }
459
-
460
- private parsePipeline(): Pipeline {
461
- const steps: PipelineStep[] = [];
317
+ parsePipeline() {
318
+ const steps = [];
462
319
  while (true) {
463
320
  const save = this.pos;
464
321
  this.skipSpaces();
465
- if (!this.text.startsWith('|>', this.pos)) {
322
+ if (!this.text.startsWith("|>", this.pos)) {
466
323
  this.pos = save;
467
324
  break;
468
325
  }
@@ -471,14 +328,13 @@ class Parser {
471
328
  }
472
329
  return { steps };
473
330
  }
474
-
475
- private parseNamedPipeline(): NamedPipeline {
331
+ parseNamedPipeline() {
476
332
  const start = this.pos;
477
- this.expect('pipeline');
333
+ this.expect("pipeline");
478
334
  this.skipInlineSpaces();
479
335
  const name = this.parseIdentifier();
480
336
  this.skipInlineSpaces();
481
- this.expect('=');
337
+ this.expect("=");
482
338
  this.skipInlineSpaces();
483
339
  const beforePipeline = this.pos;
484
340
  const pipeline = this.parsePipeline();
@@ -487,31 +343,28 @@ class Parser {
487
343
  this.skipSpaces();
488
344
  return { name, pipeline };
489
345
  }
490
-
491
- private parsePipelineRef(): PipelineRef {
346
+ parsePipelineRef() {
492
347
  const inline = this.tryParse(() => this.parsePipeline());
493
- if (inline && inline.steps.length > 0) return { kind: 'Inline', pipeline: inline };
494
-
348
+ if (inline && inline.steps.length > 0) return { kind: "Inline", pipeline: inline };
495
349
  const named = this.tryParse(() => {
496
350
  this.skipSpaces();
497
- this.expect('|>');
351
+ this.expect("|>");
498
352
  this.skipInlineSpaces();
499
- this.expect('pipeline:');
353
+ this.expect("pipeline:");
500
354
  this.skipInlineSpaces();
501
355
  const name = this.parseIdentifier();
502
- return { kind: 'Named', name } as PipelineRef;
356
+ return { kind: "Named", name };
503
357
  });
504
358
  if (named) return named;
505
- throw new Error('pipeline-ref');
359
+ throw new Error("pipeline-ref");
506
360
  }
507
-
508
- private parseVariable(): Variable {
361
+ parseVariable() {
509
362
  const start = this.pos;
510
363
  const varType = this.parseIdentifier();
511
364
  this.skipInlineSpaces();
512
365
  const name = this.parseIdentifier();
513
366
  this.skipInlineSpaces();
514
- this.expect('=');
367
+ this.expect("=");
515
368
  this.skipInlineSpaces();
516
369
  const value = this.parseBacktickString();
517
370
  const end = this.pos;
@@ -519,213 +372,308 @@ class Parser {
519
372
  this.skipSpaces();
520
373
  return { varType, name, value };
521
374
  }
522
-
523
- private parseRoute(): Route {
375
+ parseRoute() {
524
376
  const method = this.parseMethod();
525
377
  this.skipInlineSpaces();
526
- const path = this.consumeWhile((c) => c !== ' ' && c !== '\n');
378
+ const path = this.consumeWhile((c) => c !== " " && c !== "\n");
527
379
  this.skipSpaces();
528
380
  const pipeline = this.parsePipelineRef();
529
381
  this.skipSpaces();
530
382
  return { method, path, pipeline };
531
383
  }
532
-
533
- private parseWhen(): When {
384
+ parseWhen() {
534
385
  const calling = this.tryParse(() => {
535
- this.expect('calling');
386
+ this.expect("calling");
536
387
  this.skipInlineSpaces();
537
388
  const method = this.parseMethod();
538
389
  this.skipInlineSpaces();
539
- const path = this.consumeWhile((c) => c !== '\n');
540
- return { kind: 'CallingRoute', method, path } as When;
390
+ const path = this.consumeWhile((c) => c !== "\n");
391
+ return { kind: "CallingRoute", method, path };
541
392
  });
542
393
  if (calling) return calling;
543
-
544
394
  const executingPipeline = this.tryParse(() => {
545
- this.expect('executing');
395
+ this.expect("executing");
546
396
  this.skipInlineSpaces();
547
- this.expect('pipeline');
397
+ this.expect("pipeline");
548
398
  this.skipInlineSpaces();
549
399
  const name = this.parseIdentifier();
550
- return { kind: 'ExecutingPipeline', name } as When;
400
+ return { kind: "ExecutingPipeline", name };
551
401
  });
552
402
  if (executingPipeline) return executingPipeline;
553
-
554
403
  const executingVariable = this.tryParse(() => {
555
- this.expect('executing');
404
+ this.expect("executing");
556
405
  this.skipInlineSpaces();
557
- this.expect('variable');
406
+ this.expect("variable");
558
407
  this.skipInlineSpaces();
559
408
  const varType = this.parseIdentifier();
560
409
  this.skipInlineSpaces();
561
410
  const name = this.parseIdentifier();
562
- return { kind: 'ExecutingVariable', varType, name } as When;
411
+ return { kind: "ExecutingVariable", varType, name };
563
412
  });
564
413
  if (executingVariable) return executingVariable;
565
-
566
- throw new ParseFailure('when', this.pos);
414
+ throw new ParseFailure("when", this.pos);
567
415
  }
568
-
569
- private parseCondition(): Condition {
416
+ parseCondition() {
570
417
  this.skipSpaces();
571
418
  const ct = (() => {
572
- if (this.match('then')) return 'Then' as const;
573
- if (this.match('and')) return 'And' as const;
574
- throw new Error('condition-type');
419
+ if (this.match("then")) return "Then";
420
+ if (this.match("and")) return "And";
421
+ throw new Error("condition-type");
575
422
  })();
576
423
  this.skipInlineSpaces();
577
- const field = this.consumeWhile((c) => c !== ' ' && c !== '\n' && c !== '`');
424
+ const field = this.consumeWhile((c) => c !== " " && c !== "\n" && c !== "`");
578
425
  this.skipInlineSpaces();
579
426
  const jqExpr = this.tryParse(() => this.parseBacktickString());
580
427
  this.skipInlineSpaces();
581
- const comparison = this.consumeWhile((c) => c !== ' ' && c !== '\n');
428
+ const comparison = this.consumeWhile((c) => c !== " " && c !== "\n");
582
429
  this.skipInlineSpaces();
583
430
  const value = (() => {
584
431
  const v1 = this.tryParse(() => this.parseBacktickString());
585
432
  if (v1 !== null) return v1;
586
433
  const v2 = this.tryParse(() => this.parseQuotedString());
587
434
  if (v2 !== null) return v2;
588
- return this.consumeWhile((c) => c !== '\n');
435
+ return this.consumeWhile((c) => c !== "\n");
589
436
  })();
590
- return { conditionType: ct, field, jqExpr: jqExpr ?? undefined, comparison, value };
437
+ return { conditionType: ct, field, jqExpr: jqExpr ?? void 0, comparison, value };
591
438
  }
592
-
593
- private parseMockHead(prefixWord: 'with' | 'and'): Mock {
439
+ parseMockHead(prefixWord) {
594
440
  this.skipSpaces();
595
441
  this.expect(prefixWord);
596
442
  this.skipInlineSpaces();
597
- this.expect('mock');
443
+ this.expect("mock");
598
444
  this.skipInlineSpaces();
599
- const target = this.consumeWhile((c) => c !== ' ' && c !== '\n');
445
+ const target = this.consumeWhile((c) => c !== " " && c !== "\n");
600
446
  this.skipInlineSpaces();
601
- this.expect('returning');
447
+ this.expect("returning");
602
448
  this.skipInlineSpaces();
603
449
  const returnValue = this.parseBacktickString();
604
450
  this.skipSpaces();
605
451
  return { target, returnValue };
606
452
  }
607
-
608
- private parseMock(): Mock {
609
- return this.parseMockHead('with');
453
+ parseMock() {
454
+ return this.parseMockHead("with");
610
455
  }
611
- private parseAndMock(): Mock {
612
- return this.parseMockHead('and');
456
+ parseAndMock() {
457
+ return this.parseMockHead("and");
613
458
  }
614
-
615
- private parseIt(): It {
459
+ parseIt() {
616
460
  this.skipSpaces();
617
- this.expect('it');
461
+ this.expect("it");
618
462
  this.skipInlineSpaces();
619
463
  this.expect('"');
620
464
  const name = this.consumeWhile((c) => c !== '"');
621
465
  this.expect('"');
622
466
  this.skipSpaces();
623
-
624
- const mocks: Mock[] = [];
467
+ const mocks = [];
625
468
  while (true) {
626
469
  const m = this.tryParse(() => this.parseMock());
627
470
  if (!m) break;
628
471
  mocks.push(m);
629
472
  }
630
-
631
- this.expect('when');
473
+ this.expect("when");
632
474
  this.skipInlineSpaces();
633
475
  const when = this.parseWhen();
634
476
  this.skipSpaces();
635
-
636
477
  const input = this.tryParse(() => {
637
- this.expect('with');
478
+ this.expect("with");
638
479
  this.skipInlineSpaces();
639
- this.expect('input');
480
+ this.expect("input");
640
481
  this.skipInlineSpaces();
641
482
  const v = this.parseBacktickString();
642
483
  this.skipSpaces();
643
484
  return v;
644
- }) ?? undefined;
645
-
646
- const extraMocks: Mock[] = [];
485
+ }) ?? void 0;
486
+ const extraMocks = [];
647
487
  while (true) {
648
488
  const m = this.tryParse(() => this.parseAndMock());
649
489
  if (!m) break;
650
490
  extraMocks.push(m);
651
491
  this.skipSpaces();
652
492
  }
653
-
654
- const conditions: Condition[] = [];
493
+ const conditions = [];
655
494
  while (true) {
656
495
  const c = this.tryParse(() => this.parseCondition());
657
496
  if (!c) break;
658
497
  conditions.push(c);
659
498
  }
660
-
661
499
  return { name, mocks: [...mocks, ...extraMocks], when, input, conditions };
662
500
  }
663
-
664
- private parseDescribe(): Describe {
501
+ parseDescribe() {
665
502
  this.skipSpaces();
666
- this.expect('describe');
503
+ this.expect("describe");
667
504
  this.skipInlineSpaces();
668
505
  this.expect('"');
669
506
  const name = this.consumeWhile((c) => c !== '"');
670
507
  this.expect('"');
671
508
  this.skipSpaces();
672
-
673
- const mocks: Mock[] = [];
509
+ const mocks = [];
674
510
  while (true) {
675
511
  const m = this.tryParse(() => this.parseMock());
676
512
  if (!m) break;
677
513
  mocks.push(m);
678
514
  this.skipSpaces();
679
515
  }
680
-
681
- const tests: It[] = [];
516
+ const tests = [];
682
517
  while (true) {
683
518
  const it = this.tryParse(() => this.parseIt());
684
519
  if (!it) break;
685
520
  tests.push(it);
686
521
  }
687
-
688
522
  return { name, mocks, tests };
689
523
  }
690
- }
691
-
692
- export function parseProgram(text: string): Program {
524
+ };
525
+ function parseProgram(text) {
693
526
  const parser = new Parser(text);
694
527
  return parser.parseProgram();
695
528
  }
696
-
697
- export function parseProgramWithDiagnostics(text: string): { program: Program; diagnostics: ParseDiagnostic[] } {
529
+ function parseProgramWithDiagnostics(text) {
698
530
  const parser = new Parser(text);
699
531
  const program = parser.parseProgram();
700
532
  return { program, diagnostics: parser.getDiagnostics() };
701
533
  }
702
-
703
- export function getPipelineRanges(text: string): Map<string, { start: number; end: number }> {
534
+ function getPipelineRanges(text) {
704
535
  const parser = new Parser(text);
705
536
  parser.parseProgram();
706
537
  return parser.getPipelineRanges();
707
538
  }
708
-
709
- export function getVariableRanges(text: string): Map<string, { start: number; end: number }> {
539
+ function getVariableRanges(text) {
710
540
  const parser = new Parser(text);
711
541
  parser.parseProgram();
712
542
  return parser.getVariableRanges();
713
543
  }
714
-
715
- class ParseFailure extends Error {
716
- constructor(message: string, public at: number) {
544
+ var ParseFailure = class extends Error {
545
+ constructor(message, at) {
717
546
  super(message);
547
+ this.at = at;
548
+ }
549
+ };
550
+ function prettyPrint(program) {
551
+ const lines = [];
552
+ program.configs.forEach((config) => {
553
+ lines.push(`config ${config.name} {`);
554
+ config.properties.forEach((prop) => {
555
+ const value = formatConfigValue(prop.value);
556
+ lines.push(` ${prop.key}: ${value}`);
557
+ });
558
+ lines.push("}");
559
+ lines.push("");
560
+ });
561
+ program.variables.forEach((variable) => {
562
+ lines.push(`${variable.varType} ${variable.name} = \`${variable.value}\``);
563
+ });
564
+ if (program.variables.length > 0) lines.push("");
565
+ program.pipelines.forEach((pipeline) => {
566
+ lines.push(`pipeline ${pipeline.name} =`);
567
+ pipeline.pipeline.steps.forEach((step) => {
568
+ lines.push(formatPipelineStep(step));
569
+ });
570
+ lines.push("");
571
+ });
572
+ program.routes.forEach((route) => {
573
+ lines.push(`${route.method} ${route.path}`);
574
+ const pipelineLines = formatPipelineRef(route.pipeline);
575
+ pipelineLines.forEach((line) => lines.push(line));
576
+ lines.push("");
577
+ });
578
+ program.describes.forEach((describe) => {
579
+ lines.push(`describe "${describe.name}"`);
580
+ describe.mocks.forEach((mock) => {
581
+ lines.push(` with mock ${mock.target} returning \`${mock.returnValue}\``);
582
+ });
583
+ lines.push("");
584
+ describe.tests.forEach((test) => {
585
+ lines.push(` it "${test.name}"`);
586
+ test.mocks.forEach((mock) => {
587
+ lines.push(` with mock ${mock.target} returning \`${mock.returnValue}\``);
588
+ });
589
+ lines.push(` when ${formatWhen(test.when)}`);
590
+ if (test.input) {
591
+ lines.push(` with input \`${test.input}\``);
592
+ }
593
+ test.conditions.forEach((condition) => {
594
+ const condType = condition.conditionType.toLowerCase();
595
+ const jqPart = condition.jqExpr ? ` \`${condition.jqExpr}\`` : "";
596
+ lines.push(` ${condType} ${condition.field}${jqPart} ${condition.comparison} ${condition.value}`);
597
+ });
598
+ lines.push("");
599
+ });
600
+ });
601
+ return lines.join("\n").trim();
602
+ }
603
+ function formatConfigValue(value) {
604
+ switch (value.kind) {
605
+ case "String":
606
+ return `"${value.value}"`;
607
+ case "EnvVar":
608
+ return value.default ? `$${value.var} || "${value.default}"` : `$${value.var}`;
609
+ case "Boolean":
610
+ return value.value.toString();
611
+ case "Number":
612
+ return value.value.toString();
613
+ }
614
+ }
615
+ function formatPipelineStep(step, indent = " ") {
616
+ if (step.kind === "Regular") {
617
+ return `${indent}|> ${step.name}: ${formatStepConfig(step.config)}`;
618
+ } else {
619
+ const lines = [`${indent}|> result`];
620
+ step.branches.forEach((branch) => {
621
+ const branchName = branch.branchType.kind === "Ok" ? "ok" : branch.branchType.kind === "Default" ? "default" : branch.branchType.name;
622
+ lines.push(`${indent} ${branchName}(${branch.statusCode}):`);
623
+ branch.pipeline.steps.forEach((branchStep) => {
624
+ lines.push(formatPipelineStep(branchStep, indent + " "));
625
+ });
626
+ });
627
+ return lines.join("\n");
628
+ }
629
+ }
630
+ function formatStepConfig(config) {
631
+ if (config.includes("`")) {
632
+ return `\`${config}\``;
633
+ } else if (config.includes(" ") || config.includes("\n")) {
634
+ return `"${config}"`;
635
+ } else {
636
+ return config;
637
+ }
638
+ }
639
+ function formatPipelineRef(ref) {
640
+ if (ref.kind === "Named") {
641
+ return [` |> pipeline: ${ref.name}`];
642
+ } else {
643
+ const lines = [];
644
+ ref.pipeline.steps.forEach((step) => {
645
+ lines.push(formatPipelineStep(step));
646
+ });
647
+ return lines;
648
+ }
649
+ }
650
+ function formatWhen(when) {
651
+ switch (when.kind) {
652
+ case "CallingRoute":
653
+ return `calling ${when.method} ${when.path}`;
654
+ case "ExecutingPipeline":
655
+ return `executing pipeline ${when.name}`;
656
+ case "ExecutingVariable":
657
+ return `executing variable ${when.varType} ${when.name}`;
718
658
  }
719
659
  }
720
-
721
660
  if (import.meta.url === `file://${process.argv[1]}`) {
722
- const fs = await import('node:fs/promises');
661
+ (async () => {
662
+ const fs = await import("fs/promises");
723
663
  const path = process.argv[2];
724
664
  if (!path) {
725
- console.error('Usage: node dist/parser.js <file.wp>');
726
- process.exit(1);
665
+ console.error("Usage: node dist/index.mjs <file.wp>");
666
+ process.exit(1);
727
667
  }
728
- const src = await fs.readFile(path, 'utf8');
668
+ const src = await fs.readFile(path, "utf8");
729
669
  const program = parseProgram(src);
730
670
  console.log(JSON.stringify(program, null, 2));
671
+ })();
731
672
  }
673
+ export {
674
+ getPipelineRanges,
675
+ getVariableRanges,
676
+ parseProgram,
677
+ parseProgramWithDiagnostics,
678
+ prettyPrint
679
+ };