@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,116 +1,2 @@
1
- import { mkdtempSync, rmSync, existsSync, readdirSync, statSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join, relative } from 'node:path';
4
- import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
5
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-v4hHpQd0.mjs';
6
- import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
7
-
8
- const walk = (root) => {
9
- const entries = [];
10
- const recurse = (directory) => {
11
- for (const name of readdirSync(directory)) {
12
- const full = join(directory, name);
13
- const info = statSync(full);
14
- if (info.isDirectory()) {
15
- recurse(full);
16
- } else if (info.isFile()) {
17
- entries.push({ path: relative(root, full), sizeBytes: info.size });
18
- }
19
- }
20
- };
21
- recurse(root);
22
- return entries;
23
- };
24
- const formatBytes = (bytes) => {
25
- if (bytes < 1024) {
26
- return `${String(bytes)} B`;
27
- }
28
- if (bytes < 1024 * 1024) {
29
- return `${(bytes / 1024).toFixed(1)} KiB`;
30
- }
31
- return `${(bytes / (1024 * 1024)).toFixed(2)} MiB`;
32
- };
33
- const buildReport = (outdir) => {
34
- const all = walk(outdir);
35
- const sorted = all.toSorted((a, b) => b.sizeBytes - a.sizeBytes);
36
- const totalBytes = all.reduce((sum, entry) => sum + entry.sizeBytes, 0);
37
- const generatedFiles = all.filter((entry) => entry.path.includes("_generated"));
38
- return {
39
- generatedFiles,
40
- outdir,
41
- topModules: sorted.slice(0, 10),
42
- totalBytes,
43
- totalFiles: all.length
44
- };
45
- };
46
- const renderText = (report, logger) => {
47
- logger.info(`outdir: ${report.outdir}`);
48
- logger.info(`total: ${String(report.totalFiles)} files, ${formatBytes(report.totalBytes)}`);
49
- if (report.topModules.length > 0) {
50
- logger.info("");
51
- logger.info("top modules by size:");
52
- for (const entry of report.topModules) {
53
- logger.info(` ${formatBytes(entry.sizeBytes).padStart(10, " ")} ${entry.path}`);
54
- }
55
- }
56
- if (report.generatedFiles.length > 0) {
57
- logger.info("");
58
- logger.info(`_generated/ files: ${String(report.generatedFiles.length)}`);
59
- for (const entry of report.generatedFiles) {
60
- logger.info(` ${formatBytes(entry.sizeBytes).padStart(10, " ")} ${entry.path}`);
61
- }
62
- }
63
- };
64
- const runAnalyzeCommand = async (options) => {
65
- const cwd = options.cwd ?? process.cwd();
66
- const { logger } = options;
67
- let outdir;
68
- let descriptor;
69
- let temporary = false;
70
- if (options.inspectOnly) {
71
- outdir = options.inspectOnly;
72
- } else {
73
- outdir = mkdtempSync(join(tmpdir(), "lunora-analyze-"));
74
- temporary = true;
75
- const exec = execArgsFor(detectPackageManager(cwd), "wrangler", ["deploy", "--dry-run", "--outdir", outdir]);
76
- descriptor = {
77
- args: exec.args,
78
- command: exec.command,
79
- cwd
80
- };
81
- logger.info(`analyze: building via ${descriptor.command} ${descriptor.args.join(" ")}`);
82
- const spawner = options.spawner ?? defaultSpawner;
83
- const spawned = await spawner(descriptor);
84
- if (spawned.code !== 0) {
85
- logger.error(`analyze: wrangler dry-run failed (exit ${String(spawned.code)})`);
86
- rmSync(outdir, { force: true, recursive: true });
87
- return { code: spawned.code, descriptor, report: void 0 };
88
- }
89
- }
90
- try {
91
- if (!existsSync(outdir)) {
92
- logger.error(`analyze: outdir not found at ${outdir}`);
93
- return { code: 1, descriptor, report: void 0 };
94
- }
95
- const report = buildReport(outdir);
96
- if (options.json) {
97
- process.stdout.write(`${JSON.stringify(report, void 0, 2)}
98
- `);
99
- } else {
100
- renderText(report, logger);
101
- }
102
- return { code: 0, descriptor, report };
103
- } finally {
104
- if (temporary && existsSync(outdir)) {
105
- try {
106
- rmSync(outdir, { force: true, recursive: true });
107
- } catch {
108
- }
109
- }
110
- }
111
- };
112
- const execute = defineHandler(
113
- ({ cwd, logger, options }) => runAnalyzeCommand({ cwd, json: options.json === true, logger })
114
- );
115
-
116
- export { execute, runAnalyzeCommand };
1
+ import{mkdtempSync as p,rmSync as c,existsSync as l,readdirSync as u,statSync as y}from"node:fs";import{tmpdir as g}from"node:os";import{join as f,relative as m}from"node:path";import{i as $}from"../packem_shared/command-Bjv4VmYA.mjs";import{M as S,P as z}from"../packem_shared/detect-package-manager-B2eIuAiP.mjs";import{defaultSpawner as B}from"../packem_shared/createRecordingSpawner-C8_7nffs.mjs";const w=t=>{const r=[],e=i=>{for(const o of u(i)){const n=f(i,o),s=y(n);s.isDirectory()?e(n):s.isFile()&&r.push({path:m(t,n),sizeBytes:s.size})}};return e(t),r},d=t=>t<1024?`${String(t)} B`:t<1024*1024?`${(t/1024).toFixed(1)} KiB`:`${(t/(1024*1024)).toFixed(2)} MiB`,h=t=>{const r=w(t),e=r.toSorted((o,n)=>n.sizeBytes-o.sizeBytes),i=r.reduce((o,n)=>o+n.sizeBytes,0);return{generatedFiles:r.filter(o=>o.path.includes("_generated")),outdir:t,topModules:e.slice(0,10),totalBytes:i,totalFiles:r.length}},F=(t,r)=>{if(r.info(`outdir: ${t.outdir}`),r.info(`total: ${String(t.totalFiles)} files, ${d(t.totalBytes)}`),t.topModules.length>0){r.info(""),r.info("top modules by size:");for(const e of t.topModules)r.info(` ${d(e.sizeBytes).padStart(10," ")} ${e.path}`)}if(t.generatedFiles.length>0){r.info(""),r.info(`_generated/ files: ${String(t.generatedFiles.length)}`);for(const e of t.generatedFiles)r.info(` ${d(e.sizeBytes).padStart(10," ")} ${e.path}`)}},v=async t=>{const r=t.cwd??process.cwd(),{logger:e}=t;let i,o,n=!1;if(t.inspectOnly)i=t.inspectOnly;else{i=p(f(g(),"lunora-analyze-")),n=!0;const s=S(z(r),"wrangler",["deploy","--dry-run","--outdir",i]);o={args:s.args,command:s.command,cwd:r},e.info(`analyze: building via ${o.command} ${o.args.join(" ")}`);const a=await(t.spawner??B)(o);if(a.code!==0)return e.error(`analyze: wrangler dry-run failed (exit ${String(a.code)})`),c(i,{force:!0,recursive:!0}),{code:a.code,descriptor:o,report:void 0}}try{if(!l(i))return e.error(`analyze: outdir not found at ${i}`),{code:1,descriptor:o,report:void 0};const s=h(i);return t.json?process.stdout.write(`${JSON.stringify(s,void 0,2)}
2
+ `):F(s,e),{code:0,descriptor:o,report:s}}finally{if(n&&l(i))try{c(i,{force:!0,recursive:!0})}catch{}}},k=$(({cwd:t,logger:r,options:e})=>v({cwd:t,json:e.json===!0,logger:r}));export{k as execute,v as runAnalyzeCommand};
@@ -1,94 +1 @@
1
- import { readFileSync, existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { parse } from 'jsonc-parser';
4
- import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
5
- import { o as openUrl } from '../packem_shared/open-url-4PBLY9X0.mjs';
6
-
7
- const DEFAULT_DEV_PORT = 8787;
8
- const STUDIO_PATH = "/_lunora/studio";
9
- const findWranglerFile = (projectRoot) => {
10
- for (const candidate of ["wrangler.jsonc", "wrangler.json"]) {
11
- const fullPath = join(projectRoot, candidate);
12
- if (existsSync(fullPath)) {
13
- return fullPath;
14
- }
15
- }
16
- return void 0;
17
- };
18
- const readWrangler = (projectRoot) => {
19
- const file = findWranglerFile(projectRoot);
20
- if (!file) {
21
- return void 0;
22
- }
23
- try {
24
- const parsed = parse(readFileSync(file, "utf8"));
25
- return parsed !== null && typeof parsed === "object" ? parsed : void 0;
26
- } catch {
27
- return void 0;
28
- }
29
- };
30
- const resolveDevPort = (wrangler) => {
31
- if (!wrangler) {
32
- return DEFAULT_DEV_PORT;
33
- }
34
- const { dev } = wrangler;
35
- if (dev !== null && typeof dev === "object") {
36
- const { port } = dev;
37
- if (typeof port === "number" && Number.isFinite(port)) {
38
- return port;
39
- }
40
- }
41
- return DEFAULT_DEV_PORT;
42
- };
43
- const resolveRemoteUrl = (wrangler) => {
44
- if (!wrangler) {
45
- return void 0;
46
- }
47
- const { routes } = wrangler;
48
- if (Array.isArray(routes) && routes.length > 0) {
49
- const first = routes[0];
50
- if (typeof first === "string") {
51
- return `https://${first.split("/")[0] ?? first}${STUDIO_PATH}`;
52
- }
53
- if (first !== null && typeof first === "object") {
54
- const { pattern } = first;
55
- if (typeof pattern === "string" && pattern.length > 0) {
56
- return `https://${pattern.split("/")[0] ?? pattern}${STUDIO_PATH}`;
57
- }
58
- }
59
- }
60
- const { name } = wrangler;
61
- if (typeof name === "string" && name.length > 0) {
62
- return `https://${name}.workers.dev${STUDIO_PATH}`;
63
- }
64
- return void 0;
65
- };
66
- const runViewCommand = async (options) => {
67
- const cwd = options.cwd ?? process.cwd();
68
- const wrangler = readWrangler(cwd);
69
- const { logger } = options;
70
- let url;
71
- if (options.remote) {
72
- url = resolveRemoteUrl(wrangler);
73
- if (!url) {
74
- logger.error("view --remote: could not determine the remote URL from wrangler config (set `routes` or `name`).");
75
- return { code: 1, url: void 0 };
76
- }
77
- } else {
78
- url = `http://localhost:${String(resolveDevPort(wrangler))}${STUDIO_PATH}`;
79
- }
80
- logger.info(`opening ${url}`);
81
- try {
82
- await openUrl(url, { opener: options.opener });
83
- } catch (error) {
84
- const message = error instanceof Error ? error.message : String(error);
85
- logger.error(`view: failed to open URL: ${message}`);
86
- return { code: 1, url };
87
- }
88
- return { code: 0, url };
89
- };
90
- const execute = defineHandler(
91
- ({ cwd, logger, options }) => runViewCommand({ cwd, logger, remote: options.remote === true })
92
- );
93
-
94
- export { execute, runViewCommand };
1
+ import{readFileSync as u,existsSync as l}from"node:fs";import{join as p}from"node:path";import{parse as a}from"jsonc-parser";import{i as m}from"../packem_shared/command-Bjv4VmYA.mjs";import{f as g}from"../packem_shared/open-url-CTU_g9gr.mjs";const c=8787,i="/_lunora/studio",d=e=>{for(const t of["wrangler.jsonc","wrangler.json"]){const r=p(e,t);if(l(r))return r}},y=e=>{const t=d(e);if(t)try{const r=a(u(t,"utf8"));return r!==null&&typeof r=="object"?r:void 0}catch{return}},w=e=>{if(!e)return c;const{dev:t}=e;if(t!==null&&typeof t=="object"){const{port:r}=t;if(typeof r=="number"&&Number.isFinite(r))return r}return c},h=e=>{if(!e)return;const{routes:t}=e;if(Array.isArray(t)&&t.length>0){const n=t[0];if(typeof n=="string")return`https://${n.split("/")[0]??n}${i}`;if(n!==null&&typeof n=="object"){const{pattern:o}=n;if(typeof o=="string"&&o.length>0)return`https://${o.split("/")[0]??o}${i}`}}const{name:r}=e;if(typeof r=="string"&&r.length>0)return`https://${r}.workers.dev${i}`},$=async e=>{const t=e.cwd??process.cwd(),r=y(t),{logger:n}=e;let o;if(e.remote){if(o=h(r),!o)return n.error("view --remote: could not determine the remote URL from wrangler config (set `routes` or `name`)."),{code:1,url:void 0}}else o=`http://localhost:${String(w(r))}${i}`;n.info(`opening ${o}`);try{await g(o,{opener:e.opener})}catch(s){const f=s instanceof Error?s.message:String(s);return n.error(`view: failed to open URL: ${f}`),{code:1,url:o}}return{code:0,url:o}},R=m(({cwd:e,logger:t,options:r})=>$({cwd:e,logger:t,remote:r.remote===!0}));export{R as execute,$ as runViewCommand};
@@ -1,202 +1,2 @@
1
- import { readLinkedProject } from '@lunora/config';
2
- import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
3
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-v4hHpQd0.mjs';
4
- import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
5
- import { createR2Sql } from '@lunora/bindings/r2sql';
6
- import { createPipelineLogReader } from '@lunora/runtime';
7
-
8
- const LOG_LEVELS = /* @__PURE__ */ new Set(["debug", "error", "fatal", "info", "log", "trace", "warn"]);
9
- const EPOCH_MILLIS_RE = /^\d+$/;
10
- const parseTimestamp = (value, flag) => {
11
- if (value === void 0) {
12
- return void 0;
13
- }
14
- const trimmed = value.trim();
15
- if (EPOCH_MILLIS_RE.test(trimmed)) {
16
- return Number(trimmed);
17
- }
18
- const parsed = Date.parse(trimmed);
19
- if (Number.isNaN(parsed)) {
20
- throw new TypeError(`logs: invalid ${flag} "${value}" — pass epoch-millis or an ISO 8601 date`);
21
- }
22
- return parsed;
23
- };
24
- const parseLevel = (value, flag) => {
25
- if (value === void 0) {
26
- return void 0;
27
- }
28
- if (!LOG_LEVELS.has(value)) {
29
- throw new TypeError(`logs: invalid ${flag} "${value}" — expected one of trace, debug, log, info, warn, error, fatal`);
30
- }
31
- return value;
32
- };
33
- const buildQuery = (options) => {
34
- const query = {};
35
- const sinceTs = parseTimestamp(options.since, "--since");
36
- if (sinceTs !== void 0) {
37
- query.sinceTs = sinceTs;
38
- }
39
- const untilTs = parseTimestamp(options.until, "--until");
40
- if (untilTs !== void 0) {
41
- query.untilTs = untilTs;
42
- }
43
- const level = parseLevel(options.level, "--level");
44
- if (level !== void 0) {
45
- query.level = level;
46
- }
47
- const minLevel = parseLevel(options.minLevel, "--min-level");
48
- if (minLevel !== void 0) {
49
- query.minLevel = minLevel;
50
- }
51
- if (options.functionPrefix !== void 0) {
52
- query.functionPathPrefix = options.functionPrefix;
53
- }
54
- if (options.traceId !== void 0) {
55
- query.traceId = options.traceId;
56
- }
57
- if (options.shardKey !== void 0) {
58
- query.shardKey = options.shardKey;
59
- }
60
- if (options.userId !== void 0) {
61
- query.userId = options.userId;
62
- }
63
- if (options.limit !== void 0) {
64
- const limit = Number(options.limit);
65
- if (!Number.isFinite(limit)) {
66
- throw new TypeError(`logs: invalid --limit "${options.limit}" — expected a number`);
67
- }
68
- query.limit = limit;
69
- }
70
- if (options.cursor !== void 0) {
71
- const cursorTs = Number(options.cursor);
72
- if (!Number.isFinite(cursorTs)) {
73
- throw new TypeError(`logs: invalid --cursor "${options.cursor}" — expected the epoch-millis ts from a prior page's nextCursor`);
74
- }
75
- query.cursor = { ts: cursorTs };
76
- }
77
- return query;
78
- };
79
- const formatRow = (row) => {
80
- const time = new Date(row.ts).toISOString();
81
- const level = row.level.toUpperCase().padEnd(5);
82
- return `${time} ${level} ${row.functionPath} ${row.message}`;
83
- };
84
- const runDurableLogsCommand = async (options) => {
85
- const environment = options.environment ?? process.env;
86
- const accountId = environment.R2_SQL_ACCOUNT_ID;
87
- const apiToken = environment.R2_SQL_TOKEN;
88
- const bucket = environment.R2_SQL_BUCKET;
89
- const missing = [];
90
- if (accountId === void 0 || accountId.length === 0) {
91
- missing.push("R2_SQL_ACCOUNT_ID");
92
- }
93
- if (apiToken === void 0 || apiToken.length === 0) {
94
- missing.push("R2_SQL_TOKEN");
95
- }
96
- if (bucket === void 0 || bucket.length === 0) {
97
- missing.push("R2_SQL_BUCKET");
98
- }
99
- if (options.table === void 0 || options.table.length === 0) {
100
- missing.push("--table");
101
- }
102
- if (missing.length > 0) {
103
- options.logger.error(
104
- `logs --durable: R2 SQL not configured (missing ${missing.join(", ")}). The Pipeline must write to an R2 Data Catalog (Iceberg) table, and you must supply R2_SQL_ACCOUNT_ID / R2_SQL_TOKEN / R2_SQL_BUCKET plus --table — see the observability docs.`
105
- );
106
- return { code: 1, error: "not configured" };
107
- }
108
- let query;
109
- try {
110
- query = buildQuery(options);
111
- } catch (error) {
112
- options.logger.error(error instanceof Error ? error.message : String(error));
113
- return { code: 1, error: "invalid option" };
114
- }
115
- const client = createR2Sql({ accountId, apiToken, bucket, fetch: options.fetch });
116
- const reader = createPipelineLogReader(client, { namespace: options.namespace, table: options.table });
117
- const page = await reader.query(query);
118
- for (const row of page.rows) {
119
- process.stdout.write(`${options.ndjson === true ? JSON.stringify(row) : formatRow(row)}
120
- `);
121
- }
122
- if (page.rows.length === 0) {
123
- options.logger.info("logs --durable: no matching log records");
124
- } else if (page.nextCursor !== void 0) {
125
- options.logger.info(`logs --durable: more rows available — pass --cursor ${String(page.nextCursor.ts)} for the next page`);
126
- }
127
- return { code: 0, rows: page.rows };
128
- };
129
-
130
- const LOG_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
131
- const runLogsCommand = async (options) => {
132
- const cwd = options.cwd ?? process.cwd();
133
- if (options.format !== void 0 && !LOG_FORMATS.has(options.format)) {
134
- options.logger.error(`logs: unknown --format "${options.format}" — expected pretty | json`);
135
- return { code: 1, descriptor: void 0, error: "invalid format" };
136
- }
137
- const args = ["tail"];
138
- if (options.worker !== void 0) {
139
- args.push(options.worker);
140
- }
141
- const env = options.env ?? readLinkedProject(cwd)?.env;
142
- if (env !== void 0) {
143
- args.push("--env", env);
144
- }
145
- if (options.format !== void 0) {
146
- args.push("--format", options.format);
147
- }
148
- if (options.status !== void 0) {
149
- args.push("--status", options.status);
150
- }
151
- if (options.search !== void 0) {
152
- args.push("--search", options.search);
153
- }
154
- if (options.temporary) {
155
- args.push("--temporary");
156
- }
157
- const exec = execArgsFor(detectPackageManager(cwd), "wrangler", args);
158
- const descriptor = {
159
- args: exec.args,
160
- command: exec.command,
161
- cwd
162
- };
163
- options.logger.info(`tailing logs via ${descriptor.command} ${descriptor.args.join(" ")}`);
164
- const spawner = options.spawner ?? defaultSpawner;
165
- const result = await spawner(descriptor);
166
- return {
167
- code: result.code,
168
- descriptor
169
- };
170
- };
171
- const execute = defineHandler(({ argument, cwd, logger, options }) => {
172
- if (options.durable === true) {
173
- return runDurableLogsCommand({
174
- cursor: options.cursor,
175
- functionPrefix: options.functionPrefix,
176
- level: options.level,
177
- limit: options.limit,
178
- logger,
179
- minLevel: options.minLevel,
180
- namespace: options.namespace,
181
- ndjson: options.ndjson === true,
182
- shardKey: options.shardKey,
183
- since: options.since,
184
- table: options.table,
185
- traceId: options.traceId,
186
- until: options.until,
187
- userId: options.userId
188
- });
189
- }
190
- return runLogsCommand({
191
- cwd,
192
- env: options.env,
193
- format: options.format,
194
- logger,
195
- search: options.search,
196
- status: options.status,
197
- temporary: options.temporary === true,
198
- worker: argument[0]
199
- });
200
- });
201
-
202
- export { execute, runLogsCommand };
1
+ import{readLinkedProject as m}from"@lunora/config";import{i as g}from"../packem_shared/command-Bjv4VmYA.mjs";import{M as p,P as f}from"../packem_shared/detect-package-manager-B2eIuAiP.mjs";import{defaultSpawner as v}from"../packem_shared/createRecordingSpawner-C8_7nffs.mjs";import{createR2Sql as h}from"@lunora/bindings/r2sql";import{createPipelineLogReader as w}from"@lunora/runtime";const b=new Set(["debug","error","fatal","info","log","trace","warn"]),y=/^\d+$/,c=(r,e)=>{if(r===void 0)return;const t=r.trim();if(y.test(t))return Number(t);const o=Date.parse(t);if(Number.isNaN(o))throw new TypeError(`logs: invalid ${e} "${r}" — pass epoch-millis or an ISO 8601 date`);return o},d=(r,e)=>{if(r!==void 0){if(!b.has(r))throw new TypeError(`logs: invalid ${e} "${r}" — expected one of trace, debug, log, info, warn, error, fatal`);return r}},_=r=>{const e={},t=c(r.since,"--since");t!==void 0&&(e.sinceTs=t);const o=c(r.until,"--until");o!==void 0&&(e.untilTs=o);const s=d(r.level,"--level");s!==void 0&&(e.level=s);const n=d(r.minLevel,"--min-level");if(n!==void 0&&(e.minLevel=n),r.functionPrefix!==void 0&&(e.functionPathPrefix=r.functionPrefix),r.traceId!==void 0&&(e.traceId=r.traceId),r.shardKey!==void 0&&(e.shardKey=r.shardKey),r.userId!==void 0&&(e.userId=r.userId),r.limit!==void 0){const i=Number(r.limit);if(!Number.isFinite(i))throw new TypeError(`logs: invalid --limit "${r.limit}" — expected a number`);e.limit=i}if(r.cursor!==void 0){const i=Number(r.cursor);if(!Number.isFinite(i))throw new TypeError(`logs: invalid --cursor "${r.cursor}" — expected the epoch-millis ts from a prior page's nextCursor`);e.cursor={ts:i}}return e},S=r=>{const e=new Date(r.ts).toISOString(),t=r.level.toUpperCase().padEnd(5);return`${e} ${t} ${r.functionPath} ${r.message}`},L=async r=>{const e=r.environment??process.env,t=e.R2_SQL_ACCOUNT_ID,o=e.R2_SQL_TOKEN,s=e.R2_SQL_BUCKET,n=[];if((t===void 0||t.length===0)&&n.push("R2_SQL_ACCOUNT_ID"),(o===void 0||o.length===0)&&n.push("R2_SQL_TOKEN"),(s===void 0||s.length===0)&&n.push("R2_SQL_BUCKET"),(r.table===void 0||r.table.length===0)&&n.push("--table"),n.length>0)return r.logger.error(`logs --durable: R2 SQL not configured (missing ${n.join(", ")}). The Pipeline must write to an R2 Data Catalog (Iceberg) table, and you must supply R2_SQL_ACCOUNT_ID / R2_SQL_TOKEN / R2_SQL_BUCKET plus --table — see the observability docs.`),{code:1,error:"not configured"};let i;try{i=_(r)}catch(a){return r.logger.error(a instanceof Error?a.message:String(a)),{code:1,error:"invalid option"}}const u=h({accountId:t,apiToken:o,bucket:s,fetch:r.fetch}),l=await w(u,{namespace:r.namespace,table:r.table}).query(i);for(const a of l.rows)process.stdout.write(`${r.ndjson===!0?JSON.stringify(a):S(a)}
2
+ `);return l.rows.length===0?r.logger.info("logs --durable: no matching log records"):l.nextCursor!==void 0&&r.logger.info(`logs --durable: more rows available — pass --cursor ${String(l.nextCursor.ts)} for the next page`),{code:0,rows:l.rows}},I=new Set(["json","pretty"]),T=async r=>{const e=r.cwd??process.cwd();if(r.format!==void 0&&!I.has(r.format))return r.logger.error(`logs: unknown --format "${r.format}" — expected pretty | json`),{code:1,descriptor:void 0,error:"invalid format"};const t=["tail"];r.worker!==void 0&&t.push(r.worker);const o=r.env??m(e)?.env;o!==void 0&&t.push("--env",o),r.format!==void 0&&t.push("--format",r.format),r.status!==void 0&&t.push("--status",r.status),r.search!==void 0&&t.push("--search",r.search),r.temporary&&t.push("--temporary");const s=p(f(e),"wrangler",t),n={args:s.args,command:s.command,cwd:e};return r.logger.info(`tailing logs via ${n.command} ${n.args.join(" ")}`),{code:(await(r.spawner??v)(n)).code,descriptor:n}},K=g(({argument:r,cwd:e,logger:t,options:o})=>o.durable===!0?L({cursor:o.cursor,functionPrefix:o.functionPrefix,level:o.level,limit:o.limit,logger:t,minLevel:o.minLevel,namespace:o.namespace,ndjson:o.ndjson===!0,shardKey:o.shardKey,since:o.since,table:o.table,traceId:o.traceId,until:o.until,userId:o.userId}):T({cwd:e,env:o.env,format:o.format,logger:t,search:o.search,status:o.status,temporary:o.temporary===!0,worker:r[0]}));export{K as execute,T as runLogsCommand};
@@ -1,208 +1,2 @@
1
- import { existsSync } from 'node:fs';
2
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { LunoraError } from '@lunora/errors';
5
- import { r as resolveAdminBaseUrl } from '../packem_shared/admin-url-4UzT-CI4.mjs';
6
- import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
7
- import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
8
- import { runExportCommand, runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs';
9
-
10
- const DEFAULT_BACKUP_DIR = ".lunora-backups";
11
- const MANIFEST_FILE = "manifest.json";
12
- const PITR_ENDPOINT_PATH = "/_lunora/admin/pitr";
13
- const GET_PITR_BOOKMARK_OP = "__lunora_admin__:getPitrBookmark";
14
- const PITR_RESTORE_OP = "__lunora_admin__:pitrRestore";
15
- const isManifestEntry = (value) => typeof value === "object" && value !== null && typeof value.id === "string" && typeof value.file === "string";
16
- const readManifest = async (directory) => {
17
- const path = join(directory, MANIFEST_FILE);
18
- if (!existsSync(path)) {
19
- return [];
20
- }
21
- let parsed;
22
- try {
23
- parsed = JSON.parse(await readFile(path, "utf8"));
24
- } catch (error) {
25
- const message = error instanceof Error ? error.message : String(error);
26
- throw new LunoraError("INTERNAL", `backup: ${path} exists but is not valid JSON (${message}) — refusing to overwrite it; fix or remove it manually`, {
27
- cause: error
28
- });
29
- }
30
- if (!Array.isArray(parsed)) {
31
- throw new TypeError(`backup: ${path} exists but is not a JSON array — refusing to overwrite it; fix or remove it manually`);
32
- }
33
- return parsed.filter(isManifestEntry);
34
- };
35
- const writeManifest = async (directory, entries) => {
36
- await writeFile(join(directory, MANIFEST_FILE), `${JSON.stringify(entries, void 0, 2)}
37
- `, "utf8");
38
- };
39
- const runBackupCreate = async (options, directory) => {
40
- await mkdir(directory, { recursive: true });
41
- const timestamp = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
42
- const file = `lunora-backup-${timestamp.replaceAll(/[.:]/gu, "-")}.ndjson`;
43
- const result = await runExportCommand({
44
- fetchImpl: options.fetchImpl,
45
- logger: options.logger,
46
- out: join(directory, file),
47
- prod: options.prod,
48
- tables: options.tables,
49
- token: options.token,
50
- url: options.url
51
- });
52
- if (result.code !== 0) {
53
- return { code: result.code };
54
- }
55
- const entry = { bytes: result.bytes, createdAt: timestamp, file, id: timestamp, rows: result.rows, tables: options.tables };
56
- const manifest = await readManifest(directory);
57
- manifest.push(entry);
58
- await writeManifest(directory, manifest);
59
- options.logger.success(`backup created: ${file} (${result.rows.toString()} rows, ${result.bytes.toString()} bytes)`);
60
- return { code: 0, entry };
61
- };
62
- const runBackupList = async (options, directory) => {
63
- const manifest = await readManifest(directory);
64
- if (manifest.length === 0) {
65
- options.logger.info(`no backups found in ${directory}`);
66
- return { code: 0 };
67
- }
68
- for (const entry of manifest) {
69
- options.logger.info(`${entry.id} ${entry.rows.toString()} rows ${entry.bytes.toString()} bytes ${entry.file}`);
70
- }
71
- return { code: 0 };
72
- };
73
- const runBackupRestore = async (options, directory) => {
74
- const { target } = options;
75
- if (target === void 0 || target.length === 0) {
76
- options.logger.error("restore requires a backup id or file path. Usage: lunora backup restore <id|file>");
77
- return { code: 1 };
78
- }
79
- const manifest = await readManifest(directory);
80
- const matched = manifest.find((entry) => entry.id === target);
81
- const file = matched ? join(directory, matched.file) : target;
82
- if (!existsSync(file)) {
83
- options.logger.error(`backup not found: ${target}`);
84
- return { code: 1 };
85
- }
86
- const result = await runImportCommand({
87
- fetchImpl: options.fetchImpl,
88
- file,
89
- logger: options.logger,
90
- prod: options.prod,
91
- token: options.token,
92
- url: options.url,
93
- yes: options.yes
94
- });
95
- return { code: result.code };
96
- };
97
- const resolvePitrRequest = (options) => {
98
- const token = options.token ?? process.env.LUNORA_ADMIN_TOKEN;
99
- if (!token) {
100
- options.logger.error("admin token required — pass --token or set LUNORA_ADMIN_TOKEN");
101
- return void 0;
102
- }
103
- if (options.prod && options.url === void 0) {
104
- options.logger.error("--prod requires an explicit --url (refusing to target the implicit localhost worker)");
105
- return void 0;
106
- }
107
- if (options.restore === true && options.at === void 0 && options.bookmark === void 0) {
108
- options.logger.error("pitr --restore requires --at <time> or --bookmark <bookmark>");
109
- return void 0;
110
- }
111
- if (options.restore === true && options.prod === true && options.yes !== true) {
112
- options.logger.error("pitr --restore --prod restores production data in place. Re-run with --yes to confirm.");
113
- return void 0;
114
- }
115
- const baseUrl = resolveAdminBaseUrl(options.url, options.logger);
116
- if (baseUrl === void 0) {
117
- return void 0;
118
- }
119
- const fetchImpl = options.pitrFetch ?? globalThis.fetch;
120
- if (typeof fetchImpl !== "function") {
121
- throw new TypeError("no fetch implementation available — pass pitrFetch or run on Node >= 18");
122
- }
123
- return { fetchImpl, requestUrl: `${baseUrl}${PITR_ENDPOINT_PATH}`, token };
124
- };
125
- const buildPitrArgs = (options, isRestore) => {
126
- const args = {};
127
- if (options.at !== void 0) {
128
- args.time = options.at;
129
- }
130
- if (isRestore && options.bookmark !== void 0) {
131
- args.bookmark = options.bookmark;
132
- }
133
- if (isRestore && options.restart === true) {
134
- args.restart = true;
135
- }
136
- return args;
137
- };
138
- const runBackupPitr = async (options) => {
139
- const request = resolvePitrRequest(options);
140
- if (request === void 0) {
141
- return { code: 1 };
142
- }
143
- const isRestore = options.restore === true;
144
- const functionPath = isRestore ? PITR_RESTORE_OP : GET_PITR_BOOKMARK_OP;
145
- const args = buildPitrArgs(options, isRestore);
146
- const action = isRestore ? "restore" : "bookmark";
147
- options.logger.info(`POST ${request.requestUrl} -> pitr ${action}${options.shard === void 0 ? "" : ` (shard "${options.shard}")`}`);
148
- const response = await request.fetchImpl(request.requestUrl, {
149
- body: JSON.stringify({ args, functionPath, shardKey: options.shard }),
150
- headers: { authorization: `Bearer ${request.token}`, "content-type": "application/json" },
151
- method: "POST"
152
- });
153
- const text = await response.text();
154
- let body;
155
- try {
156
- body = JSON.parse(text);
157
- } catch {
158
- body = text;
159
- }
160
- options.logger.info(JSON.stringify(body, void 0, 2));
161
- return { code: response.ok ? 0 : 1 };
162
- };
163
- const runBackupCommand = async (options) => {
164
- const cwd = options.cwd ?? process.cwd();
165
- const directory = join(cwd, options.dir ?? DEFAULT_BACKUP_DIR);
166
- try {
167
- if (options.subcommand === "create") {
168
- return await runBackupCreate(options, directory);
169
- }
170
- if (options.subcommand === "list") {
171
- return await runBackupList(options, directory);
172
- }
173
- if (options.subcommand === "pitr") {
174
- return await runBackupPitr(options);
175
- }
176
- return await runBackupRestore(options, directory);
177
- } catch (error) {
178
- options.logger.error(error instanceof Error ? error.message : String(error));
179
- return { code: 1 };
180
- }
181
- };
182
- const isBackupSubcommand = (value) => value === "create" || value === "list" || value === "pitr" || value === "restore";
183
- const execute = defineHandler(({ argument, cwd, logger, options }) => {
184
- const sub = argument[0];
185
- if (!isBackupSubcommand(sub)) {
186
- logger.error(`backup: unknown subcommand "${sub ?? ""}" — expected create | list | restore | pitr`);
187
- return { code: 1 };
188
- }
189
- return runBackupCommand({
190
- at: options.at,
191
- bookmark: options.bookmark,
192
- cwd,
193
- dir: options.dir,
194
- logger,
195
- prod: options.prod === true,
196
- restart: options.restart === true,
197
- restore: options.restore === true,
198
- shard: options.shard,
199
- subcommand: sub,
200
- tables: options.tables,
201
- target: argument[1],
202
- token: options.token,
203
- url: resolveProductionWorkerUrl({ cwd, prod: options.prod === true, url: options.url }),
204
- yes: options.yes === true
205
- });
206
- });
207
-
208
- export { execute, runBackupCommand };
1
+ import{existsSync as d}from"node:fs";import{mkdir as m,readFile as f,writeFile as g}from"node:fs/promises";import{join as s}from"node:path";import{LunoraError as k}from"@lunora/errors";import{a as b}from"../packem_shared/admin-url-DfVehV3e.mjs";import{i as y}from"../packem_shared/command-Bjv4VmYA.mjs";import{s as w}from"../packem_shared/resolve-target-vxaXHmvg.mjs";import{runExportCommand as h,runImportCommand as $}from"../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-DL87PIEl.mjs";const v=".lunora-backups",p="manifest.json",S="/_lunora/admin/pitr",N="__lunora_admin__:getPitrBookmark",_="__lunora_admin__:pitrRestore",O=t=>typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.file=="string",u=async t=>{const o=s(t,p);if(!d(o))return[];let e;try{e=JSON.parse(await f(o,"utf8"))}catch(r){const a=r instanceof Error?r.message:String(r);throw new k("INTERNAL",`backup: ${o} exists but is not valid JSON (${a}) — refusing to overwrite it; fix or remove it manually`,{cause:r})}if(!Array.isArray(e))throw new TypeError(`backup: ${o} exists but is not a JSON array — refusing to overwrite it; fix or remove it manually`);return e.filter(O)},x=async(t,o)=>{await g(s(t,p),`${JSON.stringify(o,void 0,2)}
2
+ `,"utf8")},I=async(t,o)=>{await m(o,{recursive:!0});const e=(t.now??(()=>new Date))().toISOString(),r=`lunora-backup-${e.replaceAll(/[.:]/gu,"-")}.ndjson`,a=await h({fetchImpl:t.fetchImpl,logger:t.logger,out:s(o,r),prod:t.prod,tables:t.tables,token:t.token,url:t.url});if(a.code!==0)return{code:a.code};const i={bytes:a.bytes,createdAt:e,file:r,id:e,rows:a.rows,tables:t.tables},n=await u(o);return n.push(i),await x(o,n),t.logger.success(`backup created: ${r} (${a.rows.toString()} rows, ${a.bytes.toString()} bytes)`),{code:0,entry:i}},A=async(t,o)=>{const e=await u(o);if(e.length===0)return t.logger.info(`no backups found in ${o}`),{code:0};for(const r of e)t.logger.info(`${r.id} ${r.rows.toString()} rows ${r.bytes.toString()} bytes ${r.file}`);return{code:0}},E=async(t,o)=>{const{target:e}=t;if(e===void 0||e.length===0)return t.logger.error("restore requires a backup id or file path. Usage: lunora backup restore <id|file>"),{code:1};const r=(await u(o)).find(i=>i.id===e),a=r?s(o,r.file):e;return d(a)?{code:(await $({fetchImpl:t.fetchImpl,file:a,logger:t.logger,prod:t.prod,token:t.token,url:t.url,yes:t.yes})).code}:(t.logger.error(`backup not found: ${e}`),{code:1})},T=t=>{const o=t.token??process.env.LUNORA_ADMIN_TOKEN;if(!o){t.logger.error("admin token required — pass --token or set LUNORA_ADMIN_TOKEN");return}if(t.prod&&t.url===void 0){t.logger.error("--prod requires an explicit --url (refusing to target the implicit localhost worker)");return}if(t.restore===!0&&t.at===void 0&&t.bookmark===void 0){t.logger.error("pitr --restore requires --at <time> or --bookmark <bookmark>");return}if(t.restore===!0&&t.prod===!0&&t.yes!==!0){t.logger.error("pitr --restore --prod restores production data in place. Re-run with --yes to confirm.");return}const e=b(t.url,t.logger);if(e===void 0)return;const r=t.pitrFetch??globalThis.fetch;if(typeof r!="function")throw new TypeError("no fetch implementation available — pass pitrFetch or run on Node >= 18");return{fetchImpl:r,requestUrl:`${e}${S}`,token:o}},q=(t,o)=>{const e={};return t.at!==void 0&&(e.time=t.at),o&&t.bookmark!==void 0&&(e.bookmark=t.bookmark),o&&t.restart===!0&&(e.restart=!0),e},J=async t=>{const o=T(t);if(o===void 0)return{code:1};const e=t.restore===!0,r=e?_:N,a=q(t,e),i=e?"restore":"bookmark";t.logger.info(`POST ${o.requestUrl} -> pitr ${i}${t.shard===void 0?"":` (shard "${t.shard}")`}`);const n=await o.fetchImpl(o.requestUrl,{body:JSON.stringify({args:a,functionPath:r,shardKey:t.shard}),headers:{authorization:`Bearer ${o.token}`,"content-type":"application/json"},method:"POST"}),l=await n.text();let c;try{c=JSON.parse(l)}catch{c=l}return t.logger.info(JSON.stringify(c,void 0,2)),{code:n.ok?0:1}},R=async t=>{const o=t.cwd??process.cwd(),e=s(o,t.dir??v);try{return t.subcommand==="create"?await I(t,e):t.subcommand==="list"?await A(t,e):t.subcommand==="pitr"?await J(t):await E(t,e)}catch(r){return t.logger.error(r instanceof Error?r.message:String(r)),{code:1}}},U=t=>t==="create"||t==="list"||t==="pitr"||t==="restore",M=y(({argument:t,cwd:o,logger:e,options:r})=>{const a=t[0];return U(a)?R({at:r.at,bookmark:r.bookmark,cwd:o,dir:r.dir,logger:e,prod:r.prod===!0,restart:r.restart===!0,restore:r.restore===!0,shard:r.shard,subcommand:a,tables:r.tables,target:t[1],token:r.token,url:w({cwd:o,prod:r.prod===!0,url:r.url}),yes:r.yes===!0}):(e.error(`backup: unknown subcommand "${a??""}" — expected create | list | restore | pitr`),{code:1})});export{M as execute,R as runBackupCommand};