emdash 0.6.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 (263) 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-B4MsLM-w.mjs → apply-x0eMK1lX.mjs} +186 -28
  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 +92 -17
  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 +7 -2
  15. package/dist/astro/middleware/request-context.mjs.map +1 -1
  16. package/dist/astro/middleware/setup.mjs +1 -1
  17. package/dist/astro/middleware.d.mts.map +1 -1
  18. package/dist/astro/middleware.mjs +263 -74
  19. package/dist/astro/middleware.mjs.map +1 -1
  20. package/dist/astro/types.d.mts +25 -8
  21. package/dist/astro/types.d.mts.map +1 -1
  22. package/dist/{byline-C4OVd8b3.mjs → byline-Chbr2GoP.mjs} +3 -3
  23. package/dist/byline-Chbr2GoP.mjs.map +1 -0
  24. package/dist/{bylines-hPTW79hw.mjs → bylines-CRNsVG88.mjs} +4 -4
  25. package/dist/{bylines-hPTW79hw.mjs.map → bylines-CRNsVG88.mjs.map} +1 -1
  26. package/dist/cli/index.mjs +17 -13
  27. package/dist/cli/index.mjs.map +1 -1
  28. package/dist/client/cf-access.d.mts +1 -1
  29. package/dist/client/index.d.mts +1 -1
  30. package/dist/client/index.mjs +1 -1
  31. package/dist/{content-BsBoyj8G.mjs → content-BcQPYxdV.mjs} +39 -15
  32. package/dist/content-BcQPYxdV.mjs.map +1 -0
  33. package/dist/db/index.d.mts +3 -3
  34. package/dist/db/index.mjs +1 -1
  35. package/dist/db/libsql.d.mts +1 -1
  36. package/dist/db/postgres.d.mts +1 -1
  37. package/dist/db/sqlite.d.mts +1 -1
  38. package/dist/{db-errors-D0UT85nC.mjs → db-errors-l1Qh2RPR.mjs} +1 -1
  39. package/dist/{db-errors-D0UT85nC.mjs.map → db-errors-l1Qh2RPR.mjs.map} +1 -1
  40. package/dist/{default-CME5YdZ3.mjs → default-DCVqE5ib.mjs} +1 -1
  41. package/dist/{default-CME5YdZ3.mjs.map → default-DCVqE5ib.mjs.map} +1 -1
  42. package/dist/{error-CiYn9yDu.mjs → error-zG5T1UGA.mjs} +1 -1
  43. package/dist/error-zG5T1UGA.mjs.map +1 -0
  44. package/dist/{index-BYv0mB9g.d.mts → index-DIb-CzNx.d.mts} +232 -15
  45. package/dist/index-DIb-CzNx.d.mts.map +1 -0
  46. package/dist/index.d.mts +11 -11
  47. package/dist/index.mjs +23 -21
  48. package/dist/{load-CBcmDIot.mjs → load-CyEoextb.mjs} +1 -1
  49. package/dist/{load-CBcmDIot.mjs.map → load-CyEoextb.mjs.map} +1 -1
  50. package/dist/{loader-DeiBJEMe.mjs → loader-CndGj8kM.mjs} +8 -6
  51. package/dist/loader-CndGj8kM.mjs.map +1 -0
  52. package/dist/{manifest-schema-V30qsMft.mjs → manifest-schema-DH9xhc6t.mjs} +13 -1
  53. package/dist/manifest-schema-DH9xhc6t.mjs.map +1 -0
  54. package/dist/media/index.d.mts +1 -1
  55. package/dist/media/local-runtime.d.mts +7 -7
  56. package/dist/media/local-runtime.mjs +2 -2
  57. package/dist/{media-DqHVh136.mjs → media-D8FbNsl0.mjs} +4 -7
  58. package/dist/media-D8FbNsl0.mjs.map +1 -0
  59. package/dist/{mode-CpNnGkPz.mjs → mode-BnAOqItE.mjs} +1 -1
  60. package/dist/mode-BnAOqItE.mjs.map +1 -0
  61. package/dist/page/index.d.mts +2 -2
  62. package/dist/placeholder-C-fk5hYI.mjs.map +1 -1
  63. package/dist/{placeholder-tzpqGWII.d.mts → placeholder-D29tWZ7o.d.mts} +1 -1
  64. package/dist/{placeholder-tzpqGWII.d.mts.map → placeholder-D29tWZ7o.d.mts.map} +1 -1
  65. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  66. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  67. package/dist/{query-Bk_3vKvU.mjs → query-fqEdLFms.mjs} +9 -9
  68. package/dist/{query-Bk_3vKvU.mjs.map → query-fqEdLFms.mjs.map} +1 -1
  69. package/dist/{redirect-7lGhLBNZ.mjs → redirect-D_pshWdf.mjs} +69 -13
  70. package/dist/redirect-D_pshWdf.mjs.map +1 -0
  71. package/dist/{registry-Ci3WxVAr.mjs → registry-C3Mr0ODu.mjs} +33 -9
  72. package/dist/registry-C3Mr0ODu.mjs.map +1 -0
  73. package/dist/{request-cache-DiR961CV.mjs → request-cache-Ci7f5pBb.mjs} +1 -1
  74. package/dist/request-cache-Ci7f5pBb.mjs.map +1 -0
  75. package/dist/{runner-Fl2NcUUz.d.mts → runner-OURCaApa.d.mts} +2 -2
  76. package/dist/{runner-Fl2NcUUz.d.mts.map → runner-OURCaApa.d.mts.map} +1 -1
  77. package/dist/{runner-Cd-_WyDo.mjs → runner-tQ7BJ4T7.mjs} +211 -134
  78. package/dist/runner-tQ7BJ4T7.mjs.map +1 -0
  79. package/dist/runtime.d.mts +6 -6
  80. package/dist/runtime.mjs +2 -2
  81. package/dist/{search-DI4bM2w9.mjs → search-BoZYFuUk.mjs} +339 -102
  82. package/dist/search-BoZYFuUk.mjs.map +1 -0
  83. package/dist/seed/index.d.mts +2 -2
  84. package/dist/seed/index.mjs +12 -12
  85. package/dist/seo/index.d.mts +1 -1
  86. package/dist/storage/local.d.mts +1 -1
  87. package/dist/storage/local.mjs +1 -1
  88. package/dist/storage/s3.d.mts +1 -1
  89. package/dist/storage/s3.d.mts.map +1 -1
  90. package/dist/storage/s3.mjs +4 -4
  91. package/dist/storage/s3.mjs.map +1 -1
  92. package/dist/{taxonomies-DbrKzDju.mjs → taxonomies-B4IAshV8.mjs} +5 -5
  93. package/dist/{taxonomies-DbrKzDju.mjs.map → taxonomies-B4IAshV8.mjs.map} +1 -1
  94. package/dist/{tokens-BFPFx3CA.mjs → tokens-D9vnZqYS.mjs} +1 -1
  95. package/dist/{tokens-BFPFx3CA.mjs.map → tokens-D9vnZqYS.mjs.map} +1 -1
  96. package/dist/{transport-BykRfpyy.mjs → transport-C9ugt2Nr.mjs} +1 -1
  97. package/dist/{transport-BykRfpyy.mjs.map → transport-C9ugt2Nr.mjs.map} +1 -1
  98. package/dist/{transport-H4Iwx7tC.d.mts → transport-CUnEL3Vs.d.mts} +1 -1
  99. package/dist/{transport-H4Iwx7tC.d.mts.map → transport-CUnEL3Vs.d.mts.map} +1 -1
  100. package/dist/types-BIgulNsW.mjs +68 -0
  101. package/dist/types-BIgulNsW.mjs.map +1 -0
  102. package/dist/{types-DDS4MxsT.mjs → types-Bm1dn-q3.mjs} +1 -1
  103. package/dist/{types-DDS4MxsT.mjs.map → types-Bm1dn-q3.mjs.map} +1 -1
  104. package/dist/{types-CnZYHyLW.d.mts → types-BmPPSUEx.d.mts} +1 -1
  105. package/dist/{types-CnZYHyLW.d.mts.map → types-BmPPSUEx.d.mts.map} +1 -1
  106. package/dist/{types-6CUZRrZP.d.mts → types-BrA0xf5I.d.mts} +24 -2
  107. package/dist/{types-6CUZRrZP.d.mts.map → types-BrA0xf5I.d.mts.map} +1 -1
  108. package/dist/{types-8xrvl_68.d.mts → types-CS8FIX7L.d.mts} +10 -1
  109. package/dist/{types-8xrvl_68.d.mts.map → types-CS8FIX7L.d.mts.map} +1 -1
  110. package/dist/{types-BH2L167P.mjs → types-CgqmmMJB.mjs} +1 -1
  111. package/dist/{types-BH2L167P.mjs.map → types-CgqmmMJB.mjs.map} +1 -1
  112. package/dist/{types-CFWjXmus.d.mts → types-DIMwPFub.d.mts} +1 -1
  113. package/dist/{types-CFWjXmus.d.mts.map → types-DIMwPFub.d.mts.map} +1 -1
  114. package/dist/{types-DgrIP0tF.d.mts → types-i36XcA_X.d.mts} +49 -6
  115. package/dist/types-i36XcA_X.d.mts.map +1 -0
  116. package/dist/{validate-CqsNItbt.mjs → validate-CxVsLehf.mjs} +2 -2
  117. package/dist/{validate-CqsNItbt.mjs.map → validate-CxVsLehf.mjs.map} +1 -1
  118. package/dist/{validate-CaLH1Ia2.d.mts → validate-DHxmpFJt.d.mts} +4 -4
  119. package/dist/{validate-CaLH1Ia2.d.mts.map → validate-DHxmpFJt.d.mts.map} +1 -1
  120. package/dist/validation-C-ZpN2GI.mjs +144 -0
  121. package/dist/validation-C-ZpN2GI.mjs.map +1 -0
  122. package/dist/version-DJrV1K0M.mjs +7 -0
  123. package/dist/{version-Uaf2ynPX.mjs.map → version-DJrV1K0M.mjs.map} +1 -1
  124. package/dist/zod-generator-CpwccCIv.mjs +132 -0
  125. package/dist/zod-generator-CpwccCIv.mjs.map +1 -0
  126. package/package.json +19 -6
  127. package/src/api/auth-storage.ts +37 -0
  128. package/src/api/error.ts +6 -0
  129. package/src/api/errors.ts +8 -0
  130. package/src/api/handlers/comments.ts +13 -0
  131. package/src/api/handlers/content.ts +124 -3
  132. package/src/api/handlers/index.ts +2 -0
  133. package/src/api/handlers/media.ts +8 -1
  134. package/src/api/handlers/menus.ts +160 -21
  135. package/src/api/handlers/redirects.ts +16 -3
  136. package/src/api/handlers/sections.ts +8 -1
  137. package/src/api/handlers/taxonomies.ts +128 -16
  138. package/src/api/handlers/validation.ts +212 -0
  139. package/src/api/openapi/document.ts +4 -1
  140. package/src/api/public-url.ts +6 -3
  141. package/src/api/route-utils.ts +14 -0
  142. package/src/api/schemas/common.ts +1 -1
  143. package/src/api/schemas/content.ts +8 -0
  144. package/src/api/schemas/setup.ts +8 -0
  145. package/src/api/schemas/widgets.ts +12 -10
  146. package/src/api/setup-complete.ts +40 -0
  147. package/src/astro/integration/font-provider.ts +3 -1
  148. package/src/astro/integration/index.ts +15 -2
  149. package/src/astro/integration/routes.ts +28 -0
  150. package/src/astro/integration/runtime.ts +74 -2
  151. package/src/astro/integration/virtual-modules.ts +41 -0
  152. package/src/astro/integration/vite-config.ts +43 -12
  153. package/src/astro/middleware/auth.ts +21 -0
  154. package/src/astro/middleware.ts +18 -1
  155. package/src/astro/routes/PluginRegistry.tsx +10 -1
  156. package/src/astro/routes/admin.astro +14 -7
  157. package/src/astro/routes/api/auth/magic-link/send.ts +2 -1
  158. package/src/astro/routes/api/auth/mode.ts +57 -0
  159. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +23 -3
  160. package/src/astro/routes/api/auth/oauth/[provider].ts +10 -4
  161. package/src/astro/routes/api/auth/passkey/options.ts +2 -1
  162. package/src/astro/routes/api/auth/signup/request.ts +26 -8
  163. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +10 -6
  164. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +1 -1
  165. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +1 -1
  166. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +1 -1
  167. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +26 -0
  168. package/src/astro/routes/api/content/[collection]/[id].ts +30 -2
  169. package/src/astro/routes/api/content/[collection]/index.ts +20 -10
  170. package/src/astro/routes/api/content/[collection]/trash.ts +1 -1
  171. package/src/astro/routes/api/import/wordpress/media.ts +2 -7
  172. package/src/astro/routes/api/import/wordpress/prepare.ts +10 -0
  173. package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +4 -3
  174. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +4 -3
  175. package/src/astro/routes/api/manifest.ts +7 -0
  176. package/src/astro/routes/api/oauth/device/code.ts +2 -1
  177. package/src/astro/routes/api/oauth/device/token.ts +2 -1
  178. package/src/astro/routes/api/settings/email.ts +4 -9
  179. package/src/astro/routes/api/setup/admin-verify.ts +30 -5
  180. package/src/astro/routes/api/setup/admin.ts +38 -8
  181. package/src/astro/routes/api/setup/index.ts +7 -4
  182. package/src/astro/routes/api/setup/status.ts +3 -1
  183. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +4 -1
  184. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +4 -1
  185. package/src/astro/routes/api/widget-areas/[name].ts +4 -1
  186. package/src/astro/routes/api/widget-areas/index.ts +4 -1
  187. package/src/astro/types.ts +18 -0
  188. package/src/auth/mode.ts +15 -3
  189. package/src/auth/providers/github-admin.tsx +29 -0
  190. package/src/auth/providers/github.ts +31 -0
  191. package/src/auth/providers/google-admin.tsx +44 -0
  192. package/src/auth/providers/google.ts +31 -0
  193. package/src/auth/rate-limit.ts +50 -22
  194. package/src/auth/setup-nonce.ts +22 -0
  195. package/src/auth/trusted-proxy.ts +92 -0
  196. package/src/auth/types.ts +114 -4
  197. package/src/cli/commands/bundle.ts +3 -1
  198. package/src/components/EmDashImage.astro +7 -6
  199. package/src/components/Gallery.astro +5 -3
  200. package/src/components/Image.astro +8 -3
  201. package/src/components/InlinePortableTextEditor.tsx +2 -1
  202. package/src/components/LiveSearch.astro +5 -14
  203. package/src/database/migrations/035_bounded_404_log.ts +112 -0
  204. package/src/database/migrations/runner.ts +2 -0
  205. package/src/database/repositories/audit.ts +6 -8
  206. package/src/database/repositories/byline.ts +6 -8
  207. package/src/database/repositories/comment.ts +12 -16
  208. package/src/database/repositories/content.ts +79 -40
  209. package/src/database/repositories/index.ts +1 -1
  210. package/src/database/repositories/media.ts +10 -13
  211. package/src/database/repositories/options.ts +25 -0
  212. package/src/database/repositories/plugin-storage.ts +4 -6
  213. package/src/database/repositories/redirect.ts +123 -24
  214. package/src/database/repositories/taxonomy.ts +14 -3
  215. package/src/database/repositories/types.ts +57 -8
  216. package/src/database/repositories/user.ts +6 -8
  217. package/src/database/types.ts +9 -0
  218. package/src/emdash-runtime.ts +309 -91
  219. package/src/import/registry.ts +4 -3
  220. package/src/import/ssrf.ts +253 -12
  221. package/src/index.ts +5 -1
  222. package/src/loader.ts +6 -5
  223. package/src/mcp/server.ts +753 -107
  224. package/src/media/normalize.ts +1 -1
  225. package/src/media/url.ts +78 -0
  226. package/src/plugins/context.ts +15 -3
  227. package/src/plugins/email-console.ts +10 -3
  228. package/src/plugins/hooks.ts +11 -0
  229. package/src/plugins/manager.ts +6 -0
  230. package/src/plugins/manifest-schema.ts +12 -0
  231. package/src/plugins/request-meta.ts +66 -15
  232. package/src/plugins/routes.ts +3 -1
  233. package/src/plugins/types.ts +23 -2
  234. package/src/query.ts +1 -1
  235. package/src/request-cache.ts +3 -0
  236. package/src/schema/registry.ts +41 -5
  237. package/src/search/fts-manager.ts +0 -2
  238. package/src/search/query.ts +111 -26
  239. package/src/search/types.ts +8 -1
  240. package/src/sections/index.ts +7 -9
  241. package/src/seed/apply.ts +26 -0
  242. package/src/storage/s3.ts +12 -6
  243. package/src/virtual-modules.d.ts +21 -1
  244. package/src/visual-editing/toolbar.ts +6 -1
  245. package/src/widgets/index.ts +1 -1
  246. package/dist/apply-B4MsLM-w.mjs.map +0 -1
  247. package/dist/byline-C4OVd8b3.mjs.map +0 -1
  248. package/dist/content-BsBoyj8G.mjs.map +0 -1
  249. package/dist/error-CiYn9yDu.mjs.map +0 -1
  250. package/dist/index-BYv0mB9g.d.mts.map +0 -1
  251. package/dist/loader-DeiBJEMe.mjs.map +0 -1
  252. package/dist/manifest-schema-V30qsMft.mjs.map +0 -1
  253. package/dist/media-DqHVh136.mjs.map +0 -1
  254. package/dist/mode-CpNnGkPz.mjs.map +0 -1
  255. package/dist/redirect-7lGhLBNZ.mjs.map +0 -1
  256. package/dist/registry-Ci3WxVAr.mjs.map +0 -1
  257. package/dist/request-cache-DiR961CV.mjs.map +0 -1
  258. package/dist/runner-Cd-_WyDo.mjs.map +0 -1
  259. package/dist/search-DI4bM2w9.mjs.map +0 -1
  260. package/dist/types-CMMN0pNg.mjs +0 -31
  261. package/dist/types-CMMN0pNg.mjs.map +0 -1
  262. package/dist/types-DgrIP0tF.d.mts.map +0 -1
  263. package/dist/version-Uaf2ynPX.mjs +0 -7
@@ -1 +0,0 @@
1
- {"version":3,"file":"byline-C4OVd8b3.mjs","names":[],"sources":["../src/database/repositories/byline.ts"],"sourcesContent":["import { sql, type Kysely, type Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport { listTablesLike } from \"../dialect-helpers.js\";\nimport { withTransaction } from \"../transaction.js\";\nimport type { BylineTable, Database } from \"../types.js\";\nimport { validateIdentifier } from \"../validate.js\";\nimport {\n\tdecodeCursor,\n\tencodeCursor,\n\ttype BylineSummary,\n\ttype ContentBylineCredit,\n\ttype FindManyResult,\n} from \"./types.js\";\n\ntype BylineRow = Selectable<BylineTable>;\n\nexport interface CreateBylineInput {\n\tslug: string;\n\tdisplayName: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n}\n\nexport interface UpdateBylineInput {\n\tslug?: string;\n\tdisplayName?: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n}\n\nexport interface ContentBylineInput {\n\tbylineId: string;\n\troleLabel?: string | null;\n}\n\nfunction rowToByline(row: BylineRow): BylineSummary {\n\treturn {\n\t\tid: row.id,\n\t\tslug: row.slug,\n\t\tdisplayName: row.display_name,\n\t\tbio: row.bio,\n\t\tavatarMediaId: row.avatar_media_id,\n\t\twebsiteUrl: row.website_url,\n\t\tuserId: row.user_id,\n\t\tisGuest: row.is_guest === 1,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t};\n}\n\nexport class BylineRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\tasync findById(id: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToByline(row) : null;\n\t}\n\n\tasync findBySlug(slug: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToByline(row) : null;\n\t}\n\n\tasync findByUserId(userId: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"user_id\", \"=\", userId)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToByline(row) : null;\n\t}\n\n\tasync findMany(options?: {\n\t\tsearch?: string;\n\t\tisGuest?: boolean;\n\t\tuserId?: string;\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t}): Promise<FindManyResult<BylineSummary>> {\n\t\tconst limit = Math.min(Math.max(options?.limit ?? 50, 1), 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tif (options?.search) {\n\t\t\tconst escaped = options.search\n\t\t\t\t.replaceAll(\"\\\\\", \"\\\\\\\\\")\n\t\t\t\t.replaceAll(\"%\", \"\\\\%\")\n\t\t\t\t.replaceAll(\"_\", \"\\\\_\");\n\t\t\tconst term = `%${escaped}%`;\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([eb(\"display_name\", \"like\", term), eb(\"slug\", \"like\", term)]),\n\t\t\t);\n\t\t}\n\n\t\tif (options?.isGuest !== undefined) {\n\t\t\tquery = query.where(\"is_guest\", \"=\", options.isGuest ? 1 : 0);\n\t\t}\n\n\t\tif (options?.userId !== undefined) {\n\t\t\tquery = query.where(\"user_id\", \"=\", options.userId);\n\t\t}\n\n\t\tif (options?.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tif (decoded) {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(\"created_at\", \"<\", decoded.orderValue),\n\t\t\t\t\t\teb.and([eb(\"created_at\", \"=\", decoded.orderValue), eb(\"id\", \"<\", decoded.id)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\tconst items = rows.slice(0, limit).map(rowToByline);\n\t\tconst result: FindManyResult<BylineSummary> = { items };\n\n\t\tif (rows.length > limit) {\n\t\t\tconst last = items.at(-1);\n\t\t\tif (last) {\n\t\t\t\tresult.nextCursor = encodeCursor(last.createdAt, last.id);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tasync create(input: CreateBylineInput): Promise<BylineSummary> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_bylines\")\n\t\t\t.values({\n\t\t\t\tid,\n\t\t\t\tslug: input.slug,\n\t\t\t\tdisplay_name: input.displayName,\n\t\t\t\tbio: input.bio ?? null,\n\t\t\t\tavatar_media_id: input.avatarMediaId ?? null,\n\t\t\t\twebsite_url: input.websiteUrl ?? null,\n\t\t\t\tuser_id: input.userId ?? null,\n\t\t\t\tis_guest: input.isGuest ? 1 : 0,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.execute();\n\n\t\tconst byline = await this.findById(id);\n\t\tif (!byline) {\n\t\t\tthrow new Error(\"Failed to create byline\");\n\t\t}\n\t\treturn byline;\n\t}\n\n\tasync update(id: string, input: UpdateBylineInput): Promise<BylineSummary | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\tconst updates: Record<string, unknown> = {\n\t\t\tupdated_at: new Date().toISOString(),\n\t\t};\n\n\t\tif (input.slug !== undefined) updates.slug = input.slug;\n\t\tif (input.displayName !== undefined) updates.display_name = input.displayName;\n\t\tif (input.bio !== undefined) updates.bio = input.bio;\n\t\tif (input.avatarMediaId !== undefined) updates.avatar_media_id = input.avatarMediaId;\n\t\tif (input.websiteUrl !== undefined) updates.website_url = input.websiteUrl;\n\t\tif (input.userId !== undefined) updates.user_id = input.userId;\n\t\tif (input.isGuest !== undefined) updates.is_guest = input.isGuest ? 1 : 0;\n\n\t\tawait this.db.updateTable(\"_emdash_bylines\").set(updates).where(\"id\", \"=\", id).execute();\n\t\treturn await this.findById(id);\n\t}\n\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return false;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait trx.deleteFrom(\"_emdash_content_bylines\").where(\"byline_id\", \"=\", id).execute();\n\n\t\t\tawait trx.deleteFrom(\"_emdash_bylines\").where(\"id\", \"=\", id).execute();\n\n\t\t\tconst tableNames = await listTablesLike(trx, \"ec_%\");\n\t\t\tfor (const tableName of tableNames) {\n\t\t\t\tvalidateIdentifier(tableName, \"content table\");\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET primary_byline_id = NULL\n\t\t\t\t\tWHERE primary_byline_id = ${id}\n\t\t\t\t`.execute(trx);\n\t\t\t}\n\t\t});\n\n\t\treturn true;\n\t}\n\n\tasync getContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t): Promise<ContentBylineCredit[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.id\", \"cb.byline_id\")\n\t\t\t.select([\n\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\"b.id as id\",\n\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t])\n\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"cb.content_id\", \"=\", contentId)\n\t\t\t.orderBy(\"cb.sort_order\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map((row) => ({\n\t\t\tbyline: rowToByline(row),\n\t\t\tsortOrder: row.sort_order,\n\t\t\troleLabel: row.role_label,\n\t\t}));\n\t}\n\n\t/**\n\t * Batch-fetch byline credits for multiple content items in a single query.\n\t * Returns a Map keyed by contentId.\n\t */\n\tasync getContentBylinesMany(\n\t\tcollectionSlug: string,\n\t\tcontentIds: string[],\n\t): Promise<Map<string, ContentBylineCredit[]>> {\n\t\tconst result = new Map<string, ContentBylineCredit[]>();\n\t\tif (contentIds.length === 0) return result;\n\n\t\tconst uniqueContentIds = [...new Set(contentIds)];\n\t\tfor (const chunk of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.id\", \"cb.byline_id\")\n\t\t\t\t.select([\n\t\t\t\t\t\"cb.content_id as content_id\",\n\t\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\t\"b.id as id\",\n\t\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t])\n\t\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t\t.where(\"cb.content_id\", \"in\", chunk)\n\t\t\t\t.orderBy(\"cb.sort_order\", \"asc\")\n\t\t\t\t.execute();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst contentId = row.content_id;\n\t\t\t\tconst credit: ContentBylineCredit = {\n\t\t\t\t\tbyline: rowToByline(row),\n\t\t\t\t\tsortOrder: row.sort_order,\n\t\t\t\t\troleLabel: row.role_label,\n\t\t\t\t};\n\t\t\t\tconst existing = result.get(contentId);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.push(credit);\n\t\t\t\t} else {\n\t\t\t\t\tresult.set(contentId, [credit]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Batch-fetch byline profiles linked to user IDs in a single query.\n\t * Returns a Map keyed by userId.\n\t */\n\tasync findByUserIds(userIds: string[]): Promise<Map<string, BylineSummary>> {\n\t\tconst result = new Map<string, BylineSummary>();\n\t\tif (userIds.length === 0) return result;\n\n\t\tfor (const chunk of chunks(userIds, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.selectAll()\n\t\t\t\t.where(\"user_id\", \"in\", chunk)\n\t\t\t\t.execute();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.user_id) {\n\t\t\t\t\tresult.set(row.user_id, rowToByline(row));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync setContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t\tinputBylines: ContentBylineInput[],\n\t): Promise<ContentBylineCredit[]> {\n\t\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\t\tconst tableName = `ec_${collectionSlug}`;\n\t\tvalidateIdentifier(tableName, \"content table\");\n\n\t\tconst seen = new Set<string>();\n\t\tconst bylines = inputBylines.filter((item) => {\n\t\t\tif (seen.has(item.bylineId)) return false;\n\t\t\tseen.add(item.bylineId);\n\t\t\treturn true;\n\t\t});\n\n\t\t// This method is expected to be called within a transaction context\n\t\t// (content handlers wrap in withTransaction, seed applies sequentially).\n\t\t// All operations use this.db directly -- callers are responsible for\n\t\t// wrapping in a transaction when atomicity is required.\n\t\tif (bylines.length > 0) {\n\t\t\tconst ids = bylines.map((item) => item.bylineId);\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.select(\"id\")\n\t\t\t\t.where(\"id\", \"in\", ids)\n\t\t\t\t.execute();\n\t\t\tif (rows.length !== ids.length) {\n\t\t\t\tthrow new Error(\"One or more byline IDs do not exist\");\n\t\t\t}\n\t\t}\n\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_bylines\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"content_id\", \"=\", contentId)\n\t\t\t.execute();\n\n\t\tfor (let i = 0; i < bylines.length; i++) {\n\t\t\tconst item = bylines[i];\n\t\t\tawait this.db\n\t\t\t\t.insertInto(\"_emdash_content_bylines\")\n\t\t\t\t.values({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\tcollection_slug: collectionSlug,\n\t\t\t\t\tcontent_id: contentId,\n\t\t\t\t\tbyline_id: item.bylineId,\n\t\t\t\t\tsort_order: i,\n\t\t\t\t\trole_label: item.roleLabel ?? null,\n\t\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t\t})\n\t\t\t\t.execute();\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET primary_byline_id = ${bylines[0]?.bylineId ?? null}\n\t\t\tWHERE id = ${contentId}\n\t\t`.execute(this.db);\n\n\t\treturn await this.getContentBylines(collectionSlug, contentId);\n\t}\n}\n"],"mappings":";;;;;;;;;AA2CA,SAAS,YAAY,KAA+B;AACnD,QAAO;EACN,IAAI,IAAI;EACR,MAAM,IAAI;EACV,aAAa,IAAI;EACjB,KAAK,IAAI;EACT,eAAe,IAAI;EACnB,YAAY,IAAI;EAChB,QAAQ,IAAI;EACZ,SAAS,IAAI,aAAa;EAC1B,WAAW,IAAI;EACf,WAAW,IAAI;EACf;;AAGF,IAAa,mBAAb,MAA8B;CAC7B,YAAY,AAAQ,IAAsB;EAAtB;;CAEpB,MAAM,SAAS,IAA2C;EACzD,MAAM,MAAM,MAAM,KAAK,GACrB,WAAW,kBAAkB,CAC7B,WAAW,CACX,MAAM,MAAM,KAAK,GAAG,CACpB,kBAAkB;AACpB,SAAO,MAAM,YAAY,IAAI,GAAG;;CAGjC,MAAM,WAAW,MAA6C;EAC7D,MAAM,MAAM,MAAM,KAAK,GACrB,WAAW,kBAAkB,CAC7B,WAAW,CACX,MAAM,QAAQ,KAAK,KAAK,CACxB,kBAAkB;AACpB,SAAO,MAAM,YAAY,IAAI,GAAG;;CAGjC,MAAM,aAAa,QAA+C;EACjE,MAAM,MAAM,MAAM,KAAK,GACrB,WAAW,kBAAkB,CAC7B,WAAW,CACX,MAAM,WAAW,KAAK,OAAO,CAC7B,kBAAkB;AACpB,SAAO,MAAM,YAAY,IAAI,GAAG;;CAGjC,MAAM,SAAS,SAM4B;EAC1C,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,SAAS,SAAS,IAAI,EAAE,EAAE,IAAI;EAE9D,IAAI,QAAQ,KAAK,GACf,WAAW,kBAAkB,CAC7B,WAAW,CACX,QAAQ,cAAc,OAAO,CAC7B,QAAQ,MAAM,OAAO,CACrB,MAAM,QAAQ,EAAE;AAElB,MAAI,SAAS,QAAQ;GAKpB,MAAM,OAAO,IAJG,QAAQ,OACtB,WAAW,MAAM,OAAO,CACxB,WAAW,KAAK,MAAM,CACtB,WAAW,KAAK,MAAM,CACC;AACzB,WAAQ,MAAM,OAAO,OACpB,GAAG,GAAG,CAAC,GAAG,gBAAgB,QAAQ,KAAK,EAAE,GAAG,QAAQ,QAAQ,KAAK,CAAC,CAAC,CACnE;;AAGF,MAAI,SAAS,YAAY,OACxB,SAAQ,MAAM,MAAM,YAAY,KAAK,QAAQ,UAAU,IAAI,EAAE;AAG9D,MAAI,SAAS,WAAW,OACvB,SAAQ,MAAM,MAAM,WAAW,KAAK,QAAQ,OAAO;AAGpD,MAAI,SAAS,QAAQ;GACpB,MAAM,UAAU,aAAa,QAAQ,OAAO;AAC5C,OAAI,QACH,SAAQ,MAAM,OAAO,OACpB,GAAG,GAAG,CACL,GAAG,cAAc,KAAK,QAAQ,WAAW,EACzC,GAAG,IAAI,CAAC,GAAG,cAAc,KAAK,QAAQ,WAAW,EAAE,GAAG,MAAM,KAAK,QAAQ,GAAG,CAAC,CAAC,CAC9E,CAAC,CACF;;EAIH,MAAM,OAAO,MAAM,MAAM,SAAS;EAClC,MAAM,QAAQ,KAAK,MAAM,GAAG,MAAM,CAAC,IAAI,YAAY;EACnD,MAAM,SAAwC,EAAE,OAAO;AAEvD,MAAI,KAAK,SAAS,OAAO;GACxB,MAAM,OAAO,MAAM,GAAG,GAAG;AACzB,OAAI,KACH,QAAO,aAAa,aAAa,KAAK,WAAW,KAAK,GAAG;;AAI3D,SAAO;;CAGR,MAAM,OAAO,OAAkD;EAC9D,MAAM,KAAK,MAAM;EACjB,MAAM,uBAAM,IAAI,MAAM,EAAC,aAAa;AAEpC,QAAM,KAAK,GACT,WAAW,kBAAkB,CAC7B,OAAO;GACP;GACA,MAAM,MAAM;GACZ,cAAc,MAAM;GACpB,KAAK,MAAM,OAAO;GAClB,iBAAiB,MAAM,iBAAiB;GACxC,aAAa,MAAM,cAAc;GACjC,SAAS,MAAM,UAAU;GACzB,UAAU,MAAM,UAAU,IAAI;GAC9B,YAAY;GACZ,YAAY;GACZ,CAAC,CACD,SAAS;EAEX,MAAM,SAAS,MAAM,KAAK,SAAS,GAAG;AACtC,MAAI,CAAC,OACJ,OAAM,IAAI,MAAM,0BAA0B;AAE3C,SAAO;;CAGR,MAAM,OAAO,IAAY,OAAyD;AAEjF,MAAI,CADa,MAAM,KAAK,SAAS,GAAG,CACzB,QAAO;EAEtB,MAAM,UAAmC,EACxC,6BAAY,IAAI,MAAM,EAAC,aAAa,EACpC;AAED,MAAI,MAAM,SAAS,OAAW,SAAQ,OAAO,MAAM;AACnD,MAAI,MAAM,gBAAgB,OAAW,SAAQ,eAAe,MAAM;AAClE,MAAI,MAAM,QAAQ,OAAW,SAAQ,MAAM,MAAM;AACjD,MAAI,MAAM,kBAAkB,OAAW,SAAQ,kBAAkB,MAAM;AACvE,MAAI,MAAM,eAAe,OAAW,SAAQ,cAAc,MAAM;AAChE,MAAI,MAAM,WAAW,OAAW,SAAQ,UAAU,MAAM;AACxD,MAAI,MAAM,YAAY,OAAW,SAAQ,WAAW,MAAM,UAAU,IAAI;AAExE,QAAM,KAAK,GAAG,YAAY,kBAAkB,CAAC,IAAI,QAAQ,CAAC,MAAM,MAAM,KAAK,GAAG,CAAC,SAAS;AACxF,SAAO,MAAM,KAAK,SAAS,GAAG;;CAG/B,MAAM,OAAO,IAA8B;AAE1C,MAAI,CADa,MAAM,KAAK,SAAS,GAAG,CACzB,QAAO;AAEtB,QAAM,gBAAgB,KAAK,IAAI,OAAO,QAAQ;AAC7C,SAAM,IAAI,WAAW,0BAA0B,CAAC,MAAM,aAAa,KAAK,GAAG,CAAC,SAAS;AAErF,SAAM,IAAI,WAAW,kBAAkB,CAAC,MAAM,MAAM,KAAK,GAAG,CAAC,SAAS;GAEtE,MAAM,aAAa,MAAM,eAAe,KAAK,OAAO;AACpD,QAAK,MAAM,aAAa,YAAY;AACnC,uBAAmB,WAAW,gBAAgB;AAC9C,UAAM,GAAG;cACC,IAAI,IAAI,UAAU,CAAC;;iCAEA,GAAG;MAC9B,QAAQ,IAAI;;IAEd;AAEF,SAAO;;CAGR,MAAM,kBACL,gBACA,WACiC;AAuBjC,UAtBa,MAAM,KAAK,GACtB,WAAW,gCAAgC,CAC3C,UAAU,wBAAwB,QAAQ,eAAe,CACzD,OAAO;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,CAAC,CACD,MAAM,sBAAsB,KAAK,eAAe,CAChD,MAAM,iBAAiB,KAAK,UAAU,CACtC,QAAQ,iBAAiB,MAAM,CAC/B,SAAS,EAEC,KAAK,SAAS;GACzB,QAAQ,YAAY,IAAI;GACxB,WAAW,IAAI;GACf,WAAW,IAAI;GACf,EAAE;;;;;;CAOJ,MAAM,sBACL,gBACA,YAC8C;EAC9C,MAAM,yBAAS,IAAI,KAAoC;AACvD,MAAI,WAAW,WAAW,EAAG,QAAO;EAEpC,MAAM,mBAAmB,CAAC,GAAG,IAAI,IAAI,WAAW,CAAC;AACjD,OAAK,MAAM,SAAS,OAAO,kBAAkB,eAAe,EAAE;GAC7D,MAAM,OAAO,MAAM,KAAK,GACtB,WAAW,gCAAgC,CAC3C,UAAU,wBAAwB,QAAQ,eAAe,CACzD,OAAO;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,CAAC,CACD,MAAM,sBAAsB,KAAK,eAAe,CAChD,MAAM,iBAAiB,MAAM,MAAM,CACnC,QAAQ,iBAAiB,MAAM,CAC/B,SAAS;AAEX,QAAK,MAAM,OAAO,MAAM;IACvB,MAAM,YAAY,IAAI;IACtB,MAAM,SAA8B;KACnC,QAAQ,YAAY,IAAI;KACxB,WAAW,IAAI;KACf,WAAW,IAAI;KACf;IACD,MAAM,WAAW,OAAO,IAAI,UAAU;AACtC,QAAI,SACH,UAAS,KAAK,OAAO;QAErB,QAAO,IAAI,WAAW,CAAC,OAAO,CAAC;;;AAKlC,SAAO;;;;;;CAOR,MAAM,cAAc,SAAwD;EAC3E,MAAM,yBAAS,IAAI,KAA4B;AAC/C,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,OAAK,MAAM,SAAS,OAAO,SAAS,eAAe,EAAE;GACpD,MAAM,OAAO,MAAM,KAAK,GACtB,WAAW,kBAAkB,CAC7B,WAAW,CACX,MAAM,WAAW,MAAM,MAAM,CAC7B,SAAS;AAEX,QAAK,MAAM,OAAO,KACjB,KAAI,IAAI,QACP,QAAO,IAAI,IAAI,SAAS,YAAY,IAAI,CAAC;;AAI5C,SAAO;;CAGR,MAAM,kBACL,gBACA,WACA,cACiC;AACjC,qBAAmB,gBAAgB,kBAAkB;EACrD,MAAM,YAAY,MAAM;AACxB,qBAAmB,WAAW,gBAAgB;EAE9C,MAAM,uBAAO,IAAI,KAAa;EAC9B,MAAM,UAAU,aAAa,QAAQ,SAAS;AAC7C,OAAI,KAAK,IAAI,KAAK,SAAS,CAAE,QAAO;AACpC,QAAK,IAAI,KAAK,SAAS;AACvB,UAAO;IACN;AAMF,MAAI,QAAQ,SAAS,GAAG;GACvB,MAAM,MAAM,QAAQ,KAAK,SAAS,KAAK,SAAS;AAMhD,QALa,MAAM,KAAK,GACtB,WAAW,kBAAkB,CAC7B,OAAO,KAAK,CACZ,MAAM,MAAM,MAAM,IAAI,CACtB,SAAS,EACF,WAAW,IAAI,OACvB,OAAM,IAAI,MAAM,sCAAsC;;AAIxD,QAAM,KAAK,GACT,WAAW,0BAA0B,CACrC,MAAM,mBAAmB,KAAK,eAAe,CAC7C,MAAM,cAAc,KAAK,UAAU,CACnC,SAAS;AAEX,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACxC,MAAM,OAAO,QAAQ;AACrB,SAAM,KAAK,GACT,WAAW,0BAA0B,CACrC,OAAO;IACP,IAAI,MAAM;IACV,iBAAiB;IACjB,YAAY;IACZ,WAAW,KAAK;IAChB,YAAY;IACZ,YAAY,KAAK,aAAa;IAC9B,6BAAY,IAAI,MAAM,EAAC,aAAa;IACpC,CAAC,CACD,SAAS;;AAGZ,QAAM,GAAG;YACC,IAAI,IAAI,UAAU,CAAC;6BACF,QAAQ,IAAI,YAAY,KAAK;gBAC1C,UAAU;IACtB,QAAQ,KAAK,GAAG;AAElB,SAAO,MAAM,KAAK,kBAAkB,gBAAgB,UAAU"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"content-BsBoyj8G.mjs","names":[],"sources":["../src/utils/slugify.ts","../src/database/repositories/revision.ts","../src/database/repositories/content.ts"],"sourcesContent":["// Regex patterns for slug normalization\nconst DIACRITICS_PATTERN = /[\\u0300-\\u036f]/g;\nconst WHITESPACE_UNDERSCORE_PATTERN = /[\\s_]+/g;\nconst NON_ALPHANUMERIC_HYPHEN_PATTERN = /[^a-z0-9-]/g;\nconst MULTIPLE_HYPHENS_PATTERN = /-+/g;\nconst LEADING_TRAILING_HYPHEN_PATTERN = /^-|-$/g;\nconst TRAILING_HYPHEN_PATTERN = /-$/;\n\n/**\n * Convert a string to a URL-friendly slug.\n *\n * Handles unicode by normalizing to NFD and stripping diacritics,\n * so \"café\" becomes \"cafe\", \"naïve\" becomes \"naive\", etc.\n */\n/**\n * Decode a URI-encoded slug parameter.\n *\n * Browsers percent-encode non-ASCII characters in URLs, so a slug like\n * \"మేష-రాసి\" arrives as \"%e0%b0%ae%e0%b1%87%e0%b0%b7-%e0%b0%b0%e0%b0%be%e0%b0%b8%e0%b0%bf\".\n * Call this on `Astro.params.slug` before using it in database lookups.\n */\nexport function decodeSlug(raw: string | undefined): string | undefined {\n\treturn raw ? decodeURIComponent(raw) : undefined;\n}\n\nexport function slugify(text: string, maxLength: number = 80): string {\n\treturn (\n\t\ttext\n\t\t\t.toLowerCase()\n\t\t\t.normalize(\"NFD\")\n\t\t\t.replace(DIACRITICS_PATTERN, \"\")\n\t\t\t.replace(WHITESPACE_UNDERSCORE_PATTERN, \"-\")\n\t\t\t.replace(NON_ALPHANUMERIC_HYPHEN_PATTERN, \"\")\n\t\t\t.replace(MULTIPLE_HYPHENS_PATTERN, \"-\")\n\t\t\t.replace(LEADING_TRAILING_HYPHEN_PATTERN, \"\")\n\t\t\t.slice(0, maxLength)\n\t\t\t// Clean trailing hyphen from truncation\n\t\t\t.replace(TRAILING_HYPHEN_PATTERN, \"\")\n\t);\n}\n","import type { Kysely } from \"kysely\";\nimport { monotonicFactory } from \"ulidx\";\n\nimport type { Database, RevisionTable } from \"../types.js\";\n\nconst monotonic = monotonicFactory();\n\nexport interface Revision {\n\tid: string;\n\tcollection: string;\n\tentryId: string;\n\tdata: Record<string, unknown>;\n\tauthorId: string | null;\n\tcreatedAt: string;\n}\n\nexport interface CreateRevisionInput {\n\tcollection: string;\n\tentryId: string;\n\tdata: Record<string, unknown>;\n\tauthorId?: string;\n}\n\n/**\n * Revision repository for version history\n *\n * Each revision stores a JSON snapshot of the content at a point in time.\n * Used when collection has `supports: [\"revisions\"]` enabled.\n */\nexport class RevisionRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Create a new revision\n\t */\n\tasync create(input: CreateRevisionInput): Promise<Revision> {\n\t\tconst id = monotonic();\n\n\t\tconst row: Omit<RevisionTable, \"created_at\"> = {\n\t\t\tid,\n\t\t\tcollection: input.collection,\n\t\t\tentry_id: input.entryId,\n\t\t\tdata: JSON.stringify(input.data),\n\t\t\tauthor_id: input.authorId ?? null,\n\t\t};\n\n\t\tawait this.db.insertInto(\"revisions\").values(row).execute();\n\n\t\tconst revision = await this.findById(id);\n\t\tif (!revision) {\n\t\t\tthrow new Error(\"Failed to create revision\");\n\t\t}\n\t\treturn revision;\n\t}\n\n\t/**\n\t * Find revision by ID\n\t */\n\tasync findById(id: string): Promise<Revision | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.rowToRevision(row) : null;\n\t}\n\n\t/**\n\t * Get all revisions for an entry (newest first)\n\t *\n\t * Orders by monotonic ULID (descending). The monotonic factory\n\t * guarantees strictly increasing IDs even within the same millisecond.\n\t */\n\tasync findByEntry(\n\t\tcollection: string,\n\t\tentryId: string,\n\t\toptions: { limit?: number } = {},\n\t): Promise<Revision[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.selectAll()\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.orderBy(\"id\", \"desc\");\n\n\t\tif (options.limit) {\n\t\t\tquery = query.limit(options.limit);\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row) => this.rowToRevision(row));\n\t}\n\n\t/**\n\t * Get the most recent revision for an entry\n\t */\n\tasync findLatest(collection: string, entryId: string): Promise<Revision | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.selectAll()\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(1)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.rowToRevision(row) : null;\n\t}\n\n\t/**\n\t * Count revisions for an entry\n\t */\n\tasync countByEntry(collection: string, entryId: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result?.count || 0);\n\t}\n\n\t/**\n\t * Delete all revisions for an entry (use when entry is deleted)\n\t */\n\tasync deleteByEntry(collection: string, entryId: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"revisions\")\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\t/**\n\t * Delete old revisions, keeping the most recent N\n\t */\n\tasync pruneOldRevisions(collection: string, entryId: string, keepCount: number): Promise<number> {\n\t\t// Get IDs of revisions to keep\n\t\tconst keep = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\") // ULID tiebreaker\n\t\t\t.limit(keepCount)\n\t\t\t.execute();\n\n\t\tconst keepIds = keep.map((r) => r.id);\n\n\t\tif (keepIds.length === 0) return 0;\n\n\t\t// Delete everything else for this entry\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"revisions\")\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.where(\"id\", \"not in\", keepIds)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\t/**\n\t * Update revision data in place\n\t * Used for autosave to avoid creating many small revisions.\n\t */\n\tasync updateData(id: string, data: Record<string, unknown>): Promise<void> {\n\t\tawait this.db\n\t\t\t.updateTable(\"revisions\")\n\t\t\t.set({ data: JSON.stringify(data) })\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.execute();\n\t}\n\n\t/**\n\t * Convert database row to Revision object\n\t */\n\tprivate rowToRevision(row: {\n\t\tid: string;\n\t\tcollection: string;\n\t\tentry_id: string;\n\t\tdata: string;\n\t\tauthor_id: string | null;\n\t\tcreated_at: string;\n\t}): Revision {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tcollection: row.collection,\n\t\t\tentryId: row.entry_id,\n\t\t\tdata: JSON.parse(row.data),\n\t\t\tauthorId: row.author_id,\n\t\t\tcreatedAt: row.created_at,\n\t\t};\n\t}\n}\n","import { sql, type Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { slugify } from \"../../utils/slugify.js\";\nimport type { Database } from \"../types.js\";\nimport { validateIdentifier } from \"../validate.js\";\nimport { RevisionRepository } from \"./revision.js\";\nimport type {\n\tCreateContentInput,\n\tUpdateContentInput,\n\tFindManyOptions,\n\tFindManyResult,\n\tContentItem,\n} from \"./types.js\";\nimport { EmDashValidationError, encodeCursor, decodeCursor } from \"./types.js\";\n\n// Regex pattern for ULID validation\nconst ULID_PATTERN = /^[0-9A-Z]{26}$/;\n\n/**\n * System columns that exist in every ec_* table\n */\nconst SYSTEM_COLUMNS = new Set([\n\t\"id\",\n\t\"slug\",\n\t\"status\",\n\t\"author_id\",\n\t\"primary_byline_id\",\n\t\"created_at\",\n\t\"updated_at\",\n\t\"published_at\",\n\t\"scheduled_at\",\n\t\"deleted_at\",\n\t\"version\",\n\t\"live_revision_id\",\n\t\"draft_revision_id\",\n\t\"locale\",\n\t\"translation_group\",\n]);\n\n/**\n * Get the table name for a collection type\n */\nfunction getTableName(type: string): string {\n\tvalidateIdentifier(type, \"collection type\");\n\treturn `ec_${type}`;\n}\n\n/**\n * Serialize a value for database storage\n * Objects/arrays are JSON-stringified\n * Booleans are converted to 0/1 for SQLite\n */\nfunction serializeValue(value: unknown): unknown {\n\tif (value === null || value === undefined) {\n\t\treturn null;\n\t}\n\tif (typeof value === \"boolean\") {\n\t\treturn value ? 1 : 0;\n\t}\n\tif (typeof value === \"object\") {\n\t\treturn JSON.stringify(value);\n\t}\n\treturn value;\n}\n\n/**\n * Deserialize a value from database storage\n * Attempts to parse JSON strings that look like objects/arrays\n */\nfunction deserializeValue(value: unknown): unknown {\n\tif (typeof value === \"string\") {\n\t\t// Try to parse if it looks like JSON\n\t\tif (value.startsWith(\"{\") || value.startsWith(\"[\")) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(value);\n\t\t\t} catch {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t}\n\treturn value;\n}\n\n/** Pattern for escaping special regex characters */\nconst REGEX_ESCAPE_PATTERN = /[.*+?^${}()|[\\]\\\\]/g;\n\n/**\n * Escape special regex characters in a string for use in `new RegExp()`\n */\nfunction escapeRegExp(s: string): string {\n\treturn s.replace(REGEX_ESCAPE_PATTERN, \"\\\\$&\");\n}\n\n/**\n * Repository for content CRUD operations\n *\n * Content is stored in per-collection tables (ec_posts, ec_pages, etc.)\n * Each field becomes a real column in the table.\n */\nexport class ContentRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Create a new content item\n\t */\n\tasync create(input: CreateContentInput): Promise<ContentItem> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tconst {\n\t\t\ttype,\n\t\t\tslug,\n\t\t\tdata,\n\t\t\tstatus = \"draft\",\n\t\t\tauthorId,\n\t\t\tprimaryBylineId,\n\t\t\tlocale,\n\t\t\ttranslationOf,\n\t\t\tpublishedAt,\n\t\t\tcreatedAt,\n\t\t} = input;\n\n\t\t// Validate required fields\n\t\tif (!type) {\n\t\t\tthrow new EmDashValidationError(\"Content type is required\");\n\t\t}\n\n\t\tconst tableName = getTableName(type);\n\n\t\t// Resolve translation_group: if translationOf is set, look up the source item's group\n\t\tlet translationGroup: string = id; // default: self-reference\n\t\tif (translationOf) {\n\t\t\tconst source = await this.findById(type, translationOf);\n\t\t\tif (!source) {\n\t\t\t\tthrow new EmDashValidationError(\"Translation source content not found\");\n\t\t\t}\n\t\t\ttranslationGroup = source.translationGroup || source.id;\n\t\t}\n\n\t\t// Build column names and values\n\t\tconst columns: string[] = [\n\t\t\t\"id\",\n\t\t\t\"slug\",\n\t\t\t\"status\",\n\t\t\t\"author_id\",\n\t\t\t\"primary_byline_id\",\n\t\t\t\"created_at\",\n\t\t\t\"updated_at\",\n\t\t\t\"published_at\",\n\t\t\t\"version\",\n\t\t\t\"locale\",\n\t\t\t\"translation_group\",\n\t\t];\n\t\tconst values: unknown[] = [\n\t\t\tid,\n\t\t\tslug || null,\n\t\t\tstatus,\n\t\t\tauthorId || null,\n\t\t\tprimaryBylineId ?? null,\n\t\t\tcreatedAt || now,\n\t\t\tnow,\n\t\t\tpublishedAt || null,\n\t\t\t1,\n\t\t\tlocale || \"en\",\n\t\t\ttranslationGroup,\n\t\t];\n\n\t\t// Add data fields as columns (skip system columns to prevent injection via data)\n\t\tif (data && typeof data === \"object\") {\n\t\t\tfor (const [key, value] of Object.entries(data)) {\n\t\t\t\tif (!SYSTEM_COLUMNS.has(key)) {\n\t\t\t\t\tvalidateIdentifier(key, \"content field name\");\n\t\t\t\t\tcolumns.push(key);\n\t\t\t\t\tvalues.push(serializeValue(value));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Build dynamic INSERT using raw SQL\n\t\tconst columnRefs = columns.map((c) => sql.ref(c));\n\t\tconst valuePlaceholders = values.map((v) => (v === null ? sql`NULL` : sql`${v}`));\n\n\t\tawait sql`\n\t\t\tINSERT INTO ${sql.ref(tableName)} (${sql.join(columnRefs, sql`, `)})\n\t\t\tVALUES (${sql.join(valuePlaceholders, sql`, `)})\n\t\t`.execute(this.db);\n\n\t\t// Fetch and return the created item\n\t\tconst item = await this.findById(type, id);\n\t\tif (!item) {\n\t\t\tthrow new Error(\"Failed to create content\");\n\t\t}\n\t\treturn item;\n\t}\n\n\t/**\n\t * Generate a unique slug for a content item within a collection.\n\t *\n\t * Checks the collection table for existing slugs that match `baseSlug`\n\t * (optionally scoped to a locale) and appends a numeric suffix (`-1`,\n\t * `-2`, etc.) on collision to guarantee uniqueness.\n\t *\n\t * Returns `null` if `baseSlug` is empty after slugification.\n\t */\n\tasync generateUniqueSlug(type: string, text: string, locale?: string): Promise<string | null> {\n\t\tconst baseSlug = slugify(text);\n\t\tif (!baseSlug) return null;\n\n\t\tconst tableName = getTableName(type);\n\n\t\t// Check if the base slug is available\n\t\tconst existing = locale\n\t\t\t? await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${baseSlug}\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${baseSlug}\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db);\n\n\t\tif (existing.rows.length === 0) {\n\t\t\treturn baseSlug;\n\t\t}\n\n\t\t// Find all slugs matching the pattern `baseSlug` or `baseSlug-N`\n\t\tconst pattern = `${baseSlug}-%`;\n\t\tconst candidates = locale\n\t\t\t? await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE (slug = ${baseSlug} OR slug LIKE ${pattern})\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${baseSlug} OR slug LIKE ${pattern}\n\t\t\t\t`.execute(this.db);\n\n\t\t// Find the highest numeric suffix in use\n\t\tlet maxSuffix = 0;\n\t\tconst suffixPattern = new RegExp(`^${escapeRegExp(baseSlug)}-(\\\\d+)$`);\n\t\tfor (const row of candidates.rows) {\n\t\t\tconst match = suffixPattern.exec(row.slug);\n\t\t\tif (match) {\n\t\t\t\tconst n = parseInt(match[1], 10);\n\t\t\t\tif (n > maxSuffix) maxSuffix = n;\n\t\t\t}\n\t\t}\n\n\t\treturn `${baseSlug}-${maxSuffix + 1}`;\n\t}\n\n\t/**\n\t * Duplicate a content item\n\t * Creates a new draft copy with \"(Copy)\" appended to the title.\n\t * A slug is auto-generated from the new title by the handler layer.\n\t */\n\tasync duplicate(type: string, id: string, authorId?: string): Promise<ContentItem> {\n\t\t// Fetch the original item\n\t\tconst original = await this.findById(type, id);\n\t\tif (!original) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// Prepare the new data\n\t\tconst newData = { ...original.data };\n\n\t\t// Append \"(Copy)\" to title if present\n\t\tif (typeof newData.title === \"string\") {\n\t\t\tnewData.title = `${newData.title} (Copy)`;\n\t\t} else if (typeof newData.name === \"string\") {\n\t\t\tnewData.name = `${newData.name} (Copy)`;\n\t\t}\n\n\t\t// Auto-generate a unique slug from the new title/name\n\t\tconst slugSource =\n\t\t\ttypeof newData.title === \"string\"\n\t\t\t\t? newData.title\n\t\t\t\t: typeof newData.name === \"string\"\n\t\t\t\t\t? newData.name\n\t\t\t\t\t: null;\n\n\t\tconst slug = slugSource\n\t\t\t? await this.generateUniqueSlug(type, slugSource, original.locale ?? undefined)\n\t\t\t: null;\n\n\t\t// Create the duplicate as a draft — use override authorId if provided (caller owns the copy)\n\t\treturn this.create({\n\t\t\ttype,\n\t\t\tslug,\n\t\t\tdata: newData,\n\t\t\tstatus: \"draft\",\n\t\t\tauthorId: authorId || original.authorId || undefined,\n\t\t});\n\t}\n\n\t/**\n\t * Find content by ID\n\t */\n\tasync findById(type: string, id: string): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find content by id, including trashed (soft-deleted) items.\n\t * Used by restore endpoint for ownership checks.\n\t */\n\tasync findByIdIncludingTrashed(type: string, id: string): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE id = ${id}\n\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find content by ID or slug. Tries ID first if it looks like a ULID,\n\t * otherwise tries slug. Falls back to the other if the first lookup misses.\n\t */\n\tasync findByIdOrSlug(\n\t\ttype: string,\n\t\tidentifier: string,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\treturn this._findByIdOrSlug(type, identifier, false, locale);\n\t}\n\n\t/**\n\t * Find content by ID or slug, including trashed (soft-deleted) items.\n\t * Used by restore/permanent-delete endpoints.\n\t */\n\tasync findByIdOrSlugIncludingTrashed(\n\t\ttype: string,\n\t\tidentifier: string,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\treturn this._findByIdOrSlug(type, identifier, true, locale);\n\t}\n\n\tprivate async _findByIdOrSlug(\n\t\ttype: string,\n\t\tidentifier: string,\n\t\tincludeTrashed: boolean,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\t// ULIDs are 26 uppercase alphanumeric chars\n\t\tconst looksLikeUlid = ULID_PATTERN.test(identifier);\n\n\t\tconst findById = includeTrashed\n\t\t\t? (t: string, id: string) => this.findByIdIncludingTrashed(t, id)\n\t\t\t: (t: string, id: string) => this.findById(t, id);\n\t\tconst findBySlug = includeTrashed\n\t\t\t? (t: string, s: string) => this.findBySlugIncludingTrashed(t, s, locale)\n\t\t\t: (t: string, s: string) => this.findBySlug(t, s, locale);\n\n\t\tif (looksLikeUlid) {\n\t\t\t// Try ID first, fall back to slug\n\t\t\tconst byId = await findById(type, identifier);\n\t\t\tif (byId) return byId;\n\t\t\treturn findBySlug(type, identifier);\n\t\t}\n\t\t// Try slug first, fall back to ID\n\t\tconst bySlug = await findBySlug(type, identifier);\n\t\tif (bySlug) return bySlug;\n\t\treturn findById(type, identifier);\n\t}\n\n\t/**\n\t * Find content by slug\n\t */\n\tasync findBySlug(type: string, slug: string, locale?: string): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = locale\n\t\t\t? await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t\tAND deleted_at IS NULL\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tAND deleted_at IS NULL\n\t\t\t\t\tORDER BY locale ASC\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find content by slug, including trashed (soft-deleted) items.\n\t * Used by restore/permanent-delete endpoints.\n\t */\n\tasync findBySlugIncludingTrashed(\n\t\ttype: string,\n\t\tslug: string,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = locale\n\t\t\t? await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tORDER BY locale ASC\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find many content items with filtering and pagination\n\t */\n\tasync findMany(\n\t\ttype: string,\n\t\toptions: FindManyOptions = {},\n\t): Promise<FindManyResult<ContentItem>> {\n\t\tconst tableName = getTableName(type);\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\t// Determine ordering\n\t\tconst orderField = options.orderBy?.field || \"createdAt\";\n\t\tconst orderDirection = options.orderBy?.direction || \"desc\";\n\t\tconst dbField = this.mapOrderField(orderField);\n\n\t\t// Validate order direction to prevent injection\n\t\tconst safeOrderDirection = orderDirection.toLowerCase() === \"asc\" ? \"ASC\" : \"DESC\";\n\n\t\t// Build query with parameterized values (no string interpolation)\n\t\t// Note: Dynamic content tables have deleted_at column, cast needed for Kysely\n\t\tlet query = this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.selectAll()\n\t\t\t.where(\"deleted_at\" as never, \"is\", null);\n\n\t\t// Apply filters with parameterized queries\n\t\tif (options.where?.status) {\n\t\t\tquery = query.where(\"status\", \"=\", options.where.status);\n\t\t}\n\n\t\tif (options.where?.authorId) {\n\t\t\tquery = query.where(\"author_id\", \"=\", options.where.authorId);\n\t\t}\n\n\t\tif (options.where?.locale) {\n\t\t\tquery = query.where(\"locale\" as any, \"=\", options.where.locale);\n\t\t}\n\n\t\t// Handle cursor pagination\n\t\tif (options.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tif (decoded) {\n\t\t\t\tconst { orderValue, id: cursorId } = decoded;\n\n\t\t\t\tif (safeOrderDirection === \"DESC\") {\n\t\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(dbField as any, \"<\", orderValue),\n\t\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \"<\", cursorId)]),\n\t\t\t\t\t\t]),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(dbField as any, \">\", orderValue),\n\t\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \">\", cursorId)]),\n\t\t\t\t\t\t]),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Apply ordering and limit\n\t\tquery = query\n\t\t\t.orderBy(dbField as any, safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.orderBy(\"id\", safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tconst rows = await query.execute();\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit);\n\n\t\tconst mappedResult: FindManyResult<ContentItem> = {\n\t\t\titems: items.map((row) => this.mapRow(type, row as Record<string, unknown>)),\n\t\t};\n\n\t\tif (hasMore && items.length > 0) {\n\t\t\tconst lastRow = items.at(-1) as Record<string, unknown>;\n\t\t\tconst lastOrderValue = lastRow[dbField];\n\t\t\tconst orderStr =\n\t\t\t\ttypeof lastOrderValue === \"string\" || typeof lastOrderValue === \"number\"\n\t\t\t\t\t? String(lastOrderValue)\n\t\t\t\t\t: \"\";\n\t\t\tmappedResult.nextCursor = encodeCursor(orderStr, String(lastRow.id));\n\t\t}\n\n\t\treturn mappedResult;\n\t}\n\n\t/**\n\t * Update content\n\t */\n\tasync update(type: string, id: string, input: UpdateContentInput): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\t// Build update object with parameterized values\n\t\tconst updates: Record<string, unknown> = {\n\t\t\tupdated_at: now,\n\t\t\tversion: sql`version + 1`,\n\t\t};\n\n\t\tif (input.status !== undefined) {\n\t\t\tupdates.status = input.status;\n\t\t}\n\n\t\tif (input.slug !== undefined) {\n\t\t\tupdates.slug = input.slug;\n\t\t}\n\n\t\tif (input.publishedAt !== undefined) {\n\t\t\tupdates.published_at = input.publishedAt;\n\t\t}\n\n\t\tif (input.scheduledAt !== undefined) {\n\t\t\tupdates.scheduled_at = input.scheduledAt;\n\t\t}\n\n\t\tif (input.authorId !== undefined) {\n\t\t\tupdates.author_id = input.authorId;\n\t\t}\n\n\t\tif (input.primaryBylineId !== undefined) {\n\t\t\tupdates.primary_byline_id = input.primaryBylineId;\n\t\t}\n\n\t\t// Update data fields (skip system columns to prevent injection via data)\n\t\tif (input.data !== undefined && typeof input.data === \"object\") {\n\t\t\tfor (const [key, value] of Object.entries(input.data)) {\n\t\t\t\tif (!SYSTEM_COLUMNS.has(key)) {\n\t\t\t\t\tvalidateIdentifier(key, \"content field name\");\n\t\t\t\t\tupdates[key] = serializeValue(value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tawait this.db\n\t\t\t.updateTable(tableName as keyof Database)\n\t\t\t.set(updates)\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.where(\"deleted_at\" as never, \"is\", null)\n\t\t\t.execute();\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Delete content (soft delete - moves to trash)\n\t */\n\tasync delete(type: string, id: string): Promise<boolean> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst result = await sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET deleted_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\treturn (result.numAffectedRows ?? 0n) > 0n;\n\t}\n\n\t/**\n\t * Restore content from trash\n\t */\n\tasync restore(type: string, id: string): Promise<boolean> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET deleted_at = NULL\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NOT NULL\n\t\t`.execute(this.db);\n\n\t\treturn (result.numAffectedRows ?? 0n) > 0n;\n\t}\n\n\t/**\n\t * Permanently delete content (cannot be undone)\n\t */\n\tasync permanentDelete(type: string, id: string): Promise<boolean> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql`\n\t\t\tDELETE FROM ${sql.ref(tableName)}\n\t\t\tWHERE id = ${id}\n\t\t`.execute(this.db);\n\n\t\treturn (result.numAffectedRows ?? 0n) > 0n;\n\t}\n\n\t/**\n\t * Find trashed content items\n\t */\n\tasync findTrashed(\n\t\ttype: string,\n\t\toptions: Omit<FindManyOptions, \"where\"> = {},\n\t): Promise<FindManyResult<ContentItem & { deletedAt: string }>> {\n\t\tconst tableName = getTableName(type);\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\t// Determine ordering - default to most recently deleted\n\t\tconst orderField = options.orderBy?.field || \"deletedAt\";\n\t\tconst orderDirection = options.orderBy?.direction || \"desc\";\n\t\tconst dbField = this.mapOrderField(orderField);\n\n\t\tconst safeOrderDirection = orderDirection.toLowerCase() === \"asc\" ? \"ASC\" : \"DESC\";\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.selectAll()\n\t\t\t.where(\"deleted_at\" as never, \"is not\", null);\n\n\t\t// Handle cursor pagination\n\t\tif (options.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tif (decoded) {\n\t\t\t\tconst { orderValue, id: cursorId } = decoded;\n\n\t\t\t\tif (safeOrderDirection === \"DESC\") {\n\t\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(dbField as any, \"<\", orderValue),\n\t\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \"<\", cursorId)]),\n\t\t\t\t\t\t]),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(dbField as any, \">\", orderValue),\n\t\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \">\", cursorId)]),\n\t\t\t\t\t\t]),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tquery = query\n\t\t\t.orderBy(dbField as any, safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.orderBy(\"id\", safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tconst rows = await query.execute();\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit);\n\n\t\tconst mappedResult: FindManyResult<ContentItem & { deletedAt: string }> = {\n\t\t\titems: items.map((row) => {\n\t\t\t\tconst record = row as Record<string, unknown>;\n\t\t\t\treturn {\n\t\t\t\t\t...this.mapRow(type, record),\n\t\t\t\t\tdeletedAt: typeof record.deleted_at === \"string\" ? record.deleted_at : \"\",\n\t\t\t\t};\n\t\t\t}),\n\t\t};\n\n\t\tif (hasMore && items.length > 0) {\n\t\t\tconst lastRow = items.at(-1) as Record<string, unknown>;\n\t\t\tconst lastOrderValue = lastRow[dbField];\n\t\t\tconst orderStr =\n\t\t\t\ttypeof lastOrderValue === \"string\" || typeof lastOrderValue === \"number\"\n\t\t\t\t\t? String(lastOrderValue)\n\t\t\t\t\t: \"\";\n\t\t\tmappedResult.nextCursor = encodeCursor(orderStr, String(lastRow.id));\n\t\t}\n\n\t\treturn mappedResult;\n\t}\n\n\t/**\n\t * Count trashed content items\n\t */\n\tasync countTrashed(type: string): Promise<number> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"deleted_at\" as never, \"is not\", null)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result?.count || 0);\n\t}\n\n\t/**\n\t * Count content items\n\t */\n\tasync count(\n\t\ttype: string,\n\t\twhere?: { status?: string; authorId?: string; locale?: string },\n\t): Promise<number> {\n\t\tconst tableName = getTableName(type);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"deleted_at\" as never, \"is\", null);\n\n\t\tif (where?.status) {\n\t\t\tquery = query.where(\"status\", \"=\", where.status);\n\t\t}\n\n\t\tif (where?.authorId) {\n\t\t\tquery = query.where(\"author_id\", \"=\", where.authorId);\n\t\t}\n\n\t\tif (where?.locale) {\n\t\t\tquery = query.where(\"locale\" as any, \"=\", where.locale);\n\t\t}\n\n\t\tconst result = await query.executeTakeFirst();\n\t\treturn Number(result?.count || 0);\n\t}\n\n\t// get overall statistics (total, published, draft) for a content type in a single query\n\tasync getStats(type: string): Promise<{ total: number; published: number; draft: number }> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.select((eb) => [\n\t\t\t\teb.fn.count(\"id\").as(\"total\"),\n\t\t\t\teb.fn.sum(eb.case().when(\"status\", \"=\", \"published\").then(1).else(0).end()).as(\"published\"),\n\t\t\t\teb.fn.sum(eb.case().when(\"status\", \"=\", \"draft\").then(1).else(0).end()).as(\"draft\"),\n\t\t\t])\n\t\t\t.where(\"deleted_at\" as never, \"is\", null)\n\t\t\t.executeTakeFirst();\n\n\t\treturn {\n\t\t\ttotal: Number(result?.total || 0),\n\t\t\tpublished: Number(result?.published || 0),\n\t\t\tdraft: Number(result?.draft || 0),\n\t\t};\n\t}\n\n\t/**\n\t * Schedule content for future publishing\n\t *\n\t * Sets status to 'scheduled' and stores the scheduled publish time.\n\t * The content will be auto-published when the scheduled time is reached.\n\t */\n\tasync schedule(type: string, id: string, scheduledAt: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\t// Validate scheduledAt is in the future\n\t\tconst scheduledDate = new Date(scheduledAt);\n\t\tif (isNaN(scheduledDate.getTime())) {\n\t\t\tthrow new EmDashValidationError(\"Invalid scheduled date\");\n\t\t}\n\t\tif (scheduledDate <= new Date()) {\n\t\t\tthrow new EmDashValidationError(\"Scheduled date must be in the future\");\n\t\t}\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// Published posts keep their status — the schedule applies to the\n\t\t// pending draft, not the currently-live revision. Unpublished posts\n\t\t// transition to 'scheduled' so they aren't visible before the time.\n\t\tconst newStatus = existing.status === \"published\" ? \"published\" : \"scheduled\";\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET status = ${newStatus},\n\t\t\t\tscheduled_at = ${scheduledAt},\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Unschedule content\n\t *\n\t * Clears the scheduled time. Published posts stay published;\n\t * draft/scheduled posts revert to 'draft'.\n\t */\n\tasync unschedule(type: string, id: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// Published posts keep their status — just clear the pending schedule.\n\t\t// Draft/scheduled posts revert to 'draft'.\n\t\tconst newStatus = existing.status === \"published\" ? \"published\" : \"draft\";\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET status = ${newStatus},\n\t\t\t\tscheduled_at = NULL,\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND scheduled_at IS NOT NULL\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Find content that is ready to be published\n\t *\n\t * Returns all content where scheduled_at <= now, regardless of status.\n\t * This covers both draft-scheduled posts (status='scheduled') and\n\t * published posts with scheduled draft changes (status='published').\n\t */\n\tasync findReadyToPublish(type: string): Promise<ContentItem[]> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t\tAND scheduled_at <= ${now}\n\t\t\tAND deleted_at IS NULL\n\t\t\tORDER BY scheduled_at ASC\n\t\t`.execute(this.db);\n\n\t\treturn result.rows.map((row) => this.mapRow(type, row));\n\t}\n\n\t/**\n\t * Find all translations in a translation group\n\t */\n\tasync findTranslations(type: string, translationGroup: string): Promise<ContentItem[]> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE translation_group = ${translationGroup}\n\t\t\tAND deleted_at IS NULL\n\t\t\tORDER BY locale ASC\n\t\t`.execute(this.db);\n\n\t\treturn result.rows.map((row) => this.mapRow(type, row));\n\t}\n\n\t/**\n\t * Publish the current draft\n\t *\n\t * Promotes draft_revision_id to live_revision_id and clears draft pointer.\n\t * Syncs the draft revision's data into the content table columns so the\n\t * content table always reflects the published version.\n\t * If no draft revision exists, creates one from current data and publishes it.\n\t */\n\tasync publish(type: string, id: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\tconst revisionRepo = new RevisionRepository(this.db);\n\t\tlet revisionToPublish = existing.draftRevisionId || existing.liveRevisionId;\n\n\t\tif (!revisionToPublish) {\n\t\t\t// No revision exists - create one from current data\n\t\t\tconst revision = await revisionRepo.create({\n\t\t\t\tcollection: type,\n\t\t\t\tentryId: id,\n\t\t\t\tdata: existing.data,\n\t\t\t});\n\t\t\trevisionToPublish = revision.id;\n\t\t}\n\n\t\t// Sync the revision's data into the content table columns\n\t\t// so the content table always holds the published version\n\t\tconst revision = await revisionRepo.findById(revisionToPublish);\n\t\tif (revision) {\n\t\t\tawait this.syncDataColumns(type, id, revision.data);\n\n\t\t\t// Sync slug from revision if stored there\n\t\t\tif (typeof revision.data._slug === \"string\") {\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET slug = ${revision.data._slug}\n\t\t\t\t\tWHERE id = ${id}\n\t\t\t\t`.execute(this.db);\n\t\t\t}\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET live_revision_id = ${revisionToPublish},\n\t\t\t\tdraft_revision_id = NULL,\n\t\t\t\tstatus = 'published',\n\t\t\t\tscheduled_at = NULL,\n\t\t\t\tpublished_at = COALESCE(published_at, ${now}),\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Unpublish content\n\t *\n\t * Removes live pointer but preserves draft. If no draft exists,\n\t * creates one from the live version so the content isn't lost.\n\t */\n\tasync unpublish(type: string, id: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// If no draft exists, create one from the live version\n\t\tif (!existing.draftRevisionId && existing.liveRevisionId) {\n\t\t\tconst revisionRepo = new RevisionRepository(this.db);\n\t\t\tconst liveRevision = await revisionRepo.findById(existing.liveRevisionId);\n\t\t\tif (liveRevision) {\n\t\t\t\tconst draft = await revisionRepo.create({\n\t\t\t\t\tcollection: type,\n\t\t\t\t\tentryId: id,\n\t\t\t\t\tdata: liveRevision.data,\n\t\t\t\t});\n\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET draft_revision_id = ${draft.id}\n\t\t\t\t\tWHERE id = ${id}\n\t\t\t\t`.execute(this.db);\n\t\t\t}\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET live_revision_id = NULL,\n\t\t\t\tstatus = 'draft',\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Discard pending draft changes\n\t *\n\t * Clears draft_revision_id. The content table columns already hold the\n\t * published version, so no data sync is needed.\n\t */\n\tasync discardDraft(type: string, id: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\tif (!existing.draftRevisionId) {\n\t\t\t// No draft to discard\n\t\t\treturn existing;\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET draft_revision_id = NULL,\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Sync data columns in the content table from a data object.\n\t * Used to promote revision data into the content table on publish.\n\t * Keys starting with _ are revision metadata (e.g. _slug) and are skipped.\n\t */\n\tprivate async syncDataColumns(\n\t\ttype: string,\n\t\tid: string,\n\t\tdata: Record<string, unknown>,\n\t): Promise<void> {\n\t\tconst tableName = getTableName(type);\n\t\tconst updates: Record<string, unknown> = {};\n\n\t\tfor (const [key, value] of Object.entries(data)) {\n\t\t\tif (SYSTEM_COLUMNS.has(key)) continue;\n\t\t\tif (key.startsWith(\"_\")) continue; // revision metadata\n\t\t\tvalidateIdentifier(key, \"content field name\");\n\t\t\tupdates[key] = serializeValue(value);\n\t\t}\n\n\t\tif (Object.keys(updates).length === 0) return;\n\n\t\tawait this.db\n\t\t\t.updateTable(tableName as keyof Database)\n\t\t\t.set(updates)\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.execute();\n\t}\n\n\t/**\n\t * Count content items with a pending schedule.\n\t * Includes both draft-scheduled (status='scheduled') and published\n\t * posts with scheduled draft changes (status='published', scheduled_at set).\n\t */\n\tasync countScheduled(type: string): Promise<number> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<{ count: number }>`\n\t\t\tSELECT COUNT(id) as count FROM ${sql.ref(tableName)}\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\treturn Number(result.rows[0]?.count || 0);\n\t}\n\n\t/**\n\t * Map database row to ContentItem\n\t * Extracts system columns and puts content fields in data\n\t * Excludes null values from data to match input semantics\n\t */\n\tprivate mapRow(type: string, row: Record<string, unknown>): ContentItem {\n\t\tconst data: Record<string, unknown> = {};\n\n\t\tfor (const [key, value] of Object.entries(row)) {\n\t\t\tif (!SYSTEM_COLUMNS.has(key) && value !== null) {\n\t\t\t\tdata[key] = deserializeValue(value);\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid: row.id as string,\n\t\t\ttype,\n\t\t\tslug: row.slug as string | null,\n\t\t\tstatus: row.status as string,\n\t\t\tdata,\n\t\t\tauthorId: row.author_id as string | null,\n\t\t\tprimaryBylineId: (row.primary_byline_id as string | null) ?? null,\n\t\t\tcreatedAt: row.created_at as string,\n\t\t\tupdatedAt: row.updated_at as string,\n\t\t\tpublishedAt: row.published_at as string | null,\n\t\t\tscheduledAt: row.scheduled_at as string | null,\n\t\t\tliveRevisionId: (row.live_revision_id as string | null) ?? null,\n\t\t\tdraftRevisionId: (row.draft_revision_id as string | null) ?? null,\n\t\t\tversion: typeof row.version === \"number\" ? row.version : 1,\n\t\t\tlocale: (row.locale as string) ?? null,\n\t\t\ttranslationGroup: (row.translation_group as string) ?? null,\n\t\t};\n\t}\n\n\t/**\n\t * Map order field names to database columns.\n\t * Only allows known fields to prevent column enumeration via crafted orderBy values.\n\t */\n\tprivate mapOrderField(field: string): string {\n\t\tconst mapping: Record<string, string> = {\n\t\t\tcreatedAt: \"created_at\",\n\t\t\tupdatedAt: \"updated_at\",\n\t\t\tpublishedAt: \"published_at\",\n\t\t\tscheduledAt: \"scheduled_at\",\n\t\t\tdeletedAt: \"deleted_at\",\n\t\t\ttitle: \"title\",\n\t\t\tslug: \"slug\",\n\t\t};\n\n\t\tconst mapped = mapping[field];\n\t\tif (!mapped) {\n\t\t\tthrow new EmDashValidationError(`Invalid order field: ${field}`);\n\t\t}\n\t\treturn mapped;\n\t}\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-CiYn9yDu.mjs","names":[],"sources":["../src/api/error.ts"],"sourcesContent":["/**\n * Standardized API error responses.\n *\n * All API routes should use these utilities instead of inline\n * `new Response(JSON.stringify({ error: ... }), ...)` patterns.\n */\n\nimport { mapErrorStatus } from \"./errors.js\";\nimport type { ApiResult } from \"./types.js\";\n\n// Re-export everything from errors.ts so existing `import { mapErrorStatus } from \"./error.js\"` still works\nexport * from \"./errors.js\";\n\n/**\n * Standard cache headers for all API responses.\n *\n * Cache-Control: private, no-store -- prevents CDN/proxy caching of authenticated data.\n * no-store already tells caches not to store the response, so Vary is unnecessary.\n */\nconst API_CACHE_HEADERS: HeadersInit = {\n\t\"Cache-Control\": \"private, no-store\",\n};\n\n/**\n * Create a standardized error response.\n *\n * Always returns `{ error: { code, message } }` with correct Content-Type.\n * Use this for all error responses in API routes.\n */\nexport function apiError(code: string, message: string, status: number): Response {\n\treturn Response.json({ error: { code, message } }, { status, headers: API_CACHE_HEADERS });\n}\n\n/**\n * Create a standardized success response.\n *\n * Always returns `{ data: T }` with correct status code.\n * Use this for all success responses in API routes.\n */\nexport function apiSuccess<T>(data: T, status = 200): Response {\n\treturn Response.json({ data }, { status, headers: API_CACHE_HEADERS });\n}\n\n/**\n * Handle an unknown error in a catch block.\n *\n * - Logs the full error server-side\n * - Returns a generic message to the client (never leaks error.message)\n * - Use `fallbackMessage` for the public-facing message\n * - Use `fallbackCode` for the error code\n */\nexport function handleError(\n\terror: unknown,\n\tfallbackMessage: string,\n\tfallbackCode: string,\n): Response {\n\tconsole.error(`[${fallbackCode}]`, error);\n\treturn apiError(fallbackCode, fallbackMessage, 500);\n}\n\n/**\n * Standard initialization check.\n *\n * Returns an error response if EmDash is not initialized, or null if OK.\n * Usage: `const err = requireInit(emdash); if (err) return err;`\n */\nexport function requireInit(emdash: unknown): Response | null {\n\tif (!emdash || typeof emdash !== \"object\") {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\treturn null;\n}\n\n/**\n * Standard database check.\n *\n * Returns an error response if the database is not available, or null if OK.\n * Usage: `const err = requireDb(emdash?.db); if (err) return err;`\n */\nexport function requireDb(db: unknown): Response | null {\n\tif (!db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\treturn null;\n}\n\n/**\n * Convert an ApiResult into an HTTP Response.\n *\n * Collapses the handler-to-response boilerplate:\n * - Success: returns `apiSuccess(result.data, successStatus)`\n * - Error: returns `apiError(code, message, mapErrorStatus(code))`\n */\nexport function unwrapResult<T>(result: ApiResult<T>, successStatus = 200): Response {\n\tif (!result.success) {\n\t\treturn apiError(result.error.code, result.error.message, mapErrorStatus(result.error.code));\n\t}\n\treturn apiSuccess(result.data, successStatus);\n}\n"],"mappings":";;;;;;;AAmBA,MAAM,oBAAiC,EACtC,iBAAiB,qBACjB;;;;;;;AAQD,SAAgB,SAAS,MAAc,SAAiB,QAA0B;AACjF,QAAO,SAAS,KAAK,EAAE,OAAO;EAAE;EAAM;EAAS,EAAE,EAAE;EAAE;EAAQ,SAAS;EAAmB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-BYv0mB9g.d.mts","names":[],"sources":["../src/database/connection.ts","../src/database/repositories/content.ts","../src/database/repositories/media.ts","../src/database/repositories/revision.ts","../src/database/repositories/comment.ts","../src/database/repositories/byline.ts","../src/fields/types.ts","../src/fields/image.ts","../src/fields/reference.ts","../src/fields/portable-text.ts","../src/api/types.ts","../src/api/handlers/content.ts","../src/api/handlers/manifest.ts","../src/api/handlers/revision.ts","../src/api/handlers/media.ts","../src/schema/registry.ts","../src/schema/query.ts","../src/sections/types.ts","../src/sections/index.ts","../src/plugins/sandbox/types.ts","../src/content/converters/types.ts","../src/content/converters/prosemirror-to-portable-text.ts","../src/content/converters/portable-text-to-prosemirror.ts","../src/utils/hash.ts","../src/utils/url.ts","../src/utils/slugify.ts","../src/visual-editing/editable.ts","../src/query.ts","../src/after.ts","../src/i18n/config.ts","../src/loader.ts","../src/cli/wxr/parser.ts","../src/plugins/define-plugin.ts","../src/auth/types.ts","../src/astro/storage/types.ts","../src/astro/integration/runtime.ts","../src/plugins/manifest-schema.ts","../src/plugins/hooks.ts","../src/plugins/email.ts","../src/plugins/context.ts","../src/plugins/routes.ts","../src/plugins/manager.ts","../src/plugins/sandbox/noop.ts","../src/import/types.ts","../src/import/sections.ts","../src/import/registry.ts","../src/import/sources/wxr.ts","../src/import/sources/wordpress-rest.ts","../src/preview/tokens.ts","../src/preview/urls.ts","../src/preview/helpers.ts","../src/settings/index.ts","../src/comments/query.ts","../src/menus/types.ts","../src/menus/index.ts","../src/bylines/index.ts","../src/taxonomies/types.ts","../src/taxonomies/index.ts","../src/widgets/types.ts","../src/widgets/index.ts","../src/search/types.ts","../src/search/fts-manager.ts","../src/search/query.ts","../src/search/text-extraction.ts"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"loader-DeiBJEMe.mjs","names":[],"sources":["../src/loader.ts"],"sourcesContent":["/**\n * Astro Live Collections loader for EmDash\n *\n * This loader implements the Astro LiveLoader interface to fetch content\n * at runtime from the database, enabling live editing without rebuilds.\n *\n * Architecture:\n * - Single `_emdash` Astro collection handles all content types\n * - Dialect comes from virtual module (configured in astro.config.mjs)\n * - Each content type maps to its own database table: ec_posts, ec_products, etc.\n * - `getEmDashCollection()` / `getEmDashEntry()` wrap Astro's live collection API\n */\n\nimport type { LiveLoader } from \"astro/loaders\";\nimport { Kysely, sql, type Dialect } from \"kysely\";\n\nimport { currentTimestampValue, isPostgres } from \"./database/dialect-helpers.js\";\nimport { kyselyLogOption } from \"./database/instrumentation.js\";\nimport { decodeCursor, encodeCursor } from \"./database/repositories/types.js\";\nimport { validateIdentifier } from \"./database/validate.js\";\nimport type { Database } from \"./index.js\";\nimport { getRequestContext } from \"./request-context.js\";\nimport { isMissingTableError } from \"./utils/db-errors.js\";\n\nconst FIELD_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\n/**\n * System columns that are not part of the content data\n */\n/**\n * System columns excluded from entry.data\n * Note: slug is intentionally NOT excluded - it's useful as data.slug in templates\n */\nconst SYSTEM_COLUMNS = new Set([\n\t\"id\",\n\t// \"slug\" - kept in data for template access\n\t\"status\",\n\t\"author_id\",\n\t\"primary_byline_id\",\n\t\"created_at\",\n\t\"updated_at\",\n\t\"published_at\",\n\t\"scheduled_at\",\n\t\"deleted_at\",\n\t\"version\",\n\t\"live_revision_id\",\n\t\"draft_revision_id\",\n\t\"locale\",\n\t\"translation_group\",\n]);\n\n/**\n * Get the table name for a collection type\n */\nfunction getTableName(type: string): string {\n\tvalidateIdentifier(type, \"collection type\");\n\treturn `ec_${type}`;\n}\n\n/**\n * Cache for taxonomy names (only used for the primary database).\n * Skipped when a per-request DB override is active (e.g. preview mode)\n * because the override DB may have different taxonomies.\n */\nlet taxonomyNames: Set<string> | null = null;\n\n/**\n * Get all taxonomy names (cached for the primary DB, bypassed only when\n * the per-request DB is an isolated instance — playground / DO preview).\n * Plain D1 Sessions routing shares schema with the singleton, so the\n * module-scoped cache stays valid.\n */\nasync function getTaxonomyNames(db: Kysely<Database>): Promise<Set<string>> {\n\tconst hasIsolatedDb = getRequestContext()?.dbIsIsolated === true;\n\n\tif (!hasIsolatedDb && taxonomyNames) {\n\t\treturn taxonomyNames;\n\t}\n\n\ttry {\n\t\tconst defs = await db.selectFrom(\"_emdash_taxonomy_defs\").select(\"name\").execute();\n\t\tconst names = new Set(defs.map((d) => d.name));\n\t\tif (!hasIsolatedDb) {\n\t\t\ttaxonomyNames = names;\n\t\t}\n\t\treturn names;\n\t} catch {\n\t\t// Table doesn't exist yet, return empty set\n\t\tconst empty = new Set<string>();\n\t\tif (!hasIsolatedDb) {\n\t\t\ttaxonomyNames = empty;\n\t\t}\n\t\treturn empty;\n\t}\n}\n\n/**\n * System columns to include in data (mapped to camelCase where needed)\n */\nconst INCLUDE_IN_DATA: Record<string, string> = {\n\tid: \"id\",\n\tstatus: \"status\",\n\tauthor_id: \"authorId\",\n\tprimary_byline_id: \"primaryBylineId\",\n\tcreated_at: \"createdAt\",\n\tupdated_at: \"updatedAt\",\n\tpublished_at: \"publishedAt\",\n\tscheduled_at: \"scheduledAt\",\n\tdraft_revision_id: \"draftRevisionId\",\n\tlive_revision_id: \"liveRevisionId\",\n\tlocale: \"locale\",\n\ttranslation_group: \"translationGroup\",\n};\n\n/** System date columns that should be converted to Date objects */\nconst DATE_COLUMNS = new Set([\"created_at\", \"updated_at\", \"published_at\", \"scheduled_at\"]);\n\n/** Safely extract a string value from a record, returning fallback if not a string */\nfunction rowStr(row: Record<string, unknown>, key: string, fallback = \"\"): string {\n\tconst val = row[key];\n\treturn typeof val === \"string\" ? val : fallback;\n}\n\n/**\n * Map a database row to entry data\n * Extracts content fields (non-system columns) and parses JSON where needed.\n * System columns needed for templates (id, status, dates) are included with camelCase names.\n */\nfunction mapRowToData(row: Record<string, unknown>): Record<string, unknown> {\n\tconst data: Record<string, unknown> = {};\n\n\tfor (const [key, value] of Object.entries(row)) {\n\t\t// Include certain system columns (mapped to camelCase where needed)\n\t\tif (key in INCLUDE_IN_DATA) {\n\t\t\t// Convert date columns from ISO strings to Date objects\n\t\t\tif (DATE_COLUMNS.has(key)) {\n\t\t\t\tdata[INCLUDE_IN_DATA[key]] = typeof value === \"string\" ? new Date(value) : null;\n\t\t\t} else {\n\t\t\t\tdata[INCLUDE_IN_DATA[key]] = value;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (SYSTEM_COLUMNS.has(key)) continue;\n\n\t\t// Try to parse JSON strings (for portableText, json fields, etc.)\n\t\tif (typeof value === \"string\") {\n\t\t\ttry {\n\t\t\t\t// Only parse if it looks like JSON (starts with { or [)\n\t\t\t\tif (value.startsWith(\"{\") || value.startsWith(\"[\")) {\n\t\t\t\t\tdata[key] = JSON.parse(value);\n\t\t\t\t} else {\n\t\t\t\t\tdata[key] = value;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tdata[key] = value;\n\t\t\t}\n\t\t} else {\n\t\t\tdata[key] = value;\n\t\t}\n\t}\n\n\treturn data;\n}\n\n/**\n * Map revision data (already-parsed JSON object) to entry data.\n * Strips _-prefixed metadata keys (e.g. _slug) used internally by revisions.\n */\nfunction mapRevisionData(data: Record<string, unknown>): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (key.startsWith(\"_\")) continue; // revision metadata\n\t\tresult[key] = value;\n\t}\n\treturn result;\n}\n\n// Virtual module imports are lazy-loaded to avoid errors when importing\n// emdash outside of Astro/Vite context (e.g., in astro.config.mjs)\nlet virtualConfig:\n\t| {\n\t\t\tdatabase?: { config: unknown };\n\t\t\ti18n?: { defaultLocale: string; locales: string[]; prefixDefaultLocale?: boolean } | null;\n\t }\n\t| undefined;\nlet virtualCreateDialect: ((config: unknown) => Dialect) | undefined;\n\nasync function loadVirtualModules() {\n\tif (virtualConfig === undefined) {\n\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t// @ts-ignore - virtual module\n\t\tconst configModule = await import(\"virtual:emdash/config\");\n\t\tvirtualConfig = configModule.default;\n\t}\n\tif (virtualCreateDialect === undefined) {\n\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t// @ts-ignore - virtual module\n\t\tconst dialectModule = await import(\"virtual:emdash/dialect\");\n\t\tvirtualCreateDialect = dialectModule.createDialect;\n\t\t// dialectType is no longer needed here — dialect detection is\n\t\t// done via the db adapter instance in dialect-helpers.ts\n\t}\n}\n\n/**\n * Entry data type - generic object\n */\nexport type EntryData = Record<string, unknown>;\n\n/**\n * Sort direction\n */\nexport type SortDirection = \"asc\" | \"desc\";\n\n/**\n * Order by specification - field name to direction\n * @example { created_at: \"desc\" } - Sort by created_at descending\n * @example { title: \"asc\" } - Sort by title ascending\n */\nexport type OrderBySpec = Record<string, SortDirection>;\n\n/**\n * Build WHERE clause for status filtering.\n * When filtering for 'published' status, also include scheduled content\n * whose scheduled_at time has passed (treating it as effectively published).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nfunction buildStatusCondition(\n\tdb: Kysely<any>,\n\tstatus: string,\n\ttablePrefix?: string,\n): ReturnType<typeof sql> {\n\tconst statusField = tablePrefix ? `${tablePrefix}.status` : \"status\";\n\tconst scheduledAtField = tablePrefix ? `${tablePrefix}.scheduled_at` : \"scheduled_at\";\n\n\tif (status === \"published\") {\n\t\t// Include both published content AND scheduled content past its publish time.\n\t\t// scheduled_at is stored as text (ISO 8601). On Postgres, we must cast it\n\t\t// to timestamptz for the comparison with CURRENT_TIMESTAMP to work.\n\t\tconst scheduledAtExpr = isPostgres(db)\n\t\t\t? sql`${sql.ref(scheduledAtField)}::timestamptz`\n\t\t\t: sql.ref(scheduledAtField);\n\t\treturn sql`(${sql.ref(statusField)} = 'published' OR (${sql.ref(statusField)} = 'scheduled' AND ${scheduledAtExpr} <= ${currentTimestampValue(db)}))`;\n\t}\n\n\t// For other statuses (draft, archived), just match exactly\n\treturn sql`${sql.ref(statusField)} = ${status}`;\n}\n\n/**\n * Resolved primary sort field and direction (used for cursor pagination).\n */\ninterface PrimarySort {\n\tfield: string;\n\tdirection: SortDirection;\n}\n\n/**\n * Get the primary sort field from an orderBy spec (first valid field, or default).\n */\nfunction getPrimarySort(orderBy: OrderBySpec | undefined, tablePrefix?: string): PrimarySort {\n\tif (orderBy) {\n\t\tfor (const [field, direction] of Object.entries(orderBy)) {\n\t\t\tif (FIELD_NAME_PATTERN.test(field)) {\n\t\t\t\tconst fullField = tablePrefix ? `${tablePrefix}.${field}` : field;\n\t\t\t\treturn { field: fullField, direction };\n\t\t\t}\n\t\t}\n\t}\n\tconst defaultField = tablePrefix ? `${tablePrefix}.created_at` : \"created_at\";\n\treturn { field: defaultField, direction: \"desc\" };\n}\n\n/**\n * Build ORDER BY clause from orderBy spec\n * Validates field names to prevent SQL injection (alphanumeric + underscore only)\n * Supports multiple sort fields in object key order\n */\nfunction buildOrderByClause(\n\torderBy: OrderBySpec | undefined,\n\ttablePrefix?: string,\n): ReturnType<typeof sql> {\n\t// Default to created_at DESC\n\tif (!orderBy || Object.keys(orderBy).length === 0) {\n\t\tconst field = tablePrefix ? `${tablePrefix}.created_at` : \"created_at\";\n\t\treturn sql`ORDER BY ${sql.ref(field)} DESC, ${sql.ref(tablePrefix ? `${tablePrefix}.id` : \"id\")} DESC`;\n\t}\n\n\tconst sortParts: ReturnType<typeof sql>[] = [];\n\n\tfor (const [field, direction] of Object.entries(orderBy)) {\n\t\t// Validate field name (alphanumeric + underscore only)\n\t\tif (!FIELD_NAME_PATTERN.test(field)) {\n\t\t\tcontinue; // Skip invalid field names\n\t\t}\n\n\t\tconst fullField = tablePrefix ? `${tablePrefix}.${field}` : field;\n\t\tconst dir = direction === \"asc\" ? sql`ASC` : sql`DESC`;\n\t\tsortParts.push(sql`${sql.ref(fullField)} ${dir}`);\n\t}\n\n\t// If no valid sort fields, fall back to default\n\tif (sortParts.length === 0) {\n\t\tconst defaultField = tablePrefix ? `${tablePrefix}.created_at` : \"created_at\";\n\t\treturn sql`ORDER BY ${sql.ref(defaultField)} DESC, ${sql.ref(tablePrefix ? `${tablePrefix}.id` : \"id\")} DESC`;\n\t}\n\n\t// Add id as tiebreaker to ensure stable cursor ordering\n\tconst primary = getPrimarySort(orderBy, tablePrefix);\n\tconst idField = tablePrefix ? `${tablePrefix}.id` : \"id\";\n\tconst idDir = primary.direction === \"asc\" ? sql`ASC` : sql`DESC`;\n\tsortParts.push(sql`${sql.ref(idField)} ${idDir}`);\n\n\treturn sql`ORDER BY ${sql.join(sortParts, sql`, `)}`;\n}\n\n/**\n * Build a cursor WHERE condition for keyset pagination.\n * Uses the primary sort field + id as tiebreaker for stable ordering.\n */\nfunction buildCursorCondition(\n\tcursor: string,\n\torderBy: OrderBySpec | undefined,\n\ttablePrefix?: string,\n): ReturnType<typeof sql> | null {\n\tconst decoded = decodeCursor(cursor);\n\tif (!decoded) return null;\n\n\tconst { orderValue, id: cursorId } = decoded;\n\tconst primary = getPrimarySort(orderBy, tablePrefix);\n\tconst idField = tablePrefix ? `${tablePrefix}.id` : \"id\";\n\n\tif (primary.direction === \"desc\") {\n\t\treturn sql`(${sql.ref(primary.field)} < ${orderValue} OR (${sql.ref(primary.field)} = ${orderValue} AND ${sql.ref(idField)} < ${cursorId}))`;\n\t}\n\treturn sql`(${sql.ref(primary.field)} > ${orderValue} OR (${sql.ref(primary.field)} = ${orderValue} AND ${sql.ref(idField)} > ${cursorId}))`;\n}\n\n/**\n * Filter for loadCollection - type is required\n */\nexport interface CollectionFilter {\n\ttype: string;\n\tstatus?: \"draft\" | \"published\" | \"archived\";\n\tlimit?: number;\n\t/**\n\t * Opaque cursor for keyset pagination.\n\t * Pass the `nextCursor` value from a previous result to fetch the next page.\n\t */\n\tcursor?: string;\n\t/**\n\t * Filter by field values or taxonomy terms\n\t */\n\twhere?: Record<string, string | string[]>;\n\t/**\n\t * Order results by field(s)\n\t * @default { created_at: \"desc\" }\n\t */\n\torderBy?: OrderBySpec;\n\t/**\n\t * Filter by locale (e.g. 'en', 'fr').\n\t * When set, only returns content in this locale.\n\t */\n\tlocale?: string;\n}\n\n/**\n * Filter for loadEntry - type and id are required\n */\nexport interface EntryFilter {\n\ttype: string;\n\tid: string;\n\t/**\n\t * When set, fetch content data from this revision instead of the content table.\n\t * Used by preview mode to serve draft revision data.\n\t */\n\trevisionId?: string;\n\t/**\n\t * Locale to scope slug lookup. Only affects slug resolution;\n\t * IDs are globally unique and always resolve regardless of locale.\n\t */\n\tlocale?: string;\n}\n\n// Cached database instance (shared across calls)\nlet dbInstance: Kysely<Database> | null = null;\n\n/**\n * Get the database instance. Used by query wrapper functions and middleware.\n *\n * Checks the ALS request context first — if a per-request DB override is set\n * (e.g. by DO preview middleware), it takes precedence over the module-level\n * cached instance. This allows preview mode to route queries to an isolated\n * Durable Object database without modifying any calling code.\n *\n * Initializes the default database on first call using config from virtual module.\n */\nexport async function getDb(): Promise<Kysely<Database>> {\n\t// Per-request DB override via ALS (normal mode)\n\tconst ctx = getRequestContext();\n\tif (ctx?.db) {\n\t\treturn ctx.db as Kysely<Database>; // eslint-disable-line typescript-eslint(no-unsafe-type-assertion) -- db is typed as unknown in RequestContext to avoid circular deps\n\t}\n\n\tif (!dbInstance) {\n\t\tawait loadVirtualModules();\n\t\tif (!virtualConfig?.database || typeof virtualCreateDialect !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"EmDash database not configured. Add database config to emdash() in astro.config.mjs\",\n\t\t\t);\n\t\t}\n\t\tconst dialect = virtualCreateDialect(virtualConfig.database.config);\n\t\tdbInstance = new Kysely<Database>({ dialect, log: kyselyLogOption() });\n\t}\n\treturn dbInstance;\n}\n\n/**\n * Create an EmDash Live Collections loader\n *\n * This loader handles ALL content types in a single Astro collection.\n * Use `getEmDashCollection()` and `getEmDashEntry()` to query\n * specific content types.\n *\n * Database is configured in astro.config.mjs via the emdash() integration.\n *\n * @example\n * ```ts\n * // src/live.config.ts\n * import { defineLiveCollection } from \"astro:content\";\n * import { emdashLoader } from \"emdash\";\n *\n * export const collections = {\n * emdash: defineLiveCollection({\n * loader: emdashLoader(),\n * }),\n * };\n * ```\n */\nexport function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFilter> {\n\treturn {\n\t\tname: \"emdash\",\n\n\t\t/**\n\t\t * Load all entries for a content type\n\t\t */\n\t\tasync loadCollection({ filter }) {\n\t\t\ttry {\n\t\t\t\t// Get DB instance (initializes on first use)\n\t\t\t\tconst db = await getDb();\n\n\t\t\t\t// Type filter is required\n\t\t\t\tconst type = filter?.type;\n\t\t\t\tif (!type) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: new Error(\n\t\t\t\t\t\t\t\"type filter is required. Use getEmDashCollection() instead of getLiveCollection() directly.\",\n\t\t\t\t\t\t),\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Query the per-collection table (ec_posts, ec_products, etc.)\n\t\t\t\tconst tableName = getTableName(type);\n\n\t\t\t\t// Build query with dynamic table name\n\t\t\t\tconst status = filter?.status || \"published\";\n\t\t\t\tconst limit = filter?.limit;\n\t\t\t\tconst cursor = filter?.cursor;\n\t\t\t\tconst where = filter?.where;\n\t\t\t\tconst orderBy = filter?.orderBy;\n\t\t\t\tconst locale = filter?.locale;\n\n\t\t\t\t// Cursor pagination: over-fetch by 1 to detect next page\n\t\t\t\tconst fetchLimit = limit ? limit + 1 : undefined;\n\n\t\t\t\t// Build cursor condition if cursor is provided\n\t\t\t\tconst cursorCondition = cursor ? buildCursorCondition(cursor, orderBy) : null;\n\t\t\t\tconst cursorConditionPrefixed = cursor\n\t\t\t\t\t? buildCursorCondition(cursor, orderBy, tableName)\n\t\t\t\t\t: null;\n\n\t\t\t\t// Check if we need taxonomy filtering\n\t\t\t\tlet result: { rows: Record<string, unknown>[] };\n\n\t\t\t\tif (where && Object.keys(where).length > 0) {\n\t\t\t\t\t// Get taxonomy names to detect taxonomy filters\n\t\t\t\t\tconst taxNames = await getTaxonomyNames(db);\n\t\t\t\t\tconst taxonomyFilters: Record<string, string | string[]> = {};\n\n\t\t\t\t\tfor (const [key, value] of Object.entries(where)) {\n\t\t\t\t\t\tif (taxNames.has(key)) {\n\t\t\t\t\t\t\ttaxonomyFilters[key] = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If we have taxonomy filters, use JOIN\n\t\t\t\t\tif (Object.keys(taxonomyFilters).length > 0) {\n\t\t\t\t\t\t// Build query with taxonomy JOIN\n\t\t\t\t\t\t// For now, support single taxonomy filter (can extend later for multiple)\n\t\t\t\t\t\tconst [taxName, termSlugs] = Object.entries(taxonomyFilters)[0];\n\t\t\t\t\t\tconst slugs = Array.isArray(termSlugs) ? termSlugs : [termSlugs];\n\t\t\t\t\t\tconst orderByClause = buildOrderByClause(orderBy, tableName);\n\n\t\t\t\t\t\tconst statusCondition = buildStatusCondition(db, status, tableName);\n\t\t\t\t\t\tconst localeCondition = locale\n\t\t\t\t\t\t\t? sql`AND ${sql.ref(tableName)}.locale = ${locale}`\n\t\t\t\t\t\t\t: sql``;\n\t\t\t\t\t\tconst cursorCond = cursorConditionPrefixed\n\t\t\t\t\t\t\t? sql`AND ${cursorConditionPrefixed}`\n\t\t\t\t\t\t\t: sql``;\n\t\t\t\t\t\tresult = await sql<Record<string, unknown>>`\n\t\t\t\t\t\t\tSELECT DISTINCT ${sql.ref(tableName)}.* FROM ${sql.ref(tableName)}\n\t\t\t\t\t\t\tINNER JOIN content_taxonomies ct\n\t\t\t\t\t\t\t\tON ct.collection = ${type}\n\t\t\t\t\t\t\t\tAND ct.entry_id = ${sql.ref(tableName)}.id\n\t\t\t\t\t\t\tINNER JOIN taxonomies t\n\t\t\t\t\t\t\t\tON t.id = ct.taxonomy_id\n\t\t\t\t\t\t\tWHERE ${sql.ref(tableName)}.deleted_at IS NULL\n\t\t\t\t\t\t\t\tAND ${statusCondition}\n\t\t\t\t\t\t\t\t${localeCondition}\n\t\t\t\t\t\t\t\t${cursorCond}\n\t\t\t\t\t\t\t\tAND t.name = ${taxName}\n\t\t\t\t\t\t\t\tAND t.slug IN (${sql.join(slugs.map((s) => sql`${s}`))})\n\t\t\t\t\t\t\t${orderByClause}\n\t\t\t\t\t\t\t${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}\n\t\t\t\t\t\t`.execute(db);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// No taxonomy filters, use simple query\n\t\t\t\t\t\tconst orderByClause = buildOrderByClause(orderBy);\n\t\t\t\t\t\tconst statusCondition = buildStatusCondition(db, status);\n\t\t\t\t\t\tconst localeFilter = locale ? sql`AND locale = ${locale}` : sql``;\n\t\t\t\t\t\tconst cursorCond = cursorCondition ? sql`AND ${cursorCondition}` : sql``;\n\t\t\t\t\t\tresult = await sql<Record<string, unknown>>`\n\t\t\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t\t\tAND ${statusCondition}\n\t\t\t\t\t\t\t${localeFilter}\n\t\t\t\t\t\t\t${cursorCond}\n\t\t\t\t\t\t\t${orderByClause}\n\t\t\t\t\t\t\t${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}\n\t\t\t\t\t\t`.execute(db);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// No where clause, use simple query\n\t\t\t\t\tconst orderByClause = buildOrderByClause(orderBy);\n\t\t\t\t\tconst statusCondition = buildStatusCondition(db, status);\n\t\t\t\t\tconst localeFilter = locale ? sql`AND locale = ${locale}` : sql``;\n\t\t\t\t\tconst cursorCond = cursorCondition ? sql`AND ${cursorCondition}` : sql``;\n\t\t\t\t\tresult = await sql<Record<string, unknown>>`\n\t\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t\tAND ${statusCondition}\n\t\t\t\t\t\t${localeFilter}\n\t\t\t\t\t\t${cursorCond}\n\t\t\t\t\t\t${orderByClause}\n\t\t\t\t\t\t${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}\n\t\t\t\t\t`.execute(db);\n\t\t\t\t}\n\n\t\t\t\t// Detect whether there are more results (over-fetched by 1)\n\t\t\t\tconst hasMore = limit ? result.rows.length > limit : false;\n\t\t\t\tconst rows = hasMore ? result.rows.slice(0, limit) : result.rows;\n\n\t\t\t\t// Map rows to entries\n\t\t\t\tconst i18nConfig = virtualConfig?.i18n;\n\t\t\t\tconst i18nEnabled = i18nConfig && i18nConfig.locales.length > 1;\n\t\t\t\tconst entries = rows.map((row) => {\n\t\t\t\t\tconst slug = rowStr(row, \"slug\") || rowStr(row, \"id\");\n\t\t\t\t\tconst rowLocale = rowStr(row, \"locale\");\n\t\t\t\t\tconst shouldPrefix =\n\t\t\t\t\t\ti18nEnabled &&\n\t\t\t\t\t\trowLocale !== \"\" &&\n\t\t\t\t\t\t(rowLocale !== i18nConfig.defaultLocale || i18nConfig.prefixDefaultLocale);\n\t\t\t\t\tconst id = shouldPrefix ? `${rowLocale}/${slug}` : slug;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tslug: rowStr(row, \"slug\"),\n\t\t\t\t\t\tstatus: rowStr(row, \"status\", \"draft\"),\n\t\t\t\t\t\tdata: mapRowToData(row),\n\t\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\t\ttags: [rowStr(row, \"id\")],\n\t\t\t\t\t\t\tlastModified: row.updated_at ? new Date(rowStr(row, \"updated_at\")) : undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\t// Encode nextCursor from the last row if there are more results\n\t\t\t\tlet nextCursor: string | undefined;\n\t\t\t\tif (hasMore && rows.length > 0) {\n\t\t\t\t\tconst lastRow = rows.at(-1)!;\n\t\t\t\t\tconst primary = getPrimarySort(orderBy);\n\t\t\t\t\t// Strip table prefix from field name for row lookup\n\t\t\t\t\tconst fieldName = primary.field.includes(\".\")\n\t\t\t\t\t\t? primary.field.split(\".\").pop()!\n\t\t\t\t\t\t: primary.field;\n\t\t\t\t\tconst lastOrderValue = lastRow[fieldName];\n\t\t\t\t\tconst orderStr =\n\t\t\t\t\t\ttypeof lastOrderValue === \"string\" || typeof lastOrderValue === \"number\"\n\t\t\t\t\t\t\t? String(lastOrderValue)\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\tnextCursor = encodeCursor(orderStr, String(lastRow.id));\n\t\t\t\t}\n\n\t\t\t\t// Collection-level cache hint uses the most recent updated_at\n\t\t\t\tlet collectionLastModified: Date | undefined;\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tif (row.updated_at) {\n\t\t\t\t\t\tconst d = new Date(rowStr(row, \"updated_at\"));\n\t\t\t\t\t\tif (!collectionLastModified || d > collectionLastModified) {\n\t\t\t\t\t\t\tcollectionLastModified = d;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tentries,\n\t\t\t\t\tnextCursor,\n\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\ttags: [type],\n\t\t\t\t\t\tlastModified: collectionLastModified,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\t// Handle missing table gracefully - return empty collection.\n\t\t\t\t// This happens before migrations have run.\n\t\t\t\tif (isMissingTableError(error)) {\n\t\t\t\t\treturn { entries: [] };\n\t\t\t\t}\n\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\treturn {\n\t\t\t\t\terror: new Error(`Failed to load collection: ${message}`),\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Load a single entry by type and ID/slug\n\t\t *\n\t\t * When filter.revisionId is set (preview mode), the entry's data\n\t\t * comes from the revisions table instead of the content table columns.\n\t\t */\n\t\tasync loadEntry({ filter }) {\n\t\t\ttry {\n\t\t\t\t// Get DB instance\n\t\t\t\tconst db = await getDb();\n\n\t\t\t\t// Both type and id are required\n\t\t\t\tconst type = filter?.type;\n\t\t\t\tconst id = filter?.id;\n\n\t\t\t\tif (!type || !id) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: new Error(\n\t\t\t\t\t\t\t\"type and id filters are required. Use getEmDashEntry() instead of getLiveEntry() directly.\",\n\t\t\t\t\t\t),\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Query the per-collection table\n\t\t\t\tconst tableName = getTableName(type);\n\t\t\t\tconst locale = filter?.locale;\n\n\t\t\t\t// Use raw SQL for dynamic table name, match by slug or id\n\t\t\t\t// When locale is specified, prefer locale-scoped slug match,\n\t\t\t\t// but IDs are globally unique so always check id without locale scope\n\t\t\t\tconst result = locale\n\t\t\t\t\t? await sql<Record<string, unknown>>`\n\t\t\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t\t\tAND ((slug = ${id} AND locale = ${locale}) OR id = ${id})\n\t\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t\t`.execute(db)\n\t\t\t\t\t: await sql<Record<string, unknown>>`\n\t\t\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t\t\tAND (slug = ${id} OR id = ${id})\n\t\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t\t`.execute(db);\n\n\t\t\t\tconst row = result.rows[0];\n\t\t\t\tif (!row) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tconst i18nConfig = virtualConfig?.i18n;\n\t\t\t\tconst i18nEnabled = i18nConfig && i18nConfig.locales.length > 1;\n\t\t\t\tconst entrySlug = rowStr(row, \"slug\") || rowStr(row, \"id\");\n\t\t\t\tconst entryLocale = rowStr(row, \"locale\");\n\t\t\t\tconst shouldPrefixEntry =\n\t\t\t\t\ti18nEnabled &&\n\t\t\t\t\tentryLocale !== \"\" &&\n\t\t\t\t\t(entryLocale !== i18nConfig.defaultLocale || i18nConfig.prefixDefaultLocale);\n\t\t\t\tconst entryId = shouldPrefixEntry ? `${entryLocale}/${entrySlug}` : entrySlug;\n\n\t\t\t\t// Preview mode: override content fields with revision data,\n\t\t\t\t// keeping system metadata from the content table row.\n\t\t\t\tconst revisionId = filter?.revisionId;\n\t\t\t\tif (revisionId) {\n\t\t\t\t\tconst revRow = await sql<{ data: string }>`\n\t\t\t\t\t\tSELECT data FROM revisions\n\t\t\t\t\t\tWHERE id = ${revisionId}\n\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t`.execute(db);\n\n\t\t\t\t\tconst revData = revRow.rows[0];\n\t\t\t\t\tif (revData) {\n\t\t\t\t\t\tconst parsed: Record<string, unknown> = JSON.parse(revData.data);\n\t\t\t\t\t\t// System metadata from content table + content fields from revision\n\t\t\t\t\t\tconst systemData: Record<string, unknown> = {};\n\t\t\t\t\t\tfor (const [key, mappedKey] of Object.entries(INCLUDE_IN_DATA)) {\n\t\t\t\t\t\t\tif (key in row) {\n\t\t\t\t\t\t\t\tif (DATE_COLUMNS.has(key)) {\n\t\t\t\t\t\t\t\t\tsystemData[mappedKey] = typeof row[key] === \"string\" ? new Date(row[key]) : null;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tsystemData[mappedKey] = row[key];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Use slug from revision metadata if present, else from content table\n\t\t\t\t\t\tconst slug = typeof parsed._slug === \"string\" ? parsed._slug : rowStr(row, \"slug\");\n\t\t\t\t\t\tconst revSlug = slug || rowStr(row, \"id\");\n\t\t\t\t\t\tconst revLocale = rowStr(row, \"locale\");\n\t\t\t\t\t\tconst shouldPrefixRev =\n\t\t\t\t\t\t\ti18nEnabled &&\n\t\t\t\t\t\t\trevLocale !== \"\" &&\n\t\t\t\t\t\t\t(revLocale !== i18nConfig.defaultLocale || i18nConfig.prefixDefaultLocale);\n\t\t\t\t\t\tconst revId = shouldPrefixRev ? `${revLocale}/${revSlug}` : revSlug;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: revId,\n\t\t\t\t\t\t\tslug,\n\t\t\t\t\t\t\tstatus: rowStr(row, \"status\", \"draft\"),\n\t\t\t\t\t\t\tdata: { ...systemData, slug, ...mapRevisionData(parsed) },\n\t\t\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\t\t\ttags: [rowStr(row, \"id\")],\n\t\t\t\t\t\t\t\tlastModified: row.updated_at ? new Date(rowStr(row, \"updated_at\")) : undefined,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tid: entryId,\n\t\t\t\t\tslug: rowStr(row, \"slug\"),\n\t\t\t\t\tstatus: rowStr(row, \"status\", \"draft\"),\n\t\t\t\t\tdata: mapRowToData(row),\n\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\ttags: [rowStr(row, \"id\")],\n\t\t\t\t\t\tlastModified: row.updated_at ? new Date(rowStr(row, \"updated_at\")) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\t// Handle missing table gracefully - return undefined (not found).\n\t\t\t\t// This happens before migrations have run.\n\t\t\t\tif (isMissingTableError(error)) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\treturn {\n\t\t\t\t\terror: new Error(`Failed to load entry: ${message}`),\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"manifest-schema-V30qsMft.mjs","names":[],"sources":["../src/plugins/manifest-schema.ts"],"sourcesContent":["/**\n * Zod schema for PluginManifest validation\n *\n * Used to validate manifest.json from plugin bundles at every parse site:\n * - Client-side download (marketplace.ts extractBundle)\n * - R2 load (api/handlers/marketplace.ts loadBundleFromR2)\n * - CLI publish preview (cli/commands/publish.ts readManifestFromTarball)\n * - Marketplace ingest extends this with publishing-specific fields\n */\n\nimport { z } from \"zod\";\n\n// ── Enum values (must stay in sync with types.ts) ───────────────\n\nexport const PLUGIN_CAPABILITIES = [\n\t\"network:fetch\",\n\t\"network:fetch:any\",\n\t\"read:content\",\n\t\"write:content\",\n\t\"read:media\",\n\t\"write:media\",\n\t\"read:users\",\n\t\"email:send\",\n\t\"email:provide\",\n\t\"email:intercept\",\n\t\"page:inject\",\n] as const;\n\n/** Must stay in sync with FieldType in schema/types.ts */\nconst FIELD_TYPES = [\n\t\"string\",\n\t\"text\",\n\t\"number\",\n\t\"integer\",\n\t\"boolean\",\n\t\"datetime\",\n\t\"select\",\n\t\"multiSelect\",\n\t\"portableText\",\n\t\"image\",\n\t\"file\",\n\t\"reference\",\n\t\"json\",\n\t\"slug\",\n\t\"repeater\",\n] as const;\n\nexport const HOOK_NAMES = [\n\t\"plugin:install\",\n\t\"plugin:activate\",\n\t\"plugin:deactivate\",\n\t\"plugin:uninstall\",\n\t\"content:beforeSave\",\n\t\"content:afterSave\",\n\t\"content:beforeDelete\",\n\t\"content:afterDelete\",\n\t\"content:afterPublish\",\n\t\"content:afterUnpublish\",\n\t\"media:beforeUpload\",\n\t\"media:afterUpload\",\n\t\"cron\",\n\t\"email:beforeSend\",\n\t\"email:deliver\",\n\t\"email:afterSend\",\n\t\"comment:beforeCreate\",\n\t\"comment:moderate\",\n\t\"comment:afterCreate\",\n\t\"comment:afterModerate\",\n\t\"page:metadata\",\n\t\"page:fragments\",\n] as const;\n\n/**\n * Structured hook entry for manifest — name plus optional metadata.\n * During a transition period, both plain strings and objects are accepted.\n */\nconst manifestHookEntrySchema = z.object({\n\tname: z.enum(HOOK_NAMES),\n\texclusive: z.boolean().optional(),\n\tpriority: z.number().int().optional(),\n\ttimeout: z.number().int().positive().optional(),\n});\n\n/**\n * Structured route entry for manifest — name plus optional metadata.\n * Both plain strings and objects are accepted; strings are normalized\n * to `{ name }` objects via `normalizeManifestRoute()`.\n */\n/** Route names must be safe path segments — alphanumeric, hyphens, underscores, forward slashes */\nconst routeNamePattern = /^[a-zA-Z0-9][a-zA-Z0-9_\\-/]*$/;\n\nconst manifestRouteEntrySchema = z.object({\n\tname: z.string().min(1).regex(routeNamePattern, \"Route name must be a safe path segment\"),\n\tpublic: z.boolean().optional(),\n});\n\n// ── Sub-schemas ─────────────────────────────────────────────────\n\n/** Index field names must be valid identifiers to prevent SQL injection via JSON path expressions */\nconst indexFieldName = z.string().regex(/^[a-zA-Z][a-zA-Z0-9_]*$/);\n\nconst storageCollectionSchema = z.object({\n\tindexes: z.array(z.union([indexFieldName, z.array(indexFieldName)])),\n\tuniqueIndexes: z.array(z.union([indexFieldName, z.array(indexFieldName)])).optional(),\n});\n\nconst baseSettingFields = {\n\tlabel: z.string(),\n\tdescription: z.string().optional(),\n};\n\nconst settingFieldSchema = z.discriminatedUnion(\"type\", [\n\tz.object({\n\t\t...baseSettingFields,\n\t\ttype: z.literal(\"string\"),\n\t\tdefault: z.string().optional(),\n\t\tmultiline: z.boolean().optional(),\n\t}),\n\tz.object({\n\t\t...baseSettingFields,\n\t\ttype: z.literal(\"number\"),\n\t\tdefault: z.number().optional(),\n\t\tmin: z.number().optional(),\n\t\tmax: z.number().optional(),\n\t}),\n\tz.object({ ...baseSettingFields, type: z.literal(\"boolean\"), default: z.boolean().optional() }),\n\tz.object({\n\t\t...baseSettingFields,\n\t\ttype: z.literal(\"select\"),\n\t\toptions: z.array(z.object({ value: z.string(), label: z.string() })),\n\t\tdefault: z.string().optional(),\n\t}),\n\tz.object({ ...baseSettingFields, type: z.literal(\"secret\") }),\n]);\n\nconst adminPageSchema = z.object({\n\tpath: z.string(),\n\tlabel: z.string(),\n\ticon: z.string().optional(),\n});\n\nconst dashboardWidgetSchema = z.object({\n\tid: z.string(),\n\tsize: z.enum([\"full\", \"half\", \"third\"]).optional(),\n\ttitle: z.string().optional(),\n});\n\nconst pluginAdminConfigSchema = z.object({\n\tentry: z.string().optional(),\n\tsettingsSchema: z.record(z.string(), settingFieldSchema).optional(),\n\tpages: z.array(adminPageSchema).optional(),\n\twidgets: z.array(dashboardWidgetSchema).optional(),\n\tfieldWidgets: z\n\t\t.array(\n\t\t\tz.object({\n\t\t\t\tname: z.string().min(1),\n\t\t\t\tlabel: z.string().min(1),\n\t\t\t\tfieldTypes: z.array(z.enum(FIELD_TYPES)),\n\t\t\t\telements: z\n\t\t\t\t\t.array(\n\t\t\t\t\t\tz\n\t\t\t\t\t\t\t.object({\n\t\t\t\t\t\t\t\ttype: z.string(),\n\t\t\t\t\t\t\t\taction_id: z.string(),\n\t\t\t\t\t\t\t\tlabel: z.string().optional(),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.passthrough(),\n\t\t\t\t\t)\n\t\t\t\t\t.optional(),\n\t\t\t}),\n\t\t)\n\t\t.optional(),\n});\n\n// ── Main schema ─────────────────────────────────────────────────\n\n/**\n * Zod schema matching the PluginManifest interface from types.ts.\n *\n * Every JSON.parse of a manifest.json should validate through this.\n */\nexport const pluginManifestSchema = z.object({\n\tid: z.string().min(1),\n\tversion: z.string().min(1),\n\tcapabilities: z.array(z.enum(PLUGIN_CAPABILITIES)),\n\tallowedHosts: z.array(z.string()),\n\tstorage: z.record(z.string(), storageCollectionSchema),\n\t/**\n\t * Hook declarations — accepts both plain name strings (legacy) and\n\t * structured objects with exclusive/priority/timeout metadata.\n\t * Plain strings are normalized to `{ name }` objects after parsing.\n\t */\n\thooks: z.array(z.union([z.enum(HOOK_NAMES), manifestHookEntrySchema])),\n\t/**\n\t * Route declarations — accepts both plain name strings and\n\t * structured objects with public metadata.\n\t * Plain strings are normalized to `{ name }` objects after parsing.\n\t */\n\troutes: z.array(\n\t\tz.union([\n\t\t\tz.string().min(1).regex(routeNamePattern, \"Route name must be a safe path segment\"),\n\t\t\tmanifestRouteEntrySchema,\n\t\t]),\n\t),\n\tadmin: pluginAdminConfigSchema,\n});\n\nexport type ValidatedPluginManifest = z.infer<typeof pluginManifestSchema>;\n\n/**\n * Normalize a manifest hook entry — plain strings become `{ name }` objects.\n */\nexport function normalizeManifestHook(\n\tentry: string | { name: string; exclusive?: boolean; priority?: number; timeout?: number },\n): { name: string; exclusive?: boolean; priority?: number; timeout?: number } {\n\tif (typeof entry === \"string\") {\n\t\treturn { name: entry };\n\t}\n\treturn entry;\n}\n\n/**\n * Normalize a manifest route entry — plain strings become `{ name }` objects.\n */\nexport function normalizeManifestRoute(entry: string | { name: string; public?: boolean }): {\n\tname: string;\n\tpublic?: boolean;\n} {\n\tif (typeof entry === \"string\") {\n\t\treturn { name: entry };\n\t}\n\treturn entry;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"media-DqHVh136.mjs","names":[],"sources":["../src/database/repositories/media.ts"],"sourcesContent":["import { sql, type Kysely, type SqlBool } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport type { Database, MediaRow } from \"../types.js\";\nimport type { FindManyResult } from \"./types.js\";\nimport { encodeCursor, decodeCursor } from \"./types.js\";\n\n/** Escape LIKE wildcard characters and the escape char itself in user-supplied values */\nfunction escapeLike(value: string): string {\n\treturn value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\"%\", \"\\\\%\").replaceAll(\"_\", \"\\\\_\");\n}\n\nexport type MediaStatus = \"pending\" | \"ready\" | \"failed\";\n\nexport interface MediaItem {\n\tid: string;\n\tfilename: string;\n\tmimeType: string;\n\tsize: number | null;\n\twidth: number | null;\n\theight: number | null;\n\talt: string | null;\n\tcaption: string | null;\n\tstorageKey: string;\n\tstatus: MediaStatus;\n\tcontentHash: string | null;\n\tblurhash: string | null;\n\tdominantColor: string | null;\n\tcreatedAt: string;\n\tauthorId: string | null;\n}\n\nexport interface CreateMediaInput {\n\tfilename: string;\n\tmimeType: string;\n\tsize?: number;\n\twidth?: number;\n\theight?: number;\n\talt?: string;\n\tcaption?: string;\n\tstorageKey: string;\n\tcontentHash?: string;\n\tblurhash?: string;\n\tdominantColor?: string;\n\tstatus?: MediaStatus;\n\tauthorId?: string;\n}\n\nexport interface FindManyMediaOptions {\n\tlimit?: number;\n\tcursor?: string;\n\tmimeType?: string; // Filter by mime type prefix, e.g., \"image/\"\n\tstatus?: MediaStatus | \"all\"; // Filter by status, defaults to \"ready\"\n}\n\n/**\n * Media repository for database operations\n */\nexport class MediaRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Create a new media item\n\t */\n\tasync create(input: CreateMediaInput): Promise<MediaItem> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tconst row: Omit<MediaRow, \"rowid\"> = {\n\t\t\tid,\n\t\t\tfilename: input.filename,\n\t\t\tmime_type: input.mimeType,\n\t\t\tsize: input.size ?? null,\n\t\t\twidth: input.width ?? null,\n\t\t\theight: input.height ?? null,\n\t\t\talt: input.alt ?? null,\n\t\t\tcaption: input.caption ?? null,\n\t\t\tstorage_key: input.storageKey,\n\t\t\tcontent_hash: input.contentHash ?? null,\n\t\t\tblurhash: input.blurhash ?? null,\n\t\t\tdominant_color: input.dominantColor ?? null,\n\t\t\tstatus: input.status ?? \"ready\",\n\t\t\tcreated_at: now,\n\t\t\tauthor_id: input.authorId ?? null,\n\t\t};\n\n\t\tawait this.db.insertInto(\"media\").values(row).execute();\n\n\t\treturn this.rowToItem(row as MediaRow);\n\t}\n\n\t/**\n\t * Create a pending media item (for signed URL upload flow)\n\t */\n\tasync createPending(input: {\n\t\tfilename: string;\n\t\tmimeType: string;\n\t\tsize?: number;\n\t\tstorageKey: string;\n\t\tcontentHash?: string;\n\t\tauthorId?: string;\n\t}): Promise<MediaItem> {\n\t\treturn this.create({\n\t\t\t...input,\n\t\t\tstatus: \"pending\",\n\t\t});\n\t}\n\n\t/**\n\t * Confirm upload (mark as ready)\n\t */\n\tasync confirmUpload(\n\t\tid: string,\n\t\tmetadata?: { width?: number; height?: number; size?: number },\n\t): Promise<MediaItem | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst updates: Partial<MediaRow> = {\n\t\t\tstatus: \"ready\",\n\t\t};\n\t\tif (metadata?.width !== undefined) updates.width = metadata.width;\n\t\tif (metadata?.height !== undefined) updates.height = metadata.height;\n\t\tif (metadata?.size !== undefined) updates.size = metadata.size;\n\n\t\tawait this.db.updateTable(\"media\").set(updates).where(\"id\", \"=\", id).execute();\n\n\t\treturn this.findById(id);\n\t}\n\n\t/**\n\t * Mark upload as failed\n\t */\n\tasync markFailed(id: string): Promise<MediaItem | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) {\n\t\t\treturn null;\n\t\t}\n\n\t\tawait this.db.updateTable(\"media\").set({ status: \"failed\" }).where(\"id\", \"=\", id).execute();\n\n\t\treturn this.findById(id);\n\t}\n\n\t/**\n\t * Find media by ID\n\t */\n\tasync findById(id: string): Promise<MediaItem | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"media\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.rowToItem(row) : null;\n\t}\n\n\t/**\n\t * Find media by filename\n\t * Useful for idempotent imports\n\t */\n\tasync findByFilename(filename: string): Promise<MediaItem | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"media\")\n\t\t\t.selectAll()\n\t\t\t.where(\"filename\", \"=\", filename)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.rowToItem(row) : null;\n\t}\n\n\t/**\n\t * Find media by content hash\n\t * Used for deduplication - same content = same hash\n\t */\n\tasync findByContentHash(contentHash: string): Promise<MediaItem | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"media\")\n\t\t\t.selectAll()\n\t\t\t.where(\"content_hash\", \"=\", contentHash)\n\t\t\t.where(\"status\", \"=\", \"ready\")\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.rowToItem(row) : null;\n\t}\n\n\t/**\n\t * Find many media items with cursor pagination\n\t *\n\t * Uses keyset pagination (cursor-based) for consistent results.\n\t * The cursor encodes the created_at and id of the last item.\n\t */\n\tasync findMany(options: FindManyMediaOptions = {}): Promise<FindManyResult<MediaItem>> {\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"media\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\t// Handle cursor-based pagination\n\t\tif (options.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tif (decoded) {\n\t\t\t\tconst { orderValue: createdAt, id: cursorId } = decoded;\n\n\t\t\t\t// Keyset pagination: get items where (created_at, id) < cursor\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(\"created_at\", \"<\", createdAt),\n\t\t\t\t\t\teb.and([eb(\"created_at\", \"=\", createdAt), eb(\"id\", \"<\", cursorId)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (options.mimeType) {\n\t\t\tconst pattern = `${escapeLike(options.mimeType)}%`;\n\t\t\tquery = query.where(sql<SqlBool>`mime_type LIKE ${pattern} ESCAPE '\\\\'`);\n\t\t}\n\n\t\t// Default to only showing ready items\n\t\tif (options.status !== \"all\") {\n\t\t\tquery = query.where(\"status\", \"=\", options.status ?? \"ready\");\n\t\t}\n\n\t\tconst rows = await query.execute();\n\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit).map((row) => this.rowToItem(row));\n\n\t\tlet nextCursor: string | undefined;\n\t\tif (hasMore && items.length > 0) {\n\t\t\tconst lastItem = items.at(-1)!;\n\t\t\tnextCursor = encodeCursor(lastItem.createdAt, lastItem.id);\n\t\t}\n\n\t\treturn { items, nextCursor };\n\t}\n\n\t/**\n\t * Update media metadata\n\t */\n\tasync update(\n\t\tid: string,\n\t\tinput: Partial<Pick<CreateMediaInput, \"alt\" | \"caption\" | \"width\" | \"height\">>,\n\t): Promise<MediaItem | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst updates: Partial<MediaRow> = {};\n\t\tif (input.alt !== undefined) updates.alt = input.alt;\n\t\tif (input.caption !== undefined) updates.caption = input.caption;\n\t\tif (input.width !== undefined) updates.width = input.width;\n\t\tif (input.height !== undefined) updates.height = input.height;\n\n\t\tif (Object.keys(updates).length > 0) {\n\t\t\tawait this.db.updateTable(\"media\").set(updates).where(\"id\", \"=\", id).execute();\n\t\t}\n\n\t\treturn this.findById(id);\n\t}\n\n\t/**\n\t * Delete media item\n\t */\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst result = await this.db.deleteFrom(\"media\").where(\"id\", \"=\", id).executeTakeFirst();\n\n\t\treturn (result.numDeletedRows ?? 0) > 0;\n\t}\n\n\t/**\n\t * Count media items\n\t */\n\tasync count(mimeType?: string): Promise<number> {\n\t\tlet query = this.db.selectFrom(\"media\").select((eb) => eb.fn.count(\"id\").as(\"count\"));\n\n\t\tif (mimeType) {\n\t\t\tconst pattern = `${escapeLike(mimeType)}%`;\n\t\t\tquery = query.where(sql<SqlBool>`mime_type LIKE ${pattern} ESCAPE '\\\\'`);\n\t\t}\n\n\t\tconst result = await query.executeTakeFirst();\n\t\treturn Number(result?.count || 0);\n\t}\n\n\t/**\n\t * Delete pending uploads older than the given age.\n\t * Pending uploads that were never confirmed indicate abandoned upload flows.\n\t *\n\t * Returns the storage keys of deleted rows so callers can remove the\n\t * corresponding files from object storage.\n\t */\n\tasync cleanupPendingUploads(maxAgeMs: number = 60 * 60 * 1000): Promise<string[]> {\n\t\tconst cutoff = new Date(Date.now() - maxAgeMs).toISOString();\n\n\t\t// Select the storage keys first -- SQLite doesn't support RETURNING\n\t\t// on DELETE in all drivers, and Kysely's RETURNING isn't universal.\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"media\")\n\t\t\t.select(\"storage_key\")\n\t\t\t.where(\"status\", \"=\", \"pending\")\n\t\t\t.where(\"created_at\", \"<\", cutoff)\n\t\t\t.execute();\n\n\t\tif (rows.length === 0) return [];\n\n\t\tawait this.db\n\t\t\t.deleteFrom(\"media\")\n\t\t\t.where(\"status\", \"=\", \"pending\")\n\t\t\t.where(\"created_at\", \"<\", cutoff)\n\t\t\t.execute();\n\n\t\treturn rows.map((r) => r.storage_key);\n\t}\n\n\t/**\n\t * Convert database row to MediaItem\n\t */\n\tprivate rowToItem(row: MediaRow): MediaItem {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tfilename: row.filename,\n\t\t\tmimeType: row.mime_type,\n\t\t\tsize: row.size,\n\t\t\twidth: row.width,\n\t\t\theight: row.height,\n\t\t\talt: row.alt,\n\t\t\tcaption: row.caption,\n\t\t\tstorageKey: row.storage_key,\n\t\t\tcontentHash: row.content_hash,\n\t\t\tblurhash: row.blurhash,\n\t\t\tdominantColor: row.dominant_color,\n\t\t\t// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- DB stores string; validated at insert but linter can't follow\n\t\t\tstatus: row.status as MediaStatus,\n\t\t\tcreatedAt: row.created_at,\n\t\t\tauthorId: row.author_id,\n\t\t};\n\t}\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mode-CpNnGkPz.mjs","names":[],"sources":["../src/auth/mode.ts"],"sourcesContent":["/**\n * Auth Mode Detection\n *\n * Determines which authentication provider is active based on config.\n * Supports both passkey (default) and external auth providers via AuthDescriptor.\n */\n\nimport type { EmDashConfig } from \"../astro/integration/runtime.js\";\nimport type { AuthDescriptor, AuthResult, ExternalAuthConfig } from \"./types.js\";\n\nexport type { AuthDescriptor, AuthResult, ExternalAuthConfig };\n\n/**\n * Passkey auth mode (default)\n */\nexport interface PasskeyAuthMode {\n\ttype: \"passkey\";\n}\n\n/**\n * External auth provider mode (Cloudflare Access, etc.)\n */\nexport interface ExternalAuthMode {\n\ttype: \"external\";\n\t/** Provider type identifier (e.g., \"cloudflare-access\") */\n\tproviderType: string;\n\t/** Module to import for authentication */\n\tentrypoint: string;\n\t/** Provider-specific configuration */\n\tconfig: unknown;\n}\n\n/**\n * Union of all auth modes\n */\nexport type AuthMode = PasskeyAuthMode | ExternalAuthMode;\n\n/**\n * Extended config type with auth.\n *\n * This is the same as `EmDashConfig` with an optional `auth` field.\n * Kept for backwards compatibility — prefer `EmDashConfig` in new code\n * since `getAuthMode` now accepts `EmDashConfig` directly.\n */\nexport interface EmDashConfigWithAuth extends EmDashConfig {\n\tauth?: AuthDescriptor;\n}\n\n/**\n * Determine the active auth mode from config.\n *\n * Accepts `EmDashConfig` (or subtype) — checks for `auth` field via duck typing.\n *\n * @param config EmDash configuration\n * @returns The active auth mode\n */\nexport function getAuthMode(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): AuthMode {\n\tconst auth = config?.auth;\n\n\t// Check for AuthDescriptor (new style)\n\tif (auth && \"entrypoint\" in auth && auth.entrypoint) {\n\t\treturn {\n\t\t\ttype: \"external\",\n\t\t\tproviderType: auth.type,\n\t\t\tentrypoint: auth.entrypoint,\n\t\t\tconfig: auth.config,\n\t\t};\n\t}\n\n\t// Default to passkey\n\treturn { type: \"passkey\" };\n}\n\n/**\n * Check if an external auth provider is active\n */\nexport function isExternalAuthEnabled(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): boolean {\n\treturn getAuthMode(config).type === \"external\";\n}\n\n/**\n * Get external auth config if enabled\n */\nexport function getExternalAuthConfig(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): ExternalAuthMode | null {\n\tconst mode = getAuthMode(config);\n\tif (mode.type === \"external\") {\n\t\treturn mode;\n\t}\n\treturn null;\n}\n"],"mappings":";;;;;;;;;AAwDA,SAAgB,YACf,QACW;CACX,MAAM,OAAO,QAAQ;AAGrB,KAAI,QAAQ,gBAAgB,QAAQ,KAAK,WACxC,QAAO;EACN,MAAM;EACN,cAAc,KAAK;EACnB,YAAY,KAAK;EACjB,QAAQ,KAAK;EACb;AAIF,QAAO,EAAE,MAAM,WAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"redirect-7lGhLBNZ.mjs","names":[],"sources":["../src/database/repositories/redirect.ts"],"sourcesContent":["import { sql, type Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport {\n\tcompilePattern,\n\tmatchPattern,\n\tinterpolateDestination,\n\tisPattern,\n} from \"../../redirects/patterns.js\";\nimport { currentTimestampValue } from \"../dialect-helpers.js\";\nimport type { Database, RedirectTable } from \"../types.js\";\nimport { encodeCursor, decodeCursor, type FindManyResult } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface Redirect {\n\tid: string;\n\tsource: string;\n\tdestination: string;\n\ttype: number;\n\tisPattern: boolean;\n\tenabled: boolean;\n\thits: number;\n\tlastHitAt: string | null;\n\tgroupName: string | null;\n\tauto: boolean;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface CreateRedirectInput {\n\tsource: string;\n\tdestination: string;\n\ttype?: number;\n\tisPattern?: boolean;\n\tenabled?: boolean;\n\tgroupName?: string | null;\n\tauto?: boolean;\n}\n\nexport interface UpdateRedirectInput {\n\tsource?: string;\n\tdestination?: string;\n\ttype?: number;\n\tisPattern?: boolean;\n\tenabled?: boolean;\n\tgroupName?: string | null;\n}\n\nexport interface NotFoundEntry {\n\tid: string;\n\tpath: string;\n\treferrer: string | null;\n\tuserAgent: string | null;\n\tip: string | null;\n\tcreatedAt: string;\n}\n\nexport interface NotFoundSummary {\n\tpath: string;\n\tcount: number;\n\tlastSeen: string;\n\ttopReferrer: string | null;\n}\n\nexport interface RedirectMatch {\n\tredirect: Redirect;\n\tresolvedDestination: string;\n}\n\n// ---------------------------------------------------------------------------\n// Row mapping\n// ---------------------------------------------------------------------------\n\nfunction rowToRedirect(row: RedirectTable): Redirect {\n\treturn {\n\t\tid: row.id,\n\t\tsource: row.source,\n\t\tdestination: row.destination,\n\t\ttype: row.type,\n\t\tisPattern: row.is_pattern === 1,\n\t\tenabled: row.enabled === 1,\n\t\thits: row.hits,\n\t\tlastHitAt: row.last_hit_at,\n\t\tgroupName: row.group_name,\n\t\tauto: row.auto === 1,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Repository\n// ---------------------------------------------------------------------------\n\nexport class RedirectRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t// --- CRUD ---------------------------------------------------------------\n\n\tasync findById(id: string): Promise<Redirect | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_redirects\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToRedirect(row) : null;\n\t}\n\n\tasync findBySource(source: string): Promise<Redirect | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_redirects\")\n\t\t\t.selectAll()\n\t\t\t.where(\"source\", \"=\", source)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToRedirect(row) : null;\n\t}\n\n\tasync findMany(opts: {\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t\tsearch?: string;\n\t\tgroup?: string;\n\t\tenabled?: boolean;\n\t\tauto?: boolean;\n\t}): Promise<FindManyResult<Redirect>> {\n\t\tconst limit = Math.min(Math.max(opts.limit ?? 50, 1), 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_redirects\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tif (opts.search) {\n\t\t\tconst term = `%${opts.search}%`;\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([eb(\"source\", \"like\", term), eb(\"destination\", \"like\", term)]),\n\t\t\t);\n\t\t}\n\n\t\tif (opts.group !== undefined) {\n\t\t\tquery = query.where(\"group_name\", \"=\", opts.group);\n\t\t}\n\n\t\tif (opts.enabled !== undefined) {\n\t\t\tquery = query.where(\"enabled\", \"=\", opts.enabled ? 1 : 0);\n\t\t}\n\n\t\tif (opts.auto !== undefined) {\n\t\t\tquery = query.where(\"auto\", \"=\", opts.auto ? 1 : 0);\n\t\t}\n\n\t\tif (opts.cursor) {\n\t\t\tconst decoded = decodeCursor(opts.cursor);\n\t\t\tif (decoded) {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(\"created_at\", \"<\", decoded.orderValue),\n\t\t\t\t\t\teb.and([eb(\"created_at\", \"=\", decoded.orderValue), eb(\"id\", \"<\", decoded.id)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\tconst items = rows.slice(0, limit).map(rowToRedirect);\n\t\tconst result: FindManyResult<Redirect> = { items };\n\n\t\tif (rows.length > limit) {\n\t\t\tconst last = items.at(-1)!;\n\t\t\tresult.nextCursor = encodeCursor(last.createdAt, last.id);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tasync create(input: CreateRedirectInput): Promise<Redirect> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\t\tconst patternFlag = input.isPattern ?? isPattern(input.source);\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_redirects\")\n\t\t\t.values({\n\t\t\t\tid,\n\t\t\t\tsource: input.source,\n\t\t\t\tdestination: input.destination,\n\t\t\t\ttype: input.type ?? 301,\n\t\t\t\tis_pattern: patternFlag ? 1 : 0,\n\t\t\t\tenabled: input.enabled !== false ? 1 : 0,\n\t\t\t\thits: 0,\n\t\t\t\tlast_hit_at: null,\n\t\t\t\tgroup_name: input.groupName ?? null,\n\t\t\t\tauto: input.auto ? 1 : 0,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.execute();\n\n\t\treturn (await this.findById(id))!;\n\t}\n\n\tasync update(id: string, input: UpdateRedirectInput): Promise<Redirect | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\tconst now = new Date().toISOString();\n\t\tconst values: Record<string, unknown> = { updated_at: now };\n\n\t\tif (input.source !== undefined) {\n\t\t\tvalues.source = input.source;\n\t\t\tvalues.is_pattern =\n\t\t\t\tinput.isPattern !== undefined ? (input.isPattern ? 1 : 0) : isPattern(input.source) ? 1 : 0;\n\t\t} else if (input.isPattern !== undefined) {\n\t\t\tvalues.is_pattern = input.isPattern ? 1 : 0;\n\t\t}\n\n\t\tif (input.destination !== undefined) values.destination = input.destination;\n\t\tif (input.type !== undefined) values.type = input.type;\n\t\tif (input.enabled !== undefined) values.enabled = input.enabled ? 1 : 0;\n\t\tif (input.groupName !== undefined) values.group_name = input.groupName;\n\n\t\tawait this.db.updateTable(\"_emdash_redirects\").set(values).where(\"id\", \"=\", id).execute();\n\n\t\treturn (await this.findById(id))!;\n\t}\n\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_redirects\")\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn BigInt(result.numDeletedRows) > 0n;\n\t}\n\n\t/**\n\t * Fetch all enabled redirects (for loop detection graph building).\n\t * Not paginated — returns the full set.\n\t */\n\tasync findAllEnabled(): Promise<Redirect[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_redirects\")\n\t\t\t.selectAll()\n\t\t\t.where(\"enabled\", \"=\", 1)\n\t\t\t.execute();\n\t\treturn rows.map(rowToRedirect);\n\t}\n\n\t// --- Matching -----------------------------------------------------------\n\n\tasync findExactMatch(path: string): Promise<Redirect | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_redirects\")\n\t\t\t.selectAll()\n\t\t\t.where(\"source\", \"=\", path)\n\t\t\t.where(\"enabled\", \"=\", 1)\n\t\t\t.where(\"is_pattern\", \"=\", 0)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToRedirect(row) : null;\n\t}\n\n\tasync findEnabledPatternRules(): Promise<Redirect[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_redirects\")\n\t\t\t.selectAll()\n\t\t\t.where(\"enabled\", \"=\", 1)\n\t\t\t.where(\"is_pattern\", \"=\", 1)\n\t\t\t.execute();\n\t\treturn rows.map(rowToRedirect);\n\t}\n\n\t/**\n\t * Match a request path against all enabled redirect rules.\n\t * Checks exact matches first (indexed), then pattern rules.\n\t * Returns the matched redirect and the resolved destination URL.\n\t */\n\tasync matchPath(path: string): Promise<RedirectMatch | null> {\n\t\t// 1. Exact match (fast, indexed)\n\t\tconst exact = await this.findExactMatch(path);\n\t\tif (exact) {\n\t\t\treturn { redirect: exact, resolvedDestination: exact.destination };\n\t\t}\n\n\t\t// 2. Pattern match\n\t\tconst patterns = await this.findEnabledPatternRules();\n\t\tfor (const redirect of patterns) {\n\t\t\tconst compiled = compilePattern(redirect.source);\n\t\t\tconst params = matchPattern(compiled, path);\n\t\t\tif (params) {\n\t\t\t\tconst resolved = interpolateDestination(redirect.destination, params);\n\t\t\t\treturn { redirect, resolvedDestination: resolved };\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t// --- Hit tracking -------------------------------------------------------\n\n\tasync recordHit(id: string): Promise<void> {\n\t\tawait sql`\n\t\t\tUPDATE _emdash_redirects\n\t\t\tSET hits = hits + 1, last_hit_at = ${currentTimestampValue(this.db)}, updated_at = ${currentTimestampValue(this.db)}\n\t\t\tWHERE id = ${id}\n\t\t`.execute(this.db);\n\t}\n\n\t// --- Auto-redirects (slug change) ---------------------------------------\n\n\t/**\n\t * Create an auto-redirect when a content slug changes.\n\t * Uses the collection's URL pattern to compute old/new URLs.\n\t * Collapses existing redirect chains pointing to the old URL.\n\t */\n\tasync createAutoRedirect(\n\t\tcollection: string,\n\t\toldSlug: string,\n\t\tnewSlug: string,\n\t\tcontentId: string,\n\t\turlPattern: string | null,\n\t): Promise<Redirect> {\n\t\tconst oldUrl = urlPattern\n\t\t\t? urlPattern.replace(\"{slug}\", oldSlug).replace(\"{id}\", contentId)\n\t\t\t: `/${collection}/${oldSlug}`;\n\t\tconst newUrl = urlPattern\n\t\t\t? urlPattern.replace(\"{slug}\", newSlug).replace(\"{id}\", contentId)\n\t\t\t: `/${collection}/${newSlug}`;\n\n\t\t// Collapse chains: update any existing redirects pointing to the old URL\n\t\tawait this.collapseChains(oldUrl, newUrl);\n\n\t\t// Check if a redirect from this source already exists\n\t\tconst existing = await this.findBySource(oldUrl);\n\t\tif (existing) {\n\t\t\t// Update the existing redirect to point to the new URL\n\t\t\treturn (await this.update(existing.id, { destination: newUrl }))!;\n\t\t}\n\n\t\treturn this.create({\n\t\t\tsource: oldUrl,\n\t\t\tdestination: newUrl,\n\t\t\ttype: 301,\n\t\t\tisPattern: false,\n\t\t\tauto: true,\n\t\t\tgroupName: \"Auto: slug change\",\n\t\t});\n\t}\n\n\t/**\n\t * Update all redirects whose destination matches oldDestination\n\t * to point to newDestination instead. Prevents redirect chains.\n\t * Returns the number of updated rows.\n\t */\n\tasync collapseChains(oldDestination: string, newDestination: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.updateTable(\"_emdash_redirects\")\n\t\t\t.set({\n\t\t\t\tdestination: newDestination,\n\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t})\n\t\t\t.where(\"destination\", \"=\", oldDestination)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numUpdatedRows);\n\t}\n\n\t// --- 404 log ------------------------------------------------------------\n\n\tasync log404(entry: {\n\t\tpath: string;\n\t\treferrer?: string | null;\n\t\tuserAgent?: string | null;\n\t\tip?: string | null;\n\t}): Promise<void> {\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_404_log\")\n\t\t\t.values({\n\t\t\t\tid: ulid(),\n\t\t\t\tpath: entry.path,\n\t\t\t\treferrer: entry.referrer ?? null,\n\t\t\t\tuser_agent: entry.userAgent ?? null,\n\t\t\t\tip: entry.ip ?? null,\n\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t})\n\t\t\t.execute();\n\t}\n\n\tasync find404s(opts: {\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t\tsearch?: string;\n\t}): Promise<FindManyResult<NotFoundEntry>> {\n\t\tconst limit = Math.min(Math.max(opts.limit ?? 50, 1), 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_404_log\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tif (opts.search) {\n\t\t\tquery = query.where(\"path\", \"like\", `%${opts.search}%`);\n\t\t}\n\n\t\tif (opts.cursor) {\n\t\t\tconst decoded = decodeCursor(opts.cursor);\n\t\t\tif (decoded) {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(\"created_at\", \"<\", decoded.orderValue),\n\t\t\t\t\t\teb.and([eb(\"created_at\", \"=\", decoded.orderValue), eb(\"id\", \"<\", decoded.id)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\tconst items: NotFoundEntry[] = rows.slice(0, limit).map((row) => ({\n\t\t\tid: row.id,\n\t\t\tpath: row.path,\n\t\t\treferrer: row.referrer,\n\t\t\tuserAgent: row.user_agent,\n\t\t\tip: row.ip,\n\t\t\tcreatedAt: row.created_at,\n\t\t}));\n\n\t\tconst result: FindManyResult<NotFoundEntry> = { items };\n\t\tif (rows.length > limit) {\n\t\t\tconst last = items.at(-1)!;\n\t\t\tresult.nextCursor = encodeCursor(last.createdAt, last.id);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tasync get404Summary(limit = 50): Promise<NotFoundSummary[]> {\n\t\tconst rows = await sql<{\n\t\t\tpath: string;\n\t\t\tcount: number;\n\t\t\tlast_seen: string;\n\t\t\ttop_referrer: string | null;\n\t\t}>`\n\t\t\tSELECT\n\t\t\t\tpath,\n\t\t\t\tCOUNT(*) as count,\n\t\t\t\tMAX(created_at) as last_seen,\n\t\t\t\t(\n\t\t\t\t\tSELECT referrer FROM _emdash_404_log AS inner_log\n\t\t\t\t\tWHERE inner_log.path = _emdash_404_log.path\n\t\t\t\t\t\tAND referrer IS NOT NULL AND referrer != ''\n\t\t\t\t\tGROUP BY referrer\n\t\t\t\t\tORDER BY COUNT(*) DESC\n\t\t\t\t\tLIMIT 1\n\t\t\t\t) as top_referrer\n\t\t\tFROM _emdash_404_log\n\t\t\tGROUP BY path\n\t\t\tORDER BY count DESC\n\t\t\tLIMIT ${limit}\n\t\t`.execute(this.db);\n\n\t\treturn rows.rows.map((row) => ({\n\t\t\tpath: row.path,\n\t\t\tcount: Number(row.count),\n\t\t\tlastSeen: row.last_seen,\n\t\t\ttopReferrer: row.top_referrer,\n\t\t}));\n\t}\n\n\tasync delete404(id: string): Promise<boolean> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_404_log\")\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn BigInt(result.numDeletedRows) > 0n;\n\t}\n\n\tasync clear404s(): Promise<number> {\n\t\tconst result = await this.db.deleteFrom(\"_emdash_404_log\").executeTakeFirst();\n\t\treturn Number(result.numDeletedRows);\n\t}\n\n\tasync prune404s(olderThan: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_404_log\")\n\t\t\t.where(\"created_at\", \"<\", olderThan)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numDeletedRows);\n\t}\n}\n"],"mappings":"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"}