@optique/core 1.3.0-dev.2375 → 1.3.0-dev.2380
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 +344 -38
- package/dist/constructs.js +345 -39
- package/dist/dependency-metadata.cjs +13 -1
- package/dist/dependency-metadata.d.cts +20 -1
- package/dist/dependency-metadata.d.ts +20 -1
- package/dist/dependency-metadata.js +13 -1
- package/dist/dependency-runtime.cjs +216 -0
- package/dist/dependency-runtime.d.cts +162 -1
- package/dist/dependency-runtime.d.ts +162 -1
- package/dist/dependency-runtime.js +213 -1
- package/dist/facade.cjs +54 -26
- package/dist/facade.js +55 -27
- package/dist/index.cjs +0 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/internal/parser.cjs +23 -3
- package/dist/internal/parser.d.cts +80 -3
- package/dist/internal/parser.d.ts +80 -3
- package/dist/internal/parser.js +23 -4
- package/dist/modifiers.cjs +43 -2
- package/dist/modifiers.js +43 -2
- package/dist/parser.d.cts +2 -2
- package/dist/parser.d.ts +2 -2
- package/dist/primitives.cjs +14 -0
- package/dist/primitives.js +15 -1
- package/dist/valueparser.cjs +0 -66
- package/dist/valueparser.d.cts +1 -59
- package/dist/valueparser.d.ts +1 -59
- package/dist/valueparser.js +1 -66
- package/package.json +2 -2
- package/skills/optique/SKILL.md +5 -6
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { Message } from "./message.js";
|
|
1
2
|
import { DependencyRegistryLike } from "./registry-types.js";
|
|
2
3
|
import { ValueParserResult } from "./valueparser.js";
|
|
3
4
|
import { ParserDependencyMetadata } from "./dependency-metadata.js";
|
|
5
|
+
import { InputTrace } from "./input-trace.js";
|
|
6
|
+
import { ExecutionContext } from "./internal/parser.js";
|
|
4
7
|
|
|
5
8
|
//#region src/dependency-runtime.d.ts
|
|
6
9
|
|
|
@@ -255,6 +258,164 @@ declare function replayDerivedParserAsync(node: RuntimeNode, rawInput: string, r
|
|
|
255
258
|
* @since 1.0.0
|
|
256
259
|
*/
|
|
257
260
|
declare function extractRawInputFromState(state: unknown): string | undefined;
|
|
261
|
+
/**
|
|
262
|
+
* Internal parser hook for constructs whose effectful scheduling nodes
|
|
263
|
+
* cannot be derived from flattened field pairs alone.
|
|
264
|
+
*
|
|
265
|
+
* `merge()` installs it so a parent's scheduling expansion uses the same
|
|
266
|
+
* child-indexed paths, declaration order, and duplicate-field exclusion
|
|
267
|
+
* as the merge's own scheduling pass; `or()`/`longestMatch()` install it
|
|
268
|
+
* to expose the committed branch; `command()` installs it to expose its
|
|
269
|
+
* inner parser once the command has matched. The returned node paths
|
|
270
|
+
* must match the execution paths used when the same parsers complete, so
|
|
271
|
+
* the run-scoped completion cache lines up.
|
|
272
|
+
*
|
|
273
|
+
* @internal
|
|
274
|
+
* @since 1.3.0
|
|
275
|
+
*/
|
|
276
|
+
declare const effectfulSchedulingNodesKey: unique symbol;
|
|
277
|
+
/**
|
|
278
|
+
* The shape of the {@link effectfulSchedulingNodesKey} hook.
|
|
279
|
+
*
|
|
280
|
+
* @internal
|
|
281
|
+
* @since 1.3.0
|
|
282
|
+
*/
|
|
283
|
+
type EffectfulSchedulingNodesFn = (state: unknown, parentPath: readonly PropertyKey[] | undefined) => readonly RuntimeNode[];
|
|
284
|
+
/**
|
|
285
|
+
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
286
|
+
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
287
|
+
* wrapped parser—a selected exclusive or command branch, or an ordinary
|
|
288
|
+
* construct with nested effectful sources—stays visible to a parent
|
|
289
|
+
* construct's scheduling expansion.
|
|
290
|
+
*
|
|
291
|
+
* The installed hook simply re-exposes the inner parser as a node with
|
|
292
|
+
* the wrapper's own state shape unwrapped (`adaptState`, e.g. the
|
|
293
|
+
* `[innerState]` array used by `optional()`/`withDefault()`); the
|
|
294
|
+
* expansion then applies its ordinary rules to the inner parser, whether
|
|
295
|
+
* it carries its own hook, flattened field pairs, or source metadata.
|
|
296
|
+
* Wrappers are path-transparent, so the node keeps the wrapper's path.
|
|
297
|
+
*
|
|
298
|
+
* @internal
|
|
299
|
+
* @since 1.3.0
|
|
300
|
+
*/
|
|
301
|
+
declare function defineForwardedEffectfulSchedulingNodes(wrapper: object, inner: {
|
|
302
|
+
readonly dependencyMetadata?: ParserDependencyMetadata;
|
|
303
|
+
}, adaptState?: (state: unknown) => unknown): void;
|
|
304
|
+
/**
|
|
305
|
+
* A completed effectful source result to be reused by the owning
|
|
306
|
+
* construct's final completion phase, keyed by the node's last path
|
|
307
|
+
* segment (its field key or tuple index).
|
|
308
|
+
*
|
|
309
|
+
* @internal
|
|
310
|
+
* @since 1.3.0
|
|
311
|
+
*/
|
|
312
|
+
interface EffectfulSourceCompletion {
|
|
313
|
+
/** The node's field key or index (its last path segment). */
|
|
314
|
+
readonly key: PropertyKey;
|
|
315
|
+
/** The effectful completion result. */
|
|
316
|
+
readonly result: ValueParserResult<unknown>;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* The result of scheduling effectful source completions.
|
|
320
|
+
*
|
|
321
|
+
* @internal
|
|
322
|
+
* @since 1.3.0
|
|
323
|
+
*/
|
|
324
|
+
type EffectfulSourceCompletionResult = {
|
|
325
|
+
readonly success: true;
|
|
326
|
+
readonly completed: readonly EffectfulSourceCompletion[];
|
|
327
|
+
} | {
|
|
328
|
+
readonly success: false;
|
|
329
|
+
readonly error: Message;
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* Collects the dependency source IDs demanded by consumers among the
|
|
333
|
+
* given nodes and state subtree.
|
|
334
|
+
*
|
|
335
|
+
* A consumer demands its sources when it has raw input evidence: either a
|
|
336
|
+
* trace entry recorded at its path during parsing, or a legacy
|
|
337
|
+
* `DeferredParseState` embedded in the state subtree. Consumers without
|
|
338
|
+
* raw input never replay against real dependency values, so their sources
|
|
339
|
+
* are not demanded.
|
|
340
|
+
*
|
|
341
|
+
* @param nodes The direct-child runtime nodes of the owning construct.
|
|
342
|
+
* @param state The construct's state subtree (for legacy deferred states).
|
|
343
|
+
* @param trace The input trace recorded during parsing.
|
|
344
|
+
* @returns The set of demanded dependency source IDs.
|
|
345
|
+
* @internal
|
|
346
|
+
* @since 1.3.0
|
|
347
|
+
*/
|
|
348
|
+
declare function collectDemandedDependencyIds(nodes: readonly RuntimeNode[], state: unknown, trace: InputTrace | undefined): ReadonlySet<symbol>;
|
|
349
|
+
/**
|
|
350
|
+
* Options for {@link completeEffectfulSourcesAsync}.
|
|
351
|
+
*
|
|
352
|
+
* @internal
|
|
353
|
+
* @since 1.3.0
|
|
354
|
+
*/
|
|
355
|
+
interface CompleteEffectfulSourcesOptions {
|
|
356
|
+
/**
|
|
357
|
+
* Nodes used for demand detection instead of the scheduled nodes.
|
|
358
|
+
* Constructs whose parse-time trace paths differ from their scheduling
|
|
359
|
+
* node paths (e.g., `merge()`, which records child-indexed paths but
|
|
360
|
+
* schedules flattened field nodes) pass path-corrected nodes here.
|
|
361
|
+
*/
|
|
362
|
+
readonly demandNodes?: readonly RuntimeNode[];
|
|
363
|
+
/**
|
|
364
|
+
* Whether a node's completion result may be cached by the owning
|
|
365
|
+
* construct for reuse in its final completion phase. Defaults to
|
|
366
|
+
* treating every node as reusable. Non-reusable completions (nodes
|
|
367
|
+
* expanded from nested children, or sources whose field value differs
|
|
368
|
+
* from the source value) rely on the run-scoped session cache to avoid
|
|
369
|
+
* running twice, and are skipped when no session is available.
|
|
370
|
+
*/
|
|
371
|
+
readonly isReusable?: (node: RuntimeNode) => boolean;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
375
|
+
* in declaration order for source nodes whose value is not yet registered.
|
|
376
|
+
*
|
|
377
|
+
* This is the scheduling half of the `completeSource` capability contract:
|
|
378
|
+
*
|
|
379
|
+
* - Runs only during real completion (`exec.phase === "complete"`); probe
|
|
380
|
+
* and suggest phases return immediately without effects.
|
|
381
|
+
* - Precedence is structural: a source whose value was registered before
|
|
382
|
+
* the pass begins (from CLI state, environment, configuration, or a
|
|
383
|
+
* default) or that has already failed is never completed effectfully.
|
|
384
|
+
* When several scheduled occurrences share one source, each occurrence
|
|
385
|
+
* still completes and re-registers, so the last occurrence wins—the
|
|
386
|
+
* same rule as repeated command-line source occurrences.
|
|
387
|
+
* - Completion results that are `undefined` or marked `deferred` are
|
|
388
|
+
* treated as declined and neither registered nor cached.
|
|
389
|
+
* - A successful result registers its value unless the value is
|
|
390
|
+
* `undefined`. When the node is reusable (a direct child whose field
|
|
391
|
+
* value is the source value), the result is also returned for reuse by
|
|
392
|
+
* the owning construct so the node is not completed twice; otherwise the
|
|
393
|
+
* effectful parser's own run-scoped session cache prevents a second
|
|
394
|
+
* execution, so nodes that are not reusable are only scheduled when a
|
|
395
|
+
* session is present. For a source behind a transform such as `map()`
|
|
396
|
+
* (`preservesSourceValue: false`), the `completeSource` contract still
|
|
397
|
+
* yields the pre-transform source value, so registration stays correct
|
|
398
|
+
* while the field's final value is produced separately.
|
|
399
|
+
* - A failed result (e.g., a cancelled prompt) marks the source as failed
|
|
400
|
+
* and aborts immediately—later effectful completions do not run.
|
|
401
|
+
*
|
|
402
|
+
* When the run-scoped session policy is `"demand-only"` (the phase-two
|
|
403
|
+
* seed pass), the demanded source IDs from
|
|
404
|
+
* {@link collectDemandedDependencyIds} are added to the session before any
|
|
405
|
+
* completion runs, letting effectful parsers defer when no phase-one
|
|
406
|
+
* consumer demands their value.
|
|
407
|
+
*
|
|
408
|
+
* @param nodes The runtime nodes to schedule, in declaration order.
|
|
409
|
+
* @param state The construct's state subtree (for demand detection).
|
|
410
|
+
* @param runtime The dependency runtime context.
|
|
411
|
+
* @param exec The execution context of the owning construct.
|
|
412
|
+
* @param options Scheduling options.
|
|
413
|
+
* @returns The scheduling result: completed nodes for reuse, or the first
|
|
414
|
+
* failure.
|
|
415
|
+
* @internal
|
|
416
|
+
* @since 1.3.0
|
|
417
|
+
*/
|
|
418
|
+
declare function completeEffectfulSourcesAsync(nodes: readonly RuntimeNode[], state: unknown, runtime: DependencyRuntimeContext, exec: ExecutionContext | undefined, options?: CompleteEffectfulSourcesOptions): Promise<EffectfulSourceCompletionResult>;
|
|
258
419
|
/**
|
|
259
420
|
* Recursively collects dependency source values from {@link DependencySourceState}
|
|
260
421
|
* objects found in the state tree and registers them in the runtime.
|
|
@@ -334,4 +495,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
334
495
|
readonly initialState?: unknown;
|
|
335
496
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
336
497
|
//#endregion
|
|
337
|
-
export { DependencyRequest, DependencyResolution, DependencyRuntimeContext, ReplayKey, RuntimeNode, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync };
|
|
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 };
|
|
@@ -478,6 +478,218 @@ function extractRawInputFromState(state) {
|
|
|
478
478
|
return void 0;
|
|
479
479
|
}
|
|
480
480
|
/**
|
|
481
|
+
* Internal parser hook for constructs whose effectful scheduling nodes
|
|
482
|
+
* cannot be derived from flattened field pairs alone.
|
|
483
|
+
*
|
|
484
|
+
* `merge()` installs it so a parent's scheduling expansion uses the same
|
|
485
|
+
* child-indexed paths, declaration order, and duplicate-field exclusion
|
|
486
|
+
* as the merge's own scheduling pass; `or()`/`longestMatch()` install it
|
|
487
|
+
* to expose the committed branch; `command()` installs it to expose its
|
|
488
|
+
* inner parser once the command has matched. The returned node paths
|
|
489
|
+
* must match the execution paths used when the same parsers complete, so
|
|
490
|
+
* the run-scoped completion cache lines up.
|
|
491
|
+
*
|
|
492
|
+
* @internal
|
|
493
|
+
* @since 1.3.0
|
|
494
|
+
*/
|
|
495
|
+
const effectfulSchedulingNodesKey = Symbol("@optique/core/dependency-runtime/effectfulSchedulingNodes");
|
|
496
|
+
/**
|
|
497
|
+
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
498
|
+
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
499
|
+
* wrapped parser—a selected exclusive or command branch, or an ordinary
|
|
500
|
+
* construct with nested effectful sources—stays visible to a parent
|
|
501
|
+
* construct's scheduling expansion.
|
|
502
|
+
*
|
|
503
|
+
* The installed hook simply re-exposes the inner parser as a node with
|
|
504
|
+
* the wrapper's own state shape unwrapped (`adaptState`, e.g. the
|
|
505
|
+
* `[innerState]` array used by `optional()`/`withDefault()`); the
|
|
506
|
+
* expansion then applies its ordinary rules to the inner parser, whether
|
|
507
|
+
* it carries its own hook, flattened field pairs, or source metadata.
|
|
508
|
+
* Wrappers are path-transparent, so the node keeps the wrapper's path.
|
|
509
|
+
*
|
|
510
|
+
* @internal
|
|
511
|
+
* @since 1.3.0
|
|
512
|
+
*/
|
|
513
|
+
function defineForwardedEffectfulSchedulingNodes(wrapper, inner, adaptState) {
|
|
514
|
+
if (inner.dependencyMetadata?.source != null) return;
|
|
515
|
+
Object.defineProperty(wrapper, effectfulSchedulingNodesKey, {
|
|
516
|
+
value: ((state, parentPath) => [{
|
|
517
|
+
path: parentPath ?? [],
|
|
518
|
+
parser: inner,
|
|
519
|
+
state: adaptState == null ? state : adaptState(state)
|
|
520
|
+
}]),
|
|
521
|
+
configurable: true,
|
|
522
|
+
enumerable: false
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Collects the dependency source IDs demanded by consumers among the
|
|
527
|
+
* given nodes and state subtree.
|
|
528
|
+
*
|
|
529
|
+
* A consumer demands its sources when it has raw input evidence: either a
|
|
530
|
+
* trace entry recorded at its path during parsing, or a legacy
|
|
531
|
+
* `DeferredParseState` embedded in the state subtree. Consumers without
|
|
532
|
+
* raw input never replay against real dependency values, so their sources
|
|
533
|
+
* are not demanded.
|
|
534
|
+
*
|
|
535
|
+
* @param nodes The direct-child runtime nodes of the owning construct.
|
|
536
|
+
* @param state The construct's state subtree (for legacy deferred states).
|
|
537
|
+
* @param trace The input trace recorded during parsing.
|
|
538
|
+
* @returns The set of demanded dependency source IDs.
|
|
539
|
+
* @internal
|
|
540
|
+
* @since 1.3.0
|
|
541
|
+
*/
|
|
542
|
+
function collectDemandedDependencyIds(nodes, state, trace) {
|
|
543
|
+
const demanded = /* @__PURE__ */ new Set();
|
|
544
|
+
for (const node of nodes) {
|
|
545
|
+
const derived = node.parser.dependencyMetadata?.derived;
|
|
546
|
+
if (derived == null) continue;
|
|
547
|
+
const hasRawInput = trace?.get(node.path)?.rawInput != null || extractRawInputFromState(node.state) != null;
|
|
548
|
+
if (!hasRawInput) continue;
|
|
549
|
+
for (const id of derived.dependencyIds) demanded.add(id);
|
|
550
|
+
}
|
|
551
|
+
collectDeferredDemand(state, demanded, /* @__PURE__ */ new WeakSet());
|
|
552
|
+
return demanded;
|
|
553
|
+
}
|
|
554
|
+
function collectDeferredDemand(state, demanded, visited) {
|
|
555
|
+
if (state == null || typeof state !== "object") return;
|
|
556
|
+
if (visited.has(state)) return;
|
|
557
|
+
visited.add(state);
|
|
558
|
+
if (isDeferredParseState(state)) {
|
|
559
|
+
const ids = state.dependencyIds != null && state.dependencyIds.length > 0 ? state.dependencyIds : [state.dependencyId];
|
|
560
|
+
for (const id of ids) demanded.add(id);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
for (const key of Reflect.ownKeys(state)) collectDeferredDemand(state[key], demanded, visited);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
567
|
+
* in declaration order for source nodes whose value is not yet registered.
|
|
568
|
+
*
|
|
569
|
+
* This is the scheduling half of the `completeSource` capability contract:
|
|
570
|
+
*
|
|
571
|
+
* - Runs only during real completion (`exec.phase === "complete"`); probe
|
|
572
|
+
* and suggest phases return immediately without effects.
|
|
573
|
+
* - Precedence is structural: a source whose value was registered before
|
|
574
|
+
* the pass begins (from CLI state, environment, configuration, or a
|
|
575
|
+
* default) or that has already failed is never completed effectfully.
|
|
576
|
+
* When several scheduled occurrences share one source, each occurrence
|
|
577
|
+
* still completes and re-registers, so the last occurrence wins—the
|
|
578
|
+
* same rule as repeated command-line source occurrences.
|
|
579
|
+
* - Completion results that are `undefined` or marked `deferred` are
|
|
580
|
+
* treated as declined and neither registered nor cached.
|
|
581
|
+
* - A successful result registers its value unless the value is
|
|
582
|
+
* `undefined`. When the node is reusable (a direct child whose field
|
|
583
|
+
* value is the source value), the result is also returned for reuse by
|
|
584
|
+
* the owning construct so the node is not completed twice; otherwise the
|
|
585
|
+
* effectful parser's own run-scoped session cache prevents a second
|
|
586
|
+
* execution, so nodes that are not reusable are only scheduled when a
|
|
587
|
+
* session is present. For a source behind a transform such as `map()`
|
|
588
|
+
* (`preservesSourceValue: false`), the `completeSource` contract still
|
|
589
|
+
* yields the pre-transform source value, so registration stays correct
|
|
590
|
+
* while the field's final value is produced separately.
|
|
591
|
+
* - A failed result (e.g., a cancelled prompt) marks the source as failed
|
|
592
|
+
* and aborts immediately—later effectful completions do not run.
|
|
593
|
+
*
|
|
594
|
+
* When the run-scoped session policy is `"demand-only"` (the phase-two
|
|
595
|
+
* seed pass), the demanded source IDs from
|
|
596
|
+
* {@link collectDemandedDependencyIds} are added to the session before any
|
|
597
|
+
* completion runs, letting effectful parsers defer when no phase-one
|
|
598
|
+
* consumer demands their value.
|
|
599
|
+
*
|
|
600
|
+
* @param nodes The runtime nodes to schedule, in declaration order.
|
|
601
|
+
* @param state The construct's state subtree (for demand detection).
|
|
602
|
+
* @param runtime The dependency runtime context.
|
|
603
|
+
* @param exec The execution context of the owning construct.
|
|
604
|
+
* @param options Scheduling options.
|
|
605
|
+
* @returns The scheduling result: completed nodes for reuse, or the first
|
|
606
|
+
* failure.
|
|
607
|
+
* @internal
|
|
608
|
+
* @since 1.3.0
|
|
609
|
+
*/
|
|
610
|
+
async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, options) {
|
|
611
|
+
const empty = {
|
|
612
|
+
success: true,
|
|
613
|
+
completed: []
|
|
614
|
+
};
|
|
615
|
+
if (exec == null || exec.phase !== "complete") return empty;
|
|
616
|
+
const session = exec.effectfulCompletionSession;
|
|
617
|
+
if (session?.policy === "demand-only") {
|
|
618
|
+
const demandNodes = options?.demandNodes ?? nodes;
|
|
619
|
+
const demanded = collectDemandedDependencyIds(demandNodes, state, exec.trace);
|
|
620
|
+
for (const id of demanded) session.demanded.add(id);
|
|
621
|
+
}
|
|
622
|
+
if (session != null) {
|
|
623
|
+
for (const result of session.results.values()) if (!result.success) return {
|
|
624
|
+
success: false,
|
|
625
|
+
error: result.error
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
const schedulable = nodes.filter((node) => node.parser.dependencyMetadata?.source?.completeSource != null);
|
|
629
|
+
if (schedulable.length === 0) return empty;
|
|
630
|
+
const preexisting = /* @__PURE__ */ new Set();
|
|
631
|
+
for (const node of schedulable) {
|
|
632
|
+
const source = node.parser.dependencyMetadata?.source;
|
|
633
|
+
if (source != null && runtime.hasSource(source.sourceId) && session?.effectfulSources.has(source.sourceId) !== true) preexisting.add(source.sourceId);
|
|
634
|
+
}
|
|
635
|
+
const completed = [];
|
|
636
|
+
for (const node of schedulable) {
|
|
637
|
+
const source = node.parser.dependencyMetadata?.source;
|
|
638
|
+
if (source?.completeSource == null) continue;
|
|
639
|
+
const pathKey = serializeSchedulingPath(node.path);
|
|
640
|
+
const priorResult = session?.completedByPath.get(pathKey);
|
|
641
|
+
if (priorResult != null) {
|
|
642
|
+
if (source.preservesSourceValue && (options?.isReusable?.(node) ?? true)) completed.push({
|
|
643
|
+
key: node.path[node.path.length - 1],
|
|
644
|
+
result: priorResult
|
|
645
|
+
});
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (preexisting.has(source.sourceId)) continue;
|
|
649
|
+
const reusable = source.preservesSourceValue && (options?.isReusable?.(node) ?? true);
|
|
650
|
+
if (!reusable && session == null) continue;
|
|
651
|
+
const childExec = {
|
|
652
|
+
...exec,
|
|
653
|
+
path: node.path
|
|
654
|
+
};
|
|
655
|
+
const result = await source.completeSource(node.state, childExec);
|
|
656
|
+
if (result == null) continue;
|
|
657
|
+
if (!result.success) {
|
|
658
|
+
runtime.markSourceFailed(source.sourceId);
|
|
659
|
+
return {
|
|
660
|
+
success: false,
|
|
661
|
+
error: result.error
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
if (result.deferred === true) continue;
|
|
665
|
+
session?.completedByPath.set(pathKey, result);
|
|
666
|
+
if (reusable) completed.push({
|
|
667
|
+
key: node.path[node.path.length - 1],
|
|
668
|
+
result
|
|
669
|
+
});
|
|
670
|
+
if (result.value !== void 0) runtime.registerSource(source.sourceId, result.value);
|
|
671
|
+
}
|
|
672
|
+
return {
|
|
673
|
+
success: true,
|
|
674
|
+
completed
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Serializes a scheduling node path into a stable string key, using
|
|
679
|
+
* length-prefixed segments so no separator escaping is needed.
|
|
680
|
+
*/
|
|
681
|
+
function serializeSchedulingPath(path) {
|
|
682
|
+
return path.map((segment) => {
|
|
683
|
+
if (typeof segment === "symbol") {
|
|
684
|
+
const key = stableSymbolKey(segment);
|
|
685
|
+
return `y${key.length}:${key}`;
|
|
686
|
+
}
|
|
687
|
+
const tag = typeof segment === "number" ? "n" : "s";
|
|
688
|
+
const text = String(segment);
|
|
689
|
+
return `${tag}${text.length}:${text}`;
|
|
690
|
+
}).join("");
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
481
693
|
* Checks if a value is a plain object (not a class instance) for the
|
|
482
694
|
* purpose of recursive state traversal.
|
|
483
695
|
*/
|
|
@@ -717,4 +929,4 @@ function buildRuntimeNodesFromArray(parsers, stateArray, parentPath) {
|
|
|
717
929
|
}
|
|
718
930
|
|
|
719
931
|
//#endregion
|
|
720
|
-
export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync };
|
|
932
|
+
export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, replayDerivedParser, replayDerivedParserAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync };
|
package/dist/facade.cjs
CHANGED
|
@@ -116,7 +116,17 @@ function createParseExec(parser) {
|
|
|
116
116
|
function getCommandPath(exec) {
|
|
117
117
|
return exec?.commandPath ?? [];
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
const effectfulSessionOptionsKey = Symbol("@optique/core/facade/effectfulSessionOptionsKey");
|
|
120
|
+
function getEffectfulSessionOption(options) {
|
|
121
|
+
return options[effectfulSessionOptionsKey];
|
|
122
|
+
}
|
|
123
|
+
function withEffectfulSessionOption(options, session) {
|
|
124
|
+
return {
|
|
125
|
+
...options,
|
|
126
|
+
[effectfulSessionOptionsKey]: session
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function createCompleteExec(exec, context, session) {
|
|
120
130
|
const runtime = require_dependency_runtime.createDependencyRuntimeContext();
|
|
121
131
|
return {
|
|
122
132
|
...exec,
|
|
@@ -124,10 +134,11 @@ function createCompleteExec(exec, context) {
|
|
|
124
134
|
dependencyRuntime: runtime,
|
|
125
135
|
dependencyRegistry: runtime.registry,
|
|
126
136
|
commandPath: getCommandPath(context.exec) ?? exec.commandPath,
|
|
127
|
-
trace: context.exec?.trace ?? context.trace ?? exec.trace
|
|
137
|
+
trace: context.exec?.trace ?? context.trace ?? exec.trace,
|
|
138
|
+
effectfulCompletionSession: session ?? require_internal_parser.createEffectfulCompletionSession()
|
|
128
139
|
};
|
|
129
140
|
}
|
|
130
|
-
function attemptParseSync(parser, args, mode = "complete") {
|
|
141
|
+
function attemptParseSync(parser, args, mode = "complete", session) {
|
|
131
142
|
const shouldUnwrapAnnotatedValue = require_internal_annotations.isInjectedAnnotationWrapper(parser.initialState);
|
|
132
143
|
const exec = createParseExec(parser);
|
|
133
144
|
let context = require_internal_parser.createParserContext({
|
|
@@ -166,7 +177,7 @@ function attemptParseSync(parser, args, mode = "complete") {
|
|
|
166
177
|
kind: "success",
|
|
167
178
|
value: void 0
|
|
168
179
|
};
|
|
169
|
-
const endResult = parser.complete(context.state, createCompleteExec(exec, context));
|
|
180
|
+
const endResult = parser.complete(context.state, createCompleteExec(exec, context, session));
|
|
170
181
|
if (!endResult.success) return {
|
|
171
182
|
kind: "failure",
|
|
172
183
|
error: endResult.error,
|
|
@@ -180,7 +191,7 @@ function attemptParseSync(parser, args, mode = "complete") {
|
|
|
180
191
|
value: shouldUnwrapAnnotatedValue ? require_internal_annotations.unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value
|
|
181
192
|
};
|
|
182
193
|
}
|
|
183
|
-
async function attemptParseAsync(parser, args, mode = "complete") {
|
|
194
|
+
async function attemptParseAsync(parser, args, mode = "complete", session) {
|
|
184
195
|
const shouldUnwrapAnnotatedValue = require_internal_annotations.isInjectedAnnotationWrapper(parser.initialState);
|
|
185
196
|
const exec = createParseExec(parser);
|
|
186
197
|
let context = require_internal_parser.createParserContext({
|
|
@@ -219,7 +230,7 @@ async function attemptParseAsync(parser, args, mode = "complete") {
|
|
|
219
230
|
kind: "success",
|
|
220
231
|
value: void 0
|
|
221
232
|
};
|
|
222
|
-
const endResult = await parser.complete(context.state, createCompleteExec(exec, context));
|
|
233
|
+
const endResult = await parser.complete(context.state, createCompleteExec(exec, context, session));
|
|
223
234
|
if (!endResult.success) return {
|
|
224
235
|
kind: "failure",
|
|
225
236
|
error: endResult.error,
|
|
@@ -233,7 +244,7 @@ async function attemptParseAsync(parser, args, mode = "complete") {
|
|
|
233
244
|
value: shouldUnwrapAnnotatedValue ? require_internal_annotations.unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value
|
|
234
245
|
};
|
|
235
246
|
}
|
|
236
|
-
function createPhase2SeedExec(parser, context) {
|
|
247
|
+
function createPhase2SeedExec(parser, context, session) {
|
|
237
248
|
const exec = {
|
|
238
249
|
usage: parser.usage,
|
|
239
250
|
phase: "parse",
|
|
@@ -248,7 +259,8 @@ function createPhase2SeedExec(parser, context) {
|
|
|
248
259
|
dependencyRuntime: runtime,
|
|
249
260
|
dependencyRegistry: runtime.registry,
|
|
250
261
|
commandPath: getCommandPath(context.exec),
|
|
251
|
-
trace: context.exec?.trace ?? context.trace ?? exec.trace
|
|
262
|
+
trace: context.exec?.trace ?? context.trace ?? exec.trace,
|
|
263
|
+
effectfulCompletionSession: session ?? require_internal_parser.createEffectfulCompletionSession()
|
|
252
264
|
};
|
|
253
265
|
}
|
|
254
266
|
function createPhase2SeedContext(parser, args) {
|
|
@@ -265,27 +277,27 @@ function createPhase2SeedContext(parser, args) {
|
|
|
265
277
|
optionsTerminated: false
|
|
266
278
|
}, exec);
|
|
267
279
|
}
|
|
268
|
-
function extractPhase2SeedSync(parser, args) {
|
|
280
|
+
function extractPhase2SeedSync(parser, args, session) {
|
|
269
281
|
let context = createPhase2SeedContext(parser, args);
|
|
270
282
|
do {
|
|
271
283
|
const result = parser.parse(context);
|
|
272
|
-
if (!result.success) return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
284
|
+
if (!result.success) return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context, session));
|
|
273
285
|
const previousBuffer = context.buffer;
|
|
274
286
|
context = result.next;
|
|
275
|
-
if (isBufferUnchanged(previousBuffer, context.buffer)) return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
287
|
+
if (isBufferUnchanged(previousBuffer, context.buffer)) return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context, session));
|
|
276
288
|
} while (context.buffer.length > 0);
|
|
277
|
-
return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
289
|
+
return require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context, session));
|
|
278
290
|
}
|
|
279
|
-
async function extractPhase2SeedAsync(parser, args) {
|
|
291
|
+
async function extractPhase2SeedAsync(parser, args, session) {
|
|
280
292
|
let context = createPhase2SeedContext(parser, args);
|
|
281
293
|
do {
|
|
282
294
|
const result = await parser.parse(context);
|
|
283
|
-
if (!result.success) return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
295
|
+
if (!result.success) return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context, session));
|
|
284
296
|
const previousBuffer = context.buffer;
|
|
285
297
|
context = result.next;
|
|
286
|
-
if (isBufferUnchanged(previousBuffer, context.buffer)) return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
298
|
+
if (isBufferUnchanged(previousBuffer, context.buffer)) return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context, session));
|
|
287
299
|
} while (context.buffer.length > 0);
|
|
288
|
-
return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context));
|
|
300
|
+
return await require_phase2_seed.completeOrExtractPhase2Seed(parser, context.state, createPhase2SeedExec(parser, context, session));
|
|
289
301
|
}
|
|
290
302
|
function getMetaCommandAliases(names) {
|
|
291
303
|
const [, firstAlias, ...restAliases] = names;
|
|
@@ -1243,7 +1255,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1243
1255
|
};
|
|
1244
1256
|
const parserMode = parser.mode;
|
|
1245
1257
|
return require_mode_dispatch.dispatchByMode(parserMode, () => {
|
|
1246
|
-
const attempted = attemptParseSync(parser, args);
|
|
1258
|
+
const attempted = attemptParseSync(parser, args, "complete", getEffectfulSessionOption(options));
|
|
1247
1259
|
const classified = attempted.kind === "success" ? {
|
|
1248
1260
|
type: "success",
|
|
1249
1261
|
value: attempted.value
|
|
@@ -1252,7 +1264,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1252
1264
|
if (handled instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
|
|
1253
1265
|
return handled;
|
|
1254
1266
|
}, async () => {
|
|
1255
|
-
const attempted = await attemptParseAsync(parser, args);
|
|
1267
|
+
const attempted = await attemptParseAsync(parser, args, "complete", getEffectfulSessionOption(options));
|
|
1256
1268
|
const classified = attempted.kind === "success" ? {
|
|
1257
1269
|
type: "success",
|
|
1258
1270
|
value: attempted.value
|
|
@@ -1600,16 +1612,24 @@ async function runWithBody(parser, programName, contexts, args, options) {
|
|
|
1600
1612
|
if (parser.mode === "async") return runParser(augmentedParser1, programName, args, options);
|
|
1601
1613
|
return Promise.resolve(runParser(augmentedParser1, programName, args, options));
|
|
1602
1614
|
}
|
|
1603
|
-
const
|
|
1615
|
+
const seedSession = require_internal_parser.createEffectfulCompletionSession("demand-only");
|
|
1616
|
+
const finalSession = {
|
|
1617
|
+
...seedSession,
|
|
1618
|
+
policy: "eager",
|
|
1619
|
+
effectfulSources: /* @__PURE__ */ new Set(),
|
|
1620
|
+
completedByPath: /* @__PURE__ */ new Map()
|
|
1621
|
+
};
|
|
1622
|
+
const sessionOptions = withEffectfulSessionOption(options, finalSession);
|
|
1623
|
+
const firstPassSeed = await require_mode_dispatch.dispatchByMode(parser.mode, () => extractPhase2SeedSync(augmentedParser1, args, seedSession), () => extractPhase2SeedAsync(augmentedParser1, args, seedSession));
|
|
1604
1624
|
if (firstPassSeed == null) {
|
|
1605
1625
|
const fallbackParser = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1606
|
-
if (parser.mode === "async") return runParser(fallbackParser, programName, args,
|
|
1607
|
-
return Promise.resolve(runParser(fallbackParser, programName, args,
|
|
1626
|
+
if (parser.mode === "async") return runParser(fallbackParser, programName, args, sessionOptions);
|
|
1627
|
+
return Promise.resolve(runParser(fallbackParser, programName, args, sessionOptions));
|
|
1608
1628
|
}
|
|
1609
1629
|
const { annotations: finalAnnotations } = await collectFinalAnnotations(contexts, phase1Snapshots, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
|
|
1610
1630
|
const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
|
|
1611
|
-
if (parser.mode === "async") return runParser(augmentedParser2, programName, args,
|
|
1612
|
-
return Promise.resolve(runParser(augmentedParser2, programName, args,
|
|
1631
|
+
if (parser.mode === "async") return runParser(augmentedParser2, programName, args, sessionOptions);
|
|
1632
|
+
return Promise.resolve(runParser(augmentedParser2, programName, args, sessionOptions));
|
|
1613
1633
|
}
|
|
1614
1634
|
/**
|
|
1615
1635
|
* Runs a parser with multiple source contexts.
|
|
@@ -1715,14 +1735,22 @@ function runWithSyncBody(parser, programName, contexts, args, options) {
|
|
|
1715
1735
|
}
|
|
1716
1736
|
const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1717
1737
|
if (!needsTwoPhase) return runParser(augmentedParser1, programName, args, options);
|
|
1718
|
-
const
|
|
1738
|
+
const seedSession = require_internal_parser.createEffectfulCompletionSession("demand-only");
|
|
1739
|
+
const finalSession = {
|
|
1740
|
+
...seedSession,
|
|
1741
|
+
policy: "eager",
|
|
1742
|
+
effectfulSources: /* @__PURE__ */ new Set(),
|
|
1743
|
+
completedByPath: /* @__PURE__ */ new Map()
|
|
1744
|
+
};
|
|
1745
|
+
const sessionOptions = withEffectfulSessionOption(options, finalSession);
|
|
1746
|
+
const firstPassSeed = extractPhase2SeedSync(augmentedParser1, args, seedSession);
|
|
1719
1747
|
if (firstPassSeed == null) {
|
|
1720
1748
|
const fallbackParser = injectAnnotationsIntoParser(parser, phase1Annotations);
|
|
1721
|
-
return runParser(fallbackParser, programName, args,
|
|
1749
|
+
return runParser(fallbackParser, programName, args, sessionOptions);
|
|
1722
1750
|
}
|
|
1723
1751
|
const { annotations: finalAnnotations } = collectFinalAnnotationsSync(contexts, phase1Snapshots, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
|
|
1724
1752
|
const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
|
|
1725
|
-
return runParser(augmentedParser2, programName, args,
|
|
1753
|
+
return runParser(augmentedParser2, programName, args, sessionOptions);
|
|
1726
1754
|
}
|
|
1727
1755
|
/**
|
|
1728
1756
|
* Runs a synchronous parser with multiple source contexts.
|