create-questpie 2.0.2 → 2.0.4
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/dist/index.mjs +244 -30
- package/package.json +1 -1
- package/skills/questpie/AGENTS.md +310 -103
- package/skills/questpie/SKILL.md +196 -84
- package/skills/questpie/coverage.json +213 -0
- package/skills/questpie/references/auth.md +119 -4
- package/skills/questpie/references/business-logic.md +126 -56
- package/skills/questpie/references/crud-api.md +231 -29
- package/skills/questpie/references/data-modeling.md +26 -6
- package/skills/questpie/references/extend.md +98 -7
- package/skills/questpie/references/field-types.md +14 -2
- package/skills/questpie/references/infrastructure-adapters.md +207 -32
- package/skills/questpie/references/mcp.md +147 -0
- package/skills/questpie/references/multi-tenancy.md +1 -2
- package/skills/questpie/references/production.md +218 -53
- package/skills/questpie/references/quickstart.md +31 -18
- package/skills/questpie/references/rules.md +140 -13
- package/skills/questpie/references/sandbox.md +110 -0
- package/skills/questpie/references/tanstack-query.md +34 -11
- package/skills/questpie/references/type-inference.md +167 -0
- package/skills/questpie/references/workflows.md +155 -0
- package/skills/questpie-admin/AGENTS.md +141 -68
- package/skills/questpie-admin/SKILL.md +96 -63
- package/skills/questpie-admin/references/blocks.md +28 -4
- package/skills/questpie-admin/references/custom-ui.md +1 -1
- package/skills/questpie-admin/references/views.md +21 -5
- package/templates/tanstack-start/AGENTS.md +15 -8
- package/templates/tanstack-start/CLAUDE.md +12 -5
- package/templates/tanstack-start/README.md +7 -6
- package/templates/tanstack-start/package.json +1 -0
- package/templates/tanstack-start/src/lib/query-client.ts +10 -1
- package/templates/tanstack-start/src/questpie/admin/modules.ts +3 -1
- package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +10 -9
- package/templates/tanstack-start/src/questpie/server/config/auth.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/modules.ts +4 -5
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +2 -1
- package/templates/tanstack-start/src/routes/admin/$.tsx +12 -1
- package/templates/tanstack-start/src/routes/admin/index.tsx +12 -5
- package/templates/tanstack-start/src/routes/api/$.ts +1 -2
- package/templates/tanstack-start/vite.config.ts +2 -2
|
@@ -13,11 +13,11 @@ The QUESTPIE admin panel is a **projection of your server schema** — not the f
|
|
|
13
13
|
|
|
14
14
|
## Reference Topics
|
|
15
15
|
|
|
16
|
-
| Topic
|
|
17
|
-
|
|
18
|
-
| Views
|
|
19
|
-
| Blocks
|
|
20
|
-
| Custom UI | `references/custom-ui.md` | Custom fields, custom views, registries, reactive fields, widgets
|
|
16
|
+
| Topic | File | Covers |
|
|
17
|
+
| --------- | ------------------------- | -------------------------------------------------------------------------------------- |
|
|
18
|
+
| Views | `references/views.md` | List views, form views, dashboard, sidebar, filters, bulk actions, visibility, history |
|
|
19
|
+
| Blocks | `references/blocks.md` | Block definitions, fields, prefetch, renderers, block picker |
|
|
20
|
+
| Custom UI | `references/custom-ui.md` | Custom fields, custom views, registries, reactive fields, widgets |
|
|
21
21
|
|
|
22
22
|
## Full Compiled Document
|
|
23
23
|
|
|
@@ -29,7 +29,8 @@ For the complete admin reference with all topics expanded: `AGENTS.md`
|
|
|
29
29
|
- **@base-ui/react** primitives (NOT @radix-ui)
|
|
30
30
|
- **@iconify/react** with Phosphor icon set (`ph:icon-name`)
|
|
31
31
|
- **sonner** for toasts — `toast.error()`, `toast.success()`
|
|
32
|
-
-
|
|
32
|
+
- QUESTPIE Neutral Design: flat surfaces, soft neutral geometry,
|
|
33
|
+
tokenized radius, and restrained floating shadows
|
|
33
34
|
|
|
34
35
|
## Setup
|
|
35
36
|
|
|
@@ -42,8 +43,7 @@ bun add @questpie/admin
|
|
|
42
43
|
### 2. Runtime Config
|
|
43
44
|
|
|
44
45
|
```ts title="questpie.config.ts"
|
|
45
|
-
import { runtimeConfig } from "questpie";
|
|
46
|
-
|
|
46
|
+
import { runtimeConfig } from "questpie/app";
|
|
47
47
|
export default runtimeConfig({
|
|
48
48
|
app: { url: process.env.APP_URL || "http://localhost:3000" },
|
|
49
49
|
db: { url: process.env.DATABASE_URL },
|
|
@@ -56,7 +56,8 @@ The admin module contributes the codegen plugin automatically. It discovers `con
|
|
|
56
56
|
### 3. Modules
|
|
57
57
|
|
|
58
58
|
```ts title="modules.ts"
|
|
59
|
-
import { adminModule
|
|
59
|
+
import { adminModule } from "@questpie/admin/modules/admin";
|
|
60
|
+
import { auditModule } from "@questpie/admin/modules/audit";
|
|
60
61
|
|
|
61
62
|
export default [adminModule, auditModule] as const;
|
|
62
63
|
```
|
|
@@ -66,6 +67,26 @@ export default [adminModule, auditModule] as const;
|
|
|
66
67
|
| `adminModule` | User collection, auth pages, admin UI |
|
|
67
68
|
| `auditModule` | Audit log collection, timeline widget |
|
|
68
69
|
|
|
70
|
+
### Auth/User Contract - Critical
|
|
71
|
+
|
|
72
|
+
`adminModule` includes the starter auth model and owns the canonical Better Auth `user` collection shape used by admin setup and login guards. That contract includes `user.role` with at least `admin` and `user` values. The built-in setup route checks for `role = "admin"`, and the admin `AuthGuard` expects `session.user.role === "admin"`.
|
|
73
|
+
|
|
74
|
+
Do not create a replacement `collection("user")` from scratch in an app that uses `adminModule`. If the app needs to customize the user admin UI or add fields, merge the starter user collection and extend it:
|
|
75
|
+
|
|
76
|
+
```ts title="collections/user.ts"
|
|
77
|
+
import { starterModule } from "questpie/app";
|
|
78
|
+
import { collection } from "#questpie/factories";
|
|
79
|
+
|
|
80
|
+
export default collection("user")
|
|
81
|
+
.merge(starterModule.collections.user)
|
|
82
|
+
.fields(({ f }) => ({
|
|
83
|
+
// add app-specific user fields here
|
|
84
|
+
internalNotes: f.textarea(),
|
|
85
|
+
}));
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
App-specific authorization tables are fine, but they must not replace or remove the admin user contract unless the app also replaces the built-in admin setup route and auth guard.
|
|
89
|
+
|
|
69
90
|
### 4. Admin Config
|
|
70
91
|
|
|
71
92
|
```ts title="config/admin.ts"
|
|
@@ -138,44 +159,31 @@ export default adminConfig({
|
|
|
138
159
|
|
|
139
160
|
The admin uses CSS variables for all theming. Override them in your own CSS file.
|
|
140
161
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
|
144
|
-
|
|
|
145
|
-
|
|
|
146
|
-
|
|
|
147
|
-
|
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
| `--
|
|
156
|
-
| `--
|
|
157
|
-
| `--
|
|
158
|
-
| `--
|
|
159
|
-
| `--radius` | `0px` | Border radius (0 = brutalist) |
|
|
160
|
-
|
|
161
|
-
### Dark Theme (`.dark` class)
|
|
162
|
-
|
|
163
|
-
Dark mode uses the `.dark` class on the root element. Key overrides:
|
|
164
|
-
|
|
165
|
-
| Variable | Dark Value |
|
|
166
|
-
| -------------- | ---------- |
|
|
167
|
-
| `--background` | `#0A0A0A` |
|
|
168
|
-
| `--foreground` | `#FFFFFF` |
|
|
169
|
-
| `--card` | `#111111` |
|
|
170
|
-
| `--border` | `#333333` |
|
|
171
|
-
| `--muted` | `#1A1A1A` |
|
|
162
|
+
The full source of truth is `packages/admin/DESIGN.md`. Key defaults:
|
|
163
|
+
|
|
164
|
+
| Role | Dark | Light |
|
|
165
|
+
| ------------- | --------- | --------- |
|
|
166
|
+
| Background | `#121212` | `#fafafa` |
|
|
167
|
+
| Foreground | `#ececec` | `#1c1c1c` |
|
|
168
|
+
| Card | `#1b1b1b` | `#ffffff` |
|
|
169
|
+
| Surface high | `#2a2a2a` | `#e8e8e8` |
|
|
170
|
+
| Border | `#343434` | `#e2e2e2` |
|
|
171
|
+
| Border subtle | `#262626` | `#ebebeb` |
|
|
172
|
+
| Brand primary | `#b700ff` | `#b700ff` |
|
|
173
|
+
|
|
174
|
+
| Token | Default | Use |
|
|
175
|
+
| ------------------------ | ------- | ------------------------------------------- |
|
|
176
|
+
| `--control-radius-inner` | `8px` | Icon buttons/actions nested inside controls |
|
|
177
|
+
| `--control-radius` | `12px` | Inputs, selects, buttons, compact controls |
|
|
178
|
+
| `--surface-radius` | `14px` | Cards, panels, grouped fields, docs blocks |
|
|
179
|
+
| `--floating-radius` | `14px` | Menus, popovers, dialogs, command panels |
|
|
172
180
|
|
|
173
181
|
### Typography
|
|
174
182
|
|
|
175
183
|
| Variable | Value |
|
|
176
184
|
| ------------- | ------------------------------------------------------------------- |
|
|
177
|
-
| `--font-sans` | `"Geist Variable"` —
|
|
178
|
-
| `--font-mono` | `"JetBrains Mono Variable"` —
|
|
185
|
+
| `--font-sans` | `"Geist Variable"` — UI, prose, headings, labels, navigation |
|
|
186
|
+
| `--font-mono` | `"JetBrains Mono Variable"` — code, file paths, commands, IDs |
|
|
179
187
|
|
|
180
188
|
### Sidebar Variables
|
|
181
189
|
|
|
@@ -193,8 +201,7 @@ Separate tokens for independent sidebar theming: `--sidebar`, `--sidebar-foregro
|
|
|
193
201
|
When collections have `.localized()` fields, the admin shows a locale switcher:
|
|
194
202
|
|
|
195
203
|
```ts title="config/app.ts"
|
|
196
|
-
import { appConfig } from "questpie";
|
|
197
|
-
|
|
204
|
+
import { appConfig } from "questpie/app";
|
|
198
205
|
export default appConfig({
|
|
199
206
|
locale: {
|
|
200
207
|
locales: [
|
|
@@ -222,9 +229,9 @@ The admin renders drag-and-drop upload, image preview, file info, and remove but
|
|
|
222
229
|
|
|
223
230
|
## Live Preview
|
|
224
231
|
|
|
225
|
-
Live Preview
|
|
232
|
+
Live Preview is one system: the existing collection `FormView`, Preview button, `LivePreviewMode`, and frontend iframe. Preserve the normal form lifecycle. Do not introduce a separate visual-edit form API, a second default form view, or a parallel preview surface.
|
|
226
233
|
|
|
227
|
-
|
|
234
|
+
The admin form is authoritative. The iframe mirrors form state through `postMessage`, supports field/block focus, and may request inline scalar edits. Persistence still goes through existing save, autosave, Cmd+S, history, workflow, locks, and actions.
|
|
228
235
|
|
|
229
236
|
### Server Config
|
|
230
237
|
|
|
@@ -248,35 +255,57 @@ export const pages = collection("pages")
|
|
|
248
255
|
});
|
|
249
256
|
```
|
|
250
257
|
|
|
251
|
-
|
|
258
|
+
Preview opens the existing split-screen editor. Patches and refresh/resync messages update the iframe mirror; save/autosave still writes through the form.
|
|
252
259
|
|
|
253
|
-
### Frontend
|
|
260
|
+
### Prepare a Frontend Page
|
|
254
261
|
|
|
255
|
-
Use `useCollectionPreview
|
|
262
|
+
Use exported APIs only: `useCollectionPreview`, `PreviewProvider`, `PreviewField`, and `BlockRenderer`.
|
|
263
|
+
|
|
264
|
+
Checklist:
|
|
265
|
+
|
|
266
|
+
1. Configure `.preview({ url })` on the collection.
|
|
267
|
+
2. Load the same record shape the page normally renders.
|
|
268
|
+
3. For workflow-published pages, public reads use `stage: "published"`; if the public client/HTTP API is exposed, anonymous read access also checks `input?.stage === "published"`. Authorized preview/draft reads can load the working record.
|
|
269
|
+
4. Call `useCollectionPreview({ initialData, onRefresh })` in the page renderer.
|
|
270
|
+
5. Wrap the visual output in `PreviewProvider`.
|
|
271
|
+
6. Render from `preview.data`, not the original loader object.
|
|
272
|
+
7. Wrap editable scalar text with `PreviewField field="..." editable="text" | "textarea"`.
|
|
273
|
+
8. Render block content with `BlockRenderer`; pass `selectedBlockId` and `onBlockClick`.
|
|
274
|
+
9. Keep add/remove/reorder/nesting block operations in the existing block editor.
|
|
256
275
|
|
|
257
276
|
```tsx
|
|
258
277
|
import {
|
|
278
|
+
BlockRenderer,
|
|
259
279
|
PreviewField,
|
|
260
280
|
PreviewProvider,
|
|
261
281
|
useCollectionPreview,
|
|
262
282
|
} from "@questpie/admin/client";
|
|
283
|
+
import admin from "@/questpie/admin/.generated/client";
|
|
263
284
|
|
|
264
|
-
function PagePreview({
|
|
285
|
+
function PagePreview({ page }) {
|
|
265
286
|
const router = useRouter();
|
|
266
287
|
const preview = useCollectionPreview({
|
|
267
|
-
initialData,
|
|
288
|
+
initialData: page,
|
|
268
289
|
onRefresh: () => router.invalidate(),
|
|
269
290
|
});
|
|
270
291
|
|
|
271
292
|
return (
|
|
272
|
-
<PreviewProvider
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
293
|
+
<PreviewProvider preview={preview}>
|
|
294
|
+
<main className={preview.isPreviewMode ? "questpie-preview" : undefined}>
|
|
295
|
+
<PreviewField field="title" editable="text" as="h1">
|
|
296
|
+
{preview.data.title}
|
|
297
|
+
</PreviewField>
|
|
298
|
+
|
|
299
|
+
<BlockRenderer
|
|
300
|
+
content={preview.data.content}
|
|
301
|
+
data={preview.data.content?._data}
|
|
302
|
+
renderers={admin.blocks}
|
|
303
|
+
selectedBlockId={preview.selectedBlockId}
|
|
304
|
+
onBlockClick={
|
|
305
|
+
preview.isPreviewMode ? preview.handleBlockClick : undefined
|
|
306
|
+
}
|
|
307
|
+
/>
|
|
308
|
+
</main>
|
|
280
309
|
</PreviewProvider>
|
|
281
310
|
);
|
|
282
311
|
}
|
|
@@ -284,11 +313,15 @@ function PagePreview({ initialData }) {
|
|
|
284
313
|
|
|
285
314
|
### Key Principles
|
|
286
315
|
|
|
287
|
-
-
|
|
288
|
-
-
|
|
316
|
+
- Keep `FormView`, the Preview button, and `LivePreviewMode`
|
|
317
|
+
- Never add a separate visual-edit form API, a second default form view, or parallel preview API names
|
|
318
|
+
- Preserve save/autosave/Cmd+S/history/workflow/locks/actions
|
|
319
|
+
- `useCollectionPreview` handles preview mode, mirrored data, refresh/resync, and focus state
|
|
289
320
|
- `PreviewProvider` supplies preview context to `PreviewField`
|
|
290
|
-
-
|
|
291
|
-
-
|
|
321
|
+
- `PreviewField` annotates scalar fields and can opt into inline editing with `editable`
|
|
322
|
+
- `BlockRenderer` preserves block IDs and block scopes for block annotations
|
|
323
|
+
- Use `BlockScopeProvider` only for custom/manual block rendering outside `BlockRenderer`
|
|
324
|
+
- Validate all iframe messages before updating form state
|
|
292
325
|
|
|
293
326
|
## History & Versions
|
|
294
327
|
|
|
@@ -281,11 +281,35 @@ function PageRenderer({ page }) {
|
|
|
281
281
|
|
|
282
282
|
## Blocks in Live Preview
|
|
283
283
|
|
|
284
|
-
When a collection has `.preview()` configured, blocks
|
|
284
|
+
When a collection has `.preview()` configured, blocks participate in the existing Live Preview system through `BlockRenderer` and `PreviewField`. The form remains authoritative; block annotations only mirror values, focus fields, select blocks, or request inline scalar edits.
|
|
285
285
|
|
|
286
|
-
###
|
|
286
|
+
### Preferred: BlockRenderer
|
|
287
287
|
|
|
288
|
-
Use `
|
|
288
|
+
Use `BlockRenderer` for normal frontend page rendering. It preserves `data-block-id`, routes block selection, and scopes nested `PreviewField` paths automatically.
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
<BlockRenderer
|
|
292
|
+
content={preview.data.content}
|
|
293
|
+
data={preview.data.content?._data}
|
|
294
|
+
renderers={admin.blocks}
|
|
295
|
+
selectedBlockId={preview.selectedBlockId}
|
|
296
|
+
onBlockClick={preview.isPreviewMode ? preview.handleBlockClick : undefined}
|
|
297
|
+
/>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Inside custom block renderers, annotate scalar values with `PreviewField`:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
<PreviewField field="title" editable="text" as="h2">
|
|
304
|
+
{values.title}
|
|
305
|
+
</PreviewField>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
This resolves to `content._values.{blockId}.title` when rendered inside `BlockRenderer`.
|
|
309
|
+
|
|
310
|
+
### Manual BlockScopeProvider Wrapper
|
|
311
|
+
|
|
312
|
+
Use `BlockScopeProvider` only when you manually render blocks outside `BlockRenderer`:
|
|
289
313
|
|
|
290
314
|
```tsx
|
|
291
315
|
import { BlockScopeProvider } from "@questpie/admin/client";
|
|
@@ -304,4 +328,4 @@ function PageRenderer({ blocks, previewData }) {
|
|
|
304
328
|
|
|
305
329
|
`PreviewField` components inside the provider resolve paths like `content._values.{blockId}.title`.
|
|
306
330
|
|
|
307
|
-
|
|
331
|
+
Inline block edits target `_values`, for example `content._values.<blockId>.title`. Tree edits such as add, remove, reorder, and nesting stay in the existing block editor and should trigger refresh or resync when patching is not safe.
|
|
@@ -302,4 +302,4 @@ toast.error("Failed to save");
|
|
|
302
302
|
|
|
303
303
|
6. **MEDIUM: Using `@phosphor-icons/react` or `lucide-react`** — use `@iconify/react` with `ph:` prefix for all icons.
|
|
304
304
|
|
|
305
|
-
7. **LOW: Not using shadcn components** — prefer `<Button>`, `<Card>`, `<Input>` from the shadcn component library instead of raw HTML elements. The admin
|
|
305
|
+
7. **LOW: Not using shadcn components** — prefer `<Button>`, `<Card>`, `<Input>` from the shadcn component library instead of raw HTML elements. The admin follows QUESTPIE Neutral Design.
|
|
@@ -161,7 +161,7 @@ Section-level visibility:
|
|
|
161
161
|
{
|
|
162
162
|
type: "section",
|
|
163
163
|
label: { en: "SEO" },
|
|
164
|
-
hidden: ({ data }) => !data.
|
|
164
|
+
hidden: ({ data }) => !data.showSeo,
|
|
165
165
|
fields: [f.metaTitle, f.metaDescription],
|
|
166
166
|
}
|
|
167
167
|
```
|
|
@@ -404,7 +404,7 @@ export const logs = collection("logs")
|
|
|
404
404
|
|
|
405
405
|
## Form Views and Live Preview
|
|
406
406
|
|
|
407
|
-
Form views connect to the Live Preview
|
|
407
|
+
Form views connect to the existing Live Preview system when the collection has `.preview()` configured. Keep `v.collectionForm()` as the form surface; do not introduce a separate visual-edit form API, a second default form view, or parallel preview API names. The form editor remains the source of patches, refreshes, commits, and resyncs.
|
|
408
408
|
|
|
409
409
|
### Enabling Preview on a Collection
|
|
410
410
|
|
|
@@ -428,6 +428,22 @@ export const pages = collection("pages")
|
|
|
428
428
|
### How It Works
|
|
429
429
|
|
|
430
430
|
1. The form view detects `.preview()` config and opens a split-screen layout
|
|
431
|
-
2.
|
|
432
|
-
3.
|
|
433
|
-
4.
|
|
431
|
+
2. The preview iframe mirrors form state with snapshot, patch, refresh, commit, and resync messages
|
|
432
|
+
3. Save/autosave/Cmd+S/history/workflow/locks/actions stay in the form lifecycle
|
|
433
|
+
4. The preview page uses `useCollectionPreview({ initialData, onRefresh })`
|
|
434
|
+
5. `PreviewProvider`, `PreviewField`, and `BlockRenderer` wire field and block annotations
|
|
435
|
+
|
|
436
|
+
### Frontend Preparation Checklist
|
|
437
|
+
|
|
438
|
+
Use this checklist before expecting visual editing to work:
|
|
439
|
+
|
|
440
|
+
1. The collection has `.preview({ url })`.
|
|
441
|
+
2. The page loader returns the complete rendered record shape.
|
|
442
|
+
3. Public workflow reads use `stage: "published"`; if public client/HTTP access is enabled, anonymous read access also requires `input?.stage === "published"`. Preview/draft-mode reads load the working record for authorized editors.
|
|
443
|
+
4. The page renderer calls `useCollectionPreview({ initialData, onRefresh })`.
|
|
444
|
+
5. The rendered page is wrapped in `PreviewProvider preview={preview}`.
|
|
445
|
+
6. UI reads from `preview.data`, not directly from loader data.
|
|
446
|
+
7. Scalar text uses `PreviewField field="..." editable="text" | "textarea"` when inline editing is expected.
|
|
447
|
+
8. Blocks render through `BlockRenderer` with `selectedBlockId` and `onBlockClick`.
|
|
448
|
+
9. `BlockScopeProvider` is only needed for custom/manual block rendering outside `BlockRenderer`.
|
|
449
|
+
10. Add/remove/reorder/nesting block operations stay in the existing block editor.
|
|
@@ -124,6 +124,12 @@ src/
|
|
|
124
124
|
- **`questpie.config.ts`** — CLI config (migration directory, app reference).
|
|
125
125
|
- **`src/routes/api/$.ts`** — API catch-all handler. Serves REST + OpenAPI docs at `/api/docs`.
|
|
126
126
|
|
|
127
|
+
### Admin User Contract
|
|
128
|
+
|
|
129
|
+
`adminModule` includes the starter auth model and owns the canonical Better Auth `user` collection shape used by admin setup and login guards. That contract includes `user.role` (`admin` or `user`).
|
|
130
|
+
|
|
131
|
+
Do **not** create a replacement `collection("user")` from scratch. If you need custom user fields or admin layout, merge `starterModule.collections.user` or `adminModule.collections.user` and extend it. Replacing the user collection breaks admin setup/login.
|
|
132
|
+
|
|
127
133
|
## How To Write Code
|
|
128
134
|
|
|
129
135
|
### Creating a Collection
|
|
@@ -160,7 +166,7 @@ export const posts = collection("posts")
|
|
|
160
166
|
{ value: "tutorial", label: "Tutorial" },
|
|
161
167
|
])
|
|
162
168
|
.label("Category")
|
|
163
|
-
author: f.relation("
|
|
169
|
+
author: f.relation("user").label("Author"),
|
|
164
170
|
image: f.upload().label("Cover Image"),
|
|
165
171
|
}))
|
|
166
172
|
.title(({ f }) => f.title)
|
|
@@ -194,12 +200,12 @@ Then (preferred):
|
|
|
194
200
|
|
|
195
201
|
1. Use `bun questpie add collection <name>` to scaffold files
|
|
196
202
|
2. Codegen runs automatically
|
|
197
|
-
3. Run `bun run migrate:create`
|
|
203
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
198
204
|
|
|
199
205
|
Manual workflow (if you create files yourself):
|
|
200
206
|
|
|
201
207
|
1. Run `bun run questpie:generate` to regenerate `.generated/index.ts`
|
|
202
|
-
2. Run `bun run migrate:create`
|
|
208
|
+
2. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
203
209
|
|
|
204
210
|
Collections are auto-discovered by codegen — no manual registration needed.
|
|
205
211
|
|
|
@@ -233,12 +239,12 @@ Then (preferred):
|
|
|
233
239
|
|
|
234
240
|
1. Use `bun questpie add global <name>` to scaffold files
|
|
235
241
|
2. Codegen runs automatically
|
|
236
|
-
3. Run `bun run migrate:create`
|
|
242
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
237
243
|
|
|
238
244
|
Manual workflow (if you create files yourself):
|
|
239
245
|
|
|
240
246
|
1. Run `bun run questpie:generate` to regenerate `.generated/index.ts`
|
|
241
|
-
2. Run `bun run migrate:create`
|
|
247
|
+
2. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
242
248
|
|
|
243
249
|
Globals are auto-discovered by codegen — no manual registration needed.
|
|
244
250
|
|
|
@@ -250,7 +256,7 @@ Routes are defined as standalone files in `routes/` and auto-discovered by codeg
|
|
|
250
256
|
|
|
251
257
|
```ts
|
|
252
258
|
// src/questpie/server/routes/get-stats.ts
|
|
253
|
-
import { route } from "questpie";
|
|
259
|
+
import { route } from "questpie/services";
|
|
254
260
|
import { z } from "zod";
|
|
255
261
|
|
|
256
262
|
export default route()
|
|
@@ -384,7 +390,7 @@ If your blocks only use declarative prefetch (`{ with: { field: true } }`), you
|
|
|
384
390
|
|
|
385
391
|
Fields support reactive behaviors in `meta.admin`:
|
|
386
392
|
|
|
387
|
-
- **`hidden`**: Conditionally hide — `({ data }: { data: Record<string,
|
|
393
|
+
- **`hidden`**: Conditionally hide — `({ data }: { data: Record<string, unknown> }) => !data.isPublished`
|
|
388
394
|
- **`readOnly`**: Make read-only based on conditions
|
|
389
395
|
- **`disabled`**: Disable conditionally
|
|
390
396
|
- **`compute`**: Auto-compute values — `{ handler, deps, debounce }`
|
|
@@ -457,7 +463,7 @@ export const {
|
|
|
457
463
|
|
|
458
464
|
```ts
|
|
459
465
|
// src/routes/api/$.ts
|
|
460
|
-
import { createFetchHandler } from "questpie";
|
|
466
|
+
import { createFetchHandler } from "questpie/http";
|
|
461
467
|
import { app } from "#questpie";
|
|
462
468
|
|
|
463
469
|
const handler = createFetchHandler(app, { basePath: "/api" });
|
|
@@ -498,6 +504,7 @@ Optional (with defaults):
|
|
|
498
504
|
bun dev # Start dev server
|
|
499
505
|
bun build # Build for production
|
|
500
506
|
bun start # Start production server
|
|
507
|
+
bun run db:push # Push schema to local dev database
|
|
501
508
|
bun run migrate # Run database migrations
|
|
502
509
|
bun run migrate:create # Create new migration
|
|
503
510
|
bun run routes:generate # Regenerate TanStack Router route tree
|
|
@@ -15,6 +15,7 @@ This is a [QUESTPIE](https://questpie.com) project scaffolded with `create-quest
|
|
|
15
15
|
| `bun run questpie:generate` | Regenerate .generated/index.ts |
|
|
16
16
|
| `bun run scaffold:generate` | Regenerate route tree and QUESTPIE output |
|
|
17
17
|
| `bun run scaffold:verify` | Regenerate codegen and type-check |
|
|
18
|
+
| `bun run db:push` | Push schema to local dev database |
|
|
18
19
|
| `bun run migrate:create` | Generate a migration from schema diff |
|
|
19
20
|
| `bun run migrate` | Run pending migrations |
|
|
20
21
|
| `bun questpie seed` | Run pending seeds |
|
|
@@ -62,6 +63,12 @@ src/questpie/
|
|
|
62
63
|
- **`questpie.config.ts`** — CLI config (migration directory, app reference).
|
|
63
64
|
- **`src/routes/api/$.ts`** — API catch-all handler. Serves REST + OpenAPI docs at `/api/docs`.
|
|
64
65
|
|
|
66
|
+
## Admin User Contract
|
|
67
|
+
|
|
68
|
+
`adminModule` includes the starter auth model and owns the canonical Better Auth `user` collection shape used by admin setup and login guards. That contract includes `user.role` (`admin` or `user`).
|
|
69
|
+
|
|
70
|
+
Do **not** create a replacement `collection("user")` from scratch. If you need custom user fields or admin layout, merge `starterModule.collections.user` or `adminModule.collections.user` and extend it. Replacing the user collection breaks admin setup/login.
|
|
71
|
+
|
|
65
72
|
## Environment Variables
|
|
66
73
|
|
|
67
74
|
Defined in `src/lib/env.ts` with runtime validation. See `.env.example` for all available variables.
|
|
@@ -84,7 +91,7 @@ Preferred workflow:
|
|
|
84
91
|
|
|
85
92
|
1. Run `bun questpie add collection my-thing`
|
|
86
93
|
2. The CLI creates the file and auto-runs codegen
|
|
87
|
-
3. Run `bun run migrate:create`
|
|
94
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
88
95
|
|
|
89
96
|
Manual workflow:
|
|
90
97
|
|
|
@@ -94,7 +101,7 @@ Manual workflow:
|
|
|
94
101
|
export const myThing = collection("my-thing").fields(({ f }) => ({ ... }));
|
|
95
102
|
```
|
|
96
103
|
2. Run `bun run questpie:generate` to regenerate `.generated/index.ts`
|
|
97
|
-
3. Run `bun run migrate:create`
|
|
104
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
98
105
|
|
|
99
106
|
Collections are auto-discovered by codegen — no manual registration needed.
|
|
100
107
|
|
|
@@ -104,20 +111,20 @@ Preferred workflow:
|
|
|
104
111
|
|
|
105
112
|
1. Run `bun questpie add global my-global`
|
|
106
113
|
2. The CLI creates the file and auto-runs codegen
|
|
107
|
-
3. Run `bun run migrate:create`
|
|
114
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
108
115
|
|
|
109
116
|
Manual workflow:
|
|
110
117
|
|
|
111
118
|
1. Create `src/questpie/server/globals/my-global.ts` with a named export
|
|
112
119
|
2. Run `bun run questpie:generate`
|
|
113
|
-
3. Run `bun run migrate:create`
|
|
120
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
114
121
|
|
|
115
122
|
### Add a server route (end-to-end type-safe)
|
|
116
123
|
|
|
117
124
|
1. Create `src/questpie/server/routes/my-function.ts`:
|
|
118
125
|
|
|
119
126
|
```ts
|
|
120
|
-
import { route } from "questpie";
|
|
127
|
+
import { route } from "questpie/services";
|
|
121
128
|
import { z } from "zod";
|
|
122
129
|
|
|
123
130
|
export default route()
|
|
@@ -18,8 +18,8 @@ docker compose up -d
|
|
|
18
18
|
# 2) Regenerate codegen and type-check
|
|
19
19
|
bun run scaffold:verify
|
|
20
20
|
|
|
21
|
-
# 3)
|
|
22
|
-
bun run
|
|
21
|
+
# 3) Create local database tables
|
|
22
|
+
bun run db:push
|
|
23
23
|
|
|
24
24
|
# 4) Start development server
|
|
25
25
|
bun run dev
|
|
@@ -75,6 +75,7 @@ migrations/
|
|
|
75
75
|
| `bun run routes:generate` | Regenerate TanStack Router route tree |
|
|
76
76
|
| `bun run questpie:generate` | Regenerate `src/questpie/server/.generated/*` |
|
|
77
77
|
| `bun questpie add <type> <name>` | Scaffold entity files (auto-runs codegen) |
|
|
78
|
+
| `bun run db:push` | Push schema directly to local dev database |
|
|
78
79
|
| `bun run migrate` | Run migrations |
|
|
79
80
|
| `bun run migrate:create` | Create migration |
|
|
80
81
|
|
|
@@ -84,14 +85,14 @@ Preferred workflow:
|
|
|
84
85
|
|
|
85
86
|
1. Run `bun questpie add collection products`.
|
|
86
87
|
2. The CLI creates the file and runs codegen automatically.
|
|
87
|
-
3. Run `bun run migrate:create
|
|
88
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
88
89
|
|
|
89
90
|
Manual workflow (when you create files by hand):
|
|
90
91
|
|
|
91
92
|
1. Create a file in `src/questpie/server/collections/`.
|
|
92
93
|
2. Export a collection builder from that file.
|
|
93
94
|
3. Run `bun run questpie:generate`.
|
|
94
|
-
4. Run `bun run migrate:create
|
|
95
|
+
4. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
95
96
|
|
|
96
97
|
Collections are discovered automatically by codegen. No manual `app.ts` registration is required.
|
|
97
98
|
|
|
@@ -101,13 +102,13 @@ Preferred workflow:
|
|
|
101
102
|
|
|
102
103
|
1. Run `bun questpie add global marketing`.
|
|
103
104
|
2. The CLI creates the file and runs codegen automatically.
|
|
104
|
-
3. Run `bun run migrate:create
|
|
105
|
+
3. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
105
106
|
|
|
106
107
|
Manual workflow (when you create files by hand):
|
|
107
108
|
|
|
108
109
|
1. Create a file in `src/questpie/server/globals/`.
|
|
109
110
|
2. Export a global builder from that file.
|
|
110
111
|
3. Run `bun run questpie:generate`.
|
|
111
|
-
4. Run `bun run migrate:create
|
|
112
|
+
4. Run `bun run db:push` for local development, or `bun run migrate:create` for production migrations.
|
|
112
113
|
|
|
113
114
|
Globals are discovered automatically by codegen. No manual `app.ts` registration is required.
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"routes:generate": "tsr generate",
|
|
16
16
|
"questpie:generate": "questpie generate -c src/questpie/server/questpie.config.ts",
|
|
17
17
|
"scaffold:generate": "bun run routes:generate && bun run questpie:generate",
|
|
18
|
+
"db:push": "questpie push -c questpie.config.ts",
|
|
18
19
|
"migrate": "questpie migrate -c questpie.config.ts",
|
|
19
20
|
"migrate:create": "questpie migrate:create -c questpie.config.ts",
|
|
20
21
|
"migrate:status": "questpie migrate:status -c questpie.config.ts",
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { QueryClient } from "@tanstack/react-query";
|
|
2
2
|
|
|
3
|
+
const ONE_MINUTE = 60 * 1000;
|
|
4
|
+
const FIVE_MINUTES = 5 * ONE_MINUTE;
|
|
5
|
+
|
|
3
6
|
export const queryClient = new QueryClient({
|
|
4
7
|
defaultOptions: {
|
|
5
8
|
queries: {
|
|
6
|
-
staleTime:
|
|
9
|
+
staleTime: ONE_MINUTE,
|
|
10
|
+
gcTime: FIVE_MINUTES,
|
|
11
|
+
refetchOnWindowFocus: false,
|
|
12
|
+
retry: 1,
|
|
13
|
+
},
|
|
14
|
+
mutations: {
|
|
15
|
+
retry: 0,
|
|
7
16
|
},
|
|
8
17
|
},
|
|
9
18
|
});
|