emdash 0.0.0-b → 0.0.1

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 (661) hide show
  1. package/README.md +87 -43
  2. package/dist/adapters-BLMa4JGD.d.mts +106 -0
  3. package/dist/adapters-BLMa4JGD.d.mts.map +1 -0
  4. package/dist/apply-Bjfq_b4-.mjs +1293 -0
  5. package/dist/apply-Bjfq_b4-.mjs.map +1 -0
  6. package/dist/astro/index.d.mts +51 -0
  7. package/dist/astro/index.d.mts.map +1 -0
  8. package/dist/astro/index.mjs +1333 -0
  9. package/dist/astro/index.mjs.map +1 -0
  10. package/dist/astro/middleware/auth.d.mts +31 -0
  11. package/dist/astro/middleware/auth.d.mts.map +1 -0
  12. package/dist/astro/middleware/auth.mjs +654 -0
  13. package/dist/astro/middleware/auth.mjs.map +1 -0
  14. package/dist/astro/middleware/redirect.d.mts +22 -0
  15. package/dist/astro/middleware/redirect.d.mts.map +1 -0
  16. package/dist/astro/middleware/redirect.mjs +63 -0
  17. package/dist/astro/middleware/redirect.mjs.map +1 -0
  18. package/dist/astro/middleware/request-context.d.mts +18 -0
  19. package/dist/astro/middleware/request-context.d.mts.map +1 -0
  20. package/dist/astro/middleware/request-context.mjs +1310 -0
  21. package/dist/astro/middleware/request-context.mjs.map +1 -0
  22. package/dist/astro/middleware/setup.d.mts +20 -0
  23. package/dist/astro/middleware/setup.d.mts.map +1 -0
  24. package/dist/astro/middleware/setup.mjs +47 -0
  25. package/dist/astro/middleware/setup.mjs.map +1 -0
  26. package/dist/astro/middleware.d.mts +13 -0
  27. package/dist/astro/middleware.d.mts.map +1 -0
  28. package/dist/astro/middleware.mjs +1613 -0
  29. package/dist/astro/middleware.mjs.map +1 -0
  30. package/dist/astro/types.d.mts +250 -0
  31. package/dist/astro/types.d.mts.map +1 -0
  32. package/dist/astro/types.mjs +1 -0
  33. package/dist/base64-MBPo9ozB.mjs +59 -0
  34. package/dist/base64-MBPo9ozB.mjs.map +1 -0
  35. package/dist/byline-CL847F26.mjs +213 -0
  36. package/dist/byline-CL847F26.mjs.map +1 -0
  37. package/dist/bylines-C2a-2TGt.mjs +136 -0
  38. package/dist/bylines-C2a-2TGt.mjs.map +1 -0
  39. package/dist/chunk-ClPoSABd.mjs +21 -0
  40. package/dist/cli/index.d.mts +1 -0
  41. package/dist/cli/index.mjs +3909 -0
  42. package/dist/cli/index.mjs.map +1 -0
  43. package/dist/client/cf-access.d.mts +60 -0
  44. package/dist/client/cf-access.d.mts.map +1 -0
  45. package/dist/client/cf-access.mjs +179 -0
  46. package/dist/client/cf-access.mjs.map +1 -0
  47. package/dist/client/index.d.mts +398 -0
  48. package/dist/client/index.d.mts.map +1 -0
  49. package/dist/client/index.mjs +346 -0
  50. package/dist/client/index.mjs.map +1 -0
  51. package/dist/config-CKE8p9xM.mjs +55 -0
  52. package/dist/config-CKE8p9xM.mjs.map +1 -0
  53. package/dist/connection-B4zVnQIa.mjs +40 -0
  54. package/dist/connection-B4zVnQIa.mjs.map +1 -0
  55. package/dist/content-D6C2WsZC.mjs +824 -0
  56. package/dist/content-D6C2WsZC.mjs.map +1 -0
  57. package/dist/db/index.d.mts +4 -0
  58. package/dist/db/index.mjs +62 -0
  59. package/dist/db/index.mjs.map +1 -0
  60. package/dist/db/libsql.d.mts +11 -0
  61. package/dist/db/libsql.d.mts.map +1 -0
  62. package/dist/db/libsql.mjs +17 -0
  63. package/dist/db/libsql.mjs.map +1 -0
  64. package/dist/db/postgres.d.mts +11 -0
  65. package/dist/db/postgres.d.mts.map +1 -0
  66. package/dist/db/postgres.mjs +30 -0
  67. package/dist/db/postgres.mjs.map +1 -0
  68. package/dist/db/sqlite.d.mts +11 -0
  69. package/dist/db/sqlite.d.mts.map +1 -0
  70. package/dist/db/sqlite.mjs +16 -0
  71. package/dist/db/sqlite.mjs.map +1 -0
  72. package/dist/default-Cyi4aAxu.mjs +81 -0
  73. package/dist/default-Cyi4aAxu.mjs.map +1 -0
  74. package/dist/dialect-helpers-B9uSp2GJ.mjs +90 -0
  75. package/dist/dialect-helpers-B9uSp2GJ.mjs.map +1 -0
  76. package/dist/error-Cxz0tQeO.mjs +27 -0
  77. package/dist/error-Cxz0tQeO.mjs.map +1 -0
  78. package/dist/index-C1xF3OGh.d.mts +4527 -0
  79. package/dist/index-C1xF3OGh.d.mts.map +1 -0
  80. package/dist/index.d.mts +16 -0
  81. package/dist/index.mjs +30 -0
  82. package/dist/load-yOOlckBj.mjs +28 -0
  83. package/dist/load-yOOlckBj.mjs.map +1 -0
  84. package/dist/loader-fz8Q_3EO.mjs +447 -0
  85. package/dist/loader-fz8Q_3EO.mjs.map +1 -0
  86. package/dist/manifest-schema-Dcl0R6nM.mjs +184 -0
  87. package/dist/manifest-schema-Dcl0R6nM.mjs.map +1 -0
  88. package/dist/media/index.d.mts +26 -0
  89. package/dist/media/index.d.mts.map +1 -0
  90. package/dist/media/index.mjs +55 -0
  91. package/dist/media/index.mjs.map +1 -0
  92. package/dist/media/local-runtime.d.mts +39 -0
  93. package/dist/media/local-runtime.d.mts.map +1 -0
  94. package/dist/media/local-runtime.mjs +133 -0
  95. package/dist/media/local-runtime.mjs.map +1 -0
  96. package/dist/media-DqHVh136.mjs +200 -0
  97. package/dist/media-DqHVh136.mjs.map +1 -0
  98. package/dist/mode-C2EzN1uE.mjs +23 -0
  99. package/dist/mode-C2EzN1uE.mjs.map +1 -0
  100. package/dist/page/index.d.mts +140 -0
  101. package/dist/page/index.d.mts.map +1 -0
  102. package/dist/page/index.mjs +416 -0
  103. package/dist/page/index.mjs.map +1 -0
  104. package/dist/placeholder-CmGAmqeO.d.mts +276 -0
  105. package/dist/placeholder-CmGAmqeO.d.mts.map +1 -0
  106. package/dist/placeholder-SmpOx-_v.mjs +243 -0
  107. package/dist/placeholder-SmpOx-_v.mjs.map +1 -0
  108. package/dist/plugin-utils.d.mts +58 -0
  109. package/dist/plugin-utils.d.mts.map +1 -0
  110. package/dist/plugin-utils.mjs +78 -0
  111. package/dist/plugin-utils.mjs.map +1 -0
  112. package/dist/plugins/adapt-sandbox-entry.d.mts +22 -0
  113. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -0
  114. package/dist/plugins/adapt-sandbox-entry.mjs +113 -0
  115. package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -0
  116. package/dist/query-CS_iSj34.mjs +460 -0
  117. package/dist/query-CS_iSj34.mjs.map +1 -0
  118. package/dist/redirect-DIfIni3r.mjs +329 -0
  119. package/dist/redirect-DIfIni3r.mjs.map +1 -0
  120. package/dist/registry-D_w5HW4G.mjs +863 -0
  121. package/dist/registry-D_w5HW4G.mjs.map +1 -0
  122. package/dist/request-context.d.mts +49 -0
  123. package/dist/request-context.d.mts.map +1 -0
  124. package/dist/request-context.mjs +43 -0
  125. package/dist/request-context.mjs.map +1 -0
  126. package/dist/runner-B-u2F2b6.mjs +1412 -0
  127. package/dist/runner-B-u2F2b6.mjs.map +1 -0
  128. package/dist/runner-EAtf0ZIe.d.mts +27 -0
  129. package/dist/runner-EAtf0ZIe.d.mts.map +1 -0
  130. package/dist/runtime.d.mts +26 -0
  131. package/dist/runtime.d.mts.map +1 -0
  132. package/dist/runtime.mjs +42 -0
  133. package/dist/runtime.mjs.map +1 -0
  134. package/dist/search-DG603UrT.mjs +9211 -0
  135. package/dist/search-DG603UrT.mjs.map +1 -0
  136. package/dist/seed/index.d.mts +3 -0
  137. package/dist/seed/index.mjs +15 -0
  138. package/dist/seo/index.d.mts +70 -0
  139. package/dist/seo/index.d.mts.map +1 -0
  140. package/dist/seo/index.mjs +70 -0
  141. package/dist/seo/index.mjs.map +1 -0
  142. package/dist/storage/local.d.mts +39 -0
  143. package/dist/storage/local.d.mts.map +1 -0
  144. package/dist/storage/local.mjs +166 -0
  145. package/dist/storage/local.mjs.map +1 -0
  146. package/dist/storage/s3.d.mts +32 -0
  147. package/dist/storage/s3.d.mts.map +1 -0
  148. package/dist/storage/s3.mjs +175 -0
  149. package/dist/storage/s3.mjs.map +1 -0
  150. package/dist/tokens-DpgrkrXK.mjs +171 -0
  151. package/dist/tokens-DpgrkrXK.mjs.map +1 -0
  152. package/dist/transport-BFGblqwG.d.mts +42 -0
  153. package/dist/transport-BFGblqwG.d.mts.map +1 -0
  154. package/dist/transport-yxiQsi8I.mjs +418 -0
  155. package/dist/transport-yxiQsi8I.mjs.map +1 -0
  156. package/dist/types-BRuPJGdV.d.mts +102 -0
  157. package/dist/types-BRuPJGdV.d.mts.map +1 -0
  158. package/dist/types-C4-fAxN3.d.mts +182 -0
  159. package/dist/types-C4-fAxN3.d.mts.map +1 -0
  160. package/dist/types-CMMN0pNg.mjs +31 -0
  161. package/dist/types-CMMN0pNg.mjs.map +1 -0
  162. package/dist/types-CUBbjgmP.mjs +16 -0
  163. package/dist/types-CUBbjgmP.mjs.map +1 -0
  164. package/dist/types-DRjfYOEv.d.mts +426 -0
  165. package/dist/types-DRjfYOEv.d.mts.map +1 -0
  166. package/dist/types-DY5zk5HN.mjs +73 -0
  167. package/dist/types-DY5zk5HN.mjs.map +1 -0
  168. package/dist/types-DaNLHo_T.d.mts +184 -0
  169. package/dist/types-DaNLHo_T.d.mts.map +1 -0
  170. package/dist/types-DvhsUmSJ.d.mts +1111 -0
  171. package/dist/types-DvhsUmSJ.d.mts.map +1 -0
  172. package/dist/validate-CpBtVMsD.d.mts +378 -0
  173. package/dist/validate-CpBtVMsD.d.mts.map +1 -0
  174. package/dist/validate-CqRJb_xU.mjs +97 -0
  175. package/dist/validate-CqRJb_xU.mjs.map +1 -0
  176. package/dist/validate-O7PWmlnq.mjs +328 -0
  177. package/dist/validate-O7PWmlnq.mjs.map +1 -0
  178. package/locals.d.ts +46 -0
  179. package/package.json +233 -19
  180. package/src/api/authorize.ts +63 -0
  181. package/src/api/csrf.ts +48 -0
  182. package/src/api/error.ts +99 -0
  183. package/src/api/errors.ts +445 -0
  184. package/src/api/escape.ts +9 -0
  185. package/src/api/handlers/api-tokens.ts +240 -0
  186. package/src/api/handlers/comments.ts +314 -0
  187. package/src/api/handlers/content.ts +1315 -0
  188. package/src/api/handlers/dashboard.ts +205 -0
  189. package/src/api/handlers/device-flow.ts +687 -0
  190. package/src/api/handlers/index.ts +163 -0
  191. package/src/api/handlers/manifest.ts +158 -0
  192. package/src/api/handlers/marketplace.ts +930 -0
  193. package/src/api/handlers/media.ts +207 -0
  194. package/src/api/handlers/menus.ts +493 -0
  195. package/src/api/handlers/oauth-authorization.ts +429 -0
  196. package/src/api/handlers/oauth-clients.ts +353 -0
  197. package/src/api/handlers/oauth-user-lookup.ts +39 -0
  198. package/src/api/handlers/plugins.ts +254 -0
  199. package/src/api/handlers/redirects.ts +360 -0
  200. package/src/api/handlers/revision.ts +145 -0
  201. package/src/api/handlers/schema.ts +534 -0
  202. package/src/api/handlers/sections.ts +289 -0
  203. package/src/api/handlers/seo.ts +115 -0
  204. package/src/api/handlers/settings.ts +49 -0
  205. package/src/api/handlers/snapshot.ts +350 -0
  206. package/src/api/handlers/taxonomies.ts +523 -0
  207. package/src/api/index.ts +6 -0
  208. package/src/api/openapi/document.ts +2368 -0
  209. package/src/api/openapi/index.ts +1 -0
  210. package/src/api/parse.ts +139 -0
  211. package/src/api/redirect.ts +14 -0
  212. package/src/api/rev.ts +67 -0
  213. package/src/api/schemas/auth.ts +112 -0
  214. package/src/api/schemas/bylines.ts +85 -0
  215. package/src/api/schemas/comments.ts +117 -0
  216. package/src/api/schemas/common.ts +89 -0
  217. package/src/api/schemas/content.ts +191 -0
  218. package/src/api/schemas/import.ts +52 -0
  219. package/src/api/schemas/index.ts +17 -0
  220. package/src/api/schemas/media.ts +116 -0
  221. package/src/api/schemas/menus.ts +111 -0
  222. package/src/api/schemas/redirects.ts +155 -0
  223. package/src/api/schemas/schema.ts +203 -0
  224. package/src/api/schemas/search.ts +63 -0
  225. package/src/api/schemas/sections.ts +67 -0
  226. package/src/api/schemas/settings.ts +63 -0
  227. package/src/api/schemas/setup.ts +37 -0
  228. package/src/api/schemas/taxonomies.ts +113 -0
  229. package/src/api/schemas/users.ts +96 -0
  230. package/src/api/schemas/widgets.ts +80 -0
  231. package/src/api/site-url.ts +25 -0
  232. package/src/api/types.ts +82 -0
  233. package/src/astro/index.ts +27 -0
  234. package/src/astro/integration/index.ts +303 -0
  235. package/src/astro/integration/routes.ts +834 -0
  236. package/src/astro/integration/runtime.ts +338 -0
  237. package/src/astro/integration/virtual-modules.ts +469 -0
  238. package/src/astro/integration/vite-config.ts +328 -0
  239. package/src/astro/middleware/auth.ts +743 -0
  240. package/src/astro/middleware/redirect.ts +89 -0
  241. package/src/astro/middleware/request-context.ts +129 -0
  242. package/src/astro/middleware/setup.ts +89 -0
  243. package/src/astro/middleware.ts +398 -0
  244. package/src/astro/routes/PluginRegistry.tsx +15 -0
  245. package/src/astro/routes/admin.astro +81 -0
  246. package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
  247. package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
  248. package/src/astro/routes/api/admin/api-tokens/[id].ts +40 -0
  249. package/src/astro/routes/api/admin/api-tokens/index.ts +68 -0
  250. package/src/astro/routes/api/admin/bylines/[id]/index.ts +87 -0
  251. package/src/astro/routes/api/admin/bylines/index.ts +72 -0
  252. package/src/astro/routes/api/admin/comments/[id]/status.ts +120 -0
  253. package/src/astro/routes/api/admin/comments/[id].ts +64 -0
  254. package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
  255. package/src/astro/routes/api/admin/comments/counts.ts +30 -0
  256. package/src/astro/routes/api/admin/comments/index.ts +46 -0
  257. package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +91 -0
  258. package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
  259. package/src/astro/routes/api/admin/oauth-clients/[id].ts +110 -0
  260. package/src/astro/routes/api/admin/oauth-clients/index.ts +71 -0
  261. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +39 -0
  262. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +39 -0
  263. package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
  264. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +48 -0
  265. package/src/astro/routes/api/admin/plugins/[id]/update.ts +59 -0
  266. package/src/astro/routes/api/admin/plugins/index.ts +32 -0
  267. package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +61 -0
  268. package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
  269. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +62 -0
  270. package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
  271. package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
  272. package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
  273. package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +61 -0
  274. package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
  275. package/src/astro/routes/api/admin/users/[id]/disable.ts +69 -0
  276. package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
  277. package/src/astro/routes/api/admin/users/[id]/index.ts +146 -0
  278. package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
  279. package/src/astro/routes/api/admin/users/index.ts +66 -0
  280. package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
  281. package/src/astro/routes/api/auth/invite/accept.ts +52 -0
  282. package/src/astro/routes/api/auth/invite/complete.ts +84 -0
  283. package/src/astro/routes/api/auth/invite/index.ts +99 -0
  284. package/src/astro/routes/api/auth/logout.ts +40 -0
  285. package/src/astro/routes/api/auth/magic-link/send.ts +89 -0
  286. package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
  287. package/src/astro/routes/api/auth/me.ts +60 -0
  288. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +219 -0
  289. package/src/astro/routes/api/auth/oauth/[provider].ts +119 -0
  290. package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
  291. package/src/astro/routes/api/auth/passkey/index.ts +54 -0
  292. package/src/astro/routes/api/auth/passkey/options.ts +82 -0
  293. package/src/astro/routes/api/auth/passkey/register/options.ts +86 -0
  294. package/src/astro/routes/api/auth/passkey/register/verify.ts +117 -0
  295. package/src/astro/routes/api/auth/passkey/verify.ts +66 -0
  296. package/src/astro/routes/api/auth/signup/complete.ts +85 -0
  297. package/src/astro/routes/api/auth/signup/request.ts +77 -0
  298. package/src/astro/routes/api/auth/signup/verify.ts +53 -0
  299. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +312 -0
  300. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
  301. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +54 -0
  302. package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +61 -0
  303. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +33 -0
  304. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
  305. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +56 -0
  306. package/src/astro/routes/api/content/[collection]/[id]/restore.ts +54 -0
  307. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
  308. package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +105 -0
  309. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +140 -0
  310. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +30 -0
  311. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +56 -0
  312. package/src/astro/routes/api/content/[collection]/[id].ts +137 -0
  313. package/src/astro/routes/api/content/[collection]/index.ts +59 -0
  314. package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
  315. package/src/astro/routes/api/dashboard.ts +32 -0
  316. package/src/astro/routes/api/dev/emails.ts +36 -0
  317. package/src/astro/routes/api/import/probe.ts +47 -0
  318. package/src/astro/routes/api/import/wordpress/analyze.ts +510 -0
  319. package/src/astro/routes/api/import/wordpress/execute.ts +283 -0
  320. package/src/astro/routes/api/import/wordpress/media.ts +338 -0
  321. package/src/astro/routes/api/import/wordpress/prepare.ts +181 -0
  322. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +393 -0
  323. package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
  324. package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
  325. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +347 -0
  326. package/src/astro/routes/api/manifest.ts +62 -0
  327. package/src/astro/routes/api/mcp.ts +124 -0
  328. package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
  329. package/src/astro/routes/api/media/[id].ts +145 -0
  330. package/src/astro/routes/api/media/file/[key].ts +79 -0
  331. package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +86 -0
  332. package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
  333. package/src/astro/routes/api/media/providers/index.ts +30 -0
  334. package/src/astro/routes/api/media/upload-url.ts +137 -0
  335. package/src/astro/routes/api/media.ts +190 -0
  336. package/src/astro/routes/api/menus/[name]/items.ts +87 -0
  337. package/src/astro/routes/api/menus/[name]/reorder.ts +33 -0
  338. package/src/astro/routes/api/menus/[name].ts +65 -0
  339. package/src/astro/routes/api/menus/index.ts +47 -0
  340. package/src/astro/routes/api/oauth/authorize.ts +412 -0
  341. package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
  342. package/src/astro/routes/api/oauth/device/code.ts +51 -0
  343. package/src/astro/routes/api/oauth/device/token.ts +69 -0
  344. package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
  345. package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
  346. package/src/astro/routes/api/oauth/token.ts +184 -0
  347. package/src/astro/routes/api/openapi.json.ts +32 -0
  348. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +92 -0
  349. package/src/astro/routes/api/redirects/404s/index.ts +72 -0
  350. package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
  351. package/src/astro/routes/api/redirects/[id].ts +84 -0
  352. package/src/astro/routes/api/redirects/index.ts +52 -0
  353. package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
  354. package/src/astro/routes/api/revisions/[revisionId]/restore.ts +62 -0
  355. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +76 -0
  356. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +52 -0
  357. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +32 -0
  358. package/src/astro/routes/api/schema/collections/[slug]/index.ts +80 -0
  359. package/src/astro/routes/api/schema/collections/index.ts +47 -0
  360. package/src/astro/routes/api/schema/index.ts +109 -0
  361. package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
  362. package/src/astro/routes/api/schema/orphans/index.ts +26 -0
  363. package/src/astro/routes/api/search/enable.ts +64 -0
  364. package/src/astro/routes/api/search/index.ts +55 -0
  365. package/src/astro/routes/api/search/rebuild.ts +72 -0
  366. package/src/astro/routes/api/search/stats.ts +35 -0
  367. package/src/astro/routes/api/search/suggest.ts +53 -0
  368. package/src/astro/routes/api/sections/[slug].ts +84 -0
  369. package/src/astro/routes/api/sections/index.ts +52 -0
  370. package/src/astro/routes/api/settings/email.ts +150 -0
  371. package/src/astro/routes/api/settings.ts +67 -0
  372. package/src/astro/routes/api/setup/admin-verify.ts +100 -0
  373. package/src/astro/routes/api/setup/admin.ts +94 -0
  374. package/src/astro/routes/api/setup/dev-bypass.ts +199 -0
  375. package/src/astro/routes/api/setup/dev-reset.ts +40 -0
  376. package/src/astro/routes/api/setup/index.ts +126 -0
  377. package/src/astro/routes/api/setup/status.ts +122 -0
  378. package/src/astro/routes/api/snapshot.ts +75 -0
  379. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +95 -0
  380. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +69 -0
  381. package/src/astro/routes/api/taxonomies/index.ts +59 -0
  382. package/src/astro/routes/api/themes/preview.ts +77 -0
  383. package/src/astro/routes/api/typegen.ts +114 -0
  384. package/src/astro/routes/api/well-known/auth.ts +68 -0
  385. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +44 -0
  386. package/src/astro/routes/api/well-known/oauth-protected-resource.ts +37 -0
  387. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +72 -0
  388. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +127 -0
  389. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +80 -0
  390. package/src/astro/routes/api/widget-areas/[name].ts +87 -0
  391. package/src/astro/routes/api/widget-areas/index.ts +99 -0
  392. package/src/astro/routes/api/widget-components.ts +22 -0
  393. package/src/astro/routes/robots.txt.ts +77 -0
  394. package/src/astro/routes/sitemap.xml.ts +97 -0
  395. package/src/astro/storage/adapters.ts +74 -0
  396. package/src/astro/storage/index.ts +19 -0
  397. package/src/astro/storage/types.ts +60 -0
  398. package/src/astro/types.ts +346 -0
  399. package/src/auth/api-tokens.ts +25 -0
  400. package/src/auth/challenge-store.ts +80 -0
  401. package/src/auth/mode.ts +96 -0
  402. package/src/auth/oauth-state-store.ts +96 -0
  403. package/src/auth/passkey-config.ts +27 -0
  404. package/src/auth/rate-limit.ts +158 -0
  405. package/src/auth/scopes.ts +33 -0
  406. package/src/auth/types.ts +104 -0
  407. package/src/aws-sdk.d.ts +100 -0
  408. package/src/bylines/index.ts +237 -0
  409. package/src/cleanup.ts +153 -0
  410. package/src/cli/client-factory.ts +100 -0
  411. package/src/cli/commands/auth.ts +46 -0
  412. package/src/cli/commands/bundle-utils.ts +247 -0
  413. package/src/cli/commands/bundle.ts +609 -0
  414. package/src/cli/commands/content.ts +442 -0
  415. package/src/cli/commands/dev.ts +191 -0
  416. package/src/cli/commands/doctor.ts +211 -0
  417. package/src/cli/commands/export-seed.ts +630 -0
  418. package/src/cli/commands/import/wordpress.ts +1056 -0
  419. package/src/cli/commands/init.ts +192 -0
  420. package/src/cli/commands/login.ts +547 -0
  421. package/src/cli/commands/media.ts +165 -0
  422. package/src/cli/commands/menu.ts +67 -0
  423. package/src/cli/commands/plugin-init.ts +291 -0
  424. package/src/cli/commands/plugin-validate.ts +31 -0
  425. package/src/cli/commands/plugin.ts +33 -0
  426. package/src/cli/commands/publish.ts +699 -0
  427. package/src/cli/commands/schema.ts +233 -0
  428. package/src/cli/commands/search-cmd.ts +54 -0
  429. package/src/cli/commands/seed.ts +288 -0
  430. package/src/cli/commands/taxonomy.ts +128 -0
  431. package/src/cli/commands/types.ts +68 -0
  432. package/src/cli/credentials.ts +236 -0
  433. package/src/cli/index.ts +70 -0
  434. package/src/cli/output.ts +75 -0
  435. package/src/cli/wxr/parser.ts +969 -0
  436. package/src/client/cf-access.ts +193 -0
  437. package/src/client/index.ts +854 -0
  438. package/src/client/portable-text.ts +413 -0
  439. package/src/client/transport.ts +200 -0
  440. package/src/comments/moderator.ts +46 -0
  441. package/src/comments/notifications.ts +144 -0
  442. package/src/comments/query.ts +105 -0
  443. package/src/comments/service.ts +213 -0
  444. package/src/components/Break.astro +45 -0
  445. package/src/components/Button.astro +71 -0
  446. package/src/components/Buttons.astro +49 -0
  447. package/src/components/Code.astro +59 -0
  448. package/src/components/Columns.astro +59 -0
  449. package/src/components/CommentForm.astro +315 -0
  450. package/src/components/Comments.astro +232 -0
  451. package/src/components/Cover.astro +128 -0
  452. package/src/components/EmDashBodyEnd.astro +32 -0
  453. package/src/components/EmDashBodyStart.astro +32 -0
  454. package/src/components/EmDashHead.astro +53 -0
  455. package/src/components/EmDashImage.astro +178 -0
  456. package/src/components/EmDashMedia.astro +167 -0
  457. package/src/components/Embed.astro +128 -0
  458. package/src/components/File.astro +122 -0
  459. package/src/components/Gallery.astro +93 -0
  460. package/src/components/HtmlBlock.astro +33 -0
  461. package/src/components/Image.astro +178 -0
  462. package/src/components/InlineEditor.astro +27 -0
  463. package/src/components/InlinePortableTextEditor.tsx +1905 -0
  464. package/src/components/LiveSearch.astro +614 -0
  465. package/src/components/PortableText.astro +51 -0
  466. package/src/components/Pullquote.astro +51 -0
  467. package/src/components/Table.astro +108 -0
  468. package/src/components/WidgetArea.astro +22 -0
  469. package/src/components/WidgetRenderer.astro +72 -0
  470. package/src/components/index.ts +116 -0
  471. package/src/components/marks/Link.astro +31 -0
  472. package/src/components/marks/StrikeThrough.astro +7 -0
  473. package/src/components/marks/Subscript.astro +7 -0
  474. package/src/components/marks/Superscript.astro +7 -0
  475. package/src/components/marks/Underline.astro +7 -0
  476. package/src/components/widgets/Archives.astro +65 -0
  477. package/src/components/widgets/Categories.astro +35 -0
  478. package/src/components/widgets/RecentPosts.astro +51 -0
  479. package/src/components/widgets/Search.astro +18 -0
  480. package/src/components/widgets/Tags.astro +38 -0
  481. package/src/content/converters/index.ts +9 -0
  482. package/src/content/converters/portable-text-to-prosemirror.ts +385 -0
  483. package/src/content/converters/prosemirror-to-portable-text.ts +413 -0
  484. package/src/content/converters/types.ts +120 -0
  485. package/src/content/index.ts +5 -0
  486. package/src/database/connection.ts +67 -0
  487. package/src/database/dialect-helpers.ts +138 -0
  488. package/src/database/index.ts +5 -0
  489. package/src/database/migrations/001_initial.ts +136 -0
  490. package/src/database/migrations/002_media_status.ts +26 -0
  491. package/src/database/migrations/003_schema_registry.ts +79 -0
  492. package/src/database/migrations/004_plugins.ts +62 -0
  493. package/src/database/migrations/005_menus.ts +67 -0
  494. package/src/database/migrations/006_taxonomy_defs.ts +51 -0
  495. package/src/database/migrations/007_widgets.ts +42 -0
  496. package/src/database/migrations/008_auth.ts +194 -0
  497. package/src/database/migrations/009_user_disabled.ts +27 -0
  498. package/src/database/migrations/011_sections.ts +65 -0
  499. package/src/database/migrations/012_search.ts +25 -0
  500. package/src/database/migrations/013_scheduled_publishing.ts +51 -0
  501. package/src/database/migrations/014_draft_revisions.ts +72 -0
  502. package/src/database/migrations/015_indexes.ts +82 -0
  503. package/src/database/migrations/016_api_tokens.ts +89 -0
  504. package/src/database/migrations/017_authorization_codes.ts +45 -0
  505. package/src/database/migrations/018_seo.ts +56 -0
  506. package/src/database/migrations/019_i18n.ts +618 -0
  507. package/src/database/migrations/020_collection_url_pattern.ts +23 -0
  508. package/src/database/migrations/021_remove_section_categories.ts +43 -0
  509. package/src/database/migrations/022_marketplace_plugin_state.ts +46 -0
  510. package/src/database/migrations/023_plugin_metadata.ts +33 -0
  511. package/src/database/migrations/024_media_placeholders.ts +32 -0
  512. package/src/database/migrations/025_oauth_clients.ts +28 -0
  513. package/src/database/migrations/026_cron_tasks.ts +49 -0
  514. package/src/database/migrations/027_comments.ts +87 -0
  515. package/src/database/migrations/028_drop_author_url.ts +9 -0
  516. package/src/database/migrations/029_redirects.ts +67 -0
  517. package/src/database/migrations/030_widen_scheduled_index.ts +48 -0
  518. package/src/database/migrations/031_bylines.ts +90 -0
  519. package/src/database/migrations/032_rate_limits.ts +42 -0
  520. package/src/database/migrations/runner.ts +170 -0
  521. package/src/database/repositories/audit.ts +294 -0
  522. package/src/database/repositories/byline.ts +387 -0
  523. package/src/database/repositories/comment.ts +458 -0
  524. package/src/database/repositories/content.ts +1144 -0
  525. package/src/database/repositories/index.ts +30 -0
  526. package/src/database/repositories/media.ts +347 -0
  527. package/src/database/repositories/options.ts +150 -0
  528. package/src/database/repositories/plugin-storage.ts +373 -0
  529. package/src/database/repositories/redirect.ts +480 -0
  530. package/src/database/repositories/revision.ts +200 -0
  531. package/src/database/repositories/seo.ts +176 -0
  532. package/src/database/repositories/taxonomy.ts +294 -0
  533. package/src/database/repositories/types.ts +132 -0
  534. package/src/database/repositories/user.ts +258 -0
  535. package/src/database/transaction.ts +54 -0
  536. package/src/database/types.ts +501 -0
  537. package/src/database/validate.ts +138 -0
  538. package/src/db/adapters.ts +125 -0
  539. package/src/db/index.ts +37 -0
  540. package/src/db/libsql.ts +23 -0
  541. package/src/db/postgres.ts +30 -0
  542. package/src/db/sqlite.ts +27 -0
  543. package/src/emdash-runtime.ts +2096 -0
  544. package/src/fields/boolean.ts +34 -0
  545. package/src/fields/datetime.ts +44 -0
  546. package/src/fields/file.ts +41 -0
  547. package/src/fields/image.ts +34 -0
  548. package/src/fields/index.ts +42 -0
  549. package/src/fields/integer.ts +50 -0
  550. package/src/fields/json.ts +37 -0
  551. package/src/fields/multiselect.ts +48 -0
  552. package/src/fields/number.ts +52 -0
  553. package/src/fields/portable-text.ts +33 -0
  554. package/src/fields/reference.ts +29 -0
  555. package/src/fields/richtext.ts +31 -0
  556. package/src/fields/select.ts +46 -0
  557. package/src/fields/slug.ts +38 -0
  558. package/src/fields/text.ts +55 -0
  559. package/src/fields/textarea.ts +52 -0
  560. package/src/fields/types.ts +64 -0
  561. package/src/i18n/config.ts +68 -0
  562. package/src/import/index.ts +90 -0
  563. package/src/import/menus.ts +436 -0
  564. package/src/import/registry.ts +111 -0
  565. package/src/import/sections.ts +103 -0
  566. package/src/import/settings.ts +281 -0
  567. package/src/import/sources/wordpress-plugin.ts +641 -0
  568. package/src/import/sources/wordpress-rest.ts +191 -0
  569. package/src/import/sources/wxr.ts +330 -0
  570. package/src/import/ssrf.ts +260 -0
  571. package/src/import/types.ts +418 -0
  572. package/src/import/utils.ts +412 -0
  573. package/src/index.ts +481 -0
  574. package/src/loader.ts +770 -0
  575. package/src/mcp/server.ts +1463 -0
  576. package/src/media/index.ts +32 -0
  577. package/src/media/local-runtime.ts +213 -0
  578. package/src/media/local.ts +46 -0
  579. package/src/media/normalize.ts +190 -0
  580. package/src/media/placeholder.ts +150 -0
  581. package/src/media/provider-loader.ts +78 -0
  582. package/src/media/types.ts +279 -0
  583. package/src/menus/index.ts +324 -0
  584. package/src/menus/types.ts +112 -0
  585. package/src/page/context.ts +93 -0
  586. package/src/page/fragments.ts +89 -0
  587. package/src/page/index.ts +58 -0
  588. package/src/page/jsonld.ts +94 -0
  589. package/src/page/metadata.ts +185 -0
  590. package/src/page/seo-contributions.ts +136 -0
  591. package/src/plugin-utils.ts +80 -0
  592. package/src/plugins/adapt-sandbox-entry.ts +207 -0
  593. package/src/plugins/context.ts +833 -0
  594. package/src/plugins/cron.ts +361 -0
  595. package/src/plugins/define-plugin.ts +259 -0
  596. package/src/plugins/email-console.ts +73 -0
  597. package/src/plugins/email.ts +209 -0
  598. package/src/plugins/hooks.ts +1273 -0
  599. package/src/plugins/index.ts +193 -0
  600. package/src/plugins/manager.ts +595 -0
  601. package/src/plugins/manifest-schema.ts +230 -0
  602. package/src/plugins/marketplace.ts +460 -0
  603. package/src/plugins/request-meta.ts +139 -0
  604. package/src/plugins/routes.ts +302 -0
  605. package/src/plugins/sandbox/index.ts +18 -0
  606. package/src/plugins/sandbox/noop.ts +76 -0
  607. package/src/plugins/sandbox/types.ts +173 -0
  608. package/src/plugins/scheduler/node.ts +122 -0
  609. package/src/plugins/scheduler/piggyback.ts +71 -0
  610. package/src/plugins/scheduler/types.ts +27 -0
  611. package/src/plugins/state.ts +208 -0
  612. package/src/plugins/storage-indexes.ts +326 -0
  613. package/src/plugins/storage-query.ts +240 -0
  614. package/src/plugins/types.ts +1284 -0
  615. package/src/preview/helpers.ts +27 -0
  616. package/src/preview/index.ts +40 -0
  617. package/src/preview/tokens.ts +279 -0
  618. package/src/preview/urls.ts +118 -0
  619. package/src/query.ts +674 -0
  620. package/src/redirects/patterns.ts +224 -0
  621. package/src/request-context.ts +67 -0
  622. package/src/runtime.ts +21 -0
  623. package/src/schema/index.ts +29 -0
  624. package/src/schema/query.ts +44 -0
  625. package/src/schema/registry.ts +965 -0
  626. package/src/schema/types.ts +276 -0
  627. package/src/schema/zod-generator.ts +413 -0
  628. package/src/search/fts-manager.ts +452 -0
  629. package/src/search/index.ts +26 -0
  630. package/src/search/query.ts +396 -0
  631. package/src/search/text-extraction.ts +162 -0
  632. package/src/search/types.ts +114 -0
  633. package/src/sections/index.ts +226 -0
  634. package/src/sections/types.ts +86 -0
  635. package/src/seed/apply.ts +1141 -0
  636. package/src/seed/default.ts +86 -0
  637. package/src/seed/index.ts +28 -0
  638. package/src/seed/load.ts +35 -0
  639. package/src/seed/types.ts +341 -0
  640. package/src/seed/validate.ts +642 -0
  641. package/src/seo/index.ts +179 -0
  642. package/src/settings/index.ts +203 -0
  643. package/src/settings/types.ts +58 -0
  644. package/src/storage/index.ts +28 -0
  645. package/src/storage/local.ts +253 -0
  646. package/src/storage/s3.ts +271 -0
  647. package/src/storage/types.ts +204 -0
  648. package/src/taxonomies/index.ts +309 -0
  649. package/src/taxonomies/types.ts +61 -0
  650. package/src/ui.ts +75 -0
  651. package/src/utils/base64.ts +73 -0
  652. package/src/utils/hash.ts +36 -0
  653. package/src/utils/sanitize.ts +20 -0
  654. package/src/utils/slugify.ts +29 -0
  655. package/src/utils/url.ts +48 -0
  656. package/src/virtual-modules.d.ts +111 -0
  657. package/src/visual-editing/editable.ts +108 -0
  658. package/src/visual-editing/toolbar.ts +1229 -0
  659. package/src/widgets/components.ts +105 -0
  660. package/src/widgets/index.ts +131 -0
  661. package/src/widgets/types.ts +81 -0
@@ -0,0 +1,863 @@
1
+ import { t as __exportAll } from "./chunk-ClPoSABd.mjs";
2
+ import { a as isSqlite, c as tableExists, n as currentTimestamp, s as listTablesLike } from "./dialect-helpers-B9uSp2GJ.mjs";
3
+ import { t as validateIdentifier } from "./validate-CqRJb_xU.mjs";
4
+ import { i as RESERVED_FIELD_SLUGS, n as FIELD_TYPE_TO_COLUMN, r as RESERVED_COLLECTION_SLUGS } from "./types-DY5zk5HN.mjs";
5
+ import { sql } from "kysely";
6
+ import { ulid } from "ulidx";
7
+
8
+ //#region src/database/transaction.ts
9
+ /**
10
+ * Run a callback inside a transaction if supported, or directly if not.
11
+ *
12
+ * Probes the database once on first call to determine if transactions work.
13
+ * The result is cached for the lifetime of the process/worker.
14
+ */
15
+ let transactionsSupported = null;
16
+ const TRANSACTIONS_NOT_SUPPORTED_RE = /transactions are not supported/i;
17
+ async function withTransaction(db, fn) {
18
+ if (transactionsSupported === true) return db.transaction().execute(fn);
19
+ if (transactionsSupported === false) return fn(db);
20
+ try {
21
+ const result = await db.transaction().execute(fn);
22
+ transactionsSupported = true;
23
+ return result;
24
+ } catch (error) {
25
+ if (error instanceof Error && TRANSACTIONS_NOT_SUPPORTED_RE.test(error.message)) {
26
+ transactionsSupported = false;
27
+ return fn(db);
28
+ }
29
+ throw error;
30
+ }
31
+ }
32
+
33
+ //#endregion
34
+ //#region src/search/fts-manager.ts
35
+ /**
36
+ * FTS5 Manager
37
+ *
38
+ * Handles creation, deletion, and management of FTS5 virtual tables
39
+ * for full-text search on content collections.
40
+ */
41
+ var FTSManager = class {
42
+ constructor(db) {
43
+ this.db = db;
44
+ }
45
+ /**
46
+ * Validate a collection slug and its searchable field names.
47
+ * Must be called before any raw SQL interpolation.
48
+ */
49
+ validateInputs(collectionSlug, searchableFields) {
50
+ validateIdentifier(collectionSlug, "collection slug");
51
+ if (searchableFields) for (const field of searchableFields) validateIdentifier(field, "searchable field name");
52
+ }
53
+ /**
54
+ * Get the FTS table name for a collection
55
+ * Uses _emdash_ prefix to clearly mark as internal/system table
56
+ */
57
+ getFtsTableName(collectionSlug) {
58
+ return `_emdash_fts_${collectionSlug}`;
59
+ }
60
+ /**
61
+ * Get the content table name for a collection
62
+ */
63
+ getContentTableName(collectionSlug) {
64
+ return `ec_${collectionSlug}`;
65
+ }
66
+ /**
67
+ * Check if an FTS table exists for a collection
68
+ */
69
+ async ftsTableExists(collectionSlug) {
70
+ const ftsTable = this.getFtsTableName(collectionSlug);
71
+ return tableExists(this.db, ftsTable);
72
+ }
73
+ /**
74
+ * Create an FTS5 virtual table for a collection.
75
+ * FTS5 is SQLite-only; on other dialects this is a no-op.
76
+ *
77
+ * @param collectionSlug - The collection slug
78
+ * @param searchableFields - Array of field names to index
79
+ * @param weights - Optional field weights for ranking
80
+ */
81
+ async createFtsTable(collectionSlug, searchableFields, _weights) {
82
+ if (!isSqlite(this.db)) return;
83
+ this.validateInputs(collectionSlug, searchableFields);
84
+ const ftsTable = this.getFtsTableName(collectionSlug);
85
+ const contentTable = this.getContentTableName(collectionSlug);
86
+ const columns = [
87
+ "id UNINDEXED",
88
+ "locale UNINDEXED",
89
+ ...searchableFields
90
+ ].join(", ");
91
+ await sql.raw(`
92
+ CREATE VIRTUAL TABLE IF NOT EXISTS "${ftsTable}" USING fts5(
93
+ ${columns},
94
+ content='${contentTable}',
95
+ content_rowid='rowid',
96
+ tokenize='porter unicode61'
97
+ )
98
+ `).execute(this.db);
99
+ await this.createTriggers(collectionSlug, searchableFields);
100
+ }
101
+ /**
102
+ * Create triggers to keep FTS table in sync with content table
103
+ */
104
+ async createTriggers(collectionSlug, searchableFields) {
105
+ const ftsTable = this.getFtsTableName(collectionSlug);
106
+ const contentTable = this.getContentTableName(collectionSlug);
107
+ const fieldList = searchableFields.join(", ");
108
+ const newFieldList = searchableFields.map((f) => `NEW.${f}`).join(", ");
109
+ await sql.raw(`
110
+ CREATE TRIGGER IF NOT EXISTS "${ftsTable}_insert"
111
+ AFTER INSERT ON "${contentTable}"
112
+ BEGIN
113
+ INSERT INTO "${ftsTable}"(rowid, id, locale, ${fieldList})
114
+ VALUES (NEW.rowid, NEW.id, NEW.locale, ${newFieldList});
115
+ END
116
+ `).execute(this.db);
117
+ await sql.raw(`
118
+ CREATE TRIGGER IF NOT EXISTS "${ftsTable}_update"
119
+ AFTER UPDATE ON "${contentTable}"
120
+ BEGIN
121
+ DELETE FROM "${ftsTable}" WHERE rowid = OLD.rowid;
122
+ INSERT INTO "${ftsTable}"(rowid, id, locale, ${fieldList})
123
+ VALUES (NEW.rowid, NEW.id, NEW.locale, ${newFieldList});
124
+ END
125
+ `).execute(this.db);
126
+ await sql.raw(`
127
+ CREATE TRIGGER IF NOT EXISTS "${ftsTable}_delete"
128
+ AFTER DELETE ON "${contentTable}"
129
+ BEGIN
130
+ DELETE FROM "${ftsTable}" WHERE rowid = OLD.rowid;
131
+ END
132
+ `).execute(this.db);
133
+ }
134
+ /**
135
+ * Drop triggers for a collection
136
+ */
137
+ async dropTriggers(collectionSlug) {
138
+ const ftsTable = this.getFtsTableName(collectionSlug);
139
+ await sql.raw(`DROP TRIGGER IF EXISTS "${ftsTable}_insert"`).execute(this.db);
140
+ await sql.raw(`DROP TRIGGER IF EXISTS "${ftsTable}_update"`).execute(this.db);
141
+ await sql.raw(`DROP TRIGGER IF EXISTS "${ftsTable}_delete"`).execute(this.db);
142
+ }
143
+ /**
144
+ * Drop the FTS table and triggers for a collection
145
+ */
146
+ async dropFtsTable(collectionSlug) {
147
+ if (!isSqlite(this.db)) return;
148
+ this.validateInputs(collectionSlug);
149
+ const ftsTable = this.getFtsTableName(collectionSlug);
150
+ await this.dropTriggers(collectionSlug);
151
+ await sql.raw(`DROP TABLE IF EXISTS "${ftsTable}"`).execute(this.db);
152
+ }
153
+ /**
154
+ * Rebuild the FTS index for a collection
155
+ *
156
+ * This is useful after bulk imports or if the index gets out of sync.
157
+ */
158
+ async rebuildIndex(collectionSlug, searchableFields, weights) {
159
+ if (!isSqlite(this.db)) return;
160
+ await this.dropFtsTable(collectionSlug);
161
+ await this.createFtsTable(collectionSlug, searchableFields, weights);
162
+ await this.populateFromContent(collectionSlug, searchableFields);
163
+ }
164
+ /**
165
+ * Populate the FTS table from existing content
166
+ */
167
+ async populateFromContent(collectionSlug, searchableFields) {
168
+ if (!isSqlite(this.db)) return;
169
+ this.validateInputs(collectionSlug, searchableFields);
170
+ const ftsTable = this.getFtsTableName(collectionSlug);
171
+ const contentTable = this.getContentTableName(collectionSlug);
172
+ const fieldList = searchableFields.join(", ");
173
+ await sql.raw(`
174
+ INSERT INTO "${ftsTable}"(rowid, id, locale, ${fieldList})
175
+ SELECT rowid, id, locale, ${fieldList} FROM "${contentTable}"
176
+ WHERE deleted_at IS NULL
177
+ `).execute(this.db);
178
+ }
179
+ /**
180
+ * Get the search configuration for a collection
181
+ */
182
+ async getSearchConfig(collectionSlug) {
183
+ const result = await this.db.selectFrom("_emdash_collections").select("search_config").where("slug", "=", collectionSlug).executeTakeFirst();
184
+ if (!result?.search_config) return null;
185
+ try {
186
+ const parsed = JSON.parse(result.search_config);
187
+ if (typeof parsed !== "object" || parsed === null || !("enabled" in parsed) || typeof parsed.enabled !== "boolean") return null;
188
+ const config = { enabled: parsed.enabled };
189
+ if ("weights" in parsed && typeof parsed.weights === "object" && parsed.weights !== null) {
190
+ const weights = {};
191
+ for (const [k, v] of Object.entries(parsed.weights)) if (typeof v === "number") weights[k] = v;
192
+ config.weights = weights;
193
+ }
194
+ return config;
195
+ } catch {
196
+ return null;
197
+ }
198
+ }
199
+ /**
200
+ * Update the search configuration for a collection
201
+ */
202
+ async setSearchConfig(collectionSlug, config) {
203
+ await this.db.updateTable("_emdash_collections").set({ search_config: JSON.stringify(config) }).where("slug", "=", collectionSlug).execute();
204
+ }
205
+ /**
206
+ * Get searchable fields for a collection
207
+ */
208
+ async getSearchableFields(collectionSlug) {
209
+ const collection = await this.db.selectFrom("_emdash_collections").select("id").where("slug", "=", collectionSlug).executeTakeFirst();
210
+ if (!collection) return [];
211
+ return (await this.db.selectFrom("_emdash_fields").select("slug").where("collection_id", "=", collection.id).where("searchable", "=", 1).execute()).map((f) => f.slug);
212
+ }
213
+ /**
214
+ * Enable search for a collection
215
+ *
216
+ * Creates the FTS table and triggers, and populates from existing content.
217
+ */
218
+ async enableSearch(collectionSlug, options) {
219
+ if (!isSqlite(this.db)) throw new Error("Full-text search is only available with SQLite databases");
220
+ const searchableFields = await this.getSearchableFields(collectionSlug);
221
+ if (searchableFields.length === 0) throw new Error(`No searchable fields defined for collection "${collectionSlug}". Mark at least one field as searchable before enabling search.`);
222
+ await this.createFtsTable(collectionSlug, searchableFields, options?.weights);
223
+ await this.populateFromContent(collectionSlug, searchableFields);
224
+ await this.setSearchConfig(collectionSlug, {
225
+ enabled: true,
226
+ weights: options?.weights
227
+ });
228
+ }
229
+ /**
230
+ * Disable search for a collection
231
+ *
232
+ * Drops the FTS table and triggers.
233
+ */
234
+ async disableSearch(collectionSlug) {
235
+ if (!isSqlite(this.db)) return;
236
+ await this.dropFtsTable(collectionSlug);
237
+ await this.setSearchConfig(collectionSlug, { enabled: false });
238
+ }
239
+ /**
240
+ * Get index statistics for a collection
241
+ */
242
+ async getIndexStats(collectionSlug) {
243
+ if (!isSqlite(this.db)) return null;
244
+ this.validateInputs(collectionSlug);
245
+ const ftsTable = this.getFtsTableName(collectionSlug);
246
+ if (!await this.ftsTableExists(collectionSlug)) return null;
247
+ return { indexed: (await sql`
248
+ SELECT COUNT(*) as count FROM "${sql.raw(ftsTable)}"
249
+ `.execute(this.db)).rows[0]?.count ?? 0 };
250
+ }
251
+ /**
252
+ * Verify FTS index integrity and rebuild if corrupted.
253
+ *
254
+ * Checks for two corruption indicators:
255
+ * 1. Row count mismatch between content table and FTS table
256
+ * 2. FTS5 integrity-check failure (catches shadow table inconsistencies)
257
+ *
258
+ * Returns true if the index was rebuilt, false if it was healthy.
259
+ */
260
+ async verifyAndRepairIndex(collectionSlug) {
261
+ if (!isSqlite(this.db)) return false;
262
+ this.validateInputs(collectionSlug);
263
+ const ftsTable = this.getFtsTableName(collectionSlug);
264
+ const contentTable = this.getContentTableName(collectionSlug);
265
+ if (!await this.ftsTableExists(collectionSlug)) return false;
266
+ const contentCount = await sql`
267
+ SELECT COUNT(*) as count FROM ${sql.ref(contentTable)}
268
+ WHERE deleted_at IS NULL
269
+ `.execute(this.db);
270
+ const ftsCount = await sql`
271
+ SELECT COUNT(*) as count FROM "${sql.raw(ftsTable)}"
272
+ `.execute(this.db);
273
+ const contentRows = contentCount.rows[0]?.count ?? 0;
274
+ const ftsRows = ftsCount.rows[0]?.count ?? 0;
275
+ if (contentRows !== ftsRows) {
276
+ console.warn(`FTS index for "${collectionSlug}" has ${ftsRows} rows but content table has ${contentRows}. Rebuilding.`);
277
+ const fields = await this.getSearchableFields(collectionSlug);
278
+ const config = await this.getSearchConfig(collectionSlug);
279
+ if (fields.length > 0) await this.rebuildIndex(collectionSlug, fields, config?.weights);
280
+ return true;
281
+ }
282
+ try {
283
+ await sql.raw(`INSERT INTO "${ftsTable}"("${ftsTable}") VALUES('integrity-check')`).execute(this.db);
284
+ } catch {
285
+ console.warn(`FTS integrity check failed for "${collectionSlug}". Rebuilding index.`);
286
+ const fields = await this.getSearchableFields(collectionSlug);
287
+ const config = await this.getSearchConfig(collectionSlug);
288
+ if (fields.length > 0) await this.rebuildIndex(collectionSlug, fields, config?.weights);
289
+ return true;
290
+ }
291
+ return false;
292
+ }
293
+ /**
294
+ * Verify and repair FTS indexes for all search-enabled collections.
295
+ *
296
+ * Intended to run at startup to auto-heal any corruption from
297
+ * previous process crashes.
298
+ */
299
+ async verifyAndRepairAll() {
300
+ if (!isSqlite(this.db)) return 0;
301
+ const collections = await this.db.selectFrom("_emdash_collections").select("slug").where("search_config", "is not", null).execute();
302
+ let repaired = 0;
303
+ for (const { slug } of collections) {
304
+ if (!(await this.getSearchConfig(slug))?.enabled) continue;
305
+ try {
306
+ if (await this.verifyAndRepairIndex(slug)) repaired++;
307
+ } catch (error) {
308
+ console.error(`Failed to verify/repair FTS index for "${slug}":`, error);
309
+ }
310
+ }
311
+ return repaired;
312
+ }
313
+ };
314
+
315
+ //#endregion
316
+ //#region src/schema/registry.ts
317
+ var registry_exports = /* @__PURE__ */ __exportAll({
318
+ SchemaError: () => SchemaError,
319
+ SchemaRegistry: () => SchemaRegistry
320
+ });
321
+ const SLUG_VALIDATION_PATTERN = /^[a-z][a-z0-9_]*$/;
322
+ const EC_PREFIX_PATTERN = /^ec_/;
323
+ const SINGLE_QUOTE_PATTERN = /'/g;
324
+ const UNDERSCORE_PATTERN = /_/g;
325
+ const WORD_BOUNDARY_PATTERN = /\b\w/g;
326
+ /** Valid column types for runtime validation */
327
+ const COLUMN_TYPES = new Set([
328
+ "TEXT",
329
+ "REAL",
330
+ "INTEGER",
331
+ "JSON"
332
+ ]);
333
+ /** Valid collection source prefixes/values */
334
+ const VALID_SOURCES = new Set([
335
+ "manual",
336
+ "discovered",
337
+ "seed"
338
+ ]);
339
+ function isCollectionSource(value) {
340
+ return VALID_SOURCES.has(value) || value.startsWith("template:") || value.startsWith("import:");
341
+ }
342
+ function isFieldType(value) {
343
+ return value in FIELD_TYPE_TO_COLUMN;
344
+ }
345
+ function isColumnType(value) {
346
+ return COLUMN_TYPES.has(value);
347
+ }
348
+ /**
349
+ * Error thrown when a schema operation fails
350
+ */
351
+ var SchemaError = class extends Error {
352
+ constructor(message, code, details) {
353
+ super(message);
354
+ this.code = code;
355
+ this.details = details;
356
+ this.name = "SchemaError";
357
+ }
358
+ };
359
+ /**
360
+ * Schema Registry
361
+ *
362
+ * Manages collection and field definitions stored in D1.
363
+ * Handles runtime DDL operations (CREATE TABLE, ALTER TABLE).
364
+ */
365
+ var SchemaRegistry = class {
366
+ constructor(db) {
367
+ this.db = db;
368
+ }
369
+ /**
370
+ * List all collections
371
+ */
372
+ async listCollections() {
373
+ return (await this.db.selectFrom("_emdash_collections").selectAll().orderBy("slug", "asc").execute()).map(this.mapCollectionRow);
374
+ }
375
+ /**
376
+ * Get a collection by slug
377
+ */
378
+ async getCollection(slug) {
379
+ const row = await this.db.selectFrom("_emdash_collections").where("slug", "=", slug).selectAll().executeTakeFirst();
380
+ return row ? this.mapCollectionRow(row) : null;
381
+ }
382
+ /**
383
+ * Get a collection with all its fields
384
+ */
385
+ async getCollectionWithFields(slug) {
386
+ const collection = await this.getCollection(slug);
387
+ if (!collection) return null;
388
+ const fields = await this.listFields(collection.id);
389
+ return {
390
+ ...collection,
391
+ fields
392
+ };
393
+ }
394
+ /**
395
+ * Create a new collection
396
+ */
397
+ async createCollection(input) {
398
+ this.validateSlug(input.slug, "collection");
399
+ if (RESERVED_COLLECTION_SLUGS.includes(input.slug)) throw new SchemaError(`Collection slug "${input.slug}" is reserved`, "RESERVED_SLUG");
400
+ if (await this.getCollection(input.slug)) throw new SchemaError(`Collection "${input.slug}" already exists`, "COLLECTION_EXISTS");
401
+ const id = ulid();
402
+ const hasSeo = input.hasSeo ?? input.supports?.includes("seo") ?? false;
403
+ await withTransaction(this.db, async (trx) => {
404
+ await trx.insertInto("_emdash_collections").values({
405
+ id,
406
+ slug: input.slug,
407
+ label: input.label,
408
+ label_singular: input.labelSingular ?? null,
409
+ description: input.description ?? null,
410
+ icon: input.icon ?? null,
411
+ supports: input.supports ? JSON.stringify(input.supports) : null,
412
+ source: input.source ?? "manual",
413
+ has_seo: hasSeo ? 1 : 0,
414
+ comments_enabled: input.commentsEnabled ? 1 : 0,
415
+ url_pattern: input.urlPattern ?? null
416
+ }).execute();
417
+ await this.createContentTable(input.slug, trx);
418
+ });
419
+ const collection = await this.getCollection(input.slug);
420
+ if (!collection) throw new SchemaError("Failed to create collection", "CREATE_FAILED");
421
+ return collection;
422
+ }
423
+ /**
424
+ * Update a collection
425
+ */
426
+ async updateCollection(slug, input) {
427
+ const existing = await this.getCollection(slug);
428
+ if (!existing) throw new SchemaError(`Collection "${slug}" not found`, "COLLECTION_NOT_FOUND");
429
+ const now = (/* @__PURE__ */ new Date()).toISOString();
430
+ const supportsArray = input.supports ?? existing.supports;
431
+ const hasSeo = input.hasSeo !== void 0 ? input.hasSeo : input.supports !== void 0 ? supportsArray.includes("seo") : existing.hasSeo;
432
+ await this.db.updateTable("_emdash_collections").set({
433
+ label: input.label ?? existing.label,
434
+ label_singular: input.labelSingular ?? existing.labelSingular ?? null,
435
+ description: input.description ?? existing.description ?? null,
436
+ icon: input.icon ?? existing.icon ?? null,
437
+ supports: input.supports ? JSON.stringify(input.supports) : JSON.stringify(existing.supports),
438
+ url_pattern: input.urlPattern !== void 0 ? input.urlPattern ?? null : existing.urlPattern ?? null,
439
+ has_seo: hasSeo ? 1 : 0,
440
+ comments_enabled: input.commentsEnabled !== void 0 ? input.commentsEnabled ? 1 : 0 : existing.commentsEnabled ? 1 : 0,
441
+ comments_moderation: input.commentsModeration ?? existing.commentsModeration,
442
+ comments_closed_after_days: input.commentsClosedAfterDays !== void 0 ? input.commentsClosedAfterDays : existing.commentsClosedAfterDays,
443
+ comments_auto_approve_users: input.commentsAutoApproveUsers !== void 0 ? input.commentsAutoApproveUsers ? 1 : 0 : existing.commentsAutoApproveUsers ? 1 : 0,
444
+ updated_at: now
445
+ }).where("slug", "=", slug).execute();
446
+ const updated = await this.getCollection(slug);
447
+ if (!updated) throw new SchemaError("Failed to update collection", "UPDATE_FAILED");
448
+ return updated;
449
+ }
450
+ /**
451
+ * Delete a collection
452
+ */
453
+ async deleteCollection(slug, options) {
454
+ const existing = await this.getCollection(slug);
455
+ if (!existing) throw new SchemaError(`Collection "${slug}" not found`, "COLLECTION_NOT_FOUND");
456
+ if (!options?.force) {
457
+ if (await this.collectionHasContent(slug)) throw new SchemaError(`Collection "${slug}" has content. Use force: true to delete.`, "COLLECTION_HAS_CONTENT");
458
+ }
459
+ await this.dropContentTable(slug);
460
+ await this.db.deleteFrom("_emdash_collections").where("id", "=", existing.id).execute();
461
+ }
462
+ /**
463
+ * List fields for a collection
464
+ */
465
+ async listFields(collectionId) {
466
+ return (await this.db.selectFrom("_emdash_fields").where("collection_id", "=", collectionId).selectAll().orderBy("sort_order", "asc").orderBy("created_at", "asc").execute()).map(this.mapFieldRow);
467
+ }
468
+ /**
469
+ * Get a field by slug within a collection
470
+ */
471
+ async getField(collectionSlug, fieldSlug) {
472
+ const collection = await this.getCollection(collectionSlug);
473
+ if (!collection) return null;
474
+ const row = await this.db.selectFrom("_emdash_fields").where("collection_id", "=", collection.id).where("slug", "=", fieldSlug).selectAll().executeTakeFirst();
475
+ return row ? this.mapFieldRow(row) : null;
476
+ }
477
+ /**
478
+ * Create a new field
479
+ */
480
+ async createField(collectionSlug, input) {
481
+ const collection = await this.getCollection(collectionSlug);
482
+ if (!collection) throw new SchemaError(`Collection "${collectionSlug}" not found`, "COLLECTION_NOT_FOUND");
483
+ this.validateSlug(input.slug, "field");
484
+ if (RESERVED_FIELD_SLUGS.includes(input.slug)) throw new SchemaError(`Field slug "${input.slug}" is reserved`, "RESERVED_SLUG");
485
+ if (await this.getField(collectionSlug, input.slug)) throw new SchemaError(`Field "${input.slug}" already exists in collection "${collectionSlug}"`, "FIELD_EXISTS");
486
+ const id = ulid();
487
+ const columnType = FIELD_TYPE_TO_COLUMN[input.type];
488
+ const maxSort = await this.db.selectFrom("_emdash_fields").where("collection_id", "=", collection.id).select((eb) => eb.fn.max("sort_order").as("max")).executeTakeFirst();
489
+ const sortOrder = input.sortOrder ?? (maxSort?.max ?? -1) + 1;
490
+ await this.db.insertInto("_emdash_fields").values({
491
+ id,
492
+ collection_id: collection.id,
493
+ slug: input.slug,
494
+ label: input.label,
495
+ type: input.type,
496
+ column_type: columnType,
497
+ required: input.required ? 1 : 0,
498
+ unique: input.unique ? 1 : 0,
499
+ default_value: input.defaultValue !== void 0 ? JSON.stringify(input.defaultValue) : null,
500
+ validation: input.validation ? JSON.stringify(input.validation) : null,
501
+ widget: input.widget ?? null,
502
+ options: input.options ? JSON.stringify(input.options) : null,
503
+ sort_order: sortOrder,
504
+ searchable: input.searchable ? 1 : 0,
505
+ translatable: input.translatable === false ? 0 : 1
506
+ }).execute();
507
+ await this.addColumn(collectionSlug, input.slug, input.type, {
508
+ required: input.required,
509
+ defaultValue: input.defaultValue
510
+ });
511
+ const field = await this.getField(collectionSlug, input.slug);
512
+ if (!field) throw new SchemaError("Failed to create field", "CREATE_FAILED");
513
+ return field;
514
+ }
515
+ /**
516
+ * Update a field
517
+ */
518
+ async updateField(collectionSlug, fieldSlug, input) {
519
+ const field = await this.getField(collectionSlug, fieldSlug);
520
+ if (!field) throw new SchemaError(`Field "${fieldSlug}" not found in collection "${collectionSlug}"`, "FIELD_NOT_FOUND");
521
+ await this.db.updateTable("_emdash_fields").set({
522
+ label: input.label ?? field.label,
523
+ required: input.required !== void 0 ? input.required ? 1 : 0 : field.required ? 1 : 0,
524
+ unique: input.unique !== void 0 ? input.unique ? 1 : 0 : field.unique ? 1 : 0,
525
+ searchable: input.searchable !== void 0 ? input.searchable ? 1 : 0 : field.searchable ? 1 : 0,
526
+ translatable: input.translatable !== void 0 ? input.translatable ? 1 : 0 : field.translatable ? 1 : 0,
527
+ default_value: input.defaultValue !== void 0 ? JSON.stringify(input.defaultValue) : field.defaultValue !== void 0 ? JSON.stringify(field.defaultValue) : null,
528
+ validation: input.validation ? JSON.stringify(input.validation) : field.validation ? JSON.stringify(field.validation) : null,
529
+ widget: input.widget ?? field.widget ?? null,
530
+ options: input.options ? JSON.stringify(input.options) : field.options ? JSON.stringify(field.options) : null,
531
+ sort_order: input.sortOrder ?? field.sortOrder
532
+ }).where("id", "=", field.id).execute();
533
+ const updated = await this.getField(collectionSlug, fieldSlug);
534
+ if (!updated) throw new SchemaError("Failed to update field", "UPDATE_FAILED");
535
+ if (input.searchable !== void 0 && input.searchable !== field.searchable) await this.rebuildSearchIndex(collectionSlug);
536
+ return updated;
537
+ }
538
+ /**
539
+ * Rebuild the search index for a collection
540
+ *
541
+ * Called when searchable fields change. If search is enabled for the collection,
542
+ * this will rebuild the FTS table with the updated field list.
543
+ */
544
+ async rebuildSearchIndex(collectionSlug) {
545
+ const ftsManager = new FTSManager(this.db);
546
+ const config = await ftsManager.getSearchConfig(collectionSlug);
547
+ if (!config?.enabled) return;
548
+ const searchableFields = await ftsManager.getSearchableFields(collectionSlug);
549
+ if (searchableFields.length === 0) await ftsManager.disableSearch(collectionSlug);
550
+ else await ftsManager.rebuildIndex(collectionSlug, searchableFields, config.weights);
551
+ }
552
+ /**
553
+ * Delete a field
554
+ */
555
+ async deleteField(collectionSlug, fieldSlug) {
556
+ const field = await this.getField(collectionSlug, fieldSlug);
557
+ if (!field) throw new SchemaError(`Field "${fieldSlug}" not found in collection "${collectionSlug}"`, "FIELD_NOT_FOUND");
558
+ await this.dropColumn(collectionSlug, fieldSlug);
559
+ await this.db.deleteFrom("_emdash_fields").where("id", "=", field.id).execute();
560
+ }
561
+ /**
562
+ * Reorder fields
563
+ */
564
+ async reorderFields(collectionSlug, fieldSlugs) {
565
+ const collection = await this.getCollection(collectionSlug);
566
+ if (!collection) throw new SchemaError(`Collection "${collectionSlug}" not found`, "COLLECTION_NOT_FOUND");
567
+ for (let i = 0; i < fieldSlugs.length; i++) await this.db.updateTable("_emdash_fields").set({ sort_order: i }).where("collection_id", "=", collection.id).where("slug", "=", fieldSlugs[i]).execute();
568
+ }
569
+ /**
570
+ * Create a content table for a collection
571
+ */
572
+ async createContentTable(slug, db) {
573
+ const conn = db ?? this.db;
574
+ const tableName = this.getTableName(slug);
575
+ await conn.schema.createTable(tableName).addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text").addColumn("status", "text", (col) => col.defaultTo("draft")).addColumn("author_id", "text").addColumn("primary_byline_id", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(conn))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(conn))).addColumn("published_at", "text").addColumn("scheduled_at", "text").addColumn("deleted_at", "text").addColumn("version", "integer", (col) => col.defaultTo(1)).addColumn("live_revision_id", "text", (col) => col.references("revisions.id")).addColumn("draft_revision_id", "text", (col) => col.references("revisions.id")).addColumn("locale", "text", (col) => col.notNull().defaultTo("en")).addColumn("translation_group", "text").addUniqueConstraint(`${tableName}_slug_locale_unique`, ["slug", "locale"]).execute();
576
+ await sql`
577
+ CREATE INDEX ${sql.ref(`idx_${tableName}_status`)}
578
+ ON ${sql.ref(tableName)} (status)
579
+ `.execute(conn);
580
+ await sql`
581
+ CREATE INDEX ${sql.ref(`idx_${tableName}_slug`)}
582
+ ON ${sql.ref(tableName)} (slug)
583
+ `.execute(conn);
584
+ await sql`
585
+ CREATE INDEX ${sql.ref(`idx_${tableName}_created`)}
586
+ ON ${sql.ref(tableName)} (created_at)
587
+ `.execute(conn);
588
+ await sql`
589
+ CREATE INDEX ${sql.ref(`idx_${tableName}_deleted`)}
590
+ ON ${sql.ref(tableName)} (deleted_at)
591
+ `.execute(conn);
592
+ await sql`
593
+ CREATE INDEX ${sql.ref(`idx_${tableName}_scheduled`)}
594
+ ON ${sql.ref(tableName)} (scheduled_at)
595
+ WHERE scheduled_at IS NOT NULL
596
+ `.execute(conn);
597
+ await sql`
598
+ CREATE INDEX ${sql.ref(`idx_${tableName}_live_revision`)}
599
+ ON ${sql.ref(tableName)} (live_revision_id)
600
+ `.execute(conn);
601
+ await sql`
602
+ CREATE INDEX ${sql.ref(`idx_${tableName}_draft_revision`)}
603
+ ON ${sql.ref(tableName)} (draft_revision_id)
604
+ `.execute(conn);
605
+ await sql`
606
+ CREATE INDEX ${sql.ref(`idx_${tableName}_author`)}
607
+ ON ${sql.ref(tableName)} (author_id)
608
+ `.execute(conn);
609
+ await sql`
610
+ CREATE INDEX ${sql.ref(`idx_${tableName}_primary_byline`)}
611
+ ON ${sql.ref(tableName)} (primary_byline_id)
612
+ `.execute(conn);
613
+ await sql`
614
+ CREATE INDEX ${sql.ref(`idx_${tableName}_updated`)}
615
+ ON ${sql.ref(tableName)} (updated_at)
616
+ `.execute(conn);
617
+ await sql`
618
+ CREATE INDEX ${sql.ref(`idx_${tableName}_locale`)}
619
+ ON ${sql.ref(tableName)} (locale)
620
+ `.execute(conn);
621
+ await sql`
622
+ CREATE INDEX ${sql.ref(`idx_${tableName}_translation_group`)}
623
+ ON ${sql.ref(tableName)} (translation_group)
624
+ `.execute(conn);
625
+ }
626
+ /**
627
+ * Drop a content table
628
+ */
629
+ async dropContentTable(slug) {
630
+ const tableName = this.getTableName(slug);
631
+ await sql`DROP TABLE IF EXISTS ${sql.ref(tableName)}`.execute(this.db);
632
+ }
633
+ /**
634
+ * Add a column to a content table
635
+ */
636
+ async addColumn(collectionSlug, fieldSlug, fieldType, options) {
637
+ const tableName = this.getTableName(collectionSlug);
638
+ const columnType = FIELD_TYPE_TO_COLUMN[fieldType];
639
+ const columnName = this.getColumnName(fieldSlug);
640
+ if (options?.required && options?.defaultValue !== void 0) {
641
+ const defaultVal = this.formatDefaultValue(options.defaultValue, fieldType);
642
+ await sql`
643
+ ALTER TABLE ${sql.ref(tableName)}
644
+ ADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}
645
+ `.execute(this.db);
646
+ } else if (options?.required) {
647
+ const defaultVal = this.getEmptyDefault(fieldType);
648
+ await sql`
649
+ ALTER TABLE ${sql.ref(tableName)}
650
+ ADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}
651
+ `.execute(this.db);
652
+ } else await sql`
653
+ ALTER TABLE ${sql.ref(tableName)}
654
+ ADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)}
655
+ `.execute(this.db);
656
+ }
657
+ /**
658
+ * Drop a column from a content table
659
+ */
660
+ async dropColumn(collectionSlug, fieldSlug) {
661
+ const tableName = this.getTableName(collectionSlug);
662
+ const columnName = this.getColumnName(fieldSlug);
663
+ await sql`
664
+ ALTER TABLE ${sql.ref(tableName)}
665
+ DROP COLUMN ${sql.ref(columnName)}
666
+ `.execute(this.db);
667
+ }
668
+ /**
669
+ * Check if a collection has any content
670
+ */
671
+ async collectionHasContent(slug) {
672
+ const tableName = this.getTableName(slug);
673
+ try {
674
+ return ((await sql`
675
+ SELECT COUNT(*) as count FROM ${sql.ref(tableName)}
676
+ WHERE deleted_at IS NULL
677
+ `.execute(this.db)).rows[0]?.count ?? 0) > 0;
678
+ } catch {
679
+ return false;
680
+ }
681
+ }
682
+ /**
683
+ * Get table name for a collection
684
+ */
685
+ getTableName(slug) {
686
+ return `ec_${slug}`;
687
+ }
688
+ /**
689
+ * Get column name for a field
690
+ */
691
+ getColumnName(slug) {
692
+ return slug;
693
+ }
694
+ /**
695
+ * Validate a slug
696
+ */
697
+ validateSlug(slug, type) {
698
+ if (!slug || typeof slug !== "string") throw new SchemaError(`${type} slug is required`, "INVALID_SLUG");
699
+ if (!SLUG_VALIDATION_PATTERN.test(slug)) throw new SchemaError(`${type} slug must start with a letter and contain only lowercase letters, numbers, and underscores`, "INVALID_SLUG");
700
+ if (slug.length > 63) throw new SchemaError(`${type} slug must be 63 characters or less`, "INVALID_SLUG");
701
+ }
702
+ /**
703
+ * Format a default value for SQL.
704
+ *
705
+ * SQLite `ALTER TABLE ADD COLUMN ... DEFAULT` requires a literal constant
706
+ * expression — parameterized values cannot be used here. We manually escape
707
+ * single quotes and coerce types to ensure the output is safe.
708
+ *
709
+ * INTEGER/REAL values are coerced through `Number()` which can only produce
710
+ * digits, `.`, `-`, `e`, `Infinity`, or `NaN` — all safe in SQL.
711
+ * TEXT/JSON values have single quotes escaped via SQL standard doubling (`''`).
712
+ */
713
+ formatDefaultValue(value, fieldType) {
714
+ if (value === null || value === void 0) return "NULL";
715
+ const columnType = FIELD_TYPE_TO_COLUMN[fieldType];
716
+ if (columnType === "JSON") return `'${JSON.stringify(value).replace(SINGLE_QUOTE_PATTERN, "''")}'`;
717
+ if (columnType === "INTEGER") {
718
+ if (typeof value === "boolean") return value ? "1" : "0";
719
+ const num = Number(value);
720
+ if (!Number.isFinite(num)) return "0";
721
+ return String(Math.trunc(num));
722
+ }
723
+ if (columnType === "REAL") {
724
+ const num = Number(value);
725
+ if (!Number.isFinite(num)) return "0";
726
+ return String(num);
727
+ }
728
+ let text;
729
+ if (typeof value === "string") text = value;
730
+ else if (typeof value === "number" || typeof value === "boolean") text = String(value);
731
+ else if (typeof value === "object" && value !== null) text = JSON.stringify(value);
732
+ else text = "";
733
+ return `'${text.replace(SINGLE_QUOTE_PATTERN, "''")}'`;
734
+ }
735
+ /**
736
+ * Get empty default for a field type
737
+ */
738
+ getEmptyDefault(fieldType) {
739
+ switch (FIELD_TYPE_TO_COLUMN[fieldType]) {
740
+ case "INTEGER": return "0";
741
+ case "REAL": return "0.0";
742
+ case "JSON": return "'null'";
743
+ default: return "''";
744
+ }
745
+ }
746
+ /**
747
+ * Map a collection row to a Collection object
748
+ */
749
+ mapCollectionRow = (row) => {
750
+ const moderation = row.comments_moderation;
751
+ return {
752
+ id: row.id,
753
+ slug: row.slug,
754
+ label: row.label,
755
+ labelSingular: row.label_singular ?? void 0,
756
+ description: row.description ?? void 0,
757
+ icon: row.icon ?? void 0,
758
+ supports: row.supports ? JSON.parse(row.supports) : [],
759
+ source: row.source && isCollectionSource(row.source) ? row.source : void 0,
760
+ hasSeo: row.has_seo === 1,
761
+ urlPattern: row.url_pattern ?? void 0,
762
+ commentsEnabled: row.comments_enabled === 1,
763
+ commentsModeration: moderation === "all" || moderation === "first_time" || moderation === "none" ? moderation : "first_time",
764
+ commentsClosedAfterDays: row.comments_closed_after_days ?? 90,
765
+ commentsAutoApproveUsers: row.comments_auto_approve_users === 1,
766
+ createdAt: row.created_at,
767
+ updatedAt: row.updated_at
768
+ };
769
+ };
770
+ /**
771
+ * Map a field row to a Field object
772
+ */
773
+ mapFieldRow = (row) => {
774
+ return {
775
+ id: row.id,
776
+ collectionId: row.collection_id,
777
+ slug: row.slug,
778
+ label: row.label,
779
+ type: isFieldType(row.type) ? row.type : "string",
780
+ columnType: isColumnType(row.column_type) ? row.column_type : "TEXT",
781
+ required: row.required === 1,
782
+ unique: row.unique === 1,
783
+ defaultValue: row.default_value ? JSON.parse(row.default_value) : void 0,
784
+ validation: row.validation ? JSON.parse(row.validation) : void 0,
785
+ widget: row.widget ?? void 0,
786
+ options: row.options ? JSON.parse(row.options) : void 0,
787
+ sortOrder: row.sort_order,
788
+ searchable: row.searchable === 1,
789
+ translatable: row.translatable !== 0,
790
+ createdAt: row.created_at
791
+ };
792
+ };
793
+ /**
794
+ * Discover orphaned content tables
795
+ *
796
+ * Finds ec_* tables that exist in the database but don't have a
797
+ * corresponding entry in _emdash_collections.
798
+ */
799
+ async discoverOrphanedTables() {
800
+ const allTables = await listTablesLike(this.db, "ec_%");
801
+ const registered = await this.listCollections();
802
+ const registeredSlugs = new Set(registered.map((c) => c.slug));
803
+ const orphans = [];
804
+ for (const tableName of allTables) {
805
+ const slug = tableName.replace(EC_PREFIX_PATTERN, "");
806
+ if (!registeredSlugs.has(slug)) try {
807
+ const countResult = await sql`
808
+ SELECT COUNT(*) as count FROM ${sql.ref(tableName)}
809
+ WHERE deleted_at IS NULL
810
+ `.execute(this.db);
811
+ orphans.push({
812
+ slug,
813
+ tableName,
814
+ rowCount: countResult.rows[0]?.count ?? 0
815
+ });
816
+ } catch {
817
+ orphans.push({
818
+ slug,
819
+ tableName,
820
+ rowCount: 0
821
+ });
822
+ }
823
+ }
824
+ return orphans;
825
+ }
826
+ /**
827
+ * Register an orphaned table as a collection
828
+ *
829
+ * Creates a _emdash_collections entry for an existing ec_* table.
830
+ */
831
+ async registerOrphanedTable(slug, options) {
832
+ const tableName = this.getTableName(slug);
833
+ if (!await tableExists(this.db, tableName)) throw new SchemaError(`Table "${tableName}" does not exist`, "TABLE_NOT_FOUND");
834
+ if (await this.getCollection(slug)) throw new SchemaError(`Collection "${slug}" is already registered`, "COLLECTION_EXISTS");
835
+ const id = ulid();
836
+ const label = options?.label || this.slugToLabel(slug);
837
+ await this.db.insertInto("_emdash_collections").values({
838
+ id,
839
+ slug,
840
+ label,
841
+ label_singular: options?.labelSingular ?? null,
842
+ description: options?.description ?? null,
843
+ icon: null,
844
+ supports: JSON.stringify([]),
845
+ source: "discovered",
846
+ has_seo: 0,
847
+ url_pattern: null
848
+ }).execute();
849
+ const collection = await this.getCollection(slug);
850
+ if (!collection) throw new SchemaError("Failed to register orphaned table", "REGISTER_FAILED");
851
+ return collection;
852
+ }
853
+ /**
854
+ * Convert slug to human-readable label
855
+ */
856
+ slugToLabel(slug) {
857
+ return slug.replace(UNDERSCORE_PATTERN, " ").replace(WORD_BOUNDARY_PATTERN, (c) => c.toUpperCase());
858
+ }
859
+ };
860
+
861
+ //#endregion
862
+ export { withTransaction as a, FTSManager as i, SchemaRegistry as n, registry_exports as r, SchemaError as t };
863
+ //# sourceMappingURL=registry-D_w5HW4G.mjs.map