zerostarter 0.0.0-daca3ce → 0.0.0-efd8af0

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.
Files changed (2) hide show
  1. package/dist/bin/index.mjs +17 -17
  2. package/package.json +1 -1
@@ -1,14 +1,15 @@
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-DMU8fiBc.mjs";import{basename as f,delimiter as p,join as m,resolve as h}from"node:path";import{existsSync as g,readdirSync as _}from"node:fs";import{createInterface as v}from"node:readline/promises";import{parseArgs as y}from"node:util";import{randomBytes as b}from"node:crypto";import{spawn as ee}from"node:child_process";import{access as te}from"node:fs/promises";import{homedir as ne}from"node:os";var re={name:`zerostarter`,version:`0.0.0-daca3ce`,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 x=(e,t=process.stdout)=>n=>t.isTTY?`\x1b[${e}m${n}\x1b[0m`:n,S=x(`38;5;208`),ie=x(`38;2;63;185;80`),C=x(`38;2;210;153;34`),w=x(`38;2;56;189;248`),T=x(`38;2;110;118;129`),E=x(`2`),D=x(`38;2;248;81;73`,process.stderr),O={barStart:`┌`,bar:`│`,barEnd:`└`,active:`◆`,submit:`◇`,radioOn:`●`,radioOff:`○`,warn:`▲`,error:`■`},k=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),ae=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,A=e=>{process.stdout.write(`\n${T(O.barStart)} ${e}\n`)},j=e=>{process.stdout.write(`${T(O.bar)}\n${T(O.barEnd)} ${e}\n`)},M=(e,t=[])=>{process.stdout.write(`${T(O.bar)}\n${w(O.submit)} ${e}\n`);for(let e of t)process.stdout.write(`${T(O.bar)} ${e}\n`)},N=(e,t=[])=>{process.stdout.write(`${T(O.bar)}\n${C(O.warn)} ${C(e)}\n`);for(let e of t)process.stdout.write(`${T(O.bar)} ${C(e)}\n`)},oe=e=>{let t=e?`${ie(O.radioOn)} Yes`:E(`${O.radioOff} Yes`),n=e?E(`${O.radioOff} No`):`${ie(O.radioOn)} No`;return`${T(O.bar)} ${t} ${E(`/`)} ${n}`},P=async(e,t=!0)=>{let n=process.stdout;return k()?new Promise(r=>{let i=t,a=process.stdin;n.write(`${T(O.bar)}\n${w(O.active)} ${e}\n`),n.write(oe(i)),a.setRawMode(!0),a.resume(),a.setEncoding(`utf8`);let o=()=>{a.setRawMode(!1),a.pause(),a.removeListener(`data`,c)},s=()=>{o(),n.write(`\r\x1B[K`),n.write(`\x1B[1A\r\x1B[K`),n.write(`${w(O.submit)} ${e} ${E(i?`Yes`:`No`)}\n`),r(i)};function c(e){e===``?(o(),n.write(`
3
- `),process.exit(1)):e===`\r`||e===`
4
- `?s():e===`y`||e===`Y`?(i=!0,s()):e===`n`||e===`N`?(i=!1,s()):(e===`\x1B[C`||e===`\x1B[D`||e===`\x1B[A`||e===`\x1B[B`||e===`h`||e===`l`)&&(i=!i,n.write(`\r\x1b[K${oe(i)}`))}a.on(`data`,c)}):(n.write(`${T(O.bar)}\n${w(O.submit)} ${e} ${E(t?`Yes`:`No`)}\n`),t)},se=async(e,t=``)=>{process.stdout.write(`${T(O.bar)}\n${w(O.active)} ${e}${t?` ${E(`(${t})`)}`:``}\n`);let n=v({input:process.stdin,output:process.stdout});try{return(await n.question(`${T(O.bar)} `)).trim()||t}finally{n.close()}};var ce=class extends Error{constructor(...e){super(...e),this.name=`SubprocessError`,this.stdout=``,this.stderr=``}};const le=[`.exe`,`.com`],F=e=>e.replaceAll(/([()\][%!^"`<>&|;, *?])/g,`^$1`),ue=e=>F(F(`"${e.replaceAll(/(\\*)"/g,`$1$1\\"`).replace(/(\\*)$/,`$1$1`)}"`)),de=async(e,t,n)=>{if(le.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=>le.map(n=>te(`${h(t,e)}${n}`)))),!0}catch{return!1}},fe=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 de(e,i,a)?[F(e),t.map(ue),{...n,shell:!0}]:[e,t,n]},I=async(e,t,n,r)=>{let i=[e,...t].join(` `),[a,o,s]=await fe(e,t,n);s.shell&&o.length>0&&(a=[a,...o].join(` `),o=[]);let c=ee(a,o,s),l=``,u=``;return new Promise((e,t)=>{let n=(e,n,r)=>{let i=new ce(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})})})},L=async(e,t,n)=>{let{stdout:r}=await I(e,t,{cwd:n,stdio:[`ignore`,`pipe`,`pipe`]});return r},R=async(e,t,n)=>{await I(e,t,{cwd:n,stdio:`inherit`})},z=async(e,t)=>{try{return await I(e,t,{stdio:`ignore`}),!0}catch{return!1}},pe=RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),me=e=>e.replace(pe,``),B=e=>e>=1e3?`[${(e/1e3).toFixed(2)}s]`:`[${e.toFixed(2)}ms]`,V=async(e,t,n)=>{let r=Date.now(),i=process.stdout;if(!i.isTTY){i.write(`${T(O.bar)}\n${w(O.active)} ${n.label}\n`),await R(e,t,n.cwd);return}let a=n.lines??3,o=[],s=0,c=``,l=``,u=()=>(i.columns||80)-1,d=e=>{i.write(e?`\x1B[?7h`:`\x1B[?7l`)},f=()=>{s>0&&(i.write(`[${s}A`),s=0)},p=()=>{f();let e=o.slice(-a);for(let t of e)i.write(`${T(O.bar)} ${E(t.slice(0,Math.max(0,u()-3)))}\n`);s=e.length},m=e=>{l+=e,c+=e;let t=c.split(`
5
- `);c=t.pop()??``;for(let e of t)o.push(me(e).replace(/\r/g,``).replace(/\t/g,` `).trimEnd()),p()};i.write(`${T(O.bar)}\n${w(O.active)} ${n.label}\n`),d(!1);try{await I(e,t,{cwd:n.cwd,stdio:[`ignore`,`pipe`,`pipe`]},m)}catch(e){throw f(),d(!0),i.write(l),e}f(),d(!0);let h=n.summarize(l,Date.now()-r).trim();i.write(`\r${w(O.submit)} ${h||n.label}\n`)},H=`pglaunch@5.5.7`,he=async()=>z(`docker`,[`info`]),U=e=>{let r=m(e,`.env`);if(!s(r)){let i=m(e,`.env.example`);n(r,s(i)?t(i):``)}return r},W=(e,n)=>{let r=`${n}=`,i=t(e).split(`
6
- `).find(e=>e.startsWith(r));return i?i.slice(r.length).trim():``},G=(e,r,i)=>{let a=`${r}=`,o=t(e).split(`
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-DMU8fiBc.mjs";import{basename as f,delimiter as p,join as m,resolve as h}from"node:path";import{existsSync as g,readdirSync as _}from"node:fs";import{createInterface as v}from"node:readline/promises";import{parseArgs as y}from"node:util";import{randomBytes as b}from"node:crypto";import{spawn as ee}from"node:child_process";import{access as te}from"node:fs/promises";import{homedir as ne}from"node:os";var re={name:`zerostarter`,version:`0.0.0-efd8af0`,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 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),O=x(`38;5;208`,`39`),k={barStart:`┌`,bar:`│`,barEnd:`└`,active:`◆`,submit:`◇`,radioOn:`●`,radioOff:`○`,warn:`▲`,error:`■`,barH:`─`,cornerTR:`╮`,connectL:`├`,cornerBL:`╰`,cornerBR:`╯`},A=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),ie=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,j=e=>{process.stdout.write(`\n${T(k.barStart)} ${e}\n`)},M=e=>{process.stdout.write(`${T(k.bar)}\n${T(k.barEnd)} ${e}\n`)},N=(e=`Cancelled`)=>{process.stdout.write(`${T(k.bar)}\n${T(k.barEnd)} ${D(e)}\n`)},P=e=>e.replace(RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),``).replace(RegExp(`\x1B\\]8;;.*?\\x07`,`g`),``).length,F=(e,t=``,n=!1)=>{let r=`\n${e}\n`.split(`
3
+ `),i=P(t),a=Math.max(r.reduce((e,t)=>Math.max(e,P(t)),i),i)+2,o=r.map(e=>`${T(k.bar)} ${e}${` `.repeat(a-P(e))}${T(k.bar)}`).join(`
4
+ `),s=n?k.cornerBL:k.connectL;process.stdout.write(`${T(k.bar)}\n${S(k.submit)} ${t} ${T(k.barH.repeat(Math.max(a-i-1,1))+k.cornerTR)}\n${o}\n${T(s+k.barH.repeat(a+2)+k.cornerBR)}\n`)},I=(e,t=[])=>{process.stdout.write(`${T(k.bar)}\n${S(k.submit)} ${e}\n`);for(let e of t)process.stdout.write(`${T(k.bar)} ${e}\n`)},ae=e=>{process.stdout.write(`${T(k.bar)}\n${C(k.submit)} ${C(e)}\n`)},L=(e,t=[])=>{process.stdout.write(`${T(k.bar)}\n${w(k.warn)} ${w(e)}\n`);for(let e of t)process.stdout.write(`${T(k.bar)} ${w(e)}\n`)},oe=e=>{let t=e?`${C(k.radioOn)} Yes`:E(`${k.radioOff} Yes`),n=e?E(`${k.radioOff} No`):`${C(k.radioOn)} No`;return`${T(k.bar)} ${t} ${E(`/`)} ${n}`},R=async(e,t=!0,n=!1)=>{let r=process.stdout;return A()?new Promise(i=>{let a=t,o=process.stdin;r.write(`${T(k.bar)}\n${S(k.active)} ${e}\n`),r.write(oe(a)),o.setRawMode(!0),o.resume(),o.setEncoding(`utf8`);let s=()=>{o.setRawMode(!1),o.pause(),o.removeListener(`data`,l)},c=()=>{s(),r.write(`\r\x1B[K\x1B[1A\r\x1B[K`),n?r.write(`\x1B[1A\r\x1B[K`):r.write(`${S(k.submit)} ${e} ${E(a?`Yes`:`No`)}\n`),i(a)};function l(t){t===``?(s(),r.write(`\r\x1b[K\x1b[1A\r\x1b[K${E(k.submit)} ${E(e)}\n`),N(),process.exit(130)):t===`\r`||t===`
5
+ `?c():t===`y`||t===`Y`?(a=!0,c()):t===`n`||t===`N`?(a=!1,c()):(t===`\x1B[C`||t===`\x1B[D`||t===`\x1B[A`||t===`\x1B[B`||t===`h`||t===`l`)&&(a=!a,r.write(`\r\x1b[K${oe(a)}`))}o.on(`data`,l)}):(n||r.write(`${T(k.bar)}\n${S(k.submit)} ${e} ${E(t?`Yes`:`No`)}\n`),t)},se=async(e,t=``)=>{process.stdout.write(`${T(k.bar)}\n${S(k.active)} ${e}${t?` ${E(`(${t})`)}`:``}\n`);let n=v({input:process.stdin,output:process.stdout});try{return(await n.question(`${T(k.bar)} `)).trim()||t}finally{n.close()}};var ce=class extends Error{constructor(...e){super(...e),this.name=`SubprocessError`,this.stdout=``,this.stderr=``}};const z=[`.exe`,`.com`],B=e=>e.replaceAll(/([()\][%!^"`<>&|;, *?])/g,`^$1`),le=e=>B(B(`"${e.replaceAll(/(\\*)"/g,`$1$1\\"`).replace(/(\\*)$/,`$1$1`)}"`)),ue=async(e,t,n)=>{if(z.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=>z.map(n=>te(`${h(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)?[B(e),t.map(le),{...n,shell:!0}]:[e,t,n]},V=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=ee(a,o,s),l=``,u=``;return new Promise((e,t)=>{let n=(e,n,r)=>{let i=new ce(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})})})},H=async(e,t,n)=>{let{stdout:r}=await V(e,t,{cwd:n,stdio:[`ignore`,`pipe`,`pipe`]});return r},U=async(e,t,n)=>{await V(e,t,{cwd:n,stdio:`inherit`})},W=async(e,t)=>{try{return await V(e,t,{stdio:`ignore`}),!0}catch{return!1}},fe=RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),pe=e=>e.replace(fe,``),G=e=>e>=1e3?`[${(e/1e3).toFixed(2)}s]`:`[${e.toFixed(2)}ms]`,me=async(e,t,n)=>{let r=Date.now(),i=process.stdout,a=!!n.label;if(!i.isTTY){a&&i.write(`${T(k.bar)}\n${S(k.active)} ${n.label}\n`),await U(e,t,n.cwd);return}let o=n.lines??3,s=[],c=0,l=``,u=``,d=()=>(i.columns||80)-1,f=e=>{i.write(e?`\x1B[?7h`:`\x1B[?7l`)},p=()=>{c>0&&(i.write(`[${c}A`),c=0)},m=()=>{p();let e=s.slice(-o);for(let t of e)i.write(`${T(k.bar)} ${E(t.slice(0,Math.max(0,d()-3)))}\n`);c=e.length},h=e=>{u+=e,l+=e;let t=l.split(`
6
+ `);l=t.pop()??``;for(let e of t)s.push(pe(e).replace(/\r/g,``).replace(/\t/g,` `).trimEnd()),m()};i.write(a?`${T(k.bar)}\n${S(k.active)} ${n.label}\n${T(k.bar)}\n`:`${T(k.bar)}\n`),f(!1);try{await V(e,t,{cwd:n.cwd,stdio:[`ignore`,`pipe`,`pipe`]},h)}catch(e){throw p(),f(!0),i.write(u),e}p(),f(!0);let g=n.summarize(u,Date.now()-r).trim();a?(i.write(`\x1B[1A\r\x1B[K`),i.write(`\r${S(k.submit)} ${g||n.label}\n`)):i.write(`${S(k.submit)} ${g}\n`)},he=`pglaunch@5.5.7`,ge=async()=>W(`docker`,[`info`]),_e=e=>{let r=m(e,`.env`);if(!s(r)){let i=m(e,`.env.example`);n(r,s(i)?t(i):``)}return r},ve=(e,n)=>{let r=`${n}=`,i=t(e).split(`
7
+ `).find(e=>e.startsWith(r));return i?i.slice(r.length).trim():``},ye=(e,r,i)=>{let a=`${r}=`,o=t(e).split(`
7
8
  `),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(`
8
- `))},ge=e=>{let t=U(e);W(t,`BETTER_AUTH_SECRET`)||G(t,`BETTER_AUTH_SECRET`,b(32).toString(`base64`))},K=e=>{let t=m(e,`.env`);return s(t)&&W(t,`POSTGRES_URL`)!==``},_e=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]:``}},ve=async(e,t)=>{let n=t?[`--bun`,H,`-k`,`-c`]:[`--bun`,H,`-k`],r;try{r=await L(`bunx`,n,e)}catch(e){let t=e;r=[t.stdout,t.stderr].filter(Boolean).join(`
9
- `)}return _e(r)},ye=e=>new Promise(t=>setTimeout(t,e)),be=async e=>{if(e)for(let t=0;t<30;t++){if(await z(`docker`,[`exec`,e,`pg_isready`,`-U`,`postgres`]))return;await ye(1e3)}},xe=async e=>{let t=U(e),n=await ve(e,!1)||await ve(e,!0);if(!n)throw Error(`pglaunch did not print a connection URL`);G(t,`POSTGRES_URL`,n.url),await be(n.container);let r=Se(e);await V(`bun`,[`run`,`db:migrate`],{cwd:e,label:`Provisioning a local Postgres`,summarize:(e,t)=>e.includes(`migrations applied successfully`)?`${r} migration${r===1?``:`s`} applied ${B(t)}`:`Database migrated ${B(t)}`})},Se=e=>{try{return _(m(e,`packages/db/drizzle`)).filter(e=>e.endsWith(`.sql`)).length}catch{return 0}},q=async e=>{await V(`bun`,[`install`],{cwd:e,label:`Installing dependencies`,summarize:e=>(e.match(/[\d,]+ packages installed[^\n]*/g)??e.match(/Checked [^\n]*install[^\n]*/g))?.at(-1)?.trim()??`Dependencies installed`})},Ce=async()=>{if(!await z(`bun`,[`--version`]))throw Error(`bun is not on PATH`)},we=async(e=Ce,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}},Te=async(e,t=`main`)=>{await L(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},Ee=async(e,t=`main`)=>{await L(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e,`-o`])},De=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()},Oe=async e=>(await L(`git`,[`status`,`--porcelain`],e)).trim()===``,ke=async(e,t)=>{for(let n of t)try{await L(`git`,[`checkout`,`--`,n],e),await L(`git`,[`clean`,`-fd`,`--`,n],e)}catch{}},Ae=async e=>{await L(`git`,[`reset`,`--hard`,`HEAD`],e),await L(`git`,[`clean`,`-fd`],e)},je=async(e,t,n)=>{if(!s(m(e,`.git`)))throw Error(t);if(!await Oe(e))throw Error(n)},Me=async(e,t,n)=>{try{await n()}catch(n){throw await Ae(e),console.log(t),n}},Ne=async e=>{await L(`git`,[`init`,`-q`,`-b`,`canary`],e)},Pe=async(e,t)=>{await L(`git`,[`branch`,t],e)},J=async(e,t)=>{await L(`git`,[`add`,`-A`],e);try{await L(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},Fe=(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`,Ie=(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`]},Le=(e=process.argv.slice(2))=>[`zerostarter`,...e].join(` `),Re=()=>m(process.env.BUN_INSTALL||m(ne(),`.bun`),`bin`),Y=async(e=!1)=>{if(await we())return;let t=Fe(),n=`bunx --bun ${Le()}`;if(console.error(D(`Seems like you are using ${t===`unknown`?`npx/pnpx`:t} or Windows, try using:`)),console.error(S(` ${n}`)),console.error(``),e||k()&&await P(`Or install bun now and continue?`,!0)){let{cmd:e,args:t}=Ie();console.error(`Installing bun from https://bun.sh/install ...`);try{if(await R(e,t),process.env.PATH=`${Re()}${p}${process.env.PATH||``}`,await we())return;console.error(D(`
10
- bun is installed but isn't on this shell's PATH yet. Open a new terminal and re-run:`)),console.error(S(` ${n}`))}catch{console.error(D(`
11
- Could not install bun. Install it from https://bun.sh, then re-run:`)),console.error(S(` ${n}`))}}process.exit(1)},ze=e=>!g(e)||_(e).filter(e=>e!==`.git`).length===0,X=e=>s(m(e,`packages/config/src/site.ts`)),Be=async e=>{let{positionals:t,values:n}=y({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(`Usage:
9
+ `))},be=e=>{let t=_e(e);ve(t,`BETTER_AUTH_SECRET`)||ye(t,`BETTER_AUTH_SECRET`,b(32).toString(`base64`))},K=e=>{let t=m(e,`.env`);return s(t)&&ve(t,`POSTGRES_URL`)!==``},xe=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]:``}},Se=async(e,t)=>{let n=t?[`--bun`,he,`-k`,`-c`]:[`--bun`,he,`-k`],r;try{r=await H(`bunx`,n,e)}catch(e){let t=e;r=[t.stdout,t.stderr].filter(Boolean).join(`
10
+ `)}return xe(r)},Ce=e=>new Promise(t=>setTimeout(t,e)),we=async e=>{if(e)for(let t=0;t<30;t++){if(await W(`docker`,[`exec`,e,`pg_isready`,`-U`,`postgres`]))return;await Ce(1e3)}},Te=async e=>{let t=_e(e),n=await Se(e,!1)||await Se(e,!0);if(!n)throw Error(`pglaunch did not print a connection URL`);ye(t,`POSTGRES_URL`,n.url),await we(n.container);let r=Ee(e);await me(`bun`,[`run`,`db:migrate`],{cwd:e,summarize:(e,t)=>e.includes(`migrations applied successfully`)?`${r} migration${r===1?``:`s`} applied ${G(t)}`:`Database migrated ${G(t)}`})},Ee=e=>{try{return _(m(e,`packages/db/drizzle`)).filter(e=>e.endsWith(`.sql`)).length}catch{return 0}},q=async e=>{await me(`bun`,[`install`],{cwd:e,label:`Installing dependencies`,summarize:e=>(e.match(/[\d,]+ packages installed[^\n]*/g)??e.match(/Checked [^\n]*install[^\n]*/g))?.at(-1)?.trim()??`Dependencies installed`})},De=async()=>{if(!await W(`bun`,[`--version`]))throw Error(`bun is not on PATH`)},Oe=async(e=De,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}},ke=async(e,t=`main`)=>{await H(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},Ae=async(e,t=`main`)=>{await H(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e,`-o`])},je=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()},Me=async e=>(await H(`git`,[`status`,`--porcelain`],e)).trim()===``,Ne=async(e,t)=>{for(let n of t)try{await H(`git`,[`checkout`,`--`,n],e),await H(`git`,[`clean`,`-fd`,`--`,n],e)}catch{}},Pe=async e=>{await H(`git`,[`reset`,`--hard`,`HEAD`],e),await H(`git`,[`clean`,`-fd`],e)},Fe=async(e,t,n)=>{if(!s(m(e,`.git`)))throw Error(t);if(!await Me(e))throw Error(n)},Ie=async(e,t,n)=>{try{await n()}catch(n){throw await Pe(e),console.log(t),n}},Le=async e=>{await H(`git`,[`init`,`-q`,`-b`,`canary`],e)},Re=async(e,t)=>{await H(`git`,[`branch`,t],e)},J=async(e,t)=>{await H(`git`,[`add`,`-A`],e);try{await H(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},ze=(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`,Be=(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`]},Ve=(e=process.argv.slice(2))=>[`zerostarter`,...e].join(` `),He=()=>m(process.env.BUN_INSTALL||m(ne(),`.bun`),`bin`),Y=async(e=!1)=>{if(await Oe())return;let t=ze(),n=`bunx --bun ${Ve()}`;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||A()&&await R(`Or install bun now and continue?`,!0)){let{cmd:e,args:t}=Be();console.error(`Installing bun from https://bun.sh/install ...`);try{if(await U(e,t),process.env.PATH=`${He()}${p}${process.env.PATH||``}`,await Oe())return;console.error(D(`
11
+ 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(`
12
+ Could not install bun. Install it from https://bun.sh, then re-run:`)),console.error(O(` ${n}`))}}process.exit(1)},Ue=e=>!g(e)||_(e).filter(e=>e!==`.git`).length===0,X=e=>s(m(e,`packages/config/src/site.ts`)),We=async e=>{let{positionals:t,values:n}=y({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(`Usage:
12
13
  $ bunx zerostarter init [dir] [options]
13
14
 
14
15
  Scaffold ZeroStarter into dir (default .) as a fresh product. The author's
@@ -21,10 +22,9 @@ Options:
21
22
  -y, --yes Skip prompts, taking defaults (provisions Postgres when Docker is running)
22
23
  --db Provision a local Postgres (pglaunch) and migrate; needs Docker
23
24
  --dry-run Print the plan without writing anything
24
- -h, --help Display help`);return}n[`dry-run`]||await Y(!!n.yes);let r=k()&&!n.yes,i=t[0]??`.`,a=h(i),o=X(a);if(!o&&!ze(a)){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 se(`Directory isn't empty. Project name`);if(!e)throw Error(`No directory name provided.`);i=e}let c=h(i),l=f(c),d={name:l};if(n[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${c}`),console.log(` name: ${l}`),console.log(` mode: ${X(c)?`in place`:`fetch first`}`);return}if(A(w(`ZeroStarter`)),o&&r&&!await P(C(`Convert ${c} in place? This rewrites files and commits.`),!1)){j(`Aborted`);return}X(c)||(await Te(c),M(`Fetched the latest ZeroStarter`)),s(m(c,`.git`))||(await Ne(c),await J(c,`ci(init): scaffold from zerostarter`),await Pe(c,`main`)),u(c,d),M(`Rebranded to ${l}`),await q(c),await J(c,`ci(init): re-baseline as ${l}`),ge(c);let p=!1,g=await he(),_=K(c),v=!1;if(_?n.db&&N(`--db ignored: POSTGRES_URL is already set in .env.`):v=n.db?!0:r?await P(`Provision a local Postgres database now?`,g):g,v&&g)try{await xe(c),p=!0}catch(e){let t=e.stderr,n=t?String(t).trim():e instanceof Error?e.message:String(e);K(c)?N(`Postgres is provisioned, but the migration failed; run bun run db:migrate.`,n?[n]:[]):N(`Database setup failed; set POSTGRES_URL in .env yourself.`,n?[n]:[])}else v&&N(`Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`);let b=[[`packages/config/src/site.ts`,`your brand: name, tagline, links`],[`web/next/content`,`your docs and blog`]];j(w(`${l} is ready`)),console.log(),console.log(`Next steps:`),c!==process.cwd()&&console.log(` ${S(`cd ${i}`)}`),K(c)||console.log(` ${S(`set POSTGRES_URL in .env`)} # your Postgres connection string`),p||console.log(` ${S(`bun run db:migrate`)}`),console.log(` ${S(`bun run dev`)}`),console.log(`
25
- Push to an empty GitHub repo when ready:`),console.log(` ${S(`git push origin canary`)}`),console.log(`
26
- Make it yours:`);for(let[e,t]of b)console.log(` ${e.padEnd(29)} ${t}`);p||console.log(`
27
- It needs a Postgres database to run: a hosted one like Neon works, or a local Docker one (re-run with Docker running to auto-provision). OAuth and other credentials are optional.`)},Ve=async t=>{let{positionals:n,values:r}=y({allowPositionals:!0,args:t,options:{help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(r.help){console.log(`Usage:
25
+ -h, --help Display help`);return}n[`dry-run`]||await Y(!!n.yes);let r=A()&&!n.yes,i=t[0]??`.`,a=h(i),o=X(a);if(!o&&!Ue(a)){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 se(`What should we name your project?`);if(!e)throw Error(`No directory name provided.`);i=e}let c=h(i),l=f(c),d={name:l};if(n[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${c}`),console.log(` name: ${l}`),console.log(` mode: ${X(c)?`in place`:`fetch first`}`);return}if(j(S(`https://zerostarter.dev`)),o&&r&&!await R(w(`Convert ${c} in place? This rewrites files and commits.`),!1)){N(`Aborted`);return}X(c)||(await ke(c),I(`Fetched the latest ZeroStarter`)),s(m(c,`.git`))||(await Le(c),await J(c,`ci(init): scaffold from zerostarter`),await Re(c,`main`)),u(c,d),I(`Rebranded to ${l}`),await q(c),await J(c,`ci(init): re-baseline as ${l}`),be(c);let p=!1,g=await ge(),_=K(c),v=!1;if(_?n.db&&L(`--db ignored: POSTGRES_URL is already set in .env.`):v=n.db?!0:r?await R(`Provision a local Postgres database now?`,g):g,v&&g)try{await Te(c),p=!0}catch(e){let t=e.stderr,n=t?String(t).trim():e instanceof Error?e.message:String(e);K(c)?L(`Postgres is provisioned, but the migration failed; run bun run db:migrate.`,n?[n]:[]):L(`Database setup failed; set POSTGRES_URL in .env yourself.`,n?[n]:[])}else v&&L(`Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`);F([`${O(`packages/config/src/site.ts`)} and ${O(`web/next/content`)}`,`to manage branding and blogs & docs respectively`].join(`
26
+ `),`Edit and make it yours`),ae(`${l} is ready`);let b=[];c!==process.cwd()&&b.push(O(`cd ${i}`)),K(c)||b.push(O(`set POSTGRES_URL in .env`)),p||b.push(O(`bun run db:migrate`)),b.push(O(`bun run dev`)),F(b.join(`
27
+ `),`Next steps`),M(`Learn more ${S(`https://zerostarter.dev/docs`)}`)},Ge=async t=>{let{positionals:n,values:r}=y({allowPositionals:!0,args:t,options:{help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(r.help){console.log(`Usage:
28
28
  $ bunx zerostarter reinit [dir] [options]
29
29
 
30
30
  Re-scaffold an existing git repo (default .) as a fresh ZeroStarter: delete every
@@ -34,7 +34,7 @@ branch; push when ready.
34
34
 
35
35
  Options:
36
36
  -y, --yes Skip the confirmation prompt
37
- -h, --help Display help`);return}await Y(!!r.yes);let i=h(n[0]??`.`),a=f(i),o=k()&&!r.yes;if(await je(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.`),A(w(`ZeroStarter`)),o&&!await P(`Delete every file in ${i} (keeping .git and .env*) and re-scaffold as ${a}?`,!1)){j(`Aborted`);return}await Me(i,C(`reinit failed; restored your committed files (deleted gitignored files, except .env*, are gone).`),async()=>{e(i),await Ee(i),M(`Fetched the latest ZeroStarter (kept .git and .env*)`),u(i,{name:a}),M(`Rebranded to ${a}`),await q(i),ge(i),await J(i,`ci(reinit): re-baseline as ${a}`)}),j(w(`${a} re-scaffolded; .git history, remote, and .env* files are intact`)),console.log(),console.log(`Next steps:`),K(i)||console.log(` ${S(`set POSTGRES_URL in .env`)} # your Postgres connection string`),console.log(` ${S(`bun run db:migrate`)}`),console.log(` ${S(`bun run dev`)}`),console.log(` ${S(`git push`)} # to your existing remote`)},He=async e=>{let{positionals:t,values:n}=y({allowPositionals:!0,args:e,options:{help:{short:`h`,type:`boolean`}}});if(n.help){console.log(`Usage:
37
+ -h, --help Display help`);return}await Y(!!r.yes);let i=h(n[0]??`.`),a=f(i),o=A()&&!r.yes;if(await Fe(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.`),j(S(`https://zerostarter.dev`)),o&&!await R(`Delete every file in ${i} (keeping .git and .env*) and re-scaffold as ${a}?`,!1)){N(`Aborted`);return}await Ie(i,w(`reinit failed; restored your committed files (deleted gitignored files, except .env*, are gone).`),async()=>{e(i),await Ae(i),I(`Fetched the latest ZeroStarter (kept .git and .env*)`),u(i,{name:a}),I(`Rebranded to ${a}`),await q(i),be(i),await J(i,`ci(reinit): re-baseline as ${a}`)}),M(C(`${a} re-scaffolded; .git history, remote, and .env* files are intact`)),console.log(),console.log(`Next steps:`),K(i)||console.log(` ${O(`set POSTGRES_URL in .env`)} # your Postgres connection string`),console.log(` ${O(`bun run db:migrate`)}`),console.log(` ${O(`bun run dev`)}`),console.log(` ${O(`git push`)} # to your existing remote`)},Ke=async e=>{let{positionals:t,values:n}=y({allowPositionals:!0,args:e,options:{help:{short:`h`,type:`boolean`}}});if(n.help){console.log(`Usage:
38
38
  $ bunx zerostarter sync [dir] [options]
39
39
 
40
40
  Re-baseline an existing fork (default .) on the latest ZeroStarter: a gitpick overlay
@@ -43,8 +43,8 @@ identity, and favicon are preserved. Requires a clean tree; lands as a reviewabl
43
43
  you commit yourself.
44
44
 
45
45
  Options:
46
- -h, --help Display help`);return}await Y();let u=h(t[0]??`.`);await je(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.`),A(w(`ZeroStarter`));let f=m(u,`package.json`),p=new Map(l(u).map(e=>[e,a(e)])),g=i(await De());g.length===0&&N(`No PRESERVE_ON_SYNC directive found; fork-owned files may be overwritten.`),await Me(u,C(`Sync failed; rolled the working tree back to your last commit.`),async()=>{await Ee(u),o(u),d(m(u,`.gitpickignore`));for(let[e,t]of p)s(e)&&r(e,c(t,a(e),e===f));await ke(u,g)}),M(`Overlaid the latest ZeroStarter (content, public/marketing, and branding preserved)`),await q(u),j(S(`Synced to the latest ZeroStarter`)),console.log(),console.log(`Starter files were updated (edits to them were overwritten); files you added and your`),console.log(`content, public/marketing, and branding were preserved.`),console.log(C(`Review the diff and commit: git -C ${u} status`))},{author:Z,name:Q,version:Ue}=re,$=`Version:
47
- ${Q}@${Ue}
46
+ -h, --help Display help`);return}await Y();let u=h(t[0]??`.`);await Fe(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.`),j(S(`https://zerostarter.dev`));let f=m(u,`package.json`),p=new Map(l(u).map(e=>[e,a(e)])),g=i(await je());g.length===0&&L(`No PRESERVE_ON_SYNC directive found; fork-owned files may be overwritten.`),await Ie(u,w(`Sync failed; rolled the working tree back to your last commit.`),async()=>{await Ae(u),o(u),d(m(u,`.gitpickignore`));for(let[e,t]of p)s(e)&&r(e,c(t,a(e),e===f));await Ne(u,g)}),I(`Overlaid the latest ZeroStarter (content, public/marketing, and branding preserved)`),await q(u),M(O(`Synced to the latest ZeroStarter`)),console.log(),console.log(`Starter files were updated (edits to them were overwritten); files you added and your`),console.log(`content, public/marketing, and branding were preserved.`),console.log(w(`Review the diff and commit: git -C ${u} status`))},{author:Z,name:Q,version:qe}=re,$=`Version:
47
+ ${Q}@${qe}
48
48
 
49
49
  Go from zero to a production-ready SaaS, rebranded and ready to ship.
50
50
 
@@ -62,4 +62,4 @@ Options:
62
62
  -h, --help Display help
63
63
 
64
64
  Author:
65
- ${Z.name} <${Z.email}> (${ae(Z.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await Be(n);case`reinit`:return await Ve(n);case`sync`:return await He(n);case void 0:case`-h`:case`--help`:console.log($);return;case`-v`:case`--version`:console.log(`${Q}@${Ue}`);return;default:console.error(D(`Unknown command: ${t}\n`)),console.log($),process.exit(1)}}catch(e){console.error(D(e instanceof Error?e.message:String(e))),process.exit(1)}})();export{};
65
+ ${Z.name} <${Z.email}> (${ie(Z.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await We(n);case`reinit`:return await Ge(n);case`sync`:return await Ke(n);case void 0:case`-h`:case`--help`:console.log($);return;case`-v`:case`--version`:console.log(`${Q}@${qe}`);return;default:console.error(D(`Unknown command: ${t}\n`)),console.log($),process.exit(1)}}catch(e){console.error(D(e instanceof Error?e.message:String(e))),process.exit(1)}})();export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zerostarter",
3
- "version": "0.0.0-daca3ce",
3
+ "version": "0.0.0-efd8af0",
4
4
  "description": "Go from zero to a production-ready SaaS, rebranded and ready to ship.",
5
5
  "keywords": [
6
6
  "cli",