create-questpie 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +544 -87
- package/package.json +2 -3
- package/templates/elysia/AGENTS.md +56 -0
- package/templates/elysia/CLAUDE.md +39 -0
- package/templates/elysia/Dockerfile +24 -0
- package/templates/elysia/README.md +148 -0
- package/templates/elysia/docker/init-extensions.sql +11 -0
- package/templates/elysia/docker-compose.yml +21 -0
- package/templates/elysia/env.example +16 -0
- package/templates/elysia/gitignore +6 -0
- package/templates/elysia/package.json +47 -0
- package/templates/elysia/questpie.config.ts +12 -0
- package/templates/elysia/src/index.ts +21 -0
- package/templates/elysia/src/lib/auth-client.ts +32 -0
- package/templates/elysia/src/lib/client.ts +13 -0
- package/templates/elysia/src/lib/env.ts +24 -0
- package/templates/elysia/src/lib/query-client.ts +18 -0
- package/templates/elysia/src/lib/query.ts +18 -0
- package/templates/elysia/src/questpie/server/.generated/context.gen.ts +200 -0
- package/templates/elysia/src/questpie/server/.generated/entities.gen.ts +84 -0
- package/templates/elysia/src/questpie/server/.generated/factories.ts +65 -0
- package/templates/elysia/src/questpie/server/.generated/index.ts +131 -0
- package/templates/elysia/src/questpie/server/.generated/names.gen.ts +25 -0
- package/templates/elysia/src/questpie/server/app.ts +10 -0
- package/templates/elysia/src/questpie/server/collections/index.ts +1 -0
- package/templates/elysia/src/questpie/server/collections/posts.collection.ts +10 -0
- package/templates/elysia/src/questpie/server/config/auth.ts +8 -0
- package/templates/elysia/src/questpie/server/config/openapi.ts +10 -0
- package/templates/elysia/src/questpie/server/globals/index.ts +1 -0
- package/templates/elysia/src/questpie/server/globals/site-settings.global.ts +10 -0
- package/templates/elysia/src/questpie/server/modules.ts +8 -0
- package/templates/elysia/src/questpie/server/questpie.config.ts +21 -0
- package/templates/elysia/tsconfig.json +28 -0
- package/templates/hono/AGENTS.md +56 -0
- package/templates/hono/CLAUDE.md +39 -0
- package/templates/hono/Dockerfile +24 -0
- package/templates/hono/README.md +148 -0
- package/templates/hono/docker/init-extensions.sql +11 -0
- package/templates/hono/docker-compose.yml +21 -0
- package/templates/hono/env.example +16 -0
- package/templates/hono/gitignore +6 -0
- package/templates/hono/package.json +47 -0
- package/templates/hono/questpie.config.ts +12 -0
- package/templates/hono/src/index.ts +30 -0
- package/templates/hono/src/lib/auth-client.ts +32 -0
- package/templates/hono/src/lib/client.ts +13 -0
- package/templates/hono/src/lib/env.ts +24 -0
- package/templates/hono/src/lib/query-client.ts +18 -0
- package/templates/hono/src/lib/query.ts +18 -0
- package/templates/hono/src/questpie/server/.generated/context.gen.ts +200 -0
- package/templates/hono/src/questpie/server/.generated/entities.gen.ts +84 -0
- package/templates/hono/src/questpie/server/.generated/factories.ts +65 -0
- package/templates/hono/src/questpie/server/.generated/index.ts +131 -0
- package/templates/hono/src/questpie/server/.generated/names.gen.ts +25 -0
- package/templates/hono/src/questpie/server/app.ts +10 -0
- package/templates/hono/src/questpie/server/collections/index.ts +1 -0
- package/templates/hono/src/questpie/server/collections/posts.collection.ts +10 -0
- package/templates/hono/src/questpie/server/config/auth.ts +8 -0
- package/templates/hono/src/questpie/server/config/openapi.ts +10 -0
- package/templates/hono/src/questpie/server/globals/index.ts +1 -0
- package/templates/hono/src/questpie/server/globals/site-settings.global.ts +10 -0
- package/templates/hono/src/questpie/server/modules.ts +8 -0
- package/templates/hono/src/questpie/server/questpie.config.ts +21 -0
- package/templates/hono/tsconfig.json +28 -0
- package/templates/next/AGENTS.md +55 -0
- package/templates/next/CLAUDE.md +39 -0
- package/templates/next/Dockerfile +25 -0
- package/templates/next/README.md +148 -0
- package/templates/next/components.json +22 -0
- package/templates/next/docker/init-extensions.sql +11 -0
- package/templates/next/docker-compose.yml +21 -0
- package/templates/next/env.example +16 -0
- package/templates/next/gitignore +10 -0
- package/templates/next/next-env.d.ts +5 -0
- package/templates/next/next.config.ts +20 -0
- package/templates/next/package.json +54 -0
- package/templates/next/postcss.config.mjs +8 -0
- package/templates/next/public/.gitkeep +0 -0
- package/templates/next/questpie.config.ts +12 -0
- package/templates/next/src/app/admin/[[...all]]/page.tsx +34 -0
- package/templates/next/src/app/admin/admin.css +4 -0
- package/templates/next/src/app/admin/layout.tsx +63 -0
- package/templates/next/src/app/api/[...all]/route.ts +24 -0
- package/templates/next/src/app/layout.tsx +24 -0
- package/templates/next/src/app/not-found.tsx +18 -0
- package/templates/next/src/app/page.tsx +74 -0
- package/templates/next/src/app/providers.tsx +11 -0
- package/templates/next/src/lib/auth-client.ts +12 -0
- package/templates/next/src/lib/client.ts +13 -0
- package/templates/next/src/lib/env.ts +24 -0
- package/templates/next/src/lib/query-client.ts +18 -0
- package/templates/next/src/lib/query.ts +18 -0
- package/templates/next/src/questpie/admin/.generated/client.ts +13 -0
- package/templates/next/src/questpie/admin/admin.ts +9 -0
- package/templates/next/src/questpie/admin/modules.ts +3 -0
- package/templates/next/src/questpie/server/.generated/context.gen.ts +204 -0
- package/templates/next/src/questpie/server/.generated/entities.gen.ts +100 -0
- package/templates/next/src/questpie/server/.generated/factories.ts +204 -0
- package/templates/next/src/questpie/server/.generated/index.ts +139 -0
- package/templates/next/src/questpie/server/.generated/names.gen.ts +31 -0
- package/templates/next/src/questpie/server/app.ts +10 -0
- package/templates/next/src/questpie/server/collections/index.ts +1 -0
- package/templates/next/src/questpie/server/collections/posts.collection.ts +58 -0
- package/templates/next/src/questpie/server/config/admin.ts +80 -0
- package/templates/next/src/questpie/server/config/auth.ts +8 -0
- package/templates/next/src/questpie/server/config/openapi.ts +10 -0
- package/templates/next/src/questpie/server/globals/index.ts +1 -0
- package/templates/next/src/questpie/server/globals/site-settings.global.ts +19 -0
- package/templates/next/src/questpie/server/modules.ts +9 -0
- package/templates/next/src/questpie/server/questpie.config.ts +21 -0
- package/templates/next/src/styles.css +125 -0
- package/templates/next/tsconfig.json +37 -0
- package/templates/tanstack-start/AGENTS.md +35 -600
- package/templates/tanstack-start/CLAUDE.md +26 -127
- package/templates/tanstack-start/README.md +20 -7
- package/templates/tanstack-start/docker/init-extensions.sql +11 -0
- package/templates/tanstack-start/docker-compose.yml +1 -0
- package/templates/tanstack-start/package.json +1 -0
- package/templates/tanstack-start/src/lib/auth-client.ts +1 -1
- package/templates/tanstack-start/src/lib/client.ts +1 -1
- package/templates/tanstack-start/src/lib/query.ts +18 -0
- 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/collections/index.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/config/auth.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/globals/index.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/modules.ts +4 -5
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +3 -2
- package/templates/tanstack-start/src/routes/__root.tsx +31 -1
- package/templates/tanstack-start/src/routes/api/$.ts +2 -3
- package/templates/tanstack-start/src/routes/index.tsx +97 -0
- package/templates/tanstack-start/vite.config.ts +2 -2
- package/skills/questpie/AGENTS.md +0 -2670
- package/skills/questpie/SKILL.md +0 -260
- package/skills/questpie/references/auth.md +0 -121
- package/skills/questpie/references/business-logic.md +0 -550
- package/skills/questpie/references/codegen-plugin-api.md +0 -382
- package/skills/questpie/references/crud-api.md +0 -378
- package/skills/questpie/references/data-modeling.md +0 -493
- package/skills/questpie/references/extend.md +0 -557
- package/skills/questpie/references/field-types.md +0 -386
- package/skills/questpie/references/infrastructure-adapters.md +0 -545
- package/skills/questpie/references/multi-tenancy.md +0 -364
- package/skills/questpie/references/production.md +0 -475
- package/skills/questpie/references/query-operators.md +0 -125
- package/skills/questpie/references/quickstart.md +0 -564
- package/skills/questpie/references/rules.md +0 -389
- package/skills/questpie/references/tanstack-query.md +0 -520
- package/skills/questpie-admin/AGENTS.md +0 -1508
- package/skills/questpie-admin/SKILL.md +0 -436
- package/skills/questpie-admin/references/blocks.md +0 -331
- package/skills/questpie-admin/references/custom-ui.md +0 -305
- package/skills/questpie-admin/references/views.md +0 -449
|
@@ -1,620 +1,55 @@
|
|
|
1
1
|
# AGENTS.md
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Guidance for AI agents working in this [QUESTPIE](https://questpie.com) project.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Use the QUESTPIE skills
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This project is built on QUESTPIE. Do not work from memory — the framework is
|
|
8
|
+
codegen-driven and the APIs evolve. Lean on the installed skills:
|
|
8
9
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
- **Auth**: Better Auth (email/password)
|
|
14
|
-
- **Package manager**: Bun
|
|
15
|
-
- **Validation**: Zod v4 (NOT v3)
|
|
10
|
+
- **`questpie`** — collections, globals, routes, jobs, codegen, auth, business
|
|
11
|
+
logic, the typed client + TanStack Query. Invoke it for any server/data work.
|
|
12
|
+
- **`questpie-admin`** — admin UI: views, blocks, custom fields, branding,
|
|
13
|
+
dashboard, live preview. Invoke it for any admin panel work.
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Invoke skills by name (the `/skill` convention) — they are commands, not files
|
|
16
|
+
to read.
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
1. **LLMs full docs**: https://questpie.com/llms-full.txt — complete documentation in a single LLM-optimized file
|
|
22
|
-
2. **Online docs**: https://questpie.com/docs — browsable documentation
|
|
23
|
-
3. **Local API docs**: http://localhost:3000/api/docs — Scalar UI (available when dev server is running)
|
|
24
|
-
|
|
25
|
-
Key documentation pages:
|
|
26
|
-
|
|
27
|
-
| Topic | URL |
|
|
28
|
-
| -------------------------- | ---------------------------------------------------------------- |
|
|
29
|
-
| Getting Started | https://questpie.com/docs/getting-started |
|
|
30
|
-
| Project Structure | https://questpie.com/docs/getting-started/project-structure |
|
|
31
|
-
| Your First QUESTPIE | https://questpie.com/docs/getting-started/your-first-app |
|
|
32
|
-
| Architecture Principles | https://questpie.com/docs/mentality |
|
|
33
|
-
| Field Builder | https://questpie.com/docs/server/field-builder |
|
|
34
|
-
| Field Types Reference | https://questpie.com/docs/server/field-types |
|
|
35
|
-
| Collections | https://questpie.com/docs/server/collections |
|
|
36
|
-
| Globals | https://questpie.com/docs/server/globals |
|
|
37
|
-
| Relations | https://questpie.com/docs/server/relations |
|
|
38
|
-
| Routes | https://questpie.com/docs/backend/business-logic/routes |
|
|
39
|
-
| Hooks & Lifecycle | https://questpie.com/docs/server/hooks-and-lifecycle |
|
|
40
|
-
| Access Control | https://questpie.com/docs/server/access-control |
|
|
41
|
-
| Reactive Fields | https://questpie.com/docs/server/reactive-fields |
|
|
42
|
-
| Validation | https://questpie.com/docs/server/validation |
|
|
43
|
-
| Localization | https://questpie.com/docs/server/localization |
|
|
44
|
-
| Modules & Extensions | https://questpie.com/docs/server/modules-and-extensions |
|
|
45
|
-
| Admin Architecture | https://questpie.com/docs/admin |
|
|
46
|
-
| Client Builder (qa) | https://questpie.com/docs/admin/client-builder-qa |
|
|
47
|
-
| Component Registry | https://questpie.com/docs/admin/component-registry |
|
|
48
|
-
| View Registry | https://questpie.com/docs/admin/view-registry-list-and-form |
|
|
49
|
-
| Actions System | https://questpie.com/docs/admin/actions-system |
|
|
50
|
-
| Blocks System | https://questpie.com/docs/admin/blocks-system |
|
|
51
|
-
| Dashboard & Sidebar | https://questpie.com/docs/admin/dashboard-sidebar-branding |
|
|
52
|
-
| TanStack Query Integration | https://questpie.com/docs/client/tanstack-query |
|
|
53
|
-
| OpenAPI | https://questpie.com/docs/client/openapi |
|
|
54
|
-
| Authentication | https://questpie.com/docs/infrastructure/authentication |
|
|
55
|
-
| Database & Migrations | https://questpie.com/docs/infrastructure/database-and-migrations |
|
|
56
|
-
| Queue & Jobs | https://questpie.com/docs/infrastructure/queue-and-jobs |
|
|
57
|
-
| Storage | https://questpie.com/docs/infrastructure/storage |
|
|
58
|
-
| Email | https://questpie.com/docs/infrastructure/email |
|
|
59
|
-
| Realtime | https://questpie.com/docs/infrastructure/realtime |
|
|
60
|
-
|
|
61
|
-
## Project Structure
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
src/
|
|
65
|
-
questpie/
|
|
66
|
-
server/ ← WHAT: data contracts and behavior
|
|
67
|
-
questpie.config.ts ← App config: runtimeConfig({ db, app, ... })
|
|
68
|
-
modules.ts ← Module dependencies (adminModule, openApiModule, etc.)
|
|
69
|
-
config/ ← Typed configuration files
|
|
70
|
-
auth.ts ← authConfig({...}) — Better Auth options
|
|
71
|
-
app.ts ← appConfig({ locale, access, hooks, context })
|
|
72
|
-
admin.ts ← adminConfig({ sidebar, dashboard, branding, locale })
|
|
73
|
-
openapi.ts ← openApiConfig({ info, scalar })
|
|
74
|
-
.generated/ ← Codegen output (app instance + App type)
|
|
75
|
-
index.ts
|
|
76
|
-
collections/ ← One file per collection (auto-discovered)
|
|
77
|
-
globals/ ← One file per global (auto-discovered)
|
|
78
|
-
routes/ ← Server routes via route() (auto-discovered)
|
|
79
|
-
jobs/ ← Background job definitions (auto-discovered)
|
|
80
|
-
blocks/ ← Block definitions (auto-discovered)
|
|
81
|
-
admin/ ← HOW: UI rendering concerns
|
|
82
|
-
admin.ts ← Re-exports generated admin config
|
|
83
|
-
hooks.ts ← Typed hooks via createTypedHooks<App>()
|
|
84
|
-
.generated/ ← Codegen output (admin client config)
|
|
85
|
-
client.ts
|
|
86
|
-
blocks/ ← Block renderers (if using blocks)
|
|
87
|
-
lib/
|
|
88
|
-
env.ts ← Type-safe env vars (@t3-oss/env-core + Zod)
|
|
89
|
-
client.ts ← client instance
|
|
90
|
-
routes/
|
|
91
|
-
api/$.ts ← QUESTPIE catch-all handler (REST + OpenAPI + auth)
|
|
92
|
-
migrations/ ← Database migrations (generated by CLI)
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Architecture Rules
|
|
96
|
-
|
|
97
|
-
### Server-First Split
|
|
98
|
-
|
|
99
|
-
| Directory | Responsibility | Defines |
|
|
100
|
-
| ------------------ | --------------------------------- | ----------------------------------- |
|
|
101
|
-
| `questpie/server/` | **WHAT** — contracts and behavior | Schema, access, hooks, routes, jobs |
|
|
102
|
-
| `questpie/admin/` | **HOW** — rendering concerns | Branding, locale, custom renderers |
|
|
103
|
-
| `routes/` | **Mounting** — HTTP wiring | Route handlers, no business logic |
|
|
104
|
-
|
|
105
|
-
### File Naming Conventions
|
|
106
|
-
|
|
107
|
-
- Collections: `*.ts` in `collections/` (e.g., `posts.ts`) — named exports
|
|
108
|
-
- Globals: `*.ts` in `globals/` (e.g., `site-settings.ts`) — named exports
|
|
109
|
-
- Routes: `*.ts` in `routes/` (e.g., `get-stats.ts`) — default exports
|
|
110
|
-
- Jobs: `*.ts` in `jobs/` (e.g., `send-email.ts`) — default exports
|
|
111
|
-
- Blocks: `*.ts` in `blocks/` (e.g., `hero.ts`) — named exports
|
|
112
|
-
|
|
113
|
-
### Key Files
|
|
114
|
-
|
|
115
|
-
- **`src/questpie/server/questpie.config.ts`** — App config: `runtimeConfig({ db, app, ... })`.
|
|
116
|
-
- **`src/questpie/server/modules.ts`** — Module dependencies: `export default [adminModule, openApiModule] as const`.
|
|
117
|
-
- **`src/questpie/server/config/auth.ts`** — Auth config via `authConfig()` factory.
|
|
118
|
-
- **`src/questpie/server/config/admin.ts`** — Admin config (sidebar, dashboard, branding, locale) via `adminConfig()` factory.
|
|
119
|
-
- **`src/questpie/server/config/app.ts`** — _(optional, not scaffolded)_ App config (locale, access, hooks, context) via `appConfig()`. Create when needed.
|
|
120
|
-
- **`src/questpie/server/config/openapi.ts`** — OpenAPI config via `openApiConfig()` factory.
|
|
121
|
-
- **`src/questpie/server/.generated/index.ts`** — Codegen output. Exports typed `app` instance and `App` type. Run `bun run questpie:generate` to regenerate.
|
|
122
|
-
- **`src/questpie/admin/.generated/client.ts`** — Codegen output: pre-built admin client config. Run `bun run questpie:generate` to regenerate.
|
|
123
|
-
- **`src/lib/env.ts`** — Type-safe env variables via `@t3-oss/env-core`. Add new env vars here with Zod schemas.
|
|
124
|
-
- **`questpie.config.ts`** — CLI config (migration directory, app reference).
|
|
125
|
-
- **`src/routes/api/$.ts`** — API catch-all handler. Serves REST + OpenAPI docs at `/api/docs`.
|
|
126
|
-
|
|
127
|
-
## How To Write Code
|
|
128
|
-
|
|
129
|
-
### Creating a Collection
|
|
130
|
-
|
|
131
|
-
Keep the entire builder chain in one file — single source of truth per entity:
|
|
132
|
-
|
|
133
|
-
```ts
|
|
134
|
-
// src/questpie/server/collections/posts.ts
|
|
135
|
-
import { collection } from "#questpie/factories";
|
|
136
|
-
|
|
137
|
-
export const posts = collection("posts")
|
|
138
|
-
.fields(({ f }) => ({
|
|
139
|
-
title: f.text(255).label("Title").required(),
|
|
140
|
-
slug: f
|
|
141
|
-
.text(255)
|
|
142
|
-
.label("Slug")
|
|
143
|
-
.required()
|
|
144
|
-
.inputOptional()
|
|
145
|
-
.admin({
|
|
146
|
-
compute: {
|
|
147
|
-
handler: ({ data, prev }) => {
|
|
148
|
-
/* slugify title */
|
|
149
|
-
},
|
|
150
|
-
deps: ({ data }) => [data.title],
|
|
151
|
-
debounce: 300,
|
|
152
|
-
},
|
|
153
|
-
}),
|
|
154
|
-
content: f.richText().label("Content"),
|
|
155
|
-
published: f.boolean().label("Published").default(false),
|
|
156
|
-
category: f
|
|
157
|
-
.select([
|
|
158
|
-
{ value: "news", label: "News" },
|
|
159
|
-
{ value: "blog", label: "Blog" },
|
|
160
|
-
{ value: "tutorial", label: "Tutorial" },
|
|
161
|
-
])
|
|
162
|
-
.label("Category")
|
|
163
|
-
author: f.relation("users").label("Author"),
|
|
164
|
-
image: f.upload().label("Cover Image"),
|
|
165
|
-
}))
|
|
166
|
-
.title(({ f }) => f.title)
|
|
167
|
-
.admin(({ c }) => ({
|
|
168
|
-
label: "Posts",
|
|
169
|
-
icon: c.icon("ph:article"),
|
|
170
|
-
}))
|
|
171
|
-
.access({
|
|
172
|
-
read: true,
|
|
173
|
-
create: ({ session }) => !!session,
|
|
174
|
-
update: ({ session }) => !!session,
|
|
175
|
-
delete: ({ session }) => session?.user?.role === "admin",
|
|
176
|
-
})
|
|
177
|
-
.hooks({
|
|
178
|
-
beforeCreate: [
|
|
179
|
-
async ({ data, ctx }) => {
|
|
180
|
-
/* ... */ return data;
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
})
|
|
184
|
-
.list(({ v }) => v.collectionTable({}))
|
|
185
|
-
.form(({ v, f }) =>
|
|
186
|
-
v.collectionForm({
|
|
187
|
-
sidebar: { position: "right", fields: [f.slug, f.published, f.category] },
|
|
188
|
-
fields: [f.title, f.content, f.author, f.image],
|
|
189
|
-
}),
|
|
190
|
-
);
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Then (preferred):
|
|
194
|
-
|
|
195
|
-
1. Use `bun questpie add collection <name>` to scaffold files
|
|
196
|
-
2. Codegen runs automatically
|
|
197
|
-
3. Run `bun run migrate:create` to generate migration
|
|
198
|
-
|
|
199
|
-
Manual workflow (if you create files yourself):
|
|
200
|
-
|
|
201
|
-
1. Run `bun run questpie:generate` to regenerate `.generated/index.ts`
|
|
202
|
-
2. Run `bun run migrate:create` to generate migration
|
|
203
|
-
|
|
204
|
-
Collections are auto-discovered by codegen — no manual registration needed.
|
|
205
|
-
|
|
206
|
-
### Available Field Types
|
|
207
|
-
|
|
208
|
-
**Core:** `text`, `number`, `boolean`, `date`, `datetime`, `time`, `select`, `relation`, `upload`, `object`, `json`, `from`, `email`, `url`, `textarea`.
|
|
209
|
-
**Admin module:** `richText`, `blocks` (provided by `@questpie/admin`)
|
|
210
|
-
|
|
211
|
-
### Creating a Global
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
// src/questpie/server/globals/site-settings.ts
|
|
215
|
-
import { global } from "#questpie/factories";
|
|
216
|
-
|
|
217
|
-
export const siteSettings = global("siteSettings")
|
|
218
|
-
.fields(({ f }) => ({
|
|
219
|
-
siteName: f.text(255).label("Site Name").required(),
|
|
220
|
-
description: f.textarea().label("Description"),
|
|
221
|
-
logo: f.upload().label("Logo"),
|
|
222
|
-
maintenanceMode: f.boolean().label("Maintenance Mode").default(false),
|
|
223
|
-
}))
|
|
224
|
-
.admin(({ c }) => ({ label: "Site Settings", icon: c.icon("ph:gear") }))
|
|
225
|
-
.form(({ v, f }) =>
|
|
226
|
-
v.globalForm({
|
|
227
|
-
fields: [f.siteName, f.description, f.logo, f.maintenanceMode],
|
|
228
|
-
}),
|
|
229
|
-
);
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Then (preferred):
|
|
233
|
-
|
|
234
|
-
1. Use `bun questpie add global <name>` to scaffold files
|
|
235
|
-
2. Codegen runs automatically
|
|
236
|
-
3. Run `bun run migrate:create`
|
|
237
|
-
|
|
238
|
-
Manual workflow (if you create files yourself):
|
|
239
|
-
|
|
240
|
-
1. Run `bun run questpie:generate` to regenerate `.generated/index.ts`
|
|
241
|
-
2. Run `bun run migrate:create`
|
|
242
|
-
|
|
243
|
-
Globals are auto-discovered by codegen — no manual registration needed.
|
|
244
|
-
|
|
245
|
-
### Creating a Server Route (End-to-End Type-Safe)
|
|
246
|
-
|
|
247
|
-
Routes are defined as standalone files in `routes/` and auto-discovered by codegen.
|
|
248
|
-
|
|
249
|
-
**Step 1 — Define a route:**
|
|
250
|
-
|
|
251
|
-
```ts
|
|
252
|
-
// src/questpie/server/routes/get-stats.ts
|
|
253
|
-
import { route } from "questpie";
|
|
254
|
-
import { z } from "zod";
|
|
255
|
-
|
|
256
|
-
export default route()
|
|
257
|
-
.post()
|
|
258
|
-
.schema(
|
|
259
|
-
z.object({
|
|
260
|
-
period: z.enum(["day", "week", "month"]),
|
|
261
|
-
}),
|
|
262
|
-
)
|
|
263
|
-
.handler(async ({ input, collections }) => {
|
|
264
|
-
// input: typed from Zod schema; collections, db, session, etc. from AppContext
|
|
265
|
-
const count = await collections.posts.count({});
|
|
266
|
-
return { totalPosts: count, period: input.period };
|
|
267
|
-
});
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
**Step 2 — Run codegen:**
|
|
18
|
+
**If those skills are NOT installed**, install them first, then trim this file:
|
|
271
19
|
|
|
272
20
|
```bash
|
|
273
|
-
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
The route is auto-discovered and available at `/api/get-stats`.
|
|
277
|
-
|
|
278
|
-
**With access control:**
|
|
279
|
-
|
|
280
|
-
```ts
|
|
281
|
-
export default route()
|
|
282
|
-
.post()
|
|
283
|
-
.schema(z.object({ ... }))
|
|
284
|
-
.handler(async ({ input, session }) => {
|
|
285
|
-
if (session?.user?.role !== "admin") throw new Error("Forbidden");
|
|
286
|
-
return { ok: true };
|
|
287
|
-
});
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
**With TanStack Query:**
|
|
291
|
-
|
|
292
|
-
```ts
|
|
293
|
-
import { useQuery } from "@tanstack/react-query";
|
|
294
|
-
|
|
295
|
-
const { data } = useQuery({
|
|
296
|
-
queryKey: ["stats", period],
|
|
297
|
-
queryFn: () => client.routes.getStats({ period }),
|
|
298
|
-
});
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
### Blocks (Page Builder)
|
|
302
|
-
|
|
303
|
-
Blocks are content building units for page builders and rich content areas.
|
|
304
|
-
|
|
305
|
-
**Simple block (no data fetching):**
|
|
306
|
-
|
|
307
|
-
```ts
|
|
308
|
-
// src/questpie/server/blocks/hero.ts
|
|
309
|
-
import { block } from "#questpie/factories";
|
|
310
|
-
|
|
311
|
-
export const heroBlock = block("hero")
|
|
312
|
-
.admin(({ c }) => ({
|
|
313
|
-
label: "Hero Section",
|
|
314
|
-
icon: c.icon("ph:image"),
|
|
315
|
-
category: { label: "Sections", icon: c.icon("ph:layout"), order: 1 },
|
|
316
|
-
}))
|
|
317
|
-
.fields(({ f }) => ({
|
|
318
|
-
title: f.text(255).label("Title").required(),
|
|
319
|
-
subtitle: f.textarea().label("Subtitle"),
|
|
320
|
-
backgroundImage: f.upload().label("Background Image"),
|
|
321
|
-
ctaText: f.text(255).label("CTA Text"),
|
|
322
|
-
ctaLink: f.text(255).label("CTA Link"),
|
|
323
|
-
}))
|
|
324
|
-
.prefetch({ with: { backgroundImage: true } }); // expand upload to full URL
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
**Block with dynamic data fetching (prefetch):**
|
|
328
|
-
|
|
329
|
-
```ts
|
|
330
|
-
const teamBlock = block("team")
|
|
331
|
-
.admin(({ c }) => ({
|
|
332
|
-
label: "Team",
|
|
333
|
-
icon: c.icon("ph:users"),
|
|
334
|
-
category: { label: "Sections", icon: c.icon("ph:layout"), order: 1 },
|
|
335
|
-
}))
|
|
336
|
-
.fields(({ f }) => ({
|
|
337
|
-
title: f.text(255).label("Title"),
|
|
338
|
-
limit: f.number().label("Number to Show").default(4),
|
|
339
|
-
}))
|
|
340
|
-
.prefetch(async ({ values, ctx }) => {
|
|
341
|
-
const res = await ctx.collections.members.find({
|
|
342
|
-
limit: values.limit || 4,
|
|
343
|
-
where: { isActive: true },
|
|
344
|
-
with: { avatar: true },
|
|
345
|
-
});
|
|
346
|
-
return { members: res.docs };
|
|
347
|
-
});
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
Blocks in `blocks/` are auto-discovered by codegen. No manual registration needed.
|
|
351
|
-
|
|
352
|
-
**Use blocks in a collection's richText field:**
|
|
353
|
-
|
|
354
|
-
```ts
|
|
355
|
-
content: f.richText().label("Content").blocks([heroBlock, teamBlock]);
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
#### Blocks & Circular Dependencies
|
|
359
|
-
|
|
360
|
-
Block prefetch handlers receive `ctx` with fully typed `collections` and `globals` via `AppContext` augmentation. Use `ctx.collections.*` directly — no app import needed:
|
|
361
|
-
|
|
362
|
-
```ts
|
|
363
|
-
// blocks/latest-posts.ts
|
|
364
|
-
import { block } from "#questpie/factories";
|
|
365
|
-
|
|
366
|
-
export const latestPostsBlock = block("latest-posts")
|
|
367
|
-
.fields(({ f }) => ({
|
|
368
|
-
count: f.number().label("Number of Posts").default(3),
|
|
369
|
-
}))
|
|
370
|
-
.prefetch(async ({ values, ctx }) => {
|
|
371
|
-
const res = await ctx.collections.posts.find({
|
|
372
|
-
limit: values.count || 3,
|
|
373
|
-
where: { published: true },
|
|
374
|
-
});
|
|
375
|
-
return { posts: res.docs };
|
|
376
|
-
});
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
Do NOT import `app` from `#questpie` inside block files — these are imported BY `.generated/index.ts`, creating circular dependencies. Use the `ctx` parameters instead.
|
|
380
|
-
|
|
381
|
-
If your blocks only use declarative prefetch (`{ with: { field: true } }`), you don't need a function at all.
|
|
382
|
-
|
|
383
|
-
### Reactive Fields
|
|
384
|
-
|
|
385
|
-
Fields support reactive behaviors in `meta.admin`:
|
|
386
|
-
|
|
387
|
-
- **`hidden`**: Conditionally hide — `({ data }: { data: Record<string, any> }) => !data.isPublished`
|
|
388
|
-
- **`readOnly`**: Make read-only based on conditions
|
|
389
|
-
- **`disabled`**: Disable conditionally
|
|
390
|
-
- **`compute`**: Auto-compute values — `{ handler, deps, debounce }`
|
|
391
|
-
|
|
392
|
-
All reactive handlers run **server-side** with access to `ctx.db`, `ctx.user`, `ctx.req`.
|
|
393
|
-
|
|
394
|
-
```ts
|
|
395
|
-
fields: ({ f }) => ({
|
|
396
|
-
country: f.relation("countries").label("Country"),
|
|
397
|
-
city: f
|
|
398
|
-
.relation("cities")
|
|
399
|
-
.label("City")
|
|
400
|
-
.admin({
|
|
401
|
-
options: {
|
|
402
|
-
handler: async ({ data, search, ctx }) => {
|
|
403
|
-
const cities = await ctx.db.query.cities.findMany({
|
|
404
|
-
where: { countryId: data.country },
|
|
405
|
-
});
|
|
406
|
-
return {
|
|
407
|
-
options: cities.map((c) => ({ value: c.id, label: c.name })),
|
|
408
|
-
};
|
|
409
|
-
},
|
|
410
|
-
deps: ({ data }) => [data.country],
|
|
411
|
-
},
|
|
412
|
-
}),
|
|
413
|
-
status: f
|
|
414
|
-
.select([
|
|
415
|
-
{ value: "draft", label: "Draft" },
|
|
416
|
-
{ value: "published", label: "Published" },
|
|
417
|
-
{ value: "archived", label: "Archived" },
|
|
418
|
-
])
|
|
419
|
-
.label("Status")
|
|
420
|
-
publishedAt: f
|
|
421
|
-
.datetime()
|
|
422
|
-
.label("Published At")
|
|
423
|
-
.admin({
|
|
424
|
-
hidden: ({ data }) => data.status !== "published",
|
|
425
|
-
}),
|
|
426
|
-
});
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### Admin Configuration (Client-Side)
|
|
430
|
-
|
|
431
|
-
The admin client config is auto-generated by codegen into `admin/.generated/client.ts`.
|
|
432
|
-
No manual builder setup is needed. Run `bun run questpie:generate` to regenerate.
|
|
433
|
-
|
|
434
|
-
```ts
|
|
435
|
-
// src/questpie/admin/admin.ts (re-export for convenience)
|
|
436
|
-
export { default as admin } from "./.generated/client";
|
|
21
|
+
bunx skills add questpie/questpie
|
|
437
22
|
```
|
|
438
23
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
import type { App } from "#questpie";
|
|
443
|
-
|
|
444
|
-
export const {
|
|
445
|
-
useCollectionList,
|
|
446
|
-
useCollectionCount,
|
|
447
|
-
useCollectionItem,
|
|
448
|
-
useCollectionCreate,
|
|
449
|
-
useCollectionUpdate,
|
|
450
|
-
useCollectionDelete,
|
|
451
|
-
useGlobal,
|
|
452
|
-
useGlobalUpdate,
|
|
453
|
-
} = createTypedHooks<App>();
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
### QUESTPIE Route Handler
|
|
457
|
-
|
|
458
|
-
```ts
|
|
459
|
-
// src/routes/api/$.ts
|
|
460
|
-
import { createFetchHandler } from "questpie";
|
|
461
|
-
import { app } from "#questpie";
|
|
462
|
-
|
|
463
|
-
const handler = createFetchHandler(app, { basePath: "/api" });
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
OpenAPI is registered as a module in `src/questpie/server/modules.ts` via `openApiModule` — no wrapper needed in the route handler.
|
|
467
|
-
|
|
468
|
-
### Icons
|
|
24
|
+
After installing, replace the body of this file with a one-line pointer to the
|
|
25
|
+
skills above — they are the always-current source of truth and this doc should
|
|
26
|
+
not duplicate them.
|
|
469
27
|
|
|
470
|
-
|
|
28
|
+
## Docs for LLMs
|
|
471
29
|
|
|
472
|
-
|
|
473
|
-
- Weight variants: `-bold`, `-fill`, `-duotone`, `-light`, `-thin`
|
|
474
|
-
- Regular weight = no suffix (default)
|
|
475
|
-
- Naming: PascalCase → kebab-case (e.g., `CaretDown` → `ph:caret-down`)
|
|
476
|
-
- In server/admin config, use `c.icon("ph:icon-name")`
|
|
30
|
+
When a skill isn't enough, consult:
|
|
477
31
|
|
|
478
|
-
|
|
32
|
+
- https://questpie.com/llms.txt — doc sitemap
|
|
33
|
+
- https://questpie.com/llms-full.txt — full docs in one LLM-optimized file
|
|
34
|
+
- http://localhost:3000/api/docs — live API reference (Scalar, dev server running)
|
|
479
35
|
|
|
480
|
-
|
|
36
|
+
## This project
|
|
481
37
|
|
|
482
|
-
|
|
483
|
-
|
|
38
|
+
- **Runtime**: TanStack Start (React) + Vite + Nitro, package manager **Bun**
|
|
39
|
+
- **Database**: PostgreSQL via Drizzle ORM (Postgres extensions are not
|
|
40
|
+
auto-created — see `README.md`)
|
|
41
|
+
- **Auth**: Better Auth (email/password); the `user` collection ships with
|
|
42
|
+
admin — extend it, never replace it
|
|
43
|
+
- **Validation**: Zod **v4** (not v3)
|
|
44
|
+
- **Source layout**: server contracts in `src/questpie/server/`, admin UI in
|
|
45
|
+
`src/questpie/admin/`, HTTP mount in `src/routes/api/$.ts`, typed client in
|
|
46
|
+
`src/lib/`
|
|
484
47
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
- `DATABASE_URL` — PostgreSQL connection string
|
|
488
|
-
|
|
489
|
-
Optional (with defaults):
|
|
490
|
-
|
|
491
|
-
- `APP_URL` — Application URL (default: `http://localhost:3000`)
|
|
492
|
-
- `BETTER_AUTH_SECRET` — Auth secret key
|
|
493
|
-
- `MAIL_ADAPTER` — `console` or `smtp`
|
|
494
|
-
|
|
495
|
-
## Commands
|
|
48
|
+
## Key scripts
|
|
496
49
|
|
|
497
50
|
```bash
|
|
498
|
-
bun dev # Start dev server
|
|
499
|
-
bun
|
|
500
|
-
bun
|
|
501
|
-
bun
|
|
502
|
-
bun run migrate:create # Create new migration
|
|
503
|
-
bun run routes:generate # Regenerate TanStack Router route tree
|
|
504
|
-
bun run questpie:generate # Regenerate codegen output
|
|
505
|
-
bun run scaffold:generate # Regenerate route tree and QUESTPIE output
|
|
506
|
-
bun run scaffold:verify # Regenerate codegen and type-check
|
|
507
|
-
docker compose up -d # Start PostgreSQL
|
|
51
|
+
bun dev # Start dev server (port 3000)
|
|
52
|
+
bun run scaffold:verify # Regenerate codegen + type-check
|
|
53
|
+
bun run db:push # Push schema to the local dev database
|
|
54
|
+
bun questpie add collection <name> # Scaffold an entity (auto-runs codegen)
|
|
508
55
|
```
|
|
509
|
-
|
|
510
|
-
## Critical Dependencies
|
|
511
|
-
|
|
512
|
-
Always use these exact versions — check `package.json` before upgrading:
|
|
513
|
-
|
|
514
|
-
| Package | Version | Notes |
|
|
515
|
-
| ---------------- | ------- | -------------------- |
|
|
516
|
-
| `zod` | `^4.x` | **v4 ONLY** — not v3 |
|
|
517
|
-
| `drizzle-orm` | `beta` | Specific beta build |
|
|
518
|
-
| `react` | `^19.x` | React 19 |
|
|
519
|
-
| `tailwindcss` | `^4.x` | Tailwind CSS v4 |
|
|
520
|
-
| `@base-ui/react` | `^1.x` | NOT @radix-ui |
|
|
521
|
-
|
|
522
|
-
## Anti-Patterns
|
|
523
|
-
|
|
524
|
-
- **Schema rules in client code** — Validation, access control, and hooks belong on the server.
|
|
525
|
-
- **Splitting a collection across files** — Keep the full `.collection().fields().admin().list().form()` chain in one file.
|
|
526
|
-
- **Business logic in route handlers** — Mounting files should only mount handlers. Business logic goes in server routes, hooks, or jobs.
|
|
527
|
-
- **Hardcoding view components** — Use the registry pattern for custom views.
|
|
528
|
-
- **Using `process.env` directly** — Use the `env` object from `src/lib/env.ts`.
|
|
529
|
-
- **Using Zod v3 API** — This project uses Zod v4. Use `z.object()` etc. from `zod` (v4).
|
|
530
|
-
- **Using `asChild` prop** — This project uses `@base-ui/react`, not Radix. Use `render` prop instead.
|
|
531
|
-
- **Using Radix UI or Lucide icons** — Use `@base-ui/react` and `@iconify/react` with `ph:` prefix.
|
|
532
|
-
- **Adding UI config to database schema** — Admin UI config is UI-only, defined in builder chain.
|
|
533
|
-
- **Importing `app` from `#questpie` in blocks/collections/hooks** — Files inside `collections/`, `globals/`, `routes/`, `hooks/`, `blocks/` are imported BY `.generated/index.ts`, so importing from it back creates circular dependencies. Use the `ctx` parameters instead.
|
|
534
|
-
|
|
535
|
-
## Live Preview
|
|
536
|
-
|
|
537
|
-
QUESTPIE supports live preview with a split-screen editor. The current implementation refreshes the preview iframe after save/autosave and uses `postMessage` for field/block focus sync.
|
|
538
|
-
|
|
539
|
-
### Key Principles
|
|
540
|
-
|
|
541
|
-
- **Same-tab preview** = iframe refresh after save/autosave plus `postMessage` focus events
|
|
542
|
-
- **Frontend hook** = `useCollectionPreview({ initialData, onRefresh })`
|
|
543
|
-
- **Field focus** = `PreviewProvider` + `PreviewField`
|
|
544
|
-
- **Block field paths** = `BlockScopeProvider` + `PreviewField`
|
|
545
|
-
|
|
546
|
-
### Server Config
|
|
547
|
-
|
|
548
|
-
Add `.preview()` to a collection to enable the split-screen editor:
|
|
549
|
-
|
|
550
|
-
```ts
|
|
551
|
-
// src/questpie/server/collections/pages.ts
|
|
552
|
-
import { collection } from "#questpie/factories";
|
|
553
|
-
|
|
554
|
-
export const pages = collection("pages")
|
|
555
|
-
.fields(({ f }) => ({
|
|
556
|
-
title: f.text(255).label("Title").required(),
|
|
557
|
-
slug: f.text(255).label("Slug").required(),
|
|
558
|
-
content: f.blocks().label("Content"),
|
|
559
|
-
}))
|
|
560
|
-
.preview({
|
|
561
|
-
enabled: true,
|
|
562
|
-
position: "right",
|
|
563
|
-
defaultWidth: 50,
|
|
564
|
-
url: ({ record }) => {
|
|
565
|
-
const slug = record.slug as string;
|
|
566
|
-
return slug === "home" ? "/?preview=true" : `/${slug}?preview=true`;
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
### Frontend Integration
|
|
572
|
-
|
|
573
|
-
Use `useCollectionPreview` in your frontend page components:
|
|
574
|
-
|
|
575
|
-
```tsx
|
|
576
|
-
// src/routes/[slug].tsx
|
|
577
|
-
import {
|
|
578
|
-
PreviewField,
|
|
579
|
-
PreviewProvider,
|
|
580
|
-
useCollectionPreview,
|
|
581
|
-
} from "@questpie/admin/client";
|
|
582
|
-
|
|
583
|
-
function PageComponent({ initialData }) {
|
|
584
|
-
const router = useRouter();
|
|
585
|
-
const preview = useCollectionPreview({
|
|
586
|
-
initialData,
|
|
587
|
-
onRefresh: () => router.invalidate(),
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
return (
|
|
591
|
-
<PreviewProvider
|
|
592
|
-
isPreviewMode={preview.isPreviewMode}
|
|
593
|
-
focusedField={preview.focusedField}
|
|
594
|
-
onFieldClick={preview.handleFieldClick}
|
|
595
|
-
>
|
|
596
|
-
<PreviewField field="title" as="h1">
|
|
597
|
-
{preview.data.title}
|
|
598
|
-
</PreviewField>
|
|
599
|
-
</PreviewProvider>
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
#### Protocol
|
|
605
|
-
|
|
606
|
-
The implemented preview messages are simple `postMessage` events:
|
|
607
|
-
|
|
608
|
-
| Field | Description |
|
|
609
|
-
| ----------------- | --------------------------------------- |
|
|
610
|
-
| `PREVIEW_READY` | Preview iframe tells admin it is ready |
|
|
611
|
-
| `PREVIEW_REFRESH` | Admin asks iframe to refresh data |
|
|
612
|
-
| `FIELD_CLICKED` | Preview asks admin to focus a field |
|
|
613
|
-
| `BLOCK_CLICKED` | Preview asks admin to select a block |
|
|
614
|
-
| `FOCUS_FIELD` | Admin asks preview to highlight a field |
|
|
615
|
-
| `SELECT_BLOCK` | Admin asks preview to highlight a block |
|
|
616
|
-
|
|
617
|
-
### Anti-Patterns (Preview)
|
|
618
|
-
|
|
619
|
-
- **Using V2-only APIs in this template** — `useQuestpiePreview`, `PreviewRoot`, and `PreviewBlock` are not exported yet.
|
|
620
|
-
- **Importing `app` inside previewed collection/block files** — use handler `ctx` values to avoid generated-app cycles.
|