@optique/config 1.0.0-dev.664 → 1.0.0-dev.667

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/dist/index.cjs CHANGED
@@ -28,6 +28,10 @@ const __optique_core_message = __toESM(require("@optique/core/message"));
28
28
  const __optique_core_mode_dispatch = __toESM(require("@optique/core/mode-dispatch"));
29
29
 
30
30
  //#region src/index.ts
31
+ const deferPromptUntilConfigResolvesKey = Symbol.for("@optique/config/deferPromptUntilResolved");
32
+ const phase1ConfigAnnotationsKey = Symbol.for("@optique/config/phase1PromptAnnotations");
33
+ const phase2UndefinedParsedValueKey = Symbol.for("@optique/config/phase2UndefinedParsedValue");
34
+ const deferredPromptValueKey = Symbol.for("@optique/inquirer/deferredPromptValue");
31
35
  /**
32
36
  * Internal registry for active config data during config context execution.
33
37
  * This is a workaround for the limitation that object() doesn't propagate
@@ -40,6 +44,117 @@ const activeConfigRegistry = /* @__PURE__ */ new Map();
40
44
  * @internal
41
45
  */
42
46
  const activeConfigMetaRegistry = /* @__PURE__ */ new Map();
47
+ const phase1ConfigAnnotationMarker = Symbol("@optique/config/phase1Annotation");
48
+ function isDeferredPromptValue(value) {
49
+ return value != null && typeof value === "object" && deferredPromptValueKey in value;
50
+ }
51
+ function isPhase2UndefinedParsedValue(value) {
52
+ return value != null && typeof value === "object" && phase2UndefinedParsedValueKey in value;
53
+ }
54
+ function isPlainObject(value) {
55
+ const proto = Object.getPrototypeOf(value);
56
+ return proto === Object.prototype || proto === null;
57
+ }
58
+ function shouldSkipCollectionOwnKey(value, key) {
59
+ if (Array.isArray(value)) return key === "length" || typeof key === "string" && Number.isInteger(Number(key)) && String(Number(key)) === key;
60
+ return false;
61
+ }
62
+ function containsDeferredPromptValuesInOwnProperties(value, seen) {
63
+ for (const key of Reflect.ownKeys(value)) {
64
+ if (shouldSkipCollectionOwnKey(value, key)) continue;
65
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
66
+ if (descriptor != null && "value" in descriptor && containsDeferredPromptValues(descriptor.value, seen)) return true;
67
+ }
68
+ return false;
69
+ }
70
+ function copySanitizedOwnProperties(source, target, seen) {
71
+ for (const key of Reflect.ownKeys(source)) {
72
+ if (shouldSkipCollectionOwnKey(source, key)) continue;
73
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
74
+ if (descriptor == null) continue;
75
+ if ("value" in descriptor) descriptor.value = stripDeferredPromptValues(descriptor.value, seen);
76
+ Object.defineProperty(target, key, descriptor);
77
+ }
78
+ }
79
+ function containsDeferredPromptValues(value, seen = /* @__PURE__ */ new WeakSet()) {
80
+ if (isDeferredPromptValue(value)) return true;
81
+ if (value == null || typeof value !== "object") return false;
82
+ if (seen.has(value)) return false;
83
+ seen.add(value);
84
+ if (Array.isArray(value)) {
85
+ if (value.some((item) => containsDeferredPromptValues(item, seen))) return true;
86
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
87
+ }
88
+ if (value instanceof Set) {
89
+ for (const entryValue of value) if (containsDeferredPromptValues(entryValue, seen)) return true;
90
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
91
+ }
92
+ if (value instanceof Map) {
93
+ for (const [key, entryValue] of value) if (containsDeferredPromptValues(key, seen) || containsDeferredPromptValues(entryValue, seen)) return true;
94
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
95
+ }
96
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
97
+ }
98
+ function createSanitizedNonPlainView(value, seen) {
99
+ const proxy = new Proxy(value, {
100
+ get(target, key, receiver) {
101
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
102
+ if (descriptor != null && "value" in descriptor) return stripDeferredPromptValues(descriptor.value, seen);
103
+ return Reflect.get(target, key, receiver);
104
+ },
105
+ getOwnPropertyDescriptor(target, key) {
106
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
107
+ if (descriptor == null || !("value" in descriptor)) return descriptor;
108
+ return {
109
+ ...descriptor,
110
+ value: stripDeferredPromptValues(descriptor.value, seen)
111
+ };
112
+ }
113
+ });
114
+ seen.set(value, proxy);
115
+ return proxy;
116
+ }
117
+ function stripDeferredPromptValues(value, seen = /* @__PURE__ */ new WeakMap()) {
118
+ if (isDeferredPromptValue(value)) return void 0;
119
+ if (value == null || typeof value !== "object") return value;
120
+ const cached = seen.get(value);
121
+ if (cached !== void 0) return cached;
122
+ if (Array.isArray(value)) {
123
+ if (!containsDeferredPromptValues(value)) return value;
124
+ const clone$1 = new Array(value.length);
125
+ seen.set(value, clone$1);
126
+ for (let i = 0; i < value.length; i++) clone$1[i] = stripDeferredPromptValues(value[i], seen);
127
+ copySanitizedOwnProperties(value, clone$1, seen);
128
+ return clone$1;
129
+ }
130
+ if (value instanceof Set) {
131
+ if (!containsDeferredPromptValues(value)) return value;
132
+ const clone$1 = /* @__PURE__ */ new Set();
133
+ seen.set(value, clone$1);
134
+ for (const entryValue of value) clone$1.add(stripDeferredPromptValues(entryValue, seen));
135
+ copySanitizedOwnProperties(value, clone$1, seen);
136
+ return clone$1;
137
+ }
138
+ if (value instanceof Map) {
139
+ if (!containsDeferredPromptValues(value)) return value;
140
+ const clone$1 = /* @__PURE__ */ new Map();
141
+ seen.set(value, clone$1);
142
+ for (const [key, entryValue] of value) clone$1.set(stripDeferredPromptValues(key, seen), stripDeferredPromptValues(entryValue, seen));
143
+ copySanitizedOwnProperties(value, clone$1, seen);
144
+ return clone$1;
145
+ }
146
+ if (!isPlainObject(value)) return containsDeferredPromptValues(value) ? createSanitizedNonPlainView(value, seen) : value;
147
+ if (!containsDeferredPromptValues(value)) return value;
148
+ const clone = Object.create(Object.getPrototypeOf(value));
149
+ seen.set(value, clone);
150
+ for (const key of Reflect.ownKeys(value)) {
151
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
152
+ if (descriptor == null) continue;
153
+ if ("value" in descriptor) descriptor.value = stripDeferredPromptValues(descriptor.value, seen);
154
+ Object.defineProperty(clone, key, descriptor);
155
+ }
156
+ return clone;
157
+ }
43
158
  /**
44
159
  * Sets active config data for a context.
45
160
  * @internal
@@ -130,15 +245,20 @@ function validateWithSchema(schema, rawData) {
130
245
  */
131
246
  function createConfigContext(options) {
132
247
  const contextId = Symbol(`@optique/config:${Math.random()}`);
133
- return {
248
+ const context = {
134
249
  id: contextId,
135
250
  schema: options.schema,
136
251
  mode: "dynamic",
252
+ [phase1ConfigAnnotationsKey](parsed, annotations) {
253
+ if (parsed === void 0) return { [contextId]: phase1ConfigAnnotationMarker };
254
+ return Object.getOwnPropertySymbols(annotations).includes(contextId) ? void 0 : { [contextId]: void 0 };
255
+ },
137
256
  getAnnotations(parsed, runtimeOptions) {
138
257
  if (parsed === void 0) return {};
139
258
  const opts = runtimeOptions;
140
259
  if (!opts || !opts.getConfigPath && !opts.load) throw new TypeError("Either getConfigPath or load must be provided in the runner options when using ConfigContext.");
141
- const parsedValue = parsed;
260
+ const parsedValue = isPhase2UndefinedParsedValue(parsed) ? void 0 : stripDeferredPromptValues(parsed);
261
+ const parsedPlaceholder = parsedValue;
142
262
  const buildAnnotations = (configData, configMeta) => {
143
263
  if (configData === void 0 || configData === null) return {};
144
264
  setActiveConfig(contextId, configData);
@@ -157,12 +277,12 @@ function createConfigContext(options) {
157
277
  return buildAnnotations(validated, configMeta);
158
278
  };
159
279
  if (opts.load) {
160
- const loaded = opts.load(parsedValue);
280
+ const loaded = opts.load(parsedPlaceholder);
161
281
  if (loaded instanceof Promise) return loaded.then(({ config, meta }) => validateAndBuildAnnotations(config, meta));
162
282
  return validateAndBuildAnnotations(loaded.config, loaded.meta);
163
283
  }
164
284
  if (opts.getConfigPath) {
165
- const configPath = opts.getConfigPath(parsedValue);
285
+ const configPath = opts.getConfigPath(parsedPlaceholder);
166
286
  if (!configPath) return {};
167
287
  const absoluteConfigPath = (0, node_path.resolve)(configPath);
168
288
  const singleFileMeta = {
@@ -191,6 +311,7 @@ function createConfigContext(options) {
191
311
  clearActiveConfigMeta(contextId);
192
312
  }
193
313
  };
314
+ return context;
194
315
  }
195
316
  /**
196
317
  * Binds a parser to configuration values with fallback priority.
@@ -228,7 +349,11 @@ function bindConfig(parser, options) {
228
349
  function isConfigBindState(value) {
229
350
  return value != null && typeof value === "object" && configBindStateKey in value;
230
351
  }
231
- return {
352
+ function shouldDeferPromptUntilConfigResolves(state) {
353
+ const annotations = (0, __optique_core_annotations.getAnnotations)(state);
354
+ return annotations?.[options.context.id] === phase1ConfigAnnotationMarker;
355
+ }
356
+ const boundParser = {
232
357
  $mode: parser.$mode,
233
358
  $valueType: parser.$valueType,
234
359
  $stateType: parser.$stateType,
@@ -285,11 +410,13 @@ function bindConfig(parser, options) {
285
410
  return (0, __optique_core_mode_dispatch.wrapForMode)(parser.$mode, getConfigOrDefault(state, options));
286
411
  },
287
412
  suggest: parser.suggest,
413
+ [deferPromptUntilConfigResolvesKey]: shouldDeferPromptUntilConfigResolves,
288
414
  getDocFragments(state, upperDefaultValue) {
289
415
  const defaultValue = upperDefaultValue ?? options.default;
290
416
  return parser.getDocFragments(state, defaultValue);
291
417
  }
292
418
  };
419
+ return boundParser;
293
420
  }
294
421
  /**
295
422
  * Helper function to get value from config or default.
package/dist/index.js CHANGED
@@ -5,6 +5,10 @@ import { message } from "@optique/core/message";
5
5
  import { mapModeValue, wrapForMode } from "@optique/core/mode-dispatch";
6
6
 
7
7
  //#region src/index.ts
8
+ const deferPromptUntilConfigResolvesKey = Symbol.for("@optique/config/deferPromptUntilResolved");
9
+ const phase1ConfigAnnotationsKey = Symbol.for("@optique/config/phase1PromptAnnotations");
10
+ const phase2UndefinedParsedValueKey = Symbol.for("@optique/config/phase2UndefinedParsedValue");
11
+ const deferredPromptValueKey = Symbol.for("@optique/inquirer/deferredPromptValue");
8
12
  /**
9
13
  * Internal registry for active config data during config context execution.
10
14
  * This is a workaround for the limitation that object() doesn't propagate
@@ -17,6 +21,117 @@ const activeConfigRegistry = /* @__PURE__ */ new Map();
17
21
  * @internal
18
22
  */
19
23
  const activeConfigMetaRegistry = /* @__PURE__ */ new Map();
24
+ const phase1ConfigAnnotationMarker = Symbol("@optique/config/phase1Annotation");
25
+ function isDeferredPromptValue(value) {
26
+ return value != null && typeof value === "object" && deferredPromptValueKey in value;
27
+ }
28
+ function isPhase2UndefinedParsedValue(value) {
29
+ return value != null && typeof value === "object" && phase2UndefinedParsedValueKey in value;
30
+ }
31
+ function isPlainObject(value) {
32
+ const proto = Object.getPrototypeOf(value);
33
+ return proto === Object.prototype || proto === null;
34
+ }
35
+ function shouldSkipCollectionOwnKey(value, key) {
36
+ if (Array.isArray(value)) return key === "length" || typeof key === "string" && Number.isInteger(Number(key)) && String(Number(key)) === key;
37
+ return false;
38
+ }
39
+ function containsDeferredPromptValuesInOwnProperties(value, seen) {
40
+ for (const key of Reflect.ownKeys(value)) {
41
+ if (shouldSkipCollectionOwnKey(value, key)) continue;
42
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
43
+ if (descriptor != null && "value" in descriptor && containsDeferredPromptValues(descriptor.value, seen)) return true;
44
+ }
45
+ return false;
46
+ }
47
+ function copySanitizedOwnProperties(source, target, seen) {
48
+ for (const key of Reflect.ownKeys(source)) {
49
+ if (shouldSkipCollectionOwnKey(source, key)) continue;
50
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
51
+ if (descriptor == null) continue;
52
+ if ("value" in descriptor) descriptor.value = stripDeferredPromptValues(descriptor.value, seen);
53
+ Object.defineProperty(target, key, descriptor);
54
+ }
55
+ }
56
+ function containsDeferredPromptValues(value, seen = /* @__PURE__ */ new WeakSet()) {
57
+ if (isDeferredPromptValue(value)) return true;
58
+ if (value == null || typeof value !== "object") return false;
59
+ if (seen.has(value)) return false;
60
+ seen.add(value);
61
+ if (Array.isArray(value)) {
62
+ if (value.some((item) => containsDeferredPromptValues(item, seen))) return true;
63
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
64
+ }
65
+ if (value instanceof Set) {
66
+ for (const entryValue of value) if (containsDeferredPromptValues(entryValue, seen)) return true;
67
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
68
+ }
69
+ if (value instanceof Map) {
70
+ for (const [key, entryValue] of value) if (containsDeferredPromptValues(key, seen) || containsDeferredPromptValues(entryValue, seen)) return true;
71
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
72
+ }
73
+ return containsDeferredPromptValuesInOwnProperties(value, seen);
74
+ }
75
+ function createSanitizedNonPlainView(value, seen) {
76
+ const proxy = new Proxy(value, {
77
+ get(target, key, receiver) {
78
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
79
+ if (descriptor != null && "value" in descriptor) return stripDeferredPromptValues(descriptor.value, seen);
80
+ return Reflect.get(target, key, receiver);
81
+ },
82
+ getOwnPropertyDescriptor(target, key) {
83
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
84
+ if (descriptor == null || !("value" in descriptor)) return descriptor;
85
+ return {
86
+ ...descriptor,
87
+ value: stripDeferredPromptValues(descriptor.value, seen)
88
+ };
89
+ }
90
+ });
91
+ seen.set(value, proxy);
92
+ return proxy;
93
+ }
94
+ function stripDeferredPromptValues(value, seen = /* @__PURE__ */ new WeakMap()) {
95
+ if (isDeferredPromptValue(value)) return void 0;
96
+ if (value == null || typeof value !== "object") return value;
97
+ const cached = seen.get(value);
98
+ if (cached !== void 0) return cached;
99
+ if (Array.isArray(value)) {
100
+ if (!containsDeferredPromptValues(value)) return value;
101
+ const clone$1 = new Array(value.length);
102
+ seen.set(value, clone$1);
103
+ for (let i = 0; i < value.length; i++) clone$1[i] = stripDeferredPromptValues(value[i], seen);
104
+ copySanitizedOwnProperties(value, clone$1, seen);
105
+ return clone$1;
106
+ }
107
+ if (value instanceof Set) {
108
+ if (!containsDeferredPromptValues(value)) return value;
109
+ const clone$1 = /* @__PURE__ */ new Set();
110
+ seen.set(value, clone$1);
111
+ for (const entryValue of value) clone$1.add(stripDeferredPromptValues(entryValue, seen));
112
+ copySanitizedOwnProperties(value, clone$1, seen);
113
+ return clone$1;
114
+ }
115
+ if (value instanceof Map) {
116
+ if (!containsDeferredPromptValues(value)) return value;
117
+ const clone$1 = /* @__PURE__ */ new Map();
118
+ seen.set(value, clone$1);
119
+ for (const [key, entryValue] of value) clone$1.set(stripDeferredPromptValues(key, seen), stripDeferredPromptValues(entryValue, seen));
120
+ copySanitizedOwnProperties(value, clone$1, seen);
121
+ return clone$1;
122
+ }
123
+ if (!isPlainObject(value)) return containsDeferredPromptValues(value) ? createSanitizedNonPlainView(value, seen) : value;
124
+ if (!containsDeferredPromptValues(value)) return value;
125
+ const clone = Object.create(Object.getPrototypeOf(value));
126
+ seen.set(value, clone);
127
+ for (const key of Reflect.ownKeys(value)) {
128
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
129
+ if (descriptor == null) continue;
130
+ if ("value" in descriptor) descriptor.value = stripDeferredPromptValues(descriptor.value, seen);
131
+ Object.defineProperty(clone, key, descriptor);
132
+ }
133
+ return clone;
134
+ }
20
135
  /**
21
136
  * Sets active config data for a context.
22
137
  * @internal
@@ -107,15 +222,20 @@ function validateWithSchema(schema, rawData) {
107
222
  */
108
223
  function createConfigContext(options) {
109
224
  const contextId = Symbol(`@optique/config:${Math.random()}`);
110
- return {
225
+ const context = {
111
226
  id: contextId,
112
227
  schema: options.schema,
113
228
  mode: "dynamic",
229
+ [phase1ConfigAnnotationsKey](parsed, annotations) {
230
+ if (parsed === void 0) return { [contextId]: phase1ConfigAnnotationMarker };
231
+ return Object.getOwnPropertySymbols(annotations).includes(contextId) ? void 0 : { [contextId]: void 0 };
232
+ },
114
233
  getAnnotations(parsed, runtimeOptions) {
115
234
  if (parsed === void 0) return {};
116
235
  const opts = runtimeOptions;
117
236
  if (!opts || !opts.getConfigPath && !opts.load) throw new TypeError("Either getConfigPath or load must be provided in the runner options when using ConfigContext.");
118
- const parsedValue = parsed;
237
+ const parsedValue = isPhase2UndefinedParsedValue(parsed) ? void 0 : stripDeferredPromptValues(parsed);
238
+ const parsedPlaceholder = parsedValue;
119
239
  const buildAnnotations = (configData, configMeta) => {
120
240
  if (configData === void 0 || configData === null) return {};
121
241
  setActiveConfig(contextId, configData);
@@ -134,12 +254,12 @@ function createConfigContext(options) {
134
254
  return buildAnnotations(validated, configMeta);
135
255
  };
136
256
  if (opts.load) {
137
- const loaded = opts.load(parsedValue);
257
+ const loaded = opts.load(parsedPlaceholder);
138
258
  if (loaded instanceof Promise) return loaded.then(({ config, meta }) => validateAndBuildAnnotations(config, meta));
139
259
  return validateAndBuildAnnotations(loaded.config, loaded.meta);
140
260
  }
141
261
  if (opts.getConfigPath) {
142
- const configPath = opts.getConfigPath(parsedValue);
262
+ const configPath = opts.getConfigPath(parsedPlaceholder);
143
263
  if (!configPath) return {};
144
264
  const absoluteConfigPath = resolve(configPath);
145
265
  const singleFileMeta = {
@@ -168,6 +288,7 @@ function createConfigContext(options) {
168
288
  clearActiveConfigMeta(contextId);
169
289
  }
170
290
  };
291
+ return context;
171
292
  }
172
293
  /**
173
294
  * Binds a parser to configuration values with fallback priority.
@@ -205,7 +326,11 @@ function bindConfig(parser, options) {
205
326
  function isConfigBindState(value) {
206
327
  return value != null && typeof value === "object" && configBindStateKey in value;
207
328
  }
208
- return {
329
+ function shouldDeferPromptUntilConfigResolves(state) {
330
+ const annotations = getAnnotations(state);
331
+ return annotations?.[options.context.id] === phase1ConfigAnnotationMarker;
332
+ }
333
+ const boundParser = {
209
334
  $mode: parser.$mode,
210
335
  $valueType: parser.$valueType,
211
336
  $stateType: parser.$stateType,
@@ -262,11 +387,13 @@ function bindConfig(parser, options) {
262
387
  return wrapForMode(parser.$mode, getConfigOrDefault(state, options));
263
388
  },
264
389
  suggest: parser.suggest,
390
+ [deferPromptUntilConfigResolvesKey]: shouldDeferPromptUntilConfigResolves,
265
391
  getDocFragments(state, upperDefaultValue) {
266
392
  const defaultValue = upperDefaultValue ?? options.default;
267
393
  return parser.getDocFragments(state, defaultValue);
268
394
  }
269
395
  };
396
+ return boundParser;
270
397
  }
271
398
  /**
272
399
  * Helper function to get value from config or default.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/config",
3
- "version": "1.0.0-dev.664+4cd3da03",
3
+ "version": "1.0.0-dev.667+b4962990",
4
4
  "description": "Configuration file support for Optique with Standard Schema validation",
5
5
  "keywords": [
6
6
  "CLI",
@@ -59,7 +59,7 @@
59
59
  "@standard-schema/spec": "^1.1.0"
60
60
  },
61
61
  "dependencies": {
62
- "@optique/core": "1.0.0-dev.664+4cd3da03"
62
+ "@optique/core": "1.0.0-dev.667+b4962990"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@standard-schema/spec": "^1.1.0",
@@ -67,7 +67,7 @@
67
67
  "tsdown": "^0.13.0",
68
68
  "typescript": "^5.8.3",
69
69
  "zod": "^3.25.0 || ^4.0.0",
70
- "@optique/env": "1.0.0-dev.664+4cd3da03"
70
+ "@optique/env": "1.0.0-dev.667+b4962990"
71
71
  },
72
72
  "scripts": {
73
73
  "build": "tsdown",