@optique/core 1.0.0-dev.1616 → 1.0.0-dev.1659
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 +1354 -606
- package/dist/constructs.js +1356 -608
- package/dist/dependency-metadata.cjs +12 -13
- package/dist/dependency-metadata.d.cts +7 -3
- package/dist/dependency-metadata.d.ts +7 -3
- package/dist/dependency-metadata.js +12 -13
- package/dist/dependency-runtime.cjs +387 -19
- package/dist/dependency-runtime.d.cts +28 -2
- package/dist/dependency-runtime.d.ts +28 -2
- package/dist/dependency-runtime.js +382 -20
- package/dist/dependency.cjs +158 -65
- package/dist/dependency.d.cts +34 -8
- package/dist/dependency.d.ts +34 -8
- package/dist/dependency.js +156 -66
- package/dist/facade.cjs +2 -2
- package/dist/facade.js +2 -2
- package/dist/index.cjs +12 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -4
- package/dist/input-trace.cjs +56 -0
- package/dist/input-trace.d.cts +77 -0
- package/dist/input-trace.d.ts +77 -0
- package/dist/input-trace.js +55 -0
- package/dist/modifiers.cjs +337 -186
- package/dist/modifiers.js +338 -187
- package/dist/parser.cjs +169 -20
- package/dist/parser.d.cts +140 -5
- package/dist/parser.d.ts +140 -5
- package/dist/parser.js +162 -21
- package/dist/primitives.cjs +311 -177
- package/dist/primitives.d.cts +2 -8
- package/dist/primitives.d.ts +2 -8
- package/dist/primitives.js +312 -178
- package/package.json +1 -1
|
@@ -50,12 +50,17 @@ function extractDependencyMetadata(valueParser) {
|
|
|
50
50
|
return void 0;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Extracts the source parse result from a
|
|
54
|
-
*
|
|
53
|
+
* Extracts the source parse result from a plain source state.
|
|
54
|
+
*
|
|
55
|
+
* Accepts either a `DependencySourceState` or a bare
|
|
56
|
+
* `ValueParserResult`-shaped object, and returns `undefined` for
|
|
57
|
+
* unrelated states. Used as the base `extractSourceValue` for plain
|
|
58
|
+
* dependency sources.
|
|
55
59
|
*/
|
|
56
60
|
function extractFromBareState(state) {
|
|
57
|
-
if (
|
|
58
|
-
return state
|
|
61
|
+
if (require_dependency.isDependencySourceState(state)) return state.result;
|
|
62
|
+
if (state != null && typeof state === "object" && Object.hasOwn(state, "success") && typeof state.success === "boolean" && (state.success && Object.hasOwn(state, "value") || !state.success && Object.hasOwn(state, "error"))) return state;
|
|
63
|
+
return void 0;
|
|
59
64
|
}
|
|
60
65
|
/**
|
|
61
66
|
* Wraps an inner `extractSourceValue` to unwrap `[innerState]` first.
|
|
@@ -101,19 +106,13 @@ function composeDependencyMetadata(inner, wrapperKind, options) {
|
|
|
101
106
|
}
|
|
102
107
|
case "withDefault": {
|
|
103
108
|
const wrappedExtract = inner.source?.extractSourceValue != null ? unwrapArrayThenExtract(inner.source.extractSourceValue) : void 0;
|
|
104
|
-
|
|
109
|
+
const preservesSourceValue = inner.source?.preservesSourceValue !== false;
|
|
110
|
+
if (inner.source != null) return {
|
|
105
111
|
...inner,
|
|
106
112
|
source: {
|
|
107
113
|
...inner.source,
|
|
108
114
|
...wrappedExtract != null && { extractSourceValue: wrappedExtract },
|
|
109
|
-
getMissingSourceValue: options
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
if (wrappedExtract != null && inner.source != null) return {
|
|
113
|
-
...inner,
|
|
114
|
-
source: {
|
|
115
|
-
...inner.source,
|
|
116
|
-
extractSourceValue: wrappedExtract
|
|
115
|
+
...preservesSourceValue && options?.defaultValue != null && { getMissingSourceValue: options.defaultValue }
|
|
117
116
|
}
|
|
118
117
|
};
|
|
119
118
|
return inner;
|
|
@@ -17,16 +17,20 @@ interface DependencySourceCapability {
|
|
|
17
17
|
/**
|
|
18
18
|
* Extracts the dependency source parse result from the parser's state.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* The `state` argument is the current parser state for the source-owning
|
|
21
|
+
* parser. Each wrapper composes this method to handle its own state shape:
|
|
21
22
|
* - plain source: reads from `DependencySourceState`
|
|
22
23
|
* - `optional()` / `withDefault()`: unwraps `[innerState]` first
|
|
23
24
|
* - `map()`: reads the pre-transform value from inner state
|
|
24
25
|
*
|
|
25
26
|
* Returns the `ValueParserResult` (which may be successful with any
|
|
26
|
-
* value including `undefined`, or failed),
|
|
27
|
+
* value including `undefined`, or failed), a promise that resolves to the
|
|
28
|
+
* same shape when extraction needs async work, or `undefined` if the state
|
|
27
29
|
* does not contain a source result at all (unpopulated / wrong shape).
|
|
30
|
+
* Callers and wrapper authors must handle both direct and promise-wrapped
|
|
31
|
+
* results when composing `extractSourceValue`.
|
|
28
32
|
*/
|
|
29
|
-
readonly extractSourceValue: (state: unknown) => ValueParserResult<unknown> | undefined;
|
|
33
|
+
readonly extractSourceValue: (state: unknown) => ValueParserResult<unknown> | Promise<ValueParserResult<unknown> | undefined> | undefined;
|
|
30
34
|
/**
|
|
31
35
|
* When present, provides a missing-source value (e.g., from a
|
|
32
36
|
* `withDefault()` wrapper). Called during the `fillMissingSourceDefaults`
|
|
@@ -17,16 +17,20 @@ interface DependencySourceCapability {
|
|
|
17
17
|
/**
|
|
18
18
|
* Extracts the dependency source parse result from the parser's state.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* The `state` argument is the current parser state for the source-owning
|
|
21
|
+
* parser. Each wrapper composes this method to handle its own state shape:
|
|
21
22
|
* - plain source: reads from `DependencySourceState`
|
|
22
23
|
* - `optional()` / `withDefault()`: unwraps `[innerState]` first
|
|
23
24
|
* - `map()`: reads the pre-transform value from inner state
|
|
24
25
|
*
|
|
25
26
|
* Returns the `ValueParserResult` (which may be successful with any
|
|
26
|
-
* value including `undefined`, or failed),
|
|
27
|
+
* value including `undefined`, or failed), a promise that resolves to the
|
|
28
|
+
* same shape when extraction needs async work, or `undefined` if the state
|
|
27
29
|
* does not contain a source result at all (unpopulated / wrong shape).
|
|
30
|
+
* Callers and wrapper authors must handle both direct and promise-wrapped
|
|
31
|
+
* results when composing `extractSourceValue`.
|
|
28
32
|
*/
|
|
29
|
-
readonly extractSourceValue: (state: unknown) => ValueParserResult<unknown> | undefined;
|
|
33
|
+
readonly extractSourceValue: (state: unknown) => ValueParserResult<unknown> | Promise<ValueParserResult<unknown> | undefined> | undefined;
|
|
30
34
|
/**
|
|
31
35
|
* When present, provides a missing-source value (e.g., from a
|
|
32
36
|
* `withDefault()` wrapper). Called during the `fillMissingSourceDefaults`
|
|
@@ -50,12 +50,17 @@ function extractDependencyMetadata(valueParser) {
|
|
|
50
50
|
return void 0;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Extracts the source parse result from a
|
|
54
|
-
*
|
|
53
|
+
* Extracts the source parse result from a plain source state.
|
|
54
|
+
*
|
|
55
|
+
* Accepts either a `DependencySourceState` or a bare
|
|
56
|
+
* `ValueParserResult`-shaped object, and returns `undefined` for
|
|
57
|
+
* unrelated states. Used as the base `extractSourceValue` for plain
|
|
58
|
+
* dependency sources.
|
|
55
59
|
*/
|
|
56
60
|
function extractFromBareState(state) {
|
|
57
|
-
if (
|
|
58
|
-
return state
|
|
61
|
+
if (isDependencySourceState(state)) return state.result;
|
|
62
|
+
if (state != null && typeof state === "object" && Object.hasOwn(state, "success") && typeof state.success === "boolean" && (state.success && Object.hasOwn(state, "value") || !state.success && Object.hasOwn(state, "error"))) return state;
|
|
63
|
+
return void 0;
|
|
59
64
|
}
|
|
60
65
|
/**
|
|
61
66
|
* Wraps an inner `extractSourceValue` to unwrap `[innerState]` first.
|
|
@@ -101,19 +106,13 @@ function composeDependencyMetadata(inner, wrapperKind, options) {
|
|
|
101
106
|
}
|
|
102
107
|
case "withDefault": {
|
|
103
108
|
const wrappedExtract = inner.source?.extractSourceValue != null ? unwrapArrayThenExtract(inner.source.extractSourceValue) : void 0;
|
|
104
|
-
|
|
109
|
+
const preservesSourceValue = inner.source?.preservesSourceValue !== false;
|
|
110
|
+
if (inner.source != null) return {
|
|
105
111
|
...inner,
|
|
106
112
|
source: {
|
|
107
113
|
...inner.source,
|
|
108
114
|
...wrappedExtract != null && { extractSourceValue: wrappedExtract },
|
|
109
|
-
getMissingSourceValue: options
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
if (wrappedExtract != null && inner.source != null) return {
|
|
113
|
-
...inner,
|
|
114
|
-
source: {
|
|
115
|
-
...inner.source,
|
|
116
|
-
extractSourceValue: wrappedExtract
|
|
115
|
+
...preservesSourceValue && options?.defaultValue != null && { getMissingSourceValue: options.defaultValue }
|
|
117
116
|
}
|
|
118
117
|
};
|
|
119
118
|
return inner;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
const require_message = require('./message.cjs');
|
|
1
2
|
const require_dependency = require('./dependency.cjs');
|
|
3
|
+
const require_parser = require('./parser.cjs');
|
|
2
4
|
|
|
3
5
|
//#region src/dependency-runtime.ts
|
|
4
6
|
const symbolIds = /* @__PURE__ */ new WeakMap();
|
|
@@ -18,7 +20,11 @@ var DependencyRuntimeContextImpl = class {
|
|
|
18
20
|
#replayCache = /* @__PURE__ */ new Map();
|
|
19
21
|
#failedSources = /* @__PURE__ */ new Set();
|
|
20
22
|
constructor(registry) {
|
|
21
|
-
|
|
23
|
+
if (registry instanceof FailedAwareRegistry) {
|
|
24
|
+
this.registry = registry.rebindFailedSources(this.#failedSources);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.registry = new FailedAwareRegistry(registry, this.#failedSources);
|
|
22
28
|
}
|
|
23
29
|
registerSource(sourceId, value, _origin) {
|
|
24
30
|
this.registry.set(sourceId, value);
|
|
@@ -48,6 +54,47 @@ var DependencyRuntimeContextImpl = class {
|
|
|
48
54
|
return resolveRequest(this, request);
|
|
49
55
|
}
|
|
50
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Registry wrapper that hides values for sources that have failed.
|
|
59
|
+
*
|
|
60
|
+
* The wrapper lets clones share an underlying registry while maintaining
|
|
61
|
+
* an isolated failed-source view, so later lookups do not reuse stale
|
|
62
|
+
* values after extraction errors.
|
|
63
|
+
*
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
var FailedAwareRegistry = class FailedAwareRegistry {
|
|
67
|
+
#inner;
|
|
68
|
+
#failedSources;
|
|
69
|
+
constructor(inner, failedSources) {
|
|
70
|
+
this.#inner = inner;
|
|
71
|
+
this.#failedSources = failedSources;
|
|
72
|
+
}
|
|
73
|
+
set(id, value) {
|
|
74
|
+
this.#inner.set(id, value);
|
|
75
|
+
this.#failedSources.delete(id);
|
|
76
|
+
}
|
|
77
|
+
get(id) {
|
|
78
|
+
if (this.#failedSources.has(id)) return void 0;
|
|
79
|
+
return this.#inner.get(id);
|
|
80
|
+
}
|
|
81
|
+
has(id) {
|
|
82
|
+
if (this.#failedSources.has(id)) return false;
|
|
83
|
+
return this.#inner.has(id);
|
|
84
|
+
}
|
|
85
|
+
copyFailedSources(target) {
|
|
86
|
+
for (const sourceId of this.#failedSources) target.add(sourceId);
|
|
87
|
+
}
|
|
88
|
+
rebindFailedSources(target) {
|
|
89
|
+
this.copyFailedSources(target);
|
|
90
|
+
return new FailedAwareRegistry(this.#inner, target);
|
|
91
|
+
}
|
|
92
|
+
clone() {
|
|
93
|
+
const failedSources = new Set(this.#failedSources);
|
|
94
|
+
const innerClone = this.#inner.clone();
|
|
95
|
+
return innerClone instanceof FailedAwareRegistry ? innerClone.rebindFailedSources(failedSources) : new FailedAwareRegistry(innerClone, failedSources);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
51
98
|
function resolveRequest(ctx, request) {
|
|
52
99
|
const values = [];
|
|
53
100
|
const usedDefaults = [];
|
|
@@ -55,13 +102,13 @@ function resolveRequest(ctx, request) {
|
|
|
55
102
|
let defaultedCount = 0;
|
|
56
103
|
for (let i = 0; i < request.dependencyIds.length; i++) {
|
|
57
104
|
const id = request.dependencyIds[i];
|
|
58
|
-
if (ctx.
|
|
105
|
+
if (ctx.isSourceFailed(id)) {
|
|
106
|
+
values.push(void 0);
|
|
107
|
+
usedDefaults.push(false);
|
|
108
|
+
} else if (ctx.hasSource(id)) {
|
|
59
109
|
values.push(ctx.getSource(id));
|
|
60
110
|
usedDefaults.push(false);
|
|
61
111
|
resolvedCount++;
|
|
62
|
-
} else if (ctx.isSourceFailed(id)) {
|
|
63
|
-
values.push(void 0);
|
|
64
|
-
usedDefaults.push(false);
|
|
65
112
|
} else if (request.defaultValues != null && i < request.defaultValues.length) {
|
|
66
113
|
values.push(request.defaultValues[i]);
|
|
67
114
|
usedDefaults.push(true);
|
|
@@ -126,11 +173,80 @@ function createDependencyRuntimeContext(registry) {
|
|
|
126
173
|
return new DependencyRuntimeContextImpl(registry ?? new SimpleRegistry());
|
|
127
174
|
}
|
|
128
175
|
/**
|
|
176
|
+
* Creates a stable fingerprint from dependency values.
|
|
177
|
+
*
|
|
178
|
+
* @param values The dependency values to fingerprint.
|
|
179
|
+
* @returns A string fingerprint.
|
|
180
|
+
* @internal
|
|
181
|
+
* @since 1.0.0
|
|
182
|
+
*/
|
|
183
|
+
function createDependencyFingerprint(values) {
|
|
184
|
+
return values.map((v) => {
|
|
185
|
+
const raw = fingerprintValue(v);
|
|
186
|
+
return `${raw.length}:${raw}`;
|
|
187
|
+
}).join("");
|
|
188
|
+
}
|
|
189
|
+
const objectIds = /* @__PURE__ */ new WeakMap();
|
|
190
|
+
let objectIdCounter = 0;
|
|
191
|
+
function fingerprintValue(v) {
|
|
192
|
+
if (v === void 0) return "u:";
|
|
193
|
+
if (v === null) return "n:";
|
|
194
|
+
if (typeof v === "string") return `s:${v}`;
|
|
195
|
+
if (typeof v === "number") {
|
|
196
|
+
if (Object.is(v, -0)) return "d:-0";
|
|
197
|
+
return `d:${v}`;
|
|
198
|
+
}
|
|
199
|
+
if (typeof v === "boolean") return `b:${v}`;
|
|
200
|
+
if (typeof v === "symbol") return `y:${stableSymbolKey(v)}`;
|
|
201
|
+
if (typeof v === "object" || typeof v === "function") {
|
|
202
|
+
let id = objectIds.get(v);
|
|
203
|
+
if (id === void 0) {
|
|
204
|
+
id = objectIdCounter++;
|
|
205
|
+
objectIds.set(v, id);
|
|
206
|
+
}
|
|
207
|
+
return `o:${id}`;
|
|
208
|
+
}
|
|
209
|
+
return `?:${String(v)}`;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Creates a {@link ReplayKey} from a path, raw input, and dependency values.
|
|
213
|
+
*
|
|
214
|
+
* @param path The parser path.
|
|
215
|
+
* @param rawInput The raw input string.
|
|
216
|
+
* @param dependencyValues The dependency values.
|
|
217
|
+
* @returns A replay key.
|
|
218
|
+
* @internal
|
|
219
|
+
* @since 1.0.0
|
|
220
|
+
*/
|
|
221
|
+
const parserIds = /* @__PURE__ */ new WeakMap();
|
|
222
|
+
let parserIdCounter = 0;
|
|
223
|
+
/** Get a stable identity string for a replayParse function reference. */
|
|
224
|
+
function getParserFingerprint(replayParse) {
|
|
225
|
+
let id = parserIds.get(replayParse);
|
|
226
|
+
if (id === void 0) {
|
|
227
|
+
id = parserIdCounter++;
|
|
228
|
+
parserIds.set(replayParse, id);
|
|
229
|
+
}
|
|
230
|
+
return `p:${id}`;
|
|
231
|
+
}
|
|
232
|
+
function createReplayKey(path, rawInput, dependencyValues, replayParse) {
|
|
233
|
+
return {
|
|
234
|
+
path,
|
|
235
|
+
rawInput,
|
|
236
|
+
dependencyFingerprint: createDependencyFingerprint(dependencyValues),
|
|
237
|
+
parserFingerprint: replayParse != null ? getParserFingerprint(replayParse) : ""
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
129
241
|
* Collects explicit source values from parser states and registers them
|
|
130
242
|
* in the runtime context.
|
|
131
243
|
*
|
|
132
244
|
* @param nodes The runtime nodes to inspect.
|
|
133
245
|
* @param runtime The dependency runtime context.
|
|
246
|
+
* @throws Propagates synchronous errors thrown by `extractSourceValue()`.
|
|
247
|
+
* @throws {TypeError} If `extractSourceValue()` returns a promise-like result.
|
|
248
|
+
* Use {@link collectExplicitSourceValuesAsync} when async source
|
|
249
|
+
* extraction is expected.
|
|
134
250
|
* @internal
|
|
135
251
|
* @since 1.0.0
|
|
136
252
|
*/
|
|
@@ -140,10 +256,205 @@ function collectExplicitSourceValues(nodes, runtime) {
|
|
|
140
256
|
if (meta?.source == null) continue;
|
|
141
257
|
if (meta.source.extractSourceValue == null) continue;
|
|
142
258
|
const result = meta.source.extractSourceValue(node.state);
|
|
143
|
-
if (result
|
|
144
|
-
|
|
145
|
-
|
|
259
|
+
if (isPromiseLike(result)) throw new TypeError(`collectExplicitSourceValues() received an async extractSourceValue() result for ${String(meta.source.sourceId)}. Use collectExplicitSourceValuesAsync() instead.`);
|
|
260
|
+
registerExplicitSourceValue(meta.source.sourceId, result, runtime);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function registerExplicitSourceValue(sourceId, result, runtime) {
|
|
264
|
+
if (result == null) return;
|
|
265
|
+
if (result.success) runtime.registerSource(sourceId, result.value, "cli");
|
|
266
|
+
else runtime.markSourceFailed(sourceId);
|
|
267
|
+
}
|
|
268
|
+
function isPromiseLike(value) {
|
|
269
|
+
return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Async version of {@link collectExplicitSourceValues}.
|
|
273
|
+
* Awaits async `extractSourceValue` results instead of rejecting
|
|
274
|
+
* promise-like values as the synchronous variant does.
|
|
275
|
+
*
|
|
276
|
+
* @param nodes The runtime nodes to inspect.
|
|
277
|
+
* @param runtime The dependency runtime context.
|
|
278
|
+
* @throws Propagates errors thrown or rejected by `extractSourceValue()`.
|
|
279
|
+
* @internal
|
|
280
|
+
* @since 1.0.0
|
|
281
|
+
*/
|
|
282
|
+
async function collectExplicitSourceValuesAsync(nodes, runtime) {
|
|
283
|
+
for (const node of nodes) {
|
|
284
|
+
const meta = node.parser.dependencyMetadata;
|
|
285
|
+
if (meta?.source == null) continue;
|
|
286
|
+
if (meta.source.extractSourceValue == null) continue;
|
|
287
|
+
const result = await meta.source.extractSourceValue(node.state);
|
|
288
|
+
registerExplicitSourceValue(meta.source.sourceId, result, runtime);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Fills missing source defaults for source parsers whose state is
|
|
293
|
+
* unpopulated.
|
|
294
|
+
*
|
|
295
|
+
* Returns an array of failures for sources whose default evaluation
|
|
296
|
+
* failed (either threw or returned `{ success: false }`). The caller
|
|
297
|
+
* should propagate these so that dependent parsers see the real error
|
|
298
|
+
* instead of silently treating the source as absent.
|
|
299
|
+
*
|
|
300
|
+
* @param nodes The runtime nodes to inspect.
|
|
301
|
+
* @param runtime The dependency runtime context.
|
|
302
|
+
* @returns Failures from default evaluation (empty if all succeeded).
|
|
303
|
+
* @throws {TypeError} If `getMissingSourceValue()` returns a promise-like
|
|
304
|
+
* result. Use {@link fillMissingSourceDefaultsAsync} when async
|
|
305
|
+
* default extraction is expected.
|
|
306
|
+
* @internal
|
|
307
|
+
* @since 1.0.0
|
|
308
|
+
*/
|
|
309
|
+
function fillMissingSourceDefaults(nodes, runtime) {
|
|
310
|
+
const failures = [];
|
|
311
|
+
for (const node of nodes) {
|
|
312
|
+
const meta = node.parser.dependencyMetadata;
|
|
313
|
+
if (meta?.source == null) continue;
|
|
314
|
+
if (runtime.hasSource(meta.source.sourceId)) continue;
|
|
315
|
+
if (runtime.isSourceFailed(meta.source.sourceId)) continue;
|
|
316
|
+
if (node.matched === true) continue;
|
|
317
|
+
if (!meta.source.preservesSourceValue) continue;
|
|
318
|
+
if (meta.source.getMissingSourceValue == null) continue;
|
|
319
|
+
let result;
|
|
320
|
+
try {
|
|
321
|
+
result = meta.source.getMissingSourceValue();
|
|
322
|
+
} catch (e) {
|
|
323
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
324
|
+
failures.push({
|
|
325
|
+
sourceId: meta.source.sourceId,
|
|
326
|
+
path: node.path,
|
|
327
|
+
error: {
|
|
328
|
+
success: false,
|
|
329
|
+
error: require_message.message`Default value evaluation failed: ${msg}`
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (isPromiseLike(result)) throw new TypeError(`fillMissingSourceDefaults() received an async getMissingSourceValue() result for ${String(meta.source.sourceId)}. Use fillMissingSourceDefaultsAsync() instead.`);
|
|
335
|
+
if (result.success) runtime.registerSource(meta.source.sourceId, result.value, "default");
|
|
336
|
+
else failures.push({
|
|
337
|
+
sourceId: meta.source.sourceId,
|
|
338
|
+
path: node.path,
|
|
339
|
+
error: result
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return failures;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Async version of {@link fillMissingSourceDefaults}.
|
|
346
|
+
* Awaits async `getMissingSourceValue` results.
|
|
347
|
+
*
|
|
348
|
+
* @param nodes The runtime nodes to inspect.
|
|
349
|
+
* @param runtime The dependency runtime context.
|
|
350
|
+
* @returns Failures from default evaluation (empty if all succeeded).
|
|
351
|
+
* @internal
|
|
352
|
+
* @since 1.0.0
|
|
353
|
+
*/
|
|
354
|
+
async function fillMissingSourceDefaultsAsync(nodes, runtime) {
|
|
355
|
+
const failures = [];
|
|
356
|
+
for (const node of nodes) {
|
|
357
|
+
const meta = node.parser.dependencyMetadata;
|
|
358
|
+
if (meta?.source == null) continue;
|
|
359
|
+
if (runtime.hasSource(meta.source.sourceId)) continue;
|
|
360
|
+
if (runtime.isSourceFailed(meta.source.sourceId)) continue;
|
|
361
|
+
if (node.matched === true) continue;
|
|
362
|
+
if (!meta.source.preservesSourceValue) continue;
|
|
363
|
+
if (meta.source.getMissingSourceValue == null) continue;
|
|
364
|
+
let result;
|
|
365
|
+
try {
|
|
366
|
+
result = await meta.source.getMissingSourceValue();
|
|
367
|
+
} catch (e) {
|
|
368
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
369
|
+
failures.push({
|
|
370
|
+
sourceId: meta.source.sourceId,
|
|
371
|
+
path: node.path,
|
|
372
|
+
error: {
|
|
373
|
+
success: false,
|
|
374
|
+
error: require_message.message`Default value evaluation failed: ${msg}`
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (result.success) runtime.registerSource(meta.source.sourceId, result.value, "default");
|
|
380
|
+
else failures.push({
|
|
381
|
+
sourceId: meta.source.sourceId,
|
|
382
|
+
path: node.path,
|
|
383
|
+
error: result
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return failures;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Replays a derived parser with resolved dependency values (sync).
|
|
390
|
+
*
|
|
391
|
+
* Returns `undefined` if dependencies cannot be resolved.
|
|
392
|
+
*
|
|
393
|
+
* @param node The runtime node with derived metadata.
|
|
394
|
+
* @param rawInput The raw input to replay.
|
|
395
|
+
* @param runtime The dependency runtime context.
|
|
396
|
+
* @returns The replay result, or `undefined`.
|
|
397
|
+
* @throws {TypeError} If `replayParse()` returns a promise-like result.
|
|
398
|
+
* Use {@link replayDerivedParserAsync} for async replay.
|
|
399
|
+
* @internal
|
|
400
|
+
* @since 1.0.0
|
|
401
|
+
*/
|
|
402
|
+
function replayDerivedParser(node, rawInput, runtime) {
|
|
403
|
+
const meta = node.parser.dependencyMetadata;
|
|
404
|
+
if (meta?.derived == null) return void 0;
|
|
405
|
+
let defaults = node.defaultDependencyValues;
|
|
406
|
+
if (defaults == null && meta.derived.getDefaultDependencyValues != null) try {
|
|
407
|
+
defaults = meta.derived.getDefaultDependencyValues();
|
|
408
|
+
} catch {
|
|
409
|
+
return void 0;
|
|
410
|
+
}
|
|
411
|
+
const resolution = runtime.resolveDependencies({
|
|
412
|
+
dependencyIds: meta.derived.dependencyIds,
|
|
413
|
+
defaultValues: defaults
|
|
414
|
+
});
|
|
415
|
+
if (resolution.kind === "missing") return void 0;
|
|
416
|
+
if (resolution.kind === "partial") return void 0;
|
|
417
|
+
const key = createReplayKey(node.path, rawInput, resolution.values, meta.derived.replayParse);
|
|
418
|
+
const cached = runtime.getReplayResult(key);
|
|
419
|
+
if (cached != null) return cached;
|
|
420
|
+
const result = meta.derived.replayParse(rawInput, resolution.values);
|
|
421
|
+
if (isPromiseLike(result)) throw new TypeError("replayDerivedParser() received an async replayParse() result. Use replayDerivedParserAsync() instead.");
|
|
422
|
+
runtime.setReplayResult(key, result);
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Replays a derived parser with resolved dependency values (async).
|
|
427
|
+
*
|
|
428
|
+
* Returns `undefined` if dependencies cannot be resolved.
|
|
429
|
+
*
|
|
430
|
+
* @param node The runtime node with derived metadata.
|
|
431
|
+
* @param rawInput The raw input to replay.
|
|
432
|
+
* @param runtime The dependency runtime context.
|
|
433
|
+
* @returns The replay result, or `undefined`.
|
|
434
|
+
* @internal
|
|
435
|
+
* @since 1.0.0
|
|
436
|
+
*/
|
|
437
|
+
async function replayDerivedParserAsync(node, rawInput, runtime) {
|
|
438
|
+
const meta = node.parser.dependencyMetadata;
|
|
439
|
+
if (meta?.derived == null) return void 0;
|
|
440
|
+
let defaults = node.defaultDependencyValues;
|
|
441
|
+
if (defaults == null && meta.derived.getDefaultDependencyValues != null) try {
|
|
442
|
+
defaults = meta.derived.getDefaultDependencyValues();
|
|
443
|
+
} catch {
|
|
444
|
+
return void 0;
|
|
146
445
|
}
|
|
446
|
+
const resolution = runtime.resolveDependencies({
|
|
447
|
+
dependencyIds: meta.derived.dependencyIds,
|
|
448
|
+
defaultValues: defaults
|
|
449
|
+
});
|
|
450
|
+
if (resolution.kind === "missing") return void 0;
|
|
451
|
+
if (resolution.kind === "partial") return void 0;
|
|
452
|
+
const key = createReplayKey(node.path, rawInput, resolution.values, meta.derived.replayParse);
|
|
453
|
+
const cached = runtime.getReplayResult(key);
|
|
454
|
+
if (cached != null) return cached;
|
|
455
|
+
const result = await meta.derived.replayParse(rawInput, resolution.values);
|
|
456
|
+
runtime.setReplayResult(key, result);
|
|
457
|
+
return result;
|
|
147
458
|
}
|
|
148
459
|
/**
|
|
149
460
|
* Checks if a value is a plain object (not a class instance) for the
|
|
@@ -171,7 +482,7 @@ function resolveSingleDeferred(deferred, runtime) {
|
|
|
171
482
|
if (resolution.usedDefaults.every((d) => d)) return deferred.preliminaryResult;
|
|
172
483
|
const depValue = isMultiDep ? resolution.values : resolution.values[0];
|
|
173
484
|
const result = deferred.parser[require_dependency.parseWithDependency](deferred.rawInput, depValue);
|
|
174
|
-
if (result
|
|
485
|
+
if (isPromiseLike(result)) throw new TypeError("resolveStateWithRuntime() received an async parseWithDependency() result. Use resolveStateWithRuntimeAsync() instead.");
|
|
175
486
|
return result;
|
|
176
487
|
}
|
|
177
488
|
/**
|
|
@@ -180,8 +491,14 @@ function resolveSingleDeferred(deferred, runtime) {
|
|
|
180
491
|
*
|
|
181
492
|
* This must run BEFORE deferred resolution so that all source values
|
|
182
493
|
* are available when replaying derived parsers.
|
|
494
|
+
*
|
|
495
|
+
* @param state The state tree to traverse.
|
|
496
|
+
* @param runtime The dependency runtime context to populate.
|
|
497
|
+
* @param visited Cycle guard for recursive traversal.
|
|
498
|
+
* @param excludedFields Optional property keys to skip at the current level.
|
|
499
|
+
* This exclusion set is not propagated recursively.
|
|
183
500
|
*/
|
|
184
|
-
function collectSourcesFromState(state, runtime, visited = /* @__PURE__ */ new WeakSet()) {
|
|
501
|
+
function collectSourcesFromState(state, runtime, visited = /* @__PURE__ */ new WeakSet(), excludedFields) {
|
|
185
502
|
if (state == null || typeof state !== "object") return;
|
|
186
503
|
if (visited.has(state)) return;
|
|
187
504
|
visited.add(state);
|
|
@@ -197,7 +514,10 @@ function collectSourcesFromState(state, runtime, visited = /* @__PURE__ */ new W
|
|
|
197
514
|
for (const item of state) collectSourcesFromState(item, runtime, visited);
|
|
198
515
|
return;
|
|
199
516
|
}
|
|
200
|
-
if (typeof state === "object") for (const key of Reflect.ownKeys(state))
|
|
517
|
+
if (typeof state === "object") for (const key of Reflect.ownKeys(state)) {
|
|
518
|
+
if (excludedFields?.has(key)) continue;
|
|
519
|
+
collectSourcesFromState(state[key], runtime, visited);
|
|
520
|
+
}
|
|
201
521
|
}
|
|
202
522
|
/**
|
|
203
523
|
* Recursively resolves all {@link DeferredParseState} objects in a state
|
|
@@ -214,6 +534,9 @@ function collectSourcesFromState(state, runtime, visited = /* @__PURE__ */ new W
|
|
|
214
534
|
* @param state The state tree to resolve.
|
|
215
535
|
* @param runtime The dependency runtime context.
|
|
216
536
|
* @returns The resolved state tree.
|
|
537
|
+
* @throws {TypeError} If a deferred parser returns a promise-like result from
|
|
538
|
+
* `parseWithDependency()`. Use {@link resolveStateWithRuntimeAsync}
|
|
539
|
+
* for async resolution.
|
|
217
540
|
* @internal
|
|
218
541
|
* @since 1.0.0
|
|
219
542
|
*/
|
|
@@ -230,10 +553,16 @@ function resolveDeferredInState(state, runtime, visited = /* @__PURE__ */ new We
|
|
|
230
553
|
}
|
|
231
554
|
if (require_dependency.isDeferredParseState(state)) return resolveSingleDeferred(state, runtime);
|
|
232
555
|
if (require_dependency.isDependencySourceState(state)) return state;
|
|
233
|
-
if (Array.isArray(state))
|
|
556
|
+
if (Array.isArray(state)) {
|
|
557
|
+
const resolved = state.map((item) => resolveDeferredInState(item, runtime, visited));
|
|
558
|
+
return resolved.every((item, index) => item === state[index]) ? state : resolved;
|
|
559
|
+
}
|
|
234
560
|
if (isPlainObject(state)) {
|
|
561
|
+
const keys = Reflect.ownKeys(state);
|
|
562
|
+
const resolvedEntries = keys.map((key) => [key, resolveDeferredInState(state[key], runtime, visited)]);
|
|
563
|
+
if (resolvedEntries.every(([key, value]) => value === state[key])) return state;
|
|
235
564
|
const resolved = Object.create(Object.getPrototypeOf(state));
|
|
236
|
-
for (const key of
|
|
565
|
+
for (const [key, value] of resolvedEntries) resolved[key] = value;
|
|
237
566
|
return resolved;
|
|
238
567
|
}
|
|
239
568
|
return state;
|
|
@@ -272,13 +601,18 @@ async function resolveDeferredInStateAsync(state, runtime, visited = /* @__PURE_
|
|
|
272
601
|
return Promise.resolve(deferred.parser[require_dependency.parseWithDependency](deferred.rawInput, depValue));
|
|
273
602
|
}
|
|
274
603
|
if (require_dependency.isDependencySourceState(state)) return state;
|
|
275
|
-
if (Array.isArray(state))
|
|
604
|
+
if (Array.isArray(state)) {
|
|
605
|
+
const resolved = await Promise.all(state.map((item) => resolveDeferredInStateAsync(item, runtime, visited)));
|
|
606
|
+
return resolved.every((item, index) => item === state[index]) ? state : resolved;
|
|
607
|
+
}
|
|
276
608
|
if (isPlainObject(state)) {
|
|
277
|
-
const resolved = Object.create(Object.getPrototypeOf(state));
|
|
278
609
|
const keys = Reflect.ownKeys(state);
|
|
279
|
-
await Promise.all(keys.map(async (key) => {
|
|
280
|
-
|
|
610
|
+
const resolvedEntries = await Promise.all(keys.map(async (key) => {
|
|
611
|
+
return [key, await resolveDeferredInStateAsync(state[key], runtime, visited)];
|
|
281
612
|
}));
|
|
613
|
+
if (resolvedEntries.every(([key, value]) => value === state[key])) return state;
|
|
614
|
+
const resolved = Object.create(Object.getPrototypeOf(state));
|
|
615
|
+
for (const [key, value] of resolvedEntries) resolved[key] = value;
|
|
282
616
|
return resolved;
|
|
283
617
|
}
|
|
284
618
|
return state;
|
|
@@ -289,8 +623,9 @@ async function resolveDeferredInStateAsync(state, runtime, visited = /* @__PURE_
|
|
|
289
623
|
*/
|
|
290
624
|
function isMatchedState(fieldState, parser) {
|
|
291
625
|
if (fieldState === void 0) return false;
|
|
292
|
-
|
|
293
|
-
if (require_dependency.isPendingDependencySourceState(
|
|
626
|
+
const innerState = Array.isArray(fieldState) && fieldState.length === 1 ? fieldState[0] : fieldState;
|
|
627
|
+
if (require_dependency.isPendingDependencySourceState(innerState)) return false;
|
|
628
|
+
if (parser[require_parser.unmatchedNonCliDependencySourceStateMarker] === true && innerState != null && typeof innerState === "object" && Object.hasOwn(innerState, "hasCliValue") && innerState.hasCliValue === false) return false;
|
|
294
629
|
if (fieldState === parser.initialState) return false;
|
|
295
630
|
return true;
|
|
296
631
|
}
|
|
@@ -320,11 +655,44 @@ function buildRuntimeNodesFromPairs(pairs, state, parentPath) {
|
|
|
320
655
|
}
|
|
321
656
|
return nodes;
|
|
322
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* Builds {@link RuntimeNode}s from a parser array and a state array.
|
|
660
|
+
*
|
|
661
|
+
* Used by `tuple()` and `concat()` constructs.
|
|
662
|
+
*
|
|
663
|
+
* @param parsers The child parsers.
|
|
664
|
+
* @param stateArray The state array (one element per parser).
|
|
665
|
+
* @param parentPath Optional parent path prefix.
|
|
666
|
+
* @returns An array of runtime nodes.
|
|
667
|
+
* @internal
|
|
668
|
+
* @since 1.0.0
|
|
669
|
+
*/
|
|
670
|
+
function buildRuntimeNodesFromArray(parsers, stateArray, parentPath) {
|
|
671
|
+
const prefix = parentPath ?? [];
|
|
672
|
+
const nodes = [];
|
|
673
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
674
|
+
const parser = parsers[i];
|
|
675
|
+
const elemState = i < stateArray.length ? stateArray[i] : void 0;
|
|
676
|
+
nodes.push({
|
|
677
|
+
path: [...prefix, i],
|
|
678
|
+
parser,
|
|
679
|
+
state: elemState,
|
|
680
|
+
matched: isMatchedState(elemState, parser)
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
return nodes;
|
|
684
|
+
}
|
|
323
685
|
|
|
324
686
|
//#endregion
|
|
687
|
+
exports.buildRuntimeNodesFromArray = buildRuntimeNodesFromArray;
|
|
325
688
|
exports.buildRuntimeNodesFromPairs = buildRuntimeNodesFromPairs;
|
|
326
689
|
exports.collectExplicitSourceValues = collectExplicitSourceValues;
|
|
690
|
+
exports.collectExplicitSourceValuesAsync = collectExplicitSourceValuesAsync;
|
|
327
691
|
exports.collectSourcesFromState = collectSourcesFromState;
|
|
328
692
|
exports.createDependencyRuntimeContext = createDependencyRuntimeContext;
|
|
693
|
+
exports.fillMissingSourceDefaults = fillMissingSourceDefaults;
|
|
694
|
+
exports.fillMissingSourceDefaultsAsync = fillMissingSourceDefaultsAsync;
|
|
695
|
+
exports.replayDerivedParser = replayDerivedParser;
|
|
696
|
+
exports.replayDerivedParserAsync = replayDerivedParserAsync;
|
|
329
697
|
exports.resolveStateWithRuntime = resolveStateWithRuntime;
|
|
330
698
|
exports.resolveStateWithRuntimeAsync = resolveStateWithRuntimeAsync;
|