@questpie/admin 3.2.7 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +4 -6
  2. package/dist/client/blocks/block-renderer.d.mts +2 -2
  3. package/dist/client/builder/admin-types.d.mts +3 -3
  4. package/dist/client/builder/types/action-types.d.mts +1 -1
  5. package/dist/client/builder/types/collection-types.d.mts +59 -2
  6. package/dist/client/components/blocks/block-editor-provider.mjs +13 -0
  7. package/dist/client/components/fields/array-field.mjs +105 -122
  8. package/dist/client/components/fields/asset-preview-field.mjs +1 -1
  9. package/dist/client/components/fields/blocks-field/blocks-field.mjs +1 -1
  10. package/dist/client/components/fields/boolean-field.mjs +1 -1
  11. package/dist/client/components/fields/date-field.mjs +1 -1
  12. package/dist/client/components/fields/datetime-field.mjs +1 -1
  13. package/dist/client/components/fields/email-field.mjs +1 -1
  14. package/dist/client/components/fields/field-wrapper.mjs +44 -15
  15. package/dist/client/components/fields/number-field.mjs +1 -1
  16. package/dist/client/components/fields/object-array-field.mjs +179 -149
  17. package/dist/client/components/fields/object-field.mjs +96 -87
  18. package/dist/client/components/fields/relation-picker.mjs +1 -1
  19. package/dist/client/components/fields/relation-select.mjs +1 -1
  20. package/dist/client/components/fields/rich-text-editor/index.mjs +1 -1
  21. package/dist/client/components/fields/select-field.mjs +1 -1
  22. package/dist/client/components/fields/text-field.mjs +1 -1
  23. package/dist/client/components/fields/textarea-field.mjs +1 -1
  24. package/dist/client/components/fields/time-field.mjs +1 -1
  25. package/dist/client/components/fields/upload-field.mjs +1 -1
  26. package/dist/client/components/history-sidebar.mjs +10 -4
  27. package/dist/client/components/structured-diff.mjs +367 -0
  28. package/dist/client/components/ui/sidebar.mjs +1 -1
  29. package/dist/client/hooks/use-field-options.mjs +34 -15
  30. package/dist/client/hooks/use-transition-stage.mjs +2 -2
  31. package/dist/client/modules/admin.d.mts +3 -0
  32. package/dist/client/modules/admin.mjs +3 -0
  33. package/dist/client/preview/block-scope-context.d.mts +2 -2
  34. package/dist/client/preview/preview-banner.d.mts +2 -2
  35. package/dist/client/preview/preview-field.d.mts +4 -4
  36. package/dist/client/utils/auto-expand-fields.mjs +1 -1
  37. package/dist/client/views/collection/auto-form-fields.mjs +23 -19
  38. package/dist/client/views/collection/cells/complex-cells.mjs +1 -1
  39. package/dist/client/views/collection/columns/build-columns.mjs +1 -1
  40. package/dist/client/views/collection/columns/column-defaults.mjs +17 -4
  41. package/dist/client/views/collection/field-renderer.mjs +19 -7
  42. package/dist/client/views/collection/form-view.mjs +10 -6
  43. package/dist/client/views/collection/list-view.mjs +830 -0
  44. package/dist/client/views/collection/outline.mjs +363 -0
  45. package/dist/client/views/collection/table-view.mjs +25 -16
  46. package/dist/client/views/globals/global-form-view.mjs +47 -27
  47. package/dist/client/views/layout/admin-layout.d.mts +15 -1
  48. package/dist/client/views/layout/admin-layout.mjs +95 -31
  49. package/dist/client/views/layout/admin-sidebar.mjs +2 -2
  50. package/dist/client.d.mts +6 -6
  51. package/dist/client.mjs +1 -1
  52. package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
  53. package/dist/factories.d.mts +19 -0
  54. package/dist/factories.mjs +11 -0
  55. package/dist/fields.d.mts +4 -0
  56. package/dist/fields.mjs +5 -0
  57. package/dist/index.d.mts +6 -6
  58. package/dist/index.mjs +1 -1
  59. package/dist/modules/admin.d.mts +10 -0
  60. package/dist/modules/admin.mjs +9 -0
  61. package/dist/modules/audit.d.mts +5 -0
  62. package/dist/modules/audit.mjs +5 -0
  63. package/dist/server/augmentation/form-layout.d.mts +57 -2
  64. package/dist/server/augmentation/index.d.mts +3 -1
  65. package/dist/server/augmentation/shell.d.mts +48 -0
  66. package/dist/server/augmentation.d.mts +2 -1
  67. package/dist/server/codegen/admin-client-template.mjs +11 -4
  68. package/dist/server/fields/blocks.d.mts +9 -2
  69. package/dist/server/fields/blocks.mjs +1 -1
  70. package/dist/server/fields/index.d.mts +2 -2
  71. package/dist/server/fields/index.mjs +2 -2
  72. package/dist/server/fields/rich-text.d.mts +9 -2
  73. package/dist/server/fields/rich-text.mjs +1 -1
  74. package/dist/server/i18n/messages/cs.mjs +8 -0
  75. package/dist/server/i18n/messages/de.mjs +8 -0
  76. package/dist/server/i18n/messages/en.mjs +8 -0
  77. package/dist/server/i18n/messages/es.mjs +8 -0
  78. package/dist/server/i18n/messages/fr.mjs +8 -0
  79. package/dist/server/i18n/messages/pl.mjs +8 -0
  80. package/dist/server/i18n/messages/pt.mjs +8 -0
  81. package/dist/server/i18n/messages/sk.mjs +8 -0
  82. package/dist/server/modules/admin/.generated/module.d.mts +24 -19
  83. package/dist/server/modules/admin/.generated/module.mjs +5 -1
  84. package/dist/server/modules/admin/.generated/registries.d.mts +6 -4
  85. package/dist/server/modules/admin/client/.generated/module.d.mts +70 -70
  86. package/dist/server/modules/admin/client/.generated/module.mjs +3 -1
  87. package/dist/server/modules/admin/client/views/collection-form.d.mts +6 -0
  88. package/dist/server/modules/admin/client/views/collection-table.d.mts +6 -0
  89. package/dist/server/modules/admin/client/views/global-form.d.mts +6 -0
  90. package/dist/server/modules/admin/client/views/list-view.d.mts +6 -0
  91. package/dist/server/modules/admin/client/views/list-view.mjs +10 -0
  92. package/dist/server/modules/admin/collections/account.d.mts +50 -50
  93. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  94. package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
  95. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
  96. package/dist/server/modules/admin/collections/apikey.d.mts +39 -39
  97. package/dist/server/modules/admin/collections/assets.d.mts +39 -39
  98. package/dist/server/modules/admin/collections/session.d.mts +42 -42
  99. package/dist/server/modules/admin/collections/user.d.mts +63 -63
  100. package/dist/server/modules/admin/collections/verification.d.mts +36 -36
  101. package/dist/server/modules/admin/dto/admin-config.dto.mjs +17 -0
  102. package/dist/server/modules/admin/index.d.mts +30 -31
  103. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -17
  104. package/dist/server/modules/admin/routes/admin-config.mjs +21 -5
  105. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  106. package/dist/server/modules/admin/routes/execute-action.mjs +18 -12
  107. package/dist/server/modules/admin/routes/i18n-helpers.d.mts +4 -0
  108. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  109. package/dist/server/modules/admin/routes/preview.d.mts +24 -19
  110. package/dist/server/modules/admin/routes/preview.mjs +83 -62
  111. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  112. package/dist/server/modules/admin/routes/route-helpers.mjs +36 -1
  113. package/dist/server/modules/admin/routes/setup.d.mts +7 -14
  114. package/dist/server/modules/admin/routes/setup.mjs +16 -3
  115. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  116. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  117. package/dist/server/modules/admin/views/list-view.d.mts +8 -0
  118. package/dist/server/modules/admin/views/list-view.mjs +7 -0
  119. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +41 -41
  120. package/dist/server/modules/audit/.generated/module.d.mts +6 -6
  121. package/dist/server/modules/audit/collections/audit-log.d.mts +87 -80
  122. package/dist/server/modules/audit/collections/audit-log.mjs +7 -2
  123. package/dist/server/modules/audit/config/localize-title.mjs +67 -0
  124. package/dist/server/modules/audit/index.d.mts +3 -2
  125. package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
  126. package/dist/server/modules/audit/log-audit-entry.d.mts +85 -0
  127. package/dist/server/modules/audit/log-audit-entry.mjs +125 -0
  128. package/dist/server/plugin.d.mts +1 -1
  129. package/dist/server/plugin.mjs +31 -31
  130. package/dist/server.d.mts +6 -4
  131. package/dist/server.mjs +9 -8
  132. package/dist/shared/preview-utils.d.mts +4 -4
  133. package/dist/shared/preview-utils.mjs +5 -7
  134. package/package.json +13 -3
  135. package/dist/client/hooks/use-audit-history.mjs +0 -38
@@ -1,9 +1,7 @@
1
- import { getPreviewSecret } from "../../../../shared/preview-utils.mjs";
2
1
  import { getApp, getCollectionState, getLocale, getSession } from "./route-helpers.mjs";
3
2
  import { translateAdminMessage } from "./i18n-helpers.mjs";
4
3
  import { z } from "zod";
5
4
  import { ApiError, route } from "questpie";
6
- import { createHmac, timingSafeEqual } from "node:crypto";
7
5
 
8
6
  //#region src/server/modules/admin/routes/preview.ts
9
7
  /**
@@ -14,14 +12,72 @@ import { createHmac, timingSafeEqual } from "node:crypto";
14
12
  *
15
13
  * Browser-safe utilities (isDraftMode, createDraftModeCookie, etc.) are in @questpie/admin/shared
16
14
  */
15
+ const textEncoder = new TextEncoder();
16
+ const textDecoder = new TextDecoder();
17
+ function bytesFromBase64Input(input) {
18
+ if (typeof input === "string") return textEncoder.encode(input);
19
+ if (input instanceof Uint8Array) return input;
20
+ return new Uint8Array(input);
21
+ }
22
+ function bytesToBase64(bytes) {
23
+ let binary = "";
24
+ const chunkSize = 32768;
25
+ for (let i = 0; i < bytes.length; i += chunkSize) binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
26
+ return btoa(binary);
27
+ }
28
+ function base64ToBytes(input) {
29
+ const binary = atob(input);
30
+ const bytes = new Uint8Array(binary.length);
31
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
32
+ return bytes;
33
+ }
17
34
  function base64UrlEncode(input) {
18
- return (Buffer.isBuffer(input) ? input : Buffer.from(input)).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
35
+ return bytesToBase64(bytesFromBase64Input(input)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
19
36
  }
20
37
  function base64UrlDecode(input) {
21
38
  let base64 = input.replace(/-/g, "+").replace(/_/g, "/");
22
39
  const padding = base64.length % 4;
23
40
  if (padding) base64 += "=".repeat(4 - padding);
24
- return Buffer.from(base64, "base64").toString("utf8");
41
+ return textDecoder.decode(base64ToBytes(base64));
42
+ }
43
+ async function signPayload(payload, secret) {
44
+ const subtle = globalThis.crypto?.subtle;
45
+ if (!subtle) throw new Error("[preview] Web Crypto API is required to sign preview tokens.");
46
+ const key = await subtle.importKey("raw", textEncoder.encode(secret), {
47
+ name: "HMAC",
48
+ hash: "SHA-256"
49
+ }, false, ["sign"]);
50
+ return base64UrlEncode(await subtle.sign("HMAC", key, textEncoder.encode(payload)));
51
+ }
52
+ function timingSafeEqualAscii(a, b) {
53
+ const aBytes = textEncoder.encode(a);
54
+ const bBytes = textEncoder.encode(b);
55
+ let diff = aBytes.length ^ bBytes.length;
56
+ const maxLength = Math.max(aBytes.length, bBytes.length);
57
+ for (let i = 0; i < maxLength; i++) diff |= (aBytes[i] ?? 0) ^ (bBytes[i] ?? 0);
58
+ return diff === 0;
59
+ }
60
+ function defaultPreviewSecret(ctx) {
61
+ return getApp(ctx).config.secret ?? "dev-preview-secret";
62
+ }
63
+ async function resolvePreviewSecret(source, ctx) {
64
+ const secret = typeof source === "function" ? await source(ctx) : source;
65
+ if (!secret) throw new Error("[preview] Preview token secret must not be empty.");
66
+ return secret;
67
+ }
68
+ function parsePreviewTokenPayload(encodedPayload) {
69
+ try {
70
+ const payload = JSON.parse(base64UrlDecode(encodedPayload));
71
+ if (!payload?.exp || typeof payload.exp !== "number") return { error: "invalidPayload" };
72
+ if (payload.exp < Date.now()) return { error: "tokenExpired" };
73
+ if (!payload.path || typeof payload.path !== "string") return { error: "invalidPath" };
74
+ return { payload };
75
+ } catch {
76
+ return { error: "invalidPayload" };
77
+ }
78
+ }
79
+ async function verifyPreviewSignature(encodedPayload, signature, secret) {
80
+ return timingSafeEqualAscii(signature, await signPayload(encodedPayload, secret));
25
81
  }
26
82
  const mintPreviewTokenSchema = z.object({
27
83
  path: z.string().min(1),
@@ -41,13 +97,11 @@ const DEFAULT_TTL_MS = 3600 * 1e3;
41
97
  /**
42
98
  * Create preview-related RPC functions.
43
99
  *
44
- * @param secret - Secret key for signing tokens
100
+ * @param secret - Secret key or resolver for signing tokens. Defaults to
101
+ * `app.config.secret` from the current route context.
45
102
  * @returns Object with preview functions
46
103
  */
47
- function createPreviewFunctions(secret) {
48
- const signPayload = (payload) => {
49
- return base64UrlEncode(createHmac("sha256", secret).update(payload).digest());
50
- };
104
+ function createPreviewFunctions(secret = defaultPreviewSecret) {
51
105
  return {
52
106
  mintPreviewToken: route().post().schema(mintPreviewTokenSchema).outputSchema(mintPreviewTokenOutputSchema).handler(async (ctx) => {
53
107
  const { input } = ctx;
@@ -62,7 +116,7 @@ function createPreviewFunctions(secret) {
62
116
  };
63
117
  const encodedPayload = base64UrlEncode(JSON.stringify(payload));
64
118
  return {
65
- token: `${encodedPayload}.${signPayload(encodedPayload)}`,
119
+ token: `${encodedPayload}.${await signPayload(encodedPayload, await resolvePreviewSecret(secret, ctx))}`,
66
120
  expiresAt
67
121
  };
68
122
  }),
@@ -76,41 +130,19 @@ function createPreviewFunctions(secret) {
76
130
  valid: false,
77
131
  error: t("preview.invalidTokenFormat")
78
132
  };
79
- const expectedSignature = signPayload(encodedPayload);
80
- const signatureBuffer = Uint8Array.from(Buffer.from(signature));
81
- const expectedBuffer = Uint8Array.from(Buffer.from(expectedSignature));
82
- if (signatureBuffer.length !== expectedBuffer.length) return {
133
+ if (!await verifyPreviewSignature(encodedPayload, signature, await resolvePreviewSecret(secret, ctx))) return {
83
134
  valid: false,
84
135
  error: t("preview.invalidSignature")
85
136
  };
86
- if (!timingSafeEqual(signatureBuffer, expectedBuffer)) return {
137
+ const parsed = parsePreviewTokenPayload(encodedPayload);
138
+ if ("error" in parsed) return {
87
139
  valid: false,
88
- error: t("preview.invalidSignature")
140
+ error: t(`preview.${parsed.error}`)
141
+ };
142
+ return {
143
+ valid: true,
144
+ path: parsed.payload.path
89
145
  };
90
- try {
91
- const payload = JSON.parse(base64UrlDecode(encodedPayload));
92
- if (!payload?.exp || typeof payload.exp !== "number") return {
93
- valid: false,
94
- error: t("preview.invalidPayload")
95
- };
96
- if (payload.exp < Date.now()) return {
97
- valid: false,
98
- error: t("preview.tokenExpired")
99
- };
100
- if (!payload.path || typeof payload.path !== "string") return {
101
- valid: false,
102
- error: t("preview.invalidPath")
103
- };
104
- return {
105
- valid: true,
106
- path: payload.path
107
- };
108
- } catch {
109
- return {
110
- valid: false,
111
- error: t("preview.invalidPayload")
112
- };
113
- }
114
146
  })
115
147
  };
116
148
  }
@@ -122,47 +154,35 @@ function createPreviewFunctions(secret) {
122
154
  * @param secret - The secret used to sign the token
123
155
  * @returns The payload if valid, null otherwise
124
156
  */
125
- function verifyPreviewTokenDirect(token, secret) {
157
+ async function verifyPreviewTokenDirect(token, secret) {
126
158
  const [encodedPayload, signature] = token.split(".");
127
159
  if (!encodedPayload || !signature) return null;
128
- const expectedSignature = base64UrlEncode(createHmac("sha256", secret).update(encodedPayload).digest());
129
- const signatureBuffer = Uint8Array.from(Buffer.from(signature));
130
- const expectedBuffer = Uint8Array.from(Buffer.from(expectedSignature));
131
- if (signatureBuffer.length !== expectedBuffer.length) return null;
132
- if (!timingSafeEqual(signatureBuffer, expectedBuffer)) return null;
133
- try {
134
- const payload = JSON.parse(base64UrlDecode(encodedPayload));
135
- if (!payload?.exp || typeof payload.exp !== "number") return null;
136
- if (payload.exp < Date.now()) return null;
137
- if (!payload.path || typeof payload.path !== "string") return null;
138
- return payload;
139
- } catch {
140
- return null;
141
- }
160
+ if (!await verifyPreviewSignature(encodedPayload, signature, secret)) return null;
161
+ const parsed = parsePreviewTokenPayload(encodedPayload);
162
+ return "payload" in parsed ? parsed.payload : null;
142
163
  }
143
164
  /**
144
165
  * Create a preview token verifier with bound secret.
145
166
  * Use this in route handlers to avoid passing secret repeatedly.
146
167
  *
147
- * @param secret - The secret used to sign tokens (optional, uses env if not provided)
168
+ * @param secret - The secret used to sign tokens.
148
169
  * @returns A verify function that only needs the token
149
170
  *
150
171
  * @example
151
172
  * ```ts
152
173
  * // Create once at module level
153
- * const verifyPreviewToken = createPreviewTokenVerifier();
174
+ * const verifyPreviewToken = createPreviewTokenVerifier(secret);
154
175
  *
155
176
  * // Use in route handler
156
- * const payload = verifyPreviewToken(token);
177
+ * const payload = await verifyPreviewToken(token);
157
178
  * if (!payload) {
158
179
  * return new Response("Invalid token", { status: 401 });
159
180
  * }
160
181
  * ```
161
182
  */
162
183
  function createPreviewTokenVerifier(secret) {
163
- const resolvedSecret = secret ?? getPreviewSecret();
164
184
  return (token) => {
165
- return verifyPreviewTokenDirect(token, resolvedSecret);
185
+ return verifyPreviewTokenDirect(token, secret);
166
186
  };
167
187
  }
168
188
  const getPreviewUrlSchema = z.object({
@@ -226,11 +246,12 @@ const getPreviewUrl = route().post().schema(getPreviewUrlSchema).outputSchema(ge
226
246
  }
227
247
  });
228
248
  /**
229
- * Default preview functions bundle with env-based secret.
249
+ * Default preview functions bundle. The route handlers resolve the token
250
+ * secret from `app.config.secret` at request time.
230
251
  * Used by the `adminModule` to register preview RPC functions.
231
252
  */
232
253
  const previewFunctions = {
233
- ...createPreviewFunctions(getPreviewSecret()),
254
+ ...createPreviewFunctions(),
234
255
  getPreviewUrl
235
256
  };
236
257
 
@@ -1,4 +1,4 @@
1
- import * as questpie80 from "questpie";
1
+ import * as questpie131 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/reactive.d.ts
4
4
 
@@ -13,7 +13,7 @@ import * as questpie80 from "questpie";
13
13
  * Batch reactive endpoint.
14
14
  * Executes multiple reactive handlers in a single request.
15
15
  */
16
- declare const batchReactive: questpie80.JsonRouteDefinition<{
16
+ declare const batchReactive: questpie131.JsonRouteDefinition<{
17
17
  collection: string;
18
18
  type: "collection" | "global";
19
19
  requests: {
@@ -27,12 +27,12 @@ declare const batchReactive: questpie80.JsonRouteDefinition<{
27
27
  }[];
28
28
  formData?: Record<string, unknown> | undefined;
29
29
  prevData?: Record<string, unknown> | null | undefined;
30
- }, any, questpie80.JsonRouteParams>;
30
+ }, any, questpie131.JsonRouteParams>;
31
31
  /**
32
32
  * Dynamic options endpoint.
33
33
  * Fetches options for select/relation fields with search and pagination.
34
34
  */
35
- declare const fieldOptions: questpie80.JsonRouteDefinition<{
35
+ declare const fieldOptions: questpie131.JsonRouteDefinition<{
36
36
  collection: string;
37
37
  type: "collection" | "global";
38
38
  field: string;
@@ -41,12 +41,12 @@ declare const fieldOptions: questpie80.JsonRouteDefinition<{
41
41
  page: number;
42
42
  limit: number;
43
43
  siblingData?: Record<string, unknown> | null | undefined;
44
- }, any, questpie80.JsonRouteParams>;
44
+ }, any, questpie131.JsonRouteParams>;
45
45
  /**
46
46
  * Reactive functions bundle.
47
47
  */
48
48
  declare const reactiveFunctions: {
49
- readonly batchReactive: questpie80.JsonRouteDefinition<{
49
+ readonly batchReactive: questpie131.JsonRouteDefinition<{
50
50
  collection: string;
51
51
  type: "collection" | "global";
52
52
  requests: {
@@ -60,8 +60,8 @@ declare const reactiveFunctions: {
60
60
  }[];
61
61
  formData?: Record<string, unknown> | undefined;
62
62
  prevData?: Record<string, unknown> | null | undefined;
63
- }, any, questpie80.JsonRouteParams>;
64
- readonly fieldOptions: questpie80.JsonRouteDefinition<{
63
+ }, any, questpie131.JsonRouteParams>;
64
+ readonly fieldOptions: questpie131.JsonRouteDefinition<{
65
65
  collection: string;
66
66
  type: "collection" | "global";
67
67
  field: string;
@@ -70,7 +70,7 @@ declare const reactiveFunctions: {
70
70
  page: number;
71
71
  limit: number;
72
72
  siblingData?: Record<string, unknown> | null | undefined;
73
- }, any, questpie80.JsonRouteParams>;
73
+ }, any, questpie131.JsonRouteParams>;
74
74
  };
75
75
  //#endregion
76
76
  export { batchReactive, fieldOptions, reactiveFunctions };
@@ -60,6 +60,41 @@ function getAdminConfig(app) {
60
60
  return getAppState(app).config?.admin || {};
61
61
  }
62
62
  /**
63
+ * Get registered collection definitions from the current or legacy app shape.
64
+ */
65
+ function getCollections(app) {
66
+ const appRec = app;
67
+ if (typeof appRec.getCollections === "function") return appRec.getCollections();
68
+ return getAppState(app).collections ?? {};
69
+ }
70
+ /**
71
+ * Get a registered collection definition by slug.
72
+ */
73
+ function getCollection(app, collectionSlug) {
74
+ return getCollections(app)[collectionSlug];
75
+ }
76
+ /**
77
+ * Get collection CRUD APIs from the current or legacy app shape.
78
+ */
79
+ function getCollectionCrud(app, collectionSlug) {
80
+ const appRec = app;
81
+ return appRec.collections?.[collectionSlug] ?? appRec.api?.collections?.[collectionSlug] ?? appRec._api?.collections?.[collectionSlug];
82
+ }
83
+ /**
84
+ * Get all collection CRUD APIs from the current or legacy app shape.
85
+ */
86
+ function getCollectionCruds(app) {
87
+ const appRec = app;
88
+ return appRec.collections ?? appRec.api?.collections ?? appRec._api?.collections;
89
+ }
90
+ /**
91
+ * Get all global CRUD APIs from the current or legacy app shape.
92
+ */
93
+ function getGlobalCruds(app) {
94
+ const appRec = app;
95
+ return appRec.globals ?? appRec.api?.globals ?? appRec._api?.globals;
96
+ }
97
+ /**
63
98
  * Build a ReactiveServerContext from route handler context.
64
99
  */
65
100
  function buildServerContext(ctx) {
@@ -73,4 +108,4 @@ function buildServerContext(ctx) {
73
108
  }
74
109
 
75
110
  //#endregion
76
- export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollectionState, getGlobalState, getLocale, getSession };
111
+ export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollection, getCollectionCrud, getCollectionCruds, getCollectionState, getGlobalCruds, getGlobalState, getLocale, getSession };
@@ -1,14 +1,7 @@
1
- import * as questpie72 from "questpie";
1
+ import * as questpie139 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/setup.d.ts
4
4
 
5
- /**
6
- * Setup Functions
7
- *
8
- * Built-in functions for bootstrapping the first admin user.
9
- * Solves the chicken-and-egg problem where invitation-based systems
10
- * need an existing admin to create the first invitation.
11
- */
12
5
  /**
13
6
  * Check if setup is required (no admin users exist in the system).
14
7
  *
@@ -20,7 +13,7 @@ import * as questpie72 from "questpie";
20
13
  * }
21
14
  * ```
22
15
  */
23
- declare const isSetupRequired: questpie72.JsonRouteDefinition<Record<string, never>, any, questpie72.JsonRouteParams>;
16
+ declare const isSetupRequired: questpie139.JsonRouteDefinition<Record<string, never>, any, questpie139.JsonRouteParams>;
24
17
  /**
25
18
  * Create the first admin user in the system.
26
19
  * This function only works when no admin users exist (setup mode).
@@ -43,21 +36,21 @@ declare const isSetupRequired: questpie72.JsonRouteDefinition<Record<string, nev
43
36
  * }
44
37
  * ```
45
38
  */
46
- declare const createFirstAdmin: questpie72.JsonRouteDefinition<{
39
+ declare const createFirstAdmin: questpie139.JsonRouteDefinition<{
47
40
  email: string;
48
41
  password: string;
49
42
  name: string;
50
- }, any, questpie72.JsonRouteParams>;
43
+ }, any, questpie139.JsonRouteParams>;
51
44
  /**
52
45
  * Bundle of setup-related functions.
53
46
  */
54
47
  declare const setupFunctions: {
55
- readonly isSetupRequired: questpie72.JsonRouteDefinition<Record<string, never>, any, questpie72.JsonRouteParams>;
56
- readonly createFirstAdmin: questpie72.JsonRouteDefinition<{
48
+ readonly isSetupRequired: questpie139.JsonRouteDefinition<Record<string, never>, any, questpie139.JsonRouteParams>;
49
+ readonly createFirstAdmin: questpie139.JsonRouteDefinition<{
57
50
  email: string;
58
51
  password: string;
59
52
  name: string;
60
- }, any, questpie72.JsonRouteParams>;
53
+ }, any, questpie139.JsonRouteParams>;
61
54
  };
62
55
  //#endregion
63
56
  export { createFirstAdmin, isSetupRequired, setupFunctions };
@@ -28,6 +28,17 @@ const createFirstAdminOutputSchema = z.object({
28
28
  }).optional(),
29
29
  error: z.string().optional()
30
30
  });
31
+ const adminUserContractMessage = "QUESTPIE Admin requires the canonical Better Auth \"user\" collection from adminModule/starterModule. The current \"user\" collection is missing required admin auth fields. Do not replace collection(\"user\") from scratch; merge starterModule.collections.user or adminModule.collections.user and extend it.";
32
+ function getAdminUserTableContract(userCollection) {
33
+ const table = userCollection?.table;
34
+ const missing = [
35
+ "id",
36
+ "role",
37
+ "emailVerified"
38
+ ].filter((field$1) => !table?.[field$1]);
39
+ if (missing.length > 0) throw new Error(`${adminUserContractMessage} Missing field(s): ${missing.join(", ")}.`);
40
+ return table;
41
+ }
31
42
  /**
32
43
  * Check if setup is required (no admin users exist in the system).
33
44
  *
@@ -42,7 +53,8 @@ const createFirstAdminOutputSchema = z.object({
42
53
  const isSetupRequired = route().post().schema(isSetupRequiredSchema).outputSchema(isSetupRequiredOutputSchema).handler(async (ctx) => {
43
54
  const app = getApp(ctx);
44
55
  const userCollection = app.getCollectionConfig("user");
45
- return { required: (await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userCollection.table.role, "admin")))[0].count === 0 };
56
+ const userTable = getAdminUserTableContract(userCollection);
57
+ return { required: (await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userTable.role, "admin")))[0].count === 0 };
46
58
  });
47
59
  /**
48
60
  * Create the first admin user in the system.
@@ -72,7 +84,8 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
72
84
  const t = (key, params) => translateAdminMessage(locale, key, params);
73
85
  const input = ctx.input;
74
86
  const userCollection = app.getCollectionConfig("user");
75
- if ((await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userCollection.table.role, "admin")))[0].count > 0) return {
87
+ const userTable = getAdminUserTableContract(userCollection);
88
+ if ((await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userTable.role, "admin")))[0].count > 0) return {
76
89
  success: false,
77
90
  error: t("auth.setupAlreadyCompleted")
78
91
  };
@@ -89,7 +102,7 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
89
102
  await app.db.update(userCollection.table).set({
90
103
  role: "admin",
91
104
  emailVerified: true
92
- }).where(eq(userCollection.table.id, signUpResult.user.id));
105
+ }).where(eq(userTable.id, signUpResult.user.id));
93
106
  return {
94
107
  success: true,
95
108
  user: {
@@ -1,4 +1,4 @@
1
- import * as questpie397 from "questpie";
1
+ import * as questpie458 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/translations.d.ts
4
4
 
@@ -17,10 +17,10 @@ import * as questpie397 from "questpie";
17
17
  * Bundle of translation-related functions.
18
18
  */
19
19
  declare const translationFunctions: {
20
- readonly getAdminTranslations: questpie397.JsonRouteDefinition<{
20
+ readonly getAdminTranslations: questpie458.JsonRouteDefinition<{
21
21
  locale: string;
22
- }, any, questpie397.JsonRouteParams>;
23
- readonly getAdminLocales: questpie397.JsonRouteDefinition<Record<string, never> | undefined, any, questpie397.JsonRouteParams>;
22
+ }, any, questpie458.JsonRouteParams>;
23
+ readonly getAdminLocales: questpie458.JsonRouteDefinition<Record<string, never> | undefined, any, questpie458.JsonRouteParams>;
24
24
  };
25
25
  //#endregion
26
26
  export { translationFunctions };
@@ -1,4 +1,4 @@
1
- import * as questpie16 from "questpie";
1
+ import * as questpie147 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/widget-data.d.ts
4
4
 
@@ -20,13 +20,13 @@ import * as questpie16 from "questpie";
20
20
  * const data = await client.routes.fetchWidgetData({ widgetId: "my-widget" });
21
21
  * ```
22
22
  */
23
- declare const fetchWidgetData: questpie16.JsonRouteDefinition<{
23
+ declare const fetchWidgetData: questpie147.JsonRouteDefinition<{
24
24
  widgetId: string;
25
- }, any, questpie16.JsonRouteParams>;
25
+ }, any, questpie147.JsonRouteParams>;
26
26
  declare const widgetDataFunctions: {
27
- readonly fetchWidgetData: questpie16.JsonRouteDefinition<{
27
+ readonly fetchWidgetData: questpie147.JsonRouteDefinition<{
28
28
  widgetId: string;
29
- }, any, questpie16.JsonRouteParams>;
29
+ }, any, questpie147.JsonRouteParams>;
30
30
  };
31
31
  //#endregion
32
32
  export { fetchWidgetData, widgetDataFunctions };
@@ -0,0 +1,8 @@
1
+ import { ListViewConfig } from "../../../augmentation/form-layout.mjs";
2
+ import { ViewDefinition } from "../../../augmentation/views.mjs";
3
+ import "../../../augmentation.mjs";
4
+
5
+ //#region src/server/modules/admin/views/list-view.d.ts
6
+ declare const _default: ViewDefinition<"list-view", "list", ListViewConfig>;
7
+ //#endregion
8
+ export { _default };
@@ -0,0 +1,7 @@
1
+ import { view } from "../../../registry-helpers.mjs";
2
+
3
+ //#region src/server/modules/admin/views/list-view.ts
4
+ var list_view_default = view("list-view", { kind: "list" });
5
+
6
+ //#endregion
7
+ export { list_view_default as default };
@@ -1,10 +1,10 @@
1
1
  import { FilterOperator, FilterRule, SortConfig, ViewConfiguration } from "../../../../shared/types/saved-views.types.mjs";
2
- import * as questpie_shared0 from "questpie/shared";
3
- import * as questpie36 from "questpie";
2
+ import * as questpie_shared16 from "questpie/shared";
3
+ import * as questpie69 from "questpie";
4
4
  import * as questpie_src_server_modules_core_fields_email_js1 from "questpie/src/server/modules/core/fields/email.js";
5
5
  import * as questpie_src_server_modules_core_fields_json_js1 from "questpie/src/server/modules/core/fields/json.js";
6
- import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
7
- import * as drizzle_orm0 from "drizzle-orm";
6
+ import * as drizzle_orm_pg_core22 from "drizzle-orm/pg-core";
7
+ import * as drizzle_orm5 from "drizzle-orm";
8
8
 
9
9
  //#region src/server/modules/admin-preferences/collections/saved-views.d.ts
10
10
 
@@ -31,61 +31,61 @@ import * as drizzle_orm0 from "drizzle-orm";
31
31
  * });
32
32
  * ```
33
33
  */
34
- declare const savedViewsCollection: questpie36.CollectionBuilder<questpie_shared0.Override<questpie_shared0.Override<questpie36.EmptyCollectionState<"admin_saved_views", undefined, {
35
- readonly text: typeof questpie36.text;
36
- readonly textarea: typeof questpie36.textarea;
34
+ declare const savedViewsCollection: questpie69.CollectionBuilder<questpie_shared16.Override<questpie_shared16.Override<questpie69.EmptyCollectionState<"admin_saved_views", undefined, {
35
+ readonly text: typeof questpie69.text;
36
+ readonly textarea: typeof questpie69.textarea;
37
37
  readonly email: typeof questpie_src_server_modules_core_fields_email_js1.email;
38
- readonly url: typeof questpie36.url;
39
- readonly number: typeof questpie36.number;
40
- readonly boolean: typeof questpie36.boolean;
41
- readonly date: typeof questpie36.date;
42
- readonly datetime: typeof questpie36.datetime;
43
- readonly time: typeof questpie36.time;
44
- readonly select: typeof questpie36.select;
45
- readonly upload: typeof questpie36.upload;
46
- readonly relation: typeof questpie36.relation;
47
- readonly object: typeof questpie36.object;
38
+ readonly url: typeof questpie69.url;
39
+ readonly number: typeof questpie69.number;
40
+ readonly boolean: typeof questpie69.boolean;
41
+ readonly date: typeof questpie69.date;
42
+ readonly datetime: typeof questpie69.datetime;
43
+ readonly time: typeof questpie69.time;
44
+ readonly select: typeof questpie69.select;
45
+ readonly upload: typeof questpie69.upload;
46
+ readonly relation: typeof questpie69.relation;
47
+ readonly object: typeof questpie69.object;
48
48
  readonly json: typeof questpie_src_server_modules_core_fields_json_js1.json;
49
- readonly from: typeof questpie36.from;
49
+ readonly from: typeof questpie69.from;
50
50
  }>, {
51
51
  fields: {
52
- readonly userId: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
53
- readonly collectionName: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
54
- readonly name: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
55
- readonly configuration: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgJsonbBuilder>;
56
- readonly isDefault: drizzle_orm0.HasDefault<drizzle_orm_pg_core0.PgBooleanBuilder>;
52
+ readonly userId: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
53
+ readonly collectionName: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
54
+ readonly name: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
55
+ readonly configuration: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgJsonbBuilder>;
56
+ readonly isDefault: drizzle_orm5.HasDefault<drizzle_orm_pg_core22.PgBooleanBuilder>;
57
57
  };
58
58
  localized: readonly string[];
59
59
  fieldDefinitions: {
60
- readonly userId: questpie36.FieldWithMethods<Omit<questpie36.TextFieldState, "notNull" | "column"> & {
60
+ readonly userId: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
61
61
  notNull: true;
62
- column: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
62
+ column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
63
63
  } & {
64
- label: questpie_shared0.I18nText;
65
- }, questpie36.TextFieldMethods>;
66
- readonly collectionName: questpie36.FieldWithMethods<Omit<questpie36.TextFieldState, "notNull" | "column"> & {
64
+ label: questpie_shared16.I18nText;
65
+ }, questpie69.TextFieldMethods>;
66
+ readonly collectionName: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
67
67
  notNull: true;
68
- column: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
68
+ column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
69
69
  } & {
70
- label: questpie_shared0.I18nText;
71
- }, questpie36.TextFieldMethods>;
72
- readonly name: questpie36.FieldWithMethods<Omit<questpie36.TextFieldState, "notNull" | "column"> & {
70
+ label: questpie_shared16.I18nText;
71
+ }, questpie69.TextFieldMethods>;
72
+ readonly name: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
73
73
  notNull: true;
74
- column: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
74
+ column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
75
75
  } & {
76
- label: questpie_shared0.I18nText;
77
- }, questpie36.TextFieldMethods>;
78
- readonly configuration: questpie36.Field<Omit<questpie36.JsonFieldState, "notNull" | "column"> & {
76
+ label: questpie_shared16.I18nText;
77
+ }, questpie69.TextFieldMethods>;
78
+ readonly configuration: questpie69.Field<Omit<questpie69.JsonFieldState, "notNull" | "column"> & {
79
79
  notNull: true;
80
- column: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgJsonbBuilder>;
80
+ column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgJsonbBuilder>;
81
81
  } & {
82
- label: questpie_shared0.I18nText;
82
+ label: questpie_shared16.I18nText;
83
83
  }>;
84
- readonly isDefault: questpie36.Field<Omit<questpie36.BooleanFieldState, "column" | "hasDefault"> & {
84
+ readonly isDefault: questpie69.Field<Omit<questpie69.BooleanFieldState, "column" | "hasDefault"> & {
85
85
  hasDefault: true;
86
- column: drizzle_orm0.HasDefault<drizzle_orm_pg_core0.PgBooleanBuilder>;
86
+ column: drizzle_orm5.HasDefault<drizzle_orm_pg_core22.PgBooleanBuilder>;
87
87
  } & {
88
- label: questpie_shared0.I18nText;
88
+ label: questpie_shared16.I18nText;
89
89
  }>;
90
90
  };
91
91
  }>, {
@@ -1,6 +1,6 @@
1
1
  import { auditLogCollection } from "../collections/audit-log.mjs";
2
2
  import { auditCleanupJob } from "../jobs/audit-cleanup.mjs";
3
- import * as questpie146 from "questpie";
3
+ import * as questpie16 from "questpie";
4
4
 
5
5
  //#region src/server/modules/audit/.generated/module.d.ts
6
6
  interface AuditCollections {
@@ -43,13 +43,13 @@ declare const _module: {
43
43
  app: {
44
44
  hooks: {
45
45
  collections: {
46
- afterChange: (ctx: questpie146.GlobalCollectionHookContext) => Promise<void>;
47
- afterDelete: (ctx: questpie146.GlobalCollectionHookContext) => Promise<void>;
48
- afterTransition: (ctx: questpie146.GlobalCollectionTransitionHookContext) => Promise<void>;
46
+ afterChange: (ctx: questpie16.GlobalCollectionHookContext) => Promise<void>;
47
+ afterDelete: (ctx: questpie16.GlobalCollectionHookContext) => Promise<void>;
48
+ afterTransition: (ctx: questpie16.GlobalCollectionTransitionHookContext) => Promise<void>;
49
49
  };
50
50
  globals: {
51
- afterChange: (ctx: questpie146.GlobalGlobalHookContext) => Promise<void>;
52
- afterTransition: (ctx: questpie146.GlobalGlobalTransitionHookContext) => Promise<void>;
51
+ afterChange: (ctx: questpie16.GlobalGlobalHookContext) => Promise<void>;
52
+ afterTransition: (ctx: questpie16.GlobalGlobalTransitionHookContext) => Promise<void>;
53
53
  };
54
54
  };
55
55
  };