emdash 0.40.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/route-utils.mjs +6 -6
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/{api-cFFZpyS0.mjs → api-CuyNEHZG.mjs} +3 -3
- package/dist/{api-cFFZpyS0.mjs.map → api-CuyNEHZG.mjs.map} +1 -1
- package/dist/{apply-CoT603H3.mjs → apply-npY1TAhG.mjs} +237 -34
- package/dist/apply-npY1TAhG.mjs.map +1 -0
- package/dist/astro/index.d.mts +3 -3
- package/dist/astro/index.mjs +9 -9
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +3 -3
- package/dist/astro/middleware/redirect.mjs +1 -1
- package/dist/astro/middleware.d.mts +3 -3
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +19 -18
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/progress.mjs +2 -2
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +11 -11
- package/dist/astro/routes/api/admin/plugins/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +11 -11
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +12 -12
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +9 -9
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +9 -9
- package/dist/astro/routes/api/admin/plugins/updates.mjs +8 -8
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +8 -8
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/approvals/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/capabilities.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/index.mjs +3 -3
- package/dist/astro/routes/api/dashboard.mjs +2 -2
- package/dist/astro/routes/api/health.mjs +1 -1
- package/dist/astro/routes/api/import/probe.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress/media.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +3 -3
- package/dist/astro/routes/api/manifest.mjs +1 -1
- package/dist/astro/routes/api/mcp.mjs +13 -33
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +1 -1
- package/dist/astro/routes/api/media/_id_/upload.mjs +1 -1
- package/dist/astro/routes/api/media/_id_/usage.mjs +2 -2
- package/dist/astro/routes/api/media/_id_.mjs +2 -2
- package/dist/astro/routes/api/media/asset/_id_/_filename_.mjs +1 -1
- package/dist/astro/routes/api/media/upload-url.mjs +1 -1
- package/dist/astro/routes/api/media.mjs +2 -2
- package/dist/astro/routes/api/redirects/404s/index.mjs +2 -2
- package/dist/astro/routes/api/redirects/404s/summary.mjs +2 -2
- package/dist/astro/routes/api/redirects/_id_.mjs +2 -2
- package/dist/astro/routes/api/redirects/index.mjs +2 -2
- package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs +8 -8
- package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs +8 -8
- package/dist/astro/routes/api/schema/block-types/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/reorder.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/index.mjs +8 -8
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/export.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/index.mjs +2 -2
- package/dist/astro/routes/api/settings.mjs +3 -3
- package/dist/astro/routes/api/setup/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/index.mjs +35 -10
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/snapshot.mjs +2 -2
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/robots.txt.mjs +2 -2
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +2 -2
- package/dist/astro/routes/sitemap.xml.mjs +2 -2
- package/dist/astro/types.d.mts +7 -3
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{backup-CBu0LrXf.mjs → backup-BQRj3afm.mjs} +3 -3
- package/dist/{backup-CBu0LrXf.mjs.map → backup-BQRj3afm.mjs.map} +1 -1
- package/dist/cli/index.mjs +6 -6
- package/dist/client/index.d.mts +14 -14
- package/dist/{config-5ii2haYu.d.mts → config-BjEm_3xL.d.mts} +25 -16
- package/dist/config-BjEm_3xL.d.mts.map +1 -0
- package/dist/{context-BeGccdUX.mjs → context-BV_7t-Ua.mjs} +6 -6
- package/dist/{context-BeGccdUX.mjs.map → context-BV_7t-Ua.mjs.map} +1 -1
- package/dist/{dashboard-DjrB-xxZ.mjs → dashboard-BP-iJqpd.mjs} +2 -2
- package/dist/{dashboard-DjrB-xxZ.mjs.map → dashboard-BP-iJqpd.mjs.map} +1 -1
- package/dist/db/libsql-migrations.mjs +2 -2
- package/dist/db/postgres-migrations.mjs +2 -2
- package/dist/db/sqlite-migrations.mjs +2 -2
- package/dist/{direct-executor-BmuFJJZV.mjs → direct-executor-D2lNadwo.mjs} +2 -2
- package/dist/{direct-executor-BmuFJJZV.mjs.map → direct-executor-D2lNadwo.mjs.map} +1 -1
- package/dist/{emdash-runtime-DAjghyEG.mjs → emdash-runtime-CtfnZmPi.mjs} +56 -56
- package/dist/emdash-runtime-CtfnZmPi.mjs.map +1 -0
- package/dist/{entry-lock-DVhCt49A.d.mts → entry-lock-Cf5OoIgN.d.mts} +18 -18
- package/dist/{entry-lock-DVhCt49A.d.mts.map → entry-lock-Cf5OoIgN.d.mts.map} +1 -1
- package/dist/{hreflang-DMFBL2eb.mjs → hreflang-CbVI4paR.mjs} +2 -2
- package/dist/{hreflang-DMFBL2eb.mjs.map → hreflang-CbVI4paR.mjs.map} +1 -1
- package/dist/{identity-IyNWwXr6.mjs → identity-Be5kzutJ.mjs} +2 -2
- package/dist/{identity-IyNWwXr6.mjs.map → identity-Be5kzutJ.mjs.map} +1 -1
- package/dist/{import-C3JZplWq.mjs → import-YzfKCaco.mjs} +2 -2
- package/dist/{import-C3JZplWq.mjs.map → import-YzfKCaco.mjs.map} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +16 -16
- package/dist/{manifest-builder-B8zR-73J.mjs → manifest-builder-BmSAOOEF.mjs} +2 -2
- package/dist/{manifest-builder-B8zR-73J.mjs.map → manifest-builder-BmSAOOEF.mjs.map} +1 -1
- package/dist/media/local-runtime.d.mts +3 -3
- package/dist/media/local-runtime.mjs +3 -3
- package/dist/{media-Qp5S3eH3.mjs → media-CPXTbb1R.mjs} +20 -2
- package/dist/media-CPXTbb1R.mjs.map +1 -0
- package/dist/{media-h7tOiDLj.mjs → media-JXRWDhw3.mjs} +77 -4
- package/dist/media-JXRWDhw3.mjs.map +1 -0
- package/dist/{media-usage-My-Vi-ex.mjs → media-usage-De-zeR9E.mjs} +2 -2
- package/dist/{media-usage-My-Vi-ex.mjs.map → media-usage-De-zeR9E.mjs.map} +1 -1
- package/dist/{menus-TsSGmSR4.mjs → menus-Cm68rsAq.mjs} +4 -4
- package/dist/{menus-TsSGmSR4.mjs.map → menus-Cm68rsAq.mjs.map} +1 -1
- package/dist/migrations/index.mjs +2 -2
- package/dist/page/index.mjs +2 -2
- package/dist/plugin-test-runtime.d.mts +3 -3
- package/dist/plugin-test-runtime.mjs +18 -18
- package/dist/plugin-utils.d.mts +3 -3
- package/dist/plugins/adapt-sandbox-entry.d.mts +3 -3
- package/dist/plugins/host.d.mts +1 -1
- package/dist/plugins/host.mjs +6 -6
- package/dist/{redirect-iJf5fltD.mjs → redirect-B29UjdUn.mjs} +10 -14
- package/dist/redirect-B29UjdUn.mjs.map +1 -0
- package/dist/{redirects-CYu1u1aK.mjs → redirects-Ca2MPfEG.mjs} +2 -2
- package/dist/{redirects-CYu1u1aK.mjs.map → redirects-Ca2MPfEG.mjs.map} +1 -1
- package/dist/{routes-CUsM40Cc.mjs → routes-vrR95WR6.mjs} +2 -2
- package/dist/{routes-CUsM40Cc.mjs.map → routes-vrR95WR6.mjs.map} +1 -1
- package/dist/runtime.d.mts +3 -3
- package/dist/seed/index.mjs +4 -4
- package/dist/seo/index.mjs +3 -3
- package/dist/{settings-CgjRWGW9.mjs → settings-B4vDl1fK.mjs} +2 -2
- package/dist/{settings-CgjRWGW9.mjs.map → settings-B4vDl1fK.mjs.map} +1 -1
- package/dist/{settings-2xNYr90o.mjs → settings-Dyj6v8C4.mjs} +2 -2
- package/dist/{settings-2xNYr90o.mjs.map → settings-Dyj6v8C4.mjs.map} +1 -1
- package/dist/{snapshot-CugwUqKQ.mjs → snapshot-PawoX-WG.mjs} +180 -3
- package/dist/snapshot-PawoX-WG.mjs.map +1 -0
- package/dist/{storage-query-Bdv7A62k.d.mts → storage-query-FXJH5pI8.d.mts} +6 -1
- package/dist/storage-query-FXJH5pI8.d.mts.map +1 -0
- package/dist/{transfer-BhUCrgDc.mjs → transfer-DgeCvHnj.mjs} +4 -4
- package/dist/{transfer-BhUCrgDc.mjs.map → transfer-DgeCvHnj.mjs.map} +1 -1
- package/dist/validate-CM79QXYE.d.mts.map +1 -1
- package/dist/version-DXsxRrob.mjs +7 -0
- package/dist/{version-DUQDHgrf.mjs.map → version-DXsxRrob.mjs.map} +1 -1
- package/package.json +5 -5
- package/src/api/handlers/index.ts +1 -0
- package/src/api/handlers/media-upload.ts +17 -71
- package/src/api/handlers/media.ts +97 -3
- package/src/api/handlers/snapshot.ts +257 -3
- package/src/astro/integration/vite-config.ts +10 -4
- package/src/astro/middleware.ts +1 -0
- package/src/astro/routes/api/setup/index.ts +66 -23
- package/src/astro/routes/api/snapshot.ts +2 -1
- package/src/astro/types.ts +5 -0
- package/src/cleanup.ts +10 -0
- package/src/components/Columns.astro +3 -1
- package/src/components/Cover.astro +3 -1
- package/src/database/repositories/media.ts +40 -1
- package/src/database/repositories/redirect.ts +17 -34
- package/src/emdash-runtime.ts +28 -0
- package/src/index.ts +1 -0
- package/src/mcp/server.ts +13 -48
- package/src/seed/apply.ts +322 -41
- package/dist/apply-CoT603H3.mjs.map +0 -1
- package/dist/config-5ii2haYu.d.mts.map +0 -1
- package/dist/emdash-runtime-DAjghyEG.mjs.map +0 -1
- package/dist/media-Qp5S3eH3.mjs.map +0 -1
- package/dist/media-h7tOiDLj.mjs.map +0 -1
- package/dist/redirect-iJf5fltD.mjs.map +0 -1
- package/dist/snapshot-CugwUqKQ.mjs.map +0 -1
- package/dist/storage-query-Bdv7A62k.d.mts.map +0 -1
- package/dist/version-DUQDHgrf.mjs +0 -7
|
@@ -2,10 +2,10 @@ import "../../parse-CVgO-Qai.mjs";
|
|
|
2
2
|
import "../../types-B7Lmsh1E.mjs";
|
|
3
3
|
import "../../types-Cr-Kg-3D.mjs";
|
|
4
4
|
import "../../options-SQCHQCPH.mjs";
|
|
5
|
-
import "../../entry-lock-
|
|
6
|
-
import "../../config-
|
|
5
|
+
import "../../entry-lock-Cf5OoIgN.mjs";
|
|
6
|
+
import "../../config-BjEm_3xL.mjs";
|
|
7
7
|
import "../../runner-D5_LzKXI.mjs";
|
|
8
|
-
import "../../storage-query-
|
|
8
|
+
import "../../storage-query-FXJH5pI8.mjs";
|
|
9
9
|
import "../../index-D-hdtLe8.mjs";
|
|
10
10
|
import "../../types-Cd-O03VG.mjs";
|
|
11
11
|
import "../../hreflang-D5uNnsGJ.mjs";
|
|
@@ -4,7 +4,7 @@ import "../../after-PcEvMY_6.mjs";
|
|
|
4
4
|
import "../../base64-DvnNgP7e.mjs";
|
|
5
5
|
import "../../types-BkUg2yHx.mjs";
|
|
6
6
|
import "../../resolve-2WbQsCvT.mjs";
|
|
7
|
-
import { t as RedirectRepository } from "../../redirect-
|
|
7
|
+
import { t as RedirectRepository } from "../../redirect-B29UjdUn.mjs";
|
|
8
8
|
import "../../request-cache-DzLYKm_x.mjs";
|
|
9
9
|
import { i as matchCachedPatterns, r as loadCachedRedirects } from "../../cache-m7UEkRFV.mjs";
|
|
10
10
|
import { s as getDb } from "../../loader-BzqeR1ZN.mjs";
|
|
@@ -2,10 +2,10 @@ import "../parse-CVgO-Qai.mjs";
|
|
|
2
2
|
import "../types-B7Lmsh1E.mjs";
|
|
3
3
|
import "../types-Cr-Kg-3D.mjs";
|
|
4
4
|
import "../options-SQCHQCPH.mjs";
|
|
5
|
-
import "../entry-lock-
|
|
6
|
-
import { cr as EmDashRuntime, gr as PluginContentCacheInvalidator, pr as PublishedRef } from "../config-
|
|
5
|
+
import "../entry-lock-Cf5OoIgN.mjs";
|
|
6
|
+
import { cr as EmDashRuntime, gr as PluginContentCacheInvalidator, pr as PublishedRef } from "../config-BjEm_3xL.mjs";
|
|
7
7
|
import "../runner-D5_LzKXI.mjs";
|
|
8
|
-
import "../storage-query-
|
|
8
|
+
import "../storage-query-FXJH5pI8.mjs";
|
|
9
9
|
import "../index-D-hdtLe8.mjs";
|
|
10
10
|
import "../types-Cd-O03VG.mjs";
|
|
11
11
|
import "../hreflang-D5uNnsGJ.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.mts","names":[],"sources":["../../src/astro/middleware.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;AAySA;;;;;;;;;iBAAsB,iBAAA,CACrB,OAAA;EACC,WAAA,IAAe,IAAA,EAAM,YAAA,OAAmB,OAAA;EACxC,sBAAA,GAAyB,6BAAA;AAAA,IAExB,OAAA;EAAU,SAAA,EAAW,YAAA;AAAA;;;;;;;;AAyCxB;;;;;;;;;;;;;;;;;;;;;;AAuSA;;;;;;iBAvSsB,iBAAA,GAAA,CACrB,GAAA,GAAM,OAAA,EAAS,aAAA,KAAkB,CAAA,GAAI,OAAA,CAAQ,CAAA,IAC3C,OAAA,CAAQ,CAAA;AAAA,cAqSE,SAAA,
|
|
1
|
+
{"version":3,"file":"middleware.d.mts","names":[],"sources":["../../src/astro/middleware.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;AAySA;;;;;;;;;iBAAsB,iBAAA,CACrB,OAAA;EACC,WAAA,IAAe,IAAA,EAAM,YAAA,OAAmB,OAAA;EACxC,sBAAA,GAAyB,6BAAA;AAAA,IAExB,OAAA;EAAU,SAAA,EAAW,YAAA;AAAA;;;;;;;;AAyCxB;;;;;;;;;;;;;;;;;;;;;;AAuSA;;;;;;iBAvSsB,iBAAA,GAAA,CACrB,GAAA,GAAM,OAAA,EAAS,aAAA,KAAkB,CAAA,GAAI,OAAA,CAAQ,CAAA,IAC3C,OAAA,CAAQ,CAAA;AAAA,cAqSE,SAAA,EA0eX,KAAA,CA1eoB,iBAAA"}
|
|
@@ -3,7 +3,7 @@ import { n as isMissingTableError } from "../db-errors-CBD5yMro.mjs";
|
|
|
3
3
|
import "../dialect-helpers-7_IqPdh9.mjs";
|
|
4
4
|
import { c as setI18nConfig } from "../config-CvmjJcVw.mjs";
|
|
5
5
|
import "../datetime-normalization-PeiEdcRY.mjs";
|
|
6
|
-
import { j as EmDashConfigurationError, t as getMenu } from "../menus-
|
|
6
|
+
import { j as EmDashConfigurationError, t as getMenu } from "../menus-Cm68rsAq.mjs";
|
|
7
7
|
import { n as createDeferredTaskTracker, t as after } from "../after-PcEvMY_6.mjs";
|
|
8
8
|
import { createRequestMetrics, getRequestContext, runWithContext } from "../request-context.mjs";
|
|
9
9
|
import "../options-DhrI7eMv.mjs";
|
|
@@ -14,15 +14,15 @@ import "../content-DFaIEDj9.mjs";
|
|
|
14
14
|
import { s as getLastContentWriteAt } from "../object-cache-CqWNY5uP.mjs";
|
|
15
15
|
import "../slugify-T5GHH42D.mjs";
|
|
16
16
|
import "../types-BkUg2yHx.mjs";
|
|
17
|
-
import "../media-
|
|
17
|
+
import "../media-CPXTbb1R.mjs";
|
|
18
18
|
import "../user-DDSL-sGw.mjs";
|
|
19
19
|
import "../taxonomy-C3Fu1YeH.mjs";
|
|
20
20
|
import "../comment-CrBl22OF.mjs";
|
|
21
21
|
import "../audit-RfCugiSB.mjs";
|
|
22
|
-
import "../context-
|
|
22
|
+
import "../context-BV_7t-Ua.mjs";
|
|
23
23
|
import "../menus-ITeAbcIF.mjs";
|
|
24
24
|
import "../resolve-2WbQsCvT.mjs";
|
|
25
|
-
import "../redirect-
|
|
25
|
+
import "../redirect-B29UjdUn.mjs";
|
|
26
26
|
import { i as setRequestCacheEntry, r as requestCached } from "../request-cache-DzLYKm_x.mjs";
|
|
27
27
|
import "../byline-registry-gnKscbQX.mjs";
|
|
28
28
|
import "../field-defs-cache-HIgBbqZZ.mjs";
|
|
@@ -38,7 +38,7 @@ import { s as getDb } from "../loader-BzqeR1ZN.mjs";
|
|
|
38
38
|
import { createRecorder, flushRecorder, isInstrumentationEnabled } from "../database/instrumentation.mjs";
|
|
39
39
|
import "../collection-slugs-cache-CjGk__zp.mjs";
|
|
40
40
|
import { a as getTaxonomyDefs, o as getTaxonomyTerms } from "../taxonomies-C1IwNe8Q.mjs";
|
|
41
|
-
import "../api-
|
|
41
|
+
import "../api-CuyNEHZG.mjs";
|
|
42
42
|
import "../capture-triggers-kJyl_yyM.mjs";
|
|
43
43
|
import "../activation-LOkDM5WT.mjs";
|
|
44
44
|
import "../block-type-registry-DUI_3KxT.mjs";
|
|
@@ -48,14 +48,14 @@ import "../canonical-YhEQezjg.mjs";
|
|
|
48
48
|
import "../digest-BarSh-pN.mjs";
|
|
49
49
|
import "../hash-SDAW6zC-.mjs";
|
|
50
50
|
import "../zod-generator-D6uE4WiS.mjs";
|
|
51
|
-
import "../dashboard-
|
|
51
|
+
import "../dashboard-BP-iJqpd.mjs";
|
|
52
52
|
import "../block-values-C_VIPr5t.mjs";
|
|
53
|
-
import "../media-
|
|
53
|
+
import "../media-JXRWDhw3.mjs";
|
|
54
54
|
import "../media-folders-BE0oo2eX.mjs";
|
|
55
55
|
import "../media-usage-work-C2OZLCAd.mjs";
|
|
56
56
|
import "../content-repair-Q8IUrpLL.mjs";
|
|
57
57
|
import "../collection-deletion-processor-Br6rxs3u.mjs";
|
|
58
|
-
import "../media-usage-
|
|
58
|
+
import "../media-usage-De-zeR9E.mjs";
|
|
59
59
|
import "../settings-DYidIB26.mjs";
|
|
60
60
|
import "../site-settings-DOgRUoFX.mjs";
|
|
61
61
|
import "../errors-BF0_LFhW.mjs";
|
|
@@ -66,9 +66,9 @@ import "../schema-BABiRdsH.mjs";
|
|
|
66
66
|
import "../schema-Du3MzCG0.mjs";
|
|
67
67
|
import "../block-types-BmZmLmAD.mjs";
|
|
68
68
|
import "../seo-CZT2oLtT.mjs";
|
|
69
|
-
import { i as getSiteSettings } from "../settings-
|
|
69
|
+
import { i as getSiteSettings } from "../settings-B4vDl1fK.mjs";
|
|
70
70
|
import "../sections-Ddptx_o9.mjs";
|
|
71
|
-
import "../settings-
|
|
71
|
+
import "../settings-Dyj6v8C4.mjs";
|
|
72
72
|
import "../taxonomies-C_XMoRcX.mjs";
|
|
73
73
|
import "../manifest-schema-CY3zng_M.mjs";
|
|
74
74
|
import { t as EmDashStorageError } from "../types-VT4-BugW.mjs";
|
|
@@ -85,20 +85,20 @@ import "../comments-fHZ8ON9O.mjs";
|
|
|
85
85
|
import "../relations-CK9CYUyr.mjs";
|
|
86
86
|
import "../menus-BM7BPLOb.mjs";
|
|
87
87
|
import "../status-DZKIG4a9.mjs";
|
|
88
|
-
import {
|
|
88
|
+
import { o as createPublicMediaUrlResolver } from "../apply-npY1TAhG.mjs";
|
|
89
89
|
import { a as invalidateUrlPatternCache } from "../query-B48peoRT.mjs";
|
|
90
|
-
import "../import-
|
|
91
|
-
import "../redirects-
|
|
90
|
+
import "../import-YzfKCaco.mjs";
|
|
91
|
+
import "../redirects-Ca2MPfEG.mjs";
|
|
92
92
|
import "../ssrf-DpBH7aOt.mjs";
|
|
93
93
|
import "../cron-7DCJwPXq.mjs";
|
|
94
94
|
import "../email-console-bJn3ctqA.mjs";
|
|
95
95
|
import "../fence-CMDXJYc_.mjs";
|
|
96
96
|
import "../route-wire--1hLuACz.mjs";
|
|
97
|
-
import "../routes-
|
|
97
|
+
import "../routes-vrR95WR6.mjs";
|
|
98
98
|
import "../utils-DdhmO9w9.mjs";
|
|
99
99
|
import "../tokens-u78jETkX.mjs";
|
|
100
100
|
import "../preview-CIUcPYr6.mjs";
|
|
101
|
-
import "../hreflang-
|
|
101
|
+
import "../hreflang-CbVI4paR.mjs";
|
|
102
102
|
import "../comment-reaction-JEQ2Fpd8.mjs";
|
|
103
103
|
import "../bylines-BPwJqoYH.mjs";
|
|
104
104
|
import { r as getWidgetAreas } from "../widgets-WdV72brA.mjs";
|
|
@@ -106,11 +106,11 @@ import "../validate-CHosPEhs.mjs";
|
|
|
106
106
|
import "../load-XgJiE6_3.mjs";
|
|
107
107
|
import "../search-Bt1PL1jf.mjs";
|
|
108
108
|
import { i as resolveRuntimeMigrationMode, t as PendingMigrationsError } from "../policy-CUvwAARc.mjs";
|
|
109
|
-
import { n as EmDashRuntime, t as DB_INIT_DEADLINE_MS } from "../emdash-runtime-
|
|
109
|
+
import { n as EmDashRuntime, t as DB_INIT_DEADLINE_MS } from "../emdash-runtime-CtfnZmPi.mjs";
|
|
110
110
|
import "../site-url-BU_bbxwj.mjs";
|
|
111
111
|
import "../public-submission-BFBUrqst.mjs";
|
|
112
|
-
import "../snapshot-
|
|
113
|
-
import "../backup-
|
|
112
|
+
import "../snapshot-PawoX-WG.mjs";
|
|
113
|
+
import "../backup-BQRj3afm.mjs";
|
|
114
114
|
import "../comments-2ah9du9y.mjs";
|
|
115
115
|
import "../rate-limit-BZ6nybgc.mjs";
|
|
116
116
|
import "../service-DuykXPyW.mjs";
|
|
@@ -1004,6 +1004,7 @@ const onRequest = defineMiddleware(async (context, next) => {
|
|
|
1004
1004
|
handleMediaGet: runtime.handleMediaGet.bind(runtime),
|
|
1005
1005
|
handleMediaUpload: runtime.handleMediaUpload.bind(runtime),
|
|
1006
1006
|
handleMediaCreate: runtime.handleMediaCreate.bind(runtime),
|
|
1007
|
+
handleMediaRegisterUpload: runtime.handleMediaRegisterUpload.bind(runtime),
|
|
1007
1008
|
handleMediaUpdate: runtime.handleMediaUpdate.bind(runtime),
|
|
1008
1009
|
...runtime.handleMediaReplaceMetadata ? { handleMediaReplaceMetadata: runtime.handleMediaReplaceMetadata.bind(runtime) } : {},
|
|
1009
1010
|
handleMediaDelete: runtime.handleMediaDelete.bind(runtime),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.mjs","names":["virtualPlugins","virtualCreateDialect","virtualCreateCoalescingDialect","virtualCreateStorage","virtualCreateScheduler","virtualSandboxedPlugins","virtualMediaProviders","createRequestScopedDb","virtualCreateRequestScopedDb","virtualBuildTime"],"sources":["../../src/astro/middleware/scoped-db.ts","../../src/astro/middleware/stream-end-metrics.ts","../../src/astro/prefetch.ts","../../src/astro/public-plugin-api-routes.ts","../../src/astro/middleware.ts"],"sourcesContent":["/**\n * Request-scoped database lifecycle helpers.\n *\n * Extracted from middleware.ts so they can be unit-tested without pulling in\n * the virtual:emdash/* module graph. The middleware imports these to settle a\n * request-scoped db adapter's lifecycle around the response.\n */\n\nimport { createDeferredTaskTracker } from \"../../deferred-tasks.js\";\nimport type { DeferredTaskTracker } from \"../../deferred-tasks.js\";\n\n/**\n * Astro attaches AstroCookies to outgoing responses via a well-known global\n * symbol. Cloning a Response (`new Response(body, init)`) drops non-header\n * metadata, so any helper that wraps the response must explicitly forward this\n * symbol or `cookies.set()` calls will be silently dropped. `Symbol.for`\n * returns the same registry symbol everywhere, so this matches the copy in\n * middleware.ts.\n */\nexport const ASTRO_COOKIES_SYMBOL = Symbol.for(\"astro.cookies\");\n\ninterface ScopedDbLifecycle {\n\tcommit: () => void;\n\tclose?: () => void;\n}\n\n/**\n * Whether the request ended authenticated, for the db adapter's `commit()`.\n * `isAuthenticated` is captured before the route runs, so it misses\n * login/signup/invite requests that create the Astro session mid-request;\n * `session.set()` writes the `astro-session` cookie into the jar synchronously,\n * so scanning the outgoing cookies detects them. Only outgoing cookies count —\n * a stale `astro-session` cookie arriving on an anonymous render must not\n * qualify, or its replica-read bookmark would overwrite a fresher one from an\n * earlier authenticated write.\n */\nexport function requestEndedAuthenticated(\n\tisAuthenticated: boolean,\n\tcookies: { headers(): Iterable<string> },\n): boolean {\n\tif (isAuthenticated) return true;\n\tfor (const header of cookies.headers()) {\n\t\tif (header.startsWith(\"astro-session=\")) return true;\n\t}\n\treturn false;\n}\n\n/** Hold the real adapter close behind both response and deferred-task completion. */\nexport function coordinateScopedDbLifecycle(scoped: ScopedDbLifecycle): {\n\tclosed?: Promise<void>;\n\tdeferredTasks?: DeferredTaskTracker;\n\tlifecycle: ScopedDbLifecycle;\n} {\n\tif (!scoped.close) return { lifecycle: scoped };\n\n\tconst close = scoped.close;\n\tconst deferredTasks = createDeferredTaskTracker(() => {\n\t\ttry {\n\t\t\tclose();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] request-scoped db close failed:\", error);\n\t\t}\n\t});\n\treturn {\n\t\tclosed: deferredTasks.settled,\n\t\tdeferredTasks,\n\t\tlifecycle: { commit: scoped.commit, close: deferredTasks.settle },\n\t};\n}\n\n/**\n * Run a request-scoped db's `close()` once the response body has finished\n * streaming. Astro streams HTML and components issue DB queries during that\n * stream, so a connection-backed adapter (e.g. Postgres over Hyperdrive) must\n * not be torn down until the body is flushed. Bodyless responses (redirects,\n * 304s, errors) close immediately. A guard makes close idempotent and a stream\n * `cancel` (client disconnect) still triggers it so connections never leak.\n *\n * No-op for adapters without a `close` (D1): the response passes through.\n */\nexport function wrapResponseForScopedClose(response: Response, close: () => void): Response {\n\tlet closed = false;\n\tconst runClose = () => {\n\t\tif (closed) return;\n\t\tclosed = true;\n\t\ttry {\n\t\t\tclose();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] request-scoped db close failed:\", error);\n\t\t}\n\t};\n\n\tif (!response.body) {\n\t\trunClose();\n\t\treturn response;\n\t}\n\n\tconst transform = new TransformStream<Uint8Array, Uint8Array>({\n\t\tflush: runClose,\n\t\tcancel: runClose,\n\t});\n\tconst wrapped = new Response(response.body.pipeThrough(transform), response);\n\tconst astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);\n\tif (astroCookies !== undefined) {\n\t\tReflect.set(wrapped, ASTRO_COOKIES_SYMBOL, astroCookies);\n\t}\n\t// Byte counts are preserved by the identity transform, but a stale\n\t// Content-Length on a reconstructed streaming Response risks truncation.\n\twrapped.headers.delete(\"Content-Length\");\n\treturn wrapped;\n}\n\n/**\n * Run the request body under a request-scoped db, then settle its lifecycle:\n * `commit()` runs before the response is returned (so per-request state like a\n * D1 bookmark cookie is persisted in the headers, even if render throws), while\n * `close()` (if any) is deferred to lifecycle settlement so a\n * connection-backed adapter isn't torn down mid-render or mid-task. On error\n * the lifecycle is settled before rethrowing so it never leaks.\n *\n * On the error path both `commit()` and `close()` are defended: a throw from\n * either is logged and swallowed so it can't replace the propagating render\n * error (which is the one the caller needs to see). On the success path\n * `commit()` is guarded too — if it throws, the connection is closed before the\n * failure is surfaced, so it never leaks. For the current adapters `commit()`\n * is a no-op (Hyperdrive) or a cookie write (D1, no `close`) and `close()` is\n * fire-and-forget, so these guards only matter for a future connection-backed\n * adapter with throwing teardown, but the helper is generic and must not leak\n * or mask.\n */\nexport async function finishScoped(\n\tscoped: ScopedDbLifecycle,\n\trun: () => Promise<Response>,\n): Promise<Response> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await run();\n\t} catch (error) {\n\t\t// A render error is already propagating; neither commit nor close may\n\t\t// mask it, and close must still run so the connection doesn't leak.\n\t\tcommitSafely(scoped.commit);\n\t\tcloseSafely(scoped.close);\n\t\tthrow error;\n\t}\n\ttry {\n\t\tscoped.commit();\n\t} catch (error) {\n\t\t// commit() failed on the success path: close the connection now (the\n\t\t// response won't be wrapped, so stream-end close would never run) and\n\t\t// surface the failure. close is swallowed so it can't mask the commit\n\t\t// error that the caller needs to see.\n\t\tcloseSafely(scoped.close);\n\t\tthrow error;\n\t}\n\treturn scoped.close ? wrapResponseForScopedClose(response, scoped.close) : response;\n}\n\n/**\n * Run commit() swallowing any error. Used where an exception is already\n * propagating (or about to be thrown) and a commit failure must neither mask it\n * nor skip the subsequent close().\n */\nfunction commitSafely(commit: () => void): void {\n\ttry {\n\t\tcommit();\n\t} catch (error) {\n\t\tconsole.error(\"[emdash] request-scoped db commit failed during error handling:\", error);\n\t}\n}\n\n/**\n * Run close() swallowing any error. Used on the error/commit-failure paths\n * where another exception is the one the caller must see; a throwing teardown\n * must not replace it.\n */\nfunction closeSafely(close: (() => void) | undefined): void {\n\tif (!close) return;\n\ttry {\n\t\tclose();\n\t} catch (error) {\n\t\tconsole.error(\"[emdash] request-scoped db close failed during error handling:\", error);\n\t}\n}\n","/**\n * Stream-end metrics\n *\n * Server-Timing db.* counters are snapshotted when middleware's next()\n * returns — but at that point only the response *headers* are final.\n * Astro streams the body afterwards, and components rendered during\n * streaming issue further DB queries that the headers can never report.\n *\n * This module wraps the response body in an identity TransformStream and\n * snapshots the request metrics in flush(), i.e. when the body actually\n * finishes streaming. The metrics object lives on the request context\n * (AsyncLocalStorage) and is mutated in-place by the Kysely log hook, so\n * a reference captured before wrapping observes every post-header query.\n * The snapshot is emitted as prefixed NDJSON on stdout (same transport as\n * [emdash-query-log] — console.log works in both Node and workerd).\n *\n * Gated on isInstrumentationEnabled() (EMDASH_QUERY_LOG=1): zero overhead\n * in normal production traffic.\n */\n\nimport { flushRecorder, isInstrumentationEnabled } from \"../../database/instrumentation.js\";\nimport { getRequestContext } from \"../../request-context.js\";\n// Reuse the single source of truth for Astro's well-known cookies symbol\n// rather than redefining `Symbol.for(\"astro.cookies\")` here — it must stay in\n// lockstep with the copy the rest of the middleware forwards.\nimport { ASTRO_COOKIES_SYMBOL } from \"./scoped-db.js\";\n\nexport const STREAM_END_PREFIX = \"[emdash-stream-end]\";\n\n/** Shape of the NDJSON snapshot emitted when the body finishes streaming. */\nexport interface StreamEndSnapshot {\n\troute?: string;\n\tmethod?: string;\n\tphase?: string;\n\t/** Total elapsed ms from middleware entry to end of body streaming. */\n\ttotalMs: number;\n\tdbCount: number;\n\tdbTotalMs: number;\n\tdbFirstOffset: number | null;\n\tdbLastOffset: number | null;\n\tcacheHits: number;\n\tcacheMisses: number;\n}\n\n/**\n * Wrap a response body so the FINAL request metrics are emitted when the\n * body finishes streaming. Returns the response unchanged when\n * instrumentation is disabled, the body is null, or no request metrics\n * are attached (e.g. outside the middleware's ALS context).\n */\nexport function wrapBodyForStreamMetrics(response: Response): Response {\n\tif (!isInstrumentationEnabled()) return response;\n\tif (!response.body) return response;\n\n\t// Capture the context's metrics object BEFORE wrapping: flush() runs\n\t// after the middleware's ALS frame may have exited, but the object\n\t// reference stays live and is mutated in-place by the Kysely log hook.\n\tconst ctx = getRequestContext();\n\tconst metrics = ctx?.metrics;\n\tif (!metrics) return response;\n\tconst recorder = ctx?.queryRecorder;\n\n\t// Claim the per-query flush: the recorder is mutated in-place by the\n\t// Kysely log hook for the whole request, including queries issued by\n\t// components while the body streams. Flushing here (rather than when\n\t// middleware returns) is what captures those streaming queries. The\n\t// flag tells the middleware's fallback flush to leave this recorder\n\t// to us.\n\tif (recorder) recorder.deferredFlush = true;\n\n\tconst transform = new TransformStream<Uint8Array, Uint8Array>({\n\t\tflush() {\n\t\t\tconst snapshot: StreamEndSnapshot = {\n\t\t\t\troute: recorder?.route,\n\t\t\t\tmethod: recorder?.method,\n\t\t\t\tphase: recorder?.phase,\n\t\t\t\ttotalMs: performance.now() - metrics.start,\n\t\t\t\tdbCount: metrics.dbCount,\n\t\t\t\tdbTotalMs: metrics.dbTotalMs,\n\t\t\t\tdbFirstOffset: metrics.dbFirstOffset,\n\t\t\t\tdbLastOffset: metrics.dbLastOffset,\n\t\t\t\tcacheHits: metrics.cacheHits,\n\t\t\t\tcacheMisses: metrics.cacheMisses,\n\t\t\t};\n\t\t\tconsole.log(`${STREAM_END_PREFIX} ${JSON.stringify(snapshot)}`);\n\t\t\t// Emit the full per-query log now that streaming is complete.\n\t\t\tif (recorder) flushRecorder(recorder);\n\t\t},\n\t});\n\n\tconst wrapped = new Response(response.body.pipeThrough(transform), response);\n\tconst astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);\n\tif (astroCookies !== undefined) {\n\t\tReflect.set(wrapped, ASTRO_COOKIES_SYMBOL, astroCookies);\n\t}\n\t// The identity transform preserves byte counts today, but a stale\n\t// Content-Length on a re-constructed streaming Response risks\n\t// truncation if anything upstream changes; the header is recomputed\n\t// (or chunked encoding used) by the server layer anyway.\n\twrapped.headers.delete(\"Content-Length\");\n\treturn wrapped;\n}\n","/**\n * Eager, transparent prefetch of site-global \"chrome\" data.\n *\n * On a public page render, the shared layout pulls the same site-global data on\n * every request -- menus, widget areas, taxonomy term lists, site settings --\n * but each is awaited inside a separately-rendered Astro component, so they\n * execute as serial DB round trips. This fires them all CONCURRENTLY at the\n * very start of the request, before `next()`:\n *\n * - On remote backends (D1, Durable Objects) the round trips overlap instead\n * of serializing, collapsing ~N sequential RTTs into ~1 wall-clock RTT. On\n * a coalescing backend they additionally batch into a single round trip.\n * - The results land in the per-request `requestCached` store under the exact\n * keys the layout helpers use, so when the components render they hit a warm\n * (in-flight or resolved) cache entry instead of issuing their own query.\n *\n * Nothing here changes what templates call -- it warms the real helpers, so the\n * cache keys and value shapes are guaranteed identical. The caller gates this to\n * the public-page path on a request-scoped (remote) backend; it is a no-op-ish\n * waste on synchronous local SQLite, so don't call it there.\n *\n * Fire-and-forget: never awaited by middleware, never throws (a prefetch failure\n * must not affect the request -- the helpers will simply run on demand).\n */\n\nimport { getDb } from \"../loader.js\";\nimport { getMenu } from \"../menus/index.js\";\nimport { requestCached, setRequestCacheEntry } from \"../request-cache.js\";\nimport { getSiteSettings } from \"../settings/index.js\";\nimport { getTaxonomyDefs, getTaxonomyTerms } from \"../taxonomies/index.js\";\nimport { getWidgetAreas } from \"../widgets/index.js\";\n\n/** Warm widget areas: one bulk load, primed under each per-area cache key. */\nasync function prefetchWidgetAreas(): Promise<void> {\n\tconst areas = await requestCached(\"widget-areas\", getWidgetAreas);\n\t// getWidgetArea(name) caches under `widget-area:${name}` and returns the same\n\t// WidgetArea shape getWidgetAreas yields, so priming here makes those calls hit.\n\tfor (const area of areas) {\n\t\tsetRequestCacheEntry(`widget-area:${area.name}`, area);\n\t}\n}\n\n/**\n * Warm every taxonomy's term list via the real helper (primes per-name keys).\n * Counts are left out: they cost an aggregate over the whole assignment pivot\n * per taxonomy, and only a consumer that renders one can say it's needed. A\n * consumer that does asks for it and reuses the term list warmed here.\n */\nasync function prefetchTaxonomyTerms(): Promise<void> {\n\tconst defs = await getTaxonomyDefs();\n\tawait Promise.allSettled(defs.map((def) => getTaxonomyTerms(def.name, { includeCounts: false })));\n}\n\n/** Warm every menu via the real helper (primes `menu:${name}:${locale}`). */\nasync function prefetchMenus(): Promise<void> {\n\tconst db = await getDb();\n\t// The layout calls getMenu(name) with hardcoded names; we can't know them, so\n\t// discover every menu name and warm them all (small, bounded chrome table).\n\tconst rows = await db.selectFrom(\"_emdash_menus\").select(\"name\").distinct().execute();\n\tconst names = [...new Set(rows.map((r) => r.name))];\n\tawait Promise.allSettled(names.map((name) => getMenu(name)));\n}\n\n/**\n * Concurrently warm the site-global layout data for the current request.\n * Safe to call only inside the request ALS frame that owns the (remote)\n * request-scoped db. Never throws.\n */\nexport async function prefetchLayoutData(): Promise<void> {\n\ttry {\n\t\tawait Promise.allSettled([\n\t\t\tgetSiteSettings(),\n\t\t\tprefetchMenus(),\n\t\t\tprefetchWidgetAreas(),\n\t\t\tprefetchTaxonomyTerms(),\n\t\t]);\n\t} catch (error) {\n\t\t// Defensive: Promise.allSettled shouldn't reject, but never let a prefetch\n\t\t// failure surface to the request.\n\t\tconsole.error(\"[emdash] layout prefetch failed (non-fatal):\", error);\n\t}\n}\n","import type { RouteMeta } from \"../plugins/routes.js\";\nimport type { HandlerResponse } from \"./types.js\";\n\nexport type PublicPluginApiRouteHandler = (\n\tpluginId: string,\n\tmethod: string,\n\tpath: string,\n\trequest: Request,\n) => Promise<HandlerResponse>;\n\ninterface PublicPluginApiRouteRuntime {\n\tgetPluginRouteMeta(pluginId: string, path: string): RouteMeta | null;\n\thandlePluginApiRoute(\n\t\tpluginId: string,\n\t\tmethod: string,\n\t\tpath: string,\n\t\trequest: Request,\n\t): Promise<HandlerResponse>;\n}\n\nfunction pluginRouteNotFound(): HandlerResponse {\n\treturn {\n\t\tsuccess: false,\n\t\terror: {\n\t\t\tcode: \"NOT_FOUND\",\n\t\t\tmessage: \"Plugin route not found\",\n\t\t},\n\t};\n}\n\nexport function createPublicPluginApiRouteHandler(\n\truntime: PublicPluginApiRouteRuntime,\n): PublicPluginApiRouteHandler {\n\treturn async (pluginId, method, path, request) => {\n\t\tconst meta = runtime.getPluginRouteMeta(pluginId, path);\n\t\tif (meta?.public !== true) {\n\t\t\treturn pluginRouteNotFound();\n\t\t}\n\n\t\treturn runtime.handlePluginApiRoute(pluginId, method, path, request);\n\t};\n}\n","/**\n * EmDash middleware\n *\n * Thin wrapper that initializes EmDashRuntime and attaches it to locals.\n * All heavy lifting happens in EmDashRuntime.\n */\n\nimport type { APIContext } from \"astro\";\nimport { defineMiddleware } from \"astro:middleware\";\nimport type { Kysely } from \"kysely\";\n// Import from virtual modules (populated by integration at build time)\n// @ts-ignore - virtual module\nimport { buildTime as virtualBuildTime } from \"virtual:emdash/build\";\n// @ts-ignore - virtual module\nimport virtualConfig from \"virtual:emdash/config\";\n// @ts-ignore - virtual module\nimport {\n\tcreateCoalescingDialect as virtualCreateCoalescingDialect,\n\tcreateDialect as virtualCreateDialect,\n\tcreateRequestScopedDb as virtualCreateRequestScopedDb,\n} from \"virtual:emdash/dialect\";\nimport type { RequestScopedDbOpts } from \"virtual:emdash/dialect\";\n// @ts-ignore - virtual module\nimport { mediaProviders as virtualMediaProviders } from \"virtual:emdash/media-providers\";\n// @ts-ignore - virtual module\nimport { plugins as virtualPlugins } from \"virtual:emdash/plugins\";\n// @ts-ignore - virtual module\nimport * as virtualSandboxRunnerModule from \"virtual:emdash/sandbox-runner\";\n// @ts-ignore - virtual module\nimport { sandboxedPlugins as virtualSandboxedPlugins } from \"virtual:emdash/sandboxed-plugins\";\n// @ts-ignore - virtual module\nimport { createScheduler as virtualCreateScheduler } from \"virtual:emdash/scheduler\";\n// @ts-ignore - virtual module\nimport { createStorage as virtualCreateStorage } from \"virtual:emdash/storage\";\n\nimport { after } from \"../after.js\";\nimport { apiError } from \"../api/error.js\";\nimport { EmDashConfigurationError } from \"../config/errors.js\";\nimport {\n\tcreateRecorder,\n\tflushRecorder,\n\tisInstrumentationEnabled,\n} from \"../database/instrumentation.js\";\nimport {\n\tPendingMigrationsError,\n\tresolveRuntimeMigrationMode,\n\ttype RuntimeMigrationMode,\n} from \"../database/migrations/policy.js\";\nimport { createDeferredTaskTracker } from \"../deferred-tasks.js\";\nimport {\n\tDB_INIT_DEADLINE_MS,\n\tEmDashRuntime,\n\ttype RuntimeDependencies,\n\ttype SandboxedPluginEntry,\n\ttype MediaProviderEntry,\n\ttype CreateSchedulerFn,\n} from \"../emdash-runtime.js\";\nimport { setI18nConfig } from \"../i18n/config.js\";\nimport type { Database, Storage } from \"../index.js\";\nimport { createPublicMediaUrlResolver } from \"../media/url.js\";\nimport { getLastContentWriteAt } from \"../object-cache/index.js\";\nimport type { PluginContentCacheInvalidator } from \"../plugins/routes.js\";\nimport type { SandboxRunnerFactory } from \"../plugins/sandbox/types.js\";\nimport type { ResolvedPlugin } from \"../plugins/types.js\";\nimport { invalidateUrlPatternCache } from \"../query.js\";\nimport {\n\tcreateRequestMetrics,\n\tgetRequestContext,\n\ttype RequestMetrics,\n\trunWithContext,\n} from \"../request-context.js\";\nimport type { PublishedRef } from \"../scheduled-publish.js\";\nimport { EmDashStorageError } from \"../storage/types.js\";\nimport { isMissingTableError } from \"../utils/db-errors.js\";\nimport { createInitLock, type InitLock, initWithLock } from \"../utils/init-lock.js\";\nimport type { EmDashConfig } from \"./integration/runtime.js\";\nimport {\n\tASTRO_COOKIES_SYMBOL,\n\tcoordinateScopedDbLifecycle,\n\tfinishScoped,\n\trequestEndedAuthenticated,\n} from \"./middleware/scoped-db.js\";\nimport { wrapBodyForStreamMetrics } from \"./middleware/stream-end-metrics.js\";\nimport { prefetchLayoutData } from \"./prefetch.js\";\nimport { createPublicPluginApiRouteHandler } from \"./public-plugin-api-routes.js\";\nimport { resolveSessionUser } from \"./session-user.js\";\nimport type { EmDashHandlers } from \"./types.js\";\n\n// Public type for withEmDashRuntime() consumers (queue/scheduled handlers).\nexport type { EmDashRuntime } from \"../emdash-runtime.js\";\n\n/**\n * Runtime init lock reclaim deadline. Must be strictly larger than the db\n * init deadline: this lock wraps EmDashRuntime.create() → getDatabase() →\n * the db init lock, and equal deadlines would let this outer lock reclaim\n * (spawning a second cron scheduler and sandbox runner) while the inner db\n * init is legitimately still working through a contended migration.\n */\nconst RUNTIME_INIT_DEADLINE_MS = DB_INIT_DEADLINE_MS + 15_000;\n\n/**\n * Throttle for the anonymous-path runtime-init failure log. While a site is\n * stuck (e.g. a failing migration in its backoff window, #1744) every\n * anonymous request lands in that catch; one line per interval per isolate\n * keeps the failure visible without flooding logs on a busy site. Plain\n * module state (not globalThis): a duplicated SSR chunk just means an extra\n * log line, which is harmless.\n */\nconst RUNTIME_INIT_ERROR_LOG_INTERVAL_MS = 30_000;\nlet lastRuntimeInitErrorLogAt = 0;\n\n/**\n * Whether we've verified the database has been set up.\n * On a fresh deployment the first request may hit a public page, bypassing\n * runtime init. Without this check, template helpers like getSiteSettings()\n * would query an empty database and crash. Once verified (or once the runtime\n * has initialized via an admin/API request), this stays true for the worker's\n * lifetime.\n *\n * Stored on globalThis behind a Symbol key so the flag is a true singleton\n * even when the bundler duplicates this module across SSR chunks (same\n * pattern as request-cache.ts). A plain module-scoped `let` becomes multiple\n * independent variables, which would make the setup probe re-run far more\n * often than intended — and every re-run is another chance for a transient\n * DB error to be misread as \"fresh install\" and bounce visitors to setup.\n */\nconst SETUP_VERIFIED_KEY = Symbol.for(\"emdash:setup-verified\");\nconst setupFlagStore = globalThis as Record<symbol, unknown>;\n\nfunction isSetupVerified(): boolean {\n\treturn setupFlagStore[SETUP_VERIFIED_KEY] === true;\n}\n\nfunction markSetupVerified(): void {\n\tsetupFlagStore[SETUP_VERIFIED_KEY] = true;\n}\n\n/**\n * The runtime singleton and its init lock live on globalThis behind a\n * Symbol — same reasoning as SETUP_VERIFIED_KEY above: the bundler can\n * duplicate this module across SSR chunks, and a duplicated instance/lock\n * would mean multiple runtimes (each with its own cron scheduler) per\n * isolate, initializing and reclaiming independently.\n */\nconst RUNTIME_HOLDER_KEY = Symbol.for(\"emdash:runtime-holder\");\ninterface RuntimeHolder {\n\tinstance: EmDashRuntime | null;\n\tlock: InitLock;\n}\n\nfunction getRuntimeHolder(): RuntimeHolder {\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis symbol slot, written only below\n\tlet holder = setupFlagStore[RUNTIME_HOLDER_KEY] as RuntimeHolder | undefined;\n\tif (!holder) {\n\t\tholder = { instance: null, lock: createInitLock() };\n\t\tsetupFlagStore[RUNTIME_HOLDER_KEY] = holder;\n\t}\n\treturn holder;\n}\n\n/** Whether i18n config has been initialized from the virtual module */\nlet i18nInitialized = false;\n\n/**\n * Get EmDash configuration from virtual module\n */\nfunction getConfig(): EmDashConfig | null {\n\tif (virtualConfig && typeof virtualConfig === \"object\") {\n\t\t// Initialize i18n config on first access (once per worker lifetime)\n\t\tif (!i18nInitialized) {\n\t\t\ti18nInitialized = true;\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- virtual module checked as object above\n\t\t\tconst config = virtualConfig as Record<string, unknown>;\n\t\t\tif (config.i18n && typeof config.i18n === \"object\") {\n\t\t\t\tsetI18nConfig(\n\t\t\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- runtime-checked above\n\t\t\t\t\tconfig.i18n as {\n\t\t\t\t\t\tdefaultLocale: string;\n\t\t\t\t\t\tlocales: string[];\n\t\t\t\t\t\tfallback?: Record<string, string>;\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tsetI18nConfig(null);\n\t\t\t}\n\t\t}\n\n\t\treturn virtualConfig;\n\t}\n\treturn null;\n}\n\n/**\n * Get plugins from virtual module\n */\nfunction getPlugins(): ResolvedPlugin[] {\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- virtual module import is untyped (@ts-ignore above)\n\treturn (virtualPlugins as ResolvedPlugin[]) || [];\n}\n\n/**\n * Build runtime dependencies from virtual modules\n */\nfunction buildDependencies(\n\tconfig: EmDashConfig,\n\tmigrationMode: RuntimeMigrationMode,\n): RuntimeDependencies {\n\t/* eslint-disable typescript-eslint/no-unsafe-type-assertion --\n\t The virtual:emdash/* imports above use @ts-ignore because tsgo/IDE\n\t resolution can't see virtual-modules.d.ts in every consumer setup,\n\t so they arrive as `any`. The casts here line each entry up with\n\t RuntimeDependencies's expected shape. The contract is enforced by\n\t the integration that populates these virtual modules. */\n\tconst sandboxModule = virtualSandboxRunnerModule as Record<string, unknown>;\n\treturn {\n\t\tconfig,\n\t\tmigrationMode,\n\t\tplugins: getPlugins(),\n\t\tcreateDialect: virtualCreateDialect as (config: Record<string, unknown>) => unknown,\n\t\t// Optional: only batching backends (D1, DO) export this; undefined otherwise.\n\t\tcreateCoalescingDialect: virtualCreateCoalescingDialect as\n\t\t\t| ((config: Record<string, unknown>) => unknown)\n\t\t\t| undefined,\n\t\tcreateStorage: virtualCreateStorage as ((config: Record<string, unknown>) => Storage) | null,\n\t\tcreateScheduler: virtualCreateScheduler as CreateSchedulerFn | null,\n\t\tsandboxEnabled: sandboxModule.sandboxEnabled as boolean,\n\t\tsandboxBypassed: (sandboxModule.sandboxBypassed as boolean) ?? false,\n\t\tsandboxedPluginEntries: (virtualSandboxedPlugins as SandboxedPluginEntry[]) || [],\n\t\tcreateSandboxRunner: sandboxModule.createSandboxRunner as SandboxRunnerFactory | null,\n\t\tmediaProviderEntries: (virtualMediaProviders as MediaProviderEntry[]) || [],\n\t};\n\t/* eslint-enable typescript-eslint/no-unsafe-type-assertion */\n}\n\n/**\n * Get or create the runtime instance.\n *\n * When `initTimings` is provided, any timing samples recorded during a\n * genuine cold init are appended. Subsequent warm calls (hitting the\n * cached instance) push nothing — callers should treat an empty array\n * as \"warm, nothing to report\".\n */\nasync function getRuntime(\n\tconfig: EmDashConfig,\n\tmigrationMode: RuntimeMigrationMode,\n\tinitTimings?: Array<{ name: string; dur: number; desc?: string }>,\n): Promise<EmDashRuntime> {\n\t// Waiters poll rather than awaiting the initializing request's promise —\n\t// workerd flags cross-request promise resolution (warnings + potential\n\t// hangs). If the initializing request is cancelled mid-create (client\n\t// disconnect tears down its continuation, skipping any `finally`), the\n\t// anchored init keeps running under waitUntil and populates the cache;\n\t// failing that, the stale lock is reclaimed after a deadline instead of\n\t// hanging every subsequent request in the isolate until eviction.\n\tconst holder = getRuntimeHolder();\n\treturn initWithLock(\n\t\tholder.lock,\n\t\t() => holder.instance,\n\t\tasync (isCurrentClaim) => {\n\t\t\tconst deps = buildDependencies(config, migrationMode);\n\t\t\tconst runtime = await EmDashRuntime.create(deps, initTimings);\n\t\t\tif (isCurrentClaim()) {\n\t\t\t\tholder.instance = runtime;\n\t\t\t} else {\n\t\t\t\t// This init was reclaimed mid-flight (it ran past the deadline\n\t\t\t\t// and a waiter started its own). Don't overwrite the\n\t\t\t\t// reclaimer's published runtime, and stop this one's cron\n\t\t\t\t// scheduler so it doesn't keep firing unreferenced. The\n\t\t\t\t// runtime is still returned — it's fully functional for the\n\t\t\t\t// request that built it.\n\t\t\t\truntime.stopCron().catch((error: unknown) => {\n\t\t\t\t\tconsole.error(\"[emdash] failed to stop superseded runtime's cron:\", error);\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn runtime;\n\t\t},\n\t\t{\n\t\t\tdeadlineMs: RUNTIME_INIT_DEADLINE_MS,\n\t\t\tanchor: (promise) => after(() => promise),\n\t\t},\n\t);\n}\n\n/**\n * Run scheduled maintenance (cron tasks, scheduled publishing, system cleanup)\n * outside any request. Resolves the runtime from the build-time virtual config\n * and the cached singleton — the same instance request handlers use.\n *\n * Wired into a platform heartbeat that is not a request: the Cloudflare Worker's\n * `scheduled()` handler (Cron Trigger) calls this. On Node the runtime's own\n * timer-based scheduler already drives the same work, so this isn't needed there.\n *\n * Returns the content promoted by the publishing sweep so the caller can purge\n * edge-cache tags for it. `onPublished` (optional) is awaited after each\n * collection's batch so the caller can invalidate edge-cache tags incrementally\n * rather than only after the whole sweep.\n */\nexport async function runScheduledTasks(\n\toptions: {\n\t\tonPublished?: (refs: PublishedRef[]) => Promise<void>;\n\t\tinvalidateContentCache?: PluginContentCacheInvalidator;\n\t} = {},\n): Promise<{ published: PublishedRef[] }> {\n\tconst config = getConfig();\n\tif (!config) return { published: [] };\n\treturn runOutsideRequest(config, (runtime) => runtime.runScheduledTasks(options));\n}\n\n/**\n * Run a callback against the EmDash runtime outside any HTTP request — from a\n * Cloudflare Queue consumer, a `scheduled()` handler, or any other\n * platform-event handler that has no request and therefore no `locals.emdash`.\n *\n * Resolves the same cached runtime singleton request handlers use, so hooks,\n * plugin state, and storage all behave exactly as they do during a request.\n * A typical queue consumer finishes a job by calling back into a plugin route:\n *\n * ```ts\n * import { withEmDashRuntime } from \"emdash/middleware\";\n *\n * async function queue(batch: MessageBatch) {\n * \tawait withEmDashRuntime(async (runtime) => {\n * \t\tfor (const message of batch.messages) {\n * \t\t\tawait runtime.handlePluginApiRoute(\n * \t\t\t\t\"my-plugin\",\n * \t\t\t\t\"POST\",\n * \t\t\t\t\"/finishJob\",\n * \t\t\t\tnew Request(\"https://internal/\", {\n * \t\t\t\t\tmethod: \"POST\",\n * \t\t\t\t\tbody: JSON.stringify(message.body),\n * \t\t\t\t}),\n * \t\t\t);\n * \t\t}\n * \t});\n * }\n * ```\n *\n * Server-only and fully trusted: the callback gets the raw runtime with no\n * auth or CSRF checks, same trust level as plugin cron. Never expose it to\n * user input without validating that input yourself.\n *\n * Throws when EmDash is not configured (no `emdash()` Astro integration).\n */\nexport async function withEmDashRuntime<T>(\n\trun: (runtime: EmDashRuntime) => T | Promise<T>,\n): Promise<T> {\n\tconst config = getConfig();\n\tif (!config) {\n\t\tthrow new Error(\n\t\t\t\"EmDash is not configured — withEmDashRuntime() requires the emdash() Astro integration.\",\n\t\t);\n\t}\n\treturn runOutsideRequest(config, async (runtime) => run(runtime));\n}\n\n/**\n * Shared plumbing for request-free entry points (`runScheduledTasks`,\n * `withEmDashRuntime`): resolve the runtime singleton, then run the callback\n * under an event-scoped db when the adapter needs one.\n *\n * Connection-backed adapters (e.g. Postgres over Hyperdrive) cannot reuse\n * the per-isolate singleton from a platform event: its socket belongs to the\n * request that opened it, and workerd rejects cross-event I/O. Open an\n * event-scoped connection and run the callback under it in ALS — the\n * runtime's db getter, the cron executor, and plugin contexts all resolve\n * the connection from ALS — then close it when required. Stateless adapters\n * that need primary routing can return a close-less scope; adapters with no\n * event scoping return null and keep using the singleton.\n */\nasync function runOutsideRequest<T>(\n\tconfig: EmDashConfig,\n\tfn: (runtime: EmDashRuntime) => Promise<T>,\n): Promise<T> {\n\tconst migrationMode = resolveConfiguredMigrationMode(config);\n\tif (getRequestContext()) {\n\t\tconst runtime = await getRuntime(config, migrationMode);\n\t\treturn runOutsideRequestWithRuntime(config, runtime, fn);\n\t}\n\n\tconst deferredTasks = createDeferredTaskTracker(() => {});\n\tconst context = {\n\t\teditMode: false,\n\t\tmetrics: createRequestMetrics(performance.now()),\n\t\tdeferredTasks,\n\t};\n\treturn runWithContext(context, async () => {\n\t\tconst runtime = await (async () => {\n\t\t\ttry {\n\t\t\t\treturn await getRuntime(config, migrationMode);\n\t\t\t} finally {\n\t\t\t\tdeferredTasks.settle();\n\t\t\t\tawait deferredTasks.settled;\n\t\t\t}\n\t\t})();\n\t\treturn runOutsideRequestWithRuntime(config, runtime, fn);\n\t});\n}\n\nasync function runOutsideRequestWithRuntime<T>(\n\tconfig: EmDashConfig,\n\truntime: EmDashRuntime,\n\tfn: (runtime: EmDashRuntime) => Promise<T>,\n): Promise<T> {\n\tconst scoped = createRequestScopedDb({\n\t\tconfig: config.database?.config,\n\t\tisAuthenticated: false,\n\t\t// Event handlers publish, clean up, or run jobs — a write workload —\n\t\t// so a connection-backed adapter routes them to the primary.\n\t\tisWrite: true,\n\t\tcanUseCachedBinding: false,\n\t\tcookies: NOOP_COOKIE_JAR,\n\t\turl: CRON_EVENT_URL,\n\t});\n\tif (!scoped) {\n\t\t// This adapter needs no event-specific routing or connection.\n\t\treturn fn(runtime);\n\t}\n\tconst { closed, deferredTasks, lifecycle } = coordinateScopedDbLifecycle(scoped);\n\n\tconst parent = getRequestContext();\n\tconst ctx = parent\n\t\t? { ...parent, db: scoped.db, deferredTasks }\n\t\t: {\n\t\t\t\teditMode: false,\n\t\t\t\tdb: scoped.db,\n\t\t\t\tmetrics: createRequestMetrics(performance.now()),\n\t\t\t\tdeferredTasks,\n\t\t\t};\n\ttry {\n\t\treturn await runWithContext(ctx, () => fn(runtime));\n\t} finally {\n\t\t// Guard both so a throw in teardown can't mask the callback result or\n\t\t// skip lifecycle settlement and leak the connection.\n\t\ttry {\n\t\t\tlifecycle.commit();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] event-scoped db commit failed:\", error);\n\t\t}\n\t\ttry {\n\t\t\tlifecycle.close?.();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] event-scoped db close failed:\", error);\n\t\t}\n\t\tawait closed;\n\t}\n}\n\n/**\n * A cookie jar that reads nothing and writes nothing, for request-scoped db\n * adapters invoked outside an HTTP request (the Cron Trigger sweep). Connection\n * adapters like Hyperdrive ignore cookies entirely; the D1 session adapter\n * reads/writes a bookmark cookie, but cron never reaches that path (it has no\n * `close()`), so the no-ops are never observed.\n */\nconst NOOP_COOKIE_JAR = {\n\tget: () => undefined,\n\tset: () => {},\n};\n\n/**\n * Synthetic URL for the cron sweep's request-scoped db opts. Only the D1\n * session adapter inspects `url` (for cookie `secure`), and cron doesn't take\n * that path, so the value is never used — it exists to satisfy the contract.\n */\nconst CRON_EVENT_URL = new URL(\"https://cron.emdash.internal/\");\n\nfunction resolveConfiguredMigrationMode(config: EmDashConfig): RuntimeMigrationMode {\n\tconst processOverride =\n\t\ttypeof process !== \"undefined\" && process.env ? process.env.EMDASH_MIGRATIONS_MODE : undefined;\n\tconst importMetaOverride = import.meta.env.EMDASH_MIGRATIONS_MODE;\n\treturn resolveRuntimeMigrationMode(config.migrations, {\n\t\tdev: import.meta.env.DEV,\n\t\toverride: processOverride ?? importMetaOverride,\n\t});\n}\n\nfunction pendingMigrationsResponse(error: PendingMigrationsError): Response {\n\tconsole.error(\"[emdash] database migrations are pending:\", error.pending.join(\", \"));\n\treturn migrationRequiredResponse();\n}\n\nfunction migrationRequiredResponse(): Response {\n\treturn new Response(\n\t\t\"Database migrations are required. Apply the deployment migration manifest and retry.\",\n\t\t{\n\t\t\tstatus: 503,\n\t\t\theaders: { \"Retry-After\": \"60\" },\n\t\t},\n\t);\n}\n\nfunction unmigratedDatabaseResponse(): Response {\n\treturn new Response(\n\t\t'<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"robots\" content=\"noindex\"><title>Site unavailable</title></head><body><p>This site\\'s database has not been migrated yet. <a href=\"/_emdash/admin/setup\">Set up EmDash</a></p></body></html>',\n\t\t{\n\t\t\tstatus: 503,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"text/html; charset=utf-8\",\n\t\t\t\t\"Cache-Control\": \"no-store\",\n\t\t\t\t\"Retry-After\": \"60\",\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction runtimeConfigurationErrorResponse(error: unknown, pathname: string): Response | null {\n\tif (!pathname.startsWith(\"/_emdash/api/\")) return null;\n\tif (error instanceof EmDashConfigurationError) {\n\t\treturn apiError(error.code, error.message, 500);\n\t}\n\tif (error instanceof EmDashStorageError && error.code === \"BINDING_NOT_FOUND\") {\n\t\treturn apiError(error.code, error.message, 500);\n\t}\n\treturn null;\n}\n\n/**\n * Baseline security headers applied to all responses.\n * Admin routes get additional headers (strict CSP) from auth middleware.\n */\nfunction finalizeResponse(\n\tresponse: Response,\n\tserverTimings?: Array<{ name: string; dur: number; desc?: string }>,\n): Response {\n\tconst res = new Response(response.body, response);\n\tconst astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);\n\tif (astroCookies !== undefined) {\n\t\tReflect.set(res, ASTRO_COOKIES_SYMBOL, astroCookies);\n\t}\n\t// Set-if-absent so a host app that sets stricter values on its own routes\n\t// wins. The middleware registers `order: 'pre'` (#1282), so on the response\n\t// path it runs *after* host middleware; unconditional `set()` would clobber\n\t// the host's headers on every public route (#1393). Mirrors the CSP guard.\n\tif (!res.headers.has(\"X-Content-Type-Options\")) {\n\t\tres.headers.set(\"X-Content-Type-Options\", \"nosniff\");\n\t}\n\tif (!res.headers.has(\"Referrer-Policy\")) {\n\t\tres.headers.set(\"Referrer-Policy\", \"strict-origin-when-cross-origin\");\n\t}\n\tif (!res.headers.has(\"Permissions-Policy\")) {\n\t\tres.headers.set(\"Permissions-Policy\", \"camera=(), microphone=(), geolocation=(), payment=()\");\n\t}\n\tif (!res.headers.has(\"Content-Security-Policy\")) {\n\t\tres.headers.set(\"X-Frame-Options\", \"SAMEORIGIN\");\n\t}\n\tif (serverTimings && serverTimings.length > 0) {\n\t\tres.headers.set(\n\t\t\t\"Server-Timing\",\n\t\t\tserverTimings\n\t\t\t\t.map((t) => {\n\t\t\t\t\tconst dur = Math.round(t.dur);\n\t\t\t\t\treturn t.desc ? `${t.name};dur=${dur};desc=\"${t.desc}\"` : `${t.name};dur=${dur}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \"),\n\t\t);\n\t}\n\treturn res;\n}\n\n/**\n * Append always-on counters (db.*, cache.*) to the Server-Timing list.\n *\n * dur values for `count`, `hit`, `miss` are integer counts — Server-Timing\n * spec only models milliseconds, but browsers show whatever number is given,\n * which is the convention most projects use for non-time samples.\n */\nfunction pushMetricsTimings(\n\ttimings: Array<{ name: string; dur: number; desc?: string }>,\n\tmetrics: RequestMetrics,\n): void {\n\tif (metrics.dbCount > 0) {\n\t\ttimings.push({ name: \"db.total\", dur: metrics.dbTotalMs, desc: \"DB total\" });\n\t\ttimings.push({ name: \"db.count\", dur: metrics.dbCount, desc: \"Query count\" });\n\t\tif (metrics.dbFirstOffset !== null) {\n\t\t\ttimings.push({ name: \"db.first\", dur: metrics.dbFirstOffset, desc: \"First query at\" });\n\t\t}\n\t\tif (metrics.dbLastOffset !== null) {\n\t\t\ttimings.push({ name: \"db.last\", dur: metrics.dbLastOffset, desc: \"Last query at\" });\n\t\t}\n\t}\n\tif (metrics.rpcCount > 0) {\n\t\ttimings.push({ name: \"rpc.count\", dur: metrics.rpcCount, desc: \"DB round trips\" });\n\t}\n\tif (metrics.cacheHits + metrics.cacheMisses > 0) {\n\t\ttimings.push({ name: \"cache.hit\", dur: metrics.cacheHits, desc: \"Cache hits\" });\n\t\ttimings.push({ name: \"cache.miss\", dur: metrics.cacheMisses, desc: \"Cache misses\" });\n\t}\n}\n\n/** Public routes that require the runtime (sitemap, robots.txt, etc.) */\nconst PUBLIC_RUNTIME_ROUTES = new Set([\"/sitemap.xml\", \"/robots.txt\"]);\nconst SITEMAP_COLLECTION_RE = /^\\/sitemap-[a-z][a-z0-9_]*\\.xml$/;\n\n/**\n * Ask the configured database adapter for a per-request scoped Kysely. The\n * adapter encapsulates any per-request semantics (D1 sessions, read-replica\n * routing, bookmark cookies, etc.); core just forwards the cookie jar and\n * request flags and wraps next() in ALS if a scope was returned.\n */\nfunction createRequestScopedDb(\n\topts: RequestScopedDbOpts,\n): { db: Kysely<Database>; commit: () => void; close?: () => void } | null {\n\tif (typeof virtualCreateRequestScopedDb !== \"function\") return null;\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- adapter returns Kysely<unknown>; cast to Database since core owns that type\n\tconst fn = virtualCreateRequestScopedDb as (\n\t\to: RequestScopedDbOpts,\n\t) => { db: Kysely<Database>; commit: () => void; close?: () => void } | null;\n\treturn fn(opts);\n}\n\nconst buildDate = virtualBuildTime ? new Date(virtualBuildTime) : null;\n\n/**\n * Fold the build timestamp into the route cache validator.\n *\n * `CacheHint.lastModified` describes the content, but the response also depends\n * on the build: `/_astro/*` names are content-hashed, and a deployment only\n * serves its own. Without the build dimension a code-only deploy answers a\n * returning visitor's conditional request with 304, leaving them on HTML whose\n * assets 404.\n *\n * Prerendered pages are served by the host's static layer, which manages its\n * own validators — only on-demand responses need the build dimension.\n *\n * Only forward moves are covered. `Last-Modified` expresses newer, not\n * different, so after a rollback the earlier build still answers a conditional\n * request with 304 and the browser stays on the newer build's HTML.\n *\n * Must run before next(): Astro keeps the later of two dates, so a route's own\n * hint still wins when content is newer, and a route that opts out with\n * `Astro.cache.set(false)` stays opted out — calling set() afterwards would\n * clear that opt-out.\n */\nfunction applyBuildValidator(context: APIContext): void {\n\tif (context.isPrerendered || !buildDate || !context.cache?.enabled) return;\n\tcontext.cache.set({ lastModified: buildDate });\n}\n\nexport const onRequest = defineMiddleware(async (context, next) => {\n\tconst { request, locals, cookies } = context;\n\tconst url = context.url;\n\n\t// Fast path: routes outside /_emdash/ that plugins inject (e.g.,\n\t// /.well-known/atproto-client-metadata.json) skip the entire runtime\n\t// init + middleware chain. External servers fetch these with tight\n\t// timeouts (~1-2s) so they must respond quickly even on cold starts.\n\tif (!url.pathname.startsWith(\"/_emdash\") && virtualConfig?.authProviders) {\n\t\tconst isPluginFastRoute = virtualConfig.authProviders.some(\n\t\t\t(p: { routes?: { pattern?: string }[] }) =>\n\t\t\t\tp.routes?.some((r: { pattern?: string }) => r.pattern && url.pathname === r.pattern),\n\t\t);\n\t\tif (isPluginFastRoute) {\n\t\t\treturn finalizeResponse(await next());\n\t\t}\n\t}\n\n\tapplyBuildValidator(context);\n\n\tconst queryRecorder = isInstrumentationEnabled()\n\t\t? createRecorder(url.pathname, request.method, request.headers.get(\"x-perf-phase\") ?? \"default\")\n\t\t: undefined;\n\n\tconst metrics = createRequestMetrics(performance.now());\n\n\tconst run = async (): Promise<Response> => {\n\t\tconst config = getConfig();\n\t\tconst migrationMode = config ? resolveConfiguredMigrationMode(config) : \"auto\";\n\t\t// Process /_emdash routes and public routes with an active session\n\t\t// (logged-in editors need the runtime for toolbar/visual editing on public pages)\n\t\tconst isEmDashRoute = url.pathname.startsWith(\"/_emdash\");\n\t\tconst isPublicRuntimeRoute =\n\t\t\tPUBLIC_RUNTIME_ROUTES.has(url.pathname) || SITEMAP_COLLECTION_RE.test(url.pathname);\n\n\t\t// Check for edit mode cookie - editors viewing public pages need the runtime\n\t\t// so auth middleware can verify their session for visual editing\n\t\tconst hasEditCookie = cookies.get(\"emdash-edit-mode\")?.value === \"true\";\n\t\tconst hasPreviewToken = url.searchParams.has(\"_preview\");\n\n\t\t// Playground mode: the playground middleware stashes the per-session DO database\n\t\t// on locals.__playgroundDb. When present, use runWithContext() to make it\n\t\t// available to getDb() and the runtime's db getter via the correct ALS instance.\n\t\tconst playgroundDb = locals.__playgroundDb;\n\n\t\t// Read the Astro session user once up-front. Both the anonymous fast path\n\t\t// and the full doInit path need this, and the session store is network-backed\n\t\t// (KV / Durable Object) so we want to avoid re-fetching on the hot path.\n\t\t// Skipped entirely for:\n\t\t// - prerendered requests (no session at build time)\n\t\t// - requests without an `astro-session` cookie (no session to look up)\n\t\t// The cookie check matters on Cloudflare Workers, where Astro's session\n\t\t// backend is KV: calling session.get() on every anonymous public request\n\t\t// turns normal traffic into a flood of KV read misses. See #733.\n\t\tconst hasSessionCookie = cookies.get(\"astro-session\") !== undefined;\n\t\tconst sessionUser =\n\t\t\tcontext.isPrerendered || !hasSessionCookie ? null : await resolveSessionUser(context.session);\n\n\t\t// Credentialed API requests (API tokens `ec_pat_*`, OAuth tokens\n\t\t// `ec_oat_*`, and other Bearer credentials) carry no `astro-session`\n\t\t// cookie, so `sessionUser` is null for them -- yet they still expect\n\t\t// read-your-writes. The auth middleware that resolves the token runs\n\t\t// *after* this one, so `locals.user` isn't populated here; detect the\n\t\t// credential directly on the request. Request-scoped adapters use this to\n\t\t// keep such requests on the primary/uncached connection (not a lagging\n\t\t// read replica or the Hyperdrive query cache).\n\t\tconst hasBearerAuth = (request.headers.get(\"authorization\") ?? \"\")\n\t\t\t.toLowerCase()\n\t\t\t.startsWith(\"bearer \");\n\t\tconst isWrite = request.method !== \"GET\" && request.method !== \"HEAD\";\n\t\tconst isAuthenticated = !!sessionUser || hasBearerAuth;\n\t\tconst endedAuthenticated = () => requestEndedAuthenticated(isAuthenticated, cookies);\n\t\tconst canUseCachedBinding =\n\t\t\t!isAuthenticated &&\n\t\t\t!isWrite &&\n\t\t\t!playgroundDb &&\n\t\t\t!isEmDashRoute &&\n\t\t\t!hasEditCookie &&\n\t\t\t!hasPreviewToken;\n\n\t\tif (!isEmDashRoute && !isPublicRuntimeRoute && !hasEditCookie && !hasPreviewToken) {\n\t\t\tif (!sessionUser && !playgroundDb) {\n\t\t\t\tconst timings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\t\tconst mwStart = performance.now();\n\n\t\t\t\t// On a fresh deployment the database may be completely empty.\n\t\t\t\t// Public pages call getSiteSettings() / getMenu() via getDb(), which\n\t\t\t\t// bypasses runtime init and would crash with \"no such table: options\".\n\t\t\t\t// Do a one-time lightweight probe using the same getDb() instance the\n\t\t\t\t// page will use: if the migrations table doesn't exist, no migrations\n\t\t\t\t// have ever run, and the page may only render once runtime init below\n\t\t\t\t// has migrated the database.\n\t\t\t\t// Skip the probe when prerendering: the build database is legitimately\n\t\t\t\t// empty in CI, and a response derived from it would be baked into the\n\t\t\t\t// static page (session reads are already skipped for prerender above\n\t\t\t\t// for the same reason).\n\t\t\t\tlet unmigrated = false;\n\t\t\t\tlet probeFailed = false;\n\t\t\t\tif (migrationMode === \"auto\" && !isSetupVerified() && !context.isPrerendered) {\n\t\t\t\t\tconst t0 = performance.now();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { getDb } = await import(\"../loader.js\");\n\t\t\t\t\t\tconst db = await getDb();\n\t\t\t\t\t\tawait db.selectFrom(\"_emdash_migrations\").selectAll().limit(1).execute();\n\t\t\t\t\t\tmarkSetupVerified();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (isMissingTableError(error)) {\n\t\t\t\t\t\t\tunmigrated = true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Any other failure (transient D1/replica error, timeout, cold-start\n\t\t\t\t\t\t\t// race, locked SQLite) must NOT be read as \"fresh install\". Leave the\n\t\t\t\t\t\t\t// flag unset so a later request can re-verify, and fall through to\n\t\t\t\t\t\t\t// render the page normally. Unless a runtime is already running,\n\t\t\t\t\t\t\t// skip runtime init for this request: it would connect to the same\n\t\t\t\t\t\t\t// database again and make the visitor wait through a second timeout.\n\t\t\t\t\t\t\tprobeFailed = !getRuntimeHolder().instance;\n\t\t\t\t\t\t\tconsole.error(\"Setup probe failed (non-fatal):\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttimings.push({ name: \"setup\", dur: performance.now() - t0, desc: \"Setup probe\" });\n\t\t\t\t}\n\n\t\t\t\t// Initialize the runtime for page:metadata and page:fragments hooks.\n\t\t\t\t// The runtime is a cached singleton — after the first request,\n\t\t\t\t// getRuntime() is just a null-check. This enables SEO plugins to\n\t\t\t\t// contribute meta tags for all visitors, not just logged-in editors,\n\t\t\t\t// except on a request whose setup probe just failed.\n\t\t\t\tif (config && !probeFailed) {\n\t\t\t\t\t// Sub-phase timings are populated only on the cold init. Warm\n\t\t\t\t\t// requests hit the cached runtime and leave this empty.\n\t\t\t\t\tconst initSubTimings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\t\t\tconst t0 = performance.now();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst runtime = await getRuntime(config, migrationMode, initSubTimings);\n\t\t\t\t\t\tmarkSetupVerified();\n\t\t\t\t\t\tconst handlePublicPluginApiRoute = createPublicPluginApiRouteHandler(runtime);\n\t\t\t\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- partial object; getPageRuntime() only checks for the page-contribution methods\n\t\t\t\t\t\tlocals.emdash = {\n\t\t\t\t\t\t\thandlePublicPluginApiRoute,\n\t\t\t\t\t\t\tcollectPageMetadata: runtime.collectPageMetadata.bind(runtime),\n\t\t\t\t\t\t\tcollectPageFragments: runtime.collectPageFragments.bind(runtime),\n\t\t\t\t\t\t\tgetPublicMediaUrl: createPublicMediaUrlResolver(runtime.storage),\n\t\t\t\t\t\t\t// Exposed so the wrapped image endpoint (`/_image`) can read media\n\t\t\t\t\t\t\t// bytes from storage on the anonymous fast path -- public `<img>`\n\t\t\t\t\t\t\t// requests carry no session.\n\t\t\t\t\t\t\tstorage: runtime.storage,\n\t\t\t\t\t\t} as EmDashHandlers;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (error instanceof PendingMigrationsError) {\n\t\t\t\t\t\t\treturn pendingMigrationsResponse(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (migrationMode === \"manual\" && isMissingTableError(error)) {\n\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\"[emdash] database schema is unavailable in manual migration mode:\",\n\t\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn migrationRequiredResponse();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Non-fatal — EmDashHead falls back to base SEO contributions —\n\t\t\t\t\t\t// but log it (throttled): a persistently failing init (e.g. a\n\t\t\t\t\t\t// failing migration, #1744) is otherwise invisible on the\n\t\t\t\t\t\t// anonymous path, silently degrading every public page.\n\t\t\t\t\t\tif (Date.now() - lastRuntimeInitErrorLogAt >= RUNTIME_INIT_ERROR_LOG_INTERVAL_MS) {\n\t\t\t\t\t\t\tlastRuntimeInitErrorLogAt = Date.now();\n\t\t\t\t\t\t\tconsole.error(\"[emdash] runtime init failed (page renders without CMS data):\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttimings.push({ name: \"rt\", dur: performance.now() - t0, desc: \"Runtime init\" });\n\t\t\t\t\t// Append cold-only sub-phase timings so the breakdown is visible\n\t\t\t\t\t// in Server-Timing (rt.db, rt.fts, rt.plugins, rt.site,\n\t\t\t\t\t// rt.sandbox, rt.market, rt.hooks, rt.cron).\n\t\t\t\t\tfor (const sub of initSubTimings) timings.push(sub);\n\t\t\t\t}\n\t\t\t\tif (unmigrated && !locals.emdash) {\n\t\t\t\t\treturn finalizeResponse(unmigratedDatabaseResponse(), timings);\n\t\t\t\t}\n\n\t\t\t\t// Even on the anonymous fast path we ask the adapter for a per-request\n\t\t\t\t// scoped db. For D1 with read replication this routes anonymous reads\n\t\t\t\t// to the nearest replica; for other adapters it's a no-op.\n\t\t\t\tconst lastContentWriteAt =\n\t\t\t\t\tcanUseCachedBinding && config?.database?.needsLastContentWriteAt\n\t\t\t\t\t\t? await getLastContentWriteAt()\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst anonScoped = createRequestScopedDb({\n\t\t\t\t\tconfig: config?.database?.config,\n\t\t\t\t\tisAuthenticated,\n\t\t\t\t\tendedAuthenticated,\n\t\t\t\t\tisWrite,\n\t\t\t\t\tcanUseCachedBinding,\n\t\t\t\t\tcookies,\n\t\t\t\t\turl,\n\t\t\t\t\tlastContentWriteAt,\n\t\t\t\t});\n\t\t\t\tconst runAnon = async () => {\n\t\t\t\t\tconst t0 = performance.now();\n\t\t\t\t\tconst response = await next();\n\t\t\t\t\ttimings.push({ name: \"render\", dur: performance.now() - t0, desc: \"Page render\" });\n\t\t\t\t\ttimings.push({ name: \"mw\", dur: performance.now() - mwStart, desc: \"Total middleware\" });\n\t\t\t\t\tpushMetricsTimings(timings, metrics);\n\t\t\t\t\t// Server-Timing only sees pre-stream queries; the stream-end\n\t\t\t\t\t// wrapper (instrumentation-gated, no-op otherwise) emits the\n\t\t\t\t\t// final counters once the body finishes streaming.\n\t\t\t\t\treturn wrapBodyForStreamMetrics(finalizeResponse(response, timings));\n\t\t\t\t};\n\t\t\t\tif (anonScoped) {\n\t\t\t\t\tconst { deferredTasks, lifecycle } = coordinateScopedDbLifecycle(anonScoped);\n\t\t\t\t\tconst parent = getRequestContext();\n\t\t\t\t\tconst ctx = parent\n\t\t\t\t\t\t? { ...parent, db: anonScoped.db, deferredTasks }\n\t\t\t\t\t\t: { editMode: false, db: anonScoped.db, metrics, deferredTasks };\n\t\t\t\t\t// Eagerly warm site-global layout data (menus, widget areas,\n\t\t\t\t\t// taxonomy terms, settings) concurrently so the layout's\n\t\t\t\t\t// per-component reads overlap into ~one wall-clock round trip and\n\t\t\t\t\t// hit a warm cache instead of serializing. Three guards:\n\t\t\t\t\t// - request-scoped (remote) backend only -- this branch implies it;\n\t\t\t\t\t// pointless on synchronous local SQLite.\n\t\t\t\t\t// - HTML navigations only -- feeds/sitemaps/JSON don't render the\n\t\t\t\t\t// layout, so prefetching their chrome is pure waste.\n\t\t\t\t\t// - the work starts immediately, then after() keeps both it and the\n\t\t\t\t\t// request-scoped connection alive until the response also finishes.\n\t\t\t\t\t// Gate on the CLIENT'S PREFERRED type (leading media range), not a\n\t\t\t\t\t// substring -- browser navigations lead with `text/html`, while feed\n\t\t\t\t\t// readers lead with `application/rss+xml` etc. and only list\n\t\t\t\t\t// `text/html;q=0.8` later, so a substring match would leak onto feeds.\n\t\t\t\t\tconst acceptsHtml = (request.headers.get(\"accept\") ?? \"\")\n\t\t\t\t\t\t.split(\",\", 1)[0]!\n\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t.startsWith(\"text/html\");\n\t\t\t\t\treturn runWithContext(ctx, async () => {\n\t\t\t\t\t\tif (acceptsHtml) after(() => prefetchLayoutData());\n\t\t\t\t\t\t// commit() persists per-request state (e.g. the D1 bookmark cookie)\n\t\t\t\t\t\t// before the response is returned, even if render throws; close()\n\t\t\t\t\t\t// waits for stream-end and request-owned deferred work. See finishScoped.\n\t\t\t\t\t\treturn finishScoped(lifecycle, runAnon);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn runAnon();\n\t\t\t}\n\t\t}\n\n\t\tif (!config) {\n\t\t\tconsole.error(\"EmDash: No configuration found\");\n\t\t\treturn finalizeResponse(await next());\n\t\t}\n\n\t\t// In playground mode, wrap the entire runtime init + request handling in\n\t\t// runWithContext so that getDatabase() and all init queries use the real\n\t\t// DO database via the same AsyncLocalStorage instance as the loader.\n\t\tconst doInit = async () => {\n\t\t\tconst timings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\tconst mwStart = performance.now();\n\n\t\t\ttry {\n\t\t\t\t// Get or create runtime. Sub-phase timings (rt.db, rt.fts, rt.plugins,\n\t\t\t\t// rt.site, rt.sandbox, rt.market, rt.hooks, rt.cron) are populated\n\t\t\t\t// only on the cold init — subsequent warm calls find the cached\n\t\t\t\t// instance and `initSubTimings` stays empty.\n\t\t\t\tconst initSubTimings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\t\tlet t0 = performance.now();\n\t\t\t\tconst runtime = await getRuntime(config, migrationMode, initSubTimings);\n\t\t\t\ttimings.push({ name: \"rt\", dur: performance.now() - t0, desc: \"Runtime init\" });\n\t\t\t\t// Forward any sub-phase samples so cold-start breakdown is visible\n\t\t\t\t// in Server-Timing. Each phase appears prefixed \"rt.\" to distinguish\n\t\t\t\t// from the aggregate \"rt\" timing above.\n\t\t\t\tfor (const sub of initSubTimings) timings.push(sub);\n\n\t\t\t\t// Runtime initialization has satisfied the effective migration policy.\n\t\t\t\tmarkSetupVerified();\n\n\t\t\t\t// The manifest is no longer pre-loaded here. It's admin-only\n\t\t\t\t// content that public/anonymous requests never read, and\n\t\t\t\t// loading it on every request put logged-out hot paths on\n\t\t\t\t// the same staleness budget as admin operations. Admin\n\t\t\t\t// routes call `emdash.getManifest()` directly.\n\n\t\t\t\t// Attach to locals for route handlers\n\t\t\t\tlocals.emdash = {\n\t\t\t\t\t// Content handlers\n\t\t\t\t\thandleContentList: runtime.handleContentList.bind(runtime),\n\t\t\t\t\thandleContentGet: runtime.handleContentGet.bind(runtime),\n\t\t\t\t\thandleContentAuthors: runtime.handleContentAuthors.bind(runtime),\n\t\t\t\t\thandleContentCreate: runtime.handleContentCreate.bind(runtime),\n\t\t\t\t\thandleContentUpdate: runtime.handleContentUpdate.bind(runtime),\n\t\t\t\t\thandleContentDelete: runtime.handleContentDelete.bind(runtime),\n\n\t\t\t\t\t// Trash handlers\n\t\t\t\t\thandleContentListTrashed: runtime.handleContentListTrashed.bind(runtime),\n\t\t\t\t\thandleContentRestore: runtime.handleContentRestore.bind(runtime),\n\t\t\t\t\thandleContentPermanentDelete: runtime.handleContentPermanentDelete.bind(runtime),\n\t\t\t\t\thandleContentCountTrashed: runtime.handleContentCountTrashed.bind(runtime),\n\t\t\t\t\thandleContentGetIncludingTrashed: runtime.handleContentGetIncludingTrashed.bind(runtime),\n\n\t\t\t\t\t// Duplicate handler\n\t\t\t\t\thandleContentDuplicate: runtime.handleContentDuplicate.bind(runtime),\n\n\t\t\t\t\t// Publishing & Scheduling handlers\n\t\t\t\t\thandleContentPublish: runtime.handleContentPublish.bind(runtime),\n\t\t\t\t\thandleContentUnpublish: runtime.handleContentUnpublish.bind(runtime),\n\t\t\t\t\thandleContentSchedule: runtime.handleContentSchedule.bind(runtime),\n\t\t\t\t\thandleContentUnschedule: runtime.handleContentUnschedule.bind(runtime),\n\t\t\t\t\thandleContentCountScheduled: runtime.handleContentCountScheduled.bind(runtime),\n\t\t\t\t\thandleContentDiscardDraft: runtime.handleContentDiscardDraft.bind(runtime),\n\t\t\t\t\thandleContentCompare: runtime.handleContentCompare.bind(runtime),\n\t\t\t\t\thandleContentTranslations: runtime.handleContentTranslations.bind(runtime),\n\n\t\t\t\t\t// Media handlers\n\t\t\t\t\thandleMediaList: runtime.handleMediaList.bind(runtime),\n\t\t\t\t\thandleMediaGet: runtime.handleMediaGet.bind(runtime),\n\t\t\t\t\thandleMediaUpload: runtime.handleMediaUpload.bind(runtime),\n\t\t\t\t\thandleMediaCreate: runtime.handleMediaCreate.bind(runtime),\n\t\t\t\t\thandleMediaUpdate: runtime.handleMediaUpdate.bind(runtime),\n\t\t\t\t\t...(runtime.handleMediaReplaceMetadata\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\thandleMediaReplaceMetadata: runtime.handleMediaReplaceMetadata.bind(runtime),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\thandleMediaDelete: runtime.handleMediaDelete.bind(runtime),\n\n\t\t\t\t\t// Comment administration\n\t\t\t\t\t...(runtime.handleCommentModerate\n\t\t\t\t\t\t? { handleCommentModerate: runtime.handleCommentModerate.bind(runtime) }\n\t\t\t\t\t\t: {}),\n\n\t\t\t\t\t// Revision handlers\n\t\t\t\t\thandleRevisionList: runtime.handleRevisionList.bind(runtime),\n\t\t\t\t\thandleRevisionGet: runtime.handleRevisionGet.bind(runtime),\n\t\t\t\t\thandleRevisionRestore: runtime.handleRevisionRestore.bind(runtime),\n\n\t\t\t\t\t// Plugin routes\n\t\t\t\t\thandlePluginApiRoute: runtime.handlePluginApiRoute.bind(runtime),\n\t\t\t\t\tgetPluginEditorExtension: runtime.getPluginEditorExtension.bind(runtime),\n\t\t\t\t\tgetPluginEditorDraftSchema: runtime.getPluginEditorDraftSchema.bind(runtime),\n\t\t\t\t\thandlePublicPluginApiRoute: createPublicPluginApiRouteHandler(runtime),\n\t\t\t\t\tgetPluginRouteMeta: runtime.getPluginRouteMeta.bind(runtime),\n\t\t\t\t\tgetPluginMcpTools: runtime.getPluginMcpTools.bind(runtime),\n\t\t\t\t\tgetEnabledPluginMcpTools: runtime.getEnabledPluginMcpTools.bind(runtime),\n\t\t\t\t\tserializePluginMcpConsent: runtime.serializePluginMcpConsent.bind(runtime),\n\t\t\t\t\thandlePluginMcpTool: runtime.handlePluginMcpTool.bind(runtime),\n\t\t\t\t\thandlePluginMcpDenied: runtime.handlePluginMcpDenied.bind(runtime),\n\n\t\t\t\t\t// Media provider methods\n\t\t\t\t\tgetMediaProvider: runtime.getMediaProvider.bind(runtime),\n\t\t\t\t\tgetMediaProviderList: runtime.getMediaProviderList.bind(runtime),\n\n\t\t\t\t\t// Page contribution methods (for EmDashHead/EmDashBodyStart/EmDashBodyEnd)\n\t\t\t\t\tcollectPageMetadata: runtime.collectPageMetadata.bind(runtime),\n\t\t\t\t\tcollectPageFragments: runtime.collectPageFragments.bind(runtime),\n\n\t\t\t\t\t// Lazy search index health check — search endpoints call this\n\t\t\t\t\t// before querying so a crash-corrupted index gets repaired on\n\t\t\t\t\t// first use rather than stalling every cold start.\n\t\t\t\t\tensureSearchHealthy: runtime.ensureSearchHealthy.bind(runtime),\n\n\t\t\t\t\t// Direct access (for advanced use cases)\n\t\t\t\t\tstorage: runtime.storage,\n\t\t\t\t\t// Lazy getter, not an eager snapshot: `locals.emdash` is built\n\t\t\t\t\t// before the per-request scoped db is installed in ALS, so reading\n\t\t\t\t\t// `runtime.db` here would capture the per-isolate singleton. Routes\n\t\t\t\t\t// access `emdash.db` later, during the request, when the scoped db\n\t\t\t\t\t// is active. For a stateless binding (D1) the two are equivalent,\n\t\t\t\t\t// but for a request-bound connection (pg/Hyperdrive) the singleton\n\t\t\t\t\t// belongs to the cold-start request and reusing it from a warm\n\t\t\t\t\t// request hangs on workerd's cross-request I/O guard.\n\t\t\t\t\tget db() {\n\t\t\t\t\t\treturn runtime.db;\n\t\t\t\t\t},\n\t\t\t\t\tgetPublicMediaUrl: createPublicMediaUrlResolver(runtime.storage),\n\t\t\t\t\thooks: runtime.hooks,\n\t\t\t\t\temail: runtime.email,\n\t\t\t\t\tconfiguredPlugins: runtime.configuredPlugins,\n\t\t\t\t\tsandboxedPluginEntries: runtime.sandboxedPluginEntries,\n\n\t\t\t\t\t// Configuration (for checking database type, auth mode, etc.)\n\t\t\t\t\tconfig,\n\n\t\t\t\t\t// Lazy manifest accessor — admin-only consumers call this on\n\t\t\t\t\t// demand. `requestCached` inside `getManifest` dedupes within\n\t\t\t\t\t// a single request.\n\t\t\t\t\tgetManifest: runtime.getManifest.bind(runtime),\n\n\t\t\t\t\t// Clear the URL pattern cache after schema mutations that\n\t\t\t\t\t// affect collection URL patterns.\n\t\t\t\t\tinvalidateUrlPatternCache,\n\n\t\t\t\t\t// Sandbox runner (for marketplace plugin install/update)\n\t\t\t\t\tgetSandboxRunner: runtime.getSandboxRunner.bind(runtime),\n\t\t\t\t\tisSandboxBypassed: runtime.isSandboxBypassed.bind(runtime),\n\n\t\t\t\t\t// Sync marketplace plugin states (after install/update/uninstall)\n\t\t\t\t\tsyncMarketplacePlugins: runtime.syncMarketplacePlugins.bind(runtime),\n\n\t\t\t\t\t// Sync registry plugin states (after install/update/uninstall)\n\t\t\t\t\tsyncRegistryPlugins: runtime.syncRegistryPlugins.bind(runtime),\n\t\t\t\t\trunPluginInstallLifecycle: runtime.runPluginInstallLifecycle.bind(runtime),\n\t\t\t\t\trunPluginActivateLifecycle: runtime.runPluginActivateLifecycle.bind(runtime),\n\t\t\t\t\trunPluginUninstallLifecycle: runtime.runPluginUninstallLifecycle.bind(runtime),\n\t\t\t\t\tgetRuntimePluginSettingsSchema: runtime.getRuntimePluginSettingsSchema.bind(runtime),\n\n\t\t\t\t\t// Update plugin enabled/disabled status and rebuild hook pipeline\n\t\t\t\t\tsetPluginStatus: runtime.setPluginStatus.bind(runtime),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof PendingMigrationsError) {\n\t\t\t\t\treturn pendingMigrationsResponse(error);\n\t\t\t\t}\n\t\t\t\tif (migrationMode === \"manual\" && isMissingTableError(error)) {\n\t\t\t\t\tconsole.error(\"[emdash] database schema is unavailable in manual migration mode:\", error);\n\t\t\t\t\treturn migrationRequiredResponse();\n\t\t\t\t}\n\t\t\t\tconsole.error(\"EmDash middleware error:\", error);\n\t\t\t\tconst configurationError = runtimeConfigurationErrorResponse(error, url.pathname);\n\t\t\t\tif (configurationError) return configurationError;\n\t\t\t}\n\n\t\t\t// Ask the adapter for a request-scoped db. When it returns one, we stash\n\t\t\t// it in ALS so the runtime's db getter and loader's getDb() pick it up,\n\t\t\t// then call commit() after next() so the adapter can persist any\n\t\t\t// per-request state (e.g. a D1 bookmark cookie for read-your-writes).\n\t\t\tconst lastContentWriteAt =\n\t\t\t\tcanUseCachedBinding && config?.database?.needsLastContentWriteAt\n\t\t\t\t\t? await getLastContentWriteAt()\n\t\t\t\t\t: undefined;\n\t\t\tconst scoped = createRequestScopedDb({\n\t\t\t\tconfig: config?.database?.config,\n\t\t\t\tisAuthenticated,\n\t\t\t\tendedAuthenticated,\n\t\t\t\tisWrite,\n\t\t\t\tcanUseCachedBinding,\n\t\t\t\tcookies: context.cookies,\n\t\t\t\turl,\n\t\t\t\tlastContentWriteAt,\n\t\t\t});\n\n\t\t\tconst renderAndFinalize = async () => {\n\t\t\t\tconst t0 = performance.now();\n\t\t\t\tconst response = await next();\n\t\t\t\ttimings.push({ name: \"render\", dur: performance.now() - t0, desc: \"Page render\" });\n\t\t\t\ttimings.push({ name: \"mw\", dur: performance.now() - mwStart, desc: \"Total middleware\" });\n\t\t\t\tpushMetricsTimings(timings, metrics);\n\t\t\t\t// Server-Timing only sees pre-stream queries; the stream-end\n\t\t\t\t// wrapper (instrumentation-gated, no-op otherwise) emits the\n\t\t\t\t// final counters once the body finishes streaming.\n\t\t\t\treturn wrapBodyForStreamMetrics(finalizeResponse(response, timings));\n\t\t\t};\n\n\t\t\tif (scoped) {\n\t\t\t\tconst { deferredTasks, lifecycle } = coordinateScopedDbLifecycle(scoped);\n\t\t\t\tconst parent = getRequestContext();\n\t\t\t\tconst ctx = parent\n\t\t\t\t\t? { ...parent, db: scoped.db, deferredTasks }\n\t\t\t\t\t: { editMode: false, db: scoped.db, metrics, deferredTasks };\n\t\t\t\treturn runWithContext(ctx, () =>\n\t\t\t\t\t// commit() persists per-request state (e.g. the D1 bookmark cookie)\n\t\t\t\t\t// before the response returns, even if render throws; close()\n\t\t\t\t\t// waits for stream-end and request-owned deferred work. See finishScoped.\n\t\t\t\t\tfinishScoped(lifecycle, renderAndFinalize),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn renderAndFinalize();\n\t\t}; // end doInit\n\n\t\tif (playgroundDb) {\n\t\t\t// Read the edit-mode cookie to determine if visual editing is active.\n\t\t\t// Default to false -- editing is opt-in via the playground toolbar toggle.\n\t\t\tconst editMode = context.cookies.get(\"emdash-edit-mode\")?.value === \"true\";\n\t\t\t// Playground DBs are per-session isolated instances whose schema is\n\t\t\t// independent of the configured one — flag as isolated so schema-\n\t\t\t// derived caches (manifest, taxonomy defs) rebuild against it.\n\t\t\tconst parent = getRequestContext();\n\t\t\tconst ctx = parent\n\t\t\t\t? { ...parent, editMode, db: playgroundDb, dbIsIsolated: true }\n\t\t\t\t: { editMode, db: playgroundDb, dbIsIsolated: true, metrics };\n\t\t\treturn runWithContext(ctx, doInit);\n\t\t}\n\t\treturn doInit();\n\t};\n\n\ttry {\n\t\treturn await runWithContext({ editMode: false, queryRecorder, metrics }, run);\n\t} finally {\n\t\t// Streamed responses defer the flush to stream end (see\n\t\t// wrapBodyForStreamMetrics) so the log captures queries issued while\n\t\t// the body renders. Only flush here for responses that were not\n\t\t// wrapped (no body: redirects, 304s, bodyless errors), where all\n\t\t// queries have already run by the time middleware returns.\n\t\tif (queryRecorder && !queryRecorder.deferredFlush) flushRecorder(queryRecorder);\n\t}\n});\n\nexport default onRequest;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"middleware.mjs","names":["virtualPlugins","virtualCreateDialect","virtualCreateCoalescingDialect","virtualCreateStorage","virtualCreateScheduler","virtualSandboxedPlugins","virtualMediaProviders","createRequestScopedDb","virtualCreateRequestScopedDb","virtualBuildTime"],"sources":["../../src/astro/middleware/scoped-db.ts","../../src/astro/middleware/stream-end-metrics.ts","../../src/astro/prefetch.ts","../../src/astro/public-plugin-api-routes.ts","../../src/astro/middleware.ts"],"sourcesContent":["/**\n * Request-scoped database lifecycle helpers.\n *\n * Extracted from middleware.ts so they can be unit-tested without pulling in\n * the virtual:emdash/* module graph. The middleware imports these to settle a\n * request-scoped db adapter's lifecycle around the response.\n */\n\nimport { createDeferredTaskTracker } from \"../../deferred-tasks.js\";\nimport type { DeferredTaskTracker } from \"../../deferred-tasks.js\";\n\n/**\n * Astro attaches AstroCookies to outgoing responses via a well-known global\n * symbol. Cloning a Response (`new Response(body, init)`) drops non-header\n * metadata, so any helper that wraps the response must explicitly forward this\n * symbol or `cookies.set()` calls will be silently dropped. `Symbol.for`\n * returns the same registry symbol everywhere, so this matches the copy in\n * middleware.ts.\n */\nexport const ASTRO_COOKIES_SYMBOL = Symbol.for(\"astro.cookies\");\n\ninterface ScopedDbLifecycle {\n\tcommit: () => void;\n\tclose?: () => void;\n}\n\n/**\n * Whether the request ended authenticated, for the db adapter's `commit()`.\n * `isAuthenticated` is captured before the route runs, so it misses\n * login/signup/invite requests that create the Astro session mid-request;\n * `session.set()` writes the `astro-session` cookie into the jar synchronously,\n * so scanning the outgoing cookies detects them. Only outgoing cookies count —\n * a stale `astro-session` cookie arriving on an anonymous render must not\n * qualify, or its replica-read bookmark would overwrite a fresher one from an\n * earlier authenticated write.\n */\nexport function requestEndedAuthenticated(\n\tisAuthenticated: boolean,\n\tcookies: { headers(): Iterable<string> },\n): boolean {\n\tif (isAuthenticated) return true;\n\tfor (const header of cookies.headers()) {\n\t\tif (header.startsWith(\"astro-session=\")) return true;\n\t}\n\treturn false;\n}\n\n/** Hold the real adapter close behind both response and deferred-task completion. */\nexport function coordinateScopedDbLifecycle(scoped: ScopedDbLifecycle): {\n\tclosed?: Promise<void>;\n\tdeferredTasks?: DeferredTaskTracker;\n\tlifecycle: ScopedDbLifecycle;\n} {\n\tif (!scoped.close) return { lifecycle: scoped };\n\n\tconst close = scoped.close;\n\tconst deferredTasks = createDeferredTaskTracker(() => {\n\t\ttry {\n\t\t\tclose();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] request-scoped db close failed:\", error);\n\t\t}\n\t});\n\treturn {\n\t\tclosed: deferredTasks.settled,\n\t\tdeferredTasks,\n\t\tlifecycle: { commit: scoped.commit, close: deferredTasks.settle },\n\t};\n}\n\n/**\n * Run a request-scoped db's `close()` once the response body has finished\n * streaming. Astro streams HTML and components issue DB queries during that\n * stream, so a connection-backed adapter (e.g. Postgres over Hyperdrive) must\n * not be torn down until the body is flushed. Bodyless responses (redirects,\n * 304s, errors) close immediately. A guard makes close idempotent and a stream\n * `cancel` (client disconnect) still triggers it so connections never leak.\n *\n * No-op for adapters without a `close` (D1): the response passes through.\n */\nexport function wrapResponseForScopedClose(response: Response, close: () => void): Response {\n\tlet closed = false;\n\tconst runClose = () => {\n\t\tif (closed) return;\n\t\tclosed = true;\n\t\ttry {\n\t\t\tclose();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] request-scoped db close failed:\", error);\n\t\t}\n\t};\n\n\tif (!response.body) {\n\t\trunClose();\n\t\treturn response;\n\t}\n\n\tconst transform = new TransformStream<Uint8Array, Uint8Array>({\n\t\tflush: runClose,\n\t\tcancel: runClose,\n\t});\n\tconst wrapped = new Response(response.body.pipeThrough(transform), response);\n\tconst astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);\n\tif (astroCookies !== undefined) {\n\t\tReflect.set(wrapped, ASTRO_COOKIES_SYMBOL, astroCookies);\n\t}\n\t// Byte counts are preserved by the identity transform, but a stale\n\t// Content-Length on a reconstructed streaming Response risks truncation.\n\twrapped.headers.delete(\"Content-Length\");\n\treturn wrapped;\n}\n\n/**\n * Run the request body under a request-scoped db, then settle its lifecycle:\n * `commit()` runs before the response is returned (so per-request state like a\n * D1 bookmark cookie is persisted in the headers, even if render throws), while\n * `close()` (if any) is deferred to lifecycle settlement so a\n * connection-backed adapter isn't torn down mid-render or mid-task. On error\n * the lifecycle is settled before rethrowing so it never leaks.\n *\n * On the error path both `commit()` and `close()` are defended: a throw from\n * either is logged and swallowed so it can't replace the propagating render\n * error (which is the one the caller needs to see). On the success path\n * `commit()` is guarded too — if it throws, the connection is closed before the\n * failure is surfaced, so it never leaks. For the current adapters `commit()`\n * is a no-op (Hyperdrive) or a cookie write (D1, no `close`) and `close()` is\n * fire-and-forget, so these guards only matter for a future connection-backed\n * adapter with throwing teardown, but the helper is generic and must not leak\n * or mask.\n */\nexport async function finishScoped(\n\tscoped: ScopedDbLifecycle,\n\trun: () => Promise<Response>,\n): Promise<Response> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await run();\n\t} catch (error) {\n\t\t// A render error is already propagating; neither commit nor close may\n\t\t// mask it, and close must still run so the connection doesn't leak.\n\t\tcommitSafely(scoped.commit);\n\t\tcloseSafely(scoped.close);\n\t\tthrow error;\n\t}\n\ttry {\n\t\tscoped.commit();\n\t} catch (error) {\n\t\t// commit() failed on the success path: close the connection now (the\n\t\t// response won't be wrapped, so stream-end close would never run) and\n\t\t// surface the failure. close is swallowed so it can't mask the commit\n\t\t// error that the caller needs to see.\n\t\tcloseSafely(scoped.close);\n\t\tthrow error;\n\t}\n\treturn scoped.close ? wrapResponseForScopedClose(response, scoped.close) : response;\n}\n\n/**\n * Run commit() swallowing any error. Used where an exception is already\n * propagating (or about to be thrown) and a commit failure must neither mask it\n * nor skip the subsequent close().\n */\nfunction commitSafely(commit: () => void): void {\n\ttry {\n\t\tcommit();\n\t} catch (error) {\n\t\tconsole.error(\"[emdash] request-scoped db commit failed during error handling:\", error);\n\t}\n}\n\n/**\n * Run close() swallowing any error. Used on the error/commit-failure paths\n * where another exception is the one the caller must see; a throwing teardown\n * must not replace it.\n */\nfunction closeSafely(close: (() => void) | undefined): void {\n\tif (!close) return;\n\ttry {\n\t\tclose();\n\t} catch (error) {\n\t\tconsole.error(\"[emdash] request-scoped db close failed during error handling:\", error);\n\t}\n}\n","/**\n * Stream-end metrics\n *\n * Server-Timing db.* counters are snapshotted when middleware's next()\n * returns — but at that point only the response *headers* are final.\n * Astro streams the body afterwards, and components rendered during\n * streaming issue further DB queries that the headers can never report.\n *\n * This module wraps the response body in an identity TransformStream and\n * snapshots the request metrics in flush(), i.e. when the body actually\n * finishes streaming. The metrics object lives on the request context\n * (AsyncLocalStorage) and is mutated in-place by the Kysely log hook, so\n * a reference captured before wrapping observes every post-header query.\n * The snapshot is emitted as prefixed NDJSON on stdout (same transport as\n * [emdash-query-log] — console.log works in both Node and workerd).\n *\n * Gated on isInstrumentationEnabled() (EMDASH_QUERY_LOG=1): zero overhead\n * in normal production traffic.\n */\n\nimport { flushRecorder, isInstrumentationEnabled } from \"../../database/instrumentation.js\";\nimport { getRequestContext } from \"../../request-context.js\";\n// Reuse the single source of truth for Astro's well-known cookies symbol\n// rather than redefining `Symbol.for(\"astro.cookies\")` here — it must stay in\n// lockstep with the copy the rest of the middleware forwards.\nimport { ASTRO_COOKIES_SYMBOL } from \"./scoped-db.js\";\n\nexport const STREAM_END_PREFIX = \"[emdash-stream-end]\";\n\n/** Shape of the NDJSON snapshot emitted when the body finishes streaming. */\nexport interface StreamEndSnapshot {\n\troute?: string;\n\tmethod?: string;\n\tphase?: string;\n\t/** Total elapsed ms from middleware entry to end of body streaming. */\n\ttotalMs: number;\n\tdbCount: number;\n\tdbTotalMs: number;\n\tdbFirstOffset: number | null;\n\tdbLastOffset: number | null;\n\tcacheHits: number;\n\tcacheMisses: number;\n}\n\n/**\n * Wrap a response body so the FINAL request metrics are emitted when the\n * body finishes streaming. Returns the response unchanged when\n * instrumentation is disabled, the body is null, or no request metrics\n * are attached (e.g. outside the middleware's ALS context).\n */\nexport function wrapBodyForStreamMetrics(response: Response): Response {\n\tif (!isInstrumentationEnabled()) return response;\n\tif (!response.body) return response;\n\n\t// Capture the context's metrics object BEFORE wrapping: flush() runs\n\t// after the middleware's ALS frame may have exited, but the object\n\t// reference stays live and is mutated in-place by the Kysely log hook.\n\tconst ctx = getRequestContext();\n\tconst metrics = ctx?.metrics;\n\tif (!metrics) return response;\n\tconst recorder = ctx?.queryRecorder;\n\n\t// Claim the per-query flush: the recorder is mutated in-place by the\n\t// Kysely log hook for the whole request, including queries issued by\n\t// components while the body streams. Flushing here (rather than when\n\t// middleware returns) is what captures those streaming queries. The\n\t// flag tells the middleware's fallback flush to leave this recorder\n\t// to us.\n\tif (recorder) recorder.deferredFlush = true;\n\n\tconst transform = new TransformStream<Uint8Array, Uint8Array>({\n\t\tflush() {\n\t\t\tconst snapshot: StreamEndSnapshot = {\n\t\t\t\troute: recorder?.route,\n\t\t\t\tmethod: recorder?.method,\n\t\t\t\tphase: recorder?.phase,\n\t\t\t\ttotalMs: performance.now() - metrics.start,\n\t\t\t\tdbCount: metrics.dbCount,\n\t\t\t\tdbTotalMs: metrics.dbTotalMs,\n\t\t\t\tdbFirstOffset: metrics.dbFirstOffset,\n\t\t\t\tdbLastOffset: metrics.dbLastOffset,\n\t\t\t\tcacheHits: metrics.cacheHits,\n\t\t\t\tcacheMisses: metrics.cacheMisses,\n\t\t\t};\n\t\t\tconsole.log(`${STREAM_END_PREFIX} ${JSON.stringify(snapshot)}`);\n\t\t\t// Emit the full per-query log now that streaming is complete.\n\t\t\tif (recorder) flushRecorder(recorder);\n\t\t},\n\t});\n\n\tconst wrapped = new Response(response.body.pipeThrough(transform), response);\n\tconst astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);\n\tif (astroCookies !== undefined) {\n\t\tReflect.set(wrapped, ASTRO_COOKIES_SYMBOL, astroCookies);\n\t}\n\t// The identity transform preserves byte counts today, but a stale\n\t// Content-Length on a re-constructed streaming Response risks\n\t// truncation if anything upstream changes; the header is recomputed\n\t// (or chunked encoding used) by the server layer anyway.\n\twrapped.headers.delete(\"Content-Length\");\n\treturn wrapped;\n}\n","/**\n * Eager, transparent prefetch of site-global \"chrome\" data.\n *\n * On a public page render, the shared layout pulls the same site-global data on\n * every request -- menus, widget areas, taxonomy term lists, site settings --\n * but each is awaited inside a separately-rendered Astro component, so they\n * execute as serial DB round trips. This fires them all CONCURRENTLY at the\n * very start of the request, before `next()`:\n *\n * - On remote backends (D1, Durable Objects) the round trips overlap instead\n * of serializing, collapsing ~N sequential RTTs into ~1 wall-clock RTT. On\n * a coalescing backend they additionally batch into a single round trip.\n * - The results land in the per-request `requestCached` store under the exact\n * keys the layout helpers use, so when the components render they hit a warm\n * (in-flight or resolved) cache entry instead of issuing their own query.\n *\n * Nothing here changes what templates call -- it warms the real helpers, so the\n * cache keys and value shapes are guaranteed identical. The caller gates this to\n * the public-page path on a request-scoped (remote) backend; it is a no-op-ish\n * waste on synchronous local SQLite, so don't call it there.\n *\n * Fire-and-forget: never awaited by middleware, never throws (a prefetch failure\n * must not affect the request -- the helpers will simply run on demand).\n */\n\nimport { getDb } from \"../loader.js\";\nimport { getMenu } from \"../menus/index.js\";\nimport { requestCached, setRequestCacheEntry } from \"../request-cache.js\";\nimport { getSiteSettings } from \"../settings/index.js\";\nimport { getTaxonomyDefs, getTaxonomyTerms } from \"../taxonomies/index.js\";\nimport { getWidgetAreas } from \"../widgets/index.js\";\n\n/** Warm widget areas: one bulk load, primed under each per-area cache key. */\nasync function prefetchWidgetAreas(): Promise<void> {\n\tconst areas = await requestCached(\"widget-areas\", getWidgetAreas);\n\t// getWidgetArea(name) caches under `widget-area:${name}` and returns the same\n\t// WidgetArea shape getWidgetAreas yields, so priming here makes those calls hit.\n\tfor (const area of areas) {\n\t\tsetRequestCacheEntry(`widget-area:${area.name}`, area);\n\t}\n}\n\n/**\n * Warm every taxonomy's term list via the real helper (primes per-name keys).\n * Counts are left out: they cost an aggregate over the whole assignment pivot\n * per taxonomy, and only a consumer that renders one can say it's needed. A\n * consumer that does asks for it and reuses the term list warmed here.\n */\nasync function prefetchTaxonomyTerms(): Promise<void> {\n\tconst defs = await getTaxonomyDefs();\n\tawait Promise.allSettled(defs.map((def) => getTaxonomyTerms(def.name, { includeCounts: false })));\n}\n\n/** Warm every menu via the real helper (primes `menu:${name}:${locale}`). */\nasync function prefetchMenus(): Promise<void> {\n\tconst db = await getDb();\n\t// The layout calls getMenu(name) with hardcoded names; we can't know them, so\n\t// discover every menu name and warm them all (small, bounded chrome table).\n\tconst rows = await db.selectFrom(\"_emdash_menus\").select(\"name\").distinct().execute();\n\tconst names = [...new Set(rows.map((r) => r.name))];\n\tawait Promise.allSettled(names.map((name) => getMenu(name)));\n}\n\n/**\n * Concurrently warm the site-global layout data for the current request.\n * Safe to call only inside the request ALS frame that owns the (remote)\n * request-scoped db. Never throws.\n */\nexport async function prefetchLayoutData(): Promise<void> {\n\ttry {\n\t\tawait Promise.allSettled([\n\t\t\tgetSiteSettings(),\n\t\t\tprefetchMenus(),\n\t\t\tprefetchWidgetAreas(),\n\t\t\tprefetchTaxonomyTerms(),\n\t\t]);\n\t} catch (error) {\n\t\t// Defensive: Promise.allSettled shouldn't reject, but never let a prefetch\n\t\t// failure surface to the request.\n\t\tconsole.error(\"[emdash] layout prefetch failed (non-fatal):\", error);\n\t}\n}\n","import type { RouteMeta } from \"../plugins/routes.js\";\nimport type { HandlerResponse } from \"./types.js\";\n\nexport type PublicPluginApiRouteHandler = (\n\tpluginId: string,\n\tmethod: string,\n\tpath: string,\n\trequest: Request,\n) => Promise<HandlerResponse>;\n\ninterface PublicPluginApiRouteRuntime {\n\tgetPluginRouteMeta(pluginId: string, path: string): RouteMeta | null;\n\thandlePluginApiRoute(\n\t\tpluginId: string,\n\t\tmethod: string,\n\t\tpath: string,\n\t\trequest: Request,\n\t): Promise<HandlerResponse>;\n}\n\nfunction pluginRouteNotFound(): HandlerResponse {\n\treturn {\n\t\tsuccess: false,\n\t\terror: {\n\t\t\tcode: \"NOT_FOUND\",\n\t\t\tmessage: \"Plugin route not found\",\n\t\t},\n\t};\n}\n\nexport function createPublicPluginApiRouteHandler(\n\truntime: PublicPluginApiRouteRuntime,\n): PublicPluginApiRouteHandler {\n\treturn async (pluginId, method, path, request) => {\n\t\tconst meta = runtime.getPluginRouteMeta(pluginId, path);\n\t\tif (meta?.public !== true) {\n\t\t\treturn pluginRouteNotFound();\n\t\t}\n\n\t\treturn runtime.handlePluginApiRoute(pluginId, method, path, request);\n\t};\n}\n","/**\n * EmDash middleware\n *\n * Thin wrapper that initializes EmDashRuntime and attaches it to locals.\n * All heavy lifting happens in EmDashRuntime.\n */\n\nimport type { APIContext } from \"astro\";\nimport { defineMiddleware } from \"astro:middleware\";\nimport type { Kysely } from \"kysely\";\n// Import from virtual modules (populated by integration at build time)\n// @ts-ignore - virtual module\nimport { buildTime as virtualBuildTime } from \"virtual:emdash/build\";\n// @ts-ignore - virtual module\nimport virtualConfig from \"virtual:emdash/config\";\n// @ts-ignore - virtual module\nimport {\n\tcreateCoalescingDialect as virtualCreateCoalescingDialect,\n\tcreateDialect as virtualCreateDialect,\n\tcreateRequestScopedDb as virtualCreateRequestScopedDb,\n} from \"virtual:emdash/dialect\";\nimport type { RequestScopedDbOpts } from \"virtual:emdash/dialect\";\n// @ts-ignore - virtual module\nimport { mediaProviders as virtualMediaProviders } from \"virtual:emdash/media-providers\";\n// @ts-ignore - virtual module\nimport { plugins as virtualPlugins } from \"virtual:emdash/plugins\";\n// @ts-ignore - virtual module\nimport * as virtualSandboxRunnerModule from \"virtual:emdash/sandbox-runner\";\n// @ts-ignore - virtual module\nimport { sandboxedPlugins as virtualSandboxedPlugins } from \"virtual:emdash/sandboxed-plugins\";\n// @ts-ignore - virtual module\nimport { createScheduler as virtualCreateScheduler } from \"virtual:emdash/scheduler\";\n// @ts-ignore - virtual module\nimport { createStorage as virtualCreateStorage } from \"virtual:emdash/storage\";\n\nimport { after } from \"../after.js\";\nimport { apiError } from \"../api/error.js\";\nimport { EmDashConfigurationError } from \"../config/errors.js\";\nimport {\n\tcreateRecorder,\n\tflushRecorder,\n\tisInstrumentationEnabled,\n} from \"../database/instrumentation.js\";\nimport {\n\tPendingMigrationsError,\n\tresolveRuntimeMigrationMode,\n\ttype RuntimeMigrationMode,\n} from \"../database/migrations/policy.js\";\nimport { createDeferredTaskTracker } from \"../deferred-tasks.js\";\nimport {\n\tDB_INIT_DEADLINE_MS,\n\tEmDashRuntime,\n\ttype RuntimeDependencies,\n\ttype SandboxedPluginEntry,\n\ttype MediaProviderEntry,\n\ttype CreateSchedulerFn,\n} from \"../emdash-runtime.js\";\nimport { setI18nConfig } from \"../i18n/config.js\";\nimport type { Database, Storage } from \"../index.js\";\nimport { createPublicMediaUrlResolver } from \"../media/url.js\";\nimport { getLastContentWriteAt } from \"../object-cache/index.js\";\nimport type { PluginContentCacheInvalidator } from \"../plugins/routes.js\";\nimport type { SandboxRunnerFactory } from \"../plugins/sandbox/types.js\";\nimport type { ResolvedPlugin } from \"../plugins/types.js\";\nimport { invalidateUrlPatternCache } from \"../query.js\";\nimport {\n\tcreateRequestMetrics,\n\tgetRequestContext,\n\ttype RequestMetrics,\n\trunWithContext,\n} from \"../request-context.js\";\nimport type { PublishedRef } from \"../scheduled-publish.js\";\nimport { EmDashStorageError } from \"../storage/types.js\";\nimport { isMissingTableError } from \"../utils/db-errors.js\";\nimport { createInitLock, type InitLock, initWithLock } from \"../utils/init-lock.js\";\nimport type { EmDashConfig } from \"./integration/runtime.js\";\nimport {\n\tASTRO_COOKIES_SYMBOL,\n\tcoordinateScopedDbLifecycle,\n\tfinishScoped,\n\trequestEndedAuthenticated,\n} from \"./middleware/scoped-db.js\";\nimport { wrapBodyForStreamMetrics } from \"./middleware/stream-end-metrics.js\";\nimport { prefetchLayoutData } from \"./prefetch.js\";\nimport { createPublicPluginApiRouteHandler } from \"./public-plugin-api-routes.js\";\nimport { resolveSessionUser } from \"./session-user.js\";\nimport type { EmDashHandlers } from \"./types.js\";\n\n// Public type for withEmDashRuntime() consumers (queue/scheduled handlers).\nexport type { EmDashRuntime } from \"../emdash-runtime.js\";\n\n/**\n * Runtime init lock reclaim deadline. Must be strictly larger than the db\n * init deadline: this lock wraps EmDashRuntime.create() → getDatabase() →\n * the db init lock, and equal deadlines would let this outer lock reclaim\n * (spawning a second cron scheduler and sandbox runner) while the inner db\n * init is legitimately still working through a contended migration.\n */\nconst RUNTIME_INIT_DEADLINE_MS = DB_INIT_DEADLINE_MS + 15_000;\n\n/**\n * Throttle for the anonymous-path runtime-init failure log. While a site is\n * stuck (e.g. a failing migration in its backoff window, #1744) every\n * anonymous request lands in that catch; one line per interval per isolate\n * keeps the failure visible without flooding logs on a busy site. Plain\n * module state (not globalThis): a duplicated SSR chunk just means an extra\n * log line, which is harmless.\n */\nconst RUNTIME_INIT_ERROR_LOG_INTERVAL_MS = 30_000;\nlet lastRuntimeInitErrorLogAt = 0;\n\n/**\n * Whether we've verified the database has been set up.\n * On a fresh deployment the first request may hit a public page, bypassing\n * runtime init. Without this check, template helpers like getSiteSettings()\n * would query an empty database and crash. Once verified (or once the runtime\n * has initialized via an admin/API request), this stays true for the worker's\n * lifetime.\n *\n * Stored on globalThis behind a Symbol key so the flag is a true singleton\n * even when the bundler duplicates this module across SSR chunks (same\n * pattern as request-cache.ts). A plain module-scoped `let` becomes multiple\n * independent variables, which would make the setup probe re-run far more\n * often than intended — and every re-run is another chance for a transient\n * DB error to be misread as \"fresh install\" and bounce visitors to setup.\n */\nconst SETUP_VERIFIED_KEY = Symbol.for(\"emdash:setup-verified\");\nconst setupFlagStore = globalThis as Record<symbol, unknown>;\n\nfunction isSetupVerified(): boolean {\n\treturn setupFlagStore[SETUP_VERIFIED_KEY] === true;\n}\n\nfunction markSetupVerified(): void {\n\tsetupFlagStore[SETUP_VERIFIED_KEY] = true;\n}\n\n/**\n * The runtime singleton and its init lock live on globalThis behind a\n * Symbol — same reasoning as SETUP_VERIFIED_KEY above: the bundler can\n * duplicate this module across SSR chunks, and a duplicated instance/lock\n * would mean multiple runtimes (each with its own cron scheduler) per\n * isolate, initializing and reclaiming independently.\n */\nconst RUNTIME_HOLDER_KEY = Symbol.for(\"emdash:runtime-holder\");\ninterface RuntimeHolder {\n\tinstance: EmDashRuntime | null;\n\tlock: InitLock;\n}\n\nfunction getRuntimeHolder(): RuntimeHolder {\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis symbol slot, written only below\n\tlet holder = setupFlagStore[RUNTIME_HOLDER_KEY] as RuntimeHolder | undefined;\n\tif (!holder) {\n\t\tholder = { instance: null, lock: createInitLock() };\n\t\tsetupFlagStore[RUNTIME_HOLDER_KEY] = holder;\n\t}\n\treturn holder;\n}\n\n/** Whether i18n config has been initialized from the virtual module */\nlet i18nInitialized = false;\n\n/**\n * Get EmDash configuration from virtual module\n */\nfunction getConfig(): EmDashConfig | null {\n\tif (virtualConfig && typeof virtualConfig === \"object\") {\n\t\t// Initialize i18n config on first access (once per worker lifetime)\n\t\tif (!i18nInitialized) {\n\t\t\ti18nInitialized = true;\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- virtual module checked as object above\n\t\t\tconst config = virtualConfig as Record<string, unknown>;\n\t\t\tif (config.i18n && typeof config.i18n === \"object\") {\n\t\t\t\tsetI18nConfig(\n\t\t\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- runtime-checked above\n\t\t\t\t\tconfig.i18n as {\n\t\t\t\t\t\tdefaultLocale: string;\n\t\t\t\t\t\tlocales: string[];\n\t\t\t\t\t\tfallback?: Record<string, string>;\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tsetI18nConfig(null);\n\t\t\t}\n\t\t}\n\n\t\treturn virtualConfig;\n\t}\n\treturn null;\n}\n\n/**\n * Get plugins from virtual module\n */\nfunction getPlugins(): ResolvedPlugin[] {\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- virtual module import is untyped (@ts-ignore above)\n\treturn (virtualPlugins as ResolvedPlugin[]) || [];\n}\n\n/**\n * Build runtime dependencies from virtual modules\n */\nfunction buildDependencies(\n\tconfig: EmDashConfig,\n\tmigrationMode: RuntimeMigrationMode,\n): RuntimeDependencies {\n\t/* eslint-disable typescript-eslint/no-unsafe-type-assertion --\n\t The virtual:emdash/* imports above use @ts-ignore because tsgo/IDE\n\t resolution can't see virtual-modules.d.ts in every consumer setup,\n\t so they arrive as `any`. The casts here line each entry up with\n\t RuntimeDependencies's expected shape. The contract is enforced by\n\t the integration that populates these virtual modules. */\n\tconst sandboxModule = virtualSandboxRunnerModule as Record<string, unknown>;\n\treturn {\n\t\tconfig,\n\t\tmigrationMode,\n\t\tplugins: getPlugins(),\n\t\tcreateDialect: virtualCreateDialect as (config: Record<string, unknown>) => unknown,\n\t\t// Optional: only batching backends (D1, DO) export this; undefined otherwise.\n\t\tcreateCoalescingDialect: virtualCreateCoalescingDialect as\n\t\t\t| ((config: Record<string, unknown>) => unknown)\n\t\t\t| undefined,\n\t\tcreateStorage: virtualCreateStorage as ((config: Record<string, unknown>) => Storage) | null,\n\t\tcreateScheduler: virtualCreateScheduler as CreateSchedulerFn | null,\n\t\tsandboxEnabled: sandboxModule.sandboxEnabled as boolean,\n\t\tsandboxBypassed: (sandboxModule.sandboxBypassed as boolean) ?? false,\n\t\tsandboxedPluginEntries: (virtualSandboxedPlugins as SandboxedPluginEntry[]) || [],\n\t\tcreateSandboxRunner: sandboxModule.createSandboxRunner as SandboxRunnerFactory | null,\n\t\tmediaProviderEntries: (virtualMediaProviders as MediaProviderEntry[]) || [],\n\t};\n\t/* eslint-enable typescript-eslint/no-unsafe-type-assertion */\n}\n\n/**\n * Get or create the runtime instance.\n *\n * When `initTimings` is provided, any timing samples recorded during a\n * genuine cold init are appended. Subsequent warm calls (hitting the\n * cached instance) push nothing — callers should treat an empty array\n * as \"warm, nothing to report\".\n */\nasync function getRuntime(\n\tconfig: EmDashConfig,\n\tmigrationMode: RuntimeMigrationMode,\n\tinitTimings?: Array<{ name: string; dur: number; desc?: string }>,\n): Promise<EmDashRuntime> {\n\t// Waiters poll rather than awaiting the initializing request's promise —\n\t// workerd flags cross-request promise resolution (warnings + potential\n\t// hangs). If the initializing request is cancelled mid-create (client\n\t// disconnect tears down its continuation, skipping any `finally`), the\n\t// anchored init keeps running under waitUntil and populates the cache;\n\t// failing that, the stale lock is reclaimed after a deadline instead of\n\t// hanging every subsequent request in the isolate until eviction.\n\tconst holder = getRuntimeHolder();\n\treturn initWithLock(\n\t\tholder.lock,\n\t\t() => holder.instance,\n\t\tasync (isCurrentClaim) => {\n\t\t\tconst deps = buildDependencies(config, migrationMode);\n\t\t\tconst runtime = await EmDashRuntime.create(deps, initTimings);\n\t\t\tif (isCurrentClaim()) {\n\t\t\t\tholder.instance = runtime;\n\t\t\t} else {\n\t\t\t\t// This init was reclaimed mid-flight (it ran past the deadline\n\t\t\t\t// and a waiter started its own). Don't overwrite the\n\t\t\t\t// reclaimer's published runtime, and stop this one's cron\n\t\t\t\t// scheduler so it doesn't keep firing unreferenced. The\n\t\t\t\t// runtime is still returned — it's fully functional for the\n\t\t\t\t// request that built it.\n\t\t\t\truntime.stopCron().catch((error: unknown) => {\n\t\t\t\t\tconsole.error(\"[emdash] failed to stop superseded runtime's cron:\", error);\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn runtime;\n\t\t},\n\t\t{\n\t\t\tdeadlineMs: RUNTIME_INIT_DEADLINE_MS,\n\t\t\tanchor: (promise) => after(() => promise),\n\t\t},\n\t);\n}\n\n/**\n * Run scheduled maintenance (cron tasks, scheduled publishing, system cleanup)\n * outside any request. Resolves the runtime from the build-time virtual config\n * and the cached singleton — the same instance request handlers use.\n *\n * Wired into a platform heartbeat that is not a request: the Cloudflare Worker's\n * `scheduled()` handler (Cron Trigger) calls this. On Node the runtime's own\n * timer-based scheduler already drives the same work, so this isn't needed there.\n *\n * Returns the content promoted by the publishing sweep so the caller can purge\n * edge-cache tags for it. `onPublished` (optional) is awaited after each\n * collection's batch so the caller can invalidate edge-cache tags incrementally\n * rather than only after the whole sweep.\n */\nexport async function runScheduledTasks(\n\toptions: {\n\t\tonPublished?: (refs: PublishedRef[]) => Promise<void>;\n\t\tinvalidateContentCache?: PluginContentCacheInvalidator;\n\t} = {},\n): Promise<{ published: PublishedRef[] }> {\n\tconst config = getConfig();\n\tif (!config) return { published: [] };\n\treturn runOutsideRequest(config, (runtime) => runtime.runScheduledTasks(options));\n}\n\n/**\n * Run a callback against the EmDash runtime outside any HTTP request — from a\n * Cloudflare Queue consumer, a `scheduled()` handler, or any other\n * platform-event handler that has no request and therefore no `locals.emdash`.\n *\n * Resolves the same cached runtime singleton request handlers use, so hooks,\n * plugin state, and storage all behave exactly as they do during a request.\n * A typical queue consumer finishes a job by calling back into a plugin route:\n *\n * ```ts\n * import { withEmDashRuntime } from \"emdash/middleware\";\n *\n * async function queue(batch: MessageBatch) {\n * \tawait withEmDashRuntime(async (runtime) => {\n * \t\tfor (const message of batch.messages) {\n * \t\t\tawait runtime.handlePluginApiRoute(\n * \t\t\t\t\"my-plugin\",\n * \t\t\t\t\"POST\",\n * \t\t\t\t\"/finishJob\",\n * \t\t\t\tnew Request(\"https://internal/\", {\n * \t\t\t\t\tmethod: \"POST\",\n * \t\t\t\t\tbody: JSON.stringify(message.body),\n * \t\t\t\t}),\n * \t\t\t);\n * \t\t}\n * \t});\n * }\n * ```\n *\n * Server-only and fully trusted: the callback gets the raw runtime with no\n * auth or CSRF checks, same trust level as plugin cron. Never expose it to\n * user input without validating that input yourself.\n *\n * Throws when EmDash is not configured (no `emdash()` Astro integration).\n */\nexport async function withEmDashRuntime<T>(\n\trun: (runtime: EmDashRuntime) => T | Promise<T>,\n): Promise<T> {\n\tconst config = getConfig();\n\tif (!config) {\n\t\tthrow new Error(\n\t\t\t\"EmDash is not configured — withEmDashRuntime() requires the emdash() Astro integration.\",\n\t\t);\n\t}\n\treturn runOutsideRequest(config, async (runtime) => run(runtime));\n}\n\n/**\n * Shared plumbing for request-free entry points (`runScheduledTasks`,\n * `withEmDashRuntime`): resolve the runtime singleton, then run the callback\n * under an event-scoped db when the adapter needs one.\n *\n * Connection-backed adapters (e.g. Postgres over Hyperdrive) cannot reuse\n * the per-isolate singleton from a platform event: its socket belongs to the\n * request that opened it, and workerd rejects cross-event I/O. Open an\n * event-scoped connection and run the callback under it in ALS — the\n * runtime's db getter, the cron executor, and plugin contexts all resolve\n * the connection from ALS — then close it when required. Stateless adapters\n * that need primary routing can return a close-less scope; adapters with no\n * event scoping return null and keep using the singleton.\n */\nasync function runOutsideRequest<T>(\n\tconfig: EmDashConfig,\n\tfn: (runtime: EmDashRuntime) => Promise<T>,\n): Promise<T> {\n\tconst migrationMode = resolveConfiguredMigrationMode(config);\n\tif (getRequestContext()) {\n\t\tconst runtime = await getRuntime(config, migrationMode);\n\t\treturn runOutsideRequestWithRuntime(config, runtime, fn);\n\t}\n\n\tconst deferredTasks = createDeferredTaskTracker(() => {});\n\tconst context = {\n\t\teditMode: false,\n\t\tmetrics: createRequestMetrics(performance.now()),\n\t\tdeferredTasks,\n\t};\n\treturn runWithContext(context, async () => {\n\t\tconst runtime = await (async () => {\n\t\t\ttry {\n\t\t\t\treturn await getRuntime(config, migrationMode);\n\t\t\t} finally {\n\t\t\t\tdeferredTasks.settle();\n\t\t\t\tawait deferredTasks.settled;\n\t\t\t}\n\t\t})();\n\t\treturn runOutsideRequestWithRuntime(config, runtime, fn);\n\t});\n}\n\nasync function runOutsideRequestWithRuntime<T>(\n\tconfig: EmDashConfig,\n\truntime: EmDashRuntime,\n\tfn: (runtime: EmDashRuntime) => Promise<T>,\n): Promise<T> {\n\tconst scoped = createRequestScopedDb({\n\t\tconfig: config.database?.config,\n\t\tisAuthenticated: false,\n\t\t// Event handlers publish, clean up, or run jobs — a write workload —\n\t\t// so a connection-backed adapter routes them to the primary.\n\t\tisWrite: true,\n\t\tcanUseCachedBinding: false,\n\t\tcookies: NOOP_COOKIE_JAR,\n\t\turl: CRON_EVENT_URL,\n\t});\n\tif (!scoped) {\n\t\t// This adapter needs no event-specific routing or connection.\n\t\treturn fn(runtime);\n\t}\n\tconst { closed, deferredTasks, lifecycle } = coordinateScopedDbLifecycle(scoped);\n\n\tconst parent = getRequestContext();\n\tconst ctx = parent\n\t\t? { ...parent, db: scoped.db, deferredTasks }\n\t\t: {\n\t\t\t\teditMode: false,\n\t\t\t\tdb: scoped.db,\n\t\t\t\tmetrics: createRequestMetrics(performance.now()),\n\t\t\t\tdeferredTasks,\n\t\t\t};\n\ttry {\n\t\treturn await runWithContext(ctx, () => fn(runtime));\n\t} finally {\n\t\t// Guard both so a throw in teardown can't mask the callback result or\n\t\t// skip lifecycle settlement and leak the connection.\n\t\ttry {\n\t\t\tlifecycle.commit();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] event-scoped db commit failed:\", error);\n\t\t}\n\t\ttry {\n\t\t\tlifecycle.close?.();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[emdash] event-scoped db close failed:\", error);\n\t\t}\n\t\tawait closed;\n\t}\n}\n\n/**\n * A cookie jar that reads nothing and writes nothing, for request-scoped db\n * adapters invoked outside an HTTP request (the Cron Trigger sweep). Connection\n * adapters like Hyperdrive ignore cookies entirely; the D1 session adapter\n * reads/writes a bookmark cookie, but cron never reaches that path (it has no\n * `close()`), so the no-ops are never observed.\n */\nconst NOOP_COOKIE_JAR = {\n\tget: () => undefined,\n\tset: () => {},\n};\n\n/**\n * Synthetic URL for the cron sweep's request-scoped db opts. Only the D1\n * session adapter inspects `url` (for cookie `secure`), and cron doesn't take\n * that path, so the value is never used — it exists to satisfy the contract.\n */\nconst CRON_EVENT_URL = new URL(\"https://cron.emdash.internal/\");\n\nfunction resolveConfiguredMigrationMode(config: EmDashConfig): RuntimeMigrationMode {\n\tconst processOverride =\n\t\ttypeof process !== \"undefined\" && process.env ? process.env.EMDASH_MIGRATIONS_MODE : undefined;\n\tconst importMetaOverride = import.meta.env.EMDASH_MIGRATIONS_MODE;\n\treturn resolveRuntimeMigrationMode(config.migrations, {\n\t\tdev: import.meta.env.DEV,\n\t\toverride: processOverride ?? importMetaOverride,\n\t});\n}\n\nfunction pendingMigrationsResponse(error: PendingMigrationsError): Response {\n\tconsole.error(\"[emdash] database migrations are pending:\", error.pending.join(\", \"));\n\treturn migrationRequiredResponse();\n}\n\nfunction migrationRequiredResponse(): Response {\n\treturn new Response(\n\t\t\"Database migrations are required. Apply the deployment migration manifest and retry.\",\n\t\t{\n\t\t\tstatus: 503,\n\t\t\theaders: { \"Retry-After\": \"60\" },\n\t\t},\n\t);\n}\n\nfunction unmigratedDatabaseResponse(): Response {\n\treturn new Response(\n\t\t'<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"robots\" content=\"noindex\"><title>Site unavailable</title></head><body><p>This site\\'s database has not been migrated yet. <a href=\"/_emdash/admin/setup\">Set up EmDash</a></p></body></html>',\n\t\t{\n\t\t\tstatus: 503,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"text/html; charset=utf-8\",\n\t\t\t\t\"Cache-Control\": \"no-store\",\n\t\t\t\t\"Retry-After\": \"60\",\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction runtimeConfigurationErrorResponse(error: unknown, pathname: string): Response | null {\n\tif (!pathname.startsWith(\"/_emdash/api/\")) return null;\n\tif (error instanceof EmDashConfigurationError) {\n\t\treturn apiError(error.code, error.message, 500);\n\t}\n\tif (error instanceof EmDashStorageError && error.code === \"BINDING_NOT_FOUND\") {\n\t\treturn apiError(error.code, error.message, 500);\n\t}\n\treturn null;\n}\n\n/**\n * Baseline security headers applied to all responses.\n * Admin routes get additional headers (strict CSP) from auth middleware.\n */\nfunction finalizeResponse(\n\tresponse: Response,\n\tserverTimings?: Array<{ name: string; dur: number; desc?: string }>,\n): Response {\n\tconst res = new Response(response.body, response);\n\tconst astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);\n\tif (astroCookies !== undefined) {\n\t\tReflect.set(res, ASTRO_COOKIES_SYMBOL, astroCookies);\n\t}\n\t// Set-if-absent so a host app that sets stricter values on its own routes\n\t// wins. The middleware registers `order: 'pre'` (#1282), so on the response\n\t// path it runs *after* host middleware; unconditional `set()` would clobber\n\t// the host's headers on every public route (#1393). Mirrors the CSP guard.\n\tif (!res.headers.has(\"X-Content-Type-Options\")) {\n\t\tres.headers.set(\"X-Content-Type-Options\", \"nosniff\");\n\t}\n\tif (!res.headers.has(\"Referrer-Policy\")) {\n\t\tres.headers.set(\"Referrer-Policy\", \"strict-origin-when-cross-origin\");\n\t}\n\tif (!res.headers.has(\"Permissions-Policy\")) {\n\t\tres.headers.set(\"Permissions-Policy\", \"camera=(), microphone=(), geolocation=(), payment=()\");\n\t}\n\tif (!res.headers.has(\"Content-Security-Policy\")) {\n\t\tres.headers.set(\"X-Frame-Options\", \"SAMEORIGIN\");\n\t}\n\tif (serverTimings && serverTimings.length > 0) {\n\t\tres.headers.set(\n\t\t\t\"Server-Timing\",\n\t\t\tserverTimings\n\t\t\t\t.map((t) => {\n\t\t\t\t\tconst dur = Math.round(t.dur);\n\t\t\t\t\treturn t.desc ? `${t.name};dur=${dur};desc=\"${t.desc}\"` : `${t.name};dur=${dur}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \"),\n\t\t);\n\t}\n\treturn res;\n}\n\n/**\n * Append always-on counters (db.*, cache.*) to the Server-Timing list.\n *\n * dur values for `count`, `hit`, `miss` are integer counts — Server-Timing\n * spec only models milliseconds, but browsers show whatever number is given,\n * which is the convention most projects use for non-time samples.\n */\nfunction pushMetricsTimings(\n\ttimings: Array<{ name: string; dur: number; desc?: string }>,\n\tmetrics: RequestMetrics,\n): void {\n\tif (metrics.dbCount > 0) {\n\t\ttimings.push({ name: \"db.total\", dur: metrics.dbTotalMs, desc: \"DB total\" });\n\t\ttimings.push({ name: \"db.count\", dur: metrics.dbCount, desc: \"Query count\" });\n\t\tif (metrics.dbFirstOffset !== null) {\n\t\t\ttimings.push({ name: \"db.first\", dur: metrics.dbFirstOffset, desc: \"First query at\" });\n\t\t}\n\t\tif (metrics.dbLastOffset !== null) {\n\t\t\ttimings.push({ name: \"db.last\", dur: metrics.dbLastOffset, desc: \"Last query at\" });\n\t\t}\n\t}\n\tif (metrics.rpcCount > 0) {\n\t\ttimings.push({ name: \"rpc.count\", dur: metrics.rpcCount, desc: \"DB round trips\" });\n\t}\n\tif (metrics.cacheHits + metrics.cacheMisses > 0) {\n\t\ttimings.push({ name: \"cache.hit\", dur: metrics.cacheHits, desc: \"Cache hits\" });\n\t\ttimings.push({ name: \"cache.miss\", dur: metrics.cacheMisses, desc: \"Cache misses\" });\n\t}\n}\n\n/** Public routes that require the runtime (sitemap, robots.txt, etc.) */\nconst PUBLIC_RUNTIME_ROUTES = new Set([\"/sitemap.xml\", \"/robots.txt\"]);\nconst SITEMAP_COLLECTION_RE = /^\\/sitemap-[a-z][a-z0-9_]*\\.xml$/;\n\n/**\n * Ask the configured database adapter for a per-request scoped Kysely. The\n * adapter encapsulates any per-request semantics (D1 sessions, read-replica\n * routing, bookmark cookies, etc.); core just forwards the cookie jar and\n * request flags and wraps next() in ALS if a scope was returned.\n */\nfunction createRequestScopedDb(\n\topts: RequestScopedDbOpts,\n): { db: Kysely<Database>; commit: () => void; close?: () => void } | null {\n\tif (typeof virtualCreateRequestScopedDb !== \"function\") return null;\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- adapter returns Kysely<unknown>; cast to Database since core owns that type\n\tconst fn = virtualCreateRequestScopedDb as (\n\t\to: RequestScopedDbOpts,\n\t) => { db: Kysely<Database>; commit: () => void; close?: () => void } | null;\n\treturn fn(opts);\n}\n\nconst buildDate = virtualBuildTime ? new Date(virtualBuildTime) : null;\n\n/**\n * Fold the build timestamp into the route cache validator.\n *\n * `CacheHint.lastModified` describes the content, but the response also depends\n * on the build: `/_astro/*` names are content-hashed, and a deployment only\n * serves its own. Without the build dimension a code-only deploy answers a\n * returning visitor's conditional request with 304, leaving them on HTML whose\n * assets 404.\n *\n * Prerendered pages are served by the host's static layer, which manages its\n * own validators — only on-demand responses need the build dimension.\n *\n * Only forward moves are covered. `Last-Modified` expresses newer, not\n * different, so after a rollback the earlier build still answers a conditional\n * request with 304 and the browser stays on the newer build's HTML.\n *\n * Must run before next(): Astro keeps the later of two dates, so a route's own\n * hint still wins when content is newer, and a route that opts out with\n * `Astro.cache.set(false)` stays opted out — calling set() afterwards would\n * clear that opt-out.\n */\nfunction applyBuildValidator(context: APIContext): void {\n\tif (context.isPrerendered || !buildDate || !context.cache?.enabled) return;\n\tcontext.cache.set({ lastModified: buildDate });\n}\n\nexport const onRequest = defineMiddleware(async (context, next) => {\n\tconst { request, locals, cookies } = context;\n\tconst url = context.url;\n\n\t// Fast path: routes outside /_emdash/ that plugins inject (e.g.,\n\t// /.well-known/atproto-client-metadata.json) skip the entire runtime\n\t// init + middleware chain. External servers fetch these with tight\n\t// timeouts (~1-2s) so they must respond quickly even on cold starts.\n\tif (!url.pathname.startsWith(\"/_emdash\") && virtualConfig?.authProviders) {\n\t\tconst isPluginFastRoute = virtualConfig.authProviders.some(\n\t\t\t(p: { routes?: { pattern?: string }[] }) =>\n\t\t\t\tp.routes?.some((r: { pattern?: string }) => r.pattern && url.pathname === r.pattern),\n\t\t);\n\t\tif (isPluginFastRoute) {\n\t\t\treturn finalizeResponse(await next());\n\t\t}\n\t}\n\n\tapplyBuildValidator(context);\n\n\tconst queryRecorder = isInstrumentationEnabled()\n\t\t? createRecorder(url.pathname, request.method, request.headers.get(\"x-perf-phase\") ?? \"default\")\n\t\t: undefined;\n\n\tconst metrics = createRequestMetrics(performance.now());\n\n\tconst run = async (): Promise<Response> => {\n\t\tconst config = getConfig();\n\t\tconst migrationMode = config ? resolveConfiguredMigrationMode(config) : \"auto\";\n\t\t// Process /_emdash routes and public routes with an active session\n\t\t// (logged-in editors need the runtime for toolbar/visual editing on public pages)\n\t\tconst isEmDashRoute = url.pathname.startsWith(\"/_emdash\");\n\t\tconst isPublicRuntimeRoute =\n\t\t\tPUBLIC_RUNTIME_ROUTES.has(url.pathname) || SITEMAP_COLLECTION_RE.test(url.pathname);\n\n\t\t// Check for edit mode cookie - editors viewing public pages need the runtime\n\t\t// so auth middleware can verify their session for visual editing\n\t\tconst hasEditCookie = cookies.get(\"emdash-edit-mode\")?.value === \"true\";\n\t\tconst hasPreviewToken = url.searchParams.has(\"_preview\");\n\n\t\t// Playground mode: the playground middleware stashes the per-session DO database\n\t\t// on locals.__playgroundDb. When present, use runWithContext() to make it\n\t\t// available to getDb() and the runtime's db getter via the correct ALS instance.\n\t\tconst playgroundDb = locals.__playgroundDb;\n\n\t\t// Read the Astro session user once up-front. Both the anonymous fast path\n\t\t// and the full doInit path need this, and the session store is network-backed\n\t\t// (KV / Durable Object) so we want to avoid re-fetching on the hot path.\n\t\t// Skipped entirely for:\n\t\t// - prerendered requests (no session at build time)\n\t\t// - requests without an `astro-session` cookie (no session to look up)\n\t\t// The cookie check matters on Cloudflare Workers, where Astro's session\n\t\t// backend is KV: calling session.get() on every anonymous public request\n\t\t// turns normal traffic into a flood of KV read misses. See #733.\n\t\tconst hasSessionCookie = cookies.get(\"astro-session\") !== undefined;\n\t\tconst sessionUser =\n\t\t\tcontext.isPrerendered || !hasSessionCookie ? null : await resolveSessionUser(context.session);\n\n\t\t// Credentialed API requests (API tokens `ec_pat_*`, OAuth tokens\n\t\t// `ec_oat_*`, and other Bearer credentials) carry no `astro-session`\n\t\t// cookie, so `sessionUser` is null for them -- yet they still expect\n\t\t// read-your-writes. The auth middleware that resolves the token runs\n\t\t// *after* this one, so `locals.user` isn't populated here; detect the\n\t\t// credential directly on the request. Request-scoped adapters use this to\n\t\t// keep such requests on the primary/uncached connection (not a lagging\n\t\t// read replica or the Hyperdrive query cache).\n\t\tconst hasBearerAuth = (request.headers.get(\"authorization\") ?? \"\")\n\t\t\t.toLowerCase()\n\t\t\t.startsWith(\"bearer \");\n\t\tconst isWrite = request.method !== \"GET\" && request.method !== \"HEAD\";\n\t\tconst isAuthenticated = !!sessionUser || hasBearerAuth;\n\t\tconst endedAuthenticated = () => requestEndedAuthenticated(isAuthenticated, cookies);\n\t\tconst canUseCachedBinding =\n\t\t\t!isAuthenticated &&\n\t\t\t!isWrite &&\n\t\t\t!playgroundDb &&\n\t\t\t!isEmDashRoute &&\n\t\t\t!hasEditCookie &&\n\t\t\t!hasPreviewToken;\n\n\t\tif (!isEmDashRoute && !isPublicRuntimeRoute && !hasEditCookie && !hasPreviewToken) {\n\t\t\tif (!sessionUser && !playgroundDb) {\n\t\t\t\tconst timings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\t\tconst mwStart = performance.now();\n\n\t\t\t\t// On a fresh deployment the database may be completely empty.\n\t\t\t\t// Public pages call getSiteSettings() / getMenu() via getDb(), which\n\t\t\t\t// bypasses runtime init and would crash with \"no such table: options\".\n\t\t\t\t// Do a one-time lightweight probe using the same getDb() instance the\n\t\t\t\t// page will use: if the migrations table doesn't exist, no migrations\n\t\t\t\t// have ever run, and the page may only render once runtime init below\n\t\t\t\t// has migrated the database.\n\t\t\t\t// Skip the probe when prerendering: the build database is legitimately\n\t\t\t\t// empty in CI, and a response derived from it would be baked into the\n\t\t\t\t// static page (session reads are already skipped for prerender above\n\t\t\t\t// for the same reason).\n\t\t\t\tlet unmigrated = false;\n\t\t\t\tlet probeFailed = false;\n\t\t\t\tif (migrationMode === \"auto\" && !isSetupVerified() && !context.isPrerendered) {\n\t\t\t\t\tconst t0 = performance.now();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { getDb } = await import(\"../loader.js\");\n\t\t\t\t\t\tconst db = await getDb();\n\t\t\t\t\t\tawait db.selectFrom(\"_emdash_migrations\").selectAll().limit(1).execute();\n\t\t\t\t\t\tmarkSetupVerified();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (isMissingTableError(error)) {\n\t\t\t\t\t\t\tunmigrated = true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Any other failure (transient D1/replica error, timeout, cold-start\n\t\t\t\t\t\t\t// race, locked SQLite) must NOT be read as \"fresh install\". Leave the\n\t\t\t\t\t\t\t// flag unset so a later request can re-verify, and fall through to\n\t\t\t\t\t\t\t// render the page normally. Unless a runtime is already running,\n\t\t\t\t\t\t\t// skip runtime init for this request: it would connect to the same\n\t\t\t\t\t\t\t// database again and make the visitor wait through a second timeout.\n\t\t\t\t\t\t\tprobeFailed = !getRuntimeHolder().instance;\n\t\t\t\t\t\t\tconsole.error(\"Setup probe failed (non-fatal):\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttimings.push({ name: \"setup\", dur: performance.now() - t0, desc: \"Setup probe\" });\n\t\t\t\t}\n\n\t\t\t\t// Initialize the runtime for page:metadata and page:fragments hooks.\n\t\t\t\t// The runtime is a cached singleton — after the first request,\n\t\t\t\t// getRuntime() is just a null-check. This enables SEO plugins to\n\t\t\t\t// contribute meta tags for all visitors, not just logged-in editors,\n\t\t\t\t// except on a request whose setup probe just failed.\n\t\t\t\tif (config && !probeFailed) {\n\t\t\t\t\t// Sub-phase timings are populated only on the cold init. Warm\n\t\t\t\t\t// requests hit the cached runtime and leave this empty.\n\t\t\t\t\tconst initSubTimings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\t\t\tconst t0 = performance.now();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst runtime = await getRuntime(config, migrationMode, initSubTimings);\n\t\t\t\t\t\tmarkSetupVerified();\n\t\t\t\t\t\tconst handlePublicPluginApiRoute = createPublicPluginApiRouteHandler(runtime);\n\t\t\t\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- partial object; getPageRuntime() only checks for the page-contribution methods\n\t\t\t\t\t\tlocals.emdash = {\n\t\t\t\t\t\t\thandlePublicPluginApiRoute,\n\t\t\t\t\t\t\tcollectPageMetadata: runtime.collectPageMetadata.bind(runtime),\n\t\t\t\t\t\t\tcollectPageFragments: runtime.collectPageFragments.bind(runtime),\n\t\t\t\t\t\t\tgetPublicMediaUrl: createPublicMediaUrlResolver(runtime.storage),\n\t\t\t\t\t\t\t// Exposed so the wrapped image endpoint (`/_image`) can read media\n\t\t\t\t\t\t\t// bytes from storage on the anonymous fast path -- public `<img>`\n\t\t\t\t\t\t\t// requests carry no session.\n\t\t\t\t\t\t\tstorage: runtime.storage,\n\t\t\t\t\t\t} as EmDashHandlers;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (error instanceof PendingMigrationsError) {\n\t\t\t\t\t\t\treturn pendingMigrationsResponse(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (migrationMode === \"manual\" && isMissingTableError(error)) {\n\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\"[emdash] database schema is unavailable in manual migration mode:\",\n\t\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn migrationRequiredResponse();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Non-fatal — EmDashHead falls back to base SEO contributions —\n\t\t\t\t\t\t// but log it (throttled): a persistently failing init (e.g. a\n\t\t\t\t\t\t// failing migration, #1744) is otherwise invisible on the\n\t\t\t\t\t\t// anonymous path, silently degrading every public page.\n\t\t\t\t\t\tif (Date.now() - lastRuntimeInitErrorLogAt >= RUNTIME_INIT_ERROR_LOG_INTERVAL_MS) {\n\t\t\t\t\t\t\tlastRuntimeInitErrorLogAt = Date.now();\n\t\t\t\t\t\t\tconsole.error(\"[emdash] runtime init failed (page renders without CMS data):\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttimings.push({ name: \"rt\", dur: performance.now() - t0, desc: \"Runtime init\" });\n\t\t\t\t\t// Append cold-only sub-phase timings so the breakdown is visible\n\t\t\t\t\t// in Server-Timing (rt.db, rt.fts, rt.plugins, rt.site,\n\t\t\t\t\t// rt.sandbox, rt.market, rt.hooks, rt.cron).\n\t\t\t\t\tfor (const sub of initSubTimings) timings.push(sub);\n\t\t\t\t}\n\t\t\t\tif (unmigrated && !locals.emdash) {\n\t\t\t\t\treturn finalizeResponse(unmigratedDatabaseResponse(), timings);\n\t\t\t\t}\n\n\t\t\t\t// Even on the anonymous fast path we ask the adapter for a per-request\n\t\t\t\t// scoped db. For D1 with read replication this routes anonymous reads\n\t\t\t\t// to the nearest replica; for other adapters it's a no-op.\n\t\t\t\tconst lastContentWriteAt =\n\t\t\t\t\tcanUseCachedBinding && config?.database?.needsLastContentWriteAt\n\t\t\t\t\t\t? await getLastContentWriteAt()\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst anonScoped = createRequestScopedDb({\n\t\t\t\t\tconfig: config?.database?.config,\n\t\t\t\t\tisAuthenticated,\n\t\t\t\t\tendedAuthenticated,\n\t\t\t\t\tisWrite,\n\t\t\t\t\tcanUseCachedBinding,\n\t\t\t\t\tcookies,\n\t\t\t\t\turl,\n\t\t\t\t\tlastContentWriteAt,\n\t\t\t\t});\n\t\t\t\tconst runAnon = async () => {\n\t\t\t\t\tconst t0 = performance.now();\n\t\t\t\t\tconst response = await next();\n\t\t\t\t\ttimings.push({ name: \"render\", dur: performance.now() - t0, desc: \"Page render\" });\n\t\t\t\t\ttimings.push({ name: \"mw\", dur: performance.now() - mwStart, desc: \"Total middleware\" });\n\t\t\t\t\tpushMetricsTimings(timings, metrics);\n\t\t\t\t\t// Server-Timing only sees pre-stream queries; the stream-end\n\t\t\t\t\t// wrapper (instrumentation-gated, no-op otherwise) emits the\n\t\t\t\t\t// final counters once the body finishes streaming.\n\t\t\t\t\treturn wrapBodyForStreamMetrics(finalizeResponse(response, timings));\n\t\t\t\t};\n\t\t\t\tif (anonScoped) {\n\t\t\t\t\tconst { deferredTasks, lifecycle } = coordinateScopedDbLifecycle(anonScoped);\n\t\t\t\t\tconst parent = getRequestContext();\n\t\t\t\t\tconst ctx = parent\n\t\t\t\t\t\t? { ...parent, db: anonScoped.db, deferredTasks }\n\t\t\t\t\t\t: { editMode: false, db: anonScoped.db, metrics, deferredTasks };\n\t\t\t\t\t// Eagerly warm site-global layout data (menus, widget areas,\n\t\t\t\t\t// taxonomy terms, settings) concurrently so the layout's\n\t\t\t\t\t// per-component reads overlap into ~one wall-clock round trip and\n\t\t\t\t\t// hit a warm cache instead of serializing. Three guards:\n\t\t\t\t\t// - request-scoped (remote) backend only -- this branch implies it;\n\t\t\t\t\t// pointless on synchronous local SQLite.\n\t\t\t\t\t// - HTML navigations only -- feeds/sitemaps/JSON don't render the\n\t\t\t\t\t// layout, so prefetching their chrome is pure waste.\n\t\t\t\t\t// - the work starts immediately, then after() keeps both it and the\n\t\t\t\t\t// request-scoped connection alive until the response also finishes.\n\t\t\t\t\t// Gate on the CLIENT'S PREFERRED type (leading media range), not a\n\t\t\t\t\t// substring -- browser navigations lead with `text/html`, while feed\n\t\t\t\t\t// readers lead with `application/rss+xml` etc. and only list\n\t\t\t\t\t// `text/html;q=0.8` later, so a substring match would leak onto feeds.\n\t\t\t\t\tconst acceptsHtml = (request.headers.get(\"accept\") ?? \"\")\n\t\t\t\t\t\t.split(\",\", 1)[0]!\n\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t.startsWith(\"text/html\");\n\t\t\t\t\treturn runWithContext(ctx, async () => {\n\t\t\t\t\t\tif (acceptsHtml) after(() => prefetchLayoutData());\n\t\t\t\t\t\t// commit() persists per-request state (e.g. the D1 bookmark cookie)\n\t\t\t\t\t\t// before the response is returned, even if render throws; close()\n\t\t\t\t\t\t// waits for stream-end and request-owned deferred work. See finishScoped.\n\t\t\t\t\t\treturn finishScoped(lifecycle, runAnon);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn runAnon();\n\t\t\t}\n\t\t}\n\n\t\tif (!config) {\n\t\t\tconsole.error(\"EmDash: No configuration found\");\n\t\t\treturn finalizeResponse(await next());\n\t\t}\n\n\t\t// In playground mode, wrap the entire runtime init + request handling in\n\t\t// runWithContext so that getDatabase() and all init queries use the real\n\t\t// DO database via the same AsyncLocalStorage instance as the loader.\n\t\tconst doInit = async () => {\n\t\t\tconst timings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\tconst mwStart = performance.now();\n\n\t\t\ttry {\n\t\t\t\t// Get or create runtime. Sub-phase timings (rt.db, rt.fts, rt.plugins,\n\t\t\t\t// rt.site, rt.sandbox, rt.market, rt.hooks, rt.cron) are populated\n\t\t\t\t// only on the cold init — subsequent warm calls find the cached\n\t\t\t\t// instance and `initSubTimings` stays empty.\n\t\t\t\tconst initSubTimings: Array<{ name: string; dur: number; desc?: string }> = [];\n\t\t\t\tlet t0 = performance.now();\n\t\t\t\tconst runtime = await getRuntime(config, migrationMode, initSubTimings);\n\t\t\t\ttimings.push({ name: \"rt\", dur: performance.now() - t0, desc: \"Runtime init\" });\n\t\t\t\t// Forward any sub-phase samples so cold-start breakdown is visible\n\t\t\t\t// in Server-Timing. Each phase appears prefixed \"rt.\" to distinguish\n\t\t\t\t// from the aggregate \"rt\" timing above.\n\t\t\t\tfor (const sub of initSubTimings) timings.push(sub);\n\n\t\t\t\t// Runtime initialization has satisfied the effective migration policy.\n\t\t\t\tmarkSetupVerified();\n\n\t\t\t\t// The manifest is no longer pre-loaded here. It's admin-only\n\t\t\t\t// content that public/anonymous requests never read, and\n\t\t\t\t// loading it on every request put logged-out hot paths on\n\t\t\t\t// the same staleness budget as admin operations. Admin\n\t\t\t\t// routes call `emdash.getManifest()` directly.\n\n\t\t\t\t// Attach to locals for route handlers\n\t\t\t\tlocals.emdash = {\n\t\t\t\t\t// Content handlers\n\t\t\t\t\thandleContentList: runtime.handleContentList.bind(runtime),\n\t\t\t\t\thandleContentGet: runtime.handleContentGet.bind(runtime),\n\t\t\t\t\thandleContentAuthors: runtime.handleContentAuthors.bind(runtime),\n\t\t\t\t\thandleContentCreate: runtime.handleContentCreate.bind(runtime),\n\t\t\t\t\thandleContentUpdate: runtime.handleContentUpdate.bind(runtime),\n\t\t\t\t\thandleContentDelete: runtime.handleContentDelete.bind(runtime),\n\n\t\t\t\t\t// Trash handlers\n\t\t\t\t\thandleContentListTrashed: runtime.handleContentListTrashed.bind(runtime),\n\t\t\t\t\thandleContentRestore: runtime.handleContentRestore.bind(runtime),\n\t\t\t\t\thandleContentPermanentDelete: runtime.handleContentPermanentDelete.bind(runtime),\n\t\t\t\t\thandleContentCountTrashed: runtime.handleContentCountTrashed.bind(runtime),\n\t\t\t\t\thandleContentGetIncludingTrashed: runtime.handleContentGetIncludingTrashed.bind(runtime),\n\n\t\t\t\t\t// Duplicate handler\n\t\t\t\t\thandleContentDuplicate: runtime.handleContentDuplicate.bind(runtime),\n\n\t\t\t\t\t// Publishing & Scheduling handlers\n\t\t\t\t\thandleContentPublish: runtime.handleContentPublish.bind(runtime),\n\t\t\t\t\thandleContentUnpublish: runtime.handleContentUnpublish.bind(runtime),\n\t\t\t\t\thandleContentSchedule: runtime.handleContentSchedule.bind(runtime),\n\t\t\t\t\thandleContentUnschedule: runtime.handleContentUnschedule.bind(runtime),\n\t\t\t\t\thandleContentCountScheduled: runtime.handleContentCountScheduled.bind(runtime),\n\t\t\t\t\thandleContentDiscardDraft: runtime.handleContentDiscardDraft.bind(runtime),\n\t\t\t\t\thandleContentCompare: runtime.handleContentCompare.bind(runtime),\n\t\t\t\t\thandleContentTranslations: runtime.handleContentTranslations.bind(runtime),\n\n\t\t\t\t\t// Media handlers\n\t\t\t\t\thandleMediaList: runtime.handleMediaList.bind(runtime),\n\t\t\t\t\thandleMediaGet: runtime.handleMediaGet.bind(runtime),\n\t\t\t\t\thandleMediaUpload: runtime.handleMediaUpload.bind(runtime),\n\t\t\t\t\thandleMediaCreate: runtime.handleMediaCreate.bind(runtime),\n\t\t\t\t\thandleMediaRegisterUpload: runtime.handleMediaRegisterUpload.bind(runtime),\n\t\t\t\t\thandleMediaUpdate: runtime.handleMediaUpdate.bind(runtime),\n\t\t\t\t\t...(runtime.handleMediaReplaceMetadata\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\thandleMediaReplaceMetadata: runtime.handleMediaReplaceMetadata.bind(runtime),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\thandleMediaDelete: runtime.handleMediaDelete.bind(runtime),\n\n\t\t\t\t\t// Comment administration\n\t\t\t\t\t...(runtime.handleCommentModerate\n\t\t\t\t\t\t? { handleCommentModerate: runtime.handleCommentModerate.bind(runtime) }\n\t\t\t\t\t\t: {}),\n\n\t\t\t\t\t// Revision handlers\n\t\t\t\t\thandleRevisionList: runtime.handleRevisionList.bind(runtime),\n\t\t\t\t\thandleRevisionGet: runtime.handleRevisionGet.bind(runtime),\n\t\t\t\t\thandleRevisionRestore: runtime.handleRevisionRestore.bind(runtime),\n\n\t\t\t\t\t// Plugin routes\n\t\t\t\t\thandlePluginApiRoute: runtime.handlePluginApiRoute.bind(runtime),\n\t\t\t\t\tgetPluginEditorExtension: runtime.getPluginEditorExtension.bind(runtime),\n\t\t\t\t\tgetPluginEditorDraftSchema: runtime.getPluginEditorDraftSchema.bind(runtime),\n\t\t\t\t\thandlePublicPluginApiRoute: createPublicPluginApiRouteHandler(runtime),\n\t\t\t\t\tgetPluginRouteMeta: runtime.getPluginRouteMeta.bind(runtime),\n\t\t\t\t\tgetPluginMcpTools: runtime.getPluginMcpTools.bind(runtime),\n\t\t\t\t\tgetEnabledPluginMcpTools: runtime.getEnabledPluginMcpTools.bind(runtime),\n\t\t\t\t\tserializePluginMcpConsent: runtime.serializePluginMcpConsent.bind(runtime),\n\t\t\t\t\thandlePluginMcpTool: runtime.handlePluginMcpTool.bind(runtime),\n\t\t\t\t\thandlePluginMcpDenied: runtime.handlePluginMcpDenied.bind(runtime),\n\n\t\t\t\t\t// Media provider methods\n\t\t\t\t\tgetMediaProvider: runtime.getMediaProvider.bind(runtime),\n\t\t\t\t\tgetMediaProviderList: runtime.getMediaProviderList.bind(runtime),\n\n\t\t\t\t\t// Page contribution methods (for EmDashHead/EmDashBodyStart/EmDashBodyEnd)\n\t\t\t\t\tcollectPageMetadata: runtime.collectPageMetadata.bind(runtime),\n\t\t\t\t\tcollectPageFragments: runtime.collectPageFragments.bind(runtime),\n\n\t\t\t\t\t// Lazy search index health check — search endpoints call this\n\t\t\t\t\t// before querying so a crash-corrupted index gets repaired on\n\t\t\t\t\t// first use rather than stalling every cold start.\n\t\t\t\t\tensureSearchHealthy: runtime.ensureSearchHealthy.bind(runtime),\n\n\t\t\t\t\t// Direct access (for advanced use cases)\n\t\t\t\t\tstorage: runtime.storage,\n\t\t\t\t\t// Lazy getter, not an eager snapshot: `locals.emdash` is built\n\t\t\t\t\t// before the per-request scoped db is installed in ALS, so reading\n\t\t\t\t\t// `runtime.db` here would capture the per-isolate singleton. Routes\n\t\t\t\t\t// access `emdash.db` later, during the request, when the scoped db\n\t\t\t\t\t// is active. For a stateless binding (D1) the two are equivalent,\n\t\t\t\t\t// but for a request-bound connection (pg/Hyperdrive) the singleton\n\t\t\t\t\t// belongs to the cold-start request and reusing it from a warm\n\t\t\t\t\t// request hangs on workerd's cross-request I/O guard.\n\t\t\t\t\tget db() {\n\t\t\t\t\t\treturn runtime.db;\n\t\t\t\t\t},\n\t\t\t\t\tgetPublicMediaUrl: createPublicMediaUrlResolver(runtime.storage),\n\t\t\t\t\thooks: runtime.hooks,\n\t\t\t\t\temail: runtime.email,\n\t\t\t\t\tconfiguredPlugins: runtime.configuredPlugins,\n\t\t\t\t\tsandboxedPluginEntries: runtime.sandboxedPluginEntries,\n\n\t\t\t\t\t// Configuration (for checking database type, auth mode, etc.)\n\t\t\t\t\tconfig,\n\n\t\t\t\t\t// Lazy manifest accessor — admin-only consumers call this on\n\t\t\t\t\t// demand. `requestCached` inside `getManifest` dedupes within\n\t\t\t\t\t// a single request.\n\t\t\t\t\tgetManifest: runtime.getManifest.bind(runtime),\n\n\t\t\t\t\t// Clear the URL pattern cache after schema mutations that\n\t\t\t\t\t// affect collection URL patterns.\n\t\t\t\t\tinvalidateUrlPatternCache,\n\n\t\t\t\t\t// Sandbox runner (for marketplace plugin install/update)\n\t\t\t\t\tgetSandboxRunner: runtime.getSandboxRunner.bind(runtime),\n\t\t\t\t\tisSandboxBypassed: runtime.isSandboxBypassed.bind(runtime),\n\n\t\t\t\t\t// Sync marketplace plugin states (after install/update/uninstall)\n\t\t\t\t\tsyncMarketplacePlugins: runtime.syncMarketplacePlugins.bind(runtime),\n\n\t\t\t\t\t// Sync registry plugin states (after install/update/uninstall)\n\t\t\t\t\tsyncRegistryPlugins: runtime.syncRegistryPlugins.bind(runtime),\n\t\t\t\t\trunPluginInstallLifecycle: runtime.runPluginInstallLifecycle.bind(runtime),\n\t\t\t\t\trunPluginActivateLifecycle: runtime.runPluginActivateLifecycle.bind(runtime),\n\t\t\t\t\trunPluginUninstallLifecycle: runtime.runPluginUninstallLifecycle.bind(runtime),\n\t\t\t\t\tgetRuntimePluginSettingsSchema: runtime.getRuntimePluginSettingsSchema.bind(runtime),\n\n\t\t\t\t\t// Update plugin enabled/disabled status and rebuild hook pipeline\n\t\t\t\t\tsetPluginStatus: runtime.setPluginStatus.bind(runtime),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof PendingMigrationsError) {\n\t\t\t\t\treturn pendingMigrationsResponse(error);\n\t\t\t\t}\n\t\t\t\tif (migrationMode === \"manual\" && isMissingTableError(error)) {\n\t\t\t\t\tconsole.error(\"[emdash] database schema is unavailable in manual migration mode:\", error);\n\t\t\t\t\treturn migrationRequiredResponse();\n\t\t\t\t}\n\t\t\t\tconsole.error(\"EmDash middleware error:\", error);\n\t\t\t\tconst configurationError = runtimeConfigurationErrorResponse(error, url.pathname);\n\t\t\t\tif (configurationError) return configurationError;\n\t\t\t}\n\n\t\t\t// Ask the adapter for a request-scoped db. When it returns one, we stash\n\t\t\t// it in ALS so the runtime's db getter and loader's getDb() pick it up,\n\t\t\t// then call commit() after next() so the adapter can persist any\n\t\t\t// per-request state (e.g. a D1 bookmark cookie for read-your-writes).\n\t\t\tconst lastContentWriteAt =\n\t\t\t\tcanUseCachedBinding && config?.database?.needsLastContentWriteAt\n\t\t\t\t\t? await getLastContentWriteAt()\n\t\t\t\t\t: undefined;\n\t\t\tconst scoped = createRequestScopedDb({\n\t\t\t\tconfig: config?.database?.config,\n\t\t\t\tisAuthenticated,\n\t\t\t\tendedAuthenticated,\n\t\t\t\tisWrite,\n\t\t\t\tcanUseCachedBinding,\n\t\t\t\tcookies: context.cookies,\n\t\t\t\turl,\n\t\t\t\tlastContentWriteAt,\n\t\t\t});\n\n\t\t\tconst renderAndFinalize = async () => {\n\t\t\t\tconst t0 = performance.now();\n\t\t\t\tconst response = await next();\n\t\t\t\ttimings.push({ name: \"render\", dur: performance.now() - t0, desc: \"Page render\" });\n\t\t\t\ttimings.push({ name: \"mw\", dur: performance.now() - mwStart, desc: \"Total middleware\" });\n\t\t\t\tpushMetricsTimings(timings, metrics);\n\t\t\t\t// Server-Timing only sees pre-stream queries; the stream-end\n\t\t\t\t// wrapper (instrumentation-gated, no-op otherwise) emits the\n\t\t\t\t// final counters once the body finishes streaming.\n\t\t\t\treturn wrapBodyForStreamMetrics(finalizeResponse(response, timings));\n\t\t\t};\n\n\t\t\tif (scoped) {\n\t\t\t\tconst { deferredTasks, lifecycle } = coordinateScopedDbLifecycle(scoped);\n\t\t\t\tconst parent = getRequestContext();\n\t\t\t\tconst ctx = parent\n\t\t\t\t\t? { ...parent, db: scoped.db, deferredTasks }\n\t\t\t\t\t: { editMode: false, db: scoped.db, metrics, deferredTasks };\n\t\t\t\treturn runWithContext(ctx, () =>\n\t\t\t\t\t// commit() persists per-request state (e.g. the D1 bookmark cookie)\n\t\t\t\t\t// before the response returns, even if render throws; close()\n\t\t\t\t\t// waits for stream-end and request-owned deferred work. See finishScoped.\n\t\t\t\t\tfinishScoped(lifecycle, renderAndFinalize),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn renderAndFinalize();\n\t\t}; // end doInit\n\n\t\tif (playgroundDb) {\n\t\t\t// Read the edit-mode cookie to determine if visual editing is active.\n\t\t\t// Default to false -- editing is opt-in via the playground toolbar toggle.\n\t\t\tconst editMode = context.cookies.get(\"emdash-edit-mode\")?.value === \"true\";\n\t\t\t// Playground DBs are per-session isolated instances whose schema is\n\t\t\t// independent of the configured one — flag as isolated so schema-\n\t\t\t// derived caches (manifest, taxonomy defs) rebuild against it.\n\t\t\tconst parent = getRequestContext();\n\t\t\tconst ctx = parent\n\t\t\t\t? { ...parent, editMode, db: playgroundDb, dbIsIsolated: true }\n\t\t\t\t: { editMode, db: playgroundDb, dbIsIsolated: true, metrics };\n\t\t\treturn runWithContext(ctx, doInit);\n\t\t}\n\t\treturn doInit();\n\t};\n\n\ttry {\n\t\treturn await runWithContext({ editMode: false, queryRecorder, metrics }, run);\n\t} finally {\n\t\t// Streamed responses defer the flush to stream end (see\n\t\t// wrapBodyForStreamMetrics) so the log captures queries issued while\n\t\t// the body renders. Only flush here for responses that were not\n\t\t// wrapped (no body: redirects, 304s, bodyless errors), where all\n\t\t// queries have already run by the time middleware returns.\n\t\tif (queryRecorder && !queryRecorder.deferredFlush) flushRecorder(queryRecorder);\n\t}\n});\n\nexport default onRequest;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,MAAa,uBAAuB,OAAO,IAAI,gBAAgB;;;;;;;;;;;AAiB/D,SAAgB,0BACf,iBACA,SACU;AACV,KAAI,gBAAiB,QAAO;AAC5B,MAAK,MAAM,UAAU,QAAQ,SAAS,CACrC,KAAI,OAAO,WAAW,iBAAiB,CAAE,QAAO;AAEjD,QAAO;;;AAIR,SAAgB,4BAA4B,QAI1C;AACD,KAAI,CAAC,OAAO,MAAO,QAAO,EAAE,WAAW,QAAQ;CAE/C,MAAM,QAAQ,OAAO;CACrB,MAAM,gBAAgB,gCAAgC;AACrD,MAAI;AACH,UAAO;WACC,OAAO;AACf,WAAQ,MAAM,4CAA4C,MAAM;;GAEhE;AACF,QAAO;EACN,QAAQ,cAAc;EACtB;EACA,WAAW;GAAE,QAAQ,OAAO;GAAQ,OAAO,cAAc;GAAQ;EACjE;;;;;;;;;;;;AAaF,SAAgB,2BAA2B,UAAoB,OAA6B;CAC3F,IAAI,SAAS;CACb,MAAM,iBAAiB;AACtB,MAAI,OAAQ;AACZ,WAAS;AACT,MAAI;AACH,UAAO;WACC,OAAO;AACf,WAAQ,MAAM,4CAA4C,MAAM;;;AAIlE,KAAI,CAAC,SAAS,MAAM;AACnB,YAAU;AACV,SAAO;;CAGR,MAAM,YAAY,IAAI,gBAAwC;EAC7D,OAAO;EACP,QAAQ;EACR,CAAC;CACF,MAAM,UAAU,IAAI,SAAS,SAAS,KAAK,YAAY,UAAU,EAAE,SAAS;CAC5E,MAAM,eAAe,QAAQ,IAAI,UAAU,qBAAqB;AAChE,KAAI,iBAAiB,OACpB,SAAQ,IAAI,SAAS,sBAAsB,aAAa;AAIzD,SAAQ,QAAQ,OAAO,iBAAiB;AACxC,QAAO;;;;;;;;;;;;;;;;;;;;AAqBR,eAAsB,aACrB,QACA,KACoB;CACpB,IAAI;AACJ,KAAI;AACH,aAAW,MAAM,KAAK;UACd,OAAO;AAGf,eAAa,OAAO,OAAO;AAC3B,cAAY,OAAO,MAAM;AACzB,QAAM;;AAEP,KAAI;AACH,SAAO,QAAQ;UACP,OAAO;AAKf,cAAY,OAAO,MAAM;AACzB,QAAM;;AAEP,QAAO,OAAO,QAAQ,2BAA2B,UAAU,OAAO,MAAM,GAAG;;;;;;;AAQ5E,SAAS,aAAa,QAA0B;AAC/C,KAAI;AACH,UAAQ;UACA,OAAO;AACf,UAAQ,MAAM,mEAAmE,MAAM;;;;;;;;AASzF,SAAS,YAAY,OAAuC;AAC3D,KAAI,CAAC,MAAO;AACZ,KAAI;AACH,SAAO;UACC,OAAO;AACf,UAAQ,MAAM,kEAAkE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;ACzJxF,MAAa,oBAAoB;;;;;;;AAuBjC,SAAgB,yBAAyB,UAA8B;AACtE,KAAI,CAAC,0BAA0B,CAAE,QAAO;AACxC,KAAI,CAAC,SAAS,KAAM,QAAO;CAK3B,MAAM,MAAM,mBAAmB;CAC/B,MAAM,UAAU,KAAK;AACrB,KAAI,CAAC,QAAS,QAAO;CACrB,MAAM,WAAW,KAAK;AAQtB,KAAI,SAAU,UAAS,gBAAgB;CAEvC,MAAM,YAAY,IAAI,gBAAwC,EAC7D,QAAQ;EACP,MAAM,WAA8B;GACnC,OAAO,UAAU;GACjB,QAAQ,UAAU;GAClB,OAAO,UAAU;GACjB,SAAS,YAAY,KAAK,GAAG,QAAQ;GACrC,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,eAAe,QAAQ;GACvB,cAAc,QAAQ;GACtB,WAAW,QAAQ;GACnB,aAAa,QAAQ;GACrB;AACD,UAAQ,IAAI,GAAG,kBAAkB,GAAG,KAAK,UAAU,SAAS,GAAG;AAE/D,MAAI,SAAU,eAAc,SAAS;IAEtC,CAAC;CAEF,MAAM,UAAU,IAAI,SAAS,SAAS,KAAK,YAAY,UAAU,EAAE,SAAS;CAC5E,MAAM,eAAe,QAAQ,IAAI,UAAU,qBAAqB;AAChE,KAAI,iBAAiB,OACpB,SAAQ,IAAI,SAAS,sBAAsB,aAAa;AAMzD,SAAQ,QAAQ,OAAO,iBAAiB;AACxC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnER,eAAe,sBAAqC;CACnD,MAAM,QAAQ,MAAM,cAAc,gBAAgB,eAAe;AAGjE,MAAK,MAAM,QAAQ,MAClB,sBAAqB,eAAe,KAAK,QAAQ,KAAK;;;;;;;;AAUxD,eAAe,wBAAuC;CACrD,MAAM,OAAO,MAAM,iBAAiB;AACpC,OAAM,QAAQ,WAAW,KAAK,KAAK,QAAQ,iBAAiB,IAAI,MAAM,EAAE,eAAe,OAAO,CAAC,CAAC,CAAC;;;AAIlG,eAAe,gBAA+B;CAI7C,MAAM,OAAO,OAHF,MAAM,OAAO,EAGF,WAAW,gBAAgB,CAAC,OAAO,OAAO,CAAC,UAAU,CAAC,SAAS;CACrF,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC;AACnD,OAAM,QAAQ,WAAW,MAAM,KAAK,SAAS,QAAQ,KAAK,CAAC,CAAC;;;;;;;AAQ7D,eAAsB,qBAAoC;AACzD,KAAI;AACH,QAAM,QAAQ,WAAW;GACxB,iBAAiB;GACjB,eAAe;GACf,qBAAqB;GACrB,uBAAuB;GACvB,CAAC;UACM,OAAO;AAGf,UAAQ,MAAM,gDAAgD,MAAM;;;;;;AC3DtE,SAAS,sBAAuC;AAC/C,QAAO;EACN,SAAS;EACT,OAAO;GACN,MAAM;GACN,SAAS;GACT;EACD;;AAGF,SAAgB,kCACf,SAC8B;AAC9B,QAAO,OAAO,UAAU,QAAQ,MAAM,YAAY;AAEjD,MADa,QAAQ,mBAAmB,UAAU,KAAK,EAC7C,WAAW,KACpB,QAAO,qBAAqB;AAG7B,SAAO,QAAQ,qBAAqB,UAAU,QAAQ,MAAM,QAAQ;;;;;;;;;;;;;AC2DtE,MAAM,2BAA2B,sBAAsB;;;;;;;;;AAUvD,MAAM,qCAAqC;AAC3C,IAAI,4BAA4B;;;;;;;;;;;;;;;;AAiBhC,MAAM,qBAAqB,OAAO,IAAI,wBAAwB;AAC9D,MAAM,iBAAiB;AAEvB,SAAS,kBAA2B;AACnC,QAAO,eAAe,wBAAwB;;AAG/C,SAAS,oBAA0B;AAClC,gBAAe,sBAAsB;;;;;;;;;AAUtC,MAAM,qBAAqB,OAAO,IAAI,wBAAwB;AAM9D,SAAS,mBAAkC;CAE1C,IAAI,SAAS,eAAe;AAC5B,KAAI,CAAC,QAAQ;AACZ,WAAS;GAAE,UAAU;GAAM,MAAM,gBAAgB;GAAE;AACnD,iBAAe,sBAAsB;;AAEtC,QAAO;;;AAIR,IAAI,kBAAkB;;;;AAKtB,SAAS,YAAiC;AACzC,KAAI,iBAAiB,OAAO,kBAAkB,UAAU;AAEvD,MAAI,CAAC,iBAAiB;AACrB,qBAAkB;GAElB,MAAM,SAAS;AACf,OAAI,OAAO,QAAQ,OAAO,OAAO,SAAS,SACzC,eAEC,OAAO,KAKP;OAED,eAAc,KAAK;;AAIrB,SAAO;;AAER,QAAO;;;;;AAMR,SAAS,aAA+B;AAEvC,QAAQA,WAAuC,EAAE;;;;;AAMlD,SAAS,kBACR,QACA,eACsB;CAOtB,MAAM,gBAAgB;AACtB,QAAO;EACN;EACA;EACA,SAAS,YAAY;EACNC;EAEUC;EAGVC;EACEC;EACjB,gBAAgB,cAAc;EAC9B,iBAAkB,cAAc,mBAA+B;EAC/D,wBAAyBC,oBAAsD,EAAE;EACjF,qBAAqB,cAAc;EACnC,sBAAuBC,kBAAkD,EAAE;EAC3E;;;;;;;;;;AAYF,eAAe,WACd,QACA,eACA,aACyB;CAQzB,MAAM,SAAS,kBAAkB;AACjC,QAAO,aACN,OAAO,YACD,OAAO,UACb,OAAO,mBAAmB;EACzB,MAAM,OAAO,kBAAkB,QAAQ,cAAc;EACrD,MAAM,UAAU,MAAM,cAAc,OAAO,MAAM,YAAY;AAC7D,MAAI,gBAAgB,CACnB,QAAO,WAAW;MAQlB,SAAQ,UAAU,CAAC,OAAO,UAAmB;AAC5C,WAAQ,MAAM,sDAAsD,MAAM;IACzE;AAEH,SAAO;IAER;EACC,YAAY;EACZ,SAAS,YAAY,YAAY,QAAQ;EACzC,CACD;;;;;;;;;;;;;;;;AAiBF,eAAsB,kBACrB,UAGI,EAAE,EACmC;CACzC,MAAM,SAAS,WAAW;AAC1B,KAAI,CAAC,OAAQ,QAAO,EAAE,WAAW,EAAE,EAAE;AACrC,QAAO,kBAAkB,SAAS,YAAY,QAAQ,kBAAkB,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsClF,eAAsB,kBACrB,KACa;CACb,MAAM,SAAS,WAAW;AAC1B,KAAI,CAAC,OACJ,OAAM,IAAI,MACT,0FACA;AAEF,QAAO,kBAAkB,QAAQ,OAAO,YAAY,IAAI,QAAQ,CAAC;;;;;;;;;;;;;;;;AAiBlE,eAAe,kBACd,QACA,IACa;CACb,MAAM,gBAAgB,+BAA+B,OAAO;AAC5D,KAAI,mBAAmB,CAEtB,QAAO,6BAA6B,QADpB,MAAM,WAAW,QAAQ,cAAc,EACF,GAAG;CAGzD,MAAM,gBAAgB,gCAAgC,GAAG;AAMzD,QAAO,eALS;EACf,UAAU;EACV,SAAS,qBAAqB,YAAY,KAAK,CAAC;EAChD;EACA,EAC8B,YAAY;AAS1C,SAAO,6BAA6B,QARpB,OAAO,YAAY;AAClC,OAAI;AACH,WAAO,MAAM,WAAW,QAAQ,cAAc;aACrC;AACT,kBAAc,QAAQ;AACtB,UAAM,cAAc;;MAElB,EACiD,GAAG;GACvD;;AAGH,eAAe,6BACd,QACA,SACA,IACa;CACb,MAAM,SAASC,wBAAsB;EACpC,QAAQ,OAAO,UAAU;EACzB,iBAAiB;EAGjB,SAAS;EACT,qBAAqB;EACrB,SAAS;EACT,KAAK;EACL,CAAC;AACF,KAAI,CAAC,OAEJ,QAAO,GAAG,QAAQ;CAEnB,MAAM,EAAE,QAAQ,eAAe,cAAc,4BAA4B,OAAO;CAEhF,MAAM,SAAS,mBAAmB;CAClC,MAAM,MAAM,SACT;EAAE,GAAG;EAAQ,IAAI,OAAO;EAAI;EAAe,GAC3C;EACA,UAAU;EACV,IAAI,OAAO;EACX,SAAS,qBAAqB,YAAY,KAAK,CAAC;EAChD;EACA;AACH,KAAI;AACH,SAAO,MAAM,eAAe,WAAW,GAAG,QAAQ,CAAC;WAC1C;AAGT,MAAI;AACH,aAAU,QAAQ;WACV,OAAO;AACf,WAAQ,MAAM,2CAA2C,MAAM;;AAEhE,MAAI;AACH,aAAU,SAAS;WACX,OAAO;AACf,WAAQ,MAAM,0CAA0C,MAAM;;AAE/D,QAAM;;;;;;;;;;AAWR,MAAM,kBAAkB;CACvB,WAAW;CACX,WAAW;CACX;;;;;;AAOD,MAAM,iBAAiB,IAAI,IAAI,gCAAgC;AAE/D,SAAS,+BAA+B,QAA4C;CACnF,MAAM,kBACL,OAAO,YAAY,eAAe,QAAQ,MAAM,QAAQ,IAAI,yBAAyB;CACtF,MAAM,qBAAqB,OAAO,KAAK,IAAI;AAC3C,QAAO,4BAA4B,OAAO,YAAY;EACrD,KAAK,OAAO,KAAK,IAAI;EACrB,UAAU,mBAAmB;EAC7B,CAAC;;AAGH,SAAS,0BAA0B,OAAyC;AAC3E,SAAQ,MAAM,6CAA6C,MAAM,QAAQ,KAAK,KAAK,CAAC;AACpF,QAAO,2BAA2B;;AAGnC,SAAS,4BAAsC;AAC9C,QAAO,IAAI,SACV,wFACA;EACC,QAAQ;EACR,SAAS,EAAE,eAAe,MAAM;EAChC,CACD;;AAGF,SAAS,6BAAuC;AAC/C,QAAO,IAAI,SACV,gRACA;EACC,QAAQ;EACR,SAAS;GACR,gBAAgB;GAChB,iBAAiB;GACjB,eAAe;GACf;EACD,CACD;;AAGF,SAAS,kCAAkC,OAAgB,UAAmC;AAC7F,KAAI,CAAC,SAAS,WAAW,gBAAgB,CAAE,QAAO;AAClD,KAAI,iBAAiB,yBACpB,QAAO,SAAS,MAAM,MAAM,MAAM,SAAS,IAAI;AAEhD,KAAI,iBAAiB,sBAAsB,MAAM,SAAS,oBACzD,QAAO,SAAS,MAAM,MAAM,MAAM,SAAS,IAAI;AAEhD,QAAO;;;;;;AAOR,SAAS,iBACR,UACA,eACW;CACX,MAAM,MAAM,IAAI,SAAS,SAAS,MAAM,SAAS;CACjD,MAAM,eAAe,QAAQ,IAAI,UAAU,qBAAqB;AAChE,KAAI,iBAAiB,OACpB,SAAQ,IAAI,KAAK,sBAAsB,aAAa;AAMrD,KAAI,CAAC,IAAI,QAAQ,IAAI,yBAAyB,CAC7C,KAAI,QAAQ,IAAI,0BAA0B,UAAU;AAErD,KAAI,CAAC,IAAI,QAAQ,IAAI,kBAAkB,CACtC,KAAI,QAAQ,IAAI,mBAAmB,kCAAkC;AAEtE,KAAI,CAAC,IAAI,QAAQ,IAAI,qBAAqB,CACzC,KAAI,QAAQ,IAAI,sBAAsB,uDAAuD;AAE9F,KAAI,CAAC,IAAI,QAAQ,IAAI,0BAA0B,CAC9C,KAAI,QAAQ,IAAI,mBAAmB,aAAa;AAEjD,KAAI,iBAAiB,cAAc,SAAS,EAC3C,KAAI,QAAQ,IACX,iBACA,cACE,KAAK,MAAM;EACX,MAAM,MAAM,KAAK,MAAM,EAAE,IAAI;AAC7B,SAAO,EAAE,OAAO,GAAG,EAAE,KAAK,OAAO,IAAI,SAAS,EAAE,KAAK,KAAK,GAAG,EAAE,KAAK,OAAO;GAC1E,CACD,KAAK,KAAK,CACZ;AAEF,QAAO;;;;;;;;;AAUR,SAAS,mBACR,SACA,SACO;AACP,KAAI,QAAQ,UAAU,GAAG;AACxB,UAAQ,KAAK;GAAE,MAAM;GAAY,KAAK,QAAQ;GAAW,MAAM;GAAY,CAAC;AAC5E,UAAQ,KAAK;GAAE,MAAM;GAAY,KAAK,QAAQ;GAAS,MAAM;GAAe,CAAC;AAC7E,MAAI,QAAQ,kBAAkB,KAC7B,SAAQ,KAAK;GAAE,MAAM;GAAY,KAAK,QAAQ;GAAe,MAAM;GAAkB,CAAC;AAEvF,MAAI,QAAQ,iBAAiB,KAC5B,SAAQ,KAAK;GAAE,MAAM;GAAW,KAAK,QAAQ;GAAc,MAAM;GAAiB,CAAC;;AAGrF,KAAI,QAAQ,WAAW,EACtB,SAAQ,KAAK;EAAE,MAAM;EAAa,KAAK,QAAQ;EAAU,MAAM;EAAkB,CAAC;AAEnF,KAAI,QAAQ,YAAY,QAAQ,cAAc,GAAG;AAChD,UAAQ,KAAK;GAAE,MAAM;GAAa,KAAK,QAAQ;GAAW,MAAM;GAAc,CAAC;AAC/E,UAAQ,KAAK;GAAE,MAAM;GAAc,KAAK,QAAQ;GAAa,MAAM;GAAgB,CAAC;;;;AAKtF,MAAM,wBAAwB,IAAI,IAAI,CAAC,gBAAgB,cAAc,CAAC;AACtE,MAAM,wBAAwB;;;;;;;AAQ9B,SAASA,wBACR,MAC0E;AAC1E,KAAI,OAAOC,0BAAiC,WAAY,QAAO;AAK/D,QAHWA,sBAGD,KAAK;;AAGhB,MAAM,YAAYC,YAAmB,IAAI,KAAKA,UAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;AAuBlE,SAAS,oBAAoB,SAA2B;AACvD,KAAI,QAAQ,iBAAiB,CAAC,aAAa,CAAC,QAAQ,OAAO,QAAS;AACpE,SAAQ,MAAM,IAAI,EAAE,cAAc,WAAW,CAAC;;AAG/C,MAAa,YAAY,iBAAiB,OAAO,SAAS,SAAS;CAClE,MAAM,EAAE,SAAS,QAAQ,YAAY;CACrC,MAAM,MAAM,QAAQ;AAMpB,KAAI,CAAC,IAAI,SAAS,WAAW,WAAW,IAAI,eAAe,eAK1D;MAJ0B,cAAc,cAAc,MACpD,MACA,EAAE,QAAQ,MAAM,MAA4B,EAAE,WAAW,IAAI,aAAa,EAAE,QAAQ,CACrF,CAEA,QAAO,iBAAiB,MAAM,MAAM,CAAC;;AAIvC,qBAAoB,QAAQ;CAE5B,MAAM,gBAAgB,0BAA0B,GAC7C,eAAe,IAAI,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,eAAe,IAAI,UAAU,GAC9F;CAEH,MAAM,UAAU,qBAAqB,YAAY,KAAK,CAAC;CAEvD,MAAM,MAAM,YAA+B;EAC1C,MAAM,SAAS,WAAW;EAC1B,MAAM,gBAAgB,SAAS,+BAA+B,OAAO,GAAG;EAGxE,MAAM,gBAAgB,IAAI,SAAS,WAAW,WAAW;EACzD,MAAM,uBACL,sBAAsB,IAAI,IAAI,SAAS,IAAI,sBAAsB,KAAK,IAAI,SAAS;EAIpF,MAAM,gBAAgB,QAAQ,IAAI,mBAAmB,EAAE,UAAU;EACjE,MAAM,kBAAkB,IAAI,aAAa,IAAI,WAAW;EAKxD,MAAM,eAAe,OAAO;EAW5B,MAAM,mBAAmB,QAAQ,IAAI,gBAAgB,KAAK;EAC1D,MAAM,cACL,QAAQ,iBAAiB,CAAC,mBAAmB,OAAO,MAAM,mBAAmB,QAAQ,QAAQ;EAU9F,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,gBAAgB,IAAI,IAC7D,aAAa,CACb,WAAW,UAAU;EACvB,MAAM,UAAU,QAAQ,WAAW,SAAS,QAAQ,WAAW;EAC/D,MAAM,kBAAkB,CAAC,CAAC,eAAe;EACzC,MAAM,2BAA2B,0BAA0B,iBAAiB,QAAQ;EACpF,MAAM,sBACL,CAAC,mBACD,CAAC,WACD,CAAC,gBACD,CAAC,iBACD,CAAC,iBACD,CAAC;AAEF,MAAI,CAAC,iBAAiB,CAAC,wBAAwB,CAAC,iBAAiB,CAAC,iBACjE;OAAI,CAAC,eAAe,CAAC,cAAc;IAClC,MAAM,UAA+D,EAAE;IACvE,MAAM,UAAU,YAAY,KAAK;IAajC,IAAI,aAAa;IACjB,IAAI,cAAc;AAClB,QAAI,kBAAkB,UAAU,CAAC,iBAAiB,IAAI,CAAC,QAAQ,eAAe;KAC7E,MAAM,KAAK,YAAY,KAAK;AAC5B,SAAI;MACH,MAAM,EAAE,UAAU,MAAM,OAAO;AAE/B,aADW,MAAM,OAAO,EACf,WAAW,qBAAqB,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,SAAS;AACxE,yBAAmB;cACX,OAAO;AACf,UAAI,oBAAoB,MAAM,CAC7B,cAAa;WACP;AAON,qBAAc,CAAC,kBAAkB,CAAC;AAClC,eAAQ,MAAM,mCAAmC,MAAM;;;AAGzD,aAAQ,KAAK;MAAE,MAAM;MAAS,KAAK,YAAY,KAAK,GAAG;MAAI,MAAM;MAAe,CAAC;;AAQlF,QAAI,UAAU,CAAC,aAAa;KAG3B,MAAM,iBAAsE,EAAE;KAC9E,MAAM,KAAK,YAAY,KAAK;AAC5B,SAAI;MACH,MAAM,UAAU,MAAM,WAAW,QAAQ,eAAe,eAAe;AACvE,yBAAmB;AAGnB,aAAO,SAAS;OACf,4BAHkC,kCAAkC,QAAQ;OAI5E,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;OAC9D,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;OAChE,mBAAmB,6BAA6B,QAAQ,QAAQ;OAIhE,SAAS,QAAQ;OACjB;cACO,OAAO;AACf,UAAI,iBAAiB,uBACpB,QAAO,0BAA0B,MAAM;AAExC,UAAI,kBAAkB,YAAY,oBAAoB,MAAM,EAAE;AAC7D,eAAQ,MACP,qEACA,MACA;AACD,cAAO,2BAA2B;;AAMnC,UAAI,KAAK,KAAK,GAAG,6BAA6B,oCAAoC;AACjF,mCAA4B,KAAK,KAAK;AACtC,eAAQ,MAAM,iEAAiE,MAAM;;;AAGvF,aAAQ,KAAK;MAAE,MAAM;MAAM,KAAK,YAAY,KAAK,GAAG;MAAI,MAAM;MAAgB,CAAC;AAI/E,UAAK,MAAM,OAAO,eAAgB,SAAQ,KAAK,IAAI;;AAEpD,QAAI,cAAc,CAAC,OAAO,OACzB,QAAO,iBAAiB,4BAA4B,EAAE,QAAQ;IAM/D,MAAM,qBACL,uBAAuB,QAAQ,UAAU,0BACtC,MAAM,uBAAuB,GAC7B;IACJ,MAAM,aAAaF,wBAAsB;KACxC,QAAQ,QAAQ,UAAU;KAC1B;KACA;KACA;KACA;KACA;KACA;KACA;KACA,CAAC;IACF,MAAM,UAAU,YAAY;KAC3B,MAAM,KAAK,YAAY,KAAK;KAC5B,MAAM,WAAW,MAAM,MAAM;AAC7B,aAAQ,KAAK;MAAE,MAAM;MAAU,KAAK,YAAY,KAAK,GAAG;MAAI,MAAM;MAAe,CAAC;AAClF,aAAQ,KAAK;MAAE,MAAM;MAAM,KAAK,YAAY,KAAK,GAAG;MAAS,MAAM;MAAoB,CAAC;AACxF,wBAAmB,SAAS,QAAQ;AAIpC,YAAO,yBAAyB,iBAAiB,UAAU,QAAQ,CAAC;;AAErE,QAAI,YAAY;KACf,MAAM,EAAE,eAAe,cAAc,4BAA4B,WAAW;KAC5E,MAAM,SAAS,mBAAmB;KAClC,MAAM,MAAM,SACT;MAAE,GAAG;MAAQ,IAAI,WAAW;MAAI;MAAe,GAC/C;MAAE,UAAU;MAAO,IAAI,WAAW;MAAI;MAAS;MAAe;KAejE,MAAM,eAAe,QAAQ,QAAQ,IAAI,SAAS,IAAI,IACpD,MAAM,KAAK,EAAE,CAAC,GACd,MAAM,CACN,WAAW,YAAY;AACzB,YAAO,eAAe,KAAK,YAAY;AACtC,UAAI,YAAa,aAAY,oBAAoB,CAAC;AAIlD,aAAO,aAAa,WAAW,QAAQ;OACtC;;AAEH,WAAO,SAAS;;;AAIlB,MAAI,CAAC,QAAQ;AACZ,WAAQ,MAAM,iCAAiC;AAC/C,UAAO,iBAAiB,MAAM,MAAM,CAAC;;EAMtC,MAAM,SAAS,YAAY;GAC1B,MAAM,UAA+D,EAAE;GACvE,MAAM,UAAU,YAAY,KAAK;AAEjC,OAAI;IAKH,MAAM,iBAAsE,EAAE;IAC9E,IAAI,KAAK,YAAY,KAAK;IAC1B,MAAM,UAAU,MAAM,WAAW,QAAQ,eAAe,eAAe;AACvE,YAAQ,KAAK;KAAE,MAAM;KAAM,KAAK,YAAY,KAAK,GAAG;KAAI,MAAM;KAAgB,CAAC;AAI/E,SAAK,MAAM,OAAO,eAAgB,SAAQ,KAAK,IAAI;AAGnD,uBAAmB;AASnB,WAAO,SAAS;KAEf,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAC1D,kBAAkB,QAAQ,iBAAiB,KAAK,QAAQ;KACxD,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;KAChE,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;KAC9D,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;KAC9D,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;KAG9D,0BAA0B,QAAQ,yBAAyB,KAAK,QAAQ;KACxE,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;KAChE,8BAA8B,QAAQ,6BAA6B,KAAK,QAAQ;KAChF,2BAA2B,QAAQ,0BAA0B,KAAK,QAAQ;KAC1E,kCAAkC,QAAQ,iCAAiC,KAAK,QAAQ;KAGxF,wBAAwB,QAAQ,uBAAuB,KAAK,QAAQ;KAGpE,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;KAChE,wBAAwB,QAAQ,uBAAuB,KAAK,QAAQ;KACpE,uBAAuB,QAAQ,sBAAsB,KAAK,QAAQ;KAClE,yBAAyB,QAAQ,wBAAwB,KAAK,QAAQ;KACtE,6BAA6B,QAAQ,4BAA4B,KAAK,QAAQ;KAC9E,2BAA2B,QAAQ,0BAA0B,KAAK,QAAQ;KAC1E,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;KAChE,2BAA2B,QAAQ,0BAA0B,KAAK,QAAQ;KAG1E,iBAAiB,QAAQ,gBAAgB,KAAK,QAAQ;KACtD,gBAAgB,QAAQ,eAAe,KAAK,QAAQ;KACpD,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAC1D,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAC1D,2BAA2B,QAAQ,0BAA0B,KAAK,QAAQ;KAC1E,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAC1D,GAAI,QAAQ,6BACT,EACA,4BAA4B,QAAQ,2BAA2B,KAAK,QAAQ,EAC5E,GACA,EAAE;KACL,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAG1D,GAAI,QAAQ,wBACT,EAAE,uBAAuB,QAAQ,sBAAsB,KAAK,QAAQ,EAAE,GACtE,EAAE;KAGL,oBAAoB,QAAQ,mBAAmB,KAAK,QAAQ;KAC5D,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAC1D,uBAAuB,QAAQ,sBAAsB,KAAK,QAAQ;KAGlE,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;KAChE,0BAA0B,QAAQ,yBAAyB,KAAK,QAAQ;KACxE,4BAA4B,QAAQ,2BAA2B,KAAK,QAAQ;KAC5E,4BAA4B,kCAAkC,QAAQ;KACtE,oBAAoB,QAAQ,mBAAmB,KAAK,QAAQ;KAC5D,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAC1D,0BAA0B,QAAQ,yBAAyB,KAAK,QAAQ;KACxE,2BAA2B,QAAQ,0BAA0B,KAAK,QAAQ;KAC1E,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;KAC9D,uBAAuB,QAAQ,sBAAsB,KAAK,QAAQ;KAGlE,kBAAkB,QAAQ,iBAAiB,KAAK,QAAQ;KACxD,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;KAGhE,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;KAC9D,sBAAsB,QAAQ,qBAAqB,KAAK,QAAQ;KAKhE,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;KAG9D,SAAS,QAAQ;KASjB,IAAI,KAAK;AACR,aAAO,QAAQ;;KAEhB,mBAAmB,6BAA6B,QAAQ,QAAQ;KAChE,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,mBAAmB,QAAQ;KAC3B,wBAAwB,QAAQ;KAGhC;KAKA,aAAa,QAAQ,YAAY,KAAK,QAAQ;KAI9C;KAGA,kBAAkB,QAAQ,iBAAiB,KAAK,QAAQ;KACxD,mBAAmB,QAAQ,kBAAkB,KAAK,QAAQ;KAG1D,wBAAwB,QAAQ,uBAAuB,KAAK,QAAQ;KAGpE,qBAAqB,QAAQ,oBAAoB,KAAK,QAAQ;KAC9D,2BAA2B,QAAQ,0BAA0B,KAAK,QAAQ;KAC1E,4BAA4B,QAAQ,2BAA2B,KAAK,QAAQ;KAC5E,6BAA6B,QAAQ,4BAA4B,KAAK,QAAQ;KAC9E,gCAAgC,QAAQ,+BAA+B,KAAK,QAAQ;KAGpF,iBAAiB,QAAQ,gBAAgB,KAAK,QAAQ;KACtD;YACO,OAAO;AACf,QAAI,iBAAiB,uBACpB,QAAO,0BAA0B,MAAM;AAExC,QAAI,kBAAkB,YAAY,oBAAoB,MAAM,EAAE;AAC7D,aAAQ,MAAM,qEAAqE,MAAM;AACzF,YAAO,2BAA2B;;AAEnC,YAAQ,MAAM,4BAA4B,MAAM;IAChD,MAAM,qBAAqB,kCAAkC,OAAO,IAAI,SAAS;AACjF,QAAI,mBAAoB,QAAO;;GAOhC,MAAM,qBACL,uBAAuB,QAAQ,UAAU,0BACtC,MAAM,uBAAuB,GAC7B;GACJ,MAAM,SAASA,wBAAsB;IACpC,QAAQ,QAAQ,UAAU;IAC1B;IACA;IACA;IACA;IACA,SAAS,QAAQ;IACjB;IACA;IACA,CAAC;GAEF,MAAM,oBAAoB,YAAY;IACrC,MAAM,KAAK,YAAY,KAAK;IAC5B,MAAM,WAAW,MAAM,MAAM;AAC7B,YAAQ,KAAK;KAAE,MAAM;KAAU,KAAK,YAAY,KAAK,GAAG;KAAI,MAAM;KAAe,CAAC;AAClF,YAAQ,KAAK;KAAE,MAAM;KAAM,KAAK,YAAY,KAAK,GAAG;KAAS,MAAM;KAAoB,CAAC;AACxF,uBAAmB,SAAS,QAAQ;AAIpC,WAAO,yBAAyB,iBAAiB,UAAU,QAAQ,CAAC;;AAGrE,OAAI,QAAQ;IACX,MAAM,EAAE,eAAe,cAAc,4BAA4B,OAAO;IACxE,MAAM,SAAS,mBAAmB;AAIlC,WAAO,eAHK,SACT;KAAE,GAAG;KAAQ,IAAI,OAAO;KAAI;KAAe,GAC3C;KAAE,UAAU;KAAO,IAAI,OAAO;KAAI;KAAS;KAAe,QAK5D,aAAa,WAAW,kBAAkB,CAC1C;;AAGF,UAAO,mBAAmB;;AAG3B,MAAI,cAAc;GAGjB,MAAM,WAAW,QAAQ,QAAQ,IAAI,mBAAmB,EAAE,UAAU;GAIpE,MAAM,SAAS,mBAAmB;AAIlC,UAAO,eAHK,SACT;IAAE,GAAG;IAAQ;IAAU,IAAI;IAAc,cAAc;IAAM,GAC7D;IAAE;IAAU,IAAI;IAAc,cAAc;IAAM;IAAS,EACnC,OAAO;;AAEnC,SAAO,QAAQ;;AAGhB,KAAI;AACH,SAAO,MAAM,eAAe;GAAE,UAAU;GAAO;GAAe;GAAS,EAAE,IAAI;WACpE;AAMT,MAAI,iBAAiB,CAAC,cAAc,cAAe,eAAc,cAAc;;EAE/E"}
|
|
@@ -2,7 +2,7 @@ import "../../../../../dialect-helpers-7_IqPdh9.mjs";
|
|
|
2
2
|
import "../../../../../config-CvmjJcVw.mjs";
|
|
3
3
|
import "../../../../../base64-DvnNgP7e.mjs";
|
|
4
4
|
import "../../../../../types-BkUg2yHx.mjs";
|
|
5
|
-
import "../../../../../media-
|
|
5
|
+
import "../../../../../media-CPXTbb1R.mjs";
|
|
6
6
|
import "../../../../../request-cache-DzLYKm_x.mjs";
|
|
7
7
|
import "../../../../../fts-manager-S-K0laoS.mjs";
|
|
8
8
|
import "../../../../../collection-slugs-cache-CjGk__zp.mjs";
|
|
@@ -18,7 +18,7 @@ import "../../../../../zod-generator-D6uE4WiS.mjs";
|
|
|
18
18
|
import "../../../../../media-usage-work-C2OZLCAd.mjs";
|
|
19
19
|
import "../../../../../content-repair-Q8IUrpLL.mjs";
|
|
20
20
|
import "../../../../../collection-deletion-processor-Br6rxs3u.mjs";
|
|
21
|
-
import { n as handleMediaUsageProgress, r as handleMediaUsageProgressAdvance } from "../../../../../media-usage-
|
|
21
|
+
import { n as handleMediaUsageProgress, r as handleMediaUsageProgressAdvance } from "../../../../../media-usage-De-zeR9E.mjs";
|
|
22
22
|
import "../../../../../settings-DYidIB26.mjs";
|
|
23
23
|
import "../../../../../site-settings-DOgRUoFX.mjs";
|
|
24
24
|
import "../../../../../errors-BF0_LFhW.mjs";
|