@optique/core 0.10.0-dev.378 → 0.10.0

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.
@@ -666,8 +666,12 @@ async function* suggestObjectAsync(context, prefix, parserPairs) {
666
666
  * Recursively collects dependency values from DependencySourceState objects
667
667
  * found anywhere in the state tree.
668
668
  */
669
- function collectDependencies(state, registry) {
669
+ function collectDependencies(state, registry, visited = /* @__PURE__ */ new WeakSet()) {
670
670
  if (state === null || state === void 0) return;
671
+ if (typeof state === "object") {
672
+ if (visited.has(state)) return;
673
+ visited.add(state);
674
+ }
671
675
  if (require_dependency.isDependencySourceState(state)) {
672
676
  const depId = state[require_dependency.dependencyId];
673
677
  const result = state.result;
@@ -675,10 +679,10 @@ function collectDependencies(state, registry) {
675
679
  return;
676
680
  }
677
681
  if (Array.isArray(state)) {
678
- for (const item of state) collectDependencies(item, registry);
682
+ for (const item of state) collectDependencies(item, registry, visited);
679
683
  return;
680
684
  }
681
- if (typeof state === "object" && !require_dependency.isDeferredParseState(state)) for (const key of Reflect.ownKeys(state)) collectDependencies(state[key], registry);
685
+ if (typeof state === "object" && !require_dependency.isDeferredParseState(state)) for (const key of Reflect.ownKeys(state)) collectDependencies(state[key], registry, visited);
682
686
  }
683
687
  /**
684
688
  * Checks if a value is a plain object (created with `{}` or `Object.create(null)`).
@@ -727,8 +731,12 @@ function collectDependencyValues(deferredState, registry) {
727
731
  * Does NOT traverse class instances (e.g., Temporal.PlainDate, URL) since these
728
732
  * are user values that should be preserved as-is.
729
733
  */
730
- function resolveDeferred(state, registry) {
734
+ function resolveDeferred(state, registry, visited = /* @__PURE__ */ new WeakSet()) {
731
735
  if (state === null || state === void 0) return state;
736
+ if (typeof state === "object") {
737
+ if (visited.has(state)) return state;
738
+ visited.add(state);
739
+ }
732
740
  if (require_dependency.isDeferredParseState(state)) {
733
741
  const deferredState = state;
734
742
  const dependencyValue = collectDependencyValues(deferredState, registry);
@@ -738,10 +746,10 @@ function resolveDeferred(state, registry) {
738
746
  return reParseResult;
739
747
  }
740
748
  if (require_dependency.isDependencySourceState(state)) return state;
741
- if (Array.isArray(state)) return state.map((item) => resolveDeferred(item, registry));
749
+ if (Array.isArray(state)) return state.map((item) => resolveDeferred(item, registry, visited));
742
750
  if (isPlainObject(state)) {
743
751
  const resolved = {};
744
- for (const key of Reflect.ownKeys(state)) resolved[key] = resolveDeferred(state[key], registry);
752
+ for (const key of Reflect.ownKeys(state)) resolved[key] = resolveDeferred(state[key], registry, visited);
745
753
  return resolved;
746
754
  }
747
755
  return state;
@@ -764,8 +772,12 @@ function resolveDeferredParseStates(fieldStates) {
764
772
  * Does NOT traverse class instances (e.g., Temporal.PlainDate, URL) since these
765
773
  * are user values that should be preserved as-is.
766
774
  */
767
- async function resolveDeferredAsync(state, registry) {
775
+ async function resolveDeferredAsync(state, registry, visited = /* @__PURE__ */ new WeakSet()) {
768
776
  if (state === null || state === void 0) return state;
777
+ if (typeof state === "object") {
778
+ if (visited.has(state)) return state;
779
+ visited.add(state);
780
+ }
769
781
  if (require_dependency.isDeferredParseState(state)) {
770
782
  const deferredState = state;
771
783
  const dependencyValue = collectDependencyValues(deferredState, registry);
@@ -774,12 +786,12 @@ async function resolveDeferredAsync(state, registry) {
774
786
  return Promise.resolve(reParseResult);
775
787
  }
776
788
  if (require_dependency.isDependencySourceState(state)) return state;
777
- if (Array.isArray(state)) return Promise.all(state.map((item) => resolveDeferredAsync(item, registry)));
789
+ if (Array.isArray(state)) return Promise.all(state.map((item) => resolveDeferredAsync(item, registry, visited)));
778
790
  if (isPlainObject(state)) {
779
791
  const resolved = {};
780
792
  const keys = Reflect.ownKeys(state);
781
793
  await Promise.all(keys.map(async (key) => {
782
- resolved[key] = await resolveDeferredAsync(state[key], registry);
794
+ resolved[key] = await resolveDeferredAsync(state[key], registry, visited);
783
795
  }));
784
796
  return resolved;
785
797
  }
@@ -666,8 +666,12 @@ async function* suggestObjectAsync(context, prefix, parserPairs) {
666
666
  * Recursively collects dependency values from DependencySourceState objects
667
667
  * found anywhere in the state tree.
668
668
  */
669
- function collectDependencies(state, registry) {
669
+ function collectDependencies(state, registry, visited = /* @__PURE__ */ new WeakSet()) {
670
670
  if (state === null || state === void 0) return;
671
+ if (typeof state === "object") {
672
+ if (visited.has(state)) return;
673
+ visited.add(state);
674
+ }
671
675
  if (isDependencySourceState(state)) {
672
676
  const depId = state[dependencyId];
673
677
  const result = state.result;
@@ -675,10 +679,10 @@ function collectDependencies(state, registry) {
675
679
  return;
676
680
  }
677
681
  if (Array.isArray(state)) {
678
- for (const item of state) collectDependencies(item, registry);
682
+ for (const item of state) collectDependencies(item, registry, visited);
679
683
  return;
680
684
  }
681
- if (typeof state === "object" && !isDeferredParseState(state)) for (const key of Reflect.ownKeys(state)) collectDependencies(state[key], registry);
685
+ if (typeof state === "object" && !isDeferredParseState(state)) for (const key of Reflect.ownKeys(state)) collectDependencies(state[key], registry, visited);
682
686
  }
683
687
  /**
684
688
  * Checks if a value is a plain object (created with `{}` or `Object.create(null)`).
@@ -727,8 +731,12 @@ function collectDependencyValues(deferredState, registry) {
727
731
  * Does NOT traverse class instances (e.g., Temporal.PlainDate, URL) since these
728
732
  * are user values that should be preserved as-is.
729
733
  */
730
- function resolveDeferred(state, registry) {
734
+ function resolveDeferred(state, registry, visited = /* @__PURE__ */ new WeakSet()) {
731
735
  if (state === null || state === void 0) return state;
736
+ if (typeof state === "object") {
737
+ if (visited.has(state)) return state;
738
+ visited.add(state);
739
+ }
732
740
  if (isDeferredParseState(state)) {
733
741
  const deferredState = state;
734
742
  const dependencyValue = collectDependencyValues(deferredState, registry);
@@ -738,10 +746,10 @@ function resolveDeferred(state, registry) {
738
746
  return reParseResult;
739
747
  }
740
748
  if (isDependencySourceState(state)) return state;
741
- if (Array.isArray(state)) return state.map((item) => resolveDeferred(item, registry));
749
+ if (Array.isArray(state)) return state.map((item) => resolveDeferred(item, registry, visited));
742
750
  if (isPlainObject(state)) {
743
751
  const resolved = {};
744
- for (const key of Reflect.ownKeys(state)) resolved[key] = resolveDeferred(state[key], registry);
752
+ for (const key of Reflect.ownKeys(state)) resolved[key] = resolveDeferred(state[key], registry, visited);
745
753
  return resolved;
746
754
  }
747
755
  return state;
@@ -764,8 +772,12 @@ function resolveDeferredParseStates(fieldStates) {
764
772
  * Does NOT traverse class instances (e.g., Temporal.PlainDate, URL) since these
765
773
  * are user values that should be preserved as-is.
766
774
  */
767
- async function resolveDeferredAsync(state, registry) {
775
+ async function resolveDeferredAsync(state, registry, visited = /* @__PURE__ */ new WeakSet()) {
768
776
  if (state === null || state === void 0) return state;
777
+ if (typeof state === "object") {
778
+ if (visited.has(state)) return state;
779
+ visited.add(state);
780
+ }
769
781
  if (isDeferredParseState(state)) {
770
782
  const deferredState = state;
771
783
  const dependencyValue = collectDependencyValues(deferredState, registry);
@@ -774,12 +786,12 @@ async function resolveDeferredAsync(state, registry) {
774
786
  return Promise.resolve(reParseResult);
775
787
  }
776
788
  if (isDependencySourceState(state)) return state;
777
- if (Array.isArray(state)) return Promise.all(state.map((item) => resolveDeferredAsync(item, registry)));
789
+ if (Array.isArray(state)) return Promise.all(state.map((item) => resolveDeferredAsync(item, registry, visited)));
778
790
  if (isPlainObject(state)) {
779
791
  const resolved = {};
780
792
  const keys = Reflect.ownKeys(state);
781
793
  await Promise.all(keys.map(async (key) => {
782
- resolved[key] = await resolveDeferredAsync(state[key], registry);
794
+ resolved[key] = await resolveDeferredAsync(state[key], registry, visited);
783
795
  }));
784
796
  return resolved;
785
797
  }
@@ -199,6 +199,9 @@ function determineFactoryModeForDeriveFrom(options) {
199
199
  const parser = options.factory(...defaultValues$1);
200
200
  return parser.$mode === "async";
201
201
  }
202
+ function isAsyncModeParser(parser) {
203
+ return parser.$mode === "async";
204
+ }
202
205
  function createSyncDerivedFromParser(sourceId, options) {
203
206
  const alldependencyIds = options.dependencies.map((dep) => dep[dependencyId]);
204
207
  return {
@@ -211,6 +214,10 @@ function createSyncDerivedFromParser(sourceId, options) {
211
214
  parse(input) {
212
215
  const sourceValues = options.defaultValues();
213
216
  const derivedParser = options.factory(...sourceValues);
217
+ if (isAsyncModeParser(derivedParser)) return {
218
+ success: false,
219
+ error: require_message.message`Factory returned an async parser where a sync parser is required.`
220
+ };
214
221
  return derivedParser.parse(input);
215
222
  },
216
223
  [parseWithDependency](input, dependencyValue) {
@@ -224,6 +231,10 @@ function createSyncDerivedFromParser(sourceId, options) {
224
231
  error: require_message.message`Factory error: ${msg}`
225
232
  };
226
233
  }
234
+ if (isAsyncModeParser(derivedParser)) return {
235
+ success: false,
236
+ error: require_message.message`Factory returned an async parser where a sync parser is required.`
237
+ };
227
238
  return derivedParser.parse(input);
228
239
  },
229
240
  format(value) {
@@ -393,6 +404,10 @@ function createSyncDerivedParser(sourceId, options) {
393
404
  parse(input) {
394
405
  const sourceValue = options.defaultValue();
395
406
  const derivedParser = options.factory(sourceValue);
407
+ if (isAsyncModeParser(derivedParser)) return {
408
+ success: false,
409
+ error: require_message.message`Factory returned an async parser where a sync parser is required.`
410
+ };
396
411
  return derivedParser.parse(input);
397
412
  },
398
413
  [parseWithDependency](input, dependencyValue) {
@@ -406,6 +421,10 @@ function createSyncDerivedParser(sourceId, options) {
406
421
  error: require_message.message`Factory error: ${msg}`
407
422
  };
408
423
  }
424
+ if (isAsyncModeParser(derivedParser)) return {
425
+ success: false,
426
+ error: require_message.message`Factory returned an async parser where a sync parser is required.`
427
+ };
409
428
  return derivedParser.parse(input);
410
429
  },
411
430
  format(value) {
@@ -199,6 +199,9 @@ function determineFactoryModeForDeriveFrom(options) {
199
199
  const parser = options.factory(...defaultValues$1);
200
200
  return parser.$mode === "async";
201
201
  }
202
+ function isAsyncModeParser(parser) {
203
+ return parser.$mode === "async";
204
+ }
202
205
  function createSyncDerivedFromParser(sourceId, options) {
203
206
  const alldependencyIds = options.dependencies.map((dep) => dep[dependencyId]);
204
207
  return {
@@ -211,6 +214,10 @@ function createSyncDerivedFromParser(sourceId, options) {
211
214
  parse(input) {
212
215
  const sourceValues = options.defaultValues();
213
216
  const derivedParser = options.factory(...sourceValues);
217
+ if (isAsyncModeParser(derivedParser)) return {
218
+ success: false,
219
+ error: message`Factory returned an async parser where a sync parser is required.`
220
+ };
214
221
  return derivedParser.parse(input);
215
222
  },
216
223
  [parseWithDependency](input, dependencyValue) {
@@ -224,6 +231,10 @@ function createSyncDerivedFromParser(sourceId, options) {
224
231
  error: message`Factory error: ${msg}`
225
232
  };
226
233
  }
234
+ if (isAsyncModeParser(derivedParser)) return {
235
+ success: false,
236
+ error: message`Factory returned an async parser where a sync parser is required.`
237
+ };
227
238
  return derivedParser.parse(input);
228
239
  },
229
240
  format(value) {
@@ -393,6 +404,10 @@ function createSyncDerivedParser(sourceId, options) {
393
404
  parse(input) {
394
405
  const sourceValue = options.defaultValue();
395
406
  const derivedParser = options.factory(sourceValue);
407
+ if (isAsyncModeParser(derivedParser)) return {
408
+ success: false,
409
+ error: message`Factory returned an async parser where a sync parser is required.`
410
+ };
396
411
  return derivedParser.parse(input);
397
412
  },
398
413
  [parseWithDependency](input, dependencyValue) {
@@ -406,6 +421,10 @@ function createSyncDerivedParser(sourceId, options) {
406
421
  error: message`Factory error: ${msg}`
407
422
  };
408
423
  }
424
+ if (isAsyncModeParser(derivedParser)) return {
425
+ success: false,
426
+ error: message`Factory returned an async parser where a sync parser is required.`
427
+ };
409
428
  return derivedParser.parse(input);
410
429
  },
411
430
  format(value) {
package/dist/facade.cjs CHANGED
@@ -808,6 +808,31 @@ function mergeAnnotations(annotationsList) {
808
808
  return result;
809
809
  }
810
810
  /**
811
+ * Collects phase 1 annotations from all contexts and determines whether
812
+ * two-phase parsing is needed.
813
+ *
814
+ * @param contexts Source contexts to collect annotations from.
815
+ * @returns Promise with merged annotations and dynamic-context hint.
816
+ */
817
+ async function collectPhase1Annotations(contexts) {
818
+ const annotationsList = [];
819
+ let hasDynamic = false;
820
+ for (const context of contexts) {
821
+ const result = context.getAnnotations();
822
+ if (result instanceof Promise) {
823
+ hasDynamic = true;
824
+ annotationsList.push(await result);
825
+ } else {
826
+ if (Object.getOwnPropertySymbols(result).length === 0) hasDynamic = true;
827
+ annotationsList.push(result);
828
+ }
829
+ }
830
+ return {
831
+ annotations: mergeAnnotations(annotationsList),
832
+ hasDynamic
833
+ };
834
+ }
835
+ /**
811
836
  * Collects annotations from all contexts.
812
837
  *
813
838
  * @param contexts Source contexts to collect annotations from.
@@ -818,47 +843,48 @@ async function collectAnnotations(contexts, parsed) {
818
843
  const annotationsList = [];
819
844
  for (const context of contexts) {
820
845
  const result = context.getAnnotations(parsed);
821
- if (result instanceof Promise) annotationsList.push(await result);
822
- else annotationsList.push(result);
846
+ annotationsList.push(result instanceof Promise ? await result : result);
823
847
  }
824
848
  return mergeAnnotations(annotationsList);
825
849
  }
826
850
  /**
827
- * Collects annotations from all contexts synchronously.
851
+ * Collects phase 1 annotations from all contexts synchronously and determines
852
+ * whether two-phase parsing is needed.
828
853
  *
829
854
  * @param contexts Source contexts to collect annotations from.
830
- * @param parsed Optional parsed result from a previous parse pass.
831
- * @returns Merged annotations.
855
+ * @returns Merged annotations with dynamic-context hint.
832
856
  * @throws Error if any context returns a Promise.
833
857
  */
834
- function collectAnnotationsSync(contexts, parsed) {
858
+ function collectPhase1AnnotationsSync(contexts) {
835
859
  const annotationsList = [];
860
+ let hasDynamic = false;
836
861
  for (const context of contexts) {
837
- const result = context.getAnnotations(parsed);
862
+ const result = context.getAnnotations();
838
863
  if (result instanceof Promise) throw new Error(`Context ${String(context.id)} returned a Promise in sync mode. Use runWith() or runWithAsync() for async contexts.`);
864
+ if (Object.getOwnPropertySymbols(result).length === 0) hasDynamic = true;
839
865
  annotationsList.push(result);
840
866
  }
841
- return mergeAnnotations(annotationsList);
867
+ return {
868
+ annotations: mergeAnnotations(annotationsList),
869
+ hasDynamic
870
+ };
842
871
  }
843
872
  /**
844
- * Checks if any context has dynamic behavior (returns different annotations
845
- * when called with parsed results).
846
- *
847
- * A context is considered dynamic if:
848
- * - It returns a Promise
849
- * - It returns empty annotations without parsed results but may return
850
- * non-empty annotations with parsed results
873
+ * Collects annotations from all contexts synchronously.
851
874
  *
852
- * @param contexts Source contexts to check.
853
- * @returns `true` if any context appears to be dynamic.
875
+ * @param contexts Source contexts to collect annotations from.
876
+ * @param parsed Optional parsed result from a previous parse pass.
877
+ * @returns Merged annotations.
878
+ * @throws Error if any context returns a Promise.
854
879
  */
855
- function hasDynamicContexts(contexts) {
880
+ function collectAnnotationsSync(contexts, parsed) {
881
+ const annotationsList = [];
856
882
  for (const context of contexts) {
857
- const result = context.getAnnotations();
858
- if (result instanceof Promise) return true;
859
- if (Object.getOwnPropertySymbols(result).length === 0) return true;
883
+ const result = context.getAnnotations(parsed);
884
+ if (result instanceof Promise) throw new Error(`Context ${String(context.id)} returned a Promise in sync mode. Use runWith() or runWithAsync() for async contexts.`);
885
+ annotationsList.push(result);
860
886
  }
861
- return false;
887
+ return mergeAnnotations(annotationsList);
862
888
  }
863
889
  /**
864
890
  * Runs a parser with multiple source contexts.
@@ -918,8 +944,7 @@ async function runWith(parser, programName, contexts, options) {
918
944
  if (parser.$mode === "async") return runParser(parser, programName, args, options);
919
945
  return Promise.resolve(runParser(parser, programName, args, options));
920
946
  }
921
- const phase1Annotations = await collectAnnotations(contexts);
922
- const needsTwoPhase = hasDynamicContexts(contexts);
947
+ const { annotations: phase1Annotations, hasDynamic: needsTwoPhase } = await collectPhase1Annotations(contexts);
923
948
  if (!needsTwoPhase) {
924
949
  const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
925
950
  if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
@@ -971,8 +996,7 @@ function runWithSync(parser, programName, contexts, options) {
971
996
  const args = options?.args ?? [];
972
997
  if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
973
998
  if (contexts.length === 0) return runParser(parser, programName, args, options);
974
- const phase1Annotations = collectAnnotationsSync(contexts);
975
- const needsTwoPhase = hasDynamicContexts(contexts);
999
+ const { annotations: phase1Annotations, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts);
976
1000
  if (!needsTwoPhase) {
977
1001
  const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
978
1002
  return runParser(augmentedParser, programName, args, options);
package/dist/facade.js CHANGED
@@ -808,6 +808,31 @@ function mergeAnnotations(annotationsList) {
808
808
  return result;
809
809
  }
810
810
  /**
811
+ * Collects phase 1 annotations from all contexts and determines whether
812
+ * two-phase parsing is needed.
813
+ *
814
+ * @param contexts Source contexts to collect annotations from.
815
+ * @returns Promise with merged annotations and dynamic-context hint.
816
+ */
817
+ async function collectPhase1Annotations(contexts) {
818
+ const annotationsList = [];
819
+ let hasDynamic = false;
820
+ for (const context of contexts) {
821
+ const result = context.getAnnotations();
822
+ if (result instanceof Promise) {
823
+ hasDynamic = true;
824
+ annotationsList.push(await result);
825
+ } else {
826
+ if (Object.getOwnPropertySymbols(result).length === 0) hasDynamic = true;
827
+ annotationsList.push(result);
828
+ }
829
+ }
830
+ return {
831
+ annotations: mergeAnnotations(annotationsList),
832
+ hasDynamic
833
+ };
834
+ }
835
+ /**
811
836
  * Collects annotations from all contexts.
812
837
  *
813
838
  * @param contexts Source contexts to collect annotations from.
@@ -818,47 +843,48 @@ async function collectAnnotations(contexts, parsed) {
818
843
  const annotationsList = [];
819
844
  for (const context of contexts) {
820
845
  const result = context.getAnnotations(parsed);
821
- if (result instanceof Promise) annotationsList.push(await result);
822
- else annotationsList.push(result);
846
+ annotationsList.push(result instanceof Promise ? await result : result);
823
847
  }
824
848
  return mergeAnnotations(annotationsList);
825
849
  }
826
850
  /**
827
- * Collects annotations from all contexts synchronously.
851
+ * Collects phase 1 annotations from all contexts synchronously and determines
852
+ * whether two-phase parsing is needed.
828
853
  *
829
854
  * @param contexts Source contexts to collect annotations from.
830
- * @param parsed Optional parsed result from a previous parse pass.
831
- * @returns Merged annotations.
855
+ * @returns Merged annotations with dynamic-context hint.
832
856
  * @throws Error if any context returns a Promise.
833
857
  */
834
- function collectAnnotationsSync(contexts, parsed) {
858
+ function collectPhase1AnnotationsSync(contexts) {
835
859
  const annotationsList = [];
860
+ let hasDynamic = false;
836
861
  for (const context of contexts) {
837
- const result = context.getAnnotations(parsed);
862
+ const result = context.getAnnotations();
838
863
  if (result instanceof Promise) throw new Error(`Context ${String(context.id)} returned a Promise in sync mode. Use runWith() or runWithAsync() for async contexts.`);
864
+ if (Object.getOwnPropertySymbols(result).length === 0) hasDynamic = true;
839
865
  annotationsList.push(result);
840
866
  }
841
- return mergeAnnotations(annotationsList);
867
+ return {
868
+ annotations: mergeAnnotations(annotationsList),
869
+ hasDynamic
870
+ };
842
871
  }
843
872
  /**
844
- * Checks if any context has dynamic behavior (returns different annotations
845
- * when called with parsed results).
846
- *
847
- * A context is considered dynamic if:
848
- * - It returns a Promise
849
- * - It returns empty annotations without parsed results but may return
850
- * non-empty annotations with parsed results
873
+ * Collects annotations from all contexts synchronously.
851
874
  *
852
- * @param contexts Source contexts to check.
853
- * @returns `true` if any context appears to be dynamic.
875
+ * @param contexts Source contexts to collect annotations from.
876
+ * @param parsed Optional parsed result from a previous parse pass.
877
+ * @returns Merged annotations.
878
+ * @throws Error if any context returns a Promise.
854
879
  */
855
- function hasDynamicContexts(contexts) {
880
+ function collectAnnotationsSync(contexts, parsed) {
881
+ const annotationsList = [];
856
882
  for (const context of contexts) {
857
- const result = context.getAnnotations();
858
- if (result instanceof Promise) return true;
859
- if (Object.getOwnPropertySymbols(result).length === 0) return true;
883
+ const result = context.getAnnotations(parsed);
884
+ if (result instanceof Promise) throw new Error(`Context ${String(context.id)} returned a Promise in sync mode. Use runWith() or runWithAsync() for async contexts.`);
885
+ annotationsList.push(result);
860
886
  }
861
- return false;
887
+ return mergeAnnotations(annotationsList);
862
888
  }
863
889
  /**
864
890
  * Runs a parser with multiple source contexts.
@@ -918,8 +944,7 @@ async function runWith(parser, programName, contexts, options) {
918
944
  if (parser.$mode === "async") return runParser(parser, programName, args, options);
919
945
  return Promise.resolve(runParser(parser, programName, args, options));
920
946
  }
921
- const phase1Annotations = await collectAnnotations(contexts);
922
- const needsTwoPhase = hasDynamicContexts(contexts);
947
+ const { annotations: phase1Annotations, hasDynamic: needsTwoPhase } = await collectPhase1Annotations(contexts);
923
948
  if (!needsTwoPhase) {
924
949
  const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
925
950
  if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
@@ -971,8 +996,7 @@ function runWithSync(parser, programName, contexts, options) {
971
996
  const args = options?.args ?? [];
972
997
  if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
973
998
  if (contexts.length === 0) return runParser(parser, programName, args, options);
974
- const phase1Annotations = collectAnnotationsSync(contexts);
975
- const needsTwoPhase = hasDynamicContexts(contexts);
999
+ const { annotations: phase1Annotations, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts);
976
1000
  if (!needsTwoPhase) {
977
1001
  const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
978
1002
  return runParser(augmentedParser, programName, args, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.10.0-dev.378+995d618d",
3
+ "version": "0.10.0",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",