create-questpie 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -12
- package/dist/index.mjs +9 -2
- package/package.json +21 -21
- package/templates/tanstack-start/AGENTS.md +366 -318
- package/templates/tanstack-start/CLAUDE.md +84 -52
- package/templates/tanstack-start/README.md +59 -52
- package/templates/tanstack-start/components.json +20 -20
- package/templates/tanstack-start/package.json +6 -0
- package/templates/tanstack-start/questpie.config.ts +7 -7
- package/templates/tanstack-start/src/lib/auth-client.ts +3 -3
- package/templates/tanstack-start/src/lib/client.ts +13 -0
- package/templates/tanstack-start/src/lib/env.ts +19 -22
- package/templates/tanstack-start/src/lib/query-client.ts +5 -5
- package/templates/tanstack-start/src/questpie/admin/admin.ts +8 -4
- package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +318 -0
- package/templates/tanstack-start/src/questpie/server/.generated/index.ts +153 -0
- package/templates/tanstack-start/src/questpie/server/app.ts +10 -52
- package/templates/tanstack-start/src/questpie/server/collections/posts.collection.ts +39 -53
- package/templates/tanstack-start/src/questpie/server/config/admin.ts +83 -0
- package/templates/tanstack-start/src/questpie/server/config/auth.ts +8 -0
- package/templates/tanstack-start/src/questpie/server/config/openapi.ts +10 -0
- package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +9 -14
- package/templates/tanstack-start/src/questpie/server/modules.ts +10 -0
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +20 -0
- package/templates/tanstack-start/src/router.tsx +6 -5
- package/templates/tanstack-start/src/routes/__root.tsx +11 -9
- package/templates/tanstack-start/src/routes/admin/$.tsx +14 -13
- package/templates/tanstack-start/src/routes/admin/index.tsx +11 -10
- package/templates/tanstack-start/src/routes/admin/login.tsx +11 -10
- package/templates/tanstack-start/src/routes/admin.tsx +53 -52
- package/templates/tanstack-start/src/routes/api/{cms/$.ts → $.ts} +6 -20
- package/templates/tanstack-start/src/styles.css +109 -109
- package/templates/tanstack-start/tsconfig.json +27 -25
- package/templates/tanstack-start/vite.config.ts +5 -3
- package/templates/tanstack-start/src/lib/cms-client.ts +0 -12
- package/templates/tanstack-start/src/migrations/index.ts +0 -8
- package/templates/tanstack-start/src/questpie/admin/builder.ts +0 -4
- package/templates/tanstack-start/src/questpie/server/builder.ts +0 -4
- package/templates/tanstack-start/src/questpie/server/dashboard.ts +0 -68
- package/templates/tanstack-start/src/questpie/server/rpc.ts +0 -4
- package/templates/tanstack-start/src/questpie/server/sidebar.ts +0 -26
|
@@ -0,0 +1,318 @@
|
|
|
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 {
|
|
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";
|
|
19
|
+
|
|
20
|
+
// ── 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";
|
|
41
|
+
// ════════════════════════════════════════════════════════════
|
|
42
|
+
// Type extraction from Registry — driven by CategoryDeclaration
|
|
43
|
+
// ════════════════════════════════════════════════════════════
|
|
44
|
+
|
|
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">;
|
|
55
|
+
|
|
56
|
+
// Field types — extracted from modules
|
|
57
|
+
import type { ExtractModuleProp } from "questpie";
|
|
58
|
+
|
|
59
|
+
type _AllFieldTypes = ExtractModuleProp<
|
|
60
|
+
{ modules: typeof _modulesArr },
|
|
61
|
+
"fields"
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
// ════════════════════════════════════════════════════════════
|
|
65
|
+
// Type augmentations — generated from plugin registries
|
|
66
|
+
// ════════════════════════════════════════════════════════════
|
|
67
|
+
|
|
68
|
+
declare module "questpie" {
|
|
69
|
+
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>;
|
|
101
|
+
preview(config: PreviewConfig): CollectionBuilder<TState>;
|
|
102
|
+
actions(
|
|
103
|
+
configFn: (
|
|
104
|
+
ctx: ActionsConfigContext<Record<string, unknown>, _ComponentsRecord>,
|
|
105
|
+
) => ServerActionsConfig,
|
|
106
|
+
): CollectionBuilder<TState>;
|
|
107
|
+
}
|
|
108
|
+
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>;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
declare global {
|
|
130
|
+
namespace Questpie {
|
|
131
|
+
interface FieldTypeRegistry extends Record<keyof _AllFieldTypes, {}> {}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
declare module "@questpie/admin/server" {
|
|
136
|
+
interface ComponentTypeRegistry extends Record<_ComponentsNames_Strict, {}> {}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ════════════════════════════════════════════════════════════
|
|
140
|
+
// Extension registries
|
|
141
|
+
// ════════════════════════════════════════════════════════════
|
|
142
|
+
|
|
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
|
+
},
|
|
152
|
+
},
|
|
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
|
+
},
|
|
174
|
+
},
|
|
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
|
+
},
|
|
187
|
+
},
|
|
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
|
+
},
|
|
200
|
+
},
|
|
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
|
+
},
|
|
212
|
+
},
|
|
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
|
+
},
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// ════════════════════════════════════════════════════════════
|
|
229
|
+
// Factory functions
|
|
230
|
+
// ════════════════════════════════════════════════════════════
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Create a typed collection builder with plugin extensions.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* import { collection } from "#questpie/factories";
|
|
238
|
+
*
|
|
239
|
+
* export default collection("posts")
|
|
240
|
+
* .fields(({ f }) => ({ title: f.text({ required: true }) }))
|
|
241
|
+
* .admin(({ c }) => ({ icon: c.icon("ph:article") }))
|
|
242
|
+
* .list(({ v, f }) => v.collectionTable({ columns: [f.title] }))
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
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;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Create a typed global builder with plugin extensions.
|
|
257
|
+
*/
|
|
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;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ════════════════════════════════════════════════════════════
|
|
269
|
+
// Singleton factory functions
|
|
270
|
+
// ════════════════════════════════════════════════════════════
|
|
271
|
+
|
|
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;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Typed factory for context config. */
|
|
288
|
+
export function context<T extends ContextResolver>(config: T): T {
|
|
289
|
+
return config;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Typed factory for branding config. */
|
|
293
|
+
export function branding<T extends ServerBrandingConfig>(config: T): T {
|
|
294
|
+
return config;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Typed factory for adminLocale config. */
|
|
298
|
+
export function adminLocale<T extends AdminLocaleConfig>(config: T): T {
|
|
299
|
+
return config;
|
|
300
|
+
}
|
|
301
|
+
|
|
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
|
+
}
|
|
310
|
+
|
|
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
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/* oxlint-disable */
|
|
2
|
+
// AUTO-GENERATED by questpie codegen — DO NOT EDIT
|
|
3
|
+
// Regenerate with: questpie generate
|
|
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";
|
|
14
|
+
|
|
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
|
+
// ── Runtime ────────────────────────────────────────────────
|
|
25
|
+
import _runtime from "../questpie.config";
|
|
26
|
+
// ════════════════════════════════════════════════════════════
|
|
27
|
+
// TYPES — composed from typeof references (zero inference cost)
|
|
28
|
+
// ════════════════════════════════════════════════════════════
|
|
29
|
+
import _sidebar from "../sidebar";
|
|
30
|
+
|
|
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
|
+
>;
|
|
35
|
+
type _MP<K extends string> = [_MPRaw<K>] extends [never] ? {} : _MPRaw<K>;
|
|
36
|
+
|
|
37
|
+
type _ModuleCollections = _MP<"collections">;
|
|
38
|
+
type _ModuleGlobals = _MP<"globals">;
|
|
39
|
+
type _ModuleJobs = _MP<"jobs">;
|
|
40
|
+
type _ModuleRoutes = _MP<"routes">;
|
|
41
|
+
type _ModuleServices = _MP<"services">;
|
|
42
|
+
|
|
43
|
+
/** All collections in the app (modules + user, user overrides) */
|
|
44
|
+
export type AppCollections = _ModuleCollections & {
|
|
45
|
+
"posts.collection": typeof _coll_posts_collection;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** All globals in the app (modules + user, user overrides) */
|
|
49
|
+
export type AppGlobals = _ModuleGlobals & {
|
|
50
|
+
"siteSettings.global": typeof _glob_siteSettings_global;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** All jobs in the app (modules + user, user overrides) */
|
|
54
|
+
export type AppJobs = _ModuleJobs;
|
|
55
|
+
|
|
56
|
+
/** All routes in the app (modules + user, user overrides) */
|
|
57
|
+
export type AppRoutes = _ModuleRoutes;
|
|
58
|
+
|
|
59
|
+
/** All services in the app (modules + user, user overrides). Values are service instances. */
|
|
60
|
+
export type AppServices = _ModuleServices;
|
|
61
|
+
|
|
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
|
+
>;
|
|
79
|
+
|
|
80
|
+
// ── 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;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Flat config type for client APIs.
|
|
109
|
+
* Use with `createClient<AppConfig>()` and `createAdminAuthClient<AppConfig>()`.
|
|
110
|
+
* For handler context, use `AppContext` (auto-typed via module augmentation).
|
|
111
|
+
*/
|
|
112
|
+
export type AppConfig = {
|
|
113
|
+
collections: AppCollections;
|
|
114
|
+
globals: AppGlobals;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// ════════════════════════════════════════════════════════════
|
|
118
|
+
// RUNTIME — create the app instance
|
|
119
|
+
// ════════════════════════════════════════════════════════════
|
|
120
|
+
|
|
121
|
+
export const app = (await createApp(
|
|
122
|
+
{
|
|
123
|
+
modules: _modules as any,
|
|
124
|
+
collections: {
|
|
125
|
+
"posts.collection": _coll_posts_collection,
|
|
126
|
+
},
|
|
127
|
+
globals: {
|
|
128
|
+
"siteSettings.global": _glob_siteSettings_global,
|
|
129
|
+
},
|
|
130
|
+
branding: _branding as any,
|
|
131
|
+
dashboard: _dashboard as any,
|
|
132
|
+
sidebar: _sidebar as any,
|
|
133
|
+
},
|
|
134
|
+
_runtime,
|
|
135
|
+
)) as unknown as AppConfig & {
|
|
136
|
+
waitForInit(): Promise<void>;
|
|
137
|
+
destroy(): Promise<void>;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// ── createContext — typed context for scripts ──────────────
|
|
141
|
+
/**
|
|
142
|
+
* Create a typed AppContext for use in scripts, tests, or standalone code.
|
|
143
|
+
* Resolves all services and provides flat access to infrastructure.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* import { createContext } from "#questpie";
|
|
148
|
+
*
|
|
149
|
+
* const ctx = await createContext();
|
|
150
|
+
* const posts = await ctx.collections.posts.find({});
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export const createContext = createContextFactory(app);
|
|
@@ -1,52 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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, app } from "./.generated/index";
|
|
@@ -1,68 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collection } from "#questpie/factories";
|
|
2
2
|
|
|
3
|
-
export const posts =
|
|
4
|
-
.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
+
if (currentSlug && prevTitle === title) return undefined;
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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.
|
|
49
|
+
.list(({ v }) => v.collectionTable({}))
|
|
64
50
|
.form(({ v, f }) =>
|
|
65
|
-
v.
|
|
51
|
+
v.collectionForm({
|
|
66
52
|
sidebar: {
|
|
67
53
|
position: "right",
|
|
68
54
|
fields: [f.slug, f.published],
|