dineway 0.1.49 → 0.2.1

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 (434) hide show
  1. package/dist/activity-DUaz_w7p.mjs +79 -0
  2. package/dist/{activity-events-CwM7yxPW.mjs → activity-events-CJDvE2uc.mjs} +5 -113
  3. package/dist/actor-0QJ1Gbtf.mjs +37 -0
  4. package/dist/after-BfniwgIf.mjs +9 -0
  5. package/dist/{allowed-origins-CyGnbgBd.mjs → allowed-origins--OwHHiY8.mjs} +1 -1
  6. package/dist/api/route-utils.d.mts +9 -10
  7. package/dist/api/route-utils.mjs +16 -13
  8. package/dist/api/schemas/index.d.mts +2 -2
  9. package/dist/api/schemas/index.mjs +5 -4
  10. package/dist/{api-BMIxRIEN.mjs → api-B4PHIF4V.mjs} +93 -41
  11. package/dist/{api-tokens-DtvdtIkm.mjs → api-tokens-tbK_sORH.mjs} +6 -2
  12. package/dist/{apply-D_RlTYtC.mjs → apply-DjTqq6Ae.mjs} +117 -77
  13. package/dist/astro/image-endpoint.d.mts +7 -0
  14. package/dist/astro/image-endpoint.mjs +54 -0
  15. package/dist/astro/index.d.mts +34 -11
  16. package/dist/astro/index.mjs +163 -24
  17. package/dist/astro/integration/shims/use-sync-external-store-with-selector.js +71 -0
  18. package/dist/astro/integration/shims/use-sync-external-store.js +1 -0
  19. package/dist/astro/middleware/auth.d.mts +8 -18
  20. package/dist/astro/middleware/auth.mjs +42 -24
  21. package/dist/astro/middleware/redirect.mjs +13 -4
  22. package/dist/astro/middleware/request-context.mjs +8 -5
  23. package/dist/astro/middleware/seed.d.mts +1 -1
  24. package/dist/astro/middleware/seed.mjs +2 -2
  25. package/dist/astro/middleware/setup.mjs +1 -1
  26. package/dist/astro/middleware.mjs +389 -206
  27. package/dist/astro/routes/admin.astro +6 -1
  28. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -6
  29. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -6
  30. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  31. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  32. package/dist/astro/routes/api/admin/briefing.mjs +21 -17
  33. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  34. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -8
  35. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -8
  36. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -8
  37. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -14
  38. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -14
  39. package/dist/astro/routes/api/admin/bylines/index.mjs +17 -14
  40. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +14 -12
  41. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -5
  42. package/dist/astro/routes/api/admin/comments/bulk.mjs +11 -9
  43. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -5
  44. package/dist/astro/routes/api/admin/comments/index.mjs +11 -9
  45. package/dist/astro/routes/api/admin/context/_id_/history.mjs +27 -24
  46. package/dist/astro/routes/api/admin/context/_id_/index.mjs +27 -24
  47. package/dist/astro/routes/api/admin/context/_id_/review.mjs +32 -28
  48. package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +32 -28
  49. package/dist/astro/routes/api/admin/context/diff.mjs +31 -27
  50. package/dist/astro/routes/api/admin/context/index.mjs +32 -28
  51. package/dist/astro/routes/api/admin/context/stale.mjs +31 -27
  52. package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +27 -24
  53. package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +31 -27
  54. package/dist/astro/routes/api/admin/hitl-requests/index.mjs +31 -27
  55. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +28 -25
  56. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  57. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
  58. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
  59. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -51
  60. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -51
  61. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +47 -43
  62. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -49
  63. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +53 -48
  64. package/dist/astro/routes/api/admin/plugins/index.mjs +47 -43
  65. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  66. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +46 -42
  67. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +53 -48
  68. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +46 -42
  69. package/dist/astro/routes/api/admin/plugins/updates.mjs +46 -42
  70. package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +5 -5
  71. package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +32 -63
  72. package/dist/astro/routes/api/admin/review-requests/index.d.mts +2 -1
  73. package/dist/astro/routes/api/admin/review-requests/index.mjs +58 -10
  74. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +46 -42
  75. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  76. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +46 -42
  77. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
  78. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  79. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -7
  80. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
  81. package/dist/astro/routes/api/admin/users/index.mjs +7 -6
  82. package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
  83. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  84. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -11
  85. package/dist/astro/routes/api/auth/invite/index.mjs +9 -8
  86. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -10
  87. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  88. package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -10
  89. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  90. package/dist/astro/routes/api/auth/me.mjs +9 -8
  91. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  92. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  93. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  94. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -5
  95. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  96. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -11
  97. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -9
  98. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -10
  99. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -10
  100. package/dist/astro/routes/api/auth/setup-token/index.mjs +5 -5
  101. package/dist/astro/routes/api/auth/setup-token/verify.mjs +2 -2
  102. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -11
  103. package/dist/astro/routes/api/auth/signup/request.mjs +11 -10
  104. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  105. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +15 -13
  106. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +8 -0
  107. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +160 -0
  108. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  109. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -5
  110. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -5
  111. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +6 -5
  112. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -10
  113. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +49 -20
  114. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +6 -5
  115. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  116. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +67 -32
  117. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +16 -14
  118. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  119. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -5
  120. package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -9
  121. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  122. package/dist/astro/routes/api/content/_collection_/index.mjs +11 -9
  123. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -6
  124. package/dist/astro/routes/api/dashboard.mjs +9 -8
  125. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  126. package/dist/astro/routes/api/health.mjs +1 -1
  127. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  128. package/dist/astro/routes/api/import/probe.mjs +22 -10
  129. package/dist/astro/routes/api/import/wordpress/analyze.mjs +9 -6
  130. package/dist/astro/routes/api/import/wordpress/execute.d.mts +18 -1
  131. package/dist/astro/routes/api/import/wordpress/execute.mjs +85 -37
  132. package/dist/astro/routes/api/import/wordpress/media.d.mts +12 -1
  133. package/dist/astro/routes/api/import/wordpress/media.mjs +20 -14
  134. package/dist/astro/routes/api/import/wordpress/prepare.mjs +36 -32
  135. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +17 -1
  136. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +121 -100
  137. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  138. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +24 -12
  139. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +55 -2
  140. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +284 -55
  141. package/dist/astro/routes/api/manifest.mjs +14 -5
  142. package/dist/astro/routes/api/mcp.mjs +753 -170
  143. package/dist/astro/routes/api/media/_id_/confirm.mjs +38 -10
  144. package/dist/astro/routes/api/media/_id_.mjs +11 -7
  145. package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
  146. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  147. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  148. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  149. package/dist/astro/routes/api/media/upload-url.mjs +11 -10
  150. package/dist/astro/routes/api/media.mjs +28 -35
  151. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +32 -28
  152. package/dist/astro/routes/api/menus/_name_/items.mjs +32 -28
  153. package/dist/astro/routes/api/menus/_name_/reorder.mjs +32 -28
  154. package/dist/astro/routes/api/menus/_name_/translations.mjs +32 -28
  155. package/dist/astro/routes/api/menus/_name_.mjs +32 -28
  156. package/dist/astro/routes/api/menus/index.mjs +32 -28
  157. package/dist/astro/routes/api/oauth/authorize.mjs +7 -7
  158. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  159. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  160. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  161. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  162. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  163. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  164. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  165. package/dist/astro/routes/api/openapi.json.mjs +4 -3
  166. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +46 -31
  167. package/dist/astro/routes/api/redirects/404s/index.mjs +11 -10
  168. package/dist/astro/routes/api/redirects/404s/summary.mjs +11 -10
  169. package/dist/astro/routes/api/redirects/_id_.mjs +33 -29
  170. package/dist/astro/routes/api/redirects/index.mjs +33 -29
  171. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  172. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
  173. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +48 -43
  174. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +48 -43
  175. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +48 -43
  176. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +48 -43
  177. package/dist/astro/routes/api/schema/collections/index.mjs +48 -43
  178. package/dist/astro/routes/api/schema/index.mjs +14 -15
  179. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +46 -42
  180. package/dist/astro/routes/api/schema/orphans/index.mjs +46 -42
  181. package/dist/astro/routes/api/search/enable.mjs +11 -10
  182. package/dist/astro/routes/api/search/index.mjs +10 -9
  183. package/dist/astro/routes/api/search/rebuild.mjs +11 -10
  184. package/dist/astro/routes/api/search/stats.mjs +6 -6
  185. package/dist/astro/routes/api/search/suggest.mjs +10 -9
  186. package/dist/astro/routes/api/sections/_slug_.mjs +32 -28
  187. package/dist/astro/routes/api/sections/index.mjs +32 -28
  188. package/dist/astro/routes/api/settings/email.mjs +4 -4
  189. package/dist/astro/routes/api/settings.mjs +32 -28
  190. package/dist/astro/routes/api/setup/admin-verify.mjs +14 -13
  191. package/dist/astro/routes/api/setup/admin.mjs +12 -11
  192. package/dist/astro/routes/api/setup/dev-bypass.mjs +31 -25
  193. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  194. package/dist/astro/routes/api/setup/index.mjs +29 -25
  195. package/dist/astro/routes/api/setup/status.mjs +3 -3
  196. package/dist/astro/routes/api/snapshot.mjs +8 -7
  197. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +35 -31
  198. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +35 -31
  199. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +35 -31
  200. package/dist/astro/routes/api/taxonomies/index.mjs +35 -31
  201. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  202. package/dist/astro/routes/api/typegen.mjs +4 -4
  203. package/dist/astro/routes/api/well-known/auth.mjs +3 -3
  204. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +3 -3
  205. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +3 -3
  206. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +31 -27
  207. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +32 -28
  208. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +32 -28
  209. package/dist/astro/routes/api/widget-areas/_name_.mjs +29 -26
  210. package/dist/astro/routes/api/widget-areas/index.mjs +32 -28
  211. package/dist/astro/routes/api/widget-components.mjs +2 -2
  212. package/dist/astro/routes/robots.txt.mjs +8 -7
  213. package/dist/astro/routes/sitemap-_collection_.xml.mjs +23 -14
  214. package/dist/astro/routes/sitemap.xml.mjs +9 -8
  215. package/dist/astro/session-driver.d.mts +1 -1
  216. package/dist/astro/session-driver.mjs +2 -2
  217. package/dist/astro/types.d.mts +22 -13
  218. package/dist/{audit-BeNsSyfU.mjs → audit-B2pRxZ20.mjs} +1 -1
  219. package/dist/auth/providers/github.d.mts +1 -1
  220. package/dist/auth/providers/google.d.mts +1 -1
  221. package/dist/{auth-control-guard-Dr14I98m.mjs → auth-control-guard-D71jourk.mjs} +1 -1
  222. package/dist/{authorize-BLO0m7fW.mjs → authorize-ZXPGPwKn.mjs} +1 -1
  223. package/dist/{briefing-CkTee1ex.mjs → briefing-0rf-su3-.mjs} +90 -28
  224. package/dist/{briefing-DJX6irto.mjs → briefing-kRhegBoF.mjs} +2 -1
  225. package/dist/bundle-CEcdUa1s.mjs +602 -0
  226. package/dist/{byline-BVC1lV6G.mjs → byline-7xR185Ly.mjs} +30 -62
  227. package/dist/{byline-fields-Cj39WhXC.mjs → byline-fields-BKzA0fJU.mjs} +1 -1
  228. package/dist/{byline-fields-uJlygdzM.d.mts → byline-fields-Ckeqd1OY.d.mts} +88 -35
  229. package/dist/{byline-fields-cNrLJVRs.mjs → byline-fields-WN7RQT4q.mjs} +10 -4
  230. package/dist/{byline-registry-4v6tXMNX.mjs → byline-registry-Ba54M74c.mjs} +2 -2
  231. package/dist/{bylines-LN9XISRo.mjs → bylines-C-EL6dh3.mjs} +9 -3
  232. package/dist/{bylines-DwokOFj6.mjs → bylines-CmZFCMr0.mjs} +5 -5
  233. package/dist/{cache-Bac05gBd.mjs → cache-DkVCKGio.mjs} +2 -2
  234. package/dist/{chunks-CuUt7K50.mjs → chunks-ChGNDY8r.mjs} +1 -1
  235. package/dist/cli/index.d.mts +29 -1
  236. package/dist/cli/index.mjs +1466 -1763
  237. package/dist/cli/plugin-kit.d.mts +75 -0
  238. package/dist/cli/plugin-kit.mjs +4 -0
  239. package/dist/client/external-auth-headers.d.mts +1 -1
  240. package/dist/client/index.d.mts +27 -52
  241. package/dist/client/index.mjs +34 -6
  242. package/dist/client-factory-BDD9azPJ.mjs +233 -0
  243. package/dist/{comment-Beue8-15.mjs → comment-DFALgsJD.mjs} +13 -3
  244. package/dist/comment-reaction-C37bf99u.mjs +58 -0
  245. package/dist/{comments-DcjFIfqX.mjs → comments-B3NwDBxl.mjs} +1 -1
  246. package/dist/{content-CBxYGWfL.mjs → content-DJcAYFhz.mjs} +51 -34
  247. package/dist/content-refresh-B4ZCmxgd.mjs +1576 -0
  248. package/dist/{context-Df1xVYyr.mjs → context-DnpXcp6Y.mjs} +2 -2
  249. package/dist/{context-CQVNZaR8.mjs → context-fSCYLL3c.mjs} +125 -53
  250. package/dist/{context-route-helpers-B9wj8Rmh.mjs → context-route-helpers-CI8XzcsK.mjs} +2 -1
  251. package/dist/{cron-DxCAU_Sn.mjs → cron-B1SL_lsb.mjs} +7 -3
  252. package/dist/{dashboard-B7ZUNuyF.mjs → dashboard-D0nj5oWg.mjs} +4 -4
  253. package/dist/db/index.d.mts +3 -3
  254. package/dist/db/index.mjs +1 -1
  255. package/dist/db/libsql.d.mts +1 -1
  256. package/dist/db/postgres.d.mts +1 -1
  257. package/dist/db/sqlite.d.mts +1 -1
  258. package/dist/{device-flow-BZVGftqi.mjs → device-flow-BJe6VEhP.mjs} +5 -5
  259. package/dist/{placeholder-d54Qr_7n.mjs → enrich-Ck9tfMqe.mjs} +36 -9
  260. package/dist/{error-DCSuUAZW.mjs → error-CSVaQNZy.mjs} +5 -0
  261. package/dist/{experimental-workflows-CSMffP6q.mjs → experimental-workflows-GVhUgzEs.mjs} +1 -1
  262. package/dist/field-defs-cache-DL-T2XVS.mjs +49 -0
  263. package/dist/forgeway-seo-client-G5ddhVHd.mjs +456 -0
  264. package/dist/{fts-manager-B5l_qwrA.mjs → fts-manager-ewTGohf4.mjs} +1 -1
  265. package/dist/{hitl-requests-7NlzVeUA.mjs → hitl-requests-DEFj8e6u.mjs} +2 -2
  266. package/dist/{hitl-route-helpers-k---_H3-.mjs → hitl-route-helpers-C23JHaWz.mjs} +5 -4
  267. package/dist/import-CXXL5pip.mjs +1354 -0
  268. package/dist/index-DcnOCD1e.d.mts +375 -0
  269. package/dist/index.d.mts +15 -16
  270. package/dist/index.mjs +85 -70
  271. package/dist/{jsonld-DZ67souc.d.mts → jsonld-DzKbhRZ4.d.mts} +8 -4
  272. package/dist/{load-D9OiGr2m.mjs → load-CFtJRT3c.mjs} +1 -1
  273. package/dist/{loader--GOYux8M.mjs → loader-aWkkc1_o.mjs} +117 -31
  274. package/dist/{manifest-schema-qh2P7RDX.mjs → manifest-schema-uA6pRFXi.mjs} +38 -1
  275. package/dist/mcp-reviewed-executor-Dn52L_yY.mjs +558 -0
  276. package/dist/media/image-endpoint.d.mts +32 -0
  277. package/dist/media/image-endpoint.mjs +111 -0
  278. package/dist/media/index.d.mts +27 -2
  279. package/dist/media/index.mjs +5 -3
  280. package/dist/media/local-runtime.d.mts +15 -11
  281. package/dist/media/local-runtime.mjs +9 -7
  282. package/dist/{media-CbKyYukG.mjs → media-CVMOoywq.mjs} +3 -1
  283. package/dist/{media-allowlist-BYxJIcTO.mjs → media-allowlist-zDNJmXCo.mjs} +1 -1
  284. package/dist/media-url-BdoLPvg8.mjs +20 -0
  285. package/dist/{menus-C7F0sCrC.mjs → menus-CCG2z5um.mjs} +80 -15
  286. package/dist/{menus-BZbXf-ej.mjs → menus-CmyIkRG5.mjs} +19 -5
  287. package/dist/{normalize-ByemhhfW.mjs → normalize-Cyt7JdF7.mjs} +8 -1
  288. package/dist/{oauth-authorization-DF-LCsIA.mjs → oauth-authorization-B9H7X85L.mjs} +5 -5
  289. package/dist/{oauth-clients-Du-V0fEm.mjs → oauth-clients-Dwl32-Uj.mjs} +2 -2
  290. package/dist/object-cache/memory.d.mts +14 -0
  291. package/dist/object-cache/memory.mjs +55 -0
  292. package/dist/object-cache-D51qOseS.mjs +419 -0
  293. package/dist/output-DhBe5_Pi.mjs +68 -0
  294. package/dist/page/index.d.mts +10 -3
  295. package/dist/page/index.mjs +2 -2
  296. package/dist/{parse-CneSgl7-.mjs → parse-i15EFSAE.mjs} +1 -1
  297. package/dist/{parser-DxfsARXL.mjs → parser-06LLLL4E.mjs} +151 -9
  298. package/dist/{placeholder-CXfoCtCd.d.mts → placeholder-DkWkXfDI.d.mts} +12 -4
  299. package/dist/plugin-utils.d.mts +8 -9
  300. package/dist/plugins/adapt-sandbox-entry.d.mts +8 -9
  301. package/dist/plugins/adapt-sandbox-entry.mjs +13 -2
  302. package/dist/{plugins-DzfqRREq.mjs → plugins-CPA2PtfY.mjs} +919 -66
  303. package/dist/{transport-LSdZVXHF.mjs → portable-text-BASHceim.mjs} +1 -134
  304. package/dist/portable-text-E79LyjGo.d.mts +45 -0
  305. package/dist/{preview-C-3YTX2U.mjs → preview-BjedZm8G.mjs} +4 -3
  306. package/dist/process-cache-CCDgti7j.mjs +106 -0
  307. package/dist/project-env-DKibmuP8.mjs +95 -0
  308. package/dist/{provider-loader-9it4vd6L.d.mts → provider-loader-DC2JdWeO.d.mts} +1 -1
  309. package/dist/{public-url-B4xV4rGa.d.mts → public-url-DQJCnYi9.d.mts} +1 -1
  310. package/dist/publication-authorization-RKiHXKtq.mjs +985 -0
  311. package/dist/{query-CZ3TH_Uu.mjs → query-C2U5u5_p.mjs} +11 -6
  312. package/dist/{query-Ds0EnMFX.mjs → query-DEv5x6yB.mjs} +281 -76
  313. package/dist/{rate-limit-i0G3IAOX.mjs → rate-limit-DglsAJSw.mjs} +2 -2
  314. package/dist/{redirect-CEACczxo.mjs → redirect-ubhEqQCQ.mjs} +2 -2
  315. package/dist/{redirects-ocRJmQHP.mjs → redirects-Bhpm8bRe.mjs} +35 -19
  316. package/dist/{redirects-DYj1yPxs.mjs → redirects-xs2yahLq.mjs} +47 -24
  317. package/dist/{registry-D7HACema.mjs → registry-BYYyb2tF.mjs} +148 -81
  318. package/dist/request-context.d.mts +2 -0
  319. package/dist/{request-meta-DAb6xaP9.mjs → request-meta-CFe1tW2M.mjs} +1 -1
  320. package/dist/review-payload-BysRWpxS.mjs +111 -0
  321. package/dist/{review-requests-BW2NxY-l.mjs → review-requests-CTWGnWUt.mjs} +1 -1
  322. package/dist/review-requests-D42VEP1l.mjs +142 -0
  323. package/dist/review-route-helpers-C31vO6SI.mjs +41 -0
  324. package/dist/runner-BgmttQmD.mjs +7444 -0
  325. package/dist/{runner-09sTqtv0.d.mts → runner-D6hzs8L9.d.mts} +1 -1
  326. package/dist/runtime.d.mts +9 -10
  327. package/dist/runtime.mjs +3 -3
  328. package/dist/schema-DUtnZxjZ.mjs +8 -0
  329. package/dist/{search-Gdrqj582.mjs → search-DUKgZUlm.mjs} +5 -3
  330. package/dist/{secrets-CeGpBFV2.mjs → secrets-ZV5rd8yV.mjs} +14 -11
  331. package/dist/{sections-BOjU13Y-.mjs → sections-DvFgsnda.mjs} +2 -2
  332. package/dist/seed/index.d.mts +2 -2
  333. package/dist/seed/index.mjs +21 -18
  334. package/dist/seo/index.d.mts +1 -1
  335. package/dist/seo/index.mjs +3 -12
  336. package/dist/{seo-5lJOtB5J.mjs → seo-BLszyJxd.mjs} +22 -4
  337. package/dist/{seo-CLj8lS7Q.mjs → seo-BU8MLCS9.mjs} +5 -2
  338. package/dist/{seo-contributions-CqVvReEj.mjs → seo-contributions-GnCuL1ML.mjs} +11 -3
  339. package/dist/{service-jGlhrICs.mjs → service-FL3fB0Rx.mjs} +2 -2
  340. package/dist/session-user-Ct6GBLvn.mjs +22 -0
  341. package/dist/{settings-CUXOxzsT.mjs → settings-BECcAIRH.mjs} +28 -55
  342. package/dist/{settings-DlBsT2VS.mjs → settings-EzADu-dd.mjs} +2 -2
  343. package/dist/{setup-complete-BRrdPYjZ.mjs → setup-complete-Da0axZEE.mjs} +1 -1
  344. package/dist/{site-activity-DONFrMBJ.mjs → site-activity-h8smOlYI.mjs} +1 -1
  345. package/dist/{site-context-C8Az_P_I.mjs → site-context-B8T1kpUP.mjs} +337 -530
  346. package/dist/{site-url-DIMD68go.mjs → site-url-DJEid0ml.mjs} +1 -1
  347. package/dist/stable-hash-DshcXvRE.mjs +19 -0
  348. package/dist/status-NqWOSFAD.mjs +21 -0
  349. package/dist/storage/local.d.mts +1 -1
  350. package/dist/storage/local.mjs +1 -1
  351. package/dist/storage/s3.d.mts +1 -1
  352. package/dist/storage/s3.mjs +1 -1
  353. package/dist/{taxonomies-D9Sw4wWM.mjs → taxonomies-BiM9NtgW.mjs} +36 -29
  354. package/dist/taxonomies-DSnY5nOa.mjs +443 -0
  355. package/dist/{taxonomy-B169Nm6B.mjs → taxonomy-0k_vRVsD.mjs} +44 -9
  356. package/dist/transport-C46KHLMP.mjs +134 -0
  357. package/dist/types-A8fPOJnl.mjs +69 -0
  358. package/dist/types-BLQCz46p.d.mts +118 -0
  359. package/dist/{types-CwawBUTa.d.mts → types-B_FlxIIl.d.mts} +4 -1
  360. package/dist/{types-CSgR9nQB.mjs → types-ChxYDRvx.mjs} +1 -1
  361. package/dist/{types-GaCtZ1k9.d.mts → types-Cw0PeCT_.d.mts} +18 -0
  362. package/dist/types-DuQn1G7-.d.mts +2973 -0
  363. package/dist/{runtime-C4-2lSFg.d.mts → types-DwoUifGE.d.mts} +5668 -3000
  364. package/dist/{types-DRIKLmJI.d.mts → types-opjX_kz6.d.mts} +17 -2
  365. package/dist/ui/server-runtime.d.mts +13 -15
  366. package/dist/ui/server-runtime.mjs +24 -20
  367. package/dist/url-guoXQQcV.mjs +33 -0
  368. package/dist/{user-DTLoZs16.mjs → user-BH-n01y3.mjs} +2 -2
  369. package/dist/{utils-B8hBJ9Oz.mjs → utils-D1SrKGtP.mjs} +129 -7
  370. package/dist/{validate-DzjCdy6v.d.mts → validate-CbVFoJgc.d.mts} +65 -8
  371. package/dist/{validate-DWmPO6oj.mjs → validate-D1HL-oLR.mjs} +11 -6
  372. package/dist/version-C3iVRuaF.mjs +6 -0
  373. package/dist/{widgets-BpJ8EpPd.mjs → widgets-BZrLrNgS.mjs} +46 -43
  374. package/dist/{wordpress-slugs-DHNR9LpG.mjs → wordpress-slugs-C3PWcowq.mjs} +1 -1
  375. package/dist/{zod-generator-K91-g665.mjs → zod-generator-C7AsLpSB.mjs} +42 -14
  376. package/package.json +38 -8
  377. package/src/components/Block.astro +50 -0
  378. package/src/components/Comments.astro +143 -1
  379. package/src/components/DinewayHead.astro +19 -2
  380. package/src/components/DinewayImage.astro +66 -33
  381. package/src/components/DinewayMedia.astro +1 -2
  382. package/src/components/Image.astro +85 -40
  383. package/src/components/InlinePortableTextEditor.tsx +12 -2
  384. package/src/components/JsonLdScript.astro +9 -0
  385. package/src/components/LiveSearch.astro +40 -4
  386. package/src/components/index.ts +4 -0
  387. package/src/components/live-search-routing.ts +30 -0
  388. package/src/components/portable-text-text-align.ts +15 -0
  389. package/src/ui.ts +2 -0
  390. package/dist/entity-aliases-4y93Yjuc.mjs +0 -51
  391. package/dist/import-4YBXbDkI.mjs +0 -684
  392. package/dist/index-CZiWCQDp.d.mts +0 -227
  393. package/dist/review-requests-Qum4713s.mjs +0 -79
  394. package/dist/runner-DzIpHGND.mjs +0 -2836
  395. package/dist/schema-Bsi-VpYv.mjs +0 -8
  396. package/dist/taxonomies-CZ-_DitR.mjs +0 -327
  397. package/dist/types-CuJOqHfM.d.mts +0 -758
  398. package/dist/types-DSPGWjxO.d.mts +0 -1247
  399. package/dist/types-nP9vGUFq.d.mts +0 -56
  400. package/dist/version-C4gfn0PJ.mjs +0 -6
  401. /package/dist/{adapters-OQCImOL7.d.mts → adapters-BiRymx02.d.mts} +0 -0
  402. /package/dist/{api-tokens-CYUoFGo7.mjs → api-tokens-Ypaly90d.mjs} +0 -0
  403. /package/dist/{challenge-store-DFxX0_15.mjs → challenge-store-D5pSY2S1.mjs} +0 -0
  404. /package/dist/{components--pg6zBvu.mjs → components-DImzlLhb.mjs} +0 -0
  405. /package/dist/{context-types-r8oFvyOq.mjs → context-types-eBuMDAg-.mjs} +0 -0
  406. /package/dist/{default-B6iaLvP7.mjs → default-D4Vygcy_.mjs} +0 -0
  407. /package/dist/{email-console-BOVqIYpB.mjs → email-console-Bp0r68iF.mjs} +0 -0
  408. /package/dist/{escape-BDgouFFY.mjs → escape-DN4Qmjsl.mjs} +0 -0
  409. /package/dist/{hash-BPmX6P8I.mjs → hash-mxxx2oPW.mjs} +0 -0
  410. /package/dist/{mime-CbKsKDCB.mjs → mime-C0vOHFm7.mjs} +0 -0
  411. /package/dist/{mode-CZqCF9dZ.mjs → mode-C8yZnIlR.mjs} +0 -0
  412. /package/dist/{oauth-state-store-qncPB_IU.mjs → oauth-state-store-CndT35iw.mjs} +0 -0
  413. /package/dist/{oauth-user-lookup-BFjWF8wK.mjs → oauth-user-lookup-CMehAaKs.mjs} +0 -0
  414. /package/dist/{options-LsPjZlqD.mjs → options-DQPTH5n0.mjs} +0 -0
  415. /package/dist/{passkey-config-DzHQK90v.mjs → passkey-config-NYukYGed.mjs} +0 -0
  416. /package/dist/{patterns-DKAX6oqN.mjs → patterns-CoqlXUvk.mjs} +0 -0
  417. /package/dist/{provider-loader-DzUxejk7.mjs → provider-loader-BtfrrzfD.mjs} +0 -0
  418. /package/dist/{public-url-B_m9IwdV.mjs → public-url-Cp6hyjVR.mjs} +0 -0
  419. /package/dist/{redirect-BjIBtDWY.mjs → redirect-DrbEmU9a.mjs} +0 -0
  420. /package/dist/{request-cache-ho99schz.mjs → request-cache-CBFjCwSt.mjs} +0 -0
  421. /package/dist/{resolve-DTuDyYKs.mjs → resolve-DE71WcL9.mjs} +0 -0
  422. /package/dist/{setup-nonce-pyGfc9hV.mjs → setup-nonce-BtkKnvxQ.mjs} +0 -0
  423. /package/dist/{sidecar-client-mJxwayYN.mjs → sidecar-client-Ctzfyyp-.mjs} +0 -0
  424. /package/dist/{slugify-BqebcqBg.mjs → slugify-KSsVD_D3.mjs} +0 -0
  425. /package/dist/{ssrf-DiKqWd3N.mjs → ssrf-FJqwQzj4.mjs} +0 -0
  426. /package/dist/{tokens-SLmgowfd.mjs → tokens-BDQU29Vd.mjs} +0 -0
  427. /package/dist/{transaction-CFLhEOuH.mjs → transaction-0LEAAy8R.mjs} +0 -0
  428. /package/dist/{transport-BnsNxh2l.d.mts → transport-D9pKAzFP.d.mts} +0 -0
  429. /package/dist/{trusted-proxy-BgIIcbsN.mjs → trusted-proxy-Dm7Mxvt6.mjs} +0 -0
  430. /package/dist/{types-CJ8DkL0q.mjs → types-B7TDDFMG.mjs} +0 -0
  431. /package/dist/{types-Dryvv7LH.d.mts → types-g0zMtraY.d.mts} +0 -0
  432. /package/dist/{types-Cg276-3z.mjs → types-mVVgRPaU.mjs} +0 -0
  433. /package/dist/{types-DZpT-PnG.d.mts → types-wFu80FrO.d.mts} +0 -0
  434. /package/dist/{url-w7Mp5qAD.mjs → url-EMAguGVk.mjs} +0 -0
@@ -1,14 +1,75 @@
1
- import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-CuUt7K50.mjs";
2
- import { t as OptionsRepository } from "./options-LsPjZlqD.mjs";
3
- import { n as createPluginContext, t as PluginContextFactory } from "./context-CQVNZaR8.mjs";
4
- import { t as extractRequestMeta } from "./request-meta-DAb6xaP9.mjs";
5
- import { r as setCronTasksEnabled } from "./cron-DxCAU_Sn.mjs";
6
- import { sql } from "kysely";
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-ChGNDY8r.mjs";
4
+ import { t as OptionsRepository } from "./options-DQPTH5n0.mjs";
5
+ import { n as createPluginContext, t as PluginContextFactory } from "./context-fSCYLL3c.mjs";
6
+ import { t as extractRequestMeta } from "./request-meta-CFe1tW2M.mjs";
7
+ import { r as setCronTasksEnabled } from "./cron-B1SL_lsb.mjs";
8
+ import { i as parseEncryptionKeys, t as DinewaySecretsError } from "./secrets-ZV5rd8yV.mjs";
9
+ import { Migrator, sql } from "kysely";
7
10
  import { AsyncLocalStorage } from "node:async_hooks";
8
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,13 +1246,20 @@ function definePlugin(definition) {
848
1246
  * Internal: define a native-format plugin with full validation and normalization.
849
1247
  */
850
1248
  function defineNativePlugin(definition) {
851
- const { id, version, capabilities = [], allowedHosts = [], hooks = {}, routes = {}, admin = {} } = 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+/;
1252
+ const MIGRATION_NAME_PATTERN = /^\d{3}_[a-z][a-z0-9_]*$/;
1253
+ const { id, version, capabilities = [], allowedHosts = [], migrations = {}, hooks = {}, routes = {}, publicationReleasePolicies = [], admin = {} } = definition;
1254
+ const mcp = resolveMcp(definition.mcp, id);
852
1255
  const storage = definition.storage ?? {};
1256
+ const normalizedMigrations = Object.fromEntries(Object.entries(migrations).toSorted(([left], [right]) => left.localeCompare(right)));
853
1257
  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
1258
  if (!SEMVER_PATTERN.test(version)) throw new Error(`Invalid plugin version "${version}". Must be semver format (e.g., "1.0.0").`);
855
1259
  const validCapabilities = new Set([
856
1260
  "network:request",
857
1261
  "network:request:unrestricted",
1262
+ "network:request:development-loopback",
858
1263
  "content:read",
859
1264
  "content:write",
860
1265
  "media:read",
@@ -866,21 +1271,125 @@ function defineNativePlugin(definition) {
866
1271
  "hooks.page-fragments:register"
867
1272
  ]);
868
1273
  for (const cap of capabilities) if (!validCapabilities.has(cap)) throw new Error(`Invalid capability "${cap}" in plugin "${id}".`);
1274
+ for (const [name, migration] of Object.entries(normalizedMigrations)) {
1275
+ if (!MIGRATION_NAME_PATTERN.test(name)) throw new Error(`Invalid migration name "${name}" in plugin "${id}". Use NNN_descriptive_name.`);
1276
+ if (typeof migration.up !== "function") throw new Error(`Plugin "${id}" migration "${name}" requires an up function.`);
1277
+ if (migration.down !== void 0 && typeof migration.down !== "function") throw new Error(`Plugin "${id}" migration "${name}" has an invalid down function.`);
1278
+ }
869
1279
  const normalizedCapabilities = [...capabilities];
870
1280
  if (capabilities.includes("content:write") && !capabilities.includes("content:read")) normalizedCapabilities.push("content:read");
871
1281
  if (capabilities.includes("media:write") && !capabilities.includes("media:read")) normalizedCapabilities.push("media:read");
872
1282
  if (capabilities.includes("network:request:unrestricted") && !capabilities.includes("network:request")) normalizedCapabilities.push("network:request");
1283
+ if (capabilities.includes("network:request:development-loopback") && !capabilities.includes("network:request:unrestricted")) throw new Error(`Plugin "${id}" must also declare "network:request:unrestricted" when using "network:request:development-loopback".`);
1284
+ for (const [routeName, route] of Object.entries(routes)) {
1285
+ if (route.body !== void 0 && route.body !== "json" && route.body !== "raw") throw new Error(`Plugin "${id}" route "${routeName}" has an invalid body mode.`);
1286
+ if (route.body === "raw" && route.input) throw new Error(`Plugin "${id}" raw-body route "${routeName}" cannot declare an input schema.`);
1287
+ if (route.body !== "raw" && route.maxBodyBytes !== void 0) throw new Error(`Plugin "${id}" JSON route "${routeName}" cannot declare a raw body-size limit.`);
1288
+ 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.`);
1289
+ if (route.public === true && route.authorization) throw new Error(`Plugin "${id}" public route "${routeName}" cannot declare private authorization.`);
1290
+ if (route.apiTokenPolicy && (route.public === true || !route.authorization)) throw new Error(`Plugin "${id}" route "${routeName}" API-token policy requires private authorization.`);
1291
+ if (route.apiTokenPolicy?.effect === "write" && (route.apiTokenPolicy.idempotencyField.trim().length === 0 || !route.input)) throw new Error(`Plugin "${id}" route "${routeName}" API-token write policy requires a parsed input schema and idempotency field.`);
1292
+ 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.`);
1293
+ const restaurantPermission = route.authorization?.restaurantPermission;
1294
+ 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.`);
1295
+ if (!restaurantPermission && (route.resolveTarget || route.authorizeTarget)) throw new Error(`Plugin "${id}" route "${routeName}" cannot declare target authorization without a restaurant permission.`);
1296
+ }
1297
+ const resolvedHooks = resolveHooks(hooks, id);
1298
+ const releasePolicyIds = /* @__PURE__ */ new Set();
1299
+ for (const policy of publicationReleasePolicies) {
1300
+ if (!policy.id.trim()) throw new Error(`Plugin "${id}" publication release policy requires an ID.`);
1301
+ if (releasePolicyIds.has(policy.id)) throw new Error(`Plugin "${id}" has duplicate publication release policy "${policy.id}".`);
1302
+ releasePolicyIds.add(policy.id);
1303
+ }
873
1304
  return {
874
1305
  id,
875
1306
  version,
876
1307
  capabilities: normalizedCapabilities,
877
1308
  allowedHosts,
878
1309
  storage,
879
- hooks: resolveHooks(hooks, id),
1310
+ migrations: Object.freeze(normalizedMigrations),
1311
+ hooks: resolvedHooks,
880
1312
  routes,
1313
+ mcp,
1314
+ publicationReleasePolicies: [...publicationReleasePolicies].toSorted((left, right) => left.id.localeCompare(right.id)),
881
1315
  admin
882
1316
  };
883
1317
  }
1318
+ function resolveMcp(mcp, pluginId) {
1319
+ const tools = [...mcp?.tools ?? []];
1320
+ const resources = [...mcp?.resources ?? []];
1321
+ const namespace = `${pluginId.split("/").at(-1).replaceAll("-", "_")}_`;
1322
+ const toolNames = /* @__PURE__ */ new Set();
1323
+ const resourceNames = /* @__PURE__ */ new Set();
1324
+ const resourceUris = /* @__PURE__ */ new Set();
1325
+ const authorizeTarget = mcp?.authorizeTarget;
1326
+ for (const tool of tools) {
1327
+ validateMcpName(tool.name, "tool", pluginId, namespace, MCP_NAME_PATTERN);
1328
+ if (toolNames.has(tool.name)) throw new Error(`Duplicate MCP tool "${tool.name}" in plugin "${pluginId}".`);
1329
+ toolNames.add(tool.name);
1330
+ validateMcpText(tool.title, "title", tool.name, pluginId);
1331
+ validateMcpText(tool.description, "description", tool.name, pluginId);
1332
+ validateMcpAuthorization(tool.authorization, tool.name, pluginId);
1333
+ validateZodSchema(tool.inputSchema, "inputSchema", tool.name, pluginId);
1334
+ if (tool.outputSchema) validateZodSchema(tool.outputSchema, "outputSchema", tool.name, pluginId);
1335
+ if (typeof tool.handler !== "function") throw new Error(`MCP tool "${tool.name}" in plugin "${pluginId}" requires a handler.`);
1336
+ if (tool.authorization.restaurantPermission && !tool.resolveTarget) throw new Error(`MCP tool "${tool.name}" in plugin "${pluginId}" declares a restaurant permission but no resolveTarget function.`);
1337
+ if (tool.authorization.restaurantPermission && typeof authorizeTarget !== "function") throw new Error(`MCP tool "${tool.name}" in plugin "${pluginId}" requires an MCP target authorizer.`);
1338
+ if (tool.kind === "command") {
1339
+ if (typeof tool.preview !== "function") throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" requires preview.`);
1340
+ if (typeof tool.actionFingerprint !== "function" || tool.idempotency?.required !== true) throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" requires actionFingerprint and required idempotency.`);
1341
+ 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.`);
1342
+ validateMcpText(tool.risk.reason, "risk reason", tool.name, pluginId);
1343
+ validateCommandRisk(tool, pluginId);
1344
+ }
1345
+ }
1346
+ for (const resource of resources) {
1347
+ validateMcpName(resource.name, "resource", pluginId, namespace, MCP_NAME_PATTERN);
1348
+ if (resourceNames.has(resource.name)) throw new Error(`Duplicate MCP resource "${resource.name}" in plugin "${pluginId}".`);
1349
+ resourceNames.add(resource.name);
1350
+ validateMcpText(resource.title, "title", resource.name, pluginId);
1351
+ validateMcpText(resource.description, "description", resource.name, pluginId);
1352
+ validateMcpAuthorization(resource.authorization, resource.name, pluginId);
1353
+ if (resource.authorization.restaurantPermission && !resource.target) throw new Error(`MCP resource "${resource.name}" in plugin "${pluginId}" declares a restaurant permission but no target.`);
1354
+ if (resource.authorization.restaurantPermission && typeof authorizeTarget !== "function") throw new Error(`MCP resource "${resource.name}" in plugin "${pluginId}" requires an MCP target authorizer.`);
1355
+ try {
1356
+ if (!new URL(resource.uri).protocol) throw new Error("Missing URI protocol");
1357
+ } catch {
1358
+ throw new Error(`Invalid MCP resource URI "${resource.uri}" in plugin "${pluginId}".`);
1359
+ }
1360
+ if (resourceUris.has(resource.uri)) throw new Error(`Duplicate MCP resource URI "${resource.uri}" in plugin "${pluginId}".`);
1361
+ resourceUris.add(resource.uri);
1362
+ if (typeof resource.handler !== "function") throw new Error(`MCP resource "${resource.name}" in plugin "${pluginId}" requires a handler.`);
1363
+ }
1364
+ return authorizeTarget ? {
1365
+ tools,
1366
+ resources,
1367
+ authorizeTarget
1368
+ } : {
1369
+ tools,
1370
+ resources
1371
+ };
1372
+ }
1373
+ function validateCommandRisk(tool, pluginId) {
1374
+ if (tool.risk.mandatory !== true) throw new Error(`MCP command "${tool.name}" in plugin "${pluginId}" must declare a mandatory risk floor.`);
1375
+ 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.`);
1376
+ 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.`);
1377
+ if (tool.risk.level === "strong_confirmation" && tool.audit !== "strict") throw new Error(`Strong-confirmation MCP command "${tool.name}" in plugin "${pluginId}" requires strict audit.`);
1378
+ 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.`);
1379
+ }
1380
+ function validateMcpName(name, kind, pluginId, namespace, pattern) {
1381
+ 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}".`);
1382
+ }
1383
+ function validateMcpText(value, field, name, pluginId) {
1384
+ if (typeof value !== "string" || value.trim().length === 0) throw new Error(`MCP contribution "${name}" in plugin "${pluginId}" requires ${field}.`);
1385
+ }
1386
+ function validateMcpAuthorization(authorization, name, pluginId) {
1387
+ if (!authorization || !Object.hasOwn(Permissions, authorization.permission)) throw new Error(`MCP contribution "${name}" in plugin "${pluginId}" declares an invalid permission.`);
1388
+ 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.`);
1389
+ }
1390
+ function validateZodSchema(schema, field, name, pluginId) {
1391
+ 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}.`);
1392
+ }
884
1393
  /**
885
1394
  * Resolve hooks to normalized format with defaults.
886
1395
  *
@@ -929,6 +1438,69 @@ function resolveHook(hook, pluginId) {
929
1438
  };
930
1439
  }
931
1440
 
1441
+ //#endregion
1442
+ //#region src/plugins/migrations.ts
1443
+ const LEADING_AT = /^@/;
1444
+ var StaticPluginMigrationProvider = class {
1445
+ constructor(migrations) {
1446
+ this.migrations = migrations;
1447
+ }
1448
+ async getMigrations() {
1449
+ return this.migrations;
1450
+ }
1451
+ };
1452
+ /**
1453
+ * Run migrations contributed by configured trusted native plugins.
1454
+ *
1455
+ * Each plugin receives isolated Kysely migration and lock tables. This keeps migration history valid
1456
+ * when another plugin is removed and lets different plugins safely reuse local migration names.
1457
+ */
1458
+ async function runPluginMigrations(db, plugins) {
1459
+ const applied = [];
1460
+ for (const plugin of [...plugins].toSorted((left, right) => left.id.localeCompare(right.id))) {
1461
+ if (Object.keys(plugin.migrations).length === 0) continue;
1462
+ const tables = pluginMigrationTableNames(plugin.id);
1463
+ const { error, results } = await new Migrator({
1464
+ db,
1465
+ provider: new StaticPluginMigrationProvider(plugin.migrations),
1466
+ migrationTableName: tables.migrations,
1467
+ migrationLockTableName: tables.lock
1468
+ }).migrateToLatest();
1469
+ const successful = results?.filter((result) => result.status === "Success").map((result) => result.migrationName) ?? [];
1470
+ if (error) {
1471
+ const failed = results?.find((result) => result.status === "Error")?.migrationName;
1472
+ throw new Error(`Plugin migration failed for ${plugin.id}${failed ? ` (${failed})` : ""}: ${errorMessage(error)}`, { cause: error });
1473
+ }
1474
+ applied.push({
1475
+ pluginId: plugin.id,
1476
+ applied: successful
1477
+ });
1478
+ }
1479
+ return applied;
1480
+ }
1481
+ function pluginMigrationTableNames(pluginId) {
1482
+ const slug = pluginId.replace(LEADING_AT, "").replaceAll("/", "_").replaceAll("-", "_").slice(0, 20);
1483
+ const suffix = fnv1a(pluginId);
1484
+ return {
1485
+ migrations: `_dineway_plugin_migration_${slug}_${suffix}`,
1486
+ lock: `_dineway_plugin_migration_lock_${slug}_${suffix}`
1487
+ };
1488
+ }
1489
+ function fnv1a(value) {
1490
+ let hash = 2166136261;
1491
+ for (let index = 0; index < value.length; index++) {
1492
+ hash ^= value.charCodeAt(index);
1493
+ hash = Math.imul(hash, 16777619);
1494
+ }
1495
+ return (hash >>> 0).toString(16).padStart(8, "0");
1496
+ }
1497
+ function errorMessage(error) {
1498
+ if (!(error instanceof Error)) return JSON.stringify(error);
1499
+ if (error.message) return error.message;
1500
+ if (error.cause instanceof Error && error.cause.message) return error.cause.message;
1501
+ return "unknown error";
1502
+ }
1503
+
932
1504
  //#endregion
933
1505
  //#region src/plugins/hooks.ts
934
1506
  /**
@@ -1023,6 +1595,9 @@ var HookPipeline = class HookPipeline {
1023
1595
  this.registerPluginHook(plugin, "content:afterDelete");
1024
1596
  this.registerPluginHook(plugin, "content:afterPublish");
1025
1597
  this.registerPluginHook(plugin, "content:afterUnpublish");
1598
+ this.registerPluginHook(plugin, "content:afterRestore");
1599
+ this.registerPluginHook(plugin, "content:afterSchedule");
1600
+ this.registerPluginHook(plugin, "content:afterUnschedule");
1026
1601
  this.registerPluginHook(plugin, "media:beforeUpload");
1027
1602
  this.registerPluginHook(plugin, "media:afterUpload");
1028
1603
  this.registerPluginHook(plugin, "cron");
@@ -1055,6 +1630,9 @@ var HookPipeline = class HookPipeline {
1055
1630
  ["content:afterDelete", "content:read"],
1056
1631
  ["content:afterPublish", "content:read"],
1057
1632
  ["content:afterUnpublish", "content:read"],
1633
+ ["content:afterRestore", "content:read"],
1634
+ ["content:afterSchedule", "content:read"],
1635
+ ["content:afterUnschedule", "content:read"],
1058
1636
  ["media:beforeUpload", "media:write"],
1059
1637
  ["media:afterUpload", "media:read"],
1060
1638
  ["comment:beforeCreate", "users:read"],
@@ -1348,40 +1926,34 @@ var HookPipeline = class HookPipeline {
1348
1926
  * Run content:afterPublish hooks (fire-and-forget).
1349
1927
  */
1350
1928
  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;
1929
+ return this.runContentStateChangeHook("content:afterPublish", content, collection);
1379
1930
  }
1380
1931
  /**
1381
1932
  * Run content:afterUnpublish hooks (fire-and-forget).
1382
1933
  */
1383
1934
  async runContentAfterUnpublish(content, collection) {
1384
- const hooks = this.getTypedHooks("content:afterUnpublish");
1935
+ return this.runContentStateChangeHook("content:afterUnpublish", content, collection);
1936
+ }
1937
+ /**
1938
+ * Run content:afterRestore hooks (fire-and-forget).
1939
+ */
1940
+ async runContentAfterRestore(content, collection) {
1941
+ return this.runContentStateChangeHook("content:afterRestore", content, collection);
1942
+ }
1943
+ /**
1944
+ * Run content:afterSchedule hooks (fire-and-forget).
1945
+ */
1946
+ async runContentAfterSchedule(content, collection) {
1947
+ return this.runContentStateChangeHook("content:afterSchedule", content, collection);
1948
+ }
1949
+ /**
1950
+ * Run content:afterUnschedule hooks (fire-and-forget).
1951
+ */
1952
+ async runContentAfterUnschedule(content, collection) {
1953
+ return this.runContentStateChangeHook("content:afterUnschedule", content, collection);
1954
+ }
1955
+ async runContentStateChangeHook(name, content, collection) {
1956
+ const hooks = this.getTypedHooks(name);
1385
1957
  const results = [];
1386
1958
  for (const hook of hooks) {
1387
1959
  const { handler } = hook;
@@ -2022,6 +2594,23 @@ var EmailPipeline = class {
2022
2594
  * - Error handling
2023
2595
  *
2024
2596
  */
2597
+ const CONSUMED_BODY_METHODS = new Set([
2598
+ "json",
2599
+ "text",
2600
+ "arrayBuffer",
2601
+ "blob",
2602
+ "formData",
2603
+ "bytes"
2604
+ ]);
2605
+ function guardConsumedRequestBody(request, bodyField) {
2606
+ return new Proxy(request, { get(target, prop) {
2607
+ if (typeof prop === "string" && CONSUMED_BODY_METHODS.has(prop)) return () => {
2608
+ 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}().`);
2609
+ };
2610
+ const value = Reflect.get(target, prop, target);
2611
+ return typeof value === "function" ? value.bind(target) : value;
2612
+ } });
2613
+ }
2025
2614
  /**
2026
2615
  * Route handler for a plugin
2027
2616
  */
@@ -2047,8 +2636,20 @@ var PluginRouteHandler = class {
2047
2636
  },
2048
2637
  status: 404
2049
2638
  };
2639
+ const requestMethod = options.request.method.toUpperCase();
2640
+ const allowedMethods = route.methods;
2641
+ if (allowedMethods && !allowedMethods.includes(requestMethod)) return {
2642
+ success: false,
2643
+ error: {
2644
+ code: "METHOD_NOT_ALLOWED",
2645
+ message: `Method ${requestMethod} is not allowed for this plugin route`
2646
+ },
2647
+ status: 405
2648
+ };
2649
+ const bodyMode = route.body ?? "json";
2050
2650
  let validatedInput;
2051
- if (route.input) {
2651
+ if (bodyMode === "raw") validatedInput = void 0;
2652
+ else if (route.input) {
2052
2653
  const parseResult = route.input.safeParse(options.body);
2053
2654
  if (!parseResult.success) return {
2054
2655
  success: false,
@@ -2061,11 +2662,72 @@ var PluginRouteHandler = class {
2061
2662
  };
2062
2663
  validatedInput = parseResult.data;
2063
2664
  } else validatedInput = options.body;
2665
+ const baseContext = this.contextFactory.createContext(this.plugin);
2666
+ const actor = options.actor ?? null;
2667
+ const rawBody = bodyMode === "raw" ? options.rawBody?.slice() ?? new Uint8Array() : null;
2668
+ const request = guardConsumedRequestBody(options.request, bodyMode === "raw" ? "ctx.rawBody" : "ctx.input");
2669
+ const requestMeta = extractRequestMeta(options.request, this.trustedProxyHeaders);
2670
+ let target = null;
2671
+ if (route.authorization && !actor) return {
2672
+ success: false,
2673
+ error: {
2674
+ code: "UNAUTHORIZED",
2675
+ message: "Authentication required"
2676
+ },
2677
+ status: 401
2678
+ };
2679
+ const restaurantPermission = route.authorization?.restaurantPermission;
2680
+ if (restaurantPermission) {
2681
+ if (!actor) return {
2682
+ success: false,
2683
+ error: {
2684
+ code: "UNAUTHORIZED",
2685
+ message: "Authentication required"
2686
+ },
2687
+ status: 401
2688
+ };
2689
+ try {
2690
+ const resolvedTarget = await route.resolveTarget(validatedInput, {
2691
+ ...baseContext,
2692
+ actor,
2693
+ request,
2694
+ requestMeta
2695
+ });
2696
+ validateTarget(resolvedTarget);
2697
+ target = Object.freeze({ ...resolvedTarget });
2698
+ const authorizationContext = {
2699
+ ...baseContext,
2700
+ input: validatedInput,
2701
+ rawBody,
2702
+ actor,
2703
+ target,
2704
+ request,
2705
+ requestMeta
2706
+ };
2707
+ await route.authorizeTarget({
2708
+ restaurantPermission,
2709
+ target,
2710
+ context: authorizationContext
2711
+ });
2712
+ } catch {
2713
+ return {
2714
+ success: false,
2715
+ error: {
2716
+ code: "FORBIDDEN",
2717
+ message: "Restaurant target authorization denied for this operation"
2718
+ },
2719
+ status: 403
2720
+ };
2721
+ }
2722
+ }
2064
2723
  const routeContext = {
2065
- ...this.contextFactory.createContext(this.plugin),
2724
+ ...baseContext,
2066
2725
  input: validatedInput,
2067
- request: options.request,
2068
- requestMeta: extractRequestMeta(options.request, this.trustedProxyHeaders)
2726
+ rawBody,
2727
+ actor,
2728
+ target,
2729
+ request,
2730
+ requestMeta
2069
2731
  };
2070
2732
  try {
2071
2733
  return {
@@ -2113,9 +2775,22 @@ var PluginRouteHandler = class {
2113
2775
  getRouteMeta(name) {
2114
2776
  const route = this.plugin.routes[name];
2115
2777
  if (!route) return null;
2116
- return { public: route.public === true };
2778
+ return {
2779
+ public: route.public === true,
2780
+ body: route.body ?? "json",
2781
+ ...route.body === "raw" ? { maxBodyBytes: route.maxBodyBytes ?? DEFAULT_PLUGIN_RAW_BODY_LIMIT_BYTES } : {},
2782
+ authorization: route.authorization,
2783
+ apiTokenPolicy: route.apiTokenPolicy
2784
+ };
2117
2785
  }
2118
2786
  };
2787
+ function validateTarget(target) {
2788
+ if (![
2789
+ "plugin",
2790
+ "location",
2791
+ "entity"
2792
+ ].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");
2793
+ }
2119
2794
  /**
2120
2795
  * Error class for plugin routes
2121
2796
  * Allows plugins to return structured errors with specific HTTP status codes
@@ -2224,6 +2899,164 @@ var PluginRouteRegistry = class {
2224
2899
  }
2225
2900
  };
2226
2901
 
2902
+ //#endregion
2903
+ //#region src/plugins/secrets.ts
2904
+ const ENCRYPTION_VERSION = 1;
2905
+ const AES_GCM_NONCE_BYTES = 12;
2906
+ const MAX_SECRET_BYTES = 65536;
2907
+ /**
2908
+ * Encrypted storage for trusted plugin credentials.
2909
+ *
2910
+ * Management surfaces must use set/status/list/delete and never echo the value
2911
+ * passed to set. resolveForRuntime is reserved for trusted server-side provider
2912
+ * calls and must not be exposed through plugin routes, MCP, logs, or audit data.
2913
+ */
2914
+ var PluginSecretStore = class {
2915
+ repository;
2916
+ keysPromise;
2917
+ constructor(db, options = {}) {
2918
+ this.repository = new PluginSecretRepository(db);
2919
+ this.keysPromise = options.keys ? Promise.resolve(options.keys) : parseEncryptionKeys(readEncryptionKey(options.env));
2920
+ }
2921
+ async set(pluginId, name, value) {
2922
+ validateSecretIdentity(pluginId, name);
2923
+ validateSecretValue(value);
2924
+ const primaryKey = (await this.requireKeys())[0];
2925
+ if (!primaryKey) throw encryptionKeyRequired();
2926
+ const encrypted = await encryptSecret(primaryKey, pluginId, name, value);
2927
+ return toStatus(await this.repository.put({
2928
+ pluginId,
2929
+ name,
2930
+ encryptionVersion: ENCRYPTION_VERSION,
2931
+ keyId: primaryKey.kid,
2932
+ nonce: encrypted.nonce,
2933
+ ciphertext: encrypted.ciphertext,
2934
+ fingerprint: await fingerprintSecret(primaryKey, value)
2935
+ }));
2936
+ }
2937
+ async status(pluginId, name) {
2938
+ validateSecretIdentity(pluginId, name);
2939
+ const stored = await this.repository.find(pluginId, name);
2940
+ return stored ? toStatus(stored) : null;
2941
+ }
2942
+ async list(pluginId) {
2943
+ validatePluginIdentifier(pluginId, "plugin id");
2944
+ return (await this.repository.list(pluginId)).map(toStatus);
2945
+ }
2946
+ async delete(pluginId, name) {
2947
+ validateSecretIdentity(pluginId, name);
2948
+ return this.repository.delete(pluginId, name);
2949
+ }
2950
+ async resolveForRuntime(pluginId, name) {
2951
+ validateSecretIdentity(pluginId, name);
2952
+ const stored = await this.repository.find(pluginId, name);
2953
+ if (!stored) return null;
2954
+ if (stored.encryptionVersion !== ENCRYPTION_VERSION) throw new DinewaySecretsError("The stored plugin secret uses an unsupported encryption version.", "PLUGIN_SECRET_VERSION_UNSUPPORTED");
2955
+ const keys = await this.requireKeys();
2956
+ const decryptionKey = keys.find((key) => key.kid === stored.keyId);
2957
+ if (!decryptionKey) throw new DinewaySecretsError("The stored plugin secret cannot be decrypted by the configured key ring.", "PLUGIN_SECRET_KEY_UNAVAILABLE");
2958
+ let value;
2959
+ try {
2960
+ value = await decryptSecret(decryptionKey, stored);
2961
+ } catch {
2962
+ throw new DinewaySecretsError("The stored plugin secret failed authenticated decryption.", "PLUGIN_SECRET_DECRYPT_FAILED");
2963
+ }
2964
+ const primaryKey = keys[0];
2965
+ if (primaryKey && primaryKey.kid !== stored.keyId) {
2966
+ const encrypted = await encryptSecret(primaryKey, pluginId, name, value);
2967
+ await this.repository.put({
2968
+ pluginId,
2969
+ name,
2970
+ encryptionVersion: ENCRYPTION_VERSION,
2971
+ keyId: primaryKey.kid,
2972
+ nonce: encrypted.nonce,
2973
+ ciphertext: encrypted.ciphertext,
2974
+ fingerprint: await fingerprintSecret(primaryKey, value)
2975
+ });
2976
+ }
2977
+ return value;
2978
+ }
2979
+ async requireKeys() {
2980
+ const keys = await this.keysPromise;
2981
+ if (!keys || keys.length === 0) throw encryptionKeyRequired();
2982
+ return keys;
2983
+ }
2984
+ };
2985
+ async function encryptSecret(key, pluginId, name, value) {
2986
+ const nonce = crypto.getRandomValues(new Uint8Array(AES_GCM_NONCE_BYTES));
2987
+ const cryptoKey = await importAesKey(key, ["encrypt"]);
2988
+ const ciphertext = await crypto.subtle.encrypt({
2989
+ name: "AES-GCM",
2990
+ iv: toArrayBuffer$1(nonce),
2991
+ additionalData: aad(pluginId, name, ENCRYPTION_VERSION)
2992
+ }, cryptoKey, new TextEncoder().encode(value));
2993
+ return {
2994
+ nonce: encodeBase64url(nonce),
2995
+ ciphertext: encodeBase64url(new Uint8Array(ciphertext))
2996
+ };
2997
+ }
2998
+ async function decryptSecret(key, stored) {
2999
+ const cryptoKey = await importAesKey(key, ["decrypt"]);
3000
+ const plaintext = await crypto.subtle.decrypt({
3001
+ name: "AES-GCM",
3002
+ iv: toArrayBuffer$1(decodeBase64url(stored.nonce)),
3003
+ additionalData: aad(stored.pluginId, stored.name, stored.encryptionVersion)
3004
+ }, cryptoKey, toArrayBuffer$1(decodeBase64url(stored.ciphertext)));
3005
+ return new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
3006
+ }
3007
+ async function importAesKey(key, usages) {
3008
+ return crypto.subtle.importKey("raw", toArrayBuffer$1(key.key), { name: "AES-GCM" }, false, usages);
3009
+ }
3010
+ function aad(pluginId, name, encryptionVersion) {
3011
+ return toArrayBuffer$1(new TextEncoder().encode(JSON.stringify([
3012
+ "dineway-plugin-secret",
3013
+ encryptionVersion,
3014
+ pluginId,
3015
+ name
3016
+ ])));
3017
+ }
3018
+ async function fingerprintSecret(key, value) {
3019
+ const hmacKey = await crypto.subtle.importKey("raw", toArrayBuffer$1(key.key), {
3020
+ name: "HMAC",
3021
+ hash: "SHA-256"
3022
+ }, false, ["sign"]);
3023
+ const digest = await crypto.subtle.sign("HMAC", hmacKey, new TextEncoder().encode(`dineway-plugin-secret-fingerprint:v1:${value}`));
3024
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 12);
3025
+ }
3026
+ function toArrayBuffer$1(bytes) {
3027
+ const buffer = new ArrayBuffer(bytes.byteLength);
3028
+ new Uint8Array(buffer).set(bytes);
3029
+ return buffer;
3030
+ }
3031
+ function validateSecretIdentity(pluginId, name) {
3032
+ validatePluginIdentifier(pluginId, "plugin id");
3033
+ validateIdentifier(name, "plugin secret name");
3034
+ }
3035
+ function validateSecretValue(value) {
3036
+ const byteLength = new TextEncoder().encode(value).byteLength;
3037
+ 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");
3038
+ }
3039
+ function encryptionKeyRequired() {
3040
+ return new DinewaySecretsError("DINEWAY_ENCRYPTION_KEY is required before plugin secrets can be written or decrypted.", "ENCRYPTION_KEY_REQUIRED");
3041
+ }
3042
+ function readEncryptionKey(env) {
3043
+ if (env) return env.DINEWAY_ENCRYPTION_KEY;
3044
+ const meta = import.meta.env ?? {};
3045
+ const proc = typeof process !== "undefined" && process.env ? process.env : {};
3046
+ return meta.DINEWAY_ENCRYPTION_KEY ?? proc.DINEWAY_ENCRYPTION_KEY;
3047
+ }
3048
+ function toStatus(stored) {
3049
+ return {
3050
+ pluginId: stored.pluginId,
3051
+ name: stored.name,
3052
+ present: true,
3053
+ fingerprint: stored.fingerprint,
3054
+ keyId: stored.keyId,
3055
+ createdAt: stored.createdAt,
3056
+ updatedAt: stored.updatedAt
3057
+ };
3058
+ }
3059
+
2227
3060
  //#endregion
2228
3061
  //#region src/plugins/manager.ts
2229
3062
  /** Options table key prefix for exclusive hook DB reads via PluginManager */
@@ -2393,6 +3226,27 @@ var PluginManager = class {
2393
3226
  return this.hookPipeline.runContentAfterUnpublish(content, collection);
2394
3227
  }
2395
3228
  /**
3229
+ * Run content:afterRestore hooks across all active plugins
3230
+ */
3231
+ async runContentAfterRestore(content, collection) {
3232
+ this.ensureInitialized();
3233
+ return this.hookPipeline.runContentAfterRestore(content, collection);
3234
+ }
3235
+ /**
3236
+ * Run content:afterSchedule hooks across all active plugins
3237
+ */
3238
+ async runContentAfterSchedule(content, collection) {
3239
+ this.ensureInitialized();
3240
+ return this.hookPipeline.runContentAfterSchedule(content, collection);
3241
+ }
3242
+ /**
3243
+ * Run content:afterUnschedule hooks across all active plugins
3244
+ */
3245
+ async runContentAfterUnschedule(content, collection) {
3246
+ this.ensureInitialized();
3247
+ return this.hookPipeline.runContentAfterUnschedule(content, collection);
3248
+ }
3249
+ /**
2396
3250
  * Run media:beforeUpload hooks across all active plugins
2397
3251
  */
2398
3252
  async runMediaBeforeUpload(file) {
@@ -2859,7 +3713,13 @@ export async function invokeRoute(routeName, input, serializedRequest) {
2859
3713
  throw new Error(\`Route \${routeName} handler is not a function\`);
2860
3714
  }
2861
3715
  return handler(
2862
- { input, request: serializedRequest, requestMeta: serializedRequest.meta },
3716
+ {
3717
+ input,
3718
+ actor: serializedRequest.actor ?? null,
3719
+ target: null,
3720
+ request: serializedRequest,
3721
+ requestMeta: serializedRequest.meta,
3722
+ },
2863
3723
  ctx,
2864
3724
  );
2865
3725
  }
@@ -3039,8 +3899,13 @@ function createResolvedPluginFromManifest(manifest) {
3039
3899
  capabilities: normalizeCapabilities(manifest.capabilities),
3040
3900
  allowedHosts: manifest.allowedHosts,
3041
3901
  storage: manifest.storage,
3902
+ migrations: {},
3042
3903
  hooks: {},
3043
3904
  routes: {},
3905
+ mcp: {
3906
+ tools: [],
3907
+ resources: []
3908
+ },
3044
3909
  admin: manifest.admin
3045
3910
  };
3046
3911
  }
@@ -3455,16 +4320,4 @@ function createNodeSandboxRunner(options) {
3455
4320
  }
3456
4321
 
3457
4322
  //#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 };
4323
+ export { portableTextToProsemirror as C, image as D, reference as E, RelationRepository as O, verifyGuestAccessToken as S, portableText as T, DEFAULT_PLUGIN_RAW_BODY_LIMIT_BYTES as _, createNoopSandboxRunner as a, hashGuestAccessToken as b, PluginSecretStore as c, EmailPipeline as d, HookPipeline as f, definePlugin as g, runPluginMigrations 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, readBoundedPluginRouteBody as v, prosemirrorToPortableText as w, issueGuestAccessToken as x, GuestAccessTokenConfigurationError as y };