@optique/core 1.3.0-dev.2396 → 1.3.0-dev.2399
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 +22 -18
- package/dist/constructs.js +23 -19
- package/dist/dependency-metadata.cjs +13 -6
- package/dist/dependency-metadata.d.cts +4 -0
- package/dist/dependency-metadata.d.ts +4 -0
- package/dist/dependency-metadata.js +14 -7
- package/dist/dependency-runtime.cjs +396 -5
- package/dist/dependency-runtime.d.cts +68 -1
- package/dist/dependency-runtime.d.ts +68 -1
- package/dist/dependency-runtime.js +393 -7
- package/dist/internal/dependency.cjs +31 -10
- package/dist/internal/dependency.d.cts +23 -6
- package/dist/internal/dependency.d.ts +23 -6
- package/dist/internal/dependency.js +31 -11
- package/dist/primitives.cjs +19 -11
- package/dist/primitives.js +20 -12
- package/package.json +2 -2
- package/skills/optique/SKILL.md +7 -0
|
@@ -3,6 +3,32 @@ const require_internal_dependency = require('./internal/dependency.cjs');
|
|
|
3
3
|
const require_internal_parser = require('./internal/parser.cjs');
|
|
4
4
|
|
|
5
5
|
//#region src/dependency-runtime.ts
|
|
6
|
+
/**
|
|
7
|
+
* Stores the raw token parsed by a derived value parser on structural parser
|
|
8
|
+
* states that can safely carry an in-band annotation.
|
|
9
|
+
*
|
|
10
|
+
* The execution trace remains the canonical diagnostic record. This state
|
|
11
|
+
* marker lets construct-independent dependency resolution replay a derived
|
|
12
|
+
* source before downstream fields complete.
|
|
13
|
+
* @internal
|
|
14
|
+
* @since 1.3.0
|
|
15
|
+
*/
|
|
16
|
+
const derivedRawInputKey = Symbol("@optique/core/dependency-runtime/derivedRawInput");
|
|
17
|
+
const derivedRawInputs = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
/**
|
|
19
|
+
* Records a derived parser's raw token without modifying its parse result.
|
|
20
|
+
*
|
|
21
|
+
* Parse results may be frozen or carry class private state, so primitives keep
|
|
22
|
+
* their original identity and associate replay metadata out of band.
|
|
23
|
+
*
|
|
24
|
+
* @param state The original value parser result.
|
|
25
|
+
* @param rawInput The token parsed into that result.
|
|
26
|
+
* @internal
|
|
27
|
+
* @since 1.3.0
|
|
28
|
+
*/
|
|
29
|
+
function recordDerivedRawInput(state, rawInput) {
|
|
30
|
+
derivedRawInputs.set(state, rawInput);
|
|
31
|
+
}
|
|
6
32
|
const symbolIds = /* @__PURE__ */ new WeakMap();
|
|
7
33
|
let symbolCounter = 0;
|
|
8
34
|
function stableSymbolKey(sym) {
|
|
@@ -19,6 +45,8 @@ var DependencyRuntimeContextImpl = class {
|
|
|
19
45
|
registry;
|
|
20
46
|
#replayCache = /* @__PURE__ */ new Map();
|
|
21
47
|
#failedSources = /* @__PURE__ */ new Set();
|
|
48
|
+
#sourceMetadata = /* @__PURE__ */ new Map();
|
|
49
|
+
#sourceFailures = /* @__PURE__ */ new Map();
|
|
22
50
|
constructor(registry) {
|
|
23
51
|
if (registry instanceof FailedAwareRegistry) {
|
|
24
52
|
this.registry = registry.rebindFailedSources(this.#failedSources);
|
|
@@ -28,6 +56,7 @@ var DependencyRuntimeContextImpl = class {
|
|
|
28
56
|
}
|
|
29
57
|
registerSource(sourceId, value) {
|
|
30
58
|
this.registry.set(sourceId, value);
|
|
59
|
+
this.#sourceFailures.delete(sourceId);
|
|
31
60
|
}
|
|
32
61
|
hasSource(sourceId) {
|
|
33
62
|
return this.registry.has(sourceId);
|
|
@@ -46,6 +75,45 @@ var DependencyRuntimeContextImpl = class {
|
|
|
46
75
|
}
|
|
47
76
|
markSourceFailed(sourceId) {
|
|
48
77
|
this.#failedSources.add(sourceId);
|
|
78
|
+
const lineage = this.#getSourceLineage(sourceId, /* @__PURE__ */ new Set());
|
|
79
|
+
const failure = {
|
|
80
|
+
chain: lineage.labels,
|
|
81
|
+
participants: lineage.sourceIds,
|
|
82
|
+
diagnosticChain: lineage.labels
|
|
83
|
+
};
|
|
84
|
+
this.#sourceFailures.set(sourceId, failure);
|
|
85
|
+
this.#promoteDiagnosticChain(failure);
|
|
86
|
+
}
|
|
87
|
+
registerSourceMetadata(sourceId, label, dependencyIds = []) {
|
|
88
|
+
this.#sourceMetadata.set(sourceId, {
|
|
89
|
+
label,
|
|
90
|
+
dependencyIds
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
propagateSourceFailure(dependencyIds, label, sourceId) {
|
|
94
|
+
const upstream = dependencyIds.filter((id) => this.isSourceFailed(id)).map((id) => this.#sourceFailures.get(id) ?? {
|
|
95
|
+
chain: [this.#getSourceLabel(id)],
|
|
96
|
+
participants: [id],
|
|
97
|
+
diagnosticChain: [this.#getSourceLabel(id)]
|
|
98
|
+
}).sort((left, right) => right.chain.length - left.chain.length)[0];
|
|
99
|
+
if (upstream == null) return false;
|
|
100
|
+
const chain = upstream.chain.at(-1) === label ? upstream.chain : [...upstream.chain, label];
|
|
101
|
+
const participants = sourceId == null || upstream.participants.includes(sourceId) ? upstream.participants : [...upstream.participants, sourceId];
|
|
102
|
+
const failure = {
|
|
103
|
+
chain,
|
|
104
|
+
participants,
|
|
105
|
+
diagnosticChain: chain
|
|
106
|
+
};
|
|
107
|
+
if (sourceId != null) {
|
|
108
|
+
this.#failedSources.add(sourceId);
|
|
109
|
+
this.#sourceFailures.set(sourceId, failure);
|
|
110
|
+
}
|
|
111
|
+
this.#promoteDiagnosticChain(failure);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
getSourceFailureChain(sourceId) {
|
|
115
|
+
if (!this.#failedSources.has(sourceId)) return void 0;
|
|
116
|
+
return this.#sourceFailures.get(sourceId)?.diagnosticChain;
|
|
49
117
|
}
|
|
50
118
|
isSourceFailed(sourceId) {
|
|
51
119
|
return this.#failedSources.has(sourceId);
|
|
@@ -53,6 +121,31 @@ var DependencyRuntimeContextImpl = class {
|
|
|
53
121
|
getSuggestionDependencies(request) {
|
|
54
122
|
return resolveRequest(this, request);
|
|
55
123
|
}
|
|
124
|
+
#getSourceLabel(sourceId) {
|
|
125
|
+
return this.#sourceMetadata.get(sourceId)?.label ?? sourceId.description ?? String(sourceId);
|
|
126
|
+
}
|
|
127
|
+
#getSourceLineage(sourceId, visited) {
|
|
128
|
+
if (visited.has(sourceId)) return {
|
|
129
|
+
labels: [this.#getSourceLabel(sourceId)],
|
|
130
|
+
sourceIds: [sourceId]
|
|
131
|
+
};
|
|
132
|
+
visited.add(sourceId);
|
|
133
|
+
const metadata = this.#sourceMetadata.get(sourceId);
|
|
134
|
+
const upstream = (metadata?.dependencyIds ?? []).map((id) => this.#getSourceLineage(id, new Set(visited))).sort((left, right) => right.labels.length - left.labels.length)[0];
|
|
135
|
+
return upstream == null ? {
|
|
136
|
+
labels: [this.#getSourceLabel(sourceId)],
|
|
137
|
+
sourceIds: [sourceId]
|
|
138
|
+
} : {
|
|
139
|
+
labels: [...upstream.labels, this.#getSourceLabel(sourceId)],
|
|
140
|
+
sourceIds: [...upstream.sourceIds, sourceId]
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
#promoteDiagnosticChain(failure) {
|
|
144
|
+
for (const sourceId of failure.participants) {
|
|
145
|
+
const current = this.#sourceFailures.get(sourceId);
|
|
146
|
+
if (current != null && current.diagnosticChain.length < failure.diagnosticChain.length) current.diagnosticChain = failure.diagnosticChain;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
56
149
|
};
|
|
57
150
|
/**
|
|
58
151
|
* Registry wrapper that hides values for sources that have failed.
|
|
@@ -251,14 +344,17 @@ function createReplayKey(path, rawInput, dependencyValues, replayParse) {
|
|
|
251
344
|
* @since 1.0.0
|
|
252
345
|
*/
|
|
253
346
|
function collectExplicitSourceValues(nodes, runtime) {
|
|
347
|
+
registerRuntimeSourceMetadata(nodes, runtime);
|
|
254
348
|
for (const node of nodes) {
|
|
255
349
|
const meta = node.parser.dependencyMetadata;
|
|
256
350
|
if (meta?.source == null) continue;
|
|
257
351
|
if (meta.source.extractSourceValue == null) continue;
|
|
352
|
+
if (meta.derived != null && getNodeRawInput(node) != null) continue;
|
|
258
353
|
const result = meta.source.extractSourceValue(node.state);
|
|
259
354
|
if (isPromiseLike(result)) throw new TypeError(`collectExplicitSourceValues() received an async extractSourceValue() result for ${String(meta.source.sourceId)}. Use collectExplicitSourceValuesAsync() instead.`);
|
|
260
355
|
registerExplicitSourceValue(meta.source.sourceId, result, runtime);
|
|
261
356
|
}
|
|
357
|
+
resolveDerivedSourceValues(nodes, runtime);
|
|
262
358
|
}
|
|
263
359
|
function registerExplicitSourceValue(sourceId, result, runtime) {
|
|
264
360
|
if (result == null) return;
|
|
@@ -280,13 +376,182 @@ function isPromiseLike(value) {
|
|
|
280
376
|
* @since 1.0.0
|
|
281
377
|
*/
|
|
282
378
|
async function collectExplicitSourceValuesAsync(nodes, runtime) {
|
|
379
|
+
registerRuntimeSourceMetadata(nodes, runtime);
|
|
283
380
|
for (const node of nodes) {
|
|
284
381
|
const meta = node.parser.dependencyMetadata;
|
|
285
382
|
if (meta?.source == null) continue;
|
|
286
383
|
if (meta.source.extractSourceValue == null) continue;
|
|
384
|
+
if (meta.derived != null && getNodeRawInput(node) != null) continue;
|
|
287
385
|
const result = await meta.source.extractSourceValue(node.state);
|
|
288
386
|
registerExplicitSourceValue(meta.source.sourceId, result, runtime);
|
|
289
387
|
}
|
|
388
|
+
await resolveDerivedSourceValuesAsync(nodes, runtime);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Orders runtime nodes so every in-scope provider precedes a derived source
|
|
392
|
+
* that consumes it. Independent nodes retain declaration order.
|
|
393
|
+
*
|
|
394
|
+
* Missing providers create no edge because the consumer may use its declared
|
|
395
|
+
* default. Scheduling barriers act as providers for the source IDs their
|
|
396
|
+
* selected subtree may expose and depend on their discriminator source.
|
|
397
|
+
*
|
|
398
|
+
* @param nodes Runtime nodes in declaration order.
|
|
399
|
+
* @returns The same nodes in stable dependency order.
|
|
400
|
+
* @throws {TypeError} If active provider edges contain a cycle.
|
|
401
|
+
* @internal
|
|
402
|
+
* @since 1.3.0
|
|
403
|
+
*/
|
|
404
|
+
function orderDependencyNodes(nodes) {
|
|
405
|
+
const providers = /* @__PURE__ */ new Map();
|
|
406
|
+
const addProvider = (sourceId, node) => {
|
|
407
|
+
const existing = providers.get(sourceId);
|
|
408
|
+
if (existing == null) providers.set(sourceId, [node]);
|
|
409
|
+
else existing.push(node);
|
|
410
|
+
};
|
|
411
|
+
for (const node of nodes) {
|
|
412
|
+
const source = node.parser.dependencyMetadata?.source;
|
|
413
|
+
if (source != null) addProvider(source.sourceId, node);
|
|
414
|
+
for (const sourceId of node.providesSourceIds ?? []) addProvider(sourceId, node);
|
|
415
|
+
}
|
|
416
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
417
|
+
const indegree = /* @__PURE__ */ new Map();
|
|
418
|
+
for (const node of nodes) indegree.set(node, 0);
|
|
419
|
+
const addEdge = (provider, consumer) => {
|
|
420
|
+
const edges = outgoing.get(provider) ?? /* @__PURE__ */ new Set();
|
|
421
|
+
if (edges.has(consumer)) return;
|
|
422
|
+
edges.add(consumer);
|
|
423
|
+
outgoing.set(provider, edges);
|
|
424
|
+
indegree.set(consumer, (indegree.get(consumer) ?? 0) + 1);
|
|
425
|
+
};
|
|
426
|
+
for (const node of nodes) {
|
|
427
|
+
const derived = node.parser.dependencyMetadata?.derived;
|
|
428
|
+
if (derived != null) for (const dependencySourceId of derived.dependencyIds) for (const provider of providers.get(dependencySourceId) ?? []) addEdge(provider, node);
|
|
429
|
+
if (node.requiresSourceId != null) {
|
|
430
|
+
for (const provider of providers.get(node.requiresSourceId) ?? []) if (provider !== node) addEdge(provider, node);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const declarationOrder = new Map(nodes.map((node, index) => [node, index]));
|
|
434
|
+
const ready = nodes.filter((node) => indegree.get(node) === 0);
|
|
435
|
+
const ordered = [];
|
|
436
|
+
while (ready.length > 0) {
|
|
437
|
+
ready.sort((left, right) => declarationOrder.get(left) - declarationOrder.get(right));
|
|
438
|
+
const node = ready.shift();
|
|
439
|
+
ordered.push(node);
|
|
440
|
+
for (const consumer of outgoing.get(node) ?? []) {
|
|
441
|
+
const next = (indegree.get(consumer) ?? 0) - 1;
|
|
442
|
+
indegree.set(consumer, next);
|
|
443
|
+
if (next === 0) ready.push(consumer);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (ordered.length !== nodes.length) {
|
|
447
|
+
const cycle = nodes.filter((node) => (indegree.get(node) ?? 0) > 0);
|
|
448
|
+
const labels = cycle.map(formatDependencyNodeLabel).join(" -> ");
|
|
449
|
+
throw new TypeError(`Circular dependency detected among derived sources: ${labels}.`);
|
|
450
|
+
}
|
|
451
|
+
return ordered;
|
|
452
|
+
}
|
|
453
|
+
/** Resolves and publishes matched derived sources in stable dependency order. */
|
|
454
|
+
function resolveDerivedSourceValues(nodes, runtime, options) {
|
|
455
|
+
resolveDerivedSourceValuesInOrder(orderDependencyNodes(nodes), nodes, runtime, (node, rawInput) => replayDerivedParser(node, rawInput, runtime), options);
|
|
456
|
+
}
|
|
457
|
+
/** Async version of {@link resolveDerivedSourceValues}. */
|
|
458
|
+
async function resolveDerivedSourceValuesAsync(nodes, runtime, options) {
|
|
459
|
+
await resolveDerivedSourceValuesInOrderAsync(orderDependencyNodes(nodes), nodes, runtime, options);
|
|
460
|
+
}
|
|
461
|
+
function resolveDerivedSourceValuesInOrder(ordered, allNodes, runtime, replay, options) {
|
|
462
|
+
const providers = collectSourceProviders(allNodes);
|
|
463
|
+
const settled = /* @__PURE__ */ new Set();
|
|
464
|
+
for (const node of ordered) {
|
|
465
|
+
const metadata = node.parser.dependencyMetadata;
|
|
466
|
+
const rawInput = getNodeRawInput(node);
|
|
467
|
+
if (metadata?.derived == null) continue;
|
|
468
|
+
const label = formatDependencyNodeMetavar(node);
|
|
469
|
+
if (runtime.propagateSourceFailure(metadata.derived.dependencyIds, label, metadata.source?.sourceId)) {
|
|
470
|
+
if (metadata.source != null) settled.add(node);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (metadata.source == null || rawInput == null) continue;
|
|
474
|
+
if (hasPendingProvider(node, providers, settled, runtime, options)) continue;
|
|
475
|
+
settleDerivedSource(node, rawInput, runtime, replay);
|
|
476
|
+
settled.add(node);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async function resolveDerivedSourceValuesInOrderAsync(ordered, allNodes, runtime, options) {
|
|
480
|
+
const providers = collectSourceProviders(allNodes);
|
|
481
|
+
const settled = /* @__PURE__ */ new Set();
|
|
482
|
+
for (const node of ordered) {
|
|
483
|
+
const metadata = node.parser.dependencyMetadata;
|
|
484
|
+
const rawInput = getNodeRawInput(node);
|
|
485
|
+
if (metadata?.derived == null) continue;
|
|
486
|
+
const label = formatDependencyNodeMetavar(node);
|
|
487
|
+
if (runtime.propagateSourceFailure(metadata.derived.dependencyIds, label, metadata.source?.sourceId)) {
|
|
488
|
+
if (metadata.source != null) settled.add(node);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (metadata.source == null || rawInput == null) continue;
|
|
492
|
+
if (hasPendingProvider(node, providers, settled, runtime, options)) continue;
|
|
493
|
+
const result = await replayDerivedParserAsync(node, rawInput, runtime);
|
|
494
|
+
publishDerivedSourceResult(metadata.source.sourceId, result, runtime);
|
|
495
|
+
settled.add(node);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function settleDerivedSource(node, rawInput, runtime, replay) {
|
|
499
|
+
const metadata = node.parser.dependencyMetadata;
|
|
500
|
+
publishDerivedSourceResult(metadata.source.sourceId, replay(node, rawInput), runtime);
|
|
501
|
+
}
|
|
502
|
+
function publishDerivedSourceResult(sourceId, result, runtime) {
|
|
503
|
+
if (result == null || result.success && result.deferred === true) return;
|
|
504
|
+
if (!result.success) runtime.markSourceFailed(sourceId);
|
|
505
|
+
else runtime.registerSource(sourceId, result.value);
|
|
506
|
+
}
|
|
507
|
+
function collectSourceProviders(nodes) {
|
|
508
|
+
const providers = /* @__PURE__ */ new Map();
|
|
509
|
+
for (const node of nodes) {
|
|
510
|
+
const sourceId = node.parser.dependencyMetadata?.source?.sourceId;
|
|
511
|
+
if (sourceId == null) continue;
|
|
512
|
+
const existing = providers.get(sourceId);
|
|
513
|
+
if (existing == null) providers.set(sourceId, [node]);
|
|
514
|
+
else existing.push(node);
|
|
515
|
+
}
|
|
516
|
+
return providers;
|
|
517
|
+
}
|
|
518
|
+
function hasPendingProvider(node, providers, settled, runtime, options) {
|
|
519
|
+
const derived = node.parser.dependencyMetadata.derived;
|
|
520
|
+
return derived.dependencyIds.some((sourceId) => (providers.get(sourceId) ?? []).some((provider) => {
|
|
521
|
+
if (provider === node || runtime.hasSource(sourceId) || runtime.isSourceFailed(sourceId)) return false;
|
|
522
|
+
const metadata = provider.parser.dependencyMetadata;
|
|
523
|
+
if (metadata?.derived != null && getNodeRawInput(provider) != null) return !settled.has(provider);
|
|
524
|
+
return options?.effectfulProviders !== "inactive" && metadata?.source?.completeSource != null;
|
|
525
|
+
}));
|
|
526
|
+
}
|
|
527
|
+
function getNodeRawInput(node) {
|
|
528
|
+
return node.rawInput ?? extractRawInputFromState(node.state);
|
|
529
|
+
}
|
|
530
|
+
function registerRuntimeSourceMetadata(nodes, runtime) {
|
|
531
|
+
for (const node of nodes) {
|
|
532
|
+
const metadata = node.parser.dependencyMetadata;
|
|
533
|
+
if (metadata?.source == null) continue;
|
|
534
|
+
runtime.registerSourceMetadata(metadata.source.sourceId, formatDependencyNodeMetavar(node), metadata.derived?.dependencyIds);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function propagateRuntimeSourceFailures(nodes, runtime) {
|
|
538
|
+
for (const node of orderDependencyNodes(nodes)) {
|
|
539
|
+
const metadata = node.parser.dependencyMetadata;
|
|
540
|
+
if (metadata?.derived == null) continue;
|
|
541
|
+
runtime.propagateSourceFailure(metadata.derived.dependencyIds, formatDependencyNodeMetavar(node), metadata.source?.sourceId);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
function includeSourceFailureChain(error, sourceId, runtime) {
|
|
545
|
+
const chain = runtime.getSourceFailureChain(sourceId);
|
|
546
|
+
return chain == null || chain.length < 2 ? error : require_message.message`${error} Dependency chain: ${chain.join(" -> ")}.`;
|
|
547
|
+
}
|
|
548
|
+
function formatDependencyNodeMetavar(node) {
|
|
549
|
+
return node.parser.dependencyMetadata?.derived?.metavar ?? node.parser.dependencyMetadata?.source?.metavar ?? (node.path.map(String).join(".") || "<root>");
|
|
550
|
+
}
|
|
551
|
+
function formatDependencyNodeLabel(node) {
|
|
552
|
+
const metavar = node.parser.dependencyMetadata?.derived?.metavar ?? node.parser.dependencyMetadata?.source?.metavar;
|
|
553
|
+
const path = node.path.map(String).join(".") || "<root>";
|
|
554
|
+
return metavar == null ? path : `${metavar} (${path})`;
|
|
290
555
|
}
|
|
291
556
|
/**
|
|
292
557
|
* Fills missing source defaults for source parsers whose state is
|
|
@@ -339,6 +604,7 @@ function fillMissingSourceDefaults(nodes, runtime) {
|
|
|
339
604
|
error: result
|
|
340
605
|
});
|
|
341
606
|
}
|
|
607
|
+
resolveDerivedSourceValues(nodes, runtime);
|
|
342
608
|
return failures;
|
|
343
609
|
}
|
|
344
610
|
/**
|
|
@@ -383,6 +649,7 @@ async function fillMissingSourceDefaultsAsync(nodes, runtime) {
|
|
|
383
649
|
error: result
|
|
384
650
|
});
|
|
385
651
|
}
|
|
652
|
+
await resolveDerivedSourceValuesAsync(nodes, runtime);
|
|
386
653
|
return failures;
|
|
387
654
|
}
|
|
388
655
|
/**
|
|
@@ -414,6 +681,10 @@ function replayDerivedParser(node, rawInput, runtime) {
|
|
|
414
681
|
});
|
|
415
682
|
if (resolution.kind === "missing") return void 0;
|
|
416
683
|
if (resolution.kind === "partial") return void 0;
|
|
684
|
+
if (resolution.usedDefaults.every((usedDefault) => usedDefault)) {
|
|
685
|
+
const preliminary = extractPreliminaryResultFromState(node.state);
|
|
686
|
+
if (preliminary != null) return preliminary;
|
|
687
|
+
}
|
|
417
688
|
const key = createReplayKey(node.path, rawInput, resolution.values, meta.derived.replayParse);
|
|
418
689
|
const cached = runtime.getReplayResult(key);
|
|
419
690
|
if (cached != null) return cached;
|
|
@@ -449,6 +720,10 @@ async function replayDerivedParserAsync(node, rawInput, runtime) {
|
|
|
449
720
|
});
|
|
450
721
|
if (resolution.kind === "missing") return void 0;
|
|
451
722
|
if (resolution.kind === "partial") return void 0;
|
|
723
|
+
if (resolution.usedDefaults.every((usedDefault) => usedDefault)) {
|
|
724
|
+
const preliminary = extractPreliminaryResultFromState(node.state);
|
|
725
|
+
if (preliminary != null) return preliminary;
|
|
726
|
+
}
|
|
452
727
|
const key = createReplayKey(node.path, rawInput, resolution.values, meta.derived.replayParse);
|
|
453
728
|
const cached = runtime.getReplayResult(key);
|
|
454
729
|
if (cached != null) return cached;
|
|
@@ -471,10 +746,60 @@ async function replayDerivedParserAsync(node, rawInput, runtime) {
|
|
|
471
746
|
* @since 1.0.0
|
|
472
747
|
*/
|
|
473
748
|
function extractRawInputFromState(state) {
|
|
749
|
+
return extractRawInputFromStateInner(state, /* @__PURE__ */ new Set());
|
|
750
|
+
}
|
|
751
|
+
function extractRawInputFromStateInner(state, visited) {
|
|
474
752
|
if (state == null) return void 0;
|
|
475
753
|
if (typeof state !== "object") return void 0;
|
|
754
|
+
if (visited.has(state)) return void 0;
|
|
755
|
+
visited.add(state);
|
|
756
|
+
const recordedRawInput = getRecordedDerivedRawInput(state);
|
|
757
|
+
if (recordedRawInput != null) return recordedRawInput;
|
|
476
758
|
if (require_internal_dependency.isDeferredParseState(state)) return state.rawInput;
|
|
477
|
-
if (Array.isArray(state)
|
|
759
|
+
if (Array.isArray(state)) {
|
|
760
|
+
for (let index = state.length - 1; index >= 0; index--) {
|
|
761
|
+
const rawInput = extractRawInputFromStateInner(state[index], visited);
|
|
762
|
+
if (rawInput != null) return rawInput;
|
|
763
|
+
}
|
|
764
|
+
return void 0;
|
|
765
|
+
}
|
|
766
|
+
const nested = /* @__PURE__ */ new Set();
|
|
767
|
+
for (const value of Object.values(state)) {
|
|
768
|
+
const rawInput = extractRawInputFromStateInner(value, visited);
|
|
769
|
+
if (rawInput != null) nested.add(rawInput);
|
|
770
|
+
}
|
|
771
|
+
return nested.size === 1 ? nested.values().next().value : void 0;
|
|
772
|
+
}
|
|
773
|
+
function extractPreliminaryResultFromState(state) {
|
|
774
|
+
return extractPreliminaryResultFromStateInner(state, /* @__PURE__ */ new Set());
|
|
775
|
+
}
|
|
776
|
+
function extractPreliminaryResultFromStateInner(state, visited) {
|
|
777
|
+
if (state == null || typeof state !== "object") return void 0;
|
|
778
|
+
if (visited.has(state)) return void 0;
|
|
779
|
+
visited.add(state);
|
|
780
|
+
if (require_internal_dependency.isDeferredParseState(state)) return state.preliminaryResult;
|
|
781
|
+
if (getRecordedDerivedRawInput(state) != null && "success" in state && typeof state.success === "boolean") {
|
|
782
|
+
if (state.success === true && "value" in state) return state;
|
|
783
|
+
if (state.success === false && "error" in state) return state;
|
|
784
|
+
}
|
|
785
|
+
if (Array.isArray(state)) {
|
|
786
|
+
for (let index = state.length - 1; index >= 0; index--) {
|
|
787
|
+
const result = extractPreliminaryResultFromStateInner(state[index], visited);
|
|
788
|
+
if (result != null) return result;
|
|
789
|
+
}
|
|
790
|
+
return void 0;
|
|
791
|
+
}
|
|
792
|
+
const nested = /* @__PURE__ */ new Set();
|
|
793
|
+
for (const value of Object.values(state)) {
|
|
794
|
+
const result = extractPreliminaryResultFromStateInner(value, visited);
|
|
795
|
+
if (result != null) nested.add(result);
|
|
796
|
+
}
|
|
797
|
+
return nested.size === 1 ? nested.values().next().value : void 0;
|
|
798
|
+
}
|
|
799
|
+
function getRecordedDerivedRawInput(state) {
|
|
800
|
+
const recorded = derivedRawInputs.get(state);
|
|
801
|
+
if (recorded != null) return recorded;
|
|
802
|
+
if (derivedRawInputKey in state && typeof state[derivedRawInputKey] === "string") return state[derivedRawInputKey];
|
|
478
803
|
return void 0;
|
|
479
804
|
}
|
|
480
805
|
/**
|
|
@@ -658,6 +983,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
658
983
|
completed: []
|
|
659
984
|
};
|
|
660
985
|
if (exec == null || exec.phase !== "complete") return empty;
|
|
986
|
+
registerRuntimeSourceMetadata(nodes, runtime);
|
|
661
987
|
const session = exec.effectfulCompletionSession;
|
|
662
988
|
if (session?.policy === "demand-only") {
|
|
663
989
|
const demandNodes = options?.demandNodes ?? nodes;
|
|
@@ -667,6 +993,12 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
667
993
|
while (demandAdded) {
|
|
668
994
|
demandAdded = false;
|
|
669
995
|
for (const node of nodes) {
|
|
996
|
+
const metadata = node.parser.dependencyMetadata;
|
|
997
|
+
if (metadata?.source != null && metadata.derived != null && session.demanded.has(metadata.source.sourceId)) for (const dependencySourceId of metadata.derived.dependencyIds) {
|
|
998
|
+
if (session.demanded.has(dependencySourceId)) continue;
|
|
999
|
+
session.demanded.add(dependencySourceId);
|
|
1000
|
+
demandAdded = true;
|
|
1001
|
+
}
|
|
670
1002
|
if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
|
|
671
1003
|
if (session.demanded.has(node.requiresSourceId)) continue;
|
|
672
1004
|
for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
|
|
@@ -686,7 +1018,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
686
1018
|
const schedulable = nodes.filter((node) => node.parser.dependencyMetadata?.source?.completeSource != null || node.prepare != null);
|
|
687
1019
|
if (schedulable.length === 0 && options?.includeStructural !== true) return empty;
|
|
688
1020
|
const completed = [];
|
|
689
|
-
for (const node of nodes) {
|
|
1021
|
+
for (const node of orderDependencyNodes(nodes)) {
|
|
690
1022
|
if (node.prepare != null) {
|
|
691
1023
|
const barrierFailure = await node.prepare({
|
|
692
1024
|
runtime,
|
|
@@ -703,6 +1035,28 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
703
1035
|
const source = node.parser.dependencyMetadata?.source;
|
|
704
1036
|
if (source == null) continue;
|
|
705
1037
|
if (source.completeSource == null) {
|
|
1038
|
+
const derived = node.parser.dependencyMetadata?.derived;
|
|
1039
|
+
const rawInput = getNodeRawInput(node);
|
|
1040
|
+
if (derived != null && rawInput != null) {
|
|
1041
|
+
if (runtime.propagateSourceFailure(derived.dependencyIds, formatDependencyNodeMetavar(node), source.sourceId)) continue;
|
|
1042
|
+
const replayed = await replayDerivedParserAsync(node, rawInput, runtime);
|
|
1043
|
+
if (replayed == null) continue;
|
|
1044
|
+
if (!replayed.success) {
|
|
1045
|
+
runtime.markSourceFailed(source.sourceId);
|
|
1046
|
+
propagateRuntimeSourceFailures(nodes, runtime);
|
|
1047
|
+
return {
|
|
1048
|
+
success: false,
|
|
1049
|
+
error: includeSourceFailureChain(replayed.error, source.sourceId, runtime)
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
if (replayed.deferred === true) continue;
|
|
1053
|
+
runtime.registerSource(source.sourceId, replayed.value);
|
|
1054
|
+
if (source.preservesSourceValue && (options?.isReusable?.(node) ?? true)) completed.push({
|
|
1055
|
+
key: node.path[node.path.length - 1],
|
|
1056
|
+
result: replayed
|
|
1057
|
+
});
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
706
1060
|
const collected = options?.isCollected?.(node) ?? options?.isReusable?.(node) ?? true;
|
|
707
1061
|
if (source.extractSourceValue == null || collected === false) continue;
|
|
708
1062
|
const extracted = await source.extractSourceValue(node.state);
|
|
@@ -728,9 +1082,10 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
728
1082
|
if (result == null) continue;
|
|
729
1083
|
if (!result.success) {
|
|
730
1084
|
runtime.markSourceFailed(source.sourceId);
|
|
1085
|
+
propagateRuntimeSourceFailures(nodes, runtime);
|
|
731
1086
|
return {
|
|
732
1087
|
success: false,
|
|
733
|
-
error: result.error
|
|
1088
|
+
error: includeSourceFailureChain(result.error, source.sourceId, runtime)
|
|
734
1089
|
};
|
|
735
1090
|
}
|
|
736
1091
|
if (result.deferred === true) continue;
|
|
@@ -960,11 +1315,15 @@ function buildRuntimeNodesFromPairs(pairs, state, parentPath) {
|
|
|
960
1315
|
const nodes = [];
|
|
961
1316
|
for (const [field, parser] of pairs) {
|
|
962
1317
|
const fieldState = Object.hasOwn(state, field) ? state[field] : void 0;
|
|
1318
|
+
const rawInput = extractRawInputFromState(fieldState);
|
|
1319
|
+
const defaultDependencyValues = getDefaultDependencySnapshot(fieldState);
|
|
963
1320
|
nodes.push({
|
|
964
1321
|
path: [...prefix, field],
|
|
965
1322
|
parser,
|
|
966
1323
|
state: fieldState,
|
|
967
|
-
matched: isMatchedState(fieldState, parser)
|
|
1324
|
+
matched: isMatchedState(fieldState, parser),
|
|
1325
|
+
...rawInput != null ? { rawInput } : {},
|
|
1326
|
+
...defaultDependencyValues != null ? { defaultDependencyValues } : {}
|
|
968
1327
|
});
|
|
969
1328
|
}
|
|
970
1329
|
return nodes;
|
|
@@ -987,15 +1346,42 @@ function buildRuntimeNodesFromArray(parsers, stateArray, parentPath) {
|
|
|
987
1346
|
for (let i = 0; i < parsers.length; i++) {
|
|
988
1347
|
const parser = parsers[i];
|
|
989
1348
|
const elemState = i < stateArray.length ? stateArray[i] : void 0;
|
|
1349
|
+
const rawInput = extractRawInputFromState(elemState);
|
|
1350
|
+
const defaultDependencyValues = getDefaultDependencySnapshot(elemState);
|
|
990
1351
|
nodes.push({
|
|
991
1352
|
path: [...prefix, i],
|
|
992
1353
|
parser,
|
|
993
1354
|
state: elemState,
|
|
994
|
-
matched: isMatchedState(elemState, parser)
|
|
1355
|
+
matched: isMatchedState(elemState, parser),
|
|
1356
|
+
...rawInput != null ? { rawInput } : {},
|
|
1357
|
+
...defaultDependencyValues != null ? { defaultDependencyValues } : {}
|
|
995
1358
|
});
|
|
996
1359
|
}
|
|
997
1360
|
return nodes;
|
|
998
1361
|
}
|
|
1362
|
+
function getDefaultDependencySnapshot(state) {
|
|
1363
|
+
return getDefaultDependencySnapshotInner(state, /* @__PURE__ */ new Set());
|
|
1364
|
+
}
|
|
1365
|
+
function getDefaultDependencySnapshotInner(state, visited) {
|
|
1366
|
+
if (state == null || typeof state !== "object") return void 0;
|
|
1367
|
+
if (visited.has(state)) return void 0;
|
|
1368
|
+
visited.add(state);
|
|
1369
|
+
const direct = require_internal_dependency.getSnapshottedDefaultDependencyValues(state);
|
|
1370
|
+
if (direct != null) return direct;
|
|
1371
|
+
if (Array.isArray(state)) {
|
|
1372
|
+
for (let index = state.length - 1; index >= 0; index--) {
|
|
1373
|
+
const snapshot = getDefaultDependencySnapshotInner(state[index], visited);
|
|
1374
|
+
if (snapshot != null) return snapshot;
|
|
1375
|
+
}
|
|
1376
|
+
return void 0;
|
|
1377
|
+
}
|
|
1378
|
+
const nested = [];
|
|
1379
|
+
for (const value of Object.values(state)) {
|
|
1380
|
+
const snapshot = getDefaultDependencySnapshotInner(value, visited);
|
|
1381
|
+
if (snapshot != null) nested.push(snapshot);
|
|
1382
|
+
}
|
|
1383
|
+
return nested.length === 1 ? nested[0] : void 0;
|
|
1384
|
+
}
|
|
999
1385
|
|
|
1000
1386
|
//#endregion
|
|
1001
1387
|
exports.buildRuntimeNodesFromArray = buildRuntimeNodesFromArray;
|
|
@@ -1009,12 +1395,17 @@ exports.createDependencyFingerprint = createDependencyFingerprint;
|
|
|
1009
1395
|
exports.createDependencyRuntimeContext = createDependencyRuntimeContext;
|
|
1010
1396
|
exports.createReplayKey = createReplayKey;
|
|
1011
1397
|
exports.defineForwardedEffectfulSchedulingNodes = defineForwardedEffectfulSchedulingNodes;
|
|
1398
|
+
exports.derivedRawInputKey = derivedRawInputKey;
|
|
1012
1399
|
exports.effectfulSchedulingNodesKey = effectfulSchedulingNodesKey;
|
|
1013
1400
|
exports.extractRawInputFromState = extractRawInputFromState;
|
|
1014
1401
|
exports.fillMissingSourceDefaults = fillMissingSourceDefaults;
|
|
1015
1402
|
exports.fillMissingSourceDefaultsAsync = fillMissingSourceDefaultsAsync;
|
|
1403
|
+
exports.orderDependencyNodes = orderDependencyNodes;
|
|
1404
|
+
exports.recordDerivedRawInput = recordDerivedRawInput;
|
|
1016
1405
|
exports.replayDerivedParser = replayDerivedParser;
|
|
1017
1406
|
exports.replayDerivedParserAsync = replayDerivedParserAsync;
|
|
1407
|
+
exports.resolveDerivedSourceValues = resolveDerivedSourceValues;
|
|
1408
|
+
exports.resolveDerivedSourceValuesAsync = resolveDerivedSourceValuesAsync;
|
|
1018
1409
|
exports.resolveStateWithRuntime = resolveStateWithRuntime;
|
|
1019
1410
|
exports.resolveStateWithRuntimeAsync = resolveStateWithRuntimeAsync;
|
|
1020
1411
|
exports.serializeSchedulingPath = serializeSchedulingPath;
|
|
@@ -7,6 +7,29 @@ import { ExecutionContext } from "./internal/parser.cjs";
|
|
|
7
7
|
|
|
8
8
|
//#region src/dependency-runtime.d.ts
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Stores the raw token parsed by a derived value parser on structural parser
|
|
12
|
+
* states that can safely carry an in-band annotation.
|
|
13
|
+
*
|
|
14
|
+
* The execution trace remains the canonical diagnostic record. This state
|
|
15
|
+
* marker lets construct-independent dependency resolution replay a derived
|
|
16
|
+
* source before downstream fields complete.
|
|
17
|
+
* @internal
|
|
18
|
+
* @since 1.3.0
|
|
19
|
+
*/
|
|
20
|
+
declare const derivedRawInputKey: unique symbol;
|
|
21
|
+
/**
|
|
22
|
+
* Records a derived parser's raw token without modifying its parse result.
|
|
23
|
+
*
|
|
24
|
+
* Parse results may be frozen or carry class private state, so primitives keep
|
|
25
|
+
* their original identity and associate replay metadata out of band.
|
|
26
|
+
*
|
|
27
|
+
* @param state The original value parser result.
|
|
28
|
+
* @param rawInput The token parsed into that result.
|
|
29
|
+
* @internal
|
|
30
|
+
* @since 1.3.0
|
|
31
|
+
*/
|
|
32
|
+
declare function recordDerivedRawInput(state: object, rawInput: string): void;
|
|
10
33
|
/**
|
|
11
34
|
* A request to resolve one or more dependency values.
|
|
12
35
|
*
|
|
@@ -89,6 +112,8 @@ interface RuntimeNode {
|
|
|
89
112
|
};
|
|
90
113
|
/** The parser's current state. */
|
|
91
114
|
readonly state: unknown;
|
|
115
|
+
/** Raw input captured for a derived parser, when this node matched. */
|
|
116
|
+
readonly rawInput?: string;
|
|
92
117
|
/**
|
|
93
118
|
* Whether the parser consumed explicit input during parsing.
|
|
94
119
|
* When `true`, the parser's state reflects user-provided input (which
|
|
@@ -142,6 +167,20 @@ interface RuntimeNode {
|
|
|
142
167
|
*/
|
|
143
168
|
readonly requiresSourceId?: symbol;
|
|
144
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Options for resolving matched derived source values.
|
|
172
|
+
*
|
|
173
|
+
* @internal
|
|
174
|
+
* @since 1.3.0
|
|
175
|
+
*/
|
|
176
|
+
interface ResolveDerivedSourceValuesOptions {
|
|
177
|
+
/**
|
|
178
|
+
* Whether an unpopulated effectful source can still provide a value later.
|
|
179
|
+
* Suggestion generation sets this to `"inactive"` because it never runs
|
|
180
|
+
* effects and must let downstream parsers use declared dependency defaults.
|
|
181
|
+
*/
|
|
182
|
+
readonly effectfulProviders?: "pending" | "inactive";
|
|
183
|
+
}
|
|
145
184
|
/**
|
|
146
185
|
* The context handed to a {@link RuntimeNode.prepare} barrier.
|
|
147
186
|
*
|
|
@@ -190,6 +229,15 @@ interface DependencyRuntimeContext {
|
|
|
190
229
|
* defaults for failed sources.
|
|
191
230
|
*/
|
|
192
231
|
markSourceFailed(sourceId: symbol): void;
|
|
232
|
+
/** Register a source's diagnostic label and upstream dependencies. */
|
|
233
|
+
registerSourceMetadata(sourceId: symbol, label: string, dependencyIds?: readonly symbol[]): void;
|
|
234
|
+
/**
|
|
235
|
+
* Propagate a failed upstream source through one derived dependency edge.
|
|
236
|
+
* Returns whether any upstream source had failed.
|
|
237
|
+
*/
|
|
238
|
+
propagateSourceFailure(dependencyIds: readonly symbol[], label: string, sourceId?: symbol): boolean;
|
|
239
|
+
/** Return the most informative dependency chain for a failed source. */
|
|
240
|
+
getSourceFailureChain(sourceId: symbol): readonly string[] | undefined;
|
|
193
241
|
/**
|
|
194
242
|
* Check if a source was explicitly attempted but failed validation.
|
|
195
243
|
*/
|
|
@@ -242,6 +290,25 @@ declare function collectExplicitSourceValues(nodes: readonly RuntimeNode[], runt
|
|
|
242
290
|
* @since 1.0.0
|
|
243
291
|
*/
|
|
244
292
|
declare function collectExplicitSourceValuesAsync(nodes: readonly RuntimeNode[], runtime: DependencyRuntimeContext): Promise<void>;
|
|
293
|
+
/**
|
|
294
|
+
* Orders runtime nodes so every in-scope provider precedes a derived source
|
|
295
|
+
* that consumes it. Independent nodes retain declaration order.
|
|
296
|
+
*
|
|
297
|
+
* Missing providers create no edge because the consumer may use its declared
|
|
298
|
+
* default. Scheduling barriers act as providers for the source IDs their
|
|
299
|
+
* selected subtree may expose and depend on their discriminator source.
|
|
300
|
+
*
|
|
301
|
+
* @param nodes Runtime nodes in declaration order.
|
|
302
|
+
* @returns The same nodes in stable dependency order.
|
|
303
|
+
* @throws {TypeError} If active provider edges contain a cycle.
|
|
304
|
+
* @internal
|
|
305
|
+
* @since 1.3.0
|
|
306
|
+
*/
|
|
307
|
+
declare function orderDependencyNodes(nodes: readonly RuntimeNode[]): readonly RuntimeNode[];
|
|
308
|
+
/** Resolves and publishes matched derived sources in stable dependency order. */
|
|
309
|
+
declare function resolveDerivedSourceValues(nodes: readonly RuntimeNode[], runtime: DependencyRuntimeContext, options?: ResolveDerivedSourceValuesOptions): void;
|
|
310
|
+
/** Async version of {@link resolveDerivedSourceValues}. */
|
|
311
|
+
declare function resolveDerivedSourceValuesAsync(nodes: readonly RuntimeNode[], runtime: DependencyRuntimeContext, options?: ResolveDerivedSourceValuesOptions): Promise<void>;
|
|
245
312
|
/**
|
|
246
313
|
* Fills missing source defaults for source parsers whose state is
|
|
247
314
|
* unpopulated.
|
|
@@ -616,4 +683,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
616
683
|
readonly initialState?: unknown;
|
|
617
684
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
618
685
|
//#endregion
|
|
619
|
-
export { CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ReplayKey, RuntimeNode, SchedulingBarrierContext, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
686
|
+
export { CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ReplayKey, ResolveDerivedSourceValuesOptions, RuntimeNode, SchedulingBarrierContext, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|