emdash 0.40.0 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-token-CbKDcPJi.mjs +26 -0
- package/dist/action-token-CbKDcPJi.mjs.map +1 -0
- package/dist/activation-PtniBLt9.mjs +382 -0
- package/dist/activation-PtniBLt9.mjs.map +1 -0
- package/dist/adapters-C_Tf5x90.d.mts +171 -0
- package/dist/adapters-C_Tf5x90.d.mts.map +1 -0
- package/dist/allowed-origins-U1z4TdEZ.mjs +116 -0
- package/dist/allowed-origins-U1z4TdEZ.mjs.map +1 -0
- package/dist/api/route-utils.d.mts +4 -4
- package/dist/api/route-utils.mjs +46 -45
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +10 -10
- package/dist/api-CXwQkV_m.mjs +5122 -0
- package/dist/api-CXwQkV_m.mjs.map +1 -0
- package/dist/api-tokens-89WlohEN.mjs +166 -0
- package/dist/api-tokens-89WlohEN.mjs.map +1 -0
- package/dist/apply-Do3KNnUk.mjs +1659 -0
- package/dist/apply-Do3KNnUk.mjs.map +1 -0
- package/dist/approvals-CAX9Yw33.mjs +460 -0
- package/dist/approvals-CAX9Yw33.mjs.map +1 -0
- package/dist/artifact-fetch-B9W4CEST.mjs +659 -0
- package/dist/artifact-fetch-B9W4CEST.mjs.map +1 -0
- package/dist/astro/index.d.mts +14 -14
- package/dist/astro/index.mjs +26 -30
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +12 -12
- package/dist/astro/middleware/auth.mjs +10 -10
- package/dist/astro/middleware/media-usage-write-fence.mjs +5 -5
- package/dist/astro/middleware/redirect.mjs +7 -7
- package/dist/astro/middleware/request-context.mjs +3 -3
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +12 -12
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +110 -106
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +14 -14
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +14 -14
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +7 -7
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +7 -7
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +17 -17
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +17 -17
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +17 -17
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +23 -23
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +23 -23
- package/dist/astro/routes/api/admin/bylines/index.mjs +23 -23
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +18 -18
- package/dist/astro/routes/api/admin/comments/_id_.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/bulk.mjs +17 -17
- package/dist/astro/routes/api/admin/comments/counts.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/index.mjs +17 -17
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
- package/dist/astro/routes/api/admin/media-usage/activation.mjs +20 -20
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +22 -22
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +22 -22
- package/dist/astro/routes/api/admin/media-usage/progress.mjs +25 -25
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +31 -31
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs +22 -22
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +22 -22
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +72 -68
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +7 -7
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +71 -67
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +72 -68
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +82 -78
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +72 -68
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +5 -5
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +71 -67
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +81 -77
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +71 -67
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +72 -68
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +83 -79
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +9 -9
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +74 -70
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/publisher-handle.mjs +7 -7
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +72 -68
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +71 -67
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/scheduled-policy-rejections/_collection_/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +71 -67
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +5 -5
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +71 -67
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/transfer/_...path_.mjs +4 -4
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.mjs +40 -40
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.mjs +40 -40
- package/dist/astro/routes/api/admin/transfer/approvals/index.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/capabilities.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.mjs +43 -43
- package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.mjs +43 -43
- package/dist/astro/routes/api/admin/transfer/exports/_id_/index.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.mjs +43 -43
- package/dist/astro/routes/api/admin/transfer/exports/index.mjs +44 -44
- package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.mjs +44 -44
- package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.mjs +44 -44
- package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/imports/_id_/index.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.mjs +44 -44
- package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.mjs +42 -42
- package/dist/astro/routes/api/admin/transfer/imports/index.mjs +44 -44
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +5 -5
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +4 -4
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +15 -15
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +6 -6
- package/dist/astro/routes/api/admin/users/index.mjs +14 -14
- package/dist/astro/routes/api/auth/dev-bypass.mjs +6 -6
- package/dist/astro/routes/api/auth/invite/accept.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/complete.mjs +18 -18
- package/dist/astro/routes/api/auth/invite/index.mjs +16 -16
- package/dist/astro/routes/api/auth/invite/register-options.mjs +17 -17
- package/dist/astro/routes/api/auth/logout.mjs +5 -5
- package/dist/astro/routes/api/auth/magic-link/send.mjs +18 -18
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +14 -14
- package/dist/astro/routes/api/auth/me.mjs +15 -15
- package/dist/astro/routes/api/auth/mode.mjs +5 -5
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +14 -14
- package/dist/astro/routes/api/auth/passkey/index.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/options.mjs +19 -19
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +17 -17
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +18 -18
- package/dist/astro/routes/api/auth/passkey/verify.mjs +18 -18
- package/dist/astro/routes/api/auth/signup/complete.mjs +18 -18
- package/dist/astro/routes/api/auth/signup/request.mjs +17 -17
- package/dist/astro/routes/api/auth/signup/verify.mjs +4 -4
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +12 -12
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +18 -18
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +17 -17
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/lock.mjs +17 -17
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/plugin-extensions/_pluginId_/_kind_/_extensionId_.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +31 -31
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +17 -17
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts +1 -2
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +42 -53
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +34 -29
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +17 -17
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +25 -24
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +17 -17
- package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +18 -18
- package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/authors.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/index.mjs +15 -15
- package/dist/astro/routes/api/content/_collection_/trash.mjs +15 -15
- package/dist/astro/routes/api/dashboard.mjs +10 -9
- package/dist/astro/routes/api/dashboard.mjs.map +1 -1
- package/dist/astro/routes/api/dev/emails.mjs +5 -5
- package/dist/astro/routes/api/health.mjs +6 -6
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +23 -23
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +13 -13
- package/dist/astro/routes/api/import/wordpress/execute.mjs +16 -16
- package/dist/astro/routes/api/import/wordpress/media.d.mts +12 -12
- package/dist/astro/routes/api/import/wordpress/media.mjs +19 -19
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +18 -18
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +13 -13
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +28 -28
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +23 -23
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +13 -13
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +34 -34
- package/dist/astro/routes/api/manifest.mjs +6 -6
- package/dist/astro/routes/api/mcp.mjs +98 -118
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +19 -19
- package/dist/astro/routes/api/media/_id_/replace.mjs +16 -16
- package/dist/astro/routes/api/media/_id_/upload.mjs +9 -9
- package/dist/astro/routes/api/media/_id_/usage.mjs +33 -33
- package/dist/astro/routes/api/media/_id_.mjs +31 -31
- package/dist/astro/routes/api/media/asset/_id_/_filename_.mjs +6 -6
- package/dist/astro/routes/api/media/file/_...key_.mjs +9 -9
- package/dist/astro/routes/api/media/folders/_id_.mjs +16 -16
- package/dist/astro/routes/api/media/folders/index.mjs +16 -16
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +5 -5
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +5 -5
- package/dist/astro/routes/api/media/providers/index.mjs +5 -5
- package/dist/astro/routes/api/media/upload-url.mjs +18 -18
- package/dist/astro/routes/api/media.mjs +35 -35
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +17 -17
- package/dist/astro/routes/api/menus/_name_/items.mjs +17 -17
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +17 -17
- package/dist/astro/routes/api/menus/_name_/translations.mjs +17 -17
- package/dist/astro/routes/api/menus/_name_.mjs +17 -17
- package/dist/astro/routes/api/menus/index.mjs +17 -17
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.d.mts +2 -1
- package/dist/astro/routes/api/oauth/device/authorize.d.mts.map +1 -1
- package/dist/astro/routes/api/oauth/device/authorize.mjs +23 -9
- package/dist/astro/routes/api/oauth/device/authorize.mjs.map +1 -1
- package/dist/astro/routes/api/oauth/device/code.mjs +11 -11
- package/dist/astro/routes/api/oauth/device/token.mjs +10 -10
- package/dist/astro/routes/api/oauth/register.mjs +5 -5
- package/dist/astro/routes/api/oauth/token/refresh.mjs +8 -8
- package/dist/astro/routes/api/oauth/token/revoke.mjs +8 -8
- package/dist/astro/routes/api/oauth/token.mjs +8 -8
- package/dist/astro/routes/api/openapi.json.mjs +20 -20
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +11 -11
- package/dist/astro/routes/api/redirects/404s/index.mjs +19 -19
- package/dist/astro/routes/api/redirects/404s/summary.mjs +19 -19
- package/dist/astro/routes/api/redirects/_id_.mjs +19 -19
- package/dist/astro/routes/api/redirects/index.mjs +19 -19
- package/dist/astro/routes/api/relations/_id_/index.mjs +34 -29
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/index.mjs +36 -31
- package/dist/astro/routes/api/relations/index.mjs.map +1 -1
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +5 -5
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +5 -5
- package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs +71 -67
- package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs +71 -67
- package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs.map +1 -1
- package/dist/astro/routes/api/schema/block-types/index.mjs +71 -67
- package/dist/astro/routes/api/schema/block-types/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +73 -69
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +71 -67
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +71 -67
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +71 -67
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +71 -67
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/reorder.mjs +71 -67
- package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +16 -16
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +71 -67
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +71 -67
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +19 -19
- package/dist/astro/routes/api/search/index.mjs +18 -18
- package/dist/astro/routes/api/search/rebuild.mjs +19 -19
- package/dist/astro/routes/api/search/stats.mjs +9 -9
- package/dist/astro/routes/api/search/suggest.mjs +18 -18
- package/dist/astro/routes/api/sections/_slug_.mjs +18 -18
- package/dist/astro/routes/api/sections/index.mjs +18 -18
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +7 -7
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +7 -7
- package/dist/astro/routes/api/settings/backups/export.mjs +7 -7
- package/dist/astro/routes/api/settings/backups/index.mjs +8 -8
- package/dist/astro/routes/api/settings/email.mjs +6 -6
- package/dist/astro/routes/api/settings.mjs +21 -21
- package/dist/astro/routes/api/setup/admin-verify.mjs +19 -19
- package/dist/astro/routes/api/setup/admin.mjs +18 -18
- package/dist/astro/routes/api/setup/dev-bypass.mjs +45 -39
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
- package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/index.mjs +80 -49
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +6 -6
- package/dist/astro/routes/api/snapshot.mjs +9 -9
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +23 -23
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +23 -23
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +23 -23
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +23 -23
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +22 -22
- package/dist/astro/routes/api/taxonomies/_name_.mjs +23 -23
- package/dist/astro/routes/api/taxonomies/bulk-tag.mjs +36 -35
- package/dist/astro/routes/api/taxonomies/bulk-tag.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.mjs +23 -23
- package/dist/astro/routes/api/themes/preview.mjs +6 -6
- package/dist/astro/routes/api/typegen.mjs +8 -8
- package/dist/astro/routes/api/visual-editing/action-token.mjs +6 -6
- package/dist/astro/routes/api/visual-editing/content/_collection_/_id_/publish.mjs +19 -19
- package/dist/astro/routes/api/visual-editing/toolbar-labels.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +16 -16
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +19 -19
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +19 -19
- package/dist/astro/routes/api/widget-areas/_name_.mjs +9 -9
- package/dist/astro/routes/api/widget-areas/index.mjs +19 -19
- package/dist/astro/routes/api/widget-components.mjs +5 -5
- package/dist/astro/routes/robots.txt.mjs +6 -6
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +9 -9
- package/dist/astro/routes/sitemap.xml.mjs +7 -7
- package/dist/astro/types.d.mts +28 -18
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/audit-CrgmE9LE.mjs +127 -0
- package/dist/audit-CrgmE9LE.mjs.map +1 -0
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/authoritative-records-C2YzIdbu.mjs +5172 -0
- package/dist/authoritative-records-C2YzIdbu.mjs.map +1 -0
- package/dist/authorize-WgdKF8QY.mjs +49 -0
- package/dist/authorize-WgdKF8QY.mjs.map +1 -0
- package/dist/backup-Ck8_lMO3.mjs +246 -0
- package/dist/backup-Ck8_lMO3.mjs.map +1 -0
- package/dist/block-types-BQ90asVI.mjs +132 -0
- package/dist/block-types-BQ90asVI.mjs.map +1 -0
- package/dist/block-values-ACFqgB1D.mjs +175 -0
- package/dist/block-values-ACFqgB1D.mjs.map +1 -0
- package/dist/bundle-Db1iNMv_.mjs +667 -0
- package/dist/bundle-Db1iNMv_.mjs.map +1 -0
- package/dist/byline-BayaYGoj.mjs +909 -0
- package/dist/byline-BayaYGoj.mjs.map +1 -0
- package/dist/byline-fields-B8HH-35f.mjs +254 -0
- package/dist/byline-fields-B8HH-35f.mjs.map +1 -0
- package/dist/byline-fields-hMPjf6Eu.mjs +123 -0
- package/dist/byline-fields-hMPjf6Eu.mjs.map +1 -0
- package/dist/byline-registry-B_EK_TQf.mjs +406 -0
- package/dist/byline-registry-B_EK_TQf.mjs.map +1 -0
- package/dist/bylines-DFAaFXWi.mjs +214 -0
- package/dist/bylines-DFAaFXWi.mjs.map +1 -0
- package/dist/bylines-DkIxUxmr.mjs +232 -0
- package/dist/bylines-DkIxUxmr.mjs.map +1 -0
- package/dist/cache-DkvhQ8XE.mjs +108 -0
- package/dist/cache-DkvhQ8XE.mjs.map +1 -0
- package/dist/canonical-CcSuuTlH.mjs +174 -0
- package/dist/canonical-CcSuuTlH.mjs.map +1 -0
- package/dist/capture-triggers-DJEoOTOq.mjs +362 -0
- package/dist/capture-triggers-DJEoOTOq.mjs.map +1 -0
- package/dist/challenge-store-CvBl81AQ.mjs +51 -0
- package/dist/challenge-store-CvBl81AQ.mjs.map +1 -0
- package/dist/checksum-CClweQzI.mjs +162 -0
- package/dist/checksum-CClweQzI.mjs.map +1 -0
- package/dist/chrome-tags-D5caJwwp.mjs +24 -0
- package/dist/chrome-tags-D5caJwwp.mjs.map +1 -0
- package/dist/chunks-DwBaj8aT.mjs +25 -0
- package/dist/chunks-DwBaj8aT.mjs.map +1 -0
- package/dist/cli/index.mjs +374 -761
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +18 -18
- package/dist/client/index.mjs +2 -2
- package/dist/collection-deletion-CLCdXsnV.mjs +436 -0
- package/dist/collection-deletion-CLCdXsnV.mjs.map +1 -0
- package/dist/collection-deletion-processor-hNw8x01F.mjs +217 -0
- package/dist/collection-deletion-processor-hNw8x01F.mjs.map +1 -0
- package/dist/collection-slugs-cache-CUuiduLq.mjs +85 -0
- package/dist/collection-slugs-cache-CUuiduLq.mjs.map +1 -0
- package/dist/comment-D2uvL63j.mjs +313 -0
- package/dist/comment-D2uvL63j.mjs.map +1 -0
- package/dist/comment-reaction-DevUuzg4.mjs +85 -0
- package/dist/comment-reaction-DevUuzg4.mjs.map +1 -0
- package/dist/comments-Cq0s7_hO.mjs +195 -0
- package/dist/comments-Cq0s7_hO.mjs.map +1 -0
- package/dist/comments-M4EDYAHw.mjs +99 -0
- package/dist/comments-M4EDYAHw.mjs.map +1 -0
- package/dist/components-CuIGwXMs.mjs +108 -0
- package/dist/components-CuIGwXMs.mjs.map +1 -0
- package/dist/config-52Ea5IWt.d.mts +39 -0
- package/dist/config-52Ea5IWt.d.mts.map +1 -0
- package/dist/config-CCz3xOLc.mjs +242 -0
- package/dist/config-CCz3xOLc.mjs.map +1 -0
- package/dist/config-D3d5Qudq.d.mts +6669 -0
- package/dist/config-D3d5Qudq.d.mts.map +1 -0
- package/dist/content-CbnufDsM.mjs +2018 -0
- package/dist/content-CbnufDsM.mjs.map +1 -0
- package/dist/content-list-query-D_7B1FE5.d.mts +26 -0
- package/dist/content-list-query-D_7B1FE5.d.mts.map +1 -0
- package/dist/content-policy-CYu6K1KB.mjs +46 -0
- package/dist/content-policy-CYu6K1KB.mjs.map +1 -0
- package/dist/content-repair-DlDUgFGp.mjs +442 -0
- package/dist/content-repair-DlDUgFGp.mjs.map +1 -0
- package/dist/context-CGQLA1-a.mjs +1922 -0
- package/dist/context-CGQLA1-a.mjs.map +1 -0
- package/dist/cron-c4D5C5Fx.mjs +284 -0
- package/dist/cron-c4D5C5Fx.mjs.map +1 -0
- package/dist/dashboard-CBq9kmOn.mjs +151 -0
- package/dist/dashboard-CBq9kmOn.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql-migrations.d.mts +1 -1
- package/dist/db/libsql-migrations.mjs +4 -4
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres-migrations.d.mts +1 -1
- package/dist/db/postgres-migrations.mjs +4 -4
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite-migrations.d.mts +1 -1
- package/dist/db/sqlite-migrations.mjs +4 -4
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.mjs +1 -1
- package/dist/default-DPsys51U.mjs +81 -0
- package/dist/default-DPsys51U.mjs.map +1 -0
- package/dist/deferred-tasks-CZ7rzBdo.d.mts +9 -0
- package/dist/deferred-tasks-CZ7rzBdo.d.mts.map +1 -0
- package/dist/dev-typegen-HRxwLRcT.mjs +29 -0
- package/dist/dev-typegen-HRxwLRcT.mjs.map +1 -0
- package/dist/device-flow-OcSqE9uY.mjs +521 -0
- package/dist/device-flow-OcSqE9uY.mjs.map +1 -0
- package/dist/digest-DVZ--cE9.mjs +194 -0
- package/dist/digest-DVZ--cE9.mjs.map +1 -0
- package/dist/direct-executor-DyjAqkpT.mjs +104 -0
- package/dist/direct-executor-DyjAqkpT.mjs.map +1 -0
- package/dist/dist-FPR93_xf.mjs +810 -0
- package/dist/dist-FPR93_xf.mjs.map +1 -0
- package/dist/email-console-DyQIgEnz.mjs +50 -0
- package/dist/email-console-DyQIgEnz.mjs.map +1 -0
- package/dist/email-locale-BYb16rVd.mjs +25 -0
- package/dist/email-locale-BYb16rVd.mjs.map +1 -0
- package/dist/emdash-runtime-CrzxJFmF.mjs +4214 -0
- package/dist/emdash-runtime-CrzxJFmF.mjs.map +1 -0
- package/dist/enrich-DBtCncE2.mjs +194 -0
- package/dist/enrich-DBtCncE2.mjs.map +1 -0
- package/dist/entry-lock-BN5y4YbO.d.mts +4481 -0
- package/dist/entry-lock-BN5y4YbO.d.mts.map +1 -0
- package/dist/entry-lock-BaRBHCsH.mjs +1173 -0
- package/dist/entry-lock-BaRBHCsH.mjs.map +1 -0
- package/dist/entry-lock-C6NcnZPV.mjs +163 -0
- package/dist/entry-lock-C6NcnZPV.mjs.map +1 -0
- package/dist/entry-locks-BTyQLdbC.mjs +167 -0
- package/dist/entry-locks-BTyQLdbC.mjs.map +1 -0
- package/dist/error-BfsNNrIE.mjs +90 -0
- package/dist/error-BfsNNrIE.mjs.map +1 -0
- package/dist/errors-BN1QAnEY.mjs +460 -0
- package/dist/errors-BN1QAnEY.mjs.map +1 -0
- package/dist/errors-Bt3goF8I.mjs +120 -0
- package/dist/errors-Bt3goF8I.mjs.map +1 -0
- package/dist/escape-BveidMa2.mjs +9 -0
- package/dist/escape-BveidMa2.mjs.map +1 -0
- package/dist/fence-Bb_IbCxI.mjs +162 -0
- package/dist/fence-Bb_IbCxI.mjs.map +1 -0
- package/dist/field-defs-cache-CsZctvbu.mjs +77 -0
- package/dist/field-defs-cache-CsZctvbu.mjs.map +1 -0
- package/dist/field-map-6m9rF49t.mjs +83 -0
- package/dist/field-map-6m9rF49t.mjs.map +1 -0
- package/dist/focal-point-CZ3d1wK5.mjs +23 -0
- package/dist/focal-point-CZ3d1wK5.mjs.map +1 -0
- package/dist/fts-manager-CCaZTp8y.mjs +443 -0
- package/dist/fts-manager-CCaZTp8y.mjs.map +1 -0
- package/dist/gc-xowRjczI.mjs +75 -0
- package/dist/gc-xowRjczI.mjs.map +1 -0
- package/dist/hash-COVK27Pc.mjs +37 -0
- package/dist/hash-COVK27Pc.mjs.map +1 -0
- package/dist/hook-result-CYSk0XFe.d.mts +31 -0
- package/dist/hook-result-CYSk0XFe.d.mts.map +1 -0
- package/dist/hreflang-CChPV7jx.mjs +105 -0
- package/dist/hreflang-CChPV7jx.mjs.map +1 -0
- package/dist/hreflang-DRslq3QI.d.mts +50 -0
- package/dist/hreflang-DRslq3QI.d.mts.map +1 -0
- package/dist/http-route-dispatch-CAdp2onn.mjs +339 -0
- package/dist/http-route-dispatch-CAdp2onn.mjs.map +1 -0
- package/dist/identity-NVTOOQyq.mjs +35 -0
- package/dist/identity-NVTOOQyq.mjs.map +1 -0
- package/dist/import-wpSO2N0x.mjs +2020 -0
- package/dist/import-wpSO2N0x.mjs.map +1 -0
- package/dist/index-CDs2wq4X.d.mts +623 -0
- package/dist/index-CDs2wq4X.d.mts.map +1 -0
- package/dist/index.d.mts +24 -24
- package/dist/index.mjs +103 -101
- package/dist/install-finalization-D9R9R-W8.mjs +58 -0
- package/dist/install-finalization-D9R9R-W8.mjs.map +1 -0
- package/dist/keys-Bck6LBfF.mjs +61 -0
- package/dist/keys-Bck6LBfF.mjs.map +1 -0
- package/dist/kinds-CYmRESWj.mjs +974 -0
- package/dist/kinds-CYmRESWj.mjs.map +1 -0
- package/dist/lifecycle-C815ZQfV.mjs +25 -0
- package/dist/lifecycle-C815ZQfV.mjs.map +1 -0
- package/dist/load-D99axixL.mjs +28 -0
- package/dist/load-D99axixL.mjs.map +1 -0
- package/dist/loader-CrRPfkHS.mjs +1197 -0
- package/dist/loader-CrRPfkHS.mjs.map +1 -0
- package/dist/loops-DMKlygqX.mjs +203 -0
- package/dist/loops-DMKlygqX.mjs.map +1 -0
- package/dist/manifest-builder-BQzCHNgB.mjs +189 -0
- package/dist/manifest-builder-BQzCHNgB.mjs.map +1 -0
- package/dist/manifest-schema-Ci2gmd0u.mjs +492 -0
- package/dist/manifest-schema-Ci2gmd0u.mjs.map +1 -0
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +4 -4
- package/dist/media/local-runtime.d.mts +14 -14
- package/dist/media/local-runtime.mjs +6 -6
- package/dist/media-BUQePMxP.mjs +373 -0
- package/dist/media-BUQePMxP.mjs.map +1 -0
- package/dist/media-DH8ibNlY.mjs +294 -0
- package/dist/media-DH8ibNlY.mjs.map +1 -0
- package/dist/media-OEIQ3bVc.mjs +408 -0
- package/dist/media-OEIQ3bVc.mjs.map +1 -0
- package/dist/media-allowlist-pYtaBA95.mjs +44 -0
- package/dist/media-allowlist-pYtaBA95.mjs.map +1 -0
- package/dist/media-folders-Bf8_s4de.mjs +210 -0
- package/dist/media-folders-Bf8_s4de.mjs.map +1 -0
- package/dist/media-url-C6klEuNd.mjs +43 -0
- package/dist/media-url-C6klEuNd.mjs.map +1 -0
- package/dist/media-usage-D7Y64m6v.mjs +1629 -0
- package/dist/media-usage-D7Y64m6v.mjs.map +1 -0
- package/dist/media-usage-activation-DrqbmgLA.mjs +78 -0
- package/dist/media-usage-activation-DrqbmgLA.mjs.map +1 -0
- package/dist/media-usage-work-DommYhYA.mjs +152 -0
- package/dist/media-usage-work-DommYhYA.mjs.map +1 -0
- package/dist/media-usage-work-HU63hNDu.mjs +586 -0
- package/dist/media-usage-work-HU63hNDu.mjs.map +1 -0
- package/dist/menus-BQfusWEC.mjs +3608 -0
- package/dist/menus-BQfusWEC.mjs.map +1 -0
- package/dist/menus-BtwjQt5s.mjs +753 -0
- package/dist/menus-BtwjQt5s.mjs.map +1 -0
- package/dist/menus-p_M1V7J_.mjs +139 -0
- package/dist/menus-p_M1V7J_.mjs.map +1 -0
- package/dist/migrations/index.d.mts +1 -1
- package/dist/migrations/index.mjs +3 -3
- package/dist/mime-C94qBG-P.mjs +36 -0
- package/dist/mime-C94qBG-P.mjs.map +1 -0
- package/dist/mode-DpYW3w6U.mjs +23 -0
- package/dist/mode-DpYW3w6U.mjs.map +1 -0
- package/dist/node-sqlite-compat-pWFw0lcc.mjs +46 -0
- package/dist/node-sqlite-compat-pWFw0lcc.mjs.map +1 -0
- package/dist/normalize-DduyxBjf.mjs +191 -0
- package/dist/normalize-DduyxBjf.mjs.map +1 -0
- package/dist/oauth-authorization-DAXsH1H9.mjs +274 -0
- package/dist/oauth-authorization-DAXsH1H9.mjs.map +1 -0
- package/dist/oauth-clients-DENiDQri.mjs +266 -0
- package/dist/oauth-clients-DENiDQri.mjs.map +1 -0
- package/dist/oauth-state-store-y8mEYVq9.mjs +50 -0
- package/dist/oauth-state-store-y8mEYVq9.mjs.map +1 -0
- package/dist/oauth-user-lookup-CtTYAy-O.mjs +26 -0
- package/dist/oauth-user-lookup-CtTYAy-O.mjs.map +1 -0
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/options-BYa1FgIM.d.mts +81 -0
- package/dist/options-BYa1FgIM.d.mts.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/page/index.mjs +5 -5
- package/dist/parse-CORGGhHf.mjs +81 -0
- package/dist/parse-CORGGhHf.mjs.map +1 -0
- package/dist/parse-avNM1xWV.d.mts +168 -0
- package/dist/parse-avNM1xWV.d.mts.map +1 -0
- package/dist/passkey-config-Dih5YeIS.mjs +46 -0
- package/dist/passkey-config-Dih5YeIS.mjs.map +1 -0
- package/dist/paths-d51RFm8M.mjs +77 -0
- package/dist/paths-d51RFm8M.mjs.map +1 -0
- package/dist/patterns-CoqlXUvk.mjs +145 -0
- package/dist/patterns-CoqlXUvk.mjs.map +1 -0
- package/dist/placeholder-D2aksPMF.d.mts +306 -0
- package/dist/placeholder-D2aksPMF.d.mts.map +1 -0
- package/dist/plan-DqeZKuaR.mjs +135 -0
- package/dist/plan-DqeZKuaR.mjs.map +1 -0
- package/dist/plugin-test-runtime.d.mts +14 -14
- package/dist/plugin-test-runtime.mjs +110 -107
- package/dist/plugin-types.d.mts +2 -2
- package/dist/plugin-utils.d.mts +12 -12
- package/dist/plugins/adapt-sandbox-entry.d.mts +12 -12
- package/dist/plugins/adapt-sandbox-entry.mjs +3 -3
- package/dist/plugins/host.d.mts +5 -5
- package/dist/plugins/host.mjs +42 -41
- package/dist/plugins-Bb7oqjQA.mjs +277 -0
- package/dist/plugins-Bb7oqjQA.mjs.map +1 -0
- package/dist/policy-BcEqut3n.mjs +45 -0
- package/dist/policy-BcEqut3n.mjs.map +1 -0
- package/dist/portable-text-DXE8ymtD.mjs +307 -0
- package/dist/portable-text-DXE8ymtD.mjs.map +1 -0
- package/dist/preview-DEDWbwA_.mjs +107 -0
- package/dist/preview-DEDWbwA_.mjs.map +1 -0
- package/dist/protocol-BJbTzvwl.d.mts +61 -0
- package/dist/protocol-BJbTzvwl.d.mts.map +1 -0
- package/dist/public-submission-D9c9S14m.mjs +250 -0
- package/dist/public-submission-D9c9S14m.mjs.map +1 -0
- package/dist/public-url-CIRe-2T6.mjs +101 -0
- package/dist/public-url-CIRe-2T6.mjs.map +1 -0
- package/dist/query-BzifXlPS.mjs +1156 -0
- package/dist/query-BzifXlPS.mjs.map +1 -0
- package/dist/query-OzMJ6vJP.mjs +41 -0
- package/dist/query-OzMJ6vJP.mjs.map +1 -0
- package/dist/rate-limit-Csp60Vik.mjs +120 -0
- package/dist/rate-limit-Csp60Vik.mjs.map +1 -0
- package/dist/receipt-B-aU4hpJ.mjs +43 -0
- package/dist/receipt-B-aU4hpJ.mjs.map +1 -0
- package/dist/redirect-CDSw1sU3.mjs +17 -0
- package/dist/redirect-CDSw1sU3.mjs.map +1 -0
- package/dist/redirect-D1ArbO85.mjs +411 -0
- package/dist/redirect-D1ArbO85.mjs.map +1 -0
- package/dist/redirects-BjTwDTsV.mjs +148 -0
- package/dist/redirects-BjTwDTsV.mjs.map +1 -0
- package/dist/redirects-Ck50kH3n.mjs +446 -0
- package/dist/redirects-Ck50kH3n.mjs.map +1 -0
- package/dist/registry/testing.mjs +3 -3
- package/dist/registry-CFcNCayZ.mjs +6754 -0
- package/dist/registry-CFcNCayZ.mjs.map +1 -0
- package/dist/relations-Cfaa1fDl.mjs +776 -0
- package/dist/relations-Cfaa1fDl.mjs.map +1 -0
- package/dist/request-cache-iHRlHx1d.mjs +104 -0
- package/dist/request-cache-iHRlHx1d.mjs.map +1 -0
- package/dist/request-context.d.mts +1 -1
- package/dist/request-meta-D47mqyev.mjs +141 -0
- package/dist/request-meta-D47mqyev.mjs.map +1 -0
- package/dist/resolve-BPE8sMvx.mjs +99 -0
- package/dist/resolve-BPE8sMvx.mjs.map +1 -0
- package/dist/resolve-C0s1E_fH.mjs +199 -0
- package/dist/resolve-C0s1E_fH.mjs.map +1 -0
- package/dist/revision-j1p4-bOj.mjs +302 -0
- package/dist/revision-j1p4-bOj.mjs.map +1 -0
- package/dist/route-wire-C9PJV9aL.mjs +197 -0
- package/dist/route-wire-C9PJV9aL.mjs.map +1 -0
- package/dist/routes-lyYw0mMf.mjs +272 -0
- package/dist/routes-lyYw0mMf.mjs.map +1 -0
- package/dist/runner-B3eyB9Gb.mjs +6121 -0
- package/dist/runner-B3eyB9Gb.mjs.map +1 -0
- package/dist/runner-DoYH2Jcu.d.mts +68 -0
- package/dist/runner-DoYH2Jcu.d.mts.map +1 -0
- package/dist/runtime.d.mts +13 -13
- package/dist/runtime.mjs +3 -3
- package/dist/schema-BAayK46S.mjs +13 -0
- package/dist/schema-BAayK46S.mjs.map +1 -0
- package/dist/schema-Gqcl-8G4.mjs +815 -0
- package/dist/schema-Gqcl-8G4.mjs.map +1 -0
- package/dist/scopes-BqPOKoKf.mjs +34 -0
- package/dist/scopes-BqPOKoKf.mjs.map +1 -0
- package/dist/search-YJKPIEV6.mjs +448 -0
- package/dist/search-YJKPIEV6.mjs.map +1 -0
- package/dist/sections-BR_e-wK5.mjs +346 -0
- package/dist/sections-BR_e-wK5.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +37 -31
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +22 -21
- package/dist/seo/index.mjs.map +1 -1
- package/dist/seo-Dsj8hy3j.mjs +114 -0
- package/dist/seo-Dsj8hy3j.mjs.map +1 -0
- package/dist/seo-XvH4LNNp.mjs +133 -0
- package/dist/seo-XvH4LNNp.mjs.map +1 -0
- package/dist/service-BsVG05gS.mjs +150 -0
- package/dist/service-BsVG05gS.mjs.map +1 -0
- package/dist/session-user-DgKbYrlj.mjs +51 -0
- package/dist/session-user-DgKbYrlj.mjs.map +1 -0
- package/dist/settings-CNPUntkh.mjs +51 -0
- package/dist/settings-CNPUntkh.mjs.map +1 -0
- package/dist/settings-D5EHi0Tk.mjs +439 -0
- package/dist/settings-D5EHi0Tk.mjs.map +1 -0
- package/dist/settings-DSfE7wOa.mjs +61 -0
- package/dist/settings-DSfE7wOa.mjs.map +1 -0
- package/dist/setup-complete-B8W8sJ9v.mjs +26 -0
- package/dist/setup-complete-B8W8sJ9v.mjs.map +1 -0
- package/dist/setup-nonce-DnSBP260.mjs +25 -0
- package/dist/setup-nonce-DnSBP260.mjs.map +1 -0
- package/dist/site-settings-NGzdrt7O.mjs +50 -0
- package/dist/site-settings-NGzdrt7O.mjs.map +1 -0
- package/dist/site-url-D6JB7tLW.mjs +16 -0
- package/dist/site-url-D6JB7tLW.mjs.map +1 -0
- package/dist/snapshot-CdcwwWAf.mjs +414 -0
- package/dist/snapshot-CdcwwWAf.mjs.map +1 -0
- package/dist/ssrf-Dv0_tRpo.mjs +406 -0
- package/dist/ssrf-Dv0_tRpo.mjs.map +1 -0
- package/dist/stage-CUjhED3P.mjs +662 -0
- package/dist/stage-CUjhED3P.mjs.map +1 -0
- package/dist/staged-C36IWXff.mjs +659 -0
- package/dist/staged-C36IWXff.mjs.map +1 -0
- package/dist/state-CKouMCSY.mjs +153 -0
- package/dist/state-CKouMCSY.mjs.map +1 -0
- package/dist/status-lzyj4Uu7.mjs +30 -0
- package/dist/status-lzyj4Uu7.mjs.map +1 -0
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/storage-query-B-cWcTjJ.d.mts +1594 -0
- package/dist/storage-query-B-cWcTjJ.d.mts.map +1 -0
- package/dist/tar-DsTXX6ch.mjs +449 -0
- package/dist/tar-DsTXX6ch.mjs.map +1 -0
- package/dist/target-DLQCwp_Z.mjs +27 -0
- package/dist/target-DLQCwp_Z.mjs.map +1 -0
- package/dist/taxonomies-BlAjr4BL.mjs +697 -0
- package/dist/taxonomies-BlAjr4BL.mjs.map +1 -0
- package/dist/taxonomies-Byyo7LOZ.mjs +939 -0
- package/dist/taxonomies-Byyo7LOZ.mjs.map +1 -0
- package/dist/taxonomy-CKDwM61Y.mjs +525 -0
- package/dist/taxonomy-CKDwM61Y.mjs.map +1 -0
- package/dist/tokens-BojXlk49.mjs +171 -0
- package/dist/tokens-BojXlk49.mjs.map +1 -0
- package/dist/transaction-DLPQWik1.mjs +29 -0
- package/dist/transaction-DLPQWik1.mjs.map +1 -0
- package/dist/transfer-XSTh2BCr.mjs +8706 -0
- package/dist/transfer-XSTh2BCr.mjs.map +1 -0
- package/dist/transfer-download-DkoCDA9d.mjs +33 -0
- package/dist/transfer-download-DkoCDA9d.mjs.map +1 -0
- package/dist/transfer-iQ26J-RT.mjs +166 -0
- package/dist/transfer-iQ26J-RT.mjs.map +1 -0
- package/dist/transformations-C7RQabKQ.mjs +196 -0
- package/dist/transformations-C7RQabKQ.mjs.map +1 -0
- package/dist/transport-D3RulfD6.mjs +136 -0
- package/dist/transport-D3RulfD6.mjs.map +1 -0
- package/dist/transport-DhlZlf4D.d.mts +42 -0
- package/dist/transport-DhlZlf4D.d.mts.map +1 -0
- package/dist/trusted-proxy-B6ECOZJc.mjs +51 -0
- package/dist/trusted-proxy-B6ECOZJc.mjs.map +1 -0
- package/dist/turnstile-lqUiY7lP.mjs +53 -0
- package/dist/turnstile-lqUiY7lP.mjs.map +1 -0
- package/dist/types-4Dn9bZhu.mjs +77 -0
- package/dist/types-4Dn9bZhu.mjs.map +1 -0
- package/dist/types-5LSo7PPx.d.mts +85 -0
- package/dist/types-5LSo7PPx.d.mts.map +1 -0
- package/dist/types-BQ3hwYJT.d.mts +896 -0
- package/dist/types-BQ3hwYJT.d.mts.map +1 -0
- package/dist/types-BYEk7qo3.d.mts +198 -0
- package/dist/types-BYEk7qo3.d.mts.map +1 -0
- package/dist/types-CJKsWVFR.mjs +199 -0
- package/dist/types-CJKsWVFR.mjs.map +1 -0
- package/dist/types-Ca_w8mmY.d.mts +363 -0
- package/dist/types-Ca_w8mmY.d.mts.map +1 -0
- package/dist/types-CkE41ZAG.mjs +18 -0
- package/dist/types-CkE41ZAG.mjs.map +1 -0
- package/dist/types-D0aJW2uR.d.mts +1641 -0
- package/dist/types-D0aJW2uR.d.mts.map +1 -0
- package/dist/types-DbpN4Qto.d.mts +372 -0
- package/dist/types-DbpN4Qto.d.mts.map +1 -0
- package/dist/types-ILDI1sdY.mjs +16 -0
- package/dist/types-ILDI1sdY.mjs.map +1 -0
- package/dist/types-OC5c-edM.mjs +22 -0
- package/dist/types-OC5c-edM.mjs.map +1 -0
- package/dist/types-aOOxRSx6.mjs +103 -0
- package/dist/types-aOOxRSx6.mjs.map +1 -0
- package/dist/types-m1WbUffD.d.mts +198 -0
- package/dist/types-m1WbUffD.d.mts.map +1 -0
- package/dist/types-uxLoPBio.d.mts +124 -0
- package/dist/types-uxLoPBio.d.mts.map +1 -0
- package/dist/types-we5uR-tV.d.mts +287 -0
- package/dist/types-we5uR-tV.d.mts.map +1 -0
- package/dist/upload-attempts-DduMy6B0.mjs +32 -0
- package/dist/upload-attempts-DduMy6B0.mjs.map +1 -0
- package/dist/url-pattern-B3EwxAAk.mjs +33 -0
- package/dist/url-pattern-B3EwxAAk.mjs.map +1 -0
- package/dist/user-ElwQjTQT.mjs +171 -0
- package/dist/user-ElwQjTQT.mjs.map +1 -0
- package/dist/utils-BlAXmfOG.mjs +439 -0
- package/dist/utils-BlAXmfOG.mjs.map +1 -0
- package/dist/validate-CH0X5b2c.d.mts +468 -0
- package/dist/validate-CH0X5b2c.d.mts.map +1 -0
- package/dist/validate-CfgRpcjA.mjs +492 -0
- package/dist/validate-CfgRpcjA.mjs.map +1 -0
- package/dist/validation-DvYJtv4k.mjs +218 -0
- package/dist/validation-DvYJtv4k.mjs.map +1 -0
- package/dist/version-DgYjhTud.mjs +9 -0
- package/dist/version-DgYjhTud.mjs.map +1 -0
- package/dist/version-Dq71scUb.mjs +7 -0
- package/dist/version-Dq71scUb.mjs.map +1 -0
- package/dist/widgets-ChFIj0be.mjs +126 -0
- package/dist/widgets-ChFIj0be.mjs.map +1 -0
- package/dist/wxr-taxonomies-CBZMp2YX.mjs +372 -0
- package/dist/wxr-taxonomies-CBZMp2YX.mjs.map +1 -0
- package/dist/wxr-taxonomies-DlKDpWN1.d.mts +34 -0
- package/dist/wxr-taxonomies-DlKDpWN1.d.mts.map +1 -0
- package/dist/zod-generator-DmTKI-Ig.mjs +426 -0
- package/dist/zod-generator-DmTKI-Ig.mjs.map +1 -0
- package/package.json +7 -6
- package/src/api/handlers/content.ts +593 -16
- package/src/api/handlers/device-flow.ts +74 -21
- package/src/api/handlers/index.ts +1 -0
- package/src/api/handlers/manifest.ts +79 -5
- package/src/api/handlers/media-upload.ts +17 -71
- package/src/api/handlers/media.ts +97 -3
- package/src/api/handlers/relations.ts +638 -157
- package/src/api/handlers/revision.ts +30 -0
- package/src/api/handlers/schema.ts +403 -1
- package/src/api/handlers/snapshot.ts +257 -3
- package/src/api/handlers/staged-references.ts +142 -0
- package/src/api/handlers/validate-references.ts +146 -0
- package/src/api/schemas/content.ts +13 -0
- package/src/api/schemas/relations.ts +54 -44
- package/src/api/schemas/schema.ts +14 -0
- package/src/astro/integration/routes.ts +15 -19
- package/src/astro/integration/vite-config.ts +10 -4
- package/src/astro/middleware.ts +1 -0
- package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/children.ts +11 -54
- package/src/astro/routes/api/content/[collection]/[id].ts +3 -1
- package/src/astro/routes/api/oauth/device/authorize.ts +33 -4
- package/src/astro/routes/api/relations/index.ts +4 -4
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +6 -2
- package/src/astro/routes/api/setup/index.ts +66 -23
- package/src/astro/routes/api/snapshot.ts +2 -1
- package/src/astro/types.ts +10 -0
- package/src/cleanup.ts +10 -0
- package/src/cli/commands/export-seed.ts +510 -89
- package/src/components/Columns.astro +3 -1
- package/src/components/Cover.astro +3 -1
- package/src/components/WidgetArea.astro +3 -2
- package/src/database/migrations/086_relations_structural.ts +197 -0
- package/src/database/migrations/087_reference_field_relations.ts +407 -0
- package/src/database/migrations/runner.ts +4 -0
- package/src/database/reference-backfill.ts +141 -0
- package/src/database/repositories/content.ts +43 -8
- package/src/database/repositories/media.ts +40 -1
- package/src/database/repositories/redirect.ts +17 -34
- package/src/database/repositories/relation.ts +620 -208
- package/src/database/repositories/revision.ts +16 -0
- package/src/database/repositories/types.ts +38 -0
- package/src/database/transaction.ts +12 -1
- package/src/database/types.ts +14 -5
- package/src/emdash-runtime.ts +114 -11
- package/src/index.ts +11 -0
- package/src/loader.ts +169 -22
- package/src/mcp/server.ts +13 -48
- package/src/query.ts +455 -25
- package/src/references/field-map.ts +115 -0
- package/src/references/resolve.ts +160 -0
- package/src/references/staged.ts +117 -0
- package/src/references/types.ts +16 -0
- package/src/registry/artifact-fetch.ts +45 -271
- package/src/schema/registry.ts +99 -24
- package/src/schema/types.ts +58 -0
- package/src/schema/zod-generator.ts +67 -1
- package/src/seed/apply.ts +841 -103
- package/src/seed/types.ts +36 -2
- package/src/seed/validate.ts +78 -2
- package/src/transfer/analyze/unique-keys.ts +3 -12
- package/src/transfer/analyze/validate.ts +65 -39
- package/src/transfer/analyze/values.ts +12 -1
- package/src/transfer/export/readers.ts +12 -8
- package/src/transfer/format/columns.ts +26 -6
- package/src/transfer/format/kinds.ts +21 -5
- package/src/transfer/import/context.ts +8 -1
- package/src/transfer/ops/package-index.ts +1 -1
- package/dist/action-token-DYwe4Pk0.mjs +0 -26
- package/dist/action-token-DYwe4Pk0.mjs.map +0 -1
- package/dist/activation-LOkDM5WT.mjs +0 -382
- package/dist/activation-LOkDM5WT.mjs.map +0 -1
- package/dist/adapters-B64LbYcB.d.mts +0 -171
- package/dist/adapters-B64LbYcB.d.mts.map +0 -1
- package/dist/allowed-origins-CHB8oZNM.mjs +0 -116
- package/dist/allowed-origins-CHB8oZNM.mjs.map +0 -1
- package/dist/api-cFFZpyS0.mjs +0 -4718
- package/dist/api-cFFZpyS0.mjs.map +0 -1
- package/dist/api-tokens-s4i4aUp_.mjs +0 -166
- package/dist/api-tokens-s4i4aUp_.mjs.map +0 -1
- package/dist/apply-CoT603H3.mjs +0 -1163
- package/dist/apply-CoT603H3.mjs.map +0 -1
- package/dist/approvals-CqMfNxak.mjs +0 -460
- package/dist/approvals-CqMfNxak.mjs.map +0 -1
- package/dist/artifact-fetch-DZMOGZ3-.mjs +0 -818
- package/dist/artifact-fetch-DZMOGZ3-.mjs.map +0 -1
- package/dist/astro/routes/api/relations/_id_/translations.d.mts +0 -8
- package/dist/astro/routes/api/relations/_id_/translations.d.mts.map +0 -1
- package/dist/astro/routes/api/relations/_id_/translations.mjs +0 -47
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +0 -1
- package/dist/audit-RfCugiSB.mjs +0 -127
- package/dist/audit-RfCugiSB.mjs.map +0 -1
- package/dist/authoritative-records-CoJvBXAD.mjs +0 -5172
- package/dist/authoritative-records-CoJvBXAD.mjs.map +0 -1
- package/dist/authorize-seCifOMJ.mjs +0 -49
- package/dist/authorize-seCifOMJ.mjs.map +0 -1
- package/dist/backup-CBu0LrXf.mjs +0 -246
- package/dist/backup-CBu0LrXf.mjs.map +0 -1
- package/dist/block-type-registry-DUI_3KxT.mjs +0 -6709
- package/dist/block-type-registry-DUI_3KxT.mjs.map +0 -1
- package/dist/block-types-BmZmLmAD.mjs +0 -132
- package/dist/block-types-BmZmLmAD.mjs.map +0 -1
- package/dist/block-values-C_VIPr5t.mjs +0 -175
- package/dist/block-values-C_VIPr5t.mjs.map +0 -1
- package/dist/byline-DYmkXqvo.mjs +0 -909
- package/dist/byline-DYmkXqvo.mjs.map +0 -1
- package/dist/byline-fields-D28AT-Fh.mjs +0 -123
- package/dist/byline-fields-D28AT-Fh.mjs.map +0 -1
- package/dist/byline-registry-gnKscbQX.mjs +0 -406
- package/dist/byline-registry-gnKscbQX.mjs.map +0 -1
- package/dist/bylines-BPwJqoYH.mjs +0 -232
- package/dist/bylines-BPwJqoYH.mjs.map +0 -1
- package/dist/bylines-ukLaYpVZ.mjs +0 -214
- package/dist/bylines-ukLaYpVZ.mjs.map +0 -1
- package/dist/cache-m7UEkRFV.mjs +0 -108
- package/dist/cache-m7UEkRFV.mjs.map +0 -1
- package/dist/canonical-YhEQezjg.mjs +0 -174
- package/dist/canonical-YhEQezjg.mjs.map +0 -1
- package/dist/capture-triggers-kJyl_yyM.mjs +0 -362
- package/dist/capture-triggers-kJyl_yyM.mjs.map +0 -1
- package/dist/challenge-store-ppU6Uv13.mjs +0 -51
- package/dist/challenge-store-ppU6Uv13.mjs.map +0 -1
- package/dist/checksum-B2IINIlN.mjs +0 -162
- package/dist/checksum-B2IINIlN.mjs.map +0 -1
- package/dist/chrome-tags-DApCQSIk.mjs +0 -24
- package/dist/chrome-tags-DApCQSIk.mjs.map +0 -1
- package/dist/chunks-Dms7Uxbt.mjs +0 -25
- package/dist/chunks-Dms7Uxbt.mjs.map +0 -1
- package/dist/collection-deletion-BIm28mo3.mjs +0 -436
- package/dist/collection-deletion-BIm28mo3.mjs.map +0 -1
- package/dist/collection-deletion-processor-Br6rxs3u.mjs +0 -217
- package/dist/collection-deletion-processor-Br6rxs3u.mjs.map +0 -1
- package/dist/collection-slugs-cache-CjGk__zp.mjs +0 -85
- package/dist/collection-slugs-cache-CjGk__zp.mjs.map +0 -1
- package/dist/comment-CrBl22OF.mjs +0 -313
- package/dist/comment-CrBl22OF.mjs.map +0 -1
- package/dist/comment-reaction-JEQ2Fpd8.mjs +0 -85
- package/dist/comment-reaction-JEQ2Fpd8.mjs.map +0 -1
- package/dist/comments-2ah9du9y.mjs +0 -195
- package/dist/comments-2ah9du9y.mjs.map +0 -1
- package/dist/comments-fHZ8ON9O.mjs +0 -99
- package/dist/comments-fHZ8ON9O.mjs.map +0 -1
- package/dist/components-CXbq7CSM.mjs +0 -108
- package/dist/components-CXbq7CSM.mjs.map +0 -1
- package/dist/config-5ii2haYu.d.mts +0 -6528
- package/dist/config-5ii2haYu.d.mts.map +0 -1
- package/dist/config-IhybvkIQ.mjs +0 -242
- package/dist/config-IhybvkIQ.mjs.map +0 -1
- package/dist/config-Py43JK9r.d.mts +0 -39
- package/dist/config-Py43JK9r.d.mts.map +0 -1
- package/dist/content-DFaIEDj9.mjs +0 -2281
- package/dist/content-DFaIEDj9.mjs.map +0 -1
- package/dist/content-list-query-Dra4Dalc.d.mts +0 -26
- package/dist/content-list-query-Dra4Dalc.d.mts.map +0 -1
- package/dist/content-policy-DFNw91-d.mjs +0 -46
- package/dist/content-policy-DFNw91-d.mjs.map +0 -1
- package/dist/content-repair-Q8IUrpLL.mjs +0 -442
- package/dist/content-repair-Q8IUrpLL.mjs.map +0 -1
- package/dist/context-BeGccdUX.mjs +0 -1921
- package/dist/context-BeGccdUX.mjs.map +0 -1
- package/dist/cron-7DCJwPXq.mjs +0 -284
- package/dist/cron-7DCJwPXq.mjs.map +0 -1
- package/dist/dashboard-DjrB-xxZ.mjs +0 -151
- package/dist/dashboard-DjrB-xxZ.mjs.map +0 -1
- package/dist/default-DQ6vsviu.mjs +0 -81
- package/dist/default-DQ6vsviu.mjs.map +0 -1
- package/dist/deferred-tasks-BPxXUB4x.d.mts +0 -9
- package/dist/deferred-tasks-BPxXUB4x.d.mts.map +0 -1
- package/dist/dev-typegen-CZaPLl3V.mjs +0 -29
- package/dist/dev-typegen-CZaPLl3V.mjs.map +0 -1
- package/dist/device-flow-CRd6DXmW.mjs +0 -466
- package/dist/device-flow-CRd6DXmW.mjs.map +0 -1
- package/dist/digest-BarSh-pN.mjs +0 -194
- package/dist/digest-BarSh-pN.mjs.map +0 -1
- package/dist/direct-executor-BmuFJJZV.mjs +0 -104
- package/dist/direct-executor-BmuFJJZV.mjs.map +0 -1
- package/dist/dist-BNwANFJR.mjs +0 -810
- package/dist/dist-BNwANFJR.mjs.map +0 -1
- package/dist/email-console-bJn3ctqA.mjs +0 -50
- package/dist/email-console-bJn3ctqA.mjs.map +0 -1
- package/dist/email-locale-CN6WAMx0.mjs +0 -25
- package/dist/email-locale-CN6WAMx0.mjs.map +0 -1
- package/dist/emdash-runtime-DAjghyEG.mjs +0 -4186
- package/dist/emdash-runtime-DAjghyEG.mjs.map +0 -1
- package/dist/enrich-Dl3kZ-lP.mjs +0 -194
- package/dist/enrich-Dl3kZ-lP.mjs.map +0 -1
- package/dist/entry-lock-BJYdBfXb.mjs +0 -1097
- package/dist/entry-lock-BJYdBfXb.mjs.map +0 -1
- package/dist/entry-lock-DHQOxN7R.mjs +0 -163
- package/dist/entry-lock-DHQOxN7R.mjs.map +0 -1
- package/dist/entry-lock-DVhCt49A.d.mts +0 -4368
- package/dist/entry-lock-DVhCt49A.d.mts.map +0 -1
- package/dist/entry-locks-D0foLdjQ.mjs +0 -167
- package/dist/entry-locks-D0foLdjQ.mjs.map +0 -1
- package/dist/error-CfvOHP0M.mjs +0 -90
- package/dist/error-CfvOHP0M.mjs.map +0 -1
- package/dist/errors-BF0_LFhW.mjs +0 -460
- package/dist/errors-BF0_LFhW.mjs.map +0 -1
- package/dist/errors-DgE1zJGG.mjs +0 -120
- package/dist/errors-DgE1zJGG.mjs.map +0 -1
- package/dist/escape-CuaZhZvL.mjs +0 -9
- package/dist/escape-CuaZhZvL.mjs.map +0 -1
- package/dist/fence-CMDXJYc_.mjs +0 -162
- package/dist/fence-CMDXJYc_.mjs.map +0 -1
- package/dist/field-defs-cache-HIgBbqZZ.mjs +0 -77
- package/dist/field-defs-cache-HIgBbqZZ.mjs.map +0 -1
- package/dist/focal-point-Gr34o-4o.mjs +0 -23
- package/dist/focal-point-Gr34o-4o.mjs.map +0 -1
- package/dist/fts-manager-S-K0laoS.mjs +0 -443
- package/dist/fts-manager-S-K0laoS.mjs.map +0 -1
- package/dist/gc-DjKY0Ay1.mjs +0 -75
- package/dist/gc-DjKY0Ay1.mjs.map +0 -1
- package/dist/hash-SDAW6zC-.mjs +0 -37
- package/dist/hash-SDAW6zC-.mjs.map +0 -1
- package/dist/hook-result-B5bXDgS2.d.mts +0 -31
- package/dist/hook-result-B5bXDgS2.d.mts.map +0 -1
- package/dist/hreflang-D5uNnsGJ.d.mts +0 -50
- package/dist/hreflang-D5uNnsGJ.d.mts.map +0 -1
- package/dist/hreflang-DMFBL2eb.mjs +0 -105
- package/dist/hreflang-DMFBL2eb.mjs.map +0 -1
- package/dist/http-route-dispatch-BitqzwMy.mjs +0 -339
- package/dist/http-route-dispatch-BitqzwMy.mjs.map +0 -1
- package/dist/identity-IyNWwXr6.mjs +0 -35
- package/dist/identity-IyNWwXr6.mjs.map +0 -1
- package/dist/import-C3JZplWq.mjs +0 -2020
- package/dist/import-C3JZplWq.mjs.map +0 -1
- package/dist/index-D-hdtLe8.d.mts +0 -623
- package/dist/index-D-hdtLe8.d.mts.map +0 -1
- package/dist/install-finalization-DfM1fj5x.mjs +0 -58
- package/dist/install-finalization-DfM1fj5x.mjs.map +0 -1
- package/dist/keys-yxLm5l35.mjs +0 -61
- package/dist/keys-yxLm5l35.mjs.map +0 -1
- package/dist/kinds-CRu_T5T5.mjs +0 -961
- package/dist/kinds-CRu_T5T5.mjs.map +0 -1
- package/dist/lifecycle-BzeGPeQ-.mjs +0 -25
- package/dist/lifecycle-BzeGPeQ-.mjs.map +0 -1
- package/dist/load-XgJiE6_3.mjs +0 -28
- package/dist/load-XgJiE6_3.mjs.map +0 -1
- package/dist/loader-BzqeR1ZN.mjs +0 -1086
- package/dist/loader-BzqeR1ZN.mjs.map +0 -1
- package/dist/loops-BQSzv5Zn.mjs +0 -203
- package/dist/loops-BQSzv5Zn.mjs.map +0 -1
- package/dist/manifest-builder-B8zR-73J.mjs +0 -189
- package/dist/manifest-builder-B8zR-73J.mjs.map +0 -1
- package/dist/manifest-schema-CY3zng_M.mjs +0 -492
- package/dist/manifest-schema-CY3zng_M.mjs.map +0 -1
- package/dist/media-Pu1L6xir.mjs +0 -408
- package/dist/media-Pu1L6xir.mjs.map +0 -1
- package/dist/media-Qp5S3eH3.mjs +0 -355
- package/dist/media-Qp5S3eH3.mjs.map +0 -1
- package/dist/media-allowlist-BSoa9Vvm.mjs +0 -44
- package/dist/media-allowlist-BSoa9Vvm.mjs.map +0 -1
- package/dist/media-folders-BE0oo2eX.mjs +0 -210
- package/dist/media-folders-BE0oo2eX.mjs.map +0 -1
- package/dist/media-h7tOiDLj.mjs +0 -221
- package/dist/media-h7tOiDLj.mjs.map +0 -1
- package/dist/media-url-jArBpzIG.mjs +0 -43
- package/dist/media-url-jArBpzIG.mjs.map +0 -1
- package/dist/media-usage-My-Vi-ex.mjs +0 -1629
- package/dist/media-usage-My-Vi-ex.mjs.map +0 -1
- package/dist/media-usage-activation-HKp7zk7W.mjs +0 -78
- package/dist/media-usage-activation-HKp7zk7W.mjs.map +0 -1
- package/dist/media-usage-work-C2OZLCAd.mjs +0 -586
- package/dist/media-usage-work-C2OZLCAd.mjs.map +0 -1
- package/dist/media-usage-work-DwUNbgvS.mjs +0 -152
- package/dist/media-usage-work-DwUNbgvS.mjs.map +0 -1
- package/dist/menus-BM7BPLOb.mjs +0 -139
- package/dist/menus-BM7BPLOb.mjs.map +0 -1
- package/dist/menus-ITeAbcIF.mjs +0 -753
- package/dist/menus-ITeAbcIF.mjs.map +0 -1
- package/dist/menus-TsSGmSR4.mjs +0 -3608
- package/dist/menus-TsSGmSR4.mjs.map +0 -1
- package/dist/mime-ChLqYwDk.mjs +0 -36
- package/dist/mime-ChLqYwDk.mjs.map +0 -1
- package/dist/mode-CCuKXUci.mjs +0 -23
- package/dist/mode-CCuKXUci.mjs.map +0 -1
- package/dist/node-sqlite-compat-CONMsl-x.mjs +0 -46
- package/dist/node-sqlite-compat-CONMsl-x.mjs.map +0 -1
- package/dist/normalize-CO2P8-iE.mjs +0 -191
- package/dist/normalize-CO2P8-iE.mjs.map +0 -1
- package/dist/oauth-authorization-DlMIL5_h.mjs +0 -274
- package/dist/oauth-authorization-DlMIL5_h.mjs.map +0 -1
- package/dist/oauth-clients-Djq9r_KP.mjs +0 -266
- package/dist/oauth-clients-Djq9r_KP.mjs.map +0 -1
- package/dist/oauth-state-store-BbjnZXNC.mjs +0 -50
- package/dist/oauth-state-store-BbjnZXNC.mjs.map +0 -1
- package/dist/oauth-user-lookup-vue9a64A.mjs +0 -26
- package/dist/oauth-user-lookup-vue9a64A.mjs.map +0 -1
- package/dist/options-SQCHQCPH.d.mts +0 -81
- package/dist/options-SQCHQCPH.d.mts.map +0 -1
- package/dist/parse-CVgO-Qai.d.mts +0 -168
- package/dist/parse-CVgO-Qai.d.mts.map +0 -1
- package/dist/parse-DY6OD1j_.mjs +0 -81
- package/dist/parse-DY6OD1j_.mjs.map +0 -1
- package/dist/passkey-config-X_H8128a.mjs +0 -46
- package/dist/passkey-config-X_H8128a.mjs.map +0 -1
- package/dist/paths-JxokKIbr.mjs +0 -77
- package/dist/paths-JxokKIbr.mjs.map +0 -1
- package/dist/patterns-BhAvqO0S.mjs +0 -145
- package/dist/patterns-BhAvqO0S.mjs.map +0 -1
- package/dist/placeholder-MSEd8gty.d.mts +0 -306
- package/dist/placeholder-MSEd8gty.d.mts.map +0 -1
- package/dist/plan-xjcWrAah.mjs +0 -135
- package/dist/plan-xjcWrAah.mjs.map +0 -1
- package/dist/plugins-DUXUt8B9.mjs +0 -277
- package/dist/plugins-DUXUt8B9.mjs.map +0 -1
- package/dist/policy-CUvwAARc.mjs +0 -45
- package/dist/policy-CUvwAARc.mjs.map +0 -1
- package/dist/portable-text-5O_ZGUjB.mjs +0 -307
- package/dist/portable-text-5O_ZGUjB.mjs.map +0 -1
- package/dist/preview-CIUcPYr6.mjs +0 -107
- package/dist/preview-CIUcPYr6.mjs.map +0 -1
- package/dist/protocol-B97DZaFr.d.mts +0 -61
- package/dist/protocol-B97DZaFr.d.mts.map +0 -1
- package/dist/public-submission-BFBUrqst.mjs +0 -250
- package/dist/public-submission-BFBUrqst.mjs.map +0 -1
- package/dist/public-url-Bn7U0D7-.mjs +0 -101
- package/dist/public-url-Bn7U0D7-.mjs.map +0 -1
- package/dist/query-B48peoRT.mjs +0 -890
- package/dist/query-B48peoRT.mjs.map +0 -1
- package/dist/query-C4IH0uhR.mjs +0 -41
- package/dist/query-C4IH0uhR.mjs.map +0 -1
- package/dist/rate-limit-BZ6nybgc.mjs +0 -120
- package/dist/rate-limit-BZ6nybgc.mjs.map +0 -1
- package/dist/receipt-B4wJj5Jq.mjs +0 -43
- package/dist/receipt-B4wJj5Jq.mjs.map +0 -1
- package/dist/redirect-Cd1E7VUt.mjs +0 -17
- package/dist/redirect-Cd1E7VUt.mjs.map +0 -1
- package/dist/redirect-iJf5fltD.mjs +0 -415
- package/dist/redirect-iJf5fltD.mjs.map +0 -1
- package/dist/redirects-CYu1u1aK.mjs +0 -446
- package/dist/redirects-CYu1u1aK.mjs.map +0 -1
- package/dist/redirects-Cv9apQKj.mjs +0 -148
- package/dist/redirects-Cv9apQKj.mjs.map +0 -1
- package/dist/relation-M4NUCb0b.mjs +0 -334
- package/dist/relation-M4NUCb0b.mjs.map +0 -1
- package/dist/relations-CCif47Oq.mjs +0 -403
- package/dist/relations-CCif47Oq.mjs.map +0 -1
- package/dist/relations-CK9CYUyr.mjs +0 -310
- package/dist/relations-CK9CYUyr.mjs.map +0 -1
- package/dist/request-cache-DzLYKm_x.mjs +0 -104
- package/dist/request-cache-DzLYKm_x.mjs.map +0 -1
- package/dist/request-meta-sZXt3sRo.mjs +0 -141
- package/dist/request-meta-sZXt3sRo.mjs.map +0 -1
- package/dist/resolve-2WbQsCvT.mjs +0 -199
- package/dist/resolve-2WbQsCvT.mjs.map +0 -1
- package/dist/route-wire--1hLuACz.mjs +0 -197
- package/dist/route-wire--1hLuACz.mjs.map +0 -1
- package/dist/routes-CUsM40Cc.mjs +0 -272
- package/dist/routes-CUsM40Cc.mjs.map +0 -1
- package/dist/runner-1y4J9fJB.mjs +0 -5742
- package/dist/runner-1y4J9fJB.mjs.map +0 -1
- package/dist/runner-D5_LzKXI.d.mts +0 -68
- package/dist/runner-D5_LzKXI.d.mts.map +0 -1
- package/dist/schema-BABiRdsH.mjs +0 -13
- package/dist/schema-BABiRdsH.mjs.map +0 -1
- package/dist/schema-Du3MzCG0.mjs +0 -446
- package/dist/schema-Du3MzCG0.mjs.map +0 -1
- package/dist/scopes-Dbk-ElhT.mjs +0 -34
- package/dist/scopes-Dbk-ElhT.mjs.map +0 -1
- package/dist/search-Bt1PL1jf.mjs +0 -448
- package/dist/search-Bt1PL1jf.mjs.map +0 -1
- package/dist/sections-Ddptx_o9.mjs +0 -346
- package/dist/sections-Ddptx_o9.mjs.map +0 -1
- package/dist/seo-CZT2oLtT.mjs +0 -114
- package/dist/seo-CZT2oLtT.mjs.map +0 -1
- package/dist/seo-D5w3-Azy.mjs +0 -133
- package/dist/seo-D5w3-Azy.mjs.map +0 -1
- package/dist/service-DuykXPyW.mjs +0 -150
- package/dist/service-DuykXPyW.mjs.map +0 -1
- package/dist/session-user-BhaVp8w0.mjs +0 -51
- package/dist/session-user-BhaVp8w0.mjs.map +0 -1
- package/dist/settings-2xNYr90o.mjs +0 -51
- package/dist/settings-2xNYr90o.mjs.map +0 -1
- package/dist/settings-CgjRWGW9.mjs +0 -439
- package/dist/settings-CgjRWGW9.mjs.map +0 -1
- package/dist/settings-DYidIB26.mjs +0 -61
- package/dist/settings-DYidIB26.mjs.map +0 -1
- package/dist/setup-complete-bOogcHWG.mjs +0 -26
- package/dist/setup-complete-bOogcHWG.mjs.map +0 -1
- package/dist/setup-nonce-0EGuKM4s.mjs +0 -25
- package/dist/setup-nonce-0EGuKM4s.mjs.map +0 -1
- package/dist/site-settings-DOgRUoFX.mjs +0 -50
- package/dist/site-settings-DOgRUoFX.mjs.map +0 -1
- package/dist/site-url-BU_bbxwj.mjs +0 -16
- package/dist/site-url-BU_bbxwj.mjs.map +0 -1
- package/dist/snapshot-CugwUqKQ.mjs +0 -237
- package/dist/snapshot-CugwUqKQ.mjs.map +0 -1
- package/dist/ssrf-CJUnJymJ.mjs +0 -406
- package/dist/ssrf-CJUnJymJ.mjs.map +0 -1
- package/dist/stage-DgrBz_du.mjs +0 -662
- package/dist/stage-DgrBz_du.mjs.map +0 -1
- package/dist/state-DDiPySSG.mjs +0 -153
- package/dist/state-DDiPySSG.mjs.map +0 -1
- package/dist/status-DZKIG4a9.mjs +0 -30
- package/dist/status-DZKIG4a9.mjs.map +0 -1
- package/dist/storage-query-Bdv7A62k.d.mts +0 -1576
- package/dist/storage-query-Bdv7A62k.d.mts.map +0 -1
- package/dist/tar-_1AgTYFu.mjs +0 -449
- package/dist/tar-_1AgTYFu.mjs.map +0 -1
- package/dist/target-_7pn2Gbg.mjs +0 -27
- package/dist/target-_7pn2Gbg.mjs.map +0 -1
- package/dist/taxonomies-C1IwNe8Q.mjs +0 -697
- package/dist/taxonomies-C1IwNe8Q.mjs.map +0 -1
- package/dist/taxonomies-C_XMoRcX.mjs +0 -939
- package/dist/taxonomies-C_XMoRcX.mjs.map +0 -1
- package/dist/taxonomy-C3Fu1YeH.mjs +0 -525
- package/dist/taxonomy-C3Fu1YeH.mjs.map +0 -1
- package/dist/tokens-u78jETkX.mjs +0 -171
- package/dist/tokens-u78jETkX.mjs.map +0 -1
- package/dist/transaction-BcJO4Tmc.mjs +0 -29
- package/dist/transaction-BcJO4Tmc.mjs.map +0 -1
- package/dist/transfer-BhUCrgDc.mjs +0 -8675
- package/dist/transfer-BhUCrgDc.mjs.map +0 -1
- package/dist/transfer-DIGvR9Af.mjs +0 -166
- package/dist/transfer-DIGvR9Af.mjs.map +0 -1
- package/dist/transfer-download-Pml7RMio.mjs +0 -33
- package/dist/transfer-download-Pml7RMio.mjs.map +0 -1
- package/dist/transformations-9gfpwY65.mjs +0 -196
- package/dist/transformations-9gfpwY65.mjs.map +0 -1
- package/dist/transport-BIfH7_hx.d.mts +0 -42
- package/dist/transport-BIfH7_hx.d.mts.map +0 -1
- package/dist/transport-DKWIq53q.mjs +0 -136
- package/dist/transport-DKWIq53q.mjs.map +0 -1
- package/dist/trusted-proxy-tKiVtyTD.mjs +0 -51
- package/dist/trusted-proxy-tKiVtyTD.mjs.map +0 -1
- package/dist/turnstile-DpsoyOfg.mjs +0 -53
- package/dist/turnstile-DpsoyOfg.mjs.map +0 -1
- package/dist/types-B4sUC2p1.d.mts +0 -198
- package/dist/types-B4sUC2p1.d.mts.map +0 -1
- package/dist/types-B7Lmsh1E.d.mts +0 -887
- package/dist/types-B7Lmsh1E.d.mts.map +0 -1
- package/dist/types-BZakh7Ym.mjs +0 -22
- package/dist/types-BZakh7Ym.mjs.map +0 -1
- package/dist/types-B_CppEmv.d.mts +0 -264
- package/dist/types-B_CppEmv.d.mts.map +0 -1
- package/dist/types-BcrBrE6-.mjs +0 -159
- package/dist/types-BcrBrE6-.mjs.map +0 -1
- package/dist/types-BkUg2yHx.mjs +0 -92
- package/dist/types-BkUg2yHx.mjs.map +0 -1
- package/dist/types-CAZgq9fO.d.mts +0 -124
- package/dist/types-CAZgq9fO.d.mts.map +0 -1
- package/dist/types-Cd-O03VG.d.mts +0 -363
- package/dist/types-Cd-O03VG.d.mts.map +0 -1
- package/dist/types-Cr-Kg-3D.d.mts +0 -1641
- package/dist/types-Cr-Kg-3D.d.mts.map +0 -1
- package/dist/types-D2fvWd1i.d.mts +0 -359
- package/dist/types-D2fvWd1i.d.mts.map +0 -1
- package/dist/types-D7HUsguf.mjs +0 -77
- package/dist/types-D7HUsguf.mjs.map +0 -1
- package/dist/types-DA1Te3Oy.d.mts +0 -198
- package/dist/types-DA1Te3Oy.d.mts.map +0 -1
- package/dist/types-DhV6Q5bP.mjs +0 -18
- package/dist/types-DhV6Q5bP.mjs.map +0 -1
- package/dist/types-DnnVSl6Y.d.mts +0 -85
- package/dist/types-DnnVSl6Y.d.mts.map +0 -1
- package/dist/types-VT4-BugW.mjs +0 -16
- package/dist/types-VT4-BugW.mjs.map +0 -1
- package/dist/upload-attempts-B68ZGNu9.mjs +0 -32
- package/dist/upload-attempts-B68ZGNu9.mjs.map +0 -1
- package/dist/url-pattern-CCeZMGOh.mjs +0 -33
- package/dist/url-pattern-CCeZMGOh.mjs.map +0 -1
- package/dist/user-DDSL-sGw.mjs +0 -171
- package/dist/user-DDSL-sGw.mjs.map +0 -1
- package/dist/utils-DdhmO9w9.mjs +0 -439
- package/dist/utils-DdhmO9w9.mjs.map +0 -1
- package/dist/validate-CHosPEhs.mjs +0 -453
- package/dist/validate-CHosPEhs.mjs.map +0 -1
- package/dist/validate-CM79QXYE.d.mts +0 -432
- package/dist/validate-CM79QXYE.d.mts.map +0 -1
- package/dist/validation-BFecQczG.mjs +0 -218
- package/dist/validation-BFecQczG.mjs.map +0 -1
- package/dist/version-BRZXVifl.mjs +0 -9
- package/dist/version-BRZXVifl.mjs.map +0 -1
- package/dist/version-DUQDHgrf.mjs +0 -7
- package/dist/version-DUQDHgrf.mjs.map +0 -1
- package/dist/widgets-WdV72brA.mjs +0 -126
- package/dist/widgets-WdV72brA.mjs.map +0 -1
- package/dist/wxr-taxonomies-BHTdqkmZ.d.mts +0 -34
- package/dist/wxr-taxonomies-BHTdqkmZ.d.mts.map +0 -1
- package/dist/wxr-taxonomies-DP6ESgMN.mjs +0 -372
- package/dist/wxr-taxonomies-DP6ESgMN.mjs.map +0 -1
- package/dist/zod-generator-D6uE4WiS.mjs +0 -389
- package/dist/zod-generator-D6uE4WiS.mjs.map +0 -1
- package/src/astro/routes/api/relations/[id]/translations.ts +0 -33
- /package/dist/{api-tokens-CE1ECIg0.mjs → api-tokens-FaG1zPsj.mjs} +0 -0
- /package/dist/{ssrf-DpBH7aOt.mjs → ssrf-DZOt8Qof.mjs} +0 -0
|
@@ -1,1921 +0,0 @@
|
|
|
1
|
-
import { n as validateJsonFieldName } from "./validate-DANX1-Qi.mjs";
|
|
2
|
-
import { _ as pluginDataUpdateGuard, g as pluginDataOrderExpr, h as pluginDataExtractExpr, v as pluginDataWriteExpr } from "./dialect-helpers-7_IqPdh9.mjs";
|
|
3
|
-
import { r as getI18nConfig, s as resolveContentCreateLocale } from "./config-CvmjJcVw.mjs";
|
|
4
|
-
import { i as serializeConditionalValue, n as assertStorageKey, r as assertStorageRevision, t as OptionsRepository } from "./options-DhrI7eMv.mjs";
|
|
5
|
-
import { a as normalizeRevisionLimit, i as RevisionRepository, t as ContentRepository } from "./content-DFaIEDj9.mjs";
|
|
6
|
-
import { o as decodeCursor, s as encodeCursor } from "./types-BkUg2yHx.mjs";
|
|
7
|
-
import { t as withTransaction } from "./transaction-BcJO4Tmc.mjs";
|
|
8
|
-
import { t as isValidFocalPointUpdate } from "./focal-point-Gr34o-4o.mjs";
|
|
9
|
-
import { t as MediaRepository } from "./media-Qp5S3eH3.mjs";
|
|
10
|
-
import { t as UserRepository } from "./user-DDSL-sGw.mjs";
|
|
11
|
-
import { t as TaxonomyRepository } from "./taxonomy-C3Fu1YeH.mjs";
|
|
12
|
-
import { t as CommentRepository } from "./comment-CrBl22OF.mjs";
|
|
13
|
-
import { a as resolveLocalizedContentRoutePath } from "./resolve-2WbQsCvT.mjs";
|
|
14
|
-
import { t as RedirectRepository } from "./redirect-iJf5fltD.mjs";
|
|
15
|
-
import { n as normalizeMime, t as matchesMimeAllowlist } from "./mime-ChLqYwDk.mjs";
|
|
16
|
-
import { t as enrichImageMetadata } from "./enrich-Dl3kZ-lP.mjs";
|
|
17
|
-
import { t as EntryLockRepository } from "./entry-locks-D0foLdjQ.mjs";
|
|
18
|
-
import { t as SeoRepository } from "./seo-D5w3-Azy.mjs";
|
|
19
|
-
import { d as findTaxonomyStructure, m as selectTaxonomyDefs } from "./loader-BzqeR1ZN.mjs";
|
|
20
|
-
import { U as markContentMediaUsageCollectionStaleSafely, a as SchemaRegistry } from "./block-type-registry-DUI_3KxT.mjs";
|
|
21
|
-
import { n as handleMediaDelete } from "./media-h7tOiDLj.mjs";
|
|
22
|
-
import { createPluginSecretRedactor } from "./plugins/secret-redactor.mjs";
|
|
23
|
-
import { s as invalidateSiteSettingsCache, u as createSettingsAccess } from "./settings-CgjRWGW9.mjs";
|
|
24
|
-
import { s as handleTermCreate } from "./taxonomies-C_XMoRcX.mjs";
|
|
25
|
-
import { o as stripCredentialHeaders, r as resolveAndValidateExternalUrl, t as SsrfError } from "./ssrf-CJUnJymJ.mjs";
|
|
26
|
-
import { f as updateRedirectBody, t as createRedirectBody } from "./redirects-Cv9apQKj.mjs";
|
|
27
|
-
import { t as CONTENT_TYPE_RE } from "./media-Pu1L6xir.mjs";
|
|
28
|
-
import { t as GLOBAL_UPLOAD_ALLOWLIST } from "./media-allowlist-BSoa9Vvm.mjs";
|
|
29
|
-
import { a as handleRedirectCreate, c as handleRedirectList, l as handleRedirectUpdate, o as handleRedirectDelete } from "./redirects-CYu1u1aK.mjs";
|
|
30
|
-
import { t as CronAccessImpl } from "./cron-7DCJwPXq.mjs";
|
|
31
|
-
import { bufferPluginHttpRequest, pluginHttpRedirectAction, pluginHttpResponseFromWire, pluginHttpResponseToWire, rewritePluginHttpRedirect } from "./plugins/http-wire.mjs";
|
|
32
|
-
import { sql } from "kysely";
|
|
33
|
-
import { ulid } from "ulidx";
|
|
34
|
-
import mime from "mime/lite";
|
|
35
|
-
|
|
36
|
-
//#region src/plugins/storage-query.ts
|
|
37
|
-
/**
|
|
38
|
-
* Error thrown when querying non-indexed fields
|
|
39
|
-
*/
|
|
40
|
-
var StorageQueryError = class extends Error {
|
|
41
|
-
constructor(message, field, suggestion) {
|
|
42
|
-
super(message);
|
|
43
|
-
this.field = field;
|
|
44
|
-
this.suggestion = suggestion;
|
|
45
|
-
this.name = "StorageQueryError";
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* Error thrown when a guarded `updateIf` loses a concurrent race by aborting
|
|
50
|
-
* rather than resolving to `{ applied: false }`.
|
|
51
|
-
*
|
|
52
|
-
* `updateIf`'s `{ applied: false }` contract (row absent OR guard failed)
|
|
53
|
-
* assumes READ COMMITTED — the default. There, a losing concurrent writer
|
|
54
|
-
* re-evaluates the guard against the winner's freshly committed row and
|
|
55
|
-
* cleanly resolves to `{ applied: false }`. Two aborts escape that contract:
|
|
56
|
-
*
|
|
57
|
-
* - `40001` (serialization_failure), only above READ COMMITTED, where the
|
|
58
|
-
* loser cannot re-read against a newer snapshot.
|
|
59
|
-
* - `40P01` (deadlock_detected), at any isolation level, when transactions
|
|
60
|
-
* take row locks in opposite order.
|
|
61
|
-
*
|
|
62
|
-
* This error surfaces either abort so the caller can retry the write.
|
|
63
|
-
*
|
|
64
|
-
* The no-oversell SAFETY invariant holds either way: a losing writer NEVER
|
|
65
|
-
* applies its update — it either sees `{ applied: false }` or throws here.
|
|
66
|
-
*/
|
|
67
|
-
var StorageSerializationError = class extends Error {
|
|
68
|
-
code = "STORAGE_SERIALIZATION_FAILURE";
|
|
69
|
-
retryable = true;
|
|
70
|
-
/** The Postgres SQLSTATE that triggered this error (`40001` / `40P01`). */
|
|
71
|
-
sqlState;
|
|
72
|
-
constructor(message, options) {
|
|
73
|
-
super(message, { cause: options?.cause });
|
|
74
|
-
this.name = "StorageSerializationError";
|
|
75
|
-
this.sqlState = options?.sqlState;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
* Check if a value is a range filter
|
|
80
|
-
*/
|
|
81
|
-
function isRangeFilter(value) {
|
|
82
|
-
if (typeof value !== "object" || value === null) return false;
|
|
83
|
-
return "gt" in value || "gte" in value || "lt" in value || "lte" in value;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Check if a value is an IN filter
|
|
87
|
-
*/
|
|
88
|
-
function isInFilter(value) {
|
|
89
|
-
if (typeof value !== "object" || value === null) return false;
|
|
90
|
-
return "in" in value && Array.isArray(value.in);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Check if a value is a startsWith filter
|
|
94
|
-
*/
|
|
95
|
-
function isStartsWithFilter(value) {
|
|
96
|
-
if (typeof value !== "object" || value === null) return false;
|
|
97
|
-
return "startsWith" in value && typeof value.startsWith === "string";
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Escape LIKE pattern metacharacters so a startsWith prefix matches
|
|
101
|
-
* literally. Without this, `%` and `_` in the prefix act as wildcards
|
|
102
|
-
* (e.g. `{ startsWith: "50%" }` would match "50x off").
|
|
103
|
-
*/
|
|
104
|
-
function escapeLikePattern(value) {
|
|
105
|
-
return value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Get the set of indexed fields from index declarations
|
|
109
|
-
*/
|
|
110
|
-
function getIndexedFields(indexes) {
|
|
111
|
-
const fields = /* @__PURE__ */ new Set();
|
|
112
|
-
for (const index of indexes) if (Array.isArray(index)) for (const field of index) fields.add(field);
|
|
113
|
-
else fields.add(index);
|
|
114
|
-
return fields;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Validate that all fields in a where clause are indexed
|
|
118
|
-
*/
|
|
119
|
-
function validateWhereClause(where, indexedFields, pluginId, collection) {
|
|
120
|
-
for (const field of Object.keys(where)) if (!indexedFields.has(field)) throw new StorageQueryError(`Cannot query on non-indexed field '${field}'.`, field, `Add '${field}' to storage.${collection}.indexes in plugin '${pluginId}' to enable this query.`);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Validate orderBy fields are indexed
|
|
124
|
-
*/
|
|
125
|
-
function validateOrderByClause(orderBy, indexedFields, pluginId, collection) {
|
|
126
|
-
for (const field of Object.keys(orderBy)) if (!indexedFields.has(field)) throw new StorageQueryError(`Cannot order by non-indexed field '${field}'.`, field, `Add '${field}' to storage.${collection}.indexes in plugin '${pluginId}' to enable ordering by this field.`);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* SQL expression for extracting a queryable field from the `_plugin_storage.data`
|
|
130
|
-
* column.
|
|
131
|
-
*
|
|
132
|
-
* Delegates to `pluginDataExtractExpr`, which validates the field name before
|
|
133
|
-
* interpolation and applies the dialect-correct extraction: a `::jsonb` cast on
|
|
134
|
-
* Postgres (the `data` column is `text`) plus an optional type-guarded
|
|
135
|
-
* `::numeric` cast so numeric comparisons don't fall back to lexical text
|
|
136
|
-
* ordering.
|
|
137
|
-
*/
|
|
138
|
-
function jsonExtract(db, field, options) {
|
|
139
|
-
return pluginDataExtractExpr(db, field, options);
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* SQL expression for ordering by a `_plugin_storage.data` field.
|
|
143
|
-
*
|
|
144
|
-
* Delegates to `pluginDataOrderExpr`, which orders over the jsonb-native value
|
|
145
|
-
* on Postgres so numeric fields sort numerically (not lexically) while staying
|
|
146
|
-
* total across heterogeneous data. SQLite keeps `json_extract` (already numeric).
|
|
147
|
-
*/
|
|
148
|
-
function jsonOrderExtract(db, field) {
|
|
149
|
-
return pluginDataOrderExpr(db, field);
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Build a WHERE clause condition for a single field
|
|
153
|
-
*/
|
|
154
|
-
function buildCondition(db, field, value) {
|
|
155
|
-
const extractFor = (numeric) => jsonExtract(db, field, { numeric });
|
|
156
|
-
if (value === null) return {
|
|
157
|
-
sql: `${extractFor(false)} IS NULL`,
|
|
158
|
-
params: []
|
|
159
|
-
};
|
|
160
|
-
if (typeof value === "number") return {
|
|
161
|
-
sql: `${extractFor(true)} = ?`,
|
|
162
|
-
params: [value]
|
|
163
|
-
};
|
|
164
|
-
if (typeof value === "string") return {
|
|
165
|
-
sql: `${extractFor(false)} = ?`,
|
|
166
|
-
params: [value]
|
|
167
|
-
};
|
|
168
|
-
if (typeof value === "boolean") return {
|
|
169
|
-
sql: `${extractFor(false)} = ?`,
|
|
170
|
-
params: [value]
|
|
171
|
-
};
|
|
172
|
-
if (isInFilter(value)) {
|
|
173
|
-
const numeric = value.in.length > 0 && value.in.every((v) => typeof v === "number");
|
|
174
|
-
const placeholders = value.in.map(() => "?").join(", ");
|
|
175
|
-
return {
|
|
176
|
-
sql: `${extractFor(numeric)} IN (${placeholders})`,
|
|
177
|
-
params: value.in
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
if (isStartsWithFilter(value)) return {
|
|
181
|
-
sql: `${extractFor(false)} LIKE ? ESCAPE '\\'`,
|
|
182
|
-
params: [`${escapeLikePattern(value.startsWith)}%`]
|
|
183
|
-
};
|
|
184
|
-
if (isRangeFilter(value)) {
|
|
185
|
-
const conditions = [];
|
|
186
|
-
const params = [];
|
|
187
|
-
const pushBound = (op, bound) => {
|
|
188
|
-
conditions.push(`${extractFor(typeof bound === "number")} ${op} ?`);
|
|
189
|
-
params.push(bound);
|
|
190
|
-
};
|
|
191
|
-
if (value.gt !== void 0) pushBound(">", value.gt);
|
|
192
|
-
if (value.gte !== void 0) pushBound(">=", value.gte);
|
|
193
|
-
if (value.lt !== void 0) pushBound("<", value.lt);
|
|
194
|
-
if (value.lte !== void 0) pushBound("<=", value.lte);
|
|
195
|
-
if (conditions.length === 0) throw new StorageQueryError(`Range filter for field '${field}' has no defined bound`, field, "Provide at least one of gt, gte, lt, or lte, or omit the field.");
|
|
196
|
-
return {
|
|
197
|
-
sql: conditions.join(" AND "),
|
|
198
|
-
params
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
throw new StorageQueryError(`Unknown filter type for field '${field}'`);
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Build a complete WHERE clause from a WhereClause object
|
|
205
|
-
*/
|
|
206
|
-
function buildWhereClause(db, where) {
|
|
207
|
-
const conditions = [];
|
|
208
|
-
const params = [];
|
|
209
|
-
for (const [field, value] of Object.entries(where)) {
|
|
210
|
-
const condition = buildCondition(db, field, value);
|
|
211
|
-
if (!condition.sql) continue;
|
|
212
|
-
conditions.push(condition.sql);
|
|
213
|
-
params.push(...condition.params);
|
|
214
|
-
}
|
|
215
|
-
if (conditions.length === 0) return {
|
|
216
|
-
sql: "",
|
|
217
|
-
params: []
|
|
218
|
-
};
|
|
219
|
-
return {
|
|
220
|
-
sql: conditions.join(" AND "),
|
|
221
|
-
params
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
//#endregion
|
|
226
|
-
//#region src/plugins/storage-update.ts
|
|
227
|
-
function isRecord(value) {
|
|
228
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
229
|
-
}
|
|
230
|
-
function requireRecord(value, label) {
|
|
231
|
-
if (!isRecord(value)) throw new TypeError(`${label} must be an object`);
|
|
232
|
-
const prototype = Object.getPrototypeOf(value);
|
|
233
|
-
if (prototype !== Object.prototype && prototype !== null) throw new TypeError(`${label} must be a plain object`);
|
|
234
|
-
return value;
|
|
235
|
-
}
|
|
236
|
-
function operand(value) {
|
|
237
|
-
if (typeof value === "string" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
238
|
-
throw new TypeError("Storage guard operands must be strings or finite numbers");
|
|
239
|
-
}
|
|
240
|
-
function normalizeFilter(value) {
|
|
241
|
-
if (value === null || typeof value === "boolean") return value;
|
|
242
|
-
if (typeof value === "string" || typeof value === "number") return operand(value);
|
|
243
|
-
const filter = requireRecord(value, "Storage guard filter");
|
|
244
|
-
const values = filter.in;
|
|
245
|
-
if (Array.isArray(values)) return { in: Array.from(values, operand) };
|
|
246
|
-
const prefix = filter.startsWith;
|
|
247
|
-
if (typeof prefix === "string") return { startsWith: prefix };
|
|
248
|
-
const range = {};
|
|
249
|
-
for (const key of [
|
|
250
|
-
"gt",
|
|
251
|
-
"gte",
|
|
252
|
-
"lt",
|
|
253
|
-
"lte"
|
|
254
|
-
]) if (key in filter) {
|
|
255
|
-
const bound = filter[key];
|
|
256
|
-
range[key] = bound === void 0 ? void 0 : operand(bound);
|
|
257
|
-
}
|
|
258
|
-
return range;
|
|
259
|
-
}
|
|
260
|
-
function parseStorageUpdate(value) {
|
|
261
|
-
const args = requireRecord(value, "Storage update arguments");
|
|
262
|
-
if (Object.keys(args).some((key) => key !== "where" && key !== "set" && key !== "delta")) throw new TypeError("Unknown storage update argument");
|
|
263
|
-
const where = {};
|
|
264
|
-
for (const [field, filter] of Object.entries(requireRecord(args.where, "Storage where guard"))) {
|
|
265
|
-
validateJsonFieldName(field);
|
|
266
|
-
where[field] = normalizeFilter(filter);
|
|
267
|
-
}
|
|
268
|
-
const set = args.set === void 0 ? {} : requireRecord(args.set, "Storage set");
|
|
269
|
-
const delta = args.delta === void 0 ? {} : requireRecord(args.delta, "Storage delta");
|
|
270
|
-
const setEntries = [];
|
|
271
|
-
for (const [field, entry] of Object.entries(set)) {
|
|
272
|
-
if (entry === void 0) continue;
|
|
273
|
-
validateJsonFieldName(field);
|
|
274
|
-
const serialized = JSON.stringify(entry);
|
|
275
|
-
if (serialized === void 0) throw new TypeError("Storage set values must be JSON serializable");
|
|
276
|
-
const normalized = JSON.parse(serialized);
|
|
277
|
-
setEntries.push([field, normalized]);
|
|
278
|
-
}
|
|
279
|
-
const setFields = new Set(setEntries.map(([field]) => field));
|
|
280
|
-
const deltaEntries = [];
|
|
281
|
-
for (const [field, entry] of Object.entries(delta)) {
|
|
282
|
-
if (entry === void 0) continue;
|
|
283
|
-
validateJsonFieldName(field);
|
|
284
|
-
if (setFields.has(field)) throw new TypeError("Storage field appears in both set and delta");
|
|
285
|
-
const spec = requireRecord(entry, "Storage numeric delta");
|
|
286
|
-
const keys = Object.keys(spec);
|
|
287
|
-
const key = keys[0];
|
|
288
|
-
if (keys.length !== 1 || key !== "inc" && key !== "dec") throw new TypeError("Storage delta must contain exactly one of inc or dec");
|
|
289
|
-
const amount = spec[key];
|
|
290
|
-
if (typeof amount !== "number" || !Number.isSafeInteger(amount)) throw new TypeError("Storage delta must be a safe integer");
|
|
291
|
-
deltaEntries.push([field, key === "dec" ? -amount : amount]);
|
|
292
|
-
}
|
|
293
|
-
if (setEntries.length + deltaEntries.length === 0) throw new TypeError("Storage update requires at least one of set or delta");
|
|
294
|
-
return {
|
|
295
|
-
where,
|
|
296
|
-
setEntries,
|
|
297
|
-
deltaEntries
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
//#endregion
|
|
302
|
-
//#region src/database/repositories/plugin-storage.ts
|
|
303
|
-
/**
|
|
304
|
-
* SQLSTATEs a losing concurrent `updateIf` writer can abort with.
|
|
305
|
-
*
|
|
306
|
-
* `40001` (serialization_failure) is raised only above READ COMMITTED, where
|
|
307
|
-
* the loser cannot re-evaluate its guard against a newer snapshot. `40P01`
|
|
308
|
-
* (deadlock_detected) is not tied to isolation level: it needs only two
|
|
309
|
-
* transactions taking row locks in opposite order, which a caller reaches at
|
|
310
|
-
* READ COMMITTED by wrapping several `updateIf` calls in one transaction.
|
|
311
|
-
*/
|
|
312
|
-
const SERIALIZATION_SQLSTATES = new Set(["40001", "40P01"]);
|
|
313
|
-
function errSqlState(err) {
|
|
314
|
-
if (typeof err !== "object" || err === null) return void 0;
|
|
315
|
-
const code = err.code;
|
|
316
|
-
if (typeof code === "string") return code;
|
|
317
|
-
const cause = err.cause;
|
|
318
|
-
if (typeof cause === "object" && cause !== null) {
|
|
319
|
-
const causeCode = cause.code;
|
|
320
|
-
if (typeof causeCode === "string") return causeCode;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
function mapSerializationFailure(err) {
|
|
324
|
-
const sqlState = errSqlState(err);
|
|
325
|
-
if (sqlState !== void 0 && SERIALIZATION_SQLSTATES.has(sqlState)) return new StorageSerializationError("Storage write must be retried. Restart the transaction before retrying when using an explicit transaction.", {
|
|
326
|
-
cause: err,
|
|
327
|
-
sqlState
|
|
328
|
-
});
|
|
329
|
-
return err;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Interleave a `?`-placeholder SQL string with its params into a single
|
|
333
|
-
* boolean raw expression. Used as a WHERE predicate directly — wrapping it
|
|
334
|
-
* in `(...) = 1` breaks on Postgres, which has a strict boolean type (#920).
|
|
335
|
-
*/
|
|
336
|
-
function rawWhereExpr(sqlText, params) {
|
|
337
|
-
const parts = [];
|
|
338
|
-
let paramIndex = 0;
|
|
339
|
-
const sqlParts = sqlText.split("?");
|
|
340
|
-
for (let i = 0; i < sqlParts.length; i++) {
|
|
341
|
-
if (i > 0) parts.push(sql`${params[paramIndex++]}`);
|
|
342
|
-
if (sqlParts[i]) parts.push(sql.raw(sqlParts[i]));
|
|
343
|
-
}
|
|
344
|
-
return sql`(${sql.join(parts, sql.raw(""))})`;
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Plugin Storage Repository
|
|
348
|
-
*
|
|
349
|
-
* Implements the StorageCollection interface for a specific plugin and collection.
|
|
350
|
-
*/
|
|
351
|
-
/**
|
|
352
|
-
* Rank a sort expression so NULLs get a deterministic position.
|
|
353
|
-
*
|
|
354
|
-
* A missing key in a schemaless document extracts as NULL, which sorts
|
|
355
|
-
* differently per dialect (SQLite first, Postgres last) and makes every
|
|
356
|
-
* comparison against it UNKNOWN. Sorting on this rank ahead of the value puts
|
|
357
|
-
* NULLs in the same place on both dialects and keeps the seek operands
|
|
358
|
-
* non-NULL.
|
|
359
|
-
*/
|
|
360
|
-
function nullRank(expr) {
|
|
361
|
-
return sql`(case when ${expr} is null then 1 else 0 end)`;
|
|
362
|
-
}
|
|
363
|
-
var PluginStorageRepository = class {
|
|
364
|
-
indexedFields;
|
|
365
|
-
constructor(db, pluginId, collection, indexes) {
|
|
366
|
-
this.db = db;
|
|
367
|
-
this.pluginId = pluginId;
|
|
368
|
-
this.collection = collection;
|
|
369
|
-
this.indexedFields = getIndexedFields(indexes);
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Get a document by ID
|
|
373
|
-
*/
|
|
374
|
-
async get(id) {
|
|
375
|
-
const row = await this.db.selectFrom("_plugin_storage").select("data").where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "=", id).executeTakeFirst();
|
|
376
|
-
if (!row) return null;
|
|
377
|
-
return JSON.parse(row.data);
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Store a document
|
|
381
|
-
*/
|
|
382
|
-
async put(id, data) {
|
|
383
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
384
|
-
const jsonData = JSON.stringify(data);
|
|
385
|
-
const revision = crypto.randomUUID();
|
|
386
|
-
await this.db.insertInto("_plugin_storage").values({
|
|
387
|
-
plugin_id: this.pluginId,
|
|
388
|
-
collection: this.collection,
|
|
389
|
-
id,
|
|
390
|
-
data: jsonData,
|
|
391
|
-
revision,
|
|
392
|
-
created_at: now,
|
|
393
|
-
updated_at: now
|
|
394
|
-
}).onConflict((oc) => oc.columns([
|
|
395
|
-
"plugin_id",
|
|
396
|
-
"collection",
|
|
397
|
-
"id"
|
|
398
|
-
]).doUpdateSet({
|
|
399
|
-
data: jsonData,
|
|
400
|
-
revision,
|
|
401
|
-
updated_at: now
|
|
402
|
-
})).execute();
|
|
403
|
-
}
|
|
404
|
-
async getVersioned(id) {
|
|
405
|
-
assertStorageKey(id);
|
|
406
|
-
const row = await this.db.selectFrom("_plugin_storage").select(["data", "revision"]).where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "=", id).executeTakeFirst();
|
|
407
|
-
if (!row) return null;
|
|
408
|
-
return {
|
|
409
|
-
value: JSON.parse(row.data),
|
|
410
|
-
revision: row.revision
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
async compareAndSet(id, expectedRevision, data) {
|
|
414
|
-
assertStorageKey(id);
|
|
415
|
-
if (expectedRevision !== null) assertStorageRevision(expectedRevision);
|
|
416
|
-
const jsonData = serializeConditionalValue(data);
|
|
417
|
-
const revision = crypto.randomUUID();
|
|
418
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
419
|
-
const row = expectedRevision === null ? await this.db.insertInto("_plugin_storage").values({
|
|
420
|
-
plugin_id: this.pluginId,
|
|
421
|
-
collection: this.collection,
|
|
422
|
-
id,
|
|
423
|
-
data: jsonData,
|
|
424
|
-
revision,
|
|
425
|
-
created_at: now,
|
|
426
|
-
updated_at: now
|
|
427
|
-
}).onConflict((oc) => oc.columns([
|
|
428
|
-
"plugin_id",
|
|
429
|
-
"collection",
|
|
430
|
-
"id"
|
|
431
|
-
]).doNothing()).returning("revision").executeTakeFirst() : await this.db.updateTable("_plugin_storage").set({
|
|
432
|
-
data: jsonData,
|
|
433
|
-
revision,
|
|
434
|
-
updated_at: now
|
|
435
|
-
}).where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "=", id).where("revision", "=", expectedRevision).returning("revision").executeTakeFirst();
|
|
436
|
-
return row ? {
|
|
437
|
-
applied: true,
|
|
438
|
-
revision: row.revision
|
|
439
|
-
} : { applied: false };
|
|
440
|
-
}
|
|
441
|
-
async compareAndDelete(id, expectedRevision) {
|
|
442
|
-
assertStorageKey(id);
|
|
443
|
-
assertStorageRevision(expectedRevision);
|
|
444
|
-
return { applied: await this.db.deleteFrom("_plugin_storage").where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "=", id).where("revision", "=", expectedRevision).returning("id").executeTakeFirst() !== void 0 };
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Delete a document
|
|
448
|
-
*/
|
|
449
|
-
async delete(id) {
|
|
450
|
-
return ((await this.db.deleteFrom("_plugin_storage").where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "=", id).executeTakeFirst()).numDeletedRows ?? 0) > 0;
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Check if a document exists
|
|
454
|
-
*/
|
|
455
|
-
async exists(id) {
|
|
456
|
-
return !!await this.db.selectFrom("_plugin_storage").select("id").where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "=", id).executeTakeFirst();
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Get multiple documents by ID
|
|
460
|
-
*/
|
|
461
|
-
async getMany(ids) {
|
|
462
|
-
if (ids.length === 0) return /* @__PURE__ */ new Map();
|
|
463
|
-
const rows = await this.db.selectFrom("_plugin_storage").select(["id", "data"]).where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "in", ids).execute();
|
|
464
|
-
const result = /* @__PURE__ */ new Map();
|
|
465
|
-
for (const row of rows) result.set(row.id, JSON.parse(row.data));
|
|
466
|
-
return result;
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Store multiple documents
|
|
470
|
-
*/
|
|
471
|
-
async putMany(items) {
|
|
472
|
-
if (items.length === 0) return;
|
|
473
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
474
|
-
await withTransaction(this.db, async (trx) => {
|
|
475
|
-
for (const item of items) {
|
|
476
|
-
const jsonData = JSON.stringify(item.data);
|
|
477
|
-
const revision = crypto.randomUUID();
|
|
478
|
-
await trx.insertInto("_plugin_storage").values({
|
|
479
|
-
plugin_id: this.pluginId,
|
|
480
|
-
collection: this.collection,
|
|
481
|
-
id: item.id,
|
|
482
|
-
data: jsonData,
|
|
483
|
-
revision,
|
|
484
|
-
created_at: now,
|
|
485
|
-
updated_at: now
|
|
486
|
-
}).onConflict((oc) => oc.columns([
|
|
487
|
-
"plugin_id",
|
|
488
|
-
"collection",
|
|
489
|
-
"id"
|
|
490
|
-
]).doUpdateSet({
|
|
491
|
-
data: jsonData,
|
|
492
|
-
revision,
|
|
493
|
-
updated_at: now
|
|
494
|
-
})).execute();
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
/**
|
|
499
|
-
* Delete multiple documents
|
|
500
|
-
*/
|
|
501
|
-
async deleteMany(ids) {
|
|
502
|
-
if (ids.length === 0) return 0;
|
|
503
|
-
const result = await this.db.deleteFrom("_plugin_storage").where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "in", ids).executeTakeFirst();
|
|
504
|
-
return Number(result.numDeletedRows ?? 0);
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* Query documents with filters
|
|
508
|
-
*/
|
|
509
|
-
async query(options = {}) {
|
|
510
|
-
const { where = {}, orderBy = {}, cursor } = options;
|
|
511
|
-
const limit = Math.min(options.limit ?? 50, 100);
|
|
512
|
-
validateWhereClause(where, this.indexedFields, this.pluginId, this.collection);
|
|
513
|
-
if (Object.keys(orderBy).length > 0) validateOrderByClause(orderBy, this.indexedFields, this.pluginId, this.collection);
|
|
514
|
-
let query = this.db.selectFrom("_plugin_storage").select([
|
|
515
|
-
"id",
|
|
516
|
-
"data",
|
|
517
|
-
"created_at"
|
|
518
|
-
]).where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection);
|
|
519
|
-
const whereResult = buildWhereClause(this.db, where);
|
|
520
|
-
if (whereResult.sql) query = query.where(rawWhereExpr(whereResult.sql, whereResult.params));
|
|
521
|
-
const orderEntries = Object.entries(orderBy);
|
|
522
|
-
const orderDirections = new Set(orderEntries.map(([, direction]) => direction));
|
|
523
|
-
const descending = orderEntries.length > 0 && orderEntries[0][1] === "desc";
|
|
524
|
-
const sortExpr = (field) => sql`${sql.raw(jsonOrderExtract(this.db, field))}`;
|
|
525
|
-
if (cursor) {
|
|
526
|
-
const decoded = decodeCursor(cursor);
|
|
527
|
-
if (orderEntries.length === 0) query = query.where(({ eb }) => eb(sql`(created_at, id)`, ">", sql`(${decoded.orderValue}, ${decoded.id})`));
|
|
528
|
-
else {
|
|
529
|
-
if (orderDirections.size > 1) throw new StorageQueryError("Cursor pagination requires every orderBy field to share one direction.", Object.keys(orderBy).join(", "), "Sort every field the same way, or page without a cursor.");
|
|
530
|
-
const op = descending ? sql`<` : sql`>`;
|
|
531
|
-
const cursorExpr = (field) => sql`(select ${sql.raw(jsonOrderExtract(this.db, field))} from _plugin_storage where plugin_id = ${this.pluginId} and collection = ${this.collection} and id = ${decoded.id})`;
|
|
532
|
-
const fields = orderEntries.map(([field]) => {
|
|
533
|
-
const own = sortExpr(field);
|
|
534
|
-
const theirs = cursorExpr(field);
|
|
535
|
-
return {
|
|
536
|
-
equal: sql`(${nullRank(own)} = ${nullRank(theirs)} and (${nullRank(own)} = 1 or ${own} = ${theirs}))`,
|
|
537
|
-
after: sql`(${nullRank(own)} ${op} ${nullRank(theirs)} or (${nullRank(own)} = ${nullRank(theirs)} and ${nullRank(own)} = 0 and ${own} ${op} ${theirs}))`
|
|
538
|
-
};
|
|
539
|
-
});
|
|
540
|
-
const terms = fields.map((_, k) => sql`(${sql.join([...fields.slice(0, k).map((f) => f.equal), fields[k].after], sql` and `)})`);
|
|
541
|
-
terms.push(sql`(${sql.join([...fields.map((f) => f.equal), sql`id ${op} ${decoded.id}`], sql` and `)})`);
|
|
542
|
-
query = query.where(sql`(${sql.join(terms, sql` or `)})`);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
if (orderEntries.length > 0) {
|
|
546
|
-
for (const [field, direction] of orderEntries) {
|
|
547
|
-
const dir = direction === "desc" ? sql`desc` : sql`asc`;
|
|
548
|
-
const expr = sortExpr(field);
|
|
549
|
-
query = query.orderBy(sql`${nullRank(expr)} ${dir}`);
|
|
550
|
-
query = query.orderBy(sql`${expr} ${dir}`);
|
|
551
|
-
}
|
|
552
|
-
query = query.orderBy("id", descending ? "desc" : "asc");
|
|
553
|
-
} else query = query.orderBy("created_at", "asc").orderBy("id", "asc");
|
|
554
|
-
query = query.limit(limit + 1);
|
|
555
|
-
const rows = await query.execute();
|
|
556
|
-
const hasMore = rows.length > limit;
|
|
557
|
-
const items = rows.slice(0, limit).map((row) => ({
|
|
558
|
-
id: row.id,
|
|
559
|
-
data: JSON.parse(row.data)
|
|
560
|
-
}));
|
|
561
|
-
let nextCursor;
|
|
562
|
-
if (hasMore) {
|
|
563
|
-
const lastItem = rows[limit - 1];
|
|
564
|
-
if (lastItem) nextCursor = encodeCursor(lastItem.created_at, lastItem.id);
|
|
565
|
-
}
|
|
566
|
-
return {
|
|
567
|
-
items,
|
|
568
|
-
cursor: nextCursor,
|
|
569
|
-
hasMore
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Count documents matching a filter
|
|
574
|
-
*/
|
|
575
|
-
async count(where) {
|
|
576
|
-
if (where && Object.keys(where).length > 0) validateWhereClause(where, this.indexedFields, this.pluginId, this.collection);
|
|
577
|
-
let query = this.db.selectFrom("_plugin_storage").select(sql`COUNT(*)`.as("count")).where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection);
|
|
578
|
-
if (where && Object.keys(where).length > 0) {
|
|
579
|
-
const whereResult = buildWhereClause(this.db, where);
|
|
580
|
-
if (whereResult.sql) query = query.where(rawWhereExpr(whereResult.sql, whereResult.params));
|
|
581
|
-
}
|
|
582
|
-
const result = await query.executeTakeFirst();
|
|
583
|
-
return Number(result?.count ?? 0);
|
|
584
|
-
}
|
|
585
|
-
async updateIf(id, args) {
|
|
586
|
-
if (typeof id !== "string") throw new TypeError("Storage ID must be a string");
|
|
587
|
-
const { where, setEntries, deltaEntries } = parseStorageUpdate(args);
|
|
588
|
-
for (const value of Object.values(where)) if (isInFilter(value) && value.in.length === 0) return { applied: false };
|
|
589
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
590
|
-
const dataExpr = pluginDataWriteExpr(this.db, setEntries, deltaEntries);
|
|
591
|
-
let query = this.db.updateTable("_plugin_storage").set({
|
|
592
|
-
data: dataExpr,
|
|
593
|
-
updated_at: now
|
|
594
|
-
}).where("plugin_id", "=", this.pluginId).where("collection", "=", this.collection).where("id", "=", id).where(pluginDataUpdateGuard(this.db, deltaEntries));
|
|
595
|
-
const whereResult = buildWhereClause(this.db, where);
|
|
596
|
-
if (whereResult.sql) query = query.where(rawWhereExpr(whereResult.sql, whereResult.params));
|
|
597
|
-
let row;
|
|
598
|
-
try {
|
|
599
|
-
row = await query.returning("data").executeTakeFirst();
|
|
600
|
-
} catch (err) {
|
|
601
|
-
throw mapSerializationFailure(err);
|
|
602
|
-
}
|
|
603
|
-
if (!row) return { applied: false };
|
|
604
|
-
return {
|
|
605
|
-
applied: true,
|
|
606
|
-
data: JSON.parse(row.data)
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
//#endregion
|
|
612
|
-
//#region src/plugins/content-access.ts
|
|
613
|
-
function createContentAccess(db, accessOptions) {
|
|
614
|
-
const contentRepo = new ContentRepository(db);
|
|
615
|
-
const seoRepo = new SeoRepository(db);
|
|
616
|
-
return {
|
|
617
|
-
async get(collection, id) {
|
|
618
|
-
const item = await contentRepo.findById(collection, id);
|
|
619
|
-
if (!item) return null;
|
|
620
|
-
const result = {
|
|
621
|
-
id: item.id,
|
|
622
|
-
type: item.type,
|
|
623
|
-
slug: item.slug,
|
|
624
|
-
status: item.status,
|
|
625
|
-
data: item.data,
|
|
626
|
-
createdAt: item.createdAt,
|
|
627
|
-
updatedAt: item.updatedAt,
|
|
628
|
-
locale: item.locale,
|
|
629
|
-
publishedAt: item.publishedAt,
|
|
630
|
-
scheduledAt: item.scheduledAt,
|
|
631
|
-
authorId: item.authorId,
|
|
632
|
-
translationGroup: item.translationGroup,
|
|
633
|
-
liveRevisionId: item.liveRevisionId,
|
|
634
|
-
draftRevisionId: item.draftRevisionId,
|
|
635
|
-
version: item.version
|
|
636
|
-
};
|
|
637
|
-
if (await seoRepo.isEnabled(collection)) result.seo = await seoRepo.get(collection, item.id);
|
|
638
|
-
return result;
|
|
639
|
-
},
|
|
640
|
-
async list(collection, options) {
|
|
641
|
-
let orderBy;
|
|
642
|
-
if (options?.orderBy) {
|
|
643
|
-
const first = Object.entries(options.orderBy)[0];
|
|
644
|
-
if (first) orderBy = {
|
|
645
|
-
field: first[0],
|
|
646
|
-
direction: first[1]
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
const result = await contentRepo.findMany(collection, {
|
|
650
|
-
limit: options?.limit ?? 50,
|
|
651
|
-
cursor: options?.cursor,
|
|
652
|
-
orderBy,
|
|
653
|
-
where: options?.where
|
|
654
|
-
});
|
|
655
|
-
const items = result.items.map((item) => ({
|
|
656
|
-
id: item.id,
|
|
657
|
-
type: item.type,
|
|
658
|
-
slug: item.slug,
|
|
659
|
-
status: item.status,
|
|
660
|
-
data: item.data,
|
|
661
|
-
createdAt: item.createdAt,
|
|
662
|
-
updatedAt: item.updatedAt,
|
|
663
|
-
locale: item.locale,
|
|
664
|
-
publishedAt: item.publishedAt,
|
|
665
|
-
scheduledAt: item.scheduledAt,
|
|
666
|
-
authorId: item.authorId,
|
|
667
|
-
translationGroup: item.translationGroup,
|
|
668
|
-
liveRevisionId: item.liveRevisionId,
|
|
669
|
-
draftRevisionId: item.draftRevisionId,
|
|
670
|
-
version: item.version
|
|
671
|
-
}));
|
|
672
|
-
if (items.length > 0 && await seoRepo.isEnabled(collection)) {
|
|
673
|
-
const seoMap = await seoRepo.getMany(collection, items.map((item) => item.id));
|
|
674
|
-
for (const item of items) {
|
|
675
|
-
const seo = seoMap.get(item.id);
|
|
676
|
-
if (seo) item.seo = seo;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return {
|
|
680
|
-
items,
|
|
681
|
-
cursor: result.nextCursor,
|
|
682
|
-
hasMore: !!result.nextCursor
|
|
683
|
-
};
|
|
684
|
-
},
|
|
685
|
-
async getTranslations(collection, id) {
|
|
686
|
-
const item = await contentRepo.findById(collection, id);
|
|
687
|
-
if (!item) throw new Error(`Content not found: ${collection}/${id}`);
|
|
688
|
-
return {
|
|
689
|
-
translationGroup: item.translationGroup || item.id,
|
|
690
|
-
translations: (item.translationGroup ? await contentRepo.findTranslations(collection, item.translationGroup) : [item]).map((translation) => ({
|
|
691
|
-
id: translation.id,
|
|
692
|
-
locale: translation.locale,
|
|
693
|
-
slug: translation.slug,
|
|
694
|
-
status: translation.status,
|
|
695
|
-
updatedAt: translation.updatedAt
|
|
696
|
-
}))
|
|
697
|
-
};
|
|
698
|
-
},
|
|
699
|
-
async getPublicUrl(collection, id) {
|
|
700
|
-
const site = accessOptions?.site;
|
|
701
|
-
if (!site?.url) return null;
|
|
702
|
-
const [item, collectionInfo] = await Promise.all([contentRepo.findById(collection, id), new SchemaRegistry(db).getCollection(collection)]);
|
|
703
|
-
if (!item || item.status !== "published" || !item.slug || !collectionInfo?.routable) return null;
|
|
704
|
-
const path = await resolveLocalizedContentRoutePath({
|
|
705
|
-
pattern: collectionInfo.urlPattern ?? null,
|
|
706
|
-
collection,
|
|
707
|
-
slug: item.slug,
|
|
708
|
-
id: item.id,
|
|
709
|
-
date: item.publishedAt,
|
|
710
|
-
locale: item.locale || site.locale,
|
|
711
|
-
trailingSlash: site.trailingSlash
|
|
712
|
-
});
|
|
713
|
-
return path === null ? null : `${site.url}${path}`;
|
|
714
|
-
},
|
|
715
|
-
...accessOptions?.revisions ? {
|
|
716
|
-
async listRevisions(collection, id, revisionOptions) {
|
|
717
|
-
return (await new RevisionRepository(db).findVisibleByEntry(collection, id, { limit: normalizeRevisionLimit(revisionOptions?.limit) })).map(({ authorId: _authorId, ...revision }) => revision);
|
|
718
|
-
},
|
|
719
|
-
async getRevision(collection, id, revisionId) {
|
|
720
|
-
const revision = await new RevisionRepository(db).findVisibleById(collection, id, revisionId);
|
|
721
|
-
if (!revision) return null;
|
|
722
|
-
const { authorId: _authorId, ...safeRevision } = revision;
|
|
723
|
-
return safeRevision;
|
|
724
|
-
}
|
|
725
|
-
} : {}
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
//#endregion
|
|
730
|
-
//#region src/plugins/media.ts
|
|
731
|
-
const DEFAULT_PLUGIN_MEDIA_READ_BYTES = 10 * 1024 * 1024;
|
|
732
|
-
const MAX_PLUGIN_MEDIA_READ_BYTES = 16 * 1024 * 1024;
|
|
733
|
-
function mediaUrl(item) {
|
|
734
|
-
return `/_emdash/api/media/asset/${encodeURIComponent(item.id)}/${encodeURIComponent(item.filename)}`;
|
|
735
|
-
}
|
|
736
|
-
function toPluginMediaItem(item) {
|
|
737
|
-
return {
|
|
738
|
-
id: item.id,
|
|
739
|
-
filename: item.filename,
|
|
740
|
-
mimeType: item.mimeType,
|
|
741
|
-
size: item.size,
|
|
742
|
-
url: mediaUrl(item),
|
|
743
|
-
createdAt: item.createdAt,
|
|
744
|
-
width: item.width,
|
|
745
|
-
height: item.height,
|
|
746
|
-
alt: item.alt,
|
|
747
|
-
caption: item.caption,
|
|
748
|
-
focalX: item.focalX,
|
|
749
|
-
focalY: item.focalY,
|
|
750
|
-
blurhash: item.blurhash,
|
|
751
|
-
dominantColor: item.dominantColor,
|
|
752
|
-
folderId: item.folderId ?? null,
|
|
753
|
-
status: "ready"
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
function resolveByteLimit(maxBytes) {
|
|
757
|
-
const limit = maxBytes ?? DEFAULT_PLUGIN_MEDIA_READ_BYTES;
|
|
758
|
-
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > MAX_PLUGIN_MEDIA_READ_BYTES) throw new RangeError(`media.readBytes maxBytes must be a positive integer no greater than ${MAX_PLUGIN_MEDIA_READ_BYTES}`);
|
|
759
|
-
return limit;
|
|
760
|
-
}
|
|
761
|
-
async function readStreamWithLimit(body, maxBytes) {
|
|
762
|
-
const reader = body.getReader();
|
|
763
|
-
const chunks = [];
|
|
764
|
-
let size = 0;
|
|
765
|
-
try {
|
|
766
|
-
while (true) {
|
|
767
|
-
const { done, value } = await reader.read();
|
|
768
|
-
if (done) break;
|
|
769
|
-
size += value.byteLength;
|
|
770
|
-
if (size > maxBytes) {
|
|
771
|
-
try {
|
|
772
|
-
await reader.cancel();
|
|
773
|
-
} catch {}
|
|
774
|
-
throw new PluginMediaByteLimitError(`Media exceeds the requested ${maxBytes}-byte limit`);
|
|
775
|
-
}
|
|
776
|
-
chunks.push(value);
|
|
777
|
-
}
|
|
778
|
-
} finally {
|
|
779
|
-
reader.releaseLock();
|
|
780
|
-
}
|
|
781
|
-
const bytes = new Uint8Array(size);
|
|
782
|
-
let offset = 0;
|
|
783
|
-
for (const chunk of chunks) {
|
|
784
|
-
bytes.set(chunk, offset);
|
|
785
|
-
offset += chunk.byteLength;
|
|
786
|
-
}
|
|
787
|
-
return bytes;
|
|
788
|
-
}
|
|
789
|
-
var PluginMediaByteLimitError = class extends RangeError {};
|
|
790
|
-
async function readPluginMediaBytes(db, storage, id, options) {
|
|
791
|
-
if (!storage) throw new Error("Media storage is not configured");
|
|
792
|
-
const limit = resolveByteLimit(options?.maxBytes);
|
|
793
|
-
const item = await new MediaRepository(db).findById(id);
|
|
794
|
-
if (!item || item.status !== "ready") throw new Error("Media item is not ready or does not exist");
|
|
795
|
-
let bytes;
|
|
796
|
-
try {
|
|
797
|
-
bytes = await readStreamWithLimit((await storage.download(item.storageKey)).body, limit);
|
|
798
|
-
} catch (error) {
|
|
799
|
-
if (error instanceof PluginMediaByteLimitError) throw error;
|
|
800
|
-
throw new Error("Failed to read media bytes");
|
|
801
|
-
}
|
|
802
|
-
return {
|
|
803
|
-
bytes,
|
|
804
|
-
filename: item.filename,
|
|
805
|
-
mimeType: item.mimeType,
|
|
806
|
-
size: bytes.byteLength,
|
|
807
|
-
...item.contentHash ? { contentHash: item.contentHash } : {}
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
async function updatePluginMediaMetadata(db, id, patch) {
|
|
811
|
-
const parsed = parsePluginMediaMetadataPatch(patch);
|
|
812
|
-
const item = await new MediaRepository(db).updateReadyMetadata(id, parsed);
|
|
813
|
-
if (!item) throw new Error("Media item is not ready or does not exist");
|
|
814
|
-
return toPluginMediaItem(item);
|
|
815
|
-
}
|
|
816
|
-
const MEDIA_METADATA_KEYS = new Set([
|
|
817
|
-
"alt",
|
|
818
|
-
"caption",
|
|
819
|
-
"focalX",
|
|
820
|
-
"focalY"
|
|
821
|
-
]);
|
|
822
|
-
function parsePluginMediaMetadataPatch(value) {
|
|
823
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("media.updateMetadata patch must be an object");
|
|
824
|
-
const keys = Object.keys(value);
|
|
825
|
-
if (keys.length === 0) throw new TypeError("media.updateMetadata must change at least one metadata field");
|
|
826
|
-
for (const key of keys) if (!MEDIA_METADATA_KEYS.has(key)) throw new TypeError(`media.updateMetadata cannot change ${key}`);
|
|
827
|
-
const alt = Reflect.get(value, "alt");
|
|
828
|
-
const caption = Reflect.get(value, "caption");
|
|
829
|
-
const focalX = Reflect.get(value, "focalX");
|
|
830
|
-
const focalY = Reflect.get(value, "focalY");
|
|
831
|
-
if (alt !== void 0 && alt !== null && typeof alt !== "string") throw new TypeError("media.updateMetadata alt must be a string or null");
|
|
832
|
-
if (caption !== void 0 && caption !== null && typeof caption !== "string") throw new TypeError("media.updateMetadata caption must be a string or null");
|
|
833
|
-
if (focalX !== void 0 && focalX !== null && typeof focalX !== "number") throw new TypeError("media.updateMetadata focalX must be a number or null");
|
|
834
|
-
if (focalY !== void 0 && focalY !== null && typeof focalY !== "number") throw new TypeError("media.updateMetadata focalY must be a number or null");
|
|
835
|
-
const patch = {};
|
|
836
|
-
if (alt !== void 0) patch.alt = alt;
|
|
837
|
-
if (caption !== void 0) patch.caption = caption;
|
|
838
|
-
if (focalX !== void 0) patch.focalX = focalX;
|
|
839
|
-
if (focalY !== void 0) patch.focalY = focalY;
|
|
840
|
-
if (!isValidFocalPointUpdate(patch)) throw new TypeError("focalX and focalY must both be valid numbers or both be null");
|
|
841
|
-
return patch;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
//#endregion
|
|
845
|
-
//#region src/plugins/route-error.ts
|
|
846
|
-
/**
|
|
847
|
-
* Error class for plugin routes
|
|
848
|
-
* Allows plugins to return structured errors with specific HTTP status codes
|
|
849
|
-
*/
|
|
850
|
-
var PluginRouteError = class PluginRouteError extends Error {
|
|
851
|
-
constructor(code, message, status = 400, details) {
|
|
852
|
-
super(message);
|
|
853
|
-
this.code = code;
|
|
854
|
-
this.status = status;
|
|
855
|
-
this.details = details;
|
|
856
|
-
this.name = "PluginRouteError";
|
|
857
|
-
}
|
|
858
|
-
/**
|
|
859
|
-
* Create a bad request error (400)
|
|
860
|
-
*/
|
|
861
|
-
static badRequest(message, details) {
|
|
862
|
-
return new PluginRouteError("BAD_REQUEST", message, 400, details);
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Create an unauthorized error (401)
|
|
866
|
-
*/
|
|
867
|
-
static unauthorized(message = "Unauthorized") {
|
|
868
|
-
return new PluginRouteError("UNAUTHORIZED", message, 401);
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* Create a forbidden error (403)
|
|
872
|
-
*/
|
|
873
|
-
static forbidden(message = "Forbidden") {
|
|
874
|
-
return new PluginRouteError("FORBIDDEN", message, 403);
|
|
875
|
-
}
|
|
876
|
-
/**
|
|
877
|
-
* Create a not found error (404)
|
|
878
|
-
*/
|
|
879
|
-
static notFound(message = "Not found") {
|
|
880
|
-
return new PluginRouteError("NOT_FOUND", message, 404);
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Create a conflict error (409)
|
|
884
|
-
*/
|
|
885
|
-
static conflict(message, details) {
|
|
886
|
-
return new PluginRouteError("CONFLICT", message, 409, details);
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* Create an internal error (500)
|
|
890
|
-
*/
|
|
891
|
-
static internal(message = "Internal error") {
|
|
892
|
-
return new PluginRouteError("INTERNAL_ERROR", message, 500);
|
|
893
|
-
}
|
|
894
|
-
};
|
|
895
|
-
|
|
896
|
-
//#endregion
|
|
897
|
-
//#region src/plugins/context.ts
|
|
898
|
-
/**
|
|
899
|
-
* Create KV accessor for a plugin
|
|
900
|
-
* All keys are automatically prefixed with the plugin ID
|
|
901
|
-
*/
|
|
902
|
-
function createKVAccess(optionsRepo, pluginId, settings = createSettingsAccess(optionsRepo, pluginId)) {
|
|
903
|
-
const prefix = `plugin:${pluginId}:`;
|
|
904
|
-
return {
|
|
905
|
-
async get(key) {
|
|
906
|
-
if (key.startsWith("settings:")) return settings.get(key.slice(9));
|
|
907
|
-
return optionsRepo.get(`${prefix}${key}`);
|
|
908
|
-
},
|
|
909
|
-
async getVersioned(key) {
|
|
910
|
-
assertStorageKey(key);
|
|
911
|
-
if (key.startsWith("settings:")) return settings.getVersioned(key.slice(9));
|
|
912
|
-
return optionsRepo.getVersioned(`${prefix}${key}`);
|
|
913
|
-
},
|
|
914
|
-
async compareAndSet(key, expectedRevision, value) {
|
|
915
|
-
assertStorageKey(key);
|
|
916
|
-
if (key.startsWith("settings:")) return settings.compareAndSet(key.slice(9), expectedRevision, value);
|
|
917
|
-
return optionsRepo.compareAndSet(`${prefix}${key}`, expectedRevision, value);
|
|
918
|
-
},
|
|
919
|
-
async compareAndDelete(key, expectedRevision) {
|
|
920
|
-
assertStorageKey(key);
|
|
921
|
-
if (key.startsWith("settings:")) return settings.compareAndDelete(key.slice(9), expectedRevision);
|
|
922
|
-
return optionsRepo.compareAndDelete(`${prefix}${key}`, expectedRevision);
|
|
923
|
-
},
|
|
924
|
-
async set(key, value) {
|
|
925
|
-
if (key.startsWith("settings:")) {
|
|
926
|
-
await settings.set(key.slice(9), value);
|
|
927
|
-
return;
|
|
928
|
-
}
|
|
929
|
-
await optionsRepo.set(`${prefix}${key}`, value);
|
|
930
|
-
},
|
|
931
|
-
async delete(key) {
|
|
932
|
-
if (key.startsWith("settings:")) return settings.delete(key.slice(9));
|
|
933
|
-
return optionsRepo.delete(`${prefix}${key}`);
|
|
934
|
-
},
|
|
935
|
-
async list(keyPrefix) {
|
|
936
|
-
const requestedPrefix = keyPrefix ?? "";
|
|
937
|
-
const includesSettings = "settings:".startsWith(requestedPrefix) || requestedPrefix.startsWith("settings:");
|
|
938
|
-
const fullPrefix = `${prefix}${requestedPrefix}`;
|
|
939
|
-
const entriesMap = await optionsRepo.getByPrefix(fullPrefix);
|
|
940
|
-
const result = [];
|
|
941
|
-
for (const [fullKey, value] of entriesMap) {
|
|
942
|
-
if (includesSettings && fullKey.startsWith(`${prefix}settings:`)) continue;
|
|
943
|
-
result.push({
|
|
944
|
-
key: fullKey.slice(prefix.length),
|
|
945
|
-
value
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
if (includesSettings) {
|
|
949
|
-
const settingPrefix = requestedPrefix.startsWith("settings:") ? requestedPrefix.slice(9) : "";
|
|
950
|
-
for (const entry of await settings.list(settingPrefix)) {
|
|
951
|
-
const key = `settings:${entry.key}`;
|
|
952
|
-
if (key.startsWith(requestedPrefix)) result.push({
|
|
953
|
-
key,
|
|
954
|
-
value: entry.value
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
return result;
|
|
959
|
-
}
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Create storage collection accessor for a plugin
|
|
964
|
-
* Wraps PluginStorageRepository with the v2 interface (no async iterators)
|
|
965
|
-
*/
|
|
966
|
-
function createStorageCollection(db, pluginId, collectionName, indexes) {
|
|
967
|
-
const repo = new PluginStorageRepository(db, pluginId, collectionName, indexes);
|
|
968
|
-
return {
|
|
969
|
-
get: (id) => repo.get(id),
|
|
970
|
-
getVersioned: (id) => repo.getVersioned(id),
|
|
971
|
-
compareAndSet: (id, expectedRevision, data) => repo.compareAndSet(id, expectedRevision, data),
|
|
972
|
-
compareAndDelete: (id, expectedRevision) => repo.compareAndDelete(id, expectedRevision),
|
|
973
|
-
put: (id, data) => repo.put(id, data),
|
|
974
|
-
delete: (id) => repo.delete(id),
|
|
975
|
-
exists: (id) => repo.exists(id),
|
|
976
|
-
getMany: (ids) => repo.getMany(ids),
|
|
977
|
-
putMany: (items) => repo.putMany(items),
|
|
978
|
-
deleteMany: (ids) => repo.deleteMany(ids),
|
|
979
|
-
count: (where) => repo.count(where),
|
|
980
|
-
updateIf: (id, updateArgs) => repo.updateIf(id, updateArgs),
|
|
981
|
-
async query(options) {
|
|
982
|
-
const result = await repo.query({
|
|
983
|
-
where: options?.where,
|
|
984
|
-
orderBy: options?.orderBy,
|
|
985
|
-
limit: options?.limit,
|
|
986
|
-
cursor: options?.cursor
|
|
987
|
-
});
|
|
988
|
-
return {
|
|
989
|
-
items: result.items,
|
|
990
|
-
cursor: result.cursor,
|
|
991
|
-
hasMore: result.hasMore
|
|
992
|
-
};
|
|
993
|
-
}
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
/**
|
|
997
|
-
* Create storage accessor with all declared collections
|
|
998
|
-
*/
|
|
999
|
-
function createStorageAccess(db, pluginId, storageConfig) {
|
|
1000
|
-
const storage = {};
|
|
1001
|
-
for (const [collectionName, config] of Object.entries(storageConfig)) storage[collectionName] = createStorageCollection(db, pluginId, collectionName, [...config.indexes, ...config.uniqueIndexes ?? []]);
|
|
1002
|
-
return storage;
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Extract `seo` from a plugin-supplied content write input and return both
|
|
1006
|
-
* parts. Mutates nothing — returns a new field map without the `seo` key.
|
|
1007
|
-
*/
|
|
1008
|
-
function splitSeoFromInput(input) {
|
|
1009
|
-
const { seo, ...fields } = input;
|
|
1010
|
-
if (seo !== void 0 && (seo === null || typeof seo !== "object" || Array.isArray(seo))) throw new Error("content.seo must be an object");
|
|
1011
|
-
return {
|
|
1012
|
-
fields,
|
|
1013
|
-
seo
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
/**
|
|
1017
|
-
* Reject writing SEO to a collection that does not have it enabled.
|
|
1018
|
-
* Matches the REST API behavior (VALIDATION_ERROR).
|
|
1019
|
-
*/
|
|
1020
|
-
async function assertSeoEnabled(seoRepo, collection, seo) {
|
|
1021
|
-
const hasSeo = await seoRepo.isEnabled(collection);
|
|
1022
|
-
if (seo !== void 0 && !hasSeo) throw new Error(`Collection "${collection}" does not have SEO enabled. Remove the seo field or enable SEO on this collection.`);
|
|
1023
|
-
return hasSeo;
|
|
1024
|
-
}
|
|
1025
|
-
/**
|
|
1026
|
-
* Parse the `collections` JSON column into a string array (`[]` on anything
|
|
1027
|
-
* else). Mirrors the guards in the Cloudflare/workerd bridges so an
|
|
1028
|
-
* in-process plugin degrades on malformed data instead of crashing.
|
|
1029
|
-
*/
|
|
1030
|
-
function parseCollectionsColumn(value) {
|
|
1031
|
-
if (!value) return [];
|
|
1032
|
-
try {
|
|
1033
|
-
const parsed = JSON.parse(value);
|
|
1034
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
1035
|
-
} catch {
|
|
1036
|
-
return [];
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
/** Map a repository `Taxonomy` row to the plugin-facing term shape. */
|
|
1040
|
-
function taxonomyToTermInfo(term) {
|
|
1041
|
-
return {
|
|
1042
|
-
id: term.id,
|
|
1043
|
-
taxonomy: term.name,
|
|
1044
|
-
slug: term.slug,
|
|
1045
|
-
label: term.label,
|
|
1046
|
-
parentId: term.parentId,
|
|
1047
|
-
data: term.data,
|
|
1048
|
-
locale: term.locale,
|
|
1049
|
-
translationGroup: term.translationGroup
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
function collectionToSchemaInfo(collection) {
|
|
1053
|
-
if (!collection) return null;
|
|
1054
|
-
return {
|
|
1055
|
-
slug: collection.slug,
|
|
1056
|
-
label: collection.label,
|
|
1057
|
-
labelSingular: collection.labelSingular ?? null,
|
|
1058
|
-
description: collection.description ?? null,
|
|
1059
|
-
supports: collection.supports,
|
|
1060
|
-
hasSeo: collection.hasSeo,
|
|
1061
|
-
titleField: collection.titleField ?? null,
|
|
1062
|
-
dateField: collection.dateField ?? null,
|
|
1063
|
-
urlPattern: collection.urlPattern ?? null,
|
|
1064
|
-
routable: collection.routable !== false,
|
|
1065
|
-
hidden: collection.hidden,
|
|
1066
|
-
fields: collection.fields.map((field) => ({
|
|
1067
|
-
slug: field.slug,
|
|
1068
|
-
label: field.label,
|
|
1069
|
-
type: field.type,
|
|
1070
|
-
required: field.required,
|
|
1071
|
-
unique: field.unique,
|
|
1072
|
-
...field.defaultValue === void 0 ? {} : { default: field.defaultValue },
|
|
1073
|
-
...field.validation === void 0 ? {} : { validation: field.validation },
|
|
1074
|
-
...field.widget === void 0 ? {} : { widget: field.widget },
|
|
1075
|
-
...field.options === void 0 ? {} : { options: field.options },
|
|
1076
|
-
searchable: field.searchable,
|
|
1077
|
-
indexed: field.indexed,
|
|
1078
|
-
translatable: field.translatable,
|
|
1079
|
-
sortOrder: field.sortOrder
|
|
1080
|
-
}))
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
|
-
function createSchemaAccess(db) {
|
|
1084
|
-
const registry = new SchemaRegistry(db);
|
|
1085
|
-
return {
|
|
1086
|
-
async listCollections() {
|
|
1087
|
-
const collections = [];
|
|
1088
|
-
for (const collection of await registry.listCollectionsWithFields()) {
|
|
1089
|
-
const info = collectionToSchemaInfo(collection);
|
|
1090
|
-
if (info) collections.push(info);
|
|
1091
|
-
}
|
|
1092
|
-
return collections;
|
|
1093
|
-
},
|
|
1094
|
-
async getCollection(slug) {
|
|
1095
|
-
return collectionToSchemaInfo(await registry.getCollectionWithFields(slug));
|
|
1096
|
-
}
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
/**
|
|
1100
|
-
* Create read-only taxonomy access (gated on `taxonomies:read`).
|
|
1101
|
-
*/
|
|
1102
|
-
function createTaxonomyAccess(db) {
|
|
1103
|
-
const taxonomyRepo = new TaxonomyRepository(db);
|
|
1104
|
-
return {
|
|
1105
|
-
async getAll(options) {
|
|
1106
|
-
let query = selectTaxonomyDefs(db);
|
|
1107
|
-
if (options?.locale !== void 0) query = query.where("d.locale", "=", options.locale);
|
|
1108
|
-
return (await query.orderBy("d.name", "asc").execute()).map((row) => ({
|
|
1109
|
-
name: row.name,
|
|
1110
|
-
label: row.label,
|
|
1111
|
-
labelSingular: row.label_singular,
|
|
1112
|
-
hierarchical: row.hierarchical === 1,
|
|
1113
|
-
collections: parseCollectionsColumn(row.collections),
|
|
1114
|
-
locale: row.locale
|
|
1115
|
-
}));
|
|
1116
|
-
},
|
|
1117
|
-
async getTerms(taxonomy, options) {
|
|
1118
|
-
return (await taxonomyRepo.findByName(taxonomy, { locale: options?.locale })).map(taxonomyToTermInfo);
|
|
1119
|
-
},
|
|
1120
|
-
async getEntryTerms(collection, entryId, options) {
|
|
1121
|
-
return (await taxonomyRepo.getTermsForEntry(collection, entryId, options?.taxonomy, options?.locale)).map(taxonomyToTermInfo);
|
|
1122
|
-
}
|
|
1123
|
-
};
|
|
1124
|
-
}
|
|
1125
|
-
function toPluginComment(comment) {
|
|
1126
|
-
if (comment.status === "trash") throw new Error("Trashed comments are not plugin-readable");
|
|
1127
|
-
return {
|
|
1128
|
-
id: comment.id,
|
|
1129
|
-
collection: comment.collection,
|
|
1130
|
-
contentId: comment.contentId,
|
|
1131
|
-
parentId: comment.parentId,
|
|
1132
|
-
authorName: comment.authorName,
|
|
1133
|
-
authorEmail: comment.authorEmail,
|
|
1134
|
-
body: comment.body,
|
|
1135
|
-
status: comment.status,
|
|
1136
|
-
ipHash: comment.ipHash,
|
|
1137
|
-
userAgent: comment.userAgent,
|
|
1138
|
-
moderationMetadata: comment.moderationMetadata,
|
|
1139
|
-
createdAt: comment.createdAt,
|
|
1140
|
-
updatedAt: comment.updatedAt
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
function createCommentAccess(db, moderate) {
|
|
1144
|
-
const repo = new CommentRepository(db);
|
|
1145
|
-
return {
|
|
1146
|
-
async get(id) {
|
|
1147
|
-
const comment = await repo.findById(id);
|
|
1148
|
-
return !comment || comment.status === "trash" ? null : toPluginComment(comment);
|
|
1149
|
-
},
|
|
1150
|
-
async list(options = {}) {
|
|
1151
|
-
const result = await repo.findForPlugin(options);
|
|
1152
|
-
return {
|
|
1153
|
-
items: result.items.map(toPluginComment),
|
|
1154
|
-
cursor: result.nextCursor,
|
|
1155
|
-
hasMore: result.nextCursor !== void 0
|
|
1156
|
-
};
|
|
1157
|
-
},
|
|
1158
|
-
count: (options) => repo.countForPlugin(options),
|
|
1159
|
-
...moderate ? { setStatus: (id, status, options) => moderate(id, status, options.expectedStatus) } : {}
|
|
1160
|
-
};
|
|
1161
|
-
}
|
|
1162
|
-
var RedirectAccessError = class extends Error {
|
|
1163
|
-
name = "RedirectAccessError";
|
|
1164
|
-
constructor(code, message) {
|
|
1165
|
-
super(message);
|
|
1166
|
-
this.code = code;
|
|
1167
|
-
}
|
|
1168
|
-
};
|
|
1169
|
-
const REDIRECT_REVISION_PREFIX = "r1.";
|
|
1170
|
-
const BASE64_PADDING_RE = /=+$/;
|
|
1171
|
-
function encodeRedirectRevision(id, revision) {
|
|
1172
|
-
const payload = `${id}\0${revision}`;
|
|
1173
|
-
return `${REDIRECT_REVISION_PREFIX}${btoa(payload).replaceAll("+", "-").replaceAll("/", "_").replace(BASE64_PADDING_RE, "")}`;
|
|
1174
|
-
}
|
|
1175
|
-
function decodeRedirectRevision(id, revision) {
|
|
1176
|
-
if (typeof revision !== "string" || !revision.startsWith(REDIRECT_REVISION_PREFIX)) throw new RedirectAccessError("INVALID_PRECONDITION", "Invalid redirect revision");
|
|
1177
|
-
try {
|
|
1178
|
-
const encoded = revision.slice(3).replaceAll("-", "+").replaceAll("_", "/");
|
|
1179
|
-
const padded = encoded.padEnd(Math.ceil(encoded.length / 4) * 4, "=");
|
|
1180
|
-
const [revisionId, updatedAt, extra] = atob(padded).split("\0");
|
|
1181
|
-
if (revisionId !== id || !updatedAt || extra !== void 0) throw new Error("invalid");
|
|
1182
|
-
return updatedAt;
|
|
1183
|
-
} catch (error) {
|
|
1184
|
-
if (error instanceof RedirectAccessError) throw error;
|
|
1185
|
-
throw new RedirectAccessError("INVALID_PRECONDITION", "Invalid redirect revision");
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
function toRedirectInfo(redirect) {
|
|
1189
|
-
return {
|
|
1190
|
-
...redirect,
|
|
1191
|
-
type: redirect.type
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
function toVersionedRedirect(record) {
|
|
1195
|
-
return {
|
|
1196
|
-
redirect: toRedirectInfo(record.redirect),
|
|
1197
|
-
_rev: encodeRedirectRevision(record.redirect.id, record.configRevision)
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
async function readVersionedRedirect(repo, id) {
|
|
1201
|
-
const record = await repo.findVersionedById(id);
|
|
1202
|
-
return record ? toVersionedRedirect(record) : null;
|
|
1203
|
-
}
|
|
1204
|
-
function throwRedirectResult(error) {
|
|
1205
|
-
throw new RedirectAccessError(error.code, error.message);
|
|
1206
|
-
}
|
|
1207
|
-
function assertNoAutomaticRedirectMarker(input) {
|
|
1208
|
-
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new RedirectAccessError("VALIDATION_ERROR", "Redirect input must be an object");
|
|
1209
|
-
if (Object.hasOwn(input, "auto")) throw new RedirectAccessError("VALIDATION_ERROR", "The automatic redirect marker is managed by EmDash");
|
|
1210
|
-
}
|
|
1211
|
-
function createRedirectAccess(db, writable = false) {
|
|
1212
|
-
const repo = new RedirectRepository(db);
|
|
1213
|
-
const readAccess = {
|
|
1214
|
-
async list(options = {}) {
|
|
1215
|
-
const result = await handleRedirectList(db, options);
|
|
1216
|
-
if (!result.success) return throwRedirectResult(result.error);
|
|
1217
|
-
return {
|
|
1218
|
-
items: result.data.items.map(toRedirectInfo),
|
|
1219
|
-
cursor: result.data.nextCursor,
|
|
1220
|
-
hasMore: result.data.nextCursor !== void 0
|
|
1221
|
-
};
|
|
1222
|
-
},
|
|
1223
|
-
get: (id) => readVersionedRedirect(repo, id)
|
|
1224
|
-
};
|
|
1225
|
-
if (!writable) return readAccess;
|
|
1226
|
-
return {
|
|
1227
|
-
...readAccess,
|
|
1228
|
-
async create(input) {
|
|
1229
|
-
assertNoAutomaticRedirectMarker(input);
|
|
1230
|
-
const parsed = createRedirectBody.safeParse(input);
|
|
1231
|
-
if (!parsed.success) throw new RedirectAccessError("VALIDATION_ERROR", parsed.error.issues[0]?.message ?? "Invalid redirect");
|
|
1232
|
-
const result = await handleRedirectCreate(db, parsed.data);
|
|
1233
|
-
if (!result.success) return throwRedirectResult(result.error);
|
|
1234
|
-
const current = await readVersionedRedirect(repo, result.data.id);
|
|
1235
|
-
if (!current) throw new RedirectAccessError("NOT_FOUND", "Created redirect not found");
|
|
1236
|
-
return current;
|
|
1237
|
-
},
|
|
1238
|
-
async update(id, input) {
|
|
1239
|
-
assertNoAutomaticRedirectMarker(input);
|
|
1240
|
-
const { _rev, ...patch } = input;
|
|
1241
|
-
const expectedRevision = decodeRedirectRevision(id, _rev);
|
|
1242
|
-
const parsed = updateRedirectBody.safeParse(patch);
|
|
1243
|
-
if (!parsed.success) throw new RedirectAccessError("VALIDATION_ERROR", parsed.error.issues[0]?.message ?? "Invalid redirect");
|
|
1244
|
-
const result = await handleRedirectUpdate(db, id, parsed.data, { expectedRevision });
|
|
1245
|
-
if (!result.success) return throwRedirectResult(result.error);
|
|
1246
|
-
const current = await readVersionedRedirect(repo, result.data.id);
|
|
1247
|
-
if (!current) throw new RedirectAccessError("NOT_FOUND", "Updated redirect not found");
|
|
1248
|
-
return current;
|
|
1249
|
-
},
|
|
1250
|
-
async delete(id, options) {
|
|
1251
|
-
if (typeof options !== "object" || options === null) throw new RedirectAccessError("INVALID_PRECONDITION", "Invalid redirect revision");
|
|
1252
|
-
const result = await handleRedirectDelete(db, id, { expectedRevision: decodeRedirectRevision(id, options._rev) });
|
|
1253
|
-
if (!result.success) return throwRedirectResult(result.error);
|
|
1254
|
-
return result.data.deleted;
|
|
1255
|
-
}
|
|
1256
|
-
};
|
|
1257
|
-
}
|
|
1258
|
-
const MAX_TAXONOMY_DELTA_TERMS = 64;
|
|
1259
|
-
function taxonomyAccessError(code, message) {
|
|
1260
|
-
return Object.assign(new Error(message), { code });
|
|
1261
|
-
}
|
|
1262
|
-
async function resolveTaxonomyDelta(db, collection, entryId, taxonomy, termIds) {
|
|
1263
|
-
if (termIds.length > MAX_TAXONOMY_DELTA_TERMS) throw taxonomyAccessError("VALIDATION_ERROR", `A taxonomy assignment delta can contain at most ${MAX_TAXONOMY_DELTA_TERMS} term IDs`);
|
|
1264
|
-
if (termIds.some((id) => typeof id !== "string" || id.length === 0)) throw taxonomyAccessError("VALIDATION_ERROR", "Taxonomy term IDs must be non-empty strings");
|
|
1265
|
-
const structure = await findTaxonomyStructure(db, taxonomy);
|
|
1266
|
-
if (!structure) throw taxonomyAccessError("NOT_FOUND", `Taxonomy '${taxonomy}' not found`);
|
|
1267
|
-
if (!structure.collections.includes(collection)) throw taxonomyAccessError("VALIDATION_ERROR", `Taxonomy '${taxonomy}' is not attached to collection '${collection}'`);
|
|
1268
|
-
const entry = await new ContentRepository(db).findById(collection, entryId);
|
|
1269
|
-
if (!entry) throw taxonomyAccessError("NOT_FOUND", `Content entry '${entryId}' not found in '${collection}'`);
|
|
1270
|
-
const repo = new TaxonomyRepository(db);
|
|
1271
|
-
const groups = [];
|
|
1272
|
-
for (const id of new Set(termIds)) {
|
|
1273
|
-
const term = await repo.findByIdOrTranslationGroup(id);
|
|
1274
|
-
if (!term) throw taxonomyAccessError("NOT_FOUND", `Taxonomy term '${id}' not found`);
|
|
1275
|
-
if (term.name !== taxonomy) throw taxonomyAccessError("VALIDATION_ERROR", `Taxonomy term '${id}' belongs to '${term.name}', not '${taxonomy}'`);
|
|
1276
|
-
groups.push(term.translationGroup ?? term.id);
|
|
1277
|
-
}
|
|
1278
|
-
return {
|
|
1279
|
-
repo,
|
|
1280
|
-
groups,
|
|
1281
|
-
locale: entry.locale ?? getI18nConfig()?.defaultLocale ?? "en"
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
async function readResolvedEntryTerms(repo, collection, entryId, taxonomy, locale) {
|
|
1285
|
-
const defaultLocale = getI18nConfig()?.defaultLocale ?? locale;
|
|
1286
|
-
return (await repo.getTermAssignmentsForEntry(collection, entryId, taxonomy, locale, defaultLocale)).flatMap(({ term }) => term ? [taxonomyToTermInfo(term)] : []);
|
|
1287
|
-
}
|
|
1288
|
-
function createTaxonomyAccessWithWrite(db) {
|
|
1289
|
-
return {
|
|
1290
|
-
...createTaxonomyAccess(db),
|
|
1291
|
-
async createTerm(taxonomy, input) {
|
|
1292
|
-
const result = await handleTermCreate(db, taxonomy, input);
|
|
1293
|
-
if (!result.success) throw taxonomyAccessError(result.error.code, result.error.message);
|
|
1294
|
-
const { term } = result.data;
|
|
1295
|
-
return {
|
|
1296
|
-
id: term.id,
|
|
1297
|
-
taxonomy: term.name,
|
|
1298
|
-
slug: term.slug,
|
|
1299
|
-
label: term.label,
|
|
1300
|
-
parentId: term.parentId,
|
|
1301
|
-
data: term.description ? { description: term.description } : null,
|
|
1302
|
-
locale: term.locale,
|
|
1303
|
-
translationGroup: term.translationGroup
|
|
1304
|
-
};
|
|
1305
|
-
},
|
|
1306
|
-
async addEntryTerms(collection, entryId, taxonomy, termIds) {
|
|
1307
|
-
const { repo, groups, locale } = await resolveTaxonomyDelta(db, collection, entryId, taxonomy, termIds);
|
|
1308
|
-
await repo.attachGroupsToEntry(collection, entryId, groups);
|
|
1309
|
-
return readResolvedEntryTerms(repo, collection, entryId, taxonomy, locale);
|
|
1310
|
-
},
|
|
1311
|
-
async removeEntryTerms(collection, entryId, taxonomy, termIds) {
|
|
1312
|
-
const { repo, groups, locale } = await resolveTaxonomyDelta(db, collection, entryId, taxonomy, termIds);
|
|
1313
|
-
await repo.detachGroupsFromEntry(collection, entryId, groups);
|
|
1314
|
-
return readResolvedEntryTerms(repo, collection, entryId, taxonomy, locale);
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
async function afterContentWrite(recordWrite) {
|
|
1319
|
-
if (typeof recordWrite === "function") await recordWrite();
|
|
1320
|
-
}
|
|
1321
|
-
/**
|
|
1322
|
-
* Create full content access with write operations.
|
|
1323
|
-
*
|
|
1324
|
-
* `create` and `update` accept a reserved `seo` key in their `data`
|
|
1325
|
-
* argument. When present, it is routed to the core SEO panel
|
|
1326
|
-
* (`_emdash_seo`) via `SeoRepository.upsert`, in the same transaction as
|
|
1327
|
-
* the content write. The returned `ContentItem.seo` reflects the resulting
|
|
1328
|
-
* SEO state for SEO-enabled collections.
|
|
1329
|
-
*/
|
|
1330
|
-
function createContentAccessWithWrite(db, beforeContentWrite, accessOptions, contentCreate) {
|
|
1331
|
-
return {
|
|
1332
|
-
...createContentAccess(db, accessOptions),
|
|
1333
|
-
async create(collection, data, options) {
|
|
1334
|
-
const locale = resolveContentCreateLocale(options?.locale);
|
|
1335
|
-
const recordWrite = await beforeContentWrite?.();
|
|
1336
|
-
if (contentCreate) {
|
|
1337
|
-
const created = await contentCreate({
|
|
1338
|
-
collection,
|
|
1339
|
-
input: data,
|
|
1340
|
-
options: {
|
|
1341
|
-
...options,
|
|
1342
|
-
locale
|
|
1343
|
-
}
|
|
1344
|
-
});
|
|
1345
|
-
await afterContentWrite(recordWrite);
|
|
1346
|
-
return created;
|
|
1347
|
-
}
|
|
1348
|
-
const { fields, seo } = splitSeoFromInput(data);
|
|
1349
|
-
let contentMutated = false;
|
|
1350
|
-
try {
|
|
1351
|
-
const created = await withTransaction(db, async (trx) => {
|
|
1352
|
-
const trxContentRepo = new ContentRepository(trx);
|
|
1353
|
-
const trxSeoRepo = new SeoRepository(trx);
|
|
1354
|
-
const hasSeo = await assertSeoEnabled(trxSeoRepo, collection, seo);
|
|
1355
|
-
const item = await trxContentRepo.create({
|
|
1356
|
-
type: collection,
|
|
1357
|
-
data: fields,
|
|
1358
|
-
locale,
|
|
1359
|
-
translationOf: options?.translationOf
|
|
1360
|
-
});
|
|
1361
|
-
contentMutated = true;
|
|
1362
|
-
const result = {
|
|
1363
|
-
id: item.id,
|
|
1364
|
-
type: item.type,
|
|
1365
|
-
slug: item.slug,
|
|
1366
|
-
status: item.status,
|
|
1367
|
-
data: item.data,
|
|
1368
|
-
createdAt: item.createdAt,
|
|
1369
|
-
updatedAt: item.updatedAt,
|
|
1370
|
-
locale: item.locale,
|
|
1371
|
-
publishedAt: item.publishedAt,
|
|
1372
|
-
scheduledAt: item.scheduledAt
|
|
1373
|
-
};
|
|
1374
|
-
if (hasSeo) result.seo = seo !== void 0 ? await trxSeoRepo.upsert(collection, item.id, seo) : await trxSeoRepo.get(collection, item.id);
|
|
1375
|
-
return result;
|
|
1376
|
-
});
|
|
1377
|
-
await markContentMediaUsageCollectionStaleSafely(db, collection, "CONTENT_USAGE_STALE");
|
|
1378
|
-
await afterContentWrite(recordWrite);
|
|
1379
|
-
return created;
|
|
1380
|
-
} catch (error) {
|
|
1381
|
-
if (contentMutated) await markContentMediaUsageCollectionStaleSafely(db, collection, "CONTENT_USAGE_STALE");
|
|
1382
|
-
throw error;
|
|
1383
|
-
}
|
|
1384
|
-
},
|
|
1385
|
-
async update(collection, id, data) {
|
|
1386
|
-
const recordWrite = await beforeContentWrite?.();
|
|
1387
|
-
const { fields, seo } = splitSeoFromInput(data);
|
|
1388
|
-
const hasFieldUpdates = Object.keys(fields).length > 0;
|
|
1389
|
-
let contentMutated = false;
|
|
1390
|
-
try {
|
|
1391
|
-
const updated = await withTransaction(db, async (trx) => {
|
|
1392
|
-
const trxContentRepo = new ContentRepository(trx);
|
|
1393
|
-
const trxSeoRepo = new SeoRepository(trx);
|
|
1394
|
-
const hasSeo = await assertSeoEnabled(trxSeoRepo, collection, seo);
|
|
1395
|
-
const item = hasFieldUpdates ? await trxContentRepo.updateDraftAware(collection, id, { data: fields }) : await (async () => {
|
|
1396
|
-
const existing = await trxContentRepo.findById(collection, id);
|
|
1397
|
-
if (!existing) throw new Error("Content not found");
|
|
1398
|
-
return existing;
|
|
1399
|
-
})();
|
|
1400
|
-
if (hasFieldUpdates) contentMutated = true;
|
|
1401
|
-
const result = {
|
|
1402
|
-
id: item.id,
|
|
1403
|
-
type: item.type,
|
|
1404
|
-
slug: item.slug,
|
|
1405
|
-
status: item.status,
|
|
1406
|
-
data: item.data,
|
|
1407
|
-
createdAt: item.createdAt,
|
|
1408
|
-
updatedAt: item.updatedAt,
|
|
1409
|
-
locale: item.locale,
|
|
1410
|
-
publishedAt: item.publishedAt,
|
|
1411
|
-
scheduledAt: item.scheduledAt
|
|
1412
|
-
};
|
|
1413
|
-
if (hasSeo) result.seo = seo !== void 0 ? await trxSeoRepo.upsert(collection, item.id, seo) : await trxSeoRepo.get(collection, item.id);
|
|
1414
|
-
return result;
|
|
1415
|
-
});
|
|
1416
|
-
if (hasFieldUpdates) await markContentMediaUsageCollectionStaleSafely(db, collection, "CONTENT_USAGE_STALE");
|
|
1417
|
-
await afterContentWrite(recordWrite);
|
|
1418
|
-
return updated;
|
|
1419
|
-
} catch (error) {
|
|
1420
|
-
if (contentMutated) await markContentMediaUsageCollectionStaleSafely(db, collection, "CONTENT_USAGE_STALE");
|
|
1421
|
-
throw error;
|
|
1422
|
-
}
|
|
1423
|
-
},
|
|
1424
|
-
async delete(collection, id) {
|
|
1425
|
-
const recordWrite = await beforeContentWrite?.();
|
|
1426
|
-
const deleted = await new ContentRepository(db).delete(collection, id);
|
|
1427
|
-
if (deleted) {
|
|
1428
|
-
await new EntryLockRepository(db).releaseEntry(collection, id);
|
|
1429
|
-
await markContentMediaUsageCollectionStaleSafely(db, collection, "CONTENT_USAGE_STALE");
|
|
1430
|
-
await afterContentWrite(recordWrite);
|
|
1431
|
-
}
|
|
1432
|
-
return deleted;
|
|
1433
|
-
}
|
|
1434
|
-
};
|
|
1435
|
-
}
|
|
1436
|
-
/**
|
|
1437
|
-
* Create read-only media access
|
|
1438
|
-
*/
|
|
1439
|
-
function createMediaAccess(db) {
|
|
1440
|
-
const mediaRepo = new MediaRepository(db);
|
|
1441
|
-
return {
|
|
1442
|
-
async get(id) {
|
|
1443
|
-
const item = await mediaRepo.findById(id);
|
|
1444
|
-
return item?.status === "ready" ? toPluginMediaItem(item) : null;
|
|
1445
|
-
},
|
|
1446
|
-
async list(options) {
|
|
1447
|
-
const result = await mediaRepo.findMany({
|
|
1448
|
-
limit: options?.limit ?? 50,
|
|
1449
|
-
cursor: options?.cursor,
|
|
1450
|
-
mimeType: options?.mimeType
|
|
1451
|
-
});
|
|
1452
|
-
return {
|
|
1453
|
-
items: result.items.map(toPluginMediaItem),
|
|
1454
|
-
cursor: result.nextCursor,
|
|
1455
|
-
hasMore: !!result.nextCursor
|
|
1456
|
-
};
|
|
1457
|
-
}
|
|
1458
|
-
};
|
|
1459
|
-
}
|
|
1460
|
-
function mediaReadDenied() {
|
|
1461
|
-
throw new Error("Missing capability: media:read");
|
|
1462
|
-
}
|
|
1463
|
-
function createBlockedMediaReadAccess() {
|
|
1464
|
-
return {
|
|
1465
|
-
get: async () => mediaReadDenied(),
|
|
1466
|
-
list: async () => mediaReadDenied()
|
|
1467
|
-
};
|
|
1468
|
-
}
|
|
1469
|
-
function allowedUploadType(contentType) {
|
|
1470
|
-
if (!CONTENT_TYPE_RE.test(contentType)) throw PluginRouteError.badRequest("Invalid content type");
|
|
1471
|
-
const mimeType = normalizeMime(contentType);
|
|
1472
|
-
if (!matchesMimeAllowlist(mimeType, GLOBAL_UPLOAD_ALLOWLIST)) throw new PluginRouteError("UNSUPPORTED_MEDIA_TYPE", "File type not allowed", 415);
|
|
1473
|
-
return mimeType;
|
|
1474
|
-
}
|
|
1475
|
-
function uploadStorageKey(filename, mimeType) {
|
|
1476
|
-
const keyPrefix = ulid();
|
|
1477
|
-
const basename = filename.split("/").pop() ?? filename;
|
|
1478
|
-
const dotIdx = basename.lastIndexOf(".");
|
|
1479
|
-
const nameExt = dotIdx > 0 ? basename.slice(dotIdx + 1).toLowerCase() : "";
|
|
1480
|
-
const nameType = mime.getType(nameExt);
|
|
1481
|
-
const nameExtAllowed = nameType !== null && matchesMimeAllowlist(nameType, GLOBAL_UPLOAD_ALLOWLIST);
|
|
1482
|
-
const ext = nameType === mimeType ? nameExt : mime.getExtension(mimeType) ?? (nameExtAllowed ? nameExt : null);
|
|
1483
|
-
return {
|
|
1484
|
-
basename,
|
|
1485
|
-
storageKey: ext ? `${keyPrefix}.${ext}` : keyPrefix
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
/**
|
|
1489
|
-
* Create full media access with write operations.
|
|
1490
|
-
*
|
|
1491
|
-
* `getUploadUrlFn` is optional: when omitted, `getUploadUrl()` is derived from
|
|
1492
|
-
* `storage` (create a pending record + a signed PUT URL), mirroring the REST
|
|
1493
|
-
* `/_emdash/api/media/upload-url` endpoint. `upload()` only needs `storage`.
|
|
1494
|
-
* If storage is not provided, both throw at call time.
|
|
1495
|
-
*/
|
|
1496
|
-
function createMediaAccessWithWrite(db, getUploadUrlFn, storage) {
|
|
1497
|
-
const mediaRepo = new MediaRepository(db);
|
|
1498
|
-
const readAccess = createMediaAccess(db);
|
|
1499
|
-
const getUploadUrl = getUploadUrlFn ? async (filename, contentType) => getUploadUrlFn(filename, allowedUploadType(contentType)) : async (filename, contentType) => {
|
|
1500
|
-
if (!storage) throw new Error("Media getUploadUrl() requires a storage backend. Configure storage in PluginContextFactoryOptions.");
|
|
1501
|
-
const mimeType = allowedUploadType(contentType);
|
|
1502
|
-
const { basename, storageKey } = uploadStorageKey(filename, mimeType);
|
|
1503
|
-
const media = await mediaRepo.createPending({
|
|
1504
|
-
filename: basename,
|
|
1505
|
-
mimeType,
|
|
1506
|
-
storageKey
|
|
1507
|
-
});
|
|
1508
|
-
return {
|
|
1509
|
-
uploadUrl: (await storage.getSignedUploadUrl({
|
|
1510
|
-
key: storageKey,
|
|
1511
|
-
contentType: mimeType,
|
|
1512
|
-
expiresIn: 3600
|
|
1513
|
-
})).url,
|
|
1514
|
-
mediaId: media.id
|
|
1515
|
-
};
|
|
1516
|
-
};
|
|
1517
|
-
return {
|
|
1518
|
-
...readAccess,
|
|
1519
|
-
getUploadUrl,
|
|
1520
|
-
async upload(filename, contentType, bytes) {
|
|
1521
|
-
if (!storage) throw new Error("Media upload() requires a storage backend. Configure storage in PluginContextFactoryOptions.");
|
|
1522
|
-
const mimeType = allowedUploadType(contentType);
|
|
1523
|
-
const { basename, storageKey } = uploadStorageKey(filename, mimeType);
|
|
1524
|
-
await storage.upload({
|
|
1525
|
-
key: storageKey,
|
|
1526
|
-
body: new Uint8Array(bytes),
|
|
1527
|
-
contentType: mimeType
|
|
1528
|
-
});
|
|
1529
|
-
const enriched = await enrichImageMetadata(new Uint8Array(bytes), mimeType);
|
|
1530
|
-
let media;
|
|
1531
|
-
try {
|
|
1532
|
-
media = await mediaRepo.create({
|
|
1533
|
-
filename: basename,
|
|
1534
|
-
mimeType,
|
|
1535
|
-
size: bytes.byteLength,
|
|
1536
|
-
storageKey,
|
|
1537
|
-
status: "ready",
|
|
1538
|
-
width: enriched.width,
|
|
1539
|
-
height: enriched.height,
|
|
1540
|
-
blurhash: enriched.blurhash,
|
|
1541
|
-
dominantColor: enriched.dominantColor
|
|
1542
|
-
});
|
|
1543
|
-
} catch (error) {
|
|
1544
|
-
try {
|
|
1545
|
-
await storage.delete(storageKey);
|
|
1546
|
-
} catch {}
|
|
1547
|
-
throw error;
|
|
1548
|
-
}
|
|
1549
|
-
return {
|
|
1550
|
-
mediaId: media.id,
|
|
1551
|
-
storageKey,
|
|
1552
|
-
url: `/_emdash/api/media/file/${storageKey}`
|
|
1553
|
-
};
|
|
1554
|
-
},
|
|
1555
|
-
async delete(id) {
|
|
1556
|
-
const result = await handleMediaDelete(db, id, storage);
|
|
1557
|
-
if (!result.success) {
|
|
1558
|
-
if (result.error.code === "NOT_FOUND") return false;
|
|
1559
|
-
throw new Error(result.error.message);
|
|
1560
|
-
}
|
|
1561
|
-
invalidateSiteSettingsCache();
|
|
1562
|
-
return true;
|
|
1563
|
-
}
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
/** Maximum number of redirects to follow in plugin HTTP access */
|
|
1567
|
-
const MAX_PLUGIN_REDIRECTS = 5;
|
|
1568
|
-
function stripTrailingDots(value) {
|
|
1569
|
-
let end = value.length;
|
|
1570
|
-
while (end > 0 && value.charCodeAt(end - 1) === 46) end--;
|
|
1571
|
-
return end === value.length ? value : value.slice(0, end);
|
|
1572
|
-
}
|
|
1573
|
-
/**
|
|
1574
|
-
* Check if a hostname matches any pattern in the allowed list.
|
|
1575
|
-
* Patterns: "*" matches all, "*.example.com" matches subdomains AND bare "example.com",
|
|
1576
|
-
* "api.example.com" matches exactly.
|
|
1577
|
-
*/
|
|
1578
|
-
function isHostAllowed(host, allowedHosts) {
|
|
1579
|
-
const normalizedHost = stripTrailingDots(host.toLowerCase());
|
|
1580
|
-
return allowedHosts.some((pattern) => {
|
|
1581
|
-
const normalizedPattern = stripTrailingDots(pattern.toLowerCase());
|
|
1582
|
-
if (normalizedPattern === "*") return true;
|
|
1583
|
-
if (normalizedPattern.startsWith("*.")) {
|
|
1584
|
-
const suffix = normalizedPattern.slice(1);
|
|
1585
|
-
return normalizedHost.endsWith(suffix) || normalizedHost === normalizedPattern.slice(2);
|
|
1586
|
-
}
|
|
1587
|
-
return normalizedHost === normalizedPattern;
|
|
1588
|
-
});
|
|
1589
|
-
}
|
|
1590
|
-
function tryParsePluginHttpTarget(url) {
|
|
1591
|
-
try {
|
|
1592
|
-
return new URL(url);
|
|
1593
|
-
} catch {
|
|
1594
|
-
return null;
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
async function validatePluginHttpTarget(pluginId, url) {
|
|
1598
|
-
try {
|
|
1599
|
-
return await resolveAndValidateExternalUrl(url);
|
|
1600
|
-
} catch (error) {
|
|
1601
|
-
const message = error instanceof SsrfError ? error.message : "SSRF validation failed";
|
|
1602
|
-
const target = tryParsePluginHttpTarget(url);
|
|
1603
|
-
throw new Error(`Plugin "${pluginId}": blocked fetch to "${target ? target.hostname : "invalid URL"}": ${message}`, { cause: error });
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
/**
|
|
1607
|
-
* Create HTTP access with host validation and SSRF protection.
|
|
1608
|
-
*
|
|
1609
|
-
* Uses redirect: "manual" to re-validate each redirect target before dispatch.
|
|
1610
|
-
*/
|
|
1611
|
-
function createHttpAccess(pluginId, allowedHosts, fetchImpl = globalThis.fetch) {
|
|
1612
|
-
return { async fetch(url, init) {
|
|
1613
|
-
if (allowedHosts.length === 0) throw new Error(`Plugin "${pluginId}" has no allowed hosts configured. Add hosts to the plugin's allowedHosts array to enable HTTP requests.`);
|
|
1614
|
-
let currentUrl = url;
|
|
1615
|
-
let currentInit = init;
|
|
1616
|
-
let requestBuffered = false;
|
|
1617
|
-
let redirected = false;
|
|
1618
|
-
for (let i = 0; i <= MAX_PLUGIN_REDIRECTS; i++) {
|
|
1619
|
-
const target = tryParsePluginHttpTarget(currentUrl);
|
|
1620
|
-
if (target && !isHostAllowed(target.hostname, allowedHosts)) throw new Error(`Plugin "${pluginId}" is not allowed to fetch from host "${target.hostname}". Allowed hosts: ${allowedHosts.join(", ")}`);
|
|
1621
|
-
await validatePluginHttpTarget(pluginId, currentUrl);
|
|
1622
|
-
if (!requestBuffered) {
|
|
1623
|
-
currentInit = await bufferPluginHttpRequest(currentInit);
|
|
1624
|
-
requestBuffered = true;
|
|
1625
|
-
}
|
|
1626
|
-
const response = await fetchImpl(currentUrl, {
|
|
1627
|
-
...currentInit,
|
|
1628
|
-
redirect: "manual"
|
|
1629
|
-
});
|
|
1630
|
-
const location = response.headers.get("Location");
|
|
1631
|
-
if (location === null) return pluginHttpResponseFromWire(await pluginHttpResponseToWire(response, currentUrl, redirected));
|
|
1632
|
-
const redirectAction = pluginHttpRedirectAction(response.status, true, currentInit);
|
|
1633
|
-
if (redirectAction === "return") return pluginHttpResponseFromWire(await pluginHttpResponseToWire(response, currentUrl, redirected));
|
|
1634
|
-
await response.body?.cancel();
|
|
1635
|
-
if (redirectAction === "error") throw new Error(`Plugin "${pluginId}": redirect mode is "error"`);
|
|
1636
|
-
const previousOrigin = new URL(currentUrl).origin;
|
|
1637
|
-
currentUrl = new URL(location, currentUrl).href;
|
|
1638
|
-
redirected = true;
|
|
1639
|
-
const nextOrigin = new URL(currentUrl).origin;
|
|
1640
|
-
currentInit = rewritePluginHttpRedirect(response.status, currentInit);
|
|
1641
|
-
if (previousOrigin !== nextOrigin && currentInit) currentInit = stripCredentialHeaders(currentInit);
|
|
1642
|
-
}
|
|
1643
|
-
throw new Error(`Plugin "${pluginId}": too many redirects (max ${MAX_PLUGIN_REDIRECTS})`);
|
|
1644
|
-
} };
|
|
1645
|
-
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Create unrestricted HTTP access (for plugins with network:request:unrestricted capability).
|
|
1648
|
-
* No host validation, but applies SSRF protection on redirect targets to
|
|
1649
|
-
* prevent plugins from being tricked into reaching internal services.
|
|
1650
|
-
*/
|
|
1651
|
-
function createUnrestrictedHttpAccess(pluginId, fetchImpl = globalThis.fetch) {
|
|
1652
|
-
return { async fetch(url, init) {
|
|
1653
|
-
let currentUrl = url;
|
|
1654
|
-
let currentInit = init;
|
|
1655
|
-
let requestBuffered = false;
|
|
1656
|
-
let redirected = false;
|
|
1657
|
-
for (let i = 0; i <= MAX_PLUGIN_REDIRECTS; i++) {
|
|
1658
|
-
await validatePluginHttpTarget(pluginId, currentUrl);
|
|
1659
|
-
if (!requestBuffered) {
|
|
1660
|
-
currentInit = await bufferPluginHttpRequest(currentInit);
|
|
1661
|
-
requestBuffered = true;
|
|
1662
|
-
}
|
|
1663
|
-
const response = await fetchImpl(currentUrl, {
|
|
1664
|
-
...currentInit,
|
|
1665
|
-
redirect: "manual"
|
|
1666
|
-
});
|
|
1667
|
-
const location = response.headers.get("Location");
|
|
1668
|
-
if (location === null) return pluginHttpResponseFromWire(await pluginHttpResponseToWire(response, currentUrl, redirected));
|
|
1669
|
-
const redirectAction = pluginHttpRedirectAction(response.status, true, currentInit);
|
|
1670
|
-
if (redirectAction === "return") return pluginHttpResponseFromWire(await pluginHttpResponseToWire(response, currentUrl, redirected));
|
|
1671
|
-
await response.body?.cancel();
|
|
1672
|
-
if (redirectAction === "error") throw new Error(`Plugin "${pluginId}": redirect mode is "error"`);
|
|
1673
|
-
const previousOrigin = new URL(currentUrl).origin;
|
|
1674
|
-
currentUrl = new URL(location, currentUrl).href;
|
|
1675
|
-
redirected = true;
|
|
1676
|
-
const nextOrigin = new URL(currentUrl).origin;
|
|
1677
|
-
currentInit = rewritePluginHttpRedirect(response.status, currentInit);
|
|
1678
|
-
if (previousOrigin !== nextOrigin && currentInit) currentInit = stripCredentialHeaders(currentInit);
|
|
1679
|
-
}
|
|
1680
|
-
throw new Error(`Plugin "${pluginId}": too many redirects (max ${MAX_PLUGIN_REDIRECTS})`);
|
|
1681
|
-
} };
|
|
1682
|
-
}
|
|
1683
|
-
/**
|
|
1684
|
-
* Create logger for a plugin
|
|
1685
|
-
*/
|
|
1686
|
-
function createLogAccess(pluginId, redactor) {
|
|
1687
|
-
const prefix = `[plugin:${pluginId}]`;
|
|
1688
|
-
const redact = (value) => redactor?.redact(value) ?? value;
|
|
1689
|
-
return {
|
|
1690
|
-
debug(message, data) {
|
|
1691
|
-
if (data !== void 0) console.debug(prefix, redact(message), redact(data));
|
|
1692
|
-
else console.debug(prefix, redact(message));
|
|
1693
|
-
},
|
|
1694
|
-
info(message, data) {
|
|
1695
|
-
if (data !== void 0) console.info(prefix, redact(message), redact(data));
|
|
1696
|
-
else console.info(prefix, redact(message));
|
|
1697
|
-
},
|
|
1698
|
-
warn(message, data) {
|
|
1699
|
-
if (data !== void 0) console.warn(prefix, redact(message), redact(data));
|
|
1700
|
-
else console.warn(prefix, redact(message));
|
|
1701
|
-
},
|
|
1702
|
-
error(message, data) {
|
|
1703
|
-
if (data !== void 0) console.error(prefix, redact(message), redact(data));
|
|
1704
|
-
else console.error(prefix, redact(message));
|
|
1705
|
-
}
|
|
1706
|
-
};
|
|
1707
|
-
}
|
|
1708
|
-
const TRAILING_SLASH_RE = /\/$/;
|
|
1709
|
-
/**
|
|
1710
|
-
* Create site info from config and settings.
|
|
1711
|
-
*
|
|
1712
|
-
* Resolution order for URL:
|
|
1713
|
-
* 1. options table (emdash:site_url)
|
|
1714
|
-
* 2. Astro `site` config
|
|
1715
|
-
* 3. fallback to empty string
|
|
1716
|
-
*/
|
|
1717
|
-
function createSiteInfo(options) {
|
|
1718
|
-
return {
|
|
1719
|
-
name: options.siteName ?? "",
|
|
1720
|
-
url: (options.siteUrl ?? "").replace(TRAILING_SLASH_RE, ""),
|
|
1721
|
-
locale: options.locale ?? "en",
|
|
1722
|
-
trailingSlash: options.trailingSlash ?? "ignore"
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
/**
|
|
1726
|
-
* Create a URL helper that generates absolute URLs from relative paths.
|
|
1727
|
-
* Validates that path starts with "/" and rejects protocol-relative paths ("//").
|
|
1728
|
-
*/
|
|
1729
|
-
function createUrlHelper(siteUrl) {
|
|
1730
|
-
const base = siteUrl.replace(TRAILING_SLASH_RE, "");
|
|
1731
|
-
return (path) => {
|
|
1732
|
-
if (!path.startsWith("/")) throw new Error(`URL path must start with "/", got: "${path}"`);
|
|
1733
|
-
if (path.startsWith("//")) throw new Error(`URL path must not be protocol-relative, got: "${path}"`);
|
|
1734
|
-
return `${base}${path}`;
|
|
1735
|
-
};
|
|
1736
|
-
}
|
|
1737
|
-
/**
|
|
1738
|
-
* Convert a UserRepository user to the plugin-facing UserInfo shape.
|
|
1739
|
-
* Strips sensitive fields (avatarUrl, emailVerified, data).
|
|
1740
|
-
*/
|
|
1741
|
-
function toUserInfo(user) {
|
|
1742
|
-
return {
|
|
1743
|
-
id: user.id,
|
|
1744
|
-
email: user.email,
|
|
1745
|
-
name: user.name,
|
|
1746
|
-
role: user.role,
|
|
1747
|
-
createdAt: user.createdAt
|
|
1748
|
-
};
|
|
1749
|
-
}
|
|
1750
|
-
/**
|
|
1751
|
-
* Create read-only user access for plugins.
|
|
1752
|
-
* Excludes sensitive fields (password hashes, sessions, passkeys, avatar URL, data).
|
|
1753
|
-
*/
|
|
1754
|
-
function createUserAccess(db) {
|
|
1755
|
-
const userRepo = new UserRepository(db);
|
|
1756
|
-
return {
|
|
1757
|
-
async get(id) {
|
|
1758
|
-
const user = await userRepo.findById(id);
|
|
1759
|
-
if (!user) return null;
|
|
1760
|
-
return toUserInfo(user);
|
|
1761
|
-
},
|
|
1762
|
-
async getByEmail(email) {
|
|
1763
|
-
const user = await userRepo.findByEmail(email);
|
|
1764
|
-
if (!user) return null;
|
|
1765
|
-
return toUserInfo(user);
|
|
1766
|
-
},
|
|
1767
|
-
async list(opts) {
|
|
1768
|
-
const result = await userRepo.findMany({
|
|
1769
|
-
role: opts?.role,
|
|
1770
|
-
cursor: opts?.cursor,
|
|
1771
|
-
limit: opts?.limit
|
|
1772
|
-
});
|
|
1773
|
-
return {
|
|
1774
|
-
items: result.items.map(toUserInfo),
|
|
1775
|
-
nextCursor: result.nextCursor
|
|
1776
|
-
};
|
|
1777
|
-
}
|
|
1778
|
-
};
|
|
1779
|
-
}
|
|
1780
|
-
/**
|
|
1781
|
-
* Factory for creating plugin contexts
|
|
1782
|
-
*/
|
|
1783
|
-
var PluginContextFactory = class {
|
|
1784
|
-
resolveDb;
|
|
1785
|
-
beforeContentWrite;
|
|
1786
|
-
contentCreate;
|
|
1787
|
-
contentActions;
|
|
1788
|
-
storage;
|
|
1789
|
-
getUploadUrl;
|
|
1790
|
-
site;
|
|
1791
|
-
urlHelper;
|
|
1792
|
-
cronReschedule;
|
|
1793
|
-
now;
|
|
1794
|
-
emailPipeline;
|
|
1795
|
-
commentModerate;
|
|
1796
|
-
/**
|
|
1797
|
-
* Plugin IDs already warned about a missing media-write backend, so the
|
|
1798
|
-
* warning fires once per factory instead of on every hook/route context
|
|
1799
|
-
* creation (which would spam logs for hook-participating plugins).
|
|
1800
|
-
*/
|
|
1801
|
-
warnedMissingMediaBackend = /* @__PURE__ */ new Set();
|
|
1802
|
-
constructor(options) {
|
|
1803
|
-
const fixedDb = options.db;
|
|
1804
|
-
this.resolveDb = options.getDb ?? (() => fixedDb);
|
|
1805
|
-
this.beforeContentWrite = options.beforeContentWrite;
|
|
1806
|
-
this.contentCreate = options.contentCreate;
|
|
1807
|
-
this.contentActions = options.contentActions;
|
|
1808
|
-
this.storage = options.storage;
|
|
1809
|
-
this.getUploadUrl = options.getUploadUrl;
|
|
1810
|
-
this.site = createSiteInfo(options.siteInfo ?? {});
|
|
1811
|
-
this.urlHelper = createUrlHelper(this.site.url);
|
|
1812
|
-
this.cronReschedule = options.cronReschedule;
|
|
1813
|
-
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1814
|
-
this.emailPipeline = options.emailPipeline;
|
|
1815
|
-
this.commentModerate = options.commentModerate;
|
|
1816
|
-
}
|
|
1817
|
-
/**
|
|
1818
|
-
* Create the unified plugin context
|
|
1819
|
-
*/
|
|
1820
|
-
createContext(plugin) {
|
|
1821
|
-
const capabilities = new Set(plugin.capabilities);
|
|
1822
|
-
const db = this.resolveDb();
|
|
1823
|
-
const optionsRepo = new OptionsRepository(db);
|
|
1824
|
-
const secretRedactor = createPluginSecretRedactor();
|
|
1825
|
-
const settings = createSettingsAccess(optionsRepo, plugin.id, plugin.admin.settingsSchema ?? {}, void 0, secretRedactor.add);
|
|
1826
|
-
const kv = createKVAccess(optionsRepo, plugin.id, settings);
|
|
1827
|
-
const log = createLogAccess(plugin.id, secretRedactor);
|
|
1828
|
-
const storage = createStorageAccess(db, plugin.id, plugin.storage);
|
|
1829
|
-
let content;
|
|
1830
|
-
if (capabilities.has("content:write")) content = createContentAccessWithWrite(db, this.beforeContentWrite, {
|
|
1831
|
-
site: this.site,
|
|
1832
|
-
revisions: capabilities.has("content:revisions:read")
|
|
1833
|
-
}, this.contentCreate ? (input) => this.contentCreate(plugin.id, input.collection, input.input, input.options) : void 0);
|
|
1834
|
-
else if (capabilities.has("content:read")) content = createContentAccess(db, {
|
|
1835
|
-
site: this.site,
|
|
1836
|
-
revisions: capabilities.has("content:revisions:read")
|
|
1837
|
-
});
|
|
1838
|
-
if (capabilities.has("content:publish") && this.contentActions) content = Object.assign(content ?? createContentAccess(db), {
|
|
1839
|
-
getVersioned: (collection, id) => this.contentActions.getVersioned(plugin.id, collection, id),
|
|
1840
|
-
publish: (collection, id, options) => this.contentActions.publish(plugin.id, collection, id, options),
|
|
1841
|
-
unpublish: (collection, id, options) => this.contentActions.unpublish(plugin.id, collection, id, options),
|
|
1842
|
-
schedule: (collection, id, options) => this.contentActions.schedule(plugin.id, collection, id, options),
|
|
1843
|
-
unschedule: (collection, id, options) => this.contentActions.unschedule(plugin.id, collection, id, options)
|
|
1844
|
-
});
|
|
1845
|
-
if (capabilities.has("content:restore") && this.contentActions) content = Object.assign(content ?? {
|
|
1846
|
-
get: async () => {
|
|
1847
|
-
throw new Error("Missing capability: content:read");
|
|
1848
|
-
},
|
|
1849
|
-
list: async () => {
|
|
1850
|
-
throw new Error("Missing capability: content:read");
|
|
1851
|
-
}
|
|
1852
|
-
}, {
|
|
1853
|
-
getTrashedVersioned: (collection, id) => this.contentActions.getTrashedVersioned(plugin.id, collection, id),
|
|
1854
|
-
restore: (collection, id, options) => this.contentActions.restore(plugin.id, collection, id, options)
|
|
1855
|
-
});
|
|
1856
|
-
const schema = capabilities.has("schema:read") ? createSchemaAccess(db) : void 0;
|
|
1857
|
-
let taxonomies;
|
|
1858
|
-
if (capabilities.has("taxonomies:write")) taxonomies = createTaxonomyAccessWithWrite(db);
|
|
1859
|
-
else if (capabilities.has("taxonomies:read")) taxonomies = createTaxonomyAccess(db);
|
|
1860
|
-
let redirects;
|
|
1861
|
-
if (capabilities.has("redirects:write")) redirects = createRedirectAccess(db, true);
|
|
1862
|
-
else if (capabilities.has("redirects:read")) redirects = createRedirectAccess(db);
|
|
1863
|
-
let media;
|
|
1864
|
-
if (capabilities.has("media:read") || capabilities.has("media:write") || capabilities.has("media:bytes:read") || capabilities.has("media:metadata:write")) media = capabilities.has("media:read") || capabilities.has("media:write") ? createMediaAccess(db) : createBlockedMediaReadAccess();
|
|
1865
|
-
if (capabilities.has("media:write")) if (this.getUploadUrl || this.storage) media = createMediaAccessWithWrite(db, this.getUploadUrl, this.storage);
|
|
1866
|
-
else {
|
|
1867
|
-
if (!this.warnedMissingMediaBackend.has(plugin.id)) {
|
|
1868
|
-
this.warnedMissingMediaBackend.add(plugin.id);
|
|
1869
|
-
log.warn("declares the media:write capability but no storage backend is configured; upload() is unavailable.");
|
|
1870
|
-
}
|
|
1871
|
-
media ??= createMediaAccess(db);
|
|
1872
|
-
}
|
|
1873
|
-
if (capabilities.has("media:bytes:read") && media) media.readBytes = (id, options) => readPluginMediaBytes(db, this.storage, id, options);
|
|
1874
|
-
if (capabilities.has("media:metadata:write") && media) media.updateMetadata = (id, patch) => updatePluginMediaMetadata(db, id, patch);
|
|
1875
|
-
let http;
|
|
1876
|
-
if (capabilities.has("network:request:unrestricted")) http = createUnrestrictedHttpAccess(plugin.id);
|
|
1877
|
-
else if (capabilities.has("network:request")) http = createHttpAccess(plugin.id, plugin.allowedHosts);
|
|
1878
|
-
let users;
|
|
1879
|
-
if (capabilities.has("users:read")) users = createUserAccess(db);
|
|
1880
|
-
let comments;
|
|
1881
|
-
if (capabilities.has("comments:moderate")) comments = createCommentAccess(db, (id, status, expectedStatus) => {
|
|
1882
|
-
if (!this.commentModerate) throw new Error("Comment moderation is unavailable");
|
|
1883
|
-
return this.commentModerate(plugin.id, id, status, expectedStatus);
|
|
1884
|
-
});
|
|
1885
|
-
else if (capabilities.has("comments:read")) comments = createCommentAccess(db);
|
|
1886
|
-
let cron;
|
|
1887
|
-
if (this.cronReschedule) cron = new CronAccessImpl(db, plugin.id, this.cronReschedule, this.now);
|
|
1888
|
-
let email;
|
|
1889
|
-
if (capabilities.has("email:send") && this.emailPipeline?.isAvailable()) {
|
|
1890
|
-
const pipeline = this.emailPipeline;
|
|
1891
|
-
const pluginId = plugin.id;
|
|
1892
|
-
email = { send: (message) => pipeline.send(message, pluginId) };
|
|
1893
|
-
}
|
|
1894
|
-
return {
|
|
1895
|
-
plugin: {
|
|
1896
|
-
id: plugin.id,
|
|
1897
|
-
version: plugin.version
|
|
1898
|
-
},
|
|
1899
|
-
storage,
|
|
1900
|
-
kv,
|
|
1901
|
-
settings,
|
|
1902
|
-
content,
|
|
1903
|
-
schema,
|
|
1904
|
-
taxonomies,
|
|
1905
|
-
redirects,
|
|
1906
|
-
media,
|
|
1907
|
-
http,
|
|
1908
|
-
log,
|
|
1909
|
-
site: this.site,
|
|
1910
|
-
url: this.urlHelper,
|
|
1911
|
-
users,
|
|
1912
|
-
comments,
|
|
1913
|
-
cron,
|
|
1914
|
-
email
|
|
1915
|
-
};
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
|
|
1919
|
-
//#endregion
|
|
1920
|
-
export { StorageSerializationError as S, readPluginMediaBytes as _, createHttpAccess as a, createContentAccess as b, createSchemaAccess as c, createTaxonomyAccessWithWrite as d, createUnrestrictedHttpAccess as f, parsePluginMediaMetadataPatch as g, MAX_PLUGIN_MEDIA_READ_BYTES as h, createContentAccessWithWrite as i, createSiteInfo as l, DEFAULT_PLUGIN_MEDIA_READ_BYTES as m, RedirectAccessError as n, createMediaAccess as o, PluginRouteError as p, createCommentAccess as r, createRedirectAccess as s, PluginContextFactory as t, createStorageAccess as u, toPluginMediaItem as v, PluginStorageRepository as x, updatePluginMediaMetadata as y };
|
|
1921
|
-
//# sourceMappingURL=context-BeGccdUX.mjs.map
|