dineway 0.1.3 → 0.1.5

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 (296) hide show
  1. package/README.md +6 -3
  2. package/dist/{apply-CAPvMfoU.mjs → apply-iVSqz2qs.mjs} +132 -39
  3. package/dist/astro/index.d.mts +18 -9
  4. package/dist/astro/index.mjs +238 -16
  5. package/dist/astro/middleware/auth.d.mts +16 -5
  6. package/dist/astro/middleware/auth.mjs +74 -37
  7. package/dist/astro/middleware/redirect.mjs +24 -8
  8. package/dist/astro/middleware/request-context.mjs +18 -5
  9. package/dist/astro/middleware/setup.mjs +1 -1
  10. package/dist/astro/middleware.mjs +411 -169
  11. package/dist/astro/types.d.mts +25 -8
  12. package/dist/{byline-DeWCMU_i.mjs → byline-OhH2dlRu.mjs} +6 -21
  13. package/dist/{bylines-DyqBV9EQ.mjs → bylines-BGpD9_hy.mjs} +16 -6
  14. package/dist/cache-BdSY-gQN.mjs +42 -0
  15. package/dist/chunks--4F8ddV4.mjs +18 -0
  16. package/dist/cli/index.mjs +935 -15
  17. package/dist/client/external-auth-headers.d.mts +1 -1
  18. package/dist/client/index.d.mts +11 -3
  19. package/dist/client/index.mjs +4 -3
  20. package/dist/{connection-C9pxzuag.mjs → connection-BCNICDWN.mjs} +22 -5
  21. package/dist/{content-zSgdNmnt.mjs → content-DWi4d0rT.mjs} +41 -2
  22. package/dist/database/instrumentation.d.mts +34 -0
  23. package/dist/database/instrumentation.mjs +53 -0
  24. package/dist/db/index.d.mts +3 -3
  25. package/dist/db/index.mjs +2 -2
  26. package/dist/db/libsql.d.mts +1 -1
  27. package/dist/db/libsql.mjs +11 -5
  28. package/dist/db/postgres.d.mts +1 -1
  29. package/dist/db/sqlite.d.mts +1 -1
  30. package/dist/db/sqlite.mjs +7 -1
  31. package/dist/db-errors-CEqD7qH9.mjs +23 -0
  32. package/dist/{default-WYlzADZL.mjs → default-VjJyuuG9.mjs} +2 -0
  33. package/dist/{dialect-helpers-B9uSp2GJ.mjs → dialect-helpers-DhTzaUxP.mjs} +3 -0
  34. package/dist/{error-DrxtnGPg.mjs → error-BmL6QipT.mjs} +7 -3
  35. package/dist/{index-C-jx21qs.d.mts → index-yvc6E_17.d.mts} +157 -30
  36. package/dist/index.d.mts +11 -11
  37. package/dist/index.mjs +24 -22
  38. package/dist/{loader-qKmo0wAY.mjs → loader-sMG4TZ-u.mjs} +9 -3
  39. package/dist/media/index.d.mts +1 -1
  40. package/dist/media/index.mjs +1 -1
  41. package/dist/media/local-runtime.d.mts +7 -7
  42. package/dist/page/index.d.mts +10 -2
  43. package/dist/page/index.mjs +22 -1
  44. package/dist/patterns-CrCYkMBb.mjs +92 -0
  45. package/dist/{placeholder-bOx1xCTY.d.mts → placeholder--wOi4TbO.d.mts} +1 -1
  46. package/dist/{placeholder-B3knXwNc.mjs → placeholder-Cp8g5Emj.mjs} +1 -1
  47. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  48. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  49. package/dist/{query-BiaPl_g2.mjs → query-kDmwCsHh.mjs} +118 -50
  50. package/dist/{redirect-JPqLAbxa.mjs → redirect-DnEWAkVg.mjs} +43 -99
  51. package/dist/{registry-DSd1GWB8.mjs → registry-C0zjeB9P.mjs} +191 -123
  52. package/dist/request-cache-Dk5qPSOx.mjs +66 -0
  53. package/dist/request-context.d.mts +4 -16
  54. package/dist/{runner-B5l1JfOj.d.mts → runner-CFI6B6J2.d.mts} +1 -1
  55. package/dist/{runner-BGUGywgG.mjs → runner-DWZm2KQm.mjs} +589 -137
  56. package/dist/runtime.d.mts +6 -6
  57. package/dist/runtime.mjs +2 -2
  58. package/dist/{search-BNruJHDL.mjs → search-ByRGV2pq.mjs} +570 -424
  59. package/dist/seed/index.d.mts +2 -2
  60. package/dist/seed/index.mjs +11 -10
  61. package/dist/seo/index.d.mts +1 -1
  62. package/dist/storage/local.d.mts +1 -1
  63. package/dist/storage/local.mjs +1 -1
  64. package/dist/storage/s3.d.mts +11 -3
  65. package/dist/storage/s3.mjs +78 -15
  66. package/dist/taxonomies-1s5PaS_8.mjs +266 -0
  67. package/dist/transaction-Cn2rjY78.mjs +27 -0
  68. package/dist/{types-BgQeVaPj.d.mts → types-BuMDPy5C.d.mts} +52 -3
  69. package/dist/{types-DuNbGKjF.mjs → types-COeOq9nK.mjs} +6 -1
  70. package/dist/{types-ju-_ORz7.d.mts → types-CWbdtiux.d.mts} +13 -5
  71. package/dist/{types-D38djUXv.d.mts → types-Cj0KMIZV.d.mts} +16 -3
  72. package/dist/{types-DkvMXalq.d.mts → types-DOrVigru.d.mts} +159 -0
  73. package/dist/{validate-CXnRKfJK.mjs → validate-BZ5wnLLp.mjs} +2 -1
  74. package/dist/{validate-DVKJJ-M_.d.mts → validate-IPf8n4Fj.d.mts} +4 -51
  75. package/dist/{validate-CqRJb_xU.mjs → validate-VPnKoIzW.mjs} +10 -10
  76. package/dist/version-BKXPsfmJ.mjs +6 -0
  77. package/package.json +53 -39
  78. package/src/astro/routes/PluginRegistry.tsx +21 -0
  79. package/src/astro/routes/admin.astro +99 -0
  80. package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
  81. package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
  82. package/src/astro/routes/api/admin/api-tokens/[id].ts +44 -0
  83. package/src/astro/routes/api/admin/api-tokens/index.ts +90 -0
  84. package/src/astro/routes/api/admin/briefing.ts +76 -0
  85. package/src/astro/routes/api/admin/bylines/[id]/index.ts +90 -0
  86. package/src/astro/routes/api/admin/bylines/index.ts +74 -0
  87. package/src/astro/routes/api/admin/comments/[id]/status.ts +120 -0
  88. package/src/astro/routes/api/admin/comments/[id].ts +64 -0
  89. package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
  90. package/src/astro/routes/api/admin/comments/counts.ts +30 -0
  91. package/src/astro/routes/api/admin/comments/index.ts +46 -0
  92. package/src/astro/routes/api/admin/context/[id]/history.ts +35 -0
  93. package/src/astro/routes/api/admin/context/[id]/index.ts +35 -0
  94. package/src/astro/routes/api/admin/context/[id]/review.ts +57 -0
  95. package/src/astro/routes/api/admin/context/[id]/supersede.ts +58 -0
  96. package/src/astro/routes/api/admin/context/diff.ts +35 -0
  97. package/src/astro/routes/api/admin/context/index.ts +69 -0
  98. package/src/astro/routes/api/admin/context/stale.ts +35 -0
  99. package/src/astro/routes/api/admin/hitl-requests/[id]/index.ts +38 -0
  100. package/src/astro/routes/api/admin/hitl-requests/[id]/resolve.ts +54 -0
  101. package/src/astro/routes/api/admin/hitl-requests/index.ts +38 -0
  102. package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +132 -0
  103. package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
  104. package/src/astro/routes/api/admin/oauth-clients/[id].ts +137 -0
  105. package/src/astro/routes/api/admin/oauth-clients/index.ts +95 -0
  106. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +91 -0
  107. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +91 -0
  108. package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
  109. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +98 -0
  110. package/src/astro/routes/api/admin/plugins/[id]/update.ts +154 -0
  111. package/src/astro/routes/api/admin/plugins/index.ts +32 -0
  112. package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +62 -0
  113. package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
  114. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +135 -0
  115. package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
  116. package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
  117. package/src/astro/routes/api/admin/review-requests/[id]/index.ts +35 -0
  118. package/src/astro/routes/api/admin/review-requests/[id]/resolve.ts +52 -0
  119. package/src/astro/routes/api/admin/review-requests/index.ts +35 -0
  120. package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
  121. package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +62 -0
  122. package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
  123. package/src/astro/routes/api/admin/users/[id]/disable.ts +72 -0
  124. package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
  125. package/src/astro/routes/api/admin/users/[id]/index.ts +166 -0
  126. package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
  127. package/src/astro/routes/api/admin/users/index.ts +66 -0
  128. package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
  129. package/src/astro/routes/api/auth/invite/accept.ts +52 -0
  130. package/src/astro/routes/api/auth/invite/complete.ts +86 -0
  131. package/src/astro/routes/api/auth/invite/index.ts +99 -0
  132. package/src/astro/routes/api/auth/invite/register-options.ts +73 -0
  133. package/src/astro/routes/api/auth/logout.ts +40 -0
  134. package/src/astro/routes/api/auth/magic-link/send.ts +90 -0
  135. package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
  136. package/src/astro/routes/api/auth/me.ts +60 -0
  137. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +221 -0
  138. package/src/astro/routes/api/auth/oauth/[provider].ts +120 -0
  139. package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
  140. package/src/astro/routes/api/auth/passkey/index.ts +54 -0
  141. package/src/astro/routes/api/auth/passkey/options.ts +85 -0
  142. package/src/astro/routes/api/auth/passkey/register/options.ts +88 -0
  143. package/src/astro/routes/api/auth/passkey/register/verify.ts +119 -0
  144. package/src/astro/routes/api/auth/passkey/verify.ts +72 -0
  145. package/src/astro/routes/api/auth/signup/complete.ts +87 -0
  146. package/src/astro/routes/api/auth/signup/request.ts +89 -0
  147. package/src/astro/routes/api/auth/signup/verify.ts +53 -0
  148. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +310 -0
  149. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
  150. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +68 -0
  151. package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +77 -0
  152. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +42 -0
  153. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
  154. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +100 -0
  155. package/src/astro/routes/api/content/[collection]/[id]/restore.ts +64 -0
  156. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
  157. package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +129 -0
  158. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +143 -0
  159. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +50 -0
  160. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +69 -0
  161. package/src/astro/routes/api/content/[collection]/[id].ts +173 -0
  162. package/src/astro/routes/api/content/[collection]/index.ts +103 -0
  163. package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
  164. package/src/astro/routes/api/dashboard.ts +32 -0
  165. package/src/astro/routes/api/dev/emails.ts +36 -0
  166. package/src/astro/routes/api/health.ts +54 -0
  167. package/src/astro/routes/api/import/probe.ts +47 -0
  168. package/src/astro/routes/api/import/wordpress/analyze.ts +523 -0
  169. package/src/astro/routes/api/import/wordpress/execute.ts +330 -0
  170. package/src/astro/routes/api/import/wordpress/media.ts +338 -0
  171. package/src/astro/routes/api/import/wordpress/prepare.ts +212 -0
  172. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +425 -0
  173. package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
  174. package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
  175. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +399 -0
  176. package/src/astro/routes/api/manifest.ts +75 -0
  177. package/src/astro/routes/api/mcp.ts +125 -0
  178. package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
  179. package/src/astro/routes/api/media/[id].ts +145 -0
  180. package/src/astro/routes/api/media/file/[...key].ts +79 -0
  181. package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +91 -0
  182. package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
  183. package/src/astro/routes/api/media/providers/index.ts +30 -0
  184. package/src/astro/routes/api/media/upload-url.ts +146 -0
  185. package/src/astro/routes/api/media.ts +204 -0
  186. package/src/astro/routes/api/menus/[name]/items.ts +206 -0
  187. package/src/astro/routes/api/menus/[name]/reorder.ts +79 -0
  188. package/src/astro/routes/api/menus/[name].ts +145 -0
  189. package/src/astro/routes/api/menus/index.ts +91 -0
  190. package/src/astro/routes/api/oauth/authorize.ts +430 -0
  191. package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
  192. package/src/astro/routes/api/oauth/device/code.ts +56 -0
  193. package/src/astro/routes/api/oauth/device/token.ts +70 -0
  194. package/src/astro/routes/api/oauth/register.ts +182 -0
  195. package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
  196. package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
  197. package/src/astro/routes/api/oauth/token.ts +195 -0
  198. package/src/astro/routes/api/openapi.json.ts +33 -0
  199. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +109 -0
  200. package/src/astro/routes/api/redirects/404s/index.ts +72 -0
  201. package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
  202. package/src/astro/routes/api/redirects/[id].ts +183 -0
  203. package/src/astro/routes/api/redirects/index.ts +100 -0
  204. package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
  205. package/src/astro/routes/api/revisions/[revisionId]/restore.ts +62 -0
  206. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +104 -0
  207. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +67 -0
  208. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +45 -0
  209. package/src/astro/routes/api/schema/collections/[slug]/index.ts +107 -0
  210. package/src/astro/routes/api/schema/collections/index.ts +61 -0
  211. package/src/astro/routes/api/schema/index.ts +109 -0
  212. package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
  213. package/src/astro/routes/api/schema/orphans/index.ts +26 -0
  214. package/src/astro/routes/api/search/enable.ts +64 -0
  215. package/src/astro/routes/api/search/index.ts +52 -0
  216. package/src/astro/routes/api/search/rebuild.ts +72 -0
  217. package/src/astro/routes/api/search/stats.ts +35 -0
  218. package/src/astro/routes/api/search/suggest.ts +50 -0
  219. package/src/astro/routes/api/sections/[slug].ts +203 -0
  220. package/src/astro/routes/api/sections/index.ts +107 -0
  221. package/src/astro/routes/api/settings/email.ts +150 -0
  222. package/src/astro/routes/api/settings.ts +116 -0
  223. package/src/astro/routes/api/setup/admin-verify.ts +122 -0
  224. package/src/astro/routes/api/setup/admin.ts +104 -0
  225. package/src/astro/routes/api/setup/dev-bypass.ts +200 -0
  226. package/src/astro/routes/api/setup/dev-reset.ts +40 -0
  227. package/src/astro/routes/api/setup/index.ts +128 -0
  228. package/src/astro/routes/api/setup/status.ts +122 -0
  229. package/src/astro/routes/api/snapshot.ts +76 -0
  230. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +232 -0
  231. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +131 -0
  232. package/src/astro/routes/api/taxonomies/index.ts +114 -0
  233. package/src/astro/routes/api/themes/preview.ts +78 -0
  234. package/src/astro/routes/api/typegen.ts +114 -0
  235. package/src/astro/routes/api/well-known/auth.ts +71 -0
  236. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +48 -0
  237. package/src/astro/routes/api/well-known/oauth-protected-resource.ts +39 -0
  238. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +114 -0
  239. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +213 -0
  240. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +126 -0
  241. package/src/astro/routes/api/widget-areas/[name].ts +135 -0
  242. package/src/astro/routes/api/widget-areas/index.ts +149 -0
  243. package/src/astro/routes/api/widget-components.ts +22 -0
  244. package/src/astro/routes/robots.txt.ts +81 -0
  245. package/src/astro/routes/sitemap-[collection].xml.ts +104 -0
  246. package/src/astro/routes/sitemap.xml.ts +92 -0
  247. package/src/components/Break.astro +45 -0
  248. package/src/components/Button.astro +71 -0
  249. package/src/components/Buttons.astro +49 -0
  250. package/src/components/Code.astro +59 -0
  251. package/src/components/Columns.astro +59 -0
  252. package/src/components/CommentForm.astro +315 -0
  253. package/src/components/Comments.astro +232 -0
  254. package/src/components/Cover.astro +128 -0
  255. package/src/components/DinewayBodyEnd.astro +32 -0
  256. package/src/components/DinewayBodyStart.astro +32 -0
  257. package/src/components/DinewayHead.astro +61 -0
  258. package/src/components/DinewayImage.astro +178 -0
  259. package/src/components/DinewayMedia.astro +167 -0
  260. package/src/components/Embed.astro +128 -0
  261. package/src/components/File.astro +122 -0
  262. package/src/components/Gallery.astro +93 -0
  263. package/src/components/HtmlBlock.astro +33 -0
  264. package/src/components/Image.astro +178 -0
  265. package/src/components/InlineEditor.astro +27 -0
  266. package/src/components/InlinePortableTextEditor.tsx +1937 -0
  267. package/src/components/LiveSearch.astro +614 -0
  268. package/src/components/PortableText.astro +51 -0
  269. package/src/components/Pullquote.astro +51 -0
  270. package/src/components/Table.astro +135 -0
  271. package/src/components/WidgetArea.astro +22 -0
  272. package/src/components/WidgetRenderer.astro +72 -0
  273. package/src/components/index.ts +106 -0
  274. package/src/components/marks/Link.astro +31 -0
  275. package/src/components/marks/StrikeThrough.astro +7 -0
  276. package/src/components/marks/Subscript.astro +7 -0
  277. package/src/components/marks/Superscript.astro +7 -0
  278. package/src/components/marks/Underline.astro +7 -0
  279. package/src/components/marks.ts +19 -0
  280. package/src/components/widgets/Archives.astro +65 -0
  281. package/src/components/widgets/Categories.astro +35 -0
  282. package/src/components/widgets/RecentPosts.astro +51 -0
  283. package/src/components/widgets/Search.astro +18 -0
  284. package/src/components/widgets/Tags.astro +38 -0
  285. package/src/ui.ts +75 -0
  286. package/LICENSE +0 -9
  287. /package/dist/{adapters-BlzWJG82.d.mts → adapters-C2ypTrZZ.d.mts} +0 -0
  288. /package/dist/{config-Cq8H0SfX.mjs → config-BXwuX8Bx.mjs} +0 -0
  289. /package/dist/{load-C6FCD1FU.mjs → load-Coc9HpHH.mjs} +0 -0
  290. /package/dist/{manifest-schema-CTSEyIJ3.mjs → manifest-schema-D1MSVnoI.mjs} +0 -0
  291. /package/dist/{mode-BlyYtIFO.mjs → mode-47goXBBK.mjs} +0 -0
  292. /package/dist/{tokens-4vgYuXsZ.mjs → tokens-CJz9ubV6.mjs} +0 -0
  293. /package/dist/{transport-C5FYnid7.mjs → transport-DB5eDN4x.mjs} +0 -0
  294. /package/dist/{transport-gIL-e43D.d.mts → transport-Wge_IzKl.d.mts} +0 -0
  295. /package/dist/{types-CLLdsG3g.d.mts → types-BzcUjoqg.d.mts} +0 -0
  296. /package/dist/{types-DShnjzb6.mjs → types-griIBQOQ.mjs} +0 -0
@@ -1,4 +1,5 @@
1
- import { G as PublicPageContext, M as PagePlacement, O as PageMetadataContribution, T as PageFragmentContribution, j as PageMetadataLinkRel, t as BreadcrumbItem } from "../types-D38djUXv.mjs";
1
+ import { A as PageMetadataContribution, D as PageFragmentContribution, N as PageMetadataLinkRel, P as PagePlacement, q as PublicPageContext, t as BreadcrumbItem } from "../types-Cj0KMIZV.mjs";
2
+ import { n as SeoSettings } from "../types-BuMDPy5C.mjs";
2
3
 
3
4
  //#region src/page/context.d.ts
4
5
  /** Fields shared by both input forms */
@@ -112,6 +113,13 @@ declare function renderFragments(contributions: PageFragmentContribution[], plac
112
113
  * Returns an empty array if no SEO-relevant data is present.
113
114
  */
114
115
  declare function generateBaseSeoContributions(page: PublicPageContext): PageMetadataContribution[];
116
+ /**
117
+ * Generate site-level SEO metadata contributions from SiteSettings.seo.
118
+ *
119
+ * These tags apply to every page, so they're sourced from global site settings
120
+ * rather than page-specific context.
121
+ */
122
+ declare function generateSiteSeoContributions(seoSettings: SeoSettings | undefined): PageMetadataContribution[];
115
123
  //#endregion
116
124
  //#region src/page/jsonld.d.ts
117
125
  /**
@@ -145,4 +153,4 @@ interface DinewayPageRuntime {
145
153
  */
146
154
  declare function getPageRuntime(locals: Record<string, unknown>): DinewayPageRuntime | undefined;
147
155
  //#endregion
148
- export { type CreatePublicPageContextInput, DinewayPageRuntime, type ResolvedPageMetadata, buildBlogPostingJsonLd, buildWebSiteJsonLd, cleanJsonLd, createPublicPageContext, escapeHtmlAttr, generateBaseSeoContributions, getPageRuntime, renderFragments, renderPageMetadata, resolveFragments, resolvePageMetadata, safeJsonLdSerialize };
156
+ export { type CreatePublicPageContextInput, DinewayPageRuntime, type ResolvedPageMetadata, buildBlogPostingJsonLd, buildWebSiteJsonLd, cleanJsonLd, createPublicPageContext, escapeHtmlAttr, generateBaseSeoContributions, generateSiteSeoContributions, getPageRuntime, renderFragments, renderPageMetadata, resolveFragments, resolvePageMetadata, safeJsonLdSerialize };
@@ -403,6 +403,27 @@ function generateBaseSeoContributions(page) {
403
403
  }
404
404
  return contributions;
405
405
  }
406
+ /**
407
+ * Generate site-level SEO metadata contributions from SiteSettings.seo.
408
+ *
409
+ * These tags apply to every page, so they're sourced from global site settings
410
+ * rather than page-specific context.
411
+ */
412
+ function generateSiteSeoContributions(seoSettings) {
413
+ const contributions = [];
414
+ if (!seoSettings) return contributions;
415
+ if (seoSettings.googleVerification) contributions.push({
416
+ kind: "meta",
417
+ name: "google-site-verification",
418
+ content: seoSettings.googleVerification
419
+ });
420
+ if (seoSettings.bingVerification) contributions.push({
421
+ kind: "meta",
422
+ name: "msvalidate.01",
423
+ content: seoSettings.bingVerification
424
+ });
425
+ return contributions;
426
+ }
406
427
 
407
428
  //#endregion
408
429
  //#region src/page/index.ts
@@ -416,4 +437,4 @@ function getPageRuntime(locals) {
416
437
  }
417
438
 
418
439
  //#endregion
419
- export { buildBlogPostingJsonLd, buildWebSiteJsonLd, cleanJsonLd, createPublicPageContext, escapeHtmlAttr, generateBaseSeoContributions, getPageRuntime, renderFragments, renderPageMetadata, resolveFragments, resolvePageMetadata, safeJsonLdSerialize };
440
+ export { buildBlogPostingJsonLd, buildWebSiteJsonLd, cleanJsonLd, createPublicPageContext, escapeHtmlAttr, generateBaseSeoContributions, generateSiteSeoContributions, getPageRuntime, renderFragments, renderPageMetadata, resolveFragments, resolvePageMetadata, safeJsonLdSerialize };
@@ -0,0 +1,92 @@
1
+ //#region src/redirects/patterns.ts
2
+ /**
3
+ * URL pattern matching for redirects.
4
+ *
5
+ * Uses Astro's route syntax: [param] for named segments, [...rest] for catch-all.
6
+ * Compiles patterns to safe regexes -- no user-supplied regex, no ReDoS risk.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const compiled = compilePattern("/old-blog/[...path]");
11
+ * const match = matchPattern(compiled, "/old-blog/2024/01/post");
12
+ * // match = { path: "2024/01/post" }
13
+ *
14
+ * interpolateDestination("/blog/[...path]", match);
15
+ * // "/blog/2024/01/post"
16
+ * ```
17
+ */
18
+ /** Matches [paramName] placeholders */
19
+ const PARAM_PATTERN = /\[(\w+)\]/g;
20
+ /** Matches [...splatName] placeholders */
21
+ const SPLAT_PATTERN = /\[\.\.\.(\w+)\]/g;
22
+ /** Combined pattern for validation: matches both [param] and [...splat] */
23
+ const ANY_PLACEHOLDER = /\[(?:\.\.\.)?(\w+)\]/g;
24
+ /** Split on capture groups in compiled regex string */
25
+ const CAPTURE_GROUP_SPLIT = /(\([^)]+\))/;
26
+ /** Escape regex-special characters in literal parts */
27
+ const REGEX_SPECIAL_CHARS = /[.*+?^${}|\\]/g;
28
+ /**
29
+ * Returns true if a source string contains [param] or [...splat] placeholders.
30
+ */
31
+ function isPattern(source) {
32
+ return source.match(ANY_PLACEHOLDER) !== null;
33
+ }
34
+ /**
35
+ * Compile a URL pattern into a regex for matching.
36
+ *
37
+ * - `[param]` matches a single path segment (`[^/]+`)
38
+ * - `[...rest]` matches one or more remaining segments (`.+`)
39
+ */
40
+ function compilePattern(source) {
41
+ const paramNames = [];
42
+ let regexStr = source.replace(SPLAT_PATTERN, (_match, name) => {
43
+ paramNames.push(name);
44
+ return "(.+)";
45
+ });
46
+ regexStr = regexStr.replace(PARAM_PATTERN, (_match, name) => {
47
+ paramNames.push(name);
48
+ return "([^/]+)";
49
+ });
50
+ const escaped = regexStr.split(CAPTURE_GROUP_SPLIT).map((part, i) => {
51
+ if (i % 2 === 1) return part;
52
+ return part.replace(REGEX_SPECIAL_CHARS, "\\$&");
53
+ }).join("");
54
+ return {
55
+ regex: new RegExp(`^${escaped}$`),
56
+ paramNames,
57
+ source
58
+ };
59
+ }
60
+ /**
61
+ * Match a path against a compiled pattern.
62
+ * Returns captured params or null if no match.
63
+ */
64
+ function matchPattern(compiled, path) {
65
+ const match = path.match(compiled.regex);
66
+ if (!match) return null;
67
+ const params = {};
68
+ for (let i = 0; i < compiled.paramNames.length; i++) {
69
+ const value = match[i + 1];
70
+ if (value !== void 0) params[compiled.paramNames[i]] = value;
71
+ }
72
+ return params;
73
+ }
74
+ /**
75
+ * Interpolate captured params into a destination pattern.
76
+ *
77
+ * @example
78
+ * interpolateDestination("/blog/[...path]", { path: "2024/01/post" })
79
+ * // "/blog/2024/01/post"
80
+ */
81
+ function interpolateDestination(destination, params) {
82
+ let result = destination.replace(SPLAT_PATTERN, (_match, name) => {
83
+ return params[name] ?? "";
84
+ });
85
+ result = result.replace(PARAM_PATTERN, (_match, name) => {
86
+ return params[name] ?? "";
87
+ });
88
+ return result;
89
+ }
90
+
91
+ //#endregion
92
+ export { matchPattern as i, interpolateDestination as n, isPattern as r, compilePattern as t };
@@ -260,7 +260,7 @@ declare function normalizeMediaValue(value: unknown, getProvider: (id: string) =
260
260
  *
261
261
  * Generates blurhash and dominant color from image buffers for LQIP support.
262
262
  * Decodes images via jpeg-js (pure JS) and upng-js (pure JS, uses pako for
263
- * deflate). No Node-specific dependencies works in Workers and Node SSR.
263
+ * deflate). No native image bindings required, so it works in server runtimes.
264
264
  */
265
265
  interface PlaceholderData {
266
266
  blurhash: string;
@@ -132,7 +132,7 @@ function recordToMediaValue(obj) {
132
132
  *
133
133
  * Generates blurhash and dominant color from image buffers for LQIP support.
134
134
  * Decodes images via jpeg-js (pure JS) and upng-js (pure JS, uses pako for
135
- * deflate). No Node-specific dependencies works in Workers and Node SSR.
135
+ * deflate). No native image bindings required, so it works in server runtimes.
136
136
  */
137
137
  const SUPPORTED_TYPES = {
138
138
  "image/jpeg": "jpeg",
@@ -1,8 +1,8 @@
1
- import "../types-DkvMXalq.mjs";
2
- import { wn as PluginDescriptor } from "../index-C-jx21qs.mjs";
3
- import "../runner-B5l1JfOj.mjs";
4
- import { $ as StandardPluginDefinition, J as ResolvedPlugin } from "../types-D38djUXv.mjs";
5
- import "../validate-DVKJJ-M_.mjs";
1
+ import "../types-DOrVigru.mjs";
2
+ import { wn as PluginDescriptor } from "../index-yvc6E_17.mjs";
3
+ import "../runner-CFI6B6J2.mjs";
4
+ import { X as ResolvedPlugin, tt as StandardPluginDefinition } from "../types-Cj0KMIZV.mjs";
5
+ import "../validate-IPf8n4Fj.mjs";
6
6
 
7
7
  //#region src/plugins/adapt-sandbox-entry.d.ts
8
8
  /**
@@ -1,4 +1,4 @@
1
- import { n as PLUGIN_CAPABILITIES, t as HOOK_NAMES } from "../manifest-schema-CTSEyIJ3.mjs";
1
+ import { n as PLUGIN_CAPABILITIES, t as HOOK_NAMES } from "../manifest-schema-D1MSVnoI.mjs";
2
2
 
3
3
  //#region src/plugins/adapt-sandbox-entry.ts
4
4
  /**
@@ -1,6 +1,8 @@
1
1
  import { t as __exportAll } from "./chunk-ClPoSABd.mjs";
2
- import { n as getI18nConfig, r as isI18nEnabled, t as getFallbackChain } from "./config-Cq8H0SfX.mjs";
3
2
  import { getRequestContext } from "./request-context.mjs";
3
+ import { n as getI18nConfig, r as isI18nEnabled, t as getFallbackChain } from "./config-BXwuX8Bx.mjs";
4
+ import { n as requestCached } from "./request-cache-Dk5qPSOx.mjs";
5
+ import { t as isMissingTableError } from "./db-errors-CEqD7qH9.mjs";
4
6
 
5
7
  //#region src/visual-editing/editable.ts
6
8
  /**
@@ -76,6 +78,7 @@ var query_exports = /* @__PURE__ */ __exportAll({
76
78
  getDinewayEntry: () => getDinewayEntry,
77
79
  getEditMeta: () => getEditMeta,
78
80
  getTranslations: () => getTranslations,
81
+ invalidateUrlPatternCache: () => invalidateUrlPatternCache,
79
82
  resolveDinewayPath: () => resolveDinewayPath
80
83
  });
81
84
  const COLLECTION_NAME = "_dineway";
@@ -166,42 +169,59 @@ function entryEditOptions(entry) {
166
169
  * ```
167
170
  */
168
171
  async function getDinewayCollection(type, filter) {
169
- const { getLiveCollection } = await import("astro:content");
170
172
  const ctx = getRequestContext();
171
173
  const i18nConfig = getI18nConfig();
172
174
  const resolvedLocale = filter?.locale ?? ctx?.locale ?? (isI18nEnabled() ? i18nConfig.defaultLocale : void 0);
173
- const result = await getLiveCollection(COLLECTION_NAME, {
174
- type,
175
- status: filter?.status,
176
- limit: filter?.limit,
177
- cursor: filter?.cursor,
178
- where: filter?.where,
179
- orderBy: filter?.orderBy,
180
- locale: resolvedLocale
181
- });
182
- const { entries, error, cacheHint } = result;
183
- const rawCursor = Object.getOwnPropertyDescriptor(result, "nextCursor")?.value;
184
- const nextCursor = typeof rawCursor === "string" ? rawCursor : void 0;
185
- if (error) return {
186
- entries: [],
187
- error,
188
- cacheHint: {}
189
- };
190
- const isEditMode = ctx?.editMode ?? false;
191
- const entriesWithEdit = entries.map((entry) => {
192
- const dbId = entryDatabaseId(entry);
193
- if (isEditMode) tagEditableFields(entryData(entry), type, dbId);
175
+ return requestCached(collectionCacheKey(type, filter, resolvedLocale), async () => {
176
+ const { getLiveCollection } = await import("astro:content");
177
+ const result = await getLiveCollection(COLLECTION_NAME, {
178
+ type,
179
+ status: filter?.status,
180
+ limit: filter?.limit,
181
+ cursor: filter?.cursor,
182
+ where: filter?.where,
183
+ orderBy: filter?.orderBy,
184
+ locale: resolvedLocale
185
+ });
186
+ const { entries, error, cacheHint } = result;
187
+ const rawCursor = Object.getOwnPropertyDescriptor(result, "nextCursor")?.value;
188
+ const nextCursor = typeof rawCursor === "string" ? rawCursor : void 0;
189
+ if (error) return {
190
+ entries: [],
191
+ error,
192
+ cacheHint: {}
193
+ };
194
+ const isEditMode = ctx?.editMode ?? false;
195
+ const entriesWithEdit = entries.map((entry) => {
196
+ const dbId = entryDatabaseId(entry);
197
+ if (isEditMode) tagEditableFields(entryData(entry), type, dbId);
198
+ return {
199
+ ...entry,
200
+ edit: isEditMode ? createEditable(type, dbId, entryEditOptions(entry)) : createNoop()
201
+ };
202
+ });
203
+ await Promise.all([hydrateEntryBylines(type, entriesWithEdit), hydrateEntryTerms(type, entriesWithEdit)]);
194
204
  return {
195
- ...entry,
196
- edit: isEditMode ? createEditable(type, dbId, entryEditOptions(entry)) : createNoop()
205
+ entries: entriesWithEdit,
206
+ nextCursor,
207
+ cacheHint: cacheHint ?? {}
197
208
  };
198
209
  });
199
- await hydrateEntryBylines(type, entriesWithEdit);
200
- return {
201
- entries: entriesWithEdit,
202
- nextCursor,
203
- cacheHint: cacheHint ?? {}
204
- };
210
+ }
211
+ function collectionCacheKey(type, filter, locale) {
212
+ return `collection:${type}:${[
213
+ filter?.status ?? "",
214
+ filter?.limit ?? "",
215
+ filter?.cursor ?? "",
216
+ filter?.where ? stableStringify(filter.where) : "",
217
+ filter?.orderBy ? JSON.stringify(filter.orderBy) : "",
218
+ locale ?? ""
219
+ ].join("|")}`;
220
+ }
221
+ function stableStringify(value) {
222
+ const ordered = {};
223
+ for (const key of Object.keys(value).toSorted()) ordered[key] = value[key];
224
+ return JSON.stringify(ordered);
205
225
  }
206
226
  /**
207
227
  * Get a single entry by type and ID/slug
@@ -228,7 +248,8 @@ async function getDinewayEntry(type, id, options) {
228
248
  const ctx = getRequestContext();
229
249
  const preview = ctx?.preview;
230
250
  const isEditMode = ctx?.editMode ?? false;
231
- const serveDrafts = !!preview && preview.collection === type || isEditMode;
251
+ const isPreviewMode = !!preview && preview.collection === type;
252
+ const serveDrafts = isPreviewMode || isEditMode;
232
253
  const requestedLocale = options?.locale ?? ctx?.locale;
233
254
  /** Wrap a raw Astro entry with edit proxy, tagging editable fields if needed */
234
255
  function wrapEntry(raw) {
@@ -247,9 +268,9 @@ async function getDinewayEntry(type, id, options) {
247
268
  return status === "published" || !!(status === "scheduled" && scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date());
248
269
  }
249
270
  const localeChain = requestedLocale && isI18nEnabled() ? getFallbackChain(requestedLocale) : [requestedLocale];
250
- /** Return a successful EntryResult with bylines hydrated */
271
+ /** Return a successful EntryResult with runtime data hydrated */
251
272
  async function successResult(wrapped, opts) {
252
- await hydrateEntryBylines(type, [wrapped]);
273
+ await Promise.all([hydrateEntryBylines(type, [wrapped]), hydrateEntryTerms(type, [wrapped])]);
253
274
  return {
254
275
  entry: wrapped,
255
276
  isPreview: opts.isPreview,
@@ -273,6 +294,17 @@ async function getDinewayEntry(type, id, options) {
273
294
  cacheHint: {}
274
295
  };
275
296
  if (!baseEntry) continue;
297
+ if (isPreviewMode && !isEditMode) {
298
+ const dbId = entryDatabaseId(baseEntry);
299
+ if (preview.id !== dbId && preview.id !== id) {
300
+ if (isVisible(baseEntry)) return successResult(wrapEntry(baseEntry), {
301
+ isPreview: false,
302
+ fallbackLocale,
303
+ cacheHint: cacheHint ?? {}
304
+ });
305
+ continue;
306
+ }
307
+ }
276
308
  const draftRevisionId = dataStr(entryData(baseEntry), "draftRevisionId") || void 0;
277
309
  if (draftRevisionId) {
278
310
  const { entry: draftEntry, error: draftError } = await getLiveEntry(COLLECTION_NAME, {
@@ -337,7 +369,7 @@ async function getDinewayEntry(type, id, options) {
337
369
  async function hydrateEntryBylines(type, entries) {
338
370
  if (entries.length === 0) return;
339
371
  try {
340
- const { getBylinesForEntries } = await import("./bylines-DyqBV9EQ.mjs").then((n) => n.t);
372
+ const { getBylinesForEntries } = await import("./bylines-BGpD9_hy.mjs").then((n) => n.t);
341
373
  const ids = entries.map((e) => dataStr(entryData(e), "id")).filter(Boolean);
342
374
  if (ids.length === 0) return;
343
375
  const bylinesMap = await getBylinesForEntries(type, ids);
@@ -350,8 +382,30 @@ async function hydrateEntryBylines(type, entries) {
350
382
  data.byline = credits[0]?.byline ?? null;
351
383
  }
352
384
  } catch (err) {
353
- const msg = err instanceof Error ? err.message : "";
354
- if (!msg.includes("no such table")) console.warn("[dineway] Failed to hydrate bylines:", msg);
385
+ if (!isMissingTableError(err)) {
386
+ const msg = err instanceof Error ? err.message : String(err);
387
+ console.warn("[dineway] Failed to hydrate bylines:", msg);
388
+ }
389
+ }
390
+ }
391
+ async function hydrateEntryTerms(type, entries) {
392
+ if (entries.length === 0) return;
393
+ try {
394
+ const { getAllTermsForEntries } = await import("./taxonomies-1s5PaS_8.mjs").then((n) => n.c);
395
+ const ids = entries.map((entry) => dataStr(entryData(entry), "id")).filter(Boolean);
396
+ if (ids.length === 0) return;
397
+ const termsMap = await getAllTermsForEntries(type, ids);
398
+ for (const entry of entries) {
399
+ const data = entryData(entry);
400
+ const dbId = dataStr(data, "id");
401
+ if (!dbId) continue;
402
+ data.terms = termsMap.get(dbId) ?? {};
403
+ }
404
+ } catch (err) {
405
+ if (!isMissingTableError(err)) {
406
+ const msg = err instanceof Error ? err.message : String(err);
407
+ console.warn("[dineway] Failed to hydrate terms:", msg);
408
+ }
355
409
  }
356
410
  }
357
411
  /**
@@ -371,9 +425,9 @@ async function hydrateEntryBylines(type, entries) {
371
425
  */
372
426
  async function getTranslations(type, id) {
373
427
  try {
374
- const db = (await import("./loader-qKmo0wAY.mjs").then((n) => n.r)).getDb;
428
+ const db = (await import("./loader-sMG4TZ-u.mjs").then((n) => n.r)).getDb;
375
429
  const dbInstance = await db();
376
- const { ContentRepository } = await import("./content-zSgdNmnt.mjs").then((n) => n.n);
430
+ const { ContentRepository } = await import("./content-DWi4d0rT.mjs").then((n) => n.n);
377
431
  const repo = new ContentRepository(dbInstance);
378
432
  const item = await repo.findByIdOrSlug(type, id);
379
433
  if (!item) return {
@@ -413,6 +467,10 @@ function patternToRegex(pattern) {
413
467
  paramNames
414
468
  };
415
469
  }
470
+ let cachedUrlPatterns = null;
471
+ function invalidateUrlPatternCache() {
472
+ cachedUrlPatterns = null;
473
+ }
416
474
  /**
417
475
  * Resolve a URL path to a content entry by matching against collection URL patterns.
418
476
  *
@@ -433,22 +491,32 @@ function patternToRegex(pattern) {
433
491
  * ```
434
492
  */
435
493
  async function resolveDinewayPath(path) {
436
- const { getDb } = await import("./loader-qKmo0wAY.mjs").then((n) => n.r);
437
- const { SchemaRegistry } = await import("./registry-DSd1GWB8.mjs").then((n) => n.r);
438
- const collections = await new SchemaRegistry(await getDb()).listCollections();
439
- for (const collection of collections) {
440
- if (!collection.urlPattern) continue;
441
- const { regex, paramNames } = patternToRegex(collection.urlPattern);
442
- const match = path.match(regex);
494
+ const hasDbOverride = !!getRequestContext()?.db;
495
+ let patterns = !hasDbOverride ? cachedUrlPatterns : null;
496
+ if (!patterns) {
497
+ const { getDb } = await import("./loader-sMG4TZ-u.mjs").then((n) => n.r);
498
+ const { SchemaRegistry } = await import("./registry-C0zjeB9P.mjs").then((n) => n.r);
499
+ patterns = (await new SchemaRegistry(await getDb()).listCollections()).filter((collection) => collection.urlPattern).map((collection) => {
500
+ const { regex, paramNames } = patternToRegex(collection.urlPattern);
501
+ return {
502
+ slug: collection.slug,
503
+ regex,
504
+ paramNames
505
+ };
506
+ });
507
+ if (!hasDbOverride) cachedUrlPatterns = patterns;
508
+ }
509
+ for (const pattern of patterns) {
510
+ const match = path.match(pattern.regex);
443
511
  if (!match) continue;
444
512
  const params = {};
445
- for (let i = 0; i < paramNames.length; i++) params[paramNames[i]] = match[i + 1];
513
+ for (let i = 0; i < pattern.paramNames.length; i++) params[pattern.paramNames[i]] = match[i + 1];
446
514
  const slug = params.slug;
447
515
  if (!slug) continue;
448
- const { entry } = await getDinewayEntry(collection.slug, slug);
516
+ const { entry } = await getDinewayEntry(pattern.slug, slug);
449
517
  if (entry) return {
450
518
  entry,
451
- collection: collection.slug,
519
+ collection: pattern.slug,
452
520
  params
453
521
  };
454
522
  }
@@ -456,4 +524,4 @@ async function resolveDinewayPath(path) {
456
524
  }
457
525
 
458
526
  //#endregion
459
- export { query_exports as a, createNoop as c, getTranslations as i, getDinewayEntry as n, resolveDinewayPath as o, getEditMeta as r, createEditable as s, getDinewayCollection as t };
527
+ export { invalidateUrlPatternCache as a, createEditable as c, getTranslations as i, createNoop as l, getDinewayEntry as n, query_exports as o, getEditMeta as r, resolveDinewayPath as s, getDinewayCollection as t };
@@ -1,100 +1,23 @@
1
- import { r as currentTimestampValue } from "./dialect-helpers-B9uSp2GJ.mjs";
1
+ import { r as currentTimestampValue } from "./dialect-helpers-DhTzaUxP.mjs";
2
2
  import { n as decodeCursor, r as encodeCursor } from "./types-BawVha09.mjs";
3
+ import { i as matchPattern, n as interpolateDestination, r as isPattern, t as compilePattern } from "./patterns-CrCYkMBb.mjs";
3
4
  import { sql } from "kysely";
4
5
  import { ulid } from "ulidx";
5
6
 
6
- //#region src/redirects/patterns.ts
7
- /**
8
- * URL pattern matching for redirects.
9
- *
10
- * Uses Astro's route syntax: [param] for named segments, [...rest] for catch-all.
11
- * Compiles patterns to safe regexes -- no user-supplied regex, no ReDoS risk.
12
- *
13
- * @example
14
- * ```ts
15
- * const compiled = compilePattern("/old-blog/[...path]");
16
- * const match = matchPattern(compiled, "/old-blog/2024/01/post");
17
- * // match = { path: "2024/01/post" }
18
- *
19
- * interpolateDestination("/blog/[...path]", match);
20
- * // "/blog/2024/01/post"
21
- * ```
22
- */
23
- /** Matches [paramName] placeholders */
24
- const PARAM_PATTERN = /\[(\w+)\]/g;
25
- /** Matches [...splatName] placeholders */
26
- const SPLAT_PATTERN = /\[\.\.\.(\w+)\]/g;
27
- /** Combined pattern for validation: matches both [param] and [...splat] */
28
- const ANY_PLACEHOLDER = /\[(?:\.\.\.)?(\w+)\]/g;
29
- /** Split on capture groups in compiled regex string */
30
- const CAPTURE_GROUP_SPLIT = /(\([^)]+\))/;
31
- /** Escape regex-special characters in literal parts */
32
- const REGEX_SPECIAL_CHARS = /[.*+?^${}|\\]/g;
33
- /**
34
- * Returns true if a source string contains [param] or [...splat] placeholders.
35
- */
36
- function isPattern(source) {
37
- return source.match(ANY_PLACEHOLDER) !== null;
38
- }
39
- /**
40
- * Compile a URL pattern into a regex for matching.
41
- *
42
- * - `[param]` matches a single path segment (`[^/]+`)
43
- * - `[...rest]` matches one or more remaining segments (`.+`)
44
- */
45
- function compilePattern(source) {
46
- const paramNames = [];
47
- let regexStr = source.replace(SPLAT_PATTERN, (_match, name) => {
48
- paramNames.push(name);
49
- return "(.+)";
50
- });
51
- regexStr = regexStr.replace(PARAM_PATTERN, (_match, name) => {
52
- paramNames.push(name);
53
- return "([^/]+)";
54
- });
55
- const escaped = regexStr.split(CAPTURE_GROUP_SPLIT).map((part, i) => {
56
- if (i % 2 === 1) return part;
57
- return part.replace(REGEX_SPECIAL_CHARS, "\\$&");
58
- }).join("");
59
- return {
60
- regex: new RegExp(`^${escaped}$`),
61
- paramNames,
62
- source
63
- };
64
- }
65
- /**
66
- * Match a path against a compiled pattern.
67
- * Returns captured params or null if no match.
68
- */
69
- function matchPattern(compiled, path) {
70
- const match = path.match(compiled.regex);
71
- if (!match) return null;
72
- const params = {};
73
- for (let i = 0; i < compiled.paramNames.length; i++) {
74
- const value = match[i + 1];
75
- if (value !== void 0) params[compiled.paramNames[i]] = value;
76
- }
77
- return params;
78
- }
7
+ //#region src/database/repositories/redirect.ts
79
8
  /**
80
- * Interpolate captured params into a destination pattern.
81
- *
82
- * @example
83
- * interpolateDestination("/blog/[...path]", { path: "2024/01/post" })
84
- * // "/blog/2024/01/post"
9
+ * Hard cap on rows stored in `_dineway_404_log`. When exceeded, the oldest
10
+ * rows by `last_seen_at` are evicted on insert.
85
11
  */
86
- function interpolateDestination(destination, params) {
87
- let result = destination.replace(SPLAT_PATTERN, (_match, name) => {
88
- return params[name] ?? "";
89
- });
90
- result = result.replace(PARAM_PATTERN, (_match, name) => {
91
- return params[name] ?? "";
92
- });
93
- return result;
12
+ const MAX_404_LOG_ROWS = 1e4;
13
+ /** Max stored length for the `Referer` header. */
14
+ const REFERRER_MAX_LENGTH = 512;
15
+ /** Max stored length for the `User-Agent` header. */
16
+ const USER_AGENT_MAX_LENGTH = 256;
17
+ function truncateOrNull(value, max) {
18
+ if (value === null || value === void 0) return null;
19
+ return value.length > max ? value.slice(0, max) : value;
94
20
  }
95
-
96
- //#endregion
97
- //#region src/database/repositories/redirect.ts
98
21
  function rowToRedirect(row) {
99
22
  return {
100
23
  id: row.id,
@@ -184,6 +107,9 @@ var RedirectRepository = class {
184
107
  const result = await this.db.deleteFrom("_dineway_redirects").where("id", "=", id).executeTakeFirst();
185
108
  return BigInt(result.numDeletedRows) > 0n;
186
109
  }
110
+ async findAllEnabled() {
111
+ return (await this.db.selectFrom("_dineway_redirects").selectAll().where("enabled", "=", 1).execute()).map(rowToRedirect);
112
+ }
187
113
  async findExactMatch(path) {
188
114
  const row = await this.db.selectFrom("_dineway_redirects").selectAll().where("source", "=", path).where("enabled", "=", 1).where("is_pattern", "=", 0).executeTakeFirst();
189
115
  return row ? rowToRedirect(row) : null;
@@ -252,14 +178,34 @@ var RedirectRepository = class {
252
178
  return Number(result.numUpdatedRows);
253
179
  }
254
180
  async log404(entry) {
181
+ const now = (/* @__PURE__ */ new Date()).toISOString();
182
+ const referrer = truncateOrNull(entry.referrer, REFERRER_MAX_LENGTH);
183
+ const userAgent = truncateOrNull(entry.userAgent, USER_AGENT_MAX_LENGTH);
184
+ const ip = entry.ip ?? null;
255
185
  await this.db.insertInto("_dineway_404_log").values({
256
186
  id: ulid(),
257
187
  path: entry.path,
258
- referrer: entry.referrer ?? null,
259
- user_agent: entry.userAgent ?? null,
260
- ip: entry.ip ?? null,
261
- created_at: (/* @__PURE__ */ new Date()).toISOString()
262
- }).execute();
188
+ referrer,
189
+ user_agent: userAgent,
190
+ ip,
191
+ hits: 1,
192
+ last_seen_at: now,
193
+ created_at: now
194
+ }).onConflict((oc) => oc.column("path").doUpdateSet({
195
+ hits: sql`hits + 1`,
196
+ last_seen_at: now,
197
+ referrer,
198
+ user_agent: userAgent,
199
+ ip
200
+ })).execute();
201
+ await this.enforce404Cap();
202
+ }
203
+ async enforce404Cap() {
204
+ const countRow = await this.db.selectFrom("_dineway_404_log").select((eb) => eb.fn.countAll().as("c")).executeTakeFirst();
205
+ const count = Number(countRow?.c ?? 0);
206
+ if (count <= MAX_404_LOG_ROWS) return;
207
+ const excess = count - MAX_404_LOG_ROWS;
208
+ await this.db.deleteFrom("_dineway_404_log").where("id", "in", this.db.selectFrom("_dineway_404_log").select("id").orderBy("last_seen_at", "asc").orderBy("id", "asc").limit(excess)).execute();
263
209
  }
264
210
  async find404s(opts) {
265
211
  const limit = Math.min(Math.max(opts.limit ?? 50, 1), 100);
@@ -289,14 +235,12 @@ var RedirectRepository = class {
289
235
  return (await sql`
290
236
  SELECT
291
237
  path,
292
- COUNT(*) as count,
293
- MAX(created_at) as last_seen,
238
+ SUM(hits) as count,
239
+ MAX(last_seen_at) as last_seen,
294
240
  (
295
241
  SELECT referrer FROM _dineway_404_log AS inner_log
296
242
  WHERE inner_log.path = _dineway_404_log.path
297
243
  AND referrer IS NOT NULL AND referrer != ''
298
- GROUP BY referrer
299
- ORDER BY COUNT(*) DESC
300
244
  LIMIT 1
301
245
  ) as top_referrer
302
246
  FROM _dineway_404_log