@optique/core 1.0.0-dev.1899 → 1.0.0-dev.1903

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.
@@ -87,33 +87,36 @@ function normalizeDelegatedAnnotationState(state) {
87
87
  function hasDelegatedAnnotationCarrier(state) {
88
88
  return state != null && typeof state === "object" && (require_annotations.isInjectedAnnotationWrapper(state) || annotationViewTargets.has(state));
89
89
  }
90
- function isPendingNestedNormalizationAlias(originalValue, normalizedValue, seen) {
91
- if (originalValue == null || typeof originalValue !== "object") return false;
90
+ function getPendingNestedNormalizationEntry(originalValue, normalizedValue, seen) {
91
+ if (originalValue == null || typeof originalValue !== "object") return void 0;
92
92
  const entry = seen.get(originalValue);
93
- return entry != null && !entry.finalized && entry.clone === normalizedValue;
93
+ return entry != null && entry.clone === normalizedValue && (!entry.finalized || entry.preferCloneOnRead) ? entry : void 0;
94
94
  }
95
95
  function createPendingNestedNormalizationClone(source) {
96
96
  return Array.isArray(source) ? [] : Object.create(Object.getPrototypeOf(source));
97
97
  }
98
- function normalizeNestedDelegatedStructuredState(source, seen) {
98
+ function normalizeNestedDelegatedStructuredState(source, seen, preferPendingClone) {
99
99
  const clone = createPendingNestedNormalizationClone(source);
100
100
  const entry = {
101
101
  clone,
102
102
  finalized: false,
103
- result: clone
103
+ result: clone,
104
+ preferCloneOnRead: false
104
105
  };
105
106
  seen.set(source, entry);
106
107
  const overrides = /* @__PURE__ */ new Map();
107
108
  let changed = false;
109
+ let hasPendingAliasOverride = false;
108
110
  for (const key of Reflect.ownKeys(source)) {
109
111
  const descriptor = Object.getOwnPropertyDescriptor(source, key);
110
112
  if (descriptor == null || !("value" in descriptor)) continue;
111
- const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen);
113
+ const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen, true);
112
114
  if (nextValue === descriptor.value) continue;
113
115
  overrides.set(key, nextValue);
114
- if (!isPendingNestedNormalizationAlias(descriptor.value, nextValue, seen)) changed = true;
116
+ if (getPendingNestedNormalizationEntry(descriptor.value, nextValue, seen) == null) changed = true;
117
+ else hasPendingAliasOverride = true;
115
118
  }
116
- if (!changed) {
119
+ if (!changed && !hasPendingAliasOverride) {
117
120
  entry.finalized = true;
118
121
  entry.result = source;
119
122
  return source;
@@ -129,17 +132,120 @@ function normalizeNestedDelegatedStructuredState(source, seen) {
129
132
  }
130
133
  Object.defineProperties(clone, descriptors);
131
134
  entry.finalized = true;
132
- entry.result = clone;
133
- return clone;
135
+ entry.preferCloneOnRead = !changed && hasPendingAliasOverride;
136
+ entry.result = changed ? clone : source;
137
+ return changed || preferPendingClone ? clone : source;
138
+ }
139
+ function normalizeNestedDelegatedMapState(source, seen, preferPendingClone) {
140
+ const clone = /* @__PURE__ */ new Map();
141
+ const entry = {
142
+ clone,
143
+ finalized: false,
144
+ result: clone,
145
+ preferCloneOnRead: false
146
+ };
147
+ seen.set(source, entry);
148
+ const normalizedEntries = [];
149
+ const overrides = /* @__PURE__ */ new Map();
150
+ let changed = false;
151
+ let hasPendingAliasOverride = false;
152
+ for (const [key, value] of source) {
153
+ const nextKey = normalizeNestedDelegatedAnnotationState(key, seen, true);
154
+ const nextValue = normalizeNestedDelegatedAnnotationState(value, seen, true);
155
+ normalizedEntries.push([nextKey, nextValue]);
156
+ if (nextKey !== key) if (getPendingNestedNormalizationEntry(key, nextKey, seen) == null) changed = true;
157
+ else hasPendingAliasOverride = true;
158
+ if (nextValue !== value) if (getPendingNestedNormalizationEntry(value, nextValue, seen) == null) changed = true;
159
+ else hasPendingAliasOverride = true;
160
+ }
161
+ for (const key of Reflect.ownKeys(source)) {
162
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
163
+ if (descriptor == null || !("value" in descriptor)) continue;
164
+ const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen, true);
165
+ if (nextValue === descriptor.value) continue;
166
+ overrides.set(key, nextValue);
167
+ if (getPendingNestedNormalizationEntry(descriptor.value, nextValue, seen) == null) changed = true;
168
+ else hasPendingAliasOverride = true;
169
+ }
170
+ if (!changed && !hasPendingAliasOverride) {
171
+ entry.finalized = true;
172
+ entry.result = source;
173
+ return source;
174
+ }
175
+ for (const [key, value] of normalizedEntries) clone.set(key, value);
176
+ const descriptors = Object.getOwnPropertyDescriptors(source);
177
+ for (const [key, nextValue] of overrides) {
178
+ const descriptor = descriptors[key];
179
+ if (descriptor == null || !("value" in descriptor)) continue;
180
+ descriptors[key] = {
181
+ ...descriptor,
182
+ value: nextValue
183
+ };
184
+ }
185
+ Object.defineProperties(clone, descriptors);
186
+ entry.finalized = true;
187
+ entry.preferCloneOnRead = !changed && hasPendingAliasOverride;
188
+ entry.result = changed ? clone : source;
189
+ return changed || preferPendingClone ? clone : source;
190
+ }
191
+ function normalizeNestedDelegatedSetState(source, seen, preferPendingClone) {
192
+ const clone = /* @__PURE__ */ new Set();
193
+ const entry = {
194
+ clone,
195
+ finalized: false,
196
+ result: clone,
197
+ preferCloneOnRead: false
198
+ };
199
+ seen.set(source, entry);
200
+ const normalizedValues = [];
201
+ const overrides = /* @__PURE__ */ new Map();
202
+ let changed = false;
203
+ let hasPendingAliasOverride = false;
204
+ for (const value of source) {
205
+ const nextValue = normalizeNestedDelegatedAnnotationState(value, seen, true);
206
+ normalizedValues.push(nextValue);
207
+ if (nextValue === value) continue;
208
+ if (getPendingNestedNormalizationEntry(value, nextValue, seen) == null) changed = true;
209
+ else hasPendingAliasOverride = true;
210
+ }
211
+ for (const key of Reflect.ownKeys(source)) {
212
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
213
+ if (descriptor == null || !("value" in descriptor)) continue;
214
+ const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen, true);
215
+ if (nextValue === descriptor.value) continue;
216
+ overrides.set(key, nextValue);
217
+ if (getPendingNestedNormalizationEntry(descriptor.value, nextValue, seen) == null) changed = true;
218
+ else hasPendingAliasOverride = true;
219
+ }
220
+ if (!changed && !hasPendingAliasOverride) {
221
+ entry.finalized = true;
222
+ entry.result = source;
223
+ return source;
224
+ }
225
+ for (const value of normalizedValues) clone.add(value);
226
+ const descriptors = Object.getOwnPropertyDescriptors(source);
227
+ for (const [key, nextValue] of overrides) {
228
+ const descriptor = descriptors[key];
229
+ if (descriptor == null || !("value" in descriptor)) continue;
230
+ descriptors[key] = {
231
+ ...descriptor,
232
+ value: nextValue
233
+ };
234
+ }
235
+ Object.defineProperties(clone, descriptors);
236
+ entry.finalized = true;
237
+ entry.preferCloneOnRead = !changed && hasPendingAliasOverride;
238
+ entry.result = changed ? clone : source;
239
+ return changed || preferPendingClone ? clone : source;
134
240
  }
135
241
  /**
136
- * Recursively removes delegated annotation carriers from plain-object and array
137
- * structures.
242
+ * Recursively removes delegated annotation carriers from plain-object, array,
243
+ * and built-in collection structures.
138
244
  *
139
- * Nested plain objects and arrays are shallow-cloned only when a delegated
140
- * carrier is found below them. Non-plain objects are unwrapped at the top
141
- * level and then preserved as-is to avoid mutating or reconstructing class
142
- * instances.
245
+ * Nested plain objects, arrays, Maps, and Sets are shallow-cloned only when a
246
+ * delegated carrier is found below them. Other non-plain objects are unwrapped
247
+ * at the top level and then preserved as-is to avoid mutating or reconstructing
248
+ * class instances.
143
249
  *
144
250
  * @param value The candidate value to normalize.
145
251
  * @param seen Tracks already-normalized objects so cyclic values keep their
@@ -148,16 +254,21 @@ function normalizeNestedDelegatedStructuredState(source, seen) {
148
254
  * normalized clone with delegated carriers removed.
149
255
  * @internal
150
256
  */
151
- function normalizeNestedDelegatedAnnotationState(value, seen = /* @__PURE__ */ new WeakMap()) {
257
+ function normalizeNestedDelegatedAnnotationState(value, seen = /* @__PURE__ */ new WeakMap(), preferPendingClone = false) {
152
258
  const normalized = normalizeDelegatedAnnotationState(value);
153
259
  if (normalized == null || typeof normalized !== "object") return normalized;
154
260
  const source = normalized;
155
261
  const existing = seen.get(source);
156
- if (existing != null) return existing.finalized ? existing.result : existing.clone;
157
- if (Array.isArray(source)) return normalizeNestedDelegatedStructuredState(source, seen);
262
+ if (existing != null) {
263
+ if (!existing.finalized) return existing.clone;
264
+ return preferPendingClone && existing.preferCloneOnRead ? existing.clone : existing.result;
265
+ }
266
+ if (Array.isArray(source)) return normalizeNestedDelegatedStructuredState(source, seen, preferPendingClone);
267
+ if (source instanceof Map) return normalizeNestedDelegatedMapState(source, seen, preferPendingClone);
268
+ if (source instanceof Set) return normalizeNestedDelegatedSetState(source, seen, preferPendingClone);
158
269
  const proto = Object.getPrototypeOf(source);
159
270
  if (proto !== Object.prototype && proto !== null) return normalized;
160
- return normalizeNestedDelegatedStructuredState(source, seen);
271
+ return normalizeNestedDelegatedStructuredState(source, seen, preferPendingClone);
161
272
  }
162
273
  /**
163
274
  * Creates a short-lived delegated state that exposes the parent's annotations
@@ -87,33 +87,36 @@ function normalizeDelegatedAnnotationState(state) {
87
87
  function hasDelegatedAnnotationCarrier(state) {
88
88
  return state != null && typeof state === "object" && (isInjectedAnnotationWrapper(state) || annotationViewTargets.has(state));
89
89
  }
90
- function isPendingNestedNormalizationAlias(originalValue, normalizedValue, seen) {
91
- if (originalValue == null || typeof originalValue !== "object") return false;
90
+ function getPendingNestedNormalizationEntry(originalValue, normalizedValue, seen) {
91
+ if (originalValue == null || typeof originalValue !== "object") return void 0;
92
92
  const entry = seen.get(originalValue);
93
- return entry != null && !entry.finalized && entry.clone === normalizedValue;
93
+ return entry != null && entry.clone === normalizedValue && (!entry.finalized || entry.preferCloneOnRead) ? entry : void 0;
94
94
  }
95
95
  function createPendingNestedNormalizationClone(source) {
96
96
  return Array.isArray(source) ? [] : Object.create(Object.getPrototypeOf(source));
97
97
  }
98
- function normalizeNestedDelegatedStructuredState(source, seen) {
98
+ function normalizeNestedDelegatedStructuredState(source, seen, preferPendingClone) {
99
99
  const clone = createPendingNestedNormalizationClone(source);
100
100
  const entry = {
101
101
  clone,
102
102
  finalized: false,
103
- result: clone
103
+ result: clone,
104
+ preferCloneOnRead: false
104
105
  };
105
106
  seen.set(source, entry);
106
107
  const overrides = /* @__PURE__ */ new Map();
107
108
  let changed = false;
109
+ let hasPendingAliasOverride = false;
108
110
  for (const key of Reflect.ownKeys(source)) {
109
111
  const descriptor = Object.getOwnPropertyDescriptor(source, key);
110
112
  if (descriptor == null || !("value" in descriptor)) continue;
111
- const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen);
113
+ const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen, true);
112
114
  if (nextValue === descriptor.value) continue;
113
115
  overrides.set(key, nextValue);
114
- if (!isPendingNestedNormalizationAlias(descriptor.value, nextValue, seen)) changed = true;
116
+ if (getPendingNestedNormalizationEntry(descriptor.value, nextValue, seen) == null) changed = true;
117
+ else hasPendingAliasOverride = true;
115
118
  }
116
- if (!changed) {
119
+ if (!changed && !hasPendingAliasOverride) {
117
120
  entry.finalized = true;
118
121
  entry.result = source;
119
122
  return source;
@@ -129,17 +132,120 @@ function normalizeNestedDelegatedStructuredState(source, seen) {
129
132
  }
130
133
  Object.defineProperties(clone, descriptors);
131
134
  entry.finalized = true;
132
- entry.result = clone;
133
- return clone;
135
+ entry.preferCloneOnRead = !changed && hasPendingAliasOverride;
136
+ entry.result = changed ? clone : source;
137
+ return changed || preferPendingClone ? clone : source;
138
+ }
139
+ function normalizeNestedDelegatedMapState(source, seen, preferPendingClone) {
140
+ const clone = /* @__PURE__ */ new Map();
141
+ const entry = {
142
+ clone,
143
+ finalized: false,
144
+ result: clone,
145
+ preferCloneOnRead: false
146
+ };
147
+ seen.set(source, entry);
148
+ const normalizedEntries = [];
149
+ const overrides = /* @__PURE__ */ new Map();
150
+ let changed = false;
151
+ let hasPendingAliasOverride = false;
152
+ for (const [key, value] of source) {
153
+ const nextKey = normalizeNestedDelegatedAnnotationState(key, seen, true);
154
+ const nextValue = normalizeNestedDelegatedAnnotationState(value, seen, true);
155
+ normalizedEntries.push([nextKey, nextValue]);
156
+ if (nextKey !== key) if (getPendingNestedNormalizationEntry(key, nextKey, seen) == null) changed = true;
157
+ else hasPendingAliasOverride = true;
158
+ if (nextValue !== value) if (getPendingNestedNormalizationEntry(value, nextValue, seen) == null) changed = true;
159
+ else hasPendingAliasOverride = true;
160
+ }
161
+ for (const key of Reflect.ownKeys(source)) {
162
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
163
+ if (descriptor == null || !("value" in descriptor)) continue;
164
+ const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen, true);
165
+ if (nextValue === descriptor.value) continue;
166
+ overrides.set(key, nextValue);
167
+ if (getPendingNestedNormalizationEntry(descriptor.value, nextValue, seen) == null) changed = true;
168
+ else hasPendingAliasOverride = true;
169
+ }
170
+ if (!changed && !hasPendingAliasOverride) {
171
+ entry.finalized = true;
172
+ entry.result = source;
173
+ return source;
174
+ }
175
+ for (const [key, value] of normalizedEntries) clone.set(key, value);
176
+ const descriptors = Object.getOwnPropertyDescriptors(source);
177
+ for (const [key, nextValue] of overrides) {
178
+ const descriptor = descriptors[key];
179
+ if (descriptor == null || !("value" in descriptor)) continue;
180
+ descriptors[key] = {
181
+ ...descriptor,
182
+ value: nextValue
183
+ };
184
+ }
185
+ Object.defineProperties(clone, descriptors);
186
+ entry.finalized = true;
187
+ entry.preferCloneOnRead = !changed && hasPendingAliasOverride;
188
+ entry.result = changed ? clone : source;
189
+ return changed || preferPendingClone ? clone : source;
190
+ }
191
+ function normalizeNestedDelegatedSetState(source, seen, preferPendingClone) {
192
+ const clone = /* @__PURE__ */ new Set();
193
+ const entry = {
194
+ clone,
195
+ finalized: false,
196
+ result: clone,
197
+ preferCloneOnRead: false
198
+ };
199
+ seen.set(source, entry);
200
+ const normalizedValues = [];
201
+ const overrides = /* @__PURE__ */ new Map();
202
+ let changed = false;
203
+ let hasPendingAliasOverride = false;
204
+ for (const value of source) {
205
+ const nextValue = normalizeNestedDelegatedAnnotationState(value, seen, true);
206
+ normalizedValues.push(nextValue);
207
+ if (nextValue === value) continue;
208
+ if (getPendingNestedNormalizationEntry(value, nextValue, seen) == null) changed = true;
209
+ else hasPendingAliasOverride = true;
210
+ }
211
+ for (const key of Reflect.ownKeys(source)) {
212
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
213
+ if (descriptor == null || !("value" in descriptor)) continue;
214
+ const nextValue = normalizeNestedDelegatedAnnotationState(descriptor.value, seen, true);
215
+ if (nextValue === descriptor.value) continue;
216
+ overrides.set(key, nextValue);
217
+ if (getPendingNestedNormalizationEntry(descriptor.value, nextValue, seen) == null) changed = true;
218
+ else hasPendingAliasOverride = true;
219
+ }
220
+ if (!changed && !hasPendingAliasOverride) {
221
+ entry.finalized = true;
222
+ entry.result = source;
223
+ return source;
224
+ }
225
+ for (const value of normalizedValues) clone.add(value);
226
+ const descriptors = Object.getOwnPropertyDescriptors(source);
227
+ for (const [key, nextValue] of overrides) {
228
+ const descriptor = descriptors[key];
229
+ if (descriptor == null || !("value" in descriptor)) continue;
230
+ descriptors[key] = {
231
+ ...descriptor,
232
+ value: nextValue
233
+ };
234
+ }
235
+ Object.defineProperties(clone, descriptors);
236
+ entry.finalized = true;
237
+ entry.preferCloneOnRead = !changed && hasPendingAliasOverride;
238
+ entry.result = changed ? clone : source;
239
+ return changed || preferPendingClone ? clone : source;
134
240
  }
135
241
  /**
136
- * Recursively removes delegated annotation carriers from plain-object and array
137
- * structures.
242
+ * Recursively removes delegated annotation carriers from plain-object, array,
243
+ * and built-in collection structures.
138
244
  *
139
- * Nested plain objects and arrays are shallow-cloned only when a delegated
140
- * carrier is found below them. Non-plain objects are unwrapped at the top
141
- * level and then preserved as-is to avoid mutating or reconstructing class
142
- * instances.
245
+ * Nested plain objects, arrays, Maps, and Sets are shallow-cloned only when a
246
+ * delegated carrier is found below them. Other non-plain objects are unwrapped
247
+ * at the top level and then preserved as-is to avoid mutating or reconstructing
248
+ * class instances.
143
249
  *
144
250
  * @param value The candidate value to normalize.
145
251
  * @param seen Tracks already-normalized objects so cyclic values keep their
@@ -148,16 +254,21 @@ function normalizeNestedDelegatedStructuredState(source, seen) {
148
254
  * normalized clone with delegated carriers removed.
149
255
  * @internal
150
256
  */
151
- function normalizeNestedDelegatedAnnotationState(value, seen = /* @__PURE__ */ new WeakMap()) {
257
+ function normalizeNestedDelegatedAnnotationState(value, seen = /* @__PURE__ */ new WeakMap(), preferPendingClone = false) {
152
258
  const normalized = normalizeDelegatedAnnotationState(value);
153
259
  if (normalized == null || typeof normalized !== "object") return normalized;
154
260
  const source = normalized;
155
261
  const existing = seen.get(source);
156
- if (existing != null) return existing.finalized ? existing.result : existing.clone;
157
- if (Array.isArray(source)) return normalizeNestedDelegatedStructuredState(source, seen);
262
+ if (existing != null) {
263
+ if (!existing.finalized) return existing.clone;
264
+ return preferPendingClone && existing.preferCloneOnRead ? existing.clone : existing.result;
265
+ }
266
+ if (Array.isArray(source)) return normalizeNestedDelegatedStructuredState(source, seen, preferPendingClone);
267
+ if (source instanceof Map) return normalizeNestedDelegatedMapState(source, seen, preferPendingClone);
268
+ if (source instanceof Set) return normalizeNestedDelegatedSetState(source, seen, preferPendingClone);
158
269
  const proto = Object.getPrototypeOf(source);
159
270
  if (proto !== Object.prototype && proto !== null) return normalized;
160
- return normalizeNestedDelegatedStructuredState(source, seen);
271
+ return normalizeNestedDelegatedStructuredState(source, seen, preferPendingClone);
161
272
  }
162
273
  /**
163
274
  * Creates a short-lived delegated state that exposes the parent's annotations
@@ -82,85 +82,102 @@ function unwrapMultipleItemState(state) {
82
82
  function isPromiseLike(value) {
83
83
  return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
84
84
  }
85
- function normalizeOptionalLikeCompleteResult(result) {
86
- return result.success ? {
85
+ function normalizeOptionalLikeCompleteResult(result, shouldNormalize) {
86
+ return result.success && shouldNormalize ? {
87
87
  ...result,
88
88
  value: require_annotation_state.normalizeNestedDelegatedAnnotationState(result.value)
89
89
  } : result;
90
90
  }
91
91
  function completeOptionalLikeSync(parser, state, exec) {
92
92
  const hasCarrier = require_annotation_state.hasDelegatedAnnotationCarrier(state);
93
+ const shouldRetryFalseResult = hasCarrier && require_annotation_state.isAnnotationWrappedInitialState(state);
94
+ const run = (candidate, shouldNormalize) => normalizeOptionalLikeCompleteResult(parser.complete(candidate, exec), shouldNormalize);
93
95
  try {
94
- return normalizeOptionalLikeCompleteResult(parser.complete(state, exec));
96
+ const result = run(state, hasCarrier);
97
+ if (!result.success && shouldRetryFalseResult) return run(require_annotation_state.normalizeDelegatedAnnotationState(state), false);
98
+ return result;
95
99
  } catch (error) {
96
100
  if (!hasCarrier) throw error;
97
- const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(state);
98
- return normalizeOptionalLikeCompleteResult(parser.complete(fallbackState, exec));
101
+ return run(require_annotation_state.normalizeDelegatedAnnotationState(state), false);
99
102
  }
100
103
  }
101
104
  async function completeOptionalLikeAsync(parser, state, exec) {
102
105
  const hasCarrier = require_annotation_state.hasDelegatedAnnotationCarrier(state);
106
+ const shouldRetryFalseResult = hasCarrier && require_annotation_state.isAnnotationWrappedInitialState(state);
107
+ const run = async (candidate, shouldNormalize) => normalizeOptionalLikeCompleteResult(await parser.complete(candidate, exec), shouldNormalize);
103
108
  try {
104
- return normalizeOptionalLikeCompleteResult(await parser.complete(state, exec));
109
+ const result = await run(state, hasCarrier);
110
+ if (!result.success && shouldRetryFalseResult) return await run(require_annotation_state.normalizeDelegatedAnnotationState(state), false);
111
+ return result;
105
112
  } catch (error) {
106
113
  if (!hasCarrier) throw error;
107
- const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(state);
108
- return normalizeOptionalLikeCompleteResult(await parser.complete(fallbackState, exec));
114
+ return await run(require_annotation_state.normalizeDelegatedAnnotationState(state), false);
109
115
  }
110
116
  }
111
- function normalizeOptionalLikePhase2Seed(seed) {
112
- return seed == null ? null : {
117
+ function normalizeOptionalLikePhase2Seed(seed, shouldNormalize) {
118
+ return seed == null ? null : shouldNormalize ? {
113
119
  ...seed,
114
120
  value: require_annotation_state.normalizeNestedDelegatedAnnotationState(seed.value)
115
- };
121
+ } : seed;
116
122
  }
117
123
  function extractOptionalLikePhase2Seed(parser, state, exec) {
118
124
  if (!Array.isArray(state) && !(state != null && typeof state === "object")) return require_mode_dispatch.wrapForMode(parser.$mode, null);
119
125
  const innerState = normalizeOptionalLikeInnerState(state, parser.initialState, parser);
120
126
  const hasCarrier = require_annotation_state.hasDelegatedAnnotationCarrier(innerState);
127
+ const shouldRetryFalseResult = hasCarrier && require_annotation_state.isAnnotationWrappedInitialState(innerState);
121
128
  return require_mode_dispatch.dispatchByMode(parser.$mode, () => {
122
129
  try {
123
130
  const result = parser.complete(innerState, exec);
124
- if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result));
131
+ if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result), hasCarrier);
132
+ if (shouldRetryFalseResult) {
133
+ const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(innerState);
134
+ const fallbackResult = parser.complete(fallbackState, exec);
135
+ if (fallbackResult.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(fallbackResult), false);
136
+ }
125
137
  const seed = require_phase2_seed.extractPhase2Seed(parser, innerState, exec);
126
138
  if (seed == null && hasCarrier) {
127
139
  const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(innerState);
128
- return normalizeOptionalLikePhase2Seed(require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec));
140
+ return normalizeOptionalLikePhase2Seed(require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec), false);
129
141
  }
130
- return normalizeOptionalLikePhase2Seed(seed);
142
+ return normalizeOptionalLikePhase2Seed(seed, hasCarrier);
131
143
  } catch (error) {
132
144
  if (!hasCarrier) throw error;
133
145
  const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(innerState);
134
146
  const result = parser.complete(fallbackState, exec);
135
- if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result));
136
- return normalizeOptionalLikePhase2Seed(require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec));
147
+ if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result), false);
148
+ return normalizeOptionalLikePhase2Seed(require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec), false);
137
149
  }
138
150
  }, async () => {
139
151
  try {
140
152
  const result = await parser.complete(innerState, exec);
141
- if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result));
153
+ if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result), hasCarrier);
154
+ if (shouldRetryFalseResult) {
155
+ const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(innerState);
156
+ const fallbackResult = await parser.complete(fallbackState, exec);
157
+ if (fallbackResult.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(fallbackResult), false);
158
+ }
142
159
  const seed = await require_phase2_seed.extractPhase2Seed(parser, innerState, exec);
143
160
  if (seed == null && hasCarrier) {
144
161
  const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(innerState);
145
- return normalizeOptionalLikePhase2Seed(await require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec));
162
+ return normalizeOptionalLikePhase2Seed(await require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec), false);
146
163
  }
147
- return normalizeOptionalLikePhase2Seed(seed);
164
+ return normalizeOptionalLikePhase2Seed(seed, hasCarrier);
148
165
  } catch (error) {
149
166
  if (!hasCarrier) throw error;
150
167
  const fallbackState = require_annotation_state.normalizeDelegatedAnnotationState(innerState);
151
168
  const result = await parser.complete(fallbackState, exec);
152
- if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result));
153
- return normalizeOptionalLikePhase2Seed(await require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec));
169
+ if (result.success) return normalizeOptionalLikePhase2Seed(require_phase2_seed.phase2SeedFromValueResult(result), false);
170
+ return normalizeOptionalLikePhase2Seed(await require_phase2_seed.extractPhase2Seed(parser, fallbackState, exec), false);
154
171
  }
155
172
  });
156
173
  }
157
174
  /**
158
175
  * Computes the inner state to pass through to the wrapped parser inside
159
176
  * {@link optional} / {@link withDefault}. When the outer state is an
160
- * array, the inner state is `state[0]`. Otherwise including the
177
+ * array, the inner state is `state[0]`. Otherwise, including the
161
178
  * common case where `optional()` sits at top level and the outer state
162
179
  * is either `undefined` or an annotation wrapper from `parseOptionalLike`
163
- * / `parse({ annotations })` we use the wrapped parser's
180
+ * / `parse({ annotations })`, we use the wrapped parser's
164
181
  * `initialState`, propagating annotations from the outer state so that
165
182
  * source-binding wrappers under `optional()` / `withDefault()` (e.g.,
166
183
  * `bindEnv()` / `bindConfig()`) can resolve their fallbacks.
@@ -181,8 +198,8 @@ function deriveOptionalInnerParseState(outerState, parser) {
181
198
  * Internal helper for optional-style parsing logic shared by optional()
182
199
  * and withDefault(). Handles the common pattern of:
183
200
  * - Unwrapping optional state to inner parser state
184
- * - Detecting if inner parser actually matched (state changed or no consumption)
185
- * - Returning success with undefined state when inner parser fails without consuming
201
+ * - Detecting if the inner parser actually matched (state changed or no consumption)
202
+ * - Returning success with undefined state when the inner parser fails without consuming
186
203
  * @internal
187
204
  */
188
205
  function parseOptionalStyleSync(context, parser) {
@@ -243,7 +260,7 @@ function processOptionalStyleResult(result, innerState, context) {
243
260
  * {@link withDefault} before delegating to the inner parser's hook.
244
261
  *
245
262
  * When state is an array, the adapter unwraps `state[0]` and propagates
246
- * annotations from the outer array. Non-array objects (e.g., PromptBindState
263
+ * annotations from the outer array. Non-array objects (e.g. PromptBindState
247
264
  * from `prompt()`) are passed through directly. `undefined` returns `false`
248
265
  * without calling the inner hook.
249
266
  *
@@ -253,10 +270,14 @@ function adaptShouldDeferCompletion(innerCheck, parser) {
253
270
  return (state, exec) => {
254
271
  if (Array.isArray(state) || state != null && typeof state === "object") {
255
272
  const innerState = normalizeOptionalLikeInnerState(state, parser.initialState, parser);
273
+ const hasCarrier = require_annotation_state.hasDelegatedAnnotationCarrier(innerState);
274
+ const shouldRetryFalseResult = hasCarrier && require_annotation_state.isAnnotationWrappedInitialState(innerState);
256
275
  try {
257
- return innerCheck(innerState, exec);
276
+ const result = innerCheck(innerState, exec);
277
+ if (!result && shouldRetryFalseResult) return innerCheck(require_annotation_state.normalizeDelegatedAnnotationState(innerState), exec);
278
+ return result;
258
279
  } catch (error) {
259
- if (!require_annotation_state.hasDelegatedAnnotationCarrier(innerState)) throw error;
280
+ if (!hasCarrier) throw error;
260
281
  return innerCheck(require_annotation_state.normalizeDelegatedAnnotationState(innerState), exec);
261
282
  }
262
283
  }
package/dist/modifiers.js CHANGED
@@ -4,7 +4,7 @@ import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } fro
4
4
  import { composeDependencyMetadata } from "./dependency-metadata.js";
5
5
  import { completeOrExtractPhase2Seed, extractPhase2Seed, extractPhase2SeedKey, phase2SeedFromValueResult } from "./phase2-seed.js";
6
6
  import { defineInheritedAnnotationParser, defineSourceBindingOnlyAnnotationCompletionParser, unmatchedNonCliDependencySourceStateMarker } from "./parser.js";
7
- import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
7
+ import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, isAnnotationWrappedInitialState, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
8
8
 
9
9
  //#region src/modifiers.ts
10
10
  function withChildExecPath(exec, segment) {
@@ -82,85 +82,102 @@ function unwrapMultipleItemState(state) {
82
82
  function isPromiseLike(value) {
83
83
  return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
84
84
  }
85
- function normalizeOptionalLikeCompleteResult(result) {
86
- return result.success ? {
85
+ function normalizeOptionalLikeCompleteResult(result, shouldNormalize) {
86
+ return result.success && shouldNormalize ? {
87
87
  ...result,
88
88
  value: normalizeNestedDelegatedAnnotationState(result.value)
89
89
  } : result;
90
90
  }
91
91
  function completeOptionalLikeSync(parser, state, exec) {
92
92
  const hasCarrier = hasDelegatedAnnotationCarrier(state);
93
+ const shouldRetryFalseResult = hasCarrier && isAnnotationWrappedInitialState(state);
94
+ const run = (candidate, shouldNormalize) => normalizeOptionalLikeCompleteResult(parser.complete(candidate, exec), shouldNormalize);
93
95
  try {
94
- return normalizeOptionalLikeCompleteResult(parser.complete(state, exec));
96
+ const result = run(state, hasCarrier);
97
+ if (!result.success && shouldRetryFalseResult) return run(normalizeDelegatedAnnotationState(state), false);
98
+ return result;
95
99
  } catch (error) {
96
100
  if (!hasCarrier) throw error;
97
- const fallbackState = normalizeDelegatedAnnotationState(state);
98
- return normalizeOptionalLikeCompleteResult(parser.complete(fallbackState, exec));
101
+ return run(normalizeDelegatedAnnotationState(state), false);
99
102
  }
100
103
  }
101
104
  async function completeOptionalLikeAsync(parser, state, exec) {
102
105
  const hasCarrier = hasDelegatedAnnotationCarrier(state);
106
+ const shouldRetryFalseResult = hasCarrier && isAnnotationWrappedInitialState(state);
107
+ const run = async (candidate, shouldNormalize) => normalizeOptionalLikeCompleteResult(await parser.complete(candidate, exec), shouldNormalize);
103
108
  try {
104
- return normalizeOptionalLikeCompleteResult(await parser.complete(state, exec));
109
+ const result = await run(state, hasCarrier);
110
+ if (!result.success && shouldRetryFalseResult) return await run(normalizeDelegatedAnnotationState(state), false);
111
+ return result;
105
112
  } catch (error) {
106
113
  if (!hasCarrier) throw error;
107
- const fallbackState = normalizeDelegatedAnnotationState(state);
108
- return normalizeOptionalLikeCompleteResult(await parser.complete(fallbackState, exec));
114
+ return await run(normalizeDelegatedAnnotationState(state), false);
109
115
  }
110
116
  }
111
- function normalizeOptionalLikePhase2Seed(seed) {
112
- return seed == null ? null : {
117
+ function normalizeOptionalLikePhase2Seed(seed, shouldNormalize) {
118
+ return seed == null ? null : shouldNormalize ? {
113
119
  ...seed,
114
120
  value: normalizeNestedDelegatedAnnotationState(seed.value)
115
- };
121
+ } : seed;
116
122
  }
117
123
  function extractOptionalLikePhase2Seed(parser, state, exec) {
118
124
  if (!Array.isArray(state) && !(state != null && typeof state === "object")) return wrapForMode(parser.$mode, null);
119
125
  const innerState = normalizeOptionalLikeInnerState(state, parser.initialState, parser);
120
126
  const hasCarrier = hasDelegatedAnnotationCarrier(innerState);
127
+ const shouldRetryFalseResult = hasCarrier && isAnnotationWrappedInitialState(innerState);
121
128
  return dispatchByMode(parser.$mode, () => {
122
129
  try {
123
130
  const result = parser.complete(innerState, exec);
124
- if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result));
131
+ if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result), hasCarrier);
132
+ if (shouldRetryFalseResult) {
133
+ const fallbackState = normalizeDelegatedAnnotationState(innerState);
134
+ const fallbackResult = parser.complete(fallbackState, exec);
135
+ if (fallbackResult.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(fallbackResult), false);
136
+ }
125
137
  const seed = extractPhase2Seed(parser, innerState, exec);
126
138
  if (seed == null && hasCarrier) {
127
139
  const fallbackState = normalizeDelegatedAnnotationState(innerState);
128
- return normalizeOptionalLikePhase2Seed(extractPhase2Seed(parser, fallbackState, exec));
140
+ return normalizeOptionalLikePhase2Seed(extractPhase2Seed(parser, fallbackState, exec), false);
129
141
  }
130
- return normalizeOptionalLikePhase2Seed(seed);
142
+ return normalizeOptionalLikePhase2Seed(seed, hasCarrier);
131
143
  } catch (error) {
132
144
  if (!hasCarrier) throw error;
133
145
  const fallbackState = normalizeDelegatedAnnotationState(innerState);
134
146
  const result = parser.complete(fallbackState, exec);
135
- if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result));
136
- return normalizeOptionalLikePhase2Seed(extractPhase2Seed(parser, fallbackState, exec));
147
+ if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result), false);
148
+ return normalizeOptionalLikePhase2Seed(extractPhase2Seed(parser, fallbackState, exec), false);
137
149
  }
138
150
  }, async () => {
139
151
  try {
140
152
  const result = await parser.complete(innerState, exec);
141
- if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result));
153
+ if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result), hasCarrier);
154
+ if (shouldRetryFalseResult) {
155
+ const fallbackState = normalizeDelegatedAnnotationState(innerState);
156
+ const fallbackResult = await parser.complete(fallbackState, exec);
157
+ if (fallbackResult.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(fallbackResult), false);
158
+ }
142
159
  const seed = await extractPhase2Seed(parser, innerState, exec);
143
160
  if (seed == null && hasCarrier) {
144
161
  const fallbackState = normalizeDelegatedAnnotationState(innerState);
145
- return normalizeOptionalLikePhase2Seed(await extractPhase2Seed(parser, fallbackState, exec));
162
+ return normalizeOptionalLikePhase2Seed(await extractPhase2Seed(parser, fallbackState, exec), false);
146
163
  }
147
- return normalizeOptionalLikePhase2Seed(seed);
164
+ return normalizeOptionalLikePhase2Seed(seed, hasCarrier);
148
165
  } catch (error) {
149
166
  if (!hasCarrier) throw error;
150
167
  const fallbackState = normalizeDelegatedAnnotationState(innerState);
151
168
  const result = await parser.complete(fallbackState, exec);
152
- if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result));
153
- return normalizeOptionalLikePhase2Seed(await extractPhase2Seed(parser, fallbackState, exec));
169
+ if (result.success) return normalizeOptionalLikePhase2Seed(phase2SeedFromValueResult(result), false);
170
+ return normalizeOptionalLikePhase2Seed(await extractPhase2Seed(parser, fallbackState, exec), false);
154
171
  }
155
172
  });
156
173
  }
157
174
  /**
158
175
  * Computes the inner state to pass through to the wrapped parser inside
159
176
  * {@link optional} / {@link withDefault}. When the outer state is an
160
- * array, the inner state is `state[0]`. Otherwise including the
177
+ * array, the inner state is `state[0]`. Otherwise, including the
161
178
  * common case where `optional()` sits at top level and the outer state
162
179
  * is either `undefined` or an annotation wrapper from `parseOptionalLike`
163
- * / `parse({ annotations })` we use the wrapped parser's
180
+ * / `parse({ annotations })`, we use the wrapped parser's
164
181
  * `initialState`, propagating annotations from the outer state so that
165
182
  * source-binding wrappers under `optional()` / `withDefault()` (e.g.,
166
183
  * `bindEnv()` / `bindConfig()`) can resolve their fallbacks.
@@ -181,8 +198,8 @@ function deriveOptionalInnerParseState(outerState, parser) {
181
198
  * Internal helper for optional-style parsing logic shared by optional()
182
199
  * and withDefault(). Handles the common pattern of:
183
200
  * - Unwrapping optional state to inner parser state
184
- * - Detecting if inner parser actually matched (state changed or no consumption)
185
- * - Returning success with undefined state when inner parser fails without consuming
201
+ * - Detecting if the inner parser actually matched (state changed or no consumption)
202
+ * - Returning success with undefined state when the inner parser fails without consuming
186
203
  * @internal
187
204
  */
188
205
  function parseOptionalStyleSync(context, parser) {
@@ -243,7 +260,7 @@ function processOptionalStyleResult(result, innerState, context) {
243
260
  * {@link withDefault} before delegating to the inner parser's hook.
244
261
  *
245
262
  * When state is an array, the adapter unwraps `state[0]` and propagates
246
- * annotations from the outer array. Non-array objects (e.g., PromptBindState
263
+ * annotations from the outer array. Non-array objects (e.g. PromptBindState
247
264
  * from `prompt()`) are passed through directly. `undefined` returns `false`
248
265
  * without calling the inner hook.
249
266
  *
@@ -253,10 +270,14 @@ function adaptShouldDeferCompletion(innerCheck, parser) {
253
270
  return (state, exec) => {
254
271
  if (Array.isArray(state) || state != null && typeof state === "object") {
255
272
  const innerState = normalizeOptionalLikeInnerState(state, parser.initialState, parser);
273
+ const hasCarrier = hasDelegatedAnnotationCarrier(innerState);
274
+ const shouldRetryFalseResult = hasCarrier && isAnnotationWrappedInitialState(innerState);
256
275
  try {
257
- return innerCheck(innerState, exec);
276
+ const result = innerCheck(innerState, exec);
277
+ if (!result && shouldRetryFalseResult) return innerCheck(normalizeDelegatedAnnotationState(innerState), exec);
278
+ return result;
258
279
  } catch (error) {
259
- if (!hasDelegatedAnnotationCarrier(innerState)) throw error;
280
+ if (!hasCarrier) throw error;
260
281
  return innerCheck(normalizeDelegatedAnnotationState(innerState), exec);
261
282
  }
262
283
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1899+f70a2468",
3
+ "version": "1.0.0-dev.1903+af120cc5",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",