create-questpie 2.0.4 → 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.
Files changed (152) hide show
  1. package/dist/index.mjs +362 -119
  2. package/package.json +2 -3
  3. package/templates/elysia/AGENTS.md +56 -0
  4. package/templates/elysia/CLAUDE.md +39 -0
  5. package/templates/elysia/Dockerfile +24 -0
  6. package/templates/elysia/README.md +148 -0
  7. package/templates/elysia/docker/init-extensions.sql +11 -0
  8. package/templates/elysia/docker-compose.yml +21 -0
  9. package/templates/elysia/env.example +16 -0
  10. package/templates/elysia/gitignore +6 -0
  11. package/templates/elysia/package.json +47 -0
  12. package/templates/elysia/questpie.config.ts +12 -0
  13. package/templates/elysia/src/index.ts +21 -0
  14. package/templates/elysia/src/lib/auth-client.ts +32 -0
  15. package/templates/elysia/src/lib/client.ts +13 -0
  16. package/templates/elysia/src/lib/env.ts +24 -0
  17. package/templates/elysia/src/lib/query-client.ts +18 -0
  18. package/templates/elysia/src/lib/query.ts +18 -0
  19. package/templates/elysia/src/questpie/server/.generated/context.gen.ts +200 -0
  20. package/templates/elysia/src/questpie/server/.generated/entities.gen.ts +84 -0
  21. package/templates/elysia/src/questpie/server/.generated/factories.ts +65 -0
  22. package/templates/elysia/src/questpie/server/.generated/index.ts +131 -0
  23. package/templates/elysia/src/questpie/server/.generated/names.gen.ts +25 -0
  24. package/templates/elysia/src/questpie/server/app.ts +10 -0
  25. package/templates/elysia/src/questpie/server/collections/index.ts +1 -0
  26. package/templates/elysia/src/questpie/server/collections/posts.collection.ts +10 -0
  27. package/templates/elysia/src/questpie/server/config/auth.ts +8 -0
  28. package/templates/elysia/src/questpie/server/config/openapi.ts +10 -0
  29. package/templates/elysia/src/questpie/server/globals/index.ts +1 -0
  30. package/templates/elysia/src/questpie/server/globals/site-settings.global.ts +10 -0
  31. package/templates/elysia/src/questpie/server/modules.ts +8 -0
  32. package/templates/elysia/src/questpie/server/questpie.config.ts +21 -0
  33. package/templates/elysia/tsconfig.json +28 -0
  34. package/templates/hono/AGENTS.md +56 -0
  35. package/templates/hono/CLAUDE.md +39 -0
  36. package/templates/hono/Dockerfile +24 -0
  37. package/templates/hono/README.md +148 -0
  38. package/templates/hono/docker/init-extensions.sql +11 -0
  39. package/templates/hono/docker-compose.yml +21 -0
  40. package/templates/hono/env.example +16 -0
  41. package/templates/hono/gitignore +6 -0
  42. package/templates/hono/package.json +47 -0
  43. package/templates/hono/questpie.config.ts +12 -0
  44. package/templates/hono/src/index.ts +30 -0
  45. package/templates/hono/src/lib/auth-client.ts +32 -0
  46. package/templates/hono/src/lib/client.ts +13 -0
  47. package/templates/hono/src/lib/env.ts +24 -0
  48. package/templates/hono/src/lib/query-client.ts +18 -0
  49. package/templates/hono/src/lib/query.ts +18 -0
  50. package/templates/hono/src/questpie/server/.generated/context.gen.ts +200 -0
  51. package/templates/hono/src/questpie/server/.generated/entities.gen.ts +84 -0
  52. package/templates/hono/src/questpie/server/.generated/factories.ts +65 -0
  53. package/templates/hono/src/questpie/server/.generated/index.ts +131 -0
  54. package/templates/hono/src/questpie/server/.generated/names.gen.ts +25 -0
  55. package/templates/hono/src/questpie/server/app.ts +10 -0
  56. package/templates/hono/src/questpie/server/collections/index.ts +1 -0
  57. package/templates/hono/src/questpie/server/collections/posts.collection.ts +10 -0
  58. package/templates/hono/src/questpie/server/config/auth.ts +8 -0
  59. package/templates/hono/src/questpie/server/config/openapi.ts +10 -0
  60. package/templates/hono/src/questpie/server/globals/index.ts +1 -0
  61. package/templates/hono/src/questpie/server/globals/site-settings.global.ts +10 -0
  62. package/templates/hono/src/questpie/server/modules.ts +8 -0
  63. package/templates/hono/src/questpie/server/questpie.config.ts +21 -0
  64. package/templates/hono/tsconfig.json +28 -0
  65. package/templates/next/AGENTS.md +55 -0
  66. package/templates/next/CLAUDE.md +39 -0
  67. package/templates/next/Dockerfile +25 -0
  68. package/templates/next/README.md +148 -0
  69. package/templates/next/components.json +22 -0
  70. package/templates/next/docker/init-extensions.sql +11 -0
  71. package/templates/next/docker-compose.yml +21 -0
  72. package/templates/next/env.example +16 -0
  73. package/templates/next/gitignore +10 -0
  74. package/templates/next/next-env.d.ts +5 -0
  75. package/templates/next/next.config.ts +20 -0
  76. package/templates/next/package.json +54 -0
  77. package/templates/next/postcss.config.mjs +8 -0
  78. package/templates/next/public/.gitkeep +0 -0
  79. package/templates/next/questpie.config.ts +12 -0
  80. package/templates/next/src/app/admin/[[...all]]/page.tsx +34 -0
  81. package/templates/next/src/app/admin/admin.css +4 -0
  82. package/templates/next/src/app/admin/layout.tsx +63 -0
  83. package/templates/next/src/app/api/[...all]/route.ts +24 -0
  84. package/templates/next/src/app/layout.tsx +24 -0
  85. package/templates/next/src/app/not-found.tsx +18 -0
  86. package/templates/next/src/app/page.tsx +74 -0
  87. package/templates/next/src/app/providers.tsx +11 -0
  88. package/templates/next/src/lib/auth-client.ts +12 -0
  89. package/templates/next/src/lib/client.ts +13 -0
  90. package/templates/next/src/lib/env.ts +24 -0
  91. package/templates/next/src/lib/query-client.ts +18 -0
  92. package/templates/next/src/lib/query.ts +18 -0
  93. package/templates/next/src/questpie/admin/.generated/client.ts +13 -0
  94. package/templates/next/src/questpie/admin/admin.ts +9 -0
  95. package/templates/next/src/questpie/admin/modules.ts +3 -0
  96. package/templates/next/src/questpie/server/.generated/context.gen.ts +204 -0
  97. package/templates/next/src/questpie/server/.generated/entities.gen.ts +100 -0
  98. package/templates/next/src/questpie/server/.generated/factories.ts +204 -0
  99. package/templates/next/src/questpie/server/.generated/index.ts +139 -0
  100. package/templates/next/src/questpie/server/.generated/names.gen.ts +31 -0
  101. package/templates/next/src/questpie/server/app.ts +10 -0
  102. package/templates/next/src/questpie/server/collections/index.ts +1 -0
  103. package/templates/next/src/questpie/server/collections/posts.collection.ts +58 -0
  104. package/templates/next/src/questpie/server/config/admin.ts +80 -0
  105. package/templates/next/src/questpie/server/config/auth.ts +8 -0
  106. package/templates/next/src/questpie/server/config/openapi.ts +10 -0
  107. package/templates/next/src/questpie/server/globals/index.ts +1 -0
  108. package/templates/next/src/questpie/server/globals/site-settings.global.ts +19 -0
  109. package/templates/next/src/questpie/server/modules.ts +9 -0
  110. package/templates/next/src/questpie/server/questpie.config.ts +21 -0
  111. package/templates/next/src/styles.css +125 -0
  112. package/templates/next/tsconfig.json +37 -0
  113. package/templates/tanstack-start/AGENTS.md +35 -607
  114. package/templates/tanstack-start/CLAUDE.md +26 -134
  115. package/templates/tanstack-start/README.md +13 -1
  116. package/templates/tanstack-start/docker/init-extensions.sql +11 -0
  117. package/templates/tanstack-start/docker-compose.yml +1 -0
  118. package/templates/tanstack-start/src/lib/auth-client.ts +1 -1
  119. package/templates/tanstack-start/src/lib/client.ts +1 -1
  120. package/templates/tanstack-start/src/lib/query.ts +18 -0
  121. package/templates/tanstack-start/src/questpie/server/collections/index.ts +1 -1
  122. package/templates/tanstack-start/src/questpie/server/globals/index.ts +1 -1
  123. package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
  124. package/templates/tanstack-start/src/routes/__root.tsx +31 -1
  125. package/templates/tanstack-start/src/routes/api/$.ts +1 -1
  126. package/templates/tanstack-start/src/routes/index.tsx +97 -0
  127. package/skills/questpie/AGENTS.md +0 -2871
  128. package/skills/questpie/SKILL.md +0 -293
  129. package/skills/questpie/coverage.json +0 -213
  130. package/skills/questpie/references/auth.md +0 -236
  131. package/skills/questpie/references/business-logic.md +0 -620
  132. package/skills/questpie/references/codegen-plugin-api.md +0 -382
  133. package/skills/questpie/references/crud-api.md +0 -580
  134. package/skills/questpie/references/data-modeling.md +0 -509
  135. package/skills/questpie/references/extend.md +0 -584
  136. package/skills/questpie/references/field-types.md +0 -398
  137. package/skills/questpie/references/infrastructure-adapters.md +0 -720
  138. package/skills/questpie/references/mcp.md +0 -147
  139. package/skills/questpie/references/multi-tenancy.md +0 -363
  140. package/skills/questpie/references/production.md +0 -640
  141. package/skills/questpie/references/query-operators.md +0 -125
  142. package/skills/questpie/references/quickstart.md +0 -562
  143. package/skills/questpie/references/rules.md +0 -454
  144. package/skills/questpie/references/sandbox.md +0 -110
  145. package/skills/questpie/references/tanstack-query.md +0 -543
  146. package/skills/questpie/references/type-inference.md +0 -167
  147. package/skills/questpie/references/workflows.md +0 -155
  148. package/skills/questpie-admin/AGENTS.md +0 -1515
  149. package/skills/questpie-admin/SKILL.md +0 -443
  150. package/skills/questpie-admin/references/blocks.md +0 -331
  151. package/skills/questpie-admin/references/custom-ui.md +0 -305
  152. package/skills/questpie-admin/references/views.md +0 -449
@@ -1,584 +0,0 @@
1
- ---
2
- name: questpie-core/extend
3
- description: QUESTPIE extensibility — codegen plugins CodegenPlugin CategoryDeclaration CallbackParamDefinition, building modules, custom field types field() factory toColumn toZodSchema getOperators getMetadata, custom adapters createFetchHandler Elysia Hono Next.js TanStack Start, type registries FieldTypeRegistry ComponentTypeRegistry ViewKindRegistry declare module augmentation, package distribution tsdown npm publishing changesets
4
- - questpie-core
5
- ---
6
-
7
- ## Overview
8
-
9
- This skill covers extending QUESTPIE: building codegen plugins, reusable modules, custom field types, framework adapters, type registries, and package distribution.
10
-
11
- ## Building a Codegen Plugin
12
-
13
- A plugin tells codegen what to discover and what types to generate. Plugins contribute to one or more codegen **targets** (e.g., `"server"`, `"admin-client"`).
14
-
15
- ### Plugin Structure
16
-
17
- ```ts
18
- import type { CodegenPlugin } from "questpie/codegen";
19
-
20
- export function myPlugin(): CodegenPlugin {
21
- return {
22
- name: "my-plugin",
23
-
24
- targets: {
25
- // Contribute to the server target
26
- server: {
27
- root: ".",
28
- outputFile: "index.ts",
29
-
30
- // Directory-pattern categories to discover
31
- categories: {
32
- widgets: {
33
- dirs: ["widgets"],
34
- prefix: "widget",
35
- emit: "record",
36
- registryKey: "widgets",
37
- includeInAppState: true,
38
- },
39
- },
40
-
41
- // Single-file / glob discover patterns
42
- discover: {
43
- widgetConfig: { pattern: "widget-config.ts", cardinality: "single" },
44
- },
45
-
46
- // Extension methods for collection()/global() factories
47
- registries: {
48
- collectionExtensions: {
49
- widget: {
50
- stateKey: "~widget",
51
- configType: "WidgetConfig",
52
- imports: [{ name: "WidgetConfig", from: "my-plugin-package" }],
53
- },
54
- },
55
- singletonFactories: {
56
- widgetConfig: {
57
- configType: "WidgetConfig",
58
- imports: [{ name: "WidgetConfig", from: "my-plugin-package" }],
59
- },
60
- },
61
- },
62
-
63
- // Callback param definitions for extension methods
64
- callbackParams: {
65
- w: {
66
- factory: "createWidgetNameProxy",
67
- from: "my-plugin-package",
68
- },
69
- },
70
- },
71
- },
72
- };
73
- }
74
- ```
75
-
76
- ### Register in Config
77
-
78
- ```ts title="questpie.config.ts"
79
- import { runtimeConfig } from "questpie/app";
80
- import { myPlugin } from "my-plugin-package";
81
-
82
- export default runtimeConfig({
83
- plugins: [myPlugin()],
84
- db: { url: process.env.DATABASE_URL! },
85
- app: { url: process.env.APP_URL! },
86
- });
87
- ```
88
-
89
- Use direct `runtimeConfig({ plugins })` registration only for standalone codegen plugins or custom setups that do not ship a module. Reusable packages should usually attach the plugin to a static module and let codegen extract it from `modules.ts`.
90
-
91
- ### Configurable Codegen-Aware Modules
92
-
93
- When a package ships a module and a `CodegenPlugin`, keep module identity static and put runtime options in a plugin-discovered config file. Codegen imports `modules.ts` before runtime app creation, so it must be able to see the same module/plugin tree regardless of environment or runtime options.
94
-
95
- #### DO THIS
96
-
97
- ```ts title="modules.ts"
98
- import { observabilityModule } from "@questpie/observability/server";
99
-
100
- export default [observabilityModule] as const;
101
- ```
102
-
103
- ```ts title="config/observability.ts"
104
- import { observabilityConfig } from "@questpie/observability/server";
105
-
106
- export default observabilityConfig({
107
- serviceName: "barbershop",
108
- enabled: process.env.NODE_ENV === "production",
109
- otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
110
- });
111
- ```
112
-
113
- ```ts title="@questpie/observability/server.ts"
114
- export const observabilityModule = module({
115
- name: "questpie-observability",
116
- plugin: observabilityPlugin(),
117
- services: {
118
- observability: service({
119
- namespace: null,
120
- lifecycle: "singleton",
121
- create: ({ app, logger }) =>
122
- createObservabilityService(app.state.config?.observability, logger),
123
- }),
124
- },
125
- });
126
- ```
127
-
128
- The plugin contributes `config/observability.ts` as a discover pattern and a typed singleton factory such as `observabilityConfig()`. The service reads the resolved config at runtime from `app.state.config.observability`.
129
-
130
- #### DON'T DO THIS
131
-
132
- Do not make runtime options the main API for modules that contribute codegen plugins:
133
-
134
- ```ts title="modules.ts"
135
- export default [
136
- observabilityModule({
137
- serviceName: "barbershop",
138
- enabled: process.env.NODE_ENV === "production",
139
- }),
140
- ] as const;
141
- ```
142
-
143
- Do not conditionally include codegen-aware modules or plugins:
144
-
145
- ```ts title="modules.ts"
146
- export default [
147
- process.env.OTEL_ENABLED ? observabilityModule : undefined,
148
- ].filter(Boolean);
149
- ```
150
-
151
- Factory modules are acceptable only for simple runtime-only modules whose plugin identity and generated contributions do not change. If the package contributes discover patterns, generated factories, module categories, views, components, fields, or collection/global extensions, use **static module + `config/*.ts` singleton factory**.
152
-
153
- ### Plugin Lifecycle
154
-
155
- 1. **Discovery** -- codegen scans for files matching category patterns and discover patterns
156
- 2. **Import** -- files are imported and exports are read
157
- 3. **Transform** -- `transform(ctx)` callbacks can modify the codegen context
158
- 4. **Generation** -- types and runtime code are emitted
159
- 5. **Validation** -- cross-target validators check projection consistency
160
-
161
- ### Real-World Example: Admin Plugin
162
-
163
- The admin module contributes a codegen plugin to both `"server"` and `"admin-client"` targets -- declaring categories (`blocks`, `views`, `components`, field types), discovering `config/admin.ts`, adding collection/global/field extensions, and defining callback context params such as `v`, `f`, `c`, and `a`.
164
-
165
- ## Building a Module
166
-
167
- A module is a reusable package that contributes entities to any QUESTPIE project.
168
-
169
- ```ts
170
- import { module } from "questpie/app";
171
- import { collection } from "questpie/builders";
172
- import { job } from "questpie/services";
173
- import { z } from "zod";
174
-
175
- const notificationsCollection = collection("notifications")
176
- .fields(({ f }) => ({
177
- title: f.text().required(),
178
- body: f.textarea(),
179
- read: f.boolean().default(false),
180
- userId: f.relation("user").required(),
181
- }))
182
- .admin(({ c }) => ({
183
- label: { en: "Notifications" },
184
- icon: c.icon("ph:bell"),
185
- }));
186
-
187
- const sendPushNotification = job({
188
- name: "sendPushNotification",
189
- schema: z.object({
190
- userId: z.string(),
191
- title: z.string(),
192
- body: z.string(),
193
- }),
194
- handler: async ({ payload }) => {
195
- // Send push notification logic
196
- },
197
- });
198
-
199
- export const notificationsModule = module({
200
- name: "notifications",
201
- collections: { notifications: notificationsCollection },
202
- jobs: { sendPushNotification },
203
- sidebar: {
204
- items: [
205
- {
206
- sectionId: "operations",
207
- type: "collection",
208
- collection: "notifications",
209
- },
210
- ],
211
- },
212
- messages: {
213
- en: {
214
- "notifications.title": "Notifications",
215
- "notifications.markRead": "Mark as read",
216
- },
217
- },
218
- });
219
- ```
220
-
221
- ### Module Options
222
-
223
- | Property | Type | Description |
224
- | ------------- | ------------- | ------------------------ |
225
- | `name` | `string` | Module identifier |
226
- | `modules` | `Module[]` | Module dependencies |
227
- | `collections` | `Record` | Collection contributions |
228
- | `globals` | `Record` | Global contributions |
229
- | `jobs` | `Record` | Job contributions |
230
- | `functions` | `Record` | Function contributions |
231
- | `services` | `Record` | Service contributions |
232
- | `routes` | `Record` | Route contributions |
233
- | `fields` | `Record` | Custom field types |
234
- | `sidebar` | `object` | Sidebar items |
235
- | `dashboard` | `object` | Dashboard widgets |
236
- | `migrations` | `Migration[]` | Database migrations |
237
- | `seeds` | `Seed[]` | Seed data |
238
- | `messages` | `Record` | i18n translations |
239
-
240
- ### How Module Contributions Merge
241
-
242
- When several modules (and the app) contribute the same key, `createApp()` merges them deterministically — later modules win per entry:
243
-
244
- | Key | Strategy |
245
- | --- | --- |
246
- | `collections`, `globals`, `jobs`, `routes`, `fields`, `services` | record spread-merge — same key: later wins |
247
- | `messages` | deep-merge by locale — same message key: later wins |
248
- | `migrations`, `seeds` | array concatenation |
249
- | `config.*` (app, auth, admin, plugin config keys) | per-key strategies; `auth`/`admin` deep-merge; unknown keys: incoming replaces existing |
250
- | anything else | auto-detect: object+object → spread, array+array → concat, otherwise incoming wins |
251
-
252
- The merge helpers behind these strategies are exported from `questpie/app` for module authors combining config fragments of their own:
253
-
254
- ```ts
255
- import { lastWins, mergeConcat, mergeDeepConcat, mergeRecord, type MergeFn } from "questpie/app";
256
-
257
- mergeRecord(a, b); // { ...a, ...b }
258
- mergeConcat(a, b); // [...a, ...b]
259
- mergeDeepConcat(a, b); // spread objects, concat array-valued props
260
- lastWins(a, b); // b
261
- ```
262
-
263
- Use them (instead of hand-rolled spreads) when a module exposes its own "combine these contributions" surface — the semantics then match what the framework does for built-in keys.
264
-
265
- ### Using a Module
266
-
267
- ```ts title="modules.ts"
268
- import { adminModule } from "@questpie/admin/modules/admin";
269
- import { notificationsModule } from "my-notifications-package";
270
-
271
- export default [adminModule, notificationsModule] as const;
272
- ```
273
-
274
- ## Custom Field Types
275
-
276
- Custom fields are registered through modules and become available on the `f` builder after codegen.
277
-
278
- ### Registration
279
-
280
- ```ts
281
- const myModule = module({
282
- name: "custom-fields",
283
- fields: {
284
- color: colorField,
285
- currency: currencyField,
286
- },
287
- });
288
- ```
289
-
290
- Once registered and codegen runs:
291
-
292
- ```ts
293
- .fields(({ f }) => ({
294
- brandColor: f.color().default("#000000"),
295
- price: f.currency({ currency: "USD" }),
296
- }))
297
- ```
298
-
299
- ### Field Definition
300
-
301
- A custom field defines:
302
-
303
- - **Storage type** -- how the value is stored in the database (via `toColumn`)
304
- - **Validation** -- Zod schema for the value (via `toZodSchema`)
305
- - **Operators** -- query operators (via `getOperators`)
306
- - **Metadata** -- introspection metadata (via `getMetadata`)
307
-
308
- The `Field` class is an immutable builder:
309
-
310
- ```ts
311
- import { Field } from "questpie/builders";
312
-
313
- // Each method returns a new Field with updated type state
314
- f.text(255).required().label({ en: "Name" }).admin({ placeholder: "..." });
315
- ```
316
-
317
- ### Admin Renderer
318
-
319
- Register a React component to render the field in the admin panel:
320
-
321
- ```tsx
322
- function ColorFieldRenderer({ value, onChange }) {
323
- return (
324
- <input
325
- type="color"
326
- value={value || "#000000"}
327
- onChange={(e) => onChange(e.target.value)}
328
- />
329
- );
330
- }
331
- ```
332
-
333
- Place it in `questpie/admin/fields/color.tsx` -- codegen discovers it automatically.
334
-
335
- ## Custom Adapters
336
-
337
- QUESTPIE ships with adapters for Hono, Elysia, and Next.js. For other frameworks, use `createFetchHandler` directly.
338
-
339
- ### Generic Fetch Handler
340
-
341
- ```ts
342
- import { createFetchHandler } from "questpie/http";
343
- import { app } from "#questpie";
344
-
345
- const handler = createFetchHandler(app, { basePath: "/api" });
346
- // Use with any framework supporting standard Request/Response
347
- const response = await handler(request);
348
- ```
349
-
350
- ### Framework Adapters
351
-
352
- **Elysia:**
353
-
354
- ```ts
355
- import { Elysia } from "elysia";
356
- import { questpieElysia } from "@questpie/elysia/server";
357
- import { app } from "#questpie";
358
-
359
- const server = new Elysia()
360
- .use(questpieElysia(app, { basePath: "/api" }))
361
- .listen(3000);
362
- ```
363
-
364
- **Hono:**
365
-
366
- ```ts
367
- import { Hono } from "hono";
368
- import { questpieHono } from "@questpie/hono/server";
369
- import { app } from "#questpie";
370
-
371
- const server = new Hono().route("/api", questpieHono(app));
372
- export default server;
373
- ```
374
-
375
- **Next.js (App Router):**
376
-
377
- ```ts title="app/api/[...slug]/route.ts"
378
- import { questpieNextRouteHandlers } from "@questpie/next";
379
- import { app } from "#questpie";
380
-
381
- export const { GET, POST, PATCH, DELETE } = questpieNextRouteHandlers(app, {
382
- basePath: "/api",
383
- });
384
- ```
385
-
386
- **TanStack Start (no adapter needed):**
387
-
388
- ```ts title="src/routes/api/$.ts"
389
- import { createAPIFileRoute } from "@tanstack/react-start/api";
390
- import { createFetchHandler } from "questpie/http";
391
- import { app } from "#questpie";
392
-
393
- const handler = createFetchHandler(app, { basePath: "/api" });
394
- export const Route = createAPIFileRoute("/api/$")({
395
- GET: ({ request }) => handler(request),
396
- POST: ({ request }) => handler(request),
397
- PATCH: ({ request }) => handler(request),
398
- DELETE: ({ request }) => handler(request),
399
- });
400
- ```
401
-
402
- ## Type Registries
403
-
404
- Three augmentation interfaces allow plugins to extend discriminant types:
405
-
406
- | Interface | Package | Purpose | Fallback |
407
- | ----------------------- | ------------------------ | ------------------------------------------------ | ------------- |
408
- | `FieldTypeRegistry` | `questpie` | Field type names (`"text"`, `"number"`, etc.) | `string` |
409
- | `ComponentTypeRegistry` | `@questpie/admin/server` | Component type names (`"icon"`, `"badge"`, etc.) | `string` |
410
- | `ViewKindRegistry` | `@questpie/admin/server` | View kind names (`"list"`, `"edit"`) | literal union |
411
-
412
- ### How Registries Work
413
-
414
- ```text
415
- Server: f.text().required()
416
- -> Generated: { type: "text", options: {...} }
417
- -> Admin Client: fieldRegistry.get("text")
418
- -> React: <TextFieldRenderer value={...} onChange={...} />
419
- ```
420
-
421
- ### Extending Registries
422
-
423
- Place files in admin directory -- codegen discovers them automatically:
424
-
425
- ```text
426
- questpie/admin/
427
- fields/
428
- color.tsx # Custom color field renderer
429
- currency.tsx # Custom currency field renderer
430
- views/
431
- kanban.tsx # Custom kanban list view
432
- ```
433
-
434
- ### Module Augmentation Pattern
435
-
436
- Codegen generates `declare module` augmentations:
437
-
438
- ```ts
439
- declare global {
440
- namespace Questpie {
441
- interface FieldTypeRegistry {
442
- color: {};
443
- currency: {};
444
- }
445
- }
446
- }
447
- ```
448
-
449
- The companion type alias uses the `[keyof Registry] extends [never] ? string : keyof Registry` pattern to fall back to `string` when the registry is empty.
450
-
451
- ## Package Distribution
452
-
453
- ### Package Structure
454
-
455
- ```text
456
- packages/my-package/
457
- src/
458
- exports/ # Public API entry points
459
- index.ts # Main entry (.)
460
- client.ts # Client entry (./client)
461
- server.ts # Server entry (./server)
462
- dist/ # Build output (gitignored)
463
- tsdown.config.ts
464
- package.json
465
- ```
466
-
467
- ### Dual Exports Strategy
468
-
469
- ```json title="package.json"
470
- {
471
- "type": "module",
472
- "exports": {
473
- ".": {
474
- "types": "./dist/index.d.mts",
475
- "default": "./src/exports/index.ts"
476
- }
477
- },
478
- "publishConfig": {
479
- "exports": {
480
- ".": {
481
- "types": "./dist/index.d.mts",
482
- "default": "./dist/index.mjs"
483
- }
484
- }
485
- },
486
- "files": ["dist"]
487
- }
488
- ```
489
-
490
- During development: `exports.default` points to `.ts` source (no build step needed). When published: `publishConfig.exports` overrides with compiled `.mjs` + `.d.mts`.
491
-
492
- ### Build with tsdown
493
-
494
- ```ts title="tsdown.config.ts"
495
- import { defineConfig } from "tsdown";
496
-
497
- export default defineConfig({
498
- entry: ["src/exports/*.ts"],
499
- outDir: "dist",
500
- format: ["esm"],
501
- clean: true,
502
- dts: { sourcemap: false },
503
- unbundle: true,
504
- });
505
- ```
506
-
507
- ### Publishing a Module
508
-
509
- ```json title="package.json"
510
- {
511
- "name": "questpie-notifications",
512
- "main": "dist/index.js",
513
- "types": "dist/index.d.ts",
514
- "peerDependencies": {
515
- "questpie": "^2.0.0"
516
- }
517
- }
518
- ```
519
-
520
- ### Versioning with Changesets
521
-
522
- Core packages use lock-step versioning. Run `bun changeset` to create, `bun run version` to apply, `bun run release` to publish.
523
-
524
- ## Common Mistakes
525
-
526
- ### HIGH: Adding `& Record<string, unknown>` to Registry augmentation
527
-
528
- This erases named keys via index signature intersection. The resulting type becomes `string` instead of a union of literal keys.
529
-
530
- ```ts
531
- // WRONG -- erases literal types
532
- declare global {
533
- namespace Questpie {
534
- interface FieldTypeRegistry extends Record<string, unknown> {
535
- color: {};
536
- }
537
- }
538
- }
539
-
540
- // CORRECT -- only named keys
541
- declare global {
542
- namespace Questpie {
543
- interface FieldTypeRegistry {
544
- color: {};
545
- }
546
- }
547
- }
548
- ```
549
-
550
- ### HIGH: Stale `.js`/`.d.ts` artifacts in src/
551
-
552
- tsdown prefers `.js` over `.ts` when both exist. Delete stale artifacts before building:
553
-
554
- ```bash
555
- # Remove tsc incremental artifacts that may shadow .ts files
556
- find src -name '*.d.ts' -o -name '*.js' | xargs rm -f
557
- ```
558
-
559
- ### MEDIUM: Not including `"skills"` in package.json files array
560
-
561
- Skills must ship with the npm package. Add the directory to `files`:
562
-
563
- ```json
564
- {
565
- "files": ["dist", "skills"]
566
- }
567
- ```
568
-
569
- ### MEDIUM: Complex conditional mapped types collapsing to `{}` in tsdown `.d.ts` emit
570
-
571
- Ensure source types have literal generic parameters. If a type like `FilterViewsByKind<TKind>` collapses to `{}` in declaration output, provide explicit `as ViewDefinition<"name", "kind", Config>` casts.
572
-
573
- ### MEDIUM: Missing `extractFromModules` for new categories
574
-
575
- If a plugin declares a new category but does not set `extractFromModules: true`, module-contributed entities of that category will not appear in the generated `App*` type.
576
-
577
- ## References
578
-
579
- | Need to... | Read |
580
- | -------------------------------- | ---------------------------------- |
581
- | See full CodegenPlugin API | `references/codegen-plugin-api.md` |
582
- | Define collections and fields | `questpie-core/data-modeling` |
583
- | Set up access, hooks, validation | `questpie-core/rules` |
584
- | Add functions, jobs, routes | `questpie-core/business-logic` |