@optique/env 1.0.0-dev.0 → 1.0.0-dev.1116

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
@@ -23,6 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  const __optique_core_annotations = __toESM(require("@optique/core/annotations"));
25
25
  const __optique_core_message = __toESM(require("@optique/core/message"));
26
+ const __optique_core_mode_dispatch = __toESM(require("@optique/core/mode-dispatch"));
26
27
  const __optique_core_valueparser = __toESM(require("@optique/core/valueparser"));
27
28
 
28
29
  //#region src/index.ts
@@ -62,16 +63,20 @@ function defaultEnvSource(key) {
62
63
  *
63
64
  * @param options Environment context options.
64
65
  * @returns A context that provides environment source annotations.
66
+ * @throws {TypeError} If `source` is not a function.
65
67
  * @since 1.0.0
66
68
  */
67
69
  function createEnvContext(options = {}) {
68
70
  const contextId = Symbol(`@optique/env context:${Math.random()}`);
69
- const source = options.source ?? defaultEnvSource;
71
+ const rawSource = options.source;
72
+ if (rawSource !== void 0 && typeof rawSource !== "function") throw new TypeError(`Expected source to be a function, but got: ${rawSource === null ? "null" : Array.isArray(rawSource) ? "array" : typeof rawSource}.`);
73
+ const source = rawSource ?? defaultEnvSource;
70
74
  const prefix = options.prefix ?? "";
71
75
  return {
72
76
  id: contextId,
73
77
  prefix,
74
78
  source,
79
+ mode: "static",
75
80
  getAnnotations() {
76
81
  const sourceData = {
77
82
  prefix,
@@ -98,13 +103,22 @@ function createEnvContext(options = {}) {
98
103
  * @param parser Parser that reads CLI values.
99
104
  * @param options Environment binding options.
100
105
  * @returns A parser with environment fallback behavior.
106
+ * @throws {TypeError} If `key` is not a string or `parser` is not a valid
107
+ * {@link ValueParser}.
108
+ * @throws {Error} If the inner parser throws while parsing or completing a
109
+ * value, if the environment source throws while reading a
110
+ * variable, or if the environment value parser throws while
111
+ * parsing the environment variable value.
101
112
  * @since 1.0.0
102
113
  */
103
114
  function bindEnv(parser, options) {
115
+ if (typeof options.key !== "string") throw new TypeError(`Expected key to be a string, but got: ${options.key === null ? "null" : Array.isArray(options.key) ? "array" : typeof options.key}.`);
116
+ if (!(0, __optique_core_valueparser.isValueParser)(options.parser)) throw new TypeError(`Expected parser to be a ValueParser, but got: ${options.parser === null ? "null" : Array.isArray(options.parser) ? "array" : typeof options.parser}.`);
104
117
  const envBindStateKey = Symbol("@optique/env/bindState");
105
118
  function isEnvBindState(value) {
106
119
  return value != null && typeof value === "object" && envBindStateKey in value;
107
120
  }
121
+ const deferPromptUntilConfigResolves = parser.shouldDeferCompletion;
108
122
  return {
109
123
  $mode: parser.$mode,
110
124
  $valueType: parser.$valueType,
@@ -122,25 +136,25 @@ function bindEnv(parser, options) {
122
136
  ...context,
123
137
  state: innerState
124
138
  } : context;
125
- const processResult = (result$1) => {
126
- if (result$1.success) {
127
- const cliConsumed = result$1.consumed.length > 0;
139
+ const processResult = (result) => {
140
+ if (result.success) {
141
+ const cliConsumed = result.consumed.length > 0;
128
142
  const nextState$1 = {
129
143
  [envBindStateKey]: true,
130
144
  hasCliValue: cliConsumed,
131
- cliState: result$1.next.state,
145
+ cliState: result.next.state,
132
146
  ...annotations && { [__optique_core_annotations.annotationKey]: annotations }
133
147
  };
134
148
  return {
135
149
  success: true,
136
150
  next: {
137
- ...result$1.next,
151
+ ...result.next,
138
152
  state: nextState$1
139
153
  },
140
- consumed: result$1.consumed
154
+ consumed: result.consumed
141
155
  };
142
156
  }
143
- if (result$1.consumed > 0) return result$1;
157
+ if (result.consumed > 0) return result;
144
158
  const nextState = {
145
159
  [envBindStateKey]: true,
146
160
  hasCliValue: false,
@@ -155,48 +169,47 @@ function bindEnv(parser, options) {
155
169
  consumed: []
156
170
  };
157
171
  };
158
- const result = parser.parse(innerContext);
159
- if (result instanceof Promise) return result.then(processResult);
160
- return processResult(result);
172
+ return (0, __optique_core_mode_dispatch.mapModeValue)(parser.$mode, parser.parse(innerContext), processResult);
161
173
  },
162
174
  complete: (state) => {
163
175
  if (isEnvBindState(state) && state.hasCliValue) return parser.complete(state.cliState);
164
176
  return getEnvOrDefault(state, options, parser.$mode, parser, isEnvBindState(state) ? state.cliState : void 0);
165
177
  },
166
178
  suggest: parser.suggest,
179
+ ...typeof deferPromptUntilConfigResolves === "function" ? { shouldDeferCompletion: deferPromptUntilConfigResolves.bind(parser) } : {},
167
180
  getDocFragments(state, upperDefaultValue) {
168
181
  const defaultValue = upperDefaultValue ?? options.default;
169
182
  return parser.getDocFragments(state, defaultValue);
170
183
  }
171
184
  };
172
185
  }
173
- function wrapForMode(mode, value) {
174
- if (mode === "async") return Promise.resolve(value);
175
- return value;
176
- }
177
186
  function getEnvOrDefault(state, options, mode, innerParser, innerState) {
178
187
  const annotations = (0, __optique_core_annotations.getAnnotations)(state);
179
188
  const sourceData = annotations?.[options.context.id] ?? getActiveEnvSource(options.context.id);
180
189
  const fullKey = `${sourceData?.prefix ?? options.context.prefix}${options.key}`;
181
190
  const rawValue = sourceData?.source(fullKey);
182
191
  if (rawValue !== void 0) {
192
+ if (typeof rawValue !== "string") {
193
+ const type = rawValue === null ? "null" : Array.isArray(rawValue) ? "array" : typeof rawValue;
194
+ return (0, __optique_core_mode_dispatch.wrapForMode)(mode, {
195
+ success: false,
196
+ error: __optique_core_message.message`Environment variable ${(0, __optique_core_message.envVar)(fullKey)} must be a string, but got: ${type}.`
197
+ });
198
+ }
183
199
  const parsed = options.parser.parse(rawValue);
184
- if (parsed instanceof Promise) return parsed;
185
- return wrapForMode(mode, parsed);
200
+ return (0, __optique_core_mode_dispatch.wrapForMode)(mode, parsed);
186
201
  }
187
- if (options.default !== void 0) return wrapForMode(mode, {
202
+ if (options.default !== void 0) return (0, __optique_core_mode_dispatch.wrapForMode)(mode, {
188
203
  success: true,
189
204
  value: options.default
190
205
  });
191
206
  if (innerParser != null) {
192
207
  const completeState = innerState ?? innerParser.initialState;
193
- const result = innerParser.complete(completeState);
194
- if (result instanceof Promise) return result;
195
- return wrapForMode(mode, result);
208
+ return (0, __optique_core_mode_dispatch.wrapForMode)(mode, innerParser.complete(completeState));
196
209
  }
197
- return wrapForMode(mode, {
210
+ return (0, __optique_core_mode_dispatch.wrapForMode)(mode, {
198
211
  success: false,
199
- error: __optique_core_message.message`Missing required environment variable: ${fullKey}.`
212
+ error: __optique_core_message.message`Missing required environment variable: ${(0, __optique_core_message.envVar)(fullKey)}`
200
213
  });
201
214
  }
202
215
  const TRUE_LITERALS = [
@@ -221,6 +234,7 @@ const FALSE_LITERALS = [
221
234
  *
222
235
  * @param options Parser configuration options.
223
236
  * @returns A value parser for Boolean values.
237
+ * @throws {TypeError} If `options.metavar` is an empty string.
224
238
  * @since 1.0.0
225
239
  */
226
240
  function bool(options = {}) {
@@ -247,6 +261,14 @@ function bool(options = {}) {
247
261
  },
248
262
  format(value) {
249
263
  return value ? "true" : "false";
264
+ },
265
+ suggest(prefix) {
266
+ const allLiterals = [...TRUE_LITERALS, ...FALSE_LITERALS];
267
+ const normalizedPrefix = prefix.toLowerCase();
268
+ return allLiterals.filter((lit) => lit.startsWith(normalizedPrefix)).map((lit) => ({
269
+ kind: "literal",
270
+ text: lit
271
+ }));
250
272
  }
251
273
  };
252
274
  }
package/dist/index.d.cts CHANGED
@@ -72,6 +72,7 @@ declare function clearActiveEnvSource(contextId: symbol): void;
72
72
  *
73
73
  * @param options Environment context options.
74
74
  * @returns A context that provides environment source annotations.
75
+ * @throws {TypeError} If `source` is not a function.
75
76
  * @since 1.0.0
76
77
  */
77
78
  declare function createEnvContext(options?: EnvContextOptions): EnvContext;
@@ -92,6 +93,10 @@ interface BindEnvOptions<M extends Mode, TValue> {
92
93
  readonly key: string;
93
94
  /**
94
95
  * Value parser used to parse the environment variable string value.
96
+ *
97
+ * In sync mode, the value parser must also be synchronous.
98
+ * In async mode, either sync or async value parsers are accepted,
99
+ * since the async pipeline can await sync results as well.
95
100
  */
96
101
  readonly parser: ValueParser<M extends "sync" ? "sync" : Mode, TValue>;
97
102
  /**
@@ -112,6 +117,12 @@ interface BindEnvOptions<M extends Mode, TValue> {
112
117
  * @param parser Parser that reads CLI values.
113
118
  * @param options Environment binding options.
114
119
  * @returns A parser with environment fallback behavior.
120
+ * @throws {TypeError} If `key` is not a string or `parser` is not a valid
121
+ * {@link ValueParser}.
122
+ * @throws {Error} If the inner parser throws while parsing or completing a
123
+ * value, if the environment source throws while reading a
124
+ * variable, or if the environment value parser throws while
125
+ * parsing the environment variable value.
115
126
  * @since 1.0.0
116
127
  */
117
128
  declare function bindEnv<M extends Mode, TValue, TState>(parser: Parser<M, TValue, TState>, options: BindEnvOptions<M, TValue>): Parser<M, TValue, TState>;
@@ -147,6 +158,7 @@ interface BoolOptions {
147
158
  *
148
159
  * @param options Parser configuration options.
149
160
  * @returns A value parser for Boolean values.
161
+ * @throws {TypeError} If `options.metavar` is an empty string.
150
162
  * @since 1.0.0
151
163
  */
152
164
  declare function bool(options?: BoolOptions): ValueParser<"sync", boolean>;
package/dist/index.d.ts CHANGED
@@ -72,6 +72,7 @@ declare function clearActiveEnvSource(contextId: symbol): void;
72
72
  *
73
73
  * @param options Environment context options.
74
74
  * @returns A context that provides environment source annotations.
75
+ * @throws {TypeError} If `source` is not a function.
75
76
  * @since 1.0.0
76
77
  */
77
78
  declare function createEnvContext(options?: EnvContextOptions): EnvContext;
@@ -92,6 +93,10 @@ interface BindEnvOptions<M extends Mode, TValue> {
92
93
  readonly key: string;
93
94
  /**
94
95
  * Value parser used to parse the environment variable string value.
96
+ *
97
+ * In sync mode, the value parser must also be synchronous.
98
+ * In async mode, either sync or async value parsers are accepted,
99
+ * since the async pipeline can await sync results as well.
95
100
  */
96
101
  readonly parser: ValueParser<M extends "sync" ? "sync" : Mode, TValue>;
97
102
  /**
@@ -112,6 +117,12 @@ interface BindEnvOptions<M extends Mode, TValue> {
112
117
  * @param parser Parser that reads CLI values.
113
118
  * @param options Environment binding options.
114
119
  * @returns A parser with environment fallback behavior.
120
+ * @throws {TypeError} If `key` is not a string or `parser` is not a valid
121
+ * {@link ValueParser}.
122
+ * @throws {Error} If the inner parser throws while parsing or completing a
123
+ * value, if the environment source throws while reading a
124
+ * variable, or if the environment value parser throws while
125
+ * parsing the environment variable value.
115
126
  * @since 1.0.0
116
127
  */
117
128
  declare function bindEnv<M extends Mode, TValue, TState>(parser: Parser<M, TValue, TState>, options: BindEnvOptions<M, TValue>): Parser<M, TValue, TState>;
@@ -147,6 +158,7 @@ interface BoolOptions {
147
158
  *
148
159
  * @param options Parser configuration options.
149
160
  * @returns A value parser for Boolean values.
161
+ * @throws {TypeError} If `options.metavar` is an empty string.
150
162
  * @since 1.0.0
151
163
  */
152
164
  declare function bool(options?: BoolOptions): ValueParser<"sync", boolean>;
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { annotationKey, getAnnotations } from "@optique/core/annotations";
2
- import { message, valueSet } from "@optique/core/message";
3
- import { ensureNonEmptyString } from "@optique/core/valueparser";
2
+ import { envVar, message, valueSet } from "@optique/core/message";
3
+ import { mapModeValue, wrapForMode } from "@optique/core/mode-dispatch";
4
+ import { ensureNonEmptyString, isValueParser } from "@optique/core/valueparser";
4
5
 
5
6
  //#region src/index.ts
6
7
  const activeEnvSourceRegistry = /* @__PURE__ */ new Map();
@@ -39,16 +40,20 @@ function defaultEnvSource(key) {
39
40
  *
40
41
  * @param options Environment context options.
41
42
  * @returns A context that provides environment source annotations.
43
+ * @throws {TypeError} If `source` is not a function.
42
44
  * @since 1.0.0
43
45
  */
44
46
  function createEnvContext(options = {}) {
45
47
  const contextId = Symbol(`@optique/env context:${Math.random()}`);
46
- const source = options.source ?? defaultEnvSource;
48
+ const rawSource = options.source;
49
+ if (rawSource !== void 0 && typeof rawSource !== "function") throw new TypeError(`Expected source to be a function, but got: ${rawSource === null ? "null" : Array.isArray(rawSource) ? "array" : typeof rawSource}.`);
50
+ const source = rawSource ?? defaultEnvSource;
47
51
  const prefix = options.prefix ?? "";
48
52
  return {
49
53
  id: contextId,
50
54
  prefix,
51
55
  source,
56
+ mode: "static",
52
57
  getAnnotations() {
53
58
  const sourceData = {
54
59
  prefix,
@@ -75,13 +80,22 @@ function createEnvContext(options = {}) {
75
80
  * @param parser Parser that reads CLI values.
76
81
  * @param options Environment binding options.
77
82
  * @returns A parser with environment fallback behavior.
83
+ * @throws {TypeError} If `key` is not a string or `parser` is not a valid
84
+ * {@link ValueParser}.
85
+ * @throws {Error} If the inner parser throws while parsing or completing a
86
+ * value, if the environment source throws while reading a
87
+ * variable, or if the environment value parser throws while
88
+ * parsing the environment variable value.
78
89
  * @since 1.0.0
79
90
  */
80
91
  function bindEnv(parser, options) {
92
+ if (typeof options.key !== "string") throw new TypeError(`Expected key to be a string, but got: ${options.key === null ? "null" : Array.isArray(options.key) ? "array" : typeof options.key}.`);
93
+ if (!isValueParser(options.parser)) throw new TypeError(`Expected parser to be a ValueParser, but got: ${options.parser === null ? "null" : Array.isArray(options.parser) ? "array" : typeof options.parser}.`);
81
94
  const envBindStateKey = Symbol("@optique/env/bindState");
82
95
  function isEnvBindState(value) {
83
96
  return value != null && typeof value === "object" && envBindStateKey in value;
84
97
  }
98
+ const deferPromptUntilConfigResolves = parser.shouldDeferCompletion;
85
99
  return {
86
100
  $mode: parser.$mode,
87
101
  $valueType: parser.$valueType,
@@ -99,25 +113,25 @@ function bindEnv(parser, options) {
99
113
  ...context,
100
114
  state: innerState
101
115
  } : context;
102
- const processResult = (result$1) => {
103
- if (result$1.success) {
104
- const cliConsumed = result$1.consumed.length > 0;
116
+ const processResult = (result) => {
117
+ if (result.success) {
118
+ const cliConsumed = result.consumed.length > 0;
105
119
  const nextState$1 = {
106
120
  [envBindStateKey]: true,
107
121
  hasCliValue: cliConsumed,
108
- cliState: result$1.next.state,
122
+ cliState: result.next.state,
109
123
  ...annotations && { [annotationKey]: annotations }
110
124
  };
111
125
  return {
112
126
  success: true,
113
127
  next: {
114
- ...result$1.next,
128
+ ...result.next,
115
129
  state: nextState$1
116
130
  },
117
- consumed: result$1.consumed
131
+ consumed: result.consumed
118
132
  };
119
133
  }
120
- if (result$1.consumed > 0) return result$1;
134
+ if (result.consumed > 0) return result;
121
135
  const nextState = {
122
136
  [envBindStateKey]: true,
123
137
  hasCliValue: false,
@@ -132,33 +146,34 @@ function bindEnv(parser, options) {
132
146
  consumed: []
133
147
  };
134
148
  };
135
- const result = parser.parse(innerContext);
136
- if (result instanceof Promise) return result.then(processResult);
137
- return processResult(result);
149
+ return mapModeValue(parser.$mode, parser.parse(innerContext), processResult);
138
150
  },
139
151
  complete: (state) => {
140
152
  if (isEnvBindState(state) && state.hasCliValue) return parser.complete(state.cliState);
141
153
  return getEnvOrDefault(state, options, parser.$mode, parser, isEnvBindState(state) ? state.cliState : void 0);
142
154
  },
143
155
  suggest: parser.suggest,
156
+ ...typeof deferPromptUntilConfigResolves === "function" ? { shouldDeferCompletion: deferPromptUntilConfigResolves.bind(parser) } : {},
144
157
  getDocFragments(state, upperDefaultValue) {
145
158
  const defaultValue = upperDefaultValue ?? options.default;
146
159
  return parser.getDocFragments(state, defaultValue);
147
160
  }
148
161
  };
149
162
  }
150
- function wrapForMode(mode, value) {
151
- if (mode === "async") return Promise.resolve(value);
152
- return value;
153
- }
154
163
  function getEnvOrDefault(state, options, mode, innerParser, innerState) {
155
164
  const annotations = getAnnotations(state);
156
165
  const sourceData = annotations?.[options.context.id] ?? getActiveEnvSource(options.context.id);
157
166
  const fullKey = `${sourceData?.prefix ?? options.context.prefix}${options.key}`;
158
167
  const rawValue = sourceData?.source(fullKey);
159
168
  if (rawValue !== void 0) {
169
+ if (typeof rawValue !== "string") {
170
+ const type = rawValue === null ? "null" : Array.isArray(rawValue) ? "array" : typeof rawValue;
171
+ return wrapForMode(mode, {
172
+ success: false,
173
+ error: message`Environment variable ${envVar(fullKey)} must be a string, but got: ${type}.`
174
+ });
175
+ }
160
176
  const parsed = options.parser.parse(rawValue);
161
- if (parsed instanceof Promise) return parsed;
162
177
  return wrapForMode(mode, parsed);
163
178
  }
164
179
  if (options.default !== void 0) return wrapForMode(mode, {
@@ -167,13 +182,11 @@ function getEnvOrDefault(state, options, mode, innerParser, innerState) {
167
182
  });
168
183
  if (innerParser != null) {
169
184
  const completeState = innerState ?? innerParser.initialState;
170
- const result = innerParser.complete(completeState);
171
- if (result instanceof Promise) return result;
172
- return wrapForMode(mode, result);
185
+ return wrapForMode(mode, innerParser.complete(completeState));
173
186
  }
174
187
  return wrapForMode(mode, {
175
188
  success: false,
176
- error: message`Missing required environment variable: ${fullKey}.`
189
+ error: message`Missing required environment variable: ${envVar(fullKey)}`
177
190
  });
178
191
  }
179
192
  const TRUE_LITERALS = [
@@ -198,6 +211,7 @@ const FALSE_LITERALS = [
198
211
  *
199
212
  * @param options Parser configuration options.
200
213
  * @returns A value parser for Boolean values.
214
+ * @throws {TypeError} If `options.metavar` is an empty string.
201
215
  * @since 1.0.0
202
216
  */
203
217
  function bool(options = {}) {
@@ -224,6 +238,14 @@ function bool(options = {}) {
224
238
  },
225
239
  format(value) {
226
240
  return value ? "true" : "false";
241
+ },
242
+ suggest(prefix) {
243
+ const allLiterals = [...TRUE_LITERALS, ...FALSE_LITERALS];
244
+ const normalizedPrefix = prefix.toLowerCase();
245
+ return allLiterals.filter((lit) => lit.startsWith(normalizedPrefix)).map((lit) => ({
246
+ kind: "literal",
247
+ text: lit
248
+ }));
227
249
  }
228
250
  };
229
251
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/env",
3
- "version": "1.0.0-dev.0",
3
+ "version": "1.0.0-dev.1116+6084dd3a",
4
4
  "description": "Environment variable support for Optique",
5
5
  "keywords": [
6
6
  "CLI",
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "sideEffects": false,
56
56
  "dependencies": {
57
- "@optique/core": "1.0.0"
57
+ "@optique/core": "1.0.0-dev.1116+6084dd3a"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^20.19.9",