@turbostarter/cli 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,25 +1,36 @@
1
- # Turbostarter CLI
1
+ # TurboStarter CLI
2
2
 
3
- Your TurboStarter assistant for starting new projects, adding plugins and more.
3
+ Official CLI for creating and managing your TurboStarter projects.
4
4
 
5
- ## Installation
5
+ ## Quick start
6
6
 
7
- You can run commands using `npx`:
7
+ Run without installing globally:
8
8
 
9
+ ```bash
10
+ npx turbostarter@latest <command>
11
+ pnpm dlx turbostarter@latest <command>
12
+ bunx turbostarter@latest <command>
9
13
  ```
10
- npx turbostarter <command>
14
+
15
+ Or install globally and run:
16
+
17
+ ```bash
18
+ npm install -g turbostarter
19
+ pnpm install -g turbostarter
20
+ bunx install -g turbostarter
21
+
22
+ turbostarter <command>
11
23
  ```
12
24
 
13
- This ensures that you always run the latest version of the CLI.
25
+ > [!NOTE]
26
+ > Commands that interact with the TurboStarter repository (updating, plugins, etc.) must be launched from the root of the repository, as they will read and write files from the codebase.
14
27
 
15
28
  ## Usage
16
29
 
17
- Running the CLI without any arguments will display the general information about the CLI:
18
-
19
- ```
30
+ ```bash
20
31
  Usage: turbostarter [options] [command]
21
32
 
22
- Your Turbo Assistant for starting new projects, adding plugins and more.
33
+ Your TurboStarter assistant for starting new projects, adding plugins and more.
23
34
 
24
35
  Options:
25
36
  -v, --version display the version number
@@ -27,20 +38,76 @@ Options:
27
38
 
28
39
  Commands:
29
40
  new create a new TurboStarter project
41
+ project manage your TurboStarter project
30
42
  help [command] display help for command
31
43
  ```
32
44
 
33
- ### Creating a new project
45
+ Running `turbostarter --help` shows the full command list.
34
46
 
35
- To create a new TurboStarter project, run the following command:
47
+ ### Creating new project
36
48
 
49
+ Create a new TurboStarter project:
50
+
51
+ ```bash
52
+ npx turbostarter@latest new
37
53
  ```
38
- npx turbostarter new
54
+
55
+ Options:
56
+
57
+ - `-c, --cwd <cwd>`: Working directory where the new project folder is created (defaults to current directory).
58
+
59
+ What it does:
60
+
61
+ - Prompts for project name and app targets (web, mobile, extension).
62
+ - Optionally walks through provider configuration (db, billing, email, storage, analytics, monitoring).
63
+ - Clones the TurboStarter template repository.
64
+ - Configures git remotes.
65
+ - Applies app-specific file modifications.
66
+ - Installs dependencies and formats files.
67
+ - Starts required local services when needed.
68
+
69
+ ### Managing existing project
70
+
71
+ Set of commands for managing your existing TurboStarter project.
72
+
73
+ ### Updating project
74
+
75
+ Update an existing TurboStarter project with the latest upstream changes:
76
+
77
+ ```bash
78
+ npx turbostarter@latest project update
79
+ ```
80
+
81
+ Options:
82
+
83
+ - `-c, --cwd <cwd>`: Path to the TurboStarter project root (defaults to current directory).
84
+
85
+ What it does:
86
+
87
+ - Validates the target folder is a TurboStarter project root.
88
+ - Verifies the git working tree is clean before updating.
89
+ - Ensures `upstream` remote points to `turbostarter/core` (SSH or HTTPS).
90
+ - Fetches upstream and merges `upstream/main` into the current branch.
91
+ - Prints conflicting files with next steps if merge conflicts occur.
92
+
93
+ ## Development
94
+
95
+ From this repository:
96
+
97
+ ```bash
98
+ pnpm install
99
+ pnpm dev
39
100
  ```
40
101
 
41
- The CLI will ask you a few questions about the project (e.g. name, database, billing etc.) and then create a new project in the working directory.
102
+ Useful scripts:
42
103
 
43
- The command will also install all dependencies and run the project setup scripts.
104
+ | Script | Description |
105
+ | -------------------------- | --------------------- |
106
+ | `pnpm build` | Build CLI to `dist/` |
107
+ | `pnpm start` | Run built CLI |
108
+ | `pnpm typecheck` | Run TypeScript checks |
109
+ | `pnpm lint` | Run ESLint |
110
+ | `pnpm format`/`format:fix` | Check/fix formatting |
44
111
 
45
112
  ## Documentation
46
113
 
@@ -48,4 +115,4 @@ Visit [https://turbostarter.dev/docs/web/cli](https://turbostarter.dev/docs/web/
48
115
 
49
116
  ## License
50
117
 
51
- Licensed under the [GNU General Public License v3.0](https://github.com/turbostarter/cli/blob/main/LICENSE).
118
+ Licensed under the [GNU General Public License v3.0](./LICENSE).
package/dist/index.mjs CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import{Command as e}from"commander";import{execa as t}from"execa";import n from"ora";import r,{join as i}from"path";import a from"picocolors";import o from"prompts";import{z as s}from"zod";import c from"lodash";import{promises as l}from"fs";const u={LOCAL:`local`,CLOUD:`cloud`},d={DB:`db`},f={S3:`s3`},p={RESEND:`resend`,SENDGRID:`sendgrid`,POSTMARK:`postmark`,PLUNK:`plunk`,NODEMAILER:`nodemailer`},m={ROOT:`./`,WEB:`./apps/web`,MOBILE:`./apps/mobile`,EXTENSION:`./apps/extension`},h={EXAMPLE:`.env.example`,LOCAL:`.env.local`},g={WEB:`web`,MOBILE:`mobile`,EXTENSION:`extension`},_={[g.WEB]:{STRIPE:`stripe`,LEMON_SQUEEZY:`lemon-squeezy`,POLAR:`polar`},[g.MOBILE]:{REVENUECAT:`revenuecat`,SUPERWALL:`superwall`}},v={[g.WEB]:{GOOGLE_ANALYTICS:`google-analytics`,MIXPANEL:`mixpanel`,OPEN_PANEL:`open-panel`,PLAUSIBLE:`plausible`,POSTHOG:`posthog`,UMAMI:`umami`,VEMETRIC:`vemetric`,VERCEL:`vercel`},[g.MOBILE]:{GOOGLE_ANALYTICS:`google-analytics`,MIXPANEL:`mixpanel`,POSTHOG:`posthog`},[g.EXTENSION]:{GOOGLE_ANALYTICS:`google-analytics`,POSTHOG:`posthog`}},y={[g.WEB]:{SENTRY:`sentry`,POSTHOG:`posthog`},[g.MOBILE]:{SENTRY:`sentry`,POSTHOG:`posthog`},[g.EXTENSION]:{SENTRY:`sentry`,POSTHOG:`posthog`}},b={db:{url:`DATABASE_URL`},billing:{[g.WEB]:{[_[g.WEB].STRIPE]:{secretKey:`STRIPE_SECRET_KEY`,webhookSecret:`STRIPE_WEBHOOK_SECRET`},[_[g.WEB].LEMON_SQUEEZY]:{apiKey:`LEMON_SQUEEZY_API_KEY`,signingSecret:`LEMON_SQUEEZY_SIGNING_SECRET`,storeId:`LEMON_SQUEEZY_STORE_ID`},[_[g.WEB].POLAR]:{accessToken:`POLAR_ACCESS_TOKEN`,webhookSecret:`POLAR_WEBHOOK_SECRET`,organizationSlug:`POLAR_ORGANIZATION_SLUG`}},[g.MOBILE]:{[_[g.MOBILE].REVENUECAT]:{appleApiKey:`EXPO_PUBLIC_REVENUECAT_APPLE_API_KEY`,googleApiKey:`EXPO_PUBLIC_REVENUECAT_GOOGLE_API_KEY`,webhookSecret:`REVENUECAT_WEBHOOK_SECRET`,apiKey:`REVENUECAT_API_KEY`},[_[g.MOBILE].SUPERWALL]:{appleApiKey:`EXPO_PUBLIC_SUPERWALL_APPLE_API_KEY`,googleApiKey:`EXPO_PUBLIC_SUPERWALL_GOOGLE_API_KEY`,webhookSecret:`SUPERWALL_WEBHOOK_SECRET`}}},email:{[p.RESEND]:{apiKey:`RESEND_API_KEY`},[p.SENDGRID]:{apiKey:`SENDGRID_API_KEY`},[p.PLUNK]:{apiKey:`PLUNK_API_KEY`},[p.POSTMARK]:{apiKey:`POSTMARK_API_KEY`},[p.NODEMAILER]:{user:`NODEMAILER_USER`,password:`NODEMAILER_PASSWORD`,host:`NODEMAILER_HOST`,port:`NODEMAILER_PORT`}},storage:{[f.S3]:{region:`S3_REGION`,bucket:`S3_BUCKET`,endpoint:`S3_ENDPOINT`,accessKeyId:`S3_ACCESS_KEY_ID`,secretAccessKey:`S3_SECRET_ACCESS_KEY`}},analytics:{[g.WEB]:{[v[g.WEB].GOOGLE_ANALYTICS]:{measurementId:`NEXT_PUBLIC_GOOGLE_ANALYTICS_MEASUREMENT_ID`,secret:`GOOGLE_ANALYTICS_SECRET`},[v[g.WEB].MIXPANEL]:{token:`NEXT_PUBLIC_MIXPANEL_TOKEN`},[v[g.WEB].OPEN_PANEL]:{clientId:`NEXT_PUBLIC_OPEN_PANEL_CLIENT_ID`,secret:`OPEN_PANEL_SECRET`},[v[g.WEB].PLAUSIBLE]:{domain:`NEXT_PUBLIC_PLAUSIBLE_DOMAIN`,host:`NEXT_PUBLIC_PLAUSIBLE_HOST`},[v[g.WEB].POSTHOG]:{key:`NEXT_PUBLIC_POSTHOG_KEY`,host:`NEXT_PUBLIC_POSTHOG_HOST`},[v[g.WEB].UMAMI]:{host:`NEXT_PUBLIC_UMAMI_HOST`,websiteId:`NEXT_PUBLIC_UMAMI_WEBSITE_ID`,apiHost:`UMAMI_API_HOST`,apiKey:`UMAMI_API_KEY`},[v[g.WEB].VEMETRIC]:{token:`NEXT_PUBLIC_VEMETRIC_PROJECT_TOKEN`},[v[g.WEB].VERCEL]:{}},[g.MOBILE]:{[v[g.MOBILE].GOOGLE_ANALYTICS]:{},[v[g.MOBILE].MIXPANEL]:{token:`EXPO_PUBLIC_MIXPANEL_TOKEN`},[v[g.MOBILE].POSTHOG]:{key:`EXPO_PUBLIC_POSTHOG_KEY`,host:`EXPO_PUBLIC_POSTHOG_HOST`}},[g.EXTENSION]:{[v[g.EXTENSION].GOOGLE_ANALYTICS]:{measurementId:`VITE_GOOGLE_ANALYTICS_MEASUREMENT_ID`,secret:`VITE_GOOGLE_ANALYTICS_SECRET`},[v[g.EXTENSION].POSTHOG]:{key:`VITE_POSTHOG_KEY`,host:`VITE_POSTHOG_HOST`}}},monitoring:{[g.WEB]:{[y[g.WEB].SENTRY]:{dsn:`NEXT_PUBLIC_SENTRY_DSN`},[y[g.WEB].POSTHOG]:{key:`NEXT_PUBLIC_POSTHOG_KEY`,host:`NEXT_PUBLIC_POSTHOG_HOST`}},[g.MOBILE]:{[y[g.MOBILE].SENTRY]:{dsn:`EXPO_PUBLIC_SENTRY_DSN`},[y[g.MOBILE].POSTHOG]:{key:`EXPO_PUBLIC_POSTHOG_KEY`,host:`EXPO_PUBLIC_POSTHOG_HOST`}},[g.EXTENSION]:{[y[g.EXTENSION].SENTRY]:{dsn:`VITE_SENTRY_DSN`},[y[g.EXTENSION].POSTHOG]:{key:`VITE_POSTHOG_KEY`,host:`VITE_POSTHOG_HOST`}}}},ee={[m.ROOT]:[b.db.url],[m.WEB]:[b.email.resend.apiKey,b.email.sendgrid.apiKey,b.email.plunk.apiKey,b.email.postmark.apiKey,b.email.nodemailer.user,b.email.nodemailer.password,b.storage.s3.accessKeyId,b.storage.s3.secretAccessKey,b.billing[g.WEB].stripe.secretKey,b.billing[g.WEB].stripe.webhookSecret,b.billing[g.WEB][`lemon-squeezy`].apiKey,b.billing[g.WEB][`lemon-squeezy`].signingSecret,b.billing[g.WEB][`lemon-squeezy`].storeId,b.billing[g.WEB].polar.accessToken,b.billing[g.WEB].polar.webhookSecret,b.billing[g.WEB].polar.organizationSlug,b.billing[g.MOBILE].revenuecat.webhookSecret,b.billing[g.MOBILE].revenuecat.apiKey,b.billing[g.MOBILE].superwall.webhookSecret,b.analytics[g.WEB][`google-analytics`].measurementId,b.analytics[g.WEB][`google-analytics`].secret,b.analytics[g.WEB].mixpanel.token,b.analytics[g.WEB][`open-panel`].clientId,b.analytics[g.WEB][`open-panel`].secret,b.analytics[g.WEB].plausible.domain,b.analytics[g.WEB].plausible.host,b.analytics[g.WEB].posthog.key,b.analytics[g.WEB].posthog.host,b.analytics[g.WEB].umami.host,b.analytics[g.WEB].umami.websiteId,b.analytics[g.WEB].umami.apiHost,b.analytics[g.WEB].umami.apiKey,b.analytics[g.WEB].vemetric.token,b.monitoring[g.WEB].sentry.dsn,b.monitoring[g.WEB].posthog.key,b.monitoring[g.WEB].posthog.host],[m.MOBILE]:[b.billing[g.MOBILE].revenuecat.appleApiKey,b.billing[g.MOBILE].revenuecat.googleApiKey,b.billing[g.MOBILE].superwall.appleApiKey,b.billing[g.MOBILE].superwall.googleApiKey,b.analytics[g.MOBILE].mixpanel.token,b.analytics[g.MOBILE].posthog.key,b.analytics[g.MOBILE].posthog.host,b.monitoring[g.MOBILE].sentry.dsn],[m.EXTENSION]:[b.analytics[g.EXTENSION][`google-analytics`].measurementId,b.analytics[g.EXTENSION][`google-analytics`].secret,b.analytics[g.EXTENSION].posthog.key,b.analytics[g.EXTENSION].posthog.host,b.monitoring[g.EXTENSION].sentry.dsn]},te={[g.WEB]:[],[g.MOBILE]:[`apps/mobile`,`packages/analytics/mobile`,`packages/billing/mobile`,`packages/monitoring/mobile`,`packages/ui/mobile`,`.github/workflows/publish-mobile.yml`],[g.EXTENSION]:[`apps/extension`,`packages/analytics/extension`,`packages/monitoring/extension`,`.github/workflows/publish-extension.yml`]},x={email:{files:[`packages/email/src/providers/index.ts`,`packages/email/src/providers/env.ts`],pattern:RegExp(`(${Object.values(p).join(`|`)})`,`gi`)},storage:{files:[`packages/storage/src/providers/index.ts`,`packages/storage/src/providers/env.ts`],pattern:RegExp(`(${Object.values(f).join(`|`)})`,`gi`)},billing:{[g.WEB]:{files:[`packages/billing/web/src/providers/index.ts`,`packages/billing/web/src/providers/env.ts`],pattern:RegExp(`(${Object.values(_[g.WEB]).join(`|`)})`,`gi`)},[g.MOBILE]:{files:[`packages/billing/mobile/src/providers/index.ts`,`packages/billing/mobile/src/providers/env.ts`,`packages/billing/mobile/src/providers/server.ts`],pattern:RegExp(`(${Object.values(_[g.MOBILE]).join(`|`)})`,`gi`)}},analytics:{[g.WEB]:{files:[`packages/analytics/web/src/providers/index.tsx`,`packages/analytics/web/src/providers/server.ts`,`packages/analytics/web/src/providers/env.ts`],pattern:RegExp(`(${Object.values(v[g.WEB]).join(`|`)})`,`gi`)},[g.MOBILE]:{files:[`packages/analytics/mobile/src/providers/index.ts`],pattern:RegExp(`(${Object.values(v[g.MOBILE]).join(`|`)})`,`gi`)},[g.EXTENSION]:{files:[`packages/analytics/extension/src/providers/index.ts`],pattern:RegExp(`(${Object.values(v[g.EXTENSION]).join(`|`)})`,`gi`)}},monitoring:{[g.WEB]:{files:[`packages/monitoring/web/src/providers/index.ts`],pattern:RegExp(`(${Object.values(y[g.WEB]).join(`|`)})`,`gi`)},[g.MOBILE]:{files:[`packages/monitoring/mobile/src/providers/index.ts`],pattern:RegExp(`(${Object.values(y[g.MOBILE]).join(`|`)})`,`gi`)},[g.EXTENSION]:{files:[`packages/monitoring/extension/src/providers/index.ts`],pattern:RegExp(`(${Object.values(y[g.EXTENSION]).join(`|`)})`,`gi`)}}},ne={[d.DB]:`@workspace/db`},S={name:`TurboStarter`,repository:`https://github.com/turbostarter/core.git`,env:b},C={error(e){console.log(a.red(e))},warn(e){console.log(a.yellow(e))},info(e){console.log(a.cyan(e))},success(e){console.log(a.green(e))},log(e){console.log(e)},break(){console.log(``)}};function re(e){typeof e==`string`&&(C.error(e),process.exit(1)),e instanceof Error&&(C.error(e.message),process.exit(1)),C.error(`Something went wrong. Please try again.`),process.exit(1)}const w=e=>e.split(`-`).map(e=>c.capitalize(e)).join(` `),T=()=>{C.break(),C.error(`Operation cancelled.`),process.exit(0)},E=async()=>o([{type:`select`,choices:Object.values(v[g.EXTENSION]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for extension analytics?`}],{onCancel:T}),D=async e=>{switch(e){case v[g.EXTENSION].GOOGLE_ANALYTICS:return o([{type:`text`,name:S.env.analytics[g.EXTENSION][`google-analytics`].measurementId,message:`Enter your Google Analytics measurement ID`},{type:`text`,name:S.env.analytics[g.EXTENSION][`google-analytics`].secret,message:`Enter your Google Analytics secret`}],{onCancel:T});case v[g.EXTENSION].POSTHOG:return o([{type:`text`,name:S.env.analytics[g.EXTENSION].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:S.env.analytics[g.EXTENSION].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:T})}},O=async()=>{let{provider:e}=await E();return{provider:e,env:await D(e)}},k=async()=>o([{type:`select`,choices:Object.values(v[g.MOBILE]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for mobile analytics?`}],{onCancel:T}),A=async e=>{switch(e){case v[g.MOBILE].GOOGLE_ANALYTICS:return C.info(`Check how to configure Google Analytics for mobile at https://www.turbostarter.dev/docs/mobile/analytics/configuration#google-analytics`),{};case v[g.MOBILE].MIXPANEL:return o([{type:`text`,name:S.env.analytics[g.MOBILE].mixpanel.token,message:`Enter your Mixpanel token`}],{onCancel:T});case v[g.MOBILE].POSTHOG:return o([{type:`text`,name:S.env.analytics[g.MOBILE].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:S.env.analytics[g.MOBILE].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:T})}},j=async()=>{let{provider:e}=await k();return{provider:e,env:await A(e)}},M=async()=>o([{type:`select`,choices:Object.values(v[g.WEB]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for web analytics?`}],{onCancel:T}),N=async e=>{switch(e){case v[g.WEB].GOOGLE_ANALYTICS:return o([{type:`text`,name:S.env.analytics[g.WEB][`google-analytics`].measurementId,message:`Enter your Google Analytics measurement ID`},{type:`text`,name:S.env.analytics[g.WEB][`google-analytics`].secret,message:`Enter your Google Analytics secret`}],{onCancel:T});case v[g.WEB].MIXPANEL:return o([{type:`text`,name:S.env.analytics[g.WEB].mixpanel.token,message:`Enter your Mixpanel token`}],{onCancel:T});case v[g.WEB].OPEN_PANEL:return o([{type:`text`,name:S.env.analytics[g.WEB][`open-panel`].clientId,message:`Enter your OpenPanel client ID`},{type:`text`,name:S.env.analytics[g.WEB][`open-panel`].secret,message:`Enter your OpenPanel secret`}],{onCancel:T});case v[g.WEB].PLAUSIBLE:return o([{type:`text`,name:S.env.analytics[g.WEB].plausible.domain,message:`Enter your Plausible domain`},{type:`text`,name:S.env.analytics[g.WEB].plausible.host,message:`Enter your Plausible host`,initial:`https://plausible.io`}],{onCancel:T});case v[g.WEB].POSTHOG:return o([{type:`text`,name:S.env.analytics[g.WEB].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:S.env.analytics[g.WEB].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:T});case v[g.WEB].UMAMI:return o([{type:`text`,name:S.env.analytics[g.WEB].umami.host,message:`Enter your Umami host`,initial:`https://cloud.umami.is`},{type:`text`,name:S.env.analytics[g.WEB].umami.websiteId,message:`Enter your Umami website ID`},{type:`text`,name:S.env.analytics[g.WEB].umami.apiHost,message:`Enter your Umami API host`,initial:`https://api-gateway.umami.dev`},{type:`text`,name:S.env.analytics[g.WEB].umami.apiKey,message:`Enter your Umami API key`}],{onCancel:T});case v[g.WEB].VEMETRIC:return o([{type:`text`,name:S.env.analytics[g.WEB].vemetric.token,message:`Enter your Vemetric project token`}],{onCancel:T});case v[g.WEB].VERCEL:return{}}},P=async()=>{let{provider:e}=await M();return{provider:e,env:await N(e)}},F=async e=>{let t={},n={};if(e.includes(g.WEB)){let{provider:e,env:r}=await P();t[g.WEB]=e,Object.assign(n,r)}if(e.includes(g.MOBILE)){let{provider:e,env:r}=await j();t[g.MOBILE]=e,Object.assign(n,r)}if(e.includes(g.EXTENSION)){let{provider:e,env:r}=await O();t[g.EXTENSION]=e,Object.assign(n,r)}return{providers:t,env:n}},I=async()=>o([{type:`select`,choices:Object.values(_[g.MOBILE]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for mobile billing?`}],{onCancel:T}),L=async e=>{switch(e){case _[g.MOBILE].REVENUECAT:return o([{type:`text`,name:S.env.billing[g.MOBILE].revenuecat.appleApiKey,message:`Enter your RevenueCat Apple API key`},{type:`text`,name:S.env.billing[g.MOBILE].revenuecat.googleApiKey,message:`Enter your RevenueCat Google API key`},{type:`text`,name:S.env.billing[g.MOBILE].revenuecat.webhookSecret,message:`Enter your RevenueCat webhook secret`},{type:`text`,name:S.env.billing[g.MOBILE].revenuecat.apiKey,message:`Enter your RevenueCat API key`}],{onCancel:T});case _[g.MOBILE].SUPERWALL:return o([{type:`text`,name:S.env.billing[g.MOBILE].superwall.appleApiKey,message:`Enter your Superwall Apple API key`},{type:`text`,name:S.env.billing[g.MOBILE].superwall.googleApiKey,message:`Enter your Superwall Google API key`},{type:`text`,name:S.env.billing[g.MOBILE].superwall.webhookSecret,message:`Enter your Superwall webhook secret`}],{onCancel:T})}},R=async()=>{let{provider:e}=await I();return{provider:e,env:await L(e)}},z=async()=>o([{type:`select`,choices:Object.values(_[g.WEB]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for web billing?`}],{onCancel:T}),B=async e=>{switch(e){case _[g.WEB].STRIPE:return o([{type:`text`,name:S.env.billing[g.WEB].stripe.secretKey,message:`Enter your Stripe secret key`},{type:`text`,name:S.env.billing[g.WEB].stripe.webhookSecret,message:`Enter your Stripe webhook secret`}],{onCancel:T});case _[g.WEB].LEMON_SQUEEZY:return o([{type:`text`,name:S.env.billing[g.WEB][`lemon-squeezy`].storeId,message:`Enter your Lemon Squeezy store ID`},{type:`text`,name:S.env.billing[g.WEB][`lemon-squeezy`].apiKey,message:`Enter your Lemon Squeezy API key`},{type:`text`,name:S.env.billing[g.WEB][`lemon-squeezy`].signingSecret,message:`Enter your Lemon Squeezy signing secret`}],{onCancel:T});case _[g.WEB].POLAR:return o([{type:`text`,name:S.env.billing[g.WEB].polar.accessToken,message:`Enter your Polar access token`},{type:`text`,name:S.env.billing[g.WEB].polar.webhookSecret,message:`Enter your Polar webhook secret`},{type:`text`,name:S.env.billing[g.WEB].polar.organizationSlug,message:`Enter your Polar organization slug`}],{onCancel:T})}},V=async()=>{let{provider:e}=await z();return{provider:e,env:await B(e)}},H=async e=>{let t={},n={};if(e.includes(g.WEB)){let{provider:e,env:r}=await V();t[g.WEB]=e,Object.assign(n,r)}if(e.includes(g.MOBILE)){let{provider:e,env:r}=await R();t[g.MOBILE]=e,Object.assign(n,r)}return{providers:t,env:n}},U=async()=>o([{type:`text`,name:S.env.db.url,message:`Enter your database URL`}],{onCancel:T}),W=async()=>{let e=await o([{type:`select`,name:`type`,message:`How do you want to use database?`,choices:[{title:`Local (powered by Docker)`,value:u.LOCAL,selected:!0},{title:`Cloud`,value:u.CLOUD}]}],{onCancel:T});if(e.type===u.CLOUD){let t=await U();return{...e,env:t}}return e},G=async()=>o([{type:`select`,name:`provider`,message:`What do you want to use for emails?`,choices:Object.values(p).map(e=>({title:w(e),value:e}))}],{onCancel:T}),K=async e=>{switch(e){case p.RESEND:return o([{type:`text`,name:S.env.email.resend.apiKey,message:`Enter your Resend API key`}],{onCancel:T});case p.SENDGRID:return o([{type:`text`,name:S.env.email.sendgrid.apiKey,message:`Enter your Sendgrid API key`}],{onCancel:T});case p.PLUNK:return o([{type:`text`,name:S.env.email.plunk.apiKey,message:`Enter your Plunk API key`}],{onCancel:T});case p.POSTMARK:return o([{type:`text`,name:S.env.email.postmark.apiKey,message:`Enter your Postmark API key`}],{onCancel:T});case p.NODEMAILER:return o([{type:`text`,name:S.env.email.nodemailer.user,message:`Enter your Nodemailer user`},{type:`text`,name:S.env.email.nodemailer.password,message:`Enter your Nodemailer user password`},{type:`text`,name:S.env.email.nodemailer.host,message:`Enter your Nodemailer host`},{type:`number`,name:S.env.email.nodemailer.port,message:`Enter your Nodemailer port`}],{onCancel:T})}},q=async()=>{let{provider:e}=await G();return{provider:e,env:await K(e)}},J=async e=>{try{await Promise.allSettled(Object.values(m).map(async t=>{let n=i(e,t);await l.copyFile(i(n,h.EXAMPLE),i(n,h.LOCAL))}))}catch{C.error(`Failed to prepare environment!`),process.exit(1)}},Y=async(e,t,n)=>{if(!n)return;let r=c.keys(c.pickBy(ee,e=>c.includes(e,t)));for(let a of r){let r=i(i(e,a),h.LOCAL),o=await l.readFile(r,`utf8`),s=RegExp(`^${t}=.*`,`gm`);s.test(o)?await l.writeFile(r,o.replace(s,`${t}="${n}"`)):await l.appendFile(r,`\n${t}="${n}"`)}},X=async(e,t)=>{let r=n(`Setting environment variables...`).start();try{for(let[n,r]of Object.entries(t))await Y(e,n,r);r.succeed(`Environment variables successfully set!`)}catch{C.error(`Failed to set environment variables!`),process.exit(1)}},ie=async()=>o([{type:`select`,choices:Object.values(y[g.EXTENSION]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for extension monitoring?`}],{onCancel:T}),ae=async e=>{switch(e){case y[g.EXTENSION].SENTRY:return o([{type:`text`,name:S.env.monitoring[g.EXTENSION].sentry.dsn,message:`Enter your Sentry DSN`}],{onCancel:T});case y[g.EXTENSION].POSTHOG:return o([{type:`text`,name:S.env.monitoring[g.EXTENSION].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:S.env.monitoring[g.EXTENSION].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:T})}},oe=async()=>{let{provider:e}=await ie();return{provider:e,env:await ae(e)}},se=async()=>o([{type:`select`,choices:Object.values(y[g.MOBILE]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for mobile monitoring?`}],{onCancel:T}),ce=async e=>{switch(e){case y[g.MOBILE].SENTRY:return o([{type:`text`,name:S.env.monitoring[g.MOBILE].sentry.dsn,message:`Enter your Sentry DSN`}],{onCancel:T});case y[g.MOBILE].POSTHOG:return o([{type:`text`,name:S.env.monitoring[g.MOBILE].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:S.env.monitoring[g.MOBILE].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:T})}},le=async()=>{let{provider:e}=await se();return{provider:e,env:await ce(e)}},ue=async()=>o([{type:`select`,choices:Object.values(y[g.WEB]).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for web monitoring?`}],{onCancel:T}),de=async e=>{switch(e){case y[g.WEB].SENTRY:return o([{type:`text`,name:S.env.monitoring[g.WEB].sentry.dsn,message:`Enter your Sentry DSN`}],{onCancel:T});case y[g.WEB].POSTHOG:return o([{type:`text`,name:S.env.monitoring[g.WEB].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:S.env.monitoring[g.WEB].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:T})}},fe=async()=>{let{provider:e}=await ue();return{provider:e,env:await de(e)}},pe=async e=>{let t={},n={};if(e.includes(g.WEB)){let{provider:e,env:r}=await fe();t[g.WEB]=e,Object.assign(n,r)}if(e.includes(g.MOBILE)){let{provider:e,env:r}=await le();t[g.MOBILE]=e,Object.assign(n,r)}if(e.includes(g.EXTENSION)){let{provider:e,env:r}=await oe();t[g.EXTENSION]=e,Object.assign(n,r)}return{providers:t,env:n}},me=async()=>o([{type:`select`,choices:Object.values(f).map(e=>({title:w(e),value:e})),name:`provider`,message:`What do you want to use for storage?`}],{onCancel:T}),he=()=>o([{type:`text`,name:S.env.storage.s3.region,message:`Enter your S3 region`,initial:`us-east-1`},{type:`text`,name:S.env.storage.s3.endpoint,message:`Enter your S3 endpoint`,initial:`https://s3.amazonaws.com`},{type:`text`,name:S.env.storage.s3.bucket,message:`Enter your default S3 bucket name`},{type:`text`,name:S.env.storage.s3.accessKeyId,message:`Enter your S3 access key ID`},{type:`text`,name:S.env.storage.s3.secretAccessKey,message:`Enter your S3 secret access key`}],{onCancel:T}),ge=async()=>{let{provider:e}=await me();return{provider:e,env:await he()}},_e=async()=>{try{await t(`node`,[`--version`])}catch{C.error(`Node.js is not installed. Please install Node.js and try again.
3
- `),C.info(`To install Node.js, visit: ${a.underline(`https://nodejs.org/en/`)}`),process.exit(1)}},ve=async()=>{try{await t(`pnpm`,[`--version`])}catch{try{await t(`npm`,[`install`,`-g`,`pnpm`])}catch{C.error(`pnpm is not installed. Please install pnpm manually and try again.
4
- `),C.info(`To install pnpm, visit: ${a.underline(`https://pnpm.io/installation`)}`),process.exit(1)}}},ye=async()=>{try{await t(`docker`,[`--version`])}catch{C.error(`Docker is not installed. Please install Docker and try again.`),C.info(`To install Docker, visit: ${a.underline(`https://docs.docker.com/get-docker/`)}`),process.exit(1)}},be=async()=>{try{await t(`git`,[`--version`])}catch{C.error(`Git is not installed. Please install Git and try again.`),C.info(`To install Git, visit: ${a.underline(`https://git-scm.com/downloads`)}`),process.exit(1)}},xe=async()=>{let e=n(`Checking prerequisites...
5
- `).start();try{await be(),await _e(),await ve(),e.succeed(`All prerequisites are satisfied, let's start! šŸš€
6
- `)}catch{e.fail(`Failed to check prerequisites.`),process.exit(1)}},Se=async(e,r)=>{await ye();let i=n(`Starting Docker services...`).start();try{await t(`pnpm`,[`services:start`,`--`,...r],{cwd:e}),await t(`pnpm`,[`with-env`,`pnpm`,`turbo`,`setup`,r.map(e=>`--filter=${ne[e]}`).join(` `)],{cwd:e}),i.succeed(`Services successfully started!`)}catch(e){console.error(e),i.fail(`Failed to start services!`),process.exit(1)}},Z=async({cwd:e,paths:t})=>{await Promise.all(t.map(async t=>{let n=i(e,t);await l.rm(n,{recursive:!0,force:!0})}))},Ce=async({cwd:e,path:t,pattern:n,value:r})=>{let a=i(e,t),o=(await l.readFile(a,`utf8`)).replace(n,r);await l.writeFile(a,o)},Q=async({cwd:e,paths:t,pattern:n,value:r})=>{await Promise.all(t.map(async t=>{await Ce({cwd:e,path:t,pattern:n,value:r})}))},we=s.object({cwd:s.string()}),Te=new e().name(`new`).description(`create a new TurboStarter project`).option(`-c, --cwd <cwd>`,`the working directory. defaults to the current directory.`,process.cwd()).action(async e=>{try{C.log(`\n${a.bgRedBright(a.white(` TurboStarter `))}\n`);let{name:t}=await Ee(we.parse({cwd:r.resolve(e.cwd)}));C.log(`
2
+ import{Command as e}from"commander";import{execa as t}from"execa";import{promises as n}from"fs";import r from"ora";import i,{join as a}from"path";import o from"picocolors";import s from"prompts";import{Node as c,Project as ee,SyntaxKind as l}from"ts-morph";import{z as u}from"zod";import d from"lodash";const f={LOCAL:`local`,CLOUD:`cloud`},p={DB:`db`},m={S3:`s3`},h={RESEND:`resend`,SENDGRID:`sendgrid`,POSTMARK:`postmark`,PLUNK:`plunk`,NODEMAILER:`nodemailer`},g={ROOT:`./`,WEB:`./apps/web`,MOBILE:`./apps/mobile`,EXTENSION:`./apps/extension`},_={EXAMPLE:`.env.example`,LOCAL:`.env.local`},v={WEB:`web`,MOBILE:`mobile`,EXTENSION:`extension`},y={[v.WEB]:{STRIPE:`stripe`,LEMON_SQUEEZY:`lemon-squeezy`,POLAR:`polar`},[v.MOBILE]:{REVENUECAT:`revenuecat`,SUPERWALL:`superwall`}},b={[v.WEB]:{GOOGLE_ANALYTICS:`google-analytics`,MIXPANEL:`mixpanel`,OPEN_PANEL:`open-panel`,PLAUSIBLE:`plausible`,POSTHOG:`posthog`,UMAMI:`umami`,VEMETRIC:`vemetric`,VERCEL:`vercel`},[v.MOBILE]:{GOOGLE_ANALYTICS:`google-analytics`,MIXPANEL:`mixpanel`,POSTHOG:`posthog`},[v.EXTENSION]:{GOOGLE_ANALYTICS:`google-analytics`,POSTHOG:`posthog`}},x={[v.WEB]:{SENTRY:`sentry`,POSTHOG:`posthog`},[v.MOBILE]:{SENTRY:`sentry`,POSTHOG:`posthog`},[v.EXTENSION]:{SENTRY:`sentry`,POSTHOG:`posthog`}},S={[p.DB]:{url:`DATABASE_URL`},billing:{[v.WEB]:{[y[v.WEB].STRIPE]:{secretKey:`STRIPE_SECRET_KEY`,webhookSecret:`STRIPE_WEBHOOK_SECRET`},[y[v.WEB].LEMON_SQUEEZY]:{apiKey:`LEMON_SQUEEZY_API_KEY`,signingSecret:`LEMON_SQUEEZY_SIGNING_SECRET`,storeId:`LEMON_SQUEEZY_STORE_ID`},[y[v.WEB].POLAR]:{accessToken:`POLAR_ACCESS_TOKEN`,webhookSecret:`POLAR_WEBHOOK_SECRET`,organizationSlug:`POLAR_ORGANIZATION_SLUG`}},[v.MOBILE]:{[y[v.MOBILE].REVENUECAT]:{appleApiKey:`EXPO_PUBLIC_REVENUECAT_APPLE_API_KEY`,googleApiKey:`EXPO_PUBLIC_REVENUECAT_GOOGLE_API_KEY`,webhookSecret:`REVENUECAT_WEBHOOK_SECRET`,apiKey:`REVENUECAT_API_KEY`},[y[v.MOBILE].SUPERWALL]:{appleApiKey:`EXPO_PUBLIC_SUPERWALL_APPLE_API_KEY`,googleApiKey:`EXPO_PUBLIC_SUPERWALL_GOOGLE_API_KEY`,webhookSecret:`SUPERWALL_WEBHOOK_SECRET`}}},email:{[h.RESEND]:{apiKey:`RESEND_API_KEY`},[h.SENDGRID]:{apiKey:`SENDGRID_API_KEY`},[h.PLUNK]:{apiKey:`PLUNK_API_KEY`},[h.POSTMARK]:{apiKey:`POSTMARK_API_KEY`},[h.NODEMAILER]:{user:`NODEMAILER_USER`,password:`NODEMAILER_PASSWORD`,host:`NODEMAILER_HOST`,port:`NODEMAILER_PORT`}},storage:{[m.S3]:{region:`S3_REGION`,bucket:`S3_BUCKET`,endpoint:`S3_ENDPOINT`,accessKeyId:`S3_ACCESS_KEY_ID`,secretAccessKey:`S3_SECRET_ACCESS_KEY`}},analytics:{[v.WEB]:{[b[v.WEB].GOOGLE_ANALYTICS]:{measurementId:`NEXT_PUBLIC_GOOGLE_ANALYTICS_MEASUREMENT_ID`,secret:`GOOGLE_ANALYTICS_SECRET`},[b[v.WEB].MIXPANEL]:{token:`NEXT_PUBLIC_MIXPANEL_TOKEN`},[b[v.WEB].OPEN_PANEL]:{clientId:`NEXT_PUBLIC_OPEN_PANEL_CLIENT_ID`,secret:`OPEN_PANEL_SECRET`},[b[v.WEB].PLAUSIBLE]:{domain:`NEXT_PUBLIC_PLAUSIBLE_DOMAIN`,host:`NEXT_PUBLIC_PLAUSIBLE_HOST`},[b[v.WEB].POSTHOG]:{key:`NEXT_PUBLIC_POSTHOG_KEY`,host:`NEXT_PUBLIC_POSTHOG_HOST`},[b[v.WEB].UMAMI]:{host:`NEXT_PUBLIC_UMAMI_HOST`,websiteId:`NEXT_PUBLIC_UMAMI_WEBSITE_ID`,apiHost:`UMAMI_API_HOST`,apiKey:`UMAMI_API_KEY`},[b[v.WEB].VEMETRIC]:{token:`NEXT_PUBLIC_VEMETRIC_PROJECT_TOKEN`},[b[v.WEB].VERCEL]:{}},[v.MOBILE]:{[b[v.MOBILE].GOOGLE_ANALYTICS]:{},[b[v.MOBILE].MIXPANEL]:{token:`EXPO_PUBLIC_MIXPANEL_TOKEN`},[b[v.MOBILE].POSTHOG]:{key:`EXPO_PUBLIC_POSTHOG_KEY`,host:`EXPO_PUBLIC_POSTHOG_HOST`}},[v.EXTENSION]:{[b[v.EXTENSION].GOOGLE_ANALYTICS]:{measurementId:`VITE_GOOGLE_ANALYTICS_MEASUREMENT_ID`,secret:`VITE_GOOGLE_ANALYTICS_SECRET`},[b[v.EXTENSION].POSTHOG]:{key:`VITE_POSTHOG_KEY`,host:`VITE_POSTHOG_HOST`}}},monitoring:{[v.WEB]:{[x[v.WEB].SENTRY]:{dsn:`NEXT_PUBLIC_SENTRY_DSN`},[x[v.WEB].POSTHOG]:{key:`NEXT_PUBLIC_POSTHOG_KEY`,host:`NEXT_PUBLIC_POSTHOG_HOST`}},[v.MOBILE]:{[x[v.MOBILE].SENTRY]:{dsn:`EXPO_PUBLIC_SENTRY_DSN`},[x[v.MOBILE].POSTHOG]:{key:`EXPO_PUBLIC_POSTHOG_KEY`,host:`EXPO_PUBLIC_POSTHOG_HOST`}},[v.EXTENSION]:{[x[v.EXTENSION].SENTRY]:{dsn:`VITE_SENTRY_DSN`},[x[v.EXTENSION].POSTHOG]:{key:`VITE_POSTHOG_KEY`,host:`VITE_POSTHOG_HOST`}}}},te={[g.ROOT]:[S.db.url],[g.WEB]:[S.email.resend.apiKey,S.email.sendgrid.apiKey,S.email.plunk.apiKey,S.email.postmark.apiKey,S.email.nodemailer.user,S.email.nodemailer.password,S.storage.s3.accessKeyId,S.storage.s3.secretAccessKey,S.billing[v.WEB].stripe.secretKey,S.billing[v.WEB].stripe.webhookSecret,S.billing[v.WEB][`lemon-squeezy`].apiKey,S.billing[v.WEB][`lemon-squeezy`].signingSecret,S.billing[v.WEB][`lemon-squeezy`].storeId,S.billing[v.WEB].polar.accessToken,S.billing[v.WEB].polar.webhookSecret,S.billing[v.WEB].polar.organizationSlug,S.billing[v.MOBILE].revenuecat.webhookSecret,S.billing[v.MOBILE].revenuecat.apiKey,S.billing[v.MOBILE].superwall.webhookSecret,S.analytics[v.WEB][`google-analytics`].measurementId,S.analytics[v.WEB][`google-analytics`].secret,S.analytics[v.WEB].mixpanel.token,S.analytics[v.WEB][`open-panel`].clientId,S.analytics[v.WEB][`open-panel`].secret,S.analytics[v.WEB].plausible.domain,S.analytics[v.WEB].plausible.host,S.analytics[v.WEB].posthog.key,S.analytics[v.WEB].posthog.host,S.analytics[v.WEB].umami.host,S.analytics[v.WEB].umami.websiteId,S.analytics[v.WEB].umami.apiHost,S.analytics[v.WEB].umami.apiKey,S.analytics[v.WEB].vemetric.token,S.monitoring[v.WEB].sentry.dsn,S.monitoring[v.WEB].posthog.key,S.monitoring[v.WEB].posthog.host],[g.MOBILE]:[S.billing[v.MOBILE].revenuecat.appleApiKey,S.billing[v.MOBILE].revenuecat.googleApiKey,S.billing[v.MOBILE].superwall.appleApiKey,S.billing[v.MOBILE].superwall.googleApiKey,S.analytics[v.MOBILE].mixpanel.token,S.analytics[v.MOBILE].posthog.key,S.analytics[v.MOBILE].posthog.host,S.monitoring[v.MOBILE].sentry.dsn],[g.EXTENSION]:[S.analytics[v.EXTENSION][`google-analytics`].measurementId,S.analytics[v.EXTENSION][`google-analytics`].secret,S.analytics[v.EXTENSION].posthog.key,S.analytics[v.EXTENSION].posthog.host,S.monitoring[v.EXTENSION].sentry.dsn]},C={email:{files:[`packages/email/src/providers/index.ts`,`packages/email/src/providers/env.ts`],pattern:RegExp(`(${Object.values(h).join(`|`)})`,`gi`)},storage:{files:[`packages/storage/src/providers/index.ts`,`packages/storage/src/providers/env.ts`],pattern:RegExp(`(${Object.values(m).join(`|`)})`,`gi`)},billing:{[v.WEB]:{files:[`packages/billing/web/src/providers/index.ts`,`packages/billing/web/src/providers/env.ts`],pattern:RegExp(`(${Object.values(y[v.WEB]).join(`|`)})`,`gi`)},[v.MOBILE]:{files:[`packages/billing/mobile/src/providers/index.ts`,`packages/billing/mobile/src/providers/env.ts`,`packages/billing/mobile/src/providers/server.ts`],pattern:RegExp(`(${Object.values(y[v.MOBILE]).join(`|`)})`,`gi`)}},analytics:{[v.WEB]:{files:[`packages/analytics/web/src/providers/index.tsx`,`packages/analytics/web/src/providers/server.ts`,`packages/analytics/web/src/providers/env.ts`],pattern:RegExp(`(${Object.values(b[v.WEB]).join(`|`)})`,`gi`)},[v.MOBILE]:{files:[`packages/analytics/mobile/src/providers/index.ts`],pattern:RegExp(`(${Object.values(b[v.MOBILE]).join(`|`)})`,`gi`)},[v.EXTENSION]:{files:[`packages/analytics/extension/src/providers/index.ts`],pattern:RegExp(`(${Object.values(b[v.EXTENSION]).join(`|`)})`,`gi`)}},monitoring:{[v.WEB]:{files:[`packages/monitoring/web/src/providers/index.ts`],pattern:RegExp(`(${Object.values(x[v.WEB]).join(`|`)})`,`gi`)},[v.MOBILE]:{files:[`packages/monitoring/mobile/src/providers/index.ts`],pattern:RegExp(`(${Object.values(x[v.MOBILE]).join(`|`)})`,`gi`)},[v.EXTENSION]:{files:[`packages/monitoring/extension/src/providers/index.ts`],pattern:RegExp(`(${Object.values(x[v.EXTENSION]).join(`|`)})`,`gi`)}}},ne={[p.DB]:`@workspace/db`},w={name:`TurboStarter`,repository:`turbostarter/core`,env:S},T=e=>typeof e==`string`?e:String(e),E={error(e){console.log(o.red(T(e)))},warn(e){console.log(o.yellow(T(e)))},info(e){console.log(o.cyan(T(e)))},success(e){console.log(o.green(T(e)))},log(e){console.log(T(e))}};function D(e){return`git@github.com:${e}`}function O(e){return`https://github.com/${e}`}async function k(){try{return await t(`ssh`,[`-T`,`git@github.com`,`-o`,`StrictHostKeyChecking=no`],{timeout:1e4}),!0}catch(e){return(e instanceof Error&&`stderr`in e?String(e.stderr):``).includes(`successfully authenticated`)}}function re(e,t){let n=e.replace(/\/+$/,``).replace(/\.git$/,``);return n===D(t)||n===O(t)}async function A({cwd:e}){try{let{stdout:n}=await t(`git remote get-url upstream`,{cwd:e});return n.trim()||void 0}catch{return}}async function j(e,{cwd:n}){await A({cwd:n})?await t(`git`,[`remote`,`set-url`,`upstream`,e],{cwd:n}):await t(`git`,[`remote`,`add`,`upstream`,e],{cwd:n})}async function ie({cwd:e}){try{let{stdout:n}=await t(`git status --porcelain`,{cwd:e});return n.trim()===``}catch{return!1}}const M=e=>e.split(`-`).map(e=>d.capitalize(e)).join(` `),N=()=>{E.error(`Operation cancelled.`),process.exit(0)},ae=(e,t)=>t.safeParse(e).success,P=e=>{if(e instanceof Error&&`stderr`in e){let t=String(e.stderr);if(t)return t}if(e instanceof Error&&`stdout`in e){let t=String(e.stdout);if(t)return t}return e instanceof Error?e.message:String(e)},F=async()=>s([{type:`select`,choices:Object.values(b[v.EXTENSION]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for extension analytics?`}],{onCancel:N}),I=async e=>{switch(e){case b[v.EXTENSION].GOOGLE_ANALYTICS:return s([{type:`text`,name:w.env.analytics[v.EXTENSION][`google-analytics`].measurementId,message:`Enter your Google Analytics measurement ID`},{type:`text`,name:w.env.analytics[v.EXTENSION][`google-analytics`].secret,message:`Enter your Google Analytics secret`}],{onCancel:N});case b[v.EXTENSION].POSTHOG:return s([{type:`text`,name:w.env.analytics[v.EXTENSION].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:w.env.analytics[v.EXTENSION].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:N})}},L=async()=>{let{provider:e}=await F();return{provider:e,env:await I(e)}},R=async()=>s([{type:`select`,choices:Object.values(b[v.MOBILE]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for mobile analytics?`}],{onCancel:N}),z=async e=>{switch(e){case b[v.MOBILE].GOOGLE_ANALYTICS:return E.info(`Check how to configure Google Analytics for mobile at https://www.turbostarter.dev/docs/mobile/analytics/configuration#google-analytics`),{};case b[v.MOBILE].MIXPANEL:return s([{type:`text`,name:w.env.analytics[v.MOBILE].mixpanel.token,message:`Enter your Mixpanel token`}],{onCancel:N});case b[v.MOBILE].POSTHOG:return s([{type:`text`,name:w.env.analytics[v.MOBILE].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:w.env.analytics[v.MOBILE].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:N})}},B=async()=>{let{provider:e}=await R();return{provider:e,env:await z(e)}},V=async()=>s([{type:`select`,choices:Object.values(b[v.WEB]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for web analytics?`}],{onCancel:N}),H=async e=>{switch(e){case b[v.WEB].GOOGLE_ANALYTICS:return s([{type:`text`,name:w.env.analytics[v.WEB][`google-analytics`].measurementId,message:`Enter your Google Analytics measurement ID`},{type:`text`,name:w.env.analytics[v.WEB][`google-analytics`].secret,message:`Enter your Google Analytics secret`}],{onCancel:N});case b[v.WEB].MIXPANEL:return s([{type:`text`,name:w.env.analytics[v.WEB].mixpanel.token,message:`Enter your Mixpanel token`}],{onCancel:N});case b[v.WEB].OPEN_PANEL:return s([{type:`text`,name:w.env.analytics[v.WEB][`open-panel`].clientId,message:`Enter your OpenPanel client ID`},{type:`text`,name:w.env.analytics[v.WEB][`open-panel`].secret,message:`Enter your OpenPanel secret`}],{onCancel:N});case b[v.WEB].PLAUSIBLE:return s([{type:`text`,name:w.env.analytics[v.WEB].plausible.domain,message:`Enter your Plausible domain`},{type:`text`,name:w.env.analytics[v.WEB].plausible.host,message:`Enter your Plausible host`,initial:`https://plausible.io`}],{onCancel:N});case b[v.WEB].POSTHOG:return s([{type:`text`,name:w.env.analytics[v.WEB].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:w.env.analytics[v.WEB].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:N});case b[v.WEB].UMAMI:return s([{type:`text`,name:w.env.analytics[v.WEB].umami.host,message:`Enter your Umami host`,initial:`https://cloud.umami.is`},{type:`text`,name:w.env.analytics[v.WEB].umami.websiteId,message:`Enter your Umami website ID`},{type:`text`,name:w.env.analytics[v.WEB].umami.apiHost,message:`Enter your Umami API host`,initial:`https://api-gateway.umami.dev`},{type:`text`,name:w.env.analytics[v.WEB].umami.apiKey,message:`Enter your Umami API key`}],{onCancel:N});case b[v.WEB].VEMETRIC:return s([{type:`text`,name:w.env.analytics[v.WEB].vemetric.token,message:`Enter your Vemetric project token`}],{onCancel:N});case b[v.WEB].VERCEL:return{}}},U=async()=>{let{provider:e}=await V();return{provider:e,env:await H(e)}},W=async e=>{let t={},n={};if(e.includes(v.WEB)){let{provider:e,env:r}=await U();t[v.WEB]=e,Object.assign(n,r)}if(e.includes(v.MOBILE)){let{provider:e,env:r}=await B();t[v.MOBILE]=e,Object.assign(n,r)}if(e.includes(v.EXTENSION)){let{provider:e,env:r}=await L();t[v.EXTENSION]=e,Object.assign(n,r)}return{providers:t,env:n}},G=async()=>s([{type:`select`,choices:Object.values(y[v.MOBILE]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for mobile billing?`}],{onCancel:N}),K=async e=>{switch(e){case y[v.MOBILE].REVENUECAT:return s([{type:`text`,name:w.env.billing[v.MOBILE].revenuecat.appleApiKey,message:`Enter your RevenueCat Apple API key`},{type:`text`,name:w.env.billing[v.MOBILE].revenuecat.googleApiKey,message:`Enter your RevenueCat Google API key`},{type:`text`,name:w.env.billing[v.MOBILE].revenuecat.webhookSecret,message:`Enter your RevenueCat webhook secret`},{type:`text`,name:w.env.billing[v.MOBILE].revenuecat.apiKey,message:`Enter your RevenueCat API key`}],{onCancel:N});case y[v.MOBILE].SUPERWALL:return s([{type:`text`,name:w.env.billing[v.MOBILE].superwall.appleApiKey,message:`Enter your Superwall Apple API key`},{type:`text`,name:w.env.billing[v.MOBILE].superwall.googleApiKey,message:`Enter your Superwall Google API key`},{type:`text`,name:w.env.billing[v.MOBILE].superwall.webhookSecret,message:`Enter your Superwall webhook secret`}],{onCancel:N})}},q=async()=>{let{provider:e}=await G();return{provider:e,env:await K(e)}},J=async()=>s([{type:`select`,choices:Object.values(y[v.WEB]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for web billing?`}],{onCancel:N}),oe=async e=>{switch(e){case y[v.WEB].STRIPE:return s([{type:`text`,name:w.env.billing[v.WEB].stripe.secretKey,message:`Enter your Stripe secret key`},{type:`text`,name:w.env.billing[v.WEB].stripe.webhookSecret,message:`Enter your Stripe webhook secret`}],{onCancel:N});case y[v.WEB].LEMON_SQUEEZY:return s([{type:`text`,name:w.env.billing[v.WEB][`lemon-squeezy`].storeId,message:`Enter your Lemon Squeezy store ID`},{type:`text`,name:w.env.billing[v.WEB][`lemon-squeezy`].apiKey,message:`Enter your Lemon Squeezy API key`},{type:`text`,name:w.env.billing[v.WEB][`lemon-squeezy`].signingSecret,message:`Enter your Lemon Squeezy signing secret`}],{onCancel:N});case y[v.WEB].POLAR:return s([{type:`text`,name:w.env.billing[v.WEB].polar.accessToken,message:`Enter your Polar access token`},{type:`text`,name:w.env.billing[v.WEB].polar.webhookSecret,message:`Enter your Polar webhook secret`},{type:`text`,name:w.env.billing[v.WEB].polar.organizationSlug,message:`Enter your Polar organization slug`}],{onCancel:N})}},se=async()=>{let{provider:e}=await J();return{provider:e,env:await oe(e)}},ce=async e=>{let t={},n={};if(e.includes(v.WEB)){let{provider:e,env:r}=await se();t[v.WEB]=e,Object.assign(n,r)}if(e.includes(v.MOBILE)){let{provider:e,env:r}=await q();t[v.MOBILE]=e,Object.assign(n,r)}return{providers:t,env:n}},le=async()=>s([{type:`text`,name:w.env[p.DB].url,message:`Enter your database URL`}],{onCancel:N}),ue=async()=>{let e=await s([{type:`select`,name:`type`,message:`How do you want to use database?`,choices:[{title:`Local (powered by Docker)`,value:f.LOCAL,selected:!0},{title:`Cloud`,value:f.CLOUD}]}],{onCancel:N});if(e.type===f.CLOUD){let t=await le();return{...e,env:t}}return e},de=async()=>s([{type:`select`,name:`provider`,message:`What do you want to use for emails?`,choices:Object.values(h).map(e=>({title:M(e),value:e}))}],{onCancel:N}),fe=async e=>{switch(e){case h.RESEND:return s([{type:`text`,name:w.env.email.resend.apiKey,message:`Enter your Resend API key`}],{onCancel:N});case h.SENDGRID:return s([{type:`text`,name:w.env.email.sendgrid.apiKey,message:`Enter your Sendgrid API key`}],{onCancel:N});case h.PLUNK:return s([{type:`text`,name:w.env.email.plunk.apiKey,message:`Enter your Plunk API key`}],{onCancel:N});case h.POSTMARK:return s([{type:`text`,name:w.env.email.postmark.apiKey,message:`Enter your Postmark API key`}],{onCancel:N});case h.NODEMAILER:return s([{type:`text`,name:w.env.email.nodemailer.user,message:`Enter your Nodemailer user`},{type:`text`,name:w.env.email.nodemailer.password,message:`Enter your Nodemailer user password`},{type:`text`,name:w.env.email.nodemailer.host,message:`Enter your Nodemailer host`},{type:`number`,name:w.env.email.nodemailer.port,message:`Enter your Nodemailer port`}],{onCancel:N})}},pe=async()=>{let{provider:e}=await de();return{provider:e,env:await fe(e)}},me=async e=>{try{await Promise.allSettled(Object.values(g).map(async t=>{let r=a(e,t);await n.copyFile(a(r,_.EXAMPLE),a(r,_.LOCAL))}))}catch(e){E.error(e),E.error(`Failed to prepare environment!`),process.exit(1)}},he=async(e,t,r)=>{if(!r)return;let i=d.keys(d.pickBy(te,e=>d.includes(e,t)));for(let o of i){let i=a(a(e,o),_.LOCAL),s=await n.readFile(i,`utf8`),c=RegExp(`^${t}=.*`,`gm`);c.test(s)?await n.writeFile(i,s.replace(c,`${t}="${r}"`)):await n.appendFile(i,`\n${t}="${r}"`)}},ge=async(e,t)=>{let n=r(`Setting environment variables...`).start();try{for(let[n,r]of Object.entries(t))await he(e,n,r);n.succeed(`Environment variables successfully set!`)}catch(e){E.error(e),E.error(`Failed to set environment variables!`),process.exit(1)}};function Y(e){return e}const X=e=>e,_e=e=>e.path.endsWith(`.json`),ve=e=>[`.ts`,`.tsx`].some(t=>e.path.endsWith(t)),ye=async({cwd:e,path:t})=>{let r=a(e,t);await n.rm(r,{recursive:!0,force:!0})},Z=(e,t)=>d.transform(e,(e,n,r)=>{[`dependencies`,`devDependencies`].includes(r)?e[r]=n&&typeof n==`object`?d.omit(n,t):n:e[r]=n},{}),be=async({cwd:e,path:t,pattern:r,value:i})=>{let o=a(e,t),s=(await n.readFile(o,`utf8`)).replace(r,i);await n.writeFile(o,s)},Q=async({cwd:e,paths:t,pattern:n,value:r})=>{await Promise.all(t.map(async t=>{await be({cwd:e,path:t,pattern:n,value:r})}))},xe={[v.WEB]:[],[v.MOBILE]:[...[`apps/mobile`,`packages/analytics/mobile`,`packages/billing/mobile`,`packages/monitoring/mobile`,`packages/ui/mobile`].map(e=>X({path:e,action:`remove`})),...[`packages/auth/src/client/mobile.ts`,`packages/auth/src/server/mobile.ts`].map(e=>Y({path:e,action:`remove`})),Y({path:`packages/api/package.json`,action:`modify`,schema:u.looseObject({dependencies:u.record(u.string(),u.string())}),modify:e=>Z(e,`@workspace/billing-mobile`)}),Y({path:`packages/auth/package.json`,action:`modify`,schema:u.looseObject({dependencies:u.record(u.string(),u.string())}),modify:e=>Z(e,`@better-auth/expo`)}),Y({path:`packages/api/src/env.ts`,action:`modify`,modify:e=>{e.getImportDeclaration(e=>e.getModuleSpecifierValue().startsWith(`@workspace/billing-mobile`))?.remove();let t=e.getVariableDeclaration(`preset`)?.getInitializer()?.getFirstDescendantByKind(l.ObjectLiteralExpression)?.getProperty(`extends`);if(!t||!c.isPropertyAssignment(t))return;let n=t.getInitializerIfKind(l.ArrayLiteralExpression);if(!n)return;let r=n.getElements();for(let e=r.length-1;e>=0;e--)r[e]?.getText()===`billingMobile`&&n.removeElement(e)}}),Y({path:`packages/api/src/modules/billing/router.ts`,action:`modify`,modify:e=>{e.getImportDeclaration(e=>e.getModuleSpecifierValue().startsWith(`@workspace/billing-mobile`))?.remove();let t=e.getDescendantsOfKind(l.CallExpression).find(e=>{let t=e.getExpression();if(!c.isPropertyAccessExpression(t))return!1;let n=e.getArguments();if(!n.length)return!1;let r=n[0].getText();return t.getName()===`post`&&r.includes(`mobile.provider`)});if(!t)return;let n=t.getExpression();c.isPropertyAccessExpression(n)&&t.replaceWithText(n.getExpression().getText())}}),Y({path:`packages/auth/src/server.ts`,action:`modify`,modify:e=>{let t=t=>{let n=(e.getVariableDeclaration(`auth`)?.getInitializerIfKind(l.CallExpression)?.getArguments()[0]?.asKind(l.ObjectLiteralExpression))?.getProperty(t);if(!(!n||!c.isPropertyAssignment(n)))return n.getInitializerIfKind(l.ArrayLiteralExpression)},n=(e,t)=>{if(!e)return;let n=e.getElements();for(let r=n.length-1;r>=0;r--)t(n[r]?.getText()??``)&&e.removeElement(r)};e.getImportDeclaration(e=>e.getModuleSpecifierValue()===`@better-auth/expo`)?.remove(),n(t(`plugins`),e=>e.startsWith(`expo(`)),n(t(`trustedOrigins`),e=>e===`"turbostarter://"`)}})],[v.EXTENSION]:[...[`apps/extension`,`packages/analytics/extension`,`packages/monitoring/extension`].map(e=>X({path:e,action:`remove`})),Y({path:`.github/workflows/publish-extension.yml`,action:`remove`}),Y({path:`packages/auth/src/server.ts`,action:`modify`,modify:e=>{let t=e.getVariableDeclaration(`auth`)?.getInitializerIfKind(l.CallExpression)?.getArguments()[0]?.asKind(l.ObjectLiteralExpression)?.getProperty(`trustedOrigins`);if(!t||!c.isPropertyAssignment(t))return;let n=t.getInitializerIfKind(l.ArrayLiteralExpression);if(!n)return;let r=n.getElements();for(let e=r.length-1;e>=0;e--)r[e]?.getText()===`"chrome-extension://"`&&n.removeElement(e)}})]},Se=async()=>s([{type:`select`,choices:Object.values(x[v.EXTENSION]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for extension monitoring?`}],{onCancel:N}),Ce=async e=>{switch(e){case x[v.EXTENSION].SENTRY:return s([{type:`text`,name:w.env.monitoring[v.EXTENSION].sentry.dsn,message:`Enter your Sentry DSN`}],{onCancel:N});case x[v.EXTENSION].POSTHOG:return s([{type:`text`,name:w.env.monitoring[v.EXTENSION].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:w.env.monitoring[v.EXTENSION].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:N})}},we=async()=>{let{provider:e}=await Se();return{provider:e,env:await Ce(e)}},Te=async()=>s([{type:`select`,choices:Object.values(x[v.MOBILE]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for mobile monitoring?`}],{onCancel:N}),Ee=async e=>{switch(e){case x[v.MOBILE].SENTRY:return s([{type:`text`,name:w.env.monitoring[v.MOBILE].sentry.dsn,message:`Enter your Sentry DSN`}],{onCancel:N});case x[v.MOBILE].POSTHOG:return s([{type:`text`,name:w.env.monitoring[v.MOBILE].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:w.env.monitoring[v.MOBILE].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:N})}},De=async()=>{let{provider:e}=await Te();return{provider:e,env:await Ee(e)}},Oe=async()=>s([{type:`select`,choices:Object.values(x[v.WEB]).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for web monitoring?`}],{onCancel:N}),ke=async e=>{switch(e){case x[v.WEB].SENTRY:return s([{type:`text`,name:w.env.monitoring[v.WEB].sentry.dsn,message:`Enter your Sentry DSN`}],{onCancel:N});case x[v.WEB].POSTHOG:return s([{type:`text`,name:w.env.monitoring[v.WEB].posthog.key,message:`Enter your PostHog key`},{type:`text`,name:w.env.monitoring[v.WEB].posthog.host,message:`Enter your PostHog host`,initial:`https://us.posthog.com`}],{onCancel:N})}},Ae=async()=>{let{provider:e}=await Oe();return{provider:e,env:await ke(e)}},je=async e=>{let t={},n={};if(e.includes(v.WEB)){let{provider:e,env:r}=await Ae();t[v.WEB]=e,Object.assign(n,r)}if(e.includes(v.MOBILE)){let{provider:e,env:r}=await De();t[v.MOBILE]=e,Object.assign(n,r)}if(e.includes(v.EXTENSION)){let{provider:e,env:r}=await we();t[v.EXTENSION]=e,Object.assign(n,r)}return{providers:t,env:n}},Me=async()=>s([{type:`select`,choices:Object.values(m).map(e=>({title:M(e),value:e})),name:`provider`,message:`What do you want to use for storage?`}],{onCancel:N}),Ne=()=>s([{type:`text`,name:w.env.storage.s3.region,message:`Enter your S3 region`,initial:`us-east-1`},{type:`text`,name:w.env.storage.s3.endpoint,message:`Enter your S3 endpoint`,initial:`https://s3.amazonaws.com`},{type:`text`,name:w.env.storage.s3.bucket,message:`Enter your default S3 bucket name`},{type:`text`,name:w.env.storage.s3.accessKeyId,message:`Enter your S3 access key ID`},{type:`text`,name:w.env.storage.s3.secretAccessKey,message:`Enter your S3 secret access key`}],{onCancel:N}),Pe=async()=>{let{provider:e}=await Me();return{provider:e,env:await Ne()}},Fe=async()=>{try{await t(`node`,[`--version`])}catch{E.error(`Node.js is not installed. Please install Node.js and try again.
3
+ `),E.info(`To install Node.js, visit: ${o.underline(`https://nodejs.org/en/`)}`),process.exit(1)}},Ie=async()=>{try{await t(`pnpm`,[`--version`])}catch{try{await t(`npm`,[`install`,`-g`,`pnpm`])}catch{E.error(`pnpm is not installed. Please install pnpm manually and try again.
4
+ `),E.info(`To install pnpm, visit: ${o.underline(`https://pnpm.io/installation`)}`),process.exit(1)}}},Le=async()=>{try{await t(`docker`,[`--version`])}catch{E.error(`Docker is not installed. Please install Docker and try again.`),E.info(`To install Docker, visit: ${o.underline(`https://docs.docker.com/get-docker/`)}`),process.exit(1)}},Re=async()=>{try{await t(`git`,[`--version`])}catch{E.error(`Git is not installed. Please install Git and try again.`),E.info(`To install Git, visit: ${o.underline(`https://git-scm.com/downloads`)}`),process.exit(1)}},ze=async()=>{let e=r(`Checking prerequisites...
5
+ `).start();try{await Re(),await Fe(),await Ie(),e.succeed(`All prerequisites satisfied, let's start! šŸš€
6
+ `)}catch{e.fail(`Failed to check prerequisites.`),process.exit(1)}},Be=async(e,n)=>{await Le();let i=r(`Starting local services...`).start();try{await t(`pnpm`,[`services:start`,`--`,...n],{cwd:e}),await t(`pnpm`,[`with-env`,`pnpm`,`turbo`,`setup`,n.map(e=>`--filter=${ne[e]}`).join(` `)],{cwd:e}),i.succeed(`Services successfully started!`)}catch(e){i.fail(`Failed to start services!`),E.error(e),process.exit(1)}},Ve=u.object({cwd:u.string()}),He=new e().name(`new`).description(`create a new TurboStarter project`).option(`-c, --cwd <cwd>`,`the working directory. Defaults to the current directory.`,process.cwd()).action(async e=>{try{E.log(`\n${o.bgRedBright(o.white(` TurboStarter `))}\n`);let{name:t}=await Ue(Ve.parse({cwd:i.resolve(e.cwd)}));E.log(`
7
7
  šŸŽ‰ You can now get started. Open the project and just ship it! šŸŽ‰
8
- `),C.log(`> cd ${t}\n> pnpm dev\n`),C.info(`Problems? ${a.underline(`https://turbostarter.dev/docs`)}`)}catch(e){C.break(),re(e)}}),Ee=async e=>{await xe();let t=await De(),n=await $();C.info(`\nLet's configure it!\nYou can skip any step by pressing ${a.bold(`enter`)}.\n`);let r=await W(),o=await q(),s=await H(n),c=await F(n),l=await ge(),f=await pe(n),p={...`env`in r?r.env:{},...s.env,...o.env,...l.env,...c.env,...f.env};C.log(`\nCreating a new TurboStarter project in ${a.greenBright(i(e.cwd,t))}. \n`);let m=await ke(e.cwd,t,n);await J(m),await Me(m,{email:o.provider,storage:l.provider,billing:s.providers,analytics:c.providers,monitoring:f.providers}),await X(m,p),await Ae(m),await je(m);let h=[...r.type===u.LOCAL?[d.DB]:[]];return h.length>0&&await Se(m,h),{name:t,apps:n}},De=async()=>{let e=await o({type:`text`,name:`name`,message:`Enter your project name.`,validate:e=>e.length>0?!0:`Name is required!`},{onCancel:T});return String(e.name)},$=async()=>{for(;;){let e=(await o({type:`multiselect`,name:`apps`,message:`What do you want to ship?`,instructions:!1,choices:[{title:`Web app`,value:g.WEB,selected:!0},{title:`Mobile app`,value:g.MOBILE,selected:!1},{title:`Browser extension`,value:g.EXTENSION,selected:!1}],hint:`You ${a.bold(`must`)} ship a web app, to ensure backend services work.`},{onCancel:T})).apps;if(e.includes(g.WEB))return e;C.error(`You ${a.bold(`must`)} ship a web app, to ensure backend services work.`)}},Oe=async e=>{try{return await t(`ssh`,[`-T`,`git@github.com`],{timeout:5e3}),e.replace(`https://github.com/`,`git@github.com:`)}catch{return e}},ke=async(e,r,a)=>{let o=n(`Cloning repository into ${r}...`).start(),s=i(e,r),c=Object.values(g).filter(e=>!a.includes(e)).map(e=>te[e]).flat();try{return await t(`git`,[`clone`,`-b`,`main`,`--single-branch`,await Oe(S.repository),r],{cwd:e}),c.length&&await Z({cwd:s,paths:c}),o.succeed(`Repository successfully pulled!`),s}catch{o.fail(`Failed to clone TurboStarter! Please try again.`),process.exit(1)}},Ae=async e=>{let r=n(`Configuring Git...`).start();try{await Z({cwd:e,paths:[`.git`]}),await t(`git`,[`init`],{cwd:e}),await t(`git`,[`remote`,`add`,`upstream`,S.repository],{cwd:e}),await t(`git`,[`add`,`.`],{cwd:e}),await t(`git`,[`commit`,`-m`,`Initial commit`],{cwd:e}),r.succeed(`Git successfully configured!`)}catch{r.fail(`Failed to configure Git! Please try again.`),process.exit(1)}},je=async e=>{let r=n(`Installing dependencies...`).start();try{await t(`pnpm`,[`install`],{cwd:e}),r.succeed(`Dependencies successfully installed!`)}catch{r.fail(`Failed to install dependencies! Please try again.`),process.exit(1)}},Me=async(e,t)=>{let r=n(`Updating providers files...`).start();try{t.email&&await Q({cwd:e,paths:x.email.files,pattern:x.email.pattern,value:t.email}),t.storage&&await Q({cwd:e,paths:x.storage.files,pattern:x.storage.pattern,value:t.storage}),t.billing&&Object.keys(t.billing).length>0&&await Promise.all(Object.entries(t.billing).map(([t,n])=>Q({cwd:e,paths:x.billing[t].files,pattern:x.billing[t].pattern,value:n}))),t.analytics&&Object.keys(t.analytics).length>0&&await Promise.all(Object.entries(t.analytics).map(([t,n])=>Q({cwd:e,paths:x.analytics[t].files,pattern:x.analytics[t].pattern,value:n}))),t.monitoring&&Object.keys(t.monitoring).length>0&&await Promise.all(Object.entries(t.monitoring).map(([t,n])=>Q({cwd:e,paths:x.monitoring[t].files,pattern:x.monitoring[t].pattern,value:n}))),r.succeed(`Providers files successfully updated!`)}catch(e){console.error(e),r.fail(`Failed to update providers files! Please try again.`),process.exit(1)}};var Ne=`1.3.1`;process.on(`SIGINT`,()=>process.exit(0)),process.on(`SIGTERM`,()=>process.exit(0));function Pe(){let t=new e().name(`turbostarter`).description(`Your TurboStarter assistant for starting new projects, adding plugins and more.`).version(Ne||`1.0.0`,`-v, --version`,`display the version number`);t.addCommand(Te),t.parse()}Pe();export{};
8
+ `),E.log(`> cd ${t}\n> pnpm dev\n`),E.info(`Problems? ${o.underline(`https://turbostarter.dev/docs`)}`)}catch(e){E.error(e),process.exit(1)}}),Ue=async e=>{await ze();let t=await We(),n=await Ge(),r=await Ke()?await qe(n):void 0;E.log(`\nCreating a new TurboStarter project in ${o.greenBright(a(e.cwd,t))}. \n`);let i=await Je(e.cwd,t,n);await Ye(i,n),await me(i),r&&(await ge(i,r.env),await Ze(i,{email:r.email.provider,storage:r.storage.provider,billing:r.billing.providers,analytics:r.analytics.providers,monitoring:r.monitoring.providers})),await Xe(i);let s=[...!r||r.db.type===f.LOCAL?[p.DB]:[]];return s.length>0&&await Be(i,s),{name:t,apps:n}},We=async()=>{let e=await s({type:`text`,name:`name`,message:`Enter your project name.`,validate:e=>e.length>0?!0:`Name is required!`},{onCancel:N});return String(e.name)},Ge=async()=>{for(;;){let e=(await s({type:`multiselect`,name:`apps`,message:`What do you want to ship?`,instructions:!1,choices:[{title:`Web app`,value:v.WEB,selected:!0},{title:`Mobile app`,value:v.MOBILE,selected:!1},{title:`Browser extension`,value:v.EXTENSION,selected:!1}],hint:`You ${o.bold(`must`)} ship a web app, to ensure backend services work.`},{onCancel:N})).apps;if(e.includes(v.WEB))return e;E.error(`You ${o.bold(`must`)} ship a web app, to ensure backend services work.`)}},Ke=async()=>!!(await s({type:`select`,name:`configure`,message:`Configure all providers now?`,choices:[{title:`Yes, configure now (recommended)`,value:!0,selected:!0},{title:`No, just let me ship, now!`,value:!1}]},{onCancel:N})).configure,qe=async e=>{E.info(`\nLet's configure it!\nYou can skip any step by pressing ${o.bold(`enter`)}.\n`);let t=await ue(),n=await pe(),r=await ce(e),i=await W(e),a=await Pe(),s=await je(e);return{db:t,email:n,billing:r,analytics:i,storage:a,monitoring:s,env:{...`env`in t?t.env:{},...r.env,...n.env,...a.env,...i.env,...s.env}}},Je=async(e,n,i)=>{let o=r(`Cloning repository into ${n}...`).start(),s=a(e,n);try{return await t(`git`,[`clone`,`-b`,`main`,`--single-branch`,await k()?D(w.repository):O(w.repository),n],{cwd:e}),await $(s,i),o.succeed(`Repository successfully pulled!`),s}catch(e){o.fail(`Failed to clone TurboStarter! Please try again.`),E.error(e),process.exit(1)}},$=async(e,t)=>{let r=Object.values(v).filter(e=>!t.includes(e)).map(e=>xe[e]).flat();if(!r.length)return;let i=new ee({skipAddingFilesFromTsConfig:!0});for(let t of r)if(t.action===`remove`&&await ye({cwd:e,path:t.path}),t.action===`modify`){if(_e(t)){let r=await n.readFile(a(e,t.path),`utf8`),i=JSON.parse(r);if(!ae(i,t.schema))continue;let o=t.modify(i);await n.writeFile(a(e,t.path),JSON.stringify(o,null,2))}if(ve(t)){let n=i.addSourceFileAtPath(a(e,t.path));t.modify(n),await n.save()}}},Ye=async(e,n)=>{let i=r(`Configuring Git...`).start(),a=Object.values(v).filter(e=>!n.includes(e));try{await j(await k()?D(w.repository):O(w.repository),{cwd:e}),a.length>0&&await t(`git`,[`commit`,`-am`,`chore: remove unnecessary files after project initialization`],{cwd:e}),i.succeed(`Git successfully configured!`)}catch(e){i.fail(`Failed to configure Git! Please try again.`),E.error(e),process.exit(1)}},Xe=async e=>{let n=r(`Installing dependencies...`).start();try{await t(`pnpm`,[`install`],{cwd:e}),await t(`pnpm`,[`format:fix`],{cwd:e}),n.succeed(`Dependencies successfully installed!`)}catch(e){n.fail(`Failed to install dependencies! Please try again.`),E.error(e),process.exit(1)}},Ze=async(e,t)=>{let n=r(`Updating providers files...`).start();try{t.email&&await Q({cwd:e,paths:C.email.files,pattern:C.email.pattern,value:t.email}),t.storage&&await Q({cwd:e,paths:C.storage.files,pattern:C.storage.pattern,value:t.storage}),t.billing&&Object.keys(t.billing).length>0&&await Promise.all(Object.entries(t.billing).map(([t,n])=>Q({cwd:e,paths:C.billing[t].files,pattern:C.billing[t].pattern,value:n}))),t.analytics&&Object.keys(t.analytics).length>0&&await Promise.all(Object.entries(t.analytics).map(([t,n])=>Q({cwd:e,paths:C.analytics[t].files,pattern:C.analytics[t].pattern,value:n}))),t.monitoring&&Object.keys(t.monitoring).length>0&&await Promise.all(Object.entries(t.monitoring).map(([t,n])=>Q({cwd:e,paths:C.monitoring[t].files,pattern:C.monitoring[t].pattern,value:n}))),n.succeed(`Providers files successfully updated!`)}catch(e){n.fail(`Failed to update providers files! Please try again.`),E.error(e),process.exit(1)}},Qe=u.object({cwd:u.string()}),$e=new e().name(`update`).description(`pull the latest changes from the upstream TurboStarter repository`).option(`-c, --cwd <cwd>`,`the working directory. Defaults to the current directory.`,process.cwd()).action(async e=>{let t=r(`Pulling latest changes from upstream...`).start();try{let n=await et({cwd:Qe.parse({cwd:e.cwd}).cwd});if(n.success){n.alreadyUpToDate?t.succeed(`Already up to date.`):t.succeed(`Successfully pulled latest changes from ${o.cyan(w.repository)}.`);return}`hasConflicts`in n?(t.fail(`Merge conflicts detected.`),E.warn(`\n${n.conflicts.length} conflicting file(s):`),n.conflicts.forEach(e=>{E.log(` - ${e}`)}),E.warn(`
9
+ Please resolve them manually:`),E.log(` 1. Fix the conflicting files`),E.log(` 2. Run: ${o.bold(`git add .`)}`),E.log(` 3. Run: ${o.bold(`git commit`)}`)):(t.fail(`Failed to pull from upstream.`),E.error(`\n${n.reason}`)),process.exit(1)}catch(e){t.fail(`Failed to pull from upstream.`),E.error(P(e)),process.exit(1)}}),et=async({cwd:e})=>{let n=await tt(e);if(!n.valid)return{success:!1,reason:n.reason};if(!await ie({cwd:e}))return{success:!1,reason:`Git working directory has uncommitted changes. Please commit or stash them before pulling upstream updates.`};let r=await A({cwd:e});if(r){if(!re(r,w.repository)){let e=r.startsWith(`git@`)?D(w.repository):O(w.repository);return{success:!1,reason:`Upstream remote points to "${r}" but expected "${e}". Please run: git remote set-url upstream <correct-url>`}}}else{let t=await k()?D(w.repository):O(w.repository);await j(t,{cwd:e}),r=t}await t(`git`,[`fetch`,`upstream`],{cwd:e});try{let{stdout:n}=await t(`git`,[`merge`,`upstream/main`,`--no-edit`],{cwd:e});return{success:!0,alreadyUpToDate:n.includes(`Already up to date`)}}catch(n){let r=P(n);if(!(r.includes(`CONFLICT`)||r.includes(`Automatic merge failed`)))return{success:!1,reason:`Merge failed: ${r}`};let{stdout:i}=await t(`git`,[`diff`,`--name-only`,`--diff-filter=U`],{cwd:e});return{success:!1,hasConflicts:!0,conflicts:i.trim().split(`
10
+ `).map(e=>e.trim()).filter(Boolean)}}},tt=async e=>{let t=i.resolve(e);return(await Promise.all([`package.json`,`pnpm-workspace.yaml`,`turbo.json`,`apps/web/package.json`,`packages/api/package.json`].map(async e=>{try{await n.access(i.join(t,e));return}catch{return e}}))).filter(Boolean).length>0?{valid:!1,reason:`This does not appear to be a TurboStarter project root. Please run this command from your project root (or pass --cwd).`}:{valid:!0}},nt=new e().name(`project`).description(`manage your TurboStarter project`).addCommand($e);var rt=`1.4.0`;process.on(`SIGINT`,()=>process.exit(0)),process.on(`SIGTERM`,()=>process.exit(0));function it(){let t=new e().name(`turbostarter`).description(`Your TurboStarter assistant for starting new projects, adding plugins and more.`).version(rt||`1.0.0`,`-v, --version`,`display the version number`);t.addCommand(He),t.addCommand(nt),t.parse()}it();export{};
9
11
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["colors","env","env","path","env","path","env","packageInfo.version"],"sources":["../src/config.ts","../src/utils/logger.ts","../src/utils/handle-error.ts","../src/utils/index.ts","../src/commands/new/config/analytics/extension.ts","../src/commands/new/config/analytics/mobile.ts","../src/commands/new/config/analytics/web.ts","../src/commands/new/config/analytics/index.ts","../src/commands/new/config/billing/mobile.ts","../src/commands/new/config/billing/web.ts","../src/commands/new/config/billing/index.ts","../src/commands/new/config/db.ts","../src/commands/new/config/email.ts","../src/commands/new/config/env.ts","../src/commands/new/config/monitoring/extension.ts","../src/commands/new/config/monitoring/mobile.ts","../src/commands/new/config/monitoring/web.ts","../src/commands/new/config/monitoring/index.ts","../src/commands/new/config/storage.ts","../src/commands/new/prerequisites.ts","../src/commands/new/services.ts","../src/utils/file.ts","../src/commands/new/index.ts","../package.json","../src/index.ts"],"sourcesContent":["type Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nexport const ServiceType = {\n LOCAL: \"local\",\n CLOUD: \"cloud\",\n} as const;\n\nexport const Service = {\n DB: \"db\",\n};\n\nexport const StorageProvider = {\n S3: \"s3\",\n} as const;\n\nexport const EmailProvider = {\n RESEND: \"resend\",\n SENDGRID: \"sendgrid\",\n POSTMARK: \"postmark\",\n PLUNK: \"plunk\",\n NODEMAILER: \"nodemailer\",\n} as const;\n\nexport const EnvPath = {\n ROOT: \"./\",\n WEB: \"./apps/web\",\n MOBILE: \"./apps/mobile\",\n EXTENSION: \"./apps/extension\",\n} as const;\n\nexport const EnvFile = {\n EXAMPLE: \".env.example\",\n LOCAL: \".env.local\",\n} as const;\n\nexport const App = {\n WEB: \"web\",\n MOBILE: \"mobile\",\n EXTENSION: \"extension\",\n} as const;\n\nexport const BillingProvider = {\n [App.WEB]: {\n STRIPE: \"stripe\",\n LEMON_SQUEEZY: \"lemon-squeezy\",\n POLAR: \"polar\",\n },\n [App.MOBILE]: {\n REVENUECAT: \"revenuecat\",\n SUPERWALL: \"superwall\",\n },\n} as const;\n\nexport const AnalyticsProvider = {\n [App.WEB]: {\n GOOGLE_ANALYTICS: \"google-analytics\",\n MIXPANEL: \"mixpanel\",\n OPEN_PANEL: \"open-panel\",\n PLAUSIBLE: \"plausible\",\n POSTHOG: \"posthog\",\n UMAMI: \"umami\",\n VEMETRIC: \"vemetric\",\n VERCEL: \"vercel\",\n },\n [App.MOBILE]: {\n GOOGLE_ANALYTICS: \"google-analytics\",\n MIXPANEL: \"mixpanel\",\n POSTHOG: \"posthog\",\n },\n [App.EXTENSION]: {\n GOOGLE_ANALYTICS: \"google-analytics\",\n POSTHOG: \"posthog\",\n },\n} as const;\n\nexport const MonitoringProvider = {\n [App.WEB]: {\n SENTRY: \"sentry\",\n POSTHOG: \"posthog\",\n },\n [App.MOBILE]: {\n SENTRY: \"sentry\",\n POSTHOG: \"posthog\",\n },\n [App.EXTENSION]: {\n SENTRY: \"sentry\",\n POSTHOG: \"posthog\",\n },\n} as const;\n\nexport type ServiceType = (typeof ServiceType)[keyof typeof ServiceType];\nexport type Service = (typeof Service)[keyof typeof Service];\nexport type StorageProvider =\n (typeof StorageProvider)[keyof typeof StorageProvider];\nexport type EmailProvider = (typeof EmailProvider)[keyof typeof EmailProvider];\nexport type EnvPath = (typeof EnvPath)[keyof typeof EnvPath];\nexport type EnvFile = (typeof EnvFile)[keyof typeof EnvFile];\nexport type App = (typeof App)[keyof typeof App];\n\nexport type BillingProvider = {\n [K in Mutable<\n keyof typeof BillingProvider\n >]: (typeof BillingProvider)[K][keyof (typeof BillingProvider)[K]];\n};\nexport type AnalyticsProvider = {\n [K in Mutable<\n keyof typeof AnalyticsProvider\n >]: (typeof AnalyticsProvider)[K][keyof (typeof AnalyticsProvider)[K]];\n};\nexport type MonitoringProvider = {\n [K in Mutable<\n keyof typeof MonitoringProvider\n >]: (typeof MonitoringProvider)[K][keyof (typeof MonitoringProvider)[K]];\n};\n\nconst env = {\n db: {\n url: \"DATABASE_URL\",\n },\n billing: {\n [App.WEB]: {\n [BillingProvider[App.WEB].STRIPE]: {\n secretKey: \"STRIPE_SECRET_KEY\",\n webhookSecret: \"STRIPE_WEBHOOK_SECRET\",\n },\n [BillingProvider[App.WEB].LEMON_SQUEEZY]: {\n apiKey: \"LEMON_SQUEEZY_API_KEY\",\n signingSecret: \"LEMON_SQUEEZY_SIGNING_SECRET\",\n storeId: \"LEMON_SQUEEZY_STORE_ID\",\n },\n [BillingProvider[App.WEB].POLAR]: {\n accessToken: \"POLAR_ACCESS_TOKEN\",\n webhookSecret: \"POLAR_WEBHOOK_SECRET\",\n organizationSlug: \"POLAR_ORGANIZATION_SLUG\",\n },\n },\n [App.MOBILE]: {\n [BillingProvider[App.MOBILE].REVENUECAT]: {\n appleApiKey: \"EXPO_PUBLIC_REVENUECAT_APPLE_API_KEY\",\n googleApiKey: \"EXPO_PUBLIC_REVENUECAT_GOOGLE_API_KEY\",\n webhookSecret: \"REVENUECAT_WEBHOOK_SECRET\",\n apiKey: \"REVENUECAT_API_KEY\",\n },\n [BillingProvider[App.MOBILE].SUPERWALL]: {\n appleApiKey: \"EXPO_PUBLIC_SUPERWALL_APPLE_API_KEY\",\n googleApiKey: \"EXPO_PUBLIC_SUPERWALL_GOOGLE_API_KEY\",\n webhookSecret: \"SUPERWALL_WEBHOOK_SECRET\",\n },\n },\n },\n email: {\n [EmailProvider.RESEND]: {\n apiKey: \"RESEND_API_KEY\",\n },\n [EmailProvider.SENDGRID]: {\n apiKey: \"SENDGRID_API_KEY\",\n },\n [EmailProvider.PLUNK]: {\n apiKey: \"PLUNK_API_KEY\",\n },\n [EmailProvider.POSTMARK]: {\n apiKey: \"POSTMARK_API_KEY\",\n },\n [EmailProvider.NODEMAILER]: {\n user: \"NODEMAILER_USER\",\n password: \"NODEMAILER_PASSWORD\",\n host: \"NODEMAILER_HOST\",\n port: \"NODEMAILER_PORT\",\n },\n },\n storage: {\n [StorageProvider.S3]: {\n region: \"S3_REGION\",\n bucket: \"S3_BUCKET\",\n endpoint: \"S3_ENDPOINT\",\n accessKeyId: \"S3_ACCESS_KEY_ID\",\n secretAccessKey: \"S3_SECRET_ACCESS_KEY\",\n },\n },\n analytics: {\n [App.WEB]: {\n [AnalyticsProvider[App.WEB].GOOGLE_ANALYTICS]: {\n measurementId: \"NEXT_PUBLIC_GOOGLE_ANALYTICS_MEASUREMENT_ID\",\n secret: \"GOOGLE_ANALYTICS_SECRET\",\n },\n [AnalyticsProvider[App.WEB].MIXPANEL]: {\n token: \"NEXT_PUBLIC_MIXPANEL_TOKEN\",\n },\n [AnalyticsProvider[App.WEB].OPEN_PANEL]: {\n clientId: \"NEXT_PUBLIC_OPEN_PANEL_CLIENT_ID\",\n secret: \"OPEN_PANEL_SECRET\",\n },\n [AnalyticsProvider[App.WEB].PLAUSIBLE]: {\n domain: \"NEXT_PUBLIC_PLAUSIBLE_DOMAIN\",\n host: \"NEXT_PUBLIC_PLAUSIBLE_HOST\",\n },\n [AnalyticsProvider[App.WEB].POSTHOG]: {\n key: \"NEXT_PUBLIC_POSTHOG_KEY\",\n host: \"NEXT_PUBLIC_POSTHOG_HOST\",\n },\n [AnalyticsProvider[App.WEB].UMAMI]: {\n host: \"NEXT_PUBLIC_UMAMI_HOST\",\n websiteId: \"NEXT_PUBLIC_UMAMI_WEBSITE_ID\",\n apiHost: \"UMAMI_API_HOST\",\n apiKey: \"UMAMI_API_KEY\",\n },\n [AnalyticsProvider[App.WEB].VEMETRIC]: {\n token: \"NEXT_PUBLIC_VEMETRIC_PROJECT_TOKEN\",\n },\n [AnalyticsProvider[App.WEB].VERCEL]: {},\n },\n [App.MOBILE]: {\n [AnalyticsProvider[App.MOBILE].GOOGLE_ANALYTICS]: {},\n [AnalyticsProvider[App.MOBILE].MIXPANEL]: {\n token: \"EXPO_PUBLIC_MIXPANEL_TOKEN\",\n },\n [AnalyticsProvider[App.MOBILE].POSTHOG]: {\n key: \"EXPO_PUBLIC_POSTHOG_KEY\",\n host: \"EXPO_PUBLIC_POSTHOG_HOST\",\n },\n },\n [App.EXTENSION]: {\n [AnalyticsProvider[App.EXTENSION].GOOGLE_ANALYTICS]: {\n measurementId: \"VITE_GOOGLE_ANALYTICS_MEASUREMENT_ID\",\n secret: \"VITE_GOOGLE_ANALYTICS_SECRET\",\n },\n [AnalyticsProvider[App.EXTENSION].POSTHOG]: {\n key: \"VITE_POSTHOG_KEY\",\n host: \"VITE_POSTHOG_HOST\",\n },\n },\n },\n monitoring: {\n [App.WEB]: {\n [MonitoringProvider[App.WEB].SENTRY]: {\n dsn: \"NEXT_PUBLIC_SENTRY_DSN\",\n },\n [MonitoringProvider[App.WEB].POSTHOG]: {\n key: \"NEXT_PUBLIC_POSTHOG_KEY\",\n host: \"NEXT_PUBLIC_POSTHOG_HOST\",\n },\n },\n [App.MOBILE]: {\n [MonitoringProvider[App.MOBILE].SENTRY]: {\n dsn: \"EXPO_PUBLIC_SENTRY_DSN\",\n },\n [MonitoringProvider[App.MOBILE].POSTHOG]: {\n key: \"EXPO_PUBLIC_POSTHOG_KEY\",\n host: \"EXPO_PUBLIC_POSTHOG_HOST\",\n },\n },\n [App.EXTENSION]: {\n [MonitoringProvider[App.EXTENSION].SENTRY]: {\n dsn: \"VITE_SENTRY_DSN\",\n },\n [MonitoringProvider[App.EXTENSION].POSTHOG]: {\n key: \"VITE_POSTHOG_KEY\",\n host: \"VITE_POSTHOG_HOST\",\n },\n },\n },\n} as const;\n\nexport const envInPaths = {\n [EnvPath.ROOT]: [env.db.url],\n [EnvPath.WEB]: [\n env.email.resend.apiKey,\n env.email.sendgrid.apiKey,\n env.email.plunk.apiKey,\n env.email.postmark.apiKey,\n env.email.nodemailer.user,\n env.email.nodemailer.password,\n env.storage.s3.accessKeyId,\n env.storage.s3.secretAccessKey,\n env.billing[App.WEB].stripe.secretKey,\n env.billing[App.WEB].stripe.webhookSecret,\n env.billing[App.WEB][\"lemon-squeezy\"].apiKey,\n env.billing[App.WEB][\"lemon-squeezy\"].signingSecret,\n env.billing[App.WEB][\"lemon-squeezy\"].storeId,\n env.billing[App.WEB].polar.accessToken,\n env.billing[App.WEB].polar.webhookSecret,\n env.billing[App.WEB].polar.organizationSlug,\n env.billing[App.MOBILE].revenuecat.webhookSecret,\n env.billing[App.MOBILE].revenuecat.apiKey,\n env.billing[App.MOBILE].superwall.webhookSecret,\n env.analytics[App.WEB][\"google-analytics\"].measurementId,\n env.analytics[App.WEB][\"google-analytics\"].secret,\n env.analytics[App.WEB].mixpanel.token,\n env.analytics[App.WEB][\"open-panel\"].clientId,\n env.analytics[App.WEB][\"open-panel\"].secret,\n env.analytics[App.WEB].plausible.domain,\n env.analytics[App.WEB].plausible.host,\n env.analytics[App.WEB].posthog.key,\n env.analytics[App.WEB].posthog.host,\n env.analytics[App.WEB].umami.host,\n env.analytics[App.WEB].umami.websiteId,\n env.analytics[App.WEB].umami.apiHost,\n env.analytics[App.WEB].umami.apiKey,\n env.analytics[App.WEB].vemetric.token,\n env.monitoring[App.WEB].sentry.dsn,\n env.monitoring[App.WEB].posthog.key,\n env.monitoring[App.WEB].posthog.host,\n ],\n [EnvPath.MOBILE]: [\n env.billing[App.MOBILE].revenuecat.appleApiKey,\n env.billing[App.MOBILE].revenuecat.googleApiKey,\n env.billing[App.MOBILE].superwall.appleApiKey,\n env.billing[App.MOBILE].superwall.googleApiKey,\n env.analytics[App.MOBILE].mixpanel.token,\n env.analytics[App.MOBILE].posthog.key,\n env.analytics[App.MOBILE].posthog.host,\n env.monitoring[App.MOBILE].sentry.dsn,\n ],\n [EnvPath.EXTENSION]: [\n env.analytics[App.EXTENSION][\"google-analytics\"].measurementId,\n env.analytics[App.EXTENSION][\"google-analytics\"].secret,\n env.analytics[App.EXTENSION].posthog.key,\n env.analytics[App.EXTENSION].posthog.host,\n env.monitoring[App.EXTENSION].sentry.dsn,\n ],\n};\n\nexport const appSpecificFiles = {\n [App.WEB]: [],\n [App.MOBILE]: [\n \"apps/mobile\",\n \"packages/analytics/mobile\",\n \"packages/billing/mobile\",\n \"packages/monitoring/mobile\",\n \"packages/ui/mobile\",\n \".github/workflows/publish-mobile.yml\",\n ],\n [App.EXTENSION]: [\n \"apps/extension\",\n \"packages/analytics/extension\",\n \"packages/monitoring/extension\",\n \".github/workflows/publish-extension.yml\",\n ],\n};\n\nexport const providerConfigFiles = {\n email: {\n files: [\n \"packages/email/src/providers/index.ts\",\n \"packages/email/src/providers/env.ts\",\n ],\n pattern: new RegExp(`(${Object.values(EmailProvider).join(\"|\")})`, \"gi\"),\n },\n storage: {\n files: [\n \"packages/storage/src/providers/index.ts\",\n \"packages/storage/src/providers/env.ts\",\n ],\n pattern: new RegExp(`(${Object.values(StorageProvider).join(\"|\")})`, \"gi\"),\n },\n billing: {\n [App.WEB]: {\n files: [\n \"packages/billing/web/src/providers/index.ts\",\n \"packages/billing/web/src/providers/env.ts\",\n ],\n pattern: new RegExp(\n `(${Object.values(BillingProvider[App.WEB]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.MOBILE]: {\n files: [\n \"packages/billing/mobile/src/providers/index.ts\",\n \"packages/billing/mobile/src/providers/env.ts\",\n \"packages/billing/mobile/src/providers/server.ts\",\n ],\n pattern: new RegExp(\n `(${Object.values(BillingProvider[App.MOBILE]).join(\"|\")})`,\n \"gi\",\n ),\n },\n },\n analytics: {\n [App.WEB]: {\n files: [\n \"packages/analytics/web/src/providers/index.tsx\",\n \"packages/analytics/web/src/providers/server.ts\",\n \"packages/analytics/web/src/providers/env.ts\",\n ],\n pattern: new RegExp(\n `(${Object.values(AnalyticsProvider[App.WEB]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.MOBILE]: {\n files: [\"packages/analytics/mobile/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(AnalyticsProvider[App.MOBILE]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.EXTENSION]: {\n files: [\"packages/analytics/extension/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(AnalyticsProvider[App.EXTENSION]).join(\"|\")})`,\n \"gi\",\n ),\n },\n },\n monitoring: {\n [App.WEB]: {\n files: [\"packages/monitoring/web/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(MonitoringProvider[App.WEB]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.MOBILE]: {\n files: [\"packages/monitoring/mobile/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(MonitoringProvider[App.MOBILE]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.EXTENSION]: {\n files: [\"packages/monitoring/extension/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(MonitoringProvider[App.EXTENSION]).join(\"|\")})`,\n \"gi\",\n ),\n },\n },\n};\n\nexport const servicesPackages: Record<Service, string> = {\n [Service.DB]: \"@workspace/db\",\n};\n\nexport const config = {\n name: \"TurboStarter\",\n repository: \"https://github.com/turbostarter/core.git\",\n env,\n} as const;\n","import colors from \"picocolors\";\n\nexport const logger = {\n error(log: string) {\n console.log(colors.red(log));\n },\n warn(log: string) {\n console.log(colors.yellow(log));\n },\n info(log: string) {\n console.log(colors.cyan(log));\n },\n success(log: string) {\n console.log(colors.green(log));\n },\n log(log: string) {\n console.log(log);\n },\n break() {\n console.log(\"\");\n },\n};\n","import { logger } from \"./logger\";\n\nexport function handleError(error: unknown) {\n if (typeof error === \"string\") {\n logger.error(error);\n process.exit(1);\n }\n\n if (error instanceof Error) {\n logger.error(error.message);\n process.exit(1);\n }\n\n logger.error(\"Something went wrong. Please try again.\");\n process.exit(1);\n}\n","import _ from \"lodash\";\n\nimport { logger } from \"~/utils/logger\";\n\nexport const getLabel = (value: string) => {\n return value\n .split(\"-\")\n .map((word) => _.capitalize(word))\n .join(\" \");\n};\n\nexport const onCancel = () => {\n logger.break();\n logger.error(\"Operation cancelled.\");\n process.exit(0);\n};\n\nexport * from \"./handle-error\";\nexport * from \"./logger\";\n","import prompts from \"prompts\";\n\nimport { AnalyticsProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getAnalyticsExtensionProvider = async (): Promise<{\n provider: AnalyticsProvider[typeof App.EXTENSION];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(AnalyticsProvider[App.EXTENSION]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for extension analytics?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getAnalyticsExtensionProviderConfig = async (\n provider: AnalyticsProvider[typeof App.EXTENSION],\n) => {\n switch (provider) {\n case AnalyticsProvider[App.EXTENSION].GOOGLE_ANALYTICS:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION][\"google-analytics\"]\n .measurementId,\n message: \"Enter your Google Analytics measurement ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION][\"google-analytics\"]\n .secret,\n message: \"Enter your Google Analytics secret\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.EXTENSION].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getAnalyticsExtensionConfig = async () => {\n const { provider } = await getAnalyticsExtensionProvider();\n const env = await getAnalyticsExtensionProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { AnalyticsProvider, App, config } from \"~/config\";\nimport { getLabel, logger, onCancel } from \"~/utils\";\n\nconst getAnalyticsMobileProvider = async (): Promise<{\n provider: AnalyticsProvider[typeof App.MOBILE];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(AnalyticsProvider[App.MOBILE]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for mobile analytics?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getAnalyticsMobileProviderConfig = async (\n provider: AnalyticsProvider[typeof App.MOBILE],\n) => {\n switch (provider) {\n case AnalyticsProvider[App.MOBILE].GOOGLE_ANALYTICS:\n logger.info(\n \"Check how to configure Google Analytics for mobile at https://www.turbostarter.dev/docs/mobile/analytics/configuration#google-analytics\",\n );\n return {};\n case AnalyticsProvider[App.MOBILE].MIXPANEL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.MOBILE].mixpanel.token,\n message: \"Enter your Mixpanel token\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.MOBILE].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.MOBILE].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.MOBILE].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getAnalyticsMobileConfig = async () => {\n const { provider } = await getAnalyticsMobileProvider();\n const env = await getAnalyticsMobileProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { AnalyticsProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getAnalyticsWebProvider = async (): Promise<{\n provider: AnalyticsProvider[typeof App.WEB];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(AnalyticsProvider[App.WEB]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for web analytics?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getAnalyticsWebProviderConfig = async (\n provider: AnalyticsProvider[typeof App.WEB],\n) => {\n switch (provider) {\n case AnalyticsProvider[App.WEB].GOOGLE_ANALYTICS:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"google-analytics\"]\n .measurementId,\n message: \"Enter your Google Analytics measurement ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"google-analytics\"].secret,\n message: \"Enter your Google Analytics secret\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].MIXPANEL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].mixpanel.token,\n message: \"Enter your Mixpanel token\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].OPEN_PANEL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"open-panel\"].clientId,\n message: \"Enter your OpenPanel client ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"open-panel\"].secret,\n message: \"Enter your OpenPanel secret\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].PLAUSIBLE:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].plausible.domain,\n message: \"Enter your Plausible domain\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].plausible.host,\n message: \"Enter your Plausible host\",\n initial: \"https://plausible.io\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].UMAMI:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.host,\n message: \"Enter your Umami host\",\n initial: \"https://cloud.umami.is\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.websiteId,\n message: \"Enter your Umami website ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.apiHost,\n message: \"Enter your Umami API host\",\n initial: \"https://api-gateway.umami.dev\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.apiKey,\n message: \"Enter your Umami API key\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].VEMETRIC:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].vemetric.token,\n message: \"Enter your Vemetric project token\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].VERCEL:\n return {};\n }\n};\n\nexport const getAnalyticsWebConfig = async () => {\n const { provider } = await getAnalyticsWebProvider();\n const env = await getAnalyticsWebProviderConfig(provider);\n\n return { provider, env };\n};\n","import { App } from \"~/config\";\n\nimport { getAnalyticsExtensionConfig } from \"./extension\";\nimport { getAnalyticsMobileConfig } from \"./mobile\";\nimport { getAnalyticsWebConfig } from \"./web\";\n\nimport type { AnalyticsProvider } from \"~/config\";\n\nexport const getAnalyticsConfig = async (apps: App[]) => {\n const providers: Partial<AnalyticsProvider> = {};\n const env: Record<string, string> = {};\n\n if (apps.includes(App.WEB)) {\n const { provider, env: webEnv } = await getAnalyticsWebConfig();\n providers[App.WEB] = provider;\n Object.assign(env, webEnv);\n }\n\n if (apps.includes(App.MOBILE)) {\n const { provider, env: mobileEnv } = await getAnalyticsMobileConfig();\n providers[App.MOBILE] = provider;\n Object.assign(env, mobileEnv);\n }\n\n if (apps.includes(App.EXTENSION)) {\n const { provider, env: extensionEnv } = await getAnalyticsExtensionConfig();\n providers[App.EXTENSION] = provider;\n Object.assign(env, extensionEnv);\n }\n\n return { providers, env };\n};\n","import prompts from \"prompts\";\n\nimport { App, BillingProvider, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nimport type { BillingProvider as BillingProviderType } from \"~/config\";\n\nconst getBillingMobileProvider = async (): Promise<{\n provider: BillingProviderType[typeof App.MOBILE];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(BillingProvider[App.MOBILE]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for mobile billing?\",\n },\n ],\n { onCancel },\n );\n};\n\nconst getBillingMobileProviderConfig = async (\n provider: BillingProviderType[typeof App.MOBILE],\n) => {\n switch (provider) {\n case BillingProvider[App.MOBILE].REVENUECAT:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.appleApiKey,\n message: \"Enter your RevenueCat Apple API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.googleApiKey,\n message: \"Enter your RevenueCat Google API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.webhookSecret,\n message: \"Enter your RevenueCat webhook secret\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.apiKey,\n message: \"Enter your RevenueCat API key\",\n },\n ],\n { onCancel },\n );\n case BillingProvider[App.MOBILE].SUPERWALL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].superwall.appleApiKey,\n message: \"Enter your Superwall Apple API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].superwall.googleApiKey,\n message: \"Enter your Superwall Google API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].superwall.webhookSecret,\n message: \"Enter your Superwall webhook secret\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getBillingMobileConfig = async () => {\n const { provider } = await getBillingMobileProvider();\n const env = await getBillingMobileProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { App, BillingProvider, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nimport type { BillingProvider as BillingProviderType } from \"~/config\";\n\nconst getBillingWebProvider = async (): Promise<{\n provider: BillingProviderType[typeof App.WEB];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(BillingProvider[App.WEB]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for web billing?\",\n },\n ],\n { onCancel },\n );\n};\n\nconst getBillingWebProviderConfig = async (\n provider: BillingProviderType[typeof App.WEB],\n) => {\n switch (provider) {\n case BillingProvider[App.WEB].STRIPE:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.WEB].stripe.secretKey,\n message: \"Enter your Stripe secret key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB].stripe.webhookSecret,\n message: \"Enter your Stripe webhook secret\",\n },\n ],\n { onCancel },\n );\n case BillingProvider[App.WEB].LEMON_SQUEEZY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.WEB][\"lemon-squeezy\"].storeId,\n message: \"Enter your Lemon Squeezy store ID\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB][\"lemon-squeezy\"].apiKey,\n message: \"Enter your Lemon Squeezy API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB][\"lemon-squeezy\"].signingSecret,\n message: \"Enter your Lemon Squeezy signing secret\",\n },\n ],\n { onCancel },\n );\n case BillingProvider[App.WEB].POLAR:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.WEB].polar.accessToken,\n message: \"Enter your Polar access token\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB].polar.webhookSecret,\n message: \"Enter your Polar webhook secret\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB].polar.organizationSlug,\n message: \"Enter your Polar organization slug\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getBillingWebConfig = async () => {\n const { provider } = await getBillingWebProvider();\n const env = await getBillingWebProviderConfig(provider);\n\n return { provider, env };\n};\n","import { App } from \"~/config\";\n\nimport { getBillingMobileConfig } from \"./mobile\";\nimport { getBillingWebConfig } from \"./web\";\n\nimport type { BillingProvider } from \"~/config\";\n\nexport const getBillingConfig = async (apps: App[]) => {\n const providers: Partial<BillingProvider> = {};\n const env: Record<string, string> = {};\n\n if (apps.includes(App.WEB)) {\n const { provider, env: webEnv } = await getBillingWebConfig();\n providers[App.WEB] = provider;\n Object.assign(env, webEnv);\n }\n\n if (apps.includes(App.MOBILE)) {\n const { provider, env: mobileEnv } = await getBillingMobileConfig();\n providers[App.MOBILE] = provider;\n Object.assign(env, mobileEnv);\n }\n\n return { providers, env };\n};\n","import prompts from \"prompts\";\n\nimport { config, ServiceType } from \"~/config\";\nimport { onCancel } from \"~/utils\";\n\nconst getDatabaseCloudConfig = async () => {\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.db.url,\n message: \"Enter your database URL\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nexport const getDatabaseConfig = async () => {\n const response = await prompts(\n [\n {\n type: \"select\",\n name: \"type\",\n message: \"How do you want to use database?\",\n choices: [\n {\n title: \"Local (powered by Docker)\",\n value: ServiceType.LOCAL,\n selected: true,\n },\n {\n title: `Cloud`,\n value: ServiceType.CLOUD,\n },\n ],\n },\n ],\n {\n onCancel,\n },\n );\n\n if (response.type === ServiceType.CLOUD) {\n const dbConfig = await getDatabaseCloudConfig();\n\n return { ...response, env: dbConfig };\n }\n\n return response;\n};\n","import prompts from \"prompts\";\n\nimport { EmailProvider, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getEmailProvider = async (): Promise<{\n provider: EmailProvider;\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n name: \"provider\",\n message: \"What do you want to use for emails?\",\n choices: Object.values(EmailProvider).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getEmailProviderConfig = async (provider: EmailProvider) => {\n switch (provider) {\n case EmailProvider.RESEND:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.resend.apiKey,\n message: \"Enter your Resend API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.SENDGRID:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.sendgrid.apiKey,\n message: \"Enter your Sendgrid API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.PLUNK:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.plunk.apiKey,\n message: \"Enter your Plunk API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.POSTMARK:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.postmark.apiKey,\n message: \"Enter your Postmark API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.NODEMAILER:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.nodemailer.user,\n message: \"Enter your Nodemailer user\",\n },\n {\n type: \"text\",\n name: config.env.email.nodemailer.password,\n message: \"Enter your Nodemailer user password\",\n },\n {\n type: \"text\",\n name: config.env.email.nodemailer.host,\n message: \"Enter your Nodemailer host\",\n },\n {\n type: \"number\",\n name: config.env.email.nodemailer.port,\n message: \"Enter your Nodemailer port\",\n },\n ],\n {\n onCancel,\n },\n );\n }\n};\n\nexport const getEmailConfig = async () => {\n const { provider } = await getEmailProvider();\n const env = await getEmailProviderConfig(provider);\n\n return { provider, env };\n};\n","import { promises } from \"fs\";\nimport _ from \"lodash\";\nimport ora from \"ora\";\nimport { join } from \"path\";\n\nimport { EnvFile, envInPaths, EnvPath } from \"~/config\";\nimport { logger } from \"~/utils\";\n\nexport const prepareEnvironment = async (projectDir: string) => {\n try {\n await Promise.allSettled(\n Object.values(EnvPath).map(async (path) => {\n const cwd = join(projectDir, path);\n await promises.copyFile(\n join(cwd, EnvFile.EXAMPLE),\n join(cwd, EnvFile.LOCAL),\n );\n }),\n );\n } catch {\n logger.error(\"Failed to prepare environment!\");\n process.exit(1);\n }\n};\n\nexport const setEnvironmentVariable = async (\n projectDir: string,\n key: string,\n value: string,\n) => {\n if (!value) {\n return;\n }\n\n const paths = _.keys(\n _.pickBy(envInPaths, (values) => _.includes(values, key)),\n );\n\n for (const path of paths) {\n const cwd = join(projectDir, path);\n const envFilePath = join(cwd, EnvFile.LOCAL);\n\n const content = await promises.readFile(envFilePath, \"utf8\");\n\n const regex = new RegExp(`^${key}=.*`, \"gm\");\n\n if (regex.test(content)) {\n await promises.writeFile(\n envFilePath,\n content.replace(regex, `${key}=\"${value}\"`),\n );\n } else {\n await promises.appendFile(envFilePath, `\\n${key}=\"${value}\"`);\n }\n }\n};\n\nexport const setEnvironmentVariables = async (\n projectDir: string,\n variables: Record<string, string>,\n) => {\n const spinner = ora(`Setting environment variables...`).start();\n\n try {\n for (const [key, value] of Object.entries(variables)) {\n await setEnvironmentVariable(projectDir, key, value);\n }\n\n spinner.succeed(\"Environment variables successfully set!\");\n } catch {\n logger.error(\"Failed to set environment variables!\");\n process.exit(1);\n }\n};\n","import prompts from \"prompts\";\n\nimport { MonitoringProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getMonitoringExtensionProvider = async (): Promise<{\n provider: MonitoringProvider[typeof App.EXTENSION];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(MonitoringProvider[App.EXTENSION]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for extension monitoring?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getMonitoringExtensionProviderConfig = async (\n provider: MonitoringProvider[typeof App.EXTENSION],\n) => {\n switch (provider) {\n case MonitoringProvider[App.EXTENSION].SENTRY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.EXTENSION].sentry.dsn,\n message: \"Enter your Sentry DSN\",\n },\n ],\n { onCancel },\n );\n case MonitoringProvider[App.EXTENSION].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.EXTENSION].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.monitoring[App.EXTENSION].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getMonitoringExtensionConfig = async () => {\n const { provider } = await getMonitoringExtensionProvider();\n const env = await getMonitoringExtensionProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { MonitoringProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getMonitoringMobileProvider = async (): Promise<{\n provider: MonitoringProvider[typeof App.MOBILE];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(MonitoringProvider[App.MOBILE]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for mobile monitoring?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getMonitoringMobileProviderConfig = async (\n provider: MonitoringProvider[typeof App.MOBILE],\n) => {\n switch (provider) {\n case MonitoringProvider[App.MOBILE].SENTRY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.MOBILE].sentry.dsn,\n message: \"Enter your Sentry DSN\",\n },\n ],\n { onCancel },\n );\n case MonitoringProvider[App.MOBILE].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.MOBILE].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.monitoring[App.MOBILE].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getMonitoringMobileConfig = async () => {\n const { provider } = await getMonitoringMobileProvider();\n const env = await getMonitoringMobileProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { MonitoringProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getMonitoringWebProvider = async (): Promise<{\n provider: MonitoringProvider[typeof App.WEB];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(MonitoringProvider[App.WEB]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for web monitoring?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getMonitoringWebProviderConfig = async (\n provider: MonitoringProvider[typeof App.WEB],\n) => {\n switch (provider) {\n case MonitoringProvider[App.WEB].SENTRY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.WEB].sentry.dsn,\n message: \"Enter your Sentry DSN\",\n },\n ],\n { onCancel },\n );\n case MonitoringProvider[App.WEB].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.WEB].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.monitoring[App.WEB].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getMonitoringWebConfig = async () => {\n const { provider } = await getMonitoringWebProvider();\n const env = await getMonitoringWebProviderConfig(provider);\n\n return { provider, env };\n};\n","import { App } from \"~/config\";\n\nimport { getMonitoringExtensionConfig } from \"./extension\";\nimport { getMonitoringMobileConfig } from \"./mobile\";\nimport { getMonitoringWebConfig } from \"./web\";\n\nimport type { MonitoringProvider } from \"~/config\";\n\nexport const getMonitoringConfig = async (apps: App[]) => {\n const providers: Partial<MonitoringProvider> = {};\n const env: Record<string, string> = {};\n\n if (apps.includes(App.WEB)) {\n const { provider, env: webEnv } = await getMonitoringWebConfig();\n providers[App.WEB] = provider;\n Object.assign(env, webEnv);\n }\n\n if (apps.includes(App.MOBILE)) {\n const { provider, env: mobileEnv } = await getMonitoringMobileConfig();\n providers[App.MOBILE] = provider;\n Object.assign(env, mobileEnv);\n }\n\n if (apps.includes(App.EXTENSION)) {\n const { provider, env: extensionEnv } =\n await getMonitoringExtensionConfig();\n providers[App.EXTENSION] = provider;\n Object.assign(env, extensionEnv);\n }\n\n return { providers, env };\n};\n","import prompts from \"prompts\";\n\nimport { config, StorageProvider } from \"~/config\";\nimport { onCancel } from \"~/utils\";\nimport { getLabel } from \"~/utils\";\n\nconst getStorageProvider = async (): Promise<{\n provider: StorageProvider;\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(StorageProvider).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for storage?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getStorageProviderConfig = () => {\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.storage.s3.region,\n message: \"Enter your S3 region\",\n initial: \"us-east-1\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.endpoint,\n message: \"Enter your S3 endpoint\",\n initial: \"https://s3.amazonaws.com\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.bucket,\n message: \"Enter your default S3 bucket name\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.accessKeyId,\n message: \"Enter your S3 access key ID\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.secretAccessKey,\n message: \"Enter your S3 secret access key\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nexport const getStorageConfig = async () => {\n const { provider } = await getStorageProvider();\n const env = await getStorageProviderConfig();\n\n return { provider, env };\n};\n","import { execa } from \"execa\";\nimport ora from \"ora\";\nimport color from \"picocolors\";\n\nimport { logger } from \"~/utils/logger\";\n\nexport const validateNodeInstalled = async () => {\n try {\n await execa(\"node\", [\"--version\"]);\n } catch {\n logger.error(\n \"Node.js is not installed. Please install Node.js and try again.\\n\",\n );\n logger.info(\n `To install Node.js, visit: ${color.underline(\"https://nodejs.org/en/\")}`,\n );\n\n process.exit(1);\n }\n};\n\nexport const validatePnpmInstalled = async () => {\n try {\n await execa(\"pnpm\", [\"--version\"]);\n } catch {\n try {\n await execa(\"npm\", [\"install\", \"-g\", \"pnpm\"]);\n } catch {\n logger.error(\n \"pnpm is not installed. Please install pnpm manually and try again. \\n\",\n );\n logger.info(\n `To install pnpm, visit: ${color.underline(\"https://pnpm.io/installation\")}`,\n );\n\n process.exit(1);\n }\n }\n};\n\nexport const validateDockerInstalled = async () => {\n try {\n await execa(\"docker\", [\"--version\"]);\n } catch {\n logger.error(\n \"Docker is not installed. Please install Docker and try again.\",\n );\n logger.info(\n `To install Docker, visit: ${color.underline(\"https://docs.docker.com/get-docker/\")}`,\n );\n process.exit(1);\n }\n};\n\nconst validateGitInstalled = async () => {\n try {\n await execa(\"git\", [\"--version\"]);\n } catch {\n logger.error(\"Git is not installed. Please install Git and try again.\");\n logger.info(\n `To install Git, visit: ${color.underline(\"https://git-scm.com/downloads\")}`,\n );\n process.exit(1);\n }\n};\n\nexport const validatePrerequisites = async () => {\n const spinner = ora(\"Checking prerequisites... \\n\").start();\n try {\n await validateGitInstalled();\n await validateNodeInstalled();\n await validatePnpmInstalled();\n spinner.succeed(\"All prerequisites are satisfied, let's start! šŸš€\\n\");\n } catch {\n spinner.fail(\"Failed to check prerequisites.\");\n process.exit(1);\n }\n};\n","import { execa } from \"execa\";\nimport ora from \"ora\";\n\nimport { validateDockerInstalled } from \"~/commands/new/prerequisites\";\nimport { servicesPackages } from \"~/config\";\n\nimport type { Service } from \"~/config\";\n\nexport const startServices = async (cwd: string, services: Service[]) => {\n await validateDockerInstalled();\n\n const spinner = ora(`Starting Docker services...`).start();\n\n try {\n await execa(\"pnpm\", [\"services:start\", \"--\", ...services], { cwd });\n await execa(\n \"pnpm\",\n [\n \"with-env\",\n \"pnpm\",\n \"turbo\",\n \"setup\",\n services\n .map((service) => `--filter=${servicesPackages[service]}`)\n .join(\" \"),\n ],\n { cwd },\n );\n\n spinner.succeed(\"Services successfully started!\");\n } catch (error) {\n console.error(error);\n spinner.fail(\"Failed to start services!\");\n process.exit(1);\n }\n};\n","import { promises } from \"fs\";\nimport { join } from \"path\";\n\nexport const removePaths = async ({\n cwd,\n paths,\n}: {\n cwd: string;\n paths: string[];\n}) => {\n await Promise.all(\n paths.map(async (path) => {\n const fullPath = join(cwd, path);\n await promises.rm(fullPath, { recursive: true, force: true });\n }),\n );\n};\n\nexport const replaceInFile = async ({\n cwd,\n path,\n pattern,\n value,\n}: {\n cwd: string;\n path: string;\n pattern: RegExp | string;\n value: string;\n}) => {\n const fullPath = join(cwd, path);\n const content = await promises.readFile(fullPath, \"utf8\");\n const newContent = content.replace(pattern, value);\n await promises.writeFile(fullPath, newContent);\n};\n\nexport const replaceInFiles = async ({\n cwd,\n paths,\n pattern,\n value,\n}: {\n cwd: string;\n paths: string[];\n pattern: RegExp | string;\n value: string;\n}) => {\n await Promise.all(\n paths.map(async (path) => {\n await replaceInFile({ cwd, path, pattern, value });\n }),\n );\n};\n","import { Command } from \"commander\";\nimport { execa } from \"execa\";\nimport ora from \"ora\";\nimport path, { join } from \"path\";\nimport color from \"picocolors\";\nimport prompts from \"prompts\";\nimport { z } from \"zod\";\n\nimport { getAnalyticsConfig } from \"~/commands/new/config/analytics\";\nimport { getBillingConfig } from \"~/commands/new/config/billing\";\nimport { getDatabaseConfig } from \"~/commands/new/config/db\";\nimport { getEmailConfig } from \"~/commands/new/config/email\";\nimport {\n prepareEnvironment,\n setEnvironmentVariables,\n} from \"~/commands/new/config/env\";\nimport { getMonitoringConfig } from \"~/commands/new/config/monitoring\";\nimport { getStorageConfig } from \"~/commands/new/config/storage\";\nimport { startServices } from \"~/commands/new/services\";\nimport {\n App,\n appSpecificFiles,\n config,\n providerConfigFiles,\n Service,\n ServiceType,\n} from \"~/config\";\nimport { handleError, logger, onCancel } from \"~/utils\";\nimport { removePaths, replaceInFiles } from \"~/utils/file\";\n\nimport { validatePrerequisites } from \"./prerequisites\";\n\nimport type {\n AnalyticsProvider,\n BillingProvider,\n EmailProvider,\n MonitoringProvider,\n StorageProvider,\n} from \"~/config\";\n\nconst newOptionsSchema = z.object({\n cwd: z.string(),\n});\n\nexport const newCommand = new Command()\n .name(\"new\")\n .description(\"create a new TurboStarter project\")\n .option(\n \"-c, --cwd <cwd>\",\n \"the working directory. defaults to the current directory.\",\n process.cwd(),\n )\n .action(async (opts: z.infer<typeof newOptionsSchema>) => {\n try {\n logger.log(`\\n${color.bgRedBright(color.white(\" TurboStarter \"))}\\n`);\n\n const options = newOptionsSchema.parse({\n cwd: path.resolve(opts.cwd),\n });\n\n const { name } = await initializeProject(options);\n\n logger.log(\n `\\nšŸŽ‰ You can now get started. Open the project and just ship it! šŸŽ‰\\n`,\n );\n logger.log(`> cd ${name}\\n> pnpm dev\\n`);\n logger.info(\n `Problems? ${color.underline(\"https://turbostarter.dev/docs\")}`,\n );\n } catch (error) {\n logger.break();\n handleError(error);\n }\n });\n\nconst initializeProject = async (options: z.infer<typeof newOptionsSchema>) => {\n await validatePrerequisites();\n\n const name = await getName();\n const apps = await getApps();\n\n logger.info(\n `\\nLet's configure it!\\nYou can skip any step by pressing ${color.bold(\"enter\")}.\\n`,\n );\n\n const dbConfig = await getDatabaseConfig();\n const emailConfig = await getEmailConfig();\n const billingConfig = await getBillingConfig(apps);\n const analyticsConfig = await getAnalyticsConfig(apps);\n const storageConfig = await getStorageConfig();\n const monitoringConfig = await getMonitoringConfig(apps);\n\n const env = {\n ...(\"env\" in dbConfig ? dbConfig.env : {}),\n ...billingConfig.env,\n ...emailConfig.env,\n ...storageConfig.env,\n ...analyticsConfig.env,\n ...monitoringConfig.env,\n };\n\n logger.log(\n `\\nCreating a new TurboStarter project in ${color.greenBright(join(options.cwd, name))}. \\n`,\n );\n\n const projectDir = await cloneRepository(options.cwd, name, apps);\n await prepareEnvironment(projectDir);\n await updateProvidersFiles(projectDir, {\n email: emailConfig.provider,\n storage: storageConfig.provider,\n billing: billingConfig.providers,\n analytics: analyticsConfig.providers,\n monitoring: monitoringConfig.providers,\n });\n await setEnvironmentVariables(projectDir, env);\n await configureGit(projectDir);\n await installDependencies(projectDir);\n\n const localServices = [\n ...(dbConfig.type === ServiceType.LOCAL ? [Service.DB] : []),\n ];\n\n if (localServices.length > 0) {\n await startServices(projectDir, localServices);\n }\n\n return { name, apps };\n};\n\nconst getName = async () => {\n const result = await prompts(\n {\n type: \"text\",\n name: \"name\",\n message: \"Enter your project name.\",\n validate: (value: string) =>\n value.length > 0 ? true : \"Name is required!\",\n },\n {\n onCancel,\n },\n );\n\n return String(result.name);\n};\n\nconst getApps = async () => {\n while (true) {\n const result = await prompts(\n {\n type: \"multiselect\",\n name: \"apps\",\n message: `What do you want to ship?`,\n instructions: false,\n choices: [\n { title: \"Web app\", value: App.WEB, selected: true },\n { title: \"Mobile app\", value: App.MOBILE, selected: false },\n {\n title: \"Browser extension\",\n value: App.EXTENSION,\n selected: false,\n },\n ],\n hint: `You ${color.bold(\"must\")} ship a web app, to ensure backend services work.`,\n },\n {\n onCancel,\n },\n );\n\n const apps = result.apps as App[];\n\n if (apps.includes(App.WEB)) {\n return apps;\n } else {\n logger.error(\n `You ${color.bold(\"must\")} ship a web app, to ensure backend services work.`,\n );\n }\n }\n};\n\nconst getRepositoryUrl = async (httpsUrl: string): Promise<string> => {\n try {\n await execa(\"ssh\", [\"-T\", \"git@github.com\"], { timeout: 5000 });\n return httpsUrl.replace(\"https://github.com/\", \"git@github.com:\");\n } catch {\n return httpsUrl;\n }\n};\n\nconst cloneRepository = async (cwd: string, name: string, apps: App[]) => {\n const spinner = ora(`Cloning repository into ${name}...`).start();\n const projectDir = join(cwd, name);\n\n const filesToRemove = Object.values(App)\n .filter((app) => !apps.includes(app))\n .map((app) => appSpecificFiles[app])\n .flat();\n\n try {\n const url = await getRepositoryUrl(config.repository);\n await execa(\"git\", [\"clone\", \"-b\", \"main\", \"--single-branch\", url, name], {\n cwd,\n });\n\n if (filesToRemove.length) {\n await removePaths({ cwd: projectDir, paths: filesToRemove });\n }\n\n spinner.succeed(\"Repository successfully pulled!\");\n return projectDir;\n } catch {\n spinner.fail(\"Failed to clone TurboStarter! Please try again.\");\n process.exit(1);\n }\n};\n\nconst configureGit = async (cwd: string) => {\n const spinner = ora(`Configuring Git...`).start();\n\n try {\n await removePaths({ cwd, paths: [\".git\"] });\n await execa(\"git\", [\"init\"], { cwd });\n await execa(\"git\", [\"remote\", \"add\", \"upstream\", config.repository], {\n cwd,\n });\n await execa(\"git\", [\"add\", \".\"], { cwd });\n await execa(\"git\", [\"commit\", \"-m\", \"Initial commit\"], { cwd });\n\n spinner.succeed(\"Git successfully configured!\");\n } catch {\n spinner.fail(\"Failed to configure Git! Please try again.\");\n process.exit(1);\n }\n};\n\nconst installDependencies = async (cwd: string) => {\n const spinner = ora(`Installing dependencies...`).start();\n\n try {\n await execa(\"pnpm\", [\"install\"], { cwd });\n\n spinner.succeed(\"Dependencies successfully installed!\");\n } catch {\n spinner.fail(\"Failed to install dependencies! Please try again.\");\n process.exit(1);\n }\n};\n\nconst updateProvidersFiles = async (\n cwd: string,\n providers: {\n email?: EmailProvider;\n storage?: StorageProvider;\n billing?: Partial<BillingProvider>;\n analytics?: Partial<AnalyticsProvider>;\n monitoring?: Partial<MonitoringProvider>;\n },\n) => {\n const spinner = ora(`Updating providers files...`).start();\n\n try {\n if (providers.email) {\n await replaceInFiles({\n cwd,\n paths: providerConfigFiles.email.files,\n pattern: providerConfigFiles.email.pattern,\n value: providers.email,\n });\n }\n if (providers.storage) {\n await replaceInFiles({\n cwd,\n paths: providerConfigFiles.storage.files,\n pattern: providerConfigFiles.storage.pattern,\n value: providers.storage,\n });\n }\n if (providers.billing && Object.keys(providers.billing).length > 0) {\n await Promise.all(\n Object.entries(providers.billing).map(([key, value]) =>\n replaceInFiles({\n cwd,\n paths:\n providerConfigFiles.billing[key as keyof typeof BillingProvider]\n .files,\n pattern:\n providerConfigFiles.billing[key as keyof typeof BillingProvider]\n .pattern,\n value,\n }),\n ),\n );\n }\n if (providers.analytics && Object.keys(providers.analytics).length > 0) {\n await Promise.all(\n Object.entries(providers.analytics).map(([key, value]) =>\n replaceInFiles({\n cwd,\n paths:\n providerConfigFiles.analytics[\n key as keyof typeof AnalyticsProvider\n ].files,\n pattern:\n providerConfigFiles.analytics[\n key as keyof typeof AnalyticsProvider\n ].pattern,\n value,\n }),\n ),\n );\n }\n if (providers.monitoring && Object.keys(providers.monitoring).length > 0) {\n await Promise.all(\n Object.entries(providers.monitoring).map(([key, value]) =>\n replaceInFiles({\n cwd,\n paths:\n providerConfigFiles.monitoring[\n key as keyof typeof MonitoringProvider\n ].files,\n pattern:\n providerConfigFiles.monitoring[\n key as keyof typeof MonitoringProvider\n ].pattern,\n value,\n }),\n ),\n );\n }\n\n spinner.succeed(\"Providers files successfully updated!\");\n } catch (e) {\n console.error(e);\n spinner.fail(\"Failed to update providers files! Please try again.\");\n process.exit(1);\n }\n};\n","","#!/usr/bin/env node\nimport { Command } from \"commander\";\n\nimport { newCommand } from \"~/commands/new\";\n\nimport packageInfo from \"../package.json\";\n\nprocess.on(\"SIGINT\", () => process.exit(0));\nprocess.on(\"SIGTERM\", () => process.exit(0));\n\nfunction main() {\n const program = new Command()\n .name(\"turbostarter\")\n .description(\n \"Your TurboStarter assistant for starting new projects, adding plugins and more.\",\n )\n .version(\n packageInfo.version || \"1.0.0\",\n \"-v, --version\",\n \"display the version number\",\n );\n\n program.addCommand(newCommand);\n program.parse();\n}\n\nvoid main();\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["colors","env","env","path","path","file","expression","env","config","file","packageInfo.version"],"sources":["../src/config.ts","../src/utils/logger.ts","../src/utils/upstream.ts","../src/utils/index.ts","../src/commands/new/config/analytics/extension.ts","../src/commands/new/config/analytics/mobile.ts","../src/commands/new/config/analytics/web.ts","../src/commands/new/config/analytics/index.ts","../src/commands/new/config/billing/mobile.ts","../src/commands/new/config/billing/web.ts","../src/commands/new/config/billing/index.ts","../src/commands/new/config/db.ts","../src/commands/new/config/email.ts","../src/commands/new/config/env.ts","../src/utils/file.ts","../src/commands/new/config/file-modifications.ts","../src/commands/new/config/monitoring/extension.ts","../src/commands/new/config/monitoring/mobile.ts","../src/commands/new/config/monitoring/web.ts","../src/commands/new/config/monitoring/index.ts","../src/commands/new/config/storage.ts","../src/commands/new/prerequisites.ts","../src/commands/new/services.ts","../src/commands/new/index.ts","../src/commands/project/update.ts","../src/commands/project/index.ts","../package.json","../src/index.ts"],"sourcesContent":["type Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nexport const ServiceType = {\n LOCAL: \"local\",\n CLOUD: \"cloud\",\n} as const;\n\nexport const Service = {\n DB: \"db\",\n} as const;\n\nexport const StorageProvider = {\n S3: \"s3\",\n} as const;\n\nexport const EmailProvider = {\n RESEND: \"resend\",\n SENDGRID: \"sendgrid\",\n POSTMARK: \"postmark\",\n PLUNK: \"plunk\",\n NODEMAILER: \"nodemailer\",\n} as const;\n\nexport const EnvPath = {\n ROOT: \"./\",\n WEB: \"./apps/web\",\n MOBILE: \"./apps/mobile\",\n EXTENSION: \"./apps/extension\",\n} as const;\n\nexport const EnvFile = {\n EXAMPLE: \".env.example\",\n LOCAL: \".env.local\",\n} as const;\n\nexport const App = {\n WEB: \"web\",\n MOBILE: \"mobile\",\n EXTENSION: \"extension\",\n} as const;\n\nexport const BillingProvider = {\n [App.WEB]: {\n STRIPE: \"stripe\",\n LEMON_SQUEEZY: \"lemon-squeezy\",\n POLAR: \"polar\",\n },\n [App.MOBILE]: {\n REVENUECAT: \"revenuecat\",\n SUPERWALL: \"superwall\",\n },\n} as const;\n\nexport const AnalyticsProvider = {\n [App.WEB]: {\n GOOGLE_ANALYTICS: \"google-analytics\",\n MIXPANEL: \"mixpanel\",\n OPEN_PANEL: \"open-panel\",\n PLAUSIBLE: \"plausible\",\n POSTHOG: \"posthog\",\n UMAMI: \"umami\",\n VEMETRIC: \"vemetric\",\n VERCEL: \"vercel\",\n },\n [App.MOBILE]: {\n GOOGLE_ANALYTICS: \"google-analytics\",\n MIXPANEL: \"mixpanel\",\n POSTHOG: \"posthog\",\n },\n [App.EXTENSION]: {\n GOOGLE_ANALYTICS: \"google-analytics\",\n POSTHOG: \"posthog\",\n },\n} as const;\n\nexport const MonitoringProvider = {\n [App.WEB]: {\n SENTRY: \"sentry\",\n POSTHOG: \"posthog\",\n },\n [App.MOBILE]: {\n SENTRY: \"sentry\",\n POSTHOG: \"posthog\",\n },\n [App.EXTENSION]: {\n SENTRY: \"sentry\",\n POSTHOG: \"posthog\",\n },\n} as const;\n\nexport type ServiceType = (typeof ServiceType)[keyof typeof ServiceType];\nexport type Service = (typeof Service)[keyof typeof Service];\nexport type StorageProvider =\n (typeof StorageProvider)[keyof typeof StorageProvider];\nexport type EmailProvider = (typeof EmailProvider)[keyof typeof EmailProvider];\nexport type EnvPath = (typeof EnvPath)[keyof typeof EnvPath];\nexport type EnvFile = (typeof EnvFile)[keyof typeof EnvFile];\nexport type App = (typeof App)[keyof typeof App];\n\nexport type BillingProvider = {\n [K in Mutable<\n keyof typeof BillingProvider\n >]: (typeof BillingProvider)[K][keyof (typeof BillingProvider)[K]];\n};\nexport type AnalyticsProvider = {\n [K in Mutable<\n keyof typeof AnalyticsProvider\n >]: (typeof AnalyticsProvider)[K][keyof (typeof AnalyticsProvider)[K]];\n};\nexport type MonitoringProvider = {\n [K in Mutable<\n keyof typeof MonitoringProvider\n >]: (typeof MonitoringProvider)[K][keyof (typeof MonitoringProvider)[K]];\n};\n\nconst env = {\n [Service.DB]: {\n url: \"DATABASE_URL\",\n },\n billing: {\n [App.WEB]: {\n [BillingProvider[App.WEB].STRIPE]: {\n secretKey: \"STRIPE_SECRET_KEY\",\n webhookSecret: \"STRIPE_WEBHOOK_SECRET\",\n },\n [BillingProvider[App.WEB].LEMON_SQUEEZY]: {\n apiKey: \"LEMON_SQUEEZY_API_KEY\",\n signingSecret: \"LEMON_SQUEEZY_SIGNING_SECRET\",\n storeId: \"LEMON_SQUEEZY_STORE_ID\",\n },\n [BillingProvider[App.WEB].POLAR]: {\n accessToken: \"POLAR_ACCESS_TOKEN\",\n webhookSecret: \"POLAR_WEBHOOK_SECRET\",\n organizationSlug: \"POLAR_ORGANIZATION_SLUG\",\n },\n },\n [App.MOBILE]: {\n [BillingProvider[App.MOBILE].REVENUECAT]: {\n appleApiKey: \"EXPO_PUBLIC_REVENUECAT_APPLE_API_KEY\",\n googleApiKey: \"EXPO_PUBLIC_REVENUECAT_GOOGLE_API_KEY\",\n webhookSecret: \"REVENUECAT_WEBHOOK_SECRET\",\n apiKey: \"REVENUECAT_API_KEY\",\n },\n [BillingProvider[App.MOBILE].SUPERWALL]: {\n appleApiKey: \"EXPO_PUBLIC_SUPERWALL_APPLE_API_KEY\",\n googleApiKey: \"EXPO_PUBLIC_SUPERWALL_GOOGLE_API_KEY\",\n webhookSecret: \"SUPERWALL_WEBHOOK_SECRET\",\n },\n },\n },\n email: {\n [EmailProvider.RESEND]: {\n apiKey: \"RESEND_API_KEY\",\n },\n [EmailProvider.SENDGRID]: {\n apiKey: \"SENDGRID_API_KEY\",\n },\n [EmailProvider.PLUNK]: {\n apiKey: \"PLUNK_API_KEY\",\n },\n [EmailProvider.POSTMARK]: {\n apiKey: \"POSTMARK_API_KEY\",\n },\n [EmailProvider.NODEMAILER]: {\n user: \"NODEMAILER_USER\",\n password: \"NODEMAILER_PASSWORD\",\n host: \"NODEMAILER_HOST\",\n port: \"NODEMAILER_PORT\",\n },\n },\n storage: {\n [StorageProvider.S3]: {\n region: \"S3_REGION\",\n bucket: \"S3_BUCKET\",\n endpoint: \"S3_ENDPOINT\",\n accessKeyId: \"S3_ACCESS_KEY_ID\",\n secretAccessKey: \"S3_SECRET_ACCESS_KEY\",\n },\n },\n analytics: {\n [App.WEB]: {\n [AnalyticsProvider[App.WEB].GOOGLE_ANALYTICS]: {\n measurementId: \"NEXT_PUBLIC_GOOGLE_ANALYTICS_MEASUREMENT_ID\",\n secret: \"GOOGLE_ANALYTICS_SECRET\",\n },\n [AnalyticsProvider[App.WEB].MIXPANEL]: {\n token: \"NEXT_PUBLIC_MIXPANEL_TOKEN\",\n },\n [AnalyticsProvider[App.WEB].OPEN_PANEL]: {\n clientId: \"NEXT_PUBLIC_OPEN_PANEL_CLIENT_ID\",\n secret: \"OPEN_PANEL_SECRET\",\n },\n [AnalyticsProvider[App.WEB].PLAUSIBLE]: {\n domain: \"NEXT_PUBLIC_PLAUSIBLE_DOMAIN\",\n host: \"NEXT_PUBLIC_PLAUSIBLE_HOST\",\n },\n [AnalyticsProvider[App.WEB].POSTHOG]: {\n key: \"NEXT_PUBLIC_POSTHOG_KEY\",\n host: \"NEXT_PUBLIC_POSTHOG_HOST\",\n },\n [AnalyticsProvider[App.WEB].UMAMI]: {\n host: \"NEXT_PUBLIC_UMAMI_HOST\",\n websiteId: \"NEXT_PUBLIC_UMAMI_WEBSITE_ID\",\n apiHost: \"UMAMI_API_HOST\",\n apiKey: \"UMAMI_API_KEY\",\n },\n [AnalyticsProvider[App.WEB].VEMETRIC]: {\n token: \"NEXT_PUBLIC_VEMETRIC_PROJECT_TOKEN\",\n },\n [AnalyticsProvider[App.WEB].VERCEL]: {},\n },\n [App.MOBILE]: {\n [AnalyticsProvider[App.MOBILE].GOOGLE_ANALYTICS]: {},\n [AnalyticsProvider[App.MOBILE].MIXPANEL]: {\n token: \"EXPO_PUBLIC_MIXPANEL_TOKEN\",\n },\n [AnalyticsProvider[App.MOBILE].POSTHOG]: {\n key: \"EXPO_PUBLIC_POSTHOG_KEY\",\n host: \"EXPO_PUBLIC_POSTHOG_HOST\",\n },\n },\n [App.EXTENSION]: {\n [AnalyticsProvider[App.EXTENSION].GOOGLE_ANALYTICS]: {\n measurementId: \"VITE_GOOGLE_ANALYTICS_MEASUREMENT_ID\",\n secret: \"VITE_GOOGLE_ANALYTICS_SECRET\",\n },\n [AnalyticsProvider[App.EXTENSION].POSTHOG]: {\n key: \"VITE_POSTHOG_KEY\",\n host: \"VITE_POSTHOG_HOST\",\n },\n },\n },\n monitoring: {\n [App.WEB]: {\n [MonitoringProvider[App.WEB].SENTRY]: {\n dsn: \"NEXT_PUBLIC_SENTRY_DSN\",\n },\n [MonitoringProvider[App.WEB].POSTHOG]: {\n key: \"NEXT_PUBLIC_POSTHOG_KEY\",\n host: \"NEXT_PUBLIC_POSTHOG_HOST\",\n },\n },\n [App.MOBILE]: {\n [MonitoringProvider[App.MOBILE].SENTRY]: {\n dsn: \"EXPO_PUBLIC_SENTRY_DSN\",\n },\n [MonitoringProvider[App.MOBILE].POSTHOG]: {\n key: \"EXPO_PUBLIC_POSTHOG_KEY\",\n host: \"EXPO_PUBLIC_POSTHOG_HOST\",\n },\n },\n [App.EXTENSION]: {\n [MonitoringProvider[App.EXTENSION].SENTRY]: {\n dsn: \"VITE_SENTRY_DSN\",\n },\n [MonitoringProvider[App.EXTENSION].POSTHOG]: {\n key: \"VITE_POSTHOG_KEY\",\n host: \"VITE_POSTHOG_HOST\",\n },\n },\n },\n} as const;\n\nexport const envInPaths = {\n [EnvPath.ROOT]: [env.db.url],\n [EnvPath.WEB]: [\n env.email.resend.apiKey,\n env.email.sendgrid.apiKey,\n env.email.plunk.apiKey,\n env.email.postmark.apiKey,\n env.email.nodemailer.user,\n env.email.nodemailer.password,\n env.storage.s3.accessKeyId,\n env.storage.s3.secretAccessKey,\n env.billing[App.WEB].stripe.secretKey,\n env.billing[App.WEB].stripe.webhookSecret,\n env.billing[App.WEB][\"lemon-squeezy\"].apiKey,\n env.billing[App.WEB][\"lemon-squeezy\"].signingSecret,\n env.billing[App.WEB][\"lemon-squeezy\"].storeId,\n env.billing[App.WEB].polar.accessToken,\n env.billing[App.WEB].polar.webhookSecret,\n env.billing[App.WEB].polar.organizationSlug,\n env.billing[App.MOBILE].revenuecat.webhookSecret,\n env.billing[App.MOBILE].revenuecat.apiKey,\n env.billing[App.MOBILE].superwall.webhookSecret,\n env.analytics[App.WEB][\"google-analytics\"].measurementId,\n env.analytics[App.WEB][\"google-analytics\"].secret,\n env.analytics[App.WEB].mixpanel.token,\n env.analytics[App.WEB][\"open-panel\"].clientId,\n env.analytics[App.WEB][\"open-panel\"].secret,\n env.analytics[App.WEB].plausible.domain,\n env.analytics[App.WEB].plausible.host,\n env.analytics[App.WEB].posthog.key,\n env.analytics[App.WEB].posthog.host,\n env.analytics[App.WEB].umami.host,\n env.analytics[App.WEB].umami.websiteId,\n env.analytics[App.WEB].umami.apiHost,\n env.analytics[App.WEB].umami.apiKey,\n env.analytics[App.WEB].vemetric.token,\n env.monitoring[App.WEB].sentry.dsn,\n env.monitoring[App.WEB].posthog.key,\n env.monitoring[App.WEB].posthog.host,\n ],\n [EnvPath.MOBILE]: [\n env.billing[App.MOBILE].revenuecat.appleApiKey,\n env.billing[App.MOBILE].revenuecat.googleApiKey,\n env.billing[App.MOBILE].superwall.appleApiKey,\n env.billing[App.MOBILE].superwall.googleApiKey,\n env.analytics[App.MOBILE].mixpanel.token,\n env.analytics[App.MOBILE].posthog.key,\n env.analytics[App.MOBILE].posthog.host,\n env.monitoring[App.MOBILE].sentry.dsn,\n ],\n [EnvPath.EXTENSION]: [\n env.analytics[App.EXTENSION][\"google-analytics\"].measurementId,\n env.analytics[App.EXTENSION][\"google-analytics\"].secret,\n env.analytics[App.EXTENSION].posthog.key,\n env.analytics[App.EXTENSION].posthog.host,\n env.monitoring[App.EXTENSION].sentry.dsn,\n ],\n};\n\nexport const providerConfigFiles = {\n email: {\n files: [\n \"packages/email/src/providers/index.ts\",\n \"packages/email/src/providers/env.ts\",\n ],\n pattern: new RegExp(`(${Object.values(EmailProvider).join(\"|\")})`, \"gi\"),\n },\n storage: {\n files: [\n \"packages/storage/src/providers/index.ts\",\n \"packages/storage/src/providers/env.ts\",\n ],\n pattern: new RegExp(`(${Object.values(StorageProvider).join(\"|\")})`, \"gi\"),\n },\n billing: {\n [App.WEB]: {\n files: [\n \"packages/billing/web/src/providers/index.ts\",\n \"packages/billing/web/src/providers/env.ts\",\n ],\n pattern: new RegExp(\n `(${Object.values(BillingProvider[App.WEB]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.MOBILE]: {\n files: [\n \"packages/billing/mobile/src/providers/index.ts\",\n \"packages/billing/mobile/src/providers/env.ts\",\n \"packages/billing/mobile/src/providers/server.ts\",\n ],\n pattern: new RegExp(\n `(${Object.values(BillingProvider[App.MOBILE]).join(\"|\")})`,\n \"gi\",\n ),\n },\n },\n analytics: {\n [App.WEB]: {\n files: [\n \"packages/analytics/web/src/providers/index.tsx\",\n \"packages/analytics/web/src/providers/server.ts\",\n \"packages/analytics/web/src/providers/env.ts\",\n ],\n pattern: new RegExp(\n `(${Object.values(AnalyticsProvider[App.WEB]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.MOBILE]: {\n files: [\"packages/analytics/mobile/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(AnalyticsProvider[App.MOBILE]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.EXTENSION]: {\n files: [\"packages/analytics/extension/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(AnalyticsProvider[App.EXTENSION]).join(\"|\")})`,\n \"gi\",\n ),\n },\n },\n monitoring: {\n [App.WEB]: {\n files: [\"packages/monitoring/web/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(MonitoringProvider[App.WEB]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.MOBILE]: {\n files: [\"packages/monitoring/mobile/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(MonitoringProvider[App.MOBILE]).join(\"|\")})`,\n \"gi\",\n ),\n },\n [App.EXTENSION]: {\n files: [\"packages/monitoring/extension/src/providers/index.ts\"],\n pattern: new RegExp(\n `(${Object.values(MonitoringProvider[App.EXTENSION]).join(\"|\")})`,\n \"gi\",\n ),\n },\n },\n};\n\nexport const servicesPackages: Record<Service, string> = {\n [Service.DB]: \"@workspace/db\",\n};\n\nexport const config = {\n name: \"TurboStarter\",\n repository: \"turbostarter/core\",\n env,\n} as const;\n","import colors from \"picocolors\";\n\nconst formatLog = (log: unknown) => {\n if (typeof log === \"string\") {\n return log;\n }\n\n return String(log);\n};\n\nexport const logger = {\n error(log: unknown) {\n console.log(colors.red(formatLog(log)));\n },\n warn(log: unknown) {\n console.log(colors.yellow(formatLog(log)));\n },\n info(log: unknown) {\n console.log(colors.cyan(formatLog(log)));\n },\n success(log: unknown) {\n console.log(colors.green(formatLog(log)));\n },\n log(log: unknown) {\n console.log(formatLog(log));\n },\n};\n","import { execa } from \"execa\";\n\nexport function sshUrl(repo: string) {\n return `git@github.com:${repo}`;\n}\n\nexport function httpsUrl(repo: string) {\n return `https://github.com/${repo}`;\n}\n\nexport async function hasSshAccess(): Promise<boolean> {\n try {\n await execa(\n \"ssh\",\n [\"-T\", \"git@github.com\", \"-o\", \"StrictHostKeyChecking=no\"],\n {\n timeout: 10_000,\n },\n );\n\n return true;\n } catch (error) {\n // ssh -T git@github.com exits with code 1 even on success,\n // but prints \"successfully authenticated\" in stderr\n const stderr =\n error instanceof Error && \"stderr\" in error\n ? String((error as { stderr: unknown }).stderr)\n : \"\";\n\n return stderr.includes(\"successfully authenticated\");\n }\n}\n\nexport function isUpstreamUrlValid(url: string, repo: string): boolean {\n const normalized = url.replace(/\\/+$/, \"\").replace(/\\.git$/, \"\");\n\n return normalized === sshUrl(repo) || normalized === httpsUrl(repo);\n}\n\nexport async function getUpstreamRemoteUrl({ cwd }: { cwd: string }) {\n try {\n const { stdout } = await execa(\"git remote get-url upstream\", { cwd });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\nexport async function setUpstreamRemote(url: string, { cwd }: { cwd: string }) {\n const currentUrl = await getUpstreamRemoteUrl({ cwd });\n\n if (currentUrl) {\n await execa(\"git\", [\"remote\", \"set-url\", \"upstream\", url], { cwd });\n } else {\n await execa(\"git\", [\"remote\", \"add\", \"upstream\", url], { cwd });\n }\n}\n\nexport async function isGitClean({ cwd }: { cwd: string }): Promise<boolean> {\n try {\n const { stdout } = await execa(\"git status --porcelain\", { cwd });\n return stdout.trim() === \"\";\n } catch {\n return false;\n }\n}\n","import _ from \"lodash\";\n\nimport { logger } from \"~/utils/logger\";\n\nimport type { z } from \"zod\";\n\nexport const getLabel = (value: string) => {\n return value\n .split(\"-\")\n .map((word) => _.capitalize(word))\n .join(\" \");\n};\n\nexport const onCancel = () => {\n logger.error(\"Operation cancelled.\");\n process.exit(0);\n};\n\nexport const enforceSchema = <Schema extends z.ZodType>(\n data: unknown,\n schema: Schema,\n): data is z.infer<Schema> => {\n return schema.safeParse(data).success;\n};\n\nexport const getErrorOutput = (error: unknown) => {\n if (error instanceof Error && \"stderr\" in error) {\n const stderr = String((error as { stderr: unknown }).stderr);\n if (stderr) {\n return stderr;\n }\n }\n\n if (error instanceof Error && \"stdout\" in error) {\n const stdout = String((error as { stdout: unknown }).stdout);\n if (stdout) {\n return stdout;\n }\n }\n\n return error instanceof Error ? error.message : String(error);\n};\n\nexport * from \"./logger\";\nexport * from \"./upstream\";\n","import prompts from \"prompts\";\n\nimport { AnalyticsProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getAnalyticsExtensionProvider = async (): Promise<{\n provider: AnalyticsProvider[typeof App.EXTENSION];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(AnalyticsProvider[App.EXTENSION]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for extension analytics?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getAnalyticsExtensionProviderConfig = async (\n provider: AnalyticsProvider[typeof App.EXTENSION],\n) => {\n switch (provider) {\n case AnalyticsProvider[App.EXTENSION].GOOGLE_ANALYTICS:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION][\"google-analytics\"]\n .measurementId,\n message: \"Enter your Google Analytics measurement ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION][\"google-analytics\"]\n .secret,\n message: \"Enter your Google Analytics secret\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.EXTENSION].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.EXTENSION].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getAnalyticsExtensionConfig = async () => {\n const { provider } = await getAnalyticsExtensionProvider();\n const env = await getAnalyticsExtensionProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { AnalyticsProvider, App, config } from \"~/config\";\nimport { getLabel, logger, onCancel } from \"~/utils\";\n\nconst getAnalyticsMobileProvider = async (): Promise<{\n provider: AnalyticsProvider[typeof App.MOBILE];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(AnalyticsProvider[App.MOBILE]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for mobile analytics?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getAnalyticsMobileProviderConfig = async (\n provider: AnalyticsProvider[typeof App.MOBILE],\n) => {\n switch (provider) {\n case AnalyticsProvider[App.MOBILE].GOOGLE_ANALYTICS:\n logger.info(\n \"Check how to configure Google Analytics for mobile at https://www.turbostarter.dev/docs/mobile/analytics/configuration#google-analytics\",\n );\n return {};\n case AnalyticsProvider[App.MOBILE].MIXPANEL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.MOBILE].mixpanel.token,\n message: \"Enter your Mixpanel token\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.MOBILE].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.MOBILE].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.MOBILE].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getAnalyticsMobileConfig = async () => {\n const { provider } = await getAnalyticsMobileProvider();\n const env = await getAnalyticsMobileProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { AnalyticsProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getAnalyticsWebProvider = async (): Promise<{\n provider: AnalyticsProvider[typeof App.WEB];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(AnalyticsProvider[App.WEB]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for web analytics?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getAnalyticsWebProviderConfig = async (\n provider: AnalyticsProvider[typeof App.WEB],\n) => {\n switch (provider) {\n case AnalyticsProvider[App.WEB].GOOGLE_ANALYTICS:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"google-analytics\"]\n .measurementId,\n message: \"Enter your Google Analytics measurement ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"google-analytics\"].secret,\n message: \"Enter your Google Analytics secret\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].MIXPANEL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].mixpanel.token,\n message: \"Enter your Mixpanel token\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].OPEN_PANEL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"open-panel\"].clientId,\n message: \"Enter your OpenPanel client ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB][\"open-panel\"].secret,\n message: \"Enter your OpenPanel secret\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].PLAUSIBLE:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].plausible.domain,\n message: \"Enter your Plausible domain\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].plausible.host,\n message: \"Enter your Plausible host\",\n initial: \"https://plausible.io\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].UMAMI:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.host,\n message: \"Enter your Umami host\",\n initial: \"https://cloud.umami.is\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.websiteId,\n message: \"Enter your Umami website ID\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.apiHost,\n message: \"Enter your Umami API host\",\n initial: \"https://api-gateway.umami.dev\",\n },\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].umami.apiKey,\n message: \"Enter your Umami API key\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].VEMETRIC:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.analytics[App.WEB].vemetric.token,\n message: \"Enter your Vemetric project token\",\n },\n ],\n { onCancel },\n );\n case AnalyticsProvider[App.WEB].VERCEL:\n return {};\n }\n};\n\nexport const getAnalyticsWebConfig = async () => {\n const { provider } = await getAnalyticsWebProvider();\n const env = await getAnalyticsWebProviderConfig(provider);\n\n return { provider, env };\n};\n","import { App } from \"~/config\";\n\nimport { getAnalyticsExtensionConfig } from \"./extension\";\nimport { getAnalyticsMobileConfig } from \"./mobile\";\nimport { getAnalyticsWebConfig } from \"./web\";\n\nimport type { AnalyticsProvider } from \"~/config\";\n\nexport const getAnalyticsConfig = async (apps: App[]) => {\n const providers: Partial<AnalyticsProvider> = {};\n const env: Record<string, string> = {};\n\n if (apps.includes(App.WEB)) {\n const { provider, env: webEnv } = await getAnalyticsWebConfig();\n providers[App.WEB] = provider;\n Object.assign(env, webEnv);\n }\n\n if (apps.includes(App.MOBILE)) {\n const { provider, env: mobileEnv } = await getAnalyticsMobileConfig();\n providers[App.MOBILE] = provider;\n Object.assign(env, mobileEnv);\n }\n\n if (apps.includes(App.EXTENSION)) {\n const { provider, env: extensionEnv } = await getAnalyticsExtensionConfig();\n providers[App.EXTENSION] = provider;\n Object.assign(env, extensionEnv);\n }\n\n return { providers, env };\n};\n","import prompts from \"prompts\";\n\nimport { App, BillingProvider, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nimport type { BillingProvider as BillingProviderType } from \"~/config\";\n\nconst getBillingMobileProvider = async (): Promise<{\n provider: BillingProviderType[typeof App.MOBILE];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(BillingProvider[App.MOBILE]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for mobile billing?\",\n },\n ],\n { onCancel },\n );\n};\n\nconst getBillingMobileProviderConfig = async (\n provider: BillingProviderType[typeof App.MOBILE],\n) => {\n switch (provider) {\n case BillingProvider[App.MOBILE].REVENUECAT:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.appleApiKey,\n message: \"Enter your RevenueCat Apple API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.googleApiKey,\n message: \"Enter your RevenueCat Google API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.webhookSecret,\n message: \"Enter your RevenueCat webhook secret\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].revenuecat.apiKey,\n message: \"Enter your RevenueCat API key\",\n },\n ],\n { onCancel },\n );\n case BillingProvider[App.MOBILE].SUPERWALL:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].superwall.appleApiKey,\n message: \"Enter your Superwall Apple API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].superwall.googleApiKey,\n message: \"Enter your Superwall Google API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.MOBILE].superwall.webhookSecret,\n message: \"Enter your Superwall webhook secret\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getBillingMobileConfig = async () => {\n const { provider } = await getBillingMobileProvider();\n const env = await getBillingMobileProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { App, BillingProvider, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nimport type { BillingProvider as BillingProviderType } from \"~/config\";\n\nconst getBillingWebProvider = async (): Promise<{\n provider: BillingProviderType[typeof App.WEB];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(BillingProvider[App.WEB]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for web billing?\",\n },\n ],\n { onCancel },\n );\n};\n\nconst getBillingWebProviderConfig = async (\n provider: BillingProviderType[typeof App.WEB],\n) => {\n switch (provider) {\n case BillingProvider[App.WEB].STRIPE:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.WEB].stripe.secretKey,\n message: \"Enter your Stripe secret key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB].stripe.webhookSecret,\n message: \"Enter your Stripe webhook secret\",\n },\n ],\n { onCancel },\n );\n case BillingProvider[App.WEB].LEMON_SQUEEZY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.WEB][\"lemon-squeezy\"].storeId,\n message: \"Enter your Lemon Squeezy store ID\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB][\"lemon-squeezy\"].apiKey,\n message: \"Enter your Lemon Squeezy API key\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB][\"lemon-squeezy\"].signingSecret,\n message: \"Enter your Lemon Squeezy signing secret\",\n },\n ],\n { onCancel },\n );\n case BillingProvider[App.WEB].POLAR:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.billing[App.WEB].polar.accessToken,\n message: \"Enter your Polar access token\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB].polar.webhookSecret,\n message: \"Enter your Polar webhook secret\",\n },\n {\n type: \"text\",\n name: config.env.billing[App.WEB].polar.organizationSlug,\n message: \"Enter your Polar organization slug\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getBillingWebConfig = async () => {\n const { provider } = await getBillingWebProvider();\n const env = await getBillingWebProviderConfig(provider);\n\n return { provider, env };\n};\n","import { App } from \"~/config\";\n\nimport { getBillingMobileConfig } from \"./mobile\";\nimport { getBillingWebConfig } from \"./web\";\n\nimport type { BillingProvider } from \"~/config\";\n\nexport const getBillingConfig = async (apps: App[]) => {\n const providers: Partial<BillingProvider> = {};\n const env: Record<string, string> = {};\n\n if (apps.includes(App.WEB)) {\n const { provider, env: webEnv } = await getBillingWebConfig();\n providers[App.WEB] = provider;\n Object.assign(env, webEnv);\n }\n\n if (apps.includes(App.MOBILE)) {\n const { provider, env: mobileEnv } = await getBillingMobileConfig();\n providers[App.MOBILE] = provider;\n Object.assign(env, mobileEnv);\n }\n\n return { providers, env };\n};\n","import prompts from \"prompts\";\n\nimport { config, Service, ServiceType } from \"~/config\";\nimport { onCancel } from \"~/utils\";\n\nconst getDatabaseCloudConfig = async () => {\n return prompts(\n [\n {\n type: \"text\",\n name: config.env[Service.DB].url,\n message: \"Enter your database URL\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nexport const getDatabaseConfig = async () => {\n const response = await prompts(\n [\n {\n type: \"select\",\n name: \"type\",\n message: \"How do you want to use database?\",\n choices: [\n {\n title: \"Local (powered by Docker)\",\n value: ServiceType.LOCAL,\n selected: true,\n },\n {\n title: `Cloud`,\n value: ServiceType.CLOUD,\n },\n ],\n },\n ],\n {\n onCancel,\n },\n );\n\n if (response.type === ServiceType.CLOUD) {\n const dbConfig = await getDatabaseCloudConfig();\n\n return { ...response, env: dbConfig };\n }\n\n return response;\n};\n","import prompts from \"prompts\";\n\nimport { EmailProvider, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getEmailProvider = async (): Promise<{\n provider: EmailProvider;\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n name: \"provider\",\n message: \"What do you want to use for emails?\",\n choices: Object.values(EmailProvider).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getEmailProviderConfig = async (provider: EmailProvider) => {\n switch (provider) {\n case EmailProvider.RESEND:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.resend.apiKey,\n message: \"Enter your Resend API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.SENDGRID:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.sendgrid.apiKey,\n message: \"Enter your Sendgrid API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.PLUNK:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.plunk.apiKey,\n message: \"Enter your Plunk API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.POSTMARK:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.postmark.apiKey,\n message: \"Enter your Postmark API key\",\n },\n ],\n {\n onCancel,\n },\n );\n case EmailProvider.NODEMAILER:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.email.nodemailer.user,\n message: \"Enter your Nodemailer user\",\n },\n {\n type: \"text\",\n name: config.env.email.nodemailer.password,\n message: \"Enter your Nodemailer user password\",\n },\n {\n type: \"text\",\n name: config.env.email.nodemailer.host,\n message: \"Enter your Nodemailer host\",\n },\n {\n type: \"number\",\n name: config.env.email.nodemailer.port,\n message: \"Enter your Nodemailer port\",\n },\n ],\n {\n onCancel,\n },\n );\n }\n};\n\nexport const getEmailConfig = async () => {\n const { provider } = await getEmailProvider();\n const env = await getEmailProviderConfig(provider);\n\n return { provider, env };\n};\n","import { promises } from \"fs\";\nimport _ from \"lodash\";\nimport ora from \"ora\";\nimport { join } from \"path\";\n\nimport { EnvFile, envInPaths, EnvPath } from \"~/config\";\nimport { logger } from \"~/utils\";\n\nexport const prepareEnvironment = async (projectDir: string) => {\n try {\n await Promise.allSettled(\n Object.values(EnvPath).map(async (path) => {\n const cwd = join(projectDir, path);\n await promises.copyFile(\n join(cwd, EnvFile.EXAMPLE),\n join(cwd, EnvFile.LOCAL),\n );\n }),\n );\n } catch (error) {\n logger.error(error);\n logger.error(\"Failed to prepare environment!\");\n process.exit(1);\n }\n};\n\nexport const setEnvironmentVariable = async (\n projectDir: string,\n key: string,\n value: string,\n) => {\n if (!value) {\n return;\n }\n\n const paths = _.keys(\n _.pickBy(envInPaths, (values) => _.includes(values, key)),\n );\n\n for (const path of paths) {\n const cwd = join(projectDir, path);\n const envFilePath = join(cwd, EnvFile.LOCAL);\n\n const content = await promises.readFile(envFilePath, \"utf8\");\n\n const regex = new RegExp(`^${key}=.*`, \"gm\");\n\n if (regex.test(content)) {\n await promises.writeFile(\n envFilePath,\n content.replace(regex, `${key}=\"${value}\"`),\n );\n } else {\n await promises.appendFile(envFilePath, `\\n${key}=\"${value}\"`);\n }\n }\n};\n\nexport const setEnvironmentVariables = async (\n projectDir: string,\n variables: Record<string, string>,\n) => {\n const spinner = ora(`Setting environment variables...`).start();\n\n try {\n for (const [key, value] of Object.entries(variables)) {\n await setEnvironmentVariable(projectDir, key, value);\n }\n\n spinner.succeed(\"Environment variables successfully set!\");\n } catch (error) {\n logger.error(error);\n logger.error(\"Failed to set environment variables!\");\n process.exit(1);\n }\n};\n","import { promises } from \"fs\";\nimport _ from \"lodash\";\nimport { join } from \"path\";\n\nimport type { SourceFile } from \"ts-morph\";\nimport type * as z from \"zod/v4/core\";\n\ntype BivariantCallback<TInput, TOutput> = {\n bivarianceHack(input: TInput): TOutput;\n}[\"bivarianceHack\"];\n\ntype GeneralFile = {\n path: string;\n} & {\n action: \"remove\";\n};\n\ntype JsonFile<Schema extends z.$ZodType, Data = z.infer<Schema>> = {\n path: `${string}.json`;\n} & (\n | {\n action: \"remove\";\n }\n | {\n action: \"modify\";\n schema: Schema;\n modify: BivariantCallback<Data, unknown>;\n }\n);\n\ntype TypescriptFile = {\n path: `${string}.ts` | `${string}.tsx`;\n} & (\n | {\n action: \"remove\";\n }\n | {\n action: \"modify\";\n modify: (file: SourceFile) => void;\n }\n);\n\ntype Directory = {\n path: string;\n} & {\n action: \"remove\";\n};\n\nexport type File = GeneralFile | TypescriptFile | JsonFile<z.$ZodType, unknown>;\nexport type Entry = File | Directory;\n\nexport function file<S extends z.$ZodType>(file: JsonFile<S>): JsonFile<S>;\nexport function file<T extends TypescriptFile>(file: T): T;\nexport function file<F extends GeneralFile>(file: F): F;\nexport function file(file: File) {\n return file;\n}\n\nexport const directory = <D extends Directory>(directory: D) => directory;\n\nexport const isJsonFile = (\n file: Entry,\n): file is JsonFile<z.$ZodType, unknown> => file.path.endsWith(\".json\");\n\nexport const isTypescriptFile = (file: Entry): file is TypescriptFile =>\n [\".ts\", \".tsx\"].some((extension) => file.path.endsWith(extension));\n\nexport const removePath = async ({\n cwd,\n path,\n}: {\n cwd: string;\n path: string;\n}) => {\n const fullPath = join(cwd, path);\n await promises.rm(fullPath, { recursive: true, force: true });\n};\n\nexport const removeDependency = <T extends Record<string, unknown>>(\n data: T,\n dependency: string,\n) => {\n return _.transform(\n data,\n (result: Record<string, unknown>, value, key) => {\n if ([\"dependencies\", \"devDependencies\"].includes(key)) {\n result[key] =\n value && typeof value === \"object\"\n ? _.omit(value, dependency)\n : value;\n } else {\n result[key] = value;\n }\n },\n {},\n ) as T;\n};\n\nexport const replaceInFile = async ({\n cwd,\n path,\n pattern,\n value,\n}: {\n cwd: string;\n path: string;\n pattern: RegExp | string;\n value: string;\n}) => {\n const fullPath = join(cwd, path);\n const content = await promises.readFile(fullPath, \"utf8\");\n const newContent = content.replace(pattern, value);\n await promises.writeFile(fullPath, newContent);\n};\n\nexport const replaceInFiles = async ({\n cwd,\n paths,\n pattern,\n value,\n}: {\n cwd: string;\n paths: string[];\n pattern: RegExp | string;\n value: string;\n}) => {\n await Promise.all(\n paths.map(async (path) => {\n await replaceInFile({ cwd, path, pattern, value });\n }),\n );\n};\n","import { Node, SyntaxKind } from \"ts-morph\";\nimport { z } from \"zod\";\n\nimport { App } from \"~/config\";\nimport { directory, file, removeDependency } from \"~/utils/file\";\n\nimport type { ArrayLiteralExpression } from \"ts-morph\";\n\nexport const fileModificationsByMissingApp = {\n [App.WEB]: [],\n [App.MOBILE]: [\n ...[\n \"apps/mobile\",\n \"packages/analytics/mobile\",\n \"packages/billing/mobile\",\n \"packages/monitoring/mobile\",\n \"packages/ui/mobile\",\n ].map((path) =>\n directory({\n path,\n action: \"remove\",\n }),\n ),\n ...[\n \"packages/auth/src/client/mobile.ts\",\n \"packages/auth/src/server/mobile.ts\",\n ].map((path) =>\n file({\n path,\n action: \"remove\",\n }),\n ),\n file({\n path: \"packages/api/package.json\",\n action: \"modify\",\n schema: z.looseObject({\n dependencies: z.record(z.string(), z.string()),\n }),\n modify: (file) => removeDependency(file, \"@workspace/billing-mobile\"),\n }),\n file({\n path: \"packages/auth/package.json\",\n action: \"modify\",\n schema: z.looseObject({\n dependencies: z.record(z.string(), z.string()),\n }),\n modify: (file) => removeDependency(file, \"@better-auth/expo\"),\n }),\n file({\n path: \"packages/api/src/env.ts\",\n action: \"modify\",\n modify: (file) => {\n file\n .getImportDeclaration((declaration) =>\n declaration\n .getModuleSpecifierValue()\n .startsWith(\"@workspace/billing-mobile\"),\n )\n ?.remove();\n\n const extendsProperty = file\n .getVariableDeclaration(\"preset\")\n ?.getInitializer()\n ?.getFirstDescendantByKind(SyntaxKind.ObjectLiteralExpression)\n ?.getProperty(\"extends\");\n\n if (!extendsProperty || !Node.isPropertyAssignment(extendsProperty)) {\n return;\n }\n\n const extendsArray = extendsProperty.getInitializerIfKind(\n SyntaxKind.ArrayLiteralExpression,\n );\n if (!extendsArray) {\n return;\n }\n\n const elements = extendsArray.getElements();\n for (let index = elements.length - 1; index >= 0; index--) {\n if (elements[index]?.getText() === \"billingMobile\") {\n extendsArray.removeElement(index);\n }\n }\n },\n }),\n file({\n path: \"packages/api/src/modules/billing/router.ts\",\n action: \"modify\",\n modify: (file) => {\n file\n .getImportDeclaration((declaration) =>\n declaration\n .getModuleSpecifierValue()\n .startsWith(\"@workspace/billing-mobile\"),\n )\n ?.remove();\n\n const callExpression = file\n .getDescendantsOfKind(SyntaxKind.CallExpression)\n .find((node) => {\n const expression = node.getExpression();\n if (!Node.isPropertyAccessExpression(expression)) {\n return false;\n }\n\n const argumentsList = node.getArguments();\n if (!argumentsList.length) {\n return false;\n }\n const firstArgumentText = argumentsList[0].getText();\n return (\n expression.getName() === \"post\" &&\n firstArgumentText.includes(\"mobile.provider\")\n );\n });\n\n if (!callExpression) {\n return;\n }\n\n const expression = callExpression.getExpression();\n if (!Node.isPropertyAccessExpression(expression)) {\n return;\n }\n\n callExpression.replaceWithText(expression.getExpression().getText());\n },\n }),\n file({\n path: \"packages/auth/src/server.ts\",\n action: \"modify\",\n modify: (file) => {\n const getArrayProperty = (\n propertyName: string,\n ): ArrayLiteralExpression | undefined => {\n const authConfig = file\n .getVariableDeclaration(\"auth\")\n ?.getInitializerIfKind(SyntaxKind.CallExpression)\n ?.getArguments()[0]\n ?.asKind(SyntaxKind.ObjectLiteralExpression);\n const property = authConfig?.getProperty(propertyName);\n if (!property || !Node.isPropertyAssignment(property)) {\n return undefined;\n }\n\n return property.getInitializerIfKind(\n SyntaxKind.ArrayLiteralExpression,\n );\n };\n\n const removeMatchingArrayElements = (\n array: ArrayLiteralExpression | undefined,\n isMatch: (text: string) => boolean,\n ) => {\n if (!array) {\n return;\n }\n\n const elements = array.getElements();\n for (let index = elements.length - 1; index >= 0; index--) {\n if (isMatch(elements[index]?.getText() ?? \"\")) {\n array.removeElement(index);\n }\n }\n };\n\n file\n .getImportDeclaration(\n (declaration) =>\n declaration.getModuleSpecifierValue() === \"@better-auth/expo\",\n )\n ?.remove();\n\n removeMatchingArrayElements(getArrayProperty(\"plugins\"), (text) =>\n text.startsWith(\"expo(\"),\n );\n removeMatchingArrayElements(\n getArrayProperty(\"trustedOrigins\"),\n (text) => text === '\"turbostarter://\"',\n );\n },\n }),\n ],\n [App.EXTENSION]: [\n ...[\n \"apps/extension\",\n \"packages/analytics/extension\",\n \"packages/monitoring/extension\",\n ].map((path) =>\n directory({\n path,\n action: \"remove\",\n }),\n ),\n file({\n path: \".github/workflows/publish-extension.yml\",\n action: \"remove\",\n }),\n file({\n path: \"packages/auth/src/server.ts\",\n action: \"modify\",\n modify: (file) => {\n const trustedOrigins = file\n .getVariableDeclaration(\"auth\")\n ?.getInitializerIfKind(SyntaxKind.CallExpression)\n ?.getArguments()[0]\n ?.asKind(SyntaxKind.ObjectLiteralExpression)\n ?.getProperty(\"trustedOrigins\");\n\n if (!trustedOrigins || !Node.isPropertyAssignment(trustedOrigins)) {\n return;\n }\n\n const trustedOriginsArray = trustedOrigins.getInitializerIfKind(\n SyntaxKind.ArrayLiteralExpression,\n );\n if (!trustedOriginsArray) {\n return;\n }\n\n const originElements = trustedOriginsArray.getElements();\n for (let index = originElements.length - 1; index >= 0; index--) {\n if (originElements[index]?.getText() === '\"chrome-extension://\"') {\n trustedOriginsArray.removeElement(index);\n }\n }\n },\n }),\n ],\n};\n","import prompts from \"prompts\";\n\nimport { MonitoringProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getMonitoringExtensionProvider = async (): Promise<{\n provider: MonitoringProvider[typeof App.EXTENSION];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(MonitoringProvider[App.EXTENSION]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for extension monitoring?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getMonitoringExtensionProviderConfig = async (\n provider: MonitoringProvider[typeof App.EXTENSION],\n) => {\n switch (provider) {\n case MonitoringProvider[App.EXTENSION].SENTRY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.EXTENSION].sentry.dsn,\n message: \"Enter your Sentry DSN\",\n },\n ],\n { onCancel },\n );\n case MonitoringProvider[App.EXTENSION].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.EXTENSION].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.monitoring[App.EXTENSION].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getMonitoringExtensionConfig = async () => {\n const { provider } = await getMonitoringExtensionProvider();\n const env = await getMonitoringExtensionProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { MonitoringProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getMonitoringMobileProvider = async (): Promise<{\n provider: MonitoringProvider[typeof App.MOBILE];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(MonitoringProvider[App.MOBILE]).map(\n (provider) => ({\n title: getLabel(provider),\n value: provider,\n }),\n ),\n name: \"provider\",\n message: \"What do you want to use for mobile monitoring?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getMonitoringMobileProviderConfig = async (\n provider: MonitoringProvider[typeof App.MOBILE],\n) => {\n switch (provider) {\n case MonitoringProvider[App.MOBILE].SENTRY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.MOBILE].sentry.dsn,\n message: \"Enter your Sentry DSN\",\n },\n ],\n { onCancel },\n );\n case MonitoringProvider[App.MOBILE].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.MOBILE].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.monitoring[App.MOBILE].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getMonitoringMobileConfig = async () => {\n const { provider } = await getMonitoringMobileProvider();\n const env = await getMonitoringMobileProviderConfig(provider);\n\n return { provider, env };\n};\n","import prompts from \"prompts\";\n\nimport { MonitoringProvider, App, config } from \"~/config\";\nimport { getLabel, onCancel } from \"~/utils\";\n\nconst getMonitoringWebProvider = async (): Promise<{\n provider: MonitoringProvider[typeof App.WEB];\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(MonitoringProvider[App.WEB]).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for web monitoring?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getMonitoringWebProviderConfig = async (\n provider: MonitoringProvider[typeof App.WEB],\n) => {\n switch (provider) {\n case MonitoringProvider[App.WEB].SENTRY:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.WEB].sentry.dsn,\n message: \"Enter your Sentry DSN\",\n },\n ],\n { onCancel },\n );\n case MonitoringProvider[App.WEB].POSTHOG:\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.monitoring[App.WEB].posthog.key,\n message: \"Enter your PostHog key\",\n },\n {\n type: \"text\",\n name: config.env.monitoring[App.WEB].posthog.host,\n message: \"Enter your PostHog host\",\n initial: \"https://us.posthog.com\",\n },\n ],\n { onCancel },\n );\n }\n};\n\nexport const getMonitoringWebConfig = async () => {\n const { provider } = await getMonitoringWebProvider();\n const env = await getMonitoringWebProviderConfig(provider);\n\n return { provider, env };\n};\n","import { App } from \"~/config\";\n\nimport { getMonitoringExtensionConfig } from \"./extension\";\nimport { getMonitoringMobileConfig } from \"./mobile\";\nimport { getMonitoringWebConfig } from \"./web\";\n\nimport type { MonitoringProvider } from \"~/config\";\n\nexport const getMonitoringConfig = async (apps: App[]) => {\n const providers: Partial<MonitoringProvider> = {};\n const env: Record<string, string> = {};\n\n if (apps.includes(App.WEB)) {\n const { provider, env: webEnv } = await getMonitoringWebConfig();\n providers[App.WEB] = provider;\n Object.assign(env, webEnv);\n }\n\n if (apps.includes(App.MOBILE)) {\n const { provider, env: mobileEnv } = await getMonitoringMobileConfig();\n providers[App.MOBILE] = provider;\n Object.assign(env, mobileEnv);\n }\n\n if (apps.includes(App.EXTENSION)) {\n const { provider, env: extensionEnv } =\n await getMonitoringExtensionConfig();\n providers[App.EXTENSION] = provider;\n Object.assign(env, extensionEnv);\n }\n\n return { providers, env };\n};\n","import prompts from \"prompts\";\n\nimport { config, StorageProvider } from \"~/config\";\nimport { onCancel } from \"~/utils\";\nimport { getLabel } from \"~/utils\";\n\nconst getStorageProvider = async (): Promise<{\n provider: StorageProvider;\n}> => {\n return prompts(\n [\n {\n type: \"select\",\n choices: Object.values(StorageProvider).map((provider) => ({\n title: getLabel(provider),\n value: provider,\n })),\n name: \"provider\",\n message: \"What do you want to use for storage?\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nconst getStorageProviderConfig = () => {\n return prompts(\n [\n {\n type: \"text\",\n name: config.env.storage.s3.region,\n message: \"Enter your S3 region\",\n initial: \"us-east-1\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.endpoint,\n message: \"Enter your S3 endpoint\",\n initial: \"https://s3.amazonaws.com\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.bucket,\n message: \"Enter your default S3 bucket name\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.accessKeyId,\n message: \"Enter your S3 access key ID\",\n },\n {\n type: \"text\",\n name: config.env.storage.s3.secretAccessKey,\n message: \"Enter your S3 secret access key\",\n },\n ],\n {\n onCancel,\n },\n );\n};\n\nexport const getStorageConfig = async () => {\n const { provider } = await getStorageProvider();\n const env = await getStorageProviderConfig();\n\n return { provider, env };\n};\n","import { execa } from \"execa\";\nimport ora from \"ora\";\nimport color from \"picocolors\";\n\nimport { logger } from \"~/utils/logger\";\n\nexport const validateNodeInstalled = async () => {\n try {\n await execa(\"node\", [\"--version\"]);\n } catch {\n logger.error(\n \"Node.js is not installed. Please install Node.js and try again.\\n\",\n );\n logger.info(\n `To install Node.js, visit: ${color.underline(\"https://nodejs.org/en/\")}`,\n );\n\n process.exit(1);\n }\n};\n\nexport const validatePnpmInstalled = async () => {\n try {\n await execa(\"pnpm\", [\"--version\"]);\n } catch {\n try {\n await execa(\"npm\", [\"install\", \"-g\", \"pnpm\"]);\n } catch {\n logger.error(\n \"pnpm is not installed. Please install pnpm manually and try again. \\n\",\n );\n logger.info(\n `To install pnpm, visit: ${color.underline(\"https://pnpm.io/installation\")}`,\n );\n\n process.exit(1);\n }\n }\n};\n\nexport const validateDockerInstalled = async () => {\n try {\n await execa(\"docker\", [\"--version\"]);\n } catch {\n logger.error(\n \"Docker is not installed. Please install Docker and try again.\",\n );\n logger.info(\n `To install Docker, visit: ${color.underline(\"https://docs.docker.com/get-docker/\")}`,\n );\n process.exit(1);\n }\n};\n\nconst validateGitInstalled = async () => {\n try {\n await execa(\"git\", [\"--version\"]);\n } catch {\n logger.error(\"Git is not installed. Please install Git and try again.\");\n logger.info(\n `To install Git, visit: ${color.underline(\"https://git-scm.com/downloads\")}`,\n );\n process.exit(1);\n }\n};\n\nexport const validatePrerequisites = async () => {\n const spinner = ora(\"Checking prerequisites... \\n\").start();\n try {\n await validateGitInstalled();\n await validateNodeInstalled();\n await validatePnpmInstalled();\n spinner.succeed(\"All prerequisites satisfied, let's start! šŸš€\\n\");\n } catch {\n spinner.fail(\"Failed to check prerequisites.\");\n process.exit(1);\n }\n};\n","import { execa } from \"execa\";\nimport ora from \"ora\";\n\nimport { validateDockerInstalled } from \"~/commands/new/prerequisites\";\nimport { servicesPackages } from \"~/config\";\nimport { logger } from \"~/utils/logger\";\n\nimport type { Service } from \"~/config\";\n\nexport const startServices = async (cwd: string, services: Service[]) => {\n await validateDockerInstalled();\n\n const spinner = ora(`Starting local services...`).start();\n\n try {\n await execa(\"pnpm\", [\"services:start\", \"--\", ...services], { cwd });\n await execa(\n \"pnpm\",\n [\n \"with-env\",\n \"pnpm\",\n \"turbo\",\n \"setup\",\n services\n .map((service) => `--filter=${servicesPackages[service]}`)\n .join(\" \"),\n ],\n { cwd },\n );\n\n spinner.succeed(\"Services successfully started!\");\n } catch (error) {\n spinner.fail(\"Failed to start services!\");\n logger.error(error);\n process.exit(1);\n }\n};\n","import { Command } from \"commander\";\nimport { execa } from \"execa\";\nimport { promises } from \"fs\";\nimport ora from \"ora\";\nimport path, { join } from \"path\";\nimport color from \"picocolors\";\nimport prompts from \"prompts\";\nimport { Project } from \"ts-morph\";\nimport { z } from \"zod\";\n\nimport { getAnalyticsConfig } from \"~/commands/new/config/analytics\";\nimport { getBillingConfig } from \"~/commands/new/config/billing\";\nimport { getDatabaseConfig } from \"~/commands/new/config/db\";\nimport { getEmailConfig } from \"~/commands/new/config/email\";\nimport {\n prepareEnvironment,\n setEnvironmentVariables,\n} from \"~/commands/new/config/env\";\nimport { fileModificationsByMissingApp } from \"~/commands/new/config/file-modifications\";\nimport { getMonitoringConfig } from \"~/commands/new/config/monitoring\";\nimport { getStorageConfig } from \"~/commands/new/config/storage\";\nimport {\n App,\n config,\n providerConfigFiles,\n Service,\n ServiceType,\n} from \"~/config\";\nimport {\n enforceSchema,\n hasSshAccess,\n httpsUrl,\n logger,\n onCancel,\n setUpstreamRemote,\n sshUrl,\n} from \"~/utils\";\nimport {\n isJsonFile,\n isTypescriptFile,\n removePath,\n replaceInFiles,\n} from \"~/utils/file\";\n\nimport { validatePrerequisites } from \"./prerequisites\";\nimport { startServices } from \"./services\";\n\nimport type {\n AnalyticsProvider,\n BillingProvider,\n EmailProvider,\n MonitoringProvider,\n StorageProvider,\n} from \"~/config\";\n\nconst newOptionsSchema = z.object({\n cwd: z.string(),\n});\n\nexport const newCommand = new Command()\n .name(\"new\")\n .description(\"create a new TurboStarter project\")\n .option(\n \"-c, --cwd <cwd>\",\n \"the working directory. Defaults to the current directory.\",\n process.cwd(),\n )\n .action(async (opts: z.infer<typeof newOptionsSchema>) => {\n try {\n logger.log(`\\n${color.bgRedBright(color.white(\" TurboStarter \"))}\\n`);\n\n const options = newOptionsSchema.parse({\n cwd: path.resolve(opts.cwd),\n });\n\n const { name } = await initializeProject(options);\n\n logger.log(\n `\\nšŸŽ‰ You can now get started. Open the project and just ship it! šŸŽ‰\\n`,\n );\n logger.log(`> cd ${name}\\n> pnpm dev\\n`);\n logger.info(\n `Problems? ${color.underline(\"https://turbostarter.dev/docs\")}`,\n );\n } catch (error) {\n logger.error(error);\n process.exit(1);\n }\n });\n\nconst initializeProject = async (options: z.infer<typeof newOptionsSchema>) => {\n await validatePrerequisites();\n\n const name = await getName();\n const apps = await getApps();\n\n const shouldConfigureProviders = await getConfigureProvidersStep();\n const config = shouldConfigureProviders\n ? await getProvidersConfig(apps)\n : undefined;\n\n logger.log(\n `\\nCreating a new TurboStarter project in ${color.greenBright(join(options.cwd, name))}. \\n`,\n );\n\n const projectDir = await cloneRepository(options.cwd, name, apps);\n await configureGit(projectDir, apps);\n await prepareEnvironment(projectDir);\n\n if (config) {\n await setEnvironmentVariables(projectDir, config.env);\n await updateProvidersFiles(projectDir, {\n email: config.email.provider,\n storage: config.storage.provider,\n billing: config.billing.providers,\n analytics: config.analytics.providers,\n monitoring: config.monitoring.providers,\n });\n }\n\n await installDependencies(projectDir);\n\n const localServices = [\n ...(!config || config.db.type === ServiceType.LOCAL ? [Service.DB] : []),\n ];\n\n if (localServices.length > 0) {\n await startServices(projectDir, localServices);\n }\n\n return { name, apps };\n};\n\nconst getName = async () => {\n const result = await prompts(\n {\n type: \"text\",\n name: \"name\",\n message: \"Enter your project name.\",\n validate: (value: string) =>\n value.length > 0 ? true : \"Name is required!\",\n },\n {\n onCancel,\n },\n );\n\n return String(result.name);\n};\n\nconst getApps = async () => {\n while (true) {\n const result = await prompts(\n {\n type: \"multiselect\",\n name: \"apps\",\n message: `What do you want to ship?`,\n instructions: false,\n choices: [\n { title: \"Web app\", value: App.WEB, selected: true },\n { title: \"Mobile app\", value: App.MOBILE, selected: false },\n {\n title: \"Browser extension\",\n value: App.EXTENSION,\n selected: false,\n },\n ],\n hint: `You ${color.bold(\"must\")} ship a web app, to ensure backend services work.`,\n },\n {\n onCancel,\n },\n );\n\n const apps = result.apps as App[];\n\n if (apps.includes(App.WEB)) {\n return apps;\n } else {\n logger.error(\n `You ${color.bold(\"must\")} ship a web app, to ensure backend services work.`,\n );\n }\n }\n};\n\nconst getConfigureProvidersStep = async () => {\n const result = await prompts(\n {\n type: \"select\",\n name: \"configure\",\n message: \"Configure all providers now?\",\n choices: [\n {\n title: \"Yes, configure now (recommended)\",\n value: true,\n selected: true,\n },\n {\n title: \"No, just let me ship, now!\",\n value: false,\n },\n ],\n },\n {\n onCancel,\n },\n );\n\n return Boolean(result.configure);\n};\n\nconst getProvidersConfig = async (apps: App[]) => {\n logger.info(\n `\\nLet's configure it!\\nYou can skip any step by pressing ${color.bold(\"enter\")}.\\n`,\n );\n\n const db = await getDatabaseConfig();\n const email = await getEmailConfig();\n const billing = await getBillingConfig(apps);\n const analytics = await getAnalyticsConfig(apps);\n const storage = await getStorageConfig();\n const monitoring = await getMonitoringConfig(apps);\n\n const env = {\n ...(\"env\" in db ? db.env : {}),\n ...billing.env,\n ...email.env,\n ...storage.env,\n ...analytics.env,\n ...monitoring.env,\n };\n\n return { db, email, billing, analytics, storage, monitoring, env };\n};\n\nconst cloneRepository = async (cwd: string, name: string, apps: App[]) => {\n const spinner = ora(`Cloning repository into ${name}...`).start();\n const projectDir = join(cwd, name);\n\n try {\n const url = (await hasSshAccess())\n ? sshUrl(config.repository)\n : httpsUrl(config.repository);\n await execa(\"git\", [\"clone\", \"-b\", \"main\", \"--single-branch\", url, name], {\n cwd,\n });\n\n await modifyFilesForMissingApps(projectDir, apps);\n\n spinner.succeed(\"Repository successfully pulled!\");\n return projectDir;\n } catch (error) {\n spinner.fail(\"Failed to clone TurboStarter! Please try again.\");\n logger.error(error);\n process.exit(1);\n }\n};\n\nconst modifyFilesForMissingApps = async (cwd: string, apps: App[]) => {\n const files = Object.values(App)\n .filter((app) => !apps.includes(app))\n .map((app) => fileModificationsByMissingApp[app])\n .flat();\n\n if (!files.length) {\n return;\n }\n\n const project = new Project({\n skipAddingFilesFromTsConfig: true,\n });\n\n for (const file of files) {\n if (file.action === \"remove\") {\n await removePath({ cwd, path: file.path });\n }\n\n if (file.action === \"modify\") {\n if (isJsonFile(file)) {\n const data = await promises.readFile(join(cwd, file.path), \"utf8\");\n const parsed: unknown = JSON.parse(data);\n if (!enforceSchema(parsed, file.schema)) {\n continue;\n }\n const modified = file.modify(parsed);\n await promises.writeFile(\n join(cwd, file.path),\n JSON.stringify(modified, null, 2),\n );\n }\n\n if (isTypescriptFile(file)) {\n const sourceFile = project.addSourceFileAtPath(join(cwd, file.path));\n file.modify(sourceFile);\n await sourceFile.save();\n }\n }\n }\n};\n\nconst configureGit = async (cwd: string, apps: App[]) => {\n const spinner = ora(`Configuring Git...`).start();\n const missingApps = Object.values(App).filter((app) => !apps.includes(app));\n\n try {\n const upstreamUrl = (await hasSshAccess())\n ? sshUrl(config.repository)\n : httpsUrl(config.repository);\n await setUpstreamRemote(upstreamUrl, { cwd });\n\n if (missingApps.length > 0) {\n await execa(\n \"git\",\n [\n \"commit\",\n \"-am\",\n \"chore: remove unnecessary files after project initialization\",\n ],\n { cwd },\n );\n }\n\n spinner.succeed(\"Git successfully configured!\");\n } catch (error) {\n spinner.fail(\"Failed to configure Git! Please try again.\");\n logger.error(error);\n process.exit(1);\n }\n};\n\nconst installDependencies = async (cwd: string) => {\n const spinner = ora(`Installing dependencies...`).start();\n\n try {\n await execa(\"pnpm\", [\"install\"], { cwd });\n await execa(\"pnpm\", [\"format:fix\"], { cwd });\n\n spinner.succeed(\"Dependencies successfully installed!\");\n } catch (error) {\n spinner.fail(\"Failed to install dependencies! Please try again.\");\n logger.error(error);\n process.exit(1);\n }\n};\n\nconst updateProvidersFiles = async (\n cwd: string,\n providers: {\n email?: EmailProvider;\n storage?: StorageProvider;\n billing?: Partial<BillingProvider>;\n analytics?: Partial<AnalyticsProvider>;\n monitoring?: Partial<MonitoringProvider>;\n },\n) => {\n const spinner = ora(`Updating providers files...`).start();\n\n try {\n if (providers.email) {\n await replaceInFiles({\n cwd,\n paths: providerConfigFiles.email.files,\n pattern: providerConfigFiles.email.pattern,\n value: providers.email,\n });\n }\n if (providers.storage) {\n await replaceInFiles({\n cwd,\n paths: providerConfigFiles.storage.files,\n pattern: providerConfigFiles.storage.pattern,\n value: providers.storage,\n });\n }\n if (providers.billing && Object.keys(providers.billing).length > 0) {\n await Promise.all(\n Object.entries(providers.billing).map(([key, value]) =>\n replaceInFiles({\n cwd,\n paths:\n providerConfigFiles.billing[key as keyof typeof BillingProvider]\n .files,\n pattern:\n providerConfigFiles.billing[key as keyof typeof BillingProvider]\n .pattern,\n value,\n }),\n ),\n );\n }\n if (providers.analytics && Object.keys(providers.analytics).length > 0) {\n await Promise.all(\n Object.entries(providers.analytics).map(([key, value]) =>\n replaceInFiles({\n cwd,\n paths:\n providerConfigFiles.analytics[\n key as keyof typeof AnalyticsProvider\n ].files,\n pattern:\n providerConfigFiles.analytics[\n key as keyof typeof AnalyticsProvider\n ].pattern,\n value,\n }),\n ),\n );\n }\n if (providers.monitoring && Object.keys(providers.monitoring).length > 0) {\n await Promise.all(\n Object.entries(providers.monitoring).map(([key, value]) =>\n replaceInFiles({\n cwd,\n paths:\n providerConfigFiles.monitoring[\n key as keyof typeof MonitoringProvider\n ].files,\n pattern:\n providerConfigFiles.monitoring[\n key as keyof typeof MonitoringProvider\n ].pattern,\n value,\n }),\n ),\n );\n }\n\n spinner.succeed(\"Providers files successfully updated!\");\n } catch (error) {\n spinner.fail(\"Failed to update providers files! Please try again.\");\n logger.error(error);\n process.exit(1);\n }\n};\n","import { Command } from \"commander\";\nimport { execa } from \"execa\";\nimport { promises } from \"fs\";\nimport ora from \"ora\";\nimport path from \"path\";\nimport color from \"picocolors\";\nimport { z } from \"zod\";\n\nimport { config } from \"~/config\";\nimport {\n getUpstreamRemoteUrl,\n hasSshAccess,\n httpsUrl,\n isUpstreamUrlValid,\n logger,\n setUpstreamRemote,\n sshUrl,\n getErrorOutput,\n isGitClean,\n} from \"~/utils\";\n\nconst projectUpdateOptionsSchema = z.object({\n cwd: z.string(),\n});\n\ntype ProjectUpdateResult =\n | {\n success: true;\n alreadyUpToDate: boolean;\n }\n | {\n success: false;\n hasConflicts: true;\n conflicts: string[];\n }\n | {\n success: false;\n reason: string;\n };\n\nexport const projectUpdateCommand = new Command()\n .name(\"update\")\n .description(\n \"pull the latest changes from the upstream TurboStarter repository\",\n )\n .option(\n \"-c, --cwd <cwd>\",\n \"the working directory. Defaults to the current directory.\",\n process.cwd(),\n )\n .action(async (opts: z.infer<typeof projectUpdateOptionsSchema>) => {\n const spinner = ora(\"Pulling latest changes from upstream...\").start();\n\n try {\n const options = projectUpdateOptionsSchema.parse({\n cwd: opts.cwd,\n });\n const result = await updateProject({ cwd: options.cwd });\n\n if (result.success) {\n if (result.alreadyUpToDate) {\n spinner.succeed(\"Already up to date.\");\n } else {\n spinner.succeed(\n `Successfully pulled latest changes from ${color.cyan(config.repository)}.`,\n );\n }\n return;\n }\n\n if (\"hasConflicts\" in result) {\n spinner.fail(\"Merge conflicts detected.\");\n logger.warn(`\\n${result.conflicts.length} conflicting file(s):`);\n result.conflicts.forEach((conflictPath) => {\n logger.log(` - ${conflictPath}`);\n });\n logger.warn(\"\\nPlease resolve them manually:\");\n logger.log(\" 1. Fix the conflicting files\");\n logger.log(` 2. Run: ${color.bold(\"git add .\")}`);\n logger.log(` 3. Run: ${color.bold(\"git commit\")}`);\n } else {\n spinner.fail(\"Failed to pull from upstream.\");\n logger.error(`\\n${result.reason}`);\n }\n\n process.exit(1);\n } catch (error) {\n spinner.fail(\"Failed to pull from upstream.\");\n logger.error(getErrorOutput(error));\n process.exit(1);\n }\n });\n\nconst updateProject = async ({\n cwd,\n}: z.infer<\n typeof projectUpdateOptionsSchema\n>): Promise<ProjectUpdateResult> => {\n const projectValidation = await isWithinTurboStarterProject(cwd);\n\n if (!projectValidation.valid) {\n return {\n success: false,\n reason: projectValidation.reason,\n };\n }\n\n const gitClean = await isGitClean({ cwd });\n\n if (!gitClean) {\n return {\n success: false,\n reason:\n \"Git working directory has uncommitted changes. Please commit or stash them before pulling upstream updates.\",\n };\n }\n\n let currentUpstreamUrl = await getUpstreamRemoteUrl({ cwd });\n\n if (!currentUpstreamUrl) {\n const useSsh = await hasSshAccess();\n const url = useSsh\n ? sshUrl(config.repository)\n : httpsUrl(config.repository);\n await setUpstreamRemote(url, { cwd });\n currentUpstreamUrl = url;\n } else if (!isUpstreamUrlValid(currentUpstreamUrl, config.repository)) {\n const useSsh = currentUpstreamUrl.startsWith(\"git@\");\n const expectedUrl = useSsh\n ? sshUrl(config.repository)\n : httpsUrl(config.repository);\n\n return {\n success: false,\n reason:\n `Upstream remote points to \"${currentUpstreamUrl}\" but expected \"${expectedUrl}\". ` +\n \"Please run: git remote set-url upstream <correct-url>\",\n };\n }\n\n await execa(\"git\", [\"fetch\", \"upstream\"], { cwd });\n\n try {\n const { stdout } = await execa(\n \"git\",\n [\"merge\", \"upstream/main\", \"--no-edit\"],\n {\n cwd,\n },\n );\n\n return {\n success: true,\n alreadyUpToDate: stdout.includes(\"Already up to date\"),\n };\n } catch (error) {\n const output = getErrorOutput(error);\n const hasMergeConflicts =\n output.includes(\"CONFLICT\") || output.includes(\"Automatic merge failed\");\n\n if (!hasMergeConflicts) {\n return {\n success: false,\n reason: `Merge failed: ${output}`,\n };\n }\n\n const { stdout } = await execa(\n \"git\",\n [\"diff\", \"--name-only\", \"--diff-filter=U\"],\n {\n cwd,\n },\n );\n const conflicts = stdout\n .trim()\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n\n return {\n success: false,\n hasConflicts: true,\n conflicts,\n };\n }\n};\n\nconst isWithinTurboStarterProject = async (\n cwd: string,\n): Promise<{ valid: true } | { valid: false; reason: string }> => {\n const normalizedCwd = path.resolve(cwd);\n const requiredMarkers = [\n \"package.json\",\n \"pnpm-workspace.yaml\",\n \"turbo.json\",\n \"apps/web/package.json\",\n \"packages/api/package.json\",\n ];\n\n const missingMarkers = (\n await Promise.all(\n requiredMarkers.map(async (marker) => {\n try {\n await promises.access(path.join(normalizedCwd, marker));\n return undefined;\n } catch {\n return marker;\n }\n }),\n )\n ).filter(Boolean);\n\n if (missingMarkers.length > 0) {\n return {\n valid: false,\n reason:\n \"This does not appear to be a TurboStarter project root. \" +\n \"Please run this command from your project root (or pass --cwd).\",\n };\n }\n\n return { valid: true };\n};\n","import { Command } from \"commander\";\n\nimport { projectUpdateCommand } from \"~/commands/project/update\";\n\nexport const projectCommand = new Command()\n .name(\"project\")\n .description(\"manage your TurboStarter project\")\n .addCommand(projectUpdateCommand);\n","","#!/usr/bin/env node\nimport { Command } from \"commander\";\n\nimport { newCommand } from \"~/commands/new\";\nimport { projectCommand } from \"~/commands/project\";\n\nimport packageInfo from \"../package.json\";\n\nprocess.on(\"SIGINT\", () => process.exit(0));\nprocess.on(\"SIGTERM\", () => process.exit(0));\n\nfunction main() {\n const program = new Command()\n .name(\"turbostarter\")\n .description(\n \"Your TurboStarter assistant for starting new projects, adding plugins and more.\",\n )\n .version(\n packageInfo.version || \"1.0.0\",\n \"-v, --version\",\n \"display the version number\",\n );\n\n program.addCommand(newCommand);\n program.addCommand(projectCommand);\n program.parse();\n}\n\nvoid main();\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turbostarter/cli",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "main": "index.mjs",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.mjs",
@@ -52,7 +52,8 @@
52
52
  "ora": "^6.1.2",
53
53
  "picocolors": "1.1.0",
54
54
  "prompts": "^2.4.2",
55
- "zod": "3.23.8"
55
+ "ts-morph": "27.0.2",
56
+ "zod": "4.3.6"
56
57
  },
57
58
  "scripts": {
58
59
  "start": "node dist/index.mjs",