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,93 @@
1
+ /**
2
+ * Public page context builder
3
+ *
4
+ * Templates call this to describe the page being rendered.
5
+ * The resulting context is passed to EmDashHead / EmDashBodyStart / EmDashBodyEnd.
6
+ */
7
+
8
+ import type { PublicPageContext } from "../plugins/types.js";
9
+
10
+ /** Fields shared by both input forms */
11
+ interface PageContextFields {
12
+ kind: "content" | "custom";
13
+ pageType?: string;
14
+ title?: string | null;
15
+ description?: string | null;
16
+ canonical?: string | null;
17
+ image?: string | null;
18
+ content?: { collection: string; id: string; slug?: string | null };
19
+ /** SEO overrides for OG/Twitter meta generation */
20
+ seo?: {
21
+ ogTitle?: string | null;
22
+ ogDescription?: string | null;
23
+ ogImage?: string | null;
24
+ robots?: string | null;
25
+ };
26
+ /** Article metadata for Open Graph article: tags */
27
+ articleMeta?: {
28
+ publishedTime?: string | null;
29
+ modifiedTime?: string | null;
30
+ author?: string | null;
31
+ };
32
+ /** Site name for structured data and og:site_name */
33
+ siteName?: string;
34
+ }
35
+
36
+ /** Input with Astro global -- used in .astro files */
37
+ interface AstroInput extends PageContextFields {
38
+ Astro: { url: URL; currentLocale?: string };
39
+ }
40
+
41
+ /** Input with explicit URL -- used outside .astro files */
42
+ interface UrlInput extends PageContextFields {
43
+ url: URL | string;
44
+ locale?: string;
45
+ }
46
+
47
+ export type CreatePublicPageContextInput = AstroInput | UrlInput;
48
+
49
+ function isAstroInput(input: CreatePublicPageContextInput): input is AstroInput {
50
+ return "Astro" in input;
51
+ }
52
+
53
+ /**
54
+ * Build a PublicPageContext from template input.
55
+ */
56
+ export function createPublicPageContext(input: CreatePublicPageContextInput): PublicPageContext {
57
+ let url: string;
58
+ let path: string;
59
+ let locale: string | null;
60
+
61
+ if (isAstroInput(input)) {
62
+ url = input.Astro.url.href;
63
+ path = input.Astro.url.pathname;
64
+ locale = input.Astro.currentLocale ?? null;
65
+ } else {
66
+ const parsed = typeof input.url === "string" ? new URL(input.url) : input.url;
67
+ url = parsed.href;
68
+ path = parsed.pathname;
69
+ locale = input.locale ?? null;
70
+ }
71
+
72
+ return {
73
+ url,
74
+ path,
75
+ locale,
76
+ kind: input.kind,
77
+ pageType: input.pageType ?? (input.kind === "content" ? "article" : "website"),
78
+ title: input.title ?? null,
79
+ description: input.description ?? null,
80
+ canonical: input.canonical ?? null,
81
+ image: input.image ?? null,
82
+ content: input.content
83
+ ? {
84
+ collection: input.content.collection,
85
+ id: input.content.id,
86
+ slug: input.content.slug ?? null,
87
+ }
88
+ : undefined,
89
+ seo: input.seo,
90
+ articleMeta: input.articleMeta,
91
+ siteName: input.siteName,
92
+ };
93
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Page fragment collection and rendering
3
+ *
4
+ * Collects raw markup / script contributions from trusted plugins via
5
+ * the page:fragments hook. Sandboxed plugins are never invoked.
6
+ */
7
+
8
+ import type { PageFragmentContribution, PagePlacement } from "../plugins/types.js";
9
+ import { escapeHtmlAttr } from "./metadata.js";
10
+
11
+ /** Escape sequences that would break out of a script tag */
12
+ const SCRIPT_CLOSE_RE = /<\//g;
13
+
14
+ // ── Dedupe and filter ───────────────────────────────────────────
15
+
16
+ /**
17
+ * Filter contributions to a specific placement and deduplicate.
18
+ * - Contributions with the same `key + placement` are deduped (first wins).
19
+ * - External scripts with the same `src + placement` are deduped.
20
+ */
21
+ export function resolveFragments(
22
+ contributions: PageFragmentContribution[],
23
+ placement: PagePlacement,
24
+ ): PageFragmentContribution[] {
25
+ const filtered = contributions.filter((c) => c.placement === placement);
26
+ const seen = new Set<string>();
27
+ const result: PageFragmentContribution[] = [];
28
+
29
+ for (const c of filtered) {
30
+ // Key-based dedupe
31
+ if (c.key) {
32
+ const dedupeKey = `key:${c.key}`;
33
+ if (seen.has(dedupeKey)) continue;
34
+ seen.add(dedupeKey);
35
+ } else if (c.kind === "external-script") {
36
+ const dedupeKey = `src:${c.src}`;
37
+ if (seen.has(dedupeKey)) continue;
38
+ seen.add(dedupeKey);
39
+ }
40
+
41
+ result.push(c);
42
+ }
43
+
44
+ return result;
45
+ }
46
+
47
+ // ── HTML rendering ──────────────────────────────────────────────
48
+
49
+ const EVENT_HANDLER_RE = /^on/i;
50
+
51
+ function renderAttributes(attrs: Record<string, string>): string {
52
+ return Object.entries(attrs)
53
+ .filter(([k]) => !EVENT_HANDLER_RE.test(k))
54
+ .map(([k, v]) => ` ${escapeHtmlAttr(k)}="${escapeHtmlAttr(v)}"`)
55
+ .join("");
56
+ }
57
+
58
+ /** Render a single fragment contribution to HTML */
59
+ function renderFragment(c: PageFragmentContribution): string {
60
+ switch (c.kind) {
61
+ case "external-script": {
62
+ let tag = `<script src="${escapeHtmlAttr(c.src)}"`;
63
+ if (c.async) tag += " async";
64
+ if (c.defer) tag += " defer";
65
+ if (c.attributes) tag += renderAttributes(c.attributes);
66
+ tag += "></script>";
67
+ return tag;
68
+ }
69
+ case "inline-script": {
70
+ let tag = "<script";
71
+ if (c.attributes) tag += renderAttributes(c.attributes);
72
+ // Escape </ to <\/ to prevent breaking out of the script tag.
73
+ // This is valid JS and protects against code built from user data.
74
+ tag += `>${c.code.replace(SCRIPT_CLOSE_RE, "<\\/")}</script>`;
75
+ return tag;
76
+ }
77
+ case "html":
78
+ return c.html;
79
+ }
80
+ }
81
+
82
+ /** Render a list of fragment contributions to an HTML string */
83
+ export function renderFragments(
84
+ contributions: PageFragmentContribution[],
85
+ placement: PagePlacement,
86
+ ): string {
87
+ const resolved = resolveFragments(contributions, placement);
88
+ return resolved.map(renderFragment).join("\n");
89
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * emdash/page — Public page contribution API
3
+ *
4
+ * Template integration points for plugin-driven head metadata
5
+ * and trusted body fragments.
6
+ */
7
+
8
+ import type {
9
+ PublicPageContext,
10
+ PageMetadataContribution,
11
+ PageFragmentContribution,
12
+ } from "../plugins/types.js";
13
+
14
+ export { createPublicPageContext } from "./context.js";
15
+ export type { CreatePublicPageContextInput } from "./context.js";
16
+
17
+ export {
18
+ resolvePageMetadata,
19
+ renderPageMetadata,
20
+ safeJsonLdSerialize,
21
+ escapeHtmlAttr,
22
+ } from "./metadata.js";
23
+ export type { ResolvedPageMetadata } from "./metadata.js";
24
+
25
+ export { resolveFragments, renderFragments } from "./fragments.js";
26
+
27
+ export { generateBaseSeoContributions } from "./seo-contributions.js";
28
+ export { cleanJsonLd, buildBlogPostingJsonLd, buildWebSiteJsonLd } from "./jsonld.js";
29
+
30
+ /**
31
+ * Shape of the EmDash runtime methods used by the render components.
32
+ * Extracted here so all three components share a single type definition.
33
+ */
34
+ export interface EmDashPageRuntime {
35
+ collectPageMetadata: (page: PublicPageContext) => Promise<PageMetadataContribution[]>;
36
+ collectPageFragments: (page: PublicPageContext) => Promise<PageFragmentContribution[]>;
37
+ }
38
+
39
+ /**
40
+ * Get the page runtime from Astro locals. Returns undefined when
41
+ * EmDash is not initialized (components render nothing in that case).
42
+ */
43
+ export function getPageRuntime(locals: Record<string, unknown>): EmDashPageRuntime | undefined {
44
+ const emdash = locals.emdash;
45
+ if (
46
+ emdash &&
47
+ typeof emdash === "object" &&
48
+ "collectPageMetadata" in emdash &&
49
+ "collectPageFragments" in emdash
50
+ ) {
51
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- structural check above confirms presence of required methods
52
+ return emdash as EmDashPageRuntime;
53
+ }
54
+ return undefined;
55
+ }
56
+
57
+ // Astro render components are exported from "emdash/ui":
58
+ // import { EmDashHead, EmDashBodyStart, EmDashBodyEnd } from "emdash/ui";
@@ -0,0 +1,94 @@
1
+ /**
2
+ * JSON-LD structured data builders
3
+ *
4
+ * Moved from template SEO.astro components into core so all JSON-LD
5
+ * is serialized via safeJsonLdSerialize() and never hand-rolled in templates.
6
+ */
7
+
8
+ import type { PublicPageContext } from "../plugins/types.js";
9
+
10
+ /**
11
+ * Remove null/undefined values from a JSON-LD object recursively.
12
+ * JSON-LD validators prefer absent keys over null values.
13
+ */
14
+ export function cleanJsonLd(obj: Record<string, unknown>): Record<string, unknown> {
15
+ const cleaned: Record<string, unknown> = {};
16
+ for (const [key, value] of Object.entries(obj)) {
17
+ if (value !== undefined && value !== null) {
18
+ if (typeof value === "object" && !Array.isArray(value)) {
19
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- non-null, non-array object is safely treated as Record<string, unknown> for JSON-LD traversal
20
+ cleaned[key] = cleanJsonLd(value as Record<string, unknown>);
21
+ } else {
22
+ cleaned[key] = value;
23
+ }
24
+ }
25
+ }
26
+ return cleaned;
27
+ }
28
+
29
+ /**
30
+ * Build a BlogPosting JSON-LD graph from page context.
31
+ * Used for article-type content pages.
32
+ */
33
+ export function buildBlogPostingJsonLd(page: PublicPageContext): Record<string, unknown> | null {
34
+ if (page.pageType !== "article" || !page.canonical) return null;
35
+
36
+ const ogTitle = page.seo?.ogTitle || page.title;
37
+ const description = page.seo?.ogDescription || page.description;
38
+ const ogImage = page.seo?.ogImage || page.image;
39
+ const publishedTime = page.articleMeta?.publishedTime;
40
+ const modifiedTime = page.articleMeta?.modifiedTime;
41
+ const author = page.articleMeta?.author;
42
+ const siteName = page.siteName;
43
+
44
+ return cleanJsonLd({
45
+ "@context": "https://schema.org",
46
+ "@type": "BlogPosting",
47
+ headline: ogTitle,
48
+ description,
49
+ image: ogImage || undefined,
50
+ url: page.canonical,
51
+ datePublished: publishedTime || undefined,
52
+ dateModified: modifiedTime || publishedTime || undefined,
53
+ author: author
54
+ ? {
55
+ "@type": "Person",
56
+ name: author,
57
+ }
58
+ : undefined,
59
+ publisher: siteName
60
+ ? {
61
+ "@type": "Organization",
62
+ name: siteName,
63
+ }
64
+ : undefined,
65
+ mainEntityOfPage: {
66
+ "@type": "WebPage",
67
+ "@id": page.canonical,
68
+ },
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Build a WebSite JSON-LD graph from page context.
74
+ * Used for non-article pages (homepage, listing pages, etc.)
75
+ */
76
+ export function buildWebSiteJsonLd(page: PublicPageContext): Record<string, unknown> | null {
77
+ const siteName = page.siteName;
78
+ if (!siteName) return null;
79
+
80
+ // Use origin from the page URL for the site URL
81
+ let siteUrl: string;
82
+ try {
83
+ siteUrl = new URL(page.url).origin;
84
+ } catch {
85
+ siteUrl = page.canonical || page.url;
86
+ }
87
+
88
+ return cleanJsonLd({
89
+ "@context": "https://schema.org",
90
+ "@type": "WebSite",
91
+ name: siteName,
92
+ url: siteUrl,
93
+ });
94
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Page metadata collection and rendering
3
+ *
4
+ * Collects typed metadata contributions from plugins via the page:metadata hook,
5
+ * validates them, and resolves them into a deduplicated structure ready to render.
6
+ */
7
+
8
+ import type { PageMetadataContribution, PageMetadataLinkRel } from "../plugins/types.js";
9
+
10
+ // ── Resolved output ─────────────────────────────────────────────
11
+
12
+ export interface ResolvedPageMetadata {
13
+ meta: Array<{ name: string; content: string }>;
14
+ properties: Array<{ property: string; content: string }>;
15
+ links: Array<{
16
+ rel: PageMetadataLinkRel;
17
+ href: string;
18
+ hreflang?: string;
19
+ }>;
20
+ jsonld: Array<{ id?: string; json: string }>;
21
+ }
22
+
23
+ // ── Validation ──────────────────────────────────────────────────
24
+
25
+ /** Schemes safe for use in link href attributes */
26
+ const SAFE_HREF_RE = /^(https?|at):\/\//i;
27
+ const HTML_ESCAPE_MAP: Record<string, string> = {
28
+ "&": "&amp;",
29
+ "<": "&lt;",
30
+ ">": "&gt;",
31
+ '"': "&quot;",
32
+ "'": "&#39;",
33
+ };
34
+ const HTML_ESCAPE_RE = /[&<>"']/g;
35
+
36
+ /** Escape a string for safe use in an HTML attribute value */
37
+ export function escapeHtmlAttr(value: string): string {
38
+ return value.replace(HTML_ESCAPE_RE, (ch) => HTML_ESCAPE_MAP[ch] ?? ch);
39
+ }
40
+
41
+ /** Validate that a URL uses a safe scheme (http, https, at) */
42
+ function isSafeHref(url: string): boolean {
43
+ return SAFE_HREF_RE.test(url);
44
+ }
45
+
46
+ // ── JSON-LD serialization ───────────────────────────────────────
47
+
48
+ const JSONLD_LT_RE = /</g;
49
+ const JSONLD_GT_RE = />/g;
50
+ const JSONLD_U2028_RE = /\u2028/g;
51
+ const JSONLD_U2029_RE = /\u2029/g;
52
+
53
+ /**
54
+ * Safely serialize a value for embedding in a <script type="application/ld+json"> tag.
55
+ *
56
+ * Plain JSON.stringify is not sufficient because:
57
+ * - "</script>" in a nested string breaks out of the script tag
58
+ * - "<!--" can open an HTML comment
59
+ * - U+2028/U+2029 are line terminators in some JS engines
60
+ */
61
+ export function safeJsonLdSerialize(value: unknown): string {
62
+ return JSON.stringify(value)
63
+ .replace(JSONLD_LT_RE, "\\u003c")
64
+ .replace(JSONLD_GT_RE, "\\u003e")
65
+ .replace(JSONLD_U2028_RE, "\\u2028")
66
+ .replace(JSONLD_U2029_RE, "\\u2029");
67
+ }
68
+
69
+ // ── Merge / dedupe ──────────────────────────────────────────────
70
+
71
+ /**
72
+ * Resolve a flat list of contributions into deduplicated metadata.
73
+ * First contribution wins for any given dedupe key.
74
+ */
75
+ export function resolvePageMetadata(
76
+ contributions: PageMetadataContribution[],
77
+ ): ResolvedPageMetadata {
78
+ const result: ResolvedPageMetadata = {
79
+ meta: [],
80
+ properties: [],
81
+ links: [],
82
+ jsonld: [],
83
+ };
84
+
85
+ const seenMeta = new Set<string>();
86
+ const seenProperties = new Set<string>();
87
+ const seenLinks = new Set<string>();
88
+ const seenJsonLd = new Set<string>();
89
+
90
+ for (const c of contributions) {
91
+ switch (c.kind) {
92
+ case "meta": {
93
+ const dedupeKey = c.key ?? c.name;
94
+ if (seenMeta.has(dedupeKey)) continue;
95
+ seenMeta.add(dedupeKey);
96
+ result.meta.push({ name: c.name, content: c.content });
97
+ break;
98
+ }
99
+ case "property": {
100
+ const dedupeKey = c.key ?? c.property;
101
+ if (seenProperties.has(dedupeKey)) continue;
102
+ seenProperties.add(dedupeKey);
103
+ result.properties.push({
104
+ property: c.property,
105
+ content: c.content,
106
+ });
107
+ break;
108
+ }
109
+ case "link": {
110
+ if (!isSafeHref(c.href)) {
111
+ if (import.meta.env?.DEV) {
112
+ console.warn(
113
+ `[page:metadata] Rejected link contribution with unsafe href scheme: ${c.href}`,
114
+ );
115
+ }
116
+ continue;
117
+ }
118
+ if (c.rel === "canonical") {
119
+ if (seenLinks.has("canonical")) continue;
120
+ seenLinks.add("canonical");
121
+ } else {
122
+ const dedupeKey = c.key ?? c.hreflang ?? c.href;
123
+ if (seenLinks.has(dedupeKey)) continue;
124
+ seenLinks.add(dedupeKey);
125
+ }
126
+ result.links.push({
127
+ rel: c.rel,
128
+ href: c.href,
129
+ ...(c.hreflang && { hreflang: c.hreflang }),
130
+ });
131
+ break;
132
+ }
133
+ case "jsonld": {
134
+ if (c.id) {
135
+ if (seenJsonLd.has(c.id)) continue;
136
+ seenJsonLd.add(c.id);
137
+ }
138
+ result.jsonld.push({
139
+ id: c.id,
140
+ json: safeJsonLdSerialize(c.graph),
141
+ });
142
+ break;
143
+ }
144
+ default:
145
+ // Unknown contribution kind -- skip silently at runtime.
146
+ // TypeScript catches this at compile time for typed callers,
147
+ // but sandboxed plugins may return unexpected shapes.
148
+ break;
149
+ }
150
+ }
151
+
152
+ return result;
153
+ }
154
+
155
+ // ── HTML rendering ──────────────────────────────────────────────
156
+
157
+ /** Render resolved metadata to an HTML string for embedding in <head> */
158
+ export function renderPageMetadata(metadata: ResolvedPageMetadata): string {
159
+ const parts: string[] = [];
160
+
161
+ for (const m of metadata.meta) {
162
+ parts.push(`<meta name="${escapeHtmlAttr(m.name)}" content="${escapeHtmlAttr(m.content)}">`);
163
+ }
164
+
165
+ for (const p of metadata.properties) {
166
+ parts.push(
167
+ `<meta property="${escapeHtmlAttr(p.property)}" content="${escapeHtmlAttr(p.content)}">`,
168
+ );
169
+ }
170
+
171
+ for (const l of metadata.links) {
172
+ let tag = `<link rel="${escapeHtmlAttr(l.rel)}" href="${escapeHtmlAttr(l.href)}"`;
173
+ if (l.hreflang) {
174
+ tag += ` hreflang="${escapeHtmlAttr(l.hreflang)}"`;
175
+ }
176
+ tag += ">";
177
+ parts.push(tag);
178
+ }
179
+
180
+ for (const j of metadata.jsonld) {
181
+ parts.push(`<script type="application/ld+json">${j.json}</script>`);
182
+ }
183
+
184
+ return parts.join("\n");
185
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Generate base SEO metadata contributions from PublicPageContext.
3
+ *
4
+ * These contributions are prepended BEFORE plugin contributions in
5
+ * resolvePageMetadata(), which uses first-wins dedup. This means
6
+ * plugins can override any base SEO tag by contributing the same key.
7
+ *
8
+ * This replaces the per-template SEO.astro components, eliminating
9
+ * the class of XSS bugs where templates hand-rolled JSON-LD serialization.
10
+ */
11
+
12
+ import type { PageMetadataContribution, PublicPageContext } from "../plugins/types.js";
13
+ import { buildBlogPostingJsonLd, buildWebSiteJsonLd } from "./jsonld.js";
14
+
15
+ /**
16
+ * Generate base metadata contributions from a page context's SEO data.
17
+ * Returns an empty array if no SEO-relevant data is present.
18
+ */
19
+ export function generateBaseSeoContributions(page: PublicPageContext): PageMetadataContribution[] {
20
+ const contributions: PageMetadataContribution[] = [];
21
+
22
+ const description = page.description;
23
+ const ogTitle = page.seo?.ogTitle || page.title;
24
+ const ogDescription = page.seo?.ogDescription || description;
25
+ const ogImage = page.seo?.ogImage || page.image;
26
+ const robots = page.seo?.robots;
27
+ const canonical = page.canonical;
28
+ const siteName = page.siteName;
29
+
30
+ // -- Meta tags --
31
+
32
+ if (description) {
33
+ contributions.push({ kind: "meta", name: "description", content: description });
34
+ }
35
+
36
+ if (robots) {
37
+ contributions.push({ kind: "meta", name: "robots", content: robots });
38
+ }
39
+
40
+ // -- Canonical link --
41
+
42
+ if (canonical) {
43
+ contributions.push({ kind: "link", rel: "canonical", href: canonical });
44
+ }
45
+
46
+ // -- Open Graph --
47
+
48
+ contributions.push({
49
+ kind: "property",
50
+ property: "og:type",
51
+ content: page.pageType === "article" ? "article" : "website",
52
+ });
53
+
54
+ if (ogTitle) {
55
+ contributions.push({ kind: "property", property: "og:title", content: ogTitle });
56
+ }
57
+
58
+ if (ogDescription) {
59
+ contributions.push({ kind: "property", property: "og:description", content: ogDescription });
60
+ }
61
+
62
+ if (ogImage) {
63
+ contributions.push({ kind: "property", property: "og:image", content: ogImage });
64
+ }
65
+
66
+ if (canonical) {
67
+ contributions.push({ kind: "property", property: "og:url", content: canonical });
68
+ }
69
+
70
+ if (siteName) {
71
+ contributions.push({ kind: "property", property: "og:site_name", content: siteName });
72
+ }
73
+
74
+ // -- Twitter Card --
75
+
76
+ contributions.push({
77
+ kind: "meta",
78
+ name: "twitter:card",
79
+ content: ogImage ? "summary_large_image" : "summary",
80
+ });
81
+
82
+ if (ogTitle) {
83
+ contributions.push({ kind: "meta", name: "twitter:title", content: ogTitle });
84
+ }
85
+
86
+ if (ogDescription) {
87
+ contributions.push({ kind: "meta", name: "twitter:description", content: ogDescription });
88
+ }
89
+
90
+ if (ogImage) {
91
+ contributions.push({ kind: "meta", name: "twitter:image", content: ogImage });
92
+ }
93
+
94
+ // -- Article metadata --
95
+
96
+ if (page.pageType === "article" && page.articleMeta) {
97
+ const { publishedTime, modifiedTime, author } = page.articleMeta;
98
+ if (publishedTime) {
99
+ contributions.push({
100
+ kind: "property",
101
+ property: "article:published_time",
102
+ content: publishedTime,
103
+ });
104
+ }
105
+ if (modifiedTime) {
106
+ contributions.push({
107
+ kind: "property",
108
+ property: "article:modified_time",
109
+ content: modifiedTime,
110
+ });
111
+ }
112
+ if (author) {
113
+ contributions.push({
114
+ kind: "property",
115
+ property: "article:author",
116
+ content: author,
117
+ });
118
+ }
119
+ }
120
+
121
+ // -- JSON-LD --
122
+
123
+ if (page.pageType === "article") {
124
+ const blogPosting = buildBlogPostingJsonLd(page);
125
+ if (blogPosting) {
126
+ contributions.push({ kind: "jsonld", id: "primary", graph: blogPosting });
127
+ }
128
+ } else if (siteName) {
129
+ const webSite = buildWebSiteJsonLd(page);
130
+ if (webSite) {
131
+ contributions.push({ kind: "jsonld", id: "primary", graph: webSite });
132
+ }
133
+ }
134
+
135
+ return contributions;
136
+ }