emdash 0.26.0 → 0.28.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/{adapters-DAAjOID4.d.mts → adapters-hUy8vOB-.d.mts} +1 -1
- package/dist/{adapters-DAAjOID4.d.mts.map → adapters-hUy8vOB-.d.mts.map} +1 -1
- package/dist/{allowed-origins-D5FxMUo8.mjs → allowed-origins-CRsJ6BPM.mjs} +2 -2
- package/dist/{allowed-origins-D5FxMUo8.mjs.map → allowed-origins-CRsJ6BPM.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +17 -16
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-BMfveMoB.mjs → api-BFURZn1i.mjs} +104 -38
- package/dist/api-BFURZn1i.mjs.map +1 -0
- package/dist/{api-tokens-D6ppjIVi.mjs → api-tokens-DvqeZmVq.mjs} +2 -2
- package/dist/{api-tokens-D6ppjIVi.mjs.map → api-tokens-DvqeZmVq.mjs.map} +1 -1
- package/dist/{apply--IVNSdXn.mjs → apply-Bu-8nYHL.mjs} +113 -76
- package/dist/apply-Bu-8nYHL.mjs.map +1 -0
- package/dist/astro/index.d.mts +12 -11
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +13 -5
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -9
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +52 -12
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.mjs +5 -5
- package/dist/astro/middleware/request-context.mjs +2 -2
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +2 -9
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +125 -71
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +8 -8
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +14 -14
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +14 -14
- package/dist/astro/routes/api/admin/bylines/index.mjs +14 -14
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +38 -37
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +38 -37
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +37 -36
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +37 -36
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +36 -35
- 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 +36 -35
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +36 -35
- 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 +37 -36
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +37 -36
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +36 -35
- 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 +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +36 -35
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
- package/dist/astro/routes/api/admin/users/index.mjs +6 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs +5 -5
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
- package/dist/astro/routes/api/auth/invite/register-options.mjs +9 -9
- package/dist/astro/routes/api/auth/logout.mjs +3 -3
- package/dist/astro/routes/api/auth/magic-link/send.mjs +9 -9
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +7 -7
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +9 -9
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/request.mjs +9 -9
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +13 -13
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +12 -12
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +10 -10
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +13 -13
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/index.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
- package/dist/astro/routes/api/dashboard.mjs +8 -8
- package/dist/astro/routes/api/dev/emails.mjs +3 -3
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +19 -11
- package/dist/astro/routes/api/import/probe.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -40
- package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/execute.mjs +12 -407
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -9
- package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +19 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +97 -79
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +19 -11
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +40 -2
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +519 -39
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.d.mts.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +14 -5
- package/dist/astro/routes/api/manifest.mjs.map +1 -1
- package/dist/astro/routes/api/mcp.mjs +39 -39
- package/dist/astro/routes/api/media/_id_/confirm.mjs +7 -7
- package/dist/astro/routes/api/media/_id_.mjs +7 -7
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.mjs +8 -8
- package/dist/astro/routes/api/media.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/items.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/translations.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_.mjs +9 -9
- package/dist/astro/routes/api/menus/index.mjs +9 -9
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
- package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
- package/dist/astro/routes/api/oauth/register.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +4 -4
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
- package/dist/astro/routes/api/redirects/404s/index.mjs +9 -9
- package/dist/astro/routes/api/redirects/404s/summary.mjs +9 -9
- package/dist/astro/routes/api/redirects/_id_.mjs +10 -10
- package/dist/astro/routes/api/redirects/index.mjs +10 -10
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +36 -35
- 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 +36 -35
- 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 +36 -35
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +36 -35
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +36 -35
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +7 -6
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +36 -35
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +36 -35
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +10 -10
- package/dist/astro/routes/api/search/index.mjs +9 -9
- package/dist/astro/routes/api/search/rebuild.mjs +10 -10
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +9 -9
- package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
- package/dist/astro/routes/api/sections/index.mjs +9 -9
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +12 -12
- package/dist/astro/routes/api/setup/admin-verify.mjs +11 -11
- package/dist/astro/routes/api/setup/admin.mjs +10 -10
- package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +28 -25
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +26 -25
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +4 -4
- package/dist/astro/routes/api/snapshot.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/index.mjs +13 -13
- package/dist/astro/routes/api/themes/preview.mjs +5 -5
- package/dist/astro/routes/api/typegen.mjs +5 -5
- 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 +7 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +9 -9
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +9 -9
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +9 -9
- package/dist/astro/routes/api/widget-components.mjs +3 -3
- 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 +14 -12
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-BZnOGJFo.mjs → authorize-Dz88F_Fw.mjs} +2 -2
- package/dist/{authorize-BZnOGJFo.mjs.map → authorize-Dz88F_Fw.mjs.map} +1 -1
- package/dist/{byline-LL8lyqdb.mjs → byline-Bm8ZgUP9.mjs} +6 -6
- package/dist/{byline-LL8lyqdb.mjs.map → byline-Bm8ZgUP9.mjs.map} +1 -1
- package/dist/{byline-fields-eyJMblSr.mjs → byline-fields-2Lp5Hbgf.mjs} +18 -3
- package/dist/byline-fields-2Lp5Hbgf.mjs.map +1 -0
- package/dist/{byline-fields-DaMKzkhO.mjs → byline-fields-CIx_GuN4.mjs} +1 -1
- package/dist/{byline-fields-DaMKzkhO.mjs.map → byline-fields-CIx_GuN4.mjs.map} +1 -1
- package/dist/{byline-fields-KD3JFRuH.d.mts → byline-fields-Cgrt9-aL.d.mts} +18 -3
- package/dist/{byline-fields-KD3JFRuH.d.mts.map → byline-fields-Cgrt9-aL.d.mts.map} +1 -1
- package/dist/{bylines-BmJ7Vg7J.mjs → bylines-6tm9mgQb.mjs} +4 -4
- package/dist/{bylines-BmJ7Vg7J.mjs.map → bylines-6tm9mgQb.mjs.map} +1 -1
- package/dist/{bylines-DE47vP8d.mjs → bylines-Woq87129.mjs} +6 -6
- package/dist/{bylines-DE47vP8d.mjs.map → bylines-Woq87129.mjs.map} +1 -1
- package/dist/{cache-D9jF5bYw.mjs → cache-CEBAZjir.mjs} +2 -2
- package/dist/{cache-D9jF5bYw.mjs.map → cache-CEBAZjir.mjs.map} +1 -1
- package/dist/{challenge-store-LhiqMccz.mjs → challenge-store-anhwVER0.mjs} +1 -1
- package/dist/{challenge-store-LhiqMccz.mjs.map → challenge-store-anhwVER0.mjs.map} +1 -1
- package/dist/{chunks-OCG163rN.mjs → chunks-B2isWQhr.mjs} +2 -2
- package/dist/{chunks-OCG163rN.mjs.map → chunks-B2isWQhr.mjs.map} +1 -1
- package/dist/cli/index.mjs +23 -22
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -2
- package/dist/{comment-D9BLDpZI.mjs → comment-CzeatwBV.mjs} +3 -3
- package/dist/{comment-D9BLDpZI.mjs.map → comment-CzeatwBV.mjs.map} +1 -1
- package/dist/{comment-reaction-Drl9SkNu.mjs → comment-reaction-Cj9QJY7I.mjs} +2 -2
- package/dist/{comment-reaction-Drl9SkNu.mjs.map → comment-reaction-Cj9QJY7I.mjs.map} +1 -1
- package/dist/{comments-BjJ9SKGU.mjs → comments-BLE-jv5k.mjs} +3 -3
- package/dist/{comments-BjJ9SKGU.mjs.map → comments-BLE-jv5k.mjs.map} +1 -1
- package/dist/{components-CYt4uVK9.mjs → components-B_sATNUc.mjs} +1 -1
- package/dist/{components-CYt4uVK9.mjs.map → components-B_sATNUc.mjs.map} +1 -1
- package/dist/{content-ncUWWjna.mjs → content-DhgEth4k.mjs} +4 -4
- package/dist/{content-ncUWWjna.mjs.map → content-DhgEth4k.mjs.map} +1 -1
- package/dist/content-refresh-CA4uKwzH.mjs +1531 -0
- package/dist/content-refresh-CA4uKwzH.mjs.map +1 -0
- package/dist/{context-C_g-iJVA.mjs → context-CUsR9dXL.mjs} +76 -54
- package/dist/context-CUsR9dXL.mjs.map +1 -0
- package/dist/{cron-C5LVoNmP.mjs → cron-v4FTEFR_.mjs} +1 -1
- package/dist/{cron-C5LVoNmP.mjs.map → cron-v4FTEFR_.mjs.map} +1 -1
- package/dist/{dashboard-CotyqQrW.mjs → dashboard-EvEKF5en.mjs} +4 -4
- package/dist/{dashboard-CotyqQrW.mjs.map → dashboard-EvEKF5en.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{default-CeRG-Ot4.mjs → default-CM_D83Mg.mjs} +1 -1
- package/dist/{default-CeRG-Ot4.mjs.map → default-CM_D83Mg.mjs.map} +1 -1
- package/dist/{device-flow-DENDCQ9F.mjs → device-flow-CfGP18Uw.mjs} +4 -4
- package/dist/{device-flow-DENDCQ9F.mjs.map → device-flow-CfGP18Uw.mjs.map} +1 -1
- package/dist/{email-console-DJP32ucW.mjs → email-console-CIf4Lfvv.mjs} +1 -1
- package/dist/{email-console-DJP32ucW.mjs.map → email-console-CIf4Lfvv.mjs.map} +1 -1
- package/dist/{error-rVGiqWDt.mjs → error-CkAseSqZ.mjs} +2 -2
- package/dist/{error-rVGiqWDt.mjs.map → error-CkAseSqZ.mjs.map} +1 -1
- package/dist/{escape-Bjio4ZsM.mjs → escape-DqD0aCKu.mjs} +1 -1
- package/dist/{escape-Bjio4ZsM.mjs.map → escape-DqD0aCKu.mjs.map} +1 -1
- package/dist/{field-defs-cache-ljXzJCfc.mjs → field-defs-cache-WUI88LX5.mjs} +2 -2
- package/dist/{field-defs-cache-ljXzJCfc.mjs.map → field-defs-cache-WUI88LX5.mjs.map} +1 -1
- package/dist/{fts-manager-BbCCbEKA.mjs → fts-manager-BbFaF_V8.mjs} +2 -2
- package/dist/{fts-manager-BbCCbEKA.mjs.map → fts-manager-BbFaF_V8.mjs.map} +1 -1
- package/dist/{import-Bb1T9WJS.mjs → import-fYLf14Co.mjs} +514 -25
- package/dist/import-fYLf14Co.mjs.map +1 -0
- package/dist/{index-BRNabKrx.d.mts → index-B9sIxwe4.d.mts} +2420 -2379
- package/dist/index-B9sIxwe4.d.mts.map +1 -0
- package/dist/{index-Dt0OKJB4.d.mts → index-Bm-Gy8Mh.d.mts} +25 -4
- package/dist/index-Bm-Gy8Mh.d.mts.map +1 -0
- package/dist/index.d.mts +18 -17
- package/dist/index.mjs +54 -53
- package/dist/{load-DMjXghbZ.mjs → load-Djn3HWQS.mjs} +2 -2
- package/dist/{load-DMjXghbZ.mjs.map → load-Djn3HWQS.mjs.map} +1 -1
- package/dist/{loader-BA_zhkS_.mjs → loader-CB6YHeul.mjs} +6 -5
- package/dist/loader-CB6YHeul.mjs.map +1 -0
- package/dist/{manifest-schema-kPGX7VS-.mjs → manifest-schema-BHlgZM1W.mjs} +1 -1
- package/dist/{manifest-schema-kPGX7VS-.mjs.map → manifest-schema-BHlgZM1W.mjs.map} +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/local-runtime.d.mts +12 -11
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +5 -5
- package/dist/{media-C5t_eCnk.mjs → media-BrXIaMFN.mjs} +2 -2
- package/dist/{media-C5t_eCnk.mjs.map → media-BrXIaMFN.mjs.map} +1 -1
- package/dist/{media-allowlist-Du8t1u6a.mjs → media-allowlist-CRHEeJ_i.mjs} +1 -1
- package/dist/{media-allowlist-Du8t1u6a.mjs.map → media-allowlist-CRHEeJ_i.mjs.map} +1 -1
- package/dist/{media-url-O4rm9-SQ.mjs → media-url-CKi5XSw4.mjs} +1 -1
- package/dist/{media-url-O4rm9-SQ.mjs.map → media-url-CKi5XSw4.mjs.map} +1 -1
- package/dist/{menus-CBb6UrMU.mjs → menus-Cfivc2Ad.mjs} +3 -3
- package/dist/{menus-CBb6UrMU.mjs.map → menus-Cfivc2Ad.mjs.map} +1 -1
- package/dist/{menus-CYH09a41.mjs → menus-CsZ2ZEK2.mjs} +16 -12
- package/dist/{menus-CYH09a41.mjs.map → menus-CsZ2ZEK2.mjs.map} +1 -1
- package/dist/{mode-BB0F8xTC.mjs → mode-BM9QgEmt.mjs} +1 -1
- package/dist/{mode-BB0F8xTC.mjs.map → mode-BM9QgEmt.mjs.map} +1 -1
- package/dist/{oauth-authorization-CsvzIp_F.mjs → oauth-authorization-CXWkIGT4.mjs} +4 -4
- package/dist/{oauth-authorization-CsvzIp_F.mjs.map → oauth-authorization-CXWkIGT4.mjs.map} +1 -1
- package/dist/{oauth-clients-C9SYwEbZ.mjs → oauth-clients-BmTI5rsl.mjs} +1 -1
- package/dist/{oauth-clients-C9SYwEbZ.mjs.map → oauth-clients-BmTI5rsl.mjs.map} +1 -1
- package/dist/{oauth-state-store---zrApfB.mjs → oauth-state-store-YLFLcH5F.mjs} +1 -1
- package/dist/{oauth-state-store---zrApfB.mjs.map → oauth-state-store-YLFLcH5F.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-SHsWRlG9.mjs → oauth-user-lookup-Bsc_GsX-.mjs} +1 -1
- package/dist/{oauth-user-lookup-SHsWRlG9.mjs.map → oauth-user-lookup-Bsc_GsX-.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{object-cache-CHbHv83-.mjs → object-cache-Bok5j2ae.mjs} +43 -3
- package/dist/object-cache-Bok5j2ae.mjs.map +1 -0
- package/dist/{options-41nCWqi9.d.mts → options-iMpRRxIe.d.mts} +3 -3
- package/dist/{options-41nCWqi9.d.mts.map → options-iMpRRxIe.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-BwAdS7FZ.mjs → parse-GmjecGct.mjs} +2 -2
- package/dist/{parse-BwAdS7FZ.mjs.map → parse-GmjecGct.mjs.map} +1 -1
- package/dist/{passkey-config-C0YfSBko.mjs → passkey-config-De0ahnRg.mjs} +1 -1
- package/dist/{passkey-config-C0YfSBko.mjs.map → passkey-config-De0ahnRg.mjs.map} +1 -1
- package/dist/{placeholder-Cuce9U-m.d.mts → placeholder-BSKwzRe4.d.mts} +1 -1
- package/dist/{placeholder-Cuce9U-m.d.mts.map → placeholder-BSKwzRe4.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +10 -9
- package/dist/plugin-utils.d.mts.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +10 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{portable-text-BICg8bTk.mjs → portable-text-cCa0cGV6.mjs} +1 -1
- package/dist/{portable-text-BICg8bTk.mjs.map → portable-text-cCa0cGV6.mjs.map} +1 -1
- package/dist/{preview-DKGCt2_p.mjs → preview-SSXNVPAC.mjs} +2 -2
- package/dist/{preview-DKGCt2_p.mjs.map → preview-SSXNVPAC.mjs.map} +1 -1
- package/dist/{public-url-CTVqgMmg.mjs → public-url-Danv47cS.mjs} +1 -1
- package/dist/{public-url-CTVqgMmg.mjs.map → public-url-Danv47cS.mjs.map} +1 -1
- package/dist/{query-BpQb-blG.mjs → query-ByGq6ver.mjs} +14 -14
- package/dist/{query-BpQb-blG.mjs.map → query-ByGq6ver.mjs.map} +1 -1
- package/dist/{rate-limit-Dap6HMNM.mjs → rate-limit-C_Lf63Dd.mjs} +2 -2
- package/dist/{rate-limit-Dap6HMNM.mjs.map → rate-limit-C_Lf63Dd.mjs.map} +1 -1
- package/dist/{redirect-CS-PHtNh.mjs → redirect-CoW3G-0U.mjs} +1 -1
- package/dist/{redirect-CS-PHtNh.mjs.map → redirect-CoW3G-0U.mjs.map} +1 -1
- package/dist/{redirect-_M0trWoo.mjs → redirect-IyfNirgS.mjs} +2 -2
- package/dist/{redirect-_M0trWoo.mjs.map → redirect-IyfNirgS.mjs.map} +1 -1
- package/dist/{redirects-w0V9vj_j.mjs → redirects-Bs1g42Ri.mjs} +4 -4
- package/dist/{redirects-w0V9vj_j.mjs.map → redirects-Bs1g42Ri.mjs.map} +1 -1
- package/dist/{redirects-Dcbuisoj.mjs → redirects-DXEXBAII.mjs} +2 -2
- package/dist/{redirects-Dcbuisoj.mjs.map → redirects-DXEXBAII.mjs.map} +1 -1
- package/dist/{registry-g8yYt4cC.mjs → registry-B65mUtPa.mjs} +122 -79
- package/dist/registry-B65mUtPa.mjs.map +1 -0
- package/dist/{request-meta-CmS1tDFf.mjs → request-meta-DbvXaAvU.mjs} +2 -2
- package/dist/{request-meta-CmS1tDFf.mjs.map → request-meta-DbvXaAvU.mjs.map} +1 -1
- package/dist/{resolve-C7I0qiR0.mjs → resolve-DR3wqMTy.mjs} +1 -1
- package/dist/{resolve-C7I0qiR0.mjs.map → resolve-DR3wqMTy.mjs.map} +1 -1
- package/dist/{runner-BbR3DfrL.d.mts → runner-DvFWoz8-.d.mts} +2 -2
- package/dist/{runner-BbR3DfrL.d.mts.map → runner-DvFWoz8-.d.mts.map} +1 -1
- package/dist/{runner-DPdt_Wpa.mjs → runner-K8XK101B.mjs} +351 -199
- package/dist/runner-K8XK101B.mjs.map +1 -0
- package/dist/runtime.d.mts +11 -10
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/dist/scheduled-publish-Drffrf2a.d.mts +11 -0
- package/dist/scheduled-publish-Drffrf2a.d.mts.map +1 -0
- package/dist/{schema-DoLd3j2e.mjs → schema-Blhoxp3P.mjs} +5 -5
- package/dist/{schema-DoLd3j2e.mjs.map → schema-Blhoxp3P.mjs.map} +1 -1
- package/dist/{search-BR0ZeZmh.mjs → search-DZN2HZ-V.mjs} +6 -4
- package/dist/search-DZN2HZ-V.mjs.map +1 -0
- package/dist/{secrets-BSf9pRRY.mjs → secrets-kp760LlD.mjs} +2 -2
- package/dist/{secrets-BSf9pRRY.mjs.map → secrets-kp760LlD.mjs.map} +1 -1
- package/dist/{sections-5J2YJPqJ.mjs → sections-CpF5SaGr.mjs} +3 -3
- package/dist/{sections-5J2YJPqJ.mjs.map → sections-CpF5SaGr.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +19 -18
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/{seo-BkhuuaaE.mjs → seo-89-53DDs.mjs} +1 -1
- package/dist/{seo-BkhuuaaE.mjs.map → seo-89-53DDs.mjs.map} +1 -1
- package/dist/{seo-DKeXHkzE.mjs → seo-DXu2UQnC.mjs} +3 -3
- package/dist/{seo-DKeXHkzE.mjs.map → seo-DXu2UQnC.mjs.map} +1 -1
- package/dist/{service-BbO2s8gT.mjs → service-DQmiBMzt.mjs} +3 -3
- package/dist/{service-BbO2s8gT.mjs.map → service-DQmiBMzt.mjs.map} +1 -1
- package/dist/{session-user-B8aLtKAH.mjs → session-user-Cascbax7.mjs} +1 -1
- package/dist/{session-user-B8aLtKAH.mjs.map → session-user-Cascbax7.mjs.map} +1 -1
- package/dist/{settings-BMNr9Dmi.mjs → settings-rZyHEcNT.mjs} +3 -3
- package/dist/{settings-BMNr9Dmi.mjs.map → settings-rZyHEcNT.mjs.map} +1 -1
- package/dist/{settings-DJeki-97.mjs → settings-zhmdKyJQ.mjs} +5 -5
- package/dist/{settings-DJeki-97.mjs.map → settings-zhmdKyJQ.mjs.map} +1 -1
- package/dist/{setup-complete-gEiySUc-.mjs → setup-complete-Cb3LRyzy.mjs} +1 -1
- package/dist/{setup-complete-gEiySUc-.mjs.map → setup-complete-Cb3LRyzy.mjs.map} +1 -1
- package/dist/{setup-nonce-DzS50zme.mjs → setup-nonce-Z8x0tHf8.mjs} +1 -1
- package/dist/{setup-nonce-DzS50zme.mjs.map → setup-nonce-Z8x0tHf8.mjs.map} +1 -1
- package/dist/{single-flight-cache-Cdfkic3t.mjs → single-flight-cache-BG3B_GhN.mjs} +1 -1
- package/dist/{single-flight-cache-Cdfkic3t.mjs.map → single-flight-cache-BG3B_GhN.mjs.map} +1 -1
- package/dist/{site-url-BLebyON8.mjs → site-url-BBfDTgZB.mjs} +1 -1
- package/dist/{site-url-BLebyON8.mjs.map → site-url-BBfDTgZB.mjs.map} +1 -1
- package/dist/{ssrf-BRKb343l.mjs → ssrf-1g7usGBg.mjs} +1 -1
- package/dist/{ssrf-BRKb343l.mjs.map → ssrf-1g7usGBg.mjs.map} +1 -1
- package/dist/{status-vUK0SA17.mjs → status-CqJQX1QN.mjs} +1 -1
- package/dist/{status-vUK0SA17.mjs.map → status-CqJQX1QN.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-CM_fUqLU.mjs → taxonomies-BGBd16Eu.mjs} +4 -4
- package/dist/{taxonomies-CM_fUqLU.mjs.map → taxonomies-BGBd16Eu.mjs.map} +1 -1
- package/dist/{taxonomies-D13i2ctt.mjs → taxonomies-Cnb4F-Pb.mjs} +7 -7
- package/dist/{taxonomies-D13i2ctt.mjs.map → taxonomies-Cnb4F-Pb.mjs.map} +1 -1
- package/dist/{taxonomy-BU0LjdVb.mjs → taxonomy-BxGxZZdu.mjs} +4 -4
- package/dist/{taxonomy-BU0LjdVb.mjs.map → taxonomy-BxGxZZdu.mjs.map} +1 -1
- package/dist/{tokens-LeuXF9gG.mjs → tokens-BGT0TUUo.mjs} +1 -1
- package/dist/{tokens-LeuXF9gG.mjs.map → tokens-BGT0TUUo.mjs.map} +1 -1
- package/dist/{transport-BZ-03aFT.d.mts → transport-BMDWHSgZ.d.mts} +1 -1
- package/dist/{transport-BZ-03aFT.d.mts.map → transport-BMDWHSgZ.d.mts.map} +1 -1
- package/dist/{transport-_2nBz7e9.mjs → transport-DPefPMcz.mjs} +1 -1
- package/dist/{transport-_2nBz7e9.mjs.map → transport-DPefPMcz.mjs.map} +1 -1
- package/dist/{trusted-proxy-DZY5WCn2.mjs → trusted-proxy-VTPRiX2s.mjs} +1 -1
- package/dist/{trusted-proxy-DZY5WCn2.mjs.map → trusted-proxy-VTPRiX2s.mjs.map} +1 -1
- package/dist/{types-ETmO_jQr.d.mts → types-BcLUUbbR.d.mts} +2 -2
- package/dist/{types-ETmO_jQr.d.mts.map → types-BcLUUbbR.d.mts.map} +1 -1
- package/dist/{types-BYnDBVwe.d.mts → types-CGbYowGQ.d.mts} +2 -2
- package/dist/{types-BYnDBVwe.d.mts.map → types-CGbYowGQ.d.mts.map} +1 -1
- package/dist/{types-BWqGVhFn.d.mts → types-CgvhU-hF.d.mts} +1 -1
- package/dist/{types-BWqGVhFn.d.mts.map → types-CgvhU-hF.d.mts.map} +1 -1
- package/dist/{types-fS7-VTdR.d.mts → types-ClUkdbWb.d.mts} +1 -1
- package/dist/{types-fS7-VTdR.d.mts.map → types-ClUkdbWb.d.mts.map} +1 -1
- package/dist/{types-CZgK3EMW.mjs → types-CuW0zWDA.mjs} +2 -2
- package/dist/{types-CZgK3EMW.mjs.map → types-CuW0zWDA.mjs.map} +1 -1
- package/dist/{types-BG8_XTLj.d.mts → types-DmHk7vAt.d.mts} +1 -1
- package/dist/{types-BG8_XTLj.d.mts.map → types-DmHk7vAt.d.mts.map} +1 -1
- package/dist/{types-CQAugunJ.mjs → types-Jo3UYd6b.mjs} +1 -1
- package/dist/{types-CQAugunJ.mjs.map → types-Jo3UYd6b.mjs.map} +1 -1
- package/dist/{types-xIfVRNLp.d.mts → types-_HFLRzTn.d.mts} +22 -1
- package/dist/types-_HFLRzTn.d.mts.map +1 -0
- package/dist/{types-DrYMm9s5.d.mts → types-ea9sAPck.d.mts} +1 -1
- package/dist/{types-DrYMm9s5.d.mts.map → types-ea9sAPck.d.mts.map} +1 -1
- package/dist/{types-CNlaBFzx.d.mts → types-g_aFIdW0.d.mts} +1 -1
- package/dist/{types-CNlaBFzx.d.mts.map → types-g_aFIdW0.d.mts.map} +1 -1
- package/dist/{types-CUsXGRVj.d.mts → types-vgEWF18A.d.mts} +19 -1
- package/dist/{types-CUsXGRVj.d.mts.map → types-vgEWF18A.d.mts.map} +1 -1
- package/dist/{user-DkG2jKCp.mjs → user-BjAOvU3Z.mjs} +3 -3
- package/dist/{user-DkG2jKCp.mjs.map → user-BjAOvU3Z.mjs.map} +1 -1
- package/dist/{utils-B7A57fm9.mjs → utils-BJXElxdw.mjs} +156 -7
- package/dist/utils-BJXElxdw.mjs.map +1 -0
- package/dist/{validate-CuJ1Ps9j.mjs → validate-CWSnKVIJ.mjs} +2 -2
- package/dist/{validate-CuJ1Ps9j.mjs.map → validate-CWSnKVIJ.mjs.map} +1 -1
- package/dist/{validate-CG0V0wNR.d.mts → validate-Z1b86_3B.d.mts} +11 -6
- package/dist/{validate-CG0V0wNR.d.mts.map → validate-Z1b86_3B.d.mts.map} +1 -1
- package/dist/{validation-CwUfTdGF.mjs → validation-c8WjhH8z.mjs} +8 -5
- package/dist/{validation-CwUfTdGF.mjs.map → validation-c8WjhH8z.mjs.map} +1 -1
- package/dist/version-CaccU7DM.mjs +7 -0
- package/dist/{version-BoNBItP_.mjs.map → version-CaccU7DM.mjs.map} +1 -1
- package/dist/{widgets-BfOZ-ajY.mjs → widgets-hWcIicX8.mjs} +3 -3
- package/dist/{widgets-BfOZ-ajY.mjs.map → widgets-hWcIicX8.mjs.map} +1 -1
- package/dist/wxr-taxonomies-0_uxSrmf.d.mts +34 -0
- package/dist/wxr-taxonomies-0_uxSrmf.d.mts.map +1 -0
- package/dist/wxr-taxonomies-BC3OYpvx.mjs +425 -0
- package/dist/wxr-taxonomies-BC3OYpvx.mjs.map +1 -0
- package/dist/{zod-generator-gFsRuNNy.mjs → zod-generator-CacrR-i8.mjs} +2 -2
- package/dist/{zod-generator-gFsRuNNy.mjs.map → zod-generator-CacrR-i8.mjs.map} +1 -1
- package/package.json +6 -6
- package/src/api/handlers/content.ts +69 -23
- package/src/api/handlers/plugins.ts +77 -10
- package/src/api/schemas/import.ts +19 -0
- package/src/astro/integration/runtime.ts +3 -6
- package/src/astro/integration/vite-config.ts +19 -1
- package/src/astro/middleware/auth.ts +10 -3
- package/src/astro/middleware/csp.ts +54 -3
- package/src/astro/middleware.ts +1 -0
- package/src/astro/routes/admin.astro +9 -0
- package/src/astro/routes/api/admin/plugins/[id]/disable.ts +6 -1
- package/src/astro/routes/api/admin/plugins/[id]/enable.ts +6 -1
- package/src/astro/routes/api/admin/plugins/[id]/index.ts +1 -0
- package/src/astro/routes/api/admin/plugins/index.ts +1 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +122 -98
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +794 -44
- package/src/astro/routes/api/manifest.ts +20 -1
- package/src/astro/routes/api/setup/dev-bypass.ts +7 -2
- package/src/astro/types.ts +4 -0
- package/src/cli/commands/seed.ts +1 -1
- package/src/components/EmDashImage.astro +59 -23
- package/src/components/Image.astro +36 -35
- package/src/components/LiveSearch.astro +40 -4
- package/src/components/live-search-routing.ts +30 -0
- package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +23 -18
- package/src/database/migrations/048_restore_content_taxonomies_term_index.ts +32 -0
- package/src/database/migrations/049_taxonomies_name_locale_index.ts +42 -0
- package/src/database/migrations/050_media_usage_index_status.ts +142 -0
- package/src/database/migrations/runner.ts +6 -0
- package/src/database/repositories/media-usage.ts +932 -58
- package/src/database/types.ts +22 -0
- package/src/emdash-runtime.ts +123 -3
- package/src/import/comments.ts +159 -0
- package/src/import/index.ts +8 -1
- package/src/import/settings.ts +65 -189
- package/src/import/sources/wordpress-plugin.ts +338 -42
- package/src/import/sources/wxr.ts +4 -1
- package/src/import/types.ts +12 -0
- package/src/import/utils.ts +207 -0
- package/src/import/wxr-taxonomies.ts +39 -0
- package/src/loader.ts +13 -2
- package/src/media/usage/content-fields.ts +119 -0
- package/src/media/usage/content-refresh.ts +524 -0
- package/src/media/usage/content-repair.ts +491 -0
- package/src/media/usage/content-snapshots.ts +435 -0
- package/src/media/usage/extractor.ts +4 -4
- package/src/media/usage/source-key.ts +22 -0
- package/src/menus/index.ts +17 -1
- package/src/object-cache/index.ts +82 -7
- package/src/plugins/context.ts +100 -71
- package/src/schema/registry.ts +240 -154
- package/src/search/query.ts +3 -0
- package/src/search/types.ts +2 -0
- package/src/seed/apply.ts +158 -101
- package/src/seed/types.ts +6 -1
- package/dist/api-BMfveMoB.mjs.map +0 -1
- package/dist/apply--IVNSdXn.mjs.map +0 -1
- package/dist/byline-fields-eyJMblSr.mjs.map +0 -1
- package/dist/context-C_g-iJVA.mjs.map +0 -1
- package/dist/import-Bb1T9WJS.mjs.map +0 -1
- package/dist/index-BRNabKrx.d.mts.map +0 -1
- package/dist/index-Dt0OKJB4.d.mts.map +0 -1
- package/dist/loader-BA_zhkS_.mjs.map +0 -1
- package/dist/object-cache-CHbHv83-.mjs.map +0 -1
- package/dist/registry-g8yYt4cC.mjs.map +0 -1
- package/dist/runner-DPdt_Wpa.mjs.map +0 -1
- package/dist/search-BR0ZeZmh.mjs.map +0 -1
- package/dist/types-xIfVRNLp.d.mts.map +0 -1
- package/dist/utils-B7A57fm9.mjs.map +0 -1
- package/dist/version-BoNBItP_.mjs +0 -7
- /package/dist/{api-tokens-B4BQybOp.mjs → api-tokens-DsvvCtdh.mjs} +0 -0
- /package/dist/{ssrf-CcX9zvMK.mjs → ssrf-CmuGgCq3.mjs} +0 -0
- /package/dist/{types-BvbeGEtr.mjs → types-DROWRxYV.mjs} +0 -0
|
@@ -7,26 +7,59 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { APIRoute } from "astro";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
ContentRepository,
|
|
12
|
+
SchemaRegistry,
|
|
13
|
+
type WxrCategory,
|
|
14
|
+
type WxrPost,
|
|
15
|
+
type WxrTag,
|
|
16
|
+
type WxrTerm,
|
|
17
|
+
} from "emdash";
|
|
18
|
+
import type { z } from "zod";
|
|
11
19
|
|
|
12
20
|
import { requirePerm } from "#api/authorize.js";
|
|
13
21
|
import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
22
|
+
import { handleTaxonomyCreate } from "#api/handlers/taxonomies.js";
|
|
14
23
|
import { isParseError, parseBody } from "#api/parse.js";
|
|
15
24
|
import { wpPluginExecuteBody } from "#api/schemas.js";
|
|
16
25
|
import { BylineRepository } from "#db/repositories/byline.js";
|
|
26
|
+
import { importCommentsFromPlugin } from "#import/comments.js";
|
|
17
27
|
import { getSource } from "#import/index.js";
|
|
28
|
+
import { importMenusFromPlugin } from "#import/menus.js";
|
|
29
|
+
import { importSiteSettings, parseSiteSettingsFromPlugin } from "#import/settings.js";
|
|
30
|
+
import {
|
|
31
|
+
fetchPluginComments,
|
|
32
|
+
fetchPluginCommentsPage,
|
|
33
|
+
fetchPluginContentPage,
|
|
34
|
+
fetchPluginMenus,
|
|
35
|
+
fetchPluginOptions,
|
|
36
|
+
fetchPluginTaxonomies,
|
|
37
|
+
} from "#import/sources/wordpress-plugin.js";
|
|
18
38
|
import { resolveAndValidateExternalUrl, SsrfError } from "#import/ssrf.js";
|
|
19
39
|
import type { ImportConfig, ImportResult, NormalizedItem } from "#import/types.js";
|
|
20
|
-
import { resolveImportByline } from "#import/utils.js";
|
|
40
|
+
import { resolveImportByline, sanitizeFieldSlug } from "#import/utils.js";
|
|
41
|
+
import {
|
|
42
|
+
attachPostTaxonomies,
|
|
43
|
+
loadTaxonomyPlanFromDb,
|
|
44
|
+
preImportWxrTaxonomies,
|
|
45
|
+
type TaxonomyImportPlan,
|
|
46
|
+
} from "#import/wxr-taxonomies.js";
|
|
21
47
|
import type { FieldType } from "#schema/types.js";
|
|
22
48
|
import type { EmDashHandlers, EmDashManifest } from "#types";
|
|
23
49
|
import { slugify } from "#utils/slugify.js";
|
|
24
50
|
|
|
51
|
+
import { importMediaWithProgress } from "../wordpress/media.js";
|
|
52
|
+
|
|
25
53
|
export const prerender = false;
|
|
26
54
|
|
|
27
55
|
export interface WpPluginImportConfig extends ImportConfig {
|
|
28
56
|
/** Author mappings (WP author login -> EmDash user ID) */
|
|
29
57
|
authorMappings?: Record<string, string | null>;
|
|
58
|
+
/** Wizard toggles. Absent means enabled (older admins don't send them). */
|
|
59
|
+
importMenus?: boolean;
|
|
60
|
+
importSiteTitle?: boolean;
|
|
61
|
+
importLogo?: boolean;
|
|
62
|
+
importSeo?: boolean;
|
|
30
63
|
}
|
|
31
64
|
|
|
32
65
|
export interface WpPluginImportResponse {
|
|
@@ -35,6 +68,71 @@ export interface WpPluginImportResponse {
|
|
|
35
68
|
error?: { message: string };
|
|
36
69
|
}
|
|
37
70
|
|
|
71
|
+
// =============================================================================
|
|
72
|
+
// Chunked mode (issue #475)
|
|
73
|
+
//
|
|
74
|
+
// A single-invocation import of a large site exceeds Cloudflare Worker
|
|
75
|
+
// resource limits (CPU time, subrequests). In chunked mode the admin drives
|
|
76
|
+
// the import as a loop of bounded requests: one WP content page per call,
|
|
77
|
+
// then paginated comments, then a small finalize step (menus + site
|
|
78
|
+
// identity). Cross-chunk state — the WP-ID -> EmDash-ID map, translation
|
|
79
|
+
// groups, comment threading roots — is accumulated by the client and sent
|
|
80
|
+
// back with each request, so the server stays stateless and an aborted
|
|
81
|
+
// import simply re-runs (skipExisting rebuilds the map while skipping work).
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
/** Posts per content chunk. 50 posts ≈ a few hundred D1 ops per invocation. */
|
|
85
|
+
const CONTENT_CHUNK_SIZE = 50;
|
|
86
|
+
|
|
87
|
+
interface ChunkCursor {
|
|
88
|
+
postTypeIndex: number;
|
|
89
|
+
page: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Per-chunk response payload: partial result + state the client must carry. */
|
|
93
|
+
interface ChunkResponse {
|
|
94
|
+
success: boolean;
|
|
95
|
+
result: ImportResult;
|
|
96
|
+
done: boolean;
|
|
97
|
+
cursor?: ChunkCursor;
|
|
98
|
+
chunk?: {
|
|
99
|
+
idMap?: Record<string, { id: string; collection: string }>;
|
|
100
|
+
translationGroups?: Record<string, string>;
|
|
101
|
+
commentRoots?: Record<string, string>;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function emptyImportResult(): ImportResult {
|
|
106
|
+
return { success: true, imported: 0, skipped: 0, errors: [], byCollection: {} };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseIdMap(idMap: Record<string, { id: string; collection: string }> | undefined): {
|
|
110
|
+
contentIdMap: Map<number, string>;
|
|
111
|
+
collectionByWpId: Map<number, string>;
|
|
112
|
+
} {
|
|
113
|
+
const contentIdMap = new Map<number, string>();
|
|
114
|
+
const collectionByWpId = new Map<number, string>();
|
|
115
|
+
for (const [key, value] of Object.entries(idMap ?? {})) {
|
|
116
|
+
const wpId = Number(key);
|
|
117
|
+
if (!Number.isFinite(wpId)) continue;
|
|
118
|
+
contentIdMap.set(wpId, value.id);
|
|
119
|
+
collectionByWpId.set(wpId, value.collection);
|
|
120
|
+
}
|
|
121
|
+
return { contentIdMap, collectionByWpId };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function serializeIdMap(
|
|
125
|
+
contentIdMap: Map<number, string>,
|
|
126
|
+
collectionByWpId: Map<number, string>,
|
|
127
|
+
): Record<string, { id: string; collection: string }> {
|
|
128
|
+
const out: Record<string, { id: string; collection: string }> = {};
|
|
129
|
+
for (const [wpId, id] of contentIdMap) {
|
|
130
|
+
const collection = collectionByWpId.get(wpId);
|
|
131
|
+
if (collection) out[String(wpId)] = { id, collection };
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
38
136
|
export const POST: APIRoute = async ({ request, locals }) => {
|
|
39
137
|
const { emdash, user } = locals;
|
|
40
138
|
|
|
@@ -81,8 +179,27 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
81
179
|
console.log("[WP Plugin Import] Starting import for:", body.url);
|
|
82
180
|
console.log("[WP Plugin Import] Post types:", postTypes);
|
|
83
181
|
|
|
182
|
+
// Chunked mode: one bounded unit of work per invocation (issue #475).
|
|
183
|
+
if (body.phase) {
|
|
184
|
+
const chunk = await runImportPhase(emdash, body, config, postTypes, emdashManifest);
|
|
185
|
+
return apiSuccess(chunk);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Pre-create taxonomy defs (for custom CPT taxonomies) and terms so
|
|
189
|
+
// per-post assignments can attach.
|
|
190
|
+
// Non-fatal: a site whose /taxonomies call fails still gets content.
|
|
191
|
+
let taxonomyPlan: TaxonomyImportPlan | undefined;
|
|
192
|
+
let taxonomyDefsCreated: string[] = [];
|
|
193
|
+
try {
|
|
194
|
+
const built = await buildTaxonomyPlan(emdash, body.url, body.token, config);
|
|
195
|
+
taxonomyPlan = built.plan;
|
|
196
|
+
taxonomyDefsCreated = built.defsCreated;
|
|
197
|
+
} catch (e) {
|
|
198
|
+
console.warn("[WP Plugin Import] Taxonomy pre-import failed:", e);
|
|
199
|
+
}
|
|
200
|
+
|
|
84
201
|
// Import content (including drafts since we have auth)
|
|
85
|
-
const result = await importContent(
|
|
202
|
+
const { result, contentIdMap, collectionByWpId } = await importContent(
|
|
86
203
|
source.fetchContent(
|
|
87
204
|
{ type: "url", url: body.url, token: body.token },
|
|
88
205
|
{ postTypes, includeDrafts: true },
|
|
@@ -90,8 +207,55 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
90
207
|
config,
|
|
91
208
|
emdash,
|
|
92
209
|
emdashManifest,
|
|
210
|
+
taxonomyPlan,
|
|
93
211
|
);
|
|
94
212
|
|
|
213
|
+
// Import navigation menus, resolving item references through the
|
|
214
|
+
// WP-post-ID -> EmDash-ID map collected during the content pass.
|
|
215
|
+
if (config.importMenus !== false) {
|
|
216
|
+
await importMenusInto(result, emdash, body.url, body.token, contentIdMap);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Import comments into EmDash's native comments table, preserving
|
|
220
|
+
// authors, dates, threading, and approval status.
|
|
221
|
+
// Non-fatal: older plugin versions have no /comments endpoint.
|
|
222
|
+
try {
|
|
223
|
+
const comments = await fetchPluginComments(body.url, body.token);
|
|
224
|
+
if (comments.length > 0) {
|
|
225
|
+
const commentsResult = await importCommentsFromPlugin(
|
|
226
|
+
comments,
|
|
227
|
+
emdash.db,
|
|
228
|
+
contentIdMap,
|
|
229
|
+
collectionByWpId,
|
|
230
|
+
);
|
|
231
|
+
result.comments = {
|
|
232
|
+
imported: commentsResult.imported,
|
|
233
|
+
skipped: commentsResult.skipped,
|
|
234
|
+
};
|
|
235
|
+
for (const commentError of commentsResult.errors) {
|
|
236
|
+
result.errors.push({
|
|
237
|
+
title: `Comment: ${commentError.comment}`,
|
|
238
|
+
error: commentError.error,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch (e) {
|
|
243
|
+
console.warn("[WP Plugin Import] Comment import failed:", e);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Apply site identity (title, tagline, logo, favicon) from the source
|
|
247
|
+
// site's options, replacing the starter template's seed placeholders.
|
|
248
|
+
// Non-fatal: content is already imported at this point.
|
|
249
|
+
try {
|
|
250
|
+
result.siteSettings = await applySiteSettings(emdash, body.url, body.token, config);
|
|
251
|
+
} catch (e) {
|
|
252
|
+
console.warn("[WP Plugin Import] Site settings import failed:", e);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (taxonomyDefsCreated.length > 0) {
|
|
256
|
+
result.taxonomiesCreated = taxonomyDefsCreated;
|
|
257
|
+
}
|
|
258
|
+
|
|
95
259
|
console.log("[WP Plugin Import] Import result:", JSON.stringify(result, null, 2));
|
|
96
260
|
|
|
97
261
|
return apiSuccess({
|
|
@@ -103,6 +267,213 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
103
267
|
}
|
|
104
268
|
};
|
|
105
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Import navigation menus into `result`, resolving item references through
|
|
272
|
+
* the WP-post-ID -> EmDash-ID map collected during the content pass.
|
|
273
|
+
* Non-fatal: older plugin versions have no /menus endpoint.
|
|
274
|
+
*/
|
|
275
|
+
async function importMenusInto(
|
|
276
|
+
result: ImportResult,
|
|
277
|
+
emdash: EmDashHandlers,
|
|
278
|
+
url: string,
|
|
279
|
+
token: string,
|
|
280
|
+
contentIdMap: Map<number, string>,
|
|
281
|
+
): Promise<void> {
|
|
282
|
+
try {
|
|
283
|
+
const menus = await fetchPluginMenus(url, token);
|
|
284
|
+
if (menus.length > 0) {
|
|
285
|
+
const menuResult = await importMenusFromPlugin(menus, emdash.db, contentIdMap);
|
|
286
|
+
result.menus = {
|
|
287
|
+
created: menuResult.menusCreated,
|
|
288
|
+
items: menuResult.itemsCreated,
|
|
289
|
+
};
|
|
290
|
+
for (const menuError of menuResult.errors) {
|
|
291
|
+
result.errors.push({ title: `Menu: ${menuError.menu}`, error: menuError.error });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
console.warn("[WP Plugin Import] Menu import failed:", e);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
type ExecuteBody = z.infer<typeof wpPluginExecuteBody>;
|
|
300
|
+
|
|
301
|
+
/** Dispatch one chunk of work for the requested phase. */
|
|
302
|
+
async function runImportPhase(
|
|
303
|
+
emdash: EmDashHandlers,
|
|
304
|
+
body: ExecuteBody,
|
|
305
|
+
config: WpPluginImportConfig,
|
|
306
|
+
postTypes: string[],
|
|
307
|
+
manifest: EmDashManifest,
|
|
308
|
+
): Promise<ChunkResponse> {
|
|
309
|
+
switch (body.phase) {
|
|
310
|
+
case "content":
|
|
311
|
+
return runContentChunk(emdash, body, config, postTypes, manifest);
|
|
312
|
+
case "comments":
|
|
313
|
+
return runCommentsChunk(emdash, body);
|
|
314
|
+
case "finalize":
|
|
315
|
+
return runFinalizePhase(emdash, body, config);
|
|
316
|
+
case undefined:
|
|
317
|
+
throw new Error("runImportPhase called without a phase");
|
|
318
|
+
default:
|
|
319
|
+
body.phase satisfies never;
|
|
320
|
+
throw new Error("Unknown import phase");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Import one page of one post type. The first chunk additionally runs the
|
|
326
|
+
* taxonomy setup (def + term creation); later chunks only reload the
|
|
327
|
+
* lookup maps from the database.
|
|
328
|
+
*/
|
|
329
|
+
async function runContentChunk(
|
|
330
|
+
emdash: EmDashHandlers,
|
|
331
|
+
body: ExecuteBody,
|
|
332
|
+
config: WpPluginImportConfig,
|
|
333
|
+
postTypes: string[],
|
|
334
|
+
manifest: EmDashManifest,
|
|
335
|
+
): Promise<ChunkResponse> {
|
|
336
|
+
const cursor = body.cursor ?? { postTypeIndex: 0, page: 1 };
|
|
337
|
+
const postType = postTypes[cursor.postTypeIndex];
|
|
338
|
+
if (!postType) {
|
|
339
|
+
return { success: true, result: emptyImportResult(), done: true };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const isFirstChunk = cursor.postTypeIndex === 0 && cursor.page === 1;
|
|
343
|
+
let taxonomyPlan: TaxonomyImportPlan | undefined;
|
|
344
|
+
let taxonomyDefsCreated: string[] = [];
|
|
345
|
+
try {
|
|
346
|
+
if (isFirstChunk) {
|
|
347
|
+
const built = await buildTaxonomyPlan(emdash, body.url, body.token, config);
|
|
348
|
+
taxonomyPlan = built.plan;
|
|
349
|
+
taxonomyDefsCreated = built.defsCreated;
|
|
350
|
+
} else {
|
|
351
|
+
taxonomyPlan = await loadTaxonomyPlanFromDb(emdash.db);
|
|
352
|
+
}
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.warn("[WP Plugin Import] Taxonomy pre-import failed:", e);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const page = await fetchPluginContentPage({
|
|
358
|
+
siteUrl: body.url,
|
|
359
|
+
token: body.token,
|
|
360
|
+
postType,
|
|
361
|
+
page: cursor.page,
|
|
362
|
+
perPage: CONTENT_CHUNK_SIZE,
|
|
363
|
+
includeDrafts: true,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Seed translation-group state from earlier chunks so a translation in
|
|
367
|
+
// this page links to its sibling imported three chunks ago.
|
|
368
|
+
const translationGroupMap = new Map(Object.entries(body.translationGroups ?? {}));
|
|
369
|
+
|
|
370
|
+
const { result, contentIdMap, collectionByWpId } = await importContent(
|
|
371
|
+
page.items,
|
|
372
|
+
config,
|
|
373
|
+
emdash,
|
|
374
|
+
manifest,
|
|
375
|
+
taxonomyPlan,
|
|
376
|
+
translationGroupMap,
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
if (taxonomyDefsCreated.length > 0) {
|
|
380
|
+
result.taxonomiesCreated = taxonomyDefsCreated;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Advance: next page of this post type, first page of the next one,
|
|
384
|
+
// or done. An empty last page (totalPages can shrink while paginating
|
|
385
|
+
// a live site) still terminates because page >= totalPages.
|
|
386
|
+
let next: ChunkCursor | undefined;
|
|
387
|
+
if (cursor.page < page.totalPages) {
|
|
388
|
+
next = { postTypeIndex: cursor.postTypeIndex, page: cursor.page + 1 };
|
|
389
|
+
} else if (cursor.postTypeIndex + 1 < postTypes.length) {
|
|
390
|
+
next = { postTypeIndex: cursor.postTypeIndex + 1, page: 1 };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
success: true,
|
|
395
|
+
result,
|
|
396
|
+
done: next === undefined,
|
|
397
|
+
cursor: next,
|
|
398
|
+
chunk: {
|
|
399
|
+
idMap: serializeIdMap(contentIdMap, collectionByWpId),
|
|
400
|
+
translationGroups: Object.fromEntries(translationGroupMap),
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Import one page of comments (500 per page, ordered by WP comment ID so
|
|
407
|
+
* parents precede children across pages). Requires the accumulated idMap
|
|
408
|
+
* from the content phase; threading roots accumulate in `commentRoots`.
|
|
409
|
+
*/
|
|
410
|
+
async function runCommentsChunk(emdash: EmDashHandlers, body: ExecuteBody): Promise<ChunkResponse> {
|
|
411
|
+
const page = body.cursor?.page ?? 1;
|
|
412
|
+
const result = emptyImportResult();
|
|
413
|
+
|
|
414
|
+
const { contentIdMap, collectionByWpId } = parseIdMap(body.idMap);
|
|
415
|
+
const rootIds = new Map<number, string>();
|
|
416
|
+
for (const [key, value] of Object.entries(body.commentRoots ?? {})) {
|
|
417
|
+
const wpId = Number(key);
|
|
418
|
+
if (Number.isFinite(wpId)) rootIds.set(wpId, value);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const { items, totalPages } = await fetchPluginCommentsPage(body.url, body.token, page);
|
|
422
|
+
|
|
423
|
+
if (items.length > 0) {
|
|
424
|
+
const commentsResult = await importCommentsFromPlugin(
|
|
425
|
+
items,
|
|
426
|
+
emdash.db,
|
|
427
|
+
contentIdMap,
|
|
428
|
+
collectionByWpId,
|
|
429
|
+
rootIds,
|
|
430
|
+
);
|
|
431
|
+
result.comments = {
|
|
432
|
+
imported: commentsResult.imported,
|
|
433
|
+
skipped: commentsResult.skipped,
|
|
434
|
+
};
|
|
435
|
+
for (const commentError of commentsResult.errors) {
|
|
436
|
+
result.errors.push({ title: `Comment: ${commentError.comment}`, error: commentError.error });
|
|
437
|
+
}
|
|
438
|
+
result.success = result.errors.length === 0;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const done = page >= totalPages;
|
|
442
|
+
const commentRoots: Record<string, string> = {};
|
|
443
|
+
for (const [wpId, id] of rootIds) commentRoots[String(wpId)] = id;
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
success: true,
|
|
447
|
+
result,
|
|
448
|
+
done,
|
|
449
|
+
cursor: done ? undefined : { postTypeIndex: 0, page: page + 1 },
|
|
450
|
+
chunk: { commentRoots },
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Menus + site identity — small, runs as a single closing chunk. */
|
|
455
|
+
async function runFinalizePhase(
|
|
456
|
+
emdash: EmDashHandlers,
|
|
457
|
+
body: ExecuteBody,
|
|
458
|
+
config: WpPluginImportConfig,
|
|
459
|
+
): Promise<ChunkResponse> {
|
|
460
|
+
const result = emptyImportResult();
|
|
461
|
+
const { contentIdMap } = parseIdMap(body.idMap);
|
|
462
|
+
|
|
463
|
+
if (config.importMenus !== false) {
|
|
464
|
+
await importMenusInto(result, emdash, body.url, body.token, contentIdMap);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
try {
|
|
468
|
+
result.siteSettings = await applySiteSettings(emdash, body.url, body.token, config);
|
|
469
|
+
} catch (e) {
|
|
470
|
+
console.warn("[WP Plugin Import] Site settings import failed:", e);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
result.success = result.errors.length === 0;
|
|
474
|
+
return { success: true, result, done: true };
|
|
475
|
+
}
|
|
476
|
+
|
|
106
477
|
/** Fields that should be auto-created if they don't exist */
|
|
107
478
|
const IMPORT_FIELDS: Array<{
|
|
108
479
|
slug: string;
|
|
@@ -134,14 +505,285 @@ const IMPORT_FIELDS: Array<{
|
|
|
134
505
|
type: "image",
|
|
135
506
|
check: (item) => !!item.featuredImage,
|
|
136
507
|
},
|
|
508
|
+
{
|
|
509
|
+
slug: "seo_title",
|
|
510
|
+
label: "SEO Title",
|
|
511
|
+
type: "string",
|
|
512
|
+
check: (item) => !!extractSeo(item).title,
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
slug: "seo_description",
|
|
516
|
+
label: "SEO Description",
|
|
517
|
+
type: "text",
|
|
518
|
+
check: (item) => !!extractSeo(item).description,
|
|
519
|
+
},
|
|
137
520
|
];
|
|
138
521
|
|
|
139
|
-
|
|
140
|
-
|
|
522
|
+
const SEO_FIELD_SLUGS = new Set(["seo_title", "seo_description"]);
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Coerce a WordPress meta value to an EmDash field type. WP postmeta is
|
|
526
|
+
* stringly typed and inconsistent across posts (the same key can hold
|
|
527
|
+
* "5", 5, "", or false). Returns `undefined` when the value can't
|
|
528
|
+
* reasonably represent the target type — the caller drops it.
|
|
529
|
+
*/
|
|
530
|
+
export function coerceToFieldType(value: unknown, fieldType: string): unknown {
|
|
531
|
+
switch (fieldType) {
|
|
532
|
+
case "integer": {
|
|
533
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
534
|
+
return Number.isInteger(n) ? n : undefined;
|
|
535
|
+
}
|
|
536
|
+
case "number": {
|
|
537
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
538
|
+
return Number.isFinite(n) ? n : undefined;
|
|
539
|
+
}
|
|
540
|
+
case "boolean": {
|
|
541
|
+
if (typeof value === "boolean") return value;
|
|
542
|
+
if (value === 1 || value === "1" || value === "true" || value === "yes") return true;
|
|
543
|
+
if (value === 0 || value === "0" || value === "false" || value === "no") return false;
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
case "datetime": {
|
|
547
|
+
if (typeof value !== "string" && typeof value !== "number") return undefined;
|
|
548
|
+
const d = new Date(value);
|
|
549
|
+
return Number.isNaN(d.getTime()) ? undefined : d.toISOString();
|
|
550
|
+
}
|
|
551
|
+
case "json":
|
|
552
|
+
// Anything serializes; objects/arrays pass through as-is
|
|
553
|
+
return value;
|
|
554
|
+
case "string":
|
|
555
|
+
case "text":
|
|
556
|
+
case "url":
|
|
557
|
+
case "select":
|
|
558
|
+
case "slug":
|
|
559
|
+
case "reference":
|
|
560
|
+
if (typeof value === "string") return value;
|
|
561
|
+
if (typeof value === "number") return String(value);
|
|
562
|
+
return undefined;
|
|
563
|
+
default:
|
|
564
|
+
// Complex types (image, portableText, repeater, multiSelect, file):
|
|
565
|
+
// raw meta can't be coerced reliably -- pass through only if it
|
|
566
|
+
// already has a non-primitive shape.
|
|
567
|
+
return typeof value === "object" ? value : undefined;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Pull the per-post SEO title/description out of the Yoast / Rank Math
|
|
573
|
+
* blobs the plugin source stashes in `item.meta`. Empty strings mean "not
|
|
574
|
+
* overridden for this post" (the plugin exports the raw meta values) and
|
|
575
|
+
* are treated as absent.
|
|
576
|
+
*/
|
|
577
|
+
function pickSeoValue(blob: unknown, key: string): string | undefined {
|
|
578
|
+
if (typeof blob !== "object" || blob === null) return undefined;
|
|
579
|
+
// eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowed to non-null object above
|
|
580
|
+
const value = (blob as Record<string, unknown>)[key];
|
|
581
|
+
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function extractSeo(item: NormalizedItem): { title?: string; description?: string } {
|
|
585
|
+
const yoast = item.meta?._yoast;
|
|
586
|
+
const rankmath = item.meta?._rankmath;
|
|
587
|
+
return {
|
|
588
|
+
title: pickSeoValue(yoast, "title") ?? pickSeoValue(rankmath, "title"),
|
|
589
|
+
description: pickSeoValue(yoast, "description") ?? pickSeoValue(rankmath, "description"),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Fetch the site's taxonomies from the plugin API and pre-create all terms,
|
|
595
|
+
* reusing the WXR taxonomy machinery (same def-lookup, idempotency, and
|
|
596
|
+
* collection-filter semantics).
|
|
597
|
+
*/
|
|
598
|
+
/** WP built-in taxonomies that either map to seeded defs or are not content taxonomies. */
|
|
599
|
+
const BUILTIN_TAXONOMIES = new Set(["category", "post_tag", "nav_menu", "post_format"]);
|
|
600
|
+
|
|
601
|
+
/** Mirrors NAME_PATTERN in the taxonomy handler -- names that fail stay in missingTaxonomies. */
|
|
602
|
+
const TAXONOMY_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Create EmDash taxonomy defs for custom WP taxonomies (e.g. CPT taxonomies
|
|
606
|
+
* like `company` or `plattform`) that don't exist yet, scoped to the
|
|
607
|
+
* collections the enabled post-type mappings target. Returns the names of
|
|
608
|
+
* the defs created. Runs before term pre-import so the terms and per-post
|
|
609
|
+
* assignments flow through the existing machinery instead of being dropped
|
|
610
|
+
* as `missingTaxonomies`.
|
|
611
|
+
*/
|
|
612
|
+
export async function ensureCustomTaxonomyDefs(
|
|
613
|
+
db: EmDashHandlers["db"],
|
|
614
|
+
taxonomies: Awaited<ReturnType<typeof fetchPluginTaxonomies>>,
|
|
615
|
+
config: WpPluginImportConfig,
|
|
616
|
+
): Promise<string[]> {
|
|
617
|
+
const created: string[] = [];
|
|
618
|
+
for (const taxonomy of taxonomies) {
|
|
619
|
+
if (BUILTIN_TAXONOMIES.has(taxonomy.name)) continue;
|
|
620
|
+
if (taxonomy.terms.length === 0) continue;
|
|
621
|
+
if (!TAXONOMY_NAME_PATTERN.test(taxonomy.name)) continue;
|
|
622
|
+
|
|
623
|
+
// handleTaxonomyCreate's duplicate guard is locale-scoped; check
|
|
624
|
+
// name-wide existence ourselves to stay idempotent on re-runs.
|
|
625
|
+
const existing = await db
|
|
626
|
+
.selectFrom("_emdash_taxonomy_defs")
|
|
627
|
+
.select("id")
|
|
628
|
+
.where("name", "=", taxonomy.name)
|
|
629
|
+
.executeTakeFirst();
|
|
630
|
+
if (existing) continue;
|
|
631
|
+
|
|
632
|
+
// Scope the def to the collections the WP post types map onto.
|
|
633
|
+
// Older plugin versions don't send post_types -> empty list = no
|
|
634
|
+
// collection filter, which the term machinery treats as "any".
|
|
635
|
+
const collections = (taxonomy.post_types ?? [])
|
|
636
|
+
.map((postType) => config.postTypeMappings[postType])
|
|
637
|
+
.filter((mapping) => mapping?.enabled)
|
|
638
|
+
.map((mapping) => mapping.collection);
|
|
639
|
+
|
|
640
|
+
const result = await handleTaxonomyCreate(db, {
|
|
641
|
+
name: taxonomy.name,
|
|
642
|
+
label: taxonomy.label,
|
|
643
|
+
labelSingular: taxonomy.label_singular,
|
|
644
|
+
hierarchical: taxonomy.hierarchical,
|
|
645
|
+
collections: [...new Set(collections)],
|
|
646
|
+
});
|
|
647
|
+
if (result.success) {
|
|
648
|
+
created.push(taxonomy.name);
|
|
649
|
+
} else {
|
|
650
|
+
console.warn(
|
|
651
|
+
`[WP Plugin Import] Could not create taxonomy '${taxonomy.name}':`,
|
|
652
|
+
result.error.message,
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return created;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
async function buildTaxonomyPlan(
|
|
660
|
+
emdash: EmDashHandlers,
|
|
661
|
+
url: string,
|
|
662
|
+
token: string,
|
|
663
|
+
config: WpPluginImportConfig,
|
|
664
|
+
): Promise<{ plan: TaxonomyImportPlan; defsCreated: string[] }> {
|
|
665
|
+
const taxonomies = await fetchPluginTaxonomies(url, token);
|
|
666
|
+
|
|
667
|
+
const defsCreated = await ensureCustomTaxonomyDefs(emdash.db, taxonomies, config);
|
|
668
|
+
|
|
669
|
+
const categories: WxrCategory[] = [];
|
|
670
|
+
const tags: WxrTag[] = [];
|
|
671
|
+
const terms: WxrTerm[] = [];
|
|
672
|
+
|
|
673
|
+
for (const taxonomy of taxonomies) {
|
|
674
|
+
for (const term of taxonomy.terms) {
|
|
675
|
+
if (taxonomy.name === "category") {
|
|
676
|
+
categories.push({ nicename: term.slug, name: term.name, description: term.description });
|
|
677
|
+
} else if (taxonomy.name === "post_tag") {
|
|
678
|
+
tags.push({ slug: term.slug, name: term.name, description: term.description });
|
|
679
|
+
} else if (taxonomy.name !== "nav_menu" && taxonomy.name !== "post_format") {
|
|
680
|
+
terms.push({
|
|
681
|
+
id: term.id,
|
|
682
|
+
taxonomy: taxonomy.name,
|
|
683
|
+
slug: term.slug,
|
|
684
|
+
name: term.name,
|
|
685
|
+
description: term.description,
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const plan = await preImportWxrTaxonomies(emdash.db, [], categories, tags, terms, undefined);
|
|
692
|
+
return { plan, defsCreated };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Fetch the source site's options and apply its identity (title, tagline,
|
|
697
|
+
* logo, favicon) as EmDash site settings, overwriting seed placeholders.
|
|
698
|
+
* Logo/favicon files are side-loaded into media storage first; the later
|
|
699
|
+
* full media pass dedupes them by content hash.
|
|
700
|
+
*
|
|
701
|
+
* Returns the list of applied setting keys.
|
|
702
|
+
*/
|
|
703
|
+
async function applySiteSettings(
|
|
704
|
+
emdash: EmDashHandlers,
|
|
705
|
+
url: string,
|
|
706
|
+
token: string,
|
|
707
|
+
config: WpPluginImportConfig,
|
|
708
|
+
): Promise<string[]> {
|
|
709
|
+
const wantTitle = config.importSiteTitle !== false;
|
|
710
|
+
const wantLogo = config.importLogo !== false;
|
|
711
|
+
if (!wantTitle && !wantLogo) return [];
|
|
712
|
+
|
|
713
|
+
const options = await fetchPluginOptions(url, token);
|
|
714
|
+
const parsed = parseSiteSettingsFromPlugin(options);
|
|
715
|
+
if (!wantTitle) {
|
|
716
|
+
delete parsed.title;
|
|
717
|
+
delete parsed.tagline;
|
|
718
|
+
}
|
|
719
|
+
if (!wantLogo) {
|
|
720
|
+
delete parsed.logo;
|
|
721
|
+
delete parsed.favicon;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const media: { logoMediaId?: string; faviconMediaId?: string } = {};
|
|
725
|
+
if (emdash.storage && (parsed.logo?.url || parsed.favicon?.url)) {
|
|
726
|
+
const attachments = [];
|
|
727
|
+
if (parsed.logo?.url) {
|
|
728
|
+
attachments.push({ id: parsed.logo.id, url: parsed.logo.url });
|
|
729
|
+
}
|
|
730
|
+
if (parsed.favicon?.url && parsed.favicon.url !== parsed.logo?.url) {
|
|
731
|
+
attachments.push({ id: parsed.favicon.id, url: parsed.favicon.url });
|
|
732
|
+
}
|
|
733
|
+
const mediaResult = await importMediaWithProgress(
|
|
734
|
+
attachments,
|
|
735
|
+
emdash.db,
|
|
736
|
+
emdash.storage,
|
|
737
|
+
() => {},
|
|
738
|
+
);
|
|
739
|
+
for (const item of mediaResult.imported) {
|
|
740
|
+
if (item.originalUrl === parsed.logo?.url) {
|
|
741
|
+
media.logoMediaId = item.mediaId;
|
|
742
|
+
}
|
|
743
|
+
if (item.originalUrl === parsed.favicon?.url) {
|
|
744
|
+
media.faviconMediaId = item.mediaId;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const settingsResult = await importSiteSettings(parsed, emdash.db, true, media);
|
|
750
|
+
for (const settingError of settingsResult.errors) {
|
|
751
|
+
console.warn(
|
|
752
|
+
`[WP Plugin Import] Site setting "${settingError.setting}" failed:`,
|
|
753
|
+
settingError.error,
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
return settingsResult.applied;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Adapt a NormalizedItem's taxonomy assignments to the WxrPost shape the
|
|
761
|
+
* shared attach helper consumes.
|
|
762
|
+
*/
|
|
763
|
+
function toWxrAssignments(item: NormalizedItem): WxrPost {
|
|
764
|
+
return {
|
|
765
|
+
categories: item.categories ?? [],
|
|
766
|
+
tags: item.tags ?? [],
|
|
767
|
+
customTaxonomies: item.customTaxonomies
|
|
768
|
+
? new Map(Object.entries(item.customTaxonomies))
|
|
769
|
+
: undefined,
|
|
770
|
+
meta: new Map(),
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/** Exported for tests (field auto-creation regression coverage). */
|
|
775
|
+
export async function importContent(
|
|
776
|
+
items: AsyncIterable<NormalizedItem> | Iterable<NormalizedItem>,
|
|
141
777
|
config: WpPluginImportConfig,
|
|
142
778
|
emdash: EmDashHandlers,
|
|
143
779
|
manifest: EmDashManifest,
|
|
144
|
-
|
|
780
|
+
taxonomyPlan: TaxonomyImportPlan | undefined,
|
|
781
|
+
seedTranslationGroups?: Map<string, string>,
|
|
782
|
+
): Promise<{
|
|
783
|
+
result: ImportResult;
|
|
784
|
+
contentIdMap: Map<number, string>;
|
|
785
|
+
collectionByWpId: Map<number, string>;
|
|
786
|
+
}> {
|
|
145
787
|
const result: ImportResult = {
|
|
146
788
|
success: true,
|
|
147
789
|
imported: 0,
|
|
@@ -150,19 +792,32 @@ async function importContent(
|
|
|
150
792
|
byCollection: {},
|
|
151
793
|
};
|
|
152
794
|
|
|
795
|
+
// WP post ID -> EmDash content ID, used to resolve menu item references
|
|
796
|
+
const contentIdMap = new Map<number, string>();
|
|
797
|
+
|
|
798
|
+
// WP post ID -> EmDash collection slug, used by the comment import
|
|
799
|
+
const collectionByWpId = new Map<number, string>();
|
|
800
|
+
|
|
153
801
|
// Create content repository for checking existing items
|
|
154
802
|
const contentRepo = new ContentRepository(emdash.db);
|
|
155
803
|
const bylineRepo = new BylineRepository(emdash.db);
|
|
156
804
|
const bylineCache = new Map<string, string>();
|
|
157
805
|
const schemaRegistry = new SchemaRegistry(emdash.db);
|
|
158
806
|
|
|
159
|
-
// Track which
|
|
160
|
-
|
|
807
|
+
// Track which (collection, field) pairs have been ensured. Keyed per
|
|
808
|
+
// field, not per collection: a field like seo_title may first be
|
|
809
|
+
// needed by the 30th item, and gating on the first item would skip it.
|
|
810
|
+
const ensuredFields = new Set<string>();
|
|
811
|
+
|
|
812
|
+
// Field slug -> type per collection, for mapping custom meta/ACF onto
|
|
813
|
+
// real fields (and coercing values to the field's type)
|
|
814
|
+
const fieldTypesByCollection = new Map<string, Map<string, string>>();
|
|
161
815
|
|
|
162
816
|
// Track source translationGroup -> EmDash item ID for translation linking.
|
|
163
817
|
// Maps source-side translation group ID to the EmDash ID of the first item
|
|
164
|
-
// imported for that group (the default-locale item).
|
|
165
|
-
|
|
818
|
+
// imported for that group (the default-locale item). The chunked import
|
|
819
|
+
// seeds this from earlier chunks so groups can span page boundaries.
|
|
820
|
+
const translationGroupMap = seedTranslationGroups ?? new Map<string, string>();
|
|
166
821
|
|
|
167
822
|
for await (const item of items) {
|
|
168
823
|
console.log("[WP Plugin Import] Processing item:", {
|
|
@@ -196,33 +851,51 @@ async function importContent(
|
|
|
196
851
|
}
|
|
197
852
|
|
|
198
853
|
try {
|
|
199
|
-
// Ensure required fields exist in the collection schema
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
854
|
+
// Ensure required fields exist in the collection schema. Checked
|
|
855
|
+
// per field and item (not once per collection): whether a field
|
|
856
|
+
// is needed depends on the item — the first post may have no SEO
|
|
857
|
+
// override while a later one does.
|
|
858
|
+
for (const field of IMPORT_FIELDS) {
|
|
859
|
+
if (config.importSeo === false && SEO_FIELD_SLUGS.has(field.slug)) continue;
|
|
860
|
+
const ensureKey = `${collection}:${field.slug}`;
|
|
861
|
+
if (ensuredFields.has(ensureKey) || !field.check(item)) continue;
|
|
862
|
+
ensuredFields.add(ensureKey);
|
|
863
|
+
const existingField = await schemaRegistry.getField(collection, field.slug);
|
|
864
|
+
if (!existingField) {
|
|
865
|
+
console.log(
|
|
866
|
+
`[WP Plugin Import] Creating missing field "${field.slug}" in collection "${collection}"`,
|
|
867
|
+
);
|
|
868
|
+
try {
|
|
869
|
+
await schemaRegistry.createField(collection, {
|
|
870
|
+
slug: field.slug,
|
|
871
|
+
label: field.label,
|
|
872
|
+
type: field.type,
|
|
873
|
+
required: false,
|
|
874
|
+
});
|
|
875
|
+
fieldTypesByCollection.get(collection)?.set(field.slug, field.type);
|
|
876
|
+
} catch (e) {
|
|
877
|
+
// Field might already exist from concurrent creation
|
|
878
|
+
console.log(
|
|
879
|
+
`[WP Plugin Import] Field "${field.slug}" creation skipped:`,
|
|
880
|
+
e instanceof Error ? e.message : e,
|
|
881
|
+
);
|
|
223
882
|
}
|
|
224
883
|
}
|
|
225
|
-
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Load the collection's field types once, so custom meta / ACF
|
|
887
|
+
// values can land in matching schema fields (created by the
|
|
888
|
+
// prepare step or already present).
|
|
889
|
+
let fieldTypes = fieldTypesByCollection.get(collection);
|
|
890
|
+
if (!fieldTypes) {
|
|
891
|
+
fieldTypes = new Map<string, string>();
|
|
892
|
+
const collectionDef = await schemaRegistry.getCollection(collection);
|
|
893
|
+
if (collectionDef) {
|
|
894
|
+
for (const field of await schemaRegistry.listFields(collectionDef.id)) {
|
|
895
|
+
fieldTypes.set(field.slug, field.type);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
fieldTypesByCollection.set(collection, fieldTypes);
|
|
226
899
|
}
|
|
227
900
|
|
|
228
901
|
// Generate slug from item slug or title
|
|
@@ -236,6 +909,12 @@ async function importContent(
|
|
|
236
909
|
if (item.translationGroup) {
|
|
237
910
|
translationGroupMap.set(item.translationGroup, existing.id);
|
|
238
911
|
}
|
|
912
|
+
// Menus and comments may reference this post even when we skip it
|
|
913
|
+
const wpId = Number(item.sourceId);
|
|
914
|
+
if (Number.isFinite(wpId)) {
|
|
915
|
+
contentIdMap.set(wpId, existing.id);
|
|
916
|
+
collectionByWpId.set(wpId, collection);
|
|
917
|
+
}
|
|
239
918
|
result.skipped++;
|
|
240
919
|
continue;
|
|
241
920
|
}
|
|
@@ -259,8 +938,49 @@ async function importContent(
|
|
|
259
938
|
console.log("[WP Plugin Import] Adding featured_image:", item.featuredImage);
|
|
260
939
|
}
|
|
261
940
|
|
|
262
|
-
//
|
|
263
|
-
|
|
941
|
+
// Per-post SEO overrides from Yoast / Rank Math
|
|
942
|
+
if (config.importSeo !== false) {
|
|
943
|
+
const seo = extractSeo(item);
|
|
944
|
+
if (seo.title) {
|
|
945
|
+
data.seo_title = seo.title;
|
|
946
|
+
}
|
|
947
|
+
if (seo.description) {
|
|
948
|
+
data.seo_description = seo.description;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Map ACF values and custom meta onto schema fields with the same
|
|
953
|
+
// (sanitized) slug — the same sanitization the analysis applied
|
|
954
|
+
// when suggesting the fields, so keys like `event-date` land in
|
|
955
|
+
// the `event_date` field the prepare step created. Values without
|
|
956
|
+
// a matching field are dropped -- the user controls the schema,
|
|
957
|
+
// we don't invent fields per meta key. Values are coerced to the
|
|
958
|
+
// field's type (the analysis inferred it from ONE sample; real
|
|
959
|
+
// values vary) and dropped when incoercible, so one odd meta
|
|
960
|
+
// value can't fail the whole item.
|
|
961
|
+
const assignMetaValue = (key: string, value: unknown) => {
|
|
962
|
+
if (value === null || value === "") return;
|
|
963
|
+
const fieldSlug = sanitizeFieldSlug(key);
|
|
964
|
+
const fieldType = fieldTypes.get(fieldSlug);
|
|
965
|
+
if (!fieldType || fieldSlug in data) return;
|
|
966
|
+
const coerced = coerceToFieldType(value, fieldType);
|
|
967
|
+
if (coerced !== undefined) {
|
|
968
|
+
data[fieldSlug] = coerced;
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
const acf = item.meta?._acf;
|
|
972
|
+
if (typeof acf === "object" && acf !== null) {
|
|
973
|
+
for (const [key, value] of Object.entries(acf)) {
|
|
974
|
+
assignMetaValue(key, value);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (item.meta) {
|
|
978
|
+
for (const [key, value] of Object.entries(item.meta)) {
|
|
979
|
+
// Underscore keys are WP-internal or handled above (_acf/_yoast/_rankmath)
|
|
980
|
+
if (key.startsWith("_")) continue;
|
|
981
|
+
assignMetaValue(key, value);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
264
984
|
|
|
265
985
|
// Resolve author ID from mappings
|
|
266
986
|
let authorId: string | undefined;
|
|
@@ -314,13 +1034,39 @@ async function importContent(
|
|
|
314
1034
|
result.imported++;
|
|
315
1035
|
result.byCollection[collection] = (result.byCollection[collection] || 0) + 1;
|
|
316
1036
|
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
1037
|
+
// eslint-disable-next-line typescript/no-unsafe-type-assertion -- create handler returns { item, _rev }
|
|
1038
|
+
const createdData = createResult.data as { item?: { id?: string } } | undefined;
|
|
1039
|
+
const createdId = createdData?.item?.id;
|
|
1040
|
+
|
|
1041
|
+
if (createdId) {
|
|
1042
|
+
const wpId = Number(item.sourceId);
|
|
1043
|
+
if (Number.isFinite(wpId)) {
|
|
1044
|
+
contentIdMap.set(wpId, createdId);
|
|
1045
|
+
collectionByWpId.set(wpId, collection);
|
|
323
1046
|
}
|
|
1047
|
+
|
|
1048
|
+
// Attach category/tag/custom-taxonomy assignments
|
|
1049
|
+
if (taxonomyPlan) {
|
|
1050
|
+
try {
|
|
1051
|
+
const attached = await attachPostTaxonomies(
|
|
1052
|
+
emdash.db,
|
|
1053
|
+
collection,
|
|
1054
|
+
createdId,
|
|
1055
|
+
toWxrAssignments(item),
|
|
1056
|
+
taxonomyPlan,
|
|
1057
|
+
);
|
|
1058
|
+
if (attached > 0) {
|
|
1059
|
+
result.taxonomyAssignments = (result.taxonomyAssignments ?? 0) + attached;
|
|
1060
|
+
}
|
|
1061
|
+
} catch (e) {
|
|
1062
|
+
console.warn(`[WP Plugin Import] Taxonomy attach failed for "${slug}":`, e);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Track translation group: first item in a group establishes the mapping
|
|
1068
|
+
if (item.translationGroup && !translationGroupMap.has(item.translationGroup) && createdId) {
|
|
1069
|
+
translationGroupMap.set(item.translationGroup, createdId);
|
|
324
1070
|
}
|
|
325
1071
|
} else {
|
|
326
1072
|
result.errors.push({
|
|
@@ -340,8 +1086,12 @@ async function importContent(
|
|
|
340
1086
|
}
|
|
341
1087
|
}
|
|
342
1088
|
|
|
1089
|
+
if (taxonomyPlan && taxonomyPlan.missingTaxonomies.length > 0) {
|
|
1090
|
+
result.missingTaxonomies = taxonomyPlan.missingTaxonomies;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
343
1093
|
result.success = result.errors.length === 0;
|
|
344
|
-
return result;
|
|
1094
|
+
return { result, contentIdMap, collectionByWpId };
|
|
345
1095
|
}
|
|
346
1096
|
|
|
347
1097
|
function mapStatus(wpStatus: string | undefined): string {
|