@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
2 lines (1 loc) • 5.48 kB
JavaScript
import{existsSync as g,readFileSync as h}from"node:fs";import{join as m}from"node:path";import{findWranglerFile as w,readWranglerJsonc as $,validateWranglerConfig as b,isPlaceholderValue as v,DEV_VARS_FILE as p,parseDevVariableEntries as x,inferLunoraBindings as y}from"@lunora/config";import{i as S}from"../packem_shared/command-Bjv4VmYA.mjs";const _=/(?:KEY|PASSWORD|SECRET|TOKEN)$/u,N=a=>{const e=a.trim();if(e==="")return!0;const n=e.toLowerCase();return n.includes("replace")||n.startsWith("<")&&n.endsWith(">")},R=a=>{const e=w(a);if(e===void 0)return{parsed:void 0,path:void 0};const{parsed:n}=$(e);return{parsed:n,path:e}},A=(a,e,n)=>{if(e===void 0){n.push({fix:"Run `lunora init` (or `lunora dev`) to scaffold and reconcile wrangler.jsonc.",level:"fail",message:"wrangler.jsonc not found."});return}if(a===void 0){n.push({fix:`Check ${e} is valid JSONC.`,level:"fail",message:`Could not parse ${e}.`});return}const r=b(a).errors.find(s=>s.includes("SHARD"));r===void 0?n.push({level:"pass",message:"wrangler.jsonc present with a SHARD durable-object binding."}):n.push({fix:"Run `lunora dev` to auto-reconcile, or add the binding manually.",level:"fail",message:r})},k=(a,e)=>{if(a===void 0)return;const n=(a.d1_databases??[]).filter(Boolean);for(const r of n){const s=typeof r.database_id=="string"?r.database_id:"";if(N(s)){const t=typeof r.binding=="string"&&r.binding.length>0?r.binding:"<unnamed>";e.push({fix:"Run `wrangler d1 create <name>` and paste the returned database_id into wrangler.jsonc.",level:"fail",message:`D1 binding "${t}" has a placeholder database_id ("${s||"<empty>"}").`})}}},O=(a,e)=>{if(a===void 0)return;const n=(a.send_email??[]).filter(Boolean);for(const r of n){const s=typeof r.destination_address=="string"?r.destination_address:"";if(s!==""&&v(s)){const t=typeof r.name=="string"&&r.name.length>0?r.name:"send_email";e.push({fix:"Set destination_address to a verified Cloudflare Email Routing address.",level:"warn",message:`send_email binding "${t}" has a placeholder destination_address ("${s}").`})}}},j=(a,e)=>{const n=m(a,p);if(!g(n))return;let r;try{r=h(n,"utf8")}catch{return}const s=x(r).filter(t=>_.test(t.key)&&v(t.value)).map(t=>t.key);s.length>0&&e.push({fix:"Run `lunora dev` to auto-generate secrets, or fill them in by hand.",level:"warn",message:`${p} has unfilled secret value(s): ${s.join(", ")}.`})},D=a=>{const e=process.env.LUNORA_ADMIN_TOKEN;e===void 0||e.trim()===""?a.push({fix:"Set LUNORA_ADMIN_TOKEN (env or `.dev.vars`) to enable admin RPCs / studio.",level:"info",message:"LUNORA_ADMIN_TOKEN is not set."}):a.push({level:"pass",message:"LUNORA_ADMIN_TOKEN is set."})},E=async(a,e)=>{let n;try{({containers:n}=await y({projectRoot:a}))}catch{return}for(const r of n)r.exported?e.push({level:"pass",message:`container "${r.exportName}" is exported by the worker entry.`}):e.push({fix:'Add `export * from "./lunora/_generated/containers"` to your worker entry (or re-run `vis generate lunora-container`).',level:"fail",message:`container "${r.exportName}" is declared but ${r.className} is not exported by the worker entry.`})},L=/^[\^~><= ]+/u,C=/^(?<core>\d+\.\d+\.\d+)(?:-(?<channel>[a-z]+)\.(?<counter>\d+))?$/u,I=a=>{const e=a.replace(L,"").trim(),n=C.exec(e);if(!n?.groups)return;const{channel:r,core:s,counter:t}=n.groups;return{channel:r??"",core:s??"",counter:t===void 0?void 0:Number.parseInt(t,10),raw:e}},W=(a,e)=>{const n=m(a,"package.json");if(!g(n))return;let r;try{r=JSON.parse(h(n,"utf8"))}catch{return}const s={...r.devDependencies,...r.dependencies},t=[];for(const[o,i]of Object.entries(s)){if(o!=="lunorash"&&!o.startsWith("@lunora/"))continue;const f=I(i);f&&t.push({name:o,version:f})}if(t.length<2)return;const d=o=>o.map(i=>`${i.name}@${i.version.raw}`).join(", "),u=new Set(t.map(o=>o.version.core)),c=new Set(t.map(o=>o.version.channel));if(u.size>1){e.push({fix:'Run `pnpm update "@lunora/*" lunorash` (or `npm`/`yarn` equivalent) so every Lunora package moves to the same release.',level:"warn",message:`Lunora packages span ${String(u.size)} different versions: ${d(t)}.`});return}if(c.size>1){e.push({fix:"Pick one channel (all stable, or all alpha/beta) and update the odd package out.",level:"warn",message:`Lunora packages mix release channels (${[...c].map(o=>o===""?"stable":o).join(" + ")}): ${d(t)}.`});return}const l=t.map(o=>o.version.counter).filter(o=>o!==void 0);if(l.length>1){const o=Math.min(...l),i=Math.max(...l);e.push({level:"info",message:l.length===t.length&&o===i?`Lunora packages are all at ${[...c][0]??""}.${String(i)}.`:`Lunora pre-release counters span ${String(o)}–${String(i)}: ${d(t)}.`})}},T=async a=>{const e=a.cwd??process.cwd(),n=[],{parsed:r,path:s}=R(e);return A(r,s,n),k(r,n),O(r,n),j(e,n),D(n),W(e,n),await E(e,n),{code:n.some(t=>t.level==="fail")?1:0,findings:n}},F={fail:"FAIL",info:"INFO",pass:"PASS",warn:"WARN"},K=(a,e)=>{e.info("lunora doctor — project preflight");for(const s of a.findings){const t=`[${F[s.level]}] ${s.message}`;s.level==="fail"?e.error(t):s.level==="warn"?e.warn(t):e.info(t),s.fix!==void 0&&s.level!=="pass"&&e.info(` fix: ${s.fix}`)}const n=a.findings.filter(s=>s.level==="fail").length,r=a.findings.filter(s=>s.level==="warn").length;n>0?e.error(`${String(n)} failure(s), ${String(r)} warning(s).`):r>0?e.warn(`0 failures, ${String(r)} warning(s).`):e.success("all checks passed.")},U=S(async({cwd:a,logger:e})=>{const n=await T({cwd:a});return K(n,e),{code:n.code}});export{U as execute,T as runDoctor};