@btst/stack 1.5.2 → 1.6.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 (201) hide show
  1. package/dist/node_modules/.pnpm/@dnd-kit_accessibility@3.1.1_react@19.2.0/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.cjs +68 -0
  2. package/dist/node_modules/.pnpm/@dnd-kit_accessibility@3.1.1_react@19.2.0/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.mjs +60 -0
  3. package/dist/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.cjs +3937 -0
  4. package/dist/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.mjs +3907 -0
  5. package/dist/node_modules/.pnpm/@dnd-kit_modifiers@9.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/modifiers/dist/modifiers.esm.cjs +30 -0
  6. package/dist/node_modules/.pnpm/@dnd-kit_modifiers@9.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/modifiers/dist/modifiers.esm.mjs +28 -0
  7. package/dist/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/sortable/dist/sortable.esm.cjs +675 -0
  8. package/dist/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/sortable/dist/sortable.esm.mjs +661 -0
  9. package/dist/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.2.0/node_modules/@dnd-kit/utilities/dist/utilities.esm.cjs +358 -0
  10. package/dist/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.2.0/node_modules/@dnd-kit/utilities/dist/utilities.esm.mjs +332 -0
  11. package/dist/node_modules/.pnpm/@radix-ui_react-tabs@1.1.13_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_865f042350eb43f3338b0fffb33f6246/node_modules/@radix-ui/react-tabs/dist/index.cjs +211 -0
  12. package/dist/node_modules/.pnpm/@radix-ui_react-tabs@1.1.13_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_865f042350eb43f3338b0fffb33f6246/node_modules/@radix-ui/react-tabs/dist/index.mjs +188 -0
  13. package/dist/packages/better-stack/src/plugins/cms/api/plugin.cjs +3 -2
  14. package/dist/packages/better-stack/src/plugins/cms/api/plugin.mjs +3 -2
  15. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.cjs +15 -15
  16. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.mjs +16 -16
  17. package/dist/packages/better-stack/src/plugins/form-builder/api/plugin.cjs +588 -0
  18. package/dist/packages/better-stack/src/plugins/form-builder/api/plugin.mjs +586 -0
  19. package/dist/packages/better-stack/src/plugins/form-builder/client/components/forms/form-renderer.cjs +131 -0
  20. package/dist/packages/better-stack/src/plugins/form-builder/client/components/forms/form-renderer.mjs +129 -0
  21. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-builder-skeleton.cjs +32 -0
  22. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-builder-skeleton.mjs +30 -0
  23. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-list-skeleton.cjs +21 -0
  24. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-list-skeleton.mjs +19 -0
  25. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/submissions-skeleton.cjs +34 -0
  26. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/submissions-skeleton.mjs +32 -0
  27. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/404-page.cjs +20 -0
  28. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/404-page.mjs +18 -0
  29. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.cjs +19 -0
  30. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.cjs +186 -0
  31. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.mjs +184 -0
  32. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.mjs +17 -0
  33. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.cjs +19 -0
  34. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.cjs +165 -0
  35. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.mjs +163 -0
  36. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.mjs +17 -0
  37. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.cjs +19 -0
  38. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +177 -0
  39. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +175 -0
  40. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.mjs +17 -0
  41. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/default-error.cjs +17 -0
  42. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/default-error.mjs +15 -0
  43. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/empty-state.cjs +16 -0
  44. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/empty-state.mjs +14 -0
  45. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/page-wrapper.cjs +27 -0
  46. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/page-wrapper.mjs +25 -0
  47. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/pagination.cjs +39 -0
  48. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/pagination.mjs +37 -0
  49. package/dist/packages/better-stack/src/plugins/form-builder/client/hooks/form-builder-hooks.cjs +551 -0
  50. package/dist/packages/better-stack/src/plugins/form-builder/client/hooks/form-builder-hooks.mjs +537 -0
  51. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-common.cjs +36 -0
  52. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-common.mjs +34 -0
  53. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-editor.cjs +19 -0
  54. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-editor.mjs +17 -0
  55. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-list.cjs +21 -0
  56. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-list.mjs +19 -0
  57. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-submissions.cjs +19 -0
  58. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-submissions.mjs +17 -0
  59. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-toasts.cjs +14 -0
  60. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-toasts.mjs +12 -0
  61. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/index.cjs +17 -0
  62. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/index.mjs +15 -0
  63. package/dist/packages/better-stack/src/plugins/form-builder/client/plugin.cjs +278 -0
  64. package/dist/packages/better-stack/src/plugins/form-builder/client/plugin.mjs +276 -0
  65. package/dist/packages/better-stack/src/plugins/form-builder/db.cjs +99 -0
  66. package/dist/packages/better-stack/src/plugins/form-builder/db.mjs +97 -0
  67. package/dist/packages/better-stack/src/plugins/form-builder/schemas.cjs +82 -0
  68. package/dist/packages/better-stack/src/plugins/form-builder/schemas.mjs +74 -0
  69. package/dist/packages/better-stack/src/plugins/form-builder/utils.cjs +37 -0
  70. package/dist/packages/better-stack/src/plugins/form-builder/utils.mjs +29 -0
  71. package/dist/packages/ui/src/components/auto-form/index.cjs +2 -12
  72. package/dist/packages/ui/src/components/auto-form/index.mjs +2 -9
  73. package/dist/packages/ui/src/components/auto-form/stepped-auto-form.cjs +377 -0
  74. package/dist/packages/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
  75. package/dist/packages/ui/src/components/auto-form/utils.cjs +1 -56
  76. package/dist/packages/ui/src/components/auto-form/utils.mjs +2 -56
  77. package/dist/packages/ui/src/components/form-builder/canvas.cjs +111 -0
  78. package/dist/packages/ui/src/components/form-builder/canvas.mjs +109 -0
  79. package/dist/packages/ui/src/components/form-builder/components/index.cjs +570 -0
  80. package/dist/packages/ui/src/components/form-builder/components/index.mjs +553 -0
  81. package/dist/packages/ui/src/components/form-builder/edit-field-dialog.cjs +131 -0
  82. package/dist/packages/ui/src/components/form-builder/edit-field-dialog.mjs +129 -0
  83. package/dist/packages/ui/src/components/form-builder/form-preview.cjs +73 -0
  84. package/dist/packages/ui/src/components/form-builder/form-preview.mjs +71 -0
  85. package/dist/packages/ui/src/components/form-builder/index.cjs +353 -0
  86. package/dist/packages/ui/src/components/form-builder/index.mjs +344 -0
  87. package/dist/packages/ui/src/components/form-builder/nested-field-editor-dialog.cjs +263 -0
  88. package/dist/packages/ui/src/components/form-builder/nested-field-editor-dialog.mjs +261 -0
  89. package/dist/packages/ui/src/components/form-builder/palette.cjs +52 -0
  90. package/dist/packages/ui/src/components/form-builder/palette.mjs +49 -0
  91. package/dist/packages/ui/src/components/form-builder/schema-utils.cjs +120 -0
  92. package/dist/packages/ui/src/components/form-builder/schema-utils.mjs +114 -0
  93. package/dist/packages/ui/src/components/form-builder/sortable-field.cjs +151 -0
  94. package/dist/packages/ui/src/components/form-builder/sortable-field.mjs +148 -0
  95. package/dist/packages/ui/src/components/form-builder/step-tabs.cjs +180 -0
  96. package/dist/packages/ui/src/components/form-builder/step-tabs.mjs +178 -0
  97. package/dist/packages/ui/src/components/form-builder/types.cjs +7 -0
  98. package/dist/packages/ui/src/components/form-builder/types.mjs +5 -0
  99. package/dist/packages/ui/src/components/form-builder/validation-schemas.cjs +67 -0
  100. package/dist/packages/ui/src/components/form-builder/validation-schemas.mjs +56 -0
  101. package/dist/packages/ui/src/components/tabs.cjs +70 -0
  102. package/dist/packages/ui/src/components/tabs.mjs +65 -0
  103. package/dist/packages/ui/src/lib/schema-converter.cjs +130 -0
  104. package/dist/packages/ui/src/lib/schema-converter.mjs +124 -0
  105. package/dist/plugins/blog/api/index.d.cts +1 -1
  106. package/dist/plugins/blog/api/index.d.mts +1 -1
  107. package/dist/plugins/blog/api/index.d.ts +1 -1
  108. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  109. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  110. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  111. package/dist/plugins/blog/client/index.d.cts +1 -1
  112. package/dist/plugins/blog/client/index.d.mts +1 -1
  113. package/dist/plugins/blog/client/index.d.ts +1 -1
  114. package/dist/plugins/blog/query-keys.d.cts +2 -2
  115. package/dist/plugins/blog/query-keys.d.mts +2 -2
  116. package/dist/plugins/blog/query-keys.d.ts +2 -2
  117. package/dist/plugins/cms/client/index.cjs +6 -0
  118. package/dist/plugins/cms/client/index.d.cts +6 -113
  119. package/dist/plugins/cms/client/index.d.mts +6 -113
  120. package/dist/plugins/cms/client/index.d.ts +6 -113
  121. package/dist/plugins/cms/client/index.mjs +1 -0
  122. package/dist/plugins/form-builder/api/index.cjs +7 -0
  123. package/dist/plugins/form-builder/api/index.d.cts +141 -0
  124. package/dist/plugins/form-builder/api/index.d.mts +141 -0
  125. package/dist/plugins/form-builder/api/index.d.ts +141 -0
  126. package/dist/plugins/form-builder/api/index.mjs +1 -0
  127. package/dist/plugins/form-builder/client/components/index.cjs +29 -0
  128. package/dist/plugins/form-builder/client/components/index.d.cts +93 -0
  129. package/dist/plugins/form-builder/client/components/index.d.mts +93 -0
  130. package/dist/plugins/form-builder/client/components/index.d.ts +93 -0
  131. package/dist/plugins/form-builder/client/components/index.mjs +18 -0
  132. package/dist/plugins/form-builder/client/hooks/index.cjs +19 -0
  133. package/dist/plugins/form-builder/client/hooks/index.d.cts +154 -0
  134. package/dist/plugins/form-builder/client/hooks/index.d.mts +154 -0
  135. package/dist/plugins/form-builder/client/hooks/index.d.ts +154 -0
  136. package/dist/plugins/form-builder/client/hooks/index.mjs +1 -0
  137. package/dist/plugins/form-builder/client/index.cjs +13 -0
  138. package/dist/plugins/form-builder/client/index.d.cts +381 -0
  139. package/dist/plugins/form-builder/client/index.d.mts +381 -0
  140. package/dist/plugins/form-builder/client/index.d.ts +381 -0
  141. package/dist/plugins/form-builder/client/index.mjs +2 -0
  142. package/dist/plugins/form-builder/client.css +3 -0
  143. package/dist/plugins/form-builder/query-keys.cjs +143 -0
  144. package/dist/plugins/form-builder/query-keys.d.cts +74 -0
  145. package/dist/plugins/form-builder/query-keys.d.mts +74 -0
  146. package/dist/plugins/form-builder/query-keys.d.ts +74 -0
  147. package/dist/plugins/form-builder/query-keys.mjs +141 -0
  148. package/dist/plugins/form-builder/style.css +19 -0
  149. package/dist/shared/stack.AX5nZ6A3.d.cts +86 -0
  150. package/dist/shared/stack.AX5nZ6A3.d.mts +86 -0
  151. package/dist/shared/stack.AX5nZ6A3.d.ts +86 -0
  152. package/dist/shared/stack.BIh2AXaW.d.cts +123 -0
  153. package/dist/shared/stack.BIh2AXaW.d.mts +123 -0
  154. package/dist/shared/stack.BIh2AXaW.d.ts +123 -0
  155. package/dist/shared/stack.DzH_wcvr.d.cts +195 -0
  156. package/dist/shared/stack.DzH_wcvr.d.mts +195 -0
  157. package/dist/shared/stack.DzH_wcvr.d.ts +195 -0
  158. package/package.json +54 -1
  159. package/src/plugins/cms/api/plugin.ts +9 -4
  160. package/src/plugins/cms/client/components/forms/content-form.tsx +23 -25
  161. package/src/plugins/cms/client/index.ts +11 -0
  162. package/src/plugins/form-builder/api/index.ts +1 -0
  163. package/src/plugins/form-builder/api/plugin.ts +776 -0
  164. package/src/plugins/form-builder/client/components/forms/form-renderer.tsx +253 -0
  165. package/src/plugins/form-builder/client/components/index.tsx +24 -0
  166. package/src/plugins/form-builder/client/components/loading/form-builder-skeleton.tsx +42 -0
  167. package/src/plugins/form-builder/client/components/loading/form-list-skeleton.tsx +25 -0
  168. package/src/plugins/form-builder/client/components/loading/index.tsx +3 -0
  169. package/src/plugins/form-builder/client/components/loading/submissions-skeleton.tsx +40 -0
  170. package/src/plugins/form-builder/client/components/pages/404-page.tsx +28 -0
  171. package/src/plugins/form-builder/client/components/pages/form-builder-page.internal.tsx +253 -0
  172. package/src/plugins/form-builder/client/components/pages/form-builder-page.tsx +26 -0
  173. package/src/plugins/form-builder/client/components/pages/form-list-page.internal.tsx +231 -0
  174. package/src/plugins/form-builder/client/components/pages/form-list-page.tsx +22 -0
  175. package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +268 -0
  176. package/src/plugins/form-builder/client/components/pages/submissions-page.tsx +26 -0
  177. package/src/plugins/form-builder/client/components/shared/default-error.tsx +30 -0
  178. package/src/plugins/form-builder/client/components/shared/empty-state.tsx +26 -0
  179. package/src/plugins/form-builder/client/components/shared/page-wrapper.tsx +32 -0
  180. package/src/plugins/form-builder/client/components/shared/pagination.tsx +52 -0
  181. package/src/plugins/form-builder/client/hooks/form-builder-hooks.tsx +799 -0
  182. package/src/plugins/form-builder/client/hooks/index.tsx +1 -0
  183. package/src/plugins/form-builder/client/index.ts +22 -0
  184. package/src/plugins/form-builder/client/localization/form-builder-common.ts +36 -0
  185. package/src/plugins/form-builder/client/localization/form-builder-editor.ts +18 -0
  186. package/src/plugins/form-builder/client/localization/form-builder-list.ts +17 -0
  187. package/src/plugins/form-builder/client/localization/form-builder-submissions.ts +17 -0
  188. package/src/plugins/form-builder/client/localization/form-builder-toasts.ts +10 -0
  189. package/src/plugins/form-builder/client/localization/index.ts +15 -0
  190. package/src/plugins/form-builder/client/overrides.ts +146 -0
  191. package/src/plugins/form-builder/client/plugin.tsx +488 -0
  192. package/src/plugins/form-builder/client.css +3 -0
  193. package/src/plugins/form-builder/db.ts +99 -0
  194. package/src/plugins/form-builder/query-keys.ts +198 -0
  195. package/src/plugins/form-builder/schemas.ts +122 -0
  196. package/src/plugins/form-builder/style.css +19 -0
  197. package/src/plugins/form-builder/types.ts +317 -0
  198. package/src/plugins/form-builder/utils.ts +63 -0
  199. package/dist/shared/{stack.DLhzx1-D.d.cts → stack.CcI4sYJP.d.cts} +1 -1
  200. package/dist/shared/{stack.DLhzx1-D.d.mts → stack.CcI4sYJP.d.mts} +1 -1
  201. package/dist/shared/{stack.DLhzx1-D.d.ts → stack.CcI4sYJP.d.ts} +1 -1
@@ -0,0 +1,588 @@
1
+ 'use strict';
2
+
3
+ const api = require('@btst/stack/plugins/api');
4
+ const z = require('zod');
5
+ const schemaConverter = require('../../../../../ui/src/lib/schema-converter.cjs');
6
+ const db = require('../db.cjs');
7
+ const schemas = require('../schemas.cjs');
8
+ const utils = require('../utils.cjs');
9
+
10
+ function serializeForm(form) {
11
+ return {
12
+ id: form.id,
13
+ name: form.name,
14
+ slug: form.slug,
15
+ description: form.description,
16
+ schema: form.schema,
17
+ successMessage: form.successMessage,
18
+ redirectUrl: form.redirectUrl,
19
+ status: form.status,
20
+ createdBy: form.createdBy,
21
+ createdAt: form.createdAt.toISOString(),
22
+ updatedAt: form.updatedAt.toISOString()
23
+ };
24
+ }
25
+ function serializeFormSubmission(submission) {
26
+ return {
27
+ ...submission,
28
+ submittedAt: submission.submittedAt.toISOString()
29
+ };
30
+ }
31
+ function serializeFormSubmissionWithData(submission) {
32
+ return {
33
+ ...serializeFormSubmission(submission),
34
+ parsedData: JSON.parse(submission.data),
35
+ form: submission.form ? serializeForm(submission.form) : void 0
36
+ };
37
+ }
38
+ const formBuilderBackendPlugin = (config = {}) => api.defineBackendPlugin({
39
+ name: "form-builder",
40
+ dbPlugin: db.formBuilderSchema,
41
+ routes: (adapter) => {
42
+ const createContext = (headers) => ({
43
+ headers,
44
+ ipAddress: utils.extractIpAddress(headers),
45
+ userAgent: utils.extractUserAgent(headers)
46
+ });
47
+ const createSubmissionContext = (formSlug, formId, headers) => ({
48
+ ...createContext(headers),
49
+ formSlug,
50
+ formId
51
+ });
52
+ const listForms = api.createEndpoint(
53
+ "/forms",
54
+ {
55
+ method: "GET",
56
+ query: schemas.listFormsQuerySchema
57
+ },
58
+ async (ctx) => {
59
+ const { status, limit, offset } = ctx.query;
60
+ const context = createContext(ctx.headers);
61
+ if (config.hooks?.onBeforeListForms) {
62
+ const canList = await config.hooks.onBeforeListForms(context);
63
+ if (!canList) {
64
+ throw ctx.error(403, { message: "Access denied" });
65
+ }
66
+ }
67
+ const whereConditions = [];
68
+ if (status) {
69
+ whereConditions.push({
70
+ field: "status",
71
+ value: status,
72
+ operator: "eq"
73
+ });
74
+ }
75
+ const allForms = await adapter.findMany({
76
+ model: "form",
77
+ where: whereConditions.length > 0 ? whereConditions : void 0
78
+ });
79
+ const total = allForms.length;
80
+ const forms = await adapter.findMany({
81
+ model: "form",
82
+ where: whereConditions.length > 0 ? whereConditions : void 0,
83
+ limit,
84
+ offset,
85
+ sortBy: { field: "createdAt", direction: "desc" }
86
+ });
87
+ return {
88
+ items: forms.map(serializeForm),
89
+ total,
90
+ limit,
91
+ offset
92
+ };
93
+ }
94
+ );
95
+ const getFormBySlug = api.createEndpoint(
96
+ "/forms/:slug",
97
+ {
98
+ method: "GET",
99
+ params: z.z.object({ slug: z.z.string() })
100
+ },
101
+ async (ctx) => {
102
+ const { slug } = ctx.params;
103
+ const context = createContext(ctx.headers);
104
+ if (config.hooks?.onBeforeGetForm) {
105
+ const canGet = await config.hooks.onBeforeGetForm(slug, context);
106
+ if (!canGet) {
107
+ throw ctx.error(403, { message: "Access denied" });
108
+ }
109
+ }
110
+ const form = await adapter.findOne({
111
+ model: "form",
112
+ where: [{ field: "slug", value: slug, operator: "eq" }]
113
+ });
114
+ if (!form) {
115
+ throw ctx.error(404, { message: "Form not found" });
116
+ }
117
+ return serializeForm(form);
118
+ }
119
+ );
120
+ const getFormById = api.createEndpoint(
121
+ "/forms/id/:id",
122
+ {
123
+ method: "GET",
124
+ params: z.z.object({ id: z.z.string() })
125
+ },
126
+ async (ctx) => {
127
+ const { id } = ctx.params;
128
+ const context = createContext(ctx.headers);
129
+ if (config.hooks?.onBeforeGetForm) {
130
+ const canGet = await config.hooks.onBeforeGetForm(id, context);
131
+ if (!canGet) {
132
+ throw ctx.error(403, { message: "Access denied" });
133
+ }
134
+ }
135
+ const form = await adapter.findOne({
136
+ model: "form",
137
+ where: [{ field: "id", value: id, operator: "eq" }]
138
+ });
139
+ if (!form) {
140
+ throw ctx.error(404, { message: "Form not found" });
141
+ }
142
+ return serializeForm(form);
143
+ }
144
+ );
145
+ const createForm = api.createEndpoint(
146
+ "/forms",
147
+ {
148
+ method: "POST",
149
+ body: schemas.createFormSchema
150
+ },
151
+ async (ctx) => {
152
+ const body = ctx.body;
153
+ const context = createContext(ctx.headers);
154
+ const slug = utils.slugify(body.slug);
155
+ if (!slug) {
156
+ throw ctx.error(400, {
157
+ message: "Invalid slug: must contain at least one alphanumeric character"
158
+ });
159
+ }
160
+ const existing = await adapter.findOne({
161
+ model: "form",
162
+ where: [{ field: "slug", value: slug, operator: "eq" }]
163
+ });
164
+ if (existing) {
165
+ throw ctx.error(409, {
166
+ message: "Form with this slug already exists"
167
+ });
168
+ }
169
+ try {
170
+ JSON.parse(body.schema);
171
+ } catch {
172
+ throw ctx.error(400, { message: "Invalid JSON Schema" });
173
+ }
174
+ let formInput = {
175
+ name: body.name,
176
+ slug,
177
+ description: body.description,
178
+ schema: body.schema,
179
+ successMessage: body.successMessage,
180
+ redirectUrl: body.redirectUrl || void 0,
181
+ status: body.status
182
+ };
183
+ if (config.hooks?.onBeforeFormCreated) {
184
+ const result = await config.hooks.onBeforeFormCreated(
185
+ formInput,
186
+ context
187
+ );
188
+ if (result === false) {
189
+ throw ctx.error(403, { message: "Create operation denied" });
190
+ }
191
+ if (result && typeof result === "object") {
192
+ formInput = result;
193
+ }
194
+ }
195
+ const form = await adapter.create({
196
+ model: "form",
197
+ data: {
198
+ name: formInput.name,
199
+ slug: formInput.slug,
200
+ description: formInput.description,
201
+ schema: formInput.schema,
202
+ successMessage: formInput.successMessage,
203
+ redirectUrl: formInput.redirectUrl,
204
+ status: formInput.status || "active",
205
+ createdBy: formInput.createdBy,
206
+ createdAt: /* @__PURE__ */ new Date(),
207
+ updatedAt: /* @__PURE__ */ new Date()
208
+ }
209
+ });
210
+ const serialized = serializeForm(form);
211
+ if (config.hooks?.onAfterFormCreated) {
212
+ await config.hooks.onAfterFormCreated(serialized, context);
213
+ }
214
+ return serialized;
215
+ }
216
+ );
217
+ const updateForm = api.createEndpoint(
218
+ "/forms/:id",
219
+ {
220
+ method: "PUT",
221
+ params: z.z.object({ id: z.z.string() }),
222
+ body: schemas.updateFormSchema
223
+ },
224
+ async (ctx) => {
225
+ const { id } = ctx.params;
226
+ const body = ctx.body;
227
+ const context = createContext(ctx.headers);
228
+ const existing = await adapter.findOne({
229
+ model: "form",
230
+ where: [{ field: "id", value: id, operator: "eq" }]
231
+ });
232
+ if (!existing) {
233
+ throw ctx.error(404, { message: "Form not found" });
234
+ }
235
+ let slug;
236
+ if (body.slug) {
237
+ slug = utils.slugify(body.slug);
238
+ if (!slug) {
239
+ throw ctx.error(400, {
240
+ message: "Invalid slug: must contain at least one alphanumeric character"
241
+ });
242
+ }
243
+ if (slug !== existing.slug) {
244
+ const duplicate = await adapter.findOne({
245
+ model: "form",
246
+ where: [
247
+ { field: "slug", value: slug, operator: "eq" }
248
+ ]
249
+ });
250
+ if (duplicate) {
251
+ throw ctx.error(409, {
252
+ message: "Form with this slug already exists"
253
+ });
254
+ }
255
+ }
256
+ }
257
+ if (body.schema) {
258
+ try {
259
+ JSON.parse(body.schema);
260
+ } catch {
261
+ throw ctx.error(400, { message: "Invalid JSON Schema" });
262
+ }
263
+ }
264
+ let updateInput = {
265
+ name: body.name,
266
+ slug,
267
+ description: body.description,
268
+ schema: body.schema,
269
+ successMessage: body.successMessage,
270
+ redirectUrl: body.redirectUrl,
271
+ status: body.status
272
+ };
273
+ if (config.hooks?.onBeforeFormUpdated) {
274
+ const result = await config.hooks.onBeforeFormUpdated(
275
+ id,
276
+ updateInput,
277
+ context
278
+ );
279
+ if (result === false) {
280
+ throw ctx.error(403, { message: "Update operation denied" });
281
+ }
282
+ if (result && typeof result === "object") {
283
+ updateInput = result;
284
+ }
285
+ }
286
+ const updateData = {
287
+ updatedAt: /* @__PURE__ */ new Date()
288
+ };
289
+ if (updateInput.name) updateData.name = updateInput.name;
290
+ if (updateInput.slug) updateData.slug = updateInput.slug;
291
+ if (updateInput.description !== void 0)
292
+ updateData.description = updateInput.description;
293
+ if (updateInput.schema) updateData.schema = updateInput.schema;
294
+ if (updateInput.successMessage !== void 0)
295
+ updateData.successMessage = updateInput.successMessage;
296
+ if (updateInput.redirectUrl !== void 0)
297
+ updateData.redirectUrl = updateInput.redirectUrl;
298
+ if (updateInput.status) updateData.status = updateInput.status;
299
+ await adapter.update({
300
+ model: "form",
301
+ where: [{ field: "id", value: id, operator: "eq" }],
302
+ update: updateData
303
+ });
304
+ const updated = await adapter.findOne({
305
+ model: "form",
306
+ where: [{ field: "id", value: id, operator: "eq" }]
307
+ });
308
+ if (!updated) {
309
+ throw ctx.error(500, { message: "Failed to fetch updated form" });
310
+ }
311
+ const serialized = serializeForm(updated);
312
+ if (config.hooks?.onAfterFormUpdated) {
313
+ await config.hooks.onAfterFormUpdated(serialized, context);
314
+ }
315
+ return serialized;
316
+ }
317
+ );
318
+ const deleteForm = api.createEndpoint(
319
+ "/forms/:id",
320
+ {
321
+ method: "DELETE",
322
+ params: z.z.object({ id: z.z.string() })
323
+ },
324
+ async (ctx) => {
325
+ const { id } = ctx.params;
326
+ const context = createContext(ctx.headers);
327
+ const existing = await adapter.findOne({
328
+ model: "form",
329
+ where: [{ field: "id", value: id, operator: "eq" }]
330
+ });
331
+ if (!existing) {
332
+ throw ctx.error(404, { message: "Form not found" });
333
+ }
334
+ if (config.hooks?.onBeforeFormDeleted) {
335
+ const canDelete = await config.hooks.onBeforeFormDeleted(
336
+ id,
337
+ context
338
+ );
339
+ if (!canDelete) {
340
+ throw ctx.error(403, { message: "Delete operation denied" });
341
+ }
342
+ }
343
+ await adapter.delete({
344
+ model: "formSubmission",
345
+ where: [{ field: "formId", value: id, operator: "eq" }]
346
+ });
347
+ await adapter.delete({
348
+ model: "form",
349
+ where: [{ field: "id", value: id, operator: "eq" }]
350
+ });
351
+ if (config.hooks?.onAfterFormDeleted) {
352
+ await config.hooks.onAfterFormDeleted(id, context);
353
+ }
354
+ return { success: true };
355
+ }
356
+ );
357
+ const submitForm = api.createEndpoint(
358
+ "/forms/:slug/submit",
359
+ {
360
+ method: "POST",
361
+ params: z.z.object({ slug: z.z.string() }),
362
+ body: z.z.object({
363
+ // Use passthrough object for dynamic form data
364
+ data: z.z.object({}).passthrough()
365
+ })
366
+ },
367
+ async (ctx) => {
368
+ const { slug } = ctx.params;
369
+ const { data } = ctx.body;
370
+ const baseContext = createContext(ctx.headers);
371
+ const form = await adapter.findOne({
372
+ model: "form",
373
+ where: [{ field: "slug", value: slug, operator: "eq" }]
374
+ });
375
+ if (!form) {
376
+ throw ctx.error(404, { message: "Form not found" });
377
+ }
378
+ if (form.status !== "active") {
379
+ throw ctx.error(400, {
380
+ message: "Form is not accepting submissions"
381
+ });
382
+ }
383
+ const submissionContext = createSubmissionContext(
384
+ slug,
385
+ form.id,
386
+ ctx.headers
387
+ );
388
+ try {
389
+ const jsonSchema = JSON.parse(form.schema);
390
+ const zodSchema = schemaConverter.formSchemaToZod(jsonSchema);
391
+ const validation = zodSchema.safeParse(data);
392
+ if (!validation.success) {
393
+ throw ctx.error(400, {
394
+ message: "Validation failed",
395
+ errors: validation.error.issues
396
+ });
397
+ }
398
+ } catch (error) {
399
+ if (error && typeof error === "object" && "code" in error) {
400
+ throw error;
401
+ }
402
+ throw ctx.error(400, { message: "Invalid form data" });
403
+ }
404
+ let finalData = data;
405
+ if (config.hooks?.onBeforeSubmission) {
406
+ try {
407
+ const result = await config.hooks.onBeforeSubmission(
408
+ slug,
409
+ data,
410
+ submissionContext
411
+ );
412
+ if (result === false) {
413
+ throw ctx.error(400, { message: "Submission rejected" });
414
+ }
415
+ if (result && typeof result === "object") {
416
+ finalData = result;
417
+ }
418
+ } catch (error) {
419
+ if (config.hooks?.onSubmissionError) {
420
+ await config.hooks.onSubmissionError(
421
+ error,
422
+ slug,
423
+ data,
424
+ submissionContext
425
+ );
426
+ }
427
+ throw error;
428
+ }
429
+ }
430
+ const submission = await adapter.create({
431
+ model: "formSubmission",
432
+ data: {
433
+ formId: form.id,
434
+ data: JSON.stringify(finalData),
435
+ submittedAt: /* @__PURE__ */ new Date(),
436
+ ipAddress: baseContext.ipAddress,
437
+ userAgent: baseContext.userAgent
438
+ }
439
+ });
440
+ const serialized = serializeFormSubmission(submission);
441
+ if (config.hooks?.onAfterSubmission) {
442
+ await config.hooks.onAfterSubmission(
443
+ serialized,
444
+ serializeForm(form),
445
+ submissionContext
446
+ );
447
+ }
448
+ return {
449
+ ...serialized,
450
+ form: {
451
+ successMessage: form.successMessage,
452
+ redirectUrl: form.redirectUrl
453
+ }
454
+ };
455
+ }
456
+ );
457
+ const listSubmissions = api.createEndpoint(
458
+ "/forms/:formId/submissions",
459
+ {
460
+ method: "GET",
461
+ params: z.z.object({ formId: z.z.string() }),
462
+ query: schemas.listSubmissionsQuerySchema
463
+ },
464
+ async (ctx) => {
465
+ const { formId } = ctx.params;
466
+ const { limit, offset } = ctx.query;
467
+ const context = createContext(ctx.headers);
468
+ const form = await adapter.findOne({
469
+ model: "form",
470
+ where: [{ field: "id", value: formId, operator: "eq" }]
471
+ });
472
+ if (!form) {
473
+ throw ctx.error(404, { message: "Form not found" });
474
+ }
475
+ if (config.hooks?.onBeforeListSubmissions) {
476
+ const canList = await config.hooks.onBeforeListSubmissions(
477
+ formId,
478
+ context
479
+ );
480
+ if (!canList) {
481
+ throw ctx.error(403, { message: "Access denied" });
482
+ }
483
+ }
484
+ const allSubmissions = await adapter.findMany({
485
+ model: "formSubmission",
486
+ where: [
487
+ { field: "formId", value: formId, operator: "eq" }
488
+ ]
489
+ });
490
+ const total = allSubmissions.length;
491
+ const submissions = await adapter.findMany({
492
+ model: "formSubmission",
493
+ where: [
494
+ { field: "formId", value: formId, operator: "eq" }
495
+ ],
496
+ limit,
497
+ offset,
498
+ sortBy: { field: "submittedAt", direction: "desc" },
499
+ join: { form: true }
500
+ });
501
+ return {
502
+ items: submissions.map(serializeFormSubmissionWithData),
503
+ total,
504
+ limit,
505
+ offset
506
+ };
507
+ }
508
+ );
509
+ const getSubmission = api.createEndpoint(
510
+ "/forms/:formId/submissions/:subId",
511
+ {
512
+ method: "GET",
513
+ params: z.z.object({ formId: z.z.string(), subId: z.z.string() })
514
+ },
515
+ async (ctx) => {
516
+ const { formId, subId } = ctx.params;
517
+ const context = createContext(ctx.headers);
518
+ if (config.hooks?.onBeforeGetSubmission) {
519
+ const canGet = await config.hooks.onBeforeGetSubmission(
520
+ subId,
521
+ context
522
+ );
523
+ if (!canGet) {
524
+ throw ctx.error(403, { message: "Access denied" });
525
+ }
526
+ }
527
+ const submission = await adapter.findOne({
528
+ model: "formSubmission",
529
+ where: [{ field: "id", value: subId, operator: "eq" }],
530
+ join: { form: true }
531
+ });
532
+ if (!submission || submission.formId !== formId) {
533
+ throw ctx.error(404, { message: "Submission not found" });
534
+ }
535
+ return serializeFormSubmissionWithData(submission);
536
+ }
537
+ );
538
+ const deleteSubmission = api.createEndpoint(
539
+ "/forms/:formId/submissions/:subId",
540
+ {
541
+ method: "DELETE",
542
+ params: z.z.object({ formId: z.z.string(), subId: z.z.string() })
543
+ },
544
+ async (ctx) => {
545
+ const { formId, subId } = ctx.params;
546
+ const context = createContext(ctx.headers);
547
+ const existing = await adapter.findOne({
548
+ model: "formSubmission",
549
+ where: [{ field: "id", value: subId, operator: "eq" }]
550
+ });
551
+ if (!existing || existing.formId !== formId) {
552
+ throw ctx.error(404, { message: "Submission not found" });
553
+ }
554
+ if (config.hooks?.onBeforeSubmissionDeleted) {
555
+ const canDelete = await config.hooks.onBeforeSubmissionDeleted(
556
+ subId,
557
+ context
558
+ );
559
+ if (!canDelete) {
560
+ throw ctx.error(403, { message: "Delete operation denied" });
561
+ }
562
+ }
563
+ await adapter.delete({
564
+ model: "formSubmission",
565
+ where: [{ field: "id", value: subId, operator: "eq" }]
566
+ });
567
+ if (config.hooks?.onAfterSubmissionDeleted) {
568
+ await config.hooks.onAfterSubmissionDeleted(subId, context);
569
+ }
570
+ return { success: true };
571
+ }
572
+ );
573
+ return {
574
+ listForms,
575
+ getFormBySlug,
576
+ getFormById,
577
+ createForm,
578
+ updateForm,
579
+ deleteForm,
580
+ submitForm,
581
+ listSubmissions,
582
+ getSubmission,
583
+ deleteSubmission
584
+ };
585
+ }
586
+ });
587
+
588
+ exports.formBuilderBackendPlugin = formBuilderBackendPlugin;