@sonicjs-cms/core 2.18.1 → 3.0.0-beta.2

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 (225) hide show
  1. package/README.md +4 -3
  2. package/dist/admin-documents-form.template-KN7JF66Q.cjs +19 -0
  3. package/dist/{admin-layout-catalyst.template-UMTIN66R.js.map → admin-documents-form.template-KN7JF66Q.cjs.map} +1 -1
  4. package/dist/admin-documents-form.template-NLSI6Z42.js +6 -0
  5. package/dist/{admin-layout-catalyst.template-HFD37TY5.cjs.map → admin-documents-form.template-NLSI6Z42.js.map} +1 -1
  6. package/dist/admin-layout-catalyst.template-WHJGSWWD.js +7 -0
  7. package/dist/admin-layout-catalyst.template-WHJGSWWD.js.map +1 -0
  8. package/dist/admin-layout-catalyst.template-ZK5HD545.cjs +17 -0
  9. package/dist/admin-layout-catalyst.template-ZK5HD545.cjs.map +1 -0
  10. package/dist/app-Bo0X1OWX.d.ts +1268 -0
  11. package/dist/app-Do66yCcV.d.cts +1268 -0
  12. package/dist/cache-DDARE4QE.js +4 -0
  13. package/dist/cache-DDARE4QE.js.map +1 -0
  14. package/dist/cache-LVYS4BPL.cjs +33 -0
  15. package/dist/cache-LVYS4BPL.cjs.map +1 -0
  16. package/dist/chunk-2CB4KY7I.cjs +771 -0
  17. package/dist/chunk-2CB4KY7I.cjs.map +1 -0
  18. package/dist/{chunk-55RDMDOP.js → chunk-3TB6AT6X.js} +148 -55
  19. package/dist/chunk-3TB6AT6X.js.map +1 -0
  20. package/dist/{chunk-ON5ZMSU4.js → chunk-6JQOUUOB.js} +3 -3
  21. package/dist/chunk-6JQOUUOB.js.map +1 -0
  22. package/dist/chunk-6OUHGKFD.js +387 -0
  23. package/dist/chunk-6OUHGKFD.js.map +1 -0
  24. package/dist/{chunk-DSUJ5YQH.cjs → chunk-AAWNRBRB.cjs} +537 -92
  25. package/dist/chunk-AAWNRBRB.cjs.map +1 -0
  26. package/dist/chunk-AI663NBO.js +821 -0
  27. package/dist/chunk-AI663NBO.js.map +1 -0
  28. package/dist/chunk-BDDABDAB.cjs +1149 -0
  29. package/dist/chunk-BDDABDAB.cjs.map +1 -0
  30. package/dist/chunk-BLMTL57B.js +767 -0
  31. package/dist/chunk-BLMTL57B.js.map +1 -0
  32. package/dist/chunk-DNQCEKUK.cjs +327 -0
  33. package/dist/chunk-DNQCEKUK.cjs.map +1 -0
  34. package/dist/chunk-DSA4UX5B.cjs +276 -0
  35. package/dist/chunk-DSA4UX5B.cjs.map +1 -0
  36. package/dist/chunk-EF2NQUIQ.js +323 -0
  37. package/dist/chunk-EF2NQUIQ.js.map +1 -0
  38. package/dist/chunk-GCDZZNIN.js +192 -0
  39. package/dist/chunk-GCDZZNIN.js.map +1 -0
  40. package/dist/{chunk-ABB34XUS.cjs → chunk-H2AXVCLS.cjs} +667 -19
  41. package/dist/chunk-H2AXVCLS.cjs.map +1 -0
  42. package/dist/{chunk-XWIA3HVX.js → chunk-HDWE5FRJ.js} +6 -1249
  43. package/dist/chunk-HDWE5FRJ.js.map +1 -0
  44. package/dist/chunk-HIKBY7MS.cjs +70 -0
  45. package/dist/chunk-HIKBY7MS.cjs.map +1 -0
  46. package/dist/chunk-IESEVHXL.js +66 -0
  47. package/dist/chunk-IESEVHXL.js.map +1 -0
  48. package/dist/chunk-IVPRUGTY.js +242 -0
  49. package/dist/chunk-IVPRUGTY.js.map +1 -0
  50. package/dist/{chunk-SQ6FNXU2.cjs → chunk-IXUHXTHW.cjs} +2 -151
  51. package/dist/chunk-IXUHXTHW.cjs.map +1 -0
  52. package/dist/chunk-J6JTWD2A.cjs +100 -0
  53. package/dist/chunk-J6JTWD2A.cjs.map +1 -0
  54. package/dist/chunk-JEQ7FLOD.cjs +199 -0
  55. package/dist/chunk-JEQ7FLOD.cjs.map +1 -0
  56. package/dist/chunk-K25XHMM3.js +566 -0
  57. package/dist/chunk-K25XHMM3.js.map +1 -0
  58. package/dist/chunk-LRZIAW7U.cjs +158 -0
  59. package/dist/chunk-LRZIAW7U.cjs.map +1 -0
  60. package/dist/{chunk-OHYBNCVL.cjs → chunk-MVIZJOO5.cjs} +10 -1256
  61. package/dist/chunk-MVIZJOO5.cjs.map +1 -0
  62. package/dist/{chunk-UYJ6TJHX.cjs → chunk-NAVPFIG5.cjs} +148 -55
  63. package/dist/chunk-NAVPFIG5.cjs.map +1 -0
  64. package/dist/chunk-NLJVSER2.js +273 -0
  65. package/dist/chunk-NLJVSER2.js.map +1 -0
  66. package/dist/chunk-NMPEMSU4.js +154 -0
  67. package/dist/chunk-NMPEMSU4.js.map +1 -0
  68. package/dist/chunk-NUKJ54GA.cjs +245 -0
  69. package/dist/chunk-NUKJ54GA.cjs.map +1 -0
  70. package/dist/{chunk-T3Q5V33G.cjs → chunk-QAYFOER6.cjs} +621 -829
  71. package/dist/chunk-QAYFOER6.cjs.map +1 -0
  72. package/dist/{chunk-MGFRZO24.js → chunk-QZGABF2M.js} +3 -149
  73. package/dist/chunk-QZGABF2M.js.map +1 -0
  74. package/dist/chunk-RNZFGN4R.js +88 -0
  75. package/dist/chunk-RNZFGN4R.js.map +1 -0
  76. package/dist/chunk-RZ6H7OZK.js +1134 -0
  77. package/dist/chunk-RZ6H7OZK.js.map +1 -0
  78. package/dist/{chunk-XXDFQERJ.js → chunk-VD2EA3WT.js} +7192 -9806
  79. package/dist/chunk-VD2EA3WT.js.map +1 -0
  80. package/dist/{chunk-SXXTQETM.cjs → chunk-VXE42MYF.cjs} +8722 -11323
  81. package/dist/chunk-VXE42MYF.cjs.map +1 -0
  82. package/dist/{chunk-4ZSNJDLS.cjs → chunk-WULONYGB.cjs} +9 -9
  83. package/dist/chunk-WULONYGB.cjs.map +1 -0
  84. package/dist/chunk-XW56B23A.cjs +408 -0
  85. package/dist/chunk-XW56B23A.cjs.map +1 -0
  86. package/dist/chunk-YA3TJ65D.cjs +575 -0
  87. package/dist/chunk-YA3TJ65D.cjs.map +1 -0
  88. package/dist/{chunk-TFNTM3OA.js → chunk-YHSQVQXX.js} +645 -15
  89. package/dist/chunk-YHSQVQXX.js.map +1 -0
  90. package/dist/chunk-YP7GW2G5.cjs +866 -0
  91. package/dist/chunk-YP7GW2G5.cjs.map +1 -0
  92. package/dist/{chunk-QFWHAFEO.js → chunk-ZEZ245PW.js} +148 -858
  93. package/dist/chunk-ZEZ245PW.js.map +1 -0
  94. package/dist/{chunk-EW5NOBVU.js → chunk-ZGGXCFR6.js} +611 -817
  95. package/dist/chunk-ZGGXCFR6.js.map +1 -0
  96. package/dist/{collection-config-B4PG-AaF.d.cts → collection-config-JgHOpFCG.d.cts} +30 -2
  97. package/dist/{collection-config-B4PG-AaF.d.ts → collection-config-JgHOpFCG.d.ts} +30 -2
  98. package/dist/config-HFXANXCC.js +6 -0
  99. package/dist/config-HFXANXCC.js.map +1 -0
  100. package/dist/config-ON6FNMYX.cjs +19 -0
  101. package/dist/config-ON6FNMYX.cjs.map +1 -0
  102. package/dist/define-plugin-BzNHc1ZI.d.ts +1321 -0
  103. package/dist/define-plugin-IWDKYaVm.d.cts +1321 -0
  104. package/dist/document-projection-TDWRJX3Z.cjs +13 -0
  105. package/dist/document-projection-TDWRJX3Z.cjs.map +1 -0
  106. package/dist/document-projection-YYMC6I4U.js +4 -0
  107. package/dist/document-projection-YYMC6I4U.js.map +1 -0
  108. package/dist/index.cjs +13735 -4329
  109. package/dist/index.cjs.map +1 -1
  110. package/dist/index.d.cts +329 -492
  111. package/dist/index.d.ts +329 -492
  112. package/dist/index.js +13386 -3999
  113. package/dist/index.js.map +1 -1
  114. package/dist/middleware.cjs +36 -32
  115. package/dist/middleware.d.cts +69 -7
  116. package/dist/middleware.d.ts +69 -7
  117. package/dist/middleware.js +7 -3
  118. package/dist/migrations-NJJWQUKK.cjs +13 -0
  119. package/dist/{migrations-IYNTWDC6.cjs.map → migrations-NJJWQUKK.cjs.map} +1 -1
  120. package/dist/migrations-WCAVBD7C.js +4 -0
  121. package/dist/{migrations-R337UD46.js.map → migrations-WCAVBD7C.js.map} +1 -1
  122. package/dist/{plugin-bootstrap-DfVerYV4.d.cts → plugin-bootstrap-B8ThJU21.d.cts} +4315 -1661
  123. package/dist/{plugin-bootstrap-P_ciLp_C.d.ts → plugin-bootstrap-qu8hJgUt.d.ts} +4315 -1661
  124. package/dist/plugins.cjs +171 -12
  125. package/dist/plugins.d.cts +36 -2
  126. package/dist/plugins.d.ts +36 -2
  127. package/dist/plugins.js +5 -2
  128. package/dist/rbac-O73MFKDA.js +5 -0
  129. package/dist/rbac-O73MFKDA.js.map +1 -0
  130. package/dist/rbac-VONLJJKB.cjs +14 -0
  131. package/dist/rbac-VONLJJKB.cjs.map +1 -0
  132. package/dist/routes.cjs +41 -45
  133. package/dist/routes.d.cts +56 -146
  134. package/dist/routes.d.ts +56 -146
  135. package/dist/routes.js +17 -9
  136. package/dist/services.cjs +39 -72
  137. package/dist/services.d.cts +79 -54
  138. package/dist/services.d.ts +79 -54
  139. package/dist/services.js +6 -3
  140. package/dist/templates.cjs +17 -29
  141. package/dist/templates.d.cts +1 -66
  142. package/dist/templates.d.ts +1 -66
  143. package/dist/templates.js +3 -3
  144. package/dist/types-Dea1eNxU.d.cts +286 -0
  145. package/dist/types-Dea1eNxU.d.ts +286 -0
  146. package/dist/types.d.cts +1 -1
  147. package/dist/types.d.ts +1 -1
  148. package/dist/utils.cjs +18 -17
  149. package/dist/utils.d.cts +1 -1
  150. package/dist/utils.d.ts +1 -1
  151. package/dist/utils.js +2 -1
  152. package/migrations/0001_core.sql +184 -0
  153. package/migrations/0002_documents.sql +163 -0
  154. package/package.json +12 -7
  155. package/dist/admin-layout-catalyst.template-HFD37TY5.cjs +0 -17
  156. package/dist/admin-layout-catalyst.template-UMTIN66R.js +0 -7
  157. package/dist/app-C9esKLmh.d.cts +0 -112
  158. package/dist/app-C9esKLmh.d.ts +0 -112
  159. package/dist/chunk-4R3NOOL3.js +0 -2217
  160. package/dist/chunk-4R3NOOL3.js.map +0 -1
  161. package/dist/chunk-4ZSNJDLS.cjs.map +0 -1
  162. package/dist/chunk-55RDMDOP.js.map +0 -1
  163. package/dist/chunk-635JAMSE.cjs +0 -653
  164. package/dist/chunk-635JAMSE.cjs.map +0 -1
  165. package/dist/chunk-ABB34XUS.cjs.map +0 -1
  166. package/dist/chunk-C54YUA23.cjs +0 -2219
  167. package/dist/chunk-C54YUA23.cjs.map +0 -1
  168. package/dist/chunk-DSUJ5YQH.cjs.map +0 -1
  169. package/dist/chunk-EW5NOBVU.js.map +0 -1
  170. package/dist/chunk-EXNEW5US.js +0 -648
  171. package/dist/chunk-EXNEW5US.js.map +0 -1
  172. package/dist/chunk-I2H5NGJQ.js +0 -692
  173. package/dist/chunk-I2H5NGJQ.js.map +0 -1
  174. package/dist/chunk-MGFRZO24.js.map +0 -1
  175. package/dist/chunk-OHYBNCVL.cjs.map +0 -1
  176. package/dist/chunk-ON5ZMSU4.js.map +0 -1
  177. package/dist/chunk-QFWHAFEO.js.map +0 -1
  178. package/dist/chunk-SQ6FNXU2.cjs.map +0 -1
  179. package/dist/chunk-SXXTQETM.cjs.map +0 -1
  180. package/dist/chunk-T3Q5V33G.cjs.map +0 -1
  181. package/dist/chunk-TFNTM3OA.js.map +0 -1
  182. package/dist/chunk-UYJ6TJHX.cjs.map +0 -1
  183. package/dist/chunk-WAEQXGCX.cjs +0 -1898
  184. package/dist/chunk-WAEQXGCX.cjs.map +0 -1
  185. package/dist/chunk-XWIA3HVX.js.map +0 -1
  186. package/dist/chunk-XXDFQERJ.js.map +0 -1
  187. package/dist/migrations-IYNTWDC6.cjs +0 -13
  188. package/dist/migrations-R337UD46.js +0 -4
  189. package/dist/plugin-manager-BoM3Q7o7.d.cts +0 -328
  190. package/dist/plugin-manager-Efx9RyDX.d.ts +0 -328
  191. package/migrations/001_initial_schema.sql +0 -170
  192. package/migrations/002_faq_plugin.sql +0 -86
  193. package/migrations/003_stage5_enhancements.sql +0 -121
  194. package/migrations/004_stage6_user_management.sql +0 -183
  195. package/migrations/005_stage7_workflow_automation.sql +0 -294
  196. package/migrations/006_plugin_system.sql +0 -155
  197. package/migrations/007_demo_login_plugin.sql +0 -23
  198. package/migrations/008_fix_slug_validation.sql +0 -22
  199. package/migrations/009_system_logging.sql +0 -57
  200. package/migrations/011_config_managed_collections.sql +0 -15
  201. package/migrations/012_testimonials_plugin.sql +0 -80
  202. package/migrations/013_code_examples_plugin.sql +0 -177
  203. package/migrations/014_fix_plugin_registry.sql +0 -88
  204. package/migrations/015_add_remaining_plugins.sql +0 -89
  205. package/migrations/016_remove_duplicate_cache_plugin.sql +0 -17
  206. package/migrations/017_auth_configurable_fields.sql +0 -49
  207. package/migrations/018_settings_table.sql +0 -23
  208. package/migrations/019_remove_blog_posts_collection.sql +0 -15
  209. package/migrations/020_add_email_plugin.sql +0 -22
  210. package/migrations/021_add_magic_link_auth_plugin.sql +0 -42
  211. package/migrations/022_add_tinymce_plugin.sql +0 -25
  212. package/migrations/023_add_easy_mdx_plugin.sql +0 -25
  213. package/migrations/024_add_quill_editor_plugin.sql +0 -25
  214. package/migrations/025_add_easymde_plugin.sql +0 -25
  215. package/migrations/026_add_otp_login.sql +0 -42
  216. package/migrations/027_fix_slug_field_type.sql +0 -18
  217. package/migrations/028_fix_slug_field_type_in_schemas.sql +0 -30
  218. package/migrations/029_add_forms_system.sql +0 -184
  219. package/migrations/030_add_turnstile_to_forms.sql +0 -14
  220. package/migrations/031_ai_search_plugin.sql +0 -45
  221. package/migrations/032_user_profiles.sql +0 -37
  222. package/migrations/033_form_content_integration.sql +0 -19
  223. package/migrations/034_security_audit_plugin.sql +0 -27
  224. package/migrations/035_user_profiles_data_column.sql +0 -16
  225. package/migrations/036_analytics_events.sql +0 -22
@@ -0,0 +1,323 @@
1
+ import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-3TB6AT6X.js';
2
+ import { escapeHtml } from './chunk-TQABQWOP.js';
3
+
4
+ // src/templates/pages/admin-documents-form.template.ts
5
+ init_admin_layout_catalyst_template();
6
+
7
+ // src/templates/components/alert.template.ts
8
+ function renderAlert(data) {
9
+ const typeClasses = {
10
+ success: "bg-green-50 dark:bg-green-500/10 border border-green-600/20 dark:border-green-500/20",
11
+ error: "bg-error/10 border border-red-600/20 dark:border-red-500/20",
12
+ warning: "bg-amber-50 dark:bg-amber-500/10 border border-amber-600/20 dark:border-amber-500/20",
13
+ info: "bg-blue-50 dark:bg-blue-500/10 border border-blue-600/20 dark:border-blue-500/20"
14
+ };
15
+ const iconClasses = {
16
+ success: "text-green-600 dark:text-green-400",
17
+ error: "text-red-600 dark:text-red-400",
18
+ warning: "text-amber-600 dark:text-amber-400",
19
+ info: "text-blue-600 dark:text-blue-400"
20
+ };
21
+ const textClasses = {
22
+ success: "text-green-900 dark:text-green-300",
23
+ error: "text-red-900 dark:text-red-300",
24
+ warning: "text-amber-900 dark:text-amber-300",
25
+ info: "text-blue-900 dark:text-blue-300"
26
+ };
27
+ const messageTextClasses = {
28
+ success: "text-green-700 dark:text-green-400",
29
+ error: "text-red-700 dark:text-red-400",
30
+ warning: "text-amber-700 dark:text-amber-400",
31
+ info: "text-blue-700 dark:text-blue-400"
32
+ };
33
+ const icons = {
34
+ success: `<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />`,
35
+ error: `<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />`,
36
+ warning: `<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />`,
37
+ info: `<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />`
38
+ };
39
+ return `
40
+ <div class="rounded-lg p-4 ${typeClasses[data.type]} ${data.className || ""}" ${data.dismissible ? 'id="dismissible-alert"' : ""}>
41
+ <div class="flex">
42
+ ${data.icon !== false ? `
43
+ <div class="flex-shrink-0">
44
+ <svg class="h-5 w-5 ${iconClasses[data.type]}" viewBox="0 0 20 20" fill="currentColor">
45
+ ${icons[data.type]}
46
+ </svg>
47
+ </div>
48
+ ` : ""}
49
+ <div class="${data.icon !== false ? "ml-3" : ""}">
50
+ ${data.title ? `
51
+ <h3 class="text-sm font-semibold ${textClasses[data.type]}">
52
+ ${escapeHtml(data.title)}
53
+ </h3>
54
+ ` : ""}
55
+ <div class="${data.title ? "mt-1 text-sm" : "text-sm"} ${messageTextClasses[data.type]}">
56
+ <p>${escapeHtml(data.message)}</p>
57
+ </div>
58
+ </div>
59
+ ${data.dismissible ? `
60
+ <div class="ml-auto pl-3">
61
+ <div class="-mx-1.5 -my-1.5">
62
+ <button
63
+ type="button"
64
+ class="inline-flex rounded-md p-1.5 ${iconClasses[data.type]} hover:bg-opacity-20 focus:outline-none focus:ring-2 focus:ring-offset-2"
65
+ onclick="document.getElementById('dismissible-alert').remove()"
66
+ >
67
+ <span class="sr-only">Dismiss</span>
68
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
69
+ <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
70
+ </svg>
71
+ </button>
72
+ </div>
73
+ </div>
74
+ ` : ""}
75
+ </div>
76
+ </div>
77
+ `;
78
+ }
79
+
80
+ // src/templates/pages/admin-documents-form.template.ts
81
+ function inputClass(error) {
82
+ const base = "block w-full rounded-lg border bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors";
83
+ return error ? `${base} border-red-400 dark:border-red-500` : `${base} border-zinc-300 dark:border-zinc-700`;
84
+ }
85
+ function renderFieldInput(field, value, error) {
86
+ const id = `data_${field.name}`;
87
+ const name = `data[${field.name}]`;
88
+ const label = field.name.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase());
89
+ const strVal = value != null ? String(value) : "";
90
+ let input;
91
+ if (field.type === "integer" || field.type === "number") {
92
+ input = `<input type="number" id="${id}" name="${name}" value="${escapeHtml(strVal)}"
93
+ step="${field.type === "integer" ? "1" : "any"}"
94
+ class="${inputClass(error)}">`;
95
+ } else if (field.type === "boolean") {
96
+ input = `<div class="flex items-center gap-2">
97
+ <input type="hidden" name="${name}" value="false">
98
+ <input type="checkbox" id="${id}" name="${name}" value="true" ${strVal === "true" ? "checked" : ""}
99
+ class="h-4 w-4 rounded border-zinc-300 dark:border-zinc-600 text-blue-600 focus:ring-blue-500">
100
+ <span class="text-sm text-zinc-700 dark:text-zinc-300">${label}</span>
101
+ </div>`;
102
+ return `
103
+ <div>
104
+ ${input}
105
+ ${error ? `<p class="mt-1 text-xs text-red-500">${escapeHtml(error)}</p>` : ""}
106
+ </div>`;
107
+ } else if (field.kind === "facet") {
108
+ const arrVal = Array.isArray(value) ? value.join(", ") : strVal;
109
+ input = `<input type="text" id="${id}" name="${name}" value="${escapeHtml(arrVal)}"
110
+ placeholder="Comma-separated values"
111
+ class="${inputClass(error)}">`;
112
+ } else {
113
+ input = `<input type="text" id="${id}" name="${name}" value="${escapeHtml(strVal)}"
114
+ class="${inputClass(error)}">`;
115
+ }
116
+ return `
117
+ <div>
118
+ <label for="${id}" class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
119
+ ${label}
120
+ </label>
121
+ ${input}
122
+ ${error ? `<p class="mt-1 text-xs text-red-500">${escapeHtml(error)}</p>` : ""}
123
+ </div>`;
124
+ }
125
+ function renderRemainingFields(allData, queryableFields, errors) {
126
+ const knownNames = new Set(queryableFields.map((f) => f.name));
127
+ const remainingKeys = Object.keys(allData).filter((k) => !knownNames.has(k));
128
+ if (remainingKeys.length === 0) return "";
129
+ const inputs = remainingKeys.map((key) => {
130
+ const val = allData[key];
131
+ const id = `data_${key}`;
132
+ const name = `data[${key}]`;
133
+ const label = key.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase());
134
+ const strVal = typeof val === "object" ? JSON.stringify(val, null, 2) : String(val ?? "");
135
+ const isMultiline = strVal.includes("\n") || strVal.length > 100;
136
+ return `
137
+ <div>
138
+ <label for="${id}" class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">${escapeHtml(label)}</label>
139
+ ${isMultiline ? `<textarea id="${id}" name="${name}" rows="4" class="${inputClass(errors[key])}">${escapeHtml(strVal)}</textarea>` : `<input type="text" id="${id}" name="${name}" value="${escapeHtml(strVal)}" class="${inputClass(errors[key])}">`}
140
+ ${errors[key] ? `<p class="mt-1 text-xs text-red-500">${escapeHtml(errors[key])}</p>` : ""}
141
+ </div>`;
142
+ }).join("");
143
+ return `
144
+ <div class="border-t border-zinc-200 dark:border-zinc-700 pt-6">
145
+ <h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400 mb-4">Additional Fields</h3>
146
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">${inputs}</div>
147
+ </div>`;
148
+ }
149
+ function renderDocumentFormPage(data) {
150
+ const { docType, doc, publishedDoc, isEdit, errors = {} } = data;
151
+ const queryableFields = docType.queryableFields ?? [];
152
+ const docData = doc?.data ?? {};
153
+ const isAdmin = data.user?.role === "admin";
154
+ const isEditor = isAdmin || data.user?.role === "editor";
155
+ const hasNewerDraft = isEdit && doc && !doc.isPublished && publishedDoc;
156
+ const isPublishedAndDraft = isEdit && doc?.isPublished && doc?.isCurrentDraft;
157
+ const formAction = isEdit ? `/admin/content/documents/${escapeHtml(docType.id)}/${escapeHtml(doc.rootId)}` : `/admin/content/documents/${escapeHtml(docType.id)}/new`;
158
+ const publishBannerHtml = (() => {
159
+ if (!isEdit || !doc) return "";
160
+ if (isPublishedAndDraft) {
161
+ return renderAlert({
162
+ type: "success",
163
+ message: 'This document is live. Saving creates a new draft. Use "Publish" to push changes live.'
164
+ });
165
+ }
166
+ if (hasNewerDraft) {
167
+ return renderAlert({
168
+ type: "info",
169
+ message: `A published version (v${publishedDoc.versionNumber}) is still live. This is an unpublished draft (v${doc.versionNumber}).`
170
+ });
171
+ }
172
+ return "";
173
+ })();
174
+ const queryableInputs = queryableFields.filter((f) => f.kind !== "reference").map((f) => renderFieldInput(f, docData[f.name], errors[`data.${f.name}`])).join("");
175
+ const remainingHtml = renderRemainingFields(docData, queryableFields, errors);
176
+ const content = `
177
+ <div class="w-full px-4 sm:px-6 lg:px-8 py-6 space-y-6">
178
+ <!-- Header -->
179
+ <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
180
+ <div>
181
+ <div class="flex items-center gap-2 text-sm text-zinc-500 dark:text-zinc-400 mb-1">
182
+ <a href="/admin/content" class="hover:text-zinc-950 dark:hover:text-white">Content</a>
183
+ <svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/></svg>
184
+ <a href="/admin/content?model=doc:${escapeHtml(docType.id)}" class="hover:text-zinc-950 dark:hover:text-white">${escapeHtml(docType.displayName)}</a>
185
+ <svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/></svg>
186
+ <span class="text-zinc-950 dark:text-white font-medium">${isEdit ? "Edit" : "New"}</span>
187
+ </div>
188
+ <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">
189
+ ${isEdit ? `Edit ${escapeHtml(docType.displayName)}` : `New ${escapeHtml(docType.displayName)}`}
190
+ </h1>
191
+ ${isEdit && doc ? `<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">v${doc.versionNumber} \xB7 root: <code class="font-mono">${escapeHtml(doc.rootId)}</code></p>` : ""}
192
+ </div>
193
+
194
+ <!-- Publish controls (edit mode, versioning types only) -->
195
+ ${isEdit && doc && isEditor && data.versioningEnabled ? `
196
+ <div class="mt-4 sm:mt-0 flex gap-2">
197
+ ${!doc.isPublished ? `
198
+ <form method="POST" action="/admin/content/documents/${escapeHtml(docType.id)}/${escapeHtml(doc.id)}/publish">
199
+ <button type="submit"
200
+ class="inline-flex items-center rounded-lg bg-green-600 px-3.5 py-2.5 text-sm font-semibold text-white hover:bg-green-500 transition-colors shadow-sm">
201
+ Publish
202
+ </button>
203
+ </form>` : `
204
+ <form method="POST" action="/admin/content/documents/${escapeHtml(docType.id)}/${escapeHtml(doc.id)}/unpublish">
205
+ <button type="submit"
206
+ class="inline-flex items-center rounded-lg bg-amber-500 px-3.5 py-2.5 text-sm font-semibold text-white hover:bg-amber-400 transition-colors shadow-sm">
207
+ Unpublish
208
+ </button>
209
+ </form>`}
210
+ </div>` : ""}
211
+ </div>
212
+
213
+ ${publishBannerHtml}
214
+ ${data.message ? renderAlert({ type: data.messageType ?? "info", message: data.message, dismissible: true }) : ""}
215
+
216
+ <!-- Form -->
217
+ <form method="POST" action="${formAction}">
218
+ ${isEdit ? `<input type="hidden" name="_method" value="PUT">` : ""}
219
+
220
+ <div class="relative rounded-xl">
221
+ <div class="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-purple-500/5 dark:from-blue-400/10 dark:to-purple-400/10 rounded-xl"></div>
222
+ <div class="relative bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 rounded-xl p-6 space-y-6">
223
+
224
+ <!-- Standard fields -->
225
+ <div>
226
+ <h3 class="text-sm font-semibold text-zinc-700 dark:text-zinc-200 mb-4">Document</h3>
227
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
228
+ <div>
229
+ <label for="title" class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">Title</label>
230
+ <input type="text" id="title" name="title" value="${escapeHtml(doc?.title ?? "")}"
231
+ class="${inputClass(errors.title)}">
232
+ ${errors.title ? `<p class="mt-1 text-xs text-red-500">${escapeHtml(errors.title)}</p>` : ""}
233
+ </div>
234
+ <div>
235
+ <label for="slug" class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">Slug</label>
236
+ <input type="text" id="slug" name="slug" value="${escapeHtml(doc?.slug ?? "")}"
237
+ placeholder="auto-generated-if-empty"
238
+ class="${inputClass(errors.slug)}">
239
+ ${errors.slug ? `<p class="mt-1 text-xs text-red-500">${escapeHtml(errors.slug)}</p>` : ""}
240
+ </div>
241
+ </div>
242
+ </div>
243
+
244
+ <!-- Queryable data fields -->
245
+ ${queryableFields.length > 0 ? `
246
+ <div class="border-t border-zinc-200 dark:border-zinc-700 pt-6">
247
+ <h3 class="text-sm font-semibold text-zinc-700 dark:text-zinc-200 mb-4">Content</h3>
248
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
249
+ ${queryableInputs}
250
+ </div>
251
+ </div>` : ""}
252
+
253
+ <!-- Remaining data fields not in queryable fields -->
254
+ ${remainingHtml}
255
+
256
+ <!-- Actions -->
257
+ <div class="border-t border-zinc-200 dark:border-zinc-700 pt-6 flex items-center justify-between">
258
+ <a href="/admin/content?model=doc:${escapeHtml(docType.id)}"
259
+ class="inline-flex items-center rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-700 dark:text-zinc-200 ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors">
260
+ Cancel
261
+ </a>
262
+ <div class="flex gap-3">
263
+ <button type="submit"
264
+ class="inline-flex items-center rounded-lg bg-zinc-950 dark:bg-white px-3.5 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors shadow-sm">
265
+ ${isEdit ? data.versioningEnabled ? "Save Draft" : "Update" : "Create"}
266
+ </button>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </div>
271
+ </form>
272
+
273
+ <!-- Version history (edit mode, versioning opt-in only) -->
274
+ ${isEdit && doc && data.versioningEnabled ? `
275
+ <details class="group">
276
+ <summary class="cursor-pointer text-sm text-zinc-500 dark:text-zinc-400 hover:text-zinc-950 dark:hover:text-white flex items-center gap-2 py-2">
277
+ <svg class="h-4 w-4 transition-transform group-open:rotate-90" viewBox="0 0 20 20" fill="currentColor">
278
+ <path fill-rule="evenodd" d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
279
+ </svg>
280
+ Version history
281
+ </summary>
282
+ <div class="mt-3 rounded-xl bg-white dark:bg-zinc-900 ring-1 ring-zinc-950/5 dark:ring-white/10 overflow-hidden">
283
+ <div id="version-history-placeholder" class="px-6 py-4 text-sm text-zinc-500 dark:text-zinc-400"
284
+ hx-get="/admin/versioning/${escapeHtml(doc.rootId)}"
285
+ hx-trigger="revealed"
286
+ hx-swap="outerHTML">
287
+ Loading version history\u2026
288
+ </div>
289
+ </div>
290
+ </details>` : ""}
291
+ </div>
292
+ `;
293
+ return renderAdminLayoutCatalyst({
294
+ title: `${isEdit ? "Edit" : "New"} ${docType.displayName} \u2014 Documents`,
295
+ currentPath: "/admin/content",
296
+ user: data.user,
297
+ version: data.version,
298
+ content
299
+ });
300
+ }
301
+ function renderVersionHistoryFragment(data) {
302
+ if (data.versions.length === 0) {
303
+ return `<div class="px-6 py-4 text-sm text-zinc-500 dark:text-zinc-400">No versions found.</div>`;
304
+ }
305
+ const rows = data.versions.map((v) => `
306
+ <div class="flex items-center justify-between px-6 py-3 border-b border-zinc-100 dark:border-zinc-800 last:border-0">
307
+ <div class="flex items-center gap-3">
308
+ <span class="text-sm font-medium text-zinc-950 dark:text-white">v${v.versionNumber}</span>
309
+ ${v.isPublished ? `<span class="inline-flex items-center rounded-md bg-green-50 dark:bg-green-500/10 px-1.5 py-0.5 text-xs font-medium text-green-700 dark:text-green-400">live</span>` : ""}
310
+ ${v.isCurrentDraft ? `<span class="inline-flex items-center rounded-md bg-blue-50 dark:bg-blue-500/10 px-1.5 py-0.5 text-xs font-medium text-blue-700 dark:text-blue-400">draft</span>` : ""}
311
+ </div>
312
+ <div class="flex items-center gap-4 text-xs text-zinc-500 dark:text-zinc-400">
313
+ <span>${v.createdBy ?? "\u2014"}</span>
314
+ <span>${new Date(v.updatedAt * 1e3).toLocaleString("en-US", { dateStyle: "short", timeStyle: "short" })}</span>
315
+ </div>
316
+ </div>
317
+ `).join("");
318
+ return `<div>${rows}</div>`;
319
+ }
320
+
321
+ export { renderAlert, renderDocumentFormPage, renderVersionHistoryFragment };
322
+ //# sourceMappingURL=chunk-EF2NQUIQ.js.map
323
+ //# sourceMappingURL=chunk-EF2NQUIQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/templates/pages/admin-documents-form.template.ts","../src/templates/components/alert.template.ts"],"names":[],"mappings":";;;;AAAA,mCAAA,EAAA;;;ACaO,SAAS,YAAY,IAAA,EAAyB;AACnD,EAAA,MAAM,WAAA,GAAc;AAAA,IAClB,OAAA,EAAS,sFAAA;AAAA,IACT,KAAA,EAAO,6DAAA;AAAA,IACP,OAAA,EAAS,sFAAA;AAAA,IACT,IAAA,EAAM;AAAA,GACR;AAEA,EAAA,MAAM,WAAA,GAAc;AAAA,IAClB,OAAA,EAAS,oCAAA;AAAA,IACT,KAAA,EAAO,gCAAA;AAAA,IACP,OAAA,EAAS,oCAAA;AAAA,IACT,IAAA,EAAM;AAAA,GACR;AAEA,EAAA,MAAM,WAAA,GAAc;AAAA,IAClB,OAAA,EAAS,oCAAA;AAAA,IACT,KAAA,EAAO,gCAAA;AAAA,IACP,OAAA,EAAS,oCAAA;AAAA,IACT,IAAA,EAAM;AAAA,GACR;AAEA,EAAA,MAAM,kBAAA,GAAqB;AAAA,IACzB,OAAA,EAAS,oCAAA;AAAA,IACT,KAAA,EAAO,gCAAA;AAAA,IACP,OAAA,EAAS,oCAAA;AAAA,IACT,IAAA,EAAM;AAAA,GACR;AAEA,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,OAAA,EAAS,CAAA,0LAAA,CAAA;AAAA,IACT,KAAA,EAAO,CAAA,4QAAA,CAAA;AAAA,IACP,OAAA,EAAS,CAAA,sQAAA,CAAA;AAAA,IACT,IAAA,EAAM,CAAA,qLAAA;AAAA,GACR;AAEA,EAAA,OAAO;AAAA,+BAAA,EACwB,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,SAAA,IAAa,EAAE,CAAA,EAAA,EAAK,IAAA,CAAK,WAAA,GAAc,wBAAA,GAA2B,EAAE,CAAA;AAAA;AAAA,QAAA,EAE1H,IAAA,CAAK,SAAS,KAAA,GAAQ;AAAA;AAAA,gCAAA,EAEE,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,cAAA,EACxC,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC;AAAA;AAAA;AAAA,QAAA,CAAA,GAGpB,EAAE;AAAA,oBAAA,EACQ,IAAA,CAAK,IAAA,KAAS,KAAA,GAAQ,MAAA,GAAS,EAAE,CAAA;AAAA,UAAA,EAC3C,KAAK,KAAA,GAAQ;AAAA,6CAAA,EACsB,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,cAAA,EACrD,UAAA,CAAW,IAAA,CAAK,KAAK,CAAC;AAAA;AAAA,UAAA,CAAA,GAExB,EAAE;AAAA,sBAAA,EACQ,IAAA,CAAK,QAAQ,cAAA,GAAiB,SAAS,IAAI,kBAAA,CAAmB,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,eAAA,EAC/E,UAAA,CAAW,IAAA,CAAK,OAAO,CAAC,CAAA;AAAA;AAAA;AAAA,QAAA,EAG/B,KAAK,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA,oDAAA,EAKyB,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA,GAUhE,EAAE;AAAA;AAAA;AAAA,EAAA,CAAA;AAId;;;ADtEA,SAAS,WAAW,KAAA,EAAwB;AAC1C,EAAA,MAAM,IAAA,GAAO,0MAAA;AACb,EAAA,OAAO,KAAA,GACH,CAAA,EAAG,IAAI,CAAA,mCAAA,CAAA,GACP,GAAG,IAAI,CAAA,qCAAA,CAAA;AACb;AAEA,SAAS,gBAAA,CAAiB,KAAA,EAAuB,KAAA,EAAgB,KAAA,EAAwB;AACvF,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,KAAA,CAAM,IAAI,CAAA,CAAA;AAC7B,EAAA,MAAM,IAAA,GAAO,CAAA,KAAA,EAAQ,KAAA,CAAM,IAAI,CAAA,CAAA,CAAA;AAC/B,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY,KAAK,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,CAAA,CAAA,KAAK,CAAA,CAAE,WAAA,EAAa,CAAA;AACtF,EAAA,MAAM,MAAA,GAAS,KAAA,IAAS,IAAA,GAAO,MAAA,CAAO,KAAK,CAAA,GAAI,EAAA;AAE/C,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,SAAA,IAAa,KAAA,CAAM,SAAS,QAAA,EAAU;AACvD,IAAA,KAAA,GAAQ,4BAA4B,EAAE,CAAA,QAAA,EAAW,IAAI,CAAA,SAAA,EAAY,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA,oBAAA,EACjE,KAAA,CAAM,IAAA,KAAS,SAAA,GAAY,GAAA,GAAM,KAAK,CAAA;AAAA,qBAAA,EACrC,UAAA,CAAW,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,EACtC,CAAA,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,SAAA,EAAW;AAGnC,IAAA,KAAA,GAAQ,CAAA;AAAA,0CAAA,EACgC,IAAI,CAAA;AAAA,0CAAA,EACJ,EAAE,CAAA,QAAA,EAAW,IAAI,kBAAkB,MAAA,KAAW,MAAA,GAAS,YAAY,EAAE;AAAA;AAAA,sEAAA,EAEzC,KAAK,CAAA;AAAA,mBAAA,CAAA;AAEzE,IAAA,OAAO;AAAA;AAAA,QAAA,EAED,KAAK;AAAA,QAAA,EACL,QAAQ,CAAA,qCAAA,EAAwC,UAAA,CAAW,KAAK,CAAC,SAAS,EAAE;AAAA,YAAA,CAAA;AAAA,EAEpF,CAAA,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,OAAA,EAAS;AAEjC,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,KAAK,IAAK,KAAA,CAAmB,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AACvE,IAAA,KAAA,GAAQ,0BAA0B,EAAE,CAAA,QAAA,EAAW,IAAI,CAAA,SAAA,EAAY,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA;AAAA,qBAAA,EAE9D,UAAA,CAAW,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,EACtC,CAAA,MAAO;AACL,IAAA,KAAA,GAAQ,0BAA0B,EAAE,CAAA,QAAA,EAAW,IAAI,CAAA,SAAA,EAAY,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA,qBAAA,EAC9D,UAAA,CAAW,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,EACtC;AAEA,EAAA,OAAO;AAAA;AAAA,kBAAA,EAEW,EAAE,CAAA;AAAA,QAAA,EACZ,KAAK;AAAA;AAAA,MAAA,EAEP,KAAK;AAAA,MAAA,EACL,QAAQ,CAAA,qCAAA,EAAwC,UAAA,CAAW,KAAK,CAAC,SAAS,EAAE;AAAA,UAAA,CAAA;AAEpF;AAEA,SAAS,qBAAA,CACP,OAAA,EACA,eAAA,EACA,MAAA,EACQ;AACR,EAAA,MAAM,UAAA,GAAa,IAAI,GAAA,CAAI,eAAA,CAAgB,IAAI,CAAA,CAAA,KAAK,CAAA,CAAE,IAAI,CAAC,CAAA;AAC3D,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,CAAO,CAAA,CAAA,KAAK,CAAC,UAAA,CAAW,GAAA,CAAI,CAAC,CAAC,CAAA;AACzE,EAAA,IAAI,aAAA,CAAc,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAEvC,EAAA,MAAM,MAAA,GAAS,aAAA,CAAc,GAAA,CAAI,CAAA,GAAA,KAAO;AACtC,IAAA,MAAM,GAAA,GAAM,QAAQ,GAAG,CAAA;AACvB,IAAA,MAAM,EAAA,GAAK,QAAQ,GAAG,CAAA,CAAA;AACtB,IAAA,MAAM,IAAA,GAAO,QAAQ,GAAG,CAAA,CAAA,CAAA;AACxB,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,OAAA,CAAQ,UAAA,EAAY,KAAK,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,CAAA,CAAA,KAAK,CAAA,CAAE,WAAA,EAAa,CAAA;AAC/E,IAAA,MAAM,MAAA,GAAS,OAAO,GAAA,KAAQ,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,GAAA,EAAK,IAAA,EAAM,CAAC,CAAA,GAAI,MAAA,CAAO,GAAA,IAAO,EAAE,CAAA;AACxF,IAAA,MAAM,cAAc,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,IAAK,OAAO,MAAA,GAAS,GAAA;AAE7D,IAAA,OAAO;AAAA;AAAA,oBAAA,EAEW,EAAE,CAAA,0EAAA,EAA6E,UAAA,CAAW,KAAK,CAAC,CAAA;AAAA,QAAA,EAC5G,WAAA,GACE,CAAA,cAAA,EAAiB,EAAE,CAAA,QAAA,EAAW,IAAI,CAAA,kBAAA,EAAqB,UAAA,CAAW,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA,EAAA,EAAK,WAAW,MAAM,CAAC,CAAA,WAAA,CAAA,GACrG,CAAA,uBAAA,EAA0B,EAAE,CAAA,QAAA,EAAW,IAAI,CAAA,SAAA,EAAY,UAAA,CAAW,MAAM,CAAC,CAAA,SAAA,EAAY,UAAA,CAAW,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA,EAAA,CAChH;AAAA,QAAA,EACE,MAAA,CAAO,GAAG,CAAA,GAAI,CAAA,qCAAA,EAAwC,UAAA,CAAW,OAAO,GAAG,CAAC,CAAC,CAAA,IAAA,CAAA,GAAS,EAAE;AAAA,YAAA,CAAA;AAAA,EAEhG,CAAC,CAAA,CAAE,IAAA,CAAK,EAAE,CAAA;AAEV,EAAA,OAAO;AAAA;AAAA;AAAA,yDAAA,EAGkD,MAAM,CAAA;AAAA,UAAA,CAAA;AAEjE;AAEO,SAAS,uBAAuB,IAAA,EAAgC;AACrE,EAAA,MAAM,EAAE,SAAS,GAAA,EAAK,YAAA,EAAc,QAAQ,MAAA,GAAS,IAAG,GAAI,IAAA;AAC5D,EAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,eAAA,IAAmB,EAAC;AACpD,EAAA,MAAM,OAAA,GAAW,GAAA,EAAK,IAAA,IAAQ,EAAC;AAE/B,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,EAAM,IAAA,KAAS,OAAA;AACpC,EAAA,MAAM,QAAA,GAAW,OAAA,IAAW,IAAA,CAAK,IAAA,EAAM,IAAA,KAAS,QAAA;AAEhD,EAAA,MAAM,aAAA,GAAgB,MAAA,IAAU,GAAA,IAAO,CAAC,IAAI,WAAA,IAAe,YAAA;AAC3D,EAAA,MAAM,mBAAA,GAAsB,MAAA,IAAU,GAAA,EAAK,WAAA,IAAe,GAAA,EAAK,cAAA;AAI/D,EAAA,MAAM,aAAa,MAAA,GACf,CAAA,yBAAA,EAA4B,UAAA,CAAW,OAAA,CAAQ,EAAE,CAAC,CAAA,CAAA,EAAI,UAAA,CAAW,GAAA,CAAK,MAAM,CAAC,CAAA,CAAA,GAC7E,4BAA4B,UAAA,CAAW,OAAA,CAAQ,EAAE,CAAC,CAAA,IAAA,CAAA;AAEtD,EAAA,MAAM,qBAAqB,MAAM;AAC/B,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,GAAA,EAAK,OAAO,EAAA;AAC5B,IAAA,IAAI,mBAAA,EAAqB;AACvB,MAAA,OAAO,WAAA,CAAY;AAAA,QACjB,IAAA,EAAM,SAAA;AAAA,QACN,OAAA,EAAS;AAAA,OACV,CAAA;AAAA,IACH;AACA,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,OAAO,WAAA,CAAY;AAAA,QACjB,IAAA,EAAM,MAAA;AAAA,QACN,SAAS,CAAA,sBAAA,EAAyB,YAAA,CAAc,aAAa,CAAA,gDAAA,EAAmD,IAAI,aAAa,CAAA,EAAA;AAAA,OAClI,CAAA;AAAA,IACH;AACA,IAAA,OAAO,EAAA;AAAA,EACT,CAAA,GAAG;AAIH,EAAA,MAAM,eAAA,GAAkB,eAAA,CACrB,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,WAAW,CAAA,CAClC,GAAA,CAAI,CAAA,CAAA,KAAK,gBAAA,CAAiB,CAAA,EAAG,QAAQ,CAAA,CAAE,IAAI,CAAA,EAAG,MAAA,CAAO,CAAA,KAAA,EAAQ,CAAA,CAAE,IAAI,CAAA,CAAE,CAAC,CAAC,CAAA,CACvE,IAAA,CAAK,EAAE,CAAA;AAEV,EAAA,MAAM,aAAA,GAAgB,qBAAA,CAAsB,OAAA,EAAS,eAAA,EAAiB,MAAM,CAAA;AAE5E,EAAA,MAAM,OAAA,GAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CAAA,EAQ8B,UAAA,CAAW,QAAQ,EAAE,CAAC,uDAAuD,UAAA,CAAW,OAAA,CAAQ,WAAW,CAAC,CAAA;AAAA;AAAA,oEAAA,EAEtF,MAAA,GAAS,SAAS,KAAK,CAAA;AAAA;AAAA;AAAA,YAAA,EAG/E,MAAA,GAAS,CAAA,KAAA,EAAQ,UAAA,CAAW,OAAA,CAAQ,WAAW,CAAC,CAAA,CAAA,GAAK,CAAA,IAAA,EAAO,UAAA,CAAW,OAAA,CAAQ,WAAW,CAAC,CAAA,CAAE;AAAA;AAAA,UAAA,EAE/F,MAAA,IAAU,GAAA,GAAM,CAAA,0DAAA,EAA6D,GAAA,CAAI,aAAa,CAAA,oCAAA,EAAoC,UAAA,CAAW,GAAA,CAAI,MAAM,CAAC,CAAA,WAAA,CAAA,GAAgB,EAAE;AAAA;;AAAA;AAAA,QAAA,EAI5K,MAAA,IAAU,GAAA,IAAO,QAAA,IAAY,IAAA,CAAK,iBAAA,GAAoB;AAAA;AAAA,UAAA,EAEpD,CAAC,IAAI,WAAA,GAAc;AAAA,+DAAA,EACkC,UAAA,CAAW,QAAQ,EAAE,CAAC,IAAI,UAAA,CAAW,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,CAAA,GAKxF;AAAA,+DAAA,EAC4C,UAAA,CAAW,QAAQ,EAAE,CAAC,IAAI,UAAA,CAAW,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,CAK3F;AAAA,cAAA,CAAA,GACA,EAAE;AAAA;;AAAA,MAAA,EAGZ,iBAAiB;AAAA,MAAA,EACjB,IAAA,CAAK,OAAA,GAAU,WAAA,CAAY,EAAE,MAAM,IAAA,CAAK,WAAA,IAAe,MAAA,EAAQ,OAAA,EAAS,KAAK,OAAA,EAAS,WAAA,EAAa,IAAA,EAAM,IAAI,EAAE;;AAAA;AAAA,kCAAA,EAGnF,UAAU,CAAA;AAAA,QAAA,EACpC,MAAA,GAAS,qDAAqD,EAAE;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oEAAA,EAYJ,UAAA,CAAW,GAAA,EAAK,KAAA,IAAS,EAAE,CAAC,CAAA;AAAA,2BAAA,EACrE,UAAA,CAAW,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,kBAAA,EACjC,MAAA,CAAO,QAAQ,CAAA,qCAAA,EAAwC,UAAA,CAAW,OAAO,KAAK,CAAC,SAAS,EAAE;AAAA;AAAA;AAAA;AAAA,kEAAA,EAI1C,UAAA,CAAW,GAAA,EAAK,IAAA,IAAQ,EAAE,CAAC,CAAA;AAAA;AAAA,2BAAA,EAElE,UAAA,CAAW,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,kBAAA,EAChC,MAAA,CAAO,OAAO,CAAA,qCAAA,EAAwC,UAAA,CAAW,OAAO,IAAI,CAAC,SAAS,EAAE;AAAA;AAAA;AAAA;;AAAA;AAAA,YAAA,EAM9F,eAAA,CAAgB,SAAS,CAAA,GAAI;AAAA;AAAA;AAAA;AAAA,gBAAA,EAIzB,eAAe;AAAA;AAAA,kBAAA,CAAA,GAEX,EAAE;;AAAA;AAAA,YAAA,EAGV,aAAa;;AAAA;AAAA;AAAA,gDAAA,EAIuB,UAAA,CAAW,OAAA,CAAQ,EAAE,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAA,EAOpD,MAAA,GAAU,IAAA,CAAK,iBAAA,GAAoB,YAAA,GAAe,WAAY,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,MAAA,EASlF,MAAA,IAAU,GAAA,IAAO,IAAA,CAAK,iBAAA,GAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAAA,EAUP,UAAA,CAAW,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAAA,CAAA,GAM7C,EAAE;AAAA;AAAA,EAAA,CAAA;AAIpB,EAAA,OAAO,yBAAA,CAA0B;AAAA,IAC/B,OAAO,CAAA,EAAG,MAAA,GAAS,SAAS,KAAK,CAAA,CAAA,EAAI,QAAQ,WAAW,CAAA,iBAAA,CAAA;AAAA,IACxD,WAAA,EAAa,gBAAA;AAAA,IACb,MAAM,IAAA,CAAK,IAAA;AAAA,IACX,SAAS,IAAA,CAAK,OAAA;AAAA,IACd;AAAA,GACD,CAAA;AACH;AAkBO,SAAS,6BAA6B,IAAA,EAAkC;AAC7E,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG;AAC9B,IAAA,OAAO,CAAA,wFAAA,CAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,CAAA,CAAA,KAAK;AAAA;AAAA;AAAA,yEAAA,EAGqC,EAAE,aAAa,CAAA;AAAA,QAAA,EAChF,CAAA,CAAE,WAAA,GAAc,CAAA,mKAAA,CAAA,GAAwK,EAAE;AAAA,QAAA,EAC1L,CAAA,CAAE,cAAA,GAAiB,CAAA,gKAAA,CAAA,GAAqK,EAAE;AAAA;AAAA;AAAA,cAAA,EAGpL,CAAA,CAAE,aAAa,QAAG,CAAA;AAAA,cAAA,EAClB,IAAI,IAAA,CAAK,CAAA,CAAE,SAAA,GAAY,GAAI,CAAA,CAAE,cAAA,CAAe,OAAA,EAAS,EAAE,SAAA,EAAW,OAAA,EAAS,SAAA,EAAW,OAAA,EAAS,CAAC,CAAA;AAAA;AAAA;AAAA,EAAA,CAG7G,CAAA,CAAE,KAAK,EAAE,CAAA;AAEV,EAAA,OAAO,QAAQ,IAAI,CAAA,MAAA,CAAA;AACrB","file":"chunk-EF2NQUIQ.js","sourcesContent":["import { renderAdminLayoutCatalyst } from '../layouts/admin-layout-catalyst.template'\nimport { renderAlert } from '../components/alert.template'\nimport { escapeHtml } from '../../utils/sanitize'\nimport type { Document, DocumentType, QueryableField } from '../../schemas/document'\n\nexport interface DocumentFormData {\n docType: DocumentType\n doc?: Document\n publishedDoc?: Document | null // Published revision when different from current draft\n isEdit: boolean\n errors?: Record<string, string>\n message?: string\n messageType?: 'success' | 'error' | 'warning' | 'info'\n user?: { name: string; email: string; role: string }\n version?: string\n versioningEnabled?: boolean // Whether versioning is opt-in for this document type\n}\n\nfunction inputClass(error?: string): string {\n const base = 'block w-full rounded-lg border bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors'\n return error\n ? `${base} border-red-400 dark:border-red-500`\n : `${base} border-zinc-300 dark:border-zinc-700`\n}\n\nfunction renderFieldInput(field: QueryableField, value: unknown, error?: string): string {\n const id = `data_${field.name}`\n const name = `data[${field.name}]`\n const label = field.name.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase())\n const strVal = value != null ? String(value) : ''\n\n let input: string\n if (field.type === 'integer' || field.type === 'number') {\n input = `<input type=\"number\" id=\"${id}\" name=\"${name}\" value=\"${escapeHtml(strVal)}\"\n step=\"${field.type === 'integer' ? '1' : 'any'}\"\n class=\"${inputClass(error)}\">`\n } else if (field.type === 'boolean') {\n // Hidden 'false' before the checkbox: an unchecked checkbox submits nothing, so without this a\n // boolean could never be set back to false (D15). When checked, the checkbox value wins.\n input = `<div class=\"flex items-center gap-2\">\n <input type=\"hidden\" name=\"${name}\" value=\"false\">\n <input type=\"checkbox\" id=\"${id}\" name=\"${name}\" value=\"true\" ${strVal === 'true' ? 'checked' : ''}\n class=\"h-4 w-4 rounded border-zinc-300 dark:border-zinc-600 text-blue-600 focus:ring-blue-500\">\n <span class=\"text-sm text-zinc-700 dark:text-zinc-300\">${label}</span>\n </div>`\n return `\n <div>\n ${input}\n ${error ? `<p class=\"mt-1 text-xs text-red-500\">${escapeHtml(error)}</p>` : ''}\n </div>`\n } else if (field.kind === 'facet') {\n // Multi-value: comma-separated list\n const arrVal = Array.isArray(value) ? (value as string[]).join(', ') : strVal\n input = `<input type=\"text\" id=\"${id}\" name=\"${name}\" value=\"${escapeHtml(arrVal)}\"\n placeholder=\"Comma-separated values\"\n class=\"${inputClass(error)}\">`\n } else {\n input = `<input type=\"text\" id=\"${id}\" name=\"${name}\" value=\"${escapeHtml(strVal)}\"\n class=\"${inputClass(error)}\">`\n }\n\n return `\n <div>\n <label for=\"${id}\" class=\"block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1\">\n ${label}\n </label>\n ${input}\n ${error ? `<p class=\"mt-1 text-xs text-red-500\">${escapeHtml(error)}</p>` : ''}\n </div>`\n}\n\nfunction renderRemainingFields(\n allData: Record<string, unknown>,\n queryableFields: QueryableField[],\n errors: Record<string, string>,\n): string {\n const knownNames = new Set(queryableFields.map(f => f.name))\n const remainingKeys = Object.keys(allData).filter(k => !knownNames.has(k))\n if (remainingKeys.length === 0) return ''\n\n const inputs = remainingKeys.map(key => {\n const val = allData[key]\n const id = `data_${key}`\n const name = `data[${key}]`\n const label = key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase())\n const strVal = typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val ?? '')\n const isMultiline = strVal.includes('\\n') || strVal.length > 100\n\n return `\n <div>\n <label for=\"${id}\" class=\"block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1\">${escapeHtml(label)}</label>\n ${isMultiline\n ? `<textarea id=\"${id}\" name=\"${name}\" rows=\"4\" class=\"${inputClass(errors[key])}\">${escapeHtml(strVal)}</textarea>`\n : `<input type=\"text\" id=\"${id}\" name=\"${name}\" value=\"${escapeHtml(strVal)}\" class=\"${inputClass(errors[key])}\">`\n }\n ${errors[key] ? `<p class=\"mt-1 text-xs text-red-500\">${escapeHtml(errors[key])}</p>` : ''}\n </div>`\n }).join('')\n\n return `\n <div class=\"border-t border-zinc-200 dark:border-zinc-700 pt-6\">\n <h3 class=\"text-sm font-medium text-zinc-500 dark:text-zinc-400 mb-4\">Additional Fields</h3>\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">${inputs}</div>\n </div>`\n}\n\nexport function renderDocumentFormPage(data: DocumentFormData): string {\n const { docType, doc, publishedDoc, isEdit, errors = {} } = data\n const queryableFields = docType.queryableFields ?? []\n const docData = (doc?.data ?? {}) as Record<string, unknown>\n\n const isAdmin = data.user?.role === 'admin'\n const isEditor = isAdmin || data.user?.role === 'editor'\n\n const hasNewerDraft = isEdit && doc && !doc.isPublished && publishedDoc\n const isPublishedAndDraft = isEdit && doc?.isPublished && doc?.isCurrentDraft\n\n // Real document CRUD lives under /admin/content/documents/:typeId/... (admin-content.ts), NOT\n // /admin/documents/ui (those are GET redirects only). Edit/save POSTs to :rootId; create to /new.\n const formAction = isEdit\n ? `/admin/content/documents/${escapeHtml(docType.id)}/${escapeHtml(doc!.rootId)}`\n : `/admin/content/documents/${escapeHtml(docType.id)}/new`\n\n const publishBannerHtml = (() => {\n if (!isEdit || !doc) return ''\n if (isPublishedAndDraft) {\n return renderAlert({\n type: 'success',\n message: 'This document is live. Saving creates a new draft. Use \"Publish\" to push changes live.',\n })\n }\n if (hasNewerDraft) {\n return renderAlert({\n type: 'info',\n message: `A published version (v${publishedDoc!.versionNumber}) is still live. This is an unpublished draft (v${doc.versionNumber}).`,\n })\n }\n return ''\n })()\n\n // Reference-kind fields are intentionally not rendered yet (D27) — fine for FAQ/testimonial, which\n // have none. When media references land (Phase 6), render a root-id picker here.\n const queryableInputs = queryableFields\n .filter(f => f.kind !== 'reference')\n .map(f => renderFieldInput(f, docData[f.name], errors[`data.${f.name}`]))\n .join('')\n\n const remainingHtml = renderRemainingFields(docData, queryableFields, errors)\n\n const content = `\n <div class=\"w-full px-4 sm:px-6 lg:px-8 py-6 space-y-6\">\n <!-- Header -->\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <div class=\"flex items-center gap-2 text-sm text-zinc-500 dark:text-zinc-400 mb-1\">\n <a href=\"/admin/content\" class=\"hover:text-zinc-950 dark:hover:text-white\">Content</a>\n <svg class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\"/></svg>\n <a href=\"/admin/content?model=doc:${escapeHtml(docType.id)}\" class=\"hover:text-zinc-950 dark:hover:text-white\">${escapeHtml(docType.displayName)}</a>\n <svg class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\"/></svg>\n <span class=\"text-zinc-950 dark:text-white font-medium\">${isEdit ? 'Edit' : 'New'}</span>\n </div>\n <h1 class=\"text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8\">\n ${isEdit ? `Edit ${escapeHtml(docType.displayName)}` : `New ${escapeHtml(docType.displayName)}`}\n </h1>\n ${isEdit && doc ? `<p class=\"mt-1 text-xs text-zinc-500 dark:text-zinc-400\">v${doc.versionNumber} · root: <code class=\"font-mono\">${escapeHtml(doc.rootId)}</code></p>` : ''}\n </div>\n\n <!-- Publish controls (edit mode, versioning types only) -->\n ${isEdit && doc && isEditor && data.versioningEnabled ? `\n <div class=\"mt-4 sm:mt-0 flex gap-2\">\n ${!doc.isPublished ? `\n <form method=\"POST\" action=\"/admin/content/documents/${escapeHtml(docType.id)}/${escapeHtml(doc.id)}/publish\">\n <button type=\"submit\"\n class=\"inline-flex items-center rounded-lg bg-green-600 px-3.5 py-2.5 text-sm font-semibold text-white hover:bg-green-500 transition-colors shadow-sm\">\n Publish\n </button>\n </form>` : `\n <form method=\"POST\" action=\"/admin/content/documents/${escapeHtml(docType.id)}/${escapeHtml(doc.id)}/unpublish\">\n <button type=\"submit\"\n class=\"inline-flex items-center rounded-lg bg-amber-500 px-3.5 py-2.5 text-sm font-semibold text-white hover:bg-amber-400 transition-colors shadow-sm\">\n Unpublish\n </button>\n </form>`}\n </div>` : ''}\n </div>\n\n ${publishBannerHtml}\n ${data.message ? renderAlert({ type: data.messageType ?? 'info', message: data.message, dismissible: true }) : ''}\n\n <!-- Form -->\n <form method=\"POST\" action=\"${formAction}\">\n ${isEdit ? `<input type=\"hidden\" name=\"_method\" value=\"PUT\">` : ''}\n\n <div class=\"relative rounded-xl\">\n <div class=\"absolute inset-0 bg-gradient-to-br from-blue-500/5 to-purple-500/5 dark:from-blue-400/10 dark:to-purple-400/10 rounded-xl\"></div>\n <div class=\"relative bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 rounded-xl p-6 space-y-6\">\n\n <!-- Standard fields -->\n <div>\n <h3 class=\"text-sm font-semibold text-zinc-700 dark:text-zinc-200 mb-4\">Document</h3>\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label for=\"title\" class=\"block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1\">Title</label>\n <input type=\"text\" id=\"title\" name=\"title\" value=\"${escapeHtml(doc?.title ?? '')}\"\n class=\"${inputClass(errors.title)}\">\n ${errors.title ? `<p class=\"mt-1 text-xs text-red-500\">${escapeHtml(errors.title)}</p>` : ''}\n </div>\n <div>\n <label for=\"slug\" class=\"block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1\">Slug</label>\n <input type=\"text\" id=\"slug\" name=\"slug\" value=\"${escapeHtml(doc?.slug ?? '')}\"\n placeholder=\"auto-generated-if-empty\"\n class=\"${inputClass(errors.slug)}\">\n ${errors.slug ? `<p class=\"mt-1 text-xs text-red-500\">${escapeHtml(errors.slug)}</p>` : ''}\n </div>\n </div>\n </div>\n\n <!-- Queryable data fields -->\n ${queryableFields.length > 0 ? `\n <div class=\"border-t border-zinc-200 dark:border-zinc-700 pt-6\">\n <h3 class=\"text-sm font-semibold text-zinc-700 dark:text-zinc-200 mb-4\">Content</h3>\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n ${queryableInputs}\n </div>\n </div>` : ''}\n\n <!-- Remaining data fields not in queryable fields -->\n ${remainingHtml}\n\n <!-- Actions -->\n <div class=\"border-t border-zinc-200 dark:border-zinc-700 pt-6 flex items-center justify-between\">\n <a href=\"/admin/content?model=doc:${escapeHtml(docType.id)}\"\n class=\"inline-flex items-center rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-700 dark:text-zinc-200 ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors\">\n Cancel\n </a>\n <div class=\"flex gap-3\">\n <button type=\"submit\"\n class=\"inline-flex items-center rounded-lg bg-zinc-950 dark:bg-white px-3.5 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors shadow-sm\">\n ${isEdit ? (data.versioningEnabled ? 'Save Draft' : 'Update') : 'Create'}\n </button>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <!-- Version history (edit mode, versioning opt-in only) -->\n ${isEdit && doc && data.versioningEnabled ? `\n <details class=\"group\">\n <summary class=\"cursor-pointer text-sm text-zinc-500 dark:text-zinc-400 hover:text-zinc-950 dark:hover:text-white flex items-center gap-2 py-2\">\n <svg class=\"h-4 w-4 transition-transform group-open:rotate-90\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\"/>\n </svg>\n Version history\n </summary>\n <div class=\"mt-3 rounded-xl bg-white dark:bg-zinc-900 ring-1 ring-zinc-950/5 dark:ring-white/10 overflow-hidden\">\n <div id=\"version-history-placeholder\" class=\"px-6 py-4 text-sm text-zinc-500 dark:text-zinc-400\"\n hx-get=\"/admin/versioning/${escapeHtml(doc.rootId)}\"\n hx-trigger=\"revealed\"\n hx-swap=\"outerHTML\">\n Loading version history…\n </div>\n </div>\n </details>` : ''}\n </div>\n `\n\n return renderAdminLayoutCatalyst({\n title: `${isEdit ? 'Edit' : 'New'} ${docType.displayName} — Documents`,\n currentPath: '/admin/content',\n user: data.user,\n version: data.version,\n content,\n })\n}\n\n// ─── Version history fragment (HTMX target) ──────────────────────────────────\n\nexport interface VersionHistoryData {\n versions: Array<{\n id: string\n versionNumber: number\n isCurrentDraft: boolean\n isPublished: boolean\n status: string\n updatedAt: number\n createdBy: string | null\n }>\n docType: DocumentType\n rootId: string\n}\n\nexport function renderVersionHistoryFragment(data: VersionHistoryData): string {\n if (data.versions.length === 0) {\n return `<div class=\"px-6 py-4 text-sm text-zinc-500 dark:text-zinc-400\">No versions found.</div>`\n }\n\n const rows = data.versions.map(v => `\n <div class=\"flex items-center justify-between px-6 py-3 border-b border-zinc-100 dark:border-zinc-800 last:border-0\">\n <div class=\"flex items-center gap-3\">\n <span class=\"text-sm font-medium text-zinc-950 dark:text-white\">v${v.versionNumber}</span>\n ${v.isPublished ? `<span class=\"inline-flex items-center rounded-md bg-green-50 dark:bg-green-500/10 px-1.5 py-0.5 text-xs font-medium text-green-700 dark:text-green-400\">live</span>` : ''}\n ${v.isCurrentDraft ? `<span class=\"inline-flex items-center rounded-md bg-blue-50 dark:bg-blue-500/10 px-1.5 py-0.5 text-xs font-medium text-blue-700 dark:text-blue-400\">draft</span>` : ''}\n </div>\n <div class=\"flex items-center gap-4 text-xs text-zinc-500 dark:text-zinc-400\">\n <span>${v.createdBy ?? '—'}</span>\n <span>${new Date(v.updatedAt * 1000).toLocaleString('en-US', { dateStyle: 'short', timeStyle: 'short' })}</span>\n </div>\n </div>\n `).join('')\n\n return `<div>${rows}</div>`\n}\n","import { escapeHtml } from '../../utils/sanitize'\n\nexport type AlertType = 'success' | 'error' | 'warning' | 'info'\n\nexport interface AlertData {\n type: AlertType\n title?: string\n message: string\n dismissible?: boolean\n className?: string\n icon?: boolean\n}\n\nexport function renderAlert(data: AlertData): string {\n const typeClasses = {\n success: 'bg-green-50 dark:bg-green-500/10 border border-green-600/20 dark:border-green-500/20',\n error: 'bg-error/10 border border-red-600/20 dark:border-red-500/20',\n warning: 'bg-amber-50 dark:bg-amber-500/10 border border-amber-600/20 dark:border-amber-500/20',\n info: 'bg-blue-50 dark:bg-blue-500/10 border border-blue-600/20 dark:border-blue-500/20'\n }\n\n const iconClasses = {\n success: 'text-green-600 dark:text-green-400',\n error: 'text-red-600 dark:text-red-400',\n warning: 'text-amber-600 dark:text-amber-400',\n info: 'text-blue-600 dark:text-blue-400'\n }\n\n const textClasses = {\n success: 'text-green-900 dark:text-green-300',\n error: 'text-red-900 dark:text-red-300',\n warning: 'text-amber-900 dark:text-amber-300',\n info: 'text-blue-900 dark:text-blue-300'\n }\n\n const messageTextClasses = {\n success: 'text-green-700 dark:text-green-400',\n error: 'text-red-700 dark:text-red-400',\n warning: 'text-amber-700 dark:text-amber-400',\n info: 'text-blue-700 dark:text-blue-400'\n }\n\n const icons = {\n success: `<path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\" clip-rule=\"evenodd\" />`,\n error: `<path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z\" clip-rule=\"evenodd\" />`,\n warning: `<path fill-rule=\"evenodd\" d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\" clip-rule=\"evenodd\" />`,\n info: `<path fill-rule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z\" clip-rule=\"evenodd\" />`\n }\n\n return `\n <div class=\"rounded-lg p-4 ${typeClasses[data.type]} ${data.className || ''}\" ${data.dismissible ? 'id=\"dismissible-alert\"' : ''}>\n <div class=\"flex\">\n ${data.icon !== false ? `\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 ${iconClasses[data.type]}\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n ${icons[data.type]}\n </svg>\n </div>\n ` : ''}\n <div class=\"${data.icon !== false ? 'ml-3' : ''}\">\n ${data.title ? `\n <h3 class=\"text-sm font-semibold ${textClasses[data.type]}\">\n ${escapeHtml(data.title)}\n </h3>\n ` : ''}\n <div class=\"${data.title ? 'mt-1 text-sm' : 'text-sm'} ${messageTextClasses[data.type]}\">\n <p>${escapeHtml(data.message)}</p>\n </div>\n </div>\n ${data.dismissible ? `\n <div class=\"ml-auto pl-3\">\n <div class=\"-mx-1.5 -my-1.5\">\n <button\n type=\"button\"\n class=\"inline-flex rounded-md p-1.5 ${iconClasses[data.type]} hover:bg-opacity-20 focus:outline-none focus:ring-2 focus:ring-offset-2\"\n onclick=\"document.getElementById('dismissible-alert').remove()\"\n >\n <span class=\"sr-only\">Dismiss</span>\n <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n </div>\n </div>\n ` : ''}\n </div>\n </div>\n `\n}\n\nexport function renderSuccessAlert(message: string, title?: string): string {\n return renderAlert({ type: 'success', message, title })\n}\n\nexport function renderErrorAlert(message: string, title?: string): string {\n return renderAlert({ type: 'error', message, title })\n}\n\nexport function renderWarningAlert(message: string, title?: string): string {\n return renderAlert({ type: 'warning', message, title })\n}\n\nexport function renderInfoAlert(message: string, title?: string): string {\n return renderAlert({ type: 'info', message, title })\n}\n"]}
@@ -0,0 +1,192 @@
1
+ import crypto from 'crypto';
2
+
3
+ // ../../node_modules/nanoid/index.js
4
+
5
+ // ../../node_modules/nanoid/url-alphabet/index.js
6
+ var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
7
+
8
+ // ../../node_modules/nanoid/index.js
9
+ var POOL_SIZE_MULTIPLIER = 128;
10
+ var pool;
11
+ var poolOffset;
12
+ var fillPool = (bytes) => {
13
+ if (bytes < 0 || bytes > 1024) throw new RangeError("Wrong ID size");
14
+ if (!pool || pool.length < bytes) {
15
+ pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
16
+ crypto.randomFillSync(pool);
17
+ poolOffset = 0;
18
+ } else if (poolOffset + bytes > pool.length) {
19
+ crypto.randomFillSync(pool);
20
+ poolOffset = 0;
21
+ }
22
+ poolOffset += bytes;
23
+ };
24
+ var nanoid = (size = 21) => {
25
+ fillPool(size |= 0);
26
+ let id = "";
27
+ for (let i = poolOffset - size; i < poolOffset; i++) {
28
+ id += urlAlphabet[pool[i] & 63];
29
+ }
30
+ return id;
31
+ };
32
+
33
+ // src/services/document-projection.ts
34
+ var MAX_PARAMS = 90;
35
+ function chunkArray(arr, size) {
36
+ const chunks = [];
37
+ for (let i = 0; i < arr.length; i += size) {
38
+ chunks.push(arr.slice(i, i + size));
39
+ }
40
+ return chunks;
41
+ }
42
+ var DocumentProjection = class {
43
+ constructor(db) {
44
+ this.db = db;
45
+ }
46
+ // Build D1 PreparedStatement arrays for inserting facets/references for a document.
47
+ // Returns raw D1 PreparedStatement objects suitable for inclusion in db.batch([...]).
48
+ buildDerivedInsertStatements(doc, queryableFields, now) {
49
+ const statements = [];
50
+ const facets = [];
51
+ const refs = [];
52
+ for (const field of queryableFields) {
53
+ const rawValue = this.extractPath(doc.data, field.path ?? `$.${field.name}`);
54
+ if (field.kind === "facet") {
55
+ const values = Array.isArray(rawValue) ? rawValue : rawValue != null ? [rawValue] : [];
56
+ values.forEach((v, ordinal) => {
57
+ const isNum = typeof v === "number";
58
+ facets.push({
59
+ id: nanoid(),
60
+ tenant_id: doc.tenantId,
61
+ document_id: doc.id,
62
+ root_id: doc.rootId,
63
+ type_id: doc.typeId,
64
+ field_name: field.name,
65
+ ordinal,
66
+ value_text: isNum ? null : String(v),
67
+ value_number: isNum ? v : null,
68
+ now
69
+ });
70
+ });
71
+ } else if (field.kind === "reference") {
72
+ const roots = Array.isArray(rawValue) ? rawValue : rawValue != null ? [rawValue] : [];
73
+ roots.forEach((rootId, ordinal) => {
74
+ refs.push({
75
+ id: nanoid(),
76
+ tenant_id: doc.tenantId,
77
+ from_root_id: doc.rootId,
78
+ from_document_id: doc.id,
79
+ field_name: field.name,
80
+ ordinal,
81
+ to_root_id: String(rootId),
82
+ ref_strength: field.refStrength ?? "weak",
83
+ now
84
+ });
85
+ });
86
+ }
87
+ }
88
+ const FACET_COLS = 10;
89
+ for (const chunk of chunkArray(facets, Math.floor(MAX_PARAMS / FACET_COLS))) {
90
+ const placeholders = chunk.map(() => "(?,?,?,?,?,?,?,?,?,?)").join(",");
91
+ const params = [];
92
+ for (const f of chunk) {
93
+ params.push(f.id, f.tenant_id, f.document_id, f.root_id, f.type_id, f.field_name, f.ordinal, f.value_text, f.value_number, f.now);
94
+ }
95
+ statements.push(
96
+ this.db.prepare(
97
+ `INSERT INTO document_facets (id, tenant_id, document_id, root_id, type_id, field_name, ordinal, value_text, value_number, created_at) VALUES ${placeholders}`
98
+ ).bind(...params)
99
+ );
100
+ }
101
+ const REF_COLS = 9;
102
+ for (const chunk of chunkArray(refs, Math.floor(MAX_PARAMS / REF_COLS))) {
103
+ const placeholders = chunk.map(() => "(?,?,?,?,?,?,?,?,?)").join(",");
104
+ const params = [];
105
+ for (const r of chunk) {
106
+ params.push(r.id, r.tenant_id, r.from_root_id, r.from_document_id, r.field_name, r.ordinal, r.to_root_id, r.ref_strength, r.now);
107
+ }
108
+ statements.push(
109
+ this.db.prepare(
110
+ `INSERT INTO document_references (id, tenant_id, from_root_id, from_document_id, field_name, ordinal, to_root_id, ref_strength, created_at) VALUES ${placeholders}`
111
+ ).bind(...params)
112
+ );
113
+ }
114
+ return statements;
115
+ }
116
+ buildDerivedDeleteStatements(documentId) {
117
+ return [
118
+ this.db.prepare("DELETE FROM document_facets WHERE document_id = ?").bind(documentId),
119
+ this.db.prepare("DELETE FROM document_references WHERE from_document_id = ?").bind(documentId)
120
+ ];
121
+ }
122
+ // Rebuild derived rows for all current-draft and published rows of a type.
123
+ // One bounded admin action; not chunked cron orchestration.
124
+ async reindexType(typeId, tenantId, queryableFields) {
125
+ const result = await this.db.prepare(
126
+ `SELECT * FROM documents
127
+ WHERE type_id = ? AND tenant_id = ? AND (is_current_draft = 1 OR is_published = 1) AND deleted_at IS NULL`
128
+ ).bind(typeId, tenantId).all();
129
+ const rows = result.results ?? [];
130
+ if (rows.length === 0) return 0;
131
+ const now = Math.floor(Date.now() / 1e3);
132
+ let rebuilt = 0;
133
+ for (const chunk of chunkArray(rows, 20)) {
134
+ const statements = [];
135
+ for (const row of chunk) {
136
+ const doc = rowToDocument(row);
137
+ statements.push(...this.buildDerivedDeleteStatements(doc.id));
138
+ statements.push(...this.buildDerivedInsertStatements(doc, queryableFields, now));
139
+ }
140
+ if (statements.length > 0) {
141
+ await this.db.batch(statements);
142
+ rebuilt += chunk.length;
143
+ }
144
+ }
145
+ return rebuilt;
146
+ }
147
+ extractPath(data, path) {
148
+ if (path.startsWith("$.")) {
149
+ const key = path.slice(2);
150
+ return data[key] ?? null;
151
+ }
152
+ return null;
153
+ }
154
+ };
155
+ function rowToDocument(row) {
156
+ return {
157
+ id: row.id,
158
+ rootId: row.root_id,
159
+ typeId: row.type_id,
160
+ typeVersion: row.type_version,
161
+ versionOfId: row.version_of_id,
162
+ versionNumber: row.version_number,
163
+ isCurrentDraft: row.is_current_draft === 1,
164
+ isPublished: row.is_published === 1,
165
+ status: row.status,
166
+ parentRootId: row.parent_root_id,
167
+ slug: row.slug,
168
+ path: row.path,
169
+ title: row.title,
170
+ zone: row.zone,
171
+ sortOrder: row.sort_order,
172
+ visible: row.visible === 1,
173
+ publishedAt: row.published_at,
174
+ scheduledAt: row.scheduled_at,
175
+ expiresAt: row.expires_at,
176
+ deletedAt: row.deleted_at,
177
+ tenantId: row.tenant_id,
178
+ locale: row.locale,
179
+ translationGroupId: row.translation_group_id,
180
+ data: JSON.parse(row.data),
181
+ metadata: JSON.parse(row.metadata),
182
+ ownerId: row.owner_id,
183
+ createdBy: row.created_by,
184
+ updatedBy: row.updated_by,
185
+ createdAt: row.created_at,
186
+ updatedAt: row.updated_at
187
+ };
188
+ }
189
+
190
+ export { DocumentProjection, nanoid };
191
+ //# sourceMappingURL=chunk-GCDZZNIN.js.map
192
+ //# sourceMappingURL=chunk-GCDZZNIN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../node_modules/nanoid/url-alphabet/index.js","../../../node_modules/nanoid/index.js","../src/services/document-projection.ts"],"names":[],"mappings":"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","file":"chunk-GCDZZNIN.js","sourcesContent":["let urlAlphabet =\n 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'\nexport { urlAlphabet }\n","import crypto from 'crypto'\nimport { urlAlphabet } from './url-alphabet/index.js'\nconst POOL_SIZE_MULTIPLIER = 128\nlet pool, poolOffset\nlet fillPool = bytes => {\n if (bytes < 0 || bytes > 1024) throw new RangeError('Wrong ID size')\n if (!pool || pool.length < bytes) {\n pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER)\n crypto.randomFillSync(pool)\n poolOffset = 0\n } else if (poolOffset + bytes > pool.length) {\n crypto.randomFillSync(pool)\n poolOffset = 0\n }\n poolOffset += bytes\n}\nlet random = bytes => {\n fillPool((bytes |= 0))\n return pool.subarray(poolOffset - bytes, poolOffset)\n}\nlet customRandom = (alphabet, defaultSize, getRandom) => {\n let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1\n let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)\n return (size = defaultSize) => {\n let id = ''\n while (true) {\n let bytes = getRandom(step)\n let i = step\n while (i--) {\n id += alphabet[bytes[i] & mask] || ''\n if (id.length === size) return id\n }\n }\n }\n}\nlet customAlphabet = (alphabet, size = 21) =>\n customRandom(alphabet, size, random)\nlet nanoid = (size = 21) => {\n fillPool((size |= 0))\n let id = ''\n for (let i = poolOffset - size; i < poolOffset; i++) {\n id += urlAlphabet[pool[i] & 63]\n }\n return id\n}\nexport { nanoid, customAlphabet, customRandom, urlAlphabet, random }\n","import { D1Database } from '@cloudflare/workers-types'\nimport { nanoid } from 'nanoid'\nimport type { Document, QueryableField, DocumentRow } from '../schemas/document'\n\n// D1 hard limit: 100 bound parameters per statement. Keep under 90 for safety.\nconst MAX_PARAMS = 90\n\nfunction chunkArray<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = []\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size))\n }\n return chunks\n}\n\ninterface FacetInsert {\n id: string\n tenant_id: string\n document_id: string\n root_id: string\n type_id: string\n field_name: string\n ordinal: number\n value_text: string | null\n value_number: number | null\n now: number\n}\n\ninterface ReferenceInsert {\n id: string\n tenant_id: string\n from_root_id: string\n from_document_id: string\n field_name: string\n ordinal: number\n to_root_id: string\n ref_strength: string\n now: number\n}\n\nexport class DocumentProjection {\n constructor(private db: D1Database) {}\n\n // Build D1 PreparedStatement arrays for inserting facets/references for a document.\n // Returns raw D1 PreparedStatement objects suitable for inclusion in db.batch([...]).\n buildDerivedInsertStatements(\n doc: Document,\n queryableFields: QueryableField[],\n now: number,\n ): D1PreparedStatement[] {\n const statements: D1PreparedStatement[] = []\n const facets: FacetInsert[] = []\n const refs: ReferenceInsert[] = []\n\n for (const field of queryableFields) {\n const rawValue = this.extractPath(doc.data, field.path ?? `$.${field.name}`)\n\n if (field.kind === 'facet') {\n const values = Array.isArray(rawValue) ? rawValue : rawValue != null ? [rawValue] : []\n values.forEach((v, ordinal) => {\n const isNum = typeof v === 'number'\n facets.push({\n id: nanoid(),\n tenant_id: doc.tenantId,\n document_id: doc.id,\n root_id: doc.rootId,\n type_id: doc.typeId,\n field_name: field.name,\n ordinal,\n value_text: isNum ? null : String(v),\n value_number: isNum ? v : null,\n now,\n })\n })\n } else if (field.kind === 'reference') {\n const roots = Array.isArray(rawValue) ? rawValue : rawValue != null ? [rawValue] : []\n roots.forEach((rootId, ordinal) => {\n refs.push({\n id: nanoid(),\n tenant_id: doc.tenantId,\n from_root_id: doc.rootId,\n from_document_id: doc.id,\n field_name: field.name,\n ordinal,\n to_root_id: String(rootId),\n ref_strength: field.refStrength ?? 'weak',\n now,\n })\n })\n }\n // 'scalar' fields are VIRTUAL generated columns; no derived rows needed.\n }\n\n // Insert facets in chunks to respect the 100-param D1 limit (9 params per row).\n const FACET_COLS = 10\n for (const chunk of chunkArray(facets, Math.floor(MAX_PARAMS / FACET_COLS))) {\n const placeholders = chunk.map(() => '(?,?,?,?,?,?,?,?,?,?)').join(',')\n const params: (string | number | null)[] = []\n for (const f of chunk) {\n params.push(f.id, f.tenant_id, f.document_id, f.root_id, f.type_id, f.field_name, f.ordinal, f.value_text, f.value_number, f.now)\n }\n statements.push(\n this.db.prepare(\n `INSERT INTO document_facets (id, tenant_id, document_id, root_id, type_id, field_name, ordinal, value_text, value_number, created_at) VALUES ${placeholders}`,\n ).bind(...params),\n )\n }\n\n // Insert references in chunks (9 params per row).\n const REF_COLS = 9\n for (const chunk of chunkArray(refs, Math.floor(MAX_PARAMS / REF_COLS))) {\n const placeholders = chunk.map(() => '(?,?,?,?,?,?,?,?,?)').join(',')\n const params: (string | number | null)[] = []\n for (const r of chunk) {\n params.push(r.id, r.tenant_id, r.from_root_id, r.from_document_id, r.field_name, r.ordinal, r.to_root_id, r.ref_strength, r.now)\n }\n statements.push(\n this.db.prepare(\n `INSERT INTO document_references (id, tenant_id, from_root_id, from_document_id, field_name, ordinal, to_root_id, ref_strength, created_at) VALUES ${placeholders}`,\n ).bind(...params),\n )\n }\n\n return statements\n }\n\n buildDerivedDeleteStatements(documentId: string): D1PreparedStatement[] {\n return [\n this.db.prepare('DELETE FROM document_facets WHERE document_id = ?').bind(documentId),\n this.db.prepare('DELETE FROM document_references WHERE from_document_id = ?').bind(documentId),\n ]\n }\n\n // Rebuild derived rows for all current-draft and published rows of a type.\n // One bounded admin action; not chunked cron orchestration.\n async reindexType(typeId: string, tenantId: string, queryableFields: QueryableField[]): Promise<number> {\n // Only reindex rows that participate in queries (current-draft or published).\n const result = await this.db\n .prepare(\n `SELECT * FROM documents\n WHERE type_id = ? AND tenant_id = ? AND (is_current_draft = 1 OR is_published = 1) AND deleted_at IS NULL`,\n )\n .bind(typeId, tenantId)\n .all<DocumentRow>()\n\n const rows = result.results ?? []\n if (rows.length === 0) return 0\n\n const now = Math.floor(Date.now() / 1000)\n let rebuilt = 0\n\n // Process in batches of 20 documents to stay well under D1's 1000-rows-per-batch guidance.\n for (const chunk of chunkArray(rows, 20)) {\n const statements: D1PreparedStatement[] = []\n\n for (const row of chunk) {\n const doc = rowToDocument(row)\n statements.push(...this.buildDerivedDeleteStatements(doc.id))\n statements.push(...this.buildDerivedInsertStatements(doc, queryableFields, now))\n }\n\n if (statements.length > 0) {\n await this.db.batch(statements)\n rebuilt += chunk.length\n }\n }\n\n return rebuilt\n }\n\n private extractPath(data: Record<string, unknown>, path: string): unknown {\n // Supports simple $.<key> paths only; full JSONPath is overkill for the POC.\n if (path.startsWith('$.')) {\n const key = path.slice(2)\n return data[key] ?? null\n }\n return null\n }\n}\n\nfunction rowToDocument(row: DocumentRow): Document {\n return {\n id: row.id,\n rootId: row.root_id,\n typeId: row.type_id,\n typeVersion: row.type_version,\n versionOfId: row.version_of_id,\n versionNumber: row.version_number,\n isCurrentDraft: row.is_current_draft === 1,\n isPublished: row.is_published === 1,\n status: row.status,\n parentRootId: row.parent_root_id,\n slug: row.slug,\n path: row.path,\n title: row.title,\n zone: row.zone,\n sortOrder: row.sort_order,\n visible: row.visible === 1,\n publishedAt: row.published_at,\n scheduledAt: row.scheduled_at,\n expiresAt: row.expires_at,\n deletedAt: row.deleted_at,\n tenantId: row.tenant_id,\n locale: row.locale,\n translationGroupId: row.translation_group_id,\n data: JSON.parse(row.data),\n metadata: JSON.parse(row.metadata),\n ownerId: row.owner_id,\n createdBy: row.created_by,\n updatedBy: row.updated_by,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n"]}