@kywi-software/core 0.13.0 → 0.15.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 (508) hide show
  1. package/AGENT-PATTERNS.md +153 -17
  2. package/README.md +38 -12
  3. package/dist/admin/chrome.d.ts.map +1 -1
  4. package/dist/admin/chrome.js +57 -8
  5. package/dist/admin/chrome.js.map +1 -1
  6. package/dist/admin/field-components.d.ts.map +1 -1
  7. package/dist/admin/field-components.js +27 -1
  8. package/dist/admin/field-components.js.map +1 -1
  9. package/dist/admin/form-helpers.d.ts.map +1 -1
  10. package/dist/admin/form-helpers.js +6 -1
  11. package/dist/admin/form-helpers.js.map +1 -1
  12. package/dist/admin/index.d.ts +1 -1
  13. package/dist/admin/index.d.ts.map +1 -1
  14. package/dist/admin/index.js +1 -1
  15. package/dist/admin/index.js.map +1 -1
  16. package/dist/admin/layout-editor/chrome-env.d.ts +35 -0
  17. package/dist/admin/layout-editor/chrome-env.d.ts.map +1 -0
  18. package/dist/admin/layout-editor/chrome-env.js +13 -0
  19. package/dist/admin/layout-editor/chrome-env.js.map +1 -0
  20. package/dist/admin/layout-editor/component-actions.d.ts +58 -7
  21. package/dist/admin/layout-editor/component-actions.d.ts.map +1 -1
  22. package/dist/admin/layout-editor/component-actions.js +131 -5
  23. package/dist/admin/layout-editor/component-actions.js.map +1 -1
  24. package/dist/admin/layout-editor/component-fork.d.ts +4 -4
  25. package/dist/admin/layout-editor/component-fork.d.ts.map +1 -1
  26. package/dist/admin/layout-editor/component-fork.js +76 -10
  27. package/dist/admin/layout-editor/component-fork.js.map +1 -1
  28. package/dist/admin/layout-editor/component-picker-field.d.ts +17 -3
  29. package/dist/admin/layout-editor/component-picker-field.d.ts.map +1 -1
  30. package/dist/admin/layout-editor/component-picker-field.js +5 -2
  31. package/dist/admin/layout-editor/component-picker-field.js.map +1 -1
  32. package/dist/admin/layout-editor/drag-feedback.d.ts +25 -1
  33. package/dist/admin/layout-editor/drag-feedback.d.ts.map +1 -1
  34. package/dist/admin/layout-editor/drag-feedback.js +9 -0
  35. package/dist/admin/layout-editor/drag-feedback.js.map +1 -1
  36. package/dist/admin/layout-editor/drag-utils.d.ts +103 -5
  37. package/dist/admin/layout-editor/drag-utils.d.ts.map +1 -1
  38. package/dist/admin/layout-editor/drag-utils.js +131 -4
  39. package/dist/admin/layout-editor/drag-utils.js.map +1 -1
  40. package/dist/admin/layout-editor/editor-canvas.d.ts +11 -0
  41. package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
  42. package/dist/admin/layout-editor/editor-canvas.js +134 -20
  43. package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
  44. package/dist/admin/layout-editor/editor-dialog.d.ts +58 -3
  45. package/dist/admin/layout-editor/editor-dialog.d.ts.map +1 -1
  46. package/dist/admin/layout-editor/editor-dialog.js +71 -9
  47. package/dist/admin/layout-editor/editor-dialog.js.map +1 -1
  48. package/dist/admin/layout-editor/editor-state.d.ts +119 -0
  49. package/dist/admin/layout-editor/editor-state.d.ts.map +1 -1
  50. package/dist/admin/layout-editor/editor-state.js +188 -6
  51. package/dist/admin/layout-editor/editor-state.js.map +1 -1
  52. package/dist/admin/layout-editor/editor-toolbar.d.ts +10 -1
  53. package/dist/admin/layout-editor/editor-toolbar.d.ts.map +1 -1
  54. package/dist/admin/layout-editor/editor-toolbar.js +2 -2
  55. package/dist/admin/layout-editor/editor-toolbar.js.map +1 -1
  56. package/dist/admin/layout-editor/editor.css +216 -8
  57. package/dist/admin/layout-editor/inline-text.d.ts +15 -1
  58. package/dist/admin/layout-editor/inline-text.d.ts.map +1 -1
  59. package/dist/admin/layout-editor/inline-text.js +30 -2
  60. package/dist/admin/layout-editor/inline-text.js.map +1 -1
  61. package/dist/admin/layout-editor/layout-editor.d.ts +21 -16
  62. package/dist/admin/layout-editor/layout-editor.d.ts.map +1 -1
  63. package/dist/admin/layout-editor/layout-editor.js +40 -17
  64. package/dist/admin/layout-editor/layout-editor.js.map +1 -1
  65. package/dist/admin/layout-editor/linked-component-notes.d.ts +88 -0
  66. package/dist/admin/layout-editor/linked-component-notes.d.ts.map +1 -0
  67. package/dist/admin/layout-editor/linked-component-notes.js +88 -0
  68. package/dist/admin/layout-editor/linked-component-notes.js.map +1 -0
  69. package/dist/admin/layout-editor/module-overlay.d.ts +23 -4
  70. package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
  71. package/dist/admin/layout-editor/module-overlay.js +56 -10
  72. package/dist/admin/layout-editor/module-overlay.js.map +1 -1
  73. package/dist/admin/layout-editor/module-variant-container-config.d.ts.map +1 -1
  74. package/dist/admin/layout-editor/module-variant-container-config.js +9 -2
  75. package/dist/admin/layout-editor/module-variant-container-config.js.map +1 -1
  76. package/dist/admin/layout-editor/module-variant-container-panel.d.ts +75 -0
  77. package/dist/admin/layout-editor/module-variant-container-panel.d.ts.map +1 -0
  78. package/dist/admin/layout-editor/module-variant-container-panel.js +29 -0
  79. package/dist/admin/layout-editor/module-variant-container-panel.js.map +1 -0
  80. package/dist/admin/layout-editor/node-lookup.d.ts.map +1 -1
  81. package/dist/admin/layout-editor/node-lookup.js +33 -3
  82. package/dist/admin/layout-editor/node-lookup.js.map +1 -1
  83. package/dist/admin/layout-editor/overlay-shell.d.ts.map +1 -1
  84. package/dist/admin/layout-editor/overlay-shell.js +19 -4
  85. package/dist/admin/layout-editor/overlay-shell.js.map +1 -1
  86. package/dist/admin/layout-editor/props-panel.d.ts +28 -11
  87. package/dist/admin/layout-editor/props-panel.d.ts.map +1 -1
  88. package/dist/admin/layout-editor/props-panel.js +75 -125
  89. package/dist/admin/layout-editor/props-panel.js.map +1 -1
  90. package/dist/admin/layout-editor/section-variant-container-config.d.ts.map +1 -1
  91. package/dist/admin/layout-editor/section-variant-container-config.js +11 -2
  92. package/dist/admin/layout-editor/section-variant-container-config.js.map +1 -1
  93. package/dist/admin/layout-editor/use-chrome-flip.d.ts +99 -11
  94. package/dist/admin/layout-editor/use-chrome-flip.d.ts.map +1 -1
  95. package/dist/admin/layout-editor/use-chrome-flip.js +268 -97
  96. package/dist/admin/layout-editor/use-chrome-flip.js.map +1 -1
  97. package/dist/admin/layout-editor/use-component-connect.d.ts +13 -0
  98. package/dist/admin/layout-editor/use-component-connect.d.ts.map +1 -1
  99. package/dist/admin/layout-editor/use-component-connect.js +116 -20
  100. package/dist/admin/layout-editor/use-component-connect.js.map +1 -1
  101. package/dist/admin/layout-editor/use-component-fork.d.ts +49 -19
  102. package/dist/admin/layout-editor/use-component-fork.d.ts.map +1 -1
  103. package/dist/admin/layout-editor/use-component-fork.js +28 -19
  104. package/dist/admin/layout-editor/use-component-fork.js.map +1 -1
  105. package/dist/admin/layout-editor/use-component-library.d.ts +4 -4
  106. package/dist/admin/layout-editor/use-component-library.d.ts.map +1 -1
  107. package/dist/admin/layout-editor/use-component-library.js +1 -1
  108. package/dist/admin/layout-editor/use-component-library.js.map +1 -1
  109. package/dist/admin/layout-editor/use-editor-dnd.d.ts +10 -0
  110. package/dist/admin/layout-editor/use-editor-dnd.d.ts.map +1 -1
  111. package/dist/admin/layout-editor/use-editor-dnd.js +234 -14
  112. package/dist/admin/layout-editor/use-editor-dnd.js.map +1 -1
  113. package/dist/admin/layout-editor/variant-container-config.d.ts.map +1 -1
  114. package/dist/admin/layout-editor/variant-container-config.js +1 -1
  115. package/dist/admin/layout-editor/variant-container-config.js.map +1 -1
  116. package/dist/admin/layout-editor/variant-editor.d.ts +16 -15
  117. package/dist/admin/layout-editor/variant-editor.d.ts.map +1 -1
  118. package/dist/admin/layout-editor/variant-editor.js +16 -5
  119. package/dist/admin/layout-editor/variant-editor.js.map +1 -1
  120. package/dist/admin/primitives.d.ts.map +1 -1
  121. package/dist/admin/primitives.js +1 -1
  122. package/dist/admin/primitives.js.map +1 -1
  123. package/dist/admin/registry.d.ts +24 -2
  124. package/dist/admin/registry.d.ts.map +1 -1
  125. package/dist/admin/registry.js +27 -0
  126. package/dist/admin/registry.js.map +1 -1
  127. package/dist/admin/server.d.ts +10 -1
  128. package/dist/admin/server.d.ts.map +1 -1
  129. package/dist/admin/server.js +4 -1
  130. package/dist/admin/server.js.map +1 -1
  131. package/dist/admin/site-switcher.d.ts +16 -3
  132. package/dist/admin/site-switcher.d.ts.map +1 -1
  133. package/dist/admin/site-switcher.js +18 -3
  134. package/dist/admin/site-switcher.js.map +1 -1
  135. package/dist/admin/styles/admin.css +0 -3
  136. package/dist/admin/surface-matcher.d.ts +6 -2
  137. package/dist/admin/surface-matcher.d.ts.map +1 -1
  138. package/dist/admin/surface-matcher.js +42 -5
  139. package/dist/admin/surface-matcher.js.map +1 -1
  140. package/dist/admin/surfaces/account/account-password.d.ts +3 -0
  141. package/dist/admin/surfaces/account/account-password.d.ts.map +1 -0
  142. package/dist/admin/surfaces/account/account-password.js +58 -0
  143. package/dist/admin/surfaces/account/account-password.js.map +1 -0
  144. package/dist/admin/surfaces/components/components-data.d.ts +31 -11
  145. package/dist/admin/surfaces/components/components-data.d.ts.map +1 -1
  146. package/dist/admin/surfaces/components/components-data.js +17 -4
  147. package/dist/admin/surfaces/components/components-data.js.map +1 -1
  148. package/dist/admin/surfaces/components/components-list.d.ts.map +1 -1
  149. package/dist/admin/surfaces/components/components-list.js +20 -7
  150. package/dist/admin/surfaces/components/components-list.js.map +1 -1
  151. package/dist/admin/surfaces/components/definition-canvas.d.ts +11 -3
  152. package/dist/admin/surfaces/components/definition-canvas.d.ts.map +1 -1
  153. package/dist/admin/surfaces/components/definition-canvas.js +138 -17
  154. package/dist/admin/surfaces/components/definition-canvas.js.map +1 -1
  155. package/dist/admin/surfaces/dashboard/dashboard-index.d.ts.map +1 -1
  156. package/dist/admin/surfaces/dashboard/dashboard-index.js +2 -2
  157. package/dist/admin/surfaces/dashboard/dashboard-index.js.map +1 -1
  158. package/dist/admin/surfaces/layouts/layout-editor-client.d.ts.map +1 -1
  159. package/dist/admin/surfaces/layouts/layout-editor-client.js +12 -11
  160. package/dist/admin/surfaces/layouts/layout-editor-client.js.map +1 -1
  161. package/dist/admin/surfaces/login.d.ts.map +1 -1
  162. package/dist/admin/surfaces/login.js +13 -1
  163. package/dist/admin/surfaces/login.js.map +1 -1
  164. package/dist/admin/surfaces/menus/menu-editor.d.ts.map +1 -1
  165. package/dist/admin/surfaces/menus/menu-editor.js +17 -4
  166. package/dist/admin/surfaces/menus/menu-editor.js.map +1 -1
  167. package/dist/admin/surfaces/menus/menu-tree.d.ts +18 -4
  168. package/dist/admin/surfaces/menus/menu-tree.d.ts.map +1 -1
  169. package/dist/admin/surfaces/menus/menu-tree.js +18 -3
  170. package/dist/admin/surfaces/menus/menu-tree.js.map +1 -1
  171. package/dist/admin/surfaces/menus/menus-data.d.ts +18 -0
  172. package/dist/admin/surfaces/menus/menus-data.d.ts.map +1 -1
  173. package/dist/admin/surfaces/menus/menus-data.js +4 -0
  174. package/dist/admin/surfaces/menus/menus-data.js.map +1 -1
  175. package/dist/admin/surfaces/menus/menus-list.d.ts.map +1 -1
  176. package/dist/admin/surfaces/menus/menus-list.js +2 -12
  177. package/dist/admin/surfaces/menus/menus-list.js.map +1 -1
  178. package/dist/admin/surfaces/settings/admin-features.d.ts.map +1 -1
  179. package/dist/admin/surfaces/settings/admin-features.js +12 -1
  180. package/dist/admin/surfaces/settings/admin-features.js.map +1 -1
  181. package/dist/admin/surfaces/users/users-data.d.ts +16 -0
  182. package/dist/admin/surfaces/users/users-data.d.ts.map +1 -1
  183. package/dist/admin/surfaces/users/users-data.js +19 -0
  184. package/dist/admin/surfaces/users/users-data.js.map +1 -1
  185. package/dist/admin/surfaces/users/users-detail.d.ts.map +1 -1
  186. package/dist/admin/surfaces/users/users-detail.js +87 -6
  187. package/dist/admin/surfaces/users/users-detail.js.map +1 -1
  188. package/dist/admin/surfaces/users/users-new.d.ts.map +1 -1
  189. package/dist/admin/surfaces/users/users-new.js +9 -5
  190. package/dist/admin/surfaces/users/users-new.js.map +1 -1
  191. package/dist/admin/user-form.d.ts +13 -2
  192. package/dist/admin/user-form.d.ts.map +1 -1
  193. package/dist/admin/user-form.js +3 -2
  194. package/dist/admin/user-form.js.map +1 -1
  195. package/dist/api/admin-features-routes.js +2 -2
  196. package/dist/api/admin-features-routes.js.map +1 -1
  197. package/dist/api/audience-preview-routes.d.ts +7 -0
  198. package/dist/api/audience-preview-routes.d.ts.map +1 -1
  199. package/dist/api/audience-preview-routes.js +4 -4
  200. package/dist/api/audience-preview-routes.js.map +1 -1
  201. package/dist/api/audience-routes.d.ts +7 -0
  202. package/dist/api/audience-routes.d.ts.map +1 -1
  203. package/dist/api/audience-routes.js +21 -15
  204. package/dist/api/audience-routes.js.map +1 -1
  205. package/dist/api/audience-runtime-routes.d.ts +6 -0
  206. package/dist/api/audience-runtime-routes.d.ts.map +1 -1
  207. package/dist/api/audience-runtime-routes.js +38 -3
  208. package/dist/api/audience-runtime-routes.js.map +1 -1
  209. package/dist/api/auth-routes.d.ts.map +1 -1
  210. package/dist/api/auth-routes.js +33 -4
  211. package/dist/api/auth-routes.js.map +1 -1
  212. package/dist/api/auth.d.ts +1 -0
  213. package/dist/api/auth.d.ts.map +1 -1
  214. package/dist/api/auth.js +43 -3
  215. package/dist/api/auth.js.map +1 -1
  216. package/dist/api/bulk-routes.d.ts.map +1 -1
  217. package/dist/api/bulk-routes.js +7 -7
  218. package/dist/api/bulk-routes.js.map +1 -1
  219. package/dist/api/category-routes.js +3 -3
  220. package/dist/api/category-routes.js.map +1 -1
  221. package/dist/api/comment-routes.js +8 -8
  222. package/dist/api/comment-routes.js.map +1 -1
  223. package/dist/api/component-routes.d.ts +25 -0
  224. package/dist/api/component-routes.d.ts.map +1 -1
  225. package/dist/api/component-routes.js +41 -16
  226. package/dist/api/component-routes.js.map +1 -1
  227. package/dist/api/content-routes.js +13 -13
  228. package/dist/api/content-routes.js.map +1 -1
  229. package/dist/api/dashboard-routes.js +1 -1
  230. package/dist/api/dashboard-routes.js.map +1 -1
  231. package/dist/api/experiment-routes.d.ts +7 -0
  232. package/dist/api/experiment-routes.d.ts.map +1 -1
  233. package/dist/api/experiment-routes.js +8 -8
  234. package/dist/api/experiment-routes.js.map +1 -1
  235. package/dist/api/experiment-runtime-routes.d.ts +6 -0
  236. package/dist/api/experiment-runtime-routes.d.ts.map +1 -1
  237. package/dist/api/experiment-runtime-routes.js +47 -11
  238. package/dist/api/experiment-runtime-routes.js.map +1 -1
  239. package/dist/api/feed-routes.d.ts +2 -2
  240. package/dist/api/feed-routes.js +3 -3
  241. package/dist/api/feed-routes.js.map +1 -1
  242. package/dist/api/form-routes.js +4 -4
  243. package/dist/api/form-routes.js.map +1 -1
  244. package/dist/api/graphql/resolvers.d.ts +1 -1
  245. package/dist/api/graphql/resolvers.d.ts.map +1 -1
  246. package/dist/api/graphql/resolvers.js +68 -33
  247. package/dist/api/graphql/resolvers.js.map +1 -1
  248. package/dist/api/group-routes.js +6 -6
  249. package/dist/api/group-routes.js.map +1 -1
  250. package/dist/api/index.d.ts +10 -0
  251. package/dist/api/index.d.ts.map +1 -1
  252. package/dist/api/index.js +97 -7
  253. package/dist/api/index.js.map +1 -1
  254. package/dist/api/layout-routes.d.ts.map +1 -1
  255. package/dist/api/layout-routes.js +27 -3
  256. package/dist/api/layout-routes.js.map +1 -1
  257. package/dist/api/media-routes.d.ts.map +1 -1
  258. package/dist/api/media-routes.js +50 -26
  259. package/dist/api/media-routes.js.map +1 -1
  260. package/dist/api/menu-routes.d.ts +9 -1
  261. package/dist/api/menu-routes.d.ts.map +1 -1
  262. package/dist/api/menu-routes.js +44 -10
  263. package/dist/api/menu-routes.js.map +1 -1
  264. package/dist/api/nav-routes.d.ts +6 -0
  265. package/dist/api/nav-routes.d.ts.map +1 -1
  266. package/dist/api/nav-routes.js +24 -3
  267. package/dist/api/nav-routes.js.map +1 -1
  268. package/dist/api/openapi.d.ts.map +1 -1
  269. package/dist/api/openapi.js +68 -33
  270. package/dist/api/openapi.js.map +1 -1
  271. package/dist/api/password-gate.d.ts +23 -0
  272. package/dist/api/password-gate.d.ts.map +1 -0
  273. package/dist/api/password-gate.js +41 -0
  274. package/dist/api/password-gate.js.map +1 -0
  275. package/dist/api/password-reset-routes.d.ts.map +1 -1
  276. package/dist/api/password-reset-routes.js +13 -6
  277. package/dist/api/password-reset-routes.js.map +1 -1
  278. package/dist/api/search-routes.js +2 -2
  279. package/dist/api/search-routes.js.map +1 -1
  280. package/dist/api/section-template-routes.d.ts.map +1 -1
  281. package/dist/api/section-template-routes.js +35 -18
  282. package/dist/api/section-template-routes.js.map +1 -1
  283. package/dist/api/site-admin-routes.d.ts +10 -1
  284. package/dist/api/site-admin-routes.d.ts.map +1 -1
  285. package/dist/api/site-admin-routes.js +21 -1
  286. package/dist/api/site-admin-routes.js.map +1 -1
  287. package/dist/api/site-context.d.ts +35 -5
  288. package/dist/api/site-context.d.ts.map +1 -1
  289. package/dist/api/site-context.js +21 -6
  290. package/dist/api/site-context.js.map +1 -1
  291. package/dist/api/tag-routes.js +3 -3
  292. package/dist/api/tag-routes.js.map +1 -1
  293. package/dist/api/translation-routes.js +5 -5
  294. package/dist/api/translation-routes.js.map +1 -1
  295. package/dist/api/user-routes.d.ts +4 -0
  296. package/dist/api/user-routes.d.ts.map +1 -1
  297. package/dist/api/user-routes.js +240 -17
  298. package/dist/api/user-routes.js.map +1 -1
  299. package/dist/api/webhook-routes.js +5 -5
  300. package/dist/api/webhook-routes.js.map +1 -1
  301. package/dist/api/workflow-routes.js +8 -8
  302. package/dist/api/workflow-routes.js.map +1 -1
  303. package/dist/audiences/collectors/identity.d.ts +14 -1
  304. package/dist/audiences/collectors/identity.d.ts.map +1 -1
  305. package/dist/audiences/collectors/identity.js +16 -3
  306. package/dist/audiences/collectors/identity.js.map +1 -1
  307. package/dist/audiences/collectors/index.d.ts +12 -2
  308. package/dist/audiences/collectors/index.d.ts.map +1 -1
  309. package/dist/audiences/collectors/index.js +16 -5
  310. package/dist/audiences/collectors/index.js.map +1 -1
  311. package/dist/audiences/collectors/pinned-audience.d.ts +28 -2
  312. package/dist/audiences/collectors/pinned-audience.d.ts.map +1 -1
  313. package/dist/audiences/collectors/pinned-audience.js +35 -5
  314. package/dist/audiences/collectors/pinned-audience.js.map +1 -1
  315. package/dist/audiences/collectors/utm.d.ts +15 -1
  316. package/dist/audiences/collectors/utm.d.ts.map +1 -1
  317. package/dist/audiences/collectors/utm.js +16 -2
  318. package/dist/audiences/collectors/utm.js.map +1 -1
  319. package/dist/audiences/consent-config.d.ts +21 -0
  320. package/dist/audiences/consent-config.d.ts.map +1 -0
  321. package/dist/audiences/consent-config.js +15 -0
  322. package/dist/audiences/consent-config.js.map +1 -0
  323. package/dist/audiences/consent-startup.d.ts +39 -0
  324. package/dist/audiences/consent-startup.d.ts.map +1 -0
  325. package/dist/audiences/consent-startup.js +105 -0
  326. package/dist/audiences/consent-startup.js.map +1 -0
  327. package/dist/audiences/consent.d.ts +192 -0
  328. package/dist/audiences/consent.d.ts.map +1 -0
  329. package/dist/audiences/consent.js +322 -0
  330. package/dist/audiences/consent.js.map +1 -0
  331. package/dist/audiences/engine.d.ts +7 -0
  332. package/dist/audiences/engine.d.ts.map +1 -1
  333. package/dist/audiences/engine.js +6 -2
  334. package/dist/audiences/engine.js.map +1 -1
  335. package/dist/audiences/index.d.ts +3 -0
  336. package/dist/audiences/index.d.ts.map +1 -1
  337. package/dist/audiences/index.js +6 -0
  338. package/dist/audiences/index.js.map +1 -1
  339. package/dist/audiences/server-eval.d.ts +13 -1
  340. package/dist/audiences/server-eval.d.ts.map +1 -1
  341. package/dist/audiences/server-eval.js +18 -2
  342. package/dist/audiences/server-eval.js.map +1 -1
  343. package/dist/audiences/ssr.d.ts +18 -1
  344. package/dist/audiences/ssr.d.ts.map +1 -1
  345. package/dist/audiences/ssr.js +18 -1
  346. package/dist/audiences/ssr.js.map +1 -1
  347. package/dist/audiences/utm-persistence.d.ts +20 -7
  348. package/dist/audiences/utm-persistence.d.ts.map +1 -1
  349. package/dist/audiences/utm-persistence.js +20 -7
  350. package/dist/audiences/utm-persistence.js.map +1 -1
  351. package/dist/auth/roles.d.ts +11 -0
  352. package/dist/auth/roles.d.ts.map +1 -1
  353. package/dist/auth/roles.js +13 -0
  354. package/dist/auth/roles.js.map +1 -1
  355. package/dist/auth/tokens.d.ts +7 -0
  356. package/dist/auth/tokens.d.ts.map +1 -1
  357. package/dist/auth/tokens.js +24 -2
  358. package/dist/auth/tokens.js.map +1 -1
  359. package/dist/config/types.d.ts +50 -0
  360. package/dist/config/types.d.ts.map +1 -1
  361. package/dist/config/types.js.map +1 -1
  362. package/dist/config/validate.d.ts.map +1 -1
  363. package/dist/config/validate.js +3 -0
  364. package/dist/config/validate.js.map +1 -1
  365. package/dist/db/apply-snapshot.d.ts +18 -0
  366. package/dist/db/apply-snapshot.d.ts.map +1 -1
  367. package/dist/db/apply-snapshot.js +41 -16
  368. package/dist/db/apply-snapshot.js.map +1 -1
  369. package/dist/db/component-instance-queries.d.ts +79 -19
  370. package/dist/db/component-instance-queries.d.ts.map +1 -1
  371. package/dist/db/component-instance-queries.js +266 -83
  372. package/dist/db/component-instance-queries.js.map +1 -1
  373. package/dist/db/component-queries.d.ts +1 -16
  374. package/dist/db/component-queries.d.ts.map +1 -1
  375. package/dist/db/component-queries.js +7 -56
  376. package/dist/db/component-queries.js.map +1 -1
  377. package/dist/db/content-queries.d.ts.map +1 -1
  378. package/dist/db/content-queries.js +1 -2
  379. package/dist/db/content-queries.js.map +1 -1
  380. package/dist/db/extension-queries.d.ts +2 -2
  381. package/dist/db/feed-queries.d.ts +3 -3
  382. package/dist/db/index.d.ts +1 -0
  383. package/dist/db/index.d.ts.map +1 -1
  384. package/dist/db/index.js +1 -0
  385. package/dist/db/index.js.map +1 -1
  386. package/dist/db/layout-queries.d.ts +2 -1
  387. package/dist/db/layout-queries.d.ts.map +1 -1
  388. package/dist/db/layout-queries.js +3 -3
  389. package/dist/db/layout-queries.js.map +1 -1
  390. package/dist/db/menu-queries.d.ts +69 -5
  391. package/dist/db/menu-queries.d.ts.map +1 -1
  392. package/dist/db/menu-queries.js +120 -13
  393. package/dist/db/menu-queries.js.map +1 -1
  394. package/dist/db/section-template-queries.d.ts +62 -0
  395. package/dist/db/section-template-queries.d.ts.map +1 -0
  396. package/dist/db/section-template-queries.js +178 -0
  397. package/dist/db/section-template-queries.js.map +1 -0
  398. package/dist/db/site-plugin-queries.d.ts +2 -2
  399. package/dist/db/tree-queries.d.ts.map +1 -1
  400. package/dist/db/tree-queries.js +5 -0
  401. package/dist/db/tree-queries.js.map +1 -1
  402. package/dist/db/user-queries.d.ts +28 -1
  403. package/dist/db/user-queries.d.ts.map +1 -1
  404. package/dist/db/user-queries.js +49 -1
  405. package/dist/db/user-queries.js.map +1 -1
  406. package/dist/db/webhook-queries.d.ts +3 -3
  407. package/dist/db/workflow-queries.d.ts.map +1 -1
  408. package/dist/db/workflow-queries.js +5 -20
  409. package/dist/db/workflow-queries.js.map +1 -1
  410. package/dist/experiments/runtime.d.ts +21 -3
  411. package/dist/experiments/runtime.d.ts.map +1 -1
  412. package/dist/experiments/runtime.js +21 -3
  413. package/dist/experiments/runtime.js.map +1 -1
  414. package/dist/host/index.d.ts +1 -0
  415. package/dist/host/index.d.ts.map +1 -1
  416. package/dist/host/index.js +12 -0
  417. package/dist/host/index.js.map +1 -1
  418. package/dist/layout/component-refs.d.ts +90 -2
  419. package/dist/layout/component-refs.d.ts.map +1 -1
  420. package/dist/layout/component-refs.js +293 -16
  421. package/dist/layout/component-refs.js.map +1 -1
  422. package/dist/layout/index.d.ts +4 -3
  423. package/dist/layout/index.d.ts.map +1 -1
  424. package/dist/layout/index.js +3 -2
  425. package/dist/layout/index.js.map +1 -1
  426. package/dist/layout/legacy-variant-container.d.ts +123 -0
  427. package/dist/layout/legacy-variant-container.d.ts.map +1 -0
  428. package/dist/layout/legacy-variant-container.js +397 -0
  429. package/dist/layout/legacy-variant-container.js.map +1 -0
  430. package/dist/layout/module-permissions.d.ts.map +1 -1
  431. package/dist/layout/module-permissions.js +25 -1
  432. package/dist/layout/module-permissions.js.map +1 -1
  433. package/dist/layout/modules-extended.d.ts +28 -1
  434. package/dist/layout/modules-extended.d.ts.map +1 -1
  435. package/dist/layout/modules-extended.js +73 -1
  436. package/dist/layout/modules-extended.js.map +1 -1
  437. package/dist/layout/modules.d.ts.map +1 -1
  438. package/dist/layout/modules.js +9 -1
  439. package/dist/layout/modules.js.map +1 -1
  440. package/dist/layout/personalization.d.ts +83 -0
  441. package/dist/layout/personalization.d.ts.map +1 -1
  442. package/dist/layout/personalization.js +250 -4
  443. package/dist/layout/personalization.js.map +1 -1
  444. package/dist/layout/registry.d.ts.map +1 -1
  445. package/dist/layout/registry.js +20 -0
  446. package/dist/layout/registry.js.map +1 -1
  447. package/dist/layout/renderer.d.ts +29 -2
  448. package/dist/layout/renderer.d.ts.map +1 -1
  449. package/dist/layout/renderer.js +23 -6
  450. package/dist/layout/renderer.js.map +1 -1
  451. package/dist/layout/types.d.ts +17 -0
  452. package/dist/layout/types.d.ts.map +1 -1
  453. package/dist/layout/types.js +7 -0
  454. package/dist/layout/types.js.map +1 -1
  455. package/dist/media/inline-risk.d.ts +26 -0
  456. package/dist/media/inline-risk.d.ts.map +1 -0
  457. package/dist/media/inline-risk.js +142 -0
  458. package/dist/media/inline-risk.js.map +1 -0
  459. package/dist/nav/builder.d.ts.map +1 -1
  460. package/dist/nav/builder.js +15 -3
  461. package/dist/nav/builder.js.map +1 -1
  462. package/dist/nav/index.d.ts +1 -1
  463. package/dist/nav/index.d.ts.map +1 -1
  464. package/dist/nav/index.js +1 -1
  465. package/dist/nav/index.js.map +1 -1
  466. package/dist/nav/menu-resolver.d.ts +11 -0
  467. package/dist/nav/menu-resolver.d.ts.map +1 -1
  468. package/dist/nav/menu-resolver.js +49 -9
  469. package/dist/nav/menu-resolver.js.map +1 -1
  470. package/dist/nav/paths.d.ts +37 -0
  471. package/dist/nav/paths.d.ts.map +1 -0
  472. package/dist/nav/paths.js +48 -0
  473. package/dist/nav/paths.js.map +1 -0
  474. package/dist/nav/types.d.ts +6 -0
  475. package/dist/nav/types.d.ts.map +1 -1
  476. package/dist/nav/url-resolver.d.ts +1 -5
  477. package/dist/nav/url-resolver.d.ts.map +1 -1
  478. package/dist/nav/url-resolver.js +6 -12
  479. package/dist/nav/url-resolver.js.map +1 -1
  480. package/dist/schema/base.d.ts +41 -4
  481. package/dist/schema/base.d.ts.map +1 -1
  482. package/dist/schema/base.js +23 -4
  483. package/dist/schema/base.js.map +1 -1
  484. package/dist/schema/components.d.ts +90 -9
  485. package/dist/schema/components.d.ts.map +1 -1
  486. package/dist/schema/components.js +113 -22
  487. package/dist/schema/components.js.map +1 -1
  488. package/dist/schema/mysql-tables.d.ts +5775 -48
  489. package/dist/schema/mysql-tables.d.ts.map +1 -1
  490. package/dist/schema/mysql-tables.js +621 -46
  491. package/dist/schema/mysql-tables.js.map +1 -1
  492. package/dist/scope/kywi-form.d.ts +11 -0
  493. package/dist/scope/kywi-form.d.ts.map +1 -1
  494. package/dist/scope/kywi-form.js +11 -1
  495. package/dist/scope/kywi-form.js.map +1 -1
  496. package/dist/scope/kywi-wizard-form.d.ts.map +1 -1
  497. package/dist/scope/kywi-wizard-form.js +76 -2
  498. package/dist/scope/kywi-wizard-form.js.map +1 -1
  499. package/dist/scope/sticky-offset.d.ts +34 -13
  500. package/dist/scope/sticky-offset.d.ts.map +1 -1
  501. package/dist/scope/sticky-offset.js +236 -43
  502. package/dist/scope/sticky-offset.js.map +1 -1
  503. package/dist/site/styles.css +61 -15
  504. package/dist/versioning/scheduler.d.ts +28 -0
  505. package/dist/versioning/scheduler.d.ts.map +1 -1
  506. package/dist/versioning/scheduler.js +50 -3
  507. package/dist/versioning/scheduler.js.map +1 -1
  508. package/package.json +7 -2
@@ -1,5 +1,33 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { mysqlTable, varchar, text, int, boolean, datetime, json, decimal, unique, } from 'drizzle-orm/mysql-core';
2
+ import { sql } from 'drizzle-orm';
3
+ import { mysqlTable, varchar, text, int, boolean, datetime, json, decimal, index, primaryKey, unique, check, } from 'drizzle-orm/mysql-core';
4
+ /**
5
+ * MySQL twins of every Postgres table in this package (kywi-cms#75).
6
+ *
7
+ * MySQL is a supported deployment target, not a Postgres subset: every
8
+ * `pgTable` exported from `schema/` (plus `audiences/` and `experiments/`) has
9
+ * a twin here with the same table name, columns, nullability and defaults.
10
+ * `schema/__tests__/mysql-parity.test.ts` enforces that programmatically —
11
+ * add a pgTable without a twin and it fails.
12
+ *
13
+ * Where a Postgres construct has no MySQL equivalent, the twin says so in a
14
+ * comment and takes the closest expressible shape. The recurring cases:
15
+ *
16
+ * - **`uuid` → `varchar(36)`.** MySQL has no uuid type.
17
+ * - **`timestamptz` → `datetime(3)`.** MySQL's `timestamp` is 2038-bounded and
18
+ * auto-updating; `datetime` with millisecond precision is the faithful twin.
19
+ * `defaultNow()` becomes `DEFAULT CURRENT_TIMESTAMP(3)` ({@link mysqlNow}).
20
+ * - **`jsonb` → `json`.** No jsonb operator classes, so no GIN-equivalent
21
+ * indexing, and a JSON column cannot take a *literal* DEFAULT — see
22
+ * {@link mysqlJsonDefault}.
23
+ * - **`text` → `varchar(n)` when the column is keyed, indexed or defaulted.**
24
+ * MySQL cannot use TEXT as a primary key, cannot index it without a prefix
25
+ * length, and cannot give it a DEFAULT. Every such conversion is marked.
26
+ * - **Partial (`WHERE …`) unique indexes have no MySQL equivalent.** The one
27
+ * place Kywi uses them — `kywi_content_permissions` — falls back to plain
28
+ * lookup indexes plus the app-level dedupe already in the migrate path; see
29
+ * the comment on {@link kywiContentPermissionsMysql} and docs/hosting.md.
30
+ */
3
31
  // ── Helper: UUID primary key for MySQL ──────────────────────────────────────
4
32
  function mysqlUuidPk() {
5
33
  return varchar({ length: 36 }).primaryKey().$defaultFn(() => randomUUID());
@@ -10,16 +38,42 @@ function mysqlUuid(name) {
10
38
  function mysqlTs(name) {
11
39
  return datetime(name, { fsp: 3 });
12
40
  }
41
+ /**
42
+ * The MySQL twin of Postgres `defaultNow()`.
43
+ *
44
+ * drizzle's mysql-core exposes `.defaultNow()` only on `timestamp`, and these
45
+ * twins deliberately use `datetime` (see the module doc). `DEFAULT
46
+ * CURRENT_TIMESTAMP(3)` is what `defaultNow()` compiles to for a `datetime(3)`
47
+ * column, and MySQL has accepted it on DATETIME since 5.6.5.
48
+ *
49
+ * Returns a fresh `SQL` each call so no two columns share one node.
50
+ */
51
+ function mysqlNow() {
52
+ return sql `CURRENT_TIMESTAMP(3)`;
53
+ }
54
+ /**
55
+ * The MySQL twin of a Postgres `jsonb(...).default([])`.
56
+ *
57
+ * MySQL rejects a literal DEFAULT on a JSON column outright; since 8.0.13 it
58
+ * accepts a *parenthesised expression* default instead, which is what this
59
+ * emits. On MySQL 8.0.12 and older there is no way to express the default at
60
+ * all and the column has to be written explicitly by the caller — the query
61
+ * layer already does, so the practical effect is limited to hand-written
62
+ * inserts. Documented in docs/hosting.md.
63
+ */
64
+ function mysqlJsonDefault(literal) {
65
+ return sql.raw(`('${literal.replace(/'/g, "''")}')`);
66
+ }
13
67
  // ── kywiSitesMysql ──────────────────────────────────────────────────────────
14
68
  export const kywiSitesMysql = mysqlTable('kywi_sites', {
15
69
  id: mysqlUuidPk(),
16
- slug: varchar('slug', { length: 100 }).notNull(),
70
+ slug: varchar('slug', { length: 100 }).notNull().unique(),
17
71
  name: text('name').notNull(),
18
72
  domain: text('domain').notNull(),
19
73
  defaultLocale: varchar('default_locale', { length: 10 }).notNull().default('en'),
20
74
  theme: varchar('theme', { length: 100 }).notNull().default('default'),
21
- createdAt: mysqlTs('created_at').notNull(),
22
- updatedAt: mysqlTs('updated_at').notNull(),
75
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
76
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
23
77
  });
24
78
  // ── kywiContentMysql ────────────────────────────────────────────────────────
25
79
  export const kywiContentMysql = mysqlTable('kywi_content', {
@@ -27,7 +81,9 @@ export const kywiContentMysql = mysqlTable('kywi_content', {
27
81
  id: mysqlUuidPk(),
28
82
  siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
29
83
  parentId: mysqlUuid('parent_id'),
30
- path: text('path').notNull(),
84
+ // pg: `text NOT NULL DEFAULT '/'`. MySQL cannot give a TEXT column a
85
+ // DEFAULT, so the twin narrows to varchar to keep the default.
86
+ path: varchar('path', { length: 1024 }).notNull().default('/'),
31
87
  depth: int('depth').notNull().default(0),
32
88
  sortOrder: int('sort_order').notNull().default(0),
33
89
  title: text('title').notNull(),
@@ -37,8 +93,8 @@ export const kywiContentMysql = mysqlTable('kywi_content', {
37
93
  baseType: varchar('base_type', { length: 50 }).notNull(),
38
94
  contentTypeName: varchar('content_type_name', { length: 100 }).notNull(),
39
95
  publishedAt: mysqlTs('published_at'),
40
- createdAt: mysqlTs('created_at').notNull(),
41
- updatedAt: mysqlTs('updated_at').notNull(),
96
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
97
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
42
98
  createdBy: mysqlUuid('created_by'),
43
99
  updatedBy: mysqlUuid('updated_by'),
44
100
  metaTitle: text('meta_title'),
@@ -56,7 +112,9 @@ export const kywiContentMysql = mysqlTable('kywi_content', {
56
112
  robotsFollow: varchar('robots_follow', { length: 10 }),
57
113
  deletedAt: mysqlTs('deleted_at'),
58
114
  featuredImageId: mysqlUuid('featured_image_id'),
59
- featureInSection: text('feature_in_section'),
115
+ // pg: `text DEFAULT 'no'` — 'no' | 'yes' | ISO date string. Narrowed to
116
+ // varchar so MySQL can carry the default (an ISO-8601 stamp is 29 chars).
117
+ featureInSection: varchar('feature_in_section', { length: 64 }).default('no'),
60
118
  expiresAt: mysqlTs('expires_at'),
61
119
  excludeFromSearch: boolean('exclude_from_search').notNull().default(false),
62
120
  internalNotes: text('internal_notes'),
@@ -105,15 +163,18 @@ export const kywiContentMysql = mysqlTable('kywi_content', {
105
163
  // ── kywiUsersMysql ──────────────────────────────────────────────────────────
106
164
  export const kywiUsersMysql = mysqlTable('kywi_users', {
107
165
  id: mysqlUuidPk(),
108
- email: text('email').notNull(),
166
+ // pg: `text NOT NULL UNIQUE`. MySQL cannot put a UNIQUE index on TEXT
167
+ // without a prefix length; 320 is the RFC 5321 maximum address length.
168
+ email: varchar('email', { length: 320 }).notNull().unique(),
109
169
  passwordHash: text('password_hash').notNull(),
110
- firstName: text('first_name').notNull(),
111
- lastName: text('last_name').notNull(),
170
+ // pg: `text NOT NULL DEFAULT ''` — narrowed so MySQL can carry the default.
171
+ firstName: varchar('first_name', { length: 255 }).notNull().default(''),
172
+ lastName: varchar('last_name', { length: 255 }).notNull().default(''),
112
173
  role: varchar('role', { length: 20 }).notNull().default('reader'),
113
174
  isActive: boolean('is_active').notNull().default(true),
114
175
  lastLoginAt: mysqlTs('last_login_at'),
115
- createdAt: mysqlTs('created_at').notNull(),
116
- updatedAt: mysqlTs('updated_at').notNull(),
176
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
177
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
117
178
  // ── Profile fields ──────────────────────────────────────────────────────────
118
179
  company: text('company'),
119
180
  jobTitle: text('job_title'),
@@ -123,6 +184,7 @@ export const kywiUsersMysql = mysqlTable('kywi_users', {
123
184
  bio: text('bio'),
124
185
  preferences: json('preferences'),
125
186
  passwordChangedAt: mysqlTs('password_changed_at'),
187
+ passwordResetRequired: boolean('password_reset_required').notNull().default(false),
126
188
  });
127
189
  // ── kywiGroupsMysql ─────────────────────────────────────────────────────────
128
190
  export const kywiGroupsMysql = mysqlTable('kywi_groups', {
@@ -131,7 +193,7 @@ export const kywiGroupsMysql = mysqlTable('kywi_groups', {
131
193
  name: text('name').notNull(),
132
194
  type: varchar('type', { length: 20 }).notNull().default('private'),
133
195
  defaultRole: varchar('default_role', { length: 20 }).notNull().default('reader'),
134
- createdAt: mysqlTs('created_at').notNull(),
196
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
135
197
  });
136
198
  // ── kywiUserGroupsMysql ─────────────────────────────────────────────────────
137
199
  // #40: unique(userId, groupId) — see the matching note on kywiUserGroups in
@@ -148,9 +210,9 @@ export const kywiApiKeysMysql = mysqlTable('kywi_api_keys', {
148
210
  name: text('name').notNull(),
149
211
  keyHash: text('key_hash').notNull(),
150
212
  keyPrefix: varchar('key_prefix', { length: 12 }).notNull(),
151
- scopes: json('scopes').notNull(),
213
+ scopes: json('scopes').notNull().default(mysqlJsonDefault('[]')),
152
214
  lastUsedAt: mysqlTs('last_used_at'),
153
- createdAt: mysqlTs('created_at').notNull(),
215
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
154
216
  });
155
217
  // ── kywiSiteMembershipsMysql ────────────────────────────────────────────────
156
218
  // #40: unique(userId, siteId) — see the matching note on kywiSiteMemberships
@@ -160,8 +222,8 @@ export const kywiApiKeysMysql = mysqlTable('kywi_api_keys', {
160
222
  export const kywiSiteMembershipsMysql = mysqlTable('kywi_site_memberships', {
161
223
  userId: mysqlUuid('user_id').notNull().references(() => kywiUsersMysql.id, { onDelete: 'cascade' }),
162
224
  siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id, { onDelete: 'cascade' }),
163
- groups: json('groups').notNull(),
164
- createdAt: mysqlTs('created_at').notNull(),
225
+ groups: json('groups').notNull().default(mysqlJsonDefault('[]')),
226
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
165
227
  }, (t) => [unique().on(t.userId, t.siteId)]);
166
228
  // ── kywiMediaMysql ──────────────────────────────────────────────────────────
167
229
  export const kywiMediaMysql = mysqlTable('kywi_media', {
@@ -180,8 +242,8 @@ export const kywiMediaMysql = mysqlTable('kywi_media', {
180
242
  uploadedBy: mysqlUuid('uploaded_by'),
181
243
  storageKey: text('storage_key').notNull(),
182
244
  deletedAt: mysqlTs('deleted_at'),
183
- createdAt: mysqlTs('created_at').notNull(),
184
- updatedAt: mysqlTs('updated_at').notNull(),
245
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
246
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
185
247
  });
186
248
  // ── kywiFormSubmissionsMysql ────────────────────────────────────────────────
187
249
  export const kywiFormSubmissionsMysql = mysqlTable('kywi_form_submissions', {
@@ -189,7 +251,7 @@ export const kywiFormSubmissionsMysql = mysqlTable('kywi_form_submissions', {
189
251
  siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
190
252
  formContentId: mysqlUuid('form_content_id').notNull(),
191
253
  data: json('data').notNull(),
192
- submittedAt: mysqlTs('submitted_at').notNull(),
254
+ submittedAt: mysqlTs('submitted_at').notNull().default(mysqlNow()),
193
255
  ipAddress: varchar('ip_address', { length: 45 }),
194
256
  status: varchar('status', { length: 20 }).notNull().default('new'),
195
257
  });
@@ -203,7 +265,7 @@ export const kywiContentVersionsMysql = mysqlTable('kywi_content_versions', {
203
265
  changeNote: text('change_note'),
204
266
  status: varchar('status', { length: 20 }).notNull(),
205
267
  createdBy: mysqlUuid('created_by'),
206
- createdAt: mysqlTs('created_at').notNull(),
268
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
207
269
  });
208
270
  // ── kywiCategoriesMysql ─────────────────────────────────────────────────────
209
271
  export const kywiCategoriesMysql = mysqlTable('kywi_categories', {
@@ -217,8 +279,8 @@ export const kywiCategoriesMysql = mysqlTable('kywi_categories', {
217
279
  isActive: boolean('is_active').notNull().default(true),
218
280
  isInterestGroup: boolean('is_interest_group').notNull().default(false),
219
281
  notes: text('notes'),
220
- createdAt: mysqlTs('created_at').notNull(),
221
- updatedAt: mysqlTs('updated_at').notNull(),
282
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
283
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
222
284
  });
223
285
  // ── kywiTagsMysql ───────────────────────────────────────────────────────────
224
286
  export const kywiTagsMysql = mysqlTable('kywi_tags', {
@@ -228,7 +290,7 @@ export const kywiTagsMysql = mysqlTable('kywi_tags', {
228
290
  label: text('label').notNull(),
229
291
  usageCount: int('usage_count').notNull().default(0),
230
292
  groupName: varchar('group_name', { length: 100 }),
231
- createdAt: mysqlTs('created_at').notNull(),
293
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
232
294
  });
233
295
  // ── kywiCommentsMysql ───────────────────────────────────────────────────────
234
296
  export const kywiCommentsMysql = mysqlTable('kywi_comments', {
@@ -241,8 +303,8 @@ export const kywiCommentsMysql = mysqlTable('kywi_comments', {
241
303
  authorEmail: text('author_email'),
242
304
  body: text('body').notNull(),
243
305
  status: varchar('status', { length: 20 }).notNull().default('pending'),
244
- createdAt: mysqlTs('created_at').notNull(),
245
- updatedAt: mysqlTs('updated_at').notNull(),
306
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
307
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
246
308
  });
247
309
  // ── kywiExperimentsMysql ─────────────────────────────────────────────────────
248
310
  export const kywiExperimentsMysql = mysqlTable('kywi_experiments', {
@@ -261,9 +323,12 @@ export const kywiExperimentsMysql = mysqlTable('kywi_experiments', {
261
323
  pausedAt: mysqlTs('paused_at'),
262
324
  completedAt: mysqlTs('completed_at'),
263
325
  createdBy: mysqlUuid('created_by'),
264
- createdAt: mysqlTs('created_at').notNull(),
265
- updatedAt: mysqlTs('updated_at').notNull(),
266
- });
326
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
327
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
328
+ }, (t) => [
329
+ index('kywi_experiments_site_idx').on(t.siteId),
330
+ index('kywi_experiments_status_idx').on(t.status),
331
+ ]);
267
332
  // ── kywiExperimentVariantsMysql ──────────────────────────────────────────────
268
333
  export const kywiExperimentVariantsMysql = mysqlTable('kywi_experiment_variants', {
269
334
  id: mysqlUuidPk(),
@@ -273,18 +338,25 @@ export const kywiExperimentVariantsMysql = mysqlTable('kywi_experiment_variants'
273
338
  isControl: boolean('is_control').notNull().default(false),
274
339
  variantKey: text('variant_key'),
275
340
  sortOrder: int('sort_order').notNull().default(0),
276
- createdAt: mysqlTs('created_at').notNull(),
277
- });
341
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
342
+ }, (t) => [
343
+ index('kywi_experiment_variants_experiment_idx').on(t.experimentId),
344
+ ]);
278
345
  // ── kywiExperimentAssignmentsMysql ───────────────────────────────────────────
279
346
  export const kywiExperimentAssignmentsMysql = mysqlTable('kywi_experiment_assignments', {
280
347
  id: mysqlUuidPk(),
281
348
  experimentId: mysqlUuid('experiment_id').notNull().references(() => kywiExperimentsMysql.id),
282
349
  variantId: mysqlUuid('variant_id').notNull(),
283
350
  siteId: mysqlUuid('site_id').notNull(),
284
- visitorId: text('visitor_id').notNull(),
285
- assignedAt: mysqlTs('assigned_at').notNull(),
351
+ // pg: `text`, but it is half of a UNIQUE index here — MySQL cannot index TEXT
352
+ // without a prefix length, so the twin narrows to varchar.
353
+ visitorId: varchar('visitor_id', { length: 255 }).notNull(),
354
+ assignedAt: mysqlTs('assigned_at').notNull().default(mysqlNow()),
286
355
  exposedAt: mysqlTs('exposed_at'),
287
- });
356
+ }, (t) => [
357
+ unique('kywi_experiment_assignments_exp_visitor_uniq').on(t.experimentId, t.visitorId),
358
+ index('kywi_experiment_assignments_exp_variant_idx').on(t.experimentId, t.variantId),
359
+ ]);
288
360
  // ── kywiExperimentConversionsMysql ───────────────────────────────────────────
289
361
  export const kywiExperimentConversionsMysql = mysqlTable('kywi_experiment_conversions', {
290
362
  id: mysqlUuidPk(),
@@ -294,8 +366,10 @@ export const kywiExperimentConversionsMysql = mysqlTable('kywi_experiment_conver
294
366
  visitorId: text('visitor_id').notNull(),
295
367
  goalType: text('goal_type').notNull(),
296
368
  metadata: json('metadata'),
297
- occurredAt: mysqlTs('occurred_at').notNull(),
298
- });
369
+ occurredAt: mysqlTs('occurred_at').notNull().default(mysqlNow()),
370
+ }, (t) => [
371
+ index('kywi_experiment_conversions_exp_variant_idx').on(t.experimentId, t.variantId),
372
+ ]);
299
373
  // ── kywiContentTypeFieldsMysql (hybrid schema engine — admin-added fields) ────
300
374
  export const kywiContentTypeFieldsMysql = mysqlTable('kywi_content_type_fields', {
301
375
  id: mysqlUuidPk(),
@@ -312,8 +386,8 @@ export const kywiContentTypeFieldsMysql = mysqlTable('kywi_content_type_fields',
312
386
  // 'list' fields set it.
313
387
  itemType: varchar('item_type', { length: 50 }),
314
388
  sortOrder: int('sort_order').notNull().default(0),
315
- createdAt: mysqlTs('created_at').notNull(),
316
- updatedAt: mysqlTs('updated_at').notNull(),
389
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
390
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
317
391
  }, (t) => [unique().on(t.contentTypeName, t.name)]);
318
392
  // ── kywiContentTypesMysql (visual type designer — admin-created types) ────────
319
393
  export const kywiContentTypesMysql = mysqlTable('kywi_content_types', {
@@ -325,8 +399,8 @@ export const kywiContentTypesMysql = mysqlTable('kywi_content_types', {
325
399
  // Whether the Title field is required on this type (kywi-cms#12). Defaults
326
400
  // true — back-compat with every type created before this setting existed.
327
401
  titleRequired: boolean('title_required').notNull().default(true),
328
- createdAt: mysqlTs('created_at').notNull(),
329
- updatedAt: mysqlTs('updated_at').notNull(),
402
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
403
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
330
404
  });
331
405
  // ── kywiMenusMysql (#110 — named owner-manageable navigation menus) ───────────
332
406
  export const kywiMenusMysql = mysqlTable('kywi_menus', {
@@ -334,8 +408,8 @@ export const kywiMenusMysql = mysqlTable('kywi_menus', {
334
408
  siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
335
409
  slug: varchar('slug', { length: 255 }).notNull(),
336
410
  name: text('name').notNull(),
337
- createdAt: mysqlTs('created_at').notNull(),
338
- updatedAt: mysqlTs('updated_at').notNull(),
411
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
412
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
339
413
  }, (t) => [unique().on(t.siteId, t.slug)]);
340
414
  // ── kywiMenuItemsMysql (#110) ────────────────────────────────────────────────
341
415
  // See the Postgres twin in base.ts for the contentId-XOR-externalUrl rule (it
@@ -346,11 +420,512 @@ export const kywiMenuItemsMysql = mysqlTable('kywi_menu_items', {
346
420
  parentId: mysqlUuid('parent_id'),
347
421
  sortOrder: int('sort_order').notNull().default(0),
348
422
  label: text('label').notNull(),
349
- contentId: mysqlUuid('content_id'),
423
+ kind: varchar('kind', { length: 20 }).notNull().default('link'),
424
+ contentId: mysqlUuid('content_id').references(() => kywiContentMysql.id, { onDelete: 'set null' }),
350
425
  externalUrl: text('external_url'),
351
426
  openInNewWindow: boolean('open_in_new_window').notNull().default(false),
352
427
  description: text('description'),
353
- createdAt: mysqlTs('created_at').notNull(),
354
- updatedAt: mysqlTs('updated_at').notNull(),
428
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
429
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
430
+ });
431
+ // ═════════════════════════════════════════════════════════════════════════════
432
+ // Twins added by the #75 parity sweep. Ordered to mirror the Postgres modules:
433
+ // base.ts, audiences/, webhooks.ts, extensions.ts, then the small per-feature
434
+ // modules. Every one is asserted against its Postgres original by
435
+ // __tests__/mysql-parity.test.ts.
436
+ // ═════════════════════════════════════════════════════════════════════════════
437
+ // ── base.ts ──────────────────────────────────────────────────────────────────
438
+ export const kywiContentDisplayIntervalsMysql = mysqlTable('kywi_content_display_intervals', {
439
+ id: mysqlUuidPk(),
440
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
441
+ dayOfWeek: int('day_of_week'),
442
+ startTime: text('start_time').notNull(),
443
+ endTime: text('end_time').notNull(),
444
+ startDate: mysqlTs('start_date'),
445
+ endDate: mysqlTs('end_date'),
446
+ });
447
+ export const kywiFeedsMysql = mysqlTable('kywi_feeds', {
448
+ id: mysqlUuidPk(),
449
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
450
+ name: text('name').notNull(),
451
+ slug: varchar('slug', { length: 255 }).notNull(),
452
+ query: json('query').notNull(),
453
+ cacheTtl: int('cache_ttl'),
454
+ createdBy: mysqlUuid('created_by'),
455
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
456
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
457
+ });
458
+ export const kywiFormSubmissionDraftsMysql = mysqlTable('kywi_form_submission_drafts', {
459
+ id: mysqlUuidPk(),
460
+ formContentId: mysqlUuid('form_content_id').notNull(),
461
+ token: varchar('token', { length: 64 }).notNull().unique(),
462
+ data: json('data').notNull(),
463
+ currentStep: int('current_step').notNull().default(0),
464
+ expiresAt: mysqlTs('expires_at').notNull(),
465
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
466
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
467
+ });
468
+ export const kywiAuditLogMysql = mysqlTable('kywi_audit_log', {
469
+ id: mysqlUuidPk(),
470
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
471
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
472
+ action: varchar('action', { length: 30 }).notNull(),
473
+ fromStatus: varchar('from_status', { length: 20 }),
474
+ toStatus: varchar('to_status', { length: 20 }),
475
+ changedFields: json('changed_fields'),
476
+ userId: mysqlUuid('user_id').notNull(),
477
+ userEmail: text('user_email').notNull(),
478
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
479
+ });
480
+ export const kywiContentCategoriesMysql = mysqlTable('kywi_content_categories', {
481
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
482
+ categoryId: mysqlUuid('category_id').notNull().references(() => kywiCategoriesMysql.id, { onDelete: 'cascade' }),
483
+ isFeatured: boolean('is_featured').notNull().default(false),
484
+ featuredUntil: mysqlTs('featured_until'),
485
+ });
486
+ export const kywiContentTagsMysql = mysqlTable('kywi_content_tags', {
487
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
488
+ tagId: mysqlUuid('tag_id').notNull().references(() => kywiTagsMysql.id, { onDelete: 'cascade' }),
489
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
490
+ }, (t) => [primaryKey({ columns: [t.contentId, t.tagId] })]);
491
+ export const kywiOAuthClientsMysql = mysqlTable('kywi_oauth_clients', {
492
+ id: mysqlUuidPk(),
493
+ clientId: varchar('client_id', { length: 64 }).notNull().unique(),
494
+ clientSecret: text('client_secret').notNull(),
495
+ name: text('name').notNull(),
496
+ scopes: json('scopes').notNull().default(mysqlJsonDefault('[]')),
497
+ redirectUris: json('redirect_uris').notNull().default(mysqlJsonDefault('[]')),
498
+ rateLimitPerMinute: int('rate_limit_per_minute').notNull().default(60),
499
+ createdBy: mysqlUuid('created_by'),
500
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
501
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
502
+ });
503
+ export const kywiLoginStrikesMysql = mysqlTable('kywi_login_strikes', {
504
+ id: mysqlUuidPk(),
505
+ userId: text('user_id').notNull(),
506
+ ip: text('ip').notNull(),
507
+ attemptedAt: mysqlTs('attempted_at').notNull().default(mysqlNow()),
508
+ });
509
+ export const kywiMfaSecretsMysql = mysqlTable('kywi_mfa_secrets', {
510
+ id: mysqlUuidPk(),
511
+ // pg: `text NOT NULL UNIQUE`, holding a kywi_users.id. MySQL cannot index
512
+ // TEXT without a prefix length, so the twin narrows to the uuid width.
513
+ userId: varchar('user_id', { length: 36 }).notNull().unique(),
514
+ secret: text('secret').notNull(),
515
+ recoveryCodes: text('recovery_codes').notNull(),
516
+ enabledAt: mysqlTs('enabled_at'),
517
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
518
+ });
519
+ export const kywiMagicLinkTokensMysql = mysqlTable('kywi_magic_link_tokens', {
520
+ id: mysqlUuidPk(),
521
+ userId: text('user_id').notNull(),
522
+ tokenHash: text('token_hash').notNull(),
523
+ expiresAt: mysqlTs('expires_at').notNull(),
524
+ usedAt: mysqlTs('used_at'),
525
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
526
+ });
527
+ export const kywiChangesetsMysql = mysqlTable('kywi_changesets', {
528
+ id: mysqlUuidPk(),
529
+ siteId: text('site_id').notNull(),
530
+ name: text('name').notNull(),
531
+ description: text('description'),
532
+ // pg: `text NOT NULL DEFAULT 'open'` — narrowed so MySQL can carry the default.
533
+ status: varchar('status', { length: 20 }).notNull().default('open'),
534
+ scheduledPublishAt: mysqlTs('scheduled_publish_at'),
535
+ publishedAt: mysqlTs('published_at'),
536
+ publishedBy: text('published_by'),
537
+ createdBy: text('created_by').notNull(),
538
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
539
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
540
+ });
541
+ export const kywiChangesetItemsMysql = mysqlTable('kywi_changeset_items', {
542
+ id: mysqlUuidPk(),
543
+ changesetId: text('changeset_id').notNull(),
544
+ contentId: text('content_id').notNull(),
545
+ versionId: text('version_id'),
546
+ action: text('action').notNull(),
547
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
548
+ });
549
+ export const kywiChangesetAuditMysql = mysqlTable('kywi_changeset_audit', {
550
+ id: mysqlUuidPk(),
551
+ changesetId: text('changeset_id').notNull(),
552
+ siteId: text('site_id').notNull(),
553
+ action: varchar('action', { length: 20 }).notNull(),
554
+ userId: text('user_id').notNull(),
555
+ detail: json('detail'),
556
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
557
+ });
558
+ export const kywiFilenameArchiveMysql = mysqlTable('kywi_filename_archive', {
559
+ id: mysqlUuidPk(),
560
+ siteId: text('site_id').notNull(),
561
+ oldPath: text('old_path').notNull(),
562
+ newPath: text('new_path').notNull(),
563
+ contentId: text('content_id').notNull(),
564
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
565
+ });
566
+ export const kywiContentLocksMysql = mysqlTable('kywi_content_locks', {
567
+ // pg: `text PRIMARY KEY`. MySQL cannot use TEXT as a primary key, so the twin
568
+ // narrows to the uuid width the column actually holds.
569
+ contentId: varchar('content_id', { length: 36 }).primaryKey(),
570
+ userId: text('user_id').notNull(),
571
+ lockedAt: mysqlTs('locked_at').notNull().default(mysqlNow()),
572
+ expiresAt: mysqlTs('expires_at').notNull(),
573
+ });
574
+ export const kywiContentStatsMysql = mysqlTable('kywi_content_stats', {
575
+ // pg: `text PRIMARY KEY` — same TEXT-as-PK narrowing as kywi_content_locks.
576
+ contentId: varchar('content_id', { length: 36 }).primaryKey(),
577
+ views: int('views').notNull().default(0),
578
+ downloads: int('downloads').notNull().default(0),
579
+ commentCount: int('comment_count').notNull().default(0),
580
+ lastViewedAt: mysqlTs('last_viewed_at'),
581
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
582
+ });
583
+ export const kywiViewLogMysql = mysqlTable('kywi_view_log', {
584
+ id: mysqlUuidPk(),
585
+ contentId: text('content_id').notNull(),
586
+ visitorId: text('visitor_id'),
587
+ viewedAt: mysqlTs('viewed_at').notNull().default(mysqlNow()),
588
+ });
589
+ export const kywiSectionTemplatesMysql = mysqlTable('kywi_section_templates', {
590
+ id: mysqlUuidPk(),
591
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
592
+ name: text('name').notNull(),
593
+ category: varchar('category', { length: 100 }).notNull().default('general'),
594
+ definition: json('definition').notNull(),
595
+ createdBy: mysqlUuid('created_by'),
596
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
597
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
598
+ });
599
+ export const kywiApprovalChainsMysql = mysqlTable('kywi_approval_chains', {
600
+ id: mysqlUuidPk(),
601
+ siteId: text('site_id').notNull(),
602
+ name: text('name').notNull(),
603
+ steps: json('steps').notNull(),
604
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
605
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
606
+ });
607
+ export const kywiApprovalActionsMysql = mysqlTable('kywi_approval_actions', {
608
+ id: mysqlUuidPk(),
609
+ chainId: text('chain_id').notNull(),
610
+ contentId: text('content_id').notNull(),
611
+ stepIndex: int('step_index').notNull(),
612
+ action: varchar('action', { length: 20 }).notNull(),
613
+ userId: text('user_id').notNull(),
614
+ comment: text('comment'),
615
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
616
+ });
617
+ export const kywiAnalyticsEventsMysql = mysqlTable('kywi_analytics_events', {
618
+ id: mysqlUuidPk(),
619
+ siteId: mysqlUuid('site_id').notNull(),
620
+ audienceId: mysqlUuid('audience_id').notNull(),
621
+ visitorId: text('visitor_id').notNull(),
622
+ // pg: `text` with an enum of 'matched' | 'impression' | 'click'. Postgres
623
+ // renders a text-with-enum column as plain text; varchar is the MySQL twin.
624
+ eventType: varchar('event_type', { length: 20 }).notNull(),
625
+ contentId: text('content_id'),
626
+ variantId: text('variant_id'),
627
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
628
+ });
629
+ // ── audiences/schema.ts ──────────────────────────────────────────────────────
630
+ export const kywiAudiencesMysql = mysqlTable('kywi_audiences', {
631
+ id: mysqlUuidPk(),
632
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
633
+ name: text('name').notNull(),
634
+ description: text('description'),
635
+ priority: int('priority').notNull(),
636
+ status: varchar('status', { length: 20 }).notNull().default('draft'),
637
+ scoreThreshold: int('score_threshold'),
638
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
639
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
640
+ }, (t) => [
641
+ index('kywi_audiences_site_idx').on(t.siteId),
642
+ unique('kywi_audiences_site_priority_uniq').on(t.siteId, t.priority),
643
+ ]);
644
+ export const kywiConditionGroupsMysql = mysqlTable('kywi_condition_groups', {
645
+ id: mysqlUuidPk(),
646
+ audienceId: mysqlUuid('audience_id').notNull().references(() => kywiAudiencesMysql.id, { onDelete: 'cascade' }),
647
+ sortOrder: int('sort_order').notNull().default(0),
355
648
  });
649
+ export const kywiConditionsMysql = mysqlTable('kywi_conditions', {
650
+ id: mysqlUuidPk(),
651
+ groupId: mysqlUuid('group_id').notNull().references(() => kywiConditionGroupsMysql.id, { onDelete: 'cascade' }),
652
+ type: varchar('type', { length: 20 }).notNull(),
653
+ signal: text('signal'),
654
+ operator: text('operator'),
655
+ value: json('value'),
656
+ // `trigger` is a MySQL reserved word; drizzle backtick-quotes every
657
+ // identifier it emits, so the column name needs no rename.
658
+ trigger: text('trigger'),
659
+ points: int('points'),
660
+ threshold: int('threshold'),
661
+ category: text('category'),
662
+ pattern: text('pattern'),
663
+ sortOrder: int('sort_order').notNull().default(0),
664
+ });
665
+ export const kywiSelfIdFieldsMysql = mysqlTable('kywi_selfid_fields', {
666
+ // pg: `text PRIMARY KEY` (a caller-supplied field key, not a uuid).
667
+ id: varchar('id', { length: 255 }).primaryKey(),
668
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
669
+ label: text('label').notNull(),
670
+ picklist: json('picklist').notNull(),
671
+ required: boolean('required').notNull().default(false),
672
+ sortOrder: int('sort_order').notNull().default(0),
673
+ });
674
+ export const kywiSelfIdWidgetConfigMysql = mysqlTable('kywi_selfid_widget_config', {
675
+ id: mysqlUuidPk(),
676
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
677
+ displayMode: varchar('display_mode', { length: 20 }).notNull().default('modal'),
678
+ trigger: json('trigger').notNull(),
679
+ headline: text('headline').notNull(),
680
+ subheadline: text('subheadline'),
681
+ fieldIds: json('field_ids').notNull(),
682
+ // pg: `text NOT NULL DEFAULT 'Submit'` — narrowed to carry the default.
683
+ submitLabel: varchar('submit_label', { length: 100 }).notNull().default('Submit'),
684
+ skipLabel: text('skip_label'),
685
+ frequency: varchar('frequency', { length: 20 }).notNull().default('once_visitor'),
686
+ });
687
+ export const kywiPreviewTokenDenylistMysql = mysqlTable('kywi_preview_token_denylist', {
688
+ // pg: `text PRIMARY KEY` — a JWT jti.
689
+ jti: varchar('jti', { length: 255 }).primaryKey(),
690
+ audienceId: mysqlUuid('audience_id').notNull(),
691
+ expiresAt: mysqlTs('expires_at').notNull(),
692
+ revokedAt: mysqlTs('revoked_at').notNull().default(mysqlNow()),
693
+ });
694
+ export const kywiAdapterIdentityStoreMysql = mysqlTable('kywi_adapter_identity_store', {
695
+ id: mysqlUuidPk(),
696
+ // pg: `text`, but both columns form a UNIQUE index — narrowed so MySQL can
697
+ // index them without a prefix length.
698
+ visitorId: varchar('visitor_id', { length: 255 }).notNull(),
699
+ adapterId: varchar('adapter_id', { length: 100 }).notNull(),
700
+ payload: json('payload').notNull(),
701
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
702
+ }, (t) => [
703
+ unique('kywi_adapter_identity_visitor_adapter_uniq').on(t.visitorId, t.adapterId),
704
+ ]);
705
+ // ── webhooks.ts ──────────────────────────────────────────────────────────────
706
+ export const kywiWebhookEndpointsMysql = mysqlTable('kywi_webhook_endpoints', {
707
+ id: mysqlUuidPk(),
708
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
709
+ url: text('url').notNull(),
710
+ secret: text('secret').notNull(),
711
+ events: json('events').notNull().default(mysqlJsonDefault('[]')),
712
+ isActive: boolean('is_active').notNull().default(true),
713
+ createdBy: mysqlUuid('created_by'),
714
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
715
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
716
+ });
717
+ export const kywiWebhookDeliveriesMysql = mysqlTable('kywi_webhook_deliveries', {
718
+ id: mysqlUuidPk(),
719
+ endpointId: mysqlUuid('endpoint_id').notNull().references(() => kywiWebhookEndpointsMysql.id, { onDelete: 'cascade' }),
720
+ event: varchar('event', { length: 100 }).notNull(),
721
+ payload: json('payload').notNull(),
722
+ status: varchar('status', { length: 20 }).notNull().default('pending'),
723
+ httpStatus: int('http_status'),
724
+ responseBody: text('response_body'),
725
+ attempts: int('attempts').notNull().default(0),
726
+ nextRetryAt: mysqlTs('next_retry_at'),
727
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
728
+ });
729
+ // ── extensions.ts ────────────────────────────────────────────────────────────
730
+ export const kywiExtendSubtypesMysql = mysqlTable('kywi_extend_subtypes', {
731
+ id: mysqlUuidPk(),
732
+ name: varchar('name', { length: 100 }).notNull(),
733
+ baseContentType: varchar('base_content_type', { length: 100 }).notNull(),
734
+ description: text('description'),
735
+ isActive: boolean('is_active').notNull().default(true),
736
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
737
+ });
738
+ export const kywiExtendSetsMysql = mysqlTable('kywi_extend_sets', {
739
+ id: mysqlUuidPk(),
740
+ subtypeId: mysqlUuid('subtype_id').notNull().references(() => kywiExtendSubtypesMysql.id, { onDelete: 'cascade' }),
741
+ name: varchar('name', { length: 100 }).notNull(),
742
+ label: varchar('label', { length: 200 }).notNull(),
743
+ sortOrder: int('sort_order').notNull().default(0),
744
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
745
+ });
746
+ export const kywiExtendAttributesMysql = mysqlTable('kywi_extend_attributes', {
747
+ id: mysqlUuidPk(),
748
+ setId: mysqlUuid('set_id').notNull().references(() => kywiExtendSetsMysql.id, { onDelete: 'cascade' }),
749
+ name: varchar('name', { length: 100 }).notNull(),
750
+ label: varchar('label', { length: 200 }).notNull(),
751
+ type: varchar('type', { length: 50 }).notNull(),
752
+ defaultValue: text('default_value'),
753
+ validation: json('validation'),
754
+ sortOrder: int('sort_order').notNull().default(0),
755
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
756
+ });
757
+ export const kywiExtendDataMysql = mysqlTable('kywi_extend_data', {
758
+ id: mysqlUuidPk(),
759
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
760
+ attributeId: mysqlUuid('attribute_id').notNull().references(() => kywiExtendAttributesMysql.id, { onDelete: 'cascade' }),
761
+ stringValue: text('string_value'),
762
+ numberValue: decimal('number_value', { precision: 20, scale: 6 }),
763
+ dateValue: mysqlTs('date_value'),
764
+ booleanValue: boolean('boolean_value'),
765
+ jsonValue: json('json_value'),
766
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
767
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
768
+ });
769
+ export const kywiExtendRelatedSetsMysql = mysqlTable('kywi_extend_related_sets', {
770
+ id: mysqlUuidPk(),
771
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
772
+ setId: mysqlUuid('set_id').notNull().references(() => kywiExtendSetsMysql.id, { onDelete: 'cascade' }),
773
+ data: json('data').notNull(),
774
+ sortOrder: int('sort_order').notNull().default(0),
775
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
776
+ });
777
+ // ── reminders.ts ─────────────────────────────────────────────────────────────
778
+ export const kywiRemindersMysql = mysqlTable('kywi_reminders', {
779
+ id: mysqlUuidPk(),
780
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
781
+ userId: mysqlUuid('user_id').notNull().references(() => kywiUsersMysql.id, { onDelete: 'cascade' }),
782
+ message: text('message').notNull(),
783
+ remindAt: mysqlTs('remind_at').notNull(),
784
+ dismissed: boolean('dismissed').notNull().default(false),
785
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
786
+ });
787
+ // ── components.ts ────────────────────────────────────────────────────────────
788
+ export const kywiComponentsMysql = mysqlTable('kywi_components', {
789
+ id: mysqlUuidPk(),
790
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
791
+ name: text('name').notNull(),
792
+ description: text('description'),
793
+ // Widened with its Postgres twin (kywi-cms#170) — `'moduleVariantContainer'`
794
+ // is 22 characters and did not fit the old 20.
795
+ type: varchar('type', { length: 40 }).notNull(),
796
+ moduleType: varchar('module_type', { length: 100 }),
797
+ definition: json('definition').notNull(),
798
+ createdBy: mysqlUuid('created_by'),
799
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
800
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
801
+ });
802
+ /**
803
+ * Holder-polymorphic since kywi-cms#156 — see the Postgres table's doc.
804
+ *
805
+ * The two placement indexes are the one place the twin reads differently from
806
+ * Postgres and means the same thing. Postgres declares them PARTIAL (`WHERE
807
+ * content_id IS NOT NULL`); MySQL has no partial indexes, but it does not need
808
+ * one here: a UNIQUE index ignores rows whose indexed columns include a NULL in
809
+ * both engines, so a plain `UNIQUE (component_id, content_id, node_id)` already
810
+ * constrains exactly the content rows and lets every blueprint row (content_id
811
+ * NULL) through — which is precisely what the partial index says out loud. This
812
+ * is NOT the `kywi_content_permissions` situation, where uniqueness genuinely
813
+ * could not be expressed and fell back to the application.
814
+ *
815
+ * The one-holder CHECK — which also ties `holder_kind` to whichever id column
816
+ * is set — is expressible directly: MySQL 8.0.16+ enforces table CHECK
817
+ * constraints. Older MySQL parses and ignores it, so on those versions the
818
+ * invariant rests on the query layer, which never writes another shape.
819
+ */
820
+ export const kywiComponentInstancesMysql = mysqlTable('kywi_component_instances', {
821
+ id: mysqlUuidPk(),
822
+ componentId: mysqlUuid('component_id').notNull().references(() => kywiComponentsMysql.id, { onDelete: 'cascade' }),
823
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id),
824
+ holderKind: varchar('holder_kind', { length: 20 }).notNull().default('content'),
825
+ contentId: mysqlUuid('content_id').references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
826
+ blueprintId: mysqlUuid('blueprint_id').references(() => kywiSectionTemplatesMysql.id, { onDelete: 'cascade' }),
827
+ contentType: varchar('content_type', { length: 100 }).notNull(),
828
+ // pg: `text`, but it is the third column of the placement UNIQUE index —
829
+ // MySQL cannot index TEXT without a prefix length, so the twin narrows it.
830
+ nodeId: varchar('node_id', { length: 255 }).notNull(),
831
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
832
+ }, (t) => [
833
+ unique('kywi_component_instances_placement').on(t.componentId, t.contentId, t.nodeId),
834
+ unique('kywi_component_instances_blueprint_placement').on(t.componentId, t.blueprintId, t.nodeId),
835
+ index('kywi_component_instances_by_content').on(t.contentId),
836
+ index('kywi_component_instances_by_blueprint').on(t.blueprintId),
837
+ check('kywi_component_instances_one_holder', sql `(${t.holderKind} = 'content' and ${t.contentId} is not null and ${t.blueprintId} is null)
838
+ or (${t.holderKind} = 'blueprint' and ${t.blueprintId} is not null and ${t.contentId} is null)`),
839
+ ]);
840
+ // ── comment-subscriptions.ts ─────────────────────────────────────────────────
841
+ export const kywiCommentSubscriptionsMysql = mysqlTable('kywi_comment_subscriptions', {
842
+ id: mysqlUuidPk(),
843
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
844
+ email: text('email').notNull(),
845
+ userId: mysqlUuid('user_id').references(() => kywiUsersMysql.id, { onDelete: 'cascade' }),
846
+ isActive: boolean('is_active').notNull().default(true),
847
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
848
+ });
849
+ // ── content-permissions.ts ───────────────────────────────────────────────────
850
+ /**
851
+ * The one place a Postgres construct has **no** MySQL equivalent.
852
+ *
853
+ * The Postgres table carries two PARTIAL unique indexes — `unique(content_id,
854
+ * user_id, action) WHERE group_id IS NULL` and its group-side mirror — because
855
+ * a grant row always has exactly one of user_id/group_id set, and `NULL !=
856
+ * NULL` for uniqueness in both dialects, so a plain four-column unique cannot
857
+ * express "no duplicate grant".
858
+ *
859
+ * MySQL has no partial indexes. The twin therefore declares the same two column
860
+ * tuples as PLAIN (non-unique) indexes: they still serve the read path that
861
+ * matters — `access-control.ts`'s permission check and the permissions-tab
862
+ * listing — but uniqueness is left to the application:
863
+ *
864
+ * - `grantContentPermission` (db/content-permission-queries.ts) checks for an
865
+ * existing identical grant before inserting, and
866
+ * - `kywi migrate` runs `dedupeContentPermissionRows` for existing databases.
867
+ *
868
+ * A MySQL 8 alternative would be a unique index over generated columns
869
+ * (`COALESCE(user_id, '-')`), which drizzle's mysql-core cannot declare today;
870
+ * it is recorded as a follow-up rather than hand-written DDL here.
871
+ */
872
+ export const kywiContentPermissionsMysql = mysqlTable('kywi_content_permissions', {
873
+ id: mysqlUuidPk(),
874
+ contentId: mysqlUuid('content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
875
+ userId: mysqlUuid('user_id').references(() => kywiUsersMysql.id, { onDelete: 'cascade' }),
876
+ groupId: mysqlUuid('group_id').references(() => kywiGroupsMysql.id, { onDelete: 'cascade' }),
877
+ action: varchar('action', { length: 20 }).notNull(),
878
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
879
+ }, (t) => [
880
+ index('kywi_content_permissions_user_grant').on(t.contentId, t.userId, t.action),
881
+ index('kywi_content_permissions_group_grant').on(t.contentId, t.groupId, t.action),
882
+ ]);
883
+ // ── content-references.ts ────────────────────────────────────────────────────
884
+ export const kywiContentReferencesMysql = mysqlTable('kywi_content_references', {
885
+ id: mysqlUuidPk(),
886
+ sourceSiteId: mysqlUuid('source_site_id').notNull().references(() => kywiSitesMysql.id),
887
+ sourceContentId: mysqlUuid('source_content_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
888
+ targetSiteId: mysqlUuid('target_site_id').notNull().references(() => kywiSitesMysql.id),
889
+ targetContentId: mysqlUuid('target_content_id').notNull().references(() => kywiContentMysql.id),
890
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
891
+ });
892
+ // ── relationships.ts ─────────────────────────────────────────────────────────
893
+ export const kywiContentRelationshipsMysql = mysqlTable('kywi_content_relationships', {
894
+ id: mysqlUuidPk(),
895
+ sourceId: mysqlUuid('source_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
896
+ targetId: mysqlUuid('target_id').notNull().references(() => kywiContentMysql.id, { onDelete: 'cascade' }),
897
+ type: varchar('type', { length: 50 }).notNull().default('related'),
898
+ sortOrder: int('sort_order').notNull().default(0),
899
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
900
+ });
901
+ // ── password-reset-tokens.ts ─────────────────────────────────────────────────
902
+ export const kywiPasswordResetTokensMysql = mysqlTable('kywi_password_reset_tokens', {
903
+ id: mysqlUuidPk(),
904
+ userId: mysqlUuid('user_id').notNull().references(() => kywiUsersMysql.id, { onDelete: 'cascade' }),
905
+ tokenHash: text('token_hash').notNull(),
906
+ expiresAt: mysqlTs('expires_at').notNull(),
907
+ usedAt: mysqlTs('used_at'),
908
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
909
+ });
910
+ // ── site-plugins.ts ──────────────────────────────────────────────────────────
911
+ export const kywiSitePluginsMysql = mysqlTable('kywi_site_plugins', {
912
+ id: mysqlUuidPk(),
913
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id, { onDelete: 'cascade' }),
914
+ pluginPackage: varchar('plugin_package', { length: 200 }).notNull(),
915
+ isActive: boolean('is_active').notNull().default(true),
916
+ config: json('config'),
917
+ createdAt: mysqlTs('created_at').notNull().default(mysqlNow()),
918
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
919
+ });
920
+ // ── site-settings.ts ─────────────────────────────────────────────────────────
921
+ export const kywiSiteSettingsMysql = mysqlTable('kywi_site_settings', {
922
+ id: mysqlUuidPk(),
923
+ siteId: mysqlUuid('site_id').notNull().references(() => kywiSitesMysql.id, { onDelete: 'cascade' }),
924
+ // `key` is a MySQL reserved word; drizzle backtick-quotes identifiers.
925
+ key: varchar('key', { length: 100 }).notNull(),
926
+ value: json('value').notNull(),
927
+ updatedAt: mysqlTs('updated_at').notNull().default(mysqlNow()),
928
+ }, (t) => [
929
+ unique('kywi_site_settings_site_id_key_unique').on(t.siteId, t.key),
930
+ ]);
356
931
  //# sourceMappingURL=mysql-tables.js.map