@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands
2 lines (1 loc) • 4.91 kB
JavaScript
import{existsSync as d,readFileSync as E,mkdtempSync as b,rmSync as w}from"node:fs";import{tmpdir as O}from"node:os";import{LunoraError as i}from"@lunora/errors";import{dirname as v,join as c}from"@visulima/path";import{downloadTemplate as B}from"giget";import{fileURLToPath as C}from"node:url";import U from"./parseManifest-CwPTKdtS.mjs";const D="alpha",S="main",k=new Set(["alpha","beta","next"]),G=/^[\w./@-]+$/,P=t=>!t.includes("..")&&G.test(t),f=()=>{try{let t=v(C(import.meta.url));for(let e=0;e<6;e+=1){const r=c(t,"package.json");if(d(r)){const s=JSON.parse(E(r,"utf8"));if(s.name==="@lunora/cli"&&typeof s.version=="string")return s.version}const n=v(t);if(n===t)break;t=n}}catch{}return"0.0.0"},R=t=>{if(t==="0.0.0")return D;const e=t.split("+")[0]??t,r=e.indexOf("-");if(r!==-1){const[n]=e.slice(r+1).split(".");if(n!==void 0&&k.has(n))return n}return S},$=t=>{if(t!==void 0&&t.length>0){if(!P(t))throw new i("INTERNAL",`invalid --ref "${t}" — a ref may contain letters, digits, ".", "_", "-", "/", "@" and must not contain "..".`);return t}return R(f())},l="anolilab/lunora",A=/^[0-9a-f]{40}$/iu,T=String.raw`\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)+)?(?:\+[0-9A-Za-z.-]+)?`,F=new RegExp(String.raw`^v?${T}$`,"u"),M=new RegExp(String.raw`^(?:@[\w.-]+\/)?[\w.-]+@${T}$`,"u"),V=t=>A.test(t)||F.test(t)||M.test(t),j=()=>{const t=process.env.GITHUB_TOKEN??process.env.GH_TOKEN;return t!==void 0&&t.length>0?{authorization:`Bearer ${t}`}:{}},x=async t=>{try{const e=await fetch(`https://api.github.com/repos/${l}/commits/${encodeURIComponent(t)}`,{headers:{accept:"application/vnd.github+json","user-agent":"lunora-cli",...j()},signal:AbortSignal.timeout(1e4)});if(!e.ok)return;const r=await e.json();return typeof r.sha=="string"&&A.test(r.sha)?r.sha:void 0}catch{return}},H=async(t,e)=>{const r=$(t);if(V(r))return r;const n=await x(r);return n===void 0?(e.warn(`could not pin ${l}#${r} to a commit — fetching the UNPINNED branch (set GITHUB_TOKEN if rate-limited).`),r):(e.info(`pinned ${l}#${r} → ${n}`),n)},ct=(t=f())=>`@lunora/cli@${t}`,z="latest",it=(t=f())=>{const e=R(t);return e===S?z:e},K="https://registry.npmjs.org",W=()=>{const t=process.env.npm_config_registry,e=t!==void 0&&t.length>0?t:K;return e.endsWith("/")?e.slice(0,-1):e},Z=async(t,e)=>{try{const r=await fetch(`${W()}/${t.replaceAll("/","%2F")}`,{headers:{accept:"application/vnd.npm.install-v1+json"},signal:AbortSignal.timeout(1e4)});return r.ok?(await r.json())["dist-tags"]?.[e]:void 0}catch{return}},ut=async(t,e)=>{const r=new Map;return await Promise.all([...new Set(t)].map(async n=>{const s=await Z(n,e);s!==void 0&&r.set(n,s)})),r},_="gh:anolilab/lunora/registry",y=/^[A-Za-z0-9][\w-]*$/u,q=t=>{if(!y.test(t))throw new i("INTERNAL",`invalid registry item name "${t}" — names must match ${y.source} (letters, digits, "-", "_"; no path separators or "..")`)},J=t=>t.includes("..")?!1:t.startsWith("gh:")||t.startsWith("github:")||t.startsWith("https://"),Y=t=>t.from===void 0&&t.source!==void 0&&t.source.length>0&&!t.allowUnsafeSource&&!J(t.source),lt=(t,e)=>Y(e)?`${t}: refusing --source ${String(e.source)} — only gh:, github:, or https:// sources are allowed (and may not contain ".."). Re-run with --allow-unsafe-source if you really want this.`:void 0,N=async(t,e,r)=>{const n=b(c(O(),`lunora-${e}-fetch-`)),s=c(n,e);r.info(`fetching ${t}`);try{const a=await B(t,{cwd:n,dir:s,force:!0,install:!1,silent:!0});return r.info(a.commit?`resolved ${a.source} @ ${a.commit}`:`resolved ${a.source}`),{cleanup:()=>{w(n,{force:!0,recursive:!0})},directory:s}}catch(a){throw w(n,{force:!0,recursive:!0}),a}},p=new WeakMap,I=async t=>{const e=p.get(t);if(e!==void 0)return e;const r=t.source!==void 0&&t.source.length>0?Promise.resolve($(t.ref)):H(t.ref,t.logger);return p.set(t,r),r},Q=async(t,e,r=_)=>{if(q(t),e.from!==void 0){const s=c(e.from,t);if(!d(s))throw new i("INTERNAL",`registry item not found in local source: ${s}`);return{cleanup:()=>{},directory:s}}const n=e.source??r;return N(`${n}/${t}#${await I(e)}`,"item",e.logger)},dt=async t=>{if(t.from!==void 0){if(!d(t.from))throw new i("INTERNAL",`registry root not found: ${t.from}`);return{cleanup:()=>{},root:t.from}}const e=t.source??_,{cleanup:r,directory:n}=await N(`${e}#${await I(t)}`,"registry",t.logger);return{cleanup:r,root:n}},X=(t,e)=>{const r=JSON.parse(E(c(t,"registry.json"),"utf8"));return U(r,e)},ft=async(t,e)=>{const r=[],n=[],s=new Set,a=new Set,h=async o=>{if(s.has(o))return;if(a.has(o))throw new i("INTERNAL",`cyclic registry dependency detected at "${o}"`);a.add(o);const{cleanup:u,directory:g}=await Q(o,e);n.push(u);const m=X(g,o);for(const L of m.requires??[])await h(L);a.delete(o),s.add(o),r.push({directory:g,manifest:m})};try{for(const o of t)await h(o)}catch(o){for(const u of n)u();throw o}return{cleanups:n,items:r}};export{f as a,H as b,Q as c,it as d,ft as e,dt as f,X as g,ut as h,$ as i,ct as r,lt as s};