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.
- package/dist/{activity-events-C9u37D5z.mjs → activity-events-y6eT8UW6.mjs} +6 -38
- package/dist/actor-CrObAPV0.mjs +37 -0
- package/dist/after-BfniwgIf.mjs +9 -0
- package/dist/{allowed-origins-DhipQNLb.mjs → allowed-origins-_oeyP6x-.mjs} +1 -1
- package/dist/api/route-utils.d.mts +10 -10
- package/dist/api/route-utils.mjs +17 -14
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +5 -4
- package/dist/{api-Eiy8_zVZ.mjs → api-ByrU2q-Z.mjs} +61 -37
- package/dist/{api-tokens-DtvdtIkm.mjs → api-tokens-CjVMQT-X.mjs} +6 -2
- package/dist/{apply-CX3no7XE.mjs → apply-bZswmZPm.mjs} +117 -77
- package/dist/astro/image-endpoint.d.mts +7 -0
- package/dist/astro/image-endpoint.mjs +54 -0
- package/dist/astro/index.d.mts +35 -11
- package/dist/astro/index.mjs +157 -23
- package/dist/astro/integration/shims/use-sync-external-store-with-selector.js +71 -0
- package/dist/astro/integration/shims/use-sync-external-store.js +1 -0
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.mjs +42 -24
- package/dist/astro/middleware/redirect.mjs +14 -5
- package/dist/astro/middleware/request-context.mjs +9 -6
- package/dist/astro/middleware/seed.d.mts +1 -1
- package/dist/astro/middleware/seed.mjs +2 -2
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.mjs +334 -193
- package/dist/astro/routes/admin.astro +6 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
- package/dist/astro/routes/api/admin/briefing.mjs +22 -18
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -8
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -8
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -8
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +18 -15
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +18 -15
- package/dist/astro/routes/api/admin/bylines/index.mjs +18 -15
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +15 -13
- package/dist/astro/routes/api/admin/comments/_id_.mjs +7 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs +12 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +7 -6
- package/dist/astro/routes/api/admin/comments/index.mjs +12 -10
- package/dist/astro/routes/api/admin/context/_id_/history.mjs +27 -25
- package/dist/astro/routes/api/admin/context/_id_/index.mjs +27 -25
- package/dist/astro/routes/api/admin/context/_id_/review.mjs +32 -29
- package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +32 -29
- package/dist/astro/routes/api/admin/context/diff.mjs +31 -28
- package/dist/astro/routes/api/admin/context/index.mjs +32 -29
- package/dist/astro/routes/api/admin/context/stale.mjs +31 -28
- package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +27 -25
- package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +31 -28
- package/dist/astro/routes/api/admin/hitl-requests/index.mjs +31 -28
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +28 -26
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -52
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -52
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +48 -44
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -50
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +53 -49
- package/dist/astro/routes/api/admin/plugins/index.mjs +48 -44
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +47 -43
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +53 -49
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +47 -43
- package/dist/astro/routes/api/admin/plugins/updates.mjs +47 -43
- package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +6 -6
- package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +32 -28
- package/dist/astro/routes/api/admin/review-requests/index.mjs +11 -10
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +47 -43
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +47 -43
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -7
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
- package/dist/astro/routes/api/admin/users/index.mjs +7 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +12 -11
- package/dist/astro/routes/api/auth/invite/index.mjs +9 -8
- package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -10
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -10
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +10 -9
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -5
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +12 -11
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -9
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -10
- package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -10
- package/dist/astro/routes/api/auth/setup-token/index.mjs +6 -6
- package/dist/astro/routes/api/auth/setup-token/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/signup/complete.mjs +12 -11
- package/dist/astro/routes/api/auth/signup/request.mjs +11 -10
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +16 -14
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +8 -0
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +160 -0
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +12 -11
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +17 -15
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/index.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -6
- package/dist/astro/routes/api/dashboard.mjs +10 -9
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/health.mjs +1 -1
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +23 -11
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +9 -6
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +19 -1
- package/dist/astro/routes/api/import/wordpress/execute.mjs +85 -38
- package/dist/astro/routes/api/import/wordpress/media.d.mts +13 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +20 -14
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +36 -33
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +18 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +121 -101
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +25 -13
- package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +56 -2
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +284 -56
- package/dist/astro/routes/api/manifest.mjs +14 -5
- package/dist/astro/routes/api/mcp.mjs +266 -70
- package/dist/astro/routes/api/media/_id_/confirm.mjs +39 -11
- package/dist/astro/routes/api/media/_id_.mjs +11 -7
- package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +12 -11
- package/dist/astro/routes/api/media.mjs +28 -35
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_/items.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_/translations.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_.mjs +32 -29
- package/dist/astro/routes/api/menus/index.mjs +32 -29
- package/dist/astro/routes/api/oauth/authorize.mjs +7 -7
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
- package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
- package/dist/astro/routes/api/oauth/register.mjs +4 -4
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +4 -3
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +30 -30
- package/dist/astro/routes/api/redirects/404s/index.mjs +12 -11
- package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -11
- package/dist/astro/routes/api/redirects/_id_.mjs +33 -30
- package/dist/astro/routes/api/redirects/index.mjs +33 -30
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/index.mjs +48 -44
- package/dist/astro/routes/api/schema/index.mjs +13 -14
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +47 -43
- package/dist/astro/routes/api/schema/orphans/index.mjs +47 -43
- package/dist/astro/routes/api/search/enable.mjs +12 -11
- package/dist/astro/routes/api/search/index.mjs +11 -10
- package/dist/astro/routes/api/search/rebuild.mjs +12 -11
- package/dist/astro/routes/api/search/stats.mjs +7 -7
- package/dist/astro/routes/api/search/suggest.mjs +11 -10
- package/dist/astro/routes/api/sections/_slug_.mjs +32 -29
- package/dist/astro/routes/api/sections/index.mjs +32 -29
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +32 -29
- package/dist/astro/routes/api/setup/admin-verify.mjs +14 -13
- package/dist/astro/routes/api/setup/admin.mjs +12 -11
- package/dist/astro/routes/api/setup/dev-bypass.mjs +32 -26
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +30 -26
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +9 -8
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +35 -32
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +35 -32
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +35 -32
- package/dist/astro/routes/api/taxonomies/index.mjs +35 -32
- package/dist/astro/routes/api/themes/preview.mjs +6 -6
- package/dist/astro/routes/api/typegen.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +3 -3
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +3 -3
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +3 -3
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +31 -28
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +32 -29
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +32 -29
- package/dist/astro/routes/api/widget-areas/_name_.mjs +29 -27
- package/dist/astro/routes/api/widget-areas/index.mjs +32 -29
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +9 -8
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +24 -15
- package/dist/astro/routes/sitemap.xml.mjs +10 -9
- package/dist/astro/session-driver.d.mts +1 -1
- package/dist/astro/session-driver.mjs +3 -3
- package/dist/astro/types.d.mts +21 -13
- package/dist/{audit-D1cbG7xH.mjs → audit-ByVKIwWF.mjs} +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{auth-control-guard-Dr14I98m.mjs → auth-control-guard-CqeUmQsv.mjs} +1 -1
- package/dist/{authorize-BLO0m7fW.mjs → authorize-CcBxcHtz.mjs} +1 -1
- package/dist/{briefing-CObXGOGz.mjs → briefing-BJaJI8dm.mjs} +2 -1
- package/dist/{briefing-gKWiGLt1.mjs → briefing-CawZ59CY.mjs} +90 -28
- package/dist/bundle-7C_DpkEq.mjs +602 -0
- package/dist/{byline-1ntDwv8c.mjs → byline-e51-fWRs.mjs} +30 -62
- package/dist/{byline-fields-BX7rWKNU.d.mts → byline-fields-CUvbslGc.d.mts} +49 -6
- package/dist/{byline-fields-C9_iaPaM.mjs → byline-fields-D-N5qVIG.mjs} +1 -1
- package/dist/{byline-fields-B-ZkoVft.mjs → byline-fields-DOJO16Gm.mjs} +3 -3
- package/dist/{byline-registry-CUSvkasJ.mjs → byline-registry-87nNnYl4.mjs} +2 -2
- package/dist/{bylines-bC15ZqAv.mjs → bylines-CMQNCNFh.mjs} +9 -3
- package/dist/{bylines-DtxChhN_.mjs → bylines-y4LO-dTj.mjs} +5 -5
- package/dist/{cache-DbKyr60Q.mjs → cache-C-FNTFYK.mjs} +2 -2
- package/dist/{chunks-C5zp2xir.mjs → chunks-CxBt-GxR.mjs} +1 -1
- package/dist/cli/index.mjs +820 -1276
- package/dist/client/external-auth-headers.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -1
- package/dist/{comment-CXDOQ93N.mjs → comment-DlA4iChg.mjs} +13 -3
- package/dist/comment-reaction-DfFao1ST.mjs +58 -0
- package/dist/{comments-hK-rdzJ9.mjs → comments-DApFGMsP.mjs} +1 -1
- package/dist/{content-QaryevFO.mjs → content-Bh4BDjv8.mjs} +51 -34
- package/dist/content-refresh-CgutSUfO.mjs +1576 -0
- package/dist/{context-CoYb_fvK.mjs → context-C5W3G18y.mjs} +111 -50
- package/dist/{context-DBL1ib4v.mjs → context-CK4ym2Sf.mjs} +2 -2
- package/dist/{context-route-helpers-CYQVmbIm.mjs → context-route-helpers-MASyGgWf.mjs} +2 -1
- package/dist/{cron-DxCAU_Sn.mjs → cron-cKcNTix_.mjs} +7 -3
- package/dist/{dashboard-DNlqsknJ.mjs → dashboard-DQkVT7Ei.mjs} +4 -4
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{device-flow-DygsFPGL.mjs → device-flow-BnqRH-T9.mjs} +5 -5
- package/dist/{placeholder-BDo81Is5.mjs → enrich-DIFIFA8O.mjs} +36 -9
- package/dist/{error-DCSuUAZW.mjs → error-CytFlgcd.mjs} +5 -0
- package/dist/field-defs-cache-DRNdhBJv.mjs +49 -0
- package/dist/{fts-manager-CIZlScg5.mjs → fts-manager-Crzypjqu.mjs} +1 -1
- package/dist/hitl-Hw1RmWOq.mjs +677 -0
- package/dist/{hitl-requests-CGQwOEiq.mjs → hitl-requests-B4Vbcmaq.mjs} +2 -2
- package/dist/{hitl-route-helpers-GSDLNPpN.mjs → hitl-route-helpers-hEVzvP8l.mjs} +5 -4
- package/dist/import-Bh9biJfx.mjs +1354 -0
- package/dist/{index-CwRuZ-u8.d.mts → index-D4y6NGiy.d.mts} +75 -3
- package/dist/index.d.mts +17 -16
- package/dist/index.mjs +81 -69
- package/dist/{jsonld-DZ67souc.d.mts → jsonld-Dq0gsnOH.d.mts} +8 -4
- package/dist/{load-xE01Jm0V.mjs → load-DmYeniXl.mjs} +1 -1
- package/dist/{loader-tg63WSY2.mjs → loader-CjWqjweO.mjs} +117 -31
- package/dist/{manifest-schema-qh2P7RDX.mjs → manifest-schema-BxVPYMJR.mjs} +34 -1
- package/dist/mcp-reviewed-executor-TwVbMzG6.mjs +555 -0
- package/dist/media/image-endpoint.d.mts +32 -0
- package/dist/media/image-endpoint.mjs +111 -0
- package/dist/media/index.d.mts +27 -2
- package/dist/media/index.mjs +5 -3
- package/dist/media/local-runtime.d.mts +16 -11
- package/dist/media/local-runtime.mjs +10 -8
- package/dist/{media-DUAjMfvR.mjs → media-DMj0grzC.mjs} +3 -1
- package/dist/{media-allowlist-BsXkT9RK.mjs → media-allowlist-BlVCSzHx.mjs} +1 -1
- package/dist/media-url-lEKSrpr9.mjs +20 -0
- package/dist/{menus-WpFRMXjz.mjs → menus-TaW1gKnk.mjs} +19 -5
- package/dist/{menus-DU1eZqw_.mjs → menus-xvsnVZXw.mjs} +80 -15
- package/dist/{normalize-DUnoXAa9.mjs → normalize-5ek_z7de.mjs} +8 -1
- package/dist/{oauth-authorization-D-1p52Je.mjs → oauth-authorization-DiaGvRVz.mjs} +5 -5
- package/dist/{oauth-clients-CzeMGyUU.mjs → oauth-clients-Dl9E8dEM.mjs} +2 -2
- package/dist/object-cache/memory.d.mts +14 -0
- package/dist/object-cache/memory.mjs +55 -0
- package/dist/object-cache-D51qOseS.mjs +419 -0
- package/dist/page/index.d.mts +4 -3
- package/dist/page/index.mjs +2 -2
- package/dist/{parse-CneSgl7-.mjs → parse-CbymENaR.mjs} +1 -1
- package/dist/{parser-DxfsARXL.mjs → parser-C1nYd-81.mjs} +151 -9
- package/dist/{placeholder-CXfoCtCd.d.mts → placeholder-uvNRZBX9.d.mts} +12 -4
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +11 -2
- package/dist/{plugins-C57YQi4l.mjs → plugins-BolVa-tl.mjs} +832 -64
- package/dist/{transport-LSdZVXHF.mjs → portable-text-dA_UlJwW.mjs} +1 -134
- package/dist/{preview-D5rvDhRt.mjs → preview-vSVg6fwG.mjs} +4 -3
- package/dist/process-cache-7xgAO9JY.mjs +106 -0
- package/dist/{provider-loader-9it4vd6L.d.mts → provider-loader-MD8zAbnH.d.mts} +1 -1
- package/dist/{public-url-DLUM5OOv.mjs → public-url-BEz_sM9n.mjs} +24 -18
- package/dist/{public-url-B5HaoGzj.d.mts → public-url-gsp3oUbu.d.mts} +5 -4
- package/dist/{query-BYc3oruA.mjs → query-CfRCdaL3.mjs} +281 -76
- package/dist/{query-DKEwzobh.mjs → query-jaAmw8E1.mjs} +11 -6
- package/dist/{rate-limit-i0G3IAOX.mjs → rate-limit-ZLdyWoZF.mjs} +2 -2
- package/dist/{redirect-C2zXRj3c.mjs → redirect-BAjt4Z6b.mjs} +2 -2
- package/dist/{redirects-YZo_znWG.mjs → redirects-BTdwpQtE.mjs} +47 -24
- package/dist/{redirects-ocRJmQHP.mjs → redirects-CA-xylPG.mjs} +19 -11
- package/dist/{registry-CTkbIDB9.mjs → registry-CtyVedcN.mjs} +130 -77
- package/dist/request-context.d.mts +2 -0
- package/dist/{request-meta-DAb6xaP9.mjs → request-meta-CUypToDJ.mjs} +1 -1
- package/dist/{review-requests-C0un5kdV.mjs → review-requests-C9Ff11Vq.mjs} +1 -1
- package/dist/{review-requests-gGMyYYhy.mjs → review-requests-DUyEmtmG.mjs} +1 -1
- package/dist/{runner-D4FL9usy.d.mts → runner-1tC2tIRV.d.mts} +1 -1
- package/dist/runner-DB7CRfOF.mjs +6740 -0
- package/dist/{runtime-DEWQpZ-4.d.mts → runtime-BOeiQsCv.d.mts} +2028 -1564
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +4 -4
- package/dist/schema-DXdYj4R3.mjs +8 -0
- package/dist/{search-axGQ1KY3.mjs → search-DSaLgEFI.mjs} +5 -3
- package/dist/{secrets-062Izo8i.mjs → secrets-CDZo2k8L.mjs} +15 -12
- package/dist/{sections-OLKgXL10.mjs → sections-PVVXHGfp.mjs} +2 -2
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +22 -19
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +3 -12
- package/dist/{seo-Cd45Po8L.mjs → seo-CQG3GCHQ.mjs} +5 -2
- package/dist/{seo-contributions-CqVvReEj.mjs → seo-contributions-C1EnikHB.mjs} +11 -3
- package/dist/{seo-5lJOtB5J.mjs → seo-qa7Dn0jK.mjs} +22 -4
- package/dist/{service-Bwad8s5h.mjs → service-DiN4ZuPp.mjs} +2 -2
- package/dist/session-user-CAKImna3.mjs +22 -0
- package/dist/{settings-yLjz_I4T.mjs → settings-BbqgHWpU.mjs} +28 -55
- package/dist/{settings-lAHjaUit.mjs → settings-i4VPP8bT.mjs} +2 -2
- package/dist/{setup-complete--PradA4R.mjs → setup-complete-B8fl-mjV.mjs} +1 -1
- package/dist/{site-activity-D7Xyropw.mjs → site-activity-Dy3FROs0.mjs} +1 -1
- package/dist/{site-context-DaN9Z91j.mjs → site-context-DQiL8Fl5.mjs} +23 -652
- package/dist/{site-url-DpGZo9Z6.mjs → site-url-BML3GQYm.mjs} +1 -1
- package/dist/status-DANd8ERo.mjs +21 -0
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-uXVEoQso.mjs → taxonomies-9s-v03H-.mjs} +36 -29
- package/dist/taxonomies-DRiixp_E.mjs +443 -0
- package/dist/{taxonomy-CBmkmMuU.mjs → taxonomy-Dvsw6BOs.mjs} +44 -9
- package/dist/{tokens-CjSgb0AC.mjs → tokens-1RK1n6ED.mjs} +1 -1
- package/dist/transport-BNmCqLu7.mjs +134 -0
- package/dist/types-4garmdWa.mjs +66 -0
- package/dist/{types-DSPGWjxO.d.mts → types-C8XW6L19.d.mts} +225 -4
- package/dist/{types-GaCtZ1k9.d.mts → types-CDpH6MA4.d.mts} +18 -0
- package/dist/{types-DRIKLmJI.d.mts → types-C_ly5eTp.d.mts} +17 -2
- package/dist/types-DF7ZuGGk.d.mts +118 -0
- package/dist/types-DU0bU2Wu.d.mts +2392 -0
- package/dist/{types-CwawBUTa.d.mts → types-DW_-sFyz.d.mts} +2 -1
- package/dist/{types-DfuD0Afl.mjs → types-Dq1qT19x.mjs} +2 -2
- package/dist/ui/server-runtime.d.mts +15 -15
- package/dist/ui/server-runtime.mjs +25 -21
- package/dist/url-Cac7a9QZ.mjs +33 -0
- package/dist/{user-DtNmjNYt.mjs → user-BIZPzCb9.mjs} +2 -2
- package/dist/{utils-wv-zvC-Y.mjs → utils-CMDhr5Xd.mjs} +129 -7
- package/dist/{validate-BMs11tan.d.mts → validate-CZvb-BKB.d.mts} +10 -7
- package/dist/{validate-7SYRbz2f.mjs → validate-DNs0pTSc.mjs} +11 -6
- package/dist/version-BTvfRGc6.mjs +6 -0
- package/dist/{widgets-DR8EHuRO.mjs → widgets-f0ibZUIq.mjs} +46 -43
- package/dist/{wordpress-slugs-Dvhxh4cl.mjs → wordpress-slugs-BhHJX8XE.mjs} +1 -1
- package/dist/{zod-generator-BS9-umbk.mjs → zod-generator-B7TMjyFJ.mjs} +41 -13
- package/package.json +31 -8
- package/src/components/Block.astro +50 -0
- package/src/components/Comments.astro +143 -1
- package/src/components/DinewayHead.astro +19 -2
- package/src/components/DinewayImage.astro +66 -33
- package/src/components/DinewayMedia.astro +1 -2
- package/src/components/Image.astro +85 -40
- package/src/components/InlinePortableTextEditor.tsx +12 -2
- package/src/components/JsonLdScript.astro +9 -0
- package/src/components/LiveSearch.astro +40 -4
- package/src/components/index.ts +4 -0
- package/src/components/live-search-routing.ts +30 -0
- package/src/components/portable-text-text-align.ts +15 -0
- package/src/ui.ts +2 -0
- package/dist/entity-aliases-Egjmqc7I.mjs +0 -51
- package/dist/import-CvHY6sf5.mjs +0 -684
- package/dist/runner-B-tbV751.mjs +0 -2741
- package/dist/schema-B6zyB-Xn.mjs +0 -8
- package/dist/taxonomies-CNOGeC-3.mjs +0 -327
- package/dist/types-eu5UDgHN.d.mts +0 -652
- package/dist/version-xlzMSXF2.mjs +0 -6
- /package/dist/{adapters-OQCImOL7.d.mts → adapters-NVSs4UH7.d.mts} +0 -0
- /package/dist/{api-tokens-CYUoFGo7.mjs → api-tokens-2r7OWi_I.mjs} +0 -0
- /package/dist/{base64-C1Q9yr0B.mjs → base64-F8-DUraK.mjs} +0 -0
- /package/dist/{challenge-store-CRSz-Xim.mjs → challenge-store-Bjlepa2E.mjs} +0 -0
- /package/dist/{components--pg6zBvu.mjs → components-Bclf98Be.mjs} +0 -0
- /package/dist/{context-types-DQh_XKkn.mjs → context-types-DqTo6C79.mjs} +0 -0
- /package/dist/{default-B6iaLvP7.mjs → default--K40jqm0.mjs} +0 -0
- /package/dist/{email-console-BOVqIYpB.mjs → email-console-ChgKFO8-.mjs} +0 -0
- /package/dist/{escape-BDgouFFY.mjs → escape-Bd4P_xKg.mjs} +0 -0
- /package/dist/{experimental-workflows-Dh4DUoD_.mjs → experimental-workflows-BMsUnIAw.mjs} +0 -0
- /package/dist/{mime-8pAB8-77.mjs → mime-CA7Lgovc.mjs} +0 -0
- /package/dist/{mode-CpyEz8pe.mjs → mode-C4CIyjtR.mjs} +0 -0
- /package/dist/{oauth-state-store-qncPB_IU.mjs → oauth-state-store-CNoXafF7.mjs} +0 -0
- /package/dist/{oauth-user-lookup-BFjWF8wK.mjs → oauth-user-lookup-Ygw4bNyt.mjs} +0 -0
- /package/dist/{options-BvJoSSJR.mjs → options-DQPTH5n0.mjs} +0 -0
- /package/dist/{passkey-config-DzHQK90v.mjs → passkey-config-BUwu8BA3.mjs} +0 -0
- /package/dist/{patterns-r-0n824Y.mjs → patterns-BhAvqO0S.mjs} +0 -0
- /package/dist/{provider-loader-DzUxejk7.mjs → provider-loader-D0w9dqjm.mjs} +0 -0
- /package/dist/{redirect-BjIBtDWY.mjs → redirect-wphRBv4y.mjs} +0 -0
- /package/dist/{request-cache-CYMbwZc6.mjs → request-cache-D9GQBxMP.mjs} +0 -0
- /package/dist/{resolve-DTuDyYKs.mjs → resolve-Cb6Dbz-C.mjs} +0 -0
- /package/dist/{setup-nonce-pyGfc9hV.mjs → setup-nonce-CoyjILvW.mjs} +0 -0
- /package/dist/{sidecar-client-mJxwayYN.mjs → sidecar-client-C3mqXg4C.mjs} +0 -0
- /package/dist/{slugify-BzGxlOFx.mjs → slugify-KSsVD_D3.mjs} +0 -0
- /package/dist/{ssrf-DiKqWd3N.mjs → ssrf-DWe8jwAp.mjs} +0 -0
- /package/dist/{transaction-CShMWIej.mjs → transaction-C2EOMTJA.mjs} +0 -0
- /package/dist/{transport-BnsNxh2l.d.mts → transport-CcJu3Co8.d.mts} +0 -0
- /package/dist/{trusted-proxy-BgIIcbsN.mjs → trusted-proxy-DTXQ2IMv.mjs} +0 -0
- /package/dist/{types-BO8TGjS8.mjs → types-B3t5cxhU.mjs} +0 -0
- /package/dist/{types-nP9vGUFq.d.mts → types-C46EvNdc.d.mts} +0 -0
- /package/dist/{types-Cg276-3z.mjs → types-C5CVgwsV.mjs} +0 -0
- /package/dist/{types-DZpT-PnG.d.mts → types-POH8PQZh.d.mts} +0 -0
- /package/dist/{types-Dryvv7LH.d.mts → types-Rc3gQqqw.d.mts} +0 -0
- /package/dist/{url-w7Mp5qAD.mjs → url-CpyFDFpO.mjs} +0 -0
|
@@ -1,14 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
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: {
|
|
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/
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
...
|
|
2641
|
+
...baseContext,
|
|
2066
2642
|
input: validatedInput,
|
|
2067
|
-
|
|
2068
|
-
|
|
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 {
|
|
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
|
-
{
|
|
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
|
-
|
|
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 };
|