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 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, "utf-8");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-questpie",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Create a new QUESTPIE project",
5
5
  "keywords": [
6
6
  "create",
@@ -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
- .options(["news", "blog", "tutorial"]),
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("site_settings")
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().to("countries").label("Country"),
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()` — no wrapper needed in the route handler.
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 architecture uses a direct `postMessage` patch bus for instant feedback — NOT save-driven iframe reloads.
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** = direct patch bus via `postMessage` (editor iframe and preview share the same browser tab)
533
- - **Save/autosave** = persistence only, NOT the live transport mechanism
534
- - **Realtime** = extension for detached/shared preview only (e.g., another browser tab or collaborator)
535
- - **Hybrid model**: local draft patches give instant response; server reconciles derived data (slugs, relations, computed fields)
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 `useQuestpiePreview` in your frontend page components to receive live patches:
569
+ Use `useCollectionPreview` in your frontend page components:
571
570
 
572
571
  ```tsx
573
572
  // src/routes/[slug].tsx
574
- import { useQuestpiePreview } from "@questpie/admin/client";
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 { data } = useQuestpiePreview({
581
+ const preview = useCollectionPreview({
579
582
  initialData,
580
- reconcile: () => router.invalidate(), // called on COMMIT — refetch server data
583
+ onRefresh: () => router.invalidate(),
581
584
  });
582
585
 
583
586
  return (
584
- <PreviewRoot>
585
- <h1>
586
- <PreviewField path="title">{data.title}</PreviewField>
587
- </h1>
588
- <PreviewBlock id="content">{/* block renderers */}</PreviewBlock>
589
- </PreviewRoot>
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
- Each `postMessage` carries a structured envelope:
602
+ The implemented preview messages are simple `postMessage` events:
597
603
 
598
- | Field | Description |
599
- | ----------------- | -------------------------------------- |
600
- | `sessionId` | Unique preview session identifier |
601
- | `seq` | Monotonic sequence number |
602
- | `timestamp` | `Date.now()` at send time (number) |
603
- | `protocolVersion` | Protocol version for forward compat |
604
- | `patches` | Array of field path + value patch ops |
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 `router.invalidate()` as the core live-preview mechanism** — this causes full data refetches and visible flicker. Use the patch bus instead.
609
- - **Tying preview freshness to save/autosave** — save is for persistence. Preview updates flow through `postMessage` patches.
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 | 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 |
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 | Description |
59
- | ----------------------------- | --------------------------------------------- |
60
- | `bun dev` | Start development server |
61
- | `bun build` | Build for production |
62
- | `bun start` | Start production server |
63
- | `bun check-types` | Type check |
64
- | `bun questpie add <type> <name>` | Scaffold entity files (auto-runs codegen) |
65
- | `bun questpie migrate` | Run migrations |
66
- | `bun questpie migrate:create` | Create migration |
67
- | `bunx questpie generate` | Regenerate `src/questpie/server/.generated/*` |
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, AppRpc } from "@/questpie/server/app.js";
3
+ import type { AppConfig } from "@/questpie/server/app.js";
4
4
 
5
- export const client = createClient<AppConfig, AppRpc>({
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
- type CollectionAccess,
8
- CollectionBuilder,
9
- type ContextResolver,
10
- createFieldNameProxy,
11
- type EmptyCollectionState,
12
- type EmptyGlobalState,
13
- GlobalBuilder,
14
- type GlobalHooksInput,
15
- type LocaleConfig,
16
- wrapBuilderWithExtensions,
17
- } from "questpie";
18
- import type { RegistryProp } from "questpie";
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
- type ActionsConfigContext,
23
- type AdminCollectionConfig,
24
- type AdminConfigContext,
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 from Registry — driven by CategoryDeclaration
40
+ // Type extraction — driven by CategoryDeclaration
43
41
  // ════════════════════════════════════════════════════════════
44
42
 
45
- // ── Module import (for type extraction) ──
46
- import type _modulesArr from "../modules";
47
-
48
- type _ViewsNames = (keyof RegistryProp<"views"> & string) | (string & {});
49
- type _ViewsRecord = RegistryProp<"views">;
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 — extracted from modules
57
- import type { ExtractModuleProp } from "questpie";
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
- configFn:
72
- | AdminCollectionConfig
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
- configFn:
111
- | AdminGlobalConfig
112
- | ((ctx: AdminConfigContext<_ComponentsRecord>) => AdminGlobalConfig),
113
- ): GlobalBuilder<TState>;
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
- admin: {
146
- stateKey: "admin",
147
- resolve(configOrFn: any) {
148
- if (typeof configOrFn === "function")
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
- list: {
154
- stateKey: "adminList",
155
- resolve(configOrFn: any) {
156
- const resolved =
157
- typeof configOrFn === "function"
158
- ? configOrFn({
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
- form: {
176
- stateKey: "adminForm",
177
- resolve(configOrFn: any) {
178
- const resolved =
179
- typeof configOrFn === "function"
180
- ? configOrFn({
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
- preview: { stateKey: "adminPreview", resolve: (v: any) => v },
189
- actions: {
190
- stateKey: "adminActions",
191
- resolve(configOrFn: any) {
192
- if (typeof configOrFn === "function")
193
- return configOrFn({
194
- a: createActionCallbackProxy(),
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
- const _globExt: Record<string, { stateKey: string; resolve: (v: any) => any }> =
204
- {
205
- admin: {
206
- stateKey: "admin",
207
- resolve(configOrFn: any) {
208
- if (typeof configOrFn === "function")
209
- return configOrFn({ c: createComponentCallbackProxy() });
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
- form: {
214
- stateKey: "adminForm",
215
- resolve(configOrFn: any) {
216
- const resolved =
217
- typeof configOrFn === "function"
218
- ? configOrFn({
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({ required: true }) }))
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
- name: TName,
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
- name: TName,
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
- // Singleton factory functions
167
+ // Builder factory functions (plugin-contributed)
270
168
  // ════════════════════════════════════════════════════════════
271
169
 
272
- /** Typed factory for locale config. */
273
- export function locale<T extends LocaleConfig>(config: T): T {
274
- return config;
275
- }
276
-
277
- /** Typed factory for hooks config. */
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
- /** Typed factory for context config. */
288
- export function context<T extends ContextResolver>(config: T): T {
289
- return config;
290
- }
178
+ // ════════════════════════════════════════════════════════════
179
+ // Singleton factory functions
180
+ // ════════════════════════════════════════════════════════════
291
181
 
292
- /** Typed factory for branding config. */
293
- export function branding<T extends ServerBrandingConfig>(config: T): T {
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 adminLocale config. */
298
- export function adminLocale<T extends AdminLocaleConfig>(config: T): T {
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 sidebar config. Accepts plain config or callback. */
303
- export function sidebar<T extends SidebarContribution>(config: T): T;
304
- export function sidebar<T extends (...args: any[]) => SidebarContribution>(
305
- cb: T,
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 dashboard config. Accepts plain config or callback. */
312
- export function dashboard<T extends DashboardContribution>(config: T): T;
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
- "posts.collection": typeof _coll_posts_collection;
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
- "siteSettings.global": typeof _glob_siteSettings_global;
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 services in the app (modules + user, user overrides). Values are service instances. */
60
- export type AppServices = _ModuleServices;
77
+ /** All service definitions in the app (modules + user, user overrides). */
78
+ type _AppServiceDefinitions = _ModuleServices;
61
79
 
62
- /**
63
- * The fully-typed app instance type.
64
- * Use `typeof app` or this alias when you need to reference the app type.
65
- *
66
- * **Note:** Do NOT import `app` inside framework-defined files (collections,
67
- * globals, routes, hooks, blocks) — it creates circular dependencies with
68
- * the generated index. Use the context parameters provided to handlers instead.
69
- */
70
- export type App = Questpie<
71
- QuestpieConfig & {
72
- collections: AppCollections;
73
- globals: AppGlobals;
74
- jobs: AppJobs;
75
- routes: AppRoutes;
76
- services: AppServices;
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 module "questpie" {
82
- interface AppContext {
83
- // Infrastructure
84
- db: App["db"];
85
- email: App["email"];
86
- queue: App["queue"];
87
- storage: App["storage"];
88
- kv: App["kv"];
89
- logger: App["logger"];
90
- search: App["search"];
91
- realtime: App["realtime"];
92
-
93
- // Entity APIs
94
- collections: App["collections"];
95
- globals: App["globals"];
96
-
97
- // Request-scoped
98
- session: Awaited<ReturnType<App["auth"]["api"]["getSession"]>> | null;
99
- t: (
100
- key: string,
101
- params?: Record<string, unknown>,
102
- locale?: string,
103
- ) => string;
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 = (await createApp(
122
- {
123
- modules: _modules as any,
165
+ export const app = await createApp(
166
+ ({
167
+ modules: _modules as ModuleDefinition[],
124
168
  collections: {
125
- "posts.collection": _coll_posts_collection,
169
+ posts: _coll_posts,
126
170
  },
127
171
  globals: {
128
- "siteSettings.global": _glob_siteSettings_global,
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
- branding: _branding as any,
131
- dashboard: _dashboard as any,
132
- sidebar: _sidebar as any,
133
- },
179
+ }) satisfies AppDefinition,
134
180
  _runtime,
135
- )) as unknown as AppConfig & {
136
- waitForInit(): Promise<void>;
137
- destroy(): Promise<void>;
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';
@@ -7,4 +7,4 @@
7
7
  * If .generated/ does not exist yet, run: bun questpie generate
8
8
  */
9
9
 
10
- export { type App, app } from "./.generated/index";
10
+ export { type App, type AppConfig, app } from "./.generated/index";
@@ -1,6 +1,6 @@
1
1
  import { global } from "#questpie/factories";
2
2
 
3
- export const siteSettings = global("site_settings")
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
- * Sidebar, dashboard, branding are file conventions.
6
+ * Admin sidebar, dashboard, and branding live in config/admin.ts.
7
7
  */
8
8
 
9
9
  import { ConsoleAdapter, runtimeConfig } from "questpie";