dineway 0.1.48 → 0.2.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 (420) hide show
  1. package/dist/{activity-events-C9u37D5z.mjs → activity-events-y6eT8UW6.mjs} +6 -38
  2. package/dist/actor-CrObAPV0.mjs +37 -0
  3. package/dist/after-BfniwgIf.mjs +9 -0
  4. package/dist/{allowed-origins-DhipQNLb.mjs → allowed-origins-_oeyP6x-.mjs} +1 -1
  5. package/dist/api/route-utils.d.mts +10 -10
  6. package/dist/api/route-utils.mjs +17 -14
  7. package/dist/api/schemas/index.d.mts +2 -2
  8. package/dist/api/schemas/index.mjs +5 -4
  9. package/dist/{api-Eiy8_zVZ.mjs → api-ByrU2q-Z.mjs} +61 -37
  10. package/dist/{api-tokens-DtvdtIkm.mjs → api-tokens-CjVMQT-X.mjs} +6 -2
  11. package/dist/{apply-CX3no7XE.mjs → apply-bZswmZPm.mjs} +117 -77
  12. package/dist/astro/image-endpoint.d.mts +7 -0
  13. package/dist/astro/image-endpoint.mjs +54 -0
  14. package/dist/astro/index.d.mts +35 -11
  15. package/dist/astro/index.mjs +157 -23
  16. package/dist/astro/integration/shims/use-sync-external-store-with-selector.js +71 -0
  17. package/dist/astro/integration/shims/use-sync-external-store.js +1 -0
  18. package/dist/astro/middleware/auth.d.mts +10 -10
  19. package/dist/astro/middleware/auth.mjs +42 -24
  20. package/dist/astro/middleware/redirect.mjs +14 -5
  21. package/dist/astro/middleware/request-context.mjs +9 -6
  22. package/dist/astro/middleware/seed.d.mts +1 -1
  23. package/dist/astro/middleware/seed.mjs +2 -2
  24. package/dist/astro/middleware/setup.mjs +1 -1
  25. package/dist/astro/middleware.mjs +334 -193
  26. package/dist/astro/routes/admin.astro +6 -1
  27. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -6
  28. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -6
  29. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  30. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  31. package/dist/astro/routes/api/admin/briefing.mjs +22 -18
  32. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  33. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -8
  34. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -8
  35. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -8
  36. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +18 -15
  37. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +18 -15
  38. package/dist/astro/routes/api/admin/bylines/index.mjs +18 -15
  39. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +15 -13
  40. package/dist/astro/routes/api/admin/comments/_id_.mjs +7 -6
  41. package/dist/astro/routes/api/admin/comments/bulk.mjs +12 -10
  42. package/dist/astro/routes/api/admin/comments/counts.mjs +7 -6
  43. package/dist/astro/routes/api/admin/comments/index.mjs +12 -10
  44. package/dist/astro/routes/api/admin/context/_id_/history.mjs +27 -25
  45. package/dist/astro/routes/api/admin/context/_id_/index.mjs +27 -25
  46. package/dist/astro/routes/api/admin/context/_id_/review.mjs +32 -29
  47. package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +32 -29
  48. package/dist/astro/routes/api/admin/context/diff.mjs +31 -28
  49. package/dist/astro/routes/api/admin/context/index.mjs +32 -29
  50. package/dist/astro/routes/api/admin/context/stale.mjs +31 -28
  51. package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +27 -25
  52. package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +31 -28
  53. package/dist/astro/routes/api/admin/hitl-requests/index.mjs +31 -28
  54. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +28 -26
  55. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  56. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
  57. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
  58. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -52
  59. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -52
  60. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +48 -44
  61. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -50
  62. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +53 -49
  63. package/dist/astro/routes/api/admin/plugins/index.mjs +48 -44
  64. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  65. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +47 -43
  66. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +53 -49
  67. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +47 -43
  68. package/dist/astro/routes/api/admin/plugins/updates.mjs +47 -43
  69. package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +6 -6
  70. package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +32 -28
  71. package/dist/astro/routes/api/admin/review-requests/index.mjs +11 -10
  72. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +47 -43
  73. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  74. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +47 -43
  75. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
  76. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  77. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -7
  78. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
  79. package/dist/astro/routes/api/admin/users/index.mjs +7 -6
  80. package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
  81. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  82. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -11
  83. package/dist/astro/routes/api/auth/invite/index.mjs +9 -8
  84. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -10
  85. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  86. package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -10
  87. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  88. package/dist/astro/routes/api/auth/me.mjs +10 -9
  89. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  90. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  91. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  92. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -5
  93. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  94. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -11
  95. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -9
  96. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -10
  97. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -10
  98. package/dist/astro/routes/api/auth/setup-token/index.mjs +6 -6
  99. package/dist/astro/routes/api/auth/setup-token/verify.mjs +2 -2
  100. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -11
  101. package/dist/astro/routes/api/auth/signup/request.mjs +11 -10
  102. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  103. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +16 -14
  104. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +8 -0
  105. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +160 -0
  106. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  107. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -6
  108. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -6
  109. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +6 -6
  110. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +12 -11
  111. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +11 -10
  112. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +6 -6
  113. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  114. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +11 -10
  115. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +17 -15
  116. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  117. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -6
  118. package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -10
  119. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  120. package/dist/astro/routes/api/content/_collection_/index.mjs +11 -10
  121. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -6
  122. package/dist/astro/routes/api/dashboard.mjs +10 -9
  123. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  124. package/dist/astro/routes/api/health.mjs +1 -1
  125. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  126. package/dist/astro/routes/api/import/probe.mjs +23 -11
  127. package/dist/astro/routes/api/import/wordpress/analyze.mjs +9 -6
  128. package/dist/astro/routes/api/import/wordpress/execute.d.mts +19 -1
  129. package/dist/astro/routes/api/import/wordpress/execute.mjs +85 -38
  130. package/dist/astro/routes/api/import/wordpress/media.d.mts +13 -1
  131. package/dist/astro/routes/api/import/wordpress/media.mjs +20 -14
  132. package/dist/astro/routes/api/import/wordpress/prepare.mjs +36 -33
  133. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +18 -1
  134. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +121 -101
  135. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  136. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +25 -13
  137. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
  138. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +56 -2
  139. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +284 -56
  140. package/dist/astro/routes/api/manifest.mjs +14 -5
  141. package/dist/astro/routes/api/mcp.mjs +266 -70
  142. package/dist/astro/routes/api/media/_id_/confirm.mjs +39 -11
  143. package/dist/astro/routes/api/media/_id_.mjs +11 -7
  144. package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
  145. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  146. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  147. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  148. package/dist/astro/routes/api/media/upload-url.mjs +12 -11
  149. package/dist/astro/routes/api/media.mjs +28 -35
  150. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +32 -29
  151. package/dist/astro/routes/api/menus/_name_/items.mjs +32 -29
  152. package/dist/astro/routes/api/menus/_name_/reorder.mjs +32 -29
  153. package/dist/astro/routes/api/menus/_name_/translations.mjs +32 -29
  154. package/dist/astro/routes/api/menus/_name_.mjs +32 -29
  155. package/dist/astro/routes/api/menus/index.mjs +32 -29
  156. package/dist/astro/routes/api/oauth/authorize.mjs +7 -7
  157. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  158. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  159. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  160. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  161. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  162. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  163. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  164. package/dist/astro/routes/api/openapi.json.mjs +4 -3
  165. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +30 -30
  166. package/dist/astro/routes/api/redirects/404s/index.mjs +12 -11
  167. package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -11
  168. package/dist/astro/routes/api/redirects/_id_.mjs +33 -30
  169. package/dist/astro/routes/api/redirects/index.mjs +33 -30
  170. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  171. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
  172. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +48 -44
  173. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +48 -44
  174. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +48 -44
  175. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +48 -44
  176. package/dist/astro/routes/api/schema/collections/index.mjs +48 -44
  177. package/dist/astro/routes/api/schema/index.mjs +13 -14
  178. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +47 -43
  179. package/dist/astro/routes/api/schema/orphans/index.mjs +47 -43
  180. package/dist/astro/routes/api/search/enable.mjs +12 -11
  181. package/dist/astro/routes/api/search/index.mjs +11 -10
  182. package/dist/astro/routes/api/search/rebuild.mjs +12 -11
  183. package/dist/astro/routes/api/search/stats.mjs +7 -7
  184. package/dist/astro/routes/api/search/suggest.mjs +11 -10
  185. package/dist/astro/routes/api/sections/_slug_.mjs +32 -29
  186. package/dist/astro/routes/api/sections/index.mjs +32 -29
  187. package/dist/astro/routes/api/settings/email.mjs +4 -4
  188. package/dist/astro/routes/api/settings.mjs +32 -29
  189. package/dist/astro/routes/api/setup/admin-verify.mjs +14 -13
  190. package/dist/astro/routes/api/setup/admin.mjs +12 -11
  191. package/dist/astro/routes/api/setup/dev-bypass.mjs +32 -26
  192. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  193. package/dist/astro/routes/api/setup/index.mjs +30 -26
  194. package/dist/astro/routes/api/setup/status.mjs +3 -3
  195. package/dist/astro/routes/api/snapshot.mjs +9 -8
  196. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +35 -32
  197. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +35 -32
  198. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +35 -32
  199. package/dist/astro/routes/api/taxonomies/index.mjs +35 -32
  200. package/dist/astro/routes/api/themes/preview.mjs +6 -6
  201. package/dist/astro/routes/api/typegen.mjs +4 -4
  202. package/dist/astro/routes/api/well-known/auth.mjs +3 -3
  203. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +3 -3
  204. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +3 -3
  205. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +31 -28
  206. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +32 -29
  207. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +32 -29
  208. package/dist/astro/routes/api/widget-areas/_name_.mjs +29 -27
  209. package/dist/astro/routes/api/widget-areas/index.mjs +32 -29
  210. package/dist/astro/routes/api/widget-components.mjs +2 -2
  211. package/dist/astro/routes/robots.txt.mjs +9 -8
  212. package/dist/astro/routes/sitemap-_collection_.xml.mjs +24 -15
  213. package/dist/astro/routes/sitemap.xml.mjs +10 -9
  214. package/dist/astro/session-driver.d.mts +1 -1
  215. package/dist/astro/session-driver.mjs +3 -3
  216. package/dist/astro/types.d.mts +21 -13
  217. package/dist/{audit-D1cbG7xH.mjs → audit-ByVKIwWF.mjs} +1 -1
  218. package/dist/auth/providers/github.d.mts +1 -1
  219. package/dist/auth/providers/google.d.mts +1 -1
  220. package/dist/{auth-control-guard-Dr14I98m.mjs → auth-control-guard-CqeUmQsv.mjs} +1 -1
  221. package/dist/{authorize-BLO0m7fW.mjs → authorize-CcBxcHtz.mjs} +1 -1
  222. package/dist/{briefing-CObXGOGz.mjs → briefing-BJaJI8dm.mjs} +2 -1
  223. package/dist/{briefing-gKWiGLt1.mjs → briefing-CawZ59CY.mjs} +90 -28
  224. package/dist/bundle-7C_DpkEq.mjs +602 -0
  225. package/dist/{byline-1ntDwv8c.mjs → byline-e51-fWRs.mjs} +30 -62
  226. package/dist/{byline-fields-BX7rWKNU.d.mts → byline-fields-CUvbslGc.d.mts} +49 -6
  227. package/dist/{byline-fields-C9_iaPaM.mjs → byline-fields-D-N5qVIG.mjs} +1 -1
  228. package/dist/{byline-fields-B-ZkoVft.mjs → byline-fields-DOJO16Gm.mjs} +3 -3
  229. package/dist/{byline-registry-CUSvkasJ.mjs → byline-registry-87nNnYl4.mjs} +2 -2
  230. package/dist/{bylines-bC15ZqAv.mjs → bylines-CMQNCNFh.mjs} +9 -3
  231. package/dist/{bylines-DtxChhN_.mjs → bylines-y4LO-dTj.mjs} +5 -5
  232. package/dist/{cache-DbKyr60Q.mjs → cache-C-FNTFYK.mjs} +2 -2
  233. package/dist/{chunks-C5zp2xir.mjs → chunks-CxBt-GxR.mjs} +1 -1
  234. package/dist/cli/index.mjs +820 -1276
  235. package/dist/client/external-auth-headers.d.mts +1 -1
  236. package/dist/client/index.d.mts +1 -1
  237. package/dist/client/index.mjs +2 -1
  238. package/dist/{comment-CXDOQ93N.mjs → comment-DlA4iChg.mjs} +13 -3
  239. package/dist/comment-reaction-DfFao1ST.mjs +58 -0
  240. package/dist/{comments-hK-rdzJ9.mjs → comments-DApFGMsP.mjs} +1 -1
  241. package/dist/{content-QaryevFO.mjs → content-Bh4BDjv8.mjs} +51 -34
  242. package/dist/content-refresh-CgutSUfO.mjs +1576 -0
  243. package/dist/{context-CoYb_fvK.mjs → context-C5W3G18y.mjs} +111 -50
  244. package/dist/{context-DBL1ib4v.mjs → context-CK4ym2Sf.mjs} +2 -2
  245. package/dist/{context-route-helpers-CYQVmbIm.mjs → context-route-helpers-MASyGgWf.mjs} +2 -1
  246. package/dist/{cron-DxCAU_Sn.mjs → cron-cKcNTix_.mjs} +7 -3
  247. package/dist/{dashboard-DNlqsknJ.mjs → dashboard-DQkVT7Ei.mjs} +4 -4
  248. package/dist/db/index.d.mts +3 -3
  249. package/dist/db/index.mjs +1 -1
  250. package/dist/db/libsql.d.mts +1 -1
  251. package/dist/db/postgres.d.mts +1 -1
  252. package/dist/db/sqlite.d.mts +1 -1
  253. package/dist/{device-flow-DygsFPGL.mjs → device-flow-BnqRH-T9.mjs} +5 -5
  254. package/dist/{placeholder-BDo81Is5.mjs → enrich-DIFIFA8O.mjs} +36 -9
  255. package/dist/{error-DCSuUAZW.mjs → error-CytFlgcd.mjs} +5 -0
  256. package/dist/field-defs-cache-DRNdhBJv.mjs +49 -0
  257. package/dist/{fts-manager-CIZlScg5.mjs → fts-manager-Crzypjqu.mjs} +1 -1
  258. package/dist/hitl-Hw1RmWOq.mjs +677 -0
  259. package/dist/{hitl-requests-CGQwOEiq.mjs → hitl-requests-B4Vbcmaq.mjs} +2 -2
  260. package/dist/{hitl-route-helpers-GSDLNPpN.mjs → hitl-route-helpers-hEVzvP8l.mjs} +5 -4
  261. package/dist/import-Bh9biJfx.mjs +1354 -0
  262. package/dist/{index-CwRuZ-u8.d.mts → index-D4y6NGiy.d.mts} +75 -3
  263. package/dist/index.d.mts +17 -16
  264. package/dist/index.mjs +81 -69
  265. package/dist/{jsonld-DZ67souc.d.mts → jsonld-Dq0gsnOH.d.mts} +8 -4
  266. package/dist/{load-xE01Jm0V.mjs → load-DmYeniXl.mjs} +1 -1
  267. package/dist/{loader-tg63WSY2.mjs → loader-CjWqjweO.mjs} +117 -31
  268. package/dist/{manifest-schema-qh2P7RDX.mjs → manifest-schema-BxVPYMJR.mjs} +34 -1
  269. package/dist/mcp-reviewed-executor-TwVbMzG6.mjs +555 -0
  270. package/dist/media/image-endpoint.d.mts +32 -0
  271. package/dist/media/image-endpoint.mjs +111 -0
  272. package/dist/media/index.d.mts +27 -2
  273. package/dist/media/index.mjs +5 -3
  274. package/dist/media/local-runtime.d.mts +16 -11
  275. package/dist/media/local-runtime.mjs +10 -8
  276. package/dist/{media-DUAjMfvR.mjs → media-DMj0grzC.mjs} +3 -1
  277. package/dist/{media-allowlist-BsXkT9RK.mjs → media-allowlist-BlVCSzHx.mjs} +1 -1
  278. package/dist/media-url-lEKSrpr9.mjs +20 -0
  279. package/dist/{menus-WpFRMXjz.mjs → menus-TaW1gKnk.mjs} +19 -5
  280. package/dist/{menus-DU1eZqw_.mjs → menus-xvsnVZXw.mjs} +80 -15
  281. package/dist/{normalize-DUnoXAa9.mjs → normalize-5ek_z7de.mjs} +8 -1
  282. package/dist/{oauth-authorization-D-1p52Je.mjs → oauth-authorization-DiaGvRVz.mjs} +5 -5
  283. package/dist/{oauth-clients-CzeMGyUU.mjs → oauth-clients-Dl9E8dEM.mjs} +2 -2
  284. package/dist/object-cache/memory.d.mts +14 -0
  285. package/dist/object-cache/memory.mjs +55 -0
  286. package/dist/object-cache-D51qOseS.mjs +419 -0
  287. package/dist/page/index.d.mts +4 -3
  288. package/dist/page/index.mjs +2 -2
  289. package/dist/{parse-CneSgl7-.mjs → parse-CbymENaR.mjs} +1 -1
  290. package/dist/{parser-DxfsARXL.mjs → parser-C1nYd-81.mjs} +151 -9
  291. package/dist/{placeholder-CXfoCtCd.d.mts → placeholder-uvNRZBX9.d.mts} +12 -4
  292. package/dist/plugin-utils.d.mts +9 -9
  293. package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
  294. package/dist/plugins/adapt-sandbox-entry.mjs +11 -2
  295. package/dist/{plugins-C57YQi4l.mjs → plugins-BolVa-tl.mjs} +832 -64
  296. package/dist/{transport-LSdZVXHF.mjs → portable-text-dA_UlJwW.mjs} +1 -134
  297. package/dist/{preview-D5rvDhRt.mjs → preview-vSVg6fwG.mjs} +4 -3
  298. package/dist/process-cache-7xgAO9JY.mjs +106 -0
  299. package/dist/{provider-loader-9it4vd6L.d.mts → provider-loader-MD8zAbnH.d.mts} +1 -1
  300. package/dist/{public-url-DLUM5OOv.mjs → public-url-BEz_sM9n.mjs} +24 -18
  301. package/dist/{public-url-B5HaoGzj.d.mts → public-url-gsp3oUbu.d.mts} +5 -4
  302. package/dist/{query-BYc3oruA.mjs → query-CfRCdaL3.mjs} +281 -76
  303. package/dist/{query-DKEwzobh.mjs → query-jaAmw8E1.mjs} +11 -6
  304. package/dist/{rate-limit-i0G3IAOX.mjs → rate-limit-ZLdyWoZF.mjs} +2 -2
  305. package/dist/{redirect-C2zXRj3c.mjs → redirect-BAjt4Z6b.mjs} +2 -2
  306. package/dist/{redirects-YZo_znWG.mjs → redirects-BTdwpQtE.mjs} +47 -24
  307. package/dist/{redirects-ocRJmQHP.mjs → redirects-CA-xylPG.mjs} +19 -11
  308. package/dist/{registry-CTkbIDB9.mjs → registry-CtyVedcN.mjs} +130 -77
  309. package/dist/request-context.d.mts +2 -0
  310. package/dist/{request-meta-DAb6xaP9.mjs → request-meta-CUypToDJ.mjs} +1 -1
  311. package/dist/{review-requests-C0un5kdV.mjs → review-requests-C9Ff11Vq.mjs} +1 -1
  312. package/dist/{review-requests-gGMyYYhy.mjs → review-requests-DUyEmtmG.mjs} +1 -1
  313. package/dist/{runner-D4FL9usy.d.mts → runner-1tC2tIRV.d.mts} +1 -1
  314. package/dist/runner-DB7CRfOF.mjs +6740 -0
  315. package/dist/{runtime-DEWQpZ-4.d.mts → runtime-BOeiQsCv.d.mts} +2028 -1564
  316. package/dist/runtime.d.mts +10 -10
  317. package/dist/runtime.mjs +4 -4
  318. package/dist/schema-DXdYj4R3.mjs +8 -0
  319. package/dist/{search-axGQ1KY3.mjs → search-DSaLgEFI.mjs} +5 -3
  320. package/dist/{secrets-062Izo8i.mjs → secrets-CDZo2k8L.mjs} +15 -12
  321. package/dist/{sections-OLKgXL10.mjs → sections-PVVXHGfp.mjs} +2 -2
  322. package/dist/seed/index.d.mts +2 -2
  323. package/dist/seed/index.mjs +22 -19
  324. package/dist/seo/index.d.mts +1 -1
  325. package/dist/seo/index.mjs +3 -12
  326. package/dist/{seo-Cd45Po8L.mjs → seo-CQG3GCHQ.mjs} +5 -2
  327. package/dist/{seo-contributions-CqVvReEj.mjs → seo-contributions-C1EnikHB.mjs} +11 -3
  328. package/dist/{seo-5lJOtB5J.mjs → seo-qa7Dn0jK.mjs} +22 -4
  329. package/dist/{service-Bwad8s5h.mjs → service-DiN4ZuPp.mjs} +2 -2
  330. package/dist/session-user-CAKImna3.mjs +22 -0
  331. package/dist/{settings-yLjz_I4T.mjs → settings-BbqgHWpU.mjs} +28 -55
  332. package/dist/{settings-lAHjaUit.mjs → settings-i4VPP8bT.mjs} +2 -2
  333. package/dist/{setup-complete--PradA4R.mjs → setup-complete-B8fl-mjV.mjs} +1 -1
  334. package/dist/{site-activity-D7Xyropw.mjs → site-activity-Dy3FROs0.mjs} +1 -1
  335. package/dist/{site-context-DaN9Z91j.mjs → site-context-DQiL8Fl5.mjs} +23 -652
  336. package/dist/{site-url-DpGZo9Z6.mjs → site-url-BML3GQYm.mjs} +1 -1
  337. package/dist/status-DANd8ERo.mjs +21 -0
  338. package/dist/storage/local.d.mts +1 -1
  339. package/dist/storage/local.mjs +1 -1
  340. package/dist/storage/s3.d.mts +1 -1
  341. package/dist/storage/s3.mjs +1 -1
  342. package/dist/{taxonomies-uXVEoQso.mjs → taxonomies-9s-v03H-.mjs} +36 -29
  343. package/dist/taxonomies-DRiixp_E.mjs +443 -0
  344. package/dist/{taxonomy-CBmkmMuU.mjs → taxonomy-Dvsw6BOs.mjs} +44 -9
  345. package/dist/{tokens-CjSgb0AC.mjs → tokens-1RK1n6ED.mjs} +1 -1
  346. package/dist/transport-BNmCqLu7.mjs +134 -0
  347. package/dist/types-4garmdWa.mjs +66 -0
  348. package/dist/{types-DSPGWjxO.d.mts → types-C8XW6L19.d.mts} +225 -4
  349. package/dist/{types-GaCtZ1k9.d.mts → types-CDpH6MA4.d.mts} +18 -0
  350. package/dist/{types-DRIKLmJI.d.mts → types-C_ly5eTp.d.mts} +17 -2
  351. package/dist/types-DF7ZuGGk.d.mts +118 -0
  352. package/dist/types-DU0bU2Wu.d.mts +2392 -0
  353. package/dist/{types-CwawBUTa.d.mts → types-DW_-sFyz.d.mts} +2 -1
  354. package/dist/{types-DfuD0Afl.mjs → types-Dq1qT19x.mjs} +2 -2
  355. package/dist/ui/server-runtime.d.mts +15 -15
  356. package/dist/ui/server-runtime.mjs +25 -21
  357. package/dist/url-Cac7a9QZ.mjs +33 -0
  358. package/dist/{user-DtNmjNYt.mjs → user-BIZPzCb9.mjs} +2 -2
  359. package/dist/{utils-wv-zvC-Y.mjs → utils-CMDhr5Xd.mjs} +129 -7
  360. package/dist/{validate-BMs11tan.d.mts → validate-CZvb-BKB.d.mts} +10 -7
  361. package/dist/{validate-7SYRbz2f.mjs → validate-DNs0pTSc.mjs} +11 -6
  362. package/dist/version-BTvfRGc6.mjs +6 -0
  363. package/dist/{widgets-DR8EHuRO.mjs → widgets-f0ibZUIq.mjs} +46 -43
  364. package/dist/{wordpress-slugs-Dvhxh4cl.mjs → wordpress-slugs-BhHJX8XE.mjs} +1 -1
  365. package/dist/{zod-generator-BS9-umbk.mjs → zod-generator-B7TMjyFJ.mjs} +41 -13
  366. package/package.json +31 -8
  367. package/src/components/Block.astro +50 -0
  368. package/src/components/Comments.astro +143 -1
  369. package/src/components/DinewayHead.astro +19 -2
  370. package/src/components/DinewayImage.astro +66 -33
  371. package/src/components/DinewayMedia.astro +1 -2
  372. package/src/components/Image.astro +85 -40
  373. package/src/components/InlinePortableTextEditor.tsx +12 -2
  374. package/src/components/JsonLdScript.astro +9 -0
  375. package/src/components/LiveSearch.astro +40 -4
  376. package/src/components/index.ts +4 -0
  377. package/src/components/live-search-routing.ts +30 -0
  378. package/src/components/portable-text-text-align.ts +15 -0
  379. package/src/ui.ts +2 -0
  380. package/dist/entity-aliases-Egjmqc7I.mjs +0 -51
  381. package/dist/import-CvHY6sf5.mjs +0 -684
  382. package/dist/runner-B-tbV751.mjs +0 -2741
  383. package/dist/schema-B6zyB-Xn.mjs +0 -8
  384. package/dist/taxonomies-CNOGeC-3.mjs +0 -327
  385. package/dist/types-eu5UDgHN.d.mts +0 -652
  386. package/dist/version-xlzMSXF2.mjs +0 -6
  387. /package/dist/{adapters-OQCImOL7.d.mts → adapters-NVSs4UH7.d.mts} +0 -0
  388. /package/dist/{api-tokens-CYUoFGo7.mjs → api-tokens-2r7OWi_I.mjs} +0 -0
  389. /package/dist/{base64-C1Q9yr0B.mjs → base64-F8-DUraK.mjs} +0 -0
  390. /package/dist/{challenge-store-CRSz-Xim.mjs → challenge-store-Bjlepa2E.mjs} +0 -0
  391. /package/dist/{components--pg6zBvu.mjs → components-Bclf98Be.mjs} +0 -0
  392. /package/dist/{context-types-DQh_XKkn.mjs → context-types-DqTo6C79.mjs} +0 -0
  393. /package/dist/{default-B6iaLvP7.mjs → default--K40jqm0.mjs} +0 -0
  394. /package/dist/{email-console-BOVqIYpB.mjs → email-console-ChgKFO8-.mjs} +0 -0
  395. /package/dist/{escape-BDgouFFY.mjs → escape-Bd4P_xKg.mjs} +0 -0
  396. /package/dist/{experimental-workflows-Dh4DUoD_.mjs → experimental-workflows-BMsUnIAw.mjs} +0 -0
  397. /package/dist/{mime-8pAB8-77.mjs → mime-CA7Lgovc.mjs} +0 -0
  398. /package/dist/{mode-CpyEz8pe.mjs → mode-C4CIyjtR.mjs} +0 -0
  399. /package/dist/{oauth-state-store-qncPB_IU.mjs → oauth-state-store-CNoXafF7.mjs} +0 -0
  400. /package/dist/{oauth-user-lookup-BFjWF8wK.mjs → oauth-user-lookup-Ygw4bNyt.mjs} +0 -0
  401. /package/dist/{options-BvJoSSJR.mjs → options-DQPTH5n0.mjs} +0 -0
  402. /package/dist/{passkey-config-DzHQK90v.mjs → passkey-config-BUwu8BA3.mjs} +0 -0
  403. /package/dist/{patterns-r-0n824Y.mjs → patterns-BhAvqO0S.mjs} +0 -0
  404. /package/dist/{provider-loader-DzUxejk7.mjs → provider-loader-D0w9dqjm.mjs} +0 -0
  405. /package/dist/{redirect-BjIBtDWY.mjs → redirect-wphRBv4y.mjs} +0 -0
  406. /package/dist/{request-cache-CYMbwZc6.mjs → request-cache-D9GQBxMP.mjs} +0 -0
  407. /package/dist/{resolve-DTuDyYKs.mjs → resolve-Cb6Dbz-C.mjs} +0 -0
  408. /package/dist/{setup-nonce-pyGfc9hV.mjs → setup-nonce-CoyjILvW.mjs} +0 -0
  409. /package/dist/{sidecar-client-mJxwayYN.mjs → sidecar-client-C3mqXg4C.mjs} +0 -0
  410. /package/dist/{slugify-BzGxlOFx.mjs → slugify-KSsVD_D3.mjs} +0 -0
  411. /package/dist/{ssrf-DiKqWd3N.mjs → ssrf-DWe8jwAp.mjs} +0 -0
  412. /package/dist/{transaction-CShMWIej.mjs → transaction-C2EOMTJA.mjs} +0 -0
  413. /package/dist/{transport-BnsNxh2l.d.mts → transport-CcJu3Co8.d.mts} +0 -0
  414. /package/dist/{trusted-proxy-BgIIcbsN.mjs → trusted-proxy-DTXQ2IMv.mjs} +0 -0
  415. /package/dist/{types-BO8TGjS8.mjs → types-B3t5cxhU.mjs} +0 -0
  416. /package/dist/{types-nP9vGUFq.d.mts → types-C46EvNdc.d.mts} +0 -0
  417. /package/dist/{types-Cg276-3z.mjs → types-C5CVgwsV.mjs} +0 -0
  418. /package/dist/{types-DZpT-PnG.d.mts → types-POH8PQZh.d.mts} +0 -0
  419. /package/dist/{types-Dryvv7LH.d.mts → types-Rc3gQqqw.d.mts} +0 -0
  420. /package/dist/{url-w7Mp5qAD.mjs → url-CpyFDFpO.mjs} +0 -0
@@ -1,14 +1,75 @@
1
- import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-C5zp2xir.mjs";
2
- import { t as OptionsRepository } from "./options-BvJoSSJR.mjs";
3
- import { n as createPluginContext, t as PluginContextFactory } from "./context-CoYb_fvK.mjs";
4
- import { t as extractRequestMeta } from "./request-meta-DAb6xaP9.mjs";
5
- import { r as setCronTasksEnabled } from "./cron-DxCAU_Sn.mjs";
1
+ import { r as validatePluginIdentifier, t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
2
+ import { i as encodeBase64url, n as decodeBase64url } from "./base64-F8-DUraK.mjs";
3
+ import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-CxBt-GxR.mjs";
4
+ import { t as OptionsRepository } from "./options-DQPTH5n0.mjs";
5
+ import { n as createPluginContext, t as PluginContextFactory } from "./context-C5W3G18y.mjs";
6
+ import { t as extractRequestMeta } from "./request-meta-CUypToDJ.mjs";
7
+ import { r as setCronTasksEnabled } from "./cron-cKcNTix_.mjs";
8
+ import { i as parseEncryptionKeys, t as DinewaySecretsError } from "./secrets-CDZo2k8L.mjs";
6
9
  import { sql } from "kysely";
7
- import { ulid } from "ulidx";
8
10
  import { AsyncLocalStorage } from "node:async_hooks";
11
+ import { ulid } from "ulidx";
9
12
  import { z } from "astro/zod";
13
+ import { Permissions } from "@dineway-ai/auth";
10
14
  import { Worker } from "node:worker_threads";
11
15
 
16
+ //#region src/database/repositories/plugin-secrets.ts
17
+ var PluginSecretRepository = class {
18
+ constructor(db) {
19
+ this.db = db;
20
+ }
21
+ async put(input) {
22
+ const now = input.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
23
+ const row = {
24
+ plugin_id: input.pluginId,
25
+ secret_name: input.name,
26
+ encryption_version: input.encryptionVersion,
27
+ key_id: input.keyId,
28
+ nonce: input.nonce,
29
+ ciphertext: input.ciphertext,
30
+ fingerprint: input.fingerprint,
31
+ created_at: now,
32
+ updated_at: now
33
+ };
34
+ await this.db.insertInto("_dineway_plugin_secrets").values(row).onConflict((conflict) => conflict.columns(["plugin_id", "secret_name"]).doUpdateSet({
35
+ encryption_version: row.encryption_version,
36
+ key_id: row.key_id,
37
+ nonce: row.nonce,
38
+ ciphertext: row.ciphertext,
39
+ fingerprint: row.fingerprint,
40
+ updated_at: now
41
+ })).execute();
42
+ const stored = await this.find(input.pluginId, input.name);
43
+ if (!stored) throw new Error("Plugin secret write did not persist.");
44
+ return stored;
45
+ }
46
+ async find(pluginId, name) {
47
+ const row = await this.db.selectFrom("_dineway_plugin_secrets").selectAll().where("plugin_id", "=", pluginId).where("secret_name", "=", name).executeTakeFirst();
48
+ return row ? rowToStoredPluginSecret(row) : null;
49
+ }
50
+ async list(pluginId) {
51
+ return (await this.db.selectFrom("_dineway_plugin_secrets").selectAll().where("plugin_id", "=", pluginId).orderBy("secret_name", "asc").execute()).map(rowToStoredPluginSecret);
52
+ }
53
+ async delete(pluginId, name) {
54
+ const result = await this.db.deleteFrom("_dineway_plugin_secrets").where("plugin_id", "=", pluginId).where("secret_name", "=", name).executeTakeFirst();
55
+ return Number(result.numDeletedRows ?? 0) > 0;
56
+ }
57
+ };
58
+ function rowToStoredPluginSecret(row) {
59
+ return {
60
+ pluginId: row.plugin_id,
61
+ name: row.secret_name,
62
+ encryptionVersion: row.encryption_version,
63
+ keyId: row.key_id,
64
+ nonce: row.nonce,
65
+ ciphertext: row.ciphertext,
66
+ fingerprint: row.fingerprint,
67
+ createdAt: row.created_at,
68
+ updatedAt: row.updated_at
69
+ };
70
+ }
71
+
72
+ //#endregion
12
73
  //#region src/database/repositories/relation.ts
13
74
  var RelationRepository = class {
14
75
  constructor(db) {
@@ -267,6 +328,9 @@ function portableText(options) {
267
328
  function generateKey() {
268
329
  return Math.random().toString(36).substring(2, 11);
269
330
  }
331
+ function normalizeTextAlign(value) {
332
+ return value === "center" || value === "right" || value === "justify" ? value : void 0;
333
+ }
270
334
  /**
271
335
  * Convert ProseMirror document to Portable Text
272
336
  */
@@ -311,13 +375,15 @@ function convertNode(node) {
311
375
  */
312
376
  function convertParagraph(node) {
313
377
  const { children, markDefs } = convertInlineContent(node.content || []);
378
+ const textAlign = normalizeTextAlign(node.attrs?.textAlign);
314
379
  if (children.length === 0) return null;
315
380
  return {
316
381
  _type: "block",
317
382
  _key: generateKey(),
318
383
  style: "normal",
319
384
  children,
320
- markDefs: markDefs.length > 0 ? markDefs : void 0
385
+ markDefs: markDefs.length > 0 ? markDefs : void 0,
386
+ ...textAlign ? { textAlign } : {}
321
387
  };
322
388
  }
323
389
  /** Map heading level number to Portable Text style */
@@ -338,13 +404,15 @@ function headingLevelToStyle(level) {
338
404
  function convertHeading(node) {
339
405
  const { children, markDefs } = convertInlineContent(node.content || []);
340
406
  const style = headingLevelToStyle(typeof node.attrs?.level === "number" ? node.attrs.level : 1);
407
+ const textAlign = normalizeTextAlign(node.attrs?.textAlign);
341
408
  if (children.length === 0) return null;
342
409
  return {
343
410
  _type: "block",
344
411
  _key: generateKey(),
345
412
  style,
346
413
  children,
347
- markDefs: markDefs.length > 0 ? markDefs : void 0
414
+ markDefs: markDefs.length > 0 ? markDefs : void 0,
415
+ ...textAlign ? { textAlign } : {}
348
416
  };
349
417
  }
350
418
  /**
@@ -612,6 +680,7 @@ function convertBlock(block) {
612
680
  */
613
681
  function convertTextBlock(block) {
614
682
  const { style = "normal", children, markDefs = [] } = block;
683
+ const textAlignAttrs = block.textAlign ? { textAlign: block.textAlign } : void 0;
615
684
  const content = convertSpans(children, markDefs);
616
685
  switch (style) {
617
686
  case "h1":
@@ -621,7 +690,10 @@ function convertTextBlock(block) {
621
690
  case "h5":
622
691
  case "h6": return {
623
692
  type: "heading",
624
- attrs: { level: parseInt(style.substring(1), 10) },
693
+ attrs: {
694
+ level: parseInt(style.substring(1), 10),
695
+ ...textAlignAttrs
696
+ },
625
697
  content: content.length > 0 ? content : void 0
626
698
  };
627
699
  case "blockquote": return {
@@ -633,6 +705,7 @@ function convertTextBlock(block) {
633
705
  };
634
706
  default: return {
635
707
  type: "paragraph",
708
+ attrs: textAlignAttrs,
636
709
  content: content.length > 0 ? content : void 0
637
710
  };
638
711
  }
@@ -825,18 +898,343 @@ function convertHtmlBlock(block) {
825
898
  }
826
899
 
827
900
  //#endregion
828
- //#region src/after.ts
829
- function after(fn) {
830
- Promise.resolve().then(fn).catch((error) => {
831
- console.error("[dineway] deferred task failed:", error);
901
+ //#region src/security/guest-access-tokens.ts
902
+ const TOKEN_VERSION = 1;
903
+ const DEFAULT_TTL_SECONDS = 720 * 60 * 60;
904
+ const MAX_TTL_SECONDS = 3 * 366 * 24 * 60 * 60;
905
+ const MAX_CLOCK_SKEW_SECONDS = 60;
906
+ const MAX_TOKEN_LENGTH = 4096;
907
+ const MIN_SECRET_BYTES = 32;
908
+ const MAX_SECRET_BYTES$1 = 4096;
909
+ const MAX_VERIFICATION_SECRETS = 8;
910
+ const SIGNATURE_BYTES = 32;
911
+ const DURATION_PATTERN = /^(\d+)([smhdw])$/;
912
+ const BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
913
+ const TOKEN_ID_PATTERN = /^gt_[A-Za-z0-9_-]{32}$/;
914
+ const PURPOSE_PATTERN = /^[a-z][a-z0-9_.:-]{0,127}$/;
915
+ const SUBJECT_TYPE_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
916
+ const OPAQUE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/;
917
+ const SIGNING_DOMAIN = "dineway-guest-access:v1";
918
+ const HASH_DOMAIN = "dineway-guest-access-hash:v1";
919
+ var GuestAccessTokenConfigurationError = class extends Error {
920
+ name = "GuestAccessTokenConfigurationError";
921
+ constructor(message, code) {
922
+ super(message);
923
+ this.code = code;
924
+ }
925
+ };
926
+ /**
927
+ * Issues an expiring capability for one guest purpose and one restaurant target.
928
+ * Persist only tokenHash/tokenId and revocation metadata; never persist or log token.
929
+ */
930
+ async function issueGuestAccessToken(options) {
931
+ validateSecret(options.secret);
932
+ validateScope({
933
+ purpose: options.purpose,
934
+ subjectType: options.subjectType,
935
+ subjectId: options.subjectId,
936
+ locationId: options.locationId
832
937
  });
938
+ if (options.tokenId !== void 0 && !TOKEN_ID_PATTERN.test(options.tokenId)) invalidInput("Guest access token identifier is invalid.");
939
+ const issuedAt = normalizeNow(options.now);
940
+ const ttl = parseDuration(options.expiresIn ?? DEFAULT_TTL_SECONDS);
941
+ const claims = {
942
+ version: TOKEN_VERSION,
943
+ tokenId: options.tokenId ?? randomTokenId(),
944
+ purpose: options.purpose,
945
+ subjectType: options.subjectType,
946
+ subjectId: options.subjectId,
947
+ locationId: options.locationId,
948
+ issuedAt,
949
+ expiresAt: issuedAt + ttl
950
+ };
951
+ const encodedPayload = encodeBase64url(new TextEncoder().encode(JSON.stringify(claims)));
952
+ const token = `${encodedPayload}.${encodeBase64url(await sign(encodedPayload, options.secret))}`;
953
+ return {
954
+ token,
955
+ tokenHash: await hashGuestAccessToken(token),
956
+ claims
957
+ };
958
+ }
959
+ /**
960
+ * Verifies signature, expiry, and the caller-declared purpose/target scope.
961
+ * A valid result still requires the plugin to check its revocation record by tokenHash.
962
+ */
963
+ async function verifyGuestAccessToken(options) {
964
+ if (!options.token) return {
965
+ valid: false,
966
+ error: "none"
967
+ };
968
+ validateExpectedScope(options.expected);
969
+ const secrets = normalizeSecrets(options.secrets);
970
+ const token = options.token;
971
+ if (token.length > MAX_TOKEN_LENGTH) return {
972
+ valid: false,
973
+ error: "malformed"
974
+ };
975
+ const parts = token.split(".");
976
+ if (parts.length !== 2) return {
977
+ valid: false,
978
+ error: "malformed"
979
+ };
980
+ const encodedPayload = parts[0];
981
+ const encodedSignature = parts[1];
982
+ if (!encodedPayload || !encodedSignature) return {
983
+ valid: false,
984
+ error: "malformed"
985
+ };
986
+ const signature = decodeCanonicalBase64url(encodedSignature);
987
+ if (!signature || signature.byteLength !== SIGNATURE_BYTES) return {
988
+ valid: false,
989
+ error: "malformed"
990
+ };
991
+ let signatureValid = false;
992
+ for (const secret of secrets) if (await verifySignature(encodedPayload, signature, secret)) {
993
+ signatureValid = true;
994
+ break;
995
+ }
996
+ if (!signatureValid) return {
997
+ valid: false,
998
+ error: "invalid"
999
+ };
1000
+ const claims = decodeClaims(encodedPayload);
1001
+ if (!claims) return {
1002
+ valid: false,
1003
+ error: "malformed"
1004
+ };
1005
+ const now = normalizeNow(options.now);
1006
+ if (claims.issuedAt > now + MAX_CLOCK_SKEW_SECONDS) return {
1007
+ valid: false,
1008
+ error: "invalid"
1009
+ };
1010
+ if (claims.expiresAt <= now) return {
1011
+ valid: false,
1012
+ error: "expired"
1013
+ };
1014
+ if (!matchesExpectedScope(claims, options.expected)) return {
1015
+ valid: false,
1016
+ error: "scope_mismatch"
1017
+ };
1018
+ return {
1019
+ valid: true,
1020
+ claims,
1021
+ tokenHash: await hashGuestAccessToken(token)
1022
+ };
1023
+ }
1024
+ /** Returns the non-reversible identifier stored for revocation and lookup. */
1025
+ async function hashGuestAccessToken(token) {
1026
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${HASH_DOMAIN}.${token}`));
1027
+ return encodeBase64url(new Uint8Array(digest));
1028
+ }
1029
+ async function sign(encodedPayload, secret) {
1030
+ const key = await importHmacKey(secret, ["sign"]);
1031
+ const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${SIGNING_DOMAIN}.${encodedPayload}`));
1032
+ return new Uint8Array(signature);
1033
+ }
1034
+ async function verifySignature(encodedPayload, signature, secret) {
1035
+ const key = await importHmacKey(secret, ["verify"]);
1036
+ return crypto.subtle.verify("HMAC", key, toArrayBuffer$2(signature), new TextEncoder().encode(`${SIGNING_DOMAIN}.${encodedPayload}`));
1037
+ }
1038
+ async function importHmacKey(secret, usages) {
1039
+ return crypto.subtle.importKey("raw", new TextEncoder().encode(secret), {
1040
+ name: "HMAC",
1041
+ hash: "SHA-256"
1042
+ }, false, usages);
1043
+ }
1044
+ function decodeClaims(encodedPayload) {
1045
+ const bytes = decodeCanonicalBase64url(encodedPayload);
1046
+ if (!bytes) return null;
1047
+ let value;
1048
+ try {
1049
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
1050
+ } catch {
1051
+ return null;
1052
+ }
1053
+ if (!isRecord$1(value) || !hasExactClaimKeys(value)) return null;
1054
+ if (value.version !== TOKEN_VERSION || typeof value.tokenId !== "string" || typeof value.purpose !== "string" || typeof value.subjectType !== "string" || typeof value.subjectId !== "string" || typeof value.locationId !== "string" || typeof value.issuedAt !== "number" || typeof value.expiresAt !== "number") return null;
1055
+ if (!TOKEN_ID_PATTERN.test(value.tokenId) || !PURPOSE_PATTERN.test(value.purpose) || !SUBJECT_TYPE_PATTERN.test(value.subjectType) || !OPAQUE_ID_PATTERN.test(value.subjectId) || !OPAQUE_ID_PATTERN.test(value.locationId) || !Number.isSafeInteger(value.issuedAt) || !Number.isSafeInteger(value.expiresAt) || value.expiresAt <= value.issuedAt || value.expiresAt - value.issuedAt > MAX_TTL_SECONDS) return null;
1056
+ return {
1057
+ version: TOKEN_VERSION,
1058
+ tokenId: value.tokenId,
1059
+ purpose: value.purpose,
1060
+ subjectType: value.subjectType,
1061
+ subjectId: value.subjectId,
1062
+ locationId: value.locationId,
1063
+ issuedAt: value.issuedAt,
1064
+ expiresAt: value.expiresAt
1065
+ };
1066
+ }
1067
+ function hasExactClaimKeys(value) {
1068
+ const keys = Object.keys(value).toSorted();
1069
+ return keys.length === 8 && keys.join(",") === "expiresAt,issuedAt,locationId,purpose,subjectId,subjectType,tokenId,version";
1070
+ }
1071
+ function matchesExpectedScope(claims, expected) {
1072
+ return claims.purpose === expected.purpose && claims.subjectType === expected.subjectType && (expected.subjectId === void 0 || claims.subjectId === expected.subjectId) && (expected.locationId === void 0 || claims.locationId === expected.locationId);
1073
+ }
1074
+ function validateExpectedScope(expected) {
1075
+ validatePurpose(expected.purpose);
1076
+ validateSubjectType(expected.subjectType);
1077
+ if (expected.subjectId !== void 0) validateOpaqueId(expected.subjectId, "subject id");
1078
+ if (expected.locationId !== void 0) validateOpaqueId(expected.locationId, "location id");
1079
+ }
1080
+ function validateScope(scope) {
1081
+ validatePurpose(scope.purpose);
1082
+ validateSubjectType(scope.subjectType);
1083
+ validateOpaqueId(scope.subjectId, "subject id");
1084
+ validateOpaqueId(scope.locationId, "location id");
1085
+ }
1086
+ function validatePurpose(value) {
1087
+ if (!PURPOSE_PATTERN.test(value)) invalidInput("Guest access purpose is invalid.");
1088
+ }
1089
+ function validateSubjectType(value) {
1090
+ if (!SUBJECT_TYPE_PATTERN.test(value)) invalidInput("Guest access subject type is invalid.");
1091
+ }
1092
+ function validateOpaqueId(value, label) {
1093
+ if (!OPAQUE_ID_PATTERN.test(value)) invalidInput(`Guest access ${label} is invalid.`);
1094
+ }
1095
+ function parseDuration(value) {
1096
+ let seconds;
1097
+ if (typeof value === "number") seconds = value;
1098
+ else {
1099
+ const match = DURATION_PATTERN.exec(value);
1100
+ if (!match) invalidInput("Guest access expiry duration is invalid.");
1101
+ seconds = Number(match[1]) * ({
1102
+ s: 1,
1103
+ m: 60,
1104
+ h: 3600,
1105
+ d: 1440 * 60,
1106
+ w: 10080 * 60
1107
+ }[match[2]] ?? 0);
1108
+ }
1109
+ if (!Number.isSafeInteger(seconds) || seconds <= 0 || seconds > MAX_TTL_SECONDS) invalidInput("Guest access expiry must be between one second and 366 days.");
1110
+ return seconds;
1111
+ }
1112
+ function normalizeSecrets(value) {
1113
+ const secrets = typeof value === "string" ? [value] : [...new Set(value)];
1114
+ if (secrets.length === 0 || secrets.length > MAX_VERIFICATION_SECRETS) throw new GuestAccessTokenConfigurationError(`Guest access verification requires 1-${MAX_VERIFICATION_SECRETS} secrets.`, "INVALID_GUEST_TOKEN_SECRET");
1115
+ for (const secret of secrets) validateSecret(secret);
1116
+ return secrets;
1117
+ }
1118
+ function validateSecret(secret) {
1119
+ const secretBytes = new TextEncoder().encode(secret).byteLength;
1120
+ if (secretBytes < MIN_SECRET_BYTES || secretBytes > MAX_SECRET_BYTES$1) throw new GuestAccessTokenConfigurationError(`Guest access signing secrets must contain ${MIN_SECRET_BYTES}-${MAX_SECRET_BYTES$1} UTF-8 bytes.`, "INVALID_GUEST_TOKEN_SECRET");
1121
+ }
1122
+ function normalizeNow(value) {
1123
+ const now = value ?? Math.floor(Date.now() / 1e3);
1124
+ if (!Number.isSafeInteger(now) || now < 0) invalidInput("Guest access clock value is invalid.");
1125
+ return now;
1126
+ }
1127
+ function randomTokenId() {
1128
+ return `gt_${encodeBase64url(crypto.getRandomValues(new Uint8Array(24)))}`;
1129
+ }
1130
+ function decodeCanonicalBase64url(value) {
1131
+ if (!BASE64URL_PATTERN.test(value)) return null;
1132
+ try {
1133
+ const decoded = decodeBase64url(value);
1134
+ return encodeBase64url(decoded) === value ? decoded : null;
1135
+ } catch {
1136
+ return null;
1137
+ }
1138
+ }
1139
+ function toArrayBuffer$2(bytes) {
1140
+ const buffer = new ArrayBuffer(bytes.byteLength);
1141
+ new Uint8Array(buffer).set(bytes);
1142
+ return buffer;
1143
+ }
1144
+ function isRecord$1(value) {
1145
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1146
+ }
1147
+ function invalidInput(message) {
1148
+ throw new GuestAccessTokenConfigurationError(message, "INVALID_GUEST_TOKEN_INPUT");
1149
+ }
1150
+
1151
+ //#endregion
1152
+ //#region src/plugins/route-body.ts
1153
+ const CONTENT_LENGTH_PATTERN = /^(?:0|[1-9][0-9]*)$/;
1154
+ const DEFAULT_PLUGIN_RAW_BODY_LIMIT_BYTES = 1024 * 1024;
1155
+ const MAX_PLUGIN_RAW_BODY_LIMIT_BYTES = 10 * 1024 * 1024;
1156
+ async function readBoundedPluginRouteBody(request, limit) {
1157
+ const declaredLength = request.headers.get("content-length");
1158
+ if (declaredLength && CONTENT_LENGTH_PATTERN.test(declaredLength) && Number(declaredLength) > limit) return {
1159
+ ok: false,
1160
+ limit
1161
+ };
1162
+ if (!request.body) return {
1163
+ ok: true,
1164
+ bytes: new Uint8Array()
1165
+ };
1166
+ const reader = request.body.getReader();
1167
+ const chunks = [];
1168
+ let total = 0;
1169
+ try {
1170
+ while (true) {
1171
+ const chunk = await reader.read();
1172
+ if (chunk.done) break;
1173
+ if (total + chunk.value.byteLength > limit) {
1174
+ await reader.cancel().catch(() => void 0);
1175
+ return {
1176
+ ok: false,
1177
+ limit
1178
+ };
1179
+ }
1180
+ chunks.push(chunk.value);
1181
+ total += chunk.value.byteLength;
1182
+ }
1183
+ } finally {
1184
+ reader.releaseLock();
1185
+ }
1186
+ const bytes = new Uint8Array(total);
1187
+ let offset = 0;
1188
+ for (const chunk of chunks) {
1189
+ bytes.set(chunk, offset);
1190
+ offset += chunk.byteLength;
1191
+ }
1192
+ return {
1193
+ ok: true,
1194
+ bytes
1195
+ };
833
1196
  }
834
1197
 
835
1198
  //#endregion
836
1199
  //#region src/plugins/define-plugin.ts
837
- const SIMPLE_ID = /^[a-z0-9-]+$/;
838
- const SCOPED_ID = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
839
- const SEMVER_PATTERN = /^\d+\.\d+\.\d+/;
1200
+ /**
1201
+ * definePlugin() Helper
1202
+ *
1203
+ * Creates a properly typed and normalized plugin definition.
1204
+ * Supports two formats:
1205
+ *
1206
+ * 1. **Native format** -- full PluginDefinition with id, version, capabilities, etc.
1207
+ * Returns a ResolvedPlugin.
1208
+ *
1209
+ * 2. **Standard format** -- just { hooks, routes }. No id/version/capabilities.
1210
+ * Returns the same object (identity function for type inference).
1211
+ * Metadata comes from the descriptor at config time.
1212
+ *
1213
+ */
1214
+ const MCP_NAME_PATTERN = /^[a-z][a-z0-9_]{2,127}$/;
1215
+ const MCP_RISK_FACTORS = new Set([
1216
+ "content",
1217
+ "availability",
1218
+ "time",
1219
+ "money",
1220
+ "allergen",
1221
+ "pii",
1222
+ "batch",
1223
+ "payment",
1224
+ "destructive",
1225
+ "external_side_effect"
1226
+ ]);
1227
+ const STRONG_RISK_FACTORS = new Set([
1228
+ "availability",
1229
+ "time",
1230
+ "money",
1231
+ "allergen",
1232
+ "pii",
1233
+ "batch",
1234
+ "payment",
1235
+ "destructive",
1236
+ "external_side_effect"
1237
+ ]);
840
1238
  function definePlugin(definition) {
841
1239
  if (!("id" in definition) || !("version" in definition)) {
842
1240
  if (!("hooks" in definition) && !("routes" in definition)) throw new Error("Standard plugin format requires at least `hooks` or `routes`. For native format, provide `id` and `version`.");
@@ -848,7 +1246,11 @@ function definePlugin(definition) {
848
1246
  * Internal: define a native-format plugin with full validation and normalization.
849
1247
  */
850
1248
  function defineNativePlugin(definition) {
1249
+ const SIMPLE_ID = /^[a-z0-9-]+$/;
1250
+ const SCOPED_ID = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
1251
+ const SEMVER_PATTERN = /^\d+\.\d+\.\d+/;
851
1252
  const { id, version, capabilities = [], allowedHosts = [], hooks = {}, routes = {}, admin = {} } = definition;
1253
+ const mcp = resolveMcp(definition.mcp, id);
852
1254
  const storage = definition.storage ?? {};
853
1255
  if (!SIMPLE_ID.test(id) && !SCOPED_ID.test(id)) throw new Error(`Invalid plugin id "${id}". Must be lowercase alphanumeric with dashes (e.g., "my-plugin" or "@scope/my-plugin").`);
854
1256
  if (!SEMVER_PATTERN.test(version)) throw new Error(`Invalid plugin version "${version}". Must be semver format (e.g., "1.0.0").`);
@@ -870,6 +1272,17 @@ function defineNativePlugin(definition) {
870
1272
  if (capabilities.includes("content:write") && !capabilities.includes("content:read")) normalizedCapabilities.push("content:read");
871
1273
  if (capabilities.includes("media:write") && !capabilities.includes("media:read")) normalizedCapabilities.push("media:read");
872
1274
  if (capabilities.includes("network:request:unrestricted") && !capabilities.includes("network:request")) normalizedCapabilities.push("network:request");
1275
+ for (const [routeName, route] of Object.entries(routes)) {
1276
+ if (route.body !== void 0 && route.body !== "json" && route.body !== "raw") throw new Error(`Plugin "${id}" route "${routeName}" has an invalid body mode.`);
1277
+ if (route.body === "raw" && route.input) throw new Error(`Plugin "${id}" raw-body route "${routeName}" cannot declare an input schema.`);
1278
+ if (route.body !== "raw" && route.maxBodyBytes !== void 0) throw new Error(`Plugin "${id}" JSON route "${routeName}" cannot declare a raw body-size limit.`);
1279
+ if (route.body === "raw" && route.maxBodyBytes !== void 0 && (!Number.isSafeInteger(route.maxBodyBytes) || route.maxBodyBytes < 1 || route.maxBodyBytes > MAX_PLUGIN_RAW_BODY_LIMIT_BYTES)) throw new Error(`Plugin "${id}" raw-body route "${routeName}" has an invalid body-size limit.`);
1280
+ if (route.public === true && route.authorization) throw new Error(`Plugin "${id}" public route "${routeName}" cannot declare private authorization.`);
1281
+ if (route.authorization && (route.authorization.anyOfTokenScopes.length === 0 || route.authorization.anyOfTokenScopes.some((scope) => scope.trim().length === 0))) throw new Error(`Plugin "${id}" route "${routeName}" requires at least one valid token scope.`);
1282
+ const restaurantPermission = route.authorization?.restaurantPermission;
1283
+ if (restaurantPermission !== void 0 && (restaurantPermission.trim().length === 0 || typeof route.resolveTarget !== "function" || typeof route.authorizeTarget !== "function")) throw new Error(`Plugin "${id}" route "${routeName}" requires a restaurant target resolver and authorizer.`);
1284
+ if (!restaurantPermission && (route.resolveTarget || route.authorizeTarget)) throw new Error(`Plugin "${id}" route "${routeName}" cannot declare target authorization without a restaurant permission.`);
1285
+ }
873
1286
  return {
874
1287
  id,
875
1288
  version,
@@ -878,9 +1291,85 @@ function defineNativePlugin(definition) {
878
1291
  storage,
879
1292
  hooks: resolveHooks(hooks, id),
880
1293
  routes,
1294
+ mcp,
881
1295
  admin
882
1296
  };
883
1297
  }
1298
+ function resolveMcp(mcp, pluginId) {
1299
+ const tools = [...mcp?.tools ?? []];
1300
+ const resources = [...mcp?.resources ?? []];
1301
+ const namespace = `${pluginId.split("/").at(-1).replaceAll("-", "_")}_`;
1302
+ const toolNames = /* @__PURE__ */ new Set();
1303
+ const resourceNames = /* @__PURE__ */ new Set();
1304
+ const resourceUris = /* @__PURE__ */ new Set();
1305
+ const authorizeTarget = mcp?.authorizeTarget;
1306
+ for (const tool of tools) {
1307
+ validateMcpName(tool.name, "tool", pluginId, namespace, MCP_NAME_PATTERN);
1308
+ if (toolNames.has(tool.name)) throw new Error(`Duplicate MCP tool "${tool.name}" in plugin "${pluginId}".`);
1309
+ toolNames.add(tool.name);
1310
+ validateMcpText(tool.title, "title", tool.name, pluginId);
1311
+ validateMcpText(tool.description, "description", tool.name, pluginId);
1312
+ validateMcpAuthorization(tool.authorization, tool.name, pluginId);
1313
+ validateZodSchema(tool.inputSchema, "inputSchema", tool.name, pluginId);
1314
+ if (tool.outputSchema) validateZodSchema(tool.outputSchema, "outputSchema", tool.name, pluginId);
1315
+ if (typeof tool.handler !== "function") throw new Error(`MCP tool "${tool.name}" in plugin "${pluginId}" requires a handler.`);
1316
+ if (tool.authorization.restaurantPermission && !tool.resolveTarget) throw new Error(`MCP tool "${tool.name}" in plugin "${pluginId}" declares a restaurant permission but no resolveTarget function.`);
1317
+ if (tool.authorization.restaurantPermission && typeof authorizeTarget !== "function") throw new Error(`MCP tool "${tool.name}" in plugin "${pluginId}" requires an MCP target authorizer.`);
1318
+ if (tool.kind === "command") {
1319
+ if (typeof tool.preview !== "function") throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" requires preview.`);
1320
+ if (typeof tool.actionFingerprint !== "function" || tool.idempotency?.required !== true) throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" requires actionFingerprint and required idempotency.`);
1321
+ if ((tool.pii.input === "sensitive" || tool.pii.output === "sensitive") && typeof tool.redactPreview !== "function") throw new Error(`Sensitive MCP command "${tool.name}" in plugin "${pluginId}" requires redactPreview.`);
1322
+ validateMcpText(tool.risk.reason, "risk reason", tool.name, pluginId);
1323
+ validateCommandRisk(tool, pluginId);
1324
+ }
1325
+ }
1326
+ for (const resource of resources) {
1327
+ validateMcpName(resource.name, "resource", pluginId, namespace, MCP_NAME_PATTERN);
1328
+ if (resourceNames.has(resource.name)) throw new Error(`Duplicate MCP resource "${resource.name}" in plugin "${pluginId}".`);
1329
+ resourceNames.add(resource.name);
1330
+ validateMcpText(resource.title, "title", resource.name, pluginId);
1331
+ validateMcpText(resource.description, "description", resource.name, pluginId);
1332
+ validateMcpAuthorization(resource.authorization, resource.name, pluginId);
1333
+ if (resource.authorization.restaurantPermission && !resource.target) throw new Error(`MCP resource "${resource.name}" in plugin "${pluginId}" declares a restaurant permission but no target.`);
1334
+ if (resource.authorization.restaurantPermission && typeof authorizeTarget !== "function") throw new Error(`MCP resource "${resource.name}" in plugin "${pluginId}" requires an MCP target authorizer.`);
1335
+ try {
1336
+ if (!new URL(resource.uri).protocol) throw new Error("Missing URI protocol");
1337
+ } catch {
1338
+ throw new Error(`Invalid MCP resource URI "${resource.uri}" in plugin "${pluginId}".`);
1339
+ }
1340
+ if (resourceUris.has(resource.uri)) throw new Error(`Duplicate MCP resource URI "${resource.uri}" in plugin "${pluginId}".`);
1341
+ resourceUris.add(resource.uri);
1342
+ if (typeof resource.handler !== "function") throw new Error(`MCP resource "${resource.name}" in plugin "${pluginId}" requires a handler.`);
1343
+ }
1344
+ return authorizeTarget ? {
1345
+ tools,
1346
+ resources,
1347
+ authorizeTarget
1348
+ } : {
1349
+ tools,
1350
+ resources
1351
+ };
1352
+ }
1353
+ function validateCommandRisk(tool, pluginId) {
1354
+ if (tool.risk.mandatory !== true) throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" must declare a mandatory risk floor.`);
1355
+ if (!Array.isArray(tool.risk.factors) || tool.risk.factors.length === 0 || tool.risk.factors.some((factor) => !MCP_RISK_FACTORS.has(factor))) throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" requires valid risk factors.`);
1356
+ if (tool.risk.factors.some((factor) => STRONG_RISK_FACTORS.has(factor)) && tool.risk.level !== "strong_confirmation") throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" requires strong confirmation for its declared risk factors.`);
1357
+ if (tool.risk.level === "strong_confirmation" && tool.audit !== "strict") throw new Error(`Strong-confirmation MCP command "${tool.name}" in plugin "${pluginId}" requires strict audit.`);
1358
+ if ((tool.pii.input === "sensitive" || tool.pii.output === "sensitive" || tool.reversibility !== "reversible") && (tool.risk.level !== "strong_confirmation" || tool.audit !== "strict")) throw new Error(`Sensitive or externally compensating MCP command "${tool.name}" in plugin "${pluginId}" requires strong confirmation and strict audit.`);
1359
+ }
1360
+ function validateMcpName(name, kind, pluginId, namespace, pattern) {
1361
+ if (!pattern.test(name) || !name.startsWith(namespace)) throw new Error(`Invalid MCP ${kind} name "${name}" in plugin "${pluginId}". Names must match ${pattern} and start with "${namespace}".`);
1362
+ }
1363
+ function validateMcpText(value, field, name, pluginId) {
1364
+ if (typeof value !== "string" || value.trim().length === 0) throw new Error(`MCP contribution "${name}" in plugin "${pluginId}" requires ${field}.`);
1365
+ }
1366
+ function validateMcpAuthorization(authorization, name, pluginId) {
1367
+ if (!authorization || !Object.hasOwn(Permissions, authorization.permission)) throw new Error(`MCP contribution "${name}" in plugin "${pluginId}" declares an invalid permission.`);
1368
+ if (!Array.isArray(authorization.anyOfTokenScopes) || authorization.anyOfTokenScopes.length === 0 || authorization.anyOfTokenScopes.some((scope) => typeof scope !== "string" || scope.trim().length === 0)) throw new Error(`MCP contribution "${name}" in plugin "${pluginId}" requires at least one token scope.`);
1369
+ }
1370
+ function validateZodSchema(schema, field, name, pluginId) {
1371
+ if (typeof schema !== "object" || schema === null || !("safeParse" in schema) || typeof schema.safeParse !== "function") throw new Error(`MCP tool "${name}" in plugin "${pluginId}" requires a Zod ${field}.`);
1372
+ }
884
1373
  /**
885
1374
  * Resolve hooks to normalized format with defaults.
886
1375
  *
@@ -1023,6 +1512,9 @@ var HookPipeline = class HookPipeline {
1023
1512
  this.registerPluginHook(plugin, "content:afterDelete");
1024
1513
  this.registerPluginHook(plugin, "content:afterPublish");
1025
1514
  this.registerPluginHook(plugin, "content:afterUnpublish");
1515
+ this.registerPluginHook(plugin, "content:afterRestore");
1516
+ this.registerPluginHook(plugin, "content:afterSchedule");
1517
+ this.registerPluginHook(plugin, "content:afterUnschedule");
1026
1518
  this.registerPluginHook(plugin, "media:beforeUpload");
1027
1519
  this.registerPluginHook(plugin, "media:afterUpload");
1028
1520
  this.registerPluginHook(plugin, "cron");
@@ -1055,6 +1547,9 @@ var HookPipeline = class HookPipeline {
1055
1547
  ["content:afterDelete", "content:read"],
1056
1548
  ["content:afterPublish", "content:read"],
1057
1549
  ["content:afterUnpublish", "content:read"],
1550
+ ["content:afterRestore", "content:read"],
1551
+ ["content:afterSchedule", "content:read"],
1552
+ ["content:afterUnschedule", "content:read"],
1058
1553
  ["media:beforeUpload", "media:write"],
1059
1554
  ["media:afterUpload", "media:read"],
1060
1555
  ["comment:beforeCreate", "users:read"],
@@ -1348,40 +1843,34 @@ var HookPipeline = class HookPipeline {
1348
1843
  * Run content:afterPublish hooks (fire-and-forget).
1349
1844
  */
1350
1845
  async runContentAfterPublish(content, collection) {
1351
- const hooks = this.getTypedHooks("content:afterPublish");
1352
- const results = [];
1353
- for (const hook of hooks) {
1354
- const { handler } = hook;
1355
- const event = {
1356
- content,
1357
- collection
1358
- };
1359
- const ctx = this.getContext(hook.pluginId);
1360
- const start = Date.now();
1361
- try {
1362
- await this.executeWithTimeout(() => handler(event, ctx), hook.timeout);
1363
- results.push({
1364
- success: true,
1365
- pluginId: hook.pluginId,
1366
- duration: Date.now() - start
1367
- });
1368
- } catch (error) {
1369
- results.push({
1370
- success: false,
1371
- error: error instanceof Error ? error : new Error(String(error)),
1372
- pluginId: hook.pluginId,
1373
- duration: Date.now() - start
1374
- });
1375
- if (hook.errorPolicy === "abort") throw error;
1376
- }
1377
- }
1378
- return results;
1846
+ return this.runContentStateChangeHook("content:afterPublish", content, collection);
1379
1847
  }
1380
1848
  /**
1381
1849
  * Run content:afterUnpublish hooks (fire-and-forget).
1382
1850
  */
1383
1851
  async runContentAfterUnpublish(content, collection) {
1384
- const hooks = this.getTypedHooks("content:afterUnpublish");
1852
+ return this.runContentStateChangeHook("content:afterUnpublish", content, collection);
1853
+ }
1854
+ /**
1855
+ * Run content:afterRestore hooks (fire-and-forget).
1856
+ */
1857
+ async runContentAfterRestore(content, collection) {
1858
+ return this.runContentStateChangeHook("content:afterRestore", content, collection);
1859
+ }
1860
+ /**
1861
+ * Run content:afterSchedule hooks (fire-and-forget).
1862
+ */
1863
+ async runContentAfterSchedule(content, collection) {
1864
+ return this.runContentStateChangeHook("content:afterSchedule", content, collection);
1865
+ }
1866
+ /**
1867
+ * Run content:afterUnschedule hooks (fire-and-forget).
1868
+ */
1869
+ async runContentAfterUnschedule(content, collection) {
1870
+ return this.runContentStateChangeHook("content:afterUnschedule", content, collection);
1871
+ }
1872
+ async runContentStateChangeHook(name, content, collection) {
1873
+ const hooks = this.getTypedHooks(name);
1385
1874
  const results = [];
1386
1875
  for (const hook of hooks) {
1387
1876
  const { handler } = hook;
@@ -2022,6 +2511,23 @@ var EmailPipeline = class {
2022
2511
  * - Error handling
2023
2512
  *
2024
2513
  */
2514
+ const CONSUMED_BODY_METHODS = new Set([
2515
+ "json",
2516
+ "text",
2517
+ "arrayBuffer",
2518
+ "blob",
2519
+ "formData",
2520
+ "bytes"
2521
+ ]);
2522
+ function guardConsumedRequestBody(request, bodyField) {
2523
+ return new Proxy(request, { get(target, prop) {
2524
+ if (typeof prop === "string" && CONSUMED_BODY_METHODS.has(prop)) return () => {
2525
+ throw new Error(`[dineway] ctx.request.${prop}() is not available inside a plugin route handler: Dineway has already consumed the request body and exposes it as ${bodyField}. Read ${bodyField} instead of ctx.request.${prop}().`);
2526
+ };
2527
+ const value = Reflect.get(target, prop, target);
2528
+ return typeof value === "function" ? value.bind(target) : value;
2529
+ } });
2530
+ }
2025
2531
  /**
2026
2532
  * Route handler for a plugin
2027
2533
  */
@@ -2047,8 +2553,20 @@ var PluginRouteHandler = class {
2047
2553
  },
2048
2554
  status: 404
2049
2555
  };
2556
+ const requestMethod = options.request.method.toUpperCase();
2557
+ const allowedMethods = route.methods;
2558
+ if (allowedMethods && !allowedMethods.includes(requestMethod)) return {
2559
+ success: false,
2560
+ error: {
2561
+ code: "METHOD_NOT_ALLOWED",
2562
+ message: `Method ${requestMethod} is not allowed for this plugin route`
2563
+ },
2564
+ status: 405
2565
+ };
2566
+ const bodyMode = route.body ?? "json";
2050
2567
  let validatedInput;
2051
- if (route.input) {
2568
+ if (bodyMode === "raw") validatedInput = void 0;
2569
+ else if (route.input) {
2052
2570
  const parseResult = route.input.safeParse(options.body);
2053
2571
  if (!parseResult.success) return {
2054
2572
  success: false,
@@ -2061,11 +2579,72 @@ var PluginRouteHandler = class {
2061
2579
  };
2062
2580
  validatedInput = parseResult.data;
2063
2581
  } else validatedInput = options.body;
2582
+ const baseContext = this.contextFactory.createContext(this.plugin);
2583
+ const actor = options.actor ?? null;
2584
+ const rawBody = bodyMode === "raw" ? options.rawBody?.slice() ?? new Uint8Array() : null;
2585
+ const request = guardConsumedRequestBody(options.request, bodyMode === "raw" ? "ctx.rawBody" : "ctx.input");
2586
+ const requestMeta = extractRequestMeta(options.request, this.trustedProxyHeaders);
2587
+ let target = null;
2588
+ if (route.authorization && !actor) return {
2589
+ success: false,
2590
+ error: {
2591
+ code: "UNAUTHORIZED",
2592
+ message: "Authentication required"
2593
+ },
2594
+ status: 401
2595
+ };
2596
+ const restaurantPermission = route.authorization?.restaurantPermission;
2597
+ if (restaurantPermission) {
2598
+ if (!actor) return {
2599
+ success: false,
2600
+ error: {
2601
+ code: "UNAUTHORIZED",
2602
+ message: "Authentication required"
2603
+ },
2604
+ status: 401
2605
+ };
2606
+ try {
2607
+ const resolvedTarget = await route.resolveTarget(validatedInput, {
2608
+ ...baseContext,
2609
+ actor,
2610
+ request,
2611
+ requestMeta
2612
+ });
2613
+ validateTarget(resolvedTarget);
2614
+ target = Object.freeze({ ...resolvedTarget });
2615
+ const authorizationContext = {
2616
+ ...baseContext,
2617
+ input: validatedInput,
2618
+ rawBody,
2619
+ actor,
2620
+ target,
2621
+ request,
2622
+ requestMeta
2623
+ };
2624
+ await route.authorizeTarget({
2625
+ restaurantPermission,
2626
+ target,
2627
+ context: authorizationContext
2628
+ });
2629
+ } catch {
2630
+ return {
2631
+ success: false,
2632
+ error: {
2633
+ code: "FORBIDDEN",
2634
+ message: "Restaurant target authorization denied for this operation"
2635
+ },
2636
+ status: 403
2637
+ };
2638
+ }
2639
+ }
2064
2640
  const routeContext = {
2065
- ...this.contextFactory.createContext(this.plugin),
2641
+ ...baseContext,
2066
2642
  input: validatedInput,
2067
- request: options.request,
2068
- requestMeta: extractRequestMeta(options.request, this.trustedProxyHeaders)
2643
+ rawBody,
2644
+ actor,
2645
+ target,
2646
+ request,
2647
+ requestMeta
2069
2648
  };
2070
2649
  try {
2071
2650
  return {
@@ -2113,9 +2692,21 @@ var PluginRouteHandler = class {
2113
2692
  getRouteMeta(name) {
2114
2693
  const route = this.plugin.routes[name];
2115
2694
  if (!route) return null;
2116
- return { public: route.public === true };
2695
+ return {
2696
+ public: route.public === true,
2697
+ body: route.body ?? "json",
2698
+ ...route.body === "raw" ? { maxBodyBytes: route.maxBodyBytes ?? DEFAULT_PLUGIN_RAW_BODY_LIMIT_BYTES } : {},
2699
+ authorization: route.authorization
2700
+ };
2117
2701
  }
2118
2702
  };
2703
+ function validateTarget(target) {
2704
+ if (![
2705
+ "plugin",
2706
+ "location",
2707
+ "entity"
2708
+ ].includes(target.type) || typeof target.id !== "string" || target.id.trim().length === 0 || typeof target.scope !== "string" || target.scope.trim().length === 0 || target.locationId !== void 0 && (typeof target.locationId !== "string" || target.locationId.trim().length === 0)) throw new Error("Invalid plugin route target");
2709
+ }
2119
2710
  /**
2120
2711
  * Error class for plugin routes
2121
2712
  * Allows plugins to return structured errors with specific HTTP status codes
@@ -2224,6 +2815,164 @@ var PluginRouteRegistry = class {
2224
2815
  }
2225
2816
  };
2226
2817
 
2818
+ //#endregion
2819
+ //#region src/plugins/secrets.ts
2820
+ const ENCRYPTION_VERSION = 1;
2821
+ const AES_GCM_NONCE_BYTES = 12;
2822
+ const MAX_SECRET_BYTES = 65536;
2823
+ /**
2824
+ * Encrypted storage for trusted plugin credentials.
2825
+ *
2826
+ * Management surfaces must use set/status/list/delete and never echo the value
2827
+ * passed to set. resolveForRuntime is reserved for trusted server-side provider
2828
+ * calls and must not be exposed through plugin routes, MCP, logs, or audit data.
2829
+ */
2830
+ var PluginSecretStore = class {
2831
+ repository;
2832
+ keysPromise;
2833
+ constructor(db, options = {}) {
2834
+ this.repository = new PluginSecretRepository(db);
2835
+ this.keysPromise = options.keys ? Promise.resolve(options.keys) : parseEncryptionKeys(readEncryptionKey(options.env));
2836
+ }
2837
+ async set(pluginId, name, value) {
2838
+ validateSecretIdentity(pluginId, name);
2839
+ validateSecretValue(value);
2840
+ const primaryKey = (await this.requireKeys())[0];
2841
+ if (!primaryKey) throw encryptionKeyRequired();
2842
+ const encrypted = await encryptSecret(primaryKey, pluginId, name, value);
2843
+ return toStatus(await this.repository.put({
2844
+ pluginId,
2845
+ name,
2846
+ encryptionVersion: ENCRYPTION_VERSION,
2847
+ keyId: primaryKey.kid,
2848
+ nonce: encrypted.nonce,
2849
+ ciphertext: encrypted.ciphertext,
2850
+ fingerprint: await fingerprintSecret(primaryKey, value)
2851
+ }));
2852
+ }
2853
+ async status(pluginId, name) {
2854
+ validateSecretIdentity(pluginId, name);
2855
+ const stored = await this.repository.find(pluginId, name);
2856
+ return stored ? toStatus(stored) : null;
2857
+ }
2858
+ async list(pluginId) {
2859
+ validatePluginIdentifier(pluginId, "plugin id");
2860
+ return (await this.repository.list(pluginId)).map(toStatus);
2861
+ }
2862
+ async delete(pluginId, name) {
2863
+ validateSecretIdentity(pluginId, name);
2864
+ return this.repository.delete(pluginId, name);
2865
+ }
2866
+ async resolveForRuntime(pluginId, name) {
2867
+ validateSecretIdentity(pluginId, name);
2868
+ const stored = await this.repository.find(pluginId, name);
2869
+ if (!stored) return null;
2870
+ if (stored.encryptionVersion !== ENCRYPTION_VERSION) throw new DinewaySecretsError("The stored plugin secret uses an unsupported encryption version.", "PLUGIN_SECRET_VERSION_UNSUPPORTED");
2871
+ const keys = await this.requireKeys();
2872
+ const decryptionKey = keys.find((key) => key.kid === stored.keyId);
2873
+ if (!decryptionKey) throw new DinewaySecretsError("The stored plugin secret cannot be decrypted by the configured key ring.", "PLUGIN_SECRET_KEY_UNAVAILABLE");
2874
+ let value;
2875
+ try {
2876
+ value = await decryptSecret(decryptionKey, stored);
2877
+ } catch {
2878
+ throw new DinewaySecretsError("The stored plugin secret failed authenticated decryption.", "PLUGIN_SECRET_DECRYPT_FAILED");
2879
+ }
2880
+ const primaryKey = keys[0];
2881
+ if (primaryKey && primaryKey.kid !== stored.keyId) {
2882
+ const encrypted = await encryptSecret(primaryKey, pluginId, name, value);
2883
+ await this.repository.put({
2884
+ pluginId,
2885
+ name,
2886
+ encryptionVersion: ENCRYPTION_VERSION,
2887
+ keyId: primaryKey.kid,
2888
+ nonce: encrypted.nonce,
2889
+ ciphertext: encrypted.ciphertext,
2890
+ fingerprint: await fingerprintSecret(primaryKey, value)
2891
+ });
2892
+ }
2893
+ return value;
2894
+ }
2895
+ async requireKeys() {
2896
+ const keys = await this.keysPromise;
2897
+ if (!keys || keys.length === 0) throw encryptionKeyRequired();
2898
+ return keys;
2899
+ }
2900
+ };
2901
+ async function encryptSecret(key, pluginId, name, value) {
2902
+ const nonce = crypto.getRandomValues(new Uint8Array(AES_GCM_NONCE_BYTES));
2903
+ const cryptoKey = await importAesKey(key, ["encrypt"]);
2904
+ const ciphertext = await crypto.subtle.encrypt({
2905
+ name: "AES-GCM",
2906
+ iv: toArrayBuffer$1(nonce),
2907
+ additionalData: aad(pluginId, name, ENCRYPTION_VERSION)
2908
+ }, cryptoKey, new TextEncoder().encode(value));
2909
+ return {
2910
+ nonce: encodeBase64url(nonce),
2911
+ ciphertext: encodeBase64url(new Uint8Array(ciphertext))
2912
+ };
2913
+ }
2914
+ async function decryptSecret(key, stored) {
2915
+ const cryptoKey = await importAesKey(key, ["decrypt"]);
2916
+ const plaintext = await crypto.subtle.decrypt({
2917
+ name: "AES-GCM",
2918
+ iv: toArrayBuffer$1(decodeBase64url(stored.nonce)),
2919
+ additionalData: aad(stored.pluginId, stored.name, stored.encryptionVersion)
2920
+ }, cryptoKey, toArrayBuffer$1(decodeBase64url(stored.ciphertext)));
2921
+ return new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
2922
+ }
2923
+ async function importAesKey(key, usages) {
2924
+ return crypto.subtle.importKey("raw", toArrayBuffer$1(key.key), { name: "AES-GCM" }, false, usages);
2925
+ }
2926
+ function aad(pluginId, name, encryptionVersion) {
2927
+ return toArrayBuffer$1(new TextEncoder().encode(JSON.stringify([
2928
+ "dineway-plugin-secret",
2929
+ encryptionVersion,
2930
+ pluginId,
2931
+ name
2932
+ ])));
2933
+ }
2934
+ async function fingerprintSecret(key, value) {
2935
+ const hmacKey = await crypto.subtle.importKey("raw", toArrayBuffer$1(key.key), {
2936
+ name: "HMAC",
2937
+ hash: "SHA-256"
2938
+ }, false, ["sign"]);
2939
+ const digest = await crypto.subtle.sign("HMAC", hmacKey, new TextEncoder().encode(`dineway-plugin-secret-fingerprint:v1:${value}`));
2940
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 12);
2941
+ }
2942
+ function toArrayBuffer$1(bytes) {
2943
+ const buffer = new ArrayBuffer(bytes.byteLength);
2944
+ new Uint8Array(buffer).set(bytes);
2945
+ return buffer;
2946
+ }
2947
+ function validateSecretIdentity(pluginId, name) {
2948
+ validatePluginIdentifier(pluginId, "plugin id");
2949
+ validateIdentifier(name, "plugin secret name");
2950
+ }
2951
+ function validateSecretValue(value) {
2952
+ const byteLength = new TextEncoder().encode(value).byteLength;
2953
+ if (byteLength === 0 || byteLength > MAX_SECRET_BYTES) throw new DinewaySecretsError(`Plugin secret values must contain between 1 and ${MAX_SECRET_BYTES} UTF-8 bytes.`, "INVALID_PLUGIN_SECRET_VALUE");
2954
+ }
2955
+ function encryptionKeyRequired() {
2956
+ return new DinewaySecretsError("DINEWAY_ENCRYPTION_KEY is required before plugin secrets can be written or decrypted.", "ENCRYPTION_KEY_REQUIRED");
2957
+ }
2958
+ function readEncryptionKey(env) {
2959
+ if (env) return env.DINEWAY_ENCRYPTION_KEY;
2960
+ const meta = import.meta.env ?? {};
2961
+ const proc = typeof process !== "undefined" && process.env ? process.env : {};
2962
+ return meta.DINEWAY_ENCRYPTION_KEY ?? proc.DINEWAY_ENCRYPTION_KEY;
2963
+ }
2964
+ function toStatus(stored) {
2965
+ return {
2966
+ pluginId: stored.pluginId,
2967
+ name: stored.name,
2968
+ present: true,
2969
+ fingerprint: stored.fingerprint,
2970
+ keyId: stored.keyId,
2971
+ createdAt: stored.createdAt,
2972
+ updatedAt: stored.updatedAt
2973
+ };
2974
+ }
2975
+
2227
2976
  //#endregion
2228
2977
  //#region src/plugins/manager.ts
2229
2978
  /** Options table key prefix for exclusive hook DB reads via PluginManager */
@@ -2393,6 +3142,27 @@ var PluginManager = class {
2393
3142
  return this.hookPipeline.runContentAfterUnpublish(content, collection);
2394
3143
  }
2395
3144
  /**
3145
+ * Run content:afterRestore hooks across all active plugins
3146
+ */
3147
+ async runContentAfterRestore(content, collection) {
3148
+ this.ensureInitialized();
3149
+ return this.hookPipeline.runContentAfterRestore(content, collection);
3150
+ }
3151
+ /**
3152
+ * Run content:afterSchedule hooks across all active plugins
3153
+ */
3154
+ async runContentAfterSchedule(content, collection) {
3155
+ this.ensureInitialized();
3156
+ return this.hookPipeline.runContentAfterSchedule(content, collection);
3157
+ }
3158
+ /**
3159
+ * Run content:afterUnschedule hooks across all active plugins
3160
+ */
3161
+ async runContentAfterUnschedule(content, collection) {
3162
+ this.ensureInitialized();
3163
+ return this.hookPipeline.runContentAfterUnschedule(content, collection);
3164
+ }
3165
+ /**
2396
3166
  * Run media:beforeUpload hooks across all active plugins
2397
3167
  */
2398
3168
  async runMediaBeforeUpload(file) {
@@ -2859,7 +3629,13 @@ export async function invokeRoute(routeName, input, serializedRequest) {
2859
3629
  throw new Error(\`Route \${routeName} handler is not a function\`);
2860
3630
  }
2861
3631
  return handler(
2862
- { input, request: serializedRequest, requestMeta: serializedRequest.meta },
3632
+ {
3633
+ input,
3634
+ actor: serializedRequest.actor ?? null,
3635
+ target: null,
3636
+ request: serializedRequest,
3637
+ requestMeta: serializedRequest.meta,
3638
+ },
2863
3639
  ctx,
2864
3640
  );
2865
3641
  }
@@ -3041,6 +3817,10 @@ function createResolvedPluginFromManifest(manifest) {
3041
3817
  storage: manifest.storage,
3042
3818
  hooks: {},
3043
3819
  routes: {},
3820
+ mcp: {
3821
+ tools: [],
3822
+ resources: []
3823
+ },
3044
3824
  admin: manifest.admin
3045
3825
  };
3046
3826
  }
@@ -3455,16 +4235,4 @@ function createNodeSandboxRunner(options) {
3455
4235
  }
3456
4236
 
3457
4237
  //#endregion
3458
- //#region src/plugins/types.ts
3459
- /**
3460
- * Check if a value is a StandardPluginDefinition (has hooks/routes but no id/version).
3461
- */
3462
- function isStandardPluginDefinition(value) {
3463
- if (typeof value !== "object" || value === null) return false;
3464
- const hasPluginShape = "hooks" in value || "routes" in value;
3465
- const hasNativeShape = "id" in value && "version" in value;
3466
- return hasPluginShape && !hasNativeShape;
3467
- }
3468
-
3469
- //#endregion
3470
- export { RelationRepository as S, portableTextToProsemirror as _, SandboxNotAvailableError as a, reference as b, createPluginManager as c, EmailPipeline as d, HookPipeline as f, after as g, definePlugin as h, NoopSandboxRunner as i, PluginRouteError as l, resolveExclusiveHooks as m, NodeSandboxRunner as n, createNoopSandboxRunner as o, createHookPipeline as p, createNodeSandboxRunner as r, PluginManager as s, isStandardPluginDefinition as t, PluginRouteRegistry as u, prosemirrorToPortableText as v, image as x, portableText as y };
4238
+ export { prosemirrorToPortableText as C, RelationRepository as D, image as E, portableTextToProsemirror as S, reference as T, readBoundedPluginRouteBody as _, createNoopSandboxRunner as a, issueGuestAccessToken as b, PluginSecretStore as c, EmailPipeline as d, HookPipeline as f, DEFAULT_PLUGIN_RAW_BODY_LIMIT_BYTES as g, definePlugin as h, SandboxNotAvailableError as i, PluginRouteError as l, resolveExclusiveHooks as m, createNodeSandboxRunner as n, PluginManager as o, createHookPipeline as p, NoopSandboxRunner as r, createPluginManager as s, NodeSandboxRunner as t, PluginRouteRegistry as u, GuestAccessTokenConfigurationError as v, portableText as w, verifyGuestAccessToken as x, hashGuestAccessToken as y };