create-questpie 1.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.
Files changed (43) hide show
  1. package/README.md +20 -12
  2. package/dist/index.mjs +10 -3
  3. package/package.json +21 -21
  4. package/templates/tanstack-start/AGENTS.md +371 -318
  5. package/templates/tanstack-start/CLAUDE.md +84 -52
  6. package/templates/tanstack-start/README.md +65 -52
  7. package/templates/tanstack-start/components.json +20 -20
  8. package/templates/tanstack-start/package.json +6 -0
  9. package/templates/tanstack-start/questpie.config.ts +7 -7
  10. package/templates/tanstack-start/src/lib/auth-client.ts +3 -3
  11. package/templates/tanstack-start/src/lib/client.ts +13 -0
  12. package/templates/tanstack-start/src/lib/env.ts +19 -22
  13. package/templates/tanstack-start/src/lib/query-client.ts +5 -5
  14. package/templates/tanstack-start/src/questpie/admin/.generated/client.ts +13 -0
  15. package/templates/tanstack-start/src/questpie/admin/admin.ts +8 -4
  16. package/templates/tanstack-start/src/questpie/admin/modules.ts +1 -0
  17. package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +194 -0
  18. package/templates/tanstack-start/src/questpie/server/.generated/index.ts +201 -0
  19. package/templates/tanstack-start/src/questpie/server/app.ts +10 -52
  20. package/templates/tanstack-start/src/questpie/server/collections/posts.collection.ts +39 -53
  21. package/templates/tanstack-start/src/questpie/server/config/admin.ts +83 -0
  22. package/templates/tanstack-start/src/questpie/server/config/auth.ts +8 -0
  23. package/templates/tanstack-start/src/questpie/server/config/openapi.ts +10 -0
  24. package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +9 -14
  25. package/templates/tanstack-start/src/questpie/server/modules.ts +10 -0
  26. package/templates/tanstack-start/src/questpie/server/questpie.config.ts +20 -0
  27. package/templates/tanstack-start/src/router.tsx +6 -5
  28. package/templates/tanstack-start/src/routes/__root.tsx +11 -9
  29. package/templates/tanstack-start/src/routes/admin/$.tsx +14 -13
  30. package/templates/tanstack-start/src/routes/admin/index.tsx +11 -10
  31. package/templates/tanstack-start/src/routes/admin/login.tsx +11 -10
  32. package/templates/tanstack-start/src/routes/admin.tsx +53 -52
  33. package/templates/tanstack-start/src/routes/api/{cms/$.ts → $.ts} +6 -20
  34. package/templates/tanstack-start/src/styles.css +109 -109
  35. package/templates/tanstack-start/tsconfig.json +27 -25
  36. package/templates/tanstack-start/vite.config.ts +5 -3
  37. package/templates/tanstack-start/src/lib/cms-client.ts +0 -12
  38. package/templates/tanstack-start/src/migrations/index.ts +0 -8
  39. package/templates/tanstack-start/src/questpie/admin/builder.ts +0 -4
  40. package/templates/tanstack-start/src/questpie/server/builder.ts +0 -4
  41. package/templates/tanstack-start/src/questpie/server/dashboard.ts +0 -68
  42. package/templates/tanstack-start/src/questpie/server/rpc.ts +0 -4
  43. package/templates/tanstack-start/src/questpie/server/sidebar.ts +0 -26
@@ -0,0 +1,194 @@
1
+ /* oxlint-disable */
2
+ // AUTO-GENERATED by questpie codegen — DO NOT EDIT
3
+ // Typed factory functions with plugin extensions. Regenerate with: questpie generate
4
+
5
+ // ── Core Imports ───────────────────────────────────────────
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;
33
+
34
+ // ── Plugin Imports ─────────────────────────────────────────
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
+
39
+ // ════════════════════════════════════════════════════════════
40
+ // Type extraction — driven by CategoryDeclaration
41
+ // ════════════════════════════════════════════════════════════
42
+
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;
48
+
49
+ // Field types — populated by module codegen via Questpie.FieldTypesMap
50
+ type _AllFieldTypes = Questpie.FieldTypesMap;
51
+
52
+ // ════════════════════════════════════════════════════════════
53
+ // Type augmentations — generated from plugin registries
54
+ // ════════════════════════════════════════════════════════════
55
+
56
+ declare module "questpie" {
57
+ interface 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>;
61
+ preview(config: PreviewConfig): CollectionBuilder<TState>;
62
+ actions(configFn: (ctx: ActionsConfigContext<Record<string, unknown>, _ComponentsRecord>) => ServerActionsConfig): CollectionBuilder<TState>;
63
+ }
64
+ interface 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>;
71
+ }
72
+ }
73
+
74
+ declare global {
75
+ namespace Questpie {
76
+ interface FieldTypesMap extends BuiltinFields {}
77
+ interface FieldTypeRegistry extends Record<keyof _AllFieldTypes, {}> {}
78
+ }
79
+ }
80
+
81
+ declare module "@questpie/admin/server" {
82
+ interface ComponentTypeRegistry extends Record<_ComponentsNames_Strict, {}> {}
83
+ }
84
+
85
+ // ════════════════════════════════════════════════════════════
86
+ // Extension registries
87
+ // ════════════════════════════════════════════════════════════
88
+
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;
95
+ },
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 };
102
+ },
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 };
109
+ },
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;
117
+ },
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;
127
+ },
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 };
134
+ },
135
+ },
136
+ };
137
+
138
+ // ════════════════════════════════════════════════════════════
139
+ // Factory functions
140
+ // ════════════════════════════════════════════════════════════
141
+
142
+ /**
143
+ * Create a typed collection builder with plugin extensions.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * import { collection } from "#questpie/factories";
148
+ *
149
+ * export default collection("posts")
150
+ * .fields(({ f }) => ({ title: f.text(255).required() }))
151
+ * .admin(({ c }) => ({ icon: c.icon("ph:article") }))
152
+ * .list(({ v, f }) => v.collectionTable({ columns: [f.title] }))
153
+ * ```
154
+ */
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;
157
+ }
158
+
159
+ /**
160
+ * Create a typed global builder with plugin extensions.
161
+ */
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;
164
+ }
165
+
166
+ // ════════════════════════════════════════════════════════════
167
+ // Builder factory functions (plugin-contributed)
168
+ // ════════════════════════════════════════════════════════════
169
+
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;
176
+ }
177
+
178
+ // ════════════════════════════════════════════════════════════
179
+ // Singleton factory functions
180
+ // ════════════════════════════════════════════════════════════
181
+
182
+ /** Typed factory for appConfig config. */
183
+ export function appConfig<T extends AppConfigInput>(config: T): T { return config; }
184
+
185
+ /** Typed factory for authConfig config. */
186
+ export function authConfig<T extends AuthConfig>(config: T): T { return config; }
187
+
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; }
192
+
193
+ /** Typed factory for openapi config. */
194
+ export function openapi<T extends OpenApiModuleConfig>(config: T): T { return config; }
@@ -0,0 +1,201 @@
1
+ /* oxlint-disable */
2
+ // AUTO-GENERATED by questpie codegen — DO NOT EDIT
3
+ // Regenerate with: questpie generate
4
+
5
+ import { createApp, createContextFactory, extractAppServices, type AppDefinition, type ModuleDefinition, type AppContext, type Registry, type QueueClient, type CollectionAPI } from "questpie";
6
+
7
+ // ── Runtime ────────────────────────────────────────────────
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
+ // ════════════════════════════════════════════════════════════
27
+ // TYPES — composed from typeof references (zero inference cost)
28
+ // ════════════════════════════════════════════════════════════
29
+
30
+ import type { ServiceCustomNamespaceInstances, ServiceInstanceOf, ServiceInstancesInNamespace, ServiceTopLevelInstances, UnionToIntersection } from "questpie";
31
+ type _Module = (typeof _modules)[number];
32
+ type _MPRaw<K extends string> = UnionToIntersection<_Module extends infer M ? M extends Record<K, infer V> ? V : never : never>;
33
+ type _MP<K extends string> = [_MPRaw<K>] extends [never] ? {} : _MPRaw<K>;
34
+
35
+ type _ModuleCollections = _MP<"collections">;
36
+ type _ModuleGlobals = _MP<"globals">;
37
+ type _ModuleJobs = _MP<"jobs">;
38
+ type _ModuleRoutes = _MP<"routes">;
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">;
60
+
61
+ /** All collections in the app (modules + user, user overrides) */
62
+ export type AppCollections = _ModuleCollections & {
63
+ posts: typeof _coll_posts;
64
+ };
65
+
66
+ /** All globals in the app (modules + user, user overrides) */
67
+ export type AppGlobals = _ModuleGlobals & {
68
+ siteSettings: typeof _glob_siteSettings;
69
+ };
70
+
71
+ /** All jobs in the app (modules + user, user overrides) */
72
+ export type AppJobs = _ModuleJobs;
73
+
74
+ /** All routes in the app (modules + user, user overrides) */
75
+ export type AppRoutes = _ModuleRoutes;
76
+
77
+ /** All service definitions in the app (modules + user, user overrides). */
78
+ type _AppServiceDefinitions = _ModuleServices;
79
+
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> };
104
+
105
+ // ── AppContext augmentation — auto-types ALL handlers ──────
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
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Flat config type for client APIs.
151
+ * Use with `createClient<AppConfig>()` and `createAdminAuthClient<AppConfig>()`.
152
+ * For handler context, use `AppContext` (auto-typed via module augmentation).
153
+ */
154
+ export type AppConfig = {
155
+ collections: AppCollections & Record<string, any>;
156
+ globals: AppGlobals & Record<string, any>;
157
+ routes: AppRoutes;
158
+ auth: typeof _authConfig;
159
+ };
160
+
161
+ // ════════════════════════════════════════════════════════════
162
+ // RUNTIME — create the app instance
163
+ // ════════════════════════════════════════════════════════════
164
+
165
+ export const app = await createApp(
166
+ ({
167
+ modules: _modules as ModuleDefinition[],
168
+ collections: {
169
+ posts: _coll_posts,
170
+ },
171
+ globals: {
172
+ siteSettings: _glob_siteSettings,
173
+ },
174
+ config: {
175
+ auth: _authConfig as any,
176
+ admin: _adminConfig as any,
177
+ openapi: _openapi as any,
178
+ },
179
+ }) satisfies AppDefinition,
180
+ _runtime,
181
+ );
182
+
183
+ /** Fully typed QUESTPIE app instance. */
184
+ export type App = typeof app;
185
+
186
+ // ── createContext — typed context for scripts ──────────────
187
+ /**
188
+ * Create a typed AppContext for use in scripts, tests, or standalone code.
189
+ * Resolves all services and provides flat access to infrastructure.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * import { createContext } from "#questpie";
194
+ *
195
+ * const ctx = await createContext();
196
+ * const posts = await ctx.collections.posts.find({});
197
+ * ```
198
+ */
199
+ export const createContext = createContextFactory(app);
200
+
201
+ // Factories: import { collection, global, ... } from '#questpie/factories';
@@ -1,52 +1,10 @@
1
- import { adminModule, adminRpc } from "@questpie/admin/server";
2
- import { ConsoleAdapter, q } from "questpie";
3
- import { env } from "@/lib/env.js";
4
- import { migrations } from "../../migrations/index.js";
5
- import { posts } from "./collections/index.js";
6
- import { configureDashboard } from "./dashboard.js";
7
- import { siteSettings } from "./globals/index.js";
8
- import { r } from "./rpc.js";
9
- import { configureSidebar } from "./sidebar.js";
10
-
11
- // ─── CMS Instance ───────────────────────────────────────────────────────────
12
- // The built CMS application. Standalone — does NOT depend on appRpc.
13
-
14
- export const cms = q({ name: "{{projectName}}" })
15
- .use(adminModule)
16
- .collections({ posts })
17
- .globals({ siteSettings })
18
- .sidebar(configureSidebar)
19
- .branding({ name: "{{projectName}}" })
20
- .dashboard(configureDashboard)
21
- .auth({
22
- emailAndPassword: {
23
- enabled: true,
24
- requireEmailVerification: false,
25
- },
26
- baseURL: env.APP_URL,
27
- basePath: "/api/cms/auth",
28
- secret: env.BETTER_AUTH_SECRET,
29
- })
30
- .build({
31
- app: { url: env.APP_URL },
32
- db: { url: env.DATABASE_URL },
33
- storage: { basePath: "/api/cms" },
34
- migrations,
35
- email: {
36
- adapter: new ConsoleAdapter({ logHtml: false }),
37
- },
38
- });
39
-
40
- // ─── RPC Router ─────────────────────────────────────────────────────────────
41
- // Standalone router. Both cms and appRpc are passed to createFetchHandler()
42
- // in routes/api/cms/$.ts. Add your custom RPC functions here.
43
-
44
- export const appRpc = r.router({
45
- ...adminRpc,
46
- });
47
-
48
- // ─── Type Exports ───────────────────────────────────────────────────────────
49
- // Used by: rpc.ts (AppCMS for typed handlers), client (AppCMS + AppRpc)
50
-
51
- export type AppCMS = typeof cms;
52
- export type AppRpc = typeof appRpc;
1
+ /**
2
+ * App Instance
3
+ *
4
+ * Re-exports the app from the generated entrypoint.
5
+ * Run `questpie generate` to regenerate .generated/index.ts.
6
+ *
7
+ * If .generated/ does not exist yet, run: bun questpie generate
8
+ */
9
+
10
+ export { type App, type AppConfig, app } from "./.generated/index";
@@ -1,68 +1,54 @@
1
- import { qb } from "@/questpie/server/builder.js";
1
+ import { collection } from "#questpie/factories";
2
2
 
3
- export const posts = qb
4
- .collection("posts")
5
- .fields((f) => ({
6
- title: f.text({
7
- label: "Title",
8
- required: true,
9
- maxLength: 255,
10
- }),
11
- slug: f.text({
12
- label: "Slug",
13
- required: true,
14
- maxLength: 255,
15
- input: "optional",
16
- meta: {
17
- admin: {
18
- compute: {
19
- handler: ({
20
- data,
21
- prev,
22
- }: {
23
- data: Record<string, unknown>;
24
- prev: { data: Record<string, unknown> };
25
- }) => {
26
- const title = data.title;
27
- const currentSlug = data.slug;
28
- const prevTitle = prev.data.title;
3
+ export const posts = collection("posts")
4
+ .fields(({ f }) => ({
5
+ title: f.text(255).label("Title").required(),
6
+ slug: f
7
+ .text(255)
8
+ .label("Slug")
9
+ .required()
10
+ .inputOptional()
11
+ .admin({
12
+ compute: {
13
+ handler: ({
14
+ data,
15
+ prev,
16
+ }: {
17
+ data: Record<string, unknown>;
18
+ prev: { data: Record<string, unknown> };
19
+ }) => {
20
+ const title = data.title;
21
+ const currentSlug = data.slug;
22
+ const prevTitle = prev.data.title;
29
23
 
30
- if (currentSlug && prevTitle === title) return undefined;
24
+ if (currentSlug && prevTitle === title) return undefined;
31
25
 
32
- if (title && typeof title === "string") {
33
- return title
34
- .toLowerCase()
35
- .replace(/[^a-z0-9]+/g, "-")
36
- .replace(/^-|-$/g, "");
37
- }
38
- return undefined;
39
- },
40
- deps: ({ data }: { data: Record<string, unknown> }) => [
41
- data.title,
42
- data.slug,
43
- ],
44
- debounce: 300,
26
+ if (title && typeof title === "string") {
27
+ return title
28
+ .toLowerCase()
29
+ .replace(/[^a-z0-9]+/g, "-")
30
+ .replace(/^-|-$/g, "");
31
+ }
32
+ return undefined;
45
33
  },
34
+ deps: ({ data }: { data: Record<string, unknown> }) => [
35
+ data.title,
36
+ data.slug,
37
+ ],
38
+ debounce: 300,
46
39
  },
47
- },
48
- }),
49
- content: f.richText({
50
- label: "Content",
51
- }),
52
- published: f.boolean({
53
- label: "Published",
54
- default: false,
55
- required: true,
56
- }),
40
+ }),
41
+ content: f.richText().label("Content"),
42
+ published: f.boolean().label("Published").default(false).required(),
57
43
  }))
58
44
  .title(({ f }) => f.title)
59
45
  .admin(({ c }) => ({
60
46
  label: "Posts",
61
47
  icon: c.icon("ph:article"),
62
48
  }))
63
- .list(({ v }) => v.table({}))
49
+ .list(({ v }) => v.collectionTable({}))
64
50
  .form(({ v, f }) =>
65
- v.form({
51
+ v.collectionForm({
66
52
  sidebar: {
67
53
  position: "right",
68
54
  fields: [f.slug, f.published],
@@ -0,0 +1,83 @@
1
+ import { adminConfig } from "#questpie/factories";
2
+
3
+ export default adminConfig({
4
+ branding: {
5
+ name: "{{projectName}}",
6
+ },
7
+ sidebar: {
8
+ sections: [
9
+ {
10
+ id: "main",
11
+ title: "Content",
12
+ items: [
13
+ {
14
+ type: "link",
15
+ label: "Dashboard",
16
+ href: "/admin",
17
+ icon: { type: "icon", props: { name: "ph:house" } },
18
+ },
19
+ { type: "collection", collection: "posts" },
20
+ { type: "global", global: "siteSettings" },
21
+ ],
22
+ },
23
+ ],
24
+ },
25
+ dashboard: {
26
+ title: "Dashboard",
27
+ description: "Overview of your content",
28
+ columns: 4,
29
+ sections: [
30
+ { id: "content", label: "Content", layout: "grid", columns: 2 },
31
+ { id: "recent", label: "Recent", layout: "grid", columns: 4 },
32
+ ],
33
+ items: [
34
+ {
35
+ sectionId: "content",
36
+ id: "total-posts",
37
+ type: "stats",
38
+ collection: "posts",
39
+ label: "Total Posts",
40
+ span: 1,
41
+ },
42
+ {
43
+ sectionId: "content",
44
+ id: "published-posts",
45
+ type: "stats",
46
+ collection: "posts",
47
+ label: "Published",
48
+ filter: { published: true },
49
+ variant: "primary",
50
+ span: 1,
51
+ },
52
+ {
53
+ sectionId: "recent",
54
+ id: "recent-posts",
55
+ type: "recentItems",
56
+ collection: "posts",
57
+ label: "Recent Posts",
58
+ limit: 5,
59
+ span: 2,
60
+ },
61
+ {
62
+ sectionId: "recent",
63
+ id: "quick-actions",
64
+ type: "quickActions",
65
+ label: "Quick Actions",
66
+ actions: [
67
+ {
68
+ label: "New Post",
69
+ action: { type: "create", collection: "posts" },
70
+ },
71
+ {
72
+ label: "Site Settings",
73
+ action: {
74
+ type: "link",
75
+ href: "/admin/globals/siteSettings",
76
+ },
77
+ },
78
+ ],
79
+ span: 2,
80
+ },
81
+ ],
82
+ },
83
+ });
@@ -0,0 +1,8 @@
1
+ import { authConfig } from "questpie";
2
+
3
+ export default authConfig({
4
+ emailAndPassword: {
5
+ enabled: true,
6
+ requireEmailVerification: false,
7
+ },
8
+ });
@@ -0,0 +1,10 @@
1
+ import { openApiConfig } from "@questpie/openapi";
2
+
3
+ export default openApiConfig({
4
+ info: {
5
+ title: "{{projectName}} API",
6
+ version: "1.0.0",
7
+ description: "QUESTPIE API",
8
+ },
9
+ scalar: { theme: "purple" },
10
+ });