@optique/config 1.0.0-dev.428 → 1.0.0-dev.429
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-CYn_0yAG.d.ts → index-0XaZnIP1.d.ts} +45 -8
- package/dist/{index-D25zYfjf.d.cts → index-BZLEbR0f.d.cts} +45 -8
- package/dist/index.cjs +6 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/run.cjs +48 -110
- package/dist/run.d.cts +34 -8
- package/dist/run.d.ts +34 -8
- package/dist/run.js +49 -111
- package/dist/{src-Dm_17c1d.js → src-DaFxoeAp.js} +36 -2
- package/dist/{src-9MkUoh9z.cjs → src-loxvrkxq.cjs} +59 -1
- package/package.json +2 -2
|
@@ -9,6 +9,26 @@ import { Parser } from "@optique/core/parser";
|
|
|
9
9
|
* @since 0.10.0
|
|
10
10
|
*/
|
|
11
11
|
declare const configKey: unique symbol;
|
|
12
|
+
/**
|
|
13
|
+
* Unique symbol for config metadata in annotations.
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
declare const configMetaKey: unique symbol;
|
|
17
|
+
/**
|
|
18
|
+
* Metadata about the loaded config source.
|
|
19
|
+
*
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
*/
|
|
22
|
+
interface ConfigMeta {
|
|
23
|
+
/**
|
|
24
|
+
* Directory containing the config file.
|
|
25
|
+
*/
|
|
26
|
+
readonly configDir: string;
|
|
27
|
+
/**
|
|
28
|
+
* Absolute path to the config file.
|
|
29
|
+
*/
|
|
30
|
+
readonly configPath: string;
|
|
31
|
+
}
|
|
12
32
|
/**
|
|
13
33
|
* Sets active config data for a context.
|
|
14
34
|
* @internal
|
|
@@ -24,6 +44,21 @@ declare function getActiveConfig<T>(contextId: symbol): T | undefined;
|
|
|
24
44
|
* @internal
|
|
25
45
|
*/
|
|
26
46
|
declare function clearActiveConfig(contextId: symbol): void;
|
|
47
|
+
/**
|
|
48
|
+
* Sets active config metadata for a context.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
declare function setActiveConfigMeta<T>(contextId: symbol, meta: T): void;
|
|
52
|
+
/**
|
|
53
|
+
* Gets active config metadata for a context.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
declare function getActiveConfigMeta<T>(contextId: symbol): T | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Clears active config metadata for a context.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
declare function clearActiveConfigMeta(contextId: symbol): void;
|
|
27
62
|
/**
|
|
28
63
|
* Options for creating a config context.
|
|
29
64
|
*
|
|
@@ -64,7 +99,7 @@ interface ConfigContextRequiredOptions {
|
|
|
64
99
|
* @template T The validated config data type.
|
|
65
100
|
* @since 0.10.0
|
|
66
101
|
*/
|
|
67
|
-
interface ConfigContext<T> extends SourceContext<ConfigContextRequiredOptions> {
|
|
102
|
+
interface ConfigContext<T, TConfigMeta = ConfigMeta> extends SourceContext<ConfigContextRequiredOptions> {
|
|
68
103
|
/**
|
|
69
104
|
* The Standard Schema validator for the config file.
|
|
70
105
|
*/
|
|
@@ -77,6 +112,7 @@ interface ConfigContext<T> extends SourceContext<ConfigContextRequiredOptions> {
|
|
|
77
112
|
* with runWith() or runWithConfig() to provide configuration file support.
|
|
78
113
|
*
|
|
79
114
|
* @template T The output type of the config schema.
|
|
115
|
+
* @template TConfigMeta The metadata type for config sources.
|
|
80
116
|
* @param options Configuration options including schema and optional parser.
|
|
81
117
|
* @returns A config context that can be used with bindConfig() and runWithConfig().
|
|
82
118
|
* @since 0.10.0
|
|
@@ -94,7 +130,7 @@ interface ConfigContext<T> extends SourceContext<ConfigContextRequiredOptions> {
|
|
|
94
130
|
* const configContext = createConfigContext({ schema });
|
|
95
131
|
* ```
|
|
96
132
|
*/
|
|
97
|
-
declare function createConfigContext<T>(options: ConfigContextOptions<T>): ConfigContext<T>;
|
|
133
|
+
declare function createConfigContext<T, TConfigMeta = ConfigMeta>(options: ConfigContextOptions<T>): ConfigContext<T, TConfigMeta>;
|
|
98
134
|
/**
|
|
99
135
|
* Options for binding a parser to config values.
|
|
100
136
|
*
|
|
@@ -102,17 +138,18 @@ declare function createConfigContext<T>(options: ConfigContextOptions<T>): Confi
|
|
|
102
138
|
* @template TValue The value type extracted from config.
|
|
103
139
|
* @since 0.10.0
|
|
104
140
|
*/
|
|
105
|
-
interface BindConfigOptions<T, TValue> {
|
|
141
|
+
interface BindConfigOptions<T, TValue, TConfigMeta = ConfigMeta> {
|
|
106
142
|
/**
|
|
107
143
|
* The config context to use for fallback values.
|
|
108
144
|
*/
|
|
109
|
-
readonly context: ConfigContext<T>;
|
|
145
|
+
readonly context: ConfigContext<T, TConfigMeta>;
|
|
110
146
|
/**
|
|
111
147
|
* Key or accessor function to extract the value from config.
|
|
112
148
|
* Can be a property key (for top-level config values) or a function
|
|
113
|
-
* that extracts nested values.
|
|
149
|
+
* that extracts nested values. Accessor callbacks receive config metadata
|
|
150
|
+
* as the second argument.
|
|
114
151
|
*/
|
|
115
|
-
readonly key: keyof T | ((config: T) => TValue);
|
|
152
|
+
readonly key: keyof T | ((config: T, meta: TConfigMeta) => TValue);
|
|
116
153
|
/**
|
|
117
154
|
* Default value to use when neither CLI nor config provides a value.
|
|
118
155
|
* If not specified, the parser will fail when no value is available.
|
|
@@ -150,6 +187,6 @@ interface BindConfigOptions<T, TValue> {
|
|
|
150
187
|
* });
|
|
151
188
|
* ```
|
|
152
189
|
*/
|
|
153
|
-
declare function bindConfig<M extends "sync" | "async", TValue, TState, T>(parser: Parser<M, TValue, TState>, options: BindConfigOptions<T, TValue>): Parser<M, TValue, TState>;
|
|
190
|
+
declare function bindConfig<M extends "sync" | "async", TValue, TState, T, TConfigMeta = ConfigMeta>(parser: Parser<M, TValue, TState>, options: BindConfigOptions<T, TValue, TConfigMeta>): Parser<M, TValue, TState>;
|
|
154
191
|
//#endregion
|
|
155
|
-
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
|
|
192
|
+
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, ConfigMeta, bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta };
|
|
@@ -9,6 +9,26 @@ import { Parser } from "@optique/core/parser";
|
|
|
9
9
|
* @since 0.10.0
|
|
10
10
|
*/
|
|
11
11
|
declare const configKey: unique symbol;
|
|
12
|
+
/**
|
|
13
|
+
* Unique symbol for config metadata in annotations.
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
declare const configMetaKey: unique symbol;
|
|
17
|
+
/**
|
|
18
|
+
* Metadata about the loaded config source.
|
|
19
|
+
*
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
*/
|
|
22
|
+
interface ConfigMeta {
|
|
23
|
+
/**
|
|
24
|
+
* Directory containing the config file.
|
|
25
|
+
*/
|
|
26
|
+
readonly configDir: string;
|
|
27
|
+
/**
|
|
28
|
+
* Absolute path to the config file.
|
|
29
|
+
*/
|
|
30
|
+
readonly configPath: string;
|
|
31
|
+
}
|
|
12
32
|
/**
|
|
13
33
|
* Sets active config data for a context.
|
|
14
34
|
* @internal
|
|
@@ -24,6 +44,21 @@ declare function getActiveConfig<T>(contextId: symbol): T | undefined;
|
|
|
24
44
|
* @internal
|
|
25
45
|
*/
|
|
26
46
|
declare function clearActiveConfig(contextId: symbol): void;
|
|
47
|
+
/**
|
|
48
|
+
* Sets active config metadata for a context.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
declare function setActiveConfigMeta<T>(contextId: symbol, meta: T): void;
|
|
52
|
+
/**
|
|
53
|
+
* Gets active config metadata for a context.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
declare function getActiveConfigMeta<T>(contextId: symbol): T | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Clears active config metadata for a context.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
declare function clearActiveConfigMeta(contextId: symbol): void;
|
|
27
62
|
/**
|
|
28
63
|
* Options for creating a config context.
|
|
29
64
|
*
|
|
@@ -64,7 +99,7 @@ interface ConfigContextRequiredOptions {
|
|
|
64
99
|
* @template T The validated config data type.
|
|
65
100
|
* @since 0.10.0
|
|
66
101
|
*/
|
|
67
|
-
interface ConfigContext<T> extends SourceContext<ConfigContextRequiredOptions> {
|
|
102
|
+
interface ConfigContext<T, TConfigMeta = ConfigMeta> extends SourceContext<ConfigContextRequiredOptions> {
|
|
68
103
|
/**
|
|
69
104
|
* The Standard Schema validator for the config file.
|
|
70
105
|
*/
|
|
@@ -77,6 +112,7 @@ interface ConfigContext<T> extends SourceContext<ConfigContextRequiredOptions> {
|
|
|
77
112
|
* with runWith() or runWithConfig() to provide configuration file support.
|
|
78
113
|
*
|
|
79
114
|
* @template T The output type of the config schema.
|
|
115
|
+
* @template TConfigMeta The metadata type for config sources.
|
|
80
116
|
* @param options Configuration options including schema and optional parser.
|
|
81
117
|
* @returns A config context that can be used with bindConfig() and runWithConfig().
|
|
82
118
|
* @since 0.10.0
|
|
@@ -94,7 +130,7 @@ interface ConfigContext<T> extends SourceContext<ConfigContextRequiredOptions> {
|
|
|
94
130
|
* const configContext = createConfigContext({ schema });
|
|
95
131
|
* ```
|
|
96
132
|
*/
|
|
97
|
-
declare function createConfigContext<T>(options: ConfigContextOptions<T>): ConfigContext<T>;
|
|
133
|
+
declare function createConfigContext<T, TConfigMeta = ConfigMeta>(options: ConfigContextOptions<T>): ConfigContext<T, TConfigMeta>;
|
|
98
134
|
/**
|
|
99
135
|
* Options for binding a parser to config values.
|
|
100
136
|
*
|
|
@@ -102,17 +138,18 @@ declare function createConfigContext<T>(options: ConfigContextOptions<T>): Confi
|
|
|
102
138
|
* @template TValue The value type extracted from config.
|
|
103
139
|
* @since 0.10.0
|
|
104
140
|
*/
|
|
105
|
-
interface BindConfigOptions<T, TValue> {
|
|
141
|
+
interface BindConfigOptions<T, TValue, TConfigMeta = ConfigMeta> {
|
|
106
142
|
/**
|
|
107
143
|
* The config context to use for fallback values.
|
|
108
144
|
*/
|
|
109
|
-
readonly context: ConfigContext<T>;
|
|
145
|
+
readonly context: ConfigContext<T, TConfigMeta>;
|
|
110
146
|
/**
|
|
111
147
|
* Key or accessor function to extract the value from config.
|
|
112
148
|
* Can be a property key (for top-level config values) or a function
|
|
113
|
-
* that extracts nested values.
|
|
149
|
+
* that extracts nested values. Accessor callbacks receive config metadata
|
|
150
|
+
* as the second argument.
|
|
114
151
|
*/
|
|
115
|
-
readonly key: keyof T | ((config: T) => TValue);
|
|
152
|
+
readonly key: keyof T | ((config: T, meta: TConfigMeta) => TValue);
|
|
116
153
|
/**
|
|
117
154
|
* Default value to use when neither CLI nor config provides a value.
|
|
118
155
|
* If not specified, the parser will fail when no value is available.
|
|
@@ -150,6 +187,6 @@ interface BindConfigOptions<T, TValue> {
|
|
|
150
187
|
* });
|
|
151
188
|
* ```
|
|
152
189
|
*/
|
|
153
|
-
declare function bindConfig<M extends "sync" | "async", TValue, TState, T>(parser: Parser<M, TValue, TState>, options: BindConfigOptions<T, TValue>): Parser<M, TValue, TState>;
|
|
190
|
+
declare function bindConfig<M extends "sync" | "async", TValue, TState, T, TConfigMeta = ConfigMeta>(parser: Parser<M, TValue, TState>, options: BindConfigOptions<T, TValue, TConfigMeta>): Parser<M, TValue, TState>;
|
|
154
191
|
//#endregion
|
|
155
|
-
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
|
|
192
|
+
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, ConfigMeta, bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta };
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
const require_src = require('./src-
|
|
1
|
+
const require_src = require('./src-loxvrkxq.cjs');
|
|
2
2
|
|
|
3
3
|
exports.bindConfig = require_src.bindConfig;
|
|
4
4
|
exports.clearActiveConfig = require_src.clearActiveConfig;
|
|
5
|
+
exports.clearActiveConfigMeta = require_src.clearActiveConfigMeta;
|
|
5
6
|
exports.configKey = require_src.configKey;
|
|
7
|
+
exports.configMetaKey = require_src.configMetaKey;
|
|
6
8
|
exports.createConfigContext = require_src.createConfigContext;
|
|
7
9
|
exports.getActiveConfig = require_src.getActiveConfig;
|
|
8
|
-
exports.
|
|
10
|
+
exports.getActiveConfigMeta = require_src.getActiveConfigMeta;
|
|
11
|
+
exports.setActiveConfig = require_src.setActiveConfig;
|
|
12
|
+
exports.setActiveConfigMeta = require_src.setActiveConfigMeta;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig } from "./index-
|
|
2
|
-
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
|
|
1
|
+
import { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, ConfigMeta, bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta } from "./index-BZLEbR0f.cjs";
|
|
2
|
+
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, ConfigMeta, bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig } from "./index-
|
|
2
|
-
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
|
|
1
|
+
import { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, ConfigMeta, bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta } from "./index-0XaZnIP1.js";
|
|
2
|
+
export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, ConfigMeta, bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig } from "./src-
|
|
1
|
+
import { bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta } from "./src-DaFxoeAp.js";
|
|
2
2
|
|
|
3
|
-
export { bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
|
|
3
|
+
export { bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta };
|
package/dist/run.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_src = require('./src-
|
|
1
|
+
const require_src = require('./src-loxvrkxq.cjs');
|
|
2
2
|
const node_fs_promises = require_src.__toESM(require("node:fs/promises"));
|
|
3
3
|
const node_path = require_src.__toESM(require("node:path"));
|
|
4
4
|
const node_process = require_src.__toESM(require("node:process"));
|
|
@@ -18,132 +18,69 @@ function createConfigSourceContext(context, options) {
|
|
|
18
18
|
async getAnnotations(parsed) {
|
|
19
19
|
if (!parsed) return {};
|
|
20
20
|
let configData;
|
|
21
|
+
let configMeta;
|
|
21
22
|
if ("load" in options) {
|
|
22
23
|
const customOptions = options;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
|
|
32
|
-
}
|
|
33
|
-
configData = validationResult.value;
|
|
34
|
-
} catch (error) {
|
|
35
|
-
if (error instanceof Error && error.message.includes("validation")) throw error;
|
|
36
|
-
throw error;
|
|
24
|
+
const loaded = await Promise.resolve(customOptions.load(parsed));
|
|
25
|
+
const validation = context.schema["~standard"].validate(loaded.config);
|
|
26
|
+
let validationResult;
|
|
27
|
+
if (validation instanceof Promise) validationResult = await validation;
|
|
28
|
+
else validationResult = validation;
|
|
29
|
+
if (validationResult.issues) {
|
|
30
|
+
const firstIssue = validationResult.issues[0];
|
|
31
|
+
throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
|
|
37
32
|
}
|
|
33
|
+
configData = validationResult.value;
|
|
34
|
+
configMeta = loaded.meta;
|
|
38
35
|
} else {
|
|
39
36
|
const singleFileOptions = options;
|
|
40
37
|
const configPath = singleFileOptions.getConfigPath(parsed);
|
|
41
|
-
if (configPath)
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
if (configPath) {
|
|
39
|
+
const absoluteConfigPath = (0, node_path.resolve)(configPath);
|
|
40
|
+
const singleFileMeta = {
|
|
41
|
+
configDir: (0, node_path.dirname)(absoluteConfigPath),
|
|
42
|
+
configPath: absoluteConfigPath
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
const contents = await (0, node_fs_promises.readFile)(absoluteConfigPath);
|
|
46
|
+
let rawData;
|
|
47
|
+
if (singleFileOptions.fileParser) rawData = singleFileOptions.fileParser(contents);
|
|
48
|
+
else {
|
|
49
|
+
const text = new TextDecoder().decode(contents);
|
|
50
|
+
rawData = JSON.parse(text);
|
|
51
|
+
}
|
|
52
|
+
const validation = context.schema["~standard"].validate(rawData);
|
|
53
|
+
let validationResult;
|
|
54
|
+
if (validation instanceof Promise) validationResult = await validation;
|
|
55
|
+
else validationResult = validation;
|
|
56
|
+
if (validationResult.issues) {
|
|
57
|
+
const firstIssue = validationResult.issues[0];
|
|
58
|
+
throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
|
|
59
|
+
}
|
|
60
|
+
configData = validationResult.value;
|
|
61
|
+
configMeta = singleFileMeta;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (isErrnoException(error) && error.code === "ENOENT") configData = void 0;
|
|
64
|
+
else if (error instanceof SyntaxError) throw new Error(`Failed to parse config file ${absoluteConfigPath}: ${error.message}`);
|
|
65
|
+
else throw error;
|
|
56
66
|
}
|
|
57
|
-
configData = validationResult.value;
|
|
58
|
-
} catch (error) {
|
|
59
|
-
if (isErrnoException(error) && error.code === "ENOENT") configData = void 0;
|
|
60
|
-
else if (error instanceof SyntaxError) throw new Error(`Failed to parse config file ${configPath}: ${error.message}`);
|
|
61
|
-
else throw error;
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
if (configData !== void 0 && configData !== null) {
|
|
65
70
|
require_src.setActiveConfig(context.id, configData);
|
|
71
|
+
if (configMeta !== void 0) {
|
|
72
|
+
require_src.setActiveConfigMeta(context.id, configMeta);
|
|
73
|
+
return {
|
|
74
|
+
[require_src.configKey]: configData,
|
|
75
|
+
[require_src.configMetaKey]: configMeta
|
|
76
|
+
};
|
|
77
|
+
}
|
|
66
78
|
return { [require_src.configKey]: configData };
|
|
67
79
|
}
|
|
68
80
|
return {};
|
|
69
81
|
}
|
|
70
82
|
};
|
|
71
83
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Runs a parser with configuration file support using two-pass parsing.
|
|
74
|
-
*
|
|
75
|
-
* This function performs the following steps:
|
|
76
|
-
* 1. First pass: Parse arguments to extract config path or data
|
|
77
|
-
* 2. Load and validate: Load config file(s) and validate using Standard Schema
|
|
78
|
-
* 3. Second pass: Parse arguments again with config data as annotations
|
|
79
|
-
*
|
|
80
|
-
* The priority order for values is: CLI > config file > default.
|
|
81
|
-
*
|
|
82
|
-
* The function also supports help, version, and completion features. When these
|
|
83
|
-
* special commands are detected, config loading is skipped entirely, ensuring
|
|
84
|
-
* these features work even when config files don't exist.
|
|
85
|
-
*
|
|
86
|
-
* @template M The parser mode (sync or async).
|
|
87
|
-
* @template TValue The parser value type.
|
|
88
|
-
* @template TState The parser state type.
|
|
89
|
-
* @template T The config data type.
|
|
90
|
-
* @template THelp The return type when help is shown.
|
|
91
|
-
* @template TError The return type when an error occurs.
|
|
92
|
-
* @param parser The parser to execute.
|
|
93
|
-
* @param context The config context with schema.
|
|
94
|
-
* @param options Run options - either SingleFileOptions or CustomLoadOptions.
|
|
95
|
-
* @returns Promise that resolves to the parsed result.
|
|
96
|
-
* @throws Error if config file validation fails.
|
|
97
|
-
* @since 0.10.0
|
|
98
|
-
*
|
|
99
|
-
* @example Single file mode
|
|
100
|
-
* ```typescript
|
|
101
|
-
* import { z } from "zod";
|
|
102
|
-
* import { runWithConfig } from "@optique/config/run";
|
|
103
|
-
* import { createConfigContext, bindConfig } from "@optique/config";
|
|
104
|
-
*
|
|
105
|
-
* const schema = z.object({
|
|
106
|
-
* host: z.string(),
|
|
107
|
-
* port: z.number(),
|
|
108
|
-
* });
|
|
109
|
-
*
|
|
110
|
-
* const context = createConfigContext({ schema });
|
|
111
|
-
*
|
|
112
|
-
* const parser = object({
|
|
113
|
-
* config: option("--config", string()),
|
|
114
|
-
* host: bindConfig(option("--host", string()), {
|
|
115
|
-
* context,
|
|
116
|
-
* key: "host",
|
|
117
|
-
* default: "localhost",
|
|
118
|
-
* }),
|
|
119
|
-
* });
|
|
120
|
-
*
|
|
121
|
-
* const result = await runWithConfig(parser, context, {
|
|
122
|
-
* getConfigPath: (parsed) => parsed.config,
|
|
123
|
-
* args: process.argv.slice(2),
|
|
124
|
-
* help: { mode: "option", onShow: () => process.exit(0) },
|
|
125
|
-
* version: { value: "1.0.0", onShow: () => process.exit(0) },
|
|
126
|
-
* });
|
|
127
|
-
* ```
|
|
128
|
-
*
|
|
129
|
-
* @example Custom load mode (multi-file merging)
|
|
130
|
-
* ```typescript
|
|
131
|
-
* import { deepMerge } from "es-toolkit";
|
|
132
|
-
*
|
|
133
|
-
* const result = await runWithConfig(parser, context, {
|
|
134
|
-
* load: async (parsed) => {
|
|
135
|
-
* const configs = await Promise.all([
|
|
136
|
-
* loadToml("/etc/app/config.toml").catch(() => ({})),
|
|
137
|
-
* loadToml("~/.config/app/config.toml").catch(() => ({})),
|
|
138
|
-
* loadToml("./.app.toml").catch(() => ({})),
|
|
139
|
-
* ]);
|
|
140
|
-
* return deepMerge(...configs);
|
|
141
|
-
* },
|
|
142
|
-
* args: process.argv.slice(2),
|
|
143
|
-
* help: { mode: "option", onShow: () => process.exit(0) },
|
|
144
|
-
* });
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
84
|
async function runWithConfig(parser, context, options) {
|
|
148
85
|
const effectiveProgramName = options.programName ?? (typeof node_process.default !== "undefined" && node_process.default.argv?.[1] ? (0, node_path.basename)(node_process.default.argv[1]) : "cli");
|
|
149
86
|
const wrapperContext = createConfigSourceContext(context, options);
|
|
@@ -182,6 +119,7 @@ async function runWithConfig(parser, context, options) {
|
|
|
182
119
|
throw error;
|
|
183
120
|
} finally {
|
|
184
121
|
require_src.clearActiveConfig(context.id);
|
|
122
|
+
require_src.clearActiveConfigMeta(context.id);
|
|
185
123
|
}
|
|
186
124
|
}
|
|
187
125
|
|
package/dist/run.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConfigContext } from "./index-
|
|
1
|
+
import { ConfigContext, ConfigMeta } from "./index-BZLEbR0f.cjs";
|
|
2
2
|
import { Parser } from "@optique/core/parser";
|
|
3
3
|
import { RunOptions } from "@optique/core/facade";
|
|
4
4
|
|
|
@@ -109,6 +109,22 @@ interface SingleFileOptions<TValue, THelp = void, TError = never> {
|
|
|
109
109
|
*/
|
|
110
110
|
readonly onError?: RunOptions<THelp, TError>["onError"];
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Result type for custom config loading.
|
|
114
|
+
*
|
|
115
|
+
* @template TConfigMeta Metadata type associated with loaded config data.
|
|
116
|
+
* @since 1.0.0
|
|
117
|
+
*/
|
|
118
|
+
interface ConfigLoadResult<TConfigMeta = ConfigMeta> {
|
|
119
|
+
/**
|
|
120
|
+
* Raw config data to validate against the schema.
|
|
121
|
+
*/
|
|
122
|
+
readonly config: unknown;
|
|
123
|
+
/**
|
|
124
|
+
* Metadata about where the config came from.
|
|
125
|
+
*/
|
|
126
|
+
readonly meta: TConfigMeta;
|
|
127
|
+
}
|
|
112
128
|
/**
|
|
113
129
|
* Options for custom config loading with multi-file merging support.
|
|
114
130
|
*
|
|
@@ -117,7 +133,7 @@ interface SingleFileOptions<TValue, THelp = void, TError = never> {
|
|
|
117
133
|
* @template TError The return type when an error occurs.
|
|
118
134
|
* @since 0.10.0
|
|
119
135
|
*/
|
|
120
|
-
interface CustomLoadOptions<TValue, THelp = void, TError = never> {
|
|
136
|
+
interface CustomLoadOptions<TValue, TConfigMeta = ConfigMeta, THelp = void, TError = never> {
|
|
121
137
|
/**
|
|
122
138
|
* Custom loader function that receives the first-pass parse result and
|
|
123
139
|
* returns the config data (or a Promise of it). This allows full control
|
|
@@ -126,9 +142,9 @@ interface CustomLoadOptions<TValue, THelp = void, TError = never> {
|
|
|
126
142
|
* The returned data will be validated against the schema.
|
|
127
143
|
*
|
|
128
144
|
* @param parsed The result from the first parse pass.
|
|
129
|
-
* @returns
|
|
145
|
+
* @returns Config data and metadata (config is validated by schema).
|
|
130
146
|
*/
|
|
131
|
-
readonly load: (parsed: TValue) => Promise<
|
|
147
|
+
readonly load: (parsed: TValue) => Promise<ConfigLoadResult<TConfigMeta>> | ConfigLoadResult<TConfigMeta>;
|
|
132
148
|
/**
|
|
133
149
|
* Command-line arguments to parse.
|
|
134
150
|
* If not provided, defaults to an empty array.
|
|
@@ -216,7 +232,7 @@ interface CustomLoadOptions<TValue, THelp = void, TError = never> {
|
|
|
216
232
|
* @template TError The return type when an error occurs.
|
|
217
233
|
* @since 0.10.0
|
|
218
234
|
*/
|
|
219
|
-
type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOptions<TValue, THelp, TError> | CustomLoadOptions<TValue, THelp, TError>;
|
|
235
|
+
type RunWithConfigOptions<TValue, TConfigMeta = ConfigMeta, THelp = void, TError = never> = SingleFileOptions<TValue, THelp, TError> | CustomLoadOptions<TValue, TConfigMeta, THelp, TError>;
|
|
220
236
|
/**
|
|
221
237
|
* Runs a parser with configuration file support using two-pass parsing.
|
|
222
238
|
*
|
|
@@ -235,6 +251,7 @@ type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOpti
|
|
|
235
251
|
* @template TValue The parser value type.
|
|
236
252
|
* @template TState The parser state type.
|
|
237
253
|
* @template T The config data type.
|
|
254
|
+
* @template TConfigMeta The config metadata type.
|
|
238
255
|
* @template THelp The return type when help is shown.
|
|
239
256
|
* @template TError The return type when an error occurs.
|
|
240
257
|
* @param parser The parser to execute.
|
|
@@ -285,13 +302,22 @@ type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOpti
|
|
|
285
302
|
* loadToml("~/.config/app/config.toml").catch(() => ({})),
|
|
286
303
|
* loadToml("./.app.toml").catch(() => ({})),
|
|
287
304
|
* ]);
|
|
288
|
-
*
|
|
305
|
+
*
|
|
306
|
+
* const configPath = parsed.config ?? "./.app.toml";
|
|
307
|
+
* return {
|
|
308
|
+
* config: deepMerge(...configs),
|
|
309
|
+
* meta: {
|
|
310
|
+
* configPath,
|
|
311
|
+
* configDir: configPath.slice(0, configPath.lastIndexOf("/")),
|
|
312
|
+
* },
|
|
313
|
+
* };
|
|
289
314
|
* },
|
|
290
315
|
* args: process.argv.slice(2),
|
|
291
316
|
* help: { mode: "option", onShow: () => process.exit(0) },
|
|
292
317
|
* });
|
|
293
318
|
* ```
|
|
294
319
|
*/
|
|
295
|
-
declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T>, options:
|
|
320
|
+
declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T, ConfigMeta>, options: SingleFileOptions<TValue, THelp, TError>): Promise<TValue>;
|
|
321
|
+
declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, TConfigMeta = ConfigMeta, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T, TConfigMeta>, options: CustomLoadOptions<TValue, TConfigMeta, THelp, TError>): Promise<TValue>;
|
|
296
322
|
//#endregion
|
|
297
|
-
export { CustomLoadOptions, RunWithConfigOptions, SingleFileOptions, runWithConfig };
|
|
323
|
+
export { ConfigLoadResult, CustomLoadOptions, RunWithConfigOptions, SingleFileOptions, runWithConfig };
|
package/dist/run.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConfigContext } from "./index-
|
|
1
|
+
import { ConfigContext, ConfigMeta } from "./index-0XaZnIP1.js";
|
|
2
2
|
import { RunOptions } from "@optique/core/facade";
|
|
3
3
|
import { Parser } from "@optique/core/parser";
|
|
4
4
|
|
|
@@ -109,6 +109,22 @@ interface SingleFileOptions<TValue, THelp = void, TError = never> {
|
|
|
109
109
|
*/
|
|
110
110
|
readonly onError?: RunOptions<THelp, TError>["onError"];
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Result type for custom config loading.
|
|
114
|
+
*
|
|
115
|
+
* @template TConfigMeta Metadata type associated with loaded config data.
|
|
116
|
+
* @since 1.0.0
|
|
117
|
+
*/
|
|
118
|
+
interface ConfigLoadResult<TConfigMeta = ConfigMeta> {
|
|
119
|
+
/**
|
|
120
|
+
* Raw config data to validate against the schema.
|
|
121
|
+
*/
|
|
122
|
+
readonly config: unknown;
|
|
123
|
+
/**
|
|
124
|
+
* Metadata about where the config came from.
|
|
125
|
+
*/
|
|
126
|
+
readonly meta: TConfigMeta;
|
|
127
|
+
}
|
|
112
128
|
/**
|
|
113
129
|
* Options for custom config loading with multi-file merging support.
|
|
114
130
|
*
|
|
@@ -117,7 +133,7 @@ interface SingleFileOptions<TValue, THelp = void, TError = never> {
|
|
|
117
133
|
* @template TError The return type when an error occurs.
|
|
118
134
|
* @since 0.10.0
|
|
119
135
|
*/
|
|
120
|
-
interface CustomLoadOptions<TValue, THelp = void, TError = never> {
|
|
136
|
+
interface CustomLoadOptions<TValue, TConfigMeta = ConfigMeta, THelp = void, TError = never> {
|
|
121
137
|
/**
|
|
122
138
|
* Custom loader function that receives the first-pass parse result and
|
|
123
139
|
* returns the config data (or a Promise of it). This allows full control
|
|
@@ -126,9 +142,9 @@ interface CustomLoadOptions<TValue, THelp = void, TError = never> {
|
|
|
126
142
|
* The returned data will be validated against the schema.
|
|
127
143
|
*
|
|
128
144
|
* @param parsed The result from the first parse pass.
|
|
129
|
-
* @returns
|
|
145
|
+
* @returns Config data and metadata (config is validated by schema).
|
|
130
146
|
*/
|
|
131
|
-
readonly load: (parsed: TValue) => Promise<
|
|
147
|
+
readonly load: (parsed: TValue) => Promise<ConfigLoadResult<TConfigMeta>> | ConfigLoadResult<TConfigMeta>;
|
|
132
148
|
/**
|
|
133
149
|
* Command-line arguments to parse.
|
|
134
150
|
* If not provided, defaults to an empty array.
|
|
@@ -216,7 +232,7 @@ interface CustomLoadOptions<TValue, THelp = void, TError = never> {
|
|
|
216
232
|
* @template TError The return type when an error occurs.
|
|
217
233
|
* @since 0.10.0
|
|
218
234
|
*/
|
|
219
|
-
type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOptions<TValue, THelp, TError> | CustomLoadOptions<TValue, THelp, TError>;
|
|
235
|
+
type RunWithConfigOptions<TValue, TConfigMeta = ConfigMeta, THelp = void, TError = never> = SingleFileOptions<TValue, THelp, TError> | CustomLoadOptions<TValue, TConfigMeta, THelp, TError>;
|
|
220
236
|
/**
|
|
221
237
|
* Runs a parser with configuration file support using two-pass parsing.
|
|
222
238
|
*
|
|
@@ -235,6 +251,7 @@ type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOpti
|
|
|
235
251
|
* @template TValue The parser value type.
|
|
236
252
|
* @template TState The parser state type.
|
|
237
253
|
* @template T The config data type.
|
|
254
|
+
* @template TConfigMeta The config metadata type.
|
|
238
255
|
* @template THelp The return type when help is shown.
|
|
239
256
|
* @template TError The return type when an error occurs.
|
|
240
257
|
* @param parser The parser to execute.
|
|
@@ -285,13 +302,22 @@ type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOpti
|
|
|
285
302
|
* loadToml("~/.config/app/config.toml").catch(() => ({})),
|
|
286
303
|
* loadToml("./.app.toml").catch(() => ({})),
|
|
287
304
|
* ]);
|
|
288
|
-
*
|
|
305
|
+
*
|
|
306
|
+
* const configPath = parsed.config ?? "./.app.toml";
|
|
307
|
+
* return {
|
|
308
|
+
* config: deepMerge(...configs),
|
|
309
|
+
* meta: {
|
|
310
|
+
* configPath,
|
|
311
|
+
* configDir: configPath.slice(0, configPath.lastIndexOf("/")),
|
|
312
|
+
* },
|
|
313
|
+
* };
|
|
289
314
|
* },
|
|
290
315
|
* args: process.argv.slice(2),
|
|
291
316
|
* help: { mode: "option", onShow: () => process.exit(0) },
|
|
292
317
|
* });
|
|
293
318
|
* ```
|
|
294
319
|
*/
|
|
295
|
-
declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T>, options:
|
|
320
|
+
declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T, ConfigMeta>, options: SingleFileOptions<TValue, THelp, TError>): Promise<TValue>;
|
|
321
|
+
declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, TConfigMeta = ConfigMeta, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T, TConfigMeta>, options: CustomLoadOptions<TValue, TConfigMeta, THelp, TError>): Promise<TValue>;
|
|
296
322
|
//#endregion
|
|
297
|
-
export { CustomLoadOptions, RunWithConfigOptions, SingleFileOptions, runWithConfig };
|
|
323
|
+
export { ConfigLoadResult, CustomLoadOptions, RunWithConfigOptions, SingleFileOptions, runWithConfig };
|
package/dist/run.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { clearActiveConfig, configKey, setActiveConfig } from "./src-
|
|
1
|
+
import { clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, setActiveConfig, setActiveConfigMeta } from "./src-DaFxoeAp.js";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { basename } from "node:path";
|
|
3
|
+
import { basename, dirname, resolve } from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { runWith } from "@optique/core/facade";
|
|
6
6
|
|
|
@@ -18,132 +18,69 @@ function createConfigSourceContext(context, options) {
|
|
|
18
18
|
async getAnnotations(parsed) {
|
|
19
19
|
if (!parsed) return {};
|
|
20
20
|
let configData;
|
|
21
|
+
let configMeta;
|
|
21
22
|
if ("load" in options) {
|
|
22
23
|
const customOptions = options;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
|
|
32
|
-
}
|
|
33
|
-
configData = validationResult.value;
|
|
34
|
-
} catch (error) {
|
|
35
|
-
if (error instanceof Error && error.message.includes("validation")) throw error;
|
|
36
|
-
throw error;
|
|
24
|
+
const loaded = await Promise.resolve(customOptions.load(parsed));
|
|
25
|
+
const validation = context.schema["~standard"].validate(loaded.config);
|
|
26
|
+
let validationResult;
|
|
27
|
+
if (validation instanceof Promise) validationResult = await validation;
|
|
28
|
+
else validationResult = validation;
|
|
29
|
+
if (validationResult.issues) {
|
|
30
|
+
const firstIssue = validationResult.issues[0];
|
|
31
|
+
throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
|
|
37
32
|
}
|
|
33
|
+
configData = validationResult.value;
|
|
34
|
+
configMeta = loaded.meta;
|
|
38
35
|
} else {
|
|
39
36
|
const singleFileOptions = options;
|
|
40
37
|
const configPath = singleFileOptions.getConfigPath(parsed);
|
|
41
|
-
if (configPath)
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
if (configPath) {
|
|
39
|
+
const absoluteConfigPath = resolve(configPath);
|
|
40
|
+
const singleFileMeta = {
|
|
41
|
+
configDir: dirname(absoluteConfigPath),
|
|
42
|
+
configPath: absoluteConfigPath
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
const contents = await readFile(absoluteConfigPath);
|
|
46
|
+
let rawData;
|
|
47
|
+
if (singleFileOptions.fileParser) rawData = singleFileOptions.fileParser(contents);
|
|
48
|
+
else {
|
|
49
|
+
const text = new TextDecoder().decode(contents);
|
|
50
|
+
rawData = JSON.parse(text);
|
|
51
|
+
}
|
|
52
|
+
const validation = context.schema["~standard"].validate(rawData);
|
|
53
|
+
let validationResult;
|
|
54
|
+
if (validation instanceof Promise) validationResult = await validation;
|
|
55
|
+
else validationResult = validation;
|
|
56
|
+
if (validationResult.issues) {
|
|
57
|
+
const firstIssue = validationResult.issues[0];
|
|
58
|
+
throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
|
|
59
|
+
}
|
|
60
|
+
configData = validationResult.value;
|
|
61
|
+
configMeta = singleFileMeta;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (isErrnoException(error) && error.code === "ENOENT") configData = void 0;
|
|
64
|
+
else if (error instanceof SyntaxError) throw new Error(`Failed to parse config file ${absoluteConfigPath}: ${error.message}`);
|
|
65
|
+
else throw error;
|
|
56
66
|
}
|
|
57
|
-
configData = validationResult.value;
|
|
58
|
-
} catch (error) {
|
|
59
|
-
if (isErrnoException(error) && error.code === "ENOENT") configData = void 0;
|
|
60
|
-
else if (error instanceof SyntaxError) throw new Error(`Failed to parse config file ${configPath}: ${error.message}`);
|
|
61
|
-
else throw error;
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
if (configData !== void 0 && configData !== null) {
|
|
65
70
|
setActiveConfig(context.id, configData);
|
|
71
|
+
if (configMeta !== void 0) {
|
|
72
|
+
setActiveConfigMeta(context.id, configMeta);
|
|
73
|
+
return {
|
|
74
|
+
[configKey]: configData,
|
|
75
|
+
[configMetaKey]: configMeta
|
|
76
|
+
};
|
|
77
|
+
}
|
|
66
78
|
return { [configKey]: configData };
|
|
67
79
|
}
|
|
68
80
|
return {};
|
|
69
81
|
}
|
|
70
82
|
};
|
|
71
83
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Runs a parser with configuration file support using two-pass parsing.
|
|
74
|
-
*
|
|
75
|
-
* This function performs the following steps:
|
|
76
|
-
* 1. First pass: Parse arguments to extract config path or data
|
|
77
|
-
* 2. Load and validate: Load config file(s) and validate using Standard Schema
|
|
78
|
-
* 3. Second pass: Parse arguments again with config data as annotations
|
|
79
|
-
*
|
|
80
|
-
* The priority order for values is: CLI > config file > default.
|
|
81
|
-
*
|
|
82
|
-
* The function also supports help, version, and completion features. When these
|
|
83
|
-
* special commands are detected, config loading is skipped entirely, ensuring
|
|
84
|
-
* these features work even when config files don't exist.
|
|
85
|
-
*
|
|
86
|
-
* @template M The parser mode (sync or async).
|
|
87
|
-
* @template TValue The parser value type.
|
|
88
|
-
* @template TState The parser state type.
|
|
89
|
-
* @template T The config data type.
|
|
90
|
-
* @template THelp The return type when help is shown.
|
|
91
|
-
* @template TError The return type when an error occurs.
|
|
92
|
-
* @param parser The parser to execute.
|
|
93
|
-
* @param context The config context with schema.
|
|
94
|
-
* @param options Run options - either SingleFileOptions or CustomLoadOptions.
|
|
95
|
-
* @returns Promise that resolves to the parsed result.
|
|
96
|
-
* @throws Error if config file validation fails.
|
|
97
|
-
* @since 0.10.0
|
|
98
|
-
*
|
|
99
|
-
* @example Single file mode
|
|
100
|
-
* ```typescript
|
|
101
|
-
* import { z } from "zod";
|
|
102
|
-
* import { runWithConfig } from "@optique/config/run";
|
|
103
|
-
* import { createConfigContext, bindConfig } from "@optique/config";
|
|
104
|
-
*
|
|
105
|
-
* const schema = z.object({
|
|
106
|
-
* host: z.string(),
|
|
107
|
-
* port: z.number(),
|
|
108
|
-
* });
|
|
109
|
-
*
|
|
110
|
-
* const context = createConfigContext({ schema });
|
|
111
|
-
*
|
|
112
|
-
* const parser = object({
|
|
113
|
-
* config: option("--config", string()),
|
|
114
|
-
* host: bindConfig(option("--host", string()), {
|
|
115
|
-
* context,
|
|
116
|
-
* key: "host",
|
|
117
|
-
* default: "localhost",
|
|
118
|
-
* }),
|
|
119
|
-
* });
|
|
120
|
-
*
|
|
121
|
-
* const result = await runWithConfig(parser, context, {
|
|
122
|
-
* getConfigPath: (parsed) => parsed.config,
|
|
123
|
-
* args: process.argv.slice(2),
|
|
124
|
-
* help: { mode: "option", onShow: () => process.exit(0) },
|
|
125
|
-
* version: { value: "1.0.0", onShow: () => process.exit(0) },
|
|
126
|
-
* });
|
|
127
|
-
* ```
|
|
128
|
-
*
|
|
129
|
-
* @example Custom load mode (multi-file merging)
|
|
130
|
-
* ```typescript
|
|
131
|
-
* import { deepMerge } from "es-toolkit";
|
|
132
|
-
*
|
|
133
|
-
* const result = await runWithConfig(parser, context, {
|
|
134
|
-
* load: async (parsed) => {
|
|
135
|
-
* const configs = await Promise.all([
|
|
136
|
-
* loadToml("/etc/app/config.toml").catch(() => ({})),
|
|
137
|
-
* loadToml("~/.config/app/config.toml").catch(() => ({})),
|
|
138
|
-
* loadToml("./.app.toml").catch(() => ({})),
|
|
139
|
-
* ]);
|
|
140
|
-
* return deepMerge(...configs);
|
|
141
|
-
* },
|
|
142
|
-
* args: process.argv.slice(2),
|
|
143
|
-
* help: { mode: "option", onShow: () => process.exit(0) },
|
|
144
|
-
* });
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
84
|
async function runWithConfig(parser, context, options) {
|
|
148
85
|
const effectiveProgramName = options.programName ?? (typeof process !== "undefined" && process.argv?.[1] ? basename(process.argv[1]) : "cli");
|
|
149
86
|
const wrapperContext = createConfigSourceContext(context, options);
|
|
@@ -182,6 +119,7 @@ async function runWithConfig(parser, context, options) {
|
|
|
182
119
|
throw error;
|
|
183
120
|
} finally {
|
|
184
121
|
clearActiveConfig(context.id);
|
|
122
|
+
clearActiveConfigMeta(context.id);
|
|
185
123
|
}
|
|
186
124
|
}
|
|
187
125
|
|
|
@@ -8,6 +8,11 @@ import { message } from "@optique/core/message";
|
|
|
8
8
|
*/
|
|
9
9
|
const configKey = Symbol.for("@optique/config");
|
|
10
10
|
/**
|
|
11
|
+
* Unique symbol for config metadata in annotations.
|
|
12
|
+
* @since 1.0.0
|
|
13
|
+
*/
|
|
14
|
+
const configMetaKey = Symbol.for("@optique/config/meta");
|
|
15
|
+
/**
|
|
11
16
|
* Internal registry for active config data during runWithConfig execution.
|
|
12
17
|
* This is a workaround for the limitation that object() doesn't propagate
|
|
13
18
|
* annotations to child field parsers.
|
|
@@ -15,6 +20,11 @@ const configKey = Symbol.for("@optique/config");
|
|
|
15
20
|
*/
|
|
16
21
|
const activeConfigRegistry = /* @__PURE__ */ new Map();
|
|
17
22
|
/**
|
|
23
|
+
* Internal registry for active config metadata during runWithConfig execution.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
const activeConfigMetaRegistry = /* @__PURE__ */ new Map();
|
|
27
|
+
/**
|
|
18
28
|
* Sets active config data for a context.
|
|
19
29
|
* @internal
|
|
20
30
|
*/
|
|
@@ -36,12 +46,34 @@ function clearActiveConfig(contextId) {
|
|
|
36
46
|
activeConfigRegistry.delete(contextId);
|
|
37
47
|
}
|
|
38
48
|
/**
|
|
49
|
+
* Sets active config metadata for a context.
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
function setActiveConfigMeta(contextId, meta) {
|
|
53
|
+
activeConfigMetaRegistry.set(contextId, meta);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Gets active config metadata for a context.
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
function getActiveConfigMeta(contextId) {
|
|
60
|
+
return activeConfigMetaRegistry.get(contextId);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Clears active config metadata for a context.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
function clearActiveConfigMeta(contextId) {
|
|
67
|
+
activeConfigMetaRegistry.delete(contextId);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
39
70
|
* Creates a config context for use with Optique parsers.
|
|
40
71
|
*
|
|
41
72
|
* The config context implements the SourceContext interface and can be used
|
|
42
73
|
* with runWith() or runWithConfig() to provide configuration file support.
|
|
43
74
|
*
|
|
44
75
|
* @template T The output type of the config schema.
|
|
76
|
+
* @template TConfigMeta The metadata type for config sources.
|
|
45
77
|
* @param options Configuration options including schema and optional parser.
|
|
46
78
|
* @returns A config context that can be used with bindConfig() and runWithConfig().
|
|
47
79
|
* @since 0.10.0
|
|
@@ -208,13 +240,15 @@ function bindConfig(parser, options) {
|
|
|
208
240
|
function getConfigOrDefault(state, options) {
|
|
209
241
|
const annotations = getAnnotations(state);
|
|
210
242
|
let configData = annotations?.[configKey];
|
|
243
|
+
let configMeta = annotations?.[configMetaKey];
|
|
211
244
|
if (configData === void 0 || configData === null) {
|
|
212
245
|
const contextId = options.context.id;
|
|
213
246
|
configData = getActiveConfig(contextId);
|
|
247
|
+
configMeta = getActiveConfigMeta(contextId);
|
|
214
248
|
}
|
|
215
249
|
let configValue;
|
|
216
250
|
if (configData !== void 0 && configData !== null) if (typeof options.key === "function") try {
|
|
217
|
-
configValue = options.key(configData);
|
|
251
|
+
configValue = options.key(configData, configMeta);
|
|
218
252
|
} catch {
|
|
219
253
|
configValue = void 0;
|
|
220
254
|
}
|
|
@@ -234,4 +268,4 @@ function getConfigOrDefault(state, options) {
|
|
|
234
268
|
}
|
|
235
269
|
|
|
236
270
|
//#endregion
|
|
237
|
-
export { bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
|
|
271
|
+
export { bindConfig, clearActiveConfig, clearActiveConfigMeta, configKey, configMetaKey, createConfigContext, getActiveConfig, getActiveConfigMeta, setActiveConfig, setActiveConfigMeta };
|
|
@@ -31,6 +31,11 @@ const __optique_core_message = __toESM(require("@optique/core/message"));
|
|
|
31
31
|
*/
|
|
32
32
|
const configKey = Symbol.for("@optique/config");
|
|
33
33
|
/**
|
|
34
|
+
* Unique symbol for config metadata in annotations.
|
|
35
|
+
* @since 1.0.0
|
|
36
|
+
*/
|
|
37
|
+
const configMetaKey = Symbol.for("@optique/config/meta");
|
|
38
|
+
/**
|
|
34
39
|
* Internal registry for active config data during runWithConfig execution.
|
|
35
40
|
* This is a workaround for the limitation that object() doesn't propagate
|
|
36
41
|
* annotations to child field parsers.
|
|
@@ -38,6 +43,11 @@ const configKey = Symbol.for("@optique/config");
|
|
|
38
43
|
*/
|
|
39
44
|
const activeConfigRegistry = /* @__PURE__ */ new Map();
|
|
40
45
|
/**
|
|
46
|
+
* Internal registry for active config metadata during runWithConfig execution.
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
const activeConfigMetaRegistry = /* @__PURE__ */ new Map();
|
|
50
|
+
/**
|
|
41
51
|
* Sets active config data for a context.
|
|
42
52
|
* @internal
|
|
43
53
|
*/
|
|
@@ -59,12 +69,34 @@ function clearActiveConfig(contextId) {
|
|
|
59
69
|
activeConfigRegistry.delete(contextId);
|
|
60
70
|
}
|
|
61
71
|
/**
|
|
72
|
+
* Sets active config metadata for a context.
|
|
73
|
+
* @internal
|
|
74
|
+
*/
|
|
75
|
+
function setActiveConfigMeta(contextId, meta) {
|
|
76
|
+
activeConfigMetaRegistry.set(contextId, meta);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Gets active config metadata for a context.
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
function getActiveConfigMeta(contextId) {
|
|
83
|
+
return activeConfigMetaRegistry.get(contextId);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Clears active config metadata for a context.
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
function clearActiveConfigMeta(contextId) {
|
|
90
|
+
activeConfigMetaRegistry.delete(contextId);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
62
93
|
* Creates a config context for use with Optique parsers.
|
|
63
94
|
*
|
|
64
95
|
* The config context implements the SourceContext interface and can be used
|
|
65
96
|
* with runWith() or runWithConfig() to provide configuration file support.
|
|
66
97
|
*
|
|
67
98
|
* @template T The output type of the config schema.
|
|
99
|
+
* @template TConfigMeta The metadata type for config sources.
|
|
68
100
|
* @param options Configuration options including schema and optional parser.
|
|
69
101
|
* @returns A config context that can be used with bindConfig() and runWithConfig().
|
|
70
102
|
* @since 0.10.0
|
|
@@ -231,13 +263,15 @@ function bindConfig(parser, options) {
|
|
|
231
263
|
function getConfigOrDefault(state, options) {
|
|
232
264
|
const annotations = (0, __optique_core_annotations.getAnnotations)(state);
|
|
233
265
|
let configData = annotations?.[configKey];
|
|
266
|
+
let configMeta = annotations?.[configMetaKey];
|
|
234
267
|
if (configData === void 0 || configData === null) {
|
|
235
268
|
const contextId = options.context.id;
|
|
236
269
|
configData = getActiveConfig(contextId);
|
|
270
|
+
configMeta = getActiveConfigMeta(contextId);
|
|
237
271
|
}
|
|
238
272
|
let configValue;
|
|
239
273
|
if (configData !== void 0 && configData !== null) if (typeof options.key === "function") try {
|
|
240
|
-
configValue = options.key(configData);
|
|
274
|
+
configValue = options.key(configData, configMeta);
|
|
241
275
|
} catch {
|
|
242
276
|
configValue = void 0;
|
|
243
277
|
}
|
|
@@ -275,12 +309,24 @@ Object.defineProperty(exports, 'clearActiveConfig', {
|
|
|
275
309
|
return clearActiveConfig;
|
|
276
310
|
}
|
|
277
311
|
});
|
|
312
|
+
Object.defineProperty(exports, 'clearActiveConfigMeta', {
|
|
313
|
+
enumerable: true,
|
|
314
|
+
get: function () {
|
|
315
|
+
return clearActiveConfigMeta;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
278
318
|
Object.defineProperty(exports, 'configKey', {
|
|
279
319
|
enumerable: true,
|
|
280
320
|
get: function () {
|
|
281
321
|
return configKey;
|
|
282
322
|
}
|
|
283
323
|
});
|
|
324
|
+
Object.defineProperty(exports, 'configMetaKey', {
|
|
325
|
+
enumerable: true,
|
|
326
|
+
get: function () {
|
|
327
|
+
return configMetaKey;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
284
330
|
Object.defineProperty(exports, 'createConfigContext', {
|
|
285
331
|
enumerable: true,
|
|
286
332
|
get: function () {
|
|
@@ -293,9 +339,21 @@ Object.defineProperty(exports, 'getActiveConfig', {
|
|
|
293
339
|
return getActiveConfig;
|
|
294
340
|
}
|
|
295
341
|
});
|
|
342
|
+
Object.defineProperty(exports, 'getActiveConfigMeta', {
|
|
343
|
+
enumerable: true,
|
|
344
|
+
get: function () {
|
|
345
|
+
return getActiveConfigMeta;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
296
348
|
Object.defineProperty(exports, 'setActiveConfig', {
|
|
297
349
|
enumerable: true,
|
|
298
350
|
get: function () {
|
|
299
351
|
return setActiveConfig;
|
|
300
352
|
}
|
|
353
|
+
});
|
|
354
|
+
Object.defineProperty(exports, 'setActiveConfigMeta', {
|
|
355
|
+
enumerable: true,
|
|
356
|
+
get: function () {
|
|
357
|
+
return setActiveConfigMeta;
|
|
358
|
+
}
|
|
301
359
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optique/config",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.429+66edc8ed",
|
|
4
4
|
"description": "Configuration file support for Optique with Standard Schema validation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CLI",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@standard-schema/spec": "^1.1.0"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@optique/core": "1.0.0-dev.
|
|
70
|
+
"@optique/core": "1.0.0-dev.429+66edc8ed"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@standard-schema/spec": "^1.1.0",
|