@lunora/cli 1.0.0-alpha.480 → 1.0.0-alpha.482
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1 -1
- package/dist/packem_chunks/handler.mjs +14 -14
- package/dist/packem_chunks/handler29.mjs +1 -1
- package/dist/packem_chunks/runDevCommand.mjs +8 -8
- package/dist/packem_shared/plan-BH6l7s48.mjs +5 -0
- package/dist/packem_shared/{planDevCommand-Bl6OrwXm.mjs → planDevCommand-Jr0dFWPV.mjs} +1 -1
- package/package.json +5 -5
- package/dist/packem_shared/plan-pog5AvMk.mjs +0 -5
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{C as o,V as a,r as n}from"./packem_chunks/cli.mjs";import{runCodegenCommand as m}from"./packem_chunks/runCodegenCommand.mjs";import{r as p}from"./packem_chunks/handler.mjs";import{runDevCommand as f}from"./packem_chunks/runDevCommand.mjs";import{p as i}from"./packem_shared/plan-
|
|
1
|
+
import{C as o,V as a,r as n}from"./packem_chunks/cli.mjs";import{runCodegenCommand as m}from"./packem_chunks/runCodegenCommand.mjs";import{r as p}from"./packem_chunks/handler.mjs";import{runDevCommand as f}from"./packem_chunks/runDevCommand.mjs";import{p as i}from"./packem_shared/plan-BH6l7s48.mjs";import{runInitCommand as l}from"./packem_chunks/runInitCommand.mjs";import{runMigrateGenerateCommand as I}from"./packem_shared/runMigrateGenerateCommand-CguLEIZp.mjs";import{runResetCommand as g}from"./packem_chunks/runResetCommand.mjs";import{runRpcCommand as T}from"./packem_chunks/runRpcCommand.mjs";import{EXIT_CODE as S,exitCodeForCode as A,exitCodeForError as c,exitCodeForStatus as M}from"./packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{insertSchemaExtension as v}from"./packem_shared/insertSchemaExtension-CbTH3lCQ.mjs";import{createLogger as b,pail as O}from"./packem_shared/createLogger-4TujevyB.mjs";import{diffSnapshots as y,renderAddColumn as L,renderCreateIndex as w,renderCreateTable as B,renderDropIndex as P,renderDropTable as U,renderMigrationFile as V,validatorKindToSqlType as W}from"./packem_shared/diffSnapshots-Y1G8am6C.mjs";import{default as N}from"./packem_shared/schemaIrToSnapshot-Ck7DSPdf.mjs";import{createRecordingSpawner as j,defaultSpawner as q}from"./packem_shared/createRecordingSpawner-DchLi0gR.mjs";import{default as K}from"./packem_shared/parseManifest-CwPTKdtS.mjs";import{D as Y,r as Z}from"./packem_shared/import-3Y59Ry9F.mjs";import{REQUIRED_COMPATIBILITY_DATE as z,REQUIRED_FLAG as J,validateWranglerProject as $,validateWranglerConfig as rr}from"@lunora/config/cloudflare";import{b as or}from"./packem_shared/catalog-CahzmDLV.mjs";import{r as nr,a as tr,b as mr}from"./packem_shared/commands-CkuCMUP_.mjs";import{runExportCommand as pr}from"./packem_shared/runExportCommand-_PtkJJDT.mjs";export{o as COMMANDS,Y as DEFAULT_IMPORT_BATCH_SIZE,S as EXIT_CODE,z as REQUIRED_COMPATIBILITY_DATE,J as REQUIRED_FLAG,a as VERSION,or as buildRegistryIndex,b as createLogger,j as createRecordingSpawner,q as defaultSpawner,y as diffSnapshots,A as exitCodeForCode,c as exitCodeForError,M as exitCodeForStatus,v as insertSchemaExtension,O as pail,K as parseManifest,i as planDevCommand,L as renderAddColumn,w as renderCreateIndex,B as renderCreateTable,P as renderDropIndex,U as renderDropTable,V as renderMigrationFile,nr as runAddCommand,tr as runBuildIndexCommand,n as runCli,m as runCodegenCommand,p as runDeployCommand,f as runDevCommand,pr as runExportCommand,Z as runImportCommand,l as runInitCommand,I as runMigrateGenerateCommand,mr as runRegistryViewCommand,g as runResetCommand,T as runRpcCommand,N as schemaIrToSnapshot,$ as validateWrangler,rr as validateWranglerConfig,W as validatorKindToSqlType};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import{mkdirSync as
|
|
1
|
+
import{mkdirSync as Re,writeFileSync as X,existsSync as b,readFileSync as E,readdirSync as K,statSync as Te,rmSync as oe,mkdtempSync as pt,realpathSync as vt}from"node:fs";import{dirname as R,join as p,resolve as _,relative as L,sep as x,extname as yt}from"node:path";import{PROJECT_CONFIG_FILENAMES as wt,isTestPath as we,discoverMigrations as kt,readServiceBindings as bt,readBoundServiceBindings as yr,runCodegen as $t}from"@lunora/codegen";import{readLinkedProject as Ce,discoverSchemaInfo as Pe,resolveSchemaDirectory as O,startDevProcess as St,acceptsConnection as Et,readApiSpecMarker as wr,resolveTargetOrThrow as ke,detectFramework as At,DEFAULT_BUNDLE_META_PATH as Rt,STRICT_ADVISORIES_ENV as Tt,LAZY_FUNCTIONS_ENV as Ct,BUNDLE_META_PATH_ENV as Pt,BUILD_TARGET_ENV as _t,BUILD_ROOT_ENV as It,canonicalPath as jt,COMPOSED_WORKER_ENTRY as Ve,discoverContainerInfo as kr,D1_PLACEHOLDER_ID as Ke,writeLinkedProject as Dt,DEV_VARS_FILE as N,parseDevVariableEntries as br,secretSourcesFromBindings as Ot,inferLunoraBindings as $r,requiredSecrets as Lt,mintableSecretKeysFor as Nt,generateSecretValue as xt,upsertDevVariableLine as Ft,writeDevVariablesFileAtomically as Mt,isPlaceholderValue as Ut,ensureGitignoreEntries as Bt,resolveLazyFunctions as de,resolveDeployDriver as le,MissingBuildOutputError as Wt,clearApiSpecMarker as Ht,createDiscoveryCache as qt,planToolchainInvocation as Sr,probeCelldToolchain as Gt,runPostCodegenHook as Vt}from"@lunora/config";import{findWranglerFile as D,readWranglerJsonc as I,HYPERDRIVE_PLACEHOLDER_ID as Er,applyModify as Ar,mergeWranglerEnvironment as ue,bindingsNeedingDatabase as Kt,warnCloudflareCliConfigOnce as zt,validateWranglerProject as Jt,wranglerCodegenInputs as Yt,reconcileWranglerBindings as Xt,reconcileWranglerCompatibilityDate as Qt,reconcileWranglerCrons as Zt,describePreservedCrons as en}from"@lunora/config/cloudflare";import{Spinner as rn}from"@visulima/spinner";import{r as tn}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{n as nn,r as on,a as sn}from"../packem_shared/admin-url-DJB8P6Jh.mjs";import{r as _e,a as an,e as cn}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as dn}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{e as ln,m as un,g as fn,i as Rr,d as gn,b as hn}from"../packem_shared/vectorize-metadata-DK61NmVx.mjs";import{w as mn}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as pn}from"../packem_shared/command-BpMv2n0o.mjs";import{r as Ie}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{a as vn}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as y}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{spawn as Tr,spawnSync as yn}from"node:child_process";import{createHash as wn,randomUUID as Cr}from"node:crypto";import{setTimeout as kn}from"node:timers/promises";import{stripVTControlCharacters as bn}from"node:util";import{toolchainExecArgs as z,detectPackageManager as F,execArgsFor as je}from"@lunora/config/cli";import{ts as w,Project as Pr,SyntaxKind as k,Node as S}from"ts-morph";import{r as _r,c as Ir}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{f as $n}from"../packem_shared/free-port-DjbFhaaT.mjs";import{l as V}from"../packem_shared/js-literal-FWLi94Lh.mjs";import{containerBuildTag as Sn}from"@lunora/container";import{a as En,i as An}from"../packem_shared/docker-DpVxvYpL.mjs";import{defaultSpawner as q}from"../packem_shared/createRecordingSpawner-DchLi0gR.mjs";import{a as De}from"../packem_shared/resolve-target-BRvw8Pf7.mjs";import{p as Rn,H as Tn,a as Cn,r as Pn,c as _n,b as In}from"../packem_shared/health-probe-BLnoq_oC.mjs";import{runAdvisor as jn,openShardAccess as ze}from"@lunora/advisor";import{join as A,relative as J,dirname as jr}from"@visulima/path";import{runMigrateDataCommand as Dn}from"../packem_shared/runMigrateGenerateCommand-CguLEIZp.mjs";import{c as On}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{l as Ln}from"../packem_shared/wrangler-secrets-C9siMBwN.mjs";const Nn=Object.freeze({edges:[],nodes:[],modules:[],unresolved:[],version:1}),xn=20,Je=e=>`${e.from} -${e.kind}-> ${e.to}`,ee=(e,r)=>{const t=new Set(e);return r.filter(n=>!t.has(n)).toSorted((n,o)=>n.localeCompare(o))},Fn=(e,r)=>{const t=e.modules.map(i=>i.name),n=r.modules.map(i=>i.name),o=e.edges.map(i=>Je(i)),s=r.edges.map(i=>Je(i));return{addedEdges:ee(o,s),addedModules:ee(t,n),removedEdges:ee(s,o),removedModules:ee(n,t)}},re=(e,r,t)=>{if(t.length===0)return[];const n=t.slice(0,xn).map(s=>` ${e} ${s}`),o=t.length-n.length;return[`${r}:`,...n,...o>0?[` … and ${o.toString()} more`]:[]]},Mn=e=>{const r=[...re("+","modules added",e.addedModules),...re("-","modules removed",e.removedModules),...re("+","edges added",e.addedEdges),...re("-","edges removed",e.removedEdges)];return r.length===0?void 0:`architecture changes since the last deploy:
|
|
2
2
|
${r.join(`
|
|
3
|
-
`)}`},
|
|
4
|
-
`,"utf8")}catch(c){n.warn(`could not record the deployed architecture at ${o}: ${c instanceof Error?c.message:String(c)}`)}}},Wn=e=>{const{cwd:r,env:t,logger:n,mintedSecretsFile:o}=e;try{const s=Ce(r),i=s?.workerName??Ie(r),a=e.url??s?.workerUrl;n.success("deploy complete"),n.info(` worker: ${i??"(see wrangler output above)"}`),t!==void 0&&n.info(` env: ${t}`),a===void 0?n.info(" url: run `lunora link --url <https://your-worker>` to record it"):n.info(` url: ${a}`),o!==void 0&&n.info(` secrets: generated value(s) recorded in ${o}`),e.logsAvailable&&n.info(" logs: lunora logs")}catch{}},Hn=/\/index$/u,qn=/[^\dA-Za-z]/gu,Gn=e=>e.replace(Hn,"").split("/").map(r=>r.replaceAll(qn,"_")),Vn=e=>Gn(e).join("_"),Kn=/[^a-z0-9-]/gu,zn=/-$/u,Xe=7,Jn=(e,r)=>{const t=e.toLowerCase(),n=t.replaceAll(Kn,"-");if(n===t&&n.length<=r)return n;const o=yn("sha256").update(t).digest("hex").slice(0,Xe-1);return`${n.slice(0,r-Xe).replace(zn,"")}-${o}`},Yn=32,Dr=15e3,Xn=5432,Qn=3306,Zn=/[^A-Za-z0-9]/gu,Y=e=>`LUNORA_${e.replaceAll(Zn,"_").toUpperCase()}_URL`,eo=e=>{const r=new Map;for(const t of e){const n=Y(t),o=r.get(n);if(o!==void 0)return{bindings:[o,t],variable:n};r.set(n,t)}},ro=(e,r)=>Jn(`${e}-${r}`,Yn),Qe=e=>e==="postgresql"?"postgres":e==="mariadb"?"mysql":e,to=e=>e==="mysql"?Qn:Xn,no=e=>{try{const r=new URL(e),t=r.protocol.slice(0,-1),n=decodeURIComponent(r.pathname.slice(1));return!["mariadb","mysql","postgres","postgresql"].includes(t)||r.hostname===""||n===""?void 0:{database:n,host:r.hostname,password:decodeURIComponent(r.password),port:r.port===""?to(Qe(t)):Number(r.port),scheme:Qe(t),user:decodeURIComponent(r.username)}}catch{return}},oo=(e,r)=>{const t=typeof r?.password=="string"?r.password:"",n=t===""?[]:[t,JSON.stringify(t).slice(1,-1),encodeURIComponent(t)];let o=e;for(const s of n)o=o.replaceAll(s,"<redacted>");return o.split(" ").map(s=>s.includes("://")?"<url>":s).join(" ")},Or=e=>(Array.isArray(e)?e:[]).flatMap((r,t)=>{const{binding:n,id:o}=typeof r=="object"&&r!==null?r:{};return typeof n=="string"&&o===Er?[{binding:n,index:t}]:[]}),se=e=>{let r="";try{const{errors:t}=JSON.parse(e.text);r=(t??[]).flatMap(n=>typeof n.message=="string"?[n.message]:[]).slice(0,2).join("; ")}catch{}return r===""?`HTTP ${String(e.status)}`:`HTTP ${String(e.status)}: ${r}`},so=e=>typeof e=="string"||typeof e=="number"?e.toString():void 0,io=["scheme","host","port","database","user"],ao=e=>e==="postgresql"?"postgres":e,Ze=(e,r)=>{const t=so(r);return e==="scheme"?ao(t):t},er=50,co=async(e,r)=>{for(let t=1;t<=er;t+=1){const n=await e(t===1?"/hyperdrive/configs":`/hyperdrive/configs?page=${String(t)}`);if(!n.ok)throw new Error(`listing Hyperdrive configs failed (${se(n)})`);if(!Array.isArray(n.body.result))throw new TypeError("the listing of Hyperdrive configs had no result list");const o=n.body.result.find(i=>i.name===r),s=typeof n.body.result_info=="object"&&n.body.result_info!==null?n.body.result_info:{};if(o!==void 0||typeof s.total_pages!="number"||t>=s.total_pages)return o}throw new Error(`the listing of Hyperdrive configs has more than ${String(er)} pages, so "${r}" can't be shown to be absent`)},lo=async(e,r,t,n)=>{const o=n?{caching:{disabled:!0}}:{},s=await co(e,r);if(typeof s?.id=="string"){const c=io.filter(d=>Ze(d,s.origin?.[d])!==Ze(d,t[d]));if(c.length>0)throw new Error(`a Hyperdrive config named "${r}" already exists for a different origin, or one that can't be confirmed (${c.join(", ")}). Delete it, or rename the Worker, so the binding does not reach the wrong database.`);const l=await e(`/hyperdrive/configs/${encodeURIComponent(s.id)}`,{body:JSON.stringify({origin:t,...o}),method:"PATCH"});if(!l.ok)throw new Error(`updating its credentials failed (${se(l)})`);return{id:s.id,reused:!0}}const i=await e("/hyperdrive/configs",{body:JSON.stringify({name:r,origin:t,...o}),method:"POST"});if(!i.ok)throw new Error(`creating it failed (${se(i)})`);const{id:a}=typeof i.body.result=="object"&&i.body.result!==null?i.body.result:{};if(typeof a!="string")throw new TypeError("the response carried no id");return{id:a,reused:!1}},uo=(e,r,t,n)=>r===void 0&&n?`${Y(e)} is set but is not a usable connection string for the "${e}" Hyperdrive binding: it needs a postgres://, postgresql:// or mysql:// scheme, a host and a database name, with any special characters percent-encoded.`:r===void 0?`the "${e}" Hyperdrive binding has no config yet. Set ${Y(e)} to its postgres:// or mysql:// connection string and Lunora creates the config, or run \`wrangler hyperdrive create\` and put its id in wrangler.jsonc.`:t?void 0:`creating the "${e}" Hyperdrive config needs CLOUDFLARE_API_TOKEN (Hyperdrive: Edit) and CLOUDFLARE_ACCOUNT_ID, or run \`wrangler hyperdrive create\` and put its id in wrangler.jsonc.`,fo=async(e,r)=>{const t=ro(e.workerName,e.binding),{id:n,reused:o}=await lo(async(s,i)=>Ir({accountId:e.accountId,apiToken:e.token,...e.fetch===void 0?{}:{fetch:e.fetch},init:{...i,signal:AbortSignal.timeout(Dr)},path:s}),t,e.origin,e.disableCaching);return e.logger.success(`${o?"reusing":"created"} Hyperdrive config "${t}" for ${e.binding} → ${e.wranglerPath}`),{id:n,text:Ar(r,[...e.path,e.index,"id"],n)}},go=e=>e===void 0||e===""?void 0:e,Oe=e=>{const r=p(R(e),".wrangler","deploy","config.json");try{const{configPath:t}=JSON.parse(E(r,"utf8"));return typeof t=="string"?_(R(r),t):void 0}catch{return}},ho=(e,r,t,n,o)=>{const s=Oe(e);if(s===void 0||s===e)return;if(n.built!==n.deployed){o.warn(`the build output ${s} was made for ${n.built===void 0?"the default environment":`"${n.built}"`}, not ${n.deployed===void 0?"the default environment":`"${n.deployed}"`}, so the "${r}" Hyperdrive id was not written there. Build with CLOUDFLARE_ENV set to the environment you deploy, then deploy again.`);return}let i;try{i=I(s)}catch{return}const a=Or(i.parsed?.hyperdrive).find(c=>c.binding===r);a!==void 0&&X(s,Ar(i.text,["hyperdrive",a.index,"id"],t),"utf8")},mo=(e,r)=>{const t=typeof e.name=="string"&&e.name!==""?e.name:"lunora";if(r===void 0)return t;const n=e.env?.[r]?.name;return typeof n=="string"&&n!==""?n:`${t}-${r}`},fe=(e,r,t)=>{const n=t??D(e),o=n===void 0?void 0:I(n).parsed;if(n===void 0||o===void 0)return;const s=r===void 0?void 0:o.env?.[r],i=Or(r===void 0?o.hyperdrive:s?.hyperdrive);return i.length===0?void 0:{accountId:s?.account_id??o.account_id,entries:i,path:r===void 0?["hyperdrive"]:["env",r,"hyperdrive"],workerName:mo(o,r),wranglerPath:n}},po=(e,r,t)=>{const n=eo(e.map(({binding:o})=>o));if(n!==void 0){const o=`deploy blocked: the Hyperdrive bindings ${n.bindings.map(s=>`"${s}"`).join(" and ")} both read ${n.variable} and would share one config. Rename one so their names differ in letters or digits, not only in punctuation.`;return t.logger.error(o),o}for(const{binding:o,origin:s,supplied:i}of e){const a=uo(o,s,r,i);if(a!==void 0&&t.dryRun===!0)t.logger.warn(`dry run: ${a} A real deploy stops here.`);else if(a!==void 0){const c=`deploy blocked: ${a}`;return t.logger.error(c),c}}},vo=async(e,r,t,n)=>{let o;try{o=await e(`/hyperdrive/configs/${encodeURIComponent(t)}`)}catch(i){n.warn(`could not check whether the "${r}" Hyperdrive config has query caching off (${i instanceof Error?i.message:String(i)}).`);return}if(!o.ok){n.warn(`could not check whether the "${r}" Hyperdrive config has query caching off (${se(o)}).`);return}const{caching:s}=typeof o.body.result=="object"&&o.body.result!==null?o.body.result:{};s?.disabled!==!0&&n.warn(`the "${r}" Hyperdrive config has query caching on, so .global() tables would read rows up to 75 s old. Run \`wrangler hyperdrive update ${t} --caching-disabled\`.`)},yo=async(e,r)=>{const t=e.wranglerPath??D(e.cwd),n=t===void 0?void 0:I(t).parsed,o=e.wranglerEnvironment===void 0?n:n?.env?.[e.wranglerEnvironment],{accountId:s,token:i}=_r(r,o?.account_id??n?.account_id);if(s===void 0||i===void 0)return;const a=async(c,l)=>Ir({accountId:s,apiToken:i,...e.fetch===void 0?{}:{fetch:e.fetch},init:{...l,signal:AbortSignal.timeout(Dr)},path:c});for(const c of Array.isArray(o?.hyperdrive)?o.hyperdrive:[]){const{binding:l,id:d}=typeof c=="object"&&c!==null?c:{};typeof l=="string"&&typeof d=="string"&&d!==Er&&await vo(a,l,d,e.logger)}},wo=async e=>{const r=e.environment??process.env;e.disableCaching===!0&&e.checkOnly!==!0&&e.dryRun!==!0&&await yo(e,r);const t=fe(e.cwd,e.wranglerEnvironment,e.wranglerPath);if(t===void 0)return;const{entries:n,path:o,wranglerPath:s}=t,{accountId:i,token:a}=_r(r,t.accountId),c=n.map(({binding:d})=>{const u=r[Y(d)]??"";return{binding:d,origin:no(u),supplied:u!==""}}),l=po(c,a!==void 0&&i!==void 0,e);if(l!==void 0)return l;if(!(e.dryRun===!0||e.checkOnly===!0))for(const[d,{binding:u,index:f}]of n.entries()){const g=c[d]?.origin;if(!(g===void 0||a===void 0||i===void 0))try{const{id:h,text:v}=await fo({accountId:i,binding:u,disableCaching:e.disableCaching===!0,fetch:e.fetch,index:f,logger:e.logger,origin:g,path:o,token:a,workerName:t.workerName,wranglerPath:s},E(s,"utf8"));X(s,v,"utf8"),ho(s,u,h,{built:go(r.CLOUDFLARE_ENV),deployed:e.wranglerEnvironment},e.logger)}catch(h){const v=h instanceof Error?h.message:String(h),m=`deploy blocked: the "${u}" Hyperdrive config could not be set up: ${oo(v,c[d]?.origin)}`;return e.logger.error(m),m}}},ko=(e,r)=>{const t=fe(e,r.env)?.entries[0]?.binding;if(t===void 0)return;const n=`prepare blocked: the "${t}" Hyperdrive binding still has its placeholder id. Run \`lunora deploy\` with ${Y(t)} set to create the config, or put the id from \`wrangler hyperdrive create\` in wrangler.jsonc.`;return r.logger.error(n),n},bo=(e,r)=>{const t=fe(e,r.env)?.entries[0]?.binding;if(t===void 0)return;const n=`deploy blocked: the "${t}" Hyperdrive binding has no config, and a --temporary deploy cannot create one: it would be made in your account while the Worker goes to a temporary one, which a temporary account has no database for. Deploy without --temporary.`;return r.logger.error(n),n},H=async(e,r,t,n,o="create",s)=>{if(n!=="cloudflare")return;if(t==="prepare")return ko(e,r);if(r.temporary===!0)return bo(e,r);const i=r.clientBuildEnvironment===void 0?void 0:{...process.env,CLOUDFLARE_ENV:r.clientBuildEnvironment},a=Pe(e,x(e),r.discoveryCache);return a.error!==void 0&&r.logger.warn(`could not read the schema (${a.error}), so Hyperdrive configs are set up with query caching off.`),wo({checkOnly:o==="check",cwd:e,disableCaching:a.error!==void 0||a.info?.hasHyperdriveGlobalTable===!0,dryRun:r.dryRun,environment:i,logger:r.logger,wranglerEnvironment:r.env,wranglerPath:s})},$o=(e,r,t)=>fe(e,r,t)!==void 0,me=[".ts",".tsx",".mts",".js",".mjs"],So=/^[a-z]+:/u,rr=/\.[cm]?js$/u,Eo=e=>!e.startsWith(".")&&!e.startsWith("/")&&!e.startsWith("#")&&!So.test(e),Ao=e=>e.split("/").slice(0,e.startsWith("@")?2:1).join("/"),Ro=e=>e.split(N).some(r=>r==="_generated"||r==="node_modules"),To=e=>e.split(N).includes("node_modules"),tr=(e,r)=>{const t=_(R(e),r),n=rr.test(t)?t.replace(rr,""):void 0;return[t,...n===void 0?[]:me.map(s=>`${n}${s}`),...me.map(s=>`${t}${s}`),...me.map(s=>p(t,`index${s}`))].find(s=>b(s)&&Re(s).isFile())},Co=/^lunora\.config\.[cm]?[jt]s$/u,ke=e=>{if(typeof e=="string")return e;if(typeof e=="object"&&e!==null)for(const[r,t]of Object.entries(e)){const n=r==="types"?void 0:ke(t);if(n!==void 0)return n}},Po=(e,r)=>{let t;try{t=JSON.parse(E(p(e,"package.json"),"utf8")).imports}catch{return}const n=ke(t?.[r]);if(n!==void 0)return n;for(const[o,s]of Object.entries(t??{})){const[i="",a=""]=o.split("*"),c=ke(s);if(c!==void 0&&o.includes("*")&&r.startsWith(i)&&r.endsWith(a)&&r.length>=o.length-1){const l=r.slice(i.length,r.length-a.length);return c.replaceAll("*",()=>l)}}},_o=(e,r)=>{const t=p(e,r,"_generated"),n=K(e).find(o=>Co.test(o));return[...b(t)?K(t).filter(o=>o.endsWith(".ts")).map(o=>p(t,o)):[],...n===void 0?[]:[p(e,n)]]},Io=e=>{const r=w.findConfigFile(e,n=>w.sys.fileExists(n));if(r===void 0)return!1;const t=w.readConfigFile(r,n=>w.sys.readFile(n));return w.parseJsonConfigFileContent(t.config??{},w.sys,R(r)).options.verbatimModuleSyntax===!0},nr=e=>e!==void 0&&(w.isNamedImports(e)||w.isNamedExports(e))&&e.elements.length>0&&e.elements.every(r=>r.isTypeOnly),jo=(e,r)=>{if(w.isImportDeclaration(e)){const{importClause:t}=e;return!(t?.phaseModifier===w.SyntaxKind.TypeKeyword||!r&&t?.name===void 0&&nr(t?.namedBindings))&&w.isStringLiteral(e.moduleSpecifier)?e.moduleSpecifier.text:void 0}if(w.isExportDeclaration(e))return!(e.isTypeOnly||!r&&nr(e.exportClause))&&e.moduleSpecifier!==void 0&&w.isStringLiteral(e.moduleSpecifier)?e.moduleSpecifier.text:void 0},Do=e=>{if(!w.isCallExpression(e)||e.expression.kind!==w.SyntaxKind.ImportKeyword)return;const[r]=e.arguments;return r!==void 0&&w.isStringLiteralLike(r)?r.text:void 0},Oo=(e,r)=>{const t=w.createSourceFile(e,E(e,"utf8"),w.ScriptTarget.Latest,!1),n=[],o=[],s=i=>{const a=Do(i),c=a??jo(i,r);c?.startsWith(".")||c?.startsWith("#")?o.push(c):a!==void 0&&Eo(a)&&n.push(a),w.forEachChild(i,s)};return s(t),{dynamicPackages:n,relative:o}},Lo=(e,r)=>{for(let t=e;;t=R(t)){if(b(p(t,"node_modules",r,"package.json")))return!0;if(R(t)===t)return!1}},No=(e,r,t)=>{const n=Io(e),o=new Set,s=new Set,i=r!==void 0&&b(_(e,r))?[_(e,r)]:_o(e,t);for(let a=i.shift();a!==void 0;a=i.shift())if(!o.has(a)&&!To(O(e,a))){o.add(a);const{dynamicPackages:c,relative:l}=Oo(a,n);if(!Ro(O(e,a)))for(const d of c)s.add(d);i.push(...l.flatMap(d=>{if(!d.startsWith("#"))return tr(a,d)??[];const u=Po(e,d);return u===void 0?[]:tr(p(e,"package.json"),u)??[]}))}return[...s].filter(a=>Lo(e,Ao(a))).toSorted((a,c)=>a.localeCompare(c))},xo=18787,or=3,be=12e4,Lr=5e3,W=10,Nr=new Set([".js",".mjs",".mts",".ts",".tsx"]),Fo=/\(void 0\)|is not a function|before initialization|of undefined/u,Mo=e=>typeof e=="object"&&e!==null&&typeof e.functionPath=="string"&&(e.deferred===void 0||["agent","module","queue","workflow"].includes(e.deferred))&&typeof e.message=="string",Uo=e=>{const r=x(e),t=new Map;let n;try{n=K(p(e,r),{recursive:!0,encoding:"utf8"})}catch{return t}for(const o of n){const s=yt(o);Nr.has(s)&&!o.split(N).some(i=>i==="_generated"||i==="node_modules")&&t.set(Vn(o.slice(0,-s.length).split(N).join("/")),p(r,o))}return t},sr=e=>Fo.test(e)?`
|
|
5
|
-
hint: if this module is part of a circular import, an import it reads is still undefined while it evaluates`:"",
|
|
3
|
+
`)}`},Un=(e,r)=>p(e,".lunora",r===void 0?"architecture.json":`architecture.${r}.json`),Bn=e=>{if(b(e))try{const r=JSON.parse(E(e,"utf8"));return r.version===1&&Array.isArray(r.modules)&&Array.isArray(r.edges)?r:void 0}catch{return}},Wn=e=>{const{cwd:r,environment:t,logger:n}=e,o=Un(r,t),s=Bn(o),i=e.current??(s===void 0?void 0:Nn);if(i===void 0)return;const a=s===void 0?void 0:Mn(Fn(s,i));return a!==void 0&&n.info(a),()=>{try{Re(R(o),{recursive:!0}),X(o,`${JSON.stringify(i,void 0,2)}
|
|
4
|
+
`,"utf8")}catch(c){n.warn(`could not record the deployed architecture at ${o}: ${c instanceof Error?c.message:String(c)}`)}}},Hn=e=>{const{cwd:r,env:t,logger:n,mintedSecretsFile:o}=e;try{const s=Ce(r),i=s?.workerName??Ie(r),a=e.url??s?.workerUrl;n.success("deploy complete"),n.info(` worker: ${i??"(see wrangler output above)"}`),t!==void 0&&n.info(` env: ${t}`),a===void 0?n.info(" url: run `lunora link --url <https://your-worker>` to record it"):n.info(` url: ${a}`),o!==void 0&&n.info(` secrets: generated value(s) recorded in ${o}`),e.logsAvailable&&n.info(" logs: lunora logs")}catch{}},qn=/\/index$/u,Gn=/[^\dA-Za-z]/gu,Vn=e=>e.replace(qn,"").split("/").map(r=>r.replaceAll(Gn,"_")),Kn=e=>Vn(e).join("_"),zn=/[^a-z0-9-]/gu,Jn=/-$/u,Ye=7,Yn=(e,r)=>{const t=e.toLowerCase(),n=t.replaceAll(zn,"-");if(n===t&&n.length<=r)return n;const o=wn("sha256").update(t).digest("hex").slice(0,Ye-1);return`${n.slice(0,r-Ye).replace(Jn,"")}-${o}`},Xn=32,Dr=15e3,Qn=5432,Zn=3306,eo=/[^A-Za-z0-9]/gu,Y=e=>`LUNORA_${e.replaceAll(eo,"_").toUpperCase()}_URL`,ro=e=>{const r=new Map;for(const t of e){const n=Y(t),o=r.get(n);if(o!==void 0)return{bindings:[o,t],variable:n};r.set(n,t)}},to=(e,r)=>Yn(`${e}-${r}`,Xn),Xe=e=>e==="postgresql"?"postgres":e==="mariadb"?"mysql":e,no=e=>e==="mysql"?Zn:Qn,oo=e=>{try{const r=new URL(e),t=r.protocol.slice(0,-1),n=decodeURIComponent(r.pathname.slice(1));return!["mariadb","mysql","postgres","postgresql"].includes(t)||r.hostname===""||n===""?void 0:{database:n,host:r.hostname,password:decodeURIComponent(r.password),port:r.port===""?no(Xe(t)):Number(r.port),scheme:Xe(t),user:decodeURIComponent(r.username)}}catch{return}},so=(e,r)=>{const t=typeof r?.password=="string"?r.password:"",n=t===""?[]:[t,JSON.stringify(t).slice(1,-1),encodeURIComponent(t)];let o=e;for(const s of n)o=o.replaceAll(s,"<redacted>");return o.split(" ").map(s=>s.includes("://")?"<url>":s).join(" ")},Or=e=>(Array.isArray(e)?e:[]).flatMap((r,t)=>{const{binding:n,id:o}=typeof r=="object"&&r!==null?r:{};return typeof n=="string"&&o===Er?[{binding:n,index:t}]:[]}),se=e=>{let r="";try{const{errors:t}=JSON.parse(e.text);r=(t??[]).flatMap(n=>typeof n.message=="string"?[n.message]:[]).slice(0,2).join("; ")}catch{}return r===""?`HTTP ${String(e.status)}`:`HTTP ${String(e.status)}: ${r}`},io=e=>typeof e=="string"||typeof e=="number"?e.toString():void 0,ao=["scheme","host","port","database","user"],co=e=>e==="postgresql"?"postgres":e,Qe=(e,r)=>{const t=io(r);return e==="scheme"?co(t):t},Ze=50,lo=async(e,r)=>{for(let t=1;t<=Ze;t+=1){const n=await e(t===1?"/hyperdrive/configs":`/hyperdrive/configs?page=${String(t)}`);if(!n.ok)throw new Error(`listing Hyperdrive configs failed (${se(n)})`);if(!Array.isArray(n.body.result))throw new TypeError("the listing of Hyperdrive configs had no result list");const o=n.body.result.find(i=>i.name===r),s=typeof n.body.result_info=="object"&&n.body.result_info!==null?n.body.result_info:{};if(o!==void 0||typeof s.total_pages!="number"||t>=s.total_pages)return o}throw new Error(`the listing of Hyperdrive configs has more than ${String(Ze)} pages, so "${r}" can't be shown to be absent`)},uo=async(e,r,t,n)=>{const o=n?{caching:{disabled:!0}}:{},s=await lo(e,r);if(typeof s?.id=="string"){const c=ao.filter(d=>Qe(d,s.origin?.[d])!==Qe(d,t[d]));if(c.length>0)throw new Error(`a Hyperdrive config named "${r}" already exists for a different origin, or one that can't be confirmed (${c.join(", ")}). Delete it, or rename the Worker, so the binding does not reach the wrong database.`);const l=await e(`/hyperdrive/configs/${encodeURIComponent(s.id)}`,{body:JSON.stringify({origin:t,...o}),method:"PATCH"});if(!l.ok)throw new Error(`updating its credentials failed (${se(l)})`);return{id:s.id,reused:!0}}const i=await e("/hyperdrive/configs",{body:JSON.stringify({name:r,origin:t,...o}),method:"POST"});if(!i.ok)throw new Error(`creating it failed (${se(i)})`);const{id:a}=typeof i.body.result=="object"&&i.body.result!==null?i.body.result:{};if(typeof a!="string")throw new TypeError("the response carried no id");return{id:a,reused:!1}},fo=(e,r,t,n)=>r===void 0&&n?`${Y(e)} is set but is not a usable connection string for the "${e}" Hyperdrive binding: it needs a postgres://, postgresql:// or mysql:// scheme, a host and a database name, with any special characters percent-encoded.`:r===void 0?`the "${e}" Hyperdrive binding has no config yet. Set ${Y(e)} to its postgres:// or mysql:// connection string and Lunora creates the config, or run \`wrangler hyperdrive create\` and put its id in wrangler.jsonc.`:t?void 0:`creating the "${e}" Hyperdrive config needs CLOUDFLARE_API_TOKEN (Hyperdrive: Edit) and CLOUDFLARE_ACCOUNT_ID, or run \`wrangler hyperdrive create\` and put its id in wrangler.jsonc.`,go=async(e,r)=>{const t=to(e.workerName,e.binding),{id:n,reused:o}=await uo(async(s,i)=>Ir({accountId:e.accountId,apiToken:e.token,...e.fetch===void 0?{}:{fetch:e.fetch},init:{...i,signal:AbortSignal.timeout(Dr)},path:s}),t,e.origin,e.disableCaching);return e.logger.success(`${o?"reusing":"created"} Hyperdrive config "${t}" for ${e.binding} → ${e.wranglerPath}`),{id:n,text:Ar(r,[...e.path,e.index,"id"],n)}},ho=e=>e===void 0||e===""?void 0:e,Oe=e=>{const r=p(R(e),".wrangler","deploy","config.json");try{const{configPath:t}=JSON.parse(E(r,"utf8"));return typeof t=="string"?_(R(r),t):void 0}catch{return}},mo=(e,r,t,n,o)=>{const s=Oe(e);if(s===void 0||s===e)return;if(n.built!==n.deployed){o.warn(`the build output ${s} was made for ${n.built===void 0?"the default environment":`"${n.built}"`}, not ${n.deployed===void 0?"the default environment":`"${n.deployed}"`}, so the "${r}" Hyperdrive id was not written there. Build with CLOUDFLARE_ENV set to the environment you deploy, then deploy again.`);return}let i;try{i=I(s)}catch{return}const a=Or(i.parsed?.hyperdrive).find(c=>c.binding===r);a!==void 0&&X(s,Ar(i.text,["hyperdrive",a.index,"id"],t),"utf8")},po=(e,r)=>{const t=typeof e.name=="string"&&e.name!==""?e.name:"lunora";if(r===void 0)return t;const n=e.env?.[r]?.name;return typeof n=="string"&&n!==""?n:`${t}-${r}`},fe=(e,r,t)=>{const n=t??D(e),o=n===void 0?void 0:I(n).parsed;if(n===void 0||o===void 0)return;const s=r===void 0?void 0:o.env?.[r],i=Or(r===void 0?o.hyperdrive:s?.hyperdrive);return i.length===0?void 0:{accountId:s?.account_id??o.account_id,entries:i,path:r===void 0?["hyperdrive"]:["env",r,"hyperdrive"],workerName:po(o,r),wranglerPath:n}},vo=(e,r,t)=>{const n=ro(e.map(({binding:o})=>o));if(n!==void 0){const o=`deploy blocked: the Hyperdrive bindings ${n.bindings.map(s=>`"${s}"`).join(" and ")} both read ${n.variable} and would share one config. Rename one so their names differ in letters or digits, not only in punctuation.`;return t.logger.error(o),o}for(const{binding:o,origin:s,supplied:i}of e){const a=fo(o,s,r,i);if(a!==void 0&&t.dryRun===!0)t.logger.warn(`dry run: ${a} A real deploy stops here.`);else if(a!==void 0){const c=`deploy blocked: ${a}`;return t.logger.error(c),c}}},yo=async(e,r,t,n)=>{let o;try{o=await e(`/hyperdrive/configs/${encodeURIComponent(t)}`)}catch(i){n.warn(`could not check whether the "${r}" Hyperdrive config has query caching off (${i instanceof Error?i.message:String(i)}).`);return}if(!o.ok){n.warn(`could not check whether the "${r}" Hyperdrive config has query caching off (${se(o)}).`);return}const{caching:s}=typeof o.body.result=="object"&&o.body.result!==null?o.body.result:{};s?.disabled!==!0&&n.warn(`the "${r}" Hyperdrive config has query caching on, so .global() tables would read rows up to 75 s old. Run \`wrangler hyperdrive update ${t} --caching-disabled\`.`)},wo=async(e,r)=>{const t=e.wranglerPath??D(e.cwd),n=t===void 0?void 0:I(t).parsed,o=e.wranglerEnvironment===void 0?n:n?.env?.[e.wranglerEnvironment],{accountId:s,token:i}=_r(r,o?.account_id??n?.account_id);if(s===void 0||i===void 0)return;const a=async(c,l)=>Ir({accountId:s,apiToken:i,...e.fetch===void 0?{}:{fetch:e.fetch},init:{...l,signal:AbortSignal.timeout(Dr)},path:c});for(const c of Array.isArray(o?.hyperdrive)?o.hyperdrive:[]){const{binding:l,id:d}=typeof c=="object"&&c!==null?c:{};typeof l=="string"&&typeof d=="string"&&d!==Er&&await yo(a,l,d,e.logger)}},ko=async e=>{const r=e.environment??process.env;e.disableCaching===!0&&e.checkOnly!==!0&&e.dryRun!==!0&&await wo(e,r);const t=fe(e.cwd,e.wranglerEnvironment,e.wranglerPath);if(t===void 0)return;const{entries:n,path:o,wranglerPath:s}=t,{accountId:i,token:a}=_r(r,t.accountId),c=n.map(({binding:d})=>{const u=r[Y(d)]??"";return{binding:d,origin:oo(u),supplied:u!==""}}),l=vo(c,a!==void 0&&i!==void 0,e);if(l!==void 0)return l;if(!(e.dryRun===!0||e.checkOnly===!0))for(const[d,{binding:u,index:f}]of n.entries()){const g=c[d]?.origin;if(!(g===void 0||a===void 0||i===void 0))try{const{id:h,text:v}=await go({accountId:i,binding:u,disableCaching:e.disableCaching===!0,fetch:e.fetch,index:f,logger:e.logger,origin:g,path:o,token:a,workerName:t.workerName,wranglerPath:s},E(s,"utf8"));X(s,v,"utf8"),mo(s,u,h,{built:ho(r.CLOUDFLARE_ENV),deployed:e.wranglerEnvironment},e.logger)}catch(h){const v=h instanceof Error?h.message:String(h),m=`deploy blocked: the "${u}" Hyperdrive config could not be set up: ${so(v,c[d]?.origin)}`;return e.logger.error(m),m}}},bo=(e,r)=>{const t=fe(e,r.env)?.entries[0]?.binding;if(t===void 0)return;const n=`prepare blocked: the "${t}" Hyperdrive binding still has its placeholder id. Run \`lunora deploy\` with ${Y(t)} set to create the config, or put the id from \`wrangler hyperdrive create\` in wrangler.jsonc.`;return r.logger.error(n),n},$o=(e,r)=>{const t=fe(e,r.env)?.entries[0]?.binding;if(t===void 0)return;const n=`deploy blocked: the "${t}" Hyperdrive binding has no config, and a --temporary deploy cannot create one: it would be made in your account while the Worker goes to a temporary one, which a temporary account has no database for. Deploy without --temporary.`;return r.logger.error(n),n},H=async(e,r,t,n,o="create",s)=>{if(n!=="cloudflare")return;if(t==="prepare")return bo(e,r);if(r.temporary===!0)return $o(e,r);const i=r.clientBuildEnvironment===void 0?void 0:{...process.env,CLOUDFLARE_ENV:r.clientBuildEnvironment},a=Pe(e,O(e),r.discoveryCache);return a.error!==void 0&&r.logger.warn(`could not read the schema (${a.error}), so Hyperdrive configs are set up with query caching off.`),ko({checkOnly:o==="check",cwd:e,disableCaching:a.error!==void 0||a.info?.hasHyperdriveGlobalTable===!0,dryRun:r.dryRun,environment:i,logger:r.logger,wranglerEnvironment:r.env,wranglerPath:s})},So=(e,r,t)=>fe(e,r,t)!==void 0,pe=[".ts",".tsx",".mts",".js",".mjs"],Eo=/^[a-z]+:/u,er=/\.[cm]?js$/u,Ao=e=>!e.startsWith(".")&&!e.startsWith("/")&&!e.startsWith("#")&&!Eo.test(e),Ro=e=>e.split("/").slice(0,e.startsWith("@")?2:1).join("/"),To=e=>e.split(x).some(r=>r==="_generated"||r==="node_modules"),Co=e=>e.split(x).includes("node_modules"),rr=(e,r)=>{const t=_(R(e),r),n=er.test(t)?t.replace(er,""):void 0;return[t,...n===void 0?[]:pe.map(s=>`${n}${s}`),...pe.map(s=>`${t}${s}`),...pe.map(s=>p(t,`index${s}`))].find(s=>b(s)&&Te(s).isFile())},Po=/^lunora\.config\.[cm]?[jt]s$/u,be=e=>{if(typeof e=="string")return e;if(typeof e=="object"&&e!==null)for(const[r,t]of Object.entries(e)){const n=r==="types"?void 0:be(t);if(n!==void 0)return n}},_o=(e,r)=>{let t;try{t=JSON.parse(E(p(e,"package.json"),"utf8")).imports}catch{return}const n=be(t?.[r]);if(n!==void 0)return n;for(const[o,s]of Object.entries(t??{})){const[i="",a=""]=o.split("*"),c=be(s);if(c!==void 0&&o.includes("*")&&r.startsWith(i)&&r.endsWith(a)&&r.length>=o.length-1){const l=r.slice(i.length,r.length-a.length);return c.replaceAll("*",()=>l)}}},Io=(e,r)=>{const t=p(e,r,"_generated"),n=K(e).find(o=>Po.test(o));return[...b(t)?K(t).filter(o=>o.endsWith(".ts")).map(o=>p(t,o)):[],...n===void 0?[]:[p(e,n)]]},jo=e=>{const r=w.findConfigFile(e,n=>w.sys.fileExists(n));if(r===void 0)return!1;const t=w.readConfigFile(r,n=>w.sys.readFile(n));return w.parseJsonConfigFileContent(t.config??{},w.sys,R(r)).options.verbatimModuleSyntax===!0},tr=e=>e!==void 0&&(w.isNamedImports(e)||w.isNamedExports(e))&&e.elements.length>0&&e.elements.every(r=>r.isTypeOnly),Do=(e,r)=>{if(w.isImportDeclaration(e)){const{importClause:t}=e;return!(t?.phaseModifier===w.SyntaxKind.TypeKeyword||!r&&t?.name===void 0&&tr(t?.namedBindings))&&w.isStringLiteral(e.moduleSpecifier)?e.moduleSpecifier.text:void 0}if(w.isExportDeclaration(e))return!(e.isTypeOnly||!r&&tr(e.exportClause))&&e.moduleSpecifier!==void 0&&w.isStringLiteral(e.moduleSpecifier)?e.moduleSpecifier.text:void 0},Oo=e=>{if(!w.isCallExpression(e)||e.expression.kind!==w.SyntaxKind.ImportKeyword)return;const[r]=e.arguments;return r!==void 0&&w.isStringLiteralLike(r)?r.text:void 0},Lo=(e,r)=>{const t=w.createSourceFile(e,E(e,"utf8"),w.ScriptTarget.Latest,!1),n=[],o=[],s=i=>{const a=Oo(i),c=a??Do(i,r);c?.startsWith(".")||c?.startsWith("#")?o.push(c):a!==void 0&&Ao(a)&&n.push(a),w.forEachChild(i,s)};return s(t),{dynamicPackages:n,relative:o}},No=(e,r)=>{for(let t=e;;t=R(t)){if(b(p(t,"node_modules",r,"package.json")))return!0;if(R(t)===t)return!1}},xo=(e,r,t)=>{const n=jo(e),o=new Set,s=new Set,i=r!==void 0&&b(_(e,r))?[_(e,r)]:Io(e,t);for(let a=i.shift();a!==void 0;a=i.shift())if(!o.has(a)&&!Co(L(e,a))){o.add(a);const{dynamicPackages:c,relative:l}=Lo(a,n);if(!To(L(e,a)))for(const d of c)s.add(d);i.push(...l.flatMap(d=>{if(!d.startsWith("#"))return rr(a,d)??[];const u=_o(e,d);return u===void 0?[]:rr(p(e,"package.json"),u)??[]}))}return[...s].filter(a=>No(e,Ro(a))).toSorted((a,c)=>a.localeCompare(c))},Fo=18787,nr=3,$e=12e4,Lr=5e3,W=10,Nr=new Set([".js",".mjs",".mts",".ts",".tsx"]),Mo=/\(void 0\)|is not a function|before initialization|of undefined/u,Uo=e=>typeof e=="object"&&e!==null&&typeof e.functionPath=="string"&&(e.deferred===void 0||["agent","module","queue","workflow"].includes(e.deferred))&&typeof e.message=="string",Bo=e=>{const r=O(e),t=new Map;let n;try{n=K(p(e,r),{recursive:!0,encoding:"utf8"})}catch{return t}for(const o of n){const s=yt(o);Nr.has(s)&&!o.split(x).some(i=>i==="_generated"||i==="node_modules")&&t.set(Kn(o.slice(0,-s.length).split(x).join("/")),p(r,o))}return t},or=e=>Mo.test(e)?`
|
|
5
|
+
hint: if this module is part of a circular import, an import it reads is still undefined while it evaluates`:"",Wo=(e,r)=>{const t=Bo(e),n=new Map,o=[];for(const{deferred:s,functionPath:i,message:a}of r){if(s!==void 0){o.push(` ${i.slice(s.length+1)} (${s}): ${a}${or(a)}`);continue}const c=i.lastIndexOf(":"),l=i.slice(0,c),d=`${l}\0${a}`,u=n.get(d)??{exports:[],message:a,namespace:l};u.exports.push(i.slice(c+1)),n.set(d,u)}return[...[...n.values()].map(({exports:s,message:i,namespace:a})=>` ${t.get(a)??a} (${s.join(", ")}): ${i}${or(i)}`),...o]},te=e=>{for(const r of Nr)for(const t of[`${e}${r}`,p(e,`index${r}`)])if(b(t))return t},Ho=(e,r)=>{const t=p(e,r,"_generated"),n=a=>{try{return E(p(t,a),"utf8")}catch{return""}},o=n("app.ts"),s=[],i=(a,c)=>{c!==void 0&&s.push({file:c,kind:a,label:L(e,c).split(x).join("/")})};return o.includes('import("./queues.js")')&&i("queue",te(p(t,"queues"))),n("workflows.mjs").includes('import("../workflows.js")')&&i("workflow",te(p(e,r,"workflows"))),(n("agents.mjs").includes('import("../agents.js")')||o.includes('import("../agents.js")'))&&i("agent",te(p(e,r,"agents"))),s},qo=/\.[cm]?js$/u,Go=(e,r)=>{const t=[],n=o=>{if(w.isCallExpression(o)&&o.expression.kind===w.SyntaxKind.ImportKeyword){const[s]=o.arguments;s!==void 0&&w.isStringLiteralLike(s)&&t.push(s.text)}w.forEachChild(o,n)};return n(w.createSourceFile(e,r,w.ScriptTarget.Latest,!1,w.ScriptKind.TS)),t},Vo=(e,r,t)=>{const n=p(e,r,"_generated"),o=new Set(t.map(a=>a.file)),s=[];let i;try{i=K(n).filter(a=>a.endsWith(".mjs")&&a!=="functions.ts")}catch{return s}for(const a of i.toSorted((c,l)=>c.localeCompare(l)))for(const c of Go(p(n,a),E(p(n,a),"utf8"))){const l=c.startsWith("."),d=l?te(_(n,c.replace(qo,""))):c;d!==void 0&&!o.has(d)&&(o.add(d),s.push({file:d,kind:"module",label:l?L(e,d).split(x).join("/"):c}))}return s};class j extends Error{skipped;constructor(r,t=!1){super(r),this.skipped=t}}const sr=/^[$_\p{ID_Start}][$\u200C\u200D\p{ID_Continue}]*$/u,xr=(e,r,t)=>{const n=t??D(e),o=n===void 0?void 0:I(n).parsed;if(o===void 0)return{assetsDirectory:void 0,durableObjectClasses:[],entrypointClasses:[],hasContainers:!1,main:void 0,workflowClasses:[]};const{merged:s}=ue(o,r),i=d=>[...new Set(d.flatMap(u=>u?.script_name===void 0&&typeof u?.class_name=="string"&&sr.test(u.class_name)?[u.class_name]:[]))],a=s.exports??{},c=d=>Object.entries(a).flatMap(([u,f])=>f?.type===d&&(f.state===void 0||f.state==="created")&&sr.test(u)?[u]:[]),l=s.assets?.directory;return{assetsDirectory:l===void 0||n===void 0?void 0:_(R(n),l),durableObjectClasses:[...new Set([...i(s.durable_objects?.bindings??[]),...c("durable-object")])],entrypointClasses:c("worker"),hasContainers:(s.containers??[]).length>0,main:s.main,workflowClasses:[...new Set([...i(s.workflows??[]),...c("workflow")])]}},Ko=({composedEntry:e,configPath:r,cwd:t,environment:n,toolchain:o})=>{if(o.devServer!=="workerd")return{skip:"this deploy target does not run on workerd"};const{assetsDirectory:s,durableObjectClasses:i,entrypointClasses:a,hasContainers:c,main:l,workflowClasses:d}=xr(t,n,r),u=e??l,f=r??D(t);let g;if(f!==void 0&&Oe(f)!==void 0)g={stubs:{durableObjects:i,entrypoints:a,workflows:d}};else if(typeof u=="string"&&!u.startsWith("virtual:")&&b(_(t,u)))g={main:u};else return{skip:"the Worker entry is not a file on disk"};const h=s!==void 0&&!b(s);if(c)return{skip:"the project declares containers, which `wrangler dev` would build locally before the real container build"};const v=Kt(t,process.env,n,r);return v.length>0?{skip:`the Hyperdrive binding ${v.join(", ")} has no local database for wrangler dev`}:{base:g,configFile:f,missingAssets:h}},zo=/The Workers runtime failed to start|Build failed with \d+ error/u,Jo=/\[ERROR\]|Uncaught/u,ir=e=>{const r=e.map(o=>bn(o).trim()).filter(o=>o!==""),t=r.filter(o=>Jo.test(o)),n=t.length>0?t.slice(0,10):r.slice(-10);return n.length===0?"":`
|
|
6
6
|
${n.map(o=>` ${o}`).join(`
|
|
7
|
-
`)}`},Le=async e=>{const{deadline:r,output:t,pending:n,port:o,query:s,startupFailed:i}=e,a=await fetch(`http://127.0.0.1:${String(o)}/_lunora/__smoke_load?${s}`,{signal:AbortSignal.any([AbortSignal.timeout(Math.max(0,r-Date.now())),i])}).catch(d=>{throw i.aborted?new j(`the smoke Worker's runtime did not start, so nothing was evaluated:${
|
|
8
|
-
Fix the entry, or pass --no-smoke-load to ship anyway.`):d instanceof Error&&d.name==="TimeoutError"?new j(n===void 0?`the smoke Worker accepted the connection but answered no request within ${String(
|
|
9
|
-
Pass --no-smoke-load to ship anyway.`:`the smoke Worker did not finish evaluating ${n} within ${String(
|
|
10
|
-
`)},
|
|
11
|
-
`))},
|
|
7
|
+
`)}`},Le=async e=>{const{deadline:r,output:t,pending:n,port:o,query:s,startupFailed:i}=e,a=await fetch(`http://127.0.0.1:${String(o)}/_lunora/__smoke_load?${s}`,{signal:AbortSignal.any([AbortSignal.timeout(Math.max(0,r-Date.now())),i])}).catch(d=>{throw i.aborted?new j(`the smoke Worker's runtime did not start, so nothing was evaluated:${ir(t)}
|
|
8
|
+
Fix the entry, or pass --no-smoke-load to ship anyway.`):d instanceof Error&&d.name==="TimeoutError"?new j(n===void 0?`the smoke Worker accepted the connection but answered no request within ${String($e/1e3)}s, so its runtime did not start:${ir(t)}
|
|
9
|
+
Pass --no-smoke-load to ship anyway.`:`the smoke Worker did not finish evaluating ${n} within ${String($e/1e3)}s — it may be awaiting something that never settles. Pass --no-smoke-load to ship anyway.`):d});if(a.status>=500)throw new j(`the smoke Worker booted but failed while ${n===void 0?"answering a request that evaluates nothing":`evaluating ${n}`} (HTTP ${String(a.status)}). Pass --no-smoke-load to ship anyway.`);const c=await a.text().catch(()=>"");let l;try{l=JSON.parse(c)}catch{l=void 0}return{body:typeof l=="object"&&l!==null?l:void 0,response:a,text:c}},Ne=(e,r)=>{const t=e.headers.get("content-type")??"no content type",n=r.replaceAll(/\s+/gu," ").trim().slice(0,120);return new j(`the smoke Worker answered with something other than the probe's result (HTTP ${String(e.status)}, ${t}${n===""?"":`: ${n}`}). A route that does not send /_lunora/* to the Worker first answers with the app's assets, so check run_worker_first. Pass --no-smoke-load to ship anyway.`)},Fr=(e,r)=>typeof e=="string"&&e!==r,Se=Symbol("another run's Worker"),ar=async(e,r)=>{const t=await Le(e),n=t.body?.message;if(Fr(t.body?.run,r))return Se;if(t.body?.run!==r||n!==void 0&&typeof n!="string")throw Ne(t.response,t.text);return n},Yo=async(e,r)=>{const t=await Le({...e,pending:"the lazy function modules",query:"step=functions"}),{checked:n,failures:o}=t.body??{};if(!Fr(t.body?.run,r)){if(t.body?.run!==r||typeof n!="number"||!Array.isArray(o)||!o.every(s=>Uo(s)))throw Ne(t.response,t.text);return{checked:n,failures:o}}},Xo=async e=>{const{deferred:r,functions:t,integrationModules:n,runId:o,...s}=e,i=await Le({...s,pending:void 0,query:"step=ping"});if(typeof i.body?.run!="string")throw Ne(i.response,i.text);if(i.body.run!==o)return;let a=0,c=[];if(t){const d=await Yo(s,o);if(d===void 0)return;a=d.checked,c=d.failures}for(const[d,u]of r.entries()){const f=await ar({...s,pending:`${u.kind} module ${u.label}`,query:`step=deferred&index=${String(d)}`},o);if(f===Se)return;f!==void 0&&c.push({deferred:u.kind,functionPath:`${u.kind}:${u.label}`,message:f})}const l=[];for(const d of n){const u=await ar({...s,pending:`import(${JSON.stringify(d)})`,query:new URLSearchParams({specifier:d,step:"integration"}).toString()},o);if(u===Se)return;u!==void 0&&l.push({message:u,specifier:d})}return{checked:a,deferredChecked:r.length,failures:c,integrations:{checked:n.length,failures:l}}},Ee=(e,r)=>{try{process.kill(-e,r)}catch{}},cr=e=>{try{return process.kill(-e,0),!0}catch{return!1}},Mr=e=>({args:["/pid",String(e),"/T","/F"],command:p(process.env.SystemRoot??String.raw`C:\Windows`,"System32","taskkill.exe")}),Ur=e=>e.error===void 0&&e.status===0,Br=(e,r)=>{const t=String(e),o=r.error===void 0&&r.status===128?`the root process ${t} has already exited, so a process it started may still hold the smoke server's port; find that port's owner with netstat -ano and end its PID with taskkill /PID <pid> /T /F`:`end it with taskkill /PID ${t} /T /F`,s=r.error===void 0?`taskkill exited with status ${String(r.status)}`:r.error.stack??r.error.message;process.stderr.write(`lunora: the smoke process tree ${t} may still be running; ${o} (${s.replaceAll(/\r?\n/gu,String.raw`\n`)})
|
|
10
|
+
`)},Qo=e=>{const{args:r,command:t}=Mr(e),n=yn(t,r,{stdio:"ignore",timeout:Lr});Ur(n)||Br(e,n)},Zo=e=>{const r=()=>{if(process.platform==="win32"){Qo(e);return}Ee(e,"SIGKILL")},t=o=>{r(),process.kill(process.pid,o)},n=()=>{process.removeListener("exit",r),process.removeListener("SIGINT",t),process.removeListener("SIGTERM",t)};return process.once("exit",r),process.once("SIGINT",t),process.once("SIGTERM",t),n},es=e=>new Promise(r=>{const{args:t,command:n}=Mr(e),o=Tr(n,t,{stdio:"ignore",windowsHide:!0});o.once("error",s=>{r({error:s})}),o.once("exit",s=>{const i={status:s};r(Ur(i)?void 0:i)})}),ie=async e=>{if(e===void 0)return;e.dispose();const{pid:r}=e;if(process.platform==="win32"){const n=await es(r);n!==void 0&&Br(r,n);return}Ee(r,"SIGTERM");const t=Date.now()+Lr;for(;cr(r)&&Date.now()<t;)await kn(50);cr(r)&&Ee(r,"SIGKILL")},rs=(e,r,t)=>[e?"lazy function modules":void 0,r===0?void 0:`${String(r)} deferred module(s)`,t===0?void 0:`${String(t)} integration module(s)`].filter(n=>n!==void 0).join(", "),ts=async(e,r)=>{if(process.platform==="win32"){try{await ie(r)}finally{await e.stop()}return}try{await e.stop()}finally{await ie(r)}},ns=e=>St(e).catch(async r=>{if(!await Et(e.port))throw r}),os=({checked:e,deferredChecked:r,integrations:t})=>[`${String(e)} lazy function(s)`,...r>0?[`${String(r)} deferred module(s)`]:[],...t.checked>0?[`${String(t.checked)} integration module(s) loaded with import()`]:[]].join(" and "),ss=(e,r,t)=>{const{checked:n,deferredChecked:o,failures:s,integrations:i}=r;if(s.length===0&&i.failures.length===0){t.success(`${os(r)} evaluate cleanly`);return}const a=Wo(e,s),c=a.length>W?[` …and ${String(a.length-W)} more module(s)`]:[];throw new j([...s.length===0?[]:[`${String(s.length)} of ${o===0?`${String(n)} lazy function(s)`:`${String(n+o)} lazy module(s)`} fail to load:`,...a.slice(0,W),...c],...i.failures.length===0?[]:[`${String(i.failures.length)} of ${String(i.checked)} integration module(s) fail to load in workerd (each would fail its first use instead):`,...i.failures.slice(0,W).map(({message:l,specifier:d})=>` import("${d}"): ${l}`),...i.failures.length>W?[` …and ${String(i.failures.length-W)} more integration module(s)`]:[]],"Fix the module, or pass --no-smoke-load to ship anyway."].join(`
|
|
11
|
+
`))},is=(e,r,t)=>{if(e instanceof j&&!e.skipped)return t.error(e.message),e.message;if(e instanceof j){t.warn(`smoke-load skipped: ${e.message}`);return}const n=r.slice(-5).join(`
|
|
12
12
|
`),o=`the smoke Worker did not start (${e instanceof Error?e.message:String(e)})${n===""?"":`
|
|
13
|
-
${n}`}. Either the entry fails when it loads, or local wrangler cannot run the project (a remote-only binding). Fix whichever it is, or pass --no-smoke-load to ship anyway.`;return t.error(o),o},
|
|
14
|
-
`)},
|
|
15
|
-
`),v="stubs"in r&&r.stubs.entrypoints.includes("default");X(f,`${
|
|
13
|
+
${n}`}. Either the entry fails when it loads, or local wrangler cannot run the project (a remote-only binding). Fix whichever it is, or pass --no-smoke-load to ship anyway.`;return t.error(o),o},as=e=>{try{return Re(p(e,".lunora"),{recursive:!0}),pt(p(e,".lunora","smoke-"))}catch(r){throw new j(`could not create a directory for the check (${r instanceof Error?r.message:String(r)})`,!0)}},cs=(e,r)=>{if("main"in r)return`export * from ${V(_(e,r.main))};`;const{durableObjects:t,entrypoints:n,workflows:o}=r.stubs,s=new Set,i=(a,c)=>a==="default"||s.has(a)?[]:(s.add(a),[`export class ${a} ${c}`]);return['import { DurableObject, WorkerEntrypoint, WorkflowEntrypoint } from "cloudflare:workers";',...t.flatMap(a=>i(a,"extends DurableObject {}")),...n.flatMap(a=>i(a,"extends WorkerEntrypoint {}")),...o.flatMap(a=>i(a,"extends WorkflowEntrypoint { async run() {} }"))].join(`
|
|
14
|
+
`)},ds=async e=>{const{base:r,configPath:t,cwd:n,deferred:o,directory:s,environment:i,functionsFile:a,integrationModules:c,missingAssets:l,preferred:d,toolchain:u}=e;try{const f=p(s,"entry.ts"),g=Cr();l&&Re(p(s,"assets"),{recursive:!0});const h=c.map(T=>` [${V(T)}, () => import(${V(T)})],`).join(`
|
|
15
|
+
`),v="stubs"in r&&r.stubs.entrypoints.includes("default");X(f,`${cs(n,r)}
|
|
16
16
|
${a===void 0?"":`import { loadAllFunctions } from ${V(a)};
|
|
17
17
|
`}
|
|
18
18
|
const runId = ${JSON.stringify(g)};
|
|
@@ -60,5 +60,5 @@ ${v?`export default class extends WorkerEntrypoint {
|
|
|
60
60
|
return smokeProbe.fetch(request);
|
|
61
61
|
}
|
|
62
62
|
}`:"export default smokeProbe;"}
|
|
63
|
-
`);const m=await bn(d),{args:$,command:C}=z(F(n),u.dev({configPath:t===void 0?void 0:O(n,t),environment:i,extraArgs:[O(n,f),"--local","--local-protocol","http","--ip","127.0.0.1","--port",String(m),"--persist-to",O(n,p(s,"state")),...l?["--assets",O(n,p(s,"assets"))]:[]]}));return{args:$,command:C,port:m,runId:g}}catch(f){throw new j(`could not prepare the check (${f instanceof Error?f.message:String(f)})`,!0)}},ds=async e=>{const{composedEntry:r,cwd:t,environment:n,launcher:o,logger:s,toolchain:i}=e,a=x(t),c=p(t,a,"_generated","functions.ts"),l=b(c)&&E(c,"utf8").includes("export const loadAllFunctions"),d=l?c:void 0,u=Wo(t,a),f=[...u,...Go(t,a,u)],g=No(t,r??xr(t,n,e.configPath).main,a);if(!l&&f.length===0&&g.length===0)return;const h=Vo(e);if("skip"in h){s.warn(`smoke-load skipped: ${h.skip}, so the modules the Worker loads on demand are not evaluated before deploy`);return}const{base:v,configFile:m,missingAssets:$}=h,C=[];let T,M;const qe=Date.now()+be,gt=process.platform!=="win32",ht=(U,Q,Z)=>{const B=Tr(U,[...Q],{cwd:Z.cwd,detached:gt,shell:Z.shell,stdio:["ignore","pipe","pipe"]});return B.pid!==void 0&&(M={dispose:Qo(B.pid),pid:B.pid}),B};try{T=is(t),s.info(`smoke-loading ${es(d!==void 0,f.length,g.length)} in workerd`);let U=xo;for(let Q=0;Q<or;Q+=1){const{args:Z,command:B,port:he,runId:mt}=await cs({base:v,configPath:m,cwd:t,deferred:f,directory:T,environment:n,functionsFile:d,integrationModules:g,missingAssets:$,preferred:U,toolchain:i}),Ge=new AbortController;U=he+1;const Ve=await ts({args:Z,command:B,cwd:t,spawn:o??ht,label:"wrangler dev (smoke-load)",notFound:"`wrangler` was not found",onLine:G=>{C.push(G),Ko.test(G)&&Ge.abort()},port:he,readyTimeoutMs:Math.max(0,qe-Date.now()),shell:process.platform==="win32"});if(Ve===void 0){await ie(M),M=void 0;continue}try{const G=await Yo({deadline:qe,deferred:f,functions:d!==void 0,integrationModules:g,output:C,port:he,runId:mt,startupFailed:Ge.signal});if(G!==void 0){os(t,G,s);return}}finally{await rs(Ve,M),M=void 0}}throw new j(`the smoke port was answered by other runs on all ${String(or)} attempts`,!0)}catch(U){return ss(U,C,s)}finally{if(await ie(M),T!==void 0)try{oe(T,{force:!0,recursive:!0})}catch{}}},ls=(e,r)=>r===void 0?e:_(e,r),us=async e=>{if(e.targets.length===0)return{builtTags:[],code:0};if(!(e.railpackAvailable??Sn)()){const n="deploy blocked: a container uses `image: { build }` (Railpack), but Railpack isn't ready. Install the `railpack` CLI and start a BuildKit instance, e.g. `docker run --rm --privileged -d --name buildkit moby/buildkit` then `export BUILDKIT_HOST=docker-container://buildkit`. Alternatively switch the container's `image` to a Dockerfile path or a pre-built registry reference.";return e.logger.error(n),{builtTags:[],code:1,error:n}}const r=e.spawner??q,t=[];for(const n of e.targets){const o=$n(n.exportName),s={args:["build",n.buildDir,"--name",o],command:"railpack",cwd:e.cwd},i=je(F(e.cwd),"wrangler",["containers","push",o]),a={args:i.args,command:i.command,cwd:e.cwd};e.logger.info(`railpack: building "${n.exportName}" → ${o} from ${n.buildDir}`);const c=await r(s);if(c.code!==0){const d=`railpack build failed for container "${n.exportName}" (${n.buildDir})`;return e.logger.error(d),{builtTags:t,code:c.code,error:d}}e.logger.info(`railpack: pushing ${o} to the Cloudflare Registry`);const l=await r(a);if(l.code!==0){const d=`wrangler containers push failed for "${n.exportName}" (${o})`;return e.logger.error(d),{builtTags:t,code:l.code,error:d}}t.push(o)}return{builtTags:t,code:0}},fs=new Set(["0.0.0.0","[::1]","[::]","localhost"]),gs=[".local",".localhost"],hs=["[fc","[fd"],ms=/^\[fe[89ab][0-9a-f]:/u,ps=/^[a-z]+:\/\//u,lr=e=>{const r=e.split(".").map(Number);if(r.length!==4||r.some(o=>!Number.isInteger(o)))return!1;const[t=-1,n=-1]=r;return t===0||t===127||t===100&&n>=64&&n<=127||t===198&&(n===18||n===19)||t===10||t===192&&n===168||t===172&&n>=16&&n<=31||t===169&&n===254},ur="[::ffff:",vs=e=>{if(!e.startsWith(ur)||!e.endsWith("]"))return;const r=e.slice(ur.length,-1);if(r.includes("."))return r;const[t=0,n=0]=r.split(":").map(o=>Number.parseInt(o,16));return[Math.floor(t/256),t%256,Math.floor(n/256),n%256].join(".")},Wr=e=>{let r=e.length;for(;r>0&&e[r-1]===".";)r-=1;return e.slice(0,r).toLowerCase()},ys=e=>{const r=e.toLowerCase(),t=vs(r);return t!==void 0?lr(t):fs.has(r)||gs.some(n=>r.endsWith(n))||hs.some(n=>r.startsWith(n))||ms.test(r)||lr(r)},Hr=e=>{const r=D(e);return r===void 0?void 0:I(r).parsed},qr=(e,r)=>{const t=typeof e?.name=="string"&&e.name!==""?e.name:void 0;if(r===void 0)return t;const n=e?.env?.[r]?.name;return typeof n=="string"&&n!==""?n:t===void 0?void 0:`${t}-${r}`},ae=(e,r)=>{let t=e.length;for(;t>0&&e[t-1]===r;)t-=1;return e.slice(0,t)},ws=e=>{const r=e.replace(ps,""),t=r.indexOf("/"),n=(t===-1?r:r.slice(0,t)).toLowerCase(),o=t===-1?"":ae(r.slice(t),"*");return o==="/"?n:`${n}${o}`},fr=e=>[e?.route,...Array.isArray(e?.routes)?e.routes:[]].flatMap(t=>{const n=typeof t=="string"?t:t?.pattern;return typeof n=="string"&&n!==""?[ws(n)]:[]}),ks=(e,r,t)=>{const n=t.indexOf("/"),o=n===-1?t:t.slice(0,n),s=n===-1?"":t.slice(n),i=o.startsWith("*");if(!(!(i?e.endsWith(o.slice(1)):e===o)||s!==""&&!`${ae(r,"/")}/`.startsWith(s)))return s.length*1e6+(i?0:5e5)+o.length},bs=e=>e!==void 0&&(Object.hasOwn(e,"route")||Object.hasOwn(e,"routes")),Gr=(e,r)=>{const t=fr(e),o=[...new Set([...Object.keys(e?.env??{}),...r===void 0?[]:[r]])].map(s=>{const i=e?.env?.[s];return{id:`env:${s}`,label:`--env ${s}`,routes:bs(i)?fr(i):t}});return[{id:"top",label:"production",routes:t},...o]},$s=(e,r,t,n)=>{const o=r===void 0?"top":`env:${r}`,s=Gr(e,r).flatMap(c=>c.routes.flatMap(l=>{const d=ks(t,n,l);return d===void 0?[]:[{block:c,score:d}]}));if(s.length===0)return{kind:"none"};const i=Math.max(...s.map(c=>c.score)),a=s.filter(c=>c.score===i);return a.some(c=>c.block.id===o)?{kind:"own"}:{kind:"other",label:a[0]?.block.label??"production"}},ne=e=>tn(`${e.origin}${e.pathname}`),Ss=(e,r,t,n,o)=>{const s=Ce(e),i=s?.workerUrl!==void 0&&URL.canParse(s.workerUrl)?new URL(s.workerUrl):void 0;if(i===void 0)return;if(s?.env===r)return ne(i)===ne(t)?null:`${n}: this checkout is linked to ${ne(i)}${o}`;const a=`${ae(i.pathname,"/")}/`;if(i.origin!==t.origin||!`${ae(t.pathname,"/")}/`.startsWith(a))return;const c=s?.env===void 0?"production":`--env ${s.env}`;return`${n}: this checkout links it to ${c}, not this deploy${o}`},Vr=(e,r,t,n,o,s)=>{const i=$s(e,r,t,n);if(i.kind==="other")return`${o}: wrangler routes it to ${i.label}, not this deploy${s}`;if(i.kind==="own")return;const a=Gr(e,r);if(a.every(l=>l.routes.length===0))return;const c=a.find(l=>l.id===(r===void 0?"top":`env:${r}`))?.routes??[];return`${o}: this deploy${s} routes to ${c.length===0?"no declared route":c.join(", ")}, not ${t}`},Es=(e,r,t,n,o,s)=>{const i=Hr(e);if(t.endsWith(".workers.dev")){const a=qr(i,r);return a!==void 0&&t.split(".")[0]!==a?`${o}: this deploy ships the Worker "${a}"${s}`:`${o}: no link ties this *.workers.dev host to your account${s}; run \`lunora link --url https://${t}${r===void 0?"":` --env ${r}`}\` once the Worker is deployed, then deploy again (or confirm the drift yourself with --allow-schema-drift)`}return Vr(i,r,t,n,o,s)},As=(e,r,t,n,o)=>{const s=Hr(e),i=Wr(t.hostname);if(i.endsWith(".workers.dev")){const a=qr(s,r);return a===void 0||i.split(".")[0]===a?void 0:`${n}: this deploy ships the Worker "${a}", not the one the link names${o}`}return Vr(s,r,i,t.pathname,n,o)},Rs=(e,r,t)=>{let n;try{n=new URL(t)}catch{return`refused ${t} as the deployment to read rows from: it is not a URL`}const o=`refused ${ne(n)} as the deployment to read rows from`,s=r===void 0?"":` (--env ${r})`,i=Wr(n.hostname);if(ys(i))return`${o}: it is a local or private address, not the worker this deploy ships to`;const a=Ss(e,r,n,o,s);return a===void 0?Es(e,r,i,n.pathname,o,s):a??As(e,r,n,o,s)},Kr=e=>{const t=[D(e),p(e,"package.json"),p(e,"wrangler.dev.jsonc")].filter(n=>n!==void 0&&b(n)).map(n=>[n,E(n,"utf8")]);return()=>{for(const[n,o]of t)E(n,"utf8")!==o&&X(n,o,"utf8")}},Ts=(e,r=Cr())=>_(e,".lunora",`vite-bundle-meta-${r}.json`),zr="LUNORA_CLIENT_BUILD",Jr="virtual:lunora/worker",Ee=(e,r)=>["ts","mts","cts","js","mjs","cjs"].some(t=>b(p(e,`${r}.config.${t}`))),Yr=e=>Ee(e,"rsbuild")?"rsbuild":Ee(e,"vite")?"vite":void 0,Cs=e=>Ee(e,"vite")?"vite":Yr(e),Ps=e=>{try{const{scripts:r}=JSON.parse(E(p(e,"package.json"),"utf8"));return typeof r?.build=="string"&&r.build!==""}catch{return!1}},ge=(e,r)=>{if(process.env[zr]==="1")return;const t=D(e),n=t===void 0?void 0:I(t).parsed,o=n?.main===Jr,s=(o?Cs(e):Yr(e))??(Ps(e)?"script":void 0);if(s===void 0)return;const i=r===void 0?void 0:n?.env?.[r]?.assets?.directory,a=n?.assets?.directory??i;return o||Et(e).class!=="C"||a!==void 0?s:void 0},_s=async(e,r,t)=>{const n=t.spawner??q,o=F(e),s=r==="script"?{args:["run","build"],command:o}:je(o,r,["build"]);t.logger.info(`running ${r} build before bundling the Worker`);const i=Kr(e),a=t.bundleMetaPath??Ts(e);oe(p(e,At),{force:!0});try{const c=await n({args:s.args,command:s.command,cwd:e,env:{[_t]:It(e),[Pt]:t.target,[Ct]:a,...t.lazyFunctions?{[Tt]:"1"}:{},...t.environment===void 0?{}:{CLOUDFLARE_ENV:t.environment},...t.strictAdvisories===!1?{[Rt]:"0"}:{},[zr]:"1"},stdoutToStderr:t.outputJson});return c.code===0?void 0:`${r} build failed with exit code ${String(c.code)}`}finally{i(),t.bundleMetaPath===void 0&&oe(a,{force:!0})}},xe=(e,r,t)=>r!==!1&&ge(e,t)!==void 0,Is=(e,r,t)=>{const n=r===!1?void 0:ge(e,t);if(n===void 0||n==="script")return!1;const o=D(e);return o!==void 0&&I(o).parsed?.main===Jr||wr(e)!==void 0},Fe=e=>{const r=e.frameworkBuild===!1?void 0:ge(e.cwd,e.environment);if(r===void 0||r==="script")return;if(e.apiSpec!==void 0)return`--api-spec is read from the ${r} plugin config when the client build runs; set it there, or pass --no-framework-build`;if(e.target!==void 0)try{we(e.cwd,e.target)}catch(n){return n instanceof Error?n.message:String(n)}let t;try{t=we(e.cwd)}catch(n){return`the project's configured target cannot be used, and the ${r} plugin reads it: ${n instanceof Error?n.message:String(n)}`}if(e.target!==void 0&&e.target!==t)return`this run targets "${e.target}", but the project's target is "${t}", which the ${r} plugin reads; set it in the project config, or pass --no-framework-build`},Xr=async e=>{const r=e.frameworkBuild===!1?void 0:ge(e.cwd,e.environment);if(r===void 0)return;let t;try{t=e.target??we(e.cwd)}catch(n){return n instanceof Error?n.message:String(n)}return _s(e.cwd,r,{bundleMetaPath:e.bundleMetaPath,environment:e.environment,strictAdvisories:e.strictAdvisories,lazyFunctions:e.lazyFunctions,logger:e.logger,outputJson:e.outputJson,spawner:e.spawner,target:t})},gr=e=>Array.isArray(e)?e.filter(r=>r!=null):void 0,Me=(e,r,t=D(e))=>{if(!t)return;const{parsed:n}=I(t);if(n===void 0)return;const{error:o,merged:s}=ue(n,r),i=o===void 0?s:n,{containers:a,d1_databases:c,vars:l}=i;return{containers:gr(a),d1_databases:gr(c),vars:typeof l=="object"&&l!==null&&!Array.isArray(l)?l:void 0}},js=/^127(?:\.\d{1,3}){3}$/,Ds=e=>{try{const{hostname:r}=new URL(e);return r==="localhost"||js.test(r)||r==="::1"||r==="[::1]"}catch{return!1}},Os=e=>e.startsWith("./")||e.startsWith("../")||e.startsWith("/")||e.includes("Dockerfile"),Ls=(e,r,t,n="deploy",o)=>{const s=(Me(e,o)?.containers??[]).filter(a=>typeof a?.image=="string"&&Os(a.image));if(s.length===0||t())return;const i=`${n} blocked: wrangler.jsonc declares ${String(s.length)} container(s) built from a local Dockerfile, but no Docker-compatible engine is available. Start Docker (or Colima), or point the container's \`image\` at a pre-built registry reference. Note: container images must target linux/amd64.`;return r.error(i),i},Ue=e=>b(A(e,Ke))?Ke:void 0,Ns=(e,r,t="deploy",n)=>{for(const o of kr(e,"lunora",n).containers){const s=o.schedulingPolicy===void 0?o.image:void 0,a=[...s?.kind==="dockerfile"?[{field:"image",path:s.dockerfilePath}]:[],...Object.entries(o.schedulingPolicy==="durable_object"?o.images??{}:{}).flatMap(([c,l])=>l.kind==="dockerfile"?[{field:`images["${c}"]`,path:l.dockerfilePath}]:[])].find(c=>!b(A(e,c.path)));if(a!==void 0){const c=`${t} blocked: container "${o.exportName}" references a Dockerfile at "${a.path}" that does not exist. Create it or fix the \`${a.field}\` path in lunora/containers.ts.`;return r.error(c),c}if(s?.kind==="build"&&!b(A(e,s.buildDir))){const c=`${t} blocked: container "${o.exportName}" references a Railpack build directory "${s.buildDir}" that does not exist. Create it or fix the \`image.build\` path in lunora/containers.ts.`;return r.error(c),c}}},xs=(e,r)=>r==="celld"?typeof e=="string"&&e!==ze&&dn(e):r==="cloudflare"&&e===ze,Fs=(e,r,t,n)=>(Me(e,t,n)?.d1_databases??[]).find(o=>xs(o.database_id,r)),Ms=e=>{if(!e.migrate||e.dryRun)return;if(e.migrateUrl===void 0){const t="--migrate requires --migrate-url <https://your-worker> — the deploy target URL is only known after wrangler runs, and this gate runs before it; refusing to default to localhost";return e.logger.error(t),t}if(e.migrateYes!==!0){const t="--migrate runs production data migrations after deploy. Re-run with --migrate-yes to confirm.";return e.logger.error(t),t}const r=e.migrateToken??process.env.LUNORA_ADMIN_TOKEN;if(r===void 0||r===""){const t="admin token required for --migrate — pass --migrate-token or set LUNORA_ADMIN_TOKEN";return e.logger.error(t),t}},Be=(e,r,t,n,o,s)=>{const i=Fs(e,n,o,s);if(i===void 0)return;const a=`${t} blocked: the "${i.binding??"<unnamed>"}" D1 binding has a placeholder database_id ("${i.database_id??""}")`,c=s===void 0?"Run `lunora doctor --fix` to create the database and write its id into wrangler.jsonc, or run `wrangler d1 create <name>` and paste the id in yourself.":`Run \`wrangler d1 create <name>\` and put the returned database_id in ${s}.`,l=n==="celld"?`${a}, which celld would use as the database's identity. Set database_id to the database celld should use, then ${t} again.`:`${a}. ${c}`;return r.error(l),l},Us=(e,r,t="deploy",n)=>{const o=Me(e,n)?.vars;if(!o)return;const s=Object.entries(o).filter(([,a])=>typeof a=="string"&&Ds(a)).map(([a])=>a).toSorted((a,c)=>a.localeCompare(c));if(s.length===0)return;const i=`${t} blocked: ${s.join(", ")} in wrangler.jsonc point at localhost. A deployed Worker can't reach a loopback address, so this silently breaks scheduled-job dispatch, reverse cross-shard relations, and auth callbacks. Set each to the deployed worker's public URL (or move it to a secret with \`wrangler secret put\`) before deploying.`;return r.error(i),i},Bs=["vite.config.ts","vite.config.mts","vite.config.cts","vite.config.js","vite.config.mjs","vite.config.cjs",...wt],Qr=/\.(?:[cm]?[jt]s|tsx|jsx)$/u,Zr=/\.(?:test|spec)\.[cm]?[jt]sx?$/u,et="allowUnauthenticatedShardAccess",Ws=/[\\/]/u,rt=new Set(["_generated","build","dist","node_modules"]),tt=(e,r)=>{const t=J(e,r).split(Ws);return Qr.test(r)&&!Zr.test(r)&&!ye(t.join("/"))&&t.every(n=>n===".."||!n.startsWith(".")&&!rt.has(n))},hr=(e,r,t)=>{const{parsed:n}=I(r);if(n===void 0)return[];const{error:o,merged:s}=ue(n,t),{main:i}=o===void 0?s:n,a=typeof i=="string"?A(jr(r),i):void 0;return a!==void 0&&b(a)&&tt(e,a)?[a]:[]},We=e=>e===void 0?!1:e.getKind()===k.FalseKeyword?!0:S.isParenthesizedExpression(e)||S.isAsExpression(e)||S.isSatisfiesExpression(e)||S.isTypeAssertion(e)||S.isNonNullExpression(e)?We(e.getExpression()):!1,Hs=e=>{try{return Re(e).isFile()}catch{return!1}},qs=new Set(["ELOOP","ENOENT","ENOTDIR"]),ce=(e,r,t)=>{const n=r instanceof Error&&"code"in r&&typeof r.code=="string"?r.code:"";qs.has(n)||t.push(e)},Gs=(e,r)=>{try{return Re(e)}catch(t){ce(e,t,r);return}},nt=(e,r)=>{try{return E(e,"utf8")}catch(t){ce(e,t,r);return}},ot=(e,r,t,n=new Set)=>{let o;try{o=vt(r)}catch(i){return ce(r,i,t),[]}if(n.has(o))return[];n.add(o);let s;try{s=K(r)}catch(i){return ce(r,i,t),[]}return s.flatMap(i=>{const a=A(r,i),c=Gs(a,t);if(c===void 0)return[];const l=J(e,a).split(N).join("/");return c.isDirectory()?rt.has(i)||i.startsWith(".")||ye(`${l}/`)?[]:ot(e,a,t,n):c.isFile()&&Qr.test(i)&&!Zr.test(i)&&!ye(l)?[a]:[]})},Vs=e=>{const r=S.isComputedPropertyName(e)?e.getExpression():e;return S.isStringLiteral(r)||S.isNoSubstitutionTemplateLiteral(r)?r.getLiteralValue():e.getText()},pe=[".ts",".tsx",".mts",".cts",".js",".jsx",".mjs",".cjs"],Ks=/\.(?:[cm]?js|jsx)$/u,zs=(e,r)=>{if(!r.startsWith("."))return;const t=_(jr(e),r),n=t.replace(Ks,"");return[t,...pe.map(s=>`${t}${s}`),...pe.map(s=>A(t,`index${s}`)),...pe.map(s=>`${n}${s}`)].find(s=>Hs(s))},Js=e=>{if(e.getExpression().getText()!=="require")return;const[r]=e.getArguments();return r!==void 0&&(S.isStringLiteral(r)||S.isNoSubstitutionTemplateLiteral(r))?r.getLiteralValue():void 0},Ys=e=>{const r=e.getModuleReference();if(!S.isExternalModuleReference(r))return;const t=r.getExpression();return S.isStringLiteral(t)?t.getLiteralValue():void 0},Xs=(e,r,t,n)=>{const o=new Set(e),s=[...e],i=[];for(let a=0;a<s.length;a+=1){const c=s[a]??"",l=nt(c,n);if(l===void 0)continue;const d=r.createSourceFile(J(t,c),l,{overwrite:!0}),u=[...d.getImportDeclarations().map(f=>f.getModuleSpecifierValue()),...d.getExportDeclarations().map(f=>f.getModuleSpecifierValue()),...d.getDescendantsOfKind(k.ImportEqualsDeclaration).map(f=>Ys(f)),...d.getDescendantsOfKind(k.CallExpression).map(f=>Js(f))].filter(f=>f!==void 0);for(const f of u){const g=zs(c,f);g!==void 0&&tt(t,g)&&!o.has(g)&&(o.add(g),i.push(g),s.push(g))}}return i},Qs=e=>{const r=e.getArgumentExpression();return r!==void 0&&(S.isStringLiteral(r)||S.isNoSubstitutionTemplateLiteral(r))?r.getLiteralValue():void 0},Zs=e=>S.isPropertyAccessExpression(e)?e.getName():S.isElementAccessExpression(e)?Qs(e):void 0,ei=new Set([k.AmpersandAmpersandEqualsToken,k.AmpersandEqualsToken,k.AsteriskAsteriskEqualsToken,k.AsteriskEqualsToken,k.BarBarEqualsToken,k.BarEqualsToken,k.CaretEqualsToken,k.EqualsToken,k.GreaterThanGreaterThanEqualsToken,k.GreaterThanGreaterThanGreaterThanEqualsToken,k.LessThanLessThanEqualsToken,k.MinusEqualsToken,k.PercentEqualsToken,k.PlusEqualsToken,k.QuestionQuestionEqualsToken,k.SlashEqualsToken]),ri=e=>{const r=e.getOperatorToken().getKind();return!ei.has(r)||Zs(e.getLeft())!==et?!1:r!==k.EqualsToken||!We(e.getRight())},ti=(e,r,t)=>{const n=[],o=[...new Set([x(e),"src"])].filter(u=>b(A(e,u))),s=D(e),i=[...Bs.map(u=>A(e,u)).filter(u=>b(u)),...s===void 0?[]:hr(e,s,r),...t.flatMap(u=>hr(e,u,r))],a=new Pr({compilerOptions:{allowJs:!0},useInMemoryFileSystem:!0}),c=o.flatMap(u=>ot(A(e,u),A(e,u),n));return{sites:[...new Set([...c,...i,...Xs([...i,...c],a,e,n)])].flatMap(u=>{const f=nt(u,n);if(f===void 0)return[];const g=a.createSourceFile(J(e,u),f,{overwrite:!0}),h=[...g.getDescendantsOfKind(k.PropertyAssignment),...g.getDescendantsOfKind(k.ShorthandPropertyAssignment),...g.getDescendantsOfKind(k.GetAccessor),...g.getDescendantsOfKind(k.MethodDeclaration)].filter(m=>{const $=S.isPropertyAssignment(m)&&We(m.getInitializer());return Vs(m.getNameNode())===et&&!$}).map(m=>m.getStartLineNumber()),v=g.getDescendantsOfKind(k.BinaryExpression).filter(m=>ri(m)).map(m=>m.getStartLineNumber());return[...h,...v].map(m=>({file:J(e,u),line:m}))}),unreadable:n}},ni=(e,r,t,n,o,s)=>{if(t!=="deploy")return;const{sites:i,unreadable:a}=ti(e,o,s),c=In({openShardAccess:i,schema:{tables:[]}},{lints:[Je]});if(c.length===0&&a.length===0)return;const l=[];if(c.length>0){const u=c.map(f=>`${String(f.metadata.file)}:${String(f.metadata.line)}`).join(", ");l.push(`${u} enable the open-shard option (any value but a literal false), so any caller may target any shard.`)}if(a.length>0&&l.push(`could not read ${a.join(", ")}, so the open-shard option cannot be ruled out there.`),n){r.warn(`deploying past the open-shard gate (--allow-open-shards): ${l.join(" ")}`);return}const d=`deploy blocked (open_shard_access): ${l.join(" ")} ${Je.remediation}`;return r.error(d),d},oi=(e,r,t,n)=>{const o=ni(e,r.logger,t,r.allowOpenShards===!0,r.env,n);return o===void 0?void 0:{code:y.USAGE,error:o}},si=(e,r,t,n)=>{const o=Be(e,r.logger,t,n,r.env);if(o!==void 0)return{code:y.USAGE,error:o};const s=Us(e,r.logger,t,r.env);if(s!==void 0)return{code:y.USAGE,error:s};const i=Ns(e,r.logger,t,r.discoveryCache);if(i!==void 0)return{code:y.USAGE,error:i};const a=Ls(e,r.logger,r.dockerAvailable??En,t,r.env);return a===void 0?void 0:{code:y.MISSING_DEPENDENCY,error:a}},ii=/https?:\/\/[^\s"'<>]+\.workers\.dev[^\s"'<>]*/u,ai=/https:\/\/[^\s"'<>]+/u,ci=e=>{if(e===void 0)return;const r=ii.exec(e);return r?r[0]:ai.exec(e)?.[0]},mr=e=>e===void 0?"":` (--env ${e})`,di=({cwd:e,env:r,logger:t,now:n,url:o})=>{if(o!==void 0)try{const s=Ce(e);if(s!==void 0){if(s.workerUrl===o&&s.env===r)return;t.warn(`link: .lunora/project.json records ${s.workerUrl??"(no url)"}${mr(s.env)}, but this deploy published ${o}${mr(r)}. Keeping the recorded value — run \`lunora link --url ${o}${r===void 0?"":` --env ${r}`}\` to update it.`);return}const i=(n??(()=>new Date().toISOString()))();jt(e,{env:r,linkedAt:i,workerName:Ie(e),workerUrl:o}),t.success(`link: recorded ${o} in .lunora/project.json (run \`lunora link\` to change)`)}catch{}},li=async(e,r)=>{const t=new Pr({skipAddingFilesFromTsConfig:!0}),n=A(r,"lunora");let o;try{o=kt(t,n)}catch(s){const i=s instanceof Error?s.message:String(s);return e.logger.warn(`--migrate: could not discover migrations (${i}); skipping`),0}if(o.length===0)return e.logger.info("--migrate: no data migrations declared in lunora/"),0;e.logger.info(`--migrate: running ${String(o.length)} migration(s) against deployed worker`);for(const s of o){e.logger.info(`--migrate: up "${s.id}" (table "${s.table}")`);const i={cwd:r,fetchImpl:e.fetchImpl,id:s.id,logger:e.logger,prod:!0,subcommand:"up",token:e.migrateToken,url:e.migrateUrl,yes:e.migrateYes===!0},a=await jn(i);if(a.code!==0)return e.logger.error(`--migrate: migration "${s.id}" failed — see output above`),a.code;e.logger.success(`--migrate: "${s.id}" applied`)}return 0},ui=async(e,r)=>{const{info:t}=Pe(r,"lunora",e.discoveryCache),n=t?.vectorMetadata??[];if(n.length===0)return;const o=[];for(const a of n){const c=ln(a.kind);if(c===void 0){e.logger.warn(`vector index "${a.index}" declares metadata "${a.property}", whose column type cannot be filtered on in Vectorize — it is stored with each vector but no filter will match it.`);continue}o.push({index:a.index,property:a.property,type:c})}if(o.length===0)return;const i=(await un({cwd:r,entries:o,exec:je(F(r),"wrangler",[]),logger:e.logger,spawner:e.spawner??q})).filter(a=>a.status!=="failed").length;i>0&&e.logger.success(`vectorize metadata indexes ready: ${String(i)}/${String(o.length)}`)},pr=5,fi=2e3,gi=async(e,r,t)=>{if(e.healthCheck!==!0)return;const n=t??De({cwd:r,env:e.env});if(n===void 0){const s="--health-check: the deploy succeeded, but no URL to probe could be resolved — wrangler's output carried none and this checkout has no link for this environment. Run `lunora link --url <https://your-worker>` and re-deploy, or drop --health-check.";return e.logger.error(s),{error:s,ok:!1,url:""}}const o=await An({attempts:pr,baseUrl:n,delayMs:fi,fetchImpl:e.healthFetch,paths:[Rn,Tn],sleep:e.healthSleep});return o.error===void 0?(e.logger.success(`health check ok (${o.url})`),{ok:!0,url:o.url}):(e.logger.error(`--health-check: the deploy succeeded, but the new version did not answer after ${String(pr)} attempt(s) — ${o.error}`),{error:o.error,ok:!1,url:o.url})},hi=async(e,r,t,n,o,s)=>{e.isolatedPreview!==!0&&await ui(e,r);const i=e.migrate?await li(e,r):0;return i===0&&o?.(),{code:i,descriptor:t,mintedSecretsFile:s,validation:n}},mi=async({cwd:e,descriptor:r,mintedSecretsFile:t,options:n,reblessSchemaBaseline:o,recordArchitecture:s,stdout:i,validation:a})=>{const c={deployedAt:new Date().toISOString(),dryRun:n.dryRun===!0,env:n.env,url:n.dryRun===!0?void 0:ci(i),workerName:Ie(e)};if(n.dryRun===!0)return n.healthCheck===!0&&n.logger.warn("--health-check skipped: a dry run publishes nothing"),{code:0,deployment:c,descriptor:r,mintedSecretsFile:t,validation:a};n.temporary!==!0&&n.isolatedPreview!==!0&&(s?.(),di({cwd:e,env:n.env,logger:n.logger,url:c.url}));const l=await gi(n,e,c.url);if(l?.error!==void 0)return{code:y.UNAVAILABLE,deployment:c,descriptor:r,healthCheck:l,mintedSecretsFile:t,validation:a};const d=n.isolatedPreview===!0?void 0:o;return{...await hi(n,e,r,a,d,t),deployment:c,healthCheck:l}},pi=(e,r,t)=>{const n=A(e,L);if(!b(n))return;let o;try{o=br(E(n,"utf8")).length}catch{return}if(o===0)return;const s=t.toolchain?.secretPut!==void 0;r.warn(s?`Note: \`lunora deploy\` does not push secrets. ${L} has ${String(o)} key(s); if you changed them, run \`lunora env push --yes\` to update the deployed secrets.`:`Note: ${t.name} deploys read no secrets. ${L} has ${String(o)} key(s) that only the dev server sees; a key the app reads from env must be a \`vars\` entry in the deployed config, one it loads from a secret manager at runtime need not.`)},st=async(e,r)=>{let t=[];try{t=Dt(await $r({cache:r,projectRoot:e,schemaDir:x(e)}))}catch{}const n=[...new Set(Ot(t).map(s=>s.key))];let o=[];try{const s=A(e,L);b(s)&&(o=br(E(s,"utf8")).map(i=>i.key).filter(i=>Rr(i)))}catch{}return{fromLocal:o.filter(s=>!n.includes(s)),fromPackages:n,mintableKeys:Lt(t)}},vi=async(e,r,t,n)=>{const{logger:o}=r,s=r.spawner??q,i=F(e),a=r.env===void 0?"":` --env ${r.env}`,c=n.toolchain?.secretPut;if(c===void 0)return o.error(`deploy target "${n.id}" has no secret store; cannot push secrets`),{minted:[],ok:!1};const l=t.map(f=>({key:f,value:Nt()})),d=n.toolchain?.secretBulk;if(d!==void 0&&l.length>=2){const f=z(i,d({environment:r.env,temporary:r.temporary})),g=await s({args:f.args,command:f.command,cwd:e,input:JSON.stringify(Object.fromEntries(l.map(h=>[h.key,h.value])))});if(g.code===0){for(const{key:h}of l)o.success(`generated + pushed ${h}`);return{minted:l,ok:!0}}o.warn(`bulk secret push failed (exit ${String(g.code)}); retrying one key at a time`)}const u=[];for(const{key:f,value:g}of l){const h=c({environment:r.env,key:f,temporary:r.temporary}),v=z(i,h),m=await s({args:v.args,command:v.command,cwd:e,input:g});if(m.code!==0)return o.error(`failed to push secret ${f} (exit ${String(m.code)}); set it manually with \`wrangler secret put ${f}${a}\`.`),{minted:u,ok:!1};u.push({key:f,value:g}),o.success(`generated + pushed ${f}`)}return{minted:u,ok:!0}},yi=/^[\w-]+$/u,vr=[".dev.vars",".dev.vars.*","!.dev.vars.example"],wi=(e,r)=>{try{const t=Ut(e,vr);t.length>0&&r.info(`.gitignore: added ${t.join(", ")} so the recorded secret cannot be committed`)}catch(t){r.warn(`could not update .gitignore (${t instanceof Error?t.message:String(t)}) — add \`${vr.join("` and `")}\` by hand before committing.`)}},ki=(e,r,t)=>{if(t.length===0)return;const n=t.map(a=>a.key).join(", ");if(r.env!==void 0&&!yi.test(r.env)){r.logger.warn(`${n}: minted and pushed for --env ${r.env}, but "${r.env}" isn't a safe filename fragment — the value could not be recorded. Capture it manually if you need it again.`);return}const o=r.env===void 0?L:`${L}.${r.env}`;wi(e,r.logger);const s=A(e,o);let i=b(s)?E(s,"utf8"):"";for(const a of t)i=xt(i,a.key,a.value);return Ft(s,i),r.logger.success(r.env===void 0?`${n}: value(s) recorded in ${o}`:`${n}: value(s) recorded in ${o} (kept separate from ${L} so \`lunora dev\` and a plain \`lunora env push\` don't inherit the --env ${r.env} value)`),o},bi=/^<[^>]+>$/u,$i=async(e,r,t)=>{let n;try{n=t.projectConfig?.(e,"deploy").vars??{}}catch(d){return{error:d instanceof Error?d.message:String(d)}}const o=d=>{const u=n[d];if(typeof u!="string")return!0;const f=u.trim();return f===""||bi.test(f)?!0:Rr(d)&&Mt(u)},s=d=>Object.hasOwn(n,d)?`${d} (empty or a placeholder)`:d,{fromLocal:i,fromPackages:a}=await st(e,r.discoveryCache),c=i.filter(d=>o(d));c.length>0&&r.logger.warn(`${L} key(s) not set in the wrangler vars: ${c.map(d=>s(d)).join(", ")}. ${t.name} has no secret store: a key the app reads from env must be a \`vars\` entry; one it loads from a secret manager at runtime need not.`);const l=a.filter(d=>o(d));return l.length===0?{}:{error:`missing required secret(s) in the wrangler vars: ${l.map(d=>s(d)).join(", ")}. ${t.name} has no secret store and Lunora reads these from env, so each must be a real \`vars\` value in the config the deploy ships — write it there from your secret manager at deploy time rather than committing it. Add them, then re-deploy.`}},Si=async(e,r,t,n)=>{if(r.dryRun===!0||r.isolatedPreview===!0)return{};if(n.toolchain?.secretList===void 0)return $i(e,r,n);const{logger:o}=r,s=r.env===void 0?"":` --env ${r.env}`;let i;try{i=await(r.secretLister??On)({cwd:e,env:r.env,target:n.id,temporary:r.temporary})}catch{return{}}if(!i.ok)return{};const a=new Set(i.names),{fromLocal:c,fromPackages:l,mintableKeys:d}=await st(e,r.discoveryCache),u=[...l,...c].filter(h=>!a.has(h));if(u.length===0)return{};if(!t)return{error:`missing required secret(s) on the deploy target: ${u.join(", ")}. Set them with \`wrangler secret put <KEY>${s}\` (or \`lunora env generate --set\` then \`lunora env push --yes${s}\`), then re-deploy.`};for(const h of u.filter(v=>!d.has(v)))o.warn(`required secret ${h} is not set on the target — set it with: wrangler secret put ${h}${s}`);const f=u.filter(h=>d.has(h));if(f.length===0)return{};if(await(r.secretConfirm??Dn())(`${String(f.length)} required secret(s) not set on the target (${f.join(", ")}). Generate strong values and push them now?`)){const{minted:h,ok:v}=await vi(e,r,f,n),m=ki(e,r,h);return v?{mintedSecretsFile:m}:{error:"failed to push required secret(s) — set them manually and re-deploy",mintedSecretsFile:m}}return o.warn(`${String(f.length)} required secret(s) not set on the target: ${f.join(", ")}. Generate + push with \`lunora env generate --set\` then \`lunora env push --yes${s}\`.`),{}},it=e=>e.format==="json"?!1:e.interactive!==void 0?e.interactive:process.stdout.isTTY&&!process.env.CI,Ei=async(e,r)=>{if(r.dryRun===!0)return;const t=kr(e,"lunora",r.discoveryCache).containers.flatMap(o=>o.schedulingPolicy===void 0&&o.image.kind==="build"?[{buildDir:o.image.buildDir,exportName:o.exportName}]:[]);if(t.length===0)return;const n=await us({cwd:e,logger:r.logger,railpackAvailable:r.railpackAvailable,spawner:r.spawner,targets:t});return n.code===0?void 0:n.error??"railpack build failed"},Ai=(e,r,t)=>{if(t!==void 0)try{const n=Qt(e,t);n.changed&&r.success(`synced ${String(t.length)} cron trigger(s) → ${n.wranglerPath??"wrangler.jsonc"}`);for(const s of n.warnings)r.warn(s);const o=Zt(n.preserved);o!==void 0&&r.info(o)}catch(n){const o=n instanceof Error?n.message:String(n);r.warn(`cron trigger sync skipped: ${o}`)}},Ri=async(e,r,t,n,o,s)=>{try{le(n);const i=await $r({cache:s,projectRoot:e,schemaDir:x(e)}),a=Yt(e,i,{environment:o,target:n}),c=a.wranglerPath??"wrangler.jsonc";if(a.added.length>0&&r.success(`provisioned bindings: ${a.added.join(", ")} → ${c}`),a.updated.length>0&&r.success(`updated bindings: ${a.updated.join(", ")} → ${c}`),a.devConfig!==void 0){const{added:l,path:d,updated:u}=a.devConfig;r.success(`dev sidecar bindings: ${[...l,...u].join(", ")} → ${d}`)}for(const l of a.warnings)r.warn(l)}catch(i){const a=i instanceof Error?i.message:String(i);r.warn(`binding inference skipped: ${a}`)}try{const i=Xt(e);i.changed&&r.success(`bumped compatibility_date to ${i.date??"unknown"} (Workers Cache enabled) → ${i.wranglerPath??"wrangler.jsonc"}`)}catch(i){const a=i instanceof Error?i.message:String(i);r.warn(`compatibility date sync skipped: ${a}`)}Ai(e,r,t)},Ti=async(e,r,t,n,o,s,i,a,c,l)=>{let d;r?(d=new en({name:"dots"}),d.start("running codegen")):t.info("running codegen");try{const u=bt({apiSpec:n,dryRun:!l,lazyFunctions:o,projectRoot:e,target:s,...Jt(e)});d?.succeed("codegen complete"),d||t.success("codegen complete");const f=sn(u.platformDiagnostics,t);if(f.errors.length>0)return{error:f.errors.join("; ")};const{errorAdvisories:g,names:h,shouldBlock:v}=an(u.advisories,c);if(v){const $=`${g.length.toString()} ERROR-level ${g.length===1?"advisory":"advisories"} (${h.join(", ")}). Pass --no-strict-advisories to downgrade this to a warning and continue.`;return t.error($),{error:$}}const m=l?await Gt({cwd:e,logger:t,spawner:i,stdoutToStderr:a}):void 0;return m?.error!==void 0?{error:m.error}:{result:u}}catch(u){d?.failed("codegen failed");const f=u instanceof Error?u.message:String(u);return t.error(`codegen failed: ${f}`),{error:`codegen failed: ${f}`}}},yr="--preview is not supported: Cloudflare gives a Worker that implements a Durable Object no version preview URL, and a version shares production's Durable Objects, D1 and R2. Deploy an isolated copy with `lunora preview deploy <name>` (its own Worker, Durable Object namespace, D1, R2 and KV; `lunora preview delete <name>` removes it), or validate without publishing with `--dry-run`.",Ci=(e,r)=>({dryRun:r.dryRun,entry:Ue(e),environment:r.env,outDir:r.outDir,secretsFile:r.secretsFile,outFile:r.outFile,preview:r.preview,temporary:r.temporary}),at=(e,r,t)=>{const{toolchain:n}=t;if(n===void 0)throw new Error(`deploy target "${t.id}" has no command-line toolchain`);const o=Ci(e,r);return Sr(t,e,"deploy",s=>n.deploy({...o,configPath:s}))},Pi=(e,r,t)=>{const n=at(e,r,t),o=Ue(e);return o!==void 0&&r.logger.info(`class-B composition: deploying ${o} (overrides wrangler main)`),r.temporary&&r.logger.info("temporary account: deploying to a short-lived Cloudflare account (~60min); wrangler will print a claim URL"),r.dryRun&&r.logger.info("dry run: validating + bundling without publishing"),r.outDir!==void 0&&r.logger.info(`build artifact: emitting bundle to ${r.outDir}`),n.commit(),n.projection!==void 0&&n.projection.dropped.length>0&&r.logger.warn(`${t.name} ignores these wrangler keys, so they were left out of ${n.projection.configPath}: ${n.projection.dropped.join(", ")}`),n.command},P={problems:[],wranglerPath:void 0},ve=(e,r)=>({code:y.USAGE,descriptor:void 0,error:e,validation:P,...r}),_i=(e,r)=>{for(const t of e.report?.warnings??[])r.warn(`wrangler.jsonc: ${t}`);if(e.problems.length===0)return!1;r.error("wrangler.jsonc validation failed:");for(const t of e.problems)r.error(` - ${t}`);return!0},Ii=(e,r,t)=>{const n=r.format==="json",o=r.dryRun!==!0,s=Pi(e,r,t),i=z(F(e),s);return{args:i.args,captureStdout:o&&!n,captureStdoutSilently:o&&n,command:i.command,cwd:e,stdoutToStderr:n&&!o}},He=e=>{if(e.skipServices===!0)return"--skip-services";if(e.isolatedPreview===!0)return"a preview never deploys services: they are production's Workers";if(e.temporary===!0)return"--temporary deploys the app to a throwaway account"},ji=(e,r)=>He(r)===void 0?Te(e).services.map(t=>t.wranglerPath):[],ct=(e,r,t,n,o)=>({cwd:e,environment:r.env,frameworkBuild:n,lazyFunctions:de(r.lazyFunctions),logger:r.logger,outputJson:r.format==="json"||r.clientBuildOutputJson===!0,spawner:o,strictAdvisories:_e(r),target:t.id}),Di=(e,r,t)=>{const{toolchain:n}=t,o=n===void 0?`target "${t.id}" has no deploy command`:He(r);if(o!==void 0||n===void 0){const s=e.map(i=>i.worker).join(", ");r.logger.warn(`services not deployed (${o??""}): ${s} — deploy them yourself if the app calls anything new`);return}return n},dt=(e,r)=>{for(const t of e){const n=Fe(r(R(t.wranglerPath)));if(n!==void 0)return`service ${t.name} (${t.worker}): ${n}`}},Oi=(e,r,t)=>{const{error:n,services:o}=Te(e);if(n!==void 0||o.length===0)return;const s=le(t);if(s.toolchain===void 0||He(r)!==void 0)return;const i=r.skipCodegen===!0?!1:r.frameworkBuild,a=[...new Map(o.map(c=>[c.wranglerPath,c])).values()];return dt(a,c=>ct(c,r,s,i,q))},lt=(e,r,t)=>{const n=e.cwd??process.cwd(),o=pn(n,e.target);if(o.target===void 0){const a=o.error??"unknown deploy target";return e.logger.error(a),{kind:"refused",error:a,validation:P}}if(e.preview===!0)return e.logger.error(yr),{kind:"refused",code:y.USAGE,error:yr,validation:P};try{at(n,e,le(o.target))}catch(a){if(!(t&&a instanceof Bt&&xe(n,e.skipCodegen===!0?!1:e.frameworkBuild,e.env))){const c=a instanceof Error?a.message:String(a);return e.logger.error(c),{kind:"refused",error:c,validation:P}}}const s=e.openShardGatePassed===!0?void 0:oi(n,e,r,ji(n,e));if(s!==void 0)return{kind:"refused",code:s.code,error:s.error,validation:P};const i=Oi(n,e,o.target);return i!==void 0?(e.logger.error(i),{kind:"refused",code:y.USAGE,error:i,validation:P}):{kind:"ok",target:o.target}},Li=(e,r)=>e.apiSpec??(e.clientBuildRan===!0?wr(r):void 0),Ni=async(e,r,t)=>{if(r!=="celld")return;const n=await(e.celldToolchain??qt)({dryRun:t==="prepare"||e.dryRun===!0,env:process.env,purpose:"deploy"});for(const o of n.warnings)e.logger.warn(o);return n.errors.length>0?n.errors.join("; "):void 0},ut=(e,r)=>{const t=De({cwd:e,env:r.env,url:r.migrateUrl}),n=t===void 0?void 0:Rs(e,r.env,t);if(n!==void 0)return{reason:n};const o=t===void 0?void 0:nn(t,r.logger,e),s=o===void 0?void 0:rn({cwd:e,token:r.migrateToken,url:o}).token;return{target:o===void 0||!s?void 0:{baseUrl:o,fetchImpl:r.fetchImpl??on,token:s}}},xi=(e,r,t)=>async n=>{const o=ut(e,r);return"reason"in o?{kind:"inconclusive",reason:o.reason}:_n({changes:n,tables:t.schemaTables,target:o.target})},Fi=(e,r)=>async t=>{const n=ut(e,r);return"reason"in n?n.reason:Pn({changes:t,now:Date.now(),target:n.target})},Mi=(e,r,t,n)=>e==="deploy"?{checkStoredRows:xi(r,t,n),countExpiringRows:Fi(r,t)}:{},Ui=async(e,r)=>{const t=e.cwd??process.cwd(),n=it(e),o=_e(e),s={problems:[],wranglerPath:void 0},i=lt(e,r,!1);if(i.kind==="refused")return{code:i.code,error:i.error,validation:i.validation};const{target:a}=i,c=await Ni(e,a,r);if(c!==void 0)return e.logger.error(c),{code:y.MISSING_DEPENDENCY,error:c,target:a,validation:s};let l;if(!e.skipCodegen){const v=await Ti(t,n,e.logger,Li(e,t),de(e.lazyFunctions),a,e.spawner,e.format==="json",o,e.clientBuildRan!==!0);if(v.error!==void 0)return{error:v.error,target:a,validation:P};l=v.result,e.discoveryCache?.clear()}let d;if(l!==void 0){const v=await Cn({allowDrift:e.allowSchemaDrift===!0,...Mi(r,t,e,l),codegen:l,command:r,logger:e.logger});if(v.blocked)return{error:`schema drift gate blocked ${r}`,schemaDrift:{blocked:!0,reason:v.reason},target:a,validation:s};d=v.rebless}const u=l&&Bn({current:l.architecture,cwd:t,environment:e.env,logger:e.logger});await Ri(t,e.logger,l?.cronTriggers,a,e.env,e.discoveryCache);const f=await H(t,e,r,a,"check");if(f!==void 0)return{error:f,target:a,validation:P};const g=si(t,e,r,a);if(g!==void 0)return{code:g.code,error:g.error,target:a,validation:P};const h=zt({environment:e.env,projectRoot:t});return _i(h,e.logger)?{error:"wrangler validation failed",target:a,validation:h}:{codegen:l,reblessSchemaBaseline:d,recordArchitecture:u,target:a,validation:h}},Bi=e=>{oe(p(e,".wrangler","deploy","config.json"),{force:!0})},Wi=async(e,r,t)=>{const n=Fe(e);if(n!==void 0)return{built:!1,error:n};const o=xe(e.cwd,r,t);return o&&Bi(e.cwd),{built:o,error:await Xr(e)}},Hi=(e,r,t)=>r||t===!1?Oe(e):void 0,qi=async(e,r,t)=>{const n=R(e);return Be(n,r.logger,"deploy",t.id,r.env,e)??H(n,r,"deploy",t.id,"check",e)},Gi=(e,r,t)=>e.map((n,o)=>{const s=r[o]===!0,i=Hi(n.wranglerPath,s,t);return{built:s,config:i??n.wranglerPath,resolved:i!==void 0,service:n}}),Vi=async(e,r,t)=>{const{config:n,resolved:o,service:s}=e;if(!b(n))return;const i=R(s.wranglerPath),a=o?void 0:r.env,c=Be(i,r.logger,"deploy",t.id,a,n);if(c!==void 0)return{code:y.USAGE,error:`service ${s.name} (${s.worker}): ${c}`};if(o&&!e.built&&r.env!==void 0&&$o(i,void 0,n)){const d=`service ${s.name} (${s.worker}): its prebuilt config ${n} still has a Hyperdrive placeholder, and it was not built for --env ${r.env}, so it cannot be resolved here. Build it with CLOUDFLARE_ENV=${r.env}.`;return r.logger.error(d),{code:y.USAGE,error:d}}const l=await H(i,{...r,clientBuildEnvironment:void 0,env:a},"deploy",t.id,"check",n);return l===void 0?void 0:{code:y.USAGE,error:`service ${s.name} (${s.worker}): ${l}`}},Ki=async(e,r,t)=>{for(const n of e){const o=await Vi(n,r,t);if(o!==void 0)return o}},zi=async(e,r,t)=>{const{config:n,resolved:o,service:s}=e;if(!b(n))return;const i=R(s.wranglerPath),a=o?void 0:r.env,c=e.built?r.env:void 0,l=await H(i,{...r,clientBuildEnvironment:c,env:a},"deploy",t.id,"create",n);if(l!==void 0)return{code:y.FAILURE,error:`service ${s.name} (${s.worker}): ${l}`};if(n===s.wranglerPath)return;const d=await H(i,{...r,clientBuildEnvironment:void 0},"deploy",t.id,"create",s.wranglerPath);return d===void 0?void 0:{code:y.FAILURE,error:`service ${s.name} (${s.worker}): ${d}`}},Ji=async(e,r,t,n,o)=>{const s=[];for(const i of e){const{built:a,error:c}=await Wi(r(R(i.wranglerPath)),t,n);if(c!==void 0){const l=`service ${i.name} (${i.worker}): ${c}`;return o.error(l),{builds:s,failure:{code:y.FAILURE,error:l}}}s.push(a)}return{builds:s}},Yi=async(e,r)=>{const{driver:t,manager:n,options:o,spawner:s,toolchain:i}=r,{config:a,service:c}=e,l=R(c.wranglerPath);let d;try{d=Sr(t,l,"deploy",g=>i.deploy({configPath:g??a,dryRun:o.dryRun===!0,environment:o.env}))}catch(g){return{code:y.FAILURE,error:`service ${c.name} (${c.worker}): ${g instanceof Error?g.message:String(g)}`}}d.commit();const u=z(n,d.command);o.logger.info(`deploying service ${c.name} (${c.worker}) via ${u.command} ${u.args.join(" ")}`);const f=await s({args:u.args,command:u.command,cwd:l,stdoutToStderr:o.format==="json"});if(f.code!==0)return{code:y.FAILURE,error:`service ${c.name} (${c.worker}): ${u.command} ${u.args.join(" ")} exited ${String(f.code)} — stopping before the app`}},Xi=async(e,r,t,n)=>{const{error:o,services:s}=Te(e);if(o!==void 0)return{code:y.FAILURE,error:o};if(s.length===0)return;const i=Di(s,r,t);if(i===void 0)return;const a=F(e),c=[...new Map(s.map($=>[$.wranglerPath,$])).values()],l=r.skipCodegen===!0?!1:r.frameworkBuild,d=$=>ct($,r,t,l,n),u=dt(c,d);if(u!==void 0)return r.logger.error(u),{code:y.USAGE,error:u};for(const $ of c){const C=await qi($.wranglerPath,r,t);if(C!==void 0)return{code:y.USAGE,error:`service ${$.name} (${$.worker}): ${C}`}}const{builds:f,failure:g}=await Ji(c,d,l,r.env,r.logger);if(g!==void 0)return g;const h=Gi(c,f,l),v=await Ki(h,r,t);if(v!==void 0)return v;const m={driver:t,manager:a,options:r,spawner:n,toolchain:i};for(const $ of h){const C=await zi($,r,t);if(C!==void 0)return C;const T=await Yi($,m);if(T!==void 0)return T}},Qi=async(e,r,t,n)=>{if(r!=="cloudflare")return;const o=e===void 0?void 0:I(e).parsed,s=(t===void 0?void 0:o?.env?.[t]?.account_id)??o?.account_id,i=await gn({accountId:s});(i.verdict==="near"||i.verdict==="full")&&n.warn(i.fix===void 0?i.message:`${i.message} ${i.fix}`)},Zi=e=>e.dryRun!==!0&&e.temporary!==!0,ea=e=>e.temporary===!0?"temporary":e.dryRun===!0?"preview":"defer",ra=e=>{const r=e.fix===void 0?e.message:`${e.message} ${e.fix}`;return e.verdict==="unchecked"?`${r} The deploy stops until it can be checked, as a namespace made by the first repo is unrestricted.`:r},ta=(e,r,t)=>{const n=[];for(const o of e)if(o.verdict==="ok")o.changesAccount===!0&&r.info(o.message);else if(o.verdict==="unchecked"&&t==="preview")r.warn(`dry run: ${o.message}${o.fix===void 0?"":` ${o.fix}`} A real deploy stops here.`);else{const s=ra(o);r.error(s),n.push(s)}return n},ft=async(e,r,t,n,o,s,i)=>{const a=t==="cloudflare"&&r!==void 0?I(r).parsed:void 0;if(a===void 0)return;const c=(ue(a,n).merged.artifacts??[]).flatMap(h=>typeof h?.binding=="string"&&typeof h.namespace=="string"?[{binding:h.binding,namespace:h.namespace}]:[]),l=c.length===0?void 0:Pe(e,x(e),s).info?.jurisdiction;if(l===void 0)return;const d=[...new Map(c.map(h=>[h.namespace,h])).values()];if(i==="temporary"){o.warn(`--temporary deploys to a throwaway account, so the jurisdiction ("${l}") of Artifacts namespace ${d.map(h=>`"${h.namespace}"`).join(", ")} was not checked: it is created there on first use, in no jurisdiction.`);return}const u=(n===void 0?void 0:a.env?.[n]?.account_id)??a.account_id,f=await Promise.all(d.map(async h=>fn({...h,accountId:u,create:i==="create",jurisdiction:l,missingOk:i==="defer"||i==="preview"}))),g=ta(f,o,i);return g.length===0?void 0:g.join(`
|
|
64
|
-
`)},
|
|
63
|
+
`);const m=await $n(d),{args:$,command:C}=z(F(n),u.dev({configPath:t===void 0?void 0:L(n,t),environment:i,extraArgs:[L(n,f),"--local","--local-protocol","http","--ip","127.0.0.1","--port",String(m),"--persist-to",L(n,p(s,"state")),...l?["--assets",L(n,p(s,"assets"))]:[]]}));return{args:$,command:C,port:m,runId:g}}catch(f){throw new j(`could not prepare the check (${f instanceof Error?f.message:String(f)})`,!0)}},ls=async e=>{const{composedEntry:r,cwd:t,environment:n,launcher:o,logger:s,toolchain:i}=e,a=O(t),c=p(t,a,"_generated","functions.ts"),l=b(c)&&E(c,"utf8").includes("export const loadAllFunctions"),d=l?c:void 0,u=Ho(t,a),f=[...u,...Vo(t,a,u)],g=xo(t,r??xr(t,n,e.configPath).main,a);if(!l&&f.length===0&&g.length===0)return;const h=Ko(e);if("skip"in h){s.warn(`smoke-load skipped: ${h.skip}, so the modules the Worker loads on demand are not evaluated before deploy`);return}const{base:v,configFile:m,missingAssets:$}=h,C=[];let T,M;const He=Date.now()+$e,gt=process.platform!=="win32",ht=(U,Q,Z)=>{const B=Tr(U,[...Q],{cwd:Z.cwd,detached:gt,shell:Z.shell,stdio:["ignore","pipe","pipe"]});return B.pid!==void 0&&(M={dispose:Zo(B.pid),pid:B.pid}),B};try{T=as(t),s.info(`smoke-loading ${rs(d!==void 0,f.length,g.length)} in workerd`);let U=Fo;for(let Q=0;Q<nr;Q+=1){const{args:Z,command:B,port:me,runId:mt}=await ds({base:v,configPath:m,cwd:t,deferred:f,directory:T,environment:n,functionsFile:d,integrationModules:g,missingAssets:$,preferred:U,toolchain:i}),qe=new AbortController;U=me+1;const Ge=await ns({args:Z,command:B,cwd:t,spawn:o??ht,label:"wrangler dev (smoke-load)",notFound:"`wrangler` was not found",onLine:G=>{C.push(G),zo.test(G)&&qe.abort()},port:me,readyTimeoutMs:Math.max(0,He-Date.now()),shell:process.platform==="win32"});if(Ge===void 0){await ie(M),M=void 0;continue}try{const G=await Xo({deadline:He,deferred:f,functions:d!==void 0,integrationModules:g,output:C,port:me,runId:mt,startupFailed:qe.signal});if(G!==void 0){ss(t,G,s);return}}finally{await ts(Ge,M),M=void 0}}throw new j(`the smoke port was answered by other runs on all ${String(nr)} attempts`,!0)}catch(U){return is(U,C,s)}finally{if(await ie(M),T!==void 0)try{oe(T,{force:!0,recursive:!0})}catch{}}},us=(e,r)=>r===void 0?e:_(e,r),fs=async e=>{if(e.targets.length===0)return{builtTags:[],code:0};if(!(e.railpackAvailable??En)()){const n="deploy blocked: a container uses `image: { build }` (Railpack), but Railpack isn't ready. Install the `railpack` CLI and start a BuildKit instance, e.g. `docker run --rm --privileged -d --name buildkit moby/buildkit` then `export BUILDKIT_HOST=docker-container://buildkit`. Alternatively switch the container's `image` to a Dockerfile path or a pre-built registry reference.";return e.logger.error(n),{builtTags:[],code:1,error:n}}const r=e.spawner??q,t=[];for(const n of e.targets){const o=Sn(n.exportName),s={args:["build",n.buildDir,"--name",o],command:"railpack",cwd:e.cwd},i=je(F(e.cwd),"wrangler",["containers","push",o]),a={args:i.args,command:i.command,cwd:e.cwd};e.logger.info(`railpack: building "${n.exportName}" → ${o} from ${n.buildDir}`);const c=await r(s);if(c.code!==0){const d=`railpack build failed for container "${n.exportName}" (${n.buildDir})`;return e.logger.error(d),{builtTags:t,code:c.code,error:d}}e.logger.info(`railpack: pushing ${o} to the Cloudflare Registry`);const l=await r(a);if(l.code!==0){const d=`wrangler containers push failed for "${n.exportName}" (${o})`;return e.logger.error(d),{builtTags:t,code:l.code,error:d}}t.push(o)}return{builtTags:t,code:0}},gs=new Set(["0.0.0.0","[::1]","[::]","localhost"]),hs=[".local",".localhost"],ms=["[fc","[fd"],ps=/^\[fe[89ab][0-9a-f]:/u,vs=/^[a-z]+:\/\//u,dr=e=>{const r=e.split(".").map(Number);if(r.length!==4||r.some(o=>!Number.isInteger(o)))return!1;const[t=-1,n=-1]=r;return t===0||t===127||t===100&&n>=64&&n<=127||t===198&&(n===18||n===19)||t===10||t===192&&n===168||t===172&&n>=16&&n<=31||t===169&&n===254},lr="[::ffff:",ys=e=>{if(!e.startsWith(lr)||!e.endsWith("]"))return;const r=e.slice(lr.length,-1);if(r.includes("."))return r;const[t=0,n=0]=r.split(":").map(o=>Number.parseInt(o,16));return[Math.floor(t/256),t%256,Math.floor(n/256),n%256].join(".")},Wr=e=>{let r=e.length;for(;r>0&&e[r-1]===".";)r-=1;return e.slice(0,r).toLowerCase()},ws=e=>{const r=e.toLowerCase(),t=ys(r);return t!==void 0?dr(t):gs.has(r)||hs.some(n=>r.endsWith(n))||ms.some(n=>r.startsWith(n))||ps.test(r)||dr(r)},Hr=e=>{const r=D(e);return r===void 0?void 0:I(r).parsed},qr=(e,r)=>{const t=typeof e?.name=="string"&&e.name!==""?e.name:void 0;if(r===void 0)return t;const n=e?.env?.[r]?.name;return typeof n=="string"&&n!==""?n:t===void 0?void 0:`${t}-${r}`},ae=(e,r)=>{let t=e.length;for(;t>0&&e[t-1]===r;)t-=1;return e.slice(0,t)},ks=e=>{const r=e.replace(vs,""),t=r.indexOf("/"),n=(t===-1?r:r.slice(0,t)).toLowerCase(),o=t===-1?"":ae(r.slice(t),"*");return o==="/"?n:`${n}${o}`},ur=e=>[e?.route,...Array.isArray(e?.routes)?e.routes:[]].flatMap(t=>{const n=typeof t=="string"?t:t?.pattern;return typeof n=="string"&&n!==""?[ks(n)]:[]}),bs=(e,r,t)=>{const n=t.indexOf("/"),o=n===-1?t:t.slice(0,n),s=n===-1?"":t.slice(n),i=o.startsWith("*");if(!(!(i?e.endsWith(o.slice(1)):e===o)||s!==""&&!`${ae(r,"/")}/`.startsWith(s)))return s.length*1e6+(i?0:5e5)+o.length},$s=e=>e!==void 0&&(Object.hasOwn(e,"route")||Object.hasOwn(e,"routes")),Gr=(e,r)=>{const t=ur(e),o=[...new Set([...Object.keys(e?.env??{}),...r===void 0?[]:[r]])].map(s=>{const i=e?.env?.[s];return{id:`env:${s}`,label:`--env ${s}`,routes:$s(i)?ur(i):t}});return[{id:"top",label:"production",routes:t},...o]},Ss=(e,r,t,n)=>{const o=r===void 0?"top":`env:${r}`,s=Gr(e,r).flatMap(c=>c.routes.flatMap(l=>{const d=bs(t,n,l);return d===void 0?[]:[{block:c,score:d}]}));if(s.length===0)return{kind:"none"};const i=Math.max(...s.map(c=>c.score)),a=s.filter(c=>c.score===i);return a.some(c=>c.block.id===o)?{kind:"own"}:{kind:"other",label:a[0]?.block.label??"production"}},ne=e=>nn(`${e.origin}${e.pathname}`),Es=(e,r,t,n,o)=>{const s=Ce(e),i=s?.workerUrl!==void 0&&URL.canParse(s.workerUrl)?new URL(s.workerUrl):void 0;if(i===void 0)return;if(s?.env===r)return ne(i)===ne(t)?null:`${n}: this checkout is linked to ${ne(i)}${o}`;const a=`${ae(i.pathname,"/")}/`;if(i.origin!==t.origin||!`${ae(t.pathname,"/")}/`.startsWith(a))return;const c=s?.env===void 0?"production":`--env ${s.env}`;return`${n}: this checkout links it to ${c}, not this deploy${o}`},Vr=(e,r,t,n,o,s)=>{const i=Ss(e,r,t,n);if(i.kind==="other")return`${o}: wrangler routes it to ${i.label}, not this deploy${s}`;if(i.kind==="own")return;const a=Gr(e,r);if(a.every(l=>l.routes.length===0))return;const c=a.find(l=>l.id===(r===void 0?"top":`env:${r}`))?.routes??[];return`${o}: this deploy${s} routes to ${c.length===0?"no declared route":c.join(", ")}, not ${t}`},As=(e,r,t,n,o,s)=>{const i=Hr(e);if(t.endsWith(".workers.dev")){const a=qr(i,r);return a!==void 0&&t.split(".")[0]!==a?`${o}: this deploy ships the Worker "${a}"${s}`:`${o}: no link ties this *.workers.dev host to your account${s}; run \`lunora link --url https://${t}${r===void 0?"":` --env ${r}`}\` once the Worker is deployed, then deploy again (or confirm the drift yourself with --allow-schema-drift)`}return Vr(i,r,t,n,o,s)},Rs=(e,r,t,n,o)=>{const s=Hr(e),i=Wr(t.hostname);if(i.endsWith(".workers.dev")){const a=qr(s,r);return a===void 0||i.split(".")[0]===a?void 0:`${n}: this deploy ships the Worker "${a}", not the one the link names${o}`}return Vr(s,r,i,t.pathname,n,o)},Ts=(e,r,t)=>{let n;try{n=new URL(t)}catch{return`refused ${t} as the deployment to read rows from: it is not a URL`}const o=`refused ${ne(n)} as the deployment to read rows from`,s=r===void 0?"":` (--env ${r})`,i=Wr(n.hostname);if(ws(i))return`${o}: it is a local or private address, not the worker this deploy ships to`;const a=Es(e,r,n,o,s);return a===void 0?As(e,r,i,n.pathname,o,s):a??Rs(e,r,n,o,s)},Kr=e=>{const t=[D(e),p(e,"package.json"),p(e,"wrangler.dev.jsonc")].filter(n=>n!==void 0&&b(n)).map(n=>[n,E(n,"utf8")]);return()=>{for(const[n,o]of t)E(n,"utf8")!==o&&X(n,o,"utf8")}},Cs=(e,r=Cr())=>_(e,".lunora",`vite-bundle-meta-${r}.json`),zr="LUNORA_CLIENT_BUILD",Jr="virtual:lunora/worker",Ae=(e,r)=>["ts","mts","cts","js","mjs","cjs"].some(t=>b(p(e,`${r}.config.${t}`))),Yr=e=>Ae(e,"rsbuild")?"rsbuild":Ae(e,"vite")?"vite":void 0,Ps=e=>Ae(e,"vite")?"vite":Yr(e),_s=e=>{try{const{scripts:r}=JSON.parse(E(p(e,"package.json"),"utf8"));return typeof r?.build=="string"&&r.build!==""}catch{return!1}},ge=(e,r)=>{if(process.env[zr]==="1")return;const t=D(e),n=t===void 0?void 0:I(t).parsed,o=n?.main===Jr,s=(o?Ps(e):Yr(e))??(_s(e)?"script":void 0);if(s===void 0)return;const i=r===void 0?void 0:n?.env?.[r]?.assets?.directory,a=n?.assets?.directory??i;return o||At(e).class!=="C"||a!==void 0?s:void 0},Is=async(e,r,t)=>{const n=t.spawner??q,o=F(e),s=r==="script"?{args:["run","build"],command:o}:je(o,r,["build"]);t.logger.info(`running ${r} build before bundling the Worker`);const i=Kr(e),a=t.bundleMetaPath??Cs(e);oe(p(e,Rt),{force:!0});try{const c=await n({args:s.args,command:s.command,cwd:e,env:{[It]:jt(e),[_t]:t.target,[Pt]:a,...t.lazyFunctions?{[Ct]:"1"}:{},...t.environment===void 0?{}:{CLOUDFLARE_ENV:t.environment},...t.strictAdvisories===!1?{[Tt]:"0"}:{},[zr]:"1"},stdoutToStderr:t.outputJson});return c.code===0?void 0:`${r} build failed with exit code ${String(c.code)}`}finally{i(),t.bundleMetaPath===void 0&&oe(a,{force:!0})}},xe=(e,r,t)=>r!==!1&&ge(e,t)!==void 0,js=(e,r,t)=>{const n=r===!1?void 0:ge(e,t);if(n===void 0||n==="script")return!1;const o=D(e);return o!==void 0&&I(o).parsed?.main===Jr||wr(e)!==void 0},Fe=e=>{const r=e.frameworkBuild===!1?void 0:ge(e.cwd,e.environment);if(r===void 0||r==="script")return;if(e.apiSpec!==void 0)return`--api-spec is read from the ${r} plugin config when the client build runs; set it there, or pass --no-framework-build`;if(e.target!==void 0)try{ke(e.cwd,e.target)}catch(n){return n instanceof Error?n.message:String(n)}let t;try{t=ke(e.cwd)}catch(n){return`the project's configured target cannot be used, and the ${r} plugin reads it: ${n instanceof Error?n.message:String(n)}`}if(e.target!==void 0&&e.target!==t)return`this run targets "${e.target}", but the project's target is "${t}", which the ${r} plugin reads; set it in the project config, or pass --no-framework-build`},Xr=async e=>{const r=e.frameworkBuild===!1?void 0:ge(e.cwd,e.environment);if(r===void 0)return;let t;try{t=e.target??ke(e.cwd)}catch(n){return n instanceof Error?n.message:String(n)}return Is(e.cwd,r,{bundleMetaPath:e.bundleMetaPath,environment:e.environment,strictAdvisories:e.strictAdvisories,lazyFunctions:e.lazyFunctions,logger:e.logger,outputJson:e.outputJson,spawner:e.spawner,target:t})},fr=e=>Array.isArray(e)?e.filter(r=>r!=null):void 0,Me=(e,r,t=D(e))=>{if(!t)return;const{parsed:n}=I(t);if(n===void 0)return;const{error:o,merged:s}=ue(n,r),i=o===void 0?s:n,{containers:a,d1_databases:c,vars:l}=i;return{containers:fr(a),d1_databases:fr(c),vars:typeof l=="object"&&l!==null&&!Array.isArray(l)?l:void 0}},Ds=/^127(?:\.\d{1,3}){3}$/,Os=e=>{try{const{hostname:r}=new URL(e);return r==="localhost"||Ds.test(r)||r==="::1"||r==="[::1]"}catch{return!1}},Ls=e=>e.startsWith("./")||e.startsWith("../")||e.startsWith("/")||e.includes("Dockerfile"),Ns=(e,r,t,n="deploy",o)=>{const s=(Me(e,o)?.containers??[]).filter(a=>typeof a?.image=="string"&&Ls(a.image));if(s.length===0||t())return;const i=`${n} blocked: wrangler.jsonc declares ${String(s.length)} container(s) built from a local Dockerfile, but no Docker-compatible engine is available. Start Docker (or Colima), or point the container's \`image\` at a pre-built registry reference. Note: container images must target linux/amd64.`;return r.error(i),i},Ue=e=>b(A(e,Ve))?Ve:void 0,xs=(e,r,t="deploy",n)=>{for(const o of kr(e,"lunora",n).containers){const s=o.schedulingPolicy===void 0?o.image:void 0,a=[...s?.kind==="dockerfile"?[{field:"image",path:s.dockerfilePath}]:[],...Object.entries(o.schedulingPolicy==="durable_object"?o.images??{}:{}).flatMap(([c,l])=>l.kind==="dockerfile"?[{field:`images["${c}"]`,path:l.dockerfilePath}]:[])].find(c=>!b(A(e,c.path)));if(a!==void 0){const c=`${t} blocked: container "${o.exportName}" references a Dockerfile at "${a.path}" that does not exist. Create it or fix the \`${a.field}\` path in lunora/containers.ts.`;return r.error(c),c}if(s?.kind==="build"&&!b(A(e,s.buildDir))){const c=`${t} blocked: container "${o.exportName}" references a Railpack build directory "${s.buildDir}" that does not exist. Create it or fix the \`image.build\` path in lunora/containers.ts.`;return r.error(c),c}}},Fs=(e,r)=>r==="celld"?typeof e=="string"&&e!==Ke&&ln(e):r==="cloudflare"&&e===Ke,Ms=(e,r,t,n)=>(Me(e,t,n)?.d1_databases??[]).find(o=>Fs(o.database_id,r)),Us=e=>{if(!e.migrate||e.dryRun)return;if(e.migrateUrl===void 0){const t="--migrate requires --migrate-url <https://your-worker> — the deploy target URL is only known after wrangler runs, and this gate runs before it; refusing to default to localhost";return e.logger.error(t),t}if(e.migrateYes!==!0){const t="--migrate runs production data migrations after deploy. Re-run with --migrate-yes to confirm.";return e.logger.error(t),t}const r=e.migrateToken??process.env.LUNORA_ADMIN_TOKEN;if(r===void 0||r===""){const t="admin token required for --migrate — pass --migrate-token or set LUNORA_ADMIN_TOKEN";return e.logger.error(t),t}},Be=(e,r,t,n,o,s)=>{const i=Ms(e,n,o,s);if(i===void 0)return;const a=`${t} blocked: the "${i.binding??"<unnamed>"}" D1 binding has a placeholder database_id ("${i.database_id??""}")`,c=s===void 0?"Run `lunora doctor --fix` to create the database and write its id into wrangler.jsonc, or run `wrangler d1 create <name>` and paste the id in yourself.":`Run \`wrangler d1 create <name>\` and put the returned database_id in ${s}.`,l=n==="celld"?`${a}, which celld would use as the database's identity. Set database_id to the database celld should use, then ${t} again.`:`${a}. ${c}`;return r.error(l),l},Bs=(e,r,t="deploy",n)=>{const o=Me(e,n)?.vars;if(!o)return;const s=Object.entries(o).filter(([,a])=>typeof a=="string"&&Os(a)).map(([a])=>a).toSorted((a,c)=>a.localeCompare(c));if(s.length===0)return;const i=`${t} blocked: ${s.join(", ")} in wrangler.jsonc point at localhost. A deployed Worker can't reach a loopback address, so this silently breaks scheduled-job dispatch, reverse cross-shard relations, and auth callbacks. Set each to the deployed worker's public URL (or move it to a secret with \`wrangler secret put\`) before deploying.`;return r.error(i),i},Ws=["vite.config.ts","vite.config.mts","vite.config.cts","vite.config.js","vite.config.mjs","vite.config.cjs",...wt],Qr=/\.(?:[cm]?[jt]s|tsx|jsx)$/u,Zr=/\.(?:test|spec)\.[cm]?[jt]sx?$/u,et="allowUnauthenticatedShardAccess",Hs=/[\\/]/u,rt=new Set(["_generated","build","dist","node_modules"]),tt=(e,r)=>{const t=J(e,r).split(Hs);return Qr.test(r)&&!Zr.test(r)&&!we(t.join("/"))&&t.every(n=>n===".."||!n.startsWith(".")&&!rt.has(n))},gr=(e,r,t)=>{const{parsed:n}=I(r);if(n===void 0)return[];const{error:o,merged:s}=ue(n,t),{main:i}=o===void 0?s:n,a=typeof i=="string"?A(jr(r),i):void 0;return a!==void 0&&b(a)&&tt(e,a)?[a]:[]},We=e=>e===void 0?!1:e.getKind()===k.FalseKeyword?!0:S.isParenthesizedExpression(e)||S.isAsExpression(e)||S.isSatisfiesExpression(e)||S.isTypeAssertion(e)||S.isNonNullExpression(e)?We(e.getExpression()):!1,qs=e=>{try{return Te(e).isFile()}catch{return!1}},Gs=new Set(["ELOOP","ENOENT","ENOTDIR"]),ce=(e,r,t)=>{const n=r instanceof Error&&"code"in r&&typeof r.code=="string"?r.code:"";Gs.has(n)||t.push(e)},Vs=(e,r)=>{try{return Te(e)}catch(t){ce(e,t,r);return}},nt=(e,r)=>{try{return E(e,"utf8")}catch(t){ce(e,t,r);return}},ot=(e,r,t,n=new Set)=>{let o;try{o=vt(r)}catch(i){return ce(r,i,t),[]}if(n.has(o))return[];n.add(o);let s;try{s=K(r)}catch(i){return ce(r,i,t),[]}return s.flatMap(i=>{const a=A(r,i),c=Vs(a,t);if(c===void 0)return[];const l=J(e,a).split(x).join("/");return c.isDirectory()?rt.has(i)||i.startsWith(".")||we(`${l}/`)?[]:ot(e,a,t,n):c.isFile()&&Qr.test(i)&&!Zr.test(i)&&!we(l)?[a]:[]})},Ks=e=>{const r=S.isComputedPropertyName(e)?e.getExpression():e;return S.isStringLiteral(r)||S.isNoSubstitutionTemplateLiteral(r)?r.getLiteralValue():e.getText()},ve=[".ts",".tsx",".mts",".cts",".js",".jsx",".mjs",".cjs"],zs=/\.(?:[cm]?js|jsx)$/u,Js=(e,r)=>{if(!r.startsWith("."))return;const t=_(jr(e),r),n=t.replace(zs,"");return[t,...ve.map(s=>`${t}${s}`),...ve.map(s=>A(t,`index${s}`)),...ve.map(s=>`${n}${s}`)].find(s=>qs(s))},Ys=e=>{if(e.getExpression().getText()!=="require")return;const[r]=e.getArguments();return r!==void 0&&(S.isStringLiteral(r)||S.isNoSubstitutionTemplateLiteral(r))?r.getLiteralValue():void 0},Xs=e=>{const r=e.getModuleReference();if(!S.isExternalModuleReference(r))return;const t=r.getExpression();return S.isStringLiteral(t)?t.getLiteralValue():void 0},Qs=(e,r,t,n)=>{const o=new Set(e),s=[...e],i=[];for(let a=0;a<s.length;a+=1){const c=s[a]??"",l=nt(c,n);if(l===void 0)continue;const d=r.createSourceFile(J(t,c),l,{overwrite:!0}),u=[...d.getImportDeclarations().map(f=>f.getModuleSpecifierValue()),...d.getExportDeclarations().map(f=>f.getModuleSpecifierValue()),...d.getDescendantsOfKind(k.ImportEqualsDeclaration).map(f=>Xs(f)),...d.getDescendantsOfKind(k.CallExpression).map(f=>Ys(f))].filter(f=>f!==void 0);for(const f of u){const g=Js(c,f);g!==void 0&&tt(t,g)&&!o.has(g)&&(o.add(g),i.push(g),s.push(g))}}return i},Zs=e=>{const r=e.getArgumentExpression();return r!==void 0&&(S.isStringLiteral(r)||S.isNoSubstitutionTemplateLiteral(r))?r.getLiteralValue():void 0},ei=e=>S.isPropertyAccessExpression(e)?e.getName():S.isElementAccessExpression(e)?Zs(e):void 0,ri=new Set([k.AmpersandAmpersandEqualsToken,k.AmpersandEqualsToken,k.AsteriskAsteriskEqualsToken,k.AsteriskEqualsToken,k.BarBarEqualsToken,k.BarEqualsToken,k.CaretEqualsToken,k.EqualsToken,k.GreaterThanGreaterThanEqualsToken,k.GreaterThanGreaterThanGreaterThanEqualsToken,k.LessThanLessThanEqualsToken,k.MinusEqualsToken,k.PercentEqualsToken,k.PlusEqualsToken,k.QuestionQuestionEqualsToken,k.SlashEqualsToken]),ti=e=>{const r=e.getOperatorToken().getKind();return!ri.has(r)||ei(e.getLeft())!==et?!1:r!==k.EqualsToken||!We(e.getRight())},ni=(e,r,t)=>{const n=[],o=[...new Set([O(e),"src"])].filter(u=>b(A(e,u))),s=D(e),i=[...Ws.map(u=>A(e,u)).filter(u=>b(u)),...s===void 0?[]:gr(e,s,r),...t.flatMap(u=>gr(e,u,r))],a=new Pr({compilerOptions:{allowJs:!0},useInMemoryFileSystem:!0}),c=o.flatMap(u=>ot(A(e,u),A(e,u),n));return{sites:[...new Set([...c,...i,...Qs([...i,...c],a,e,n)])].flatMap(u=>{const f=nt(u,n);if(f===void 0)return[];const g=a.createSourceFile(J(e,u),f,{overwrite:!0}),h=[...g.getDescendantsOfKind(k.PropertyAssignment),...g.getDescendantsOfKind(k.ShorthandPropertyAssignment),...g.getDescendantsOfKind(k.GetAccessor),...g.getDescendantsOfKind(k.MethodDeclaration)].filter(m=>{const $=S.isPropertyAssignment(m)&&We(m.getInitializer());return Ks(m.getNameNode())===et&&!$}).map(m=>m.getStartLineNumber()),v=g.getDescendantsOfKind(k.BinaryExpression).filter(m=>ti(m)).map(m=>m.getStartLineNumber());return[...h,...v].map(m=>({file:J(e,u),line:m}))}),unreadable:n}},oi=(e,r,t,n,o,s)=>{if(t!=="deploy")return;const{sites:i,unreadable:a}=ni(e,o,s),c=jn({openShardAccess:i,schema:{tables:[]}},{lints:[ze]});if(c.length===0&&a.length===0)return;const l=[];if(c.length>0){const u=c.map(f=>`${String(f.metadata.file)}:${String(f.metadata.line)}`).join(", ");l.push(`${u} enable the open-shard option (any value but a literal false), so any caller may target any shard.`)}if(a.length>0&&l.push(`could not read ${a.join(", ")}, so the open-shard option cannot be ruled out there.`),n){r.warn(`deploying past the open-shard gate (--allow-open-shards): ${l.join(" ")}`);return}const d=`deploy blocked (open_shard_access): ${l.join(" ")} ${ze.remediation}`;return r.error(d),d},si=(e,r,t,n)=>{const o=oi(e,r.logger,t,r.allowOpenShards===!0,r.env,n);return o===void 0?void 0:{code:y.USAGE,error:o}},ii=(e,r,t,n)=>{const o=Be(e,r.logger,t,n,r.env);if(o!==void 0)return{code:y.USAGE,error:o};const s=Bs(e,r.logger,t,r.env);if(s!==void 0)return{code:y.USAGE,error:s};const i=xs(e,r.logger,t,r.discoveryCache);if(i!==void 0)return{code:y.USAGE,error:i};const a=Ns(e,r.logger,r.dockerAvailable??An,t,r.env);return a===void 0?void 0:{code:y.MISSING_DEPENDENCY,error:a}},ai=/https?:\/\/[^\s"'<>]+\.workers\.dev[^\s"'<>]*/u,ci=/https:\/\/[^\s"'<>]+/u,di=e=>{if(e===void 0)return;const r=ai.exec(e);return r?r[0]:ci.exec(e)?.[0]},hr=e=>e===void 0?"":` (--env ${e})`,li=({cwd:e,env:r,logger:t,now:n,url:o})=>{if(o!==void 0)try{const s=Ce(e);if(s!==void 0){if(s.workerUrl===o&&s.env===r)return;t.warn(`link: .lunora/project.json records ${s.workerUrl??"(no url)"}${hr(s.env)}, but this deploy published ${o}${hr(r)}. Keeping the recorded value — run \`lunora link --url ${o}${r===void 0?"":` --env ${r}`}\` to update it.`);return}const i=(n??(()=>new Date().toISOString()))();Dt(e,{env:r,linkedAt:i,workerName:Ie(e),workerUrl:o}),t.success(`link: recorded ${o} in .lunora/project.json (run \`lunora link\` to change)`)}catch{}},ui=async(e,r)=>{const t=new Pr({skipAddingFilesFromTsConfig:!0}),n=A(r,"lunora");let o;try{o=kt(t,n)}catch(s){const i=s instanceof Error?s.message:String(s);return e.logger.warn(`--migrate: could not discover migrations (${i}); skipping`),0}if(o.length===0)return e.logger.info("--migrate: no data migrations declared in lunora/"),0;e.logger.info(`--migrate: running ${String(o.length)} migration(s) against deployed worker`);for(const s of o){e.logger.info(`--migrate: up "${s.id}" (table "${s.table}")`);const i={cwd:r,fetchImpl:e.fetchImpl,id:s.id,logger:e.logger,prod:!0,subcommand:"up",token:e.migrateToken,url:e.migrateUrl,yes:e.migrateYes===!0},a=await Dn(i);if(a.code!==0)return e.logger.error(`--migrate: migration "${s.id}" failed — see output above`),a.code;e.logger.success(`--migrate: "${s.id}" applied`)}return 0},fi=async(e,r)=>{const{info:t}=Pe(r,"lunora",e.discoveryCache),n=t?.vectorMetadata??[];if(n.length===0)return;const o=[];for(const a of n){const c=un(a.kind);if(c===void 0){e.logger.warn(`vector index "${a.index}" declares metadata "${a.property}", whose column type cannot be filtered on in Vectorize — it is stored with each vector but no filter will match it.`);continue}o.push({index:a.index,property:a.property,type:c})}if(o.length===0)return;const i=(await fn({cwd:r,entries:o,exec:je(F(r),"wrangler",[]),logger:e.logger,spawner:e.spawner??q})).filter(a=>a.status!=="failed").length;i>0&&e.logger.success(`vectorize metadata indexes ready: ${String(i)}/${String(o.length)}`)},mr=5,gi=2e3,hi=async(e,r,t)=>{if(e.healthCheck!==!0)return;const n=t??De({cwd:r,env:e.env});if(n===void 0){const s="--health-check: the deploy succeeded, but no URL to probe could be resolved — wrangler's output carried none and this checkout has no link for this environment. Run `lunora link --url <https://your-worker>` and re-deploy, or drop --health-check.";return e.logger.error(s),{error:s,ok:!1,url:""}}const o=await Rn({attempts:mr,baseUrl:n,delayMs:gi,fetchImpl:e.healthFetch,paths:[Tn,Cn],sleep:e.healthSleep});return o.error===void 0?(e.logger.success(`health check ok (${o.url})`),{ok:!0,url:o.url}):(e.logger.error(`--health-check: the deploy succeeded, but the new version did not answer after ${String(mr)} attempt(s) — ${o.error}`),{error:o.error,ok:!1,url:o.url})},mi=async(e,r,t,n,o,s)=>{e.isolatedPreview!==!0&&await fi(e,r);const i=e.migrate?await ui(e,r):0;return i===0&&o?.(),{code:i,descriptor:t,mintedSecretsFile:s,validation:n}},pi=async({cwd:e,descriptor:r,mintedSecretsFile:t,options:n,reblessSchemaBaseline:o,recordArchitecture:s,stdout:i,validation:a})=>{const c={deployedAt:new Date().toISOString(),dryRun:n.dryRun===!0,env:n.env,url:n.dryRun===!0?void 0:di(i),workerName:Ie(e)};if(n.dryRun===!0)return n.healthCheck===!0&&n.logger.warn("--health-check skipped: a dry run publishes nothing"),{code:0,deployment:c,descriptor:r,mintedSecretsFile:t,validation:a};n.temporary!==!0&&n.isolatedPreview!==!0&&(s?.(),li({cwd:e,env:n.env,logger:n.logger,url:c.url}));const l=await hi(n,e,c.url);if(l?.error!==void 0)return{code:y.UNAVAILABLE,deployment:c,descriptor:r,healthCheck:l,mintedSecretsFile:t,validation:a};const d=n.isolatedPreview===!0?void 0:o;return{...await mi(n,e,r,a,d,t),deployment:c,healthCheck:l}},vi=(e,r,t)=>{const n=A(e,N);if(!b(n))return;let o;try{o=br(E(n,"utf8")).length}catch{return}if(o===0)return;const s=t.toolchain?.secretPut!==void 0;r.warn(s?`Note: \`lunora deploy\` does not push secrets. ${N} has ${String(o)} key(s); if you changed them, run \`lunora env push --yes\` to update the deployed secrets.`:`Note: ${t.name} deploys read no secrets. ${N} has ${String(o)} key(s) that only the dev server sees; a key the app reads from env must be a \`vars\` entry in the deployed config, one it loads from a secret manager at runtime need not.`)},st=async(e,r)=>{let t=[];try{t=Ot(await $r({cache:r,projectRoot:e,schemaDir:O(e)}))}catch{}const n=[...new Set(Lt(t).map(s=>s.key))];let o=[];try{const s=A(e,N);b(s)&&(o=br(E(s,"utf8")).map(i=>i.key).filter(i=>Rr(i)))}catch{}return{fromLocal:o.filter(s=>!n.includes(s)),fromPackages:n,mintableKeys:Nt(t)}},yi=async(e,r,t,n)=>{const{logger:o}=r,s=r.spawner??q,i=F(e),a=r.env===void 0?"":` --env ${r.env}`,c=n.toolchain?.secretPut;if(c===void 0)return o.error(`deploy target "${n.id}" has no secret store; cannot push secrets`),{minted:[],ok:!1};const l=t.map(f=>({key:f,value:xt()})),d=n.toolchain?.secretBulk;if(d!==void 0&&l.length>=2){const f=z(i,d({environment:r.env,temporary:r.temporary})),g=await s({args:f.args,command:f.command,cwd:e,input:JSON.stringify(Object.fromEntries(l.map(h=>[h.key,h.value])))});if(g.code===0){for(const{key:h}of l)o.success(`generated + pushed ${h}`);return{minted:l,ok:!0}}o.warn(`bulk secret push failed (exit ${String(g.code)}); retrying one key at a time`)}const u=[];for(const{key:f,value:g}of l){const h=c({environment:r.env,key:f,temporary:r.temporary}),v=z(i,h),m=await s({args:v.args,command:v.command,cwd:e,input:g});if(m.code!==0)return o.error(`failed to push secret ${f} (exit ${String(m.code)}); set it manually with \`wrangler secret put ${f}${a}\`.`),{minted:u,ok:!1};u.push({key:f,value:g}),o.success(`generated + pushed ${f}`)}return{minted:u,ok:!0}},wi=/^[\w-]+$/u,pr=[".dev.vars",".dev.vars.*","!.dev.vars.example"],ki=(e,r)=>{try{const t=Bt(e,pr);t.length>0&&r.info(`.gitignore: added ${t.join(", ")} so the recorded secret cannot be committed`)}catch(t){r.warn(`could not update .gitignore (${t instanceof Error?t.message:String(t)}) — add \`${pr.join("` and `")}\` by hand before committing.`)}},bi=(e,r,t)=>{if(t.length===0)return;const n=t.map(a=>a.key).join(", ");if(r.env!==void 0&&!wi.test(r.env)){r.logger.warn(`${n}: minted and pushed for --env ${r.env}, but "${r.env}" isn't a safe filename fragment — the value could not be recorded. Capture it manually if you need it again.`);return}const o=r.env===void 0?N:`${N}.${r.env}`;ki(e,r.logger);const s=A(e,o);let i=b(s)?E(s,"utf8"):"";for(const a of t)i=Ft(i,a.key,a.value);return Mt(s,i),r.logger.success(r.env===void 0?`${n}: value(s) recorded in ${o}`:`${n}: value(s) recorded in ${o} (kept separate from ${N} so \`lunora dev\` and a plain \`lunora env push\` don't inherit the --env ${r.env} value)`),o},$i=/^<[^>]+>$/u,Si=async(e,r,t)=>{let n;try{n=t.projectConfig?.(e,"deploy").vars??{}}catch(d){return{error:d instanceof Error?d.message:String(d)}}const o=d=>{const u=n[d];if(typeof u!="string")return!0;const f=u.trim();return f===""||$i.test(f)?!0:Rr(d)&&Ut(u)},s=d=>Object.hasOwn(n,d)?`${d} (empty or a placeholder)`:d,{fromLocal:i,fromPackages:a}=await st(e,r.discoveryCache),c=i.filter(d=>o(d));c.length>0&&r.logger.warn(`${N} key(s) not set in the wrangler vars: ${c.map(d=>s(d)).join(", ")}. ${t.name} has no secret store: a key the app reads from env must be a \`vars\` entry; one it loads from a secret manager at runtime need not.`);const l=a.filter(d=>o(d));return l.length===0?{}:{error:`missing required secret(s) in the wrangler vars: ${l.map(d=>s(d)).join(", ")}. ${t.name} has no secret store and Lunora reads these from env, so each must be a real \`vars\` value in the config the deploy ships — write it there from your secret manager at deploy time rather than committing it. Add them, then re-deploy.`}},Ei=async(e,r,t,n)=>{if(r.dryRun===!0||r.isolatedPreview===!0)return{};if(n.toolchain?.secretList===void 0)return Si(e,r,n);const{logger:o}=r,s=r.env===void 0?"":` --env ${r.env}`;let i;try{i=await(r.secretLister??Ln)({cwd:e,env:r.env,target:n.id,temporary:r.temporary})}catch{return{}}if(!i.ok)return{};const a=new Set(i.names),{fromLocal:c,fromPackages:l,mintableKeys:d}=await st(e,r.discoveryCache),u=[...l,...c].filter(h=>!a.has(h));if(u.length===0)return{};if(!t)return{error:`missing required secret(s) on the deploy target: ${u.join(", ")}. Set them with \`wrangler secret put <KEY>${s}\` (or \`lunora env generate --set\` then \`lunora env push --yes${s}\`), then re-deploy.`};for(const h of u.filter(v=>!d.has(v)))o.warn(`required secret ${h} is not set on the target — set it with: wrangler secret put ${h}${s}`);const f=u.filter(h=>d.has(h));if(f.length===0)return{};if(await(r.secretConfirm??On())(`${String(f.length)} required secret(s) not set on the target (${f.join(", ")}). Generate strong values and push them now?`)){const{minted:h,ok:v}=await yi(e,r,f,n),m=bi(e,r,h);return v?{mintedSecretsFile:m}:{error:"failed to push required secret(s) — set them manually and re-deploy",mintedSecretsFile:m}}return o.warn(`${String(f.length)} required secret(s) not set on the target: ${f.join(", ")}. Generate + push with \`lunora env generate --set\` then \`lunora env push --yes${s}\`.`),{}},it=e=>e.format==="json"?!1:e.interactive!==void 0?e.interactive:process.stdout.isTTY&&!process.env.CI,Ai=async(e,r)=>{if(r.dryRun===!0)return;const t=kr(e,"lunora",r.discoveryCache).containers.flatMap(o=>o.schedulingPolicy===void 0&&o.image.kind==="build"?[{buildDir:o.image.buildDir,exportName:o.exportName}]:[]);if(t.length===0)return;const n=await fs({cwd:e,logger:r.logger,railpackAvailable:r.railpackAvailable,spawner:r.spawner,targets:t});return n.code===0?void 0:n.error??"railpack build failed"},Ri=(e,r,t)=>{if(t!==void 0)try{const n=Zt(e,t);n.changed&&r.success(`synced ${String(t.length)} cron trigger(s) → ${n.wranglerPath??"wrangler.jsonc"}`);for(const s of n.warnings)r.warn(s);const o=en(n.preserved);o!==void 0&&r.info(o)}catch(n){const o=n instanceof Error?n.message:String(n);r.warn(`cron trigger sync skipped: ${o}`)}},Ti=async(e,r,t,n,o,s,i=!1)=>{try{le(n);const a=await $r({cache:s,projectRoot:e,schemaDir:O(e),skipCompanion:i}),c=Xt(e,a,{environment:o,target:n}),l=c.wranglerPath??"wrangler.jsonc";if(c.added.length>0&&r.success(`provisioned bindings: ${c.added.join(", ")} → ${l}`),c.updated.length>0&&r.success(`updated bindings: ${c.updated.join(", ")} → ${l}`),c.devConfig!==void 0){const{added:d,path:u,updated:f}=c.devConfig;r.success(`dev sidecar bindings: ${[...d,...f].join(", ")} → ${u}`)}for(const d of c.warnings)r.warn(d)}catch(a){const c=a instanceof Error?a.message:String(a);r.warn(`binding inference skipped: ${c}`)}try{const a=Qt(e);a.changed&&r.success(`bumped compatibility_date to ${a.date??"unknown"} (Workers Cache enabled) → ${a.wranglerPath??"wrangler.jsonc"}`)}catch(a){const c=a instanceof Error?a.message:String(a);r.warn(`compatibility date sync skipped: ${c}`)}Ri(e,r,t)},Ci=async(e,r,t,n,o,s,i,a,c,l)=>{let d;r?(d=new rn({name:"dots"}),d.start("running codegen")):t.info("running codegen");try{const u=$t({apiSpec:n,dryRun:!l,lazyFunctions:o,projectRoot:e,target:s,...Yt(e)});d?.succeed("codegen complete"),d||t.success("codegen complete");const f=an(u.platformDiagnostics,t);if(f.errors.length>0)return{error:f.errors.join("; ")};const{errorAdvisories:g,names:h,shouldBlock:v}=cn(u.advisories,c);if(v){const $=`${g.length.toString()} ERROR-level ${g.length===1?"advisory":"advisories"} (${h.join(", ")}). Pass --no-strict-advisories to downgrade this to a warning and continue.`;return t.error($),{error:$}}const m=l?await Vt({cwd:e,logger:t,spawner:i,stdoutToStderr:a}):void 0;return m?.error!==void 0?{error:m.error}:{result:u}}catch(u){d?.failed("codegen failed");const f=u instanceof Error?u.message:String(u);return t.error(`codegen failed: ${f}`),{error:`codegen failed: ${f}`}}},vr="--preview is not supported: Cloudflare gives a Worker that implements a Durable Object no version preview URL, and a version shares production's Durable Objects, D1 and R2. Deploy an isolated copy with `lunora preview deploy <name>` (its own Worker, Durable Object namespace, D1, R2 and KV; `lunora preview delete <name>` removes it), or validate without publishing with `--dry-run`.",Pi=(e,r)=>({dryRun:r.dryRun,entry:Ue(e),environment:r.env,outDir:r.outDir,secretsFile:r.secretsFile,outFile:r.outFile,preview:r.preview,temporary:r.temporary}),at=(e,r,t)=>{const{toolchain:n}=t;if(n===void 0)throw new Error(`deploy target "${t.id}" has no command-line toolchain`);const o=Pi(e,r);return Sr(t,e,"deploy",s=>n.deploy({...o,configPath:s}))},_i=(e,r,t)=>{const n=at(e,r,t),o=Ue(e);return o!==void 0&&r.logger.info(`class-B composition: deploying ${o} (overrides wrangler main)`),r.temporary&&r.logger.info("temporary account: deploying to a short-lived Cloudflare account (~60min); wrangler will print a claim URL"),r.dryRun&&r.logger.info("dry run: validating + bundling without publishing"),r.outDir!==void 0&&r.logger.info(`build artifact: emitting bundle to ${r.outDir}`),n.commit(),n.projection!==void 0&&n.projection.dropped.length>0&&r.logger.warn(`${t.name} ignores these wrangler keys, so they were left out of ${n.projection.configPath}: ${n.projection.dropped.join(", ")}`),n.command},P={problems:[],wranglerPath:void 0},ye=(e,r)=>({code:y.USAGE,descriptor:void 0,error:e,validation:P,...r}),Ii=(e,r)=>{for(const t of e.report?.warnings??[])r.warn(`wrangler.jsonc: ${t}`);if(e.problems.length===0)return!1;r.error("wrangler.jsonc validation failed:");for(const t of e.problems)r.error(` - ${t}`);return!0},ji=(e,r,t)=>{const n=r.format==="json",o=r.dryRun!==!0,s=_i(e,r,t),i=z(F(e),s);return{args:i.args,captureStdout:o&&!n,captureStdoutSilently:o&&n,command:i.command,cwd:e,stdoutToStderr:n&&!o}},he=e=>{if(e.skipServices===!0)return"--skip-services";if(e.isolatedPreview===!0)return"a preview never deploys services: they are production's Workers";if(e.temporary===!0)return"--temporary deploys the app to a throwaway account"},Di=(e,r)=>he(r)===void 0?bt(e).services.map(t=>t.wranglerPath):[],ct=(e,r,t,n,o)=>({cwd:e,environment:r.env,frameworkBuild:n,lazyFunctions:de(r.lazyFunctions),logger:r.logger,outputJson:r.format==="json"||r.clientBuildOutputJson===!0,spawner:o,strictAdvisories:_e(r),target:t.id}),Oi=(e,r,t)=>{const{toolchain:n}=t,o=n===void 0?`target "${t.id}" has no deploy command`:he(r);if(o!==void 0||n===void 0){const s=e.map(i=>i.worker).join(", ");r.logger.warn(`services not deployed (${o??""}): ${s} — deploy them yourself if the app calls anything new`);return}return n},dt=(e,r)=>{for(const t of e){const n=Fe(r(R(t.wranglerPath)));if(n!==void 0)return`service ${t.name} (${t.worker}): ${n}`}},Li=(e,r,t)=>{const{error:n,services:o}=yr(e,O(e));if(n!==void 0||o.length===0)return;const s=le(t);if(s.toolchain===void 0||he(r)!==void 0)return;const i=r.skipCodegen===!0?!1:r.frameworkBuild,a=[...new Map(o.map(c=>[c.wranglerPath,c])).values()];return dt(a,c=>ct(c,r,s,i,q))},lt=(e,r,t)=>{const n=e.cwd??process.cwd(),o=vn(n,e.target);if(o.target===void 0){const a=o.error??"unknown deploy target";return e.logger.error(a),{kind:"refused",error:a,validation:P}}if(e.preview===!0)return e.logger.error(vr),{kind:"refused",code:y.USAGE,error:vr,validation:P};try{at(n,e,le(o.target))}catch(a){if(!(t&&a instanceof Wt&&xe(n,e.skipCodegen===!0?!1:e.frameworkBuild,e.env))){const c=a instanceof Error?a.message:String(a);return e.logger.error(c),{kind:"refused",error:c,validation:P}}}const s=e.openShardGatePassed===!0?void 0:si(n,e,r,Di(n,e));if(s!==void 0)return{kind:"refused",code:s.code,error:s.error,validation:P};const i=Li(n,e,o.target);return i!==void 0?(e.logger.error(i),{kind:"refused",code:y.USAGE,error:i,validation:P}):{kind:"ok",target:o.target}},Ni=(e,r)=>e.apiSpec??(e.clientBuildRan===!0?wr(r):void 0),xi=async(e,r,t)=>{if(r!=="celld")return;const n=await(e.celldToolchain??Gt)({dryRun:t==="prepare"||e.dryRun===!0,env:process.env,purpose:"deploy"});for(const o of n.warnings)e.logger.warn(o);return n.errors.length>0?n.errors.join("; "):void 0},ut=(e,r)=>{const t=De({cwd:e,env:r.env,url:r.migrateUrl}),n=t===void 0?void 0:Ts(e,r.env,t);if(n!==void 0)return{reason:n};const o=t===void 0?void 0:on(t,r.logger,e),s=o===void 0?void 0:tn({cwd:e,token:r.migrateToken,url:o}).token;return{target:o===void 0||!s?void 0:{baseUrl:o,fetchImpl:r.fetchImpl??sn,token:s}}},Fi=(e,r,t)=>async n=>{const o=ut(e,r);return"reason"in o?{kind:"inconclusive",reason:o.reason}:In({changes:n,tables:t.schemaTables,target:o.target})},Mi=(e,r)=>async t=>{const n=ut(e,r);return"reason"in n?n.reason:_n({changes:t,now:Date.now(),target:n.target})},Ui=(e,r,t,n)=>e==="deploy"?{checkStoredRows:Fi(r,t,n),countExpiringRows:Mi(r,t)}:{},Bi=async(e,r)=>{const t=e.cwd??process.cwd(),n=it(e),o=_e(e),s={problems:[],wranglerPath:void 0},i=lt(e,r,!1);if(i.kind==="refused")return{code:i.code,error:i.error,validation:i.validation};const{target:a}=i,c=await xi(e,a,r);if(c!==void 0)return e.logger.error(c),{code:y.MISSING_DEPENDENCY,error:c,target:a,validation:s};let l;if(!e.skipCodegen){const v=await Ci(t,n,e.logger,Ni(e,t),de(e.lazyFunctions),a,e.spawner,e.format==="json",o,e.clientBuildRan!==!0);if(v.error!==void 0)return{error:v.error,target:a,validation:P};l=v.result,e.discoveryCache?.clear()}let d;if(l!==void 0){const v=await Pn({allowDrift:e.allowSchemaDrift===!0,...Ui(r,t,e,l),codegen:l,command:r,logger:e.logger});if(v.blocked)return{error:`schema drift gate blocked ${r}`,schemaDrift:{blocked:!0,reason:v.reason},target:a,validation:s};d=v.rebless}const u=l&&Wn({current:l.architecture,cwd:t,environment:e.env,logger:e.logger});await Ti(t,e.logger,l?.cronTriggers,a,e.env,e.discoveryCache,he(e)!==void 0);const f=await H(t,e,r,a,"check");if(f!==void 0)return{error:f,target:a,validation:P};const g=ii(t,e,r,a);if(g!==void 0)return{code:g.code,error:g.error,target:a,validation:P};const h=Jt({environment:e.env,projectRoot:t});return Ii(h,e.logger)?{error:"wrangler validation failed",target:a,validation:h}:{codegen:l,reblessSchemaBaseline:d,recordArchitecture:u,target:a,validation:h}},Wi=e=>{oe(p(e,".wrangler","deploy","config.json"),{force:!0})},Hi=async(e,r,t)=>{const n=Fe(e);if(n!==void 0)return{built:!1,error:n};const o=xe(e.cwd,r,t);return o&&Wi(e.cwd),{built:o,error:await Xr(e)}},qi=(e,r,t)=>r||t===!1?Oe(e):void 0,Gi=async(e,r,t)=>{const n=R(e);return Be(n,r.logger,"deploy",t.id,r.env,e)??H(n,r,"deploy",t.id,"check",e)},Vi=(e,r,t)=>e.map((n,o)=>{const s=r[o]===!0,i=qi(n.wranglerPath,s,t);return{built:s,config:i??n.wranglerPath,resolved:i!==void 0,service:n}}),Ki=async(e,r,t)=>{const{config:n,resolved:o,service:s}=e;if(!b(n))return;const i=R(s.wranglerPath),a=o?void 0:r.env,c=Be(i,r.logger,"deploy",t.id,a,n);if(c!==void 0)return{code:y.USAGE,error:`service ${s.name} (${s.worker}): ${c}`};if(o&&!e.built&&r.env!==void 0&&So(i,void 0,n)){const d=`service ${s.name} (${s.worker}): its prebuilt config ${n} still has a Hyperdrive placeholder, and it was not built for --env ${r.env}, so it cannot be resolved here. Build it with CLOUDFLARE_ENV=${r.env}.`;return r.logger.error(d),{code:y.USAGE,error:d}}const l=await H(i,{...r,clientBuildEnvironment:void 0,env:a},"deploy",t.id,"check",n);return l===void 0?void 0:{code:y.USAGE,error:`service ${s.name} (${s.worker}): ${l}`}},zi=async(e,r,t)=>{for(const n of e){const o=await Ki(n,r,t);if(o!==void 0)return o}},Ji=async(e,r,t)=>{const{config:n,resolved:o,service:s}=e;if(!b(n))return;const i=R(s.wranglerPath),a=o?void 0:r.env,c=e.built?r.env:void 0,l=await H(i,{...r,clientBuildEnvironment:c,env:a},"deploy",t.id,"create",n);if(l!==void 0)return{code:y.FAILURE,error:`service ${s.name} (${s.worker}): ${l}`};if(n===s.wranglerPath)return;const d=await H(i,{...r,clientBuildEnvironment:void 0},"deploy",t.id,"create",s.wranglerPath);return d===void 0?void 0:{code:y.FAILURE,error:`service ${s.name} (${s.worker}): ${d}`}},Yi=async(e,r,t,n,o)=>{const s=[];for(const i of e){const{built:a,error:c}=await Hi(r(R(i.wranglerPath)),t,n);if(c!==void 0){const l=`service ${i.name} (${i.worker}): ${c}`;return o.error(l),{builds:s,failure:{code:y.FAILURE,error:l}}}s.push(a)}return{builds:s}},Xi=async(e,r)=>{const{driver:t,manager:n,options:o,spawner:s,toolchain:i}=r,{config:a,service:c}=e,l=R(c.wranglerPath);let d;try{d=Sr(t,l,"deploy",g=>i.deploy({configPath:g??a,dryRun:o.dryRun===!0,environment:o.env}))}catch(g){return{code:y.FAILURE,error:`service ${c.name} (${c.worker}): ${g instanceof Error?g.message:String(g)}`}}d.commit();const u=z(n,d.command);o.logger.info(`deploying service ${c.name} (${c.worker}) via ${u.command} ${u.args.join(" ")}`);const f=await s({args:u.args,command:u.command,cwd:l,stdoutToStderr:o.format==="json"});if(f.code!==0)return{code:y.FAILURE,error:`service ${c.name} (${c.worker}): ${u.command} ${u.args.join(" ")} exited ${String(f.code)} — stopping before the app`}},Qi=async(e,r,t,n)=>{const{error:o,services:s}=yr(e,O(e));if(o!==void 0)return{code:y.FAILURE,error:o};if(s.length===0)return;const i=Oi(s,r,t);if(i===void 0)return;const a=F(e),c=[...new Map(s.map($=>[$.wranglerPath,$])).values()],l=r.skipCodegen===!0?!1:r.frameworkBuild,d=$=>ct($,r,t,l,n),u=dt(c,d);if(u!==void 0)return r.logger.error(u),{code:y.USAGE,error:u};for(const $ of c){const C=await Gi($.wranglerPath,r,t);if(C!==void 0)return{code:y.USAGE,error:`service ${$.name} (${$.worker}): ${C}`}}const{builds:f,failure:g}=await Yi(c,d,l,r.env,r.logger);if(g!==void 0)return g;const h=Vi(c,f,l),v=await zi(h,r,t);if(v!==void 0)return v;const m={driver:t,manager:a,options:r,spawner:n,toolchain:i};for(const $ of h){const C=await Ji($,r,t);if(C!==void 0)return C;const T=await Xi($,m);if(T!==void 0)return T}},Zi=async(e,r,t,n)=>{if(r!=="cloudflare")return;const o=e===void 0?void 0:I(e).parsed,s=(t===void 0?void 0:o?.env?.[t]?.account_id)??o?.account_id,i=await hn({accountId:s});(i.verdict==="near"||i.verdict==="full")&&n.warn(i.fix===void 0?i.message:`${i.message} ${i.fix}`)},ea=e=>e.dryRun!==!0&&e.temporary!==!0,ra=e=>e.temporary===!0?"temporary":e.dryRun===!0?"preview":"defer",ta=e=>{const r=e.fix===void 0?e.message:`${e.message} ${e.fix}`;return e.verdict==="unchecked"?`${r} The deploy stops until it can be checked, as a namespace made by the first repo is unrestricted.`:r},na=(e,r,t)=>{const n=[];for(const o of e)if(o.verdict==="ok")o.changesAccount===!0&&r.info(o.message);else if(o.verdict==="unchecked"&&t==="preview")r.warn(`dry run: ${o.message}${o.fix===void 0?"":` ${o.fix}`} A real deploy stops here.`);else{const s=ta(o);r.error(s),n.push(s)}return n},ft=async(e,r,t,n,o,s,i)=>{const a=t==="cloudflare"&&r!==void 0?I(r).parsed:void 0;if(a===void 0)return;const c=(ue(a,n).merged.artifacts??[]).flatMap(h=>typeof h?.binding=="string"&&typeof h.namespace=="string"?[{binding:h.binding,namespace:h.namespace}]:[]),l=c.length===0?void 0:Pe(e,O(e),s).info?.jurisdiction;if(l===void 0)return;const d=[...new Map(c.map(h=>[h.namespace,h])).values()];if(i==="temporary"){o.warn(`--temporary deploys to a throwaway account, so the jurisdiction ("${l}") of Artifacts namespace ${d.map(h=>`"${h.namespace}"`).join(", ")} was not checked: it is created there on first use, in no jurisdiction.`);return}const u=(n===void 0?void 0:a.env?.[n]?.account_id)??a.account_id,f=await Promise.all(d.map(async h=>gn({...h,accountId:u,create:i==="create",jurisdiction:l,missingOk:i==="defer"||i==="preview"}))),g=na(f,o,i);return g.length===0?void 0:g.join(`
|
|
64
|
+
`)},oa=async(e,r,t,n,o,s)=>{const i=ea(r)?await ft(e,s,o,r.env,r.logger,r.discoveryCache,"create"):void 0;if(i!==void 0)return{code:y.FAILURE,error:i};const a=await H(e,r,"deploy",o);return a!==void 0?{code:y.FAILURE,error:a}:Qi(e,r,t,n)},sa=async(e,r,t,n)=>{if(!(r.smokeLoad===!1||t.toolchain===void 0))return ls({composedEntry:Ue(e),configPath:n,cwd:e,environment:r.env,launcher:r.smokeLauncher,logger:r.logger,toolchain:t.toolchain})},ia=async e=>{const r={...e,discoveryCache:e.discoveryCache??qt()},t=r.cwd??process.cwd(),n=it(r),o=await Bi(r,r.commandName??"deploy");if(o.error!==void 0){if(o.validation.problems.length>0)return{code:y.USAGE,descriptor:void 0,error:o.error,validation:o.validation};const m=o.schemaDrift===void 0?void 0:{schemaDrift:o.schemaDrift};return ye(o.error,{...m,...o.code===void 0?{}:{code:o.code}})}const{validation:s}=o,i=le(o.target),a=Us(r)??await ft(t,s.wranglerPath,o.target,r.env,r.logger,r.discoveryCache,ra(r))??await sa(t,r,i,s.wranglerPath);if(a!==void 0)return ye(a);const c=i.toolchain?.prebuildsContainerImages===!0?await Ai(t,r):void 0;if(c!==void 0)return ye(c);vi(t,r.logger,i),zt(t,m=>{r.logger.warn(m)}),await Zi(s.wranglerPath,o.target,r.env,r.logger);const{error:l,mintedSecretsFile:d}=await Ei(t,r,n,i);if(l!==void 0)return r.logger.error(l),{code:y.USAGE,descriptor:void 0,error:l,mintedSecretsFile:d,validation:s};const u=r.spawner??q,f=await oa(t,r,i,u,o.target,s.wranglerPath);if(f!==void 0)return{code:f.code,descriptor:void 0,error:f.error,mintedSecretsFile:d,validation:s};const g=ji(t,r,i);r.logger.info(`deploying via ${g.command} ${g.args.join(" ")}`);const h=await u(g);return g.captureStdoutSilently===!0&&h.stdout!==void 0&&h.stdout!==""&&process.stderr.write(h.stdout),h.code!==0?{code:h.code,descriptor:g,mintedSecretsFile:d,validation:s}:{...await pi({...o,cwd:t,descriptor:g,mintedSecretsFile:d,options:r,stdout:h.stdout}),logsAvailable:i.toolchain?.tail!==void 0}},aa=async e=>{const{logger:r}=e,t=e.cwd??process.cwd(),n={apiSpec:e.apiSpec,cwd:t,environment:e.env,bundleMetaPath:e.bundleMetaPath,frameworkBuild:e.skipCodegen===!0?!1:e.frameworkBuild,strictAdvisories:_e(e),lazyFunctions:de(e.lazyFunctions),logger:r,outputJson:e.format==="json"||e.clientBuildOutputJson===!0,spawner:e.spawner,target:e.target},o=Fe(n);if(o!==void 0)return r.error(o),{code:y.USAGE,descriptor:void 0,error:o,validation:P};Ht(t);const s=await Xr(n);if(s!==void 0)return r.error(s),{code:y.FAILURE,descriptor:void 0,error:s,validation:P}},ca=(e,r)=>{const t=r.skipCodegen===!0?!1:r.frameworkBuild;return xe(e,t,r.env)?r.env:void 0},da=async e=>{const{logger:r}=e,t=e.cwd??process.cwd(),n=lt(e,e.commandName??"deploy",!0);if(n.kind==="refused")return{code:n.code??y.USAGE,descriptor:void 0,error:n.error,validation:n.validation};const o=await aa(e);if(o!==void 0)return o;const s=e.dryRun===!0?Kr(t):void 0;let i;try{if(i=await ia({...e,clientBuildEnvironment:ca(t,e),clientBuildRan:js(t,e.skipCodegen===!0?!1:e.frameworkBuild,e.env),logger:r,openShardGatePassed:!0}),i.code===0&&e.emitBindings!==void 0){const{error:a}=mn({destination:e.emitBindings,logger:r,projectRoot:e.cwd??process.cwd()});a!==void 0&&(r.error(a),i={...i,code:y.USAGE})}}finally{s?.()}return e.format==="json"||i.code===0&&e.dryRun!==!0&&e.isolatedPreview!==!0&&Hn({cwd:e.cwd??process.cwd(),env:e.env,logger:e.logger,logsAvailable:i.logsAvailable===!0,mintedSecretsFile:i.mintedSecretsFile,url:i.deployment?.url}),i},Ga=pn(async({cwd:e,format:r,logger:t,options:n})=>{const o=us(e,n.root),s=await da({allowOpenShards:n.allowOpenShards===!0,allowSchemaDrift:n.allowSchemaDrift===!0,apiSpec:dn(n.apiSpec),frameworkBuild:n.prebuilt===!0?!1:n.frameworkBuild,lazyFunctions:de(n.lazyFunctions),cwd:o,dryRun:n.dryRun===!0,env:n.env,format:r,healthCheck:n.healthCheck===!0,logger:t,migrate:n.migrate===!0,migrateToken:n.migrateToken,migrateUrl:De({cwd:o,env:n.env,url:n.migrateUrl}),migrateYes:n.migrateYes===!0,preview:n.preview===!0,skipCodegen:n.prebuilt===!0,skipServices:n.skipServices===!0,smokeLoad:n.smokeLoad,strictAdvisories:n.strictAdvisories,target:n.target,temporary:n.temporary===!0});return{code:s.code,data:{deployment:s.deployment,healthCheck:s.healthCheck,mintedSecretsFile:s.mintedSecretsFile,schemaDrift:s.schemaDrift,validation:s.validation},error:s.error}});export{Bi as a,Ti as b,Yn as c,Kr as d,st as e,Ga as f,us as p,da as r,sa as s,Cs as v};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import{randomUUID as V,createHash as gr,createHmac as yr}from"node:crypto";import{mkdirSync as $r,writeFileSync as ne,linkSync as xe,rmSync as F,statSync as Ce,unlinkSync as kr,readFileSync as L,existsSync as Q,mkdtempSync as Er}from"node:fs";import{tmpdir as br}from"node:os";import{join as ee,dirname as _r}from"node:path";import{SCHEMA_SNAPSHOT_FILENAME as Sr,readServiceBindings as Rr}from"@lunora/codegen";import{generateSecretValue as Ir}from"@lunora/config";import{readManifest as Ke,recordManifestKey as Or,findWranglerFile as He,readWranglerJsonc as $e,applyModify as Tr}from"@lunora/config/cloudflare";import{n as pe,r as Ar,c as Cr}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{d as jr}from"../packem_shared/command-BpMv2n0o.mjs";import{r as Ur}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as w}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{c as oe,d as Nr,b as Lr,e as Dr,r as Pr}from"./handler.mjs";import{runSeedCommand as Fr}from"./handler14.mjs";const Be=r=>{if(!Number.isInteger(r)||r<=0)return!1;try{return process.kill(r,0),!0}catch(e){return e.code==="EPERM"}},ve=r=>Number(L(r,"utf8")),je=r=>{try{return ve(r)}catch{return}},Mr=(r,e)=>{const t=`${r}.retire-${String(e)}`;try{xe(r,t)}catch{return!1}try{return ve(t)!==e||Be(ve(t))?!1:(Ce(r).ino===Ce(t).ino&&kr(r),!0)}finally{F(t,{force:!0})}},Wr=(r,e={})=>{const t=ee(r,".lunora","preview.lock"),n=`${t}.${String(process.pid)}.${V()}`;$r(_r(t),{recursive:!0}),ne(n,String(process.pid));try{for(let o=0;o<3;o+=1){try{return xe(n,t),{release:()=>{je(t)===process.pid&&F(t,{force:!0})}}}catch(i){if(i.code!=="EEXIST")throw i}const s=je(t);if(s!==void 0){if(Be(s))return{error:`another preview command is running in this checkout (pid ${String(s)}); wait for it, or remove ${t} if it is not`};if(e.afterReadingOwner?.(),!Mr(t,s))return{error:`another preview command is taking over the lock at ${t}; run this again in a moment. If none is running, remove ${t}.retire-${String(s)}`}}}return{error:`could not take the preview lock ${t}`}}finally{F(n,{force:!0})}},xr=[["SIGINT",130],["SIGTERM",143]],Kr=5e3,H=new Set,Ge=r=>process.exit(r);let Ve=Ge,Z=[],he;const Hr=async r=>{const e=[...H].map(async n=>{try{await n()}catch{}});H.clear();const t=new Promise(n=>{setTimeout(n,Kr).unref()});await Promise.race([Promise.all(e),t]),Ve(r)},Br=r=>(he??=Hr(r),he),ie=(r,e)=>(H.add(r),Z.length===0&&(Z=xr.map(([t,n])=>{const o=()=>{Br(n).catch(()=>{})};return process.on(t,o),[t,o]})),()=>{if(H.delete(r),H.size===0){for(const[t,n]of Z)process.off(t,n);Z=[],Ve=Ge,he=void 0}}),Gr=/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu,ke=r=>{const e=Ke(r)?.lunora?.appId;return typeof e=="string"&&Gr.test(e)?e.toLowerCase():void 0},Vr=r=>{const e=Ke(r);if(e===void 0||e.lunoraIsForeign||e.lunora?.appId!==void 0)return;const t=V();return Or(e,"appId",t),ke(r)===t?t:void 0},se=63,Ee=new Set(["$schema","account_id","alias","assets","base_dir","build","cache","compatibility_date","compatibility_flags","dev","env","find_additional_modules","jsx_factory","jsx_fragment","keep_names","keep_vars","limits","logpush","main","migrations","minify","name","no_bundle","observability","placement","preview_urls","routes","rules","send_metrics","triggers","tsconfig","upload_source_maps","workers_dev"]),Ye=["ai","browser","define","images","media","secrets","vars","version_metadata","worker_loaders"],Yr=new Set(["env","name","routes","triggers","workers_dev"]),Jr=new Set(["tail_consumers"]),Y={d1_databases:"d1",kv_namespaces:"kv",r2_buckets:"r2"},R=r=>r==null?!1:Array.isArray(r)?r.length>0:typeof r!="object"||Object.keys(r).length>0,I=r=>(Array.isArray(r)?r:[]).filter(e=>typeof e=="object"&&e!==null),Je=(r,e)=>{if(!(Ee.has(r)||Jr.has(r)||Ye.includes(r)||r in Y)){if(r==="route")return`"route": write it as "routes" so the preview can clear it — a preview must never claim production's hostname`;if(r==="durable_objects"){const t=I(e.bindings).find(n=>n.script_name!==void 0);return t===void 0?void 0:`"durable_objects": the "${String(t.name)}" binding reaches another Worker's Durable Objects (script_name "${String(t.script_name)}"), which the preview would share`}if(r==="exports"){const t=Object.entries(e).find(([,n])=>n?.type==="workflow");return t===void 0?void 0:`"exports": workflow "${t[0]}" has an account-wide name, so the preview would take over production's workflow`}return`"${r}": a preview cannot isolate this binding yet, and copying it would point the preview at production's resource`}},Ue="LUNORA_ORIGIN_URL",U="LUNORA_PREVIEW",M=r=>`${r.production}/${r.slug}`,Xr=/[^a-z0-9]+/u,Xe=r=>{const e=r.toLowerCase().split(Xr).filter(t=>t!=="").join("-");return e===""||e===r?e:`${e}-${gr("sha256").update(r).digest("hex").slice(0,6)}`},ae=(r,e)=>oe(`${r}-preview-${e}`,se),be=(r,e)=>{const t=typeof r.name=="string"?r.name:"",n=Xe(e);return t===""?{error:`wrangler.jsonc has no "name", which the preview Worker's name is derived from`}:n===""?{error:`"${e}" has no letter or digit to name a preview by`}:{names:{environment:oe(`preview-${n}`,se),production:t,slug:n,worker:ae(t,n)}}},J=(r,e,t)=>{const n=t?.vars?.[U],o=typeof n=="string"?n.lastIndexOf("/"):-1;if(typeof n!="string"||o<=0)return!1;const s=n.slice(0,o),i=n.slice(o+1);return Xe(i)===i&&M({production:s,slug:i})===n&&e===oe(`preview-${i}`,se)&&t?.name===ae(s,i)},ce=(r,e)=>{const t=Object.entries(r.env??{}).filter(([i,a])=>!J(r,i,a)),n=t.find(([i])=>i===e)?.[1];if(n===void 0)return{error:`wrangler.jsonc declares no environment "${e}"${t.length===0?", and no environments at all":`; the environments are: ${t.map(([i])=>i).join(", ")}`}`};const o=typeof r.name=="string"?r.name:"";return{config:{...Object.fromEntries(Object.entries(r).filter(([i])=>Ee.has(i)||i==="exports")),...n,env:r.env,name:typeof n.name=="string"?n.name:`${o}-${e}`}}},zr=r=>Object.entries(r.env??{}).filter(([e,t])=>!J(r,e,t)).filter(([,e])=>typeof e.account_id=="string"&&e.account_id!==""&&e.account_id!==r.account_id).map(([e])=>e),ze=(r,e)=>oe(`${r}-${e}`,se),Ne=(r,e)=>{const t=I(r==="durable_objects"?e?.bindings:e);return new Set(t.map(n=>String(r==="durable_objects"?n.name:n.binding)))},Qr=r=>Object.entries(r.env??{}).flatMap(([e,t])=>J(r,e,t)?[]:Object.entries(t).flatMap(([n,o])=>{const s=r[n];if(n in Y||n==="durable_objects"){const i=Ne(n,s),a=[...Ne(n,o)].filter(c=>!i.has(c));return a.length===0?[]:[`"env.${e}.${n}": binding ${a.map(c=>`"${c}"`).join(", ")} is declared only in this environment, and a preview is planned from the top-level config, which lacks it; pass --env ${e} to preview that environment`]}return Je(n,o)!==void 0&&R(o)&&!R(s)?[`"env.${e}.${n}": declared only in this environment, and a preview is planned from the top-level config, which has none; pass --env ${e} to preview that environment`]:[]})),Qe=(r,e)=>Object.entries(Y).flatMap(([t,n])=>I(r[t]).map(o=>{const s=String(o.binding);return{binding:s,kind:n,name:ze(e,s)}})),Ze=(r,e,t)=>{const n=be(r,e);if(n.error!==void 0)return{error:n.error};const{environment:o,worker:s}=n.names;if(r.env?.[o]!==void 0&&!J(r,o,r.env[o]))return{error:`wrangler.jsonc already declares "env.${o}"; a preview writes that block itself, so name the preview differently`};const i=Object.entries(r).flatMap(([f,l])=>{const m=R(l)?Je(f,l):void 0;return m===void 0?[]:[m]});i.push(...t===void 0?Qr(r):[]);const a=I(r.r2_buckets).find(f=>f.jurisdiction!==void 0);if(a!==void 0&&i.push(`"r2_buckets": the "${String(a.binding)}" bucket has a jurisdiction, which a preview bucket is not created in yet`),i.length>0)return{error:`this app cannot be previewed in isolation yet:
|
|
2
2
|
- ${i.join(`
|
|
3
|
-
- `)}`};const c=Object.keys(Y).find(f=>I(r[f]).some(l=>typeof l.binding!="string"||l.binding===""));if(c!==void 0)return{error:`"${c}": every entry needs a non-empty "binding"`};const d=Qe(r,s),u=d.find((f,l)=>d.findIndex(m=>m.kind===f.kind&&m.name===f.name)!==l);return u!==void 0?{error:`two bindings would share the preview resource name "${u.name}"; rename one of them (binding names differ only by case or punctuation)`}:{plan:{...n.names,...t===void 0?{}:{sourceEnv:t},resources:d}}},_e=(r,e)=>`${r}:${e}`,le=(r,e)=>Object.fromEntries(Object.entries(r).filter(([t])=>t!==e)),Zr=(r,e,t,n)=>{const o=String(e.binding),s=t.resources.find(a=>a.kind===r&&a.binding===o),i=n.get(_e(r,o));if(s===void 0||i===void 0)throw new Error(`no preview ${r} resource for the "${o}" binding`);return r==="d1"?{...le(e,"preview_database_id"),database_id:i,database_name:s.name}:r==="kv"?{...le(e,"preview_id"),id:i}:{...le(e,"preview_bucket_name"),bucket_name:i}},qr=(r,e,t)=>{if(t.sourceEnv!==void 0)for(const[n,o]of Object.entries(r))Ee.has(n)&&!(n in e)&&!Yr.has(n)&&R(o)&&(e[n]=o)},qe=(r,e,t,n={},o)=>{const s=r,i={name:e.worker,routes:[],triggers:{crons:[]},workers_dev:!0},a={[U]:M(e)};for(const d of Ye)R(s[d])&&(i[d]=s[d]);const c=R(r.vars)?{...r.vars}:{};Ue in c&&(c[Ue]=o??e.worker),i.vars={...c,...n,...a},qr(s,i,e),R(r.durable_objects)&&(i.durable_objects=r.durable_objects),R(s.exports)&&(i.exports=s.exports);for(const[d,u]of Object.entries(Y)){const f=I(s[d]);f.length>0&&(i[d]=f.map(l=>Zr(u,l,e,t)))}return i},et={analytics_engine_datasets:["dataset"],d1_databases:["database_id","database_name","preview_database_id"],hyperdrive:["id"],kv_namespaces:["id","preview_id"],r2_buckets:["bucket_name","preview_bucket_name"],vectorize:["index_name"]},rt={d1_databases:"d1",kv_namespaces:"kv",r2_buckets:"r2"},er=(r,e)=>`${r}:${e}`,tt=r=>{const e=Object.entries(et).flatMap(([t,n])=>I(r[t]).flatMap(o=>n.map(s=>[rt[t]??t,o[s]])));return[["worker",r.name],...e].flatMap(([t,n])=>typeof n=="string"&&n!==""?[er(t,n)]:[])},nt=r=>new Set([r,...Object.entries(r.env??{}).flatMap(([e,t])=>J(r,e,t)?[]:[t])].flatMap(e=>tt(e))),Se=(r,e)=>[{kind:"worker",value:r},...e.flatMap(t=>[...t.id===void 0?[]:[{kind:t.kind,value:t.id}],{kind:t.kind,value:t.name}])],B=(r,e)=>{const t=nt(r);return e.filter(n=>t.has(er(n.kind,n.value))).map(n=>n.value)},fe="lunora-preview-registry",re=50,de=r=>{try{const{errors:e}=JSON.parse(r.text),t=(e??[]).flatMap(n=>typeof n.message=="string"?[n.message]:[]).join("; ");return t===""?`HTTP ${String(r.status)}`:`HTTP ${String(r.status)}: ${t}`}catch{return`HTTP ${String(r.status)}`}},k=(r,e)=>{if(!r.ok)throw new Error(`${e} failed (${de(r)})`);return r.body},X=r=>typeof r.result=="object"&&r.result!==null?r.result:{},rr=async(r,e,t,n)=>{const o=e.includes("?")?"&":"?";for(let s=1;s<=re;s+=1){const i=k(await r(`${e}${o}per_page=100&page=${String(s)}`),`listing ${t}`);if(!Array.isArray(i.result))throw new TypeError(`the listing of ${t} had no result list`);const a=i.result.find(d=>n(d)),c=typeof i.result_info=="object"&&i.result_info!==null?i.result_info:{};if(a!==void 0||typeof c.total_pages!="number"||s>=c.total_pages)return a}throw new Error(`the listing of ${t} has more than ${String(re)} pages`)},me=async(r,e)=>{const t=await rr(r,"/storage/kv/namespaces","KV namespaces",n=>n.title===e);return typeof t?.id=="string"?t.id:void 0},tr=async(r,e)=>{const{id:t}=X(k(await r("/storage/kv/namespaces",{body:JSON.stringify({title:e}),method:"POST"}),`creating KV namespace "${e}"`));if(typeof t!="string")throw new TypeError(`creating KV namespace "${e}" returned no id`);return t},Re=async(r,e)=>{const t=await me(r,fe);if(t!==void 0||!e)return t;try{return await tr(r,fe)}catch(n){const o=await me(r,fe);if(o===void 0)throw n;return o}},D=(r,e)=>`${r}/${e}`,Ie=(r,e)=>`/storage/kv/namespaces/${encodeURIComponent(r)}/values/${encodeURIComponent(e)}`,ot=new Set(["d1","kv","r2"]),it=(r,e)=>{const t=e;return typeof t=="object"&&t!==null&&ot.has(t.kind)&&typeof t.binding=="string"&&typeof t.id=="string"&&(t.pending===void 0||t.pending===!0)&&t.name===ze(r,t.binding)&&(t.kind!=="r2"||t.id===t.name)},st=r=>{const e=r;return typeof e=="object"&&e!==null&&typeof e.repository=="string"&&typeof e.pullRequest=="string"},at=r=>{const e=r;return typeof e=="object"&&e!==null&&e.version===1&&typeof e.worker=="string"&&typeof e.production=="string"&&(e.appId===void 0||typeof e.appId=="string")&&(e.sourceEnv===void 0||typeof e.sourceEnv=="string")&&(e.origin===void 0||st(e.origin))&&Array.isArray(e.resources)&&e.resources.every(t=>it(e.worker,t))},ge=async(r,e,t)=>{const n=await r(Ie(e,t));if(!n.ok&&n.status===404)return;const o=k(n,`reading the preview record "${t}"`);if(!at(o))throw new TypeError(`the preview record "${t}" is not one this CLI wrote; refusing to act on it`);return o},j=async(r,e,t)=>{k(await r(Ie(e,D(t.appId??t.production,t.name)),{body:JSON.stringify(t),method:"PUT"}),"writing the preview record")},nr=async(r,e,t)=>{k(await r(Ie(e,D(t.appId??t.production,t.name)),{method:"DELETE"}),"removing the preview record")},Le=async(r,e,t)=>{const n=`${t}/`,o=[];let s="";for(let a=1;;a+=1){if(a>re)throw new Error(`the preview records of "${t}" span more than ${String(re)} pages`);const c=`prefix=${encodeURIComponent(n)}${s===""?"":`&cursor=${encodeURIComponent(s)}`}`,d=k(await r(`/storage/kv/namespaces/${encodeURIComponent(e)}/keys?${c}`),"listing preview records");o.push(...(Array.isArray(d.result)?d.result:[]).flatMap(f=>typeof f.name=="string"?[f.name]:[]));const u=typeof d.result_info=="object"&&d.result_info!==null?d.result_info:{};if(typeof u.cursor!="string"||u.cursor==="")break;s=u.cursor}const i=await Promise.all(o.map(async a=>{try{return{key:a,record:await ge(r,e,a)}}catch(c){if(c instanceof TypeError)return{key:a,unreadable:!0};throw c}}));return{records:i.flatMap(a=>a.record===void 0?[]:[a.record]),unreadable:i.flatMap(a=>a.unreadable===!0?[a.key]:[])}},W=async(r,e,t)=>{if(e==="d1"){const o=await rr(r,`/d1/database?name=${encodeURIComponent(t)}`,"D1 databases",s=>s.name===t);return typeof o?.uuid=="string"?o.uuid:void 0}if(e==="kv")return me(r,t);const n=await r(`/r2/buckets/${encodeURIComponent(t)}`);if(!(!n.ok&&n.status===404))return k(n,`looking up R2 bucket "${t}"`),t},or=async(r,e,t)=>{if(e==="kv")return tr(r,t);if(e==="r2")return k(await r("/r2/buckets",{body:JSON.stringify({name:t}),method:"POST"}),`creating R2 bucket "${t}"`),t;const{uuid:n}=X(k(await r("/d1/database",{body:JSON.stringify({name:t}),method:"POST"}),`creating D1 database "${t}"`));if(typeof n!="string")throw new TypeError(`creating D1 database "${t}" returned no id`);return n},q="lunora-preview-locks",G=1800,ct=/UNIQUE constraint failed|SQLITE_CONSTRAINT/iu,dt="CREATE TABLE IF NOT EXISTS lunora_preview_locks (app_id TEXT NOT NULL, name TEXT NOT NULL, owner_token TEXT NOT NULL, expires_at INTEGER NOT NULL, PRIMARY KEY (app_id, name))",ut="INSERT INTO lunora_preview_locks (app_id, name, owner_token, expires_at) VALUES (?, ?, ?, unixepoch() + ?)",lt="SELECT owner_token, expires_at, unixepoch() AS now FROM lunora_preview_locks WHERE app_id = ? AND name = ?",ft="UPDATE lunora_preview_locks SET owner_token = ?, expires_at = unixepoch() + ? WHERE app_id = ? AND name = ? AND owner_token = ? AND expires_at <= unixepoch()",wt="DELETE FROM lunora_preview_locks WHERE app_id = ? AND name = ? AND owner_token = ?",E=async(r,e,t,n)=>{const o=await r(`/d1/database/${encodeURIComponent(e)}/query`,{body:JSON.stringify({params:n,sql:t}),method:"POST"});if(!o.ok&&ct.test(o.text))return;const s=k(o,"using the preview lock database").result?.[0];return{changes:typeof s?.meta?.changes=="number"?s.meta.changes:0,rows:Array.isArray(s?.results)?s.results:[]}},pt=4,vt=50,ht=async r=>new Promise(e=>{setTimeout(e,r)}),z="lunora_preview_lock_owner",ir="lunora-preview-locks/v1",mt=`CREATE TABLE IF NOT EXISTS ${z} (marker TEXT NOT NULL)`,gt=`INSERT INTO ${z} (marker) VALUES (?)`,yt="SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",$t=`SELECT marker FROM ${z}`,De=async(r,e)=>((await E(r,e,yt,[z]))?.rows.length??0)===0?!1:(await E(r,e,$t,[]))?.rows.some(o=>o.marker===ir)===!0,sr=async r=>{let e=await W(r,"d1",q),t=!1;if(e===void 0)try{e=await or(r,"d1",q),t=!0}catch(n){if(e=await W(r,"d1",q),e===void 0)throw n}if(t)await E(r,e,mt,[]),await E(r,e,gt,[ir]);else{let n=await De(r,e);for(let o=1;o<=pt&&!n;o+=1)await ht(vt*o),n=await De(r,e);if(!n)throw new Error(`a D1 database named "${q}" exists but was not made by lunora preview (it has no ${z} marker), so it is not used or written to. Rename it, or delete it if it is an empty leftover, then run this again`)}return await E(r,e,dt,[]),e},ar=async(r,e,t,n)=>{const o=V();for(let s=0;s<2;s+=1){if(await E(r,e,ut,[t,n,o,G])!==void 0)return{token:o};const a=(await E(r,e,lt,[t,n]))?.rows[0];if(a===void 0)continue;const c=Number(a.expires_at),d=Number.isFinite(c)&&c<=Number(a.now);return typeof a.owner_token!="string"||!d?{held:new Date(c*1e3).toISOString()}:(await E(r,e,ft,[o,G,t,n,a.owner_token]))?.changes===1?{token:o}:{held:"just now"}}return{held:"just now"}},cr="UPDATE lunora_preview_locks SET expires_at = unixepoch() + ? WHERE app_id = ? AND name = ? AND owner_token = ?",dr=(r,e,t,n,o,s,i=G*1e3/3)=>{let a=Date.now();const c=async()=>{try{(await E(r,e,cr,[G,t,n,o]))?.changes===1?a=Date.now():s()}catch{Date.now()-a>=2*i&&s()}},d=setInterval(()=>{c().catch(()=>{})},i);return d.unref(),()=>{clearInterval(d)}},ur=async(r,e,t,n,o)=>(await E(r,e,cr,[G,t,n,o]))?.changes===1,te=async(r,e,t,n,o)=>{try{await E(r,e,wt,[t,n,o])}catch{}},lr={d1:r=>`/d1/database/${encodeURIComponent(r)}`,kv:r=>`/storage/kv/namespaces/${encodeURIComponent(r)}`,r2:r=>`/r2/buckets/${encodeURIComponent(r)}`},Oe=async(r,e)=>{if(e.pending===!0)return"present";const t=await r(lr[e.kind](e.id));if(!t.ok&&t.status===404)return"missing";const n=X(k(t,`looking up ${e.kind} "${e.name}"`));return{d1:n.name,kv:n.title,r2:n.name}[e.kind]===e.name?"present":"changed"},Pe=10,kt=(r,e)=>{const t=`/r2/buckets/${encodeURIComponent(r)}/objects/`,n=`${t}${e.split("/").map(o=>encodeURIComponent(o)).join("/")}`;return e.split("/").some(o=>o==="."||o==="..")?{error:`the object key ${JSON.stringify(e)} has a "." or ".." segment, which cannot be addressed without leaving the bucket`}:new URL(`https://api.invalid/accounts/a${n}`).pathname.startsWith(`/accounts/a${t}`)?{path:n}:{error:`the object key ${JSON.stringify(e)} resolves outside bucket "${r}"`}},Et=async(r,e,t)=>{const n=kt(e,t);if(n.error!==void 0)throw new Error(`${n.error}; remove the object by hand, then delete the preview again`);const o=await r(n.path,{method:"DELETE"});if(!o.ok&&o.status!==404)throw new Error(`deleting object "${t}" failed (${de(o)})`)},bt=async(r,e)=>{let t="";for(;;){const n=await r(`/r2/buckets/${encodeURIComponent(e)}/objects?per_page=1000`);if(!n.ok&&n.status===404)return;const o=k(n,`listing the objects of R2 bucket "${e}"`),s=(Array.isArray(o.result)?o.result:[]).flatMap(a=>typeof a.key=="string"?[a.key]:[]);if(s.length===0)return;const i=`${String(s.length)}:${s[0]??""}:${s.at(-1)??""}`;if(i===t)throw new Error(`R2 bucket "${e}" still holds the same ${String(s.length)} objects after deleting them`);t=i;for(let a=0;a<s.length;a+=Pe)await Promise.all(s.slice(a,a+Pe).map(async c=>Et(r,e,c)))}},fr=async(r,e)=>{e.kind==="r2"&&await bt(r,e.id);const t=await r(lr[e.kind](e.id),{method:"DELETE"});if(!(t.ok||t.status===404))throw new Error(de(t))},_t=async(r,e)=>{const t=await r(`/workers/scripts/${encodeURIComponent(e)}?force=true`,{method:"DELETE"});if(!t.ok&&t.status!==404)throw new Error(de(t))},ue=async(r,e,t)=>{const n=await r(`/workers/scripts/${encodeURIComponent(e)}/settings`);if(!n.ok&&n.status===404)return{exists:!1};const{bindings:o}=X(k(n,`reading Worker "${e}"`)),s=(Array.isArray(o)?o:[]).find(i=>i.type==="plain_text"&&i.name===t)?.text;return typeof s=="string"?{exists:!0,marker:s}:{exists:!0}},St=async r=>{const{subdomain:e}=X(k(await r("/workers/subdomain"),"reading the workers.dev subdomain"));if(typeof e!="string"||e==="")throw new TypeError("the account has no workers.dev subdomain; register one in the Cloudflare dashboard (Workers & Pages) first");return e},Fe=["deploy","list","delete"],Rt=15e3,It=5,Ot=1e3,we="LUNORA_ADMIN_TOKEN",Tt="LUNORA_PREVIEW_SECRET_SEED",At={celld:"celld has no isolated previews: it has no Wrangler environments, and an app's Durable Objects and storage live in its fleet bucket. Deploy the copy as its own config, with CELLD_BUCKET naming a bucket of its own, and delete that bucket to remove it.",node:"the Node target has no `lunora deploy`, so there is nothing to preview: run a second process with its own database for a copy."},p=(r,e,t=w.USAGE)=>(r.error(e),{code:t,error:e}),C=r=>({environment:r.environment,name:r.name,resources:r.resources,url:r.url,worker:r.worker}),Ct=async(r,e)=>{if(r===void 0)return e();r.begin();try{return await e()}finally{r.end()}},wr=(r,e)=>{const t=pe(r.GITHUB_REPOSITORY)??pe(r.CI_PROJECT_PATH);return t===void 0?void 0:{pullRequest:e,repository:t}},jt=(r,e)=>e!==void 0&&r.origin?.repository===e.repository&&r.origin.pullRequest===e.pullRequest,Ut=r=>`the record for preview "${r}" was not made by a deploy of this pull request in this repository, so the identity the pull request's files name cannot be used to delete it; nothing was deleted`,Nt=(r,e)=>{const t=Se(e.worker,e.resources);return[...B(r.config,t),...r.alsoProduction===void 0?[]:B(r.alsoProduction,t)]},Lt=(r,e,t)=>{Object.assign(r,{origin:wr(e.environment??process.env,t)??r.origin})},Dt=r=>{const e=r.cwd??process.cwd(),t=Ur(e,r.target);if(t.target===void 0)return{error:t.error??"unknown deploy target"};if(t.target!=="cloudflare")return{error:`lunora preview: ${At[t.target]??`target "${t.target}" has no isolated previews.`}`};const n=He(e),o=n===void 0?void 0:$e(n).parsed;return n===void 0||o===void 0?{error:"lunora preview needs a readable wrangler.jsonc (or wrangler.json) in the project root"}:{context:{config:o,cwd:e,appId:ke(e),wranglerPath:n}}},pr=(r,e)=>{if(e.env!==void 0)return ce(r,e.env);const t=zr(r);return t.length>0&&pe((e.environment??process.env).CLOUDFLARE_ACCOUNT_ID)===void 0?{error:`environment(s) ${t.join(", ")} name an account_id other than the top level's, so the account to look in is not clear: pass --env <name> to use that environment's account, or set CLOUDFLARE_ACCOUNT_ID`}:{config:r}},Te=(r,e)=>{const{accountId:t,token:n}=Ar(r.environment??process.env,e.account_id);if(t===void 0||n===void 0)return{error:"lunora preview needs CLOUDFLARE_API_TOKEN (Workers Scripts, D1, Workers KV Storage and Workers R2 Storage: Edit) and CLOUDFLARE_ACCOUNT_ID (or `account_id` in wrangler.jsonc)"};const o=r.sleep??(async s=>new Promise(i=>{setTimeout(i,s)}));return{request:async(s,i)=>{for(let a=0;;a+=1){const c=await Cr({accountId:t,apiToken:n,...r.fetch===void 0?{}:{fetch:r.fetch},init:{...i,signal:AbortSignal.timeout(Rt)},path:s});if(c.ok||c.status!==429||a===It)return c;await o(Ot*2**a)}}}},Pt=r=>{const e={};for(const t of r){const n=t.indexOf("="),o=n===-1?"":t.slice(0,n);if(o==="")return{error:`--var ${t}: expected KEY=VALUE`};if(o===U)return{error:`--var ${o}: the preview sets ${o} itself; \`delete\` relies on it`};e[o]=t.slice(n+1)}return{variables:e}},$=r=>r instanceof Error?r.message:String(r),Ft=async(r,e,t,n)=>{const o=n.environment??process.env,s={},i=o[Tt],a=h=>i===void 0||i===""?Ir():yr("sha256",i).update(`${t}/${h}`).digest("hex");for(const h of n.secrets??[]){const y=o[h];if(y===void 0||y==="")return{error:`--secret ${h}: ${h} is not set in the environment, so there is no value to give the preview`};s[h]=y}const{fromLocal:c,fromPackages:d,mintableKeys:u}=await Dr(r,void 0);for(const h of[...d,...c,we])s[h]===void 0&&(u.has(h)||h===we)&&(s[h]=a(h));const f=[...new Set([...d,...c])].filter(h=>s[h]===void 0),m=(Array.isArray(e.secrets?.required)?e.secrets.required.filter(h=>typeof h=="string"):[]).filter(h=>s[h]===void 0);return m.length>0?{error:`the config requires ${m.join(", ")}, which a preview only gets when you pass ${m.map(h=>`--secret ${h}`).join(" ")}`}:{adminToken:s[we]??"",missing:f,values:s}},Mt=async(r,e,t,n)=>{const o=await Oe(r,t);if(o!=="present")throw new Error(`the record lists ${t.kind} "${t.name}" (${t.id}), which is ${o==="missing"?"gone":"now a different resource"}; run \`lunora preview delete ${e.slug}\`, then deploy again`);return n.info(`reusing preview ${t.kind} "${t.name}" for ${t.binding}`),t.id},Wt=3,xt=async(r,e,t,n)=>{let o;for(let s=1;s<=Wt;s+=1)try{await j(r,e,t);return}catch(i){o=i}try{await fr(r,n),t.resources.splice(t.resources.indexOf(n),1),await j(r,e,t).catch(()=>{})}catch{n.pending=!0}throw new Error(`${$(o)}; the ${n.kind} "${n.name}" just made could not be recorded, so it was removed again`)},Me=async(r,e,t,n,o)=>{let s=t.resources.find(i=>i.kind===n.kind&&i.binding===n.binding);if(s===void 0){if(await W(r,n.kind,n.name)!==void 0)throw new Error(`a ${n.kind} resource named "${n.name}" already exists but no preview record says this preview created it; refusing to bind or later delete it. Rename or remove it, then deploy again.`);s={binding:n.binding,id:n.kind==="r2"?n.name:"",kind:n.kind,name:n.name,pending:!0},t.resources.push(s),await j(r,e,t)}return s.id=await or(r,n.kind,n.name),delete s.pending,await xt(r,e,t,s),o.success(`created preview ${n.kind} "${n.name}" for ${n.binding}`),s.id},Kt=async(r,e,t,n,o,s,i)=>{const a=n.resources.find(c=>c.kind===o.kind&&c.binding===o.binding);if(i(),a?.pending===!0){if(await W(r,o.kind,o.name)!==void 0)throw new Error(`a ${o.kind} resource named "${o.name}" exists, but the record only claims it (a deploy was interrupted between creating it and confirming it), so nothing shows it is this preview's and it is left alone. If it is, delete it by hand, then deploy again`);return Me(r,e,n,o,s)}return a===void 0?Me(r,e,n,o,s):Mt(r,t,a,s)},Ht=async(r,e,t,n,o,s)=>{const i=new Map;for(const a of t.resources)i.set(_e(a.kind,a.binding),await Kt(r,e,t,n,a,o,s));return i},Ae=async(r,e,t)=>{const n=new Set((t?.resources??[]).map(i=>`${i.kind}:${i.name}`)),o=e.filter(i=>!n.has(`${i.kind}:${i.name}`));return(await Promise.all(o.map(async i=>await W(r,i.kind,i.name)===void 0?[]:[i]))).flat().map(i=>`${i.kind} "${i.name}"`)},vr=async(r,e,t,n,o,s,i=!0)=>{if(t!==void 0){const d=await ge(r,e,D(t,o));if(d!==void 0){if(d.appId!==t||d.name!==o)throw new Error(`the record at "${D(t,o)}" does not name this project and preview; refusing to act on it`);return d}}const a=await ge(r,e,D(n,o));if(a===void 0)return;if(a.appId!==void 0||a.production!==n||a.name!==o||a.worker!==ae(n,o))throw new Error(`the record at "${D(n,o)}" is not the one this CLI wrote for that Worker and preview; refusing to act on it`);if(t===void 0)throw new Error(`preview "${o}" is recorded under the Worker name, from before projects had an id. Add an app id to package.json (\`"lunora": { "appId": "${V()}" }\`), commit it, and run this again: it re-keys the record`);const c={...a,appId:t};return i&&(await j(r,e,c),await nr(r,e,a),s.warn(`preview "${o}" was recorded under the Worker name "${n}"; it is now recorded under project ${t}`)),c},Bt=async(r,e,t,n)=>{if(t===void 0&&n)return{code:w.USAGE,error:`a Worker named "${e.worker}" exists but no preview record says this preview deployed it; refusing to deploy over it`};if(t!==void 0&&t.worker!==e.worker)return{code:w.USAGE,error:`the record for preview "${e.slug}" names Worker "${t.worker}", not "${e.worker}" (the app was renamed since it was deployed); run \`lunora preview delete ${e.slug}\` first, then deploy it again`};const o=await Ae(r,e.resources,t);return o.length===0?void 0:{code:w.FAILURE,error:`${o.join(", ")} already exist(s) but no preview record read says this preview created it, so nothing was written or deployed. If a deploy of this preview just ran, its record may not have reached this location yet: run the deploy again. Otherwise rename or remove it, then deploy again.`}},Gt=r=>{const e=Er(ee(br(),"lunora-preview-")),t=ee(e,"secrets.json");return ne(t,JSON.stringify(r),{encoding:"utf8",mode:384}),{directory:e,path:t}},Vt=r=>({environment:r.environment,name:r.slug,resources:r.resources,worker:r.worker}),Yt=(r,e,t)=>{r.info(`dry run: preview "${e.slug}" would deploy Worker "${e.worker}" (env.${e.environment}, its own Durable Object namespace)`);for(const s of e.resources)r.info(` ${s.kind} "${s.name}" for ${s.binding} (created, or reused when this preview's record lists it)`);const{vars:n}=t,o=n===void 0?t:{...t,vars:Object.fromEntries(Object.keys(n).map(s=>[s,"<redacted>"]))};r.info(` env.${e.environment}: ${JSON.stringify(o)}`)},hr=(r,e)=>`another run is deploying or deleting preview "${r}" (its hold lapses at ${e}); wait for it. A run that crashed lets go by itself`,mr=(r,e,t)=>{const n=`the hold on preview "${e}" was lost while this run worked, so another run may have changed it too; check it with \`lunora preview list\` and run \`lunora preview delete ${e}\` if it looks wrong`;return r.error(n),{...t,code:t.code===w.SUCCESS?w.FAILURE:t.code,error:n}},Jt=async(r,e,t)=>{try{const n=await ue(r,e,U);return n.exists&&n.marker===t?"present":"absent"}catch{return"unknown"}},ye=(r,e)=>{if(r.lost)throw new Error(`the hold on preview "${e}" can no longer be vouched for, so this run stopped before changing anything more; check it with \`lunora preview list\`, then run again`)},Xt=async(r,e,t,n,o,s)=>{const{config:i,cwd:a,wranglerPath:c}=r,{logger:d}=n,{appId:u,hold:f,namespace:l,secrets:m,subdomain:h}=s;let y,_;const v=`https://${e.worker}.${h}.workers.dev`;try{const g=await ue(o,e.worker,U);if(g.exists&&g.marker!==M(e))return p(d,`a Worker named "${e.worker}" already exists and was not deployed as preview "${e.slug}"; refusing to deploy over it`);const b=new Date().toISOString(),T=await vr(o,l,u,e.production,e.slug,d),A=await Bt(o,e,T,g.exists);if(A!==void 0)return p(d,A.error,A.code);const N=B(r.committed??i,Se(e.worker,T?.resources??[]));if(N.length>0)return p(d,`preview "${e.slug}" would bind production's ${N.join(", ")}; refusing to deploy, nothing was created`);y=T??{createdAt:b,environment:e.environment,name:e.slug,production:e.production,appId:u,...e.sourceEnv===void 0?{}:{sourceEnv:e.sourceEnv},resources:[],updatedAt:b,version:1,worker:e.worker},Lt(y,n,e.slug),await f.verify(),await j(o,l,y),_=await Ht(o,l,e,y,d,()=>{ye(f,e.slug)})}catch(g){return p(d,`preview "${e.slug}": ${$(g)}`,w.FAILURE)}const O=B(r.committed??i,[..._].map(([g,b])=>({kind:g.slice(0,g.indexOf(":")),value:b})));if(O.length>0)return p(d,`preview "${e.slug}" would bind production's ${O.join(", ")}; refusing to deploy`);const x=L(c,"utf8");ne(c,Tr(x,["env",e.environment],qe(i,e,_,t,v)),"utf8");const S=Gt(m.values),K=ie(()=>{F(S.directory,{force:!0,recursive:!0})});try{ye(f,e.slug);const g=await Ct(r.baseline,async()=>Pr({allowSchemaDrift:!0,cwd:a,env:e.environment,fetchImpl:n.adminFetch,format:n.format,healthCheck:!0,healthFetch:n.healthFetch,healthSleep:n.healthSleep,interactive:!1,isolatedPreview:!0,logger:d,migrate:!0,migrateToken:m.adminToken,migrateUrl:v,migrateYes:!0,secretsFile:S.path,spawner:n.spawner,target:"cloudflare"})),b=g.code===0?"present":await Jt(o,e.worker,M(e));if(b==="present"?y.url=v:b==="absent"&&delete y.url,y.updatedAt=new Date().toISOString(),await f.verify(),await j(o,l,y),g.code!==0)return{code:g.code,data:{preview:C(y)},error:g.error??`preview "${e.slug}" did not deploy`}}catch(g){return p(d,`preview "${e.slug}": ${$(g)}`,w.FAILURE)}finally{F(S.directory,{force:!0,recursive:!0}),K()}if(n.seed===!0){const g=await Fr({cwd:a,fetchImpl:n.adminFetch,format:n.format,logger:d,prod:!0,token:m.adminToken,url:y.url,yes:!0});if(g.code!==0)return{code:g.code,data:{preview:C(y)},error:g.error??`seeding preview "${e.slug}" failed`}}return d.success(`preview "${e.slug}" is live at ${y.url??v} — remove it with \`lunora preview delete ${e.slug}\``),{code:w.SUCCESS,data:{preview:C(y)}}},zt=async(r,e,t,n,o)=>{const{appId:s,config:i,cwd:a}=r,{logger:c}=n;if(s===void 0)return p(c,"the project has no id to key the preview record by");const d=await Ft(a,i,e.worker,n);if(d.error!==void 0)return p(c,d.error);for(const v of d.missing)c.warn(`the preview has no ${v}: production's is never copied. Pass --secret ${v} with ${v} set in the environment to give it one.`);let u,f,l,m;try{f=await St(o),u=await Re(o,!0)??"",l=await sr(o);const v=await ar(o,l,s,e.slug);if(v.held!==void 0)return p(c,hr(e.slug,v.held));({token:m}=v)}catch(v){return p(c,`preview "${e.slug}": ${$(v)}`,w.FAILURE)}const h={lost:!1,verify:async()=>{if(!await ur(o,l,s,e.slug,m))throw h.lost=!0,new Error(`the hold on preview "${e.slug}" is no longer this run's, so its record was left as the other run has it`)}},y=dr(o,l,s,e.slug,m,()=>{h.lost=!0}),_=ie(async()=>{y(),await te(o,l,s,e.slug,m)});try{const v=await Xt(r,e,t,n,o,{appId:s,hold:h,namespace:u,secrets:d,subdomain:f});return h.lost?mr(c,e.slug,v):v}finally{y(),await te(o,l,s,e.slug,m),_()}},Qt=(r,e)=>{if(r.appId!==void 0)return{appId:r.appId};const t=e.environment??process.env,n='package.json has a "lunora.appId" that is not a UUID, or no `lunora` object this CLI can add one to';if(e.dryRun===!0)return e.logger.info(" app id: none yet; the first deploy writes one to package.json for you to commit"),{appId:void 0};if(t.CI!==void 0&&t.CI!==""&&t.CI!=="false")return{error:`package.json has no "lunora.appId", and a CI run cannot mint one (its checkout is discarded, so the preview could never be found again to delete). Commit \`"lunora": { "appId": "${V()}" }\` to package.json`};const o=Vr(r.cwd);return o===void 0?{error:n}:(e.logger.warn(`wrote an app id to package.json ("lunora.appId"); commit it so CI finds this project's previews`),{appId:o})},Zt=async(r,e,t)=>{const{cwd:n}=r,{logger:o}=e,s=Pt(e.vars??[]);if(s.error!==void 0)return p(o,s.error);const{variables:i}=s,a=Wr(n);if(a.error!==void 0)return p(o,a.error);const c=Qt(r,e);if(c.error!==void 0)return a.release(),p(o,c.error);let d=()=>{};const u=ee(n,"lunora",Sr);let f;try{f=Q(u)?L(u):void 0}catch(v){return a.release(),p(o,`could not read ${u}: ${$(v)}`)}let l=!1,m;const h={begin:()=>{l=!0},end:()=>{m=Q(u)?L(u):void 0,l=!1}},y=()=>{try{if(d(),f===void 0){const v=Q(u)?L(u):void 0;v!==void 0&&(l||m!==void 0&&v.equals(m))&&F(u,{force:!0})}else f.equals(Q(u)?L(u):Buffer.alloc(0))||ne(u,f)}finally{a.release()}},_=ie(y);try{d=Nr(n),await Lr(n,o,void 0,"cloudflare",void 0);const{services:v}=Rr(n);if(v.length>0)return p(o,`this app cannot be previewed in isolation yet: it declares lunora.config services (${v.map(A=>A.name).join(", ")}), which a preview would share with production`);const O=$e(r.wranglerPath).parsed??r.config,x=e.env===void 0?{config:O}:ce(O,e.env);if(x.error!==void 0)return p(o,x.error);const{config:S}=x,K=Ze(S,t,e.env);if(K.error!==void 0)return p(o,K.error);const{plan:g}=K,b=B(O,Se(g.worker,g.resources));if(b.length>0)return p(o,`preview "${g.slug}" names collide with production's ${b.join(", ")}; name the preview differently`);if(e.dryRun===!0){const A=new Map(g.resources.map(N=>[_e(N.kind,N.binding),`<${N.name}>`]));return Yt(o,g,qe(S,g,A,i,`https://${g.worker}.<your-subdomain>.workers.dev`)),o.info(` secrets: every one Lunora can mint, fresh${(e.secrets??[]).length>0?`; from the environment: ${(e.secrets??[]).join(", ")}`:""}`),{code:w.SUCCESS,data:{plan:Vt(g)}}}const T=Te(e,S);return T.error!==void 0?p(o,T.error,w.AUTH):await zt({...r,baseline:h,committed:O,config:S,appId:c.appId},g,i,e,T.request)}finally{y(),_()}},qt=async(r,e,t)=>{try{if(e.pending===!0)return await W(r,e.kind,e.name)===void 0?!0:(t.error(`${e.kind} "${e.name}" exists, but the record only claims it (a deploy was interrupted before confirming it), so nothing shows it is this preview's: it was left alone. If it is, delete it by hand, then run \`lunora preview delete\` again`),!1);const n=await Oe(r,e);return n==="changed"?(t.error(`${e.kind} "${e.name}" (${e.id}): the recorded id now names a different resource, so it was left alone`),!1):(n==="present"&&(await fr(r,e),t.success(`deleted ${e.kind} "${e.name}"`)),!0)}catch(n){return t.error(`deleting ${e.kind} "${e.name}" failed (${$(n)}); it stays on record for the next \`lunora preview delete\``),!1}},en=async(r,e,t,n,o,s,i)=>{try{s();const u=await ue(r,t.worker,U);if(u.exists&&u.marker!==M({production:t.production,slug:t.name}))return p(o,`Worker "${t.worker}" was not deployed as preview "${t.name}"; refusing to delete anything`);u.exists&&(await _t(r,t.worker),o.success(`deleted Worker "${t.worker}"`))}catch(u){return p(o,`deleting Worker "${t.worker}" failed (${$(u)}); its resources were left in place`,w.FAILURE)}const a=[];for(const u of t.resources)s(),await qt(r,u,o)||a.push(u);const c={...t,resources:a,updatedAt:new Date().toISOString()};let d;try{d=await Ae(r,n?.resources??[],t),d.length===0&&(await i(),await(a.length===0?nr(r,e,t):j(r,e,c)))}catch(u){return p(o,`preview "${t.name}": ${$(u)}`,w.FAILURE)}return d.length>0?p(o,`${d.join(", ")} exist(s) under this preview's names but not in the record read (a deploy may have just recorded it); the record was left as it is — run \`lunora preview delete ${t.name}\` again. If that keeps failing, the resource was never recorded (a deploy stopped between creating and recording it): delete it by hand, then run delete once more`,w.FAILURE):a.length>0?{code:w.FAILURE,data:{preview:C(c)},error:`${String(a.length)} preview resource(s) could not be deleted`}:{code:w.SUCCESS,data:{deleted:C(t)}}},rn=async(r,e,t)=>{const n=[{config:e}];for(const s of Object.keys(e.env??{})){const i=ce(e,s);i.config!==void 0&&n.push({config:i.config,env:s})}const o=await Promise.all(n.map(async s=>{const{names:i}=be(s.config,t);if(i===void 0)return[];const a=Qe(s.config,i.worker),c=await ue(r,i.worker,U),d=await Ae(r,a,void 0);return[...c.exists&&c.marker===M(i)?[`Worker "${i.worker}" exists`]:[],...d.map(u=>`${u} exists`)]}));return[...new Set(o.flat())]},tn=(r,e)=>{const t=e.sourceEnv===void 0?{config:r}:ce(r,e.sourceEnv);return t.config===void 0?void 0:Ze({...t.config,name:e.production},e.name,e.sourceEnv).plan},nn=async(r,e,t,n)=>{const o=await rn(r,e,t);return o.length===0?void 0:p(n,`no record of preview "${t}" could be read, but ${o.join(" and ")}; nothing was deleted. If a deploy just ran, its record may not have reached this location yet: run delete again. Otherwise delete from a checkout whose package.json has the lunora.appId that deployed it`,w.FAILURE)},on=(r,e)=>{e.info(`dry run: would delete Worker "${r.worker}" and its Durable Objects`);for(const t of r.resources)e.info(` ${t.kind} "${t.name}" (${t.binding})`);return{code:w.SUCCESS,data:{plan:C(r)}}},sn=(r,e,t)=>e.worker===ae(e.production,e.name)&&e.appId===r.appId&&e.name===t,We=async(r,e,{hold:t,names:n,namespace:o,request:s})=>{const{config:i}=r,{logger:a}=e;let c;try{if(c=o===void 0?void 0:await vr(s,o,r.appId,n.production,n.slug,a,e.dryRun!==!0),o===void 0||c===void 0){const l=await nn(s,i,n.slug,a);if(l!==void 0)return l}}catch(l){return p(a,`preview "${n.slug}": ${$(l)}`,w.FAILURE)}if(o===void 0||c===void 0)return p(a,`no preview "${n.slug}" of "${n.production}" is on record; nothing was deleted`,w.NOT_FOUND);const d=Nt(r,c);if(!sn(r,c,n.slug)||d.length>0)return p(a,`the record for preview "${n.slug}" names ${d.length>0?`production's ${d.join(", ")}`:`Worker "${c.worker}", which is not the Worker of this preview`}; refusing to delete anything`);if(r.requireOrigin===!0&&!jt(c,wr(e.environment??process.env,n.slug)))return p(a,Ut(n.slug),w.FAILURE);let u;try{u=await Promise.all(c.resources.map(async l=>Oe(s,l)))}catch(l){return p(a,`preview "${n.slug}": ${$(l)}`,w.FAILURE)}const f=c.resources.filter((l,m)=>u[m]==="changed");return f.length>0?p(a,`the record for preview "${n.slug}" lists ${f.map(l=>`${l.kind} ${l.id}`).join(", ")}, which no longer name(s) the recorded resource; refusing to delete anything`):e.dryRun===!0?on(c,a):en(s,o,c,tn(i,c),a,()=>{ye(t,c.name)},t.verify)},P=async(r,e,t)=>{const{config:n}=r,{logger:o}=e,s=be(n,t);if(s.error!==void 0)return p(o,s.error);const{names:i}=s,a=pr(n,e);if(a.error!==void 0)return p(o,a.error,e.env===void 0?w.AUTH:w.USAGE);const c=Te(e,a.config);if(c.error!==void 0)return p(o,c.error,w.AUTH);const{request:d}=c;let u;try{u=await Re(d,!1)}catch(v){return p(o,`preview "${i.slug}": ${$(v)}`,w.FAILURE)}if(r.appId===void 0||u===void 0||e.dryRun===!0)return We(r,e,{hold:{lost:!1,verify:()=>Promise.resolve()},names:i,namespace:u,request:d});const{appId:f}=r;let l,m;try{l=await sr(d);const v=await ar(d,l,f,i.slug);if(v.held!==void 0)return p(o,hr(i.slug,v.held));({token:m}=v)}catch(v){return p(o,`preview "${i.slug}": ${$(v)}`,w.FAILURE)}const h={lost:!1,verify:async()=>{if(!await ur(d,l,f,i.slug,m))throw h.lost=!0,new Error(`the hold on preview "${i.slug}" is no longer this run's, so its record was left as the other run has it`)}},y=dr(d,l,f,i.slug,m,()=>{h.lost=!0}),_=ie(async()=>{y(),await te(d,l,f,i.slug,m)});try{const v=await We(r,e,{hold:h,names:i,namespace:u,request:d});return h.lost?mr(o,i.slug,v):v}catch(v){return p(o,`preview "${i.slug}": ${$(v)}`,w.FAILURE)}finally{y(),await te(d,l,f,i.slug,m),_()}},an=async(r,e)=>{const{config:t}=r,{logger:n}=e,o=typeof t.name=="string"?t.name:"",s=pr(t,e);if(s.error!==void 0)return p(n,s.error,e.env===void 0?w.AUTH:w.USAGE);const i=Te(e,s.config);if(i.error!==void 0)return p(n,i.error,w.AUTH);let a;try{const c=await Re(i.request,!1),d={records:[],unreadable:[]},u=c===void 0||r.appId===void 0?d:await Le(i.request,c,r.appId),f=c===void 0?d:await Le(i.request,c,o);a=[...u.records,...f.records],f.records.length>0&&n.warn(`${String(f.records.length)} preview(s) are recorded under the Worker name, from before projects had an id: ${f.records.map(l=>l.name).join(", ")}. ${r.appId===void 0?"Add an app id to package.json, then run":"Run"} \`lunora preview delete <name>\` (or deploy it again) to re-key each`);for(const l of[...u.unreadable,...f.unreadable])n.warn(`the registry key "${l}" is not a preview record this CLI wrote; it is not listed, and \`delete\` will refuse it`)}catch(c){return p(n,$(c),w.FAILURE)}a.length===0&&n.info(`no previews of "${o}" are on record`);for(const c of a)n.info(`${c.name} ${c.url??"(not deployed)"} Worker ${c.worker}, ${String(c.resources.length)} resource(s), updated ${c.updatedAt}`);return{code:w.SUCCESS,data:{previews:a.map(c=>C(c))}}},cn=/^[\dA-Za-z][\w-]{0,62}$/u,dn=r=>{const e=ke(r);if(e===void 0)return{error:'its package.json has no readable "lunora.appId"'};const t=He(r);let n;try{n=t===void 0?void 0:$e(t).parsed}catch{n=void 0}const o=n?.name;return n===void 0||typeof o!="string"||!cn.test(o)?{error:"its wrangler config has no readable, valid Worker name"}:{identity:{appId:e,config:n,production:o}}},un=async(r,e,t,n)=>{const o=[...e];if(o.some(i=>i.code===w.SUCCESS)){for(const[i,a]of o.entries())if(a.code!==w.SUCCESS&&a.code!==w.NOT_FOUND){const c=await P(r[i],t,n);o[i]=c.code===w.NOT_FOUND?{code:w.SUCCESS}:c}}const s=o.find(i=>i.code!==w.SUCCESS&&i.code!==w.NOT_FOUND);return s!==void 0?s:o.find(i=>i.code===w.SUCCESS&&i.data!==void 0)??o.find(i=>i.code===w.SUCCESS)??p(t.logger,`no preview "${n}" is on record under this checkout's project or under the pull request head's, whose id or Worker name differs; nothing was deleted`,w.FAILURE)},ln=async(r,e,t,n)=>{const o=dn(n);if(o.error!==void 0){e.logger.warn(`the pull request head's identity could not be read (${o.error}); looking for the preview under this checkout's only`);const l=await P(r,e,t);return l.code===w.NOT_FOUND?p(e.logger,`no preview "${t}" is on record under this checkout's project, and the pull request head's identity could not be read (${o.error}), so it cannot be ruled out under that; nothing was deleted`,w.FAILURE):l}const{appId:s,config:i,production:a}=o.identity,c={...r,alsoProduction:i};if(s===r.appId&&a===r.config.name)return P(c,e,t);const d={...c,appId:s,config:{...r.config,...typeof i.account_id=="string"?{account_id:i.account_id}:{},name:a},requireOrigin:!0},u=[c,d],f=[await P(c,e,t),await P(d,e,t)];return un(u,f,e,t)},fn=async r=>{const{logger:e,subcommand:t}=r;if(!Fe.includes(t))return p(e,`usage: lunora preview <${Fe.join(" | ")}> [name]`);const n=Dt(r);return n.error!==void 0?p(e,n.error):r.headDir!==void 0&&t!=="delete"?p(e,"--head-dir applies to `lunora preview delete` only"):t==="list"?an(n.context,r):r.name===void 0||r.name===""?p(e,`usage: lunora preview ${t??""} <name> — e.g. the pull request number`):t==="deploy"?Zt(n.context,r,r.name):r.headDir===void 0?P(n.context,r,r.name):ln(n.context,r,r.name,r.headDir)},Rn=jr(async({argument:r,cwd:e,format:t,logger:n,options:o})=>{const s=await fn({cwd:e,dryRun:o.dryRun===!0,env:o.env,format:t,headDir:o.headDir,logger:n,name:r[1],secrets:o.secret??[],seed:o.seed===!0,subcommand:r[0],target:o.target,vars:o.var});return{code:s.code,data:s.data,error:s.error}});export{Rn as execute,fn as runPreviewCommand};
|
|
3
|
+
- `)}`};const c=Object.keys(Y).find(f=>I(r[f]).some(l=>typeof l.binding!="string"||l.binding===""));if(c!==void 0)return{error:`"${c}": every entry needs a non-empty "binding"`};const d=Qe(r,s),u=d.find((f,l)=>d.findIndex(m=>m.kind===f.kind&&m.name===f.name)!==l);return u!==void 0?{error:`two bindings would share the preview resource name "${u.name}"; rename one of them (binding names differ only by case or punctuation)`}:{plan:{...n.names,...t===void 0?{}:{sourceEnv:t},resources:d}}},_e=(r,e)=>`${r}:${e}`,le=(r,e)=>Object.fromEntries(Object.entries(r).filter(([t])=>t!==e)),Zr=(r,e,t,n)=>{const o=String(e.binding),s=t.resources.find(a=>a.kind===r&&a.binding===o),i=n.get(_e(r,o));if(s===void 0||i===void 0)throw new Error(`no preview ${r} resource for the "${o}" binding`);return r==="d1"?{...le(e,"preview_database_id"),database_id:i,database_name:s.name}:r==="kv"?{...le(e,"preview_id"),id:i}:{...le(e,"preview_bucket_name"),bucket_name:i}},qr=(r,e,t)=>{if(t.sourceEnv!==void 0)for(const[n,o]of Object.entries(r))Ee.has(n)&&!(n in e)&&!Yr.has(n)&&R(o)&&(e[n]=o)},qe=(r,e,t,n={},o)=>{const s=r,i={name:e.worker,routes:[],triggers:{crons:[]},workers_dev:!0},a={[U]:M(e)};for(const d of Ye)R(s[d])&&(i[d]=s[d]);const c=R(r.vars)?{...r.vars}:{};Ue in c&&(c[Ue]=o??e.worker),i.vars={...c,...n,...a},qr(s,i,e),R(r.durable_objects)&&(i.durable_objects=r.durable_objects),R(s.exports)&&(i.exports=s.exports);for(const[d,u]of Object.entries(Y)){const f=I(s[d]);f.length>0&&(i[d]=f.map(l=>Zr(u,l,e,t)))}return i},et={analytics_engine_datasets:["dataset"],d1_databases:["database_id","database_name","preview_database_id"],hyperdrive:["id"],kv_namespaces:["id","preview_id"],r2_buckets:["bucket_name","preview_bucket_name"],vectorize:["index_name"]},rt={d1_databases:"d1",kv_namespaces:"kv",r2_buckets:"r2"},er=(r,e)=>`${r}:${e}`,tt=r=>{const e=Object.entries(et).flatMap(([t,n])=>I(r[t]).flatMap(o=>n.map(s=>[rt[t]??t,o[s]])));return[["worker",r.name],...e].flatMap(([t,n])=>typeof n=="string"&&n!==""?[er(t,n)]:[])},nt=r=>new Set([r,...Object.entries(r.env??{}).flatMap(([e,t])=>J(r,e,t)?[]:[t])].flatMap(e=>tt(e))),Se=(r,e)=>[{kind:"worker",value:r},...e.flatMap(t=>[...t.id===void 0?[]:[{kind:t.kind,value:t.id}],{kind:t.kind,value:t.name}])],B=(r,e)=>{const t=nt(r);return e.filter(n=>t.has(er(n.kind,n.value))).map(n=>n.value)},fe="lunora-preview-registry",re=50,de=r=>{try{const{errors:e}=JSON.parse(r.text),t=(e??[]).flatMap(n=>typeof n.message=="string"?[n.message]:[]).join("; ");return t===""?`HTTP ${String(r.status)}`:`HTTP ${String(r.status)}: ${t}`}catch{return`HTTP ${String(r.status)}`}},k=(r,e)=>{if(!r.ok)throw new Error(`${e} failed (${de(r)})`);return r.body},X=r=>typeof r.result=="object"&&r.result!==null?r.result:{},rr=async(r,e,t,n)=>{const o=e.includes("?")?"&":"?";for(let s=1;s<=re;s+=1){const i=k(await r(`${e}${o}per_page=100&page=${String(s)}`),`listing ${t}`);if(!Array.isArray(i.result))throw new TypeError(`the listing of ${t} had no result list`);const a=i.result.find(d=>n(d)),c=typeof i.result_info=="object"&&i.result_info!==null?i.result_info:{};if(a!==void 0||typeof c.total_pages!="number"||s>=c.total_pages)return a}throw new Error(`the listing of ${t} has more than ${String(re)} pages`)},me=async(r,e)=>{const t=await rr(r,"/storage/kv/namespaces","KV namespaces",n=>n.title===e);return typeof t?.id=="string"?t.id:void 0},tr=async(r,e)=>{const{id:t}=X(k(await r("/storage/kv/namespaces",{body:JSON.stringify({title:e}),method:"POST"}),`creating KV namespace "${e}"`));if(typeof t!="string")throw new TypeError(`creating KV namespace "${e}" returned no id`);return t},Re=async(r,e)=>{const t=await me(r,fe);if(t!==void 0||!e)return t;try{return await tr(r,fe)}catch(n){const o=await me(r,fe);if(o===void 0)throw n;return o}},D=(r,e)=>`${r}/${e}`,Ie=(r,e)=>`/storage/kv/namespaces/${encodeURIComponent(r)}/values/${encodeURIComponent(e)}`,ot=new Set(["d1","kv","r2"]),it=(r,e)=>{const t=e;return typeof t=="object"&&t!==null&&ot.has(t.kind)&&typeof t.binding=="string"&&typeof t.id=="string"&&(t.pending===void 0||t.pending===!0)&&t.name===ze(r,t.binding)&&(t.kind!=="r2"||t.id===t.name)},st=r=>{const e=r;return typeof e=="object"&&e!==null&&typeof e.repository=="string"&&typeof e.pullRequest=="string"},at=r=>{const e=r;return typeof e=="object"&&e!==null&&e.version===1&&typeof e.worker=="string"&&typeof e.production=="string"&&(e.appId===void 0||typeof e.appId=="string")&&(e.sourceEnv===void 0||typeof e.sourceEnv=="string")&&(e.origin===void 0||st(e.origin))&&Array.isArray(e.resources)&&e.resources.every(t=>it(e.worker,t))},ge=async(r,e,t)=>{const n=await r(Ie(e,t));if(!n.ok&&n.status===404)return;const o=k(n,`reading the preview record "${t}"`);if(!at(o))throw new TypeError(`the preview record "${t}" is not one this CLI wrote; refusing to act on it`);return o},j=async(r,e,t)=>{k(await r(Ie(e,D(t.appId??t.production,t.name)),{body:JSON.stringify(t),method:"PUT"}),"writing the preview record")},nr=async(r,e,t)=>{k(await r(Ie(e,D(t.appId??t.production,t.name)),{method:"DELETE"}),"removing the preview record")},Le=async(r,e,t)=>{const n=`${t}/`,o=[];let s="";for(let a=1;;a+=1){if(a>re)throw new Error(`the preview records of "${t}" span more than ${String(re)} pages`);const c=`prefix=${encodeURIComponent(n)}${s===""?"":`&cursor=${encodeURIComponent(s)}`}`,d=k(await r(`/storage/kv/namespaces/${encodeURIComponent(e)}/keys?${c}`),"listing preview records");o.push(...(Array.isArray(d.result)?d.result:[]).flatMap(f=>typeof f.name=="string"?[f.name]:[]));const u=typeof d.result_info=="object"&&d.result_info!==null?d.result_info:{};if(typeof u.cursor!="string"||u.cursor==="")break;s=u.cursor}const i=await Promise.all(o.map(async a=>{try{return{key:a,record:await ge(r,e,a)}}catch(c){if(c instanceof TypeError)return{key:a,unreadable:!0};throw c}}));return{records:i.flatMap(a=>a.record===void 0?[]:[a.record]),unreadable:i.flatMap(a=>a.unreadable===!0?[a.key]:[])}},W=async(r,e,t)=>{if(e==="d1"){const o=await rr(r,`/d1/database?name=${encodeURIComponent(t)}`,"D1 databases",s=>s.name===t);return typeof o?.uuid=="string"?o.uuid:void 0}if(e==="kv")return me(r,t);const n=await r(`/r2/buckets/${encodeURIComponent(t)}`);if(!(!n.ok&&n.status===404))return k(n,`looking up R2 bucket "${t}"`),t},or=async(r,e,t)=>{if(e==="kv")return tr(r,t);if(e==="r2")return k(await r("/r2/buckets",{body:JSON.stringify({name:t}),method:"POST"}),`creating R2 bucket "${t}"`),t;const{uuid:n}=X(k(await r("/d1/database",{body:JSON.stringify({name:t}),method:"POST"}),`creating D1 database "${t}"`));if(typeof n!="string")throw new TypeError(`creating D1 database "${t}" returned no id`);return n},q="lunora-preview-locks",G=1800,ct=/UNIQUE constraint failed|SQLITE_CONSTRAINT/iu,dt="CREATE TABLE IF NOT EXISTS lunora_preview_locks (app_id TEXT NOT NULL, name TEXT NOT NULL, owner_token TEXT NOT NULL, expires_at INTEGER NOT NULL, PRIMARY KEY (app_id, name))",ut="INSERT INTO lunora_preview_locks (app_id, name, owner_token, expires_at) VALUES (?, ?, ?, unixepoch() + ?)",lt="SELECT owner_token, expires_at, unixepoch() AS now FROM lunora_preview_locks WHERE app_id = ? AND name = ?",ft="UPDATE lunora_preview_locks SET owner_token = ?, expires_at = unixepoch() + ? WHERE app_id = ? AND name = ? AND owner_token = ? AND expires_at <= unixepoch()",wt="DELETE FROM lunora_preview_locks WHERE app_id = ? AND name = ? AND owner_token = ?",E=async(r,e,t,n)=>{const o=await r(`/d1/database/${encodeURIComponent(e)}/query`,{body:JSON.stringify({params:n,sql:t}),method:"POST"});if(!o.ok&&ct.test(o.text))return;const s=k(o,"using the preview lock database").result?.[0];return{changes:typeof s?.meta?.changes=="number"?s.meta.changes:0,rows:Array.isArray(s?.results)?s.results:[]}},pt=4,vt=50,ht=async r=>new Promise(e=>{setTimeout(e,r)}),z="lunora_preview_lock_owner",ir="lunora-preview-locks/v1",mt=`CREATE TABLE IF NOT EXISTS ${z} (marker TEXT NOT NULL)`,gt=`INSERT INTO ${z} (marker) VALUES (?)`,yt="SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",$t=`SELECT marker FROM ${z}`,De=async(r,e)=>((await E(r,e,yt,[z]))?.rows.length??0)===0?!1:(await E(r,e,$t,[]))?.rows.some(o=>o.marker===ir)===!0,sr=async r=>{let e=await W(r,"d1",q),t=!1;if(e===void 0)try{e=await or(r,"d1",q),t=!0}catch(n){if(e=await W(r,"d1",q),e===void 0)throw n}if(t)await E(r,e,mt,[]),await E(r,e,gt,[ir]);else{let n=await De(r,e);for(let o=1;o<=pt&&!n;o+=1)await ht(vt*o),n=await De(r,e);if(!n)throw new Error(`a D1 database named "${q}" exists but was not made by lunora preview (it has no ${z} marker), so it is not used or written to. Rename it, or delete it if it is an empty leftover, then run this again`)}return await E(r,e,dt,[]),e},ar=async(r,e,t,n)=>{const o=V();for(let s=0;s<2;s+=1){if(await E(r,e,ut,[t,n,o,G])!==void 0)return{token:o};const a=(await E(r,e,lt,[t,n]))?.rows[0];if(a===void 0)continue;const c=Number(a.expires_at),d=Number.isFinite(c)&&c<=Number(a.now);return typeof a.owner_token!="string"||!d?{held:new Date(c*1e3).toISOString()}:(await E(r,e,ft,[o,G,t,n,a.owner_token]))?.changes===1?{token:o}:{held:"just now"}}return{held:"just now"}},cr="UPDATE lunora_preview_locks SET expires_at = unixepoch() + ? WHERE app_id = ? AND name = ? AND owner_token = ?",dr=(r,e,t,n,o,s,i=G*1e3/3)=>{let a=Date.now();const c=async()=>{try{(await E(r,e,cr,[G,t,n,o]))?.changes===1?a=Date.now():s()}catch{Date.now()-a>=2*i&&s()}},d=setInterval(()=>{c().catch(()=>{})},i);return d.unref(),()=>{clearInterval(d)}},ur=async(r,e,t,n,o)=>(await E(r,e,cr,[G,t,n,o]))?.changes===1,te=async(r,e,t,n,o)=>{try{await E(r,e,wt,[t,n,o])}catch{}},lr={d1:r=>`/d1/database/${encodeURIComponent(r)}`,kv:r=>`/storage/kv/namespaces/${encodeURIComponent(r)}`,r2:r=>`/r2/buckets/${encodeURIComponent(r)}`},Oe=async(r,e)=>{if(e.pending===!0)return"present";const t=await r(lr[e.kind](e.id));if(!t.ok&&t.status===404)return"missing";const n=X(k(t,`looking up ${e.kind} "${e.name}"`));return{d1:n.name,kv:n.title,r2:n.name}[e.kind]===e.name?"present":"changed"},Pe=10,kt=(r,e)=>{const t=`/r2/buckets/${encodeURIComponent(r)}/objects/`,n=`${t}${e.split("/").map(o=>encodeURIComponent(o)).join("/")}`;return e.split("/").some(o=>o==="."||o==="..")?{error:`the object key ${JSON.stringify(e)} has a "." or ".." segment, which cannot be addressed without leaving the bucket`}:new URL(`https://api.invalid/accounts/a${n}`).pathname.startsWith(`/accounts/a${t}`)?{path:n}:{error:`the object key ${JSON.stringify(e)} resolves outside bucket "${r}"`}},Et=async(r,e,t)=>{const n=kt(e,t);if(n.error!==void 0)throw new Error(`${n.error}; remove the object by hand, then delete the preview again`);const o=await r(n.path,{method:"DELETE"});if(!o.ok&&o.status!==404)throw new Error(`deleting object "${t}" failed (${de(o)})`)},bt=async(r,e)=>{let t="";for(;;){const n=await r(`/r2/buckets/${encodeURIComponent(e)}/objects?per_page=1000`);if(!n.ok&&n.status===404)return;const o=k(n,`listing the objects of R2 bucket "${e}"`),s=(Array.isArray(o.result)?o.result:[]).flatMap(a=>typeof a.key=="string"?[a.key]:[]);if(s.length===0)return;const i=`${String(s.length)}:${s[0]??""}:${s.at(-1)??""}`;if(i===t)throw new Error(`R2 bucket "${e}" still holds the same ${String(s.length)} objects after deleting them`);t=i;for(let a=0;a<s.length;a+=Pe)await Promise.all(s.slice(a,a+Pe).map(async c=>Et(r,e,c)))}},fr=async(r,e)=>{e.kind==="r2"&&await bt(r,e.id);const t=await r(lr[e.kind](e.id),{method:"DELETE"});if(!(t.ok||t.status===404))throw new Error(de(t))},_t=async(r,e)=>{const t=await r(`/workers/scripts/${encodeURIComponent(e)}?force=true`,{method:"DELETE"});if(!t.ok&&t.status!==404)throw new Error(de(t))},ue=async(r,e,t)=>{const n=await r(`/workers/scripts/${encodeURIComponent(e)}/settings`);if(!n.ok&&n.status===404)return{exists:!1};const{bindings:o}=X(k(n,`reading Worker "${e}"`)),s=(Array.isArray(o)?o:[]).find(i=>i.type==="plain_text"&&i.name===t)?.text;return typeof s=="string"?{exists:!0,marker:s}:{exists:!0}},St=async r=>{const{subdomain:e}=X(k(await r("/workers/subdomain"),"reading the workers.dev subdomain"));if(typeof e!="string"||e==="")throw new TypeError("the account has no workers.dev subdomain; register one in the Cloudflare dashboard (Workers & Pages) first");return e},Fe=["deploy","list","delete"],Rt=15e3,It=5,Ot=1e3,we="LUNORA_ADMIN_TOKEN",Tt="LUNORA_PREVIEW_SECRET_SEED",At={celld:"celld has no isolated previews: it has no Wrangler environments, and an app's Durable Objects and storage live in its fleet bucket. Deploy the copy as its own config, with CELLD_BUCKET naming a bucket of its own, and delete that bucket to remove it.",node:"the Node target has no `lunora deploy`, so there is nothing to preview: run a second process with its own database for a copy."},p=(r,e,t=w.USAGE)=>(r.error(e),{code:t,error:e}),C=r=>({environment:r.environment,name:r.name,resources:r.resources,url:r.url,worker:r.worker}),Ct=async(r,e)=>{if(r===void 0)return e();r.begin();try{return await e()}finally{r.end()}},wr=(r,e)=>{const t=pe(r.GITHUB_REPOSITORY)??pe(r.CI_PROJECT_PATH);return t===void 0?void 0:{pullRequest:e,repository:t}},jt=(r,e)=>e!==void 0&&r.origin?.repository===e.repository&&r.origin.pullRequest===e.pullRequest,Ut=r=>`the record for preview "${r}" was not made by a deploy of this pull request in this repository, so the identity the pull request's files name cannot be used to delete it; nothing was deleted`,Nt=(r,e)=>{const t=Se(e.worker,e.resources);return[...B(r.config,t),...r.alsoProduction===void 0?[]:B(r.alsoProduction,t)]},Lt=(r,e,t)=>{Object.assign(r,{origin:wr(e.environment??process.env,t)??r.origin})},Dt=r=>{const e=r.cwd??process.cwd(),t=Ur(e,r.target);if(t.target===void 0)return{error:t.error??"unknown deploy target"};if(t.target!=="cloudflare")return{error:`lunora preview: ${At[t.target]??`target "${t.target}" has no isolated previews.`}`};const n=He(e),o=n===void 0?void 0:$e(n).parsed;return n===void 0||o===void 0?{error:"lunora preview needs a readable wrangler.jsonc (or wrangler.json) in the project root"}:{context:{config:o,cwd:e,appId:ke(e),wranglerPath:n}}},pr=(r,e)=>{if(e.env!==void 0)return ce(r,e.env);const t=zr(r);return t.length>0&&pe((e.environment??process.env).CLOUDFLARE_ACCOUNT_ID)===void 0?{error:`environment(s) ${t.join(", ")} name an account_id other than the top level's, so the account to look in is not clear: pass --env <name> to use that environment's account, or set CLOUDFLARE_ACCOUNT_ID`}:{config:r}},Te=(r,e)=>{const{accountId:t,token:n}=Ar(r.environment??process.env,e.account_id);if(t===void 0||n===void 0)return{error:"lunora preview needs CLOUDFLARE_API_TOKEN (Workers Scripts, D1, Workers KV Storage and Workers R2 Storage: Edit) and CLOUDFLARE_ACCOUNT_ID (or `account_id` in wrangler.jsonc)"};const o=r.sleep??(async s=>new Promise(i=>{setTimeout(i,s)}));return{request:async(s,i)=>{for(let a=0;;a+=1){const c=await Cr({accountId:t,apiToken:n,...r.fetch===void 0?{}:{fetch:r.fetch},init:{...i,signal:AbortSignal.timeout(Rt)},path:s});if(c.ok||c.status!==429||a===It)return c;await o(Ot*2**a)}}}},Pt=r=>{const e={};for(const t of r){const n=t.indexOf("="),o=n===-1?"":t.slice(0,n);if(o==="")return{error:`--var ${t}: expected KEY=VALUE`};if(o===U)return{error:`--var ${o}: the preview sets ${o} itself; \`delete\` relies on it`};e[o]=t.slice(n+1)}return{variables:e}},$=r=>r instanceof Error?r.message:String(r),Ft=async(r,e,t,n)=>{const o=n.environment??process.env,s={},i=o[Tt],a=h=>i===void 0||i===""?Ir():yr("sha256",i).update(`${t}/${h}`).digest("hex");for(const h of n.secrets??[]){const y=o[h];if(y===void 0||y==="")return{error:`--secret ${h}: ${h} is not set in the environment, so there is no value to give the preview`};s[h]=y}const{fromLocal:c,fromPackages:d,mintableKeys:u}=await Dr(r,void 0);for(const h of[...d,...c,we])s[h]===void 0&&(u.has(h)||h===we)&&(s[h]=a(h));const f=[...new Set([...d,...c])].filter(h=>s[h]===void 0),m=(Array.isArray(e.secrets?.required)?e.secrets.required.filter(h=>typeof h=="string"):[]).filter(h=>s[h]===void 0);return m.length>0?{error:`the config requires ${m.join(", ")}, which a preview only gets when you pass ${m.map(h=>`--secret ${h}`).join(" ")}`}:{adminToken:s[we]??"",missing:f,values:s}},Mt=async(r,e,t,n)=>{const o=await Oe(r,t);if(o!=="present")throw new Error(`the record lists ${t.kind} "${t.name}" (${t.id}), which is ${o==="missing"?"gone":"now a different resource"}; run \`lunora preview delete ${e.slug}\`, then deploy again`);return n.info(`reusing preview ${t.kind} "${t.name}" for ${t.binding}`),t.id},Wt=3,xt=async(r,e,t,n)=>{let o;for(let s=1;s<=Wt;s+=1)try{await j(r,e,t);return}catch(i){o=i}try{await fr(r,n),t.resources.splice(t.resources.indexOf(n),1),await j(r,e,t).catch(()=>{})}catch{n.pending=!0}throw new Error(`${$(o)}; the ${n.kind} "${n.name}" just made could not be recorded, so it was removed again`)},Me=async(r,e,t,n,o)=>{let s=t.resources.find(i=>i.kind===n.kind&&i.binding===n.binding);if(s===void 0){if(await W(r,n.kind,n.name)!==void 0)throw new Error(`a ${n.kind} resource named "${n.name}" already exists but no preview record says this preview created it; refusing to bind or later delete it. Rename or remove it, then deploy again.`);s={binding:n.binding,id:n.kind==="r2"?n.name:"",kind:n.kind,name:n.name,pending:!0},t.resources.push(s),await j(r,e,t)}return s.id=await or(r,n.kind,n.name),delete s.pending,await xt(r,e,t,s),o.success(`created preview ${n.kind} "${n.name}" for ${n.binding}`),s.id},Kt=async(r,e,t,n,o,s,i)=>{const a=n.resources.find(c=>c.kind===o.kind&&c.binding===o.binding);if(i(),a?.pending===!0){if(await W(r,o.kind,o.name)!==void 0)throw new Error(`a ${o.kind} resource named "${o.name}" exists, but the record only claims it (a deploy was interrupted between creating it and confirming it), so nothing shows it is this preview's and it is left alone. If it is, delete it by hand, then deploy again`);return Me(r,e,n,o,s)}return a===void 0?Me(r,e,n,o,s):Mt(r,t,a,s)},Ht=async(r,e,t,n,o,s)=>{const i=new Map;for(const a of t.resources)i.set(_e(a.kind,a.binding),await Kt(r,e,t,n,a,o,s));return i},Ae=async(r,e,t)=>{const n=new Set((t?.resources??[]).map(i=>`${i.kind}:${i.name}`)),o=e.filter(i=>!n.has(`${i.kind}:${i.name}`));return(await Promise.all(o.map(async i=>await W(r,i.kind,i.name)===void 0?[]:[i]))).flat().map(i=>`${i.kind} "${i.name}"`)},vr=async(r,e,t,n,o,s,i=!0)=>{if(t!==void 0){const d=await ge(r,e,D(t,o));if(d!==void 0){if(d.appId!==t||d.name!==o)throw new Error(`the record at "${D(t,o)}" does not name this project and preview; refusing to act on it`);return d}}const a=await ge(r,e,D(n,o));if(a===void 0)return;if(a.appId!==void 0||a.production!==n||a.name!==o||a.worker!==ae(n,o))throw new Error(`the record at "${D(n,o)}" is not the one this CLI wrote for that Worker and preview; refusing to act on it`);if(t===void 0)throw new Error(`preview "${o}" is recorded under the Worker name, from before projects had an id. Add an app id to package.json (\`"lunora": { "appId": "${V()}" }\`), commit it, and run this again: it re-keys the record`);const c={...a,appId:t};return i&&(await j(r,e,c),await nr(r,e,a),s.warn(`preview "${o}" was recorded under the Worker name "${n}"; it is now recorded under project ${t}`)),c},Bt=async(r,e,t,n)=>{if(t===void 0&&n)return{code:w.USAGE,error:`a Worker named "${e.worker}" exists but no preview record says this preview deployed it; refusing to deploy over it`};if(t!==void 0&&t.worker!==e.worker)return{code:w.USAGE,error:`the record for preview "${e.slug}" names Worker "${t.worker}", not "${e.worker}" (the app was renamed since it was deployed); run \`lunora preview delete ${e.slug}\` first, then deploy it again`};const o=await Ae(r,e.resources,t);return o.length===0?void 0:{code:w.FAILURE,error:`${o.join(", ")} already exist(s) but no preview record read says this preview created it, so nothing was written or deployed. If a deploy of this preview just ran, its record may not have reached this location yet: run the deploy again. Otherwise rename or remove it, then deploy again.`}},Gt=r=>{const e=Er(ee(br(),"lunora-preview-")),t=ee(e,"secrets.json");return ne(t,JSON.stringify(r),{encoding:"utf8",mode:384}),{directory:e,path:t}},Vt=r=>({environment:r.environment,name:r.slug,resources:r.resources,worker:r.worker}),Yt=(r,e,t)=>{r.info(`dry run: preview "${e.slug}" would deploy Worker "${e.worker}" (env.${e.environment}, its own Durable Object namespace)`);for(const s of e.resources)r.info(` ${s.kind} "${s.name}" for ${s.binding} (created, or reused when this preview's record lists it)`);const{vars:n}=t,o=n===void 0?t:{...t,vars:Object.fromEntries(Object.keys(n).map(s=>[s,"<redacted>"]))};r.info(` env.${e.environment}: ${JSON.stringify(o)}`)},hr=(r,e)=>`another run is deploying or deleting preview "${r}" (its hold lapses at ${e}); wait for it. A run that crashed lets go by itself`,mr=(r,e,t)=>{const n=`the hold on preview "${e}" was lost while this run worked, so another run may have changed it too; check it with \`lunora preview list\` and run \`lunora preview delete ${e}\` if it looks wrong`;return r.error(n),{...t,code:t.code===w.SUCCESS?w.FAILURE:t.code,error:n}},Jt=async(r,e,t)=>{try{const n=await ue(r,e,U);return n.exists&&n.marker===t?"present":"absent"}catch{return"unknown"}},ye=(r,e)=>{if(r.lost)throw new Error(`the hold on preview "${e}" can no longer be vouched for, so this run stopped before changing anything more; check it with \`lunora preview list\`, then run again`)},Xt=async(r,e,t,n,o,s)=>{const{config:i,cwd:a,wranglerPath:c}=r,{logger:d}=n,{appId:u,hold:f,namespace:l,secrets:m,subdomain:h}=s;let y,_;const v=`https://${e.worker}.${h}.workers.dev`;try{const g=await ue(o,e.worker,U);if(g.exists&&g.marker!==M(e))return p(d,`a Worker named "${e.worker}" already exists and was not deployed as preview "${e.slug}"; refusing to deploy over it`);const b=new Date().toISOString(),T=await vr(o,l,u,e.production,e.slug,d),A=await Bt(o,e,T,g.exists);if(A!==void 0)return p(d,A.error,A.code);const N=B(r.committed??i,Se(e.worker,T?.resources??[]));if(N.length>0)return p(d,`preview "${e.slug}" would bind production's ${N.join(", ")}; refusing to deploy, nothing was created`);y=T??{createdAt:b,environment:e.environment,name:e.slug,production:e.production,appId:u,...e.sourceEnv===void 0?{}:{sourceEnv:e.sourceEnv},resources:[],updatedAt:b,version:1,worker:e.worker},Lt(y,n,e.slug),await f.verify(),await j(o,l,y),_=await Ht(o,l,e,y,d,()=>{ye(f,e.slug)})}catch(g){return p(d,`preview "${e.slug}": ${$(g)}`,w.FAILURE)}const O=B(r.committed??i,[..._].map(([g,b])=>({kind:g.slice(0,g.indexOf(":")),value:b})));if(O.length>0)return p(d,`preview "${e.slug}" would bind production's ${O.join(", ")}; refusing to deploy`);const x=L(c,"utf8");ne(c,Tr(x,["env",e.environment],qe(i,e,_,t,v)),"utf8");const S=Gt(m.values),K=ie(()=>{F(S.directory,{force:!0,recursive:!0})});try{ye(f,e.slug);const g=await Ct(r.baseline,async()=>Pr({allowSchemaDrift:!0,cwd:a,env:e.environment,fetchImpl:n.adminFetch,format:n.format,healthCheck:!0,healthFetch:n.healthFetch,healthSleep:n.healthSleep,interactive:!1,isolatedPreview:!0,logger:d,migrate:!0,migrateToken:m.adminToken,migrateUrl:v,migrateYes:!0,secretsFile:S.path,spawner:n.spawner,target:"cloudflare"})),b=g.code===0?"present":await Jt(o,e.worker,M(e));if(b==="present"?y.url=v:b==="absent"&&delete y.url,y.updatedAt=new Date().toISOString(),await f.verify(),await j(o,l,y),g.code!==0)return{code:g.code,data:{preview:C(y)},error:g.error??`preview "${e.slug}" did not deploy`}}catch(g){return p(d,`preview "${e.slug}": ${$(g)}`,w.FAILURE)}finally{F(S.directory,{force:!0,recursive:!0}),K()}if(n.seed===!0){const g=await Fr({cwd:a,fetchImpl:n.adminFetch,format:n.format,logger:d,prod:!0,token:m.adminToken,url:y.url,yes:!0});if(g.code!==0)return{code:g.code,data:{preview:C(y)},error:g.error??`seeding preview "${e.slug}" failed`}}return d.success(`preview "${e.slug}" is live at ${y.url??v} — remove it with \`lunora preview delete ${e.slug}\``),{code:w.SUCCESS,data:{preview:C(y)}}},zt=async(r,e,t,n,o)=>{const{appId:s,config:i,cwd:a}=r,{logger:c}=n;if(s===void 0)return p(c,"the project has no id to key the preview record by");const d=await Ft(a,i,e.worker,n);if(d.error!==void 0)return p(c,d.error);for(const v of d.missing)c.warn(`the preview has no ${v}: production's is never copied. Pass --secret ${v} with ${v} set in the environment to give it one.`);let u,f,l,m;try{f=await St(o),u=await Re(o,!0)??"",l=await sr(o);const v=await ar(o,l,s,e.slug);if(v.held!==void 0)return p(c,hr(e.slug,v.held));({token:m}=v)}catch(v){return p(c,`preview "${e.slug}": ${$(v)}`,w.FAILURE)}const h={lost:!1,verify:async()=>{if(!await ur(o,l,s,e.slug,m))throw h.lost=!0,new Error(`the hold on preview "${e.slug}" is no longer this run's, so its record was left as the other run has it`)}},y=dr(o,l,s,e.slug,m,()=>{h.lost=!0}),_=ie(async()=>{y(),await te(o,l,s,e.slug,m)});try{const v=await Xt(r,e,t,n,o,{appId:s,hold:h,namespace:u,secrets:d,subdomain:f});return h.lost?mr(c,e.slug,v):v}finally{y(),await te(o,l,s,e.slug,m),_()}},Qt=(r,e)=>{if(r.appId!==void 0)return{appId:r.appId};const t=e.environment??process.env,n='package.json has a "lunora.appId" that is not a UUID, or no `lunora` object this CLI can add one to';if(e.dryRun===!0)return e.logger.info(" app id: none yet; the first deploy writes one to package.json for you to commit"),{appId:void 0};if(t.CI!==void 0&&t.CI!==""&&t.CI!=="false")return{error:`package.json has no "lunora.appId", and a CI run cannot mint one (its checkout is discarded, so the preview could never be found again to delete). Commit \`"lunora": { "appId": "${V()}" }\` to package.json`};const o=Vr(r.cwd);return o===void 0?{error:n}:(e.logger.warn(`wrote an app id to package.json ("lunora.appId"); commit it so CI finds this project's previews`),{appId:o})},Zt=async(r,e,t)=>{const{cwd:n}=r,{logger:o}=e,s=Pt(e.vars??[]);if(s.error!==void 0)return p(o,s.error);const{variables:i}=s,a=Wr(n);if(a.error!==void 0)return p(o,a.error);const c=Qt(r,e);if(c.error!==void 0)return a.release(),p(o,c.error);let d=()=>{};const u=ee(n,"lunora",Sr);let f;try{f=Q(u)?L(u):void 0}catch(v){return a.release(),p(o,`could not read ${u}: ${$(v)}`)}let l=!1,m;const h={begin:()=>{l=!0},end:()=>{m=Q(u)?L(u):void 0,l=!1}},y=()=>{try{if(d(),f===void 0){const v=Q(u)?L(u):void 0;v!==void 0&&(l||m!==void 0&&v.equals(m))&&F(u,{force:!0})}else f.equals(Q(u)?L(u):Buffer.alloc(0))||ne(u,f)}finally{a.release()}},_=ie(y);try{d=Nr(n),await Lr(n,o,void 0,"cloudflare",void 0,void 0,!0);const{services:v}=Rr(n);if(v.length>0)return p(o,`this app cannot be previewed in isolation yet: it declares lunora.config services (${v.map(A=>A.name).join(", ")}), which a preview would share with production`);const O=$e(r.wranglerPath).parsed??r.config,x=e.env===void 0?{config:O}:ce(O,e.env);if(x.error!==void 0)return p(o,x.error);const{config:S}=x,K=Ze(S,t,e.env);if(K.error!==void 0)return p(o,K.error);const{plan:g}=K,b=B(O,Se(g.worker,g.resources));if(b.length>0)return p(o,`preview "${g.slug}" names collide with production's ${b.join(", ")}; name the preview differently`);if(e.dryRun===!0){const A=new Map(g.resources.map(N=>[_e(N.kind,N.binding),`<${N.name}>`]));return Yt(o,g,qe(S,g,A,i,`https://${g.worker}.<your-subdomain>.workers.dev`)),o.info(` secrets: every one Lunora can mint, fresh${(e.secrets??[]).length>0?`; from the environment: ${(e.secrets??[]).join(", ")}`:""}`),{code:w.SUCCESS,data:{plan:Vt(g)}}}const T=Te(e,S);return T.error!==void 0?p(o,T.error,w.AUTH):await zt({...r,baseline:h,committed:O,config:S,appId:c.appId},g,i,e,T.request)}finally{y(),_()}},qt=async(r,e,t)=>{try{if(e.pending===!0)return await W(r,e.kind,e.name)===void 0?!0:(t.error(`${e.kind} "${e.name}" exists, but the record only claims it (a deploy was interrupted before confirming it), so nothing shows it is this preview's: it was left alone. If it is, delete it by hand, then run \`lunora preview delete\` again`),!1);const n=await Oe(r,e);return n==="changed"?(t.error(`${e.kind} "${e.name}" (${e.id}): the recorded id now names a different resource, so it was left alone`),!1):(n==="present"&&(await fr(r,e),t.success(`deleted ${e.kind} "${e.name}"`)),!0)}catch(n){return t.error(`deleting ${e.kind} "${e.name}" failed (${$(n)}); it stays on record for the next \`lunora preview delete\``),!1}},en=async(r,e,t,n,o,s,i)=>{try{s();const u=await ue(r,t.worker,U);if(u.exists&&u.marker!==M({production:t.production,slug:t.name}))return p(o,`Worker "${t.worker}" was not deployed as preview "${t.name}"; refusing to delete anything`);u.exists&&(await _t(r,t.worker),o.success(`deleted Worker "${t.worker}"`))}catch(u){return p(o,`deleting Worker "${t.worker}" failed (${$(u)}); its resources were left in place`,w.FAILURE)}const a=[];for(const u of t.resources)s(),await qt(r,u,o)||a.push(u);const c={...t,resources:a,updatedAt:new Date().toISOString()};let d;try{d=await Ae(r,n?.resources??[],t),d.length===0&&(await i(),await(a.length===0?nr(r,e,t):j(r,e,c)))}catch(u){return p(o,`preview "${t.name}": ${$(u)}`,w.FAILURE)}return d.length>0?p(o,`${d.join(", ")} exist(s) under this preview's names but not in the record read (a deploy may have just recorded it); the record was left as it is — run \`lunora preview delete ${t.name}\` again. If that keeps failing, the resource was never recorded (a deploy stopped between creating and recording it): delete it by hand, then run delete once more`,w.FAILURE):a.length>0?{code:w.FAILURE,data:{preview:C(c)},error:`${String(a.length)} preview resource(s) could not be deleted`}:{code:w.SUCCESS,data:{deleted:C(t)}}},rn=async(r,e,t)=>{const n=[{config:e}];for(const s of Object.keys(e.env??{})){const i=ce(e,s);i.config!==void 0&&n.push({config:i.config,env:s})}const o=await Promise.all(n.map(async s=>{const{names:i}=be(s.config,t);if(i===void 0)return[];const a=Qe(s.config,i.worker),c=await ue(r,i.worker,U),d=await Ae(r,a,void 0);return[...c.exists&&c.marker===M(i)?[`Worker "${i.worker}" exists`]:[],...d.map(u=>`${u} exists`)]}));return[...new Set(o.flat())]},tn=(r,e)=>{const t=e.sourceEnv===void 0?{config:r}:ce(r,e.sourceEnv);return t.config===void 0?void 0:Ze({...t.config,name:e.production},e.name,e.sourceEnv).plan},nn=async(r,e,t,n)=>{const o=await rn(r,e,t);return o.length===0?void 0:p(n,`no record of preview "${t}" could be read, but ${o.join(" and ")}; nothing was deleted. If a deploy just ran, its record may not have reached this location yet: run delete again. Otherwise delete from a checkout whose package.json has the lunora.appId that deployed it`,w.FAILURE)},on=(r,e)=>{e.info(`dry run: would delete Worker "${r.worker}" and its Durable Objects`);for(const t of r.resources)e.info(` ${t.kind} "${t.name}" (${t.binding})`);return{code:w.SUCCESS,data:{plan:C(r)}}},sn=(r,e,t)=>e.worker===ae(e.production,e.name)&&e.appId===r.appId&&e.name===t,We=async(r,e,{hold:t,names:n,namespace:o,request:s})=>{const{config:i}=r,{logger:a}=e;let c;try{if(c=o===void 0?void 0:await vr(s,o,r.appId,n.production,n.slug,a,e.dryRun!==!0),o===void 0||c===void 0){const l=await nn(s,i,n.slug,a);if(l!==void 0)return l}}catch(l){return p(a,`preview "${n.slug}": ${$(l)}`,w.FAILURE)}if(o===void 0||c===void 0)return p(a,`no preview "${n.slug}" of "${n.production}" is on record; nothing was deleted`,w.NOT_FOUND);const d=Nt(r,c);if(!sn(r,c,n.slug)||d.length>0)return p(a,`the record for preview "${n.slug}" names ${d.length>0?`production's ${d.join(", ")}`:`Worker "${c.worker}", which is not the Worker of this preview`}; refusing to delete anything`);if(r.requireOrigin===!0&&!jt(c,wr(e.environment??process.env,n.slug)))return p(a,Ut(n.slug),w.FAILURE);let u;try{u=await Promise.all(c.resources.map(async l=>Oe(s,l)))}catch(l){return p(a,`preview "${n.slug}": ${$(l)}`,w.FAILURE)}const f=c.resources.filter((l,m)=>u[m]==="changed");return f.length>0?p(a,`the record for preview "${n.slug}" lists ${f.map(l=>`${l.kind} ${l.id}`).join(", ")}, which no longer name(s) the recorded resource; refusing to delete anything`):e.dryRun===!0?on(c,a):en(s,o,c,tn(i,c),a,()=>{ye(t,c.name)},t.verify)},P=async(r,e,t)=>{const{config:n}=r,{logger:o}=e,s=be(n,t);if(s.error!==void 0)return p(o,s.error);const{names:i}=s,a=pr(n,e);if(a.error!==void 0)return p(o,a.error,e.env===void 0?w.AUTH:w.USAGE);const c=Te(e,a.config);if(c.error!==void 0)return p(o,c.error,w.AUTH);const{request:d}=c;let u;try{u=await Re(d,!1)}catch(v){return p(o,`preview "${i.slug}": ${$(v)}`,w.FAILURE)}if(r.appId===void 0||u===void 0||e.dryRun===!0)return We(r,e,{hold:{lost:!1,verify:()=>Promise.resolve()},names:i,namespace:u,request:d});const{appId:f}=r;let l,m;try{l=await sr(d);const v=await ar(d,l,f,i.slug);if(v.held!==void 0)return p(o,hr(i.slug,v.held));({token:m}=v)}catch(v){return p(o,`preview "${i.slug}": ${$(v)}`,w.FAILURE)}const h={lost:!1,verify:async()=>{if(!await ur(d,l,f,i.slug,m))throw h.lost=!0,new Error(`the hold on preview "${i.slug}" is no longer this run's, so its record was left as the other run has it`)}},y=dr(d,l,f,i.slug,m,()=>{h.lost=!0}),_=ie(async()=>{y(),await te(d,l,f,i.slug,m)});try{const v=await We(r,e,{hold:h,names:i,namespace:u,request:d});return h.lost?mr(o,i.slug,v):v}catch(v){return p(o,`preview "${i.slug}": ${$(v)}`,w.FAILURE)}finally{y(),await te(d,l,f,i.slug,m),_()}},an=async(r,e)=>{const{config:t}=r,{logger:n}=e,o=typeof t.name=="string"?t.name:"",s=pr(t,e);if(s.error!==void 0)return p(n,s.error,e.env===void 0?w.AUTH:w.USAGE);const i=Te(e,s.config);if(i.error!==void 0)return p(n,i.error,w.AUTH);let a;try{const c=await Re(i.request,!1),d={records:[],unreadable:[]},u=c===void 0||r.appId===void 0?d:await Le(i.request,c,r.appId),f=c===void 0?d:await Le(i.request,c,o);a=[...u.records,...f.records],f.records.length>0&&n.warn(`${String(f.records.length)} preview(s) are recorded under the Worker name, from before projects had an id: ${f.records.map(l=>l.name).join(", ")}. ${r.appId===void 0?"Add an app id to package.json, then run":"Run"} \`lunora preview delete <name>\` (or deploy it again) to re-key each`);for(const l of[...u.unreadable,...f.unreadable])n.warn(`the registry key "${l}" is not a preview record this CLI wrote; it is not listed, and \`delete\` will refuse it`)}catch(c){return p(n,$(c),w.FAILURE)}a.length===0&&n.info(`no previews of "${o}" are on record`);for(const c of a)n.info(`${c.name} ${c.url??"(not deployed)"} Worker ${c.worker}, ${String(c.resources.length)} resource(s), updated ${c.updatedAt}`);return{code:w.SUCCESS,data:{previews:a.map(c=>C(c))}}},cn=/^[\dA-Za-z][\w-]{0,62}$/u,dn=r=>{const e=ke(r);if(e===void 0)return{error:'its package.json has no readable "lunora.appId"'};const t=He(r);let n;try{n=t===void 0?void 0:$e(t).parsed}catch{n=void 0}const o=n?.name;return n===void 0||typeof o!="string"||!cn.test(o)?{error:"its wrangler config has no readable, valid Worker name"}:{identity:{appId:e,config:n,production:o}}},un=async(r,e,t,n)=>{const o=[...e];if(o.some(i=>i.code===w.SUCCESS)){for(const[i,a]of o.entries())if(a.code!==w.SUCCESS&&a.code!==w.NOT_FOUND){const c=await P(r[i],t,n);o[i]=c.code===w.NOT_FOUND?{code:w.SUCCESS}:c}}const s=o.find(i=>i.code!==w.SUCCESS&&i.code!==w.NOT_FOUND);return s!==void 0?s:o.find(i=>i.code===w.SUCCESS&&i.data!==void 0)??o.find(i=>i.code===w.SUCCESS)??p(t.logger,`no preview "${n}" is on record under this checkout's project or under the pull request head's, whose id or Worker name differs; nothing was deleted`,w.FAILURE)},ln=async(r,e,t,n)=>{const o=dn(n);if(o.error!==void 0){e.logger.warn(`the pull request head's identity could not be read (${o.error}); looking for the preview under this checkout's only`);const l=await P(r,e,t);return l.code===w.NOT_FOUND?p(e.logger,`no preview "${t}" is on record under this checkout's project, and the pull request head's identity could not be read (${o.error}), so it cannot be ruled out under that; nothing was deleted`,w.FAILURE):l}const{appId:s,config:i,production:a}=o.identity,c={...r,alsoProduction:i};if(s===r.appId&&a===r.config.name)return P(c,e,t);const d={...c,appId:s,config:{...r.config,...typeof i.account_id=="string"?{account_id:i.account_id}:{},name:a},requireOrigin:!0},u=[c,d],f=[await P(c,e,t),await P(d,e,t)];return un(u,f,e,t)},fn=async r=>{const{logger:e,subcommand:t}=r;if(!Fe.includes(t))return p(e,`usage: lunora preview <${Fe.join(" | ")}> [name]`);const n=Dt(r);return n.error!==void 0?p(e,n.error):r.headDir!==void 0&&t!=="delete"?p(e,"--head-dir applies to `lunora preview delete` only"):t==="list"?an(n.context,r):r.name===void 0||r.name===""?p(e,`usage: lunora preview ${t??""} <name> — e.g. the pull request number`):t==="deploy"?Zt(n.context,r,r.name):r.headDir===void 0?P(n.context,r,r.name):ln(n.context,r,r.name,r.headDir)},Rn=jr(async({argument:r,cwd:e,format:t,logger:n,options:o})=>{const s=await fn({cwd:e,dryRun:o.dryRun===!0,env:o.env,format:t,headDir:o.headDir,logger:n,name:r[1],secrets:o.secret??[],seed:o.seed===!0,subcommand:r[0],target:o.target,vars:o.var});return{code:s.code,data:s.data,error:s.error}});export{Rn as execute,fn as runPreviewCommand};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{projectConfigCandidatePaths as
|
|
2
|
-
${Y(s)}`),a=
|
|
3
|
-
${
|
|
4
|
-
${Y(
|
|
5
|
-
${
|
|
6
|
-
${Q(d)}`),a.close()}),
|
|
7
|
-
${Q(a)}`)}return
|
|
8
|
-
`)),n.length>0&&o.write(n),o.pipe(t),t.pipe(o)});o.setTimeout(Jt,()=>o.destroy()),o.on("error",()=>t.destroy()),t.on("error",()=>o.destroy())},Kt=async e=>{const t=e.host??"127.0.0.1",n=t==="127.0.0.1"||t==="localhost"||t==="::1",r=new URL(e.workerOrigin);let o=U(e.logger,import.meta.url),a=V(import.meta.url);const d=Ge(e.cwd,()=>Buffer.from(Xe({adminToken:n?Je(e.cwd):void 0,basePath:"/",dataEditable:n,rulesInstalled:te(e.cwd).installed,runAsIdentity:n,schemaEditable:n,scriptSrc:"/studio.js",styleHref:"/styles.css"}))),s=(c,l,g,h,p)=>{it(c,l,g,h,p,a)},i=(c,l,g)=>{c.statusCode=l,c.setHeader("Content-Type","text/plain"),c.end(g)},u=(c,l,g,h)=>{if(n){qe(c,l,g,e.cwd,{apiSpec:e.apiSpec,lazyFunctions:P(e.lazyFunctions)});return}i(l,403,h)},S=(c,l,g)=>{const h=c==="/styles.css",p=et(c);if(!h&&!p)return!1;const y=V(import.meta.url);if(y!==a&&(o=U(e.logger,import.meta.url),a=y),o===void 0)return i(g,501,"Lunora studio assets not found — install and build @lunora/studio."),!0;if(h)return s(l,g,o.styles,"text/css; charset=utf-8","styles.css"),!0;const b=c.slice(c.lastIndexOf("/")+1),_=tt(b,import.meta.url);return _===void 0?(i(g,404,"Not found"),!0):(s(l,g,_,rt(b),b),!0)};d();const m=gt((c,l)=>{const g=B(c.url??"/");if(n){const p=z(c,e.logger);if(p!==void 0){i(l,403,p);return}}if(g.startsWith(Z)){if(!n){i(l,403,"Lunora studio: the worker admin proxy is only available on a loopback bind.");return}q(c,l,r);return}if(g===Ye){u(c,l,nt,"Lunora schema editing is only available on loopback hosts in dev.");return}if(g===Ke){u(c,l,ot,"Lunora policy scaffolding is only available on loopback hosts in dev.");return}if(g===Qe){u(c,l,at,"Lunora data seeding is only available on loopback hosts in dev.");return}if(S(g,c,l))return;const h=c.headers["sec-fetch-mode"];if(n&&typeof h=="string"&&h!=="navigate"){q(c,l,r);return}Ze(l,d())});return m.on("upgrade",(c,l,g)=>{if(!n||z(c,e.logger)!==void 0){l.destroy();return}B(c.url??"").startsWith(Z)?Yt(c,l,g,r):l.destroy()}),await new Promise((c,l)=>{m.once("error",g=>{g.code==="EADDRINUSE"?l(new Error(`studio port ${String(e.port)} is already in use — pass a different port with --port, or stop the process using it`,{cause:g})):l(g)}),m.listen(e.port,t,()=>{c()})}),{close:()=>new Promise(c=>{m.close(()=>{c()})}),url:`http://${t==="0.0.0.0"||t==="::"?"localhost":t}:${String(e.port)}`}},Qt=async e=>{const{cwd:t,logger:n,origin:r,signal:o}=e,a=e.probe??St,d=pt(e.readyTimeoutMs),s=Date.now()+d,i=()=>o.aborted;for(;!i()&&Date.now()<s;){let u=!1;try{u=await a(r,o)}catch{}if(i()||Date.now()>=s)break;if(u)return re(t,{readyAt:new Date().toISOString()},{expectedPid:process.pid}),pe(t)?.readyAt!==void 0;try{await yt(Et,void 0,{signal:o})}catch{}}return i()||n.warn(`server did not answer on ${r} within ${String(Math.round(d/1e3))}s — .lunora/dev.json will have no readyAt, so anything polling for readiness will keep waiting. The server itself is unaffected; raise LUNORA_DEV_READY_TIMEOUT_MS if it is just slow to boot.`),!1},Zt=(e,t,n,r)=>{e.info(""),e.success("Lunora dev"),e.info(` ➜ Worker: ${t.workerOrigin}`),n!==void 0&&e.info(` ➜ Studio: ${n}`),t.runsCodegenWatch&&e.info(" ➜ Codegen: watching lunora/"),r!==void 0&&e.info(` ➜ Supervisor: ${r} (needs) · ${ae} (status)`),t.remote.enabled&&(t.remote.bindings.length>0?e.info(` ➜ Remote: ${t.remote.bindings.join(", ")} → deployed worker`):t.remote.reason!==void 0&&e.warn(` ➜ Remote: requested but inactive (${t.remote.reason}) — running fully local`));for(const o of t.remote.withheld)e.warn(` ➜ ${He(o)}`);e.info("")},qt=(e,t)=>{te(t).installed||!Ne()||(e.info(` ⓘ ${je}`),e.info(""))},er=async(e,t,n)=>{let r;try{r=Te(await De({cache:n,projectRoot:t,schemaDir:Re(t)}))}catch{}if(r!==void 0)try{const a=(e.ensureExample??$e)(t,r);if(a.length>0){const d=r.map(s=>s.includes(":")?`${s.replace(":"," (")})`:s);e.logger.info(`Updated .dev.vars.example with secrets for: ${d.join(", ")} (${a.join(", ")})`)}}catch{}(await(e.ensureEnv??Ce)({confirm:wt(),cwd:t,info:a=>{e.logger.info(a)}})).status==="declined"&&!Ae()&&e.logger.info(`hint: ${H} was not scaffolded (non-interactive run). Copy ${Oe} → ${H} and fill in secrets, or run \`lunora dev\` in an interactive terminal to scaffold automatically.`);try{(e.fillSecrets??Fe)({cwd:t,secretSources:r,info:a=>{e.logger.info(a)}})}catch{}},tr=(e,t,n,r,o)=>{if(e.flavor!=="wrangler")return;r!==void 0&&re(t,{studioUrl:r});let a;try{a=It(t,n)}catch{}return Zt(n,e,r,o),a},rr=(e,t)=>{const n=Number(process.env[I]),r=ke(t,{background:process.env[ne]==="1",logFile:process.env[Pe],mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:e.workerOrigin},Number.isInteger(n)&&n>0?{supersedePid:n}:void 0);return r.ok?void 0:r.existing},nr=e=>{const{cwd:t,destination:n,logger:r,plan:o}=e,a=n!==void 0,d=n??Le,s=Ue({destination:d,dev:{...o.flavor==="wrangler"?{origin:o.workerOrigin}:{},statusFile:ae},logger:a?r:{...r,success:()=>{},warn:()=>{}},projectRoot:t});return s.error!==void 0?a?s:(r.debug?.(`skipped the default binding manifest: ${s.error}`),{}):{written:d}},or=async(e,t,n,r)=>{if(t.studioEnabled)try{return await(e.startStudio??Kt)({apiSpec:e.apiSpec,lazyFunctions:P(e.lazyFunctions),cwd:n,logger:{warnOnce:o=>{r.warn(o)}},port:t.studioPort,workerOrigin:t.workerOrigin})}catch(o){r.warn(`studio server failed to start (${o instanceof Error?o.message:String(o)}) — continuing without it`);return}},ar=e=>{const t=[e.runsCodegenWatch?"codegen watch":void 0,e.studioEnabled?"studio":void 0].filter(r=>r!==void 0);return`--no-worker: not starting wrangler. ${t.length>0?`${t.join(" + ")} running`:"nothing else to run"}; your task runner owns the worker on ${e.workerOrigin}.`},ir=(e,t,n,r,o)=>{if(!(e.sidecar===void 0||!Ft(t)))try{ee({apiSpec:t.apiSpec,lazyFunctions:P(t.lazyFunctions),lunoraDirectory:"lunora",projectRoot:n,target:o,...ie(n)})}catch(a){r.warn(`codegen (pre-sidecar) failed: ${a instanceof Error?a.message:String(a)} — the framework dev server will retry`)}},sr=e=>e.flavor==="vite"?"starting vite dev (worker + studio + codegen run inside Vite via @lunora/vite)":`starting ${e.wrangler.tag} dev + studio`,cr=(e,t)=>{e.ipv4LoopbackForced&&t.info("no IPv6 loopback (::1) on this host — binding the worker to 127.0.0.1 (--ip) so wrangler dev doesn't crash. Pin `dev.ip` in wrangler.jsonc to override.")},dr=async(e,t,n,r)=>{if(e.celldSession!==!0||e.workerEnabled)return!0;try{for(const o of await(t.writeCelldProjection??Ie)({projectRoot:n}))r.warn(o);return!0}catch(o){return r.error(`could not write the celld projection: ${o instanceof Error?o.message:String(o)}`),!1}},lr=async(e,t,n,r,o)=>{try{return e.celldSession===!0?await Lt({logger:o,port:e.workerPort,projectRoot:r,start:t.startCelldSession}):n(e.wrangler,o)}catch(a){o.error(`could not start the worker: ${a instanceof Error?a.message:String(a)}`);return}},ur=async(e,t,n)=>{const r=await(e.startHyperdrive??We)({onWarning:o=>{n.warn(o)},projectRoot:t});return r!==void 0&&r.bindings.length>0&&n.info(`Hyperdrive ${r.bindings.join(", ")}: embedded Postgres (PGlite), data in .wrangler/state/lunora-pglite. Set CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> to use your own.`),r},fr=async e=>{const{logger:t}=e,n=be(),r=e.cwd??process.cwd(),o=e.flavor??Pt(r),a=ut(r,e.target);if(a.target===void 0)throw new Error(a.error??"unknown deploy target");const{target:d}=a,s=Tt(d,o,t);s==="wrangler"&&await Nt(r,t,void 0,d,void 0,n);const i=await Dt({...e,flavor:s,target:d}),u={remoteCleanup:i.remote.cleanup,serviceConfigCleanup:i.serviceConfigCleanup};try{const S=Number(process.env[I]),m=oe(r);if(m!==void 0&&m.pid!==process.pid&&m.pid!==S)return j(t,m),{code:0,plan:i};const c=rr(i,r);if(c!==void 0)return j(t,c),{code:0,plan:i};(i.flavor==="vite"||i.flavor==="framework-worker")&&(i.wrangler.env={...i.wrangler.env,[I]:String(process.pid)}),i.workerEnabled&&d==="cloudflare"&&(u.hyperdrive=await ur(e,r,t)),await er(e,r,n),t.info(sr(i)),Rt(t,r),cr(i,t),i.runsCodegenWatch&&(u.codegen=(e.startCodegen??Gt)({apiSpec:e.apiSpec,lazyFunctions:P(e.lazyFunctions),jsonLogs:e.jsonLogs,logger:t,projectRoot:r,target:d})),u.studio=await or(e,i,r,t);const l=nr({cwd:r,destination:e.emitBindings,logger:t,plan:i});if(l.error!==void 0)return t.error(l.error),{code:$.USAGE,plan:i};await u.codegen?.ready;const g=u.studio?.url;i.frameworkHint!==void 0&&t.warn(i.frameworkHint);const h=()=>{i.flavor==="wrangler"&&(u.readyProbe=new AbortController,Qt({cwd:r,logger:t,origin:i.workerOrigin,probe:e.probeReady,signal:u.readyProbe.signal}))};if(!await dr(i,e,r,t))return{code:$.FAILURE,plan:i};if(!i.workerEnabled)return h(),t.info(ar(i)),u.tunnel=X({cwd:r,logger:t,plan:i,tunnel:e.tunnel}),{code:await(e.waitForInterrupt??$t)(t),plan:i};ir(i,e,r,t,d);const p=e.startWorker??Ct,y=await lr(i,e,p,r,t);if(y===void 0)return{code:$.FAILURE,plan:i};const b=i.sidecar===void 0?void 0:p(i.sidecar,t);return h(),u.containerLogs=tr(i,r,t,g,l.written),u.tunnel=X({cwd:r,logger:t,plan:i,tunnel:e.tunnel}),qt(t,r),{code:await At(y,b,t),plan:i}}finally{u.readyProbe?.abort(),_e(r,process.pid),await Ot(u)}},gr=e=>({codegen:e.codegen===!1?!1:void 0,studio:e.studio===!1?!1:void 0,worker:e.worker===!1?!1:void 0}),vr=e=>{if(e.local===!0&&e.remote===!0)return"`--local` and `--remote` are mutually exclusive — pass at most one.";const{entries:t,invalid:n}=ce(e.allowMail);if(n.length>0)return`\`--allow-mail\` takes email addresses or '*@domain' — not ${n.map(r=>`"${r}"`).join(", ")}.`;if(t.length>0&&e.tunnel!==!0)return"`--allow-mail` only applies to a tunnel — add `--tunnel`."},mr=e=>e.tunnel===!0?{allowMail:ce(e.allowMail).entries}:void 0,Hr=lt(async({argument:e,cwd:t,logger:n,options:r})=>{const o=r.json===!0,a=bt({cwd:t,json:o,lines:r.lines,logger:n,subcommand:e[0]});if(a!==void 0)return a;const d=process.env[ne]==="1",s=d?void 0:Se(),i=o||s!==void 0;i&&ft(),s!==void 0&&r.background!==!0&&n.info(`AI agent detected (${s.name} via ${s.variable}) — starting the dev server in background mode with JSON logs. Set LUNORA_AGENT_MODE=0 to opt out.`);const u=vr(r);if(u!==void 0)return n.error(`dev: ${u}`),{code:$.USAGE};const S=mr(r),m=r.local===!0?!1:Me({configPreference:Ee(t),envValue:process.env.LUNORA_REMOTE,flag:r.remote});if(!d&&(r.background===!0||s!==void 0)){const c=oe(t);return c!==void 0?(j(n,c),{code:0}):(S?.allowMail.length===0&&_t(n),kt({cwd:t,jsonLogs:i,logger:n,options:r,remote:m}))}return fr({apiSpec:xe(r.apiSpec),cwd:t,emitBindings:r.emitBindings,inspectorPort:r.inspectorPort,lazyFunctions:P(r.lazyFunctions),jsonLogs:i,local:r.local,logger:n,port:r.port,remote:m,target:r.target,tunnel:S,workerPort:r.workerPort,...gr(r)})});export{vr as devFlagConflict,Hr as execute,gr as negatableDevFlags,fr as runDevCommand};
|
|
1
|
+
import{projectConfigCandidatePaths as ee,readServiceBindings as ve,createCodegenProject as me,refreshCodegenProject as he,findTsconfig as we,PROJECT_CONFIG_FILENAMES as ye,runCodegen as re}from"@lunora/codegen";import{runPostCodegenHook as pe,resolveLazyFunctions as D,detectAgentRules as te,resolveSchemaDirectory as C,updateDevServerState as oe,readDevServerState as Se,DEV_DAEMON_ENV as ne,detectAiAgent as Ee,readProjectRemotePreference as be,readLiveDevServerState as ae,createDiscoveryCache as Pe,DEV_HANDOFF_ENV as I,clearDevServerState as Re,claimDevServerState as _e,DEV_LOG_FILE_ENV as De,secretSourcesFromBindings as ke,inferLunoraBindings as Te,ensureDevVarsExample as $e,ensureDevVariables as Ce,isInteractive as Ae,DEV_VARS_FILE as H,DEV_VARS_EXAMPLE_FILE as Oe,fillDevSecrets as Fe,DEV_BINDINGS_FILE as Le,DEV_STATE_FILE as ie,writeCelldDevProjection as Ie,claimAgentRulesHint as Ne,AGENT_RULES_HINT as je}from"@lunora/config";import{WRANGLER_FILES as Me,wranglerCodegenInputs as se,resolveRemoteEnabled as We,startLocalHyperdrive as He,describeWithheld as xe}from"@lunora/config/cloudflare";import{p as Ue}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{w as Ve}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{createHash as Be}from"node:crypto";import{existsSync as A,watch as N,readFileSync as ce,statSync as ze}from"node:fs";import{join as R,dirname as M}from"node:path";import{STUDIO_WRITE_TEMP_SUFFIX as Ge,consumeStudioWrite as x,loadStudioAssets as U,studioAssetsStamp as V,createStudioDocumentCache as Xe,renderStudioHtml as Je,resolveAdminToken as Ye,requestPathname as B,transportRejectionReason as z,SCHEMA_EDIT_ENDPOINT as Ke,POLICY_SCAFFOLD_ENDPOINT as Qe,SEED_ENDPOINT as Ze,ADVISOR_PROCEDURES_ENDPOINT as qe,sendStudioDocument as er,serveJsonHandler as rr,isStandaloneModulePath as tr,readStandaloneAsset as or,assetContentType as nr,handleSchemaEditRequest as ar,handlePolicyScaffoldRequest as ir,handleSeedRequest as sr,handleAdvisorProceduresRequest as cr,sendStudioAsset as dr}from"@lunora/config/studio-host";import{LunoraError as lr}from"@lunora/errors";import{ts as G}from"ts-morph";import{a as ur}from"../packem_shared/codegen-error-AmH54ofi.mjs";import{a as fr}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{d as gr}from"../packem_shared/command-BpMv2n0o.mjs";import{a as vr}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as $}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{forceJsonLogging as mr}from"../packem_shared/createLogger-4TujevyB.mjs";import{createServer as hr,request as wr}from"node:http";import{connect as yr}from"node:net";import{pipeline as pr}from"node:stream";import{c as Sr}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{setTimeout as Er}from"node:timers/promises";import{r as br,d as Pr,P as Rr,a as _r,b as j,c as Dr,s as kr,n as de,e as Tr,f as $r,g as Cr,h as Ar,i as X,w as Or,j as Fr,k as Lr,t as Ir,l as Nr,m as jr,o as Mr}from"../packem_shared/plan-BH6l7s48.mjs";import{b as Wr}from"./handler.mjs";const Hr=100,xr=/(?:\.[cm]?[jt]sx?|(?:^|[/\\])tsconfig(?:\..+)?\.json)$/u,L=300,J=/[/\\]/u,Y=e=>{try{return ce(e,"utf8")}catch{return""}},Ur=e=>{try{const{mtimeMs:r,size:o}=ze(e);return`${String(r)}:${String(o)}`}catch{return""}},Vr=["pnpm-lock.yaml","package-lock.json","yarn.lock","bun.lock","node_modules/.modules.yaml","node_modules/.package-lock.json","node_modules/.yarn-integrity"],le=e=>{const r=M(e);return r===e?[e]:[e,...le(r)]},Br=e=>{const r={...G.sys,onUnRecoverableConfigFileDiagnostic:()=>{},readDirectory:()=>[]},o=G.getParsedCommandLineOfConfigFile(e,{},r);return o===void 0?"":JSON.stringify(o.options)},zr=(e,r)=>{const o=we(r),n=[...new Set([...ee(r),...ye.map(s=>R(e,s))])].map(s=>`${s}
|
|
2
|
+
${Y(s)}`),a=le(e).flatMap(s=>Vr.map(i=>`${R(s,i)}
|
|
3
|
+
${Ur(R(s,i))}`));return[o===void 0?"":`${o}
|
|
4
|
+
${Y(o)}
|
|
5
|
+
${Br(o)}`,...n,...a].join("\0")},Gr=(e,r)=>{let o,t;return{drop:()=>{o=void 0},get:()=>{const n=zr(e,r);return o===void 0||n!==t?(t=n,o=me(r)):he(o,r),o}}},Xr=(e,r,o,t)=>{const{apiSpec:n,lazyFunctions:a,logger:d,projectRoot:s,target:i}=e;try{const g=R(s,r,"schema.ts");if(!A(g))throw new lr("INTERNAL",`schema.ts not found at ${g}`);const h=re({apiSpec:n,lazyFunctions:D(a),lunoraDirectory:r,project:t.get(),projectRoot:s,target:i,...se(s)});d.success(`codegen: wrote ${r}/_generated (${o})`),fr(h.platformDiagnostics,d)}catch(g){return t.drop(),d.error(ur(g,o)),!1}return!0},K=e=>{const r=Be("sha256");for(const o of e){if(r.update(o).update("\0"),A(o)){r.update("present\0");try{r.update(ce(o))}catch{r.update("unreadable")}}else r.update("absent");r.update("\0")}return r.digest("hex")},Q=e=>e instanceof Error?e.stack??e.message:String(e),Jr=(e,r,o)=>{const t=[];for(const n of new Set([...e].map(a=>M(a))))if(A(n))try{const a=N(n,(d,s)=>{typeof s!="string"?r(void 0):e.has(R(n,s))&&r(s)});a.on("error",d=>{o(`codegen watch: stopped watching ${n} for lunora.config changes
|
|
6
|
+
${Q(d)}`),a.close()}),t.push(a)}catch(a){o(`codegen watch: could not watch ${n} for lunora.config changes
|
|
7
|
+
${Q(a)}`)}return t},Yr=e=>{const r=e.lunoraDirectory??"lunora",o=R(e.projectRoot,r),t=e.debounceMs??Hr;let n=!1,a=!1,d=!1,s,i=0,g;const h=[...ee(o),...Me.map(u=>R(e.projectRoot,u))];let p=Promise.resolve();const S=Gr(e.projectRoot,o);let c;const l=new Promise(u=>{c=u}),f=(u,m)=>{n||(s={codegen:m||s?.codegen===!0,reason:u},!a&&(a=!0,p=(async()=>{try{for(;s!==void 0;){const y=s;if(s=void 0,y.codegen&&(g=K(h)),!(y.codegen&&!Xr(e,r,y.reason,S))){d=!0;try{(await pe({cwd:e.projectRoot,logger:e.logger,spawner:e.spawner,stdoutToStderr:e.jsonLogs})).ran&&(i=Date.now())}finally{d=!1}}}}catch(y){e.logger.error(`codegen watch: ${y instanceof Error?y.message:String(y)}`)}finally{a=!1,c()}})()))};f("startup",!0);const w=u=>(e.logger.warn(`codegen watch unavailable (${u}) — schema edits will NOT auto-regenerate. Run \`lunora codegen\` manually after each edit.`),{close:()=>(n=!0,s=void 0,p),ready:l,watchAvailable:!1});let v,P;if(!A(o))return w(`no such directory: ${o}`);const E=new Set;let _=!1;const k=(u,m)=>{if(d||Date.now()-i<L){v&&(clearTimeout(v),v=void 0);let y=!1;for(const b of[...E,u])b!==void 0&&x(b)&&(y=!0);E.clear(),_=!1,y&&f(`change: ${m}`,!1);return}u===void 0?_=!0:E.add(u),v&&clearTimeout(v),v=setTimeout(()=>{const y=[...E].map(ge=>x(ge)),b=_||y.includes(!1);E.clear(),_=!1,f(`change: ${m}`,b)},t)},O=[],F=[],ue=new Set(h);let T;const W=u=>{if(!n){if(!d&&Date.now()-i>=L){clearTimeout(T),(u===void 0||K(h)!==g)&&k(void 0,u??"?");return}clearTimeout(T),T=setTimeout(()=>{W(u)},L)}};try{P=N(o,{recursive:!0},(u,m)=>{if(typeof m!="string"){k(void 0,"?");return}m.split(J).includes("_generated")||m.endsWith(Ge)||k(R(o,m),m)});for(const u of ve(e.projectRoot).services)if(u.rpcEntrypoint!==void 0){const m=N(M(u.wranglerPath),{recursive:!0},(y,b)=>{typeof b=="string"&&xr.test(b)&&!b.split(J).includes("node_modules")&&k(void 0,b)});m.on("error",y=>{e.logger.warn(`codegen watch: stopped watching service "${u.name}" (${y.message})`),m.close()}),O.push(m)}F.push(...Jr(ue,u=>{W(u)},u=>{e.logger.warn(u)}))}catch(u){P?.close();for(const m of O)m.close();for(const m of F)m.close();return w(u instanceof Error?u.message:String(u))}const fe=P;return{close:()=>{n=!0,s=void 0,v&&clearTimeout(v),clearTimeout(T),fe.close();for(const u of O)u.close();for(const u of F)u.close();return p},ready:l,watchAvailable:!0}},Z="/_lunora",Kr=3e4,q=(e,r,o,t=Kr)=>{const n=wr({headers:{...e.headers,host:o.host},hostname:o.hostname,method:e.method,path:e.url,port:o.port},a=>{n.setTimeout(0),r.writeHead(a.statusCode??502,a.headers),pr(a,r,()=>{})});n.setTimeout(t,()=>{n.destroy(new Error(`no response within ${String(t)}ms`))}),n.on("error",a=>{if(r.headersSent){r.destroy();return}r.statusCode=a.message.startsWith("no response within")?504:502,r.end(`lunora dev: worker unreachable (${a.message})`)}),r.on("close",()=>{r.writableFinished||n.destroy()}),e.pipe(n)},Qr=5e3,Zr=(e,r,o,t)=>{const n=yr({host:t.hostname,port:Number(t.port)},()=>{n.setTimeout(0);const a=[`${e.method??"GET"} ${e.url??"/"} HTTP/1.1`];for(const[d,s]of Object.entries(e.headers))if(d!=="host")for(const i of Array.isArray(s)?s:[s])i!==void 0&&a.push(`${d}: ${i}`);a.push(`host: ${t.host}`,"",""),n.write(a.join(`\r
|
|
8
|
+
`)),o.length>0&&n.write(o),n.pipe(r),r.pipe(n)});n.setTimeout(Qr,()=>n.destroy()),n.on("error",()=>r.destroy()),r.on("error",()=>n.destroy())},qr=async e=>{const r=e.host??"127.0.0.1",o=r==="127.0.0.1"||r==="localhost"||r==="::1",t=new URL(e.workerOrigin);let n=U(e.logger,import.meta.url),a=V(import.meta.url);const d=Xe(e.cwd,()=>Buffer.from(Je({adminToken:o?Ye(e.cwd):void 0,advisorProcedures:o,basePath:"/",dataEditable:o,rulesInstalled:te(e.cwd).installed,runAsIdentity:o,schemaEditable:o,scriptSrc:"/studio.js",styleHref:"/styles.css"}))),s=(c,l,f,w,v)=>{dr(c,l,f,w,v,a)},i=(c,l,f)=>{c.statusCode=l,c.setHeader("Content-Type","text/plain"),c.end(f)},g=C(e.cwd),h=(c,l,f,w)=>{if(o){rr(c,l,f,e.cwd,{apiSpec:e.apiSpec,lazyFunctions:D(e.lazyFunctions),schemaDirectory:g});return}i(l,403,w)},p=(c,l,f)=>{const w=c==="/styles.css",v=tr(c);if(!w&&!v)return!1;const P=V(import.meta.url);if(P!==a&&(n=U(e.logger,import.meta.url),a=P),n===void 0)return i(f,501,"Lunora studio assets not found — install and build @lunora/studio."),!0;if(w)return s(l,f,n.styles,"text/css; charset=utf-8","styles.css"),!0;const E=c.slice(c.lastIndexOf("/")+1),_=or(E,import.meta.url);return _===void 0?(i(f,404,"Not found"),!0):(s(l,f,_,nr(E),E),!0)};d();const S=hr((c,l)=>{const f=B(c.url??"/");if(o){const v=z(c,e.logger);if(v!==void 0){i(l,403,v);return}}if(f.startsWith(Z)){if(!o){i(l,403,"Lunora studio: the worker admin proxy is only available on a loopback bind.");return}q(c,l,t);return}if(f===Ke){h(c,l,ar,"Lunora schema editing is only available on loopback hosts in dev.");return}if(f===Qe){h(c,l,ir,"Lunora policy scaffolding is only available on loopback hosts in dev.");return}if(f===Ze){h(c,l,sr,"Lunora data seeding is only available on loopback hosts in dev.");return}if(f===qe){h(c,l,cr,"Lunora advisor data is only available on loopback hosts in dev.");return}if(p(f,c,l))return;const w=c.headers["sec-fetch-mode"];if(o&&typeof w=="string"&&w!=="navigate"){q(c,l,t);return}er(l,d())});return S.on("upgrade",(c,l,f)=>{if(!o||z(c,e.logger)!==void 0){l.destroy();return}B(c.url??"").startsWith(Z)?Zr(c,l,f,t):l.destroy()}),await new Promise((c,l)=>{S.once("error",f=>{f.code==="EADDRINUSE"?l(new Error(`studio port ${String(e.port)} is already in use — pass a different port with --port, or stop the process using it`,{cause:f})):l(f)}),S.listen(e.port,r,()=>{c()})}),{close:()=>new Promise(c=>{S.close(()=>{c()})}),url:`http://${r==="0.0.0.0"||r==="::"?"localhost":r}:${String(e.port)}`}},et=async e=>{const{cwd:r,logger:o,origin:t,signal:n}=e,a=e.probe??Pr,d=br(e.readyTimeoutMs),s=Date.now()+d,i=()=>n.aborted;for(;!i()&&Date.now()<s;){let g=!1;try{g=await a(t,n)}catch{}if(i()||Date.now()>=s)break;if(g)return oe(r,{readyAt:new Date().toISOString()},{expectedPid:process.pid}),Se(r)?.readyAt!==void 0;try{await Er(Rr,void 0,{signal:n})}catch{}}return i()||o.warn(`server did not answer on ${t} within ${String(Math.round(d/1e3))}s — .lunora/dev.json will have no readyAt, so anything polling for readiness will keep waiting. The server itself is unaffected; raise LUNORA_DEV_READY_TIMEOUT_MS if it is just slow to boot.`),!1},rt=(e,r,o,t)=>{e.info(""),e.success("Lunora dev"),e.info(` ➜ Worker: ${r.workerOrigin}`),o!==void 0&&e.info(` ➜ Studio: ${o}`),r.runsCodegenWatch&&e.info(" ➜ Codegen: watching lunora/"),t!==void 0&&e.info(` ➜ Supervisor: ${t} (needs) · ${ie} (status)`),r.remote.enabled&&(r.remote.bindings.length>0?e.info(` ➜ Remote: ${r.remote.bindings.join(", ")} → deployed worker`):r.remote.reason!==void 0&&e.warn(` ➜ Remote: requested but inactive (${r.remote.reason}) — running fully local`));for(const n of r.remote.withheld)e.warn(` ➜ ${xe(n)}`);e.info("")},tt=(e,r)=>{te(r).installed||!Ne()||(e.info(` ⓘ ${je}`),e.info(""))},ot=async(e,r,o)=>{let t;try{t=ke(await Te({cache:o,projectRoot:r,schemaDir:C(r)}))}catch{}if(t!==void 0)try{const a=(e.ensureExample??$e)(r,t);if(a.length>0){const d=t.map(s=>s.includes(":")?`${s.replace(":"," (")})`:s);e.logger.info(`Updated .dev.vars.example with secrets for: ${d.join(", ")} (${a.join(", ")})`)}}catch{}(await(e.ensureEnv??Ce)({confirm:Sr(),cwd:r,info:a=>{e.logger.info(a)}})).status==="declined"&&!Ae()&&e.logger.info(`hint: ${H} was not scaffolded (non-interactive run). Copy ${Oe} → ${H} and fill in secrets, or run \`lunora dev\` in an interactive terminal to scaffold automatically.`);try{(e.fillSecrets??Fe)({cwd:r,secretSources:t,info:a=>{e.logger.info(a)}})}catch{}},nt=(e,r,o,t,n)=>{if(e.flavor!=="wrangler")return;t!==void 0&&oe(r,{studioUrl:t});let a;try{a=Mr(r,o)}catch{}return rt(o,e,t,n),a},at=(e,r)=>{const o=Number(process.env[I]),t=_e(r,{background:process.env[ne]==="1",logFile:process.env[De],mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:e.workerOrigin},Number.isInteger(o)&&o>0?{supersedePid:o}:void 0);return t.ok?void 0:t.existing},it=e=>{const{cwd:r,destination:o,logger:t,plan:n}=e,a=o!==void 0,d=o??Le,s=Ve({destination:d,dev:{...n.flavor==="wrangler"?{origin:n.workerOrigin}:{},statusFile:ie},logger:a?t:{...t,success:()=>{},warn:()=>{}},projectRoot:r});return s.error!==void 0?a?s:(t.debug?.(`skipped the default binding manifest: ${s.error}`),{}):{written:d}},st=async(e,r,o,t)=>{if(r.studioEnabled)try{return await(e.startStudio??qr)({apiSpec:e.apiSpec,lazyFunctions:D(e.lazyFunctions),cwd:o,logger:{warnOnce:n=>{t.warn(n)}},port:r.studioPort,workerOrigin:r.workerOrigin})}catch(n){t.warn(`studio server failed to start (${n instanceof Error?n.message:String(n)}) — continuing without it`);return}},ct=e=>{const r=[e.runsCodegenWatch?"codegen watch":void 0,e.studioEnabled?"studio":void 0].filter(t=>t!==void 0);return`--no-worker: not starting wrangler. ${r.length>0?`${r.join(" + ")} running`:"nothing else to run"}; your task runner owns the worker on ${e.workerOrigin}.`},dt=(e,r,o,t,n)=>{if(!(e.sidecar===void 0||!Nr(r)))try{re({apiSpec:r.apiSpec,lazyFunctions:D(r.lazyFunctions),lunoraDirectory:C(o),projectRoot:o,target:n,...se(o)})}catch(a){t.warn(`codegen (pre-sidecar) failed: ${a instanceof Error?a.message:String(a)} — the framework dev server will retry`)}},lt=e=>e.flavor==="vite"?"starting vite dev (worker + studio + codegen run inside Vite via @lunora/vite)":`starting ${e.wrangler.tag} dev + studio`,ut=(e,r)=>{e.ipv4LoopbackForced&&r.info("no IPv6 loopback (::1) on this host — binding the worker to 127.0.0.1 (--ip) so wrangler dev doesn't crash. Pin `dev.ip` in wrangler.jsonc to override.")},ft=async(e,r,o,t)=>{if(e.celldSession!==!0||e.workerEnabled)return!0;try{for(const n of await(r.writeCelldProjection??Ie)({projectRoot:o}))t.warn(n);return!0}catch(n){return t.error(`could not write the celld projection: ${n instanceof Error?n.message:String(n)}`),!1}},gt=async(e,r,o,t,n)=>{try{return e.celldSession===!0?await jr({logger:n,port:e.workerPort,projectRoot:t,start:r.startCelldSession}):o(e.wrangler,n)}catch(a){n.error(`could not start the worker: ${a instanceof Error?a.message:String(a)}`);return}},vt=async(e,r,o)=>{const t=await(e.startHyperdrive??He)({onWarning:n=>{o.warn(n)},projectRoot:r});return t!==void 0&&t.bindings.length>0&&o.info(`Hyperdrive ${t.bindings.join(", ")}: embedded Postgres (PGlite), data in .wrangler/state/lunora-pglite. Set CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> to use your own.`),t},mt=async e=>{const{logger:r}=e,o=Pe(),t=e.cwd??process.cwd(),n=e.flavor??Tr(t),a=vr(t,e.target);if(a.target===void 0)throw new Error(a.error??"unknown deploy target");const{target:d}=a,s=$r(d,n,r);s==="wrangler"&&await Wr(t,r,void 0,d,void 0,o);const i=await Cr({...e,flavor:s,target:d}),g={remoteCleanup:i.remote.cleanup,serviceConfigCleanup:i.serviceConfigCleanup};try{const h=Number(process.env[I]),p=ae(t);if(p!==void 0&&p.pid!==process.pid&&p.pid!==h)return j(r,p),{code:0,plan:i};const S=at(i,t);if(S!==void 0)return j(r,S),{code:0,plan:i};(i.flavor==="vite"||i.flavor==="framework-worker")&&(i.wrangler.env={...i.wrangler.env,[I]:String(process.pid)}),i.workerEnabled&&d==="cloudflare"&&(g.hyperdrive=await vt(e,t,r)),await ot(e,t,o),r.info(lt(i)),Ar(r,t),ut(i,r),i.runsCodegenWatch&&(g.codegen=(e.startCodegen??Yr)({apiSpec:e.apiSpec,lazyFunctions:D(e.lazyFunctions),jsonLogs:e.jsonLogs,logger:r,lunoraDirectory:C(t),projectRoot:t,target:d})),g.studio=await st(e,i,t,r);const c=it({cwd:t,destination:e.emitBindings,logger:r,plan:i});if(c.error!==void 0)return r.error(c.error),{code:$.USAGE,plan:i};await g.codegen?.ready;const l=g.studio?.url;i.frameworkHint!==void 0&&r.warn(i.frameworkHint);const f=()=>{i.flavor==="wrangler"&&(g.readyProbe=new AbortController,et({cwd:t,logger:r,origin:i.workerOrigin,probe:e.probeReady,signal:g.readyProbe.signal}))};if(!await ft(i,e,t,r))return{code:$.FAILURE,plan:i};if(!i.workerEnabled)return f(),r.info(ct(i)),g.tunnel=X({cwd:t,logger:r,plan:i,tunnel:e.tunnel}),{code:await(e.waitForInterrupt??Or)(r),plan:i};dt(i,e,t,r,d);const w=e.startWorker??Fr,v=await gt(i,e,w,t,r);if(v===void 0)return{code:$.FAILURE,plan:i};const P=i.sidecar===void 0?void 0:w(i.sidecar,r);return f(),g.containerLogs=nt(i,t,r,l,c.written),g.tunnel=X({cwd:t,logger:r,plan:i,tunnel:e.tunnel}),tt(r,t),{code:await Lr(v,P,r),plan:i}}finally{g.readyProbe?.abort(),Re(t,process.pid),await Ir(g)}},ht=e=>({codegen:e.codegen===!1?!1:void 0,studio:e.studio===!1?!1:void 0,worker:e.worker===!1?!1:void 0}),wt=e=>{if(e.local===!0&&e.remote===!0)return"`--local` and `--remote` are mutually exclusive — pass at most one.";const{entries:r,invalid:o}=de(e.allowMail);if(o.length>0)return`\`--allow-mail\` takes email addresses or '*@domain' — not ${o.map(t=>`"${t}"`).join(", ")}.`;if(r.length>0&&e.tunnel!==!0)return"`--allow-mail` only applies to a tunnel — add `--tunnel`."},yt=e=>e.tunnel===!0?{allowMail:de(e.allowMail).entries}:void 0,Vt=gr(async({argument:e,cwd:r,logger:o,options:t})=>{const n=t.json===!0,a=_r({cwd:r,json:n,lines:t.lines,logger:o,subcommand:e[0]});if(a!==void 0)return a;const d=process.env[ne]==="1",s=d?void 0:Ee(),i=n||s!==void 0;i&&mr(),s!==void 0&&t.background!==!0&&o.info(`AI agent detected (${s.name} via ${s.variable}) — starting the dev server in background mode with JSON logs. Set LUNORA_AGENT_MODE=0 to opt out.`);const g=wt(t);if(g!==void 0)return o.error(`dev: ${g}`),{code:$.USAGE};const h=yt(t),p=t.local===!0?!1:We({configPreference:be(r),envValue:process.env.LUNORA_REMOTE,flag:t.remote});if(!d&&(t.background===!0||s!==void 0)){const S=ae(r);return S!==void 0?(j(o,S),{code:0}):(h?.allowMail.length===0&&Dr(o),kr({cwd:r,jsonLogs:i,logger:o,options:t,remote:p}))}return mt({apiSpec:Ue(t.apiSpec),cwd:r,emitBindings:t.emitBindings,inspectorPort:t.inspectorPort,lazyFunctions:D(t.lazyFunctions),jsonLogs:i,local:t.local,logger:o,port:t.port,remote:p,target:t.target,tunnel:h,workerPort:t.workerPort,...ht(t)})});export{wt as devFlagConflict,Vt as execute,ht as negatableDevFlags,mt as runDevCommand};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{existsSync as q,mkdirSync as Te,openSync as J,closeSync as X,statSync as Pe,readFileSync as Q,readSync as be}from"node:fs";import{join as N,dirname as _e}from"node:path";import{readDevServiceBindings as $e}from"@lunora/codegen";import{readLiveDevServerState as C,discoverContainerInfo as Re,streamContainerLogs as Ne,formatLunoraEvent as Ce,updateDevServerState as b,resolveLazyFunctions as Z,targetRunsOwnDevServer as D,resolveProjectTarget as ee,claimDevServerState as De,DEV_HANDOFF_ENV as Oe,clearDevServerState as E,readDevServerState as O,isDevServerReady as j,detectFramework as re,readProjectDependencyNames as xe,DEV_LOG_FILE_ENV as Ae,DEV_DAEMON_ENV as Me,LAZY_FUNCTIONS_ENV as Ue,readProjectManifest as Ge,isRecordedProcessCurrent as Fe,DEV_LOG_FILE as je,CODEGEN_ENV as te,isCodegenDisabled as Ve,planToolchainInvocation as We,startCelldDevSession as Be,resolveDeployDriver as ne,DEV_VARS_FILE as oe,parseDevVariableEntries as Ke,resolveSchemaDirectory as ze}from"@lunora/config";import{warnCloudflareCliConfigOnce as He,findWranglerFile as L,readWranglerJsonc as x,materializeDevWranglerConfig as Ye,materializeServiceDevConfigs as qe}from"@lunora/config/cloudflare";import{f as Je}from"./free-port-DjbFhaaT.mjs";import{networkInterfaces as Xe}from"node:os";import{spawn as ie,spawnSync as T}from"node:child_process";import{setTimeout as w}from"node:timers/promises";import{EXIT_CODE as se}from"./EXIT_CODE-DQVhSE-1.mjs";import{a as h}from"./output-format-DWe_tkTR.mjs";import{spawnShellCompat as ae,defaultSpawner as Qe}from"./createRecordingSpawner-DchLi0gR.mjs";import{isLunoraError as Ze}from"@lunora/errors";import{s as V}from"./index-oHZBGBwB.mjs";import{createInterface as er}from"node:readline";import{detectPackageManager as A,execArgsFor as ce,toolchainExecArgs as le,runScriptCommand as rr}from"@lunora/config/cli";const tr=12e4,_=250,nr=1e3,or="LUNORA_DEV_READY_TIMEOUT_MS",ir=e=>{if(e!==void 0)return e;const r=Number(process.env[or]??"");return Number.isFinite(r)&&r>0?r:tr},sr=async(e,r)=>{try{const n=AbortSignal.timeout(nr);return await(await fetch(new URL("/_lunora/status",e),{redirect:"manual",signal:r===void 0?n:AbortSignal.any([r,n])})).body?.cancel(),!0}catch{return!1}},de=async(e,r,n)=>{const t=Date.now()+n.timeoutMs;for(;n.signal?.aborted!==!0;){const o=r(C(e));if(o!==void 0)return o;if(Date.now()>=t)return;try{await w(n.intervalMs??500,void 0,{signal:n.signal})}catch{return}}},W=(e,r)=>{er({crlfDelay:Number.POSITIVE_INFINITY,input:e}).on("line",r)},ar=5e3,ue=(e,r,n,t)=>{if(e.length===0)return;const o=Ce(e);if(o){t[o.level](`[lunora] ${o.text}`);return}const i=`[${r}] ${e}`;n==="stderr"?t.warn(i):t.info(i)},cr=(e,r,n=process.platform,t=T)=>{try{if(r==="SIGKILL"&&n==="win32"&&e.pid!==void 0){t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"});return}e.kill(r)}catch{}},fe=(e,r,n)=>{const t=e.direct===!0?{args:[...e.args],command:e.command,shell:!1}:ae(e.command,e.args),o=ie(t.command,t.args,{cwd:e.cwd??process.cwd(),env:e.env?{...process.env,...e.env}:process.env,shell:t.shell,stdio:[e.direct===!0?"ignore":"inherit","pipe","pipe"]});return W(o.stdout,i=>{r(i,"stdout")}),W(o.stderr,i=>{r(i,"stderr")}),{exited:new Promise(i=>{o.on("error",s=>{n?.(s),i(1)}),o.on("exit",(s,a)=>{i(s??(a?1:0))})}),kill:i=>{cr(o,i)}}},gt=(e,r)=>fe(e,(n,t)=>{ue(n.trimEnd(),e.tag,t,r)},n=>{r.error(`[${e.tag}] failed to start: ${n.message}`)}),pt=(e,r)=>{if(process.env.LUNORA_CONTAINER_LOGS==="0")return;const t=Re(e,"lunora").containers.map(o=>({className:o.className,exportName:o.exportName}));if(t.length!==0)return Ne({containers:t,onLine:o=>{const i=`[container:${o.name}] ${o.text}`;o.level==="error"?r.warn(i):r.info(i)},onUnavailable:o=>{r.warn(`[container] Docker engine unreachable — container logs unavailable (${o})`)}})},mt=async e=>{e.readyProbe?.abort(),await e.tunnel?.close(),await e.codegen?.close().catch(()=>{}),e.containerLogs?.close(),await e.studio?.close().catch(()=>{}),await e.hyperdrive?.stop().catch(()=>{});try{e.remoteCleanup?.(),e.serviceConfigCleanup?.()}catch{}},wt=async e=>await new Promise(r=>{const n={},t=s=>{e.info(`received ${s} — shutting down`),n.sigint&&process.off("SIGINT",n.sigint),n.sigterm&&process.off("SIGTERM",n.sigterm),r(0)},o=()=>{t("SIGINT")},i=()=>{t("SIGTERM")};n.sigint=o,n.sigterm=i,process.on("SIGINT",o),process.on("SIGTERM",i)}),ht=async(e,r,n)=>{let t=0,o;const i=l=>{e.kill(l),r?.kill(l)},s=()=>{t+=1,t===1?(n.info("received SIGINT — shutting down (press Ctrl-C again to force-kill)"),i("SIGTERM"),o=setTimeout(()=>{i("SIGKILL")},ar),o.unref()):i("SIGKILL")},a=()=>{i("SIGTERM")};process.on("SIGINT",s),process.on("SIGTERM",a);const c=await Promise.race([e.exited.then(l=>({code:l,who:"worker"})),...r===void 0?[]:[r.exited.then(l=>({code:l,who:"sidecar"}))]]);return r!==void 0&&(c.who==="sidecar"?(n.warn("[worker] the Lunora sidecar (wrangler dev) exited — shutting down the framework dev server"),e.kill("SIGTERM")):r.kill("SIGTERM"),await Promise.allSettled([e.exited,r.exited])),o&&clearTimeout(o),process.off("SIGINT",s),process.off("SIGTERM",a),c.code},y="2026.9.3",lr="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/",dr=/version (\S+)/u,B=/https:\/\/[\da-z-]+\.trycloudflare\.com/u,ur="1s",fr=5e3,K=async(e,r)=>await Promise.race([e.then(()=>!0),w(r,!1,{ref:!1})]),vr=6e4,gr='Vite answers 403 for hosts it does not know — add `server: { allowedHosts: [".trycloudflare.com"] }` to vite.config if the URL is blocked.',pr=async e=>{const r=`Install or update it: ${lr} (macOS: \`brew install cloudflared\`).`;let n;try{const o=await e({args:["--version"],captureStdoutSilently:!0,command:"cloudflared"});if(o.code!==0)return{message:`\`cloudflared --version\` exited ${String(o.code)}. ${r}`,ok:!1};n=o.stdout??""}catch(o){return Ze(o)&&o.code==="LOCAL_DEPENDENCY_MISSING"?{message:`--tunnel needs \`cloudflared\` ${y} or newer, and it is not on your PATH. ${r}`,ok:!1}:{message:`\`cloudflared --version\` could not run: ${o instanceof Error?o.message:String(o)}. ${r}`,ok:!1}}const t=V.coerce(dr.exec(n)?.[1])?.version;return t===void 0?{ok:!0,version:void 0}:V.gte(t,y)?{ok:!0,version:t}:{message:`--tunnel needs \`cloudflared\` ${y} or newer (protected quick tunnels); found ${t}. ${r}`,ok:!1}},mr=e=>{const r=[],n=[];for(const t of e??[]){const o=t.split(",").map(i=>i.trim()).filter(i=>i.length>0);o.length>0&&(n.push(...o.filter(i=>!i.includes("@"))),r.push(o.join(",")))}return{entries:r,invalid:n}},wr=(e,r)=>["tunnel","--url",e,"--output","json","--grace-period",ur,...r.flatMap(n=>["--allowed-mail",n])],hr=e=>{let r;try{r=JSON.parse(e)}catch{r=void 0}if(typeof r=="object"&&r!==null){const n=r,t=typeof n.message=="string"?n.message:"";return{level:typeof n.level=="string"?n.level:void 0,text:t,url:B.exec(t)?.[0]}}return{level:void 0,text:e,url:B.exec(e)?.[0]}},Sr=e=>{e.warn(" ⚠ PUBLIC TUNNEL: anyone with the tunnel URL can reach this dev server — there is no sign-in in front of it."),e.warn(" Protect it with a one-time-PIN email allow-list: lunora dev --tunnel --allow-mail you@example.com"),e.warn(" (repeat --allow-mail, pass a comma-separated list, or allow a whole domain with '*@example.com')")},kr=async(e,r,n,{intervalMs:t,timeoutMs:o=vr}={})=>{const i=await de(e,s=>s?.mode==="vite"?{origin:s.url,ownerPid:s.pid}:void 0,{intervalMs:t,signal:n,timeoutMs:o});return i===void 0&&!n.aborted&&r.warn(`--tunnel: the Vite dev server did not report its URL within ${String(o/1e3)}s — continuing without a tunnel.`),i},Ir=e=>{const{allowMail:r,cwd:n,logger:t}=e,o=r.length===0,i=new AbortController,{signal:s}=i,a=()=>s.aborted,c=e.closeTimeoutMs??fr;let l;const d=()=>{i.abort()};process.once("SIGINT",d),process.once("SIGTERM",d);const p=(async()=>{const u=await pr(e.spawner??Qe);if(!u.ok){t.error(u.message),t.warn("continuing without a tunnel.");return}u.version===void 0&&t.warn(`could not read the cloudflared version — --tunnel needs ${y} or newer; trying anyway.`);const v=e.origin.kind==="fixed"?{origin:e.origin.origin,ownerPid:process.pid}:await kr(n,t,s);if(v===void 0||s.aborted)return;o&&Sr(t),t.info(`starting a Cloudflare quick tunnel to ${v.origin}${o?"":" (email one-time-PIN protected)"}…`);let S;const U={failedToStart:!1},Le=k=>{if(k.length===0)return;const g=hr(k);if(g.url!==void 0&&S===void 0){S=g.url,t.success(` ➜ Tunnel: ${g.url}${o?" (PUBLIC)":""}`),o||t.info(` Allowed: ${r.join(", ")} — visitors confirm with a one-time PIN; a session lasts up to 4 hours`),e.origin.kind==="vite"&&t.info(` ${gr}`),b(n,{tunnelUrl:g.url},{expectedPid:v.ownerPid});return}g.level==="error"||g.level==="fatal"?t.warn(`[cloudflared] ${g.text}`):t.debug?.(`[cloudflared] ${g.text}`)},P=(e.startChild??fe)({args:wr(v.origin,r),command:"cloudflared",direct:!0},Le,k=>{U.failedToStart=!0,t.error(`could not start cloudflared (${k.message}) — continuing without a tunnel.`)});l=P;const G=()=>{P.kill("SIGTERM")};s.addEventListener("abort",G,{once:!0});const F=await P.exited;s.removeEventListener("abort",G),l=void 0,S!==void 0&&b(n,{tunnelUrl:void 0},{expectedPid:v.ownerPid}),!(a()||U.failedToStart)&&t.warn(S===void 0?`cloudflared exited (${String(F)}) before a tunnel URL was assigned — continuing without a tunnel.`:`cloudflared exited (${String(F)}) — the tunnel is closed; the dev server keeps running.`)})().catch(u=>{t.error(`--tunnel failed (${u instanceof Error?u.message:String(u)}) — continuing without a tunnel.`)});return{close:async()=>{if(process.off("SIGINT",d),process.off("SIGTERM",d),i.abort(),await K(p,c))return;const u=l;u!==void 0&&(t.warn(`cloudflared did not exit within ${String(c/1e3)}s of SIGTERM — force-killing it.`),u.kill("SIGKILL"),!await K(u.exited,c)&&t.error(`could not confirm that cloudflared stopped — ${o?"the PUBLIC tunnel":"the tunnel"} may still be reachable. Stop the \`cloudflared\` process manually.`))}}},St=e=>{const{cwd:r,logger:n,plan:t,tunnel:o}=e;if(o!==void 0)return Ir({allowMail:o.allowMail,cwd:r,logger:n,origin:t.flavor==="wrangler"?{kind:"fixed",origin:t.workerOrigin}:{kind:"vite"},spawner:o.spawner,startChild:o.startChild})},yr=(e,r)=>{const n=["dev"];if(e.apiSpec!==void 0&&n.push("--api-spec",e.apiSpec),Z(e.lazyFunctions)&&n.push("--lazy-functions"),e.port!==void 0&&n.push("--port",String(e.port)),e.workerPort!==void 0&&n.push("--worker-port",String(e.workerPort)),e.inspectorPort!==void 0&&n.push("--inspector-port",String(e.inspectorPort)),e.emitBindings!==void 0&&n.push("--emit-bindings",e.emitBindings),e.codegen===!1&&n.push("--no-codegen"),e.studio===!1&&n.push("--no-studio"),e.worker===!1&&n.push("--no-worker"),e.target!==void 0&&n.push("--target",e.target),e.tunnel===!0){n.push("--tunnel");for(const t of mr(e.allowMail).entries)n.push("--allow-mail",t)}return r&&n.push("--remote"),e.local===!0&&n.push("--local"),n},Er=1e4,Lr=100,Tr=40,I=256*1024,Pr=new Set(["nuxt","sveltekit"]),br=["@lunora/rspack","@lunora/vite"],M=e=>{if(Pr.has(re(e).framework))return"framework-worker";const r=xe(e);return br.some(n=>r.has(n))?"vite":"wrangler"},ve=e=>{const r=A(e),n=Ge(e)?.scripts,t=typeof n=="object"&&n!==null?n.dev:void 0;if(typeof t!="string"||t.trim()===""||t.includes("lunora")){const o=ce(r,"vite",["dev"]);return{args:o.args,command:o.command}}return{args:["run","dev"],command:r}},_r=e=>{Te(_e(e.logPath),{recursive:!0});const r=J(e.logPath,"w",384);let n;const t=ae(e.command,e.args);try{n=ie(t.command,t.args,{cwd:e.cwd,detached:!0,env:e.env,shell:t.shell,stdio:["ignore",r,r],windowsHide:!0})}finally{X(r)}return n.unref(),{exited:new Promise(o=>{n.on("error",()=>{o(1)}),n.on("exit",(i,s)=>{o(i??(s?1:0))})}),pid:n.pid}},$r=e=>{try{const{size:r}=Pe(e);if(r<=I)return Q(e,"utf8");const n=J(e,"r");try{const t=Buffer.alloc(I),o=be(n,t,0,I,r-I),i=t.toString("utf8",0,o);return i.slice(i.indexOf(`
|
|
2
|
+
`)+1)}finally{X(n)}}catch{return}},ge=(e,r)=>{const n=$r(e);if(n===void 0)return[];const t=n.split(`
|
|
3
|
+
`);return t.at(-1)===""&&t.pop(),r>0?t.slice(-r):t},pe=(e,r)=>r?.logFile??N(e,je),me=e=>{e.info(" Stop: lunora dev stop"),e.info(" Status: lunora dev status"),e.info(" Logs: lunora dev logs")},Rr=(e,r)=>{He(r,n=>{e.warn(n)})},Nr=(e,r)=>{e.warn(`Dev server already running at ${r.url} (pid ${String(r.pid)})`),me(e)},Cr=(e,r)=>{e.success(`Dev server running at ${r.url} (pid ${String(r.pid)})`),r.studioUrl!==void 0&&e.info(` Studio: ${r.studioUrl}`),r.tunnelUrl!==void 0&&e.info(` Tunnel: ${r.tunnelUrl}`),me(e)},Dr=3e4,Or=async(e,r,n,t)=>{const o=await de(e,i=>i?.tunnelUrl,{intervalMs:n,timeoutMs:t});o===void 0?r.warn(" Tunnel: no URL yet — `lunora dev status` shows it once cloudflared reports one; `lunora dev logs` says why if it does not."):r.success(` Tunnel: ${o}`)},xr=async e=>{const{child:r,cwd:n,deadline:t,pollIntervalMs:o,probe:i}=e,s=r.exited.then(a=>({exitCode:a}));for(;Date.now()<t;){const a=C(n);if(a!==void 0&&a.pid!==process.pid&&await i(a.url))return{state:a,status:"ready"};const c=await Promise.race([s,w(o)]);if(c!==void 0)return{...c,status:"exited"}}return{status:"timeout"}},Ar=async e=>{const{cwd:r,logger:n}=e,t=pe(r,void 0),o=ir(e.readyTimeoutMs),i=(e.spawnDetached??_r)({args:e.command.args,command:e.command.command,cwd:r,env:{...process.env,...e.env,[Me]:"1",[Ae]:t,...process.env.FORCE_COLOR===void 0?{NO_COLOR:"1"}:{},...e.json?{LUNORA_LOG_JSON:"1"}:{}},logPath:t}),s=await xr({child:i,cwd:r,deadline:Date.now()+o,pollIntervalMs:e.pollIntervalMs??_,probe:e.probe??sr});if(s.status==="ready")return Cr(n,s.state),e.tunnel===!0&&s.state.tunnelUrl===void 0&&await Or(r,n,e.pollIntervalMs??_,e.tunnelUrlTimeoutMs??Dr),{code:0};if(s.status==="exited"){n.error(`dev server exited before becoming ready (exit code ${String(s.exitCode)}). Last output:`);for(const c of ge(t,Tr))n.error(` ${c}`);return E(r,i.pid??process.pid),{code:s.exitCode===0?1:s.exitCode}}i.pid!==void 0&&O(r)?.pid===process.pid&&b(r,{logFile:t,pid:i.pid});const a=i.pid===void 0?"":` (pid ${String(i.pid)})`;return n.warn(`dev server did not confirm ready within ${String(Math.round(o/1e3))}s — it may still be compiling${a}. Check \`lunora dev status\` and \`lunora dev logs\`; \`lunora dev stop\` shuts it down.`),{code:se.UNAVAILABLE}},we=e=>e.codegen!==!1&&!Ve(process.env[te]),he=e=>{const r={...Z(e.lazyFunctions)?{[Ue]:"1"}:{},...e.remote===!0?{LUNORA_REMOTE:"1"}:{},...e.codegen===!1?{[te]:"0"}:{}};return Object.keys(r).length>0?{env:r}:{}},kt=async e=>{const{cwd:r,jsonLogs:n,logger:t,options:o,remote:i}=e,s=e.run??Ar,a=M(r),c=a==="vite"&&D(o.target??ee(r))?"wrangler":a,l=c==="wrangler"?`http://localhost:${String(o.workerPort??8787)}`:"http://localhost:5173",d=De(r,{background:!0,mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:l});if(!d.ok)return d.existing!==void 0&&Nr(t,d.existing),{code:0};const f={[Oe]:String(process.pid)};Rr(t,r);try{return c==="vite"&&o.tunnel!==!0?await s({command:ve(r),cwd:r,env:{...he({...o,remote:i}).env,...f},json:n,logger:t}):await s({command:{args:[process.argv[1]??"lunora",...yr(o,i)],command:process.execPath},cwd:r,env:f,json:n,logger:t,tunnel:o.tunnel===!0})}finally{E(r,process.pid)}},Mr=e=>{try{const r=T("ps",["-o","pgid=","-p",String(e)],{encoding:"utf8"}),n=Number.parseInt(r.stdout.trim(),10);return Number.isInteger(n)&&n>0?n:void 0}catch{return}},Ur=(e,r,n=process.platform,t=T)=>{if(n==="win32"){try{t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"})}catch{}return}if(e.background===!0)try{r(-(Mr(e.pid)??e.pid),"SIGKILL");return}catch{}try{r(e.pid,"SIGKILL")}catch{}},Gr=(e,r)=>(r.json?h({pid:e.pid,stopped:!1}):r.logger.error(`dev server (pid ${String(e.pid)}) survived the force-kill — record kept; retry \`lunora dev stop\` or kill the process manually.`),{code:1}),Fr=async e=>{const{cwd:r,logger:n}=e,t=O(r),o=e.alive??(f=>f===t?.pid&&Fe(t)),i=e.signal??((f,p)=>{process.kill(f,p)}),s=e.pollIntervalMs??_,a=e.stopGraceMs??Er,c=e.platform??process.platform,l=e.spawnSyncImpl??T;if(t===void 0||!o(t.pid))return t!==void 0&&E(r,t.pid),e.json?h({running:!1,stopped:!1}):n.info("No dev server running."),{code:0};try{i(t.pid,"SIGTERM")}catch{}const d=Date.now()+a;for(;o(t.pid)&&Date.now()<d;)await w(s);return o(t.pid)&&(Ur(t,i,c,l),await w(s),o(t.pid))?Gr(t,e):(E(r,t.pid),e.json?h({pid:t.pid,stopped:!0}):n.success(`Stopped dev server (pid ${String(t.pid)}).`),{code:0})},jr=e=>{const{cwd:r,logger:n}=e,t=C(r);if(t===void 0)return e.json?h({running:!1}):n.info("No dev server running."),{code:0};const o=e.now??Date.now,i=t.startedAt===void 0?Number.NaN:Date.parse(t.startedAt),s=Number.isFinite(i)?Math.max(0,Math.round((o()-i)/1e3)):void 0;if(e.json)return h({background:t.background===!0,logFile:t.logFile,mode:t.mode,pid:t.pid,ready:j(t),readyAt:t.readyAt,running:!0,startedAt:t.startedAt,studioUrl:t.studioUrl,tunnelUrl:t.tunnelUrl,uptimeSeconds:s,url:t.url}),{code:0};const a=[`pid ${String(t.pid)}`,...s===void 0?[]:[`uptime ${String(s)}s`],t.background===!0?"background":"foreground",j(t)?"ready":"starting"];return n.success(`Dev server running at ${t.url} (${a.join(", ")})`),t.studioUrl!==void 0&&n.info(` Studio: ${t.studioUrl}`),t.tunnelUrl!==void 0&&n.info(` Tunnel: ${t.tunnelUrl}`),t.logFile!==void 0&&n.info(` Logs: lunora dev logs (${t.logFile})`),{code:0}},Vr=e=>{const{cwd:r,logger:n}=e,t=O(r),o=pe(r,t);if(!q(o))return n.info("No dev server logs found — output is captured when the server runs in background mode (`lunora dev --background`)."),{code:0};const i=ge(o,e.lines??Lr);return i.length>0&&process.stdout.write(`${i.join(`
|
|
4
|
+
`)}
|
|
5
|
+
`),{code:0}},It=e=>{const{cwd:r,json:n,lines:t,logger:o,subcommand:i}=e;if(i==="stop")return Fr({cwd:r,json:n,logger:o});if(i==="status")return jr({cwd:r,json:n,logger:o});if(i==="logs")return Vr({cwd:r,lines:t,logger:o});if(i!==void 0)return o.error(`dev: unknown subcommand "${i}" — expected stop | status | logs (or no subcommand to start the dev server)`),{code:se.USAGE}},yt=(e,r,n)=>D(e)?(r!=="wrangler"&&n.info(`target ${e} runs its own dev server, so lunora dev serves the worker on it — start the frontend's dev server separately`),"wrangler"):r,Et=async e=>{const{logger:r}=e,n=await(e.start??Be)({log:(i,{stream:s,tag:a})=>{ue(i,a,s,r)},port:e.port,projectRoot:e.projectRoot});let t=()=>{};const o=new Promise(i=>{t=i});return{exited:Promise.race([o,n.exited]),kill:()=>{n.stop().then(()=>0,()=>1).then(t).catch(()=>{})}}},Wr=e=>{const{cwd:r,driver:n,options:t,studioPort:o,workerPort:i}=e,{projectConfig:s,toolchain:a}=n;if(s===void 0||a===void 0)throw new Error(`deploy target "${n.id}" runs its own dev server but ships no config projection or toolchain for it`);if(t.remote===!0)throw new Error(`--remote proxies bindings to Cloudflare; ${n.name} has no remote bindings to proxy to`);t.inspectorPort!==void 0&&t.logger.warn(`--inspector-port is a wrangler dev flag; ${n.name} dev has no inspector to pin`);const{command:c,projection:l}=We(n,r,"dev",f=>a.dev({configPath:f,extraArgs:["--port",String(i)]}));l!==void 0&&l.dropped.length>0&&t.logger.info(`${n.name} ignores these wrangler keys, so its dev server runs without them: ${l.dropped.join(", ")}`);const d=le(A(r),c);return{flavor:"wrangler",ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:()=>{},enabled:!1,withheld:[]},runsCodegenWatch:we(t),celldSession:!0,studioEnabled:t.studio!==!1,studioPort:o,workerEnabled:t.worker!==!1,workerOrigin:`http://localhost:${String(i)}`,workerPort:i,wrangler:{args:d.args,command:d.command,cwd:r,tag:c.tool}}},Se=(e=Xe)=>Object.values(e()).some(r=>r?.some(n=>n.internal&&n.address==="::1")),$="wrangler.dev.jsonc",R=6173,m=8787,z=5173,ke=()=>{},Br=(e,r)=>{if(!e.remote&&e.local===!0)return{args:[],plan:{bindings:[],cleanup:ke,enabled:!1,withheld:[]}};const t=(e.materializeDev??Ye)({projectRoot:r,remote:e.remote===!0}),i={bindings:t.remoteBindings.map(s=>`${s.binding} (${s.kind})`),cleanup:t.cleanup,enabled:e.remote===!0,reason:t.reason,withheld:t.withheld};return{args:t.configPath===void 0?[]:["--config",t.configPath],plan:i}},Ie=(e,r)=>{const n=[...new Set($e(r,ze(r)).services.map(s=>s.wranglerPath))],{cleanup:t,configPaths:o,withheld:i}=(e.materializeServiceConfigs??qe)(n,{projectRoot:r});return{args:o.flatMap(s=>["--config",s]),cleanup:t,withheld:i}},Kr=(e,r,n)=>{const t=L(e);return r.length===0||t===void 0?[]:[...n.length>0?[]:["--config",t],...r]},ye=e=>e.local===!0?["--local"]:[],H=e=>x(e).parsed?.dev?.ip,Ee=(e,r,n)=>{if(n!==void 0){const o=N(e,n);if(q(o)&&H(o)!==void 0)return[]}const t=L(e);return t!==void 0&&H(t)!==void 0?[]:r()?[]:["--ip","127.0.0.1"]},zr=new Set(["127.0.0.1","::1","[::1]","localhost"]),Hr=(e,r)=>{let n;try{n=Q(N(e,oe),"utf8")}catch{return[]}const t=[];for(const o of Ke(n)){let i;try{i=new URL(o.value)}catch{continue}zr.has(i.hostname)&&i.port===String(r)&&t.push(o.key)}return t},Y=async(e,r)=>{if(e.workerPort!==void 0)return e.workerPort;const n=L(r);if(n!==void 0){const{parsed:i}=x(n);if(typeof i?.dev?.port=="number")return i.dev.port}const t=await(e.findFreePort??Je)(m);if(t===m)return t;const o=Hr(r,m);if(o.length>0)throw new Error(`port ${String(m)} is in use, but ${oe} pins the worker origin to it (${o.join(", ")}). Serving on ${String(t)} would leave those URLs pointing at nothing. Stop whatever holds ${String(m)}, or run \`lunora dev --worker-port ${String(t)}\` and update those values to match.`);return t},Yr=(e,r)=>{if(e.inspectorPort!==void 0)return e.inspectorPort;const n=L(r);if(n===void 0)return;const{parsed:t}=x(n),o=t?.dev?.inspector_port;return typeof o=="number"?o:void 0},qr=(e,r,n)=>{const t=Ee(r,e.hasIpv6Loopback??Se,$),o=ne(e.target??ee(r));if(o.toolchain===void 0)throw new Error(`deploy target "${o.id}" has no dev server to run the worker sidecar`);const i=Ie(e,r),s=o.toolchain.dev({configPath:$,extraArgs:[...t,...ye(e),"--var","WORKER_ENV:development",...i.args]}),a=le(n,s);return{cleanup:i.cleanup,sidecar:{args:a.args,command:a.command,cwd:r,tag:"worker"},withheld:i.withheld}},Jr=(e,r)=>{e.worker===!1&&e.logger.warn(`--no-worker does not apply to the ${r} flavor: Vite owns the worker, codegen and studio in-process. Run your framework's dev script instead.`),e.inspectorPort!==void 0&&e.logger.warn(r==="framework-worker"?`--inspector-port does not apply to the ${r} flavor: pin \`dev.inspector_port\` in ${$} — that is the config the worker sidecar runs.`:`--inspector-port does not apply to the ${r} flavor: Vite owns the worker. Pin it in vite.config — \`lunora({ cloudflare: { inspectorPort: ${String(e.inspectorPort)} } })\`.`),e.local===!0&&r==="vite"&&e.logger.warn("--local does not apply to the vite flavor: Vite owns the worker. Turn remote bindings off in vite.config — `lunora({ cloudflare: { remoteBindings: false } })`.")},Xr=e=>{const r=e.cwd??process.cwd(),n=A(r),t=e.flavor??M(r);if(t==="vite"||t==="framework-worker"){const p=ve(r),u=t==="framework-worker"?qr(e,r,n):void 0,v=u?.sidecar;return Jr(e,t),{runsCodegenWatch:!1,flavor:t,ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:ke,enabled:e.remote===!0,withheld:u?.withheld??[]},...u?{serviceConfigCleanup:u.cleanup}:{},...v?{sidecar:v}:{},studioEnabled:!1,studioPort:e.port??R,workerEnabled:!0,workerOrigin:`http://localhost:${String(z)}`,workerPort:z,wrangler:{args:p.args,command:p.command,cwd:r,...he(e),tag:"vite"}}}const o=re(r),i=o.framework==="none"?void 0:`this project uses ${o.framework} — the worker runs inside Vite there. run \`${rr(n,"dev")}\` for the full app (frontend + HMR); \`lunora dev\` starts only the worker.`,s=e.workerPort??m,a=Br(e,r),c=Ee(r,e.hasIpv6Loopback??Se),l=e.inspectorPort===void 0?[]:["--inspector-port",String(e.inspectorPort)],d=Ie(e,r),f=ce(n,"wrangler",["dev","--port",String(s),...l,...c,...ye(e),"--var","WORKER_ENV:development",...a.args,...Kr(r,d.args,a.args)]);return{runsCodegenWatch:we(e),flavor:t,frameworkHint:i,ipv4LoopbackForced:c.length>0,remote:{...a.plan,withheld:[...a.plan.withheld,...d.withheld]},serviceConfigCleanup:d.cleanup,studioEnabled:e.studio!==!1,studioPort:e.port??R,workerEnabled:e.worker!==!1,workerOrigin:`http://localhost:${String(s)}`,workerPort:s,wrangler:{args:f.args,command:f.command,cwd:r,tag:"wrangler"}}},Lt=async e=>{const r=e.cwd??process.cwd();if(e.target!==void 0&&D(e.target))return Wr({cwd:r,driver:ne(e.target),options:e,studioPort:e.port??R,workerPort:await Y(e,r)});const n=e.flavor??M(r),t=n==="wrangler"?await Y(e,r):e.workerPort,o=n==="wrangler"?Yr(e,r):e.inspectorPort;return Xr({...e,cwd:r,flavor:n,inspectorPort:o,workerPort:t})};export{_ as P,It as a,Nr as b,Sr as c,sr as d,M as e,yt as f,Lt as g,Rr as h,St as i,gt as j,ht as k,we as l,Et as m,mr as n,pt as o,Xr as p,Yr as q,ir as r,kt as s,mt as t,Y as u,wt as w};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./free-port-DjbFhaaT.mjs";import{g as a,p as s,q as l,u as p}from"./plan-
|
|
1
|
+
import"./free-port-DjbFhaaT.mjs";import{g as a,p as s,q as l,u as p}from"./plan-BH6l7s48.mjs";export{a as buildDevPlan,s as planDevCommand,l as resolveInspectorPort,p as resolveWorkerPort};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/cli",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.482",
|
|
4
4
|
"description": "The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"@bomb.sh/tab": "0.0.22",
|
|
59
59
|
"@lunora/advisor": "^1.0.0-alpha.287",
|
|
60
60
|
"@lunora/bindings": "1.0.0-alpha.151",
|
|
61
|
-
"@lunora/codegen": "^1.0.0-alpha.
|
|
62
|
-
"@lunora/config": "^1.0.0-alpha.
|
|
61
|
+
"@lunora/codegen": "^1.0.0-alpha.398",
|
|
62
|
+
"@lunora/config": "^1.0.0-alpha.454",
|
|
63
63
|
"@lunora/container": "1.0.0-alpha.108",
|
|
64
64
|
"@lunora/d1": "^1.0.0-alpha.233",
|
|
65
65
|
"@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"giget": "3.3.1",
|
|
81
81
|
"jiti": "2.7.0",
|
|
82
82
|
"jsonc-parser": "^3.3.1",
|
|
83
|
-
"magic-string": "^1.4.
|
|
84
|
-
"react": "^19.3.
|
|
83
|
+
"magic-string": "^1.4.5",
|
|
84
|
+
"react": "^19.3.1",
|
|
85
85
|
"react-reconciler": "^0.33.0",
|
|
86
86
|
"smol-toml": "1.9.0",
|
|
87
87
|
"ts-morph": "^28.0.0"
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{existsSync as q,mkdirSync as Te,openSync as J,closeSync as X,statSync as Pe,readFileSync as Q,readSync as be}from"node:fs";import{join as N,dirname as _e}from"node:path";import{readServiceBindings as $e}from"@lunora/codegen";import{readLiveDevServerState as C,discoverContainerInfo as Re,streamContainerLogs as Ne,formatLunoraEvent as Ce,updateDevServerState as b,resolveLazyFunctions as Z,targetRunsOwnDevServer as D,resolveProjectTarget as ee,claimDevServerState as De,DEV_HANDOFF_ENV as Oe,clearDevServerState as E,readDevServerState as O,isDevServerReady as j,detectFramework as re,readProjectDependencyNames as xe,DEV_LOG_FILE_ENV as Ae,DEV_DAEMON_ENV as Me,LAZY_FUNCTIONS_ENV as Ue,readProjectManifest as Ge,isRecordedProcessCurrent as Fe,DEV_LOG_FILE as je,CODEGEN_ENV as te,isCodegenDisabled as Ve,planToolchainInvocation as We,startCelldDevSession as Be,resolveDeployDriver as ne,DEV_VARS_FILE as oe,parseDevVariableEntries as Ke}from"@lunora/config";import{warnCloudflareCliConfigOnce as ze,findWranglerFile as L,readWranglerJsonc as x,materializeDevWranglerConfig as He,materializeServiceDevConfigs as Ye}from"@lunora/config/cloudflare";import{f as qe}from"./free-port-DjbFhaaT.mjs";import{networkInterfaces as Je}from"node:os";import{spawn as ie,spawnSync as T}from"node:child_process";import{setTimeout as w}from"node:timers/promises";import{EXIT_CODE as se}from"./EXIT_CODE-DQVhSE-1.mjs";import{a as h}from"./output-format-DWe_tkTR.mjs";import{spawnShellCompat as ae,defaultSpawner as Xe}from"./createRecordingSpawner-DchLi0gR.mjs";import{isLunoraError as Qe}from"@lunora/errors";import{s as V}from"./index-oHZBGBwB.mjs";import{createInterface as Ze}from"node:readline";import{detectPackageManager as A,execArgsFor as ce,toolchainExecArgs as le,runScriptCommand as er}from"@lunora/config/cli";const rr=12e4,_=250,tr=1e3,nr="LUNORA_DEV_READY_TIMEOUT_MS",or=e=>{if(e!==void 0)return e;const r=Number(process.env[nr]??"");return Number.isFinite(r)&&r>0?r:rr},ir=async(e,r)=>{try{const n=AbortSignal.timeout(tr);return await(await fetch(new URL("/_lunora/status",e),{redirect:"manual",signal:r===void 0?n:AbortSignal.any([r,n])})).body?.cancel(),!0}catch{return!1}},de=async(e,r,n)=>{const t=Date.now()+n.timeoutMs;for(;n.signal?.aborted!==!0;){const o=r(C(e));if(o!==void 0)return o;if(Date.now()>=t)return;try{await w(n.intervalMs??500,void 0,{signal:n.signal})}catch{return}}},W=(e,r)=>{Ze({crlfDelay:Number.POSITIVE_INFINITY,input:e}).on("line",r)},sr=5e3,ue=(e,r,n,t)=>{if(e.length===0)return;const o=Ce(e);if(o){t[o.level](`[lunora] ${o.text}`);return}const i=`[${r}] ${e}`;n==="stderr"?t.warn(i):t.info(i)},ar=(e,r,n=process.platform,t=T)=>{try{if(r==="SIGKILL"&&n==="win32"&&e.pid!==void 0){t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"});return}e.kill(r)}catch{}},fe=(e,r,n)=>{const t=e.direct===!0?{args:[...e.args],command:e.command,shell:!1}:ae(e.command,e.args),o=ie(t.command,t.args,{cwd:e.cwd??process.cwd(),env:e.env?{...process.env,...e.env}:process.env,shell:t.shell,stdio:[e.direct===!0?"ignore":"inherit","pipe","pipe"]});return W(o.stdout,i=>{r(i,"stdout")}),W(o.stderr,i=>{r(i,"stderr")}),{exited:new Promise(i=>{o.on("error",s=>{n?.(s),i(1)}),o.on("exit",(s,a)=>{i(s??(a?1:0))})}),kill:i=>{ar(o,i)}}},vt=(e,r)=>fe(e,(n,t)=>{ue(n.trimEnd(),e.tag,t,r)},n=>{r.error(`[${e.tag}] failed to start: ${n.message}`)}),gt=(e,r)=>{if(process.env.LUNORA_CONTAINER_LOGS==="0")return;const t=Re(e,"lunora").containers.map(o=>({className:o.className,exportName:o.exportName}));if(t.length!==0)return Ne({containers:t,onLine:o=>{const i=`[container:${o.name}] ${o.text}`;o.level==="error"?r.warn(i):r.info(i)},onUnavailable:o=>{r.warn(`[container] Docker engine unreachable — container logs unavailable (${o})`)}})},pt=async e=>{e.readyProbe?.abort(),await e.tunnel?.close(),await e.codegen?.close().catch(()=>{}),e.containerLogs?.close(),await e.studio?.close().catch(()=>{}),await e.hyperdrive?.stop().catch(()=>{});try{e.remoteCleanup?.(),e.serviceConfigCleanup?.()}catch{}},mt=async e=>await new Promise(r=>{const n={},t=s=>{e.info(`received ${s} — shutting down`),n.sigint&&process.off("SIGINT",n.sigint),n.sigterm&&process.off("SIGTERM",n.sigterm),r(0)},o=()=>{t("SIGINT")},i=()=>{t("SIGTERM")};n.sigint=o,n.sigterm=i,process.on("SIGINT",o),process.on("SIGTERM",i)}),wt=async(e,r,n)=>{let t=0,o;const i=l=>{e.kill(l),r?.kill(l)},s=()=>{t+=1,t===1?(n.info("received SIGINT — shutting down (press Ctrl-C again to force-kill)"),i("SIGTERM"),o=setTimeout(()=>{i("SIGKILL")},sr),o.unref()):i("SIGKILL")},a=()=>{i("SIGTERM")};process.on("SIGINT",s),process.on("SIGTERM",a);const c=await Promise.race([e.exited.then(l=>({code:l,who:"worker"})),...r===void 0?[]:[r.exited.then(l=>({code:l,who:"sidecar"}))]]);return r!==void 0&&(c.who==="sidecar"?(n.warn("[worker] the Lunora sidecar (wrangler dev) exited — shutting down the framework dev server"),e.kill("SIGTERM")):r.kill("SIGTERM"),await Promise.allSettled([e.exited,r.exited])),o&&clearTimeout(o),process.off("SIGINT",s),process.off("SIGTERM",a),c.code},y="2026.9.3",cr="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/",lr=/version (\S+)/u,B=/https:\/\/[\da-z-]+\.trycloudflare\.com/u,dr="1s",ur=5e3,K=async(e,r)=>await Promise.race([e.then(()=>!0),w(r,!1,{ref:!1})]),fr=6e4,vr='Vite answers 403 for hosts it does not know — add `server: { allowedHosts: [".trycloudflare.com"] }` to vite.config if the URL is blocked.',gr=async e=>{const r=`Install or update it: ${cr} (macOS: \`brew install cloudflared\`).`;let n;try{const o=await e({args:["--version"],captureStdoutSilently:!0,command:"cloudflared"});if(o.code!==0)return{message:`\`cloudflared --version\` exited ${String(o.code)}. ${r}`,ok:!1};n=o.stdout??""}catch(o){return Qe(o)&&o.code==="LOCAL_DEPENDENCY_MISSING"?{message:`--tunnel needs \`cloudflared\` ${y} or newer, and it is not on your PATH. ${r}`,ok:!1}:{message:`\`cloudflared --version\` could not run: ${o instanceof Error?o.message:String(o)}. ${r}`,ok:!1}}const t=V.coerce(lr.exec(n)?.[1])?.version;return t===void 0?{ok:!0,version:void 0}:V.gte(t,y)?{ok:!0,version:t}:{message:`--tunnel needs \`cloudflared\` ${y} or newer (protected quick tunnels); found ${t}. ${r}`,ok:!1}},pr=e=>{const r=[],n=[];for(const t of e??[]){const o=t.split(",").map(i=>i.trim()).filter(i=>i.length>0);o.length>0&&(n.push(...o.filter(i=>!i.includes("@"))),r.push(o.join(",")))}return{entries:r,invalid:n}},mr=(e,r)=>["tunnel","--url",e,"--output","json","--grace-period",dr,...r.flatMap(n=>["--allowed-mail",n])],wr=e=>{let r;try{r=JSON.parse(e)}catch{r=void 0}if(typeof r=="object"&&r!==null){const n=r,t=typeof n.message=="string"?n.message:"";return{level:typeof n.level=="string"?n.level:void 0,text:t,url:B.exec(t)?.[0]}}return{level:void 0,text:e,url:B.exec(e)?.[0]}},hr=e=>{e.warn(" ⚠ PUBLIC TUNNEL: anyone with the tunnel URL can reach this dev server — there is no sign-in in front of it."),e.warn(" Protect it with a one-time-PIN email allow-list: lunora dev --tunnel --allow-mail you@example.com"),e.warn(" (repeat --allow-mail, pass a comma-separated list, or allow a whole domain with '*@example.com')")},Sr=async(e,r,n,{intervalMs:t,timeoutMs:o=fr}={})=>{const i=await de(e,s=>s?.mode==="vite"?{origin:s.url,ownerPid:s.pid}:void 0,{intervalMs:t,signal:n,timeoutMs:o});return i===void 0&&!n.aborted&&r.warn(`--tunnel: the Vite dev server did not report its URL within ${String(o/1e3)}s — continuing without a tunnel.`),i},kr=e=>{const{allowMail:r,cwd:n,logger:t}=e,o=r.length===0,i=new AbortController,{signal:s}=i,a=()=>s.aborted,c=e.closeTimeoutMs??ur;let l;const d=()=>{i.abort()};process.once("SIGINT",d),process.once("SIGTERM",d);const p=(async()=>{const u=await gr(e.spawner??Xe);if(!u.ok){t.error(u.message),t.warn("continuing without a tunnel.");return}u.version===void 0&&t.warn(`could not read the cloudflared version — --tunnel needs ${y} or newer; trying anyway.`);const v=e.origin.kind==="fixed"?{origin:e.origin.origin,ownerPid:process.pid}:await Sr(n,t,s);if(v===void 0||s.aborted)return;o&&hr(t),t.info(`starting a Cloudflare quick tunnel to ${v.origin}${o?"":" (email one-time-PIN protected)"}…`);let S;const U={failedToStart:!1},Le=k=>{if(k.length===0)return;const g=wr(k);if(g.url!==void 0&&S===void 0){S=g.url,t.success(` ➜ Tunnel: ${g.url}${o?" (PUBLIC)":""}`),o||t.info(` Allowed: ${r.join(", ")} — visitors confirm with a one-time PIN; a session lasts up to 4 hours`),e.origin.kind==="vite"&&t.info(` ${vr}`),b(n,{tunnelUrl:g.url},{expectedPid:v.ownerPid});return}g.level==="error"||g.level==="fatal"?t.warn(`[cloudflared] ${g.text}`):t.debug?.(`[cloudflared] ${g.text}`)},P=(e.startChild??fe)({args:mr(v.origin,r),command:"cloudflared",direct:!0},Le,k=>{U.failedToStart=!0,t.error(`could not start cloudflared (${k.message}) — continuing without a tunnel.`)});l=P;const G=()=>{P.kill("SIGTERM")};s.addEventListener("abort",G,{once:!0});const F=await P.exited;s.removeEventListener("abort",G),l=void 0,S!==void 0&&b(n,{tunnelUrl:void 0},{expectedPid:v.ownerPid}),!(a()||U.failedToStart)&&t.warn(S===void 0?`cloudflared exited (${String(F)}) before a tunnel URL was assigned — continuing without a tunnel.`:`cloudflared exited (${String(F)}) — the tunnel is closed; the dev server keeps running.`)})().catch(u=>{t.error(`--tunnel failed (${u instanceof Error?u.message:String(u)}) — continuing without a tunnel.`)});return{close:async()=>{if(process.off("SIGINT",d),process.off("SIGTERM",d),i.abort(),await K(p,c))return;const u=l;u!==void 0&&(t.warn(`cloudflared did not exit within ${String(c/1e3)}s of SIGTERM — force-killing it.`),u.kill("SIGKILL"),!await K(u.exited,c)&&t.error(`could not confirm that cloudflared stopped — ${o?"the PUBLIC tunnel":"the tunnel"} may still be reachable. Stop the \`cloudflared\` process manually.`))}}},ht=e=>{const{cwd:r,logger:n,plan:t,tunnel:o}=e;if(o!==void 0)return kr({allowMail:o.allowMail,cwd:r,logger:n,origin:t.flavor==="wrangler"?{kind:"fixed",origin:t.workerOrigin}:{kind:"vite"},spawner:o.spawner,startChild:o.startChild})},Ir=(e,r)=>{const n=["dev"];if(e.apiSpec!==void 0&&n.push("--api-spec",e.apiSpec),Z(e.lazyFunctions)&&n.push("--lazy-functions"),e.port!==void 0&&n.push("--port",String(e.port)),e.workerPort!==void 0&&n.push("--worker-port",String(e.workerPort)),e.inspectorPort!==void 0&&n.push("--inspector-port",String(e.inspectorPort)),e.emitBindings!==void 0&&n.push("--emit-bindings",e.emitBindings),e.codegen===!1&&n.push("--no-codegen"),e.studio===!1&&n.push("--no-studio"),e.worker===!1&&n.push("--no-worker"),e.target!==void 0&&n.push("--target",e.target),e.tunnel===!0){n.push("--tunnel");for(const t of pr(e.allowMail).entries)n.push("--allow-mail",t)}return r&&n.push("--remote"),e.local===!0&&n.push("--local"),n},yr=1e4,Er=100,Lr=40,I=256*1024,Tr=new Set(["nuxt","sveltekit"]),Pr=["@lunora/rspack","@lunora/vite"],M=e=>{if(Tr.has(re(e).framework))return"framework-worker";const r=xe(e);return Pr.some(n=>r.has(n))?"vite":"wrangler"},ve=e=>{const r=A(e),n=Ge(e)?.scripts,t=typeof n=="object"&&n!==null?n.dev:void 0;if(typeof t!="string"||t.trim()===""||t.includes("lunora")){const o=ce(r,"vite",["dev"]);return{args:o.args,command:o.command}}return{args:["run","dev"],command:r}},br=e=>{Te(_e(e.logPath),{recursive:!0});const r=J(e.logPath,"w",384);let n;const t=ae(e.command,e.args);try{n=ie(t.command,t.args,{cwd:e.cwd,detached:!0,env:e.env,shell:t.shell,stdio:["ignore",r,r],windowsHide:!0})}finally{X(r)}return n.unref(),{exited:new Promise(o=>{n.on("error",()=>{o(1)}),n.on("exit",(i,s)=>{o(i??(s?1:0))})}),pid:n.pid}},_r=e=>{try{const{size:r}=Pe(e);if(r<=I)return Q(e,"utf8");const n=J(e,"r");try{const t=Buffer.alloc(I),o=be(n,t,0,I,r-I),i=t.toString("utf8",0,o);return i.slice(i.indexOf(`
|
|
2
|
-
`)+1)}finally{X(n)}}catch{return}},ge=(e,r)=>{const n=_r(e);if(n===void 0)return[];const t=n.split(`
|
|
3
|
-
`);return t.at(-1)===""&&t.pop(),r>0?t.slice(-r):t},pe=(e,r)=>r?.logFile??N(e,je),me=e=>{e.info(" Stop: lunora dev stop"),e.info(" Status: lunora dev status"),e.info(" Logs: lunora dev logs")},$r=(e,r)=>{ze(r,n=>{e.warn(n)})},Rr=(e,r)=>{e.warn(`Dev server already running at ${r.url} (pid ${String(r.pid)})`),me(e)},Nr=(e,r)=>{e.success(`Dev server running at ${r.url} (pid ${String(r.pid)})`),r.studioUrl!==void 0&&e.info(` Studio: ${r.studioUrl}`),r.tunnelUrl!==void 0&&e.info(` Tunnel: ${r.tunnelUrl}`),me(e)},Cr=3e4,Dr=async(e,r,n,t)=>{const o=await de(e,i=>i?.tunnelUrl,{intervalMs:n,timeoutMs:t});o===void 0?r.warn(" Tunnel: no URL yet — `lunora dev status` shows it once cloudflared reports one; `lunora dev logs` says why if it does not."):r.success(` Tunnel: ${o}`)},Or=async e=>{const{child:r,cwd:n,deadline:t,pollIntervalMs:o,probe:i}=e,s=r.exited.then(a=>({exitCode:a}));for(;Date.now()<t;){const a=C(n);if(a!==void 0&&a.pid!==process.pid&&await i(a.url))return{state:a,status:"ready"};const c=await Promise.race([s,w(o)]);if(c!==void 0)return{...c,status:"exited"}}return{status:"timeout"}},xr=async e=>{const{cwd:r,logger:n}=e,t=pe(r,void 0),o=or(e.readyTimeoutMs),i=(e.spawnDetached??br)({args:e.command.args,command:e.command.command,cwd:r,env:{...process.env,...e.env,[Me]:"1",[Ae]:t,...process.env.FORCE_COLOR===void 0?{NO_COLOR:"1"}:{},...e.json?{LUNORA_LOG_JSON:"1"}:{}},logPath:t}),s=await Or({child:i,cwd:r,deadline:Date.now()+o,pollIntervalMs:e.pollIntervalMs??_,probe:e.probe??ir});if(s.status==="ready")return Nr(n,s.state),e.tunnel===!0&&s.state.tunnelUrl===void 0&&await Dr(r,n,e.pollIntervalMs??_,e.tunnelUrlTimeoutMs??Cr),{code:0};if(s.status==="exited"){n.error(`dev server exited before becoming ready (exit code ${String(s.exitCode)}). Last output:`);for(const c of ge(t,Lr))n.error(` ${c}`);return E(r,i.pid??process.pid),{code:s.exitCode===0?1:s.exitCode}}i.pid!==void 0&&O(r)?.pid===process.pid&&b(r,{logFile:t,pid:i.pid});const a=i.pid===void 0?"":` (pid ${String(i.pid)})`;return n.warn(`dev server did not confirm ready within ${String(Math.round(o/1e3))}s — it may still be compiling${a}. Check \`lunora dev status\` and \`lunora dev logs\`; \`lunora dev stop\` shuts it down.`),{code:se.UNAVAILABLE}},we=e=>e.codegen!==!1&&!Ve(process.env[te]),he=e=>{const r={...Z(e.lazyFunctions)?{[Ue]:"1"}:{},...e.remote===!0?{LUNORA_REMOTE:"1"}:{},...e.codegen===!1?{[te]:"0"}:{}};return Object.keys(r).length>0?{env:r}:{}},St=async e=>{const{cwd:r,jsonLogs:n,logger:t,options:o,remote:i}=e,s=e.run??xr,a=M(r),c=a==="vite"&&D(o.target??ee(r))?"wrangler":a,l=c==="wrangler"?`http://localhost:${String(o.workerPort??8787)}`:"http://localhost:5173",d=De(r,{background:!0,mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:l});if(!d.ok)return d.existing!==void 0&&Rr(t,d.existing),{code:0};const f={[Oe]:String(process.pid)};$r(t,r);try{return c==="vite"&&o.tunnel!==!0?await s({command:ve(r),cwd:r,env:{...he({...o,remote:i}).env,...f},json:n,logger:t}):await s({command:{args:[process.argv[1]??"lunora",...Ir(o,i)],command:process.execPath},cwd:r,env:f,json:n,logger:t,tunnel:o.tunnel===!0})}finally{E(r,process.pid)}},Ar=e=>{try{const r=T("ps",["-o","pgid=","-p",String(e)],{encoding:"utf8"}),n=Number.parseInt(r.stdout.trim(),10);return Number.isInteger(n)&&n>0?n:void 0}catch{return}},Mr=(e,r,n=process.platform,t=T)=>{if(n==="win32"){try{t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"})}catch{}return}if(e.background===!0)try{r(-(Ar(e.pid)??e.pid),"SIGKILL");return}catch{}try{r(e.pid,"SIGKILL")}catch{}},Ur=(e,r)=>(r.json?h({pid:e.pid,stopped:!1}):r.logger.error(`dev server (pid ${String(e.pid)}) survived the force-kill — record kept; retry \`lunora dev stop\` or kill the process manually.`),{code:1}),Gr=async e=>{const{cwd:r,logger:n}=e,t=O(r),o=e.alive??(f=>f===t?.pid&&Fe(t)),i=e.signal??((f,p)=>{process.kill(f,p)}),s=e.pollIntervalMs??_,a=e.stopGraceMs??yr,c=e.platform??process.platform,l=e.spawnSyncImpl??T;if(t===void 0||!o(t.pid))return t!==void 0&&E(r,t.pid),e.json?h({running:!1,stopped:!1}):n.info("No dev server running."),{code:0};try{i(t.pid,"SIGTERM")}catch{}const d=Date.now()+a;for(;o(t.pid)&&Date.now()<d;)await w(s);return o(t.pid)&&(Mr(t,i,c,l),await w(s),o(t.pid))?Ur(t,e):(E(r,t.pid),e.json?h({pid:t.pid,stopped:!0}):n.success(`Stopped dev server (pid ${String(t.pid)}).`),{code:0})},Fr=e=>{const{cwd:r,logger:n}=e,t=C(r);if(t===void 0)return e.json?h({running:!1}):n.info("No dev server running."),{code:0};const o=e.now??Date.now,i=t.startedAt===void 0?Number.NaN:Date.parse(t.startedAt),s=Number.isFinite(i)?Math.max(0,Math.round((o()-i)/1e3)):void 0;if(e.json)return h({background:t.background===!0,logFile:t.logFile,mode:t.mode,pid:t.pid,ready:j(t),readyAt:t.readyAt,running:!0,startedAt:t.startedAt,studioUrl:t.studioUrl,tunnelUrl:t.tunnelUrl,uptimeSeconds:s,url:t.url}),{code:0};const a=[`pid ${String(t.pid)}`,...s===void 0?[]:[`uptime ${String(s)}s`],t.background===!0?"background":"foreground",j(t)?"ready":"starting"];return n.success(`Dev server running at ${t.url} (${a.join(", ")})`),t.studioUrl!==void 0&&n.info(` Studio: ${t.studioUrl}`),t.tunnelUrl!==void 0&&n.info(` Tunnel: ${t.tunnelUrl}`),t.logFile!==void 0&&n.info(` Logs: lunora dev logs (${t.logFile})`),{code:0}},jr=e=>{const{cwd:r,logger:n}=e,t=O(r),o=pe(r,t);if(!q(o))return n.info("No dev server logs found — output is captured when the server runs in background mode (`lunora dev --background`)."),{code:0};const i=ge(o,e.lines??Er);return i.length>0&&process.stdout.write(`${i.join(`
|
|
4
|
-
`)}
|
|
5
|
-
`),{code:0}},kt=e=>{const{cwd:r,json:n,lines:t,logger:o,subcommand:i}=e;if(i==="stop")return Gr({cwd:r,json:n,logger:o});if(i==="status")return Fr({cwd:r,json:n,logger:o});if(i==="logs")return jr({cwd:r,lines:t,logger:o});if(i!==void 0)return o.error(`dev: unknown subcommand "${i}" — expected stop | status | logs (or no subcommand to start the dev server)`),{code:se.USAGE}},It=(e,r,n)=>D(e)?(r!=="wrangler"&&n.info(`target ${e} runs its own dev server, so lunora dev serves the worker on it — start the frontend's dev server separately`),"wrangler"):r,yt=async e=>{const{logger:r}=e,n=await(e.start??Be)({log:(i,{stream:s,tag:a})=>{ue(i,a,s,r)},port:e.port,projectRoot:e.projectRoot});let t=()=>{};const o=new Promise(i=>{t=i});return{exited:Promise.race([o,n.exited]),kill:()=>{n.stop().then(()=>0,()=>1).then(t).catch(()=>{})}}},Vr=e=>{const{cwd:r,driver:n,options:t,studioPort:o,workerPort:i}=e,{projectConfig:s,toolchain:a}=n;if(s===void 0||a===void 0)throw new Error(`deploy target "${n.id}" runs its own dev server but ships no config projection or toolchain for it`);if(t.remote===!0)throw new Error(`--remote proxies bindings to Cloudflare; ${n.name} has no remote bindings to proxy to`);t.inspectorPort!==void 0&&t.logger.warn(`--inspector-port is a wrangler dev flag; ${n.name} dev has no inspector to pin`);const{command:c,projection:l}=We(n,r,"dev",f=>a.dev({configPath:f,extraArgs:["--port",String(i)]}));l!==void 0&&l.dropped.length>0&&t.logger.info(`${n.name} ignores these wrangler keys, so its dev server runs without them: ${l.dropped.join(", ")}`);const d=le(A(r),c);return{flavor:"wrangler",ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:()=>{},enabled:!1,withheld:[]},runsCodegenWatch:we(t),celldSession:!0,studioEnabled:t.studio!==!1,studioPort:o,workerEnabled:t.worker!==!1,workerOrigin:`http://localhost:${String(i)}`,workerPort:i,wrangler:{args:d.args,command:d.command,cwd:r,tag:c.tool}}},Se=(e=Je)=>Object.values(e()).some(r=>r?.some(n=>n.internal&&n.address==="::1")),$="wrangler.dev.jsonc",R=6173,m=8787,z=5173,ke=()=>{},Wr=(e,r)=>{if(!e.remote&&e.local===!0)return{args:[],plan:{bindings:[],cleanup:ke,enabled:!1,withheld:[]}};const t=(e.materializeDev??He)({projectRoot:r,remote:e.remote===!0}),i={bindings:t.remoteBindings.map(s=>`${s.binding} (${s.kind})`),cleanup:t.cleanup,enabled:e.remote===!0,reason:t.reason,withheld:t.withheld};return{args:t.configPath===void 0?[]:["--config",t.configPath],plan:i}},Ie=(e,r)=>{const n=[...new Set($e(r).services.map(s=>s.wranglerPath))],{cleanup:t,configPaths:o,withheld:i}=(e.materializeServiceConfigs??Ye)(n,{projectRoot:r});return{args:o.flatMap(s=>["--config",s]),cleanup:t,withheld:i}},Br=(e,r,n)=>{const t=L(e);return r.length===0||t===void 0?[]:[...n.length>0?[]:["--config",t],...r]},ye=e=>e.local===!0?["--local"]:[],H=e=>x(e).parsed?.dev?.ip,Ee=(e,r,n)=>{if(n!==void 0){const o=N(e,n);if(q(o)&&H(o)!==void 0)return[]}const t=L(e);return t!==void 0&&H(t)!==void 0?[]:r()?[]:["--ip","127.0.0.1"]},Kr=new Set(["127.0.0.1","::1","[::1]","localhost"]),zr=(e,r)=>{let n;try{n=Q(N(e,oe),"utf8")}catch{return[]}const t=[];for(const o of Ke(n)){let i;try{i=new URL(o.value)}catch{continue}Kr.has(i.hostname)&&i.port===String(r)&&t.push(o.key)}return t},Y=async(e,r)=>{if(e.workerPort!==void 0)return e.workerPort;const n=L(r);if(n!==void 0){const{parsed:i}=x(n);if(typeof i?.dev?.port=="number")return i.dev.port}const t=await(e.findFreePort??qe)(m);if(t===m)return t;const o=zr(r,m);if(o.length>0)throw new Error(`port ${String(m)} is in use, but ${oe} pins the worker origin to it (${o.join(", ")}). Serving on ${String(t)} would leave those URLs pointing at nothing. Stop whatever holds ${String(m)}, or run \`lunora dev --worker-port ${String(t)}\` and update those values to match.`);return t},Hr=(e,r)=>{if(e.inspectorPort!==void 0)return e.inspectorPort;const n=L(r);if(n===void 0)return;const{parsed:t}=x(n),o=t?.dev?.inspector_port;return typeof o=="number"?o:void 0},Yr=(e,r,n)=>{const t=Ee(r,e.hasIpv6Loopback??Se,$),o=ne(e.target??ee(r));if(o.toolchain===void 0)throw new Error(`deploy target "${o.id}" has no dev server to run the worker sidecar`);const i=Ie(e,r),s=o.toolchain.dev({configPath:$,extraArgs:[...t,...ye(e),"--var","WORKER_ENV:development",...i.args]}),a=le(n,s);return{cleanup:i.cleanup,sidecar:{args:a.args,command:a.command,cwd:r,tag:"worker"},withheld:i.withheld}},qr=(e,r)=>{e.worker===!1&&e.logger.warn(`--no-worker does not apply to the ${r} flavor: Vite owns the worker, codegen and studio in-process. Run your framework's dev script instead.`),e.inspectorPort!==void 0&&e.logger.warn(r==="framework-worker"?`--inspector-port does not apply to the ${r} flavor: pin \`dev.inspector_port\` in ${$} — that is the config the worker sidecar runs.`:`--inspector-port does not apply to the ${r} flavor: Vite owns the worker. Pin it in vite.config — \`lunora({ cloudflare: { inspectorPort: ${String(e.inspectorPort)} } })\`.`),e.local===!0&&r==="vite"&&e.logger.warn("--local does not apply to the vite flavor: Vite owns the worker. Turn remote bindings off in vite.config — `lunora({ cloudflare: { remoteBindings: false } })`.")},Jr=e=>{const r=e.cwd??process.cwd(),n=A(r),t=e.flavor??M(r);if(t==="vite"||t==="framework-worker"){const p=ve(r),u=t==="framework-worker"?Yr(e,r,n):void 0,v=u?.sidecar;return qr(e,t),{runsCodegenWatch:!1,flavor:t,ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:ke,enabled:e.remote===!0,withheld:u?.withheld??[]},...u?{serviceConfigCleanup:u.cleanup}:{},...v?{sidecar:v}:{},studioEnabled:!1,studioPort:e.port??R,workerEnabled:!0,workerOrigin:`http://localhost:${String(z)}`,workerPort:z,wrangler:{args:p.args,command:p.command,cwd:r,...he(e),tag:"vite"}}}const o=re(r),i=o.framework==="none"?void 0:`this project uses ${o.framework} — the worker runs inside Vite there. run \`${er(n,"dev")}\` for the full app (frontend + HMR); \`lunora dev\` starts only the worker.`,s=e.workerPort??m,a=Wr(e,r),c=Ee(r,e.hasIpv6Loopback??Se),l=e.inspectorPort===void 0?[]:["--inspector-port",String(e.inspectorPort)],d=Ie(e,r),f=ce(n,"wrangler",["dev","--port",String(s),...l,...c,...ye(e),"--var","WORKER_ENV:development",...a.args,...Br(r,d.args,a.args)]);return{runsCodegenWatch:we(e),flavor:t,frameworkHint:i,ipv4LoopbackForced:c.length>0,remote:{...a.plan,withheld:[...a.plan.withheld,...d.withheld]},serviceConfigCleanup:d.cleanup,studioEnabled:e.studio!==!1,studioPort:e.port??R,workerEnabled:e.worker!==!1,workerOrigin:`http://localhost:${String(s)}`,workerPort:s,wrangler:{args:f.args,command:f.command,cwd:r,tag:"wrangler"}}},Et=async e=>{const r=e.cwd??process.cwd();if(e.target!==void 0&&D(e.target))return Vr({cwd:r,driver:ne(e.target),options:e,studioPort:e.port??R,workerPort:await Y(e,r)});const n=e.flavor??M(r),t=n==="wrangler"?await Y(e,r):e.workerPort,o=n==="wrangler"?Hr(e,r):e.inspectorPort;return Jr({...e,cwd:r,flavor:n,inspectorPort:o,workerPort:t})};export{_ as P,kt as a,Rr as b,hr as c,ir as d,M as e,It as f,Et as g,$r as h,ht as i,vt as j,wt as k,we as l,yt as m,pr as n,gt as o,Jr as p,Hr as q,or as r,St as s,pt as t,Y as u,mt as w};
|