emdash 0.4.0 → 0.6.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.
Files changed (212) hide show
  1. package/dist/{adapters-C2BzVy0p.d.mts → adapters-Di31kZ28.d.mts} +16 -1
  2. package/dist/adapters-Di31kZ28.d.mts.map +1 -0
  3. package/dist/{apply-Cma_PiF6.mjs → apply-B4MsLM-w.mjs} +27 -12
  4. package/dist/apply-B4MsLM-w.mjs.map +1 -0
  5. package/dist/astro/index.d.mts +6 -6
  6. package/dist/astro/index.d.mts.map +1 -1
  7. package/dist/astro/index.mjs +208 -34
  8. package/dist/astro/index.mjs.map +1 -1
  9. package/dist/astro/middleware/auth.d.mts +5 -5
  10. package/dist/astro/middleware/auth.d.mts.map +1 -1
  11. package/dist/astro/middleware/auth.mjs +34 -9
  12. package/dist/astro/middleware/auth.mjs.map +1 -1
  13. package/dist/astro/middleware/redirect.mjs +1 -1
  14. package/dist/astro/middleware/request-context.d.mts.map +1 -1
  15. package/dist/astro/middleware/request-context.mjs +5 -3
  16. package/dist/astro/middleware/request-context.mjs.map +1 -1
  17. package/dist/astro/middleware/setup.mjs +1 -1
  18. package/dist/astro/middleware.d.mts.map +1 -1
  19. package/dist/astro/middleware.mjs +460 -180
  20. package/dist/astro/middleware.mjs.map +1 -1
  21. package/dist/astro/types.d.mts +8 -8
  22. package/dist/{byline-WuOq9MFJ.mjs → byline-C4OVd8b3.mjs} +3 -19
  23. package/dist/byline-C4OVd8b3.mjs.map +1 -0
  24. package/dist/{bylines-C_Wsnz4L.mjs → bylines-hPTW79hw.mjs} +20 -33
  25. package/dist/bylines-hPTW79hw.mjs.map +1 -0
  26. package/dist/{cache-E3Dts-yT.mjs → cache-BkKBuIvS.mjs} +1 -1
  27. package/dist/{cache-E3Dts-yT.mjs.map → cache-BkKBuIvS.mjs.map} +1 -1
  28. package/dist/chunks-HGz06Soa.mjs +19 -0
  29. package/dist/chunks-HGz06Soa.mjs.map +1 -0
  30. package/dist/cli/index.mjs +9 -8
  31. package/dist/cli/index.mjs.map +1 -1
  32. package/dist/client/cf-access.d.mts +1 -1
  33. package/dist/client/index.d.mts +1 -1
  34. package/dist/client/index.mjs +1 -1
  35. package/dist/{config-DkxPrM9l.mjs → config-BXwuX8Bx.mjs} +1 -1
  36. package/dist/{config-DkxPrM9l.mjs.map → config-BXwuX8Bx.mjs.map} +1 -1
  37. package/dist/{connection-B4zVnQIa.mjs → connection-2igzM-AT.mjs} +19 -2
  38. package/dist/connection-2igzM-AT.mjs.map +1 -0
  39. package/dist/database/instrumentation.d.mts +45 -0
  40. package/dist/database/instrumentation.d.mts.map +1 -0
  41. package/dist/database/instrumentation.mjs +61 -0
  42. package/dist/database/instrumentation.mjs.map +1 -0
  43. package/dist/db/index.d.mts +3 -3
  44. package/dist/db/index.mjs.map +1 -1
  45. package/dist/db/libsql.d.mts +1 -1
  46. package/dist/db/postgres.d.mts +1 -1
  47. package/dist/db/sqlite.d.mts +1 -1
  48. package/dist/db-errors-D0UT85nC.mjs +41 -0
  49. package/dist/db-errors-D0UT85nC.mjs.map +1 -0
  50. package/dist/{default-PUx9RK6u.mjs → default-CME5YdZ3.mjs} +1 -1
  51. package/dist/{default-PUx9RK6u.mjs.map → default-CME5YdZ3.mjs.map} +1 -1
  52. package/dist/{error-HBeQbVhV.mjs → error-CiYn9yDu.mjs} +1 -1
  53. package/dist/{error-HBeQbVhV.mjs.map → error-CiYn9yDu.mjs.map} +1 -1
  54. package/dist/{index-CRg3PWfZ.d.mts → index-BYv0mB9g.d.mts} +135 -19
  55. package/dist/index-BYv0mB9g.d.mts.map +1 -0
  56. package/dist/index.d.mts +11 -11
  57. package/dist/index.mjs +20 -18
  58. package/dist/{load-BhSSm-TS.mjs → load-CBcmDIot.mjs} +1 -1
  59. package/dist/{load-BhSSm-TS.mjs.map → load-CBcmDIot.mjs.map} +1 -1
  60. package/dist/{loader-BYzwzORf.mjs → loader-DeiBJEMe.mjs} +18 -12
  61. package/dist/loader-DeiBJEMe.mjs.map +1 -0
  62. package/dist/{manifest-schema-BsXINkQD.mjs → manifest-schema-V30qsMft.mjs} +1 -1
  63. package/dist/{manifest-schema-BsXINkQD.mjs.map → manifest-schema-V30qsMft.mjs.map} +1 -1
  64. package/dist/media/index.d.mts +1 -1
  65. package/dist/media/index.mjs +1 -1
  66. package/dist/media/local-runtime.d.mts +7 -7
  67. package/dist/{mode-CyPLdO3C.mjs → mode-CpNnGkPz.mjs} +1 -1
  68. package/dist/{mode-CyPLdO3C.mjs.map → mode-CpNnGkPz.mjs.map} +1 -1
  69. package/dist/page/index.d.mts +11 -2
  70. package/dist/page/index.d.mts.map +1 -1
  71. package/dist/page/index.mjs +23 -1
  72. package/dist/page/index.mjs.map +1 -1
  73. package/dist/{placeholder-DntBEQo7.mjs → placeholder-C-fk5hYI.mjs} +1 -1
  74. package/dist/{placeholder-DntBEQo7.mjs.map → placeholder-C-fk5hYI.mjs.map} +1 -1
  75. package/dist/{placeholder-BBCtpTES.d.mts → placeholder-tzpqGWII.d.mts} +1 -1
  76. package/dist/{placeholder-BBCtpTES.d.mts.map → placeholder-tzpqGWII.d.mts.map} +1 -1
  77. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  78. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  79. package/dist/{query-B6Vu0d2i.mjs → query-Bk_3vKvU.mjs} +78 -11
  80. package/dist/query-Bk_3vKvU.mjs.map +1 -0
  81. package/dist/{registry-BgnP3ysR.mjs → registry-Ci3WxVAr.mjs} +133 -97
  82. package/dist/registry-Ci3WxVAr.mjs.map +1 -0
  83. package/dist/request-cache-DiR961CV.mjs +79 -0
  84. package/dist/request-cache-DiR961CV.mjs.map +1 -0
  85. package/dist/request-context.d.mts +19 -16
  86. package/dist/request-context.d.mts.map +1 -1
  87. package/dist/request-context.mjs.map +1 -1
  88. package/dist/{runner-DYv3rX8P.d.mts → runner-Fl2NcUUz.d.mts} +2 -2
  89. package/dist/{runner-DYv3rX8P.d.mts.map → runner-Fl2NcUUz.d.mts.map} +1 -1
  90. package/dist/runtime.d.mts +6 -6
  91. package/dist/runtime.mjs +1 -1
  92. package/dist/{search-B5p9D36n.mjs → search-DI4bM2w9.mjs} +110 -209
  93. package/dist/search-DI4bM2w9.mjs.map +1 -0
  94. package/dist/seed/index.d.mts +2 -2
  95. package/dist/seed/index.mjs +8 -7
  96. package/dist/seo/index.d.mts +1 -1
  97. package/dist/storage/local.d.mts +1 -1
  98. package/dist/storage/local.mjs +1 -1
  99. package/dist/storage/s3.d.mts +1 -1
  100. package/dist/storage/s3.mjs +1 -1
  101. package/dist/taxonomies-DbrKzDju.mjs +308 -0
  102. package/dist/taxonomies-DbrKzDju.mjs.map +1 -0
  103. package/dist/{tokens-DKHiCYCB.mjs → tokens-BFPFx3CA.mjs} +1 -1
  104. package/dist/{tokens-DKHiCYCB.mjs.map → tokens-BFPFx3CA.mjs.map} +1 -1
  105. package/dist/{transport-BtcQ-Z7T.mjs → transport-BykRfpyy.mjs} +1 -1
  106. package/dist/{transport-BtcQ-Z7T.mjs.map → transport-BykRfpyy.mjs.map} +1 -1
  107. package/dist/{transport-CKQA_G44.d.mts → transport-H4Iwx7tC.d.mts} +1 -1
  108. package/dist/{transport-CKQA_G44.d.mts.map → transport-H4Iwx7tC.d.mts.map} +1 -1
  109. package/dist/{types-BmkQR1En.d.mts → types-6CUZRrZP.d.mts} +1 -1
  110. package/dist/{types-BmkQR1En.d.mts.map → types-6CUZRrZP.d.mts.map} +1 -1
  111. package/dist/{types-B6BzlZxx.d.mts → types-8xrvl_68.d.mts} +1 -1
  112. package/dist/{types-B6BzlZxx.d.mts.map → types-8xrvl_68.d.mts.map} +1 -1
  113. package/dist/{types-Dz9_WMS6.mjs → types-BH2L167P.mjs} +1 -1
  114. package/dist/{types-Dz9_WMS6.mjs.map → types-BH2L167P.mjs.map} +1 -1
  115. package/dist/{types-DNZpaCBk.d.mts → types-CFWjXmus.d.mts} +1 -1
  116. package/dist/{types-DNZpaCBk.d.mts.map → types-CFWjXmus.d.mts.map} +1 -1
  117. package/dist/{types-gLYVCXCQ.d.mts → types-CnZYHyLW.d.mts} +55 -5
  118. package/dist/types-CnZYHyLW.d.mts.map +1 -0
  119. package/dist/{types-xxCWI3j0.mjs → types-DDS4MxsT.mjs} +11 -3
  120. package/dist/types-DDS4MxsT.mjs.map +1 -0
  121. package/dist/{types-BYWYxLcp.d.mts → types-DgrIP0tF.d.mts} +9 -2
  122. package/dist/types-DgrIP0tF.d.mts.map +1 -0
  123. package/dist/{validate-CcNRWH6I.d.mts → validate-CaLH1Ia2.d.mts} +5 -52
  124. package/dist/validate-CaLH1Ia2.d.mts.map +1 -0
  125. package/dist/{validate-DuZDIxfy.mjs → validate-CqsNItbt.mjs} +2 -2
  126. package/dist/{validate-DuZDIxfy.mjs.map → validate-CqsNItbt.mjs.map} +1 -1
  127. package/dist/version-Uaf2ynPX.mjs +7 -0
  128. package/dist/{version-DlTDRdpv.mjs.map → version-Uaf2ynPX.mjs.map} +1 -1
  129. package/package.json +10 -5
  130. package/src/after.ts +62 -0
  131. package/src/api/handlers/oauth-authorization.ts +2 -32
  132. package/src/api/handlers/oauth-clients.ts +40 -4
  133. package/src/api/handlers/taxonomies.ts +13 -0
  134. package/src/api/oauth/redirect-uri.ts +34 -0
  135. package/src/api/openapi/document.ts +126 -118
  136. package/src/api/schemas/auth.ts +7 -0
  137. package/src/api/schemas/media.ts +26 -15
  138. package/src/api/schemas/schema.ts +1 -0
  139. package/src/astro/integration/font-provider.ts +176 -0
  140. package/src/astro/integration/index.ts +42 -0
  141. package/src/astro/integration/routes.ts +17 -1
  142. package/src/astro/integration/runtime.ts +63 -0
  143. package/src/astro/integration/virtual-modules.ts +41 -39
  144. package/src/astro/integration/vite-config.ts +16 -5
  145. package/src/astro/middleware/auth.ts +39 -6
  146. package/src/astro/middleware/request-context.ts +15 -3
  147. package/src/astro/middleware.ts +340 -263
  148. package/src/astro/routes/admin.astro +10 -5
  149. package/src/astro/routes/api/auth/invite/register-options.ts +78 -0
  150. package/src/astro/routes/api/auth/passkey/verify.ts +5 -1
  151. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +5 -0
  152. package/src/astro/routes/api/import/wordpress/execute.ts +1 -1
  153. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +1 -1
  154. package/src/astro/routes/api/media/upload-url.ts +10 -2
  155. package/src/astro/routes/api/media.ts +10 -7
  156. package/src/astro/routes/api/oauth/register.ts +178 -0
  157. package/src/astro/routes/api/oauth/token.ts +15 -0
  158. package/src/astro/routes/api/openapi.json.ts +15 -5
  159. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +2 -0
  160. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +1 -0
  161. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +1 -0
  162. package/src/astro/routes/api/search/index.ts +5 -0
  163. package/src/astro/routes/api/search/suggest.ts +3 -0
  164. package/src/astro/routes/api/taxonomies/index.ts +1 -0
  165. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +6 -4
  166. package/src/bylines/index.ts +22 -45
  167. package/src/components/EmDashHead.astro +23 -7
  168. package/src/components/Table.astro +73 -41
  169. package/src/components/index.ts +2 -12
  170. package/src/components/marks.ts +20 -0
  171. package/src/database/connection.ts +23 -1
  172. package/src/database/instrumentation.ts +98 -0
  173. package/src/db/adapters.ts +15 -0
  174. package/src/emdash-runtime.ts +309 -91
  175. package/src/index.ts +6 -0
  176. package/src/loader.ts +19 -24
  177. package/src/menus/index.ts +6 -3
  178. package/src/page/index.ts +1 -1
  179. package/src/page/seo-contributions.ts +36 -0
  180. package/src/plugins/context.ts +1 -0
  181. package/src/plugins/email-console.ts +9 -2
  182. package/src/plugins/types.ts +8 -0
  183. package/src/query.ts +104 -7
  184. package/src/request-cache.ts +106 -0
  185. package/src/request-context.ts +19 -0
  186. package/src/schema/query.ts +5 -2
  187. package/src/schema/registry.ts +243 -166
  188. package/src/schema/types.ts +13 -2
  189. package/src/schema/zod-generator.ts +4 -0
  190. package/src/search/fts-manager.ts +19 -5
  191. package/src/search/query.ts +4 -3
  192. package/src/seed/apply.ts +15 -1
  193. package/src/settings/index.ts +24 -5
  194. package/src/taxonomies/index.ts +324 -124
  195. package/src/utils/db-errors.ts +46 -0
  196. package/src/virtual-modules.d.ts +31 -10
  197. package/src/widgets/index.ts +54 -25
  198. package/dist/adapters-C2BzVy0p.d.mts.map +0 -1
  199. package/dist/apply-Cma_PiF6.mjs.map +0 -1
  200. package/dist/byline-WuOq9MFJ.mjs.map +0 -1
  201. package/dist/bylines-C_Wsnz4L.mjs.map +0 -1
  202. package/dist/connection-B4zVnQIa.mjs.map +0 -1
  203. package/dist/index-CRg3PWfZ.d.mts.map +0 -1
  204. package/dist/loader-BYzwzORf.mjs.map +0 -1
  205. package/dist/query-B6Vu0d2i.mjs.map +0 -1
  206. package/dist/registry-BgnP3ysR.mjs.map +0 -1
  207. package/dist/search-B5p9D36n.mjs.map +0 -1
  208. package/dist/types-BYWYxLcp.d.mts.map +0 -1
  209. package/dist/types-gLYVCXCQ.d.mts.map +0 -1
  210. package/dist/types-xxCWI3j0.mjs.map +0 -1
  211. package/dist/validate-CcNRWH6I.d.mts.map +0 -1
  212. package/dist/version-DlTDRdpv.mjs +0 -7
@@ -1,32 +1,35 @@
1
- import "../connection-B4zVnQIa.mjs";
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
6
  import { r as runMigrations } from "../runner-Cd-_WyDo.mjs";
5
- import { $ as sanitizeHeadersForSandbox, At as handleContentGet, Bt as handleContentUnschedule, Ct as handleContentCompare, Dt as handleContentDelete, Et as handleContentCreate, Ft as handleContentPublish, G as DEV_CONSOLE_EMAIL_PLUGIN_ID, Ht as validateRev, It as handleContentRestore, K as devConsoleEmailDeliver, Lt as handleContentSchedule, Mt as handleContentList, Nt as handleContentListTrashed, Ot as handleContentDiscardDraft, Pt as handleContentPermanentDelete, Q as extractRequestMeta, Rt as handleContentTranslations, St as hashString, Tt as handleContentCountTrashed, Vt as handleContentUpdate, W as PluginRouteRegistry, X as resolveExclusiveHooks, Y as createHookPipeline, Z as CronExecutor, _t as handleRevisionGet, et as definePlugin, ft as handleMediaCreate, gt as handleMediaUpdate, ht as handleMediaList, jt as handleContentGetIncludingTrashed, kt as handleContentDuplicate, mt as handleMediaGet, pt as handleMediaDelete, q as EmailPipeline, st as loadBundleFromR2, ut as PluginStateRepository, vt as handleRevisionList, wt as handleContentCountScheduled, yt as handleRevisionRestore, zt as handleContentUnpublish } from "../search-B5p9D36n.mjs";
7
+ import { At as handleContentRestore, B as EmailPipeline, Ct as handleContentDuplicate, Dt as handleContentListTrashed, Et as handleContentList, Ft as handleContentUpdate, G as extractRequestMeta, H as createHookPipeline, It as validateRev, K as sanitizeHeadersForSandbox, L as PluginRouteRegistry, Mt as handleContentTranslations, Nt as handleContentUnpublish, Ot as handleContentPermanentDelete, Pt as handleContentUnschedule, R as DEV_CONSOLE_EMAIL_PLUGIN_ID, St as handleContentDiscardDraft, Tt as handleContentGetIncludingTrashed, U as resolveExclusiveHooks, W as CronExecutor, X as after, _t as handleContentCompare, bt as handleContentCreate, ct as handleMediaGet, dt as handleRevisionGet, ft as handleRevisionList, gt as hashString, it as PluginStateRepository, jt as handleContentSchedule, kt as handleContentPublish, lt as handleMediaList, ot as handleMediaCreate, pt as handleRevisionRestore, q as definePlugin, st as handleMediaDelete, tt as loadBundleFromR2, ut as handleMediaUpdate, vt as handleContentCountScheduled, wt as handleContentGet, xt as handleContentDelete, yt as handleContentCountTrashed, z as devConsoleEmailDeliver } from "../search-DI4bM2w9.mjs";
6
8
  import { r as RevisionRepository } from "../content-BsBoyj8G.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 { f as OptionsRepository } from "../apply-Cma_PiF6.mjs";
12
+ import { f as OptionsRepository } from "../apply-B4MsLM-w.mjs";
11
13
  import "../redirect-7lGhLBNZ.mjs";
12
- import "../byline-WuOq9MFJ.mjs";
13
- import { n as normalizeMediaValue } from "../placeholder-DntBEQo7.mjs";
14
- import { i as setI18nConfig } from "../config-DkxPrM9l.mjs";
15
- import { i as FTSManager, n as SchemaRegistry } from "../registry-BgnP3ysR.mjs";
16
- import { getRequestContext, runWithContext } from "../request-context.mjs";
17
- import { n as getDb } from "../loader-BYzwzORf.mjs";
18
- import { r as normalizeManifestRoute } from "../manifest-schema-BsXINkQD.mjs";
19
- import { a as invalidateUrlPatternCache } from "../query-B6Vu0d2i.mjs";
20
- import "../tokens-DKHiCYCB.mjs";
21
- import "../bylines-C_Wsnz4L.mjs";
22
- import "../load-BhSSm-TS.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-DbrKzDju.mjs";
21
+ import { r as normalizeManifestRoute } from "../manifest-schema-V30qsMft.mjs";
22
+ import { a as invalidateUrlPatternCache } from "../query-Bk_3vKvU.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-DlTDRdpv.mjs";
25
- import { t as getAuthMode } from "../mode-CyPLdO3C.mjs";
27
+ import { n as VERSION, t as COMMIT } from "../version-Uaf2ynPX.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, createSessionDialect, getBookmarkCookieName, getD1Binding, getDefaultConstraint, isSessionEnabled } from "virtual:emdash/dialect";
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 db = await EmDashRuntime.getDatabase(deps);
569
- if (isSqlite(db)) try {
570
- const repaired = await new FTSManager(db).verifyAndRepairAll();
571
- if (repaired > 0) console.log(`Repaired ${repaired} corrupted FTS index(es) at startup`);
572
- } catch {}
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
- try {
576
- const states = await db.selectFrom("_plugin_state").select(["plugin_id", "status"]).execute();
577
- pluginStates = new Map(states.map((s) => [s.plugin_id, s.status]));
578
- } catch {}
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
- try {
586
- const optionsRepo = new OptionsRepository(db);
587
- const siteName = await optionsRepo.get("emdash:site_title");
588
- const siteUrl = await optionsRepo.get("emdash:site_url");
589
- const locale = await optionsRepo.get("emdash:locale");
590
- siteInfo = {
591
- siteName: siteName ?? void 0,
592
- siteUrl: siteUrl ?? void 0,
593
- locale: locale ?? void 0
594
- };
595
- } catch {}
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
- try {
663
- cronExecutor = new CronExecutor(db, invokeCronHook);
664
- const recovered = await cronExecutor.recoverStaleLocks();
665
- if (recovered > 0) console.log(`[cron] Recovered ${recovered} stale task lock(s)`);
666
- if (typeof globalThis.navigator !== "undefined" && globalThis.navigator.userAgent === "Cloudflare-Workers") cronScheduler = new PiggybackScheduler(cronExecutor);
667
- else cronScheduler = new NodeCronScheduler(cronExecutor);
668
- cronScheduler.setSystemCleanup(async () => {
669
- try {
670
- await runSystemCleanup(db, storage ?? void 0);
671
- } catch (error) {
672
- console.error("[cleanup] System cleanup failed:", error);
673
- }
674
- });
675
- pipeline.setContextFactory({ cronReschedule: () => cronScheduler?.reschedule() });
676
- await cronScheduler.start();
677
- } catch (error) {
678
- console.warn("[cron] Failed to initialize cron system:", error);
679
- }
680
- return new EmDashRuntime(db, storage, deps.plugins, sandboxedPlugins, deps.sandboxedPluginEntries, pipeline, enabledPlugins, pluginStates, deps.config, mediaProviders, mediaProviderEntries, cronExecutor, cronScheduler, emailPipeline, allPipelinePlugins, pipelineFactoryOptions, deps, pipelineRef);
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({ dialect: deps.createDialect(dbConfig.config) });
717
- await runMigrations(db);
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-Cma_PiF6.mjs").then((n) => n.n);
729
- const { loadSeed } = await import("../load-BhSSm-TS.mjs").then((n) => n.r);
730
- const { validateSeed } = await import("../validate-DuZDIxfy.mjs").then((n) => n.n);
780
+ const { applySeed } = await import("../apply-B4MsLM-w.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
- * Build the manifest (rebuilt on each request for freshness)
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 are created, updated, or deleted.
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);
@@ -1539,18 +1686,23 @@ function buildDependencies(config) {
1539
1686
  };
1540
1687
  }
1541
1688
  /**
1542
- * Get or create the runtime instance
1689
+ * Get or create the runtime instance.
1690
+ *
1691
+ * When `initTimings` is provided, any timing samples recorded during a
1692
+ * genuine cold init are appended. Subsequent warm calls (hitting the
1693
+ * cached instance) push nothing — callers should treat an empty array
1694
+ * as "warm, nothing to report".
1543
1695
  */
1544
- async function getRuntime(config) {
1696
+ async function getRuntime(config, initTimings) {
1545
1697
  if (runtimeInstance) return runtimeInstance;
1546
1698
  if (runtimeInitializing) {
1547
1699
  await new Promise((resolve) => setTimeout(resolve, 50));
1548
- return getRuntime(config);
1700
+ return getRuntime(config, initTimings);
1549
1701
  }
1550
1702
  runtimeInitializing = true;
1551
1703
  try {
1552
1704
  const deps = buildDependencies(config);
1553
- const runtime = await EmDashRuntime.create(deps);
1705
+ const runtime = await EmDashRuntime.create(deps, initTimings);
1554
1706
  runtimeInstance = runtime;
1555
1707
  return runtime;
1556
1708
  } finally {
@@ -1558,142 +1710,270 @@ async function getRuntime(config) {
1558
1710
  }
1559
1711
  }
1560
1712
  /**
1713
+ * Astro attaches AstroCookies to outgoing responses via a well-known global
1714
+ * symbol. Cloning a Response (`new Response(body, init)`) drops non-header
1715
+ * metadata, so any middleware that wraps the response must explicitly forward
1716
+ * this symbol or `cookies.set()` calls will be silently dropped.
1717
+ */
1718
+ const ASTRO_COOKIES_SYMBOL = Symbol.for("astro.cookies");
1719
+ /**
1561
1720
  * Baseline security headers applied to all responses.
1562
1721
  * Admin routes get additional headers (strict CSP) from auth middleware.
1563
1722
  */
1564
- function setBaselineSecurityHeaders(response) {
1723
+ function finalizeResponse(response, serverTimings) {
1565
1724
  const res = new Response(response.body, response);
1725
+ const astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);
1726
+ if (astroCookies !== void 0) Reflect.set(res, ASTRO_COOKIES_SYMBOL, astroCookies);
1566
1727
  res.headers.set("X-Content-Type-Options", "nosniff");
1567
1728
  res.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
1568
1729
  res.headers.set("Permissions-Policy", "camera=(), microphone=(), geolocation=(), payment=()");
1569
1730
  if (!res.headers.has("Content-Security-Policy")) res.headers.set("X-Frame-Options", "SAMEORIGIN");
1731
+ if (serverTimings && serverTimings.length > 0) res.headers.set("Server-Timing", serverTimings.map((t) => {
1732
+ const dur = Math.round(t.dur);
1733
+ return t.desc ? `${t.name};dur=${dur};desc="${t.desc}"` : `${t.name};dur=${dur}`;
1734
+ }).join(", "));
1570
1735
  return res;
1571
1736
  }
1572
1737
  /** Public routes that require the runtime (sitemap, robots.txt, etc.) */
1573
1738
  const PUBLIC_RUNTIME_ROUTES = new Set(["/sitemap.xml", "/robots.txt"]);
1574
1739
  const SITEMAP_COLLECTION_RE = /^\/sitemap-[a-z][a-z0-9_]*\.xml$/;
1740
+ /**
1741
+ * Ask the configured database adapter for a per-request scoped Kysely. The
1742
+ * adapter encapsulates any per-request semantics (D1 sessions, read-replica
1743
+ * routing, bookmark cookies, etc.); core just forwards the cookie jar and
1744
+ * request flags and wraps next() in ALS if a scope was returned.
1745
+ */
1746
+ function createRequestScopedDb$1(opts) {
1747
+ if (typeof createRequestScopedDb !== "function") return null;
1748
+ return createRequestScopedDb(opts);
1749
+ }
1575
1750
  const onRequest = defineMiddleware(async (context, next) => {
1576
1751
  const { request, locals, cookies } = context;
1577
1752
  const url = context.url;
1578
- const isEmDashRoute = url.pathname.startsWith("/_emdash");
1579
- const isPublicRuntimeRoute = PUBLIC_RUNTIME_ROUTES.has(url.pathname) || SITEMAP_COLLECTION_RE.test(url.pathname);
1580
- const hasEditCookie = cookies.get("emdash-edit-mode")?.value === "true";
1581
- const hasPreviewToken = url.searchParams.has("_preview");
1582
- const playgroundDb = locals.__playgroundDb;
1583
- if (!isEmDashRoute && !isPublicRuntimeRoute && !hasEditCookie && !hasPreviewToken) {
1584
- if (!(context.isPrerendered ? null : await context.session?.get("user")) && !playgroundDb) {
1585
- if (!setupVerified) try {
1586
- const { getDb } = await import("../loader-BYzwzORf.mjs").then((n) => n.r);
1587
- await (await getDb()).selectFrom("_emdash_migrations").selectAll().limit(1).execute();
1588
- setupVerified = true;
1589
- } catch {
1590
- return context.redirect("/_emdash/admin/setup");
1753
+ const queryRecorder = isInstrumentationEnabled() ? createRecorder(url.pathname, request.method, request.headers.get("x-perf-phase") ?? "default") : void 0;
1754
+ const run = async () => {
1755
+ const isEmDashRoute = url.pathname.startsWith("/_emdash");
1756
+ const isPublicRuntimeRoute = PUBLIC_RUNTIME_ROUTES.has(url.pathname) || SITEMAP_COLLECTION_RE.test(url.pathname);
1757
+ const hasEditCookie = cookies.get("emdash-edit-mode")?.value === "true";
1758
+ const hasPreviewToken = url.searchParams.has("_preview");
1759
+ const playgroundDb = locals.__playgroundDb;
1760
+ const sessionUser = context.isPrerendered ? null : await context.session?.get("user");
1761
+ if (!isEmDashRoute && !isPublicRuntimeRoute && !hasEditCookie && !hasPreviewToken) {
1762
+ if (!sessionUser && !playgroundDb) {
1763
+ const timings = [];
1764
+ const mwStart = performance.now();
1765
+ if (!setupVerified) {
1766
+ const t0 = performance.now();
1767
+ try {
1768
+ const { getDb } = await import("../loader-DeiBJEMe.mjs").then((n) => n.r);
1769
+ await (await getDb()).selectFrom("_emdash_migrations").selectAll().limit(1).execute();
1770
+ setupVerified = true;
1771
+ } catch {
1772
+ return context.redirect("/_emdash/admin/setup");
1773
+ }
1774
+ timings.push({
1775
+ name: "setup",
1776
+ dur: performance.now() - t0,
1777
+ desc: "Setup probe"
1778
+ });
1779
+ }
1780
+ const config = getConfig();
1781
+ if (config) {
1782
+ const initSubTimings = [];
1783
+ const t0 = performance.now();
1784
+ try {
1785
+ const runtime = await getRuntime(config, initSubTimings);
1786
+ setupVerified = true;
1787
+ locals.emdash = {
1788
+ collectPageMetadata: runtime.collectPageMetadata.bind(runtime),
1789
+ collectPageFragments: runtime.collectPageFragments.bind(runtime)
1790
+ };
1791
+ } catch {}
1792
+ timings.push({
1793
+ name: "rt",
1794
+ dur: performance.now() - t0,
1795
+ desc: "Runtime init"
1796
+ });
1797
+ for (const sub of initSubTimings) timings.push(sub);
1798
+ }
1799
+ const anonScoped = createRequestScopedDb$1({
1800
+ config: config?.database?.config,
1801
+ isAuthenticated: false,
1802
+ isWrite: request.method !== "GET" && request.method !== "HEAD",
1803
+ cookies,
1804
+ url
1805
+ });
1806
+ const runAnon = async () => {
1807
+ const t0 = performance.now();
1808
+ const response = await next();
1809
+ timings.push({
1810
+ name: "render",
1811
+ dur: performance.now() - t0,
1812
+ desc: "Page render"
1813
+ });
1814
+ timings.push({
1815
+ name: "mw",
1816
+ dur: performance.now() - mwStart,
1817
+ desc: "Total middleware"
1818
+ });
1819
+ return finalizeResponse(response, timings);
1820
+ };
1821
+ if (anonScoped) {
1822
+ const parent = getRequestContext();
1823
+ return runWithContext(parent ? {
1824
+ ...parent,
1825
+ db: anonScoped.db
1826
+ } : {
1827
+ editMode: false,
1828
+ db: anonScoped.db
1829
+ }, async () => {
1830
+ const response = await runAnon();
1831
+ anonScoped.commit();
1832
+ return response;
1833
+ });
1834
+ }
1835
+ return runAnon();
1591
1836
  }
1592
- const config = getConfig();
1593
- if (config) try {
1594
- const runtime = await getRuntime(config);
1837
+ }
1838
+ const config = getConfig();
1839
+ if (!config) {
1840
+ console.error("EmDash: No configuration found");
1841
+ return finalizeResponse(await next());
1842
+ }
1843
+ const doInit = async () => {
1844
+ const timings = [];
1845
+ const mwStart = performance.now();
1846
+ try {
1847
+ const initSubTimings = [];
1848
+ let t0 = performance.now();
1849
+ const runtime = await getRuntime(config, initSubTimings);
1850
+ timings.push({
1851
+ name: "rt",
1852
+ dur: performance.now() - t0,
1853
+ desc: "Runtime init"
1854
+ });
1855
+ for (const sub of initSubTimings) timings.push(sub);
1595
1856
  setupVerified = true;
1857
+ t0 = performance.now();
1858
+ const manifest = await runtime.getManifest();
1859
+ timings.push({
1860
+ name: "manifest",
1861
+ dur: performance.now() - t0,
1862
+ desc: "Manifest"
1863
+ });
1864
+ locals.emdashManifest = manifest;
1596
1865
  locals.emdash = {
1866
+ handleContentList: runtime.handleContentList.bind(runtime),
1867
+ handleContentGet: runtime.handleContentGet.bind(runtime),
1868
+ handleContentCreate: runtime.handleContentCreate.bind(runtime),
1869
+ handleContentUpdate: runtime.handleContentUpdate.bind(runtime),
1870
+ handleContentDelete: runtime.handleContentDelete.bind(runtime),
1871
+ handleContentListTrashed: runtime.handleContentListTrashed.bind(runtime),
1872
+ handleContentRestore: runtime.handleContentRestore.bind(runtime),
1873
+ handleContentPermanentDelete: runtime.handleContentPermanentDelete.bind(runtime),
1874
+ handleContentCountTrashed: runtime.handleContentCountTrashed.bind(runtime),
1875
+ handleContentGetIncludingTrashed: runtime.handleContentGetIncludingTrashed.bind(runtime),
1876
+ handleContentDuplicate: runtime.handleContentDuplicate.bind(runtime),
1877
+ handleContentPublish: runtime.handleContentPublish.bind(runtime),
1878
+ handleContentUnpublish: runtime.handleContentUnpublish.bind(runtime),
1879
+ handleContentSchedule: runtime.handleContentSchedule.bind(runtime),
1880
+ handleContentUnschedule: runtime.handleContentUnschedule.bind(runtime),
1881
+ handleContentCountScheduled: runtime.handleContentCountScheduled.bind(runtime),
1882
+ handleContentDiscardDraft: runtime.handleContentDiscardDraft.bind(runtime),
1883
+ handleContentCompare: runtime.handleContentCompare.bind(runtime),
1884
+ handleContentTranslations: runtime.handleContentTranslations.bind(runtime),
1885
+ handleMediaList: runtime.handleMediaList.bind(runtime),
1886
+ handleMediaGet: runtime.handleMediaGet.bind(runtime),
1887
+ handleMediaCreate: runtime.handleMediaCreate.bind(runtime),
1888
+ handleMediaUpdate: runtime.handleMediaUpdate.bind(runtime),
1889
+ handleMediaDelete: runtime.handleMediaDelete.bind(runtime),
1890
+ handleRevisionList: runtime.handleRevisionList.bind(runtime),
1891
+ handleRevisionGet: runtime.handleRevisionGet.bind(runtime),
1892
+ handleRevisionRestore: runtime.handleRevisionRestore.bind(runtime),
1893
+ handlePluginApiRoute: runtime.handlePluginApiRoute.bind(runtime),
1894
+ getPluginRouteMeta: runtime.getPluginRouteMeta.bind(runtime),
1895
+ getMediaProvider: runtime.getMediaProvider.bind(runtime),
1896
+ getMediaProviderList: runtime.getMediaProviderList.bind(runtime),
1597
1897
  collectPageMetadata: runtime.collectPageMetadata.bind(runtime),
1598
- collectPageFragments: runtime.collectPageFragments.bind(runtime)
1898
+ collectPageFragments: runtime.collectPageFragments.bind(runtime),
1899
+ ensureSearchHealthy: runtime.ensureSearchHealthy.bind(runtime),
1900
+ storage: runtime.storage,
1901
+ db: runtime.db,
1902
+ hooks: runtime.hooks,
1903
+ email: runtime.email,
1904
+ configuredPlugins: runtime.configuredPlugins,
1905
+ config,
1906
+ invalidateManifest: runtime.invalidateManifest.bind(runtime),
1907
+ getSandboxRunner: runtime.getSandboxRunner.bind(runtime),
1908
+ syncMarketplacePlugins: runtime.syncMarketplacePlugins.bind(runtime),
1909
+ setPluginStatus: runtime.setPluginStatus.bind(runtime)
1599
1910
  };
1600
- } catch {}
1601
- return setBaselineSecurityHeaders(await next());
1602
- }
1603
- }
1604
- const config = getConfig();
1605
- if (!config) {
1606
- console.error("EmDash: No configuration found");
1607
- return next();
1608
- }
1609
- const doInit = async () => {
1610
- try {
1611
- const runtime = await getRuntime(config);
1612
- setupVerified = true;
1613
- locals.emdashManifest = await runtime.getManifest();
1614
- locals.emdash = {
1615
- handleContentList: runtime.handleContentList.bind(runtime),
1616
- handleContentGet: runtime.handleContentGet.bind(runtime),
1617
- handleContentCreate: runtime.handleContentCreate.bind(runtime),
1618
- handleContentUpdate: runtime.handleContentUpdate.bind(runtime),
1619
- handleContentDelete: runtime.handleContentDelete.bind(runtime),
1620
- handleContentListTrashed: runtime.handleContentListTrashed.bind(runtime),
1621
- handleContentRestore: runtime.handleContentRestore.bind(runtime),
1622
- handleContentPermanentDelete: runtime.handleContentPermanentDelete.bind(runtime),
1623
- handleContentCountTrashed: runtime.handleContentCountTrashed.bind(runtime),
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)
1911
+ } catch (error) {
1912
+ console.error("EmDash middleware error:", error);
1913
+ }
1914
+ const scoped = createRequestScopedDb$1({
1915
+ config: config?.database?.config,
1916
+ isAuthenticated: !!sessionUser,
1917
+ isWrite: request.method !== "GET" && request.method !== "HEAD",
1918
+ cookies: context.cookies,
1919
+ url
1920
+ });
1921
+ const renderAndFinalize = async () => {
1922
+ const t0 = performance.now();
1923
+ const response = await next();
1924
+ timings.push({
1925
+ name: "render",
1926
+ dur: performance.now() - t0,
1927
+ desc: "Page render"
1928
+ });
1929
+ timings.push({
1930
+ name: "mw",
1931
+ dur: performance.now() - mwStart,
1932
+ desc: "Total middleware"
1933
+ });
1934
+ return finalizeResponse(response, timings);
1658
1935
  };
1659
- } catch (error) {
1660
- console.error("EmDash middleware error:", error);
1661
- }
1662
- const dbConfig = config?.database?.config;
1663
- if (dbConfig && typeof isSessionEnabled === "function" && isSessionEnabled(dbConfig) && typeof getD1Binding === "function" && createSessionDialect) {
1664
- const d1Binding = getD1Binding(dbConfig);
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({
1936
+ if (scoped) {
1937
+ const parent = getRequestContext();
1938
+ return runWithContext(parent ? {
1939
+ ...parent,
1940
+ db: scoped.db
1941
+ } : {
1678
1942
  editMode: false,
1679
- db: new Kysely({ dialect: createSessionDialect(session) })
1943
+ db: scoped.db
1680
1944
  }, async () => {
1681
- const response = setBaselineSecurityHeaders(await next());
1682
- if (isAuthenticated && session && typeof session === "object" && "getBookmark" in session) {
1683
- const newBookmark = session.getBookmark.call(session);
1684
- if (newBookmark) response.headers.append("Set-Cookie", `${cookieName}=${newBookmark}; Path=/; HttpOnly; SameSite=Lax; Secure`);
1685
- }
1945
+ const response = await renderAndFinalize();
1946
+ scoped.commit();
1686
1947
  return response;
1687
1948
  });
1688
1949
  }
1950
+ return renderAndFinalize();
1951
+ };
1952
+ if (playgroundDb) {
1953
+ const editMode = context.cookies.get("emdash-edit-mode")?.value === "true";
1954
+ const parent = getRequestContext();
1955
+ return runWithContext(parent ? {
1956
+ ...parent,
1957
+ editMode,
1958
+ db: playgroundDb,
1959
+ dbIsIsolated: true
1960
+ } : {
1961
+ editMode,
1962
+ db: playgroundDb,
1963
+ dbIsIsolated: true
1964
+ }, doInit);
1689
1965
  }
1690
- return setBaselineSecurityHeaders(await next());
1966
+ return doInit();
1691
1967
  };
1692
- if (playgroundDb) return runWithContext({
1693
- editMode: context.cookies.get("emdash-edit-mode")?.value === "true",
1694
- db: playgroundDb
1695
- }, doInit);
1696
- return doInit();
1968
+ if (queryRecorder) try {
1969
+ return await runWithContext({
1970
+ editMode: false,
1971
+ queryRecorder
1972
+ }, run);
1973
+ } finally {
1974
+ flushRecorder(queryRecorder);
1975
+ }
1976
+ return run();
1697
1977
  });
1698
1978
 
1699
1979
  //#endregion