emdash 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-C2BzVy0p.d.mts → adapters-Di31kZ28.d.mts} +16 -1
- package/dist/adapters-Di31kZ28.d.mts.map +1 -0
- package/dist/{apply-Cma_PiF6.mjs → apply-5uslYdUu.mjs} +197 -25
- package/dist/apply-5uslYdUu.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +203 -33
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +5 -5
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +30 -4
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.mjs +2 -2
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +11 -4
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +467 -186
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +17 -9
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{byline-WuOq9MFJ.mjs → byline-C4OVd8b3.mjs} +3 -19
- package/dist/byline-C4OVd8b3.mjs.map +1 -0
- package/dist/{bylines-C_Wsnz4L.mjs → bylines-hPTW79hw.mjs} +20 -33
- package/dist/bylines-hPTW79hw.mjs.map +1 -0
- package/dist/{cache-E3Dts-yT.mjs → cache-BkKBuIvS.mjs} +1 -1
- package/dist/{cache-E3Dts-yT.mjs.map → cache-BkKBuIvS.mjs.map} +1 -1
- package/dist/chunks-HGz06Soa.mjs +19 -0
- package/dist/chunks-HGz06Soa.mjs.map +1 -0
- package/dist/cli/index.mjs +12 -11
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/{config-DkxPrM9l.mjs → config-BXwuX8Bx.mjs} +1 -1
- package/dist/{config-DkxPrM9l.mjs.map → config-BXwuX8Bx.mjs.map} +1 -1
- package/dist/{connection-B4zVnQIa.mjs → connection-2igzM-AT.mjs} +19 -2
- package/dist/connection-2igzM-AT.mjs.map +1 -0
- package/dist/{content-BsBoyj8G.mjs → content-D7J5y73J.mjs} +27 -1
- package/dist/{content-BsBoyj8G.mjs.map → content-D7J5y73J.mjs.map} +1 -1
- package/dist/database/instrumentation.d.mts +45 -0
- package/dist/database/instrumentation.d.mts.map +1 -0
- package/dist/database/instrumentation.mjs +61 -0
- package/dist/database/instrumentation.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db-errors-D0UT85nC.mjs +41 -0
- package/dist/db-errors-D0UT85nC.mjs.map +1 -0
- package/dist/{default-PUx9RK6u.mjs → default-CME5YdZ3.mjs} +1 -1
- package/dist/{default-PUx9RK6u.mjs.map → default-CME5YdZ3.mjs.map} +1 -1
- package/dist/{error-HBeQbVhV.mjs → error-CiYn9yDu.mjs} +1 -1
- package/dist/{error-HBeQbVhV.mjs.map → error-CiYn9yDu.mjs.map} +1 -1
- package/dist/{index-CCWzlriB.d.mts → index-De6_Xv3v.d.mts} +209 -19
- package/dist/index-De6_Xv3v.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +23 -21
- package/dist/{load-BhSSm-TS.mjs → load-CBcmDIot.mjs} +1 -1
- package/dist/{load-BhSSm-TS.mjs.map → load-CBcmDIot.mjs.map} +1 -1
- package/dist/{loader-BYzwzORf.mjs → loader-DeiBJEMe.mjs} +18 -12
- package/dist/loader-DeiBJEMe.mjs.map +1 -0
- package/dist/{manifest-schema-BsXINkQD.mjs → manifest-schema-V30qsMft.mjs} +1 -1
- package/dist/{manifest-schema-BsXINkQD.mjs.map → manifest-schema-V30qsMft.mjs.map} +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/{mode-CyPLdO3C.mjs → mode-CpNnGkPz.mjs} +1 -1
- package/dist/{mode-CyPLdO3C.mjs.map → mode-CpNnGkPz.mjs.map} +1 -1
- package/dist/page/index.d.mts +11 -2
- package/dist/page/index.d.mts.map +1 -1
- package/dist/page/index.mjs +23 -1
- package/dist/page/index.mjs.map +1 -1
- package/dist/{placeholder-DntBEQo7.mjs → placeholder-C-fk5hYI.mjs} +1 -1
- package/dist/{placeholder-DntBEQo7.mjs.map → placeholder-C-fk5hYI.mjs.map} +1 -1
- package/dist/{placeholder-BBCtpTES.d.mts → placeholder-tzpqGWII.d.mts} +1 -1
- package/dist/{placeholder-BBCtpTES.d.mts.map → placeholder-tzpqGWII.d.mts.map} +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-B6Vu0d2i.mjs → query-g4Ug-9j9.mjs} +79 -12
- package/dist/query-g4Ug-9j9.mjs.map +1 -0
- package/dist/{redirect-7lGhLBNZ.mjs → redirect-CN0Rt9Ob.mjs} +66 -10
- package/dist/redirect-CN0Rt9Ob.mjs.map +1 -0
- package/dist/{registry-BgnP3ysR.mjs → registry-Ci3WxVAr.mjs} +133 -97
- package/dist/registry-Ci3WxVAr.mjs.map +1 -0
- package/dist/request-cache-DiR961CV.mjs +79 -0
- package/dist/request-cache-DiR961CV.mjs.map +1 -0
- package/dist/request-context.d.mts +19 -16
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs.map +1 -1
- package/dist/{runner-DYv3rX8P.d.mts → runner-BR2xKwhn.d.mts} +2 -2
- package/dist/{runner-DYv3rX8P.d.mts.map → runner-BR2xKwhn.d.mts.map} +1 -1
- package/dist/{runner-Cd-_WyDo.mjs → runner-tQ7BJ4T7.mjs} +211 -134
- package/dist/runner-tQ7BJ4T7.mjs.map +1 -0
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +1 -1
- package/dist/{search-Cn1SYvYF.mjs → search-B0effn3j.mjs} +210 -226
- package/dist/search-B0effn3j.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +10 -9
- package/dist/seo/index.d.mts +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/taxonomies-K2z0Uhnj.mjs +308 -0
- package/dist/taxonomies-K2z0Uhnj.mjs.map +1 -0
- package/dist/{tokens-DKHiCYCB.mjs → tokens-BFPFx3CA.mjs} +1 -1
- package/dist/{tokens-DKHiCYCB.mjs.map → tokens-BFPFx3CA.mjs.map} +1 -1
- package/dist/{transport-BtcQ-Z7T.mjs → transport-BykRfpyy.mjs} +1 -1
- package/dist/{transport-BtcQ-Z7T.mjs.map → transport-BykRfpyy.mjs.map} +1 -1
- package/dist/{transport-CKQA_G44.d.mts → transport-H4Iwx7tC.d.mts} +1 -1
- package/dist/{transport-CKQA_G44.d.mts.map → transport-H4Iwx7tC.d.mts.map} +1 -1
- package/dist/{types-BmkQR1En.d.mts → types-6CUZRrZP.d.mts} +1 -1
- package/dist/{types-BmkQR1En.d.mts.map → types-6CUZRrZP.d.mts.map} +1 -1
- package/dist/{types-Dz9_WMS6.mjs → types-BH2L167P.mjs} +1 -1
- package/dist/{types-Dz9_WMS6.mjs.map → types-BH2L167P.mjs.map} +1 -1
- package/dist/{types-B6BzlZxx.d.mts → types-C2v0c34j.d.mts} +10 -1
- package/dist/{types-B6BzlZxx.d.mts.map → types-C2v0c34j.d.mts.map} +1 -1
- package/dist/{types-DNZpaCBk.d.mts → types-CFWjXmus.d.mts} +1 -1
- package/dist/{types-DNZpaCBk.d.mts.map → types-CFWjXmus.d.mts.map} +1 -1
- package/dist/{types-DeG21anB.d.mts → types-CnZYHyLW.d.mts} +55 -5
- package/dist/types-CnZYHyLW.d.mts.map +1 -0
- package/dist/{types-xxCWI3j0.mjs → types-DDS4MxsT.mjs} +11 -3
- package/dist/types-DDS4MxsT.mjs.map +1 -0
- package/dist/{types-C3ronwXb.d.mts → types-DgrIP0tF.d.mts} +102 -4
- package/dist/types-DgrIP0tF.d.mts.map +1 -0
- package/dist/{validate-DuZDIxfy.mjs → validate-CqsNItbt.mjs} +2 -2
- package/dist/{validate-DuZDIxfy.mjs.map → validate-CqsNItbt.mjs.map} +1 -1
- package/dist/{validate-Db1yNL3i.d.mts → validate-kM8Pjuf7.d.mts} +5 -52
- package/dist/validate-kM8Pjuf7.d.mts.map +1 -0
- package/dist/version-BnTKdfam.mjs +7 -0
- package/dist/{version-CMMjTuqu.mjs.map → version-BnTKdfam.mjs.map} +1 -1
- package/package.json +10 -5
- package/src/after.ts +62 -0
- package/src/api/handlers/content.ts +2 -0
- package/src/api/handlers/oauth-authorization.ts +2 -32
- package/src/api/handlers/oauth-clients.ts +40 -4
- package/src/api/handlers/taxonomies.ts +13 -0
- package/src/api/oauth/redirect-uri.ts +34 -0
- package/src/api/openapi/document.ts +126 -118
- package/src/api/schemas/content.ts +8 -0
- package/src/api/schemas/media.ts +26 -15
- package/src/api/schemas/schema.ts +1 -0
- package/src/astro/integration/font-provider.ts +178 -0
- package/src/astro/integration/index.ts +44 -0
- package/src/astro/integration/routes.ts +6 -0
- package/src/astro/integration/runtime.ts +117 -0
- package/src/astro/integration/virtual-modules.ts +41 -39
- package/src/astro/integration/vite-config.ts +16 -5
- package/src/astro/middleware/auth.ts +33 -1
- package/src/astro/middleware/request-context.ts +15 -3
- package/src/astro/middleware.ts +340 -263
- package/src/astro/routes/admin.astro +21 -10
- package/src/astro/routes/api/auth/magic-link/send.ts +2 -1
- package/src/astro/routes/api/auth/passkey/options.ts +2 -1
- package/src/astro/routes/api/auth/passkey/verify.ts +5 -1
- package/src/astro/routes/api/auth/signup/request.ts +26 -8
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +10 -6
- package/src/astro/routes/api/content/[collection]/[id]/compare.ts +1 -1
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +1 -1
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +1 -1
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +5 -0
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +26 -0
- package/src/astro/routes/api/content/[collection]/[id].ts +30 -2
- package/src/astro/routes/api/content/[collection]/index.ts +19 -1
- package/src/astro/routes/api/content/[collection]/trash.ts +1 -1
- package/src/astro/routes/api/import/wordpress/execute.ts +1 -1
- package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +4 -3
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +5 -4
- package/src/astro/routes/api/manifest.ts +7 -0
- package/src/astro/routes/api/media/upload-url.ts +10 -2
- package/src/astro/routes/api/media.ts +10 -7
- package/src/astro/routes/api/oauth/device/code.ts +2 -1
- package/src/astro/routes/api/oauth/device/token.ts +2 -1
- package/src/astro/routes/api/oauth/register.ts +178 -0
- package/src/astro/routes/api/oauth/token.ts +15 -0
- package/src/astro/routes/api/openapi.json.ts +15 -5
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +2 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +1 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +1 -0
- package/src/astro/routes/api/search/index.ts +5 -0
- package/src/astro/routes/api/search/suggest.ts +3 -0
- package/src/astro/routes/api/setup/admin-verify.ts +30 -5
- package/src/astro/routes/api/setup/admin.ts +32 -8
- package/src/astro/routes/api/setup/index.ts +5 -2
- package/src/astro/routes/api/taxonomies/index.ts +1 -0
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +1 -1
- package/src/astro/types.ts +9 -0
- package/src/auth/rate-limit.ts +50 -22
- package/src/auth/setup-nonce.ts +22 -0
- package/src/auth/trusted-proxy.ts +92 -0
- package/src/bylines/index.ts +22 -45
- package/src/components/EmDashHead.astro +23 -7
- package/src/database/connection.ts +23 -1
- package/src/database/instrumentation.ts +98 -0
- package/src/database/migrations/035_bounded_404_log.ts +112 -0
- package/src/database/migrations/runner.ts +2 -0
- package/src/database/repositories/content.ts +39 -0
- package/src/database/repositories/options.ts +25 -0
- package/src/database/repositories/redirect.ts +111 -8
- package/src/database/types.ts +9 -0
- package/src/db/adapters.ts +15 -0
- package/src/emdash-runtime.ts +312 -92
- package/src/import/registry.ts +4 -3
- package/src/import/ssrf.ts +253 -12
- package/src/index.ts +6 -0
- package/src/loader.ts +19 -24
- package/src/mcp/server.ts +76 -3
- package/src/menus/index.ts +6 -3
- package/src/page/index.ts +1 -1
- package/src/page/seo-contributions.ts +36 -0
- package/src/plugins/context.ts +15 -3
- package/src/plugins/manager.ts +6 -0
- package/src/plugins/request-meta.ts +66 -15
- package/src/plugins/routes.ts +3 -1
- package/src/query.ts +104 -7
- package/src/request-cache.ts +106 -0
- package/src/request-context.ts +19 -0
- package/src/schema/query.ts +5 -2
- package/src/schema/registry.ts +243 -166
- package/src/schema/types.ts +13 -2
- package/src/schema/zod-generator.ts +4 -0
- package/src/search/fts-manager.ts +19 -5
- package/src/search/query.ts +4 -3
- package/src/seed/apply.ts +41 -1
- package/src/settings/index.ts +24 -5
- package/src/taxonomies/index.ts +324 -124
- package/src/utils/db-errors.ts +46 -0
- package/src/virtual-modules.d.ts +31 -10
- package/src/visual-editing/toolbar.ts +6 -1
- package/src/widgets/index.ts +54 -25
- package/dist/adapters-C2BzVy0p.d.mts.map +0 -1
- package/dist/apply-Cma_PiF6.mjs.map +0 -1
- package/dist/byline-WuOq9MFJ.mjs.map +0 -1
- package/dist/bylines-C_Wsnz4L.mjs.map +0 -1
- package/dist/connection-B4zVnQIa.mjs.map +0 -1
- package/dist/index-CCWzlriB.d.mts.map +0 -1
- package/dist/loader-BYzwzORf.mjs.map +0 -1
- package/dist/query-B6Vu0d2i.mjs.map +0 -1
- package/dist/redirect-7lGhLBNZ.mjs.map +0 -1
- package/dist/registry-BgnP3ysR.mjs.map +0 -1
- package/dist/runner-Cd-_WyDo.mjs.map +0 -1
- package/dist/search-Cn1SYvYF.mjs.map +0 -1
- package/dist/types-C3ronwXb.d.mts.map +0 -1
- package/dist/types-DeG21anB.d.mts.map +0 -1
- package/dist/types-xxCWI3j0.mjs.map +0 -1
- package/dist/validate-Db1yNL3i.d.mts.map +0 -1
- package/dist/version-CMMjTuqu.mjs +0 -7
|
@@ -1,32 +1,35 @@
|
|
|
1
|
-
import "../
|
|
1
|
+
import { getRequestContext, runWithContext } from "../request-context.mjs";
|
|
2
|
+
import { createRecorder, flushRecorder, isInstrumentationEnabled, kyselyLogOption } from "../database/instrumentation.mjs";
|
|
3
|
+
import "../connection-2igzM-AT.mjs";
|
|
2
4
|
import { t as validateIdentifier } from "../validate-VPnKoIzW.mjs";
|
|
3
5
|
import { a as isSqlite } from "../dialect-helpers-DhTzaUxP.mjs";
|
|
4
|
-
import { r as runMigrations } from "../runner-
|
|
5
|
-
import {
|
|
6
|
-
import { r as RevisionRepository } from "../content-
|
|
6
|
+
import { r as runMigrations } from "../runner-tQ7BJ4T7.mjs";
|
|
7
|
+
import { At as handleContentPublish, B as EmailPipeline, Ct as handleContentDiscardDraft, Dt as handleContentList, Et as handleContentGetIncludingTrashed, Ft as handleContentUnschedule, G as extractRequestMeta, H as createHookPipeline, It as handleContentUpdate, J as definePlugin, K as sanitizeHeadersForSandbox, L as PluginRouteRegistry, Lt as validateRev, Mt as handleContentSchedule, Nt as handleContentTranslations, Ot as handleContentListTrashed, Pt as handleContentUnpublish, R as DEV_CONSOLE_EMAIL_PLUGIN_ID, St as handleContentDelete, Tt as handleContentGet, U as resolveExclusiveHooks, W as CronExecutor, Z as after, _t as hashString, at as PluginStateRepository, bt as handleContentCountTrashed, ct as handleMediaDelete, dt as handleMediaUpdate, ft as handleRevisionGet, jt as handleContentRestore, kt as handleContentPermanentDelete, lt as handleMediaGet, mt as handleRevisionRestore, nt as loadBundleFromR2, pt as handleRevisionList, q as getTrustedProxyHeaders, st as handleMediaCreate, ut as handleMediaList, vt as handleContentCompare, wt as handleContentDuplicate, xt as handleContentCreate, yt as handleContentCountScheduled, z as devConsoleEmailDeliver } from "../search-B0effn3j.mjs";
|
|
8
|
+
import { r as RevisionRepository } from "../content-D7J5y73J.mjs";
|
|
7
9
|
import "../base64-MBPo9ozB.mjs";
|
|
8
10
|
import "../types-CMMN0pNg.mjs";
|
|
9
11
|
import { t as MediaRepository } from "../media-DqHVh136.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import "../redirect-
|
|
12
|
-
import "../byline-
|
|
13
|
-
import { n as normalizeMediaValue } from "../placeholder-
|
|
14
|
-
import { i as setI18nConfig } from "../config-
|
|
15
|
-
import { i as FTSManager, n as SchemaRegistry } from "../registry-
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import "../
|
|
21
|
-
import "../
|
|
22
|
-
import "../
|
|
12
|
+
import { p as OptionsRepository } from "../apply-5uslYdUu.mjs";
|
|
13
|
+
import "../redirect-CN0Rt9Ob.mjs";
|
|
14
|
+
import "../byline-C4OVd8b3.mjs";
|
|
15
|
+
import { n as normalizeMediaValue } from "../placeholder-C-fk5hYI.mjs";
|
|
16
|
+
import { i as setI18nConfig } from "../config-BXwuX8Bx.mjs";
|
|
17
|
+
import { i as FTSManager, n as SchemaRegistry } from "../registry-Ci3WxVAr.mjs";
|
|
18
|
+
import { n as getDb } from "../loader-DeiBJEMe.mjs";
|
|
19
|
+
import "../request-cache-DiR961CV.mjs";
|
|
20
|
+
import "../taxonomies-K2z0Uhnj.mjs";
|
|
21
|
+
import { r as normalizeManifestRoute } from "../manifest-schema-V30qsMft.mjs";
|
|
22
|
+
import { a as invalidateUrlPatternCache } from "../query-g4Ug-9j9.mjs";
|
|
23
|
+
import "../tokens-BFPFx3CA.mjs";
|
|
24
|
+
import "../bylines-hPTW79hw.mjs";
|
|
25
|
+
import "../load-CBcmDIot.mjs";
|
|
23
26
|
import "../index.mjs";
|
|
24
|
-
import { n as VERSION, t as COMMIT } from "../version-
|
|
25
|
-
import { t as getAuthMode } from "../mode-
|
|
27
|
+
import { n as VERSION, t as COMMIT } from "../version-BnTKdfam.mjs";
|
|
28
|
+
import { t as getAuthMode } from "../mode-CpNnGkPz.mjs";
|
|
26
29
|
import { Kysely, sql } from "kysely";
|
|
27
30
|
import { defineMiddleware } from "astro:middleware";
|
|
28
31
|
import virtualConfig from "virtual:emdash/config";
|
|
29
|
-
import { createDialect,
|
|
32
|
+
import { createDialect, createRequestScopedDb } from "virtual:emdash/dialect";
|
|
30
33
|
import { mediaProviders } from "virtual:emdash/media-providers";
|
|
31
34
|
import { plugins } from "virtual:emdash/plugins";
|
|
32
35
|
import { createSandboxRunner, sandboxEnabled } from "virtual:emdash/sandbox-runner";
|
|
@@ -316,6 +319,7 @@ function isValidMetadataContribution(c) {
|
|
|
316
319
|
const FIELD_TYPE_TO_KIND = {
|
|
317
320
|
string: "string",
|
|
318
321
|
slug: "string",
|
|
322
|
+
url: "url",
|
|
319
323
|
text: "richText",
|
|
320
324
|
number: "number",
|
|
321
325
|
integer: "number",
|
|
@@ -371,6 +375,16 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
371
375
|
cronScheduler;
|
|
372
376
|
enabledPlugins;
|
|
373
377
|
pluginStates;
|
|
378
|
+
_cachedManifest = null;
|
|
379
|
+
_manifestPromise = null;
|
|
380
|
+
_manifestCacheKey;
|
|
381
|
+
/**
|
|
382
|
+
* Set to true after FTS indexes have been verified for this worker
|
|
383
|
+
* lifetime so we don't re-scan on every admin request. See
|
|
384
|
+
* ensureSearchHealthy().
|
|
385
|
+
*/
|
|
386
|
+
_searchHealthChecked = false;
|
|
387
|
+
_searchHealthPromise = null;
|
|
374
388
|
/** Current hook pipeline. Use the `hooks` getter for external access. */
|
|
375
389
|
get hooks() {
|
|
376
390
|
return this._hooks;
|
|
@@ -396,7 +410,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
396
410
|
if (ctx?.db) return ctx.db;
|
|
397
411
|
return this._db;
|
|
398
412
|
}
|
|
399
|
-
constructor(db, storage, configuredPlugins, sandboxedPlugins, sandboxedPluginEntries, hooks, enabledPlugins, pluginStates, config, mediaProviders, mediaProviderEntries, cronExecutor, cronScheduler, emailPipeline, allPipelinePlugins, pipelineFactoryOptions, runtimeDeps, pipelineRef) {
|
|
413
|
+
constructor(db, storage, configuredPlugins, sandboxedPlugins, sandboxedPluginEntries, hooks, enabledPlugins, pluginStates, config, mediaProviders, mediaProviderEntries, cronExecutor, cronScheduler, emailPipeline, allPipelinePlugins, pipelineFactoryOptions, runtimeDeps, pipelineRef, manifestCacheKey) {
|
|
400
414
|
this._db = db;
|
|
401
415
|
this.storage = storage;
|
|
402
416
|
this.configuredPlugins = configuredPlugins;
|
|
@@ -416,6 +430,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
416
430
|
this.pipelineFactoryOptions = pipelineFactoryOptions;
|
|
417
431
|
this.runtimeDeps = runtimeDeps;
|
|
418
432
|
this.pipelineRef = pipelineRef;
|
|
433
|
+
this._manifestCacheKey = manifestCacheKey;
|
|
419
434
|
}
|
|
420
435
|
/**
|
|
421
436
|
* Get the sandbox runner instance (for marketplace install/update)
|
|
@@ -456,6 +471,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
456
471
|
this.enabledPlugins.delete(pluginId);
|
|
457
472
|
await this.rebuildHookPipeline();
|
|
458
473
|
}
|
|
474
|
+
this.invalidateManifest();
|
|
459
475
|
}
|
|
460
476
|
/**
|
|
461
477
|
* Rebuild the hook pipeline from the current set of enabled plugins.
|
|
@@ -564,35 +580,49 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
564
580
|
/**
|
|
565
581
|
* Create and initialize the runtime
|
|
566
582
|
*/
|
|
567
|
-
static async create(deps) {
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
583
|
+
static async create(deps, timings) {
|
|
584
|
+
const phase = async (name, desc, fn) => {
|
|
585
|
+
if (!timings) return fn();
|
|
586
|
+
const t0 = performance.now();
|
|
587
|
+
try {
|
|
588
|
+
return await fn();
|
|
589
|
+
} finally {
|
|
590
|
+
timings.push({
|
|
591
|
+
name,
|
|
592
|
+
dur: performance.now() - t0,
|
|
593
|
+
desc
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
const db = await phase("rt.db", "DB init + migrations", () => EmDashRuntime.getDatabase(deps));
|
|
573
598
|
const storage = EmDashRuntime.getStorage(deps);
|
|
574
599
|
let pluginStates = /* @__PURE__ */ new Map();
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
600
|
+
await phase("rt.plugins", "Plugin states", async () => {
|
|
601
|
+
try {
|
|
602
|
+
const states = await db.selectFrom("_plugin_state").select(["plugin_id", "status"]).execute();
|
|
603
|
+
pluginStates = new Map(states.map((s) => [s.plugin_id, s.status]));
|
|
604
|
+
} catch {}
|
|
605
|
+
});
|
|
579
606
|
const enabledPlugins = /* @__PURE__ */ new Set();
|
|
580
607
|
for (const plugin of deps.plugins) {
|
|
581
608
|
const status = pluginStates.get(plugin.id);
|
|
582
609
|
if (status === void 0 || status === "active") enabledPlugins.add(plugin.id);
|
|
583
610
|
}
|
|
584
611
|
let siteInfo;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
612
|
+
await phase("rt.site", "Site info options", async () => {
|
|
613
|
+
try {
|
|
614
|
+
const siteOpts = await new OptionsRepository(db).getMany([
|
|
615
|
+
"emdash:site_title",
|
|
616
|
+
"emdash:site_url",
|
|
617
|
+
"emdash:locale"
|
|
618
|
+
]);
|
|
619
|
+
siteInfo = {
|
|
620
|
+
siteName: siteOpts.get("emdash:site_title") ?? void 0,
|
|
621
|
+
siteUrl: siteOpts.get("emdash:site_url") ?? void 0,
|
|
622
|
+
locale: siteOpts.get("emdash:locale") ?? void 0
|
|
623
|
+
};
|
|
624
|
+
} catch {}
|
|
625
|
+
});
|
|
596
626
|
const allPipelinePlugins = [...deps.plugins];
|
|
597
627
|
if (import.meta.env.DEV) try {
|
|
598
628
|
const devConsolePlugin = definePlugin({
|
|
@@ -631,8 +661,8 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
631
661
|
siteInfo
|
|
632
662
|
};
|
|
633
663
|
const pipeline = createHookPipeline(enabledPluginList, pipelineFactoryOptions);
|
|
634
|
-
const sandboxedPlugins = await EmDashRuntime.loadSandboxedPlugins(deps, db);
|
|
635
|
-
if (deps.config.marketplace && storage) await EmDashRuntime.loadMarketplacePlugins(db, storage, deps, sandboxedPlugins);
|
|
664
|
+
const sandboxedPlugins = await phase("rt.sandbox", "Sandboxed plugins", () => EmDashRuntime.loadSandboxedPlugins(deps, db));
|
|
665
|
+
if (deps.config.marketplace && storage) await phase("rt.market", "Marketplace plugins", () => EmDashRuntime.loadMarketplacePlugins(db, storage, deps, sandboxedPlugins));
|
|
636
666
|
const mediaProviders = /* @__PURE__ */ new Map();
|
|
637
667
|
const mediaProviderEntries = deps.mediaProviderEntries ?? [];
|
|
638
668
|
const providerContext = {
|
|
@@ -645,7 +675,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
645
675
|
} catch (error) {
|
|
646
676
|
console.warn(`Failed to initialize media provider "${entry.id}":`, error);
|
|
647
677
|
}
|
|
648
|
-
await EmDashRuntime.resolveExclusiveHooks(pipeline, db, deps);
|
|
678
|
+
await phase("rt.hooks", "Exclusive hook resolution", () => EmDashRuntime.resolveExclusiveHooks(pipeline, db, deps));
|
|
649
679
|
const emailPipeline = new EmailPipeline(pipeline);
|
|
650
680
|
if (sandboxRunner) sandboxRunner.setEmailSend((message, pluginId) => emailPipeline.send(message, pluginId));
|
|
651
681
|
const pipelineRef = { current: pipeline };
|
|
@@ -659,25 +689,41 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
659
689
|
});
|
|
660
690
|
let cronExecutor = null;
|
|
661
691
|
let cronScheduler = null;
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
692
|
+
await phase("rt.cron", "Cron init (recovery deferred post-response)", async () => {
|
|
693
|
+
try {
|
|
694
|
+
cronExecutor = new CronExecutor(db, invokeCronHook);
|
|
695
|
+
const executorForRecovery = cronExecutor;
|
|
696
|
+
after(async () => {
|
|
697
|
+
try {
|
|
698
|
+
const recovered = await executorForRecovery.recoverStaleLocks();
|
|
699
|
+
if (recovered > 0) console.log(`[cron] Recovered ${recovered} stale task lock(s)`);
|
|
700
|
+
} catch (error) {
|
|
701
|
+
console.error("[cron] Failed to recover stale task locks:", error);
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
if (typeof globalThis.navigator !== "undefined" && globalThis.navigator.userAgent === "Cloudflare-Workers") cronScheduler = new PiggybackScheduler(cronExecutor);
|
|
705
|
+
else cronScheduler = new NodeCronScheduler(cronExecutor);
|
|
706
|
+
cronScheduler.setSystemCleanup(async () => {
|
|
707
|
+
try {
|
|
708
|
+
await runSystemCleanup(db, storage ?? void 0);
|
|
709
|
+
} catch (error) {
|
|
710
|
+
console.error("[cleanup] System cleanup failed:", error);
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
pipeline.setContextFactory({ cronReschedule: () => cronScheduler?.reschedule() });
|
|
714
|
+
await cronScheduler.start();
|
|
715
|
+
} catch (error) {
|
|
716
|
+
console.warn("[cron] Failed to initialize cron system:", error);
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
const manifestCacheKey = await hashString([
|
|
720
|
+
COMMIT,
|
|
721
|
+
...deps.plugins.map((p) => `${p.id}@${p.version ?? ""}`).toSorted(),
|
|
722
|
+
...deps.sandboxedPluginEntries.map((e) => `${e.id}@${e.version}`).toSorted(),
|
|
723
|
+
virtualConfig?.i18n?.defaultLocale ?? "",
|
|
724
|
+
(virtualConfig?.i18n?.locales ?? []).toSorted().join(",")
|
|
725
|
+
].join("|"));
|
|
726
|
+
return new EmDashRuntime(db, storage, deps.plugins, sandboxedPlugins, deps.sandboxedPluginEntries, pipeline, enabledPlugins, pluginStates, deps.config, mediaProviders, mediaProviderEntries, cronExecutor, cronScheduler, emailPipeline, allPipelinePlugins, pipelineFactoryOptions, deps, pipelineRef, manifestCacheKey);
|
|
681
727
|
}
|
|
682
728
|
/**
|
|
683
729
|
* Get a media provider by ID
|
|
@@ -701,7 +747,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
701
747
|
*/
|
|
702
748
|
static async getDatabase(deps) {
|
|
703
749
|
const ctx = getRequestContext();
|
|
704
|
-
if (ctx?.db) return ctx.db;
|
|
750
|
+
if (ctx?.dbIsIsolated && ctx.db) return ctx.db;
|
|
705
751
|
const dbConfig = deps.config.database;
|
|
706
752
|
if (!dbConfig) try {
|
|
707
753
|
return await getDb();
|
|
@@ -713,8 +759,14 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
713
759
|
if (cached) return cached;
|
|
714
760
|
if (dbInitPromise) return dbInitPromise;
|
|
715
761
|
dbInitPromise = (async () => {
|
|
716
|
-
const db = new Kysely({
|
|
717
|
-
|
|
762
|
+
const db = new Kysely({
|
|
763
|
+
dialect: deps.createDialect(dbConfig.config),
|
|
764
|
+
log: kyselyLogOption()
|
|
765
|
+
});
|
|
766
|
+
const { applied } = await runMigrations(db);
|
|
767
|
+
if (applied.length > 0) try {
|
|
768
|
+
await new OptionsRepository(db).delete("emdash:manifest_cache");
|
|
769
|
+
} catch {}
|
|
718
770
|
try {
|
|
719
771
|
const [collectionCount, setupOption] = await Promise.all([db.selectFrom("_emdash_collections").select((eb) => eb.fn.countAll().as("count")).executeTakeFirstOrThrow(), db.selectFrom("options").select("value").where("name", "=", "emdash:setup_complete").executeTakeFirst()]);
|
|
720
772
|
const setupDone = (() => {
|
|
@@ -725,9 +777,9 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
725
777
|
}
|
|
726
778
|
})();
|
|
727
779
|
if (collectionCount.count === 0 && !setupDone) {
|
|
728
|
-
const { applySeed } = await import("../apply-
|
|
729
|
-
const { loadSeed } = await import("../load-
|
|
730
|
-
const { validateSeed } = await import("../validate-
|
|
780
|
+
const { applySeed } = await import("../apply-5uslYdUu.mjs").then((n) => n.n);
|
|
781
|
+
const { loadSeed } = await import("../load-CBcmDIot.mjs").then((n) => n.r);
|
|
782
|
+
const { validateSeed } = await import("../validate-CqsNItbt.mjs").then((n) => n.n);
|
|
731
783
|
const seed = await loadSeed();
|
|
732
784
|
if (validateSeed(seed).valid) {
|
|
733
785
|
await applySeed(db, seed, { onConflict: "skip" });
|
|
@@ -866,9 +918,52 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
866
918
|
});
|
|
867
919
|
}
|
|
868
920
|
/**
|
|
869
|
-
*
|
|
921
|
+
* Get the manifest, using an in-memory cache with a DB-persisted
|
|
922
|
+
* fallback for cold starts. Avoids N+1 schema registry queries
|
|
923
|
+
* on every request.
|
|
924
|
+
*
|
|
925
|
+
* Cache is invalidated by invalidateManifest(), called from schema
|
|
926
|
+
* API routes, MCP server, plugin toggle, and taxonomy def changes.
|
|
870
927
|
*/
|
|
871
928
|
async getManifest() {
|
|
929
|
+
if (getRequestContext()?.dbIsIsolated) return this._buildManifest();
|
|
930
|
+
if (this._cachedManifest) return this._cachedManifest;
|
|
931
|
+
try {
|
|
932
|
+
const cached = await new OptionsRepository(this.db).get("emdash:manifest_cache");
|
|
933
|
+
if (cached && cached.key === this._manifestCacheKey && cached.manifest) {
|
|
934
|
+
this._cachedManifest = cached.manifest;
|
|
935
|
+
return cached.manifest;
|
|
936
|
+
}
|
|
937
|
+
} catch {}
|
|
938
|
+
if (!this._manifestPromise) {
|
|
939
|
+
let manifestPromise;
|
|
940
|
+
const isCurrentLoad = () => this._manifestPromise === manifestPromise;
|
|
941
|
+
manifestPromise = this._loadManifest(isCurrentLoad);
|
|
942
|
+
this._manifestPromise = manifestPromise;
|
|
943
|
+
}
|
|
944
|
+
return this._manifestPromise;
|
|
945
|
+
}
|
|
946
|
+
async _loadManifest(isCurrentLoad) {
|
|
947
|
+
try {
|
|
948
|
+
const manifest = await this._buildManifest();
|
|
949
|
+
if (isCurrentLoad()) {
|
|
950
|
+
this._cachedManifest = manifest;
|
|
951
|
+
try {
|
|
952
|
+
await new OptionsRepository(this.db).set("emdash:manifest_cache", {
|
|
953
|
+
key: this._manifestCacheKey,
|
|
954
|
+
manifest
|
|
955
|
+
});
|
|
956
|
+
} catch {}
|
|
957
|
+
}
|
|
958
|
+
return manifest;
|
|
959
|
+
} finally {
|
|
960
|
+
if (isCurrentLoad()) this._manifestPromise = null;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Build the manifest from database (N+1 collection queries).
|
|
965
|
+
*/
|
|
966
|
+
async _buildManifest() {
|
|
872
967
|
const manifestCollections = {};
|
|
873
968
|
try {
|
|
874
969
|
const registry = new SchemaRegistry(this.db);
|
|
@@ -987,10 +1082,62 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
987
1082
|
}
|
|
988
1083
|
/**
|
|
989
1084
|
* Invalidate cached data derived from the manifest/schema.
|
|
990
|
-
* Called when collections
|
|
1085
|
+
* Called when collections, fields, plugins, or taxonomy defs change.
|
|
991
1086
|
*/
|
|
992
1087
|
invalidateManifest() {
|
|
1088
|
+
this._cachedManifest = null;
|
|
1089
|
+
this._manifestPromise = null;
|
|
993
1090
|
invalidateUrlPatternCache();
|
|
1091
|
+
try {
|
|
1092
|
+
new OptionsRepository(this.db).delete("emdash:manifest_cache").catch((error) => {
|
|
1093
|
+
console.error("Failed to delete persisted manifest cache", error);
|
|
1094
|
+
});
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
console.error("Failed to initialize manifest cache invalidation", error);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Verify and repair FTS indexes on demand. Runs at most once per worker
|
|
1101
|
+
* lifetime.
|
|
1102
|
+
*
|
|
1103
|
+
* Originally called from `EmDashRuntime.create()`, but on a busy D1 link
|
|
1104
|
+
* (e.g. SIN replica ~80-150ms per query) it added ~1.5s to every cold
|
|
1105
|
+
* start for a modest-sized site — more than every other init phase
|
|
1106
|
+
* combined. Anonymous public reads never touch the search write path,
|
|
1107
|
+
* so the cost isn't paid back for the vast majority of requests.
|
|
1108
|
+
*
|
|
1109
|
+
* Instead, search endpoints call this lazily: the first request that
|
|
1110
|
+
* actually needs the index pays the verify cost (usually fast — no
|
|
1111
|
+
* rebuild needed), everyone else runs cold-free.
|
|
1112
|
+
*
|
|
1113
|
+
* Uses the runtime's singleton database (`this._db`) rather than the
|
|
1114
|
+
* request-scoped DB. Verify reads only, but `rebuildIndex` writes, and
|
|
1115
|
+
* a GET search request on D1 carries a `first-unconstrained` session
|
|
1116
|
+
* that's free to route at a read replica — unsafe for writes. The
|
|
1117
|
+
* singleton always goes through the default binding, which the D1
|
|
1118
|
+
* adapter will promote to `first-primary` for write statements.
|
|
1119
|
+
*
|
|
1120
|
+
* Safe to call concurrently: repeated callers share the same in-flight
|
|
1121
|
+
* promise. Errors are swallowed internally so callers don't need to
|
|
1122
|
+
* defend against FTS not existing yet (pre-setup).
|
|
1123
|
+
*/
|
|
1124
|
+
async ensureSearchHealthy() {
|
|
1125
|
+
if (this._searchHealthChecked) return;
|
|
1126
|
+
if (this._searchHealthPromise) return this._searchHealthPromise;
|
|
1127
|
+
if (!isSqlite(this._db)) {
|
|
1128
|
+
this._searchHealthChecked = true;
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
this._searchHealthPromise = (async () => {
|
|
1132
|
+
try {
|
|
1133
|
+
const repaired = await new FTSManager(this._db).verifyAndRepairAll();
|
|
1134
|
+
if (repaired > 0) console.log(`Repaired ${repaired} corrupted FTS index(es)`);
|
|
1135
|
+
} catch {} finally {
|
|
1136
|
+
this._searchHealthChecked = true;
|
|
1137
|
+
this._searchHealthPromise = null;
|
|
1138
|
+
}
|
|
1139
|
+
})();
|
|
1140
|
+
return this._searchHealthPromise;
|
|
994
1141
|
}
|
|
995
1142
|
async handleContentList(collection, params) {
|
|
996
1143
|
return handleContentList(this.db, collection, params);
|
|
@@ -1016,7 +1163,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1016
1163
|
return result;
|
|
1017
1164
|
}
|
|
1018
1165
|
async handleContentUpdate(collection, id, body) {
|
|
1019
|
-
const { ContentRepository } = await import("../content-
|
|
1166
|
+
const { ContentRepository } = await import("../content-D7J5y73J.mjs").then((n) => n.n);
|
|
1020
1167
|
const repo = new ContentRepository(this.db);
|
|
1021
1168
|
const resolvedItem = await repo.findByIdOrSlug(collection, id);
|
|
1022
1169
|
const resolvedId = resolvedItem?.id ?? id;
|
|
@@ -1248,7 +1395,8 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1248
1395
|
if (trustedPlugin && this.enabledPlugins.has(trustedPlugin.id)) {
|
|
1249
1396
|
const routeRegistry = new PluginRouteRegistry({
|
|
1250
1397
|
db: this.db,
|
|
1251
|
-
emailPipeline: this.email ?? void 0
|
|
1398
|
+
emailPipeline: this.email ?? void 0,
|
|
1399
|
+
trustedProxyHeaders: getTrustedProxyHeaders(this.config)
|
|
1252
1400
|
});
|
|
1253
1401
|
routeRegistry.register(trustedPlugin);
|
|
1254
1402
|
const routeKey = path.replace(LEADING_SLASH_PATTERN, "");
|
|
@@ -1391,7 +1539,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1391
1539
|
} catch {}
|
|
1392
1540
|
try {
|
|
1393
1541
|
const headers = sanitizeHeadersForSandbox(request.headers);
|
|
1394
|
-
const meta = extractRequestMeta(request);
|
|
1542
|
+
const meta = extractRequestMeta(request, this.config);
|
|
1395
1543
|
return {
|
|
1396
1544
|
success: true,
|
|
1397
1545
|
data: await plugin.invokeRoute(routeName, body, {
|
|
@@ -1539,18 +1687,23 @@ function buildDependencies(config) {
|
|
|
1539
1687
|
};
|
|
1540
1688
|
}
|
|
1541
1689
|
/**
|
|
1542
|
-
* Get or create the runtime instance
|
|
1690
|
+
* Get or create the runtime instance.
|
|
1691
|
+
*
|
|
1692
|
+
* When `initTimings` is provided, any timing samples recorded during a
|
|
1693
|
+
* genuine cold init are appended. Subsequent warm calls (hitting the
|
|
1694
|
+
* cached instance) push nothing — callers should treat an empty array
|
|
1695
|
+
* as "warm, nothing to report".
|
|
1543
1696
|
*/
|
|
1544
|
-
async function getRuntime(config) {
|
|
1697
|
+
async function getRuntime(config, initTimings) {
|
|
1545
1698
|
if (runtimeInstance) return runtimeInstance;
|
|
1546
1699
|
if (runtimeInitializing) {
|
|
1547
1700
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1548
|
-
return getRuntime(config);
|
|
1701
|
+
return getRuntime(config, initTimings);
|
|
1549
1702
|
}
|
|
1550
1703
|
runtimeInitializing = true;
|
|
1551
1704
|
try {
|
|
1552
1705
|
const deps = buildDependencies(config);
|
|
1553
|
-
const runtime = await EmDashRuntime.create(deps);
|
|
1706
|
+
const runtime = await EmDashRuntime.create(deps, initTimings);
|
|
1554
1707
|
runtimeInstance = runtime;
|
|
1555
1708
|
return runtime;
|
|
1556
1709
|
} finally {
|
|
@@ -1558,142 +1711,270 @@ async function getRuntime(config) {
|
|
|
1558
1711
|
}
|
|
1559
1712
|
}
|
|
1560
1713
|
/**
|
|
1714
|
+
* Astro attaches AstroCookies to outgoing responses via a well-known global
|
|
1715
|
+
* symbol. Cloning a Response (`new Response(body, init)`) drops non-header
|
|
1716
|
+
* metadata, so any middleware that wraps the response must explicitly forward
|
|
1717
|
+
* this symbol or `cookies.set()` calls will be silently dropped.
|
|
1718
|
+
*/
|
|
1719
|
+
const ASTRO_COOKIES_SYMBOL = Symbol.for("astro.cookies");
|
|
1720
|
+
/**
|
|
1561
1721
|
* Baseline security headers applied to all responses.
|
|
1562
1722
|
* Admin routes get additional headers (strict CSP) from auth middleware.
|
|
1563
1723
|
*/
|
|
1564
|
-
function
|
|
1724
|
+
function finalizeResponse(response, serverTimings) {
|
|
1565
1725
|
const res = new Response(response.body, response);
|
|
1726
|
+
const astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);
|
|
1727
|
+
if (astroCookies !== void 0) Reflect.set(res, ASTRO_COOKIES_SYMBOL, astroCookies);
|
|
1566
1728
|
res.headers.set("X-Content-Type-Options", "nosniff");
|
|
1567
1729
|
res.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
1568
1730
|
res.headers.set("Permissions-Policy", "camera=(), microphone=(), geolocation=(), payment=()");
|
|
1569
1731
|
if (!res.headers.has("Content-Security-Policy")) res.headers.set("X-Frame-Options", "SAMEORIGIN");
|
|
1732
|
+
if (serverTimings && serverTimings.length > 0) res.headers.set("Server-Timing", serverTimings.map((t) => {
|
|
1733
|
+
const dur = Math.round(t.dur);
|
|
1734
|
+
return t.desc ? `${t.name};dur=${dur};desc="${t.desc}"` : `${t.name};dur=${dur}`;
|
|
1735
|
+
}).join(", "));
|
|
1570
1736
|
return res;
|
|
1571
1737
|
}
|
|
1572
1738
|
/** Public routes that require the runtime (sitemap, robots.txt, etc.) */
|
|
1573
1739
|
const PUBLIC_RUNTIME_ROUTES = new Set(["/sitemap.xml", "/robots.txt"]);
|
|
1574
1740
|
const SITEMAP_COLLECTION_RE = /^\/sitemap-[a-z][a-z0-9_]*\.xml$/;
|
|
1741
|
+
/**
|
|
1742
|
+
* Ask the configured database adapter for a per-request scoped Kysely. The
|
|
1743
|
+
* adapter encapsulates any per-request semantics (D1 sessions, read-replica
|
|
1744
|
+
* routing, bookmark cookies, etc.); core just forwards the cookie jar and
|
|
1745
|
+
* request flags and wraps next() in ALS if a scope was returned.
|
|
1746
|
+
*/
|
|
1747
|
+
function createRequestScopedDb$1(opts) {
|
|
1748
|
+
if (typeof createRequestScopedDb !== "function") return null;
|
|
1749
|
+
return createRequestScopedDb(opts);
|
|
1750
|
+
}
|
|
1575
1751
|
const onRequest = defineMiddleware(async (context, next) => {
|
|
1576
1752
|
const { request, locals, cookies } = context;
|
|
1577
1753
|
const url = context.url;
|
|
1578
|
-
const
|
|
1579
|
-
const
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1754
|
+
const queryRecorder = isInstrumentationEnabled() ? createRecorder(url.pathname, request.method, request.headers.get("x-perf-phase") ?? "default") : void 0;
|
|
1755
|
+
const run = async () => {
|
|
1756
|
+
const isEmDashRoute = url.pathname.startsWith("/_emdash");
|
|
1757
|
+
const isPublicRuntimeRoute = PUBLIC_RUNTIME_ROUTES.has(url.pathname) || SITEMAP_COLLECTION_RE.test(url.pathname);
|
|
1758
|
+
const hasEditCookie = cookies.get("emdash-edit-mode")?.value === "true";
|
|
1759
|
+
const hasPreviewToken = url.searchParams.has("_preview");
|
|
1760
|
+
const playgroundDb = locals.__playgroundDb;
|
|
1761
|
+
const sessionUser = context.isPrerendered ? null : await context.session?.get("user");
|
|
1762
|
+
if (!isEmDashRoute && !isPublicRuntimeRoute && !hasEditCookie && !hasPreviewToken) {
|
|
1763
|
+
if (!sessionUser && !playgroundDb) {
|
|
1764
|
+
const timings = [];
|
|
1765
|
+
const mwStart = performance.now();
|
|
1766
|
+
if (!setupVerified) {
|
|
1767
|
+
const t0 = performance.now();
|
|
1768
|
+
try {
|
|
1769
|
+
const { getDb } = await import("../loader-DeiBJEMe.mjs").then((n) => n.r);
|
|
1770
|
+
await (await getDb()).selectFrom("_emdash_migrations").selectAll().limit(1).execute();
|
|
1771
|
+
setupVerified = true;
|
|
1772
|
+
} catch {
|
|
1773
|
+
return context.redirect("/_emdash/admin/setup");
|
|
1774
|
+
}
|
|
1775
|
+
timings.push({
|
|
1776
|
+
name: "setup",
|
|
1777
|
+
dur: performance.now() - t0,
|
|
1778
|
+
desc: "Setup probe"
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
const config = getConfig();
|
|
1782
|
+
if (config) {
|
|
1783
|
+
const initSubTimings = [];
|
|
1784
|
+
const t0 = performance.now();
|
|
1785
|
+
try {
|
|
1786
|
+
const runtime = await getRuntime(config, initSubTimings);
|
|
1787
|
+
setupVerified = true;
|
|
1788
|
+
locals.emdash = {
|
|
1789
|
+
collectPageMetadata: runtime.collectPageMetadata.bind(runtime),
|
|
1790
|
+
collectPageFragments: runtime.collectPageFragments.bind(runtime)
|
|
1791
|
+
};
|
|
1792
|
+
} catch {}
|
|
1793
|
+
timings.push({
|
|
1794
|
+
name: "rt",
|
|
1795
|
+
dur: performance.now() - t0,
|
|
1796
|
+
desc: "Runtime init"
|
|
1797
|
+
});
|
|
1798
|
+
for (const sub of initSubTimings) timings.push(sub);
|
|
1799
|
+
}
|
|
1800
|
+
const anonScoped = createRequestScopedDb$1({
|
|
1801
|
+
config: config?.database?.config,
|
|
1802
|
+
isAuthenticated: false,
|
|
1803
|
+
isWrite: request.method !== "GET" && request.method !== "HEAD",
|
|
1804
|
+
cookies,
|
|
1805
|
+
url
|
|
1806
|
+
});
|
|
1807
|
+
const runAnon = async () => {
|
|
1808
|
+
const t0 = performance.now();
|
|
1809
|
+
const response = await next();
|
|
1810
|
+
timings.push({
|
|
1811
|
+
name: "render",
|
|
1812
|
+
dur: performance.now() - t0,
|
|
1813
|
+
desc: "Page render"
|
|
1814
|
+
});
|
|
1815
|
+
timings.push({
|
|
1816
|
+
name: "mw",
|
|
1817
|
+
dur: performance.now() - mwStart,
|
|
1818
|
+
desc: "Total middleware"
|
|
1819
|
+
});
|
|
1820
|
+
return finalizeResponse(response, timings);
|
|
1821
|
+
};
|
|
1822
|
+
if (anonScoped) {
|
|
1823
|
+
const parent = getRequestContext();
|
|
1824
|
+
return runWithContext(parent ? {
|
|
1825
|
+
...parent,
|
|
1826
|
+
db: anonScoped.db
|
|
1827
|
+
} : {
|
|
1828
|
+
editMode: false,
|
|
1829
|
+
db: anonScoped.db
|
|
1830
|
+
}, async () => {
|
|
1831
|
+
const response = await runAnon();
|
|
1832
|
+
anonScoped.commit();
|
|
1833
|
+
return response;
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
return runAnon();
|
|
1591
1837
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1838
|
+
}
|
|
1839
|
+
const config = getConfig();
|
|
1840
|
+
if (!config) {
|
|
1841
|
+
console.error("EmDash: No configuration found");
|
|
1842
|
+
return finalizeResponse(await next());
|
|
1843
|
+
}
|
|
1844
|
+
const doInit = async () => {
|
|
1845
|
+
const timings = [];
|
|
1846
|
+
const mwStart = performance.now();
|
|
1847
|
+
try {
|
|
1848
|
+
const initSubTimings = [];
|
|
1849
|
+
let t0 = performance.now();
|
|
1850
|
+
const runtime = await getRuntime(config, initSubTimings);
|
|
1851
|
+
timings.push({
|
|
1852
|
+
name: "rt",
|
|
1853
|
+
dur: performance.now() - t0,
|
|
1854
|
+
desc: "Runtime init"
|
|
1855
|
+
});
|
|
1856
|
+
for (const sub of initSubTimings) timings.push(sub);
|
|
1595
1857
|
setupVerified = true;
|
|
1858
|
+
t0 = performance.now();
|
|
1859
|
+
const manifest = await runtime.getManifest();
|
|
1860
|
+
timings.push({
|
|
1861
|
+
name: "manifest",
|
|
1862
|
+
dur: performance.now() - t0,
|
|
1863
|
+
desc: "Manifest"
|
|
1864
|
+
});
|
|
1865
|
+
locals.emdashManifest = manifest;
|
|
1596
1866
|
locals.emdash = {
|
|
1867
|
+
handleContentList: runtime.handleContentList.bind(runtime),
|
|
1868
|
+
handleContentGet: runtime.handleContentGet.bind(runtime),
|
|
1869
|
+
handleContentCreate: runtime.handleContentCreate.bind(runtime),
|
|
1870
|
+
handleContentUpdate: runtime.handleContentUpdate.bind(runtime),
|
|
1871
|
+
handleContentDelete: runtime.handleContentDelete.bind(runtime),
|
|
1872
|
+
handleContentListTrashed: runtime.handleContentListTrashed.bind(runtime),
|
|
1873
|
+
handleContentRestore: runtime.handleContentRestore.bind(runtime),
|
|
1874
|
+
handleContentPermanentDelete: runtime.handleContentPermanentDelete.bind(runtime),
|
|
1875
|
+
handleContentCountTrashed: runtime.handleContentCountTrashed.bind(runtime),
|
|
1876
|
+
handleContentGetIncludingTrashed: runtime.handleContentGetIncludingTrashed.bind(runtime),
|
|
1877
|
+
handleContentDuplicate: runtime.handleContentDuplicate.bind(runtime),
|
|
1878
|
+
handleContentPublish: runtime.handleContentPublish.bind(runtime),
|
|
1879
|
+
handleContentUnpublish: runtime.handleContentUnpublish.bind(runtime),
|
|
1880
|
+
handleContentSchedule: runtime.handleContentSchedule.bind(runtime),
|
|
1881
|
+
handleContentUnschedule: runtime.handleContentUnschedule.bind(runtime),
|
|
1882
|
+
handleContentCountScheduled: runtime.handleContentCountScheduled.bind(runtime),
|
|
1883
|
+
handleContentDiscardDraft: runtime.handleContentDiscardDraft.bind(runtime),
|
|
1884
|
+
handleContentCompare: runtime.handleContentCompare.bind(runtime),
|
|
1885
|
+
handleContentTranslations: runtime.handleContentTranslations.bind(runtime),
|
|
1886
|
+
handleMediaList: runtime.handleMediaList.bind(runtime),
|
|
1887
|
+
handleMediaGet: runtime.handleMediaGet.bind(runtime),
|
|
1888
|
+
handleMediaCreate: runtime.handleMediaCreate.bind(runtime),
|
|
1889
|
+
handleMediaUpdate: runtime.handleMediaUpdate.bind(runtime),
|
|
1890
|
+
handleMediaDelete: runtime.handleMediaDelete.bind(runtime),
|
|
1891
|
+
handleRevisionList: runtime.handleRevisionList.bind(runtime),
|
|
1892
|
+
handleRevisionGet: runtime.handleRevisionGet.bind(runtime),
|
|
1893
|
+
handleRevisionRestore: runtime.handleRevisionRestore.bind(runtime),
|
|
1894
|
+
handlePluginApiRoute: runtime.handlePluginApiRoute.bind(runtime),
|
|
1895
|
+
getPluginRouteMeta: runtime.getPluginRouteMeta.bind(runtime),
|
|
1896
|
+
getMediaProvider: runtime.getMediaProvider.bind(runtime),
|
|
1897
|
+
getMediaProviderList: runtime.getMediaProviderList.bind(runtime),
|
|
1597
1898
|
collectPageMetadata: runtime.collectPageMetadata.bind(runtime),
|
|
1598
|
-
collectPageFragments: runtime.collectPageFragments.bind(runtime)
|
|
1899
|
+
collectPageFragments: runtime.collectPageFragments.bind(runtime),
|
|
1900
|
+
ensureSearchHealthy: runtime.ensureSearchHealthy.bind(runtime),
|
|
1901
|
+
storage: runtime.storage,
|
|
1902
|
+
db: runtime.db,
|
|
1903
|
+
hooks: runtime.hooks,
|
|
1904
|
+
email: runtime.email,
|
|
1905
|
+
configuredPlugins: runtime.configuredPlugins,
|
|
1906
|
+
config,
|
|
1907
|
+
invalidateManifest: runtime.invalidateManifest.bind(runtime),
|
|
1908
|
+
getSandboxRunner: runtime.getSandboxRunner.bind(runtime),
|
|
1909
|
+
syncMarketplacePlugins: runtime.syncMarketplacePlugins.bind(runtime),
|
|
1910
|
+
setPluginStatus: runtime.setPluginStatus.bind(runtime)
|
|
1599
1911
|
};
|
|
1600
|
-
} catch {
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
handleContentGetIncludingTrashed: runtime.handleContentGetIncludingTrashed.bind(runtime),
|
|
1625
|
-
handleContentDuplicate: runtime.handleContentDuplicate.bind(runtime),
|
|
1626
|
-
handleContentPublish: runtime.handleContentPublish.bind(runtime),
|
|
1627
|
-
handleContentUnpublish: runtime.handleContentUnpublish.bind(runtime),
|
|
1628
|
-
handleContentSchedule: runtime.handleContentSchedule.bind(runtime),
|
|
1629
|
-
handleContentUnschedule: runtime.handleContentUnschedule.bind(runtime),
|
|
1630
|
-
handleContentCountScheduled: runtime.handleContentCountScheduled.bind(runtime),
|
|
1631
|
-
handleContentDiscardDraft: runtime.handleContentDiscardDraft.bind(runtime),
|
|
1632
|
-
handleContentCompare: runtime.handleContentCompare.bind(runtime),
|
|
1633
|
-
handleContentTranslations: runtime.handleContentTranslations.bind(runtime),
|
|
1634
|
-
handleMediaList: runtime.handleMediaList.bind(runtime),
|
|
1635
|
-
handleMediaGet: runtime.handleMediaGet.bind(runtime),
|
|
1636
|
-
handleMediaCreate: runtime.handleMediaCreate.bind(runtime),
|
|
1637
|
-
handleMediaUpdate: runtime.handleMediaUpdate.bind(runtime),
|
|
1638
|
-
handleMediaDelete: runtime.handleMediaDelete.bind(runtime),
|
|
1639
|
-
handleRevisionList: runtime.handleRevisionList.bind(runtime),
|
|
1640
|
-
handleRevisionGet: runtime.handleRevisionGet.bind(runtime),
|
|
1641
|
-
handleRevisionRestore: runtime.handleRevisionRestore.bind(runtime),
|
|
1642
|
-
handlePluginApiRoute: runtime.handlePluginApiRoute.bind(runtime),
|
|
1643
|
-
getPluginRouteMeta: runtime.getPluginRouteMeta.bind(runtime),
|
|
1644
|
-
getMediaProvider: runtime.getMediaProvider.bind(runtime),
|
|
1645
|
-
getMediaProviderList: runtime.getMediaProviderList.bind(runtime),
|
|
1646
|
-
collectPageMetadata: runtime.collectPageMetadata.bind(runtime),
|
|
1647
|
-
collectPageFragments: runtime.collectPageFragments.bind(runtime),
|
|
1648
|
-
storage: runtime.storage,
|
|
1649
|
-
db: runtime.db,
|
|
1650
|
-
hooks: runtime.hooks,
|
|
1651
|
-
email: runtime.email,
|
|
1652
|
-
configuredPlugins: runtime.configuredPlugins,
|
|
1653
|
-
config,
|
|
1654
|
-
invalidateManifest: runtime.invalidateManifest.bind(runtime),
|
|
1655
|
-
getSandboxRunner: runtime.getSandboxRunner.bind(runtime),
|
|
1656
|
-
syncMarketplacePlugins: runtime.syncMarketplacePlugins.bind(runtime),
|
|
1657
|
-
setPluginStatus: runtime.setPluginStatus.bind(runtime)
|
|
1912
|
+
} catch (error) {
|
|
1913
|
+
console.error("EmDash middleware error:", error);
|
|
1914
|
+
}
|
|
1915
|
+
const scoped = createRequestScopedDb$1({
|
|
1916
|
+
config: config?.database?.config,
|
|
1917
|
+
isAuthenticated: !!sessionUser,
|
|
1918
|
+
isWrite: request.method !== "GET" && request.method !== "HEAD",
|
|
1919
|
+
cookies: context.cookies,
|
|
1920
|
+
url
|
|
1921
|
+
});
|
|
1922
|
+
const renderAndFinalize = async () => {
|
|
1923
|
+
const t0 = performance.now();
|
|
1924
|
+
const response = await next();
|
|
1925
|
+
timings.push({
|
|
1926
|
+
name: "render",
|
|
1927
|
+
dur: performance.now() - t0,
|
|
1928
|
+
desc: "Page render"
|
|
1929
|
+
});
|
|
1930
|
+
timings.push({
|
|
1931
|
+
name: "mw",
|
|
1932
|
+
dur: performance.now() - mwStart,
|
|
1933
|
+
desc: "Total middleware"
|
|
1934
|
+
});
|
|
1935
|
+
return finalizeResponse(response, timings);
|
|
1658
1936
|
};
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
if (d1Binding && typeof d1Binding === "object" && "withSession" in d1Binding) {
|
|
1666
|
-
const isAuthenticated = context.isPrerendered ? false : !!await context.session?.get("user");
|
|
1667
|
-
const isWrite = request.method !== "GET" && request.method !== "HEAD";
|
|
1668
|
-
const configConstraint = getDefaultConstraint(dbConfig);
|
|
1669
|
-
const cookieName = getBookmarkCookieName(dbConfig);
|
|
1670
|
-
let constraint = configConstraint;
|
|
1671
|
-
if (isAuthenticated && isWrite) constraint = "first-primary";
|
|
1672
|
-
else if (isAuthenticated) {
|
|
1673
|
-
const bookmarkCookie = context.cookies.get(cookieName);
|
|
1674
|
-
if (bookmarkCookie?.value) constraint = bookmarkCookie.value;
|
|
1675
|
-
}
|
|
1676
|
-
const session = d1Binding.withSession.call(d1Binding, constraint);
|
|
1677
|
-
return runWithContext({
|
|
1937
|
+
if (scoped) {
|
|
1938
|
+
const parent = getRequestContext();
|
|
1939
|
+
return runWithContext(parent ? {
|
|
1940
|
+
...parent,
|
|
1941
|
+
db: scoped.db
|
|
1942
|
+
} : {
|
|
1678
1943
|
editMode: false,
|
|
1679
|
-
db:
|
|
1944
|
+
db: scoped.db
|
|
1680
1945
|
}, async () => {
|
|
1681
|
-
const response =
|
|
1682
|
-
|
|
1683
|
-
const newBookmark = session.getBookmark.call(session);
|
|
1684
|
-
if (newBookmark) response.headers.append("Set-Cookie", `${cookieName}=${newBookmark}; Path=/; HttpOnly; SameSite=Lax; Secure`);
|
|
1685
|
-
}
|
|
1946
|
+
const response = await renderAndFinalize();
|
|
1947
|
+
scoped.commit();
|
|
1686
1948
|
return response;
|
|
1687
1949
|
});
|
|
1688
1950
|
}
|
|
1951
|
+
return renderAndFinalize();
|
|
1952
|
+
};
|
|
1953
|
+
if (playgroundDb) {
|
|
1954
|
+
const editMode = context.cookies.get("emdash-edit-mode")?.value === "true";
|
|
1955
|
+
const parent = getRequestContext();
|
|
1956
|
+
return runWithContext(parent ? {
|
|
1957
|
+
...parent,
|
|
1958
|
+
editMode,
|
|
1959
|
+
db: playgroundDb,
|
|
1960
|
+
dbIsIsolated: true
|
|
1961
|
+
} : {
|
|
1962
|
+
editMode,
|
|
1963
|
+
db: playgroundDb,
|
|
1964
|
+
dbIsIsolated: true
|
|
1965
|
+
}, doInit);
|
|
1689
1966
|
}
|
|
1690
|
-
return
|
|
1967
|
+
return doInit();
|
|
1691
1968
|
};
|
|
1692
|
-
if (
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1969
|
+
if (queryRecorder) try {
|
|
1970
|
+
return await runWithContext({
|
|
1971
|
+
editMode: false,
|
|
1972
|
+
queryRecorder
|
|
1973
|
+
}, run);
|
|
1974
|
+
} finally {
|
|
1975
|
+
flushRecorder(queryRecorder);
|
|
1976
|
+
}
|
|
1977
|
+
return run();
|
|
1697
1978
|
});
|
|
1698
1979
|
|
|
1699
1980
|
//#endregion
|