@questpie/admin 3.0.9 → 3.2.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 (46) hide show
  1. package/dist/augmentation.d.mts +39 -0
  2. package/dist/client/builder/page/page.d.mts +29 -1
  3. package/dist/client/builder/types/field-types.d.mts +10 -14
  4. package/dist/client/components/fields/relation-picker.mjs +2 -2
  5. package/dist/client/components/fields/relation-select.mjs +3 -3
  6. package/dist/client/hooks/use-audit-history.mjs +10 -17
  7. package/dist/client/hooks/use-reactive-prop.mjs +308 -0
  8. package/dist/client/hooks/use-transition-stage.mjs +34 -41
  9. package/dist/client/preview/block-scope-context.d.mts +2 -2
  10. package/dist/client/preview/preview-banner.d.mts +2 -2
  11. package/dist/client/preview/preview-field.d.mts +4 -4
  12. package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
  13. package/dist/client/views/auth/auth-layout.d.mts +3 -3
  14. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  15. package/dist/client/views/auth/setup-form.d.mts +2 -2
  16. package/dist/client/views/collection/field-renderer.mjs +59 -134
  17. package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
  18. package/dist/client/views/pages/invite-page.d.mts +2 -2
  19. package/dist/client/views/pages/login-page.d.mts +2 -2
  20. package/dist/client/views/pages/reset-password-page.d.mts +2 -2
  21. package/dist/client/views/pages/setup-page.d.mts +2 -2
  22. package/dist/client.d.mts +2 -1
  23. package/dist/client.mjs +2 -1
  24. package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
  25. package/dist/index.d.mts +2 -1
  26. package/dist/index.mjs +2 -1
  27. package/dist/server/augmentation/form-layout.d.mts +48 -4
  28. package/dist/server/augmentation/index.d.mts +1 -1
  29. package/dist/server/augmentation.d.mts +1 -1
  30. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  31. package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
  32. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
  33. package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
  34. package/dist/server/modules/admin/collections/assets.d.mts +20 -20
  35. package/dist/server/modules/admin/collections/session.d.mts +42 -42
  36. package/dist/server/modules/admin/collections/user.d.mts +32 -32
  37. package/dist/server/modules/admin/collections/verification.d.mts +32 -32
  38. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  39. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  40. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  41. package/dist/server/modules/admin/routes/reactive.d.mts +13 -11
  42. package/dist/server/modules/admin/routes/reactive.mjs +75 -11
  43. package/dist/server/modules/admin/routes/setup.d.mts +7 -7
  44. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  45. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -27
  46. package/package.json +4 -4
@@ -179,6 +179,49 @@ function getReactiveHandler(app, entityName, fieldPath, handlerType, type = "col
179
179
  return null;
180
180
  }
181
181
  /**
182
+ * Coerce a `function | { handler }` config-like value to the underlying
183
+ * handler function. Returns `null` for static values.
184
+ */
185
+ function unwrapHandler(value) {
186
+ if (typeof value === "function") return value;
187
+ if (value !== null && typeof value === "object" && "handler" in value && typeof value.handler === "function") return value.handler;
188
+ return null;
189
+ }
190
+ /**
191
+ * Get a reactive prop handler. Resolution chain (first hit wins):
192
+ *
193
+ * 1. **Layout-level** `state.adminForm.fields[*].props[propPath]`
194
+ * (per-instance `.form()` override)
195
+ * 2. **Field-level** `state.fieldDefinitions[fieldPath]._state.extensions.admin[propPath]`
196
+ * (default attached to the field via `f.<x>().admin({ ... })`)
197
+ *
198
+ * The wire-side counterpart is the `ReactivePropPlaceholder` introspection
199
+ * emits for function values in either location.
200
+ */
201
+ function getReactivePropHandler(app, entityName, fieldPath, propPath, type = "collection") {
202
+ const formConfig = getEntity(app, entityName, type).state.adminForm;
203
+ const fieldEntry = findReactiveFieldEntry(formConfig, fieldPath);
204
+ if (fieldEntry) {
205
+ const props = fieldEntry.props;
206
+ if (props && typeof props === "object") {
207
+ const layoutHandler = unwrapHandler(props[propPath]);
208
+ if (layoutHandler) return layoutHandler;
209
+ }
210
+ }
211
+ let fieldDef;
212
+ try {
213
+ fieldDef = getFieldDefinition(app, entityName, fieldPath, type);
214
+ } catch {
215
+ return null;
216
+ }
217
+ const adminMeta = fieldDef._state?.extensions?.admin;
218
+ if (adminMeta && typeof adminMeta === "object") {
219
+ const fieldHandler = unwrapHandler(adminMeta[propPath]);
220
+ if (fieldHandler) return fieldHandler;
221
+ }
222
+ return null;
223
+ }
224
+ /**
182
225
  * Get options handler from field config.
183
226
  */
184
227
  function getOptionsHandler(fieldDef) {
@@ -196,8 +239,10 @@ const reactiveRequestSchema = z.object({
196
239
  "hidden",
197
240
  "readOnly",
198
241
  "disabled",
199
- "compute"
242
+ "compute",
243
+ "prop"
200
244
  ]),
245
+ propPath: z.string().optional(),
201
246
  formData: z.record(z.string(), z.unknown()).optional(),
202
247
  siblingData: z.record(z.string(), z.unknown()).nullable().optional(),
203
248
  prevData: z.record(z.string(), z.unknown()).nullable().optional(),
@@ -222,8 +267,10 @@ const reactiveResultSchema = z.object({
222
267
  "hidden",
223
268
  "readOnly",
224
269
  "disabled",
225
- "compute"
270
+ "compute",
271
+ "prop"
226
272
  ]),
273
+ propPath: z.string().optional(),
227
274
  value: z.unknown(),
228
275
  error: z.string().optional()
229
276
  });
@@ -264,27 +311,44 @@ const batchReactive = route().post().schema(batchReactiveInputSchema).outputSche
264
311
  const { collection: entityName, type: entityType, requests, formData: sharedFormData, prevData: sharedPrevData } = ctx.input;
265
312
  const serverCtx = buildServerContext(ctx);
266
313
  return { results: await Promise.all(requests.map(async (request) => {
267
- const { field: field$1, type, formData, siblingData, prevData, prevSiblingData } = request;
314
+ const { field: field$1, type, propPath, formData, siblingData, prevData, prevSiblingData } = request;
268
315
  const resolvedFormData = formData ?? sharedFormData ?? {};
269
316
  const resolvedPrevData = prevData ?? sharedPrevData ?? null;
270
317
  try {
271
318
  getFieldDefinition(app, entityName, field$1, entityType);
272
- const handler = getReactiveHandler(app, entityName, field$1, type, entityType);
273
- if (!handler) return {
274
- field: field$1,
275
- type,
276
- value: void 0,
277
- error: `No ${type} handler found for field '${field$1}'`
278
- };
319
+ let handler;
320
+ if (type === "prop") {
321
+ if (!propPath) return {
322
+ field: field$1,
323
+ type,
324
+ value: void 0,
325
+ error: "propPath is required when type === 'prop'"
326
+ };
327
+ handler = getReactivePropHandler(app, entityName, field$1, propPath, entityType);
328
+ } else handler = getReactiveHandler(app, entityName, field$1, type, entityType);
329
+ if (!handler) {
330
+ const what = type === "prop" ? `prop '${propPath}'` : `${type} handler`;
331
+ return {
332
+ field: field$1,
333
+ type,
334
+ ...propPath ? { propPath } : {},
335
+ value: void 0,
336
+ error: `No ${what} found for field '${field$1}'`
337
+ };
338
+ }
339
+ const reactiveCtx = buildReactiveContext(resolvedFormData, siblingData, resolvedPrevData, prevSiblingData, serverCtx);
340
+ const value = await handler(reactiveCtx);
279
341
  return {
280
342
  field: field$1,
281
343
  type,
282
- value: await handler(buildReactiveContext(resolvedFormData, siblingData, resolvedPrevData, prevSiblingData, serverCtx))
344
+ ...propPath ? { propPath } : {},
345
+ value
283
346
  };
284
347
  } catch (error) {
285
348
  return {
286
349
  field: field$1,
287
350
  type,
351
+ ...propPath ? { propPath } : {},
288
352
  value: void 0,
289
353
  error: error instanceof Error ? error.message : String(error)
290
354
  };
@@ -1,4 +1,4 @@
1
- import * as questpie143 from "questpie";
1
+ import * as questpie139 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/setup.d.ts
4
4
 
@@ -20,7 +20,7 @@ import * as questpie143 from "questpie";
20
20
  * }
21
21
  * ```
22
22
  */
23
- declare const isSetupRequired: questpie143.JsonRouteDefinition<Record<string, never>, any, questpie143.JsonRouteParams>;
23
+ declare const isSetupRequired: questpie139.JsonRouteDefinition<Record<string, never>, any, questpie139.JsonRouteParams>;
24
24
  /**
25
25
  * Create the first admin user in the system.
26
26
  * This function only works when no admin users exist (setup mode).
@@ -43,21 +43,21 @@ declare const isSetupRequired: questpie143.JsonRouteDefinition<Record<string, ne
43
43
  * }
44
44
  * ```
45
45
  */
46
- declare const createFirstAdmin: questpie143.JsonRouteDefinition<{
46
+ declare const createFirstAdmin: questpie139.JsonRouteDefinition<{
47
47
  email: string;
48
48
  password: string;
49
49
  name: string;
50
- }, any, questpie143.JsonRouteParams>;
50
+ }, any, questpie139.JsonRouteParams>;
51
51
  /**
52
52
  * Bundle of setup-related functions.
53
53
  */
54
54
  declare const setupFunctions: {
55
- readonly isSetupRequired: questpie143.JsonRouteDefinition<Record<string, never>, any, questpie143.JsonRouteParams>;
56
- readonly createFirstAdmin: questpie143.JsonRouteDefinition<{
55
+ readonly isSetupRequired: questpie139.JsonRouteDefinition<Record<string, never>, any, questpie139.JsonRouteParams>;
56
+ readonly createFirstAdmin: questpie139.JsonRouteDefinition<{
57
57
  email: string;
58
58
  password: string;
59
59
  name: string;
60
- }, any, questpie143.JsonRouteParams>;
60
+ }, any, questpie139.JsonRouteParams>;
61
61
  };
62
62
  //#endregion
63
63
  export { createFirstAdmin, isSetupRequired, setupFunctions };
@@ -1,4 +1,4 @@
1
- import * as questpie131 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 questpie131 from "questpie";
20
20
  * const data = await client.routes.fetchWidgetData({ widgetId: "my-widget" });
21
21
  * ```
22
22
  */
23
- declare const fetchWidgetData: questpie131.JsonRouteDefinition<{
23
+ declare const fetchWidgetData: questpie147.JsonRouteDefinition<{
24
24
  widgetId: string;
25
- }, any, questpie131.JsonRouteParams>;
25
+ }, any, questpie147.JsonRouteParams>;
26
26
  declare const widgetDataFunctions: {
27
- readonly fetchWidgetData: questpie131.JsonRouteDefinition<{
27
+ readonly fetchWidgetData: questpie147.JsonRouteDefinition<{
28
28
  widgetId: string;
29
- }, any, questpie131.JsonRouteParams>;
29
+ }, any, questpie147.JsonRouteParams>;
30
30
  };
31
31
  //#endregion
32
32
  export { fetchWidgetData, widgetDataFunctions };
@@ -1,8 +1,8 @@
1
1
  import { FilterOperator, FilterRule, SortConfig, ViewConfiguration } from "../../../../shared/types/saved-views.types.mjs";
2
2
  import * as questpie_shared15 from "questpie/shared";
3
- import * as questpie93 from "questpie";
4
- import * as questpie_src_server_modules_core_fields_email_js2 from "questpie/src/server/modules/core/fields/email.js";
5
- import * as questpie_src_server_modules_core_fields_json_js2 from "questpie/src/server/modules/core/fields/json.js";
3
+ import * as questpie69 from "questpie";
4
+ import * as questpie_src_server_modules_core_fields_email_js1 from "questpie/src/server/modules/core/fields/email.js";
5
+ import * as questpie_src_server_modules_core_fields_json_js1 from "questpie/src/server/modules/core/fields/json.js";
6
6
  import * as drizzle_orm_pg_core22 from "drizzle-orm/pg-core";
7
7
  import * as drizzle_orm5 from "drizzle-orm";
8
8
 
@@ -33,22 +33,22 @@ import * as drizzle_orm5 from "drizzle-orm";
33
33
  * });
34
34
  * ```
35
35
  */
36
- declare const savedViewsCollection: questpie93.CollectionBuilder<questpie_shared15.Override<questpie_shared15.Override<questpie93.EmptyCollectionState<"admin_saved_views", undefined, {
37
- readonly text: typeof questpie93.text;
38
- readonly textarea: typeof questpie93.textarea;
39
- readonly email: typeof questpie_src_server_modules_core_fields_email_js2.email;
40
- readonly url: typeof questpie93.url;
41
- readonly number: typeof questpie93.number;
42
- readonly boolean: typeof questpie93.boolean;
43
- readonly date: typeof questpie93.date;
44
- readonly datetime: typeof questpie93.datetime;
45
- readonly time: typeof questpie93.time;
46
- readonly select: typeof questpie93.select;
47
- readonly upload: typeof questpie93.upload;
48
- readonly relation: typeof questpie93.relation;
49
- readonly object: typeof questpie93.object;
50
- readonly json: typeof questpie_src_server_modules_core_fields_json_js2.json;
51
- readonly from: typeof questpie93.from;
36
+ declare const savedViewsCollection: questpie69.CollectionBuilder<questpie_shared15.Override<questpie_shared15.Override<questpie69.EmptyCollectionState<"admin_saved_views", undefined, {
37
+ readonly text: typeof questpie69.text;
38
+ readonly textarea: typeof questpie69.textarea;
39
+ readonly email: typeof questpie_src_server_modules_core_fields_email_js1.email;
40
+ readonly url: typeof questpie69.url;
41
+ readonly number: typeof questpie69.number;
42
+ readonly boolean: typeof questpie69.boolean;
43
+ readonly date: typeof questpie69.date;
44
+ readonly datetime: typeof questpie69.datetime;
45
+ readonly time: typeof questpie69.time;
46
+ readonly select: typeof questpie69.select;
47
+ readonly upload: typeof questpie69.upload;
48
+ readonly relation: typeof questpie69.relation;
49
+ readonly object: typeof questpie69.object;
50
+ readonly json: typeof questpie_src_server_modules_core_fields_json_js1.json;
51
+ readonly from: typeof questpie69.from;
52
52
  }>, {
53
53
  fields: {
54
54
  readonly userId: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
@@ -59,31 +59,31 @@ declare const savedViewsCollection: questpie93.CollectionBuilder<questpie_shared
59
59
  };
60
60
  localized: readonly string[];
61
61
  fieldDefinitions: {
62
- readonly userId: questpie93.FieldWithMethods<Omit<questpie93.TextFieldState, "notNull" | "column"> & {
62
+ readonly userId: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
63
63
  notNull: true;
64
64
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
65
65
  } & {
66
66
  label: questpie_shared15.I18nText;
67
- }, questpie93.TextFieldMethods>;
68
- readonly collectionName: questpie93.FieldWithMethods<Omit<questpie93.TextFieldState, "notNull" | "column"> & {
67
+ }, questpie69.TextFieldMethods>;
68
+ readonly collectionName: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
69
69
  notNull: true;
70
70
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
71
71
  } & {
72
72
  label: questpie_shared15.I18nText;
73
- }, questpie93.TextFieldMethods>;
74
- readonly name: questpie93.FieldWithMethods<Omit<questpie93.TextFieldState, "notNull" | "column"> & {
73
+ }, questpie69.TextFieldMethods>;
74
+ readonly name: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
75
75
  notNull: true;
76
76
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
77
77
  } & {
78
78
  label: questpie_shared15.I18nText;
79
- }, questpie93.TextFieldMethods>;
80
- readonly configuration: questpie93.Field<Omit<questpie93.JsonFieldState, "notNull" | "column"> & {
79
+ }, questpie69.TextFieldMethods>;
80
+ readonly configuration: questpie69.Field<Omit<questpie69.JsonFieldState, "notNull" | "column"> & {
81
81
  notNull: true;
82
82
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgJsonbBuilder>;
83
83
  } & {
84
84
  label: questpie_shared15.I18nText;
85
85
  }>;
86
- readonly isDefault: questpie93.Field<Omit<questpie93.BooleanFieldState, "column" | "hasDefault"> & {
86
+ readonly isDefault: questpie69.Field<Omit<questpie69.BooleanFieldState, "column" | "hasDefault"> & {
87
87
  hasDefault: true;
88
88
  column: drizzle_orm5.HasDefault<drizzle_orm_pg_core22.PgBooleanBuilder>;
89
89
  } & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@questpie/admin",
3
- "version": "3.0.9",
3
+ "version": "3.2.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/questpie/questpie.git",
@@ -49,7 +49,7 @@
49
49
  "dev": "tsdown --watch",
50
50
  "build": "tsdown",
51
51
  "check-types": "tsc --noEmit",
52
- "test": "bun test test/",
52
+ "test": "bun test --max-concurrency=1 test/",
53
53
  "test:watch": "bun test --watch test/",
54
54
  "questpie:generate": "bun x questpie generate --verbose"
55
55
  },
@@ -62,7 +62,7 @@
62
62
  "@fontsource-variable/jetbrains-mono": "^5.2.8",
63
63
  "@hookform/resolvers": "^5.1.0",
64
64
  "@iconify/react": "^6.0.2",
65
- "@questpie/tanstack-query": "^3.0.9",
65
+ "@questpie/tanstack-query": "^3.2.0",
66
66
  "@tailwindcss/vite": "^4.0.6",
67
67
  "@tiptap/core": "^2.x",
68
68
  "@tiptap/extension-character-count": "^2.x",
@@ -88,7 +88,7 @@
88
88
  "date-fns": "^4.1.0",
89
89
  "lowlight": "^3.x",
90
90
  "next-themes": "^0.4.6",
91
- "questpie": "^3.0.9",
91
+ "questpie": "^3.2.0",
92
92
  "react-day-picker": "^9.12.0",
93
93
  "react-hook-form": "^7.54.0",
94
94
  "react-resizable-panels": "^4.4.2",