emdash 0.7.0 → 1.0.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 (225) hide show
  1. package/dist/{adapters-Di31kZ28.d.mts → adapters-BKSf3T9R.d.mts} +1 -1
  2. package/dist/{adapters-Di31kZ28.d.mts.map → adapters-BKSf3T9R.d.mts.map} +1 -1
  3. package/dist/{apply-5uslYdUu.mjs → apply-x0eMK1lX.mjs} +18 -17
  4. package/dist/apply-x0eMK1lX.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 +86 -15
  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 +22 -2
  12. package/dist/astro/middleware/auth.mjs.map +1 -1
  13. package/dist/astro/middleware/redirect.mjs +2 -2
  14. package/dist/astro/middleware/request-context.mjs +1 -1
  15. package/dist/astro/middleware/setup.mjs +1 -1
  16. package/dist/astro/middleware.d.mts.map +1 -1
  17. package/dist/astro/middleware.mjs +259 -71
  18. package/dist/astro/middleware.mjs.map +1 -1
  19. package/dist/astro/types.d.mts +16 -8
  20. package/dist/astro/types.d.mts.map +1 -1
  21. package/dist/{byline-C4OVd8b3.mjs → byline-Chbr2GoP.mjs} +3 -3
  22. package/dist/byline-Chbr2GoP.mjs.map +1 -0
  23. package/dist/{bylines-hPTW79hw.mjs → bylines-CRNsVG88.mjs} +4 -4
  24. package/dist/{bylines-hPTW79hw.mjs.map → bylines-CRNsVG88.mjs.map} +1 -1
  25. package/dist/cli/index.mjs +16 -12
  26. package/dist/cli/index.mjs.map +1 -1
  27. package/dist/client/cf-access.d.mts +1 -1
  28. package/dist/client/index.d.mts +1 -1
  29. package/dist/client/index.mjs +1 -1
  30. package/dist/{content-D7J5y73J.mjs → content-BcQPYxdV.mjs} +13 -15
  31. package/dist/content-BcQPYxdV.mjs.map +1 -0
  32. package/dist/db/index.d.mts +3 -3
  33. package/dist/db/libsql.d.mts +1 -1
  34. package/dist/db/postgres.d.mts +1 -1
  35. package/dist/db/sqlite.d.mts +1 -1
  36. package/dist/{db-errors-D0UT85nC.mjs → db-errors-l1Qh2RPR.mjs} +1 -1
  37. package/dist/{db-errors-D0UT85nC.mjs.map → db-errors-l1Qh2RPR.mjs.map} +1 -1
  38. package/dist/{default-CME5YdZ3.mjs → default-DCVqE5ib.mjs} +1 -1
  39. package/dist/{default-CME5YdZ3.mjs.map → default-DCVqE5ib.mjs.map} +1 -1
  40. package/dist/{error-CiYn9yDu.mjs → error-zG5T1UGA.mjs} +1 -1
  41. package/dist/error-zG5T1UGA.mjs.map +1 -0
  42. package/dist/{index-De6_Xv3v.d.mts → index-DIb-CzNx.d.mts} +157 -14
  43. package/dist/index-DIb-CzNx.d.mts.map +1 -0
  44. package/dist/index.d.mts +11 -11
  45. package/dist/index.mjs +22 -20
  46. package/dist/{load-CBcmDIot.mjs → load-CyEoextb.mjs} +1 -1
  47. package/dist/{load-CBcmDIot.mjs.map → load-CyEoextb.mjs.map} +1 -1
  48. package/dist/{loader-DeiBJEMe.mjs → loader-CndGj8kM.mjs} +8 -6
  49. package/dist/loader-CndGj8kM.mjs.map +1 -0
  50. package/dist/{manifest-schema-V30qsMft.mjs → manifest-schema-DH9xhc6t.mjs} +13 -1
  51. package/dist/manifest-schema-DH9xhc6t.mjs.map +1 -0
  52. package/dist/media/index.d.mts +1 -1
  53. package/dist/media/local-runtime.d.mts +7 -7
  54. package/dist/media/local-runtime.mjs +2 -2
  55. package/dist/{media-DqHVh136.mjs → media-D8FbNsl0.mjs} +4 -7
  56. package/dist/media-D8FbNsl0.mjs.map +1 -0
  57. package/dist/{mode-CpNnGkPz.mjs → mode-BnAOqItE.mjs} +1 -1
  58. package/dist/mode-BnAOqItE.mjs.map +1 -0
  59. package/dist/page/index.d.mts +2 -2
  60. package/dist/placeholder-C-fk5hYI.mjs.map +1 -1
  61. package/dist/{placeholder-tzpqGWII.d.mts → placeholder-D29tWZ7o.d.mts} +1 -1
  62. package/dist/{placeholder-tzpqGWII.d.mts.map → placeholder-D29tWZ7o.d.mts.map} +1 -1
  63. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  64. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  65. package/dist/{query-g4Ug-9j9.mjs → query-fqEdLFms.mjs} +9 -9
  66. package/dist/{query-g4Ug-9j9.mjs.map → query-fqEdLFms.mjs.map} +1 -1
  67. package/dist/{redirect-CN0Rt9Ob.mjs → redirect-D_pshWdf.mjs} +4 -4
  68. package/dist/redirect-D_pshWdf.mjs.map +1 -0
  69. package/dist/{registry-Ci3WxVAr.mjs → registry-C3Mr0ODu.mjs} +33 -9
  70. package/dist/registry-C3Mr0ODu.mjs.map +1 -0
  71. package/dist/{request-cache-DiR961CV.mjs → request-cache-Ci7f5pBb.mjs} +1 -1
  72. package/dist/request-cache-Ci7f5pBb.mjs.map +1 -0
  73. package/dist/{runner-BR2xKwhn.d.mts → runner-OURCaApa.d.mts} +2 -2
  74. package/dist/{runner-BR2xKwhn.d.mts.map → runner-OURCaApa.d.mts.map} +1 -1
  75. package/dist/runtime.d.mts +6 -6
  76. package/dist/runtime.mjs +2 -2
  77. package/dist/{search-B0effn3j.mjs → search-BoZYFuUk.mjs} +227 -84
  78. package/dist/search-BoZYFuUk.mjs.map +1 -0
  79. package/dist/seed/index.d.mts +2 -2
  80. package/dist/seed/index.mjs +12 -12
  81. package/dist/seo/index.d.mts +1 -1
  82. package/dist/storage/local.d.mts +1 -1
  83. package/dist/storage/local.mjs +1 -1
  84. package/dist/storage/s3.d.mts +1 -1
  85. package/dist/storage/s3.d.mts.map +1 -1
  86. package/dist/storage/s3.mjs +4 -4
  87. package/dist/storage/s3.mjs.map +1 -1
  88. package/dist/{taxonomies-K2z0Uhnj.mjs → taxonomies-B4IAshV8.mjs} +5 -5
  89. package/dist/{taxonomies-K2z0Uhnj.mjs.map → taxonomies-B4IAshV8.mjs.map} +1 -1
  90. package/dist/{tokens-BFPFx3CA.mjs → tokens-D9vnZqYS.mjs} +1 -1
  91. package/dist/{tokens-BFPFx3CA.mjs.map → tokens-D9vnZqYS.mjs.map} +1 -1
  92. package/dist/{transport-BykRfpyy.mjs → transport-C9ugt2Nr.mjs} +1 -1
  93. package/dist/{transport-BykRfpyy.mjs.map → transport-C9ugt2Nr.mjs.map} +1 -1
  94. package/dist/{transport-H4Iwx7tC.d.mts → transport-CUnEL3Vs.d.mts} +1 -1
  95. package/dist/{transport-H4Iwx7tC.d.mts.map → transport-CUnEL3Vs.d.mts.map} +1 -1
  96. package/dist/types-BIgulNsW.mjs +68 -0
  97. package/dist/types-BIgulNsW.mjs.map +1 -0
  98. package/dist/{types-DDS4MxsT.mjs → types-Bm1dn-q3.mjs} +1 -1
  99. package/dist/{types-DDS4MxsT.mjs.map → types-Bm1dn-q3.mjs.map} +1 -1
  100. package/dist/{types-CnZYHyLW.d.mts → types-BmPPSUEx.d.mts} +1 -1
  101. package/dist/{types-CnZYHyLW.d.mts.map → types-BmPPSUEx.d.mts.map} +1 -1
  102. package/dist/{types-6CUZRrZP.d.mts → types-BrA0xf5I.d.mts} +24 -2
  103. package/dist/{types-6CUZRrZP.d.mts.map → types-BrA0xf5I.d.mts.map} +1 -1
  104. package/dist/{types-C2v0c34j.d.mts → types-CS8FIX7L.d.mts} +1 -1
  105. package/dist/{types-C2v0c34j.d.mts.map → types-CS8FIX7L.d.mts.map} +1 -1
  106. package/dist/{types-BH2L167P.mjs → types-CgqmmMJB.mjs} +1 -1
  107. package/dist/{types-BH2L167P.mjs.map → types-CgqmmMJB.mjs.map} +1 -1
  108. package/dist/{types-CFWjXmus.d.mts → types-DIMwPFub.d.mts} +1 -1
  109. package/dist/{types-CFWjXmus.d.mts.map → types-DIMwPFub.d.mts.map} +1 -1
  110. package/dist/{types-DgrIP0tF.d.mts → types-i36XcA_X.d.mts} +49 -6
  111. package/dist/types-i36XcA_X.d.mts.map +1 -0
  112. package/dist/{validate-CqsNItbt.mjs → validate-CxVsLehf.mjs} +2 -2
  113. package/dist/{validate-CqsNItbt.mjs.map → validate-CxVsLehf.mjs.map} +1 -1
  114. package/dist/{validate-kM8Pjuf7.d.mts → validate-DHxmpFJt.d.mts} +4 -4
  115. package/dist/{validate-kM8Pjuf7.d.mts.map → validate-DHxmpFJt.d.mts.map} +1 -1
  116. package/dist/validation-C-ZpN2GI.mjs +144 -0
  117. package/dist/validation-C-ZpN2GI.mjs.map +1 -0
  118. package/dist/version-DJrV1K0M.mjs +7 -0
  119. package/dist/{version-BnTKdfam.mjs.map → version-DJrV1K0M.mjs.map} +1 -1
  120. package/dist/zod-generator-CpwccCIv.mjs +132 -0
  121. package/dist/zod-generator-CpwccCIv.mjs.map +1 -0
  122. package/package.json +19 -6
  123. package/src/api/auth-storage.ts +37 -0
  124. package/src/api/error.ts +6 -0
  125. package/src/api/errors.ts +8 -0
  126. package/src/api/handlers/comments.ts +13 -0
  127. package/src/api/handlers/content.ts +122 -3
  128. package/src/api/handlers/index.ts +2 -0
  129. package/src/api/handlers/media.ts +8 -1
  130. package/src/api/handlers/menus.ts +160 -21
  131. package/src/api/handlers/redirects.ts +16 -3
  132. package/src/api/handlers/sections.ts +8 -1
  133. package/src/api/handlers/taxonomies.ts +128 -16
  134. package/src/api/handlers/validation.ts +212 -0
  135. package/src/api/openapi/document.ts +4 -1
  136. package/src/api/public-url.ts +6 -3
  137. package/src/api/route-utils.ts +14 -0
  138. package/src/api/schemas/common.ts +1 -1
  139. package/src/api/schemas/setup.ts +8 -0
  140. package/src/api/schemas/widgets.ts +12 -10
  141. package/src/api/setup-complete.ts +40 -0
  142. package/src/astro/integration/index.ts +13 -2
  143. package/src/astro/integration/routes.ts +28 -0
  144. package/src/astro/integration/runtime.ts +19 -1
  145. package/src/astro/integration/virtual-modules.ts +41 -0
  146. package/src/astro/integration/vite-config.ts +43 -12
  147. package/src/astro/middleware/auth.ts +21 -0
  148. package/src/astro/middleware.ts +18 -1
  149. package/src/astro/routes/PluginRegistry.tsx +10 -1
  150. package/src/astro/routes/api/auth/mode.ts +57 -0
  151. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +23 -3
  152. package/src/astro/routes/api/auth/oauth/[provider].ts +10 -4
  153. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +1 -1
  154. package/src/astro/routes/api/content/[collection]/index.ts +1 -9
  155. package/src/astro/routes/api/import/wordpress/media.ts +2 -7
  156. package/src/astro/routes/api/import/wordpress/prepare.ts +10 -0
  157. package/src/astro/routes/api/settings/email.ts +4 -9
  158. package/src/astro/routes/api/setup/admin.ts +8 -2
  159. package/src/astro/routes/api/setup/index.ts +2 -2
  160. package/src/astro/routes/api/setup/status.ts +3 -1
  161. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +4 -1
  162. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +4 -1
  163. package/src/astro/routes/api/widget-areas/[name].ts +4 -1
  164. package/src/astro/routes/api/widget-areas/index.ts +4 -1
  165. package/src/astro/types.ts +9 -0
  166. package/src/auth/mode.ts +15 -3
  167. package/src/auth/providers/github-admin.tsx +29 -0
  168. package/src/auth/providers/github.ts +31 -0
  169. package/src/auth/providers/google-admin.tsx +44 -0
  170. package/src/auth/providers/google.ts +31 -0
  171. package/src/auth/types.ts +114 -4
  172. package/src/cli/commands/bundle.ts +3 -1
  173. package/src/components/EmDashImage.astro +7 -6
  174. package/src/components/Gallery.astro +5 -3
  175. package/src/components/Image.astro +8 -3
  176. package/src/components/InlinePortableTextEditor.tsx +2 -1
  177. package/src/components/LiveSearch.astro +5 -14
  178. package/src/database/repositories/audit.ts +6 -8
  179. package/src/database/repositories/byline.ts +6 -8
  180. package/src/database/repositories/comment.ts +12 -16
  181. package/src/database/repositories/content.ts +40 -40
  182. package/src/database/repositories/index.ts +1 -1
  183. package/src/database/repositories/media.ts +10 -13
  184. package/src/database/repositories/plugin-storage.ts +4 -6
  185. package/src/database/repositories/redirect.ts +12 -16
  186. package/src/database/repositories/taxonomy.ts +14 -3
  187. package/src/database/repositories/types.ts +57 -8
  188. package/src/database/repositories/user.ts +6 -8
  189. package/src/emdash-runtime.ts +306 -90
  190. package/src/index.ts +5 -1
  191. package/src/loader.ts +6 -5
  192. package/src/mcp/server.ts +678 -105
  193. package/src/media/normalize.ts +1 -1
  194. package/src/media/url.ts +78 -0
  195. package/src/plugins/email-console.ts +10 -3
  196. package/src/plugins/hooks.ts +11 -0
  197. package/src/plugins/manifest-schema.ts +12 -0
  198. package/src/plugins/types.ts +23 -2
  199. package/src/query.ts +1 -1
  200. package/src/request-cache.ts +3 -0
  201. package/src/schema/registry.ts +41 -5
  202. package/src/search/fts-manager.ts +0 -2
  203. package/src/search/query.ts +111 -26
  204. package/src/search/types.ts +8 -1
  205. package/src/sections/index.ts +7 -9
  206. package/src/storage/s3.ts +12 -6
  207. package/src/virtual-modules.d.ts +21 -1
  208. package/src/widgets/index.ts +1 -1
  209. package/dist/apply-5uslYdUu.mjs.map +0 -1
  210. package/dist/byline-C4OVd8b3.mjs.map +0 -1
  211. package/dist/content-D7J5y73J.mjs.map +0 -1
  212. package/dist/error-CiYn9yDu.mjs.map +0 -1
  213. package/dist/index-De6_Xv3v.d.mts.map +0 -1
  214. package/dist/loader-DeiBJEMe.mjs.map +0 -1
  215. package/dist/manifest-schema-V30qsMft.mjs.map +0 -1
  216. package/dist/media-DqHVh136.mjs.map +0 -1
  217. package/dist/mode-CpNnGkPz.mjs.map +0 -1
  218. package/dist/redirect-CN0Rt9Ob.mjs.map +0 -1
  219. package/dist/registry-Ci3WxVAr.mjs.map +0 -1
  220. package/dist/request-cache-DiR961CV.mjs.map +0 -1
  221. package/dist/search-B0effn3j.mjs.map +0 -1
  222. package/dist/types-CMMN0pNg.mjs +0 -31
  223. package/dist/types-CMMN0pNg.mjs.map +0 -1
  224. package/dist/types-DgrIP0tF.d.mts.map +0 -1
  225. package/dist/version-BnTKdfam.mjs +0 -7
@@ -4,28 +4,30 @@ import "../connection-2igzM-AT.mjs";
4
4
  import { t as validateIdentifier } from "../validate-VPnKoIzW.mjs";
5
5
  import { a as isSqlite } from "../dialect-helpers-DhTzaUxP.mjs";
6
6
  import { r as runMigrations } from "../runner-tQ7BJ4T7.mjs";
7
- import { At as handleContentPublish, B as EmailPipeline, Ct as handleContentDiscardDraft, Dt as handleContentList, Et as handleContentGetIncludingTrashed, Ft as handleContentUnschedule, G as extractRequestMeta, H as createHookPipeline, It as handleContentUpdate, J as definePlugin, K as sanitizeHeadersForSandbox, L as PluginRouteRegistry, Lt as validateRev, Mt as handleContentSchedule, Nt as handleContentTranslations, Ot as handleContentListTrashed, Pt as handleContentUnpublish, R as DEV_CONSOLE_EMAIL_PLUGIN_ID, St as handleContentDelete, Tt as handleContentGet, U as resolveExclusiveHooks, W as CronExecutor, Z as after, _t as hashString, at as PluginStateRepository, bt as handleContentCountTrashed, ct as handleMediaDelete, dt as handleMediaUpdate, ft as handleRevisionGet, jt as handleContentRestore, kt as handleContentPermanentDelete, lt as handleMediaGet, mt as handleRevisionRestore, nt as loadBundleFromR2, pt as handleRevisionList, q as getTrustedProxyHeaders, st as handleMediaCreate, ut as handleMediaList, vt as handleContentCompare, wt as handleContentDuplicate, xt as handleContentCreate, yt as handleContentCountScheduled, z as devConsoleEmailDeliver } from "../search-B0effn3j.mjs";
8
- import { r as RevisionRepository } from "../content-D7J5y73J.mjs";
7
+ import { At as handleContentSchedule, B as EmailPipeline, Ct as handleContentGet, Dt as handleContentPermanentDelete, Et as handleContentListTrashed, Ft as validateRev, G as extractRequestMeta, H as createHookPipeline, J as definePlugin, K as sanitizeHeadersForSandbox, L as PluginRouteRegistry, Mt as handleContentUnpublish, Nt as handleContentUnschedule, Ot as handleContentPublish, Pt as handleContentUpdate, R as DEV_CONSOLE_EMAIL_PLUGIN_ID, St as handleContentDuplicate, Tt as handleContentList, U as resolveExclusiveHooks, W as CronExecutor, Z as after, _t as handleContentCountScheduled, at as PluginStateRepository, bt as handleContentDelete, ct as handleMediaDelete, dt as handleMediaUpdate, ft as handleRevisionGet, gt as handleContentCompare, jt as handleContentTranslations, kt as handleContentRestore, lt as handleMediaGet, mt as handleRevisionRestore, nt as loadBundleFromR2, pt as handleRevisionList, q as getTrustedProxyHeaders, st as handleMediaCreate, ut as handleMediaList, vt as handleContentCountTrashed, wt as handleContentGetIncludingTrashed, xt as handleContentDiscardDraft, yt as handleContentCreate, z as devConsoleEmailDeliver } from "../search-BoZYFuUk.mjs";
8
+ import { r as RevisionRepository } from "../content-BcQPYxdV.mjs";
9
9
  import "../base64-MBPo9ozB.mjs";
10
- import "../types-CMMN0pNg.mjs";
11
- import { t as MediaRepository } from "../media-DqHVh136.mjs";
12
- import { p as OptionsRepository } from "../apply-5uslYdUu.mjs";
13
- import "../redirect-CN0Rt9Ob.mjs";
14
- import "../byline-C4OVd8b3.mjs";
10
+ import "../types-BIgulNsW.mjs";
11
+ import { t as MediaRepository } from "../media-D8FbNsl0.mjs";
12
+ import { p as OptionsRepository } from "../apply-x0eMK1lX.mjs";
13
+ import "../redirect-D_pshWdf.mjs";
14
+ import "../byline-Chbr2GoP.mjs";
15
15
  import { n as normalizeMediaValue } from "../placeholder-C-fk5hYI.mjs";
16
16
  import { i as setI18nConfig } from "../config-BXwuX8Bx.mjs";
17
- import { i as FTSManager, n as SchemaRegistry } from "../registry-Ci3WxVAr.mjs";
18
- import { n as getDb } from "../loader-DeiBJEMe.mjs";
19
- import "../request-cache-DiR961CV.mjs";
20
- import "../taxonomies-K2z0Uhnj.mjs";
21
- import { r as normalizeManifestRoute } from "../manifest-schema-V30qsMft.mjs";
22
- import { a as invalidateUrlPatternCache } from "../query-g4Ug-9j9.mjs";
23
- import "../tokens-BFPFx3CA.mjs";
24
- import "../bylines-hPTW79hw.mjs";
25
- import "../load-CBcmDIot.mjs";
17
+ import { r as hashString } from "../zod-generator-CpwccCIv.mjs";
18
+ import { i as FTSManager, n as SchemaRegistry } from "../registry-C3Mr0ODu.mjs";
19
+ import { n as getDb } from "../loader-CndGj8kM.mjs";
20
+ import "../request-cache-Ci7f5pBb.mjs";
21
+ import "../taxonomies-B4IAshV8.mjs";
22
+ import { r as normalizeManifestRoute } from "../manifest-schema-DH9xhc6t.mjs";
23
+ import "../error-zG5T1UGA.mjs";
24
+ import { a as invalidateUrlPatternCache } from "../query-fqEdLFms.mjs";
25
+ import "../tokens-D9vnZqYS.mjs";
26
+ import "../bylines-CRNsVG88.mjs";
27
+ import "../load-CyEoextb.mjs";
26
28
  import "../index.mjs";
27
- import { n as VERSION, t as COMMIT } from "../version-BnTKdfam.mjs";
28
- import { t as getAuthMode } from "../mode-CpNnGkPz.mjs";
29
+ import { n as VERSION, t as COMMIT } from "../version-DJrV1K0M.mjs";
30
+ import { t as getAuthMode } from "../mode-BnAOqItE.mjs";
29
31
  import { Kysely, sql } from "kysely";
30
32
  import { defineMiddleware } from "astro:middleware";
31
33
  import virtualConfig from "virtual:emdash/config";
@@ -281,6 +283,19 @@ var PiggybackScheduler = class {
281
283
  //#endregion
282
284
  //#region src/emdash-runtime.ts
283
285
  const LEADING_SLASH_PATTERN = /^\//;
286
+ /**
287
+ * Parse a JSON column expected to contain an array of strings.
288
+ *
289
+ * Throws on malformed JSON rather than returning []; callers are responsible
290
+ * for deciding how to handle/log the error. Empty string / null inputs return
291
+ * [] (they represent "no value"). Non-string array entries are filtered out.
292
+ */
293
+ function parseStringArray(raw) {
294
+ if (!raw) return [];
295
+ const parsed = JSON.parse(raw);
296
+ if (!Array.isArray(parsed)) return [];
297
+ return parsed.filter((v) => typeof v === "string");
298
+ }
284
299
  const VALID_METADATA_KINDS = new Set([
285
300
  "meta",
286
301
  "property",
@@ -410,27 +425,27 @@ var EmDashRuntime = class EmDashRuntime {
410
425
  if (ctx?.db) return ctx.db;
411
426
  return this._db;
412
427
  }
413
- constructor(db, storage, configuredPlugins, sandboxedPlugins, sandboxedPluginEntries, hooks, enabledPlugins, pluginStates, config, mediaProviders, mediaProviderEntries, cronExecutor, cronScheduler, emailPipeline, allPipelinePlugins, pipelineFactoryOptions, runtimeDeps, pipelineRef, manifestCacheKey) {
414
- this._db = db;
415
- this.storage = storage;
416
- this.configuredPlugins = configuredPlugins;
417
- this.sandboxedPlugins = sandboxedPlugins;
418
- this.sandboxedPluginEntries = sandboxedPluginEntries;
419
- this.schemaRegistry = new SchemaRegistry(db);
420
- this._hooks = hooks;
421
- this.enabledPlugins = enabledPlugins;
422
- this.pluginStates = pluginStates;
423
- this.config = config;
424
- this.mediaProviders = mediaProviders;
425
- this.mediaProviderEntries = mediaProviderEntries;
426
- this.cronExecutor = cronExecutor;
427
- this.cronScheduler = cronScheduler;
428
- this.email = emailPipeline;
429
- this.allPipelinePlugins = allPipelinePlugins;
430
- this.pipelineFactoryOptions = pipelineFactoryOptions;
431
- this.runtimeDeps = runtimeDeps;
432
- this.pipelineRef = pipelineRef;
433
- this._manifestCacheKey = manifestCacheKey;
428
+ constructor(parts) {
429
+ this._db = parts.db;
430
+ this.storage = parts.storage;
431
+ this.configuredPlugins = parts.configuredPlugins;
432
+ this.sandboxedPlugins = parts.sandboxedPlugins;
433
+ this.sandboxedPluginEntries = parts.sandboxedPluginEntries;
434
+ this.schemaRegistry = new SchemaRegistry(parts.db);
435
+ this._hooks = parts.hooks;
436
+ this.enabledPlugins = parts.enabledPlugins;
437
+ this.pluginStates = parts.pluginStates;
438
+ this.config = parts.config;
439
+ this.mediaProviders = parts.mediaProviders;
440
+ this.mediaProviderEntries = parts.mediaProviderEntries;
441
+ this.cronExecutor = parts.cronExecutor;
442
+ this.cronScheduler = parts.cronScheduler;
443
+ this.email = parts.emailPipeline;
444
+ this.allPipelinePlugins = parts.allPipelinePlugins;
445
+ this.pipelineFactoryOptions = parts.pipelineFactoryOptions;
446
+ this.runtimeDeps = parts.runtimeDeps;
447
+ this.pipelineRef = parts.pipelineRef;
448
+ this._manifestCacheKey = parts.manifestCacheKey;
434
449
  }
435
450
  /**
436
451
  * Get the sandbox runner instance (for marketplace install/update)
@@ -723,7 +738,27 @@ var EmDashRuntime = class EmDashRuntime {
723
738
  virtualConfig?.i18n?.defaultLocale ?? "",
724
739
  (virtualConfig?.i18n?.locales ?? []).toSorted().join(",")
725
740
  ].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);
741
+ return new EmDashRuntime({
742
+ db,
743
+ storage,
744
+ configuredPlugins: deps.plugins,
745
+ sandboxedPlugins,
746
+ sandboxedPluginEntries: deps.sandboxedPluginEntries,
747
+ hooks: pipeline,
748
+ enabledPlugins,
749
+ pluginStates,
750
+ config: deps.config,
751
+ mediaProviders,
752
+ mediaProviderEntries,
753
+ cronExecutor,
754
+ cronScheduler,
755
+ emailPipeline,
756
+ allPipelinePlugins,
757
+ pipelineFactoryOptions,
758
+ runtimeDeps: deps,
759
+ pipelineRef,
760
+ manifestCacheKey
761
+ });
727
762
  }
728
763
  /**
729
764
  * Get a media provider by ID
@@ -777,9 +812,9 @@ var EmDashRuntime = class EmDashRuntime {
777
812
  }
778
813
  })();
779
814
  if (collectionCount.count === 0 && !setupDone) {
780
- const { applySeed } = await import("../apply-5uslYdUu.mjs").then((n) => n.n);
781
- const { loadSeed } = await import("../load-CBcmDIot.mjs").then((n) => n.r);
782
- const { validateSeed } = await import("../validate-CqsNItbt.mjs").then((n) => n.n);
815
+ const { applySeed } = await import("../apply-x0eMK1lX.mjs").then((n) => n.n);
816
+ const { loadSeed } = await import("../load-CyEoextb.mjs").then((n) => n.r);
817
+ const { validateSeed } = await import("../validate-CxVsLehf.mjs").then((n) => n.n);
783
818
  const seed = await loadSeed();
784
819
  if (validateSeed(seed).valid) {
785
820
  await applySeed(db, seed, { onConflict: "skip" });
@@ -1055,7 +1090,7 @@ var EmDashRuntime = class EmDashRuntime {
1055
1090
  label: row.label,
1056
1091
  labelSingular: row.label_singular ?? void 0,
1057
1092
  hierarchical: row.hierarchical === 1,
1058
- collections: row.collections ? JSON.parse(row.collections).toSorted() : []
1093
+ collections: parseStringArray(row.collections).toSorted()
1059
1094
  }));
1060
1095
  } catch (error) {
1061
1096
  console.debug("EmDash: Could not load taxonomy definitions:", error);
@@ -1143,16 +1178,67 @@ var EmDashRuntime = class EmDashRuntime {
1143
1178
  return handleContentList(this.db, collection, params);
1144
1179
  }
1145
1180
  async handleContentGet(collection, id, locale) {
1146
- return handleContentGet(this.db, collection, id, locale);
1181
+ const result = await handleContentGet(this.db, collection, id, locale);
1182
+ return this.hydrateDraftData(result);
1147
1183
  }
1148
1184
  async handleContentGetIncludingTrashed(collection, id, locale) {
1149
- return handleContentGetIncludingTrashed(this.db, collection, id, locale);
1185
+ const result = await handleContentGetIncludingTrashed(this.db, collection, id, locale);
1186
+ return this.hydrateDraftData(result);
1187
+ }
1188
+ /**
1189
+ * If the response item has a `draftRevisionId`, replace `item.data` with
1190
+ * the draft revision's data and expose the original published values as
1191
+ * `liveData`. This makes the content_get / content_update round-trip
1192
+ * intuitive — read returns the latest content the caller has saved
1193
+ * (their pending draft), with the previously-published values still
1194
+ * accessible for compare-style flows.
1195
+ *
1196
+ * No-op when no draft exists or the response is an error.
1197
+ */
1198
+ async hydrateDraftData(result) {
1199
+ if (!result || typeof result !== "object") return result;
1200
+ const r = result;
1201
+ if (!r.success || !r.data?.item) return result;
1202
+ const item = r.data.item;
1203
+ const draftRevisionId = typeof item.draftRevisionId === "string" ? item.draftRevisionId : null;
1204
+ if (!draftRevisionId) return result;
1205
+ try {
1206
+ const revision = await new RevisionRepository(this.db).findById(draftRevisionId);
1207
+ if (!revision) return result;
1208
+ const liveData = item.data && typeof item.data === "object" ? item.data : {};
1209
+ const revisionData = {};
1210
+ for (const [key, value] of Object.entries(revision.data)) if (!key.startsWith("_")) revisionData[key] = value;
1211
+ const mergedData = {
1212
+ ...liveData,
1213
+ ...revisionData
1214
+ };
1215
+ return {
1216
+ ...result,
1217
+ data: {
1218
+ ...r.data,
1219
+ item: {
1220
+ ...item,
1221
+ data: mergedData,
1222
+ liveData
1223
+ }
1224
+ }
1225
+ };
1226
+ } catch (error) {
1227
+ console.error("[emdash] draft hydration failed:", error);
1228
+ return result;
1229
+ }
1150
1230
  }
1151
1231
  async handleContentCreate(collection, body) {
1152
1232
  let processedData = body.data;
1153
1233
  if (this.hooks.hasHooks("content:beforeSave")) processedData = (await this.hooks.runContentBeforeSave(body.data, collection, true)).content;
1154
1234
  processedData = await this.runSandboxedBeforeSave(processedData, collection, true);
1155
1235
  processedData = await this.normalizeMediaFields(collection, processedData);
1236
+ const { validateContentData } = await import("../validation-C-ZpN2GI.mjs");
1237
+ const validation = await validateContentData(this.db, collection, processedData, { partial: false });
1238
+ if (!validation.ok) return {
1239
+ success: false,
1240
+ error: validation.error
1241
+ };
1156
1242
  const result = await handleContentCreate(this.db, collection, {
1157
1243
  ...body,
1158
1244
  data: processedData,
@@ -1163,7 +1249,7 @@ var EmDashRuntime = class EmDashRuntime {
1163
1249
  return result;
1164
1250
  }
1165
1251
  async handleContentUpdate(collection, id, body) {
1166
- const { ContentRepository } = await import("../content-D7J5y73J.mjs").then((n) => n.n);
1252
+ const { ContentRepository } = await import("../content-BcQPYxdV.mjs").then((n) => n.n);
1167
1253
  const repo = new ContentRepository(this.db);
1168
1254
  const resolvedItem = await repo.findByIdOrSlug(collection, id);
1169
1255
  const resolvedId = resolvedItem?.id ?? id;
@@ -1190,6 +1276,12 @@ var EmDashRuntime = class EmDashRuntime {
1190
1276
  if (this.hooks.hasHooks("content:beforeSave")) processedData = (await this.hooks.runContentBeforeSave(bodyWithoutRev.data, collection, false)).content;
1191
1277
  processedData = await this.runSandboxedBeforeSave(processedData, collection, false);
1192
1278
  processedData = await this.normalizeMediaFields(collection, processedData);
1279
+ const { validateContentData } = await import("../validation-C-ZpN2GI.mjs");
1280
+ const validation = await validateContentData(this.db, collection, processedData, { partial: true });
1281
+ if (!validation.ok) return {
1282
+ success: false,
1283
+ error: validation.error
1284
+ };
1193
1285
  }
1194
1286
  let usesDraftRevisions = false;
1195
1287
  if (processedData) try {
@@ -1234,8 +1326,9 @@ var EmDashRuntime = class EmDashRuntime {
1234
1326
  authorId: bodyWithoutRev.authorId,
1235
1327
  bylines: bodyWithoutRev.bylines
1236
1328
  });
1237
- if (result.success && result.data) this.runAfterSaveHooks(contentItemToRecord(result.data.item), collection, false);
1238
- return result;
1329
+ const hydrated = await this.hydrateDraftData(result);
1330
+ if (hydrated.success && hydrated.data) this.runAfterSaveHooks(contentItemToRecord(hydrated.data.item), collection, false);
1331
+ return hydrated;
1239
1332
  }
1240
1333
  async handleContentDelete(collection, id) {
1241
1334
  if (this.hooks.hasHooks("content:beforeDelete")) {
@@ -1353,7 +1446,47 @@ var EmDashRuntime = class EmDashRuntime {
1353
1446
  return handleRevisionGet(this.db, revisionId);
1354
1447
  }
1355
1448
  async handleRevisionRestore(revisionId, callerUserId) {
1356
- return handleRevisionRestore(this.db, revisionId, callerUserId);
1449
+ const revisionRepo = new RevisionRepository(this.db);
1450
+ const revision = await revisionRepo.findById(revisionId);
1451
+ if (!revision) return {
1452
+ success: false,
1453
+ error: {
1454
+ code: "NOT_FOUND",
1455
+ message: `Revision not found: ${revisionId}`
1456
+ }
1457
+ };
1458
+ if (!((await this.schemaRegistry.getCollectionWithFields(revision.collection))?.supports?.includes("revisions") ?? false)) {
1459
+ const result = await handleRevisionRestore(this.db, revisionId, callerUserId);
1460
+ return this.hydrateDraftData(result);
1461
+ }
1462
+ try {
1463
+ const newDraft = await revisionRepo.create({
1464
+ collection: revision.collection,
1465
+ entryId: revision.entryId,
1466
+ data: revision.data,
1467
+ authorId: callerUserId
1468
+ });
1469
+ validateIdentifier(revision.collection, "collection");
1470
+ const tableName = `ec_${revision.collection}`;
1471
+ await sql`
1472
+ UPDATE ${sql.ref(tableName)}
1473
+ SET draft_revision_id = ${newDraft.id},
1474
+ updated_at = ${(/* @__PURE__ */ new Date()).toISOString()}
1475
+ WHERE id = ${revision.entryId}
1476
+ `.execute(this.db);
1477
+ revisionRepo.pruneOldRevisions(revision.collection, revision.entryId, 50).catch(() => {});
1478
+ const refetched = await handleContentGet(this.db, revision.collection, revision.entryId);
1479
+ return this.hydrateDraftData(refetched);
1480
+ } catch (error) {
1481
+ console.error("[emdash] revision restore failed:", error);
1482
+ return {
1483
+ success: false,
1484
+ error: {
1485
+ code: "REVISION_RESTORE_ERROR",
1486
+ message: "Failed to restore revision"
1487
+ }
1488
+ };
1489
+ }
1357
1490
  }
1358
1491
  /**
1359
1492
  * Get route metadata for a plugin route without invoking the handler.
@@ -1486,16 +1619,30 @@ var EmDashRuntime = class EmDashRuntime {
1486
1619
  return true;
1487
1620
  }
1488
1621
  runAfterSaveHooks(content, collection, isNew) {
1489
- if (this.hooks.hasHooks("content:afterSave")) this.hooks.runContentAfterSave(content, collection, isNew).catch((err) => console.error("EmDash afterSave hook error:", err));
1490
- for (const [pluginKey, plugin] of this.sandboxedPlugins) {
1491
- const [id] = pluginKey.split(":");
1492
- if (!id || !this.isPluginEnabled(id)) continue;
1493
- plugin.invokeHook("content:afterSave", {
1494
- content,
1495
- collection,
1496
- isNew
1497
- }).catch((err) => console.error(`EmDash: Sandboxed plugin ${id} afterSave error:`, err));
1498
- }
1622
+ after(async () => {
1623
+ if (this.hooks.hasHooks("content:afterSave")) try {
1624
+ await this.hooks.runContentAfterSave(content, collection, isNew);
1625
+ } catch (err) {
1626
+ console.error("EmDash afterSave hook error:", err);
1627
+ }
1628
+ const tasks = [];
1629
+ for (const [pluginKey, plugin] of this.sandboxedPlugins) {
1630
+ const [id] = pluginKey.split(":");
1631
+ if (!id || !this.isPluginEnabled(id)) continue;
1632
+ tasks.push((async () => {
1633
+ try {
1634
+ await plugin.invokeHook("content:afterSave", {
1635
+ content,
1636
+ collection,
1637
+ isNew
1638
+ });
1639
+ } catch (err) {
1640
+ console.error(`EmDash: Sandboxed plugin ${id} afterSave error:`, err);
1641
+ }
1642
+ })());
1643
+ }
1644
+ await Promise.allSettled(tasks);
1645
+ });
1499
1646
  }
1500
1647
  runAfterDeleteHooks(id, collection, permanent) {
1501
1648
  if (this.hooks.hasHooks("content:afterDelete")) this.hooks.runContentAfterDelete(id, collection, permanent).catch((err) => console.error("EmDash afterDelete hook error:", err));
@@ -1510,15 +1657,29 @@ var EmDashRuntime = class EmDashRuntime {
1510
1657
  }
1511
1658
  }
1512
1659
  runAfterPublishHooks(content, collection) {
1513
- if (this.hooks.hasHooks("content:afterPublish")) this.hooks.runContentAfterPublish(content, collection).catch((err) => console.error("EmDash afterPublish hook error:", err));
1514
- for (const [pluginKey, plugin] of this.sandboxedPlugins) {
1515
- const [pluginId] = pluginKey.split(":");
1516
- if (!pluginId || !this.isPluginEnabled(pluginId)) continue;
1517
- plugin.invokeHook("content:afterPublish", {
1518
- content,
1519
- collection
1520
- }).catch((err) => console.error(`EmDash: Sandboxed plugin ${pluginId} afterPublish error:`, err));
1521
- }
1660
+ after(async () => {
1661
+ if (this.hooks.hasHooks("content:afterPublish")) try {
1662
+ await this.hooks.runContentAfterPublish(content, collection);
1663
+ } catch (err) {
1664
+ console.error("EmDash afterPublish hook error:", err);
1665
+ }
1666
+ const tasks = [];
1667
+ for (const [pluginKey, plugin] of this.sandboxedPlugins) {
1668
+ const [pluginId] = pluginKey.split(":");
1669
+ if (!pluginId || !this.isPluginEnabled(pluginId)) continue;
1670
+ tasks.push((async () => {
1671
+ try {
1672
+ await plugin.invokeHook("content:afterPublish", {
1673
+ content,
1674
+ collection
1675
+ });
1676
+ } catch (err) {
1677
+ console.error(`EmDash: Sandboxed plugin ${pluginId} afterPublish error:`, err);
1678
+ }
1679
+ })());
1680
+ }
1681
+ await Promise.allSettled(tasks);
1682
+ });
1522
1683
  }
1523
1684
  runAfterUnpublishHooks(content, collection) {
1524
1685
  if (this.hooks.hasHooks("content:afterUnpublish")) this.hooks.runContentAfterUnpublish(content, collection).catch((err) => console.error("EmDash afterUnpublish hook error:", err));
@@ -1629,6 +1790,28 @@ var EmDashRuntime = class EmDashRuntime {
1629
1790
  }
1630
1791
  };
1631
1792
 
1793
+ //#endregion
1794
+ //#region src/media/url.ts
1795
+ /**
1796
+ * Resolve the public URL for a locally stored media key. Returns an empty
1797
+ * string when no key is given. When a storage adapter is supplied, defers to
1798
+ * `storage.getPublicUrl()`; otherwise returns the internal proxy route.
1799
+ */
1800
+ function resolvePublicMediaUrl(storage, storageKey) {
1801
+ if (!storageKey) return "";
1802
+ if (storage) return storage.getPublicUrl(storageKey);
1803
+ return `/_emdash/api/media/file/${storageKey}`;
1804
+ }
1805
+ /**
1806
+ * Build the `getPublicMediaUrl` closure attached to `Astro.locals.emdash`.
1807
+ * Shared by the anonymous fast path and the full-runtime path in middleware.
1808
+ *
1809
+ * @internal
1810
+ */
1811
+ function createPublicMediaUrlResolver(storage) {
1812
+ return (key) => resolvePublicMediaUrl(storage, key);
1813
+ }
1814
+
1632
1815
  //#endregion
1633
1816
  //#region src/astro/middleware.ts
1634
1817
  /**
@@ -1751,6 +1934,9 @@ function createRequestScopedDb$1(opts) {
1751
1934
  const onRequest = defineMiddleware(async (context, next) => {
1752
1935
  const { request, locals, cookies } = context;
1753
1936
  const url = context.url;
1937
+ if (!url.pathname.startsWith("/_emdash") && virtualConfig?.authProviders) {
1938
+ if (virtualConfig.authProviders.some((p) => p.routes?.some((r) => r.pattern && url.pathname === r.pattern))) return finalizeResponse(await next());
1939
+ }
1754
1940
  const queryRecorder = isInstrumentationEnabled() ? createRecorder(url.pathname, request.method, request.headers.get("x-perf-phase") ?? "default") : void 0;
1755
1941
  const run = async () => {
1756
1942
  const isEmDashRoute = url.pathname.startsWith("/_emdash");
@@ -1766,7 +1952,7 @@ const onRequest = defineMiddleware(async (context, next) => {
1766
1952
  if (!setupVerified) {
1767
1953
  const t0 = performance.now();
1768
1954
  try {
1769
- const { getDb } = await import("../loader-DeiBJEMe.mjs").then((n) => n.r);
1955
+ const { getDb } = await import("../loader-CndGj8kM.mjs").then((n) => n.r);
1770
1956
  await (await getDb()).selectFrom("_emdash_migrations").selectAll().limit(1).execute();
1771
1957
  setupVerified = true;
1772
1958
  } catch {
@@ -1787,7 +1973,8 @@ const onRequest = defineMiddleware(async (context, next) => {
1787
1973
  setupVerified = true;
1788
1974
  locals.emdash = {
1789
1975
  collectPageMetadata: runtime.collectPageMetadata.bind(runtime),
1790
- collectPageFragments: runtime.collectPageFragments.bind(runtime)
1976
+ collectPageFragments: runtime.collectPageFragments.bind(runtime),
1977
+ getPublicMediaUrl: createPublicMediaUrlResolver(runtime.storage)
1791
1978
  };
1792
1979
  } catch {}
1793
1980
  timings.push({
@@ -1900,6 +2087,7 @@ const onRequest = defineMiddleware(async (context, next) => {
1900
2087
  ensureSearchHealthy: runtime.ensureSearchHealthy.bind(runtime),
1901
2088
  storage: runtime.storage,
1902
2089
  db: runtime.db,
2090
+ getPublicMediaUrl: createPublicMediaUrlResolver(runtime.storage),
1903
2091
  hooks: runtime.hooks,
1904
2092
  email: runtime.email,
1905
2093
  configuredPlugins: runtime.configuredPlugins,