create-questpie 2.0.0 → 2.0.2

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 (45) hide show
  1. package/README.md +10 -6
  2. package/dist/index.mjs +140 -25
  3. package/package.json +5 -3
  4. package/skills/questpie/AGENTS.md +2664 -0
  5. package/skills/questpie/SKILL.md +181 -0
  6. package/skills/questpie/references/auth.md +121 -0
  7. package/skills/questpie/references/business-logic.md +550 -0
  8. package/skills/questpie/references/codegen-plugin-api.md +382 -0
  9. package/skills/questpie/references/crud-api.md +378 -0
  10. package/skills/questpie/references/data-modeling.md +489 -0
  11. package/skills/questpie/references/extend.md +493 -0
  12. package/skills/questpie/references/field-types.md +386 -0
  13. package/skills/questpie/references/infrastructure-adapters.md +545 -0
  14. package/skills/questpie/references/multi-tenancy.md +364 -0
  15. package/skills/questpie/references/production.md +475 -0
  16. package/skills/questpie/references/query-operators.md +125 -0
  17. package/skills/questpie/references/quickstart.md +549 -0
  18. package/skills/questpie/references/rules.md +327 -0
  19. package/skills/questpie/references/tanstack-query.md +520 -0
  20. package/skills/questpie-admin/AGENTS.md +1442 -0
  21. package/skills/questpie-admin/SKILL.md +410 -0
  22. package/skills/questpie-admin/references/blocks.md +307 -0
  23. package/skills/questpie-admin/references/custom-ui.md +305 -0
  24. package/skills/questpie-admin/references/views.md +433 -0
  25. package/templates/tanstack-start/AGENTS.md +71 -62
  26. package/templates/tanstack-start/CLAUDE.md +26 -23
  27. package/templates/tanstack-start/README.md +32 -20
  28. package/templates/tanstack-start/env.example +1 -1
  29. package/templates/tanstack-start/package.json +20 -6
  30. package/templates/tanstack-start/src/lib/client.ts +2 -2
  31. package/templates/tanstack-start/src/lib/env.ts +1 -1
  32. package/templates/tanstack-start/src/questpie/admin/.generated/client.ts +13 -0
  33. package/templates/tanstack-start/src/questpie/admin/modules.ts +1 -0
  34. package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +117 -241
  35. package/templates/tanstack-start/src/questpie/server/.generated/index.ts +129 -81
  36. package/templates/tanstack-start/src/questpie/server/app.ts +1 -1
  37. package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
  38. package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +1 -1
  39. package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
  40. package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
  41. package/templates/tanstack-start/src/routes/__root.tsx +0 -2
  42. package/templates/tanstack-start/src/routes/admin.tsx +8 -1
  43. package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
  44. package/templates/tanstack-start/src/vite-env.d.ts +1 -0
  45. package/templates/tanstack-start/vite.config.ts +1 -3
@@ -0,0 +1,549 @@
1
+ ---
2
+ name: questpie-quickstart
3
+ description: >
4
+ End-to-end getting started with QUESTPIE — from scaffolding to production.
5
+ Load when starting a new project, onboarding, or following the happy path
6
+ from zero to a running app with collections, admin panel, and migrations.
7
+ ---
8
+
9
+ # QUESTPIE Quickstart
10
+
11
+ Complete lifecycle guide: scaffold, define data, generate, migrate, serve, deploy.
12
+
13
+ ---
14
+
15
+ ## 1. Scaffold a New Project
16
+
17
+ ```bash
18
+ bun create questpie my-app
19
+ cd my-app
20
+ ```
21
+
22
+ Options:
23
+
24
+ - `-t, --template <name>` — template to use (default: `tanstack-start`)
25
+ - `--no-install` — skip `bun install`
26
+ - `--no-git` — skip git init
27
+
28
+ After scaffolding:
29
+
30
+ ```bash
31
+ cp .env.example .env # Set DATABASE_URL, APP_URL, APP_SECRET
32
+ ```
33
+
34
+ ### Required Environment Variables
35
+
36
+ | Variable | Required | Description |
37
+ | -------------- | -------- | ---------------------------- |
38
+ | `DATABASE_URL` | Yes | PostgreSQL connection string |
39
+ | `APP_URL` | Yes | Public URL of the app |
40
+ | `APP_SECRET` | Yes | Secret for auth sessions |
41
+ | `SMTP_HOST` | No | Email SMTP host |
42
+ | `SMTP_PORT` | No | Email SMTP port |
43
+
44
+ ---
45
+
46
+ ## 2. Project Structure
47
+
48
+ ```text
49
+ my-app/
50
+ ├── questpie.config.ts # CLI config (re-exports server config)
51
+ ├── src/
52
+ │ ├── questpie/
53
+ │ │ ├── server/
54
+ │ │ │ ├── questpie.config.ts # Runtime config (DB, adapters, secrets)
55
+ │ │ │ ├── modules.ts # Module dependencies
56
+ │ │ │ ├── config/ # Typed configuration files
57
+ │ │ │ │ ├── auth.ts # authConfig({...}) — Better Auth options
58
+ │ │ │ │ ├── app.ts # appConfig({ locale, access, hooks, context })
59
+ │ │ │ │ ├── admin.ts # adminConfig({ sidebar, dashboard, branding, locale })
60
+ │ │ │ │ └── openapi.ts # openApiConfig({ info, scalar })
61
+ │ │ │ ├── collections/ # One file per collection
62
+ │ │ │ ├── globals/ # One file per global
63
+ │ │ │ ├── routes/ # App routes (JSON or raw)
64
+ │ │ │ ├── jobs/ # Background tasks
65
+ │ │ │ ├── services/ # Singleton services
66
+ │ │ │ ├── blocks/ # Content blocks (admin plugin)
67
+ │ │ │ ├── emails/ # Email templates
68
+ │ │ │ └── .generated/ # Codegen output (NEVER edit)
69
+ │ │ └── admin/ # Admin client customizations
70
+ │ │ ├── .generated/ # Codegen output (NEVER edit)
71
+ │ │ └── blocks/ # Block renderers (React)
72
+ │ ├── lib/
73
+ │ │ └── client.ts # Typed client SDK
74
+ │ └── routes/
75
+ │ └── api/
76
+ │ └── $.ts # API catch-all handler
77
+ ```
78
+
79
+ ### Discovery Rules
80
+
81
+ Codegen discovers files by **directory name** and **export pattern**:
82
+
83
+ | Directory | Key derivation | Example |
84
+ | --------------- | -------------------------- | ------------------------------------------ |
85
+ | `collections/` | Factory arg to camelCase | `collection("blog-posts")` -> `blogPosts` |
86
+ | `globals/` | Factory arg to camelCase | `global("siteSettings")` -> `siteSettings` |
87
+ | `routes/` | Filename to camelCase/path | `create-booking.ts` -> `createBooking` |
88
+ | `jobs/` | Filename to camelCase | `send-email.ts` -> `sendEmail` |
89
+ | `routes/` (raw) | Filename to path | `webhook.ts` -> `webhook` |
90
+ | `services/` | Filename to camelCase | `blog.ts` -> `blog` |
91
+ | `blocks/` | Factory arg/export name | `block("hero")` -> `hero` |
92
+ | `emails/` | Filename to camelCase | `welcome.ts` -> `welcome` |
93
+
94
+ Routes support nested directories for namespacing (`routes/booking/create.ts` -> `client.routes.booking.create()`).
95
+
96
+ Only hyphens are camelized in factory args; underscores are preserved (`global("site_settings")` -> `site_settings`).
97
+
98
+ ---
99
+
100
+ ## 3. Runtime Config
101
+
102
+ ```ts
103
+ // src/questpie/server/questpie.config.ts
104
+ import { runtimeConfig } from "questpie";
105
+
106
+ export default runtimeConfig({
107
+ app: {
108
+ url: process.env.APP_URL || "http://localhost:3000",
109
+ },
110
+ db: {
111
+ url: process.env.DATABASE_URL || "postgres://localhost/myapp",
112
+ },
113
+ secret: process.env.APP_SECRET || "change-me-in-production",
114
+ });
115
+ ```
116
+
117
+ The CLI config at the project root re-exports the server config:
118
+
119
+ ```ts
120
+ // questpie.config.ts (project root)
121
+ export { default } from "./src/questpie/server/questpie.config";
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 4. Define a Collection
127
+
128
+ ```ts
129
+ // src/questpie/server/collections/tasks.ts
130
+ import { collection } from "#questpie/factories";
131
+
132
+ export default collection("tasks").fields(({ f }) => ({
133
+ title: f.text(255).required(),
134
+ description: f.textarea(),
135
+ priority: f
136
+ .select([
137
+ { value: "low", label: "Low" },
138
+ { value: "medium", label: "Medium" },
139
+ { value: "high", label: "High" },
140
+ ])
141
+ .default("medium")
142
+ .required(),
143
+ dueDate: f.date(),
144
+ completed: f.boolean().default(false).required(),
145
+ }));
146
+ ```
147
+
148
+ This creates:
149
+
150
+ - A `tasks` database table with typed columns
151
+ - CRUD API endpoints at `/api/tasks`
152
+ - Zod validation for create/update
153
+ - Type-safe query operators for `where`, `orderBy`
154
+
155
+ ### Built-in Field Types
156
+
157
+ Core: `text`, `number`, `boolean`, `date`, `datetime`, `time`, `select`, `relation`, `upload`, `object`, `json`, `email`, `url`, `textarea`. Admin module fields: `richText`, `blocks`.
158
+
159
+ ---
160
+
161
+ ## 5. Add a Route
162
+
163
+ ```ts
164
+ // src/questpie/server/routes/get-overdue-tasks.ts
165
+ import { route } from "questpie";
166
+ import z from "zod";
167
+
168
+ export default route()
169
+ .post()
170
+ .schema(z.object({}))
171
+ .handler(async ({ collections }) => {
172
+ return await collections.tasks.find({
173
+ where: {
174
+ completed: false,
175
+ dueDate: { lt: new Date() },
176
+ },
177
+ orderBy: { dueDate: "asc" },
178
+ });
179
+ });
180
+ ```
181
+
182
+ Typed JSON routes are exposed as flat endpoints at `/api/<route-path>`.
183
+
184
+ ---
185
+
186
+ ## 6. Run Codegen
187
+
188
+ ```bash
189
+ bunx questpie generate
190
+ ```
191
+
192
+ This scans your file convention directories and generates:
193
+
194
+ - `src/questpie/server/.generated/index.ts` — `app` instance, `AppConfig` type
195
+ - `src/questpie/server/.generated/module.ts` — merged module with all discovered entities
196
+ - Module augmentation for `AppContext` (typed `collections`, `queue`, `email` in every handler)
197
+
198
+ Use `#questpie/factories` in collection, global, and block files (they need codegen-generated types). Routes, jobs, services, emails use `"questpie"` directly. Use `#questpie` for the generated app/runtime exports.
199
+
200
+ **Run codegen again every time you add, rename, or remove a file in a convention directory.**
201
+
202
+ ---
203
+
204
+ ## 7. Push Schema / Run Migrations
205
+
206
+ ### Development (quick iteration)
207
+
208
+ ```bash
209
+ bunx questpie push
210
+ ```
211
+
212
+ Syncs your Drizzle schema directly to the database. No migration files created. Use this during development only.
213
+
214
+ ### Production (migration files)
215
+
216
+ ```bash
217
+ # Generate a migration from schema diff
218
+ bunx questpie migrate:generate
219
+
220
+ # Run pending migrations
221
+ bunx questpie migrate:up
222
+
223
+ # Rollback last migration
224
+ bunx questpie migrate:down
225
+
226
+ # Reset all migrations
227
+ bunx questpie migrate:reset
228
+
229
+ # Reset + run all (fresh start)
230
+ bunx questpie migrate:fresh
231
+ ```
232
+
233
+ ---
234
+
235
+ ## 8. Wire Up the HTTP Handler
236
+
237
+ ### TanStack Start (default template)
238
+
239
+ ```ts
240
+ // src/routes/api/$.ts
241
+ import { createAPIFileRoute } from "@tanstack/react-start/api";
242
+ import { createFetchHandler } from "questpie";
243
+ import { app } from "#questpie";
244
+
245
+ const handler = createFetchHandler(app, { basePath: "/api" });
246
+
247
+ export const Route = createAPIFileRoute("/api/$")({
248
+ GET: ({ request }) => handler(request),
249
+ POST: ({ request }) => handler(request),
250
+ PATCH: ({ request }) => handler(request),
251
+ DELETE: ({ request }) => handler(request),
252
+ });
253
+ ```
254
+
255
+ ### Hono
256
+
257
+ ```ts
258
+ import { questpieHono } from "@questpie/hono/server";
259
+ import { Hono } from "hono";
260
+ import { app } from "#questpie";
261
+
262
+ export default new Hono().route("/api", questpieHono(app));
263
+ ```
264
+
265
+ ### Available Adapters
266
+
267
+ | Adapter | Package | Use case |
268
+ | -------------- | ------------------ | --------------------- |
269
+ | Hono | `@questpie/hono` | General purpose, fast |
270
+ | Elysia | `@questpie/elysia` | Bun-native |
271
+ | Next.js | `@questpie/next` | Next.js API routes |
272
+ | TanStack Start | (built-in) | Generic fetch handler |
273
+
274
+ ---
275
+
276
+ ## 9. Add the Admin Panel
277
+
278
+ ### Install
279
+
280
+ ```bash
281
+ bun add @questpie/admin
282
+ ```
283
+
284
+ ### Register the admin module
285
+
286
+ ```ts
287
+ // src/questpie/server/modules.ts
288
+ import { adminModule } from "@questpie/admin/server";
289
+
290
+ export default [adminModule] as const;
291
+ ```
292
+
293
+ ### Re-run codegen
294
+
295
+ ```bash
296
+ bunx questpie generate
297
+ ```
298
+
299
+ This picks up admin conventions (`config/admin.ts`, blocks, views, components) and generates `admin/.generated/client.ts`.
300
+
301
+ Navigate to `/admin` to see the admin panel with your collections.
302
+
303
+ ---
304
+
305
+ ## 10. Typed Client SDK
306
+
307
+ ```ts
308
+ // src/lib/client.ts
309
+ import { createClient } from "questpie/client";
310
+ import type { AppConfig } from "#questpie";
311
+
312
+ export const client = createClient<AppConfig>({
313
+ baseURL: "http://localhost:3000",
314
+ basePath: "/api",
315
+ });
316
+ ```
317
+
318
+ Usage with full type inference:
319
+
320
+ ```ts
321
+ // Typed collection queries — autocomplete on field names and operators
322
+ const tasks = await client.collections.tasks.find({
323
+ where: { completed: false },
324
+ orderBy: { priority: "desc" },
325
+ });
326
+
327
+ // Call route
328
+ const overdue = await client.routes.getOverdueTasks({});
329
+ ```
330
+
331
+ ---
332
+
333
+ ## 11. Start Development
334
+
335
+ ```bash
336
+ bun dev
337
+ ```
338
+
339
+ Test: `curl http://localhost:3000/api/tasks` for collection CRUD, `curl -X POST http://localhost:3000/api/get-overdue-tasks -H "Content-Type: application/json" -d '{}'` for typed route calls.
340
+
341
+ ---
342
+
343
+ ## Common Mistakes
344
+
345
+ ### CRITICAL: Using npm/yarn/pnpm instead of Bun
346
+
347
+ QUESTPIE requires **Bun** as the package manager and runtime. All commands use `bun` or `bunx`:
348
+
349
+ ```bash
350
+ # WRONG
351
+ npm install questpie
352
+ npx questpie generate
353
+ yarn add questpie
354
+
355
+ # CORRECT
356
+ bun add questpie
357
+ bunx questpie generate
358
+ bun dev
359
+ ```
360
+
361
+ ### HIGH: Forgetting to run `questpie generate` after changes
362
+
363
+ Every time you add, rename, or remove a file in a convention directory (`collections/`, `routes/`, `globals/`, `jobs/`, etc.), you must re-run:
364
+
365
+ ```bash
366
+ bunx questpie generate
367
+ ```
368
+
369
+ Without this, the `app` instance and types will be stale. New collections will not appear in CRUD endpoints or admin.
370
+
371
+ ### HIGH: Not setting DATABASE_URL
372
+
373
+ QUESTPIE requires PostgreSQL. Set `DATABASE_URL` in `.env` before running `push` or `migrate`:
374
+
375
+ ```bash
376
+ DATABASE_URL=postgres://user:pass@localhost:5432/myapp
377
+ ```
378
+
379
+ ### MEDIUM: Missing `export default` on convention files
380
+
381
+ Codegen discovers files by their **default export**. Files without `export default` are silently ignored:
382
+
383
+ ```ts
384
+ // WRONG — codegen will not discover this
385
+ export const tasks = collection("tasks").fields(/* ... */);
386
+
387
+ // CORRECT
388
+ export default collection("tasks").fields(/* ... */);
389
+ ```
390
+
391
+ ### MEDIUM: Putting business logic in route handlers
392
+
393
+ Framework route handlers should only mount the QUESTPIE fetch handler. Business logic belongs in `routes/`, `jobs/`, or collection hooks:
394
+
395
+ ```ts
396
+ // WRONG — business logic in route file
397
+ export const Route = createAPIFileRoute("/api/custom")({
398
+ POST: async ({ request }) => {
399
+ const db = getDB();
400
+ // ... manual queries
401
+ },
402
+ });
403
+
404
+ // CORRECT — use a typed route
405
+ // src/questpie/server/routes/my-logic.ts
406
+ export default route()
407
+ .post()
408
+ .schema(
409
+ z.object({
410
+ /* ... */
411
+ }),
412
+ )
413
+ .handler(async ({ input, collections }) => {
414
+ return await collections.tasks.create(input);
415
+ });
416
+ ```
417
+
418
+ ---
419
+
420
+ ## Quick Reference: CLI Commands
421
+
422
+ | Command | Purpose |
423
+ | -------------------------------- | ------------------------------------------- |
424
+ | `bun create questpie my-app` | Scaffold a new project |
425
+ | `bunx questpie generate` | Scan conventions, generate types and app |
426
+ | `bunx questpie push` | Push schema to DB (dev only, no migrations) |
427
+ | `bunx questpie migrate:generate` | Generate migration from schema diff |
428
+ | `bunx questpie migrate:up` | Run pending migrations |
429
+ | `bunx questpie migrate:down` | Rollback last migration |
430
+ | `bunx questpie migrate:fresh` | Reset + run all migrations |
431
+ | `bun dev` | Start development server |
432
+
433
+ ---
434
+
435
+ ## Minimal Complete Example
436
+
437
+ Starting from zero — every file needed for a working app:
438
+
439
+ ```ts
440
+ // questpie.config.ts (project root)
441
+ export { default } from "./src/questpie/server/questpie.config";
442
+ ```
443
+
444
+ ```ts
445
+ // src/questpie/server/questpie.config.ts
446
+ import { runtimeConfig } from "questpie";
447
+
448
+ export default runtimeConfig({
449
+ app: { url: process.env.APP_URL || "http://localhost:3000" },
450
+ db: { url: process.env.DATABASE_URL! },
451
+ secret: process.env.APP_SECRET || "dev-secret",
452
+ });
453
+ ```
454
+
455
+ ```ts
456
+ // src/questpie/server/collections/posts.ts
457
+ import { collection } from "#questpie/factories";
458
+
459
+ export default collection("posts").fields(({ f }) => ({
460
+ title: f.text().required(),
461
+ body: f.richText(),
462
+ status: f.select([
463
+ { value: "draft", label: "Draft" },
464
+ { value: "published", label: "Published" },
465
+ ]),
466
+ }));
467
+ ```
468
+
469
+ ```ts
470
+ // src/routes/api/$.ts
471
+ import { createAPIFileRoute } from "@tanstack/react-start/api";
472
+ import { createFetchHandler } from "questpie";
473
+ import { app } from "#questpie";
474
+
475
+ const handler = createFetchHandler(app, { basePath: "/api" });
476
+
477
+ export const Route = createAPIFileRoute("/api/$")({
478
+ GET: ({ request }) => handler(request),
479
+ POST: ({ request }) => handler(request),
480
+ PATCH: ({ request }) => handler(request),
481
+ DELETE: ({ request }) => handler(request),
482
+ });
483
+ ```
484
+
485
+ Then run:
486
+
487
+ ```bash
488
+ bunx questpie generate
489
+ bunx questpie push
490
+ bun dev
491
+ ```
492
+
493
+ ---
494
+
495
+ ## 12. Live Preview (Optional)
496
+
497
+ Add split-screen live preview to any collection with `.preview()`. The current same-tab flow refreshes the preview iframe after save/autosave and supports field focus over `postMessage`.
498
+
499
+ ### Add Preview to a Collection
500
+
501
+ ```ts
502
+ // src/questpie/server/collections/pages.ts
503
+ import { collection } from "#questpie/factories";
504
+
505
+ export default collection("pages")
506
+ .fields(({ f }) => ({
507
+ title: f.text().required(),
508
+ slug: f.text().required(),
509
+ content: f.blocks(),
510
+ }))
511
+ .preview({
512
+ enabled: true,
513
+ position: "right",
514
+ defaultWidth: 50,
515
+ url: ({ record }) => `/${record.slug}?preview=true`,
516
+ });
517
+ ```
518
+
519
+ ### Add Preview Support to the Frontend Page
520
+
521
+ ```tsx
522
+ import {
523
+ PreviewField,
524
+ PreviewProvider,
525
+ useCollectionPreview,
526
+ } from "@questpie/admin/client";
527
+
528
+ function PageView({ initialData }) {
529
+ const router = useRouter();
530
+ const preview = useCollectionPreview({
531
+ initialData,
532
+ onRefresh: () => router.invalidate(),
533
+ });
534
+
535
+ return (
536
+ <PreviewProvider
537
+ isPreviewMode={preview.isPreviewMode}
538
+ focusedField={preview.focusedField}
539
+ onFieldClick={preview.handleFieldClick}
540
+ >
541
+ <PreviewField field="title" as="h1">
542
+ {preview.data.title}
543
+ </PreviewField>
544
+ </PreviewProvider>
545
+ );
546
+ }
547
+ ```
548
+
549
+ The `"hybrid"` strategy is recommended as the default — it applies field patches instantly via `postMessage` while reconciling derived data (slugs, relations) through the server.