@e-n-v/env 0.0.1
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/README.md +72 -0
- package/assets/demo.gif +0 -0
- package/assets/env-logo.png +0 -0
- package/dist/bundle.cjs +1290 -0
- package/dist/bundle.d.cts +1057 -0
- package/dist/bundle.d.ts +1055 -0
- package/dist/bundle.js +1244 -0
- package/dist/chunk-Bp6m_JJh.js +13 -0
- package/dist/cli.cjs +141 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +136 -0
- package/dist/src-RlCvrI0s.js +3860 -0
- package/dist/src-oetJm-nZ.cjs +4098 -0
- package/package.json +31 -0
|
@@ -0,0 +1,3860 @@
|
|
|
1
|
+
import { t as __export } from "./chunk-Bp6m_JJh.js";
|
|
2
|
+
import { mkdirSync, promises, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, relative, resolve } from "node:path";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { existsSync, writeFileSync as writeFileSync$1 } from "fs";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import * as z4core from "zod/v4/core";
|
|
8
|
+
import { stdin, stdout } from "node:process";
|
|
9
|
+
import { Prompt, isCancel } from "@clack/core";
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
|
+
import color from "picocolors";
|
|
12
|
+
import { clearLine, cursorTo, moveCursor } from "node:readline";
|
|
13
|
+
|
|
14
|
+
//#region ../files/src/utils.ts
|
|
15
|
+
function normalizeContent(content) {
|
|
16
|
+
return content.replace(/\r\n/g, "\n");
|
|
17
|
+
}
|
|
18
|
+
function splitLines(content) {
|
|
19
|
+
return content === "" ? [] : content.split("\n");
|
|
20
|
+
}
|
|
21
|
+
function validateKey(key) {
|
|
22
|
+
const trimmed = key.trim();
|
|
23
|
+
if (!trimmed) throw new TypeError("Environment variable name cannot be empty");
|
|
24
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(trimmed)) throw new TypeError(`Invalid environment variable name: ${trimmed}`);
|
|
25
|
+
return trimmed;
|
|
26
|
+
}
|
|
27
|
+
function parseAssignmentEndingAt(lines, endIndex) {
|
|
28
|
+
const lastLine = lines[endIndex];
|
|
29
|
+
if (typeof lastLine !== "string" || isSkippableLine(lastLine)) return null;
|
|
30
|
+
const block = [];
|
|
31
|
+
let start = endIndex;
|
|
32
|
+
while (start >= 0) {
|
|
33
|
+
const currentLine = lines[start];
|
|
34
|
+
if (typeof currentLine !== "string") break;
|
|
35
|
+
block.unshift(currentLine);
|
|
36
|
+
if (containsAssignmentOperator(currentLine)) {
|
|
37
|
+
const parsed = tryParseAssignmentBlock(block);
|
|
38
|
+
if (!parsed) return null;
|
|
39
|
+
return {
|
|
40
|
+
...parsed,
|
|
41
|
+
startIndex: start,
|
|
42
|
+
endIndex
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
start -= 1;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function isSkippableLine(line) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
return trimmed.length === 0 || trimmed.startsWith("#");
|
|
52
|
+
}
|
|
53
|
+
function containsAssignmentOperator(line) {
|
|
54
|
+
const trimmed = line.trimStart();
|
|
55
|
+
if (trimmed.length === 0 || trimmed.startsWith("#")) return false;
|
|
56
|
+
const commentIndex = trimmed.indexOf("#");
|
|
57
|
+
return (commentIndex === -1 ? trimmed : trimmed.slice(0, commentIndex)).includes("=");
|
|
58
|
+
}
|
|
59
|
+
function tryParseAssignmentBlock(blockLines) {
|
|
60
|
+
if (blockLines.length === 0) return null;
|
|
61
|
+
const firstLine = blockLines[0];
|
|
62
|
+
const leadingMatch = firstLine.match(/^\s*/);
|
|
63
|
+
const leading = leadingMatch ? leadingMatch[0] : "";
|
|
64
|
+
let remainder = firstLine.slice(leading.length);
|
|
65
|
+
if (remainder.length === 0 || remainder.startsWith("#")) return null;
|
|
66
|
+
let exportPrefix = "";
|
|
67
|
+
if (remainder.startsWith("export")) {
|
|
68
|
+
const afterExport = remainder.slice(6);
|
|
69
|
+
if (/^\s/.test(afterExport)) {
|
|
70
|
+
const spacingMatch = afterExport.match(/^\s+/);
|
|
71
|
+
const spacing = spacingMatch ? spacingMatch[0] : " ";
|
|
72
|
+
exportPrefix = `export${spacing}`;
|
|
73
|
+
remainder = afterExport.slice(spacing.length);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const equalsIndex = remainder.indexOf("=");
|
|
77
|
+
if (equalsIndex === -1) return null;
|
|
78
|
+
const key = validateKey(remainder.slice(0, equalsIndex).trim());
|
|
79
|
+
const parsedValue = parseValueSource([remainder.slice(equalsIndex + 1).replace(/^\s+/, ""), ...blockLines.slice(1)].join("\n"));
|
|
80
|
+
if (!parsedValue) return null;
|
|
81
|
+
const trailingTrimmed = parsedValue.trailing.trim();
|
|
82
|
+
if (trailingTrimmed.length > 0 && !trailingTrimmed.startsWith("#")) return null;
|
|
83
|
+
return {
|
|
84
|
+
key,
|
|
85
|
+
value: parsedValue.value,
|
|
86
|
+
leading,
|
|
87
|
+
exportPrefix,
|
|
88
|
+
trailing: parsedValue.trailing,
|
|
89
|
+
lines: [...blockLines]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function parseValueSource(source) {
|
|
93
|
+
if (source.length === 0) return {
|
|
94
|
+
value: "",
|
|
95
|
+
trailing: ""
|
|
96
|
+
};
|
|
97
|
+
const firstChar = source[0];
|
|
98
|
+
if (firstChar === "\"") return parseDoubleQuotedValue(source);
|
|
99
|
+
if (firstChar === "'") return parseSingleQuotedValue(source);
|
|
100
|
+
if (source.includes("\n")) return null;
|
|
101
|
+
const commentIndex = source.indexOf(" #");
|
|
102
|
+
const raw = commentIndex === -1 ? source : source.slice(0, commentIndex);
|
|
103
|
+
const trailing = commentIndex === -1 ? source.slice(raw.length) : source.slice(commentIndex);
|
|
104
|
+
return {
|
|
105
|
+
value: raw.trim(),
|
|
106
|
+
trailing
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function parseDoubleQuotedValue(source) {
|
|
110
|
+
let value = "";
|
|
111
|
+
let escaping = false;
|
|
112
|
+
for (let i = 1; i < source.length; i += 1) {
|
|
113
|
+
const char = source[i] ?? "";
|
|
114
|
+
if (escaping) {
|
|
115
|
+
switch (char) {
|
|
116
|
+
case "n":
|
|
117
|
+
value += "\n";
|
|
118
|
+
break;
|
|
119
|
+
case "r":
|
|
120
|
+
value += "\r";
|
|
121
|
+
break;
|
|
122
|
+
case "t":
|
|
123
|
+
value += " ";
|
|
124
|
+
break;
|
|
125
|
+
case "\"":
|
|
126
|
+
value += "\"";
|
|
127
|
+
break;
|
|
128
|
+
case "\\":
|
|
129
|
+
value += "\\";
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
value += char;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
escaping = false;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (char === "\\") {
|
|
139
|
+
escaping = true;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (char === "\"") {
|
|
143
|
+
const trailing = source.slice(i + 1);
|
|
144
|
+
return {
|
|
145
|
+
value,
|
|
146
|
+
trailing
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (char === "\n") {
|
|
150
|
+
value += "\n";
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
value += char;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
function parseSingleQuotedValue(source) {
|
|
158
|
+
let value = "";
|
|
159
|
+
for (let i = 1; i < source.length; i += 1) {
|
|
160
|
+
const char = source[i] ?? "";
|
|
161
|
+
if (char === "'") {
|
|
162
|
+
const trailing = source.slice(i + 1);
|
|
163
|
+
return {
|
|
164
|
+
value,
|
|
165
|
+
trailing
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (char === "\n") {
|
|
169
|
+
value += "\n";
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
value += char;
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region ../files/src/get.ts
|
|
179
|
+
function get(content, arg) {
|
|
180
|
+
const lines = splitLines(normalizeContent(content));
|
|
181
|
+
if (typeof arg === "undefined") {
|
|
182
|
+
const map = scanAll(lines);
|
|
183
|
+
return Object.fromEntries(map);
|
|
184
|
+
}
|
|
185
|
+
if (typeof arg === "string") return scanForSingle(lines, arg);
|
|
186
|
+
const normalizedTargets = Array.from(new Set(arg.map((name) => validateKey(name))));
|
|
187
|
+
const lookup = scanForMany(lines, new Set(normalizedTargets));
|
|
188
|
+
const ordered = normalizedTargets.map((key) => [key, lookup.get(key)]);
|
|
189
|
+
return Object.fromEntries(ordered);
|
|
190
|
+
}
|
|
191
|
+
function scanAll(lines) {
|
|
192
|
+
const result = /* @__PURE__ */ new Map();
|
|
193
|
+
iterateAssignments(lines, (assignment) => {
|
|
194
|
+
if (!result.has(assignment.key)) result.set(assignment.key, assignment.value);
|
|
195
|
+
});
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
function scanForSingle(lines, target) {
|
|
199
|
+
const name = validateKey(target);
|
|
200
|
+
let found;
|
|
201
|
+
iterateAssignments(lines, (assignment) => {
|
|
202
|
+
if (assignment.key === name) {
|
|
203
|
+
found = assignment.value;
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return found;
|
|
208
|
+
}
|
|
209
|
+
function scanForMany(lines, targets) {
|
|
210
|
+
const normalizedTargets = /* @__PURE__ */ new Set();
|
|
211
|
+
for (const target of targets) normalizedTargets.add(validateKey(target));
|
|
212
|
+
const found = /* @__PURE__ */ new Map();
|
|
213
|
+
const remaining = new Set(normalizedTargets);
|
|
214
|
+
iterateAssignments(lines, (assignment) => {
|
|
215
|
+
if (!remaining.has(assignment.key) || found.has(assignment.key)) return;
|
|
216
|
+
found.set(assignment.key, assignment.value);
|
|
217
|
+
remaining.delete(assignment.key);
|
|
218
|
+
if (remaining.size === 0) return true;
|
|
219
|
+
});
|
|
220
|
+
return found;
|
|
221
|
+
}
|
|
222
|
+
function iterateAssignments(lines, callback) {
|
|
223
|
+
for (let i = lines.length - 1; i >= 0;) {
|
|
224
|
+
const parsed = parseAssignmentEndingAt(lines, i);
|
|
225
|
+
if (!parsed) {
|
|
226
|
+
i -= 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (callback(parsed) === true) return;
|
|
230
|
+
i = parsed.startIndex - 1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region ../files/src/set.ts
|
|
236
|
+
function set(content, arg, value) {
|
|
237
|
+
const normalized = normalizeContent(content);
|
|
238
|
+
const lines = splitLines(normalized);
|
|
239
|
+
const entries = normalizeWriteArguments(arg, value);
|
|
240
|
+
if (entries.length === 0) return ensureTrailingNewline(normalized);
|
|
241
|
+
const replacements = new Map(entries);
|
|
242
|
+
const touched = /* @__PURE__ */ new Set();
|
|
243
|
+
for (let i = lines.length - 1; i >= 0;) {
|
|
244
|
+
if (replacements.size === 0) break;
|
|
245
|
+
const parsed = parseAssignmentEndingAt(lines, i);
|
|
246
|
+
if (!parsed) {
|
|
247
|
+
i -= 1;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const { key, startIndex, endIndex, leading, exportPrefix, trailing, value: currentValue } = parsed;
|
|
251
|
+
const nextValue = replacements.get(key);
|
|
252
|
+
if (typeof nextValue === "undefined" || touched.has(key)) {
|
|
253
|
+
i = startIndex - 1;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (nextValue === currentValue) {
|
|
257
|
+
touched.add(key);
|
|
258
|
+
replacements.delete(key);
|
|
259
|
+
i = startIndex - 1;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const block = buildAssignmentLines(key, nextValue, {
|
|
263
|
+
leading,
|
|
264
|
+
exportPrefix,
|
|
265
|
+
trailing
|
|
266
|
+
});
|
|
267
|
+
lines.splice(startIndex, endIndex - startIndex + 1, ...block);
|
|
268
|
+
touched.add(key);
|
|
269
|
+
replacements.delete(key);
|
|
270
|
+
i = startIndex - 1;
|
|
271
|
+
}
|
|
272
|
+
for (const [key, val] of entries) {
|
|
273
|
+
if (touched.has(key)) continue;
|
|
274
|
+
const block = buildAssignmentLines(key, val);
|
|
275
|
+
const insertIndex = lines.length > 0 && lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
276
|
+
lines.splice(insertIndex, 0, ...block);
|
|
277
|
+
touched.add(key);
|
|
278
|
+
}
|
|
279
|
+
return ensureTrailingNewline(lines.join("\n"));
|
|
280
|
+
}
|
|
281
|
+
function ensureTrailingNewline(content) {
|
|
282
|
+
if (content.length === 0) return "";
|
|
283
|
+
if (!content.endsWith("\n")) return `${content}\n`;
|
|
284
|
+
return content;
|
|
285
|
+
}
|
|
286
|
+
function isObjectRecord(value) {
|
|
287
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
288
|
+
}
|
|
289
|
+
function normalizeWriteArguments(arg, value) {
|
|
290
|
+
if (typeof arg === "string") {
|
|
291
|
+
if (typeof value === "undefined") throw new TypeError(`Value for variable "${arg}" must be provided`);
|
|
292
|
+
return [[validateKey(arg), formatValueInput(value)]];
|
|
293
|
+
}
|
|
294
|
+
if (!isObjectRecord(arg)) throw new TypeError("Argument must be a string name or an object of key-value pairs");
|
|
295
|
+
const entries = [];
|
|
296
|
+
for (const [key, rawValue] of Object.entries(arg)) {
|
|
297
|
+
if (typeof rawValue === "undefined") throw new TypeError(`Value for variable "${key}" must be provided`);
|
|
298
|
+
entries.push([validateKey(key), formatValueInput(rawValue)]);
|
|
299
|
+
}
|
|
300
|
+
return entries;
|
|
301
|
+
}
|
|
302
|
+
function formatValueInput(value) {
|
|
303
|
+
if (typeof value === "bigint") return value.toString();
|
|
304
|
+
return String(value);
|
|
305
|
+
}
|
|
306
|
+
function serializeValue(value) {
|
|
307
|
+
const normalized = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
308
|
+
if (!(normalized.length === 0 || /[\s"'\\#]/.test(normalized) || normalized.includes("\n"))) return normalized;
|
|
309
|
+
let result = "\"";
|
|
310
|
+
for (const char of normalized) {
|
|
311
|
+
if (char === "\"") {
|
|
312
|
+
result += "\\\"";
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (char === "\\") {
|
|
316
|
+
result += "\\\\";
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (char === "\n") {
|
|
320
|
+
result += "\n";
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
result += char;
|
|
324
|
+
}
|
|
325
|
+
result += "\"";
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
328
|
+
function buildAssignmentLines(key, value, style = {}) {
|
|
329
|
+
const leading = style.leading ?? "";
|
|
330
|
+
const exportPrefix = style.exportPrefix ?? "";
|
|
331
|
+
const trailing = style.trailing ?? "";
|
|
332
|
+
const segments = serializeValue(value).split("\n");
|
|
333
|
+
return segments.map((segment, index) => {
|
|
334
|
+
if (index === 0) return `${leading}${exportPrefix}${key}=${segment}${segments.length === 1 ? trailing : ""}`;
|
|
335
|
+
if (index === segments.length - 1 && trailing && segments.length > 1) return `${segment}${trailing}`;
|
|
336
|
+
return segment;
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region ../files/src/EnvSource.ts
|
|
342
|
+
var EnvSource = class {
|
|
343
|
+
filePath;
|
|
344
|
+
constructor(filePath) {
|
|
345
|
+
this.filePath = resolve(process.cwd(), filePath);
|
|
346
|
+
}
|
|
347
|
+
readSync(arg) {
|
|
348
|
+
const content = this.readContentSync();
|
|
349
|
+
if (typeof arg === "undefined") return get(content);
|
|
350
|
+
if (typeof arg === "string") return get(content, arg);
|
|
351
|
+
return get(content, arg);
|
|
352
|
+
}
|
|
353
|
+
async read(arg) {
|
|
354
|
+
const content = await this.readContent();
|
|
355
|
+
if (typeof arg === "undefined") return get(content);
|
|
356
|
+
if (typeof arg === "string") return get(content, arg);
|
|
357
|
+
return get(content, arg);
|
|
358
|
+
}
|
|
359
|
+
writeSync(arg, value) {
|
|
360
|
+
const originalContent = this.readContentSync();
|
|
361
|
+
let nextContent;
|
|
362
|
+
if (typeof arg === "string") nextContent = set(originalContent, arg, value);
|
|
363
|
+
else nextContent = set(originalContent, arg);
|
|
364
|
+
if (nextContent === originalContent) return;
|
|
365
|
+
this.ensureDirectorySync();
|
|
366
|
+
writeFileSync(this.filePath, nextContent, "utf8");
|
|
367
|
+
}
|
|
368
|
+
async write(arg, value) {
|
|
369
|
+
const originalContent = await this.readContent();
|
|
370
|
+
let nextContent;
|
|
371
|
+
if (typeof arg === "string") nextContent = set(originalContent, arg, value);
|
|
372
|
+
else nextContent = set(originalContent, arg);
|
|
373
|
+
if (nextContent === originalContent) return;
|
|
374
|
+
await this.ensureDirectory();
|
|
375
|
+
await promises.writeFile(this.filePath, nextContent, "utf8");
|
|
376
|
+
}
|
|
377
|
+
readContentSync() {
|
|
378
|
+
try {
|
|
379
|
+
return readFileSync(this.filePath, "utf8").replace(/\r\n/g, "\n");
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (error.code === "ENOENT") return "";
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async readContent() {
|
|
386
|
+
try {
|
|
387
|
+
return (await promises.readFile(this.filePath, "utf8")).replace(/\r\n/g, "\n");
|
|
388
|
+
} catch (error) {
|
|
389
|
+
if (error.code === "ENOENT") return "";
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
ensureDirectorySync() {
|
|
394
|
+
try {
|
|
395
|
+
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
396
|
+
} catch (error) {
|
|
397
|
+
if (error.code !== "EEXIST") throw error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async ensureDirectory() {
|
|
401
|
+
try {
|
|
402
|
+
await promises.mkdir(dirname(this.filePath), { recursive: true });
|
|
403
|
+
} catch (error) {
|
|
404
|
+
if (error.code !== "EEXIST") throw error;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region ../channels/src/channels/default/DefaultEnvChannel.ts
|
|
411
|
+
/**
|
|
412
|
+
* Default implementation of EnvChannel that works with .env files using envrw
|
|
413
|
+
*/
|
|
414
|
+
var DefaultEnvChannel = class {
|
|
415
|
+
envSource;
|
|
416
|
+
/**
|
|
417
|
+
* Create a new DefaultEnvChannel
|
|
418
|
+
* @param filePath - Path to the .env file to manage
|
|
419
|
+
*/
|
|
420
|
+
constructor(filePath) {
|
|
421
|
+
this.envSource = new EnvSource(filePath);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Get all environment variables
|
|
425
|
+
* @returns Promise that resolves to object containing all environment variable key-value pairs
|
|
426
|
+
*/
|
|
427
|
+
async get() {
|
|
428
|
+
return await this.envSource.read();
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Set multiple environment variables
|
|
432
|
+
* @param values - Object containing key-value pairs to set
|
|
433
|
+
* @returns Promise that resolves when the values have been set
|
|
434
|
+
*/
|
|
435
|
+
async set(values) {
|
|
436
|
+
await this.envSource.write(values);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
//#endregion
|
|
441
|
+
//#region ../channels/src/channels/dotenv/DotEnvChannel.ts
|
|
442
|
+
function isFileNotFound(error) {
|
|
443
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
444
|
+
}
|
|
445
|
+
function normalizeParsed(parsed) {
|
|
446
|
+
if (!parsed) return {};
|
|
447
|
+
const result = {};
|
|
448
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
449
|
+
if (typeof value === "undefined") continue;
|
|
450
|
+
result[key] = value;
|
|
451
|
+
}
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
var DotEnvChannel = class {
|
|
455
|
+
dotenv;
|
|
456
|
+
targetPath;
|
|
457
|
+
absolutePath;
|
|
458
|
+
encoding;
|
|
459
|
+
parseOptions;
|
|
460
|
+
envSource;
|
|
461
|
+
constructor(dotenv, defaultPath = ".env", getOptions = {}, parseOptions = {}) {
|
|
462
|
+
this.dotenv = dotenv;
|
|
463
|
+
this.targetPath = (typeof getOptions.path === "string" ? getOptions.path : void 0) ?? defaultPath;
|
|
464
|
+
this.absolutePath = resolve(process.cwd(), this.targetPath);
|
|
465
|
+
this.encoding = getOptions.encoding ?? "utf8";
|
|
466
|
+
this.parseOptions = { ...parseOptions };
|
|
467
|
+
this.envSource = new EnvSource(this.targetPath);
|
|
468
|
+
}
|
|
469
|
+
async get() {
|
|
470
|
+
try {
|
|
471
|
+
const content = await readFile(this.absolutePath, { encoding: this.encoding });
|
|
472
|
+
return normalizeParsed(this.dotenv.parse(content, this.parseOptions));
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (isFileNotFound(error)) return {};
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async set(values) {
|
|
479
|
+
await this.envSource.write(values);
|
|
480
|
+
const parsed = await this.get();
|
|
481
|
+
const mismatches = [];
|
|
482
|
+
for (const [key, expected] of Object.entries(values)) {
|
|
483
|
+
const actual = parsed[key];
|
|
484
|
+
if (actual !== expected) mismatches.push(`${key}=expected(${JSON.stringify(expected)}) actual(${JSON.stringify(actual)})`);
|
|
485
|
+
}
|
|
486
|
+
if (mismatches.length > 0) throw new Error(`Failed to correctly write values to ${this.targetPath}: ${mismatches.join(", ")}`);
|
|
487
|
+
}
|
|
488
|
+
getPrimaryPath() {
|
|
489
|
+
return this.targetPath;
|
|
490
|
+
}
|
|
491
|
+
clearCache() {}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region ../channels/src/channels/dotenvx/DotEnvXChannel.ts
|
|
496
|
+
/**
|
|
497
|
+
* Simple DotEnvX implementation of EnvChannel that wraps @dotenvx/dotenvx library
|
|
498
|
+
* This is just an API mapping wrapper without caching or complex state management
|
|
499
|
+
*/
|
|
500
|
+
var DotEnvXChannel = class {
|
|
501
|
+
dotenvx;
|
|
502
|
+
getOptions;
|
|
503
|
+
setOptions;
|
|
504
|
+
defaultPath;
|
|
505
|
+
/**
|
|
506
|
+
* Create a new DotEnvXChannel
|
|
507
|
+
* @param dotenvx - The dotenvx instance to use
|
|
508
|
+
* @param defaultPath - Default path for env files
|
|
509
|
+
* @param getOptions - Options for get operations
|
|
510
|
+
* @param setOptions - Options for set operations
|
|
511
|
+
*/
|
|
512
|
+
constructor(dotenvx, defaultPath = ".env", getOptions = {}, setOptions = {}) {
|
|
513
|
+
this.dotenvx = dotenvx;
|
|
514
|
+
this.defaultPath = defaultPath;
|
|
515
|
+
this.getOptions = getOptions;
|
|
516
|
+
this.setOptions = setOptions;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Get all environment variables
|
|
520
|
+
* @returns Promise that resolves to object containing all environment variable key-value pairs
|
|
521
|
+
*/
|
|
522
|
+
async get() {
|
|
523
|
+
try {
|
|
524
|
+
return this.dotenvx.config({
|
|
525
|
+
processEnv: {},
|
|
526
|
+
quiet: true,
|
|
527
|
+
ignore: ["MISSING_ENV_FILE"],
|
|
528
|
+
path: this.defaultPath,
|
|
529
|
+
...this.getOptions
|
|
530
|
+
}).parsed || {};
|
|
531
|
+
} catch {
|
|
532
|
+
return {};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Set multiple environment variables
|
|
537
|
+
* @param values - Object containing key-value pairs to set
|
|
538
|
+
* @returns Promise that resolves when the values have been set
|
|
539
|
+
*/
|
|
540
|
+
async set(values) {
|
|
541
|
+
if (!existsSync(this.defaultPath)) writeFileSync$1(this.defaultPath, "", "utf8");
|
|
542
|
+
try {
|
|
543
|
+
const options = {
|
|
544
|
+
path: this.defaultPath,
|
|
545
|
+
...this.setOptions
|
|
546
|
+
};
|
|
547
|
+
for (const [key, value] of Object.entries(values)) this.dotenvx.set(key, value, options);
|
|
548
|
+
} catch (error) {
|
|
549
|
+
throw new Error(`Failed to set environment variables: ${error}`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Get the primary path being used for env files
|
|
554
|
+
* @returns The primary path
|
|
555
|
+
*/
|
|
556
|
+
getPrimaryPath() {
|
|
557
|
+
return this.defaultPath;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Clear any cached data (no-op for this implementation since we don't cache)
|
|
561
|
+
*/
|
|
562
|
+
clearCache() {}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
//#endregion
|
|
566
|
+
//#region ../channels/src/channels/processenv/ProcessEnvChannel.ts
|
|
567
|
+
/**
|
|
568
|
+
* EnvChannel implementation that works with process.env
|
|
569
|
+
* This channel reads from and writes to the current process environment
|
|
570
|
+
*/
|
|
571
|
+
var ProcessEnvChannel = class {
|
|
572
|
+
/**
|
|
573
|
+
* Get all environment variables from process.env
|
|
574
|
+
* @returns Promise that resolves to object containing all environment variable key-value pairs
|
|
575
|
+
*/
|
|
576
|
+
async get() {
|
|
577
|
+
const env = {};
|
|
578
|
+
for (const [key, value] of Object.entries(process.env)) if (value !== void 0) env[key] = value;
|
|
579
|
+
return env;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Set multiple environment variables in process.env
|
|
583
|
+
* @param values - Object containing key-value pairs to set
|
|
584
|
+
* @returns Promise that resolves when the values have been set
|
|
585
|
+
*/
|
|
586
|
+
async set(values) {
|
|
587
|
+
for (const [key, value] of Object.entries(values)) process.env[key] = value;
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region ../channels/src/resolveChannel.ts
|
|
593
|
+
/**
|
|
594
|
+
* Resolve channel options into an EnvChannel instance
|
|
595
|
+
* @param options - The channel options to resolve
|
|
596
|
+
* @param defaultPath - Default path to use for env files (default: ".env")
|
|
597
|
+
* @returns An EnvChannel instance
|
|
598
|
+
*/
|
|
599
|
+
function resolveChannel(options, defaultPath = ".env") {
|
|
600
|
+
if (options === void 0 || options === "default") return new DefaultEnvChannel(defaultPath);
|
|
601
|
+
if (options === "processenv") return new ProcessEnvChannel();
|
|
602
|
+
if (typeof options === "object" && options !== null) {
|
|
603
|
+
if ("dotenv" in options) {
|
|
604
|
+
const { dotenv, get: getOptions = {}, parse: parseOptions = {}, path } = options;
|
|
605
|
+
if (!dotenv) throw new Error("Dotenv instance is required for dotenv channel");
|
|
606
|
+
const resolvedPath = path ?? defaultPath;
|
|
607
|
+
const mergedGetOptions = { ...getOptions };
|
|
608
|
+
if (typeof mergedGetOptions.path !== "string") mergedGetOptions.path = resolvedPath;
|
|
609
|
+
return new DotEnvChannel(dotenv, resolvedPath, mergedGetOptions, parseOptions ?? {});
|
|
610
|
+
}
|
|
611
|
+
if ("dotenvx" in options) {
|
|
612
|
+
const { dotenvx, get: getOptions = {}, set: setOptions = {} } = options;
|
|
613
|
+
if (!dotenvx) throw new Error("DotEnvX instance is required for dotenvx channel");
|
|
614
|
+
return new DotEnvXChannel(dotenvx, defaultPath, getOptions, setOptions);
|
|
615
|
+
}
|
|
616
|
+
if ("process" in options) {
|
|
617
|
+
if (options.process && typeof options.process === "object" && "env" in options.process) return new ProcessEnvChannel();
|
|
618
|
+
throw new Error("Invalid process object provided to channel config");
|
|
619
|
+
}
|
|
620
|
+
if ("name" in options) if (options.name === "default") return new DefaultEnvChannel(defaultPath);
|
|
621
|
+
else if (options.name === "processenv") return new ProcessEnvChannel();
|
|
622
|
+
else throw new Error(`Unknown channel name: ${options.name}`);
|
|
623
|
+
}
|
|
624
|
+
throw new Error(`Invalid channel options: ${options}`);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
//#endregion
|
|
628
|
+
//#region ../core/src/schemas/EnvVarSchemaBase.ts
|
|
629
|
+
var EnvVarSchemaBase = class {
|
|
630
|
+
required;
|
|
631
|
+
default;
|
|
632
|
+
description;
|
|
633
|
+
process;
|
|
634
|
+
constructor(input) {
|
|
635
|
+
this.required = input?.required ?? true;
|
|
636
|
+
this.default = input?.default;
|
|
637
|
+
this.description = input?.description;
|
|
638
|
+
this.process = input.process;
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region ../core/src/processing/processors.ts
|
|
644
|
+
/**
|
|
645
|
+
* Default processors for environment variable types.
|
|
646
|
+
* Each processor factory returns a function that takes a value and returns T | undefined.
|
|
647
|
+
* Returns undefined for empty/null values, throws descriptive errors for invalid values.
|
|
648
|
+
*/
|
|
649
|
+
/**
|
|
650
|
+
* Create a string processor - pass through as-is
|
|
651
|
+
*/
|
|
652
|
+
const stringProcessor = () => (value) => {
|
|
653
|
+
if (typeof value !== "string") throw new Error("Value must be a string");
|
|
654
|
+
return value === "" ? void 0 : value;
|
|
655
|
+
};
|
|
656
|
+
/**
|
|
657
|
+
* Create a number processor using parseFloat
|
|
658
|
+
*/
|
|
659
|
+
const numberProcessor = () => (value) => {
|
|
660
|
+
if (typeof value !== "string") throw new Error("Value must be a string");
|
|
661
|
+
const trimmed = value.trim();
|
|
662
|
+
if (trimmed === "") return;
|
|
663
|
+
const parsed = parseFloat(trimmed);
|
|
664
|
+
if (isNaN(parsed)) throw new Error(`"${value}" is not a valid number`);
|
|
665
|
+
return parsed;
|
|
666
|
+
};
|
|
667
|
+
/**
|
|
668
|
+
* Create a boolean processor from string representation
|
|
669
|
+
*/
|
|
670
|
+
const booleanProcessor = () => (value) => {
|
|
671
|
+
if (typeof value !== "string") throw new Error("Value must be a string");
|
|
672
|
+
const trimmed = value.trim().toLowerCase();
|
|
673
|
+
if (trimmed === "") return;
|
|
674
|
+
if (trimmed === "true" || trimmed === "1" || trimmed === "yes" || trimmed === "on") return true;
|
|
675
|
+
if (trimmed === "false" || trimmed === "0" || trimmed === "no" || trimmed === "off") return false;
|
|
676
|
+
throw new Error(`"${value}" is not a valid boolean. Use: true/false, 1/0, yes/no, or on/off`);
|
|
677
|
+
};
|
|
678
|
+
/**
|
|
679
|
+
* Create an enum processor that validates values against allowed options
|
|
680
|
+
*/
|
|
681
|
+
const enumProcessor = (allowedValues) => (value) => {
|
|
682
|
+
if (typeof value !== "string") throw new Error("Value must be a string");
|
|
683
|
+
const trimmed = value.trim();
|
|
684
|
+
if (trimmed === "") return;
|
|
685
|
+
if (!allowedValues.includes(trimmed)) throw new Error(`"${value}" is not a valid option. Must be one of: ${allowedValues.join(", ")}`);
|
|
686
|
+
return trimmed;
|
|
687
|
+
};
|
|
688
|
+
/**
|
|
689
|
+
* Default processors map for each environment variable type
|
|
690
|
+
*/
|
|
691
|
+
const processors = {
|
|
692
|
+
string: stringProcessor,
|
|
693
|
+
number: numberProcessor,
|
|
694
|
+
boolean: booleanProcessor,
|
|
695
|
+
enum: enumProcessor
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region ../core/src/schemas/typed/BooleanEnvVarSchema.ts
|
|
700
|
+
var BooleanEnvVarSchema = class extends EnvVarSchemaBase {
|
|
701
|
+
type = "boolean";
|
|
702
|
+
constructor(input = {}) {
|
|
703
|
+
super({
|
|
704
|
+
...input,
|
|
705
|
+
process: input.process ?? processors.boolean()
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
//#endregion
|
|
711
|
+
//#region ../core/src/schemas/typed/EnumEnvVarSchema.ts
|
|
712
|
+
var EnumEnvVarSchema = class extends EnvVarSchemaBase {
|
|
713
|
+
type = "enum";
|
|
714
|
+
values;
|
|
715
|
+
constructor(input) {
|
|
716
|
+
super({
|
|
717
|
+
...input,
|
|
718
|
+
process: input.process ?? processors.enum(input.values)
|
|
719
|
+
});
|
|
720
|
+
this.values = input.values;
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
//#endregion
|
|
725
|
+
//#region ../core/src/schemas/typed/NumberEnvVarSchema.ts
|
|
726
|
+
var NumberEnvVarSchema = class extends EnvVarSchemaBase {
|
|
727
|
+
type = "number";
|
|
728
|
+
constructor(input = {}) {
|
|
729
|
+
super({
|
|
730
|
+
...input,
|
|
731
|
+
process: input.process ?? processors.number()
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
//#endregion
|
|
737
|
+
//#region ../core/src/schemas/typed/StringEnvVarSchema.ts
|
|
738
|
+
var StringEnvVarSchema = class extends EnvVarSchemaBase {
|
|
739
|
+
type = "string";
|
|
740
|
+
secret;
|
|
741
|
+
constructor(input = {}) {
|
|
742
|
+
super({
|
|
743
|
+
...input,
|
|
744
|
+
process: input.process ?? processors.string()
|
|
745
|
+
});
|
|
746
|
+
this.secret = input.secret;
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
//#endregion
|
|
751
|
+
//#region ../core/src/schemas/EnvVarSchema.ts
|
|
752
|
+
/**
|
|
753
|
+
* Type guard to check if a value is a EnvVarSchema instance
|
|
754
|
+
*/
|
|
755
|
+
function isEnvVarSchema(value) {
|
|
756
|
+
return value instanceof StringEnvVarSchema || value instanceof NumberEnvVarSchema || value instanceof BooleanEnvVarSchema || value instanceof EnumEnvVarSchema;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
//#endregion
|
|
760
|
+
//#region ../core/src/processing/preprocessors.ts
|
|
761
|
+
const defaultBooleanPreprocessorOptions = {
|
|
762
|
+
true: [
|
|
763
|
+
"on",
|
|
764
|
+
"enabled",
|
|
765
|
+
"active",
|
|
766
|
+
"yes"
|
|
767
|
+
],
|
|
768
|
+
false: [
|
|
769
|
+
"off",
|
|
770
|
+
"disabled",
|
|
771
|
+
"inactive",
|
|
772
|
+
"no"
|
|
773
|
+
]
|
|
774
|
+
};
|
|
775
|
+
/**
|
|
776
|
+
* Create a string preprocessor - pass through unchanged
|
|
777
|
+
*/
|
|
778
|
+
const stringPreprocessor = () => (value) => value;
|
|
779
|
+
/**
|
|
780
|
+
* Create a number preprocessor that strips commas and whitespace
|
|
781
|
+
*/
|
|
782
|
+
const numberPreprocessor = () => (value) => {
|
|
783
|
+
try {
|
|
784
|
+
const cleaned = value.replace(/,/g, "").trim();
|
|
785
|
+
if (/^-?\d*\.?\d+$/.test(cleaned)) return cleaned;
|
|
786
|
+
} catch {}
|
|
787
|
+
return value;
|
|
788
|
+
};
|
|
789
|
+
/**
|
|
790
|
+
* Create a boolean preprocessor that normalizes common truthy/falsy phrases
|
|
791
|
+
*/
|
|
792
|
+
const booleanPreprocessor = (options = defaultBooleanPreprocessorOptions) => {
|
|
793
|
+
const normalize = (candidate) => candidate.toLowerCase().trim();
|
|
794
|
+
const trueValues = new Set(options.true.map(normalize));
|
|
795
|
+
const falseValues = new Set(options.false.map(normalize));
|
|
796
|
+
for (const candidate of trueValues) if (falseValues.has(candidate)) throw new Error(`Boolean preprocessor option value "${candidate}" cannot map to both true and false`);
|
|
797
|
+
return (value) => {
|
|
798
|
+
try {
|
|
799
|
+
const cleaned = normalize(value);
|
|
800
|
+
if (trueValues.has(cleaned)) return true;
|
|
801
|
+
if (falseValues.has(cleaned)) return false;
|
|
802
|
+
} catch {}
|
|
803
|
+
return value;
|
|
804
|
+
};
|
|
805
|
+
};
|
|
806
|
+
/**
|
|
807
|
+
* Create an enum preprocessor - pass through unchanged
|
|
808
|
+
*/
|
|
809
|
+
const enumPreprocessor = () => (value) => value;
|
|
810
|
+
/**
|
|
811
|
+
* Default preprocessors map for each environment variable type
|
|
812
|
+
*/
|
|
813
|
+
const preprocessors = {
|
|
814
|
+
string: stringPreprocessor,
|
|
815
|
+
number: numberPreprocessor,
|
|
816
|
+
boolean: booleanPreprocessor,
|
|
817
|
+
enum: enumPreprocessor
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
//#endregion
|
|
821
|
+
//#region ../core/src/processing/Preprocessor.ts
|
|
822
|
+
const optionKeyMap = {
|
|
823
|
+
string: "string",
|
|
824
|
+
number: "number",
|
|
825
|
+
boolean: "boolean",
|
|
826
|
+
enum: "enum"
|
|
827
|
+
};
|
|
828
|
+
const explicitPreprocessorFactories = {
|
|
829
|
+
string: stringPreprocessor,
|
|
830
|
+
number: numberPreprocessor,
|
|
831
|
+
boolean: booleanPreprocessor,
|
|
832
|
+
enum: enumPreprocessor
|
|
833
|
+
};
|
|
834
|
+
function createDefaultPreprocessor(envVarType) {
|
|
835
|
+
switch (envVarType) {
|
|
836
|
+
case "number": return numberPreprocessor();
|
|
837
|
+
case "boolean": return booleanPreprocessor();
|
|
838
|
+
default: return;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function createExplicitPreprocessor(envVarType) {
|
|
842
|
+
const factory = explicitPreprocessorFactories[envVarType];
|
|
843
|
+
if (!factory) return;
|
|
844
|
+
return factory();
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Resolve the effective preprocessor for a given environment variable type.
|
|
848
|
+
* Respects custom overrides and falls back to built-in defaults (number, boolean).
|
|
849
|
+
*/
|
|
850
|
+
function resolvePreprocessor(envVarType, preprocessorOptions) {
|
|
851
|
+
if (envVarType === "boolean") {
|
|
852
|
+
const override = preprocessorOptions?.boolean;
|
|
853
|
+
if (override === void 0) return createDefaultPreprocessor(envVarType);
|
|
854
|
+
if (override === false) return;
|
|
855
|
+
if (override === true) return createExplicitPreprocessor(envVarType);
|
|
856
|
+
if (typeof override === "function") return override;
|
|
857
|
+
return booleanPreprocessor(override);
|
|
858
|
+
}
|
|
859
|
+
const key = optionKeyMap[envVarType];
|
|
860
|
+
const custom = preprocessorOptions?.[key];
|
|
861
|
+
if (custom === void 0) return createDefaultPreprocessor(envVarType);
|
|
862
|
+
if (custom === false) return;
|
|
863
|
+
if (custom === true) return createExplicitPreprocessor(envVarType);
|
|
864
|
+
return custom;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
//#endregion
|
|
868
|
+
//#region ../converters/src/converters/zod/ZodV3Converter.ts
|
|
869
|
+
/**
|
|
870
|
+
* Zod v3 schema converter implementation
|
|
871
|
+
*/
|
|
872
|
+
var ZodV3Converter = class {
|
|
873
|
+
applies(schema) {
|
|
874
|
+
return isZodV3Schema(schema);
|
|
875
|
+
}
|
|
876
|
+
convert(schema) {
|
|
877
|
+
return convertFromZodV3Schema(schema);
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
/**
|
|
881
|
+
* Check if a schema is a Zod v3 schema
|
|
882
|
+
*/
|
|
883
|
+
function isZodV3Schema(schema) {
|
|
884
|
+
if (!schema || typeof schema !== "object") return false;
|
|
885
|
+
const candidate = schema;
|
|
886
|
+
return Boolean(candidate._def && !("_zod" in candidate) && (candidate._def.typeName || typeof candidate.parse === "function"));
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Extract definition from Zod v3 schema
|
|
890
|
+
*/
|
|
891
|
+
function getV3Def(schema) {
|
|
892
|
+
return schema._def || {};
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Get the type name from a Zod v3 definition
|
|
896
|
+
*/
|
|
897
|
+
function getV3TypeName(def) {
|
|
898
|
+
return def.typeName || "unknown";
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Check if a type is a wrapper type that should be unwrapped
|
|
902
|
+
*/
|
|
903
|
+
function isV3WrapperType(typeName) {
|
|
904
|
+
return [
|
|
905
|
+
"ZodOptional",
|
|
906
|
+
"ZodDefault",
|
|
907
|
+
"ZodEffects",
|
|
908
|
+
"ZodNullable",
|
|
909
|
+
"ZodNullish",
|
|
910
|
+
"ZodReadonly",
|
|
911
|
+
"ZodBranded",
|
|
912
|
+
"ZodCatch"
|
|
913
|
+
].includes(typeName);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Get inner schema from wrapper
|
|
917
|
+
*/
|
|
918
|
+
function getV3InnerSchema(def) {
|
|
919
|
+
return def.innerType || def.schema;
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Get default value from definition
|
|
923
|
+
*/
|
|
924
|
+
function getV3DefaultValue(def) {
|
|
925
|
+
if (def.defaultValue === void 0) return void 0;
|
|
926
|
+
return typeof def.defaultValue === "function" ? def.defaultValue() : def.defaultValue;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Extract enum values from schema
|
|
930
|
+
*/
|
|
931
|
+
function extractV3EnumValues(def) {
|
|
932
|
+
if (!Array.isArray(def.values)) return void 0;
|
|
933
|
+
return def.values.map((v) => String(v));
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Get description from schema
|
|
937
|
+
*/
|
|
938
|
+
function getV3Description(schema, def) {
|
|
939
|
+
const directDesc = schema.description;
|
|
940
|
+
if (typeof directDesc === "string" && directDesc.length > 0) return directDesc;
|
|
941
|
+
if (typeof def.description === "string" && def.description.length > 0) return def.description;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Resolve environment variable type from schema
|
|
945
|
+
*/
|
|
946
|
+
function resolveV3EnvVarType(schema) {
|
|
947
|
+
const def = getV3Def(schema);
|
|
948
|
+
switch (getV3TypeName(def)) {
|
|
949
|
+
case "ZodString": return "string";
|
|
950
|
+
case "ZodNumber": return "number";
|
|
951
|
+
case "ZodBoolean": return "boolean";
|
|
952
|
+
case "ZodEnum": return "enum";
|
|
953
|
+
default:
|
|
954
|
+
if (def.values) return "enum";
|
|
955
|
+
return "string";
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
function peelV3Schema(schema) {
|
|
959
|
+
let current = schema;
|
|
960
|
+
let required = true;
|
|
961
|
+
let defaultValue = void 0;
|
|
962
|
+
let description = void 0;
|
|
963
|
+
for (let i = 0; i < 20; i++) {
|
|
964
|
+
const def = getV3Def(current);
|
|
965
|
+
const typeName = getV3TypeName(def);
|
|
966
|
+
if (!description) description = getV3Description(current, def);
|
|
967
|
+
if (!isV3WrapperType(typeName)) break;
|
|
968
|
+
if (typeName === "ZodOptional" || typeName === "ZodNullish") required = false;
|
|
969
|
+
if (typeName === "ZodDefault" && defaultValue === void 0) defaultValue = getV3DefaultValue(def);
|
|
970
|
+
const inner = getV3InnerSchema(def);
|
|
971
|
+
if (!inner) break;
|
|
972
|
+
current = inner;
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
schema: current,
|
|
976
|
+
required,
|
|
977
|
+
default: defaultValue,
|
|
978
|
+
description
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Create a process function for Zod v3 schemas
|
|
983
|
+
*/
|
|
984
|
+
function createV3ProcessFunction(schema, type) {
|
|
985
|
+
return (value) => {
|
|
986
|
+
try {
|
|
987
|
+
let processSchema = schema;
|
|
988
|
+
if (type === "number") processSchema = z.coerce.number();
|
|
989
|
+
else if (type === "boolean") processSchema = z.string().transform((val) => {
|
|
990
|
+
const lower = val.toLowerCase().trim();
|
|
991
|
+
if ([
|
|
992
|
+
"true",
|
|
993
|
+
"1",
|
|
994
|
+
"yes",
|
|
995
|
+
"on",
|
|
996
|
+
"enabled"
|
|
997
|
+
].includes(lower)) return true;
|
|
998
|
+
if ([
|
|
999
|
+
"false",
|
|
1000
|
+
"0",
|
|
1001
|
+
"no",
|
|
1002
|
+
"off",
|
|
1003
|
+
"disabled"
|
|
1004
|
+
].includes(lower)) return false;
|
|
1005
|
+
throw new Error(`Expected boolean value, received "${val}"`);
|
|
1006
|
+
});
|
|
1007
|
+
const result = processSchema.safeParse(value);
|
|
1008
|
+
if (result.success) return result.data;
|
|
1009
|
+
throw new Error(`Failed to parse value "${value}": ${result.error.issues.map((issue) => issue.message).join("; ")}`);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1012
|
+
throw new Error(message);
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Convert Zod v3 schema to EnvVarSchema
|
|
1018
|
+
*/
|
|
1019
|
+
function convertFromZodV3Schema(schema) {
|
|
1020
|
+
const peeled = peelV3Schema(schema);
|
|
1021
|
+
const type = resolveV3EnvVarType(peeled.schema);
|
|
1022
|
+
const def = getV3Def(peeled.schema);
|
|
1023
|
+
switch (type) {
|
|
1024
|
+
case "boolean": return new BooleanEnvVarSchema({
|
|
1025
|
+
process: createV3ProcessFunction(peeled.schema, "boolean"),
|
|
1026
|
+
required: peeled.required,
|
|
1027
|
+
default: typeof peeled.default === "boolean" ? peeled.default : void 0,
|
|
1028
|
+
description: peeled.description
|
|
1029
|
+
});
|
|
1030
|
+
case "number": return new NumberEnvVarSchema({
|
|
1031
|
+
process: createV3ProcessFunction(peeled.schema, "number"),
|
|
1032
|
+
required: peeled.required,
|
|
1033
|
+
default: typeof peeled.default === "number" ? peeled.default : void 0,
|
|
1034
|
+
description: peeled.description
|
|
1035
|
+
});
|
|
1036
|
+
case "enum": {
|
|
1037
|
+
const values = extractV3EnumValues(def) || [];
|
|
1038
|
+
return new EnumEnvVarSchema({
|
|
1039
|
+
process: createV3ProcessFunction(peeled.schema, "enum"),
|
|
1040
|
+
values,
|
|
1041
|
+
required: peeled.required,
|
|
1042
|
+
default: typeof peeled.default === "string" ? peeled.default : void 0,
|
|
1043
|
+
description: peeled.description
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
case "string":
|
|
1047
|
+
default: {
|
|
1048
|
+
const process$1 = createV3ProcessFunction(peeled.schema, "string");
|
|
1049
|
+
const defaultValue = typeof peeled.default === "string" ? peeled.default : void 0;
|
|
1050
|
+
return new StringEnvVarSchema({
|
|
1051
|
+
process: process$1,
|
|
1052
|
+
required: peeled.required,
|
|
1053
|
+
default: defaultValue,
|
|
1054
|
+
description: peeled.description
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
//#endregion
|
|
1061
|
+
//#region ../converters/src/converters/zod/ZodV4Converter.ts
|
|
1062
|
+
/**
|
|
1063
|
+
* Zod v4 schema converter implementation
|
|
1064
|
+
*/
|
|
1065
|
+
var ZodV4Converter = class {
|
|
1066
|
+
applies(schema) {
|
|
1067
|
+
return isZodV4Schema(schema);
|
|
1068
|
+
}
|
|
1069
|
+
convert(schema) {
|
|
1070
|
+
return convertFromZodV4Schema(schema);
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
/**
|
|
1074
|
+
* Check if a schema is a Zod v4 schema
|
|
1075
|
+
*/
|
|
1076
|
+
function isZodV4Schema(schema) {
|
|
1077
|
+
if (!schema || typeof schema !== "object") return false;
|
|
1078
|
+
return "_zod" in schema;
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Extract definition from Zod v4 schema
|
|
1082
|
+
*/
|
|
1083
|
+
function getV4Def(schema) {
|
|
1084
|
+
const container = schema;
|
|
1085
|
+
if (container._zod && container._zod.def) return container._zod.def;
|
|
1086
|
+
return {};
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Get the type name from a Zod v4 definition
|
|
1090
|
+
*/
|
|
1091
|
+
function getV4TypeName(def) {
|
|
1092
|
+
return def.type || "unknown";
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Check if a type is a wrapper type that should be unwrapped
|
|
1096
|
+
*/
|
|
1097
|
+
function isV4WrapperType(typeName) {
|
|
1098
|
+
return [
|
|
1099
|
+
"optional",
|
|
1100
|
+
"default",
|
|
1101
|
+
"catch",
|
|
1102
|
+
"readonly",
|
|
1103
|
+
"brand",
|
|
1104
|
+
"prefault",
|
|
1105
|
+
"nullable",
|
|
1106
|
+
"nullish"
|
|
1107
|
+
].includes(typeName);
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Get inner schema from wrapper
|
|
1111
|
+
*/
|
|
1112
|
+
function getV4InnerSchema(def) {
|
|
1113
|
+
return def.innerType || def.schema || def.in || def.inner;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Get default value from definition
|
|
1117
|
+
*/
|
|
1118
|
+
function getV4DefaultValue(def) {
|
|
1119
|
+
if (def.defaultValue !== void 0) return typeof def.defaultValue === "function" ? def.defaultValue() : def.defaultValue;
|
|
1120
|
+
if (def.prefaultValue !== void 0) return typeof def.prefaultValue === "function" ? def.prefaultValue() : def.prefaultValue;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Extract enum values from schema
|
|
1124
|
+
*/
|
|
1125
|
+
function extractV4EnumValues(def) {
|
|
1126
|
+
if (Array.isArray(def.values)) return def.values.map((v) => String(v));
|
|
1127
|
+
if (def.entries && typeof def.entries === "object") return Object.values(def.entries).map((v) => String(v));
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Get description from schema
|
|
1131
|
+
*/
|
|
1132
|
+
function getV4Description(schema, def) {
|
|
1133
|
+
const directDesc = schema.description;
|
|
1134
|
+
if (typeof directDesc === "string" && directDesc.length > 0) return directDesc;
|
|
1135
|
+
if (typeof def.description === "string" && def.description.length > 0) return def.description;
|
|
1136
|
+
const metaFn = schema.meta;
|
|
1137
|
+
if (typeof metaFn === "function") {
|
|
1138
|
+
const meta = metaFn.call(schema);
|
|
1139
|
+
if (meta?.description && typeof meta.description === "string") return meta.description.trim();
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Resolve environment variable type from schema
|
|
1144
|
+
*/
|
|
1145
|
+
function resolveV4EnvVarType(schema) {
|
|
1146
|
+
const def = getV4Def(schema);
|
|
1147
|
+
switch (getV4TypeName(def)) {
|
|
1148
|
+
case "string": return "string";
|
|
1149
|
+
case "number":
|
|
1150
|
+
case "int": return "number";
|
|
1151
|
+
case "boolean":
|
|
1152
|
+
case "stringbool": return "boolean";
|
|
1153
|
+
case "pipe":
|
|
1154
|
+
if (def.in && def.out) {
|
|
1155
|
+
const inType = resolveV4EnvVarType(def.in);
|
|
1156
|
+
const outType = resolveV4EnvVarType(def.out);
|
|
1157
|
+
if (inType === "string" && outType === "boolean") return "boolean";
|
|
1158
|
+
}
|
|
1159
|
+
return "string";
|
|
1160
|
+
case "enum": return "enum";
|
|
1161
|
+
default:
|
|
1162
|
+
if (def.values || def.entries) return "enum";
|
|
1163
|
+
return "string";
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
function peelV4Schema(schema) {
|
|
1167
|
+
let current = schema;
|
|
1168
|
+
let required = true;
|
|
1169
|
+
let defaultValue = void 0;
|
|
1170
|
+
let description = void 0;
|
|
1171
|
+
for (let i = 0; i < 20; i++) {
|
|
1172
|
+
const def = getV4Def(current);
|
|
1173
|
+
const typeName = getV4TypeName(def);
|
|
1174
|
+
if (!description) description = getV4Description(current, def);
|
|
1175
|
+
if (!isV4WrapperType(typeName)) if (typeName === "pipe" && def.in && def.out) {
|
|
1176
|
+
const inType = resolveV4EnvVarType(def.in);
|
|
1177
|
+
const outType = resolveV4EnvVarType(def.out);
|
|
1178
|
+
if (inType === "string" && outType === "boolean") break;
|
|
1179
|
+
} else break;
|
|
1180
|
+
if (typeName === "optional" || typeName === "nullish") required = false;
|
|
1181
|
+
if ((typeName === "default" || typeName === "prefault") && defaultValue === void 0) defaultValue = getV4DefaultValue(def);
|
|
1182
|
+
const inner = getV4InnerSchema(def);
|
|
1183
|
+
if (!inner) break;
|
|
1184
|
+
current = inner;
|
|
1185
|
+
}
|
|
1186
|
+
return {
|
|
1187
|
+
schema: current,
|
|
1188
|
+
required,
|
|
1189
|
+
default: defaultValue,
|
|
1190
|
+
description
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Create a process function for Zod v4 schemas
|
|
1195
|
+
*/
|
|
1196
|
+
function createV4ProcessFunction(schema, type, originalSchema) {
|
|
1197
|
+
return (value) => {
|
|
1198
|
+
try {
|
|
1199
|
+
let processSchema = schema;
|
|
1200
|
+
const def = getV4Def(schema);
|
|
1201
|
+
const typeName = getV4TypeName(def);
|
|
1202
|
+
if (typeName === "stringbool" || typeName === "pipe" && def.in && def.out && resolveV4EnvVarType(def.in) === "string" && resolveV4EnvVarType(def.out) === "boolean") processSchema = originalSchema;
|
|
1203
|
+
else if (type === "number") processSchema = z.coerce.number();
|
|
1204
|
+
else if (type === "boolean") processSchema = z.stringbool();
|
|
1205
|
+
const result = z4core.safeParse(processSchema, value);
|
|
1206
|
+
if (result.success) return result.data;
|
|
1207
|
+
throw new Error(`Failed to parse value "${value}": ${result.error.issues.map((issue) => issue.message).join("; ")}`);
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1210
|
+
throw new Error(message);
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Convert Zod v4 schema to EnvVarSchema
|
|
1216
|
+
*/
|
|
1217
|
+
function convertFromZodV4Schema(schema) {
|
|
1218
|
+
const peeled = peelV4Schema(schema);
|
|
1219
|
+
const type = resolveV4EnvVarType(peeled.schema);
|
|
1220
|
+
const def = getV4Def(peeled.schema);
|
|
1221
|
+
switch (type) {
|
|
1222
|
+
case "boolean": return new BooleanEnvVarSchema({
|
|
1223
|
+
process: createV4ProcessFunction(peeled.schema, "boolean", schema),
|
|
1224
|
+
required: peeled.required,
|
|
1225
|
+
default: typeof peeled.default === "boolean" ? peeled.default : void 0,
|
|
1226
|
+
description: peeled.description
|
|
1227
|
+
});
|
|
1228
|
+
case "number": return new NumberEnvVarSchema({
|
|
1229
|
+
process: createV4ProcessFunction(peeled.schema, "number", schema),
|
|
1230
|
+
required: peeled.required,
|
|
1231
|
+
default: typeof peeled.default === "number" ? peeled.default : void 0,
|
|
1232
|
+
description: peeled.description
|
|
1233
|
+
});
|
|
1234
|
+
case "enum": {
|
|
1235
|
+
const values = extractV4EnumValues(def) || [];
|
|
1236
|
+
return new EnumEnvVarSchema({
|
|
1237
|
+
process: createV4ProcessFunction(peeled.schema, "enum", schema),
|
|
1238
|
+
values,
|
|
1239
|
+
required: peeled.required,
|
|
1240
|
+
default: typeof peeled.default === "string" ? peeled.default : void 0,
|
|
1241
|
+
description: peeled.description
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
case "string":
|
|
1245
|
+
default: {
|
|
1246
|
+
const process$1 = createV4ProcessFunction(peeled.schema, "string", schema);
|
|
1247
|
+
const defaultValue = typeof peeled.default === "string" ? peeled.default : void 0;
|
|
1248
|
+
return new StringEnvVarSchema({
|
|
1249
|
+
process: process$1,
|
|
1250
|
+
required: peeled.required,
|
|
1251
|
+
default: defaultValue,
|
|
1252
|
+
description: peeled.description
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
//#endregion
|
|
1259
|
+
//#region ../converters/src/converters/joi/JoiConverter.ts
|
|
1260
|
+
/**
|
|
1261
|
+
* Joi schema converter implementation
|
|
1262
|
+
*/
|
|
1263
|
+
var JoiConverter = class {
|
|
1264
|
+
applies(schema) {
|
|
1265
|
+
return isJoiSchema(schema);
|
|
1266
|
+
}
|
|
1267
|
+
convert(schema) {
|
|
1268
|
+
return convertFromJoiSchema(schema);
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
/**
|
|
1272
|
+
* Check if a schema is a Joi schema using the official API
|
|
1273
|
+
*/
|
|
1274
|
+
function isJoiSchema(schema) {
|
|
1275
|
+
if (!schema || typeof schema !== "object") return false;
|
|
1276
|
+
const candidate = schema;
|
|
1277
|
+
if (!(typeof candidate.describe === "function" && typeof candidate.validate === "function")) return false;
|
|
1278
|
+
if (candidate.isJoi === true) return true;
|
|
1279
|
+
const hasAsyncVariant = typeof candidate.validateAsync === "function";
|
|
1280
|
+
const hasRoot = typeof candidate.$_root === "object" && candidate.$_root !== null;
|
|
1281
|
+
const hasFlags = typeof candidate._flags === "object" && candidate._flags !== null;
|
|
1282
|
+
return hasAsyncVariant || hasRoot || hasFlags;
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Get description from Joi schema using public API first, internal as fallback
|
|
1286
|
+
*/
|
|
1287
|
+
function getJoiDescription(schema) {
|
|
1288
|
+
try {
|
|
1289
|
+
return schema.describe()?.flags?.description;
|
|
1290
|
+
} catch {
|
|
1291
|
+
return schema._flags?.description;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Check if schema is required using public API first, internal as fallback
|
|
1296
|
+
*/
|
|
1297
|
+
function isJoiRequired(schema) {
|
|
1298
|
+
try {
|
|
1299
|
+
const presence = schema.describe()?.flags?.presence;
|
|
1300
|
+
if (presence === "required") return true;
|
|
1301
|
+
if (presence === "optional") return false;
|
|
1302
|
+
return presence === void 0;
|
|
1303
|
+
} catch {
|
|
1304
|
+
const flags = schema._flags;
|
|
1305
|
+
if (flags?.presence === "required") return true;
|
|
1306
|
+
if (flags?.presence === "optional") return false;
|
|
1307
|
+
return true;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Get default value from Joi schema using public API first, internal as fallback
|
|
1312
|
+
*/
|
|
1313
|
+
function getJoiDefaultValue(schema) {
|
|
1314
|
+
try {
|
|
1315
|
+
const defaultValue = schema.describe()?.flags?.default;
|
|
1316
|
+
return typeof defaultValue === "function" ? defaultValue() : defaultValue;
|
|
1317
|
+
} catch {
|
|
1318
|
+
const flags = schema._flags;
|
|
1319
|
+
if (flags?.default !== void 0) {
|
|
1320
|
+
const defaultValue = flags.default;
|
|
1321
|
+
return typeof defaultValue === "function" ? defaultValue() : defaultValue;
|
|
1322
|
+
}
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Get valid values for enum-like schemas using public API first, internal as fallback
|
|
1328
|
+
*/
|
|
1329
|
+
function getJoiValidValues(schema) {
|
|
1330
|
+
const values = [];
|
|
1331
|
+
let isOnlySchema = false;
|
|
1332
|
+
try {
|
|
1333
|
+
const description = schema.describe();
|
|
1334
|
+
if (description?.allow && Array.isArray(description.allow)) values.push(...description.allow);
|
|
1335
|
+
isOnlySchema = description?.flags?.only === true;
|
|
1336
|
+
} catch {
|
|
1337
|
+
if (schema._valids?._values) values.push(...Array.from(schema._valids._values));
|
|
1338
|
+
isOnlySchema = schema._flags?.only === true;
|
|
1339
|
+
}
|
|
1340
|
+
if (isOnlySchema && values.length > 0) return values.map((v) => String(v));
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Resolve environment variable type from Joi schema using public API first, internal as fallback
|
|
1344
|
+
*/
|
|
1345
|
+
function resolveJoiEnvVarType(schema) {
|
|
1346
|
+
let type;
|
|
1347
|
+
const exposedSchema = schema;
|
|
1348
|
+
try {
|
|
1349
|
+
type = schema.describe()?.type;
|
|
1350
|
+
} catch {
|
|
1351
|
+
type = exposedSchema._type || schema.type;
|
|
1352
|
+
}
|
|
1353
|
+
const validValues = getJoiValidValues(schema);
|
|
1354
|
+
if (validValues && validValues.length > 0) return "enum";
|
|
1355
|
+
switch (type) {
|
|
1356
|
+
case "string": return "string";
|
|
1357
|
+
case "number": return "number";
|
|
1358
|
+
case "boolean": return "boolean";
|
|
1359
|
+
default: return "string";
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Create a process function for Joi schemas
|
|
1364
|
+
*/
|
|
1365
|
+
function createJoiProcessFunction(schema, type) {
|
|
1366
|
+
return (value) => {
|
|
1367
|
+
try {
|
|
1368
|
+
let processValue$1 = value;
|
|
1369
|
+
if (type === "number") {
|
|
1370
|
+
const num = Number(value);
|
|
1371
|
+
if (Number.isNaN(num)) throw new Error(`Expected number, received "${value}"`);
|
|
1372
|
+
processValue$1 = num;
|
|
1373
|
+
} else if (type === "boolean") {
|
|
1374
|
+
const lower = value.toLowerCase().trim();
|
|
1375
|
+
if ([
|
|
1376
|
+
"true",
|
|
1377
|
+
"1",
|
|
1378
|
+
"yes",
|
|
1379
|
+
"on",
|
|
1380
|
+
"enabled"
|
|
1381
|
+
].includes(lower)) processValue$1 = true;
|
|
1382
|
+
else if ([
|
|
1383
|
+
"false",
|
|
1384
|
+
"0",
|
|
1385
|
+
"no",
|
|
1386
|
+
"off",
|
|
1387
|
+
"disabled"
|
|
1388
|
+
].includes(lower)) processValue$1 = false;
|
|
1389
|
+
else throw new Error(`Expected boolean value, received "${value}"`);
|
|
1390
|
+
}
|
|
1391
|
+
const result = schema.validate(processValue$1, { convert: true });
|
|
1392
|
+
if (result.error) {
|
|
1393
|
+
const message = result.error.details ? result.error.details.map((d) => d.message).join("; ") : result.error.message || String(result.error);
|
|
1394
|
+
throw new Error(`Validation failed: ${message}`);
|
|
1395
|
+
}
|
|
1396
|
+
return result.value;
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1399
|
+
throw new Error(message);
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Convert Joi schema to EnvVarSchema
|
|
1405
|
+
*/
|
|
1406
|
+
function convertFromJoiSchema(schema) {
|
|
1407
|
+
const type = resolveJoiEnvVarType(schema);
|
|
1408
|
+
const required = isJoiRequired(schema);
|
|
1409
|
+
const defaultValue = getJoiDefaultValue(schema);
|
|
1410
|
+
const description = getJoiDescription(schema);
|
|
1411
|
+
switch (type) {
|
|
1412
|
+
case "boolean": return new BooleanEnvVarSchema({
|
|
1413
|
+
process: createJoiProcessFunction(schema, "boolean"),
|
|
1414
|
+
required,
|
|
1415
|
+
default: typeof defaultValue === "boolean" ? defaultValue : void 0,
|
|
1416
|
+
description
|
|
1417
|
+
});
|
|
1418
|
+
case "number": return new NumberEnvVarSchema({
|
|
1419
|
+
process: createJoiProcessFunction(schema, "number"),
|
|
1420
|
+
required,
|
|
1421
|
+
default: typeof defaultValue === "number" ? defaultValue : void 0,
|
|
1422
|
+
description
|
|
1423
|
+
});
|
|
1424
|
+
case "enum": {
|
|
1425
|
+
const values = getJoiValidValues(schema) || [];
|
|
1426
|
+
return new EnumEnvVarSchema({
|
|
1427
|
+
process: createJoiProcessFunction(schema, "enum"),
|
|
1428
|
+
values,
|
|
1429
|
+
required,
|
|
1430
|
+
default: typeof defaultValue === "string" ? defaultValue : void 0,
|
|
1431
|
+
description
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
case "string":
|
|
1435
|
+
default: return new StringEnvVarSchema({
|
|
1436
|
+
process: createJoiProcessFunction(schema, "string"),
|
|
1437
|
+
required,
|
|
1438
|
+
default: typeof defaultValue === "string" ? defaultValue : void 0,
|
|
1439
|
+
description
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
//#endregion
|
|
1445
|
+
//#region ../converters/src/converters/superstruct/SuperstructConverter.ts
|
|
1446
|
+
/**
|
|
1447
|
+
* Superstruct schema converter implementation
|
|
1448
|
+
*/
|
|
1449
|
+
var SuperstructConverter = class {
|
|
1450
|
+
applies(schema) {
|
|
1451
|
+
return isSuperstructSchema(schema);
|
|
1452
|
+
}
|
|
1453
|
+
convert(schema) {
|
|
1454
|
+
return convertFromSuperstruct(schema);
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
/**
|
|
1458
|
+
* Detect if a value looks like a Superstruct Struct instance
|
|
1459
|
+
*/
|
|
1460
|
+
function isSuperstructSchema(value) {
|
|
1461
|
+
if (!value || typeof value !== "object") return false;
|
|
1462
|
+
const candidate = value;
|
|
1463
|
+
return typeof candidate.type === "string" && typeof candidate.validate === "function" && typeof candidate.create === "function";
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Convert a Superstruct schema to an EnvVarSchema
|
|
1467
|
+
*/
|
|
1468
|
+
function convertFromSuperstruct(schema) {
|
|
1469
|
+
const resolvedType = resolveSuperstructEnvVarType(schema);
|
|
1470
|
+
const required = isStructRequired(schema);
|
|
1471
|
+
const description = getStructDescription(schema);
|
|
1472
|
+
const defaultValue = getStructDefault(schema);
|
|
1473
|
+
if (resolvedType === "enum") {
|
|
1474
|
+
const values = extractEnumValues(schema);
|
|
1475
|
+
if (!values || values.length === 0) return createStringEnvVarSchema(schema, required, defaultValue, description);
|
|
1476
|
+
const readonlyValues = values;
|
|
1477
|
+
return new EnumEnvVarSchema({
|
|
1478
|
+
process: createSuperstructProcessFunction(schema, "enum"),
|
|
1479
|
+
values: readonlyValues,
|
|
1480
|
+
required,
|
|
1481
|
+
default: typeof defaultValue === "string" ? defaultValue : void 0,
|
|
1482
|
+
description
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
if (resolvedType === "boolean") return new BooleanEnvVarSchema({
|
|
1486
|
+
process: createSuperstructProcessFunction(schema, "boolean"),
|
|
1487
|
+
required,
|
|
1488
|
+
default: typeof defaultValue === "boolean" ? defaultValue : void 0,
|
|
1489
|
+
description
|
|
1490
|
+
});
|
|
1491
|
+
if (resolvedType === "number") return new NumberEnvVarSchema({
|
|
1492
|
+
process: createSuperstructProcessFunction(schema, "number"),
|
|
1493
|
+
required,
|
|
1494
|
+
default: typeof defaultValue === "number" ? defaultValue : void 0,
|
|
1495
|
+
description
|
|
1496
|
+
});
|
|
1497
|
+
return createStringEnvVarSchema(schema, required, defaultValue, description);
|
|
1498
|
+
}
|
|
1499
|
+
function createStringEnvVarSchema(schema, required, defaultValue, description) {
|
|
1500
|
+
return new StringEnvVarSchema({
|
|
1501
|
+
process: createSuperstructProcessFunction(schema, "string"),
|
|
1502
|
+
required,
|
|
1503
|
+
default: typeof defaultValue === "string" ? defaultValue : void 0,
|
|
1504
|
+
description
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
function resolveSuperstructEnvVarType(schema) {
|
|
1508
|
+
switch (getStructType(schema)) {
|
|
1509
|
+
case "boolean": return "boolean";
|
|
1510
|
+
case "number":
|
|
1511
|
+
case "integer": return "number";
|
|
1512
|
+
case "enums": return "enum";
|
|
1513
|
+
case "literal": return typeof schema.schema === "string" ? "enum" : "string";
|
|
1514
|
+
default: return "string";
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function getStructType(schema) {
|
|
1518
|
+
const typeValue = schema.type;
|
|
1519
|
+
return typeof typeValue === "string" ? typeValue : "unknown";
|
|
1520
|
+
}
|
|
1521
|
+
function extractEnumValues(schema) {
|
|
1522
|
+
const typeName = getStructType(schema);
|
|
1523
|
+
if (typeName === "enums") {
|
|
1524
|
+
const structSchema = schema.schema;
|
|
1525
|
+
if (structSchema && typeof structSchema === "object") {
|
|
1526
|
+
const rawValues = Object.values(structSchema);
|
|
1527
|
+
if (rawValues.every((value) => typeof value === "string")) return rawValues;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
if (typeName === "literal") {
|
|
1531
|
+
const value = schema.schema;
|
|
1532
|
+
if (typeof value === "string") return [value];
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
function isStructRequired(schema) {
|
|
1536
|
+
try {
|
|
1537
|
+
const [error] = schema.validate(void 0);
|
|
1538
|
+
return !!error;
|
|
1539
|
+
} catch {
|
|
1540
|
+
return true;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
function getStructDefault(schema) {
|
|
1544
|
+
try {
|
|
1545
|
+
const [error, value] = schema.validate(void 0, { coerce: true });
|
|
1546
|
+
if (!error && value !== void 0) return value;
|
|
1547
|
+
} catch {}
|
|
1548
|
+
}
|
|
1549
|
+
function getStructDescription(schema) {
|
|
1550
|
+
const structSchema = schema.schema;
|
|
1551
|
+
if (structSchema && typeof structSchema === "object" && typeof structSchema.description === "string") return structSchema.description;
|
|
1552
|
+
if (typeof schema.description === "string") return schema.description;
|
|
1553
|
+
}
|
|
1554
|
+
function createSuperstructProcessFunction(schema, type) {
|
|
1555
|
+
return (value) => {
|
|
1556
|
+
if (typeof value !== "string") throw new Error("Value must be a string");
|
|
1557
|
+
let candidate = value;
|
|
1558
|
+
if (type === "number") {
|
|
1559
|
+
const trimmed = value.trim();
|
|
1560
|
+
if (trimmed === "") candidate = void 0;
|
|
1561
|
+
else {
|
|
1562
|
+
const parsed = Number(trimmed);
|
|
1563
|
+
if (Number.isNaN(parsed) || !Number.isFinite(parsed)) throw new Error(`"${value}" is not a valid number`);
|
|
1564
|
+
candidate = parsed;
|
|
1565
|
+
}
|
|
1566
|
+
} else if (type === "boolean") {
|
|
1567
|
+
const trimmed = value.trim().toLowerCase();
|
|
1568
|
+
if (trimmed === "") candidate = void 0;
|
|
1569
|
+
else if ([
|
|
1570
|
+
"true",
|
|
1571
|
+
"1",
|
|
1572
|
+
"yes",
|
|
1573
|
+
"on",
|
|
1574
|
+
"enabled"
|
|
1575
|
+
].includes(trimmed)) candidate = true;
|
|
1576
|
+
else if ([
|
|
1577
|
+
"false",
|
|
1578
|
+
"0",
|
|
1579
|
+
"no",
|
|
1580
|
+
"off",
|
|
1581
|
+
"disabled"
|
|
1582
|
+
].includes(trimmed)) candidate = false;
|
|
1583
|
+
else throw new Error(`"${value}" is not a valid boolean. Use: true/false, 1/0, yes/no, on/off, or enabled/disabled`);
|
|
1584
|
+
} else if (value === "") candidate = void 0;
|
|
1585
|
+
const [error, result] = schema.validate(candidate, { coerce: true });
|
|
1586
|
+
if (error) throw new Error(error.message);
|
|
1587
|
+
return result;
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
//#endregion
|
|
1592
|
+
//#region ../converters/src/converters.ts
|
|
1593
|
+
/**
|
|
1594
|
+
* Registry of available schema converters
|
|
1595
|
+
*/
|
|
1596
|
+
const converters = [
|
|
1597
|
+
new ZodV4Converter(),
|
|
1598
|
+
new ZodV3Converter(),
|
|
1599
|
+
new JoiConverter(),
|
|
1600
|
+
new SuperstructConverter()
|
|
1601
|
+
];
|
|
1602
|
+
|
|
1603
|
+
//#endregion
|
|
1604
|
+
//#region ../models/src/resolve.ts
|
|
1605
|
+
/**
|
|
1606
|
+
* Resolve any supported schema to a EnvVarSchema.
|
|
1607
|
+
*
|
|
1608
|
+
* This is the main entry point for converting external schemas
|
|
1609
|
+
* (like Zod schemas) to envcredible's internal schema format.
|
|
1610
|
+
* If the input is already a EnvVarSchema, it returns it directly.
|
|
1611
|
+
*
|
|
1612
|
+
* @param schema - The schema to convert (e.g., Zod v3/v4 schema or EnvVarSchema)
|
|
1613
|
+
* @returns EnvVarSchema that can be used with envcredible
|
|
1614
|
+
* @throws Error if no converter can handle the schema
|
|
1615
|
+
*/
|
|
1616
|
+
function resolveSchema(schema) {
|
|
1617
|
+
if (isEnvVarSchema(schema)) return schema;
|
|
1618
|
+
for (const converter of converters) if (converter.applies(schema)) return converter.convert(schema);
|
|
1619
|
+
throw new Error(`No converter found for schema. Supported types: Zod v3, Zod v4, Joi, Superstruct, EnvVarSchema. Received: ${typeof schema}`);
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Resolve mixed schema map (Zod schemas or EnvVarSchema) to pure EnvVarSchema map.
|
|
1623
|
+
*
|
|
1624
|
+
* This function takes a map where values can be:
|
|
1625
|
+
* - Zod v3 schemas
|
|
1626
|
+
* - Zod v4 schemas
|
|
1627
|
+
* - Already resolved EnvVarSchema instances
|
|
1628
|
+
*
|
|
1629
|
+
* And converts everything to EnvVarSchema instances that can be used throughout envcredible.
|
|
1630
|
+
*
|
|
1631
|
+
* @param schemas - Object mapping environment variable names to various schema types
|
|
1632
|
+
* @returns Record mapping env var names to EnvVarSchema instances
|
|
1633
|
+
*/
|
|
1634
|
+
function resolveSchemas(schemas) {
|
|
1635
|
+
const resolved = {};
|
|
1636
|
+
for (const [key, schema] of Object.entries(schemas)) try {
|
|
1637
|
+
resolved[key] = resolveSchema(schema);
|
|
1638
|
+
} catch (error) {
|
|
1639
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1640
|
+
throw new Error(`Failed to resolve schema for environment variable "${key}": ${message}`);
|
|
1641
|
+
}
|
|
1642
|
+
return resolved;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
//#endregion
|
|
1646
|
+
//#region ../models/src/EnvModel.ts
|
|
1647
|
+
/**
|
|
1648
|
+
* Environment variable specification container
|
|
1649
|
+
* Encapsulates schemas and preprocessor configuration
|
|
1650
|
+
*/
|
|
1651
|
+
var EnvModel = class {
|
|
1652
|
+
/**
|
|
1653
|
+
* Resolved environment variable schemas
|
|
1654
|
+
*/
|
|
1655
|
+
schemas;
|
|
1656
|
+
/**
|
|
1657
|
+
* Preprocessing configuration for environment variable types
|
|
1658
|
+
*/
|
|
1659
|
+
preprocess;
|
|
1660
|
+
/**
|
|
1661
|
+
* Create an EnvModel instance from options
|
|
1662
|
+
* @param options - Specification options
|
|
1663
|
+
*/
|
|
1664
|
+
constructor(options) {
|
|
1665
|
+
this.schemas = resolveSchemas(options.schemas);
|
|
1666
|
+
this.preprocess = this.resolvePreprocessors(options.preprocess);
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Resolve preprocessor configuration from options
|
|
1670
|
+
* @param config - Preprocessor configuration (true, false, or partial object)
|
|
1671
|
+
* @returns Resolved preprocessors object
|
|
1672
|
+
*/
|
|
1673
|
+
resolvePreprocessors(config) {
|
|
1674
|
+
if (config === false) return {
|
|
1675
|
+
string: false,
|
|
1676
|
+
number: false,
|
|
1677
|
+
boolean: false,
|
|
1678
|
+
enum: false
|
|
1679
|
+
};
|
|
1680
|
+
if (config === true || config === void 0) return {};
|
|
1681
|
+
return { ...config };
|
|
1682
|
+
}
|
|
1683
|
+
};
|
|
1684
|
+
|
|
1685
|
+
//#endregion
|
|
1686
|
+
//#region ../models/src/model.ts
|
|
1687
|
+
/**
|
|
1688
|
+
* Create an environment variable model
|
|
1689
|
+
*
|
|
1690
|
+
* Sugar utility for creating EnvModel instances with full type inference
|
|
1691
|
+
*
|
|
1692
|
+
* @param options - Model options
|
|
1693
|
+
* @returns EnvModel instance with inferred types
|
|
1694
|
+
*
|
|
1695
|
+
* @example
|
|
1696
|
+
* ```typescript
|
|
1697
|
+
* import { define, s } from "@e-n-v/models";
|
|
1698
|
+
*
|
|
1699
|
+
* export default define({
|
|
1700
|
+
* schemas: {
|
|
1701
|
+
* PORT: s.number({ default: 3000 }),
|
|
1702
|
+
* DATABASE_URL: s.string(),
|
|
1703
|
+
* DEBUG: s.boolean({ default: false })
|
|
1704
|
+
* },
|
|
1705
|
+
* preprocess: true
|
|
1706
|
+
* });
|
|
1707
|
+
* ```
|
|
1708
|
+
*/
|
|
1709
|
+
function define(options) {
|
|
1710
|
+
return new EnvModel(options);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
//#endregion
|
|
1714
|
+
//#region ../prompt/src/options/defaults.ts
|
|
1715
|
+
var defaults_exports = /* @__PURE__ */ __export({
|
|
1716
|
+
ENV_PATH: () => ENV_PATH,
|
|
1717
|
+
SECRET_PATTERNS: () => SECRET_PATTERNS,
|
|
1718
|
+
TRUNCATE_LENGTH: () => TRUNCATE_LENGTH
|
|
1719
|
+
});
|
|
1720
|
+
const ENV_PATH = ".env";
|
|
1721
|
+
const TRUNCATE_LENGTH = 40;
|
|
1722
|
+
const SECRET_PATTERNS = Object.freeze([
|
|
1723
|
+
/password/i,
|
|
1724
|
+
/passphrase/i,
|
|
1725
|
+
/secret/i,
|
|
1726
|
+
/token/i,
|
|
1727
|
+
/api[_-]?key/i,
|
|
1728
|
+
/client[_-]?secret/i,
|
|
1729
|
+
/private[_-]?key/i,
|
|
1730
|
+
/(?:database|db|redis|mongo|sql|postgres|mysql)[_-]?(?:connection[_-]?)?(?:string|url)/i,
|
|
1731
|
+
/database(?:[_-]?url)?/i,
|
|
1732
|
+
/access[_-]?key/i,
|
|
1733
|
+
/credential/i
|
|
1734
|
+
]);
|
|
1735
|
+
|
|
1736
|
+
//#endregion
|
|
1737
|
+
//#region ../prompt/src/visuals/Theme.ts
|
|
1738
|
+
var Theme = class Theme {
|
|
1739
|
+
static default = new Theme(color.magenta);
|
|
1740
|
+
primary;
|
|
1741
|
+
bgPrimary;
|
|
1742
|
+
subtle;
|
|
1743
|
+
warn;
|
|
1744
|
+
bgWarn;
|
|
1745
|
+
error;
|
|
1746
|
+
bgError;
|
|
1747
|
+
constructor(main) {
|
|
1748
|
+
this.primary = main;
|
|
1749
|
+
this.bgPrimary = this.getBackgroundColor(main);
|
|
1750
|
+
this.subtle = color.gray;
|
|
1751
|
+
this.warn = color.yellow;
|
|
1752
|
+
this.bgWarn = this.getBackgroundColor(color.yellow);
|
|
1753
|
+
this.error = color.red;
|
|
1754
|
+
this.bgError = this.getBackgroundColor(color.red);
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Get the background version of a color function
|
|
1758
|
+
*/
|
|
1759
|
+
getBackgroundColor(colorFn) {
|
|
1760
|
+
if (colorFn === color.black) return color.bgBlack;
|
|
1761
|
+
if (colorFn === color.red) return color.bgRed;
|
|
1762
|
+
if (colorFn === color.green) return color.bgGreen;
|
|
1763
|
+
if (colorFn === color.yellow) return color.bgYellow;
|
|
1764
|
+
if (colorFn === color.blue) return color.bgBlue;
|
|
1765
|
+
if (colorFn === color.magenta) return color.bgMagenta;
|
|
1766
|
+
if (colorFn === color.cyan) return color.bgCyan;
|
|
1767
|
+
if (colorFn === color.white) return color.bgWhite;
|
|
1768
|
+
if (colorFn === color.redBright) return color.bgRedBright;
|
|
1769
|
+
if (colorFn === color.greenBright) return color.bgGreenBright;
|
|
1770
|
+
if (colorFn === color.yellowBright) return color.bgYellowBright;
|
|
1771
|
+
if (colorFn === color.blueBright) return color.bgBlueBright;
|
|
1772
|
+
if (colorFn === color.magentaBright) return color.bgMagentaBright;
|
|
1773
|
+
if (colorFn === color.cyanBright) return color.bgCyanBright;
|
|
1774
|
+
if (colorFn === color.whiteBright) return color.bgWhiteBright;
|
|
1775
|
+
return color.bgBlack;
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
|
|
1779
|
+
//#endregion
|
|
1780
|
+
//#region ../prompt/src/visuals/symbols.ts
|
|
1781
|
+
const S_BAR = "│";
|
|
1782
|
+
const S_BAR_END = "└";
|
|
1783
|
+
const S_BAR_START = "┌";
|
|
1784
|
+
const S_STEP_ACTIVE = "◆";
|
|
1785
|
+
const S_STEP_SUBMIT = "✔";
|
|
1786
|
+
const S_STEP_SKIP = "⏭";
|
|
1787
|
+
const S_STEP_CANCEL = "✕";
|
|
1788
|
+
const S_STEP_ERROR = "▲";
|
|
1789
|
+
const S_STEP_PREVIOUS = "⏮";
|
|
1790
|
+
const S_TOOL_ACTIVE = "■";
|
|
1791
|
+
const S_TOOL_INACTIVE = "□";
|
|
1792
|
+
const S_RADIO_ACTIVE = "●";
|
|
1793
|
+
const S_RADIO_INACTIVE = "○";
|
|
1794
|
+
const S_CURSOR = "█";
|
|
1795
|
+
const SECRET_MASK = "•";
|
|
1796
|
+
|
|
1797
|
+
//#endregion
|
|
1798
|
+
//#region ../prompt/src/prompts/ThemedPrompt.ts
|
|
1799
|
+
var ThemedPrompt = class extends Prompt {
|
|
1800
|
+
theme;
|
|
1801
|
+
constructor(opts, trackValue = false) {
|
|
1802
|
+
super(opts, trackValue);
|
|
1803
|
+
this.theme = opts.theme || Theme.default;
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Get the appropriate symbol based on prompt state
|
|
1807
|
+
*/
|
|
1808
|
+
getSymbol() {
|
|
1809
|
+
switch (this.state) {
|
|
1810
|
+
case "initial":
|
|
1811
|
+
case "active": return this.theme.primary(S_STEP_ACTIVE);
|
|
1812
|
+
case "error": return this.theme.warn(S_STEP_ERROR);
|
|
1813
|
+
case "submit": return this.theme.primary(S_STEP_SUBMIT);
|
|
1814
|
+
case "cancel": return this.theme.error(S_STEP_CANCEL);
|
|
1815
|
+
default: return this.theme.primary(S_STEP_ACTIVE);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
/**
|
|
1819
|
+
* Get bar color based on error state - yellow for errors, theme primary otherwise
|
|
1820
|
+
*/
|
|
1821
|
+
getBarColor(barSymbol) {
|
|
1822
|
+
return this.state === "error" ? this.theme.warn(barSymbol) : this.theme.primary(barSymbol);
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* Get bar symbols with appropriate coloring
|
|
1826
|
+
*/
|
|
1827
|
+
getBar() {
|
|
1828
|
+
return this.getBarColor(S_BAR);
|
|
1829
|
+
}
|
|
1830
|
+
getBarEnd() {
|
|
1831
|
+
return this.getBarColor(S_BAR_END);
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Color utilities for consistent theming
|
|
1835
|
+
*/
|
|
1836
|
+
get colors() {
|
|
1837
|
+
return {
|
|
1838
|
+
primary: this.theme.primary,
|
|
1839
|
+
subtle: this.theme.subtle,
|
|
1840
|
+
warn: this.theme.warn,
|
|
1841
|
+
error: this.theme.error,
|
|
1842
|
+
white: color.white,
|
|
1843
|
+
bold: color.bold,
|
|
1844
|
+
dim: color.dim,
|
|
1845
|
+
inverse: color.inverse,
|
|
1846
|
+
strikethrough: color.strikethrough
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
|
|
1851
|
+
//#endregion
|
|
1852
|
+
//#region ../prompt/src/prompts/utils/ClackPromptInternals.ts
|
|
1853
|
+
/** Hacks to access private members of Clack prompts */
|
|
1854
|
+
var ClackPromptInternals = class {
|
|
1855
|
+
prompt;
|
|
1856
|
+
constructor(prompt$1) {
|
|
1857
|
+
this.prompt = prompt$1;
|
|
1858
|
+
}
|
|
1859
|
+
set track(enabled) {
|
|
1860
|
+
this.prompt._track = enabled;
|
|
1861
|
+
}
|
|
1862
|
+
get track() {
|
|
1863
|
+
return this.prompt._track;
|
|
1864
|
+
}
|
|
1865
|
+
};
|
|
1866
|
+
|
|
1867
|
+
//#endregion
|
|
1868
|
+
//#region ../prompt/src/prompts/utils/padActiveRender.ts
|
|
1869
|
+
/**
|
|
1870
|
+
* Ensures active prompts leave a blank line beneath their output while leaving
|
|
1871
|
+
* submitted or cancelled renders untouched.
|
|
1872
|
+
*/
|
|
1873
|
+
function padActiveRender(render) {
|
|
1874
|
+
return function paddedRender() {
|
|
1875
|
+
const result = render.call(this);
|
|
1876
|
+
if (typeof result !== "string") return result;
|
|
1877
|
+
if (this.state === "submit" || this.state === "cancel") return result;
|
|
1878
|
+
const normalized = result.replace(/\r\n/g, "\n");
|
|
1879
|
+
if (!normalized) return "\n";
|
|
1880
|
+
if (normalized.endsWith("\n")) return result;
|
|
1881
|
+
return result + "\n";
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
//#endregion
|
|
1886
|
+
//#region ../prompt/src/prompts/toolbar/Toolbar.ts
|
|
1887
|
+
var Toolbar = class {
|
|
1888
|
+
_isOpen = false;
|
|
1889
|
+
cursor = 0;
|
|
1890
|
+
previous;
|
|
1891
|
+
theme;
|
|
1892
|
+
actions;
|
|
1893
|
+
secret;
|
|
1894
|
+
constructor(config) {
|
|
1895
|
+
this.previous = config.previous;
|
|
1896
|
+
this.secret = config.secret;
|
|
1897
|
+
this.theme = config.theme;
|
|
1898
|
+
this.actions = config.actions;
|
|
1899
|
+
}
|
|
1900
|
+
get isOpen() {
|
|
1901
|
+
return this._isOpen;
|
|
1902
|
+
}
|
|
1903
|
+
open() {
|
|
1904
|
+
this._isOpen = true;
|
|
1905
|
+
this.cursor = 0;
|
|
1906
|
+
}
|
|
1907
|
+
close() {
|
|
1908
|
+
this._isOpen = false;
|
|
1909
|
+
this.cursor = 0;
|
|
1910
|
+
}
|
|
1911
|
+
handleKey(char, info) {
|
|
1912
|
+
if (!info) return false;
|
|
1913
|
+
if (info.name === "tab") {
|
|
1914
|
+
if (!this._isOpen) this.open();
|
|
1915
|
+
else this.close();
|
|
1916
|
+
return true;
|
|
1917
|
+
}
|
|
1918
|
+
if (!this._isOpen) return false;
|
|
1919
|
+
switch (info.name) {
|
|
1920
|
+
case "left":
|
|
1921
|
+
case "up":
|
|
1922
|
+
this.shiftCursor(-1);
|
|
1923
|
+
return true;
|
|
1924
|
+
case "right":
|
|
1925
|
+
case "down":
|
|
1926
|
+
this.shiftCursor(1);
|
|
1927
|
+
return true;
|
|
1928
|
+
case "return":
|
|
1929
|
+
case "enter":
|
|
1930
|
+
this.activateOption();
|
|
1931
|
+
return true;
|
|
1932
|
+
}
|
|
1933
|
+
if (char && char.length === 1 && !info.ctrl && !info.meta) {
|
|
1934
|
+
this.close();
|
|
1935
|
+
return false;
|
|
1936
|
+
}
|
|
1937
|
+
return false;
|
|
1938
|
+
}
|
|
1939
|
+
render() {
|
|
1940
|
+
if (!this._isOpen) return "";
|
|
1941
|
+
const options = this.getOptions();
|
|
1942
|
+
this.ensureCursor(options);
|
|
1943
|
+
if (!options.length) return this.theme.subtle("Tab to return");
|
|
1944
|
+
const separator = this.theme.subtle(" / ");
|
|
1945
|
+
return `${options.map((option, index) => {
|
|
1946
|
+
const isFocused = index === this.cursor;
|
|
1947
|
+
const label = `${isFocused ? S_TOOL_ACTIVE : S_TOOL_INACTIVE} ${option.label}`;
|
|
1948
|
+
return isFocused ? this.theme.primary(label) : this.theme.subtle(label);
|
|
1949
|
+
}).join(separator)}${separator}${this.theme.subtle("Tab to return")}`;
|
|
1950
|
+
}
|
|
1951
|
+
shiftCursor(delta) {
|
|
1952
|
+
const options = this.getOptions();
|
|
1953
|
+
if (!options.length) return;
|
|
1954
|
+
let nextIndex = this.cursor;
|
|
1955
|
+
const len = options.length;
|
|
1956
|
+
for (let step = 0; step < len; step++) {
|
|
1957
|
+
nextIndex = (nextIndex + delta + len) % len;
|
|
1958
|
+
this.cursor = nextIndex;
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
activateOption() {
|
|
1963
|
+
const options = this.getOptions();
|
|
1964
|
+
this.ensureCursor(options);
|
|
1965
|
+
const selected = options[this.cursor];
|
|
1966
|
+
if (!selected) return;
|
|
1967
|
+
this.close();
|
|
1968
|
+
selected.action();
|
|
1969
|
+
}
|
|
1970
|
+
ensureCursor(options) {
|
|
1971
|
+
if (!options.length) {
|
|
1972
|
+
this.cursor = 0;
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
if (this.cursor >= options.length) this.cursor = 0;
|
|
1976
|
+
}
|
|
1977
|
+
getOptions() {
|
|
1978
|
+
const options = [{
|
|
1979
|
+
key: "skip",
|
|
1980
|
+
label: "Skip",
|
|
1981
|
+
action: this.actions.skip
|
|
1982
|
+
}];
|
|
1983
|
+
if (this.previous) options.push({
|
|
1984
|
+
key: "previous",
|
|
1985
|
+
label: "Previous",
|
|
1986
|
+
action: this.actions.previous
|
|
1987
|
+
});
|
|
1988
|
+
if (this.secret !== false) options.push({
|
|
1989
|
+
key: "toggleSecret",
|
|
1990
|
+
label: this.secret === "shown" ? "Hide" : "Show",
|
|
1991
|
+
action: this.actions.toggleSecret
|
|
1992
|
+
});
|
|
1993
|
+
return options;
|
|
1994
|
+
}
|
|
1995
|
+
};
|
|
1996
|
+
|
|
1997
|
+
//#endregion
|
|
1998
|
+
//#region ../prompt/src/prompts/state/EnvPromptModeDetails.ts
|
|
1999
|
+
/**
|
|
2000
|
+
* Creates initial mode details for a prompt
|
|
2001
|
+
*/
|
|
2002
|
+
function createInitialModeDetails(options) {
|
|
2003
|
+
return {
|
|
2004
|
+
mode: options.hasOptions ? "selection" : "typing",
|
|
2005
|
+
intention: "commit",
|
|
2006
|
+
secretVisibility: options.hasSecret ? "hidden" : "not-secret",
|
|
2007
|
+
validation: "pending",
|
|
2008
|
+
cursor: 0,
|
|
2009
|
+
inputValue: options.initialInputValue || "",
|
|
2010
|
+
toolbarOpen: false,
|
|
2011
|
+
consumeSubmit: false
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Computes derived state from the core mode details
|
|
2016
|
+
*/
|
|
2017
|
+
function computeState(modeDetails) {
|
|
2018
|
+
return {
|
|
2019
|
+
canSubmit: modeDetails.intention === "commit" && modeDetails.validation !== "invalid",
|
|
2020
|
+
shouldTrackInput: modeDetails.mode === "typing",
|
|
2021
|
+
shouldDimUI: modeDetails.toolbarOpen,
|
|
2022
|
+
shouldConsumeSubmit: modeDetails.consumeSubmit,
|
|
2023
|
+
shouldSkipValidation: modeDetails.validation === "suppressed"
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Creates complete state by combining mode details and computed properties
|
|
2028
|
+
*/
|
|
2029
|
+
function createState(modeDetails) {
|
|
2030
|
+
return {
|
|
2031
|
+
...modeDetails,
|
|
2032
|
+
...computeState(modeDetails)
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
//#endregion
|
|
2037
|
+
//#region ../prompt/src/prompts/state/EnvPromptMode.ts
|
|
2038
|
+
/**
|
|
2039
|
+
* Encapsulates the prompt's interaction mode state and transitions.
|
|
2040
|
+
*/
|
|
2041
|
+
var EnvPromptMode = class {
|
|
2042
|
+
_details;
|
|
2043
|
+
_listeners = [];
|
|
2044
|
+
constructor(initialModeDetails) {
|
|
2045
|
+
this._details = { ...initialModeDetails };
|
|
2046
|
+
}
|
|
2047
|
+
get intention() {
|
|
2048
|
+
return this._details.intention;
|
|
2049
|
+
}
|
|
2050
|
+
set intention(intention) {
|
|
2051
|
+
this.updateModeDetails({ intention });
|
|
2052
|
+
}
|
|
2053
|
+
/**
|
|
2054
|
+
* Complete computed state for observers.
|
|
2055
|
+
*/
|
|
2056
|
+
get state() {
|
|
2057
|
+
return createState(this._details);
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Snapshot of the raw mode details (without computed props).
|
|
2061
|
+
*/
|
|
2062
|
+
get modeDetails() {
|
|
2063
|
+
return { ...this._details };
|
|
2064
|
+
}
|
|
2065
|
+
subscribe(listener) {
|
|
2066
|
+
this._listeners.push(listener);
|
|
2067
|
+
return () => {
|
|
2068
|
+
const index = this._listeners.indexOf(listener);
|
|
2069
|
+
if (index >= 0) this._listeners.splice(index, 1);
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
notifyListeners() {
|
|
2073
|
+
const snapshot = this.state;
|
|
2074
|
+
this._listeners.forEach((listener) => listener(snapshot));
|
|
2075
|
+
}
|
|
2076
|
+
updateModeDetails(updates) {
|
|
2077
|
+
this._details = {
|
|
2078
|
+
...this._details,
|
|
2079
|
+
...updates
|
|
2080
|
+
};
|
|
2081
|
+
this.notifyListeners();
|
|
2082
|
+
}
|
|
2083
|
+
enterTyping(inputValue = "") {
|
|
2084
|
+
if (this._details.mode === "selection") this.updateModeDetails({
|
|
2085
|
+
mode: "typing",
|
|
2086
|
+
inputValue,
|
|
2087
|
+
validation: "pending"
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
exitTyping() {
|
|
2091
|
+
if (this._details.mode === "typing") this.updateModeDetails({
|
|
2092
|
+
mode: "selection",
|
|
2093
|
+
inputValue: "",
|
|
2094
|
+
validation: "pending"
|
|
2095
|
+
});
|
|
2096
|
+
}
|
|
2097
|
+
openToolbar() {
|
|
2098
|
+
this.updateModeDetails({ toolbarOpen: true });
|
|
2099
|
+
}
|
|
2100
|
+
closeToolbar() {
|
|
2101
|
+
this.updateModeDetails({ toolbarOpen: false });
|
|
2102
|
+
}
|
|
2103
|
+
moveCursor(direction, maxIndex) {
|
|
2104
|
+
if (this._details.mode === "selection" && !this._details.toolbarOpen) {
|
|
2105
|
+
const delta = direction === "up" ? -1 : 1;
|
|
2106
|
+
let cursor = this._details.cursor + delta;
|
|
2107
|
+
if (cursor < 0) cursor = maxIndex;
|
|
2108
|
+
else if (cursor > maxIndex) cursor = 0;
|
|
2109
|
+
this.updateModeDetails({
|
|
2110
|
+
cursor,
|
|
2111
|
+
validation: "pending"
|
|
2112
|
+
});
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
setCursor(position) {
|
|
2116
|
+
if (this._details.mode === "selection") this.updateModeDetails({
|
|
2117
|
+
cursor: position,
|
|
2118
|
+
validation: "pending"
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
setIntention(intention) {
|
|
2122
|
+
this.intention = intention;
|
|
2123
|
+
}
|
|
2124
|
+
toggleSecretVisibility() {
|
|
2125
|
+
if (this._details.secretVisibility !== "not-secret") {
|
|
2126
|
+
const next = this._details.secretVisibility === "hidden" ? "revealed" : "hidden";
|
|
2127
|
+
this.updateModeDetails({
|
|
2128
|
+
secretVisibility: next,
|
|
2129
|
+
validation: "suppressed",
|
|
2130
|
+
consumeSubmit: true
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
resetSecretVisibility() {
|
|
2135
|
+
if (this._details.secretVisibility === "revealed") this.updateModeDetails({ secretVisibility: "hidden" });
|
|
2136
|
+
}
|
|
2137
|
+
setValidationState(validation) {
|
|
2138
|
+
this.updateModeDetails({ validation });
|
|
2139
|
+
}
|
|
2140
|
+
suppressValidation() {
|
|
2141
|
+
this.updateModeDetails({
|
|
2142
|
+
validation: "suppressed",
|
|
2143
|
+
consumeSubmit: true
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
restoreValidation() {
|
|
2147
|
+
if (this._details.validation === "suppressed") this.updateModeDetails({ validation: "pending" });
|
|
2148
|
+
}
|
|
2149
|
+
clearConsumeSubmit() {
|
|
2150
|
+
if (this._details.consumeSubmit) this.updateModeDetails({ consumeSubmit: false });
|
|
2151
|
+
}
|
|
2152
|
+
updateInput(value) {
|
|
2153
|
+
if (this._details.mode === "typing") this.updateModeDetails({
|
|
2154
|
+
inputValue: value,
|
|
2155
|
+
validation: "pending"
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
clearInput() {
|
|
2159
|
+
this.updateModeDetails({
|
|
2160
|
+
inputValue: "",
|
|
2161
|
+
validation: "pending"
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
isInInteraction(kind) {
|
|
2165
|
+
return this._details.mode === kind;
|
|
2166
|
+
}
|
|
2167
|
+
canTransitionToInteraction(kind) {
|
|
2168
|
+
switch (kind) {
|
|
2169
|
+
case "typing": return this._details.mode === "selection" && !this._details.toolbarOpen;
|
|
2170
|
+
case "selection": return this._details.mode === "typing";
|
|
2171
|
+
case "toolbar": return !this._details.toolbarOpen;
|
|
2172
|
+
default: return false;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
shouldPreventSubmission() {
|
|
2176
|
+
const snapshot = this.state;
|
|
2177
|
+
return !snapshot.canSubmit || snapshot.shouldConsumeSubmit;
|
|
2178
|
+
}
|
|
2179
|
+
isToolbarOpen() {
|
|
2180
|
+
return this._details.toolbarOpen;
|
|
2181
|
+
}
|
|
2182
|
+
isSecretRevealed() {
|
|
2183
|
+
return this._details.secretVisibility === "revealed";
|
|
2184
|
+
}
|
|
2185
|
+
hasSecret() {
|
|
2186
|
+
return this._details.secretVisibility !== "not-secret";
|
|
2187
|
+
}
|
|
2188
|
+
getCursor() {
|
|
2189
|
+
return this._details.cursor;
|
|
2190
|
+
}
|
|
2191
|
+
getInputValue() {
|
|
2192
|
+
return this._details.inputValue;
|
|
2193
|
+
}
|
|
2194
|
+
getIntention() {
|
|
2195
|
+
return this.intention;
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
|
|
2199
|
+
//#endregion
|
|
2200
|
+
//#region ../prompt/src/prompts/processing/processValue.ts
|
|
2201
|
+
/**
|
|
2202
|
+
* Process a value through schema validation and return detailed results
|
|
2203
|
+
*/
|
|
2204
|
+
function processValue(envKey, value, schema, preprocess) {
|
|
2205
|
+
try {
|
|
2206
|
+
if (!value || value.trim() === "") return {
|
|
2207
|
+
value: void 0,
|
|
2208
|
+
rawValue: value,
|
|
2209
|
+
isValid: true
|
|
2210
|
+
};
|
|
2211
|
+
const activePreprocessor = (() => {
|
|
2212
|
+
if (preprocess === false) return;
|
|
2213
|
+
if (preprocess === true) return resolvePreprocessor(schema.type, { [schema.type]: true });
|
|
2214
|
+
if (typeof preprocess === "function") return preprocess;
|
|
2215
|
+
return resolvePreprocessor(schema.type);
|
|
2216
|
+
})();
|
|
2217
|
+
const processedValue = activePreprocessor ? activePreprocessor(value) : value;
|
|
2218
|
+
if (schema.type === "boolean" && typeof processedValue === "boolean") return {
|
|
2219
|
+
value: processedValue,
|
|
2220
|
+
rawValue: value,
|
|
2221
|
+
isValid: true
|
|
2222
|
+
};
|
|
2223
|
+
if (schema.type === "number" && typeof processedValue === "number") return {
|
|
2224
|
+
value: processedValue,
|
|
2225
|
+
rawValue: value,
|
|
2226
|
+
isValid: true
|
|
2227
|
+
};
|
|
2228
|
+
if (typeof processedValue === "string") return {
|
|
2229
|
+
value: schema.process(processedValue),
|
|
2230
|
+
rawValue: value,
|
|
2231
|
+
isValid: true
|
|
2232
|
+
};
|
|
2233
|
+
return {
|
|
2234
|
+
value: schema.process(value),
|
|
2235
|
+
rawValue: value,
|
|
2236
|
+
isValid: true
|
|
2237
|
+
};
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
return {
|
|
2240
|
+
value: void 0,
|
|
2241
|
+
rawValue: value,
|
|
2242
|
+
isValid: false,
|
|
2243
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
//#endregion
|
|
2249
|
+
//#region ../prompt/src/prompts/EnvPrompt.ts
|
|
2250
|
+
var EnvPrompt = class extends ThemedPrompt {
|
|
2251
|
+
/** Schema describing the environment variable. */
|
|
2252
|
+
schema;
|
|
2253
|
+
/** Flag indicating whether the variable must be provided. */
|
|
2254
|
+
required;
|
|
2255
|
+
/** Environment variable key, e.g. `NODE_ENV`. */
|
|
2256
|
+
key;
|
|
2257
|
+
/** Optional preprocessing applied before schema validation. */
|
|
2258
|
+
preprocess;
|
|
2259
|
+
/** Raw string value read from the environment. */
|
|
2260
|
+
rawCurrent;
|
|
2261
|
+
/** Result of preprocessing/validation for the raw current value. */
|
|
2262
|
+
currentResult;
|
|
2263
|
+
/** Latest committed value for this prompt, if any. */
|
|
2264
|
+
current;
|
|
2265
|
+
/** Fallback value supplied by the schema. */
|
|
2266
|
+
default;
|
|
2267
|
+
/** Maximum number of characters to display before truncation. */
|
|
2268
|
+
truncate;
|
|
2269
|
+
/** Zero-based index of this prompt in the session. */
|
|
2270
|
+
index;
|
|
2271
|
+
/** Total number of prompts in the session. */
|
|
2272
|
+
total;
|
|
2273
|
+
/** Indicates whether the value should be treated as secret. */
|
|
2274
|
+
secret;
|
|
2275
|
+
/** Internal helpers for managing clack prompt behavior. */
|
|
2276
|
+
internals;
|
|
2277
|
+
/** Toolbar instance controlling auxiliary actions. */
|
|
2278
|
+
toolbar;
|
|
2279
|
+
/** State machine managing prompt interaction modes. */
|
|
2280
|
+
mode;
|
|
2281
|
+
constructor(schema, opts) {
|
|
2282
|
+
const { pad = true,...promptOpts } = opts;
|
|
2283
|
+
const promptOptions = {
|
|
2284
|
+
...promptOpts,
|
|
2285
|
+
default: schema.default,
|
|
2286
|
+
render: pad ? padActiveRender(promptOpts.render) : promptOpts.render
|
|
2287
|
+
};
|
|
2288
|
+
super(promptOptions);
|
|
2289
|
+
this.schema = schema;
|
|
2290
|
+
this.internals = new ClackPromptInternals(this);
|
|
2291
|
+
this.required = schema.required;
|
|
2292
|
+
this.internals.track = false;
|
|
2293
|
+
this.key = promptOptions.key;
|
|
2294
|
+
this.preprocess = promptOptions.preprocess;
|
|
2295
|
+
this.rawCurrent = promptOptions.current;
|
|
2296
|
+
if (this.rawCurrent !== void 0) {
|
|
2297
|
+
const result = processValue(this.key, this.rawCurrent, this.schema, this.preprocess ?? void 0);
|
|
2298
|
+
this.currentResult = result;
|
|
2299
|
+
this.current = result.isValid ? result.value : void 0;
|
|
2300
|
+
} else {
|
|
2301
|
+
this.currentResult = void 0;
|
|
2302
|
+
this.current = void 0;
|
|
2303
|
+
}
|
|
2304
|
+
this.default = schema.default;
|
|
2305
|
+
this.truncate = promptOptions.truncate ?? 40;
|
|
2306
|
+
this.index = promptOptions.index ?? 0;
|
|
2307
|
+
this.total = promptOptions.total ?? 1;
|
|
2308
|
+
const hasSecret = Boolean(promptOptions.secret);
|
|
2309
|
+
const hasOptions = this.rawCurrent !== void 0 || this.default !== void 0;
|
|
2310
|
+
this.secret = hasSecret;
|
|
2311
|
+
this.mode = new EnvPromptMode(createInitialModeDetails({
|
|
2312
|
+
hasSecret,
|
|
2313
|
+
hasOptions,
|
|
2314
|
+
initialInputValue: ""
|
|
2315
|
+
}));
|
|
2316
|
+
this.mode.subscribe((state) => {
|
|
2317
|
+
this.internals.track = state.shouldTrackInput;
|
|
2318
|
+
if (this.mode.hasSecret()) this.toolbar.secret = state.secretVisibility === "revealed" ? "shown" : "hidden";
|
|
2319
|
+
});
|
|
2320
|
+
this.toolbar = new Toolbar({
|
|
2321
|
+
previous: this.index > 0,
|
|
2322
|
+
secret: hasSecret ? this.mode.isSecretRevealed() ? "shown" : "hidden" : false,
|
|
2323
|
+
theme: this.theme,
|
|
2324
|
+
actions: {
|
|
2325
|
+
toggleSecret: () => this.handleToggleSecret(),
|
|
2326
|
+
skip: () => this.handleSkip(),
|
|
2327
|
+
previous: () => this.handlePrevious()
|
|
2328
|
+
}
|
|
2329
|
+
});
|
|
2330
|
+
this.on("finalize", () => {
|
|
2331
|
+
const shouldConsumeSubmit = this.mode.state.shouldConsumeSubmit;
|
|
2332
|
+
if (!shouldConsumeSubmit) this.mode.resetSecretVisibility();
|
|
2333
|
+
this.toolbar.close();
|
|
2334
|
+
if (shouldConsumeSubmit) {
|
|
2335
|
+
this.state = "active";
|
|
2336
|
+
this.error = "";
|
|
2337
|
+
this.mode.restoreValidation();
|
|
2338
|
+
this.mode.clearConsumeSubmit();
|
|
2339
|
+
this.mode.intention = "commit";
|
|
2340
|
+
}
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
runSchemaValidation(value) {
|
|
2344
|
+
if (!value || value.trim() === "") return {
|
|
2345
|
+
success: true,
|
|
2346
|
+
value: void 0
|
|
2347
|
+
};
|
|
2348
|
+
const result = processValue(this.key, value, this.schema, this.preprocess ?? void 0);
|
|
2349
|
+
if (result.isValid) return {
|
|
2350
|
+
success: true,
|
|
2351
|
+
value: result.value
|
|
2352
|
+
};
|
|
2353
|
+
return {
|
|
2354
|
+
success: false,
|
|
2355
|
+
error: result.error ?? "Invalid value"
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
setCommittedValue(value) {
|
|
2359
|
+
this.mode.intention = "commit";
|
|
2360
|
+
this.value = value;
|
|
2361
|
+
}
|
|
2362
|
+
getOutcome() {
|
|
2363
|
+
return this.mode.intention;
|
|
2364
|
+
}
|
|
2365
|
+
buildSkipHint(base) {
|
|
2366
|
+
const skipLower = "tab for options";
|
|
2367
|
+
const skipCapitalized = "Tab for options";
|
|
2368
|
+
if (base && base.trim().length > 0) return `${base} or ${skipLower}`;
|
|
2369
|
+
return skipCapitalized;
|
|
2370
|
+
}
|
|
2371
|
+
renderFooter(baseHint) {
|
|
2372
|
+
const errorFooter = this.renderFooterError();
|
|
2373
|
+
if (errorFooter) return errorFooter;
|
|
2374
|
+
const toolbarFooter = this.renderFooterToolbar();
|
|
2375
|
+
if (toolbarFooter) return toolbarFooter;
|
|
2376
|
+
return this.renderFooterHint(baseHint);
|
|
2377
|
+
}
|
|
2378
|
+
renderFooterError() {
|
|
2379
|
+
if (!this.error) return;
|
|
2380
|
+
return this.colors.warn(this.error);
|
|
2381
|
+
}
|
|
2382
|
+
renderFooterToolbar() {
|
|
2383
|
+
if (this.mode.hasSecret()) this.toolbar.secret = this.mode.isSecretRevealed() ? "shown" : "hidden";
|
|
2384
|
+
const toolbarOutput = this.toolbar.render();
|
|
2385
|
+
if (!toolbarOutput) return;
|
|
2386
|
+
return toolbarOutput;
|
|
2387
|
+
}
|
|
2388
|
+
renderFooterHint(baseHint) {
|
|
2389
|
+
const hint = this.buildSkipHint(baseHint);
|
|
2390
|
+
return this.colors.subtle(hint);
|
|
2391
|
+
}
|
|
2392
|
+
getFooterState() {
|
|
2393
|
+
if (this.error) return "warn";
|
|
2394
|
+
if (this.mode.isToolbarOpen()) return "tools";
|
|
2395
|
+
return "hint";
|
|
2396
|
+
}
|
|
2397
|
+
handleToolbarKey(char, info) {
|
|
2398
|
+
const handled = this.toolbar.handleKey(char, info);
|
|
2399
|
+
if (handled && info?.name === "tab") {
|
|
2400
|
+
if (this.toolbar.isOpen) this.mode.openToolbar();
|
|
2401
|
+
else this.mode.closeToolbar();
|
|
2402
|
+
return true;
|
|
2403
|
+
}
|
|
2404
|
+
if (handled && (info?.name === "return" || info?.name === "enter")) {
|
|
2405
|
+
if (this.toolbar.isOpen) this.mode.suppressValidation();
|
|
2406
|
+
if (!this.toolbar.isOpen && this.mode.isToolbarOpen()) this.mode.closeToolbar();
|
|
2407
|
+
return true;
|
|
2408
|
+
}
|
|
2409
|
+
if (handled && info?.name === "escape") {
|
|
2410
|
+
this.mode.closeToolbar();
|
|
2411
|
+
return true;
|
|
2412
|
+
}
|
|
2413
|
+
if (handled && info?.name === "backspace") {
|
|
2414
|
+
this.mode.closeToolbar();
|
|
2415
|
+
return false;
|
|
2416
|
+
}
|
|
2417
|
+
if (handled && char && char.length === 1 && !info?.ctrl && !info?.meta) {
|
|
2418
|
+
this.mode.closeToolbar();
|
|
2419
|
+
return false;
|
|
2420
|
+
}
|
|
2421
|
+
if (handled && !this.toolbar.isOpen && this.mode.isToolbarOpen()) this.mode.closeToolbar();
|
|
2422
|
+
return handled;
|
|
2423
|
+
}
|
|
2424
|
+
truncateValue(value) {
|
|
2425
|
+
if (value.length <= this.truncate) return value;
|
|
2426
|
+
return `${value.substring(0, this.truncate)}...`;
|
|
2427
|
+
}
|
|
2428
|
+
renderSkipped() {
|
|
2429
|
+
return `${this.colors.dim(this.theme.primary(S_STEP_SKIP))} ${this.colors.subtle(this.colors.bold(this.key))}`;
|
|
2430
|
+
}
|
|
2431
|
+
renderPrevious() {
|
|
2432
|
+
return `${this.colors.dim(this.theme.primary(S_STEP_PREVIOUS))} ${this.colors.subtle(this.colors.bold(this.key))}`;
|
|
2433
|
+
}
|
|
2434
|
+
renderOutcomeResult() {
|
|
2435
|
+
const intention = this.mode.intention;
|
|
2436
|
+
if (intention === "skip") return this.renderSkipped();
|
|
2437
|
+
if (intention === "previous") return this.renderPrevious();
|
|
2438
|
+
}
|
|
2439
|
+
renderCancelled() {
|
|
2440
|
+
return `${this.theme.error(S_STEP_CANCEL)} ${this.colors.subtle(this.colors.bold(this.key))}`;
|
|
2441
|
+
}
|
|
2442
|
+
toggleSecretReveal() {
|
|
2443
|
+
if (!this.mode.hasSecret()) return;
|
|
2444
|
+
this.mode.toggleSecretVisibility();
|
|
2445
|
+
if (this.mode.hasSecret()) this.toolbar.secret = this.mode.isSecretRevealed() ? "shown" : "hidden";
|
|
2446
|
+
}
|
|
2447
|
+
resetSecretReveal() {
|
|
2448
|
+
if (!this.mode.hasSecret()) return;
|
|
2449
|
+
this.mode.resetSecretVisibility();
|
|
2450
|
+
}
|
|
2451
|
+
isSecretRevealed() {
|
|
2452
|
+
return this.mode.isSecretRevealed();
|
|
2453
|
+
}
|
|
2454
|
+
handleToggleSecret() {
|
|
2455
|
+
this.toggleSecretReveal();
|
|
2456
|
+
}
|
|
2457
|
+
handleSkip() {
|
|
2458
|
+
this.mode.intention = "skip";
|
|
2459
|
+
this.value = void 0;
|
|
2460
|
+
this.state = "submit";
|
|
2461
|
+
}
|
|
2462
|
+
handlePrevious() {
|
|
2463
|
+
this.mode.intention = "previous";
|
|
2464
|
+
this.value = void 0;
|
|
2465
|
+
this.state = "submit";
|
|
2466
|
+
}
|
|
2467
|
+
consumeSkipValidation() {
|
|
2468
|
+
if (this.mode.state.shouldSkipValidation) {
|
|
2469
|
+
this.mode.restoreValidation();
|
|
2470
|
+
return true;
|
|
2471
|
+
}
|
|
2472
|
+
return false;
|
|
2473
|
+
}
|
|
2474
|
+
getAnnotationLabel(value) {
|
|
2475
|
+
const matchesCurrent = Boolean(this.currentResult?.isValid) && this.current === value;
|
|
2476
|
+
const matchesDefault = this.default === value;
|
|
2477
|
+
return this.buildAnnotation({
|
|
2478
|
+
isCurrent: matchesCurrent,
|
|
2479
|
+
isDefault: matchesDefault
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Clears any active validation error so the prompt can return to the default hint state.
|
|
2484
|
+
*/
|
|
2485
|
+
clearErrorState() {
|
|
2486
|
+
if (this.state === "error" || this.error) {
|
|
2487
|
+
this.state = "active";
|
|
2488
|
+
this.error = "";
|
|
2489
|
+
this.mode.restoreValidation();
|
|
2490
|
+
this.mode.clearConsumeSubmit();
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
buildAnnotation(flags) {
|
|
2494
|
+
const labels = [];
|
|
2495
|
+
if (flags.isCurrent) labels.push("current");
|
|
2496
|
+
if (flags.isDefault) labels.push("default");
|
|
2497
|
+
if (flags.invalid) labels.push("invalid");
|
|
2498
|
+
return labels.length > 0 ? labels.join(", ") : void 0;
|
|
2499
|
+
}
|
|
2500
|
+
};
|
|
2501
|
+
|
|
2502
|
+
//#endregion
|
|
2503
|
+
//#region ../prompt/src/prompts/utils/selectableOptions.ts
|
|
2504
|
+
function buildSelectableOptions(params) {
|
|
2505
|
+
const invalidOptionKey = params.invalidOptionKey ?? "invalid";
|
|
2506
|
+
const baseOptions = params.baseOptions.map((option) => ({ ...option }));
|
|
2507
|
+
const currentRaw = params.currentResult?.rawValue;
|
|
2508
|
+
if (!(currentRaw !== void 0 && params.currentResult?.isValid === false)) return baseOptions;
|
|
2509
|
+
return [{
|
|
2510
|
+
key: invalidOptionKey,
|
|
2511
|
+
value: void 0,
|
|
2512
|
+
display: params.formatInvalidDisplay(currentRaw),
|
|
2513
|
+
annotation: params.buildAnnotation({
|
|
2514
|
+
isCurrent: true,
|
|
2515
|
+
invalid: true
|
|
2516
|
+
}) ?? void 0,
|
|
2517
|
+
invalid: true
|
|
2518
|
+
}, ...baseOptions];
|
|
2519
|
+
}
|
|
2520
|
+
function resolveInitialCursor(options, params) {
|
|
2521
|
+
if (!options.length) return 0;
|
|
2522
|
+
const equals = params.isEqual ?? ((left, right) => left === right);
|
|
2523
|
+
const findIndexForValue = (value) => {
|
|
2524
|
+
if (value === void 0) return -1;
|
|
2525
|
+
return options.findIndex((option) => !option.invalid && equals(option.value, value));
|
|
2526
|
+
};
|
|
2527
|
+
const currentIndex = findIndexForValue(params.currentValue);
|
|
2528
|
+
if (currentIndex >= 0) return currentIndex;
|
|
2529
|
+
const defaultIndex = findIndexForValue(params.defaultValue);
|
|
2530
|
+
if (defaultIndex >= 0) return defaultIndex;
|
|
2531
|
+
const firstValid = options.findIndex((option) => !option.invalid);
|
|
2532
|
+
return firstValid >= 0 ? firstValid : 0;
|
|
2533
|
+
}
|
|
2534
|
+
function getOptionAtCursor(options, cursor) {
|
|
2535
|
+
if (!options.length) return {
|
|
2536
|
+
option: void 0,
|
|
2537
|
+
index: 0
|
|
2538
|
+
};
|
|
2539
|
+
const index = Math.max(0, Math.min(cursor, options.length - 1));
|
|
2540
|
+
return {
|
|
2541
|
+
option: options[index],
|
|
2542
|
+
index
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
//#endregion
|
|
2547
|
+
//#region ../prompt/src/prompts/typed/EnvBooleanPrompt.ts
|
|
2548
|
+
var EnvBooleanPrompt = class extends EnvPrompt {
|
|
2549
|
+
cursor;
|
|
2550
|
+
constructor(schema, opts) {
|
|
2551
|
+
super(schema, {
|
|
2552
|
+
...opts,
|
|
2553
|
+
render: function() {
|
|
2554
|
+
if (this.state === "submit") {
|
|
2555
|
+
const outcomeResult = this.renderOutcomeResult();
|
|
2556
|
+
if (outcomeResult) return outcomeResult;
|
|
2557
|
+
const valueStr = this.value ? "true" : "false";
|
|
2558
|
+
return `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}${this.colors.subtle("=")}${this.colors.white(this.truncateValue(valueStr))}`;
|
|
2559
|
+
}
|
|
2560
|
+
if (this.state === "cancel") return this.renderCancelled();
|
|
2561
|
+
let output = "";
|
|
2562
|
+
output += `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}`;
|
|
2563
|
+
if (this.schema.description) output += ` ${this.colors.subtle(this.schema.description)}`;
|
|
2564
|
+
output += "\n";
|
|
2565
|
+
const options$1 = this.buildOptions();
|
|
2566
|
+
const dimInputs = !this.error && this.mode.isToolbarOpen();
|
|
2567
|
+
options$1.forEach((option, index) => {
|
|
2568
|
+
const isSelected = index === this.cursor;
|
|
2569
|
+
const circle = dimInputs ? this.colors.dim(isSelected ? S_RADIO_ACTIVE : S_RADIO_INACTIVE) : isSelected ? this.theme.primary(S_RADIO_ACTIVE) : this.colors.dim(S_RADIO_INACTIVE);
|
|
2570
|
+
const formatText = (value) => {
|
|
2571
|
+
if (dimInputs) return this.colors.dim(value);
|
|
2572
|
+
if (isSelected) return this.colors.white(value);
|
|
2573
|
+
return this.colors.subtle(value);
|
|
2574
|
+
};
|
|
2575
|
+
let text = formatText(option.display);
|
|
2576
|
+
if (option.invalid) text = `\u001b[9m${text}\u001b[29m`;
|
|
2577
|
+
const annotation = option.annotation ? ` (${option.annotation})` : "";
|
|
2578
|
+
let suffix = "";
|
|
2579
|
+
if (annotation && isSelected) suffix = dimInputs ? this.colors.dim(annotation) : this.colors.subtle(annotation);
|
|
2580
|
+
output += `${this.getBar()} ${circle} ${text}${suffix}\n`;
|
|
2581
|
+
});
|
|
2582
|
+
output += `${this.getBarEnd()} ${this.renderFooter()}`;
|
|
2583
|
+
return output;
|
|
2584
|
+
},
|
|
2585
|
+
validate: (_value) => {
|
|
2586
|
+
if (this.consumeSkipValidation()) return;
|
|
2587
|
+
if (this.getOutcome() !== "commit") return;
|
|
2588
|
+
const selectedOption = this.getSelectedOption();
|
|
2589
|
+
if (selectedOption?.invalid) return this.currentResult?.error ?? "Current value is invalid";
|
|
2590
|
+
const candidateValue = selectedOption && selectedOption.value !== void 0 ? selectedOption.value : this.default ?? false;
|
|
2591
|
+
const validation = this.runSchemaValidation(candidateValue.toString());
|
|
2592
|
+
if (!validation.success) return validation.error;
|
|
2593
|
+
}
|
|
2594
|
+
});
|
|
2595
|
+
const options = this.buildOptions();
|
|
2596
|
+
this.cursor = this.getInitialCursor(options);
|
|
2597
|
+
this.updateValue();
|
|
2598
|
+
this.on("cursor", (action) => {
|
|
2599
|
+
this.clearErrorState();
|
|
2600
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
2601
|
+
const options$1 = this.buildOptions();
|
|
2602
|
+
if (!options$1.length) return;
|
|
2603
|
+
const lastIndex = options$1.length - 1;
|
|
2604
|
+
switch (action) {
|
|
2605
|
+
case "up":
|
|
2606
|
+
this.cursor = this.cursor === 0 ? lastIndex : this.cursor - 1;
|
|
2607
|
+
break;
|
|
2608
|
+
case "down":
|
|
2609
|
+
this.cursor = this.cursor >= lastIndex ? 0 : this.cursor + 1;
|
|
2610
|
+
break;
|
|
2611
|
+
default:
|
|
2612
|
+
if (this.cursor > lastIndex) this.cursor = lastIndex;
|
|
2613
|
+
if (this.cursor < 0) this.cursor = 0;
|
|
2614
|
+
break;
|
|
2615
|
+
}
|
|
2616
|
+
this.updateValue();
|
|
2617
|
+
});
|
|
2618
|
+
this.on("key", (char, info) => {
|
|
2619
|
+
if (!info) return;
|
|
2620
|
+
this.clearErrorState();
|
|
2621
|
+
if (this.handleToolbarKey(char, info)) return;
|
|
2622
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
updateValue() {
|
|
2626
|
+
const selected = this.getSelectedOption();
|
|
2627
|
+
if (!selected || selected.invalid) {
|
|
2628
|
+
this.setCommittedValue(this.default ?? false);
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
this.setCommittedValue(selected.value ?? this.default ?? false);
|
|
2632
|
+
}
|
|
2633
|
+
buildOptions() {
|
|
2634
|
+
return buildSelectableOptions({
|
|
2635
|
+
currentResult: this.currentResult,
|
|
2636
|
+
formatInvalidDisplay: (raw) => this.truncateValue(raw ?? ""),
|
|
2637
|
+
buildAnnotation: (flags) => this.buildAnnotation(flags) ?? void 0,
|
|
2638
|
+
baseOptions: [{
|
|
2639
|
+
key: "true",
|
|
2640
|
+
value: true,
|
|
2641
|
+
display: "true",
|
|
2642
|
+
annotation: this.getAnnotationLabel(true) ?? void 0
|
|
2643
|
+
}, {
|
|
2644
|
+
key: "false",
|
|
2645
|
+
value: false,
|
|
2646
|
+
display: "false",
|
|
2647
|
+
annotation: this.getAnnotationLabel(false) ?? void 0
|
|
2648
|
+
}]
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
getInitialCursor(options) {
|
|
2652
|
+
return resolveInitialCursor(options, {
|
|
2653
|
+
currentValue: this.currentResult?.isValid !== false ? this.current : void 0,
|
|
2654
|
+
defaultValue: this.default
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
getSelectedOption() {
|
|
2658
|
+
const { option, index } = getOptionAtCursor(this.buildOptions(), this.cursor);
|
|
2659
|
+
if (index !== this.cursor) this.cursor = index;
|
|
2660
|
+
return option;
|
|
2661
|
+
}
|
|
2662
|
+
};
|
|
2663
|
+
|
|
2664
|
+
//#endregion
|
|
2665
|
+
//#region ../prompt/src/prompts/typed/EnvEnumPrompt.ts
|
|
2666
|
+
var EnvEnumPrompt = class extends EnvPrompt {
|
|
2667
|
+
cursor = 0;
|
|
2668
|
+
options;
|
|
2669
|
+
values;
|
|
2670
|
+
constructor(schema, opts) {
|
|
2671
|
+
super(schema, {
|
|
2672
|
+
...opts,
|
|
2673
|
+
render: function() {
|
|
2674
|
+
if (this.state === "submit") {
|
|
2675
|
+
const outcomeResult = this.renderOutcomeResult();
|
|
2676
|
+
if (outcomeResult) return outcomeResult;
|
|
2677
|
+
return `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}${this.colors.subtle("=")}${this.colors.white(this.truncateValue(this.value ?? ""))}`;
|
|
2678
|
+
}
|
|
2679
|
+
if (this.state === "cancel") return this.renderCancelled();
|
|
2680
|
+
let output = "";
|
|
2681
|
+
output += `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}`;
|
|
2682
|
+
if (this.schema.description) output += ` ${this.colors.subtle(this.schema.description)}`;
|
|
2683
|
+
output += "\n";
|
|
2684
|
+
const options$1 = this.buildOptions();
|
|
2685
|
+
const dimInputs = !this.error && this.mode.isToolbarOpen();
|
|
2686
|
+
options$1.forEach((option, index) => {
|
|
2687
|
+
const isSelected = index === this.cursor;
|
|
2688
|
+
const circle = dimInputs ? this.colors.dim(isSelected ? S_RADIO_ACTIVE : S_RADIO_INACTIVE) : isSelected ? this.theme.primary(S_RADIO_ACTIVE) : this.colors.dim(S_RADIO_INACTIVE);
|
|
2689
|
+
const formatText = (value) => {
|
|
2690
|
+
if (dimInputs) return this.colors.dim(value);
|
|
2691
|
+
if (isSelected) return this.colors.white(value);
|
|
2692
|
+
return this.colors.subtle(value);
|
|
2693
|
+
};
|
|
2694
|
+
let text = formatText(option.display);
|
|
2695
|
+
if (option.invalid) text = `\u001b[9m${text}\u001b[29m`;
|
|
2696
|
+
const annotation = option.annotation ? ` (${option.annotation})` : "";
|
|
2697
|
+
let suffix = "";
|
|
2698
|
+
if (annotation && isSelected) suffix = dimInputs ? this.colors.dim(annotation) : this.colors.subtle(annotation);
|
|
2699
|
+
output += `${this.getBar()} ${circle} ${text}${suffix}\n`;
|
|
2700
|
+
});
|
|
2701
|
+
output += `${this.getBarEnd()} ${this.renderFooter()}`;
|
|
2702
|
+
return output;
|
|
2703
|
+
},
|
|
2704
|
+
validate: () => {
|
|
2705
|
+
if (this.consumeSkipValidation()) return;
|
|
2706
|
+
if (this.getOutcome() !== "commit") return;
|
|
2707
|
+
const selectedOption = this.getSelectedOption();
|
|
2708
|
+
if (!selectedOption) return;
|
|
2709
|
+
if (selectedOption.invalid) return this.currentResult?.error ?? "Current value is invalid";
|
|
2710
|
+
const candidateValue = selectedOption.value ?? this.default ?? this.values[0];
|
|
2711
|
+
if (candidateValue === void 0) return;
|
|
2712
|
+
const validation = this.runSchemaValidation(candidateValue);
|
|
2713
|
+
if (!validation.success) return validation.error;
|
|
2714
|
+
}
|
|
2715
|
+
});
|
|
2716
|
+
this.options = opts;
|
|
2717
|
+
this.values = [...schema.values];
|
|
2718
|
+
const options = this.buildOptions();
|
|
2719
|
+
this.cursor = this.getInitialCursor(options);
|
|
2720
|
+
this.updateValue();
|
|
2721
|
+
this.on("cursor", (action) => {
|
|
2722
|
+
this.clearErrorState();
|
|
2723
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
2724
|
+
const options$1 = this.buildOptions();
|
|
2725
|
+
if (!options$1.length) return;
|
|
2726
|
+
const lastIndex = options$1.length - 1;
|
|
2727
|
+
switch (action) {
|
|
2728
|
+
case "up":
|
|
2729
|
+
this.cursor = this.cursor === 0 ? lastIndex : this.cursor - 1;
|
|
2730
|
+
break;
|
|
2731
|
+
case "down":
|
|
2732
|
+
this.cursor = this.cursor >= lastIndex ? 0 : this.cursor + 1;
|
|
2733
|
+
break;
|
|
2734
|
+
default:
|
|
2735
|
+
if (this.cursor > lastIndex) this.cursor = lastIndex;
|
|
2736
|
+
if (this.cursor < 0) this.cursor = 0;
|
|
2737
|
+
break;
|
|
2738
|
+
}
|
|
2739
|
+
this.updateValue();
|
|
2740
|
+
});
|
|
2741
|
+
this.on("key", (char, info) => {
|
|
2742
|
+
if (!info) return;
|
|
2743
|
+
this.clearErrorState();
|
|
2744
|
+
if (this.handleToolbarKey(char, info)) return;
|
|
2745
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
updateValue() {
|
|
2749
|
+
const selected = this.getSelectedOption();
|
|
2750
|
+
if (!selected) {
|
|
2751
|
+
this.setCommittedValue(this.default ?? this.values[0]);
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2754
|
+
if (selected.invalid) {
|
|
2755
|
+
const fallback$1 = this.default ?? this.values[0];
|
|
2756
|
+
this.setCommittedValue(fallback$1);
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2759
|
+
const fallback = this.default ?? this.values[0];
|
|
2760
|
+
this.setCommittedValue(selected.value ?? fallback);
|
|
2761
|
+
}
|
|
2762
|
+
buildOptions() {
|
|
2763
|
+
return buildSelectableOptions({
|
|
2764
|
+
currentResult: this.currentResult,
|
|
2765
|
+
formatInvalidDisplay: (raw) => this.truncateValue(raw ?? ""),
|
|
2766
|
+
buildAnnotation: (flags) => this.buildAnnotation(flags) ?? void 0,
|
|
2767
|
+
baseOptions: this.values.map((value) => ({
|
|
2768
|
+
key: value,
|
|
2769
|
+
value,
|
|
2770
|
+
display: this.truncateValue(value),
|
|
2771
|
+
annotation: this.getAnnotationLabel(value) ?? void 0
|
|
2772
|
+
}))
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
getInitialCursor(options) {
|
|
2776
|
+
return resolveInitialCursor(options, {
|
|
2777
|
+
currentValue: this.currentResult?.isValid !== false ? this.current : void 0,
|
|
2778
|
+
defaultValue: this.default
|
|
2779
|
+
});
|
|
2780
|
+
}
|
|
2781
|
+
getSelectedOption() {
|
|
2782
|
+
const { option, index } = getOptionAtCursor(this.buildOptions(), this.cursor);
|
|
2783
|
+
if (index !== this.cursor) this.cursor = index;
|
|
2784
|
+
return option;
|
|
2785
|
+
}
|
|
2786
|
+
};
|
|
2787
|
+
|
|
2788
|
+
//#endregion
|
|
2789
|
+
//#region ../prompt/src/prompts/typed/EnvNumberPrompt.ts
|
|
2790
|
+
var EnvNumberPrompt = class extends EnvPrompt {
|
|
2791
|
+
otherInputCache = "";
|
|
2792
|
+
shouldStitchInput = false;
|
|
2793
|
+
isRestoringInput = false;
|
|
2794
|
+
constructor(schema, opts) {
|
|
2795
|
+
super(schema, {
|
|
2796
|
+
...opts,
|
|
2797
|
+
render: function() {
|
|
2798
|
+
if (this.state === "submit") {
|
|
2799
|
+
const outcomeResult = this.renderOutcomeResult();
|
|
2800
|
+
if (outcomeResult) return outcomeResult;
|
|
2801
|
+
return `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}${this.colors.subtle("=")}${this.colors.white(this.formatValue(this.value))}`;
|
|
2802
|
+
}
|
|
2803
|
+
if (this.state === "cancel") return this.renderCancelled();
|
|
2804
|
+
let output = "";
|
|
2805
|
+
output += `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}`;
|
|
2806
|
+
if (this.schema.description) output += ` ${this.colors.subtle(this.schema.description)}`;
|
|
2807
|
+
output += "\n";
|
|
2808
|
+
const dimInputs = !this.error && this.mode.isToolbarOpen();
|
|
2809
|
+
if (!this.hasPresetOptions()) {
|
|
2810
|
+
if (this.mode.isInInteraction("typing")) {
|
|
2811
|
+
const displayText = dimInputs ? this.colors.dim(this.getInputDisplay(false)) : this.colors.white(this.getInputDisplay(true));
|
|
2812
|
+
output += `${this.getBar()} ${displayText}`;
|
|
2813
|
+
} else {
|
|
2814
|
+
const idleDisplay = dimInputs ? this.colors.dim(this.getInputDisplay(false)) : this.colors.white(this.getInputDisplay(true));
|
|
2815
|
+
output += `${this.getBar()} ${idleDisplay}`;
|
|
2816
|
+
}
|
|
2817
|
+
output += "\n";
|
|
2818
|
+
output += `${this.getBarEnd()} ${this.renderFooter("Enter a number")}`;
|
|
2819
|
+
return output;
|
|
2820
|
+
}
|
|
2821
|
+
this.buildSelectionOptions().forEach((option, index) => {
|
|
2822
|
+
const isSelected = index === this.mode.getCursor();
|
|
2823
|
+
const circle = dimInputs ? this.colors.dim(isSelected ? S_RADIO_ACTIVE : S_RADIO_INACTIVE) : isSelected ? this.theme.primary(S_RADIO_ACTIVE) : this.colors.dim(S_RADIO_INACTIVE);
|
|
2824
|
+
if (option.type === "other") {
|
|
2825
|
+
if (this.mode.isInInteraction("typing")) {
|
|
2826
|
+
const displayText = dimInputs ? this.colors.dim(this.getInputDisplay(false)) : this.colors.white(this.getInputDisplay(true));
|
|
2827
|
+
output += `${this.getBar()} ${circle} ${displayText}\n`;
|
|
2828
|
+
} else if (isSelected) if (dimInputs) output += `${this.getBar()} ${circle} ${this.colors.dim(option.label)}\n`;
|
|
2829
|
+
else output += `${this.getBar()} ${circle} ${this.colors.white(this.getInputDisplay(true))}\n`;
|
|
2830
|
+
else {
|
|
2831
|
+
const text$1 = dimInputs ? this.colors.dim(option.label) : this.colors.subtle(option.label);
|
|
2832
|
+
output += `${this.getBar()} ${circle} ${text$1}\n`;
|
|
2833
|
+
}
|
|
2834
|
+
return;
|
|
2835
|
+
}
|
|
2836
|
+
const formatText = (value) => {
|
|
2837
|
+
if (dimInputs) return this.colors.dim(value);
|
|
2838
|
+
if (isSelected) return this.colors.white(value);
|
|
2839
|
+
return this.colors.subtle(value);
|
|
2840
|
+
};
|
|
2841
|
+
let text = formatText(option.display);
|
|
2842
|
+
if (option.invalid) text = `\u001b[9m${text}\u001b[29m`;
|
|
2843
|
+
const annotation = option.annotation ? ` (${option.annotation})` : "";
|
|
2844
|
+
let suffix = "";
|
|
2845
|
+
if (annotation && isSelected) suffix = dimInputs ? this.colors.dim(annotation) : this.colors.subtle(annotation);
|
|
2846
|
+
output += `${this.getBar()} ${circle} ${text}${suffix}\n`;
|
|
2847
|
+
});
|
|
2848
|
+
output += `${this.getBarEnd()} ${this.renderFooter("Enter a number")}`;
|
|
2849
|
+
return output;
|
|
2850
|
+
},
|
|
2851
|
+
validate: (_value) => {
|
|
2852
|
+
if (this.consumeSkipValidation()) return;
|
|
2853
|
+
if (this.getOutcome() !== "commit") return;
|
|
2854
|
+
if (!this.hasPresetOptions()) {
|
|
2855
|
+
if (!this.userInput || !this.userInput.trim()) return "Please enter a number";
|
|
2856
|
+
const validation = this.runSchemaValidation(this.userInput);
|
|
2857
|
+
if (!validation.success) return validation.error;
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
const selectedOption = this.getSelectedOption();
|
|
2861
|
+
if (!this.mode.isInInteraction("typing") && selectedOption && selectedOption.type === "value" && selectedOption.key === "current" && selectedOption.invalid) return this.currentResult?.error ?? "Current value is invalid";
|
|
2862
|
+
const textInputIndex = this.getTextInputIndex();
|
|
2863
|
+
if (this.mode.getCursor() === textInputIndex && !this.mode.isInInteraction("typing")) {
|
|
2864
|
+
this.mode.enterTyping();
|
|
2865
|
+
this.mode.clearInput();
|
|
2866
|
+
this._setUserInput("");
|
|
2867
|
+
this.otherInputCache = "";
|
|
2868
|
+
this.updateValue();
|
|
2869
|
+
return "Please enter a number";
|
|
2870
|
+
}
|
|
2871
|
+
if (this.mode.getCursor() === textInputIndex && this.mode.isInInteraction("typing") && (!this.userInput || !this.userInput.trim())) return "Please enter a number";
|
|
2872
|
+
if (this.mode.isInInteraction("typing") && this.userInput) {
|
|
2873
|
+
const inputValidation = this.validateInput(this.userInput);
|
|
2874
|
+
if (inputValidation) return inputValidation;
|
|
2875
|
+
const validation = this.runSchemaValidation(this.userInput);
|
|
2876
|
+
if (!validation.success) return validation.error;
|
|
2877
|
+
}
|
|
2878
|
+
if (!this.mode.isInInteraction("typing")) {
|
|
2879
|
+
const candidate = selectedOption && selectedOption.type === "value" ? selectedOption.value : this.value;
|
|
2880
|
+
const validation = this.runSchemaValidation(candidate !== void 0 ? candidate.toString() : void 0);
|
|
2881
|
+
if (!validation.success) return validation.error;
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
});
|
|
2885
|
+
if (!this.hasPresetOptions()) {
|
|
2886
|
+
this.mode.enterTyping();
|
|
2887
|
+
this.mode.clearInput();
|
|
2888
|
+
this._setUserInput("");
|
|
2889
|
+
this.otherInputCache = "";
|
|
2890
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
2891
|
+
} else {
|
|
2892
|
+
const options = this.buildValueOptions();
|
|
2893
|
+
const currentValue = this.currentResult?.isValid !== false ? this.current : void 0;
|
|
2894
|
+
const defaultValue = this.default;
|
|
2895
|
+
const baseCursor = resolveInitialCursor(options, {
|
|
2896
|
+
currentValue,
|
|
2897
|
+
defaultValue
|
|
2898
|
+
});
|
|
2899
|
+
const initialCursor = options[baseCursor]?.invalid ? options.length : baseCursor;
|
|
2900
|
+
this.mode.setCursor(initialCursor);
|
|
2901
|
+
this.updateValue();
|
|
2902
|
+
queueMicrotask(() => {
|
|
2903
|
+
const refreshedOptions = this.buildValueOptions();
|
|
2904
|
+
const refreshedBase = resolveInitialCursor(refreshedOptions, {
|
|
2905
|
+
currentValue,
|
|
2906
|
+
defaultValue
|
|
2907
|
+
});
|
|
2908
|
+
const refreshedCursor = refreshedOptions[refreshedBase]?.invalid ? refreshedOptions.length : refreshedBase;
|
|
2909
|
+
this.mode.setCursor(refreshedCursor);
|
|
2910
|
+
this.updateValue();
|
|
2911
|
+
});
|
|
2912
|
+
}
|
|
2913
|
+
this.on("cursor", (action) => {
|
|
2914
|
+
this.clearErrorState();
|
|
2915
|
+
if (!this.hasPresetOptions()) return;
|
|
2916
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
2917
|
+
switch (action) {
|
|
2918
|
+
case "up":
|
|
2919
|
+
const maxIndex = this.buildSelectionOptions().length - 1;
|
|
2920
|
+
if (this.mode.isInInteraction("typing") || this.mode.getCursor() === maxIndex) {
|
|
2921
|
+
this.mode.exitTyping();
|
|
2922
|
+
this._clearUserInput();
|
|
2923
|
+
this.mode.clearInput();
|
|
2924
|
+
this.otherInputCache = "";
|
|
2925
|
+
}
|
|
2926
|
+
this.mode.moveCursor("up", maxIndex);
|
|
2927
|
+
break;
|
|
2928
|
+
case "down":
|
|
2929
|
+
const maxIndexDown = this.buildSelectionOptions().length - 1;
|
|
2930
|
+
if (this.mode.isInInteraction("typing") || this.mode.getCursor() === maxIndexDown) {
|
|
2931
|
+
this.mode.exitTyping();
|
|
2932
|
+
this._clearUserInput();
|
|
2933
|
+
this.mode.clearInput();
|
|
2934
|
+
this.otherInputCache = "";
|
|
2935
|
+
}
|
|
2936
|
+
this.mode.moveCursor("down", maxIndexDown);
|
|
2937
|
+
break;
|
|
2938
|
+
}
|
|
2939
|
+
this.updateValue();
|
|
2940
|
+
});
|
|
2941
|
+
this.on("userInput", (rawInput) => {
|
|
2942
|
+
this.clearErrorState();
|
|
2943
|
+
let input = rawInput.includes(" ") ? rawInput.replace(/\t/g, "") : rawInput;
|
|
2944
|
+
if (this.isRestoringInput) {
|
|
2945
|
+
this.isRestoringInput = false;
|
|
2946
|
+
this.otherInputCache = input;
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
if (this.mode.isInInteraction("typing")) {
|
|
2950
|
+
let nextInput = input;
|
|
2951
|
+
if (this.shouldStitchInput && this.otherInputCache) if (!input || input === this.otherInputCache) nextInput = this.otherInputCache;
|
|
2952
|
+
else if (input.startsWith(this.otherInputCache)) {
|
|
2953
|
+
nextInput = input;
|
|
2954
|
+
this.shouldStitchInput = false;
|
|
2955
|
+
} else {
|
|
2956
|
+
nextInput = `${this.otherInputCache}${input}`;
|
|
2957
|
+
this.shouldStitchInput = false;
|
|
2958
|
+
}
|
|
2959
|
+
else this.shouldStitchInput = false;
|
|
2960
|
+
this.mode.updateInput(nextInput);
|
|
2961
|
+
this.userInput = nextInput;
|
|
2962
|
+
this.otherInputCache = nextInput;
|
|
2963
|
+
try {
|
|
2964
|
+
const parsed = this.parseInput(nextInput);
|
|
2965
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
2966
|
+
} catch {}
|
|
2967
|
+
} else this.shouldStitchInput = false;
|
|
2968
|
+
});
|
|
2969
|
+
this.on("key", (char, info) => {
|
|
2970
|
+
if (!info) return;
|
|
2971
|
+
this.clearErrorState();
|
|
2972
|
+
if (this.handleToolbarKey(char, info)) return;
|
|
2973
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
2974
|
+
if (!this.hasPresetOptions()) {
|
|
2975
|
+
if (this.mode.isInInteraction("typing")) try {
|
|
2976
|
+
const parsed = this.parseInput(this.userInput);
|
|
2977
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
2978
|
+
} catch {}
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
if (char && char.length === 1 && !info.ctrl && !info.meta && !this.mode.isInInteraction("typing")) {
|
|
2982
|
+
const isArrowKey = [
|
|
2983
|
+
"up",
|
|
2984
|
+
"down",
|
|
2985
|
+
"left",
|
|
2986
|
+
"right"
|
|
2987
|
+
].includes(info.name || "");
|
|
2988
|
+
const isControlKey = [
|
|
2989
|
+
"return",
|
|
2990
|
+
"enter",
|
|
2991
|
+
"escape",
|
|
2992
|
+
"tab"
|
|
2993
|
+
].includes(info.name || "");
|
|
2994
|
+
if (!isArrowKey && !isControlKey) {
|
|
2995
|
+
const textInputIndex$1 = this.getTextInputIndex();
|
|
2996
|
+
this.mode.setCursor(textInputIndex$1);
|
|
2997
|
+
const existingInput = this.mode.getInputValue() || this.userInput || this.otherInputCache || "";
|
|
2998
|
+
this.mode.enterTyping(existingInput);
|
|
2999
|
+
const nextInput = `${existingInput}${char}`;
|
|
3000
|
+
this.otherInputCache = nextInput;
|
|
3001
|
+
this._setUserInput(nextInput);
|
|
3002
|
+
this.mode.updateInput(nextInput);
|
|
3003
|
+
this.syncCursorToInputEnd();
|
|
3004
|
+
this.updateValue();
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
const textInputIndex = this.getTextInputIndex();
|
|
3009
|
+
if (this.mode.getCursor() === textInputIndex) {
|
|
3010
|
+
if (info.name === "escape") {
|
|
3011
|
+
this.mode.exitTyping();
|
|
3012
|
+
this._clearUserInput();
|
|
3013
|
+
this.mode.clearInput();
|
|
3014
|
+
this.otherInputCache = "";
|
|
3015
|
+
this.updateValue();
|
|
3016
|
+
return;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
}
|
|
3021
|
+
getInputDisplay(includeCursor) {
|
|
3022
|
+
const inputValue = this.mode.getInputValue();
|
|
3023
|
+
if (!includeCursor) return inputValue;
|
|
3024
|
+
const rawCursor = this.mode.isInInteraction("typing") ? Math.max(0, this._cursor ?? inputValue.length) : 0;
|
|
3025
|
+
const cursorIndex = Math.min(rawCursor, inputValue.length);
|
|
3026
|
+
if (cursorIndex >= inputValue.length) return `${inputValue}${S_CURSOR}`;
|
|
3027
|
+
const before = inputValue.slice(0, cursorIndex);
|
|
3028
|
+
const cursorChar = inputValue.charAt(cursorIndex) || " ";
|
|
3029
|
+
const after = inputValue.slice(cursorIndex + 1);
|
|
3030
|
+
return `${before}${this.colors.inverse(cursorChar)}${after}`;
|
|
3031
|
+
}
|
|
3032
|
+
syncCursorToInputEnd() {
|
|
3033
|
+
const cursorHost = this;
|
|
3034
|
+
cursorHost._cursor = this.mode.getInputValue().length;
|
|
3035
|
+
}
|
|
3036
|
+
toggleSecretReveal() {
|
|
3037
|
+
const cachedInput = this.mode.getInputValue() || this.userInput || this.otherInputCache;
|
|
3038
|
+
super.toggleSecretReveal();
|
|
3039
|
+
if (!cachedInput) return;
|
|
3040
|
+
this.shouldStitchInput = Boolean(this.otherInputCache || cachedInput);
|
|
3041
|
+
const stitchedBaseline = cachedInput;
|
|
3042
|
+
queueMicrotask(() => {
|
|
3043
|
+
const restored = this.otherInputCache || stitchedBaseline;
|
|
3044
|
+
if (!restored) return;
|
|
3045
|
+
if (!this.mode.isInInteraction("typing")) this.mode.enterTyping(restored);
|
|
3046
|
+
this.isRestoringInput = true;
|
|
3047
|
+
this.internals.track = true;
|
|
3048
|
+
this.mode.updateInput(restored);
|
|
3049
|
+
this.otherInputCache = restored;
|
|
3050
|
+
this._setUserInput(restored, true);
|
|
3051
|
+
this.syncCursorToInputEnd();
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
updateValue() {
|
|
3055
|
+
if (!this.hasPresetOptions()) {
|
|
3056
|
+
try {
|
|
3057
|
+
const parsed = this.parseInput(this.mode.getInputValue());
|
|
3058
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
3059
|
+
} catch {
|
|
3060
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3061
|
+
}
|
|
3062
|
+
return;
|
|
3063
|
+
}
|
|
3064
|
+
if (!this.mode.isInInteraction("typing")) {
|
|
3065
|
+
const selected = this.buildSelectionOptions()[this.mode.getCursor()];
|
|
3066
|
+
if (!selected) {
|
|
3067
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3068
|
+
return;
|
|
3069
|
+
}
|
|
3070
|
+
if (selected.type === "value") {
|
|
3071
|
+
if (selected.invalid) {
|
|
3072
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3073
|
+
return;
|
|
3074
|
+
}
|
|
3075
|
+
this.setCommittedValue(selected.value ?? this.getDefaultValue());
|
|
3076
|
+
return;
|
|
3077
|
+
}
|
|
3078
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3079
|
+
} else try {
|
|
3080
|
+
const parsed = this.parseInput(this.mode.getInputValue());
|
|
3081
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
3082
|
+
} catch {
|
|
3083
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
formatValue(value) {
|
|
3087
|
+
const str = value !== void 0 ? value.toString() : "";
|
|
3088
|
+
return this.truncateValue(str);
|
|
3089
|
+
}
|
|
3090
|
+
parseInput(input) {
|
|
3091
|
+
if (!input || !input.trim()) return;
|
|
3092
|
+
const parsed = Number(input.trim());
|
|
3093
|
+
if (isNaN(parsed)) return;
|
|
3094
|
+
return parsed;
|
|
3095
|
+
}
|
|
3096
|
+
validateInput(input) {
|
|
3097
|
+
if (!input || !input.trim()) return "Please enter a number";
|
|
3098
|
+
const parsed = Number(input.trim());
|
|
3099
|
+
if (isNaN(parsed)) return "Please enter a valid number";
|
|
3100
|
+
}
|
|
3101
|
+
getDefaultValue() {
|
|
3102
|
+
return 0;
|
|
3103
|
+
}
|
|
3104
|
+
hasPresetOptions() {
|
|
3105
|
+
return this.currentResult?.rawValue !== void 0 || this.default !== void 0;
|
|
3106
|
+
}
|
|
3107
|
+
buildValueOptions() {
|
|
3108
|
+
const hasValidCurrent = this.currentResult?.isValid !== false && this.current !== void 0;
|
|
3109
|
+
const baseOptions = [];
|
|
3110
|
+
if (hasValidCurrent && this.current !== void 0) {
|
|
3111
|
+
const isSameAsDefault = this.current === this.default;
|
|
3112
|
+
baseOptions.push({
|
|
3113
|
+
key: "current",
|
|
3114
|
+
value: this.current,
|
|
3115
|
+
display: this.formatValue(this.current),
|
|
3116
|
+
annotation: this.buildAnnotation({
|
|
3117
|
+
isCurrent: true,
|
|
3118
|
+
isDefault: isSameAsDefault
|
|
3119
|
+
}) ?? void 0
|
|
3120
|
+
});
|
|
3121
|
+
}
|
|
3122
|
+
if (this.default !== void 0 && (!hasValidCurrent || this.current !== this.default) && this.default !== void 0) {
|
|
3123
|
+
const isSameAsCurrent = hasValidCurrent && this.current === this.default;
|
|
3124
|
+
baseOptions.push({
|
|
3125
|
+
key: "default",
|
|
3126
|
+
value: this.default,
|
|
3127
|
+
display: this.formatValue(this.default),
|
|
3128
|
+
annotation: this.buildAnnotation({
|
|
3129
|
+
isDefault: true,
|
|
3130
|
+
isCurrent: isSameAsCurrent
|
|
3131
|
+
}) ?? void 0
|
|
3132
|
+
});
|
|
3133
|
+
}
|
|
3134
|
+
return buildSelectableOptions({
|
|
3135
|
+
currentResult: this.currentResult,
|
|
3136
|
+
formatInvalidDisplay: (raw) => this.formatRawValue(raw ?? ""),
|
|
3137
|
+
buildAnnotation: (flags) => this.buildAnnotation(flags) ?? void 0,
|
|
3138
|
+
baseOptions,
|
|
3139
|
+
invalidOptionKey: "current"
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
buildSelectionOptions() {
|
|
3143
|
+
const options = this.buildValueOptions().map((option) => ({
|
|
3144
|
+
type: "value",
|
|
3145
|
+
key: option.key,
|
|
3146
|
+
value: option.value,
|
|
3147
|
+
display: option.display,
|
|
3148
|
+
annotation: option.annotation,
|
|
3149
|
+
invalid: option.invalid
|
|
3150
|
+
}));
|
|
3151
|
+
options.push({
|
|
3152
|
+
type: "other",
|
|
3153
|
+
label: "Other"
|
|
3154
|
+
});
|
|
3155
|
+
return options;
|
|
3156
|
+
}
|
|
3157
|
+
getSelectedOption() {
|
|
3158
|
+
if (!this.hasPresetOptions()) return;
|
|
3159
|
+
return this.buildSelectionOptions()[this.mode.getCursor()];
|
|
3160
|
+
}
|
|
3161
|
+
formatRawValue(raw) {
|
|
3162
|
+
if (!raw) return "";
|
|
3163
|
+
return this.truncateValue(raw);
|
|
3164
|
+
}
|
|
3165
|
+
getTextInputIndex() {
|
|
3166
|
+
if (!this.hasPresetOptions()) return 0;
|
|
3167
|
+
const options = this.buildSelectionOptions();
|
|
3168
|
+
const index = options.findIndex((option) => option.type === "other");
|
|
3169
|
+
return index === -1 ? options.length - 1 : index;
|
|
3170
|
+
}
|
|
3171
|
+
};
|
|
3172
|
+
|
|
3173
|
+
//#endregion
|
|
3174
|
+
//#region ../prompt/src/utils/secrets.ts
|
|
3175
|
+
function isSecretMatch(value, pattern) {
|
|
3176
|
+
if (!value) return false;
|
|
3177
|
+
if (typeof pattern === "string") {
|
|
3178
|
+
const needle = pattern.trim().toLowerCase();
|
|
3179
|
+
if (!needle) return false;
|
|
3180
|
+
return value.toLowerCase().includes(needle);
|
|
3181
|
+
}
|
|
3182
|
+
pattern.lastIndex = 0;
|
|
3183
|
+
const matched = pattern.test(value);
|
|
3184
|
+
pattern.lastIndex = 0;
|
|
3185
|
+
return matched;
|
|
3186
|
+
}
|
|
3187
|
+
function isSecretKey(key, description, patterns) {
|
|
3188
|
+
if (patterns.length === 0) return false;
|
|
3189
|
+
for (const pattern of patterns) if (isSecretMatch(key, pattern) || isSecretMatch(description, pattern)) return true;
|
|
3190
|
+
return false;
|
|
3191
|
+
}
|
|
3192
|
+
function maskSecretValue(value, maskChar = SECRET_MASK) {
|
|
3193
|
+
if (!value) return value;
|
|
3194
|
+
return value.replace(/./g, maskChar);
|
|
3195
|
+
}
|
|
3196
|
+
function resolveShouldMask(key, schema, patterns) {
|
|
3197
|
+
if (schema.type !== "string") return false;
|
|
3198
|
+
if ("secret" in schema && schema.secret !== void 0) return schema.secret;
|
|
3199
|
+
return isSecretKey(key, schema.description, patterns);
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
//#endregion
|
|
3203
|
+
//#region ../prompt/src/prompts/typed/EnvStringPrompt.ts
|
|
3204
|
+
var EnvStringPrompt = class extends EnvPrompt {
|
|
3205
|
+
otherInputCache = "";
|
|
3206
|
+
shouldStitchInput = false;
|
|
3207
|
+
isRestoringInput = false;
|
|
3208
|
+
constructor(schema, opts) {
|
|
3209
|
+
super(schema, {
|
|
3210
|
+
...opts,
|
|
3211
|
+
render: function() {
|
|
3212
|
+
if (this.state === "submit") {
|
|
3213
|
+
const outcomeResult = this.renderOutcomeResult();
|
|
3214
|
+
if (outcomeResult) return outcomeResult;
|
|
3215
|
+
const base = `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}`;
|
|
3216
|
+
if (!this.required && (this.value === void 0 || this.value === "")) return base;
|
|
3217
|
+
return `${base}${this.colors.subtle("=")}${this.colors.white(this.formatValue(this.value))}`;
|
|
3218
|
+
}
|
|
3219
|
+
if (this.state === "cancel") return this.renderCancelled();
|
|
3220
|
+
let output = "";
|
|
3221
|
+
output += `${this.getSymbol()} ${this.colors.bold(this.colors.white(this.key))}`;
|
|
3222
|
+
if (this.schema.description) output += ` ${this.colors.subtle(this.schema.description)}`;
|
|
3223
|
+
output += "\n";
|
|
3224
|
+
const dimInputs = !this.error && this.mode.isToolbarOpen();
|
|
3225
|
+
if (!this.hasPresetOptions()) {
|
|
3226
|
+
const displayText = dimInputs ? this.colors.dim(this.getInputDisplay(false)) : this.colors.white(this.getInputDisplay(true));
|
|
3227
|
+
output += `${this.getBar()} ${displayText}`;
|
|
3228
|
+
output += "\n";
|
|
3229
|
+
output += `${this.getBarEnd()} ${this.renderFooter(this.getEntryHint())}`;
|
|
3230
|
+
return output;
|
|
3231
|
+
}
|
|
3232
|
+
this.buildSelectionOptions().forEach((option, index) => {
|
|
3233
|
+
const isSelected = index === this.mode.getCursor();
|
|
3234
|
+
const circle = dimInputs ? this.colors.dim(isSelected ? S_RADIO_ACTIVE : S_RADIO_INACTIVE) : isSelected ? this.theme.primary(S_RADIO_ACTIVE) : this.colors.dim(S_RADIO_INACTIVE);
|
|
3235
|
+
if (option.type === "other") {
|
|
3236
|
+
if (this.mode.isInInteraction("typing")) {
|
|
3237
|
+
const displayText = dimInputs ? this.colors.dim(this.getInputDisplay(false)) : this.colors.white(this.getInputDisplay(true));
|
|
3238
|
+
output += `${this.getBar()} ${circle} ${displayText}\n`;
|
|
3239
|
+
} else if (isSelected) if (dimInputs) output += `${this.getBar()} ${circle} ${this.colors.dim(option.label)}\n`;
|
|
3240
|
+
else output += `${this.getBar()} ${circle} ${this.colors.white(this.getInputDisplay(true))}\n`;
|
|
3241
|
+
else {
|
|
3242
|
+
const text$1 = dimInputs ? this.colors.dim(option.label) : this.colors.subtle(option.label);
|
|
3243
|
+
output += `${this.getBar()} ${circle} ${text$1}\n`;
|
|
3244
|
+
}
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3247
|
+
const formatText = (value) => {
|
|
3248
|
+
if (dimInputs) return this.colors.dim(value);
|
|
3249
|
+
if (isSelected) return this.colors.white(value);
|
|
3250
|
+
return this.colors.subtle(value);
|
|
3251
|
+
};
|
|
3252
|
+
let text = formatText(option.display);
|
|
3253
|
+
if (option.invalid) text = `\u001b[9m${text}\u001b[29m`;
|
|
3254
|
+
const annotation = option.annotation ? ` (${option.annotation})` : "";
|
|
3255
|
+
let suffix = "";
|
|
3256
|
+
if (annotation && isSelected) suffix = dimInputs ? this.colors.dim(annotation) : this.colors.subtle(annotation);
|
|
3257
|
+
output += `${this.getBar()} ${circle} ${text}${suffix}\n`;
|
|
3258
|
+
});
|
|
3259
|
+
output += `${this.getBarEnd()} ${this.renderFooter(this.getEntryHint())}`;
|
|
3260
|
+
return output;
|
|
3261
|
+
},
|
|
3262
|
+
validate: (_value) => {
|
|
3263
|
+
if (this.consumeSkipValidation()) return;
|
|
3264
|
+
if (this.getOutcome() !== "commit") return;
|
|
3265
|
+
if (!this.hasPresetOptions()) {
|
|
3266
|
+
const input = this.userInput ?? "";
|
|
3267
|
+
if (!input.trim()) {
|
|
3268
|
+
if (this.required) return "Please enter a value";
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
const inputValidation = this.validateInput(input);
|
|
3272
|
+
if (inputValidation) return inputValidation;
|
|
3273
|
+
const validation = this.runSchemaValidation(input);
|
|
3274
|
+
if (!validation.success) return validation.error;
|
|
3275
|
+
return;
|
|
3276
|
+
}
|
|
3277
|
+
const selectedOption = this.getSelectedOption();
|
|
3278
|
+
if (!this.mode.isInInteraction("typing") && selectedOption && selectedOption.type === "value" && selectedOption.key === "current" && selectedOption.invalid) return this.currentResult?.error ?? "Current value is invalid";
|
|
3279
|
+
const textInputIndex = this.getTextInputIndex();
|
|
3280
|
+
if (this.mode.getCursor() === textInputIndex && !this.mode.isInInteraction("typing")) {
|
|
3281
|
+
this.mode.enterTyping();
|
|
3282
|
+
this.mode.clearInput();
|
|
3283
|
+
this._setUserInput("");
|
|
3284
|
+
this.otherInputCache = "";
|
|
3285
|
+
this.updateValue();
|
|
3286
|
+
return "Value cannot be empty";
|
|
3287
|
+
}
|
|
3288
|
+
if (this.mode.getCursor() === textInputIndex && this.mode.isInInteraction("typing") && !(this.userInput && this.userInput.trim())) {
|
|
3289
|
+
if (this.required) return "Please enter a value";
|
|
3290
|
+
return;
|
|
3291
|
+
}
|
|
3292
|
+
if (this.mode.isInInteraction("typing") && this.userInput) {
|
|
3293
|
+
const inputValidation = this.validateInput(this.userInput);
|
|
3294
|
+
if (inputValidation) return inputValidation;
|
|
3295
|
+
const validation = this.runSchemaValidation(this.userInput);
|
|
3296
|
+
if (!validation.success) return validation.error;
|
|
3297
|
+
}
|
|
3298
|
+
if (!this.mode.isInInteraction("typing")) {
|
|
3299
|
+
const candidate = selectedOption && selectedOption.type === "value" ? selectedOption.value : this.value;
|
|
3300
|
+
const validation = this.runSchemaValidation(candidate !== void 0 ? candidate : void 0);
|
|
3301
|
+
if (!validation.success) return validation.error;
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
});
|
|
3305
|
+
if (!this.hasPresetOptions()) {
|
|
3306
|
+
this.mode.enterTyping();
|
|
3307
|
+
this.mode.clearInput();
|
|
3308
|
+
this._setUserInput("");
|
|
3309
|
+
this.otherInputCache = "";
|
|
3310
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3311
|
+
} else {
|
|
3312
|
+
const options = this.buildValueOptions();
|
|
3313
|
+
const currentValue = this.currentResult?.isValid !== false ? this.current : void 0;
|
|
3314
|
+
const defaultValue = this.default;
|
|
3315
|
+
const baseCursor = resolveInitialCursor(options, {
|
|
3316
|
+
currentValue,
|
|
3317
|
+
defaultValue
|
|
3318
|
+
});
|
|
3319
|
+
const initialCursor = options[baseCursor]?.invalid ? options.length : baseCursor;
|
|
3320
|
+
this.mode.setCursor(initialCursor);
|
|
3321
|
+
this.updateValue();
|
|
3322
|
+
queueMicrotask(() => {
|
|
3323
|
+
const refreshedOptions = this.buildValueOptions();
|
|
3324
|
+
const refreshedBase = resolveInitialCursor(refreshedOptions, {
|
|
3325
|
+
currentValue,
|
|
3326
|
+
defaultValue
|
|
3327
|
+
});
|
|
3328
|
+
const refreshedCursor = refreshedOptions[refreshedBase]?.invalid ? refreshedOptions.length : refreshedBase;
|
|
3329
|
+
this.mode.setCursor(refreshedCursor);
|
|
3330
|
+
this.updateValue();
|
|
3331
|
+
});
|
|
3332
|
+
}
|
|
3333
|
+
this.on("cursor", (action) => {
|
|
3334
|
+
this.clearErrorState();
|
|
3335
|
+
if (!this.hasPresetOptions()) return;
|
|
3336
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
3337
|
+
switch (action) {
|
|
3338
|
+
case "up": {
|
|
3339
|
+
const maxIndex = this.buildSelectionOptions().length - 1;
|
|
3340
|
+
if (this.mode.isInInteraction("typing") || this.mode.getCursor() === maxIndex) {
|
|
3341
|
+
this.mode.exitTyping();
|
|
3342
|
+
this._clearUserInput();
|
|
3343
|
+
this.mode.clearInput();
|
|
3344
|
+
this.otherInputCache = "";
|
|
3345
|
+
}
|
|
3346
|
+
this.mode.moveCursor("up", maxIndex);
|
|
3347
|
+
break;
|
|
3348
|
+
}
|
|
3349
|
+
case "down": {
|
|
3350
|
+
const maxIndex = this.buildSelectionOptions().length - 1;
|
|
3351
|
+
if (this.mode.isInInteraction("typing") || this.mode.getCursor() === maxIndex) {
|
|
3352
|
+
this.mode.exitTyping();
|
|
3353
|
+
this._clearUserInput();
|
|
3354
|
+
this.mode.clearInput();
|
|
3355
|
+
this.otherInputCache = "";
|
|
3356
|
+
}
|
|
3357
|
+
this.mode.moveCursor("down", maxIndex);
|
|
3358
|
+
break;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
this.updateValue();
|
|
3362
|
+
});
|
|
3363
|
+
this.on("userInput", (rawInput) => {
|
|
3364
|
+
this.clearErrorState();
|
|
3365
|
+
let input = rawInput.includes(" ") ? rawInput.replace(/\t/g, "") : rawInput;
|
|
3366
|
+
if (this.isRestoringInput) {
|
|
3367
|
+
this.isRestoringInput = false;
|
|
3368
|
+
this.otherInputCache = input;
|
|
3369
|
+
return;
|
|
3370
|
+
}
|
|
3371
|
+
if (this.mode.isInInteraction("typing")) {
|
|
3372
|
+
let nextInput = input;
|
|
3373
|
+
if (this.shouldStitchInput && this.otherInputCache) if (!input || input === this.otherInputCache) nextInput = this.otherInputCache;
|
|
3374
|
+
else if (input.startsWith(this.otherInputCache)) {
|
|
3375
|
+
nextInput = input;
|
|
3376
|
+
this.shouldStitchInput = false;
|
|
3377
|
+
} else {
|
|
3378
|
+
nextInput = `${this.otherInputCache}${input}`;
|
|
3379
|
+
this.shouldStitchInput = false;
|
|
3380
|
+
}
|
|
3381
|
+
else this.shouldStitchInput = false;
|
|
3382
|
+
this.mode.updateInput(nextInput);
|
|
3383
|
+
this.userInput = nextInput;
|
|
3384
|
+
this.otherInputCache = nextInput;
|
|
3385
|
+
try {
|
|
3386
|
+
const parsed = this.parseInput(nextInput);
|
|
3387
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
3388
|
+
} catch {}
|
|
3389
|
+
} else this.shouldStitchInput = false;
|
|
3390
|
+
});
|
|
3391
|
+
this.on("key", (char, info) => {
|
|
3392
|
+
if (!info) return;
|
|
3393
|
+
this.clearErrorState();
|
|
3394
|
+
if (this.handleToolbarKey(char, info)) return;
|
|
3395
|
+
if (!this.error && this.mode.isToolbarOpen()) return;
|
|
3396
|
+
if (!this.hasPresetOptions()) {
|
|
3397
|
+
if (this.mode.isInInteraction("typing")) try {
|
|
3398
|
+
const parsed = this.parseInput(this.userInput);
|
|
3399
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
3400
|
+
} catch {}
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
if (char && char.length === 1 && !info.ctrl && !info.meta && !this.mode.isInInteraction("typing")) {
|
|
3404
|
+
const isArrowKey = [
|
|
3405
|
+
"up",
|
|
3406
|
+
"down",
|
|
3407
|
+
"left",
|
|
3408
|
+
"right"
|
|
3409
|
+
].includes(info.name || "");
|
|
3410
|
+
const isControlKey = [
|
|
3411
|
+
"return",
|
|
3412
|
+
"enter",
|
|
3413
|
+
"escape",
|
|
3414
|
+
"tab"
|
|
3415
|
+
].includes(info.name || "");
|
|
3416
|
+
if (!isArrowKey && !isControlKey) {
|
|
3417
|
+
const textInputIndex$1 = this.getTextInputIndex();
|
|
3418
|
+
this.mode.setCursor(textInputIndex$1);
|
|
3419
|
+
const existingInput = this.mode.getInputValue() || this.userInput || this.otherInputCache || "";
|
|
3420
|
+
this.mode.enterTyping(existingInput);
|
|
3421
|
+
const nextInput = `${existingInput}${char}`;
|
|
3422
|
+
this.otherInputCache = nextInput;
|
|
3423
|
+
this._setUserInput(nextInput);
|
|
3424
|
+
this.mode.updateInput(nextInput);
|
|
3425
|
+
this.syncCursorToInputEnd();
|
|
3426
|
+
this.updateValue();
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
const textInputIndex = this.getTextInputIndex();
|
|
3431
|
+
if (this.mode.getCursor() === textInputIndex) {
|
|
3432
|
+
if (info.name === "escape") {
|
|
3433
|
+
this.mode.exitTyping();
|
|
3434
|
+
this._clearUserInput();
|
|
3435
|
+
this.mode.clearInput();
|
|
3436
|
+
this.otherInputCache = "";
|
|
3437
|
+
this.updateValue();
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3443
|
+
get cursor() {
|
|
3444
|
+
return this.mode.getCursor();
|
|
3445
|
+
}
|
|
3446
|
+
get isTyping() {
|
|
3447
|
+
return this.mode.isInInteraction("typing");
|
|
3448
|
+
}
|
|
3449
|
+
updateValue() {
|
|
3450
|
+
if (!this.hasPresetOptions()) {
|
|
3451
|
+
try {
|
|
3452
|
+
const parsed = this.parseInput(this.mode.getInputValue());
|
|
3453
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
3454
|
+
} catch {
|
|
3455
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3456
|
+
}
|
|
3457
|
+
return;
|
|
3458
|
+
}
|
|
3459
|
+
if (!this.mode.isInInteraction("typing")) {
|
|
3460
|
+
const selected = this.buildSelectionOptions()[this.mode.getCursor()];
|
|
3461
|
+
if (!selected) {
|
|
3462
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3463
|
+
return;
|
|
3464
|
+
}
|
|
3465
|
+
if (selected.type === "value") {
|
|
3466
|
+
if (selected.invalid) {
|
|
3467
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3468
|
+
return;
|
|
3469
|
+
}
|
|
3470
|
+
this.setCommittedValue(selected.value ?? this.getDefaultValue());
|
|
3471
|
+
return;
|
|
3472
|
+
}
|
|
3473
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3474
|
+
return;
|
|
3475
|
+
}
|
|
3476
|
+
try {
|
|
3477
|
+
const parsed = this.parseInput(this.mode.getInputValue());
|
|
3478
|
+
this.setCommittedValue(parsed ?? this.getDefaultValue());
|
|
3479
|
+
} catch {
|
|
3480
|
+
this.setCommittedValue(this.getDefaultValue());
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
formatValue(value) {
|
|
3484
|
+
const hasExplicitEmpty = value === "";
|
|
3485
|
+
const str = value ?? "";
|
|
3486
|
+
let display = this.secret && str && !this.isSecretRevealed() ? this.maskValue(str) : str;
|
|
3487
|
+
if (!display && hasExplicitEmpty) display = "\"\"";
|
|
3488
|
+
return this.truncateValue(display);
|
|
3489
|
+
}
|
|
3490
|
+
parseInput(input) {
|
|
3491
|
+
return input || void 0;
|
|
3492
|
+
}
|
|
3493
|
+
validateInput(_input) {}
|
|
3494
|
+
getDefaultValue() {
|
|
3495
|
+
return "";
|
|
3496
|
+
}
|
|
3497
|
+
hasPresetOptions() {
|
|
3498
|
+
return this.currentResult?.rawValue !== void 0 || this.default !== void 0;
|
|
3499
|
+
}
|
|
3500
|
+
buildValueOptions() {
|
|
3501
|
+
const hasValidCurrent = this.currentResult?.isValid !== false && this.current !== void 0;
|
|
3502
|
+
const baseOptions = [];
|
|
3503
|
+
if (hasValidCurrent && this.current !== void 0) {
|
|
3504
|
+
const isSameAsDefault = this.current === this.default;
|
|
3505
|
+
baseOptions.push({
|
|
3506
|
+
key: "current",
|
|
3507
|
+
value: this.current,
|
|
3508
|
+
display: this.formatValue(this.current),
|
|
3509
|
+
annotation: this.buildAnnotation({
|
|
3510
|
+
isCurrent: true,
|
|
3511
|
+
isDefault: isSameAsDefault
|
|
3512
|
+
}) ?? void 0
|
|
3513
|
+
});
|
|
3514
|
+
}
|
|
3515
|
+
if (this.default !== void 0 && (!hasValidCurrent || this.current !== this.default) && this.default !== void 0) {
|
|
3516
|
+
const isSameAsCurrent = hasValidCurrent && this.current === this.default;
|
|
3517
|
+
baseOptions.push({
|
|
3518
|
+
key: "default",
|
|
3519
|
+
value: this.default,
|
|
3520
|
+
display: this.formatValue(this.default),
|
|
3521
|
+
annotation: this.buildAnnotation({
|
|
3522
|
+
isDefault: true,
|
|
3523
|
+
isCurrent: isSameAsCurrent
|
|
3524
|
+
}) ?? void 0
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
return buildSelectableOptions({
|
|
3528
|
+
currentResult: this.currentResult,
|
|
3529
|
+
formatInvalidDisplay: (raw) => this.formatRawValue(raw ?? ""),
|
|
3530
|
+
buildAnnotation: (flags) => this.buildAnnotation(flags) ?? void 0,
|
|
3531
|
+
baseOptions,
|
|
3532
|
+
invalidOptionKey: "current"
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3535
|
+
buildSelectionOptions() {
|
|
3536
|
+
const options = this.buildValueOptions().map((option) => ({
|
|
3537
|
+
type: "value",
|
|
3538
|
+
key: option.key,
|
|
3539
|
+
value: option.value,
|
|
3540
|
+
display: option.display,
|
|
3541
|
+
annotation: option.annotation,
|
|
3542
|
+
invalid: option.invalid
|
|
3543
|
+
}));
|
|
3544
|
+
options.push({
|
|
3545
|
+
type: "other",
|
|
3546
|
+
label: "Other"
|
|
3547
|
+
});
|
|
3548
|
+
return options;
|
|
3549
|
+
}
|
|
3550
|
+
getSelectedOption() {
|
|
3551
|
+
if (!this.hasPresetOptions()) return;
|
|
3552
|
+
return this.buildSelectionOptions()[this.mode.getCursor()];
|
|
3553
|
+
}
|
|
3554
|
+
formatRawValue(raw) {
|
|
3555
|
+
if (!raw) return "";
|
|
3556
|
+
const display = this.secret && !this.isSecretRevealed() ? this.maskValue(raw) : raw;
|
|
3557
|
+
return this.truncateValue(display);
|
|
3558
|
+
}
|
|
3559
|
+
maskValue(value) {
|
|
3560
|
+
return maskSecretValue(value, SECRET_MASK);
|
|
3561
|
+
}
|
|
3562
|
+
getTextInputIndex() {
|
|
3563
|
+
if (!this.hasPresetOptions()) return 0;
|
|
3564
|
+
const options = this.buildSelectionOptions();
|
|
3565
|
+
const index = options.findIndex((option) => option.type === "other");
|
|
3566
|
+
return index === -1 ? options.length - 1 : index;
|
|
3567
|
+
}
|
|
3568
|
+
getInputDisplay(includeCursor) {
|
|
3569
|
+
const inputValue = this.mode.getInputValue();
|
|
3570
|
+
const isMasked = this.secret && inputValue && !this.isSecretRevealed();
|
|
3571
|
+
const base = isMasked ? this.maskValue(inputValue) : inputValue;
|
|
3572
|
+
if (!includeCursor) return base;
|
|
3573
|
+
const rawCursor = this.mode.isInInteraction("typing") ? Math.max(0, this._cursor ?? inputValue.length) : 0;
|
|
3574
|
+
const maskLength = isMasked ? Math.max(1, SECRET_MASK.length) : 1;
|
|
3575
|
+
const cursorIndex = Math.min(rawCursor * maskLength, base.length);
|
|
3576
|
+
if (cursorIndex >= base.length) return `${base}${S_CURSOR}`;
|
|
3577
|
+
const segmentLength = maskLength;
|
|
3578
|
+
const before = base.slice(0, cursorIndex);
|
|
3579
|
+
const cursorSegment = base.slice(cursorIndex, cursorIndex + segmentLength) || " ";
|
|
3580
|
+
const after = base.slice(cursorIndex + segmentLength);
|
|
3581
|
+
return `${before}${this.colors.inverse(cursorSegment)}${after}`;
|
|
3582
|
+
}
|
|
3583
|
+
getEntryHint() {
|
|
3584
|
+
return this.secret ? "Enter a secret value" : "Enter a value";
|
|
3585
|
+
}
|
|
3586
|
+
syncCursorToInputEnd() {
|
|
3587
|
+
const cursorHost = this;
|
|
3588
|
+
cursorHost._cursor = this.mode.getInputValue().length;
|
|
3589
|
+
}
|
|
3590
|
+
toggleSecretReveal() {
|
|
3591
|
+
const cachedInput = this.mode.getInputValue() || this.userInput || this.otherInputCache;
|
|
3592
|
+
super.toggleSecretReveal();
|
|
3593
|
+
if (!cachedInput) return;
|
|
3594
|
+
this.shouldStitchInput = Boolean(this.otherInputCache || cachedInput);
|
|
3595
|
+
const stitchedBaseline = cachedInput;
|
|
3596
|
+
queueMicrotask(() => {
|
|
3597
|
+
const restored = this.otherInputCache || stitchedBaseline;
|
|
3598
|
+
if (!restored) return;
|
|
3599
|
+
if (!this.mode.isInInteraction("typing")) this.mode.enterTyping(restored);
|
|
3600
|
+
this.isRestoringInput = true;
|
|
3601
|
+
this.internals.track = true;
|
|
3602
|
+
this.mode.updateInput(restored);
|
|
3603
|
+
this.otherInputCache = restored;
|
|
3604
|
+
this._setUserInput(restored, true);
|
|
3605
|
+
this.syncCursorToInputEnd();
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3608
|
+
};
|
|
3609
|
+
|
|
3610
|
+
//#endregion
|
|
3611
|
+
//#region ../prompt/src/prompts/create/createPrompt.ts
|
|
3612
|
+
function createPrompt({ key, schema, currentValue, theme, truncate, shouldMask, index, total, input, output, preprocessors: preprocessors$1 }) {
|
|
3613
|
+
const baseOptions = {
|
|
3614
|
+
key,
|
|
3615
|
+
theme,
|
|
3616
|
+
truncate,
|
|
3617
|
+
index,
|
|
3618
|
+
total,
|
|
3619
|
+
input,
|
|
3620
|
+
output
|
|
3621
|
+
};
|
|
3622
|
+
switch (schema.type) {
|
|
3623
|
+
case "boolean":
|
|
3624
|
+
const booleanOverride = preprocessors$1?.boolean;
|
|
3625
|
+
let booleanPreprocess;
|
|
3626
|
+
if (booleanOverride === void 0) booleanPreprocess = void 0;
|
|
3627
|
+
else if (booleanOverride === true) booleanPreprocess = booleanPreprocessor();
|
|
3628
|
+
else if (booleanOverride === false) booleanPreprocess = false;
|
|
3629
|
+
else if (typeof booleanOverride === "function") booleanPreprocess = booleanOverride;
|
|
3630
|
+
else booleanPreprocess = booleanPreprocessor(booleanOverride);
|
|
3631
|
+
return new EnvBooleanPrompt(schema, {
|
|
3632
|
+
...baseOptions,
|
|
3633
|
+
current: currentValue,
|
|
3634
|
+
preprocess: booleanPreprocess
|
|
3635
|
+
});
|
|
3636
|
+
case "number": return new EnvNumberPrompt(schema, {
|
|
3637
|
+
...baseOptions,
|
|
3638
|
+
current: currentValue,
|
|
3639
|
+
preprocess: preprocessors$1?.number
|
|
3640
|
+
});
|
|
3641
|
+
case "enum": return new EnvEnumPrompt(schema, {
|
|
3642
|
+
...baseOptions,
|
|
3643
|
+
current: currentValue,
|
|
3644
|
+
preprocess: preprocessors$1?.enum
|
|
3645
|
+
});
|
|
3646
|
+
default: return new EnvStringPrompt(schema, {
|
|
3647
|
+
...baseOptions,
|
|
3648
|
+
current: currentValue,
|
|
3649
|
+
secret: shouldMask,
|
|
3650
|
+
preprocess: preprocessors$1?.string
|
|
3651
|
+
});
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
//#endregion
|
|
3656
|
+
//#region ../prompt/src/utils/clearConsoleLines.ts
|
|
3657
|
+
function clearConsoleLines(lineCount) {
|
|
3658
|
+
const output = process.stdout;
|
|
3659
|
+
if (!output?.isTTY) return;
|
|
3660
|
+
try {
|
|
3661
|
+
for (let i = 0; i < lineCount; i++) {
|
|
3662
|
+
moveCursor(output, 0, -1);
|
|
3663
|
+
cursorTo(output, 0);
|
|
3664
|
+
clearLine(output, 0);
|
|
3665
|
+
}
|
|
3666
|
+
cursorTo(output, 0);
|
|
3667
|
+
} catch {}
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
//#endregion
|
|
3671
|
+
//#region ../prompt/src/utils/getDisplayEnvPath.ts
|
|
3672
|
+
function getDisplayEnvPath(envPath, cwd = process.cwd()) {
|
|
3673
|
+
const relativeEnvPath = relative(cwd, envPath);
|
|
3674
|
+
if (relativeEnvPath === "") return ".";
|
|
3675
|
+
if (relativeEnvPath.startsWith(".")) return relativeEnvPath;
|
|
3676
|
+
return `./${relativeEnvPath}`;
|
|
3677
|
+
}
|
|
3678
|
+
|
|
3679
|
+
//#endregion
|
|
3680
|
+
//#region ../prompt/src/session/Session.ts
|
|
3681
|
+
var Session = class Session {
|
|
3682
|
+
schemas;
|
|
3683
|
+
newValues = {};
|
|
3684
|
+
promptLineHistory = [];
|
|
3685
|
+
channel;
|
|
3686
|
+
secrets;
|
|
3687
|
+
truncate;
|
|
3688
|
+
theme;
|
|
3689
|
+
output;
|
|
3690
|
+
input;
|
|
3691
|
+
displayEnvPath;
|
|
3692
|
+
envPath;
|
|
3693
|
+
preprocessors;
|
|
3694
|
+
constructor({ channel, secrets, truncate, theme, output, input, schemas, path, preprocess }) {
|
|
3695
|
+
this.channel = channel;
|
|
3696
|
+
this.secrets = secrets;
|
|
3697
|
+
this.truncate = truncate;
|
|
3698
|
+
this.theme = theme;
|
|
3699
|
+
this.output = output;
|
|
3700
|
+
this.input = input;
|
|
3701
|
+
this.displayEnvPath = getDisplayEnvPath(path);
|
|
3702
|
+
const normalizedEnvPath = path.startsWith("file://") ? fileURLToPath(path) : path;
|
|
3703
|
+
this.envPath = resolve(process.cwd(), normalizedEnvPath);
|
|
3704
|
+
this.schemas = schemas;
|
|
3705
|
+
this.preprocessors = preprocess;
|
|
3706
|
+
}
|
|
3707
|
+
static fromOptions(options) {
|
|
3708
|
+
return new Session(options);
|
|
3709
|
+
}
|
|
3710
|
+
async run() {
|
|
3711
|
+
this.renderSetupHeader();
|
|
3712
|
+
let currentValues = await this.channel.get();
|
|
3713
|
+
const schemaKeys = Object.keys(this.schemas);
|
|
3714
|
+
let index = 0;
|
|
3715
|
+
while (index < schemaKeys.length) {
|
|
3716
|
+
const key = schemaKeys[index];
|
|
3717
|
+
const envVarSchema = this.schemas[key];
|
|
3718
|
+
let addedLines = 0;
|
|
3719
|
+
if (index > 0) {
|
|
3720
|
+
this.output.write(`${color.gray("│")} \n`);
|
|
3721
|
+
addedLines++;
|
|
3722
|
+
}
|
|
3723
|
+
const shouldMask = resolveShouldMask(key, envVarSchema, this.secrets);
|
|
3724
|
+
const storedValue = this.newValues[key] ?? currentValues[key];
|
|
3725
|
+
const prompt$1 = createPrompt({
|
|
3726
|
+
key,
|
|
3727
|
+
schema: envVarSchema,
|
|
3728
|
+
currentValue: storedValue && storedValue.trim() !== "" ? storedValue : void 0,
|
|
3729
|
+
theme: this.theme,
|
|
3730
|
+
truncate: this.truncate,
|
|
3731
|
+
shouldMask,
|
|
3732
|
+
index,
|
|
3733
|
+
total: schemaKeys.length,
|
|
3734
|
+
input: this.input,
|
|
3735
|
+
output: this.output,
|
|
3736
|
+
preprocessors: this.preprocessors
|
|
3737
|
+
});
|
|
3738
|
+
const value = await prompt$1.prompt();
|
|
3739
|
+
addedLines++;
|
|
3740
|
+
const outcome = prompt$1.getOutcome();
|
|
3741
|
+
if (isCancel(value) || typeof value === "symbol" && value?.description === "clack:cancel") {
|
|
3742
|
+
this.output.write(`${color.red("│")} \n`);
|
|
3743
|
+
this.output.write(`${color.red("└")} ${color.red("Setup cancelled.")}\n\n`);
|
|
3744
|
+
return "cancelled";
|
|
3745
|
+
}
|
|
3746
|
+
if (outcome === "previous") {
|
|
3747
|
+
clearConsoleLines(addedLines);
|
|
3748
|
+
const previousLines = this.promptLineHistory.pop() ?? 0;
|
|
3749
|
+
if (previousLines > 0) clearConsoleLines(previousLines);
|
|
3750
|
+
index = Math.max(index - 1, 0);
|
|
3751
|
+
continue;
|
|
3752
|
+
}
|
|
3753
|
+
this.promptLineHistory.push(addedLines);
|
|
3754
|
+
if (outcome === "skip") {
|
|
3755
|
+
index++;
|
|
3756
|
+
continue;
|
|
3757
|
+
}
|
|
3758
|
+
const stringValue = String(value);
|
|
3759
|
+
try {
|
|
3760
|
+
await this.channel.set({ [key]: stringValue });
|
|
3761
|
+
currentValues = await this.channel.get();
|
|
3762
|
+
this.newValues[key] = stringValue;
|
|
3763
|
+
} catch (error) {
|
|
3764
|
+
this.output.write(`${color.gray(S_BAR_END)} ${color.red(`Failed to save ${key}: ${error}`)}\n\n`);
|
|
3765
|
+
return "error";
|
|
3766
|
+
}
|
|
3767
|
+
index++;
|
|
3768
|
+
}
|
|
3769
|
+
this.output.write(`${color.gray(S_BAR)}\n${color.gray(S_BAR_END)} ${this.theme.primary("Setup complete!")}\n\n`);
|
|
3770
|
+
return "success";
|
|
3771
|
+
}
|
|
3772
|
+
renderSetupHeader() {
|
|
3773
|
+
const envFileUrl = pathToFileURL(this.envPath).toString();
|
|
3774
|
+
const clickableEnvPath = this.toHyperlink(color.gray(this.displayEnvPath), envFileUrl);
|
|
3775
|
+
const variableCount = Object.keys(this.schemas).length;
|
|
3776
|
+
const variableLabel = variableCount === 1 ? "1 variable" : `${variableCount} variables`;
|
|
3777
|
+
const headerBody = [
|
|
3778
|
+
this.theme.bgPrimary(color.black(" Environment Variable Setup ")),
|
|
3779
|
+
`${color.gray("│")} ${clickableEnvPath}${color.gray(` · ${variableLabel}`)}`,
|
|
3780
|
+
`${color.gray("│")} `
|
|
3781
|
+
].join("\n");
|
|
3782
|
+
this.output.write(`\n${color.gray(S_BAR_START)} ${headerBody}\n`);
|
|
3783
|
+
}
|
|
3784
|
+
toHyperlink(text, url) {
|
|
3785
|
+
const escapeCode = "\x1B";
|
|
3786
|
+
const oscSequence = `${escapeCode}]8;;`;
|
|
3787
|
+
const stringTerminator = `${escapeCode}\\`;
|
|
3788
|
+
return `${oscSequence}${url}${stringTerminator}${text}${oscSequence}${stringTerminator}`;
|
|
3789
|
+
}
|
|
3790
|
+
};
|
|
3791
|
+
|
|
3792
|
+
//#endregion
|
|
3793
|
+
//#region ../prompt/src/options/resolve.ts
|
|
3794
|
+
function resolveTheme(themeOption) {
|
|
3795
|
+
return new Theme(themeOption ?? color.magenta);
|
|
3796
|
+
}
|
|
3797
|
+
function resolveRootDirectory(rootOption) {
|
|
3798
|
+
if (!rootOption) return;
|
|
3799
|
+
if (rootOption.startsWith("file://")) return dirname(fileURLToPath(rootOption));
|
|
3800
|
+
return rootOption;
|
|
3801
|
+
}
|
|
3802
|
+
function resolveEnvFilePath(pathOption, rootDir) {
|
|
3803
|
+
if (!rootDir) return pathOption;
|
|
3804
|
+
return resolve(rootDir, pathOption);
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
//#endregion
|
|
3808
|
+
//#region ../prompt/src/utils/isEnvChannel.ts
|
|
3809
|
+
/**
|
|
3810
|
+
* Type guard to check if a value is an EnvChannel instance
|
|
3811
|
+
*/
|
|
3812
|
+
function isEnvChannel(value) {
|
|
3813
|
+
return typeof value === "object" && value !== null && "get" in value && "set" in value && typeof value.get === "function" && typeof value.set === "function";
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
//#endregion
|
|
3817
|
+
//#region ../prompt/src/prompt.ts
|
|
3818
|
+
/**
|
|
3819
|
+
* Interactive CLI tool to generate .env files with schema validation
|
|
3820
|
+
*/
|
|
3821
|
+
async function prompt(modelOrOptions, interactiveOptions) {
|
|
3822
|
+
let schemas;
|
|
3823
|
+
let preprocessConfig;
|
|
3824
|
+
let finalOptions;
|
|
3825
|
+
if (modelOrOptions instanceof EnvModel) {
|
|
3826
|
+
const model = modelOrOptions;
|
|
3827
|
+
finalOptions = interactiveOptions || {};
|
|
3828
|
+
schemas = model.schemas;
|
|
3829
|
+
preprocessConfig = model.preprocess;
|
|
3830
|
+
} else {
|
|
3831
|
+
const options = modelOrOptions;
|
|
3832
|
+
finalOptions = options;
|
|
3833
|
+
if (options.schemas) {
|
|
3834
|
+
schemas = resolveSchemas(options.schemas);
|
|
3835
|
+
preprocessConfig = options.preprocess;
|
|
3836
|
+
} else throw new Error("Either provide a model or schemas in options");
|
|
3837
|
+
}
|
|
3838
|
+
const rootDirectory = resolveRootDirectory(finalOptions.root);
|
|
3839
|
+
const path = resolveEnvFilePath(finalOptions.path ?? ENV_PATH, rootDirectory);
|
|
3840
|
+
const truncate = finalOptions.truncate ?? TRUNCATE_LENGTH;
|
|
3841
|
+
const secrets = finalOptions.secrets ?? SECRET_PATTERNS;
|
|
3842
|
+
const input = stdin;
|
|
3843
|
+
const output = stdout;
|
|
3844
|
+
const channel = isEnvChannel(finalOptions.channel) ? finalOptions.channel : resolveChannel(finalOptions.channel, path);
|
|
3845
|
+
const theme = resolveTheme(finalOptions.theme);
|
|
3846
|
+
await new Session({
|
|
3847
|
+
schemas,
|
|
3848
|
+
channel,
|
|
3849
|
+
secrets,
|
|
3850
|
+
truncate,
|
|
3851
|
+
theme,
|
|
3852
|
+
input,
|
|
3853
|
+
output,
|
|
3854
|
+
path,
|
|
3855
|
+
preprocess: preprocessConfig
|
|
3856
|
+
}).run();
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
//#endregion
|
|
3860
|
+
export { set as A, EnvVarSchemaBase as C, DotEnvChannel as D, DotEnvXChannel as E, DefaultEnvChannel as O, stringProcessor as S, ProcessEnvChannel as T, BooleanEnvVarSchema as _, resolveSchema as a, numberProcessor as b, booleanPreprocessor as c, preprocessors as d, stringPreprocessor as f, EnumEnvVarSchema as g, NumberEnvVarSchema as h, EnvModel as i, get as j, EnvSource as k, enumPreprocessor as l, StringEnvVarSchema as m, defaults_exports as n, resolveSchemas as o, isEnvVarSchema as p, define as r, resolvePreprocessor as s, prompt as t, numberPreprocessor as u, booleanProcessor as v, resolveChannel as w, processors as x, enumProcessor as y };
|