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.
- package/README.md +10 -6
- package/dist/index.mjs +140 -25
- package/package.json +5 -3
- package/skills/questpie/AGENTS.md +2664 -0
- package/skills/questpie/SKILL.md +181 -0
- package/skills/questpie/references/auth.md +121 -0
- package/skills/questpie/references/business-logic.md +550 -0
- package/skills/questpie/references/codegen-plugin-api.md +382 -0
- package/skills/questpie/references/crud-api.md +378 -0
- package/skills/questpie/references/data-modeling.md +489 -0
- package/skills/questpie/references/extend.md +493 -0
- package/skills/questpie/references/field-types.md +386 -0
- package/skills/questpie/references/infrastructure-adapters.md +545 -0
- package/skills/questpie/references/multi-tenancy.md +364 -0
- package/skills/questpie/references/production.md +475 -0
- package/skills/questpie/references/query-operators.md +125 -0
- package/skills/questpie/references/quickstart.md +549 -0
- package/skills/questpie/references/rules.md +327 -0
- package/skills/questpie/references/tanstack-query.md +520 -0
- package/skills/questpie-admin/AGENTS.md +1442 -0
- package/skills/questpie-admin/SKILL.md +410 -0
- package/skills/questpie-admin/references/blocks.md +307 -0
- package/skills/questpie-admin/references/custom-ui.md +305 -0
- package/skills/questpie-admin/references/views.md +433 -0
- package/templates/tanstack-start/AGENTS.md +71 -62
- package/templates/tanstack-start/CLAUDE.md +26 -23
- package/templates/tanstack-start/README.md +32 -20
- package/templates/tanstack-start/env.example +1 -1
- package/templates/tanstack-start/package.json +20 -6
- package/templates/tanstack-start/src/lib/client.ts +2 -2
- package/templates/tanstack-start/src/lib/env.ts +1 -1
- package/templates/tanstack-start/src/questpie/admin/.generated/client.ts +13 -0
- package/templates/tanstack-start/src/questpie/admin/modules.ts +1 -0
- package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +117 -241
- package/templates/tanstack-start/src/questpie/server/.generated/index.ts +129 -81
- package/templates/tanstack-start/src/questpie/server/app.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
- package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
- package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
- package/templates/tanstack-start/src/routes/__root.tsx +0 -2
- package/templates/tanstack-start/src/routes/admin.tsx +8 -1
- package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
- package/templates/tanstack-start/src/vite-env.d.ts +1 -0
- package/templates/tanstack-start/vite.config.ts +1 -3
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: questpie
|
|
3
|
+
description: QUESTPIE framework — server-first TypeScript CMS. File-convention codegen, collections, globals, routes, jobs, services, emails, blocks, typed client SDK, TanStack Query integration, adapters (queue, search, realtime, storage, email, KV). Use when building, reviewing, or refactoring any QUESTPIE project.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: questpie
|
|
7
|
+
version: "3.0.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# QUESTPIE Framework
|
|
11
|
+
|
|
12
|
+
Server-first TypeScript application framework. Define your data schema once using standalone factories, codegen generates the typed runtime, and any frontend consumes it through introspection.
|
|
13
|
+
|
|
14
|
+
## When to Apply
|
|
15
|
+
|
|
16
|
+
Reference these guidelines when:
|
|
17
|
+
- Creating or modifying collections, globals, routes, jobs, services, emails, blocks
|
|
18
|
+
- Working with file conventions or codegen pipeline
|
|
19
|
+
- Configuring adapters (queue, search, storage, realtime, email, KV)
|
|
20
|
+
- Setting up access control, hooks, or validation
|
|
21
|
+
- Building typed client SDK queries or TanStack Query integrations
|
|
22
|
+
- Writing modules or plugins for QUESTPIE
|
|
23
|
+
- Scaffolding a new project or onboarding
|
|
24
|
+
|
|
25
|
+
## Import Paths — Critical
|
|
26
|
+
|
|
27
|
+
| Factory | Import From | Needs Codegen? |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `collection(name)` | `#questpie/factories` | Yes |
|
|
30
|
+
| `global(name)` | `#questpie/factories` | Yes |
|
|
31
|
+
| `block(name)` | `#questpie/factories` | Yes |
|
|
32
|
+
| `adminConfig({...})` | `#questpie/factories` | Yes |
|
|
33
|
+
| `route()` | `"questpie"` | No |
|
|
34
|
+
| `job({...})` | `"questpie"` | No |
|
|
35
|
+
| `service()` | `"questpie"` | No |
|
|
36
|
+
| `email({...})` | `"questpie"` | No |
|
|
37
|
+
| `migration({...})` | `"questpie"` | No |
|
|
38
|
+
| `seed({...})` | `"questpie"` | No |
|
|
39
|
+
| `runtimeConfig({...})` | `"questpie"` | No |
|
|
40
|
+
| `appConfig({...})` | `"questpie"` | No |
|
|
41
|
+
| `authConfig({...})` | `"questpie"` | No |
|
|
42
|
+
| `createClient<AppConfig>()` | `"questpie/client"` | No |
|
|
43
|
+
| `createQuestpieQueryOptions()` | `"@questpie/tanstack-query"` | No |
|
|
44
|
+
|
|
45
|
+
## Reference Topics
|
|
46
|
+
|
|
47
|
+
### Core
|
|
48
|
+
|
|
49
|
+
| Topic | File | Covers |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| Quickstart | `references/quickstart.md` | Scaffold, configure, codegen, migrate, serve — zero to running app |
|
|
52
|
+
| Data Modeling | `references/data-modeling.md` | Collections, globals, fields, relations, options, localization |
|
|
53
|
+
| Field Types | `references/field-types.md` | All built-in field types with options and operators |
|
|
54
|
+
| Rules | `references/rules.md` | Access control (row/field level), hooks lifecycle, validation |
|
|
55
|
+
| Business Logic | `references/business-logic.md` | Routes, jobs, services, email templates, context injection |
|
|
56
|
+
| CRUD API | `references/crud-api.md` | Server-side `find`, `create`, `update`, `delete`, globals API |
|
|
57
|
+
| Query Operators | `references/query-operators.md` | `where` clause operators by field type |
|
|
58
|
+
|
|
59
|
+
### Infrastructure
|
|
60
|
+
|
|
61
|
+
| Topic | File | Covers |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| Production | `references/production.md` | Queue, search, realtime, storage, email, KV adapter setup |
|
|
64
|
+
| Auth | `references/auth.md` | Better Auth integration, session, providers, access patterns |
|
|
65
|
+
| Adapters | `references/infrastructure-adapters.md` | All adapter configs: pg-boss, S3, SMTP, pgNotify, Redis |
|
|
66
|
+
|
|
67
|
+
### Extend
|
|
68
|
+
|
|
69
|
+
| Topic | File | Covers |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| Extend | `references/extend.md` | Custom modules, fields, operators, adapters, codegen plugins |
|
|
72
|
+
| Codegen Plugin API | `references/codegen-plugin-api.md` | Plugin architecture, category declarations, templates |
|
|
73
|
+
| Multi-Tenancy | `references/multi-tenancy.md` | Scope isolation, workspace filtering, ScopeProvider |
|
|
74
|
+
|
|
75
|
+
### Client
|
|
76
|
+
|
|
77
|
+
| Topic | File | Covers |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| TanStack Query | `references/tanstack-query.md` | `q.collections.*`, `q.globals.*`, `q.routes.*`, realtime queries |
|
|
80
|
+
|
|
81
|
+
## Key Patterns — Quick Reference
|
|
82
|
+
|
|
83
|
+
### Collection
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { collection } from "#questpie/factories";
|
|
87
|
+
|
|
88
|
+
export default collection("posts")
|
|
89
|
+
.fields(({ f }) => ({
|
|
90
|
+
title: f.text().required(),
|
|
91
|
+
status: f.select([{ value: "draft", label: "Draft" }]),
|
|
92
|
+
author: f.relation("users").required(),
|
|
93
|
+
}))
|
|
94
|
+
.access({
|
|
95
|
+
read: true,
|
|
96
|
+
create: ({ session }) => !!session,
|
|
97
|
+
update: ({ session, doc }) => doc.authorId === session?.user?.id,
|
|
98
|
+
})
|
|
99
|
+
.hooks({
|
|
100
|
+
beforeChange: async ({ data, operation }) => {
|
|
101
|
+
if (operation === "create") data.slug = slugify(data.title);
|
|
102
|
+
return data;
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
.options({ versioning: true, timestamps: true });
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Route
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { route } from "questpie";
|
|
112
|
+
import z from "zod";
|
|
113
|
+
|
|
114
|
+
export default route()
|
|
115
|
+
.post()
|
|
116
|
+
.schema(z.object({ period: z.enum(["day", "week", "month"]) }))
|
|
117
|
+
.handler(async ({ input, collections }) => {
|
|
118
|
+
return collections.posts.find({ where: { status: "published" } });
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Job
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { job } from "questpie";
|
|
126
|
+
import z from "zod";
|
|
127
|
+
|
|
128
|
+
export default job({
|
|
129
|
+
name: "sendReminder",
|
|
130
|
+
schema: z.object({ userId: z.string() }),
|
|
131
|
+
retryDelay: 5, // seconds (not ms!)
|
|
132
|
+
handler: async ({ payload, email, collections }) => {
|
|
133
|
+
const user = await collections.users.findOne({ where: { id: payload.userId } });
|
|
134
|
+
await email.send("reminder", { to: user.email, data: { name: user.name } });
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Client SDK
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { createClient } from "questpie/client";
|
|
143
|
+
import type { AppConfig } from "#questpie";
|
|
144
|
+
|
|
145
|
+
const client = createClient<AppConfig>({
|
|
146
|
+
baseURL: typeof window !== "undefined" ? window.location.origin : process.env.APP_URL,
|
|
147
|
+
basePath: "/api",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const { docs } = await client.collections.posts.find({
|
|
151
|
+
where: { status: "published" },
|
|
152
|
+
orderBy: { createdAt: "desc" },
|
|
153
|
+
with: { author: true },
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Queue Dispatch
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
// In any handler with AppContext:
|
|
161
|
+
await queue.sendReminder.publish({ userId: "abc" });
|
|
162
|
+
// NOT queue.send("sendReminder", payload)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Common Mistakes
|
|
166
|
+
|
|
167
|
+
| Severity | Mistake | Fix |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| CRITICAL | Files in wrong directory | Collections in `collections/`, routes in `routes/`, etc. |
|
|
170
|
+
| CRITICAL | Missing `export default` on convention files | Codegen silently ignores files without default export |
|
|
171
|
+
| CRITICAL | Importing route/job/service from `#questpie/factories` | Use `"questpie"` — only collection/global/block/adminConfig use `#questpie/factories` |
|
|
172
|
+
| HIGH | Forgetting `questpie generate` after adding files | Re-run codegen on any file add/remove in convention dirs |
|
|
173
|
+
| HIGH | Job handler uses `input` instead of `payload` | Jobs destructure `{ payload }`, routes destructure `{ input }` |
|
|
174
|
+
| HIGH | `queue.send("name", data)` | Use `queue.jobName.publish(data)` |
|
|
175
|
+
| HIGH | `beforeCreate` / `afterCreate` hook names | Use `beforeChange` / `afterChange` with `operation === "create"` guard |
|
|
176
|
+
| MEDIUM | Using npm/yarn instead of Bun | QUESTPIE requires Bun as package manager |
|
|
177
|
+
| MEDIUM | Editing `.generated/` files | Never edit — re-run `questpie generate` |
|
|
178
|
+
|
|
179
|
+
## Full Compiled Document
|
|
180
|
+
|
|
181
|
+
For the complete framework reference with all topics expanded: `AGENTS.md`
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Authentication Reference
|
|
2
|
+
|
|
3
|
+
Detailed authentication configuration for QUESTPIE using Better Auth.
|
|
4
|
+
|
|
5
|
+
## File Convention
|
|
6
|
+
|
|
7
|
+
Auth is configured via `config/auth.ts` using the `authConfig()` factory:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// src/questpie/server/config/auth.ts
|
|
11
|
+
import { authConfig } from "questpie";
|
|
12
|
+
|
|
13
|
+
export default authConfig({
|
|
14
|
+
emailAndPassword: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
requireEmailVerification: false,
|
|
17
|
+
},
|
|
18
|
+
baseURL: process.env.APP_URL || "http://localhost:3000",
|
|
19
|
+
basePath: "/api/auth",
|
|
20
|
+
secret: process.env.BETTER_AUTH_SECRET || "change-me",
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Codegen discovers this file automatically. No manual registration needed.
|
|
25
|
+
|
|
26
|
+
## Configuration Options
|
|
27
|
+
|
|
28
|
+
| Option | Type | Default | Description |
|
|
29
|
+
| ------------------------------------------- | --------- | ------------- | ----------------------------------------------------------- |
|
|
30
|
+
| `emailAndPassword.enabled` | `boolean` | `false` | Enable email/password authentication |
|
|
31
|
+
| `emailAndPassword.requireEmailVerification` | `boolean` | `false` | Require email verification before login |
|
|
32
|
+
| `baseURL` | `string` | — | Application public URL (used for OAuth callbacks) |
|
|
33
|
+
| `basePath` | `string` | `"/api/auth"` | Auth API route prefix |
|
|
34
|
+
| `secret` | `string` | — | Session signing secret. **Must be 32+ chars in production** |
|
|
35
|
+
|
|
36
|
+
## Session Access
|
|
37
|
+
|
|
38
|
+
### In Routes
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { route } from "questpie";
|
|
42
|
+
import z from "zod";
|
|
43
|
+
|
|
44
|
+
export default route()
|
|
45
|
+
.post()
|
|
46
|
+
.schema(z.object({ postId: z.string() }))
|
|
47
|
+
.handler(async ({ input, session, collections }) => {
|
|
48
|
+
if (!session) {
|
|
49
|
+
throw new Error("Not authenticated");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const user = session.user;
|
|
53
|
+
// user.id - unique user ID
|
|
54
|
+
// user.email - user email address
|
|
55
|
+
// user.name - user display name
|
|
56
|
+
|
|
57
|
+
const post = await collections.posts.create({
|
|
58
|
+
title: "My Post",
|
|
59
|
+
author: user.id,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return post;
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### In Hooks
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
.hooks({
|
|
70
|
+
beforeChange: async ({ data, operation, session }) => {
|
|
71
|
+
if (operation === "create") {
|
|
72
|
+
if (!session) throw new Error("Must be logged in");
|
|
73
|
+
data.createdBy = session.user.id;
|
|
74
|
+
}
|
|
75
|
+
return data;
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### In Access Rules
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
.access({
|
|
84
|
+
// Public read
|
|
85
|
+
read: true,
|
|
86
|
+
|
|
87
|
+
// Authenticated users can create
|
|
88
|
+
create: ({ session }) => !!session,
|
|
89
|
+
|
|
90
|
+
// Only admins can update/delete
|
|
91
|
+
update: ({ session }) => (session?.user as any)?.role === "admin",
|
|
92
|
+
delete: ({ session }) => (session?.user as any)?.role === "admin",
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## User Collection
|
|
97
|
+
|
|
98
|
+
The `adminModule` provides a built-in `user` collection. It stores:
|
|
99
|
+
|
|
100
|
+
- `id` -- unique identifier
|
|
101
|
+
- `email` -- email address
|
|
102
|
+
- `name` -- display name
|
|
103
|
+
- `image` -- avatar URL
|
|
104
|
+
- `emailVerified` -- verification status
|
|
105
|
+
|
|
106
|
+
This collection is automatically created when you add the admin module to your config.
|
|
107
|
+
|
|
108
|
+
## Environment Variables
|
|
109
|
+
|
|
110
|
+
| Variable | Required | Description |
|
|
111
|
+
| -------------------- | ---------- | --------------------------------------------------------- |
|
|
112
|
+
| `APP_URL` | Yes | Public URL -- used for OAuth callback URLs |
|
|
113
|
+
| `BETTER_AUTH_SECRET` | Yes (prod) | Session signing secret. Use a random 32+ character string |
|
|
114
|
+
|
|
115
|
+
## Production Security Checklist
|
|
116
|
+
|
|
117
|
+
1. Set `BETTER_AUTH_SECRET` to a strong random value (32+ chars)
|
|
118
|
+
2. Set `APP_URL` to your production domain (HTTPS)
|
|
119
|
+
3. Enable `requireEmailVerification` if using email/password
|
|
120
|
+
4. Use HTTPS for all auth endpoints
|
|
121
|
+
5. Configure proper CORS if API and frontend are on different domains
|