@optique/config 1.0.0-dev.1674 → 1.0.0-dev.1677

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
@@ -109,7 +109,8 @@ function isPromise(value) {
109
109
  return isPromiseLike(value) && value[Symbol.toStringTag] === "Promise";
110
110
  }
111
111
  function validateLoadResult(loaded) {
112
- if (loaded == null || typeof loaded !== "object" || Array.isArray(loaded)) throw new TypeError(`Expected load() to return an object, but got: ${getTypeName(loaded)}.`);
112
+ if (loaded == null) return void 0;
113
+ if (typeof loaded !== "object" || Array.isArray(loaded)) throw new TypeError(`Expected load() to return an object, but got: ${getTypeName(loaded)}.`);
113
114
  if (!("config" in loaded)) throw new TypeError("Expected load() result to have a config property.");
114
115
  const result = loaded;
115
116
  if (isPromise(result.config)) throw new TypeError("Expected config in load() result to not be a Promise. Resolve the Promise before returning.");
@@ -196,8 +197,13 @@ function createConfigContext(options) {
196
197
  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)}.`);
197
198
  const parsedValue = isPhase2UndefinedParsedValue(parsed) ? void 0 : parsed;
198
199
  const parsedPlaceholder = parsedValue;
200
+ const emptyAnnotations = () => {
201
+ clearActiveConfig(contextId);
202
+ clearActiveConfigMeta(contextId);
203
+ return {};
204
+ };
199
205
  const buildAnnotations = (configData, configMeta) => {
200
- if (configData === void 0 || configData === null) return {};
206
+ if (configData === void 0 || configData === null) return emptyAnnotations();
201
207
  setActiveConfig(contextId, configData);
202
208
  if (configMeta !== void 0) {
203
209
  setActiveConfigMeta(contextId, configMeta);
@@ -217,16 +223,18 @@ function createConfigContext(options) {
217
223
  const loaded = opts.load(parsedPlaceholder);
218
224
  if (isPromise(loaded)) return Promise.resolve(loaded).then((resolved) => {
219
225
  const validated$1 = validateLoadResult(resolved);
226
+ if (validated$1 === void 0) return emptyAnnotations();
220
227
  return validateAndBuildAnnotations(validated$1.config, validated$1.meta);
221
228
  });
222
229
  if (isPromiseLike(loaded)) throw new TypeError("Expected load() to return a plain object or Promise, but got a thenable. Use a real Promise instead.");
223
230
  const validated = validateLoadResult(loaded);
231
+ if (validated === void 0) return emptyAnnotations();
224
232
  return validateAndBuildAnnotations(validated.config, validated.meta);
225
233
  }
226
234
  if (opts.getConfigPath) {
227
235
  const configPath = opts.getConfigPath(parsedPlaceholder);
228
236
  if (configPath !== void 0 && typeof configPath !== "string") throw new TypeError(`Expected getConfigPath() to return a string or undefined, but got: ${getTypeName(configPath)}.`);
229
- if (!configPath) return {};
237
+ if (!configPath) return emptyAnnotations();
230
238
  const absoluteConfigPath = (0, node_path.resolve)(configPath);
231
239
  const singleFileMeta = {
232
240
  configDir: (0, node_path.dirname)(absoluteConfigPath),
@@ -242,12 +250,12 @@ function createConfigContext(options) {
242
250
  }
243
251
  return validateAndBuildAnnotations(rawData, singleFileMeta);
244
252
  } catch (error) {
245
- if (isErrnoException(error) && error.code === "ENOENT") return {};
253
+ if (isErrnoException(error) && error.code === "ENOENT") return emptyAnnotations();
246
254
  if (error instanceof SyntaxError) throw new Error(`Failed to parse config file ${absoluteConfigPath}: ${error.message}`);
247
255
  throw error;
248
256
  }
249
257
  }
250
- return {};
258
+ return emptyAnnotations();
251
259
  },
252
260
  [Symbol.dispose]() {
253
261
  clearActiveConfig(contextId);
package/dist/index.d.cts CHANGED
@@ -82,7 +82,10 @@ interface ConfigContextOptions<T> {
82
82
  */
83
83
  interface ConfigLoadResult<TConfigMeta = ConfigMeta> {
84
84
  /**
85
- * Raw config data to validate against the schema.
85
+ * Raw config data to validate against the schema. The value is always
86
+ * passed to the schema validator, even when `undefined` or `null`.
87
+ * To signal "no config found" without validation, return `undefined`
88
+ * or `null` directly from `load()` instead of wrapping it in an object.
86
89
  */
87
90
  readonly config: unknown;
88
91
  /**
@@ -117,15 +120,18 @@ interface ConfigContextRequiredOptions<TConfigMeta = ConfigMeta> {
117
120
  * returns the config data (or a Promise of it). This allows full control
118
121
  * over file discovery, loading, merging, and error handling.
119
122
  *
120
- * The returned data will be validated against the schema.
123
+ * The returned `ConfigLoadResult.config` is always validated against the
124
+ * schema. Return `undefined` or `null` directly (not wrapped in a
125
+ * `ConfigLoadResult`) to signal that no config data was found;
126
+ * `bindConfig()` will fall back to its defaults.
121
127
  *
122
128
  * When `load` is provided, `getConfigPath` is ignored.
123
129
  *
124
130
  * @param parsed The result from the first parse pass.
125
- * @returns Config data and metadata (config is validated by schema).
131
+ * @returns Config data and metadata, or `undefined`/`null` for no config.
126
132
  * @since 1.0.0
127
133
  */
128
- readonly load?: (parsed: ParserValuePlaceholder) => Promise<ConfigLoadResult<TConfigMeta>> | ConfigLoadResult<TConfigMeta>;
134
+ readonly load?: (parsed: ParserValuePlaceholder) => Promise<ConfigLoadResult<TConfigMeta> | undefined | null> | ConfigLoadResult<TConfigMeta> | undefined | null;
129
135
  }
130
136
  /**
131
137
  * A config context that provides configuration data via annotations.
package/dist/index.d.ts CHANGED
@@ -82,7 +82,10 @@ interface ConfigContextOptions<T> {
82
82
  */
83
83
  interface ConfigLoadResult<TConfigMeta = ConfigMeta> {
84
84
  /**
85
- * Raw config data to validate against the schema.
85
+ * Raw config data to validate against the schema. The value is always
86
+ * passed to the schema validator, even when `undefined` or `null`.
87
+ * To signal "no config found" without validation, return `undefined`
88
+ * or `null` directly from `load()` instead of wrapping it in an object.
86
89
  */
87
90
  readonly config: unknown;
88
91
  /**
@@ -117,15 +120,18 @@ interface ConfigContextRequiredOptions<TConfigMeta = ConfigMeta> {
117
120
  * returns the config data (or a Promise of it). This allows full control
118
121
  * over file discovery, loading, merging, and error handling.
119
122
  *
120
- * The returned data will be validated against the schema.
123
+ * The returned `ConfigLoadResult.config` is always validated against the
124
+ * schema. Return `undefined` or `null` directly (not wrapped in a
125
+ * `ConfigLoadResult`) to signal that no config data was found;
126
+ * `bindConfig()` will fall back to its defaults.
121
127
  *
122
128
  * When `load` is provided, `getConfigPath` is ignored.
123
129
  *
124
130
  * @param parsed The result from the first parse pass.
125
- * @returns Config data and metadata (config is validated by schema).
131
+ * @returns Config data and metadata, or `undefined`/`null` for no config.
126
132
  * @since 1.0.0
127
133
  */
128
- readonly load?: (parsed: ParserValuePlaceholder) => Promise<ConfigLoadResult<TConfigMeta>> | ConfigLoadResult<TConfigMeta>;
134
+ readonly load?: (parsed: ParserValuePlaceholder) => Promise<ConfigLoadResult<TConfigMeta> | undefined | null> | ConfigLoadResult<TConfigMeta> | undefined | null;
129
135
  }
130
136
  /**
131
137
  * A config context that provides configuration data via annotations.
package/dist/index.js CHANGED
@@ -86,7 +86,8 @@ function isPromise(value) {
86
86
  return isPromiseLike(value) && value[Symbol.toStringTag] === "Promise";
87
87
  }
88
88
  function validateLoadResult(loaded) {
89
- if (loaded == null || typeof loaded !== "object" || Array.isArray(loaded)) throw new TypeError(`Expected load() to return an object, but got: ${getTypeName(loaded)}.`);
89
+ if (loaded == null) return void 0;
90
+ if (typeof loaded !== "object" || Array.isArray(loaded)) throw new TypeError(`Expected load() to return an object, but got: ${getTypeName(loaded)}.`);
90
91
  if (!("config" in loaded)) throw new TypeError("Expected load() result to have a config property.");
91
92
  const result = loaded;
92
93
  if (isPromise(result.config)) throw new TypeError("Expected config in load() result to not be a Promise. Resolve the Promise before returning.");
@@ -173,8 +174,13 @@ function createConfigContext(options) {
173
174
  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)}.`);
174
175
  const parsedValue = isPhase2UndefinedParsedValue(parsed) ? void 0 : parsed;
175
176
  const parsedPlaceholder = parsedValue;
177
+ const emptyAnnotations = () => {
178
+ clearActiveConfig(contextId);
179
+ clearActiveConfigMeta(contextId);
180
+ return {};
181
+ };
176
182
  const buildAnnotations = (configData, configMeta) => {
177
- if (configData === void 0 || configData === null) return {};
183
+ if (configData === void 0 || configData === null) return emptyAnnotations();
178
184
  setActiveConfig(contextId, configData);
179
185
  if (configMeta !== void 0) {
180
186
  setActiveConfigMeta(contextId, configMeta);
@@ -194,16 +200,18 @@ function createConfigContext(options) {
194
200
  const loaded = opts.load(parsedPlaceholder);
195
201
  if (isPromise(loaded)) return Promise.resolve(loaded).then((resolved) => {
196
202
  const validated$1 = validateLoadResult(resolved);
203
+ if (validated$1 === void 0) return emptyAnnotations();
197
204
  return validateAndBuildAnnotations(validated$1.config, validated$1.meta);
198
205
  });
199
206
  if (isPromiseLike(loaded)) throw new TypeError("Expected load() to return a plain object or Promise, but got a thenable. Use a real Promise instead.");
200
207
  const validated = validateLoadResult(loaded);
208
+ if (validated === void 0) return emptyAnnotations();
201
209
  return validateAndBuildAnnotations(validated.config, validated.meta);
202
210
  }
203
211
  if (opts.getConfigPath) {
204
212
  const configPath = opts.getConfigPath(parsedPlaceholder);
205
213
  if (configPath !== void 0 && typeof configPath !== "string") throw new TypeError(`Expected getConfigPath() to return a string or undefined, but got: ${getTypeName(configPath)}.`);
206
- if (!configPath) return {};
214
+ if (!configPath) return emptyAnnotations();
207
215
  const absoluteConfigPath = resolve(configPath);
208
216
  const singleFileMeta = {
209
217
  configDir: dirname(absoluteConfigPath),
@@ -219,12 +227,12 @@ function createConfigContext(options) {
219
227
  }
220
228
  return validateAndBuildAnnotations(rawData, singleFileMeta);
221
229
  } catch (error) {
222
- if (isErrnoException(error) && error.code === "ENOENT") return {};
230
+ if (isErrnoException(error) && error.code === "ENOENT") return emptyAnnotations();
223
231
  if (error instanceof SyntaxError) throw new Error(`Failed to parse config file ${absoluteConfigPath}: ${error.message}`);
224
232
  throw error;
225
233
  }
226
234
  }
227
- return {};
235
+ return emptyAnnotations();
228
236
  },
229
237
  [Symbol.dispose]() {
230
238
  clearActiveConfig(contextId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/config",
3
- "version": "1.0.0-dev.1674+e531f351",
3
+ "version": "1.0.0-dev.1677+8109f98c",
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.1674+e531f351"
62
+ "@optique/core": "1.0.0-dev.1677+8109f98c"
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.1674+e531f351"
70
+ "@optique/env": "1.0.0-dev.1677+8109f98c"
71
71
  },
72
72
  "scripts": {
73
73
  "build": "tsdown",