@pepitahq/cli 0.2.0 → 0.4.0

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/README.md CHANGED
@@ -32,8 +32,8 @@ pepita publish my-site # publish draft → live
32
32
 
33
33
  | `--state` | what you get | URL |
34
34
  |-----------|--------------|-----|
35
- | `live` (default) | the published site | `my-site.pepita.dev` |
36
- | `draft` | the saved staging site (excludes unsaved edits) | `my-site--draft.pepita.dev` |
35
+ | `live` (default) | the published site | `my-site.pepita.page` |
36
+ | `draft` | the saved staging site (excludes unsaved edits) | `my-site--draft.pepita.page` |
37
37
  | `unsaved` | your current working copy, including un-saved edits | — |
38
38
 
39
39
  `live`/`draft` are complete checkouts (same as the editor's "Download .zip");
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import {hostname,userInfo,homedir}from'os';import {join,dirname,relative,sep}from'path';import {mkdirSync,writeFileSync,readFileSync,readdirSync,statSync}from'fs';import {createServer}from'http';import {spawn}from'child_process';import {unzipSync}from'fflate';import {createInterface}from'readline/promises';var je=Object.defineProperty;var l=(e,t)=>()=>(e&&(t=e(e=0)),t);var m=(e,t)=>{for(var n in t)je(e,n,{get:t[n],enumerable:true});};function I(){return process.env.PEPITA_CONFIG_DIR??join(homedir(),".pepita")}function z(){return join(I(),"config.json")}function b(){return process.env.PEPITA_API_BASE??h().apiBase??R}function h(){try{let e=readFileSync(z(),"utf-8"),t=JSON.parse(e);return {apiBase:R,...t}}catch{return {apiBase:R}}}function A(e){mkdirSync(I(),{recursive:true}),writeFileSync(z(),JSON.stringify(e,null,2),{mode:384});}function N(){let e=h();A({apiBase:e.apiBase});}var R,j=l(()=>{R="https://app.pepita.dev";});var H=l(()=>{});var M=l(()=>{});var D=l(()=>{});async function Ce(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",t);return new Uint8Array(n)}function Le(e){let t="";for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function W(e){return Le(await Ce(e))}var J=l(()=>{});function X(e){let t=e.fetchImpl??fetch,n=e.apiBase.replace(/\/+$/,""),r=i=>encodeURIComponent(i);async function o(i,a={}){let c=new Headers(a.headers);return e.token&&c.set("authorization",`Bearer ${e.token}`),t(`${n}${i}`,{...a,headers:c})}async function s(i,a){let c=await o(i,a);if(!c.ok)throw new g(c.status,`${a?.method??"GET"} ${i} \u2192 ${c.status} ${await c.text()}`);return await c.json()}return {raw:o,json:s,async listSites(){return (await s("/api/sites")).sites},getTree(i,a="develop"){return s(`/api/sites/${r(i)}/tree?branch=${a}`)},async writeFile(i,a,c,x){let p=await o(`/api/sites/${r(i)}/draft/write`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:a,content:c,encoding:x})});if(!p.ok)throw new g(p.status,`write ${a} \u2192 ${p.status} ${await p.text()}`)},async deleteFile(i,a){let c=await o(`/api/sites/${r(i)}/draft/delete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:a})});if(!c.ok)throw new g(c.status,`delete ${a} \u2192 ${c.status} ${await c.text()}`)},preflight(i,a){return s(`/api/sites/${r(i)}/draft/preflight`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)})},async flush(i,a){let c=await o(`/api/sites/${r(i)}/flush`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({branch:"develop",...a})});if(c.status===409)throw new g(409,"save conflict \u2014 pull latest and retry");if(!c.ok)throw new g(c.status,`save \u2192 ${c.status} ${await c.text()}`)},publish(i){return s(`/api/sites/${r(i)}/publish`,{method:"POST"})}}}var g,q=l(()=>{g=class extends Error{status;constructor(t,n){super(n),this.name="PepitaHttpError",this.status=t;}};});function _e(e){return e.split("/").pop()??e}function Ie(e){return e===".well-known"||e.startsWith(".well-known/")}function ze(e){return Ie(e)||_e(e)===".gitkeep"}function B(e){return ze(e)?false:e.split("/").some(t=>t.startsWith("."))}var F=l(()=>{});function He(e){let t=e.replace(/\s/g,"");if(t.length===0)return 0;let n=t.endsWith("==")?2:t.endsWith("=")?1:0;return t.length/4*3-n}function G(e,t){return t==="base64"?He(e):Ne.encode(e).length}var Ne,V=l(()=>{F();Ne=new TextEncoder;});var K=l(()=>{});var S=l(()=>{H();M();D();J();q();V();F();K();});function f(){return X({apiBase:b(),token:h().token??""})}async function Y(e,t={}){let n=h(),r=new Headers(t.headers);n.token&&r.set("authorization",`Bearer ${n.token}`);let o=await fetch(`${b()}${e}`,{...t,headers:r});if(o.status===401)throw new P("Not logged in \u2014 run `pepita login`.");return o}var P,u,d=l(()=>{j();S();P=class extends Error{},u=class extends Error{};});function Z(e=32){let t=new Uint8Array(e);return globalThis.crypto.getRandomValues(t),[...t].map(n=>n.toString(16).padStart(2,"0")).join("")}function qe(e){let t=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",n=process.platform==="win32"?["/c","start",'""',e]:[e];spawn(t,n,{stdio:"ignore",detached:true}).unref();}async function Q(){let e=Z(32),t=await W(e),n=Z(16),r=`${hostname()} (${userInfo().username})`,{code:o}=await new Promise((x,p)=>{let w=0,$=createServer((y,v)=>{let k=new URL(y.url??"","http://127.0.0.1");if(k.pathname!=="/callback"){v.writeHead(404).end();return}let L=k.searchParams.get("code"),Ae=k.searchParams.get("state");v.writeHead(200,{"content-type":"text/html"}).end('<html><body style="font-family:sans-serif;text-align:center;padding:3rem"><h2>pepita CLI</h2><p>You can close this tab and return to the terminal.</p></body></html>'),$.close(),clearTimeout(C),!L||Ae!==n?p(new Error("Authorization failed (state mismatch).")):x({code:L});}),C=setTimeout(()=>{$.close(),p(new Error("Login timed out (no response). If you clicked Cancel, run `pepita login` again."));},Xe);$.on("error",y=>{clearTimeout(C),p(y);}),$.listen(0,"127.0.0.1",()=>{let y=$.address();w=typeof y=="object"&&y?y.port:0;let v=`${b()}/auth/cli/authorize?port=${w}&state=${n}&code_challenge=${t}&label=${encodeURIComponent(r)}`;console.log("Opening your browser to authorize\u2026"),console.log(v),qe(v);});}),s=await fetch(`${b()}/auth/cli/token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({code:o,code_verifier:e})});if(!s.ok)throw new Error(`Token exchange failed: ${s.status} ${await s.text()}`);let{token:i,user:a}=await s.json(),c=h();A({...c,token:i,email:a.email}),console.log(`Logged in as ${a.email}.`);}async function ee(){if(h().token)try{await Y("/api/cli-tokens/current",{method:"DELETE"});}catch{}N(),console.log("Logged out.");}function te(){let e=h();console.log(e.token?e.email??"logged in":"Not logged in \u2014 run `pepita login`.");}var Xe,E=l(()=>{S();j();d();Xe=18e4;});var ne={};m(ne,{run:()=>Ge});var Ge,oe=l(()=>{E();Ge=Q;});var ie={};m(ie,{run:()=>Ve});var Ve,re=l(()=>{E();Ve=ee;});var se={};m(se,{run:()=>Ke});async function Ke(){te();}var ae=l(()=>{E();});var ce={};m(ce,{run:()=>Ye});async function Ye(){let e=await f().listSites();if(e.length===0)return console.log("No sites yet.");for(let t of e)console.log(t.slug);}var le=l(()=>{d();});function de(e){return e.split("/").pop()??e}function st(e){let t=de(e);return rt.has(t)||it.has(t.split(".").pop()?.toLowerCase()??"")?"utf-8":"base64"}function O(e){if(!e||e.startsWith("/")||e.startsWith("\\"))return false;let t=e.split(/[/\\]/);for(let n of t)if(n==="..")return false;return true}function at(e){return de(e)!==".gitkeep"}function ct(e,t){let n=[],r=[];for(let[o,s]of e){let i=t.get(o);(!i||i.content!==s.content||i.encoding!==s.encoding)&&n.push(o);}for(let o of t.keys())e.has(o)||r.push(o);return {writes:n,deletes:r}}async function me(e,t){let n=await f().getTree(e,t);return new Map(n.files.map(r=>[r.path,{content:r.content,encoding:r.encoding}]))}async function lt(e,t){let n=await f().raw(`/api/sites/${encodeURIComponent(e)}/download?branch=${t}`);if(!n.ok)throw new Error(`GET /download?branch=${t} \u2192 ${n.status} ${await n.text()}`);let r=new Uint8Array(await n.arrayBuffer());return new Map(Object.entries(unzipSync(r)))}async function he(e,t,n){let r;if(t==="unsaved"){r=new Map;for(let[s,i]of await me(e,"develop"))r.set(s,Buffer.from(i.content,i.encoding==="base64"?"base64":"utf-8"));}else r=await lt(e,t==="live"?"main":"develop");let o=0;for(let[s,i]of r){if(!O(s)){console.warn(`skipping unsafe remote path: ${s}`);continue}let a=join(n,...s.split("/"));mkdirSync(dirname(a),{recursive:true}),at(s)&&(writeFileSync(a,i),o++);}return o}function pt(e){let t=new Map,n=r=>{for(let o of readdirSync(r)){if(o===".git")continue;let s=join(r,o),i=relative(e,s).split(sep).join("/");if(statSync(s).isDirectory()){n(s);continue}if(!O(i)){console.warn(`skipping unsafe local path: ${i}`);continue}if(B(i))continue;let a=st(i),c=a==="base64"?readFileSync(s).toString("base64"):readFileSync(s,"utf-8");t.set(i,{content:c,encoding:a});}if(r!==e){let o=`${relative(e,r).split(sep).join("/")}/.gitkeep`;O(o)&&t.set(o,{content:"",encoding:"utf-8"});}};return n(e),t}function ut(e){let t=["push would exceed pepita size limits:"];for(let n of e.perFileViolations)t.push(` ${n.path}: ${(n.size/T).toFixed(1)} MB (max ${(e.budget.perFileBytes/T).toFixed(0)} MB per file)`);return e.projectedTotal>e.budget.totalBytes&&t.push(` total would be ${(e.projectedTotal/T).toFixed(1)} MB (max ${(e.budget.totalBytes/T).toFixed(0)} MB per site)`),t.push("Shrink or remove the oversized file(s) and try again."),t.join(`
3
- `)}async function we(e,t,n,r){let o=pt(t),s=await me(e,"develop"),i=ct(o,s);if(i.writes.length===0&&i.deletes.length===0)return {written:0,deleted:0};let a=f(),c=i.writes.map(p=>{let w=o.get(p);return {path:p,size:G(w.content,w.encoding)}}),x=await a.preflight(e,{writes:c,deletes:i.deletes});if(!x.ok)throw new u(ut(x));if(!n&&!await r(i))return {written:0,deleted:0};for(let p of i.writes){let w=o.get(p);await a.writeFile(e,p,w.content,w.encoding);}for(let p of i.deletes)await a.deleteFile(e,p);return {written:i.writes.length,deleted:i.deletes.length}}var it,rt,T,U=l(()=>{d();S();it=new Set(["txt","xml","html","htm","js","css","webmanifest","svg"]),rt=new Set(["_headers",".gitkeep"]);T=1024*1024;});var ye={};m(ye,{run:()=>gt});async function gt(e){let t=e[0];if(!t)throw new u("usage: pepita pull <slug> [--state live|draft|unsaved] [--dir <path>]");let n=e.includes("--state")?e[e.indexOf("--state")+1]:"live";if(!ft.includes(n))throw new u(`unknown --state '${n}' (expected: live | draft | unsaved)`);let r=n,o=e.includes("--dir")?e[e.indexOf("--dir")+1]:`./${t}`,s=await he(t,r,o);console.log(`Pulled ${s} file(s) from ${t} (${r}) into ${o}`);}var ft,xe=l(()=>{U();d();ft=["live","draft","unsaved"];});var be={};m(be,{run:()=>mt});async function mt(e){let t=e[0];if(!t)throw new u("usage: pepita apply <slug> [--dir <path>] [--yes]");let n=e.includes("--dir")?e[e.indexOf("--dir")+1]:`./${t}`,r=e.includes("--yes"),s=await we(t,n,r,async i=>{console.log(`Plan for ${t}: +${i.writes.length} write(s), -${i.deletes.length} delete(s)`);let a=createInterface({input:process.stdin,output:process.stdout}),c=(await a.question("Apply as unsaved changes? [y/N] ")).trim().toLowerCase();return a.close(),c==="y"||c==="yes"});console.log(`Applied to ${t}: ${s.written} written, ${s.deleted} deleted (unsaved). Run \`pepita save ${t}\` to save.`);}var $e=l(()=>{U();d();});var ve={};m(ve,{run:()=>ht});async function ht(e){let t=e[0];if(!t)throw new u("usage: pepita save <slug>");let n=f(),r=await n.getTree(t,"develop"),o=r.files.filter(s=>s.dirty);await n.flush(t,{expectedHeadSha:r.headSha,files:o.map(s=>({path:s.path,content:s.content,encoding:s.encoding})),deletions:r.deletions}),console.log(`Saved ${t} to draft. Run \`pepita publish ${t}\` to go live.`);}var Pe=l(()=>{d();});var Se={};m(Se,{run:()=>wt});async function wt(e){let t=e[0];if(!t)throw new u("usage: pepita publish <slug>");let n=await f().publish(t);console.log(`Published ${t} to live \u2192 ${n.productionUrl}`);}var Ee=l(()=>{d();});var Te={};m(Te,{run:()=>yt});async function yt(e){let t=e[0];if(!t){let o=await f().listSites();if(o.length===0){console.log("No sites yet.");return}console.log(`${o.length} site${o.length===1?"":"s"}:`);for(let s of o)console.log(` ${s.slug}`),console.log(` draft: https://${s.slug}--draft.pepita.dev live: https://${s.slug}.pepita.dev`);console.log("\nRun `pepita status <slug>` to see unsaved changes for one site.");return}let n=await f().getTree(t,"develop"),r=n.files.filter(o=>o.dirty).map(o=>o.path);console.log(`Site: ${t}`),console.log(`Draft: https://${t}--draft.pepita.dev`),console.log(`Live: https://${t}.pepita.dev`),console.log(`Unsaved: ${r.length} changed, ${n.deletions.length} deleted`);for(let o of r)console.log(` ~ ${o}`);for(let o of n.deletions)console.log(` - ${o}`);}var ke=l(()=>{d();});d();var Re=`pepita \u2014 command line for pepita sites
2
+ import {hostname,userInfo,homedir}from'os';import {join,dirname,relative,sep}from'path';import {mkdirSync,writeFileSync,existsSync,readFileSync,readdirSync,statSync}from'fs';import {createServer}from'http';import {spawn}from'child_process';import {unzipSync}from'fflate';import {createInterface}from'readline/promises';var ze=Object.defineProperty;var c=(e,t)=>()=>(e&&(t=e(e=0)),t);var m=(e,t)=>{for(var n in t)ze(e,n,{get:t[n],enumerable:true});};function H(){return process.env.PEPITA_CONFIG_DIR??join(homedir(),".pepita")}function M(){return join(H(),"config.json")}function $(){return process.env.PEPITA_API_BASE??w().apiBase??j}function w(){try{let e=readFileSync(M(),"utf-8"),t=JSON.parse(e);return {apiBase:j,...t}}catch{return {apiBase:j}}}function B(e){mkdirSync(H(),{recursive:true}),writeFileSync(M(),JSON.stringify(e,null,2),{mode:384});}function W(){let e=w();B({apiBase:e.apiBase});}var j,O=c(()=>{j="https://app.pepita.dev";});var J=c(()=>{});var q=c(()=>{});var X=c(()=>{});async function Je(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",t);return new Uint8Array(n)}function qe(e){let t="";for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function G(e){return qe(await Je(e))}var V=c(()=>{});function K(e){let t=e.fetchImpl??fetch,n=e.apiBase.replace(/\/+$/,""),o=r=>encodeURIComponent(r);async function i(r,a={}){let l=new Headers(a.headers);return e.token&&l.set("authorization",`Bearer ${e.token}`),t(`${n}${r}`,{...a,headers:l})}async function s(r,a){let l=await i(r,a);if(!l.ok)throw new h(l.status,`${a?.method??"GET"} ${r} \u2192 ${l.status} ${await l.text()}`);return await l.json()}return {raw:i,json:s,async listSites(){return (await s("/api/sites")).sites},createSite(r,a){return s("/api/sites",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:r,allowAnalytics:a?.allowAnalytics??true})})},async deleteSite(r){let a=await i(`/api/sites/${o(r)}`,{method:"DELETE"});if(!a.ok)throw new h(a.status,`delete ${r} \u2192 ${a.status} ${await a.text()}`)},getTree(r,a="develop"){return s(`/api/sites/${o(r)}/tree?branch=${a}`)},async writeFile(r,a,l,f){let p=await i(`/api/sites/${o(r)}/draft/write`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:a,content:l,encoding:f})});if(!p.ok)throw new h(p.status,`write ${a} \u2192 ${p.status} ${await p.text()}`)},async deleteFile(r,a){let l=await i(`/api/sites/${o(r)}/draft/delete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:a})});if(!l.ok)throw new h(l.status,`delete ${a} \u2192 ${l.status} ${await l.text()}`)},preflight(r,a){return s(`/api/sites/${o(r)}/draft/preflight`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)})},async flush(r,a){let l=await i(`/api/sites/${o(r)}/flush`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({branch:"develop",...a})});if(l.status===409)throw new h(409,"save conflict \u2014 pull latest and retry");if(!l.ok)throw new h(l.status,`save \u2192 ${l.status} ${await l.text()}`)},publish(r){return s(`/api/sites/${o(r)}/publish`,{method:"POST"})}}}var h,Y=c(()=>{h=class extends Error{status;constructor(t,n){super(n),this.name="PepitaHttpError",this.status=t;}};});function Xe(e){return e.split("/").pop()??e}function Ge(e){return e===".well-known"||e.startsWith(".well-known/")}function Ve(e){return Ge(e)||Xe(e)===".gitkeep"}function C(e){return Ve(e)?false:e.split("/").some(t=>t.startsWith("."))}var L=c(()=>{});function Ye(e){let t=e.replace(/\s/g,"");if(t.length===0)return 0;let n=t.endsWith("==")?2:t.endsWith("=")?1:0;return t.length/4*3-n}function Z(e,t){return t==="base64"?Ye(e):Ke.encode(e).length}var Ke,Q=c(()=>{L();Ke=new TextEncoder;});var ee=c(()=>{});var te,F,I,ne=c(()=>{te="pepita.page",F=e=>`https://${e}.${te}`,I=e=>`https://${e}--draft.${te}`;});var P=c(()=>{J();q();X();V();Y();Q();L();ee();ne();});function d(){return K({apiBase:$(),token:w().token??""})}async function oe(e,t={}){let n=w(),o=new Headers(t.headers);n.token&&o.set("authorization",`Bearer ${n.token}`);let i=await fetch(`${$()}${e}`,{...t,headers:o});if(i.status===401)throw new S("Not logged in \u2014 run `pepita login`.");return i}var S,u,g=c(()=>{O();P();S=class extends Error{},u=class extends Error{};});function ie(e=32){let t=new Uint8Array(e);return globalThis.crypto.getRandomValues(t),[...t].map(n=>n.toString(16).padStart(2,"0")).join("")}function ot(e){let t=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",n=process.platform==="win32"?["/c","start",'""',e]:[e];spawn(t,n,{stdio:"ignore",detached:true}).unref();}async function re(){let e=ie(32),t=await G(e),n=ie(16),o=`${hostname()} (${userInfo().username})`,{code:i}=await new Promise((f,p)=>{let y=0,b=createServer((x,v)=>{let U=new URL(x.url??"","http://127.0.0.1");if(U.pathname!=="/callback"){v.writeHead(404).end();return}let z=U.searchParams.get("code"),Ne=U.searchParams.get("state");v.writeHead(200,{"content-type":"text/html"}).end('<html><body style="font-family:sans-serif;text-align:center;padding:3rem"><h2>pepita CLI</h2><p>You can close this tab and return to the terminal.</p></body></html>'),b.close(),clearTimeout(N),!z||Ne!==n?p(new Error("Authorization failed (state mismatch).")):f({code:z});}),N=setTimeout(()=>{b.close(),p(new Error("Login timed out (no response). If you clicked Cancel, run `pepita login` again."));},nt);b.on("error",x=>{clearTimeout(N),p(x);}),b.listen(0,"127.0.0.1",()=>{let x=b.address();y=typeof x=="object"&&x?x.port:0;let v=`${$()}/auth/cli/authorize?port=${y}&state=${n}&code_challenge=${t}&label=${encodeURIComponent(o)}`;console.log("Opening your browser to authorize\u2026"),console.log(v),ot(v);});}),s=await fetch(`${$()}/auth/cli/token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({code:i,code_verifier:e})});if(!s.ok)throw new Error(`Token exchange failed: ${s.status} ${await s.text()}`);let{token:r,user:a}=await s.json(),l=w();B({...l,token:r,email:a.email}),console.log(`Logged in as ${a.email}.`);}async function se(){if(w().token)try{await oe("/api/cli-tokens/current",{method:"DELETE"});}catch{}W(),console.log("Logged out.");}function ae(){let e=w();console.log(e.token?e.email??"logged in":"Not logged in \u2014 run `pepita login`.");}var nt,T=c(()=>{P();O();g();nt=18e4;});var le={};m(le,{run:()=>it});var it,ce=c(()=>{T();it=re;});var pe={};m(pe,{run:()=>rt});var rt,ue=c(()=>{T();rt=se;});var de={};m(de,{run:()=>st});async function st(){ae();}var fe=c(()=>{T();});var ge={};m(ge,{run:()=>at});async function at(){let e=await d().listSites();if(e.length===0)return console.log("No sites yet.");for(let t of e)console.log(t.slug);}var me=c(()=>{g();});function $e(e){return e.split("/").pop()??e}function ht(e){let t=$e(e);return mt.has(t)||gt.has(t.split(".").pop()?.toLowerCase()??"")?"utf-8":"base64"}function _(e){if(!e||e.startsWith("/")||e.startsWith("\\"))return false;let t=e.split(/[/\\]/);for(let n of t)if(n==="..")return false;return true}function wt(e){return $e(e)!==".gitkeep"}function yt(e,t){let n=[],o=[];for(let[i,s]of e){let r=t.get(i);(!r||r.content!==s.content||r.encoding!==s.encoding)&&n.push(i);}for(let i of t.keys())e.has(i)||o.push(i);return {writes:n,deletes:o}}async function be(e,t){let n=await d().getTree(e,t);return new Map(n.files.map(o=>[o.path,{content:o.content,encoding:o.encoding}]))}async function xt(e,t){let n=await d().raw(`/api/sites/${encodeURIComponent(e)}/download?branch=${t}`);if(!n.ok)throw new Error(`GET /download?branch=${t} \u2192 ${n.status} ${await n.text()}`);let o=new Uint8Array(await n.arrayBuffer());return new Map(Object.entries(unzipSync(o)))}async function A(e,t,n){let o;if(t==="unsaved"){o=new Map;for(let[s,r]of await be(e,"develop"))o.set(s,Buffer.from(r.content,r.encoding==="base64"?"base64":"utf-8"));}else o=await xt(e,t==="live"?"main":"develop");let i=0;for(let[s,r]of o){if(!_(s)){console.warn(`skipping unsafe remote path: ${s}`);continue}let a=join(n,...s.split("/"));mkdirSync(dirname(a),{recursive:true}),wt(s)&&(writeFileSync(a,r),i++);}return i}function $t(e){let t=new Map,n=o=>{for(let i of readdirSync(o)){if(i===".git")continue;let s=join(o,i),r=relative(e,s).split(sep).join("/");if(statSync(s).isDirectory()){n(s);continue}if(!_(r)){console.warn(`skipping unsafe local path: ${r}`);continue}if(C(r))continue;let a=ht(r),l=a==="base64"?readFileSync(s).toString("base64"):readFileSync(s,"utf-8");t.set(r,{content:l,encoding:a});}if(o!==e){let i=`${relative(e,o).split(sep).join("/")}/.gitkeep`;_(i)&&t.set(i,{content:"",encoding:"utf-8"});}};return n(e),t}function bt(e){let t=["push would exceed pepita size limits:"];for(let n of e.perFileViolations)t.push(` ${n.path}: ${(n.size/k).toFixed(1)} MB (max ${(e.budget.perFileBytes/k).toFixed(0)} MB per file)`);return e.projectedTotal>e.budget.totalBytes&&t.push(` total would be ${(e.projectedTotal/k).toFixed(1)} MB (max ${(e.budget.totalBytes/k).toFixed(0)} MB per site)`),t.push("Shrink or remove the oversized file(s) and try again."),t.join(`
3
+ `)}async function R(e,t,n,o){let i=$t(t),s=await be(e,"develop"),r=yt(i,s);if(r.writes.length===0&&r.deletes.length===0)return {written:0,deleted:0};let a=d(),l=r.writes.map(p=>{let y=i.get(p);return {path:p,size:Z(y.content,y.encoding)}}),f=await a.preflight(e,{writes:l,deletes:r.deletes});if(!f.ok)throw new u(bt(f));if(!n&&!await o(r))return {written:0,deleted:0};for(let p of r.writes){let y=i.get(p);await a.writeFile(e,p,y.content,y.encoding);}for(let p of r.deletes)await a.deleteFile(e,p);return {written:r.writes.length,deleted:r.deletes.length}}var gt,mt,k,E=c(()=>{g();P();gt=new Set(["txt","xml","html","htm","js","css","webmanifest","svg"]),mt=new Set(["_headers",".gitkeep"]);k=1024*1024;});var ve={};m(ve,{run:()=>vt});async function vt(e){let t=e.find(a=>!a.startsWith("--"));if(!t)throw new u("usage: pepita create <name> [--no-analytics] [--from <dir>]");let n=!e.includes("--no-analytics"),o=e.includes("--from")?e[e.indexOf("--from")+1]:void 0,{slug:i,liveUrl:s,draftUrl:r}=await d().createSite(t,{allowAnalytics:n});if(console.log(`Created ${i}
4
+ live: ${s}
5
+ draft: ${r}`),o){console.log(`Uploading files from ${o} to ${i}\u2026`);let a;for(let l=0;l<5;l++)try{a=await R(i,o,!0,async()=>!0);break}catch(f){if(l<4&&/404|empty|not found/i.test(f.message)){await new Promise(p=>setTimeout(p,750));continue}throw f}console.log(`Uploaded ${a.written} file(s) (unsaved). Run \`pepita save ${i}\` then \`pepita publish ${i}\`.`);}}var Pe=c(()=>{g();E();});var Se={};m(Se,{run:()=>St});async function St(e){let t=e[0];if(!t)throw new u("usage: pepita pull <slug> [--state live|draft|unsaved] [--dir <path>]");let n=e.includes("--state")?e[e.indexOf("--state")+1]:"live";if(!Pt.includes(n))throw new u(`unknown --state '${n}' (expected: live | draft | unsaved)`);let o=n,i=e.includes("--dir")?e[e.indexOf("--dir")+1]:`./${t}`,s=await A(t,o,i);console.log(`Pulled ${s} file(s) from ${t} (${o}) into ${i}`);}var Pt,Ee=c(()=>{E();g();Pt=["live","draft","unsaved"];});var Te={};m(Te,{run:()=>Tt});async function Tt(e){let t=e[0];if(!t)throw new u("usage: pepita apply <slug> [--dir <path>] [--yes]");let n=e.includes("--dir")?e[e.indexOf("--dir")+1]:`./${t}`,o=e.includes("--yes"),s=await R(t,n,o,async r=>{console.log(`Plan for ${t}: +${r.writes.length} write(s), -${r.deletes.length} delete(s)`);let a=createInterface({input:process.stdin,output:process.stdout}),l=(await a.question("Apply as unsaved changes? [y/N] ")).trim().toLowerCase();return a.close(),l==="y"||l==="yes"});console.log(`Applied to ${t}: ${s.written} written, ${s.deleted} deleted (unsaved). Run \`pepita save ${t}\` to save.`);}var ke=c(()=>{E();g();});var Ae={};m(Ae,{run:()=>kt});async function kt(e){let t=e[0];if(!t)throw new u("usage: pepita save <slug>");let n=d(),o=await n.getTree(t,"develop"),i=o.files.filter(s=>s.dirty);await n.flush(t,{expectedHeadSha:o.headSha,files:i.map(s=>({path:s.path,content:s.content,encoding:s.encoding})),deletions:o.deletions}),console.log(`Saved ${t} to draft. Run \`pepita publish ${t}\` to go live.`);}var Re=c(()=>{g();});var Ue={};m(Ue,{run:()=>At});async function At(e){let t=e[0];if(!t)throw new u("usage: pepita publish <slug>");let n=await d().publish(t);console.log(`Published ${t} to live \u2192 ${n.productionUrl}`);}var je=c(()=>{g();});var Ce={};m(Ce,{confirmMatchesSlug:()=>Oe,run:()=>Ot});function Oe(e,t){return e.trim()===t}function Bt(e,t){let n=t;if(!n){let o=join(homedir(),"Downloads");n=existsSync(o)?o:process.cwd();}return join(n,`pepita-${e}-backup-${Date.now()}`)}async function Ot(e){let t=e.indexOf("--dir"),n=t===-1?-1:t+1,o=e.find((l,f)=>!l.startsWith("--")&&f!==n);if(!o)throw new u("usage: pepita delete <slug> [--download-snapshot] [--dir <path>] [--yes]");let i=e.includes("--yes"),s=e.includes("--download-snapshot"),r=t!==-1?e[t+1]:void 0,a=await d().listSites();if(!a.some(l=>l.slug===o)){let l=a.map(p=>p.slug).filter(p=>p.includes(o)||o.includes(p)),f=l.length?` Did you mean: ${l.join(", ")}?`:" Run `pepita list` to see your sites.";throw new u(`No site "${o}".${f}`)}if(!i){let l=createInterface({input:process.stdin,output:process.stdout}),f=await l.question(`This permanently deletes ${o} and stops serving it. Type the slug to confirm: `);if(l.close(),!Oe(f,o)){console.log("Cancelled.");return}}if(s){let l=Bt(o,r),f=await A(o,"unsaved",l);console.log(`Backup: ${f} file(s) -> ${l}`);}await d().deleteSite(o),console.log(`Deleted ${o} \u2014 it no longer serves.`);}var Le=c(()=>{g();E();});var Fe={};m(Fe,{run:()=>Ct});async function Ct(e){let t=e[0];if(!t){let i=await d().listSites();if(i.length===0){console.log("No sites yet.");return}console.log(`${i.length} site${i.length===1?"":"s"}:`);for(let s of i)console.log(` ${s.slug}`),console.log(` draft: ${I(s.slug)} live: ${F(s.slug)}`);console.log("\nRun `pepita status <slug>` to see unsaved changes for one site.");return}let n=await d().getTree(t,"develop"),o=n.files.filter(i=>i.dirty).map(i=>i.path);console.log(`Site: ${t}`),console.log(`Draft: ${I(t)}`),console.log(`Live: ${F(t)}`),console.log(`Unsaved: ${o.length} changed, ${n.deletions.length} deleted`);for(let i of o)console.log(` ~ ${i}`);for(let i of n.deletions)console.log(` - ${i}`);}var Ie=c(()=>{g();P();});g();var _e=`pepita \u2014 command line for pepita sites
4
6
 
5
7
  Usage: pepita <command> [args]
6
8
 
@@ -8,10 +10,12 @@ Usage: pepita <command> [args]
8
10
  logout Remove the local token
9
11
  whoami Show the logged-in account
10
12
  list List your sites
13
+ create <name> [--no-analytics] [--from d] Create a new site (optionally from a local dir)
11
14
  pull <slug> [--state live|draft|unsaved] [--dir d] Download a site's files (default: live)
12
15
  apply <slug> [--dir d] [--yes] Upload local files as unsaved changes
13
16
  save <slug> Save unsaved changes to the draft
14
17
  publish <slug> Publish the draft to live
18
+ delete <slug> [--download-snapshot] [--yes] Permanently delete a site (optionally snapshot to /tmp first)
15
19
  status <slug> Show unsaved changes + URLs
16
- `,xt={login:()=>Promise.resolve().then(()=>(oe(),ne)),logout:()=>Promise.resolve().then(()=>(re(),ie)),whoami:()=>Promise.resolve().then(()=>(ae(),se)),list:()=>Promise.resolve().then(()=>(le(),ce)),pull:()=>Promise.resolve().then(()=>(xe(),ye)),apply:()=>Promise.resolve().then(()=>($e(),be)),save:()=>Promise.resolve().then(()=>(Pe(),ve)),publish:()=>Promise.resolve().then(()=>(Ee(),Se)),status:()=>Promise.resolve().then(()=>(ke(),Te))};async function bt(){let[,,e,...t]=process.argv;if(!e||e==="--help"||e==="-h"||e==="help"){console.log(Re);return}let n=xt[e];if(!n){console.error(`Unknown command: ${e}
17
- `),console.log(Re),process.exitCode=1;return}await(await n()).run(t);}bt().catch(e=>{e instanceof u?(console.error(e.message),process.exitCode=1):e instanceof P||e instanceof g&&e.status===401?(console.error("Not logged in \u2014 run `pepita login`."),process.exitCode=2):(console.error(`Error: ${e?.message??e}`),process.exitCode=1);});
20
+ `,Lt={login:()=>Promise.resolve().then(()=>(ce(),le)),logout:()=>Promise.resolve().then(()=>(ue(),pe)),whoami:()=>Promise.resolve().then(()=>(fe(),de)),list:()=>Promise.resolve().then(()=>(me(),ge)),create:()=>Promise.resolve().then(()=>(Pe(),ve)),pull:()=>Promise.resolve().then(()=>(Ee(),Se)),apply:()=>Promise.resolve().then(()=>(ke(),Te)),save:()=>Promise.resolve().then(()=>(Re(),Ae)),publish:()=>Promise.resolve().then(()=>(je(),Ue)),delete:()=>Promise.resolve().then(()=>(Le(),Ce)),status:()=>Promise.resolve().then(()=>(Ie(),Fe))};async function Ft(){let[,,e,...t]=process.argv;if(!e||e==="--help"||e==="-h"||e==="help"){console.log(_e);return}let n=Lt[e];if(!n){console.error(`Unknown command: ${e}
21
+ `),console.log(_e),process.exitCode=1;return}await(await n()).run(t);}Ft().catch(e=>{e instanceof u?(console.error(e.message),process.exitCode=1):e instanceof S||e instanceof h&&e.status===401?(console.error("Not logged in \u2014 run `pepita login`."),process.exitCode=2):(console.error(`Error: ${e?.message??e}`),process.exitCode=1);});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pepitahq/cli",
3
- "version": "0.2.0",
4
- "description": "Command-line for pepita sites — pull, apply, save, publish.",
3
+ "version": "0.4.0",
4
+ "description": "Command-line for pepita sites — create, pull, apply, save, publish, delete.",
5
5
  "license": "MIT",
6
6
  "author": "Zoltan Gobolos <zgobolos@barelynotable.com>",
7
7
  "type": "module",