@fulmenhq/tsfulmen 0.3.1 → 0.3.3

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.
@@ -123,22 +123,29 @@ interface ConfigPathOptions {
123
123
  }
124
124
 
125
125
  /**
126
- * Options for loading configuration
126
+ * Fields common to all `loadConfig` call shapes (independent of how the
127
+ * defaults layer is supplied).
127
128
  */
128
- interface LoadConfigOptions {
129
+ interface BaseLoadConfigOptions {
129
130
  /**
130
131
  * Application identifier for resolving user config paths
131
132
  */
132
133
  identity: AppIdentifier;
133
134
  /**
134
- * Absolute path to the defaults configuration file
135
- * This file MUST exist
135
+ * Optional absolute path to a schema file for validation.
136
+ *
137
+ * If both `schemaPath` and inline `schema` are given, `schema` takes precedence.
136
138
  */
137
- defaultsPath: string;
139
+ schemaPath?: string;
138
140
  /**
139
- * Optional absolute path to a schema file for validation
141
+ * Inline schema content (JSON or YAML string) for validation.
142
+ *
143
+ * Provide this instead of `schemaPath` when the schema is embedded at build
144
+ * time. Note: schema validation still requires the JSON-Schema metaschema,
145
+ * which tsfulmen currently loads from disk — full in-binary config validation
146
+ * lands with the asset-embedding work (Phase 2 / v0.4.0).
140
147
  */
141
- schemaPath?: string;
148
+ schema?: string;
142
149
  /**
143
150
  * Optional environment variable prefix for overrides
144
151
  * Defaults to identity.app.env_prefix if available, otherwise identity.app upper-cased
@@ -157,14 +164,56 @@ interface LoadConfigOptions {
157
164
  */
158
165
  includeEnvVarReport?: boolean;
159
166
  }
167
+ /**
168
+ * Options for loading configuration from a defaults **file path**.
169
+ *
170
+ * This is the original, patch-stable shape: `defaultsPath` is required and
171
+ * typed `string`. For build-time embedded defaults (no file on disk), use
172
+ * {@link LoadInlineConfigOptions} instead.
173
+ */
174
+ interface LoadConfigOptions extends BaseLoadConfigOptions {
175
+ /**
176
+ * Absolute path to the defaults configuration file. This file MUST exist.
177
+ */
178
+ defaultsPath: string;
179
+ /** Mutually exclusive with {@link LoadInlineConfigOptions.defaults}. */
180
+ defaults?: never;
181
+ }
182
+ /**
183
+ * Options for loading configuration from **inline defaults** content.
184
+ *
185
+ * Provide a pre-parsed `defaults` object instead of a `defaultsPath` when the
186
+ * defaults are embedded at build time (e.g. a `bun --compile` single-file
187
+ * binary, where the defaults file is not on disk). Avoids the temp-file dance
188
+ * of writing embedded content out just to pass a path.
189
+ */
190
+ interface LoadInlineConfigOptions extends BaseLoadConfigOptions {
191
+ /**
192
+ * Inline defaults content as a pre-parsed object.
193
+ */
194
+ defaults: Record<string, unknown>;
195
+ /** Mutually exclusive with {@link LoadConfigOptions.defaultsPath}. */
196
+ defaultsPath?: never;
197
+ }
160
198
  /**
161
199
  * Metadata about the loaded configuration
162
200
  */
163
201
  interface ConfigMetadata {
164
202
  /**
165
- * Path to the defaults file used
203
+ * Path to the defaults file used.
204
+ *
205
+ * Empty string (`""`) when inline `defaults` was provided (there is no backing
206
+ * file). Path-based callers always get the path they passed — the static type
207
+ * stays `string` for patch compatibility. Use `defaultsSource` to distinguish
208
+ * the inline case unambiguously.
166
209
  */
167
210
  defaultsPath: string;
211
+ /**
212
+ * Origin of the defaults layer: a file path (`"path"`) or inline `defaults`
213
+ * content (`"inline"`). Optional/additive — present from the inline-defaults
214
+ * feature onward.
215
+ */
216
+ defaultsSource?: "path" | "inline";
168
217
  /**
169
218
  * Path to the user config file used (null if not found)
170
219
  */
@@ -190,9 +239,16 @@ interface ConfigMetadata {
190
239
  */
191
240
  schema: {
192
241
  /**
193
- * Path to the schema file used (if any)
242
+ * Path to the schema file used (null when none, or when inline `schema`
243
+ * content was provided — inline has no backing file).
194
244
  */
195
245
  path: string | null;
246
+ /**
247
+ * Origin of the schema used for validation: a file path (`"path"`) or inline
248
+ * `schema` content (`"inline"`), or `null` when no schema was provided.
249
+ * Optional/additive.
250
+ */
251
+ source?: "path" | "inline" | null;
196
252
  /**
197
253
  * Whether validation was performed
198
254
  */
@@ -214,11 +270,13 @@ interface LoadedConfig<T> {
214
270
  }
215
271
  /**
216
272
  * Load configuration using the Three-Layer pattern:
217
- * 1. Defaults (required)
273
+ * 1. Defaults (required) — from a file path ({@link LoadConfigOptions}) or
274
+ * inline content ({@link LoadInlineConfigOptions})
218
275
  * 2. User Config (optional, XDG-compliant)
219
276
  * 3. Environment Variables (optional, prefix-based)
220
277
  */
221
278
  declare function loadConfig<T>(options: LoadConfigOptions): Promise<LoadedConfig<T>>;
279
+ declare function loadConfig<T>(options: LoadInlineConfigOptions): Promise<LoadedConfig<T>>;
222
280
 
223
281
  /**
224
282
  * Config Path API - implements Fulmen Config Path Standard
@@ -277,4 +335,4 @@ declare function resolveConfigPath(filename: string, searchPaths: string[], opti
277
335
  */
278
336
  declare const VERSION = "0.1.0";
279
337
 
280
- export { type AppIdentifier, type ConfigMetadata, ConfigPathError, type ConfigPathOptions, ConfigValidationError, type EnvAliasConflict, type LoadConfigOptions, type LoadedConfig, type PlatformDirs, type ResolveEnvAliasesResult, VERSION, type XDGBaseDirs, ensureDirExists, getAppCacheDir, getAppConfigDir, getAppDataDir, getConfigSearchPaths, getFulmenCacheDir, getFulmenConfigDir, getFulmenDataDir, getXDGBaseDirs, loadConfig, resolveConfigPath, resolveEnvAliases };
338
+ export { type AppIdentifier, type BaseLoadConfigOptions, type ConfigMetadata, ConfigPathError, type ConfigPathOptions, ConfigValidationError, type EnvAliasConflict, type LoadConfigOptions, type LoadInlineConfigOptions, type LoadedConfig, type PlatformDirs, type ResolveEnvAliasesResult, VERSION, type XDGBaseDirs, ensureDirExists, getAppCacheDir, getAppConfigDir, getAppDataDir, getConfigSearchPaths, getFulmenCacheDir, getFulmenConfigDir, getFulmenDataDir, getXDGBaseDirs, loadConfig, resolveConfigPath, resolveEnvAliases };
@@ -1631,9 +1631,16 @@ async function parseConfigFile(path) {
1631
1631
  throw new Error(`Unsupported config file extension: ${ext}`);
1632
1632
  }
1633
1633
  async function loadConfig(options) {
1634
- const { identity, defaultsPath, userConfigName } = options;
1634
+ const { identity, defaultsPath, defaults, userConfigName } = options;
1635
1635
  const activeLayers = ["defaults"];
1636
- let mergedConfig = await parseConfigFile(defaultsPath);
1636
+ let mergedConfig;
1637
+ if (defaults !== void 0) {
1638
+ mergedConfig = structuredClone(defaults);
1639
+ } else if (defaultsPath) {
1640
+ mergedConfig = await parseConfigFile(defaultsPath);
1641
+ } else {
1642
+ throw new Error("loadConfig requires either `defaults` (inline) or `defaultsPath`");
1643
+ }
1637
1644
  const configName = userConfigName || identity.app;
1638
1645
  const extensions = [".yaml", ".yml", ".json"];
1639
1646
  let userConfigPath = null;
@@ -1659,9 +1666,10 @@ async function loadConfig(options) {
1659
1666
  mergedConfig = deepMerge(mergedConfig, envConfig);
1660
1667
  activeLayers.push("env");
1661
1668
  }
1662
- if (options.schemaPath) {
1669
+ const schemaProvided = options.schema !== void 0 || options.schemaPath !== void 0;
1670
+ if (schemaProvided) {
1663
1671
  try {
1664
- const schemaContent = await readFile(options.schemaPath, "utf-8");
1672
+ const schemaContent = options.schema !== void 0 ? options.schema : await readFile(options.schemaPath, "utf-8");
1665
1673
  const validator = await compileSchema(schemaContent);
1666
1674
  const result = validateData(mergedConfig, validator);
1667
1675
  if (!result.valid) {
@@ -1681,15 +1689,18 @@ async function loadConfig(options) {
1681
1689
  return {
1682
1690
  config: mergedConfig,
1683
1691
  metadata: {
1684
- defaultsPath,
1692
+ defaultsPath: defaults !== void 0 ? "" : defaultsPath,
1693
+ defaultsSource: defaults !== void 0 ? "inline" : "path",
1685
1694
  userConfigPath,
1686
1695
  envPrefix,
1687
1696
  envVarsConsumed: includeEnvVarReport ? envVars?.consumedKeys : void 0,
1688
1697
  envVarsConsumedCount: includeEnvVarReport ? envVars?.consumedKeys.length : void 0,
1689
1698
  activeLayers,
1690
1699
  schema: {
1691
- path: options.schemaPath || null,
1692
- validated: !!options.schemaPath
1700
+ // Inline schema wins over schemaPath, and has no backing file → report null.
1701
+ path: options.schema !== void 0 ? null : options.schemaPath ?? null,
1702
+ source: options.schema !== void 0 ? "inline" : options.schemaPath ? "path" : null,
1703
+ validated: schemaProvided
1693
1704
  }
1694
1705
  }
1695
1706
  };