@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
203 lines (199 loc) • 6.95 kB
JavaScript
import { readLinkedProject } from '@lunora/config';
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-v4hHpQd0.mjs';
import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
import { createR2Sql } from '@lunora/bindings/r2sql';
import { createPipelineLogReader } from '@lunora/runtime';
const LOG_LEVELS = /* @__PURE__ */ new Set(["debug", "error", "fatal", "info", "log", "trace", "warn"]);
const EPOCH_MILLIS_RE = /^\d+$/;
const parseTimestamp = (value, flag) => {
if (value === void 0) {
return void 0;
}
const trimmed = value.trim();
if (EPOCH_MILLIS_RE.test(trimmed)) {
return Number(trimmed);
}
const parsed = Date.parse(trimmed);
if (Number.isNaN(parsed)) {
throw new TypeError(`logs: invalid ${flag} "${value}" — pass epoch-millis or an ISO 8601 date`);
}
return parsed;
};
const parseLevel = (value, flag) => {
if (value === void 0) {
return void 0;
}
if (!LOG_LEVELS.has(value)) {
throw new TypeError(`logs: invalid ${flag} "${value}" — expected one of trace, debug, log, info, warn, error, fatal`);
}
return value;
};
const buildQuery = (options) => {
const query = {};
const sinceTs = parseTimestamp(options.since, "--since");
if (sinceTs !== void 0) {
query.sinceTs = sinceTs;
}
const untilTs = parseTimestamp(options.until, "--until");
if (untilTs !== void 0) {
query.untilTs = untilTs;
}
const level = parseLevel(options.level, "--level");
if (level !== void 0) {
query.level = level;
}
const minLevel = parseLevel(options.minLevel, "--min-level");
if (minLevel !== void 0) {
query.minLevel = minLevel;
}
if (options.functionPrefix !== void 0) {
query.functionPathPrefix = options.functionPrefix;
}
if (options.traceId !== void 0) {
query.traceId = options.traceId;
}
if (options.shardKey !== void 0) {
query.shardKey = options.shardKey;
}
if (options.userId !== void 0) {
query.userId = options.userId;
}
if (options.limit !== void 0) {
const limit = Number(options.limit);
if (!Number.isFinite(limit)) {
throw new TypeError(`logs: invalid --limit "${options.limit}" — expected a number`);
}
query.limit = limit;
}
if (options.cursor !== void 0) {
const cursorTs = Number(options.cursor);
if (!Number.isFinite(cursorTs)) {
throw new TypeError(`logs: invalid --cursor "${options.cursor}" — expected the epoch-millis ts from a prior page's nextCursor`);
}
query.cursor = { ts: cursorTs };
}
return query;
};
const formatRow = (row) => {
const time = new Date(row.ts).toISOString();
const level = row.level.toUpperCase().padEnd(5);
return `${time} ${level} ${row.functionPath} ${row.message}`;
};
const runDurableLogsCommand = async (options) => {
const environment = options.environment ?? process.env;
const accountId = environment.R2_SQL_ACCOUNT_ID;
const apiToken = environment.R2_SQL_TOKEN;
const bucket = environment.R2_SQL_BUCKET;
const missing = [];
if (accountId === void 0 || accountId.length === 0) {
missing.push("R2_SQL_ACCOUNT_ID");
}
if (apiToken === void 0 || apiToken.length === 0) {
missing.push("R2_SQL_TOKEN");
}
if (bucket === void 0 || bucket.length === 0) {
missing.push("R2_SQL_BUCKET");
}
if (options.table === void 0 || options.table.length === 0) {
missing.push("--table");
}
if (missing.length > 0) {
options.logger.error(
`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.`
);
return { code: 1, error: "not configured" };
}
let query;
try {
query = buildQuery(options);
} catch (error) {
options.logger.error(error instanceof Error ? error.message : String(error));
return { code: 1, error: "invalid option" };
}
const client = createR2Sql({ accountId, apiToken, bucket, fetch: options.fetch });
const reader = createPipelineLogReader(client, { namespace: options.namespace, table: options.table });
const page = await reader.query(query);
for (const row of page.rows) {
process.stdout.write(`${options.ndjson === true ? JSON.stringify(row) : formatRow(row)}
`);
}
if (page.rows.length === 0) {
options.logger.info("logs --durable: no matching log records");
} else if (page.nextCursor !== void 0) {
options.logger.info(`logs --durable: more rows available — pass --cursor ${String(page.nextCursor.ts)} for the next page`);
}
return { code: 0, rows: page.rows };
};
const LOG_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
const runLogsCommand = async (options) => {
const cwd = options.cwd ?? process.cwd();
if (options.format !== void 0 && !LOG_FORMATS.has(options.format)) {
options.logger.error(`logs: unknown --format "${options.format}" — expected pretty | json`);
return { code: 1, descriptor: void 0, error: "invalid format" };
}
const args = ["tail"];
if (options.worker !== void 0) {
args.push(options.worker);
}
const env = options.env ?? readLinkedProject(cwd)?.env;
if (env !== void 0) {
args.push("--env", env);
}
if (options.format !== void 0) {
args.push("--format", options.format);
}
if (options.status !== void 0) {
args.push("--status", options.status);
}
if (options.search !== void 0) {
args.push("--search", options.search);
}
if (options.temporary) {
args.push("--temporary");
}
const exec = execArgsFor(detectPackageManager(cwd), "wrangler", args);
const descriptor = {
args: exec.args,
command: exec.command,
cwd
};
options.logger.info(`tailing logs via ${descriptor.command} ${descriptor.args.join(" ")}`);
const spawner = options.spawner ?? defaultSpawner;
const result = await spawner(descriptor);
return {
code: result.code,
descriptor
};
};
const execute = defineHandler(({ argument, cwd, logger, options }) => {
if (options.durable === true) {
return runDurableLogsCommand({
cursor: options.cursor,
functionPrefix: options.functionPrefix,
level: options.level,
limit: options.limit,
logger,
minLevel: options.minLevel,
namespace: options.namespace,
ndjson: options.ndjson === true,
shardKey: options.shardKey,
since: options.since,
table: options.table,
traceId: options.traceId,
until: options.until,
userId: options.userId
});
}
return runLogsCommand({
cwd,
env: options.env,
format: options.format,
logger,
search: options.search,
status: options.status,
temporary: options.temporary === true,
worker: argument[0]
});
});
export { execute, runLogsCommand };