zerostarter 0.0.25 → 0.0.26-canary.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.
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,r as c,s as l,t as u,u as d}from"../convert-jDYJ2DZ9.mjs";import{basename as f,delimiter as p,dirname as m,join as h,parse as g,resolve as _}from"node:path";import{existsSync as v,readFileSync as ee,readdirSync as y}from"node:fs";import{spawn as b}from"node:child_process";import{access as te}from"node:fs/promises";import{createInterface as ne}from"node:readline/promises";import{randomBytes as re}from"node:crypto";import{parseArgs as ie}from"node:util";import{homedir as ae}from"node:os";const x=(e,t,n=process.stdout)=>r=>n.isTTY?`\x1b[${e}m${r}\x1b[${t}m`:r,S=x(`36`,`39`),C=x(`32`,`39`),w=x(`33`,`39`),T=x(`90`,`39`),E=x(`2`,`22`),D=x(`31`,`39`,process.stderr),oe=x(`2`,`22`,process.stderr),O=x(`38;5;208`,`39`),k=[`◇`,`◆`],A=(e,t)=>{let n=(e%k.length+k.length)%k.length;return`${S(k[n])} ${t}`},j={barStart:`┌`,bar:`│`,barEnd:`└`,active:`◆`,submit:`◇`,radioOn:`●`,radioOff:`○`,warn:`▲`,error:`■`,barH:`─`,cornerTR:`╮`,connectL:`├`,cornerBL:`╰`,cornerBR:`╯`};var se=class extends Error{constructor(...e){super(...e),this.name=`SubprocessError`,this.stdout=``,this.stderr=``}};const ce=[`.exe`,`.com`],M=e=>e.replaceAll(/([()\][%!^"`<>&|;, *?])/g,`^$1`),le=e=>M(M(`"${e.replaceAll(/(\\*)"/g,`$1$1\\"`).replace(/(\\*)$/,`$1$1`)}"`)),ue=async(e,t,n)=>{if(ce.some(t=>e.toLowerCase().endsWith(t)))return!0;let r=(n.PATH||n.Path||``).split(p).filter(Boolean).map(e=>e.replace(/^"(.*)"$/,`$1`));try{return await Promise.any([t,...r].flatMap(t=>ce.map(n=>te(`${_(t,e)}${n}`)))),!0}catch{return!1}},de=async(e,t,n,r=process.platform===`win32`)=>{let i=n.cwd?String(n.cwd):`.`,a=n.env||process.env;return r&&!n.shell&&!await ue(e,i,a)?[M(e),t.map(le),{...n,shell:!0}]:[e,t,n]},N=async(e,t,n,r)=>{let i=[e,...t].join(` `),[a,o,s]=await de(e,t,n);s.shell&&o.length>0&&(a=[a,...o].join(` `),o=[]);let c=b(a,o,s),l=``,u=``;return new Promise((e,t)=>{let n=(e,n,r)=>{let i=new se(e,r?{cause:r}:void 0);i.stdout=l,i.stderr=u,i.exitCode=n,t(i)},a=e=>{e.code!==`ERR_STREAM_PREMATURE_CLOSE`&&e.code!==`EPIPE`&&n(`Command failed: ${i}`,void 0,e)};c.stdout&&(c.stdout.setEncoding(`utf8`),c.stdout.on(`data`,e=>{l+=e,r?.(e)}),c.stdout.on(`error`,a)),c.stderr&&(c.stderr.setEncoding(`utf8`),c.stderr.on(`data`,e=>{u+=e,r?.(e)}),c.stderr.on(`error`,a)),c.on(`error`,e=>n(`Command failed: ${i}`,void 0,e)),c.on(`close`,(t,r)=>{if(r)return n(`Command was terminated with ${r}: ${i}`);if(t!==0)return n(`Command failed with exit code ${t}: ${i}`,t??void 0);e({stdout:l,stderr:u})})})},P=async(e,t,n)=>{let{stdout:r}=await N(e,t,{cwd:n,stdio:[`ignore`,`pipe`,`pipe`]});return r},fe=async(e,t,n)=>{await N(e,t,{cwd:n,stdio:`inherit`})},F=async(e,t)=>{try{return await N(e,t,{stdio:`ignore`}),!0}catch{return!1}},pe=RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),me=e=>e.replace(pe,``),he=new WeakSet,ge=e=>{if(process.stderr.write(`\n ${D(e instanceof Error?e.message:String(e))}\n`),!(e instanceof se)||he.has(e))return;let t=(e.stderr||e.stdout||``).trim();if(!t)return;let n=me(t).split(`
3
- `).map(e=>e.trimEnd()).filter(Boolean).slice(-12);for(let e of n)process.stderr.write(` ${oe(e)}\n`)},_e=async(e,t,n)=>{let r=process.stdout,i=n.label??``;if(!r.isTTY){i&&r.write(` ${S(j.active)} ${i}\n`),await fe(e,t,n.cwd);return}let a=n.lines??3,o=[],s=0,c=0,l=``,u=``,d=()=>(r.columns||80)-1,f=e=>{r.write(e?`\x1B[?7h`:`\x1B[?7l`)},p=()=>{c>0&&(r.write(`[${c}A`),c=0)},m=()=>{p();let e=0;r.write(` ${A(s,i)}\n`),e++;for(let t of o.slice(-a))r.write(` ${T(j.bar)} ${E(t.slice(0,Math.max(0,d()-5)))}\n`),e++;c=e},h=e=>{u+=e,l+=e;let t=l.split(`
4
- `);l=t.pop()??``;for(let e of t)o.push(me(e).replace(/\r/g,``).replace(/\t/g,` `).trimEnd()),m()};m(),f(!1);let g=()=>{f(!0),process.exit(130)};process.once(`SIGINT`,g);let _=setInterval(()=>{s+=1,m()},400),v=()=>{clearInterval(_),process.removeListener(`SIGINT`,g)};try{await N(e,t,{cwd:n.cwd,stdio:[`ignore`,`pipe`,`pipe`]},h)}catch(e){throw v(),p(),f(!0),r.write(u),e&&typeof e==`object`&&he.add(e),e}v(),p(),f(!0),r.write(` ${S(j.active)} ${n.done??i}\n`);for(let e of o.slice(-a))r.write(` ${T(j.bar)} ${E(e.slice(0,Math.max(0,d()-5)))}\n`)};var ve={name:`zerostarter`,version:`0.0.25`,description:`Go from zero to a production-ready SaaS, rebranded and ready to ship.`,keywords:[`cli`,`saas`,`scaffold`,`starter`,`template`,`zerostarter`],homepage:`https://github.com/nrjdalal/zerostarter#readme`,bugs:`https://github.com/nrjdalal/zerostarter/issues`,license:`MIT`,author:{name:`Neeraj Dalal`,email:`admin@nrjdalal.com`,url:`https://nrjdalal.com`},repository:`nrjdalal/zerostarter`,funding:`https://github.com/sponsors/nrjdalal`,bin:{zerostarter:`dist/bin/index.mjs`},files:[`dist`],type:`module`,types:`./dist/index.d.mts`,exports:`./dist/index.mjs`,scripts:{build:`tsdown`,"check-types":`tsc --noEmit`,dev:`tsdown --watch`,test:`bun test`},devDependencies:{"@packages/config":`workspace:*`,"@types/bun":`catalog:`,"@types/node":`catalog:`,tsdown:`catalog:`,typescript:`catalog:`}};const I=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),ye=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,L=e=>ye(e,S(e)),R=e=>{process.stdout.write(`\n ${T(j.barStart)} ${e}\n ${T(j.bar)}\n`)},z=e=>{process.stdout.write(` ${T(j.bar)}\n ${T(j.barEnd)} ${e}\n`)},B=(e=`Cancelled`)=>{process.stdout.write(` ${T(j.barEnd)} ${D(e)}\n`)},V=e=>e.replace(RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),``).replace(RegExp(`\x1B\\]8;;.*?\\x07`,`g`),``).length,H=(e,t=``,n=!1)=>{let r=`\n${e}\n`.split(`
5
- `),i=V(t),a=r.reduce((e,t)=>Math.max(e,V(t)),i),o=Math.max(a,54)+2,s=r.map(e=>` ${T(j.bar)} ${e}${` `.repeat(o-V(e))}${T(j.bar)}`).join(`
6
- `),c=n?j.cornerBL:j.connectL;process.stdout.write(` ${S(j.active)} ${t} ${T(j.barH.repeat(Math.max(o-i-1,1))+j.cornerTR)}\n${s}\n ${T(c+j.barH.repeat(o+2)+j.cornerBR)}\n`)},U=(e,t=[])=>{process.stdout.write(` ${S(j.active)} ${e}\n`);for(let e of t)process.stdout.write(` ${T(j.bar)} ${e}\n`)},be=e=>{process.stdout.write(` ${C(j.active)} ${C(e)}\n`)},W=(e,t=[])=>{process.stdout.write(` ${w(j.warn)} ${w(e)}\n`);for(let e of t)process.stdout.write(` ${T(j.bar)} ${w(e)}\n`)},xe=async(e,t,n)=>{let r=process.stdout,i=null;if(r.isTTY){let t=0;r.write(` ${A(0,e)}`),i=setInterval(()=>{t+=1,r.write(`\r\x1b[K ${A(t,e)}`)},400)}try{let e=await n();return i&&clearInterval(i),r.write(r.isTTY?`\r\x1b[K ${S(j.active)} ${t}\n`:` ${S(j.active)} ${t}\n`),e}catch(e){throw i&&clearInterval(i),r.isTTY&&r.write(`\r\x1B[K`),e}},Se=e=>{let t=e?`${C(j.radioOn)} Yes`:E(`${j.radioOff} Yes`),n=e?E(`${j.radioOff} No`):`${C(j.radioOn)} No`;return` ${T(j.bar)} ${t} ${E(`/`)} ${n}`},G=async(e,t=!0)=>{let n=process.stdout;return I()?new Promise(r=>{let i=t,a=process.stdin,o=Math.max(1,Math.ceil((5+V(e))/(n.columns||80)));n.write(` ${S(j.submit)} ${e}\n`),n.write(Se(i)),a.setRawMode(!0),a.resume(),a.setEncoding(`utf8`);let s=()=>{a.setRawMode(!1),a.pause(),a.removeListener(`data`,u)},c=()=>{n.write(`\r\x1b[K\x1b[${o}A\r\x1b[0J`)},l=()=>{s(),c(),r(i)};function u(t){t===``?(s(),c(),n.write(` ${S(j.submit)} ${E(e)}\n`),B(),process.exit(130)):t===`\r`||t===`
7
- `?l():t===`y`||t===`Y`?(i=!0,l()):t===`n`||t===`N`?(i=!1,l()):(t===`\x1B[C`||t===`\x1B[D`||t===`\x1B[A`||t===`\x1B[B`||t===`h`||t===`l`)&&(i=!i,n.write(`\r\x1b[K${Se(i)}`))}a.on(`data`,u)}):(n.write(` ${S(j.active)} ${e} ${E(t?`Yes`:`No`)}\n`),t)},Ce=async(e,t=``)=>{process.stdout.write(` ${S(j.submit)} ${e}${t?` ${E(`(${t})`)}`:``}\n`);let n=ne({input:process.stdin,output:process.stdout});try{return(await n.question(` ${T(j.bar)} `)).trim()||t}finally{n.close()}},we=`pglaunch@5.5.7`,Te=async()=>F(`docker`,[`info`]),Ee=e=>{let r=h(e,`.env`);if(!s(r)){let i=h(e,`.env.example`);n(r,s(i)?t(i):``)}return r},De=(e,n)=>{let r=`${n}=`,i=t(e).split(`
8
- `).find(e=>e.startsWith(r));return i?i.slice(r.length).trim():``},Oe=(e,r,i)=>{let a=`${r}=`,o=t(e).split(`
9
- `),s=o.findIndex(e=>e.startsWith(a));if(s>=0)o[s]=`${a}${i}`;else{for(;o.length&&o[o.length-1]===``;)o.pop();o.push(`${a}${i}`,``)}n(e,o.join(`
10
- `))},ke=e=>{let t=Ee(e);De(t,`BETTER_AUTH_SECRET`)||Oe(t,`BETTER_AUTH_SECRET`,re(32).toString(`base64`))},K=e=>{let t=h(e,`.env`);return s(t)&&De(t,`POSTGRES_URL`)!==``},Ae=e=>{let t=e.match(/postgres(?:ql)?:\/\/[\w.:@\-/%?=&]+/);if(!t)return null;let n=e.match(/name "([^" ]+) :\d+" started/),r=e.match(/similar name "([^"]+)"/);return{url:t[0],container:n?n[1]:r?r[1]:``}},je=async(e,t)=>{let n=t?[`--bun`,we,`-k`,`-c`]:[`--bun`,we,`-k`],r;try{r=await P(`bunx`,n,e)}catch(e){let t=e;r=[t.stdout,t.stderr].filter(Boolean).join(`
11
- `)}return{launch:Ae(r),out:r}},Me=e=>new Promise(t=>setTimeout(t,e)),Ne=async e=>{if(e)for(let t=0;t<30;t++){if(await F(`docker`,[`exec`,e,`pg_isready`,`-U`,`postgres`]))return;await Me(1e3)}},Pe=async e=>{let t=Ee(e),n=await je(e,!1),r=n.launch?n:await je(e,!0);if(!r.launch)throw Error(r.out.trim()||`pglaunch did not print a connection URL`);Oe(t,`POSTGRES_URL`,r.launch.url),await Ne(r.launch.container),await _e(`bun`,[`run`,`db:migrate`],{cwd:e,label:`Provisioning a local Postgres database`,done:`Provisioned a local Postgres database`})},q=async e=>{await _e(`bun`,[`install`],{cwd:e,label:`Installing dependencies`,done:`Installed dependencies`})},Fe=async()=>{if(!await F(`bun`,[`--version`]))throw Error(`bun is not on PATH`)},Ie=async(e=Fe,t=!!process.versions.bun,n=process.env.npm_config_user_agent||``)=>{if(t||n.startsWith(`bun/`))return!0;try{return await e(),!0}catch{return!1}},Le=async(e,t=`main`)=>{await P(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},Re=async(e,t=`main`)=>{await P(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e,`-o`])},ze=async(e=`main`)=>{let t=`https://raw.githubusercontent.com/nrjdalal/zerostarter/${e}/.gitpickignore`,n=await fetch(t);if(!n.ok)throw Error(`Could not fetch .gitpickignore from ${e} (HTTP ${n.status}).`);return n.text()},Be=async e=>(await P(`git`,[`status`,`--porcelain`],e)).trim()===``,Ve=async(e,t)=>{for(let n of t)try{await P(`git`,[`checkout`,`--`,n],e),await P(`git`,[`clean`,`-fd`,`--`,n],e)}catch{}},He=async e=>{await P(`git`,[`reset`,`--hard`,`HEAD`],e),await P(`git`,[`clean`,`-fd`],e)},Ue=async(e,t,n)=>{if(!s(h(e,`.git`)))throw Error(t);if(!await Be(e))throw Error(n)},We=async(e,t,n)=>{try{await n()}catch(n){throw await He(e),console.log(t),n}},Ge=async e=>{await P(`git`,[`init`,`-q`,`-b`,`canary`],e)},Ke=async(e,t)=>{await P(`git`,[`branch`,t],e)},J=async(e,t)=>{await P(`git`,[`add`,`-A`],e);try{await P(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},Y=(e,t)=>{try{return ie(t)}catch(t){process.stderr.write(`${D(t instanceof Error?t.message:String(t))}\n\n`),process.stdout.write(`${e}\n`),process.exit(1)}},qe=(e=process.env.npm_config_user_agent||``)=>e.startsWith(`bun/`)?`bunx`:e.startsWith(`pnpm/`)?`pnpm dlx`:e.startsWith(`yarn/`)?`yarn dlx`:e.startsWith(`npm/`)?`npx`:`unknown`,Je=(e=process.platform)=>e===`win32`?{cmd:`powershell`,args:[`-Command`,`irm bun.sh/install.ps1 | iex`]}:{cmd:`bash`,args:[`-c`,`curl -fsSL https://bun.sh/install | bash`]},Ye=(e=process.argv.slice(2))=>[`zerostarter`,...e].join(` `),Xe=()=>h(process.env.BUN_INSTALL||h(ae(),`.bun`),`bin`),X=async(e=!1)=>{if(await Ie())return;let t=qe(),n=`bunx --bun ${Ye()}`;if(console.error(D(`Seems like you are using ${t===`unknown`?`npx/pnpx`:t} or Windows, try using:`)),console.error(O(` ${n}`)),console.error(``),e||I()&&await G(`Or install bun now and continue?`,!0)){let{cmd:e,args:t}=Je();console.error(`Installing bun from https://bun.sh/install ...`);try{if(await fe(e,t),process.env.PATH=`${Xe()}${p}${process.env.PATH||``}`,await Ie())return;console.error(D(`
12
- bun is installed but isn't on this shell's PATH yet. Open a new terminal and re-run:`)),console.error(O(` ${n}`))}catch{console.error(D(`
13
- Could not install bun. Install it from https://bun.sh, then re-run:`)),console.error(O(` ${n}`))}}process.exit(1)},Ze=`Usage:
2
+ import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,r as c,s as l,t as u,u as d}from"../convert-HXDPuaZU.mjs";import{basename as f,delimiter as p,dirname as m,join as h,parse as g,resolve as _}from"node:path";import{existsSync as v,readFileSync as y,readdirSync as ee}from"node:fs";import{spawn as b}from"node:child_process";import{access as x}from"node:fs/promises";import{createInterface as te}from"node:readline/promises";import{randomBytes as ne}from"node:crypto";import{parseArgs as re}from"node:util";import{homedir as ie}from"node:os";const S=(e,t,n=process.stdout)=>r=>n.isTTY?`\x1b[${e}m${r}\x1b[${t}m`:r,C=S(`36`,`39`),w=S(`32`,`39`),T=S(`33`,`39`),E=S(`90`,`39`),D=S(`2`,`22`),O=S(`31`,`39`,process.stderr),ae=S(`2`,`22`,process.stderr),k=S(`38;5;208`,`39`),A=[`◇`,`◆`],j=(e,t)=>{let n=(e%A.length+A.length)%A.length;return`${C(A[n])} ${t}`},M={barStart:`┌`,bar:`│`,barEnd:`└`,active:`◆`,submit:`◇`,radioOn:`●`,radioOff:`○`,warn:`▲`,error:`■`,barH:`─`,cornerTR:`╮`,connectL:`├`,cornerBL:`╰`,cornerBR:`╯`};var oe=class extends Error{constructor(...e){super(...e),this.name=`SubprocessError`,this.stdout=``,this.stderr=``}};const se=[`.exe`,`.com`],N=e=>e.replaceAll(/([()\][%!^"`<>&|;, *?])/g,`^$1`),ce=e=>N(N(`"${e.replaceAll(/(\\*)"/g,`$1$1\\"`).replace(/(\\*)$/,`$1$1`)}"`)),le=async(e,t,n)=>{if(se.some(t=>e.toLowerCase().endsWith(t)))return!0;let r=(n.PATH||n.Path||``).split(p).filter(Boolean).map(e=>e.replace(/^"(.*)"$/,`$1`));try{return await Promise.any([t,...r].flatMap(t=>se.map(n=>x(`${_(t,e)}${n}`)))),!0}catch{return!1}},ue=async(e,t,n,r=process.platform===`win32`)=>{let i=n.cwd?String(n.cwd):`.`,a=n.env||process.env;return r&&!n.shell&&!await le(e,i,a)?[N(e),t.map(ce),{...n,shell:!0}]:[e,t,n]},P=async(e,t,n,r)=>{let i=[e,...t].join(` `),[a,o,s]=await ue(e,t,n);s.shell&&o.length>0&&(a=[a,...o].join(` `),o=[]);let c=b(a,o,s),l=``,u=``;return new Promise((e,t)=>{let n=(e,n,r)=>{let i=new oe(e,r?{cause:r}:void 0);i.stdout=l,i.stderr=u,i.exitCode=n,t(i)},a=e=>{e.code!==`ERR_STREAM_PREMATURE_CLOSE`&&e.code!==`EPIPE`&&n(`Command failed: ${i}`,void 0,e)};c.stdout&&(c.stdout.setEncoding(`utf8`),c.stdout.on(`data`,e=>{l+=e,r?.(e)}),c.stdout.on(`error`,a)),c.stderr&&(c.stderr.setEncoding(`utf8`),c.stderr.on(`data`,e=>{u+=e,r?.(e)}),c.stderr.on(`error`,a)),c.on(`error`,e=>n(`Command failed: ${i}`,void 0,e)),c.on(`close`,(t,r)=>{if(r)return n(`Command was terminated with ${r}: ${i}`);if(t!==0)return n(`Command failed with exit code ${t}: ${i}`,t??void 0);e({stdout:l,stderr:u})})})},F=async(e,t,n)=>{let{stdout:r}=await P(e,t,{cwd:n,stdio:[`ignore`,`pipe`,`pipe`]});return r},de=async(e,t,n)=>{await P(e,t,{cwd:n,stdio:`inherit`})},I=async(e,t)=>{try{return await P(e,t,{stdio:`ignore`}),!0}catch{return!1}},fe=RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),pe=e=>e.replace(fe,``),me=new WeakSet,he=e=>{if(process.stderr.write(`\n ${O(e instanceof Error?e.message:String(e))}\n`),!(e instanceof oe)||me.has(e))return;let t=(e.stderr||e.stdout||``).trim();if(!t)return;let n=pe(t).split(`
3
+ `).map(e=>e.trimEnd()).filter(Boolean).slice(-12);for(let e of n)process.stderr.write(` ${ae(e)}\n`)},ge=async(e,t,n)=>{let r=process.stdout,i=n.label??``;if(!r.isTTY){i&&r.write(` ${C(M.active)} ${i}\n`),await de(e,t,n.cwd);return}let a=n.lines??3,o=[],s=0,c=0,l=``,u=``,d=()=>(r.columns||80)-1,f=e=>{r.write(e?`\x1B[?7h`:`\x1B[?7l`)},p=()=>{c>0&&(r.write(`[${c}A`),c=0)},m=()=>{p();let e=0;r.write(` ${j(s,i)}\n`),e++;for(let t of o.slice(-a))r.write(` ${E(M.bar)} ${D(t.slice(0,Math.max(0,d()-5)))}\n`),e++;c=e},h=e=>{u+=e,l+=e;let t=l.split(`
4
+ `);l=t.pop()??``;for(let e of t)o.push(pe(e).replace(/\r/g,``).replace(/\t/g,` `).trimEnd()),m()};m(),f(!1);let g=()=>{f(!0),process.exit(130)};process.once(`SIGINT`,g);let _=setInterval(()=>{s+=1,m()},400),v=()=>{clearInterval(_),process.removeListener(`SIGINT`,g)};try{await P(e,t,{cwd:n.cwd,stdio:[`ignore`,`pipe`,`pipe`]},h)}catch(e){throw v(),p(),f(!0),r.write(u),e&&typeof e==`object`&&me.add(e),e}v(),p(),f(!0),r.write(` ${C(M.active)} ${n.done??i}\n`);for(let e of o.slice(-a))r.write(` ${E(M.bar)} ${D(e.slice(0,Math.max(0,d()-5)))}\n`)};var _e={name:`zerostarter`,version:`0.0.26-canary.0`,description:`Go from zero to a production-ready SaaS, rebranded and ready to ship.`,keywords:[`cli`,`saas`,`scaffold`,`starter`,`template`,`zerostarter`],homepage:`https://github.com/nrjdalal/zerostarter#readme`,bugs:`https://github.com/nrjdalal/zerostarter/issues`,license:`MIT`,author:{name:`Neeraj Dalal`,email:`admin@nrjdalal.com`,url:`https://nrjdalal.com`},repository:`nrjdalal/zerostarter`,funding:`https://github.com/sponsors/nrjdalal`,bin:{zerostarter:`dist/bin/index.mjs`},files:[`dist`],type:`module`,types:`./dist/index.d.mts`,exports:`./dist/index.mjs`,scripts:{build:`tsdown`,"check-types":`tsc --noEmit`,dev:`tsdown --watch`,test:`bun test`},devDependencies:{"@packages/config":`workspace:*`,"@types/bun":`catalog:`,"@types/node":`catalog:`,tsdown:`catalog:`,typescript:`catalog:`}};const L=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),ve=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,R=e=>ve(e,C(e)),z=e=>{process.stdout.write(`\n ${E(M.barStart)} ${e}\n ${E(M.bar)}\n`)},B=e=>{process.stdout.write(` ${E(M.bar)}\n ${E(M.barEnd)} ${e}\n`)},V=(e=`Cancelled`)=>{process.stdout.write(` ${E(M.barEnd)} ${O(e)}\n`)},H=e=>e.replace(RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),``).replace(RegExp(`\x1B\\]8;;.*?\\x07`,`g`),``).length,U=(e,t=``,n=!1)=>{let r=`\n${e}\n`.split(`
5
+ `),i=H(t),a=r.reduce((e,t)=>Math.max(e,H(t)),i),o=Math.max(a,54)+2,s=r.map(e=>` ${E(M.bar)} ${e}${` `.repeat(o-H(e))}${E(M.bar)}`).join(`
6
+ `),c=n?M.cornerBL:M.connectL;process.stdout.write(` ${C(M.active)} ${t} ${E(M.barH.repeat(Math.max(o-i-1,1))+M.cornerTR)}\n${s}\n ${E(c+M.barH.repeat(o+2)+M.cornerBR)}\n`)},W=(e,t=[])=>{process.stdout.write(` ${C(M.active)} ${e}\n`);for(let e of t)process.stdout.write(` ${E(M.bar)} ${e}\n`)},ye=e=>{process.stdout.write(` ${w(M.active)} ${w(e)}\n`)},G=(e,t=[])=>{process.stdout.write(` ${T(M.warn)} ${T(e)}\n`);for(let e of t)process.stdout.write(` ${E(M.bar)} ${T(e)}\n`)},be=async(e,t,n)=>{let r=process.stdout,i=null;if(r.isTTY){let t=0;r.write(` ${j(0,e)}`),i=setInterval(()=>{t+=1,r.write(`\r\x1b[K ${j(t,e)}`)},400)}try{let e=await n();return i&&clearInterval(i),r.write(r.isTTY?`\r\x1b[K ${C(M.active)} ${t}\n`:` ${C(M.active)} ${t}\n`),e}catch(e){throw i&&clearInterval(i),r.isTTY&&r.write(`\r\x1B[K`),e}},xe=e=>{let t=e?`${w(M.radioOn)} Yes`:D(`${M.radioOff} Yes`),n=e?D(`${M.radioOff} No`):`${w(M.radioOn)} No`;return` ${E(M.bar)} ${t} ${D(`/`)} ${n}`},K=async(e,t=!0)=>{let n=process.stdout;return L()?new Promise(r=>{let i=t,a=process.stdin,o=Math.max(1,Math.ceil((5+H(e))/(n.columns||80)));n.write(` ${C(M.submit)} ${e}\n`),n.write(xe(i)),a.setRawMode(!0),a.resume(),a.setEncoding(`utf8`);let s=()=>{a.setRawMode(!1),a.pause(),a.removeListener(`data`,u)},c=()=>{n.write(`\r\x1b[K\x1b[${o}A\r\x1b[0J`)},l=()=>{s(),c(),r(i)};function u(t){t===``?(s(),c(),n.write(` ${C(M.submit)} ${D(e)}\n`),V(),process.exit(130)):t===`\r`||t===`
7
+ `?l():t===`y`||t===`Y`?(i=!0,l()):t===`n`||t===`N`?(i=!1,l()):(t===`\x1B[C`||t===`\x1B[D`||t===`\x1B[A`||t===`\x1B[B`||t===`h`||t===`l`)&&(i=!i,n.write(`\r\x1b[K${xe(i)}`))}a.on(`data`,u)}):(n.write(` ${C(M.active)} ${e} ${D(t?`Yes`:`No`)}\n`),t)},Se=async(e,t=``)=>{process.stdout.write(` ${C(M.submit)} ${e}${t?` ${D(`(${t})`)}`:``}\n`);let n=te({input:process.stdin,output:process.stdout});try{return(await n.question(` ${E(M.bar)} `)).trim()||t}finally{n.close()}},Ce=`pglaunch@5.5.7`,we=async()=>I(`docker`,[`info`]),Te=t=>{let n=h(t,`.env`);if(!e(n)){let r=h(t,`.env.example`);d(n,e(r)?l(r):``)}return n},Ee=(e,t)=>{let n=`${t}=`,r=l(e).split(`
8
+ `).find(e=>e.startsWith(n));return r?r.slice(n.length).trim():``},De=(e,t,n)=>{let r=`${t}=`,i=l(e).split(`
9
+ `),a=i.findIndex(e=>e.startsWith(r));if(a>=0)i[a]=`${r}${n}`;else{for(;i.length&&i[i.length-1]===``;)i.pop();i.push(`${r}${n}`,``)}d(e,i.join(`
10
+ `))},Oe=e=>{let t=Te(e);Ee(t,`BETTER_AUTH_SECRET`)||De(t,`BETTER_AUTH_SECRET`,ne(32).toString(`base64`))},q=t=>{let n=h(t,`.env`);return e(n)&&Ee(n,`POSTGRES_URL`)!==``},ke=e=>{let t=e.match(/postgres(?:ql)?:\/\/[\w.:@\-/%?=&]+/);if(!t)return null;let n=e.match(/name "([^" ]+) :\d+" started/),r=e.match(/similar name "([^"]+)"/);return{url:t[0],container:n?n[1]:r?r[1]:``}},Ae=async(e,t)=>{let n=t?[`--bun`,Ce,`-k`,`-c`]:[`--bun`,Ce,`-k`],r;try{r=await F(`bunx`,n,e)}catch(e){let t=e;r=[t.stdout,t.stderr].filter(Boolean).join(`
11
+ `)}return{launch:ke(r),out:r}},je=e=>new Promise(t=>setTimeout(t,e)),Me=async e=>{if(e)for(let t=0;t<30;t++){if(await I(`docker`,[`exec`,e,`pg_isready`,`-U`,`postgres`]))return;await je(1e3)}},Ne=async e=>{let t=Te(e),n=await Ae(e,!1),r=n.launch?n:await Ae(e,!0);if(!r.launch)throw Error(r.out.trim()||`pglaunch did not print a connection URL`);De(t,`POSTGRES_URL`,r.launch.url),await Me(r.launch.container),await ge(`bun`,[`run`,`db:migrate`],{cwd:e,label:`Provisioning a local Postgres database`,done:`Provisioned a local Postgres database`})},J=async e=>{await ge(`bun`,[`install`],{cwd:e,label:`Installing dependencies`,done:`Installed dependencies`})},Pe=async()=>{if(!await I(`bun`,[`--version`]))throw Error(`bun is not on PATH`)},Fe=async(e=Pe,t=!!process.versions.bun,n=process.env.npm_config_user_agent||``)=>{if(t||n.startsWith(`bun/`))return!0;try{return await e(),!0}catch{return!1}},Ie=async(e,t=`main`)=>{await F(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},Le=async(e,t=`main`)=>{await F(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e,`-o`])},Re=async(e=`main`)=>{let t=`https://raw.githubusercontent.com/nrjdalal/zerostarter/${e}/.gitpickignore`,n=await fetch(t);if(!n.ok)throw Error(`Could not fetch .gitpickignore from ${e} (HTTP ${n.status}).`);return n.text()},ze=async e=>(await F(`git`,[`status`,`--porcelain`],e)).trim()===``,Be=async(e,t)=>{for(let n of t)try{await F(`git`,[`checkout`,`--`,n],e),await F(`git`,[`clean`,`-fd`,`--`,n],e)}catch{}},Ve=async e=>{await F(`git`,[`reset`,`--hard`,`HEAD`],e),await F(`git`,[`clean`,`-fd`],e)},He=async(t,n,r)=>{if(!e(h(t,`.git`)))throw Error(n);if(!await ze(t))throw Error(r)},Ue=async(e,t,n)=>{try{await n()}catch(n){throw await Ve(e),console.log(t),n}},We=async e=>{await F(`git`,[`init`,`-q`,`-b`,`canary`],e)},Ge=async(e,t)=>{await F(`git`,[`branch`,t],e)},Y=async(e,t)=>{await F(`git`,[`add`,`-A`],e);try{await F(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},X=(e,t)=>{try{return re(t)}catch(t){process.stderr.write(`${O(t instanceof Error?t.message:String(t))}\n\n`),process.stdout.write(`${e}\n`),process.exit(1)}},Ke=(e=process.env.npm_config_user_agent||``)=>e.startsWith(`bun/`)?`bunx`:e.startsWith(`pnpm/`)?`pnpm dlx`:e.startsWith(`yarn/`)?`yarn dlx`:e.startsWith(`npm/`)?`npx`:`unknown`,qe=(e=process.platform)=>e===`win32`?{cmd:`powershell`,args:[`-Command`,`irm bun.sh/install.ps1 | iex`]}:{cmd:`bash`,args:[`-c`,`curl -fsSL https://bun.sh/install | bash`]},Je=(e=process.argv.slice(2))=>[`zerostarter`,...e].join(` `),Ye=()=>h(process.env.BUN_INSTALL||h(ie(),`.bun`),`bin`),Z=async(e=!1)=>{if(await Fe())return;let t=Ke(),n=`bunx --bun ${Je()}`;if(console.error(O(`Seems like you are using ${t===`unknown`?`npx/pnpx`:t} or Windows, try using:`)),console.error(k(` ${n}`)),console.error(``),e||L()&&await K(`Or install bun now and continue?`,!0)){let{cmd:e,args:t}=qe();console.error(`Installing bun from https://bun.sh/install ...`);try{if(await de(e,t),process.env.PATH=`${Ye()}${p}${process.env.PATH||``}`,await Fe())return;console.error(O(`
12
+ bun is installed but isn't on this shell's PATH yet. Open a new terminal and re-run:`)),console.error(k(` ${n}`))}catch{console.error(O(`
13
+ Could not install bun. Install it from https://bun.sh, then re-run:`)),console.error(k(` ${n}`))}}process.exit(1)},Xe=`Usage:
14
14
  $ bunx zerostarter init [dir] [options]
15
15
 
16
16
  Scaffold ZeroStarter into dir (default .) as a fresh product. The author's
@@ -21,12 +21,13 @@ latest ZeroStarter is fetched into it first.
21
21
 
22
22
  Options:
23
23
  -y, --yes Skip prompts, taking defaults (provisions Postgres when Docker is running)
24
+ --canary Scaffold from the canary branch instead of main (for testing)
24
25
  --db Provision a local Postgres (pglaunch) and migrate; needs Docker
25
26
  --dry-run Print the plan without writing anything
26
- -h, --help Display help`,Qe=e=>!v(e)||y(e).filter(e=>e!==`.git`).length===0,Z=e=>s(h(e,`packages/config/src/site.ts`)),$e=e=>{let t=e,{root:n}=g(t);for(;;){if(v(h(t,`bun.lock`))||v(h(t,`bun.lockb`)))return t;let e=h(t,`package.json`);if(v(e))try{if(JSON.parse(ee(e,`utf8`)).workspaces)return t}catch{}if(t===n)return null;t=m(t)}},et=async e=>{let{positionals:t,values:n}=Y(Ze,{allowPositionals:!0,args:e,options:{db:{type:`boolean`},"dry-run":{type:`boolean`},help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(n.help){console.log(Ze);return}n[`dry-run`]||await X(!!n.yes);let r=I()&&!n.yes,i=t[0]??`.`,a=_(i),o=Z(a),c=!o&&!Qe(a);if(!o&&!n[`dry-run`]){let e=$e(c?process.cwd():m(a));if(e)throw Error(`Cannot scaffold inside an existing project (a workspace was found at ${e}). Run it in a fresh directory outside that project.`)}if(n[`dry-run`]||R(L(`https://zerostarter.dev`)),c){if(!r)throw Error(`Directory is not empty. Run it in an empty directory, or pass a project name: bunx zerostarter init <name>`);let e=await Ce(`What should we name your project?`);if(!e)throw Error(`No directory name provided.`);i=e}let l=_(i),d=f(l),p={name:d};if(n[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${l}`),console.log(` name: ${d}`),console.log(` mode: ${Z(l)?`in place`:`fetch first`}`);return}if(o&&r&&!await G(`Convert ${d} in place? This rewrites files and commits.`,!1)){B(`Aborted`);return}Z(l)||await xe(`Fetching the latest ZeroStarter`,`Fetched the latest ZeroStarter`,()=>Le(l)),s(h(l,`.git`))||(await Ge(l),await J(l,`ci(init): scaffold from zerostarter`),await Ke(l,`main`)),await xe(`Rebranding to ${d}`,`Rebranded to ${d}`,()=>u(l,p)),await q(l),await J(l,`ci(init): re-baseline as ${d}`),ke(l);let g=!1,v=await Te(),ee=K(l),y=!1;if(ee?n.db&&W(`--db ignored: POSTGRES_URL is already set in .env.`):y=n.db?!0:r?await G(`Provision a local Postgres database?`,v):v,y&&v)try{await Pe(l),g=!0}catch(e){if(K(l))W(`Postgres is provisioned, but the migration failed; run bun run db:migrate.`);else{let t=e instanceof Error?e.message.trim():String(e);W(`Database setup failed; set POSTGRES_URL in .env yourself.`,t?t.split(`
27
- `).filter(e=>e.trim()).slice(-5):[])}}else y&&W(`Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`);H([`${O(`packages/config/src/site.ts`)} and ${O(`web/next/content`)}`,`to manage branding and blogs & docs respectively`].join(`
28
- `),`Edit and make it yours`),be(`${d} is ready`);let b=[];l!==process.cwd()&&b.push(O(`cd ${i}`)),K(l)||b.push(`set POSTGRES_URL in .env`),g||b.push(O(`bun run db:migrate`)),b.push(O(`bun run dev`)),H(b.join(`
29
- `),`Next steps`),z(`Learn more ${L(`https://zerostarter.dev/docs`)}`)},tt=`Usage:
27
+ -h, --help Display help`,Ze=e=>!v(e)||ee(e).filter(e=>e!==`.git`).length===0,Q=t=>e(h(t,`packages/config/src/site.ts`)),Qe=e=>{let t=e,{root:n}=g(t);for(;;){if(v(h(t,`bun.lock`))||v(h(t,`bun.lockb`)))return t;let e=h(t,`package.json`);if(v(e))try{if(JSON.parse(y(e,`utf8`)).workspaces)return t}catch{}if(t===n)return null;t=m(t)}},$e=async t=>{let{positionals:n,values:r}=X(Xe,{allowPositionals:!0,args:t,options:{canary:{type:`boolean`},db:{type:`boolean`},"dry-run":{type:`boolean`},help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(r.help){console.log(Xe);return}r[`dry-run`]||await Z(!!r.yes);let i=L()&&!r.yes,a=r.canary?`canary`:`main`,o=n[0]??`.`,s=_(o),c=Q(s),l=!c&&!Ze(s);if(!c&&!r[`dry-run`]){let e=Qe(l?process.cwd():m(s));if(e)throw Error(`Cannot scaffold inside an existing project (a workspace was found at ${e}). Run it in a fresh directory outside that project.`)}if(r[`dry-run`]||z(R(`https://zerostarter.dev`)),l){if(!i)throw Error(`Directory is not empty. Run it in an empty directory, or pass a project name: bunx zerostarter init <name>`);let e=await Se(`What should we name your project?`);if(!e)throw Error(`No directory name provided.`);o=e}let d=_(o),p=f(d),g={name:p};if(r[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${d}`),console.log(` name: ${p}`),console.log(` mode: ${Q(d)?`in place`:`fetch ${a}`}`);return}if(c&&i&&!await K(`Convert ${p} in place? This rewrites files and commits.`,!1)){V(`Aborted`);return}if(Q(d))r.canary&&G(`--canary ignored: converting the existing checkout in place, so nothing is fetched.`);else{let e=r.canary?` (canary)`:``;await be(`Fetching the latest ZeroStarter${e}`,`Fetched the latest ZeroStarter${e}`,()=>Ie(d,a))}e(h(d,`.git`))||(await We(d),await Y(d,`ci(init): scaffold from zerostarter`),await Ge(d,`main`)),await be(`Rebranding to ${p}`,`Rebranded to ${p}`,()=>u(d,g)),await J(d),await Y(d,`ci(init): re-baseline as ${p}`),Oe(d);let v=!1,y=await we(),ee=q(d),b=!1;if(ee?r.db&&G(`--db ignored: POSTGRES_URL is already set in .env.`):b=r.db?!0:i?await K(`Provision a local Postgres database?`,y):y,b&&y)try{await Ne(d),v=!0}catch(e){if(q(d))G(`Postgres is provisioned, but the migration failed; run bun run db:migrate.`);else{let t=e instanceof Error?e.message.trim():String(e);G(`Database setup failed; set POSTGRES_URL in .env yourself.`,t?t.split(`
28
+ `).filter(e=>e.trim()).slice(-5):[])}}else b&&G(`Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`);U([`${k(`packages/config/src/site.ts`)} and ${k(`web/next/content`)}`,`to manage branding and blogs & docs respectively`].join(`
29
+ `),`Edit and make it yours`),ye(`${p} is ready`);let x=[];d!==process.cwd()&&x.push(k(`cd ${o}`)),q(d)||x.push(`set POSTGRES_URL in .env`),v||x.push(k(`bun run db:migrate`)),x.push(k(`bun run dev`)),U(x.join(`
30
+ `),`Next steps`),B(`Learn more ${R(`https://zerostarter.dev/docs`)}`)},et=`Usage:
30
31
  $ bunx zerostarter reinit [dir] [options]
31
32
 
32
33
  Re-scaffold an existing git repo (default .) as a fresh ZeroStarter: delete every
@@ -36,8 +37,8 @@ branch; push when ready.
36
37
 
37
38
  Options:
38
39
  -y, --yes Skip the confirmation prompt
39
- -h, --help Display help`,nt=async t=>{let{positionals:n,values:r}=Y(tt,{allowPositionals:!0,args:t,options:{help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(r.help){console.log(tt);return}await X(!!r.yes);let i=_(n[0]??`.`),a=f(i),o=I()&&!r.yes;if(await Ue(i,`No git repository in ${i}. reinit re-scaffolds an existing repo; use init for a new project.`,`Working tree has uncommitted changes. Commit or stash them first; reinit deletes every tracked file.`),R(L(`https://zerostarter.dev`)),o&&!await G(`Delete every file in ${a} (keeping .git and .env*) and re-scaffold?`,!1)){B(`Aborted`);return}await We(i,w(`reinit failed; restored your committed files (deleted gitignored files, except .env*, are gone).`),async()=>{e(i),await Re(i),U(`Fetched the latest ZeroStarter (kept .git and .env*)`),u(i,{name:a}),U(`Rebranded to ${a}`),await q(i),ke(i),await J(i,`ci(reinit): re-baseline as ${a}`)});let s=[];K(i)||s.push(`set POSTGRES_URL in .env`),s.push(O(`bun run db:migrate`)),s.push(O(`bun run dev`)),s.push(O(`git push`)),H(s.join(`
40
- `),`Next steps`),z(C(`${a} re-scaffolded; .git history, remote, and .env* files are intact`))},rt=`Usage:
40
+ -h, --help Display help`,tt=async e=>{let{positionals:t,values:n}=X(et,{allowPositionals:!0,args:e,options:{help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(n.help){console.log(et);return}await Z(!!n.yes);let r=_(t[0]??`.`),a=f(r),o=L()&&!n.yes;if(await He(r,`No git repository in ${r}. reinit re-scaffolds an existing repo; use init for a new project.`,`Working tree has uncommitted changes. Commit or stash them first; reinit deletes every tracked file.`),z(R(`https://zerostarter.dev`)),o&&!await K(`Delete every file in ${a} (keeping .git and .env*) and re-scaffold?`,!1)){V(`Aborted`);return}await Ue(r,T(`reinit failed; restored your committed files (deleted gitignored files, except .env*, are gone).`),async()=>{i(r),await Le(r),W(`Fetched the latest ZeroStarter (kept .git and .env*)`),u(r,{name:a}),W(`Rebranded to ${a}`),await J(r),Oe(r),await Y(r,`ci(reinit): re-baseline as ${a}`)});let s=[];q(r)||s.push(`set POSTGRES_URL in .env`),s.push(k(`bun run db:migrate`)),s.push(k(`bun run dev`)),s.push(k(`git push`)),U(s.join(`
41
+ `),`Next steps`),B(w(`${a} re-scaffolded; .git history, remote, and .env* files are intact`))},nt=`Usage:
41
42
  $ bunx zerostarter sync [dir] [options]
42
43
 
43
44
  Re-baseline an existing fork (default .) on the latest ZeroStarter: a gitpick overlay
@@ -46,8 +47,8 @@ identity, and favicon are preserved. Requires a clean tree; lands as a reviewabl
46
47
  you commit yourself.
47
48
 
48
49
  Options:
49
- -h, --help Display help`,it=async e=>{let{positionals:t,values:n}=Y(rt,{allowPositionals:!0,args:e,options:{help:{short:`h`,type:`boolean`}}});if(n.help){console.log(rt);return}await X();let u=_(t[0]??`.`);await Ue(u,`No git repository in ${u}. Run sync inside an existing fork.`,`Working tree has uncommitted changes. Commit or stash them first so the sync lands as a reviewable diff.`),R(L(`https://zerostarter.dev`));let f=h(u,`package.json`),p=new Map(l(u).map(e=>[e,a(e)])),m=i(await ze());m.length===0&&W(`No PRESERVE_ON_SYNC directive found; fork-owned files may be overwritten.`),await We(u,w(`Sync failed; rolled the working tree back to your last commit.`),async()=>{await Re(u),o(u),d(h(u,`.gitpickignore`));for(let[e,t]of p)s(e)&&r(e,c(t,a(e),e===f));await Ve(u,m)}),U(`Overlaid the latest ZeroStarter (content, public/marketing, and branding preserved)`),await q(u),H([`Starter files were updated (edits overwritten); your content, public/marketing, and branding were preserved.`,w(`Review the diff and commit: git -C ${u} status`)].join(`
50
- `),`Review the changes`),z(O(`Synced to the latest ZeroStarter`))},{author:Q,name:$,version:at}=ve,ot=`Version:
50
+ -h, --help Display help`,rt=async i=>{let{positionals:l,values:u}=X(nt,{allowPositionals:!0,args:i,options:{help:{short:`h`,type:`boolean`}}});if(u.help){console.log(nt);return}await Z();let d=_(l[0]??`.`);await He(d,`No git repository in ${d}. Run sync inside an existing fork.`,`Working tree has uncommitted changes. Commit or stash them first so the sync lands as a reviewable diff.`),z(R(`https://zerostarter.dev`));let f=h(d,`package.json`),p=new Map(s(d).map(e=>[e,t(e)])),m=r(await Re()).preserve;m.length===0&&G(`No PRESERVE_ON_SYNC directive found; fork-owned files may be overwritten.`),await Ue(d,T(`Sync failed; rolled the working tree back to your last commit.`),async()=>{await Le(d),o(d),a(h(d,`.gitpickignore`));for(let[r,i]of p)e(r)&&n(r,c(i,t(r),r===f));await Be(d,m)}),W(`Overlaid the latest ZeroStarter (content, public/marketing, and branding preserved)`),await J(d),U([`Starter files were updated (edits overwritten); your content, public/marketing, and branding were preserved.`,T(`Review the diff and commit: git -C ${d} status`)].join(`
51
+ `),`Review the changes`),B(k(`Synced to the latest ZeroStarter`))},{author:it,name:$,version:at}=_e,ot=`Version:
51
52
  ${$}@${at}
52
53
 
53
54
  Go from zero to a production-ready SaaS, rebranded and ready to ship.
@@ -66,4 +67,4 @@ Options:
66
67
  -h, --help Display help
67
68
 
68
69
  Author:
69
- ${Q.name} <${Q.email}> (${ye(Q.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await et(n);case`reinit`:return await nt(n);case`sync`:return await it(n);case void 0:case`-h`:case`--help`:console.log(ot);return;case`-v`:case`--version`:console.log(`${$}@${at}`);return;default:console.error(D(`Unknown command: ${t}\n`)),console.log(ot),process.exit(1)}}catch(e){ge(e),process.exit(1)}})();export{};
70
+ ${it.name} <${it.email}> (${ve(it.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await $e(n);case`reinit`:return await tt(n);case`sync`:return await rt(n);case void 0:case`-h`:case`--help`:console.log(ot);return;case`-v`:case`--version`:console.log(`${$}@${at}`);return;default:console.error(O(`Unknown command: ${t}\n`)),console.log(ot),process.exit(1)}}catch(e){he(e),process.exit(1)}})();export{};
@@ -0,0 +1,103 @@
1
+ import{dirname as e,join as t}from"node:path";import{existsSync as n,mkdirSync as r,readFileSync as i,readdirSync as a,rmSync as o,writeFileSync as s}from"node:fs";const c=e=>{let t=[],n=[],r=!1;for(let i of e.split(`
2
+ `)){let e=i.trim();if(e){if(!r&&e.startsWith(`# PRESERVE_ON_SYNC`)){n=e.slice(18).replace(/^\s*-\s*/,``).split(`,`).map(e=>e.trim()).filter(Boolean),r=!0;continue}e.startsWith(`#`)||t.push(e)}}return{excludes:t,preserve:n}},l=e=>n(e),u=e=>i(e,`utf8`),d=(t,n)=>{r(e(t),{recursive:!0}),s(t,n)},f=e=>{o(e,{force:!0,recursive:!0})},p=new Set([`node_modules`,`.next`,`.turbo`,`dist`]),m=(e,n=!0)=>{for(let r of a(e,{withFileTypes:!0})){let i=t(e,r.name);if(r.name===`.git`){if(n)continue;o(i,{force:!0,recursive:!0});continue}r.isDirectory()?p.has(r.name)?o(i,{force:!0,recursive:!0}):(m(i,!1),a(i).length===0&&o(i,{force:!0,recursive:!0})):r.name.startsWith(`.env`)||o(i,{force:!0})}},h=e=>{let n=[];for(let r of a(e,{withFileTypes:!0})){if(r.name===`.git`||p.has(r.name))continue;let i=t(e,r.name);r.isDirectory()?n.push(...h(i)):r.name===`package.json`&&n.push(i)}return n},g=e=>JSON.parse(u(e)),_=(e,t)=>{d(e,`${JSON.stringify(t,null,2)}\n`)},v=(e,t)=>{if(!n(e))return!1;let r=u(e),i=r.replace(t,``);return i===r?!1:(s(e,i),!0)},y=[`homepage`,`bugs`,`license`,`author`,`repository`,`funding`],b=[`name`,`version`,...y],x=(e,t)=>{let n={...e,...t};return Object.keys(n).length>0?n:void 0},S=(e,t)=>{let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[];return n.length||r.length?[...new Set([...r,...n])]:void 0},C=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`,`resolutions`,`catalog`,`scripts`,`overrides`],w=(e,t,n)=>{let r={...t};for(let n of Object.keys(e))n in t||(r[n]=e[n]);for(let n of C){let i=x(e[n],t[n]);i&&(r[n]=i)}let i=S(e.workspaces,t.workspaces);if(i&&(r.workspaces=i),n)for(let t of b)t in e?r[t]=e[t]:delete r[t];return r},T=({name:e})=>{let t=e.charAt(0).toUpperCase()+e.slice(1);return`// Brand identity for this app: the single source a fork edits to rebrand. web reads it via lib/config.ts.
3
+ export const site = {
4
+ name: "${t}",
5
+ description: "${t} is just getting started. Tell its story here.",
6
+ tagline: "Your tagline, ready when you are.",
7
+ social: {
8
+ github: "",
9
+ x: "",
10
+ discord: "",
11
+ },
12
+ // Local-only dev agent identity (api/hono agents router).
13
+ agent: {
14
+ name: "LocalAgent",
15
+ email: "agent@local.host",
16
+ },
17
+ // Injectable long-form text blocks. A product sets its own, or leaves them empty.
18
+ apiReferenceDescription: "",
19
+ llmsFullPreamble: "",
20
+ } as const
21
+
22
+ export type Site = typeof site
23
+ `},E=()=>`import { redirect } from "next/navigation"
24
+
25
+ // Fresh fork: redirect to the waitlist until you build your real home page.
26
+ export default function Home() {
27
+ redirect("/waitlist")
28
+ }
29
+ `,D=()=>'# AGENTS.md\n\nGuidance for AI coding agents working in this repository.\n\n## Instructions\n\n- ALWAYS: Use `@/` for imports, and follow the `design` skill for UI and styling conventions.\n- ALWAYS: Keep documentation in sync with every change.\n- NEVER: Include "Co-authored-by" in commit messages.\n- NEVER: Use em-dashes (the long dash, U+2014) in code, comments, docs, or copy. Regular hyphens are fine; for a pause or aside, use a comma, colon, or period.\n\n## Skills\n\nCustom skills live in `.agents/skills` (symlinked to `.claude/skills`). Start with the `codebase-map` skill to orient, then load the task skill that fits (`api-endpoint`, `db-migration`, `dev`, `design`, and more).\n',O=({name:e})=>`# ${e.charAt(0).toUpperCase()+e.slice(1)}
30
+
31
+ Built on top of [ZeroStarter](https://zerostarter.dev).
32
+
33
+ ## Development
34
+
35
+ \`\`\`bash
36
+ bun run dev
37
+ \`\`\`
38
+
39
+ Web runs on http://localhost:3000 and the API on http://localhost:4000.
40
+ `,k=()=>`---
41
+ slug: /docs
42
+ title: Introduction
43
+ description: Documentation.
44
+ ---
45
+
46
+ # Introduction
47
+
48
+ This is your documentation home. Start writing, this page is yours.
49
+ `,A=()=>`---
50
+ slug: /console/docs
51
+ title: Introduction
52
+ description: Internal documentation.
53
+ ---
54
+
55
+ # Introduction
56
+
57
+ Your team's internal docs live here.
58
+ `,j=e=>`---
59
+ title: Blog
60
+ description: Latest articles and updates
61
+ createdAt: ${e}
62
+ ---
63
+
64
+ ## Recent Posts
65
+
66
+ <BlogPostList />
67
+ `,M=e=>`---
68
+ title: Hello World
69
+ description: The first post on your new blog.
70
+ createdAt: ${e}
71
+ publishedAt: ${e}
72
+ ---
73
+
74
+ ## Hello World
75
+
76
+ Your very first post. Replace it with something worth reading.
77
+ `,N=()=>`import type { DocsConfig } from "./src/lib/docs"
78
+
79
+ const docsConfig = {
80
+ docs: {
81
+ "Getting Started": [
82
+ {
83
+ "/docs": {
84
+ title: "Introduction",
85
+ description: "Documentation.",
86
+ },
87
+ },
88
+ ],
89
+ },
90
+ console: {
91
+ "Getting Started": [
92
+ {
93
+ "/console/docs": {
94
+ title: "Introduction",
95
+ description: "Internal documentation.",
96
+ },
97
+ },
98
+ ],
99
+ },
100
+ } satisfies DocsConfig
101
+
102
+ export default docsConfig
103
+ `,P=(e,...n)=>t(e,...n),F=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`app`,I=e=>{let t=P(e,`.gitpickignore`);if(l(t)){for(let n of c(u(t)).excludes){if(/[*?![\]]/.test(n))throw Error(`.gitpickignore entry "${n}" is not a literal path; the in-place converter only supports literal paths (a glob or negation would diverge from gitpick's fetch).`);f(P(e,n))}f(t)}},L=/[ \t]*\{[^}\r\n]*href:[ \t]*["']\/hire["'][^}\r\n]*\},?[ \t]*\r?\n/,R=(e,t)=>{let n=new Date,r=n.toISOString().slice(0,10),i=[n.getFullYear(),n.getMonth()+1,n.getDate()].map(e=>String(e).padStart(2,`0`)).join(`-`),a=i<r?i:r;d(P(e,`web/next/content/docs/index.mdx`),k()),d(P(e,`web/next/content/blog/index.mdx`),j(a)),d(P(e,`web/next/content/blog/hello-world.mdx`),M(a)),d(P(e,`web/next/content/console/docs/index.mdx`),A()),d(P(e,`web/next/docs.config.ts`),N()),d(P(e,`web/next/public/.gitkeep`),``),d(P(e,`web/next/src/app/page.tsx`),E()),d(P(e,`AGENTS.md`),D()),d(P(e,`README.md`),O(t))},z=e=>{let t=P(e,`web/next/src/lib/fonts.ts`),n=P(e,`web/next/src/lib/marketing/fonts.ts`),r=P(e,`web/next/src/components/common/navbar.tsx`);if(v(r,L),l(n))throw Error(`web/next/src/lib/marketing/fonts.ts survived the fork strip (add web/next/src/lib/marketing/ to .gitpickignore).`);if(l(t)&&u(t).includes(`fonts/marketing/`))throw Error(`fonts.ts references fonts/marketing/; author-only marketing fonts must live in the fork-excluded web/next/src/lib/marketing/, not the shared fonts.ts.`);if(l(r)&&/href:\s*["']\/hire["']/.test(u(r)))throw Error(`common/navbar.tsx still has the /hire entry after fixDangling (regex drift). Update packages/cli/src/convert.ts.`)},B=(e,t)=>{d(P(e,`packages/config/src/site.ts`),T(t));let n=P(e,`package.json`),r=g(n);r.name=F(t.name),r.version=`0.0.0`;for(let e of y)delete r[e];_(n,r)},V=(e,t)=>{I(e),R(e,t),z(e),B(e,t)};export{l as a,g as c,_ as d,c as f,m as i,f as l,z as n,h as o,w as r,u as s,V as t,d as u};
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./convert-jDYJ2DZ9.mjs";export{e as convertRepo};
1
+ import{t as e}from"./convert-HXDPuaZU.mjs";export{e as convertRepo};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zerostarter",
3
- "version": "0.0.25",
3
+ "version": "0.0.26-canary.0",
4
4
  "description": "Go from zero to a production-ready SaaS, rebranded and ready to ship.",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,104 +0,0 @@
1
- import{dirname as e,join as t}from"node:path";import{existsSync as n,mkdirSync as r,readFileSync as i,readdirSync as a,rmSync as o,writeFileSync as s}from"node:fs";const c=e=>n(e),l=e=>i(e,`utf8`),u=(t,n)=>{r(e(t),{recursive:!0}),s(t,n)},d=e=>{o(e,{force:!0,recursive:!0})},f=new Set([`node_modules`,`.next`,`.turbo`,`dist`]),p=(e,n=!0)=>{for(let r of a(e,{withFileTypes:!0})){let i=t(e,r.name);if(r.name===`.git`){if(n)continue;o(i,{force:!0,recursive:!0});continue}r.isDirectory()?f.has(r.name)?o(i,{force:!0,recursive:!0}):(p(i,!1),a(i).length===0&&o(i,{force:!0,recursive:!0})):r.name.startsWith(`.env`)||o(i,{force:!0})}},m=e=>{let n=[];for(let r of a(e,{withFileTypes:!0})){if(r.name===`.git`||f.has(r.name))continue;let i=t(e,r.name);r.isDirectory()?n.push(...m(i)):r.name===`package.json`&&n.push(i)}return n},h=e=>JSON.parse(l(e)),g=(e,t)=>{u(e,`${JSON.stringify(t,null,2)}\n`)},_=(e,t)=>{if(!n(e))return!1;let r=l(e),i=r.replace(t,``);return i===r?!1:(s(e,i),!0)},v=[`homepage`,`bugs`,`license`,`author`,`repository`,`funding`],y=[`name`,`version`,...v],b=e=>{let t=e.split(`
2
- `).find(e=>e.trim().startsWith(`# PRESERVE_ON_SYNC`));return t?t.trim().slice(18).replace(/^\s*-\s*/,``).split(`,`).map(e=>e.trim()).filter(Boolean):[]},x=(e,t)=>{let n={...e,...t};return Object.keys(n).length>0?n:void 0},S=(e,t)=>{let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[];return n.length||r.length?[...new Set([...r,...n])]:void 0},C=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`,`resolutions`,`catalog`,`scripts`,`overrides`],w=(e,t,n)=>{let r={...t};for(let n of Object.keys(e))n in t||(r[n]=e[n]);for(let n of C){let i=x(e[n],t[n]);i&&(r[n]=i)}let i=S(e.workspaces,t.workspaces);if(i&&(r.workspaces=i),n)for(let t of y)t in e?r[t]=e[t]:delete r[t];return r},T=({name:e})=>{let t=e.charAt(0).toUpperCase()+e.slice(1);return`// Brand identity for this app: the single source a fork edits to rebrand. web reads it via lib/config.ts.
3
- export const site = {
4
- name: "${t}",
5
- description: "${t} is just getting started. Tell its story here.",
6
- tagline: "Your tagline, ready when you are.",
7
- social: {
8
- github: "",
9
- x: "",
10
- discord: "",
11
- },
12
- // Local-only dev agent identity (api/hono agents router).
13
- agent: {
14
- name: "LocalAgent",
15
- email: "agent@local.host",
16
- },
17
- // Injectable long-form text blocks. A product sets its own, or leaves them empty.
18
- apiReferenceDescription: "",
19
- llmsFullPreamble: "",
20
- } as const
21
-
22
- export type Site = typeof site
23
- `},E=()=>`import { redirect } from "next/navigation"
24
-
25
- // Fresh fork: redirect to the waitlist until you build your real home page.
26
- export default function Home() {
27
- redirect("/waitlist")
28
- }
29
- `,D=()=>'# AGENTS.md\n\nGuidance for AI coding agents working in this repository.\n\n## Instructions\n\n- ALWAYS: Use `@/` for imports, and follow the `design` skill for UI and styling conventions.\n- ALWAYS: Keep documentation in sync with every change.\n- NEVER: Include "Co-authored-by" in commit messages.\n- NEVER: Use em-dashes (the long dash, U+2014) in code, comments, docs, or copy. Regular hyphens are fine; for a pause or aside, use a comma, colon, or period.\n\n## Skills\n\nCustom skills live in `.agents/skills` (symlinked to `.claude/skills`). Start with the `codebase-map` skill to orient, then load the task skill that fits (`api-endpoint`, `db-migration`, `dev`, `design`, and more).\n',O=({name:e})=>`# ${e.charAt(0).toUpperCase()+e.slice(1)}
30
-
31
- Built on top of [ZeroStarter](https://zerostarter.dev).
32
-
33
- ## Development
34
-
35
- \`\`\`bash
36
- bun run dev
37
- \`\`\`
38
-
39
- Web runs on http://localhost:3000 and the API on http://localhost:4000.
40
- `,k=()=>`---
41
- slug: /docs
42
- title: Introduction
43
- description: Documentation.
44
- ---
45
-
46
- # Introduction
47
-
48
- This is your documentation home. Start writing, this page is yours.
49
- `,A=()=>`---
50
- slug: /console/docs
51
- title: Introduction
52
- description: Internal documentation.
53
- ---
54
-
55
- # Introduction
56
-
57
- Your team's internal docs live here.
58
- `,j=e=>`---
59
- title: Blog
60
- description: Latest articles and updates
61
- createdAt: ${e}
62
- ---
63
-
64
- ## Recent Posts
65
-
66
- <BlogPostList />
67
- `,M=e=>`---
68
- title: Hello World
69
- description: The first post on your new blog.
70
- createdAt: ${e}
71
- publishedAt: ${e}
72
- ---
73
-
74
- ## Hello World
75
-
76
- Your very first post. Replace it with something worth reading.
77
- `,N=()=>`import type { DocsConfig } from "./src/lib/docs"
78
-
79
- const docsConfig = {
80
- docs: {
81
- "Getting Started": [
82
- {
83
- "/docs": {
84
- title: "Introduction",
85
- description: "Documentation.",
86
- },
87
- },
88
- ],
89
- },
90
- console: {
91
- "Getting Started": [
92
- {
93
- "/console/docs": {
94
- title: "Introduction",
95
- description: "Internal documentation.",
96
- },
97
- },
98
- ],
99
- },
100
- } satisfies DocsConfig
101
-
102
- export default docsConfig
103
- `,P=(e,...n)=>t(e,...n),F=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`app`,I=e=>{let t=P(e,`.gitpickignore`);if(c(t)){for(let n of l(t).split(`
104
- `)){let t=n.trim();if(!(!t||t.startsWith(`#`))){if(/[*?![\]]/.test(t))throw Error(`.gitpickignore entry "${t}" is not a literal path; the in-place converter only supports literal paths (a glob or negation would diverge from gitpick's fetch).`);d(P(e,t))}}d(t)}},L=/[ \t]*\{[^}\r\n]*href:[ \t]*["']\/hire["'][^}\r\n]*\},?[ \t]*\r?\n/,R=(e,t)=>{let n=new Date,r=n.toISOString().slice(0,10),i=[n.getFullYear(),n.getMonth()+1,n.getDate()].map(e=>String(e).padStart(2,`0`)).join(`-`),a=i<r?i:r;u(P(e,`web/next/content/docs/index.mdx`),k()),u(P(e,`web/next/content/blog/index.mdx`),j(a)),u(P(e,`web/next/content/blog/hello-world.mdx`),M(a)),u(P(e,`web/next/content/console/docs/index.mdx`),A()),u(P(e,`web/next/docs.config.ts`),N()),u(P(e,`web/next/public/.gitkeep`),``),u(P(e,`web/next/src/app/page.tsx`),E()),u(P(e,`AGENTS.md`),D()),u(P(e,`README.md`),O(t))},z=e=>{let t=P(e,`web/next/src/lib/fonts.ts`),n=P(e,`web/next/src/lib/marketing/fonts.ts`),r=P(e,`web/next/src/components/common/navbar.tsx`);if(_(r,L),c(n))throw Error(`web/next/src/lib/marketing/fonts.ts survived the fork strip (add web/next/src/lib/marketing/ to .gitpickignore).`);if(c(t)&&l(t).includes(`fonts/marketing/`))throw Error(`fonts.ts references fonts/marketing/; author-only marketing fonts must live in the fork-excluded web/next/src/lib/marketing/, not the shared fonts.ts.`);if(c(r)&&/href:\s*["']\/hire["']/.test(l(r)))throw Error(`common/navbar.tsx still has the /hire entry after fixDangling (regex drift). Update packages/cli/src/convert.ts.`)},B=(e,t)=>{u(P(e,`packages/config/src/site.ts`),T(t));let n=P(e,`package.json`),r=h(n);r.name=F(t.name),r.version=`0.0.0`;for(let e of v)delete r[e];g(n,r)},V=(e,t)=>{I(e),R(e,t),z(e),B(e,t)};export{p as a,l as c,u as d,g as f,b as i,h as l,z as n,c as o,w as r,m as s,V as t,d as u};