@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
193 lines (190 loc) • 6.68 kB
JavaScript
import { readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { discoverSchema } from '@lunora/codegen';
import { readLinkedProject } from '@lunora/config';
import { parse } from 'jsonc-parser';
import { Project } from 'ts-morph';
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
const findWranglerFile = (projectRoot) => {
for (const candidate of ["wrangler.jsonc", "wrangler.json"]) {
const fullPath = join(projectRoot, candidate);
if (existsSync(fullPath)) {
return fullPath;
}
}
return void 0;
};
const stringField = (record, key) => {
if (record === null || typeof record !== "object") {
return void 0;
}
const value = record[key];
return typeof value === "string" && value.length > 0 ? value : void 0;
};
const arrayField = (record, key) => {
if (record === null || typeof record !== "object") {
return [];
}
const value = record[key];
return Array.isArray(value) ? value : [];
};
const summariseWrangler = (raw) => {
const durableObjectBindings = arrayField(raw.durable_objects ?? {}, "bindings");
const d1 = arrayField(raw, "d1_databases");
const vectorize = arrayField(raw, "vectorize");
return {
bindings: {
d1: d1.map((entry) => stringField(entry, "binding") ?? "<unnamed>"),
durableObjects: durableObjectBindings.map((entry) => stringField(entry, "name") ?? "<unnamed>"),
vectorize: vectorize.map((entry) => stringField(entry, "binding") ?? "<unnamed>")
},
compatibilityDate: stringField(raw, "compatibility_date"),
compatibilityFlags: arrayField(raw, "compatibility_flags").filter((entry) => typeof entry === "string"),
main: stringField(raw, "main"),
name: stringField(raw, "name")
};
};
const summariseSchema = (schema) => {
return {
tables: schema.tables.map((table) => {
let shard = "root";
if (table.shardMode === "global") {
shard = "global";
} else if (typeof table.shardMode === "object") {
shard = `shardBy(${table.shardMode.field})`;
}
return {
indexes: table.indexes.length,
name: table.name,
shard
};
}),
vectorIndexes: schema.vectorIndexes.length
};
};
const collectLunoraPackages = (projectRoot) => {
const pkgPath = join(projectRoot, "package.json");
if (!existsSync(pkgPath)) {
return [];
}
let pkg;
try {
pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
} catch {
return [];
}
if (pkg === null || typeof pkg !== "object") {
return [];
}
const sections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
const seen = /* @__PURE__ */ new Map();
for (const section of sections) {
const block = pkg[section];
if (block === null || typeof block !== "object") {
continue;
}
for (const [name, version] of Object.entries(block)) {
if (!name.startsWith("@lunora/")) {
continue;
}
if (typeof version === "string" && !seen.has(name)) {
seen.set(name, version);
}
}
}
return [...seen.entries()].toSorted(([a], [b]) => a.localeCompare(b)).map(([name, version]) => {
return { name, version };
});
};
const collectInfo = (projectRoot) => {
const lunoraPackages = collectLunoraPackages(projectRoot);
const wranglerPath = findWranglerFile(projectRoot);
let wrangler;
if (wranglerPath) {
try {
wrangler = summariseWrangler(parse(readFileSync(wranglerPath, "utf8")));
} catch {
wrangler = void 0;
}
}
const schemaPath = join(projectRoot, "lunora", "schema.ts");
let schema;
let schemaError;
if (existsSync(schemaPath)) {
try {
const project = new Project({ skipAddingFilesFromTsConfig: true, useInMemoryFileSystem: false });
schema = summariseSchema(discoverSchema(project, schemaPath));
} catch (error) {
schemaError = error instanceof Error ? error.message : String(error);
}
}
return {
link: readLinkedProject(projectRoot),
lunoraPackages,
projectRoot,
schema,
schemaError,
wrangler,
wranglerPath
};
};
const renderText = (snapshot, logger) => {
logger.info(`project: ${snapshot.projectRoot}`);
logger.info("");
logger.info("@lunora/* packages:");
if (snapshot.lunoraPackages.length === 0) {
logger.info(" (none found in package.json)");
} else {
for (const pkg of snapshot.lunoraPackages) {
logger.info(` ${pkg.name}@${pkg.version}`);
}
}
logger.info("");
if (snapshot.wrangler) {
logger.info(`wrangler: ${snapshot.wranglerPath ?? ""}`);
logger.info(` name: ${snapshot.wrangler.name ?? "<unset>"}`);
logger.info(` main: ${snapshot.wrangler.main ?? "<unset>"}`);
logger.info(` compatibility_date: ${snapshot.wrangler.compatibilityDate ?? "<unset>"}`);
logger.info(` compatibility_flags: ${snapshot.wrangler.compatibilityFlags.join(", ") || "<none>"}`);
logger.info(` durable objects: ${snapshot.wrangler.bindings.durableObjects.join(", ") || "<none>"}`);
logger.info(` d1 databases: ${snapshot.wrangler.bindings.d1.join(", ") || "<none>"}`);
logger.info(` vectorize indexes: ${snapshot.wrangler.bindings.vectorize.join(", ") || "<none>"}`);
} else {
logger.info("wrangler: (not found)");
}
logger.info("");
if (snapshot.link) {
logger.info(`link: ${snapshot.link.workerName ?? "(unnamed)"} -> ${snapshot.link.workerUrl ?? "<no url>"}`);
if (snapshot.link.env !== void 0) {
logger.info(` env: ${snapshot.link.env}`);
}
} else {
logger.info("link: (not linked — run `lunora link --url <https://your-worker>`)");
}
logger.info("");
if (snapshot.schemaError !== void 0) {
logger.warn(`schema: parse error — ${snapshot.schemaError}`);
} else if (snapshot.schema) {
logger.info(`schema: ${String(snapshot.schema.tables.length)} table(s), ${String(snapshot.schema.vectorIndexes)} vector index(es)`);
for (const table of snapshot.schema.tables) {
logger.info(` ${table.name} [${table.shard}, ${String(table.indexes)} index(es)]`);
}
} else {
logger.info("schema: (no lunora/schema.ts)");
}
};
const runInfoCommand = (options) => {
const cwd = options.cwd ?? process.cwd();
const snapshot = collectInfo(cwd);
if (options.json) {
process.stdout.write(`${JSON.stringify(snapshot, void 0, 2)}
`);
} else {
renderText(snapshot, options.logger);
}
return { code: 0, snapshot };
};
const execute = defineHandler(
({ cwd, logger, options }) => runInfoCommand({ cwd, json: options.json === true, logger })
);
export { collectInfo, execute, runInfoCommand };