@donotdev/cli 0.0.5 → 0.0.7

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 (102) hide show
  1. package/dependencies-matrix.json +76 -34
  2. package/dist/bin/commands/build.js +165 -161
  3. package/dist/bin/commands/bump.js +172 -160
  4. package/dist/bin/commands/cacheout.js +163 -157
  5. package/dist/bin/commands/create-app.js +205 -163
  6. package/dist/bin/commands/create-project.js +176 -161
  7. package/dist/bin/commands/deploy.js +480 -472
  8. package/dist/bin/commands/dev.js +164 -158
  9. package/dist/bin/commands/emu.js +164 -158
  10. package/dist/bin/commands/format.js +163 -157
  11. package/dist/bin/commands/lint.js +166 -157
  12. package/dist/bin/commands/make-admin.d.ts +11 -0
  13. package/dist/bin/commands/make-admin.d.ts.map +1 -0
  14. package/dist/bin/commands/make-admin.js +12 -0
  15. package/dist/bin/commands/make-admin.js.map +1 -0
  16. package/dist/bin/commands/preview.js +164 -158
  17. package/dist/bin/commands/sync-secrets.js +164 -158
  18. package/dist/bin/commands/wai.d.ts +11 -0
  19. package/dist/bin/commands/wai.d.ts.map +1 -0
  20. package/dist/bin/commands/wai.js +12 -0
  21. package/dist/bin/commands/wai.js.map +1 -0
  22. package/dist/bin/dndev.js +24 -8
  23. package/dist/bin/donotdev.js +24 -8
  24. package/dist/index.js +557 -514
  25. package/package.json +1 -1
  26. package/templates/app-demo/index.html.example +4 -0
  27. package/templates/app-demo/src/App.tsx.example +28 -10
  28. package/templates/app-demo/src/config/app.ts.example +68 -0
  29. package/templates/app-next/src/app/ClientLayout.tsx.example +4 -3
  30. package/templates/app-next/src/app/layout.tsx.example +17 -25
  31. package/templates/app-next/src/config/app.ts.example +75 -48
  32. package/templates/app-next/src/globals.css.example +10 -7
  33. package/templates/app-next/src/locales/dndev_en.json.example +68 -0
  34. package/templates/app-next/src/pages/locales/example_en.json.example +5 -0
  35. package/templates/app-vite/index.html.example +71 -34
  36. package/templates/app-vite/src/config/app.ts.example +75 -47
  37. package/templates/app-vite/src/globals.css.example +14 -6
  38. package/templates/app-vite/src/locales/dndev_en.json.example +68 -0
  39. package/templates/app-vite/src/pages/FormPageExample.tsx.example +152 -0
  40. package/templates/app-vite/src/pages/HomePage.tsx.example +81 -134
  41. package/templates/app-vite/src/pages/ListPageExample.tsx.example +88 -0
  42. package/templates/functions-firebase/README.md.example +25 -0
  43. package/templates/functions-firebase/build.mjs.example +8 -1
  44. package/templates/functions-firebase/functions-firebase/build.mjs.example +8 -1
  45. package/templates/functions-firebase/functions-firebase/src/index.ts.example +19 -25
  46. package/templates/functions-firebase/functions.config.js.example +35 -0
  47. package/templates/functions-firebase/tsconfig.json.example +3 -13
  48. package/templates/functions-vercel/tsconfig.json.example +1 -13
  49. package/templates/root-consumer/entities/ExampleEntity.ts.example +223 -0
  50. package/templates/root-consumer/entities/demo.ts.example +562 -0
  51. package/templates/root-consumer/entities/index.ts.example +15 -0
  52. package/templates/root-consumer/firebase.json.example +1 -1
  53. package/templates/root-consumer/guides/{AGENT_START_HERE.md.example → dndev/AGENT_START_HERE.md.example} +22 -0
  54. package/templates/root-consumer/guides/{COMPONENTS_ADV.md.example → dndev/COMPONENTS_ADV.md.example} +456 -360
  55. package/templates/root-consumer/guides/{COMPONENTS_ATOMIC.md.example → dndev/COMPONENTS_ATOMIC.md.example} +42 -0
  56. package/templates/root-consumer/guides/dndev/COMPONENTS_CRUD.md.example +231 -0
  57. package/templates/root-consumer/guides/{INDEX.md.example → dndev/INDEX.md.example} +3 -0
  58. package/templates/root-consumer/guides/{SETUP_APP_CONFIG.md.example → dndev/SETUP_APP_CONFIG.md.example} +5 -2
  59. package/templates/root-consumer/guides/{SETUP_AUTH.md.example → dndev/SETUP_AUTH.md.example} +30 -0
  60. package/templates/root-consumer/guides/{SETUP_BILLING.md.example → dndev/SETUP_BILLING.md.example} +44 -4
  61. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +473 -0
  62. package/templates/root-consumer/guides/dndev/SETUP_FUNCTIONS.md.example +116 -0
  63. package/templates/root-consumer/guides/{SETUP_PAGES.md.example → dndev/SETUP_PAGES.md.example} +17 -0
  64. package/templates/root-consumer/guides/dndev/SETUP_PWA.md.example +213 -0
  65. package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +503 -0
  66. package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +404 -0
  67. package/templates/root-consumer/guides/wai-way/agents/architect.md.example +78 -0
  68. package/templates/root-consumer/guides/wai-way/agents/builder.md.example +87 -0
  69. package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +325 -0
  70. package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +100 -0
  71. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +281 -0
  72. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +77 -0
  73. package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +104 -0
  74. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +124 -0
  75. package/templates/root-consumer/guides/wai-way/blueprints/4_configure.md.example +165 -0
  76. package/templates/root-consumer/guides/wai-way/context_map.json.example +95 -0
  77. package/templates/root-consumer/guides/wai-way/entity_patterns.md.example +840 -0
  78. package/templates/root-consumer/guides/wai-way/page_patterns.md.example +686 -0
  79. package/templates/root-consumer/guides/wai-way/presets_guide.md.example +217 -0
  80. package/templates/root-consumer/guides/wai-way/spec_template.md.example +312 -0
  81. package/templates/root-consumer/vercel.json.example +315 -20
  82. package/templates/app-demo/src/Routes.tsx.example +0 -20
  83. package/templates/app-vite/src/Routes.tsx.example +0 -16
  84. package/templates/app-vite/src/pages/locales/README.md.example +0 -1
  85. package/templates/functions-firebase/functions-firebase/src/crud/createEntity.ts.example +0 -19
  86. package/templates/functions-firebase/functions-firebase/src/crud/deleteEntity.ts.example +0 -14
  87. package/templates/functions-firebase/functions-firebase/src/crud/getEntity.ts.example +0 -14
  88. package/templates/functions-firebase/functions-firebase/src/crud/index.ts.example +0 -12
  89. package/templates/functions-firebase/functions-firebase/src/crud/listEntities.ts.example +0 -14
  90. package/templates/functions-firebase/functions-firebase/src/crud/updateEntity.ts.example +0 -14
  91. package/templates/root-consumer/guides/COMPONENTS_CRUD.md.example +0 -70
  92. package/templates/root-consumer/guides/SETUP_FUNCTIONS.md.example +0 -62
  93. /package/templates/root-consumer/guides/{COMPONENTS_UI.md.example → dndev/COMPONENTS_UI.md.example} +0 -0
  94. /package/templates/root-consumer/guides/{ENV_SETUP.md.example → dndev/ENV_SETUP.md.example} +0 -0
  95. /package/templates/root-consumer/guides/{SETUP_I18N.md.example → dndev/SETUP_I18N.md.example} +0 -0
  96. /package/templates/root-consumer/guides/{SETUP_LAYOUTS.md.example → dndev/SETUP_LAYOUTS.md.example} +0 -0
  97. /package/templates/root-consumer/guides/{SETUP_OAUTH.md.example → dndev/SETUP_OAUTH.md.example} +0 -0
  98. /package/templates/root-consumer/guides/{SETUP_THEMES.md.example → dndev/SETUP_THEMES.md.example} +0 -0
  99. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/APP_CHECK.md.example +0 -0
  100. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/COOKIE_REFERENCE.md.example +0 -0
  101. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/EMULATORS.md.example +0 -0
  102. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/VERSION_CONTROL.md.example +0 -0
@@ -22,6 +22,7 @@ These are the only ways one should handle layout to get to quick results functio
22
22
  ## Content Components
23
23
 
24
24
  - **Card** - Premium card component with title, content, footer, and elevation variants.
25
+ - **Navigation with middle-click support:** Wrap Card in Link component for navigation that supports middle-click (opens in new tab). See navigation section below.
25
26
  - **DualCard** - Side-by-side card layout for comparison or feature showcases.
26
27
  - **Text** - Typography component with semantic text variants.
27
28
  - **Blockquote** - Styled blockquote for citations and testimonials.
@@ -130,5 +131,46 @@ These are the only ways one should handle layout to get to quick results functio
130
131
 
131
132
  **See also:** [COMPONENTS_UI.md](./COMPONENTS_UI.md) for layout/composition components | [COMPONENTS_CRUD.md](./COMPONENTS_CRUD.md) for CRUD operations and entity forms
132
133
 
134
+ ---
135
+
136
+ ## Navigation with Card Component
137
+
138
+ **For clickable cards with navigation support (including middle-click):**
139
+
140
+ Wrap Card in Link component from `@donotdev/ui`:
141
+
142
+ ```tsx
143
+ import { Card } from '@donotdev/components';
144
+ import { Link } from '@donotdev/ui';
145
+
146
+ // Card with navigation (supports middle-click)
147
+ <Link
148
+ path="/about"
149
+ style={{ display: 'block', textDecoration: 'none', height: '100%' }}
150
+ aria-label="Learn more about About"
151
+ >
152
+ <Card
153
+ title="About"
154
+ subtitle="Learn more about us"
155
+ content="Click to navigate"
156
+ />
157
+ </Link>
158
+
159
+ // Card with onClick only (no middle-click support)
160
+ <Card
161
+ title="Action Card"
162
+ onClick={() => handleAction()}
163
+ />
164
+ ```
165
+
166
+ **Why wrap in Link?**
167
+ - Card component is standalone and not routing-aware
168
+ - Link wrapper provides framework navigation (SPA routing, keeps outlet context)
169
+ - Native browser middle-click support (opens in new tab)
170
+ - CSR/SSR safe
171
+
172
+ **Alternative:** Use FeatureCard from `@donotdev/ui` which handles this automatically when `href` prop is provided.
173
+
174
+ ---
133
175
 
134
176
  **All props documented in JSDoc** - Hover in IDE for complete reference.
@@ -0,0 +1,231 @@
1
+ # CRUD Package Reference
2
+
3
+ **Import:** `@donotdev/crud`
4
+
5
+ ---
6
+
7
+ ## Hooks
8
+
9
+ ### useCrud
10
+ Single document CRUD actions (add, update, delete, get, subscribe).
11
+
12
+ ```tsx
13
+ const { add, update, delete: remove, get, data, loading, error } = useCrud(carEntity);
14
+ await add({ name: 'Tesla', year: 2024 });
15
+ await remove('doc-id');
16
+ ```
17
+
18
+ ### useCrudList
19
+ Paginated list with automatic loading. For data tables.
20
+
21
+ ```tsx
22
+ const { items, loading, hasMore, loadMore } = useCrudList(productEntity);
23
+ ```
24
+
25
+ ### useCrudCardList
26
+ Card-based list with infinite scroll.
27
+
28
+ ```tsx
29
+ const { items, loading, hasMore, loadMore, refresh } = useCrudCardList(articleEntity);
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Form Components
35
+
36
+ ### EntityFormRenderer
37
+ Auto-render full entity form from definition.
38
+
39
+ ```tsx
40
+ <EntityFormRenderer
41
+ entity={productEntity}
42
+ operation="create"
43
+ onSubmit={handleSubmit}
44
+ />
45
+ ```
46
+
47
+ ### FormFieldRenderer
48
+ Render single field. Used inside custom forms.
49
+
50
+ ```tsx
51
+ <FormFieldRenderer field={field} control={form.control} />
52
+ ```
53
+
54
+ ### EntityList
55
+ Data table for entity collection.
56
+
57
+ ```tsx
58
+ <EntityList entity={userEntity} onRowClick={(user) => edit(user.id)} />
59
+ ```
60
+
61
+ ### EntityCardList
62
+ Card grid for entity collection.
63
+
64
+ ```tsx
65
+ <EntityCardList entity={productEntity} renderCard={(item) => <ProductCard {...item} />} />
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Form Building Blocks
71
+
72
+ For custom forms, use these low-level utilities:
73
+
74
+ ```tsx
75
+ import { useEntityForm, getFieldsForOperation, validateEntity } from '@donotdev/crud';
76
+
77
+ const { register, handleSubmit, fields, formState } = useEntityForm(productEntity, {
78
+ operation: 'create'
79
+ });
80
+ ```
81
+
82
+ | Export | Purpose |
83
+ |--------|---------|
84
+ | `useEntityForm` | React Hook Form wrapper for entities |
85
+ | `useEntityField` | Single field hook |
86
+ | `getFieldsForOperation` | Get editable fields for create/edit |
87
+ | `validateEntity` | Validate data against entity schema |
88
+ | `isFieldEditable` | Check if field is editable |
89
+
90
+ ---
91
+
92
+ ## Field Types (Built-in)
93
+
94
+ Field components are auto-rendered by `FormFieldRenderer`. You don't import them directly.
95
+
96
+ ### Text Inputs
97
+ - `text` - Single-line text input
98
+ - `email` - Email input with validation
99
+ - `tel` - Phone number input
100
+ - `url` - URL input
101
+ - `color` - Color picker
102
+ - `password` - Password input (masked)
103
+ - `textarea` - Multi-line text input
104
+ - `richtext` - Rich text editor
105
+
106
+ ### Numbers
107
+ - `number` - Numeric input
108
+ - `range` - Slider input
109
+
110
+ ### Boolean
111
+ - `checkbox` - Checkbox input
112
+ - `boolean` - Alias for checkbox
113
+ - `switch` - Toggle switch
114
+
115
+ ### Dates & Time
116
+ - `date` - Date picker
117
+ - `datetime-local` - Date and time picker
118
+ - `time` - Time picker
119
+ - `week` - Week picker
120
+ - `month` - Month picker
121
+ - `timestamp` - Timestamp (Firestore Timestamp)
122
+
123
+ ### Selection
124
+ - `select` - Dropdown select
125
+ - `combobox` - Searchable dropdown
126
+ - `multiselect` - Multiple selection dropdown
127
+ - `radio` - Radio button group
128
+
129
+ ### Files & Media
130
+ - `file` - Single file upload
131
+ - `files` - Multiple file uploads
132
+ - `document` - Document upload (PDF, etc.)
133
+ - `documents` - Multiple document uploads
134
+ - `image` - Single image upload
135
+ - `images` - Multiple image uploads
136
+
137
+ ### Complex Types
138
+ - `geopoint` - Geographic coordinates (lat/lng)
139
+ - `address` - Address input with autocomplete
140
+ - `map` - Map picker
141
+ - `array` - Array of text inputs
142
+
143
+ ### Special
144
+ - `avatar` - Avatar image upload
145
+ - `badge` - Badge display
146
+ - `hidden` - Hidden field (not displayed)
147
+ - `submit` - Submit button (uncontrolled)
148
+ - `reset` - Reset button (uncontrolled)
149
+
150
+ To add custom field types:
151
+ ```tsx
152
+ import { useController, registerFieldType } from '@donotdev/crud';
153
+ import type { ControlledFieldProps } from '@donotdev/crud';
154
+
155
+ // Custom controlled component MUST use framework's useController (not react-hook-form's)
156
+ function RatingField({
157
+ fieldConfig,
158
+ control,
159
+ errors,
160
+ t,
161
+ onChange
162
+ }: ControlledFieldProps) {
163
+ // REQUIRED: Use framework's useController - ensures type compatibility
164
+ const { field, fieldState } = useController({
165
+ name: fieldConfig.name,
166
+ control: control,
167
+ });
168
+
169
+ return (
170
+ <div>
171
+ <label>{t(fieldConfig.label)}</label>
172
+ {/* Use field.value and field.onChange */}
173
+ <input
174
+ type="number"
175
+ value={field.value ?? 0}
176
+ onChange={(e) => {
177
+ const value = Number(e.target.value);
178
+ field.onChange(value);
179
+ onChange?.(value);
180
+ }}
181
+ min={0}
182
+ max={5}
183
+ />
184
+ {fieldState?.error && (
185
+ <span className="error">{fieldState.error.message}</span>
186
+ )}
187
+ </div>
188
+ );
189
+ }
190
+
191
+ registerFieldType({
192
+ type: 'rating',
193
+ controlledComponent: RatingField,
194
+ });
195
+ ```
196
+
197
+ **Important:**
198
+ - Custom controlled components receive `control` prop, NOT `field` prop
199
+ - You must use **framework's `useController`** (from `@donotdev/crud`), NOT `react-hook-form`'s useController
200
+ - This ensures type compatibility - no type assertions needed
201
+
202
+ ---
203
+
204
+ ## Service & Store
205
+
206
+ Direct access (rarely needed):
207
+
208
+ ```tsx
209
+ import { getCrudService, useCrudStore } from '@donotdev/crud';
210
+
211
+ const service = getCrudService();
212
+ await service.query('products', { where: [['active', '==', true]] });
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Collection Utilities
218
+
219
+ ```tsx
220
+ import { loadDeterministicRange, upsertDeterministic, appendToCollection } from '@donotdev/crud';
221
+ ```
222
+
223
+ | Utility | Purpose |
224
+ |---------|---------|
225
+ | `loadDeterministicRange` | Paginated loading with deterministic IDs |
226
+ | `upsertDeterministic` | Insert or update with deterministic ID |
227
+ | `appendToCollection` | Add to collection end |
228
+
229
+ ---
230
+
231
+ **JSDoc in IDE** - Hover over any import for full props and examples.
@@ -7,6 +7,7 @@
7
7
  ## Core Setup
8
8
 
9
9
  - [SETUP_PAGES.md](./SETUP_PAGES.md) - Pages & routing (pre-configured)
10
+ - [USE_ROUTING.md](./USE_ROUTING.md) - **Routing components & hooks (CRITICAL)**
10
11
  - [SETUP_APP_CONFIG.md](./SETUP_APP_CONFIG.md) - App + Vite configuration
11
12
  - [SETUP_I18N.md](./SETUP_I18N.md) - Translations (pre-configured)
12
13
  - [SETUP_THEMES.md](./SETUP_THEMES.md) - Themes & CSS variables (pre-configured)
@@ -16,9 +17,11 @@
16
17
 
17
18
  ## Feature Setup
18
19
 
20
+ - [SETUP_CRUD.md](./SETUP_CRUD.md) - CRUD operations & data access (critical)
19
21
  - [SETUP_AUTH.md](./SETUP_AUTH.md) - Authentication (pre-configured)
20
22
  - [SETUP_OAUTH.md](./SETUP_OAUTH.md) - OAuth connections (pre-configured)
21
23
  - [SETUP_BILLING.md](./SETUP_BILLING.md) - Stripe subscriptions (pre-configured)
24
+ - [SETUP_PWA.md](./SETUP_PWA.md) - Progressive Web App setup
22
25
  - [SETUP_FUNCTIONS.md](./SETUP_FUNCTIONS.md) - Firebase Functions (pre-configured)
23
26
 
24
27
  ---
@@ -13,6 +13,8 @@ import type { AppConfig } from '@donotdev/core';
13
13
  export const appConfig: AppConfig = {
14
14
  app: {
15
15
  name: 'My App',
16
+ shortName: 'App', // Required for PWA
17
+ description: 'My app description', // Required for PWA
16
18
  url: 'https://myapp.com',
17
19
  },
18
20
 
@@ -93,9 +95,10 @@ export default defineViteConfig({
93
95
  generateSitemap: true,
94
96
  },
95
97
 
96
- // PWA
98
+ // PWA (see SETUP_PWA.md for full guide)
97
99
  pwa: {
98
- enabled: false,
100
+ enabled: false, // Enable PWA in production
101
+ devEnabled: false, // Enable PWA in development (optional)
99
102
  },
100
103
  });
101
104
  ```
@@ -74,4 +74,34 @@ const signOut = useAuth('signOut');
74
74
 
75
75
  ---
76
76
 
77
+ ## User Roles
78
+
79
+ **Role hierarchy:** `guest (0) < user (1) < admin (2) < super (3)`
80
+
81
+ **Default:** All authenticated users are `'user'` role.
82
+
83
+ **Setting admin/super roles:** Set Firebase Auth customClaims:
84
+
85
+ ```typescript
86
+ // Using Firebase Admin SDK
87
+ import { getFirebaseAdminAuth } from '@donotdev/firebase/server';
88
+
89
+ const auth = getFirebaseAdminAuth();
90
+ await auth.setCustomUserClaims(userId, {
91
+ role: 'admin', // or 'super'
92
+ isAdmin: true // legacy support
93
+ });
94
+ ```
95
+
96
+ **Using CLI:**
97
+ ```bash
98
+ dndev make-admin <userId>
99
+ ```
100
+
101
+ **Note:** User must sign out and sign in again for role changes to take effect (customClaims are in the ID token).
102
+
103
+ **Used by:** CRUD access control, protected routes, field visibility filtering.
104
+
105
+ ---
106
+
77
107
  **Add env vars, get auth. Framework handles the rest.**
@@ -1,6 +1,6 @@
1
1
  # Setup: Billing
2
2
 
3
- **Most is pre-configured.** Create Stripe config files. Framework handles checkout, webhooks, subscriptions.
3
+ **Most is pre-configured.** Create Stripe config files. Framework handles checkout, webhooks, subscriptions, and loading UX.
4
4
 
5
5
  ---
6
6
 
@@ -24,6 +24,36 @@ STRIPE_WEBHOOK_SECRET=whsec_xxx
24
24
 
25
25
  ---
26
26
 
27
+ ## Automatic UX (Zero Config)
28
+
29
+ **The hook handles loading overlays automatically for redirect operations:**
30
+
31
+ ```tsx
32
+ // Just call the method - framework handles the rest
33
+ const checkout = useStripeBilling('checkout', authState);
34
+ const openCustomerPortal = useStripeBilling('openCustomerPortal', authState);
35
+
36
+ // Checkout → Shows "Redirecting to Stripe" / "Initializing secure payment..."
37
+ await checkout({ priceId: 'price_123', mode: 'payment' });
38
+
39
+ // Portal → Shows "Redirecting to Stripe" / "Opening billing portal..."
40
+ await openCustomerPortal();
41
+ ```
42
+
43
+ **What happens automatically:**
44
+ - Blocking fullscreen overlay during Firebase Functions cold start
45
+ - Operation-specific messaging (checkout vs portal)
46
+ - Translated in all 8 supported languages
47
+ - Clean removal on redirect or error
48
+
49
+ **Optional:** Use `loading` state if you want to disable your own buttons:
50
+ ```tsx
51
+ const loading = useStripeBilling('loading', authState);
52
+ <Button disabled={loading}>Purchase</Button>
53
+ ```
54
+
55
+ ---
56
+
27
57
  ## Advanced: Frontend Config
28
58
 
29
59
  ```typescript
@@ -69,10 +99,20 @@ import { useStripeBilling } from '@donotdev/billing';
69
99
  import { SubscriptionTemplate, PaymentTemplate } from '@donotdev/templates';
70
100
  import { ProductCard, SubscriptionManager } from '@donotdev/billing';
71
101
 
72
- const checkout = useStripeBilling('checkout');
73
- await checkout({ priceId: 'price_123', mode: 'subscription' });
102
+ // Auth state required
103
+ const user = useAuth('user');
104
+ const status = useAuth('status');
105
+ const authState = { user, status };
106
+
107
+ // Methods - framework handles overlay automatically
108
+ const checkout = useStripeBilling('checkout', authState);
109
+ const openCustomerPortal = useStripeBilling('openCustomerPortal', authState);
110
+
111
+ // Optional state access
112
+ const loading = useStripeBilling('loading', authState);
113
+ const error = useStripeBilling('error', authState);
74
114
  ```
75
115
 
76
116
  ---
77
117
 
78
- **Create config files, get billing. Framework handles the rest.**
118
+ **Create config files, get billing with best-in-class UX. Framework handles the rest.**