emdash 0.19.0 → 0.20.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 (403) hide show
  1. package/dist/{adapters-C5AWLJSD.d.mts → adapters-BzIHV3sw.d.mts} +1 -1
  2. package/dist/{adapters-C5AWLJSD.d.mts.map → adapters-BzIHV3sw.d.mts.map} +1 -1
  3. package/dist/{allowed-origins-CyYLEJkp.mjs → allowed-origins-B1u7Qnvg.mjs} +2 -2
  4. package/dist/{allowed-origins-CyYLEJkp.mjs.map → allowed-origins-B1u7Qnvg.mjs.map} +1 -1
  5. package/dist/api/route-utils.d.mts +3 -3
  6. package/dist/api/route-utils.mjs +5 -5
  7. package/dist/api/schemas/index.d.mts +1 -1
  8. package/dist/api/schemas/index.mjs +2 -2
  9. package/dist/{api-BZ6bhjYs.mjs → api-DStv36ik.mjs} +36 -5
  10. package/dist/api-DStv36ik.mjs.map +1 -0
  11. package/dist/{api-tokens-VrXNiNvV.mjs → api-tokens-DPfhPu5V.mjs} +2 -2
  12. package/dist/{api-tokens-VrXNiNvV.mjs.map → api-tokens-DPfhPu5V.mjs.map} +1 -1
  13. package/dist/{apply-hQkKKBCf.mjs → apply-Dr7snAMT.mjs} +7 -7
  14. package/dist/{apply-hQkKKBCf.mjs.map → apply-Dr7snAMT.mjs.map} +1 -1
  15. package/dist/astro/index.d.mts +10 -10
  16. package/dist/astro/index.mjs +3 -3
  17. package/dist/astro/middleware/auth.d.mts +9 -9
  18. package/dist/astro/middleware/auth.mjs +4 -4
  19. package/dist/astro/middleware/redirect.mjs +1 -1
  20. package/dist/astro/middleware/request-context.mjs +1 -1
  21. package/dist/astro/middleware/setup.mjs +1 -1
  22. package/dist/astro/middleware.d.mts +1 -1
  23. package/dist/astro/middleware.d.mts.map +1 -1
  24. package/dist/astro/middleware.mjs +63 -112
  25. package/dist/astro/middleware.mjs.map +1 -1
  26. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +2 -2
  27. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +2 -2
  28. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
  29. package/dist/astro/routes/api/admin/api-tokens/index.mjs +2 -2
  30. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +2 -2
  31. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +4 -4
  32. package/dist/astro/routes/api/admin/byline-fields/index.mjs +4 -4
  33. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +4 -4
  34. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +6 -6
  35. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +6 -6
  36. package/dist/astro/routes/api/admin/bylines/index.mjs +6 -6
  37. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +6 -6
  38. package/dist/astro/routes/api/admin/comments/_id_.mjs +2 -2
  39. package/dist/astro/routes/api/admin/comments/bulk.mjs +4 -4
  40. package/dist/astro/routes/api/admin/comments/counts.mjs +2 -2
  41. package/dist/astro/routes/api/admin/comments/index.mjs +4 -4
  42. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +1 -1
  43. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +1 -1
  44. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +1 -1
  45. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +1 -1
  46. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +14 -14
  47. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +14 -14
  48. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +14 -14
  49. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +14 -14
  50. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +14 -14
  51. package/dist/astro/routes/api/admin/plugins/index.mjs +14 -14
  52. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +1 -1
  53. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +14 -14
  54. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +14 -14
  55. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +14 -14
  56. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +14 -14
  57. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +15 -15
  58. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +14 -14
  59. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +15 -15
  60. package/dist/astro/routes/api/admin/plugins/updates.mjs +14 -14
  61. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +14 -14
  62. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +1 -1
  63. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +14 -14
  64. package/dist/astro/routes/api/admin/users/_id_/index.mjs +2 -2
  65. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +1 -1
  66. package/dist/astro/routes/api/admin/users/index.mjs +2 -2
  67. package/dist/astro/routes/api/auth/dev-bypass.mjs +2 -2
  68. package/dist/astro/routes/api/auth/invite/complete.mjs +6 -6
  69. package/dist/astro/routes/api/auth/invite/index.mjs +3 -3
  70. package/dist/astro/routes/api/auth/invite/register-options.mjs +5 -5
  71. package/dist/astro/routes/api/auth/logout.mjs +1 -1
  72. package/dist/astro/routes/api/auth/magic-link/send.mjs +4 -4
  73. package/dist/astro/routes/api/auth/magic-link/verify.mjs +1 -1
  74. package/dist/astro/routes/api/auth/me.mjs +2 -2
  75. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  76. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  77. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  78. package/dist/astro/routes/api/auth/passkey/_id_.mjs +2 -2
  79. package/dist/astro/routes/api/auth/passkey/options.mjs +6 -6
  80. package/dist/astro/routes/api/auth/passkey/register/options.mjs +5 -5
  81. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +6 -6
  82. package/dist/astro/routes/api/auth/passkey/verify.mjs +6 -6
  83. package/dist/astro/routes/api/auth/signup/complete.mjs +6 -6
  84. package/dist/astro/routes/api/auth/signup/request.mjs +4 -4
  85. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +6 -6
  86. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +1 -1
  87. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +1 -1
  88. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +1 -1
  89. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +1 -1
  90. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +4 -4
  91. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +3 -3
  92. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +1 -1
  93. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +1 -1
  94. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +3 -3
  95. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +5 -5
  96. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +1 -1
  97. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +1 -1
  98. package/dist/astro/routes/api/content/_collection_/_id_.mjs +3 -3
  99. package/dist/astro/routes/api/content/_collection_/authors.mjs +1 -1
  100. package/dist/astro/routes/api/content/_collection_/index.mjs +3 -3
  101. package/dist/astro/routes/api/content/_collection_/trash.mjs +3 -3
  102. package/dist/astro/routes/api/dashboard.mjs +1 -1
  103. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  104. package/dist/astro/routes/api/import/probe.mjs +3 -3
  105. package/dist/astro/routes/api/import/wordpress/analyze.mjs +1 -1
  106. package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
  107. package/dist/astro/routes/api/import/wordpress/execute.mjs +3 -3
  108. package/dist/astro/routes/api/import/wordpress/media.mjs +3 -3
  109. package/dist/astro/routes/api/import/wordpress/prepare.mjs +3 -3
  110. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +3 -3
  111. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  112. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +3 -3
  113. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
  114. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +3 -3
  115. package/dist/astro/routes/api/manifest.mjs +2 -2
  116. package/dist/astro/routes/api/mcp.mjs +18 -18
  117. package/dist/astro/routes/api/media/_id_/confirm.mjs +3 -3
  118. package/dist/astro/routes/api/media/_id_.mjs +3 -3
  119. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +1 -1
  120. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +1 -1
  121. package/dist/astro/routes/api/media/providers/index.mjs +1 -1
  122. package/dist/astro/routes/api/media/upload-url.mjs +4 -4
  123. package/dist/astro/routes/api/media.mjs +4 -4
  124. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +3 -3
  125. package/dist/astro/routes/api/menus/_name_/items.mjs +3 -3
  126. package/dist/astro/routes/api/menus/_name_/reorder.mjs +3 -3
  127. package/dist/astro/routes/api/menus/_name_/translations.mjs +3 -3
  128. package/dist/astro/routes/api/menus/_name_.mjs +3 -3
  129. package/dist/astro/routes/api/menus/index.mjs +3 -3
  130. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  131. package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
  132. package/dist/astro/routes/api/oauth/device/code.mjs +5 -5
  133. package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
  134. package/dist/astro/routes/api/oauth/register.mjs +1 -1
  135. package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
  136. package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
  137. package/dist/astro/routes/api/oauth/token.mjs +4 -4
  138. package/dist/astro/routes/api/openapi.json.mjs +1 -1
  139. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +2 -2
  140. package/dist/astro/routes/api/redirects/404s/index.mjs +4 -4
  141. package/dist/astro/routes/api/redirects/404s/summary.mjs +4 -4
  142. package/dist/astro/routes/api/redirects/_id_.mjs +4 -4
  143. package/dist/astro/routes/api/redirects/index.mjs +4 -4
  144. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +1 -1
  145. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +1 -1
  146. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +14 -14
  147. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +14 -14
  148. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +14 -14
  149. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +14 -14
  150. package/dist/astro/routes/api/schema/collections/index.mjs +14 -14
  151. package/dist/astro/routes/api/schema/index.mjs +5 -10
  152. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  153. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +14 -14
  154. package/dist/astro/routes/api/schema/orphans/index.mjs +14 -14
  155. package/dist/astro/routes/api/search/enable.mjs +5 -5
  156. package/dist/astro/routes/api/search/index.mjs +4 -4
  157. package/dist/astro/routes/api/search/rebuild.mjs +5 -5
  158. package/dist/astro/routes/api/search/stats.mjs +3 -3
  159. package/dist/astro/routes/api/search/suggest.mjs +4 -4
  160. package/dist/astro/routes/api/sections/_slug_.mjs +5 -5
  161. package/dist/astro/routes/api/sections/index.mjs +5 -5
  162. package/dist/astro/routes/api/settings/email.mjs +1 -1
  163. package/dist/astro/routes/api/settings.mjs +6 -6
  164. package/dist/astro/routes/api/setup/admin-verify.mjs +7 -7
  165. package/dist/astro/routes/api/setup/admin.mjs +6 -6
  166. package/dist/astro/routes/api/setup/dev-bypass.mjs +10 -10
  167. package/dist/astro/routes/api/setup/index.mjs +9 -9
  168. package/dist/astro/routes/api/setup/status.mjs +2 -2
  169. package/dist/astro/routes/api/snapshot.mjs +3 -3
  170. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +6 -6
  171. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +6 -6
  172. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +6 -6
  173. package/dist/astro/routes/api/taxonomies/index.mjs +6 -6
  174. package/dist/astro/routes/api/themes/preview.mjs +3 -3
  175. package/dist/astro/routes/api/well-known/auth.mjs +1 -1
  176. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  177. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  178. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +3 -3
  179. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +6 -5
  180. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  181. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +6 -5
  182. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  183. package/dist/astro/routes/api/widget-areas/_name_.mjs +4 -3
  184. package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
  185. package/dist/astro/routes/api/widget-areas/index.mjs +6 -5
  186. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  187. package/dist/astro/routes/api/widget-components.mjs +1 -1
  188. package/dist/astro/routes/robots.txt.mjs +3 -3
  189. package/dist/astro/routes/sitemap-_collection_.xml.d.mts.map +1 -1
  190. package/dist/astro/routes/sitemap-_collection_.xml.mjs +12 -5
  191. package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
  192. package/dist/astro/routes/sitemap.xml.mjs +4 -4
  193. package/dist/astro/types.d.mts +12 -12
  194. package/dist/auth/providers/github.d.mts +1 -1
  195. package/dist/auth/providers/google.d.mts +1 -1
  196. package/dist/{authorize-C_8t2KGa.mjs → authorize-DsMSVSaY.mjs} +1 -1
  197. package/dist/{authorize-C_8t2KGa.mjs.map → authorize-DsMSVSaY.mjs.map} +1 -1
  198. package/dist/{byline-fields-C_OsR-KF.mjs → byline-fields--WxSNS79.mjs} +1 -1
  199. package/dist/{byline-fields-C_OsR-KF.mjs.map → byline-fields--WxSNS79.mjs.map} +1 -1
  200. package/dist/{byline-fields-51kg6Vuv.mjs → byline-fields-8TMtkBnH.mjs} +2 -2
  201. package/dist/{byline-fields-51kg6Vuv.mjs.map → byline-fields-8TMtkBnH.mjs.map} +1 -1
  202. package/dist/{byline-fields-DYXKDuNX.d.mts → byline-fields-DbibsvTl.d.mts} +5 -1
  203. package/dist/byline-fields-DbibsvTl.d.mts.map +1 -0
  204. package/dist/{bylines-Cx5n-WqP.mjs → bylines-BdxWCnPL.mjs} +1 -1
  205. package/dist/{bylines-Cx5n-WqP.mjs.map → bylines-BdxWCnPL.mjs.map} +1 -1
  206. package/dist/{bylines-wurS258E.mjs → bylines-s8c2DXbH.mjs} +3 -3
  207. package/dist/{bylines-wurS258E.mjs.map → bylines-s8c2DXbH.mjs.map} +1 -1
  208. package/dist/{challenge-store-DGwuCc4R.mjs → challenge-store-DXX3rfdI.mjs} +1 -1
  209. package/dist/{challenge-store-DGwuCc4R.mjs.map → challenge-store-DXX3rfdI.mjs.map} +1 -1
  210. package/dist/cli/index.mjs +11 -10
  211. package/dist/cli/index.mjs.map +1 -1
  212. package/dist/client/cf-access.d.mts +1 -1
  213. package/dist/client/index.d.mts +1 -1
  214. package/dist/client/index.mjs +1 -1
  215. package/dist/{comments-CJ0RZsYR.mjs → comments-Vkivawyl.mjs} +1 -1
  216. package/dist/{comments-CJ0RZsYR.mjs.map → comments-Vkivawyl.mjs.map} +1 -1
  217. package/dist/{components-CTfpu3PZ.mjs → components-CK0cuUoH.mjs} +1 -1
  218. package/dist/{components-CTfpu3PZ.mjs.map → components-CK0cuUoH.mjs.map} +1 -1
  219. package/dist/{context-GG52SPgh.mjs → context-Y7BRkWes.mjs} +2 -2
  220. package/dist/{context-GG52SPgh.mjs.map → context-Y7BRkWes.mjs.map} +1 -1
  221. package/dist/database/instrumentation.d.mts +10 -1
  222. package/dist/database/instrumentation.d.mts.map +1 -1
  223. package/dist/database/instrumentation.mjs +13 -1
  224. package/dist/database/instrumentation.mjs.map +1 -1
  225. package/dist/db/index.d.mts +3 -3
  226. package/dist/db/libsql.d.mts +1 -1
  227. package/dist/db/postgres.d.mts +1 -1
  228. package/dist/db/sqlite.d.mts +1 -1
  229. package/dist/{default-xLFNSsZ9.mjs → default-IlBaTFxM.mjs} +1 -1
  230. package/dist/{default-xLFNSsZ9.mjs.map → default-IlBaTFxM.mjs.map} +1 -1
  231. package/dist/{device-flow-s6_q3T7A.mjs → device-flow-R23SIbQ2.mjs} +4 -4
  232. package/dist/{device-flow-s6_q3T7A.mjs.map → device-flow-R23SIbQ2.mjs.map} +1 -1
  233. package/dist/{escape-bIyGoW5W.mjs → escape-Ds07EEyu.mjs} +1 -1
  234. package/dist/{escape-bIyGoW5W.mjs.map → escape-Ds07EEyu.mjs.map} +1 -1
  235. package/dist/{index-FfiTQJq2.d.mts → index-B1keaX5Y.d.mts} +43 -12
  236. package/dist/{index-FfiTQJq2.d.mts.map → index-B1keaX5Y.d.mts.map} +1 -1
  237. package/dist/{index-BpYeJO1E.d.mts → index-DR56od45.d.mts} +3 -3
  238. package/dist/{index-BpYeJO1E.d.mts.map → index-DR56od45.d.mts.map} +1 -1
  239. package/dist/index.d.mts +16 -16
  240. package/dist/index.mjs +22 -22
  241. package/dist/{load-B84ohfBk.mjs → load-BBetCvLC.mjs} +1 -1
  242. package/dist/{load-B84ohfBk.mjs.map → load-BBetCvLC.mjs.map} +1 -1
  243. package/dist/{loader-CpZKpFz0.mjs → loader-ZN1ll-d-.mjs} +11 -14
  244. package/dist/loader-ZN1ll-d-.mjs.map +1 -0
  245. package/dist/{manifest-schema-Cj-YrzrF.mjs → manifest-schema-BtwbL_vj.mjs} +55 -2
  246. package/dist/manifest-schema-BtwbL_vj.mjs.map +1 -0
  247. package/dist/media/index.d.mts +1 -1
  248. package/dist/media/local-runtime.d.mts +11 -11
  249. package/dist/media/local-runtime.mjs +2 -2
  250. package/dist/{media-allowlist-CMcoYIjQ.mjs → media-allowlist-Dknq-OFY.mjs} +1 -1
  251. package/dist/{media-allowlist-CMcoYIjQ.mjs.map → media-allowlist-Dknq-OFY.mjs.map} +1 -1
  252. package/dist/media-url-VClf8glU.mjs +26 -0
  253. package/dist/media-url-VClf8glU.mjs.map +1 -0
  254. package/dist/{menus-Dp9xporj.mjs → menus-DrQLusqj.mjs} +6 -33
  255. package/dist/menus-DrQLusqj.mjs.map +1 -0
  256. package/dist/{mode-BjlXswIw.mjs → mode-CO2vQHfq.mjs} +1 -1
  257. package/dist/{mode-BjlXswIw.mjs.map → mode-CO2vQHfq.mjs.map} +1 -1
  258. package/dist/{oauth-authorization-1aPAYjiC.mjs → oauth-authorization-Bw4NdF_S.mjs} +4 -4
  259. package/dist/{oauth-authorization-1aPAYjiC.mjs.map → oauth-authorization-Bw4NdF_S.mjs.map} +1 -1
  260. package/dist/{oauth-clients-8mPDStMv.mjs → oauth-clients-BGGFp57s.mjs} +1 -1
  261. package/dist/{oauth-clients-8mPDStMv.mjs.map → oauth-clients-BGGFp57s.mjs.map} +1 -1
  262. package/dist/{oauth-state-store-BJ7YtrfD.mjs → oauth-state-store-97x0xtN2.mjs} +1 -1
  263. package/dist/{oauth-state-store-BJ7YtrfD.mjs.map → oauth-state-store-97x0xtN2.mjs.map} +1 -1
  264. package/dist/{oauth-user-lookup-BdDSDvjF.mjs → oauth-user-lookup-B_vnZHKO.mjs} +1 -1
  265. package/dist/{oauth-user-lookup-BdDSDvjF.mjs.map → oauth-user-lookup-B_vnZHKO.mjs.map} +1 -1
  266. package/dist/{options-D4MnavW_.d.mts → options-DyYIYpPd.d.mts} +3 -3
  267. package/dist/{options-D4MnavW_.d.mts.map → options-DyYIYpPd.d.mts.map} +1 -1
  268. package/dist/page/index.d.mts +2 -2
  269. package/dist/{passkey-config-BDVM86Tj.mjs → passkey-config-C3QgnQnU.mjs} +1 -1
  270. package/dist/{passkey-config-BDVM86Tj.mjs.map → passkey-config-C3QgnQnU.mjs.map} +1 -1
  271. package/dist/{placeholder-B9lUUEmj.d.mts → placeholder-CVBv5z8k.d.mts} +1 -1
  272. package/dist/{placeholder-B9lUUEmj.d.mts.map → placeholder-CVBv5z8k.d.mts.map} +1 -1
  273. package/dist/plugin-types.d.mts +1 -1
  274. package/dist/plugin-utils.d.mts +9 -9
  275. package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
  276. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  277. package/dist/{public-url-egRHCy1m.mjs → public-url-BFVC2OTJ.mjs} +1 -1
  278. package/dist/{public-url-egRHCy1m.mjs.map → public-url-BFVC2OTJ.mjs.map} +1 -1
  279. package/dist/{query-BFQ029Ts.mjs → query-CbUcI4Xk.mjs} +18 -8
  280. package/dist/query-CbUcI4Xk.mjs.map +1 -0
  281. package/dist/{rate-limit-ClFFUga6.mjs → rate-limit-C7hjdkS5.mjs} +1 -1
  282. package/dist/{rate-limit-ClFFUga6.mjs.map → rate-limit-C7hjdkS5.mjs.map} +1 -1
  283. package/dist/{redirect-Cw3JTlmj.mjs → redirect-B_q19j4v.mjs} +1 -1
  284. package/dist/{redirect-Cw3JTlmj.mjs.map → redirect-B_q19j4v.mjs.map} +1 -1
  285. package/dist/{redirects-DEygMrRO.mjs → redirects-CCbCqCCd.mjs} +4 -2
  286. package/dist/redirects-CCbCqCCd.mjs.map +1 -0
  287. package/dist/{redirects-OIu6vQ2i.mjs → redirects-DxVoR7PI.mjs} +1 -1
  288. package/dist/{redirects-OIu6vQ2i.mjs.map → redirects-DxVoR7PI.mjs.map} +1 -1
  289. package/dist/request-context.d.mts +7 -0
  290. package/dist/request-context.d.mts.map +1 -1
  291. package/dist/request-context.mjs +2 -1
  292. package/dist/request-context.mjs.map +1 -1
  293. package/dist/{runner-BcRuXq_h.d.mts → runner-DTdhuI9i.d.mts} +2 -2
  294. package/dist/{runner-BcRuXq_h.d.mts.map → runner-DTdhuI9i.d.mts.map} +1 -1
  295. package/dist/runtime.d.mts +10 -10
  296. package/dist/runtime.mjs +1 -1
  297. package/dist/{schema-CS7Eg5gh.mjs → schema-C1E70ug_.mjs} +2 -2
  298. package/dist/{schema-CS7Eg5gh.mjs.map → schema-C1E70ug_.mjs.map} +1 -1
  299. package/dist/{search-o-aQzHI1.mjs → search-B3SGZw91.mjs} +2 -2
  300. package/dist/{search-o-aQzHI1.mjs.map → search-B3SGZw91.mjs.map} +1 -1
  301. package/dist/{secrets-C_ZtRos3.mjs → secrets-ChPTmy9x.mjs} +1 -1
  302. package/dist/{secrets-C_ZtRos3.mjs.map → secrets-ChPTmy9x.mjs.map} +1 -1
  303. package/dist/{sections-DhsZ0ns9.mjs → sections-D_lVzwRZ.mjs} +2 -2
  304. package/dist/{sections-DhsZ0ns9.mjs.map → sections-D_lVzwRZ.mjs.map} +1 -1
  305. package/dist/seed/index.d.mts +2 -2
  306. package/dist/seed/index.mjs +6 -6
  307. package/dist/seo/index.d.mts +1 -1
  308. package/dist/seo/index.d.mts.map +1 -1
  309. package/dist/seo/index.mjs +3 -12
  310. package/dist/seo/index.mjs.map +1 -1
  311. package/dist/{seo-DfjLvu8i.mjs → seo-D_LPkOtu.mjs} +4 -3
  312. package/dist/seo-D_LPkOtu.mjs.map +1 -0
  313. package/dist/{service-DAxg8RPR.mjs → service-ChDcsTBs.mjs} +2 -2
  314. package/dist/{service-DAxg8RPR.mjs.map → service-ChDcsTBs.mjs.map} +1 -1
  315. package/dist/{settings-DIsbHTRE.mjs → settings-Cv47v9u8.mjs} +2 -2
  316. package/dist/{settings-DIsbHTRE.mjs.map → settings-Cv47v9u8.mjs.map} +1 -1
  317. package/dist/settings-DfxiWY_s.mjs +411 -0
  318. package/dist/settings-DfxiWY_s.mjs.map +1 -0
  319. package/dist/{setup-complete-Yuv78yua.mjs → setup-complete-yvPE4OsP.mjs} +1 -1
  320. package/dist/{setup-complete-Yuv78yua.mjs.map → setup-complete-yvPE4OsP.mjs.map} +1 -1
  321. package/dist/{setup-nonce-Bm0uKqmf.mjs → setup-nonce-C9aFzb94.mjs} +1 -1
  322. package/dist/{setup-nonce-Bm0uKqmf.mjs.map → setup-nonce-C9aFzb94.mjs.map} +1 -1
  323. package/dist/{site-url-mEVmwIFi.mjs → site-url-CnHlmAs9.mjs} +1 -1
  324. package/dist/{site-url-mEVmwIFi.mjs.map → site-url-CnHlmAs9.mjs.map} +1 -1
  325. package/dist/storage/local.d.mts +1 -1
  326. package/dist/storage/s3.d.mts +1 -1
  327. package/dist/{taxonomies-UusDXv3C.mjs → taxonomies-BILwiyGk.mjs} +2 -2
  328. package/dist/{taxonomies-UusDXv3C.mjs.map → taxonomies-BILwiyGk.mjs.map} +1 -1
  329. package/dist/{taxonomies-BEW7S5AI.mjs → taxonomies-BdAmbOwx.mjs} +46 -9
  330. package/dist/taxonomies-BdAmbOwx.mjs.map +1 -0
  331. package/dist/{transport-BwQeeY2p.d.mts → transport-B7PPP2CC.d.mts} +1 -1
  332. package/dist/{transport-BwQeeY2p.d.mts.map → transport-B7PPP2CC.d.mts.map} +1 -1
  333. package/dist/{transport--Ck3RBin.mjs → transport-CmpLD7W3.mjs} +1 -1
  334. package/dist/{transport--Ck3RBin.mjs.map → transport-CmpLD7W3.mjs.map} +1 -1
  335. package/dist/{types-DWnN7weG.d.mts → types-BFgrqwSk.d.mts} +1 -1
  336. package/dist/{types-DWnN7weG.d.mts.map → types-BFgrqwSk.d.mts.map} +1 -1
  337. package/dist/{types-Qa7-HJJC.d.mts → types-BH8-30hc.d.mts} +1 -1
  338. package/dist/{types-Qa7-HJJC.d.mts.map → types-BH8-30hc.d.mts.map} +1 -1
  339. package/dist/{types-OT_Es5mp.d.mts → types-BPzXTV9x.d.mts} +1 -1
  340. package/dist/{types-OT_Es5mp.d.mts.map → types-BPzXTV9x.d.mts.map} +1 -1
  341. package/dist/{types-DbCWhHet.d.mts → types-BUUVn1zr.d.mts} +2 -2
  342. package/dist/types-BUUVn1zr.d.mts.map +1 -0
  343. package/dist/{types-DMwSpvcw.d.mts → types-CPAPl93j.d.mts} +9 -3
  344. package/dist/{types-DMwSpvcw.d.mts.map → types-CPAPl93j.d.mts.map} +1 -1
  345. package/dist/types-CZI4E3qG.mjs +3 -0
  346. package/dist/{types-kwqCOUxj.d.mts → types-D4kUqbHh.d.mts} +1 -1
  347. package/dist/{types-kwqCOUxj.d.mts.map → types-D4kUqbHh.d.mts.map} +1 -1
  348. package/dist/{types-WVmpZBJV.d.mts → types-DTniiNto.d.mts} +2 -2
  349. package/dist/{types-WVmpZBJV.d.mts.map → types-DTniiNto.d.mts.map} +1 -1
  350. package/dist/types-DZk_y-MU.mjs.map +1 -1
  351. package/dist/{types-DX6v9KzJ.d.mts → types-S15DXXNi.d.mts} +1 -1
  352. package/dist/{types-DX6v9KzJ.d.mts.map → types-S15DXXNi.d.mts.map} +1 -1
  353. package/dist/{validate-ZP9Dvg0P.mjs → validate-Bz4vqcX1.mjs} +1 -1
  354. package/dist/{validate-ZP9Dvg0P.mjs.map → validate-Bz4vqcX1.mjs.map} +1 -1
  355. package/dist/{validate-BPAHUSge.d.mts → validate-CNwkPWzz.d.mts} +5 -5
  356. package/dist/{validate-BPAHUSge.d.mts.map → validate-CNwkPWzz.d.mts.map} +1 -1
  357. package/dist/{validation-CE5i4q0c.mjs → validation-DgGTJm3u.mjs} +1 -1
  358. package/dist/{validation-CE5i4q0c.mjs.map → validation-DgGTJm3u.mjs.map} +1 -1
  359. package/dist/version-D-5txk2m.mjs +7 -0
  360. package/dist/{version-Dw0JXu45.mjs.map → version-D-5txk2m.mjs.map} +1 -1
  361. package/dist/{widgets-ClEnYQCH.mjs → widgets-DZfmAbE4.mjs} +47 -44
  362. package/dist/widgets-DZfmAbE4.mjs.map +1 -0
  363. package/package.json +10 -10
  364. package/src/api/handlers/marketplace.ts +2 -5
  365. package/src/api/handlers/registry.ts +70 -0
  366. package/src/api/handlers/seo.ts +9 -1
  367. package/src/api/schemas/schema.ts +13 -1
  368. package/src/astro/middleware.ts +20 -6
  369. package/src/astro/routes/api/schema/index.ts +7 -15
  370. package/src/astro/routes/sitemap-[collection].xml.ts +13 -2
  371. package/src/cli/commands/bundle-utils.ts +2 -0
  372. package/src/cli/commands/secrets.ts +2 -2
  373. package/src/database/instrumentation.ts +13 -0
  374. package/src/emdash-runtime.ts +31 -25
  375. package/src/loader.ts +24 -15
  376. package/src/plugins/manifest-schema.ts +75 -0
  377. package/src/plugins/marketplace.ts +2 -5
  378. package/src/plugins/types.ts +12 -0
  379. package/src/query.ts +13 -2
  380. package/src/request-context.ts +8 -0
  381. package/src/schema/types.ts +11 -1
  382. package/src/seo/index.ts +2 -28
  383. package/src/seo/media-url.ts +32 -0
  384. package/src/settings/index.ts +32 -40
  385. package/src/taxonomies/index.ts +78 -12
  386. package/src/utils/isolate-cache.ts +189 -0
  387. package/src/widgets/index.ts +57 -54
  388. package/dist/api-BZ6bhjYs.mjs.map +0 -1
  389. package/dist/byline-fields-DYXKDuNX.d.mts.map +0 -1
  390. package/dist/loader-CpZKpFz0.mjs.map +0 -1
  391. package/dist/manifest-schema-Cj-YrzrF.mjs.map +0 -1
  392. package/dist/menus-Dp9xporj.mjs.map +0 -1
  393. package/dist/query-BFQ029Ts.mjs.map +0 -1
  394. package/dist/redirects-DEygMrRO.mjs.map +0 -1
  395. package/dist/seo-DfjLvu8i.mjs.map +0 -1
  396. package/dist/settings-B1p-gPUK.mjs +0 -235
  397. package/dist/settings-B1p-gPUK.mjs.map +0 -1
  398. package/dist/taxonomies-BEW7S5AI.mjs.map +0 -1
  399. package/dist/types-Cj2S6FuC.mjs +0 -3
  400. package/dist/types-DbCWhHet.d.mts.map +0 -1
  401. package/dist/version-Dw0JXu45.mjs +0 -7
  402. package/dist/widgets-ClEnYQCH.mjs.map +0 -1
  403. /package/dist/{api-tokens-B6VgoE6M.mjs → api-tokens-Oq39ba-Z.mjs} +0 -0
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Isolate-lifetime async value cache with single-flight and poison-immunity.
3
+ *
4
+ * Built for the "compute once per isolate, read on every request" caches
5
+ * (site settings, search-health verification, ...). These must coalesce
6
+ * concurrent cold-isolate reads into one query — but the obvious way to do
7
+ * that, caching the in-flight *promise* on an isolate-global and awaiting it
8
+ * from later requests, is unsafe on workerd: if the request that created the
9
+ * promise is cancelled mid-await (client disconnect, context teardown), its
10
+ * continuation never runs, so the promise neither resolves nor rejects. Every
11
+ * later request that awaits that shared promise then hangs until the isolate
12
+ * is evicted (observed as 524s at the 100s wall, near-zero CPU). A `.catch`
13
+ * that clears the cache doesn't help — a cancelled request doesn't reject.
14
+ *
15
+ * This cache stores the resolved *value* (not a promise) and coalesces via
16
+ * `initWithLock`: one request becomes the owner and runs `fetch`, everyone
17
+ * else polls for the published value and never awaits the owner's promise.
18
+ * A cancelled owner can therefore never strand a waiter — the worst case is
19
+ * the lock looks held until `deadlineMs`, then the next caller reclaims. The
20
+ * owner's `fetch` is also anchored (waitUntil) so a cancelled originator's
21
+ * query still completes and populates the cache, and bounded by
22
+ * `ownerTimeoutMs` so a genuinely stuck fetch reclaims instead of hanging.
23
+ *
24
+ * Invalidation bumps `version`; reads compare against the version captured at
25
+ * call time and refetch on mismatch.
26
+ */
27
+
28
+ import { createInitLock, type InitLock, initWithLock } from "./init-lock.js";
29
+
30
+ export interface IsolateCache<T> {
31
+ /** Last resolved value, valid only when `hasValue` is true. */
32
+ value: T | null;
33
+ /**
34
+ * Presence flag, separate from `value` so that falsy/`undefined`/`void`
35
+ * results cache correctly (a plain null check can't distinguish "cached
36
+ * undefined" from "never fetched").
37
+ */
38
+ hasValue: boolean;
39
+ /** Invalidation counter; bumped by `invalidateIsolateCache`. */
40
+ version: number;
41
+ /** The `version` the cached value was fetched at. */
42
+ valueVersion: number;
43
+ /** Reclaimable single-flight lock (see init-lock.ts). */
44
+ lock: InitLock;
45
+ }
46
+
47
+ export function createIsolateCache<T>(): IsolateCache<T> {
48
+ return { value: null, hasValue: false, version: 0, valueVersion: -1, lock: createInitLock() };
49
+ }
50
+
51
+ /**
52
+ * Force the next `isolateCachedAsync` call to refetch. An in-flight owner
53
+ * fetched at the old version will not publish into the new version, so its
54
+ * result is ignored by subsequent reads.
55
+ */
56
+ export function invalidateIsolateCache(cache: IsolateCache<unknown>): void {
57
+ cache.version++;
58
+ cache.hasValue = false;
59
+ cache.value = null;
60
+ cache.valueVersion = -1;
61
+ // Free the single-flight lock so a reader at the new version starts the
62
+ // refetch immediately instead of waiting out a stale owner's deadline. A
63
+ // still-running old-version owner can neither publish into the new version
64
+ // (version gate) nor clobber a new owner (claim gate), so releasing here
65
+ // is safe; the worst case is one brief duplicate fetch.
66
+ cache.lock.ownerStartedAt = null;
67
+ }
68
+
69
+ /**
70
+ * Headroom between the owner's own timeout and the waiter reclaim deadline.
71
+ * The reclaim deadline must sit *above* `ownerTimeoutMs` so a slow-but-live
72
+ * owner times out (and releases the lock) before a waiter would reclaim it —
73
+ * otherwise a fetch slower than the deadline is superseded before it can
74
+ * publish, and steady traffic turns that into a self-sustaining stampede.
75
+ */
76
+ const RECLAIM_HEADROOM_MS = 5_000;
77
+
78
+ export interface IsolateCachedOptions {
79
+ /**
80
+ * Hand the in-flight fetch to the host's lifetime extender (waitUntil via
81
+ * `after()`), so a cancelled originating request still drives it to
82
+ * completion and populates the cache.
83
+ */
84
+ anchor?: (promise: Promise<void>) => void;
85
+ /** Reclaim the single-flight lock if the owner holds it past this. */
86
+ deadlineMs?: number;
87
+ /** Waiter poll interval. */
88
+ pollMs?: number;
89
+ /** Waiter gives up and throws after this long rather than hanging. */
90
+ maxWaitMs?: number;
91
+ /**
92
+ * Bound the owner's own `fetch`: if it doesn't settle within this, the
93
+ * owner rejects (and releases the lock) instead of waiting indefinitely.
94
+ * The anchored copy keeps running, so a slow-but-live fetch can still
95
+ * publish for a later caller. Omit to leave the owner unbounded.
96
+ */
97
+ ownerTimeoutMs?: number;
98
+ }
99
+
100
+ /** Boxed cache hit so a `void`/falsy value is still distinguishable from a miss. */
101
+ interface Box<T> {
102
+ v: T;
103
+ }
104
+
105
+ function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
106
+ return new Promise<T>((resolve, reject) => {
107
+ const timer = setTimeout(() => {
108
+ reject(new Error(`isolateCachedAsync: owner fetch exceeded ${ms}ms`));
109
+ }, ms);
110
+ // Settle from the underlying promise (whichever wins the race with the
111
+ // timer), and always clear the timer so a resolved fetch doesn't leave
112
+ // a pending timeout holding the isolate alive.
113
+ promise.then(resolve, reject).finally(() => {
114
+ clearTimeout(timer);
115
+ });
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Return the cached value for `cache`, computing it via `fetch` under a
121
+ * single-flight lock on a miss. Concurrent callers coalesce onto one fetch;
122
+ * a cancelled owner cannot poison later callers (see file header).
123
+ */
124
+ export function isolateCachedAsync<T>(
125
+ cache: IsolateCache<T>,
126
+ fetch: () => Promise<T>,
127
+ options: IsolateCachedOptions = {},
128
+ ): Promise<T> {
129
+ // Capture the version once: a value published at this version satisfies
130
+ // this call; an invalidation that lands mid-fetch makes the published
131
+ // value stale for *later* calls (which captured the newer version) but
132
+ // still valid for this one.
133
+ const versionAtCall = cache.version;
134
+
135
+ // Ignore a non-positive / non-finite owner timeout rather than letting it
136
+ // degenerate into an instant-reject (setTimeout coerces NaN/0 to ~0ms).
137
+ const ownerTimeoutMs =
138
+ options.ownerTimeoutMs !== undefined &&
139
+ Number.isFinite(options.ownerTimeoutMs) &&
140
+ options.ownerTimeoutMs > 0
141
+ ? options.ownerTimeoutMs
142
+ : undefined;
143
+
144
+ // Keep the reclaim deadline above the owner timeout (see RECLAIM_HEADROOM_MS):
145
+ // the owner's own timeout, not a waiter reclaim, is the primary release.
146
+ const deadlineMs =
147
+ ownerTimeoutMs === undefined
148
+ ? options.deadlineMs
149
+ : Math.max(options.deadlineMs ?? 0, ownerTimeoutMs + RECLAIM_HEADROOM_MS);
150
+
151
+ return initWithLock<Box<T>>(
152
+ cache.lock,
153
+ () =>
154
+ cache.hasValue && cache.valueVersion === versionAtCall
155
+ ? // eslint-disable-next-line typescript/no-unsafe-type-assertion -- hasValue gates that `value` holds a real T
156
+ ({ v: cache.value as T } satisfies Box<T>)
157
+ : null,
158
+ (isCurrentClaim) => {
159
+ // The real work, anchored independently so a cancelled owner's
160
+ // fetch still settles and publishes. Publication is gated on the
161
+ // claim so a reclaimed slow owner can't clobber the reclaimer's
162
+ // value (same contract as initWithLock's own callers).
163
+ const real = (async (): Promise<Box<T>> => {
164
+ const value = await fetch();
165
+ if (isCurrentClaim()) {
166
+ cache.value = value;
167
+ cache.hasValue = true;
168
+ cache.valueVersion = versionAtCall;
169
+ }
170
+ return { v: value };
171
+ })();
172
+ // Anchor the real fetch (not the timeout race): this is what must
173
+ // survive a cancelled owner and run to publication. initWithLock is
174
+ // left to manage only the lock; we don't double-anchor.
175
+ options.anchor?.(
176
+ real.then(
177
+ () => undefined,
178
+ () => undefined,
179
+ ),
180
+ );
181
+ return ownerTimeoutMs === undefined ? real : withTimeout(real, ownerTimeoutMs);
182
+ },
183
+ {
184
+ deadlineMs,
185
+ pollMs: options.pollMs,
186
+ maxWaitMs: options.maxWaitMs,
187
+ },
188
+ ).then((box) => box.v);
189
+ }
@@ -1,4 +1,5 @@
1
1
  import { getDb } from "../loader.js";
2
+ import { requestCached } from "../request-cache.js";
2
3
  import { getWidgetComponents as getComponentRegistry } from "./components.js";
3
4
  import type { Widget, WidgetArea, WidgetRow, WidgetComponentDef } from "./types.js";
4
5
 
@@ -22,62 +23,64 @@ export type {
22
23
  * row with null widget columns, which we skip when mapping.
23
24
  */
24
25
  export async function getWidgetArea(name: string): Promise<WidgetArea | null> {
25
- const db = await getDb();
26
- const rows = await db
27
- .selectFrom("_emdash_widget_areas as a")
28
- .leftJoin("_emdash_widgets as w", "w.area_id", "a.id")
29
- .select([
30
- "a.id as a_id",
31
- "a.name as a_name",
32
- "a.label as a_label",
33
- "a.description as a_description",
34
- "w.id as w_id",
35
- "w.type as w_type",
36
- "w.title as w_title",
37
- "w.content as w_content",
38
- "w.menu_name as w_menu_name",
39
- "w.component_id as w_component_id",
40
- "w.component_props as w_component_props",
41
- "w.area_id as w_area_id",
42
- "w.sort_order as w_sort_order",
43
- "w.created_at as w_created_at",
44
- ])
45
- .where("a.name", "=", name)
46
- .orderBy("w.sort_order", "asc")
47
- .execute();
26
+ return requestCached(`widget-area:${name}`, async () => {
27
+ const db = await getDb();
28
+ const rows = await db
29
+ .selectFrom("_emdash_widget_areas as a")
30
+ .leftJoin("_emdash_widgets as w", "w.area_id", "a.id")
31
+ .select([
32
+ "a.id as a_id",
33
+ "a.name as a_name",
34
+ "a.label as a_label",
35
+ "a.description as a_description",
36
+ "w.id as w_id",
37
+ "w.type as w_type",
38
+ "w.title as w_title",
39
+ "w.content as w_content",
40
+ "w.menu_name as w_menu_name",
41
+ "w.component_id as w_component_id",
42
+ "w.component_props as w_component_props",
43
+ "w.area_id as w_area_id",
44
+ "w.sort_order as w_sort_order",
45
+ "w.created_at as w_created_at",
46
+ ])
47
+ .where("a.name", "=", name)
48
+ .orderBy("w.sort_order", "asc")
49
+ .execute();
48
50
 
49
- const first = rows[0];
50
- if (!first) return null;
51
- const widgets: Widget[] = [];
52
- for (const row of rows) {
53
- if (row.w_id === null) continue; // area has no widgets (left-join null row)
54
- // Left-join makes every w_* column nullable in the type; at runtime
55
- // they're all non-null once w_id is (we match on widgets.area_id, so
56
- // a widget row always has the not-null columns filled). Cast is the
57
- // price of that structural fact.
58
- // eslint-disable-next-line typescript/no-unsafe-type-assertion -- left-join row is non-null when w_id is set; see above
59
- const widgetRow = {
60
- id: row.w_id,
61
- type: row.w_type,
62
- title: row.w_title,
63
- content: row.w_content,
64
- menu_name: row.w_menu_name,
65
- component_id: row.w_component_id,
66
- component_props: row.w_component_props,
67
- area_id: row.w_area_id,
68
- sort_order: row.w_sort_order,
69
- created_at: row.w_created_at,
70
- } as WidgetRow;
71
- widgets.push(rowToWidget(widgetRow));
72
- }
51
+ const first = rows[0];
52
+ if (!first) return null;
53
+ const widgets: Widget[] = [];
54
+ for (const row of rows) {
55
+ if (row.w_id === null) continue; // area has no widgets (left-join null row)
56
+ // Left-join makes every w_* column nullable in the type; at runtime
57
+ // they're all non-null once w_id is (we match on widgets.area_id, so
58
+ // a widget row always has the not-null columns filled). Cast is the
59
+ // price of that structural fact.
60
+ // eslint-disable-next-line typescript/no-unsafe-type-assertion -- left-join row is non-null when w_id is set; see above
61
+ const widgetRow = {
62
+ id: row.w_id,
63
+ type: row.w_type,
64
+ title: row.w_title,
65
+ content: row.w_content,
66
+ menu_name: row.w_menu_name,
67
+ component_id: row.w_component_id,
68
+ component_props: row.w_component_props,
69
+ area_id: row.w_area_id,
70
+ sort_order: row.w_sort_order,
71
+ created_at: row.w_created_at,
72
+ } as WidgetRow;
73
+ widgets.push(rowToWidget(widgetRow));
74
+ }
73
75
 
74
- return {
75
- id: first.a_id,
76
- name: first.a_name,
77
- label: first.a_label,
78
- description: first.a_description ?? undefined,
79
- widgets,
80
- };
76
+ return {
77
+ id: first.a_id,
78
+ name: first.a_name,
79
+ label: first.a_label,
80
+ description: first.a_description ?? undefined,
81
+ widgets,
82
+ };
83
+ });
81
84
  }
82
85
 
83
86
  /**