palbase 0.40.0 → 0.40.1
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/ai/index.js +2 -2
- package/dist/backend/engine.js +2 -2
- package/dist/backend/env.js +1 -1
- package/dist/backend/index.js +3 -3
- package/dist/backend/stack.js +1 -1
- package/dist/backend/test.js +2 -2
- package/dist/chunks/{VFZBC3RM.js → 2IKNMW7Y.js} +2 -2
- package/dist/chunks/{A2D7FD7W.js → 3Y6WVYKR.js} +1 -1
- package/dist/chunks/{4PBWRT4P.js → 5CTANDHW.js} +2 -2
- package/dist/chunks/{YZHSYYPA.js → 63SU2T7P.js} +1 -1
- package/dist/chunks/{QSON7FZZ.js → 6G3YX6EZ.js} +2 -2
- package/dist/chunks/{565OM5B4.js → 6NQBYUIY.js} +2 -2
- package/dist/chunks/{CTSLJSZE.js → 7CXVTSSA.js} +2 -2
- package/dist/chunks/{M5RJHNP6.js → BW62XV6A.js} +2 -2
- package/dist/chunks/{JWARTQKO.js → D3MW6KOP.js} +2 -2
- package/dist/chunks/{FULS6XEF.js → DP3GYHDU.js} +2 -2
- package/dist/chunks/{MDWMGUWL.js → ENRH3EIZ.js} +2 -2
- package/dist/chunks/{C4YKYPTT.js → H3K2PBLV.js} +2 -2
- package/dist/chunks/{BKIIOP4S.js → HPSVINM6.js} +1 -1
- package/dist/chunks/{RZF2I4FM.js → JY3YYVFL.js} +2 -2
- package/dist/chunks/{AG7NTVAG.js → LH4H2JDJ.js} +2 -2
- package/dist/chunks/{FQTHVNTU.js → MWNZCX43.js} +2 -2
- package/dist/chunks/{SCZSNZ7Q.js → NEWHG5BQ.js} +2 -2
- package/dist/chunks/{C2QJHZUF.js → QVUY5H2G.js} +1 -1
- package/dist/chunks/{MSMA7KGA.js → SSYFUO3Y.js} +2 -2
- package/dist/chunks/{UVBIMGFU.js → TEMKJNGH.js} +1 -1
- package/dist/chunks/{JY4SEIMB.js → TXIW7P2Z.js} +2 -2
- package/dist/chunks/{YJV5IDVY.js → W57GZCEA.js} +1 -1
- package/dist/chunks/{LT3QQ7XF.js → WMJRHKND.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/cloud/index.js +2 -2
- package/dist/refuse/ai.js +1 -1
- package/dist/refuse/backend.js +1 -1
- package/dist/refuse/cloud.js +1 -1
- package/dist/refuse/server.js +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/testing.js +2 -2
- package/dist/web/index.js +2 -2
- package/dist/web/message.js +2 -2
- package/dist/web/palbe.js +2 -2
- package/dist/web/paywall.js +2 -2
- package/dist/web/push-sw.js +1 -1
- package/dist/web/ssr.js +2 -2
- package/dist/web/vite.js +2 -2
- package/package.json +2 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! palbase 0.40.
|
|
2
|
-
import{b}from"./
|
|
1
|
+
/*! palbase 0.40.1 — Palbase SDK License (see LICENSE) */
|
|
2
|
+
import{b}from"./TEMKJNGH.js";import{a as k,b as A}from"./QVUY5H2G.js";import{randomBytes as _}from"node:crypto";async function N(){let n={keys:[{id:"k1",key:_(32).toString("base64url"),activates_at:new Date(0).toISOString()}],lookup:_(32).toString("base64url")},e=new b({fetch:async()=>n});return await e.load(),e.close(),e}var f=class extends Error{status;error;data;body;constructor(e,a,l,d){let u=d!==null&&typeof d=="object"?d:{},o=u.error??String(l);super(`${e} ${a} \u2192 ${l} ${o}${u.error_description?`: ${u.error_description}`:""}`),this.name="TestApiError",this.status=l,this.error=o,this.data=u.data,this.body=u}};function x(n,e){if(!n)throw new Error(`${e} is not set: the test client has nowhere to send its requests \u2014 \`palbase test\` sets it, as the platform's test run does`);return n}function B(n){let e=n.split(".")[1];if(!e)return null;try{let a=JSON.parse(Buffer.from(e,"base64url").toString("utf8"));return typeof a.exp=="number"?a.exp-Math.floor(Date.now()/1e3):null}catch{return null}}function L(n){try{return JSON.parse(n)}catch{return n}}function $(n){let e=x(n.baseUrl,"PALBASE_TEST_BASE_URL").replace(/\/+$/,""),a=n.apiKey||void 0,l=(n.authUrl??e).replace(/\/+$/,""),d=n.fetch??fetch,u=[],o=null;async function c(r,t,s,i,T={},O=!0){let g={...T.headers},m=h=>Object.keys(g).some(R=>R.toLowerCase()===h);a!==void 0&&!m("apikey")&&(g.apikey=a),o&&O&&!m("authorization")&&(g.authorization=`Bearer ${o}`),i!==void 0&&!m("content-type")&&(g["content-type"]="application/json");let I=Date.now(),p=await d(`${r}${s}`,{method:t,headers:g,body:i===void 0?void 0:JSON.stringify(i)}),E=await p.text(),S=E?L(E):void 0;if(u.push({method:t,path:s,status:p.status,ms:Date.now()-I}),!p.ok){let h=p.status===401&&o?B(o):null;throw h!==null&&h<=0?new f(t,s,p.status,{error:"access_token_expired",error_description:`this run's access token expired ${-h}s ago: a test user's token is made for one run \u2014 run the tests again (\`palbase test\` makes new ones)`}):new f(t,s,p.status,S)}return S}let y={requests:u,get:(r,t)=>c(e,"GET",r,void 0,t),post:(r,t,s)=>c(e,"POST",r,t,s),patch:(r,t,s)=>c(e,"PATCH",r,t,s),put:(r,t,s)=>c(e,"PUT",r,t,s),delete:(r,t)=>c(e,"DELETE",r,void 0,t),query:(r,t,s)=>c(e,"QUERY",r,t,s),async signInAs(r){let t=n.identities?.[r];if(!t){let s=Object.keys(n.identities??{});throw s.length===0?new Error(`no test user named ${JSON.stringify(r)}: this run has no test users \u2014 none were named (the machine's test users, or \`palbase test --users <names>\`), or they could not be made: the run's log says which`):new Error(`no test user named ${JSON.stringify(r)} \u2014 this run has: ${s.join(", ")}. A run makes the test users its machine names (palbase machines:update <machine> --test-users <names>) or \`palbase test --users <names>\` names`)}return t.accessToken?(o=t.accessToken,{id:t.id??"",email:t.email}):y.signIn(t)},async signIn(r){if(a===void 0)throw new Error("PALBASE_TEST_API_KEY is not set: a sign-in at the project's auth is made with a publishable key of the project's (palbase apps:sdkconfig shows one) \u2014 a run's test users come with their tokens: api.signInAs(name)");let t=i=>c(l,"POST","/auth/login",r,i?{headers:i}:{},!1),s;try{s=await t()}catch(i){let T=i instanceof f&&i.status===403?k(i.body):null;if(!T)throw i;s=await t(await A(T))}return o=s.access_token,s.user??{id:""}},async signOut(){await c(l,"POST","/auth/logout",void 0),o=null},asAnonymous(){o=null}};return y}function P(n){if(!n)return{};let e;try{e=JSON.parse(n)}catch{throw new Error("PALBASE_TEST_IDENTITIES is set but is not JSON: the run that started these tests handed over a broken set of test users")}if(e===null||typeof e!="object"||Array.isArray(e))throw new Error("PALBASE_TEST_IDENTITIES is not an object of test users by name");return e}var j={get stack(){return!!process.env.PALBASE_TEST_BASE_URL},minted(n){return Object.prototype.hasOwnProperty.call(P(process.env.PALBASE_TEST_IDENTITIES),n)}},w,q=new Proxy({},{get(n,e){return w??=$({baseUrl:process.env.PALBASE_TEST_BASE_URL??"",apiKey:process.env.PALBASE_TEST_API_KEY??"",authUrl:process.env.PALBASE_TEST_AUTH_URL,identities:P(process.env.PALBASE_TEST_IDENTITIES)}),Reflect.get(w,e,w)}});export{N as a,f as b,$ as c,j as d,q as e};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! palbase 0.40.
|
|
1
|
+
/*! palbase 0.40.1 — Palbase SDK License (see LICENSE) */
|
|
2
2
|
var Rt=d=>[...new Uint8Array(d)].map(t=>t.toString(16).padStart(2,"0")).join(""),At=d=>`pw-font-${d}`;function ne(d){return(typeof d=="string"?/^asset:([0-9a-f-]{36})$/.exec(d):null)?.[1]}async function an(d,t={}){let a={},l=t.fetch??((c,p)=>globalThis.fetch(c,p));return await Promise.all(d.map(async c=>{try{if(!/^https:\/\//.test(c.url))return;let p=await l(c.url,{signal:t.signal,credentials:"omit"});if(!p.ok)return;let g=await p.arrayBuffer();if(g.byteLength!==c.bytes&&c.bytes>0||Rt(await globalThis.crypto.subtle.digest("SHA-256",g))!==c.sha256.toLowerCase())return;let w=_t(c.kind,new Uint8Array(g));if(w===void 0)return;let y=await Et(c,g,w,t);y&&(a[c.id]=y)}catch{}})),a}var C=(d,t,a=0)=>d.length>=a+t.length&&t.every((l,c)=>d[a+c]===l),W=d=>[...d].map(t=>t.charCodeAt(0));function _t(d,t){switch(d){case"image":return C(t,[137,80,78,71,13,10,26,10])?"image/png":C(t,[255,216,255])?"image/jpeg":C(t,W("RIFF"))&&C(t,W("WEBP"),8)?"image/webp":void 0;case"video":return C(t,W("ftyp"),4)?C(t,W("qt "),8)?"video/quicktime":"video/mp4":C(t,[26,69,223,163])?"video/webm":void 0;case"font":return C(t,W("wOF2"))?"font/woff2":C(t,W("wOFF"))?"font/woff":C(t,W("OTTO"))?"font/otf":C(t,[0,1,0,0])||C(t,W("true"))?"font/ttf":void 0;case"html":return C(t,[80,75,3,4])||C(t,[80,75,5,6])?"application/zip":void 0;default:return}}async function Et(d,t,a,l){switch(d.kind){case"image":case"video":return{kind:d.kind,url:URL.createObjectURL(new Blob([t],{type:a}))};case"font":{let c=At(d.id),p=globalThis.FontFace,g=l.fonts??globalThis.document?.fonts;if(!p||!g)return;let h=new p(c,t,{display:"block"});return await h.load(),g.add(h),{kind:"font",url:"",family:c}}case"html":{let c=await Lt(new Uint8Array(t)),p=Ot(c);return p===void 0?void 0:{kind:"html",url:"",html:p}}}}async function Lt(d){let t=new DataView(d.buffer,d.byteOffset,d.byteLength),a=-1;for(let h=d.length-22;h>=Math.max(0,d.length-22-65535);h--)if(t.getUint32(h,!0)===101010256){a=h;break}if(a<0)throw new Error("not a zip");let l=t.getUint16(a+10,!0),c=t.getUint32(a+16,!0),p=new Map,g=0;for(let h=0;h<l;h++){if(t.getUint32(c,!0)!==33639248)throw new Error("a broken zip");let w=t.getUint16(c+10,!0),y=t.getUint32(c+20,!0),m=t.getUint32(c+24,!0),v=t.getUint16(c+28,!0),_=t.getUint16(c+30,!0),P=t.getUint16(c+32,!0),R=t.getUint32(c+42,!0),x=new TextDecoder().decode(d.subarray(c+46,c+46+v));if(c+=46+v+_+P,x.endsWith("/"))continue;if(x.startsWith("/")||x.split("/").includes(".."))throw new Error("a path leaving the zip");if(g+=m,p.size>=200||g>20971520)throw new Error("a zip too large");let T=R+30+t.getUint16(R+26,!0)+t.getUint16(R+28,!0),O=d.subarray(T,T+y);if(w===0)p.set(x,O);else if(w===8)p.set(x,await Pt(O));else throw new Error("a zip entry compressed another way")}return p}async function Pt(d){let t=new Response(new Uint8Array(d)).body.pipeThrough(new DecompressionStream("deflate-raw"));return new Uint8Array(await new Response(t).arrayBuffer())}var Tt={html:"text/html",css:"text/css",js:"text/javascript",mjs:"text/javascript",json:"application/json",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",webp:"image/webp",gif:"image/gif",svg:"image/svg+xml",mp4:"video/mp4",webm:"video/webm",woff2:"font/woff2",woff:"font/woff",ttf:"font/ttf",otf:"font/otf"};function qe(d,t){let a=Tt[d.split(".").pop()?.toLowerCase()??""]??"application/octet-stream",l="";for(let c=0;c<t.length;c+=32768)l+=String.fromCharCode(...t.subarray(c,c+32768));return`data:${a};base64,${btoa(l)}`}var $t="default-src 'none'; img-src data:; media-src data:; font-src data:; style-src 'unsafe-inline' data:; script-src 'unsafe-inline' data:";function Ot(d){let t=d.get("index.html");if(!t)return;let a=new TextDecoder().decode(t),l=y=>new TextDecoder().decode(d.get(y)),c=y=>{let m=y.replace(/^\.\//,"").split(/[?#]/)[0];return d.has(m)?m:void 0},p=y=>y.replace(/url\(\s*(['"]?)([^'")]+)\1\s*\)/g,(m,v,_)=>{let P=c(_);return P?`url("${qe(P,d.get(P))}")`:m}),g=globalThis.DOMParser;if(!g)return;let h=new g().parseFromString(a,"text/html");for(let y of[...h.querySelectorAll('link[rel~="stylesheet"][href]')]){let m=c(y.getAttribute("href")),v=h.createElement("style");v.textContent=m?p(l(m)):"",y.replaceWith(v)}for(let y of[...h.querySelectorAll("style")])y.textContent=p(y.textContent??"");for(let y of[...h.querySelectorAll("[src], [href], [poster]")])for(let m of["src","href","poster"]){let v=y.getAttribute(m);if(v===null)continue;let _=c(v);_?y.setAttribute(m,qe(_,d.get(_))):/^(https?:)?\/\//i.test(v)&&y.removeAttribute(m)}for(let y of[...h.querySelectorAll("[style]")])y.setAttribute("style",p(y.getAttribute("style")??""));let w=h.createElement("meta");return w.setAttribute("http-equiv","Content-Security-Policy"),w.setAttribute("content",$t),h.head.prepend(w),`<!doctype html>${h.documentElement.outerHTML}`}var K=(d,t,a)=>`M${d-a} ${t}a${a} ${a} 0 1 0 ${2*a} 0a${a} ${a} 0 1 0 ${-2*a} 0`,de={ad_free:["M3 7h18v10H3z","M4 4l16 16"],bell:["M6 16v-5a6 6 0 0 1 12 0v5l2 2H4z","M10 21a2 2 0 0 0 4 0"],bolt:["M13 2L4 14h7l-1 8 9-12h-7z"],book:["M5 4h11a3 3 0 0 1 3 3v13H8a3 3 0 0 1-3-3z","M5 17a3 3 0 0 1 3-3h11"],chart:["M3 20h18","M6 20v-8","M11 20V5","M16 20v-6","M21 20V9"],chat:["M4 5h16v11H9l-5 4z"],check:["M5 12l5 5 9-10"],check_circle:[K(12,12,9),"M8 12l3 3 5-6"],clock:[K(12,12,9),"M12 7v5l3 2"],cloud:["M7 18a4 4 0 0 1-.5-7.97A6 6 0 0 1 18 9a4.5 4.5 0 0 1-.5 9z"],crown:["M3 8l4 4 5-7 5 7 4-4-2 11H5z"],download:["M12 4v11","M7 10l5 5 5-5","M4 20h16"],gift:["M3 9h18v4H3z","M5 13v8h14v-8","M12 9v12","M12 9C10 5 6 5 6 7.5S12 9 12 9zm0 0c2-4 6-4 6-1.5S12 9 12 9z"],globe:[K(12,12,9),"M3 12h18","M12 3a14 14 0 0 1 0 18a14 14 0 0 1 0-18z"],heart:["M12 20s-7-4.5-9-9a4.5 4.5 0 0 1 9-3 4.5 4.5 0 0 1 9 3c-2 4.5-9 9-9 9z"],infinity:["M7 9a3 3 0 1 0 0 6c3 0 7-6 10-6a3 3 0 1 1 0 6c-3 0-7-6-10-6z"],lock_open:["M5 11h14v10H5z","M8 11V7a4 4 0 0 1 7.5-2"],music:["M9 18V5l11-2v13",K(6,18,3),K(17,16,3)],people:[K(9,8,3.5),"M2 20a7 7 0 0 1 14 0","M16 4.5a3.5 3.5 0 0 1 0 7","M18 14a6 6 0 0 1 4 6"],person:[K(12,8,4),"M4 21a8 8 0 0 1 16 0"],photo:["M3 5h18v14H3z",K(8.5,9.5,1.5),"M3 17l5-5 4 4 3-3 6 6"],shield:["M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z"],sparkles:["M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z","M19 15l.8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8z"],star:["M12 3l2.8 5.7 6.2.9-4.5 4.4 1 6.2L12 17.3l-5.5 2.9 1-6.2L3 9.6l6.2-.9z"],video:["M3 6h12v12H3z","M15 10l6-3v10l-6-3z"],arrow_right:["M4 12h15","M13 6l6 6-6 6"],calendar:["M4 6h16v15H4z","M4 10h16","M8 3v5","M16 3v5"],close:["M6 6l12 12","M18 6L6 18"],lock:["M5 11h14v10H5z","M8 11V7a4 4 0 0 1 8 0v4"],play:["M7 4l13 8-13 8z"],quote:["M5 17c3 0 4-2 4-5V7H4v5h3","M15 17c3 0 4-2 4-5V7h-5v5h3"],trophy:["M7 4h10v5a5 5 0 0 1-10 0z","M7 6H4v2a3 3 0 0 0 3 3","M17 6h3v2a3 3 0 0 1-3 3","M12 14v4","M8 21h8","M9 18h6v3H9z"]};var Be="http://www.w3.org/2000/svg";function pe(d,t,a){let l=d.createElementNS(Be,"svg");l.setAttribute("viewBox","0 0 24 24"),l.setAttribute("width",String(a)),l.setAttribute("height",String(a)),l.setAttribute("fill","none"),l.setAttribute("stroke","currentColor"),l.setAttribute("stroke-width","2"),l.setAttribute("stroke-linecap","round"),l.setAttribute("stroke-linejoin","round"),l.setAttribute("aria-hidden","true"),l.setAttribute("focusable","false");for(let c of t){let p=d.createElementNS(Be,"path");p.setAttribute("d",c),l.append(p)}return l}var dn=2,pn=2,f=(d,t=!1)=>({kind:"value",value:"plain",def:d,required:t}),j=d=>({kind:"value",value:"color",def:d}),We=d=>({kind:"value",value:"spacing",def:d}),zt=d=>({kind:"value",value:"radius",def:d}),X=(d=!1)=>({kind:"text",required:d}),Me=(d,t=!1)=>({kind:"list",item:d,required:t}),se=(d,t=!1)=>({kind:"object",item:d,required:t}),Ke=(d=!1)=>({kind:"component",required:d}),Re={kind:"components"},Ge={stack:{axis:f("vertical"),spacing:We(0),align:f("center"),distribution:f("start"),scroll:f(!1),children:Re},text:{text:X(),spans:Me("span"),font:f(),size:f(),weight:f(),line_height:f(),letter_spacing:f(),color:{kind:"value",value:"textColor",def:"text"},align:f("center"),max_lines:f(),uppercase:f(!1)},image:{src:f(void 0,!0),fit:f("fit"),aspect:f(),mask:se("mask"),overlay:{kind:"value",value:"textColor"},alt:X(),decorative:f(!1)},icon:{name:f(void 0,!0),color:j("accent"),size:f(24)},video:{src:f(void 0,!0),poster:f(),autoplay:f(!0),loop:f(!0),mute:f(!0),controls:f(!1),fit:f("fill"),aspect:f(),alt:X()},button:{action:se("action",!0),content:Ke(!0)},purchase_button:{content:Ke(!0),package:f()},package:{product:f(void 0,!0),plan:f(void 0,!0),selected:f(!1),offer:f(),children:Re},tabs:{},switch:{},carousel:{pages:Re,peek:f(0),spacing:f(8),loop:f(!1),auto_advance_ms:f(),indicator:se("indicator"),height:f("")},countdown:{},timeline:{items:Me("timeline_item",!0),line_color:j("accent"),line_width:f(2),icon_color:j("on_accent"),color:j("text"),spacing:f(16)},features:{items:Me("feature",!0),icon_color:j("accent"),color:j("text"),spacing:f(12)},spacer:{size:We()},divider:{color:j("muted"),thickness:f(1)},custom_html:{src:f(void 0,!0),height:f("",!0)}},Ht={style:{padding:{kind:"edges"},margin:{kind:"edges"},width:f(""),height:f(""),max_width:f(),background:{kind:"fill"},border:se("border"),corners:{kind:"corners"},shadow:se("shadow"),opacity:f(1)},border:{width:f(),color:j("")},shadow:{color:j(""),radius:f(0),x:f(0),y:f(0)},linear:{type:f(""),angle:f(180),stops:f()},radial:{type:f(""),center_x:f(.5),center_y:f(.5),radius:f(.5),stops:f()},image_fill:{type:f(""),src:f(),fit:f("fill")},video_fill:{type:f(""),src:f(),poster:f(),loop:f(!0)},animation:{type:f(""),duration_ms:f(300),delay_ms:f(0),easing:f("ease_out")},action:{type:f(""),url:f(),sheet:f(),package:f()},mask:{shape:f(""),radius:zt(0),depth:f(16)},span:{text:X(!0),bold:f(!1),italic:f(!1),underline:f(!1),strikethrough:f(!1),color:j(""),link:f()},timeline_item:{icon:f(void 0,!0),title:X(!0),text:X(),color:j("")},feature:{icon:f(void 0,!0),text:X(!0)},indicator:{style:f("dots"),color:j("muted"),active_color:j("accent"),size:f(8),spacing:f(6)},typography:{font:f("system"),size:f(),weight:f(400),line_height:f(),letter_spacing:f(0),text_style:f("body")}},Xe=new Set(["package","purchase_button"]),Ct={action:{type:f(""),url:f(),link:f(),sheet:f(),placement:f(),name:f(),value:f(),offer:f(),dismiss:f(!0)},presentation:{type:f(""),position:f("bottom"),auto_dismiss_seconds:f(),dismissible:f(!0),backdrop:j("#00000080")}},jt={modal:["type","dismissible","backdrop"],fullscreen:["type"],banner:["type","position","auto_dismiss_seconds","dismissible"],card:["type"]},Ft={linear:"linear",radial:"radial",image:"image_fill",video:"video_fill"},Nt=["top","bottom","leading","trailing"],Dt=["top_leading","top_trailing","bottom_leading","bottom_trailing"],Jt=Object.keys(Ge),un=Jt.filter(d=>!Xe.has(d)),Ye=/\{\{\s*([^{}]*?)\s*\}\}/g,Q=(d,t)=>typeof d=="object"&&d!==null&&Object.hasOwn(d,t),M=d=>typeof d=="object"&&d!==null&&!Array.isArray(d)?d:{},I=d=>Array.isArray(d)?d:[],G=(d,t)=>d??t;function It(d){let t="";for(let a of new TextEncoder().encode(d)){let l=String.fromCharCode(a);t+=/^[A-Za-z0-9._~-]$/.test(l)?l:`%${a.toString(16).toUpperCase().padStart(2,"0")}`}return t}function Vt(d){let t=Math.floor(d/86400),a=Math.floor(d%86400/3600),l=Math.floor(d%3600/60),c=d%60,p=h=>String(h).padStart(2,"0");return{countdown:t>0?`${t}d ${p(a)}:${p(l)}:${p(c)}`:a>0?`${a}:${p(l)}:${p(c)}`:`${p(l)}:${p(c)}`,days:String(t),hours:p(a),minutes:p(l),seconds:p(c)}}function fn(d){let t=[],a=c=>{if(Array.isArray(c))return c.forEach(a);if(typeof c!="object"||c===null)return;let p=c;if(p.type==="package"&&typeof p.product=="string"&&typeof p.plan=="string"){let g=`${p.product}.${p.plan}`;t.includes(g)||t.push(g)}for(let g of Object.keys(p).sort())a(p[g])},l=d;for(let c of["header","root","footer"])a(l[c]);for(let c of Object.keys(M(l.sheets)).sort())a(M(M(l.sheets)[c]).root);return t}function Ut(d){let t=new Map,a=c=>{if(Array.isArray(c))return c.forEach(a);if(typeof c!="object"||c===null)return;let p=c;if(p.type==="package"&&typeof p.offer=="string"){let g=`${p.product}.${p.plan}`;t.has(g)||t.set(g,p.offer)}for(let g of Object.keys(p).sort())a(p[g])},l=d;for(let c of["header","root","footer","sheets"])a(l[c]);return t}function gn(d,t){return _e(d,t).tree}function _e(d,t){return new ue("paywall",d,t).resolve()}function hn(d,t){return Ee(d,t).tree}function Ee(d,t){let{variables:a,...l}=t;return new ue("message",d,{...l,packages:{"":{...a}}}).resolve()}var ue=class{kind;d;opts;lang;texts={};palette;typography={};radii;spacing;packages;offers;selected="";tab="";paths=new WeakMap;tabsShown={};switchesOn={};countdowns={};constructor(t,a,l){this.kind=t,this.d=a,this.opts=l,this.offers=Ut(a);let c={};for(let[y,m]of Object.entries(l.packages??{})){let v={...m};this.open(y)||(m.price!==void 0?v.offer_price=m.price:delete v.offer_price),c[y]=v}this.packages=c;let p=M(this.d.locales),g=String(this.d.default_locale??""),h=l.locale;Q(p,h)||(h=h.split("-")[0]??"",Q(p,h)||(h=g)),this.lang=h;for(let y of[g,h])for(let[m,v]of Object.entries(M(p[y])))typeof v=="string"&&(this.texts[m]=v);let w=M(this.d.theme);this.palette=M(M(w.colors)[l.appearance]);for(let[y,m]of Object.entries(M(w.typography))){let v={};for(let[_,P]of Object.entries(this.objectFields("typography"))){let R=G(M(m)[_],P.def);R!=null&&(v[_]=R)}this.typography[y]=v}this.radii=M(w.radii),this.spacing=M(w.spacing)}open(t){let a=this.offers.get(t);return a!==void 0&&(this.opts.offers??[]).includes(a)}fields(t){if(!(this.kind==="message"&&Xe.has(t)))return Ge[t]}objectFields(t){return(this.kind==="message"?Ct[t]:void 0)??Ht[t]??{}}resolve(){let[t,a]=this.initialState();this.tab=t,this.selected=this.kind==="paywall"?a:"";let l={theme:{colors:this.palette,typography:this.typography,radii:this.radii,spacing:this.spacing}};this.kind==="paywall"&&(l.selected=a);let c=this.d.presentation;if(typeof c=="object"&&c!==null){let g=this.item("presentation",M(c),void 0)??{},h=jt[String(M(c).type)]??[];for(let w of Object.keys(g))h.includes(w)||delete g[w];l.presentation=g}for(let g of["header","root","footer"]){let h=this.d[g];if(typeof h=="object"&&h!==null){let w=this.component(M(h),g,"",!1);w&&(l[g]=w)}}let p={};if(typeof this.d.sheets=="object"&&this.d.sheets!==null){let g={};for(let[h,w]of Object.entries(M(this.d.sheets))){let y=this.component(M(M(w).root),`sheets.${h}.root`,"",!1);y&&(g[h]=y);let m=this.text(M(w).title,this.packages[this.selected]);m!==null&&(p[h]=m)}l.sheets=g}return{tree:l,pathOf:g=>this.paths.get(g),tabs:this.tabsShown,switches:this.switchesOn,sheetTitles:p,countdowns:this.countdowns,language:this.lang}}switchOn(t,a){return this.opts.switches&&Object.hasOwn(this.opts.switches,a)?this.opts.switches[a]:this.opts.switch?this.opts.switch==="on":t.default===!0}tabShown(t,a){let c=I(t.tabs).map(M).map(g=>String(g.id)),p=this.opts.tabs?.[a];return p!==void 0&&c.includes(p)?p:this.opts.tab?this.opts.tab:typeof t.default=="string"&&t.default!==""?t.default:c[0]??""}initialState(){let t="",a="",l="",c="",p=(h,w)=>{if(Array.isArray(h)){h.forEach((m,v)=>p(m,`${w}[${v}]`));return}if(typeof h!="object"||h===null)return;let y=h;switch(y.type){case"package":{let m=`${y.product}.${y.plan}`;l===""&&(l=m),y.selected===!0&&a===""&&(a=m);break}case"switch":{let m=this.switchOn(y,w),v=m?y.on_package:y.off_package;typeof v=="string"&&c===""&&(c=v),p(m?y.on:y.off,`${w}.${m?"on":"off"}`);return}case"tabs":{let m=this.tabShown(y,w);t===""&&(t=m),I(y.tabs).forEach((v,_)=>{let P=M(v);P.id===m&&(typeof P.package=="string"&&c===""&&(c=P.package),p(P.content,`${w}.tabs[${_}].content`))});return}}for(let m of Object.keys(y).sort())m!=="sheets"&&p(y[m],w===""?m:`${w}.${m}`)};for(let h of["header","root","footer"])p(this.d[h],h);let g=this.opts.selected||c||a||l;return[t,g]}holds(t,a){let l=a||this.selected;for(let[c,p]of Object.entries(t))switch(c){case"selected_package":if(!I(p).includes(this.selected))return!1;break;case"intro_eligible":if((this.opts.eligible??[]).includes(l)!==p)return!1;break;case"offer_open":if(this.open(l)!==p)return!1;break;case"platform":if(!I(p).includes(this.opts.platform))return!1;break;case"locale":if(!I(p).includes(this.opts.locale)&&!I(p).includes(this.lang))return!1;break;case"appearance":if(p!==this.opts.appearance)return!1;break;case"tab":if(p!==this.tab)return!1;break}return!0}component(t,a,l,c){if(typeof t.visible=="object"&&t.visible!==null&&!this.holds(M(t.visible),l))return null;let p=String(t.type),g=l,h=c;p==="package"&&(g=`${t.product}.${t.plan}`,h=g===this.selected);let w={type:p};this.paths.set(w,a),Q(t,"id")&&(w.id=t.id);let y={...M(t.style)};h&&Object.assign(y,M(M(t.states).selected)),Object.keys(y).length>0&&(w.style=this.style(y)),typeof t.animation=="object"&&t.animation!==null&&(w.animation=this.defaults(M(t.animation),this.objectFields("animation")));let m=this.packages[g||this.selected];switch(p){case"tabs":return this.tabs(t,w,a,g);case"switch":return this.switchOf(t,w,a,g,h);case"countdown":return this.countdown(t,w,a,g,h);case"purchase_button":{let v=typeof t.package=="string"?t.package:this.selected;w.package=v,this.open(v)&&(w.offer=this.offers.get(v));let _=this.component(M(t.content),`${a}.content`,v,h);return _?(w.content=_,w):null}case"package":w.product=t.product,w.plan=t.plan,w.selected=h,typeof t.offer=="string"&&(this.opts.offers??[]).includes(t.offer)&&(w.offer=t.offer)}for(let[v,_]of Object.entries(this.fields(p)??{})){if(p==="package"&&(v==="product"||v==="plan"||v==="selected"||v==="offer"))continue;let P=t[v],R=Q(t,v);switch(_.kind){case"component":{if(!R)continue;let x=this.component(M(P),`${a}.${v}`,g,h);if(!x&&_.required)return null;x&&(w[v]=x);break}case"components":{let x=[];I(P).forEach((T,O)=>{let V=this.component(M(T),`${a}.${v}[${O}]`,g,h);V&&x.push(V)}),w[v]=x;break}case"text":{let x=this.text(P,m);if(x!==null)w[v]=x;else if(_.required||p==="text"&&v==="text"&&R)return null;break}case"list":{let x=[];for(let T of I(P)){let O=this.item(_.item,M(T),m);O&&x.push(O)}if(p==="text"&&x.length===0&&R)return null;R&&(w[v]=x);break}case"object":{if(R){let x=this.object(M(P),_.item,m);if(x===null&&_.required)return null;w[v]=x}else v==="indicator"&&(w[v]=this.object({},_.item,m));break}case"value":{let x=this.value(G(P,_.def),_);x!=null&&x!==""&&(w[v]=x);break}}}return p==="text"&&this.typeStyle(w),w}typeStyle(t){if(typeof t.size!="string"){t.size!==void 0&&t.size!==null&&(t.text_style="body");return}let a=this.typography[t.size];for(let l of["font","weight","line_height","letter_spacing","text_style"])!Q(t,l)&&a?.[l]!==void 0&&a?.[l]!==null&&(t[l]=a[l]);t.size=a?.size??null}tabs(t,a,l,c){let p=this.tabShown(t,l);this.tabsShown[l]=p;let g=[];return I(t.tabs).forEach((h,w)=>{let y=M(h),m=y.id===p,v=this.component(M(y.label),`${l}.tabs[${w}].label`,c,m);if(g.push({id:y.id,selected:m,label:v}),m){let _=this.component(M(y.content),`${l}.tabs[${w}].content`,c,!1);_&&(a.content=_)}}),a.tabs=g,typeof t.bar=="object"&&t.bar!==null&&(a.bar=this.style(M(t.bar))),a}switchOf(t,a,l,c,p){let g=this.switchOn(t,l);this.switchesOn[l]=g,a.on=g;let h=this.text(t.label,this.packages[c||this.selected]);h!==null&&(a.label=h),a.tint=this.color(String(G(t.tint,"accent")));let w=g?"on":"off",y=this.component(M(t[w]),`${l}.${w}`,c,p);return y&&(a.content=y),a}countdown(t,a,l,c,p){let g=M(t.target),h=this.opts.now??Date.now(),w;if(typeof g.after_open_seconds=="number"?w=Math.trunc(g.after_open_seconds)-(this.opts.openedSecondsAgo??0):w=Math.trunc((Date.parse(String(g.at))-h)/1e3),w>0){a.state="counting",a.remaining_seconds=w,this.countdowns[l]=w;let y=this.packages,m={};for(let[_,P]of Object.entries(y))m[_]={...P,...Vt(w)};this.packages=m;let v=this.component(M(t.counting),`${l}.counting`,c,p);return v&&(a.content=v),this.packages=y,a}if(a.state="ended",a.remaining_seconds=0,typeof t.ended=="object"&&t.ended!==null){let y=this.component(M(t.ended),`${l}.ended`,c,p);y&&(a.content=y)}return a}item(t,a,l){let c={};for(let[p,g]of Object.entries(this.objectFields(t))){let h=a[p];if(g.kind==="text"){let w=this.text(h,l);if(w!==null)c[p]=w;else if(g.required)return null}else{let w=g.def,y=this.value(G(h,w),g);y!=null&&(Q(a,p)||w!=null&&w!=="")&&(c[p]=y)}}return c}object(t,a,l){if(a==="style")return this.style(t);let c=this.item(a,t,l)??{};if(a==="action")for(let p of["url","link"]){if(typeof c[p]!="string")continue;let g=qt(c[p],l);if(g===null)return null;c[p]=g}return c}defaults(t,a){let l={};for(let[c,p]of Object.entries(a)){let g=G(t[c],p.def);g!=null&&g!==""&&(l[c]=g)}return l}style(t){let a={};for(let[l,c]of Object.entries(t)){let p=this.objectFields("style")[l];if(!p){a[l]=c;continue}switch(p.kind){case"edges":a[l]=this.sides(c,Nt,this.spacing);break;case"corners":a[l]=this.sides(c,Dt,this.radii);break;case"fill":a[l]=this.fill(c);break;case"object":{let g=M(c),h={};for(let[w,y]of Object.entries(this.objectFields(p.item))){let m=G(g[w],y.def);m!=null&&m!==""&&(h[w]=this.value(m,y))}a[l]=h;break}default:a[l]=c}}return a}sides(t,a,l){let c={};for(let p of a){let g=typeof t=="object"&&t!==null?G(M(t)[p],0):t;c[p]=Ae(g,l)}return c}fill(t){if(typeof t=="string")return{type:"color",color:this.color(t)};let a=M(t),l=this.defaults(a,this.objectFields(Ft[String(a.type)]??""));return Array.isArray(a.stops)&&(l.stops=a.stops.map(c=>({color:this.color(String(M(c).color)),at:M(c).at}))),l}value(t,a){if(a.kind!=="value")return t;switch(a.value){case"color":return typeof t=="string"&&t!==""?this.color(t):t;case"textColor":return typeof t=="string"?this.color(t):t==null?t:this.fill(t);case"spacing":return Ae(t,this.spacing);case"radius":return Ae(t,this.radii);default:return t}}color(t){let a=Object.hasOwn(this.palette,t)?this.palette[t]:void 0;return typeof a=="string"?a:t}text(t,a){let l;if(typeof t=="string")l=t;else if(typeof t=="object"&&t!==null&&!Array.isArray(t)){let p=t.t;l=typeof p=="string"&&Object.hasOwn(this.texts,p)?this.texts[p]:""}else return null;let c=!1;return l=l.replace(Ye,(p,g)=>{let h=a&&Object.hasOwn(a,g)?a[g]:void 0;return typeof h!="string"||h===""?(c=!0,""):h}),c||l.trim()===""?null:l}};function qt(d,t){let a=!1,l=d.replace(Ye,(c,p)=>{let g=t&&Object.hasOwn(t,p)?t[p]:void 0;return typeof g!="string"||g===""?(a=!0,""):It(g)});return a?null:l}function Ae(d,t){return typeof d=="string"?Object.hasOwn(t,d)?t[d]:null:d}var Bt={packages:"Choose a plan",close:"Close",restore:"Restore purchases",page:(d,t)=>`${d} of ${t}`},ee={system:'-apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',rounded:'ui-rounded, "SF Pro Rounded", Nunito, "Varela Round", system-ui, -apple-system, sans-serif',serif:'ui-serif, "New York", "Iowan Old Style", Georgia, "Times New Roman", serif',mono:'ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace'},Wt=1.35,Ze=20,Kt=new Set(["button","purchase_button","package","text"]),Gt=new Set(["ar","he","fa","ur","ps","yi","dv","ug","ku","sd"]),Qe={ease_out:"cubic-bezier(0.16, 1, 0.3, 1)",ease_in_out:"cubic-bezier(0.65, 0, 0.35, 1)",spring:"cubic-bezier(0.34, 1.56, 0.64, 1)"},Xt={start:"flex-start",center:"center",end:"flex-end",stretch:"stretch"},Yt={start:"flex-start",center:"center",end:"flex-end",space_between:"space-between",space_around:"space-around",space_evenly:"space-evenly"},Zt={stack:["children"],package:["children"],button:["content"],purchase_button:["content"],tabs:["tabs","content"],switch:["content"],carousel:["pages"],countdown:["content"]},Qt={package:["selected"],purchase_button:["package"],switch:["on"],countdown:["state","remaining_seconds"]},en=`
|
|
3
3
|
:host{display:block;height:100%}
|
|
4
4
|
.pw2{position:relative;box-sizing:border-box;display:flex;flex-direction:column;height:100%;overflow:hidden;font-family:${ee.system};font-size:1rem;line-height:1.3;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;text-rendering:optimizeLegibility}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! palbase 0.40.
|
|
1
|
+
/*! palbase 0.40.1 — Palbase SDK License (see LICENSE) */
|
|
2
2
|
import{lstat as j,mkdir as F,readdir as M,readFile as N,rename as J,rm as T,writeFile as v}from"node:fs/promises";import{join as E}from"node:path";var o="palbase/strings",f="_meta.json",p=3,y=2,$=r=>typeof r=="object"&&r!==null&&!Array.isArray(r);function m(r){try{return Intl.getCanonicalLocales(r)[0]}catch{return}}function b(r,n){try{return JSON.parse(r)}catch(t){throw new Error(`${n} is not JSON: ${t.message}`)}}async function x(r){let n=E(r,o);try{if(!(await j(n)).isDirectory())throw new Error(`${o} is not a directory: the strings live in a directory of their own`)}catch(e){if(e.code==="ENOENT")return;throw e}let t=new Map;for(let e of await M(n,{withFileTypes:!0}))if(!(e.name.startsWith(".")||!e.name.endsWith(".json"))){if(!e.isFile())throw new Error(`${o}/${e.name} is not a regular file`);t.set(e.name,await N(E(n,e.name),"utf8"))}return t.size===0?void 0:t}async function R(r,n=[p,y]){let t=await x(r);if(t===void 0)return;let e=t.get(f);if(e===void 0)throw new Error(`${o}/${f} is missing: it names the source language, and without it the language files are not a table`);let u=b(e,`${o}/${f}`);if(!$(u))throw new Error(`${o}/${f}: not a JSON object`);let s=u.version;if(s===y&&!n.includes(y))throw new Error(`${o}/${f}: version 2 is the earlier format ({"value", "state"} for each sentence) \u2014 run \`npx palbase strings\` once, and it writes the table as version ${p}`);if(typeof s!="number"||!n.includes(s))throw new Error(`${o}/${f}: version ${JSON.stringify(s)} is not one this reads (${n.join(" or ")})`);let a=u.source;if(typeof a!="string"||m(a)!==a)throw new Error(`${o}/${f}: source ${JSON.stringify(a)} is not a language tag as it is written (${m(String(a))??"en, en-US, zh-Hans"})`);let S=new Map;for(let w of[...t.keys()].sort()){if(w===f)continue;let i=w.slice(0,-5),g=`${o}/${w}`,d=m(i);if(d===void 0)throw new Error(`${g}: ${JSON.stringify(i)} is not a language tag`);if(d!==i)throw new Error(`${g}: the language is written ${d} \u2014 name the file ${d}.json`);if(i===a)throw new Error(`${g}: ${i} is the source language \u2014 its text is the key itself, and it has no file`);if(i==="und"||i==="root"||i.startsWith("x-"))throw new Error(`${g}: ${i} is no language a request names \u2014 name the file for one (en, de, pt-BR)`);let O=b(t.get(w),g);if(!$(O))throw new Error(`${g}: not a JSON object of "sentence": "translation"`);let h=new Map;for(let[l,c]of Object.entries(O))if(s===p){if(typeof c!="string")throw new Error(`${g}: the translation of ${JSON.stringify(l)} is not a string`);h.set(l,c)}else if(typeof c=="string")h.set(l,c);else{if(!$(c)||typeof c.value!="string")throw new Error(`${g}: key ${JSON.stringify(l)} is not {"value": string, "state": string}`);h.set(l,c.state==="missing"?"":c.value)}S.set(i,h)}return{format:s,source:a,languages:S}}async function A(r){let n=await R(r,[p]);if(n===void 0)return;let t={};Object.setPrototypeOf(t,null);for(let[e,u]of n.languages)for(let[s,a]of u)Object.hasOwn(t,s)||(t[s]={},Object.setPrototypeOf(t[s],null)),t[s][e]=a;return{source:n.source,locales:[n.source,...n.languages.keys()],strings:t}}export{A as a};
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! palbase 0.40.
|
|
2
|
+
/*! palbase 0.40.1 — Palbase SDK License (see LICENSE) */
|
|
3
3
|
var nw=Object.defineProperty;var We=(e,t)=>{for(var n in t)nw(e,n,{get:t[n],enumerable:!0})};import{parseArgs as qw}from"node:util";var sw=Symbol.for("palbase.data"),aw=globalThis,od=aw[sw]??=new WeakSet;function rw(e){return e instanceof Date||ArrayBuffer.isView(e)||e instanceof ArrayBuffer}function Ma(e){let t=[e];for(;t.length>0;){let n=t.pop();if(!(typeof n!="object"||n===null||od.has(n)||rw(n))){od.add(n);for(let s of Array.isArray(n)?n:Object.values(n))t.push(s)}}return e}var iw="X-PoW-Challenge-ID",ow="X-PoW-Nonce";function ld(e){if(typeof e!="object"||e===null)return null;let t=e;if(t.error!=="pow_required"||typeof t.challenge!="object"||t.challenge===null)return null;let{id:n,prefix:s,difficulty:a}=t.challenge;return typeof n!="string"||typeof s!="string"||typeof a!="number"||!Number.isInteger(a)||a<0?null:{id:n,prefix:s,difficulty:a}}function lw(e){return 8*2**e}var Vn;async function dw(){if(Vn)return Vn;let e=globalThis;if(e.process?.versions?.node!==void 0||e.process?.versions?.bun!==void 0)try{let s=(await import("node:crypto")).createHash;if(typeof s=="function")return Vn=a=>new Uint8Array(s("sha256").update(a).digest()),Vn}catch{}let t=new TextEncoder;return Vn=async n=>new Uint8Array(await crypto.subtle.digest("SHA-256",t.encode(n))),Vn}function pw(e){let t=0;for(let n of e){if(n===0){t+=8;continue}return t+Math.clz32(n)-24}return t}var uw=4096,cw=typeof globalThis.setImmediate=="function"?()=>new Promise(e=>globalThis.setImmediate(e)):()=>new Promise(e=>setTimeout(e,0));async function dd(e,t={}){if(e.difficulty>24)throw new Error(`proof-of-work: difficulty ${e.difficulty} is above auth's ceiling of 24`);let n=await dw(),s=lw(e.difficulty),a=Date.now()+(t.timeLimitMs??12e4);for(let r=0;r<s;r++){if(r%uw===0&&r>0){if(await cw(),t.signal?.aborted)throw t.signal.reason??new DOMException("proof-of-work aborted","AbortError");if(Date.now()>a)throw new Error(`proof-of-work: gave up on difficulty ${e.difficulty} after ${r} attempts`)}let i=n(e.prefix+r),o=i instanceof Uint8Array?i:await i;if(pw(o)>=e.difficulty)return{[iw]:e.id,[ow]:String(r)}}throw new Error(`proof-of-work: no nonce for difficulty ${e.difficulty} within ${s} attempts`)}var C=class extends Error{status;code;requestId;retryAfter;body;op;get isNetworkError(){return this.code==="network_error"}constructor(t){super(t.message,t.cause===void 0?void 0:{cause:t.cause}),this.name="PalbaseError",this.op=t.op,this.status=t.status,this.code=t.code,this.requestId=t.requestId,this.retryAfter=t.retryAfter,this.body=t.body}};function c(e,t,n){let s=String(n);if(s===""||s==="."||s==="..")throw new C({op:e,status:0,code:"invalid_path_parameter",message:`${e}: ${t} cannot be ${s===""?"empty":JSON.stringify(s)}`});return encodeURIComponent(s)}var hw=new Set(["apikey","authorization"]),mw=64*1024;function ki(e){let t=Ia(e.baseUrl),n=e.fetch??globalThis.fetch,s=e.solveProofOfWork??!0,a=async(r,i)=>{let o=yw(t,r),l=new Headers;for(let[b,w]of Object.entries(r.headers??{}))w!=null&&!hw.has(b.toLowerCase())&&l.set(b,String(w));for(let[b,w]of Object.entries(i))l.set(b,w);await bw(r,e.credentials,l),l.has("accept")||l.set("accept","application/json");let p={method:r.method,headers:l,redirect:"manual",signal:r.signal,...r.keepalive?{keepalive:!0}:{}};r.body&&r.body.value!==void 0&&(l.set("content-type",r.body.contentType),p.body=ww(r.body.kind,r.body.value),Hn(p.body)||(p.duplex="half"));let d;try{d=r.onUploadProgress&&e.xmlHttpRequest?await gw(e.xmlHttpRequest,r.op,o,p,r.onUploadProgress):await n(o,p)}catch(b){throw r.signal?.aborted||b instanceof C?b:new C({op:r.op,status:0,code:"network_error",message:`${r.op}: the request did not reach ${t.origin}`,cause:b})}let u=r.answers[d.status]??(r.ownResponse&&d.status>=200&&d.status<300?"raw":void 0);if(u===void 0)throw d.status>=200&&d.status<300?(await d.body?.cancel(),new C({op:r.op,status:d.status,code:"undeclared_answer",message:`${r.op}: the server answered ${d.status}, which its contract does not declare; the request may have taken effect`})):await _w(r.op,d);let g;switch(u){case"json":try{g=Ma(await d.json())}catch(b){throw new C({op:r.op,status:d.status,code:"invalid_json",message:`${r.op}: the answer is not JSON`,cause:b})}break;case"raw":g=d;break;default:await d.body?.cancel(),g=void 0}return{status:d.status,data:g,headers:d.headers}};return{async send(r){try{return await a(r,{})}catch(i){let o=s&&i instanceof C&&i.status===403?ld(i.body):null,l=r.body?.value;if(!o||r.body?.kind==="raw"&&!Hn(l))throw i;let p=i,d;try{d=await dd(o,{signal:r.signal})}catch(u){throw r.signal?.aborted?u:new C({op:r.op,status:403,code:"pow_unsolved",message:`${r.op}: ${u instanceof Error?u.message:String(u)}`,requestId:p.requestId,body:p.body,cause:u})}return a(r,d)}}}}function gw(e,t,n,s,a){return new Promise((r,i)=>{let o=s.signal;if(o?.aborted)return i(o.reason);let l=new e,p=()=>l.abort();o?.addEventListener("abort",p,{once:!0});let d=u=>{o?.removeEventListener("abort",p),u()};l.open(s.method??"GET",n.href),l.responseType="arraybuffer",new Headers(s.headers).forEach((u,g)=>l.setRequestHeader(g,u)),l.upload&&(l.upload.onprogress=u=>{u.lengthComputable&&a(u.loaded,u.total)}),l.onload=()=>d(()=>{if(l.responseURL!==""&&l.responseURL!==n.href){i(new C({op:t,status:0,code:"undeclared_redirect",message:`${t}: the server redirected; the answer is another address's, and was not read`}));return}if(l.status<200||l.status>599)return i(new TypeError(`${t}: answered ${l.status}`));let u=l.status===204||l.status===205||l.status===304;r(new Response(u?null:l.response,{status:l.status,statusText:l.statusText,headers:fw(l.getAllResponseHeaders())}))}),l.onerror=()=>d(()=>i(new TypeError(`${t}: the request failed`))),l.onabort=()=>d(()=>i(o?.reason??new DOMException("The request was aborted","AbortError"))),l.send(s.body)})}function fw(e){let t=new Headers;for(let n of e.split(/\r?\n/)){let s=n.indexOf(":");if(!(s<=0))try{t.append(n.slice(0,s).trim(),n.slice(s+1).trim())}catch{}}return t}function Ia(e){let t;try{t=new URL(e)}catch{throw new TypeError(`baseUrl ${JSON.stringify(e)} is not an absolute URL`)}let n=t.hostname==="localhost"||t.hostname.endsWith(".localhost")||t.hostname==="127.0.0.1"||t.hostname==="[::1]";if(t.protocol!=="https:"&&!(t.protocol==="http:"&&n))throw new TypeError(`baseUrl must be https (http only for a loopback address): ${t.origin}`);if(t.username||t.password||t.search||t.hash)throw new TypeError("baseUrl carries no user, query or fragment");if(t.pathname!=="/"&&t.pathname!=="")throw new TypeError("baseUrl is the project origin alone, with no path");return t}function yw(e,t){if(!t.path.startsWith("/")||t.path.startsWith("//"))throw new C({op:t.op,status:0,code:"invalid_path",message:`${t.op}: path ${JSON.stringify(t.path)} is not absolute`});let n=new URL(t.path+vw(t.query),e);if(n.origin!==e.origin)throw new C({op:t.op,status:0,code:"invalid_path",message:`${t.op}: path leaves the project's origin`});return n}async function bw(e,t,n){if(e.auth.length===0)return;let s=e.auth.some(i=>i.includes("userJWT"))?await t.accessToken?.():void 0,a=i=>{switch(i){case"ApiKeyAuth":return!!(t.publishableKey??t.secretKey);case"SecretKeyAuth":case"AdminAuth":return!!(t.secretKey??t.token);case"PalbaseBearer":return!!t.token;case"userJWT":return!!s&&!t.token;default:return!1}},r=e.auth.filter(i=>i.every(a)).reduce((i,o)=>i===void 0||o.length>i.length?o:i,void 0);if(!r)throw new C({op:e.op,status:0,code:"missing_credential",message:`${e.op} needs one of: ${e.auth.map(i=>i.length===0?"nothing":i.join(" + ")).join(" | ")}`});for(let i of r)i==="ApiKeyAuth"?n.set("apikey",t.publishableKey??t.secretKey):(i==="SecretKeyAuth"||i==="AdminAuth"||i==="PalbaseBearer")&&t.token?n.set("authorization",`Bearer ${t.token}`):i==="SecretKeyAuth"||i==="AdminAuth"?n.set("apikey",t.secretKey):i==="userJWT"&&n.set("authorization",`Bearer ${s}`)}function ww(e,t){switch(e){case"json":return JSON.stringify(t);case"form":{let n=new URLSearchParams;for(let[s,a]of Object.entries(t))Si(n,s,a);return n}case"raw":return t}}function Hn(e){return!!(e==null||typeof e=="string"||e instanceof URLSearchParams||e instanceof Blob||e instanceof FormData||e instanceof ArrayBuffer||ArrayBuffer.isView(e))}function vw(e){if(!e)return"";let t=new URLSearchParams;for(let[s,a]of Object.entries(e))Si(t,s,a);let n=t.toString();return n===""?"":`?${n}`}function Si(e,t,n){if(n!=null){if(Array.isArray(n)){for(let s of n)Si(e,t,s);return}if(n instanceof Date){e.append(t,n.toISOString());return}e.append(t,typeof n=="object"?JSON.stringify(n):String(n))}}async function _w(e,t){let n=await kw(t,mw).catch(()=>""),s;try{s=n===""?void 0:JSON.parse(n)}catch{s=n}let a=Sw(s)?s:void 0,r=Number(t.headers.get("retry-after")),i=t.status>=300&&t.status<400;return new C({op:e,status:t.status,code:a?.error??(i?"undeclared_redirect":`http_${t.status}`),message:a?.error_description??(i?`${e}: the server redirected (${t.status}); the request was not followed, its credentials stay here`:`${e}: ${t.status} ${t.statusText}`.trim()),requestId:a?.request_id??t.headers.get("x-request-id")??void 0,retryAfter:a?.retry_after??(Number.isFinite(r)&&r>0?r:void 0),body:s})}async function kw(e,t){if(!e.body)return"";let n=e.body.getReader(),s=[],a=0;for(;a<t;){let{done:o,value:l}=await n.read();if(o)break;s.push(l),a+=l.byteLength}await n.cancel().catch(()=>{});let r=new Uint8Array(Math.min(a,t)),i=0;for(let o of s){let l=Math.min(o.byteLength,r.byteLength-i);if(r.set(o.subarray(0,l),i),i+=l,i>=r.byteLength)break}return new TextDecoder().decode(r)}function Sw(e){return typeof e=="object"&&e!==null&&typeof e.error=="string"}var La={project:{type:"string",short:"P",value:"project",description:"the project (name or id; org/project when the name is in two organizations)"},env:{type:"string",short:"E",value:"env",description:"the project's environment (slug or ref)"},json:{type:"boolean",description:"print the answer as JSON"},"non-interactive":{type:"boolean",description:"never ask: refuse what would need a question"},yes:{type:"boolean",short:"y",description:"confirm a change to a production environment without asking"},debug:{type:"boolean",description:"print each request the command sends"},help:{type:"boolean",short:"h",description:"show help"}};var y=class extends Error{exitCode;hint;code;reported;constructor(t,n,s={}){super(n),this.name="CliError",this.code=t,this.hint=s.hint,this.exitCode=s.exitCode??1,this.reported=s.reported??!1}};function m(e,t){return new y("usage",e,{hint:t,exitCode:2})}var Da=class{io;json;constructor(t,n){this.io=t,this.json=n}result(t,n){this.json?this.io.stdout(`${JSON.stringify(t,null,2)}
|
|
4
4
|
`):n()}line(t=""){this.io.stdout(`${q(t)}
|
|
5
5
|
`)}note(t){this.io.stderr(`${q(t)}
|
|
@@ -9,7 +9,7 @@ var nw=Object.defineProperty;var We=(e,t)=>{for(var n in t)nw(e,n,{get:t[n],enum
|
|
|
9
9
|
`);let i;try{i=await this.io.fetch(t,{method:n.method,headers:{...n.headers,...a},body:n.body,signal:n.signal,...s?{}:{duplex:"half"},redirect:"manual"})}catch(l){throw n.signal?.aborted?l:new y("network_error",`${new URL(t).origin} could not be reached: ${l instanceof Error?l.message:String(l)}`)}if(this.debug&&this.io.stderr(`< ${i.status} ${i.headers.get("x-request-id")??""}
|
|
10
10
|
`),i.status!==503||!s||r>=Dw||!Lw.has(await Ai(i)??""))return i;await i.body?.cancel();let o=Number(i.headers.get("retry-after")??"");await this.io.sleep((Number.isFinite(o)&&o>0?Math.min(o,30):1)*1e3)}}async exchange(t,n,s){let a=await this.token(),r=await this.send(t,n,s,a.value);if(r.status===401&&a.renewable){let i=await this.renew(await this.currentSession());r=await this.send(t,n,s,i.access_token)}return{res:r,renewable:a.renewable}}async answered(t,n,s,a){if(s.status===204)return;let r=await s.text(),i=r===""?void 0:xw(r);if(s.ok)return i;let o=i??{};throw s.status===401?o.error==="token_not_for_machines"?Pd(new URL(this.cloud.studioApi).host):a?Yn():this.tokenRefused("Studio"):new C({op:`${t} ${n}`,status:s.status,code:o.error??`http_${s.status}`,message:o.error_description??`Studio answered ${s.status}`,requestId:o.request_id??s.headers.get("x-request-id")??void 0,body:i})}tokenRefused(t){return(this.io.env.PALBASE_TOKEN??"").startsWith("pbe_")?new y("token_refused",`${t} refused PALBASE_TOKEN, an environment's token: it ended (a few hours at most), it was revoked, or what minted it no longer holds`,{hint:"mint another: palbase env:tokens:create --scope <scope> (an admin), or with the environment's secret key (Studio's POST /environments/{ref}/tokens)"}):(this.io.env.PALBASE_TOKEN??"").startsWith("pbu_")?new y("token_refused",`${t} refused PALBASE_TOKEN, an account token: it was revoked, it ended, or, in this organization, the one who made it left it`,{hint:"its maker makes a new one: palbase account:tokens:create <name>"}):new y("token_refused",`${t} refused PALBASE_TOKEN: it was revoked, it ended, or the one who made it no longer holds its role`,{hint:"an admin makes a new one: palbase tokens:create <name>"})}async token(){let t=this.io.env.PALBASE_TOKEN;return t&&t!==""?{value:t,renewable:!1}:{value:(await this.currentSession()).access_token,renewable:!0}}async renew(t){let n=await _d(this.io,this.cloud,t);return await this.state.writeSession(n),this.session=n,n}async send(t,n,s,a){let r=`${this.cloud.studioApi}${Iw}${n}`;this.debug&&this.io.stderr(`> ${t} ${r}
|
|
11
11
|
`);try{let i=await this.io.fetch(r,{method:t,headers:{...a===void 0?{}:{authorization:`Bearer ${a}`},accept:"application/json",...s===void 0?{}:{"content-type":"application/json"}},body:s===void 0?void 0:JSON.stringify(s),redirect:"manual"});return this.debug&&this.io.stderr(`< ${i.status} ${i.headers.get("x-request-id")??""}
|
|
12
|
-
`),i}catch(i){throw new y("network_error",`Studio (${this.cloud.studioApi}) could not be reached: ${i instanceof Error?i.message:String(i)}`)}}};function Pd(e){return new y("token_not_for_machines",`the platform's edge refused PALBASE_TOKEN at ${e}, taking it for a machine's address (token_not_for_machines): the token itself was not refused`,{hint:"nothing is wrong with the token: run this again in a few minutes, and tell Palbase if it lasts"})}async function Ai(e){let t=await e.clone().json().catch(()=>null),n=t&&typeof t=="object"?t.error:void 0;return typeof n=="string"&&/^(console|token|edge)_/.test(n)?n:void 0}function xw(e){try{return JSON.parse(e)}catch{return e}}var Ad={version:"0.40.
|
|
12
|
+
`),i}catch(i){throw new y("network_error",`Studio (${this.cloud.studioApi}) could not be reached: ${i instanceof Error?i.message:String(i)}`)}}};function Pd(e){return new y("token_not_for_machines",`the platform's edge refused PALBASE_TOKEN at ${e}, taking it for a machine's address (token_not_for_machines): the token itself was not refused`,{hint:"nothing is wrong with the token: run this again in a few minutes, and tell Palbase if it lasts"})}async function Ai(e){let t=await e.clone().json().catch(()=>null),n=t&&typeof t=="object"?t.error:void 0;return typeof n=="string"&&/^(console|token|edge)_/.test(n)?n:void 0}function xw(e){try{return JSON.parse(e)}catch{return e}}var Ad={version:"0.40.1"};var Ga=Ad.version;async function jd(e,t,n){let s=new Map(n.map(i=>[i.name,i])),a=e.includes("--json"),r=e.includes("--debug");try{let{name:i,at:o}=Kw(e);if(i===void 0)return e.includes("--version")?(t.stdout(`palbase ${Ga}
|
|
13
13
|
`),0):(t.stdout(Cd(n)),e.length===0||e.includes("--help")||e.includes("-h")?0:2);if(i==="help"){let S=e[o+1],P=S===void 0?void 0:s.get(S);if(S!==void 0&&!P)throw Rd(S,n);return t.stdout(P?Ed(P):Cd(n)),0}let l=s.get(i);if(!l)throw Rd(i,n);let p=[...e.slice(0,o),...e.slice(o+1)];if(p.includes("--help")||p.includes("-h"))return t.stdout(Ed(l)),0;let{flags:d,positionals:u}=Gw(l,p),g=l.args??{min:0,max:0};if(u.length<g.min)throw m(`${l.name} needs ${g.min===1?"an argument":`${g.min} arguments`}`,Ti(l));if(u.length>g.max)throw m(`${l.name} takes ${g.max===0?"no argument":`at most ${g.max}`}: ${JSON.stringify(u.slice(g.max))} is left over`,Ti(l));let b={project:d.project,env:d.env,json:d.json===!0,nonInteractive:d["non-interactive"]===!0,yes:d.yes===!0,debug:d.debug===!0},w=gd(t.env),_=new xa(bd(t.configDir,w)),$={io:t,state:_,out:new Da(t,b.json),global:b,args:u,flags:d,interactive:t.interactive&&!b.nonInteractive,cloud:w,studio:new Ka(t,w,_,b.debug)};return await l.run($)??0}catch(i){return Uw(i,t,a,r)}}function Kw(e){let t=new Set;for(let[n,s]of Object.entries(La))s.type==="string"&&(t.add(`--${n}`),s.short&&t.add(`-${s.short}`));for(let n=0;n<e.length;n++){let s=e[n];if(s==="--")return{at:-1};if(t.has(s)){n++;continue}if(!s.startsWith("-"))return{name:s,at:n}}return{at:-1}}function Gw(e,t){let n={...La,...e.flags??{}},s={};for(let[a,r]of Object.entries(n))s[a]={type:r.type,...r.short?{short:r.short}:{},...r.multiple?{multiple:!0}:{}};try{let{values:a,positionals:r}=qw({args:t,options:s,strict:!0,allowPositionals:!0});return{flags:a,positionals:r}}catch(a){throw m(a instanceof Error?a.message.replace(/^.*?: /,""):String(a),`palbase help ${e.name}`)}}function Uw(e,t,n,s){let a="error",r,i,o=1,l;return e instanceof y?(a=e.code,r=e.message,i=e.hint,o=e.exitCode):e instanceof C?(a=e.code,r=e.message,l=e.requestId,e.code==="token_out_of_scope"&&t.env.PALBASE_TOKEN&&(r=`${e.op} refused PALBASE_TOKEN: ${e.message}`,i="this is the token's reach, fixed when it was minted: a person's yes does not change it \u2014 mint a token whose scopes name this call, or have a person run it signed in")):r=e instanceof Error?e.message:String(e),n&&e instanceof y&&e.reported||(n&&t.stdout(`${JSON.stringify({error:a,message:r,...i?{hint:i}:{},...l?{request_id:l}:{}},null,2)}
|
|
14
14
|
`),t.stderr(`error: ${q(r)}${l?` (request ${q(l)})`:""}
|
|
15
15
|
`),i&&t.stderr(` ${q(i)}
|
|
@@ -37,7 +37,7 @@ ${" ".repeat(15)}`)}${t}`;case"none":return`none: its app serves none${t}`}retur
|
|
|
37
37
|
`))e.out.line(i)})}},Dc={name:"machines:logs",summary:"a machine's log: the last hour, or since a time; -f follows what it writes next",usage:"[name] [-f] [--since <time>] [--lines <n>] [--search <text>]",details:["Besides the machine's own lines: its migration runs' (migration), and what a build printed while its tests ran against it (test) \u2014 the error it answered a test with, and that request's id: palbase machines:logs <name> --search <request_id>."],args:{min:0,max:1},flags:{follow:{type:"boolean",short:"f",description:"keep printing what the machine writes"},since:{type:"string",value:"time",description:"from when: an RFC 3339 time, or 10m, 2h, 1d ago"},lines:{type:"string",value:"n",description:"how many lines at most (default 200)"},search:{type:"string",value:"text",description:"only the lines that hold this text"}},scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.args[0]),s=Sn(e.flags.lines,"lines")??200,a=e.flags.since?J0(e.flags.since,e.io.now()):void 0,r=e.flags.search,i=new Set,o=async p=>{let d=new URLSearchParams({lines:String(s),order:p,...a?{since:a}:{},...r?{q:r}:{}}),u=await G(e,t,"GET",`/machines/${ee(n)}/logs?${d}`),g=u!==null&&typeof u=="object"?[u]:String(u??"").split(`
|
|
38
38
|
`).filter(b=>b.trim()!=="").map(b=>JSON.parse(b));return p==="desc"?g.reverse():g},l=p=>{for(let d of p)i.has(d.id)||(i.add(d.id),e.global.json?e.io.stdout(`${JSON.stringify(d)}
|
|
39
39
|
`):e.out.line(`${d.ts} ${d.pod?`[${d.pod.slice(-5)}] `:""}${d.source&&d.source!=="machine"?`(${d.source}) `:""}${d.line}`));p.length>0&&(a=p[p.length-1].ts)};for(l(await o("desc"));e.flags.follow===!0;)await e.io.sleep(2e3),l(await o("asc"))}},xc={name:"machines:deploy",summary:"build the head of a machine's branch now and roll it out, as a push does",usage:"[name] [--wait]",details:["A push to the branch does this by itself; this is for the one GitHub did not deliver, or a build again.","The commit is the branch's head on GitHub: push first."],args:{min:0,max:1},flags:{wait:{type:"boolean",description:"follow the build until the machine serves it"}},scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.args[0]);await v(e,t,`build and roll out ${n} from the head of its branch`);let s=await G(e,t,"POST",`/machines/${ee(n)}/builds`);e.flags.wait===!0&&(s=await B0(e,t,n)),e.out.result(s,()=>e.out.line(e.flags.wait===!0?`${s.name} serves version ${s.running_version}${s.build?` (${s.build.commit.slice(0,7)})`:""}.`:`${s.name}: a build of ${s.source.branch}'s head is on its way (palbase machines:builds).`))}};async function B0(e,t,n){let s="";for(let a=0;a<900;a++){let r=await G(e,t,"GET",`/machines/${ee(n)}`);if(!r.build_requested){let{builds:i}=await G(e,t,"GET",`/machines/${ee(n)}/builds?limit=1`),o=i[0];if(!o)throw new y("build_failed",`${n} has no build${r.state_reason?`: ${r.state_reason}`:""}`,{hint:`palbase machines:get ${n}`});let l=`build ${o.id.slice(0,8)} (${o.commit.slice(0,7)}): ${o.state}${o.running?", serving":""}`;if(l!==s&&e.out.note(` ${l}`),s=l,o.state==="failed"||o.state==="cancelled"||o.state==="skipped")throw new y("build_failed",`the build ${o.state}${o.reason?`: ${o.reason}`:""}`,{hint:`palbase machines:builds:log ${o.id} --machine ${n}`});if(o.state==="succeeded"&&r.state==="failed")throw new y("build_failed",`${n}: ${r.state_reason??"failed"}`,{hint:`palbase machines:get ${n}`});if(o.running&&r.running_version===r.version)return r}await e.io.sleep(2e3)}throw new y("timeout",`${n} was not serving the build after 30 minutes`,{hint:`palbase machines:builds ${n}`})}var Nc={name:"machines:rollback",summary:"serve an earlier build again (the one before the serving one, or a named one)",usage:"[name] [--to <build>]",args:{min:0,max:1},flags:{to:{type:"string",value:"build",description:"the build to go back to (default: the last one that served before)"}},scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.args[0]),s=e.flags.to;if(!s){let{builds:r}=await G(e,t,"GET",`/machines/${ee(n)}/builds?limit=50`),i=r.findIndex(l=>l.running),o=r.slice(i+1).find(l=>l.state==="succeeded");if(i<0||!o)throw new y("no_earlier_build",`${n} has no earlier build that succeeded`,{hint:"name one with --to (palbase machines:builds lists them)"});s=o.id}await v(e,t,`roll ${n} back to build ${s}`);let a=await G(e,t,"POST",`/machines/${ee(n)}/rollback`,{build:s});e.out.result(a,()=>e.out.line(`${a.name} rolls back to build ${s} (version ${a.version}).`))}},Kt={machine:{type:"string",value:"name",description:"the machine (default: the only one, or this directory's)"}},So=e=>e.replace(/[\u0000-\u001f\u007f-\u009f]/g," "),cr=(e,t,n="")=>`/machines/${ee(e)}/crons/${x("the cron's name",t)}${n}`,$o=(e,t,n="")=>`/machines/${ee(e)}/tasks/${x("the task's id",t)}${n}`,qc={name:"machines:crons",summary:"the crons a machine runs on a schedule (its app's @Cron classes): when each is next due, on which zone's clocks, and how its last run went",usage:"[name]",args:{min:0,max:1},scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.args[0]),s=await G(e,t,"GET",`/machines/${ee(n)}/crons`),a=s.crons??[];e.out.result(s,()=>e.out.table(a,[{header:"cron",value:r=>r.name},{header:"schedule",value:r=>r.schedule},{header:"zone",value:r=>r.time_zone},{header:"next",value:r=>r.paused?"paused":r.next_at??""},{header:"last run",value:r=>r.last_run?`${r.last_run.state} ${r.last_run.started_at??""}${r.last_run.manual?" (run now)":""}`:""}]))}},Kc={name:"machines:crons:runs",summary:"a cron's runs, newest first",usage:"<cron> [--machine <name>]",args:{min:1,max:1},flags:Kt,scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.flags.machine),s=await G(e,t,"GET",cr(n,e.args[0],"/runs")),a=s.runs??[];e.out.result(s,()=>e.out.table(a,[{header:"run",value:r=>r.id},{header:"state",value:r=>r.manual?`${r.state} (run now)`:r.state},{header:"attempt",value:r=>String(r.attempt)},{header:"started",value:r=>r.started_at??""},{header:"finished",value:r=>r.finished_at??""},{header:"reason",value:r=>So(r.reason??"")}]))}},Gc={name:"machines:crons:run",summary:"run a cron now, apart from its schedule \u2014 the machine woken for it if asleep",usage:"<cron> [--machine <name>]",args:{min:1,max:1},flags:Kt,scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.flags.machine);await v(e,t,`run ${e.args[0]} of ${n} now`);let s=await G(e,t,"POST",cr(n,e.args[0],"/run"));e.out.result(s,()=>e.out.line(`${e.args[0]} runs now: run ${s.id}, ${s.state} for its first attempt (palbase machines:crons:runs ${e.args[0]} shows how it went).`))}},Uc={name:"machines:crons:pause",summary:"pause a cron: it is not run until resumed, whatever version of the machine comes up meanwhile",usage:"<cron> [--machine <name>]",args:{min:1,max:1},flags:Kt,scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.flags.machine);await v(e,t,`pause ${e.args[0]} of ${n}`);let s=await G(e,t,"POST",cr(n,e.args[0],"/pause"));e.out.result(s,()=>e.out.line(`${s.name} is paused: it is not run until palbase machines:crons:resume ${s.name}.`))}},zc={name:"machines:crons:resume",summary:"resume a paused cron: it is due at its next time from now \u2014 what it missed is not made up",usage:"<cron> [--machine <name>]",args:{min:1,max:1},flags:Kt,scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.flags.machine);await v(e,t,`resume ${e.args[0]} of ${n}`);let s=await G(e,t,"POST",cr(n,e.args[0],"/resume"));e.out.result(s,()=>e.out.line(`${s.name} is resumed: next due ${s.next_at??""} (${s.time_zone}).`))}},dc=["waiting","running","succeeded","failed","cancelled"],Bc={name:"machines:tasks",summary:"the tasks on a machine's queue (its app's @Task classes), newest first: of one state, one type, or all",usage:"[--machine <name>] [--state waiting|running|succeeded|failed|cancelled] [--task <type>] [--limit <n>]",args:{min:0,max:0},flags:{...Kt,state:{type:"string",value:"state",description:"only tasks in this state"},task:{type:"string",value:"type",description:"only tasks of this type"},limit:{type:"string",value:"n",description:"the most listed (50; 100 at most)"}},scope:"environment",async run(e){let t=e.flags.state;if(t!==void 0&&!dc.includes(t))throw m(`--state ${t} is not one: ${dc.join(", ")}`);let n=await f(e),s=await ue(e,n,e.flags.machine),a=new URLSearchParams;t&&a.set("state",t),e.flags.task&&a.set("task",e.flags.task),e.flags.limit&&a.set("limit",e.flags.limit);let r=await G(e,n,"GET",`/machines/${ee(s)}/tasks${a.size>0?`?${a}`:""}`),i=r.tasks??[];e.out.result(r,()=>e.out.table(i,[{header:"task",value:o=>o.id},{header:"type",value:o=>o.task},{header:"state",value:o=>o.state},{header:"attempt",value:o=>`${o.attempt}/${o.retries+1}`},{header:"due",value:o=>o.state==="waiting"?o.run_at:o.finished_at??""},{header:"reason",value:o=>So(o.reason??"")}]))}},Fc={name:"machines:tasks:types",summary:"the task types a machine's running version declares, and what each one's queue holds",usage:"[name]",args:{min:0,max:1},scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.args[0]),s=await G(e,t,"GET",`/machines/${ee(n)}/task-types`),a=s.types??[];e.out.result(s,()=>e.out.table(a,[{header:"type",value:r=>r.name},{header:"at once",value:r=>String(r.concurrency)},{header:"retries",value:r=>String(r.retries)},{header:"timeout",value:r=>`${r.timeout} s`},{header:"waiting",value:r=>String(r.waiting)},{header:"running",value:r=>String(r.running)},{header:"failed",value:r=>String(r.failed)}]))}},Jc={name:"machines:tasks:get",summary:"one task on a machine's queue, with what it carries",usage:"<task id> [--machine <name>]",args:{min:1,max:1},flags:Kt,scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.flags.machine),s=await G(e,t,"GET",$o(n,e.args[0]));e.out.result(s,()=>{e.out.line(`${s.id} (${s.task}): ${s.state}, attempt ${s.attempt} of ${s.retries+1}${s.reason?` \u2014 ${So(s.reason)}`:""}`),e.out.line(s.payload===void 0?"payload: not kept \u2014 the task is over":`payload: ${JSON.stringify(s.payload)}`)})}},Wc={name:"machines:tasks:enqueue",summary:"put a task on a machine's queue, as its app's Tasks.enqueue does",usage:"<type> [--payload '<json>'] [--delay <seconds>] [--key <key>] [--machine <name>]",args:{min:1,max:1},flags:{...Kt,payload:{type:"string",value:"json",description:"what it carries: a JSON value (64 KiB at most)"},delay:{type:"string",value:"seconds",description:"due this many seconds from now (30 days at most)"},key:{type:"string",value:"key",description:"put once under this key (held until a day after the task is over)"}},scope:"environment",async run(e){let t=null;if(e.flags.payload!==void 0)try{t=JSON.parse(e.flags.payload)}catch{throw m("--payload is a JSON value",`like --payload '{"orderId":"o-1"}'`)}let n=e.flags.delay===void 0?void 0:Number(e.flags.delay);if(n!==void 0&&(!Number.isInteger(n)||n<0))throw m("--delay is whole seconds");let s=await f(e),a=await ue(e,s,e.flags.machine);await v(e,s,`put a ${e.args[0]} task on ${a}'s queue`);let r=await G(e,s,"POST",`/machines/${ee(a)}/tasks`,{task:e.args[0],payload:t,...n!==void 0?{delay:n}:{},...e.flags.key?{key:e.flags.key}:{}});e.out.result(r,()=>e.out.line(`${r.task} ${r.id}: ${r.state}, due ${r.run_at}.`))}},Vc={name:"machines:tasks:retry",summary:"put a task that failed back on the queue, due now, with as many attempts again as its type gives",usage:"<task id> [--machine <name>]",args:{min:1,max:1},flags:Kt,scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.flags.machine);await v(e,t,`try task ${e.args[0]} of ${n} again`);let s=await G(e,t,"POST",$o(n,e.args[0],"/retry"));e.out.result(s,()=>e.out.line(`${s.task} ${s.id} is back on the queue: ${s.state}, due now.`))}},Hc={name:"machines:tasks:cancel",summary:"take a task that is waiting off the queue: it is not run, nor tried again",usage:"<task id> [--machine <name>]",args:{min:1,max:1},flags:Kt,scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.flags.machine);await v(e,t,`cancel task ${e.args[0]} of ${n}`);let s=await G(e,t,"POST",$o(n,e.args[0],"/cancel"));e.out.result(s,()=>e.out.line(`${s.task} ${s.id} is cancelled: it is not run.`))}},Yc={name:"machines:contract",summary:"the API a Palbase backend serves (its OpenAPI document), as the platform reads it from the machine",usage:"[name]",args:{min:0,max:1},scope:"environment",async run(e){let t=await f(e),n=await ue(e,t,e.args[0]),s=await G(e,t,"GET",`/machines/${ee(n)}/openapi`);e.io.stdout(`${JSON.stringify(s,null,2)}
|
|
40
|
-
`)}},Xc={name:"machines:delete",summary:"delete a machine: it stops serving, its address answers nothing",usage:"<name>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=e.args[0];if(!e.global.yes){if(!e.interactive)throw new y("confirmation_needed","deleting a machine is confirmed",{hint:"confirm with -y"});if((await e.io.prompt("Type the machine's name to delete it: ")).trim()!==n)throw new y("cancelled","nothing was deleted")}await G(e,t,"DELETE",`/machines/${ee(n)}`),e.out.result({deleted:n},()=>e.out.line(`Deleted the machine ${n}.`))}};async function F0(e,t,n){e.out.note(`Following ${n}'s first build\u2026`);let s="";for(let a=0;a<900;a++){let r=await G(e,t,"GET",`/machines/${ee(n)}`),i=`${r.state}${r.build?` (${r.build.commit.slice(0,7)})`:""}`;if(i!==s&&e.out.note(` ${i}${r.state_reason?` \u2014 ${r.state_reason}`:""}`),s=i,r.running_version>=1&&r.running_version===r.version&&(r.state==="awake"||r.state==="asleep"))return r;if(r.state==="failed")throw new y("build_failed",`${n}'s build failed${r.state_reason?`: ${r.state_reason}`:""}`,{hint:`palbase machines:builds:log --machine ${n}`});await e.io.sleep(2e3)}throw new y("timeout",`${n} was not serving after 30 minutes`,{hint:`palbase machines:get ${n}`})}function J0(e,t){let n=/^(\d+)([smhd])$/.exec(e);if(n)return new Date(t-Number(n[1])*{s:1e3,m:6e4,h:36e5,d:864e5}[n[2]]).toISOString();if(Number.isNaN(Date.parse(e)))throw m(`--since is a time (2026-10-08T12:00:00Z) or a duration ago (10m, 2h, 1d), not ${JSON.stringify(e)}`);return new Date(e).toISOString()}var Zc={name:"databases:list",summary:"the project's databases, each with the environment that keeps it: size, state, how full",details:["A project's databases are the project's: any machine of the project may be given one, whichever environment keeps it (machines:update --database)."],scope:"environment",async run(e){let t=await f(e),[{databases:n},s]=await Promise.all([G(e,t,"GET","/databases?limit=100"),Fs(e.studio,t)]);e.out.result({databases:n},()=>{n.length===0&&e.out.line("No database yet: palbase databases:create makes one."),e.out.table(n,[{header:"name",value:a=>a.name},{header:"env",value:a=>`${Zt(s,a.environment)}${a.moving_to?` \u2192 ${Zt(s,a.moving_to)}`:""}`},{header:"size",value:a=>a.size},{header:"state",value:a=>`${a.state}${a.deleted_at?" (deleted)":""}`},{header:"used",value:a=>a.used_bytes!==void 0?`${(a.used_bytes/1e6).toFixed(1)} / ${(a.quota_bytes/1e6).toFixed(0)} MB`:""},{header:"host",value:a=>a.host}])})}},Qc={name:"databases:get",summary:"a database in full, with how to connect to it",usage:"<name>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=await G(e,t,"GET",`/databases/${ee(e.args[0])}`),s=await e.state.databasePasswords(t.env_ref,n.name);e.out.result({...n,passwords_on_this_computer:{owner:s.owner!==void 0,app:s.app!==void 0}},()=>{e.out.line(`${n.name} \u2014 ${n.size}, ${n.state}${n.state_reason?` (${n.state_reason})`:""}`),e.out.line(` host ${n.host}:${n.port} (pooled ${n.pooled_port})`),n.former_hosts?.length&&e.out.line(` also ${n.former_hosts.join(", ")} (its hosts before it moved)`),n.moving_to?e.out.line(` moving to ${n.moving_to.name||n.moving_to.ref}, in a moment`):n.move_note&&e.out.line(` last move ${n.move_note}`),e.out.line(` allows ${n.allow.length===0?"anyone with the password":n.allow.join(", ")}`),e.out.line(` backups ${n.backup_days} days, any moment of them (databases:restore --to)`),n.used_bytes!==void 0&&e.out.line(` used ${(n.used_bytes/1e6).toFixed(1)} MB of ${(n.quota_bytes/1e6).toFixed(0)} MB`),n.awake_hours_limit&&e.out.line(` awake ${n.awake_hours??0} h this month; the environment's free databases ${n.environment_awake_hours??n.awake_hours??0} of ${n.awake_hours_limit} hours`),e.out.line(` passwords ${s.owner||s.app?`this computer holds ${[s.owner&&"owner",s.app&&"app"].filter(Boolean).join(" and ")}`:"none on this computer (databases:password:reset makes new ones)"}`)})}},eh={name:"databases:move",summary:"bind a database to another environment of the project: its data stays as it is, and it answers throughout",usage:"<name> --to <env>",details:["Nothing is copied: the database keeps its data where it is and answers throughout. In a moment it is the new environment's \u2014 what it is counted and paid for from then on, its host (<name>-<its ref>) \u2014 and the host it had keeps working, for good. The machines given it are rolled out to the new host.","Its migrations are the machines' of the environment that keeps it: the machines that run them move with it with --with-machines (each once the database has, its migrations theirs there); without it, such a move is refused, naming them.","It needs the admin role in both environments, and the new environment room for it as for a database opened there. The passwords this computer holds for it go with it."],args:{min:1,max:1},flags:{to:{type:"string",value:"env",description:"the environment of the project it moves to (its slug)"},"with-machines":{type:"boolean",description:"move the machines that run its migrations with it"}},scope:"environment",async run(e){let t=await f(e),n=e.flags.to;if(typeof n!="string"||n==="")throw m("--to names the environment of the project it moves to");let s=(await Se(e.studio)).filter(u=>u.project_id===t.project_id),a=s.find(u=>u.slug===n||u.ref===n);if(!a)throw new y("no_such_environment",`the project has no environment ${JSON.stringify(n)}`,{hint:`its environments: ${s.map(u=>u.slug).join(", ")}`});let r=e.args[0];await v(e,t,`move the database ${r}${e.flags["with-machines"]===!0?", and the machines that run its migrations,":""} out of this environment, into ${a.slug}`),await v(e,ge(a),`move the database ${r} into this environment: it is counted and paid for here`);let i=e.flags["with-machines"]===!0,o=await G(e,t,"POST",`/databases/${ee(r)}/move`,{environment:a.ref,...i?{with_machines:!0}:{}}),l=e.io.now()+6e4;for(;o.moving_to&&e.io.now()<l;)await e.io.sleep(2e3),o=await G(e,t,"GET",`/databases/${ee(r)}`);let p=o.environment?.ref===a.ref,d=await e.state.databasePasswords(t.env_ref,r);if(p&&(d.owner!==void 0||d.app!==void 0)&&(await e.state.keepDatabasePasswords(a.ref,r,d),await e.state.forgetDatabase(t.env_ref,r)),e.out.result(o,()=>{p?e.out.line(`${o.name} is ${a.slug}'s now; ${o.host} is its host, and the one it had keeps working (palbase databases:get ${o.name} -E ${a.slug}).`):o.moving_to?e.out.line(`${o.name} is still moving to ${a.slug} (palbase databases:get ${o.name} shows when it has).`):e.out.line(`${o.name} did not move${o.move_note?`: ${o.move_note}`:""}.`)}),!p&&!o.moving_to)throw new y("database_not_moved",`${o.name} did not move${o.move_note?`: ${o.move_note}`:""}`)}};function th(e){if(e!==void 0)return String(e).split(",").map(t=>t.trim()).filter(t=>t!=="")}var nh={name:"databases:create",summary:"open a database; its passwords are kept on this computer (the platform shows them once)",usage:"[name] [--size <size>] [--allow <ip/cidr,\u2026>] [--backup-days <n>] [--show-passwords]",args:{min:0,max:1},flags:{size:{type:"string",value:"size",description:"its size (palbase databases:sizes lists what is open)"},allow:{type:"string",value:"list",description:"who may connect, as addresses or CIDRs, comma-separated (default: anyone with the password)"},"backup-days":{type:"string",value:"n",description:"how many days of backups to keep, 1 to 35"},"show-passwords":{type:"boolean",description:"print the passwords too (they are kept on this computer either way)"}},scope:"environment",async run(e){let t=await f(e),n={};e.args[0]&&(n.name=e.args[0]),e.flags.size!==void 0&&(n.size=e.flags.size);let s=th(e.flags.allow);s&&(n.allow=s);let a=Sn(e.flags["backup-days"],"backup-days");a!==void 0&&(n.backup_days=a),await v(e,t,`open a database ${e.args[0]??"main"}${n.size?` (${n.size})`:""}`);let r=await G(e,t,"POST","/databases",n);await e.state.keepDatabasePasswords(t.env_ref,r.name,{owner:r.credentials.owner.password,app:r.credentials.app.password});let i=e.flags["show-passwords"]===!0,{credentials:o,...l}=r;e.out.result(i?r:{...l,credentials:{owner:ko(o.owner),app:ko(o.app)}},()=>{e.out.line(`Opened the database ${r.name} (${r.size}) at ${r.host}.`),e.out.line("Its passwords are kept on this computer: palbase db:url and db:shell use them."),i&&(e.out.line(`owner: ${r.credentials.owner.password??""}`),e.out.line(`app: ${r.credentials.app.password??""}`))})}},_o="[YOUR-PASSWORD]";function ko(e){let{password:t,...n}=e,s={};for(let[a,r]of Object.entries(n))s[a]=typeof r=="string"?W0(r,t):r;return s}function W0(e,t){let n=e.replace(/(postgres(?:ql)?:\/\/[^:/@\s'"]+:)[^@\s'"]*(@)/gi,`$1${_o}$2`).replace(/([?&;]password=)[^&;\s'"]*/gi,`$1${_o}`);if(t)for(let s of new Set([t,encodeURIComponent(t),new URLSearchParams({p:t}).toString().slice(2)]))n=n.split(s).join(_o);return n}var sh={name:"databases:update",summary:"change a database's size, who may connect to it, or how long its backups are kept",usage:'<name> [--size <size>] [--allow <list>|""] [--backup-days <n>] [--wait]',details:["A new size is taken at once and the database moves to it serving on (palbase databases:sizes lists them): its sessions on the direct port end once, when its primary moves, and new logins wait a few seconds then."],args:{min:1,max:1},flags:{size:{type:"string",value:"size",description:"the size to move it to"},allow:{type:"string",value:"list",description:'who may connect (comma-separated addresses or CIDRs); "" for anyone with the password'},"backup-days":{type:"string",value:"n",description:"how many days of backups to keep, 1 to 35"},wait:{type:"boolean",description:"with --size: follow the move until it serves at the new size"}},scope:"environment",async run(e){let t=await f(e),n=e.args[0],s={},a=e.flags.size;if(a!==void 0){if(a==="")throw m("--size is one of palbase databases:sizes");s.size=a}let r=th(e.flags.allow);r&&(s.allow=r);let i=Sn(e.flags["backup-days"],"backup-days");if(i!==void 0&&(s.backup_days=i),Object.keys(s).length===0)throw m("nothing to change: --size, --allow or --backup-days");await v(e,t,`change the database ${n}: ${Object.keys(s).join(", ")}`);let o=await G(e,t,"PATCH",`/databases/${ee(n)}`,s);a!==void 0&&e.flags.wait===!0&&(o=await V0(e,t,n,a)),e.out.result(o,()=>{a!==void 0&&e.out.line(o.state==="resizing"?`${o.name} is moving to ${a}, serving on: its sessions on the direct port end once, when its primary moves (palbase databases:get ${o.name} follows it).`:`${o.name} serves at ${o.size} (${o.state}).`),(r||i!==void 0)&&e.out.line(`${o.name}: allows ${o.allow.length===0?"anyone with the password":o.allow.join(", ")}; backups ${o.backup_days} days.`)})}};async function V0(e,t,n,s){e.out.note(`Following ${n}'s move to ${s}\u2026`);let a="";for(let r=0;r<900;r++){let i=await G(e,t,"GET",`/databases/${ee(n)}`),o=`${i.state}${i.state_reason?` \u2014 ${i.state_reason}`:""}`;if(o!==a&&e.out.note(` ${o}`),a=o,i.size===s&&i.state!=="resizing"&&i.state!=="starting"){if(i.state==="failed")throw new y("resize_failed",`${n} did not come up at ${s}${i.state_reason?`: ${i.state_reason}`:""}`,{hint:`palbase databases:get ${n}`});return i}if(i.size!==s)throw new y("resize_changed",`${n} is asked for ${i.size} now, not ${s}: another change came`,{hint:`palbase databases:get ${n}`});await e.io.sleep(2e3)}throw new y("timeout",`${n} was still moving to ${s} after 30 minutes; it serves on meanwhile`,{hint:`palbase databases:get ${n}`})}var ah={name:"databases:password:reset",summary:"make a database role's password new; it is kept on this computer, the old one stops working",usage:"<name> --role owner|app [--show-password]",details:["The app role's new password reaches the machines given the database as they roll out with it; they keep serving."],args:{min:1,max:1},flags:{role:{type:"string",value:"role",description:"owner (migrations, the console) or app (what the machines use)"},"show-password":{type:"boolean",description:"print it too"}},scope:"environment",async run(e){let t=e.flags.role;if(t!=="owner"&&t!=="app")throw m("--role is owner or app");let n=await f(e);await v(e,n,`make the ${t} password of ${e.args[0]} new`);let a=(await G(e,n,"POST",`/databases/${ee(e.args[0])}/password`,{role:t}))[t];if(!a?.password)throw new y("no_password","the platform answered no password");await e.state.keepDatabasePasswords(n.env_ref,e.args[0],{[t]:a.password}),await H0(e,n,e.args[0]),e.out.result(e.flags["show-password"]===!0?a:ko(a),()=>{e.out.line(`The ${t} password of ${e.args[0]} is new and kept on this computer.`),e.flags["show-password"]===!0&&e.out.line(a.password??"")})}};async function H0(e,t,n){for(let s=0;s<60;s++){if((await G(e,t,"GET",`/databases/${ee(n)}`)).password_changing!==!0)return;s===0&&e.out.note(`Waiting for ${n} to take the new password\u2026`),await e.io.sleep(1e3)}throw new y("timeout",`${n} had not taken the new password after a minute; it is kept here and works once it has`,{hint:`palbase databases:get ${n}`})}var rh={name:"databases:restore",summary:"bring a database back to a moment of its backups",usage:"<name> --to <time>",args:{min:1,max:1},flags:{to:{type:"string",value:"time",description:"the moment, as an RFC 3339 time (2026-10-08T12:00:00Z)"}},scope:"environment",async run(e){let t=e.flags.to;if(!t||Number.isNaN(Date.parse(t)))throw m("--to is the moment to go back to, as an RFC 3339 time");let n=await f(e);await v(e,n,`bring ${e.args[0]} back to ${t} (what was written since goes)`);let s=await G(e,n,"POST",`/databases/${ee(e.args[0])}/restore`,{to:new Date(t).toISOString()});e.out.result(s,()=>e.out.line(`${s.name} is being brought back to ${t} \u2014 ${s.state}.`))}};function Po(e,t,n,s,a,r,i=!1){return{name:e,summary:t,usage:"<name>",args:{min:1,max:1},scope:"environment",async run(o){let l=await f(o),p=o.args[0];if(n==="DELETE"||s==="/purge"){if(!o.global.yes){if(!o.interactive)throw new y("confirmation_needed",`${a(p)} is confirmed`,{hint:"confirm with -y"});if((await o.io.prompt("Type the database's name to go ahead: ")).trim()!==p)throw new y("cancelled","nothing was changed")}}else await v(o,l,a(p));await G(o,l,n,`/databases/${ee(p)}${s}`),i&&await o.state.forgetDatabase(l.env_ref,p),o.out.result({database:p,done:e},()=>o.out.line(r(p)))}}}var ih=Po("databases:delete","delete a database (the platform keeps it 7 days to take back)","DELETE","",e=>`deleting the database ${e}`,e=>`Deleted ${e}; databases:undelete takes it back within 7 days.`),oh=Po("databases:undelete","take back a database deleted in the last 7 days","POST","/undelete",e=>`take back the database ${e}`,e=>`${e} is back.`),lh=Po("databases:purge","delete a deleted database for good, now","POST","/purge",e=>`purging the database ${e} for good`,e=>`${e} is purged.`,!0),dh={name:"github:connect",summary:"link the environment to GitHub: Palbase Cloud's GitHub app on the account whose repositories machines are built from",usage:"[--device | --github-token-stdin | --from <env> [--repo <owner/name>\u2026]] [--installation <account|id>] [--all-envs]",details:["Opens GitHub; back from it, the page shows the account and you link it by writing the environment's ref.","--from <env>: the installation another environment of the organization linked (its slug in this project, or its ref), linked here too with no GitHub sign-in \u2014 a program that opens a project for each product (an admin, or an admin access token). The environment reaches what the GitHub user who linked it there can push to.","--repo <owner/name> (given again for more): with --from, the environment reaches those repositories of the installation alone, never one it reaches later \u2014 a product's environment builds nothing of another product's.","--device: no browser here \u2014 GitHub's device flow (a code you enter at github.com/login/device on any device); the GitHub token stays in memory.","--all-envs links every environment of the project the same way."],flags:{device:{type:"boolean",description:"GitHub's device flow, for a computer with no browser (a server, an agent)"},"github-token-stdin":{type:"boolean",description:"a GitHub user's token from stdin (CI): the installations it reaches, no browser and no code"},installation:{type:"string",value:"account|id",description:"which installation to link, when the GitHub user reaches more than one (--device)"},"all-envs":{type:"boolean",description:"link every environment of the project"},from:{type:"string",value:"env",description:"another environment of the organization whose linked installation this one shares (its slug in this project, or its ref): no GitHub sign-in"},repo:{type:"string",multiple:!0,value:"owner/name",description:"with --from: the repositories it reaches of the installation, and no other (given again for more)"}},scope:"environment",async run(e){let t=await f(e),n=e.flags["all-envs"]===!0?(await qt(e)).map(ge):[t],s=e.flags.repo;if(typeof e.flags.from=="string"){if(e.flags.device===!0||e.flags["github-token-stdin"]===!0)throw m("--from links what another environment linked: no GitHub sign-in goes with it");for(let p of s??[])if(!/^[A-Za-z0-9-]{1,39}\/[A-Za-z0-9._-]{1,100}$/.test(p))throw m(`--repo names a repository as owner/name: ${JSON.stringify(p)} is none`);await Y0(e,t,n,e.flags.from,s);return}if(s!==void 0)throw m("--repo goes with --from: it narrows what a shared installation reaches");let a=await G(e,t,"POST","/machines/github/connect"),r=e.flags["github-token-stdin"]===!0;if(!r&&e.flags.device!==!0&&e.interactive&&n.length===1){let p=await e.io.openBrowser(a.url);e.out.result({url:a.url,expires_at:a.expires_at,ref:t.env_ref},()=>e.out.line(p?`Opened GitHub. When it asks, link the environment ${t.env_ref}.`:`Open ${a.url} (good until ${a.expires_at}) and link the environment ${t.env_ref}.`));return}let i=r?await Ie(e,"a GitHub user's token"):await X0(e,a.client_id),o=await Z0(e,i),l=[];for(let p of n){await v(e,p,`link the GitHub account ${o.account}`);let d=await G(e,p,"POST","/machines/github/installations",{installation_id:o.id,user_token:i});l.push({ref:p.env_ref,account:d.account})}e.out.result({linked:l},()=>{for(let p of l)e.out.line(`Linked ${p.account} to ${p.ref}.`)})}};async function Y0(e,t,n,s,a){let r=await Se(e.studio),i=r.find(u=>u.ref===s)??r.find(u=>u.project_id===t.project_id&&u.slug===s);if(!i||i.organization_id!==t.org_id)throw new y("no_such_environment",`no environment ${JSON.stringify(s)} of ${t.org_name} besides this one`,{hint:"name it by its slug in this project, or by its ref (palbase projects:list)"});let o=(await G(e,ge(i),"GET","/machines/github/installations")).installations,l=e.flags.installation,p=l!==void 0?o.filter(u=>String(u.installation_id)===l||u.account.toLowerCase()===l.toLowerCase()):o;if(p.length===0)throw new y("no_installation",`${i.slug} (${i.ref}) links no GitHub account${l!==void 0?` ${JSON.stringify(l)}`:""}`,{hint:"link it there first: palbase github:connect -E <env>"});if(p.length>1)throw m("which installation? --installation <account|id>",`${i.slug} links ${p.map(u=>u.account).join(", ")}`);let d=[];for(let u of n){if(u.env_ref===i.ref)continue;await v(e,u,`link the GitHub account ${p[0].account}${a?` for ${a.join(", ")} alone`:""}, as ${i.slug} (${i.ref}) links it`);let g=await e.studio.post(`/environments/${x("the environment's ref",u.env_ref)}/github/installations`,{installation_id:p[0].installation_id,from:i.ref,...a?{repositories:a}:{}});d.push({ref:u.env_ref,account:g.account})}e.out.result({linked:d},()=>{for(let u of d)e.out.line(`Linked ${u.account} to ${u.ref}, as ${i.slug} (${i.ref}) links it.`)})}async function X0(e,t){let n=o=>({method:"POST",headers:{accept:"application/json","content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams(o)}),s=await e.io.fetch("https://github.com/login/device/code",n({client_id:t}));if(!s.ok)throw new y("github_unavailable",`GitHub did not start the sign-in: ${s.status}`);let a=await s.json();e.out.note(""),e.out.note(` Enter this code at ${a.verification_uri}: ${a.user_code}`),e.out.note("");let r=Math.max(1,a.interval??5),i=e.io.now()+(a.expires_in??900)*1e3;for(;e.io.now()<i;){await e.io.sleep(r*1e3);let o;try{o=await e.io.fetch("https://github.com/login/oauth/access_token",n({client_id:t,device_code:a.device_code,grant_type:"urn:ietf:params:oauth:grant-type:device_code"}))}catch{continue}if(o.status>=500||o.status===429)continue;let l=await o.json().catch(()=>({}));if(l.access_token)return l.access_token;if(l.error!=="authorization_pending"){if(l.error==="slow_down"){r+=5;continue}if(l.error==="expired_token")break;throw l.error==="access_denied"?new y("access_denied","the sign-in was denied on GitHub"):new y("github_denied",`GitHub's sign-in ended: ${l.error??o.status}`)}}throw new y("expired_code","GitHub's code expired before it was entered",{hint:"run the command again"})}async function Z0(e,t){let n=await e.io.fetch("https://api.github.com/user/installations?per_page=100",{headers:{authorization:`Bearer ${t}`,accept:"application/vnd.github+json","x-github-api-version":"2022-11-28"}});if(!n.ok)throw new y("github_unavailable",`GitHub did not list your installations: ${n.status}`);let s=(await n.json()).installations.map(r=>({id:r.id,account:r.account.login}));if(s.length===0)throw new y("no_installation","Palbase Cloud is installed on no GitHub account you reach",{hint:"install it (palbase github:connect, in a browser) or have the account's owner install it"});let a=e.flags.installation;if(a!==void 0){let r=s.find(i=>String(i.id)===a||i.account.toLowerCase()===a.toLowerCase());if(!r)throw new y("no_such_installation",`you reach no installation ${JSON.stringify(a)}: ${s.map(i=>i.account).join(", ")}`);return r}if(s.length===1)return s[0];if(!e.interactive)throw m("which installation? --installation <account>",`yours: ${s.map(r=>r.account).join(", ")}`);for(s.forEach((r,i)=>e.out.note(` ${i+1}) ${r.account}`));;){let r=Number((await e.io.prompt(`Which account? [1-${s.length}] `)).trim());if(Number.isInteger(r)&&r>=1&&r<=s.length)return s[r-1]}}var ph={name:"github:list",summary:"the GitHub accounts linked to the environment, and who linked each",scope:"environment",async run(e){let t=await f(e),{installations:n}=await G(e,t,"GET","/machines/github/installations");e.out.result({installations:n},()=>{n.length===0&&e.out.line("No GitHub account is linked: palbase github:connect links one."),e.out.table(n,[{header:"account",value:s=>s.account},{header:"installation",value:s=>String(s.installation_id)},{header:"linked by",value:s=>s.linked_by}])})}},uh={name:"github:repos",summary:"the repositories machines of the environment may be built from",scope:"environment",async run(e){let t=await f(e),{repositories:n}=await G(e,t,"GET","/machines/github/repositories");e.out.result({repositories:n},()=>e.out.table(n,[{header:"repository",value:s=>s.repository},{header:"default branch",value:s=>s.default_branch},{header:"",value:s=>s.private?"private":""}]))}},ch={name:"github:disconnect",summary:"unlink a GitHub account from the environment; its repositories are no longer built",usage:"<account|installation-id>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),{installations:n}=await G(e,t,"GET","/machines/github/installations"),s=n.find(a=>String(a.installation_id)===e.args[0]||a.account.toLowerCase()===e.args[0].toLowerCase());if(!s)throw new y("no_such_installation",`no linked GitHub account ${JSON.stringify(e.args[0])}`);await v(e,t,`unlink the GitHub account ${s.account}`),await G(e,t,"DELETE",`/machines/github/installations/${s.installation_id}`),e.out.result({unlinked:s},()=>e.out.line(`Unlinked ${s.account}.`))}},Q0=["static","server","backend"];function ek(e,t){let n=(e.split("/").filter(s=>s!=="").at(-1)??t.split("/").at(-1)??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^[^a-z]+/,"");for(let s="";s!==n;)s=n,n=n.slice(0,32).replace(/-+$/,"").replace(/-git(?=-|$)/,"git").replace(/-pr(\d+)$/,"pr$1");return n===""?"app":["github","network","sizes"].includes(n)?`${n}-app`:n}function tk(e,t,n){let s={static:"a static web app: a site",server:"a web app that runs on a server: a machine, and a site it serves",backend:"a backend: a machine"};if(e.out.line(`${t.repository}@${t.branch}${t.path?`/${t.path}`:""} (${t.commit.slice(0,7)}): ${t.framework_name||"no framework known"} \u2014 ${s[t.kind]}${n!==t.kind?`; made as ${n} (--as)`:""}`),e.out.line(` why ${t.why}`),n==="static"){let a=t.build??{};e.out.line(` install ${a.install_command||"found as it builds"}`),e.out.line(` build ${a.command||"found as it builds"}`),e.out.line(` output ${a.output_dir||"found as it builds"}`),t.node&&e.out.line(` node ${t.node}`)}else e.out.line(` dockerfile ${t.dockerfile?"its own":"none: its language is found as it builds"}`)}var hh={name:"hosting:import",summary:"import a GitHub repository's directory, as Vercel does: what it is found to be \u2014 a static web app, a web app that runs on a server, a backend \u2014 is made and built on every push",usage:"[--repo <owner/name>] [--branch <branch>] [--path <dir>] [--name <name>] [--as static|server|backend]",details:["Run in a checkout, it reads the repository from origin, the branch from HEAD and the directory from where you are; flags say otherwise.","What the directory is, is read on GitHub at the branch's head \u2014 its package.json, its framework's configuration, its lockfile, its Node.js version \u2014 and printed before anything is made; at a terminal you are asked to go ahead.","static: a site, built as found. server (Next.js, Nuxt, SvelteKit on a server): a machine, and a site of its name with no files whose server it is \u2014 its address on the hosting domain, and its CDN. backend: a machine. --as makes it the other way.","The environment needs its GitHub link first: palbase github:connect."],flags:{repo:{type:"string",value:"owner/name",description:"the GitHub repository (default: this checkout's origin)"},branch:{type:"string",value:"branch",description:"the branch whose pushes are built (default: the one checked out)"},path:{type:"string",value:"dir",description:"the directory, from the repository's root (default: this directory's place in it)"},name:{type:"string",value:"name",description:"the site's and the machine's name (default: the directory's, or the repository's at its root)"},as:{type:"string",value:"static|server|backend",description:"make it as this, whatever it is found to be"}},scope:"environment",async run(e){let t=e.flags.as;if(t!==void 0&&!Q0.includes(t))throw m("--as is static, server or backend");let n=await f(e),s=await Xn(e.io,e.io.cwd),a=e.flags.repo??s?.repository,r=e.flags.branch??s?.branch;if(!a)throw m("which repository? this directory's origin is not a GitHub repository","pass --repo owner/name");if(!r)throw m("which branch? none is checked out here","pass --branch <branch>");let i=e.flags.path??(e.flags.repo===void 0?s?.path??"":""),o=e.flags.name??ek(i,a);ee(o);let l;try{l=await G(e,n,"GET",`/machines/github/detect?${new URLSearchParams({repository:a,branch:r,...i!==""?{path:i}:{}})}`)}catch(_){if(!(_ instanceof C&&_.code==="project_not_detected"))throw _;if(t===void 0)throw new y(_.code,_.message,{hint:"say what it is with --as static|server|backend"});e.out.warn(`${_.message}: it is made as --as ${t} says`)}let p=t??l?.kind;l&&!e.global.json&&tk(e,l,p);for(let _ of l?.warnings??[])e.out.warn(_);let d={repository:a,branch:r,path:i},u=`${a}@${r}${i?`/${i}`:""}`,g=p==="static"?void 0:await vc(e,n),b={static:`the site ${o}, built from ${u}`,server:`the machine ${o} from ${u} (${g}), and the site ${o} it serves`,backend:`the machine ${o} from ${u} (${g})`}[p];await v(e,n,`make ${b}`,e.interactive?`This makes ${b}`:void 0);let w={kind:p,...l?{detection:l}:{}};if(p==="static"){let _=l?.build;w.site=await G(e,n,"POST","/sites",{name:o,source:d,..._?{build:{install_command:_.install_command,command:_.command,output_dir:_.output_dir,env:{}}}:{}})}else{let _=await G(e,n,"POST","/machines",_c(o,d,g));if(w.machine=_,p==="server")try{w.site=await G(e,n,"POST","/sites",{name:o,server:{machine:_.name,paths:[]}})}catch($){throw $ instanceof C?new y($.code,`made the machine ${_.name}, not its site: ${$.message}`,{hint:`palbase hosting:sites:create ${o} --server ${_.name} makes it`}):$}}e.out.result(w,()=>{let{machine:_,site:$}=w;_&&e.out.line(`Made the machine ${_.name} (${_.size}): ${_.url}`),$&&e.out.line(`Opened the site ${$.name}: ${$.url}${$.server?` \u2014 ${$.server.machine} answers it`:""}`),e.out.line(_?`Its first build is on its way: palbase machines:builds ${_.name} lists it, machines:builds:log <build> --machine ${_.name} prints its log.`:`Its first build is on its way: palbase hosting:builds --site ${$.name} lists it, hosting:builds <build> --site ${$.name} --wait follows it.`)})}};var Le=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)?e:void 0;function Gt(e,t,n){let s=Le(Le(Le(e)?.components)?.schemas)??{},a=(d,u)=>{let g=new Set,b=(_,$,S)=>{let P=Le(_);if(!P)return;if(typeof P.$ref=="string"){if(S.has(P.$ref)||!P.$ref.startsWith("#/components/schemas/"))return;b(s[P.$ref.slice(21)],$,new Set(S).add(P.$ref));return}if(P[Xa]===!0){g.add($);return}for(let A of["anyOf","oneOf","allOf"]){let D=P[A];if(Array.isArray(D))for(let z of D)b(z,$,S)}let R=Le(P.properties);if(R)for(let[A,D]of Object.entries(R))b(D,`${$}/${tr(A)}`,S);Le(P.items)&&b(P.items,`${$}/*`,S);for(let A of["additionalProperties","patternProperties","prefixItems"])if(P[A]!==void 0&&w(P[A],new Set(S)))throw new Error(`${n}: an encrypted field is a named property \u2014 not every key of a record, nor a tuple's place`)},w=(_,$)=>{if(Array.isArray(_))return _.some(P=>w(P,$));let S=Le(_);return S?S[Xa]===!0?!0:typeof S.$ref=="string"&&!$.has(S.$ref)&&S.$ref.startsWith("#/components/schemas/")?($.add(S.$ref),w(s[S.$ref.slice(21)],$)):Object.values(S).some(P=>typeof P=="object"&&P!==null&&w(P,$)):!1};return b(d,u,new Set),[...g]},r=d=>Le(Le(Le(d)?.["application/json"])?.schema),i=[];i.push(...a(r(Le(t.requestBody)?.content),"/body"));for(let d of Array.isArray(t.parameters)?t.parameters:[]){let u=Le(d);if(!u)continue;let g=a(u.schema,`/query/${tr(String(u.name))}`);if(g.length!==0){if(u.in!=="query")throw new Error(`${n}: ${String(u.in)} parameter ${String(u.name)} is marked encrypted \u2014 a body's or a query's field is`);if(g.some(b=>b.split("/").length!==3))throw new Error(`${n}: an encrypted query field is one of the query's own names`);i.push(...g)}}let o=Le(t.responses)??{},l=Object.keys(o).filter(d=>/^2\d\d$/.test(d)).sort((d,u)=>d==="200"?-1:u==="200"?1:d.localeCompare(u)),p=l.length>0?a(r(Le(o[l[0]])?.content),"/response"):[];if(i.length+p.length!==0){if(t["x-palbase-sse"]!==void 0&&p.length>0)throw new Error(`${n}: a streamed answer carries no encrypted field`);if(t["x-palbase-upload"]!==void 0)throw new Error(`${n}: an upload goes through storage, which seals nothing \u2014 it carries no encrypted field`);return{request:i,response:p}}}function mh(e){return JSON.stringify(e??null).includes(`"${Xa}":true`)}async function gh(e){let t=`${e.method.toUpperCase()} ${e.path}`,n=Le(Le(Le(Le(e.contract)?.paths)?.[e.path])?.[e.method.toLowerCase()]),s=n?Gt(e.contract,n,t):void 0;if(!s)return{body:e.body,query:e.query,open:async o=>o};let{key:a,header:r}=await Zd(e.key.kid,Pt(e.key.publicKey),Qa(e.projectId,e.method,e.path,e.params??[])),i=(o,l)=>o==null?o:es(o,l,s.request,(p,d)=>Xs(a,p,d));return{header:r,body:await i(e.body,"/body"),query:await i(e.query,"/query"),open:o=>o===void 0?Promise.resolve(o):es(o,"/response",s.response,async(l,p)=>{if(!Zs(p))throw new $e("unopenable",`${t}: ${l} came plain where the contract says it is encrypted`);return er(a,l,p)})}}var fh=["GET","POST","PUT","PATCH","DELETE"];function nk(e,t,n){let s=n.split("/").filter(Boolean);for(let[a,r]of Object.entries(e.paths??{})){let i=r?.[t.toLowerCase()];if(typeof i!="object"||i===null)continue;let o=a.split("/").filter(Boolean);if(o.length!==s.length)continue;let l=[],p=!0;for(let d=0;d<s.length&&p;d++)/^\{[^/{}]+\}$/.test(o[d])?l.push(decodeURIComponent(s[d])):p=o[d]===s[d];if(p)return{template:a,params:l,op:i}}}var yh={name:"machines:call",summary:"call a route of a machine's backend as its apps do: the fields its contract marks encrypted sealed, its answer's opened",usage:"<machine> <METHOD> <path> [--data <json>] [--token <access token>]",details:[`palbase machines:call api POST /patients --data '{"name":"Ada","tc":"10000000146"}'`,"The route is found in the machine's contract; a field it marks encrypted (palbase/backend's encrypted() and encrypt()) goes sealed to the project's key in use and comes back opened, as the app's generated client does.","--token is a user's access token, for a route that takes one; without it the call is nobody's."],args:{min:3,max:3},flags:{data:{type:"string",value:"json",description:"the request's body, as JSON"},token:{type:"string",value:"token",description:"a user's access token: the call is theirs"}},scope:"environment",async run(e){let t=await f(e),[n,s,a]=e.args,r=s.toUpperCase();if(!fh.includes(r))throw m(`${s} is not a method a route answers`,`one of ${fh.join(", ")}`);if(!a.startsWith("/"))throw m(`${a}: the route's path starts with /`,"palbase machines:call api GET /patients/p1");let i;if(e.flags.data!==void 0)try{i=JSON.parse(e.flags.data)}catch{throw m("--data is JSON",`--data '{"name":"x"}'`)}let o=x("the machine's name",n),{body:l}=await we(e.studio,t.env_ref,"GET",`/machines/${o}`),{body:p}=await we(e.studio,t.env_ref,"GET",`/machines/${o}/openapi`),d=new URL(l.url),u=new URL(a,d);if(u.origin!==d.origin||u.pathname.startsWith("//"))throw m(`${a} is not a path of ${n}`,"palbase machines:call api GET /patients/p1");let g=nk(p,r,u.pathname);if(!g)throw new y("route_not_found",`${n}'s contract has no ${r} ${u.pathname}`,{hint:`palbase machines:contract ${n} lists its routes`});let b=u.search===""?void 0:Object.fromEntries(u.searchParams),w,_="";if(Gt(p,g.op,`${r} ${g.template}`)){_=(await e.studio.get(`/environments/${t.env_ref}`)).platform_id;let{body:F}=await we(e.studio,t.env_ref,"GET","/secrets/transit"),I=F.keys.find(J=>J.id===F.current);if(!I)throw new y("no_transit_key","the project has no transit key in use: palbase transit:list says what there is");w={kid:I.id,publicKey:I.public_key}}let $=w?await gh({contract:p,method:r,path:g.template,params:g.params,body:i,query:b,projectId:_,key:w}):{body:i,query:b,header:void 0,open:async z=>z},S=new URL(d.origin);S.pathname=u.pathname;for(let[z,F]of Object.entries($.query??{}))S.searchParams.set(z,typeof F=="string"?F:JSON.stringify(F));let P={accept:"application/json"};$.body!==void 0&&(P["content-type"]="application/json"),e.flags.token&&(P.authorization=`Bearer ${e.flags.token}`),$.header&&(P[ut]=$.header);let R=await e.io.fetch(S.href,{method:r,headers:P,body:$.body===void 0?void 0:JSON.stringify($.body),redirect:"manual"}),A=await R.text(),D=A;if(A!=="")try{D=JSON.parse(A)}catch{}return R.ok&&typeof D=="object"&&(D=await $.open(D)),e.out.result({status:R.status,encrypted:w!==void 0,answer:D},()=>{e.out.line(`${R.status} ${r} ${u.pathname}${w?` (its encrypted fields sealed to ${w.kid}, the answer's opened)`:""}`),A!==""&&e.out.line(typeof D=="string"?D:JSON.stringify(D,null,2))}),R.ok?0:1}};import{createHash as xm}from"node:crypto";import{lstat as C$,mkdir as j$,mkdtemp as O$,readdir as Bo,readFile as ca,rm as Nm,rmdir as M$,writeFile as I$}from"node:fs/promises";import{tmpdir as L$}from"node:os";import{dirname as D$,join as rt,relative as Ir,resolve as Im,sep as Mr}from"node:path";function rk(e){return e.replace(/\s+/g," ").trim()}var nN=String.raw`
|
|
40
|
+
`)}},Xc={name:"machines:delete",summary:"delete a machine: it stops serving, its address answers nothing",usage:"<name>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=e.args[0];if(!e.global.yes){if(!e.interactive)throw new y("confirmation_needed","deleting a machine is confirmed",{hint:"confirm with -y"});if((await e.io.prompt("Type the machine's name to delete it: ")).trim()!==n)throw new y("cancelled","nothing was deleted")}await G(e,t,"DELETE",`/machines/${ee(n)}`),e.out.result({deleted:n},()=>e.out.line(`Deleted the machine ${n}.`))}};async function F0(e,t,n){e.out.note(`Following ${n}'s first build\u2026`);let s="";for(let a=0;a<900;a++){let r=await G(e,t,"GET",`/machines/${ee(n)}`),i=`${r.state}${r.build?` (${r.build.commit.slice(0,7)})`:""}`;if(i!==s&&e.out.note(` ${i}${r.state_reason?` \u2014 ${r.state_reason}`:""}`),s=i,r.running_version>=1&&r.running_version===r.version&&(r.state==="awake"||r.state==="asleep"))return r;if(r.state==="failed")throw new y("build_failed",`${n}'s build failed${r.state_reason?`: ${r.state_reason}`:""}`,{hint:`palbase machines:builds:log --machine ${n}`});await e.io.sleep(2e3)}throw new y("timeout",`${n} was not serving after 30 minutes`,{hint:`palbase machines:get ${n}`})}function J0(e,t){let n=/^(\d+)([smhd])$/.exec(e);if(n)return new Date(t-Number(n[1])*{s:1e3,m:6e4,h:36e5,d:864e5}[n[2]]).toISOString();if(Number.isNaN(Date.parse(e)))throw m(`--since is a time (2026-10-08T12:00:00Z) or a duration ago (10m, 2h, 1d), not ${JSON.stringify(e)}`);return new Date(e).toISOString()}var Zc={name:"databases:list",summary:"the project's databases, each with the environment that keeps it: size, state, how full",details:["A project's databases are the project's: any machine of the project may be given one, whichever environment keeps it (machines:update --database)."],scope:"environment",async run(e){let t=await f(e),[{databases:n},s]=await Promise.all([G(e,t,"GET","/databases?limit=100"),Fs(e.studio,t)]);e.out.result({databases:n},()=>{n.length===0&&e.out.line("No database yet: palbase databases:create makes one."),e.out.table(n,[{header:"name",value:a=>a.name},{header:"env",value:a=>`${Zt(s,a.environment)}${a.moving_to?` \u2192 ${Zt(s,a.moving_to)}`:""}`},{header:"size",value:a=>a.size},{header:"state",value:a=>`${a.state}${a.deleted_at?" (deleted)":""}`},{header:"used",value:a=>a.used_bytes!==void 0?`${(a.used_bytes/1e6).toFixed(1)} / ${(a.quota_bytes/1e6).toFixed(0)} MB`:""},{header:"host",value:a=>a.host}])})}},Qc={name:"databases:get",summary:"a database in full, with how to connect to it",usage:"<name>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=await G(e,t,"GET",`/databases/${ee(e.args[0])}`),s=await e.state.databasePasswords(t.env_ref,n.name);e.out.result({...n,passwords_on_this_computer:{owner:s.owner!==void 0,app:s.app!==void 0}},()=>{e.out.line(`${n.name} \u2014 ${n.size}, ${n.state}${n.state_reason?` (${n.state_reason})`:""}`),e.out.line(` host ${n.host}:${n.port} (pooled ${n.pooled_port})`),n.former_hosts?.length&&e.out.line(` also ${n.former_hosts.join(", ")} (its hosts before it moved)`),n.moving_to?e.out.line(` moving to ${n.moving_to.name||n.moving_to.ref}, in a moment`):n.move_note&&e.out.line(` last move ${n.move_note}`),e.out.line(` allows ${n.allow.length===0?"anyone with the password":n.allow.join(", ")}`),e.out.line(` backups ${n.backup_days} days, any moment of them (databases:restore --to)`),n.used_bytes!==void 0&&e.out.line(` used ${(n.used_bytes/1e6).toFixed(1)} MB of ${(n.quota_bytes/1e6).toFixed(0)} MB`),n.awake_hours_limit&&e.out.line(` awake ${n.awake_hours??0} h this month; the environment's free databases ${n.environment_awake_hours??n.awake_hours??0} of ${n.awake_hours_limit} hours`),e.out.line(` passwords ${s.owner||s.app?`this computer holds ${[s.owner&&"owner",s.app&&"app"].filter(Boolean).join(" and ")}`:"none on this computer (databases:password:reset makes new ones)"}`)})}},eh={name:"databases:move",summary:"bind a database to another environment of the project: its data stays as it is, and it answers throughout",usage:"<name> --to <env>",details:["Nothing is copied: the database keeps its data where it is and answers throughout. In a moment it is the new environment's \u2014 what it is counted and paid for from then on, its host (<name>-<its ref>) \u2014 and the host it had keeps working, for good. The machines given it are rolled out to the new host.","Its migrations are the machines' of the environment that keeps it: the machines that run them move with it with --with-machines (each once the database has, its migrations theirs there); without it, such a move is refused, naming them.","It needs the admin role in both environments, and the new environment room for it as for a database opened there. The passwords this computer holds for it go with it."],args:{min:1,max:1},flags:{to:{type:"string",value:"env",description:"the environment of the project it moves to (its slug)"},"with-machines":{type:"boolean",description:"move the machines that run its migrations with it"}},scope:"environment",async run(e){let t=await f(e),n=e.flags.to;if(typeof n!="string"||n==="")throw m("--to names the environment of the project it moves to");let s=(await Se(e.studio)).filter(u=>u.project_id===t.project_id),a=s.find(u=>u.slug===n||u.ref===n);if(!a)throw new y("no_such_environment",`the project has no environment ${JSON.stringify(n)}`,{hint:`its environments: ${s.map(u=>u.slug).join(", ")}`});let r=e.args[0];await v(e,t,`move the database ${r}${e.flags["with-machines"]===!0?", and the machines that run its migrations,":""} out of this environment, into ${a.slug}`),await v(e,ge(a),`move the database ${r} into this environment: it is counted and paid for here`);let i=e.flags["with-machines"]===!0,o=await G(e,t,"POST",`/databases/${ee(r)}/move`,{environment:a.ref,...i?{with_machines:!0}:{}}),l=e.io.now()+6e4;for(;o.moving_to&&e.io.now()<l;)await e.io.sleep(2e3),o=await G(e,t,"GET",`/databases/${ee(r)}`);let p=o.environment?.ref===a.ref,d=await e.state.databasePasswords(t.env_ref,r);if(p&&(d.owner!==void 0||d.app!==void 0)&&(await e.state.keepDatabasePasswords(a.ref,r,d),await e.state.forgetDatabase(t.env_ref,r)),e.out.result(o,()=>{p?e.out.line(`${o.name} is ${a.slug}'s now; ${o.host} is its host, and the one it had keeps working (palbase databases:get ${o.name} -E ${a.slug}).`):o.moving_to?e.out.line(`${o.name} is still moving to ${a.slug} (palbase databases:get ${o.name} shows when it has).`):e.out.line(`${o.name} did not move${o.move_note?`: ${o.move_note}`:""}.`)}),!p&&!o.moving_to)throw new y("database_not_moved",`${o.name} did not move${o.move_note?`: ${o.move_note}`:""}`)}};function th(e){if(e!==void 0)return String(e).split(",").map(t=>t.trim()).filter(t=>t!=="")}var nh={name:"databases:create",summary:"open a database; its passwords are kept on this computer (the platform shows them once)",usage:"[name] [--size <size>] [--allow <ip/cidr,\u2026>] [--backup-days <n>] [--show-passwords]",args:{min:0,max:1},flags:{size:{type:"string",value:"size",description:"its size (palbase databases:sizes lists what is open)"},allow:{type:"string",value:"list",description:"who may connect, as addresses or CIDRs, comma-separated (default: anyone with the password)"},"backup-days":{type:"string",value:"n",description:"how many days of backups to keep, 1 to 35"},"show-passwords":{type:"boolean",description:"print the passwords too (they are kept on this computer either way)"}},scope:"environment",async run(e){let t=await f(e),n={};e.args[0]&&(n.name=e.args[0]),e.flags.size!==void 0&&(n.size=e.flags.size);let s=th(e.flags.allow);s&&(n.allow=s);let a=Sn(e.flags["backup-days"],"backup-days");a!==void 0&&(n.backup_days=a),await v(e,t,`open a database ${e.args[0]??"main"}${n.size?` (${n.size})`:""}`);let r=await G(e,t,"POST","/databases",n);await e.state.keepDatabasePasswords(t.env_ref,r.name,{owner:r.credentials.owner.password,app:r.credentials.app.password});let i=e.flags["show-passwords"]===!0,{credentials:o,...l}=r;e.out.result(i?r:{...l,credentials:{owner:ko(o.owner),app:ko(o.app)}},()=>{e.out.line(`Opened the database ${r.name} (${r.size}) at ${r.host}.`),e.out.line("Its passwords are kept on this computer: palbase db:url and db:shell use them."),i&&(e.out.line(`owner: ${r.credentials.owner.password??""}`),e.out.line(`app: ${r.credentials.app.password??""}`))})}},_o="[YOUR-PASSWORD]";function ko(e){let{password:t,...n}=e,s={};for(let[a,r]of Object.entries(n))s[a]=typeof r=="string"?W0(r,t):r;return s}function W0(e,t){let n=e.replace(/(postgres(?:ql)?:\/\/[^:/@\s'"]+:)[^@\s'"]*(@)/gi,`$1${_o}$2`).replace(/([?&;]password=)[^&;\s'"]*/gi,`$1${_o}`);if(t)for(let s of new Set([t,encodeURIComponent(t),new URLSearchParams({p:t}).toString().slice(2)]))n=n.split(s).join(_o);return n}var sh={name:"databases:update",summary:"change a database's size, who may connect to it, or how long its backups are kept",usage:'<name> [--size <size>] [--allow <list>|""] [--backup-days <n>] [--wait]',details:["A new size is taken at once and the database moves to it serving on (palbase databases:sizes lists them): its sessions on the direct port end once, when its primary moves, and new logins wait a few seconds then."],args:{min:1,max:1},flags:{size:{type:"string",value:"size",description:"the size to move it to"},allow:{type:"string",value:"list",description:'who may connect (comma-separated addresses or CIDRs); "" for anyone with the password'},"backup-days":{type:"string",value:"n",description:"how many days of backups to keep, 1 to 35"},wait:{type:"boolean",description:"with --size: follow the move until it serves at the new size"}},scope:"environment",async run(e){let t=await f(e),n=e.args[0],s={},a=e.flags.size;if(a!==void 0){if(a==="")throw m("--size is one of palbase databases:sizes");s.size=a}let r=th(e.flags.allow);r&&(s.allow=r);let i=Sn(e.flags["backup-days"],"backup-days");if(i!==void 0&&(s.backup_days=i),Object.keys(s).length===0)throw m("nothing to change: --size, --allow or --backup-days");await v(e,t,`change the database ${n}: ${Object.keys(s).join(", ")}`);let o=await G(e,t,"PATCH",`/databases/${ee(n)}`,s);a!==void 0&&e.flags.wait===!0&&(o=await V0(e,t,n,a)),e.out.result(o,()=>{a!==void 0&&e.out.line(o.state==="resizing"?`${o.name} is moving to ${a}, serving on: its sessions on the direct port end once, when its primary moves (palbase databases:get ${o.name} follows it).`:`${o.name} serves at ${o.size} (${o.state}).`),(r||i!==void 0)&&e.out.line(`${o.name}: allows ${o.allow.length===0?"anyone with the password":o.allow.join(", ")}; backups ${o.backup_days} days.`)})}};async function V0(e,t,n,s){e.out.note(`Following ${n}'s move to ${s}\u2026`);let a="";for(let r=0;r<900;r++){let i=await G(e,t,"GET",`/databases/${ee(n)}`),o=`${i.state}${i.state_reason?` \u2014 ${i.state_reason}`:""}`;if(o!==a&&e.out.note(` ${o}`),a=o,i.size===s&&i.state!=="resizing"&&i.state!=="starting"){if(i.state==="failed")throw new y("resize_failed",`${n} did not come up at ${s}${i.state_reason?`: ${i.state_reason}`:""}`,{hint:`palbase databases:get ${n}`});return i}if(i.size!==s)throw new y("resize_changed",`${n} is asked for ${i.size} now, not ${s}: another change came`,{hint:`palbase databases:get ${n}`});await e.io.sleep(2e3)}throw new y("timeout",`${n} was still moving to ${s} after 30 minutes; it serves on meanwhile`,{hint:`palbase databases:get ${n}`})}var ah={name:"databases:password:reset",summary:"make a database role's password new; it is kept on this computer, the old one stops working",usage:"<name> --role owner|app [--show-password]",details:["The app role's new password reaches the machines given the database as they roll out with it; they keep serving."],args:{min:1,max:1},flags:{role:{type:"string",value:"role",description:"owner (migrations, the console) or app (what the machines use)"},"show-password":{type:"boolean",description:"print it too"}},scope:"environment",async run(e){let t=e.flags.role;if(t!=="owner"&&t!=="app")throw m("--role is owner or app");let n=await f(e);await v(e,n,`make the ${t} password of ${e.args[0]} new`);let a=(await G(e,n,"POST",`/databases/${ee(e.args[0])}/password`,{role:t}))[t];if(!a?.password)throw new y("no_password","the platform answered no password");await e.state.keepDatabasePasswords(n.env_ref,e.args[0],{[t]:a.password}),await H0(e,n,e.args[0]),e.out.result(e.flags["show-password"]===!0?a:ko(a),()=>{e.out.line(`The ${t} password of ${e.args[0]} is new and kept on this computer.`),e.flags["show-password"]===!0&&e.out.line(a.password??"")})}};async function H0(e,t,n){for(let s=0;s<60;s++){if((await G(e,t,"GET",`/databases/${ee(n)}`)).password_changing!==!0)return;s===0&&e.out.note(`Waiting for ${n} to take the new password\u2026`),await e.io.sleep(1e3)}throw new y("timeout",`${n} had not taken the new password after a minute; it is kept here and works once it has`,{hint:`palbase databases:get ${n}`})}var rh={name:"databases:restore",summary:"bring a database back to a moment of its backups",usage:"<name> --to <time>",args:{min:1,max:1},flags:{to:{type:"string",value:"time",description:"the moment, as an RFC 3339 time (2026-10-08T12:00:00Z)"}},scope:"environment",async run(e){let t=e.flags.to;if(!t||Number.isNaN(Date.parse(t)))throw m("--to is the moment to go back to, as an RFC 3339 time");let n=await f(e);await v(e,n,`bring ${e.args[0]} back to ${t} (what was written since goes)`);let s=await G(e,n,"POST",`/databases/${ee(e.args[0])}/restore`,{to:new Date(t).toISOString()});e.out.result(s,()=>e.out.line(`${s.name} is being brought back to ${t} \u2014 ${s.state}.`))}};function Po(e,t,n,s,a,r,i=!1){return{name:e,summary:t,usage:"<name>",args:{min:1,max:1},scope:"environment",async run(o){let l=await f(o),p=o.args[0];if(n==="DELETE"||s==="/purge"){if(!o.global.yes){if(!o.interactive)throw new y("confirmation_needed",`${a(p)} is confirmed`,{hint:"confirm with -y"});if((await o.io.prompt("Type the database's name to go ahead: ")).trim()!==p)throw new y("cancelled","nothing was changed")}}else await v(o,l,a(p));await G(o,l,n,`/databases/${ee(p)}${s}`),i&&await o.state.forgetDatabase(l.env_ref,p),o.out.result({database:p,done:e},()=>o.out.line(r(p)))}}}var ih=Po("databases:delete","delete a database (the platform keeps it 7 days to take back)","DELETE","",e=>`deleting the database ${e}`,e=>`Deleted ${e}; databases:undelete takes it back within 7 days.`),oh=Po("databases:undelete","take back a database deleted in the last 7 days","POST","/undelete",e=>`take back the database ${e}`,e=>`${e} is back.`),lh=Po("databases:purge","delete a deleted database for good, now","POST","/purge",e=>`purging the database ${e} for good`,e=>`${e} is purged.`,!0),dh={name:"github:connect",summary:"link the environment to GitHub: Palbase Cloud's GitHub app on the account whose repositories machines are built from",usage:"[--device | --github-token-stdin | --from <env> [--repo <owner/name>\u2026]] [--installation <account|id>] [--all-envs]",details:["Opens GitHub; back from it, the page shows the account and you link it by writing the environment's ref.","--from <env>: the installation another environment of the organization linked (its slug in this project, or its ref), linked here too with no GitHub sign-in \u2014 a program that opens a project for each product (an admin, or an admin access token). The environment reaches what the GitHub user who linked it there can push to.","--repo <owner/name> (given again for more): with --from, the environment reaches those repositories of the installation alone, never one it reaches later \u2014 a product's environment builds nothing of another product's.","--device: no browser here \u2014 GitHub's device flow (a code you enter at github.com/login/device on any device); the GitHub token stays in memory.","--all-envs links every environment of the project the same way."],flags:{device:{type:"boolean",description:"GitHub's device flow, for a computer with no browser (a server, an agent)"},"github-token-stdin":{type:"boolean",description:"a GitHub user's token from stdin (CI): the installations it reaches, no browser and no code"},installation:{type:"string",value:"account|id",description:"which installation to link, when the GitHub user reaches more than one (--device)"},"all-envs":{type:"boolean",description:"link every environment of the project"},from:{type:"string",value:"env",description:"another environment of the organization whose linked installation this one shares (its slug in this project, or its ref): no GitHub sign-in"},repo:{type:"string",multiple:!0,value:"owner/name",description:"with --from: the repositories it reaches of the installation, and no other (given again for more)"}},scope:"environment",async run(e){let t=await f(e),n=e.flags["all-envs"]===!0?(await qt(e)).map(ge):[t],s=e.flags.repo;if(typeof e.flags.from=="string"){if(e.flags.device===!0||e.flags["github-token-stdin"]===!0)throw m("--from links what another environment linked: no GitHub sign-in goes with it");for(let p of s??[])if(!/^[A-Za-z0-9-]{1,39}\/[A-Za-z0-9._-]{1,100}$/.test(p))throw m(`--repo names a repository as owner/name: ${JSON.stringify(p)} is none`);await Y0(e,t,n,e.flags.from,s);return}if(s!==void 0)throw m("--repo goes with --from: it narrows what a shared installation reaches");let a=await G(e,t,"POST","/machines/github/connect"),r=e.flags["github-token-stdin"]===!0;if(!r&&e.flags.device!==!0&&e.interactive&&n.length===1){let p=await e.io.openBrowser(a.url);e.out.result({url:a.url,expires_at:a.expires_at,ref:t.env_ref},()=>e.out.line(p?`Opened GitHub. When it asks, link the environment ${t.env_ref}.`:`Open ${a.url} (good until ${a.expires_at}) and link the environment ${t.env_ref}.`));return}let i=r?await Ie(e,"a GitHub user's token"):await X0(e,a.client_id),o=await Z0(e,i),l=[];for(let p of n){await v(e,p,`link the GitHub account ${o.account}`);let d=await G(e,p,"POST","/machines/github/installations",{installation_id:o.id,user_token:i});l.push({ref:p.env_ref,account:d.account})}e.out.result({linked:l},()=>{for(let p of l)e.out.line(`Linked ${p.account} to ${p.ref}.`)})}};async function Y0(e,t,n,s,a){let r=await Se(e.studio),i=r.find(u=>u.ref===s)??r.find(u=>u.project_id===t.project_id&&u.slug===s);if(!i||i.organization_id!==t.org_id)throw new y("no_such_environment",`no environment ${JSON.stringify(s)} of ${t.org_name} besides this one`,{hint:"name it by its slug in this project, or by its ref (palbase projects:list)"});let o=(await G(e,ge(i),"GET","/machines/github/installations")).installations,l=e.flags.installation,p=l!==void 0?o.filter(u=>String(u.installation_id)===l||u.account.toLowerCase()===l.toLowerCase()):o;if(p.length===0)throw new y("no_installation",`${i.slug} (${i.ref}) links no GitHub account${l!==void 0?` ${JSON.stringify(l)}`:""}`,{hint:"link it there first: palbase github:connect -E <env>"});if(p.length>1)throw m("which installation? --installation <account|id>",`${i.slug} links ${p.map(u=>u.account).join(", ")}`);let d=[];for(let u of n){if(u.env_ref===i.ref)continue;await v(e,u,`link the GitHub account ${p[0].account}${a?` for ${a.join(", ")} alone`:""}, as ${i.slug} (${i.ref}) links it`);let g=await e.studio.post(`/environments/${x("the environment's ref",u.env_ref)}/github/installations`,{installation_id:p[0].installation_id,from:i.ref,...a?{repositories:a}:{}});d.push({ref:u.env_ref,account:g.account})}e.out.result({linked:d},()=>{for(let u of d)e.out.line(`Linked ${u.account} to ${u.ref}, as ${i.slug} (${i.ref}) links it.`)})}async function X0(e,t){let n=o=>({method:"POST",headers:{accept:"application/json","content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams(o)}),s=await e.io.fetch("https://github.com/login/device/code",n({client_id:t}));if(!s.ok)throw new y("github_unavailable",`GitHub did not start the sign-in: ${s.status}`);let a=await s.json();e.out.note(""),e.out.note(` Enter this code at ${a.verification_uri}: ${a.user_code}`),e.out.note("");let r=Math.max(1,a.interval??5),i=e.io.now()+(a.expires_in??900)*1e3;for(;e.io.now()<i;){await e.io.sleep(r*1e3);let o;try{o=await e.io.fetch("https://github.com/login/oauth/access_token",n({client_id:t,device_code:a.device_code,grant_type:"urn:ietf:params:oauth:grant-type:device_code"}))}catch{continue}if(o.status>=500||o.status===429)continue;let l=await o.json().catch(()=>({}));if(l.access_token)return l.access_token;if(l.error!=="authorization_pending"){if(l.error==="slow_down"){r+=5;continue}if(l.error==="expired_token")break;throw l.error==="access_denied"?new y("access_denied","the sign-in was denied on GitHub"):new y("github_denied",`GitHub's sign-in ended: ${l.error??o.status}`)}}throw new y("expired_code","GitHub's code expired before it was entered",{hint:"run the command again"})}async function Z0(e,t){let n=await e.io.fetch("https://api.github.com/user/installations?per_page=100",{headers:{authorization:`Bearer ${t}`,accept:"application/vnd.github+json","x-github-api-version":"2022-11-28"}});if(!n.ok)throw new y("github_unavailable",`GitHub did not list your installations: ${n.status}`);let s=(await n.json()).installations.map(r=>({id:r.id,account:r.account.login}));if(s.length===0)throw new y("no_installation","Palbase Cloud is installed on no GitHub account you reach",{hint:"install it (palbase github:connect, in a browser) or have the account's owner install it"});let a=e.flags.installation;if(a!==void 0){let r=s.find(i=>String(i.id)===a||i.account.toLowerCase()===a.toLowerCase());if(!r)throw new y("no_such_installation",`you reach no installation ${JSON.stringify(a)}: ${s.map(i=>i.account).join(", ")}`);return r}if(s.length===1)return s[0];if(!e.interactive)throw m("which installation? --installation <account>",`yours: ${s.map(r=>r.account).join(", ")}`);for(s.forEach((r,i)=>e.out.note(` ${i+1}) ${r.account}`));;){let r=Number((await e.io.prompt(`Which account? [1-${s.length}] `)).trim());if(Number.isInteger(r)&&r>=1&&r<=s.length)return s[r-1]}}var ph={name:"github:list",summary:"the GitHub accounts linked to the environment, and who linked each",scope:"environment",async run(e){let t=await f(e),{installations:n}=await G(e,t,"GET","/machines/github/installations");e.out.result({installations:n},()=>{n.length===0&&e.out.line("No GitHub account is linked: palbase github:connect links one."),e.out.table(n,[{header:"account",value:s=>s.account},{header:"installation",value:s=>String(s.installation_id)},{header:"linked by",value:s=>s.linked_by}])})}},uh={name:"github:repos",summary:"the repositories machines of the environment may be built from",scope:"environment",async run(e){let t=await f(e),{repositories:n}=await G(e,t,"GET","/machines/github/repositories");e.out.result({repositories:n},()=>e.out.table(n,[{header:"repository",value:s=>s.repository},{header:"default branch",value:s=>s.default_branch},{header:"",value:s=>s.private?"private":""}]))}},ch={name:"github:disconnect",summary:"unlink a GitHub account from the environment; its repositories are no longer built",usage:"<account|installation-id>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),{installations:n}=await G(e,t,"GET","/machines/github/installations"),s=n.find(a=>String(a.installation_id)===e.args[0]||a.account.toLowerCase()===e.args[0].toLowerCase());if(!s)throw new y("no_such_installation",`no linked GitHub account ${JSON.stringify(e.args[0])}`);await v(e,t,`unlink the GitHub account ${s.account}`),await G(e,t,"DELETE",`/machines/github/installations/${s.installation_id}`),e.out.result({unlinked:s},()=>e.out.line(`Unlinked ${s.account}.`))}},Q0=["static","server","backend"];function ek(e,t){let n=(e.split("/").filter(s=>s!=="").at(-1)??t.split("/").at(-1)??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^[^a-z]+/,"");for(let s="";s!==n;)s=n,n=n.slice(0,32).replace(/-+$/,"").replace(/-git(?=-|$)/,"git").replace(/-pr(\d+)$/,"pr$1");return n===""?"app":["github","network","sizes"].includes(n)?`${n}-app`:n}function tk(e,t,n){let s={static:"a static web app: a site",server:"a web app that runs on a server: a machine, and a site it serves",backend:"a backend: a machine"};if(e.out.line(`${t.repository}@${t.branch}${t.path?`/${t.path}`:""} (${t.commit.slice(0,7)}): ${t.framework_name||"no framework known"} \u2014 ${s[t.kind]}${n!==t.kind?`; made as ${n} (--as)`:""}`),e.out.line(` why ${t.why}`),n==="static"){let a=t.build??{};e.out.line(` install ${a.install_command||"found as it builds"}`),e.out.line(` build ${a.command||"found as it builds"}`),e.out.line(` output ${a.output_dir||"found as it builds"}`),t.node&&e.out.line(` node ${t.node}`)}else e.out.line(` dockerfile ${t.dockerfile?"its own":"none: its language is found as it builds"}`)}var hh={name:"hosting:import",summary:"import a GitHub repository's directory, as Vercel does: what it is found to be \u2014 a static web app, a web app that runs on a server, a backend \u2014 is made and built on every push",usage:"[--repo <owner/name>] [--branch <branch>] [--path <dir>] [--name <name>] [--as static|server|backend]",details:["Run in a checkout, it reads the repository from origin, the branch from HEAD and the directory from where you are; flags say otherwise.","What the directory is, is read on GitHub at the branch's head \u2014 its package.json, its framework's configuration, its lockfile, its Node.js version \u2014 and printed before anything is made; at a terminal you are asked to go ahead.","static: a site, built as found. server (Next.js, Nuxt, SvelteKit on a server): a machine, and a site of its name with no files whose server it is \u2014 its address on the hosting domain, and its CDN. backend: a machine. --as makes it the other way.","A static site's pull requests and other branches, and a web app's pull requests, are previewed from the start \u2014 for the project's team alone (hosting:previews:link shares one); a backend's previews are turned on as a machine's are (machines:update --previews).","The environment needs its GitHub link first: palbase github:connect."],flags:{repo:{type:"string",value:"owner/name",description:"the GitHub repository (default: this checkout's origin)"},branch:{type:"string",value:"branch",description:"the branch whose pushes are built (default: the one checked out)"},path:{type:"string",value:"dir",description:"the directory, from the repository's root (default: this directory's place in it)"},name:{type:"string",value:"name",description:"the site's and the machine's name (default: the directory's, or the repository's at its root)"},as:{type:"string",value:"static|server|backend",description:"make it as this, whatever it is found to be"}},scope:"environment",async run(e){let t=e.flags.as;if(t!==void 0&&!Q0.includes(t))throw m("--as is static, server or backend");let n=await f(e),s=await Xn(e.io,e.io.cwd),a=e.flags.repo??s?.repository,r=e.flags.branch??s?.branch;if(!a)throw m("which repository? this directory's origin is not a GitHub repository","pass --repo owner/name");if(!r)throw m("which branch? none is checked out here","pass --branch <branch>");let i=e.flags.path??(e.flags.repo===void 0?s?.path??"":""),o=e.flags.name??ek(i,a);ee(o);let l;try{l=await G(e,n,"GET",`/machines/github/detect?${new URLSearchParams({repository:a,branch:r,...i!==""?{path:i}:{}})}`)}catch(_){if(!(_ instanceof C&&_.code==="project_not_detected"))throw _;if(t===void 0)throw new y(_.code,_.message,{hint:"say what it is with --as static|server|backend"});e.out.warn(`${_.message}: it is made as --as ${t} says`)}let p=t??l?.kind;l&&!e.global.json&&tk(e,l,p);for(let _ of l?.warnings??[])e.out.warn(_);let d={repository:a,branch:r,path:i},u=`${a}@${r}${i?`/${i}`:""}`,g=p==="static"?void 0:await vc(e,n),b={static:`the site ${o}, built from ${u}`,server:`the machine ${o} from ${u} (${g}), and the site ${o} it serves`,backend:`the machine ${o} from ${u} (${g})`}[p];await v(e,n,`make ${b}`,e.interactive?`This makes ${b}`:void 0);let w={kind:p,...l?{detection:l}:{}};if(p==="static"){let _=l?.build;w.site=await G(e,n,"POST","/sites",{name:o,source:d,previews:!0,..._?{build:{install_command:_.install_command,command:_.command,output_dir:_.output_dir,env:{}}}:{}})}else{let _=await G(e,n,"POST","/machines",{..._c(o,d,g),...p==="server"?{previews:!0}:{}});if(w.machine=_,p==="server")try{w.site=await G(e,n,"POST","/sites",{name:o,server:{machine:_.name,paths:[]}})}catch($){throw $ instanceof C?new y($.code,`made the machine ${_.name}, not its site: ${$.message}`,{hint:`palbase hosting:sites:create ${o} --server ${_.name} makes it`}):$}}e.out.result(w,()=>{let{machine:_,site:$}=w;_&&e.out.line(`Made the machine ${_.name} (${_.size}): ${_.url}`),$&&e.out.line(`Opened the site ${$.name}: ${$.url}${$.server?` \u2014 ${$.server.machine} answers it`:""}`),e.out.line(_?`Its first build is on its way: palbase machines:builds ${_.name} lists it, machines:builds:log <build> --machine ${_.name} prints its log.`:`Its first build is on its way: palbase hosting:builds --site ${$.name} lists it, hosting:builds <build> --site ${$.name} --wait follows it.`)})}};var Le=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)?e:void 0;function Gt(e,t,n){let s=Le(Le(Le(e)?.components)?.schemas)??{},a=(d,u)=>{let g=new Set,b=(_,$,S)=>{let P=Le(_);if(!P)return;if(typeof P.$ref=="string"){if(S.has(P.$ref)||!P.$ref.startsWith("#/components/schemas/"))return;b(s[P.$ref.slice(21)],$,new Set(S).add(P.$ref));return}if(P[Xa]===!0){g.add($);return}for(let A of["anyOf","oneOf","allOf"]){let D=P[A];if(Array.isArray(D))for(let z of D)b(z,$,S)}let R=Le(P.properties);if(R)for(let[A,D]of Object.entries(R))b(D,`${$}/${tr(A)}`,S);Le(P.items)&&b(P.items,`${$}/*`,S);for(let A of["additionalProperties","patternProperties","prefixItems"])if(P[A]!==void 0&&w(P[A],new Set(S)))throw new Error(`${n}: an encrypted field is a named property \u2014 not every key of a record, nor a tuple's place`)},w=(_,$)=>{if(Array.isArray(_))return _.some(P=>w(P,$));let S=Le(_);return S?S[Xa]===!0?!0:typeof S.$ref=="string"&&!$.has(S.$ref)&&S.$ref.startsWith("#/components/schemas/")?($.add(S.$ref),w(s[S.$ref.slice(21)],$)):Object.values(S).some(P=>typeof P=="object"&&P!==null&&w(P,$)):!1};return b(d,u,new Set),[...g]},r=d=>Le(Le(Le(d)?.["application/json"])?.schema),i=[];i.push(...a(r(Le(t.requestBody)?.content),"/body"));for(let d of Array.isArray(t.parameters)?t.parameters:[]){let u=Le(d);if(!u)continue;let g=a(u.schema,`/query/${tr(String(u.name))}`);if(g.length!==0){if(u.in!=="query")throw new Error(`${n}: ${String(u.in)} parameter ${String(u.name)} is marked encrypted \u2014 a body's or a query's field is`);if(g.some(b=>b.split("/").length!==3))throw new Error(`${n}: an encrypted query field is one of the query's own names`);i.push(...g)}}let o=Le(t.responses)??{},l=Object.keys(o).filter(d=>/^2\d\d$/.test(d)).sort((d,u)=>d==="200"?-1:u==="200"?1:d.localeCompare(u)),p=l.length>0?a(r(Le(o[l[0]])?.content),"/response"):[];if(i.length+p.length!==0){if(t["x-palbase-sse"]!==void 0&&p.length>0)throw new Error(`${n}: a streamed answer carries no encrypted field`);if(t["x-palbase-upload"]!==void 0)throw new Error(`${n}: an upload goes through storage, which seals nothing \u2014 it carries no encrypted field`);return{request:i,response:p}}}function mh(e){return JSON.stringify(e??null).includes(`"${Xa}":true`)}async function gh(e){let t=`${e.method.toUpperCase()} ${e.path}`,n=Le(Le(Le(Le(e.contract)?.paths)?.[e.path])?.[e.method.toLowerCase()]),s=n?Gt(e.contract,n,t):void 0;if(!s)return{body:e.body,query:e.query,open:async o=>o};let{key:a,header:r}=await Zd(e.key.kid,Pt(e.key.publicKey),Qa(e.projectId,e.method,e.path,e.params??[])),i=(o,l)=>o==null?o:es(o,l,s.request,(p,d)=>Xs(a,p,d));return{header:r,body:await i(e.body,"/body"),query:await i(e.query,"/query"),open:o=>o===void 0?Promise.resolve(o):es(o,"/response",s.response,async(l,p)=>{if(!Zs(p))throw new $e("unopenable",`${t}: ${l} came plain where the contract says it is encrypted`);return er(a,l,p)})}}var fh=["GET","POST","PUT","PATCH","DELETE"];function nk(e,t,n){let s=n.split("/").filter(Boolean);for(let[a,r]of Object.entries(e.paths??{})){let i=r?.[t.toLowerCase()];if(typeof i!="object"||i===null)continue;let o=a.split("/").filter(Boolean);if(o.length!==s.length)continue;let l=[],p=!0;for(let d=0;d<s.length&&p;d++)/^\{[^/{}]+\}$/.test(o[d])?l.push(decodeURIComponent(s[d])):p=o[d]===s[d];if(p)return{template:a,params:l,op:i}}}var yh={name:"machines:call",summary:"call a route of a machine's backend as its apps do: the fields its contract marks encrypted sealed, its answer's opened",usage:"<machine> <METHOD> <path> [--data <json>] [--token <access token>]",details:[`palbase machines:call api POST /patients --data '{"name":"Ada","tc":"10000000146"}'`,"The route is found in the machine's contract; a field it marks encrypted (palbase/backend's encrypted() and encrypt()) goes sealed to the project's key in use and comes back opened, as the app's generated client does.","--token is a user's access token, for a route that takes one; without it the call is nobody's."],args:{min:3,max:3},flags:{data:{type:"string",value:"json",description:"the request's body, as JSON"},token:{type:"string",value:"token",description:"a user's access token: the call is theirs"}},scope:"environment",async run(e){let t=await f(e),[n,s,a]=e.args,r=s.toUpperCase();if(!fh.includes(r))throw m(`${s} is not a method a route answers`,`one of ${fh.join(", ")}`);if(!a.startsWith("/"))throw m(`${a}: the route's path starts with /`,"palbase machines:call api GET /patients/p1");let i;if(e.flags.data!==void 0)try{i=JSON.parse(e.flags.data)}catch{throw m("--data is JSON",`--data '{"name":"x"}'`)}let o=x("the machine's name",n),{body:l}=await we(e.studio,t.env_ref,"GET",`/machines/${o}`),{body:p}=await we(e.studio,t.env_ref,"GET",`/machines/${o}/openapi`),d=new URL(l.url),u=new URL(a,d);if(u.origin!==d.origin||u.pathname.startsWith("//"))throw m(`${a} is not a path of ${n}`,"palbase machines:call api GET /patients/p1");let g=nk(p,r,u.pathname);if(!g)throw new y("route_not_found",`${n}'s contract has no ${r} ${u.pathname}`,{hint:`palbase machines:contract ${n} lists its routes`});let b=u.search===""?void 0:Object.fromEntries(u.searchParams),w,_="";if(Gt(p,g.op,`${r} ${g.template}`)){_=(await e.studio.get(`/environments/${t.env_ref}`)).platform_id;let{body:F}=await we(e.studio,t.env_ref,"GET","/secrets/transit"),I=F.keys.find(J=>J.id===F.current);if(!I)throw new y("no_transit_key","the project has no transit key in use: palbase transit:list says what there is");w={kid:I.id,publicKey:I.public_key}}let $=w?await gh({contract:p,method:r,path:g.template,params:g.params,body:i,query:b,projectId:_,key:w}):{body:i,query:b,header:void 0,open:async z=>z},S=new URL(d.origin);S.pathname=u.pathname;for(let[z,F]of Object.entries($.query??{}))S.searchParams.set(z,typeof F=="string"?F:JSON.stringify(F));let P={accept:"application/json"};$.body!==void 0&&(P["content-type"]="application/json"),e.flags.token&&(P.authorization=`Bearer ${e.flags.token}`),$.header&&(P[ut]=$.header);let R=await e.io.fetch(S.href,{method:r,headers:P,body:$.body===void 0?void 0:JSON.stringify($.body),redirect:"manual"}),A=await R.text(),D=A;if(A!=="")try{D=JSON.parse(A)}catch{}return R.ok&&typeof D=="object"&&(D=await $.open(D)),e.out.result({status:R.status,encrypted:w!==void 0,answer:D},()=>{e.out.line(`${R.status} ${r} ${u.pathname}${w?` (its encrypted fields sealed to ${w.kid}, the answer's opened)`:""}`),A!==""&&e.out.line(typeof D=="string"?D:JSON.stringify(D,null,2))}),R.ok?0:1}};import{createHash as xm}from"node:crypto";import{lstat as C$,mkdir as j$,mkdtemp as O$,readdir as Bo,readFile as ca,rm as Nm,rmdir as M$,writeFile as I$}from"node:fs/promises";import{tmpdir as L$}from"node:os";import{dirname as D$,join as rt,relative as Ir,resolve as Im,sep as Mr}from"node:path";function rk(e){return e.replace(/\s+/g," ").trim()}var nN=String.raw`
|
|
41
41
|
import Foundation
|
|
42
42
|
|
|
43
43
|
/// Any JSON value: what a contract leaves open.
|