@mars-stack/core 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/README.md +32 -0
  2. package/cursor/manifest.json +304 -0
  3. package/cursor/rules/mars-composition-patterns.mdc +186 -0
  4. package/cursor/rules/mars-data-access.mdc +26 -0
  5. package/cursor/rules/mars-project-structure.mdc +34 -0
  6. package/cursor/rules/mars-security.mdc +25 -0
  7. package/cursor/rules/mars-testing.mdc +24 -0
  8. package/cursor/rules/mars-ui-conventions.mdc +29 -0
  9. package/cursor/skills/mars-add-api-route/SKILL.md +120 -0
  10. package/cursor/skills/mars-add-audit-log/SKILL.md +373 -0
  11. package/cursor/skills/mars-add-blog/SKILL.md +447 -0
  12. package/cursor/skills/mars-add-command-palette/SKILL.md +438 -0
  13. package/cursor/skills/mars-add-component/SKILL.md +158 -0
  14. package/cursor/skills/mars-add-crud-routes/SKILL.md +221 -0
  15. package/cursor/skills/mars-add-e2e-test/SKILL.md +227 -0
  16. package/cursor/skills/mars-add-error-boundary/SKILL.md +472 -0
  17. package/cursor/skills/mars-add-feature/SKILL.md +174 -0
  18. package/cursor/skills/mars-add-middleware/SKILL.md +135 -0
  19. package/cursor/skills/mars-add-page/SKILL.md +153 -0
  20. package/cursor/skills/mars-add-prisma-model/SKILL.md +148 -0
  21. package/cursor/skills/mars-add-protected-resource/SKILL.md +192 -0
  22. package/cursor/skills/mars-add-role/SKILL.md +156 -0
  23. package/cursor/skills/mars-add-server-action/SKILL.md +167 -0
  24. package/cursor/skills/mars-add-webhook/SKILL.md +192 -0
  25. package/cursor/skills/mars-build-complete-feature/SKILL.md +228 -0
  26. package/cursor/skills/mars-build-dashboard/SKILL.md +211 -0
  27. package/cursor/skills/mars-build-data-table/SKILL.md +284 -0
  28. package/cursor/skills/mars-build-form/SKILL.md +229 -0
  29. package/cursor/skills/mars-build-landing-page/SKILL.md +248 -0
  30. package/cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
  31. package/cursor/skills/mars-configure-ai/SKILL.md +617 -0
  32. package/cursor/skills/mars-configure-analytics/SKILL.md +413 -0
  33. package/cursor/skills/mars-configure-dark-mode/SKILL.md +309 -0
  34. package/cursor/skills/mars-configure-email/SKILL.md +170 -0
  35. package/cursor/skills/mars-configure-email-verification/SKILL.md +333 -0
  36. package/cursor/skills/mars-configure-feature-flags/SKILL.md +361 -0
  37. package/cursor/skills/mars-configure-i18n/SKILL.md +518 -0
  38. package/cursor/skills/mars-configure-jobs/SKILL.md +500 -0
  39. package/cursor/skills/mars-configure-magic-links/SKILL.md +385 -0
  40. package/cursor/skills/mars-configure-multi-tenancy/SKILL.md +611 -0
  41. package/cursor/skills/mars-configure-notifications/SKILL.md +569 -0
  42. package/cursor/skills/mars-configure-oauth/SKILL.md +217 -0
  43. package/cursor/skills/mars-configure-onboarding/SKILL.md +483 -0
  44. package/cursor/skills/mars-configure-payments/SKILL.md +243 -0
  45. package/cursor/skills/mars-configure-realtime/SKILL.md +733 -0
  46. package/cursor/skills/mars-configure-search/SKILL.md +581 -0
  47. package/cursor/skills/mars-configure-storage/SKILL.md +273 -0
  48. package/cursor/skills/mars-configure-two-factor/SKILL.md +518 -0
  49. package/cursor/skills/mars-create-execution-plan/SKILL.md +204 -0
  50. package/cursor/skills/mars-create-seed/SKILL.md +191 -0
  51. package/cursor/skills/mars-deploy-to-vercel/SKILL.md +300 -0
  52. package/cursor/skills/mars-design-tokens/SKILL.md +138 -0
  53. package/cursor/skills/mars-setup-billing/SKILL.md +322 -0
  54. package/cursor/skills/mars-setup-project/SKILL.md +104 -0
  55. package/cursor/skills/mars-setup-teams/SKILL.md +688 -0
  56. package/cursor/skills/mars-test-api-route/SKILL.md +219 -0
  57. package/cursor/skills/mars-update-architecture-docs/SKILL.md +189 -0
  58. package/dist/api-error/index.d.ts +27 -0
  59. package/dist/api-error/index.d.ts.map +1 -0
  60. package/dist/api-error/index.js +2 -0
  61. package/dist/auth/credential-tag.d.ts +5 -0
  62. package/dist/auth/credential-tag.d.ts.map +1 -0
  63. package/dist/auth/credential-tag.js +2 -0
  64. package/dist/auth/crypto-utils.d.ts +43 -0
  65. package/dist/auth/crypto-utils.d.ts.map +1 -0
  66. package/dist/auth/crypto-utils.js +1 -0
  67. package/dist/auth/csrf.d.ts +32 -0
  68. package/dist/auth/csrf.d.ts.map +1 -0
  69. package/dist/auth/csrf.js +2 -0
  70. package/dist/auth/hooks/index.d.ts +4 -0
  71. package/dist/auth/hooks/index.d.ts.map +1 -0
  72. package/dist/auth/hooks/index.js +68 -0
  73. package/dist/auth/hooks/useCSRF.d.ts +7 -0
  74. package/dist/auth/hooks/useCSRF.d.ts.map +1 -0
  75. package/dist/auth/hooks/usePasswordStrength.d.ts +17 -0
  76. package/dist/auth/hooks/usePasswordStrength.d.ts.map +1 -0
  77. package/dist/auth/internal-api-key.d.ts +5 -0
  78. package/dist/auth/internal-api-key.d.ts.map +1 -0
  79. package/dist/auth/internal-api-key.js +30 -0
  80. package/dist/auth/link-utils.d.ts +13 -0
  81. package/dist/auth/link-utils.d.ts.map +1 -0
  82. package/dist/auth/link-utils.js +1 -0
  83. package/dist/auth/middleware.d.ts +56 -0
  84. package/dist/auth/middleware.d.ts.map +1 -0
  85. package/dist/auth/middleware.js +3 -0
  86. package/dist/auth/password.d.ts +28 -0
  87. package/dist/auth/password.d.ts.map +1 -0
  88. package/dist/auth/password.js +1 -0
  89. package/dist/auth/reset-token.d.ts +3 -0
  90. package/dist/auth/reset-token.d.ts.map +1 -0
  91. package/dist/auth/reset-token.js +9 -0
  92. package/dist/auth/responses.d.ts +15 -0
  93. package/dist/auth/responses.d.ts.map +1 -0
  94. package/dist/auth/responses.js +2 -0
  95. package/dist/auth/session.d.ts +79 -0
  96. package/dist/auth/session.d.ts.map +1 -0
  97. package/dist/auth/session.js +1 -0
  98. package/dist/auth/types.d.ts +18 -0
  99. package/dist/auth/types.d.ts.map +1 -0
  100. package/dist/auth/types.js +10 -0
  101. package/dist/auth/validation.d.ts +146 -0
  102. package/dist/auth/validation.d.ts.map +1 -0
  103. package/dist/auth/validation.js +116 -0
  104. package/dist/auth/validators.d.ts +4 -0
  105. package/dist/auth/validators.d.ts.map +1 -0
  106. package/dist/auth/validators.js +27 -0
  107. package/dist/auth/verification.d.ts +54 -0
  108. package/dist/auth/verification.d.ts.map +1 -0
  109. package/dist/auth/verification.js +39 -0
  110. package/dist/chunk-4LS3QDD5.js +162 -0
  111. package/dist/chunk-ABBUHT5Z.js +110 -0
  112. package/dist/chunk-CTYAVMOF.js +15 -0
  113. package/dist/chunk-GVLH2GQP.js +14 -0
  114. package/dist/chunk-HOSMMQMA.js +109 -0
  115. package/dist/chunk-MXQ66RUN.js +28 -0
  116. package/dist/chunk-PZE3JGXO.js +149 -0
  117. package/dist/chunk-QAH2Y5WK.js +93 -0
  118. package/dist/chunk-QWMN5UJC.js +76 -0
  119. package/dist/chunk-ROQV54MU.js +117 -0
  120. package/dist/chunk-U4NZQ366.js +46 -0
  121. package/dist/chunk-WBJOIENS.js +22 -0
  122. package/dist/chunk-WO6FHJHG.js +29 -0
  123. package/dist/chunk-Z5BEKPJI.js +96 -0
  124. package/dist/chunk-ZA46T6GX.js +24 -0
  125. package/dist/configure-mars.d.ts +104 -0
  126. package/dist/configure-mars.d.ts.map +1 -0
  127. package/dist/database/index.d.ts +8 -0
  128. package/dist/database/index.d.ts.map +1 -0
  129. package/dist/database/index.js +1 -0
  130. package/dist/email/index.d.ts +25 -0
  131. package/dist/email/index.d.ts.map +1 -0
  132. package/dist/email/index.js +2 -0
  133. package/dist/email/types.d.ts +18 -0
  134. package/dist/email/types.d.ts.map +1 -0
  135. package/dist/env/index.d.ts +36 -0
  136. package/dist/env/index.d.ts.map +1 -0
  137. package/dist/env/index.js +1 -0
  138. package/dist/index.d.ts +6 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +163 -0
  141. package/dist/logger/index.d.ts +80 -0
  142. package/dist/logger/index.d.ts.map +1 -0
  143. package/dist/logger/index.js +1 -0
  144. package/dist/payments/index.d.ts +53 -0
  145. package/dist/payments/index.d.ts.map +1 -0
  146. package/dist/payments/index.js +72 -0
  147. package/dist/plugin/builtin/email-plugins.d.ts +10 -0
  148. package/dist/plugin/builtin/email-plugins.d.ts.map +1 -0
  149. package/dist/plugin/builtin/index.d.ts +4 -0
  150. package/dist/plugin/builtin/index.d.ts.map +1 -0
  151. package/dist/plugin/builtin/index.js +324 -0
  152. package/dist/plugin/builtin/payment-plugins.d.ts +4 -0
  153. package/dist/plugin/builtin/payment-plugins.d.ts.map +1 -0
  154. package/dist/plugin/builtin/storage-plugins.d.ts +5 -0
  155. package/dist/plugin/builtin/storage-plugins.d.ts.map +1 -0
  156. package/dist/plugin/index.d.ts +21 -0
  157. package/dist/plugin/index.d.ts.map +1 -0
  158. package/dist/plugin/index.js +30 -0
  159. package/dist/rate-limit/index.d.ts +89 -0
  160. package/dist/rate-limit/index.d.ts.map +1 -0
  161. package/dist/rate-limit/index.js +166 -0
  162. package/dist/seo/faq.d.ts +37 -0
  163. package/dist/seo/faq.d.ts.map +1 -0
  164. package/dist/seo/index.d.ts +75 -0
  165. package/dist/seo/index.d.ts.map +1 -0
  166. package/dist/seo/index.js +1 -0
  167. package/dist/storage/index.d.ts +50 -0
  168. package/dist/storage/index.d.ts.map +1 -0
  169. package/dist/storage/index.js +211 -0
  170. package/dist/test-utils/factories.d.ts +38 -0
  171. package/dist/test-utils/factories.d.ts.map +1 -0
  172. package/dist/test-utils/index.d.ts +6 -0
  173. package/dist/test-utils/index.d.ts.map +1 -0
  174. package/dist/test-utils/index.js +117 -0
  175. package/dist/test-utils/mock-auth.d.ts +25 -0
  176. package/dist/test-utils/mock-auth.d.ts.map +1 -0
  177. package/dist/test-utils/mock-prisma.d.ts +55 -0
  178. package/dist/test-utils/mock-prisma.d.ts.map +1 -0
  179. package/dist/test-utils/render.d.ts +4 -0
  180. package/dist/test-utils/render.d.ts.map +1 -0
  181. package/dist/test-utils/request-helpers.d.ts +6 -0
  182. package/dist/test-utils/request-helpers.d.ts.map +1 -0
  183. package/dist/types.d.ts +53 -0
  184. package/dist/types.d.ts.map +1 -0
  185. package/dist/utils/math.d.ts +2 -0
  186. package/dist/utils/math.d.ts.map +1 -0
  187. package/dist/utils/math.js +7 -0
  188. package/dist/utils/optional-import.d.ts +14 -0
  189. package/dist/utils/optional-import.d.ts.map +1 -0
  190. package/package.json +205 -0
  191. package/scripts/generate-skill-adapters.ts +146 -0
  192. package/scripts/postinstall.mjs +146 -0
@@ -0,0 +1,138 @@
1
+ # Skill: Work with the Design Token System
2
+
3
+ Understand and extend the MARS three-layer design token architecture for theming and UI consistency.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when the user asks about theming, changing colours, adding dark mode support, creating new design tokens, or understanding the styling system.
8
+
9
+ ## Architecture Overview
10
+
11
+ ```
12
+ Layer 1: Primitive Palette (@mars-stack/ui/styles/primitives.css)
13
+ ↓ referenced by
14
+ Layer 2: Semantic Tokens (@mars-stack/ui/styles/tokens.css)
15
+ ↓ registered as
16
+ Layer 3: Tailwind Theme (@mars-stack/ui/styles/theme.css via @theme)
17
+ ↓ used in
18
+ Components (via Tailwind utility classes)
19
+ ```
20
+
21
+ ### Layer 1: Primitive Palette (`@mars-stack/ui/styles/primitives.css`)
22
+
23
+ Raw colour scales generated from the primary colour. These are building blocks, never used directly in components.
24
+
25
+ ```css
26
+ :root {
27
+ --brand-50: oklch(0.97 0.01 250);
28
+ --brand-100: oklch(0.93 0.03 250);
29
+ /* ... full 50-950 scale */
30
+ --gray-50: oklch(0.985 0 0);
31
+ --gray-100: oklch(0.97 0 0);
32
+ /* ... */
33
+ }
34
+ ```
35
+
36
+ ### Layer 2: Semantic Tokens (`@mars-stack/ui/styles/tokens.css`)
37
+
38
+ What colours *mean*. These swap between light and dark themes.
39
+
40
+ ```css
41
+ :root {
42
+ --surface-card: white;
43
+ --text-primary: var(--gray-900);
44
+ --border-input: var(--gray-300);
45
+ --brand-primary: var(--brand-600);
46
+ }
47
+
48
+ .dark {
49
+ --surface-card: var(--gray-900);
50
+ --text-primary: var(--gray-100);
51
+ --border-input: var(--gray-700);
52
+ --brand-primary: var(--brand-500);
53
+ }
54
+ ```
55
+
56
+ ### Layer 3: Tailwind Theme (`@mars-stack/ui/styles/theme.css`)
57
+
58
+ Registers semantic tokens as Tailwind utilities via `@theme`:
59
+
60
+ ```css
61
+ @theme {
62
+ --color-surface-background: var(--surface-background);
63
+ --color-surface-card: var(--surface-card);
64
+ --color-text-primary: var(--text-primary);
65
+ --color-border-default: var(--border-default);
66
+ --color-brand-primary: var(--brand-primary);
67
+ /* ... */
68
+ }
69
+ ```
70
+
71
+ This enables `bg-surface-card`, `text-text-primary`, `border-border-default`, etc. in components.
72
+
73
+ ## Token Categories
74
+
75
+ | Category | Prefix | Examples |
76
+ |----------|--------|---------|
77
+ | Surfaces | `surface-` | `bg-surface-background`, `bg-surface-card`, `bg-surface-input` |
78
+ | Text | `text-` | `text-text-primary`, `text-text-secondary`, `text-text-muted` |
79
+ | Borders | `border-` | `border-border-default`, `border-border-input`, `border-border-focus` |
80
+ | Brand | `brand-` | `bg-brand-primary`, `hover:bg-brand-primary-hover` |
81
+ | Feedback | varies | `bg-success-muted`, `text-text-error`, `border-border-success` |
82
+ | Interactive | varies | `bg-ghost-hover`, `focus:ring-ring-focus`, `bg-danger-bg` |
83
+ | Navigation | `nav-` | `text-nav-item`, `bg-nav-item-active-bg` |
84
+ | Table | `table-` | `bg-table-header-bg`, `hover:bg-table-row-hover` |
85
+ | Avatar | `avatar-` | `bg-avatar-bg`, `text-avatar-text` |
86
+
87
+ ## How to Change the Theme
88
+
89
+ ### Change Primary Colour
90
+
91
+ 1. Edit `@mars-stack/ui/styles/primitives.css` -- update the `--brand-*` scale to your new colour's oklch values.
92
+ 2. Everything else (semantic tokens, components) updates automatically.
93
+
94
+ ### Add a New Semantic Token
95
+
96
+ 1. Define the token in `@mars-stack/ui/styles/tokens.css` for both `:root` and `.dark`:
97
+ ```css
98
+ :root { --sidebar-accent: var(--brand-100); }
99
+ .dark { --sidebar-accent: var(--brand-900); }
100
+ ```
101
+
102
+ 2. Register it in `@mars-stack/ui/styles/theme.css`:
103
+ ```css
104
+ @theme {
105
+ --color-sidebar-accent: var(--sidebar-accent);
106
+ }
107
+ ```
108
+
109
+ 3. Use in components: `bg-sidebar-accent`.
110
+
111
+ ### Add a New Colour Scale
112
+
113
+ 1. Add the full 50-950 scale in `@mars-stack/ui/styles/primitives.css`.
114
+ 2. Add semantic tokens that reference it in `@mars-stack/ui/styles/tokens.css`.
115
+ 3. Register the semantic tokens in `@mars-stack/ui/styles/theme.css`.
116
+
117
+ ## Rules
118
+
119
+ - **Never use raw Tailwind colours** (`text-gray-900`, `bg-blue-500`) in components. Always use semantic tokens.
120
+ - **Always define dark mode counterparts** when adding new semantic tokens.
121
+ - **Components get their colours from Layer 3** (Tailwind utilities backed by semantic tokens). They never reference Layer 1 directly.
122
+ - The `app.config.ts` `theme.primaryColor` field is used by the CLI during scaffolding to generate the correct primitive palette. At runtime, the CSS tokens are the source of truth.
123
+
124
+ ## Breakpoints
125
+
126
+ Responsive breakpoints are defined in `@mars-stack/ui/styles/breakpoints.css` and registered in theme.css:
127
+
128
+ ```css
129
+ @theme {
130
+ --breakpoint-sm: 640px;
131
+ --breakpoint-md: 768px;
132
+ --breakpoint-lg: 1024px;
133
+ --breakpoint-xl: 1280px;
134
+ --breakpoint-2xl: 1536px;
135
+ }
136
+ ```
137
+
138
+ Use as standard Tailwind responsive prefixes: `md:grid-cols-2`, `lg:px-8`.
@@ -0,0 +1,322 @@
1
+ # Skill: Setup Billing (Meta-Skill)
2
+
3
+ Orchestrate the complete billing and subscription system: Stripe integration, webhook handling, subscription UI, customer portal, and documentation updates.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when the user asks to:
8
+ - Set up billing or subscriptions
9
+ - Add a paywall or premium features
10
+ - Integrate Stripe end-to-end
11
+ - Add pricing page with checkout
12
+
13
+ This meta-skill chains sub-skills in the correct order. For just the Stripe SDK setup, use `configure-payments` instead.
14
+
15
+ ## Prerequisites
16
+
17
+ - A Stripe account at [stripe.com](https://stripe.com)
18
+ - Read `src/config/app.config.ts` to check if billing is already flagged
19
+ - Read `prisma/schema/` to check for existing billing models
20
+
21
+ ## Execution Order
22
+
23
+ ```
24
+ ┌─────────────────────────────────────────────────────┐
25
+ │ Phase 0: Plan │
26
+ │ create-execution-plan │
27
+ ├─────────────────────────────────────────────────────┤
28
+ │ Phase 1: Payment Provider │
29
+ │ configure-payments (Stripe SDK, env vars, client) │
30
+ ├─────────────────────────────────────────────────────┤
31
+ │ Phase 2: Data Layer │
32
+ │ add-prisma-model (Subscription, WebhookEvent) │
33
+ ├─────────────────────────────────────────────────────┤
34
+ │ Phase 3: Webhook │
35
+ │ add-webhook (Stripe webhook handler) │
36
+ ├─────────────────────────────────────────────────────┤
37
+ │ Phase 4: Feature Module │
38
+ │ add-feature (billing service, checkout, portal) │
39
+ ├─────────────────────────────────────────────────────┤
40
+ │ Phase 5: API Routes │
41
+ │ add-api-route (checkout, portal, subscription) │
42
+ ├─────────────────────────────────────────────────────┤
43
+ │ Phase 6: UI │
44
+ │ add-page (pricing, billing dashboard) │
45
+ │ ↳ If pricing page needed: build-landing-page │
46
+ ├─────────────────────────────────────────────────────┤
47
+ │ Phase 7: Testing & Docs │
48
+ │ test-api-route → update-architecture-docs │
49
+ └─────────────────────────────────────────────────────┘
50
+ ```
51
+
52
+ ## Phase 0: Create Execution Plan
53
+
54
+ **Skill:** `create-execution-plan`
55
+
56
+ Create `docs/exec-plans/active/setup-billing.md` with all tasks listed below.
57
+
58
+ ## Phase 1: Payment Provider Setup
59
+
60
+ **Skill:** `configure-payments`
61
+
62
+ 1. Install Stripe SDK: `yarn add stripe`
63
+ 2. Set environment variables:
64
+ - `STRIPE_SECRET_KEY`
65
+ - `STRIPE_WEBHOOK_SECRET`
66
+ - `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`
67
+ 3. Create the lazy Stripe client at `src/features/billing/server/stripe.ts`
68
+ 4. Enable the billing feature flag:
69
+
70
+ ```typescript
71
+ // src/config/app.config.ts
72
+ features: {
73
+ billing: true,
74
+ }
75
+ ```
76
+
77
+ **Decision: Payment model?**
78
+
79
+ | Model | Use when | Stripe mode |
80
+ |-------|----------|-------------|
81
+ | Subscription | Monthly/annual recurring billing | `mode: 'subscription'` |
82
+ | One-time | Single purchases, credits | `mode: 'payment'` |
83
+ | Usage-based | Pay per API call, storage, etc. | `mode: 'subscription'` + metered billing |
84
+
85
+ ## Phase 2: Data Layer
86
+
87
+ **Skill:** `add-prisma-model`
88
+
89
+ ### Subscription model
90
+
91
+ ```prisma
92
+ // prisma/schema/billing.prisma
93
+ model Subscription {
94
+ id String @id @default(cuid())
95
+ userId String @unique
96
+ stripeCustomerId String @unique
97
+ stripeSubscriptionId String? @unique
98
+ stripePriceId String?
99
+ status String @default("inactive")
100
+ currentPeriodEnd DateTime?
101
+ cancelAtPeriodEnd Boolean @default(false)
102
+ createdAt DateTime @default(now())
103
+ updatedAt DateTime @updatedAt
104
+
105
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
106
+
107
+ @@index([stripeCustomerId])
108
+ @@index([status])
109
+ }
110
+ ```
111
+
112
+ ### Webhook event model (for idempotency)
113
+
114
+ ```prisma
115
+ model WebhookEvent {
116
+ id String @id @default(cuid())
117
+ externalId String @unique
118
+ type String
119
+ processedAt DateTime @default(now())
120
+
121
+ @@index([externalId])
122
+ }
123
+ ```
124
+
125
+ Run `yarn db:push`.
126
+
127
+ ## Phase 3: Webhook Handler
128
+
129
+ **Skill:** `add-webhook`
130
+
131
+ Create `src/app/api/webhooks/stripe/route.ts`:
132
+
133
+ 1. Verify Stripe signature using `stripe.webhooks.constructEvent`
134
+ 2. Check idempotency (has this event been processed?)
135
+ 3. Handle key events:
136
+
137
+ | Event | Handler | Action |
138
+ |-------|---------|--------|
139
+ | `checkout.session.completed` | `handleCheckoutCompleted` | Create/update subscription, set status to active |
140
+ | `customer.subscription.updated` | `handleSubscriptionUpdated` | Sync status, price, period end, cancel flag |
141
+ | `customer.subscription.deleted` | `handleSubscriptionDeleted` | Set status to canceled |
142
+ | `invoice.payment_failed` | `handlePaymentFailed` | Notify user, set status to past_due |
143
+
144
+ 4. Exclude webhook route from CSRF in middleware
145
+ 5. Return 200 quickly — do heavy processing inline (Vercel has 10s limit)
146
+
147
+ **If payment model is one-time instead of subscription:**
148
+
149
+ Handle `checkout.session.completed` to record the purchase. Skip subscription update/delete events.
150
+
151
+ **If payment model is usage-based:**
152
+
153
+ Additionally handle `invoice.created` to add usage line items before the invoice is finalized.
154
+
155
+ ## Phase 4: Feature Module
156
+
157
+ **Skill:** `add-feature`
158
+
159
+ Create `src/features/billing/`:
160
+
161
+ ```
162
+ src/features/billing/
163
+ ├── components/
164
+ │ ├── PricingCard.tsx # Individual plan card
165
+ │ ├── PricingTable.tsx # All plans side by side
166
+ │ ├── SubscriptionStatus.tsx # Current plan display
167
+ │ └── index.ts
168
+ ├── server/
169
+ │ ├── stripe.ts # Lazy Stripe client (from Phase 1)
170
+ │ ├── index.ts # Subscription queries + helpers
171
+ │ └── checkout.ts # Checkout session creation
172
+ ├── validation/
173
+ │ └── schemas.ts # Zod schemas for billing inputs
174
+ └── types.ts # Billing TypeScript types
175
+ ```
176
+
177
+ Key server functions:
178
+
179
+ ```typescript
180
+ // src/features/billing/server/index.ts
181
+ export { getStripe } from './stripe';
182
+
183
+ export async function getUserSubscription(userId: string) { ... }
184
+ export async function isSubscribed(userId: string): Promise<boolean> { ... }
185
+ export async function getSubscriptionStatus(userId: string) { ... }
186
+ ```
187
+
188
+ ## Phase 5: API Routes
189
+
190
+ **Skill:** `add-api-route`
191
+
192
+ | Route | Method | Auth | Purpose |
193
+ |-------|--------|------|---------|
194
+ | `/api/protected/billing/checkout` | POST | `withAuthNoParams` | Create Stripe Checkout session |
195
+ | `/api/protected/billing/portal` | POST | `withAuthNoParams` | Create Stripe Customer Portal session |
196
+ | `/api/protected/billing/subscription` | GET | `withAuthNoParams` | Get current subscription status |
197
+
198
+ ### Checkout route
199
+
200
+ Accepts `{ priceId: string }`. Creates or retrieves Stripe customer, then creates a Checkout Session with success/cancel URLs.
201
+
202
+ ### Portal route
203
+
204
+ Creates a Billing Portal session for subscription management. Requires an existing Stripe customer.
205
+
206
+ ### Subscription route
207
+
208
+ Returns the current subscription status, plan, and renewal date for display in the UI.
209
+
210
+ ## Phase 6: UI
211
+
212
+ **Skill:** `add-page`
213
+
214
+ ### Pricing page (public)
215
+
216
+ Create `src/app/(public)/pricing/page.tsx` or include in the landing page:
217
+
218
+ ```tsx
219
+ // Uses PricingTable component
220
+ // Shows plans with features and CTAs
221
+ // Unauthenticated users → redirect to signup with plan param
222
+ // Authenticated users → redirect to checkout
223
+ ```
224
+
225
+ ### Billing dashboard (protected)
226
+
227
+ Create `src/app/(protected)/billing/page.tsx`:
228
+
229
+ ```tsx
230
+ // Shows current subscription status
231
+ // "Manage Subscription" → opens Stripe Customer Portal
232
+ // "Upgrade" → creates checkout session
233
+ // Shows billing history (if available)
234
+ ```
235
+
236
+ **Conditional: If a pricing page is needed on the landing page:**
237
+
238
+ **Skill:** `build-landing-page` — Include a pricing section with plan comparison.
239
+
240
+ ### Subscription gate component
241
+
242
+ ```tsx
243
+ // src/features/billing/components/SubscriptionGate.tsx
244
+ 'use client';
245
+
246
+ export function SubscriptionGate({
247
+ children,
248
+ fallback,
249
+ }: {
250
+ children: React.ReactNode;
251
+ fallback?: React.ReactNode;
252
+ }) {
253
+ // Check subscription status
254
+ // If subscribed, render children
255
+ // If not, render fallback (upgrade prompt)
256
+ }
257
+ ```
258
+
259
+ ## Phase 7: Testing & Documentation
260
+
261
+ **Skill:** `test-api-route` + `update-architecture-docs`
262
+
263
+ ### Tests
264
+
265
+ 1. Unit test checkout route (mock Stripe, verify session creation)
266
+ 2. Unit test portal route (mock Stripe, verify portal session)
267
+ 3. Unit test webhook handler (mock signature verification, verify DB updates)
268
+ 4. Unit test `isSubscribed` helper
269
+ 5. E2E test: full checkout flow (requires Stripe test mode)
270
+
271
+ ### Webhook local testing
272
+
273
+ ```bash
274
+ stripe listen --forward-to localhost:3000/api/webhooks/stripe
275
+ ```
276
+
277
+ ### Documentation updates
278
+
279
+ 1. Update `docs/QUALITY_SCORE.md`:
280
+
281
+ ```markdown
282
+ | Billing | B | Stripe checkout, portal, webhook, subscription gate |
283
+ ```
284
+
285
+ 2. Update `ARCHITECTURE.md` if this is the first payment integration
286
+ 3. Mark execution plan as complete
287
+
288
+ ## Environment Variables Summary
289
+
290
+ ```bash
291
+ # Required
292
+ STRIPE_SECRET_KEY=sk_test_...
293
+ STRIPE_WEBHOOK_SECRET=whsec_...
294
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
295
+
296
+ # Optional
297
+ STRIPE_PRICE_ID_MONTHLY=price_...
298
+ STRIPE_PRICE_ID_YEARLY=price_...
299
+ ```
300
+
301
+ ## Checklist
302
+
303
+ - [ ] Execution plan created
304
+ - [ ] Stripe SDK installed
305
+ - [ ] Environment variables set
306
+ - [ ] Lazy Stripe client created
307
+ - [ ] Subscription + WebhookEvent Prisma models
308
+ - [ ] Webhook handler with signature verification and idempotency
309
+ - [ ] Webhook excluded from CSRF in middleware
310
+ - [ ] Billing feature module with server/components/validation
311
+ - [ ] Checkout API route
312
+ - [ ] Portal API route
313
+ - [ ] Subscription status API route
314
+ - [ ] Pricing page (public or landing page section)
315
+ - [ ] Billing dashboard page (protected)
316
+ - [ ] SubscriptionGate component for premium features
317
+ - [ ] `isSubscribed` helper for server-side gating
318
+ - [ ] Feature flag `appConfig.features.billing` set to `true`
319
+ - [ ] Unit tests for API routes and webhook
320
+ - [ ] Local webhook testing verified
321
+ - [ ] QUALITY_SCORE.md updated
322
+ - [ ] Execution plan completed
@@ -0,0 +1,104 @@
1
+ # Skill: Set Up a MARS Project
2
+
3
+ Guide for setting up a new MARS project from scratch or getting an existing one running locally.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when the user asks how to get started, set up their project, run locally, or troubleshoot a fresh install.
8
+
9
+ ## New Project (via CLI)
10
+
11
+ ```bash
12
+ npx @mars-stack/cli create
13
+ ```
14
+
15
+ The CLI will prompt for project name, features, services, and theme, then scaffold a ready-to-run project.
16
+
17
+ ## Existing Project Setup
18
+
19
+ ### 1. Install Dependencies
20
+
21
+ ```bash
22
+ yarn install
23
+ ```
24
+
25
+ ### 2. Environment Variables
26
+
27
+ Copy the example and fill in required values:
28
+
29
+ ```bash
30
+ cp .env.example .env
31
+ ```
32
+
33
+ **Required variables:**
34
+ - `DATABASE_URL` -- Postgres connection string
35
+ - `JWT_SECRET` -- Minimum 32 characters (generate with `openssl rand -hex 32`)
36
+
37
+ **Optional (based on features):**
38
+ - `SENDGRID_API_KEY` / `SENDGRID_FROM_EMAIL` -- if email provider is SendGrid
39
+ - `STRIPE_SECRET_KEY` / `STRIPE_WEBHOOK_SECRET` -- if payments enabled
40
+ - `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` -- for production rate limiting
41
+
42
+ ### 3. Database
43
+
44
+ **Option A: Local Docker (recommended for development)**
45
+
46
+ ```bash
47
+ yarn dev:services # Starts Postgres + Redis via Docker Compose
48
+ yarn db:push # Push schema to database
49
+ ```
50
+
51
+ **Option B: Cloud database (Neon, Supabase, etc.)**
52
+
53
+ Set `DATABASE_URL` in `.env` to your cloud connection string. For Neon:
54
+ ```
55
+ DATABASE_URL="postgresql://user:pass@ep-xxx.region.aws.neon.tech/dbname?sslmode=require"
56
+ ```
57
+
58
+ Then: `yarn db:push`
59
+
60
+ ### 4. Start Development
61
+
62
+ ```bash
63
+ yarn dev # Starts Next.js with Turbopack
64
+ ```
65
+
66
+ The app will be available at `http://localhost:3000`.
67
+
68
+ ## Health Check
69
+
70
+ Run the built-in doctor to verify your setup:
71
+
72
+ ```bash
73
+ npx @mars-stack/cli doctor
74
+ ```
75
+
76
+ This checks: Node.js version, Yarn, Docker, Git, `.env` file, Prisma schema, database connection, and JWT_SECRET.
77
+
78
+ ## Common Issues
79
+
80
+ ### "Cannot connect to the database"
81
+ - Is Docker running? `docker ps`
82
+ - Start services: `yarn dev:services`
83
+ - Check `DATABASE_URL` in `.env`
84
+
85
+ ### "Database tables are missing"
86
+ - Run `yarn db:push` to create tables from the Prisma schema
87
+
88
+ ### "JWT_SECRET is required"
89
+ - Generate one: `openssl rand -hex 32`
90
+ - Add to `.env`: `JWT_SECRET="<generated-value>"`
91
+
92
+ ### Build fails with environment errors
93
+ - The env validation is lazy and skips during build. Ensure `.env` exists for runtime.
94
+ - `APP_URL` is optional (falls back to `localhost:3000` in dev).
95
+
96
+ ## Key Files
97
+
98
+ | File | Purpose |
99
+ |------|---------|
100
+ | `src/config/app.config.ts` | App identity, features, services, theme |
101
+ | `.env` | Environment secrets (never commit) |
102
+ | `docker-compose.yml` | Local Postgres + Redis |
103
+ | `prisma/schema/` | Database models (multi-file) |
104
+ | `src/middleware.ts` | Auth redirects, CSRF, coming-soon mode |