emdash 0.7.0 → 0.9.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 (354) hide show
  1. package/dist/{adapters-Di31kZ28.d.mts → adapters-DoNJiveC.d.mts} +1 -1
  2. package/dist/{adapters-Di31kZ28.d.mts.map → adapters-DoNJiveC.d.mts.map} +1 -1
  3. package/dist/{apply-5uslYdUu.mjs → apply-BzltprvY.mjs} +90 -139
  4. package/dist/apply-BzltprvY.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 +194 -17
  8. package/dist/astro/index.mjs.map +1 -1
  9. package/dist/astro/middleware/auth.d.mts +6 -7
  10. package/dist/astro/middleware/auth.d.mts.map +1 -1
  11. package/dist/astro/middleware/auth.mjs +34 -57
  12. package/dist/astro/middleware/auth.mjs.map +1 -1
  13. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  14. package/dist/astro/middleware/redirect.mjs +17 -12
  15. package/dist/astro/middleware/redirect.mjs.map +1 -1
  16. package/dist/astro/middleware/request-context.d.mts.map +1 -1
  17. package/dist/astro/middleware/request-context.mjs +9 -6
  18. package/dist/astro/middleware/request-context.mjs.map +1 -1
  19. package/dist/astro/middleware/setup.mjs +1 -1
  20. package/dist/astro/middleware.d.mts.map +1 -1
  21. package/dist/astro/middleware.mjs +301 -165
  22. package/dist/astro/middleware.mjs.map +1 -1
  23. package/dist/astro/types.d.mts +34 -10
  24. package/dist/astro/types.d.mts.map +1 -1
  25. package/dist/{base64-MBPo9ozB.mjs → base64-BRICGH2l.mjs} +1 -1
  26. package/dist/{base64-MBPo9ozB.mjs.map → base64-BRICGH2l.mjs.map} +1 -1
  27. package/dist/{byline-C4OVd8b3.mjs → byline-BSaNL1w7.mjs} +5 -5
  28. package/dist/byline-BSaNL1w7.mjs.map +1 -0
  29. package/dist/bylines-CvJ3PYz2.mjs +113 -0
  30. package/dist/bylines-CvJ3PYz2.mjs.map +1 -0
  31. package/dist/cache-C6N_hhN7.mjs +65 -0
  32. package/dist/cache-C6N_hhN7.mjs.map +1 -0
  33. package/dist/{chunks-HGz06Soa.mjs → chunks-NBQVDOci.mjs} +8 -2
  34. package/dist/{chunks-HGz06Soa.mjs.map → chunks-NBQVDOci.mjs.map} +1 -1
  35. package/dist/cli/index.mjs +229 -31
  36. package/dist/cli/index.mjs.map +1 -1
  37. package/dist/client/cf-access.d.mts +1 -1
  38. package/dist/client/index.d.mts +1 -1
  39. package/dist/client/index.mjs +3 -3
  40. package/dist/client/index.mjs.map +1 -1
  41. package/dist/{config-BXwuX8Bx.mjs → config-BI0V3ICQ.mjs} +1 -1
  42. package/dist/{config-BXwuX8Bx.mjs.map → config-BI0V3ICQ.mjs.map} +1 -1
  43. package/dist/{content-D7J5y73J.mjs → content-8lOYF0pr.mjs} +43 -28
  44. package/dist/content-8lOYF0pr.mjs.map +1 -0
  45. package/dist/db/index.d.mts +3 -3
  46. package/dist/db/index.mjs +2 -2
  47. package/dist/db/libsql.d.mts +1 -1
  48. package/dist/db/libsql.d.mts.map +1 -1
  49. package/dist/db/libsql.mjs +7 -2
  50. package/dist/db/libsql.mjs.map +1 -1
  51. package/dist/db/postgres.d.mts +1 -1
  52. package/dist/db/sqlite.d.mts +1 -1
  53. package/dist/db/sqlite.d.mts.map +1 -1
  54. package/dist/db/sqlite.mjs +8 -3
  55. package/dist/db/sqlite.mjs.map +1 -1
  56. package/dist/{db-errors-D0UT85nC.mjs → db-errors-WRezodiz.mjs} +1 -1
  57. package/dist/{db-errors-D0UT85nC.mjs.map → db-errors-WRezodiz.mjs.map} +1 -1
  58. package/dist/{default-CME5YdZ3.mjs → default-D8ksjWhO.mjs} +1 -1
  59. package/dist/{default-CME5YdZ3.mjs.map → default-D8ksjWhO.mjs.map} +1 -1
  60. package/dist/{dialect-helpers-DhTzaUxP.mjs → dialect-helpers-BKCvISIQ.mjs} +19 -2
  61. package/dist/dialect-helpers-BKCvISIQ.mjs.map +1 -0
  62. package/dist/{error-CiYn9yDu.mjs → error-D_-tqP-I.mjs} +1 -1
  63. package/dist/error-D_-tqP-I.mjs.map +1 -0
  64. package/dist/{index-De6_Xv3v.d.mts → index-BFRaVcD6.d.mts} +243 -40
  65. package/dist/index-BFRaVcD6.d.mts.map +1 -0
  66. package/dist/index.d.mts +11 -11
  67. package/dist/index.mjs +29 -25
  68. package/dist/{load-CBcmDIot.mjs → load-DDqMMvZL.mjs} +2 -2
  69. package/dist/{load-CBcmDIot.mjs.map → load-DDqMMvZL.mjs.map} +1 -1
  70. package/dist/{loader-DeiBJEMe.mjs → loader-CKLbBnhK.mjs} +32 -10
  71. package/dist/loader-CKLbBnhK.mjs.map +1 -0
  72. package/dist/{manifest-schema-V30qsMft.mjs → manifest-schema-DqWNC3lM.mjs} +45 -3
  73. package/dist/manifest-schema-DqWNC3lM.mjs.map +1 -0
  74. package/dist/media/index.d.mts +1 -1
  75. package/dist/media/index.mjs +1 -1
  76. package/dist/media/local-runtime.d.mts +7 -7
  77. package/dist/media/local-runtime.mjs +3 -3
  78. package/dist/{media-DqHVh136.mjs → media-BW32b4gi.mjs} +4 -7
  79. package/dist/media-BW32b4gi.mjs.map +1 -0
  80. package/dist/{mode-CpNnGkPz.mjs → mode-ier8jbBk.mjs} +1 -1
  81. package/dist/mode-ier8jbBk.mjs.map +1 -0
  82. package/dist/options-BVp3UsTS.mjs +117 -0
  83. package/dist/options-BVp3UsTS.mjs.map +1 -0
  84. package/dist/page/index.d.mts +2 -2
  85. package/dist/{placeholder-tzpqGWII.d.mts → placeholder-BE4o_2dc.d.mts} +1 -1
  86. package/dist/{placeholder-tzpqGWII.d.mts.map → placeholder-BE4o_2dc.d.mts.map} +1 -1
  87. package/dist/{placeholder-C-fk5hYI.mjs → placeholder-CIJejMlK.mjs} +1 -1
  88. package/dist/placeholder-CIJejMlK.mjs.map +1 -0
  89. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  90. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
  91. package/dist/plugins/adapt-sandbox-entry.mjs +6 -5
  92. package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
  93. package/dist/public-url-DByxYjUw.mjs +51 -0
  94. package/dist/public-url-DByxYjUw.mjs.map +1 -0
  95. package/dist/{query-g4Ug-9j9.mjs → query-Cg9ZKRQ0.mjs} +114 -16
  96. package/dist/query-Cg9ZKRQ0.mjs.map +1 -0
  97. package/dist/{redirect-CN0Rt9Ob.mjs → redirect-BhUBKRc1.mjs} +13 -8
  98. package/dist/redirect-BhUBKRc1.mjs.map +1 -0
  99. package/dist/{registry-Ci3WxVAr.mjs → registry-Dw70ChxB.mjs} +69 -11
  100. package/dist/registry-Dw70ChxB.mjs.map +1 -0
  101. package/dist/{request-cache-DiR961CV.mjs → request-cache-B-bmkipQ.mjs} +1 -1
  102. package/dist/request-cache-B-bmkipQ.mjs.map +1 -0
  103. package/dist/runner-Bnoj7vjK.d.mts +44 -0
  104. package/dist/runner-Bnoj7vjK.d.mts.map +1 -0
  105. package/dist/{runner-tQ7BJ4T7.mjs → runner-C7ADox5q.mjs} +185 -55
  106. package/dist/{runner-tQ7BJ4T7.mjs.map → runner-C7ADox5q.mjs.map} +1 -1
  107. package/dist/runtime.d.mts +6 -6
  108. package/dist/runtime.mjs +4 -4
  109. package/dist/{search-B0effn3j.mjs → search-dOGEccMa.mjs} +341 -152
  110. package/dist/search-dOGEccMa.mjs.map +1 -0
  111. package/dist/secrets-CW3reAnU.mjs +314 -0
  112. package/dist/secrets-CW3reAnU.mjs.map +1 -0
  113. package/dist/seed/index.d.mts +2 -2
  114. package/dist/seed/index.mjs +15 -14
  115. package/dist/seo/index.d.mts +1 -1
  116. package/dist/storage/local.d.mts +1 -1
  117. package/dist/storage/local.mjs +1 -1
  118. package/dist/storage/s3.d.mts +1 -1
  119. package/dist/storage/s3.d.mts.map +1 -1
  120. package/dist/storage/s3.mjs +4 -4
  121. package/dist/storage/s3.mjs.map +1 -1
  122. package/dist/{taxonomies-K2z0Uhnj.mjs → taxonomies-ZlRtD6AG.mjs} +14 -7
  123. package/dist/taxonomies-ZlRtD6AG.mjs.map +1 -0
  124. package/dist/{tokens-BFPFx3CA.mjs → tokens-D7zMmWi2.mjs} +2 -2
  125. package/dist/{tokens-BFPFx3CA.mjs.map → tokens-D7zMmWi2.mjs.map} +1 -1
  126. package/dist/{transport-BykRfpyy.mjs → transport-BeMCmin1.mjs} +6 -5
  127. package/dist/{transport-BykRfpyy.mjs.map → transport-BeMCmin1.mjs.map} +1 -1
  128. package/dist/{transport-H4Iwx7tC.d.mts → transport-DNEfeMaU.d.mts} +1 -1
  129. package/dist/{transport-H4Iwx7tC.d.mts.map → transport-DNEfeMaU.d.mts.map} +1 -1
  130. package/dist/types-4fVtCIm0.mjs +68 -0
  131. package/dist/types-4fVtCIm0.mjs.map +1 -0
  132. package/dist/{types-CnZYHyLW.d.mts → types-BSyXeCFW.d.mts} +24 -2
  133. package/dist/{types-CnZYHyLW.d.mts.map → types-BSyXeCFW.d.mts.map} +1 -1
  134. package/dist/{types-DgrIP0tF.d.mts → types-BuBIptGk.d.mts} +80 -106
  135. package/dist/types-BuBIptGk.d.mts.map +1 -0
  136. package/dist/{types-BH2L167P.mjs → types-CDbKp7ND.mjs} +1 -1
  137. package/dist/{types-BH2L167P.mjs.map → types-CDbKp7ND.mjs.map} +1 -1
  138. package/dist/{types-DDS4MxsT.mjs → types-CIOg5AR8.mjs} +1 -1
  139. package/dist/{types-DDS4MxsT.mjs.map → types-CIOg5AR8.mjs.map} +1 -1
  140. package/dist/{types-6CUZRrZP.d.mts → types-CJsYGpco.d.mts} +24 -2
  141. package/dist/{types-6CUZRrZP.d.mts.map → types-CJsYGpco.d.mts.map} +1 -1
  142. package/dist/types-CRxNbK-Z.mjs +68 -0
  143. package/dist/types-CRxNbK-Z.mjs.map +1 -0
  144. package/dist/{types-C2v0c34j.d.mts → types-CrtWgIvl.d.mts} +1 -1
  145. package/dist/{types-C2v0c34j.d.mts.map → types-CrtWgIvl.d.mts.map} +1 -1
  146. package/dist/{types-CFWjXmus.d.mts → types-M78DQ1lx.d.mts} +1 -1
  147. package/dist/{types-CFWjXmus.d.mts.map → types-M78DQ1lx.d.mts.map} +1 -1
  148. package/dist/{validate-CqsNItbt.mjs → validate-Baqf0slj.mjs} +3 -3
  149. package/dist/{validate-CqsNItbt.mjs.map → validate-Baqf0slj.mjs.map} +1 -1
  150. package/dist/{validate-kM8Pjuf7.d.mts → validate-BfQh_C_y.d.mts} +4 -4
  151. package/dist/{validate-kM8Pjuf7.d.mts.map → validate-BfQh_C_y.d.mts.map} +1 -1
  152. package/dist/validation-BfEI7tNe.mjs +144 -0
  153. package/dist/validation-BfEI7tNe.mjs.map +1 -0
  154. package/dist/version-DoxrVdYf.mjs +7 -0
  155. package/dist/{version-BnTKdfam.mjs.map → version-DoxrVdYf.mjs.map} +1 -1
  156. package/dist/zod-generator-CC0xNe_K.mjs +132 -0
  157. package/dist/zod-generator-CC0xNe_K.mjs.map +1 -0
  158. package/locals.d.ts +1 -6
  159. package/package.json +21 -7
  160. package/src/api/auth-storage.ts +37 -0
  161. package/src/api/error.ts +6 -0
  162. package/src/api/errors.ts +8 -0
  163. package/src/api/handlers/comments.ts +19 -4
  164. package/src/api/handlers/content.ts +151 -4
  165. package/src/api/handlers/device-flow.ts +5 -0
  166. package/src/api/handlers/index.ts +2 -0
  167. package/src/api/handlers/marketplace.ts +11 -4
  168. package/src/api/handlers/media.ts +8 -1
  169. package/src/api/handlers/menus.ts +160 -21
  170. package/src/api/handlers/oauth-authorization.ts +72 -33
  171. package/src/api/handlers/redirects.ts +16 -3
  172. package/src/api/handlers/revision.ts +23 -14
  173. package/src/api/handlers/sections.ts +8 -1
  174. package/src/api/handlers/taxonomies.ts +131 -22
  175. package/src/api/handlers/validation.ts +212 -0
  176. package/src/api/openapi/document.ts +4 -1
  177. package/src/api/public-url.ts +54 -5
  178. package/src/api/route-utils.ts +14 -0
  179. package/src/api/schemas/comments.ts +2 -2
  180. package/src/api/schemas/common.ts +1 -1
  181. package/src/api/schemas/content.ts +17 -0
  182. package/src/api/schemas/sections.ts +3 -3
  183. package/src/api/schemas/setup.ts +8 -0
  184. package/src/api/schemas/users.ts +1 -1
  185. package/src/api/schemas/widgets.ts +12 -10
  186. package/src/api/setup-complete.ts +40 -0
  187. package/src/api/types.ts +5 -1
  188. package/src/astro/integration/index.ts +30 -2
  189. package/src/astro/integration/routes.ts +28 -0
  190. package/src/astro/integration/runtime.ts +49 -1
  191. package/src/astro/integration/virtual-modules.ts +73 -2
  192. package/src/astro/integration/vite-config.ts +49 -13
  193. package/src/astro/middleware/auth.ts +34 -6
  194. package/src/astro/middleware/redirect.ts +29 -16
  195. package/src/astro/middleware/request-context.ts +15 -5
  196. package/src/astro/middleware.ts +41 -10
  197. package/src/astro/routes/PluginRegistry.tsx +10 -1
  198. package/src/astro/routes/api/auth/invite/complete.ts +6 -1
  199. package/src/astro/routes/api/auth/mode.ts +57 -0
  200. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +23 -3
  201. package/src/astro/routes/api/auth/oauth/[provider].ts +10 -4
  202. package/src/astro/routes/api/auth/passkey/register/verify.ts +6 -1
  203. package/src/astro/routes/api/auth/passkey/verify.ts +6 -1
  204. package/src/astro/routes/api/auth/signup/complete.ts +6 -1
  205. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +2 -2
  206. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +4 -2
  207. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +34 -12
  208. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +32 -2
  209. package/src/astro/routes/api/content/[collection]/[id]/restore.ts +4 -2
  210. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +3 -2
  211. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +8 -4
  212. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +1 -1
  213. package/src/astro/routes/api/content/[collection]/[id].ts +12 -0
  214. package/src/astro/routes/api/content/[collection]/index.ts +1 -9
  215. package/src/astro/routes/api/import/wordpress/execute.ts +3 -1
  216. package/src/astro/routes/api/import/wordpress/media.ts +2 -7
  217. package/src/astro/routes/api/import/wordpress/prepare.ts +9 -0
  218. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +3 -1
  219. package/src/astro/routes/api/manifest.ts +62 -45
  220. package/src/astro/routes/api/media/[id]/confirm.ts +10 -1
  221. package/src/astro/routes/api/media/providers/[providerId]/index.ts +12 -3
  222. package/src/astro/routes/api/openapi.json.ts +27 -10
  223. package/src/astro/routes/api/redirects/404s/index.ts +10 -4
  224. package/src/astro/routes/api/redirects/404s/summary.ts +4 -2
  225. package/src/astro/routes/api/redirects/[id].ts +10 -4
  226. package/src/astro/routes/api/redirects/index.ts +7 -3
  227. package/src/astro/routes/api/revisions/[revisionId]/index.ts +1 -1
  228. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +0 -2
  229. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +0 -1
  230. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +0 -1
  231. package/src/astro/routes/api/schema/collections/[slug]/index.ts +2 -2
  232. package/src/astro/routes/api/schema/collections/index.ts +1 -1
  233. package/src/astro/routes/api/search/index.ts +10 -2
  234. package/src/astro/routes/api/sections/[slug].ts +10 -4
  235. package/src/astro/routes/api/sections/index.ts +7 -3
  236. package/src/astro/routes/api/settings/email.ts +4 -9
  237. package/src/astro/routes/api/setup/admin-verify.ts +6 -1
  238. package/src/astro/routes/api/setup/admin.ts +8 -2
  239. package/src/astro/routes/api/setup/index.ts +2 -2
  240. package/src/astro/routes/api/setup/status.ts +3 -1
  241. package/src/astro/routes/api/snapshot.ts +44 -18
  242. package/src/astro/routes/api/taxonomies/index.ts +0 -1
  243. package/src/astro/routes/api/themes/preview.ts +11 -5
  244. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +4 -1
  245. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +4 -1
  246. package/src/astro/routes/api/widget-areas/[name].ts +4 -1
  247. package/src/astro/routes/api/widget-areas/index.ts +4 -1
  248. package/src/astro/types.ts +32 -3
  249. package/src/auth/allowed-origins.ts +168 -0
  250. package/src/auth/mode.ts +15 -3
  251. package/src/auth/passkey-config.ts +35 -13
  252. package/src/auth/providers/github-admin.tsx +29 -0
  253. package/src/auth/providers/github.ts +31 -0
  254. package/src/auth/providers/google-admin.tsx +44 -0
  255. package/src/auth/providers/google.ts +31 -0
  256. package/src/auth/types.ts +114 -4
  257. package/src/bylines/index.ts +37 -88
  258. package/src/cli/commands/auth.ts +28 -6
  259. package/src/cli/commands/bundle-utils.ts +11 -2
  260. package/src/cli/commands/bundle.ts +31 -9
  261. package/src/cli/commands/content.ts +13 -0
  262. package/src/cli/commands/login.ts +8 -1
  263. package/src/cli/commands/publish.ts +24 -0
  264. package/src/cli/commands/secrets.ts +183 -0
  265. package/src/cli/credentials.ts +1 -1
  266. package/src/cli/index.ts +5 -1
  267. package/src/client/index.ts +4 -4
  268. package/src/client/transport.ts +17 -7
  269. package/src/components/Break.astro +2 -2
  270. package/src/components/EmDashHead.astro +18 -13
  271. package/src/components/EmDashImage.astro +7 -6
  272. package/src/components/Embed.astro +1 -1
  273. package/src/components/Gallery.astro +6 -4
  274. package/src/components/Image.astro +9 -4
  275. package/src/components/InlinePortableTextEditor.tsx +106 -19
  276. package/src/components/LiveSearch.astro +5 -14
  277. package/src/config/secrets.ts +528 -0
  278. package/src/database/dialect-helpers.ts +50 -0
  279. package/src/database/migrations/034_published_at_index.ts +1 -1
  280. package/src/database/migrations/035_bounded_404_log.ts +56 -39
  281. package/src/database/migrations/runner.ts +156 -23
  282. package/src/database/repositories/audit.ts +6 -8
  283. package/src/database/repositories/byline.ts +6 -8
  284. package/src/database/repositories/comment.ts +12 -16
  285. package/src/database/repositories/content.ts +76 -52
  286. package/src/database/repositories/index.ts +1 -1
  287. package/src/database/repositories/media.ts +10 -13
  288. package/src/database/repositories/plugin-storage.ts +4 -6
  289. package/src/database/repositories/redirect.ts +26 -19
  290. package/src/database/repositories/taxonomy.ts +40 -3
  291. package/src/database/repositories/types.ts +57 -8
  292. package/src/database/repositories/user.ts +6 -8
  293. package/src/db/libsql.ts +1 -3
  294. package/src/db/sqlite.ts +2 -5
  295. package/src/emdash-runtime.ts +388 -247
  296. package/src/index.ts +14 -1
  297. package/src/loader.ts +30 -6
  298. package/src/mcp/server.ts +781 -141
  299. package/src/media/normalize.ts +1 -1
  300. package/src/media/url.ts +78 -0
  301. package/src/page/site-identity.ts +58 -0
  302. package/src/plugins/adapt-sandbox-entry.ts +22 -10
  303. package/src/plugins/context.ts +13 -10
  304. package/src/plugins/define-plugin.ts +40 -12
  305. package/src/plugins/email-console.ts +10 -3
  306. package/src/plugins/hooks.ts +34 -19
  307. package/src/plugins/index.ts +9 -0
  308. package/src/plugins/manifest-schema.ts +49 -2
  309. package/src/plugins/types.ts +174 -13
  310. package/src/preview/urls.ts +23 -3
  311. package/src/query.ts +149 -6
  312. package/src/redirects/cache.ts +38 -18
  313. package/src/request-cache.ts +3 -0
  314. package/src/schema/registry.ts +97 -5
  315. package/src/schema/zod-generator.ts +27 -5
  316. package/src/search/fts-manager.ts +0 -2
  317. package/src/search/query.ts +111 -26
  318. package/src/search/types.ts +8 -1
  319. package/src/sections/index.ts +7 -9
  320. package/src/seed/apply.ts +2 -0
  321. package/src/settings/index.ts +80 -6
  322. package/src/settings/types.ts +23 -1
  323. package/src/storage/s3.ts +12 -6
  324. package/src/taxonomies/index.ts +11 -1
  325. package/src/virtual-modules.d.ts +21 -1
  326. package/src/widgets/index.ts +1 -1
  327. package/dist/apply-5uslYdUu.mjs.map +0 -1
  328. package/dist/byline-C4OVd8b3.mjs.map +0 -1
  329. package/dist/bylines-hPTW79hw.mjs +0 -157
  330. package/dist/bylines-hPTW79hw.mjs.map +0 -1
  331. package/dist/cache-BkKBuIvS.mjs +0 -56
  332. package/dist/cache-BkKBuIvS.mjs.map +0 -1
  333. package/dist/chunk-ClPoSABd.mjs +0 -21
  334. package/dist/content-D7J5y73J.mjs.map +0 -1
  335. package/dist/dialect-helpers-DhTzaUxP.mjs.map +0 -1
  336. package/dist/error-CiYn9yDu.mjs.map +0 -1
  337. package/dist/index-De6_Xv3v.d.mts.map +0 -1
  338. package/dist/loader-DeiBJEMe.mjs.map +0 -1
  339. package/dist/manifest-schema-V30qsMft.mjs.map +0 -1
  340. package/dist/media-DqHVh136.mjs.map +0 -1
  341. package/dist/mode-CpNnGkPz.mjs.map +0 -1
  342. package/dist/placeholder-C-fk5hYI.mjs.map +0 -1
  343. package/dist/query-g4Ug-9j9.mjs.map +0 -1
  344. package/dist/redirect-CN0Rt9Ob.mjs.map +0 -1
  345. package/dist/registry-Ci3WxVAr.mjs.map +0 -1
  346. package/dist/request-cache-DiR961CV.mjs.map +0 -1
  347. package/dist/runner-BR2xKwhn.d.mts +0 -34
  348. package/dist/runner-BR2xKwhn.d.mts.map +0 -1
  349. package/dist/search-B0effn3j.mjs.map +0 -1
  350. package/dist/taxonomies-K2z0Uhnj.mjs.map +0 -1
  351. package/dist/types-CMMN0pNg.mjs +0 -31
  352. package/dist/types-CMMN0pNg.mjs.map +0 -1
  353. package/dist/types-DgrIP0tF.d.mts.map +0 -1
  354. package/dist/version-BnTKdfam.mjs +0 -7
@@ -4,6 +4,7 @@
4
4
  * GET /_emdash/api/search?q=query&collections=posts,pages&limit=20
5
5
  */
6
6
 
7
+ import { hasPermission } from "@emdash-cms/auth";
7
8
  import type { APIRoute } from "astro";
8
9
 
9
10
  import { apiError, apiSuccess, handleError } from "#api/error.js";
@@ -23,7 +24,7 @@ export const prerender = false;
23
24
  * - limit: Maximum results (optional, defaults to 20)
24
25
  */
25
26
  export const GET: APIRoute = async ({ url, locals }) => {
26
- const { emdash } = locals;
27
+ const { emdash, user } = locals;
27
28
 
28
29
  if (!emdash?.db) {
29
30
  return apiError("NOT_CONFIGURED", "EmDash not configured", 500);
@@ -36,6 +37,13 @@ export const GET: APIRoute = async ({ url, locals }) => {
36
37
  ? query.collections.split(",").map((c: string) => c.trim())
37
38
  : undefined;
38
39
 
40
+ // Only users with content:read_drafts may search non-published statuses.
41
+ // Anonymous and subscriber requests are forced to "published".
42
+ const status =
43
+ query.status && query.status !== "published" && hasPermission(user, "content:read_drafts")
44
+ ? query.status
45
+ : "published";
46
+
39
47
  try {
40
48
  // Verify FTS indexes are healthy on first use. At most once per worker
41
49
  // lifetime; no-op after that. Moved off the cold-start hot path to
@@ -44,7 +52,7 @@ export const GET: APIRoute = async ({ url, locals }) => {
44
52
 
45
53
  const result = await searchWithDb(emdash.db, query.q, {
46
54
  collections,
47
- status: query.status,
55
+ status,
48
56
  locale: query.locale,
49
57
  limit: query.limit,
50
58
  });
@@ -9,7 +9,7 @@
9
9
  import type { APIRoute } from "astro";
10
10
 
11
11
  import { requirePerm } from "#api/authorize.js";
12
- import { apiError, handleError, unwrapResult } from "#api/error.js";
12
+ import { apiError, handleError, requireDb, unwrapResult } from "#api/error.js";
13
13
  import {
14
14
  handleSectionDelete,
15
15
  handleSectionGet,
@@ -22,7 +22,9 @@ export const prerender = false;
22
22
 
23
23
  export const GET: APIRoute = async ({ params, locals }) => {
24
24
  const { emdash, user } = locals;
25
- const db = emdash.db;
25
+ const dbErr = requireDb(emdash?.db);
26
+ if (dbErr) return dbErr;
27
+ const db = emdash!.db;
26
28
  const { slug } = params;
27
29
 
28
30
  const denied = requirePerm(user, "sections:read");
@@ -42,7 +44,9 @@ export const GET: APIRoute = async ({ params, locals }) => {
42
44
 
43
45
  export const PUT: APIRoute = async ({ params, request, locals }) => {
44
46
  const { emdash, user } = locals;
45
- const db = emdash.db;
47
+ const dbErr = requireDb(emdash?.db);
48
+ if (dbErr) return dbErr;
49
+ const db = emdash!.db;
46
50
  const { slug } = params;
47
51
 
48
52
  const denied = requirePerm(user, "sections:manage");
@@ -65,7 +69,9 @@ export const PUT: APIRoute = async ({ params, request, locals }) => {
65
69
 
66
70
  export const DELETE: APIRoute = async ({ params, locals }) => {
67
71
  const { emdash, user } = locals;
68
- const db = emdash.db;
72
+ const dbErr = requireDb(emdash?.db);
73
+ if (dbErr) return dbErr;
74
+ const db = emdash!.db;
69
75
  const { slug } = params;
70
76
 
71
77
  const denied = requirePerm(user, "sections:manage");
@@ -8,7 +8,7 @@
8
8
  import type { APIRoute } from "astro";
9
9
 
10
10
  import { requirePerm } from "#api/authorize.js";
11
- import { handleError, unwrapResult } from "#api/error.js";
11
+ import { handleError, requireDb, unwrapResult } from "#api/error.js";
12
12
  import { handleSectionCreate, handleSectionList } from "#api/handlers/sections.js";
13
13
  import { isParseError, parseBody, parseQuery } from "#api/parse.js";
14
14
  import { createSectionBody, sectionsListQuery } from "#api/schemas.js";
@@ -17,7 +17,9 @@ export const prerender = false;
17
17
 
18
18
  export const GET: APIRoute = async ({ url, locals }) => {
19
19
  const { emdash, user } = locals;
20
- const db = emdash.db;
20
+ const dbErr = requireDb(emdash?.db);
21
+ if (dbErr) return dbErr;
22
+ const db = emdash!.db;
21
23
 
22
24
  const denied = requirePerm(user, "sections:read");
23
25
  if (denied) return denied;
@@ -35,7 +37,9 @@ export const GET: APIRoute = async ({ url, locals }) => {
35
37
 
36
38
  export const POST: APIRoute = async ({ request, locals }) => {
37
39
  const { emdash, user } = locals;
38
- const db = emdash.db;
40
+ const dbErr = requireDb(emdash?.db);
41
+ if (dbErr) return dbErr;
42
+ const db = emdash!.db;
39
43
 
40
44
  const denied = requirePerm(user, "sections:manage");
41
45
  if (denied) return denied;
@@ -49,20 +49,15 @@ export const GET: APIRoute = async ({ locals }) => {
49
49
  `emdash:exclusive_hook:${EMAIL_DELIVER_HOOK}`,
50
50
  );
51
51
 
52
- // Get middleware hooks (beforeSend / afterSend)
52
+ // Get middleware hooks (beforeSend / afterSend). These are non-exclusive —
53
+ // many plugins can subscribe — so we enumerate non-exclusive providers.
53
54
  const beforeSendPlugins = pipeline
54
- .getExclusiveHookProviders(EMAIL_BEFORE_SEND_HOOK)
55
+ .getHookProviders(EMAIL_BEFORE_SEND_HOOK)
55
56
  .map((p) => p.pluginId);
56
57
  const afterSendPlugins = pipeline
57
- .getExclusiveHookProviders(EMAIL_AFTER_SEND_HOOK)
58
+ .getHookProviders(EMAIL_AFTER_SEND_HOOK)
58
59
  .map((p) => p.pluginId);
59
60
 
60
- // Note: beforeSend/afterSend are NOT exclusive hooks, but getExclusiveHookProviders
61
- // only finds exclusive ones. We need all hooks for those names.
62
- // For now, report what we can from the exclusive hook system.
63
- // Middleware is non-exclusive so we'd need a different query.
64
- // TODO: Add getHookProviders() for non-exclusive hooks to the pipeline.
65
-
66
61
  return apiSuccess({
67
62
  available: emdash.email?.isAvailable() ?? false,
68
63
  providers: providers.map((p) => ({
@@ -16,6 +16,7 @@ import { apiError, apiSuccess, handleError } from "#api/error.js";
16
16
  import { isParseError, parseBody } from "#api/parse.js";
17
17
  import { getPublicOrigin } from "#api/public-url.js";
18
18
  import { setupAdminVerifyBody } from "#api/schemas.js";
19
+ import { getConfiguredAllowedOrigins, validateAllowedOrigins } from "#auth/allowed-origins.js";
19
20
  import { createChallengeStore } from "#auth/challenge-store.js";
20
21
  import { getPasskeyConfig } from "#auth/passkey-config.js";
21
22
  import { SETUP_NONCE_COOKIE } from "#auth/setup-nonce.js";
@@ -83,7 +84,11 @@ export const POST: APIRoute = async ({ cookies, request, locals }) => {
83
84
  const url = new URL(request.url);
84
85
  const siteName = (await options.get<string>("emdash:site_title")) ?? undefined;
85
86
  const siteUrl = getPublicOrigin(url, emdash?.config);
86
- const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl);
87
+ const allowedOrigins = validateAllowedOrigins(
88
+ siteUrl,
89
+ getConfiguredAllowedOrigins(emdash?.config),
90
+ );
91
+ const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl, allowedOrigins);
87
92
 
88
93
  // Verify the registration response
89
94
  const challengeStore = createChallengeStore(emdash.db);
@@ -49,6 +49,10 @@ export const POST: APIRoute = async ({ cookies, request, locals }) => {
49
49
  const body = await parseBody(request, setupAdminBody);
50
50
  if (isParseError(body)) return body;
51
51
 
52
+ // Preserve title/tagline from step 1 by reading existing setup state
53
+ // before we overwrite it below.
54
+ const existingState = await options.get<Record<string, unknown>>("emdash:setup_state");
55
+
52
56
  // Mint a fresh session nonce. This binds the follow-up
53
57
  // /setup/admin/verify call to the same browser that made this
54
58
  // request, so an unauthenticated attacker on another host cannot
@@ -81,9 +85,11 @@ export const POST: APIRoute = async ({ cookies, request, locals }) => {
81
85
  challengeStore,
82
86
  );
83
87
 
84
- // Store the nonce alongside the rest of the setup state. The verify
85
- // endpoint will constant-time compare this with the incoming cookie.
88
+ // Store the nonce alongside the rest of the setup state, preserving
89
+ // title/tagline from step 1. The verify endpoint will constant-time
90
+ // compare the nonce with the incoming cookie.
86
91
  await options.set("emdash:setup_state", {
92
+ ...existingState,
87
93
  step: "admin",
88
94
  email: body.email.toLowerCase(),
89
95
  name: body.name || null,
@@ -81,7 +81,7 @@ export const POST: APIRoute = async ({ request, url, locals }) => {
81
81
 
82
82
  // 5. Store setup state
83
83
  // In external auth mode, mark setup complete immediately (first user to login becomes admin)
84
- // In passkey mode, setup_complete is set after admin user is created
84
+ // Otherwise, setup_complete is set after admin user is created (passkey or auth provider)
85
85
  const authMode = getAuthMode(emdash.config);
86
86
  const useExternalAuth = authMode.type === "external";
87
87
 
@@ -105,7 +105,7 @@ export const POST: APIRoute = async ({ request, url, locals }) => {
105
105
  await options.set("emdash:site_tagline", body.tagline);
106
106
  }
107
107
  } else {
108
- // Passkey mode: store state for next step (admin creation)
108
+ // Passkey/provider mode: store state for next step (admin creation)
109
109
  await options.set("emdash:setup_state", {
110
110
  step: "site_complete",
111
111
  title: body.title,
@@ -91,7 +91,7 @@ export const GET: APIRoute = async ({ locals }) => {
91
91
  const authMode = getAuthMode(emdash.config);
92
92
  const useExternalAuth = authMode.type === "external";
93
93
 
94
- // In external auth mode, setup is complete if flag is set (no users required initially)
94
+ // In external auth mode (not atproto), setup is complete if flag is set (no users required initially)
95
95
  if (useExternalAuth && isComplete) {
96
96
  return apiSuccess({
97
97
  needsSetup: false,
@@ -106,6 +106,8 @@ export const GET: APIRoute = async ({ locals }) => {
106
106
  description: seed.meta?.description || "",
107
107
  collections: seed.collections?.length || 0,
108
108
  hasContent: !!(seed.content && Object.keys(seed.content).length > 0),
109
+ title: seed.settings?.title,
110
+ tagline: seed.settings?.tagline,
109
111
  }
110
112
  : null;
111
113
 
@@ -7,6 +7,7 @@
7
7
  * - Excludes auth/user/session/token tables
8
8
  */
9
9
 
10
+ import type { User } from "@emdash-cms/auth";
10
11
  import type { APIRoute } from "astro";
11
12
 
12
13
  import { requirePerm } from "#api/authorize.js";
@@ -17,11 +18,31 @@ import {
17
18
  verifyPreviewSignature,
18
19
  } from "#api/handlers/snapshot.js";
19
20
  import { getPublicOrigin } from "#api/public-url.js";
21
+ import { resolveSecretsCached } from "#config/secrets.js";
20
22
 
21
23
  export const prerender = false;
22
24
 
23
- export const GET: APIRoute = async ({ request, locals, url }) => {
24
- const { emdash, user } = locals;
25
+ export const GET: APIRoute = async ({ request, locals, url, session }) => {
26
+ const { emdash } = locals;
27
+ // This route is in PUBLIC_API_EXACT (for preview-signature callers with no session),
28
+ // so auth middleware skips user resolution. Manually resolve the session user here
29
+ // to support session-authenticated admin users alongside preview-signature auth.
30
+ let user: User | undefined = (locals as { user?: User }).user;
31
+ if (!user && session && emdash?.db) {
32
+ try {
33
+ const { createKyselyAdapter } = await import("@emdash-cms/auth/adapters/kysely");
34
+ const sessionUser = await session.get("user");
35
+ if (sessionUser?.id) {
36
+ const adapter = createKyselyAdapter(emdash.db);
37
+ const resolved = await adapter.getUserById(sessionUser.id);
38
+ if (resolved && !resolved.disabled) {
39
+ user = resolved;
40
+ }
41
+ }
42
+ } catch {
43
+ // Session resolution failed, continue to preview-signature check
44
+ }
45
+ }
25
46
 
26
47
  if (!emdash?.db) {
27
48
  return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
@@ -32,24 +53,29 @@ export const GET: APIRoute = async ({ request, locals, url }) => {
32
53
  let authorized = false;
33
54
 
34
55
  if (previewSig) {
35
- const secret = import.meta.env.EMDASH_PREVIEW_SECRET || import.meta.env.PREVIEW_SECRET || "";
36
- if (!secret) {
37
- console.warn(
38
- "[snapshot] X-Preview-Signature header present but no PREVIEW_SECRET configured",
39
- );
56
+ // Resolves env override or DB-stored value. Always non-empty after
57
+ // resolution, so the signature path is never silently disabled.
58
+ // Note: a signing process without access to this database (e.g. a
59
+ // remote preview Worker) must set the same `EMDASH_PREVIEW_SECRET`
60
+ // env var on both sides.
61
+ const { previewSecret: secret, previewSecretSource } = await resolveSecretsCached(emdash.db);
62
+ const parsed = parsePreviewSignatureHeader(previewSig);
63
+ if (!parsed) {
64
+ console.warn("[snapshot] Failed to parse X-Preview-Signature header");
40
65
  } else {
41
- const parsed = parsePreviewSignatureHeader(previewSig);
42
- if (!parsed) {
43
- console.warn("[snapshot] Failed to parse X-Preview-Signature header");
44
- } else {
45
- authorized = await verifyPreviewSignature(parsed.source, parsed.exp, parsed.sig, secret);
46
- if (!authorized) {
47
- console.warn("[snapshot] Preview signature verification failed", {
48
- source: parsed.source,
49
- exp: parsed.exp,
50
- expired: parsed.exp < Date.now() / 1000,
51
- });
66
+ authorized = await verifyPreviewSignature(parsed.source, parsed.exp, parsed.sig, secret);
67
+ if (!authorized) {
68
+ const fields: Record<string, unknown> = {
69
+ source: parsed.source,
70
+ exp: parsed.exp,
71
+ expired: parsed.exp < Date.now() / 1000,
72
+ secretSource: previewSecretSource,
73
+ };
74
+ if (previewSecretSource === "db") {
75
+ fields.hint =
76
+ "Set EMDASH_PREVIEW_SECRET in both this process and the signing process to share secrets across deployments";
52
77
  }
78
+ console.warn("[snapshot] Preview signature verification failed", fields);
53
79
  }
54
80
  }
55
81
  }
@@ -52,7 +52,6 @@ export const POST: APIRoute = async ({ request, locals }) => {
52
52
  if (isParseError(body)) return body;
53
53
 
54
54
  const result = await handleTaxonomyCreate(emdash.db, body);
55
- if (result.success) emdash.invalidateManifest();
56
55
  return unwrapResult(result, 201);
57
56
  } catch (error) {
58
57
  return handleError(error, "Failed to create taxonomy", "TAXONOMY_CREATE_ERROR");
@@ -4,7 +4,13 @@
4
4
  * POST /_emdash/api/themes/preview
5
5
  *
6
6
  * Generates a signed preview URL for the "Try with my data" feature.
7
- * The PREVIEW_SECRET must be set in the environment (shared with preview Workers).
7
+ *
8
+ * Uses the resolved preview secret: env override (`EMDASH_PREVIEW_SECRET`)
9
+ * wins, otherwise an auto-generated stable per-site value persisted in the
10
+ * options table is used. Processes that share the same database converge on
11
+ * the same auto-generated value; only set `EMDASH_PREVIEW_SECRET` in both
12
+ * processes when the verifying side runs without access to the EmDash DB
13
+ * (e.g. a remote preview Worker).
8
14
  */
9
15
 
10
16
  import type { APIRoute } from "astro";
@@ -12,6 +18,7 @@ import type { APIRoute } from "astro";
12
18
  import { requirePerm } from "#api/authorize.js";
13
19
  import { apiError, apiSuccess } from "#api/error.js";
14
20
  import { getPublicOrigin } from "#api/public-url.js";
21
+ import { resolveSecretsCached } from "#config/secrets.js";
15
22
 
16
23
  export const prerender = false;
17
24
 
@@ -25,10 +32,9 @@ export const POST: APIRoute = async ({ request, url, locals }) => {
25
32
  const denied = requirePerm(user, "plugins:read");
26
33
  if (denied) return denied;
27
34
 
28
- const secret = import.meta.env.EMDASH_PREVIEW_SECRET || import.meta.env.PREVIEW_SECRET || "";
29
- if (!secret) {
30
- return apiError("NOT_CONFIGURED", "PREVIEW_SECRET is not configured", 500);
31
- }
35
+ // Always non-empty after resolution; env override wins, otherwise a
36
+ // stable DB-stored value is used.
37
+ const { previewSecret: secret } = await resolveSecretsCached(emdash.db);
32
38
 
33
39
  let body: { previewUrl: string };
34
40
  try {
@@ -11,6 +11,8 @@ import { requirePerm } from "#api/authorize.js";
11
11
  import { apiError, apiSuccess, handleError } from "#api/error.js";
12
12
  import { isParseError, parseBody } from "#api/parse.js";
13
13
  import { updateWidgetBody } from "#api/schemas.js";
14
+ import { rowToWidget } from "#widgets/index.js";
15
+ import type { WidgetRow } from "#widgets/types.js";
14
16
 
15
17
  export const prerender = false;
16
18
 
@@ -73,10 +75,11 @@ export const PUT: APIRoute = async ({ params, request, locals }) => {
73
75
  const widget = await db
74
76
  .selectFrom("_emdash_widgets")
75
77
  .selectAll()
78
+ .$castTo<WidgetRow>()
76
79
  .where("id", "=", id)
77
80
  .executeTakeFirstOrThrow();
78
81
 
79
- return apiSuccess(widget);
82
+ return apiSuccess(rowToWidget(widget));
80
83
  } catch (error) {
81
84
  return handleError(error, "Failed to update widget", "WIDGET_UPDATE_ERROR");
82
85
  }
@@ -11,6 +11,8 @@ import { requirePerm } from "#api/authorize.js";
11
11
  import { apiError, apiSuccess, handleError } from "#api/error.js";
12
12
  import { isParseError, parseBody } from "#api/parse.js";
13
13
  import { createWidgetBody } from "#api/schemas.js";
14
+ import { rowToWidget } from "#widgets/index.js";
15
+ import type { WidgetRow } from "#widgets/types.js";
14
16
 
15
17
  export const prerender = false;
16
18
 
@@ -70,10 +72,11 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
70
72
  const widget = await db
71
73
  .selectFrom("_emdash_widgets")
72
74
  .selectAll()
75
+ .$castTo<WidgetRow>()
73
76
  .where("id", "=", id)
74
77
  .executeTakeFirstOrThrow();
75
78
 
76
- return apiSuccess(widget, 201);
79
+ return apiSuccess(rowToWidget(widget), 201);
77
80
  } catch (error) {
78
81
  return handleError(error, "Failed to create widget", "WIDGET_CREATE_ERROR");
79
82
  }
@@ -9,6 +9,8 @@ import type { APIRoute } from "astro";
9
9
 
10
10
  import { requirePerm } from "#api/authorize.js";
11
11
  import { apiError, apiSuccess, handleError } from "#api/error.js";
12
+ import { rowToWidget } from "#widgets/index.js";
13
+ import type { WidgetRow } from "#widgets/types.js";
12
14
 
13
15
  export const prerender = false;
14
16
 
@@ -40,13 +42,14 @@ export const GET: APIRoute = async ({ params, locals }) => {
40
42
  const widgets = await db
41
43
  .selectFrom("_emdash_widgets")
42
44
  .selectAll()
45
+ .$castTo<WidgetRow>()
43
46
  .where("area_id", "=", area.id)
44
47
  .orderBy("sort_order", "asc")
45
48
  .execute();
46
49
 
47
50
  return apiSuccess({
48
51
  ...area,
49
- widgets,
52
+ widgets: widgets.map((row) => rowToWidget(row)),
50
53
  });
51
54
  } catch (error) {
52
55
  return handleError(error, "Failed to fetch widget area", "WIDGET_AREA_GET_ERROR");
@@ -12,6 +12,8 @@ import { requirePerm } from "#api/authorize.js";
12
12
  import { apiError, apiSuccess, handleError } from "#api/error.js";
13
13
  import { isParseError, parseBody } from "#api/parse.js";
14
14
  import { createWidgetAreaBody } from "#api/schemas.js";
15
+ import { rowToWidget } from "#widgets/index.js";
16
+ import type { WidgetRow } from "#widgets/types.js";
15
17
 
16
18
  export const prerender = false;
17
19
 
@@ -35,13 +37,14 @@ export const GET: APIRoute = async ({ locals }) => {
35
37
  const widgets = await db
36
38
  .selectFrom("_emdash_widgets")
37
39
  .selectAll()
40
+ .$castTo<WidgetRow>()
38
41
  .where("area_id", "=", area.id)
39
42
  .orderBy("sort_order", "asc")
40
43
  .execute();
41
44
 
42
45
  return {
43
46
  ...area,
44
- widgets,
47
+ widgets: widgets.map((row) => rowToWidget(row)),
45
48
  widgetCount: widgets.length,
46
49
  };
47
50
  }),
@@ -228,6 +228,15 @@ export interface EmDashHandlers {
228
228
  slug?: string;
229
229
  status?: string;
230
230
  authorId?: string | null;
231
+ bylines?: Array<{ bylineId: string; roleLabel?: string | null }>;
232
+ seo?: {
233
+ title?: string | null;
234
+ description?: string | null;
235
+ image?: string | null;
236
+ canonical?: string | null;
237
+ noIndex?: boolean;
238
+ };
239
+ publishedAt?: string | null;
231
240
  _rev?: string;
232
241
  },
233
242
  ) => Promise<HandlerResponse>;
@@ -255,7 +264,11 @@ export interface EmDashHandlers {
255
264
  ) => Promise<HandlerResponse>;
256
265
 
257
266
  // Publishing & Scheduling handlers
258
- handleContentPublish: (collection: string, id: string) => Promise<HandlerResponse>;
267
+ handleContentPublish: (
268
+ collection: string,
269
+ id: string,
270
+ options?: { publishedAt?: string },
271
+ ) => Promise<HandlerResponse>;
259
272
 
260
273
  handleContentUnpublish: (collection: string, id: string) => Promise<HandlerResponse>;
261
274
 
@@ -348,6 +361,7 @@ export interface EmDashHandlers {
348
361
  // Direct access to storage and database for advanced use cases
349
362
  storage: import("../index.js").Storage | null;
350
363
  db: Kysely<import("../index.js").Database>;
364
+ getPublicMediaUrl?: (storageKey: string) => string;
351
365
 
352
366
  // Hook pipeline for plugin integrations
353
367
  hooks: import("../plugins/hooks.js").HookPipeline;
@@ -361,8 +375,15 @@ export interface EmDashHandlers {
361
375
  // Configuration (for checking database type, auth mode, etc.)
362
376
  config: import("./integration/runtime.js").EmDashConfig;
363
377
 
364
- // Manifest invalidation (call after schema changes)
365
- invalidateManifest: () => void;
378
+ // Build the admin manifest from the live database. Only used by admin
379
+ // routes; logged-out requests don't need it. Per-request, deduplicated
380
+ // by `requestCached`.
381
+ getManifest: () => Promise<EmDashManifest>;
382
+
383
+ // Clear the cached URL patterns used by `resolveEmDashPath`. Call after
384
+ // any schema mutation that creates/updates/deletes a collection's
385
+ // `urlPattern` so public routing picks up the change immediately.
386
+ invalidateUrlPatternCache: () => void;
366
387
 
367
388
  // Sandbox runner (for marketplace plugin install/update)
368
389
  getSandboxRunner: () => import("../plugins/sandbox/types.js").SandboxRunner | null;
@@ -380,4 +401,12 @@ export interface EmDashHandlers {
380
401
  collectPageFragments: (
381
402
  page: import("../plugins/types.js").PublicPageContext,
382
403
  ) => Promise<import("../plugins/types.js").PageFragmentContribution[]>;
404
+
405
+ /**
406
+ * Lazy search index health check. Search routes call this before
407
+ * querying so a crash-corrupted index gets repaired on first use
408
+ * rather than stalling cold start. Optional because it's only
409
+ * meaningful when an FTS5-capable runtime is wired in.
410
+ */
411
+ ensureSearchHealthy?: () => Promise<void>;
383
412
  }