@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.
- package/dependencies-matrix.json +356 -88
- package/dist/bin/commands/agent-setup.js +7 -1
- package/dist/bin/commands/build.js +118 -38
- package/dist/bin/commands/bump.js +74 -28
- package/dist/bin/commands/cacheout.js +37 -9
- package/dist/bin/commands/create-app.js +222 -115
- package/dist/bin/commands/create-project.js +455 -140
- package/dist/bin/commands/deploy.js +1736 -697
- package/dist/bin/commands/dev.js +138 -23
- package/dist/bin/commands/emu.js +215 -58
- package/dist/bin/commands/format.js +37 -9
- package/dist/bin/commands/lint.js +37 -9
- package/dist/bin/commands/preview.js +142 -23
- package/dist/bin/commands/supabase-setup.d.ts +6 -0
- package/dist/bin/commands/supabase-setup.d.ts.map +1 -0
- package/dist/bin/commands/supabase-setup.js +7 -0
- package/dist/bin/commands/supabase-setup.js.map +1 -0
- package/dist/bin/commands/sync-secrets.js +211 -34
- package/dist/bin/commands/type-check.d.ts +14 -0
- package/dist/bin/commands/type-check.d.ts.map +1 -0
- package/dist/bin/commands/type-check.js +314 -0
- package/dist/bin/commands/type-check.js.map +1 -0
- package/dist/bin/commands/wai.js +3 -1
- package/dist/bin/dndev.js +27 -2
- package/dist/bin/donotdev.js +27 -2
- package/dist/index.js +3960 -3015
- package/package.json +2 -2
- package/templates/app-demo/src/App.tsx.example +1 -0
- package/templates/app-demo/src/pages/FullPage.tsx.example +2 -2
- package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +2 -2
- package/templates/app-demo/src/themes.css.example +5 -12
- package/templates/app-expo/.env.example +64 -0
- package/templates/app-expo/.expo/README.md.example +5 -0
- package/templates/app-expo/.gitignore.example +36 -0
- package/templates/app-expo/README.md.example +58 -0
- package/templates/app-expo/app/.gitkeep +2 -0
- package/templates/app-expo/app/_layout.tsx.example +41 -0
- package/templates/app-expo/app/form.tsx.example +52 -0
- package/templates/app-expo/app/index.tsx.example +89 -0
- package/templates/app-expo/app/list.tsx.example +32 -0
- package/templates/app-expo/app/profile.tsx.example +76 -0
- package/templates/app-expo/app/signin.tsx.example +53 -0
- package/templates/app-expo/app.json.example +39 -0
- package/templates/app-expo/babel.config.js.example +10 -0
- package/templates/app-expo/eas.json.example +20 -0
- package/templates/app-expo/expo-env.d.ts.example +4 -0
- package/templates/app-expo/metro.config.js.example +20 -0
- package/templates/app-expo/service-account-key.json.example +12 -0
- package/templates/app-expo/tsconfig.json.example +19 -0
- package/templates/app-next/.env.example +4 -33
- package/templates/app-next/src/app/ClientLayout.tsx.example +2 -0
- package/templates/app-next/src/app/layout.tsx.example +7 -6
- package/templates/app-next/src/globals.css.example +2 -11
- package/templates/app-next/src/pages/HomePage.tsx.example +1 -1
- package/templates/app-next/src/themes.css.example +10 -13
- package/templates/app-vite/.env.example +3 -32
- package/templates/app-vite/index.html.example +2 -24
- package/templates/app-vite/src/App.tsx.example +2 -0
- package/templates/app-vite/src/globals.css.example +2 -12
- package/templates/app-vite/src/pages/FormPageExample.tsx.example +1 -2
- package/templates/app-vite/src/pages/HomePage.tsx.example +1 -1
- package/templates/app-vite/src/themes.css.example +109 -79
- package/templates/app-vite/vercel.json.example +11 -0
- package/templates/functions-firebase/build.mjs.example +2 -72
- package/templates/functions-firebase/functions-firebase/.env.example.example +23 -25
- package/templates/functions-firebase/functions-firebase/build.mjs.example +2 -72
- package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
- package/templates/functions-supabase/supabase/functions/cancel-subscription/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/change-plan/index.ts.example +11 -0
- package/templates/functions-supabase/supabase/functions/create-checkout-session/index.ts.example +11 -0
- package/templates/functions-supabase/supabase/functions/create-customer-portal/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/crud/index.ts.example +16 -0
- package/templates/functions-supabase/supabase/functions/delete-account/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/get-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/get-user-auth-status/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/refresh-subscription-status/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/remove-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/set-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/migrations/20250101000000_idempotency.sql +24 -0
- package/templates/functions-supabase/supabase/migrations/20250101000001_rate_limits.sql +22 -0
- package/templates/functions-supabase/supabase/migrations/20250101000002_cleanup_jobs.sql +28 -0
- package/templates/functions-supabase/supabase/migrations/20250101000003_operation_metrics.sql +28 -0
- package/templates/functions-vercel/functions-vercel/tsconfig.json.example +1 -1
- package/templates/functions-vercel/functions-vercel/vercel.json.example +1 -1
- package/templates/functions-vercel/vercel.json.example +1 -1
- package/templates/github/github/workflows/firebase-deploy.yml.example +1 -1
- package/templates/github/workflows/firebase-deploy.yml.example +1 -1
- package/templates/overlay-firebase/env.fragment.example +34 -0
- package/templates/overlay-firebase/env.fragment.expo.example +34 -0
- package/templates/overlay-firebase/env.fragment.nextjs.example +34 -0
- package/templates/overlay-firebase/src/config/providers.expo.ts.example +49 -0
- package/templates/overlay-firebase/src/config/providers.ts.example +23 -0
- package/templates/overlay-supabase/env.fragment.example +7 -0
- package/templates/overlay-supabase/env.fragment.expo.example +7 -0
- package/templates/overlay-supabase/env.fragment.nextjs.example +7 -0
- package/templates/overlay-supabase/src/config/providers.expo.ts.example +35 -0
- package/templates/overlay-supabase/src/config/providers.ts.example +33 -0
- package/templates/overlay-supabase/vercel.headers.example +23 -0
- package/templates/overlay-supabase/vercel.json.example +22 -0
- package/templates/overlay-vercel/env.fragment.example +34 -0
- package/templates/overlay-vercel/env.fragment.nextjs.example +34 -0
- package/templates/overlay-vercel/src/config/providers.ts.example +24 -0
- package/templates/root-consumer/.claude/agents/architect.md.example +2 -310
- package/templates/root-consumer/.claude/agents/builder.md.example +2 -326
- package/templates/root-consumer/.claude/agents/coder.md.example +2 -83
- package/templates/root-consumer/.claude/agents/extractor.md.example +2 -231
- package/templates/root-consumer/.claude/agents/polisher.md.example +2 -132
- package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +2 -81
- package/templates/root-consumer/.claude/commands/grill.md.example +30 -0
- package/templates/root-consumer/.claude/commands/techdebt.md.example +28 -0
- package/templates/root-consumer/.clinerules.example +1 -0
- package/templates/root-consumer/.cursor/rules/no-docs.mdc.example +15 -0
- package/templates/root-consumer/.cursorrules.example +1 -0
- package/templates/root-consumer/.github/copilot-instructions.md.example +1 -0
- package/templates/root-consumer/.windsurfrules.example +1 -0
- package/templates/root-consumer/AI.md.example +29 -123
- package/templates/root-consumer/CLAUDE.md.example +1 -134
- package/templates/root-consumer/CONVENTIONS.md.example +1 -0
- package/templates/root-consumer/GEMINI.md.example +1 -0
- package/templates/root-consumer/firebase.json.example +1 -1
- package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +20 -0
- package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +0 -18
- package/templates/root-consumer/guides/dndev/COMPONENTS_UI.md.example +1 -1
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +99 -30
- package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -1
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +143 -12
- package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +9 -3
- package/templates/root-consumer/guides/dndev/SETUP_SOC2.md.example +234 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -0
- package/templates/root-consumer/guides/dndev/SETUP_THEMES.md.example +6 -2
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +176 -0
- package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +5 -9
- package/templates/root-consumer/guides/dndev/essences_reference.css.example +174 -0
- package/templates/root-consumer/guides/wai-way/agents/builder.md.example +10 -0
- package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +25 -5
- package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +13 -2
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +2 -2
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +47 -11
- package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +15 -4
- package/templates/root-consumer/guides/wai-way/spec_template.md.example +7 -6
- package/templates/app-payload/.env.example +0 -28
- package/templates/app-payload/README.md.example +0 -233
- package/templates/app-payload/collections/Company.ts.example +0 -125
- package/templates/app-payload/collections/Hero.ts.example +0 -62
- package/templates/app-payload/collections/Media.ts.example +0 -41
- package/templates/app-payload/collections/Products.ts.example +0 -115
- package/templates/app-payload/collections/Services.ts.example +0 -104
- package/templates/app-payload/collections/Testimonials.ts.example +0 -92
- package/templates/app-payload/collections/Users.ts.example +0 -35
- package/templates/app-payload/src/server.ts.example +0 -79
- package/templates/app-payload/tsconfig.json.example +0 -24
|
@@ -335,24 +335,6 @@ import { StartChallengeForm } from '@donotdev/adv-comps';
|
|
|
335
335
|
/>
|
|
336
336
|
```
|
|
337
337
|
|
|
338
|
-
### Waterfall
|
|
339
|
-
|
|
340
|
-
Waterfall component with lazy loading built-in. Features clean cascade layout with diagonal staircase effect, perfect for step-by-step processes or feature showcases.
|
|
341
|
-
|
|
342
|
-
```tsx
|
|
343
|
-
import { Waterfall } from '@donotdev/adv-comps';
|
|
344
|
-
import type { WaterfallProps } from '@donotdev/adv-comps';
|
|
345
|
-
|
|
346
|
-
<Waterfall
|
|
347
|
-
items: ComponentData[]
|
|
348
|
-
className?: string
|
|
349
|
-
connectorClassName?: string
|
|
350
|
-
density?: 'compact' | 'comfortable'
|
|
351
|
-
ariaLabel?: string
|
|
352
|
-
direction?: 'horizontal' | 'descending'
|
|
353
|
-
/>
|
|
354
|
-
```
|
|
355
|
-
|
|
356
338
|
## Notes
|
|
357
339
|
|
|
358
340
|
- All components are lazy-loaded by default for optimal performance
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
- **usePrefetch** - Prefetch route data.
|
|
84
84
|
- **useLocation** - Get current location.
|
|
85
85
|
- **useParams** - Get route parameters.
|
|
86
|
-
- **useSearchParams** - Get URL search parameters.
|
|
86
|
+
- **useSearchParams** - Get URL search parameters. Returns `URLSearchParams` directly (NOT a tuple). Use `.get('key')` to read values.
|
|
87
87
|
- **useMatch** - Match current route pattern.
|
|
88
88
|
- **useQueryParams** - Get and set query parameters.
|
|
89
89
|
- **useRedirectGuard** - Guard against redirects.
|
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
**The complete onboarding flow — from `dndev init` to deployed app.**
|
|
4
4
|
|
|
5
|
+
If you haven’t run `dndev init` yet, see [AGENT_START_HERE.md](./AGENT_START_HERE.md) § **Getting started (for humans)** (P0 → Install CLI → Run AI.md → Enjoy).
|
|
6
|
+
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
## The Flow
|
|
8
10
|
|
|
9
11
|
```
|
|
10
|
-
bun install
|
|
11
|
-
bun dev
|
|
12
|
-
dndev firebase:setup
|
|
13
|
-
|
|
14
|
-
dndev
|
|
12
|
+
bun install -> install dependencies
|
|
13
|
+
bun dev -> start app, read the homepage setup guide
|
|
14
|
+
dndev firebase:setup -> configure Firebase project + .env
|
|
15
|
+
-- or --
|
|
16
|
+
dndev supabase:setup -> configure Supabase project + .env
|
|
17
|
+
dndev emu start -> test locally with emulators (Firebase)
|
|
18
|
+
dndev deploy -> deploy to production
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
---
|
|
@@ -43,29 +47,45 @@ git push -u origin main
|
|
|
43
47
|
|
|
44
48
|
---
|
|
45
49
|
|
|
46
|
-
## Step 3:
|
|
50
|
+
## Step 3: Backend Setup
|
|
51
|
+
|
|
52
|
+
### Firebase
|
|
47
53
|
|
|
48
54
|
```bash
|
|
49
55
|
dndev firebase:setup
|
|
50
56
|
```
|
|
51
57
|
|
|
52
|
-
Automates: project selection/creation, web app, SDK config
|
|
58
|
+
Automates: project selection/creation, web app, SDK config -> `.env`, `.firebaserc`.
|
|
53
59
|
|
|
54
60
|
Then 2 manual steps (CLI gives you direct links):
|
|
55
|
-
1. Download service account key
|
|
61
|
+
1. Download service account key -> save as `service-account-key.json`
|
|
56
62
|
2. Enable Auth + Firestore in Firebase Console
|
|
57
63
|
|
|
58
64
|
See [SETUP_FIREBASE.md](./SETUP_FIREBASE.md) for full details.
|
|
59
65
|
|
|
66
|
+
### Supabase
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
dndev supabase:setup
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Asks for your **public** project URL and anon key (both safe to share -- shipped in client bundle).
|
|
73
|
+
|
|
74
|
+
Get them from: https://supabase.com/dashboard -> your project -> Settings -> API
|
|
75
|
+
|
|
76
|
+
See [SETUP_SUPABASE.md](./SETUP_SUPABASE.md) for full details (tables, RLS, `dn generate sql`). See [Secrets Philosophy](#secrets-philosophy) for how we handle secret keys.
|
|
77
|
+
|
|
60
78
|
---
|
|
61
79
|
|
|
62
80
|
## Step 4: Test Locally
|
|
63
81
|
|
|
64
82
|
```bash
|
|
65
|
-
dndev emu start
|
|
83
|
+
dndev emu start # Firebase emulators
|
|
66
84
|
```
|
|
67
85
|
|
|
68
|
-
Select Auth + Firestore + Functions.
|
|
86
|
+
Select services: Auth + Firestore + Functions. This starts Firebase emulators so you can develop without touching production.
|
|
87
|
+
|
|
88
|
+
For Supabase, development runs against your Supabase project directly (or use `supabase start` for local Supabase if installed).
|
|
69
89
|
|
|
70
90
|
---
|
|
71
91
|
|
|
@@ -74,11 +94,11 @@ Select Auth + Firestore + Functions. Develops against local emulators.
|
|
|
74
94
|
Open the project in **Cursor**, **Claude Code**, **Windsurf**, or **AntiGravity**.
|
|
75
95
|
|
|
76
96
|
The AI reads `AI.md` and follows the WAI-WAY protocol:
|
|
77
|
-
- Phase 0: Brainstorm
|
|
78
|
-
- Phase 1: Scaffold
|
|
79
|
-
- Phase 2: Entities
|
|
80
|
-
- Phase 3: Compose
|
|
81
|
-
- Phase 4: Configure
|
|
97
|
+
- Phase 0: Brainstorm -> produce a spec
|
|
98
|
+
- Phase 1: Scaffold -> create pages
|
|
99
|
+
- Phase 2: Entities -> define data models
|
|
100
|
+
- Phase 3: Compose -> build pages with components
|
|
101
|
+
- Phase 4: Configure -> generate tests, firestore rules, CI/CD
|
|
82
102
|
|
|
83
103
|
Read `guides/wai-way/WAI_WAY_CLI.md` for the full workflow.
|
|
84
104
|
|
|
@@ -90,7 +110,51 @@ Read `guides/wai-way/WAI_WAY_CLI.md` for the full workflow.
|
|
|
90
110
|
dndev deploy
|
|
91
111
|
```
|
|
92
112
|
|
|
93
|
-
Deploys hosting + functions + rules.
|
|
113
|
+
Deploys hosting + functions + rules. Syncs runtime secrets automatically before deploying functions.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Secrets Philosophy
|
|
118
|
+
|
|
119
|
+
**DoNotDev follows strict rules about credentials:**
|
|
120
|
+
|
|
121
|
+
### Tier 1: Public Keys -- We Ask Directly
|
|
122
|
+
|
|
123
|
+
These are safe to share, shipped in your client JS bundle. We ask for them during setup.
|
|
124
|
+
|
|
125
|
+
| Key | Where It Goes |
|
|
126
|
+
|-----|--------------|
|
|
127
|
+
| Firebase API key, project ID, auth domain, etc. | `apps/<app>/.env` as `VITE_FIREBASE_*` |
|
|
128
|
+
| Supabase project URL | `apps/<app>/.env` as `VITE_SUPABASE_URL` |
|
|
129
|
+
| Supabase anon key (public JWT) | `apps/<app>/.env` as `VITE_SUPABASE_ANON_KEY` |
|
|
130
|
+
| Stripe publishable key | `apps/<app>/.env` as `VITE_STRIPE_PUBLISHABLE_KEY` |
|
|
131
|
+
|
|
132
|
+
### Tier 2: Secret Keys -- We NEVER Ask, We Tell You Where To Put Them
|
|
133
|
+
|
|
134
|
+
These are server-side only. We never prompt for them, never store them in client code.
|
|
135
|
+
|
|
136
|
+
| Key | Where It Goes | How To Get It |
|
|
137
|
+
|-----|--------------|---------------|
|
|
138
|
+
| Stripe secret key | `functions/.env` as `STRIPE_SECRET_KEY` | https://dashboard.stripe.com/apikeys |
|
|
139
|
+
| Stripe webhook secret | `functions/.env` as `STRIPE_WEBHOOK_SECRET` | Stripe Dashboard -> Webhooks |
|
|
140
|
+
| Supabase service_role key | `functions/.env` as `SUPABASE_SERVICE_ROLE_KEY` | Supabase Dashboard -> Settings -> API |
|
|
141
|
+
| OAuth client secrets | `functions/.env` as `*_CLIENT_SECRET` | Provider dashboard |
|
|
142
|
+
|
|
143
|
+
Then sync to your runtime:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
dndev sync-secrets # -> Firebase Secret Manager or Vercel env
|
|
147
|
+
dndev sync-secrets --target github # -> GitHub Secrets (for CI/CD)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Tier 3: Service Account Files -- Download, Place, Gitignore
|
|
151
|
+
|
|
152
|
+
| File | Where It Goes | How To Get It |
|
|
153
|
+
|------|--------------|---------------|
|
|
154
|
+
| `service-account-key.json` | App root (next to firebase.json) | Firebase Console -> Settings -> Service Accounts |
|
|
155
|
+
| `service-account-key.staging.json` | Same location, for staging | Same, from staging project |
|
|
156
|
+
|
|
157
|
+
These files are `.gitignored`. Never commit them. For CI/CD, upload the file content as a GitHub Secret and decode it in your workflow.
|
|
94
158
|
|
|
95
159
|
---
|
|
96
160
|
|
|
@@ -100,18 +164,21 @@ Deploys hosting + functions + rules. Cloud Run IAM handled automatically.
|
|
|
100
164
|
|
|
101
165
|
```
|
|
102
166
|
my-project/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
167
|
+
+-- .env.example <-- NOT loaded by Vite (reference only)
|
|
168
|
+
+-- apps/
|
|
169
|
+
| +-- my-app/
|
|
170
|
+
| +-- .env <-- Vite reads THIS (public keys: VITE_*)
|
|
171
|
+
| +-- .env.local <-- Overrides .env (gitignored)
|
|
172
|
+
| +-- .env.staging <-- Used by dndev staging
|
|
173
|
+
| +-- .env.production <-- Production overrides
|
|
174
|
+
+-- functions/
|
|
175
|
+
+-- .env <-- Server secrets (Stripe, OAuth, service_role)
|
|
112
176
|
```
|
|
113
177
|
|
|
114
|
-
**
|
|
178
|
+
**Rules:**
|
|
179
|
+
- `VITE_*` / `NEXT_PUBLIC_*` vars -> `apps/<app>/.env` (public, shipped to browser)
|
|
180
|
+
- Server secrets -> `functions/.env` (never exposed to client)
|
|
181
|
+
- Service account files -> app root, gitignored
|
|
115
182
|
|
|
116
183
|
---
|
|
117
184
|
|
|
@@ -122,9 +189,11 @@ my-project/
|
|
|
122
189
|
| `bun dev` | Start dev server |
|
|
123
190
|
| `dndev emu start` | Start Firebase emulators |
|
|
124
191
|
| `dndev firebase:setup` | Configure Firebase project + .env |
|
|
125
|
-
| `dndev
|
|
192
|
+
| `dndev supabase:setup` | Configure Supabase project + .env |
|
|
193
|
+
| `dndev deploy` | **Firebase:** hosting + functions + rules. **Supabase:** deploys frontend to [Vercel](https://vercel.com) (via scaffolded vercel.json) and Edge Functions to Supabase. Set `VITE_SUPABASE_*` in Vercel project env. |
|
|
126
194
|
| `dndev staging` | Deploy to staging environment |
|
|
127
|
-
| `dndev sync-secrets` | Push functions/.env to Firebase
|
|
195
|
+
| `dndev sync-secrets` | Push functions/.env to runtime (Firebase/Vercel) |
|
|
196
|
+
| `dndev sync-secrets --target github` | Push secrets to GitHub Secrets (CI/CD) |
|
|
128
197
|
| `bun test` | Run tests (after Phase 4) |
|
|
129
198
|
| `bun run type-check` | TypeScript validation |
|
|
130
199
|
|
|
@@ -135,8 +204,8 @@ my-project/
|
|
|
135
204
|
| Feature | Required? | When to Set Up |
|
|
136
205
|
|---------|-----------|---------------|
|
|
137
206
|
| Git + GitHub | Recommended | Before starting development |
|
|
138
|
-
| Firebase
|
|
139
|
-
| Cloud Functions | If using
|
|
207
|
+
| Firebase or Supabase | Yes (pick one) | Before building any features |
|
|
208
|
+
| Cloud Functions | If using server logic | Before deploying functions |
|
|
140
209
|
| Stripe | If using billing | When adding payment features |
|
|
141
210
|
| GitHub Actions CI/CD | Optional | Phase 4 generates the workflow |
|
|
142
211
|
| Staging environment | Optional | When you want a test environment |
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
## Getting Started
|
|
10
10
|
|
|
11
|
-
-
|
|
11
|
+
- **Human flow:** P0 (Bun/Node + AI IDE) → Install CLI & `dndev init` → Run **AI.md** → Enjoy. See [AGENT_START_HERE.md](./AGENT_START_HERE.md) § Getting started (for humans).
|
|
12
|
+
- [ENV_SETUP.md](./ENV_SETUP.md) — After `dndev init`: env, Firebase/Supabase, deploy
|
|
12
13
|
- [GOTCHAS.md](./GOTCHAS.md) - **Common mistakes & pitfalls** (phase-tagged, read before coding)
|
|
13
14
|
- [SETUP_FIREBASE.md](./SETUP_FIREBASE.md) - Firebase project setup (`dndev firebase:setup`)
|
|
15
|
+
- [SETUP_SUPABASE.md](./SETUP_SUPABASE.md) - Supabase project setup (`dndev supabase:setup`, `dn generate sql`)
|
|
14
16
|
- [SETUP_TESTING.md](./SETUP_TESTING.md) - Test generation (Phase 4)
|
|
15
17
|
|
|
16
18
|
---
|
|
@@ -20,6 +20,34 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## 0. Provider configuration (required)
|
|
24
|
+
|
|
25
|
+
CRUD operations use the **CRUD provider** registered at app startup. You must call `configureProviders()` before any component uses `useCrud`.
|
|
26
|
+
|
|
27
|
+
- **Where:** In a dedicated module, e.g. `src/config/providers.ts`.
|
|
28
|
+
- **When:** Import that module from your root component (e.g. `App.tsx`) so it runs before any CRUD usage.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// App.tsx
|
|
32
|
+
import './config/providers'; // Before any useCrud
|
|
33
|
+
// ...
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// config/providers.ts
|
|
38
|
+
import { configureProviders } from '@donotdev/core';
|
|
39
|
+
import { FirestoreAdapter } from '@donotdev/firebase'; // or SupabaseCrudAdapter from '@donotdev/supabase'
|
|
40
|
+
|
|
41
|
+
configureProviders({
|
|
42
|
+
crud: new FirestoreAdapter(),
|
|
43
|
+
// auth, storage optional
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If you skip this step, `useCrud` will throw at runtime: "Provider \"crud\" not available. Call configureProviders() at app startup."
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
23
51
|
## 1. Define Entity
|
|
24
52
|
|
|
25
53
|
```typescript
|
|
@@ -80,7 +108,7 @@ For multi-tenant apps where data belongs to a company/tenant/workspace:
|
|
|
80
108
|
### Step 1: Register Scope Provider (once at app startup)
|
|
81
109
|
|
|
82
110
|
```typescript
|
|
83
|
-
// src/
|
|
111
|
+
// src/App.tsx
|
|
84
112
|
import { registerScopeProvider } from '@donotdev/core';
|
|
85
113
|
import { useCurrentCompanyStore } from './stores/currentCompanyStore';
|
|
86
114
|
|
|
@@ -665,9 +693,15 @@ transmission: {
|
|
|
665
693
|
|
|
666
694
|
## 7. Custom Fields & Schemas
|
|
667
695
|
|
|
668
|
-
|
|
696
|
+
A custom field type (when the requirement is not a built-in type) needs up to four things: **(1)** entity field with your custom `type` and `validation.schema`, **(2)** form component(s) so the field is editable, **(3)** optional **display** formatter so list/card/detail views render the value correctly, **(4)** optional **filter** so the field appears in EntityFilters with the right filter UI. All four are wired through a single `registerFieldType({ ... })` call.
|
|
697
|
+
|
|
698
|
+
> **Custom type strings work out of the box.** Just use any string as the `type` — no declaration file or module augmentation needed. The framework accepts any string while still autocompleting built-in types.
|
|
699
|
+
>
|
|
700
|
+
> `CustomFieldOptionsMap` augmentation (see below) is only needed if you want type-checked `options.fieldSpecific` for your custom type.
|
|
669
701
|
|
|
670
|
-
|
|
702
|
+
### Form (required for editable fields)
|
|
703
|
+
|
|
704
|
+
You must register a **controlled component** so the field appears in forms. Optionally register an **uncontrolled component** (e.g. for submit/reset-style fields).
|
|
671
705
|
|
|
672
706
|
```typescript
|
|
673
707
|
import { registerFieldType, useController } from '@donotdev/crud';
|
|
@@ -687,7 +721,6 @@ function RepairOperationsField({
|
|
|
687
721
|
control: control,
|
|
688
722
|
});
|
|
689
723
|
|
|
690
|
-
// Use field.value and field.onChange for form state
|
|
691
724
|
const value = (field.value as any) || [];
|
|
692
725
|
|
|
693
726
|
return (
|
|
@@ -701,13 +734,13 @@ function RepairOperationsField({
|
|
|
701
734
|
);
|
|
702
735
|
}
|
|
703
736
|
|
|
704
|
-
//
|
|
737
|
+
// Minimal registration (form only)
|
|
705
738
|
registerFieldType({
|
|
706
739
|
type: 'repairOperations',
|
|
707
740
|
controlledComponent: RepairOperationsField,
|
|
708
741
|
});
|
|
709
742
|
|
|
710
|
-
//
|
|
743
|
+
// Entity with schema
|
|
711
744
|
export const carEntity = defineEntity({
|
|
712
745
|
name: 'Car',
|
|
713
746
|
collection: 'cars',
|
|
@@ -715,11 +748,10 @@ export const carEntity = defineEntity({
|
|
|
715
748
|
repairs: {
|
|
716
749
|
name: 'repairs',
|
|
717
750
|
label: 'repairs',
|
|
718
|
-
type: 'repairOperations'
|
|
751
|
+
type: 'repairOperations',
|
|
719
752
|
visibility: 'admin',
|
|
720
753
|
validation: {
|
|
721
754
|
required: false,
|
|
722
|
-
// Custom Valibot schema - single source of truth
|
|
723
755
|
schema: v.nullish(v.array(v.object({
|
|
724
756
|
operation: v.string(),
|
|
725
757
|
cost: v.number(),
|
|
@@ -730,10 +762,109 @@ export const carEntity = defineEntity({
|
|
|
730
762
|
});
|
|
731
763
|
```
|
|
732
764
|
|
|
733
|
-
**Important:**
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
765
|
+
**Important:** Custom controlled components receive `control` prop, NOT `field` prop. You MUST use `useController` to get `field` and `fieldState`. Define schema in `validation.schema` — it's the single source of truth.
|
|
766
|
+
|
|
767
|
+
### Display (list/card/detail)
|
|
768
|
+
|
|
769
|
+
Without a **displayFormatter**, list/card/detail views show the raw value (or a fallback). To control how the value is rendered in read-only views, pass `displayFormatter` in the same `registerFieldType` call. Signature: `(value, fieldConfig, t, options?) => string | ReactNode`. Use `options?.compact` for list/card vs detail.
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
// Example: format a custom object for display
|
|
773
|
+
displayFormatter: (value, fieldConfig, t, options) => {
|
|
774
|
+
if (value == null) return '';
|
|
775
|
+
const arr = Array.isArray(value) ? value : [];
|
|
776
|
+
if (options?.compact) return `${arr.length} item(s)`;
|
|
777
|
+
return arr.map((item: any) => `${item.operation}: ${item.cost}`).join(', ') || '—';
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Filter (EntityFilters)
|
|
782
|
+
|
|
783
|
+
For the field to appear in the list/card **filters** section (EntityFilters), set **filterable: true** and **filterType** in `registerFieldType`. The filter type determines the filter UI:
|
|
784
|
+
|
|
785
|
+
| filterType | Use for |
|
|
786
|
+
|----------------|--------|
|
|
787
|
+
| `'text'` | Free text search |
|
|
788
|
+
| `'range'` | Numbers, dates (min/max) |
|
|
789
|
+
| `'select'` | Single choice (e.g. enum) |
|
|
790
|
+
| `'multiselect'`| Multiple choices |
|
|
791
|
+
| `'address'` | Address-based filter |
|
|
792
|
+
| `'none'` | Not filterable (omit or set filterable: false) |
|
|
793
|
+
|
|
794
|
+
```typescript
|
|
795
|
+
registerFieldType({
|
|
796
|
+
type: 'repairOperations',
|
|
797
|
+
controlledComponent: RepairOperationsField,
|
|
798
|
+
displayFormatter: (value, fieldConfig, t, options) => { /* ... */ },
|
|
799
|
+
filterable: true,
|
|
800
|
+
filterType: 'text', // or 'range', 'select', 'multiselect', 'address', 'none'
|
|
801
|
+
});
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### Full example: form + display + filter
|
|
805
|
+
|
|
806
|
+
One registration with form, displayFormatter, and filter:
|
|
807
|
+
|
|
808
|
+
```typescript
|
|
809
|
+
import { registerFieldType, useController } from '@donotdev/crud';
|
|
810
|
+
import type { ControlledFieldProps } from '@donotdev/crud';
|
|
811
|
+
import { defineEntity } from '@donotdev/core';
|
|
812
|
+
import * as v from 'valibot';
|
|
813
|
+
|
|
814
|
+
function StatusTierField({ fieldConfig, control, errors, t }: ControlledFieldProps) {
|
|
815
|
+
const { field, fieldState } = useController({ name: fieldConfig.name, control });
|
|
816
|
+
return (
|
|
817
|
+
<div>
|
|
818
|
+
<label>{t(fieldConfig.label)}</label>
|
|
819
|
+
<select value={field.value ?? ''} onChange={(e) => field.onChange(e.target.value)}>
|
|
820
|
+
<option value="">—</option>
|
|
821
|
+
<option value="basic">Basic</option>
|
|
822
|
+
<option value="premium">Premium</option>
|
|
823
|
+
</select>
|
|
824
|
+
{fieldState?.error && <span className="error">{fieldState.error.message}</span>}
|
|
825
|
+
</div>
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
registerFieldType({
|
|
830
|
+
type: 'statusTier',
|
|
831
|
+
controlledComponent: StatusTierField,
|
|
832
|
+
displayFormatter: (value) => (value ? String(value) : '—'),
|
|
833
|
+
filterable: true,
|
|
834
|
+
filterType: 'select',
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
export const productEntity = defineEntity({
|
|
838
|
+
name: 'Product',
|
|
839
|
+
collection: 'products',
|
|
840
|
+
fields: {
|
|
841
|
+
statusTier: {
|
|
842
|
+
name: 'statusTier',
|
|
843
|
+
label: 'statusTier',
|
|
844
|
+
type: 'statusTier',
|
|
845
|
+
visibility: 'guest',
|
|
846
|
+
validation: { schema: v.optional(v.picklist(['basic', 'premium'])) },
|
|
847
|
+
},
|
|
848
|
+
},
|
|
849
|
+
});
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
### Typing custom field options
|
|
853
|
+
|
|
854
|
+
To get type-checked `options.fieldSpecific` for custom types (e.g. `extractDistrictCode: boolean`), augment the framework's `CustomFieldOptionsMap` in your app:
|
|
855
|
+
|
|
856
|
+
```typescript
|
|
857
|
+
// e.g. in src/types/crud.d.ts or next to your entity
|
|
858
|
+
import '@donotdev/core';
|
|
859
|
+
declare module '@donotdev/core' {
|
|
860
|
+
interface CustomFieldOptionsMap {
|
|
861
|
+
'repairOperations': { maxItems?: number };
|
|
862
|
+
'isousou-address': { extractDistrictCode?: boolean };
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
Then in entity fields you can use `type: 'repairOperations'` and `options: { fieldSpecific: { maxItems: 10 } }` without type errors or `as any`.
|
|
737
868
|
|
|
738
869
|
### Custom Schemas (No Custom UI)
|
|
739
870
|
|
|
@@ -112,23 +112,29 @@ This handles everything:
|
|
|
112
112
|
|
|
113
113
|
## Secrets (Stripe, OAuth, etc.)
|
|
114
114
|
|
|
115
|
-
Server-side secrets go in `functions/.env`, not the app `.env
|
|
115
|
+
Server-side secrets go in `functions/.env`, not the app `.env`.
|
|
116
|
+
|
|
117
|
+
**We NEVER ask for secret keys.** You place them yourself:
|
|
116
118
|
|
|
117
119
|
```bash
|
|
118
120
|
# functions/.env
|
|
119
121
|
STRIPE_SECRET_KEY=sk_live_...
|
|
120
122
|
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
123
|
+
SUPABASE_SERVICE_ROLE_KEY=eyJ... # if using Supabase
|
|
121
124
|
GITHUB_CLIENT_SECRET=...
|
|
122
125
|
```
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
Then sync to your runtime and CI/CD:
|
|
125
128
|
|
|
126
129
|
```bash
|
|
127
|
-
dndev sync-secrets
|
|
130
|
+
dndev sync-secrets # Firebase Secret Manager / Vercel env
|
|
131
|
+
dndev sync-secrets --target github # GitHub Secrets (for CI/CD workflows)
|
|
128
132
|
```
|
|
129
133
|
|
|
130
134
|
Secrets are auto-loaded by Cloud Functions at runtime. Never put server secrets in `VITE_*` variables — those are exposed to the browser.
|
|
131
135
|
|
|
136
|
+
See [ENV_SETUP.md → Secrets Philosophy](./ENV_SETUP.md#secrets-philosophy) for the full policy.
|
|
137
|
+
|
|
132
138
|
---
|
|
133
139
|
|
|
134
140
|
## Cloud Run IAM (Technical Detail)
|