UNPKG

@lunora/cli

Version:

The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands

203 lines (199 loc) 6.95 kB
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 };