@optique/core 1.0.0-dev.524 → 1.0.0-dev.555

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.
@@ -16,6 +16,29 @@
16
16
  */
17
17
  const annotationKey = Symbol.for("@optique/core/parser/annotation");
18
18
  /**
19
+ * Internal key for preserving primitive parser state values when annotations
20
+ * are injected into non-object states.
21
+ * @internal
22
+ */
23
+ const annotationStateValueKey = Symbol.for("@optique/core/parser/annotationStateValue");
24
+ /**
25
+ * Internal marker key that indicates annotation wrapping was injected by
26
+ * Optique internals for non-object states.
27
+ * @internal
28
+ */
29
+ const annotationWrapperKey = Symbol.for("@optique/core/parser/annotationWrapper");
30
+ /**
31
+ * Internal symbol keys that define Optique's primitive-state annotation
32
+ * wrapper shape.
33
+ * @internal
34
+ */
35
+ const annotationWrapperKeys = new Set([
36
+ annotationKey,
37
+ annotationStateValueKey,
38
+ annotationWrapperKey
39
+ ]);
40
+ const injectedAnnotationWrappers = /* @__PURE__ */ new WeakSet();
41
+ /**
19
42
  * Extracts annotations from parser state.
20
43
  *
21
44
  * @param state Parser state that may contain annotations
@@ -35,7 +58,165 @@ function getAnnotations(state) {
35
58
  if (annotations != null && typeof annotations === "object") return annotations;
36
59
  return void 0;
37
60
  }
61
+ /**
62
+ * Propagates annotations from one parser state to another.
63
+ *
64
+ * This is mainly used by parsers that rebuild array states with spread syntax.
65
+ * Array spread copies elements but drops custom symbol properties, so we need
66
+ * to reattach annotations explicitly when present.
67
+ *
68
+ * @param source The original state that may carry annotations.
69
+ * @param target The new state to receive annotations.
70
+ * @returns The target state, with annotations copied when available.
71
+ * @internal
72
+ */
73
+ function inheritAnnotations(source, target) {
74
+ const annotations = getAnnotations(source);
75
+ if (annotations === void 0) return target;
76
+ if (target == null || typeof target !== "object") return injectAnnotations(target, annotations);
77
+ if (isInjectedAnnotationWrapper(target)) return injectAnnotations(target, annotations);
78
+ if (Array.isArray(target)) {
79
+ const cloned$1 = [...target];
80
+ cloned$1[annotationKey] = annotations;
81
+ return cloned$1;
82
+ }
83
+ if (target instanceof Date) {
84
+ const cloned$1 = new Date(target.getTime());
85
+ cloned$1[annotationKey] = annotations;
86
+ return cloned$1;
87
+ }
88
+ if (target instanceof Map) {
89
+ const cloned$1 = new Map(target);
90
+ cloned$1[annotationKey] = annotations;
91
+ return cloned$1;
92
+ }
93
+ if (target instanceof Set) {
94
+ const cloned$1 = new Set(target);
95
+ cloned$1[annotationKey] = annotations;
96
+ return cloned$1;
97
+ }
98
+ if (target instanceof RegExp) {
99
+ const cloned$1 = new RegExp(target);
100
+ cloned$1[annotationKey] = annotations;
101
+ return cloned$1;
102
+ }
103
+ if (Object.getPrototypeOf(target) !== Object.prototype && Object.getPrototypeOf(target) !== null) return target;
104
+ const cloned = Object.create(Object.getPrototypeOf(target), Object.getOwnPropertyDescriptors(target));
105
+ cloned[annotationKey] = annotations;
106
+ return cloned;
107
+ }
108
+ /**
109
+ * Injects annotations into parser state while preserving state shape.
110
+ *
111
+ * - Primitive, null, and undefined states are wrapped with internal metadata.
112
+ * - Array states are cloned and annotated without mutating the original.
113
+ * - Plain object states are shallow-cloned with annotations attached.
114
+ * - Built-in object states (Date/Map/Set/RegExp) are cloned by constructor.
115
+ * - Other non-plain object states are cloned via prototype/descriptors.
116
+ *
117
+ * @param state The parser state to annotate.
118
+ * @param annotations The annotations to inject.
119
+ * @returns Annotated state.
120
+ * @internal
121
+ */
122
+ function injectAnnotations(state, annotations) {
123
+ if (state == null || typeof state !== "object") {
124
+ const wrapper = {};
125
+ Object.defineProperties(wrapper, {
126
+ [annotationKey]: {
127
+ value: annotations,
128
+ enumerable: true,
129
+ writable: true,
130
+ configurable: true
131
+ },
132
+ [annotationStateValueKey]: {
133
+ value: state,
134
+ enumerable: false,
135
+ writable: true,
136
+ configurable: true
137
+ },
138
+ [annotationWrapperKey]: {
139
+ value: true,
140
+ enumerable: false,
141
+ writable: true,
142
+ configurable: true
143
+ }
144
+ });
145
+ injectedAnnotationWrappers.add(wrapper);
146
+ return wrapper;
147
+ }
148
+ if (Array.isArray(state)) {
149
+ const cloned$1 = [...state];
150
+ cloned$1[annotationKey] = annotations;
151
+ return cloned$1;
152
+ }
153
+ if (isInjectedAnnotationWrapper(state)) {
154
+ state[annotationKey] = annotations;
155
+ return state;
156
+ }
157
+ if (state instanceof Date) {
158
+ const cloned$1 = new Date(state.getTime());
159
+ cloned$1[annotationKey] = annotations;
160
+ return cloned$1;
161
+ }
162
+ if (state instanceof Map) {
163
+ const cloned$1 = new Map(state);
164
+ cloned$1[annotationKey] = annotations;
165
+ return cloned$1;
166
+ }
167
+ if (state instanceof Set) {
168
+ const cloned$1 = new Set(state);
169
+ cloned$1[annotationKey] = annotations;
170
+ return cloned$1;
171
+ }
172
+ if (state instanceof RegExp) {
173
+ const cloned$1 = new RegExp(state);
174
+ cloned$1[annotationKey] = annotations;
175
+ return cloned$1;
176
+ }
177
+ const proto = Object.getPrototypeOf(state);
178
+ if (proto === Object.prototype || proto === null) return {
179
+ ...state,
180
+ [annotationKey]: annotations
181
+ };
182
+ const cloned = Object.create(proto, Object.getOwnPropertyDescriptors(state));
183
+ cloned[annotationKey] = annotations;
184
+ return cloned;
185
+ }
186
+ /**
187
+ * Unwraps a primitive-state annotation wrapper injected by Optique internals.
188
+ *
189
+ * @param value Value to potentially unwrap.
190
+ * @returns The unwrapped primitive value when the input is an injected wrapper;
191
+ * otherwise the original value.
192
+ * @internal
193
+ */
194
+ function unwrapInjectedAnnotationWrapper(value) {
195
+ if (value == null || typeof value !== "object") return value;
196
+ const valueRecord = value;
197
+ if (valueRecord[annotationWrapperKey] !== true) return value;
198
+ const ownKeys = Reflect.ownKeys(valueRecord);
199
+ if (ownKeys.length === 3 && ownKeys.every((key) => annotationWrapperKeys.has(key)) && isInjectedAnnotationWrapper(value)) return valueRecord[annotationStateValueKey];
200
+ return value;
201
+ }
202
+ /**
203
+ * Returns whether the given value is an internal primitive-state annotation
204
+ * wrapper that was injected by Optique.
205
+ *
206
+ * @param value Value to check.
207
+ * @returns `true` if the value is an injected internal wrapper.
208
+ * @internal
209
+ */
210
+ function isInjectedAnnotationWrapper(value) {
211
+ return value != null && typeof value === "object" && injectedAnnotationWrappers.has(value);
212
+ }
38
213
 
39
214
  //#endregion
40
215
  exports.annotationKey = annotationKey;
41
- exports.getAnnotations = getAnnotations;
216
+ exports.annotationStateValueKey = annotationStateValueKey;
217
+ exports.annotationWrapperKey = annotationWrapperKey;
218
+ exports.getAnnotations = getAnnotations;
219
+ exports.inheritAnnotations = inheritAnnotations;
220
+ exports.injectAnnotations = injectAnnotations;
221
+ exports.isInjectedAnnotationWrapper = isInjectedAnnotationWrapper;
222
+ exports.unwrapInjectedAnnotationWrapper = unwrapInjectedAnnotationWrapper;
@@ -14,6 +14,18 @@
14
14
  * @since 0.10.0
15
15
  */
16
16
  declare const annotationKey: unique symbol;
17
+ /**
18
+ * Internal key for preserving primitive parser state values when annotations
19
+ * are injected into non-object states.
20
+ * @internal
21
+ */
22
+ declare const annotationStateValueKey: unique symbol;
23
+ /**
24
+ * Internal marker key that indicates annotation wrapping was injected by
25
+ * Optique internals for non-object states.
26
+ * @internal
27
+ */
28
+ declare const annotationWrapperKey: unique symbol;
17
29
  /**
18
30
  * Annotations that can be passed to parsers during execution.
19
31
  * Allows external packages to provide additional data that parsers can access
@@ -56,5 +68,51 @@ interface ParseOptions {
56
68
  * ```
57
69
  */
58
70
  declare function getAnnotations(state: unknown): Annotations | undefined;
71
+ /**
72
+ * Propagates annotations from one parser state to another.
73
+ *
74
+ * This is mainly used by parsers that rebuild array states with spread syntax.
75
+ * Array spread copies elements but drops custom symbol properties, so we need
76
+ * to reattach annotations explicitly when present.
77
+ *
78
+ * @param source The original state that may carry annotations.
79
+ * @param target The new state to receive annotations.
80
+ * @returns The target state, with annotations copied when available.
81
+ * @internal
82
+ */
83
+ declare function inheritAnnotations<T>(source: unknown, target: T): T;
84
+ /**
85
+ * Injects annotations into parser state while preserving state shape.
86
+ *
87
+ * - Primitive, null, and undefined states are wrapped with internal metadata.
88
+ * - Array states are cloned and annotated without mutating the original.
89
+ * - Plain object states are shallow-cloned with annotations attached.
90
+ * - Built-in object states (Date/Map/Set/RegExp) are cloned by constructor.
91
+ * - Other non-plain object states are cloned via prototype/descriptors.
92
+ *
93
+ * @param state The parser state to annotate.
94
+ * @param annotations The annotations to inject.
95
+ * @returns Annotated state.
96
+ * @internal
97
+ */
98
+ declare function injectAnnotations<TState>(state: TState, annotations: Annotations): TState;
99
+ /**
100
+ * Unwraps a primitive-state annotation wrapper injected by Optique internals.
101
+ *
102
+ * @param value Value to potentially unwrap.
103
+ * @returns The unwrapped primitive value when the input is an injected wrapper;
104
+ * otherwise the original value.
105
+ * @internal
106
+ */
107
+ declare function unwrapInjectedAnnotationWrapper<T>(value: T): T;
108
+ /**
109
+ * Returns whether the given value is an internal primitive-state annotation
110
+ * wrapper that was injected by Optique.
111
+ *
112
+ * @param value Value to check.
113
+ * @returns `true` if the value is an injected internal wrapper.
114
+ * @internal
115
+ */
116
+ declare function isInjectedAnnotationWrapper(value: unknown): boolean;
59
117
  //#endregion
60
- export { Annotations, ParseOptions, annotationKey, getAnnotations };
118
+ export { Annotations, ParseOptions, annotationKey, annotationStateValueKey, annotationWrapperKey, getAnnotations, inheritAnnotations, injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper };
@@ -14,6 +14,18 @@
14
14
  * @since 0.10.0
15
15
  */
16
16
  declare const annotationKey: unique symbol;
17
+ /**
18
+ * Internal key for preserving primitive parser state values when annotations
19
+ * are injected into non-object states.
20
+ * @internal
21
+ */
22
+ declare const annotationStateValueKey: unique symbol;
23
+ /**
24
+ * Internal marker key that indicates annotation wrapping was injected by
25
+ * Optique internals for non-object states.
26
+ * @internal
27
+ */
28
+ declare const annotationWrapperKey: unique symbol;
17
29
  /**
18
30
  * Annotations that can be passed to parsers during execution.
19
31
  * Allows external packages to provide additional data that parsers can access
@@ -56,5 +68,51 @@ interface ParseOptions {
56
68
  * ```
57
69
  */
58
70
  declare function getAnnotations(state: unknown): Annotations | undefined;
71
+ /**
72
+ * Propagates annotations from one parser state to another.
73
+ *
74
+ * This is mainly used by parsers that rebuild array states with spread syntax.
75
+ * Array spread copies elements but drops custom symbol properties, so we need
76
+ * to reattach annotations explicitly when present.
77
+ *
78
+ * @param source The original state that may carry annotations.
79
+ * @param target The new state to receive annotations.
80
+ * @returns The target state, with annotations copied when available.
81
+ * @internal
82
+ */
83
+ declare function inheritAnnotations<T>(source: unknown, target: T): T;
84
+ /**
85
+ * Injects annotations into parser state while preserving state shape.
86
+ *
87
+ * - Primitive, null, and undefined states are wrapped with internal metadata.
88
+ * - Array states are cloned and annotated without mutating the original.
89
+ * - Plain object states are shallow-cloned with annotations attached.
90
+ * - Built-in object states (Date/Map/Set/RegExp) are cloned by constructor.
91
+ * - Other non-plain object states are cloned via prototype/descriptors.
92
+ *
93
+ * @param state The parser state to annotate.
94
+ * @param annotations The annotations to inject.
95
+ * @returns Annotated state.
96
+ * @internal
97
+ */
98
+ declare function injectAnnotations<TState>(state: TState, annotations: Annotations): TState;
99
+ /**
100
+ * Unwraps a primitive-state annotation wrapper injected by Optique internals.
101
+ *
102
+ * @param value Value to potentially unwrap.
103
+ * @returns The unwrapped primitive value when the input is an injected wrapper;
104
+ * otherwise the original value.
105
+ * @internal
106
+ */
107
+ declare function unwrapInjectedAnnotationWrapper<T>(value: T): T;
108
+ /**
109
+ * Returns whether the given value is an internal primitive-state annotation
110
+ * wrapper that was injected by Optique.
111
+ *
112
+ * @param value Value to check.
113
+ * @returns `true` if the value is an injected internal wrapper.
114
+ * @internal
115
+ */
116
+ declare function isInjectedAnnotationWrapper(value: unknown): boolean;
59
117
  //#endregion
60
- export { Annotations, ParseOptions, annotationKey, getAnnotations };
118
+ export { Annotations, ParseOptions, annotationKey, annotationStateValueKey, annotationWrapperKey, getAnnotations, inheritAnnotations, injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper };
@@ -15,6 +15,29 @@
15
15
  */
16
16
  const annotationKey = Symbol.for("@optique/core/parser/annotation");
17
17
  /**
18
+ * Internal key for preserving primitive parser state values when annotations
19
+ * are injected into non-object states.
20
+ * @internal
21
+ */
22
+ const annotationStateValueKey = Symbol.for("@optique/core/parser/annotationStateValue");
23
+ /**
24
+ * Internal marker key that indicates annotation wrapping was injected by
25
+ * Optique internals for non-object states.
26
+ * @internal
27
+ */
28
+ const annotationWrapperKey = Symbol.for("@optique/core/parser/annotationWrapper");
29
+ /**
30
+ * Internal symbol keys that define Optique's primitive-state annotation
31
+ * wrapper shape.
32
+ * @internal
33
+ */
34
+ const annotationWrapperKeys = new Set([
35
+ annotationKey,
36
+ annotationStateValueKey,
37
+ annotationWrapperKey
38
+ ]);
39
+ const injectedAnnotationWrappers = /* @__PURE__ */ new WeakSet();
40
+ /**
18
41
  * Extracts annotations from parser state.
19
42
  *
20
43
  * @param state Parser state that may contain annotations
@@ -34,6 +57,158 @@ function getAnnotations(state) {
34
57
  if (annotations != null && typeof annotations === "object") return annotations;
35
58
  return void 0;
36
59
  }
60
+ /**
61
+ * Propagates annotations from one parser state to another.
62
+ *
63
+ * This is mainly used by parsers that rebuild array states with spread syntax.
64
+ * Array spread copies elements but drops custom symbol properties, so we need
65
+ * to reattach annotations explicitly when present.
66
+ *
67
+ * @param source The original state that may carry annotations.
68
+ * @param target The new state to receive annotations.
69
+ * @returns The target state, with annotations copied when available.
70
+ * @internal
71
+ */
72
+ function inheritAnnotations(source, target) {
73
+ const annotations = getAnnotations(source);
74
+ if (annotations === void 0) return target;
75
+ if (target == null || typeof target !== "object") return injectAnnotations(target, annotations);
76
+ if (isInjectedAnnotationWrapper(target)) return injectAnnotations(target, annotations);
77
+ if (Array.isArray(target)) {
78
+ const cloned$1 = [...target];
79
+ cloned$1[annotationKey] = annotations;
80
+ return cloned$1;
81
+ }
82
+ if (target instanceof Date) {
83
+ const cloned$1 = new Date(target.getTime());
84
+ cloned$1[annotationKey] = annotations;
85
+ return cloned$1;
86
+ }
87
+ if (target instanceof Map) {
88
+ const cloned$1 = new Map(target);
89
+ cloned$1[annotationKey] = annotations;
90
+ return cloned$1;
91
+ }
92
+ if (target instanceof Set) {
93
+ const cloned$1 = new Set(target);
94
+ cloned$1[annotationKey] = annotations;
95
+ return cloned$1;
96
+ }
97
+ if (target instanceof RegExp) {
98
+ const cloned$1 = new RegExp(target);
99
+ cloned$1[annotationKey] = annotations;
100
+ return cloned$1;
101
+ }
102
+ if (Object.getPrototypeOf(target) !== Object.prototype && Object.getPrototypeOf(target) !== null) return target;
103
+ const cloned = Object.create(Object.getPrototypeOf(target), Object.getOwnPropertyDescriptors(target));
104
+ cloned[annotationKey] = annotations;
105
+ return cloned;
106
+ }
107
+ /**
108
+ * Injects annotations into parser state while preserving state shape.
109
+ *
110
+ * - Primitive, null, and undefined states are wrapped with internal metadata.
111
+ * - Array states are cloned and annotated without mutating the original.
112
+ * - Plain object states are shallow-cloned with annotations attached.
113
+ * - Built-in object states (Date/Map/Set/RegExp) are cloned by constructor.
114
+ * - Other non-plain object states are cloned via prototype/descriptors.
115
+ *
116
+ * @param state The parser state to annotate.
117
+ * @param annotations The annotations to inject.
118
+ * @returns Annotated state.
119
+ * @internal
120
+ */
121
+ function injectAnnotations(state, annotations) {
122
+ if (state == null || typeof state !== "object") {
123
+ const wrapper = {};
124
+ Object.defineProperties(wrapper, {
125
+ [annotationKey]: {
126
+ value: annotations,
127
+ enumerable: true,
128
+ writable: true,
129
+ configurable: true
130
+ },
131
+ [annotationStateValueKey]: {
132
+ value: state,
133
+ enumerable: false,
134
+ writable: true,
135
+ configurable: true
136
+ },
137
+ [annotationWrapperKey]: {
138
+ value: true,
139
+ enumerable: false,
140
+ writable: true,
141
+ configurable: true
142
+ }
143
+ });
144
+ injectedAnnotationWrappers.add(wrapper);
145
+ return wrapper;
146
+ }
147
+ if (Array.isArray(state)) {
148
+ const cloned$1 = [...state];
149
+ cloned$1[annotationKey] = annotations;
150
+ return cloned$1;
151
+ }
152
+ if (isInjectedAnnotationWrapper(state)) {
153
+ state[annotationKey] = annotations;
154
+ return state;
155
+ }
156
+ if (state instanceof Date) {
157
+ const cloned$1 = new Date(state.getTime());
158
+ cloned$1[annotationKey] = annotations;
159
+ return cloned$1;
160
+ }
161
+ if (state instanceof Map) {
162
+ const cloned$1 = new Map(state);
163
+ cloned$1[annotationKey] = annotations;
164
+ return cloned$1;
165
+ }
166
+ if (state instanceof Set) {
167
+ const cloned$1 = new Set(state);
168
+ cloned$1[annotationKey] = annotations;
169
+ return cloned$1;
170
+ }
171
+ if (state instanceof RegExp) {
172
+ const cloned$1 = new RegExp(state);
173
+ cloned$1[annotationKey] = annotations;
174
+ return cloned$1;
175
+ }
176
+ const proto = Object.getPrototypeOf(state);
177
+ if (proto === Object.prototype || proto === null) return {
178
+ ...state,
179
+ [annotationKey]: annotations
180
+ };
181
+ const cloned = Object.create(proto, Object.getOwnPropertyDescriptors(state));
182
+ cloned[annotationKey] = annotations;
183
+ return cloned;
184
+ }
185
+ /**
186
+ * Unwraps a primitive-state annotation wrapper injected by Optique internals.
187
+ *
188
+ * @param value Value to potentially unwrap.
189
+ * @returns The unwrapped primitive value when the input is an injected wrapper;
190
+ * otherwise the original value.
191
+ * @internal
192
+ */
193
+ function unwrapInjectedAnnotationWrapper(value) {
194
+ if (value == null || typeof value !== "object") return value;
195
+ const valueRecord = value;
196
+ if (valueRecord[annotationWrapperKey] !== true) return value;
197
+ const ownKeys = Reflect.ownKeys(valueRecord);
198
+ if (ownKeys.length === 3 && ownKeys.every((key) => annotationWrapperKeys.has(key)) && isInjectedAnnotationWrapper(value)) return valueRecord[annotationStateValueKey];
199
+ return value;
200
+ }
201
+ /**
202
+ * Returns whether the given value is an internal primitive-state annotation
203
+ * wrapper that was injected by Optique.
204
+ *
205
+ * @param value Value to check.
206
+ * @returns `true` if the value is an injected internal wrapper.
207
+ * @internal
208
+ */
209
+ function isInjectedAnnotationWrapper(value) {
210
+ return value != null && typeof value === "object" && injectedAnnotationWrappers.has(value);
211
+ }
37
212
 
38
213
  //#endregion
39
- export { annotationKey, getAnnotations };
214
+ export { annotationKey, annotationStateValueKey, annotationWrapperKey, getAnnotations, inheritAnnotations, injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper };
package/dist/context.cjs CHANGED
@@ -4,8 +4,9 @@
4
4
  * Checks whether a context is static (returns annotations without needing
5
5
  * parsed results).
6
6
  *
7
- * A context is considered static if `getAnnotations()` called without
8
- * arguments returns a non-empty annotations object synchronously.
7
+ * A context is considered static if it declares `mode: "static"` or if
8
+ * `getAnnotations()` called without arguments returns a non-empty
9
+ * annotations object synchronously.
9
10
  *
10
11
  * @param context The source context to check.
11
12
  * @returns `true` if the context is static, `false` otherwise.
@@ -154,8 +154,9 @@ interface SourceContext<TRequiredOptions = void> {
154
154
  * Checks whether a context is static (returns annotations without needing
155
155
  * parsed results).
156
156
  *
157
- * A context is considered static if `getAnnotations()` called without
158
- * arguments returns a non-empty annotations object synchronously.
157
+ * A context is considered static if it declares `mode: "static"` or if
158
+ * `getAnnotations()` called without arguments returns a non-empty
159
+ * annotations object synchronously.
159
160
  *
160
161
  * @param context The source context to check.
161
162
  * @returns `true` if the context is static, `false` otherwise.
package/dist/context.d.ts CHANGED
@@ -154,8 +154,9 @@ interface SourceContext<TRequiredOptions = void> {
154
154
  * Checks whether a context is static (returns annotations without needing
155
155
  * parsed results).
156
156
  *
157
- * A context is considered static if `getAnnotations()` called without
158
- * arguments returns a non-empty annotations object synchronously.
157
+ * A context is considered static if it declares `mode: "static"` or if
158
+ * `getAnnotations()` called without arguments returns a non-empty
159
+ * annotations object synchronously.
159
160
  *
160
161
  * @param context The source context to check.
161
162
  * @returns `true` if the context is static, `false` otherwise.
package/dist/context.js CHANGED
@@ -3,8 +3,9 @@
3
3
  * Checks whether a context is static (returns annotations without needing
4
4
  * parsed results).
5
5
  *
6
- * A context is considered static if `getAnnotations()` called without
7
- * arguments returns a non-empty annotations object synchronously.
6
+ * A context is considered static if it declares `mode: "static"` or if
7
+ * `getAnnotations()` called without arguments returns a non-empty
8
+ * annotations object synchronously.
8
9
  *
9
10
  * @param context The source context to check.
10
11
  * @returns `true` if the context is static, `false` otherwise.
package/dist/facade.cjs CHANGED
@@ -870,13 +870,8 @@ async function collectPhase1Annotations(contexts, options) {
870
870
  let hasDynamic = false;
871
871
  for (const context of contexts) {
872
872
  const result = context.getAnnotations(void 0, options);
873
- if (result instanceof Promise) {
874
- hasDynamic = true;
875
- annotationsList.push(await result);
876
- } else {
877
- if (Object.getOwnPropertySymbols(result).length === 0) hasDynamic = true;
878
- annotationsList.push(result);
879
- }
873
+ hasDynamic ||= needsTwoPhaseContext(context, result);
874
+ annotationsList.push(result instanceof Promise ? await result : result);
880
875
  }
881
876
  return {
882
877
  annotations: mergeAnnotations(annotationsList),
@@ -914,7 +909,7 @@ function collectPhase1AnnotationsSync(contexts, options) {
914
909
  for (const context of contexts) {
915
910
  const result = context.getAnnotations(void 0, options);
916
911
  if (result instanceof Promise) throw new Error(`Context ${String(context.id)} returned a Promise in sync mode. Use runWith() or runWithAsync() for async contexts.`);
917
- if (Object.getOwnPropertySymbols(result).length === 0) hasDynamic = true;
912
+ hasDynamic ||= needsTwoPhaseContext(context, result);
918
913
  annotationsList.push(result);
919
914
  }
920
915
  return {
@@ -923,6 +918,18 @@ function collectPhase1AnnotationsSync(contexts, options) {
923
918
  };
924
919
  }
925
920
  /**
921
+ * Determines whether a context requires a second parse pass.
922
+ *
923
+ * Explicit `mode` declarations take precedence over legacy heuristics so
924
+ * static contexts are not forced into two-phase parsing when they return
925
+ * empty annotations or a Promise.
926
+ */
927
+ function needsTwoPhaseContext(context, result) {
928
+ if (context.mode !== void 0) return context.mode === "dynamic";
929
+ if (result instanceof Promise) return true;
930
+ return Object.getOwnPropertySymbols(result).length === 0;
931
+ }
932
+ /**
926
933
  * Collects annotations from all contexts synchronously.
927
934
  *
928
935
  * @param contexts Source contexts to collect annotations from.
@@ -1122,10 +1129,7 @@ function runWithAsync(parser, programName, contexts, options) {
1122
1129
  * @returns A new parser with annotations in its initial state.
1123
1130
  */
1124
1131
  function injectAnnotationsIntoParser(parser, annotations) {
1125
- const newInitialState = {
1126
- ...typeof parser.initialState === "object" && parser.initialState !== null ? parser.initialState : {},
1127
- [require_annotations.annotationKey]: annotations
1128
- };
1132
+ const newInitialState = require_annotations.injectAnnotations(parser.initialState, annotations);
1129
1133
  return {
1130
1134
  ...parser,
1131
1135
  initialState: newInitialState