@lunora/cli 1.0.0-alpha.10 → 1.0.0-alpha.100
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/LICENSE.md +6 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/bin.mjs +1 -1
- package/dist/index.d.mts +345 -320
- package/dist/index.d.ts +345 -320
- package/dist/index.mjs +8 -9
- package/dist/packem_chunks/handler.mjs +15 -5
- package/dist/packem_chunks/handler10.mjs +8 -14
- package/dist/packem_chunks/handler11.mjs +20 -189
- package/dist/packem_chunks/handler12.mjs +176 -115
- package/dist/packem_chunks/handler13.mjs +118 -52
- package/dist/packem_chunks/handler14.mjs +50 -43
- package/dist/packem_chunks/handler15.mjs +27 -6
- package/dist/packem_chunks/handler16.mjs +3 -2
- package/dist/packem_chunks/handler17.mjs +1 -1
- package/dist/packem_chunks/handler18.mjs +3 -3
- package/dist/packem_chunks/handler19.mjs +44 -5
- package/dist/packem_chunks/handler2.mjs +6 -4
- package/dist/packem_chunks/handler20.mjs +2 -2
- package/dist/packem_chunks/handler21.mjs +158 -267
- package/dist/packem_chunks/handler3.mjs +8 -4
- package/dist/packem_chunks/handler4.mjs +2 -2
- package/dist/packem_chunks/handler5.mjs +7 -4
- package/dist/packem_chunks/handler6.mjs +10 -7
- package/dist/packem_chunks/handler7.mjs +2 -2
- package/dist/packem_chunks/handler8.mjs +1 -1
- package/dist/packem_chunks/handler9.mjs +330 -12
- package/dist/packem_chunks/planDevCommand.mjs +762 -81
- package/dist/packem_chunks/runCodegenCommand.mjs +5 -4
- package/dist/packem_chunks/runDeployCommand.mjs +137 -22
- package/dist/packem_chunks/runInitCommand.mjs +1193 -98
- package/dist/packem_chunks/runMigrateGenerateCommand.mjs +94 -47
- package/dist/packem_chunks/runResetCommand.mjs +2 -2
- package/dist/packem_chunks/runRpcCommand.mjs +3 -2
- package/dist/packem_shared/{COMMANDS-D3h9Iwvl.mjs → COMMANDS-BOJIDoVY.mjs} +112 -25
- package/dist/packem_shared/{DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs → DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs} +7 -2
- package/dist/packem_shared/{api-spec-CtA6ilu4.mjs → api-spec-Bx0iKbxA.mjs} +3 -1
- package/dist/packem_shared/{buildRegistryIndex-BcYe607_.mjs → buildRegistryIndex-BS5ig822.mjs} +1 -1
- package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +31 -0
- package/dist/packem_shared/{command-BC30oSBW.mjs → command-lYnl4QyF.mjs} +6 -1
- package/dist/packem_shared/{commands-DPKWlqqX.mjs → commands-D5Yxt9VY.mjs} +199 -20
- package/dist/packem_shared/{createLogger-B40gPzQo.mjs → createLogger-CIWSHrTL.mjs} +33 -6
- package/dist/packem_shared/{createRecordingSpawner-DxI3mebw.mjs → createRecordingSpawner-WuSn20kb.mjs} +23 -2
- package/dist/packem_shared/detect-package-manager-v4hHpQd0.mjs +62 -0
- package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
- package/dist/packem_shared/{open-url-Dfq6fAyT.mjs → open-url-4PBLY9X0.mjs} +3 -2
- package/dist/packem_shared/{output-format-wUvAN6AL.mjs → output-format-B4642rjE.mjs} +1 -1
- package/dist/packem_shared/{parseManifest--vZf2FY1.mjs → parseManifest-Dbp-Q2q3.mjs} +36 -9
- package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
- package/dist/packem_shared/runAddCommand-D1hgfqFQ.mjs +4 -0
- package/dist/packem_shared/{storage-2RJBhUC4.mjs → storage-BXU4ax4O.mjs} +1 -1
- package/dist/packem_shared/{tui-prompts-DEiPCKV-.mjs → tui-prompts-BjEN8XgP.mjs} +9 -12
- package/dist/packem_shared/wrangler-secrets-Coni-mER.mjs +49 -0
- package/package.json +19 -14
- package/skills/lunora-quickstart/SKILL.md +26 -5
- package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +0 -61
- package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
- package/dist/packem_shared/runAddCommand-CTRA_JlL.mjs +0 -4
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { discoverSchema, discoverMigrations } from '@lunora/codegen';
|
|
4
|
+
import { isInteractive, promptSelect, promptText } from '@lunora/config';
|
|
5
|
+
import { LunoraError } from '@lunora/errors';
|
|
4
6
|
import { join } from '@visulima/path';
|
|
5
7
|
import { Project } from 'ts-morph';
|
|
6
8
|
import { r as resolveAdminBaseUrl } from '../packem_shared/admin-url-4UzT-CI4.mjs';
|
|
7
|
-
import { d as defineHandler } from '../packem_shared/command-
|
|
9
|
+
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
|
|
8
10
|
import { diffSnapshots, renderMigrationFile } from '../packem_shared/diffSnapshots-BeDvvNiF.mjs';
|
|
9
11
|
import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
|
|
10
12
|
import schemaIrToSnapshot from '../packem_shared/schemaIrToSnapshot-DdsljJT-.mjs';
|
|
11
|
-
import { runExportCommand, runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-
|
|
13
|
+
import { runExportCommand, runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs';
|
|
12
14
|
|
|
13
15
|
const SNAPSHOT_FILENAME = ".snapshot.json";
|
|
14
16
|
const NON_ALPHANUMERIC = /[^\da-z]+/gu;
|
|
@@ -41,12 +43,12 @@ const loadSnapshot = (path) => {
|
|
|
41
43
|
const raw = readFileSync(path, "utf8");
|
|
42
44
|
const parsed = JSON.parse(raw);
|
|
43
45
|
if (parsed.version !== 1) {
|
|
44
|
-
throw new
|
|
46
|
+
throw new LunoraError("INTERNAL", `unsupported snapshot version: ${parsed.version}`);
|
|
45
47
|
}
|
|
46
48
|
return parsed;
|
|
47
49
|
} catch (error) {
|
|
48
50
|
const message = error instanceof Error ? error.message : String(error);
|
|
49
|
-
throw new
|
|
51
|
+
throw new LunoraError("INTERNAL", `failed to read ${path}: ${message}`, { cause: error });
|
|
50
52
|
}
|
|
51
53
|
};
|
|
52
54
|
const runMigrateGenerateCommand = (options) => {
|
|
@@ -151,7 +153,47 @@ const camelCase = (slug) => slug.split("-").filter((part) => part.length > 0).ma
|
|
|
151
153
|
}
|
|
152
154
|
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
153
155
|
}).join("");
|
|
154
|
-
const
|
|
156
|
+
const discoverKnownTables = (cwd) => {
|
|
157
|
+
const schemaPath = join(cwd, "lunora", "schema.ts");
|
|
158
|
+
if (!existsSync(schemaPath)) {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
163
|
+
return discoverSchema(project, schemaPath).tables.map((table) => table.name);
|
|
164
|
+
} catch {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const promptForTable = async (tables) => {
|
|
169
|
+
if (tables.length > 0) {
|
|
170
|
+
return promptSelect(
|
|
171
|
+
"Which table does this migration iterate?",
|
|
172
|
+
tables.map((name) => {
|
|
173
|
+
return { label: name, value: name };
|
|
174
|
+
})
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return promptText("Target table for the migration: ");
|
|
178
|
+
};
|
|
179
|
+
const resolveCreateTable = async (cwd, options) => {
|
|
180
|
+
if (options.table !== void 0) {
|
|
181
|
+
return options.table;
|
|
182
|
+
}
|
|
183
|
+
const prompt = options.promptTable ?? (isInteractive() ? promptForTable : void 0);
|
|
184
|
+
if (prompt === void 0) {
|
|
185
|
+
options.logger.error("migrate create requires a target table when not running interactively — re-run with --table <table>");
|
|
186
|
+
return void 0;
|
|
187
|
+
}
|
|
188
|
+
const answer = await prompt(discoverKnownTables(cwd));
|
|
189
|
+
const table = answer?.trim();
|
|
190
|
+
if (table === void 0 || table === "") {
|
|
191
|
+
options.logger.error("no table selected — re-run with --table <table>");
|
|
192
|
+
return void 0;
|
|
193
|
+
}
|
|
194
|
+
return table;
|
|
195
|
+
};
|
|
196
|
+
const runMigrateCreateCommand = async (options) => {
|
|
155
197
|
const cwd = options.cwd ?? process.cwd();
|
|
156
198
|
const slug = kebabCase(options.name);
|
|
157
199
|
if (slug === "") {
|
|
@@ -165,9 +207,12 @@ const runMigrateCreateCommand = (options) => {
|
|
|
165
207
|
);
|
|
166
208
|
return { code: 1, file: "" };
|
|
167
209
|
}
|
|
168
|
-
const table =
|
|
210
|
+
const table = await resolveCreateTable(cwd, options);
|
|
211
|
+
if (table === void 0) {
|
|
212
|
+
return { code: 1, file: "" };
|
|
213
|
+
}
|
|
169
214
|
if (!IDENTIFIER_PATTERN.test(table)) {
|
|
170
|
-
options.logger.error(`invalid
|
|
215
|
+
options.logger.error(`invalid table: "${table}" — must be a valid identifier ([A-Za-z_][A-Za-z0-9_]*)`);
|
|
171
216
|
return { code: 1, file: "" };
|
|
172
217
|
}
|
|
173
218
|
const lunoraDirectory = join(cwd, "lunora");
|
|
@@ -195,9 +240,6 @@ ${content}`;
|
|
|
195
240
|
${block}
|
|
196
241
|
`, "utf8");
|
|
197
242
|
options.logger.success(`scaffolded migration "${slug}" in ${file}`);
|
|
198
|
-
if (options.table === void 0) {
|
|
199
|
-
options.logger.warn(`set the \`table\` field on "${slug}" — it defaults to "${table}"`);
|
|
200
|
-
}
|
|
201
243
|
return { code: 0, file };
|
|
202
244
|
};
|
|
203
245
|
const resolveMigrationTable = (cwd, id) => {
|
|
@@ -305,44 +347,49 @@ const runMigrateToHyperdriveCommand = async (options) => {
|
|
|
305
347
|
}
|
|
306
348
|
const temporaryDirectory = options.out === void 0 ? mkdtempSync(join(tmpdir(), "lunora-d1ps-")) : void 0;
|
|
307
349
|
const dumpPath = options.out ?? join(temporaryDirectory, "dump.ndjson");
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
350
|
+
try {
|
|
351
|
+
logger.info(`Exporting .global() data from the D1 source (${fromUrl ?? "http://localhost:8787"}) …`);
|
|
352
|
+
const exportResult = await runExportCommand({
|
|
353
|
+
fetchImpl: options.fetchImpl,
|
|
354
|
+
logger,
|
|
355
|
+
out: dumpPath,
|
|
356
|
+
prod: options.prod,
|
|
357
|
+
tables: options.tables,
|
|
358
|
+
token: options.fromToken,
|
|
359
|
+
url: fromUrl
|
|
360
|
+
});
|
|
361
|
+
if (exportResult.code !== 0) {
|
|
362
|
+
return { code: exportResult.code };
|
|
363
|
+
}
|
|
364
|
+
logger.info(`Exported ${String(exportResult.rows)} row(s) (${String(exportResult.bytes)} bytes).`);
|
|
365
|
+
logger.info(`Importing into the Hyperdrive target (${toUrl ?? "http://localhost:8787"}) …`);
|
|
366
|
+
const importResult = await runImportCommand({
|
|
367
|
+
batchSize: options.batchSize,
|
|
368
|
+
fetchImpl: options.fetchImpl,
|
|
369
|
+
file: dumpPath,
|
|
370
|
+
logger,
|
|
371
|
+
prod: options.prod,
|
|
372
|
+
token: options.toToken,
|
|
373
|
+
url: toUrl
|
|
374
|
+
});
|
|
375
|
+
if (importResult.code !== 0) {
|
|
376
|
+
return { code: importResult.code };
|
|
377
|
+
}
|
|
378
|
+
if (importResult.inserted === exportResult.rows) {
|
|
379
|
+
logger.info(
|
|
380
|
+
`✓ Migrated ${String(exportResult.rows)} row(s) — counts match. Verify your app reads from Hyperdrive, then decommission the D1 binding.`
|
|
381
|
+
);
|
|
382
|
+
} else {
|
|
383
|
+
logger.warn(
|
|
384
|
+
`Imported ${String(importResult.inserted)} of ${String(exportResult.rows)} exported row(s) — the remainder likely already existed in the target (see conflicts above). Re-run after resolving, or inspect the dump with --out.`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
return { code: 0 };
|
|
388
|
+
} finally {
|
|
389
|
+
if (temporaryDirectory !== void 0) {
|
|
390
|
+
rmSync(temporaryDirectory, { force: true, recursive: true });
|
|
391
|
+
}
|
|
344
392
|
}
|
|
345
|
-
return { code: 0 };
|
|
346
393
|
};
|
|
347
394
|
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
348
395
|
const sub = argument[0];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, rmSync } from 'node:fs';
|
|
2
2
|
import { join } from '@visulima/path';
|
|
3
|
-
import { d as defineHandler } from '../packem_shared/command-
|
|
4
|
-
import { b as tuiConfirm } from '../packem_shared/tui-prompts-
|
|
3
|
+
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
|
|
4
|
+
import { b as tuiConfirm } from '../packem_shared/tui-prompts-BjEN8XgP.mjs';
|
|
5
5
|
|
|
6
6
|
const runResetCommand = async (options) => {
|
|
7
7
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
|
|
2
3
|
import { r as resolveWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
|
|
3
4
|
|
|
4
5
|
const parseArgsJson = (raw) => {
|
|
@@ -9,7 +10,7 @@ const parseArgsJson = (raw) => {
|
|
|
9
10
|
return JSON.parse(raw);
|
|
10
11
|
} catch (error) {
|
|
11
12
|
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
-
throw new
|
|
13
|
+
throw new LunoraError("INTERNAL", `failed to parse --args as JSON: ${message}`, { cause: error });
|
|
13
14
|
}
|
|
14
15
|
};
|
|
15
16
|
const TRAILING_SLASH = /\/$/u;
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { mkdirSync, readFileSync, lstatSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { resolveHint, isLunoraError, flattenHint, findSolutionByMessage } from '@lunora/errors';
|
|
2
3
|
import { createCerebro } from '@visulima/cerebro';
|
|
3
4
|
import completionCommand from '@visulima/cerebro/command/completion';
|
|
4
5
|
import versionCommand from '@visulima/cerebro/command/version';
|
|
5
|
-
import { A as API_SPEC_HELP } from './api-spec-
|
|
6
|
-
import { createLogger } from './createLogger-
|
|
7
|
-
import {
|
|
6
|
+
import { A as API_SPEC_HELP } from './api-spec-Bx0iKbxA.mjs';
|
|
7
|
+
import { createLogger } from './createLogger-CIWSHrTL.mjs';
|
|
8
|
+
import { VisulimaError, renderError } from '@visulima/error';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
8
10
|
import { join } from 'node:path';
|
|
9
11
|
|
|
10
12
|
const addCommand = {
|
|
11
|
-
argument: {
|
|
12
|
-
|
|
13
|
+
argument: {
|
|
14
|
+
description: "Feature or registry item: ai | auth | email | storage | crons | presence | queue | workflow | flags | backup | …",
|
|
15
|
+
name: "feature",
|
|
16
|
+
type: String
|
|
17
|
+
},
|
|
18
|
+
description: "Add a feature or registry item (ai, auth, email, storage, crons, …) to the current Lunora project",
|
|
13
19
|
examples: [
|
|
14
20
|
["lunora add auth", "Add authentication (asks which provider)"],
|
|
15
21
|
["lunora add auth --provider clerk", "Add Clerk auth without prompting"],
|
|
@@ -37,7 +43,8 @@ const addCommand = {
|
|
|
37
43
|
name: "ref",
|
|
38
44
|
type: String
|
|
39
45
|
},
|
|
40
|
-
{ description: "Permit --source values outside gh:/github:/https://", name: "allow-unsafe-source", type: Boolean }
|
|
46
|
+
{ description: "Permit --source values outside gh:/github:/https://", name: "allow-unsafe-source", type: Boolean },
|
|
47
|
+
{ description: "Output format: pretty (default) or json", name: "format", type: String }
|
|
41
48
|
]
|
|
42
49
|
};
|
|
43
50
|
|
|
@@ -212,9 +219,19 @@ const deploymentsCommand = {
|
|
|
212
219
|
};
|
|
213
220
|
|
|
214
221
|
const devCommand = {
|
|
222
|
+
argument: {
|
|
223
|
+
description: "Optional subcommand: stop (shut the running dev server down) | status (report it) | logs (print its captured output)",
|
|
224
|
+
name: "args",
|
|
225
|
+
type: String
|
|
226
|
+
},
|
|
215
227
|
description: "Run the dev stack: wrangler worker + studio + codegen watch",
|
|
216
228
|
examples: [
|
|
217
229
|
["lunora dev", "Run the worker + studio + codegen watch"],
|
|
230
|
+
["lunora dev --background", "Run detached: blocks until ready, prints URL + PID, then returns"],
|
|
231
|
+
["lunora dev stop", "Stop the background/tracked dev server (idempotent)"],
|
|
232
|
+
["lunora dev status", "Report the running dev server (URL, PID, uptime)"],
|
|
233
|
+
["lunora dev logs", "Print the captured dev-server log (background runs)"],
|
|
234
|
+
["lunora dev --json", "Machine-readable JSON log lines (also LUNORA_LOG_JSON=1)"],
|
|
218
235
|
["lunora dev --no-studio", "Skip the embedded studio server"],
|
|
219
236
|
["lunora dev --worker-port 8080", "Use a custom wrangler dev port"],
|
|
220
237
|
["lunora dev --remote", "Proxy D1/KV/R2 to the deployed worker (also LUNORA_REMOTE=1)"]
|
|
@@ -224,10 +241,19 @@ const devCommand = {
|
|
|
224
241
|
return { default: m.execute };
|
|
225
242
|
}),
|
|
226
243
|
name: "dev",
|
|
244
|
+
// KEEP IN SYNC with `daemonArguments` in `./lifecycle.ts`: a new flag that
|
|
245
|
+
// must reach a `--background` daemon has to be forwarded there explicitly.
|
|
227
246
|
options: [
|
|
228
247
|
{ description: `Which API spec(s) codegen emits: ${API_SPEC_HELP} (default openapi)`, name: "api-spec", type: String },
|
|
229
248
|
{ description: "Studio server port (default 6173)", name: "port", type: Number },
|
|
230
249
|
{ description: "wrangler dev port (default 8787)", name: "worker-port", type: Number },
|
|
250
|
+
{
|
|
251
|
+
description: "Run the dev server as a managed background process (auto-enabled when an AI agent is detected; LUNORA_AGENT_MODE=0 disables)",
|
|
252
|
+
name: "background",
|
|
253
|
+
type: Boolean
|
|
254
|
+
},
|
|
255
|
+
{ description: "Emit machine-readable JSON log lines (also LUNORA_LOG_JSON=1; auto-enabled for AI agents)", name: "json", type: Boolean },
|
|
256
|
+
{ description: "How many trailing lines `lunora dev logs` prints (default 100, 0 = all)", name: "lines", type: Number },
|
|
231
257
|
{ description: "Don't start the embedded studio server", name: "no-studio", type: Boolean },
|
|
232
258
|
{ description: "Don't watch + regenerate codegen", name: "no-codegen", type: Boolean },
|
|
233
259
|
{ description: "Proxy D1/KV/R2 bindings to the deployed worker (or set LUNORA_REMOTE=1)", name: "remote", type: Boolean }
|
|
@@ -260,21 +286,24 @@ const doctorCommand = {
|
|
|
260
286
|
};
|
|
261
287
|
|
|
262
288
|
const envCommand = {
|
|
263
|
-
argument: { description: "list | get <KEY> | set <KEY> <VALUE> | unset <KEY> | push | diff | doctor", name: "subcommand", type: String },
|
|
264
|
-
description: "Manage .dev.vars and sync secrets via wrangler (list | get | set | unset | push | diff | doctor)",
|
|
289
|
+
argument: { description: "list | get <KEY> | set <KEY> <VALUE> | unset <KEY> | generate [KEY] | push | diff | doctor", name: "subcommand", type: String },
|
|
290
|
+
description: "Manage .dev.vars and sync secrets via wrangler (list | get | set | unset | generate | push | diff | doctor)",
|
|
265
291
|
examples: [
|
|
266
292
|
["lunora env list", "List .dev.vars keys"],
|
|
267
293
|
["lunora env set API_KEY secret", "Set a local variable"],
|
|
294
|
+
["lunora env generate", "Generate strong values for the project's secrets (print KEY=value)"],
|
|
295
|
+
["lunora env generate AUTH_SECRET --set", "Generate one secret and write it to .dev.vars"],
|
|
268
296
|
["lunora env push --yes", "Upload secrets to Cloudflare"],
|
|
269
297
|
["lunora env diff", "Compare local .dev.vars keys against Cloudflare"]
|
|
270
298
|
],
|
|
271
299
|
group: "Data",
|
|
272
|
-
loader: () => import('../packem_chunks/
|
|
300
|
+
loader: () => import('../packem_chunks/handler9.mjs').then((m) => {
|
|
273
301
|
return { default: m.execute };
|
|
274
302
|
}),
|
|
275
303
|
name: "env",
|
|
276
304
|
options: [
|
|
277
305
|
{ description: "Target production for `push` (passes --env production to wrangler)", name: "prod", type: Boolean },
|
|
306
|
+
{ description: "For `generate` — write the generated secrets into .dev.vars instead of printing them", name: "set", type: Boolean },
|
|
278
307
|
{
|
|
279
308
|
description: "Push secrets to a temporary-account deployment when unauthenticated (wrangler secret put --temporary). Errors if you're already authenticated.",
|
|
280
309
|
name: "temporary",
|
|
@@ -292,7 +321,7 @@ const exportCommand = {
|
|
|
292
321
|
["lunora export --tables messages,users", "Export only specific tables"]
|
|
293
322
|
],
|
|
294
323
|
group: "Data",
|
|
295
|
-
loader: () => import('../packem_chunks/
|
|
324
|
+
loader: () => import('../packem_chunks/handler10.mjs').then((m) => {
|
|
296
325
|
return { default: m.execute };
|
|
297
326
|
}),
|
|
298
327
|
name: "export",
|
|
@@ -314,7 +343,7 @@ const importCommand = {
|
|
|
314
343
|
description: "Bulk-insert rows from an NDJSON file via the worker's admin endpoint",
|
|
315
344
|
examples: [["lunora import backup.ndjson", "Bulk-insert rows from an NDJSON file"]],
|
|
316
345
|
group: "Data",
|
|
317
|
-
loader: () => import('../packem_chunks/
|
|
346
|
+
loader: () => import('../packem_chunks/handler11.mjs').then((m) => {
|
|
318
347
|
return { default: m.execute };
|
|
319
348
|
}),
|
|
320
349
|
name: "import",
|
|
@@ -322,6 +351,7 @@ const importCommand = {
|
|
|
322
351
|
{ description: "Wrap each bare doc as `{table:<name>,doc:...}`", name: "table", type: String },
|
|
323
352
|
{ description: "Rows per HTTP request (default 500)", name: "batch-size", type: Number },
|
|
324
353
|
{ description: "Target production — requires an explicit --url", name: "prod", type: Boolean },
|
|
354
|
+
{ description: "Confirm bulk-writing production (required with --prod)", name: "yes", type: Boolean },
|
|
325
355
|
{ description: "Worker URL (default http://localhost:8787)", name: "url", type: String },
|
|
326
356
|
{
|
|
327
357
|
description: "Admin bearer token (prefer LUNORA_ADMIN_TOKEN; --token is visible to other local processes via the process table)",
|
|
@@ -338,7 +368,7 @@ const infoCommand = {
|
|
|
338
368
|
["lunora info --json", "Emit a JSON snapshot"]
|
|
339
369
|
],
|
|
340
370
|
group: "Project",
|
|
341
|
-
loader: () => import('../packem_chunks/
|
|
371
|
+
loader: () => import('../packem_chunks/handler12.mjs').then((m) => {
|
|
342
372
|
return { default: m.execute };
|
|
343
373
|
}),
|
|
344
374
|
name: "info",
|
|
@@ -350,6 +380,7 @@ const initCommand = {
|
|
|
350
380
|
description: "Scaffold a new Lunora project",
|
|
351
381
|
examples: [
|
|
352
382
|
["lunora init my-app", "Scaffold with the default (vite) template"],
|
|
383
|
+
["lunora init my-app -t next", "Scaffold a Next.js app"],
|
|
353
384
|
["lunora init my-app -t tanstack-start-react", "Scaffold a TanStack Start (React) app"],
|
|
354
385
|
["lunora init my-app -t tanstack-start-solid", "Scaffold a TanStack Start (Solid) app"],
|
|
355
386
|
["lunora init my-app --ref alpha", "Scaffold from the alpha branch's templates"],
|
|
@@ -369,7 +400,7 @@ const initCommand = {
|
|
|
369
400
|
// picker (default React overlay) and a non-interactive run errors.
|
|
370
401
|
// For React/Vue/Solid/Svelte SPAs use `--vite <framework>` (overlay);
|
|
371
402
|
// `-t` selects a bespoke template.
|
|
372
|
-
description: "Bespoke template (standalone | astro | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid). For an SPA use --vite react|vue|solid|svelte.",
|
|
403
|
+
description: "Bespoke template (standalone | astro | next | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid). For an SPA use --vite react|vue|solid|svelte.",
|
|
373
404
|
name: "template",
|
|
374
405
|
type: String
|
|
375
406
|
},
|
|
@@ -421,7 +452,7 @@ const initCommand = {
|
|
|
421
452
|
type: String
|
|
422
453
|
},
|
|
423
454
|
{
|
|
424
|
-
description: "Add features non-interactively after scaffolding (comma-separated): auth |
|
|
455
|
+
description: "Add features non-interactively after scaffolding (comma-separated): ai | auth | backup | browser | cloudflare-access | crons | email | flags | hyperdrive | payment | presence | queue | storage | workflow",
|
|
425
456
|
name: "add",
|
|
426
457
|
type: String
|
|
427
458
|
},
|
|
@@ -442,7 +473,7 @@ const insightsCommand = {
|
|
|
442
473
|
["lunora insights --prod --url https://app.example.com --token $LUNORA_ADMIN_TOKEN", "Report against production"]
|
|
443
474
|
],
|
|
444
475
|
group: "Develop",
|
|
445
|
-
loader: () => import('../packem_chunks/
|
|
476
|
+
loader: () => import('../packem_chunks/handler13.mjs').then((m) => {
|
|
446
477
|
return { default: m.execute };
|
|
447
478
|
}),
|
|
448
479
|
name: "insights",
|
|
@@ -464,7 +495,7 @@ const linkCommand = {
|
|
|
464
495
|
["lunora link --remove", "Remove the link"]
|
|
465
496
|
],
|
|
466
497
|
group: "Deploy",
|
|
467
|
-
loader: () => import('../packem_chunks/
|
|
498
|
+
loader: () => import('../packem_chunks/handler14.mjs').then((m) => {
|
|
468
499
|
return { default: m.execute };
|
|
469
500
|
}),
|
|
470
501
|
name: "link",
|
|
@@ -478,9 +509,9 @@ const linkCommand = {
|
|
|
478
509
|
|
|
479
510
|
const logsCommand = {
|
|
480
511
|
argument: { description: "Worker name (defaults to the name in wrangler config)", name: "worker", type: String },
|
|
481
|
-
description: "Stream live logs from a deployed
|
|
512
|
+
description: "Stream live logs from a deployed Worker, or read the durable log archive with --durable",
|
|
482
513
|
group: "Deploy",
|
|
483
|
-
loader: () => import('../packem_chunks/
|
|
514
|
+
loader: () => import('../packem_chunks/handler21.mjs').then((m) => {
|
|
484
515
|
return { default: m.execute };
|
|
485
516
|
}),
|
|
486
517
|
name: "logs",
|
|
@@ -493,7 +524,22 @@ const logsCommand = {
|
|
|
493
524
|
description: "Tail a temporary-account deployment when unauthenticated (wrangler tail --temporary). Errors if you're already authenticated.",
|
|
494
525
|
name: "temporary",
|
|
495
526
|
type: Boolean
|
|
496
|
-
}
|
|
527
|
+
},
|
|
528
|
+
// --- durable-archive path (--durable) ---
|
|
529
|
+
{ description: "Read the durable log archive (pipelineLogSink → R2) via R2 SQL instead of tailing live", name: "durable", type: Boolean },
|
|
530
|
+
{ description: "durable: Iceberg table the Pipeline writes to (required with --durable)", name: "table", type: String },
|
|
531
|
+
{ description: "durable: Iceberg namespace (R2 Data Catalog database) the table lives in", name: "namespace", type: String },
|
|
532
|
+
{ description: "durable: lower time bound (epoch-millis or ISO 8601), inclusive", name: "since", type: String },
|
|
533
|
+
{ description: "durable: upper time bound (epoch-millis or ISO 8601), inclusive", name: "until", type: String },
|
|
534
|
+
{ description: "durable: exact severity filter (trace|debug|log|info|warn|error|fatal)", name: "level", type: String },
|
|
535
|
+
{ description: "durable: severity floor — this level and every more-severe one", name: "min-level", type: String },
|
|
536
|
+
{ description: "durable: keep function paths starting with this prefix (LIKE 'prefix%')", name: "function-prefix", type: String },
|
|
537
|
+
{ description: "durable: trace-id filter", name: "trace-id", type: String },
|
|
538
|
+
{ description: "durable: shard-key filter", name: "shard-key", type: String },
|
|
539
|
+
{ description: "durable: user-id filter", name: "user-id", type: String },
|
|
540
|
+
{ description: "durable: max rows (clamped to 1–10000; default 500)", name: "limit", type: String },
|
|
541
|
+
{ description: "durable: resume after a prior page — the ts from its nextCursor", name: "cursor", type: String },
|
|
542
|
+
{ description: "durable: emit one JSON object per line instead of a table", name: "ndjson", type: Boolean }
|
|
497
543
|
]
|
|
498
544
|
};
|
|
499
545
|
|
|
@@ -514,7 +560,7 @@ const migrateCommand = {
|
|
|
514
560
|
name: "migrate",
|
|
515
561
|
options: [
|
|
516
562
|
{ description: "Migration name slug (e.g. add_users_email)", name: "name", type: String },
|
|
517
|
-
{ description: "Target table for `create`", name: "table", type: String },
|
|
563
|
+
{ description: "Target table for `create` (prompted for interactively when omitted)", name: "table", type: String },
|
|
518
564
|
{ description: "Preview a data migration without rewriting rows", name: "dry-run", type: Boolean },
|
|
519
565
|
{ description: "Rows per batch for a data migration", name: "batch-size", type: Number },
|
|
520
566
|
{ description: "Cap batches processed this run (maps to the runner's maxBatches)", name: "steps", type: Number },
|
|
@@ -678,7 +724,8 @@ const verifyCommand = {
|
|
|
678
724
|
description: "Validate wrangler.jsonc + codegen dry-run + tsc --noEmit (no files written)",
|
|
679
725
|
examples: [
|
|
680
726
|
["lunora verify", "Validate wrangler + codegen + tsc"],
|
|
681
|
-
["lunora verify --no-typecheck", "Skip the TypeScript type-check"]
|
|
727
|
+
["lunora verify --no-typecheck", "Skip the TypeScript type-check"],
|
|
728
|
+
["lunora verify --health-url https://my-app.workers.dev", "Also probe the deployment's /_lunora/health"]
|
|
682
729
|
],
|
|
683
730
|
group: "Deploy",
|
|
684
731
|
loader: () => import('../packem_chunks/handler19.mjs').then((m) => {
|
|
@@ -689,6 +736,11 @@ const verifyCommand = {
|
|
|
689
736
|
{ description: "Treat breaking schema drift as a warning instead of a failure", name: "allow-schema-drift", type: Boolean },
|
|
690
737
|
{ description: `Which API spec(s) to emit: ${API_SPEC_HELP} (default openapi)`, name: "api-spec", type: String },
|
|
691
738
|
{ description: "Output format: pretty (default) or json", name: "format", type: String },
|
|
739
|
+
{
|
|
740
|
+
description: "Probe this deployment's /_lunora/health endpoint (off by default; keeps verify offline-safe)",
|
|
741
|
+
name: "health-url",
|
|
742
|
+
type: String
|
|
743
|
+
},
|
|
692
744
|
{ description: "Skip the TypeScript type-check step", name: "no-typecheck", type: Boolean }
|
|
693
745
|
]
|
|
694
746
|
};
|
|
@@ -707,6 +759,21 @@ const viewCommand = {
|
|
|
707
759
|
options: [{ description: "Open the deployed worker URL instead of localhost", name: "remote", type: Boolean }]
|
|
708
760
|
};
|
|
709
761
|
|
|
762
|
+
const NO_STACK = { filterStacktrace: () => false, hideErrorCodeView: true };
|
|
763
|
+
const renderLunoraError = (error, options = {}) => {
|
|
764
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
765
|
+
const hint = resolveHint(isLunoraError(error) ? { code: error.code, hint: error.hint, message } : message);
|
|
766
|
+
const rendered = new VisulimaError({
|
|
767
|
+
// `renderError` iterates `hint` as lines; the shared flattener returns one
|
|
768
|
+
// string, so split it back to lines for the terminal renderer.
|
|
769
|
+
hint: hint === void 0 ? void 0 : flattenHint(hint).split("\n"),
|
|
770
|
+
message: options.reason === void 0 ? message : `${options.reason}: ${message}`,
|
|
771
|
+
name: error instanceof Error && error.name.length > 0 ? error.name : "Error"
|
|
772
|
+
});
|
|
773
|
+
rendered.stack = "";
|
|
774
|
+
return renderError(rendered, NO_STACK);
|
|
775
|
+
};
|
|
776
|
+
|
|
710
777
|
const editDistance = (a, b) => {
|
|
711
778
|
const distances = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
712
779
|
for (let row = 1; row <= a.length; row += 1) {
|
|
@@ -769,6 +836,15 @@ const isNewer = (current, latest) => compareVersions(latest, current) > 0;
|
|
|
769
836
|
const isCacheFresh = (checkedAt, nowMs, ttlMs) => nowMs - checkedAt < ttlMs;
|
|
770
837
|
const formatUpdateNotice = (current, latest) => `Update available for @lunora/cli: ${current} → ${latest} — run \`pnpm add -D @lunora/cli@latest\``;
|
|
771
838
|
const cacheFilePath = (cacheDirectory) => join(cacheDirectory, "lunora-cli-update.json");
|
|
839
|
+
const defaultCacheDirectory = (env) => {
|
|
840
|
+
const base = env.XDG_CACHE_HOME && env.XDG_CACHE_HOME.length > 0 ? env.XDG_CACHE_HOME : join(homedir(), ".cache");
|
|
841
|
+
const directory = join(base, "lunora");
|
|
842
|
+
try {
|
|
843
|
+
mkdirSync(directory, { mode: 448, recursive: true });
|
|
844
|
+
} catch {
|
|
845
|
+
}
|
|
846
|
+
return directory;
|
|
847
|
+
};
|
|
772
848
|
const readCache = (cacheDirectory) => {
|
|
773
849
|
try {
|
|
774
850
|
const parsed = JSON.parse(readFileSync(cacheFilePath(cacheDirectory), "utf8"));
|
|
@@ -784,7 +860,14 @@ const readCache = (cacheDirectory) => {
|
|
|
784
860
|
};
|
|
785
861
|
const writeCache = (cacheDirectory, cache) => {
|
|
786
862
|
try {
|
|
787
|
-
|
|
863
|
+
const path = cacheFilePath(cacheDirectory);
|
|
864
|
+
try {
|
|
865
|
+
if (lstatSync(path).isSymbolicLink()) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
} catch {
|
|
869
|
+
}
|
|
870
|
+
writeFileSync(path, `${JSON.stringify(cache)}
|
|
788
871
|
`, "utf8");
|
|
789
872
|
} catch {
|
|
790
873
|
}
|
|
@@ -809,7 +892,7 @@ const maybeNotifyUpdate = async (deps) => {
|
|
|
809
892
|
if (isNotifierDisabled(deps.current, env, isTty)) {
|
|
810
893
|
return;
|
|
811
894
|
}
|
|
812
|
-
const cacheDirectory = deps.cacheDir ??
|
|
895
|
+
const cacheDirectory = deps.cacheDir ?? defaultCacheDirectory(env);
|
|
813
896
|
const nowMs = (deps.now ?? Date.now)();
|
|
814
897
|
const ttlMs = deps.ttlMs ?? CACHE_TTL_MS;
|
|
815
898
|
const cache = readCache(cacheDirectory);
|
|
@@ -921,7 +1004,11 @@ const reportRunError = (error) => {
|
|
|
921
1004
|
const message = error instanceof Error ? error.message : String(error);
|
|
922
1005
|
const unknown = UNKNOWN_COMMAND.exec(message);
|
|
923
1006
|
if (!unknown?.groups) {
|
|
924
|
-
|
|
1007
|
+
if (isLunoraError(error) || findSolutionByMessage(message) !== void 0) {
|
|
1008
|
+
logger.error(renderLunoraError(error));
|
|
1009
|
+
} else {
|
|
1010
|
+
logger.error(message);
|
|
1011
|
+
}
|
|
925
1012
|
return;
|
|
926
1013
|
}
|
|
927
1014
|
const name = unknown.groups.name ?? "";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createWriteStream, createReadStream } from 'node:fs';
|
|
2
2
|
import { unlink, stat } from 'node:fs/promises';
|
|
3
|
+
import { LunoraError } from '@lunora/errors';
|
|
3
4
|
import { r as resolveAdminBaseUrl } from './admin-url-4UzT-CI4.mjs';
|
|
4
5
|
|
|
5
6
|
const EXPORT_ENDPOINT_PATH = "/_lunora/admin/export";
|
|
@@ -129,6 +130,10 @@ const resolveImportRequest = async (options) => {
|
|
|
129
130
|
options.logger.error("--prod requires an explicit --url (refusing to import to the implicit localhost worker)");
|
|
130
131
|
return void 0;
|
|
131
132
|
}
|
|
133
|
+
if (options.prod && options.yes !== true) {
|
|
134
|
+
options.logger.error("import --prod bulk-writes production. Re-run with --yes to confirm.");
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
132
137
|
const token = options.token ?? process.env["LUNORA_ADMIN_TOKEN"];
|
|
133
138
|
if (!token) {
|
|
134
139
|
options.logger.error("admin token required — pass --token or set LUNORA_ADMIN_TOKEN");
|
|
@@ -183,7 +188,7 @@ const runImportCommand = async (options) => {
|
|
|
183
188
|
});
|
|
184
189
|
if (!response.ok) {
|
|
185
190
|
const text = await response.text().catch(() => "<no body>");
|
|
186
|
-
throw new
|
|
191
|
+
throw new LunoraError("INTERNAL", `import batch failed (HTTP ${String(response.status)}): ${text}`);
|
|
187
192
|
}
|
|
188
193
|
const json = await response.json();
|
|
189
194
|
if (json.inserted) {
|
|
@@ -213,7 +218,7 @@ const runImportCommand = async (options) => {
|
|
|
213
218
|
parsedDocument = JSON.parse(trimmed);
|
|
214
219
|
} catch (error) {
|
|
215
220
|
const message = error instanceof Error ? error.message : String(error);
|
|
216
|
-
throw new
|
|
221
|
+
throw new LunoraError("INTERNAL", `invalid JSON on line ${String(lineNumber)}: ${message}`, { cause: error });
|
|
217
222
|
}
|
|
218
223
|
batch.push(JSON.stringify({ doc: parsedDocument, table: options.table }));
|
|
219
224
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
|
|
1
3
|
const API_SPEC_VALUES = ["both", "none", "openapi", "openrpc"];
|
|
2
4
|
const API_SPEC_HELP = API_SPEC_VALUES.join(" | ");
|
|
3
5
|
const parseApiSpec = (value) => {
|
|
@@ -7,7 +9,7 @@ const parseApiSpec = (value) => {
|
|
|
7
9
|
if (API_SPEC_VALUES.includes(value)) {
|
|
8
10
|
return value;
|
|
9
11
|
}
|
|
10
|
-
throw new
|
|
12
|
+
throw new LunoraError("INTERNAL", `invalid --api-spec "${value}" — expected one of: ${API_SPEC_HELP}`);
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export { API_SPEC_HELP as A, parseApiSpec as p };
|
package/dist/packem_shared/{buildRegistryIndex-BcYe607_.mjs → buildRegistryIndex-BS5ig822.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from '@visulima/path';
|
|
3
|
-
import parseManifest from './parseManifest
|
|
3
|
+
import parseManifest from './parseManifest-Dbp-Q2q3.mjs';
|
|
4
4
|
|
|
5
5
|
const CONTROL_CHARS = /[\u0000-\u001F\u007F-\u009F]/gu;
|
|
6
6
|
const stripControlChars = (value) => value === void 0 ? void 0 : value.replaceAll(CONTROL_CHARS, "");
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { findLunoraSolution } from '@lunora/codegen';
|
|
2
|
+
import { VisulimaError, renderError } from '@visulima/error';
|
|
3
|
+
|
|
4
|
+
const flattenSolutionBody = (solution) => solution.body.split("\n").filter((line) => !line.startsWith("```")).join("\n").replaceAll(/\*\*(.+?)\*\*/gu, "$1").replaceAll(/`([^`]+)`/gu, "$1");
|
|
5
|
+
const NO_STACK = { filterStacktrace: () => false, hideErrorCodeView: true };
|
|
6
|
+
const renderCodegenFailure = (error, reason) => {
|
|
7
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8
|
+
const solution = findLunoraSolution(message);
|
|
9
|
+
const rendered = new VisulimaError({
|
|
10
|
+
hint: solution ? [solution.header, "", flattenSolutionBody(solution)] : void 0,
|
|
11
|
+
message: reason === void 0 ? `codegen failed: ${message}` : `codegen failed (${reason}): ${message}`,
|
|
12
|
+
name: "CodegenError"
|
|
13
|
+
});
|
|
14
|
+
rendered.stack = "";
|
|
15
|
+
return renderError(rendered, NO_STACK);
|
|
16
|
+
};
|
|
17
|
+
const renderCodegenHint = (message) => {
|
|
18
|
+
const solution = findLunoraSolution(message);
|
|
19
|
+
if (solution === void 0) {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
const rendered = new VisulimaError({
|
|
23
|
+
hint: [flattenSolutionBody(solution)],
|
|
24
|
+
message: solution.header,
|
|
25
|
+
name: "Hint"
|
|
26
|
+
});
|
|
27
|
+
rendered.stack = "";
|
|
28
|
+
return renderError(rendered, NO_STACK);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { renderCodegenHint as a, renderCodegenFailure as r };
|