@optique/core 1.3.0-dev.2385 → 1.3.0-dev.2388
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 +277 -59
- package/dist/constructs.js +278 -60
- package/dist/dependency-runtime.cjs +86 -11
- package/dist/dependency-runtime.d.cts +128 -7
- package/dist/dependency-runtime.d.ts +128 -7
- package/dist/dependency-runtime.js +83 -11
- package/dist/facade.cjs +2 -1
- package/dist/facade.js +2 -1
- package/dist/internal/parser.cjs +2 -1
- package/dist/internal/parser.d.cts +10 -0
- package/dist/internal/parser.d.ts +10 -0
- package/dist/internal/parser.js +2 -1
- package/dist/primitives.cjs +10 -0
- package/dist/primitives.js +11 -1
- package/package.json +2 -2
|
@@ -494,6 +494,41 @@ function extractRawInputFromState(state) {
|
|
|
494
494
|
*/
|
|
495
495
|
const effectfulSchedulingNodesKey = Symbol("@optique/core/dependency-runtime/effectfulSchedulingNodes");
|
|
496
496
|
/**
|
|
497
|
+
* Opt-in marker for parsers whose {@link effectfulSchedulingNodesKey}
|
|
498
|
+
* hook also defines their explicit-source *collection* scope.
|
|
499
|
+
*
|
|
500
|
+
* A parent construct normally collects explicit source values from its
|
|
501
|
+
* direct children only. A parser carrying this marker (with value
|
|
502
|
+
* `true`) asks the parent to expand it through its scheduling hook
|
|
503
|
+
* before collecting, so command-line source values inside it—such as a
|
|
504
|
+
* `conditional()` discriminator, a committed conditional branch, or a
|
|
505
|
+
* selected `command()` subtree—register into the parent's dependency
|
|
506
|
+
* runtime exactly like a prompt-completed value would. Constructs
|
|
507
|
+
* without the marker (plain nested `object()`, uncommitted exclusive
|
|
508
|
+
* branches) keep their existing scope.
|
|
509
|
+
*
|
|
510
|
+
* @internal
|
|
511
|
+
* @since 1.3.0
|
|
512
|
+
*/
|
|
513
|
+
const sourceCollectionExpansionKey = Symbol("@optique/core/dependency-runtime/sourceCollectionExpansion");
|
|
514
|
+
/**
|
|
515
|
+
* Static child parsers reachable for dependency-source estimation.
|
|
516
|
+
*
|
|
517
|
+
* `collectStaticSourceIds()` walks flattened field pairs, which stops at
|
|
518
|
+
* parsers whose children are not field-shaped—a `command()`'s inner
|
|
519
|
+
* parser, a nested `conditional()`'s branches, exclusive alternatives,
|
|
520
|
+
* or a transparent wrapper's inner construct. Such parsers expose their
|
|
521
|
+
* children here so the walk can estimate every source a subtree may
|
|
522
|
+
* provide. The estimate feeds demand-only control dependencies, where
|
|
523
|
+
* an overcount merely completes a discriminator earlier than strictly
|
|
524
|
+
* needed, while an undercount delays an effectful completion to the
|
|
525
|
+
* final pass and starves phase-two contexts of seed values.
|
|
526
|
+
*
|
|
527
|
+
* @internal
|
|
528
|
+
* @since 1.3.0
|
|
529
|
+
*/
|
|
530
|
+
const staticSourceScopeKey = Symbol("@optique/core/dependency-runtime/staticSourceScope");
|
|
531
|
+
/**
|
|
497
532
|
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
498
533
|
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
499
534
|
* wrapped parser—a selected exclusive or command branch, or an ordinary
|
|
@@ -511,6 +546,11 @@ const effectfulSchedulingNodesKey = Symbol("@optique/core/dependency-runtime/eff
|
|
|
511
546
|
* @since 1.3.0
|
|
512
547
|
*/
|
|
513
548
|
function defineForwardedEffectfulSchedulingNodes(wrapper, inner, adaptState) {
|
|
549
|
+
Object.defineProperty(wrapper, staticSourceScopeKey, {
|
|
550
|
+
value: [inner],
|
|
551
|
+
configurable: true,
|
|
552
|
+
enumerable: false
|
|
553
|
+
});
|
|
514
554
|
if (inner.dependencyMetadata?.source != null) return;
|
|
515
555
|
Object.defineProperty(wrapper, effectfulSchedulingNodesKey, {
|
|
516
556
|
value: ((state, parentPath) => [{
|
|
@@ -521,6 +561,11 @@ function defineForwardedEffectfulSchedulingNodes(wrapper, inner, adaptState) {
|
|
|
521
561
|
configurable: true,
|
|
522
562
|
enumerable: false
|
|
523
563
|
});
|
|
564
|
+
if (inner[sourceCollectionExpansionKey] === true) Object.defineProperty(wrapper, sourceCollectionExpansionKey, {
|
|
565
|
+
value: true,
|
|
566
|
+
configurable: true,
|
|
567
|
+
enumerable: false
|
|
568
|
+
});
|
|
524
569
|
}
|
|
525
570
|
/**
|
|
526
571
|
* Collects the dependency source IDs demanded by consumers among the
|
|
@@ -570,12 +615,12 @@ function collectDeferredDemand(state, demanded, visited) {
|
|
|
570
615
|
*
|
|
571
616
|
* - Runs only during real completion (`exec.phase === "complete"`); probe
|
|
572
617
|
* and suggest phases return immediately without effects.
|
|
573
|
-
* - Precedence is structural
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
* When several scheduled occurrences
|
|
577
|
-
*
|
|
578
|
-
*
|
|
618
|
+
* - Precedence is structural per occurrence: an effectful completion
|
|
619
|
+
* returns its own field's command-line or bound value without running
|
|
620
|
+
* the effect, and structural occurrences re-register their extracted
|
|
621
|
+
* values in declaration order. When several scheduled occurrences
|
|
622
|
+
* share one source, the last occurrence wins—the same rule as repeated
|
|
623
|
+
* command-line source occurrences.
|
|
579
624
|
* - Completion results that are `undefined` or marked `deferred` are
|
|
580
625
|
* treated as declined and neither registered nor cached.
|
|
581
626
|
* - A successful result registers its value unless the value is
|
|
@@ -618,6 +663,14 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
618
663
|
const demandNodes = options?.demandNodes ?? nodes;
|
|
619
664
|
const demanded = collectDemandedDependencyIds(demandNodes, state, exec.trace);
|
|
620
665
|
for (const id of demanded) session.demanded.add(id);
|
|
666
|
+
for (const node of nodes) {
|
|
667
|
+
if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
|
|
668
|
+
if (session.demanded.has(node.requiresSourceId)) continue;
|
|
669
|
+
for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
|
|
670
|
+
session.demanded.add(node.requiresSourceId);
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
621
674
|
}
|
|
622
675
|
if (session != null) {
|
|
623
676
|
for (const result of session.results.values()) if (!result.success) return {
|
|
@@ -625,14 +678,28 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
625
678
|
error: result.error
|
|
626
679
|
};
|
|
627
680
|
}
|
|
628
|
-
const schedulable = nodes.filter((node) => node.parser.dependencyMetadata?.source?.completeSource != null);
|
|
629
|
-
if (schedulable.length === 0) return empty;
|
|
681
|
+
const schedulable = nodes.filter((node) => node.parser.dependencyMetadata?.source?.completeSource != null || node.prepare != null);
|
|
682
|
+
if (schedulable.length === 0 && options?.includeStructural !== true) return empty;
|
|
630
683
|
const completed = [];
|
|
631
684
|
for (const node of nodes) {
|
|
685
|
+
if (node.prepare != null) {
|
|
686
|
+
const barrierFailure = await node.prepare({
|
|
687
|
+
runtime,
|
|
688
|
+
exec,
|
|
689
|
+
schedule: (barrierNodes) => completeEffectfulSourcesAsync(barrierNodes, state, runtime, exec, {
|
|
690
|
+
isReusable: () => false,
|
|
691
|
+
isCollected: () => true,
|
|
692
|
+
includeStructural: true
|
|
693
|
+
}).then((result$1) => result$1.success ? void 0 : result$1)
|
|
694
|
+
});
|
|
695
|
+
if (barrierFailure != null) return barrierFailure;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
632
698
|
const source = node.parser.dependencyMetadata?.source;
|
|
633
699
|
if (source == null) continue;
|
|
634
700
|
if (source.completeSource == null) {
|
|
635
|
-
|
|
701
|
+
const collected = options?.isCollected?.(node) ?? options?.isReusable?.(node) ?? true;
|
|
702
|
+
if (source.extractSourceValue == null || collected === false) continue;
|
|
636
703
|
const extracted = await source.extractSourceValue(node.state);
|
|
637
704
|
if (extracted?.success === true && extracted.value !== void 0) runtime.registerSource(source.sourceId, extracted.value);
|
|
638
705
|
continue;
|
|
@@ -676,7 +743,12 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
676
743
|
}
|
|
677
744
|
/**
|
|
678
745
|
* Serializes a scheduling node path into a stable string key, using
|
|
679
|
-
* length-prefixed segments so no separator escaping is needed.
|
|
746
|
+
* length-prefixed segments so no separator escaping is needed. Shared
|
|
747
|
+
* with constructs that key run-scoped session entries by path (e.g.,
|
|
748
|
+
* `conditional()`'s prepared branch selections).
|
|
749
|
+
*
|
|
750
|
+
* @internal
|
|
751
|
+
* @since 1.3.0
|
|
680
752
|
*/
|
|
681
753
|
function serializeSchedulingPath(path) {
|
|
682
754
|
return path.map(serializePathSegment).join("");
|
|
@@ -939,4 +1011,7 @@ exports.fillMissingSourceDefaultsAsync = fillMissingSourceDefaultsAsync;
|
|
|
939
1011
|
exports.replayDerivedParser = replayDerivedParser;
|
|
940
1012
|
exports.replayDerivedParserAsync = replayDerivedParserAsync;
|
|
941
1013
|
exports.resolveStateWithRuntime = resolveStateWithRuntime;
|
|
942
|
-
exports.resolveStateWithRuntimeAsync = resolveStateWithRuntimeAsync;
|
|
1014
|
+
exports.resolveStateWithRuntimeAsync = resolveStateWithRuntimeAsync;
|
|
1015
|
+
exports.serializeSchedulingPath = serializeSchedulingPath;
|
|
1016
|
+
exports.sourceCollectionExpansionKey = sourceCollectionExpansionKey;
|
|
1017
|
+
exports.staticSourceScopeKey = staticSourceScopeKey;
|
|
@@ -105,6 +105,63 @@ interface RuntimeNode {
|
|
|
105
105
|
* @since 1.0.0
|
|
106
106
|
*/
|
|
107
107
|
readonly defaultDependencyValues?: readonly unknown[];
|
|
108
|
+
/**
|
|
109
|
+
* A scheduling barrier: a preparation step executed serially at this
|
|
110
|
+
* node's declaration position during the effectful completion pass.
|
|
111
|
+
*
|
|
112
|
+
* A `conditional()` whose branch selection is still unknown installs
|
|
113
|
+
* one after its discriminator node: the preparation resolves the
|
|
114
|
+
* branch from the discriminator's completion, caches the decision in
|
|
115
|
+
* the run-scoped session so final completion reuses it, and schedules
|
|
116
|
+
* the chosen branch's nodes through `ctx.schedule` within the same
|
|
117
|
+
* pass. A failure aborts the pass like a failed effectful
|
|
118
|
+
* completion; `undefined` declines without effect.
|
|
119
|
+
*
|
|
120
|
+
* @since 1.3.0
|
|
121
|
+
*/
|
|
122
|
+
readonly prepare?: (ctx: SchedulingBarrierContext) => Promise<{
|
|
123
|
+
readonly success: false;
|
|
124
|
+
readonly error: Message;
|
|
125
|
+
} | undefined>;
|
|
126
|
+
/**
|
|
127
|
+
* Source IDs that the subtree guarded by this barrier can provide.
|
|
128
|
+
* Used by the demand-only pass: when any of them is demanded, the
|
|
129
|
+
* barrier's {@link RuntimeNode.requiresSourceId} becomes demanded as
|
|
130
|
+
* a control dependency, so the guarding discriminator completes in
|
|
131
|
+
* the seed pass even though no consumer demands it directly.
|
|
132
|
+
*
|
|
133
|
+
* @since 1.3.0
|
|
134
|
+
*/
|
|
135
|
+
readonly providesSourceIds?: ReadonlySet<symbol>;
|
|
136
|
+
/**
|
|
137
|
+
* The source ID whose completion this barrier's preparation depends
|
|
138
|
+
* on (a `conditional()` discriminator). See
|
|
139
|
+
* {@link RuntimeNode.providesSourceIds}.
|
|
140
|
+
*
|
|
141
|
+
* @since 1.3.0
|
|
142
|
+
*/
|
|
143
|
+
readonly requiresSourceId?: symbol;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The context handed to a {@link RuntimeNode.prepare} barrier.
|
|
147
|
+
*
|
|
148
|
+
* @internal
|
|
149
|
+
* @since 1.3.0
|
|
150
|
+
*/
|
|
151
|
+
interface SchedulingBarrierContext {
|
|
152
|
+
/** The runtime the current pass registers source values into. */
|
|
153
|
+
readonly runtime: DependencyRuntimeContext;
|
|
154
|
+
/** The execution context of the current pass. */
|
|
155
|
+
readonly exec: ExecutionContext | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* Schedules further (already expanded) nodes within the current pass,
|
|
158
|
+
* at the barrier's position. Results are deduplicated through the
|
|
159
|
+
* run-scoped session and are not cached by the owning construct.
|
|
160
|
+
*/
|
|
161
|
+
readonly schedule: (nodes: readonly RuntimeNode[]) => Promise<{
|
|
162
|
+
readonly success: false;
|
|
163
|
+
readonly error: Message;
|
|
164
|
+
} | undefined>;
|
|
108
165
|
}
|
|
109
166
|
/**
|
|
110
167
|
* Dependency runtime context for centralized dependency resolution.
|
|
@@ -281,6 +338,41 @@ declare const effectfulSchedulingNodesKey: unique symbol;
|
|
|
281
338
|
* @since 1.3.0
|
|
282
339
|
*/
|
|
283
340
|
type EffectfulSchedulingNodesFn = (state: unknown, parentPath: readonly PropertyKey[] | undefined) => readonly RuntimeNode[];
|
|
341
|
+
/**
|
|
342
|
+
* Opt-in marker for parsers whose {@link effectfulSchedulingNodesKey}
|
|
343
|
+
* hook also defines their explicit-source *collection* scope.
|
|
344
|
+
*
|
|
345
|
+
* A parent construct normally collects explicit source values from its
|
|
346
|
+
* direct children only. A parser carrying this marker (with value
|
|
347
|
+
* `true`) asks the parent to expand it through its scheduling hook
|
|
348
|
+
* before collecting, so command-line source values inside it—such as a
|
|
349
|
+
* `conditional()` discriminator, a committed conditional branch, or a
|
|
350
|
+
* selected `command()` subtree—register into the parent's dependency
|
|
351
|
+
* runtime exactly like a prompt-completed value would. Constructs
|
|
352
|
+
* without the marker (plain nested `object()`, uncommitted exclusive
|
|
353
|
+
* branches) keep their existing scope.
|
|
354
|
+
*
|
|
355
|
+
* @internal
|
|
356
|
+
* @since 1.3.0
|
|
357
|
+
*/
|
|
358
|
+
declare const sourceCollectionExpansionKey: unique symbol;
|
|
359
|
+
/**
|
|
360
|
+
* Static child parsers reachable for dependency-source estimation.
|
|
361
|
+
*
|
|
362
|
+
* `collectStaticSourceIds()` walks flattened field pairs, which stops at
|
|
363
|
+
* parsers whose children are not field-shaped—a `command()`'s inner
|
|
364
|
+
* parser, a nested `conditional()`'s branches, exclusive alternatives,
|
|
365
|
+
* or a transparent wrapper's inner construct. Such parsers expose their
|
|
366
|
+
* children here so the walk can estimate every source a subtree may
|
|
367
|
+
* provide. The estimate feeds demand-only control dependencies, where
|
|
368
|
+
* an overcount merely completes a discriminator earlier than strictly
|
|
369
|
+
* needed, while an undercount delays an effectful completion to the
|
|
370
|
+
* final pass and starves phase-two contexts of seed values.
|
|
371
|
+
*
|
|
372
|
+
* @internal
|
|
373
|
+
* @since 1.3.0
|
|
374
|
+
*/
|
|
375
|
+
declare const staticSourceScopeKey: unique symbol;
|
|
284
376
|
/**
|
|
285
377
|
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
286
378
|
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
@@ -369,6 +461,25 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
369
461
|
* running twice, and are skipped when no session is available.
|
|
370
462
|
*/
|
|
371
463
|
readonly isReusable?: (node: RuntimeNode) => boolean;
|
|
464
|
+
/**
|
|
465
|
+
* Whether a node participated in the owning construct's explicit
|
|
466
|
+
* source collection, and therefore must have its structural value
|
|
467
|
+
* re-registered at its declaration position so registration order
|
|
468
|
+
* follows declaration order across structural and effectful
|
|
469
|
+
* occurrences. This is wider than {@link isReusable}: nodes expanded
|
|
470
|
+
* from an opted-in child (see `sourceCollectionExpansionKey`) are
|
|
471
|
+
* collected but not reusable. Defaults to {@link isReusable}.
|
|
472
|
+
*/
|
|
473
|
+
readonly isCollected?: (node: RuntimeNode) => boolean;
|
|
474
|
+
/**
|
|
475
|
+
* Processes structural source nodes even when no node carries an
|
|
476
|
+
* effectful completion or a barrier. A barrier's nested scheduling
|
|
477
|
+
* call sets this: the branch it schedules may hold only structural
|
|
478
|
+
* (command-line) values, which still must register into the pass's
|
|
479
|
+
* runtime, while ordinary construct passes keep the cheap early
|
|
480
|
+
* return.
|
|
481
|
+
*/
|
|
482
|
+
readonly includeStructural?: boolean;
|
|
372
483
|
}
|
|
373
484
|
/**
|
|
374
485
|
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
@@ -378,12 +489,12 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
378
489
|
*
|
|
379
490
|
* - Runs only during real completion (`exec.phase === "complete"`); probe
|
|
380
491
|
* and suggest phases return immediately without effects.
|
|
381
|
-
* - Precedence is structural
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
* When several scheduled occurrences
|
|
385
|
-
*
|
|
386
|
-
*
|
|
492
|
+
* - Precedence is structural per occurrence: an effectful completion
|
|
493
|
+
* returns its own field's command-line or bound value without running
|
|
494
|
+
* the effect, and structural occurrences re-register their extracted
|
|
495
|
+
* values in declaration order. When several scheduled occurrences
|
|
496
|
+
* share one source, the last occurrence wins—the same rule as repeated
|
|
497
|
+
* command-line source occurrences.
|
|
387
498
|
* - Completion results that are `undefined` or marked `deferred` are
|
|
388
499
|
* treated as declined and neither registered nor cached.
|
|
389
500
|
* - A successful result registers its value unless the value is
|
|
@@ -416,6 +527,16 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
416
527
|
* @since 1.3.0
|
|
417
528
|
*/
|
|
418
529
|
declare function completeEffectfulSourcesAsync(nodes: readonly RuntimeNode[], state: unknown, runtime: DependencyRuntimeContext, exec: ExecutionContext | undefined, options?: CompleteEffectfulSourcesOptions): Promise<EffectfulSourceCompletionResult>;
|
|
530
|
+
/**
|
|
531
|
+
* Serializes a scheduling node path into a stable string key, using
|
|
532
|
+
* length-prefixed segments so no separator escaping is needed. Shared
|
|
533
|
+
* with constructs that key run-scoped session entries by path (e.g.,
|
|
534
|
+
* `conditional()`'s prepared branch selections).
|
|
535
|
+
*
|
|
536
|
+
* @internal
|
|
537
|
+
* @since 1.3.0
|
|
538
|
+
*/
|
|
539
|
+
declare function serializeSchedulingPath(path: readonly PropertyKey[]): string;
|
|
419
540
|
/**
|
|
420
541
|
* Recursively collects dependency source values from {@link DependencySourceState}
|
|
421
542
|
* objects found in the state tree and registers them in the runtime.
|
|
@@ -495,4 +616,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
495
616
|
readonly initialState?: unknown;
|
|
496
617
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
497
618
|
//#endregion
|
|
498
|
-
export { CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ReplayKey, RuntimeNode, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync };
|
|
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 };
|
|
@@ -105,6 +105,63 @@ interface RuntimeNode {
|
|
|
105
105
|
* @since 1.0.0
|
|
106
106
|
*/
|
|
107
107
|
readonly defaultDependencyValues?: readonly unknown[];
|
|
108
|
+
/**
|
|
109
|
+
* A scheduling barrier: a preparation step executed serially at this
|
|
110
|
+
* node's declaration position during the effectful completion pass.
|
|
111
|
+
*
|
|
112
|
+
* A `conditional()` whose branch selection is still unknown installs
|
|
113
|
+
* one after its discriminator node: the preparation resolves the
|
|
114
|
+
* branch from the discriminator's completion, caches the decision in
|
|
115
|
+
* the run-scoped session so final completion reuses it, and schedules
|
|
116
|
+
* the chosen branch's nodes through `ctx.schedule` within the same
|
|
117
|
+
* pass. A failure aborts the pass like a failed effectful
|
|
118
|
+
* completion; `undefined` declines without effect.
|
|
119
|
+
*
|
|
120
|
+
* @since 1.3.0
|
|
121
|
+
*/
|
|
122
|
+
readonly prepare?: (ctx: SchedulingBarrierContext) => Promise<{
|
|
123
|
+
readonly success: false;
|
|
124
|
+
readonly error: Message;
|
|
125
|
+
} | undefined>;
|
|
126
|
+
/**
|
|
127
|
+
* Source IDs that the subtree guarded by this barrier can provide.
|
|
128
|
+
* Used by the demand-only pass: when any of them is demanded, the
|
|
129
|
+
* barrier's {@link RuntimeNode.requiresSourceId} becomes demanded as
|
|
130
|
+
* a control dependency, so the guarding discriminator completes in
|
|
131
|
+
* the seed pass even though no consumer demands it directly.
|
|
132
|
+
*
|
|
133
|
+
* @since 1.3.0
|
|
134
|
+
*/
|
|
135
|
+
readonly providesSourceIds?: ReadonlySet<symbol>;
|
|
136
|
+
/**
|
|
137
|
+
* The source ID whose completion this barrier's preparation depends
|
|
138
|
+
* on (a `conditional()` discriminator). See
|
|
139
|
+
* {@link RuntimeNode.providesSourceIds}.
|
|
140
|
+
*
|
|
141
|
+
* @since 1.3.0
|
|
142
|
+
*/
|
|
143
|
+
readonly requiresSourceId?: symbol;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The context handed to a {@link RuntimeNode.prepare} barrier.
|
|
147
|
+
*
|
|
148
|
+
* @internal
|
|
149
|
+
* @since 1.3.0
|
|
150
|
+
*/
|
|
151
|
+
interface SchedulingBarrierContext {
|
|
152
|
+
/** The runtime the current pass registers source values into. */
|
|
153
|
+
readonly runtime: DependencyRuntimeContext;
|
|
154
|
+
/** The execution context of the current pass. */
|
|
155
|
+
readonly exec: ExecutionContext | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* Schedules further (already expanded) nodes within the current pass,
|
|
158
|
+
* at the barrier's position. Results are deduplicated through the
|
|
159
|
+
* run-scoped session and are not cached by the owning construct.
|
|
160
|
+
*/
|
|
161
|
+
readonly schedule: (nodes: readonly RuntimeNode[]) => Promise<{
|
|
162
|
+
readonly success: false;
|
|
163
|
+
readonly error: Message;
|
|
164
|
+
} | undefined>;
|
|
108
165
|
}
|
|
109
166
|
/**
|
|
110
167
|
* Dependency runtime context for centralized dependency resolution.
|
|
@@ -281,6 +338,41 @@ declare const effectfulSchedulingNodesKey: unique symbol;
|
|
|
281
338
|
* @since 1.3.0
|
|
282
339
|
*/
|
|
283
340
|
type EffectfulSchedulingNodesFn = (state: unknown, parentPath: readonly PropertyKey[] | undefined) => readonly RuntimeNode[];
|
|
341
|
+
/**
|
|
342
|
+
* Opt-in marker for parsers whose {@link effectfulSchedulingNodesKey}
|
|
343
|
+
* hook also defines their explicit-source *collection* scope.
|
|
344
|
+
*
|
|
345
|
+
* A parent construct normally collects explicit source values from its
|
|
346
|
+
* direct children only. A parser carrying this marker (with value
|
|
347
|
+
* `true`) asks the parent to expand it through its scheduling hook
|
|
348
|
+
* before collecting, so command-line source values inside it—such as a
|
|
349
|
+
* `conditional()` discriminator, a committed conditional branch, or a
|
|
350
|
+
* selected `command()` subtree—register into the parent's dependency
|
|
351
|
+
* runtime exactly like a prompt-completed value would. Constructs
|
|
352
|
+
* without the marker (plain nested `object()`, uncommitted exclusive
|
|
353
|
+
* branches) keep their existing scope.
|
|
354
|
+
*
|
|
355
|
+
* @internal
|
|
356
|
+
* @since 1.3.0
|
|
357
|
+
*/
|
|
358
|
+
declare const sourceCollectionExpansionKey: unique symbol;
|
|
359
|
+
/**
|
|
360
|
+
* Static child parsers reachable for dependency-source estimation.
|
|
361
|
+
*
|
|
362
|
+
* `collectStaticSourceIds()` walks flattened field pairs, which stops at
|
|
363
|
+
* parsers whose children are not field-shaped—a `command()`'s inner
|
|
364
|
+
* parser, a nested `conditional()`'s branches, exclusive alternatives,
|
|
365
|
+
* or a transparent wrapper's inner construct. Such parsers expose their
|
|
366
|
+
* children here so the walk can estimate every source a subtree may
|
|
367
|
+
* provide. The estimate feeds demand-only control dependencies, where
|
|
368
|
+
* an overcount merely completes a discriminator earlier than strictly
|
|
369
|
+
* needed, while an undercount delays an effectful completion to the
|
|
370
|
+
* final pass and starves phase-two contexts of seed values.
|
|
371
|
+
*
|
|
372
|
+
* @internal
|
|
373
|
+
* @since 1.3.0
|
|
374
|
+
*/
|
|
375
|
+
declare const staticSourceScopeKey: unique symbol;
|
|
284
376
|
/**
|
|
285
377
|
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
286
378
|
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
@@ -369,6 +461,25 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
369
461
|
* running twice, and are skipped when no session is available.
|
|
370
462
|
*/
|
|
371
463
|
readonly isReusable?: (node: RuntimeNode) => boolean;
|
|
464
|
+
/**
|
|
465
|
+
* Whether a node participated in the owning construct's explicit
|
|
466
|
+
* source collection, and therefore must have its structural value
|
|
467
|
+
* re-registered at its declaration position so registration order
|
|
468
|
+
* follows declaration order across structural and effectful
|
|
469
|
+
* occurrences. This is wider than {@link isReusable}: nodes expanded
|
|
470
|
+
* from an opted-in child (see `sourceCollectionExpansionKey`) are
|
|
471
|
+
* collected but not reusable. Defaults to {@link isReusable}.
|
|
472
|
+
*/
|
|
473
|
+
readonly isCollected?: (node: RuntimeNode) => boolean;
|
|
474
|
+
/**
|
|
475
|
+
* Processes structural source nodes even when no node carries an
|
|
476
|
+
* effectful completion or a barrier. A barrier's nested scheduling
|
|
477
|
+
* call sets this: the branch it schedules may hold only structural
|
|
478
|
+
* (command-line) values, which still must register into the pass's
|
|
479
|
+
* runtime, while ordinary construct passes keep the cheap early
|
|
480
|
+
* return.
|
|
481
|
+
*/
|
|
482
|
+
readonly includeStructural?: boolean;
|
|
372
483
|
}
|
|
373
484
|
/**
|
|
374
485
|
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
@@ -378,12 +489,12 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
378
489
|
*
|
|
379
490
|
* - Runs only during real completion (`exec.phase === "complete"`); probe
|
|
380
491
|
* and suggest phases return immediately without effects.
|
|
381
|
-
* - Precedence is structural
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
* When several scheduled occurrences
|
|
385
|
-
*
|
|
386
|
-
*
|
|
492
|
+
* - Precedence is structural per occurrence: an effectful completion
|
|
493
|
+
* returns its own field's command-line or bound value without running
|
|
494
|
+
* the effect, and structural occurrences re-register their extracted
|
|
495
|
+
* values in declaration order. When several scheduled occurrences
|
|
496
|
+
* share one source, the last occurrence wins—the same rule as repeated
|
|
497
|
+
* command-line source occurrences.
|
|
387
498
|
* - Completion results that are `undefined` or marked `deferred` are
|
|
388
499
|
* treated as declined and neither registered nor cached.
|
|
389
500
|
* - A successful result registers its value unless the value is
|
|
@@ -416,6 +527,16 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
416
527
|
* @since 1.3.0
|
|
417
528
|
*/
|
|
418
529
|
declare function completeEffectfulSourcesAsync(nodes: readonly RuntimeNode[], state: unknown, runtime: DependencyRuntimeContext, exec: ExecutionContext | undefined, options?: CompleteEffectfulSourcesOptions): Promise<EffectfulSourceCompletionResult>;
|
|
530
|
+
/**
|
|
531
|
+
* Serializes a scheduling node path into a stable string key, using
|
|
532
|
+
* length-prefixed segments so no separator escaping is needed. Shared
|
|
533
|
+
* with constructs that key run-scoped session entries by path (e.g.,
|
|
534
|
+
* `conditional()`'s prepared branch selections).
|
|
535
|
+
*
|
|
536
|
+
* @internal
|
|
537
|
+
* @since 1.3.0
|
|
538
|
+
*/
|
|
539
|
+
declare function serializeSchedulingPath(path: readonly PropertyKey[]): string;
|
|
419
540
|
/**
|
|
420
541
|
* Recursively collects dependency source values from {@link DependencySourceState}
|
|
421
542
|
* objects found in the state tree and registers them in the runtime.
|
|
@@ -495,4 +616,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
495
616
|
readonly initialState?: unknown;
|
|
496
617
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
497
618
|
//#endregion
|
|
498
|
-
export { CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ReplayKey, RuntimeNode, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync };
|
|
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 };
|
|
@@ -494,6 +494,41 @@ function extractRawInputFromState(state) {
|
|
|
494
494
|
*/
|
|
495
495
|
const effectfulSchedulingNodesKey = Symbol("@optique/core/dependency-runtime/effectfulSchedulingNodes");
|
|
496
496
|
/**
|
|
497
|
+
* Opt-in marker for parsers whose {@link effectfulSchedulingNodesKey}
|
|
498
|
+
* hook also defines their explicit-source *collection* scope.
|
|
499
|
+
*
|
|
500
|
+
* A parent construct normally collects explicit source values from its
|
|
501
|
+
* direct children only. A parser carrying this marker (with value
|
|
502
|
+
* `true`) asks the parent to expand it through its scheduling hook
|
|
503
|
+
* before collecting, so command-line source values inside it—such as a
|
|
504
|
+
* `conditional()` discriminator, a committed conditional branch, or a
|
|
505
|
+
* selected `command()` subtree—register into the parent's dependency
|
|
506
|
+
* runtime exactly like a prompt-completed value would. Constructs
|
|
507
|
+
* without the marker (plain nested `object()`, uncommitted exclusive
|
|
508
|
+
* branches) keep their existing scope.
|
|
509
|
+
*
|
|
510
|
+
* @internal
|
|
511
|
+
* @since 1.3.0
|
|
512
|
+
*/
|
|
513
|
+
const sourceCollectionExpansionKey = Symbol("@optique/core/dependency-runtime/sourceCollectionExpansion");
|
|
514
|
+
/**
|
|
515
|
+
* Static child parsers reachable for dependency-source estimation.
|
|
516
|
+
*
|
|
517
|
+
* `collectStaticSourceIds()` walks flattened field pairs, which stops at
|
|
518
|
+
* parsers whose children are not field-shaped—a `command()`'s inner
|
|
519
|
+
* parser, a nested `conditional()`'s branches, exclusive alternatives,
|
|
520
|
+
* or a transparent wrapper's inner construct. Such parsers expose their
|
|
521
|
+
* children here so the walk can estimate every source a subtree may
|
|
522
|
+
* provide. The estimate feeds demand-only control dependencies, where
|
|
523
|
+
* an overcount merely completes a discriminator earlier than strictly
|
|
524
|
+
* needed, while an undercount delays an effectful completion to the
|
|
525
|
+
* final pass and starves phase-two contexts of seed values.
|
|
526
|
+
*
|
|
527
|
+
* @internal
|
|
528
|
+
* @since 1.3.0
|
|
529
|
+
*/
|
|
530
|
+
const staticSourceScopeKey = Symbol("@optique/core/dependency-runtime/staticSourceScope");
|
|
531
|
+
/**
|
|
497
532
|
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
498
533
|
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
499
534
|
* wrapped parser—a selected exclusive or command branch, or an ordinary
|
|
@@ -511,6 +546,11 @@ const effectfulSchedulingNodesKey = Symbol("@optique/core/dependency-runtime/eff
|
|
|
511
546
|
* @since 1.3.0
|
|
512
547
|
*/
|
|
513
548
|
function defineForwardedEffectfulSchedulingNodes(wrapper, inner, adaptState) {
|
|
549
|
+
Object.defineProperty(wrapper, staticSourceScopeKey, {
|
|
550
|
+
value: [inner],
|
|
551
|
+
configurable: true,
|
|
552
|
+
enumerable: false
|
|
553
|
+
});
|
|
514
554
|
if (inner.dependencyMetadata?.source != null) return;
|
|
515
555
|
Object.defineProperty(wrapper, effectfulSchedulingNodesKey, {
|
|
516
556
|
value: ((state, parentPath) => [{
|
|
@@ -521,6 +561,11 @@ function defineForwardedEffectfulSchedulingNodes(wrapper, inner, adaptState) {
|
|
|
521
561
|
configurable: true,
|
|
522
562
|
enumerable: false
|
|
523
563
|
});
|
|
564
|
+
if (inner[sourceCollectionExpansionKey] === true) Object.defineProperty(wrapper, sourceCollectionExpansionKey, {
|
|
565
|
+
value: true,
|
|
566
|
+
configurable: true,
|
|
567
|
+
enumerable: false
|
|
568
|
+
});
|
|
524
569
|
}
|
|
525
570
|
/**
|
|
526
571
|
* Collects the dependency source IDs demanded by consumers among the
|
|
@@ -570,12 +615,12 @@ function collectDeferredDemand(state, demanded, visited) {
|
|
|
570
615
|
*
|
|
571
616
|
* - Runs only during real completion (`exec.phase === "complete"`); probe
|
|
572
617
|
* and suggest phases return immediately without effects.
|
|
573
|
-
* - Precedence is structural
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
* When several scheduled occurrences
|
|
577
|
-
*
|
|
578
|
-
*
|
|
618
|
+
* - Precedence is structural per occurrence: an effectful completion
|
|
619
|
+
* returns its own field's command-line or bound value without running
|
|
620
|
+
* the effect, and structural occurrences re-register their extracted
|
|
621
|
+
* values in declaration order. When several scheduled occurrences
|
|
622
|
+
* share one source, the last occurrence wins—the same rule as repeated
|
|
623
|
+
* command-line source occurrences.
|
|
579
624
|
* - Completion results that are `undefined` or marked `deferred` are
|
|
580
625
|
* treated as declined and neither registered nor cached.
|
|
581
626
|
* - A successful result registers its value unless the value is
|
|
@@ -618,6 +663,14 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
618
663
|
const demandNodes = options?.demandNodes ?? nodes;
|
|
619
664
|
const demanded = collectDemandedDependencyIds(demandNodes, state, exec.trace);
|
|
620
665
|
for (const id of demanded) session.demanded.add(id);
|
|
666
|
+
for (const node of nodes) {
|
|
667
|
+
if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
|
|
668
|
+
if (session.demanded.has(node.requiresSourceId)) continue;
|
|
669
|
+
for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
|
|
670
|
+
session.demanded.add(node.requiresSourceId);
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
621
674
|
}
|
|
622
675
|
if (session != null) {
|
|
623
676
|
for (const result of session.results.values()) if (!result.success) return {
|
|
@@ -625,14 +678,28 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
625
678
|
error: result.error
|
|
626
679
|
};
|
|
627
680
|
}
|
|
628
|
-
const schedulable = nodes.filter((node) => node.parser.dependencyMetadata?.source?.completeSource != null);
|
|
629
|
-
if (schedulable.length === 0) return empty;
|
|
681
|
+
const schedulable = nodes.filter((node) => node.parser.dependencyMetadata?.source?.completeSource != null || node.prepare != null);
|
|
682
|
+
if (schedulable.length === 0 && options?.includeStructural !== true) return empty;
|
|
630
683
|
const completed = [];
|
|
631
684
|
for (const node of nodes) {
|
|
685
|
+
if (node.prepare != null) {
|
|
686
|
+
const barrierFailure = await node.prepare({
|
|
687
|
+
runtime,
|
|
688
|
+
exec,
|
|
689
|
+
schedule: (barrierNodes) => completeEffectfulSourcesAsync(barrierNodes, state, runtime, exec, {
|
|
690
|
+
isReusable: () => false,
|
|
691
|
+
isCollected: () => true,
|
|
692
|
+
includeStructural: true
|
|
693
|
+
}).then((result$1) => result$1.success ? void 0 : result$1)
|
|
694
|
+
});
|
|
695
|
+
if (barrierFailure != null) return barrierFailure;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
632
698
|
const source = node.parser.dependencyMetadata?.source;
|
|
633
699
|
if (source == null) continue;
|
|
634
700
|
if (source.completeSource == null) {
|
|
635
|
-
|
|
701
|
+
const collected = options?.isCollected?.(node) ?? options?.isReusable?.(node) ?? true;
|
|
702
|
+
if (source.extractSourceValue == null || collected === false) continue;
|
|
636
703
|
const extracted = await source.extractSourceValue(node.state);
|
|
637
704
|
if (extracted?.success === true && extracted.value !== void 0) runtime.registerSource(source.sourceId, extracted.value);
|
|
638
705
|
continue;
|
|
@@ -676,7 +743,12 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
676
743
|
}
|
|
677
744
|
/**
|
|
678
745
|
* Serializes a scheduling node path into a stable string key, using
|
|
679
|
-
* length-prefixed segments so no separator escaping is needed.
|
|
746
|
+
* length-prefixed segments so no separator escaping is needed. Shared
|
|
747
|
+
* with constructs that key run-scoped session entries by path (e.g.,
|
|
748
|
+
* `conditional()`'s prepared branch selections).
|
|
749
|
+
*
|
|
750
|
+
* @internal
|
|
751
|
+
* @since 1.3.0
|
|
680
752
|
*/
|
|
681
753
|
function serializeSchedulingPath(path) {
|
|
682
754
|
return path.map(serializePathSegment).join("");
|
|
@@ -921,4 +993,4 @@ function buildRuntimeNodesFromArray(parsers, stateArray, parentPath) {
|
|
|
921
993
|
}
|
|
922
994
|
|
|
923
995
|
//#endregion
|
|
924
|
-
export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync };
|
|
996
|
+
export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
package/dist/facade.cjs
CHANGED
|
@@ -144,7 +144,8 @@ function createRunWithSessions() {
|
|
|
144
144
|
...seedSession,
|
|
145
145
|
policy: "eager",
|
|
146
146
|
effectfulSources: /* @__PURE__ */ new Set(),
|
|
147
|
-
completedByPath: /* @__PURE__ */ new Map()
|
|
147
|
+
completedByPath: /* @__PURE__ */ new Map(),
|
|
148
|
+
preparedByPath: /* @__PURE__ */ new Map()
|
|
148
149
|
};
|
|
149
150
|
return {
|
|
150
151
|
seedSession,
|