@optique/core 1.0.0-dev.921 → 1.0.1

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.
Files changed (109) hide show
  1. package/dist/annotation-state.cjs +425 -0
  2. package/dist/annotation-state.d.cts +24 -0
  3. package/dist/annotation-state.d.ts +24 -0
  4. package/dist/annotation-state.js +414 -0
  5. package/dist/annotations.cjs +2 -248
  6. package/dist/annotations.d.cts +2 -137
  7. package/dist/annotations.d.ts +2 -137
  8. package/dist/annotations.js +2 -238
  9. package/dist/completion.cjs +611 -100
  10. package/dist/completion.d.cts +1 -1
  11. package/dist/completion.d.ts +1 -1
  12. package/dist/completion.js +611 -100
  13. package/dist/constructs.cjs +3338 -827
  14. package/dist/constructs.d.cts +48 -7
  15. package/dist/constructs.d.ts +48 -7
  16. package/dist/constructs.js +3338 -827
  17. package/dist/context.cjs +0 -23
  18. package/dist/context.d.cts +119 -53
  19. package/dist/context.d.ts +119 -53
  20. package/dist/context.js +0 -22
  21. package/dist/dependency-metadata.cjs +139 -0
  22. package/dist/dependency-metadata.d.cts +112 -0
  23. package/dist/dependency-metadata.d.ts +112 -0
  24. package/dist/dependency-metadata.js +138 -0
  25. package/dist/dependency-runtime.cjs +698 -0
  26. package/dist/dependency-runtime.d.cts +149 -0
  27. package/dist/dependency-runtime.d.ts +149 -0
  28. package/dist/dependency-runtime.js +687 -0
  29. package/dist/dependency.cjs +7 -928
  30. package/dist/dependency.d.cts +2 -794
  31. package/dist/dependency.d.ts +2 -794
  32. package/dist/dependency.js +2 -899
  33. package/dist/displaywidth.cjs +44 -0
  34. package/dist/displaywidth.js +43 -0
  35. package/dist/doc.cjs +285 -23
  36. package/dist/doc.d.cts +57 -2
  37. package/dist/doc.d.ts +57 -2
  38. package/dist/doc.js +283 -25
  39. package/dist/execution-context.cjs +56 -0
  40. package/dist/execution-context.js +53 -0
  41. package/dist/extension.cjs +87 -0
  42. package/dist/extension.d.cts +97 -0
  43. package/dist/extension.d.ts +97 -0
  44. package/dist/extension.js +76 -0
  45. package/dist/facade.cjs +718 -525
  46. package/dist/facade.d.cts +59 -15
  47. package/dist/facade.d.ts +59 -15
  48. package/dist/facade.js +718 -525
  49. package/dist/index.cjs +14 -29
  50. package/dist/index.d.cts +10 -10
  51. package/dist/index.d.ts +10 -10
  52. package/dist/index.js +7 -7
  53. package/dist/input-trace.cjs +56 -0
  54. package/dist/input-trace.d.cts +77 -0
  55. package/dist/input-trace.d.ts +77 -0
  56. package/dist/input-trace.js +55 -0
  57. package/dist/internal/annotations.cjs +316 -0
  58. package/dist/internal/annotations.d.cts +140 -0
  59. package/dist/internal/annotations.d.ts +140 -0
  60. package/dist/internal/annotations.js +306 -0
  61. package/dist/internal/dependency.cjs +984 -0
  62. package/dist/internal/dependency.d.cts +539 -0
  63. package/dist/internal/dependency.d.ts +539 -0
  64. package/dist/internal/dependency.js +964 -0
  65. package/dist/{mode-dispatch.cjs → internal/mode-dispatch.cjs} +1 -3
  66. package/dist/{mode-dispatch.d.cts → internal/mode-dispatch.d.cts} +3 -7
  67. package/dist/{mode-dispatch.d.ts → internal/mode-dispatch.d.ts} +3 -7
  68. package/dist/{mode-dispatch.js → internal/mode-dispatch.js} +1 -3
  69. package/dist/internal/parser.cjs +728 -0
  70. package/dist/internal/parser.d.cts +947 -0
  71. package/dist/internal/parser.d.ts +947 -0
  72. package/dist/internal/parser.js +711 -0
  73. package/dist/message.cjs +84 -26
  74. package/dist/message.d.cts +49 -9
  75. package/dist/message.d.ts +49 -9
  76. package/dist/message.js +84 -27
  77. package/dist/modifiers.cjs +1023 -240
  78. package/dist/modifiers.d.cts +42 -1
  79. package/dist/modifiers.d.ts +42 -1
  80. package/dist/modifiers.js +1023 -240
  81. package/dist/parser.cjs +11 -463
  82. package/dist/parser.d.cts +3 -537
  83. package/dist/parser.d.ts +3 -537
  84. package/dist/parser.js +2 -433
  85. package/dist/phase2-seed.cjs +59 -0
  86. package/dist/phase2-seed.js +56 -0
  87. package/dist/primitives.cjs +557 -208
  88. package/dist/primitives.d.cts +10 -14
  89. package/dist/primitives.d.ts +10 -14
  90. package/dist/primitives.js +557 -208
  91. package/dist/program.cjs +5 -1
  92. package/dist/program.d.cts +5 -3
  93. package/dist/program.d.ts +5 -3
  94. package/dist/program.js +6 -1
  95. package/dist/suggestion.cjs +22 -8
  96. package/dist/suggestion.js +22 -8
  97. package/dist/usage-internals.cjs +3 -2
  98. package/dist/usage-internals.js +4 -2
  99. package/dist/usage.cjs +195 -40
  100. package/dist/usage.d.cts +92 -11
  101. package/dist/usage.d.ts +92 -11
  102. package/dist/usage.js +194 -41
  103. package/dist/validate.cjs +170 -0
  104. package/dist/validate.js +164 -0
  105. package/dist/valueparser.cjs +1270 -187
  106. package/dist/valueparser.d.cts +320 -14
  107. package/dist/valueparser.d.ts +320 -14
  108. package/dist/valueparser.js +1269 -188
  109. package/package.json +9 -9
package/dist/context.cjs CHANGED
@@ -1,23 +0,0 @@
1
-
2
- //#region src/context.ts
3
- /**
4
- * Checks whether a context is static (returns annotations without needing
5
- * parsed results).
6
- *
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.
10
- *
11
- * @param context The source context to check.
12
- * @returns `true` if the context is static, `false` otherwise.
13
- * @since 0.10.0
14
- */
15
- function isStaticContext(context) {
16
- if (context.mode !== void 0) return context.mode === "static";
17
- const result = context.getAnnotations();
18
- if (result instanceof Promise) return false;
19
- return Object.getOwnPropertySymbols(result).length > 0;
20
- }
21
-
22
- //#endregion
23
- exports.isStaticContext = isStaticContext;
@@ -1,18 +1,57 @@
1
- import { Annotations } from "./annotations.cjs";
1
+ import { Annotations } from "./internal/annotations.cjs";
2
2
 
3
3
  //#region src/context.d.ts
4
4
 
5
5
  /**
6
- * Declares whether a {@link SourceContext} provides its annotations
7
- * immediately (`"static"`) or only after a prior parse pass (`"dynamic"`).
6
+ * Declares whether a {@link SourceContext} participates only in the initial
7
+ * annotation collection (`"single-pass"`) or is recollected after a usable
8
+ * first parse pass (`"two-pass"`).
8
9
  *
9
- * Used as the type of the optional `mode` field on {@link SourceContext}.
10
- * When set, {@link isStaticContext} reads this value directly instead of
11
- * calling `getAnnotations()`, preventing any side effects.
10
+ * Used as the type of the required `phase` field on {@link SourceContext}.
12
11
  *
13
12
  * @since 1.0.0
14
13
  */
15
- type SourceContextMode = "static" | "dynamic";
14
+ type SourceContextPhase = "single-pass" | "two-pass";
15
+ /**
16
+ * Phase-1 annotation collection request for a {@link SourceContext}.
17
+ *
18
+ * @since 1.0.0
19
+ */
20
+ interface SourceContextPhase1Request {
21
+ /**
22
+ * Indicates that the runner is collecting initial annotations before the
23
+ * first parse pass.
24
+ */
25
+ readonly phase: "phase1";
26
+ }
27
+ /**
28
+ * Phase-2 annotation collection request for a {@link SourceContext}.
29
+ *
30
+ * @since 1.0.0
31
+ */
32
+ interface SourceContextPhase2Request {
33
+ /**
34
+ * Indicates that the runner is recollecting annotations after a usable
35
+ * first parse pass.
36
+ */
37
+ readonly phase: "phase2";
38
+ /**
39
+ * Parsed result from the first pass, or a best-effort partial value
40
+ * extracted from parser state when the first pass reached a usable
41
+ * intermediate state but did not complete successfully.
42
+ */
43
+ readonly parsed: unknown;
44
+ }
45
+ /**
46
+ * Request object passed to {@link SourceContext.getAnnotations} and
47
+ * {@link SourceContext.getInternalAnnotations}.
48
+ *
49
+ * This makes phase 1 and phase 2 explicit so successful parser results of
50
+ * `undefined` are no longer ambiguous.
51
+ *
52
+ * @since 1.0.0
53
+ */
54
+ type SourceContextRequest = SourceContextPhase1Request | SourceContextPhase2Request;
16
55
  /**
17
56
  * Brand symbol for ParserValuePlaceholder type.
18
57
  * @internal
@@ -50,16 +89,20 @@ type ParserValuePlaceholder = {
50
89
  /**
51
90
  * A source context that can provide data to parsers via annotations.
52
91
  *
53
- * Source contexts are used to inject external data (like environment variables
54
- * or config files) into the parsing process. They can be either:
92
+ * Source contexts are used to inject external data (like environment
93
+ * variables or config files) into the parsing process. They can be either:
55
94
  *
56
- * - *Static*: Data is immediately available (e.g., environment variables)
57
- * - *Dynamic*: Data depends on parsing results (e.g., config files whose path
58
- * is determined by a CLI option)
95
+ * - *Single-pass*: The runner collects annotations once before parsing
96
+ * (e.g., environment variables)
97
+ * - *Two-pass*: The runner collects annotations before parsing and then
98
+ * recollects them after a usable first parse pass (e.g., config files whose
99
+ * path is determined by a CLI option)
59
100
  *
60
101
  * Contexts may optionally implement `Disposable` or `AsyncDisposable` for
61
102
  * cleanup. When present, `runWith()` and `runWithSync()` call the dispose
62
- * method in a `finally` block after parsing completes.
103
+ * method after parsing completes. In async runners, cleanup waits for the
104
+ * full `runWith()` Promise to settle, including later asynchronous
105
+ * `complete()` work.
63
106
  *
64
107
  * @template TRequiredOptions Additional options that `runWith()` must provide
65
108
  * when this context is used. Use `void` (the default) for contexts that
@@ -68,9 +111,10 @@ type ParserValuePlaceholder = {
68
111
  *
69
112
  * @example
70
113
  * ```typescript
71
- * // Static context example (environment variables) - no extra options needed
114
+ * // Single-pass context example (environment variables)
72
115
  * const envContext: SourceContext = {
73
116
  * id: Symbol.for("@myapp/env"),
117
+ * phase: "single-pass",
74
118
  * getAnnotations() {
75
119
  * return {
76
120
  * [Symbol.for("@myapp/env")]: {
@@ -81,7 +125,7 @@ type ParserValuePlaceholder = {
81
125
  * }
82
126
  * };
83
127
  *
84
- * // Dynamic context that requires options from runWith()
128
+ * // Two-pass context that requires options from runWith()
85
129
  * interface ConfigContext extends SourceContext<{
86
130
  * getConfigPath: (parsed: ParserValuePlaceholder) => string | undefined;
87
131
  * }> {
@@ -96,7 +140,9 @@ interface SourceContext<TRequiredOptions = void> {
96
140
  * Unique identifier for this context.
97
141
  *
98
142
  * This symbol is typically the same as the annotation key used by parsers
99
- * that consume this context's data.
143
+ * that consume this context's data. Passing multiple contexts with the
144
+ * same id to {@link runWith}, {@link runWithSync}, or {@link runWithAsync}
145
+ * throws a `TypeError`.
100
146
  */
101
147
  readonly id: symbol;
102
148
  /**
@@ -105,65 +151,85 @@ interface SourceContext<TRequiredOptions = void> {
105
151
  */
106
152
  readonly $requiredOptions?: TRequiredOptions;
107
153
  /**
108
- * Optional declaration of whether this context is static or dynamic.
154
+ * Declares whether this context is collected once or recollected after a
155
+ * usable first parse pass.
109
156
  *
110
- * When present, {@link isStaticContext} reads this field directly instead
111
- * of calling {@link getAnnotations}, avoiding any side effects that
112
- * `getAnnotations` might have (such as mutating a global registry).
113
- *
114
- * If omitted, {@link isStaticContext} falls back to calling
115
- * `getAnnotations()` with no arguments to determine static-ness.
157
+ * `single-pass` contexts contribute only their phase-1 annotations to the
158
+ * final parse. `two-pass` contexts are called again with the first-pass
159
+ * parsed value (or a best-effort seed extracted from parser state) and that
160
+ * second return value becomes the context's final annotation snapshot.
116
161
  *
117
162
  * @since 1.0.0
118
163
  */
119
- readonly mode?: SourceContextMode;
164
+ readonly phase: SourceContextPhase;
120
165
  /**
121
166
  * Get annotations to inject into parsing.
122
167
  *
123
- * This method is called twice during `runWith()` execution:
168
+ * This method is called during phase 1 for every context and during phase 2
169
+ * only for `two-pass` contexts:
170
+ *
171
+ * 1. *Phase 1*: `request.phase` is `"phase1"`.
172
+ * 2. *Phase 2*: `request.phase` is `"phase2"` and `request.parsed`
173
+ * contains the first pass result, or a best-effort partial value
174
+ * extracted from parser state when the first pass reached a usable
175
+ * intermediate state but still did not complete successfully.
176
+ * Deferred or otherwise unresolved fields may be `undefined`. This
177
+ * second return value is treated as the context's final annotation
178
+ * snapshot for the second parse pass, replacing that context's phase-one
179
+ * contribution. If the runner cannot extract a usable value at all, this
180
+ * second call is skipped and the original parse failure is reported
181
+ * instead.
124
182
  *
125
- * 1. *First call*: `parsed` is `undefined`. Static contexts should return
126
- * their annotations, while dynamic contexts should return an empty object.
127
- * 2. *Second call*: `parsed` contains the result from the first parse pass.
128
- * Dynamic contexts can use this to load external data (e.g., reading
129
- * a config file whose path was determined in the first pass).
183
+ * Omitting the request is treated as a manual phase-1 call for
184
+ * convenience, so `context.getAnnotations()` continues to work for
185
+ * simple one-shot annotation reads.
130
186
  *
131
- * @param parsed Optional parsed result from a previous parse pass.
132
- * Static contexts can ignore this parameter.
133
- * Dynamic contexts use this to extract necessary data.
187
+ * @param request Optional request describing which collection phase the
188
+ * runner is performing. `single-pass` contexts can ignore
189
+ * this parameter. `two-pass` contexts should branch on
190
+ * `request.phase` rather than inferring phases from
191
+ * `request.parsed`.
134
192
  * @param options Optional context-required options provided by the caller
135
193
  * of `runWith()`. These are the options declared via the
136
194
  * `TRequiredOptions` type parameter.
137
- * @returns Annotations to merge into the parsing session. Can be a Promise
138
- * for async operations (e.g., loading config files).
195
+ * @returns Annotations to merge into the parsing session. During phase 2,
196
+ * returning `{}` clears any annotations this context contributed
197
+ * during phase 1. Can be a Promise for async operations (e.g.,
198
+ * loading config files).
199
+ */
200
+ getAnnotations(request?: SourceContextRequest, options?: unknown): Promise<Annotations> | Annotations;
201
+ /**
202
+ * Optional hook to provide additional internal annotations during
203
+ * annotation collection. Called after {@link getAnnotations} with the
204
+ * same request object and the annotations returned by `getAnnotations()`.
205
+ *
206
+ * Returns additional annotations to merge, or `undefined` to add nothing.
207
+ * This enables contexts to inject phase-specific markers without
208
+ * exposing them through the primary `getAnnotations()` API.
209
+ *
210
+ * @param request The request describing the current collection phase.
211
+ * @param annotations The annotations returned by `getAnnotations()`.
212
+ * @returns Additional annotations to merge, or `undefined`.
213
+ * @since 1.0.0
139
214
  */
140
- getAnnotations(parsed?: unknown, options?: unknown): Promise<Annotations> | Annotations;
215
+ getInternalAnnotations?(request: SourceContextRequest, annotations: Annotations): Annotations | undefined;
141
216
  /**
142
217
  * Optional synchronous cleanup method. Called by `runWith()` and
143
- * `runWithSync()` in a `finally` block after parsing completes.
218
+ * `runWithSync()` after parsing completes. In `runWith()`, this happens
219
+ * only after the full Promise settles, including any later async
220
+ * `complete()` work.
144
221
  */
145
222
  [Symbol.dispose]?(): void;
146
223
  /**
147
- * Optional asynchronous cleanup method. Called by `runWith()` in a
148
- * `finally` block after parsing completes. Takes precedence over
224
+ * Optional asynchronous cleanup method. Called by `runWith()` after
225
+ * parsing completes. Cleanup starts only after the full `runWith()`
226
+ * Promise settles, including any later async `complete()` work. Takes
227
+ * precedence over
149
228
  * `[Symbol.dispose]` in async runners. `runWithSync()` also calls this
150
229
  * method when `[Symbol.dispose]` is absent, but throws if it returns a
151
230
  * Promise.
152
231
  */
153
232
  [Symbol.asyncDispose]?(): void | PromiseLike<void>;
154
233
  }
155
- /**
156
- * Checks whether a context is static (returns annotations without needing
157
- * parsed results).
158
- *
159
- * A context is considered static if it declares `mode: "static"` or if
160
- * `getAnnotations()` called without arguments returns a non-empty
161
- * annotations object synchronously.
162
- *
163
- * @param context The source context to check.
164
- * @returns `true` if the context is static, `false` otherwise.
165
- * @since 0.10.0
166
- */
167
- declare function isStaticContext(context: SourceContext<unknown>): boolean;
168
234
  //#endregion
169
- export { type Annotations, ParserValuePlaceholder, SourceContext, SourceContextMode, isStaticContext };
235
+ export { type Annotations, ParserValuePlaceholder, SourceContext, SourceContextPhase, SourceContextPhase1Request, SourceContextPhase2Request, SourceContextRequest };
package/dist/context.d.ts CHANGED
@@ -1,18 +1,57 @@
1
- import { Annotations } from "./annotations.js";
1
+ import { Annotations } from "./internal/annotations.js";
2
2
 
3
3
  //#region src/context.d.ts
4
4
 
5
5
  /**
6
- * Declares whether a {@link SourceContext} provides its annotations
7
- * immediately (`"static"`) or only after a prior parse pass (`"dynamic"`).
6
+ * Declares whether a {@link SourceContext} participates only in the initial
7
+ * annotation collection (`"single-pass"`) or is recollected after a usable
8
+ * first parse pass (`"two-pass"`).
8
9
  *
9
- * Used as the type of the optional `mode` field on {@link SourceContext}.
10
- * When set, {@link isStaticContext} reads this value directly instead of
11
- * calling `getAnnotations()`, preventing any side effects.
10
+ * Used as the type of the required `phase` field on {@link SourceContext}.
12
11
  *
13
12
  * @since 1.0.0
14
13
  */
15
- type SourceContextMode = "static" | "dynamic";
14
+ type SourceContextPhase = "single-pass" | "two-pass";
15
+ /**
16
+ * Phase-1 annotation collection request for a {@link SourceContext}.
17
+ *
18
+ * @since 1.0.0
19
+ */
20
+ interface SourceContextPhase1Request {
21
+ /**
22
+ * Indicates that the runner is collecting initial annotations before the
23
+ * first parse pass.
24
+ */
25
+ readonly phase: "phase1";
26
+ }
27
+ /**
28
+ * Phase-2 annotation collection request for a {@link SourceContext}.
29
+ *
30
+ * @since 1.0.0
31
+ */
32
+ interface SourceContextPhase2Request {
33
+ /**
34
+ * Indicates that the runner is recollecting annotations after a usable
35
+ * first parse pass.
36
+ */
37
+ readonly phase: "phase2";
38
+ /**
39
+ * Parsed result from the first pass, or a best-effort partial value
40
+ * extracted from parser state when the first pass reached a usable
41
+ * intermediate state but did not complete successfully.
42
+ */
43
+ readonly parsed: unknown;
44
+ }
45
+ /**
46
+ * Request object passed to {@link SourceContext.getAnnotations} and
47
+ * {@link SourceContext.getInternalAnnotations}.
48
+ *
49
+ * This makes phase 1 and phase 2 explicit so successful parser results of
50
+ * `undefined` are no longer ambiguous.
51
+ *
52
+ * @since 1.0.0
53
+ */
54
+ type SourceContextRequest = SourceContextPhase1Request | SourceContextPhase2Request;
16
55
  /**
17
56
  * Brand symbol for ParserValuePlaceholder type.
18
57
  * @internal
@@ -50,16 +89,20 @@ type ParserValuePlaceholder = {
50
89
  /**
51
90
  * A source context that can provide data to parsers via annotations.
52
91
  *
53
- * Source contexts are used to inject external data (like environment variables
54
- * or config files) into the parsing process. They can be either:
92
+ * Source contexts are used to inject external data (like environment
93
+ * variables or config files) into the parsing process. They can be either:
55
94
  *
56
- * - *Static*: Data is immediately available (e.g., environment variables)
57
- * - *Dynamic*: Data depends on parsing results (e.g., config files whose path
58
- * is determined by a CLI option)
95
+ * - *Single-pass*: The runner collects annotations once before parsing
96
+ * (e.g., environment variables)
97
+ * - *Two-pass*: The runner collects annotations before parsing and then
98
+ * recollects them after a usable first parse pass (e.g., config files whose
99
+ * path is determined by a CLI option)
59
100
  *
60
101
  * Contexts may optionally implement `Disposable` or `AsyncDisposable` for
61
102
  * cleanup. When present, `runWith()` and `runWithSync()` call the dispose
62
- * method in a `finally` block after parsing completes.
103
+ * method after parsing completes. In async runners, cleanup waits for the
104
+ * full `runWith()` Promise to settle, including later asynchronous
105
+ * `complete()` work.
63
106
  *
64
107
  * @template TRequiredOptions Additional options that `runWith()` must provide
65
108
  * when this context is used. Use `void` (the default) for contexts that
@@ -68,9 +111,10 @@ type ParserValuePlaceholder = {
68
111
  *
69
112
  * @example
70
113
  * ```typescript
71
- * // Static context example (environment variables) - no extra options needed
114
+ * // Single-pass context example (environment variables)
72
115
  * const envContext: SourceContext = {
73
116
  * id: Symbol.for("@myapp/env"),
117
+ * phase: "single-pass",
74
118
  * getAnnotations() {
75
119
  * return {
76
120
  * [Symbol.for("@myapp/env")]: {
@@ -81,7 +125,7 @@ type ParserValuePlaceholder = {
81
125
  * }
82
126
  * };
83
127
  *
84
- * // Dynamic context that requires options from runWith()
128
+ * // Two-pass context that requires options from runWith()
85
129
  * interface ConfigContext extends SourceContext<{
86
130
  * getConfigPath: (parsed: ParserValuePlaceholder) => string | undefined;
87
131
  * }> {
@@ -96,7 +140,9 @@ interface SourceContext<TRequiredOptions = void> {
96
140
  * Unique identifier for this context.
97
141
  *
98
142
  * This symbol is typically the same as the annotation key used by parsers
99
- * that consume this context's data.
143
+ * that consume this context's data. Passing multiple contexts with the
144
+ * same id to {@link runWith}, {@link runWithSync}, or {@link runWithAsync}
145
+ * throws a `TypeError`.
100
146
  */
101
147
  readonly id: symbol;
102
148
  /**
@@ -105,65 +151,85 @@ interface SourceContext<TRequiredOptions = void> {
105
151
  */
106
152
  readonly $requiredOptions?: TRequiredOptions;
107
153
  /**
108
- * Optional declaration of whether this context is static or dynamic.
154
+ * Declares whether this context is collected once or recollected after a
155
+ * usable first parse pass.
109
156
  *
110
- * When present, {@link isStaticContext} reads this field directly instead
111
- * of calling {@link getAnnotations}, avoiding any side effects that
112
- * `getAnnotations` might have (such as mutating a global registry).
113
- *
114
- * If omitted, {@link isStaticContext} falls back to calling
115
- * `getAnnotations()` with no arguments to determine static-ness.
157
+ * `single-pass` contexts contribute only their phase-1 annotations to the
158
+ * final parse. `two-pass` contexts are called again with the first-pass
159
+ * parsed value (or a best-effort seed extracted from parser state) and that
160
+ * second return value becomes the context's final annotation snapshot.
116
161
  *
117
162
  * @since 1.0.0
118
163
  */
119
- readonly mode?: SourceContextMode;
164
+ readonly phase: SourceContextPhase;
120
165
  /**
121
166
  * Get annotations to inject into parsing.
122
167
  *
123
- * This method is called twice during `runWith()` execution:
168
+ * This method is called during phase 1 for every context and during phase 2
169
+ * only for `two-pass` contexts:
170
+ *
171
+ * 1. *Phase 1*: `request.phase` is `"phase1"`.
172
+ * 2. *Phase 2*: `request.phase` is `"phase2"` and `request.parsed`
173
+ * contains the first pass result, or a best-effort partial value
174
+ * extracted from parser state when the first pass reached a usable
175
+ * intermediate state but still did not complete successfully.
176
+ * Deferred or otherwise unresolved fields may be `undefined`. This
177
+ * second return value is treated as the context's final annotation
178
+ * snapshot for the second parse pass, replacing that context's phase-one
179
+ * contribution. If the runner cannot extract a usable value at all, this
180
+ * second call is skipped and the original parse failure is reported
181
+ * instead.
124
182
  *
125
- * 1. *First call*: `parsed` is `undefined`. Static contexts should return
126
- * their annotations, while dynamic contexts should return an empty object.
127
- * 2. *Second call*: `parsed` contains the result from the first parse pass.
128
- * Dynamic contexts can use this to load external data (e.g., reading
129
- * a config file whose path was determined in the first pass).
183
+ * Omitting the request is treated as a manual phase-1 call for
184
+ * convenience, so `context.getAnnotations()` continues to work for
185
+ * simple one-shot annotation reads.
130
186
  *
131
- * @param parsed Optional parsed result from a previous parse pass.
132
- * Static contexts can ignore this parameter.
133
- * Dynamic contexts use this to extract necessary data.
187
+ * @param request Optional request describing which collection phase the
188
+ * runner is performing. `single-pass` contexts can ignore
189
+ * this parameter. `two-pass` contexts should branch on
190
+ * `request.phase` rather than inferring phases from
191
+ * `request.parsed`.
134
192
  * @param options Optional context-required options provided by the caller
135
193
  * of `runWith()`. These are the options declared via the
136
194
  * `TRequiredOptions` type parameter.
137
- * @returns Annotations to merge into the parsing session. Can be a Promise
138
- * for async operations (e.g., loading config files).
195
+ * @returns Annotations to merge into the parsing session. During phase 2,
196
+ * returning `{}` clears any annotations this context contributed
197
+ * during phase 1. Can be a Promise for async operations (e.g.,
198
+ * loading config files).
199
+ */
200
+ getAnnotations(request?: SourceContextRequest, options?: unknown): Promise<Annotations> | Annotations;
201
+ /**
202
+ * Optional hook to provide additional internal annotations during
203
+ * annotation collection. Called after {@link getAnnotations} with the
204
+ * same request object and the annotations returned by `getAnnotations()`.
205
+ *
206
+ * Returns additional annotations to merge, or `undefined` to add nothing.
207
+ * This enables contexts to inject phase-specific markers without
208
+ * exposing them through the primary `getAnnotations()` API.
209
+ *
210
+ * @param request The request describing the current collection phase.
211
+ * @param annotations The annotations returned by `getAnnotations()`.
212
+ * @returns Additional annotations to merge, or `undefined`.
213
+ * @since 1.0.0
139
214
  */
140
- getAnnotations(parsed?: unknown, options?: unknown): Promise<Annotations> | Annotations;
215
+ getInternalAnnotations?(request: SourceContextRequest, annotations: Annotations): Annotations | undefined;
141
216
  /**
142
217
  * Optional synchronous cleanup method. Called by `runWith()` and
143
- * `runWithSync()` in a `finally` block after parsing completes.
218
+ * `runWithSync()` after parsing completes. In `runWith()`, this happens
219
+ * only after the full Promise settles, including any later async
220
+ * `complete()` work.
144
221
  */
145
222
  [Symbol.dispose]?(): void;
146
223
  /**
147
- * Optional asynchronous cleanup method. Called by `runWith()` in a
148
- * `finally` block after parsing completes. Takes precedence over
224
+ * Optional asynchronous cleanup method. Called by `runWith()` after
225
+ * parsing completes. Cleanup starts only after the full `runWith()`
226
+ * Promise settles, including any later async `complete()` work. Takes
227
+ * precedence over
149
228
  * `[Symbol.dispose]` in async runners. `runWithSync()` also calls this
150
229
  * method when `[Symbol.dispose]` is absent, but throws if it returns a
151
230
  * Promise.
152
231
  */
153
232
  [Symbol.asyncDispose]?(): void | PromiseLike<void>;
154
233
  }
155
- /**
156
- * Checks whether a context is static (returns annotations without needing
157
- * parsed results).
158
- *
159
- * A context is considered static if it declares `mode: "static"` or if
160
- * `getAnnotations()` called without arguments returns a non-empty
161
- * annotations object synchronously.
162
- *
163
- * @param context The source context to check.
164
- * @returns `true` if the context is static, `false` otherwise.
165
- * @since 0.10.0
166
- */
167
- declare function isStaticContext(context: SourceContext<unknown>): boolean;
168
234
  //#endregion
169
- export { type Annotations, ParserValuePlaceholder, SourceContext, SourceContextMode, isStaticContext };
235
+ export { type Annotations, ParserValuePlaceholder, SourceContext, SourceContextPhase, SourceContextPhase1Request, SourceContextPhase2Request, SourceContextRequest };
package/dist/context.js CHANGED
@@ -1,22 +0,0 @@
1
- //#region src/context.ts
2
- /**
3
- * Checks whether a context is static (returns annotations without needing
4
- * parsed results).
5
- *
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.
9
- *
10
- * @param context The source context to check.
11
- * @returns `true` if the context is static, `false` otherwise.
12
- * @since 0.10.0
13
- */
14
- function isStaticContext(context) {
15
- if (context.mode !== void 0) return context.mode === "static";
16
- const result = context.getAnnotations();
17
- if (result instanceof Promise) return false;
18
- return Object.getOwnPropertySymbols(result).length > 0;
19
- }
20
-
21
- //#endregion
22
- export { isStaticContext };