varlock 0.0.0 → 0.0.2

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.
Files changed (70) hide show
  1. package/README.md +46 -0
  2. package/bin/cli.js +2 -0
  3. package/dist/auto-load.d.ts +2 -0
  4. package/dist/auto-load.js +11 -0
  5. package/dist/auto-load.js.map +1 -0
  6. package/dist/chunk-332HXE6L.js +53 -0
  7. package/dist/chunk-332HXE6L.js.map +1 -0
  8. package/dist/chunk-33ROL4J5.js +1013 -0
  9. package/dist/chunk-33ROL4J5.js.map +1 -0
  10. package/dist/chunk-4WO4BGKU.js +70 -0
  11. package/dist/chunk-4WO4BGKU.js.map +1 -0
  12. package/dist/chunk-6YLXKXKR.js +225 -0
  13. package/dist/chunk-6YLXKXKR.js.map +1 -0
  14. package/dist/chunk-7UQXFWKN.js +82 -0
  15. package/dist/chunk-7UQXFWKN.js.map +1 -0
  16. package/dist/chunk-B4UBSMSZ.js +142 -0
  17. package/dist/chunk-B4UBSMSZ.js.map +1 -0
  18. package/dist/chunk-CPA2D42B.js +146 -0
  19. package/dist/chunk-CPA2D42B.js.map +1 -0
  20. package/dist/chunk-DAZNZPLN.js +675 -0
  21. package/dist/chunk-DAZNZPLN.js.map +1 -0
  22. package/dist/chunk-G4BD2BPH.js +2310 -0
  23. package/dist/chunk-G4BD2BPH.js.map +1 -0
  24. package/dist/chunk-LQZ6ICSS.js +21 -0
  25. package/dist/chunk-LQZ6ICSS.js.map +1 -0
  26. package/dist/chunk-O2QPQ5MG.js +98 -0
  27. package/dist/chunk-O2QPQ5MG.js.map +1 -0
  28. package/dist/chunk-QCC3P7BT.js +39 -0
  29. package/dist/chunk-QCC3P7BT.js.map +1 -0
  30. package/dist/chunk-RCHPHIHX.js +15 -0
  31. package/dist/chunk-RCHPHIHX.js.map +1 -0
  32. package/dist/chunk-RY3YIJFG.js +681 -0
  33. package/dist/chunk-RY3YIJFG.js.map +1 -0
  34. package/dist/chunk-XCFZJA7V.js +32 -0
  35. package/dist/chunk-XCFZJA7V.js.map +1 -0
  36. package/dist/chunk-XN24GZXQ.js +35 -0
  37. package/dist/chunk-XN24GZXQ.js.map +1 -0
  38. package/dist/chunk-Z55UMN2B.js +69 -0
  39. package/dist/chunk-Z55UMN2B.js.map +1 -0
  40. package/dist/cli/cli-executable.d.ts +2 -0
  41. package/dist/cli/cli-executable.js +205 -0
  42. package/dist/cli/cli-executable.js.map +1 -0
  43. package/dist/doctor.command-SHZI7SR3.js +6 -0
  44. package/dist/doctor.command-SHZI7SR3.js.map +1 -0
  45. package/dist/dotenv-compat.d.ts +2 -0
  46. package/dist/dotenv-compat.js +11 -0
  47. package/dist/dotenv-compat.js.map +1 -0
  48. package/dist/en-US-TSGNDI2P.js +22 -0
  49. package/dist/en-US-TSGNDI2P.js.map +1 -0
  50. package/dist/encrypt.command-VGJABHNK.js +6 -0
  51. package/dist/encrypt.command-VGJABHNK.js.map +1 -0
  52. package/dist/help.command-YZDL2VEQ.js +5 -0
  53. package/dist/help.command-YZDL2VEQ.js.map +1 -0
  54. package/dist/index.d.ts +37 -0
  55. package/dist/index.js +8 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/init.command-ERPGU436.js +10 -0
  58. package/dist/init.command-ERPGU436.js.map +1 -0
  59. package/dist/ja-JP-UBPCQAAD.js +22 -0
  60. package/dist/ja-JP-UBPCQAAD.js.map +1 -0
  61. package/dist/load.command-LQDSWJSK.js +9 -0
  62. package/dist/load.command-LQDSWJSK.js.map +1 -0
  63. package/dist/login.command-22RUZJLR.js +7 -0
  64. package/dist/login.command-22RUZJLR.js.map +1 -0
  65. package/dist/opt-out.command-Y4KUQ6PQ.js +5 -0
  66. package/dist/opt-out.command-Y4KUQ6PQ.js.map +1 -0
  67. package/dist/run.command-OF3UV7AD.js +9 -0
  68. package/dist/run.command-OF3UV7AD.js.map +1 -0
  69. package/package.json +50 -3
  70. package/notes.md +0 -3
@@ -0,0 +1,681 @@
1
+ import { ve, pD, dD, SD, LD } from './chunk-DAZNZPLN.js';
2
+ import { logLines, fmt } from './chunk-LQZ6ICSS.js';
3
+ import { define } from './chunk-33ROL4J5.js';
4
+ import { joinAndCompact, loadVarlockEnvGraph } from './chunk-6YLXKXKR.js';
5
+ import { my_dash_default, StaticValueResolver, DotEnvFileDataSource, checkIsFileGitIgnored } from './chunk-G4BD2BPH.js';
6
+ import { ansis_default } from './chunk-7UQXFWKN.js';
7
+ import { __name } from './chunk-XN24GZXQ.js';
8
+ import path from 'node:path';
9
+ import fs3, { access } from 'node:fs/promises';
10
+ import { envSpecUpdater, ParsedEnvSpecStaticValue, parseEnvSpecDotEnvFile } from '@env-spec/parser';
11
+ import fs2, { accessSync, existsSync } from 'node:fs';
12
+ import { WriteStream } from 'node:tty';
13
+ import process2 from 'node:process';
14
+ import { execSync } from 'node:child_process';
15
+
16
+ async function pathExists(p) {
17
+ try {
18
+ await access(p);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ __name(pathExists, "pathExists");
25
+ function pathExistsSync(p) {
26
+ try {
27
+ accessSync(p);
28
+ return true;
29
+ } catch {
30
+ return false;
31
+ }
32
+ }
33
+ __name(pathExistsSync, "pathExistsSync");
34
+ function isUnicodeSupported() {
35
+ const { env } = process2;
36
+ const { TERM, TERM_PROGRAM } = env;
37
+ if (process2.platform !== "win32") {
38
+ return TERM !== "linux";
39
+ }
40
+ return Boolean(env.WT_SESSION) || Boolean(env.TERMINUS_SUBLIME) || env.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
41
+ }
42
+ __name(isUnicodeSupported, "isUnicodeSupported");
43
+
44
+ // src/cli/helpers/prompts.ts
45
+ var unicode = isUnicodeSupported();
46
+ var unicodeOr = /* @__PURE__ */ __name((c, fallback) => unicode ? c : fallback, "unicodeOr");
47
+ var S_STEP_ACTIVE = unicodeOr("\u25C6", "*");
48
+ var S_STEP_CANCEL = unicodeOr("\u25A0", "x");
49
+ var S_STEP_ERROR = unicodeOr("\u25B2", "x");
50
+ var S_STEP_SUBMIT = unicodeOr("\u25C7", "o");
51
+ unicodeOr("\u250C", "T");
52
+ var S_BAR = unicodeOr("\u2502", "|");
53
+ var S_BAR_END = unicodeOr("\u2514", "\u2014");
54
+ var S_RADIO_ACTIVE = unicodeOr("\u25CF", ">");
55
+ var S_RADIO_INACTIVE = unicodeOr("\u25CB", " ");
56
+ var S_CHECKBOX_ACTIVE = unicodeOr("\u25FB", "[\u2022]");
57
+ var S_CHECKBOX_SELECTED = unicodeOr("\u25FC", "[+]");
58
+ var S_CHECKBOX_INACTIVE = unicodeOr("\u25FB", "[ ]");
59
+ unicodeOr("\u25AA", "\u2022");
60
+ unicodeOr("\u2500", "-");
61
+ unicodeOr("\u256E", "+");
62
+ unicodeOr("\u251C", "+");
63
+ unicodeOr("\u256F", "+");
64
+ unicodeOr("\u25CF", "\u2022");
65
+ unicodeOr("\u25C6", "*");
66
+ unicodeOr("\u25B2", "!");
67
+ unicodeOr("\u25A0", "x");
68
+ var symbol = /* @__PURE__ */ __name((state) => {
69
+ switch (state) {
70
+ case "initial":
71
+ case "active":
72
+ return ansis_default.cyan(S_STEP_ACTIVE);
73
+ case "cancel":
74
+ return ansis_default.red(S_STEP_CANCEL);
75
+ case "error":
76
+ return ansis_default.yellow(S_STEP_ERROR);
77
+ case "submit":
78
+ return ansis_default.green(S_STEP_SUBMIT);
79
+ }
80
+ }, "symbol");
81
+ var limitOptions = /* @__PURE__ */ __name((params) => {
82
+ const { cursor, options, style } = params;
83
+ const output = params.output ?? process.stdout;
84
+ const rows = output instanceof WriteStream && output.rows !== void 0 ? output.rows : 10;
85
+ const paramMaxItems = params.maxItems ?? Number.POSITIVE_INFINITY;
86
+ const outputMaxItems = Math.max(rows - 4, 0);
87
+ const maxItems = Math.min(outputMaxItems, Math.max(paramMaxItems, 5));
88
+ let slidingWindowLocation = 0;
89
+ if (cursor >= slidingWindowLocation + maxItems - 3) {
90
+ slidingWindowLocation = Math.max(Math.min(cursor - maxItems + 3, options.length - maxItems), 0);
91
+ } else if (cursor < slidingWindowLocation + 2) {
92
+ slidingWindowLocation = Math.max(cursor - 2, 0);
93
+ }
94
+ const shouldRenderTopEllipsis = maxItems < options.length && slidingWindowLocation > 0;
95
+ const shouldRenderBottomEllipsis = maxItems < options.length && slidingWindowLocation + maxItems < options.length;
96
+ return options.slice(slidingWindowLocation, slidingWindowLocation + maxItems).map((option, i, arr) => {
97
+ const isTopLimit = i === 0 && shouldRenderTopEllipsis;
98
+ const isBottomLimit = i === arr.length - 1 && shouldRenderBottomEllipsis;
99
+ return isTopLimit || isBottomLimit ? ansis_default.dim("...") : style(option, i + slidingWindowLocation === cursor);
100
+ });
101
+ }, "limitOptions");
102
+ var confirm = /* @__PURE__ */ __name((opts) => {
103
+ const active = opts.active ?? "Yes";
104
+ const inactive = opts.inactive ?? "No";
105
+ return new dD({
106
+ active,
107
+ inactive,
108
+ input: opts.input,
109
+ output: opts.output,
110
+ initialValue: opts.initialValue ?? true,
111
+ render() {
112
+ const title = `
113
+ ${symbol(this.state)} ${opts.message}
114
+ `;
115
+ const value = this.value ? active : inactive;
116
+ switch (this.state) {
117
+ case "submit":
118
+ return `${title}\u200E ${ansis_default.dim(value)}`;
119
+ case "cancel":
120
+ return `${title}\u200E ${ansis_default.strikethrough(
121
+ ansis_default.dim(value)
122
+ )}
123
+ `;
124
+ default: {
125
+ return `${title}\u200E ${this.value ? `${ansis_default.green(S_RADIO_ACTIVE)} ${active}` : `${ansis_default.dim(S_RADIO_INACTIVE)} ${ansis_default.dim(active)}`} ${ansis_default.dim("/")} ${!this.value ? `${ansis_default.green(S_RADIO_ACTIVE)} ${inactive}` : `${ansis_default.dim(S_RADIO_INACTIVE)} ${ansis_default.dim(inactive)}`}
126
+ `;
127
+ }
128
+ }
129
+ }
130
+ }).prompt();
131
+ }, "confirm");
132
+ var select = /* @__PURE__ */ __name((opts) => {
133
+ const opt = /* @__PURE__ */ __name((option, state) => {
134
+ const label = option.label ?? String(option.value);
135
+ switch (state) {
136
+ case "selected":
137
+ return `${ansis_default.dim(label)}`;
138
+ case "active":
139
+ return `${ansis_default.green(S_RADIO_ACTIVE)} ${label} ${option.hint ? ansis_default.dim(`(${option.hint})`) : ""}`;
140
+ case "cancelled":
141
+ return `${ansis_default.strikethrough(ansis_default.dim(label))}`;
142
+ default:
143
+ return `${ansis_default.dim(S_RADIO_INACTIVE)} ${ansis_default.dim(label)}`;
144
+ }
145
+ }, "opt");
146
+ return new LD({
147
+ options: opts.options,
148
+ input: opts.input,
149
+ output: opts.output,
150
+ initialValue: opts.initialValue,
151
+ render() {
152
+ const title = `${ansis_default.gray(S_BAR)}
153
+ ${symbol(this.state)} ${opts.message}
154
+ `;
155
+ switch (this.state) {
156
+ case "submit":
157
+ return `${title}${ansis_default.gray(S_BAR)} ${opt(this.options[this.cursor], "selected")}`;
158
+ case "cancel":
159
+ return `${title}${ansis_default.gray(S_BAR)} ${opt(
160
+ this.options[this.cursor],
161
+ "cancelled"
162
+ )}
163
+ ${ansis_default.gray(S_BAR)}`;
164
+ default: {
165
+ return `${title}${ansis_default.cyan(S_BAR)} ${limitOptions({
166
+ output: opts.output,
167
+ cursor: this.cursor,
168
+ options: this.options,
169
+ maxItems: opts.maxItems,
170
+ style: /* @__PURE__ */ __name((item, active) => opt(item, active ? "active" : "inactive"), "style")
171
+ }).join(`
172
+ ${ansis_default.cyan(S_BAR)} `)}
173
+ ${ansis_default.cyan(S_BAR_END)}
174
+ `;
175
+ }
176
+ }
177
+ }
178
+ }).prompt();
179
+ }, "select");
180
+ var multiselect = /* @__PURE__ */ __name((opts) => {
181
+ const opt = /* @__PURE__ */ __name((option, state) => {
182
+ const label = option.label ?? String(option.value);
183
+ if (state === "active") {
184
+ return `${ansis_default.cyan(S_CHECKBOX_ACTIVE)} ${label} ${option.hint ? ansis_default.dim(`(${option.hint})`) : ""}`;
185
+ }
186
+ if (state === "selected") {
187
+ return `${ansis_default.green(S_CHECKBOX_SELECTED)} ${ansis_default.dim(label)} ${option.hint ? ansis_default.dim(`(${option.hint})`) : ""}`;
188
+ }
189
+ if (state === "cancelled") {
190
+ return `${ansis_default.strikethrough(ansis_default.dim(label))}`;
191
+ }
192
+ if (state === "active-selected") {
193
+ return `${ansis_default.green(S_CHECKBOX_SELECTED)} ${label} ${option.hint ? ansis_default.dim(`(${option.hint})`) : ""}`;
194
+ }
195
+ if (state === "submitted") {
196
+ return `${ansis_default.dim(label)}`;
197
+ }
198
+ return `${ansis_default.dim(S_CHECKBOX_INACTIVE)} ${ansis_default.dim(label)}`;
199
+ }, "opt");
200
+ return new SD({
201
+ options: opts.options,
202
+ input: opts.input,
203
+ output: opts.output,
204
+ initialValues: opts.initialValues,
205
+ required: opts.required ?? true,
206
+ cursorAt: opts.cursorAt,
207
+ validate(selected) {
208
+ if (this.required && selected.length === 0) {
209
+ return `Please select at least one option.
210
+ ${ansis_default.reset(
211
+ ansis_default.dim(
212
+ `Press ${ansis_default.gray(ansis_default.bgWhite(ansis_default.inverse(" space ")))} to select, ${ansis_default.gray(
213
+ ansis_default.bgWhite(ansis_default.inverse(" enter "))
214
+ )} to submit`
215
+ )
216
+ )}`;
217
+ }
218
+ },
219
+ render() {
220
+ let title = `${ansis_default.gray(S_BAR)}
221
+ ${symbol(this.state)} ${opts.message}
222
+ `;
223
+ if (opts.details) title += `${ansis_default.gray(S_BAR)} ${opts.details}
224
+ `;
225
+ const styleOption = /* @__PURE__ */ __name((option, active) => {
226
+ const selected = this.value.includes(option.value);
227
+ if (active && selected) {
228
+ return opt(option, "active-selected");
229
+ }
230
+ if (selected) {
231
+ return opt(option, "selected");
232
+ }
233
+ return opt(option, active ? "active" : "inactive");
234
+ }, "styleOption");
235
+ switch (this.state) {
236
+ case "submit": {
237
+ return `${title}${ansis_default.gray(S_BAR)} ${this.options.filter(({ value }) => this.value.includes(value)).map((option) => opt(option, "submitted")).join(ansis_default.dim(", ")) || ansis_default.dim("none")}`;
238
+ }
239
+ case "cancel": {
240
+ const label = this.options.filter(({ value }) => this.value.includes(value)).map((option) => opt(option, "cancelled")).join(ansis_default.dim(", "));
241
+ return `${title}${ansis_default.gray(S_BAR)} ${label.trim() ? `${label}
242
+ ${ansis_default.gray(S_BAR)}` : ""}`;
243
+ }
244
+ case "error": {
245
+ const footer = this.error.split("\n").map((ln, i) => i === 0 ? `${ansis_default.yellow(S_BAR_END)} ${ansis_default.yellow(ln)}` : ` ${ln}`).join("\n");
246
+ return `${title + ansis_default.yellow(S_BAR)} ${limitOptions({
247
+ output: opts.output,
248
+ options: this.options,
249
+ cursor: this.cursor,
250
+ maxItems: opts.maxItems,
251
+ style: styleOption
252
+ }).join(`
253
+ ${ansis_default.yellow(S_BAR)} `)}
254
+ ${footer}
255
+ `;
256
+ }
257
+ default: {
258
+ return `${title}${ansis_default.cyan(S_BAR)} ${limitOptions({
259
+ output: opts.output,
260
+ options: this.options,
261
+ cursor: this.cursor,
262
+ maxItems: opts.maxItems,
263
+ style: styleOption
264
+ }).join(`
265
+ ${ansis_default.cyan(S_BAR)} `)}
266
+ ${ansis_default.cyan(S_BAR_END)}
267
+ `;
268
+ }
269
+ }
270
+ }
271
+ }).prompt();
272
+ }, "multiselect");
273
+ var prompts = {
274
+ confirm,
275
+ select,
276
+ multiselect
277
+ };
278
+ var prompts_default = prompts;
279
+ var PUBLIC_PREFIXES = [
280
+ "PUBLIC",
281
+ "VITE",
282
+ "NEXT_PUBLIC",
283
+ "NUXT_PUBLIC"
284
+ ];
285
+ var PUBLIC_KEYWORDS = ["PUBLIC"];
286
+ var SENSITIVE_KEYWORDS = [
287
+ "SECRET",
288
+ "API_KEY",
289
+ "PASSWORD",
290
+ "TOKEN",
291
+ "PRIVATE",
292
+ "CREDENTIALS"
293
+ ];
294
+ function isValidUrl(val) {
295
+ try {
296
+ const u = new URL(val);
297
+ return true;
298
+ } catch (err) {
299
+ return false;
300
+ }
301
+ }
302
+ __name(isValidUrl, "isValidUrl");
303
+ var EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
304
+ var VALID_NUMBER_REGEX = /^(0|([1-9][0-9]*))?(\.[0-9]+)?$/;
305
+ function inferItemDecorators(file, itemKey, valueStr) {
306
+ let itemIsPublic = false;
307
+ if (PUBLIC_PREFIXES.some((prefix) => itemKey.startsWith(prefix))) itemIsPublic = true;
308
+ if (PUBLIC_KEYWORDS.some((keyword) => itemKey.includes(keyword))) itemIsPublic = true;
309
+ let itemIsSensitive = false;
310
+ if (SENSITIVE_KEYWORDS.some((keyword) => itemKey.includes(keyword))) itemIsSensitive = true;
311
+ if (itemIsPublic) ; else if (itemIsSensitive) {
312
+ envSpecUpdater.setItemDecorator(file, itemKey, "sensitive", "true");
313
+ }
314
+ if (itemKey === "PORT" || itemKey.endsWith("_PORT")) {
315
+ envSpecUpdater.setItemDecorator(file, itemKey, "type", "port");
316
+ } else if (itemKey.endsWith("_EMAIL")) {
317
+ envSpecUpdater.setItemDecorator(file, itemKey, "type", "email");
318
+ } else if (itemKey.endsWith("_URL") || itemKey.endsWith("_URI")) {
319
+ envSpecUpdater.setItemDecorator(file, itemKey, "type", "url");
320
+ } else if (valueStr) {
321
+ if (valueStr.startsWith("<") && valueStr.endsWith(">")) {
322
+ envSpecUpdater.setItemDecorator(file, itemKey, "example", valueStr);
323
+ }
324
+ if (valueStr === "true" || valueStr === "false") {
325
+ envSpecUpdater.setItemDecorator(file, itemKey, "type", "boolean");
326
+ } else if (EMAIL_REGEX.test(valueStr)) {
327
+ envSpecUpdater.setItemDecorator(file, itemKey, "type", "email");
328
+ } else if (valueStr !== "0" && valueStr !== "1" && VALID_NUMBER_REGEX.test(valueStr)) {
329
+ envSpecUpdater.setItemDecorator(file, itemKey, "type", "number");
330
+ } else if (isValidUrl(valueStr)) {
331
+ envSpecUpdater.setItemDecorator(file, itemKey, "type", "url");
332
+ }
333
+ }
334
+ }
335
+ __name(inferItemDecorators, "inferItemDecorators");
336
+ function inferSchemaUpdates(file) {
337
+ for (const item of file.configItems) {
338
+ const valueStr = item.value instanceof ParsedEnvSpecStaticValue && item.value.value?.toString() || "";
339
+ inferItemDecorators(file, item.key, valueStr);
340
+ }
341
+ }
342
+ __name(inferSchemaUpdates, "inferSchemaUpdates");
343
+ function ensureAllItemsExist(envGraph, schemaFile) {
344
+ const addedItemKeys = [];
345
+ for (const itemKey in envGraph.configSchema) {
346
+ const item = envGraph.configSchema[itemKey];
347
+ const itemInSchema = schemaFile.configItems.find((i) => i.key === itemKey);
348
+ if (!itemInSchema) {
349
+ if (addedItemKeys.length === 0) {
350
+ envSpecUpdater.injectFromStr(schemaFile, [
351
+ "",
352
+ "# items added to schema by `varlock init`",
353
+ "# that were missing in example, but detected in other env files",
354
+ "# PLEASE REVIEW THESE!",
355
+ "# ---",
356
+ ""
357
+ ].join("\n"), { location: "end" });
358
+ }
359
+ addedItemKeys.push(itemKey);
360
+ envSpecUpdater.injectFromStr(schemaFile, [`${itemKey}=`].join("\n"));
361
+ const itemValue = item.valueResolver instanceof StaticValueResolver && item.valueResolver.staticValue || "";
362
+ inferItemDecorators(schemaFile, itemKey, String(itemValue));
363
+ }
364
+ }
365
+ }
366
+ __name(ensureAllItemsExist, "ensureAllItemsExist");
367
+ async function detectRedundantValues(envGraph, opts = {}) {
368
+ const schema = envGraph.schemaDataSource;
369
+ if (!schema) return {};
370
+ const redundantItemsBySourcePath = {};
371
+ const schemaValues = schema.getStaticValues();
372
+ for (const source of envGraph.dataSources) {
373
+ if (source === schema) continue;
374
+ if (source.type === "example") continue;
375
+ if (!(source instanceof DotEnvFileDataSource) || !source.parsedFile) continue;
376
+ const sourceValues = source.getStaticValues();
377
+ for (const [key, value] of Object.entries(sourceValues)) {
378
+ if (schemaValues[key] !== value) continue;
379
+ redundantItemsBySourcePath[source.fullPath] ||= [];
380
+ redundantItemsBySourcePath[source.fullPath].push(key);
381
+ if (opts.delete) {
382
+ envSpecUpdater.deleteItem(source.parsedFile, key);
383
+ }
384
+ }
385
+ if (opts.delete) {
386
+ await fs3.writeFile(source.fullPath, source.parsedFile.toString(), "utf8");
387
+ }
388
+ }
389
+ return redundantItemsBySourcePath;
390
+ }
391
+ __name(detectRedundantValues, "detectRedundantValues");
392
+
393
+ // src/cli/helpers/exit-error.ts
394
+ var CliExitError = class extends Error {
395
+ constructor(message, more) {
396
+ super(message);
397
+ this.more = more;
398
+ }
399
+ static {
400
+ __name(this, "CliExitError");
401
+ }
402
+ get forceExit() {
403
+ return !!this.more?.forceExit;
404
+ }
405
+ getFormattedOutput() {
406
+ let msg = `
407
+ \u{1F4A5} ${ansis_default.red(this.message)} \u{1F4A5}
408
+ `;
409
+ if (this.more?.details) {
410
+ msg += joinAndCompact(my_dash_default.castArray(this.more?.details), "\n");
411
+ }
412
+ if (this.more?.suggestion) {
413
+ msg += joinAndCompact(my_dash_default.castArray(this.more?.suggestion), "\n");
414
+ }
415
+ msg += "\n";
416
+ return msg;
417
+ }
418
+ };
419
+ var JS_PACKAGE_MANAGERS = Object.freeze({
420
+ npm: {
421
+ name: "npm",
422
+ lockfile: "package-lock.json",
423
+ add: "npm install",
424
+ // add also works
425
+ exec: "npm exec --",
426
+ dlx: "npx"
427
+ },
428
+ pnpm: {
429
+ name: "pnpm",
430
+ lockfile: "pnpm-lock.yaml",
431
+ add: "pnpm add",
432
+ exec: "pnpm exec",
433
+ dlx: "pnpm dlx"
434
+ },
435
+ yarn: {
436
+ name: "yarn",
437
+ lockfile: "yarn.lock",
438
+ add: "yarn add",
439
+ exec: "yarn exec --",
440
+ dlx: "yarn dlx"
441
+ },
442
+ bun: {
443
+ name: "bun",
444
+ lockfile: "bun.lockb",
445
+ add: "bun add",
446
+ exec: "bun run",
447
+ dlx: "bunx"
448
+ },
449
+ deno: {
450
+ //! deno not fully supported yet
451
+ name: "deno",
452
+ lockfile: "deno.lock",
453
+ add: "deno add",
454
+ // TODO: don't think these are quite right...
455
+ exec: "deno run",
456
+ dlx: "deno run"
457
+ }
458
+ });
459
+ function detectJsPackageManager(opts) {
460
+ let cwd = opts?.cwd || process.cwd();
461
+ const cwdParts = cwd.split(path.sep);
462
+ do {
463
+ let pm;
464
+ let detectedPm;
465
+ for (pm in JS_PACKAGE_MANAGERS) {
466
+ const lockFilePath = path.join(
467
+ cwd,
468
+ JS_PACKAGE_MANAGERS[pm].lockfile
469
+ );
470
+ if (pathExistsSync(lockFilePath)) {
471
+ if (detectedPm) {
472
+ throw new CliExitError("Found multiple js package manager lockfiles", {
473
+ details: `${JS_PACKAGE_MANAGERS[pm].lockfile} and ${JS_PACKAGE_MANAGERS[detectedPm].lockfile}`,
474
+ forceExit: true
475
+ });
476
+ }
477
+ detectedPm = pm;
478
+ }
479
+ }
480
+ if (detectedPm) return JS_PACKAGE_MANAGERS[detectedPm];
481
+ cwdParts.pop();
482
+ cwd = path.join(...cwdParts);
483
+ if (opts?.workspaceRootPath) {
484
+ if (opts.workspaceRootPath === cwd) break;
485
+ } else {
486
+ if (pathExistsSync(path.join(cwd, ".git"))) break;
487
+ }
488
+ } while (cwd);
489
+ if (process.env.npm_config_user_agent) {
490
+ const pmFromAgent = process.env.npm_config_user_agent.split("/")[0];
491
+ if (Object.keys(JS_PACKAGE_MANAGERS).includes(pmFromAgent)) {
492
+ return JS_PACKAGE_MANAGERS[pmFromAgent];
493
+ }
494
+ }
495
+ if (opts?.exitIfNotFound) {
496
+ throw new CliExitError("Unable to find detect your JavaScript package manager!", {
497
+ suggestion: "We look for lock files (ex: package-lock.json) so you may just need to run a dependency install (ie `npm install`)",
498
+ forceExit: true
499
+ });
500
+ }
501
+ }
502
+ __name(detectJsPackageManager, "detectJsPackageManager");
503
+ function installJsDependency(opts) {
504
+ const packageJsonPath = path.join(opts.packagePath || process.cwd(), "package.json");
505
+ if (!existsSync(packageJsonPath)) return false;
506
+ const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
507
+ if (packageJson.dependencies?.varlock) return false;
508
+ execSync([
509
+ opts.packagePath ? `cd ${opts.packagePath} &&` : "",
510
+ // `add` works in all of them
511
+ `${opts.packageManager} add ${opts.packageName}`,
512
+ opts.isMonoRepoRoot && opts.packageManager === "pnpm" ? "-w" : ""
513
+ ].join(" "));
514
+ return true;
515
+ }
516
+ __name(installJsDependency, "installJsDependency");
517
+
518
+ // src/cli/commands/init.command.ts
519
+ var commandSpec = define({
520
+ name: "init",
521
+ description: "Set up varlock in the current project",
522
+ args: {}
523
+ });
524
+ var commandFn = /* @__PURE__ */ __name(async (ctx) => {
525
+ {
526
+ console.log("\u{1F9D9} Hello and welcome to Varlock \u{1F512}\u{1F525}\u2728");
527
+ }
528
+ let envGraph = await loadVarlockEnvGraph();
529
+ const existingSchemaFile = envGraph.dataSources.find((dataSource) => {
530
+ return dataSource.type === "schema";
531
+ });
532
+ if (existingSchemaFile) {
533
+ logLines([
534
+ `It looks like you already have a ${fmt.fileName(".env.schema")} file \u{1F389}`,
535
+ "This init helper is meant to help you get a new project set up.",
536
+ "If you need to make changes to your schema or values, you can update your files directly.",
537
+ "See more docs at https://varlock.dev/guides/schema"
538
+ ]);
539
+ } else {
540
+ let exampleFileToConvert = null;
541
+ const allExampleFiles = envGraph.dataSources.filter((dataSource) => {
542
+ return dataSource instanceof DotEnvFileDataSource && dataSource.type === "example";
543
+ });
544
+ if (allExampleFiles.length === 1) {
545
+ exampleFileToConvert = allExampleFiles[0];
546
+ } else if (allExampleFiles.length > 1) {
547
+ console.log("");
548
+ const selectedExample = await ve({
549
+ message: `We detected more than one example .env file. Which one should we use to create your new ${fmt.fileName(".env.schema")}?`,
550
+ options: allExampleFiles.map((file) => ({
551
+ label: file.fileName,
552
+ value: file
553
+ }))
554
+ });
555
+ if (pD(selectedExample)) process.exit(0);
556
+ exampleFileToConvert = selectedExample;
557
+ }
558
+ const parsedEnvFile = exampleFileToConvert?.parsedFile || parseEnvSpecDotEnvFile("");
559
+ if (!parsedEnvFile) throw new Error("No parsed .env file found");
560
+ envSpecUpdater.ensureHeader(parsedEnvFile, [
561
+ "This env file uses @env-spec - see https://varlock.dev/env-spec for more info",
562
+ ""
563
+ // TODO: add env spec version? real links?
564
+ ].join("\n"));
565
+ envSpecUpdater.setRootDecorator(parsedEnvFile, "defaultRequired", "false", { explicitTrue: true });
566
+ envSpecUpdater.setRootDecorator(parsedEnvFile, "defaultSensitive", "false", { explicitTrue: true });
567
+ envSpecUpdater.setRootDecorator(parsedEnvFile, "generateTypes", "lang=ts, path=env.d.ts", { bareFnArgs: true });
568
+ envSpecUpdater.injectFromStr(parsedEnvFile, [
569
+ "",
570
+ "# example env variable injected by `varlock init`",
571
+ '# @required @sensitive @example="example value"',
572
+ 'EXAMPLE_ITEM="delete me!"',
573
+ ""
574
+ ].join("\n"), { location: "after_header" });
575
+ inferSchemaUpdates(parsedEnvFile);
576
+ ensureAllItemsExist(envGraph, parsedEnvFile);
577
+ const schemaFilePath = path.join(process.cwd(), ".env.schema");
578
+ await fs3.writeFile(schemaFilePath, parsedEnvFile.toString());
579
+ if (exampleFileToConvert) {
580
+ logLines([
581
+ "",
582
+ `Your ${fmt.fileName(exampleFileToConvert.fileName)} has been used to generate your new ${fmt.fileName(".env.schema")}:`,
583
+ fmt.filePath(schemaFilePath)
584
+ ]);
585
+ } else {
586
+ logLines([
587
+ "",
588
+ `Your new ${fmt.fileName(".env.schema")} file has been created:`,
589
+ fmt.filePath(schemaFilePath)
590
+ ]);
591
+ }
592
+ if (await checkIsFileGitIgnored(schemaFilePath)) {
593
+ logLines([ansis_default.dim(`(and updated ${fmt.fileName(".gitignore")} to ensure it will be tracked by git)`)]);
594
+ await fs3.appendFile(".gitignore", "\n!.env.schema");
595
+ }
596
+ logLines([
597
+ "",
598
+ ansis_default.bold(`\u{1F6A7} Please review and update your new ${fmt.fileName(".env.schema")} file! \u{1F6A7}`),
599
+ `We've done our best to get you started, but you must review and make sure it is correct!`,
600
+ "",
601
+ `\u{1F449} Some helpful pointers to get you started:`,
602
+ `- use ${fmt.decorator("@required")} (or ${fmt.decorator("@optional")}) to tag items that should fail validation when empty`,
603
+ `- use ${fmt.decorator("@sensitive")} to tag items that contain sensitive secrets, and must be handled accordingly`,
604
+ `- use ${fmt.decorator("@type")} to set an item's data type (if not a basic string), which affects validation and coercion logic`,
605
+ `- if an item value is purely an ${ansis_default.italic("example")} rather than a default, move it into an ${fmt.decorator("@example")} decorator, or delete it`,
606
+ `- if an item value is just a dummy placeholder, delete it`
607
+ ]);
608
+ const confirmReviewed = await prompts_default.confirm({
609
+ message: `Have you reviewed and updated your new ${fmt.fileName(".env.schema")} file?`
610
+ });
611
+ if (pD(confirmReviewed)) process.exit(0);
612
+ envGraph = await loadVarlockEnvGraph();
613
+ if (envGraph.configSchema.EXAMPLE_ITEM) {
614
+ logLines([
615
+ "",
616
+ ansis_default.bold(`\u{1F6A8} Really? ${ansis_default.red("You didn't remove the EXAMPLE_ITEM!")}`),
617
+ `Please make sure your schema is all correct before using it...`
618
+ ]);
619
+ }
620
+ if (exampleFileToConvert) {
621
+ const confirmDeleteExample = await prompts_default.confirm({
622
+ message: `Should we delete your ${fmt.fileName(exampleFileToConvert.fileName)} file? ${ansis_default.italic.gray("(you can always do this yourself later)")}`
623
+ });
624
+ if (pD(confirmDeleteExample)) process.exit(0);
625
+ if (confirmDeleteExample) {
626
+ await fs3.unlink(exampleFileToConvert.fullPath);
627
+ }
628
+ }
629
+ const defaultsFile = envGraph.dataSources.find((dataSource) => {
630
+ return dataSource instanceof DotEnvFileDataSource && dataSource.type === "defaults";
631
+ });
632
+ if (defaultsFile) {
633
+ logLines([
634
+ "",
635
+ `\u{1F6A7} We detected a ${fmt.fileName(defaultsFile.fileName)} file in your project`,
636
+ `You should migrate these default values into ${fmt.fileName(".env.schema")} and delete ${fmt.fileName(defaultsFile.fileName)}`
637
+ ]);
638
+ }
639
+ const redundantInfo = await detectRedundantValues(envGraph);
640
+ if (Object.keys(redundantInfo).length > 0) {
641
+ logLines([
642
+ "",
643
+ ansis_default.bold("\u203C\uFE0F Now that your schema contains defaults, some values in your other .env files are redundant:")
644
+ ]);
645
+ for (const [sourcePath, itemKeys] of Object.entries(redundantInfo)) {
646
+ console.log(fmt.filePath(sourcePath));
647
+ console.log(" ", itemKeys.map((k) => ansis_default.italic(k)).join(", "));
648
+ }
649
+ const confirmDeleteRedundant = await prompts_default.confirm({
650
+ message: "Should we delete these redundant values from your other .env files?"
651
+ });
652
+ if (pD(confirmDeleteRedundant)) process.exit(0);
653
+ if (confirmDeleteRedundant) {
654
+ await detectRedundantValues(envGraph, { delete: true });
655
+ }
656
+ }
657
+ logLines([
658
+ "",
659
+ ansis_default.bold("\u{1F389} Great!"),
660
+ `You can run ${fmt.command("varlock load")} to attempt loading your env vars validate against your new schema.`,
661
+ "Check out our integration guide for more info about integrating into your application."
662
+ ]);
663
+ }
664
+ const jsPackageManager = detectJsPackageManager();
665
+ if (jsPackageManager && await pathExists(path.join(process.cwd(), "package.json"))) {
666
+ const installResult = installJsDependency({
667
+ packageManager: jsPackageManager.name,
668
+ packageName: "varlock"
669
+ });
670
+ if (installResult) {
671
+ logLines([
672
+ "",
673
+ `\u2705 Added ${fmt.packageName("varlock")} as a dependency in your package.json`
674
+ ]);
675
+ }
676
+ }
677
+ }, "commandFn");
678
+
679
+ export { CliExitError, commandFn, commandSpec };
680
+ //# sourceMappingURL=chunk-RY3YIJFG.js.map
681
+ //# sourceMappingURL=chunk-RY3YIJFG.js.map