dineway 0.1.8 → 0.1.11
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/LICENSE +9 -0
- package/README.md +63 -17
- package/dist/activity-events-BsMaXdJa.mjs +540 -0
- package/dist/allowed-origins-DG86sH8U.mjs +68 -0
- package/dist/api/route-utils.d.mts +41 -0
- package/dist/api/route-utils.mjs +26 -0
- package/dist/api/schemas/index.d.mts +3 -0
- package/dist/api/schemas/index.mjs +6 -0
- package/dist/api/schemas/setup.d.mts +42 -0
- package/dist/api/schemas/setup.mjs +39 -0
- package/dist/api-Cmy8Rjk5.mjs +2704 -0
- package/dist/api-tokens-Bu3ez1MO.mjs +153 -0
- package/dist/api-tokens-DzloJxuh.mjs +3 -0
- package/dist/{apply-iVSqz2qs.mjs → apply-Co5imxxT.mjs} +15 -689
- package/dist/astro/index.d.mts +10 -6
- package/dist/astro/index.mjs +86 -11
- package/dist/astro/middleware/auth.d.mts +10 -7
- package/dist/astro/middleware/auth.mjs +19 -104
- package/dist/astro/middleware/redirect.mjs +24 -14
- package/dist/astro/middleware/request-context.mjs +9 -6
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.mjs +86 -145
- package/dist/astro/routes/PluginRegistry.d.mts +14 -0
- package/dist/astro/routes/PluginRegistry.mjs +24 -0
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.d.mts +14 -0
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +65 -0
- package/dist/astro/routes/api/admin/allowed-domains/index.d.mts +14 -0
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +65 -0
- package/dist/astro/routes/api/admin/api-tokens/_id_.d.mts +10 -0
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +33 -0
- package/dist/astro/routes/api/admin/api-tokens/index.d.mts +16 -0
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +59 -0
- package/dist/astro/routes/api/admin/briefing.d.mts +7 -0
- package/dist/astro/routes/api/admin/briefing.mjs +71 -0
- package/dist/astro/routes/api/admin/bylines/_id_/index.d.mts +9 -0
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +74 -0
- package/dist/astro/routes/api/admin/bylines/index.d.mts +8 -0
- package/dist/astro/routes/api/admin/bylines/index.mjs +61 -0
- package/dist/astro/routes/api/admin/comments/_id_/status.d.mts +7 -0
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +80 -0
- package/dist/astro/routes/api/admin/comments/_id_.d.mts +14 -0
- package/dist/astro/routes/api/admin/comments/_id_.mjs +46 -0
- package/dist/astro/routes/api/admin/comments/bulk.d.mts +7 -0
- package/dist/astro/routes/api/admin/comments/bulk.mjs +36 -0
- package/dist/astro/routes/api/admin/comments/counts.d.mts +7 -0
- package/dist/astro/routes/api/admin/comments/counts.mjs +24 -0
- package/dist/astro/routes/api/admin/comments/index.d.mts +10 -0
- package/dist/astro/routes/api/admin/comments/index.mjs +40 -0
- package/dist/astro/routes/api/admin/context/_id_/history.d.mts +7 -0
- package/dist/astro/routes/api/admin/context/_id_/history.mjs +45 -0
- package/dist/astro/routes/api/admin/context/_id_/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/context/_id_/index.mjs +45 -0
- package/dist/astro/routes/api/admin/context/_id_/review.d.mts +7 -0
- package/dist/astro/routes/api/admin/context/_id_/review.mjs +60 -0
- package/dist/astro/routes/api/admin/context/_id_/supersede.d.mts +7 -0
- package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +63 -0
- package/dist/astro/routes/api/admin/context/diff.d.mts +7 -0
- package/dist/astro/routes/api/admin/context/diff.mjs +49 -0
- package/dist/astro/routes/api/admin/context/index.d.mts +8 -0
- package/dist/astro/routes/api/admin/context/index.mjs +71 -0
- package/dist/astro/routes/api/admin/context/stale.d.mts +7 -0
- package/dist/astro/routes/api/admin/context/stale.mjs +49 -0
- package/dist/astro/routes/api/admin/hitl-requests/_id_/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +51 -0
- package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.d.mts +7 -0
- package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +67 -0
- package/dist/astro/routes/api/admin/hitl-requests/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/hitl-requests/index.mjs +55 -0
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.d.mts +7 -0
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +98 -0
- package/dist/astro/routes/api/admin/hooks/exclusive/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +33 -0
- package/dist/astro/routes/api/admin/oauth-clients/_id_.d.mts +18 -0
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +79 -0
- package/dist/astro/routes/api/admin/oauth-clients/index.d.mts +14 -0
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +58 -0
- package/dist/astro/routes/api/admin/plugins/_id_/disable.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +89 -0
- package/dist/astro/routes/api/admin/plugins/_id_/enable.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +89 -0
- package/dist/astro/routes/api/admin/plugins/_id_/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +54 -0
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +98 -0
- package/dist/astro/routes/api/admin/plugins/_id_/update.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +131 -0
- package/dist/astro/routes/api/admin/plugins/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/index.mjs +52 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +36 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +54 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +128 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +61 -0
- package/dist/astro/routes/api/admin/plugins/updates.d.mts +7 -0
- package/dist/astro/routes/api/admin/plugins/updates.mjs +52 -0
- package/dist/astro/routes/api/admin/review-requests/_id_/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +26 -0
- package/dist/astro/routes/api/admin/review-requests/_id_/resolve.d.mts +7 -0
- package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +97 -0
- package/dist/astro/routes/api/admin/review-requests/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/review-requests/index.mjs +31 -0
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +54 -0
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.d.mts +7 -0
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +36 -0
- package/dist/astro/routes/api/admin/themes/marketplace/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +70 -0
- package/dist/astro/routes/api/admin/users/_id_/disable.d.mts +7 -0
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +38 -0
- package/dist/astro/routes/api/admin/users/_id_/enable.d.mts +7 -0
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +29 -0
- package/dist/astro/routes/api/admin/users/_id_/index.d.mts +8 -0
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +104 -0
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.d.mts +7 -0
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +43 -0
- package/dist/astro/routes/api/admin/users/index.d.mts +7 -0
- package/dist/astro/routes/api/admin/users/index.mjs +54 -0
- package/dist/astro/routes/api/auth/dev-bypass.d.mts +8 -0
- package/dist/astro/routes/api/auth/dev-bypass.mjs +81 -0
- package/dist/astro/routes/api/auth/invite/accept.d.mts +7 -0
- package/dist/astro/routes/api/auth/invite/accept.mjs +31 -0
- package/dist/astro/routes/api/auth/invite/complete.d.mts +7 -0
- package/dist/astro/routes/api/auth/invite/complete.mjs +54 -0
- package/dist/astro/routes/api/auth/invite/index.d.mts +7 -0
- package/dist/astro/routes/api/auth/invite/index.mjs +51 -0
- package/dist/astro/routes/api/auth/invite/register-options.d.mts +7 -0
- package/dist/astro/routes/api/auth/invite/register-options.mjs +44 -0
- package/dist/astro/routes/api/auth/logout.d.mts +7 -0
- package/dist/astro/routes/api/auth/logout.mjs +24 -0
- package/dist/astro/routes/api/auth/magic-link/send.d.mts +7 -0
- package/dist/astro/routes/api/auth/magic-link/send.mjs +48 -0
- package/dist/astro/routes/api/auth/magic-link/verify.d.mts +7 -0
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +32 -0
- package/dist/astro/routes/api/auth/me.d.mts +13 -0
- package/dist/astro/routes/api/auth/me.mjs +41 -0
- package/dist/astro/routes/api/auth/mode.d.mts +7 -0
- package/dist/astro/routes/api/auth/mode.mjs +28 -0
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.d.mts +7 -0
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +114 -0
- package/dist/astro/routes/api/auth/oauth/_provider_.d.mts +7 -0
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +58 -0
- package/dist/astro/routes/api/auth/passkey/_id_.d.mts +14 -0
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +62 -0
- package/dist/astro/routes/api/auth/passkey/index.d.mts +7 -0
- package/dist/astro/routes/api/auth/passkey/index.mjs +25 -0
- package/dist/astro/routes/api/auth/passkey/options.d.mts +7 -0
- package/dist/astro/routes/api/auth/passkey/options.mjs +46 -0
- package/dist/astro/routes/api/auth/passkey/register/options.d.mts +7 -0
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +44 -0
- package/dist/astro/routes/api/auth/passkey/register/verify.d.mts +7 -0
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +59 -0
- package/dist/astro/routes/api/auth/passkey/verify.d.mts +7 -0
- package/dist/astro/routes/api/auth/passkey/verify.mjs +47 -0
- package/dist/astro/routes/api/auth/signup/complete.d.mts +7 -0
- package/dist/astro/routes/api/auth/signup/complete.mjs +55 -0
- package/dist/astro/routes/api/auth/signup/request.d.mts +7 -0
- package/dist/astro/routes/api/auth/signup/request.mjs +44 -0
- package/dist/astro/routes/api/auth/signup/verify.d.mts +7 -0
- package/dist/astro/routes/api/auth/signup/verify.mjs +32 -0
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.d.mts +14 -0
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +193 -0
- package/dist/astro/routes/api/content/_collection_/_id_/compare.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +17 -0
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +36 -0
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +39 -0
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +31 -0
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +78 -0
- package/dist/astro/routes/api/content/_collection_/_id_/publish.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +92 -0
- package/dist/astro/routes/api/content/_collection_/_id_/restore.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +36 -0
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +19 -0
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.d.mts +8 -0
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +75 -0
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts +14 -0
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +85 -0
- package/dist/astro/routes/api/content/_collection_/_id_/translations.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +40 -0
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +36 -0
- package/dist/astro/routes/api/content/_collection_/_id_.d.mts +9 -0
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +114 -0
- package/dist/astro/routes/api/content/_collection_/index.d.mts +8 -0
- package/dist/astro/routes/api/content/_collection_/index.mjs +74 -0
- package/dist/astro/routes/api/content/_collection_/trash.d.mts +7 -0
- package/dist/astro/routes/api/content/_collection_/trash.mjs +23 -0
- package/dist/astro/routes/api/dashboard.d.mts +7 -0
- package/dist/astro/routes/api/dashboard.mjs +26 -0
- package/dist/astro/routes/api/dev/emails.d.mts +8 -0
- package/dist/astro/routes/api/dev/emails.mjs +17 -0
- package/dist/astro/routes/api/health.d.mts +7 -0
- package/dist/astro/routes/api/health.mjs +34 -0
- package/dist/astro/routes/api/import/probe.d.mts +17 -0
- package/dist/astro/routes/api/import/probe.mjs +33 -0
- package/dist/astro/routes/api/import/wordpress/analyze.d.mts +87 -0
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +305 -0
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +37 -0
- package/dist/astro/routes/api/import/wordpress/execute.mjs +197 -0
- package/dist/astro/routes/api/import/wordpress/media.d.mts +35 -0
- package/dist/astro/routes/api/import/wordpress/media.mjs +222 -0
- package/dist/astro/routes/api/import/wordpress/prepare.d.mts +19 -0
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +155 -0
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +21 -0
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +289 -0
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +15 -0
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +69 -0
- package/dist/astro/routes/api/import/wordpress-plugin/callback.d.mts +7 -0
- package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +28 -0
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +19 -0
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +268 -0
- package/dist/astro/routes/api/manifest.d.mts +7 -0
- package/dist/astro/routes/api/manifest.mjs +50 -0
- package/dist/astro/routes/api/mcp.d.mts +15 -0
- package/dist/astro/routes/api/mcp.mjs +2700 -0
- package/dist/astro/routes/api/media/_id_/confirm.d.mts +10 -0
- package/dist/astro/routes/api/media/_id_/confirm.mjs +59 -0
- package/dist/astro/routes/api/media/_id_.d.mts +22 -0
- package/dist/astro/routes/api/media/_id_.mjs +81 -0
- package/dist/astro/routes/api/media/file/_...key_.d.mts +7 -0
- package/dist/astro/routes/api/media/file/_...key_.mjs +49 -0
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.d.mts +14 -0
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +49 -0
- package/dist/astro/routes/api/media/providers/_providerId_/index.d.mts +14 -0
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +72 -0
- package/dist/astro/routes/api/media/providers/index.d.mts +10 -0
- package/dist/astro/routes/api/media/providers/index.mjs +18 -0
- package/dist/astro/routes/api/media/upload-url.d.mts +10 -0
- package/dist/astro/routes/api/media/upload-url.mjs +82 -0
- package/dist/astro/routes/api/media.d.mts +16 -0
- package/dist/astro/routes/api/media.mjs +137 -0
- package/dist/astro/routes/api/menus/_name_/items.d.mts +9 -0
- package/{src/astro/routes/api/menus/[name]/items.ts → dist/astro/routes/api/menus/_name_/items.mjs} +63 -105
- package/dist/astro/routes/api/menus/_name_/reorder.d.mts +7 -0
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +77 -0
- package/dist/astro/routes/api/menus/_name_.d.mts +9 -0
- package/dist/astro/routes/api/menus/_name_.mjs +123 -0
- package/dist/astro/routes/api/menus/index.d.mts +8 -0
- package/dist/astro/routes/api/menus/index.mjs +84 -0
- package/dist/astro/routes/api/oauth/authorize.d.mts +8 -0
- package/dist/astro/routes/api/oauth/authorize.mjs +265 -0
- package/dist/astro/routes/api/oauth/device/authorize.d.mts +7 -0
- package/dist/astro/routes/api/oauth/device/authorize.mjs +30 -0
- package/dist/astro/routes/api/oauth/device/code.d.mts +7 -0
- package/dist/astro/routes/api/oauth/device/code.mjs +34 -0
- package/dist/astro/routes/api/oauth/device/token.d.mts +7 -0
- package/dist/astro/routes/api/oauth/device/token.mjs +45 -0
- package/dist/astro/routes/api/oauth/register.d.mts +8 -0
- package/dist/astro/routes/api/oauth/register.mjs +115 -0
- package/dist/astro/routes/api/oauth/token/refresh.d.mts +7 -0
- package/dist/astro/routes/api/oauth/token/refresh.mjs +28 -0
- package/dist/astro/routes/api/oauth/token/revoke.d.mts +7 -0
- package/dist/astro/routes/api/oauth/token/revoke.mjs +25 -0
- package/dist/astro/routes/api/oauth/token.d.mts +8 -0
- package/dist/astro/routes/api/oauth/token.mjs +138 -0
- package/dist/astro/routes/api/openapi.json.d.mts +7 -0
- package/dist/astro/routes/api/openapi.json.mjs +2638 -0
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts +11 -0
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +77 -0
- package/dist/astro/routes/api/redirects/404s/index.d.mts +9 -0
- package/dist/astro/routes/api/redirects/404s/index.mjs +62 -0
- package/dist/astro/routes/api/redirects/404s/summary.d.mts +7 -0
- package/dist/astro/routes/api/redirects/404s/summary.mjs +34 -0
- package/dist/astro/routes/api/redirects/_id_.d.mts +9 -0
- package/dist/astro/routes/api/redirects/_id_.mjs +152 -0
- package/dist/astro/routes/api/redirects/index.d.mts +8 -0
- package/dist/astro/routes/api/redirects/index.mjs +97 -0
- package/dist/astro/routes/api/revisions/_revisionId_/index.d.mts +7 -0
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +16 -0
- package/dist/astro/routes/api/revisions/_revisionId_/restore.d.mts +7 -0
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +23 -0
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.d.mts +9 -0
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +98 -0
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.d.mts +8 -0
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +80 -0
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.d.mts +7 -0
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +67 -0
- package/dist/astro/routes/api/schema/collections/_slug_/index.d.mts +9 -0
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +97 -0
- package/dist/astro/routes/api/schema/collections/index.d.mts +8 -0
- package/dist/astro/routes/api/schema/collections/index.mjs +77 -0
- package/dist/astro/routes/api/schema/index.d.mts +7 -0
- package/dist/astro/routes/api/schema/index.mjs +79 -0
- package/dist/astro/routes/api/schema/orphans/_slug_.d.mts +7 -0
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +58 -0
- package/dist/astro/routes/api/schema/orphans/index.d.mts +7 -0
- package/dist/astro/routes/api/schema/orphans/index.mjs +53 -0
- package/dist/astro/routes/api/search/enable.d.mts +15 -0
- package/dist/astro/routes/api/search/enable.mjs +55 -0
- package/dist/astro/routes/api/search/index.d.mts +16 -0
- package/dist/astro/routes/api/search/index.mjs +52 -0
- package/dist/astro/routes/api/search/rebuild.d.mts +13 -0
- package/dist/astro/routes/api/search/rebuild.mjs +48 -0
- package/dist/astro/routes/api/search/stats.d.mts +10 -0
- package/dist/astro/routes/api/search/stats.mjs +28 -0
- package/dist/astro/routes/api/search/suggest.d.mts +15 -0
- package/dist/astro/routes/api/search/suggest.mjs +43 -0
- package/dist/astro/routes/api/sections/_slug_.d.mts +9 -0
- package/dist/astro/routes/api/sections/_slug_.mjs +156 -0
- package/dist/astro/routes/api/sections/index.d.mts +8 -0
- package/dist/astro/routes/api/sections/index.mjs +99 -0
- package/dist/astro/routes/api/settings/email.d.mts +17 -0
- package/dist/astro/routes/api/settings/email.mjs +102 -0
- package/dist/astro/routes/api/settings.d.mts +20 -0
- package/dist/astro/routes/api/settings.mjs +101 -0
- package/dist/astro/routes/api/setup/admin-verify.d.mts +7 -0
- package/dist/astro/routes/api/setup/admin-verify.mjs +67 -0
- package/dist/astro/routes/api/setup/admin.d.mts +7 -0
- package/dist/astro/routes/api/setup/admin.mjs +68 -0
- package/dist/astro/routes/api/setup/dev-bypass.d.mts +8 -0
- package/dist/astro/routes/api/setup/dev-bypass.mjs +137 -0
- package/dist/astro/routes/api/setup/dev-reset.d.mts +7 -0
- package/dist/astro/routes/api/setup/dev-reset.mjs +22 -0
- package/dist/astro/routes/api/setup/index.d.mts +7 -0
- package/dist/astro/routes/api/setup/index.mjs +93 -0
- package/dist/astro/routes/api/setup/status.d.mts +7 -0
- package/dist/astro/routes/api/setup/status.mjs +57 -0
- package/dist/astro/routes/api/snapshot.d.mts +7 -0
- package/dist/astro/routes/api/snapshot.mjs +227 -0
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts +18 -0
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +189 -0
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts +14 -0
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +113 -0
- package/dist/astro/routes/api/taxonomies/index.d.mts +14 -0
- package/dist/astro/routes/api/taxonomies/index.mjs +103 -0
- package/dist/astro/routes/api/themes/preview.d.mts +7 -0
- package/dist/astro/routes/api/themes/preview.mjs +47 -0
- package/dist/astro/routes/api/typegen.d.mts +17 -0
- package/dist/astro/routes/api/typegen.mjs +75 -0
- package/dist/astro/routes/api/well-known/auth.d.mts +7 -0
- package/dist/astro/routes/api/well-known/auth.mjs +42 -0
- package/dist/astro/routes/api/well-known/oauth-authorization-server.d.mts +7 -0
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +33 -0
- package/dist/astro/routes/api/well-known/oauth-protected-resource.d.mts +7 -0
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +21 -0
- package/dist/astro/routes/api/widget-areas/_name_/reorder.d.mts +7 -0
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +88 -0
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts +8 -0
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +158 -0
- package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts +7 -0
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +104 -0
- package/dist/astro/routes/api/widget-areas/_name_.d.mts +8 -0
- package/dist/astro/routes/api/widget-areas/_name_.mjs +99 -0
- package/dist/astro/routes/api/widget-areas/index.d.mts +8 -0
- package/dist/astro/routes/api/widget-areas/index.mjs +108 -0
- package/dist/astro/routes/api/widget-components.d.mts +7 -0
- package/dist/astro/routes/api/widget-components.mjs +15 -0
- package/dist/astro/routes/robots.txt.d.mts +7 -0
- package/dist/astro/routes/robots.txt.mjs +60 -0
- package/dist/astro/routes/sitemap-_collection_.xml.d.mts +7 -0
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +70 -0
- package/dist/astro/routes/sitemap.xml.d.mts +7 -0
- package/dist/astro/routes/sitemap.xml.mjs +63 -0
- package/dist/astro/types.d.mts +41 -9
- package/dist/auth/providers/github-admin.d.mts +9 -0
- package/dist/auth/providers/github-admin.mjs +27 -0
- package/dist/auth/providers/github.d.mts +12 -0
- package/dist/auth/providers/github.mjs +17 -0
- package/dist/auth/providers/google-admin.d.mts +9 -0
- package/dist/auth/providers/google-admin.mjs +43 -0
- package/dist/auth/providers/google.d.mts +12 -0
- package/dist/auth/providers/google.mjs +17 -0
- package/dist/auth-control-guard-DKUe_1oa.mjs +13 -0
- package/dist/authorize-BBj8C6Y8.mjs +36 -0
- package/dist/briefing-BrXCuMEE.mjs +1294 -0
- package/dist/briefing-ClWw4mc9.mjs +29 -0
- package/dist/{byline-OhH2dlRu.mjs → byline-naZxOPSa.mjs} +3 -3
- package/dist/{bylines-BGpD9_hy.mjs → bylines-BcOPh6Ej.mjs} +20 -53
- package/dist/bylines-HfUKum_j.d.mts +2023 -0
- package/dist/{cache-BdSY-gQN.mjs → cache-DEbQ13c9.mjs} +21 -11
- package/dist/challenge-store-DHMgBGOq.mjs +48 -0
- package/dist/cli/index.mjs +142 -22
- package/dist/client/external-auth-headers.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +3 -3
- package/dist/comment-DFO-gWDH.mjs +246 -0
- package/dist/comments-Gy3zLBaP.mjs +186 -0
- package/dist/components-DND2rd3D.mjs +107 -0
- package/dist/{content-DWi4d0rT.mjs → content-CyLkb-qH.mjs} +33 -44
- package/dist/context-bE5Kyvcj.mjs +184 -0
- package/dist/context-nxMyOe3p.mjs +849 -0
- package/dist/context-route-helpers-D-6uCQ0S.mjs +45 -0
- package/dist/context-types-C-LwdAxx.mjs +23 -0
- package/dist/cron-DGzVTtJp.mjs +263 -0
- package/dist/dashboard-DqnYU8EU.mjs +120 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/libsql.mjs +3 -3
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.mjs +1 -2
- package/dist/device-flow-7AhWNwCK.mjs +487 -0
- package/dist/email-console-CgLVZbcn.mjs +36 -0
- package/dist/entity-aliases-C0v-yNET.mjs +51 -0
- package/dist/error-DEGjx2Xw.mjs +435 -0
- package/dist/escape-mNZr4t2A.mjs +8 -0
- package/dist/experimental-workflows-DldxJlqV.mjs +38 -0
- package/dist/fts-manager-B1pTNEG_.mjs +297 -0
- package/dist/hash-CDX7M0ze.mjs +32 -0
- package/dist/hitl-requests-Bx3Bkk9l.mjs +118 -0
- package/dist/hitl-route-helpers-DMmJRS7B.mjs +96 -0
- package/dist/import-DD3f2jkc.mjs +243 -0
- package/dist/import-DVZcYlDp.mjs +1323 -0
- package/dist/index-CkljPf5F.d.mts +227 -0
- package/dist/index.d.mts +15 -11
- package/dist/index.mjs +60 -22
- package/dist/{loader-sMG4TZ-u.mjs → loader-PZnPxFLc.mjs} +42 -5
- package/dist/{manifest-schema-D1MSVnoI.mjs → manifest-schema-DYoCQ5np.mjs} +22 -10
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +2 -1
- package/dist/media/local-runtime.d.mts +11 -7
- package/dist/media/local-runtime.mjs +3 -3
- package/dist/{media-DMTr80Gv.mjs → media-_7Fxdu45.mjs} +1 -1
- package/dist/menus-BacxVCCo.mjs +312 -0
- package/dist/menus-CrzHokKj.mjs +3502 -0
- package/dist/normalize-C49G_o1k.mjs +126 -0
- package/dist/oauth-authorization-C1qiw4hd.mjs +283 -0
- package/dist/oauth-clients-CvWatf5p.mjs +298 -0
- package/dist/oauth-state-store-hSdzxsEe.mjs +48 -0
- package/dist/oauth-user-lookup-B4OcmsLV.mjs +25 -0
- package/dist/options-z8VVg1Ll.mjs +114 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/parse-BeQXIt1U.mjs +88 -0
- package/dist/passkey-config-Daqs5fjq.mjs +42 -0
- package/dist/{patterns-CrCYkMBb.mjs → patterns-K0DLqWir.mjs} +53 -1
- package/dist/{placeholder-Cp8g5Emj.mjs → placeholder-C2P5fKa4.mjs} +1 -126
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -5
- package/dist/plugins/adapt-sandbox-entry.mjs +4 -4
- package/dist/preview-C_4DyVox.mjs +788 -0
- package/dist/public-url-BB_umF5G.mjs +71 -0
- package/dist/{query-kDmwCsHh.mjs → query-RiobVwB5.mjs} +93 -19
- package/dist/rate-limit-CbJoj_fT.mjs +112 -0
- package/dist/{redirect-DnEWAkVg.mjs → redirect-CGl64yOX.mjs} +9 -5
- package/dist/redirect-ClSmMOtC.mjs +16 -0
- package/dist/redirects-B69T59hK.mjs +499 -0
- package/dist/redirects-CqaxraTO.mjs +1070 -0
- package/dist/{registry-C0zjeB9P.mjs → registry-C-_hxLqa.mjs} +26 -294
- package/dist/request-meta-Bd0mQfiS.mjs +130 -0
- package/dist/review-requests-C2DIHwlJ.mjs +148 -0
- package/dist/review-requests-DIyjw-K_.mjs +79 -0
- package/dist/{runner-CFI6B6J2.d.mts → runner-9eIQXuc2.d.mts} +1 -1
- package/dist/{index-yvc6E_17.d.mts → runtime-C4-7y7xK.d.mts} +1539 -2007
- package/dist/runtime.d.mts +10 -6
- package/dist/runtime.mjs +3 -3
- package/dist/schema-BNpI53of.mjs +40 -0
- package/dist/search-DM6CVti3.mjs +337 -0
- package/dist/secrets-dI8zzTV7.mjs +160 -0
- package/dist/sections-DZFyAQXd.mjs +338 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +18 -13
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo-BBgTCOYU.mjs +85 -0
- package/dist/seo-CUQctrog.mjs +129 -0
- package/dist/service-CSfcQguB.mjs +194 -0
- package/dist/settings-4XnpVMOS.mjs +223 -0
- package/dist/settings-Bw93cLfe.mjs +50 -0
- package/dist/setup-complete-DidsDQ1e.mjs +21 -0
- package/dist/setup-nonce-pml1PMKo.mjs +17 -0
- package/dist/sidecar-client-vzwV98K4.mjs +66 -0
- package/dist/site-activity-B8FjLIVh.mjs +104 -0
- package/dist/site-context-Bpu_Paur.mjs +4122 -0
- package/dist/site-url-CYIcO0Tj.mjs +12 -0
- package/dist/slugify-PDTDtMXp.mjs +30 -0
- package/dist/ssrf-CmM76lLV.mjs +248 -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 +2 -2
- package/dist/{taxonomies-1s5PaS_8.mjs → taxonomies-BvBgfzn3.mjs} +11 -7
- package/dist/taxonomies-CpqGcIJD.mjs +355 -0
- package/dist/taxonomy-D5cbhc8u.mjs +165 -0
- package/dist/{tokens-CJz9ubV6.mjs → tokens-DLTo4dO2.mjs} +1 -1
- package/dist/{transport-DB5eDN4x.mjs → transport-C9e_h-BF.mjs} +5 -4
- package/dist/trusted-proxy-Bi0Cuk5n.mjs +30 -0
- package/dist/{types-BawVha09.mjs → types-Bs6lTBBW.mjs} +1 -1
- package/dist/types-C982qI5I.d.mts +344 -0
- package/dist/types-D4XVOt01.d.mts +165 -0
- package/dist/{types-Cj0KMIZV.d.mts → types-DgfUZqcd.d.mts} +54 -16
- package/dist/{types-BuMDPy5C.d.mts → types-IPACEM14.d.mts} +6 -0
- package/dist/user-CcXq-zoL.mjs +154 -0
- package/dist/utils-D2in-zwy.mjs +285 -0
- package/dist/{validate-BZ5wnLLp.mjs → validate-BJgA6TW_.mjs} +1 -1
- package/dist/{validate-IPf8n4Fj.d.mts → validate-JCZihRIa.d.mts} +3 -3
- package/dist/version-DH53KCQd.mjs +6 -0
- package/dist/widgets-B7Q_7bxN.mjs +104 -0
- package/dist/wordpress-slugs-BevajWrC.mjs +14 -0
- package/dist/zod-generator-DBVP8D0P.mjs +132 -0
- package/locals.d.ts +1 -6
- package/package.json +96 -41
- package/src/components/DinewayHead.astro +8 -4
- package/src/components/DinewayImage.astro +7 -5
- package/src/components/DinewayMedia.astro +9 -3
- package/src/components/Gallery.astro +5 -3
- package/src/components/Image.astro +5 -1
- package/src/components/InlinePortableTextEditor.tsx +68 -19
- package/dist/error-BmL6QipT.mjs +0 -30
- package/dist/search-Hlm6g8Td.mjs +0 -11200
- package/dist/version-DxxaFHZ_.mjs +0 -6
- package/src/astro/routes/PluginRegistry.tsx +0 -21
- package/src/astro/routes/api/admin/allowed-domains/[domain].ts +0 -112
- package/src/astro/routes/api/admin/allowed-domains/index.ts +0 -108
- package/src/astro/routes/api/admin/api-tokens/[id].ts +0 -44
- package/src/astro/routes/api/admin/api-tokens/index.ts +0 -90
- package/src/astro/routes/api/admin/briefing.ts +0 -76
- package/src/astro/routes/api/admin/bylines/[id]/index.ts +0 -90
- package/src/astro/routes/api/admin/bylines/index.ts +0 -74
- package/src/astro/routes/api/admin/comments/[id]/status.ts +0 -120
- package/src/astro/routes/api/admin/comments/[id].ts +0 -64
- package/src/astro/routes/api/admin/comments/bulk.ts +0 -42
- package/src/astro/routes/api/admin/comments/counts.ts +0 -30
- package/src/astro/routes/api/admin/comments/index.ts +0 -46
- package/src/astro/routes/api/admin/context/[id]/history.ts +0 -35
- package/src/astro/routes/api/admin/context/[id]/index.ts +0 -35
- package/src/astro/routes/api/admin/context/[id]/review.ts +0 -57
- package/src/astro/routes/api/admin/context/[id]/supersede.ts +0 -58
- package/src/astro/routes/api/admin/context/diff.ts +0 -35
- package/src/astro/routes/api/admin/context/index.ts +0 -69
- package/src/astro/routes/api/admin/context/stale.ts +0 -35
- package/src/astro/routes/api/admin/hitl-requests/[id]/index.ts +0 -38
- package/src/astro/routes/api/admin/hitl-requests/[id]/resolve.ts +0 -54
- package/src/astro/routes/api/admin/hitl-requests/index.ts +0 -38
- package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +0 -132
- package/src/astro/routes/api/admin/hooks/exclusive/index.ts +0 -51
- package/src/astro/routes/api/admin/oauth-clients/[id].ts +0 -137
- package/src/astro/routes/api/admin/oauth-clients/index.ts +0 -95
- package/src/astro/routes/api/admin/plugins/[id]/disable.ts +0 -91
- package/src/astro/routes/api/admin/plugins/[id]/enable.ts +0 -91
- package/src/astro/routes/api/admin/plugins/[id]/index.ts +0 -38
- package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +0 -98
- package/src/astro/routes/api/admin/plugins/[id]/update.ts +0 -154
- package/src/astro/routes/api/admin/plugins/index.ts +0 -32
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +0 -62
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +0 -33
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +0 -135
- package/src/astro/routes/api/admin/plugins/marketplace/index.ts +0 -38
- package/src/astro/routes/api/admin/plugins/updates.ts +0 -28
- package/src/astro/routes/api/admin/review-requests/[id]/index.ts +0 -35
- package/src/astro/routes/api/admin/review-requests/[id]/resolve.ts +0 -52
- package/src/astro/routes/api/admin/review-requests/index.ts +0 -35
- package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +0 -33
- package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +0 -62
- package/src/astro/routes/api/admin/themes/marketplace/index.ts +0 -45
- package/src/astro/routes/api/admin/users/[id]/disable.ts +0 -72
- package/src/astro/routes/api/admin/users/[id]/enable.ts +0 -48
- package/src/astro/routes/api/admin/users/[id]/index.ts +0 -166
- package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +0 -72
- package/src/astro/routes/api/admin/users/index.ts +0 -66
- package/src/astro/routes/api/auth/dev-bypass.ts +0 -139
- package/src/astro/routes/api/auth/invite/accept.ts +0 -52
- package/src/astro/routes/api/auth/invite/complete.ts +0 -86
- package/src/astro/routes/api/auth/invite/index.ts +0 -99
- package/src/astro/routes/api/auth/invite/register-options.ts +0 -73
- package/src/astro/routes/api/auth/logout.ts +0 -40
- package/src/astro/routes/api/auth/magic-link/send.ts +0 -90
- package/src/astro/routes/api/auth/magic-link/verify.ts +0 -71
- package/src/astro/routes/api/auth/me.ts +0 -60
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +0 -221
- package/src/astro/routes/api/auth/oauth/[provider].ts +0 -120
- package/src/astro/routes/api/auth/passkey/[id].ts +0 -124
- package/src/astro/routes/api/auth/passkey/index.ts +0 -54
- package/src/astro/routes/api/auth/passkey/options.ts +0 -85
- package/src/astro/routes/api/auth/passkey/register/options.ts +0 -88
- package/src/astro/routes/api/auth/passkey/register/verify.ts +0 -119
- package/src/astro/routes/api/auth/passkey/verify.ts +0 -72
- package/src/astro/routes/api/auth/signup/complete.ts +0 -87
- package/src/astro/routes/api/auth/signup/request.ts +0 -89
- package/src/astro/routes/api/auth/signup/verify.ts +0 -53
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +0 -310
- package/src/astro/routes/api/content/[collection]/[id]/compare.ts +0 -28
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +0 -68
- package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +0 -77
- package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +0 -42
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +0 -107
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +0 -100
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +0 -64
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +0 -31
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +0 -129
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +0 -143
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +0 -50
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +0 -69
- package/src/astro/routes/api/content/[collection]/[id].ts +0 -173
- package/src/astro/routes/api/content/[collection]/index.ts +0 -103
- package/src/astro/routes/api/content/[collection]/trash.ts +0 -33
- package/src/astro/routes/api/dashboard.ts +0 -32
- package/src/astro/routes/api/dev/emails.ts +0 -36
- package/src/astro/routes/api/health.ts +0 -54
- package/src/astro/routes/api/import/probe.ts +0 -47
- package/src/astro/routes/api/import/wordpress/analyze.ts +0 -523
- package/src/astro/routes/api/import/wordpress/execute.ts +0 -330
- package/src/astro/routes/api/import/wordpress/media.ts +0 -338
- package/src/astro/routes/api/import/wordpress/prepare.ts +0 -212
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +0 -425
- package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +0 -111
- package/src/astro/routes/api/import/wordpress-plugin/callback.ts +0 -58
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +0 -399
- package/src/astro/routes/api/manifest.ts +0 -75
- package/src/astro/routes/api/mcp.ts +0 -125
- package/src/astro/routes/api/media/[id]/confirm.ts +0 -93
- package/src/astro/routes/api/media/[id].ts +0 -145
- package/src/astro/routes/api/media/file/[...key].ts +0 -79
- package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +0 -91
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +0 -111
- package/src/astro/routes/api/media/providers/index.ts +0 -30
- package/src/astro/routes/api/media/upload-url.ts +0 -146
- package/src/astro/routes/api/media.ts +0 -204
- package/src/astro/routes/api/menus/[name]/reorder.ts +0 -79
- package/src/astro/routes/api/menus/[name].ts +0 -145
- package/src/astro/routes/api/menus/index.ts +0 -91
- package/src/astro/routes/api/oauth/authorize.ts +0 -430
- package/src/astro/routes/api/oauth/device/authorize.ts +0 -45
- package/src/astro/routes/api/oauth/device/code.ts +0 -56
- package/src/astro/routes/api/oauth/device/token.ts +0 -70
- package/src/astro/routes/api/oauth/register.ts +0 -182
- package/src/astro/routes/api/oauth/token/refresh.ts +0 -38
- package/src/astro/routes/api/oauth/token/revoke.ts +0 -38
- package/src/astro/routes/api/oauth/token.ts +0 -195
- package/src/astro/routes/api/openapi.json.ts +0 -33
- package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +0 -109
- package/src/astro/routes/api/redirects/404s/index.ts +0 -72
- package/src/astro/routes/api/redirects/404s/summary.ts +0 -33
- package/src/astro/routes/api/redirects/[id].ts +0 -183
- package/src/astro/routes/api/redirects/index.ts +0 -100
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +0 -29
- package/src/astro/routes/api/revisions/[revisionId]/restore.ts +0 -62
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +0 -104
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +0 -67
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +0 -45
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +0 -107
- package/src/astro/routes/api/schema/collections/index.ts +0 -61
- package/src/astro/routes/api/schema/index.ts +0 -109
- package/src/astro/routes/api/schema/orphans/[slug].ts +0 -36
- package/src/astro/routes/api/schema/orphans/index.ts +0 -26
- package/src/astro/routes/api/search/enable.ts +0 -64
- package/src/astro/routes/api/search/index.ts +0 -52
- package/src/astro/routes/api/search/rebuild.ts +0 -72
- package/src/astro/routes/api/search/stats.ts +0 -35
- package/src/astro/routes/api/search/suggest.ts +0 -50
- package/src/astro/routes/api/sections/[slug].ts +0 -203
- package/src/astro/routes/api/sections/index.ts +0 -107
- package/src/astro/routes/api/settings/email.ts +0 -150
- package/src/astro/routes/api/settings.ts +0 -116
- package/src/astro/routes/api/setup/admin-verify.ts +0 -122
- package/src/astro/routes/api/setup/admin.ts +0 -104
- package/src/astro/routes/api/setup/dev-bypass.ts +0 -200
- package/src/astro/routes/api/setup/dev-reset.ts +0 -40
- package/src/astro/routes/api/setup/index.ts +0 -128
- package/src/astro/routes/api/setup/status.ts +0 -122
- package/src/astro/routes/api/snapshot.ts +0 -76
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +0 -232
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +0 -131
- package/src/astro/routes/api/taxonomies/index.ts +0 -114
- package/src/astro/routes/api/themes/preview.ts +0 -78
- package/src/astro/routes/api/typegen.ts +0 -114
- package/src/astro/routes/api/well-known/auth.ts +0 -71
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +0 -48
- package/src/astro/routes/api/well-known/oauth-protected-resource.ts +0 -39
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +0 -114
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +0 -213
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +0 -126
- package/src/astro/routes/api/widget-areas/[name].ts +0 -135
- package/src/astro/routes/api/widget-areas/index.ts +0 -149
- package/src/astro/routes/api/widget-components.ts +0 -22
- package/src/astro/routes/robots.txt.ts +0 -81
- package/src/astro/routes/sitemap-[collection].xml.ts +0 -104
- package/src/astro/routes/sitemap.xml.ts +0 -92
- /package/dist/{adapters-C2ypTrZZ.d.mts → adapters-BLDldpJg.d.mts} +0 -0
- /package/{src → dist}/astro/routes/admin.astro +0 -0
- /package/dist/{base64-F8-DUraK.mjs → base64-Cz-aU0X1.mjs} +0 -0
- /package/dist/{chunks--4F8ddV4.mjs → chunks-D_jVet6z.mjs} +0 -0
- /package/dist/{config-BXwuX8Bx.mjs → config-CAMFxGaV.mjs} +0 -0
- /package/dist/{db-errors-CEqD7qH9.mjs → db-errors-DKUg_NgF.mjs} +0 -0
- /package/dist/{default-VjJyuuG9.mjs → default-C3PZN-bz.mjs} +0 -0
- /package/dist/{load-Coc9HpHH.mjs → load-D-9NhLmF.mjs} +0 -0
- /package/dist/{mode-47goXBBK.mjs → mode-C80mAZQv.mjs} +0 -0
- /package/dist/{placeholder--wOi4TbO.d.mts → placeholder-CHkLckzK.d.mts} +0 -0
- /package/dist/{request-cache-Dk5qPSOx.mjs → request-cache-DHMRr2Lf.mjs} +0 -0
- /package/dist/{transaction-Cn2rjY78.mjs → transaction-x2tJQ-A1.mjs} +0 -0
- /package/dist/{transport-Wge_IzKl.d.mts → transport-6RefuBdV.d.mts} +0 -0
- /package/dist/{types-griIBQOQ.mjs → types-B9gKVOHk.mjs} +0 -0
- /package/dist/{types-CWbdtiux.d.mts → types-B9qVtiHb.d.mts} +0 -0
- /package/dist/{types-COeOq9nK.mjs → types-DL7Y8D_t.mjs} +0 -0
- /package/dist/{types-BzcUjoqg.d.mts → types-Djdp0cZO.d.mts} +0 -0
- /package/dist/{types-DOrVigru.d.mts → types-Du8jreyC.d.mts} +0 -0
|
@@ -0,0 +1,4122 @@
|
|
|
1
|
+
import { t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
|
|
2
|
+
import { t as ContentRepository } from "./content-CyLkb-qH.mjs";
|
|
3
|
+
import { n as decodeCursor, r as encodeCursor } from "./types-Bs6lTBBW.mjs";
|
|
4
|
+
import { t as SiteActivityRepository } from "./site-activity-B8FjLIVh.mjs";
|
|
5
|
+
import { t as ReviewRequestRepository } from "./review-requests-C2DIHwlJ.mjs";
|
|
6
|
+
import { n as normalizeEntityAlias, t as EntityAliasRepository } from "./entity-aliases-C0v-yNET.mjs";
|
|
7
|
+
import { r as isPattern } from "./patterns-K0DLqWir.mjs";
|
|
8
|
+
import { a as ContextRepository, i as parseSiteBriefingScope, l as handlePluginList } from "./briefing-BrXCuMEE.mjs";
|
|
9
|
+
import { t as FTSManager } from "./fts-manager-B1pTNEG_.mjs";
|
|
10
|
+
import { n as SchemaRegistry } from "./registry-C-_hxLqa.mjs";
|
|
11
|
+
import { a as getSiteSettingsWithDb } from "./settings-4XnpVMOS.mjs";
|
|
12
|
+
import { p as normalizeUrl } from "./utils-D2in-zwy.mjs";
|
|
13
|
+
import { t as sanitizeWordPressImportSlug } from "./wordpress-slugs-BevajWrC.mjs";
|
|
14
|
+
import { s as searchCollection } from "./search-DM6CVti3.mjs";
|
|
15
|
+
import { n as assertExperimentalSiteContextWorkflowsEnabled } from "./experimental-workflows-DldxJlqV.mjs";
|
|
16
|
+
import { sql } from "kysely";
|
|
17
|
+
import { ulid } from "ulidx";
|
|
18
|
+
import { Role } from "@dineway-ai/auth";
|
|
19
|
+
|
|
20
|
+
//#region src/database/repositories/assignments.ts
|
|
21
|
+
const ASSIGNMENT_ACTOR_TYPES = [
|
|
22
|
+
"user",
|
|
23
|
+
"api_token",
|
|
24
|
+
"system"
|
|
25
|
+
];
|
|
26
|
+
const ASSIGNMENT_STATUSES = [
|
|
27
|
+
"pending",
|
|
28
|
+
"accepted",
|
|
29
|
+
"in_progress",
|
|
30
|
+
"blocked",
|
|
31
|
+
"completed",
|
|
32
|
+
"declined",
|
|
33
|
+
"cancelled"
|
|
34
|
+
];
|
|
35
|
+
const ASSIGNMENT_PRIORITIES = [
|
|
36
|
+
"low",
|
|
37
|
+
"normal",
|
|
38
|
+
"high",
|
|
39
|
+
"urgent"
|
|
40
|
+
];
|
|
41
|
+
var AssignmentRepository = class {
|
|
42
|
+
constructor(db) {
|
|
43
|
+
this.db = db;
|
|
44
|
+
}
|
|
45
|
+
async create(input) {
|
|
46
|
+
const id = ulid();
|
|
47
|
+
const now = input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
48
|
+
await this.db.insertInto("_dineway_assignments").values(toInsertableAssignment(id, input, now)).execute();
|
|
49
|
+
const assignment = await this.findById(id);
|
|
50
|
+
if (!assignment) throw new Error("Failed to create assignment");
|
|
51
|
+
return assignment;
|
|
52
|
+
}
|
|
53
|
+
async findById(id) {
|
|
54
|
+
const row = await this.db.selectFrom("_dineway_assignments").selectAll().where("id", "=", id).executeTakeFirst();
|
|
55
|
+
return row ? rowToAssignment(row) : null;
|
|
56
|
+
}
|
|
57
|
+
async list(query = {}) {
|
|
58
|
+
const limit = clampLimit$3(query.limit);
|
|
59
|
+
const rows = await this.queryRows(query).execute();
|
|
60
|
+
const items = rows.slice(0, limit).map(rowToAssignment);
|
|
61
|
+
const result = { items };
|
|
62
|
+
if (rows.length > limit && items.length > 0) {
|
|
63
|
+
const last = items.at(-1);
|
|
64
|
+
result.nextCursor = encodeCursor(last.createdAt, last.id);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
async accept(id, input = {}) {
|
|
69
|
+
const value = input.acceptedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
70
|
+
return this.transition(id, ["pending"], {
|
|
71
|
+
status: "accepted",
|
|
72
|
+
assigned_to_actor_type: input.assignedToActorType ?? null,
|
|
73
|
+
assigned_to_actor_id: input.assignedToActorId ?? null,
|
|
74
|
+
accepted_at: value,
|
|
75
|
+
updated_at: value,
|
|
76
|
+
metadata: jsonString$1(input.metadata)
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async start(id, input = {}) {
|
|
80
|
+
const value = input.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
81
|
+
return this.transition(id, ["accepted", "blocked"], {
|
|
82
|
+
status: "in_progress",
|
|
83
|
+
started_at: value,
|
|
84
|
+
updated_at: value,
|
|
85
|
+
metadata: jsonString$1(input.metadata)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async block(id, input = {}) {
|
|
89
|
+
const value = input.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
90
|
+
return this.transition(id, ["accepted", "in_progress"], {
|
|
91
|
+
status: "blocked",
|
|
92
|
+
blocked_at: value,
|
|
93
|
+
updated_at: value,
|
|
94
|
+
metadata: jsonString$1(input.metadata)
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async complete(id, input = {}) {
|
|
98
|
+
const value = input.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
99
|
+
return this.transition(id, [
|
|
100
|
+
"pending",
|
|
101
|
+
"accepted",
|
|
102
|
+
"in_progress"
|
|
103
|
+
], {
|
|
104
|
+
status: "completed",
|
|
105
|
+
completed_at: value,
|
|
106
|
+
updated_at: value,
|
|
107
|
+
metadata: jsonString$1(input.metadata)
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async decline(id, input = {}) {
|
|
111
|
+
const value = input.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
112
|
+
return this.transition(id, ["pending", "accepted"], {
|
|
113
|
+
status: "declined",
|
|
114
|
+
declined_at: value,
|
|
115
|
+
updated_at: value,
|
|
116
|
+
metadata: jsonString$1(input.metadata)
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async cancel(id, input = {}) {
|
|
120
|
+
const value = input.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
121
|
+
return this.transition(id, [
|
|
122
|
+
"pending",
|
|
123
|
+
"accepted",
|
|
124
|
+
"in_progress",
|
|
125
|
+
"blocked"
|
|
126
|
+
], {
|
|
127
|
+
status: "cancelled",
|
|
128
|
+
cancelled_at: value,
|
|
129
|
+
updated_at: value,
|
|
130
|
+
metadata: jsonString$1(input.metadata)
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async linkHitlRequest(id, relatedHitlRequestId, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
134
|
+
const result = await this.db.updateTable("_dineway_assignments").set({
|
|
135
|
+
related_hitl_request_id: relatedHitlRequestId,
|
|
136
|
+
updated_at: at
|
|
137
|
+
}).where("id", "=", id).executeTakeFirst();
|
|
138
|
+
if (Number(result.numUpdatedRows ?? 0) === 0) return null;
|
|
139
|
+
return this.findById(id);
|
|
140
|
+
}
|
|
141
|
+
queryRows(query) {
|
|
142
|
+
const limit = clampLimit$3(query.limit);
|
|
143
|
+
let q = this.db.selectFrom("_dineway_assignments").selectAll().orderBy("created_at", "desc").orderBy("id", "desc").limit(limit + 1);
|
|
144
|
+
if (query.scope) q = q.where("scope", "=", query.scope);
|
|
145
|
+
if (query.scopes && query.scopes.length > 0) q = q.where("scope", "in", query.scopes);
|
|
146
|
+
if (query.statuses && query.statuses.length > 0) q = q.where("status", "in", query.statuses);
|
|
147
|
+
if (query.priority) q = q.where("priority", "=", query.priority);
|
|
148
|
+
if (query.assignmentType) q = q.where("assignment_type", "=", query.assignmentType);
|
|
149
|
+
if (query.assignedToActorType && query.assignedToActorId) q = q.where("assigned_to_actor_type", "=", query.assignedToActorType).where("assigned_to_actor_id", "=", query.assignedToActorId);
|
|
150
|
+
if (query.assignedByActorType && query.assignedByActorId) q = q.where("assigned_by_actor_type", "=", query.assignedByActorType).where("assigned_by_actor_id", "=", query.assignedByActorId);
|
|
151
|
+
if (query.participantActor) q = q.where((eb) => eb.or([eb.and([eb("assigned_by_actor_type", "=", query.participantActor.actorType), eb("assigned_by_actor_id", "=", query.participantActor.actorId)]), eb.and([eb("assigned_to_actor_type", "=", query.participantActor.actorType), eb("assigned_to_actor_id", "=", query.participantActor.actorId)])]));
|
|
152
|
+
if (query.since) q = q.where("updated_at", ">=", query.since);
|
|
153
|
+
if (query.cursor) {
|
|
154
|
+
const decoded = decodeCursor(query.cursor);
|
|
155
|
+
if (decoded) q = q.where((eb) => eb.or([eb("created_at", "<", decoded.orderValue), eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)])]));
|
|
156
|
+
}
|
|
157
|
+
return q;
|
|
158
|
+
}
|
|
159
|
+
async transition(id, validFrom, update) {
|
|
160
|
+
const result = await this.db.updateTable("_dineway_assignments").set(update).where("id", "=", id).where("status", "in", validFrom).executeTakeFirst();
|
|
161
|
+
if (Number(result.numUpdatedRows ?? 0) === 0) return null;
|
|
162
|
+
return this.findById(id);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
function toInsertableAssignment(id, input, now) {
|
|
166
|
+
return {
|
|
167
|
+
id,
|
|
168
|
+
scope: input.scope,
|
|
169
|
+
assignment_type: input.assignmentType,
|
|
170
|
+
status: "pending",
|
|
171
|
+
priority: input.priority ?? "normal",
|
|
172
|
+
title: input.title,
|
|
173
|
+
summary: input.summary ?? null,
|
|
174
|
+
details: input.details ?? null,
|
|
175
|
+
assigned_by_actor_type: input.assignedByActorType,
|
|
176
|
+
assigned_by_actor_id: input.assignedByActorId,
|
|
177
|
+
assigned_to_actor_type: input.assignedToActorType ?? null,
|
|
178
|
+
assigned_to_actor_id: input.assignedToActorId ?? null,
|
|
179
|
+
metadata: jsonString$1(input.metadata),
|
|
180
|
+
related_handoff_snapshot_id: input.relatedHandoffSnapshotId ?? null,
|
|
181
|
+
related_hitl_request_id: input.relatedHitlRequestId ?? null,
|
|
182
|
+
due_at: input.dueAt ?? null,
|
|
183
|
+
created_at: now,
|
|
184
|
+
updated_at: now,
|
|
185
|
+
accepted_at: null,
|
|
186
|
+
started_at: null,
|
|
187
|
+
blocked_at: null,
|
|
188
|
+
completed_at: null,
|
|
189
|
+
declined_at: null,
|
|
190
|
+
cancelled_at: null
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function rowToAssignment(row) {
|
|
194
|
+
return {
|
|
195
|
+
id: row.id,
|
|
196
|
+
scope: row.scope,
|
|
197
|
+
assignmentType: row.assignment_type,
|
|
198
|
+
status: row.status,
|
|
199
|
+
priority: row.priority,
|
|
200
|
+
title: row.title,
|
|
201
|
+
summary: row.summary,
|
|
202
|
+
details: row.details,
|
|
203
|
+
assignedByActorType: row.assigned_by_actor_type,
|
|
204
|
+
assignedByActorId: row.assigned_by_actor_id,
|
|
205
|
+
assignedToActorType: row.assigned_to_actor_type,
|
|
206
|
+
assignedToActorId: row.assigned_to_actor_id,
|
|
207
|
+
metadata: parseRecord$2(row.metadata),
|
|
208
|
+
relatedHandoffSnapshotId: row.related_handoff_snapshot_id,
|
|
209
|
+
relatedHitlRequestId: row.related_hitl_request_id,
|
|
210
|
+
dueAt: row.due_at,
|
|
211
|
+
createdAt: row.created_at,
|
|
212
|
+
updatedAt: row.updated_at,
|
|
213
|
+
acceptedAt: row.accepted_at,
|
|
214
|
+
startedAt: row.started_at,
|
|
215
|
+
blockedAt: row.blocked_at,
|
|
216
|
+
completedAt: row.completed_at,
|
|
217
|
+
declinedAt: row.declined_at,
|
|
218
|
+
cancelledAt: row.cancelled_at
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function jsonString$1(value) {
|
|
222
|
+
return JSON.stringify(value ?? {});
|
|
223
|
+
}
|
|
224
|
+
function parseRecord$2(value) {
|
|
225
|
+
if (!value) return {};
|
|
226
|
+
try {
|
|
227
|
+
const parsed = JSON.parse(value);
|
|
228
|
+
return isRecord$5(parsed) ? parsed : {};
|
|
229
|
+
} catch {
|
|
230
|
+
return {};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function isRecord$5(value) {
|
|
234
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
235
|
+
}
|
|
236
|
+
function clampLimit$3(limit) {
|
|
237
|
+
if (!limit || limit < 1) return 50;
|
|
238
|
+
return Math.min(limit, 100);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/database/repositories/hitl-requests.ts
|
|
243
|
+
const HITL_REQUEST_STATUSES = [
|
|
244
|
+
"pending",
|
|
245
|
+
"approved",
|
|
246
|
+
"rejected"
|
|
247
|
+
];
|
|
248
|
+
const HITL_REQUEST_PRIORITIES = [
|
|
249
|
+
"low",
|
|
250
|
+
"normal",
|
|
251
|
+
"high",
|
|
252
|
+
"urgent"
|
|
253
|
+
];
|
|
254
|
+
var HitlRequestRepository = class {
|
|
255
|
+
constructor(db) {
|
|
256
|
+
this.db = db;
|
|
257
|
+
}
|
|
258
|
+
async create(input) {
|
|
259
|
+
const id = ulid();
|
|
260
|
+
const now = input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
261
|
+
await this.db.insertInto("_dineway_hitl_requests").values(toInsertableHitlRequest(id, input, now)).execute();
|
|
262
|
+
const request = await this.findById(id);
|
|
263
|
+
if (!request) throw new Error("Failed to create HITL request");
|
|
264
|
+
return request;
|
|
265
|
+
}
|
|
266
|
+
async findById(id) {
|
|
267
|
+
const row = await this.db.selectFrom("_dineway_hitl_requests").selectAll().where("id", "=", id).executeTakeFirst();
|
|
268
|
+
return row ? rowToHitlRequest(row) : null;
|
|
269
|
+
}
|
|
270
|
+
async list(query = {}) {
|
|
271
|
+
const limit = clampLimit$2(query.limit);
|
|
272
|
+
const rows = await this.queryRows(query).execute();
|
|
273
|
+
const items = rows.slice(0, limit).map(rowToHitlRequest);
|
|
274
|
+
const result = { items };
|
|
275
|
+
if (rows.length > limit && items.length > 0) {
|
|
276
|
+
const last = items.at(-1);
|
|
277
|
+
result.nextCursor = encodeCursor(last.createdAt, last.id);
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
async resolve(id, input) {
|
|
282
|
+
const resolvedAt = input.resolvedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
283
|
+
const result = await this.db.updateTable("_dineway_hitl_requests").set({
|
|
284
|
+
status: input.decision,
|
|
285
|
+
resolved_by_actor_type: input.resolvedByActorType,
|
|
286
|
+
resolved_by_actor_id: input.resolvedByActorId,
|
|
287
|
+
resolved_auth_metadata: jsonStringOrNull(input.resolvedAuthMetadata),
|
|
288
|
+
review_note: input.reviewNote ?? null,
|
|
289
|
+
resolved_at: resolvedAt,
|
|
290
|
+
updated_at: resolvedAt
|
|
291
|
+
}).where("id", "=", id).where("status", "=", "pending").executeTakeFirst();
|
|
292
|
+
if (Number(result.numUpdatedRows ?? 0) === 0) return null;
|
|
293
|
+
return this.findById(id);
|
|
294
|
+
}
|
|
295
|
+
queryRows(query) {
|
|
296
|
+
const limit = clampLimit$2(query.limit);
|
|
297
|
+
let q = this.db.selectFrom("_dineway_hitl_requests").selectAll().orderBy("created_at", "desc").orderBy("id", "desc").limit(limit + 1);
|
|
298
|
+
if (query.scope) q = q.where("scope", "=", query.scope);
|
|
299
|
+
if (query.scopes && query.scopes.length > 0) q = q.where("scope", "in", query.scopes);
|
|
300
|
+
if (query.statuses && query.statuses.length > 0) q = q.where("status", "in", query.statuses);
|
|
301
|
+
if (query.priority) q = q.where("priority", "=", query.priority);
|
|
302
|
+
if (query.actionType) q = q.where("action_type", "=", query.actionType);
|
|
303
|
+
if (query.targetRefType) q = q.where("target_ref_type", "=", query.targetRefType);
|
|
304
|
+
if (query.targetRefId) q = q.where("target_ref_id", "=", query.targetRefId);
|
|
305
|
+
if (query.relatedAssignmentId) q = q.where("related_assignment_id", "=", query.relatedAssignmentId);
|
|
306
|
+
if (query.requestedByActorType && query.requestedByActorId) q = q.where("requested_by_actor_type", "=", query.requestedByActorType).where("requested_by_actor_id", "=", query.requestedByActorId);
|
|
307
|
+
if (query.participantActor) q = q.where("requested_by_actor_type", "=", query.participantActor.actorType).where("requested_by_actor_id", "=", query.participantActor.actorId);
|
|
308
|
+
if (query.since) q = q.where("updated_at", ">=", query.since);
|
|
309
|
+
if (query.cursor) {
|
|
310
|
+
const decoded = decodeCursor(query.cursor);
|
|
311
|
+
if (decoded) q = q.where((eb) => eb.or([eb("created_at", "<", decoded.orderValue), eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)])]));
|
|
312
|
+
}
|
|
313
|
+
return q;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
function toInsertableHitlRequest(id, input, now) {
|
|
317
|
+
return {
|
|
318
|
+
id,
|
|
319
|
+
scope: input.scope,
|
|
320
|
+
action_type: input.actionType,
|
|
321
|
+
status: "pending",
|
|
322
|
+
priority: input.priority ?? "normal",
|
|
323
|
+
title: input.title,
|
|
324
|
+
summary: input.summary ?? null,
|
|
325
|
+
risk_reason: input.riskReason,
|
|
326
|
+
review_payload: JSON.stringify(input.reviewPayload),
|
|
327
|
+
target_ref_type: input.targetRefType,
|
|
328
|
+
target_ref_id: input.targetRefId,
|
|
329
|
+
requested_by_actor_type: input.requestedByActorType,
|
|
330
|
+
requested_by_actor_id: input.requestedByActorId,
|
|
331
|
+
requested_auth_metadata: jsonStringOrNull(input.requestedAuthMetadata),
|
|
332
|
+
resolved_by_actor_type: null,
|
|
333
|
+
resolved_by_actor_id: null,
|
|
334
|
+
resolved_auth_metadata: null,
|
|
335
|
+
review_note: null,
|
|
336
|
+
metadata: jsonString(input.metadata),
|
|
337
|
+
related_assignment_id: input.relatedAssignmentId ?? null,
|
|
338
|
+
related_handoff_snapshot_id: input.relatedHandoffSnapshotId ?? null,
|
|
339
|
+
sla_due_at: input.slaDueAt ?? null,
|
|
340
|
+
created_at: now,
|
|
341
|
+
updated_at: now,
|
|
342
|
+
resolved_at: null
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function rowToHitlRequest(row) {
|
|
346
|
+
return {
|
|
347
|
+
id: row.id,
|
|
348
|
+
scope: row.scope,
|
|
349
|
+
actionType: row.action_type,
|
|
350
|
+
status: row.status,
|
|
351
|
+
priority: row.priority,
|
|
352
|
+
title: row.title,
|
|
353
|
+
summary: row.summary,
|
|
354
|
+
riskReason: row.risk_reason,
|
|
355
|
+
reviewPayload: parseRecord$1(row.review_payload),
|
|
356
|
+
targetRefType: row.target_ref_type,
|
|
357
|
+
targetRefId: row.target_ref_id,
|
|
358
|
+
requestedByActorType: row.requested_by_actor_type,
|
|
359
|
+
requestedByActorId: row.requested_by_actor_id,
|
|
360
|
+
requestedAuthMetadata: parseNullableRecord(row.requested_auth_metadata),
|
|
361
|
+
resolvedByActorType: row.resolved_by_actor_type,
|
|
362
|
+
resolvedByActorId: row.resolved_by_actor_id,
|
|
363
|
+
resolvedAuthMetadata: parseNullableRecord(row.resolved_auth_metadata),
|
|
364
|
+
reviewNote: row.review_note,
|
|
365
|
+
metadata: parseRecord$1(row.metadata),
|
|
366
|
+
relatedAssignmentId: row.related_assignment_id,
|
|
367
|
+
relatedHandoffSnapshotId: row.related_handoff_snapshot_id,
|
|
368
|
+
slaDueAt: row.sla_due_at,
|
|
369
|
+
createdAt: row.created_at,
|
|
370
|
+
updatedAt: row.updated_at,
|
|
371
|
+
resolvedAt: row.resolved_at
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function jsonString(value) {
|
|
375
|
+
return JSON.stringify(value ?? {});
|
|
376
|
+
}
|
|
377
|
+
function jsonStringOrNull(value) {
|
|
378
|
+
return value ? JSON.stringify(value) : null;
|
|
379
|
+
}
|
|
380
|
+
function parseRecord$1(value) {
|
|
381
|
+
if (!value) return {};
|
|
382
|
+
try {
|
|
383
|
+
const parsed = JSON.parse(value);
|
|
384
|
+
return isRecord$4(parsed) ? parsed : {};
|
|
385
|
+
} catch {
|
|
386
|
+
return {};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function parseNullableRecord(value) {
|
|
390
|
+
if (!value) return null;
|
|
391
|
+
return parseRecord$1(value);
|
|
392
|
+
}
|
|
393
|
+
function isRecord$4(value) {
|
|
394
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
395
|
+
}
|
|
396
|
+
function clampLimit$2(limit) {
|
|
397
|
+
if (!limit || limit < 1) return 50;
|
|
398
|
+
return Math.min(limit, 100);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/site-context/context-entry-public.ts
|
|
403
|
+
function toPublicContextEntry(entry) {
|
|
404
|
+
return {
|
|
405
|
+
id: entry.id,
|
|
406
|
+
scope: entry.scope,
|
|
407
|
+
contextType: entry.contextType,
|
|
408
|
+
title: entry.title,
|
|
409
|
+
body: entry.body,
|
|
410
|
+
policyKey: entry.policyKey,
|
|
411
|
+
sourceActivityId: entry.sourceActivityId,
|
|
412
|
+
createdByActorType: entry.createdByActorType,
|
|
413
|
+
createdByActorId: entry.createdByActorId,
|
|
414
|
+
createdAuthMetadata: entry.createdAuthMetadata,
|
|
415
|
+
supersedesId: entry.supersedesId,
|
|
416
|
+
version: entry.version,
|
|
417
|
+
isCurrent: entry.isCurrent,
|
|
418
|
+
validUntil: entry.validUntil,
|
|
419
|
+
reviewedAt: entry.reviewedAt,
|
|
420
|
+
reviewedByActorType: entry.reviewedByActorType,
|
|
421
|
+
reviewedByActorId: entry.reviewedByActorId,
|
|
422
|
+
reviewNote: entry.reviewNote,
|
|
423
|
+
tags: entry.tags,
|
|
424
|
+
createdAt: entry.createdAt,
|
|
425
|
+
updatedAt: entry.updatedAt
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function toPublicContextEntries(entries) {
|
|
429
|
+
return entries.map(toPublicContextEntry);
|
|
430
|
+
}
|
|
431
|
+
function toPublicContextEntryPage(page) {
|
|
432
|
+
return {
|
|
433
|
+
items: toPublicContextEntries(page.items),
|
|
434
|
+
nextCursor: page.nextCursor
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
function toPublicContextDiff(diff) {
|
|
438
|
+
return {
|
|
439
|
+
newEntries: toPublicContextEntries(diff.newEntries),
|
|
440
|
+
supersededEntries: toPublicContextEntries(diff.supersededEntries),
|
|
441
|
+
staleEntries: toPublicContextEntries(diff.staleEntries),
|
|
442
|
+
reviewedEntries: toPublicContextEntries(diff.reviewedEntries)
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
//#endregion
|
|
447
|
+
//#region src/site-context/action-hash.ts
|
|
448
|
+
async function buildStableActionHash(value) {
|
|
449
|
+
return sha256Hex(stableStringify(value));
|
|
450
|
+
}
|
|
451
|
+
function stableStringify(value) {
|
|
452
|
+
if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
453
|
+
if (isRecord$3(value)) return `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`;
|
|
454
|
+
return JSON.stringify(value ?? null);
|
|
455
|
+
}
|
|
456
|
+
function isRecord$3(value) {
|
|
457
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
458
|
+
}
|
|
459
|
+
async function sha256Hex(value) {
|
|
460
|
+
const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
461
|
+
return Array.from(new Uint8Array(buffer), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region src/site-context/review-payload.ts
|
|
466
|
+
var ReviewPayloadBuilder = class {
|
|
467
|
+
constructor(handlers) {
|
|
468
|
+
this.handlers = handlers;
|
|
469
|
+
}
|
|
470
|
+
async buildContentReviewPayload(input) {
|
|
471
|
+
const actionType = input.actionType ?? "content.publish";
|
|
472
|
+
const content = await this.loadContent(input.collection, input.id);
|
|
473
|
+
const compare = await this.loadCompare(input.collection, content.entryId);
|
|
474
|
+
const live = asRecord(compare.live);
|
|
475
|
+
const draft = asRecord(compare.draft) ?? content.data;
|
|
476
|
+
const hasChanges = typeof compare.hasChanges === "boolean" ? compare.hasChanges : stableStringify(live) !== stableStringify(draft);
|
|
477
|
+
const actionHash = await buildStableActionHash({
|
|
478
|
+
actionType,
|
|
479
|
+
collection: input.collection,
|
|
480
|
+
entryId: content.entryId,
|
|
481
|
+
liveRevisionId: content.liveRevisionId,
|
|
482
|
+
draftRevisionId: content.draftRevisionId,
|
|
483
|
+
reviewedRev: content.reviewedRev,
|
|
484
|
+
live,
|
|
485
|
+
draft
|
|
486
|
+
});
|
|
487
|
+
const scope = `content:${input.collection}:${content.entryId}`;
|
|
488
|
+
const summary = input.summary ?? `Review ${actionType} for ${input.collection}/${content.displayTitle ?? content.entryId}`;
|
|
489
|
+
return {
|
|
490
|
+
collection: input.collection,
|
|
491
|
+
entryId: content.entryId,
|
|
492
|
+
scope,
|
|
493
|
+
liveRevisionId: content.liveRevisionId,
|
|
494
|
+
draftRevisionId: content.draftRevisionId,
|
|
495
|
+
reviewedRev: content.reviewedRev,
|
|
496
|
+
actionType,
|
|
497
|
+
actionHash,
|
|
498
|
+
reviewPayload: {
|
|
499
|
+
kind: "content_review",
|
|
500
|
+
actionType,
|
|
501
|
+
summary,
|
|
502
|
+
target: {
|
|
503
|
+
collection: input.collection,
|
|
504
|
+
entryId: content.entryId,
|
|
505
|
+
scope,
|
|
506
|
+
liveRevisionId: content.liveRevisionId,
|
|
507
|
+
draftRevisionId: content.draftRevisionId,
|
|
508
|
+
reviewedRev: content.reviewedRev,
|
|
509
|
+
actionHash
|
|
510
|
+
},
|
|
511
|
+
content: {
|
|
512
|
+
id: content.entryId,
|
|
513
|
+
slug: content.slug,
|
|
514
|
+
status: content.status,
|
|
515
|
+
title: content.displayTitle,
|
|
516
|
+
updatedAt: content.updatedAt
|
|
517
|
+
},
|
|
518
|
+
diff: {
|
|
519
|
+
hasChanges,
|
|
520
|
+
live,
|
|
521
|
+
draft
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
async loadContent(collection, id) {
|
|
527
|
+
const result = await this.handlers.handleContentGet(collection, id);
|
|
528
|
+
if (!result.success) throw new Error(result.error?.message ?? `Content item not found: ${collection}/${id}`);
|
|
529
|
+
const payload = asRecord(result.data);
|
|
530
|
+
const item = asRecord(payload?.item);
|
|
531
|
+
if (!payload || !item) throw new Error("Content get response did not include an item");
|
|
532
|
+
const entryId = stringOrNull(item.id);
|
|
533
|
+
const reviewedRev = stringOrNull(payload._rev);
|
|
534
|
+
if (!entryId) throw new Error("Content item is missing id");
|
|
535
|
+
if (!reviewedRev) throw new Error("Content item is missing _rev for review binding");
|
|
536
|
+
const data = asRecord(item.data) ?? {};
|
|
537
|
+
const title = stringOrNull(data.title) ?? stringOrNull(item.title) ?? stringOrNull(item.slug);
|
|
538
|
+
return {
|
|
539
|
+
entryId,
|
|
540
|
+
reviewedRev,
|
|
541
|
+
liveRevisionId: stringOrNull(item.liveRevisionId),
|
|
542
|
+
draftRevisionId: stringOrNull(item.draftRevisionId),
|
|
543
|
+
slug: stringOrNull(item.slug),
|
|
544
|
+
status: stringOrNull(item.status),
|
|
545
|
+
updatedAt: stringOrNull(item.updatedAt),
|
|
546
|
+
displayTitle: title,
|
|
547
|
+
data
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
async loadCompare(collection, id) {
|
|
551
|
+
const result = await this.handlers.handleContentCompare(collection, id);
|
|
552
|
+
if (!result.success) throw new Error(result.error?.message ?? `Failed to compare content: ${collection}/${id}`);
|
|
553
|
+
return asRecord(result.data) ?? {};
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
function asRecord(value) {
|
|
557
|
+
return isRecord$2(value) ? value : null;
|
|
558
|
+
}
|
|
559
|
+
function isRecord$2(value) {
|
|
560
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
561
|
+
}
|
|
562
|
+
function stringOrNull(value) {
|
|
563
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
//#endregion
|
|
567
|
+
//#region src/site-context/risk-policy.ts
|
|
568
|
+
var RiskPolicyEvaluator = class {
|
|
569
|
+
repository;
|
|
570
|
+
hitlRepository;
|
|
571
|
+
payloadBuilder;
|
|
572
|
+
constructor(options) {
|
|
573
|
+
this.repository = new ReviewRequestRepository(options.db);
|
|
574
|
+
this.hitlRepository = new HitlRequestRepository(options.db);
|
|
575
|
+
this.payloadBuilder = new ReviewPayloadBuilder(options.handlers);
|
|
576
|
+
}
|
|
577
|
+
requiresContentPublishReview(actor) {
|
|
578
|
+
return isTokenAuthenticatedActor(actor);
|
|
579
|
+
}
|
|
580
|
+
requiresWorkflowHitl(actor) {
|
|
581
|
+
return isTokenAuthenticatedActor(actor);
|
|
582
|
+
}
|
|
583
|
+
async evaluateContentPublishReview(input) {
|
|
584
|
+
if (!this.requiresContentPublishReview(input.actor)) return {
|
|
585
|
+
allowed: true,
|
|
586
|
+
required: false
|
|
587
|
+
};
|
|
588
|
+
if (!input.reviewRequestId) return {
|
|
589
|
+
allowed: false,
|
|
590
|
+
required: true,
|
|
591
|
+
reason: "missing_review_request",
|
|
592
|
+
message: "content.publish requires an approved review_request_id for token-authenticated actors. Submit review_request_submit, resolve it as approved, then retry publish with that id."
|
|
593
|
+
};
|
|
594
|
+
const built = await this.payloadBuilder.buildContentReviewPayload({
|
|
595
|
+
collection: input.collection,
|
|
596
|
+
id: input.id,
|
|
597
|
+
actionType: "content.publish"
|
|
598
|
+
});
|
|
599
|
+
const target = {
|
|
600
|
+
collection: built.collection,
|
|
601
|
+
entryId: built.entryId,
|
|
602
|
+
liveRevisionId: built.liveRevisionId,
|
|
603
|
+
draftRevisionId: built.draftRevisionId,
|
|
604
|
+
reviewedRev: built.reviewedRev,
|
|
605
|
+
actionType: built.actionType,
|
|
606
|
+
actionHash: built.actionHash
|
|
607
|
+
};
|
|
608
|
+
const approved = await this.repository.findApprovedForTarget(target);
|
|
609
|
+
if (approved?.id === input.reviewRequestId) return {
|
|
610
|
+
allowed: true,
|
|
611
|
+
required: true,
|
|
612
|
+
reviewRequest: approved,
|
|
613
|
+
target
|
|
614
|
+
};
|
|
615
|
+
return {
|
|
616
|
+
allowed: false,
|
|
617
|
+
required: true,
|
|
618
|
+
reason: "stale_or_unapproved_review",
|
|
619
|
+
message: "The supplied review_request_id is not approved for the current content draft/action. Submit and approve a fresh review request before publishing.",
|
|
620
|
+
target
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
async evaluateWorkflowHitl(input) {
|
|
624
|
+
if (!this.requiresWorkflowHitl(input.actor)) return {
|
|
625
|
+
allowed: true,
|
|
626
|
+
required: false
|
|
627
|
+
};
|
|
628
|
+
if (!input.hitlRequestId) return {
|
|
629
|
+
allowed: false,
|
|
630
|
+
required: true,
|
|
631
|
+
reason: "missing_hitl_request",
|
|
632
|
+
message: `${input.action.actionType} requires an approved hitl_request_id for token-authenticated actors. Resolve the generated HITL request, then retry this tool with that id.`,
|
|
633
|
+
action: input.action
|
|
634
|
+
};
|
|
635
|
+
const request = await this.hitlRepository.findById(input.hitlRequestId);
|
|
636
|
+
if (request && this.matchesApprovedWorkflowHitl(request, input.action)) return {
|
|
637
|
+
allowed: true,
|
|
638
|
+
required: true,
|
|
639
|
+
hitlRequest: request,
|
|
640
|
+
action: input.action
|
|
641
|
+
};
|
|
642
|
+
return {
|
|
643
|
+
allowed: false,
|
|
644
|
+
required: true,
|
|
645
|
+
reason: "stale_or_unapproved_hitl",
|
|
646
|
+
message: "The supplied hitl_request_id is not approved for the current action target or payload. Resolve a matching HITL request, then retry this tool with that id.",
|
|
647
|
+
action: input.action
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
matchesApprovedWorkflowHitl(request, action) {
|
|
651
|
+
return request.status === "approved" && request.scope === action.scope && request.actionType === action.actionType && request.targetRefType === action.targetRefType && request.targetRefId === action.targetRefId && extractActionHash(request.metadata) === action.actionHash;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
function extractActionHash(metadata) {
|
|
655
|
+
const value = metadata.actionHash;
|
|
656
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
657
|
+
}
|
|
658
|
+
function isTokenAuthenticatedActor(actor) {
|
|
659
|
+
if (actor.actorType === "api_token") return true;
|
|
660
|
+
const authMethod = actor.authMetadata.auth_method;
|
|
661
|
+
return authMethod === "api_token" || authMethod === "oauth_token";
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
//#endregion
|
|
665
|
+
//#region src/site-context/schema-hitl.ts
|
|
666
|
+
var SchemaHitlPayloadBuilder = class {
|
|
667
|
+
constructor(db) {
|
|
668
|
+
this.db = db;
|
|
669
|
+
}
|
|
670
|
+
async buildDeleteCollectionRequest(input) {
|
|
671
|
+
const collection = await this.requireCollection(input.slug);
|
|
672
|
+
const collectionSummary = summarizeCollection(collection);
|
|
673
|
+
const actionHash = await buildStableActionHash({
|
|
674
|
+
actionType: "schema.delete_collection",
|
|
675
|
+
scope: collectionScope(collection.slug),
|
|
676
|
+
targetRefType: "collection",
|
|
677
|
+
targetRefId: collection.slug,
|
|
678
|
+
force: input.force === true,
|
|
679
|
+
collection: collectionSummary
|
|
680
|
+
});
|
|
681
|
+
const title = `Approve deleting collection ${collection.slug}`;
|
|
682
|
+
const summary = `Delete collection ${collection.slug} (${collection.label}) and its content table` + (input.force ? " with force enabled." : ".");
|
|
683
|
+
return {
|
|
684
|
+
scope: collectionScope(collection.slug),
|
|
685
|
+
actionType: "schema.delete_collection",
|
|
686
|
+
title,
|
|
687
|
+
summary,
|
|
688
|
+
riskReason: "destructive_schema_change",
|
|
689
|
+
targetRefType: "collection",
|
|
690
|
+
targetRefId: collection.slug,
|
|
691
|
+
priority: "high",
|
|
692
|
+
actionHash,
|
|
693
|
+
metadata: {
|
|
694
|
+
actionHash,
|
|
695
|
+
force: input.force === true
|
|
696
|
+
},
|
|
697
|
+
reviewPayload: {
|
|
698
|
+
kind: "schema_delete_collection",
|
|
699
|
+
actionType: "schema.delete_collection",
|
|
700
|
+
title,
|
|
701
|
+
summary,
|
|
702
|
+
target: {
|
|
703
|
+
scope: collectionScope(collection.slug),
|
|
704
|
+
targetRefType: "collection",
|
|
705
|
+
targetRefId: collection.slug,
|
|
706
|
+
actionHash
|
|
707
|
+
},
|
|
708
|
+
collection: collectionSummary,
|
|
709
|
+
options: { force: input.force === true }
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
async buildDeleteFieldRequest(input) {
|
|
714
|
+
const collection = await this.requireCollection(input.collection);
|
|
715
|
+
const field = collection.fields.find((item) => item.slug === input.fieldSlug);
|
|
716
|
+
if (!field) throw new Error(`Field not found: ${input.collection}.${input.fieldSlug}`);
|
|
717
|
+
const fieldSummary = summarizeField(field);
|
|
718
|
+
const actionHash = await buildStableActionHash({
|
|
719
|
+
actionType: "schema.delete_field",
|
|
720
|
+
scope: collectionScope(collection.slug),
|
|
721
|
+
targetRefType: "collection_field",
|
|
722
|
+
targetRefId: `${collection.slug}:${field.slug}`,
|
|
723
|
+
collection: summarizeCollection(collection),
|
|
724
|
+
field: fieldSummary
|
|
725
|
+
});
|
|
726
|
+
const title = `Approve deleting field ${collection.slug}.${field.slug}`;
|
|
727
|
+
const summary = `Delete field ${field.slug} (${field.label}) from collection ${collection.slug}.`;
|
|
728
|
+
return {
|
|
729
|
+
scope: collectionScope(collection.slug),
|
|
730
|
+
actionType: "schema.delete_field",
|
|
731
|
+
title,
|
|
732
|
+
summary,
|
|
733
|
+
riskReason: "destructive_schema_change",
|
|
734
|
+
targetRefType: "collection_field",
|
|
735
|
+
targetRefId: `${collection.slug}:${field.slug}`,
|
|
736
|
+
priority: "high",
|
|
737
|
+
actionHash,
|
|
738
|
+
metadata: { actionHash },
|
|
739
|
+
reviewPayload: {
|
|
740
|
+
kind: "schema_delete_field",
|
|
741
|
+
actionType: "schema.delete_field",
|
|
742
|
+
title,
|
|
743
|
+
summary,
|
|
744
|
+
target: {
|
|
745
|
+
scope: collectionScope(collection.slug),
|
|
746
|
+
targetRefType: "collection_field",
|
|
747
|
+
targetRefId: `${collection.slug}:${field.slug}`,
|
|
748
|
+
actionHash
|
|
749
|
+
},
|
|
750
|
+
collection: summarizeCollection(collection),
|
|
751
|
+
field: fieldSummary
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
async requireCollection(slug) {
|
|
756
|
+
const collection = await new SchemaRegistry(this.db).getCollectionWithFields(slug);
|
|
757
|
+
if (!collection) throw new Error(`Collection not found: ${slug}`);
|
|
758
|
+
return collection;
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
function collectionScope(slug) {
|
|
762
|
+
return `collection:${slug}`;
|
|
763
|
+
}
|
|
764
|
+
function summarizeCollection(collection) {
|
|
765
|
+
return {
|
|
766
|
+
id: collection.id,
|
|
767
|
+
slug: collection.slug,
|
|
768
|
+
label: collection.label,
|
|
769
|
+
labelSingular: collection.labelSingular,
|
|
770
|
+
description: collection.description,
|
|
771
|
+
icon: collection.icon,
|
|
772
|
+
supports: collection.supports,
|
|
773
|
+
hasSeo: collection.hasSeo,
|
|
774
|
+
commentsEnabled: collection.commentsEnabled,
|
|
775
|
+
fieldCount: collection.fields.length,
|
|
776
|
+
fields: collection.fields.map((field) => summarizeField(field))
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function summarizeField(field) {
|
|
780
|
+
return {
|
|
781
|
+
id: field.id,
|
|
782
|
+
slug: field.slug,
|
|
783
|
+
label: field.label,
|
|
784
|
+
type: field.type,
|
|
785
|
+
required: field.required,
|
|
786
|
+
unique: field.unique,
|
|
787
|
+
searchable: field.searchable,
|
|
788
|
+
translatable: field.translatable,
|
|
789
|
+
defaultValue: field.defaultValue ?? null,
|
|
790
|
+
validation: field.validation ?? null,
|
|
791
|
+
options: field.options ?? null
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
//#endregion
|
|
796
|
+
//#region src/site-context/plugin-hitl.ts
|
|
797
|
+
var PluginHitlPayloadBuilder = class {
|
|
798
|
+
async buildEnableRequest(plugin) {
|
|
799
|
+
return this.build(plugin, {
|
|
800
|
+
actionType: "plugin.enable",
|
|
801
|
+
action: "enable",
|
|
802
|
+
title: `Enable plugin ${plugin.name}`,
|
|
803
|
+
summary: `Activate plugin ${plugin.id} and re-enable its hooks, routes, and scheduled work.`,
|
|
804
|
+
riskReason: "Enabling a plugin can change site behavior, expose routes, and restart plugin automation."
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
async buildDisableRequest(plugin) {
|
|
808
|
+
return this.build(plugin, {
|
|
809
|
+
actionType: "plugin.disable",
|
|
810
|
+
action: "disable",
|
|
811
|
+
title: `Disable plugin ${plugin.name}`,
|
|
812
|
+
summary: `Deactivate plugin ${plugin.id} and stop its hooks, routes, and scheduled work.`,
|
|
813
|
+
riskReason: "Disabling a plugin can remove site functionality and interrupt scheduled or automated behavior."
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
async buildUninstallRequest(plugin, input) {
|
|
817
|
+
const reviewPayload = {
|
|
818
|
+
plugin: summarizePlugin(plugin),
|
|
819
|
+
requestedChange: {
|
|
820
|
+
action: "uninstall",
|
|
821
|
+
deleteData: input.deleteData
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
const actionHash = await buildStableActionHash({
|
|
825
|
+
actionType: "plugin.uninstall",
|
|
826
|
+
plugin: summarizePlugin(plugin),
|
|
827
|
+
requestedChange: reviewPayload.requestedChange
|
|
828
|
+
});
|
|
829
|
+
return {
|
|
830
|
+
scope: pluginScope(plugin.id),
|
|
831
|
+
actionType: "plugin.uninstall",
|
|
832
|
+
title: `Uninstall plugin ${plugin.name}`,
|
|
833
|
+
summary: `Remove plugin ${plugin.id} from the site${input.deleteData ? " and delete its stored data" : ""}.`,
|
|
834
|
+
riskReason: "Uninstalling a plugin removes site functionality and may permanently delete plugin-managed state.",
|
|
835
|
+
reviewPayload,
|
|
836
|
+
targetRefType: "plugin",
|
|
837
|
+
targetRefId: plugin.id,
|
|
838
|
+
priority: "high",
|
|
839
|
+
metadata: { actionHash },
|
|
840
|
+
actionHash
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
async buildInstallRequest(plugin) {
|
|
844
|
+
const reviewPayload = {
|
|
845
|
+
plugin: summarizeMarketplacePlugin(plugin),
|
|
846
|
+
requestedChange: {
|
|
847
|
+
action: "install",
|
|
848
|
+
version: plugin.version
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
const actionHash = await buildStableActionHash({
|
|
852
|
+
actionType: "plugin.install",
|
|
853
|
+
plugin: summarizeMarketplacePlugin(plugin),
|
|
854
|
+
requestedChange: reviewPayload.requestedChange
|
|
855
|
+
});
|
|
856
|
+
return {
|
|
857
|
+
scope: pluginScope(plugin.id),
|
|
858
|
+
actionType: "plugin.install",
|
|
859
|
+
title: `Install marketplace plugin ${plugin.name}`,
|
|
860
|
+
summary: `Install marketplace plugin ${plugin.id} at version ${plugin.version}.`,
|
|
861
|
+
riskReason: "Installing a marketplace plugin downloads remote code, adds new capabilities, and can change site behavior immediately.",
|
|
862
|
+
reviewPayload,
|
|
863
|
+
targetRefType: "plugin",
|
|
864
|
+
targetRefId: plugin.id,
|
|
865
|
+
priority: "high",
|
|
866
|
+
metadata: { actionHash },
|
|
867
|
+
actionHash
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
async buildMarketplaceUpdateRequest(plugin) {
|
|
871
|
+
const reviewPayload = {
|
|
872
|
+
plugin: summarizeMarketplacePlugin(plugin),
|
|
873
|
+
currentVersion: plugin.oldVersion,
|
|
874
|
+
requestedChange: {
|
|
875
|
+
action: "update",
|
|
876
|
+
version: plugin.version
|
|
877
|
+
},
|
|
878
|
+
capabilityChanges: {
|
|
879
|
+
added: [...plugin.capabilityChanges.added],
|
|
880
|
+
removed: [...plugin.capabilityChanges.removed]
|
|
881
|
+
},
|
|
882
|
+
routeVisibilityChanges: { newlyPublic: [...plugin.routeVisibilityChanges.newlyPublic] }
|
|
883
|
+
};
|
|
884
|
+
const actionHash = await buildStableActionHash({
|
|
885
|
+
actionType: "plugin.update",
|
|
886
|
+
plugin: summarizeMarketplacePlugin(plugin),
|
|
887
|
+
currentVersion: plugin.oldVersion,
|
|
888
|
+
requestedChange: reviewPayload.requestedChange,
|
|
889
|
+
capabilityChanges: reviewPayload.capabilityChanges,
|
|
890
|
+
routeVisibilityChanges: reviewPayload.routeVisibilityChanges
|
|
891
|
+
});
|
|
892
|
+
return {
|
|
893
|
+
scope: pluginScope(plugin.id),
|
|
894
|
+
actionType: "plugin.update",
|
|
895
|
+
title: `Update marketplace plugin ${plugin.name}`,
|
|
896
|
+
summary: `Update marketplace plugin ${plugin.id} from ${plugin.oldVersion} to ${plugin.version}.`,
|
|
897
|
+
riskReason: "Updating a marketplace plugin replaces remote code and may add capabilities or expose new public routes.",
|
|
898
|
+
reviewPayload,
|
|
899
|
+
targetRefType: "plugin",
|
|
900
|
+
targetRefId: plugin.id,
|
|
901
|
+
priority: "high",
|
|
902
|
+
metadata: { actionHash },
|
|
903
|
+
actionHash
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
async build(plugin, input) {
|
|
907
|
+
const reviewPayload = {
|
|
908
|
+
plugin: summarizePlugin(plugin),
|
|
909
|
+
requestedChange: { action: input.action }
|
|
910
|
+
};
|
|
911
|
+
const actionHash = await buildStableActionHash({
|
|
912
|
+
actionType: input.actionType,
|
|
913
|
+
plugin: summarizePlugin(plugin),
|
|
914
|
+
requestedChange: reviewPayload.requestedChange
|
|
915
|
+
});
|
|
916
|
+
return {
|
|
917
|
+
scope: pluginScope(plugin.id),
|
|
918
|
+
actionType: input.actionType,
|
|
919
|
+
title: input.title,
|
|
920
|
+
summary: input.summary,
|
|
921
|
+
riskReason: input.riskReason,
|
|
922
|
+
reviewPayload,
|
|
923
|
+
targetRefType: "plugin",
|
|
924
|
+
targetRefId: plugin.id,
|
|
925
|
+
priority: "high",
|
|
926
|
+
metadata: { actionHash },
|
|
927
|
+
actionHash
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
function summarizePlugin(plugin) {
|
|
932
|
+
return {
|
|
933
|
+
id: plugin.id,
|
|
934
|
+
name: plugin.name,
|
|
935
|
+
status: plugin.status,
|
|
936
|
+
source: plugin.source ?? "config",
|
|
937
|
+
version: plugin.marketplaceVersion ?? plugin.version,
|
|
938
|
+
capabilities: [...plugin.capabilities].toSorted(),
|
|
939
|
+
hasAdminPages: plugin.hasAdminPages,
|
|
940
|
+
hasDashboardWidgets: plugin.hasDashboardWidgets,
|
|
941
|
+
hasHooks: plugin.hasHooks
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function summarizeMarketplacePlugin(plugin) {
|
|
945
|
+
return {
|
|
946
|
+
id: plugin.id,
|
|
947
|
+
name: plugin.name,
|
|
948
|
+
description: plugin.description,
|
|
949
|
+
authorName: plugin.authorName,
|
|
950
|
+
version: plugin.version,
|
|
951
|
+
minDinewayVersion: plugin.minDinewayVersion,
|
|
952
|
+
bundleSize: plugin.bundleSize,
|
|
953
|
+
checksum: plugin.checksum,
|
|
954
|
+
changelog: plugin.changelog,
|
|
955
|
+
capabilities: [...plugin.capabilities].toSorted(),
|
|
956
|
+
status: plugin.status,
|
|
957
|
+
auditVerdict: plugin.auditVerdict,
|
|
958
|
+
imageAuditVerdict: plugin.imageAuditVerdict,
|
|
959
|
+
publishedAt: plugin.publishedAt
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function pluginScope(pluginId) {
|
|
963
|
+
return `plugin:${pluginId}`;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
//#endregion
|
|
967
|
+
//#region src/site-context/exclusive-hook-hitl.ts
|
|
968
|
+
var ExclusiveHookHitlPayloadBuilder = class {
|
|
969
|
+
async buildSetSelectionRequest(input) {
|
|
970
|
+
const providers = normalizeProviders(input.providers);
|
|
971
|
+
const actionHash = await buildStableActionHash({
|
|
972
|
+
actionType: "hook.exclusive.update",
|
|
973
|
+
hookName: input.hookName,
|
|
974
|
+
currentPluginId: input.currentPluginId,
|
|
975
|
+
requestedPluginId: input.requestedPluginId,
|
|
976
|
+
providers: providers.map((provider) => provider.pluginId)
|
|
977
|
+
});
|
|
978
|
+
const title = input.requestedPluginId ? `Approve exclusive hook provider change for ${input.hookName}` : `Approve clearing exclusive hook provider for ${input.hookName}`;
|
|
979
|
+
const summary = input.requestedPluginId ? `Select ${input.requestedPluginId} as the provider for ${input.hookName}.` : `Clear the selected provider for ${input.hookName}.`;
|
|
980
|
+
return {
|
|
981
|
+
scope: "site",
|
|
982
|
+
actionType: "hook.exclusive.update",
|
|
983
|
+
title,
|
|
984
|
+
summary,
|
|
985
|
+
riskReason: "exclusive_hook_selection_change",
|
|
986
|
+
targetRefType: "exclusive_hook",
|
|
987
|
+
targetRefId: input.hookName,
|
|
988
|
+
priority: "high",
|
|
989
|
+
metadata: {
|
|
990
|
+
actionHash,
|
|
991
|
+
hookName: input.hookName,
|
|
992
|
+
currentPluginId: input.currentPluginId,
|
|
993
|
+
requestedPluginId: input.requestedPluginId,
|
|
994
|
+
providerIds: providers.map((provider) => provider.pluginId)
|
|
995
|
+
},
|
|
996
|
+
reviewPayload: {
|
|
997
|
+
kind: "exclusive_hook_update",
|
|
998
|
+
actionType: "hook.exclusive.update",
|
|
999
|
+
title,
|
|
1000
|
+
summary,
|
|
1001
|
+
target: {
|
|
1002
|
+
scope: "site",
|
|
1003
|
+
targetRefType: "exclusive_hook",
|
|
1004
|
+
targetRefId: input.hookName,
|
|
1005
|
+
actionHash
|
|
1006
|
+
},
|
|
1007
|
+
hookName: input.hookName,
|
|
1008
|
+
currentPluginId: input.currentPluginId,
|
|
1009
|
+
requestedPluginId: input.requestedPluginId,
|
|
1010
|
+
providers
|
|
1011
|
+
},
|
|
1012
|
+
actionHash
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
function normalizeProviders(providers) {
|
|
1017
|
+
return providers.map((provider) => ({
|
|
1018
|
+
pluginId: provider.pluginId,
|
|
1019
|
+
pluginName: provider.pluginName
|
|
1020
|
+
})).toSorted((left, right) => left.pluginId.localeCompare(right.pluginId));
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
//#endregion
|
|
1024
|
+
//#region src/site-context/import-hitl.ts
|
|
1025
|
+
var WordPressImportHitlPayloadBuilder = class {
|
|
1026
|
+
async buildPrepareRequest(input) {
|
|
1027
|
+
const plan = normalizePreparePlan(input.postTypes);
|
|
1028
|
+
const targetRefId = plan.postTypes.length > 0 ? plan.postTypes.map((item) => item.collection).join(",") : "site";
|
|
1029
|
+
const title = "Approve WordPress schema prepare";
|
|
1030
|
+
const summary = plan.postTypes.length === 1 ? `Create or update import-ready schema for collection ${plan.postTypes[0]?.collection}.` : `Create or update import-ready schema for ${plan.postTypes.length} WordPress collection mappings.`;
|
|
1031
|
+
const actionHash = await buildStableActionHash({
|
|
1032
|
+
actionType: "import.wordpress.prepare",
|
|
1033
|
+
plan
|
|
1034
|
+
});
|
|
1035
|
+
return {
|
|
1036
|
+
scope: "site",
|
|
1037
|
+
actionType: "import.wordpress.prepare",
|
|
1038
|
+
title,
|
|
1039
|
+
summary,
|
|
1040
|
+
riskReason: "Preparing WordPress import schema can create collections, add fields, and enable search behavior.",
|
|
1041
|
+
reviewPayload: {
|
|
1042
|
+
kind: "wordpress_import_prepare",
|
|
1043
|
+
actionType: "import.wordpress.prepare",
|
|
1044
|
+
title,
|
|
1045
|
+
summary,
|
|
1046
|
+
target: {
|
|
1047
|
+
scope: "site",
|
|
1048
|
+
targetRefType: "wordpress_import_plan",
|
|
1049
|
+
targetRefId,
|
|
1050
|
+
actionHash
|
|
1051
|
+
},
|
|
1052
|
+
plan
|
|
1053
|
+
},
|
|
1054
|
+
targetRefType: "wordpress_import_plan",
|
|
1055
|
+
targetRefId,
|
|
1056
|
+
priority: "high",
|
|
1057
|
+
metadata: {
|
|
1058
|
+
actionHash,
|
|
1059
|
+
collectionCount: plan.postTypes.length
|
|
1060
|
+
},
|
|
1061
|
+
actionHash
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
async buildExecuteRequest(input) {
|
|
1065
|
+
const fileDigest = await sha256BufferHex(input.fileBuffer);
|
|
1066
|
+
const config = normalizeExecuteConfig(input.config);
|
|
1067
|
+
const targetRefId = `wxr:${fileDigest}`;
|
|
1068
|
+
const enabledMappings = Object.entries(config.postTypeMappings).filter(([, mapping]) => mapping.enabled);
|
|
1069
|
+
const title = "Approve WordPress WXR import";
|
|
1070
|
+
const summary = enabledMappings.length === 0 ? "Run a WordPress WXR import with no enabled post-type mappings." : `Run a WordPress WXR import into ${enabledMappings.length} enabled Dineway collection mappings.`;
|
|
1071
|
+
const actionHash = await buildStableActionHash({
|
|
1072
|
+
actionType: "import.wordpress.execute",
|
|
1073
|
+
fileDigest,
|
|
1074
|
+
config
|
|
1075
|
+
});
|
|
1076
|
+
return {
|
|
1077
|
+
scope: "site",
|
|
1078
|
+
actionType: "import.wordpress.execute",
|
|
1079
|
+
title,
|
|
1080
|
+
summary,
|
|
1081
|
+
riskReason: "Running a WordPress WXR import can create or overwrite large amounts of site content in bulk.",
|
|
1082
|
+
reviewPayload: {
|
|
1083
|
+
kind: "wordpress_import_execute",
|
|
1084
|
+
actionType: "import.wordpress.execute",
|
|
1085
|
+
title,
|
|
1086
|
+
summary,
|
|
1087
|
+
target: {
|
|
1088
|
+
scope: "site",
|
|
1089
|
+
targetRefType: "wordpress_import_file",
|
|
1090
|
+
targetRefId,
|
|
1091
|
+
actionHash
|
|
1092
|
+
},
|
|
1093
|
+
file: {
|
|
1094
|
+
name: input.fileName,
|
|
1095
|
+
digest: fileDigest
|
|
1096
|
+
},
|
|
1097
|
+
config
|
|
1098
|
+
},
|
|
1099
|
+
targetRefType: "wordpress_import_file",
|
|
1100
|
+
targetRefId,
|
|
1101
|
+
priority: "high",
|
|
1102
|
+
metadata: {
|
|
1103
|
+
actionHash,
|
|
1104
|
+
fileDigest,
|
|
1105
|
+
fileName: input.fileName
|
|
1106
|
+
},
|
|
1107
|
+
actionHash
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
async buildPluginExecuteRequest(input) {
|
|
1111
|
+
const source = normalizePluginExecuteSource(input.siteUrl, input.items);
|
|
1112
|
+
const config = normalizeExecuteConfig(input.config);
|
|
1113
|
+
const enabledMappings = Object.entries(config.postTypeMappings).filter(([, mapping]) => mapping.enabled);
|
|
1114
|
+
const targetRefId = source.siteUrl;
|
|
1115
|
+
const sourceFingerprint = await buildStableActionHash(source.fingerprintItems);
|
|
1116
|
+
const title = "Approve WordPress plugin import";
|
|
1117
|
+
const summary = enabledMappings.length === 0 ? `Run a WordPress plugin import from ${source.siteUrl} with no enabled post-type mappings.` : `Run a WordPress plugin import from ${source.siteUrl} into ${enabledMappings.length} enabled Dineway collection mappings using ${source.totalItems} reviewed remote items.`;
|
|
1118
|
+
const actionHash = await buildStableActionHash({
|
|
1119
|
+
actionType: "import.wordpress_plugin.execute",
|
|
1120
|
+
siteUrl: source.siteUrl,
|
|
1121
|
+
sourceFingerprint,
|
|
1122
|
+
config
|
|
1123
|
+
});
|
|
1124
|
+
return {
|
|
1125
|
+
scope: "site",
|
|
1126
|
+
actionType: "import.wordpress_plugin.execute",
|
|
1127
|
+
title,
|
|
1128
|
+
summary,
|
|
1129
|
+
riskReason: "Running a remote WordPress plugin import can create or overwrite large amounts of site content in bulk from a live external source.",
|
|
1130
|
+
reviewPayload: {
|
|
1131
|
+
kind: "wordpress_plugin_import_execute",
|
|
1132
|
+
actionType: "import.wordpress_plugin.execute",
|
|
1133
|
+
title,
|
|
1134
|
+
summary,
|
|
1135
|
+
target: {
|
|
1136
|
+
scope: "site",
|
|
1137
|
+
targetRefType: "wordpress_plugin_source",
|
|
1138
|
+
targetRefId,
|
|
1139
|
+
actionHash
|
|
1140
|
+
},
|
|
1141
|
+
source: {
|
|
1142
|
+
siteUrl: source.siteUrl,
|
|
1143
|
+
totalItems: source.totalItems,
|
|
1144
|
+
postTypes: source.postTypes,
|
|
1145
|
+
fingerprint: sourceFingerprint
|
|
1146
|
+
},
|
|
1147
|
+
config
|
|
1148
|
+
},
|
|
1149
|
+
targetRefType: "wordpress_plugin_source",
|
|
1150
|
+
targetRefId,
|
|
1151
|
+
priority: "high",
|
|
1152
|
+
metadata: {
|
|
1153
|
+
actionHash,
|
|
1154
|
+
sourceFingerprint,
|
|
1155
|
+
siteUrl: source.siteUrl,
|
|
1156
|
+
itemCount: source.totalItems
|
|
1157
|
+
},
|
|
1158
|
+
actionHash
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
async buildRewriteUrlsRequest(input) {
|
|
1162
|
+
const rewrite = normalizeRewriteRequest(input);
|
|
1163
|
+
const targetRefId = rewrite.collections?.length ? rewrite.collections.join(",") : "all";
|
|
1164
|
+
const title = "Approve WordPress URL rewrite";
|
|
1165
|
+
const summary = rewrite.collections?.length ? `Rewrite imported WordPress URLs across ${rewrite.collections.length} selected collections.` : "Rewrite imported WordPress URLs across all collections.";
|
|
1166
|
+
const actionHash = await buildStableActionHash({
|
|
1167
|
+
actionType: "import.wordpress.rewrite_urls",
|
|
1168
|
+
rewrite
|
|
1169
|
+
});
|
|
1170
|
+
return {
|
|
1171
|
+
scope: "site",
|
|
1172
|
+
actionType: "import.wordpress.rewrite_urls",
|
|
1173
|
+
title,
|
|
1174
|
+
summary,
|
|
1175
|
+
riskReason: "Rewriting imported WordPress URLs updates existing content in bulk and can change many references at once.",
|
|
1176
|
+
reviewPayload: {
|
|
1177
|
+
kind: "wordpress_import_rewrite_urls",
|
|
1178
|
+
actionType: "import.wordpress.rewrite_urls",
|
|
1179
|
+
title,
|
|
1180
|
+
summary,
|
|
1181
|
+
target: {
|
|
1182
|
+
scope: "site",
|
|
1183
|
+
targetRefType: "wordpress_url_rewrite",
|
|
1184
|
+
targetRefId,
|
|
1185
|
+
actionHash
|
|
1186
|
+
},
|
|
1187
|
+
rewrite
|
|
1188
|
+
},
|
|
1189
|
+
targetRefType: "wordpress_url_rewrite",
|
|
1190
|
+
targetRefId,
|
|
1191
|
+
priority: "high",
|
|
1192
|
+
metadata: {
|
|
1193
|
+
actionHash,
|
|
1194
|
+
urlCount: Object.keys(rewrite.urlMap).length
|
|
1195
|
+
},
|
|
1196
|
+
actionHash
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
function normalizePreparePlan(postTypes) {
|
|
1201
|
+
return { postTypes: postTypes.map((postType) => ({
|
|
1202
|
+
name: postType.name,
|
|
1203
|
+
collection: sanitizeWordPressImportSlug(postType.collection),
|
|
1204
|
+
fields: (postType.fields ?? []).map((field) => ({
|
|
1205
|
+
slug: sanitizeWordPressImportSlug(field.slug),
|
|
1206
|
+
label: field.label,
|
|
1207
|
+
type: field.type,
|
|
1208
|
+
required: field.required,
|
|
1209
|
+
searchable: field.searchable ?? false
|
|
1210
|
+
}))
|
|
1211
|
+
})) };
|
|
1212
|
+
}
|
|
1213
|
+
function normalizeExecuteConfig(input) {
|
|
1214
|
+
return {
|
|
1215
|
+
postTypeMappings: Object.fromEntries(Object.entries(input.postTypeMappings).map(([postType, mapping]) => [postType, {
|
|
1216
|
+
collection: sanitizeWordPressImportSlug(mapping.collection),
|
|
1217
|
+
enabled: mapping.enabled
|
|
1218
|
+
}])),
|
|
1219
|
+
skipExisting: input.skipExisting,
|
|
1220
|
+
importSections: input.importSections !== false,
|
|
1221
|
+
authorMappings: input.authorMappings ?? null,
|
|
1222
|
+
locale: input.locale ?? null
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
function normalizeRewriteRequest(input) {
|
|
1226
|
+
return {
|
|
1227
|
+
urlMap: input.urlMap,
|
|
1228
|
+
collections: input.collections && input.collections.length > 0 ? [...new Set(input.collections.map((collection) => sanitizeWordPressImportSlug(collection)))].toSorted() : null
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
function normalizePluginExecuteSource(siteUrl, items) {
|
|
1232
|
+
const normalizedSiteUrl = normalizeUrl(siteUrl);
|
|
1233
|
+
const fingerprintItems = items.map((item) => ({
|
|
1234
|
+
sourceId: String(item.sourceId),
|
|
1235
|
+
postType: item.postType,
|
|
1236
|
+
status: item.status,
|
|
1237
|
+
slug: item.slug,
|
|
1238
|
+
title: item.title,
|
|
1239
|
+
content: item.content,
|
|
1240
|
+
excerpt: item.excerpt ?? null,
|
|
1241
|
+
featuredImage: item.featuredImage ?? null,
|
|
1242
|
+
author: item.author ?? null,
|
|
1243
|
+
date: normalizeItemDate(item.date),
|
|
1244
|
+
locale: item.locale ?? null,
|
|
1245
|
+
translationGroup: item.translationGroup ?? null
|
|
1246
|
+
})).toSorted(comparePluginFingerprintItems);
|
|
1247
|
+
const postTypes = summarizePluginPostTypes(fingerprintItems);
|
|
1248
|
+
return {
|
|
1249
|
+
siteUrl: normalizedSiteUrl,
|
|
1250
|
+
totalItems: fingerprintItems.length,
|
|
1251
|
+
postTypes,
|
|
1252
|
+
fingerprintItems
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
function summarizePluginPostTypes(items) {
|
|
1256
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1257
|
+
for (const item of items) {
|
|
1258
|
+
const existing = grouped.get(item.postType) ?? {
|
|
1259
|
+
count: 0,
|
|
1260
|
+
statuses: /* @__PURE__ */ new Map(),
|
|
1261
|
+
newestDate: null
|
|
1262
|
+
};
|
|
1263
|
+
existing.count += 1;
|
|
1264
|
+
existing.statuses.set(item.status, (existing.statuses.get(item.status) ?? 0) + 1);
|
|
1265
|
+
if (item.date && (!existing.newestDate || item.date > existing.newestDate)) existing.newestDate = item.date;
|
|
1266
|
+
grouped.set(item.postType, existing);
|
|
1267
|
+
}
|
|
1268
|
+
return Array.from(grouped.entries(), ([postType, summary]) => ({
|
|
1269
|
+
postType,
|
|
1270
|
+
count: summary.count,
|
|
1271
|
+
newestDate: summary.newestDate,
|
|
1272
|
+
statuses: Object.fromEntries([...summary.statuses.entries()].toSorted(([left], [right]) => left.localeCompare(right)))
|
|
1273
|
+
})).toSorted((left, right) => left.postType.localeCompare(right.postType));
|
|
1274
|
+
}
|
|
1275
|
+
function comparePluginFingerprintItems(left, right) {
|
|
1276
|
+
return left.postType.localeCompare(right.postType) || (left.locale ?? "").localeCompare(right.locale ?? "") || left.slug.localeCompare(right.slug) || left.sourceId.localeCompare(right.sourceId);
|
|
1277
|
+
}
|
|
1278
|
+
function normalizeItemDate(value) {
|
|
1279
|
+
if (!value) return null;
|
|
1280
|
+
const time = value.getTime();
|
|
1281
|
+
return Number.isNaN(time) ? null : value.toISOString();
|
|
1282
|
+
}
|
|
1283
|
+
async function sha256BufferHex(buffer) {
|
|
1284
|
+
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
1285
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
//#endregion
|
|
1289
|
+
//#region src/site-context/settings-hitl.ts
|
|
1290
|
+
var SettingsHitlPayloadBuilder = class {
|
|
1291
|
+
constructor(db) {
|
|
1292
|
+
this.db = db;
|
|
1293
|
+
}
|
|
1294
|
+
async buildUpdateSettingsRequest(input) {
|
|
1295
|
+
const current = normalizeSiteSettings(await getSiteSettingsWithDb(this.db));
|
|
1296
|
+
const requestedChange = normalizeSiteSettings(input);
|
|
1297
|
+
const changedKeys = Object.keys(requestedChange).toSorted();
|
|
1298
|
+
const seoOnly = changedKeys.length === 1 && changedKeys[0] === "seo";
|
|
1299
|
+
const actionHash = await buildStableActionHash({
|
|
1300
|
+
actionType: "settings.update",
|
|
1301
|
+
current,
|
|
1302
|
+
requestedChange
|
|
1303
|
+
});
|
|
1304
|
+
const title = seoOnly ? "Approve updating site SEO defaults" : "Approve updating site settings";
|
|
1305
|
+
const summary = seoOnly ? "Update site-level SEO defaults and verification settings." : `Update site settings: ${formatChangedKeys(changedKeys)}.`;
|
|
1306
|
+
return {
|
|
1307
|
+
scope: "site",
|
|
1308
|
+
actionType: "settings.update",
|
|
1309
|
+
title,
|
|
1310
|
+
summary,
|
|
1311
|
+
riskReason: seoOnly ? "seo_strategy_change" : "site_configuration_change",
|
|
1312
|
+
targetRefType: "site_settings",
|
|
1313
|
+
targetRefId: "site",
|
|
1314
|
+
priority: "high",
|
|
1315
|
+
metadata: {
|
|
1316
|
+
actionHash,
|
|
1317
|
+
changedKeys,
|
|
1318
|
+
touchesSeo: changedKeys.includes("seo")
|
|
1319
|
+
},
|
|
1320
|
+
reviewPayload: {
|
|
1321
|
+
kind: "site_settings_update",
|
|
1322
|
+
actionType: "settings.update",
|
|
1323
|
+
title,
|
|
1324
|
+
summary,
|
|
1325
|
+
target: {
|
|
1326
|
+
scope: "site",
|
|
1327
|
+
targetRefType: "site_settings",
|
|
1328
|
+
targetRefId: "site",
|
|
1329
|
+
actionHash
|
|
1330
|
+
},
|
|
1331
|
+
changedKeys,
|
|
1332
|
+
current,
|
|
1333
|
+
requestedChange
|
|
1334
|
+
},
|
|
1335
|
+
actionHash
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
function normalizeSiteSettings(input) {
|
|
1340
|
+
return {
|
|
1341
|
+
title: input.title,
|
|
1342
|
+
tagline: input.tagline,
|
|
1343
|
+
logo: normalizeMediaReference(input.logo),
|
|
1344
|
+
favicon: normalizeMediaReference(input.favicon),
|
|
1345
|
+
url: input.url,
|
|
1346
|
+
postsPerPage: input.postsPerPage,
|
|
1347
|
+
dateFormat: input.dateFormat,
|
|
1348
|
+
timezone: input.timezone,
|
|
1349
|
+
social: input.social ? {
|
|
1350
|
+
twitter: input.social.twitter,
|
|
1351
|
+
github: input.social.github,
|
|
1352
|
+
facebook: input.social.facebook,
|
|
1353
|
+
instagram: input.social.instagram,
|
|
1354
|
+
linkedin: input.social.linkedin,
|
|
1355
|
+
youtube: input.social.youtube
|
|
1356
|
+
} : void 0,
|
|
1357
|
+
seo: input.seo ? {
|
|
1358
|
+
titleSeparator: input.seo.titleSeparator,
|
|
1359
|
+
defaultOgImage: normalizeMediaReference(input.seo.defaultOgImage),
|
|
1360
|
+
robotsTxt: input.seo.robotsTxt,
|
|
1361
|
+
googleVerification: input.seo.googleVerification,
|
|
1362
|
+
bingVerification: input.seo.bingVerification
|
|
1363
|
+
} : void 0
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function normalizeMediaReference(value) {
|
|
1367
|
+
if (!value) return void 0;
|
|
1368
|
+
return {
|
|
1369
|
+
mediaId: value.mediaId,
|
|
1370
|
+
alt: value.alt
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
function formatChangedKeys(keys) {
|
|
1374
|
+
if (keys.length === 0) return "no fields";
|
|
1375
|
+
if (keys.length === 1) return keys[0];
|
|
1376
|
+
return keys.join(", ");
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
//#endregion
|
|
1380
|
+
//#region src/site-context/redirect-hitl.ts
|
|
1381
|
+
var RedirectHitlPayloadBuilder = class {
|
|
1382
|
+
async buildCreateRedirectRequest(input) {
|
|
1383
|
+
const requestedChange = normalizeCreateRedirect(input);
|
|
1384
|
+
const actionHash = await buildStableActionHash({
|
|
1385
|
+
actionType: "redirect.create",
|
|
1386
|
+
requestedChange
|
|
1387
|
+
});
|
|
1388
|
+
const title = `Approve creating redirect ${input.source}`;
|
|
1389
|
+
const summary = `Create redirect ${input.source} -> ${input.destination} (${requestedChange.type}).`;
|
|
1390
|
+
return {
|
|
1391
|
+
scope: "site",
|
|
1392
|
+
actionType: "redirect.create",
|
|
1393
|
+
title,
|
|
1394
|
+
summary,
|
|
1395
|
+
riskReason: "seo_redirect_change",
|
|
1396
|
+
targetRefType: "redirect",
|
|
1397
|
+
targetRefId: input.source,
|
|
1398
|
+
priority: "high",
|
|
1399
|
+
metadata: {
|
|
1400
|
+
actionHash,
|
|
1401
|
+
source: requestedChange.source,
|
|
1402
|
+
destination: requestedChange.destination,
|
|
1403
|
+
redirectType: requestedChange.type,
|
|
1404
|
+
enabled: requestedChange.enabled,
|
|
1405
|
+
isPattern: requestedChange.isPattern
|
|
1406
|
+
},
|
|
1407
|
+
reviewPayload: {
|
|
1408
|
+
kind: "redirect_create",
|
|
1409
|
+
actionType: "redirect.create",
|
|
1410
|
+
title,
|
|
1411
|
+
summary,
|
|
1412
|
+
target: {
|
|
1413
|
+
scope: "site",
|
|
1414
|
+
targetRefType: "redirect",
|
|
1415
|
+
targetRefId: input.source,
|
|
1416
|
+
actionHash
|
|
1417
|
+
},
|
|
1418
|
+
requestedChange
|
|
1419
|
+
},
|
|
1420
|
+
actionHash
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
async buildUpdateRedirectRequest(input) {
|
|
1424
|
+
const current = normalizeRedirect(input.redirect);
|
|
1425
|
+
const requestedChange = normalizeUpdateRedirect(input);
|
|
1426
|
+
const changedKeys = Object.keys(requestedChange).toSorted();
|
|
1427
|
+
const actionHash = await buildStableActionHash({
|
|
1428
|
+
actionType: "redirect.update",
|
|
1429
|
+
current,
|
|
1430
|
+
requestedChange
|
|
1431
|
+
});
|
|
1432
|
+
const title = `Approve updating redirect ${input.redirect.source}`;
|
|
1433
|
+
const summary = `Update redirect ${input.redirect.source} -> ${input.redirect.destination}.`;
|
|
1434
|
+
return {
|
|
1435
|
+
scope: "site",
|
|
1436
|
+
actionType: "redirect.update",
|
|
1437
|
+
title,
|
|
1438
|
+
summary,
|
|
1439
|
+
riskReason: "seo_redirect_change",
|
|
1440
|
+
targetRefType: "redirect",
|
|
1441
|
+
targetRefId: input.redirect.id,
|
|
1442
|
+
priority: "high",
|
|
1443
|
+
metadata: {
|
|
1444
|
+
actionHash,
|
|
1445
|
+
redirectId: input.redirect.id,
|
|
1446
|
+
source: input.redirect.source,
|
|
1447
|
+
destination: input.redirect.destination,
|
|
1448
|
+
changedKeys
|
|
1449
|
+
},
|
|
1450
|
+
reviewPayload: {
|
|
1451
|
+
kind: "redirect_update",
|
|
1452
|
+
actionType: "redirect.update",
|
|
1453
|
+
title,
|
|
1454
|
+
summary,
|
|
1455
|
+
target: {
|
|
1456
|
+
scope: "site",
|
|
1457
|
+
targetRefType: "redirect",
|
|
1458
|
+
targetRefId: input.redirect.id,
|
|
1459
|
+
actionHash
|
|
1460
|
+
},
|
|
1461
|
+
changedKeys,
|
|
1462
|
+
current,
|
|
1463
|
+
requestedChange
|
|
1464
|
+
},
|
|
1465
|
+
actionHash
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
async buildDeleteRedirectRequest(input) {
|
|
1469
|
+
const current = normalizeRedirect(input.redirect);
|
|
1470
|
+
const actionHash = await buildStableActionHash({
|
|
1471
|
+
actionType: "redirect.delete",
|
|
1472
|
+
current
|
|
1473
|
+
});
|
|
1474
|
+
const title = `Approve deleting redirect ${input.redirect.source}`;
|
|
1475
|
+
const summary = `Delete redirect ${input.redirect.source} -> ${input.redirect.destination}.`;
|
|
1476
|
+
return {
|
|
1477
|
+
scope: "site",
|
|
1478
|
+
actionType: "redirect.delete",
|
|
1479
|
+
title,
|
|
1480
|
+
summary,
|
|
1481
|
+
riskReason: "seo_redirect_change",
|
|
1482
|
+
targetRefType: "redirect",
|
|
1483
|
+
targetRefId: input.redirect.id,
|
|
1484
|
+
priority: "high",
|
|
1485
|
+
metadata: {
|
|
1486
|
+
actionHash,
|
|
1487
|
+
redirectId: input.redirect.id,
|
|
1488
|
+
source: input.redirect.source,
|
|
1489
|
+
destination: input.redirect.destination
|
|
1490
|
+
},
|
|
1491
|
+
reviewPayload: {
|
|
1492
|
+
kind: "redirect_delete",
|
|
1493
|
+
actionType: "redirect.delete",
|
|
1494
|
+
title,
|
|
1495
|
+
summary,
|
|
1496
|
+
target: {
|
|
1497
|
+
scope: "site",
|
|
1498
|
+
targetRefType: "redirect",
|
|
1499
|
+
targetRefId: input.redirect.id,
|
|
1500
|
+
actionHash
|
|
1501
|
+
},
|
|
1502
|
+
current
|
|
1503
|
+
},
|
|
1504
|
+
actionHash
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
function normalizeCreateRedirect(input) {
|
|
1509
|
+
return {
|
|
1510
|
+
source: input.source,
|
|
1511
|
+
destination: input.destination,
|
|
1512
|
+
type: input.type ?? 301,
|
|
1513
|
+
enabled: input.enabled ?? true,
|
|
1514
|
+
groupName: input.groupName ?? null,
|
|
1515
|
+
isPattern: isPattern(input.source),
|
|
1516
|
+
auto: false
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
function normalizeUpdateRedirect(input) {
|
|
1520
|
+
const requestedChange = {};
|
|
1521
|
+
if (input.source !== void 0) {
|
|
1522
|
+
requestedChange.source = input.source;
|
|
1523
|
+
requestedChange.isPattern = isPattern(input.source);
|
|
1524
|
+
}
|
|
1525
|
+
if (input.destination !== void 0) requestedChange.destination = input.destination;
|
|
1526
|
+
if (input.type !== void 0) requestedChange.type = input.type;
|
|
1527
|
+
if (input.enabled !== void 0) requestedChange.enabled = input.enabled;
|
|
1528
|
+
if (input.groupName !== void 0) requestedChange.groupName = input.groupName;
|
|
1529
|
+
return requestedChange;
|
|
1530
|
+
}
|
|
1531
|
+
function normalizeRedirect(redirect) {
|
|
1532
|
+
return {
|
|
1533
|
+
id: redirect.id,
|
|
1534
|
+
source: redirect.source,
|
|
1535
|
+
destination: redirect.destination,
|
|
1536
|
+
type: redirect.type,
|
|
1537
|
+
isPattern: redirect.isPattern,
|
|
1538
|
+
enabled: redirect.enabled,
|
|
1539
|
+
groupName: redirect.groupName,
|
|
1540
|
+
auto: redirect.auto
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
//#endregion
|
|
1545
|
+
//#region src/site-context/menu-hitl.ts
|
|
1546
|
+
var MenuHitlPayloadBuilder = class {
|
|
1547
|
+
constructor(db) {
|
|
1548
|
+
this.db = db;
|
|
1549
|
+
}
|
|
1550
|
+
async buildCreateMenuRequest(input) {
|
|
1551
|
+
const requestedChange = normalizeCreateMenuInput(input);
|
|
1552
|
+
const actionHash = await buildStableActionHash({
|
|
1553
|
+
actionType: "menu.create",
|
|
1554
|
+
requestedChange
|
|
1555
|
+
});
|
|
1556
|
+
const title = `Approve creating menu ${input.name}`;
|
|
1557
|
+
const summary = `Create navigation menu ${input.name} (${input.label}).`;
|
|
1558
|
+
return {
|
|
1559
|
+
scope: "site",
|
|
1560
|
+
actionType: "menu.create",
|
|
1561
|
+
title,
|
|
1562
|
+
summary,
|
|
1563
|
+
riskReason: "navigation_restructure",
|
|
1564
|
+
targetRefType: "menu",
|
|
1565
|
+
targetRefId: input.name,
|
|
1566
|
+
priority: "high",
|
|
1567
|
+
metadata: {
|
|
1568
|
+
actionHash,
|
|
1569
|
+
menuName: input.name
|
|
1570
|
+
},
|
|
1571
|
+
reviewPayload: {
|
|
1572
|
+
kind: "menu_create",
|
|
1573
|
+
actionType: "menu.create",
|
|
1574
|
+
title,
|
|
1575
|
+
summary,
|
|
1576
|
+
target: {
|
|
1577
|
+
scope: "site",
|
|
1578
|
+
targetRefType: "menu",
|
|
1579
|
+
targetRefId: input.name,
|
|
1580
|
+
actionHash
|
|
1581
|
+
},
|
|
1582
|
+
requestedChange
|
|
1583
|
+
},
|
|
1584
|
+
actionHash
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
async buildUpdateMenuRequest(input) {
|
|
1588
|
+
const snapshot = await this.requireMenuSnapshot(input.name);
|
|
1589
|
+
const requestedChange = normalizeUpdateMenuInput(input);
|
|
1590
|
+
const current = summarizeMenuSnapshot(snapshot);
|
|
1591
|
+
const actionHash = await buildStableActionHash({
|
|
1592
|
+
actionType: "menu.update",
|
|
1593
|
+
current,
|
|
1594
|
+
requestedChange
|
|
1595
|
+
});
|
|
1596
|
+
const title = `Approve updating menu ${snapshot.menu.name}`;
|
|
1597
|
+
const summary = requestedChange.label !== null ? `Update navigation menu ${snapshot.menu.name} label to ${requestedChange.label}.` : `Update navigation menu ${snapshot.menu.name}.`;
|
|
1598
|
+
return {
|
|
1599
|
+
scope: "site",
|
|
1600
|
+
actionType: "menu.update",
|
|
1601
|
+
title,
|
|
1602
|
+
summary,
|
|
1603
|
+
riskReason: "navigation_restructure",
|
|
1604
|
+
targetRefType: "menu",
|
|
1605
|
+
targetRefId: snapshot.menu.name,
|
|
1606
|
+
priority: "high",
|
|
1607
|
+
metadata: {
|
|
1608
|
+
actionHash,
|
|
1609
|
+
menuName: snapshot.menu.name,
|
|
1610
|
+
menuId: snapshot.menu.id
|
|
1611
|
+
},
|
|
1612
|
+
reviewPayload: {
|
|
1613
|
+
kind: "menu_update",
|
|
1614
|
+
actionType: "menu.update",
|
|
1615
|
+
title,
|
|
1616
|
+
summary,
|
|
1617
|
+
target: {
|
|
1618
|
+
scope: "site",
|
|
1619
|
+
targetRefType: "menu",
|
|
1620
|
+
targetRefId: snapshot.menu.name,
|
|
1621
|
+
actionHash
|
|
1622
|
+
},
|
|
1623
|
+
current,
|
|
1624
|
+
requestedChange
|
|
1625
|
+
},
|
|
1626
|
+
actionHash
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
async buildDeleteMenuRequest(input) {
|
|
1630
|
+
const snapshot = await this.requireMenuSnapshot(input.name);
|
|
1631
|
+
const current = summarizeMenuSnapshot(snapshot);
|
|
1632
|
+
const actionHash = await buildStableActionHash({
|
|
1633
|
+
actionType: "menu.delete",
|
|
1634
|
+
current
|
|
1635
|
+
});
|
|
1636
|
+
const title = `Approve deleting menu ${snapshot.menu.name}`;
|
|
1637
|
+
const summary = current.itemCount > 0 ? `Delete navigation menu ${snapshot.menu.name} and remove ${current.itemCount} menu items.` : `Delete navigation menu ${snapshot.menu.name}.`;
|
|
1638
|
+
return {
|
|
1639
|
+
scope: "site",
|
|
1640
|
+
actionType: "menu.delete",
|
|
1641
|
+
title,
|
|
1642
|
+
summary,
|
|
1643
|
+
riskReason: "navigation_restructure",
|
|
1644
|
+
targetRefType: "menu",
|
|
1645
|
+
targetRefId: snapshot.menu.name,
|
|
1646
|
+
priority: "high",
|
|
1647
|
+
metadata: {
|
|
1648
|
+
actionHash,
|
|
1649
|
+
menuName: snapshot.menu.name,
|
|
1650
|
+
menuId: snapshot.menu.id
|
|
1651
|
+
},
|
|
1652
|
+
reviewPayload: {
|
|
1653
|
+
kind: "menu_delete",
|
|
1654
|
+
actionType: "menu.delete",
|
|
1655
|
+
title,
|
|
1656
|
+
summary,
|
|
1657
|
+
target: {
|
|
1658
|
+
scope: "site",
|
|
1659
|
+
targetRefType: "menu",
|
|
1660
|
+
targetRefId: snapshot.menu.name,
|
|
1661
|
+
actionHash
|
|
1662
|
+
},
|
|
1663
|
+
current
|
|
1664
|
+
},
|
|
1665
|
+
actionHash
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
async buildCreateMenuItemRequest(input) {
|
|
1669
|
+
const snapshot = await this.requireMenuSnapshot(input.menuName);
|
|
1670
|
+
const current = summarizeMenuSnapshot(snapshot);
|
|
1671
|
+
const requestedChange = normalizeCreateMenuItemInput(input);
|
|
1672
|
+
const actionHash = await buildStableActionHash({
|
|
1673
|
+
actionType: "menu.create_item",
|
|
1674
|
+
current,
|
|
1675
|
+
requestedChange
|
|
1676
|
+
});
|
|
1677
|
+
const title = `Approve adding item to menu ${snapshot.menu.name}`;
|
|
1678
|
+
const summary = `Add navigation item ${requestedChange.label} to menu ${snapshot.menu.name}.`;
|
|
1679
|
+
return {
|
|
1680
|
+
scope: "site",
|
|
1681
|
+
actionType: "menu.create_item",
|
|
1682
|
+
title,
|
|
1683
|
+
summary,
|
|
1684
|
+
riskReason: "navigation_restructure",
|
|
1685
|
+
targetRefType: "menu",
|
|
1686
|
+
targetRefId: snapshot.menu.name,
|
|
1687
|
+
priority: "high",
|
|
1688
|
+
metadata: {
|
|
1689
|
+
actionHash,
|
|
1690
|
+
menuName: snapshot.menu.name,
|
|
1691
|
+
menuId: snapshot.menu.id
|
|
1692
|
+
},
|
|
1693
|
+
reviewPayload: {
|
|
1694
|
+
kind: "menu_create_item",
|
|
1695
|
+
actionType: "menu.create_item",
|
|
1696
|
+
title,
|
|
1697
|
+
summary,
|
|
1698
|
+
target: {
|
|
1699
|
+
scope: "site",
|
|
1700
|
+
targetRefType: "menu",
|
|
1701
|
+
targetRefId: snapshot.menu.name,
|
|
1702
|
+
actionHash
|
|
1703
|
+
},
|
|
1704
|
+
current,
|
|
1705
|
+
requestedChange
|
|
1706
|
+
},
|
|
1707
|
+
actionHash
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
async buildUpdateMenuItemRequest(input) {
|
|
1711
|
+
const snapshot = await this.requireMenuSnapshot(input.menuName);
|
|
1712
|
+
const item = requireMenuItem(snapshot, input.itemId);
|
|
1713
|
+
const current = summarizeMenuSnapshot(snapshot);
|
|
1714
|
+
const currentItem = summarizeMenuItem(item);
|
|
1715
|
+
const requestedChange = normalizeUpdateMenuItemInput(input);
|
|
1716
|
+
const actionHash = await buildStableActionHash({
|
|
1717
|
+
actionType: "menu.update_item",
|
|
1718
|
+
current,
|
|
1719
|
+
currentItem,
|
|
1720
|
+
requestedChange
|
|
1721
|
+
});
|
|
1722
|
+
const title = `Approve updating menu item ${item.label}`;
|
|
1723
|
+
const summary = `Update navigation item ${item.id} in menu ${snapshot.menu.name}.`;
|
|
1724
|
+
return {
|
|
1725
|
+
scope: "site",
|
|
1726
|
+
actionType: "menu.update_item",
|
|
1727
|
+
title,
|
|
1728
|
+
summary,
|
|
1729
|
+
riskReason: "navigation_restructure",
|
|
1730
|
+
targetRefType: "menu_item",
|
|
1731
|
+
targetRefId: item.id,
|
|
1732
|
+
priority: "high",
|
|
1733
|
+
metadata: {
|
|
1734
|
+
actionHash,
|
|
1735
|
+
menuName: snapshot.menu.name,
|
|
1736
|
+
menuId: snapshot.menu.id,
|
|
1737
|
+
itemId: item.id
|
|
1738
|
+
},
|
|
1739
|
+
reviewPayload: {
|
|
1740
|
+
kind: "menu_update_item",
|
|
1741
|
+
actionType: "menu.update_item",
|
|
1742
|
+
title,
|
|
1743
|
+
summary,
|
|
1744
|
+
target: {
|
|
1745
|
+
scope: "site",
|
|
1746
|
+
targetRefType: "menu_item",
|
|
1747
|
+
targetRefId: item.id,
|
|
1748
|
+
actionHash
|
|
1749
|
+
},
|
|
1750
|
+
current,
|
|
1751
|
+
currentItem,
|
|
1752
|
+
requestedChange
|
|
1753
|
+
},
|
|
1754
|
+
actionHash
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
async buildDeleteMenuItemRequest(input) {
|
|
1758
|
+
const snapshot = await this.requireMenuSnapshot(input.menuName);
|
|
1759
|
+
const item = requireMenuItem(snapshot, input.itemId);
|
|
1760
|
+
const current = summarizeMenuSnapshot(snapshot);
|
|
1761
|
+
const currentItem = summarizeMenuItem(item);
|
|
1762
|
+
const actionHash = await buildStableActionHash({
|
|
1763
|
+
actionType: "menu.delete_item",
|
|
1764
|
+
current,
|
|
1765
|
+
currentItem
|
|
1766
|
+
});
|
|
1767
|
+
const title = `Approve deleting menu item ${item.label}`;
|
|
1768
|
+
const summary = `Delete navigation item ${item.id} from menu ${snapshot.menu.name}.`;
|
|
1769
|
+
return {
|
|
1770
|
+
scope: "site",
|
|
1771
|
+
actionType: "menu.delete_item",
|
|
1772
|
+
title,
|
|
1773
|
+
summary,
|
|
1774
|
+
riskReason: "navigation_restructure",
|
|
1775
|
+
targetRefType: "menu_item",
|
|
1776
|
+
targetRefId: item.id,
|
|
1777
|
+
priority: "high",
|
|
1778
|
+
metadata: {
|
|
1779
|
+
actionHash,
|
|
1780
|
+
menuName: snapshot.menu.name,
|
|
1781
|
+
menuId: snapshot.menu.id,
|
|
1782
|
+
itemId: item.id
|
|
1783
|
+
},
|
|
1784
|
+
reviewPayload: {
|
|
1785
|
+
kind: "menu_delete_item",
|
|
1786
|
+
actionType: "menu.delete_item",
|
|
1787
|
+
title,
|
|
1788
|
+
summary,
|
|
1789
|
+
target: {
|
|
1790
|
+
scope: "site",
|
|
1791
|
+
targetRefType: "menu_item",
|
|
1792
|
+
targetRefId: item.id,
|
|
1793
|
+
actionHash
|
|
1794
|
+
},
|
|
1795
|
+
current,
|
|
1796
|
+
currentItem
|
|
1797
|
+
},
|
|
1798
|
+
actionHash
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
async buildReorderMenuItemsRequest(input) {
|
|
1802
|
+
const snapshot = await this.requireMenuSnapshot(input.menuName);
|
|
1803
|
+
const current = summarizeMenuSnapshot(snapshot);
|
|
1804
|
+
const requestedChange = normalizeReorderMenuItemsInput(input.items);
|
|
1805
|
+
const actionHash = await buildStableActionHash({
|
|
1806
|
+
actionType: "menu.reorder",
|
|
1807
|
+
current,
|
|
1808
|
+
requestedChange
|
|
1809
|
+
});
|
|
1810
|
+
const title = `Approve reordering menu ${snapshot.menu.name}`;
|
|
1811
|
+
const summary = requestedChange.items.length > 0 ? `Reorder ${requestedChange.items.length} navigation items in menu ${snapshot.menu.name}.` : `Reorder navigation items in menu ${snapshot.menu.name}.`;
|
|
1812
|
+
return {
|
|
1813
|
+
scope: "site",
|
|
1814
|
+
actionType: "menu.reorder",
|
|
1815
|
+
title,
|
|
1816
|
+
summary,
|
|
1817
|
+
riskReason: "navigation_restructure",
|
|
1818
|
+
targetRefType: "menu",
|
|
1819
|
+
targetRefId: snapshot.menu.name,
|
|
1820
|
+
priority: "high",
|
|
1821
|
+
metadata: {
|
|
1822
|
+
actionHash,
|
|
1823
|
+
menuName: snapshot.menu.name,
|
|
1824
|
+
menuId: snapshot.menu.id,
|
|
1825
|
+
itemCount: requestedChange.items.length
|
|
1826
|
+
},
|
|
1827
|
+
reviewPayload: {
|
|
1828
|
+
kind: "menu_reorder",
|
|
1829
|
+
actionType: "menu.reorder",
|
|
1830
|
+
title,
|
|
1831
|
+
summary,
|
|
1832
|
+
target: {
|
|
1833
|
+
scope: "site",
|
|
1834
|
+
targetRefType: "menu",
|
|
1835
|
+
targetRefId: snapshot.menu.name,
|
|
1836
|
+
actionHash
|
|
1837
|
+
},
|
|
1838
|
+
current,
|
|
1839
|
+
requestedChange
|
|
1840
|
+
},
|
|
1841
|
+
actionHash
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
async requireMenuSnapshot(name) {
|
|
1845
|
+
const menu = await this.db.selectFrom("_dineway_menus").selectAll().where("name", "=", name).executeTakeFirst();
|
|
1846
|
+
if (!menu) throw new Error(`Menu not found: ${name}`);
|
|
1847
|
+
return {
|
|
1848
|
+
menu,
|
|
1849
|
+
items: await this.db.selectFrom("_dineway_menu_items").selectAll().where("menu_id", "=", menu.id).orderBy("sort_order", "asc").orderBy("id", "asc").execute()
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
};
|
|
1853
|
+
function requireMenuItem(snapshot, itemId) {
|
|
1854
|
+
const item = snapshot.items.find((entry) => entry.id === itemId);
|
|
1855
|
+
if (!item) throw new Error(`Menu item not found: ${itemId}`);
|
|
1856
|
+
return item;
|
|
1857
|
+
}
|
|
1858
|
+
function summarizeMenuSnapshot(snapshot) {
|
|
1859
|
+
return {
|
|
1860
|
+
id: snapshot.menu.id,
|
|
1861
|
+
name: snapshot.menu.name,
|
|
1862
|
+
label: snapshot.menu.label,
|
|
1863
|
+
itemCount: snapshot.items.length,
|
|
1864
|
+
items: snapshot.items.map((item) => summarizeMenuItem(item))
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
function summarizeMenuItem(item) {
|
|
1868
|
+
return {
|
|
1869
|
+
id: item.id,
|
|
1870
|
+
parentId: item.parent_id,
|
|
1871
|
+
sortOrder: item.sort_order,
|
|
1872
|
+
type: item.type,
|
|
1873
|
+
referenceCollection: item.reference_collection,
|
|
1874
|
+
referenceId: item.reference_id,
|
|
1875
|
+
customUrl: item.custom_url,
|
|
1876
|
+
label: item.label,
|
|
1877
|
+
titleAttr: item.title_attr,
|
|
1878
|
+
target: item.target,
|
|
1879
|
+
cssClasses: item.css_classes
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
function normalizeCreateMenuInput(input) {
|
|
1883
|
+
return {
|
|
1884
|
+
name: input.name,
|
|
1885
|
+
label: input.label
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
function normalizeUpdateMenuInput(input) {
|
|
1889
|
+
return { label: input.label ?? null };
|
|
1890
|
+
}
|
|
1891
|
+
function normalizeCreateMenuItemInput(input) {
|
|
1892
|
+
return {
|
|
1893
|
+
type: input.type,
|
|
1894
|
+
label: input.label,
|
|
1895
|
+
referenceCollection: input.referenceCollection ?? null,
|
|
1896
|
+
referenceId: input.referenceId ?? null,
|
|
1897
|
+
customUrl: input.customUrl ?? null,
|
|
1898
|
+
target: input.target ?? null,
|
|
1899
|
+
titleAttr: input.titleAttr ?? null,
|
|
1900
|
+
cssClasses: input.cssClasses ?? null,
|
|
1901
|
+
parentId: input.parentId ?? null,
|
|
1902
|
+
sortOrder: input.sortOrder ?? null
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
function normalizeUpdateMenuItemInput(input) {
|
|
1906
|
+
return {
|
|
1907
|
+
label: input.label ?? null,
|
|
1908
|
+
customUrl: input.customUrl ?? null,
|
|
1909
|
+
target: input.target ?? null,
|
|
1910
|
+
titleAttr: input.titleAttr ?? null,
|
|
1911
|
+
cssClasses: input.cssClasses ?? null,
|
|
1912
|
+
parentId: input.parentId ?? null,
|
|
1913
|
+
sortOrder: input.sortOrder ?? null
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
function normalizeReorderMenuItemsInput(items) {
|
|
1917
|
+
return { items: items.map((item) => ({
|
|
1918
|
+
id: item.id,
|
|
1919
|
+
parentId: item.parentId,
|
|
1920
|
+
sortOrder: item.sortOrder
|
|
1921
|
+
})).toSorted((left, right) => left.id.localeCompare(right.id)) };
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
//#endregion
|
|
1925
|
+
//#region src/site-context/taxonomy-hitl.ts
|
|
1926
|
+
var TaxonomyHitlPayloadBuilder = class {
|
|
1927
|
+
constructor(db) {
|
|
1928
|
+
this.db = db;
|
|
1929
|
+
}
|
|
1930
|
+
async loadTaxonomyDefinition(name) {
|
|
1931
|
+
const row = await this.db.selectFrom("_dineway_taxonomy_defs").selectAll().where("name", "=", name).executeTakeFirst();
|
|
1932
|
+
return row ? normalizeTaxonomyDefinition(row) : null;
|
|
1933
|
+
}
|
|
1934
|
+
async buildCreateTaxonomyRequest(input) {
|
|
1935
|
+
const requestedChange = normalizeCreateTaxonomyInput(input);
|
|
1936
|
+
const actionHash = await buildStableActionHash({
|
|
1937
|
+
actionType: "taxonomy.create",
|
|
1938
|
+
requestedChange
|
|
1939
|
+
});
|
|
1940
|
+
const title = `Approve creating taxonomy ${requestedChange.name}`;
|
|
1941
|
+
const summary = requestedChange.collections.length > 0 ? `Create taxonomy ${requestedChange.name} (${requestedChange.label}) for ${requestedChange.collections.join(", ")}.` : `Create taxonomy ${requestedChange.name} (${requestedChange.label}).`;
|
|
1942
|
+
return {
|
|
1943
|
+
scope: "site",
|
|
1944
|
+
actionType: "taxonomy.create",
|
|
1945
|
+
title,
|
|
1946
|
+
summary,
|
|
1947
|
+
riskReason: "taxonomy_structure_change",
|
|
1948
|
+
targetRefType: "taxonomy",
|
|
1949
|
+
targetRefId: requestedChange.name,
|
|
1950
|
+
priority: "high",
|
|
1951
|
+
metadata: {
|
|
1952
|
+
actionHash,
|
|
1953
|
+
taxonomyName: requestedChange.name,
|
|
1954
|
+
hierarchical: requestedChange.hierarchical,
|
|
1955
|
+
collections: requestedChange.collections
|
|
1956
|
+
},
|
|
1957
|
+
reviewPayload: {
|
|
1958
|
+
kind: "taxonomy_create",
|
|
1959
|
+
actionType: "taxonomy.create",
|
|
1960
|
+
title,
|
|
1961
|
+
summary,
|
|
1962
|
+
target: {
|
|
1963
|
+
scope: "site",
|
|
1964
|
+
targetRefType: "taxonomy",
|
|
1965
|
+
targetRefId: requestedChange.name,
|
|
1966
|
+
actionHash
|
|
1967
|
+
},
|
|
1968
|
+
requestedChange
|
|
1969
|
+
},
|
|
1970
|
+
actionHash
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
async buildCreateTermRequest(input) {
|
|
1974
|
+
const currentTaxonomy = normalizeTaxonomyDefinitionInput(input.taxonomy);
|
|
1975
|
+
const requestedChange = normalizeCreateTermInput(input);
|
|
1976
|
+
const actionHash = await buildStableActionHash({
|
|
1977
|
+
actionType: "taxonomy.term_create",
|
|
1978
|
+
currentTaxonomy,
|
|
1979
|
+
requestedChange
|
|
1980
|
+
});
|
|
1981
|
+
const title = `Approve creating term ${requestedChange.slug} in ${input.taxonomy.name}`;
|
|
1982
|
+
const summary = `Create taxonomy term ${requestedChange.label} in ${input.taxonomy.name}.`;
|
|
1983
|
+
return {
|
|
1984
|
+
scope: "site",
|
|
1985
|
+
actionType: "taxonomy.term_create",
|
|
1986
|
+
title,
|
|
1987
|
+
summary,
|
|
1988
|
+
riskReason: "taxonomy_structure_change",
|
|
1989
|
+
targetRefType: "taxonomy_term",
|
|
1990
|
+
targetRefId: taxonomyTermRefId(input.taxonomy.name, requestedChange.slug),
|
|
1991
|
+
priority: "high",
|
|
1992
|
+
metadata: {
|
|
1993
|
+
actionHash,
|
|
1994
|
+
taxonomyId: input.taxonomy.id,
|
|
1995
|
+
taxonomyName: input.taxonomy.name,
|
|
1996
|
+
termSlug: requestedChange.slug
|
|
1997
|
+
},
|
|
1998
|
+
reviewPayload: {
|
|
1999
|
+
kind: "taxonomy_term_create",
|
|
2000
|
+
actionType: "taxonomy.term_create",
|
|
2001
|
+
title,
|
|
2002
|
+
summary,
|
|
2003
|
+
target: {
|
|
2004
|
+
scope: "site",
|
|
2005
|
+
targetRefType: "taxonomy_term",
|
|
2006
|
+
targetRefId: taxonomyTermRefId(input.taxonomy.name, requestedChange.slug),
|
|
2007
|
+
actionHash
|
|
2008
|
+
},
|
|
2009
|
+
currentTaxonomy,
|
|
2010
|
+
requestedChange
|
|
2011
|
+
},
|
|
2012
|
+
actionHash
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
async buildUpdateTermRequest(input) {
|
|
2016
|
+
const currentTaxonomy = normalizeTaxonomyDefinitionInput(input.taxonomy);
|
|
2017
|
+
const current = normalizeTerm(input.currentTerm);
|
|
2018
|
+
const requestedChange = normalizeUpdateTermInput(input);
|
|
2019
|
+
const changedKeys = Object.keys(requestedChange).toSorted();
|
|
2020
|
+
const actionHash = await buildStableActionHash({
|
|
2021
|
+
actionType: "taxonomy.term_update",
|
|
2022
|
+
currentTaxonomy,
|
|
2023
|
+
current,
|
|
2024
|
+
requestedChange
|
|
2025
|
+
});
|
|
2026
|
+
const title = `Approve updating term ${input.currentTerm.slug} in ${input.taxonomy.name}`;
|
|
2027
|
+
const summary = `Update taxonomy term ${input.currentTerm.slug} in ${input.taxonomy.name}.`;
|
|
2028
|
+
return {
|
|
2029
|
+
scope: "site",
|
|
2030
|
+
actionType: "taxonomy.term_update",
|
|
2031
|
+
title,
|
|
2032
|
+
summary,
|
|
2033
|
+
riskReason: "taxonomy_structure_change",
|
|
2034
|
+
targetRefType: "taxonomy_term",
|
|
2035
|
+
targetRefId: input.currentTerm.id,
|
|
2036
|
+
priority: "high",
|
|
2037
|
+
metadata: {
|
|
2038
|
+
actionHash,
|
|
2039
|
+
taxonomyId: input.taxonomy.id,
|
|
2040
|
+
taxonomyName: input.taxonomy.name,
|
|
2041
|
+
termId: input.currentTerm.id,
|
|
2042
|
+
termSlug: input.currentTerm.slug,
|
|
2043
|
+
changedKeys
|
|
2044
|
+
},
|
|
2045
|
+
reviewPayload: {
|
|
2046
|
+
kind: "taxonomy_term_update",
|
|
2047
|
+
actionType: "taxonomy.term_update",
|
|
2048
|
+
title,
|
|
2049
|
+
summary,
|
|
2050
|
+
target: {
|
|
2051
|
+
scope: "site",
|
|
2052
|
+
targetRefType: "taxonomy_term",
|
|
2053
|
+
targetRefId: input.currentTerm.id,
|
|
2054
|
+
actionHash
|
|
2055
|
+
},
|
|
2056
|
+
changedKeys,
|
|
2057
|
+
currentTaxonomy,
|
|
2058
|
+
current,
|
|
2059
|
+
requestedChange
|
|
2060
|
+
},
|
|
2061
|
+
actionHash
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
async buildDeleteTermRequest(input) {
|
|
2065
|
+
const currentTaxonomy = normalizeTaxonomyDefinitionInput(input.taxonomy);
|
|
2066
|
+
const current = normalizeTerm(input.currentTerm);
|
|
2067
|
+
const actionHash = await buildStableActionHash({
|
|
2068
|
+
actionType: "taxonomy.term_delete",
|
|
2069
|
+
currentTaxonomy,
|
|
2070
|
+
current
|
|
2071
|
+
});
|
|
2072
|
+
const title = `Approve deleting term ${input.currentTerm.slug} from ${input.taxonomy.name}`;
|
|
2073
|
+
const summary = `Delete taxonomy term ${input.currentTerm.slug} from ${input.taxonomy.name}.`;
|
|
2074
|
+
return {
|
|
2075
|
+
scope: "site",
|
|
2076
|
+
actionType: "taxonomy.term_delete",
|
|
2077
|
+
title,
|
|
2078
|
+
summary,
|
|
2079
|
+
riskReason: "taxonomy_structure_change",
|
|
2080
|
+
targetRefType: "taxonomy_term",
|
|
2081
|
+
targetRefId: input.currentTerm.id,
|
|
2082
|
+
priority: "high",
|
|
2083
|
+
metadata: {
|
|
2084
|
+
actionHash,
|
|
2085
|
+
taxonomyId: input.taxonomy.id,
|
|
2086
|
+
taxonomyName: input.taxonomy.name,
|
|
2087
|
+
termId: input.currentTerm.id,
|
|
2088
|
+
termSlug: input.currentTerm.slug
|
|
2089
|
+
},
|
|
2090
|
+
reviewPayload: {
|
|
2091
|
+
kind: "taxonomy_term_delete",
|
|
2092
|
+
actionType: "taxonomy.term_delete",
|
|
2093
|
+
title,
|
|
2094
|
+
summary,
|
|
2095
|
+
target: {
|
|
2096
|
+
scope: "site",
|
|
2097
|
+
targetRefType: "taxonomy_term",
|
|
2098
|
+
targetRefId: input.currentTerm.id,
|
|
2099
|
+
actionHash
|
|
2100
|
+
},
|
|
2101
|
+
currentTaxonomy,
|
|
2102
|
+
current
|
|
2103
|
+
},
|
|
2104
|
+
actionHash
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
};
|
|
2108
|
+
function normalizeTaxonomyDefinition(row) {
|
|
2109
|
+
return {
|
|
2110
|
+
id: row.id,
|
|
2111
|
+
name: row.name,
|
|
2112
|
+
label: row.label,
|
|
2113
|
+
labelSingular: row.label_singular,
|
|
2114
|
+
hierarchical: row.hierarchical === 1,
|
|
2115
|
+
collections: normalizeCollections(row.collections ? JSON.parse(row.collections) : [])
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
function normalizeTaxonomyDefinitionInput(taxonomy) {
|
|
2119
|
+
return {
|
|
2120
|
+
id: taxonomy.id,
|
|
2121
|
+
name: taxonomy.name,
|
|
2122
|
+
label: taxonomy.label,
|
|
2123
|
+
labelSingular: taxonomy.labelSingular,
|
|
2124
|
+
hierarchical: taxonomy.hierarchical,
|
|
2125
|
+
collections: normalizeCollections(taxonomy.collections)
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
function normalizeCreateTaxonomyInput(input) {
|
|
2129
|
+
return {
|
|
2130
|
+
name: input.name,
|
|
2131
|
+
label: input.label,
|
|
2132
|
+
hierarchical: input.hierarchical ?? false,
|
|
2133
|
+
collections: normalizeCollections(input.collections ?? [])
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
function normalizeCreateTermInput(input) {
|
|
2137
|
+
return {
|
|
2138
|
+
slug: input.slug,
|
|
2139
|
+
label: input.label,
|
|
2140
|
+
parentId: input.parentId ?? null,
|
|
2141
|
+
description: input.description ?? null
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
function normalizeUpdateTermInput(input) {
|
|
2145
|
+
const requestedChange = {};
|
|
2146
|
+
if (input.slug !== void 0) requestedChange.slug = input.slug;
|
|
2147
|
+
if (input.label !== void 0) requestedChange.label = input.label;
|
|
2148
|
+
if (input.parentId !== void 0) requestedChange.parentId = input.parentId;
|
|
2149
|
+
if (input.description !== void 0) requestedChange.description = input.description;
|
|
2150
|
+
return requestedChange;
|
|
2151
|
+
}
|
|
2152
|
+
function normalizeTerm(term) {
|
|
2153
|
+
return {
|
|
2154
|
+
id: term.id,
|
|
2155
|
+
name: term.name,
|
|
2156
|
+
slug: term.slug,
|
|
2157
|
+
label: term.label,
|
|
2158
|
+
parentId: term.parentId,
|
|
2159
|
+
description: term.description ?? null
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
function normalizeCollections(collections) {
|
|
2163
|
+
if (!Array.isArray(collections)) return [];
|
|
2164
|
+
return [...new Set(collections.filter((value) => typeof value === "string"))].toSorted();
|
|
2165
|
+
}
|
|
2166
|
+
function taxonomyTermRefId(taxonomyName, termSlug) {
|
|
2167
|
+
return `${taxonomyName}:${termSlug}`;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
//#endregion
|
|
2171
|
+
//#region src/site-context/section-hitl.ts
|
|
2172
|
+
var SectionHitlPayloadBuilder = class {
|
|
2173
|
+
constructor(db) {
|
|
2174
|
+
this.db = db;
|
|
2175
|
+
}
|
|
2176
|
+
async loadSectionSnapshot(slug) {
|
|
2177
|
+
const row = await this.db.selectFrom("_dineway_sections").selectAll().where("slug", "=", slug).executeTakeFirst();
|
|
2178
|
+
return row ? normalizeSection(row) : null;
|
|
2179
|
+
}
|
|
2180
|
+
async buildCreateSectionRequest(input) {
|
|
2181
|
+
const requestedChange = normalizeCreateSectionInput(input);
|
|
2182
|
+
const actionHash = await buildStableActionHash({
|
|
2183
|
+
actionType: "section.create",
|
|
2184
|
+
requestedChange
|
|
2185
|
+
});
|
|
2186
|
+
const title = `Approve creating section ${requestedChange.slug}`;
|
|
2187
|
+
const summary = `Create reusable section ${requestedChange.slug} (${requestedChange.title}).`;
|
|
2188
|
+
return {
|
|
2189
|
+
scope: "site",
|
|
2190
|
+
actionType: "section.create",
|
|
2191
|
+
title,
|
|
2192
|
+
summary,
|
|
2193
|
+
riskReason: "reusable_content_change",
|
|
2194
|
+
targetRefType: "section",
|
|
2195
|
+
targetRefId: requestedChange.slug,
|
|
2196
|
+
priority: "high",
|
|
2197
|
+
metadata: {
|
|
2198
|
+
actionHash,
|
|
2199
|
+
sectionSlug: requestedChange.slug,
|
|
2200
|
+
source: requestedChange.source,
|
|
2201
|
+
themeId: requestedChange.themeId
|
|
2202
|
+
},
|
|
2203
|
+
reviewPayload: {
|
|
2204
|
+
kind: "section_create",
|
|
2205
|
+
actionType: "section.create",
|
|
2206
|
+
title,
|
|
2207
|
+
summary,
|
|
2208
|
+
target: {
|
|
2209
|
+
scope: "site",
|
|
2210
|
+
targetRefType: "section",
|
|
2211
|
+
targetRefId: requestedChange.slug,
|
|
2212
|
+
actionHash
|
|
2213
|
+
},
|
|
2214
|
+
requestedChange
|
|
2215
|
+
},
|
|
2216
|
+
actionHash
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
async buildUpdateSectionRequest(input) {
|
|
2220
|
+
const current = normalizeSectionInput(input.section);
|
|
2221
|
+
const requestedChange = normalizeUpdateSectionInput(input);
|
|
2222
|
+
const changedKeys = Object.keys(requestedChange).toSorted();
|
|
2223
|
+
const actionHash = await buildStableActionHash({
|
|
2224
|
+
actionType: "section.update",
|
|
2225
|
+
current,
|
|
2226
|
+
requestedChange
|
|
2227
|
+
});
|
|
2228
|
+
const title = `Approve updating section ${input.section.slug}`;
|
|
2229
|
+
const summary = `Update reusable section ${input.section.slug}.`;
|
|
2230
|
+
return {
|
|
2231
|
+
scope: "site",
|
|
2232
|
+
actionType: "section.update",
|
|
2233
|
+
title,
|
|
2234
|
+
summary,
|
|
2235
|
+
riskReason: "reusable_content_change",
|
|
2236
|
+
targetRefType: "section",
|
|
2237
|
+
targetRefId: input.section.id,
|
|
2238
|
+
priority: "high",
|
|
2239
|
+
metadata: {
|
|
2240
|
+
actionHash,
|
|
2241
|
+
sectionId: input.section.id,
|
|
2242
|
+
sectionSlug: input.section.slug,
|
|
2243
|
+
source: input.section.source,
|
|
2244
|
+
themeId: input.section.themeId,
|
|
2245
|
+
changedKeys
|
|
2246
|
+
},
|
|
2247
|
+
reviewPayload: {
|
|
2248
|
+
kind: "section_update",
|
|
2249
|
+
actionType: "section.update",
|
|
2250
|
+
title,
|
|
2251
|
+
summary,
|
|
2252
|
+
target: {
|
|
2253
|
+
scope: "site",
|
|
2254
|
+
targetRefType: "section",
|
|
2255
|
+
targetRefId: input.section.id,
|
|
2256
|
+
actionHash
|
|
2257
|
+
},
|
|
2258
|
+
changedKeys,
|
|
2259
|
+
current,
|
|
2260
|
+
requestedChange
|
|
2261
|
+
},
|
|
2262
|
+
actionHash
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
async buildDeleteSectionRequest(input) {
|
|
2266
|
+
const current = normalizeSectionInput(input.section);
|
|
2267
|
+
const actionHash = await buildStableActionHash({
|
|
2268
|
+
actionType: "section.delete",
|
|
2269
|
+
current
|
|
2270
|
+
});
|
|
2271
|
+
const title = `Approve deleting section ${input.section.slug}`;
|
|
2272
|
+
const summary = `Delete reusable section ${input.section.slug}.`;
|
|
2273
|
+
return {
|
|
2274
|
+
scope: "site",
|
|
2275
|
+
actionType: "section.delete",
|
|
2276
|
+
title,
|
|
2277
|
+
summary,
|
|
2278
|
+
riskReason: "reusable_content_change",
|
|
2279
|
+
targetRefType: "section",
|
|
2280
|
+
targetRefId: input.section.id,
|
|
2281
|
+
priority: "high",
|
|
2282
|
+
metadata: {
|
|
2283
|
+
actionHash,
|
|
2284
|
+
sectionId: input.section.id,
|
|
2285
|
+
sectionSlug: input.section.slug,
|
|
2286
|
+
source: input.section.source,
|
|
2287
|
+
themeId: input.section.themeId
|
|
2288
|
+
},
|
|
2289
|
+
reviewPayload: {
|
|
2290
|
+
kind: "section_delete",
|
|
2291
|
+
actionType: "section.delete",
|
|
2292
|
+
title,
|
|
2293
|
+
summary,
|
|
2294
|
+
target: {
|
|
2295
|
+
scope: "site",
|
|
2296
|
+
targetRefType: "section",
|
|
2297
|
+
targetRefId: input.section.id,
|
|
2298
|
+
actionHash
|
|
2299
|
+
},
|
|
2300
|
+
current
|
|
2301
|
+
},
|
|
2302
|
+
actionHash
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
};
|
|
2306
|
+
function normalizeSection(row) {
|
|
2307
|
+
return {
|
|
2308
|
+
id: row.id,
|
|
2309
|
+
slug: row.slug,
|
|
2310
|
+
title: row.title,
|
|
2311
|
+
description: row.description,
|
|
2312
|
+
keywords: parseStringArray(row.keywords),
|
|
2313
|
+
content: parseUnknownArray$1(row.content),
|
|
2314
|
+
previewMediaId: row.preview_media_id,
|
|
2315
|
+
source: row.source,
|
|
2316
|
+
themeId: row.theme_id
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
function normalizeSectionInput(section) {
|
|
2320
|
+
return {
|
|
2321
|
+
id: section.id,
|
|
2322
|
+
slug: section.slug,
|
|
2323
|
+
title: section.title,
|
|
2324
|
+
description: section.description,
|
|
2325
|
+
keywords: [...section.keywords],
|
|
2326
|
+
content: [...section.content],
|
|
2327
|
+
previewMediaId: section.previewMediaId,
|
|
2328
|
+
source: section.source,
|
|
2329
|
+
themeId: section.themeId
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2332
|
+
function normalizeCreateSectionInput(input) {
|
|
2333
|
+
return {
|
|
2334
|
+
slug: input.slug,
|
|
2335
|
+
title: input.title,
|
|
2336
|
+
description: input.description ?? null,
|
|
2337
|
+
keywords: input.keywords ?? [],
|
|
2338
|
+
content: input.content,
|
|
2339
|
+
previewMediaId: input.previewMediaId ?? null,
|
|
2340
|
+
source: input.source ?? "user",
|
|
2341
|
+
themeId: input.themeId ?? null
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
function normalizeUpdateSectionInput(input) {
|
|
2345
|
+
const requestedChange = {};
|
|
2346
|
+
if (input.slug !== void 0) requestedChange.slug = input.slug;
|
|
2347
|
+
if (input.title !== void 0) requestedChange.title = input.title;
|
|
2348
|
+
if (input.description !== void 0) requestedChange.description = input.description;
|
|
2349
|
+
if (input.keywords !== void 0) requestedChange.keywords = input.keywords;
|
|
2350
|
+
if (input.content !== void 0) requestedChange.content = input.content;
|
|
2351
|
+
if (input.previewMediaId !== void 0) requestedChange.previewMediaId = input.previewMediaId;
|
|
2352
|
+
return requestedChange;
|
|
2353
|
+
}
|
|
2354
|
+
function parseStringArray(value) {
|
|
2355
|
+
if (!value) return [];
|
|
2356
|
+
try {
|
|
2357
|
+
const parsed = JSON.parse(value);
|
|
2358
|
+
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
|
|
2359
|
+
} catch {
|
|
2360
|
+
return [];
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
function parseUnknownArray$1(value) {
|
|
2364
|
+
try {
|
|
2365
|
+
const parsed = JSON.parse(value);
|
|
2366
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
2367
|
+
} catch {
|
|
2368
|
+
return [];
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
//#endregion
|
|
2373
|
+
//#region src/site-context/widget-hitl.ts
|
|
2374
|
+
var WidgetHitlPayloadBuilder = class {
|
|
2375
|
+
constructor(db) {
|
|
2376
|
+
this.db = db;
|
|
2377
|
+
}
|
|
2378
|
+
async loadWidgetAreaSnapshot(name) {
|
|
2379
|
+
const area = await this.db.selectFrom("_dineway_widget_areas").selectAll().where("name", "=", name).executeTakeFirst();
|
|
2380
|
+
if (!area) return null;
|
|
2381
|
+
const widgets = await this.db.selectFrom("_dineway_widgets").selectAll().where("area_id", "=", area.id).orderBy("sort_order", "asc").execute();
|
|
2382
|
+
return {
|
|
2383
|
+
...normalizeWidgetArea(area),
|
|
2384
|
+
widgets: widgets.map(normalizeWidget)
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
async buildCreateWidgetAreaRequest(input) {
|
|
2388
|
+
const requestedChange = normalizeCreateWidgetAreaInput(input);
|
|
2389
|
+
const actionHash = await buildStableActionHash({
|
|
2390
|
+
actionType: "widget_area.create",
|
|
2391
|
+
requestedChange
|
|
2392
|
+
});
|
|
2393
|
+
const title = `Approve creating widget area ${requestedChange.name}`;
|
|
2394
|
+
const summary = `Create widget area ${requestedChange.name} (${requestedChange.label}).`;
|
|
2395
|
+
return {
|
|
2396
|
+
scope: "site",
|
|
2397
|
+
actionType: "widget_area.create",
|
|
2398
|
+
title,
|
|
2399
|
+
summary,
|
|
2400
|
+
riskReason: "layout_structure_change",
|
|
2401
|
+
targetRefType: "widget_area",
|
|
2402
|
+
targetRefId: requestedChange.name,
|
|
2403
|
+
priority: "high",
|
|
2404
|
+
metadata: {
|
|
2405
|
+
actionHash,
|
|
2406
|
+
areaName: requestedChange.name
|
|
2407
|
+
},
|
|
2408
|
+
reviewPayload: {
|
|
2409
|
+
kind: "widget_area_create",
|
|
2410
|
+
actionType: "widget_area.create",
|
|
2411
|
+
title,
|
|
2412
|
+
summary,
|
|
2413
|
+
target: {
|
|
2414
|
+
scope: "site",
|
|
2415
|
+
targetRefType: "widget_area",
|
|
2416
|
+
targetRefId: requestedChange.name,
|
|
2417
|
+
actionHash
|
|
2418
|
+
},
|
|
2419
|
+
requestedChange
|
|
2420
|
+
},
|
|
2421
|
+
actionHash
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
async buildDeleteWidgetAreaRequest(input) {
|
|
2425
|
+
const current = normalizeWidgetAreaInput(input.area);
|
|
2426
|
+
const actionHash = await buildStableActionHash({
|
|
2427
|
+
actionType: "widget_area.delete",
|
|
2428
|
+
current
|
|
2429
|
+
});
|
|
2430
|
+
const title = `Approve deleting widget area ${input.area.name}`;
|
|
2431
|
+
const summary = input.area.widgets.length > 0 ? `Delete widget area ${input.area.name} and remove ${input.area.widgets.length} widgets.` : `Delete widget area ${input.area.name}.`;
|
|
2432
|
+
return {
|
|
2433
|
+
scope: "site",
|
|
2434
|
+
actionType: "widget_area.delete",
|
|
2435
|
+
title,
|
|
2436
|
+
summary,
|
|
2437
|
+
riskReason: "layout_structure_change",
|
|
2438
|
+
targetRefType: "widget_area",
|
|
2439
|
+
targetRefId: input.area.id,
|
|
2440
|
+
priority: "high",
|
|
2441
|
+
metadata: {
|
|
2442
|
+
actionHash,
|
|
2443
|
+
areaId: input.area.id,
|
|
2444
|
+
areaName: input.area.name,
|
|
2445
|
+
widgetCount: input.area.widgets.length
|
|
2446
|
+
},
|
|
2447
|
+
reviewPayload: {
|
|
2448
|
+
kind: "widget_area_delete",
|
|
2449
|
+
actionType: "widget_area.delete",
|
|
2450
|
+
title,
|
|
2451
|
+
summary,
|
|
2452
|
+
target: {
|
|
2453
|
+
scope: "site",
|
|
2454
|
+
targetRefType: "widget_area",
|
|
2455
|
+
targetRefId: input.area.id,
|
|
2456
|
+
actionHash
|
|
2457
|
+
},
|
|
2458
|
+
current
|
|
2459
|
+
},
|
|
2460
|
+
actionHash
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
async buildCreateWidgetRequest(input) {
|
|
2464
|
+
const currentArea = normalizeWidgetAreaInput(input.area);
|
|
2465
|
+
const requestedChange = normalizeCreateWidgetInput(input);
|
|
2466
|
+
const actionHash = await buildStableActionHash({
|
|
2467
|
+
actionType: "widget.create",
|
|
2468
|
+
currentArea,
|
|
2469
|
+
requestedChange
|
|
2470
|
+
});
|
|
2471
|
+
const title = `Approve adding widget to ${input.area.name}`;
|
|
2472
|
+
const summary = `Add ${requestedChange.type} widget to area ${input.area.name}.`;
|
|
2473
|
+
return {
|
|
2474
|
+
scope: "site",
|
|
2475
|
+
actionType: "widget.create",
|
|
2476
|
+
title,
|
|
2477
|
+
summary,
|
|
2478
|
+
riskReason: "layout_structure_change",
|
|
2479
|
+
targetRefType: "widget_area",
|
|
2480
|
+
targetRefId: input.area.id,
|
|
2481
|
+
priority: "high",
|
|
2482
|
+
metadata: {
|
|
2483
|
+
actionHash,
|
|
2484
|
+
areaId: input.area.id,
|
|
2485
|
+
areaName: input.area.name,
|
|
2486
|
+
widgetType: requestedChange.type
|
|
2487
|
+
},
|
|
2488
|
+
reviewPayload: {
|
|
2489
|
+
kind: "widget_create",
|
|
2490
|
+
actionType: "widget.create",
|
|
2491
|
+
title,
|
|
2492
|
+
summary,
|
|
2493
|
+
target: {
|
|
2494
|
+
scope: "site",
|
|
2495
|
+
targetRefType: "widget_area",
|
|
2496
|
+
targetRefId: input.area.id,
|
|
2497
|
+
actionHash
|
|
2498
|
+
},
|
|
2499
|
+
currentArea,
|
|
2500
|
+
requestedChange
|
|
2501
|
+
},
|
|
2502
|
+
actionHash
|
|
2503
|
+
};
|
|
2504
|
+
}
|
|
2505
|
+
async buildUpdateWidgetRequest(input) {
|
|
2506
|
+
const current = normalizeWidgetInput(input.currentWidget);
|
|
2507
|
+
const requestedChange = normalizeUpdateWidgetInput(input);
|
|
2508
|
+
const changedKeys = Object.keys(requestedChange).toSorted();
|
|
2509
|
+
const actionHash = await buildStableActionHash({
|
|
2510
|
+
actionType: "widget.update",
|
|
2511
|
+
current,
|
|
2512
|
+
requestedChange
|
|
2513
|
+
});
|
|
2514
|
+
const title = `Approve updating widget ${input.currentWidget.id}`;
|
|
2515
|
+
const summary = `Update widget ${input.currentWidget.id} in area ${input.area.name}.`;
|
|
2516
|
+
return {
|
|
2517
|
+
scope: "site",
|
|
2518
|
+
actionType: "widget.update",
|
|
2519
|
+
title,
|
|
2520
|
+
summary,
|
|
2521
|
+
riskReason: "layout_structure_change",
|
|
2522
|
+
targetRefType: "widget",
|
|
2523
|
+
targetRefId: input.currentWidget.id,
|
|
2524
|
+
priority: "high",
|
|
2525
|
+
metadata: {
|
|
2526
|
+
actionHash,
|
|
2527
|
+
areaId: input.area.id,
|
|
2528
|
+
areaName: input.area.name,
|
|
2529
|
+
widgetId: input.currentWidget.id,
|
|
2530
|
+
changedKeys
|
|
2531
|
+
},
|
|
2532
|
+
reviewPayload: {
|
|
2533
|
+
kind: "widget_update",
|
|
2534
|
+
actionType: "widget.update",
|
|
2535
|
+
title,
|
|
2536
|
+
summary,
|
|
2537
|
+
target: {
|
|
2538
|
+
scope: "site",
|
|
2539
|
+
targetRefType: "widget",
|
|
2540
|
+
targetRefId: input.currentWidget.id,
|
|
2541
|
+
actionHash
|
|
2542
|
+
},
|
|
2543
|
+
changedKeys,
|
|
2544
|
+
current,
|
|
2545
|
+
requestedChange
|
|
2546
|
+
},
|
|
2547
|
+
actionHash
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
async buildDeleteWidgetRequest(input) {
|
|
2551
|
+
const current = normalizeWidgetInput(input.currentWidget);
|
|
2552
|
+
const actionHash = await buildStableActionHash({
|
|
2553
|
+
actionType: "widget.delete",
|
|
2554
|
+
current
|
|
2555
|
+
});
|
|
2556
|
+
const title = `Approve deleting widget ${input.currentWidget.id}`;
|
|
2557
|
+
const summary = `Delete widget ${input.currentWidget.id} from area ${input.area.name}.`;
|
|
2558
|
+
return {
|
|
2559
|
+
scope: "site",
|
|
2560
|
+
actionType: "widget.delete",
|
|
2561
|
+
title,
|
|
2562
|
+
summary,
|
|
2563
|
+
riskReason: "layout_structure_change",
|
|
2564
|
+
targetRefType: "widget",
|
|
2565
|
+
targetRefId: input.currentWidget.id,
|
|
2566
|
+
priority: "high",
|
|
2567
|
+
metadata: {
|
|
2568
|
+
actionHash,
|
|
2569
|
+
areaId: input.area.id,
|
|
2570
|
+
areaName: input.area.name,
|
|
2571
|
+
widgetId: input.currentWidget.id
|
|
2572
|
+
},
|
|
2573
|
+
reviewPayload: {
|
|
2574
|
+
kind: "widget_delete",
|
|
2575
|
+
actionType: "widget.delete",
|
|
2576
|
+
title,
|
|
2577
|
+
summary,
|
|
2578
|
+
target: {
|
|
2579
|
+
scope: "site",
|
|
2580
|
+
targetRefType: "widget",
|
|
2581
|
+
targetRefId: input.currentWidget.id,
|
|
2582
|
+
actionHash
|
|
2583
|
+
},
|
|
2584
|
+
current
|
|
2585
|
+
},
|
|
2586
|
+
actionHash
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
async buildReorderWidgetsRequest(input) {
|
|
2590
|
+
const currentArea = normalizeWidgetAreaInput(input.area);
|
|
2591
|
+
const requestedChange = normalizeReorderWidgetsInput(input.widgetIds);
|
|
2592
|
+
const actionHash = await buildStableActionHash({
|
|
2593
|
+
actionType: "widget.reorder",
|
|
2594
|
+
currentArea,
|
|
2595
|
+
requestedChange
|
|
2596
|
+
});
|
|
2597
|
+
const title = `Approve reordering widgets in ${input.area.name}`;
|
|
2598
|
+
const summary = `Reorder ${input.widgetIds.length} widgets in area ${input.area.name}.`;
|
|
2599
|
+
return {
|
|
2600
|
+
scope: "site",
|
|
2601
|
+
actionType: "widget.reorder",
|
|
2602
|
+
title,
|
|
2603
|
+
summary,
|
|
2604
|
+
riskReason: "layout_structure_change",
|
|
2605
|
+
targetRefType: "widget_area",
|
|
2606
|
+
targetRefId: input.area.id,
|
|
2607
|
+
priority: "high",
|
|
2608
|
+
metadata: {
|
|
2609
|
+
actionHash,
|
|
2610
|
+
areaId: input.area.id,
|
|
2611
|
+
areaName: input.area.name,
|
|
2612
|
+
widgetIds: [...input.widgetIds]
|
|
2613
|
+
},
|
|
2614
|
+
reviewPayload: {
|
|
2615
|
+
kind: "widget_reorder",
|
|
2616
|
+
actionType: "widget.reorder",
|
|
2617
|
+
title,
|
|
2618
|
+
summary,
|
|
2619
|
+
target: {
|
|
2620
|
+
scope: "site",
|
|
2621
|
+
targetRefType: "widget_area",
|
|
2622
|
+
targetRefId: input.area.id,
|
|
2623
|
+
actionHash
|
|
2624
|
+
},
|
|
2625
|
+
currentArea,
|
|
2626
|
+
requestedChange
|
|
2627
|
+
},
|
|
2628
|
+
actionHash
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
};
|
|
2632
|
+
function normalizeWidgetArea(row) {
|
|
2633
|
+
return {
|
|
2634
|
+
id: row.id,
|
|
2635
|
+
name: row.name,
|
|
2636
|
+
label: row.label,
|
|
2637
|
+
description: row.description
|
|
2638
|
+
};
|
|
2639
|
+
}
|
|
2640
|
+
function normalizeWidget(row) {
|
|
2641
|
+
return {
|
|
2642
|
+
id: row.id,
|
|
2643
|
+
areaId: row.area_id,
|
|
2644
|
+
sortOrder: row.sort_order,
|
|
2645
|
+
type: row.type,
|
|
2646
|
+
title: row.title,
|
|
2647
|
+
content: parseUnknownArray(row.content),
|
|
2648
|
+
menuName: row.menu_name,
|
|
2649
|
+
componentId: row.component_id,
|
|
2650
|
+
componentProps: parseUnknownRecord(row.component_props)
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
function normalizeWidgetAreaInput(area) {
|
|
2654
|
+
return {
|
|
2655
|
+
id: area.id,
|
|
2656
|
+
name: area.name,
|
|
2657
|
+
label: area.label,
|
|
2658
|
+
description: area.description,
|
|
2659
|
+
widgets: area.widgets.map(normalizeWidgetInput)
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
function normalizeWidgetInput(widget) {
|
|
2663
|
+
return {
|
|
2664
|
+
id: widget.id,
|
|
2665
|
+
areaId: widget.areaId,
|
|
2666
|
+
sortOrder: widget.sortOrder,
|
|
2667
|
+
type: widget.type,
|
|
2668
|
+
title: widget.title,
|
|
2669
|
+
content: widget.content,
|
|
2670
|
+
menuName: widget.menuName,
|
|
2671
|
+
componentId: widget.componentId,
|
|
2672
|
+
componentProps: widget.componentProps
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
function normalizeCreateWidgetAreaInput(input) {
|
|
2676
|
+
return {
|
|
2677
|
+
name: input.name,
|
|
2678
|
+
label: input.label,
|
|
2679
|
+
description: input.description ?? null
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
function normalizeCreateWidgetInput(input) {
|
|
2683
|
+
return {
|
|
2684
|
+
type: input.type,
|
|
2685
|
+
title: input.title ?? null,
|
|
2686
|
+
content: input.content ?? null,
|
|
2687
|
+
menuName: input.menuName ?? null,
|
|
2688
|
+
componentId: input.componentId ?? null,
|
|
2689
|
+
componentProps: input.componentProps ?? null
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
function normalizeUpdateWidgetInput(input) {
|
|
2693
|
+
const requestedChange = {};
|
|
2694
|
+
if (input.type !== void 0) requestedChange.type = input.type;
|
|
2695
|
+
if (input.title !== void 0) requestedChange.title = input.title || null;
|
|
2696
|
+
if (input.content !== void 0) requestedChange.content = input.content;
|
|
2697
|
+
if (input.menuName !== void 0) requestedChange.menuName = input.menuName || null;
|
|
2698
|
+
if (input.componentId !== void 0) requestedChange.componentId = input.componentId || null;
|
|
2699
|
+
if (input.componentProps !== void 0) requestedChange.componentProps = input.componentProps;
|
|
2700
|
+
return requestedChange;
|
|
2701
|
+
}
|
|
2702
|
+
function normalizeReorderWidgetsInput(widgetIds) {
|
|
2703
|
+
return widgetIds.map((id, index) => ({
|
|
2704
|
+
id,
|
|
2705
|
+
sortOrder: index
|
|
2706
|
+
}));
|
|
2707
|
+
}
|
|
2708
|
+
function parseUnknownArray(value) {
|
|
2709
|
+
if (!value) return null;
|
|
2710
|
+
try {
|
|
2711
|
+
const parsed = JSON.parse(value);
|
|
2712
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
2713
|
+
} catch {
|
|
2714
|
+
return null;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
function parseUnknownRecord(value) {
|
|
2718
|
+
if (!value) return null;
|
|
2719
|
+
try {
|
|
2720
|
+
const parsed = JSON.parse(value);
|
|
2721
|
+
return isRecord$1(parsed) ? parsed : null;
|
|
2722
|
+
} catch {
|
|
2723
|
+
return null;
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
function isRecord$1(value) {
|
|
2727
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
//#endregion
|
|
2731
|
+
//#region src/site-context/entity-resolve.ts
|
|
2732
|
+
const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
2733
|
+
const DISPLAY_FALLBACK_FIELDS = [
|
|
2734
|
+
"title",
|
|
2735
|
+
"name",
|
|
2736
|
+
"headline",
|
|
2737
|
+
"label",
|
|
2738
|
+
"question"
|
|
2739
|
+
];
|
|
2740
|
+
const DISPLAY_FIELD_TYPES = new Set([
|
|
2741
|
+
"string",
|
|
2742
|
+
"text",
|
|
2743
|
+
"url",
|
|
2744
|
+
"slug",
|
|
2745
|
+
"select"
|
|
2746
|
+
]);
|
|
2747
|
+
const ENTITY_TYPES = [
|
|
2748
|
+
"content",
|
|
2749
|
+
"collection",
|
|
2750
|
+
"plugin",
|
|
2751
|
+
"taxonomy",
|
|
2752
|
+
"menu"
|
|
2753
|
+
];
|
|
2754
|
+
function isDisplayField(field) {
|
|
2755
|
+
return DISPLAY_FIELD_TYPES.has(field.type);
|
|
2756
|
+
}
|
|
2757
|
+
function uniqueValues(values) {
|
|
2758
|
+
return [...new Set(values)];
|
|
2759
|
+
}
|
|
2760
|
+
function compareOptionalIsoDesc(a, b) {
|
|
2761
|
+
if (a && b) return b.localeCompare(a);
|
|
2762
|
+
if (a) return -1;
|
|
2763
|
+
if (b) return 1;
|
|
2764
|
+
return 0;
|
|
2765
|
+
}
|
|
2766
|
+
function normalizeDisplayMatch(value) {
|
|
2767
|
+
return normalizeEntityAlias(value);
|
|
2768
|
+
}
|
|
2769
|
+
function candidateIdentity(candidate) {
|
|
2770
|
+
return candidate.collection ? `${candidate.entityType}:${candidate.collection}:${candidate.entityId}` : `${candidate.entityType}:${candidate.entityId}`;
|
|
2771
|
+
}
|
|
2772
|
+
function sortCandidates(candidates) {
|
|
2773
|
+
return candidates.toSorted((left, right) => {
|
|
2774
|
+
const reasonRank = matchReasonRank(left.matchReason) - matchReasonRank(right.matchReason);
|
|
2775
|
+
if (reasonRank !== 0) return reasonRank;
|
|
2776
|
+
const providerRank = (left.providerRank ?? Number.MAX_SAFE_INTEGER) - (right.providerRank ?? Number.MAX_SAFE_INTEGER);
|
|
2777
|
+
if (providerRank !== 0) return providerRank;
|
|
2778
|
+
const updatedAtRank = compareOptionalIsoDesc(left.updatedAt, right.updatedAt);
|
|
2779
|
+
if (updatedAtRank !== 0) return updatedAtRank;
|
|
2780
|
+
const createdAtRank = compareOptionalIsoDesc(left.createdAt, right.createdAt);
|
|
2781
|
+
if (createdAtRank !== 0) return createdAtRank;
|
|
2782
|
+
const labelRank = left.label.localeCompare(right.label);
|
|
2783
|
+
if (labelRank !== 0) return labelRank;
|
|
2784
|
+
const keyRank = left.entityKey.localeCompare(right.entityKey);
|
|
2785
|
+
if (keyRank !== 0) return keyRank;
|
|
2786
|
+
return left.entityId.localeCompare(right.entityId);
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2789
|
+
function matchReasonRank(reason) {
|
|
2790
|
+
switch (reason) {
|
|
2791
|
+
case "exact_id": return 0;
|
|
2792
|
+
case "exact_key": return 1;
|
|
2793
|
+
case "exact_slug": return 2;
|
|
2794
|
+
case "alias": return 3;
|
|
2795
|
+
case "display_exact": return 4;
|
|
2796
|
+
case "display_partial": return 5;
|
|
2797
|
+
case "search_provider": return 6;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
function contentDisplayLabel(item, displayFields) {
|
|
2801
|
+
for (const field of displayFields) {
|
|
2802
|
+
const value = item.data[field];
|
|
2803
|
+
if (typeof value === "string" && value.trim().length > 0) return value;
|
|
2804
|
+
}
|
|
2805
|
+
if (typeof item.data.title === "string" && item.data.title.trim().length > 0) return item.data.title;
|
|
2806
|
+
if (typeof item.data.name === "string" && item.data.name.trim().length > 0) return item.data.name;
|
|
2807
|
+
if (item.slug) return item.slug;
|
|
2808
|
+
return item.id;
|
|
2809
|
+
}
|
|
2810
|
+
var EntityResolver = class {
|
|
2811
|
+
registry;
|
|
2812
|
+
contentRepository;
|
|
2813
|
+
aliasRepository;
|
|
2814
|
+
collectionContextCache = /* @__PURE__ */ new Map();
|
|
2815
|
+
constructor(options) {
|
|
2816
|
+
this.options = options;
|
|
2817
|
+
this.registry = new SchemaRegistry(options.db);
|
|
2818
|
+
this.contentRepository = new ContentRepository(options.db);
|
|
2819
|
+
this.aliasRepository = new EntityAliasRepository(options.db);
|
|
2820
|
+
}
|
|
2821
|
+
async resolve(input) {
|
|
2822
|
+
const query = input.query.trim();
|
|
2823
|
+
if (!query) return {
|
|
2824
|
+
status: "not_found",
|
|
2825
|
+
suggestion: "Provide a non-empty query."
|
|
2826
|
+
};
|
|
2827
|
+
const entityTypes = input.entityTypes?.length ? uniqueValues(input.entityTypes) : ENTITY_TYPES;
|
|
2828
|
+
const limit = Math.min(Math.max(input.limit ?? 5, 1), 20);
|
|
2829
|
+
const exactIdOrKey = await this.resolveExactIdOrKey(query, entityTypes, input.collection);
|
|
2830
|
+
const idOrKeyResult = this.finalizeTier(exactIdOrKey, query, limit, entityTypes);
|
|
2831
|
+
if (idOrKeyResult) return idOrKeyResult;
|
|
2832
|
+
const exactSlug = await this.resolveExactSlug(query, entityTypes, input.collection);
|
|
2833
|
+
const slugResult = this.finalizeTier(exactSlug, query, limit, entityTypes);
|
|
2834
|
+
if (slugResult) return slugResult;
|
|
2835
|
+
const alias = await this.resolveAlias(query, entityTypes, input.collection, limit);
|
|
2836
|
+
const aliasResult = this.finalizeTier(alias, query, limit, entityTypes);
|
|
2837
|
+
if (aliasResult) return aliasResult;
|
|
2838
|
+
const displayExact = await this.resolveDisplay(query, entityTypes, input.collection, true, limit);
|
|
2839
|
+
const displayExactResult = this.finalizeTier(displayExact, query, limit, entityTypes);
|
|
2840
|
+
if (displayExactResult) return displayExactResult;
|
|
2841
|
+
const displayPartial = await this.resolveDisplay(query, entityTypes, input.collection, false, limit);
|
|
2842
|
+
const displayPartialResult = this.finalizeTier(displayPartial, query, limit, entityTypes);
|
|
2843
|
+
if (displayPartialResult) return displayPartialResult;
|
|
2844
|
+
const searchMatches = await this.resolveSearchProvider(query, entityTypes, input.collection, limit);
|
|
2845
|
+
const searchResult = this.finalizeTier(searchMatches, query, limit, entityTypes);
|
|
2846
|
+
if (searchResult) return searchResult;
|
|
2847
|
+
return {
|
|
2848
|
+
status: "not_found",
|
|
2849
|
+
suggestion: `No ${describeEntityTypes(entityTypes)} found for "${query}". Check the ID or slug, add an alias, or use a more specific label.`
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
finalizeTier(candidates, query, limit, entityTypes) {
|
|
2853
|
+
if (candidates.length === 0) return null;
|
|
2854
|
+
const sorted = dedupeCandidates(candidates).slice(0, limit);
|
|
2855
|
+
if (sorted.length === 1) return {
|
|
2856
|
+
status: "resolved",
|
|
2857
|
+
resolved: stripInternalCandidate(sorted[0])
|
|
2858
|
+
};
|
|
2859
|
+
return {
|
|
2860
|
+
status: "ambiguous",
|
|
2861
|
+
candidates: sorted.map(stripInternalCandidate),
|
|
2862
|
+
suggestion: `Found ${sorted.length} ${describeEntityTypes(entityTypes)} matches for "${query}". Narrow with entity_types or collection before proceeding.`
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
async resolveExactIdOrKey(query, entityTypes, collectionHint) {
|
|
2866
|
+
const candidates = [];
|
|
2867
|
+
const [contentCollections, collectionContexts] = await Promise.all([entityTypes.includes("content") ? this.listContentCollections(collectionHint) : Promise.resolve([]), entityTypes.includes("collection") ? this.listAllCollectionContexts() : Promise.resolve([])]);
|
|
2868
|
+
if (entityTypes.includes("content")) for (const context of contentCollections) {
|
|
2869
|
+
const item = await this.contentRepository.findById(context.collection.slug, query);
|
|
2870
|
+
if (!item) continue;
|
|
2871
|
+
candidates.push(this.contentCandidate(item, context, 1, "exact_id"));
|
|
2872
|
+
}
|
|
2873
|
+
const [menus, taxonomies, plugins] = await Promise.all([
|
|
2874
|
+
entityTypes.includes("menu") ? this.listMenus() : Promise.resolve([]),
|
|
2875
|
+
entityTypes.includes("taxonomy") ? this.listTaxonomies() : Promise.resolve([]),
|
|
2876
|
+
entityTypes.includes("plugin") ? this.listPlugins() : Promise.resolve([])
|
|
2877
|
+
]);
|
|
2878
|
+
if (entityTypes.includes("collection")) {
|
|
2879
|
+
for (const context of collectionContexts) if (ULID_PATTERN.test(query) && context.collection.id === query) {
|
|
2880
|
+
candidates.push(collectionCandidate(context.collection, 1, "exact_id"));
|
|
2881
|
+
continue;
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
if (entityTypes.includes("menu")) for (const menu of menus) {
|
|
2885
|
+
if (ULID_PATTERN.test(query) && menu.id === query) {
|
|
2886
|
+
candidates.push(menuCandidate(menu, 1, "exact_id"));
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
2889
|
+
if (menu.name === query) candidates.push(menuCandidate(menu, 1, "exact_key"));
|
|
2890
|
+
}
|
|
2891
|
+
if (entityTypes.includes("taxonomy")) for (const taxonomy of taxonomies) {
|
|
2892
|
+
if (ULID_PATTERN.test(query) && taxonomy.id === query) {
|
|
2893
|
+
candidates.push(taxonomyCandidate(taxonomy, 1, "exact_id"));
|
|
2894
|
+
continue;
|
|
2895
|
+
}
|
|
2896
|
+
if (taxonomy.name === query) candidates.push(taxonomyCandidate(taxonomy, 1, "exact_key"));
|
|
2897
|
+
}
|
|
2898
|
+
if (entityTypes.includes("plugin")) {
|
|
2899
|
+
for (const plugin of plugins) if (plugin.id === query) candidates.push(pluginCandidate(plugin, 1, "exact_key"));
|
|
2900
|
+
}
|
|
2901
|
+
return candidates;
|
|
2902
|
+
}
|
|
2903
|
+
async resolveExactSlug(query, entityTypes, collectionHint) {
|
|
2904
|
+
const candidates = [];
|
|
2905
|
+
const [contentCollections, collectionContexts] = await Promise.all([entityTypes.includes("content") ? this.listContentCollections(collectionHint) : Promise.resolve([]), entityTypes.includes("collection") ? this.listAllCollectionContexts() : Promise.resolve([])]);
|
|
2906
|
+
if (entityTypes.includes("collection")) {
|
|
2907
|
+
for (const context of collectionContexts) if (context.collection.slug === query) candidates.push(collectionCandidate(context.collection, 2, "exact_slug"));
|
|
2908
|
+
}
|
|
2909
|
+
if (entityTypes.includes("content")) for (const context of contentCollections) {
|
|
2910
|
+
const item = await this.contentRepository.findBySlug(context.collection.slug, query);
|
|
2911
|
+
if (!item) continue;
|
|
2912
|
+
candidates.push(this.contentCandidate(item, context, 2, "exact_slug"));
|
|
2913
|
+
}
|
|
2914
|
+
return candidates;
|
|
2915
|
+
}
|
|
2916
|
+
async resolveAlias(query, entityTypes, collectionHint, limit) {
|
|
2917
|
+
const matches = await this.aliasRepository.findByNormalizedAlias({
|
|
2918
|
+
normalizedAlias: normalizeEntityAlias(query),
|
|
2919
|
+
entityTypes,
|
|
2920
|
+
collectionSlug: collectionHint,
|
|
2921
|
+
limit
|
|
2922
|
+
});
|
|
2923
|
+
return this.hydrateAliasMatches(matches);
|
|
2924
|
+
}
|
|
2925
|
+
async hydrateAliasMatches(matches) {
|
|
2926
|
+
const candidates = [];
|
|
2927
|
+
for (const match of matches) switch (match.entityType) {
|
|
2928
|
+
case "content": {
|
|
2929
|
+
if (!match.collectionSlug) continue;
|
|
2930
|
+
const context = await this.getCollectionContext(match.collectionSlug);
|
|
2931
|
+
if (!context) continue;
|
|
2932
|
+
const item = await this.contentRepository.findById(match.collectionSlug, match.entityId);
|
|
2933
|
+
if (!item) continue;
|
|
2934
|
+
candidates.push(this.contentCandidate(item, context, 3, "alias"));
|
|
2935
|
+
break;
|
|
2936
|
+
}
|
|
2937
|
+
case "collection": {
|
|
2938
|
+
const collection = (await this.listAllCollectionContexts()).find((item) => item.collection.id === match.entityId)?.collection;
|
|
2939
|
+
if (collection) candidates.push(collectionCandidate(collection, 3, "alias"));
|
|
2940
|
+
break;
|
|
2941
|
+
}
|
|
2942
|
+
case "menu": {
|
|
2943
|
+
const menu = await this.options.db.selectFrom("_dineway_menus").selectAll().where("id", "=", match.entityId).executeTakeFirst();
|
|
2944
|
+
if (menu) candidates.push(menuCandidate(menu, 3, "alias"));
|
|
2945
|
+
break;
|
|
2946
|
+
}
|
|
2947
|
+
case "taxonomy": {
|
|
2948
|
+
const taxonomy = await this.options.db.selectFrom("_dineway_taxonomy_defs").selectAll().where("id", "=", match.entityId).executeTakeFirst();
|
|
2949
|
+
if (taxonomy) candidates.push(taxonomyCandidate(taxonomy, 3, "alias"));
|
|
2950
|
+
break;
|
|
2951
|
+
}
|
|
2952
|
+
case "plugin": {
|
|
2953
|
+
const plugin = (await this.listPlugins()).find((item) => item.id === match.entityId);
|
|
2954
|
+
if (plugin) candidates.push(pluginCandidate(plugin, 3, "alias"));
|
|
2955
|
+
break;
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
return candidates;
|
|
2959
|
+
}
|
|
2960
|
+
async resolveDisplay(query, entityTypes, collectionHint, exact, limit) {
|
|
2961
|
+
const candidates = [];
|
|
2962
|
+
const normalizedQuery = normalizeDisplayMatch(query);
|
|
2963
|
+
const [contentCollections, collectionContexts, menus, taxonomies, plugins] = await Promise.all([
|
|
2964
|
+
entityTypes.includes("content") ? this.listContentCollections(collectionHint) : Promise.resolve([]),
|
|
2965
|
+
entityTypes.includes("collection") ? this.listAllCollectionContexts() : Promise.resolve([]),
|
|
2966
|
+
entityTypes.includes("menu") ? this.listMenus() : Promise.resolve([]),
|
|
2967
|
+
entityTypes.includes("taxonomy") ? this.listTaxonomies() : Promise.resolve([]),
|
|
2968
|
+
entityTypes.includes("plugin") ? this.listPlugins() : Promise.resolve([])
|
|
2969
|
+
]);
|
|
2970
|
+
if (entityTypes.includes("collection")) {
|
|
2971
|
+
for (const context of collectionContexts) if (matchesDisplayValues([context.collection.label, context.collection.labelSingular ?? ""], normalizedQuery, exact)) candidates.push(collectionCandidate(context.collection, 4, exact ? "display_exact" : "display_partial"));
|
|
2972
|
+
}
|
|
2973
|
+
if (entityTypes.includes("menu")) {
|
|
2974
|
+
for (const menu of menus) if (matchesDisplayValues([menu.label], normalizedQuery, exact)) candidates.push(menuCandidate(menu, 4, exact ? "display_exact" : "display_partial"));
|
|
2975
|
+
}
|
|
2976
|
+
if (entityTypes.includes("taxonomy")) {
|
|
2977
|
+
for (const taxonomy of taxonomies) if (matchesDisplayValues([taxonomy.label, taxonomy.label_singular ?? ""], normalizedQuery, exact)) candidates.push(taxonomyCandidate(taxonomy, 4, exact ? "display_exact" : "display_partial"));
|
|
2978
|
+
}
|
|
2979
|
+
if (entityTypes.includes("plugin")) {
|
|
2980
|
+
for (const plugin of plugins) if (matchesDisplayValues([plugin.name], normalizedQuery, exact)) candidates.push(pluginCandidate(plugin, 4, exact ? "display_exact" : "display_partial"));
|
|
2981
|
+
}
|
|
2982
|
+
if (entityTypes.includes("content")) for (const context of contentCollections) {
|
|
2983
|
+
const matches = await this.resolveContentDisplay(context, normalizedQuery, exact, limit);
|
|
2984
|
+
candidates.push(...matches);
|
|
2985
|
+
}
|
|
2986
|
+
return candidates;
|
|
2987
|
+
}
|
|
2988
|
+
async resolveContentDisplay(context, normalizedQuery, exact, limit) {
|
|
2989
|
+
const candidates = [];
|
|
2990
|
+
const tableName = `ec_${context.collection.slug}`;
|
|
2991
|
+
validateIdentifier(context.collection.slug, "content collection slug");
|
|
2992
|
+
validateIdentifier(tableName, "content table name");
|
|
2993
|
+
for (const fieldSlug of context.displayFields) {
|
|
2994
|
+
validateIdentifier(fieldSlug, "content display field");
|
|
2995
|
+
const fieldRef = sql.ref(fieldSlug);
|
|
2996
|
+
const likePattern = `%${normalizedQuery}%`;
|
|
2997
|
+
const result = await sql`
|
|
2998
|
+
SELECT
|
|
2999
|
+
id,
|
|
3000
|
+
slug,
|
|
3001
|
+
status,
|
|
3002
|
+
locale,
|
|
3003
|
+
created_at,
|
|
3004
|
+
updated_at
|
|
3005
|
+
FROM ${sql.ref(tableName)}
|
|
3006
|
+
WHERE deleted_at IS NULL
|
|
3007
|
+
AND ${exact ? sql`lower(${fieldRef}) = ${normalizedQuery}` : sql`lower(${fieldRef}) LIKE ${likePattern}`}
|
|
3008
|
+
ORDER BY updated_at DESC, created_at DESC, id ASC
|
|
3009
|
+
LIMIT ${limit}
|
|
3010
|
+
`.execute(this.options.db);
|
|
3011
|
+
for (const row of result.rows) {
|
|
3012
|
+
const item = await this.contentRepository.findById(context.collection.slug, row.id);
|
|
3013
|
+
if (!item) continue;
|
|
3014
|
+
candidates.push(this.contentCandidate(item, context, 4, exact ? "display_exact" : "display_partial"));
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
return candidates;
|
|
3018
|
+
}
|
|
3019
|
+
async resolveSearchProvider(query, entityTypes, collectionHint, limit) {
|
|
3020
|
+
if (!entityTypes.includes("content")) return [];
|
|
3021
|
+
const candidates = [];
|
|
3022
|
+
const collections = await this.listContentCollections(collectionHint);
|
|
3023
|
+
for (const context of collections) {
|
|
3024
|
+
if (context.searchEnabled) {
|
|
3025
|
+
const results = await searchCollection(this.options.db, context.collection.slug, query, { limit });
|
|
3026
|
+
let providerRank = 0;
|
|
3027
|
+
for (const result of results.items) {
|
|
3028
|
+
providerRank += 1;
|
|
3029
|
+
const item = await this.contentRepository.findById(context.collection.slug, result.id);
|
|
3030
|
+
if (!item) continue;
|
|
3031
|
+
candidates.push({
|
|
3032
|
+
...this.contentCandidate(item, context, 5, "search_provider"),
|
|
3033
|
+
providerRank
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3036
|
+
continue;
|
|
3037
|
+
}
|
|
3038
|
+
candidates.push(...await this.resolveFallbackSearchProvider(context, query, limit));
|
|
3039
|
+
}
|
|
3040
|
+
return candidates;
|
|
3041
|
+
}
|
|
3042
|
+
async resolveFallbackSearchProvider(context, query, limit) {
|
|
3043
|
+
if (context.searchFields.length === 0) return [];
|
|
3044
|
+
const tableName = `ec_${context.collection.slug}`;
|
|
3045
|
+
validateIdentifier(context.collection.slug, "content collection slug");
|
|
3046
|
+
validateIdentifier(tableName, "content table name");
|
|
3047
|
+
const trimmedQuery = query.trim().toLowerCase();
|
|
3048
|
+
if (!trimmedQuery) return [];
|
|
3049
|
+
const likePattern = `%${trimmedQuery}%`;
|
|
3050
|
+
const searchConditions = context.searchFields.map((fieldSlug) => {
|
|
3051
|
+
validateIdentifier(fieldSlug, "content search field");
|
|
3052
|
+
return sql`lower(cast(${sql.ref(fieldSlug)} as text)) LIKE ${likePattern}`;
|
|
3053
|
+
});
|
|
3054
|
+
const result = await sql`
|
|
3055
|
+
SELECT
|
|
3056
|
+
id,
|
|
3057
|
+
slug,
|
|
3058
|
+
status,
|
|
3059
|
+
locale,
|
|
3060
|
+
created_at,
|
|
3061
|
+
updated_at
|
|
3062
|
+
FROM ${sql.ref(tableName)}
|
|
3063
|
+
WHERE deleted_at IS NULL
|
|
3064
|
+
AND (${sql.join(searchConditions, sql` OR `)})
|
|
3065
|
+
ORDER BY updated_at DESC, created_at DESC, id ASC
|
|
3066
|
+
LIMIT ${limit}
|
|
3067
|
+
`.execute(this.options.db);
|
|
3068
|
+
const candidates = [];
|
|
3069
|
+
for (const row of result.rows) {
|
|
3070
|
+
const item = await this.contentRepository.findById(context.collection.slug, row.id);
|
|
3071
|
+
if (!item) continue;
|
|
3072
|
+
candidates.push(this.contentCandidate(item, context, 5, "search_provider"));
|
|
3073
|
+
}
|
|
3074
|
+
return candidates;
|
|
3075
|
+
}
|
|
3076
|
+
async listAllCollectionContexts() {
|
|
3077
|
+
return (await Promise.all((await this.registry.listCollections()).map((collection) => this.getCollectionContext(collection.slug)))).filter((value) => value !== null);
|
|
3078
|
+
}
|
|
3079
|
+
async listContentCollections(collectionHint) {
|
|
3080
|
+
if (collectionHint) {
|
|
3081
|
+
const context = await this.getCollectionContext(collectionHint);
|
|
3082
|
+
return context ? [context] : [];
|
|
3083
|
+
}
|
|
3084
|
+
return this.listAllCollectionContexts();
|
|
3085
|
+
}
|
|
3086
|
+
async getCollectionContext(slug) {
|
|
3087
|
+
let promise = this.collectionContextCache.get(slug);
|
|
3088
|
+
if (!promise) {
|
|
3089
|
+
promise = this.buildCollectionContext(slug);
|
|
3090
|
+
this.collectionContextCache.set(slug, promise);
|
|
3091
|
+
}
|
|
3092
|
+
return promise;
|
|
3093
|
+
}
|
|
3094
|
+
async buildCollectionContext(slug) {
|
|
3095
|
+
const collection = await this.registry.getCollectionWithFields(slug);
|
|
3096
|
+
if (!collection) return null;
|
|
3097
|
+
const ftsManager = new FTSManager(this.options.db);
|
|
3098
|
+
const searchableFields = await ftsManager.getSearchableFields(slug);
|
|
3099
|
+
const searchConfig = await ftsManager.getSearchConfig(slug);
|
|
3100
|
+
return {
|
|
3101
|
+
collection,
|
|
3102
|
+
displayFields: resolveDisplayFields(collection, searchableFields, searchConfig?.weights),
|
|
3103
|
+
searchFields: searchableFields,
|
|
3104
|
+
searchEnabled: searchConfig?.enabled === true
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
async listMenus() {
|
|
3108
|
+
return this.options.db.selectFrom("_dineway_menus").selectAll().execute();
|
|
3109
|
+
}
|
|
3110
|
+
async listTaxonomies() {
|
|
3111
|
+
return this.options.db.selectFrom("_dineway_taxonomy_defs").selectAll().execute();
|
|
3112
|
+
}
|
|
3113
|
+
async listPlugins() {
|
|
3114
|
+
const result = await handlePluginList(this.options.db, this.options.handlers.configuredPlugins, this.options.handlers.config.marketplace);
|
|
3115
|
+
return result.success ? result.data?.items ?? [] : [];
|
|
3116
|
+
}
|
|
3117
|
+
contentCandidate(item, context, matchTier, matchReason) {
|
|
3118
|
+
return {
|
|
3119
|
+
entityType: "content",
|
|
3120
|
+
entityId: item.id,
|
|
3121
|
+
entityKey: item.slug ?? item.id,
|
|
3122
|
+
label: contentDisplayLabel(item, context.displayFields),
|
|
3123
|
+
matchTier,
|
|
3124
|
+
matchReason,
|
|
3125
|
+
collection: context.collection.slug,
|
|
3126
|
+
slug: item.slug,
|
|
3127
|
+
status: item.status,
|
|
3128
|
+
locale: item.locale,
|
|
3129
|
+
updatedAt: item.updatedAt,
|
|
3130
|
+
createdAt: item.createdAt
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
function resolveDisplayFields(collection, searchableFields, weights) {
|
|
3135
|
+
const fieldsBySlug = new Map(collection.fields.map((field) => [field.slug, field]));
|
|
3136
|
+
const weightedFields = Object.entries(weights ?? {}).filter(([slug]) => {
|
|
3137
|
+
const field = fieldsBySlug.get(slug);
|
|
3138
|
+
return !!field && isDisplayField(field);
|
|
3139
|
+
}).toSorted((left, right) => right[1] - left[1]).map(([slug]) => slug);
|
|
3140
|
+
const configuredSearchFields = searchableFields.filter((slug) => {
|
|
3141
|
+
const field = fieldsBySlug.get(slug);
|
|
3142
|
+
return !!field && isDisplayField(field);
|
|
3143
|
+
});
|
|
3144
|
+
const fallbackFields = DISPLAY_FALLBACK_FIELDS.filter((slug) => {
|
|
3145
|
+
const field = fieldsBySlug.get(slug);
|
|
3146
|
+
return !!field && isDisplayField(field);
|
|
3147
|
+
});
|
|
3148
|
+
const remainingDisplayFields = collection.fields.filter((field) => isDisplayField(field)).map((field) => field.slug);
|
|
3149
|
+
return uniqueValues([
|
|
3150
|
+
...weightedFields,
|
|
3151
|
+
...configuredSearchFields,
|
|
3152
|
+
...fallbackFields,
|
|
3153
|
+
...remainingDisplayFields
|
|
3154
|
+
]);
|
|
3155
|
+
}
|
|
3156
|
+
function dedupeCandidates(candidates) {
|
|
3157
|
+
const sorted = sortCandidates(candidates);
|
|
3158
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3159
|
+
const deduped = [];
|
|
3160
|
+
for (const candidate of sorted) {
|
|
3161
|
+
const identity = candidateIdentity(candidate);
|
|
3162
|
+
if (seen.has(identity)) continue;
|
|
3163
|
+
seen.add(identity);
|
|
3164
|
+
deduped.push(candidate);
|
|
3165
|
+
}
|
|
3166
|
+
return deduped;
|
|
3167
|
+
}
|
|
3168
|
+
function stripInternalCandidate(candidate) {
|
|
3169
|
+
const { providerRank: _providerRank, ...publicCandidate } = candidate;
|
|
3170
|
+
return publicCandidate;
|
|
3171
|
+
}
|
|
3172
|
+
function collectionCandidate(collection, matchTier, matchReason) {
|
|
3173
|
+
return {
|
|
3174
|
+
entityType: "collection",
|
|
3175
|
+
entityId: collection.id,
|
|
3176
|
+
entityKey: collection.slug,
|
|
3177
|
+
label: collection.labelSingular ?? collection.label,
|
|
3178
|
+
matchTier,
|
|
3179
|
+
matchReason,
|
|
3180
|
+
slug: collection.slug,
|
|
3181
|
+
updatedAt: collection.updatedAt,
|
|
3182
|
+
createdAt: collection.createdAt
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
function menuCandidate(menu, matchTier, matchReason) {
|
|
3186
|
+
return {
|
|
3187
|
+
entityType: "menu",
|
|
3188
|
+
entityId: menu.id,
|
|
3189
|
+
entityKey: menu.name,
|
|
3190
|
+
label: menu.label,
|
|
3191
|
+
matchTier,
|
|
3192
|
+
matchReason,
|
|
3193
|
+
updatedAt: menu.updated_at,
|
|
3194
|
+
createdAt: menu.created_at
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
function taxonomyCandidate(taxonomy, matchTier, matchReason) {
|
|
3198
|
+
return {
|
|
3199
|
+
entityType: "taxonomy",
|
|
3200
|
+
entityId: taxonomy.id,
|
|
3201
|
+
entityKey: taxonomy.name,
|
|
3202
|
+
label: taxonomy.label_singular ?? taxonomy.label,
|
|
3203
|
+
matchTier,
|
|
3204
|
+
matchReason,
|
|
3205
|
+
createdAt: taxonomy.created_at
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
function pluginCandidate(plugin, matchTier, matchReason) {
|
|
3209
|
+
const updatedAt = plugin.activatedAt ?? plugin.installedAt ?? plugin.deactivatedAt;
|
|
3210
|
+
return {
|
|
3211
|
+
entityType: "plugin",
|
|
3212
|
+
entityId: plugin.id,
|
|
3213
|
+
entityKey: plugin.id,
|
|
3214
|
+
label: plugin.name,
|
|
3215
|
+
matchTier,
|
|
3216
|
+
matchReason,
|
|
3217
|
+
status: plugin.status,
|
|
3218
|
+
updatedAt,
|
|
3219
|
+
createdAt: plugin.installedAt
|
|
3220
|
+
};
|
|
3221
|
+
}
|
|
3222
|
+
function matchesDisplayValues(values, normalizedQuery, exact) {
|
|
3223
|
+
return values.some((value) => {
|
|
3224
|
+
const normalizedValue = normalizeDisplayMatch(value);
|
|
3225
|
+
return exact ? normalizedValue === normalizedQuery : normalizedValue.includes(normalizedQuery);
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3228
|
+
function describeEntityTypes(entityTypes) {
|
|
3229
|
+
if (entityTypes.length === 1) return entityTypes[0];
|
|
3230
|
+
if (entityTypes.length === ENTITY_TYPES.length) return "entities";
|
|
3231
|
+
return entityTypes.join(", ");
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
//#endregion
|
|
3235
|
+
//#region src/site-context/expertise.ts
|
|
3236
|
+
var ActorExpertiseService = class {
|
|
3237
|
+
db;
|
|
3238
|
+
constructor(options) {
|
|
3239
|
+
this.db = options.db;
|
|
3240
|
+
}
|
|
3241
|
+
async findByScope(input) {
|
|
3242
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3243
|
+
const parsed = parseSiteBriefingScope(input.scope);
|
|
3244
|
+
const includeInherited = input.includeInherited !== false;
|
|
3245
|
+
const scopes = includeInherited ? parsed.ancestors : [parsed.scope];
|
|
3246
|
+
const limit = clampLimit$1(input.limit);
|
|
3247
|
+
const contributions = await this.collectSignals({ scopes });
|
|
3248
|
+
const experts = Array.from(aggregateByActor(contributions).values(), (entry) => accumulatorToActorSummary(entry)).toSorted(compareActorSummaries).slice(0, limit);
|
|
3249
|
+
return {
|
|
3250
|
+
mode: "by_scope",
|
|
3251
|
+
scope: {
|
|
3252
|
+
requested: input.scope,
|
|
3253
|
+
resolved: parsed.scope,
|
|
3254
|
+
type: parsed.type,
|
|
3255
|
+
ancestors: parsed.ancestors,
|
|
3256
|
+
scopes,
|
|
3257
|
+
includeInherited,
|
|
3258
|
+
collection: parsed.collection,
|
|
3259
|
+
contentId: parsed.contentId,
|
|
3260
|
+
pluginId: parsed.pluginId
|
|
3261
|
+
},
|
|
3262
|
+
experts
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
async findByActor(input) {
|
|
3266
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3267
|
+
const contributions = await this.collectSignals({
|
|
3268
|
+
actorType: input.actorType,
|
|
3269
|
+
actorId: input.actorId
|
|
3270
|
+
});
|
|
3271
|
+
const scopes = Array.from(aggregateByScope(contributions).values(), (entry) => accumulatorToScopeSummary(entry)).toSorted(compareScopeSummaries);
|
|
3272
|
+
const limitedScopes = input.limit === void 0 ? scopes : scopes.slice(0, clampLimit$1(input.limit));
|
|
3273
|
+
return {
|
|
3274
|
+
mode: "by_actor",
|
|
3275
|
+
actor: {
|
|
3276
|
+
actorType: input.actorType,
|
|
3277
|
+
actorId: input.actorId
|
|
3278
|
+
},
|
|
3279
|
+
counts: sumCounts(limitedScopes.map((item) => item.counts)),
|
|
3280
|
+
scopes: limitedScopes
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
async collectSignals(query) {
|
|
3284
|
+
if (query.scopes && query.scopes.length === 0) return [];
|
|
3285
|
+
const [activitySignals, contextSignals, requestedReviewSignals, resolvedReviewSignals] = await Promise.all([
|
|
3286
|
+
this.listActivitySignals(query),
|
|
3287
|
+
this.listContextSignals(query),
|
|
3288
|
+
this.listRequestedReviewSignals(query),
|
|
3289
|
+
this.listResolvedReviewSignals(query)
|
|
3290
|
+
]);
|
|
3291
|
+
return [
|
|
3292
|
+
...activitySignals,
|
|
3293
|
+
...contextSignals,
|
|
3294
|
+
...requestedReviewSignals,
|
|
3295
|
+
...resolvedReviewSignals
|
|
3296
|
+
];
|
|
3297
|
+
}
|
|
3298
|
+
async listActivitySignals(query) {
|
|
3299
|
+
let q = this.db.selectFrom("_dineway_activity_log").select([
|
|
3300
|
+
"actor_type as actorType",
|
|
3301
|
+
"actor_id as actorId",
|
|
3302
|
+
"scope",
|
|
3303
|
+
"action_type as actionType",
|
|
3304
|
+
"created_at as contributedAt"
|
|
3305
|
+
]);
|
|
3306
|
+
if (query.scopes) q = q.where("scope", "in", query.scopes);
|
|
3307
|
+
if (query.actorType) q = q.where("actor_type", "=", query.actorType);
|
|
3308
|
+
if (query.actorId) q = q.where("actor_id", "=", query.actorId);
|
|
3309
|
+
return (await q.execute()).map((row) => ({
|
|
3310
|
+
actorType: row.actorType,
|
|
3311
|
+
actorId: row.actorId,
|
|
3312
|
+
scope: row.scope,
|
|
3313
|
+
type: "activity",
|
|
3314
|
+
actionType: row.actionType,
|
|
3315
|
+
contributedAt: row.contributedAt
|
|
3316
|
+
}));
|
|
3317
|
+
}
|
|
3318
|
+
async listContextSignals(query) {
|
|
3319
|
+
let q = this.db.selectFrom("_dineway_context_entries").select([
|
|
3320
|
+
"created_by_actor_type as actorType",
|
|
3321
|
+
"created_by_actor_id as actorId",
|
|
3322
|
+
"scope",
|
|
3323
|
+
"context_type as contextType",
|
|
3324
|
+
"created_at as contributedAt"
|
|
3325
|
+
]).where("is_current", "=", 1);
|
|
3326
|
+
if (query.scopes) q = q.where("scope", "in", query.scopes);
|
|
3327
|
+
if (query.actorType) q = q.where("created_by_actor_type", "=", query.actorType);
|
|
3328
|
+
if (query.actorId) q = q.where("created_by_actor_id", "=", query.actorId);
|
|
3329
|
+
return (await q.execute()).map((row) => ({
|
|
3330
|
+
actorType: row.actorType,
|
|
3331
|
+
actorId: row.actorId,
|
|
3332
|
+
scope: row.scope,
|
|
3333
|
+
type: "context",
|
|
3334
|
+
contextType: row.contextType,
|
|
3335
|
+
contributedAt: row.contributedAt
|
|
3336
|
+
}));
|
|
3337
|
+
}
|
|
3338
|
+
async listRequestedReviewSignals(query) {
|
|
3339
|
+
let q = this.db.selectFrom("_dineway_review_requests").select([
|
|
3340
|
+
"requested_by_actor_type as actorType",
|
|
3341
|
+
"requested_by_actor_id as actorId",
|
|
3342
|
+
"scope",
|
|
3343
|
+
"action_type as actionType",
|
|
3344
|
+
"created_at as contributedAt"
|
|
3345
|
+
]);
|
|
3346
|
+
if (query.scopes) q = q.where("scope", "in", query.scopes);
|
|
3347
|
+
if (query.actorType) q = q.where("requested_by_actor_type", "=", query.actorType);
|
|
3348
|
+
if (query.actorId) q = q.where("requested_by_actor_id", "=", query.actorId);
|
|
3349
|
+
return (await q.execute()).map((row) => ({
|
|
3350
|
+
actorType: row.actorType,
|
|
3351
|
+
actorId: row.actorId,
|
|
3352
|
+
scope: row.scope,
|
|
3353
|
+
type: "review",
|
|
3354
|
+
actionType: row.actionType,
|
|
3355
|
+
contributedAt: row.contributedAt
|
|
3356
|
+
}));
|
|
3357
|
+
}
|
|
3358
|
+
async listResolvedReviewSignals(query) {
|
|
3359
|
+
let q = this.db.selectFrom("_dineway_review_requests").select([
|
|
3360
|
+
"resolved_by_actor_type as actorType",
|
|
3361
|
+
"resolved_by_actor_id as actorId",
|
|
3362
|
+
"scope",
|
|
3363
|
+
"action_type as actionType",
|
|
3364
|
+
"resolved_at as contributedAt"
|
|
3365
|
+
]).where("resolved_by_actor_type", "is not", null).where("resolved_by_actor_id", "is not", null).where("resolved_at", "is not", null);
|
|
3366
|
+
if (query.scopes) q = q.where("scope", "in", query.scopes);
|
|
3367
|
+
if (query.actorType) q = q.where("resolved_by_actor_type", "=", query.actorType);
|
|
3368
|
+
if (query.actorId) q = q.where("resolved_by_actor_id", "=", query.actorId);
|
|
3369
|
+
return (await q.execute()).map((row) => ({
|
|
3370
|
+
actorType: row.actorType,
|
|
3371
|
+
actorId: row.actorId,
|
|
3372
|
+
scope: row.scope,
|
|
3373
|
+
type: "review",
|
|
3374
|
+
actionType: row.actionType,
|
|
3375
|
+
contributedAt: row.contributedAt
|
|
3376
|
+
}));
|
|
3377
|
+
}
|
|
3378
|
+
};
|
|
3379
|
+
function aggregateByActor(signals) {
|
|
3380
|
+
const result = /* @__PURE__ */ new Map();
|
|
3381
|
+
for (const signal of signals) {
|
|
3382
|
+
const key = `${signal.actorType}:${signal.actorId}`;
|
|
3383
|
+
const accumulator = result.get(key) ?? createAccumulator({
|
|
3384
|
+
actorType: signal.actorType,
|
|
3385
|
+
actorId: signal.actorId,
|
|
3386
|
+
scope: signal.scope,
|
|
3387
|
+
contributedAt: signal.contributedAt
|
|
3388
|
+
});
|
|
3389
|
+
applySignal(accumulator, signal);
|
|
3390
|
+
result.set(key, accumulator);
|
|
3391
|
+
}
|
|
3392
|
+
return result;
|
|
3393
|
+
}
|
|
3394
|
+
function aggregateByScope(signals) {
|
|
3395
|
+
const result = /* @__PURE__ */ new Map();
|
|
3396
|
+
for (const signal of signals) {
|
|
3397
|
+
const key = signal.scope;
|
|
3398
|
+
const accumulator = result.get(key) ?? createAccumulator({
|
|
3399
|
+
actorType: signal.actorType,
|
|
3400
|
+
actorId: signal.actorId,
|
|
3401
|
+
scope: signal.scope,
|
|
3402
|
+
contributedAt: signal.contributedAt
|
|
3403
|
+
});
|
|
3404
|
+
applySignal(accumulator, signal);
|
|
3405
|
+
result.set(key, accumulator);
|
|
3406
|
+
}
|
|
3407
|
+
return result;
|
|
3408
|
+
}
|
|
3409
|
+
function createAccumulator(input) {
|
|
3410
|
+
return {
|
|
3411
|
+
actorType: input.actorType,
|
|
3412
|
+
actorId: input.actorId,
|
|
3413
|
+
scope: input.scope,
|
|
3414
|
+
lastContributedAt: input.contributedAt,
|
|
3415
|
+
contextEntryCount: 0,
|
|
3416
|
+
reviewRequestCount: 0,
|
|
3417
|
+
activityCount: 0,
|
|
3418
|
+
contextTypes: /* @__PURE__ */ new Set(),
|
|
3419
|
+
actionTypes: /* @__PURE__ */ new Set(),
|
|
3420
|
+
reviewActionTypes: /* @__PURE__ */ new Set(),
|
|
3421
|
+
matchedScopes: new Set([input.scope])
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
function applySignal(accumulator, signal) {
|
|
3425
|
+
accumulator.lastContributedAt = maxIsoTimestamp(accumulator.lastContributedAt, signal.contributedAt);
|
|
3426
|
+
accumulator.matchedScopes.add(signal.scope);
|
|
3427
|
+
switch (signal.type) {
|
|
3428
|
+
case "activity":
|
|
3429
|
+
accumulator.activityCount += 1;
|
|
3430
|
+
if (signal.actionType) accumulator.actionTypes.add(signal.actionType);
|
|
3431
|
+
return;
|
|
3432
|
+
case "context":
|
|
3433
|
+
accumulator.contextEntryCount += 1;
|
|
3434
|
+
if (signal.contextType) accumulator.contextTypes.add(signal.contextType);
|
|
3435
|
+
return;
|
|
3436
|
+
case "review":
|
|
3437
|
+
accumulator.reviewRequestCount += 1;
|
|
3438
|
+
if (signal.actionType) accumulator.reviewActionTypes.add(signal.actionType);
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
function accumulatorToActorSummary(entry) {
|
|
3443
|
+
return {
|
|
3444
|
+
actorType: entry.actorType,
|
|
3445
|
+
actorId: entry.actorId,
|
|
3446
|
+
lastContributedAt: entry.lastContributedAt,
|
|
3447
|
+
counts: toCounts(entry),
|
|
3448
|
+
contextTypes: [...entry.contextTypes].toSorted(),
|
|
3449
|
+
actionTypes: [...entry.actionTypes].toSorted(),
|
|
3450
|
+
reviewActionTypes: [...entry.reviewActionTypes].toSorted(),
|
|
3451
|
+
scopes: [...entry.matchedScopes].toSorted()
|
|
3452
|
+
};
|
|
3453
|
+
}
|
|
3454
|
+
function accumulatorToScopeSummary(entry) {
|
|
3455
|
+
const parsed = parseSiteBriefingScope(entry.scope);
|
|
3456
|
+
return {
|
|
3457
|
+
scope: entry.scope,
|
|
3458
|
+
scopeInfo: {
|
|
3459
|
+
scope: parsed.scope,
|
|
3460
|
+
type: parsed.type,
|
|
3461
|
+
collection: parsed.collection,
|
|
3462
|
+
contentId: parsed.contentId,
|
|
3463
|
+
pluginId: parsed.pluginId
|
|
3464
|
+
},
|
|
3465
|
+
lastContributedAt: entry.lastContributedAt,
|
|
3466
|
+
counts: toCounts(entry),
|
|
3467
|
+
contextTypes: [...entry.contextTypes].toSorted(),
|
|
3468
|
+
actionTypes: [...entry.actionTypes].toSorted(),
|
|
3469
|
+
reviewActionTypes: [...entry.reviewActionTypes].toSorted()
|
|
3470
|
+
};
|
|
3471
|
+
}
|
|
3472
|
+
function toCounts(entry) {
|
|
3473
|
+
const total = entry.contextEntryCount + entry.reviewRequestCount + entry.activityCount;
|
|
3474
|
+
return {
|
|
3475
|
+
contextEntries: entry.contextEntryCount,
|
|
3476
|
+
reviewRequests: entry.reviewRequestCount,
|
|
3477
|
+
activities: entry.activityCount,
|
|
3478
|
+
total
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
function compareActorSummaries(left, right) {
|
|
3482
|
+
return compareCounts(left.counts, right.counts) || compareIsoDesc(left.lastContributedAt, right.lastContributedAt) || left.actorType.localeCompare(right.actorType) || left.actorId.localeCompare(right.actorId);
|
|
3483
|
+
}
|
|
3484
|
+
function compareScopeSummaries(left, right) {
|
|
3485
|
+
return compareCounts(left.counts, right.counts) || compareIsoDesc(left.lastContributedAt, right.lastContributedAt) || left.scope.localeCompare(right.scope);
|
|
3486
|
+
}
|
|
3487
|
+
function compareCounts(left, right) {
|
|
3488
|
+
return right.contextEntries - left.contextEntries || right.reviewRequests - left.reviewRequests || right.activities - left.activities || right.total - left.total;
|
|
3489
|
+
}
|
|
3490
|
+
function compareIsoDesc(left, right) {
|
|
3491
|
+
return right.localeCompare(left);
|
|
3492
|
+
}
|
|
3493
|
+
function sumCounts(values) {
|
|
3494
|
+
return values.reduce((accumulator, value) => ({
|
|
3495
|
+
contextEntries: accumulator.contextEntries + value.contextEntries,
|
|
3496
|
+
reviewRequests: accumulator.reviewRequests + value.reviewRequests,
|
|
3497
|
+
activities: accumulator.activities + value.activities,
|
|
3498
|
+
total: accumulator.total + value.total
|
|
3499
|
+
}), {
|
|
3500
|
+
contextEntries: 0,
|
|
3501
|
+
reviewRequests: 0,
|
|
3502
|
+
activities: 0,
|
|
3503
|
+
total: 0
|
|
3504
|
+
});
|
|
3505
|
+
}
|
|
3506
|
+
function maxIsoTimestamp(left, right) {
|
|
3507
|
+
return left.localeCompare(right) >= 0 ? left : right;
|
|
3508
|
+
}
|
|
3509
|
+
function clampLimit$1(value) {
|
|
3510
|
+
return Math.min(Math.max(1, value ?? 10), 100);
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
//#endregion
|
|
3514
|
+
//#region src/database/repositories/handoff-snapshots.ts
|
|
3515
|
+
var HandoffSnapshotRepository = class {
|
|
3516
|
+
constructor(db) {
|
|
3517
|
+
this.db = db;
|
|
3518
|
+
}
|
|
3519
|
+
async create(input) {
|
|
3520
|
+
const id = ulid();
|
|
3521
|
+
await this.db.insertInto("_dineway_handoff_snapshots").values(toInsertableHandoffSnapshot(id, input)).execute();
|
|
3522
|
+
return this.findById(id).then((snapshot) => {
|
|
3523
|
+
if (!snapshot) throw new Error("Failed to create handoff snapshot");
|
|
3524
|
+
return snapshot;
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
async findById(id) {
|
|
3528
|
+
const row = await this.db.selectFrom("_dineway_handoff_snapshots").selectAll().where("id", "=", id).executeTakeFirst();
|
|
3529
|
+
return row ? rowToHandoffSnapshot(row) : null;
|
|
3530
|
+
}
|
|
3531
|
+
async markResumed(id, resumedAt) {
|
|
3532
|
+
const value = resumedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3533
|
+
await this.db.updateTable("_dineway_handoff_snapshots").set({ resumed_at: value }).where("id", "=", id).execute();
|
|
3534
|
+
return this.findById(id);
|
|
3535
|
+
}
|
|
3536
|
+
};
|
|
3537
|
+
function toInsertableHandoffSnapshot(id, input) {
|
|
3538
|
+
return {
|
|
3539
|
+
id,
|
|
3540
|
+
scope: input.scope,
|
|
3541
|
+
actor_type: input.actorType,
|
|
3542
|
+
actor_id: input.actorId,
|
|
3543
|
+
auth_metadata: jsonOrNull(input.authMetadata),
|
|
3544
|
+
objective: input.objective,
|
|
3545
|
+
reasoning: input.reasoning,
|
|
3546
|
+
key_findings: JSON.stringify(input.keyFindings ?? []),
|
|
3547
|
+
touched_objects: JSON.stringify(input.touchedObjects ?? []),
|
|
3548
|
+
pending_actions: JSON.stringify(input.pendingActions ?? []),
|
|
3549
|
+
tool_evidence: JSON.stringify(input.toolEvidence ?? []),
|
|
3550
|
+
confidence: input.confidence ?? null,
|
|
3551
|
+
handoff_kind: input.handoffKind ?? "pause",
|
|
3552
|
+
reference_type: input.referenceType ?? null,
|
|
3553
|
+
reference_id: input.referenceId ?? null,
|
|
3554
|
+
created_at: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3555
|
+
resumed_at: null
|
|
3556
|
+
};
|
|
3557
|
+
}
|
|
3558
|
+
function rowToHandoffSnapshot(row) {
|
|
3559
|
+
return {
|
|
3560
|
+
id: row.id,
|
|
3561
|
+
scope: row.scope,
|
|
3562
|
+
actorType: row.actor_type,
|
|
3563
|
+
actorId: row.actor_id,
|
|
3564
|
+
authMetadata: parseRecord(row.auth_metadata),
|
|
3565
|
+
objective: row.objective,
|
|
3566
|
+
reasoning: row.reasoning,
|
|
3567
|
+
keyFindings: parseArray(row.key_findings),
|
|
3568
|
+
touchedObjects: parseArray(row.touched_objects),
|
|
3569
|
+
pendingActions: parseArray(row.pending_actions),
|
|
3570
|
+
toolEvidence: parseArray(row.tool_evidence),
|
|
3571
|
+
confidence: row.confidence,
|
|
3572
|
+
handoffKind: row.handoff_kind,
|
|
3573
|
+
referenceType: row.reference_type,
|
|
3574
|
+
referenceId: row.reference_id,
|
|
3575
|
+
createdAt: row.created_at,
|
|
3576
|
+
resumedAt: row.resumed_at
|
|
3577
|
+
};
|
|
3578
|
+
}
|
|
3579
|
+
function jsonOrNull(value) {
|
|
3580
|
+
return value ? JSON.stringify(value) : null;
|
|
3581
|
+
}
|
|
3582
|
+
function parseRecord(value) {
|
|
3583
|
+
if (!value) return null;
|
|
3584
|
+
try {
|
|
3585
|
+
const parsed = JSON.parse(value);
|
|
3586
|
+
return isRecord(parsed) ? parsed : null;
|
|
3587
|
+
} catch {
|
|
3588
|
+
return null;
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
function parseArray(value) {
|
|
3592
|
+
if (!value) return [];
|
|
3593
|
+
try {
|
|
3594
|
+
const parsed = JSON.parse(value);
|
|
3595
|
+
return Array.isArray(parsed) ? parsed.filter((_item) => true) : [];
|
|
3596
|
+
} catch {
|
|
3597
|
+
return [];
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
function isRecord(value) {
|
|
3601
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
//#endregion
|
|
3605
|
+
//#region src/site-context/handoff.ts
|
|
3606
|
+
const DEFAULT_ACTIVITY_LIMIT = 25;
|
|
3607
|
+
const DEFAULT_REVIEW_LIMIT = 25;
|
|
3608
|
+
const MAX_DIFF_LIMIT = 100;
|
|
3609
|
+
var HandoffSnapshotStore = class {
|
|
3610
|
+
db;
|
|
3611
|
+
now;
|
|
3612
|
+
constructor(options) {
|
|
3613
|
+
this.db = options.db;
|
|
3614
|
+
this.now = options.now ?? /* @__PURE__ */ new Date();
|
|
3615
|
+
}
|
|
3616
|
+
async capture(input) {
|
|
3617
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3618
|
+
parseSiteBriefingScope(input.scope);
|
|
3619
|
+
return new HandoffSnapshotRepository(this.db).create(input);
|
|
3620
|
+
}
|
|
3621
|
+
async get(id) {
|
|
3622
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3623
|
+
return new HandoffSnapshotRepository(this.db).findById(id);
|
|
3624
|
+
}
|
|
3625
|
+
async resume(input) {
|
|
3626
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3627
|
+
const repository = new HandoffSnapshotRepository(this.db);
|
|
3628
|
+
const snapshot = await repository.findById(input.id);
|
|
3629
|
+
if (!snapshot) return null;
|
|
3630
|
+
const resumedAt = this.now.toISOString();
|
|
3631
|
+
const resumed = await repository.markResumed(snapshot.id, resumedAt);
|
|
3632
|
+
if (!resumed) return null;
|
|
3633
|
+
const scope = parseSiteBriefingScope(resumed.scope);
|
|
3634
|
+
const permissions = {
|
|
3635
|
+
canReadContent: false,
|
|
3636
|
+
...input.permissions
|
|
3637
|
+
};
|
|
3638
|
+
const activityLimit = clampLimit(input.activityLimit ?? DEFAULT_ACTIVITY_LIMIT);
|
|
3639
|
+
const reviewLimit = clampLimit(input.reviewLimit ?? DEFAULT_REVIEW_LIMIT);
|
|
3640
|
+
const [activity, context, reviewRequests] = permissions.canReadContent ? await Promise.all([
|
|
3641
|
+
new SiteActivityRepository(this.db).findTimeline({
|
|
3642
|
+
scopes: scope.ancestors,
|
|
3643
|
+
since: resumed.createdAt,
|
|
3644
|
+
limit: activityLimit
|
|
3645
|
+
}),
|
|
3646
|
+
new ContextRepository(this.db).diff({
|
|
3647
|
+
scopes: scope.ancestors,
|
|
3648
|
+
since: resumed.createdAt,
|
|
3649
|
+
now: resumedAt
|
|
3650
|
+
}),
|
|
3651
|
+
new ReviewRequestRepository(this.db).list({
|
|
3652
|
+
scopes: scope.ancestors,
|
|
3653
|
+
since: resumed.createdAt,
|
|
3654
|
+
limit: reviewLimit
|
|
3655
|
+
})
|
|
3656
|
+
]) : [
|
|
3657
|
+
null,
|
|
3658
|
+
null,
|
|
3659
|
+
null
|
|
3660
|
+
];
|
|
3661
|
+
return {
|
|
3662
|
+
snapshot: resumed,
|
|
3663
|
+
resumedAt,
|
|
3664
|
+
diff: {
|
|
3665
|
+
scope: summarizeScope(scope),
|
|
3666
|
+
since: resumed.createdAt,
|
|
3667
|
+
activity: permissions.canReadContent ? includedSection({
|
|
3668
|
+
scopes: scope.ancestors,
|
|
3669
|
+
items: activity.items
|
|
3670
|
+
}) : omittedSection({
|
|
3671
|
+
scopes: scope.ancestors,
|
|
3672
|
+
items: []
|
|
3673
|
+
}, "missing content:read permission"),
|
|
3674
|
+
context: permissions.canReadContent ? includedSection({
|
|
3675
|
+
scopes: scope.ancestors,
|
|
3676
|
+
diff: context
|
|
3677
|
+
}) : omittedSection({
|
|
3678
|
+
scopes: scope.ancestors,
|
|
3679
|
+
diff: {
|
|
3680
|
+
newEntries: [],
|
|
3681
|
+
supersededEntries: [],
|
|
3682
|
+
staleEntries: [],
|
|
3683
|
+
reviewedEntries: []
|
|
3684
|
+
}
|
|
3685
|
+
}, "missing content:read permission"),
|
|
3686
|
+
reviewRequests: permissions.canReadContent ? includedSection({
|
|
3687
|
+
scopes: scope.ancestors,
|
|
3688
|
+
items: reviewRequests.items
|
|
3689
|
+
}) : omittedSection({
|
|
3690
|
+
scopes: scope.ancestors,
|
|
3691
|
+
items: []
|
|
3692
|
+
}, "missing content:read permission")
|
|
3693
|
+
}
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
};
|
|
3697
|
+
function summarizeScope(scope) {
|
|
3698
|
+
return {
|
|
3699
|
+
requested: scope.scope,
|
|
3700
|
+
resolved: scope.scope,
|
|
3701
|
+
type: scope.type,
|
|
3702
|
+
ancestors: scope.ancestors,
|
|
3703
|
+
collection: scope.collection,
|
|
3704
|
+
contentId: scope.contentId,
|
|
3705
|
+
pluginId: scope.pluginId
|
|
3706
|
+
};
|
|
3707
|
+
}
|
|
3708
|
+
function includedSection(data) {
|
|
3709
|
+
return {
|
|
3710
|
+
status: "included",
|
|
3711
|
+
data
|
|
3712
|
+
};
|
|
3713
|
+
}
|
|
3714
|
+
function omittedSection(data, reason) {
|
|
3715
|
+
return {
|
|
3716
|
+
status: "omitted",
|
|
3717
|
+
reason,
|
|
3718
|
+
data
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
function clampLimit(value) {
|
|
3722
|
+
return Math.min(Math.max(1, Math.floor(value)), MAX_DIFF_LIMIT);
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
//#endregion
|
|
3726
|
+
//#region src/site-context/assignments.ts
|
|
3727
|
+
const ASSIGNMENT_VALID_FROM = {
|
|
3728
|
+
accept: ["pending"],
|
|
3729
|
+
start: ["accepted", "blocked"],
|
|
3730
|
+
block: ["accepted", "in_progress"],
|
|
3731
|
+
complete: [
|
|
3732
|
+
"pending",
|
|
3733
|
+
"accepted",
|
|
3734
|
+
"in_progress"
|
|
3735
|
+
],
|
|
3736
|
+
decline: ["pending", "accepted"],
|
|
3737
|
+
cancel: [
|
|
3738
|
+
"pending",
|
|
3739
|
+
"accepted",
|
|
3740
|
+
"in_progress",
|
|
3741
|
+
"blocked"
|
|
3742
|
+
]
|
|
3743
|
+
};
|
|
3744
|
+
var AssignmentService = class {
|
|
3745
|
+
repository;
|
|
3746
|
+
constructor(db) {
|
|
3747
|
+
this.db = db;
|
|
3748
|
+
this.repository = new AssignmentRepository(db);
|
|
3749
|
+
}
|
|
3750
|
+
async create(actor, input) {
|
|
3751
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3752
|
+
if (input.relatedHandoffSnapshotId) {
|
|
3753
|
+
const snapshot = await new HandoffSnapshotRepository(this.db).findById(input.relatedHandoffSnapshotId);
|
|
3754
|
+
if (!snapshot) throw new Error(`Handoff snapshot not found: ${input.relatedHandoffSnapshotId}`);
|
|
3755
|
+
if (snapshot.scope !== input.scope) throw new Error("Related handoff snapshot scope must match the assignment scope.");
|
|
3756
|
+
}
|
|
3757
|
+
return this.repository.create({
|
|
3758
|
+
scope: input.scope,
|
|
3759
|
+
assignmentType: input.assignmentType,
|
|
3760
|
+
title: input.title,
|
|
3761
|
+
summary: input.summary,
|
|
3762
|
+
details: input.details,
|
|
3763
|
+
priority: input.priority,
|
|
3764
|
+
assignedByActorType: actor.actorType,
|
|
3765
|
+
assignedByActorId: actor.actorId,
|
|
3766
|
+
assignedToActorType: input.assignedToActorType,
|
|
3767
|
+
assignedToActorId: input.assignedToActorId,
|
|
3768
|
+
metadata: input.metadata,
|
|
3769
|
+
relatedHandoffSnapshotId: input.relatedHandoffSnapshotId,
|
|
3770
|
+
dueAt: input.dueAt,
|
|
3771
|
+
createdAt: input.createdAt
|
|
3772
|
+
});
|
|
3773
|
+
}
|
|
3774
|
+
async get(id, actor) {
|
|
3775
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3776
|
+
const assignment = await this.repository.findById(id);
|
|
3777
|
+
if (!assignment) return null;
|
|
3778
|
+
this.assertReadable(assignment, actor);
|
|
3779
|
+
return assignment;
|
|
3780
|
+
}
|
|
3781
|
+
async list(actor, input = {}) {
|
|
3782
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3783
|
+
return this.repository.list(this.buildListQuery(actor, input));
|
|
3784
|
+
}
|
|
3785
|
+
async accept(input) {
|
|
3786
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3787
|
+
const assignment = await this.loadExisting(input.id);
|
|
3788
|
+
this.assertAssigneeAction("accept", assignment, input.actor);
|
|
3789
|
+
this.assertValidAction("accept", assignment.status);
|
|
3790
|
+
const accepted = await this.repository.accept(assignment.id, {
|
|
3791
|
+
assignedToActorType: assignment.assignedToActorType ?? input.actor.actorType,
|
|
3792
|
+
assignedToActorId: assignment.assignedToActorId ?? input.actor.actorId,
|
|
3793
|
+
acceptedAt: input.at,
|
|
3794
|
+
metadata: assignment.metadata
|
|
3795
|
+
});
|
|
3796
|
+
if (!accepted) throw new Error(`Failed to accept assignment ${assignment.id}.`);
|
|
3797
|
+
return accepted;
|
|
3798
|
+
}
|
|
3799
|
+
async start(input) {
|
|
3800
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3801
|
+
const assignment = await this.loadExisting(input.id);
|
|
3802
|
+
this.assertAssigneeAction("start", assignment, input.actor);
|
|
3803
|
+
this.assertValidAction("start", assignment.status);
|
|
3804
|
+
const started = await this.repository.start(assignment.id, {
|
|
3805
|
+
at: input.at ?? assignment.startedAt ?? void 0,
|
|
3806
|
+
metadata: assignment.metadata
|
|
3807
|
+
});
|
|
3808
|
+
if (!started) throw new Error(`Failed to start assignment ${assignment.id}.`);
|
|
3809
|
+
return started;
|
|
3810
|
+
}
|
|
3811
|
+
async block(input) {
|
|
3812
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3813
|
+
const assignment = await this.loadExisting(input.id);
|
|
3814
|
+
this.assertAssigneeAction("block", assignment, input.actor);
|
|
3815
|
+
this.assertValidAction("block", assignment.status);
|
|
3816
|
+
const blocked = await this.repository.block(assignment.id, {
|
|
3817
|
+
at: input.at,
|
|
3818
|
+
metadata: mergeReasonMetadata(assignment.metadata, "block_reason", input.reason)
|
|
3819
|
+
});
|
|
3820
|
+
if (!blocked) throw new Error(`Failed to block assignment ${assignment.id}.`);
|
|
3821
|
+
return blocked;
|
|
3822
|
+
}
|
|
3823
|
+
async complete(input) {
|
|
3824
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3825
|
+
const assignment = await this.loadExisting(input.id);
|
|
3826
|
+
this.assertAssigneeAction("complete", assignment, input.actor);
|
|
3827
|
+
this.assertValidAction("complete", assignment.status);
|
|
3828
|
+
const completed = await this.repository.complete(assignment.id, {
|
|
3829
|
+
at: input.at,
|
|
3830
|
+
metadata: mergeReasonMetadata(assignment.metadata, "completion_note", input.reason)
|
|
3831
|
+
});
|
|
3832
|
+
if (!completed) throw new Error(`Failed to complete assignment ${assignment.id}.`);
|
|
3833
|
+
return completed;
|
|
3834
|
+
}
|
|
3835
|
+
async decline(input) {
|
|
3836
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3837
|
+
const assignment = await this.loadExisting(input.id);
|
|
3838
|
+
this.assertAssigneeAction("decline", assignment, input.actor);
|
|
3839
|
+
this.assertValidAction("decline", assignment.status);
|
|
3840
|
+
const declined = await this.repository.decline(assignment.id, {
|
|
3841
|
+
at: input.at,
|
|
3842
|
+
metadata: mergeReasonMetadata(assignment.metadata, "decline_reason", input.reason)
|
|
3843
|
+
});
|
|
3844
|
+
if (!declined) throw new Error(`Failed to decline assignment ${assignment.id}.`);
|
|
3845
|
+
return declined;
|
|
3846
|
+
}
|
|
3847
|
+
async cancel(input) {
|
|
3848
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3849
|
+
const assignment = await this.loadExisting(input.id);
|
|
3850
|
+
this.assertCanceller(assignment, input.actor);
|
|
3851
|
+
this.assertValidAction("cancel", assignment.status);
|
|
3852
|
+
const cancelled = await this.repository.cancel(assignment.id, {
|
|
3853
|
+
at: input.at,
|
|
3854
|
+
metadata: mergeReasonMetadata(assignment.metadata, "cancel_reason", input.reason)
|
|
3855
|
+
});
|
|
3856
|
+
if (!cancelled) throw new Error(`Failed to cancel assignment ${assignment.id}.`);
|
|
3857
|
+
return cancelled;
|
|
3858
|
+
}
|
|
3859
|
+
buildListQuery(actor, input) {
|
|
3860
|
+
const query = {
|
|
3861
|
+
scope: input.scope,
|
|
3862
|
+
scopes: input.scopes,
|
|
3863
|
+
statuses: input.statuses,
|
|
3864
|
+
priority: input.priority,
|
|
3865
|
+
assignmentType: input.assignmentType,
|
|
3866
|
+
since: input.since,
|
|
3867
|
+
limit: input.limit,
|
|
3868
|
+
cursor: input.cursor
|
|
3869
|
+
};
|
|
3870
|
+
if (isWorkflowSupervisor(actor.userRole)) {
|
|
3871
|
+
if (input.assignedToMe && !input.assignedByMe) {
|
|
3872
|
+
query.assignedToActorType = actor.actorType;
|
|
3873
|
+
query.assignedToActorId = actor.actorId;
|
|
3874
|
+
} else if (input.assignedByMe && !input.assignedToMe) {
|
|
3875
|
+
query.assignedByActorType = actor.actorType;
|
|
3876
|
+
query.assignedByActorId = actor.actorId;
|
|
3877
|
+
}
|
|
3878
|
+
return query;
|
|
3879
|
+
}
|
|
3880
|
+
if (input.assignedToMe && !input.assignedByMe) {
|
|
3881
|
+
query.assignedToActorType = actor.actorType;
|
|
3882
|
+
query.assignedToActorId = actor.actorId;
|
|
3883
|
+
return query;
|
|
3884
|
+
}
|
|
3885
|
+
if (input.assignedByMe && !input.assignedToMe) {
|
|
3886
|
+
query.assignedByActorType = actor.actorType;
|
|
3887
|
+
query.assignedByActorId = actor.actorId;
|
|
3888
|
+
return query;
|
|
3889
|
+
}
|
|
3890
|
+
query.participantActor = {
|
|
3891
|
+
actorType: actor.actorType,
|
|
3892
|
+
actorId: actor.actorId
|
|
3893
|
+
};
|
|
3894
|
+
return query;
|
|
3895
|
+
}
|
|
3896
|
+
async loadExisting(id) {
|
|
3897
|
+
const assignment = await this.repository.findById(id);
|
|
3898
|
+
if (!assignment) throw new Error(`Assignment not found: ${id}`);
|
|
3899
|
+
return assignment;
|
|
3900
|
+
}
|
|
3901
|
+
assertReadable(assignment, actor) {
|
|
3902
|
+
if (isWorkflowSupervisor(actor.userRole)) return;
|
|
3903
|
+
if (matchesActor$1(assignment.assignedByActorType, assignment.assignedByActorId, actor) || matchesActor$1(assignment.assignedToActorType, assignment.assignedToActorId, actor)) return;
|
|
3904
|
+
throw new Error("Insufficient permissions to access this assignment.");
|
|
3905
|
+
}
|
|
3906
|
+
assertAssigneeAction(action, assignment, actor) {
|
|
3907
|
+
if (isWorkflowSupervisor(actor.userRole)) return;
|
|
3908
|
+
if (!assignment.assignedToActorType || !assignment.assignedToActorId) {
|
|
3909
|
+
if (action === "accept") return;
|
|
3910
|
+
throw new Error(`Only an assigned actor can ${action} this assignment.`);
|
|
3911
|
+
}
|
|
3912
|
+
if (matchesActor$1(assignment.assignedToActorType, assignment.assignedToActorId, actor)) return;
|
|
3913
|
+
throw new Error(`Only the assignee can ${action} this assignment.`);
|
|
3914
|
+
}
|
|
3915
|
+
assertCanceller(assignment, actor) {
|
|
3916
|
+
if (isWorkflowSupervisor(actor.userRole)) return;
|
|
3917
|
+
if (matchesActor$1(assignment.assignedByActorType, assignment.assignedByActorId, actor)) return;
|
|
3918
|
+
throw new Error("Only the assigner or an editor can cancel this assignment.");
|
|
3919
|
+
}
|
|
3920
|
+
assertValidAction(action, currentStatus) {
|
|
3921
|
+
const blocker = validateAssignmentAction(action, currentStatus);
|
|
3922
|
+
if (blocker) throw new Error(blocker);
|
|
3923
|
+
}
|
|
3924
|
+
};
|
|
3925
|
+
function validateAssignmentAction(action, currentStatus) {
|
|
3926
|
+
const validFrom = ASSIGNMENT_VALID_FROM[action];
|
|
3927
|
+
if (validFrom.includes(currentStatus)) return null;
|
|
3928
|
+
return `Cannot ${action} an assignment that is currently '${currentStatus}'. Must be: ${validFrom.join(" or ")}`;
|
|
3929
|
+
}
|
|
3930
|
+
function matchesActor$1(actorType, actorId, actor) {
|
|
3931
|
+
return actorType === actor.actorType && actorId === actor.actorId;
|
|
3932
|
+
}
|
|
3933
|
+
function isWorkflowSupervisor(userRole) {
|
|
3934
|
+
return userRole >= Role.EDITOR;
|
|
3935
|
+
}
|
|
3936
|
+
function mergeReasonMetadata(metadata, key, reason) {
|
|
3937
|
+
if (!reason) return metadata;
|
|
3938
|
+
return {
|
|
3939
|
+
...metadata,
|
|
3940
|
+
[key]: reason
|
|
3941
|
+
};
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
//#endregion
|
|
3945
|
+
//#region src/site-context/hitl.ts
|
|
3946
|
+
const HITL_REQUEST_VALID_FROM = { resolve: ["pending"] };
|
|
3947
|
+
var HitlRequestService = class {
|
|
3948
|
+
repository;
|
|
3949
|
+
assignments;
|
|
3950
|
+
constructor(db) {
|
|
3951
|
+
this.db = db;
|
|
3952
|
+
this.repository = new HitlRequestRepository(db);
|
|
3953
|
+
this.assignments = new AssignmentRepository(db);
|
|
3954
|
+
}
|
|
3955
|
+
async create(actor, input) {
|
|
3956
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3957
|
+
parseSiteBriefingScope(input.scope);
|
|
3958
|
+
const assignment = input.relatedAssignmentId ? await this.assignments.findById(input.relatedAssignmentId) : null;
|
|
3959
|
+
if (input.relatedAssignmentId && !assignment) throw new Error(`Assignment not found: ${input.relatedAssignmentId}`);
|
|
3960
|
+
if (assignment) {
|
|
3961
|
+
this.assertRelatedAssignmentReadable(assignment, actor);
|
|
3962
|
+
if (assignment.scope !== input.scope) throw new Error("Related assignment scope must match the HITL request scope.");
|
|
3963
|
+
if (assignment.relatedHitlRequestId) throw new Error(`Assignment ${assignment.id} is already linked to HITL request ${assignment.relatedHitlRequestId}.`);
|
|
3964
|
+
}
|
|
3965
|
+
if (input.relatedHandoffSnapshotId) {
|
|
3966
|
+
const snapshot = await new HandoffSnapshotRepository(this.db).findById(input.relatedHandoffSnapshotId);
|
|
3967
|
+
if (!snapshot) throw new Error(`Handoff snapshot not found: ${input.relatedHandoffSnapshotId}`);
|
|
3968
|
+
this.assertRelatedHandoffSnapshotReadable(snapshot, actor);
|
|
3969
|
+
if (snapshot.scope !== input.scope) throw new Error("Related handoff snapshot scope must match the HITL request scope.");
|
|
3970
|
+
}
|
|
3971
|
+
const request = await this.repository.create({
|
|
3972
|
+
scope: input.scope,
|
|
3973
|
+
actionType: input.actionType,
|
|
3974
|
+
title: input.title,
|
|
3975
|
+
summary: input.summary,
|
|
3976
|
+
riskReason: input.riskReason,
|
|
3977
|
+
reviewPayload: input.reviewPayload,
|
|
3978
|
+
targetRefType: input.targetRefType,
|
|
3979
|
+
targetRefId: input.targetRefId,
|
|
3980
|
+
requestedByActorType: actor.actorType,
|
|
3981
|
+
requestedByActorId: actor.actorId,
|
|
3982
|
+
requestedAuthMetadata: actor.authMetadata,
|
|
3983
|
+
priority: input.priority,
|
|
3984
|
+
metadata: input.metadata,
|
|
3985
|
+
relatedAssignmentId: input.relatedAssignmentId,
|
|
3986
|
+
relatedHandoffSnapshotId: input.relatedHandoffSnapshotId,
|
|
3987
|
+
slaDueAt: input.slaDueAt,
|
|
3988
|
+
createdAt: input.createdAt
|
|
3989
|
+
});
|
|
3990
|
+
if (assignment) {
|
|
3991
|
+
if (!await this.assignments.linkHitlRequest(assignment.id, request.id, request.createdAt)) throw new Error(`Failed to link assignment ${assignment.id} to HITL request ${request.id}.`);
|
|
3992
|
+
}
|
|
3993
|
+
return request;
|
|
3994
|
+
}
|
|
3995
|
+
async get(id, actor) {
|
|
3996
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
3997
|
+
const request = await this.repository.findById(id);
|
|
3998
|
+
if (!request) return null;
|
|
3999
|
+
this.assertReadable(request, actor);
|
|
4000
|
+
return request;
|
|
4001
|
+
}
|
|
4002
|
+
async list(actor, input = {}) {
|
|
4003
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
4004
|
+
return this.repository.list(this.buildListQuery(actor, input));
|
|
4005
|
+
}
|
|
4006
|
+
async resolve(input) {
|
|
4007
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
4008
|
+
const request = await this.loadExisting(input.id);
|
|
4009
|
+
this.assertResolver(input.actor);
|
|
4010
|
+
this.assertReadable(request, input.actor);
|
|
4011
|
+
this.assertValidAction("resolve", request.status);
|
|
4012
|
+
const resolved = await this.repository.resolve(request.id, {
|
|
4013
|
+
decision: input.decision,
|
|
4014
|
+
resolvedByActorType: input.actor.actorType,
|
|
4015
|
+
resolvedByActorId: input.actor.actorId,
|
|
4016
|
+
resolvedAuthMetadata: input.actor.authMetadata,
|
|
4017
|
+
reviewNote: input.reviewNote,
|
|
4018
|
+
resolvedAt: input.at
|
|
4019
|
+
});
|
|
4020
|
+
if (!resolved) throw new Error(`Failed to resolve HITL request ${request.id}.`);
|
|
4021
|
+
return resolved;
|
|
4022
|
+
}
|
|
4023
|
+
buildListQuery(actor, input) {
|
|
4024
|
+
const query = {
|
|
4025
|
+
scope: input.scope,
|
|
4026
|
+
scopes: input.scopes,
|
|
4027
|
+
statuses: input.statuses,
|
|
4028
|
+
priority: input.priority,
|
|
4029
|
+
actionType: input.actionType,
|
|
4030
|
+
targetRefType: input.targetRefType,
|
|
4031
|
+
targetRefId: input.targetRefId,
|
|
4032
|
+
relatedAssignmentId: input.relatedAssignmentId,
|
|
4033
|
+
since: input.since,
|
|
4034
|
+
limit: input.limit,
|
|
4035
|
+
cursor: input.cursor
|
|
4036
|
+
};
|
|
4037
|
+
if (isHitlSupervisor(actor.userRole)) return query;
|
|
4038
|
+
return {
|
|
4039
|
+
...query,
|
|
4040
|
+
requestedByActorType: actor.actorType,
|
|
4041
|
+
requestedByActorId: actor.actorId
|
|
4042
|
+
};
|
|
4043
|
+
}
|
|
4044
|
+
async loadExisting(id) {
|
|
4045
|
+
const request = await this.repository.findById(id);
|
|
4046
|
+
if (!request) throw new Error(`HITL request not found: ${id}`);
|
|
4047
|
+
return request;
|
|
4048
|
+
}
|
|
4049
|
+
assertReadable(request, actor) {
|
|
4050
|
+
if (isHitlSupervisor(actor.userRole)) return;
|
|
4051
|
+
if (request.requestedByActorType === actor.actorType && request.requestedByActorId === actor.actorId) return;
|
|
4052
|
+
throw new Error("Insufficient permissions to read this HITL request.");
|
|
4053
|
+
}
|
|
4054
|
+
assertResolver(actor) {
|
|
4055
|
+
if (isHitlSupervisor(actor.userRole)) return;
|
|
4056
|
+
throw new Error("HITL requests may only be resolved by an editor or admin.");
|
|
4057
|
+
}
|
|
4058
|
+
assertRelatedAssignmentReadable(assignment, actor) {
|
|
4059
|
+
if (isHitlSupervisor(actor.userRole)) return;
|
|
4060
|
+
if (matchesActor(assignment.assignedByActorType, assignment.assignedByActorId, actor) || matchesActor(assignment.assignedToActorType, assignment.assignedToActorId, actor)) return;
|
|
4061
|
+
throw new Error("Insufficient permissions to link this assignment to a HITL request.");
|
|
4062
|
+
}
|
|
4063
|
+
assertRelatedHandoffSnapshotReadable(snapshot, actor) {
|
|
4064
|
+
if (isHitlSupervisor(actor.userRole)) return;
|
|
4065
|
+
if (matchesActor(snapshot.actorType, snapshot.actorId, actor)) return;
|
|
4066
|
+
throw new Error("Insufficient permissions to link this handoff snapshot to a HITL request.");
|
|
4067
|
+
}
|
|
4068
|
+
assertValidAction(action, status) {
|
|
4069
|
+
const validFrom = HITL_REQUEST_VALID_FROM[action];
|
|
4070
|
+
if (!validFrom.includes(status)) throw new Error(`Cannot ${action} a HITL request in status "${status}". Valid statuses: ${validFrom.join(", ")}.`);
|
|
4071
|
+
}
|
|
4072
|
+
};
|
|
4073
|
+
function isHitlSupervisor(role) {
|
|
4074
|
+
return role >= Role.EDITOR;
|
|
4075
|
+
}
|
|
4076
|
+
function matchesActor(actorType, actorId, actor) {
|
|
4077
|
+
return actorType === actor.actorType && actorId === actor.actorId;
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4080
|
+
//#endregion
|
|
4081
|
+
//#region src/site-context/workflow-hitl.ts
|
|
4082
|
+
var WorkflowHitlCoordinator = class {
|
|
4083
|
+
constructor(db) {
|
|
4084
|
+
this.db = db;
|
|
4085
|
+
}
|
|
4086
|
+
async ensureRequest(input) {
|
|
4087
|
+
assertExperimentalSiteContextWorkflowsEnabled();
|
|
4088
|
+
const matching = (await new HitlRequestRepository(this.db).list({
|
|
4089
|
+
scope: input.action.scope,
|
|
4090
|
+
statuses: ["pending", "approved"],
|
|
4091
|
+
actionType: input.action.actionType,
|
|
4092
|
+
targetRefType: input.action.targetRefType,
|
|
4093
|
+
targetRefId: input.action.targetRefId,
|
|
4094
|
+
limit: 50
|
|
4095
|
+
})).items.find((item) => {
|
|
4096
|
+
const actionHash = item.metadata.actionHash;
|
|
4097
|
+
return typeof actionHash === "string" && actionHash === input.action.actionHash;
|
|
4098
|
+
});
|
|
4099
|
+
if (matching) return {
|
|
4100
|
+
created: false,
|
|
4101
|
+
request: matching
|
|
4102
|
+
};
|
|
4103
|
+
return {
|
|
4104
|
+
created: true,
|
|
4105
|
+
request: await new HitlRequestService(this.db).create(input.actor, {
|
|
4106
|
+
scope: input.action.scope,
|
|
4107
|
+
actionType: input.action.actionType,
|
|
4108
|
+
title: input.action.title,
|
|
4109
|
+
summary: input.action.summary,
|
|
4110
|
+
riskReason: input.action.riskReason,
|
|
4111
|
+
reviewPayload: input.action.reviewPayload,
|
|
4112
|
+
targetRefType: input.action.targetRefType,
|
|
4113
|
+
targetRefId: input.action.targetRefId,
|
|
4114
|
+
priority: input.action.priority,
|
|
4115
|
+
metadata: input.action.metadata
|
|
4116
|
+
})
|
|
4117
|
+
};
|
|
4118
|
+
}
|
|
4119
|
+
};
|
|
4120
|
+
|
|
4121
|
+
//#endregion
|
|
4122
|
+
export { HITL_REQUEST_STATUSES as C, ASSIGNMENT_STATUSES as E, HITL_REQUEST_PRIORITIES as S, ASSIGNMENT_PRIORITIES as T, RiskPolicyEvaluator as _, ActorExpertiseService as a, toPublicContextEntry as b, SectionHitlPayloadBuilder as c, RedirectHitlPayloadBuilder as d, SettingsHitlPayloadBuilder as f, SchemaHitlPayloadBuilder as g, PluginHitlPayloadBuilder as h, HandoffSnapshotStore as i, TaxonomyHitlPayloadBuilder as l, ExclusiveHookHitlPayloadBuilder as m, HitlRequestService as n, EntityResolver as o, WordPressImportHitlPayloadBuilder as p, AssignmentService as r, WidgetHitlPayloadBuilder as s, WorkflowHitlCoordinator as t, MenuHitlPayloadBuilder as u, ReviewPayloadBuilder as v, ASSIGNMENT_ACTOR_TYPES as w, toPublicContextEntryPage as x, toPublicContextDiff as y };
|