@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands
4 lines (3 loc) • 6.05 kB
JavaScript
import{writeFileSync as E,existsSync as y,cpSync as C,statSync as b,readdirSync as F,mkdirSync as D,readFileSync as L}from"node:fs";import{join as f,sep as j,resolve as O,dirname as I}from"node:path";import{SDK_TARGETS as M,SDK_LANGUAGES as x,generateSdk as B,isTypedSchema as K}from"@lunora/codegen";import{LunoraError as l}from"@lunora/errors";import{d as V}from"../packem_shared/command-DwC11Cwe.mjs";import{r as _,a as U,b as G,c as P,s as Q}from"../packem_shared/resolve-DZAGzg67.mjs";const S="gh:anolilab/lunora/sdks",v="lunora-transport.json",J=/_test\.|Tests?\./u,q=r=>r.startsWith("test_")||J.test(r),N=(r,s="")=>F(r).toSorted((e,t)=>e.localeCompare(t)).flatMap(e=>{const t=f(r,e),a=s.length>0?`${s}/${e}`:e;return b(t).isDirectory()?N(t,a):[a]}),A=(r,s,e,t)=>{const a=f(r,e);if(!y(a))throw new l("NOT_FOUND",`transport is missing ${e}`);const n=f(s,t);return C(a,n,{filter:o=>!q(o.slice(o.lastIndexOf(j)+1)),force:!0,recursive:!0}),b(n).isDirectory()?N(n).map(o=>`${t}/${o}`):[t]},k=(r,s)=>s.vendor.every(e=>y(f(r,e.from))),H=async(r,s)=>{const{language:e,logger:t,outputDirectory:a,registryOptions:n,target:o}=s,{cleanup:u,directory:h}=await P(e,{...n,ref:r},S);try{if(!k(h,o)){t.warn(`${S}/${e} does not exist at ${r}.`);return}return{files:o.vendor.flatMap(c=>A(h,a,c.from,c.to)).toSorted((c,d)=>c.localeCompare(d)),ref:r,source:`${S}/${e}`,versionMatched:r===_()}}finally{u()}},W=(r,s)=>{const{language:e,outputDirectory:t,target:a}=s,n=f(r,e);if(!y(n))throw new l("NOT_FOUND",`no transport at ${n} — --from must point at a directory of per-language transports (the repo's \`sdks/\`)`);if(!k(n,a)){const o=a.vendor.map(u=>u.from).join(", ");throw new l("NOT_FOUND",`${n} is not a ${e} transport — expected it to contain ${o}`)}return{files:a.vendor.flatMap(o=>A(n,t,o.from,o.to)).toSorted((o,u)=>o.localeCompare(u)),ref:"local",source:n,versionMatched:!1}},X=async r=>{const{allowUnsafeSource:s,from:e,language:t,logger:a,outputDirectory:n,ref:o,source:u,target:h}=r;if(e!==void 0)return W(e,{language:t,outputDirectory:n,target:h});const c={allowUnsafeSource:s,logger:a,names:[],source:u},d=async i=>H(await G(i,a),{language:t,logger:a,outputDirectory:n,registryOptions:c,target:h});if(o!==void 0&&o.length>0){const i=await d(o);if(i===void 0)throw new l("NOT_FOUND",`--ref ${o} carries no ${t} transport — pick a ref that does, or pass --from <dir> to copy a local one`);return i}const m=_(),g=await d(m).catch(i=>{const w=i instanceof Error?i.message.split(`
`)[0]??i.message:String(i);a.warn(`could not fetch the transport at ${m} (${w}).`)});if(g!==void 0)return g;const p=await d(void 0);if(p===void 0)throw new l("NOT_FOUND",`no ${t} transport at ${m} or on the release branch. This language may not be released yet — pass --from <dir> to copy one from a checkout, or --ref <tag> to name a ref that has it.`);return a.warn(`FALLBACK: the transport is NOT version-matched to this CLI (${U()}). Wanted ${m}, used ${p.ref}. The vendored wire protocol may not be the one this surface was generated against; ${v} records what was actually copied.`),p},z=(r,s,e)=>{const t={cliVersion:U(),files:e.files,language:s,note:e.versionMatched?"Vendored by `lunora sdk generate` at the ref matching the CLI that generated the surface beside it. Regenerate with a newer CLI to upgrade.":"Vendored by `lunora sdk generate` from a ref that is NOT this CLI's release tag, so the transport and the generated surface may be different protocol vintages.",ref:e.ref,source:e.source,versionMatched:e.versionMatched};E(f(r,v),`${JSON.stringify(t,void 0,4)}
`,"utf8")},Y=f("lunora","_generated","openrpc.json"),Z=r=>{if(!y(r))throw new l("NOT_FOUND",`no OpenRPC document at ${r} — run \`lunora codegen --api-spec openrpc\` (or \`--api-spec both\`) first, or pass --spec`);let s;try{s=JSON.parse(L(r,"utf8"))}catch(e){throw new l("BAD_REQUEST",`${r} is not valid JSON: ${e instanceof Error?e.message:String(e)}`)}if(s===null||typeof s!="object"||!Array.isArray(s.methods))throw new l("BAD_REQUEST",`${r} is not an OpenRPC document (no \`methods\` array)`);return s},ae=V(async({argument:r,cwd:s,logger:e,options:t})=>{const a=r[0]??"generate";if(a!=="generate")throw new l("BAD_REQUEST",`unknown subcommand "${a}" — the only one is \`lunora sdk generate\``);const n=t.lang??"python",o=M[n];if(o===void 0)throw new l("BAD_REQUEST",`unsupported --lang "${n}" — expected one of: ${x.join(" | ")}`);const u=Q("sdk generate",{allowUnsafeSource:t.allowUnsafeSource,logger:e,names:[],source:t.source});if(u!==void 0)throw new l("BAD_REQUEST",u);const h=O(s,t.spec??Y),c=O(s,t.out??f("sdk",n)),d=Z(h);d.methods.length===0&&e.warn(`${h} declares no methods — writing an empty SDK.`);const{files:m,undeclared:g,unrepresentable:p}=await B(d,o);D(c,{recursive:!0});const i=await X({allowUnsafeSource:t.allowUnsafeSource,from:t.from,language:n,logger:e,outputDirectory:c,ref:t.ref,source:t.source,target:o});z(c,n,i);for(const[$,R]of Object.entries(m)){const T=f(c,$);D(I(T),{recursive:!0}),E(T,R,"utf8")}const w=d.methods.filter($=>!K($.result?.schema)).length;return e.success(`Generated ${n} SDK for ${String(d.methods.length)} function(s) → ${c}`),e.info(`Vendored the ${n} transport (${String(i.files.length)} file(s)) from ${i.source} @ ${i.ref}${i.versionMatched?" — version-matched to this CLI":""}; see ${v}.`),e.info(o.requires.length>0?`Install in the consuming project: ${o.requires.join(", ")}.`:"Nothing to install — the transport is vendored and needs only the standard library."),g.length>0&&e.warn(`${String(g.length)} schema(s) are not expressible as a named ${n} model, so those arguments or returns stay untyped: ${g.join(", ")}`),p.length>0&&e.warn(`${String(p.length)} function(s) take or return a \`v.bigint()\`/\`v.bytes()\`, which no generated model can carry — their parameters stay untyped, so pass wire values directly: ${p.join(", ")}`),w>0&&e.warn(`${String(w)} of ${String(d.methods.length)} function(s) return an untyped result — declare \`.output()\` on them for typed returns.`),{code:0}});export{ae as execute};