create-questpie 2.0.0 → 2.0.1
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 +1 -1
- package/package.json +1 -1
- package/templates/tanstack-start/AGENTS.md +55 -50
- package/templates/tanstack-start/CLAUDE.md +15 -15
- package/templates/tanstack-start/README.md +16 -10
- package/templates/tanstack-start/src/lib/client.ts +2 -2
- 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/globals/site-settings.global.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
package/dist/index.mjs
CHANGED
|
@@ -179,7 +179,7 @@ function isTextFile(filename) {
|
|
|
179
179
|
return TEXT_EXTENSIONS.has(ext);
|
|
180
180
|
}
|
|
181
181
|
async function replaceInFile(filePath, vars) {
|
|
182
|
-
const content = await readFile(filePath
|
|
182
|
+
const content = (await readFile(filePath)).toString("utf-8");
|
|
183
183
|
const replaced = content.replace(TEMPLATE_VAR_REGEX, (match, key) => {
|
|
184
184
|
return key in vars ? vars[key] : match;
|
|
185
185
|
});
|
package/package.json
CHANGED
|
@@ -115,8 +115,8 @@ src/
|
|
|
115
115
|
- **`src/questpie/server/questpie.config.ts`** — App config: `runtimeConfig({ db, app, ... })`.
|
|
116
116
|
- **`src/questpie/server/modules.ts`** — Module dependencies: `export default [adminModule, openApiModule] as const`.
|
|
117
117
|
- **`src/questpie/server/config/auth.ts`** — Auth config via `authConfig()` factory.
|
|
118
|
-
- **`src/questpie/server/config/app.ts`** — App config (locale, access, hooks, context) via `appConfig()` factory.
|
|
119
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
120
|
- **`src/questpie/server/config/openapi.ts`** — OpenAPI config via `openApiConfig()` factory.
|
|
121
121
|
- **`src/questpie/server/.generated/index.ts`** — Codegen output. Exports typed `app` instance and `App` type. Run `bunx questpie generate` to regenerate.
|
|
122
122
|
- **`src/questpie/admin/.generated/client.ts`** — Codegen output: pre-built admin client config. Run `bunx questpie generate` to regenerate.
|
|
@@ -132,7 +132,7 @@ Keep the entire builder chain in one file — single source of truth per entity:
|
|
|
132
132
|
|
|
133
133
|
```ts
|
|
134
134
|
// src/questpie/server/collections/posts.ts
|
|
135
|
-
import { collection } from "questpie";
|
|
135
|
+
import { collection } from "#questpie/factories";
|
|
136
136
|
|
|
137
137
|
export const posts = collection("posts")
|
|
138
138
|
.fields(({ f }) => ({
|
|
@@ -154,10 +154,13 @@ export const posts = collection("posts")
|
|
|
154
154
|
content: f.richText().label("Content"),
|
|
155
155
|
published: f.boolean().label("Published").default(false),
|
|
156
156
|
category: f
|
|
157
|
-
.select(
|
|
157
|
+
.select([
|
|
158
|
+
{ value: "news", label: "News" },
|
|
159
|
+
{ value: "blog", label: "Blog" },
|
|
160
|
+
{ value: "tutorial", label: "Tutorial" },
|
|
161
|
+
])
|
|
158
162
|
.label("Category")
|
|
159
|
-
|
|
160
|
-
author: f.relation().label("Author").to("users"),
|
|
163
|
+
author: f.relation("users").label("Author"),
|
|
161
164
|
image: f.upload().label("Cover Image"),
|
|
162
165
|
}))
|
|
163
166
|
.title(({ f }) => f.title)
|
|
@@ -209,9 +212,9 @@ Collections are auto-discovered by codegen — no manual registration needed.
|
|
|
209
212
|
|
|
210
213
|
```ts
|
|
211
214
|
// src/questpie/server/globals/site-settings.ts
|
|
212
|
-
import { global } from "questpie";
|
|
215
|
+
import { global } from "#questpie/factories";
|
|
213
216
|
|
|
214
|
-
export const siteSettings = global("
|
|
217
|
+
export const siteSettings = global("siteSettings")
|
|
215
218
|
.fields(({ f }) => ({
|
|
216
219
|
siteName: f.text(255).label("Site Name").required(),
|
|
217
220
|
description: f.textarea().label("Description"),
|
|
@@ -390,10 +393,9 @@ All reactive handlers run **server-side** with access to `ctx.db`, `ctx.user`, `
|
|
|
390
393
|
|
|
391
394
|
```ts
|
|
392
395
|
fields: ({ f }) => ({
|
|
393
|
-
country: f.relation(
|
|
396
|
+
country: f.relation("countries").label("Country"),
|
|
394
397
|
city: f
|
|
395
|
-
.relation()
|
|
396
|
-
.to("cities")
|
|
398
|
+
.relation("cities")
|
|
397
399
|
.label("City")
|
|
398
400
|
.admin({
|
|
399
401
|
options: {
|
|
@@ -409,9 +411,12 @@ fields: ({ f }) => ({
|
|
|
409
411
|
},
|
|
410
412
|
}),
|
|
411
413
|
status: f
|
|
412
|
-
.select(
|
|
414
|
+
.select([
|
|
415
|
+
{ value: "draft", label: "Draft" },
|
|
416
|
+
{ value: "published", label: "Published" },
|
|
417
|
+
{ value: "archived", label: "Archived" },
|
|
418
|
+
])
|
|
413
419
|
.label("Status")
|
|
414
|
-
.options(["draft", "published", "archived"]),
|
|
415
420
|
publishedAt: f
|
|
416
421
|
.datetime()
|
|
417
422
|
.label("Published At")
|
|
@@ -458,7 +463,7 @@ import { app } from "#questpie";
|
|
|
458
463
|
const handler = createFetchHandler(app, { basePath: "/api" });
|
|
459
464
|
```
|
|
460
465
|
|
|
461
|
-
OpenAPI is registered as a module in `src/questpie/server/modules.ts` via `openApiModule
|
|
466
|
+
OpenAPI is registered as a module in `src/questpie/server/modules.ts` via `openApiModule` — no wrapper needed in the route handler.
|
|
462
467
|
|
|
463
468
|
### Icons
|
|
464
469
|
|
|
@@ -525,15 +530,14 @@ Always use these exact versions — check `package.json` before upgrading:
|
|
|
525
530
|
|
|
526
531
|
## Live Preview
|
|
527
532
|
|
|
528
|
-
QUESTPIE supports live preview with a split-screen editor. The preview
|
|
533
|
+
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.
|
|
529
534
|
|
|
530
535
|
### Key Principles
|
|
531
536
|
|
|
532
|
-
- **Same-tab preview** =
|
|
533
|
-
- **
|
|
534
|
-
- **
|
|
535
|
-
- **
|
|
536
|
-
- **Preview wrappers must prevent accidental navigation** inside the iframe
|
|
537
|
+
- **Same-tab preview** = iframe refresh after save/autosave plus `postMessage` focus events
|
|
538
|
+
- **Frontend hook** = `useCollectionPreview({ initialData, onRefresh })`
|
|
539
|
+
- **Field focus** = `PreviewProvider` + `PreviewField`
|
|
540
|
+
- **Block field paths** = `BlockScopeProvider` + `PreviewField`
|
|
537
541
|
|
|
538
542
|
### Server Config
|
|
539
543
|
|
|
@@ -541,7 +545,7 @@ Add `.preview()` to a collection to enable the split-screen editor:
|
|
|
541
545
|
|
|
542
546
|
```ts
|
|
543
547
|
// src/questpie/server/collections/pages.ts
|
|
544
|
-
import { collection } from "questpie";
|
|
548
|
+
import { collection } from "#questpie/factories";
|
|
545
549
|
|
|
546
550
|
export const pages = collection("pages")
|
|
547
551
|
.fields(({ f }) => ({
|
|
@@ -550,62 +554,63 @@ export const pages = collection("pages")
|
|
|
550
554
|
content: f.blocks().label("Content"),
|
|
551
555
|
}))
|
|
552
556
|
.preview({
|
|
557
|
+
enabled: true,
|
|
558
|
+
position: "right",
|
|
559
|
+
defaultWidth: 50,
|
|
553
560
|
url: ({ record }) => {
|
|
554
561
|
const slug = record.slug as string;
|
|
555
562
|
return slug === "home" ? "/?preview=true" : `/${slug}?preview=true`;
|
|
556
563
|
},
|
|
557
|
-
watch: ["title", "slug", "content"],
|
|
558
|
-
strategy: "hybrid", // "instant" | "server" | "hybrid"
|
|
559
564
|
});
|
|
560
565
|
```
|
|
561
566
|
|
|
562
|
-
| Strategy | Behavior |
|
|
563
|
-
| ----------- | ------------------------------------------------------------------------ |
|
|
564
|
-
| `"instant"` | Patches applied locally only — no server round-trip |
|
|
565
|
-
| `"server"` | Every change round-trips through the server before preview updates |
|
|
566
|
-
| `"hybrid"` | Local patches for instant response + server reconcile for derived fields |
|
|
567
|
-
|
|
568
567
|
### Frontend Integration
|
|
569
568
|
|
|
570
|
-
Use `
|
|
569
|
+
Use `useCollectionPreview` in your frontend page components:
|
|
571
570
|
|
|
572
571
|
```tsx
|
|
573
572
|
// src/routes/[slug].tsx
|
|
574
|
-
import {
|
|
573
|
+
import {
|
|
574
|
+
PreviewField,
|
|
575
|
+
PreviewProvider,
|
|
576
|
+
useCollectionPreview,
|
|
577
|
+
} from "@questpie/admin/client";
|
|
575
578
|
|
|
576
579
|
function PageComponent({ initialData }) {
|
|
577
580
|
const router = useRouter();
|
|
578
|
-
const
|
|
581
|
+
const preview = useCollectionPreview({
|
|
579
582
|
initialData,
|
|
580
|
-
|
|
583
|
+
onRefresh: () => router.invalidate(),
|
|
581
584
|
});
|
|
582
585
|
|
|
583
586
|
return (
|
|
584
|
-
<
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
587
|
+
<PreviewProvider
|
|
588
|
+
isPreviewMode={preview.isPreviewMode}
|
|
589
|
+
focusedField={preview.focusedField}
|
|
590
|
+
onFieldClick={preview.handleFieldClick}
|
|
591
|
+
>
|
|
592
|
+
<PreviewField field="title" as="h1">
|
|
593
|
+
{preview.data.title}
|
|
594
|
+
</PreviewField>
|
|
595
|
+
</PreviewProvider>
|
|
590
596
|
);
|
|
591
597
|
}
|
|
592
598
|
```
|
|
593
599
|
|
|
594
600
|
#### Protocol
|
|
595
601
|
|
|
596
|
-
|
|
602
|
+
The implemented preview messages are simple `postMessage` events:
|
|
597
603
|
|
|
598
|
-
| Field | Description
|
|
599
|
-
| ----------------- |
|
|
600
|
-
| `
|
|
601
|
-
| `
|
|
602
|
-
| `
|
|
603
|
-
| `
|
|
604
|
-
| `
|
|
604
|
+
| Field | Description |
|
|
605
|
+
| ----------------- | --------------------------------------- |
|
|
606
|
+
| `PREVIEW_READY` | Preview iframe tells admin it is ready |
|
|
607
|
+
| `PREVIEW_REFRESH` | Admin asks iframe to refresh data |
|
|
608
|
+
| `FIELD_CLICKED` | Preview asks admin to focus a field |
|
|
609
|
+
| `BLOCK_CLICKED` | Preview asks admin to select a block |
|
|
610
|
+
| `FOCUS_FIELD` | Admin asks preview to highlight a field |
|
|
611
|
+
| `SELECT_BLOCK` | Admin asks preview to highlight a block |
|
|
605
612
|
|
|
606
613
|
### Anti-Patterns (Preview)
|
|
607
614
|
|
|
608
|
-
- **Using
|
|
609
|
-
- **
|
|
610
|
-
- **Using realtime transport for same-tab preview** — realtime (SSE/WebSocket) is for detached or shared preview sessions, not the default same-tab flow.
|
|
611
|
-
- **Allowing navigation inside the preview iframe** — preview wrappers must intercept link clicks and route changes to prevent the iframe from navigating away.
|
|
615
|
+
- **Using V2-only APIs in this template** — `useQuestpiePreview`, `PreviewRoot`, and `PreviewBlock` are not exported yet.
|
|
616
|
+
- **Importing `app` inside previewed collection/block files** — use handler `ctx` values to avoid generated-app cycles.
|
|
@@ -4,18 +4,18 @@ This is a [QUESTPIE](https://questpie.com) project scaffolded with `create-quest
|
|
|
4
4
|
|
|
5
5
|
## Quick Reference
|
|
6
6
|
|
|
7
|
-
| Command
|
|
8
|
-
|
|
|
9
|
-
| `bun dev`
|
|
10
|
-
| `bun build`
|
|
11
|
-
| `bun start`
|
|
12
|
-
| `bun questpie add <type> <name>`
|
|
13
|
-
| `bun questpie add --list`
|
|
14
|
-
| `bun questpie generate`
|
|
15
|
-
| `bun questpie migrate:create`
|
|
16
|
-
| `bun questpie migrate`
|
|
17
|
-
| `bun questpie seed`
|
|
18
|
-
| `docker compose up -d`
|
|
7
|
+
| Command | Purpose |
|
|
8
|
+
| -------------------------------- | ---------------------------------------------- |
|
|
9
|
+
| `bun dev` | Start dev server (port 3000) |
|
|
10
|
+
| `bun build` | Build for production |
|
|
11
|
+
| `bun start` | Start production server |
|
|
12
|
+
| `bun questpie add <type> <name>` | Scaffold a new entity (collection, seed, etc.) |
|
|
13
|
+
| `bun questpie add --list` | List all available scaffold types |
|
|
14
|
+
| `bun questpie generate` | Regenerate .generated/index.ts |
|
|
15
|
+
| `bun questpie migrate:create` | Generate a migration from schema diff |
|
|
16
|
+
| `bun questpie migrate` | Run pending migrations |
|
|
17
|
+
| `bun questpie seed` | Run pending seeds |
|
|
18
|
+
| `docker compose up -d` | Start PostgreSQL |
|
|
19
19
|
|
|
20
20
|
## Project Architecture
|
|
21
21
|
|
|
@@ -31,9 +31,9 @@ src/questpie/
|
|
|
31
31
|
modules.ts ← Module dependencies (adminModule, openApiModule, etc.)
|
|
32
32
|
config/ ← Typed configuration files
|
|
33
33
|
auth.ts ← authConfig({...}) — Better Auth options
|
|
34
|
-
app.ts ← appConfig({ locale, access, hooks, context })
|
|
35
34
|
admin.ts ← adminConfig({ sidebar, dashboard, branding, locale })
|
|
36
35
|
openapi.ts ← openApiConfig({ info, scalar })
|
|
36
|
+
app.ts ← (optional) appConfig({ locale, access, hooks, context })
|
|
37
37
|
.generated/ ← Codegen output (app instance + App type)
|
|
38
38
|
index.ts
|
|
39
39
|
collections/ ← One file per collection (auto-discovered)
|
|
@@ -52,8 +52,8 @@ src/questpie/
|
|
|
52
52
|
- **`src/questpie/server/questpie.config.ts`** — App config: `runtimeConfig({ db, app, ... })`.
|
|
53
53
|
- **`src/questpie/server/modules.ts`** — Module dependencies: `export default [adminModule, openApiModule] as const`.
|
|
54
54
|
- **`src/questpie/server/config/auth.ts`** — Auth config via `authConfig()` factory.
|
|
55
|
-
- **`src/questpie/server/config/app.ts`** — App config (locale, access, hooks, context) via `appConfig()` factory.
|
|
56
55
|
- **`src/questpie/server/config/admin.ts`** — Admin config (sidebar, dashboard, branding, locale) via `adminConfig()` factory.
|
|
56
|
+
- **`src/questpie/server/config/app.ts`** — *(optional, not scaffolded)* App config (locale, access, hooks, context) via `appConfig()`. Create when needed.
|
|
57
57
|
- **`src/questpie/server/.generated/index.ts`** — Codegen output. Exports typed `app` instance and `App` type. Run `bunx questpie generate` to regenerate.
|
|
58
58
|
- **`src/lib/env.ts`** — Type-safe env variables via `@t3-oss/env-core`. Add new env vars here with Zod schemas.
|
|
59
59
|
- **`questpie.config.ts`** — CLI config (migration directory, app reference).
|
|
@@ -87,7 +87,7 @@ Manual workflow:
|
|
|
87
87
|
|
|
88
88
|
1. Create `src/questpie/server/collections/my-thing.ts` with a named export:
|
|
89
89
|
```ts
|
|
90
|
-
import { collection } from "questpie";
|
|
90
|
+
import { collection } from "#questpie/factories";
|
|
91
91
|
export const myThing = collection("my-thing").fields(({ f }) => ({ ... }));
|
|
92
92
|
```
|
|
93
93
|
2. Run `bunx questpie generate` to regenerate `.generated/index.ts`
|
|
@@ -33,6 +33,10 @@ src/
|
|
|
33
33
|
server/
|
|
34
34
|
questpie.config.ts # Runtime config
|
|
35
35
|
modules.ts # Module list (admin/openapi/...)
|
|
36
|
+
config/
|
|
37
|
+
admin.ts # Admin sidebar/dashboard/branding
|
|
38
|
+
auth.ts # Auth config
|
|
39
|
+
openapi.ts # OpenAPI/Scalar config
|
|
36
40
|
app.ts # Re-export of generated app
|
|
37
41
|
.generated/ # Codegen output (do not edit manually)
|
|
38
42
|
collections/
|
|
@@ -41,6 +45,8 @@ src/
|
|
|
41
45
|
site-settings.global.ts
|
|
42
46
|
admin/
|
|
43
47
|
admin.ts # Re-export of generated admin config
|
|
48
|
+
modules.ts # Admin client module defaults
|
|
49
|
+
.generated/ # Admin client codegen output
|
|
44
50
|
routes/
|
|
45
51
|
api/$.ts # QUESTPIE fetch handler mount
|
|
46
52
|
admin.tsx
|
|
@@ -55,16 +61,16 @@ migrations/
|
|
|
55
61
|
|
|
56
62
|
## Scripts
|
|
57
63
|
|
|
58
|
-
| Command
|
|
59
|
-
|
|
|
60
|
-
| `bun dev`
|
|
61
|
-
| `bun build`
|
|
62
|
-
| `bun start`
|
|
63
|
-
| `bun check-types`
|
|
64
|
-
| `bun questpie add <type> <name>` | Scaffold entity files (auto-runs codegen)
|
|
65
|
-
| `bun questpie migrate`
|
|
66
|
-
| `bun questpie migrate:create`
|
|
67
|
-
| `bunx questpie generate`
|
|
64
|
+
| Command | Description |
|
|
65
|
+
| -------------------------------- | --------------------------------------------- |
|
|
66
|
+
| `bun dev` | Start development server |
|
|
67
|
+
| `bun build` | Build for production |
|
|
68
|
+
| `bun start` | Start production server |
|
|
69
|
+
| `bun check-types` | Type check |
|
|
70
|
+
| `bun questpie add <type> <name>` | Scaffold entity files (auto-runs codegen) |
|
|
71
|
+
| `bun questpie migrate` | Run migrations |
|
|
72
|
+
| `bun questpie migrate:create` | Create migration |
|
|
73
|
+
| `bunx questpie generate` | Regenerate `src/questpie/server/.generated/*` |
|
|
68
74
|
|
|
69
75
|
## Adding a Collection
|
|
70
76
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createClient } from "questpie/client";
|
|
2
2
|
|
|
3
|
-
import type { AppConfig
|
|
3
|
+
import type { AppConfig } from "@/questpie/server/app.js";
|
|
4
4
|
|
|
5
|
-
export const client = createClient<AppConfig
|
|
5
|
+
export const client = createClient<AppConfig>({
|
|
6
6
|
baseURL:
|
|
7
7
|
typeof window !== "undefined"
|
|
8
8
|
? window.location.origin
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Client Config
|
|
3
|
+
*
|
|
4
|
+
* Auto-generated by questpie codegen — do not edit.
|
|
5
|
+
*
|
|
6
|
+
* Pass this directly to <AdminLayoutProvider admin={admin} />
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { default as _modules } from "../modules";
|
|
10
|
+
|
|
11
|
+
const admin = _modules;
|
|
12
|
+
|
|
13
|
+
export default admin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@questpie/admin/client-module";
|
|
@@ -3,63 +3,51 @@
|
|
|
3
3
|
// Typed factory functions with plugin extensions. Regenerate with: questpie generate
|
|
4
4
|
|
|
5
5
|
// ── Core Imports ───────────────────────────────────────────
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
import { CollectionBuilder, GlobalBuilder, wrapBuilderWithExtensions, builtinFields, type EmptyCollectionState, type EmptyGlobalState, type BuiltinFields, Field } from "questpie";
|
|
7
|
+
|
|
8
|
+
// ── Runtime Field Imports ──────────────────────────────────
|
|
9
|
+
import { adminFields } from "@questpie/admin/server";
|
|
10
|
+
|
|
11
|
+
const _fieldExt: Record<string, { stateKey: string; resolve: (v: any) => any }> = {
|
|
12
|
+
admin: { stateKey: "admin", resolve: (v: any) => v },
|
|
13
|
+
form: {
|
|
14
|
+
stateKey: "form",
|
|
15
|
+
resolve(configOrFn: any) {
|
|
16
|
+
if (typeof configOrFn === 'function') return configOrFn({ f: createFieldNameProxy() });
|
|
17
|
+
return configOrFn;
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Merged field factories — builtins + module-contributed (e.g. richText, blocks) + user fields
|
|
23
|
+
const _rawFieldDefs = { ...builtinFields, ...adminFields } as const;
|
|
24
|
+
|
|
25
|
+
// Wrap field factories so returned Field instances have extension methods
|
|
26
|
+
function _wrapFieldFactory(fn: (...args: any[]) => any): (...args: any[]) => any {
|
|
27
|
+
return (...args: any[]) => wrapBuilderWithExtensions(fn(...args), _fieldExt, Field);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const _allFieldDefs = Object.fromEntries(
|
|
31
|
+
Object.entries(_rawFieldDefs).map(([k, v]) => [k, _wrapFieldFactory(v as any)])
|
|
32
|
+
) as unknown as typeof _rawFieldDefs;
|
|
19
33
|
|
|
20
34
|
// ── Plugin Imports ─────────────────────────────────────────
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
type AdminGlobalConfig,
|
|
26
|
-
type AdminLocaleConfig,
|
|
27
|
-
createActionCallbackProxy,
|
|
28
|
-
createComponentCallbackProxy,
|
|
29
|
-
createViewCallbackProxy,
|
|
30
|
-
type DashboardContribution,
|
|
31
|
-
type FilterViewsByKind,
|
|
32
|
-
type FormViewConfig,
|
|
33
|
-
type FormViewConfigContext,
|
|
34
|
-
type ListViewConfig,
|
|
35
|
-
type ListViewConfigContext,
|
|
36
|
-
type PreviewConfig,
|
|
37
|
-
type ServerActionsConfig,
|
|
38
|
-
type ServerBrandingConfig,
|
|
39
|
-
type SidebarContribution,
|
|
40
|
-
} from "@questpie/admin/server";
|
|
35
|
+
import { type AdminCollectionConfig, type AdminConfigContext, type ListViewConfig, type ListViewConfigContext, type FilterViewsByKind, type FormViewConfig, type FormViewConfigContext, type PreviewConfig, type ServerActionsConfig, type ActionsConfigContext, type AdminGlobalConfig, type AdminConfigInput, createViewCallbackProxy, createComponentCallbackProxy, createActionCallbackProxy } from "@questpie/admin/server";
|
|
36
|
+
import { type AppConfigInput, type AuthConfig, createFieldNameProxy } from "questpie";
|
|
37
|
+
import { type OpenApiModuleConfig } from "@questpie/openapi";
|
|
38
|
+
|
|
41
39
|
// ════════════════════════════════════════════════════════════
|
|
42
|
-
// Type extraction
|
|
40
|
+
// Type extraction — driven by CategoryDeclaration
|
|
43
41
|
// ════════════════════════════════════════════════════════════
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
type
|
|
49
|
-
type
|
|
50
|
-
type _ComponentsNames =
|
|
51
|
-
| (keyof RegistryProp<"components"> & string)
|
|
52
|
-
| (string & {});
|
|
53
|
-
type _ComponentsNames_Strict = keyof RegistryProp<"components"> & string;
|
|
54
|
-
type _ComponentsRecord = RegistryProp<"components">;
|
|
43
|
+
type _ViewsNames = (keyof Questpie.ViewsRegistry & string) | (string & {});
|
|
44
|
+
type _ViewsRecord = Questpie.ViewsRegistry;
|
|
45
|
+
type _ComponentsNames = (keyof Questpie.ComponentsRegistry & string) | (string & {});
|
|
46
|
+
type _ComponentsNames_Strict = keyof Questpie.ComponentsRegistry & string;
|
|
47
|
+
type _ComponentsRecord = Questpie.ComponentsRegistry;
|
|
55
48
|
|
|
56
|
-
// Field types —
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
type _AllFieldTypes = ExtractModuleProp<
|
|
60
|
-
{ modules: typeof _modulesArr },
|
|
61
|
-
"fields"
|
|
62
|
-
>;
|
|
49
|
+
// Field types — populated by module codegen via Questpie.FieldTypesMap
|
|
50
|
+
type _AllFieldTypes = Questpie.FieldTypesMap;
|
|
63
51
|
|
|
64
52
|
// ════════════════════════════════════════════════════════════
|
|
65
53
|
// Type augmentations — generated from plugin registries
|
|
@@ -67,67 +55,25 @@ type _AllFieldTypes = ExtractModuleProp<
|
|
|
67
55
|
|
|
68
56
|
declare module "questpie" {
|
|
69
57
|
interface CollectionBuilder<TState> {
|
|
70
|
-
admin(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
| ((
|
|
74
|
-
ctx: AdminConfigContext<_ComponentsRecord>,
|
|
75
|
-
) => AdminCollectionConfig),
|
|
76
|
-
): CollectionBuilder<TState>;
|
|
77
|
-
list(
|
|
78
|
-
configFn: (
|
|
79
|
-
ctx: ListViewConfigContext<
|
|
80
|
-
TState extends {
|
|
81
|
-
fieldDefinitions: infer F extends Record<string, any>;
|
|
82
|
-
}
|
|
83
|
-
? F
|
|
84
|
-
: Record<string, any>,
|
|
85
|
-
FilterViewsByKind<_ViewsRecord, "list">
|
|
86
|
-
>,
|
|
87
|
-
) => ListViewConfig,
|
|
88
|
-
): CollectionBuilder<TState>;
|
|
89
|
-
form(
|
|
90
|
-
configFn: (
|
|
91
|
-
ctx: FormViewConfigContext<
|
|
92
|
-
TState extends {
|
|
93
|
-
fieldDefinitions: infer F extends Record<string, any>;
|
|
94
|
-
}
|
|
95
|
-
? F
|
|
96
|
-
: Record<string, any>,
|
|
97
|
-
FilterViewsByKind<_ViewsRecord, "form">
|
|
98
|
-
>,
|
|
99
|
-
) => FormViewConfig,
|
|
100
|
-
): CollectionBuilder<TState>;
|
|
58
|
+
admin(configFn: AdminCollectionConfig | ((ctx: AdminConfigContext<_ComponentsRecord>) => AdminCollectionConfig)): CollectionBuilder<TState>;
|
|
59
|
+
list(configFn: (ctx: ListViewConfigContext<TState extends { fieldDefinitions: infer F extends Record<string, any> } ? F : Record<string, any>, FilterViewsByKind<_ViewsRecord, "list">>) => ListViewConfig): CollectionBuilder<TState>;
|
|
60
|
+
form(configFn: (ctx: FormViewConfigContext<TState extends { fieldDefinitions: infer F extends Record<string, any> } ? F : Record<string, any>, FilterViewsByKind<_ViewsRecord, "form">>) => FormViewConfig): CollectionBuilder<TState>;
|
|
101
61
|
preview(config: PreviewConfig): CollectionBuilder<TState>;
|
|
102
|
-
actions(
|
|
103
|
-
configFn: (
|
|
104
|
-
ctx: ActionsConfigContext<Record<string, unknown>, _ComponentsRecord>,
|
|
105
|
-
) => ServerActionsConfig,
|
|
106
|
-
): CollectionBuilder<TState>;
|
|
62
|
+
actions(configFn: (ctx: ActionsConfigContext<Record<string, unknown>, _ComponentsRecord>) => ServerActionsConfig): CollectionBuilder<TState>;
|
|
107
63
|
}
|
|
108
64
|
interface GlobalBuilder<TState> {
|
|
109
|
-
admin(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
):
|
|
114
|
-
form(
|
|
115
|
-
configFn: (
|
|
116
|
-
ctx: FormViewConfigContext<
|
|
117
|
-
TState extends {
|
|
118
|
-
fieldDefinitions: infer F extends Record<string, any>;
|
|
119
|
-
}
|
|
120
|
-
? F
|
|
121
|
-
: Record<string, any>,
|
|
122
|
-
FilterViewsByKind<_ViewsRecord, "form">
|
|
123
|
-
>,
|
|
124
|
-
) => FormViewConfig,
|
|
125
|
-
): GlobalBuilder<TState>;
|
|
65
|
+
admin(configFn: AdminGlobalConfig | ((ctx: AdminConfigContext<_ComponentsRecord>) => AdminGlobalConfig)): GlobalBuilder<TState>;
|
|
66
|
+
form(configFn: (ctx: FormViewConfigContext<TState extends { fieldDefinitions: infer F extends Record<string, any> } ? F : Record<string, any>, FilterViewsByKind<_ViewsRecord, "form">>) => FormViewConfig): GlobalBuilder<TState>;
|
|
67
|
+
}
|
|
68
|
+
interface Field<TState> {
|
|
69
|
+
admin(config: unknown): Field<TState>;
|
|
70
|
+
form(configFn: (ctx: { f: Record<string, string> }) => { fields: import('@questpie/admin/server').FieldLayoutItem[] }): Field<TState>;
|
|
126
71
|
}
|
|
127
72
|
}
|
|
128
73
|
|
|
129
74
|
declare global {
|
|
130
75
|
namespace Questpie {
|
|
76
|
+
interface FieldTypesMap extends BuiltinFields {}
|
|
131
77
|
interface FieldTypeRegistry extends Record<keyof _AllFieldTypes, {}> {}
|
|
132
78
|
}
|
|
133
79
|
}
|
|
@@ -140,90 +86,54 @@ declare module "@questpie/admin/server" {
|
|
|
140
86
|
// Extension registries
|
|
141
87
|
// ════════════════════════════════════════════════════════════
|
|
142
88
|
|
|
143
|
-
const _collExt: Record<string, { stateKey: string; resolve: (v: any) => any }> =
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return configOrFn({ c: createComponentCallbackProxy() });
|
|
150
|
-
return configOrFn;
|
|
151
|
-
},
|
|
89
|
+
const _collExt: Record<string, { stateKey: string; resolve: (v: any) => any }> = {
|
|
90
|
+
admin: {
|
|
91
|
+
stateKey: "admin",
|
|
92
|
+
resolve(configOrFn: any) {
|
|
93
|
+
if (typeof configOrFn === 'function') return configOrFn({ c: createComponentCallbackProxy() });
|
|
94
|
+
return configOrFn;
|
|
152
95
|
},
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
v: createViewCallbackProxy(),
|
|
160
|
-
f: createFieldNameProxy(),
|
|
161
|
-
a: createActionCallbackProxy(),
|
|
162
|
-
})
|
|
163
|
-
: configOrFn;
|
|
164
|
-
return {
|
|
165
|
-
...{
|
|
166
|
-
view: "collection-table",
|
|
167
|
-
showSearch: true,
|
|
168
|
-
showFilters: true,
|
|
169
|
-
showToolbar: true,
|
|
170
|
-
},
|
|
171
|
-
...resolved,
|
|
172
|
-
};
|
|
173
|
-
},
|
|
96
|
+
},
|
|
97
|
+
list: {
|
|
98
|
+
stateKey: "adminList",
|
|
99
|
+
resolve(configOrFn: any) {
|
|
100
|
+
const resolved = typeof configOrFn === 'function' ? configOrFn({ v: createViewCallbackProxy(), f: createFieldNameProxy(), a: createActionCallbackProxy() }) : configOrFn;
|
|
101
|
+
return { ...{"view":"collection-table","showSearch":true,"showFilters":true,"showToolbar":true}, ...resolved };
|
|
174
102
|
},
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
v: createViewCallbackProxy(),
|
|
182
|
-
f: createFieldNameProxy(),
|
|
183
|
-
})
|
|
184
|
-
: configOrFn;
|
|
185
|
-
return { ...{ view: "collection-form", showMeta: true }, ...resolved };
|
|
186
|
-
},
|
|
103
|
+
},
|
|
104
|
+
form: {
|
|
105
|
+
stateKey: "adminForm",
|
|
106
|
+
resolve(configOrFn: any) {
|
|
107
|
+
const resolved = typeof configOrFn === 'function' ? configOrFn({ v: createViewCallbackProxy(), f: createFieldNameProxy() }) : configOrFn;
|
|
108
|
+
return { ...{"view":"collection-form","showMeta":true}, ...resolved };
|
|
187
109
|
},
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
c: createComponentCallbackProxy(),
|
|
196
|
-
f: createFieldNameProxy(),
|
|
197
|
-
});
|
|
198
|
-
return configOrFn;
|
|
199
|
-
},
|
|
110
|
+
},
|
|
111
|
+
preview: { stateKey: "adminPreview", resolve: (v: any) => v },
|
|
112
|
+
actions: {
|
|
113
|
+
stateKey: "adminActions",
|
|
114
|
+
resolve(configOrFn: any) {
|
|
115
|
+
if (typeof configOrFn === 'function') return configOrFn({ a: createActionCallbackProxy(), c: createComponentCallbackProxy(), f: createFieldNameProxy() });
|
|
116
|
+
return configOrFn;
|
|
200
117
|
},
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return configOrFn;
|
|
211
|
-
},
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const _globExt: Record<string, { stateKey: string; resolve: (v: any) => any }> = {
|
|
122
|
+
admin: {
|
|
123
|
+
stateKey: "admin",
|
|
124
|
+
resolve(configOrFn: any) {
|
|
125
|
+
if (typeof configOrFn === 'function') return configOrFn({ c: createComponentCallbackProxy() });
|
|
126
|
+
return configOrFn;
|
|
212
127
|
},
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
v: createViewCallbackProxy(),
|
|
220
|
-
f: createFieldNameProxy(),
|
|
221
|
-
})
|
|
222
|
-
: configOrFn;
|
|
223
|
-
return { ...{ view: "global-form", showMeta: true }, ...resolved };
|
|
224
|
-
},
|
|
128
|
+
},
|
|
129
|
+
form: {
|
|
130
|
+
stateKey: "adminForm",
|
|
131
|
+
resolve(configOrFn: any) {
|
|
132
|
+
const resolved = typeof configOrFn === 'function' ? configOrFn({ v: createViewCallbackProxy(), f: createFieldNameProxy() }) : configOrFn;
|
|
133
|
+
return { ...{"view":"global-form","showMeta":true}, ...resolved };
|
|
225
134
|
},
|
|
226
|
-
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
227
137
|
|
|
228
138
|
// ════════════════════════════════════════════════════════════
|
|
229
139
|
// Factory functions
|
|
@@ -237,82 +147,48 @@ const _globExt: Record<string, { stateKey: string; resolve: (v: any) => any }> =
|
|
|
237
147
|
* import { collection } from "#questpie/factories";
|
|
238
148
|
*
|
|
239
149
|
* export default collection("posts")
|
|
240
|
-
* .fields(({ f }) => ({ title: f.text(
|
|
150
|
+
* .fields(({ f }) => ({ title: f.text(255).required() }))
|
|
241
151
|
* .admin(({ c }) => ({ icon: c.icon("ph:article") }))
|
|
242
152
|
* .list(({ v, f }) => v.collectionTable({ columns: [f.title] }))
|
|
243
153
|
* ```
|
|
244
154
|
*/
|
|
245
|
-
export function collection<TName extends string>(
|
|
246
|
-
|
|
247
|
-
): CollectionBuilder<EmptyCollectionState<TName, undefined, _AllFieldTypes>> {
|
|
248
|
-
return wrapBuilderWithExtensions(
|
|
249
|
-
CollectionBuilder.create<TName, _AllFieldTypes>(name),
|
|
250
|
-
_collExt,
|
|
251
|
-
CollectionBuilder,
|
|
252
|
-
) as any;
|
|
155
|
+
export function collection<TName extends string>(name: TName): CollectionBuilder<EmptyCollectionState<TName, undefined, _AllFieldTypes>> {
|
|
156
|
+
return wrapBuilderWithExtensions(CollectionBuilder.create<TName, _AllFieldTypes>(name, _allFieldDefs), _collExt, CollectionBuilder) as any;
|
|
253
157
|
}
|
|
254
158
|
|
|
255
159
|
/**
|
|
256
160
|
* Create a typed global builder with plugin extensions.
|
|
257
161
|
*/
|
|
258
|
-
export function global<TName extends string>(
|
|
259
|
-
|
|
260
|
-
): GlobalBuilder<EmptyGlobalState<TName, undefined, _AllFieldTypes>> {
|
|
261
|
-
return wrapBuilderWithExtensions(
|
|
262
|
-
GlobalBuilder.create<TName, _AllFieldTypes>(name),
|
|
263
|
-
_globExt,
|
|
264
|
-
GlobalBuilder,
|
|
265
|
-
) as any;
|
|
162
|
+
export function global<TName extends string>(name: TName): GlobalBuilder<EmptyGlobalState<TName, undefined, _AllFieldTypes>> {
|
|
163
|
+
return wrapBuilderWithExtensions(GlobalBuilder.create<TName, _AllFieldTypes>(name, _allFieldDefs), _globExt, GlobalBuilder) as any;
|
|
266
164
|
}
|
|
267
165
|
|
|
268
166
|
// ════════════════════════════════════════════════════════════
|
|
269
|
-
//
|
|
167
|
+
// Builder factory functions (plugin-contributed)
|
|
270
168
|
// ════════════════════════════════════════════════════════════
|
|
271
169
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
export function hooks<T extends GlobalHooksInput>(config: T): T {
|
|
279
|
-
return config;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/** Typed factory for access config. */
|
|
283
|
-
export function access<T extends CollectionAccess>(config: T): T {
|
|
284
|
-
return config;
|
|
170
|
+
import { BlockBuilder } from "@questpie/admin/server";
|
|
171
|
+
/**
|
|
172
|
+
* Create a typed block builder with wrapped field defs.
|
|
173
|
+
*/
|
|
174
|
+
export function block<TName extends string>(name: TName): import('@questpie/admin/server').BlockBuilder<{ name: TName }> {
|
|
175
|
+
return BlockBuilder.create(name, _allFieldDefs) as any;
|
|
285
176
|
}
|
|
286
177
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
178
|
+
// ════════════════════════════════════════════════════════════
|
|
179
|
+
// Singleton factory functions
|
|
180
|
+
// ════════════════════════════════════════════════════════════
|
|
291
181
|
|
|
292
|
-
/** Typed factory for
|
|
293
|
-
export function
|
|
294
|
-
return config;
|
|
295
|
-
}
|
|
182
|
+
/** Typed factory for appConfig config. */
|
|
183
|
+
export function appConfig<T extends AppConfigInput>(config: T): T { return config; }
|
|
296
184
|
|
|
297
|
-
/** Typed factory for
|
|
298
|
-
export function
|
|
299
|
-
return config;
|
|
300
|
-
}
|
|
185
|
+
/** Typed factory for authConfig config. */
|
|
186
|
+
export function authConfig<T extends AuthConfig>(config: T): T { return config; }
|
|
301
187
|
|
|
302
|
-
/** Typed factory for
|
|
303
|
-
export function
|
|
304
|
-
export function
|
|
305
|
-
|
|
306
|
-
): T;
|
|
307
|
-
export function sidebar(v: any): any {
|
|
308
|
-
return v;
|
|
309
|
-
}
|
|
188
|
+
/** Typed factory for adminConfig config. Accepts plain config or callback. */
|
|
189
|
+
export function adminConfig<T extends AdminConfigInput>(config: T): T;
|
|
190
|
+
export function adminConfig<T extends (...args: any[]) => AdminConfigInput>(cb: T): T;
|
|
191
|
+
export function adminConfig(v: any): any { return v; }
|
|
310
192
|
|
|
311
|
-
/** Typed factory for
|
|
312
|
-
export function
|
|
313
|
-
export function dashboard<T extends (...args: any[]) => DashboardContribution>(
|
|
314
|
-
cb: T,
|
|
315
|
-
): T;
|
|
316
|
-
export function dashboard(v: any): any {
|
|
317
|
-
return v;
|
|
318
|
-
}
|
|
193
|
+
/** Typed factory for openapi config. */
|
|
194
|
+
export function openapi<T extends OpenApiModuleConfig>(config: T): T { return config; }
|
|
@@ -2,36 +2,34 @@
|
|
|
2
2
|
// AUTO-GENERATED by questpie codegen — DO NOT EDIT
|
|
3
3
|
// Regenerate with: questpie generate
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
type AppContext,
|
|
7
|
-
createApp,
|
|
8
|
-
createContextFactory,
|
|
9
|
-
type Questpie,
|
|
10
|
-
type QuestpieConfig,
|
|
11
|
-
} from "questpie";
|
|
12
|
-
// ── Module type extraction (inline, no recursive types) ───
|
|
13
|
-
import type { UnionToIntersection } from "questpie";
|
|
5
|
+
import { createApp, createContextFactory, extractAppServices, type AppDefinition, type ModuleDefinition, type AppContext, type Registry, type QueueClient, type CollectionAPI } from "questpie";
|
|
14
6
|
|
|
15
|
-
// ── Plugin Singles ─────────────────────────────────────────
|
|
16
|
-
import _branding from "../branding";
|
|
17
|
-
// ── Collections ────────────────────────────────────────────
|
|
18
|
-
import { posts as _coll_posts_collection } from "../collections/posts.collection";
|
|
19
|
-
import _dashboard from "../dashboard";
|
|
20
|
-
// ── Globals ────────────────────────────────────────────────
|
|
21
|
-
import { siteSettings as _glob_siteSettings_global } from "../globals/site-settings.global";
|
|
22
|
-
// ── Modules ────────────────────────────────────────────────
|
|
23
|
-
import _modules from "../modules";
|
|
24
7
|
// ── Runtime ────────────────────────────────────────────────
|
|
25
8
|
import _runtime from "../questpie.config";
|
|
9
|
+
|
|
10
|
+
// ── Modules ────────────────────────────────────────────────
|
|
11
|
+
import _modules from "../modules";
|
|
12
|
+
|
|
13
|
+
// ── Collections ────────────────────────────────────────────
|
|
14
|
+
import { posts as _coll_posts } from "../collections/posts.collection";
|
|
15
|
+
|
|
16
|
+
// ── Globals ────────────────────────────────────────────────
|
|
17
|
+
import { siteSettings as _glob_siteSettings } from "../globals/site-settings.global";
|
|
18
|
+
|
|
19
|
+
// ── Core Singles ───────────────────────────────────────────
|
|
20
|
+
import _authConfig from "../config/auth";
|
|
21
|
+
|
|
22
|
+
// ── Plugin Singles ─────────────────────────────────────────
|
|
23
|
+
import _adminConfig from "../config/admin";
|
|
24
|
+
import _openapi from "../config/openapi";
|
|
25
|
+
|
|
26
26
|
// ════════════════════════════════════════════════════════════
|
|
27
27
|
// TYPES — composed from typeof references (zero inference cost)
|
|
28
28
|
// ════════════════════════════════════════════════════════════
|
|
29
|
-
import _sidebar from "../sidebar";
|
|
30
29
|
|
|
30
|
+
import type { ServiceCustomNamespaceInstances, ServiceInstanceOf, ServiceInstancesInNamespace, ServiceTopLevelInstances, UnionToIntersection } from "questpie";
|
|
31
31
|
type _Module = (typeof _modules)[number];
|
|
32
|
-
type _MPRaw<K extends string> = UnionToIntersection<
|
|
33
|
-
_Module extends infer M ? (M extends Record<K, infer V> ? V : never) : never
|
|
34
|
-
>;
|
|
32
|
+
type _MPRaw<K extends string> = UnionToIntersection<_Module extends infer M ? M extends Record<K, infer V> ? V : never : never>;
|
|
35
33
|
type _MP<K extends string> = [_MPRaw<K>] extends [never] ? {} : _MPRaw<K>;
|
|
36
34
|
|
|
37
35
|
type _ModuleCollections = _MP<"collections">;
|
|
@@ -39,15 +37,35 @@ type _ModuleGlobals = _MP<"globals">;
|
|
|
39
37
|
type _ModuleJobs = _MP<"jobs">;
|
|
40
38
|
type _ModuleRoutes = _MP<"routes">;
|
|
41
39
|
type _ModuleServices = _MP<"services">;
|
|
40
|
+
type _ModuleFieldTypes = _MP<"fieldTypes">;
|
|
41
|
+
type _ModuleViews = _MP<"views">;
|
|
42
|
+
type _ModuleComponents = _MP<"components">;
|
|
43
|
+
type _ModuleBlocks = _MP<"blocks">;
|
|
44
|
+
// Registry category extraction from modules
|
|
45
|
+
type _Registry_Collections = _MP<"collections">;
|
|
46
|
+
type _Registry_Globals = _MP<"globals">;
|
|
47
|
+
type _Registry_Jobs = _MP<"jobs">;
|
|
48
|
+
type _Registry_Routes = _MP<"routes">;
|
|
49
|
+
type _Registry_Services = _MP<"services">;
|
|
50
|
+
type _Registry_Emails = _MP<"emails">;
|
|
51
|
+
type _Registry_FieldTypes = _MP<"fieldTypes">;
|
|
52
|
+
type _Registry_Views = _MP<"views">;
|
|
53
|
+
type _Registry_Components = _MP<"components">;
|
|
54
|
+
type _Registry_Blocks = _MP<"blocks">;
|
|
55
|
+
|
|
56
|
+
// Recursive module property extraction (for fields contributed at each level)
|
|
57
|
+
import type { ExtractModuleProp } from "questpie";
|
|
58
|
+
|
|
59
|
+
type _AllModuleFields = ExtractModuleProp<{ modules: typeof _modules }, "fields">;
|
|
42
60
|
|
|
43
61
|
/** All collections in the app (modules + user, user overrides) */
|
|
44
62
|
export type AppCollections = _ModuleCollections & {
|
|
45
|
-
|
|
63
|
+
posts: typeof _coll_posts;
|
|
46
64
|
};
|
|
47
65
|
|
|
48
66
|
/** All globals in the app (modules + user, user overrides) */
|
|
49
67
|
export type AppGlobals = _ModuleGlobals & {
|
|
50
|
-
|
|
68
|
+
siteSettings: typeof _glob_siteSettings;
|
|
51
69
|
};
|
|
52
70
|
|
|
53
71
|
/** All jobs in the app (modules + user, user overrides) */
|
|
@@ -56,51 +74,75 @@ export type AppJobs = _ModuleJobs;
|
|
|
56
74
|
/** All routes in the app (modules + user, user overrides) */
|
|
57
75
|
export type AppRoutes = _ModuleRoutes;
|
|
58
76
|
|
|
59
|
-
/** All
|
|
60
|
-
|
|
77
|
+
/** All service definitions in the app (modules + user, user overrides). */
|
|
78
|
+
type _AppServiceDefinitions = _ModuleServices;
|
|
61
79
|
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
/** All services in the app as resolved service instances. */
|
|
81
|
+
export type AppServices = {
|
|
82
|
+
[K in keyof _AppServiceDefinitions]: ServiceInstanceOf<_AppServiceDefinitions[K]>;
|
|
83
|
+
};
|
|
84
|
+
type _AppDefaultServices = ServiceInstancesInNamespace<_AppServiceDefinitions, "services">;
|
|
85
|
+
type _AppTopLevelServices = ServiceTopLevelInstances<_AppServiceDefinitions>;
|
|
86
|
+
type _AppCustomServiceNamespaces = ServiceCustomNamespaceInstances<_AppServiceDefinitions>;
|
|
87
|
+
|
|
88
|
+
/** All email templates in the app — use with email.sendTemplate() */
|
|
89
|
+
export type AppEmailTemplates = Record<string, never>;
|
|
90
|
+
|
|
91
|
+
/** All fieldtypes in the app (modules + user, user overrides) */
|
|
92
|
+
export type AppFieldTypes = _ModuleFieldTypes;
|
|
93
|
+
|
|
94
|
+
/** All views in the app (modules + user, user overrides) */
|
|
95
|
+
export type AppViews = _ModuleViews;
|
|
96
|
+
|
|
97
|
+
/** All components in the app (modules + user, user overrides) */
|
|
98
|
+
export type AppComponents = _ModuleComponents;
|
|
99
|
+
|
|
100
|
+
/** All blocks in the app (modules + user, user overrides) */
|
|
101
|
+
export type AppBlocks = _ModuleBlocks;
|
|
102
|
+
|
|
103
|
+
type _CollectionsAPI = { [K in keyof AppCollections]: CollectionAPI<AppCollections[K], AppCollections> };
|
|
79
104
|
|
|
80
105
|
// ── AppContext augmentation — auto-types ALL handlers ──────
|
|
81
|
-
declare
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
params?: Record<string, unknown>,
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
declare global {
|
|
107
|
+
namespace Questpie {
|
|
108
|
+
interface AppContext extends _AppTopLevelServices, _AppCustomServiceNamespaces {
|
|
109
|
+
// Infrastructure
|
|
110
|
+
db: (typeof app)['db'];
|
|
111
|
+
email: (typeof app)['email'];
|
|
112
|
+
queue: QueueClient<AppJobs>;
|
|
113
|
+
storage: (typeof app)['storage'];
|
|
114
|
+
kv: (typeof app)['kv'];
|
|
115
|
+
logger: (typeof app)['logger'];
|
|
116
|
+
search: (typeof app)['search'];
|
|
117
|
+
realtime: (typeof app)['realtime'];
|
|
118
|
+
|
|
119
|
+
// Entity APIs
|
|
120
|
+
collections: _CollectionsAPI;
|
|
121
|
+
globals: (typeof app)['globals'];
|
|
122
|
+
tables: (typeof app)['tables'];
|
|
123
|
+
|
|
124
|
+
// Request-scoped
|
|
125
|
+
session: (typeof app)['auth'] extends { api: { getSession: (...args: any[]) => Promise<infer TSession> } } ? NonNullable<TSession> | null : null;
|
|
126
|
+
t: (key: string, params?: Record<string, unknown>, locale?: string) => string;
|
|
127
|
+
|
|
128
|
+
// User services
|
|
129
|
+
services: _AppDefaultServices;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface ServiceCreateContext extends AppContext {}
|
|
133
|
+
|
|
134
|
+
interface Registry {
|
|
135
|
+
collections: _Registry_Collections;
|
|
136
|
+
globals: _Registry_Globals;
|
|
137
|
+
jobs: _Registry_Jobs;
|
|
138
|
+
routes: _Registry_Routes;
|
|
139
|
+
services: _Registry_Services;
|
|
140
|
+
emails: _Registry_Emails;
|
|
141
|
+
"~fieldTypes": _Registry_FieldTypes & _AllModuleFields;
|
|
142
|
+
views: _Registry_Views;
|
|
143
|
+
components: _Registry_Components;
|
|
144
|
+
blocks: _Registry_Blocks;
|
|
145
|
+
}
|
|
104
146
|
}
|
|
105
147
|
}
|
|
106
148
|
|
|
@@ -110,32 +152,36 @@ declare module "questpie" {
|
|
|
110
152
|
* For handler context, use `AppContext` (auto-typed via module augmentation).
|
|
111
153
|
*/
|
|
112
154
|
export type AppConfig = {
|
|
113
|
-
collections: AppCollections
|
|
114
|
-
globals: AppGlobals
|
|
155
|
+
collections: AppCollections & Record<string, any>;
|
|
156
|
+
globals: AppGlobals & Record<string, any>;
|
|
157
|
+
routes: AppRoutes;
|
|
158
|
+
auth: typeof _authConfig;
|
|
115
159
|
};
|
|
116
160
|
|
|
117
161
|
// ════════════════════════════════════════════════════════════
|
|
118
162
|
// RUNTIME — create the app instance
|
|
119
163
|
// ════════════════════════════════════════════════════════════
|
|
120
164
|
|
|
121
|
-
export const app =
|
|
122
|
-
{
|
|
123
|
-
modules: _modules as
|
|
165
|
+
export const app = await createApp(
|
|
166
|
+
({
|
|
167
|
+
modules: _modules as ModuleDefinition[],
|
|
124
168
|
collections: {
|
|
125
|
-
|
|
169
|
+
posts: _coll_posts,
|
|
126
170
|
},
|
|
127
171
|
globals: {
|
|
128
|
-
|
|
172
|
+
siteSettings: _glob_siteSettings,
|
|
173
|
+
},
|
|
174
|
+
config: {
|
|
175
|
+
auth: _authConfig as any,
|
|
176
|
+
admin: _adminConfig as any,
|
|
177
|
+
openapi: _openapi as any,
|
|
129
178
|
},
|
|
130
|
-
|
|
131
|
-
dashboard: _dashboard as any,
|
|
132
|
-
sidebar: _sidebar as any,
|
|
133
|
-
},
|
|
179
|
+
}) satisfies AppDefinition,
|
|
134
180
|
_runtime,
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
/** Fully typed QUESTPIE app instance. */
|
|
184
|
+
export type App = typeof app;
|
|
139
185
|
|
|
140
186
|
// ── createContext — typed context for scripts ──────────────
|
|
141
187
|
/**
|
|
@@ -151,3 +197,5 @@ export const app = (await createApp(
|
|
|
151
197
|
* ```
|
|
152
198
|
*/
|
|
153
199
|
export const createContext = createContextFactory(app);
|
|
200
|
+
|
|
201
|
+
// Factories: import { collection, global, ... } from '#questpie/factories';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { global } from "#questpie/factories";
|
|
2
2
|
|
|
3
|
-
export const siteSettings = global("
|
|
3
|
+
export const siteSettings = global("siteSettings")
|
|
4
4
|
.fields(({ f }) => ({
|
|
5
5
|
siteName: f.text().label("Site Name").required().default("{{projectName}}"),
|
|
6
6
|
description: f
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runtime-only configuration: database, adapters, secrets.
|
|
5
5
|
* Entity definitions (collections, globals, etc.) are codegen-generated.
|
|
6
|
-
*
|
|
6
|
+
* Admin sidebar, dashboard, and branding live in config/admin.ts.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ConsoleAdapter, runtimeConfig } from "questpie";
|