@optique/config 0.10.7-dev.485 → 1.0.0-dev.1109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,11 +29,11 @@ Quick start
29
29
  ~~~~ typescript
30
30
  import { z } from "zod";
31
31
  import { createConfigContext, bindConfig } from "@optique/config";
32
- import { runWithConfig } from "@optique/config/run";
33
32
  import { object } from "@optique/core/constructs";
34
33
  import { option } from "@optique/core/primitives";
35
34
  import { string, integer } from "@optique/core/valueparser";
36
35
  import { withDefault } from "@optique/core/modifiers";
36
+ import { runAsync } from "@optique/run";
37
37
 
38
38
  // 1. Define config schema
39
39
  const configSchema = z.object({
@@ -58,8 +58,9 @@ const parser = object({
58
58
  }),
59
59
  });
60
60
 
61
- // 3. Run with config support
62
- const result = await runWithConfig(parser, configContext, {
61
+ // 3. Run with config support via contexts
62
+ const result = await runAsync(parser, {
63
+ contexts: [configContext],
63
64
  getConfigPath: (parsed) => parsed.config,
64
65
  args: process.argv.slice(2),
65
66
  });
package/dist/index.cjs CHANGED
@@ -1,8 +1,617 @@
1
- const require_src = require('./src-9MkUoh9z.cjs');
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
2
22
 
3
- exports.bindConfig = require_src.bindConfig;
4
- exports.clearActiveConfig = require_src.clearActiveConfig;
5
- exports.configKey = require_src.configKey;
6
- exports.createConfigContext = require_src.createConfigContext;
7
- exports.getActiveConfig = require_src.getActiveConfig;
8
- exports.setActiveConfig = require_src.setActiveConfig;
23
+ //#endregion
24
+ const node_fs = __toESM(require("node:fs"));
25
+ const node_path = __toESM(require("node:path"));
26
+ const __optique_core_annotations = __toESM(require("@optique/core/annotations"));
27
+ const __optique_core_context = __toESM(require("@optique/core/context"));
28
+ const __optique_core_message = __toESM(require("@optique/core/message"));
29
+ const __optique_core_mode_dispatch = __toESM(require("@optique/core/mode-dispatch"));
30
+
31
+ //#region src/index.ts
32
+ const phase2UndefinedParsedValueKey = Symbol("@optique/config/phase2UndefinedParsedValue");
33
+ /**
34
+ * Internal registry for active config data during config context execution.
35
+ * This is a workaround for the limitation that object() doesn't propagate
36
+ * annotations to child field parsers.
37
+ * @internal
38
+ */
39
+ const activeConfigRegistry = /* @__PURE__ */ new Map();
40
+ /**
41
+ * Internal registry for active config metadata during config context execution.
42
+ * @internal
43
+ */
44
+ const activeConfigMetaRegistry = /* @__PURE__ */ new Map();
45
+ const phase1ConfigAnnotationMarker = Symbol("@optique/config/phase1Annotation");
46
+ function isDeferredPromptValue(value) {
47
+ return (0, __optique_core_context.isPlaceholderValue)(value);
48
+ }
49
+ function isPhase2UndefinedParsedValue(value) {
50
+ return value != null && typeof value === "object" && phase2UndefinedParsedValueKey in value;
51
+ }
52
+ function isPlainObject(value) {
53
+ const proto = Object.getPrototypeOf(value);
54
+ return proto === Object.prototype || proto === null;
55
+ }
56
+ function shouldSkipCollectionOwnKey(value, key) {
57
+ if (Array.isArray(value)) return key === "length" || typeof key === "string" && Number.isInteger(Number(key)) && String(Number(key)) === key;
58
+ return false;
59
+ }
60
+ function containsDeferredPromptValuesInOwnProperties(value, seen) {
61
+ for (const key of Reflect.ownKeys(value)) {
62
+ if (shouldSkipCollectionOwnKey(value, key)) continue;
63
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
64
+ if (descriptor != null && "value" in descriptor && containsDeferredPromptValues(descriptor.value, seen)) return true;
65
+ }
66
+ return false;
67
+ }
68
+ function copySanitizedOwnProperties(source, target, seen) {
69
+ for (const key of Reflect.ownKeys(source)) {
70
+ if (shouldSkipCollectionOwnKey(source, key)) continue;
71
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
72
+ if (descriptor == null) continue;
73
+ if ("value" in descriptor) descriptor.value = stripDeferredPromptValues(descriptor.value, seen);
74
+ Object.defineProperty(target, key, descriptor);
75
+ }
76
+ }
77
+ function containsDeferredPromptValues(value, seen = /* @__PURE__ */ new WeakSet()) {
78
+ if (isDeferredPromptValue(value)) return true;
79
+ if (value == null || typeof value !== "object") return false;
80
+ if (seen.has(value)) return false;
81
+ seen.add(value);
82
+ if (Array.isArray(value)) {
83
+ if (value.some((item) => containsDeferredPromptValues(item, seen))) return true;
84
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
85
+ }
86
+ if (value instanceof Set) {
87
+ for (const entryValue of value) if (containsDeferredPromptValues(entryValue, seen)) return true;
88
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
89
+ }
90
+ if (value instanceof Map) {
91
+ for (const [key, entryValue] of value) if (containsDeferredPromptValues(key, seen) || containsDeferredPromptValues(entryValue, seen)) return true;
92
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
93
+ }
94
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
95
+ }
96
+ const SANITIZE_FAILED = Symbol("sanitizeFailed");
97
+ const activeSanitizations = /* @__PURE__ */ new WeakMap();
98
+ function callWithSanitizedOwnProperties(target, fn, args, strip, seen) {
99
+ let active = activeSanitizations.get(target);
100
+ if (active != null) active.count++;
101
+ else {
102
+ const saved = /* @__PURE__ */ new Map();
103
+ const sanitizedValues = /* @__PURE__ */ new Map();
104
+ for (const key of Reflect.ownKeys(target)) {
105
+ const desc = Object.getOwnPropertyDescriptor(target, key);
106
+ if (desc != null && "value" in desc) {
107
+ let stripped;
108
+ try {
109
+ stripped = strip(desc.value, seen);
110
+ } catch {
111
+ for (const [k, d] of saved) try {
112
+ Object.defineProperty(target, k, d);
113
+ } catch {}
114
+ return SANITIZE_FAILED;
115
+ }
116
+ if (stripped !== desc.value) try {
117
+ Object.defineProperty(target, key, {
118
+ ...desc,
119
+ value: stripped
120
+ });
121
+ saved.set(key, desc);
122
+ sanitizedValues.set(key, stripped);
123
+ } catch {
124
+ for (const [k, d] of saved) try {
125
+ Object.defineProperty(target, k, d);
126
+ } catch {}
127
+ return SANITIZE_FAILED;
128
+ }
129
+ }
130
+ }
131
+ active = {
132
+ saved,
133
+ sanitizedValues,
134
+ count: 1
135
+ };
136
+ activeSanitizations.set(target, active);
137
+ }
138
+ function release() {
139
+ active.count--;
140
+ if (active.count === 0) {
141
+ activeSanitizations.delete(target);
142
+ for (const [key, desc] of active.saved) try {
143
+ const current = Object.getOwnPropertyDescriptor(target, key);
144
+ if (current == null) continue;
145
+ if ("value" in current && current.value !== active.sanitizedValues.get(key)) continue;
146
+ Object.defineProperty(target, key, desc);
147
+ } catch {}
148
+ }
149
+ }
150
+ let result;
151
+ try {
152
+ result = fn.apply(target, args);
153
+ } catch (e) {
154
+ release();
155
+ throw e;
156
+ }
157
+ if (result instanceof Promise) return result.then((v) => {
158
+ release();
159
+ return strip(v, seen);
160
+ }, (e) => {
161
+ release();
162
+ throw e;
163
+ });
164
+ release();
165
+ return strip(result, seen);
166
+ }
167
+ function callMethodOnSanitizedTarget(fn, proxy, target, args, strip, seen) {
168
+ const result = callWithSanitizedOwnProperties(target, fn, args, strip, seen);
169
+ if (result !== SANITIZE_FAILED) return result;
170
+ const fallback = fn.apply(proxy, args);
171
+ if (fallback instanceof Promise) return fallback.then((v) => strip(v, seen));
172
+ return strip(fallback, seen);
173
+ }
174
+ function createSanitizedNonPlainView(value, seen) {
175
+ const methodCache = /* @__PURE__ */ new Map();
176
+ const proxy = new Proxy(value, {
177
+ get(target, key, receiver) {
178
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
179
+ if (descriptor != null && "value" in descriptor) {
180
+ if (!descriptor.configurable && !descriptor.writable) return descriptor.value;
181
+ const val = stripDeferredPromptValues(descriptor.value, seen);
182
+ if (typeof val === "function") {
183
+ if (!descriptor.configurable && !descriptor.writable || /^class[\s{]/.test(Function.prototype.toString.call(val))) return val;
184
+ const cached = methodCache.get(key);
185
+ if (cached != null && cached.fn === val) return cached.wrapper;
186
+ const wrapper = function(...args) {
187
+ if (this !== proxy) return stripDeferredPromptValues(val.apply(this, args), seen);
188
+ return callMethodOnSanitizedTarget(val, proxy, target, args, stripDeferredPromptValues, seen);
189
+ };
190
+ methodCache.set(key, {
191
+ fn: val,
192
+ wrapper
193
+ });
194
+ return wrapper;
195
+ }
196
+ return val;
197
+ }
198
+ let isAccessor = false;
199
+ for (let proto = target; proto != null; proto = Object.getPrototypeOf(proto)) {
200
+ const d = Object.getOwnPropertyDescriptor(proto, key);
201
+ if (d != null) {
202
+ isAccessor = "get" in d;
203
+ break;
204
+ }
205
+ }
206
+ const result = Reflect.get(target, key, receiver);
207
+ if (typeof result === "function") {
208
+ if (/^class[\s{]/.test(Function.prototype.toString.call(result))) return result;
209
+ if (!isAccessor) {
210
+ const cached = methodCache.get(key);
211
+ if (cached != null && cached.fn === result) return cached.wrapper;
212
+ const wrapper = function(...args) {
213
+ if (this !== proxy) return stripDeferredPromptValues(result.apply(this, args), seen);
214
+ return callMethodOnSanitizedTarget(result, proxy, target, args, stripDeferredPromptValues, seen);
215
+ };
216
+ methodCache.set(key, {
217
+ fn: result,
218
+ wrapper
219
+ });
220
+ return wrapper;
221
+ }
222
+ return function(...args) {
223
+ if (this !== proxy) return stripDeferredPromptValues(result.apply(this, args), seen);
224
+ return callMethodOnSanitizedTarget(result, proxy, target, args, stripDeferredPromptValues, seen);
225
+ };
226
+ }
227
+ return stripDeferredPromptValues(result, seen);
228
+ },
229
+ getOwnPropertyDescriptor(target, key) {
230
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
231
+ if (descriptor == null || !("value" in descriptor)) return descriptor;
232
+ if (!descriptor.configurable && !descriptor.writable) return descriptor;
233
+ return {
234
+ ...descriptor,
235
+ value: stripDeferredPromptValues(descriptor.value, seen)
236
+ };
237
+ }
238
+ });
239
+ seen.set(value, proxy);
240
+ return proxy;
241
+ }
242
+ function stripDeferredPromptValues(value, seen = /* @__PURE__ */ new WeakMap()) {
243
+ if (isDeferredPromptValue(value)) return void 0;
244
+ if (value == null || typeof value !== "object") return value;
245
+ const cached = seen.get(value);
246
+ if (cached !== void 0) return cached;
247
+ if (Array.isArray(value)) {
248
+ if (!containsDeferredPromptValues(value)) return value;
249
+ const clone$1 = new Array(value.length);
250
+ seen.set(value, clone$1);
251
+ for (let i = 0; i < value.length; i++) clone$1[i] = stripDeferredPromptValues(value[i], seen);
252
+ copySanitizedOwnProperties(value, clone$1, seen);
253
+ return clone$1;
254
+ }
255
+ if (value instanceof Set) {
256
+ if (!containsDeferredPromptValues(value)) return value;
257
+ const clone$1 = /* @__PURE__ */ new Set();
258
+ seen.set(value, clone$1);
259
+ for (const entryValue of value) clone$1.add(stripDeferredPromptValues(entryValue, seen));
260
+ copySanitizedOwnProperties(value, clone$1, seen);
261
+ return clone$1;
262
+ }
263
+ if (value instanceof Map) {
264
+ if (!containsDeferredPromptValues(value)) return value;
265
+ const clone$1 = /* @__PURE__ */ new Map();
266
+ seen.set(value, clone$1);
267
+ for (const [key, entryValue] of value) clone$1.set(stripDeferredPromptValues(key, seen), stripDeferredPromptValues(entryValue, seen));
268
+ copySanitizedOwnProperties(value, clone$1, seen);
269
+ return clone$1;
270
+ }
271
+ if (!isPlainObject(value)) return containsDeferredPromptValues(value) ? createSanitizedNonPlainView(value, seen) : value;
272
+ if (!containsDeferredPromptValues(value)) return value;
273
+ const clone = Object.create(Object.getPrototypeOf(value));
274
+ seen.set(value, clone);
275
+ for (const key of Reflect.ownKeys(value)) {
276
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
277
+ if (descriptor == null) continue;
278
+ if ("value" in descriptor) descriptor.value = stripDeferredPromptValues(descriptor.value, seen);
279
+ Object.defineProperty(clone, key, descriptor);
280
+ }
281
+ return clone;
282
+ }
283
+ /**
284
+ * Sets active config data for a context.
285
+ * @internal
286
+ */
287
+ function setActiveConfig(contextId, data) {
288
+ activeConfigRegistry.set(contextId, data);
289
+ }
290
+ /**
291
+ * Gets active config data for a context.
292
+ * @internal
293
+ */
294
+ function getActiveConfig(contextId) {
295
+ return activeConfigRegistry.get(contextId);
296
+ }
297
+ /**
298
+ * Clears active config data for a context.
299
+ * @internal
300
+ */
301
+ function clearActiveConfig(contextId) {
302
+ activeConfigRegistry.delete(contextId);
303
+ }
304
+ /**
305
+ * Sets active config metadata for a context.
306
+ * @internal
307
+ */
308
+ function setActiveConfigMeta(contextId, meta) {
309
+ activeConfigMetaRegistry.set(contextId, meta);
310
+ }
311
+ /**
312
+ * Gets active config metadata for a context.
313
+ * @internal
314
+ */
315
+ function getActiveConfigMeta(contextId) {
316
+ return activeConfigMetaRegistry.get(contextId);
317
+ }
318
+ /**
319
+ * Clears active config metadata for a context.
320
+ * @internal
321
+ */
322
+ function clearActiveConfigMeta(contextId) {
323
+ activeConfigMetaRegistry.delete(contextId);
324
+ }
325
+ function getTypeName(value) {
326
+ if (value === null) return "null";
327
+ if (Array.isArray(value)) return "array";
328
+ return typeof value;
329
+ }
330
+ function isStandardSchema(value) {
331
+ if (value == null || typeof value !== "object" && typeof value !== "function") return false;
332
+ if (!("~standard" in value)) return false;
333
+ const standard = value["~standard"];
334
+ if (standard == null || typeof standard !== "object" && typeof standard !== "function") return false;
335
+ return typeof standard.validate === "function";
336
+ }
337
+ function isErrnoException(error) {
338
+ return typeof error === "object" && error !== null && "code" in error;
339
+ }
340
+ /**
341
+ * Validates raw data against a Standard Schema and returns the validated value.
342
+ * @internal
343
+ */
344
+ function processValidationResult(validationResult) {
345
+ if (validationResult.issues) {
346
+ const firstIssue = validationResult.issues[0];
347
+ throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
348
+ }
349
+ return validationResult.value;
350
+ }
351
+ function validateWithSchema(schema, rawData) {
352
+ const validation = schema["~standard"].validate(rawData);
353
+ if (validation instanceof Promise) return validation.then((result) => processValidationResult(result));
354
+ return processValidationResult(validation);
355
+ }
356
+ /**
357
+ * Creates a config context for use with Optique parsers.
358
+ *
359
+ * The config context implements the `SourceContext` interface and can be used
360
+ * with `runWith()` from *@optique/core* or `run()`/`runAsync()` from
361
+ * *@optique/run* to provide configuration file support.
362
+ *
363
+ * @template T The output type of the config schema.
364
+ * @template TConfigMeta The metadata type for config sources.
365
+ * @param options Configuration options including schema and optional file
366
+ * parser.
367
+ * @returns A config context that can be used with `bindConfig()` and runners.
368
+ * @throws {TypeError} If `schema` is not a valid Standard Schema object.
369
+ * @throws {TypeError} If `fileParser` is provided but is not a function.
370
+ * @since 0.10.0
371
+ *
372
+ * @example
373
+ * ```typescript
374
+ * import { z } from "zod";
375
+ * import { createConfigContext } from "@optique/config";
376
+ *
377
+ * const schema = z.object({
378
+ * host: z.string(),
379
+ * port: z.number(),
380
+ * });
381
+ *
382
+ * const configContext = createConfigContext({ schema });
383
+ * ```
384
+ */
385
+ function createConfigContext(options) {
386
+ const rawSchema = options.schema;
387
+ if (!isStandardSchema(rawSchema)) throw new TypeError(`Expected schema to be a Standard Schema object, but got: ${getTypeName(rawSchema)}.`);
388
+ const rawFileParser = options.fileParser;
389
+ if (rawFileParser !== void 0 && typeof rawFileParser !== "function") throw new TypeError(`Expected fileParser to be a function, but got: ${getTypeName(rawFileParser)}.`);
390
+ const contextId = Symbol(`@optique/config:${Math.random()}`);
391
+ const context = {
392
+ id: contextId,
393
+ schema: rawSchema,
394
+ mode: "dynamic",
395
+ getInternalAnnotations(parsed, annotations) {
396
+ if (parsed === void 0) return { [contextId]: phase1ConfigAnnotationMarker };
397
+ return Object.getOwnPropertySymbols(annotations).includes(contextId) ? void 0 : { [contextId]: void 0 };
398
+ },
399
+ finalizeParsed(parsed) {
400
+ return parsed === void 0 ? { [phase2UndefinedParsedValueKey]: true } : parsed;
401
+ },
402
+ getAnnotations(parsed, runtimeOptions) {
403
+ if (parsed === void 0) return {};
404
+ const opts = runtimeOptions;
405
+ if (!opts || !opts.getConfigPath && !opts.load) throw new TypeError("Either getConfigPath or load must be provided in the runner options when using ConfigContext.");
406
+ if (opts.load !== void 0 && typeof opts.load !== "function") throw new TypeError(`Expected load to be a function, but got: ${getTypeName(opts.load)}.`);
407
+ if (!opts.load && opts.getConfigPath !== void 0 && typeof opts.getConfigPath !== "function") throw new TypeError(`Expected getConfigPath to be a function, but got: ${getTypeName(opts.getConfigPath)}.`);
408
+ const parsedValue = isPhase2UndefinedParsedValue(parsed) ? void 0 : stripDeferredPromptValues(parsed);
409
+ const parsedPlaceholder = parsedValue;
410
+ const buildAnnotations = (configData, configMeta) => {
411
+ if (configData === void 0 || configData === null) return {};
412
+ setActiveConfig(contextId, configData);
413
+ if (configMeta !== void 0) {
414
+ setActiveConfigMeta(contextId, configMeta);
415
+ return { [contextId]: {
416
+ data: configData,
417
+ meta: configMeta
418
+ } };
419
+ }
420
+ return { [contextId]: { data: configData } };
421
+ };
422
+ const validateAndBuildAnnotations = (rawData, configMeta) => {
423
+ const validated = validateWithSchema(rawSchema, rawData);
424
+ if (validated instanceof Promise) return validated.then((configData) => buildAnnotations(configData, configMeta));
425
+ return buildAnnotations(validated, configMeta);
426
+ };
427
+ if (opts.load) {
428
+ const loaded = opts.load(parsedPlaceholder);
429
+ if (loaded instanceof Promise) return loaded.then(({ config, meta }) => validateAndBuildAnnotations(config, meta));
430
+ return validateAndBuildAnnotations(loaded.config, loaded.meta);
431
+ }
432
+ if (opts.getConfigPath) {
433
+ const configPath = opts.getConfigPath(parsedPlaceholder);
434
+ if (configPath !== void 0 && typeof configPath !== "string") throw new TypeError(`Expected getConfigPath() to return a string or undefined, but got: ${getTypeName(configPath)}.`);
435
+ if (!configPath) return {};
436
+ const absoluteConfigPath = (0, node_path.resolve)(configPath);
437
+ const singleFileMeta = {
438
+ configDir: (0, node_path.dirname)(absoluteConfigPath),
439
+ configPath: absoluteConfigPath
440
+ };
441
+ try {
442
+ const contents = (0, node_fs.readFileSync)(absoluteConfigPath);
443
+ let rawData;
444
+ if (rawFileParser) rawData = rawFileParser(contents);
445
+ else {
446
+ const text = new TextDecoder().decode(contents);
447
+ rawData = JSON.parse(text);
448
+ }
449
+ return validateAndBuildAnnotations(rawData, singleFileMeta);
450
+ } catch (error) {
451
+ if (isErrnoException(error) && error.code === "ENOENT") return {};
452
+ if (error instanceof SyntaxError) throw new Error(`Failed to parse config file ${absoluteConfigPath}: ${error.message}`);
453
+ throw error;
454
+ }
455
+ }
456
+ return {};
457
+ },
458
+ [Symbol.dispose]() {
459
+ clearActiveConfig(contextId);
460
+ clearActiveConfigMeta(contextId);
461
+ }
462
+ };
463
+ return context;
464
+ }
465
+ /**
466
+ * Binds a parser to configuration values with fallback priority.
467
+ *
468
+ * The binding implements the following priority order:
469
+ * 1. CLI argument (if provided)
470
+ * 2. Config file value (if available)
471
+ * 3. Default value (if specified)
472
+ * 4. Error (if none of the above)
473
+ *
474
+ * @template M The parser mode (sync or async).
475
+ * @template TValue The parser value type.
476
+ * @template TState The parser state type.
477
+ * @template T The config data type.
478
+ * @param parser The parser to bind to config values.
479
+ * @param options Binding options including context, key, and default.
480
+ * @returns A new parser with config fallback behavior.
481
+ * @throws {TypeError} If `key` is not a property key or function.
482
+ * @since 0.10.0
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * import { bindConfig } from "@optique/config";
487
+ * import { option } from "@optique/core/primitives";
488
+ * import { string } from "@optique/core/valueparser";
489
+ *
490
+ * const hostParser = bindConfig(option("--host", string()), {
491
+ * context: configContext,
492
+ * key: "host",
493
+ * default: "localhost",
494
+ * });
495
+ * ```
496
+ */
497
+ function bindConfig(parser, options) {
498
+ const keyType = typeof options.key;
499
+ if (keyType !== "string" && keyType !== "number" && keyType !== "symbol" && keyType !== "function") throw new TypeError(`Expected key to be a property key or function, but got: ${getTypeName(options.key)}.`);
500
+ const configBindStateKey = Symbol("@optique/config/bindState");
501
+ function isConfigBindState(value) {
502
+ return value != null && typeof value === "object" && configBindStateKey in value;
503
+ }
504
+ function shouldDeferPromptUntilConfigResolves(state) {
505
+ const annotations = (0, __optique_core_annotations.getAnnotations)(state);
506
+ return annotations?.[options.context.id] === phase1ConfigAnnotationMarker;
507
+ }
508
+ const boundParser = {
509
+ $mode: parser.$mode,
510
+ $valueType: parser.$valueType,
511
+ $stateType: parser.$stateType,
512
+ priority: parser.priority,
513
+ usage: options.default !== void 0 ? [{
514
+ type: "optional",
515
+ terms: parser.usage
516
+ }] : parser.usage,
517
+ initialState: parser.initialState,
518
+ parse: (context) => {
519
+ const annotations = (0, __optique_core_annotations.getAnnotations)(context.state);
520
+ const innerState = isConfigBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
521
+ const innerContext = innerState !== context.state ? {
522
+ ...context,
523
+ state: innerState
524
+ } : context;
525
+ const processResult = (result) => {
526
+ if (result.success) {
527
+ const cliConsumed = result.consumed.length > 0;
528
+ const newState$1 = {
529
+ [configBindStateKey]: true,
530
+ hasCliValue: cliConsumed,
531
+ cliState: result.next.state,
532
+ ...annotations && { [__optique_core_annotations.annotationKey]: annotations }
533
+ };
534
+ return {
535
+ success: true,
536
+ next: {
537
+ ...result.next,
538
+ state: newState$1
539
+ },
540
+ consumed: result.consumed
541
+ };
542
+ }
543
+ if (result.consumed > 0) return result;
544
+ const newState = {
545
+ [configBindStateKey]: true,
546
+ hasCliValue: false,
547
+ ...annotations && { [__optique_core_annotations.annotationKey]: annotations }
548
+ };
549
+ return {
550
+ success: true,
551
+ next: {
552
+ ...innerContext,
553
+ state: newState
554
+ },
555
+ consumed: []
556
+ };
557
+ };
558
+ return (0, __optique_core_mode_dispatch.mapModeValue)(parser.$mode, parser.parse(innerContext), processResult);
559
+ },
560
+ complete: (state) => {
561
+ if (isConfigBindState(state) && state.hasCliValue) return parser.complete(state.cliState);
562
+ return (0, __optique_core_mode_dispatch.wrapForMode)(parser.$mode, getConfigOrDefault(state, options));
563
+ },
564
+ suggest: parser.suggest,
565
+ shouldDeferCompletion: shouldDeferPromptUntilConfigResolves,
566
+ getDocFragments(state, upperDefaultValue) {
567
+ const defaultValue = upperDefaultValue ?? options.default;
568
+ return parser.getDocFragments(state, defaultValue);
569
+ }
570
+ };
571
+ return boundParser;
572
+ }
573
+ /**
574
+ * Helper function to get value from config or default.
575
+ * Checks both annotations (for top-level parsers) and the active config
576
+ * registry (for parsers nested inside object() when used with context-aware
577
+ * runners).
578
+ * @throws {TypeError} If the key callback returns a Promise or thenable.
579
+ */
580
+ function getConfigOrDefault(state, options) {
581
+ const annotations = (0, __optique_core_annotations.getAnnotations)(state);
582
+ const contextId = options.context.id;
583
+ const annotationValue = annotations?.[contextId];
584
+ let configData = annotationValue?.data;
585
+ let configMeta = annotationValue?.meta;
586
+ if (configData === void 0 || configData === null) {
587
+ configData = getActiveConfig(contextId);
588
+ configMeta = getActiveConfigMeta(contextId);
589
+ }
590
+ let configValue;
591
+ if (configData !== void 0 && configData !== null) if (typeof options.key === "function") {
592
+ configValue = options.key(configData, configMeta);
593
+ if (configValue != null && (typeof configValue === "object" || typeof configValue === "function") && "then" in configValue && typeof configValue.then === "function") throw new TypeError("The key callback must return a synchronous value, but got a thenable.");
594
+ } else configValue = configData[options.key];
595
+ if (configValue !== void 0) return {
596
+ success: true,
597
+ value: configValue
598
+ };
599
+ if (options.default !== void 0) return {
600
+ success: true,
601
+ value: options.default
602
+ };
603
+ return {
604
+ success: false,
605
+ error: __optique_core_message.message`Missing required configuration value.`
606
+ };
607
+ }
608
+
609
+ //#endregion
610
+ exports.bindConfig = bindConfig;
611
+ exports.clearActiveConfig = clearActiveConfig;
612
+ exports.clearActiveConfigMeta = clearActiveConfigMeta;
613
+ exports.createConfigContext = createConfigContext;
614
+ exports.getActiveConfig = getActiveConfig;
615
+ exports.getActiveConfigMeta = getActiveConfigMeta;
616
+ exports.setActiveConfig = setActiveConfig;
617
+ exports.setActiveConfigMeta = setActiveConfigMeta;