@kywi-software/core 0.2.0 → 0.3.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 (364) hide show
  1. package/README.md +57 -2
  2. package/dist/admin/app.d.ts +10 -1
  3. package/dist/admin/app.d.ts.map +1 -1
  4. package/dist/admin/app.js +2 -2
  5. package/dist/admin/app.js.map +1 -1
  6. package/dist/admin/approval-queue.d.ts +3 -1
  7. package/dist/admin/approval-queue.d.ts.map +1 -1
  8. package/dist/admin/approval-queue.js +2 -2
  9. package/dist/admin/approval-queue.js.map +1 -1
  10. package/dist/admin/audience-list.d.ts.map +1 -1
  11. package/dist/admin/audience-list.js +9 -1
  12. package/dist/admin/audience-list.js.map +1 -1
  13. package/dist/admin/chrome.d.ts +7 -1
  14. package/dist/admin/chrome.d.ts.map +1 -1
  15. package/dist/admin/chrome.js +2 -1
  16. package/dist/admin/chrome.js.map +1 -1
  17. package/dist/admin/content-editor-api.d.ts +27 -1
  18. package/dist/admin/content-editor-api.d.ts.map +1 -1
  19. package/dist/admin/content-editor-api.js +60 -2
  20. package/dist/admin/content-editor-api.js.map +1 -1
  21. package/dist/admin/content-form.d.ts +62 -6
  22. package/dist/admin/content-form.d.ts.map +1 -1
  23. package/dist/admin/content-form.js +118 -13
  24. package/dist/admin/content-form.js.map +1 -1
  25. package/dist/admin/content-permissions-tab.d.ts +13 -0
  26. package/dist/admin/content-permissions-tab.d.ts.map +1 -0
  27. package/dist/admin/content-permissions-tab.js +90 -0
  28. package/dist/admin/content-permissions-tab.js.map +1 -0
  29. package/dist/admin/context.d.ts +8 -0
  30. package/dist/admin/context.d.ts.map +1 -1
  31. package/dist/admin/context.js.map +1 -1
  32. package/dist/admin/feed-builder.d.ts.map +1 -1
  33. package/dist/admin/feed-builder.js +20 -1
  34. package/dist/admin/feed-builder.js.map +1 -1
  35. package/dist/admin/field-components.d.ts.map +1 -1
  36. package/dist/admin/field-components.js +26 -1
  37. package/dist/admin/field-components.js.map +1 -1
  38. package/dist/admin/form-builder.js +6 -1
  39. package/dist/admin/form-builder.js.map +1 -1
  40. package/dist/admin/form-helpers.d.ts.map +1 -1
  41. package/dist/admin/form-helpers.js +2 -0
  42. package/dist/admin/form-helpers.js.map +1 -1
  43. package/dist/admin/kywi-logo.d.ts +21 -0
  44. package/dist/admin/kywi-logo.d.ts.map +1 -0
  45. package/dist/admin/kywi-logo.js +123 -0
  46. package/dist/admin/kywi-logo.js.map +1 -0
  47. package/dist/admin/layout-editor/component-actions.d.ts +18 -0
  48. package/dist/admin/layout-editor/component-actions.d.ts.map +1 -1
  49. package/dist/admin/layout-editor/component-actions.js +19 -0
  50. package/dist/admin/layout-editor/component-actions.js.map +1 -1
  51. package/dist/admin/layout-editor/component-picker-field.d.ts +20 -0
  52. package/dist/admin/layout-editor/component-picker-field.d.ts.map +1 -0
  53. package/dist/admin/layout-editor/component-picker-field.js +16 -0
  54. package/dist/admin/layout-editor/component-picker-field.js.map +1 -0
  55. package/dist/admin/layout-editor/drag-utils.d.ts +48 -0
  56. package/dist/admin/layout-editor/drag-utils.d.ts.map +1 -1
  57. package/dist/admin/layout-editor/drag-utils.js +43 -0
  58. package/dist/admin/layout-editor/drag-utils.js.map +1 -1
  59. package/dist/admin/layout-editor/editor-canvas.d.ts +9 -1
  60. package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
  61. package/dist/admin/layout-editor/editor-canvas.js +41 -15
  62. package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
  63. package/dist/admin/layout-editor/editor-state.d.ts +9 -1
  64. package/dist/admin/layout-editor/editor-state.d.ts.map +1 -1
  65. package/dist/admin/layout-editor/editor-state.js +34 -0
  66. package/dist/admin/layout-editor/editor-state.js.map +1 -1
  67. package/dist/admin/layout-editor/editor.css +83 -0
  68. package/dist/admin/layout-editor/form-picker-field.d.ts +17 -0
  69. package/dist/admin/layout-editor/form-picker-field.d.ts.map +1 -0
  70. package/dist/admin/layout-editor/form-picker-field.js +35 -0
  71. package/dist/admin/layout-editor/form-picker-field.js.map +1 -0
  72. package/dist/admin/layout-editor/layout-editor.d.ts.map +1 -1
  73. package/dist/admin/layout-editor/layout-editor.js +95 -28
  74. package/dist/admin/layout-editor/layout-editor.js.map +1 -1
  75. package/dist/admin/layout-editor/mode-toggle.d.ts.map +1 -1
  76. package/dist/admin/layout-editor/mode-toggle.js +1 -1
  77. package/dist/admin/layout-editor/mode-toggle.js.map +1 -1
  78. package/dist/admin/layout-editor/module-overlay.d.ts +10 -1
  79. package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
  80. package/dist/admin/layout-editor/module-overlay.js +3 -3
  81. package/dist/admin/layout-editor/module-overlay.js.map +1 -1
  82. package/dist/admin/layout-editor/props-panel.d.ts +10 -1
  83. package/dist/admin/layout-editor/props-panel.d.ts.map +1 -1
  84. package/dist/admin/layout-editor/props-panel.js +25 -2
  85. package/dist/admin/layout-editor/props-panel.js.map +1 -1
  86. package/dist/admin/layout-editor/section-insert-line.d.ts.map +1 -1
  87. package/dist/admin/layout-editor/section-insert-line.js +1 -1
  88. package/dist/admin/layout-editor/section-insert-line.js.map +1 -1
  89. package/dist/admin/layout-editor/section-overlay.d.ts.map +1 -1
  90. package/dist/admin/layout-editor/section-overlay.js +1 -1
  91. package/dist/admin/layout-editor/section-overlay.js.map +1 -1
  92. package/dist/admin/layout-editor/section-template-picker.d.ts +9 -1
  93. package/dist/admin/layout-editor/section-template-picker.d.ts.map +1 -1
  94. package/dist/admin/layout-editor/section-template-picker.js +45 -2
  95. package/dist/admin/layout-editor/section-template-picker.js.map +1 -1
  96. package/dist/admin/registry.d.ts.map +1 -1
  97. package/dist/admin/registry.js +11 -0
  98. package/dist/admin/registry.js.map +1 -1
  99. package/dist/admin/server.d.ts +15 -2
  100. package/dist/admin/server.d.ts.map +1 -1
  101. package/dist/admin/server.js +37 -1
  102. package/dist/admin/server.js.map +1 -1
  103. package/dist/admin/shell.d.ts.map +1 -1
  104. package/dist/admin/shell.js +62 -3
  105. package/dist/admin/shell.js.map +1 -1
  106. package/dist/admin/styles/admin.css +105 -27
  107. package/dist/admin/surface-matcher.d.ts +27 -0
  108. package/dist/admin/surface-matcher.d.ts.map +1 -1
  109. package/dist/admin/surface-matcher.js +11 -0
  110. package/dist/admin/surface-matcher.js.map +1 -1
  111. package/dist/admin/surfaces/approvals/approvals-data.d.ts +29 -9
  112. package/dist/admin/surfaces/approvals/approvals-data.d.ts.map +1 -1
  113. package/dist/admin/surfaces/approvals/approvals-data.js +65 -22
  114. package/dist/admin/surfaces/approvals/approvals-data.js.map +1 -1
  115. package/dist/admin/surfaces/approvals/approvals-index.d.ts.map +1 -1
  116. package/dist/admin/surfaces/approvals/approvals-index.js +47 -14
  117. package/dist/admin/surfaces/approvals/approvals-index.js.map +1 -1
  118. package/dist/admin/surfaces/content/content-edit.d.ts.map +1 -1
  119. package/dist/admin/surfaces/content/content-edit.js +34 -8
  120. package/dist/admin/surfaces/content/content-edit.js.map +1 -1
  121. package/dist/admin/surfaces/content/content-index.d.ts.map +1 -1
  122. package/dist/admin/surfaces/content/content-index.js +29 -2
  123. package/dist/admin/surfaces/content/content-index.js.map +1 -1
  124. package/dist/admin/surfaces/content/content-new.d.ts.map +1 -1
  125. package/dist/admin/surfaces/content/content-new.js +6 -4
  126. package/dist/admin/surfaces/content/content-new.js.map +1 -1
  127. package/dist/admin/surfaces/contentTree/content-tree.d.ts.map +1 -1
  128. package/dist/admin/surfaces/contentTree/content-tree.js +25 -2
  129. package/dist/admin/surfaces/contentTree/content-tree.js.map +1 -1
  130. package/dist/admin/surfaces/contentTypes/content-types-designer.d.ts.map +1 -1
  131. package/dist/admin/surfaces/contentTypes/content-types-designer.js +78 -47
  132. package/dist/admin/surfaces/contentTypes/content-types-designer.js.map +1 -1
  133. package/dist/admin/surfaces/feeds/feed-editor.d.ts.map +1 -1
  134. package/dist/admin/surfaces/feeds/feed-editor.js +21 -4
  135. package/dist/admin/surfaces/feeds/feed-editor.js.map +1 -1
  136. package/dist/admin/surfaces/layouts/layout-editor-client.d.ts.map +1 -1
  137. package/dist/admin/surfaces/layouts/layout-editor-client.js +7 -4
  138. package/dist/admin/surfaces/layouts/layout-editor-client.js.map +1 -1
  139. package/dist/admin/surfaces/login.d.ts.map +1 -1
  140. package/dist/admin/surfaces/login.js +2 -1
  141. package/dist/admin/surfaces/login.js.map +1 -1
  142. package/dist/admin/surfaces/media/media-client.d.ts.map +1 -1
  143. package/dist/admin/surfaces/media/media-client.js +10 -2
  144. package/dist/admin/surfaces/media/media-client.js.map +1 -1
  145. package/dist/admin/surfaces/settings/settings-site.d.ts.map +1 -1
  146. package/dist/admin/surfaces/settings/settings-site.js +39 -2
  147. package/dist/admin/surfaces/settings/settings-site.js.map +1 -1
  148. package/dist/admin/surfaces/settings/settings-sites.js +18 -0
  149. package/dist/admin/surfaces/settings/settings-sites.js.map +1 -1
  150. package/dist/admin/surfaces/trash/trash-index.d.ts.map +1 -1
  151. package/dist/admin/surfaces/trash/trash-index.js +45 -8
  152. package/dist/admin/surfaces/trash/trash-index.js.map +1 -1
  153. package/dist/admin/surfaces/users/users-data.d.ts +11 -0
  154. package/dist/admin/surfaces/users/users-data.d.ts.map +1 -1
  155. package/dist/admin/surfaces/users/users-data.js +20 -1
  156. package/dist/admin/surfaces/users/users-data.js.map +1 -1
  157. package/dist/admin/surfaces/webServices/web-services-data.d.ts +11 -1
  158. package/dist/admin/surfaces/webServices/web-services-data.d.ts.map +1 -1
  159. package/dist/admin/surfaces/webServices/web-services-data.js +13 -0
  160. package/dist/admin/surfaces/webServices/web-services-data.js.map +1 -1
  161. package/dist/admin/surfaces/webServices/webhooks.d.ts +3 -0
  162. package/dist/admin/surfaces/webServices/webhooks.d.ts.map +1 -0
  163. package/dist/admin/surfaces/webServices/webhooks.js +69 -0
  164. package/dist/admin/surfaces/webServices/webhooks.js.map +1 -0
  165. package/dist/admin/webhook-manager.d.ts +32 -0
  166. package/dist/admin/webhook-manager.d.ts.map +1 -0
  167. package/dist/admin/webhook-manager.js +62 -0
  168. package/dist/admin/webhook-manager.js.map +1 -0
  169. package/dist/api/admin-features-routes.d.ts.map +1 -1
  170. package/dist/api/admin-features-routes.js +30 -0
  171. package/dist/api/admin-features-routes.js.map +1 -1
  172. package/dist/api/api-key-routes.d.ts.map +1 -1
  173. package/dist/api/api-key-routes.js +6 -1
  174. package/dist/api/api-key-routes.js.map +1 -1
  175. package/dist/api/auth.d.ts +8 -0
  176. package/dist/api/auth.d.ts.map +1 -1
  177. package/dist/api/auth.js +11 -1
  178. package/dist/api/auth.js.map +1 -1
  179. package/dist/api/comment-routes.d.ts.map +1 -1
  180. package/dist/api/comment-routes.js +102 -47
  181. package/dist/api/comment-routes.js.map +1 -1
  182. package/dist/api/config-routes.d.ts +10 -2
  183. package/dist/api/config-routes.d.ts.map +1 -1
  184. package/dist/api/config-routes.js +46 -25
  185. package/dist/api/config-routes.js.map +1 -1
  186. package/dist/api/content-routes.d.ts.map +1 -1
  187. package/dist/api/content-routes.js +41 -0
  188. package/dist/api/content-routes.js.map +1 -1
  189. package/dist/api/content-schemas.d.ts.map +1 -1
  190. package/dist/api/content-schemas.js +34 -1
  191. package/dist/api/content-schemas.js.map +1 -1
  192. package/dist/api/content-type-routes.d.ts.map +1 -1
  193. package/dist/api/content-type-routes.js +58 -3
  194. package/dist/api/content-type-routes.js.map +1 -1
  195. package/dist/api/feed-routes.d.ts +180 -0
  196. package/dist/api/feed-routes.d.ts.map +1 -1
  197. package/dist/api/feed-routes.js +10 -2
  198. package/dist/api/feed-routes.js.map +1 -1
  199. package/dist/api/form-routes.d.ts +6 -0
  200. package/dist/api/form-routes.d.ts.map +1 -1
  201. package/dist/api/form-routes.js +93 -14
  202. package/dist/api/form-routes.js.map +1 -1
  203. package/dist/api/graphql/resolvers.d.ts +4 -4
  204. package/dist/api/index.d.ts.map +1 -1
  205. package/dist/api/index.js +12 -1
  206. package/dist/api/index.js.map +1 -1
  207. package/dist/api/media-routes.d.ts.map +1 -1
  208. package/dist/api/media-routes.js +18 -1
  209. package/dist/api/media-routes.js.map +1 -1
  210. package/dist/api/webhook-routes.d.ts.map +1 -1
  211. package/dist/api/webhook-routes.js +19 -2
  212. package/dist/api/webhook-routes.js.map +1 -1
  213. package/dist/api/workflow-routes.d.ts.map +1 -1
  214. package/dist/api/workflow-routes.js +163 -4
  215. package/dist/api/workflow-routes.js.map +1 -1
  216. package/dist/auth/roles.d.ts +28 -0
  217. package/dist/auth/roles.d.ts.map +1 -1
  218. package/dist/auth/roles.js +49 -0
  219. package/dist/auth/roles.js.map +1 -1
  220. package/dist/config/reserved-field-names.d.ts +29 -0
  221. package/dist/config/reserved-field-names.d.ts.map +1 -0
  222. package/dist/config/reserved-field-names.js +59 -0
  223. package/dist/config/reserved-field-names.js.map +1 -0
  224. package/dist/config/types.d.ts +31 -2
  225. package/dist/config/types.d.ts.map +1 -1
  226. package/dist/config/types.js.map +1 -1
  227. package/dist/db/analytics-queries.d.ts +1 -1
  228. package/dist/db/api-key-queries.d.ts +7 -2
  229. package/dist/db/api-key-queries.d.ts.map +1 -1
  230. package/dist/db/api-key-queries.js +8 -2
  231. package/dist/db/api-key-queries.js.map +1 -1
  232. package/dist/db/apply-snapshot.d.ts +58 -0
  233. package/dist/db/apply-snapshot.d.ts.map +1 -0
  234. package/dist/db/apply-snapshot.js +142 -0
  235. package/dist/db/apply-snapshot.js.map +1 -0
  236. package/dist/db/audit-queries.d.ts +3 -3
  237. package/dist/db/category-queries.d.ts +3 -3
  238. package/dist/db/changeset-audit-queries.d.ts +2 -2
  239. package/dist/db/changeset-queries.d.ts +5 -5
  240. package/dist/db/changeset-queries.d.ts.map +1 -1
  241. package/dist/db/changeset-queries.js +24 -7
  242. package/dist/db/changeset-queries.js.map +1 -1
  243. package/dist/db/content-queries.d.ts +6 -0
  244. package/dist/db/content-queries.d.ts.map +1 -1
  245. package/dist/db/content-queries.js +8 -0
  246. package/dist/db/content-queries.js.map +1 -1
  247. package/dist/db/content-type-queries.d.ts +20 -0
  248. package/dist/db/content-type-queries.d.ts.map +1 -1
  249. package/dist/db/content-type-queries.js +32 -0
  250. package/dist/db/content-type-queries.js.map +1 -1
  251. package/dist/db/dashboard-queries.d.ts.map +1 -1
  252. package/dist/db/dashboard-queries.js +5 -1
  253. package/dist/db/dashboard-queries.js.map +1 -1
  254. package/dist/db/dynamic-schema.d.ts.map +1 -1
  255. package/dist/db/dynamic-schema.js +2 -0
  256. package/dist/db/dynamic-schema.js.map +1 -1
  257. package/dist/db/extension-queries.d.ts +8 -8
  258. package/dist/db/feed-queries.d.ts +3 -3
  259. package/dist/db/index.d.ts +3 -0
  260. package/dist/db/index.d.ts.map +1 -1
  261. package/dist/db/index.js +7 -0
  262. package/dist/db/index.js.map +1 -1
  263. package/dist/db/media-queries.d.ts +31 -7
  264. package/dist/db/media-queries.d.ts.map +1 -1
  265. package/dist/db/media-queries.js +13 -1
  266. package/dist/db/media-queries.js.map +1 -1
  267. package/dist/db/membership-dedupe.d.ts +26 -0
  268. package/dist/db/membership-dedupe.d.ts.map +1 -0
  269. package/dist/db/membership-dedupe.js +68 -0
  270. package/dist/db/membership-dedupe.js.map +1 -0
  271. package/dist/db/revision-queries.d.ts +122 -0
  272. package/dist/db/revision-queries.d.ts.map +1 -0
  273. package/dist/db/revision-queries.js +213 -0
  274. package/dist/db/revision-queries.js.map +1 -0
  275. package/dist/db/schema-apply.d.ts +8 -0
  276. package/dist/db/schema-apply.d.ts.map +1 -1
  277. package/dist/db/schema-apply.js +17 -0
  278. package/dist/db/schema-apply.js.map +1 -1
  279. package/dist/db/schema-field-queries.d.ts +2 -0
  280. package/dist/db/schema-field-queries.d.ts.map +1 -1
  281. package/dist/db/schema-field-queries.js +4 -0
  282. package/dist/db/schema-field-queries.js.map +1 -1
  283. package/dist/db/site-plugin-queries.d.ts +2 -2
  284. package/dist/db/user-queries.d.ts +12 -0
  285. package/dist/db/user-queries.d.ts.map +1 -1
  286. package/dist/db/user-queries.js +18 -4
  287. package/dist/db/user-queries.js.map +1 -1
  288. package/dist/db/version-queries.d.ts +37 -0
  289. package/dist/db/version-queries.d.ts.map +1 -1
  290. package/dist/db/version-queries.js +36 -0
  291. package/dist/db/version-queries.js.map +1 -1
  292. package/dist/db/webhook-queries.d.ts +27 -5
  293. package/dist/db/webhook-queries.d.ts.map +1 -1
  294. package/dist/db/webhook-queries.js +17 -0
  295. package/dist/db/webhook-queries.js.map +1 -1
  296. package/dist/db/workflow-queries.d.ts +2 -2
  297. package/dist/experiments/schema.d.ts +1 -1
  298. package/dist/hooks/index.d.ts +1 -1
  299. package/dist/hooks/index.d.ts.map +1 -1
  300. package/dist/hooks/index.js +1 -1
  301. package/dist/hooks/index.js.map +1 -1
  302. package/dist/hooks/webhook-retry.d.ts +22 -1
  303. package/dist/hooks/webhook-retry.d.ts.map +1 -1
  304. package/dist/hooks/webhook-retry.js +38 -1
  305. package/dist/hooks/webhook-retry.js.map +1 -1
  306. package/dist/host/index.d.ts +28 -0
  307. package/dist/host/index.d.ts.map +1 -1
  308. package/dist/host/index.js +56 -1
  309. package/dist/host/index.js.map +1 -1
  310. package/dist/layout/comments-module.d.ts +18 -0
  311. package/dist/layout/comments-module.d.ts.map +1 -0
  312. package/dist/layout/comments-module.js +130 -0
  313. package/dist/layout/comments-module.js.map +1 -0
  314. package/dist/layout/feed-hydration.d.ts +75 -0
  315. package/dist/layout/feed-hydration.d.ts.map +1 -0
  316. package/dist/layout/feed-hydration.js +158 -0
  317. package/dist/layout/feed-hydration.js.map +1 -0
  318. package/dist/layout/index.d.ts +2 -1
  319. package/dist/layout/index.d.ts.map +1 -1
  320. package/dist/layout/index.js +2 -1
  321. package/dist/layout/index.js.map +1 -1
  322. package/dist/layout/module-render-context.d.ts +13 -0
  323. package/dist/layout/module-render-context.d.ts.map +1 -0
  324. package/dist/layout/module-render-context.js +15 -0
  325. package/dist/layout/module-render-context.js.map +1 -0
  326. package/dist/layout/modules-extended.d.ts.map +1 -1
  327. package/dist/layout/modules-extended.js +92 -13
  328. package/dist/layout/modules-extended.js.map +1 -1
  329. package/dist/layout/registry.d.ts.map +1 -1
  330. package/dist/layout/registry.js +22 -6
  331. package/dist/layout/registry.js.map +1 -1
  332. package/dist/layout/renderer.d.ts +17 -1
  333. package/dist/layout/renderer.d.ts.map +1 -1
  334. package/dist/layout/renderer.js +11 -4
  335. package/dist/layout/renderer.js.map +1 -1
  336. package/dist/nav/index.d.ts +1 -0
  337. package/dist/nav/index.d.ts.map +1 -1
  338. package/dist/nav/index.js +4 -0
  339. package/dist/nav/index.js.map +1 -1
  340. package/dist/nav/url-resolver.d.ts +10 -2
  341. package/dist/nav/url-resolver.d.ts.map +1 -1
  342. package/dist/nav/url-resolver.js +17 -3
  343. package/dist/nav/url-resolver.js.map +1 -1
  344. package/dist/schema/base.d.ts +37 -1
  345. package/dist/schema/base.d.ts.map +1 -1
  346. package/dist/schema/base.js +20 -2
  347. package/dist/schema/base.js.map +1 -1
  348. package/dist/schema/generate.d.ts +8 -0
  349. package/dist/schema/generate.d.ts.map +1 -1
  350. package/dist/schema/generate.js +7 -4
  351. package/dist/schema/generate.js.map +1 -1
  352. package/dist/schema/mapper.d.ts.map +1 -1
  353. package/dist/schema/mapper.js +2 -0
  354. package/dist/schema/mapper.js.map +1 -1
  355. package/dist/schema/mysql-tables.d.ts +34 -0
  356. package/dist/schema/mysql-tables.d.ts.map +1 -1
  357. package/dist/schema/mysql-tables.js +14 -2
  358. package/dist/schema/mysql-tables.js.map +1 -1
  359. package/dist/server.d.ts +1 -0
  360. package/dist/server.d.ts.map +1 -1
  361. package/dist/server.js +1 -0
  362. package/dist/server.js.map +1 -1
  363. package/dist/site/styles.css +384 -0
  364. package/package.json +2 -1
@@ -1,5 +1,5 @@
1
1
  import { createHmac } from 'node:crypto';
2
- import { updateDeliveryStatus } from '../db/webhook-queries.js';
2
+ import { updateDeliveryStatus, listPendingRetries, getEndpointById } from '../db/webhook-queries.js';
3
3
  // Exponential backoff: 1min, 5min, 15min, 60min, ...
4
4
  function computeNextRetryDelay(attempts) {
5
5
  const base = 60_000; // 1 minute
@@ -22,6 +22,43 @@ export function createWebhookRetryWorker(db, deps) {
22
22
  },
23
23
  };
24
24
  }
25
+ /**
26
+ * Start an in-process interval runner that re-dispatches due webhook retries
27
+ * every `intervalSeconds`, wiring the real DB deps (`listPendingRetries` /
28
+ * `getEndpointById`) into {@link createWebhookRetryWorker}. Mirrors
29
+ * `startScheduler`: overlapping ticks are skipped (a slow sweep never piles up),
30
+ * the timer is `unref()`d so it never keeps the process alive on its own, and
31
+ * one failed sweep routes to `onError` instead of crashing the host. Returns a
32
+ * handle whose `stop()` clears the interval.
33
+ *
34
+ * This is the missing runtime call site for the retry model (#57): before this,
35
+ * `createWebhookRetryWorker` was dead code and failed deliveries never retried.
36
+ */
37
+ export function startWebhookRetryWorker(db, opts) {
38
+ const worker = createWebhookRetryWorker(db, {
39
+ listPendingRetries: (d) => listPendingRetries(d, opts.maxAttempts !== undefined ? { maxAttempts: opts.maxAttempts } : {}),
40
+ getEndpointById: (d, id) => getEndpointById(d, id),
41
+ });
42
+ const ms = Math.max(1, opts.intervalSeconds) * 1000;
43
+ const onError = opts.onError ?? ((err) => console.warn('[kywi:webhooks] retry sweep failed:', err));
44
+ let running = false;
45
+ const timer = setInterval(() => {
46
+ if (running)
47
+ return;
48
+ running = true;
49
+ void worker.tick()
50
+ .catch(onError)
51
+ .finally(() => {
52
+ running = false;
53
+ });
54
+ }, ms);
55
+ // Do not hold the event loop open for the retry worker alone.
56
+ if (typeof timer.unref === 'function') {
57
+ ;
58
+ timer.unref();
59
+ }
60
+ return { stop: () => clearInterval(timer) };
61
+ }
25
62
  async function retryDeliver(url, secret, event, payload, deliveryId, previousAttempts, db) {
26
63
  const body = JSON.stringify({ event, data: payload, timestamp: Date.now() });
27
64
  const signature = createHmac('sha256', secret).update(body).digest('hex');
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-retry.js","sourceRoot":"","sources":["../../src/hooks/webhook-retry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook-retry.js","sourceRoot":"","sources":["../../src/hooks/webhook-retry.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  export { assertProductionAuthSecret, isDevFallbackSecret, DEV_FALLBACK_SECRETS, } from '../auth/auth-secret.js';
2
+ export { canAccessContent, hasMinRole, type ContentAction, type KywiRole, } from '../auth/roles.js';
2
3
  /** httpOnly cookie holding the short-lived access JWT (type=access). */
3
4
  export declare const ACCESS_COOKIE = "kywi_access_token";
4
5
  /** httpOnly cookie holding the long-lived refresh JWT (type=refresh). */
@@ -31,6 +32,33 @@ export type AccessTokenStatus =
31
32
  * because it expired is still refreshable ('stale'); anything else is 'invalid'.
32
33
  */
33
34
  export declare function classifyAccessToken(token: string | undefined, secret: string): Promise<AccessTokenStatus>;
35
+ /** The non-sensitive claims carried by a signature-verified access token. */
36
+ export interface SessionClaims {
37
+ /** The authenticated user's id (token `sub`). */
38
+ sub: string;
39
+ /** The user's role — feeds the content-permission gates (see canAccessContent). */
40
+ role: string;
41
+ /** The user's group ids (empty when the token carries none). */
42
+ groups: string[];
43
+ }
44
+ /**
45
+ * Verify an access-token cookie against the host's auth secret and return its
46
+ * non-sensitive claims (sub / role / groups), or null when the token is missing,
47
+ * malformed, the wrong type, or its signature does not match `secret`.
48
+ *
49
+ * A token whose signature verified but has merely EXPIRED still yields its
50
+ * claims: a public (site) route is NOT in the middleware's refresh matcher, so
51
+ * an actively-signed-in admin's short-lived access cookie may have lapsed by the
52
+ * time they open a page with `?kywi-edit=1`. These claims only gate a client-side
53
+ * edit AFFORDANCE (the front-of-site overlay); every real mutation still flows
54
+ * through /api/v1, which the middleware refreshes and which re-checks auth. So
55
+ * surfacing the overlay for a signature-valid-but-stale admin is safe and correct
56
+ * UX. Anything whose signature does not verify returns null.
57
+ *
58
+ * Intended for a host's public render to decide whether to mount the overlay for
59
+ * the current visitor — pair it with the re-exported `canAccessContent`.
60
+ */
61
+ export declare function readSessionClaims(token: string | undefined, secret: string): Promise<SessionClaims | null>;
34
62
  /**
35
63
  * Pull the access/refresh tokens out of a token-issuing endpoint's JSON body
36
64
  * (`{ data: { accessToken, refreshToken } }`). Missing/non-string fields and
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AA8BA,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,wBAAwB,CAAA;AAE/B,wEAAwE;AACxE,eAAO,MAAM,aAAa,sBAAsB,CAAA;AAChD,yEAAyE;AACzE,eAAO,MAAM,cAAc,uBAAuB,CAAA;AAGlD,eAAO,MAAM,kBAAkB,QAAU,CAAA;AACzC,eAAO,MAAM,mBAAmB,QAAmB,CAAA;AAEnD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAMlD,CAAA;AAEF,MAAM,MAAM,iBAAiB;AAC3B,wEAAwE;AACtE,OAAO;AACT,2EAA2E;GACzE,OAAO;AACT,yEAAyE;GACvE,SAAS,CAAA;AAcb;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAUA;AAED;oCACoC;AACpC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,KAAK,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAA;KAAE,CAAA;CACvF;AAkBD,+DAA+D;AAC/D,wBAAgB,mBAAmB,CAAC,MAAM,SAAqB,GAAG,oBAAoB,CAErF;AAED,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,MAAM,SAAsB,GAAG,oBAAoB,CAEvF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAExE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEzE;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAG7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AA8BA,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,wBAAwB,CAAA;AAM/B,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,QAAQ,GACd,MAAM,kBAAkB,CAAA;AAEzB,wEAAwE;AACxE,eAAO,MAAM,aAAa,sBAAsB,CAAA;AAChD,yEAAyE;AACzE,eAAO,MAAM,cAAc,uBAAuB,CAAA;AAGlD,eAAO,MAAM,kBAAkB,QAAU,CAAA;AACzC,eAAO,MAAM,mBAAmB,QAAmB,CAAA;AAEnD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAMlD,CAAA;AAEF,MAAM,MAAM,iBAAiB;AAC3B,wEAAwE;AACtE,OAAO;AACT,2EAA2E;GACzE,OAAO;AACT,yEAAyE;GACvE,SAAS,CAAA;AAcb;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAA;IACX,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAA;IACZ,gEAAgE;IAChE,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAaD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAiB/B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAUA;AAED;oCACoC;AACpC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,KAAK,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAA;KAAE,CAAA;CACvF;AAkBD,+DAA+D;AAC/D,wBAAgB,mBAAmB,CAAC,MAAM,SAAqB,GAAG,oBAAoB,CAErF;AAED,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,MAAM,SAAsB,GAAG,oBAAoB,CAEvF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAExE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEzE;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAG7D"}
@@ -22,12 +22,17 @@
22
22
  * them. The security-critical crypto (JWT verify, token classification, cookie
23
23
  * options) lives here once; the two host files carry only framework wiring.
24
24
  */
25
- import { jwtVerify } from 'jose';
25
+ import { jwtVerify, decodeJwt } from 'jose';
26
26
  // Re-export the AUTH_SECRET production guard so a generated app can call it at
27
27
  // module scope (in middleware.ts / kywi.config.ts) and refuse to build/start in
28
28
  // production with a missing or dev-fallback secret. Shares its policy with the
29
29
  // CLI/app env-guard, so both agree on what an insecure secret is.
30
30
  export { assertProductionAuthSecret, isDevFallbackSecret, DEV_FALLBACK_SECRETS, } from '../auth/auth-secret.js';
31
+ // Re-export the pure role → content-permission helpers so a host's public (site)
32
+ // render can decide, from a verified token's role, whether to surface the
33
+ // front-of-site edit overlay (see readSessionClaims). Both are dependency-free,
34
+ // so this adds nothing to the edge bundle beyond the constants they close over.
35
+ export { canAccessContent, hasMinRole, } from '../auth/roles.js';
31
36
  /** httpOnly cookie holding the short-lived access JWT (type=access). */
32
37
  export const ACCESS_COOKIE = 'kywi_access_token';
33
38
  /** httpOnly cookie holding the long-lived refresh JWT (type=refresh). */
@@ -88,6 +93,56 @@ export async function classifyAccessToken(token, secret) {
88
93
  return 'invalid';
89
94
  }
90
95
  }
96
+ function claimsFromPayload(payload) {
97
+ if (payload['type'] !== 'access')
98
+ return null;
99
+ const sub = typeof payload['sub'] === 'string' ? payload['sub'] : null;
100
+ const role = typeof payload['role'] === 'string' ? payload['role'] : null;
101
+ if (!sub || !role)
102
+ return null;
103
+ const groups = Array.isArray(payload['groups'])
104
+ ? payload['groups'].filter((g) => typeof g === 'string')
105
+ : [];
106
+ return { sub, role, groups };
107
+ }
108
+ /**
109
+ * Verify an access-token cookie against the host's auth secret and return its
110
+ * non-sensitive claims (sub / role / groups), or null when the token is missing,
111
+ * malformed, the wrong type, or its signature does not match `secret`.
112
+ *
113
+ * A token whose signature verified but has merely EXPIRED still yields its
114
+ * claims: a public (site) route is NOT in the middleware's refresh matcher, so
115
+ * an actively-signed-in admin's short-lived access cookie may have lapsed by the
116
+ * time they open a page with `?kywi-edit=1`. These claims only gate a client-side
117
+ * edit AFFORDANCE (the front-of-site overlay); every real mutation still flows
118
+ * through /api/v1, which the middleware refreshes and which re-checks auth. So
119
+ * surfacing the overlay for a signature-valid-but-stale admin is safe and correct
120
+ * UX. Anything whose signature does not verify returns null.
121
+ *
122
+ * Intended for a host's public render to decide whether to mount the overlay for
123
+ * the current visitor — pair it with the re-exported `canAccessContent`.
124
+ */
125
+ export async function readSessionClaims(token, secret) {
126
+ if (!token)
127
+ return null;
128
+ try {
129
+ const { payload } = await jwtVerify(token, keyFor(secret), { algorithms: ['HS256'] });
130
+ return claimsFromPayload(payload);
131
+ }
132
+ catch (err) {
133
+ // jose verifies the signature BEFORE the exp claim, so ERR_JWT_EXPIRED means
134
+ // the signature was valid — decode the (unverified) claims to read the role.
135
+ if (err.code === 'ERR_JWT_EXPIRED') {
136
+ try {
137
+ return claimsFromPayload(decodeJwt(token));
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+ }
91
146
  /**
92
147
  * Pull the access/refresh tokens out of a token-issuing endpoint's JSON body
93
148
  * (`{ data: { accessToken, refreshToken } }`). Missing/non-string fields and
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Comments layout module (#29). Renders the approved-comment list for the
3
+ * current content plus a public submission form. New comments POST to the
4
+ * collection endpoint `POST /api/v1/comments` and always land in the moderation
5
+ * queue as `pending`; they only appear here once an editor approves them (the
6
+ * list reads `GET /api/v1/comments?contentId=…&status=approved`, which the API
7
+ * only ever returns approved rows for).
8
+ *
9
+ * Inside the layout-editor canvas it renders a non-interactive preview (no live
10
+ * `<form>`), matching the Form module's editor behaviour (kywi-cms#36).
11
+ */
12
+ interface ModuleProps {
13
+ props: Record<string, unknown>;
14
+ style?: Record<string, string>;
15
+ }
16
+ export declare function CommentsModule({ props }: ModuleProps): import("react/jsx-runtime").JSX.Element;
17
+ export {};
18
+ //# sourceMappingURL=comments-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments-module.d.ts","sourceRoot":"","sources":["../../src/layout/comments-module.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AAEH,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AA2OD,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,2CAmBpD"}
@@ -0,0 +1,130 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useContext, useEffect, useRef, useState } from 'react';
4
+ import { ModuleEditorContext } from './module-render-context.js';
5
+ const COMMENTS_ENDPOINT = '/api/v1/comments';
6
+ const HONEYPOT_FIELD = '_kywi_hp';
7
+ /**
8
+ * Resolve the content id this module is attached to, in priority order:
9
+ * 1. an explicit `contentId` prop (advanced binding / preview),
10
+ * 2. a `<meta name="kywi:content-id">` emitted on the public content page,
11
+ * 3. the nearest `[data-kywi-content-id]` ancestor (shared with inline editing).
12
+ * Resolution beyond the explicit prop happens client-side only, so SSR stays
13
+ * deterministic.
14
+ */
15
+ function useResolvedContentId(explicit, ref) {
16
+ const [resolved, setResolved] = useState(explicit ?? null);
17
+ useEffect(() => {
18
+ if (explicit) {
19
+ setResolved(explicit);
20
+ return;
21
+ }
22
+ if (typeof document === 'undefined')
23
+ return;
24
+ const meta = document
25
+ .querySelector('meta[name="kywi:content-id"]')
26
+ ?.getAttribute('content');
27
+ if (meta) {
28
+ setResolved(meta);
29
+ return;
30
+ }
31
+ const el = ref.current?.closest('[data-kywi-content-id]');
32
+ const attr = el?.getAttribute('data-kywi-content-id');
33
+ if (attr)
34
+ setResolved(attr);
35
+ }, [explicit, ref]);
36
+ return resolved;
37
+ }
38
+ function authorLabel(c) {
39
+ return c.authorName && c.authorName.trim() ? c.authorName : 'Anonymous';
40
+ }
41
+ // ─── Editor preview (non-interactive) ────────────────────────────────────────
42
+ function CommentsModulePreview({ title, submitLabel }) {
43
+ return (_jsxs("div", { className: "kywi-module kywi-module-comments kywi-module-comments-preview", "aria-hidden": "true", children: [_jsx("h2", { className: "kywi-comments-title", children: title }), _jsx("p", { className: "kywi-comments-preview-note", children: "Approved comments for this content appear here; visitors can post new ones (held for moderation)." }), _jsxs("div", { className: "kywi-comments-form kywi-comments-form-preview", children: [_jsx("input", { className: "kywi-form-input", disabled: true, placeholder: "Name (optional)" }), _jsx("textarea", { className: "kywi-form-textarea", disabled: true, placeholder: "Add a comment\u2026" }), _jsx("button", { type: "button", className: "kywi-form-submit", disabled: true, children: submitLabel })] })] }));
44
+ }
45
+ // ─── Public view (interactive) ───────────────────────────────────────────────
46
+ function CommentsModuleView({ title, submitLabel, allowAnonymous, explicitId, }) {
47
+ const wrapperRef = useRef(null);
48
+ const contentId = useResolvedContentId(explicitId, wrapperRef);
49
+ const [comments, setComments] = useState([]);
50
+ const [loaded, setLoaded] = useState(false);
51
+ const [authorName, setAuthorName] = useState('');
52
+ const [authorEmail, setAuthorEmail] = useState('');
53
+ const [body, setBody] = useState('');
54
+ const [honeypot, setHoneypot] = useState('');
55
+ const [submitting, setSubmitting] = useState(false);
56
+ const [submitted, setSubmitted] = useState(false);
57
+ const [error, setError] = useState(null);
58
+ useEffect(() => {
59
+ if (!contentId)
60
+ return;
61
+ let cancelled = false;
62
+ fetch(`${COMMENTS_ENDPOINT}?contentId=${encodeURIComponent(contentId)}&status=approved`, {
63
+ credentials: 'include',
64
+ })
65
+ .then(r => (r.ok ? r.json() : null))
66
+ .then((payload) => {
67
+ if (!cancelled && Array.isArray(payload?.data)) {
68
+ // API returns newest-first; show oldest-first for readable threads.
69
+ setComments([...payload.data].reverse());
70
+ }
71
+ })
72
+ .catch(() => { })
73
+ .finally(() => { if (!cancelled)
74
+ setLoaded(true); });
75
+ return () => { cancelled = true; };
76
+ }, [contentId]);
77
+ const handleSubmit = async (e) => {
78
+ e.preventDefault();
79
+ setError(null);
80
+ if (!contentId) {
81
+ setError('This comments block is not connected to any content yet.');
82
+ return;
83
+ }
84
+ setSubmitting(true);
85
+ try {
86
+ const payload = { contentId, body };
87
+ if (authorName)
88
+ payload.authorName = authorName;
89
+ if (authorEmail)
90
+ payload.authorEmail = authorEmail;
91
+ if (honeypot)
92
+ payload[HONEYPOT_FIELD] = honeypot;
93
+ const res = await fetch(COMMENTS_ENDPOINT, {
94
+ method: 'POST',
95
+ headers: { 'Content-Type': 'application/json' },
96
+ credentials: 'include',
97
+ body: JSON.stringify(payload),
98
+ });
99
+ const result = (await res.json().catch(() => null));
100
+ if (!res.ok) {
101
+ setError(result?.error?.message ?? 'Could not submit your comment. Please try again.');
102
+ }
103
+ else {
104
+ setSubmitted(true);
105
+ setAuthorName('');
106
+ setAuthorEmail('');
107
+ setBody('');
108
+ }
109
+ }
110
+ catch {
111
+ setError('Network error. Please try again.');
112
+ }
113
+ finally {
114
+ setSubmitting(false);
115
+ }
116
+ };
117
+ return (_jsxs("div", { ref: wrapperRef, className: "kywi-module kywi-module-comments", "data-content-id": contentId ?? '', children: [_jsx("h2", { className: "kywi-comments-title", children: title }), _jsx("ul", { className: "kywi-comments-list", children: comments.map(c => (_jsxs("li", { className: "kywi-comment", children: [_jsxs("div", { className: "kywi-comment-meta", children: [_jsx("span", { className: "kywi-comment-author", children: authorLabel(c) }), c.createdAt && (_jsx("time", { className: "kywi-comment-date", dateTime: c.createdAt, children: new Date(c.createdAt).toLocaleDateString() }))] }), _jsx("p", { className: "kywi-comment-body", children: c.body })] }, c.id))) }), loaded && comments.length === 0 && (_jsx("p", { className: "kywi-comments-empty", children: "Be the first to comment." })), submitted ? (_jsx("div", { className: "kywi-comments-success", role: "status", children: _jsx("p", { children: "Thanks \u2014 your comment has been submitted and is awaiting moderation." }) })) : (_jsxs("form", { className: "kywi-comments-form", onSubmit: handleSubmit, children: [_jsx("div", { style: { position: 'absolute', left: '-9999px' }, "aria-hidden": "true", children: _jsx("input", { type: "text", name: HONEYPOT_FIELD, tabIndex: -1, autoComplete: "off", value: honeypot, onChange: e => setHoneypot(e.target.value) }) }), allowAnonymous && (_jsxs("div", { className: "kywi-comments-fields", children: [_jsx("label", { className: "kywi-form-label", htmlFor: "kywi-comment-name", children: "Name" }), _jsx("input", { id: "kywi-comment-name", className: "kywi-form-input", type: "text", value: authorName, placeholder: "Your name (optional)", onChange: e => setAuthorName(e.target.value) }), _jsx("label", { className: "kywi-form-label", htmlFor: "kywi-comment-email", children: "Email" }), _jsx("input", { id: "kywi-comment-email", className: "kywi-form-input", type: "email", value: authorEmail, placeholder: "Your email (optional, not published)", onChange: e => setAuthorEmail(e.target.value) })] })), _jsx("label", { className: "kywi-form-label", htmlFor: "kywi-comment-body", children: "Comment" }), _jsx("textarea", { id: "kywi-comment-body", className: "kywi-form-textarea", required: true, value: body, placeholder: "Add a comment\u2026", onChange: e => setBody(e.target.value) }), error && _jsx("div", { className: "kywi-form-error", role: "alert", children: error }), _jsx("div", { className: "kywi-form-actions", children: _jsx("button", { type: "submit", className: "kywi-form-submit", disabled: submitting || !body.trim(), children: submitting ? 'Submitting…' : submitLabel }) })] }))] }));
118
+ }
119
+ export function CommentsModule({ props }) {
120
+ const inEditor = useContext(ModuleEditorContext);
121
+ const title = String(props.title ?? 'Comments');
122
+ const submitLabel = String(props.submitLabel ?? 'Post Comment');
123
+ const allowAnonymous = props.allowAnonymous !== false;
124
+ const explicitId = props.contentId ? String(props.contentId) : undefined;
125
+ if (inEditor) {
126
+ return _jsx(CommentsModulePreview, { title: title, submitLabel: submitLabel });
127
+ }
128
+ return (_jsx(CommentsModuleView, { title: title, submitLabel: submitLabel, allowAnonymous: allowAnonymous, explicitId: explicitId }));
129
+ }
130
+ //# sourceMappingURL=comments-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments-module.js","sourceRoot":"","sources":["../../src/layout/comments-module.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * feed-hydration.ts
3
+ *
4
+ * Server-side data resolution for the public layout render path.
5
+ *
6
+ * The `feedDisplay` module (see modules.tsx) is a `'use client'` component: it can
7
+ * only render items it is *given* via `props.items` — it cannot run a database
8
+ * query itself. On the public site, nothing ran that query, so every Feed Display
9
+ * module rendered an empty `<div class="kywi-feed-items">` (kywi-cms#28).
10
+ *
11
+ * This module closes that gap without coupling the renderer to a database: the
12
+ * host supplies a {@link FeedItemsResolver} (it has DB + feed-engine access), and
13
+ * {@link hydrateLayoutFeeds} walks a {@link LayoutDocument}, runs each feed once,
14
+ * maps rows to the display shape the module expects, and injects `props.items`
15
+ * before the document is handed to `<KywiRegion>`. This mirrors the existing
16
+ * `ComponentResolver` seam in renderer.tsx: core owns the walk + shape, the host
17
+ * owns the data access.
18
+ */
19
+ import type { LayoutDocument } from './types.js';
20
+ /**
21
+ * The display-ready item shape `FeedDisplayModule` renders. Every field is a
22
+ * plain serializable value so the hydrated document can cross the
23
+ * server → client boundary as props.
24
+ */
25
+ export interface FeedDisplayItem {
26
+ id: string;
27
+ title: string;
28
+ /** Absolute-or-root-relative link to the item, or null to render an unlinked title. */
29
+ href: string | null;
30
+ /** ISO-8601 timestamp string, or null when the row has no publish/created date. */
31
+ date: string | null;
32
+ summary: string | null;
33
+ /** Resolved image URL, or null. Media-id → URL resolution is a host concern. */
34
+ image: string | null;
35
+ }
36
+ /**
37
+ * Resolve a feed slug to its raw content rows, server-side. The host implements
38
+ * this over the feed engine, e.g.
39
+ *
40
+ * ```ts
41
+ * const resolveFeedItems: FeedItemsResolver = async (slug, { limit }) => {
42
+ * const feed = await getFeedBySlug(db, slug, siteId)
43
+ * if (!feed) return []
44
+ * const result = await executeFeedQuery(
45
+ * db,
46
+ * { ...feed.query, siteId, status: 'published', limit },
47
+ * contentTypeMap,
48
+ * customTableMap,
49
+ * )
50
+ * return result.items
51
+ * }
52
+ * ```
53
+ *
54
+ * Rows are mapped to {@link FeedDisplayItem} by {@link contentRowsToFeedItems}.
55
+ */
56
+ export type FeedItemsResolver = (feedSlug: string, opts: {
57
+ limit: number;
58
+ }) => Promise<Array<Record<string, unknown>>>;
59
+ /**
60
+ * Map raw feed-engine content rows to the {@link FeedDisplayItem} shape the
61
+ * Feed Display module renders. Unknown/missing fields degrade to null (the
62
+ * module hides those pieces) rather than throwing. `image` is only populated
63
+ * when the row already carries a URL string — resolving a media id to a URL is
64
+ * left to the host (it can pre-map rows before returning them from the resolver).
65
+ */
66
+ export declare function contentRowsToFeedItems(rows: Array<Record<string, unknown>>): FeedDisplayItem[];
67
+ /**
68
+ * Resolve every `feedDisplay` module in `layout` and inject its items, returning
69
+ * a new document (the input is not mutated). Each distinct (slug, limit) pair is
70
+ * queried once and shared. Modules that already carry inline items are left
71
+ * untouched; a resolver error or an empty result yields an empty list (the module
72
+ * renders no items rather than crashing the page).
73
+ */
74
+ export declare function hydrateLayoutFeeds(layout: LayoutDocument, resolveFeedItems: FeedItemsResolver): Promise<LayoutDocument>;
75
+ //# sourceMappingURL=feed-hydration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed-hydration.d.ts","sourceRoot":"","sources":["../../src/layout/feed-hydration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,YAAY,CAAA;AAKvF;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,mFAAmF;IACnF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,gFAAgF;IAChF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KACpB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;AAqC5C;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACnC,eAAe,EAAE,CASnB;AA8CD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,gBAAgB,EAAE,iBAAiB,GAClC,OAAO,CAAC,cAAc,CAAC,CAmCzB"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * feed-hydration.ts
3
+ *
4
+ * Server-side data resolution for the public layout render path.
5
+ *
6
+ * The `feedDisplay` module (see modules.tsx) is a `'use client'` component: it can
7
+ * only render items it is *given* via `props.items` — it cannot run a database
8
+ * query itself. On the public site, nothing ran that query, so every Feed Display
9
+ * module rendered an empty `<div class="kywi-feed-items">` (kywi-cms#28).
10
+ *
11
+ * This module closes that gap without coupling the renderer to a database: the
12
+ * host supplies a {@link FeedItemsResolver} (it has DB + feed-engine access), and
13
+ * {@link hydrateLayoutFeeds} walks a {@link LayoutDocument}, runs each feed once,
14
+ * maps rows to the display shape the module expects, and injects `props.items`
15
+ * before the document is handed to `<KywiRegion>`. This mirrors the existing
16
+ * `ComponentResolver` seam in renderer.tsx: core owns the walk + shape, the host
17
+ * owns the data access.
18
+ */
19
+ import { isLayoutSection } from './types.js';
20
+ // ── Row → display-item mapping ────────────────────────────────────────────────
21
+ const DEFAULT_LIMIT = 10;
22
+ function toLimit(raw) {
23
+ const n = Number(raw);
24
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_LIMIT;
25
+ }
26
+ function firstString(...values) {
27
+ for (const value of values) {
28
+ if (typeof value === 'string' && value.trim() !== '')
29
+ return value;
30
+ }
31
+ return null;
32
+ }
33
+ /** Derive a root-relative link from a content row's `path`/`slug`, or null. */
34
+ function rowHref(row) {
35
+ const explicit = firstString(row.href, row.url);
36
+ if (explicit)
37
+ return explicit;
38
+ const path = row.path;
39
+ if (typeof path === 'string' && path !== '' && path !== '/')
40
+ return path;
41
+ const slug = row.slug;
42
+ if (typeof slug === 'string' && slug.trim() !== '')
43
+ return `/${slug.replace(/^\/+/, '')}`;
44
+ return null;
45
+ }
46
+ /** Derive an ISO date string from a content row's publish/created columns, or null. */
47
+ function rowDate(row) {
48
+ const raw = row.publishedAt ?? row.date ?? row.createdAt;
49
+ if (raw == null)
50
+ return null;
51
+ const d = raw instanceof Date ? raw : new Date(String(raw));
52
+ return Number.isNaN(d.getTime()) ? null : d.toISOString();
53
+ }
54
+ /**
55
+ * Map raw feed-engine content rows to the {@link FeedDisplayItem} shape the
56
+ * Feed Display module renders. Unknown/missing fields degrade to null (the
57
+ * module hides those pieces) rather than throwing. `image` is only populated
58
+ * when the row already carries a URL string — resolving a media id to a URL is
59
+ * left to the host (it can pre-map rows before returning them from the resolver).
60
+ */
61
+ export function contentRowsToFeedItems(rows) {
62
+ return rows.map((row, i) => ({
63
+ id: firstString(row.id) ?? String(i),
64
+ title: firstString(row.title, row.name, row.menuTitle) ?? '',
65
+ href: rowHref(row),
66
+ date: rowDate(row),
67
+ summary: firstString(row.summary, row.metaDescription, row.excerpt),
68
+ image: firstString(row.image),
69
+ }));
70
+ }
71
+ // ── Layout walk + injection ───────────────────────────────────────────────────
72
+ function* modulesInSection(section) {
73
+ for (const column of section.columns) {
74
+ for (const node of column.nodes)
75
+ yield node;
76
+ }
77
+ }
78
+ /**
79
+ * Yield every ModuleNode reachable from a layout document — across the default
80
+ * regions, every page variant, every A/B experiment arm, and the sections nested
81
+ * inside variantContainers — so no feed module is missed regardless of where the
82
+ * author placed it.
83
+ */
84
+ function* iterateModuleNodes(layout) {
85
+ const regionMaps = [layout.regions];
86
+ for (const pv of layout.pageVariants ?? [])
87
+ regionMaps.push(pv.regions);
88
+ for (const exp of layout.abExperiments ?? []) {
89
+ for (const variant of exp.variants)
90
+ regionMaps.push(variant.regions);
91
+ }
92
+ for (const regions of regionMaps) {
93
+ for (const nodes of Object.values(regions)) {
94
+ for (const node of nodes) {
95
+ if (isLayoutSection(node)) {
96
+ yield* modulesInSection(node);
97
+ }
98
+ else {
99
+ for (const section of node.defaultSections)
100
+ yield* modulesInSection(section);
101
+ for (const variant of node.variants) {
102
+ for (const section of variant.sections)
103
+ yield* modulesInSection(section);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ function hasResolvedItems(node) {
111
+ const items = node.props.items;
112
+ if (Array.isArray(items))
113
+ return items.length > 0;
114
+ // A non-empty JSON string of items counts as author-provided too.
115
+ return typeof items === 'string' && items.trim() !== '' && items.trim() !== '[]';
116
+ }
117
+ /**
118
+ * Resolve every `feedDisplay` module in `layout` and inject its items, returning
119
+ * a new document (the input is not mutated). Each distinct (slug, limit) pair is
120
+ * queried once and shared. Modules that already carry inline items are left
121
+ * untouched; a resolver error or an empty result yields an empty list (the module
122
+ * renders no items rather than crashing the page).
123
+ */
124
+ export async function hydrateLayoutFeeds(layout, resolveFeedItems) {
125
+ const cloned = structuredClone(layout);
126
+ const feedNodes = [];
127
+ for (const node of iterateModuleNodes(cloned)) {
128
+ if (node.type === 'feedDisplay')
129
+ feedNodes.push(node);
130
+ }
131
+ if (feedNodes.length === 0)
132
+ return cloned;
133
+ const cache = new Map();
134
+ for (const node of feedNodes) {
135
+ if (hasResolvedItems(node))
136
+ continue;
137
+ const feedSlug = typeof node.props.feedSlug === 'string' ? node.props.feedSlug.trim() : '';
138
+ if (feedSlug === '')
139
+ continue;
140
+ const limit = toLimit(node.props.limit);
141
+ const key = `${feedSlug}\u0000${limit}`;
142
+ let items = cache.get(key);
143
+ if (!items) {
144
+ let rows = [];
145
+ try {
146
+ rows = await resolveFeedItems(feedSlug, { limit });
147
+ }
148
+ catch {
149
+ rows = [];
150
+ }
151
+ items = contentRowsToFeedItems(Array.isArray(rows) ? rows : []);
152
+ cache.set(key, items);
153
+ }
154
+ node.props.items = items;
155
+ }
156
+ return cloned;
157
+ }
158
+ //# sourceMappingURL=feed-hydration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed-hydration.js","sourceRoot":"","sources":["../../src/layout/feed-hydration.ts"],"names":[],"mappings":"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"}