@optique/core 1.0.0-dev.1776 → 1.0.0-dev.1785
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/constructs.cjs +621 -58
- package/dist/constructs.js +621 -58
- package/dist/context.d.cts +8 -3
- package/dist/context.d.ts +8 -3
- package/dist/facade.cjs +96 -41
- package/dist/facade.d.cts +13 -5
- package/dist/facade.d.ts +13 -5
- package/dist/facade.js +97 -42
- package/dist/modifiers.cjs +102 -0
- package/dist/modifiers.js +102 -0
- package/dist/phase2-seed.cjs +59 -0
- package/dist/phase2-seed.js +56 -0
- package/dist/primitives.cjs +29 -0
- package/dist/primitives.js +29 -0
- package/package.json +1 -1
package/dist/context.d.cts
CHANGED
|
@@ -126,9 +126,14 @@ interface SourceContext<TRequiredOptions = void> {
|
|
|
126
126
|
*
|
|
127
127
|
* 1. *First call*: `parsed` is `undefined`. Static contexts should return
|
|
128
128
|
* their annotations, while dynamic contexts should return an empty object.
|
|
129
|
-
* 2. *Second call*: `parsed` contains the
|
|
130
|
-
*
|
|
131
|
-
*
|
|
129
|
+
* 2. *Second call*: `parsed` contains the first pass result, or a
|
|
130
|
+
* best-effort partial value extracted from parser state when the first
|
|
131
|
+
* pass reached a usable intermediate state but still did not complete
|
|
132
|
+
* successfully. Dynamic contexts can use this to load external data
|
|
133
|
+
* (e.g., reading a config file whose path was determined in the first
|
|
134
|
+
* pass). Deferred or otherwise unresolved fields may be `undefined`.
|
|
135
|
+
* If the runner cannot extract a usable value at all, this second call
|
|
136
|
+
* is skipped and the original parse failure is reported instead.
|
|
132
137
|
*
|
|
133
138
|
* @param parsed Optional parsed result from a previous parse pass.
|
|
134
139
|
* Static contexts can ignore this parameter.
|
package/dist/context.d.ts
CHANGED
|
@@ -126,9 +126,14 @@ interface SourceContext<TRequiredOptions = void> {
|
|
|
126
126
|
*
|
|
127
127
|
* 1. *First call*: `parsed` is `undefined`. Static contexts should return
|
|
128
128
|
* their annotations, while dynamic contexts should return an empty object.
|
|
129
|
-
* 2. *Second call*: `parsed` contains the
|
|
130
|
-
*
|
|
131
|
-
*
|
|
129
|
+
* 2. *Second call*: `parsed` contains the first pass result, or a
|
|
130
|
+
* best-effort partial value extracted from parser state when the first
|
|
131
|
+
* pass reached a usable intermediate state but still did not complete
|
|
132
|
+
* successfully. Dynamic contexts can use this to load external data
|
|
133
|
+
* (e.g., reading a config file whose path was determined in the first
|
|
134
|
+
* pass). Deferred or otherwise unresolved fields may be `undefined`.
|
|
135
|
+
* If the runner cannot extract a usable value at all, this second call
|
|
136
|
+
* is skipped and the original parse failure is reported instead.
|
|
132
137
|
*
|
|
133
138
|
* @param parsed Optional parsed result from a previous parse pass.
|
|
134
139
|
* Static contexts can ignore this parameter.
|
package/dist/facade.cjs
CHANGED
|
@@ -5,10 +5,13 @@ const require_validate = require('./validate.cjs');
|
|
|
5
5
|
const require_usage = require('./usage.cjs');
|
|
6
6
|
const require_doc = require('./doc.cjs');
|
|
7
7
|
const require_mode_dispatch = require('./mode-dispatch.cjs');
|
|
8
|
+
const require_input_trace = require('./input-trace.cjs');
|
|
9
|
+
const require_phase2_seed = require('./phase2-seed.cjs');
|
|
8
10
|
const require_modifiers = require('./modifiers.cjs');
|
|
9
11
|
const require_valueparser = require('./valueparser.cjs');
|
|
10
12
|
const require_primitives = require('./primitives.cjs');
|
|
11
13
|
const require_parser = require('./parser.cjs');
|
|
14
|
+
const require_dependency_runtime = require('./dependency-runtime.cjs');
|
|
12
15
|
const require_constructs = require('./constructs.cjs');
|
|
13
16
|
|
|
14
17
|
//#region src/facade.ts
|
|
@@ -89,6 +92,60 @@ function prepareParsedForContexts(parsed, deferred, deferredKeys) {
|
|
|
89
92
|
function withPreparedParsedForContext(context, preparedParsed, run) {
|
|
90
93
|
return run(finalizeParsedForContext(context, preparedParsed));
|
|
91
94
|
}
|
|
95
|
+
function isBufferUnchanged(previous, current) {
|
|
96
|
+
return current.length > 0 && current.length === previous.length && current.every((item, i) => item === previous[i]);
|
|
97
|
+
}
|
|
98
|
+
function createPhase2SeedExec(parser, context) {
|
|
99
|
+
const exec = {
|
|
100
|
+
usage: parser.usage,
|
|
101
|
+
phase: "parse",
|
|
102
|
+
path: [],
|
|
103
|
+
trace: require_input_trace.createInputTrace()
|
|
104
|
+
};
|
|
105
|
+
const runtime = require_dependency_runtime.createDependencyRuntimeContext();
|
|
106
|
+
return {
|
|
107
|
+
...exec,
|
|
108
|
+
phase: "complete",
|
|
109
|
+
dependencyRuntime: runtime,
|
|
110
|
+
dependencyRegistry: runtime.registry,
|
|
111
|
+
trace: context.exec?.trace ?? context.trace ?? exec.trace
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createPhase2SeedContext(parser, args) {
|
|
115
|
+
const exec = {
|
|
116
|
+
usage: parser.usage,
|
|
117
|
+
phase: "parse",
|
|
118
|
+
path: [],
|
|
119
|
+
trace: require_input_trace.createInputTrace()
|
|
120
|
+
};
|
|
121
|
+
return require_parser.createParserContext({
|
|
122
|
+
buffer: args,
|
|
123
|
+
state: parser.initialState,
|
|
124
|
+
optionsTerminated: false
|
|
125
|
+
}, exec);
|
|
126
|
+
}
|
|
127
|
+
function extractPhase2SeedSync(parser, args) {
|
|
128
|
+
let context = createPhase2SeedContext(parser, args);
|
|
129
|
+
do {
|
|
130
|
+
const result = parser.parse(context);
|
|
131
|
+
if (!result.success) return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
132
|
+
const previousBuffer = context.buffer;
|
|
133
|
+
context = result.next;
|
|
134
|
+
if (isBufferUnchanged(previousBuffer, context.buffer)) return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
135
|
+
} while (context.buffer.length > 0);
|
|
136
|
+
return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
137
|
+
}
|
|
138
|
+
async function extractPhase2SeedAsync(parser, args) {
|
|
139
|
+
let context = createPhase2SeedContext(parser, args);
|
|
140
|
+
do {
|
|
141
|
+
const result = await parser.parse(context);
|
|
142
|
+
if (!result.success) return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
143
|
+
const previousBuffer = context.buffer;
|
|
144
|
+
context = result.next;
|
|
145
|
+
if (isBufferUnchanged(previousBuffer, context.buffer)) return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
146
|
+
} while (context.buffer.length > 0);
|
|
147
|
+
return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
148
|
+
}
|
|
92
149
|
/**
|
|
93
150
|
* Creates help parsers based on the sub-config.
|
|
94
151
|
*/
|
|
@@ -1197,30 +1254,13 @@ async function runWithBody(parser, programName, contexts, args, options) {
|
|
|
1197
1254
|
return Promise.resolve(runParser(augmentedParser, programName, args, options));
|
|
1198
1255
|
}
|
|
1199
1256
|
const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
let firstPassDeferredKeys;
|
|
1203
|
-
let firstPassFailed = false;
|
|
1204
|
-
try {
|
|
1205
|
-
if (parser.$mode === "async") firstPassResult = await require_parser.parseAsync(augmentedParser1, args);
|
|
1206
|
-
else firstPassResult = require_parser.parseSync(augmentedParser1, args);
|
|
1207
|
-
if (typeof firstPassResult === "object" && firstPassResult !== null && "success" in firstPassResult) {
|
|
1208
|
-
const result = firstPassResult;
|
|
1209
|
-
if (result.success) {
|
|
1210
|
-
firstPassResult = result.value;
|
|
1211
|
-
firstPassDeferred = result.deferred;
|
|
1212
|
-
firstPassDeferredKeys = result.deferredKeys;
|
|
1213
|
-
} else firstPassFailed = true;
|
|
1214
|
-
}
|
|
1215
|
-
} catch {
|
|
1216
|
-
firstPassFailed = true;
|
|
1217
|
-
}
|
|
1218
|
-
if (firstPassFailed) {
|
|
1257
|
+
const firstPassSeed = await require_mode_dispatch.dispatchByMode(parser.$mode, () => extractPhase2SeedSync(augmentedParser1, args), () => extractPhase2SeedAsync(augmentedParser1, args));
|
|
1258
|
+
if (firstPassSeed == null) {
|
|
1219
1259
|
const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1220
1260
|
if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
|
|
1221
1261
|
return Promise.resolve(runParser(augmentedParser, programName, args, options));
|
|
1222
1262
|
}
|
|
1223
|
-
const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts,
|
|
1263
|
+
const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
|
|
1224
1264
|
const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
|
|
1225
1265
|
const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
|
|
1226
1266
|
if (parser.$mode === "async") return runParser(augmentedParser2, programName, args, options);
|
|
@@ -1236,13 +1276,19 @@ async function runWithBody(parser, programName, contexts, args, options) {
|
|
|
1236
1276
|
*
|
|
1237
1277
|
* 1. *Phase 1*: Collect annotations from all contexts (static contexts return
|
|
1238
1278
|
* their data, dynamic contexts may return empty).
|
|
1239
|
-
* 2. *First parse*: Parse with Phase 1 annotations.
|
|
1279
|
+
* 2. *First parse*: Parse with Phase 1 annotations. If that pass finishes
|
|
1280
|
+
* successfully, its value becomes the phase-two input. If the parser
|
|
1281
|
+
* reaches a usable intermediate state but still does not complete
|
|
1282
|
+
* successfully, the runner extracts a best-effort seed from that state
|
|
1283
|
+
* instead.
|
|
1240
1284
|
* 3. *Phase 2*: Call `getAnnotations(parsed)` on all contexts with the first
|
|
1241
|
-
*
|
|
1285
|
+
* pass value. Deferred or otherwise unresolved fields in `parsed` may be
|
|
1286
|
+
* `undefined`.
|
|
1242
1287
|
* 4. *Second parse*: Parse again with merged annotations from both phases.
|
|
1243
1288
|
*
|
|
1244
|
-
* If all contexts are static (no dynamic contexts), the second parse is
|
|
1245
|
-
* for optimization.
|
|
1289
|
+
* If all contexts are static (no dynamic contexts), the second parse is
|
|
1290
|
+
* skipped for optimization. Phase 2 is also skipped when the first pass does
|
|
1291
|
+
* not yield any usable seed at all.
|
|
1246
1292
|
*
|
|
1247
1293
|
* @template TParser The parser type.
|
|
1248
1294
|
* @template THelp Return type when help is shown.
|
|
@@ -1317,20 +1363,12 @@ function runWithSyncBody(parser, programName, contexts, args, options) {
|
|
|
1317
1363
|
return runParser(augmentedParser, programName, args, options);
|
|
1318
1364
|
}
|
|
1319
1365
|
const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
const result = require_parser.parseSync(augmentedParser1, args);
|
|
1325
|
-
if (result.success) {
|
|
1326
|
-
firstPassResult = result.value;
|
|
1327
|
-
firstPassDeferred = result.deferred;
|
|
1328
|
-
firstPassDeferredKeys = result.deferredKeys;
|
|
1329
|
-
} else return runParser(augmentedParser1, programName, args, options);
|
|
1330
|
-
} catch {
|
|
1331
|
-
return runParser(augmentedParser1, programName, args, options);
|
|
1366
|
+
const firstPassSeed = extractPhase2SeedSync(augmentedParser1, args);
|
|
1367
|
+
if (firstPassSeed == null) {
|
|
1368
|
+
const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1369
|
+
return runParser(augmentedParser, programName, args, options);
|
|
1332
1370
|
}
|
|
1333
|
-
const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts,
|
|
1371
|
+
const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
|
|
1334
1372
|
const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
|
|
1335
1373
|
const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
|
|
1336
1374
|
return runParser(augmentedParser2, programName, args, options);
|
|
@@ -1339,7 +1377,9 @@ function runWithSyncBody(parser, programName, contexts, args, options) {
|
|
|
1339
1377
|
* Runs a synchronous parser with multiple source contexts.
|
|
1340
1378
|
*
|
|
1341
1379
|
* This is the sync-only variant of {@link runWith}. All contexts must return
|
|
1342
|
-
* annotations synchronously (not Promises).
|
|
1380
|
+
* annotations synchronously (not Promises). It uses the same two-phase
|
|
1381
|
+
* best-effort seed extraction as {@link runWith} when dynamic contexts are
|
|
1382
|
+
* present.
|
|
1343
1383
|
*
|
|
1344
1384
|
* @template TParser The sync parser type.
|
|
1345
1385
|
* @template THelp Return type when help is shown.
|
|
@@ -1415,10 +1455,25 @@ function runWithAsync(parser, programName, contexts, options) {
|
|
|
1415
1455
|
*/
|
|
1416
1456
|
function injectAnnotationsIntoParser(parser, annotations) {
|
|
1417
1457
|
const newInitialState = require_annotations.injectAnnotations(parser.initialState, annotations);
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1458
|
+
const descriptors = { ...Object.getOwnPropertyDescriptors(parser) };
|
|
1459
|
+
const initialState = descriptors.initialState;
|
|
1460
|
+
descriptors.initialState = initialState == null ? {
|
|
1461
|
+
value: newInitialState,
|
|
1462
|
+
writable: true,
|
|
1463
|
+
enumerable: true,
|
|
1464
|
+
configurable: true
|
|
1465
|
+
} : "get" in initialState || "set" in initialState ? {
|
|
1466
|
+
value: newInitialState,
|
|
1467
|
+
writable: true,
|
|
1468
|
+
enumerable: initialState.enumerable ?? true,
|
|
1469
|
+
configurable: initialState.configurable ?? true
|
|
1470
|
+
} : {
|
|
1471
|
+
value: newInitialState,
|
|
1472
|
+
writable: initialState.writable ?? true,
|
|
1473
|
+
enumerable: initialState.enumerable ?? true,
|
|
1474
|
+
configurable: initialState.configurable ?? true
|
|
1421
1475
|
};
|
|
1476
|
+
return Object.create(Object.getPrototypeOf(parser), descriptors);
|
|
1422
1477
|
}
|
|
1423
1478
|
|
|
1424
1479
|
//#endregion
|
package/dist/facade.d.cts
CHANGED
|
@@ -412,13 +412,19 @@ type ContextOptionsParam<TContexts extends readonly SourceContext<unknown>[], TV
|
|
|
412
412
|
*
|
|
413
413
|
* 1. *Phase 1*: Collect annotations from all contexts (static contexts return
|
|
414
414
|
* their data, dynamic contexts may return empty).
|
|
415
|
-
* 2. *First parse*: Parse with Phase 1 annotations.
|
|
415
|
+
* 2. *First parse*: Parse with Phase 1 annotations. If that pass finishes
|
|
416
|
+
* successfully, its value becomes the phase-two input. If the parser
|
|
417
|
+
* reaches a usable intermediate state but still does not complete
|
|
418
|
+
* successfully, the runner extracts a best-effort seed from that state
|
|
419
|
+
* instead.
|
|
416
420
|
* 3. *Phase 2*: Call `getAnnotations(parsed)` on all contexts with the first
|
|
417
|
-
*
|
|
421
|
+
* pass value. Deferred or otherwise unresolved fields in `parsed` may be
|
|
422
|
+
* `undefined`.
|
|
418
423
|
* 4. *Second parse*: Parse again with merged annotations from both phases.
|
|
419
424
|
*
|
|
420
|
-
* If all contexts are static (no dynamic contexts), the second parse is
|
|
421
|
-
* for optimization.
|
|
425
|
+
* If all contexts are static (no dynamic contexts), the second parse is
|
|
426
|
+
* skipped for optimization. Phase 2 is also skipped when the first pass does
|
|
427
|
+
* not yield any usable seed at all.
|
|
422
428
|
*
|
|
423
429
|
* @template TParser The parser type.
|
|
424
430
|
* @template THelp Return type when help is shown.
|
|
@@ -460,7 +466,9 @@ declare function runWith<TParser extends Parser<Mode, unknown, unknown>, TContex
|
|
|
460
466
|
* Runs a synchronous parser with multiple source contexts.
|
|
461
467
|
*
|
|
462
468
|
* This is the sync-only variant of {@link runWith}. All contexts must return
|
|
463
|
-
* annotations synchronously (not Promises).
|
|
469
|
+
* annotations synchronously (not Promises). It uses the same two-phase
|
|
470
|
+
* best-effort seed extraction as {@link runWith} when dynamic contexts are
|
|
471
|
+
* present.
|
|
464
472
|
*
|
|
465
473
|
* @template TParser The sync parser type.
|
|
466
474
|
* @template THelp Return type when help is shown.
|
package/dist/facade.d.ts
CHANGED
|
@@ -412,13 +412,19 @@ type ContextOptionsParam<TContexts extends readonly SourceContext<unknown>[], TV
|
|
|
412
412
|
*
|
|
413
413
|
* 1. *Phase 1*: Collect annotations from all contexts (static contexts return
|
|
414
414
|
* their data, dynamic contexts may return empty).
|
|
415
|
-
* 2. *First parse*: Parse with Phase 1 annotations.
|
|
415
|
+
* 2. *First parse*: Parse with Phase 1 annotations. If that pass finishes
|
|
416
|
+
* successfully, its value becomes the phase-two input. If the parser
|
|
417
|
+
* reaches a usable intermediate state but still does not complete
|
|
418
|
+
* successfully, the runner extracts a best-effort seed from that state
|
|
419
|
+
* instead.
|
|
416
420
|
* 3. *Phase 2*: Call `getAnnotations(parsed)` on all contexts with the first
|
|
417
|
-
*
|
|
421
|
+
* pass value. Deferred or otherwise unresolved fields in `parsed` may be
|
|
422
|
+
* `undefined`.
|
|
418
423
|
* 4. *Second parse*: Parse again with merged annotations from both phases.
|
|
419
424
|
*
|
|
420
|
-
* If all contexts are static (no dynamic contexts), the second parse is
|
|
421
|
-
* for optimization.
|
|
425
|
+
* If all contexts are static (no dynamic contexts), the second parse is
|
|
426
|
+
* skipped for optimization. Phase 2 is also skipped when the first pass does
|
|
427
|
+
* not yield any usable seed at all.
|
|
422
428
|
*
|
|
423
429
|
* @template TParser The parser type.
|
|
424
430
|
* @template THelp Return type when help is shown.
|
|
@@ -460,7 +466,9 @@ declare function runWith<TParser extends Parser<Mode, unknown, unknown>, TContex
|
|
|
460
466
|
* Runs a synchronous parser with multiple source contexts.
|
|
461
467
|
*
|
|
462
468
|
* This is the sync-only variant of {@link runWith}. All contexts must return
|
|
463
|
-
* annotations synchronously (not Promises).
|
|
469
|
+
* annotations synchronously (not Promises). It uses the same two-phase
|
|
470
|
+
* best-effort seed extraction as {@link runWith} when dynamic contexts are
|
|
471
|
+
* present.
|
|
464
472
|
*
|
|
465
473
|
* @template TParser The sync parser type.
|
|
466
474
|
* @template THelp Return type when help is shown.
|
package/dist/facade.js
CHANGED
|
@@ -5,10 +5,13 @@ import { validateCommandNames, validateContextIds, validateMetaNameCollisions, v
|
|
|
5
5
|
import { extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage } from "./usage.js";
|
|
6
6
|
import { formatDocPage } from "./doc.js";
|
|
7
7
|
import { dispatchByMode } from "./mode-dispatch.js";
|
|
8
|
+
import { createInputTrace } from "./input-trace.js";
|
|
9
|
+
import { completeOrExtractPhase2Seed } from "./phase2-seed.js";
|
|
8
10
|
import { multiple, optional, withDefault } from "./modifiers.js";
|
|
9
11
|
import { string } from "./valueparser.js";
|
|
10
12
|
import { argument, command, constant, flag, option } from "./primitives.js";
|
|
11
|
-
import { getDocPage, parseAsync, parseSync, suggest, suggestAsync } from "./parser.js";
|
|
13
|
+
import { createParserContext, getDocPage, parseAsync, parseSync, suggest, suggestAsync } from "./parser.js";
|
|
14
|
+
import { createDependencyRuntimeContext } from "./dependency-runtime.js";
|
|
12
15
|
import { group, longestMatch, object } from "./constructs.js";
|
|
13
16
|
|
|
14
17
|
//#region src/facade.ts
|
|
@@ -89,6 +92,60 @@ function prepareParsedForContexts(parsed, deferred, deferredKeys) {
|
|
|
89
92
|
function withPreparedParsedForContext(context, preparedParsed, run) {
|
|
90
93
|
return run(finalizeParsedForContext(context, preparedParsed));
|
|
91
94
|
}
|
|
95
|
+
function isBufferUnchanged(previous, current) {
|
|
96
|
+
return current.length > 0 && current.length === previous.length && current.every((item, i) => item === previous[i]);
|
|
97
|
+
}
|
|
98
|
+
function createPhase2SeedExec(parser, context) {
|
|
99
|
+
const exec = {
|
|
100
|
+
usage: parser.usage,
|
|
101
|
+
phase: "parse",
|
|
102
|
+
path: [],
|
|
103
|
+
trace: createInputTrace()
|
|
104
|
+
};
|
|
105
|
+
const runtime = createDependencyRuntimeContext();
|
|
106
|
+
return {
|
|
107
|
+
...exec,
|
|
108
|
+
phase: "complete",
|
|
109
|
+
dependencyRuntime: runtime,
|
|
110
|
+
dependencyRegistry: runtime.registry,
|
|
111
|
+
trace: context.exec?.trace ?? context.trace ?? exec.trace
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createPhase2SeedContext(parser, args) {
|
|
115
|
+
const exec = {
|
|
116
|
+
usage: parser.usage,
|
|
117
|
+
phase: "parse",
|
|
118
|
+
path: [],
|
|
119
|
+
trace: createInputTrace()
|
|
120
|
+
};
|
|
121
|
+
return createParserContext({
|
|
122
|
+
buffer: args,
|
|
123
|
+
state: parser.initialState,
|
|
124
|
+
optionsTerminated: false
|
|
125
|
+
}, exec);
|
|
126
|
+
}
|
|
127
|
+
function extractPhase2SeedSync(parser, args) {
|
|
128
|
+
let context = createPhase2SeedContext(parser, args);
|
|
129
|
+
do {
|
|
130
|
+
const result = parser.parse(context);
|
|
131
|
+
if (!result.success) return completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
132
|
+
const previousBuffer = context.buffer;
|
|
133
|
+
context = result.next;
|
|
134
|
+
if (isBufferUnchanged(previousBuffer, context.buffer)) return completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
135
|
+
} while (context.buffer.length > 0);
|
|
136
|
+
return completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
137
|
+
}
|
|
138
|
+
async function extractPhase2SeedAsync(parser, args) {
|
|
139
|
+
let context = createPhase2SeedContext(parser, args);
|
|
140
|
+
do {
|
|
141
|
+
const result = await parser.parse(context);
|
|
142
|
+
if (!result.success) return await completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
143
|
+
const previousBuffer = context.buffer;
|
|
144
|
+
context = result.next;
|
|
145
|
+
if (isBufferUnchanged(previousBuffer, context.buffer)) return await completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
146
|
+
} while (context.buffer.length > 0);
|
|
147
|
+
return await completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
148
|
+
}
|
|
92
149
|
/**
|
|
93
150
|
* Creates help parsers based on the sub-config.
|
|
94
151
|
*/
|
|
@@ -1197,30 +1254,13 @@ async function runWithBody(parser, programName, contexts, args, options) {
|
|
|
1197
1254
|
return Promise.resolve(runParser(augmentedParser, programName, args, options));
|
|
1198
1255
|
}
|
|
1199
1256
|
const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
let firstPassDeferredKeys;
|
|
1203
|
-
let firstPassFailed = false;
|
|
1204
|
-
try {
|
|
1205
|
-
if (parser.$mode === "async") firstPassResult = await parseAsync(augmentedParser1, args);
|
|
1206
|
-
else firstPassResult = parseSync(augmentedParser1, args);
|
|
1207
|
-
if (typeof firstPassResult === "object" && firstPassResult !== null && "success" in firstPassResult) {
|
|
1208
|
-
const result = firstPassResult;
|
|
1209
|
-
if (result.success) {
|
|
1210
|
-
firstPassResult = result.value;
|
|
1211
|
-
firstPassDeferred = result.deferred;
|
|
1212
|
-
firstPassDeferredKeys = result.deferredKeys;
|
|
1213
|
-
} else firstPassFailed = true;
|
|
1214
|
-
}
|
|
1215
|
-
} catch {
|
|
1216
|
-
firstPassFailed = true;
|
|
1217
|
-
}
|
|
1218
|
-
if (firstPassFailed) {
|
|
1257
|
+
const firstPassSeed = await dispatchByMode(parser.$mode, () => extractPhase2SeedSync(augmentedParser1, args), () => extractPhase2SeedAsync(augmentedParser1, args));
|
|
1258
|
+
if (firstPassSeed == null) {
|
|
1219
1259
|
const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1220
1260
|
if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
|
|
1221
1261
|
return Promise.resolve(runParser(augmentedParser, programName, args, options));
|
|
1222
1262
|
}
|
|
1223
|
-
const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts,
|
|
1263
|
+
const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
|
|
1224
1264
|
const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
|
|
1225
1265
|
const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
|
|
1226
1266
|
if (parser.$mode === "async") return runParser(augmentedParser2, programName, args, options);
|
|
@@ -1236,13 +1276,19 @@ async function runWithBody(parser, programName, contexts, args, options) {
|
|
|
1236
1276
|
*
|
|
1237
1277
|
* 1. *Phase 1*: Collect annotations from all contexts (static contexts return
|
|
1238
1278
|
* their data, dynamic contexts may return empty).
|
|
1239
|
-
* 2. *First parse*: Parse with Phase 1 annotations.
|
|
1279
|
+
* 2. *First parse*: Parse with Phase 1 annotations. If that pass finishes
|
|
1280
|
+
* successfully, its value becomes the phase-two input. If the parser
|
|
1281
|
+
* reaches a usable intermediate state but still does not complete
|
|
1282
|
+
* successfully, the runner extracts a best-effort seed from that state
|
|
1283
|
+
* instead.
|
|
1240
1284
|
* 3. *Phase 2*: Call `getAnnotations(parsed)` on all contexts with the first
|
|
1241
|
-
*
|
|
1285
|
+
* pass value. Deferred or otherwise unresolved fields in `parsed` may be
|
|
1286
|
+
* `undefined`.
|
|
1242
1287
|
* 4. *Second parse*: Parse again with merged annotations from both phases.
|
|
1243
1288
|
*
|
|
1244
|
-
* If all contexts are static (no dynamic contexts), the second parse is
|
|
1245
|
-
* for optimization.
|
|
1289
|
+
* If all contexts are static (no dynamic contexts), the second parse is
|
|
1290
|
+
* skipped for optimization. Phase 2 is also skipped when the first pass does
|
|
1291
|
+
* not yield any usable seed at all.
|
|
1246
1292
|
*
|
|
1247
1293
|
* @template TParser The parser type.
|
|
1248
1294
|
* @template THelp Return type when help is shown.
|
|
@@ -1317,20 +1363,12 @@ function runWithSyncBody(parser, programName, contexts, args, options) {
|
|
|
1317
1363
|
return runParser(augmentedParser, programName, args, options);
|
|
1318
1364
|
}
|
|
1319
1365
|
const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
const result = parseSync(augmentedParser1, args);
|
|
1325
|
-
if (result.success) {
|
|
1326
|
-
firstPassResult = result.value;
|
|
1327
|
-
firstPassDeferred = result.deferred;
|
|
1328
|
-
firstPassDeferredKeys = result.deferredKeys;
|
|
1329
|
-
} else return runParser(augmentedParser1, programName, args, options);
|
|
1330
|
-
} catch {
|
|
1331
|
-
return runParser(augmentedParser1, programName, args, options);
|
|
1366
|
+
const firstPassSeed = extractPhase2SeedSync(augmentedParser1, args);
|
|
1367
|
+
if (firstPassSeed == null) {
|
|
1368
|
+
const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1369
|
+
return runParser(augmentedParser, programName, args, options);
|
|
1332
1370
|
}
|
|
1333
|
-
const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts,
|
|
1371
|
+
const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
|
|
1334
1372
|
const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
|
|
1335
1373
|
const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
|
|
1336
1374
|
return runParser(augmentedParser2, programName, args, options);
|
|
@@ -1339,7 +1377,9 @@ function runWithSyncBody(parser, programName, contexts, args, options) {
|
|
|
1339
1377
|
* Runs a synchronous parser with multiple source contexts.
|
|
1340
1378
|
*
|
|
1341
1379
|
* This is the sync-only variant of {@link runWith}. All contexts must return
|
|
1342
|
-
* annotations synchronously (not Promises).
|
|
1380
|
+
* annotations synchronously (not Promises). It uses the same two-phase
|
|
1381
|
+
* best-effort seed extraction as {@link runWith} when dynamic contexts are
|
|
1382
|
+
* present.
|
|
1343
1383
|
*
|
|
1344
1384
|
* @template TParser The sync parser type.
|
|
1345
1385
|
* @template THelp Return type when help is shown.
|
|
@@ -1415,10 +1455,25 @@ function runWithAsync(parser, programName, contexts, options) {
|
|
|
1415
1455
|
*/
|
|
1416
1456
|
function injectAnnotationsIntoParser(parser, annotations) {
|
|
1417
1457
|
const newInitialState = injectAnnotations(parser.initialState, annotations);
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1458
|
+
const descriptors = { ...Object.getOwnPropertyDescriptors(parser) };
|
|
1459
|
+
const initialState = descriptors.initialState;
|
|
1460
|
+
descriptors.initialState = initialState == null ? {
|
|
1461
|
+
value: newInitialState,
|
|
1462
|
+
writable: true,
|
|
1463
|
+
enumerable: true,
|
|
1464
|
+
configurable: true
|
|
1465
|
+
} : "get" in initialState || "set" in initialState ? {
|
|
1466
|
+
value: newInitialState,
|
|
1467
|
+
writable: true,
|
|
1468
|
+
enumerable: initialState.enumerable ?? true,
|
|
1469
|
+
configurable: initialState.configurable ?? true
|
|
1470
|
+
} : {
|
|
1471
|
+
value: newInitialState,
|
|
1472
|
+
writable: initialState.writable ?? true,
|
|
1473
|
+
enumerable: initialState.enumerable ?? true,
|
|
1474
|
+
configurable: initialState.configurable ?? true
|
|
1421
1475
|
};
|
|
1476
|
+
return Object.create(Object.getPrototypeOf(parser), descriptors);
|
|
1422
1477
|
}
|
|
1423
1478
|
|
|
1424
1479
|
//#endregion
|
package/dist/modifiers.cjs
CHANGED
|
@@ -2,6 +2,7 @@ const require_annotations = require('./annotations.cjs');
|
|
|
2
2
|
const require_message = require('./message.cjs');
|
|
3
3
|
const require_mode_dispatch = require('./mode-dispatch.cjs');
|
|
4
4
|
const require_dependency_metadata = require('./dependency-metadata.cjs');
|
|
5
|
+
const require_phase2_seed = require('./phase2-seed.cjs');
|
|
5
6
|
const require_parser = require('./parser.cjs');
|
|
6
7
|
|
|
7
8
|
//#region src/modifiers.ts
|
|
@@ -79,6 +80,10 @@ function unwrapMultipleItemState(state) {
|
|
|
79
80
|
function isPromiseLike(value) {
|
|
80
81
|
return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
81
82
|
}
|
|
83
|
+
function extractOptionalLikePhase2Seed(parser, state, exec) {
|
|
84
|
+
if (!Array.isArray(state) && !(state != null && typeof state === "object")) return require_mode_dispatch.wrapForMode(parser.$mode, null);
|
|
85
|
+
return require_phase2_seed.completeOrExtractPhase2Seed(parser, normalizeOptionalLikeInnerState(state, parser.initialState, parser), exec);
|
|
86
|
+
}
|
|
82
87
|
/**
|
|
83
88
|
* Computes the inner state to pass through to the wrapped parser inside
|
|
84
89
|
* {@link optional} / {@link withDefault}. When the outer state is an
|
|
@@ -251,6 +256,9 @@ function optional(parser) {
|
|
|
251
256
|
state: innerState
|
|
252
257
|
}] : []);
|
|
253
258
|
},
|
|
259
|
+
[require_phase2_seed.extractPhase2SeedKey](state, exec) {
|
|
260
|
+
return extractOptionalLikePhase2Seed(parser, state, exec);
|
|
261
|
+
},
|
|
254
262
|
parse(context) {
|
|
255
263
|
return require_mode_dispatch.dispatchByMode(parser.$mode, () => parseOptionalStyleSync(context, syncParser), () => parseOptionalStyleAsync(context, parser));
|
|
256
264
|
},
|
|
@@ -416,6 +424,9 @@ function withDefault(parser, defaultValue, options) {
|
|
|
416
424
|
state: innerState
|
|
417
425
|
}] : []);
|
|
418
426
|
},
|
|
427
|
+
[require_phase2_seed.extractPhase2SeedKey](state, exec) {
|
|
428
|
+
return extractOptionalLikePhase2Seed(parser, state, exec);
|
|
429
|
+
},
|
|
419
430
|
parse(context) {
|
|
420
431
|
return require_mode_dispatch.dispatchByMode(parser.$mode, () => parseOptionalStyleSync(context, syncParser), () => parseOptionalStyleAsync(context, parser));
|
|
421
432
|
},
|
|
@@ -656,6 +667,23 @@ function map(parser, transform) {
|
|
|
656
667
|
...parser,
|
|
657
668
|
$valueType: [],
|
|
658
669
|
complete,
|
|
670
|
+
[require_phase2_seed.extractPhase2SeedKey](state, exec) {
|
|
671
|
+
return require_mode_dispatch.mapModeValue(parser.$mode, require_phase2_seed.completeOrExtractPhase2Seed(parser, state, exec), (seed) => {
|
|
672
|
+
if (seed == null) return null;
|
|
673
|
+
if (seed.deferred) try {
|
|
674
|
+
return {
|
|
675
|
+
value: transform(seed.value),
|
|
676
|
+
deferred: true
|
|
677
|
+
};
|
|
678
|
+
} catch {
|
|
679
|
+
return {
|
|
680
|
+
value: void 0,
|
|
681
|
+
deferred: true
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
return { value: transform(seed.value) };
|
|
685
|
+
});
|
|
686
|
+
},
|
|
659
687
|
getSuggestRuntimeNodes(state, path) {
|
|
660
688
|
if (mappedParser.dependencyMetadata?.source != null) return [{
|
|
661
689
|
path,
|
|
@@ -795,6 +823,26 @@ function multiple(parser, options = {}) {
|
|
|
795
823
|
});
|
|
796
824
|
}
|
|
797
825
|
};
|
|
826
|
+
const extractPhase2SeedSyncWithUnwrappedFallback = (state, exec) => {
|
|
827
|
+
try {
|
|
828
|
+
const seed = require_phase2_seed.completeOrExtractPhase2Seed(syncParser, state, exec);
|
|
829
|
+
if (seed == null && require_annotations.isInjectedAnnotationWrapper(state)) return require_phase2_seed.completeOrExtractPhase2Seed(syncParser, unwrapInjectedWrapper(state), exec);
|
|
830
|
+
return seed;
|
|
831
|
+
} catch (error) {
|
|
832
|
+
if (!require_annotations.isInjectedAnnotationWrapper(state)) throw error;
|
|
833
|
+
return require_phase2_seed.completeOrExtractPhase2Seed(syncParser, unwrapInjectedWrapper(state), exec);
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
const extractPhase2SeedAsyncWithUnwrappedFallback = async (state, exec) => {
|
|
837
|
+
try {
|
|
838
|
+
const seed = await require_phase2_seed.completeOrExtractPhase2Seed(parser, state, exec);
|
|
839
|
+
if (seed == null && require_annotations.isInjectedAnnotationWrapper(state)) return await require_phase2_seed.completeOrExtractPhase2Seed(parser, unwrapInjectedWrapper(state), exec);
|
|
840
|
+
return seed;
|
|
841
|
+
} catch (error) {
|
|
842
|
+
if (!require_annotations.isInjectedAnnotationWrapper(state)) throw error;
|
|
843
|
+
return await require_phase2_seed.completeOrExtractPhase2Seed(parser, unwrapInjectedWrapper(state), exec);
|
|
844
|
+
}
|
|
845
|
+
};
|
|
798
846
|
const getInnerSuggestRuntimeNodes = (state, path) => parser.getSuggestRuntimeNodes?.(state, path) ?? (parser.dependencyMetadata?.source != null ? [{
|
|
799
847
|
path,
|
|
800
848
|
parser,
|
|
@@ -1023,6 +1071,53 @@ function multiple(parser, options = {}) {
|
|
|
1023
1071
|
return validateMultipleResult(values, deferredIndices, hasDeferred);
|
|
1024
1072
|
});
|
|
1025
1073
|
},
|
|
1074
|
+
[require_phase2_seed.extractPhase2SeedKey](state, exec) {
|
|
1075
|
+
return require_mode_dispatch.dispatchByMode(parser.$mode, () => {
|
|
1076
|
+
const values = [];
|
|
1077
|
+
const deferredIndices = /* @__PURE__ */ new Map();
|
|
1078
|
+
let hasDeferred = false;
|
|
1079
|
+
let hasAnySeed = false;
|
|
1080
|
+
for (let i = 0; i < state.length; i++) {
|
|
1081
|
+
const seed = extractPhase2SeedSyncWithUnwrappedFallback(state[i], withChildExecPath(exec, i));
|
|
1082
|
+
if (seed == null) continue;
|
|
1083
|
+
hasAnySeed = true;
|
|
1084
|
+
values[i] = seed.value;
|
|
1085
|
+
if (seed.deferred) if (seed.deferredKeys) deferredIndices.set(i, seed.deferredKeys);
|
|
1086
|
+
else if (seed.value == null || typeof seed.value !== "object") deferredIndices.set(i, null);
|
|
1087
|
+
else hasDeferred = true;
|
|
1088
|
+
}
|
|
1089
|
+
if (!hasAnySeed) return null;
|
|
1090
|
+
return {
|
|
1091
|
+
value: values,
|
|
1092
|
+
...deferredIndices.size > 0 || hasDeferred ? {
|
|
1093
|
+
deferred: true,
|
|
1094
|
+
...deferredIndices.size > 0 ? { deferredKeys: deferredIndices } : {}
|
|
1095
|
+
} : {}
|
|
1096
|
+
};
|
|
1097
|
+
}, async () => {
|
|
1098
|
+
const values = [];
|
|
1099
|
+
const deferredIndices = /* @__PURE__ */ new Map();
|
|
1100
|
+
let hasDeferred = false;
|
|
1101
|
+
let hasAnySeed = false;
|
|
1102
|
+
for (let i = 0; i < state.length; i++) {
|
|
1103
|
+
const seed = await extractPhase2SeedAsyncWithUnwrappedFallback(state[i], withChildExecPath(exec, i));
|
|
1104
|
+
if (seed == null) continue;
|
|
1105
|
+
hasAnySeed = true;
|
|
1106
|
+
values[i] = seed.value;
|
|
1107
|
+
if (seed.deferred) if (seed.deferredKeys) deferredIndices.set(i, seed.deferredKeys);
|
|
1108
|
+
else if (seed.value == null || typeof seed.value !== "object") deferredIndices.set(i, null);
|
|
1109
|
+
else hasDeferred = true;
|
|
1110
|
+
}
|
|
1111
|
+
if (!hasAnySeed) return null;
|
|
1112
|
+
return {
|
|
1113
|
+
value: values,
|
|
1114
|
+
...deferredIndices.size > 0 || hasDeferred ? {
|
|
1115
|
+
deferred: true,
|
|
1116
|
+
...deferredIndices.size > 0 ? { deferredKeys: deferredIndices } : {}
|
|
1117
|
+
} : {}
|
|
1118
|
+
};
|
|
1119
|
+
});
|
|
1120
|
+
},
|
|
1026
1121
|
suggest(context, prefix) {
|
|
1027
1122
|
const currentItemState = context.state.at(-1);
|
|
1028
1123
|
const canExtendCurrent = currentItemState != null && !isTerminalMultipleItemState(currentItemState);
|
|
@@ -1365,6 +1460,13 @@ function nonEmpty(parser) {
|
|
|
1365
1460
|
configurable: true,
|
|
1366
1461
|
enumerable: false
|
|
1367
1462
|
});
|
|
1463
|
+
Object.defineProperty(nonEmptyParser, require_phase2_seed.extractPhase2SeedKey, {
|
|
1464
|
+
value(state, exec) {
|
|
1465
|
+
return require_phase2_seed.extractPhase2Seed(parser, state, exec);
|
|
1466
|
+
},
|
|
1467
|
+
configurable: true,
|
|
1468
|
+
enumerable: false
|
|
1469
|
+
});
|
|
1368
1470
|
if (typeof parser.normalizeValue === "function") Object.defineProperty(nonEmptyParser, "normalizeValue", {
|
|
1369
1471
|
value: parser.normalizeValue.bind(parser),
|
|
1370
1472
|
configurable: true,
|