@optique/core 0.5.0-dev.79 → 0.5.0-dev.80

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.
@@ -0,0 +1,754 @@
1
+ const require_message = require('./message.cjs');
2
+
3
+ //#region src/constructs.ts
4
+ /**
5
+ * @since 0.5.0
6
+ */
7
+ function or(...args) {
8
+ let parsers;
9
+ let options;
10
+ if (args.length > 0 && args[args.length - 1] && typeof args[args.length - 1] === "object" && !("$valueType" in args[args.length - 1])) {
11
+ options = args[args.length - 1];
12
+ parsers = args.slice(0, -1);
13
+ } else {
14
+ parsers = args;
15
+ options = void 0;
16
+ }
17
+ return {
18
+ $valueType: [],
19
+ $stateType: [],
20
+ priority: Math.max(...parsers.map((p) => p.priority)),
21
+ usage: [{
22
+ type: "exclusive",
23
+ terms: parsers.map((p) => p.usage)
24
+ }],
25
+ initialState: void 0,
26
+ complete(state) {
27
+ if (state == null) return {
28
+ success: false,
29
+ error: options?.errors?.noMatch ?? require_message.message`No matching option or command found.`
30
+ };
31
+ const [i, result] = state;
32
+ if (result.success) return parsers[i].complete(result.next.state);
33
+ return {
34
+ success: false,
35
+ error: result.error
36
+ };
37
+ },
38
+ parse(context) {
39
+ let error = {
40
+ consumed: 0,
41
+ error: context.buffer.length < 1 ? options?.errors?.noMatch ?? require_message.message`No matching option or command found.` : (() => {
42
+ const token = context.buffer[0];
43
+ const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
44
+ return options?.errors?.unexpectedInput != null ? typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput : defaultMsg;
45
+ })()
46
+ };
47
+ const orderedParsers = parsers.map((p, i) => [p, i]);
48
+ orderedParsers.sort(([_, a], [__, b]) => context.state?.[0] === a ? -1 : context.state?.[0] === b ? 1 : a - b);
49
+ for (const [parser, i] of orderedParsers) {
50
+ const result = parser.parse({
51
+ ...context,
52
+ state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
53
+ });
54
+ if (result.success && result.consumed.length > 0) {
55
+ if (context.state?.[0] !== i && context.state?.[1].success) return {
56
+ success: false,
57
+ consumed: context.buffer.length - result.next.buffer.length,
58
+ error: require_message.message`${require_message.values(context.state[1].consumed)} and ${require_message.values(result.consumed)} cannot be used together.`
59
+ };
60
+ return {
61
+ success: true,
62
+ next: {
63
+ ...context,
64
+ buffer: result.next.buffer,
65
+ optionsTerminated: result.next.optionsTerminated,
66
+ state: [i, result]
67
+ },
68
+ consumed: result.consumed
69
+ };
70
+ } else if (!result.success && error.consumed < result.consumed) error = result;
71
+ }
72
+ return {
73
+ ...error,
74
+ success: false
75
+ };
76
+ },
77
+ getDocFragments(state, _defaultValue) {
78
+ let description;
79
+ let fragments;
80
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }, void 0).fragments);
81
+ else {
82
+ const [index, parserResult] = state.state;
83
+ const innerState = parserResult.success ? {
84
+ kind: "available",
85
+ state: parserResult.next.state
86
+ } : { kind: "unavailable" };
87
+ const docFragments = parsers[index].getDocFragments(innerState, void 0);
88
+ description = docFragments.description;
89
+ fragments = docFragments.fragments;
90
+ }
91
+ const entries = fragments.filter((f) => f.type === "entry");
92
+ const sections = [];
93
+ for (const fragment of fragments) {
94
+ if (fragment.type !== "section") continue;
95
+ if (fragment.title == null) entries.push(...fragment.entries);
96
+ else sections.push(fragment);
97
+ }
98
+ return {
99
+ description,
100
+ fragments: [...sections.map((s) => ({
101
+ ...s,
102
+ type: "section"
103
+ })), {
104
+ type: "section",
105
+ entries
106
+ }]
107
+ };
108
+ }
109
+ };
110
+ }
111
+ /**
112
+ * @since 0.5.0
113
+ */
114
+ function longestMatch(...args) {
115
+ let parsers;
116
+ let options;
117
+ if (args.length > 0 && args[args.length - 1] && typeof args[args.length - 1] === "object" && !("$valueType" in args[args.length - 1])) {
118
+ options = args[args.length - 1];
119
+ parsers = args.slice(0, -1);
120
+ } else {
121
+ parsers = args;
122
+ options = void 0;
123
+ }
124
+ return {
125
+ $valueType: [],
126
+ $stateType: [],
127
+ priority: Math.max(...parsers.map((p) => p.priority)),
128
+ usage: [{
129
+ type: "exclusive",
130
+ terms: parsers.map((p) => p.usage)
131
+ }],
132
+ initialState: void 0,
133
+ complete(state) {
134
+ if (state == null) return {
135
+ success: false,
136
+ error: options?.errors?.noMatch ?? require_message.message`No matching option or command found.`
137
+ };
138
+ const [i, result] = state;
139
+ if (result.success) return parsers[i].complete(result.next.state);
140
+ return {
141
+ success: false,
142
+ error: result.error
143
+ };
144
+ },
145
+ parse(context) {
146
+ let bestMatch = null;
147
+ let error = {
148
+ consumed: 0,
149
+ error: context.buffer.length < 1 ? options?.errors?.noMatch ?? require_message.message`No matching option or command found.` : (() => {
150
+ const token = context.buffer[0];
151
+ const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
152
+ return options?.errors?.unexpectedInput != null ? typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput : defaultMsg;
153
+ })()
154
+ };
155
+ for (let i = 0; i < parsers.length; i++) {
156
+ const parser = parsers[i];
157
+ const result = parser.parse({
158
+ ...context,
159
+ state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
160
+ });
161
+ if (result.success) {
162
+ const consumed = context.buffer.length - result.next.buffer.length;
163
+ if (bestMatch === null || consumed > bestMatch.consumed) bestMatch = {
164
+ index: i,
165
+ result,
166
+ consumed
167
+ };
168
+ } else if (error.consumed < result.consumed) error = result;
169
+ }
170
+ if (bestMatch && bestMatch.result.success) return {
171
+ success: true,
172
+ next: {
173
+ ...context,
174
+ buffer: bestMatch.result.next.buffer,
175
+ optionsTerminated: bestMatch.result.next.optionsTerminated,
176
+ state: [bestMatch.index, bestMatch.result]
177
+ },
178
+ consumed: bestMatch.result.consumed
179
+ };
180
+ return {
181
+ ...error,
182
+ success: false
183
+ };
184
+ },
185
+ getDocFragments(state, _defaultValue) {
186
+ let description;
187
+ let fragments;
188
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
189
+ else {
190
+ const [i, result] = state.state;
191
+ if (result.success) {
192
+ const docResult = parsers[i].getDocFragments({
193
+ kind: "available",
194
+ state: result.next.state
195
+ });
196
+ description = docResult.description;
197
+ fragments = docResult.fragments;
198
+ } else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
199
+ }
200
+ return {
201
+ description,
202
+ fragments
203
+ };
204
+ }
205
+ };
206
+ }
207
+ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
208
+ const label = typeof labelOrParsers === "string" ? labelOrParsers : void 0;
209
+ let parsers;
210
+ let options = {};
211
+ if (typeof labelOrParsers === "string") {
212
+ parsers = maybeParsersOrOptions;
213
+ options = maybeOptions ?? {};
214
+ } else {
215
+ parsers = labelOrParsers;
216
+ options = maybeParsersOrOptions ?? {};
217
+ }
218
+ const parserPairs = Object.entries(parsers);
219
+ parserPairs.sort(([_, parserA], [__, parserB]) => parserB.priority - parserA.priority);
220
+ return {
221
+ $valueType: [],
222
+ $stateType: [],
223
+ priority: Math.max(...Object.values(parsers).map((p) => p.priority)),
224
+ usage: parserPairs.flatMap(([_, p]) => p.usage),
225
+ initialState: Object.fromEntries(Object.entries(parsers).map(([key, parser]) => [key, parser.initialState])),
226
+ parse(context) {
227
+ let error = {
228
+ consumed: 0,
229
+ error: context.buffer.length > 0 ? (() => {
230
+ const token = context.buffer[0];
231
+ const customMessage = options.errors?.unexpectedInput;
232
+ return customMessage ? typeof customMessage === "function" ? customMessage(token) : customMessage : require_message.message`Unexpected option or argument: ${token}.`;
233
+ })() : options.errors?.endOfInput ?? require_message.message`Expected an option or argument, but got end of input.`
234
+ };
235
+ let currentContext = context;
236
+ let anySuccess = false;
237
+ const allConsumed = [];
238
+ let madeProgress = true;
239
+ while (madeProgress && currentContext.buffer.length > 0) {
240
+ madeProgress = false;
241
+ for (const [field, parser] of parserPairs) {
242
+ const result = parser.parse({
243
+ ...currentContext,
244
+ state: currentContext.state && typeof currentContext.state === "object" && field in currentContext.state ? currentContext.state[field] : parser.initialState
245
+ });
246
+ if (result.success && result.consumed.length > 0) {
247
+ currentContext = {
248
+ ...currentContext,
249
+ buffer: result.next.buffer,
250
+ optionsTerminated: result.next.optionsTerminated,
251
+ state: {
252
+ ...currentContext.state,
253
+ [field]: result.next.state
254
+ }
255
+ };
256
+ allConsumed.push(...result.consumed);
257
+ anySuccess = true;
258
+ madeProgress = true;
259
+ break;
260
+ } else if (!result.success && error.consumed < result.consumed) error = result;
261
+ }
262
+ }
263
+ if (anySuccess) return {
264
+ success: true,
265
+ next: currentContext,
266
+ consumed: allConsumed
267
+ };
268
+ if (context.buffer.length === 0) {
269
+ let allCanComplete = true;
270
+ for (const [field, parser] of parserPairs) {
271
+ const fieldState = context.state && typeof context.state === "object" && field in context.state ? context.state[field] : parser.initialState;
272
+ const completeResult = parser.complete(fieldState);
273
+ if (!completeResult.success) {
274
+ allCanComplete = false;
275
+ break;
276
+ }
277
+ }
278
+ if (allCanComplete) return {
279
+ success: true,
280
+ next: context,
281
+ consumed: []
282
+ };
283
+ }
284
+ return {
285
+ ...error,
286
+ success: false
287
+ };
288
+ },
289
+ complete(state) {
290
+ const result = {};
291
+ for (const field in state) {
292
+ if (!(field in parsers)) continue;
293
+ const valueResult = parsers[field].complete(state[field]);
294
+ if (valueResult.success) result[field] = valueResult.value;
295
+ else return {
296
+ success: false,
297
+ error: valueResult.error
298
+ };
299
+ }
300
+ return {
301
+ success: true,
302
+ value: result
303
+ };
304
+ },
305
+ getDocFragments(state, defaultValue) {
306
+ const fragments = parserPairs.flatMap(([field, p]) => {
307
+ const fieldState = state.kind === "unavailable" ? { kind: "unavailable" } : {
308
+ kind: "available",
309
+ state: state.state[field]
310
+ };
311
+ return p.getDocFragments(fieldState, defaultValue?.[field]).fragments;
312
+ });
313
+ const entries = fragments.filter((d) => d.type === "entry");
314
+ const sections = [];
315
+ for (const fragment of fragments) {
316
+ if (fragment.type !== "section") continue;
317
+ if (fragment.title == null) entries.push(...fragment.entries);
318
+ else sections.push(fragment);
319
+ }
320
+ const section = {
321
+ title: label,
322
+ entries
323
+ };
324
+ sections.push(section);
325
+ return { fragments: sections.map((s) => ({
326
+ ...s,
327
+ type: "section"
328
+ })) };
329
+ }
330
+ };
331
+ }
332
+ function tuple(labelOrParsers, maybeParsers) {
333
+ const label = typeof labelOrParsers === "string" ? labelOrParsers : void 0;
334
+ const parsers = typeof labelOrParsers === "string" ? maybeParsers : labelOrParsers;
335
+ return {
336
+ $valueType: [],
337
+ $stateType: [],
338
+ usage: parsers.toSorted((a, b) => b.priority - a.priority).flatMap((p) => p.usage),
339
+ priority: parsers.length > 0 ? Math.max(...parsers.map((p) => p.priority)) : 0,
340
+ initialState: parsers.map((parser) => parser.initialState),
341
+ parse(context) {
342
+ let currentContext = context;
343
+ const allConsumed = [];
344
+ const matchedParsers = /* @__PURE__ */ new Set();
345
+ while (matchedParsers.size < parsers.length) {
346
+ let foundMatch = false;
347
+ let error = {
348
+ consumed: 0,
349
+ error: require_message.message`No remaining parsers could match the input.`
350
+ };
351
+ const remainingParsers = parsers.map((parser, index) => [parser, index]).filter(([_, index]) => !matchedParsers.has(index)).sort(([parserA], [parserB]) => parserB.priority - parserA.priority);
352
+ for (const [parser, index] of remainingParsers) {
353
+ const result = parser.parse({
354
+ ...currentContext,
355
+ state: currentContext.state[index]
356
+ });
357
+ if (result.success && result.consumed.length > 0) {
358
+ currentContext = {
359
+ ...currentContext,
360
+ buffer: result.next.buffer,
361
+ optionsTerminated: result.next.optionsTerminated,
362
+ state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
363
+ };
364
+ allConsumed.push(...result.consumed);
365
+ matchedParsers.add(index);
366
+ foundMatch = true;
367
+ break;
368
+ } else if (!result.success && error.consumed < result.consumed) error = result;
369
+ }
370
+ if (!foundMatch) for (const [parser, index] of remainingParsers) {
371
+ const result = parser.parse({
372
+ ...currentContext,
373
+ state: currentContext.state[index]
374
+ });
375
+ if (result.success && result.consumed.length < 1) {
376
+ currentContext = {
377
+ ...currentContext,
378
+ state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
379
+ };
380
+ matchedParsers.add(index);
381
+ foundMatch = true;
382
+ break;
383
+ } else if (!result.success && result.consumed < 1) {
384
+ matchedParsers.add(index);
385
+ foundMatch = true;
386
+ break;
387
+ }
388
+ }
389
+ if (!foundMatch) return {
390
+ ...error,
391
+ success: false
392
+ };
393
+ }
394
+ return {
395
+ success: true,
396
+ next: currentContext,
397
+ consumed: allConsumed
398
+ };
399
+ },
400
+ complete(state) {
401
+ const result = [];
402
+ for (let i = 0; i < parsers.length; i++) {
403
+ const valueResult = parsers[i].complete(state[i]);
404
+ if (valueResult.success) result[i] = valueResult.value;
405
+ else return {
406
+ success: false,
407
+ error: valueResult.error
408
+ };
409
+ }
410
+ return {
411
+ success: true,
412
+ value: result
413
+ };
414
+ },
415
+ getDocFragments(state, defaultValue) {
416
+ const fragments = parsers.flatMap((p, i) => {
417
+ const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
418
+ kind: "available",
419
+ state: state.state[i]
420
+ };
421
+ return p.getDocFragments(indexState, defaultValue?.[i]).fragments;
422
+ });
423
+ const entries = fragments.filter((d) => d.type === "entry");
424
+ const sections = [];
425
+ for (const fragment of fragments) {
426
+ if (fragment.type !== "section") continue;
427
+ if (fragment.title == null) entries.push(...fragment.entries);
428
+ else sections.push(fragment);
429
+ }
430
+ const section = {
431
+ title: label,
432
+ entries
433
+ };
434
+ sections.push(section);
435
+ return { fragments: sections.map((s) => ({
436
+ ...s,
437
+ type: "section"
438
+ })) };
439
+ },
440
+ [Symbol.for("Deno.customInspect")]() {
441
+ const parsersStr = parsers.length === 1 ? `[1 parser]` : `[${parsers.length} parsers]`;
442
+ return label ? `tuple(${JSON.stringify(label)}, ${parsersStr})` : `tuple(${parsersStr})`;
443
+ }
444
+ };
445
+ }
446
+ function merge(...args) {
447
+ const label = typeof args[0] === "string" ? args[0] : void 0;
448
+ let parsers = typeof args[0] === "string" ? args.slice(1) : args;
449
+ parsers = parsers.toSorted((a, b) => b.priority - a.priority);
450
+ const initialState = {};
451
+ for (const parser of parsers) if (parser.initialState && typeof parser.initialState === "object") for (const field in parser.initialState) initialState[field] = parser.initialState[field];
452
+ return {
453
+ $valueType: [],
454
+ $stateType: [],
455
+ priority: Math.max(...parsers.map((p) => p.priority)),
456
+ usage: parsers.flatMap((p) => p.usage),
457
+ initialState,
458
+ parse(context) {
459
+ for (let i = 0; i < parsers.length; i++) {
460
+ const parser = parsers[i];
461
+ let parserState;
462
+ if (parser.initialState === void 0) parserState = void 0;
463
+ else if (parser.initialState && typeof parser.initialState === "object") if (context.state && typeof context.state === "object") {
464
+ const extractedState = {};
465
+ for (const field in parser.initialState) extractedState[field] = field in context.state ? context.state[field] : parser.initialState[field];
466
+ parserState = extractedState;
467
+ } else parserState = parser.initialState;
468
+ else parserState = parser.initialState;
469
+ const result = parser.parse({
470
+ ...context,
471
+ state: parserState
472
+ });
473
+ if (result.success) {
474
+ let newState;
475
+ if (parser.initialState === void 0) newState = {
476
+ ...context.state,
477
+ [`__parser_${i}`]: result.next.state
478
+ };
479
+ else newState = {
480
+ ...context.state,
481
+ ...result.next.state
482
+ };
483
+ return {
484
+ success: true,
485
+ next: {
486
+ ...context,
487
+ buffer: result.next.buffer,
488
+ optionsTerminated: result.next.optionsTerminated,
489
+ state: newState
490
+ },
491
+ consumed: result.consumed
492
+ };
493
+ } else if (result.consumed < 1) continue;
494
+ else return result;
495
+ }
496
+ return {
497
+ success: false,
498
+ consumed: 0,
499
+ error: require_message.message`No matching option or argument found.`
500
+ };
501
+ },
502
+ complete(state) {
503
+ const object$1 = {};
504
+ for (let i = 0; i < parsers.length; i++) {
505
+ const parser = parsers[i];
506
+ let parserState;
507
+ if (parser.initialState === void 0) {
508
+ const key = `__parser_${i}`;
509
+ if (state && typeof state === "object" && key in state) parserState = state[key];
510
+ else parserState = void 0;
511
+ } else if (parser.initialState && typeof parser.initialState === "object") if (state && typeof state === "object") {
512
+ const extractedState = {};
513
+ for (const field in parser.initialState) extractedState[field] = field in state ? state[field] : parser.initialState[field];
514
+ parserState = extractedState;
515
+ } else parserState = parser.initialState;
516
+ else parserState = parser.initialState;
517
+ const result = parser.complete(parserState);
518
+ if (!result.success) return result;
519
+ for (const field in result.value) object$1[field] = result.value[field];
520
+ }
521
+ return {
522
+ success: true,
523
+ value: object$1
524
+ };
525
+ },
526
+ getDocFragments(state, _defaultValue) {
527
+ const fragments = parsers.flatMap((p) => {
528
+ const parserState = p.initialState === void 0 ? { kind: "unavailable" } : state.kind === "unavailable" ? { kind: "unavailable" } : {
529
+ kind: "available",
530
+ state: state.state
531
+ };
532
+ return p.getDocFragments(parserState, void 0).fragments;
533
+ });
534
+ const entries = fragments.filter((f) => f.type === "entry");
535
+ const sections = [];
536
+ for (const fragment of fragments) {
537
+ if (fragment.type !== "section") continue;
538
+ if (fragment.title == null) entries.push(...fragment.entries);
539
+ else sections.push(fragment);
540
+ }
541
+ if (label) {
542
+ const labeledSection = {
543
+ title: label,
544
+ entries
545
+ };
546
+ sections.push(labeledSection);
547
+ return { fragments: sections.map((s) => ({
548
+ ...s,
549
+ type: "section"
550
+ })) };
551
+ }
552
+ return { fragments: [...sections.map((s) => ({
553
+ ...s,
554
+ type: "section"
555
+ })), {
556
+ type: "section",
557
+ entries
558
+ }] };
559
+ }
560
+ };
561
+ }
562
+ function concat(...parsers) {
563
+ const initialState = parsers.map((parser) => parser.initialState);
564
+ return {
565
+ $valueType: [],
566
+ $stateType: [],
567
+ priority: parsers.length > 0 ? Math.max(...parsers.map((p) => p.priority)) : 0,
568
+ usage: parsers.flatMap((p) => p.usage),
569
+ initialState,
570
+ parse(context) {
571
+ let currentContext = context;
572
+ const allConsumed = [];
573
+ const matchedParsers = /* @__PURE__ */ new Set();
574
+ while (matchedParsers.size < parsers.length) {
575
+ let foundMatch = false;
576
+ let error = {
577
+ consumed: 0,
578
+ error: require_message.message`No remaining parsers could match the input.`
579
+ };
580
+ const remainingParsers = parsers.map((parser, index) => [parser, index]).filter(([_, index]) => !matchedParsers.has(index)).sort(([parserA], [parserB]) => parserB.priority - parserA.priority);
581
+ for (const [parser, index] of remainingParsers) {
582
+ const result = parser.parse({
583
+ ...currentContext,
584
+ state: currentContext.state[index]
585
+ });
586
+ if (result.success && result.consumed.length > 0) {
587
+ currentContext = {
588
+ ...currentContext,
589
+ buffer: result.next.buffer,
590
+ optionsTerminated: result.next.optionsTerminated,
591
+ state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
592
+ };
593
+ allConsumed.push(...result.consumed);
594
+ matchedParsers.add(index);
595
+ foundMatch = true;
596
+ break;
597
+ } else if (!result.success && error.consumed < result.consumed) error = result;
598
+ }
599
+ if (!foundMatch) for (const [parser, index] of remainingParsers) {
600
+ const result = parser.parse({
601
+ ...currentContext,
602
+ state: currentContext.state[index]
603
+ });
604
+ if (result.success && result.consumed.length < 1) {
605
+ currentContext = {
606
+ ...currentContext,
607
+ state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
608
+ };
609
+ matchedParsers.add(index);
610
+ foundMatch = true;
611
+ break;
612
+ } else if (!result.success && result.consumed < 1) {
613
+ matchedParsers.add(index);
614
+ foundMatch = true;
615
+ break;
616
+ }
617
+ }
618
+ if (!foundMatch) return {
619
+ ...error,
620
+ success: false
621
+ };
622
+ }
623
+ return {
624
+ success: true,
625
+ next: currentContext,
626
+ consumed: allConsumed
627
+ };
628
+ },
629
+ complete(state) {
630
+ const results = [];
631
+ for (let i = 0; i < parsers.length; i++) {
632
+ const parser = parsers[i];
633
+ const parserState = state[i];
634
+ const result = parser.complete(parserState);
635
+ if (!result.success) return result;
636
+ if (Array.isArray(result.value)) results.push(...result.value);
637
+ else results.push(result.value);
638
+ }
639
+ return {
640
+ success: true,
641
+ value: results
642
+ };
643
+ },
644
+ getDocFragments(state, _defaultValue) {
645
+ const fragments = parsers.flatMap((p, index) => {
646
+ const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
647
+ kind: "available",
648
+ state: state.state[index]
649
+ };
650
+ return p.getDocFragments(indexState, void 0).fragments;
651
+ });
652
+ const entries = fragments.filter((f) => f.type === "entry");
653
+ const sections = [];
654
+ for (const fragment of fragments) {
655
+ if (fragment.type !== "section") continue;
656
+ if (fragment.title == null) entries.push(...fragment.entries);
657
+ else sections.push(fragment);
658
+ }
659
+ const result = [...sections.map((s) => ({
660
+ ...s,
661
+ type: "section"
662
+ }))];
663
+ if (entries.length > 0) result.push({
664
+ type: "section",
665
+ entries
666
+ });
667
+ return { fragments: result };
668
+ }
669
+ };
670
+ }
671
+ /**
672
+ * Wraps a parser with a group label for documentation purposes.
673
+ *
674
+ * The `group()` function is a documentation-only wrapper that applies a label
675
+ * to any parser for help text organization. This allows you to use clean code
676
+ * structure with combinators like {@link merge} while maintaining well-organized
677
+ * help text through group labeling.
678
+ *
679
+ * The wrapped parser has identical parsing behavior but generates documentation
680
+ * fragments wrapped in a labeled section. This is particularly useful when
681
+ * combining parsers using {@link merge}—you can wrap the merged result with
682
+ * `group()` to add a section header in help output.
683
+ *
684
+ * @example
685
+ * ```typescript
686
+ * const apiOptions = merge(
687
+ * object({ endpoint: option("--endpoint", string()) }),
688
+ * object({ timeout: option("--timeout", integer()) })
689
+ * );
690
+ *
691
+ * const groupedApiOptions = group("API Options", apiOptions);
692
+ * // Now produces a labeled "API Options" section in help text
693
+ * ```
694
+ *
695
+ * @example
696
+ * ```typescript
697
+ * // Can be used with any parser, not just merge()
698
+ * const verboseGroup = group("Verbosity", object({
699
+ * verbose: option("-v", "--verbose"),
700
+ * quiet: option("-q", "--quiet")
701
+ * }));
702
+ * ```
703
+ *
704
+ * @template TValue The value type of the wrapped parser.
705
+ * @template TState The state type of the wrapped parser.
706
+ * @param label A descriptive label for this parser group, used for
707
+ * documentation and help text organization.
708
+ * @param parser The parser to wrap with a group label.
709
+ * @returns A new parser that behaves identically to the input parser
710
+ * but generates documentation within a labeled section.
711
+ * @since 0.4.0
712
+ */
713
+ function group(label, parser) {
714
+ return {
715
+ $valueType: parser.$valueType,
716
+ $stateType: parser.$stateType,
717
+ priority: parser.priority,
718
+ usage: parser.usage,
719
+ initialState: parser.initialState,
720
+ parse: (context) => parser.parse(context),
721
+ complete: (state) => parser.complete(state),
722
+ getDocFragments: (state, defaultValue) => {
723
+ const { description, fragments } = parser.getDocFragments(state, defaultValue);
724
+ const allEntries = [];
725
+ const titledSections = [];
726
+ for (const fragment of fragments) if (fragment.type === "entry") allEntries.push(fragment);
727
+ else if (fragment.type === "section") if (fragment.title) titledSections.push(fragment);
728
+ else allEntries.push(...fragment.entries);
729
+ const labeledSection = {
730
+ title: label,
731
+ entries: allEntries
732
+ };
733
+ return {
734
+ description,
735
+ fragments: [...titledSections.map((s) => ({
736
+ ...s,
737
+ type: "section"
738
+ })), {
739
+ type: "section",
740
+ ...labeledSection
741
+ }]
742
+ };
743
+ }
744
+ };
745
+ }
746
+
747
+ //#endregion
748
+ exports.concat = concat;
749
+ exports.group = group;
750
+ exports.longestMatch = longestMatch;
751
+ exports.merge = merge;
752
+ exports.object = object;
753
+ exports.or = or;
754
+ exports.tuple = tuple;