generatesaas 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -471,7 +471,7 @@ export default app;
|
|
|
471
471
|
import app from "@repo/api";
|
|
472
472
|
|
|
473
473
|
export default handle(app);
|
|
474
|
-
`}var qr={smtp:[{key:"SMTP_HOST",defaultValue:"localhost"},{key:"SMTP_PORT",defaultValue:"1025"}],ses:[{key:"AMAZON_SES_REGION",comment:"# TODO: Configure Amazon SES credentials (e.g. us-east-1)"},{key:"AMAZON_SES_KEY"},{key:"AMAZON_SES_SECRET"}],resend:[{key:"RESEND_API_KEY",comment:"# TODO: Add your Resend API key"}]},Jr={stripe:[{key:"STRIPE_SECRET_KEY",comment:"# TODO: Add your Stripe keys"},{key:"STRIPE_WEBHOOK_SECRET"}],polar:[{key:"POLAR_ACCESS_TOKEN",comment:"# TODO: Add your Polar keys"},{key:"POLAR_WEBHOOK_SECRET"}]};function He(e,t){return t?e.map(r=>{let i=t[r.key];return i?{...r,defaultValue:i,comment:void 0}:r}):e}function Ye(e,t){for(let r of e)r.comment&&t.push(r.comment),r.defaultValue!==void 0?t.push(`${r.key}=${r.defaultValue}`):t.push(`#${r.key}=`)}function Ot(e){return Array.from(crypto.getRandomValues(new Uint8Array(e))).map(t=>t.toString(16).padStart(2,"0")).join("")}function Dt(e){return e.architecture==="fullstack"?{apiUrl:"http://localhost:
|
|
474
|
+
`}var qr={smtp:[{key:"SMTP_HOST",defaultValue:"localhost"},{key:"SMTP_PORT",defaultValue:"1025"}],ses:[{key:"AMAZON_SES_REGION",comment:"# TODO: Configure Amazon SES credentials (e.g. us-east-1)"},{key:"AMAZON_SES_KEY"},{key:"AMAZON_SES_SECRET"}],resend:[{key:"RESEND_API_KEY",comment:"# TODO: Add your Resend API key"}]},Jr={stripe:[{key:"STRIPE_SECRET_KEY",comment:"# TODO: Add your Stripe keys"},{key:"STRIPE_WEBHOOK_SECRET"}],polar:[{key:"POLAR_ACCESS_TOKEN",comment:"# TODO: Add your Polar keys"},{key:"POLAR_WEBHOOK_SECRET"}]};function He(e,t){return t?e.map(r=>{let i=t[r.key];return i?{...r,defaultValue:i,comment:void 0}:r}):e}function Ye(e,t){for(let r of e)r.comment&&t.push(r.comment),r.defaultValue!==void 0?t.push(`${r.key}=${r.defaultValue}`):t.push(`#${r.key}=`)}function Ot(e){return Array.from(crypto.getRandomValues(new Uint8Array(e))).map(t=>t.toString(16).padStart(2,"0")).join("")}function Dt(e){return e.architecture==="fullstack"?{apiUrl:"http://localhost:3001/api",baseUrl:"http://localhost:3001"}:{apiUrl:`http://localhost:${e.deploymentTarget==="cloudflare"?8787:3010}`,baseUrl:"http://localhost:3001"}}function Wr(e){let{architecture:t,deploymentTarget:r}=e;return t==="fullstack"?r==="vercel"?{frontend:"https://your-app.vercel.app",backend:"https://your-app.vercel.app"}:r==="node"?{frontend:"https://your-app.example.com",backend:"https://your-app.example.com"}:null:r==="vercel"?{frontend:"https://your-app.vercel.app",backend:"https://api-your-app.vercel.app"}:r==="cloudflare"?{frontend:"https://your-app.example.com",backend:"https://your-app.workers.dev"}:{frontend:"https://your-app.example.com",backend:"https://your-app.example.com/api"}}function Xr(e){let t=crypto.randomUUID(),r=Ot(32),i=Ot(32),{apiUrl:n,baseUrl:o}=Dt(e),s=["# API Configuration",`API_URL=${n}`,`BASE_URL=${o}`];s.push("","# Database"),Ye(He(B[e.databaseProvider].envVars,e.credentials),s),s.push("","# Cache"),Ye(He(G[e.cacheProvider].envVars,e.credentials),s),s.push("","# Authentication",`BETTER_AUTH_SECRET=${t}`,"","# Job Queue - Inngest","INNGEST_APP_ID=api",`INNGEST_EVENT_KEY=${r}`,`INNGEST_SIGNING_KEY=${i}`,"INNGEST_BASE_URL=http://127.0.0.1:8288"),e.architecture==="separate"&&(s.push("","# API Port (standalone backend)","API_PORT=3010"),s.push("","# CORS + cross-subdomain cookies (production only \u2014 leave unset for local dev)","# TRUSTED_ORIGINS=https://your-app.example.com","# AUTH_COOKIE_DOMAIN=.example.com"));let a=qr[e.emailProvider];if(a&&(s.push("","# Email"),Ye(He(a,e.credentials),s)),e.paymentProvider!=="none"){let d=Jr[e.paymentProvider];d&&(s.push("","# Payment"),Ye(He(d,e.credentials),s))}return s.push(""),s.join(`
|
|
475
475
|
`)}var Zr={vercel:"vercel"};function Qr(e){let{apiUrl:t}=Dt(e),r=["# API Configuration",`NUXT_PUBLIC_API_URL=${t}`];if(e.architecture==="fullstack"){let n=Zr[e.deploymentTarget];n&&r.push("","# Build",`NITRO_PRESET=${n}`)}let i=Wr(e);return i&&e.architecture==="separate"&&r.push("","# Production (uncomment and replace with your deployed hostnames):",`# NUXT_PUBLIC_API_URL=${i.backend}`),r.push(""),r.join(`
|
|
476
476
|
`)}async function Ct(e){let t=Xr(e),r=Qr(e);if(e.architecture==="fullstack"){let i=r+`
|
|
477
477
|
`+t;await m(`${e.projectDir}/apps/web-nuxt/.env`,i)}else await m(`${e.projectDir}/apps/web-nuxt/.env`,r),await m(`${e.projectDir}/apps/backend/.env`,t)}import{join as Nt,relative as tn}from"path";import{createHash as xt}from"crypto";import{readFile as en}from"fs/promises";async function qe(e){let t=await en(e);return xt("sha256").update(t).digest("hex")}function Lt(e){return xt("sha256").update(e).digest("hex")}var rn=new Set(["data",x]);function nn(e){let t=e.split("/");for(let r of t)if(rt.has(r)||rn.has(r)||r.startsWith(".env")&&!r.includes("example"))return!0;return!1}async function $t(e,t){let i=(await ce(e.projectDir,e.projectDir,nn)).sort(),n=await Promise.all(i.map(async a=>[tn(e.projectDir,a),await qe(a)])),o=Object.fromEntries(n),s={version:e.version,initialVersion:e.version,repo:"Duzbee/GenerateSaaS",frontend:e.frontend,aiTools:e.aiTools,deploymentTarget:e.deploymentTarget,databaseProvider:e.databaseProvider,cacheProvider:e.cacheProvider,revenueSharing:e.revenueSharing,...t&&{licenseToken:t.token,licenseKeyHash:t.keyHash,installId:t.installId}};await m(Nt(e.projectDir,J),JSON.stringify(s,null," ")+`
|
|
@@ -784,7 +784,7 @@ ${n}
|
|
|
784
784
|
`)}import{relative as fn}from"path";async function we(e){let r=(await ce(e,e,fe)).sort(),i=await Promise.all(r.map(async n=>[fn(e,n),await qe(n)]));return Object.fromEntries(i)}import{copyFile as gn,mkdir as yn,rm as hn}from"fs/promises";import{dirname as vn,join as zt,relative as Sn}from"path";import{existsSync as En}from"fs";async function Kt(e,t){let r=zt(t,Be);En(r)&&await hn(r,{recursive:!0,force:!0});let i=await ce(e,e,fe);for(let n of i){let o=Sn(e,n),s=zt(r,o);await yn(vn(s),{recursive:!0}),await gn(n,s)}}import{existsSync as _n}from"fs";import{readFile as Bt,readdir as An}from"fs/promises";import{join as M,dirname as In,resolve as Tn,sep as wn}from"path";import{fileURLToPath as Pn}from"url";var Pe={"claude-code":".claude/skills",cursor:".cursor/skills",codex:".agents/skills","gemini-cli":".gemini/skills",windsurf:".windsurf/skills"},Rn=Object.values(Pe),ot="generatesaas-update",Gt=In(Pn(import.meta.url));function bn(){let e=M(Gt,"skill","content");return _n(e)?e:M(Gt,"content")}function st(e){return!e||e.length===0?Rn:e.map(t=>Pe[t])}async function at(e,t,r,i){let n=st(i);for(let o of n){let s=M(e,o,ot),a=M(s,"scripts"),d=M(s,"references");await Ge(a),await Ge(d),await m(M(s,"SKILL.md"),t.replaceAll("__SKILL_ROOT__",o)),await m(M(d,".gitkeep"),"");for(let[E,v]of Object.entries(r)){let f=Tn(a,E);f.startsWith(a+wn)&&await m(f,v)}}}async function Ht(e,t){let r=bn(),i=await Bt(M(r,"SKILL.md"),"utf-8"),n=M(r,"scripts"),o=await An(n),s={};for(let a of o)a!==".gitkeep"&&(s[a]=await Bt(M(n,a),"utf-8"));await at(e,i,s,t)}import{execFile as kn,execFileSync as On}from"child_process";import{access as Dn}from"fs/promises";import{join as Cn}from"path";import*as F from"@clack/prompts";function Xe(e){try{let t=process.platform==="win32"?"where":"which";return On(t,[e],{stdio:"ignore"}),!0}catch{return!1}}function We(e,t,r,i=3e5){return new Promise((n,o)=>{kn(e,t,{cwd:r,timeout:i},s=>{s?o(s):n()})})}async function Yt(e){if(!Xe("pnpm"))return F.log.warn("pnpm not found. Skipping dependency installation."),F.log.info("Install pnpm: https://pnpm.io/installation"),!1;let t=F.spinner();t.start("Installing dependencies (this may take a minute)...");try{return await We("pnpm",["install"],e),t.stop("Dependencies installed."),!0}catch{return t.stop("Dependency installation failed."),F.log.warn("pnpm install failed. You can run it manually later."),!1}}async function qt(e){try{return await Dn(Cn(e,".git")),F.log.info("Git repository already exists, skipping init."),!0}catch{}if(!Xe("git"))return F.log.warn("git not found. Skipping repository initialization."),!1;let t=F.spinner();t.start("Initializing git repository...");try{return await We("git",["init"],e),await We("git",["add","-A"],e),await We("git",["commit","-m","Initial commit from GenerateSaaS"],e),t.stop("Git repository initialized."),!0}catch{return t.stop("Git initialization failed."),F.log.warn("You can run git init manually later."),!1}}import*as ye from"@clack/prompts";import N from"picocolors";function Jt(e,t){t.dockerComposeGenerated&&!t.dockerAvailable&&ye.log.warn("Docker not found. Install Docker to run local services: https://docs.docker.com/get-docker/");let r=[];if(r.push(`cd ${e.projectDir}`),t.pnpmInstalled||r.push("pnpm install"),t.dockerComposeGenerated){let o=e.dockerServices.map(s=>me[s].label).join(", ");r.push(`pnpm infra ${N.dim(`# ${o}`)}`)}if(r.push(`pnpm dev ${N.dim("# http://localhost:3000")}`),t.skippedCredentials.length>0&&(r.push(""),r.push(N.dim("Fill in remaining TODO values in .env"))),ye.note(r.join(`
|
|
785
785
|
`),N.yellow("Start Development")),t.dockerComposeGenerated){let o=[];o.push(`App ${N.cyan("http://localhost:3000")}`),e.architecture==="separate"&&o.push(`API ${N.cyan("http://localhost:3010")}`),e.dockerServices.includes("mailpit")&&o.push(`Mailpit ${N.cyan("http://localhost:8025")}`),e.dockerServices.includes("inngest")&&o.push(`Inngest ${N.cyan("http://localhost:8288")}`),ye.note(o.join(`
|
|
786
786
|
`),N.yellow("Dev Tools"))}let i=[],n=xn(e);n.length>0&&i.push(`Set in production: ${N.dim(n.join(", "))}`),i.push("pnpm db:push # Run database migrations"),i.push(Ln(e)),ye.note(i.join(`
|
|
787
|
-
`),N.yellow("Deployment"))}function xn(e){let t=["DATABASE_URL","BETTER_AUTH_SECRET"];return e.cacheProvider==="upstash"?t.push("UPSTASH_REDIS_REST_URL","UPSTASH_REDIS_REST_TOKEN"):t.push("REDIS_URL"),e.paymentProvider==="stripe"?t.push("STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"):e.paymentProvider==="polar"&&t.push("POLAR_ACCESS_TOKEN","POLAR_WEBHOOK_SECRET"),e.emailProvider==="ses"?t.push("AMAZON_SES_REGION","AMAZON_SES_KEY","AMAZON_SES_SECRET"):e.emailProvider==="resend"?t.push("RESEND_API_KEY"):t.push("SMTP_HOST","SMTP_PORT"),t}function Ln(e){switch(e.deploymentTarget){case"node":return"Deploy with Docker or your preferred Node.js host";case"cloudflare":return"wrangler deploy # Deploy to Cloudflare Workers";case"vercel":return"vercel deploy # Deploy to Vercel"}}function Xt(e){let t={};if(e.name!==void 0){if(!Fe(e.name))throw new Error(`Invalid project name "${e.name}". Use lowercase letters, numbers, and hyphens only. Must start with a letter.`);t.projectName=e.name}if(e.appName!==void 0){if(!e.appName.trim())throw new Error("App name cannot be empty.");t.appName=e.appName}if(e.location!==void 0?t.projectDir=e.location==="."?process.cwd():e.location:t.projectName!==void 0&&(t.projectDir=`./${t.projectName}`),e.frontend!==void 0&&(t.frontend=e.frontend),e.architecture!==void 0&&(t.architecture=e.architecture),e.payment!==void 0&&(t.paymentProvider=e.payment),e.email!==void 0&&(t.emailProvider=e.email),e.org!==void 0&&(t.multiTenancy=e.org),e.billingScope!==void 0){if(e.org===!1)throw new Error("--billing-scope requires --org to be enabled.");t.billingScope=e.billingScope}if(e.blog!==void 0&&(t.blog=e.blog),e.revenueSharing!==void 0&&(t.revenueSharing=e.revenueSharing),e.docker!==void 0&&(t.dockerServices=Wt(e.docker,je,"docker service")),e.aiTools!==void 0&&(t.aiTools=Wt(e.aiTools,Ue,"AI tool")),e.currency!==void 0){if(!ne.includes(e.currency))throw new Error(`Invalid currency "${e.currency}". Valid values: ${ne.join(", ")}`);t.defaultCurrency=e.currency}if(e.deploy!==void 0){if(!ie.includes(e.deploy))throw new Error(`Invalid deployment target "${e.deploy}". Valid values: ${ie.join(", ")}`);t.deploymentTarget=e.deploy}if(e.database!==void 0){if(!oe.includes(e.database))throw new Error(`Invalid database provider "${e.database}". Valid values: ${oe.join(", ")}`);t.databaseProvider=e.database}if(e.cache!==void 0){if(!se.includes(e.cache))throw new Error(`Invalid cache provider "${e.cache}". Valid values: ${se.join(", ")}`);t.cacheProvider=e.cache}return t}var Z={projectName:"my-saas",frontend:"nuxt",architecture:"fullstack",paymentProvider:"stripe",emailProvider:"smtp",multiTenancy:!1,billingScope:"user",blog:!1,revenueSharing:!1,dockerServices:["postgres","redis","inngest"],aiTools:[],defaultCurrency:"USD",deploymentTarget:"node",databaseProvider:"postgres",cacheProvider:"redis"};function Zt(e){let t=e.projectName??Z.projectName,r=e.projectDir??`./${t}`,i=e.appName??Me(t),n=e.deploymentTarget??Z.deploymentTarget,o=K[n]?.edgeRuntime??!1,s=e.databaseProvider??(o?"neon":Z.databaseProvider),a=e.cacheProvider??(o?"upstash":Z.cacheProvider),d=e.emailProvider??(o?"resend":Z.emailProvider),E=e.dockerServices??(o?Z.dockerServices.filter(f=>f!=="postgres"&&f!=="redis"):Z.dockerServices),v={...Z,...e,projectName:t,appName:i,projectDir:r,deploymentTarget:n,databaseProvider:s,cacheProvider:a,emailProvider:d,dockerServices:E};for(let f of _e){if(v.deploymentTarget!==f.target)continue;let h=v.databaseProvider===f.provider?"database":"cache";if(v.databaseProvider===f.provider||v.cacheProvider===f.provider)throw new Error(`Incompatible: --deploy ${f.target} + --${h} ${f.provider}. ${f.reason}`)}for(let f of xe)if(v.architecture===f.architecture&&v.deploymentTarget===f.target)throw new Error(`Incompatible: --architecture ${f.architecture} + --deploy ${f.target}. ${f.reason}`);return v}function Wt(e,t,r){if(e.trim()==="")return[];let i=e.split(",").map(o=>o.trim()).filter(Boolean),n=i.filter(o=>!t.includes(o));if(n.length>0)throw new Error(`Invalid ${r}(s): ${n.join(", ")}. Valid values: ${t.join(", ")}`);return i}import Mn from"picocolors";function Fn(e){if(e===void 0)return;let t=e.trim().replace(/^v/,"");if(!/^\d+\.\d+\.\d+$/.test(t))throw new Error(`Invalid template version "${e}". Use semver like 1.2.3.`);return t}function tr(e){e.command("init").description("Scaffold a new GenerateSaaS project").option("-n, --name <name>","project name (lowercase, hyphens, starts with letter)").option("--app-name <name>","display name for the app").option("-l, --location <path>","project directory (default: ./{name})").addOption(new Q("--architecture <type>","fullstack or separate").choices([...Le])).addOption(new Q("--payment <provider>","payment provider").choices([...Ne])).addOption(new Q("--email <provider>","email provider").choices([...$e])).option("--org","enable multi-tenancy (organizations)").option("--no-org","disable multi-tenancy").addOption(new Q("--billing-scope <scope>","billing scope (requires --org)").choices([...Ve])).option("--blog","enable blog").option("--no-blog","disable blog").option("--revenue-sharing","enable revenue sharing").option("--no-revenue-sharing","disable revenue sharing").option("--docker <services>","comma-separated: postgres,redis,inngest,mailpit").option("--ai-tools <tools>","comma-separated: claude-code,cursor,codex,gemini-cli,windsurf").addOption(new Q("--currency <code>","default currency for billing").choices([...ne])).addOption(new Q("--deploy <target>","deployment target").choices([...ie])).addOption(new Q("--database <provider>","database provider").choices([...oe])).addOption(new Q("--cache <provider>","cache provider").choices([...se])).option("--template-version <version>","specific template version to scaffold").option("--api-key <key>","API key (skips interactive prompt)").option("-y, --yes","accept defaults for unspecified options (non-interactive)").action(async t=>{await zn(t)})}async function zn(e){let t=performance.now();mt("0.11.
|
|
787
|
+
`),N.yellow("Deployment"))}function xn(e){let t=["DATABASE_URL","BETTER_AUTH_SECRET"];return e.cacheProvider==="upstash"?t.push("UPSTASH_REDIS_REST_URL","UPSTASH_REDIS_REST_TOKEN"):t.push("REDIS_URL"),e.paymentProvider==="stripe"?t.push("STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"):e.paymentProvider==="polar"&&t.push("POLAR_ACCESS_TOKEN","POLAR_WEBHOOK_SECRET"),e.emailProvider==="ses"?t.push("AMAZON_SES_REGION","AMAZON_SES_KEY","AMAZON_SES_SECRET"):e.emailProvider==="resend"?t.push("RESEND_API_KEY"):t.push("SMTP_HOST","SMTP_PORT"),t}function Ln(e){switch(e.deploymentTarget){case"node":return"Deploy with Docker or your preferred Node.js host";case"cloudflare":return"wrangler deploy # Deploy to Cloudflare Workers";case"vercel":return"vercel deploy # Deploy to Vercel"}}function Xt(e){let t={};if(e.name!==void 0){if(!Fe(e.name))throw new Error(`Invalid project name "${e.name}". Use lowercase letters, numbers, and hyphens only. Must start with a letter.`);t.projectName=e.name}if(e.appName!==void 0){if(!e.appName.trim())throw new Error("App name cannot be empty.");t.appName=e.appName}if(e.location!==void 0?t.projectDir=e.location==="."?process.cwd():e.location:t.projectName!==void 0&&(t.projectDir=`./${t.projectName}`),e.frontend!==void 0&&(t.frontend=e.frontend),e.architecture!==void 0&&(t.architecture=e.architecture),e.payment!==void 0&&(t.paymentProvider=e.payment),e.email!==void 0&&(t.emailProvider=e.email),e.org!==void 0&&(t.multiTenancy=e.org),e.billingScope!==void 0){if(e.org===!1)throw new Error("--billing-scope requires --org to be enabled.");t.billingScope=e.billingScope}if(e.blog!==void 0&&(t.blog=e.blog),e.revenueSharing!==void 0&&(t.revenueSharing=e.revenueSharing),e.docker!==void 0&&(t.dockerServices=Wt(e.docker,je,"docker service")),e.aiTools!==void 0&&(t.aiTools=Wt(e.aiTools,Ue,"AI tool")),e.currency!==void 0){if(!ne.includes(e.currency))throw new Error(`Invalid currency "${e.currency}". Valid values: ${ne.join(", ")}`);t.defaultCurrency=e.currency}if(e.deploy!==void 0){if(!ie.includes(e.deploy))throw new Error(`Invalid deployment target "${e.deploy}". Valid values: ${ie.join(", ")}`);t.deploymentTarget=e.deploy}if(e.database!==void 0){if(!oe.includes(e.database))throw new Error(`Invalid database provider "${e.database}". Valid values: ${oe.join(", ")}`);t.databaseProvider=e.database}if(e.cache!==void 0){if(!se.includes(e.cache))throw new Error(`Invalid cache provider "${e.cache}". Valid values: ${se.join(", ")}`);t.cacheProvider=e.cache}return t}var Z={projectName:"my-saas",frontend:"nuxt",architecture:"fullstack",paymentProvider:"stripe",emailProvider:"smtp",multiTenancy:!1,billingScope:"user",blog:!1,revenueSharing:!1,dockerServices:["postgres","redis","inngest"],aiTools:[],defaultCurrency:"USD",deploymentTarget:"node",databaseProvider:"postgres",cacheProvider:"redis"};function Zt(e){let t=e.projectName??Z.projectName,r=e.projectDir??`./${t}`,i=e.appName??Me(t),n=e.deploymentTarget??Z.deploymentTarget,o=K[n]?.edgeRuntime??!1,s=e.databaseProvider??(o?"neon":Z.databaseProvider),a=e.cacheProvider??(o?"upstash":Z.cacheProvider),d=e.emailProvider??(o?"resend":Z.emailProvider),E=e.dockerServices??(o?Z.dockerServices.filter(f=>f!=="postgres"&&f!=="redis"):Z.dockerServices),v={...Z,...e,projectName:t,appName:i,projectDir:r,deploymentTarget:n,databaseProvider:s,cacheProvider:a,emailProvider:d,dockerServices:E};for(let f of _e){if(v.deploymentTarget!==f.target)continue;let h=v.databaseProvider===f.provider?"database":"cache";if(v.databaseProvider===f.provider||v.cacheProvider===f.provider)throw new Error(`Incompatible: --deploy ${f.target} + --${h} ${f.provider}. ${f.reason}`)}for(let f of xe)if(v.architecture===f.architecture&&v.deploymentTarget===f.target)throw new Error(`Incompatible: --architecture ${f.architecture} + --deploy ${f.target}. ${f.reason}`);return v}function Wt(e,t,r){if(e.trim()==="")return[];let i=e.split(",").map(o=>o.trim()).filter(Boolean),n=i.filter(o=>!t.includes(o));if(n.length>0)throw new Error(`Invalid ${r}(s): ${n.join(", ")}. Valid values: ${t.join(", ")}`);return i}import Mn from"picocolors";function Fn(e){if(e===void 0)return;let t=e.trim().replace(/^v/,"");if(!/^\d+\.\d+\.\d+$/.test(t))throw new Error(`Invalid template version "${e}". Use semver like 1.2.3.`);return t}function tr(e){e.command("init").description("Scaffold a new GenerateSaaS project").option("-n, --name <name>","project name (lowercase, hyphens, starts with letter)").option("--app-name <name>","display name for the app").option("-l, --location <path>","project directory (default: ./{name})").addOption(new Q("--architecture <type>","fullstack or separate").choices([...Le])).addOption(new Q("--payment <provider>","payment provider").choices([...Ne])).addOption(new Q("--email <provider>","email provider").choices([...$e])).option("--org","enable multi-tenancy (organizations)").option("--no-org","disable multi-tenancy").addOption(new Q("--billing-scope <scope>","billing scope (requires --org)").choices([...Ve])).option("--blog","enable blog").option("--no-blog","disable blog").option("--revenue-sharing","enable revenue sharing").option("--no-revenue-sharing","disable revenue sharing").option("--docker <services>","comma-separated: postgres,redis,inngest,mailpit").option("--ai-tools <tools>","comma-separated: claude-code,cursor,codex,gemini-cli,windsurf").addOption(new Q("--currency <code>","default currency for billing").choices([...ne])).addOption(new Q("--deploy <target>","deployment target").choices([...ie])).addOption(new Q("--database <provider>","database provider").choices([...oe])).addOption(new Q("--cache <provider>","cache provider").choices([...se])).option("--template-version <version>","specific template version to scaffold").option("--api-key <key>","API key (skips interactive prompt)").option("-y, --yes","accept defaults for unspecified options (non-interactive)").action(async t=>{await zn(t)})}async function zn(e){let t=performance.now();mt("0.11.1");let r,i;try{r=Xt(e),i=Fn(e.templateVersion)}catch(y){_.cancel(T(y)),process.exit(1)}let n=_.spinner(),o;try{o=await ue({apiKey:e.apiKey,prompt:!e.yes})}catch(y){_.cancel(T(y)),process.exit(1)}let s=L(o),a=async()=>{let y=await V(s),I=y.latest,c=i??I;if(i&&!y.versions.some(p=>p.version===c))throw new Error(`Template version "${i}" is not available.`);return{latestVersion:I,selectedVersion:c}};n.start("Verifying access...");let d,E;try{({latestVersion:d,selectedVersion:E}=await a()),n.stop("Access verified."),Y(o)}catch(y){if(n.stop("Access verification failed."),y instanceof b&&y.status===401){e.yes&&(_.cancel("Invalid API key. Cannot prompt in non-interactive mode."),process.exit(1)),_.log.warning("Invalid API key."),o=await q(),s=L(o),n.start("Verifying access...");try{({latestVersion:d,selectedVersion:E}=await a()),n.stop("Access verified."),Y(o)}catch(I){n.stop("Access verification failed."),_.cancel(I instanceof b&&I.status===401?"Invalid API key.":T(I)),process.exit(1)}}else _.cancel(T(y)),process.exit(1)}_.log.success(`Latest version: ${d}`),E!==d&&_.log.success(`Using template version: ${E}`);let v;n.start("Activating license...");try{let y=crypto.randomUUID();v={token:(await At(s,{frontend:"nuxt",version:E,installId:y})).token,keyHash:Lt(o),installId:y},n.stop("License activated.")}catch(y){n.stop("License activation failed."),_.cancel(T(y)),process.exit(1)}let f;e.yes?f=Zt(r):f=await gt(r);let h=Vn(f.projectDir);if(Qt(h)&&Nn(h).length>0)if(e.yes)_.log.info(`Directory ${h} is not empty. Merging (keeping existing files, overwriting conflicts).`);else{let I=await _.select({message:`Directory ${h} is not empty.`,options:[{value:"merge",label:"Merge",hint:"keep existing files, overwrite conflicts"},{value:"overwrite",label:"Overwrite",hint:"delete everything and start fresh"},{value:"cancel",label:"Cancel"}]});(_.isCancel(I)||I==="cancel")&&(_.cancel("Setup cancelled."),process.exit(0)),I==="overwrite"&&$n(h,{recursive:!0,force:!0})}let S={...f,projectDir:h,version:E};n.start("Downloading template...");try{await Ae(s,E,h);let y=await we(h);await m(er(h,Ke),JSON.stringify(y,null," ")+`
|
|
788
788
|
`),await Kt(h,h),await yt(h),n.stop("Template downloaded.")}catch(y){n.stop("Download failed."),_.cancel(T(y)),process.exit(1)}let C;n.start("Generating project files...");try{await Pt(S),await Mt(S),await Ct(S),C=await bt(S),await Ft(S),await kt(S),await Rt(S),await wt(S),await Vt(S),await Ht(h,S.aiTools),await Ut(S),Kn(S),await $t(S,v),n.stop("Project files generated.")}catch(y){n.stop("Generation failed."),_.cancel(T(y)),process.exit(1)}let de=await Yt(h);await qt(h);let ee=Xe("docker"),te=et(S).map(y=>y.key).filter(y=>!S.credentials?.[y]);Jt(S,{pnpmInstalled:de,dockerComposeGenerated:C,dockerAvailable:ee,skippedCredentials:te}),ut(),_.log.info(Mn.dim(`Done in ${((performance.now()-t)/1e3).toFixed(1)}s`))}function Kn(e){let t=er(e.projectDir,"apps/backend/wrangler.toml");if(!Qt(t))return;let r=jn(t,"utf-8");Un(t,r.replaceAll("your-project",e.projectName))}import{existsSync as rr}from"fs";import{readFile as Bn}from"fs/promises";import{join as Re,resolve as Gn}from"path";import*as P from"@clack/prompts";import he from"picocolors";function nr(e){e.command("update").description("Update AI skill files and stage template updates").option("--cwd <path>","project directory (default: current directory)").action(async t=>{let r=Gn(t.cwd??process.cwd()),i=Re(r,J),n;try{n=JSON.parse(await Bn(i,"utf-8"))}catch{P.cancel(".generatesaas/manifest.json not found. Run this from a GenerateSaaS project."),process.exit(1)}let o;try{o=await ue()}catch(d){P.cancel(T(d)),process.exit(1)}let s=L(o),a=P.spinner();try{a.start("Verifying access...");let d;try{d=await V(s)}catch(w){if(w instanceof b&&w.status===401)a.stop("Invalid API key."),o=await q(),s=L(o),a.start("Verifying access..."),d=await V(s);else throw w}a.stop("Access verified."),Y(o),a.start("Fetching latest skill files...");let E=await _t(s,d.latest);await at(r,E.skillMd,E.scripts,n.aiTools);let v=st(n.aiTools);if(a.stop("Skills updated."),P.log.success(`Skill files installed to ${he.cyan(v.length.toString())} locations.`),n.licenseToken)try{let w=await It(s,{currentToken:n.licenseToken,newVersion:n.version});n.licenseToken=w.token,await m(i,JSON.stringify(n,null," ")+`
|
|
789
789
|
`),P.log.success("License refreshed.")}catch{P.log.warn("License refresh skipped.")}if(n.version===d.latest){P.log.info(`Already on the latest version (${n.version}).`);return}let f=Re(r,vt);a.start(`Downloading v${d.latest} template...`),await Ae(s,d.latest,f),a.stop("Template staged.");let h=await Et(s,d.latest);h&&P.note(h,`Changelog v${d.latest}`);let S=Re(r,Ke),C=Re(r,Be),de=!rr(C),ee=!rr(S);if(de){if(a.start("Downloading baseline template (one-time migration)..."),await Ae(s,n.version,C),ee){let w=await we(C);await m(S,JSON.stringify(w,null," ")+`
|
|
790
790
|
`)}a.stop("Baseline template stored.")}else if(ee){a.start("Computing baseline template hashes...");let w=await we(C);await m(S,JSON.stringify(w,null," ")+`
|
|
@@ -798,7 +798,7 @@ ${n}
|
|
|
798
798
|
`,` .route("/license", licenseRoutes)
|
|
799
799
|
`]}];function ri(e){return(e&&e.length>0?e.map(r=>Pe[r]):Object.values(Pe)).map(r=>pe(r,ot))}function pt(e){return Ze(e)?(Qn(e,{recursive:!0}),!0):!1}function ni(e,t){if(!Ze(e))return!1;let r=lt(e,"utf-8"),i=r;for(let n of t)i=i.replace(n,"");return i===r?!1:(pr(e,i,"utf-8"),!0)}function ii(e){let t=pe(e,".gitignore");if(!Ze(t))return!1;let r=lt(t,"utf-8"),i=r.split(`
|
|
800
800
|
`).filter(n=>!n.includes(".generatesaas")).join(`
|
|
801
|
-
`);return i===r?!1:(pr(t,i,"utf-8"),!0)}function lr(e){e.command("eject").description("Remove all GenerateSaaS ties \u2014 manifest, license, heartbeat, skills").action(async()=>{let t=process.cwd(),r=pe(t,J),i;try{i=JSON.parse(lt(r,"utf-8"))}catch{D.cancel("No GenerateSaaS project found in this directory."),process.exit(1)}let n=await D.text({message:'Type "eject" to confirm (this cannot be undone):',validate:a=>{if(a!=="eject")return'Type "eject" to confirm, or press Ctrl+C to cancel.'}});D.isCancel(n)&&(D.cancel("Eject cancelled."),process.exit(0));let o=[],s=[];for(let a of ri(i.aiTools))pt(pe(t,a))&&o.push(a);for(let a of ei)pt(pe(t,a))&&o.push(a);pt(pe(t,x))&&o.push(x+"/");for(let a of ti){let d=pe(t,a.file);ni(d,a.removals)?s.push(a.file):Ze(d)&&D.log.warn(`Could not auto-modify ${a.file} \u2014 manually remove license/heartbeat references.`)}ii(t)&&s.push(".gitignore");for(let a of o)D.log.info(`Deleted ${a}`);for(let a of s)D.log.info(`Modified ${a}`);D.log.success("Ejected successfully. This project is now fully standalone.")})}var le=new oi().name("generatesaas").description("CLI for scaffolding and managing GenerateSaaS projects").version("0.11.
|
|
801
|
+
`);return i===r?!1:(pr(t,i,"utf-8"),!0)}function lr(e){e.command("eject").description("Remove all GenerateSaaS ties \u2014 manifest, license, heartbeat, skills").action(async()=>{let t=process.cwd(),r=pe(t,J),i;try{i=JSON.parse(lt(r,"utf-8"))}catch{D.cancel("No GenerateSaaS project found in this directory."),process.exit(1)}let n=await D.text({message:'Type "eject" to confirm (this cannot be undone):',validate:a=>{if(a!=="eject")return'Type "eject" to confirm, or press Ctrl+C to cancel.'}});D.isCancel(n)&&(D.cancel("Eject cancelled."),process.exit(0));let o=[],s=[];for(let a of ri(i.aiTools))pt(pe(t,a))&&o.push(a);for(let a of ei)pt(pe(t,a))&&o.push(a);pt(pe(t,x))&&o.push(x+"/");for(let a of ti){let d=pe(t,a.file);ni(d,a.removals)?s.push(a.file):Ze(d)&&D.log.warn(`Could not auto-modify ${a.file} \u2014 manually remove license/heartbeat references.`)}ii(t)&&s.push(".gitignore");for(let a of o)D.log.info(`Deleted ${a}`);for(let a of s)D.log.info(`Modified ${a}`);D.log.success("Ejected successfully. This project is now fully standalone.")})}var le=new oi().name("generatesaas").description("CLI for scaffolding and managing GenerateSaaS projects").version("0.11.1").addHelpText("after",`
|
|
802
802
|
Examples:
|
|
803
803
|
$ generatesaas init Interactive setup
|
|
804
804
|
$ generatesaas init -n my-app -y Quick setup with defaults
|