@donotdev/cli 0.0.14 → 0.0.15

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 (151) hide show
  1. package/dependencies-matrix.json +356 -88
  2. package/dist/bin/commands/agent-setup.js +7 -1
  3. package/dist/bin/commands/build.js +118 -38
  4. package/dist/bin/commands/bump.js +74 -28
  5. package/dist/bin/commands/cacheout.js +37 -9
  6. package/dist/bin/commands/create-app.js +222 -115
  7. package/dist/bin/commands/create-project.js +455 -140
  8. package/dist/bin/commands/deploy.js +1736 -697
  9. package/dist/bin/commands/dev.js +138 -23
  10. package/dist/bin/commands/emu.js +215 -58
  11. package/dist/bin/commands/format.js +37 -9
  12. package/dist/bin/commands/lint.js +37 -9
  13. package/dist/bin/commands/preview.js +142 -23
  14. package/dist/bin/commands/supabase-setup.d.ts +6 -0
  15. package/dist/bin/commands/supabase-setup.d.ts.map +1 -0
  16. package/dist/bin/commands/supabase-setup.js +7 -0
  17. package/dist/bin/commands/supabase-setup.js.map +1 -0
  18. package/dist/bin/commands/sync-secrets.js +211 -34
  19. package/dist/bin/commands/type-check.d.ts +14 -0
  20. package/dist/bin/commands/type-check.d.ts.map +1 -0
  21. package/dist/bin/commands/type-check.js +314 -0
  22. package/dist/bin/commands/type-check.js.map +1 -0
  23. package/dist/bin/commands/wai.js +3 -1
  24. package/dist/bin/dndev.js +27 -2
  25. package/dist/bin/donotdev.js +27 -2
  26. package/dist/index.js +3960 -3015
  27. package/package.json +2 -2
  28. package/templates/app-demo/src/App.tsx.example +1 -0
  29. package/templates/app-demo/src/pages/FullPage.tsx.example +2 -2
  30. package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +2 -2
  31. package/templates/app-demo/src/themes.css.example +5 -12
  32. package/templates/app-expo/.env.example +64 -0
  33. package/templates/app-expo/.expo/README.md.example +5 -0
  34. package/templates/app-expo/.gitignore.example +36 -0
  35. package/templates/app-expo/README.md.example +58 -0
  36. package/templates/app-expo/app/.gitkeep +2 -0
  37. package/templates/app-expo/app/_layout.tsx.example +41 -0
  38. package/templates/app-expo/app/form.tsx.example +52 -0
  39. package/templates/app-expo/app/index.tsx.example +89 -0
  40. package/templates/app-expo/app/list.tsx.example +32 -0
  41. package/templates/app-expo/app/profile.tsx.example +76 -0
  42. package/templates/app-expo/app/signin.tsx.example +53 -0
  43. package/templates/app-expo/app.json.example +39 -0
  44. package/templates/app-expo/babel.config.js.example +10 -0
  45. package/templates/app-expo/eas.json.example +20 -0
  46. package/templates/app-expo/expo-env.d.ts.example +4 -0
  47. package/templates/app-expo/metro.config.js.example +20 -0
  48. package/templates/app-expo/service-account-key.json.example +12 -0
  49. package/templates/app-expo/tsconfig.json.example +19 -0
  50. package/templates/app-next/.env.example +4 -33
  51. package/templates/app-next/src/app/ClientLayout.tsx.example +2 -0
  52. package/templates/app-next/src/app/layout.tsx.example +7 -6
  53. package/templates/app-next/src/globals.css.example +2 -11
  54. package/templates/app-next/src/pages/HomePage.tsx.example +1 -1
  55. package/templates/app-next/src/themes.css.example +10 -13
  56. package/templates/app-vite/.env.example +3 -32
  57. package/templates/app-vite/index.html.example +2 -24
  58. package/templates/app-vite/src/App.tsx.example +2 -0
  59. package/templates/app-vite/src/globals.css.example +2 -12
  60. package/templates/app-vite/src/pages/FormPageExample.tsx.example +1 -2
  61. package/templates/app-vite/src/pages/HomePage.tsx.example +1 -1
  62. package/templates/app-vite/src/themes.css.example +109 -79
  63. package/templates/app-vite/vercel.json.example +11 -0
  64. package/templates/functions-firebase/build.mjs.example +2 -72
  65. package/templates/functions-firebase/functions-firebase/.env.example.example +23 -25
  66. package/templates/functions-firebase/functions-firebase/build.mjs.example +2 -72
  67. package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
  68. package/templates/functions-supabase/supabase/functions/cancel-subscription/index.ts.example +7 -0
  69. package/templates/functions-supabase/supabase/functions/change-plan/index.ts.example +11 -0
  70. package/templates/functions-supabase/supabase/functions/create-checkout-session/index.ts.example +11 -0
  71. package/templates/functions-supabase/supabase/functions/create-customer-portal/index.ts.example +7 -0
  72. package/templates/functions-supabase/supabase/functions/crud/index.ts.example +16 -0
  73. package/templates/functions-supabase/supabase/functions/delete-account/index.ts.example +7 -0
  74. package/templates/functions-supabase/supabase/functions/get-custom-claims/index.ts.example +7 -0
  75. package/templates/functions-supabase/supabase/functions/get-user-auth-status/index.ts.example +7 -0
  76. package/templates/functions-supabase/supabase/functions/refresh-subscription-status/index.ts.example +7 -0
  77. package/templates/functions-supabase/supabase/functions/remove-custom-claims/index.ts.example +7 -0
  78. package/templates/functions-supabase/supabase/functions/set-custom-claims/index.ts.example +7 -0
  79. package/templates/functions-supabase/supabase/migrations/20250101000000_idempotency.sql +24 -0
  80. package/templates/functions-supabase/supabase/migrations/20250101000001_rate_limits.sql +22 -0
  81. package/templates/functions-supabase/supabase/migrations/20250101000002_cleanup_jobs.sql +28 -0
  82. package/templates/functions-supabase/supabase/migrations/20250101000003_operation_metrics.sql +28 -0
  83. package/templates/functions-vercel/functions-vercel/tsconfig.json.example +1 -1
  84. package/templates/functions-vercel/functions-vercel/vercel.json.example +1 -1
  85. package/templates/functions-vercel/vercel.json.example +1 -1
  86. package/templates/github/github/workflows/firebase-deploy.yml.example +1 -1
  87. package/templates/github/workflows/firebase-deploy.yml.example +1 -1
  88. package/templates/overlay-firebase/env.fragment.example +34 -0
  89. package/templates/overlay-firebase/env.fragment.expo.example +34 -0
  90. package/templates/overlay-firebase/env.fragment.nextjs.example +34 -0
  91. package/templates/overlay-firebase/src/config/providers.expo.ts.example +49 -0
  92. package/templates/overlay-firebase/src/config/providers.ts.example +23 -0
  93. package/templates/overlay-supabase/env.fragment.example +7 -0
  94. package/templates/overlay-supabase/env.fragment.expo.example +7 -0
  95. package/templates/overlay-supabase/env.fragment.nextjs.example +7 -0
  96. package/templates/overlay-supabase/src/config/providers.expo.ts.example +35 -0
  97. package/templates/overlay-supabase/src/config/providers.ts.example +33 -0
  98. package/templates/overlay-supabase/vercel.headers.example +23 -0
  99. package/templates/overlay-supabase/vercel.json.example +22 -0
  100. package/templates/overlay-vercel/env.fragment.example +34 -0
  101. package/templates/overlay-vercel/env.fragment.nextjs.example +34 -0
  102. package/templates/overlay-vercel/src/config/providers.ts.example +24 -0
  103. package/templates/root-consumer/.claude/agents/architect.md.example +2 -310
  104. package/templates/root-consumer/.claude/agents/builder.md.example +2 -326
  105. package/templates/root-consumer/.claude/agents/coder.md.example +2 -83
  106. package/templates/root-consumer/.claude/agents/extractor.md.example +2 -231
  107. package/templates/root-consumer/.claude/agents/polisher.md.example +2 -132
  108. package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +2 -81
  109. package/templates/root-consumer/.claude/commands/grill.md.example +30 -0
  110. package/templates/root-consumer/.claude/commands/techdebt.md.example +28 -0
  111. package/templates/root-consumer/.clinerules.example +1 -0
  112. package/templates/root-consumer/.cursor/rules/no-docs.mdc.example +15 -0
  113. package/templates/root-consumer/.cursorrules.example +1 -0
  114. package/templates/root-consumer/.github/copilot-instructions.md.example +1 -0
  115. package/templates/root-consumer/.windsurfrules.example +1 -0
  116. package/templates/root-consumer/AI.md.example +29 -123
  117. package/templates/root-consumer/CLAUDE.md.example +1 -134
  118. package/templates/root-consumer/CONVENTIONS.md.example +1 -0
  119. package/templates/root-consumer/GEMINI.md.example +1 -0
  120. package/templates/root-consumer/firebase.json.example +1 -1
  121. package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +20 -0
  122. package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +0 -18
  123. package/templates/root-consumer/guides/dndev/COMPONENTS_UI.md.example +1 -1
  124. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +99 -30
  125. package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -1
  126. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +143 -12
  127. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +9 -3
  128. package/templates/root-consumer/guides/dndev/SETUP_SOC2.md.example +234 -0
  129. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -0
  130. package/templates/root-consumer/guides/dndev/SETUP_THEMES.md.example +6 -2
  131. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +176 -0
  132. package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +5 -9
  133. package/templates/root-consumer/guides/dndev/essences_reference.css.example +174 -0
  134. package/templates/root-consumer/guides/wai-way/agents/builder.md.example +10 -0
  135. package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +25 -5
  136. package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +13 -2
  137. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +2 -2
  138. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +47 -11
  139. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +15 -4
  140. package/templates/root-consumer/guides/wai-way/spec_template.md.example +7 -6
  141. package/templates/app-payload/.env.example +0 -28
  142. package/templates/app-payload/README.md.example +0 -233
  143. package/templates/app-payload/collections/Company.ts.example +0 -125
  144. package/templates/app-payload/collections/Hero.ts.example +0 -62
  145. package/templates/app-payload/collections/Media.ts.example +0 -41
  146. package/templates/app-payload/collections/Products.ts.example +0 -115
  147. package/templates/app-payload/collections/Services.ts.example +0 -104
  148. package/templates/app-payload/collections/Testimonials.ts.example +0 -92
  149. package/templates/app-payload/collections/Users.ts.example +0 -35
  150. package/templates/app-payload/src/server.ts.example +0 -79
  151. package/templates/app-payload/tsconfig.json.example +0 -24
@@ -0,0 +1,234 @@
1
+ # Setup: SOC2 Readiness
2
+
3
+ **DoNotDev auto-enforces SOC2 baseline controls.** Zero config needed for the MVP. This guide covers advanced controls and audit verification.
4
+
5
+ ---
6
+
7
+ ## Zero-Config Baseline (Always On)
8
+
9
+ When you wire `DndevSecurity` into your app, these controls activate automatically:
10
+
11
+ | Control | Default | SOC2 Criteria |
12
+ |---------|---------|---------------|
13
+ | Structured audit logging (all CRUD + auth) | ✅ Automatic | CC7.1 |
14
+ | Rate limiting (100 writes/min, 500 reads/min) | ✅ Automatic | CC6.6 |
15
+ | Brute-force lockout (5 attempts → 15 min) | ✅ Automatic | CC6.1 |
16
+ | Session timeout (8h idle) | ✅ Automatic | CC6.1 |
17
+ | Field-level RBAC + visibility | ✅ Always on (entity config) | CC6.3 |
18
+ | Supabase RLS / Firestore default-deny | ✅ By convention | CC6.3 |
19
+
20
+ ---
21
+
22
+ ## 1. Wire `DndevSecurity` (Required Once)
23
+
24
+ ```typescript
25
+ // src/security.ts
26
+ import { DndevSecurity } from '@donotdev/security/server';
27
+
28
+ export const security = new DndevSecurity({
29
+ // Optional: override defaults
30
+ // rateLimit: { writesPerMin: 50, readsPerMin: 200 },
31
+ // lockout: { maxAttempts: 3, lockoutMs: 30 * 60 * 1000 },
32
+ // sessionTimeoutMs: 4 * 60 * 60 * 1000,
33
+
34
+ // Required for PII encryption (see § PII Encryption below)
35
+ // piiSecret: process.env.PII_SECRET,
36
+
37
+ // Optional: send anomaly alerts to your SIEM/Slack
38
+ // anomalyHandler: async (alert) => { await fetch(process.env.SIEM_WEBHOOK!, { method: 'POST', body: JSON.stringify(alert) }); },
39
+ });
40
+ ```
41
+
42
+ ```typescript
43
+ // src/main.ts (or app entry point)
44
+ import { security } from './security';
45
+ import { crudService } from './crud'; // your CrudService instance
46
+ import { auth } from './auth'; // your SupabaseAuth or FirebaseSDK instance
47
+
48
+ crudService.setSecurity(security);
49
+ auth.setSecurity(security);
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 2. PII Encryption (Opt-In — C1 Confidentiality)
55
+
56
+ Required only for entities storing personal data (email, phone, SSN, health data, etc.).
57
+
58
+ ```typescript
59
+ // entities/user.ts
60
+ export const userEntity = defineEntity({
61
+ name: 'User',
62
+ collection: 'users',
63
+ security: {
64
+ piiFields: ['email', 'phone', 'dateOfBirth'], // Encrypted at rest with AES-256-GCM
65
+ },
66
+ fields: { /* ... */ },
67
+ });
68
+ ```
69
+
70
+ ```bash
71
+ # Add to .env (min 32 chars, generate with: openssl rand -hex 32)
72
+ PII_SECRET=your-secret-key-min-32-chars-here
73
+ ```
74
+
75
+ Pass to security:
76
+ ```typescript
77
+ export const security = new DndevSecurity({
78
+ piiSecret: process.env.PII_SECRET,
79
+ });
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 3. MFA Enforcement (Opt-In — CC6.1)
85
+
86
+ Enforce MFA for specific roles on sensitive entities:
87
+
88
+ ```typescript
89
+ export const paymentEntity = defineEntity({
90
+ name: 'Payment',
91
+ collection: 'payments',
92
+ security: {
93
+ requireMfa: 'admin', // 'admin' | 'manager' | 'user'
94
+ },
95
+ fields: { /* ... */ },
96
+ });
97
+ ```
98
+
99
+ Enable MFA in your provider:
100
+ - **Supabase:** Dashboard → Auth → Multi Factor Authentication → Enable TOTP
101
+ - **Firebase:** Firebase Console → Authentication → Multi-factor Auth → Enable
102
+
103
+ ---
104
+
105
+ ## 4. Data Retention (Opt-In — P6 Privacy)
106
+
107
+ ```typescript
108
+ export const auditLogEntity = defineEntity({
109
+ name: 'AuditLog',
110
+ collection: 'audit_logs',
111
+ security: {
112
+ retention: { days: 365 }, // Auto-flag records for purge after 1 year
113
+ },
114
+ fields: { /* ... */ },
115
+ });
116
+ ```
117
+
118
+ Run retention purge (e.g., in a scheduled Cloud Function):
119
+ ```typescript
120
+ import { privacyManager } from '@donotdev/security/server';
121
+ // privacyManager.shouldPurge(record.createdAt, retentionDays) → boolean
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 5. Right to Erasure (GDPR Art. 17 / P8)
127
+
128
+ ```typescript
129
+ import { DndevSecurity } from '@donotdev/security/server';
130
+
131
+ // In your account deletion handler:
132
+ await security.privacyManager.eraseUser(userId, async (uid) => {
133
+ // Your custom erasure logic per collection
134
+ await db.from('orders').update({ userId: null }).eq('userId', uid);
135
+ await db.from('profiles').delete().eq('id', uid);
136
+ });
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 6. Health Monitoring (Opt-In — A1 Availability)
142
+
143
+ ```typescript
144
+ // src/client/health.ts
145
+ import { HealthMonitor } from '@donotdev/security';
146
+
147
+ const dbMonitor = new HealthMonitor({ failureThreshold: 3, successThreshold: 2, timeoutMs: 5000 });
148
+
149
+ // Wrap all downstream DB calls through the circuit breaker
150
+ export const safeQuery = (fn: () => Promise<unknown>) => dbMonitor.protect(fn);
151
+
152
+ // Expose a health endpoint (Next.js example)
153
+ // app/api/health/route.ts
154
+ export async function GET() {
155
+ return Response.json({ status: dbMonitor.checkLiveness() });
156
+ }
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 7. Anomaly Detection (Opt-In — CC7.2)
162
+
163
+ Triggered automatically when brute-force or bulk-delete thresholds are exceeded. Wire a handler to send alerts:
164
+
165
+ ```typescript
166
+ export const security = new DndevSecurity({
167
+ anomalyHandler: async (alert) => {
168
+ // Send to Slack, PagerDuty, Datadog, etc.
169
+ await fetch(process.env.SLACK_WEBHOOK!, {
170
+ method: 'POST',
171
+ body: JSON.stringify({ text: `🚨 SOC2 Anomaly: ${alert.type} — ${alert.details}` }),
172
+ });
173
+ },
174
+ });
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 8. Audit Log Output
180
+
181
+ Audit events write to `process.stdout` as NDJSON — pipe to your SIEM:
182
+
183
+ ```json
184
+ {"timestamp":"2025-01-15T10:23:00.000Z","type":"crud.create","userId":"u_123","resource":"products","resourceId":"p_456","ip":"192.168.1.1"}
185
+ {"timestamp":"2025-01-15T10:23:01.000Z","type":"auth.login.failure","userId":"unknown","resource":"auth","ip":"10.0.0.5","details":"brute_force_lockout"}
186
+ ```
187
+
188
+ **Cloud Logging (Firebase / GCP):** Stdout is captured automatically.
189
+ **Datadog:** Set `DD_LOGS_INJECTION=true` and pipe stdout to the Datadog agent.
190
+ **Supabase:** Export `process.stderr` anomaly alerts to Logflare.
191
+
192
+ ---
193
+
194
+ ## SOC2 Readiness Check
195
+
196
+ Run before any audit:
197
+
198
+ ```bash
199
+ dn soc2 # Scan current app
200
+ dn soc2 --app my-app # Scan specific app
201
+ dn soc2 --json # Machine-readable JSON output (CI/CD)
202
+ dn soc2 --verbose # Show details for passing checks too
203
+ ```
204
+
205
+ **Exit codes:** `0` = all checks pass, `1` = one or more checks failed (CI-friendly).
206
+
207
+ **Checks run:**
208
+
209
+ | Check | Criteria | What It Looks For |
210
+ |-------|----------|--------------------|
211
+ | SecurityContext wired | CC6.1 | `DndevSecurity` in source |
212
+ | PII encryption secret | C1 | `PII_SECRET` in env when `piiFields` defined |
213
+ | Firestore default-deny | CC6.3 | `allow read, write: if false` in `firestore.rules` |
214
+ | Supabase RLS | CC6.3 | `ENABLE ROW LEVEL SECURITY` in SQL migrations |
215
+ | Audit logging | CC7.1 | `.audit()` / `AuditLogger` usage |
216
+ | Rate limiting | CC6.6 | `rateLimit` / `DndevRateLimiter` usage |
217
+ | Retention policies | P6 | `retention.days` in entity config |
218
+ | Health monitoring | A1 | `HealthMonitor` or `/api/health` endpoint |
219
+ | MFA enforcement | CC6.1 | `requireMfa` in sensitive entities |
220
+
221
+ ---
222
+
223
+ ## Coverage After Full Setup
224
+
225
+ | SOC2 Criteria | Control | Status |
226
+ |---------------|---------|--------|
227
+ | CC6.1 Auth hardening | Lockout + session timeout + MFA | Framework-enforced |
228
+ | CC6.3 Authorization | RBAC + field visibility + RLS/Firestore rules | Framework-enforced |
229
+ | CC6.6 Rate limiting | Per-user sliding window | Framework-enforced |
230
+ | CC7.1 Audit logging | All CRUD + auth events as NDJSON | Framework-enforced |
231
+ | CC7.2 Anomaly detection | Auth failures + bulk ops threshold alerts | Opt-in handler |
232
+ | A1 Availability | Circuit breaker + liveness probes | Opt-in `HealthMonitor` |
233
+ | C1 Confidentiality | AES-256-GCM field encryption | Opt-in `piiFields` |
234
+ | P1–P8 Privacy | Retention policies + right-to-erasure | Opt-in `retention` |
@@ -0,0 +1,124 @@
1
+ # Setup: Supabase
2
+
3
+ **From zero to a working Supabase backend: env, tables, RLS, and adapter behavior.**
4
+
5
+ ---
6
+
7
+ ## Step 1: Run Supabase Setup
8
+
9
+ ```bash
10
+ dndev supabase:setup
11
+ ```
12
+
13
+ This command:
14
+ - Lets you choose the target app (if you have an `apps/` directory)
15
+ - Asks for your **public** Supabase project URL and anon key
16
+ - Writes `VITE_SUPABASE_URL` and `VITE_SUPABASE_ANON_KEY` to your app's `.env`
17
+
18
+ **We only ask for public credentials** (safe to ship in your client bundle). We never ask for the service_role key.
19
+
20
+ Get URL and anon key from: [Supabase Dashboard](https://supabase.com/dashboard) → your project → **Settings → API**.
21
+
22
+ ---
23
+
24
+ ## Step 2: Generate Tables from Entities
25
+
26
+ The framework can generate PostgreSQL migrations from your entity definitions (same source as the schema used by the app).
27
+
28
+ ```bash
29
+ dn generate sql
30
+ ```
31
+
32
+ **What it does:**
33
+ - Discovers entities (e.g. in `entities/` or your configured `--entity-dir`)
34
+ - For each entity: `CREATE TABLE` with columns mapped from field types (text, number, boolean, timestamptz, uuid, jsonb, etc.)
35
+ - Adds technical columns: `id` (uuid, default `gen_random_uuid()`), `user_id`, `created_at`, `updated_at` (with `DEFAULT now()`), `created_by_id`, `updated_by_id`, `status`
36
+ - Enables **Row Level Security (RLS)** and creates policies so rows are scoped by `auth.uid() = user_id`
37
+ - Adds a trigger so `updated_at` is set automatically on every `UPDATE`
38
+
39
+ **Options (optional):**
40
+ - `--entity-dir <path>` — where to find entity files (default: from app root)
41
+ - `--output-dir <path>` — where to write migrations (default: `supabase/migrations`)
42
+ - `--no-single-file` — one migration file per entity instead of one combined file
43
+
44
+ Output is written to `supabase/migrations/` (or your `--output-dir`) as a timestamped `.sql` file. Apply it with the Supabase CLI or Dashboard SQL editor.
45
+
46
+ ---
47
+
48
+ ## Step 3: Apply Migrations
49
+
50
+ After generating SQL:
51
+
52
+ **Option A — Supabase CLI (recommended)**
53
+
54
+ ```bash
55
+ supabase db push
56
+ ```
57
+
58
+ (or `supabase migration up` if you manage migrations locally)
59
+
60
+ **Option B — Dashboard**
61
+
62
+ Copy the contents of the generated migration file into the SQL Editor in the Supabase Dashboard and run it.
63
+
64
+ ---
65
+
66
+ ## Step 4: Adapter Behavior (DB-Managed Timestamps)
67
+
68
+ Tables use **snake_case** column names and **timestamptz** for `created_at` / `updated_at`. The framework expects **camelCase** and **ISO date strings** in the app.
69
+
70
+ The **Supabase CRUD adapter** handles this automatically:
71
+
72
+ | Direction | Behavior |
73
+ |-----------|----------|
74
+ | **Read** (get, query, subscribe) | Rows from Supabase are normalized: snake_case → camelCase (e.g. `created_at` → `createdAt`), and timestamp columns are converted to ISO strings. Your UI receives the same shape as with Firebase. |
75
+ | **Write** (add, set, update) | The adapter does **not** send `createdAt` / `updatedAt` (or snake equivalents). The database sets them via `DEFAULT now()` and the `updated_at` trigger. |
76
+
77
+ So you never set timestamps in app code when using Supabase — the DB owns them.
78
+
79
+ ---
80
+
81
+ ## Environment Variables
82
+
83
+ **Client (Vite):** in `apps/<app>/.env`
84
+
85
+ | Variable | Purpose |
86
+ |----------|---------|
87
+ | `VITE_SUPABASE_URL` | Project URL (public) |
88
+ | `VITE_SUPABASE_ANON_KEY` | Anon key (public, safe in bundle) |
89
+
90
+ **Server (e.g. API routes, Edge Functions):** use the same URL and `SUPABASE_SERVICE_ROLE_KEY` for admin operations. Never expose the service_role key to the client. Put it in `functions/.env` or your host’s env (Vercel, etc.).
91
+
92
+ See [ENV_SETUP.md](./ENV_SETUP.md) for where to put secrets.
93
+
94
+ ---
95
+
96
+ ## Storage (Optional)
97
+
98
+ If your app uploads files, create a storage bucket in the Supabase Dashboard (e.g. `uploads`). The default bucket name used by the framework is `uploads`. Configure public or RLS policies in the Dashboard as needed.
99
+
100
+ ---
101
+
102
+ ## Hosting the frontend
103
+
104
+ Supabase gives you **Auth, Postgres, Storage, and Edge Functions**. It does **not** host your built frontend (Vite/Next SPA). You need a separate host for the `dist/` output.
105
+
106
+ **We recommend:**
107
+
108
+ - **Vercel** — Connect your repo, set `VITE_SUPABASE_URL` and `VITE_SUPABASE_ANON_KEY` in the project env, then deploy. Good fit for Next.js or Vite.
109
+ - We scaffold **vercel.json**; run `dndev deploy` and choose Frontend (Vercel) or Frontend + Edge Functions. Set `VITE_SUPABASE_*` in Vercel project env.
110
+
111
+ **Deploy:** Frontend goes to Vercel (scaffolded vercel.json); Edge Functions to Supabase. It does **not** deploy your Supabase app’s frontend to Vercel/Netlify. For that, use the host’s dashboard or CLI (e.g. `vercel`, `netlify deploy`) after building. See [ENV_SETUP.md](./ENV_SETUP.md) for production env vars on Vercel.
112
+
113
+ ---
114
+
115
+ ## Local Development
116
+
117
+ - **Against hosted Supabase:** After `supabase:setup`, run `bun dev` — the app talks to your Supabase project.
118
+ - **Local Supabase:** Install the [Supabase CLI](https://supabase.com/docs/guides/cli) and run `supabase start` for a local Postgres + Auth + Storage stack. Point `VITE_SUPABASE_URL` and keys to the local instance.
119
+
120
+ ---
121
+
122
+ ## Summary
123
+
124
+ **`dndev supabase:setup`** → paste URL + anon key → **`dn generate sql`** → apply migrations → **`bun dev`**. The adapter normalizes read (snake→camel, ISO) and leaves timestamps to the DB on write.
@@ -1,12 +1,16 @@
1
1
  # Setup: Themes
2
2
 
3
- **Most is pre-configured.** Override CSS variables in `src/themes.css`. Framework handles theme switching, auto-computed colors.
3
+ **Single source of truth:** `src/themes.css`. Import it from `globals.css`; do not set font/color overrides in globals. Framework handles theme switching.
4
+
5
+ **Default essence = SaaS** (Inter, neutral). Optional essences (Brutalist, Luxury) are in the scaffold; they do **not** apply until you set the class on `<html>` (e.g. `class="brutalist"`) or use the theme switcher.
6
+
7
+ **Reference:** Copy Brutalist/Luxury blocks from `guides/dndev/essences_reference.css` into your `src/themes.css` if you need them. Default-essence fonts (Inter, Space Grotesk, Playfair, Roboto) are bundled via `@donotdev/ui`; no Google Fonts, no `public/fonts/` required.
4
8
 
5
9
  ---
6
10
 
7
11
  ## Standard Use
8
12
 
9
- **File:** `src/themes.css` (scaffolded with all variables)
13
+ **File:** `src/themes.css` (scaffolded with light, dark, and optional Brutalist/Luxury)
10
14
 
11
15
  **Override colors:**
12
16
  ```css
@@ -0,0 +1,176 @@
1
+ # Setup: Vercel
2
+
3
+ **From zero to deployed: Vercel hosting + API routes with Firebase data layer.**
4
+
5
+ ---
6
+
7
+ ## Architecture
8
+
9
+ Vercel is your **hosting and API platform** — it serves your frontend and runs serverless API routes.
10
+ Firebase is your **data layer** — Firestore (CRUD), Firebase Auth (users), Firebase Storage (files).
11
+
12
+ The framework generates API routes as Vercel Serverless Functions that talk to Firebase on the backend.
13
+
14
+ ---
15
+
16
+ ## Step 1: Create Firebase Project (Data Layer)
17
+
18
+ Even though you deploy to Vercel, you still need Firebase for data.
19
+
20
+ 1. Go to [Firebase Console](https://console.firebase.google.com) → Create a project
21
+ 2. Enable **Authentication** → Email/Password (+ OAuth providers if needed)
22
+ 3. Enable **Cloud Firestore** → Create Database → select region
23
+ 4. Enable **Storage** if your app uploads files
24
+
25
+ Get the Firebase web config from: **Project Settings → General → Your apps → Web app → SDK config**.
26
+
27
+ ---
28
+
29
+ ## Step 2: Run Setup
30
+
31
+ ```bash
32
+ dndev firebase:setup
33
+ ```
34
+
35
+ This writes Firebase SDK config to your app's `.env`. The `overlay-vercel` providers.ts initializes the Firebase client SDK.
36
+
37
+ ---
38
+
39
+ ## Step 3: Configure Vercel
40
+
41
+ 1. Create a [Vercel](https://vercel.com) account and link your Git repo
42
+ 2. Import the project in Vercel Dashboard
43
+ 3. Set the **Root Directory** to `apps/<your-app>` (or leave blank if monorepo auto-detected)
44
+ 4. Set **Framework Preset** to Vite (or Next.js if using Next)
45
+
46
+ **Environment Variables** (in Vercel Dashboard → Settings → Environment Variables):
47
+
48
+ Copy your Firebase vars from `.env`:
49
+ - `VITE_FIREBASE_API_KEY`
50
+ - `VITE_FIREBASE_PROJECT_ID`
51
+ - `VITE_FIREBASE_AUTH_DOMAIN`
52
+ - `VITE_FIREBASE_STORAGE_BUCKET`
53
+ - `VITE_FIREBASE_MESSAGING_SENDER_ID`
54
+ - `VITE_FIREBASE_APP_ID`
55
+
56
+ For Next.js apps, use `NEXT_PUBLIC_` prefix instead of `VITE_`.
57
+
58
+ **Server secrets** (for API routes):
59
+ - `STRIPE_SECRET_KEY`
60
+ - `STRIPE_WEBHOOK_SECRET`
61
+ - Any OAuth client secrets
62
+
63
+ ---
64
+
65
+ ## Step 4: API Routes (Functions)
66
+
67
+ Your backend functions are in `functions/` and deploy as Vercel Serverless Functions.
68
+
69
+ ```
70
+ functions/
71
+ ├── src/
72
+ │ ├── auth/ # Auth endpoints (signup, login, etc.)
73
+ │ ├── billing/ # Stripe endpoints (checkout, webhook, etc.)
74
+ │ ├── crud/ # CRUD endpoints (create, read, update, delete)
75
+ │ └── oauth/ # OAuth callback handlers
76
+ ├── vercel.json # Route configuration
77
+ ├── tsconfig.json
78
+ └── package.json
79
+ ```
80
+
81
+ Functions use the Firebase Admin SDK on the server side to access Firestore, verify auth tokens, etc.
82
+
83
+ ---
84
+
85
+ ## Step 5: Deploy
86
+
87
+ **Option A — Git push (recommended)**
88
+
89
+ Push to your connected branch. Vercel auto-deploys.
90
+
91
+ ```bash
92
+ git push origin main
93
+ ```
94
+
95
+ **Option B — Vercel CLI**
96
+
97
+ ```bash
98
+ npx vercel --prod
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Environment Variables
104
+
105
+ | File | What Goes Here | Loaded By |
106
+ |------|---------------|-----------|
107
+ | `apps/<app>/.env` | Firebase config, license key, Stripe publishable key | Vite/Next.js (dev + build) |
108
+ | `apps/<app>/.env.local` | Local overrides (gitignored) | Vite/Next.js (overrides .env) |
109
+ | `apps/<app>/.env.production` | Production overrides | Vite/Next.js (build --mode production) |
110
+ | `functions/.env` | Server secrets: STRIPE_SECRET_KEY, OAuth secrets | API routes runtime |
111
+ | Vercel Dashboard | All production env vars (client + server) | Vercel runtime |
112
+
113
+ **Client vars** (browser-safe): `VITE_*` or `NEXT_PUBLIC_*` prefix.
114
+ **Server vars** (secrets): No prefix needed in Vercel Dashboard — only accessible in API routes.
115
+
116
+ ---
117
+
118
+ ## Local Development
119
+
120
+ ```bash
121
+ bun dev
122
+ ```
123
+
124
+ The app runs locally, talking to your Firebase project. API routes can be tested with:
125
+
126
+ ```bash
127
+ vercel dev
128
+ ```
129
+
130
+ Or use Firebase emulators for fully local development:
131
+
132
+ ```bash
133
+ dndev emu start
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Secrets
139
+
140
+ Server-side secrets go in `functions/.env` locally and in Vercel Dashboard for production.
141
+
142
+ **We NEVER ask for secret keys.** You place them yourself:
143
+
144
+ ```bash
145
+ # functions/.env
146
+ STRIPE_SECRET_KEY=sk_live_...
147
+ STRIPE_WEBHOOK_SECRET=whsec_...
148
+ ```
149
+
150
+ Then add the same values in Vercel Dashboard → Settings → Environment Variables.
151
+
152
+ See [ENV_SETUP.md → Secrets Philosophy](./ENV_SETUP.md#secrets-philosophy) for the full policy.
153
+
154
+ ---
155
+
156
+ ## Troubleshooting
157
+
158
+ **"Firebase config not loading"**
159
+ → Check `.env` is in your **app directory** (`apps/<app>/.env`), not repo root
160
+ → Vite vars must start with `VITE_`, Next.js vars with `NEXT_PUBLIC_`
161
+
162
+ **"401 / Permission denied on API routes"**
163
+ → Check Firebase service account key is configured in Vercel env vars
164
+ → Verify `GOOGLE_APPLICATION_CREDENTIALS` or inline credentials in API routes
165
+
166
+ **"CORS error"**
167
+ → Vercel handles CORS for same-origin requests automatically
168
+ → For cross-origin: add CORS headers in your API route handler
169
+
170
+ **"Build fails on Vercel"**
171
+ → Check Root Directory is set to your app directory
172
+ → Ensure `package.json` has correct `build` script
173
+
174
+ ---
175
+
176
+ **`dndev firebase:setup` → configure Vercel project → set env vars → `git push`. Vercel deploys automatically.**
@@ -226,18 +226,14 @@ navigate('/products', {
226
226
 
227
227
  ### 5. Query Parameters
228
228
 
229
- **✅ Use framework useSearchParams:**
229
+ **✅ Use framework useSearchParams (read-only, returns URLSearchParams directly):**
230
230
  ```tsx
231
231
  import { useSearchParams } from '@donotdev/ui/routing';
232
232
 
233
233
  function ProductList() {
234
- const [searchParams, setSearchParams] = useSearchParams();
234
+ const searchParams = useSearchParams();
235
235
  const page = searchParams.get('page') || '1';
236
-
237
- const handlePageChange = (newPage: string) => {
238
- setSearchParams({ page: newPage });
239
- };
240
-
236
+
241
237
  return <div>Page: {page}</div>;
242
238
  }
243
239
  ```
@@ -478,8 +474,8 @@ navigate('/products', { replace: true });
478
474
  // Route params
479
475
  const id = useRouteParam('id');
480
476
 
481
- // Query params
482
- const [searchParams, setSearchParams] = useSearchParams();
477
+ // Query params (read-only, returns URLSearchParams directly)
478
+ const searchParams = useSearchParams();
483
479
  const page = searchParams.get('page');
484
480
 
485
481
  // Navigation menu