@lunora/cli 1.0.0-alpha.100 → 1.0.0-alpha.102

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 (83) hide show
  1. package/dist/bin.mjs +2 -10
  2. package/dist/index.mjs +1 -18
  3. package/dist/packem_chunks/handler.mjs +1 -160
  4. package/dist/packem_chunks/handler10.mjs +1 -16
  5. package/dist/packem_chunks/handler11.mjs +1 -23
  6. package/dist/packem_chunks/handler12.mjs +2 -192
  7. package/dist/packem_chunks/handler13.mjs +2 -131
  8. package/dist/packem_chunks/handler14.mjs +1 -65
  9. package/dist/packem_chunks/handler15.mjs +1 -100
  10. package/dist/packem_chunks/handler16.mjs +1 -44
  11. package/dist/packem_chunks/handler17.mjs +1 -105
  12. package/dist/packem_chunks/handler18.mjs +3 -170
  13. package/dist/packem_chunks/handler19.mjs +1 -128
  14. package/dist/packem_chunks/handler2.mjs +2 -116
  15. package/dist/packem_chunks/handler20.mjs +1 -94
  16. package/dist/packem_chunks/handler21.mjs +2 -202
  17. package/dist/packem_chunks/handler3.mjs +2 -208
  18. package/dist/packem_chunks/handler4.mjs +1 -33
  19. package/dist/packem_chunks/handler5.mjs +1 -52
  20. package/dist/packem_chunks/handler6.mjs +1 -94
  21. package/dist/packem_chunks/handler7.mjs +1 -42
  22. package/dist/packem_chunks/handler8.mjs +1 -174
  23. package/dist/packem_chunks/handler9.mjs +8 -334
  24. package/dist/packem_chunks/planDevCommand.mjs +7 -1181
  25. package/dist/packem_chunks/runCodegenCommand.mjs +4 -53
  26. package/dist/packem_chunks/runDeployCommand.mjs +1 -619
  27. package/dist/packem_chunks/runInitCommand.mjs +51 -1411
  28. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +9 -442
  29. package/dist/packem_chunks/runResetCommand.mjs +1 -41
  30. package/dist/packem_chunks/runRpcCommand.mjs +1 -69
  31. package/dist/packem_shared/COMMANDS-DRqlsGT2.mjs +3 -0
  32. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-DL87PIEl.mjs +8 -0
  33. package/dist/packem_shared/admin-url-DfVehV3e.mjs +1 -0
  34. package/dist/packem_shared/api-spec-BENwiyUa.mjs +1 -0
  35. package/dist/packem_shared/buildRegistryIndex-DwySASBu.mjs +1 -0
  36. package/dist/packem_shared/codegen-error-DJN6pTH5.mjs +3 -0
  37. package/dist/packem_shared/command-Bjv4VmYA.mjs +1 -0
  38. package/dist/packem_shared/commands-BhX2Bo9B.mjs +19 -0
  39. package/dist/packem_shared/createLogger--C3x1FNE.mjs +2 -0
  40. package/dist/packem_shared/createRecordingSpawner-C8_7nffs.mjs +1 -0
  41. package/dist/packem_shared/detect-package-manager-B2eIuAiP.mjs +1 -0
  42. package/dist/packem_shared/diffSnapshots-9bDd9nOK.mjs +5 -0
  43. package/dist/packem_shared/docker-CKvYE9YF.mjs +1 -0
  44. package/dist/packem_shared/insertSchemaExtension-DZReBZ4_.mjs +8 -0
  45. package/dist/packem_shared/open-url-CTU_g9gr.mjs +1 -0
  46. package/dist/packem_shared/output-format-D7cKB-O5.mjs +2 -0
  47. package/dist/packem_shared/parseManifest-x3WsxKHz.mjs +1 -0
  48. package/dist/packem_shared/prompt-cancelled-C8Sj7OqI.mjs +1 -0
  49. package/dist/packem_shared/resolve-target-vxaXHmvg.mjs +1 -0
  50. package/dist/packem_shared/runAddCommand--TBYJQYi.mjs +1 -0
  51. package/dist/packem_shared/schema-drift-gate-CpqG-xP4.mjs +1 -0
  52. package/dist/packem_shared/schemaIrToSnapshot-Clwd2A8e.mjs +1 -0
  53. package/dist/packem_shared/storage-DOvAKGi4.mjs +1 -0
  54. package/dist/packem_shared/tui-prompts-CCt3jm1k.mjs +4 -0
  55. package/dist/packem_shared/wrangler-name-HIl0Z6yx.mjs +1 -0
  56. package/dist/packem_shared/wrangler-secrets-BMRJwKQU.mjs +1 -0
  57. package/package.json +6 -6
  58. package/dist/packem_shared/COMMANDS-BOJIDoVY.mjs +0 -1031
  59. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs +0 -249
  60. package/dist/packem_shared/admin-url-4UzT-CI4.mjs +0 -19
  61. package/dist/packem_shared/api-spec-Bx0iKbxA.mjs +0 -15
  62. package/dist/packem_shared/buildRegistryIndex-BS5ig822.mjs +0 -38
  63. package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +0 -31
  64. package/dist/packem_shared/command-lYnl4QyF.mjs +0 -19
  65. package/dist/packem_shared/commands-D5Yxt9VY.mjs +0 -991
  66. package/dist/packem_shared/createLogger-CIWSHrTL.mjs +0 -105
  67. package/dist/packem_shared/createRecordingSpawner-WuSn20kb.mjs +0 -64
  68. package/dist/packem_shared/detect-package-manager-v4hHpQd0.mjs +0 -62
  69. package/dist/packem_shared/diffSnapshots-BeDvvNiF.mjs +0 -161
  70. package/dist/packem_shared/docker-hMQ97KSQ.mjs +0 -21
  71. package/dist/packem_shared/insertSchemaExtension-DAqbfr9Z.mjs +0 -64
  72. package/dist/packem_shared/open-url-4PBLY9X0.mjs +0 -42
  73. package/dist/packem_shared/output-format-B4642rjE.mjs +0 -17
  74. package/dist/packem_shared/parseManifest-Dbp-Q2q3.mjs +0 -121
  75. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +0 -9
  76. package/dist/packem_shared/resolve-target-qbsJ_5sF.mjs +0 -16
  77. package/dist/packem_shared/runAddCommand-D1hgfqFQ.mjs +0 -4
  78. package/dist/packem_shared/schema-drift-gate-BtBt0as0.mjs +0 -79
  79. package/dist/packem_shared/schemaIrToSnapshot-DdsljJT-.mjs +0 -43
  80. package/dist/packem_shared/storage-BXU4ax4O.mjs +0 -84
  81. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +0 -658
  82. package/dist/packem_shared/wrangler-name-cy4yhm9j.mjs +0 -12
  83. package/dist/packem_shared/wrangler-secrets-Coni-mER.mjs +0 -49
@@ -1,334 +1,8 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, generateSecretValue, DEV_VARS_EXAMPLE_FILE, parseDevVariableEntries, isPlaceholderValue, packageNamesFromBindings, inferLunoraBindings, requiredSecrets, isMintableSecretKey } from '@lunora/config';
4
- import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
5
- import { d as detectPackageManager, e as execArgsFor } from '../packem_shared/detect-package-manager-v4hHpQd0.mjs';
6
- import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
7
- import { l as listRemoteSecrets } from '../packem_shared/wrangler-secrets-Coni-mER.mjs';
8
-
9
- const NEWLINE_PRESENT = /[\r\n]/u;
10
- const UNREPRESENTABLE_PRESENT = /["\\]/u;
11
- const parseDevVariables = (content) => {
12
- const map = /* @__PURE__ */ new Map();
13
- for (const entry of parseDevVariableEntries(content)) {
14
- map.set(entry.key, entry);
15
- }
16
- return map;
17
- };
18
- const escapeRegExp = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`);
19
- const devVariableLinePattern = (key, global) => new RegExp(String.raw`^[ \t]*${escapeRegExp(key)}[ \t]*=.*(\r?\n|$)`, global ? "gmu" : "mu");
20
- const upsertDevVariableLine = (content, key, value) => {
21
- const rendered = `${key}="${value}"`;
22
- if (!devVariableLinePattern(key, false).test(content)) {
23
- if (content === "") {
24
- return `${rendered}
25
- `;
26
- }
27
- return content.endsWith("\n") ? `${content}${rendered}
28
- ` : `${content}
29
- ${rendered}
30
- `;
31
- }
32
- let replacedFirst = false;
33
- return content.replace(devVariableLinePattern(key, true), (_match, newline) => {
34
- if (replacedFirst) {
35
- return "";
36
- }
37
- replacedFirst = true;
38
- return `${rendered}${newline}`;
39
- });
40
- };
41
- const removeDevVariableLine = (content, key) => content.replaceAll(new RegExp(String.raw`^[ \t]*${key}[ \t]*=.*(?:\r?\n|$)`, "gmu"), "");
42
- const redact = (value) => {
43
- if (value.length <= 4) {
44
- return "****";
45
- }
46
- return `${value.slice(0, 4)}${"*".repeat(Math.min(8, value.length - 4))}`;
47
- };
48
- const loadDevVariables = (devVariablesPath) => {
49
- if (!existsSync(devVariablesPath)) {
50
- return /* @__PURE__ */ new Map();
51
- }
52
- return parseDevVariables(readFileSync(devVariablesPath, "utf8"));
53
- };
54
- const runEnvList = (context) => {
55
- const map = loadDevVariables(context.devVariablesPath);
56
- if (map.size === 0) {
57
- context.logger.info(`${DEV_VARS_FILE}: (empty)`);
58
- return { code: 0, descriptors: [] };
59
- }
60
- for (const entry of map.values()) {
61
- context.logger.info(`${entry.key}=${redact(entry.value)}`);
62
- }
63
- return { code: 0, descriptors: [] };
64
- };
65
- const runEnvGet = (context) => {
66
- const { devVariablesPath, logger, options } = context;
67
- if (!options.key) {
68
- logger.error("env get requires a key. Usage: lunora env get <KEY>");
69
- return { code: 1, descriptors: [] };
70
- }
71
- const entry = loadDevVariables(devVariablesPath).get(options.key);
72
- if (!entry) {
73
- logger.error(`env: ${options.key} is not set in ${DEV_VARS_FILE}`);
74
- return { code: 1, descriptors: [] };
75
- }
76
- process.stdout.write(`${entry.value}
77
- `);
78
- return { code: 0, descriptors: [] };
79
- };
80
- const runEnvSet = (context) => {
81
- const { devVariablesPath, logger, options } = context;
82
- if (!options.key) {
83
- logger.error("env set requires a key. Usage: lunora env set <KEY> <VALUE>");
84
- return { code: 1, descriptors: [] };
85
- }
86
- if (!DEV_VARS_KEY_PATTERN.test(options.key)) {
87
- logger.error(`env: invalid key "${options.key}" — must match [A-Za-z_][A-Za-z0-9_]*`);
88
- return { code: 1, descriptors: [] };
89
- }
90
- if (options.value === void 0) {
91
- logger.error("env set requires a value. Usage: lunora env set <KEY> <VALUE>");
92
- return { code: 1, descriptors: [] };
93
- }
94
- if (NEWLINE_PRESENT.test(options.value)) {
95
- logger.error(`env: value for "${options.key}" contains a newline, which .dev.vars cannot represent`);
96
- return { code: 1, descriptors: [] };
97
- }
98
- if (UNREPRESENTABLE_PRESENT.test(options.value)) {
99
- logger.error(`env: value for "${options.key}" contains a double-quote or backslash, which .dev.vars cannot round-trip`);
100
- return { code: 1, descriptors: [] };
101
- }
102
- const raw = existsSync(devVariablesPath) ? readFileSync(devVariablesPath, "utf8") : "";
103
- writeFileSync(devVariablesPath, upsertDevVariableLine(raw, options.key, options.value), "utf8");
104
- logger.success(`env: set ${options.key} (${redact(options.value)}) in ${DEV_VARS_FILE}`);
105
- return { code: 0, descriptors: [] };
106
- };
107
- const runEnvUnset = (context) => {
108
- const { devVariablesPath, logger, options } = context;
109
- if (!options.key) {
110
- logger.error("env unset requires a key. Usage: lunora env unset <KEY>");
111
- return { code: 1, descriptors: [] };
112
- }
113
- const raw = existsSync(devVariablesPath) ? readFileSync(devVariablesPath, "utf8") : "";
114
- if (!parseDevVariables(raw).has(options.key)) {
115
- logger.warn(`env: ${options.key} was not set in ${DEV_VARS_FILE}`);
116
- return { code: 0, descriptors: [] };
117
- }
118
- writeFileSync(devVariablesPath, removeDevVariableLine(raw, options.key), "utf8");
119
- logger.success(`env: unset ${options.key} in ${DEV_VARS_FILE}`);
120
- return { code: 0, descriptors: [] };
121
- };
122
- const runEnvPush = async (context) => {
123
- const { devVariablesPath, logger, options } = context;
124
- if (!options.yes) {
125
- logger.error("env push uploads secrets to Cloudflare. Re-run with --yes to confirm.");
126
- return { code: 1, descriptors: [] };
127
- }
128
- const map = loadDevVariables(devVariablesPath);
129
- if (map.size === 0) {
130
- logger.warn(`${DEV_VARS_FILE}: nothing to push (empty)`);
131
- return { code: 0, descriptors: [] };
132
- }
133
- const spawner = options.spawner ?? defaultSpawner;
134
- const cwd = options.cwd ?? process.cwd();
135
- const manager = detectPackageManager(cwd);
136
- const descriptors = [];
137
- for (const entry of map.values()) {
138
- const args = ["secret", "put", entry.key];
139
- if (options.prod) {
140
- args.push("--env", "production");
141
- }
142
- if (options.temporary) {
143
- args.push("--temporary");
144
- }
145
- const exec = execArgsFor(manager, "wrangler", args);
146
- const descriptor = {
147
- args: exec.args,
148
- command: exec.command,
149
- cwd,
150
- // `wrangler secret put <name>` reads the value from stdin. We
151
- // pipe it through the spawner's `input` channel so the secret
152
- // never lands on the command line, in env, or in shell history.
153
- input: entry.value
154
- };
155
- descriptors.push(descriptor);
156
- logger.info(`pushing ${entry.key} -> wrangler secret${options.prod ? " (production)" : ""}`);
157
- const result = await spawner(descriptor);
158
- if (result.code !== 0) {
159
- logger.error(`env push: failed at ${entry.key} (exit ${String(result.code)})`);
160
- return { code: result.code, descriptors };
161
- }
162
- }
163
- logger.success(`env: pushed ${String(map.size)} secret(s)`);
164
- return { code: 0, descriptors };
165
- };
166
- const fetchRemoteSecretNames = (context) => {
167
- const lister = context.options.secretLister ?? listRemoteSecrets;
168
- return lister({
169
- cwd: context.cwd,
170
- env: context.options.prod ? "production" : void 0,
171
- temporary: context.options.temporary
172
- });
173
- };
174
- const runEnvDiff = async (context) => {
175
- const { devVariablesPath, logger } = context;
176
- const remote = await fetchRemoteSecretNames(context);
177
- if (!remote.ok) {
178
- logger.error(`env diff: ${remote.error ?? "failed to list remote secrets"}`);
179
- return { code: 1, descriptors: [] };
180
- }
181
- const localKeys = new Set(loadDevVariables(devVariablesPath).keys());
182
- const remoteKeys = new Set(remote.names);
183
- const localOnly = [...localKeys].filter((key) => !remoteKeys.has(key)).toSorted((a, b) => a.localeCompare(b));
184
- const remoteOnly = remote.names.filter((name) => !localKeys.has(name));
185
- const both = [...localKeys].filter((key) => remoteKeys.has(key)).toSorted((a, b) => a.localeCompare(b));
186
- for (const key of localOnly) {
187
- logger.info(`local only (run \`lunora env push\`): ${key}`);
188
- }
189
- for (const key of remoteOnly) {
190
- logger.info(`remote only (in Cloudflare, not ${DEV_VARS_FILE}): ${key}`);
191
- }
192
- logger.info(`in both: ${String(both.length)} secret(s)`);
193
- if (localOnly.length === 0 && remoteOnly.length === 0) {
194
- logger.success("env diff: local and remote secret names match");
195
- }
196
- return { code: 0, descriptors: [] };
197
- };
198
- const runEnvDoctor = (context) => {
199
- const { cwd, devVariablesPath, logger } = context;
200
- const examplePath = join(cwd, DEV_VARS_EXAMPLE_FILE);
201
- if (!existsSync(examplePath)) {
202
- logger.info(`env doctor: no ${DEV_VARS_EXAMPLE_FILE} to check against — nothing to validate.`);
203
- return { code: 0, descriptors: [] };
204
- }
205
- const exampleKeys = parseDevVariableEntries(readFileSync(examplePath, "utf8")).map((entry) => entry.key);
206
- const current = loadDevVariables(devVariablesPath);
207
- if (!existsSync(devVariablesPath)) {
208
- logger.error(`env doctor: ${DEV_VARS_FILE} is missing. Run \`lunora dev\` to scaffold it, or \`lunora env set <KEY> <VALUE>\`.`);
209
- logger.info(`expected (from ${DEV_VARS_EXAMPLE_FILE}): ${exampleKeys.join(", ")}`);
210
- return { code: 1, descriptors: [] };
211
- }
212
- const missing = exampleKeys.filter((key) => !current.has(key));
213
- const placeholders = [...current.values()].filter((entry) => isPlaceholderValue(entry.value)).map((entry) => entry.key);
214
- const exampleKeySet = new Set(exampleKeys);
215
- const extra = [...current.keys()].filter((key) => !exampleKeySet.has(key));
216
- for (const key of missing) {
217
- logger.error(`missing: ${key} is in ${DEV_VARS_EXAMPLE_FILE} but not ${DEV_VARS_FILE}`);
218
- }
219
- for (const key of placeholders) {
220
- logger.error(`unset: ${key} still has a placeholder value`);
221
- }
222
- for (const key of extra) {
223
- logger.info(`extra: ${key} is set locally but not listed in ${DEV_VARS_EXAMPLE_FILE}`);
224
- }
225
- if (missing.length === 0 && placeholders.length === 0) {
226
- logger.success(`env doctor: ${DEV_VARS_FILE} looks good (${String(current.size)} var(s)).`);
227
- return { code: 0, descriptors: [] };
228
- }
229
- return { code: 1, descriptors: [] };
230
- };
231
- const resolveMintableKeys = async (context) => {
232
- let packages = [];
233
- try {
234
- packages = packageNamesFromBindings(await inferLunoraBindings({ projectRoot: context.cwd }));
235
- } catch {
236
- }
237
- const fromPackages = requiredSecrets(packages).map((entry) => entry.key).filter((key) => isMintableSecretKey(key));
238
- const fromLocal = [...loadDevVariables(context.devVariablesPath).keys()].filter((key) => isMintableSecretKey(key));
239
- return [.../* @__PURE__ */ new Set([...fromPackages, ...fromLocal])];
240
- };
241
- const runEnvGenerate = async (context) => {
242
- const { devVariablesPath, logger, options } = context;
243
- let keys;
244
- if (options.key === void 0) {
245
- keys = await resolveMintableKeys(context);
246
- if (keys.length === 0) {
247
- logger.info("env generate: no locally-generatable secrets for this project. Name one explicitly: lunora env generate <KEY>");
248
- return { code: 0, descriptors: [] };
249
- }
250
- } else {
251
- if (!DEV_VARS_KEY_PATTERN.test(options.key)) {
252
- logger.error(`env: invalid key "${options.key}" — must match [A-Za-z_][A-Za-z0-9_]*`);
253
- return { code: 1, descriptors: [] };
254
- }
255
- keys = [options.key];
256
- }
257
- const generated = keys.map((key) => {
258
- return { key, value: generateSecretValue() };
259
- });
260
- if (options.set === true) {
261
- let raw = existsSync(devVariablesPath) ? readFileSync(devVariablesPath, "utf8") : "";
262
- for (const entry of generated) {
263
- raw = upsertDevVariableLine(raw, entry.key, entry.value);
264
- }
265
- writeFileSync(devVariablesPath, raw, "utf8");
266
- logger.success(`env: generated ${String(generated.length)} secret(s) into ${DEV_VARS_FILE}: ${generated.map((entry) => entry.key).join(", ")}`);
267
- return { code: 0, descriptors: [] };
268
- }
269
- for (const entry of generated) {
270
- process.stdout.write(`${entry.key}=${entry.value}
271
- `);
272
- }
273
- return { code: 0, descriptors: [] };
274
- };
275
- const runEnvCommand = async (options) => {
276
- const cwd = options.cwd ?? process.cwd();
277
- const context = {
278
- cwd,
279
- devVariablesPath: join(cwd, DEV_VARS_FILE),
280
- logger: options.logger,
281
- options
282
- };
283
- switch (options.subcommand) {
284
- case "diff": {
285
- return runEnvDiff(context);
286
- }
287
- case "doctor": {
288
- return runEnvDoctor(context);
289
- }
290
- case "generate": {
291
- return runEnvGenerate(context);
292
- }
293
- case "get": {
294
- return runEnvGet(context);
295
- }
296
- case "list": {
297
- return runEnvList(context);
298
- }
299
- case "push": {
300
- return runEnvPush(context);
301
- }
302
- case "set": {
303
- return runEnvSet(context);
304
- }
305
- case "unset": {
306
- return runEnvUnset(context);
307
- }
308
- default: {
309
- options.logger.error(`env: unknown subcommand "${options.subcommand}"`);
310
- return { code: 1, descriptors: [] };
311
- }
312
- }
313
- };
314
- const isEnvSubcommand = (value) => value === "list" || value === "get" || value === "set" || value === "unset" || value === "push" || value === "diff" || value === "doctor" || value === "generate";
315
- const execute = defineHandler(({ argument, cwd, logger, options }) => {
316
- const sub = argument[0];
317
- if (!isEnvSubcommand(sub)) {
318
- logger.error(`env: unknown subcommand "${sub ?? ""}" — expected list | get | set | unset | push | diff | doctor | generate`);
319
- return { code: 1 };
320
- }
321
- return runEnvCommand({
322
- cwd,
323
- key: argument[1],
324
- logger,
325
- prod: options.prod === true,
326
- set: options.set === true,
327
- subcommand: sub,
328
- temporary: options.temporary === true,
329
- value: argument[2],
330
- yes: options.yes === true
331
- });
332
- });
333
-
334
- export { execute, runEnvCommand };
1
+ import{existsSync as f,readFileSync as m,writeFileSync as $}from"node:fs";import{join as S}from"node:path";import{DEV_VARS_FILE as d,DEV_VARS_KEY_PATTERN as b,generateSecretValue as _,DEV_VARS_EXAMPLE_FILE as v,parseDevVariableEntries as E,isPlaceholderValue as x,packageNamesFromBindings as R,inferLunoraBindings as z,requiredSecrets as L,isMintableSecretKey as k}from"@lunora/config";import{i as K}from"../packem_shared/command-Bjv4VmYA.mjs";import{P as Y,M as q}from"../packem_shared/detect-package-manager-B2eIuAiP.mjs";import{defaultSpawner as U}from"../packem_shared/createRecordingSpawner-C8_7nffs.mjs";import{f as j}from"../packem_shared/wrangler-secrets-BMRJwKQU.mjs";const C=/[\r\n]/u,F=/["\\]/u,V=e=>{const o=new Map;for(const t of E(e))o.set(t.key,t);return o},M=e=>e.replaceAll(/[.*+?^${}()|[\]\\]/gu,String.raw`\$&`),w=(e,o)=>new RegExp(String.raw`^[ \t]*${M(e)}[ \t]*=.*(\r?\n|$)`,o?"gmu":"mu"),P=(e,o,t)=>{const r=`${o}="${t}"`;if(!w(o,!1).test(e))return e===""?`${r}
2
+ `:e.endsWith(`
3
+ `)?`${e}${r}
4
+ `:`${e}
5
+ ${r}
6
+ `;let s=!1;return e.replace(w(o,!0),(i,n)=>s?"":(s=!0,`${r}${n}`))},Z=(e,o)=>e.replaceAll(new RegExp(String.raw`^[ \t]*${o}[ \t]*=.*(?:\r?\n|$)`,"gmu"),""),A=e=>e.length<=4?"****":`${e.slice(0,4)}${"*".repeat(Math.min(8,e.length-4))}`,g=e=>f(e)?V(m(e,"utf8")):new Map,D=e=>{const o=g(e.devVariablesPath);if(o.size===0)return e.logger.info(`${d}: (empty)`),{code:0,descriptors:[]};for(const t of o.values())e.logger.info(`${t.key}=${A(t.value)}`);return{code:0,descriptors:[]}},B=e=>{const{devVariablesPath:o,logger:t,options:r}=e;if(!r.key)return t.error("env get requires a key. Usage: lunora env get <KEY>"),{code:1,descriptors:[]};const s=g(o).get(r.key);return s?(process.stdout.write(`${s.value}
7
+ `),{code:0,descriptors:[]}):(t.error(`env: ${r.key} is not set in ${d}`),{code:1,descriptors:[]})},I=e=>{const{devVariablesPath:o,logger:t,options:r}=e;if(!r.key)return t.error("env set requires a key. Usage: lunora env set <KEY> <VALUE>"),{code:1,descriptors:[]};if(!b.test(r.key))return t.error(`env: invalid key "${r.key}" — must match [A-Za-z_][A-Za-z0-9_]*`),{code:1,descriptors:[]};if(r.value===void 0)return t.error("env set requires a value. Usage: lunora env set <KEY> <VALUE>"),{code:1,descriptors:[]};if(C.test(r.value))return t.error(`env: value for "${r.key}" contains a newline, which .dev.vars cannot represent`),{code:1,descriptors:[]};if(F.test(r.value))return t.error(`env: value for "${r.key}" contains a double-quote or backslash, which .dev.vars cannot round-trip`),{code:1,descriptors:[]};const s=f(o)?m(o,"utf8"):"";return $(o,P(s,r.key,r.value),"utf8"),t.success(`env: set ${r.key} (${A(r.value)}) in ${d}`),{code:0,descriptors:[]}},N=e=>{const{devVariablesPath:o,logger:t,options:r}=e;if(!r.key)return t.error("env unset requires a key. Usage: lunora env unset <KEY>"),{code:1,descriptors:[]};const s=f(o)?m(o,"utf8"):"";return V(s).has(r.key)?($(o,Z(s,r.key),"utf8"),t.success(`env: unset ${r.key} in ${d}`),{code:0,descriptors:[]}):(t.warn(`env: ${r.key} was not set in ${d}`),{code:0,descriptors:[]})},T=async e=>{const{devVariablesPath:o,logger:t,options:r}=e;if(!r.yes)return t.error("env push uploads secrets to Cloudflare. Re-run with --yes to confirm."),{code:1,descriptors:[]};const s=g(o);if(s.size===0)return t.warn(`${d}: nothing to push (empty)`),{code:0,descriptors:[]};const i=r.spawner??U,n=r.cwd??process.cwd(),u=Y(n),p=[];for(const a of s.values()){const l=["secret","put",a.key];r.prod&&l.push("--env","production"),r.temporary&&l.push("--temporary");const c=q(u,"wrangler",l),h={args:c.args,command:c.command,cwd:n,input:a.value};p.push(h),t.info(`pushing ${a.key} -> wrangler secret${r.prod?" (production)":""}`);const y=await i(h);if(y.code!==0)return t.error(`env push: failed at ${a.key} (exit ${String(y.code)})`),{code:y.code,descriptors:p}}return t.success(`env: pushed ${String(s.size)} secret(s)`),{code:0,descriptors:p}},W=e=>(e.options.secretLister??j)({cwd:e.cwd,env:e.options.prod?"production":void 0,temporary:e.options.temporary}),X=async e=>{const{devVariablesPath:o,logger:t}=e,r=await W(e);if(!r.ok)return t.error(`env diff: ${r.error??"failed to list remote secrets"}`),{code:1,descriptors:[]};const s=new Set(g(o).keys()),i=new Set(r.names),n=[...s].filter(a=>!i.has(a)).toSorted((a,l)=>a.localeCompare(l)),u=r.names.filter(a=>!s.has(a)),p=[...s].filter(a=>i.has(a)).toSorted((a,l)=>a.localeCompare(l));for(const a of n)t.info(`local only (run \`lunora env push\`): ${a}`);for(const a of u)t.info(`remote only (in Cloudflare, not ${d}): ${a}`);return t.info(`in both: ${String(p.length)} secret(s)`),n.length===0&&u.length===0&&t.success("env diff: local and remote secret names match"),{code:0,descriptors:[]}},G=e=>{const{cwd:o,devVariablesPath:t,logger:r}=e,s=S(o,v);if(!f(s))return r.info(`env doctor: no ${v} to check against — nothing to validate.`),{code:0,descriptors:[]};const i=E(m(s,"utf8")).map(c=>c.key),n=g(t);if(!f(t))return r.error(`env doctor: ${d} is missing. Run \`lunora dev\` to scaffold it, or \`lunora env set <KEY> <VALUE>\`.`),r.info(`expected (from ${v}): ${i.join(", ")}`),{code:1,descriptors:[]};const u=i.filter(c=>!n.has(c)),p=[...n.values()].filter(c=>x(c.value)).map(c=>c.key),a=new Set(i),l=[...n.keys()].filter(c=>!a.has(c));for(const c of u)r.error(`missing: ${c} is in ${v} but not ${d}`);for(const c of p)r.error(`unset: ${c} still has a placeholder value`);for(const c of l)r.info(`extra: ${c} is set locally but not listed in ${v}`);return u.length===0&&p.length===0?(r.success(`env doctor: ${d} looks good (${String(n.size)} var(s)).`),{code:0,descriptors:[]}):{code:1,descriptors:[]}},H=async e=>{let o=[];try{o=R(await z({projectRoot:e.cwd}))}catch{}const t=L(o).map(s=>s.key).filter(s=>k(s)),r=[...g(e.devVariablesPath).keys()].filter(s=>k(s));return[...new Set([...t,...r])]},J=async e=>{const{devVariablesPath:o,logger:t,options:r}=e;let s;if(r.key===void 0){if(s=await H(e),s.length===0)return t.info("env generate: no locally-generatable secrets for this project. Name one explicitly: lunora env generate <KEY>"),{code:0,descriptors:[]}}else{if(!b.test(r.key))return t.error(`env: invalid key "${r.key}" — must match [A-Za-z_][A-Za-z0-9_]*`),{code:1,descriptors:[]};s=[r.key]}const i=s.map(n=>({key:n,value:_()}));if(r.set===!0){let n=f(o)?m(o,"utf8"):"";for(const u of i)n=P(n,u.key,u.value);return $(o,n,"utf8"),t.success(`env: generated ${String(i.length)} secret(s) into ${d}: ${i.map(u=>u.key).join(", ")}`),{code:0,descriptors:[]}}for(const n of i)process.stdout.write(`${n.key}=${n.value}
8
+ `);return{code:0,descriptors:[]}},Q=async e=>{const o=e.cwd??process.cwd(),t={cwd:o,devVariablesPath:S(o,d),logger:e.logger,options:e};switch(e.subcommand){case"diff":return X(t);case"doctor":return G(t);case"generate":return J(t);case"get":return B(t);case"list":return D(t);case"push":return T(t);case"set":return I(t);case"unset":return N(t);default:return e.logger.error(`env: unknown subcommand "${e.subcommand}"`),{code:1,descriptors:[]}}},O=e=>e==="list"||e==="get"||e==="set"||e==="unset"||e==="push"||e==="diff"||e==="doctor"||e==="generate",ce=K(({argument:e,cwd:o,logger:t,options:r})=>{const s=e[0];return O(s)?Q({cwd:o,key:e[1],logger:t,prod:r.prod===!0,set:r.set===!0,subcommand:s,temporary:r.temporary===!0,value:e[2],yes:r.yes===!0}):(t.error(`env: unknown subcommand "${s??""}" — expected list | get | set | unset | push | diff | doctor | generate`),{code:1})});export{ce as execute,Q as runEnvCommand};