edgepress 2026.10.1 → 2026.10.13

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 (485) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -16
  3. package/config.yml +99 -127
  4. package/content/assets/edgepress/brand/avatar.png +0 -0
  5. package/content/assets/edgepress/brand/deploy-esa.svg +1 -0
  6. package/content/assets/edgepress/brand/home-hero-768.webp +0 -0
  7. package/content/assets/edgepress/brand/home-hero.png +0 -0
  8. package/content/assets/edgepress/brand/home-hero.webp +0 -0
  9. package/content/assets/edgepress/brand/mascot.png +0 -0
  10. package/content/assets/edgepress/brand/mascot.webp +0 -0
  11. package/content/assets/edgepress/brand/og-image.png +0 -0
  12. package/content/assets/edgepress/brand/og-image.webp +0 -0
  13. package/content/assets/edgepress/brand/sources/edgepress-avatar-master.png +0 -0
  14. package/content/assets/edgepress/brand/sources/export-manifest.json +181 -0
  15. package/content/assets/edgepress/brand/sources/prompts.json +12 -0
  16. package/content/assets/edgepress/favicon/apple-touch-icon.png +0 -0
  17. package/content/assets/edgepress/favicon/favicon-16.png +0 -0
  18. package/content/assets/edgepress/favicon/favicon-32.png +0 -0
  19. package/content/assets/edgepress/favicon/favicon-48.png +0 -0
  20. package/content/assets/edgepress/favicon/favicon-64.png +0 -0
  21. package/content/assets/edgepress/favicon/favicon.ico +0 -0
  22. package/content/assets/edgepress/icons/LICENSE.txt +43 -0
  23. package/content/assets/edgepress/icons/arrow-right.svg +16 -0
  24. package/content/assets/edgepress/icons/arrow-up-right.svg +16 -0
  25. package/content/assets/edgepress/icons/book-open.svg +16 -0
  26. package/content/assets/edgepress/icons/check-circle.svg +16 -0
  27. package/content/assets/edgepress/icons/check.svg +15 -0
  28. package/content/assets/edgepress/icons/cloud.svg +15 -0
  29. package/content/assets/edgepress/icons/code.svg +16 -0
  30. package/content/assets/edgepress/icons/cpu.svg +28 -0
  31. package/content/assets/edgepress/icons/file-text.svg +19 -0
  32. package/content/assets/edgepress/icons/github.svg +17 -0
  33. package/content/assets/edgepress/icons/globe.svg +17 -0
  34. package/content/assets/edgepress/icons/help-circle.svg +17 -0
  35. package/content/assets/edgepress/icons/home.svg +16 -0
  36. package/content/assets/edgepress/icons/layers.svg +17 -0
  37. package/content/assets/edgepress/icons/layout-grid.svg +18 -0
  38. package/content/assets/edgepress/icons/mail.svg +16 -0
  39. package/content/assets/edgepress/icons/newspaper.svg +18 -0
  40. package/content/assets/edgepress/icons/palette.svg +19 -0
  41. package/content/assets/edgepress/icons/plug.svg +18 -0
  42. package/content/assets/edgepress/icons/provenance.json +8 -0
  43. package/content/assets/edgepress/icons/puzzle.svg +15 -0
  44. package/content/assets/edgepress/icons/rocket.svg +18 -0
  45. package/content/assets/edgepress/icons/rss.svg +17 -0
  46. package/content/assets/edgepress/icons/search.svg +16 -0
  47. package/content/assets/edgepress/icons/settings.svg +16 -0
  48. package/content/assets/edgepress/icons/shield-check.svg +16 -0
  49. package/content/assets/edgepress/icons/sparkles.svg +18 -0
  50. package/content/assets/edgepress/icons/terminal.svg +16 -0
  51. package/content/assets/edgepress/icons/x.svg +16 -0
  52. package/{static → content/assets}/edgepress-markdown-guide.svg +1 -1
  53. package/content/assets/images/previews/edgepress-en.webp +0 -0
  54. package/content/assets/images/previews/edgepress-revisions-en.png +0 -0
  55. package/content/assets/images/previews/edgepress-workflow-en.90f4650b21013786.gif +0 -0
  56. package/content/assets/images/previews/edgepress-workflow-zh.41f1e0a685f47267.gif +0 -0
  57. package/content/assets/images/previews/edgepress-zh.webp +0 -0
  58. package/content/assets/images/previews/provenance.json +49 -0
  59. package/content/guides/markdown-media.md +38 -0
  60. package/content/guides/platforms.md +15 -0
  61. package/content/guides/product-demos.md +37 -0
  62. package/content/guides/zh-cn.md +62 -0
  63. package/content/pages/deployment/en.json +74 -0
  64. package/content/pages/deployment/zh-cn.json +74 -0
  65. package/content/pages/home/en.json +248 -0
  66. package/content/pages/home/zh-cn.json +248 -0
  67. package/content/pages/local-tools/en.json +14 -0
  68. package/content/pages/local-tools/zh-cn.json +14 -0
  69. package/content/pages/page-authoring/en.json +107 -0
  70. package/content/pages/page-authoring/zh-cn.json +107 -0
  71. package/content/pages/plugin-development/en.json +202 -0
  72. package/content/pages/plugin-development/zh-cn.json +202 -0
  73. package/content/pages/privacy-policy/en.json +171 -0
  74. package/content/pages/privacy-policy/zh-cn.json +171 -0
  75. package/content/pages/project-introduction/en.json +158 -0
  76. package/content/pages/project-introduction/zh-cn.json +158 -0
  77. package/content/pages/quick-start/en.json +325 -0
  78. package/content/pages/quick-start/zh-cn.json +325 -0
  79. package/content/pages/theme-development/en.json +353 -0
  80. package/content/pages/theme-development/zh-cn.json +349 -0
  81. package/content/posts/2026-09-24-edgepress-release-2026-9-1/en.md +1 -1
  82. package/content/posts/2026-09-24-edgepress-release-2026-9-1/zh-cn.md +1 -1
  83. package/content/posts/2026-10-03-edgepress-release-2026-10-3/en.md +16 -0
  84. package/content/posts/2026-10-03-edgepress-release-2026-10-3/zh-cn.md +16 -0
  85. package/content/posts/2026-10-03-edgepress-release-202610-4/en.md +22 -0
  86. package/content/posts/2026-10-03-edgepress-release-202610-4/zh-cn.md +20 -0
  87. package/content/posts/2026-10-03-edgepress-release-202610-5/en.md +15 -0
  88. package/content/posts/2026-10-03-edgepress-release-202610-5/zh-cn.md +15 -0
  89. package/edgeone.json +43 -0
  90. package/esa.jsonc +9 -0
  91. package/languages/base/en.json +75 -10
  92. package/languages/packs/zh-CN.json +74 -9
  93. package/netlify.toml +6 -0
  94. package/package.json +37 -6
  95. package/plugins/consent/index.js +10 -17
  96. package/project-compatibility.json +142 -25
  97. package/schemas/pages.schema.json +74 -0
  98. package/src/assets.js +146 -18
  99. package/src/browser-audit.js +158 -0
  100. package/src/build.js +34 -9
  101. package/src/cli.js +46 -41
  102. package/src/config.js +114 -63
  103. package/src/consent-csp.js +137 -0
  104. package/src/content.js +55 -30
  105. package/src/data-saver.js +194 -0
  106. package/src/deploy.js +28 -0
  107. package/src/document.js +44 -0
  108. package/src/embed.js +60 -0
  109. package/src/generators.js +101 -59
  110. package/src/i18n.js +19 -3
  111. package/src/icons.js +5 -4
  112. package/src/image-dimensions.js +17 -0
  113. package/src/local-tool.js +29 -0
  114. package/src/maintenance.js +25 -8
  115. package/src/markdown-fixture.js +20 -0
  116. package/src/markdown.js +80 -16
  117. package/src/media-assets.js +19 -0
  118. package/src/media-viewer.js +47 -0
  119. package/src/oembed.js +53 -0
  120. package/src/page-blocks.js +124 -54
  121. package/src/page-check.js +29 -39
  122. package/src/plugin-api.js +7 -1
  123. package/src/post-details.js +60 -0
  124. package/src/post-template.js +5 -0
  125. package/src/publication-time.js +47 -0
  126. package/src/report-path.js +19 -0
  127. package/src/report-visuals.js +91 -18
  128. package/src/revision-cards.js +30 -0
  129. package/src/revision-images.js +44 -0
  130. package/src/revisions.js +77 -0
  131. package/src/rss.js +100 -0
  132. package/src/service-block.js +18 -0
  133. package/src/theme.js +59 -9
  134. package/src/video-metadata.js +36 -0
  135. package/static/edgepress/data-saver.css +12 -0
  136. package/static/edgepress/data-saver.js +333 -0
  137. package/static/edgepress/document-viewer.js +35 -0
  138. package/static/edgepress/external-links-policy.js +14 -0
  139. package/static/edgepress/external-links.css +13 -0
  140. package/static/edgepress/external-links.js +120 -0
  141. package/static/edgepress/fonts/faces.css +2217 -0
  142. package/static/edgepress/fonts/noto-sans-OFL.txt +93 -0
  143. package/static/edgepress/fonts/noto-sans-cyrillic-ext-wght-normal.82ff72b28e6610f2.woff2 +0 -0
  144. package/static/edgepress/fonts/noto-sans-cyrillic-wght-normal.6ab64433de6077ca.woff2 +0 -0
  145. package/static/edgepress/fonts/noto-sans-devanagari-wght-normal.ea6a530b75d93de7.woff2 +0 -0
  146. package/static/edgepress/fonts/noto-sans-greek-ext-wght-normal.790b255c10c32e0b.woff2 +0 -0
  147. package/static/edgepress/fonts/noto-sans-greek-wght-normal.7f655207c2f6c140.woff2 +0 -0
  148. package/static/edgepress/fonts/noto-sans-latin-ext-wght-normal.6d5a79315528df19.woff2 +0 -0
  149. package/static/edgepress/fonts/noto-sans-latin-wght-normal.51ca196f49a33e79.woff2 +0 -0
  150. package/static/edgepress/fonts/noto-sans-sc-100-wght-normal.ad5f29028d5699a5.woff2 +0 -0
  151. package/static/edgepress/fonts/noto-sans-sc-101-wght-normal.e189b81fc22ca6af.woff2 +0 -0
  152. package/static/edgepress/fonts/noto-sans-sc-102-wght-normal.941c0c2971fc1d6d.woff2 +0 -0
  153. package/static/edgepress/fonts/noto-sans-sc-103-wght-normal.bafbd591af6493ff.woff2 +0 -0
  154. package/static/edgepress/fonts/noto-sans-sc-104-wght-normal.b0bf80ced89dd61b.woff2 +0 -0
  155. package/static/edgepress/fonts/noto-sans-sc-105-wght-normal.8d8b4e6f4a00621d.woff2 +0 -0
  156. package/static/edgepress/fonts/noto-sans-sc-106-wght-normal.bae74acc629d2e74.woff2 +0 -0
  157. package/static/edgepress/fonts/noto-sans-sc-107-wght-normal.a42eff5010de7b0d.woff2 +0 -0
  158. package/static/edgepress/fonts/noto-sans-sc-108-wght-normal.91882203459867be.woff2 +0 -0
  159. package/static/edgepress/fonts/noto-sans-sc-109-wght-normal.a91f8b038e5ce108.woff2 +0 -0
  160. package/static/edgepress/fonts/noto-sans-sc-110-wght-normal.bbca9081655c4523.woff2 +0 -0
  161. package/static/edgepress/fonts/noto-sans-sc-111-wght-normal.372be010ac0e87cb.woff2 +0 -0
  162. package/static/edgepress/fonts/noto-sans-sc-112-wght-normal.34a4580239eae8dc.woff2 +0 -0
  163. package/static/edgepress/fonts/noto-sans-sc-113-wght-normal.614ab8f616352583.woff2 +0 -0
  164. package/static/edgepress/fonts/noto-sans-sc-114-wght-normal.cf7b8344807a316e.woff2 +0 -0
  165. package/static/edgepress/fonts/noto-sans-sc-115-wght-normal.fb29957c7adccb9a.woff2 +0 -0
  166. package/static/edgepress/fonts/noto-sans-sc-116-wght-normal.a98dc5a726f82141.woff2 +0 -0
  167. package/static/edgepress/fonts/noto-sans-sc-117-wght-normal.d94b5af0c14838af.woff2 +0 -0
  168. package/static/edgepress/fonts/noto-sans-sc-118-wght-normal.76c9ddb85ae4578c.woff2 +0 -0
  169. package/static/edgepress/fonts/noto-sans-sc-119-wght-normal.b3b2374881f4cd93.woff2 +0 -0
  170. package/static/edgepress/fonts/noto-sans-sc-21-wght-normal.c1409c43e55ecff2.woff2 +0 -0
  171. package/static/edgepress/fonts/noto-sans-sc-22-wght-normal.77037ed42ff4e7b0.woff2 +0 -0
  172. package/static/edgepress/fonts/noto-sans-sc-23-wght-normal.38d0df21ac374e93.woff2 +0 -0
  173. package/static/edgepress/fonts/noto-sans-sc-24-wght-normal.8fa21e7e457acf3e.woff2 +0 -0
  174. package/static/edgepress/fonts/noto-sans-sc-25-wght-normal.f760b7a3871d214d.woff2 +0 -0
  175. package/static/edgepress/fonts/noto-sans-sc-26-wght-normal.be8386e7bd7facfe.woff2 +0 -0
  176. package/static/edgepress/fonts/noto-sans-sc-27-wght-normal.4895abcaddbd162e.woff2 +0 -0
  177. package/static/edgepress/fonts/noto-sans-sc-28-wght-normal.82540b6201a9b9ad.woff2 +0 -0
  178. package/static/edgepress/fonts/noto-sans-sc-29-wght-normal.ff21d8fcc60ce391.woff2 +0 -0
  179. package/static/edgepress/fonts/noto-sans-sc-30-wght-normal.1f76f6ffded00b9e.woff2 +0 -0
  180. package/static/edgepress/fonts/noto-sans-sc-31-wght-normal.e6c25be1bb08a63a.woff2 +0 -0
  181. package/static/edgepress/fonts/noto-sans-sc-32-wght-normal.0cda750d7d04a4e1.woff2 +0 -0
  182. package/static/edgepress/fonts/noto-sans-sc-33-wght-normal.b9732b5606bd87c5.woff2 +0 -0
  183. package/static/edgepress/fonts/noto-sans-sc-34-wght-normal.38aa326b4c84902a.woff2 +0 -0
  184. package/static/edgepress/fonts/noto-sans-sc-35-wght-normal.bd98226c8e0aa808.woff2 +0 -0
  185. package/static/edgepress/fonts/noto-sans-sc-36-wght-normal.0db1ca695a491d37.woff2 +0 -0
  186. package/static/edgepress/fonts/noto-sans-sc-37-wght-normal.8df409a4e0a237c2.woff2 +0 -0
  187. package/static/edgepress/fonts/noto-sans-sc-38-wght-normal.672d2d994994a3c3.woff2 +0 -0
  188. package/static/edgepress/fonts/noto-sans-sc-39-wght-normal.272d6d0e153f56e8.woff2 +0 -0
  189. package/static/edgepress/fonts/noto-sans-sc-4-wght-normal.9fafd93ac77b81ff.woff2 +0 -0
  190. package/static/edgepress/fonts/noto-sans-sc-40-wght-normal.5b9ef1ed48420aa7.woff2 +0 -0
  191. package/static/edgepress/fonts/noto-sans-sc-41-wght-normal.30aee47f660fd88a.woff2 +0 -0
  192. package/static/edgepress/fonts/noto-sans-sc-42-wght-normal.ec0014830db682b6.woff2 +0 -0
  193. package/static/edgepress/fonts/noto-sans-sc-43-wght-normal.43df2b5dad9bb835.woff2 +0 -0
  194. package/static/edgepress/fonts/noto-sans-sc-44-wght-normal.acaa6195041b1a92.woff2 +0 -0
  195. package/static/edgepress/fonts/noto-sans-sc-45-wght-normal.8cdb9903f770db21.woff2 +0 -0
  196. package/static/edgepress/fonts/noto-sans-sc-46-wght-normal.6590d0a6fe94571c.woff2 +0 -0
  197. package/static/edgepress/fonts/noto-sans-sc-47-wght-normal.b05afdd32b3bf722.woff2 +0 -0
  198. package/static/edgepress/fonts/noto-sans-sc-48-wght-normal.c9585f7d070700be.woff2 +0 -0
  199. package/static/edgepress/fonts/noto-sans-sc-49-wght-normal.4943895a4dfa5dcf.woff2 +0 -0
  200. package/static/edgepress/fonts/noto-sans-sc-5-wght-normal.e64f57937e3ae4fe.woff2 +0 -0
  201. package/static/edgepress/fonts/noto-sans-sc-50-wght-normal.13e51063d7743cfe.woff2 +0 -0
  202. package/static/edgepress/fonts/noto-sans-sc-51-wght-normal.fd4df1f02c2cb4d7.woff2 +0 -0
  203. package/static/edgepress/fonts/noto-sans-sc-52-wght-normal.06999a39c4c3a7ea.woff2 +0 -0
  204. package/static/edgepress/fonts/noto-sans-sc-53-wght-normal.767b7b9864658d91.woff2 +0 -0
  205. package/static/edgepress/fonts/noto-sans-sc-54-wght-normal.4cd00537a46f6f1a.woff2 +0 -0
  206. package/static/edgepress/fonts/noto-sans-sc-55-wght-normal.0118ec5d8c30b197.woff2 +0 -0
  207. package/static/edgepress/fonts/noto-sans-sc-56-wght-normal.a3e4a78929826930.woff2 +0 -0
  208. package/static/edgepress/fonts/noto-sans-sc-57-wght-normal.7623501009e4a6db.woff2 +0 -0
  209. package/static/edgepress/fonts/noto-sans-sc-58-wght-normal.56ae1a59a32e4a25.woff2 +0 -0
  210. package/static/edgepress/fonts/noto-sans-sc-59-wght-normal.7a377a40e99fd9bc.woff2 +0 -0
  211. package/static/edgepress/fonts/noto-sans-sc-6-wght-normal.17e15c4f0a5bfd92.woff2 +0 -0
  212. package/static/edgepress/fonts/noto-sans-sc-60-wght-normal.3657f66babdf6aff.woff2 +0 -0
  213. package/static/edgepress/fonts/noto-sans-sc-61-wght-normal.23919a2eb11d25dd.woff2 +0 -0
  214. package/static/edgepress/fonts/noto-sans-sc-62-wght-normal.a901edf25bc1cfae.woff2 +0 -0
  215. package/static/edgepress/fonts/noto-sans-sc-63-wght-normal.92f56f713cc0b62f.woff2 +0 -0
  216. package/static/edgepress/fonts/noto-sans-sc-64-wght-normal.4929c9f39a5a4bd9.woff2 +0 -0
  217. package/static/edgepress/fonts/noto-sans-sc-65-wght-normal.8daa61282fb62a11.woff2 +0 -0
  218. package/static/edgepress/fonts/noto-sans-sc-66-wght-normal.6479166925eb462b.woff2 +0 -0
  219. package/static/edgepress/fonts/noto-sans-sc-67-wght-normal.a6b0e11c4c761ffd.woff2 +0 -0
  220. package/static/edgepress/fonts/noto-sans-sc-68-wght-normal.65b77f1fe9d68dae.woff2 +0 -0
  221. package/static/edgepress/fonts/noto-sans-sc-69-wght-normal.b34f765cbbf4d02e.woff2 +0 -0
  222. package/static/edgepress/fonts/noto-sans-sc-70-wght-normal.a99d55c00e4f95b7.woff2 +0 -0
  223. package/static/edgepress/fonts/noto-sans-sc-71-wght-normal.3298bed30dc8d233.woff2 +0 -0
  224. package/static/edgepress/fonts/noto-sans-sc-72-wght-normal.6597209ca9707726.woff2 +0 -0
  225. package/static/edgepress/fonts/noto-sans-sc-73-wght-normal.e7ac1d4930eabd78.woff2 +0 -0
  226. package/static/edgepress/fonts/noto-sans-sc-74-wght-normal.1f0287be83a28e7a.woff2 +0 -0
  227. package/static/edgepress/fonts/noto-sans-sc-75-wght-normal.d9a4bb7bcc2be578.woff2 +0 -0
  228. package/static/edgepress/fonts/noto-sans-sc-76-wght-normal.5a019946bcadcec6.woff2 +0 -0
  229. package/static/edgepress/fonts/noto-sans-sc-77-wght-normal.7c445e85d04f5319.woff2 +0 -0
  230. package/static/edgepress/fonts/noto-sans-sc-78-wght-normal.7d4afb70f641781c.woff2 +0 -0
  231. package/static/edgepress/fonts/noto-sans-sc-79-wght-normal.5798753a721bb4ec.woff2 +0 -0
  232. package/static/edgepress/fonts/noto-sans-sc-80-wght-normal.db887159ca6e46f3.woff2 +0 -0
  233. package/static/edgepress/fonts/noto-sans-sc-81-wght-normal.71e5743bbd225b3d.woff2 +0 -0
  234. package/static/edgepress/fonts/noto-sans-sc-82-wght-normal.8c5094c3785fd04f.woff2 +0 -0
  235. package/static/edgepress/fonts/noto-sans-sc-83-wght-normal.4a60a1ccba905284.woff2 +0 -0
  236. package/static/edgepress/fonts/noto-sans-sc-84-wght-normal.3ed54e59ef4758e3.woff2 +0 -0
  237. package/static/edgepress/fonts/noto-sans-sc-85-wght-normal.e87e0a1342e1778c.woff2 +0 -0
  238. package/static/edgepress/fonts/noto-sans-sc-86-wght-normal.6d3ac3e20959dbcb.woff2 +0 -0
  239. package/static/edgepress/fonts/noto-sans-sc-87-wght-normal.0eeffd5daad5c17b.woff2 +0 -0
  240. package/static/edgepress/fonts/noto-sans-sc-88-wght-normal.b1d9582dfa268463.woff2 +0 -0
  241. package/static/edgepress/fonts/noto-sans-sc-89-wght-normal.8b437aeb9e9fcb5a.woff2 +0 -0
  242. package/static/edgepress/fonts/noto-sans-sc-90-wght-normal.83b0a59e0a5e045a.woff2 +0 -0
  243. package/static/edgepress/fonts/noto-sans-sc-91-wght-normal.382f72b98d4bb9d7.woff2 +0 -0
  244. package/static/edgepress/fonts/noto-sans-sc-97-wght-normal.5e52856457c5cdab.woff2 +0 -0
  245. package/static/edgepress/fonts/noto-sans-sc-98-wght-normal.ffded4e7fd560d93.woff2 +0 -0
  246. package/static/edgepress/fonts/noto-sans-sc-99-wght-normal.54e302c23f11a8fd.woff2 +0 -0
  247. package/static/edgepress/fonts/noto-sans-sc-OFL.txt +93 -0
  248. package/static/edgepress/fonts/noto-sans-sc-cyrillic-wght-normal.5932d441e7126669.woff2 +0 -0
  249. package/static/edgepress/fonts/noto-sans-sc-latin-ext-wght-normal.e316201f3fc493d1.woff2 +0 -0
  250. package/static/edgepress/fonts/noto-sans-sc-latin-wght-normal.c1d919c8701a86ed.woff2 +0 -0
  251. package/static/edgepress/fonts/noto-sans-sc-vietnamese-wght-normal.cba3d00c8db989f0.woff2 +0 -0
  252. package/static/edgepress/fonts/noto-sans-tc-0-wght-normal.63c21eda5d20203f.woff2 +0 -0
  253. package/static/edgepress/fonts/noto-sans-tc-100-wght-normal.87a7c76301c24a28.woff2 +0 -0
  254. package/static/edgepress/fonts/noto-sans-tc-101-wght-normal.c9e83fc4ffd53746.woff2 +0 -0
  255. package/static/edgepress/fonts/noto-sans-tc-102-wght-normal.9a00ae0e5735852b.woff2 +0 -0
  256. package/static/edgepress/fonts/noto-sans-tc-103-wght-normal.7cfe93b3221f4def.woff2 +0 -0
  257. package/static/edgepress/fonts/noto-sans-tc-104-wght-normal.2d5ac7497d84cb99.woff2 +0 -0
  258. package/static/edgepress/fonts/noto-sans-tc-105-wght-normal.0989885bb16bcd9b.woff2 +0 -0
  259. package/static/edgepress/fonts/noto-sans-tc-106-wght-normal.1faa921b035064e7.woff2 +0 -0
  260. package/static/edgepress/fonts/noto-sans-tc-107-wght-normal.2f6006745ae16d69.woff2 +0 -0
  261. package/static/edgepress/fonts/noto-sans-tc-108-wght-normal.a61e48c10a2cf4b9.woff2 +0 -0
  262. package/static/edgepress/fonts/noto-sans-tc-109-wght-normal.f28c767724736d2d.woff2 +0 -0
  263. package/static/edgepress/fonts/noto-sans-tc-110-wght-normal.4b04bd88c3dc7c5d.woff2 +0 -0
  264. package/static/edgepress/fonts/noto-sans-tc-111-wght-normal.b6251e1967932a52.woff2 +0 -0
  265. package/static/edgepress/fonts/noto-sans-tc-112-wght-normal.e264513b32f90660.woff2 +0 -0
  266. package/static/edgepress/fonts/noto-sans-tc-113-wght-normal.46cb347d88f3338e.woff2 +0 -0
  267. package/static/edgepress/fonts/noto-sans-tc-114-wght-normal.9f9e1e0d0ab2aeb4.woff2 +0 -0
  268. package/static/edgepress/fonts/noto-sans-tc-115-wght-normal.653dd2ab7d551b40.woff2 +0 -0
  269. package/static/edgepress/fonts/noto-sans-tc-116-wght-normal.4718bf3627191297.woff2 +0 -0
  270. package/static/edgepress/fonts/noto-sans-tc-117-wght-normal.b241a9409682af5f.woff2 +0 -0
  271. package/static/edgepress/fonts/noto-sans-tc-118-wght-normal.fda2800dc1c06852.woff2 +0 -0
  272. package/static/edgepress/fonts/noto-sans-tc-119-wght-normal.948af36bd63a72c7.woff2 +0 -0
  273. package/static/edgepress/fonts/noto-sans-tc-19-wght-normal.62191cdc3a10e60a.woff2 +0 -0
  274. package/static/edgepress/fonts/noto-sans-tc-20-wght-normal.65c90892c2e01657.woff2 +0 -0
  275. package/static/edgepress/fonts/noto-sans-tc-21-wght-normal.d3f59b094c334866.woff2 +0 -0
  276. package/static/edgepress/fonts/noto-sans-tc-22-wght-normal.b1983a041d7e4e1f.woff2 +0 -0
  277. package/static/edgepress/fonts/noto-sans-tc-23-wght-normal.16e2163b66c7732a.woff2 +0 -0
  278. package/static/edgepress/fonts/noto-sans-tc-24-wght-normal.72c8561976116537.woff2 +0 -0
  279. package/static/edgepress/fonts/noto-sans-tc-25-wght-normal.b2948d928ce4dd54.woff2 +0 -0
  280. package/static/edgepress/fonts/noto-sans-tc-26-wght-normal.d583236b90abedc9.woff2 +0 -0
  281. package/static/edgepress/fonts/noto-sans-tc-27-wght-normal.7d6073ba07597c9b.woff2 +0 -0
  282. package/static/edgepress/fonts/noto-sans-tc-28-wght-normal.517204d716b089cd.woff2 +0 -0
  283. package/static/edgepress/fonts/noto-sans-tc-29-wght-normal.10189a8f8ca38099.woff2 +0 -0
  284. package/static/edgepress/fonts/noto-sans-tc-30-wght-normal.695a71d72dfab3f7.woff2 +0 -0
  285. package/static/edgepress/fonts/noto-sans-tc-31-wght-normal.868d59473ef0e73c.woff2 +0 -0
  286. package/static/edgepress/fonts/noto-sans-tc-32-wght-normal.92efa2d4b3702ec0.woff2 +0 -0
  287. package/static/edgepress/fonts/noto-sans-tc-33-wght-normal.d2274bcd5636197d.woff2 +0 -0
  288. package/static/edgepress/fonts/noto-sans-tc-34-wght-normal.1ac8c3718ee2eed2.woff2 +0 -0
  289. package/static/edgepress/fonts/noto-sans-tc-35-wght-normal.00b3df730d5a4c73.woff2 +0 -0
  290. package/static/edgepress/fonts/noto-sans-tc-36-wght-normal.6963feb6c628b81f.woff2 +0 -0
  291. package/static/edgepress/fonts/noto-sans-tc-37-wght-normal.a1b4bd294af24cd1.woff2 +0 -0
  292. package/static/edgepress/fonts/noto-sans-tc-38-wght-normal.8edb2f7f2955921b.woff2 +0 -0
  293. package/static/edgepress/fonts/noto-sans-tc-39-wght-normal.37bf7394eb3148c6.woff2 +0 -0
  294. package/static/edgepress/fonts/noto-sans-tc-40-wght-normal.3efa655bc48f9133.woff2 +0 -0
  295. package/static/edgepress/fonts/noto-sans-tc-41-wght-normal.48d47014544ebd5d.woff2 +0 -0
  296. package/static/edgepress/fonts/noto-sans-tc-42-wght-normal.214c733a59d423e7.woff2 +0 -0
  297. package/static/edgepress/fonts/noto-sans-tc-43-wght-normal.c2c9876f138bb20f.woff2 +0 -0
  298. package/static/edgepress/fonts/noto-sans-tc-44-wght-normal.28928bfea29db06a.woff2 +0 -0
  299. package/static/edgepress/fonts/noto-sans-tc-45-wght-normal.bf3743f8155fa9d2.woff2 +0 -0
  300. package/static/edgepress/fonts/noto-sans-tc-46-wght-normal.06970903593781c3.woff2 +0 -0
  301. package/static/edgepress/fonts/noto-sans-tc-47-wght-normal.8932ed83a91794d4.woff2 +0 -0
  302. package/static/edgepress/fonts/noto-sans-tc-48-wght-normal.dfd9225628bcf234.woff2 +0 -0
  303. package/static/edgepress/fonts/noto-sans-tc-49-wght-normal.c27d341ad39d44eb.woff2 +0 -0
  304. package/static/edgepress/fonts/noto-sans-tc-50-wght-normal.01a0f4e7dfba0a56.woff2 +0 -0
  305. package/static/edgepress/fonts/noto-sans-tc-51-wght-normal.5a30022af3d30e0f.woff2 +0 -0
  306. package/static/edgepress/fonts/noto-sans-tc-52-wght-normal.4884f032342a3274.woff2 +0 -0
  307. package/static/edgepress/fonts/noto-sans-tc-53-wght-normal.537adcae44511270.woff2 +0 -0
  308. package/static/edgepress/fonts/noto-sans-tc-54-wght-normal.6bc8c7d3e47c6fde.woff2 +0 -0
  309. package/static/edgepress/fonts/noto-sans-tc-55-wght-normal.725a48711f1e1997.woff2 +0 -0
  310. package/static/edgepress/fonts/noto-sans-tc-56-wght-normal.f43ecaa73e0cd8fc.woff2 +0 -0
  311. package/static/edgepress/fonts/noto-sans-tc-57-wght-normal.15b24e9d42a3270f.woff2 +0 -0
  312. package/static/edgepress/fonts/noto-sans-tc-58-wght-normal.9b2d8a71667d3da6.woff2 +0 -0
  313. package/static/edgepress/fonts/noto-sans-tc-59-wght-normal.a68535a6b3289d89.woff2 +0 -0
  314. package/static/edgepress/fonts/noto-sans-tc-6-wght-normal.32cece1ae80c4f2b.woff2 +0 -0
  315. package/static/edgepress/fonts/noto-sans-tc-60-wght-normal.3b2c56a921d1cad4.woff2 +0 -0
  316. package/static/edgepress/fonts/noto-sans-tc-61-wght-normal.54f51f80a0740878.woff2 +0 -0
  317. package/static/edgepress/fonts/noto-sans-tc-62-wght-normal.ba029c5bb6c07bf1.woff2 +0 -0
  318. package/static/edgepress/fonts/noto-sans-tc-63-wght-normal.380919beec4c7f3d.woff2 +0 -0
  319. package/static/edgepress/fonts/noto-sans-tc-64-wght-normal.f96fe0be479d201f.woff2 +0 -0
  320. package/static/edgepress/fonts/noto-sans-tc-65-wght-normal.7d544b80701fe678.woff2 +0 -0
  321. package/static/edgepress/fonts/noto-sans-tc-66-wght-normal.fab669c07a1e3bee.woff2 +0 -0
  322. package/static/edgepress/fonts/noto-sans-tc-67-wght-normal.0001bb744743fdfa.woff2 +0 -0
  323. package/static/edgepress/fonts/noto-sans-tc-68-wght-normal.1a57eda03886b43c.woff2 +0 -0
  324. package/static/edgepress/fonts/noto-sans-tc-69-wght-normal.017fecc749bcd6d5.woff2 +0 -0
  325. package/static/edgepress/fonts/noto-sans-tc-7-wght-normal.cd0f0cac743dbbfe.woff2 +0 -0
  326. package/static/edgepress/fonts/noto-sans-tc-70-wght-normal.79fb9d70b9534ec7.woff2 +0 -0
  327. package/static/edgepress/fonts/noto-sans-tc-71-wght-normal.04a6ea581b5f1195.woff2 +0 -0
  328. package/static/edgepress/fonts/noto-sans-tc-72-wght-normal.d3a4eb35499157b7.woff2 +0 -0
  329. package/static/edgepress/fonts/noto-sans-tc-73-wght-normal.d81f8484404ee465.woff2 +0 -0
  330. package/static/edgepress/fonts/noto-sans-tc-74-wght-normal.4a5da9e0c2702f7d.woff2 +0 -0
  331. package/static/edgepress/fonts/noto-sans-tc-75-wght-normal.8b87f9c6c164513a.woff2 +0 -0
  332. package/static/edgepress/fonts/noto-sans-tc-76-wght-normal.a71463dbfd9fa7b5.woff2 +0 -0
  333. package/static/edgepress/fonts/noto-sans-tc-77-wght-normal.d094414ea752cd52.woff2 +0 -0
  334. package/static/edgepress/fonts/noto-sans-tc-78-wght-normal.04f7c97bd9cbc73b.woff2 +0 -0
  335. package/static/edgepress/fonts/noto-sans-tc-79-wght-normal.1ed089d037a02049.woff2 +0 -0
  336. package/static/edgepress/fonts/noto-sans-tc-8-wght-normal.d052677dfb5db5bd.woff2 +0 -0
  337. package/static/edgepress/fonts/noto-sans-tc-80-wght-normal.6d41ca59cfd50442.woff2 +0 -0
  338. package/static/edgepress/fonts/noto-sans-tc-81-wght-normal.f09bd0afa8a58114.woff2 +0 -0
  339. package/static/edgepress/fonts/noto-sans-tc-82-wght-normal.7920841c526b1b02.woff2 +0 -0
  340. package/static/edgepress/fonts/noto-sans-tc-83-wght-normal.0c6f7f82db8da0d8.woff2 +0 -0
  341. package/static/edgepress/fonts/noto-sans-tc-84-wght-normal.f37c0a853b31265e.woff2 +0 -0
  342. package/static/edgepress/fonts/noto-sans-tc-85-wght-normal.4173bff087653b82.woff2 +0 -0
  343. package/static/edgepress/fonts/noto-sans-tc-86-wght-normal.dac35605ebec15c4.woff2 +0 -0
  344. package/static/edgepress/fonts/noto-sans-tc-87-wght-normal.8ac85fdd0b15a6bd.woff2 +0 -0
  345. package/static/edgepress/fonts/noto-sans-tc-88-wght-normal.61625b0b21e8407f.woff2 +0 -0
  346. package/static/edgepress/fonts/noto-sans-tc-89-wght-normal.a1da3dccbf00698e.woff2 +0 -0
  347. package/static/edgepress/fonts/noto-sans-tc-90-wght-normal.bf08f0355222a782.woff2 +0 -0
  348. package/static/edgepress/fonts/noto-sans-tc-91-wght-normal.2ecf295bba2bfaa3.woff2 +0 -0
  349. package/static/edgepress/fonts/noto-sans-tc-92-wght-normal.36508326bb68115f.woff2 +0 -0
  350. package/static/edgepress/fonts/noto-sans-tc-97-wght-normal.c712899e0fc9e947.woff2 +0 -0
  351. package/static/edgepress/fonts/noto-sans-tc-98-wght-normal.13a648e521d18d7f.woff2 +0 -0
  352. package/static/edgepress/fonts/noto-sans-tc-99-wght-normal.7f8c9a777e64159a.woff2 +0 -0
  353. package/static/edgepress/fonts/noto-sans-tc-OFL.txt +93 -0
  354. package/static/edgepress/fonts/noto-sans-tc-cyrillic-wght-normal.dd0980ced8e46cb3.woff2 +0 -0
  355. package/static/edgepress/fonts/noto-sans-tc-latin-ext-wght-normal.817d96335dc52ce6.woff2 +0 -0
  356. package/static/edgepress/fonts/noto-sans-tc-latin-wght-normal.50cb5fb0a522cfaf.woff2 +0 -0
  357. package/static/edgepress/fonts/noto-sans-tc-vietnamese-wght-normal.2a77fb7b70fa304d.woff2 +0 -0
  358. package/static/edgepress/fonts/noto-sans-vietnamese-wght-normal.791e3f213c155a5b.woff2 +0 -0
  359. package/static/edgepress/fonts/noto-serif-OFL.txt +93 -0
  360. package/static/edgepress/fonts/noto-serif-cyrillic-ext-wght-normal.7c7afb20d33e30ec.woff2 +0 -0
  361. package/static/edgepress/fonts/noto-serif-cyrillic-wght-normal.54e3bd0c4a002b0f.woff2 +0 -0
  362. package/static/edgepress/fonts/noto-serif-greek-ext-wght-normal.9b3f4c4ff3adefd2.woff2 +0 -0
  363. package/static/edgepress/fonts/noto-serif-greek-wght-normal.1252037b6f1d6e86.woff2 +0 -0
  364. package/static/edgepress/fonts/noto-serif-latin-ext-wght-normal.2654f8d1261a420f.woff2 +0 -0
  365. package/static/edgepress/fonts/noto-serif-latin-wght-normal.46281456234014ce.woff2 +0 -0
  366. package/static/edgepress/fonts/noto-serif-math-wght-normal.d4a6aba2cb4636b3.woff2 +0 -0
  367. package/static/edgepress/fonts/noto-serif-vietnamese-wght-normal.fb6a5ed734c2fbe3.woff2 +0 -0
  368. package/static/edgepress/fonts/provenance.json +1356 -0
  369. package/static/edgepress/fonts.css +14 -0
  370. package/static/edgepress/image-viewer.js +23 -0
  371. package/static/edgepress/images.css +4 -0
  372. package/static/edgepress/link-directory.css +14 -0
  373. package/static/edgepress/local-time.js +12 -0
  374. package/static/edgepress/media-viewer.css +1 -0
  375. package/static/edgepress/media-viewer.js +111 -0
  376. package/static/edgepress/navigation-select.js +14 -0
  377. package/static/edgepress/oembed.js +129 -0
  378. package/static/edgepress/plugins/backend/external-api.js +4 -0
  379. package/static/edgepress/plugins/backend/external-widget.js +13 -0
  380. package/static/edgepress/plugins/consent/choices.js +47 -0
  381. package/static/edgepress/plugins/consent/manager.js +56 -74
  382. package/static/edgepress/project-demo.js +93 -0
  383. package/static/edgepress/search.js +2 -2
  384. package/static/edgepress/services-consent.js +3 -0
  385. package/static/edgepress/services.js +15 -0
  386. package/static/edgepress/webmcp.js +86 -0
  387. package/tests/assets-runtime.test.mjs +91 -0
  388. package/tests/audit.test.mjs +142 -0
  389. package/tests/brand-regression.test.mjs +110 -0
  390. package/tests/consent-browser.test.mjs +132 -0
  391. package/tests/consent-config.test.mjs +85 -0
  392. package/tests/consent-csp.test.mjs +90 -0
  393. package/tests/data-saver-loading.test.mjs +158 -0
  394. package/tests/data-saver-network.test.mjs +99 -0
  395. package/tests/data-saver.test.mjs +294 -0
  396. package/tests/document-package.test.mjs +28 -0
  397. package/tests/document-viewer.test.mjs +152 -0
  398. package/tests/edge-issues.test.mjs +65 -0
  399. package/tests/embed-syntax.test.mjs +68 -0
  400. package/tests/external-links.test.mjs +198 -0
  401. package/tests/fixtures/recording.mp4 +0 -0
  402. package/tests/fonts.test.mjs +99 -0
  403. package/tests/image-dimensions.test.mjs +15 -0
  404. package/tests/image-viewer.test.mjs +36 -0
  405. package/tests/json-pages.test.mjs +66 -0
  406. package/tests/link-directory.test.mjs +51 -0
  407. package/tests/local-tool.test.mjs +34 -0
  408. package/tests/markdown-media.test.mjs +119 -0
  409. package/tests/media-block.test.mjs +102 -0
  410. package/tests/navigation.test.mjs +87 -0
  411. package/tests/oembed-layout.test.mjs +98 -0
  412. package/tests/oembed.test.mjs +96 -0
  413. package/tests/page-authoring.test.mjs +27 -0
  414. package/tests/page-formatting.test.mjs +37 -0
  415. package/tests/post-list.test.mjs +96 -0
  416. package/tests/project-demo.test.mjs +73 -0
  417. package/tests/publication-time.test.mjs +43 -0
  418. package/tests/release-version.test.mjs +28 -0
  419. package/tests/revisions.test.mjs +93 -0
  420. package/tests/rss.test.mjs +54 -0
  421. package/tests/services.test.mjs +35 -0
  422. package/tests/theme-layout.test.mjs +95 -0
  423. package/tests/video-metadata.test.mjs +28 -0
  424. package/tests/webmcp.test.mjs +121 -0
  425. package/tests/writing.test.mjs +101 -0
  426. package/themes/atelier/assets/style.css +144 -22
  427. package/themes/atelier/layouts/partials/head.html +8 -1
  428. package/themes/atelier/layouts/partials/header.html +3 -1
  429. package/themes/default/assets/data-saver-theme.css +2 -0
  430. package/themes/default/assets/style.css +139 -23
  431. package/themes/default/layouts/partials/head.html +9 -2
  432. package/themes/default/layouts/partials/header.html +3 -1
  433. package/themes/default/theme.json +1 -0
  434. package/themes/folio/README.md +7 -0
  435. package/themes/folio/assets/data-saver-theme.css +3 -0
  436. package/themes/folio/assets/folio/navigation.js +10 -0
  437. package/themes/folio/assets/style.css +516 -0
  438. package/themes/folio/layouts/layout.html +15 -0
  439. package/themes/folio/layouts/partials/footer.html +6 -0
  440. package/themes/folio/layouts/partials/head.html +30 -0
  441. package/themes/folio/layouts/partials/header.html +8 -0
  442. package/themes/folio/theme.json +7 -0
  443. package/themes/signal/assets/style.css +143 -21
  444. package/themes/signal/layouts/partials/head.html +8 -1
  445. package/themes/signal/layouts/partials/header.html +3 -1
  446. package/tools/README.md +14 -0
  447. package/tools/assets/format-pages.mjs +56 -0
  448. package/tools/assets/generate-brand-assets.py +160 -0
  449. package/tools/assets/sync-fonts.mjs +71 -0
  450. package/tools/assets/sync-icons.mjs +17 -0
  451. package/tools/assets/video-preview.mjs +15 -0
  452. package/tools/recordings/capture-document-demo.mjs +147 -0
  453. package/tools/recordings/demo-workbench.mjs +57 -0
  454. package/tools/recordings/desktop-recording.mjs +92 -0
  455. package/tools/recordings/provision-vm.sh +18 -0
  456. package/tools/recordings/start-vm-desktop.sh +28 -0
  457. package/tools/recordings/vm-desktop.mjs +124 -0
  458. package/tools/verification/README.md +44 -0
  459. package/tools/verification/data-saver-site.mjs +405 -0
  460. package/tools/verification/page-performance.mjs +76 -0
  461. package/tools/verification/verify-brand-assets.py +52 -0
  462. package/tools/verification/verify-brand.mjs +180 -0
  463. package/vercel.json +36 -0
  464. package/wrangler.jsonc +5 -7
  465. package/content/pages/home/en.md +0 -99
  466. package/content/pages/home/zh-cn.md +0 -99
  467. package/content/pages/plugin-development/en.md +0 -109
  468. package/content/pages/plugin-development/zh-cn.md +0 -109
  469. package/content/pages/privacy-policy/en.md +0 -93
  470. package/content/pages/privacy-policy/zh-cn.md +0 -93
  471. package/content/pages/project-introduction/en.md +0 -88
  472. package/content/pages/project-introduction/zh-cn.md +0 -88
  473. package/content/pages/quick-start/en.md +0 -153
  474. package/content/pages/quick-start/zh-cn.md +0 -153
  475. package/content/pages/theme-development/en.md +0 -132
  476. package/content/pages/theme-development/zh-cn.md +0 -132
  477. package/src/worker.js +0 -139
  478. package/static/edgepress/brand/edgepress-mark.svg +0 -7
  479. package/static/edgepress/brand/mascot-209.webp +0 -0
  480. package/static/edgepress/brand/mascot-418.png +0 -0
  481. package/static/edgepress/brand/mascot-418.webp +0 -0
  482. package/static/edgepress/brand/mascot.png +0 -0
  483. package/static/edgepress/brand/og-image.png +0 -0
  484. package/static/edgepress/brand/og-image.svg +0 -46
  485. package/static/edgepress/icons.svg +0 -24
@@ -0,0 +1,180 @@
1
+ import assert from 'node:assert/strict';
2
+ import { createServer } from 'node:http';
3
+ import { readFile, writeFile, readdir, mkdir, stat } from 'node:fs/promises';
4
+ import { resolve, relative, sep, extname } from 'node:path';
5
+ import { spawn } from 'node:child_process';
6
+ import { chromium } from 'playwright';
7
+
8
+ const site = resolve(process.argv[2] || process.cwd());
9
+ const output = resolve(site, 'dist');
10
+ const evidence = resolve(site, 'tools/reports/brand-verification');
11
+ await mkdir(evidence, { recursive: true });
12
+ const configFile = resolve(site, 'edgepress.config.mjs');
13
+ const siteFile = resolve(site, 'config.yml');
14
+ const originalConfig = await readFile(configFile, 'utf8');
15
+ const originalSite = await readFile(siteFile, 'utf8');
16
+ const configuredTheme = originalSite.match(/^theme:\s*(\S+)/m)?.[1];
17
+ const defaultLocale = originalConfig.match(/defaultLocale:\s*["']([^"']+)/)?.[1];
18
+ assert.ok(defaultLocale, 'Default language must be declared.');
19
+ const themes = (await readdir(resolve(site, 'themes'), { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
20
+ const locales = (await Promise.all((await readdir(resolve(site, 'content/pages/home'))).filter((name) => /\.(?:md|json)$/.test(name)).map(async (name) => {
21
+ const text = await readFile(resolve(site, 'content/pages/home', name), 'utf8');
22
+ return name.endsWith('.json') ? JSON.parse(text).lang : text.match(/^lang:\s*(\S+)/m)?.[1];
23
+ }))).filter(Boolean);
24
+ const cli = await stat(resolve(site, 'src/cli.js')).then(() => resolve(site, 'src/cli.js')).catch(() => resolve(site, 'node_modules/edgepress/src/cli.js'));
25
+ async function build() {
26
+ await new Promise((done, reject) => {
27
+ const child = spawn(process.execPath, [cli, 'build'], { cwd: site, windowsHide: true });
28
+ let text = '';
29
+ child.stdout.on('data', (chunk) => { text += chunk; });
30
+ child.stderr.on('data', (chunk) => { text += chunk; });
31
+ child.once('error', reject);
32
+ child.once('exit', (code) => code === 0 ? done() : reject(new Error(text)));
33
+ });
34
+ }
35
+ async function filesIn(directory) {
36
+ const entries = await readdir(directory, { withFileTypes: true });
37
+ return (await Promise.all(entries.map((entry) => entry.isDirectory() ? filesIn(resolve(directory, entry.name)) : resolve(directory, entry.name)))).flat();
38
+ }
39
+ const server = createServer(async (request, response) => {
40
+ try {
41
+ const pathname = decodeURIComponent(new URL(request.url, 'http://local.test').pathname);
42
+ let path = resolve(output, '.' + pathname);
43
+ const rel = relative(output, path);
44
+ if (rel === '..' || rel.startsWith('..' + sep)) throw new Error('Invalid path');
45
+ if ((await stat(path)).isDirectory()) path = resolve(path, 'index.html');
46
+ const types = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.webp': 'image/webp', '.svg': 'image/svg+xml', '.json': 'application/json', '.ico': 'image/x-icon' };
47
+ response.writeHead(200, { 'Content-Type': types[extname(path)] || 'application/octet-stream', 'Cache-Control': 'no-store' });
48
+ response.end(await readFile(path));
49
+ } catch { response.writeHead(404); response.end('Not found'); }
50
+ });
51
+ await new Promise((done) => server.listen(0, '127.0.0.1', done));
52
+ const base = 'http://127.0.0.1:' + server.address().port;
53
+ const browser = await chromium.launch({ executablePath: 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe', headless: true });
54
+ const results = [];
55
+ const failures = [];
56
+ try {
57
+ for (const theme of themes) {
58
+ if (configuredTheme) await writeFile(siteFile, originalSite.replace(/^theme:\s*\S+/m, 'theme: ' + theme));
59
+ else await writeFile(configFile, originalConfig.replace(/(theme:\s*["'])themes\/[^"']+(["'])/, '$1themes/' + theme + '$2'));
60
+ await build();
61
+ const htmlFiles = (await filesIn(output)).filter((file) => file.endsWith('.html'));
62
+ for (const file of htmlFiles) {
63
+ const html = await readFile(file, 'utf8');
64
+ assert.doesNotMatch(html, /<use\b|\/icons\.svg(?:#|["'])/i, 'Old sprite remains in ' + file);
65
+ for (const match of html.matchAll(/<img\b[^>]*\bsrc="(\/[^"]+)"/g)) {
66
+ const path = resolve(output, '.' + new URL(match[1], base).pathname);
67
+ await stat(path);
68
+ }
69
+ }
70
+ const article = await (async () => {
71
+ for (const file of htmlFiles) if ((await readFile(file, 'utf8')).includes('<article class="post">')) return '/' + relative(output, file).split(sep).join('/').replace(/index\.html$/, '');
72
+ return null;
73
+ })();
74
+ for (const scheme of ['light', 'dark']) {
75
+ for (const width of [390, 1440]) {
76
+ const context = await browser.newContext({ viewport: { width, height: 920 }, colorScheme: scheme });
77
+ await context.route('**/*', (route) => route.request().url().startsWith(base) ? route.continue() : route.abort());
78
+ const page = await context.newPage();
79
+ for (const locale of locales) {
80
+ const prefix = locale === defaultLocale ? '/' : '/' + locale + '/';
81
+ const paths = [prefix, prefix + 'archives/'];
82
+ if (await stat(resolve(output, '.' + prefix + 'archives/page/2/index.html')).then(() => true).catch(() => false)) paths.push(prefix + 'archives/page/2/');
83
+ if (await stat(resolve(output, '.' + prefix + 'edgepress/index.html')).then(() => true).catch(() => false)) paths.push(prefix + 'edgepress/');
84
+ for (const path of paths) {
85
+ await page.goto(base + path, { waitUntil: 'networkidle' });
86
+ const measured = await page.evaluate(async () => {
87
+ const images = [...document.querySelectorAll('img')];
88
+ for (const image of images) image.loading = 'eager';
89
+ await Promise.all(images.map((image) => image.decode().catch(() => {})));
90
+ const icons = [...document.querySelectorAll('.icon-library')];
91
+ return {
92
+ colorScheme: getComputedStyle(document.documentElement).colorScheme,
93
+ overflow: document.documentElement.scrollWidth > innerWidth + 1,
94
+ brokenImages: images.filter((image) => !image.complete || !image.naturalWidth).map((image) => image.src),
95
+ inlineSvg: document.querySelectorAll('svg,use').length,
96
+ iconSemantics: icons.every((image) => image.tagName === 'IMG' && image.alt === '' && image.getAttribute('aria-hidden') === 'true' && /\/edgepress\/icons\/[a-z-]+\.svg$/.test(image.src)),
97
+ rawLanguageCodes: [...document.querySelectorAll('.post-language')].some((element) => /^(?:en|zh-(?:CN|TW|SG))$/.test(element.textContent.trim())),
98
+ cardGap: document.querySelector('.post-list') ? parseFloat(getComputedStyle(document.querySelector('.post-list')).rowGap) : null,
99
+ cellGap: document.querySelector('.page-builder-cell') ? parseFloat(getComputedStyle(document.querySelector('.page-builder-cell')).rowGap) : null,
100
+ filter: icons.length ? getComputedStyle(icons[0]).filter : null,
101
+ h1: document.querySelectorAll('h1').length,
102
+ ogImage: document.querySelector('meta[property="og:image"]')?.content,
103
+ currentPage: document.querySelector('.pagination-current')?.getAttribute('aria-current')
104
+ };
105
+ });
106
+ assert.equal(measured.overflow, false, theme + ' ' + path + ' overflows at ' + width);
107
+ assert.deepEqual(measured.brokenImages, [], theme + ' ' + path + ' has broken images');
108
+ assert.equal(measured.inlineSvg, 0);
109
+ assert.equal(measured.iconSemantics, true);
110
+ assert.equal(measured.rawLanguageCodes, false);
111
+ assert.equal(measured.h1, 1);
112
+ assert.match(measured.ogImage || '', /^https:\/\/.+\.png$/);
113
+ if (measured.cardGap !== null) assert.ok(measured.cardGap >= 16);
114
+ if (measured.cellGap !== null) assert.ok(measured.cellGap >= 16);
115
+ if (measured.currentPage) assert.equal(measured.currentPage, 'page');
116
+ if (theme === 'signal' && measured.colorScheme === 'dark' || theme === 'lumen' && scheme === 'dark') assert.notEqual(measured.filter, 'none');
117
+ results.push({ theme, scheme, width, locale, path, ...measured });
118
+ if (path === prefix && locale === defaultLocale) {
119
+ await page.screenshot({ path: resolve(evidence, 'home-' + theme + '-' + width + '-' + scheme + '.png'), fullPage: true });
120
+ await page.keyboard.press('Tab');
121
+ assert.equal(await page.evaluate(() => document.activeElement.classList.contains('skip-link')), true, 'Skip link must be first keyboard target');
122
+ if (await page.locator('.privacy-settings-button').count()) {
123
+ if (await page.locator('.privacy-panel').isVisible()) await page.locator('.privacy-close').click();
124
+ await page.locator('.privacy-settings-button').click();
125
+ assert.equal(await page.locator('.privacy-settings-button').getAttribute('aria-expanded'), 'true');
126
+ assert.equal(await page.locator('.privacy-panel').isVisible(), true);
127
+ assert.equal(await page.locator('.privacy-panel svg').count(), 0);
128
+ const contrasts = await page.evaluate(() => {
129
+ const luminance = (color) => {
130
+ const values = color.match(/[\d.]+/g).slice(0, 3).map(Number).map((value) => {
131
+ value /= 255;
132
+ return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
133
+ });
134
+ return values[0] * 0.2126 + values[1] * 0.7152 + values[2] * 0.0722;
135
+ };
136
+ return [...document.querySelectorAll('.privacy-settings-button,.privacy-accept,.privacy-reject')].map((button) => {
137
+ const style = getComputedStyle(button);
138
+ const foreground = luminance(style.color);
139
+ const background = luminance(style.backgroundColor);
140
+ return (Math.max(foreground, background) + 0.05) / (Math.min(foreground, background) + 0.05);
141
+ });
142
+ });
143
+ assert.ok(contrasts.every((ratio) => ratio >= 4.5), theme + ' privacy button contrast: ' + contrasts.join(', '));
144
+ await page.locator('.privacy-close').click();
145
+ assert.equal(await page.locator('.privacy-panel').isVisible(), false);
146
+ await page.screenshot({ path: resolve(evidence, 'home-' + theme + '-' + width + '-' + scheme + '.png'), fullPage: true });
147
+ }
148
+ }
149
+ }
150
+ }
151
+ if (article) {
152
+ await page.goto(base + article, { waitUntil: 'networkidle' });
153
+ if (await page.locator('.privacy-panel').isVisible()) await page.locator('.privacy-close').click();
154
+ if (await page.locator('.post-toc-toggle').count()) {
155
+ await page.evaluate(() => window.scrollTo(0, 400));
156
+ await page.locator('.post-toc-toggle').waitFor({ state: 'visible' });
157
+ assert.equal(await page.locator('.post-toc-toggle img').count(), 1);
158
+ await page.locator('.post-toc-toggle').click();
159
+ assert.equal(await page.locator('.post-toc-panel').isVisible(), true);
160
+ await page.locator('.post-toc-close').click();
161
+ }
162
+ }
163
+ await context.close();
164
+ }
165
+ }
166
+ console.log(theme + ': image references, layouts, locales, light/dark, keyboard and library icon controls passed.');
167
+ }
168
+ } catch (error) {
169
+ failures.push(error.message);
170
+ process.exitCode = 1;
171
+ console.error(error.stack);
172
+ } finally {
173
+ await writeFile(configFile, originalConfig);
174
+ await writeFile(siteFile, originalSite);
175
+ await build();
176
+ await browser.close();
177
+ await new Promise((done) => server.close(done));
178
+ await writeFile(resolve(evidence, 'browser-report.json'), JSON.stringify({ status: failures.length ? 'fail' : 'pass', cases: results.length, results, failures,
179
+ limitations: ['Browser measurements use desktop Edge and simulated viewports/color schemes. Physical devices and assistive screen readers were not tested.'] }, null, 2) + '\n');
180
+ }
package/vercel.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://openapi.vercel.sh/vercel.json",
3
+ "framework": null,
4
+ "installCommand": "npm ci",
5
+ "buildCommand": "npm run build",
6
+ "outputDirectory": "dist",
7
+ "trailingSlash": true,
8
+ "headers": [
9
+ {
10
+ "source": "/(.*)",
11
+ "headers": [
12
+ {
13
+ "key": "Cache-Control",
14
+ "value": "public, max-age=0, must-revalidate"
15
+ },
16
+ {
17
+ "key": "X-Content-Type-Options",
18
+ "value": "nosniff"
19
+ },
20
+ {
21
+ "key": "Referrer-Policy",
22
+ "value": "strict-origin-when-cross-origin"
23
+ }
24
+ ]
25
+ },
26
+ {
27
+ "source": "/:asset(.*\\.[a-f0-9]{16}\\..*)",
28
+ "headers": [
29
+ {
30
+ "key": "Cache-Control",
31
+ "value": "public, max-age=31536000, immutable"
32
+ }
33
+ ]
34
+ }
35
+ ]
36
+ }
package/wrangler.jsonc CHANGED
@@ -1,13 +1,11 @@
1
- // For the optional URL/token backend, add vars.BACKEND_URL (for example, https://api.example.org) and run `edgepress secret put BACKEND_TOKEN`.
2
1
  {
3
- "$schema": "./node_modules/wrangler/config-schema.json",
4
2
  "name": "edgepress",
5
- "main": "./src/worker.js",
6
- "compatibility_date": "2026-09-01",
3
+ "compatibility_date": "2026-10-03",
7
4
  "assets": {
8
5
  "directory": "./dist",
9
- "binding": "ASSETS",
10
6
  "html_handling": "auto-trailing-slash",
11
- "run_worker_first": ["/api/*"]
12
- }
7
+ "not_found_handling": "404-page"
8
+ },
9
+ "workers_dev": false,
10
+ "preview_urls": false
13
11
  }
@@ -1,99 +0,0 @@
1
- ---
2
- title: EdgePress website builder
3
- lang: en
4
- slug: home
5
- homepage: true
6
- description: Build fast, secure websites on Cloudflare Workers with Markdown posts and editable page elements.
7
- keywords:
8
- - website builder
9
- - Cloudflare Workers
10
- - page elements
11
- - website plugins
12
- blocks:
13
- - columns: 1
14
- cells:
15
- -
16
- - type: hero
17
- eyebrow: A project built for the edge
18
- title: Build a fast website from clear, editable source files.
19
- text: EdgePress turns Markdown posts, page layouts, themes, and consent-aware integrations into a static site served by Cloudflare Workers.
20
- mascotSrc: /edgepress/brand/mascot-418.png
21
- mascotAlt: A curious sparrow folded from layered paper pages, EdgePress's project mascot.
22
- mascotWidth: 418
23
- mascotHeight: 440
24
- mascotSizes: 209px
25
- mascotWebpSrcset:
26
- - src: /edgepress/brand/mascot-209.webp
27
- width: 209
28
- - src: /edgepress/brand/mascot-418.webp
29
- width: 418
30
- cta:
31
- label: Start with the quick guide
32
- url: /quick-start/
33
- highlights:
34
- - Editable pages in localized Markdown front matter
35
- - Articles written in Markdown
36
- - Shared layouts and theme-defined palettes
37
- - columns: 1
38
- cells:
39
- -
40
- - type: feature-grid
41
- title: A clear place for each part of a site
42
- items:
43
- - title: Pages use editable elements
44
- icon: layout-grid
45
- text: Set the number of columns first, then choose elements and edit their content in each page file.
46
- url: /theme-development/
47
- - title: Posts use Markdown
48
- icon: file-text
49
- text: Write long-form articles in Markdown and let the built-in renderer sanitize and format them.
50
- url: /project-introduction/
51
- - title: Themes own the design
52
- icon: palette
53
- text: Build layouts with HTML partials and keep each theme's color palette in its stylesheet.
54
- url: /theme-development/
55
- - title: Integrations wait for consent
56
- icon: shield-check
57
- text: Add tracking, statistics, advertising, or CAPTCHA services with provider details in configuration.
58
- url: /plugin-development/
59
- - columns: 2
60
- cells:
61
- -
62
- - type: section
63
- title: Learn the project
64
- tone: soft
65
- blocks:
66
- - type: text
67
- paragraphs:
68
- - Start with the project introduction and quick start. The guides cover the feature lifecycle, supported runtimes, and the recommended publishing workflow.
69
- - type: link-list
70
- title: Project guides
71
- items:
72
- - label: Project introduction
73
- url: /project-introduction/
74
- - label: Quick start
75
- url: /quick-start/
76
- -
77
- - type: section
78
- title: Extend the project
79
- tone: soft
80
- blocks:
81
- - type: text
82
- paragraphs:
83
- - Use theme partials for shared head metadata, navigation, footer, and error-page layout. Page-specific rows and element content stay in localized page files.
84
- - Build plugins run in Node. Browser services wait for explicit consent before requesting vendor code.
85
- - type: link-list
86
- title: Development guides
87
- items:
88
- - label: Theme development
89
- url: /theme-development/
90
- - label: Plugin development
91
- url: /plugin-development/
92
- - columns: 1
93
- cells:
94
- -
95
- - type: latest-posts
96
- title: Latest project updates
97
- count: 3
98
- paginate: true
99
- ---
@@ -1,99 +0,0 @@
1
- ---
2
- title: EdgePress 网站构建器
3
- lang: zh-CN
4
- slug: home
5
- homepage: true
6
- description: 使用 Markdown 文章和可编辑页面元素,在 Cloudflare Workers 上构建快速、安全的网站。
7
- keywords:
8
- - 网站构建器
9
- - Cloudflare Workers
10
- - 页面元素
11
- - 网站插件
12
- blocks:
13
- - columns: 1
14
- cells:
15
- -
16
- - type: hero
17
- eyebrow: 为边缘网络构建
18
- title: 用清晰、可编辑的源文件构建快速网站。
19
- text: EdgePress 将 Markdown 文章、页面布局、主题和遵循同意状态的集成构建为由 Cloudflare Workers 提供服务的静态网站。
20
- mascotSrc: /edgepress/brand/mascot-418.png
21
- mascotAlt: EdgePress 吉祥物,一只由层叠折页构成、神情好奇的小鸟。
22
- mascotWidth: 418
23
- mascotHeight: 440
24
- mascotSizes: 209px
25
- mascotWebpSrcset:
26
- - src: /edgepress/brand/mascot-209.webp
27
- width: 209
28
- - src: /edgepress/brand/mascot-418.webp
29
- width: 418
30
- cta:
31
- label: 从快速入门开始
32
- url: /[launge]/quick-start/
33
- highlights:
34
- - 在本地化 Markdown 前置数据中编辑页面
35
- - 使用 Markdown 撰写文章
36
- - 复用布局,配色由主题定义
37
- - columns: 1
38
- cells:
39
- -
40
- - type: feature-grid
41
- title: 网站各部分各有清晰位置
42
- items:
43
- - title: 页面使用可编辑元素
44
- icon: layout-grid
45
- text: 先设置栏数,再选择元素类型,并在各页面文件中编辑对应内容。
46
- url: /[launge]/theme-development/
47
- - title: 文章使用 Markdown
48
- icon: file-text
49
- text: 使用 Markdown 撰写长文,由内置渲染器进行清理和排版。
50
- url: /[launge]/project-introduction/
51
- - title: 主题负责视觉设计
52
- icon: palette
53
- text: 使用 HTML 局部模板构建布局,并将各主题的配色保留在样式表中。
54
- url: /[launge]/theme-development/
55
- - title: 集成遵循用户同意
56
- icon: shield-check
57
- text: 配置跟踪、统计、广告或人机验证服务及其供应商信息。
58
- url: /[launge]/plugin-development/
59
- - columns: 2
60
- cells:
61
- -
62
- - type: section
63
- title: 了解项目
64
- tone: soft
65
- blocks:
66
- - type: text
67
- paragraphs:
68
- - 从项目介绍和快速入门开始。指南涵盖功能生命周期、支持的运行环境和建议的发布流程。
69
- - type: link-list
70
- title: 项目指南
71
- items:
72
- - label: 项目介绍
73
- url: /[launge]/project-introduction/
74
- - label: 快速入门
75
- url: /[launge]/quick-start/
76
- -
77
- - type: section
78
- title: 扩展项目
79
- tone: soft
80
- blocks:
81
- - type: text
82
- paragraphs:
83
- - 使用主题局部模板复用 head 元数据、导航、页脚和错误页布局。每个页面的行布局和元素内容保存在本地化页面文件中。
84
- - 构建插件在 Node 中运行。浏览器服务会等到访客明确同意后才请求供应商代码。
85
- - type: link-list
86
- title: 开发指南
87
- items:
88
- - label: 主题开发
89
- url: /[launge]/theme-development/
90
- - label: 插件开发
91
- url: /[launge]/plugin-development/
92
- - columns: 1
93
- cells:
94
- -
95
- - type: latest-posts
96
- title: 最新项目动态
97
- count: 3
98
- paginate: true
99
- ---
@@ -1,109 +0,0 @@
1
- ---
2
- title: Plugin development
3
- lang: en
4
- slug: plugin-development
5
- description: Extend EdgePress with trusted build plugins, consent-gated browser integrations, and private backend forwarding.
6
- blocks:
7
- - columns: 1
8
- cells:
9
- -
10
- - type: hero
11
- eyebrow: Extension guide
12
- title: Connect trusted build code, browser providers, and backend services.
13
- text: Build-time plugins run in Node. Browser integrations wait for visitor consent. Runtime backend requests stay behind the Worker.
14
- - columns: 2
15
- cells:
16
- -
17
- - type: section
18
- title: Build-time plugins
19
- tone: soft
20
- blocks:
21
- - type: text
22
- paragraphs:
23
- - Local JavaScript modules in plugins/ may transform source documents, register hooks or renderers, and generate routes. They execute with the build process permissions, so only load code reviewed by the project owner.
24
- - The starter consent plugin adds a privacy panel after the shared page layout. Build plugins are trusted code and are not deployed as browser scripts.
25
- - type: code
26
- title: Register a route generator
27
- language: js
28
- code: |
29
- export default function projectMeta(api) {
30
- api.registerGenerator('project-meta', ({ site }) => ({
31
- path: 'project-meta.json',
32
- body: JSON.stringify({
33
- title: site.config.site.title,
34
- posts: site.posts.length
35
- }),
36
- contentType: 'application/json; charset=utf-8'
37
- }));
38
- }
39
- -
40
- - type: section
41
- title: Tracking, statistics, advertising, and CAPTCHA
42
- tone: soft
43
- blocks:
44
- - type: text
45
- paragraphs:
46
- - Add browser providers in config.yml under plugins.consent.tracking, plugins.consent.statistics, plugins.consent.advertising, or plugins.consent.captcha. Each service needs a unique ID, provider, purpose, dataCategories, recipient, retention, a privacyUrl pointing to the provider's current privacy information, and its public site identifier. privacyUrl must be an HTTPS URL. Localized fields are maps keyed by every enabled locale, such as en and zh-CN.
47
- - Available providers include Google Tag Manager and Meta Pixel; Cloudflare Web Analytics, Google Analytics, and Baidu Tongji; Google AdSense; and Cloudflare Turnstile, Google reCAPTCHA, and hCaptcha.
48
- - Set plugins.consent.proposedDate to the YYYY-MM-DD date the notice was drafted. Set effectiveDate when the notice takes effect; omit it while no effective date has been set. Updating either date makes saved consent stale, so visitors are asked again.
49
- - Provider code is imported only after the visitor accepts that integration. The first layer offers accept and reject together; service data and individual switches are available in its details. Keep CAPTCHA secret keys out of config.yml and client code. The server must submit each CAPTCHA response to the provider verification API before accepting a form.
50
- - type: notice
51
- title: Operator details
52
- text: Fill privacy.controller.name, privacy.controller.contact, and privacy.policyUrl before configuring any browser provider. The build rejects provider services when the controller name or contact is blank.
53
- tone: warning
54
- - columns: 1
55
- cells:
56
- -
57
- - type: section
58
- title: Forward requests to a URL and token backend
59
- blocks:
60
- - type: text
61
- paragraphs:
62
- - The Worker exposes /api/v1/* for GET, POST, PUT, PATCH, and DELETE. With BACKEND_URL and BACKEND_TOKEN configured, it appends the request path and query to the backend URL, sends a Bearer token, forwards a small allowlist of request headers, and marks responses no-store.
63
- - BACKEND_URL must use HTTPS. Plain HTTP is accepted only for localhost development. Request bodies are limited to 1 MB. The backend still needs to authenticate the scoped token, validate user input, authorize every action, and enforce rate and size limits.
64
- - Prefer a Cloudflare BACKEND Service Binding for private Worker-to-Worker calls. A browser must never receive BACKEND_TOKEN. This generic proxy does not create application accounts, forms, or data models by itself.
65
- - type: steps
66
- items:
67
- - title: Set the backend base URL
68
- text: Add vars.BACKEND_URL in wrangler.jsonc. Use an HTTPS base URL without credentials or a token in its URL.
69
- - title: Store the secret
70
- text: Wrangler prompts for the secret value. Use a narrowly scoped backend token and rotate it under the operator's process.
71
- command: edgepress secret put BACKEND_TOKEN
72
- - title: Configure and deploy
73
- text: Confirm the backend accepts the forwarded path and Bearer token, then deploy the Worker.
74
- command: edgepress deploy
75
- - type: code
76
- title: Local development
77
- language: sh
78
- code: |
79
- # .dev.vars is local and must not be committed
80
- BACKEND_URL=http://127.0.0.1:8788
81
- BACKEND_TOKEN=replace-with-a-local-token
82
- - type: text
83
- text: The Worker returns 503 when no backend binding or URL and token are configured. It rejects unsupported methods and oversized request bodies.
84
- - type: link-list
85
- title: Provider validation reference
86
- items:
87
- - label: Cloudflare Turnstile Siteverify
88
- url: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
89
- - label: EDPB consent summary
90
- url: https://www.edpb.europa.eu/system/files/2026-04/edpb-summary-consent_en.pdf
91
- - columns: 1
92
- cells:
93
- -
94
- - type: section
95
- title: Content security and maintenance
96
- blocks:
97
- - type: text
98
- paragraphs:
99
- - The built-in Markdown renderer sanitizes post output. Custom renderers, plugins, themes, and files in static/ are trusted project code; review changes before building or deploying. Do not put secrets in posts, themes, or public assets.
100
- - edgepress iterate writes a report-only review plan under .edgepress/reports/. It does not edit source files, update dependencies, or deploy. edgepress security checks dependency advisories. Review the lockfile and generated page report before deployment, and keep a known-good deployment for rollback.
101
- - type: code
102
- title: Review project changes
103
- language: sh
104
- code: |
105
- edgepress security
106
- edgepress iterate
107
- edgepress check
108
- edgepress server
109
- ---
@@ -1,109 +0,0 @@
1
- ---
2
- title: 插件开发
3
- lang: zh-CN
4
- slug: plugin-development
5
- description: 使用可信构建插件、遵循同意设置的浏览器集成和私有后端转发扩展 EdgePress。
6
- blocks:
7
- - columns: 1
8
- cells:
9
- -
10
- - type: hero
11
- eyebrow: 扩展指南
12
- title: 连接可信构建代码、浏览器供应商和后端服务。
13
- text: 构建插件在 Node 中运行。浏览器集成需等待访客同意。运行时后端请求由 Worker 转发。
14
- - columns: 2
15
- cells:
16
- -
17
- - type: section
18
- title: 构建插件
19
- tone: soft
20
- blocks:
21
- - type: text
22
- paragraphs:
23
- - plugins/ 中的本地 JavaScript 模块可以转换源文档、注册钩子或渲染器,并生成路由。它们拥有构建进程的权限,因此只应加载项目负责人审查过的代码。
24
- - 起始同意插件会在共享页面布局后添加隐私面板。构建插件属于可信代码,不会作为浏览器脚本发布。
25
- - type: code
26
- title: 注册路由生成器
27
- language: js
28
- code: |
29
- export default function projectMeta(api) {
30
- api.registerGenerator('project-meta', ({ site }) => ({
31
- path: 'project-meta.json',
32
- body: JSON.stringify({
33
- title: site.config.site.title,
34
- posts: site.posts.length
35
- }),
36
- contentType: 'application/json; charset=utf-8'
37
- }));
38
- }
39
- -
40
- - type: section
41
- title: 跟踪、统计、广告和人机验证
42
- tone: soft
43
- blocks:
44
- - type: text
45
- paragraphs:
46
- - 在 config.yml 的 plugins.consent.tracking、plugins.consent.statistics、plugins.consent.advertising 或 plugins.consent.captcha 下添加浏览器供应商。每项需要唯一 ID、provider、purpose、dataCategories、recipient、retention、指向供应商当前隐私说明的 privacyUrl,以及公开站点标识。privacyUrl 必须是 HTTPS 地址。多语言字段需使用语言映射,覆盖已启用的每种语言,例如 en 与 zh-CN。
47
- - 可用供应商包括 Google Tag Manager 和 Meta Pixel;Cloudflare Web Analytics、Google Analytics 和百度统计;Google AdSense;以及 Cloudflare Turnstile、Google reCAPTCHA 和 hCaptcha。
48
- - plugins.consent.proposedDate 必须设置为 YYYY-MM-DD 格式的告知拟定日期。告知生效后再设置 effectiveDate;尚未生效时可以省略。修改任一日期都会使旧的同意记录失效,并重新询问访客。
49
- - 只有访客接受对应集成后才会导入供应商代码。首层同时提供接受和拒绝,数据详情与逐项开关位于展开内容中。不要把 CAPTCHA 密钥放入 config.yml 或客户端代码。服务器必须在接受表单前将每个 CAPTCHA 响应提交给供应商验证 API。
50
- - type: notice
51
- title: 运营者信息
52
- text: 配置浏览器供应商前,请填写 privacy.controller.name、privacy.controller.contact 和 privacy.policyUrl。控制者名称或联系方式为空时,构建器会拒绝启用供应商服务。
53
- tone: warning
54
- - columns: 1
55
- cells:
56
- -
57
- - type: section
58
- title: 通过 URL 和令牌转发后端请求
59
- blocks:
60
- - type: text
61
- paragraphs:
62
- - Worker 为 GET、POST、PUT、PATCH 和 DELETE 提供 /api/v1/* 接口。配置 BACKEND_URL 和 BACKEND_TOKEN 后,Worker 会将请求路径及查询参数附加到后端 URL,发送 Bearer 令牌,转发有限的请求头,并为响应设置 no-store。
63
- - BACKEND_URL 必须使用 HTTPS。只有 localhost 开发环境允许普通 HTTP。请求正文最大为 1 MB。后端仍需验证受限令牌、校验用户输入、授权每项操作,并执行自己的请求频率和大小限制。
64
- - 私有 Worker 间调用优先使用 Cloudflare BACKEND Service Binding。浏览器绝不能获得 BACKEND_TOKEN。此通用代理本身不会创建应用账户、表单或数据模型。
65
- - type: steps
66
- items:
67
- - title: 设置后端基础 URL
68
- text: 在 wrangler.jsonc 中添加 vars.BACKEND_URL。基础 URL 应使用 HTTPS,且不能包含凭据或令牌。
69
- - title: 保存密钥
70
- text: Wrangler 会提示输入密钥值。请使用权限范围有限的后端令牌,并依照运营者流程定期轮换。
71
- command: edgepress secret put BACKEND_TOKEN
72
- - title: 配置并部署
73
- text: 确认后端接受转发路径和 Bearer 令牌,再部署 Worker。
74
- command: edgepress deploy
75
- - type: code
76
- title: 本地开发
77
- language: sh
78
- code: |
79
- # .dev.vars 仅供本地使用,不要提交到版本库
80
- BACKEND_URL=http://127.0.0.1:8788
81
- BACKEND_TOKEN=replace-with-a-local-token
82
- - type: text
83
- text: 未配置后端绑定,或 URL 和令牌时,Worker 会返回 503。它会拒绝不支持的方法和过大的请求正文。
84
- - type: link-list
85
- title: 供应商验证参考
86
- items:
87
- - label: Cloudflare Turnstile Siteverify
88
- url: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
89
- - label: EDPB 同意摘要
90
- url: https://www.edpb.europa.eu/system/files/2026-04/edpb-summary-consent_en.pdf
91
- - columns: 1
92
- cells:
93
- -
94
- - type: section
95
- title: 内容安全与维护
96
- blocks:
97
- - type: text
98
- paragraphs:
99
- - 内置 Markdown 渲染器会清理文章输出。自定义渲染器、插件、主题和 static/ 中的文件都属于可信项目代码;构建或部署前应审查变更。不要将密钥放在文章、主题或公开资源中。
100
- - edgepress iterate 会在 .edgepress/reports/ 下生成只读维护计划,不会修改源文件、更新依赖或部署。edgepress security 检查依赖安全公告。部署前请审查锁文件和页面报告,并保留可用于回滚的已知稳定部署。
101
- - type: code
102
- title: 审查项目变更
103
- language: sh
104
- code: |
105
- edgepress security
106
- edgepress iterate
107
- edgepress check
108
- edgepress server
109
- ---