@we8/cms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +78 -0
  2. package/dist/auth/auth.d.ts +87 -0
  3. package/dist/auth/auth.d.ts.map +1 -0
  4. package/dist/auth/auth.js +90 -0
  5. package/dist/auth/auth.js.map +1 -0
  6. package/dist/auth/routes.admin.d.ts +22 -0
  7. package/dist/auth/routes.admin.d.ts.map +1 -0
  8. package/dist/auth/routes.admin.js +106 -0
  9. package/dist/auth/routes.admin.js.map +1 -0
  10. package/dist/auth/schema.d.ts +1395 -0
  11. package/dist/auth/schema.d.ts.map +1 -0
  12. package/dist/auth/schema.js +76 -0
  13. package/dist/auth/schema.js.map +1 -0
  14. package/dist/env.d.ts +64 -0
  15. package/dist/env.d.ts.map +1 -0
  16. package/dist/env.js +48 -0
  17. package/dist/env.js.map +1 -0
  18. package/dist/index.d.ts +22 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +170 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/keys/middleware.d.ts +54 -0
  23. package/dist/keys/middleware.d.ts.map +1 -0
  24. package/dist/keys/middleware.js +84 -0
  25. package/dist/keys/middleware.js.map +1 -0
  26. package/dist/keys/routes.admin.d.ts +16 -0
  27. package/dist/keys/routes.admin.d.ts.map +1 -0
  28. package/dist/keys/routes.admin.js +34 -0
  29. package/dist/keys/routes.admin.js.map +1 -0
  30. package/dist/keys/service.d.ts +36 -0
  31. package/dist/keys/service.d.ts.map +1 -0
  32. package/dist/keys/service.js +120 -0
  33. package/dist/keys/service.js.map +1 -0
  34. package/dist/lib/bots.d.ts +24 -0
  35. package/dist/lib/bots.d.ts.map +1 -0
  36. package/dist/lib/bots.js +79 -0
  37. package/dist/lib/bots.js.map +1 -0
  38. package/dist/lib/email.d.ts +62 -0
  39. package/dist/lib/email.d.ts.map +1 -0
  40. package/dist/lib/email.js +64 -0
  41. package/dist/lib/email.js.map +1 -0
  42. package/dist/lib/errors.d.ts +12 -0
  43. package/dist/lib/errors.d.ts.map +1 -0
  44. package/dist/lib/errors.js +23 -0
  45. package/dist/lib/errors.js.map +1 -0
  46. package/dist/lib/notify.d.ts +20 -0
  47. package/dist/lib/notify.d.ts.map +1 -0
  48. package/dist/lib/notify.js +60 -0
  49. package/dist/lib/notify.js.map +1 -0
  50. package/dist/lib/retention.d.ts +15 -0
  51. package/dist/lib/retention.d.ts.map +1 -0
  52. package/dist/lib/retention.js +40 -0
  53. package/dist/lib/retention.js.map +1 -0
  54. package/dist/lib/sanitize.d.ts +14 -0
  55. package/dist/lib/sanitize.d.ts.map +1 -0
  56. package/dist/lib/sanitize.js +145 -0
  57. package/dist/lib/sanitize.js.map +1 -0
  58. package/dist/lib/turnstile.d.ts +7 -0
  59. package/dist/lib/turnstile.d.ts.map +1 -0
  60. package/dist/lib/turnstile.js +32 -0
  61. package/dist/lib/turnstile.js.map +1 -0
  62. package/dist/lib/validate.d.ts +11 -0
  63. package/dist/lib/validate.d.ts.map +1 -0
  64. package/dist/lib/validate.js +22 -0
  65. package/dist/lib/validate.js.map +1 -0
  66. package/dist/lib/word-count.d.ts +7 -0
  67. package/dist/lib/word-count.d.ts.map +1 -0
  68. package/dist/lib/word-count.js +14 -0
  69. package/dist/lib/word-count.js.map +1 -0
  70. package/dist/middleware/auth.d.ts +46 -0
  71. package/dist/middleware/auth.d.ts.map +1 -0
  72. package/dist/middleware/auth.js +60 -0
  73. package/dist/middleware/auth.js.map +1 -0
  74. package/dist/middleware/cors.d.ts +33 -0
  75. package/dist/middleware/cors.d.ts.map +1 -0
  76. package/dist/middleware/cors.js +55 -0
  77. package/dist/middleware/cors.js.map +1 -0
  78. package/dist/middleware/module-gate.d.ts +11 -0
  79. package/dist/middleware/module-gate.d.ts.map +1 -0
  80. package/dist/middleware/module-gate.js +18 -0
  81. package/dist/middleware/module-gate.js.map +1 -0
  82. package/dist/middleware/rate-limit.d.ts +13 -0
  83. package/dist/middleware/rate-limit.d.ts.map +1 -0
  84. package/dist/middleware/rate-limit.js +30 -0
  85. package/dist/middleware/rate-limit.js.map +1 -0
  86. package/dist/middleware/request-id.d.ts +8 -0
  87. package/dist/middleware/request-id.d.ts.map +1 -0
  88. package/dist/middleware/request-id.js +11 -0
  89. package/dist/middleware/request-id.js.map +1 -0
  90. package/dist/modules/aeo/checks.d.ts +41 -0
  91. package/dist/modules/aeo/checks.d.ts.map +1 -0
  92. package/dist/modules/aeo/checks.js +127 -0
  93. package/dist/modules/aeo/checks.js.map +1 -0
  94. package/dist/modules/aeo/llms.d.ts +24 -0
  95. package/dist/modules/aeo/llms.d.ts.map +1 -0
  96. package/dist/modules/aeo/llms.js +92 -0
  97. package/dist/modules/aeo/llms.js.map +1 -0
  98. package/dist/modules/aeo/routes.admin.d.ts +12 -0
  99. package/dist/modules/aeo/routes.admin.d.ts.map +1 -0
  100. package/dist/modules/aeo/routes.admin.js +37 -0
  101. package/dist/modules/aeo/routes.admin.js.map +1 -0
  102. package/dist/modules/analytics/db.d.ts +24 -0
  103. package/dist/modules/analytics/db.d.ts.map +1 -0
  104. package/dist/modules/analytics/db.js +24 -0
  105. package/dist/modules/analytics/db.js.map +1 -0
  106. package/dist/modules/analytics/routes.admin.d.ts +6 -0
  107. package/dist/modules/analytics/routes.admin.d.ts.map +1 -0
  108. package/dist/modules/analytics/routes.admin.js +21 -0
  109. package/dist/modules/analytics/routes.admin.js.map +1 -0
  110. package/dist/modules/analytics/routes.public.d.ts +6 -0
  111. package/dist/modules/analytics/routes.public.d.ts.map +1 -0
  112. package/dist/modules/analytics/routes.public.js +33 -0
  113. package/dist/modules/analytics/routes.public.js.map +1 -0
  114. package/dist/modules/analytics/service.d.ts +16 -0
  115. package/dist/modules/analytics/service.d.ts.map +1 -0
  116. package/dist/modules/analytics/service.js +148 -0
  117. package/dist/modules/analytics/service.js.map +1 -0
  118. package/dist/modules/authors/routes.admin.d.ts +6 -0
  119. package/dist/modules/authors/routes.admin.d.ts.map +1 -0
  120. package/dist/modules/authors/routes.admin.js +29 -0
  121. package/dist/modules/authors/routes.admin.js.map +1 -0
  122. package/dist/modules/authors/routes.public.d.ts +6 -0
  123. package/dist/modules/authors/routes.public.d.ts.map +1 -0
  124. package/dist/modules/authors/routes.public.js +11 -0
  125. package/dist/modules/authors/routes.public.js.map +1 -0
  126. package/dist/modules/authors/service.d.ts +26 -0
  127. package/dist/modules/authors/service.d.ts.map +1 -0
  128. package/dist/modules/authors/service.js +116 -0
  129. package/dist/modules/authors/service.js.map +1 -0
  130. package/dist/modules/content/db.d.ts +127 -0
  131. package/dist/modules/content/db.d.ts.map +1 -0
  132. package/dist/modules/content/db.js +203 -0
  133. package/dist/modules/content/db.js.map +1 -0
  134. package/dist/modules/content/publish.d.ts +38 -0
  135. package/dist/modules/content/publish.d.ts.map +1 -0
  136. package/dist/modules/content/publish.js +184 -0
  137. package/dist/modules/content/publish.js.map +1 -0
  138. package/dist/modules/content/routes.admin.d.ts +8 -0
  139. package/dist/modules/content/routes.admin.d.ts.map +1 -0
  140. package/dist/modules/content/routes.admin.js +122 -0
  141. package/dist/modules/content/routes.admin.js.map +1 -0
  142. package/dist/modules/content/routes.public.d.ts +6 -0
  143. package/dist/modules/content/routes.public.d.ts.map +1 -0
  144. package/dist/modules/content/routes.public.js +56 -0
  145. package/dist/modules/content/routes.public.js.map +1 -0
  146. package/dist/modules/content/service.d.ts +137 -0
  147. package/dist/modules/content/service.d.ts.map +1 -0
  148. package/dist/modules/content/service.js +348 -0
  149. package/dist/modules/content/service.js.map +1 -0
  150. package/dist/modules/forms/db.d.ts +42 -0
  151. package/dist/modules/forms/db.d.ts.map +1 -0
  152. package/dist/modules/forms/db.js +62 -0
  153. package/dist/modules/forms/db.js.map +1 -0
  154. package/dist/modules/forms/forms-routes.admin.d.ts +6 -0
  155. package/dist/modules/forms/forms-routes.admin.d.ts.map +1 -0
  156. package/dist/modules/forms/forms-routes.admin.js +37 -0
  157. package/dist/modules/forms/forms-routes.admin.js.map +1 -0
  158. package/dist/modules/forms/forms-service.d.ts +18 -0
  159. package/dist/modules/forms/forms-service.d.ts.map +1 -0
  160. package/dist/modules/forms/forms-service.js +121 -0
  161. package/dist/modules/forms/forms-service.js.map +1 -0
  162. package/dist/modules/forms/routes.public.d.ts +6 -0
  163. package/dist/modules/forms/routes.public.d.ts.map +1 -0
  164. package/dist/modules/forms/routes.public.js +41 -0
  165. package/dist/modules/forms/routes.public.js.map +1 -0
  166. package/dist/modules/forms/submissions-routes.admin.d.ts +6 -0
  167. package/dist/modules/forms/submissions-routes.admin.d.ts.map +1 -0
  168. package/dist/modules/forms/submissions-routes.admin.js +46 -0
  169. package/dist/modules/forms/submissions-routes.admin.js.map +1 -0
  170. package/dist/modules/forms/submissions-service.d.ts +45 -0
  171. package/dist/modules/forms/submissions-service.d.ts.map +1 -0
  172. package/dist/modules/forms/submissions-service.js +174 -0
  173. package/dist/modules/forms/submissions-service.js.map +1 -0
  174. package/dist/modules/media/db.d.ts +15 -0
  175. package/dist/modules/media/db.d.ts.map +1 -0
  176. package/dist/modules/media/db.js +24 -0
  177. package/dist/modules/media/db.js.map +1 -0
  178. package/dist/modules/media/routes.admin.d.ts +6 -0
  179. package/dist/modules/media/routes.admin.d.ts.map +1 -0
  180. package/dist/modules/media/routes.admin.js +30 -0
  181. package/dist/modules/media/routes.admin.js.map +1 -0
  182. package/dist/modules/media/routes.public.d.ts +6 -0
  183. package/dist/modules/media/routes.public.d.ts.map +1 -0
  184. package/dist/modules/media/routes.public.js +35 -0
  185. package/dist/modules/media/routes.public.js.map +1 -0
  186. package/dist/modules/media/service.d.ts +32 -0
  187. package/dist/modules/media/service.d.ts.map +1 -0
  188. package/dist/modules/media/service.js +99 -0
  189. package/dist/modules/media/service.js.map +1 -0
  190. package/dist/modules/openapi/spec.d.ts +21 -0
  191. package/dist/modules/openapi/spec.d.ts.map +1 -0
  192. package/dist/modules/openapi/spec.js +468 -0
  193. package/dist/modules/openapi/spec.js.map +1 -0
  194. package/dist/modules/seo/jsonld.d.ts +25 -0
  195. package/dist/modules/seo/jsonld.d.ts.map +1 -0
  196. package/dist/modules/seo/jsonld.js +67 -0
  197. package/dist/modules/seo/jsonld.js.map +1 -0
  198. package/dist/modules/seo/routes.public.d.ts +6 -0
  199. package/dist/modules/seo/routes.public.d.ts.map +1 -0
  200. package/dist/modules/seo/routes.public.js +39 -0
  201. package/dist/modules/seo/routes.public.js.map +1 -0
  202. package/dist/modules/seo/sitemap.d.ts +3 -0
  203. package/dist/modules/seo/sitemap.d.ts.map +1 -0
  204. package/dist/modules/seo/sitemap.js +39 -0
  205. package/dist/modules/seo/sitemap.js.map +1 -0
  206. package/dist/modules/settings/routes.admin.d.ts +12 -0
  207. package/dist/modules/settings/routes.admin.d.ts.map +1 -0
  208. package/dist/modules/settings/routes.admin.js +27 -0
  209. package/dist/modules/settings/routes.admin.js.map +1 -0
  210. package/dist/modules/site-documents/routes.admin.d.ts +10 -0
  211. package/dist/modules/site-documents/routes.admin.d.ts.map +1 -0
  212. package/dist/modules/site-documents/routes.admin.js +29 -0
  213. package/dist/modules/site-documents/routes.admin.js.map +1 -0
  214. package/dist/modules/site-documents/routes.public.d.ts +6 -0
  215. package/dist/modules/site-documents/routes.public.d.ts.map +1 -0
  216. package/dist/modules/site-documents/routes.public.js +16 -0
  217. package/dist/modules/site-documents/routes.public.js.map +1 -0
  218. package/dist/modules/site-documents/service.d.ts +11 -0
  219. package/dist/modules/site-documents/service.d.ts.map +1 -0
  220. package/dist/modules/site-documents/service.js +42 -0
  221. package/dist/modules/site-documents/service.js.map +1 -0
  222. package/dist/modules/webhooks/dispatch.d.ts +14 -0
  223. package/dist/modules/webhooks/dispatch.d.ts.map +1 -0
  224. package/dist/modules/webhooks/dispatch.js +84 -0
  225. package/dist/modules/webhooks/dispatch.js.map +1 -0
  226. package/dist/shared/constants.d.ts +79 -0
  227. package/dist/shared/constants.d.ts.map +1 -0
  228. package/dist/shared/constants.js +89 -0
  229. package/dist/shared/constants.js.map +1 -0
  230. package/dist/shared/envelope.d.ts +37 -0
  231. package/dist/shared/envelope.d.ts.map +1 -0
  232. package/dist/shared/envelope.js +46 -0
  233. package/dist/shared/envelope.js.map +1 -0
  234. package/dist/shared/hash.d.ts +2 -0
  235. package/dist/shared/hash.d.ts.map +1 -0
  236. package/dist/shared/hash.js +8 -0
  237. package/dist/shared/hash.js.map +1 -0
  238. package/dist/shared/index.d.ts +13 -0
  239. package/dist/shared/index.d.ts.map +1 -0
  240. package/dist/shared/index.js +13 -0
  241. package/dist/shared/index.js.map +1 -0
  242. package/dist/shared/schemas/aeo.d.ts +182 -0
  243. package/dist/shared/schemas/aeo.d.ts.map +1 -0
  244. package/dist/shared/schemas/aeo.js +43 -0
  245. package/dist/shared/schemas/aeo.js.map +1 -0
  246. package/dist/shared/schemas/analytics.d.ts +277 -0
  247. package/dist/shared/schemas/analytics.d.ts.map +1 -0
  248. package/dist/shared/schemas/analytics.js +74 -0
  249. package/dist/shared/schemas/analytics.js.map +1 -0
  250. package/dist/shared/schemas/api-key.d.ts +77 -0
  251. package/dist/shared/schemas/api-key.d.ts.map +1 -0
  252. package/dist/shared/schemas/api-key.js +27 -0
  253. package/dist/shared/schemas/api-key.js.map +1 -0
  254. package/dist/shared/schemas/author.d.ts +147 -0
  255. package/dist/shared/schemas/author.d.ts.map +1 -0
  256. package/dist/shared/schemas/author.js +27 -0
  257. package/dist/shared/schemas/author.js.map +1 -0
  258. package/dist/shared/schemas/form.d.ts +341 -0
  259. package/dist/shared/schemas/form.d.ts.map +1 -0
  260. package/dist/shared/schemas/form.js +117 -0
  261. package/dist/shared/schemas/form.js.map +1 -0
  262. package/dist/shared/schemas/index.d.ts +12 -0
  263. package/dist/shared/schemas/index.d.ts.map +1 -0
  264. package/dist/shared/schemas/index.js +12 -0
  265. package/dist/shared/schemas/index.js.map +1 -0
  266. package/dist/shared/schemas/media.d.ts +42 -0
  267. package/dist/shared/schemas/media.d.ts.map +1 -0
  268. package/dist/shared/schemas/media.js +16 -0
  269. package/dist/shared/schemas/media.js.map +1 -0
  270. package/dist/shared/schemas/pagination.d.ts +34 -0
  271. package/dist/shared/schemas/pagination.d.ts.map +1 -0
  272. package/dist/shared/schemas/pagination.js +21 -0
  273. package/dist/shared/schemas/pagination.js.map +1 -0
  274. package/dist/shared/schemas/post.d.ts +862 -0
  275. package/dist/shared/schemas/post.d.ts.map +1 -0
  276. package/dist/shared/schemas/post.js +187 -0
  277. package/dist/shared/schemas/post.js.map +1 -0
  278. package/dist/shared/schemas/settings.d.ts +839 -0
  279. package/dist/shared/schemas/settings.d.ts.map +1 -0
  280. package/dist/shared/schemas/settings.js +202 -0
  281. package/dist/shared/schemas/settings.js.map +1 -0
  282. package/dist/shared/schemas/site-document.d.ts +38 -0
  283. package/dist/shared/schemas/site-document.d.ts.map +1 -0
  284. package/dist/shared/schemas/site-document.js +21 -0
  285. package/dist/shared/schemas/site-document.js.map +1 -0
  286. package/dist/shared/schemas/webhook.d.ts +39 -0
  287. package/dist/shared/schemas/webhook.d.ts.map +1 -0
  288. package/dist/shared/schemas/webhook.js +20 -0
  289. package/dist/shared/schemas/webhook.js.map +1 -0
  290. package/dist/spa.d.ts +9 -0
  291. package/dist/spa.d.ts.map +1 -0
  292. package/dist/spa.js +57 -0
  293. package/dist/spa.js.map +1 -0
  294. package/migrations/0001_settings.sql +39 -0
  295. package/migrations/0002_content.sql +135 -0
  296. package/migrations/0003_forms.sql +35 -0
  297. package/migrations/0004_analytics.sql +42 -0
  298. package/migrations/0005_media.sql +17 -0
  299. package/migrations/0006_site_documents.sql +18 -0
  300. package/migrations/0007_auth.sql +81 -0
  301. package/migrations/0008_api_keys.sql +20 -0
  302. package/package.json +68 -0
  303. package/scripts/bundle-admin.mjs +54 -0
  304. package/seed/local.sql +63 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../../src/modules/seo/sitemap.ts"],"names":[],"mappings":"AA2BA,+FAA+F;AAC/F,wBAAsB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BnF"}
@@ -0,0 +1,39 @@
1
+ import { resolveContentPath } from '../../shared/index.js';
2
+ import { getSettings } from '../../env.js';
3
+ function escapeXml(value) {
4
+ return value
5
+ .replace(/&/g, '&')
6
+ .replace(/</g, '&lt;')
7
+ .replace(/>/g, '&gt;')
8
+ .replace(/"/g, '&quot;')
9
+ .replace(/'/g, '&apos;');
10
+ }
11
+ function urlEntry(loc, lastmod, changefreq, priority) {
12
+ let entry = `<url><loc>${escapeXml(loc)}</loc>`;
13
+ if (lastmod)
14
+ entry += `<lastmod>${escapeXml(lastmod)}</lastmod>`;
15
+ if (changefreq)
16
+ entry += `<changefreq>${escapeXml(changefreq)}</changefreq>`;
17
+ if (priority !== undefined)
18
+ entry += `<priority>${priority}</priority>`;
19
+ entry += `</url>`;
20
+ return entry;
21
+ }
22
+ /** Published posts (cap 1000, lastmod from published_at) plus the configured static routes. */
23
+ export async function buildSitemap(db, siteUrl) {
24
+ const settings = await getSettings(db);
25
+ const { results } = await db
26
+ .prepare(`SELECT posts.slug, posts.type, pv.published_at
27
+ FROM posts JOIN post_versions pv ON pv.id = posts.current_version_id
28
+ WHERE posts.status = 'published'
29
+ ORDER BY pv.published_at DESC
30
+ LIMIT 1000`)
31
+ .all();
32
+ const staticEntries = settings.sitemapStatic.map((entry) => urlEntry(`${siteUrl}${entry.path}`, undefined, entry.changefreq, entry.priority));
33
+ // Per-type sections: each post's <loc> uses its type's resolved path, and an
34
+ // empty contentPaths map reproduces single-prefix output byte for byte.
35
+ const postEntries = results.map((row) => urlEntry(`${siteUrl}${resolveContentPath(row.type, settings)}/${row.slug}`, row.published_at.slice(0, 10)));
36
+ const body = [...staticEntries, ...postEntries].join('');
37
+ return `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${body}</urlset>`;
38
+ }
39
+ //# sourceMappingURL=sitemap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../../../src/modules/seo/sitemap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAiB,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAQ3C,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW,EAAE,OAAgB,EAAE,UAAmB,EAAE,QAAiB;IACrF,IAAI,KAAK,GAAG,aAAa,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;IAChD,IAAI,OAAO;QAAE,KAAK,IAAI,YAAY,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC;IACjE,IAAI,UAAU;QAAE,KAAK,IAAI,eAAe,SAAS,CAAC,UAAU,CAAC,eAAe,CAAC;IAC7E,IAAI,QAAQ,KAAK,SAAS;QAAE,KAAK,IAAI,aAAa,QAAQ,aAAa,CAAC;IACxE,KAAK,IAAI,QAAQ,CAAC;IAClB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAc,EAAE,OAAe;IAChE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;IAEvC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE;SACzB,OAAO,CACN;;;;kBAIY,CACb;SACA,GAAG,EAAkB,CAAC;IAEzB,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACzD,QAAQ,CAAC,GAAG,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CACjF,CAAC;IAEF,6EAA6E;IAC7E,wEAAwE;IACxE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACtC,QAAQ,CACN,GAAG,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,EACjE,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAC9B,CACF,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzD,OAAO,qGAAqG,IAAI,WAAW,CAAC;AAC9H,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { Hono } from 'hono';
2
+ import { type Bindings } from '../../env.js';
3
+ /**
4
+ * Not gated by moduleGate: settings is the mechanism that controls the five
5
+ * toggleable modules (content/forms/analytics/media/seo), not a module
6
+ * itself, so it has no flag of its own to gate on (same reasoning as
7
+ * /v1/health).
8
+ */
9
+ export declare const settingsAdminRoutes: Hono<{
10
+ Bindings: Bindings;
11
+ }, import("hono/types").BlankSchema, "/">;
12
+ //# sourceMappingURL=routes.admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../../src/modules/settings/routes.admin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAA4B,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAIvE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;cAAwB,QAAQ;yCAAK,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { ok, siteSettingsUpdateInputSchema } from '../../shared/index.js';
2
+ import { Hono } from 'hono';
3
+ import { getSettings, setSettings } from '../../env.js';
4
+ import { requireOwner } from '../../middleware/auth.js';
5
+ import { parseOrThrow } from '../../lib/validate.js';
6
+ /**
7
+ * Not gated by moduleGate: settings is the mechanism that controls the five
8
+ * toggleable modules (content/forms/analytics/media/seo), not a module
9
+ * itself, so it has no flag of its own to gate on (same reasoning as
10
+ * /v1/health).
11
+ */
12
+ export const settingsAdminRoutes = new Hono();
13
+ // GET stays editor+: reading module config / notify list is not a mutation.
14
+ settingsAdminRoutes.get('/', async (c) => {
15
+ const settings = await getSettings(c.env.DB);
16
+ return c.json(ok({ settings }));
17
+ });
18
+ // The settings MUTATION is OWNER-ONLY. It changes notifyEmails, webhooks,
19
+ // allowedOrigins, module flags, and retention: security-relevant configuration
20
+ // (redirecting form notifications, for one), so a member may read it but not
21
+ // change it.
22
+ settingsAdminRoutes.patch('/', requireOwner, async (c) => {
23
+ const patch = parseOrThrow(siteSettingsUpdateInputSchema, await c.req.json());
24
+ const settings = await setSettings(c.env.DB, patch);
25
+ return c.json(ok({ settings }));
26
+ });
27
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../../src/modules/settings/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAiB,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,IAAI,EAA0B,CAAC;AAEtE,4EAA4E;AAC5E,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACvC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,0EAA0E;AAC1E,+EAA+E;AAC/E,6EAA6E;AAC7E,aAAa;AACb,mBAAmB,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACvD,MAAM,KAAK,GAAG,YAAY,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACpD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Hono } from 'hono';
2
+ import type { Bindings } from '../../env.js';
3
+ /**
4
+ * Not gated by moduleGate: site documents are a site-level concern (like
5
+ * settings), not one of the five toggleable modules.
6
+ */
7
+ export declare const siteDocumentsAdminRoutes: Hono<{
8
+ Bindings: Bindings;
9
+ }, import("hono/types").BlankSchema, "/">;
10
+ //# sourceMappingURL=routes.admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../../src/modules/site-documents/routes.admin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C;;;GAGG;AACH,eAAO,MAAM,wBAAwB;cAAwB,QAAQ;yCAAK,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { ok, siteDocumentKindSchema, siteDocumentUpdateInputSchema } from '../../shared/index.js';
2
+ import { Hono } from 'hono';
3
+ import { HttpError } from '../../lib/errors.js';
4
+ import { parseOrThrow } from '../../lib/validate.js';
5
+ import { getDocument, upsertDocument } from './service.js';
6
+ /**
7
+ * Not gated by moduleGate: site documents are a site-level concern (like
8
+ * settings), not one of the five toggleable modules.
9
+ */
10
+ export const siteDocumentsAdminRoutes = new Hono();
11
+ function parseKind(raw) {
12
+ const result = siteDocumentKindSchema.safeParse(raw);
13
+ if (!result.success) {
14
+ throw new HttpError('not_found', 'unknown document kind');
15
+ }
16
+ return result.data;
17
+ }
18
+ siteDocumentsAdminRoutes.get('/:kind', async (c) => {
19
+ const kind = parseKind(c.req.param('kind'));
20
+ const document = await getDocument(c.env.DB, kind);
21
+ return c.json(ok({ document }));
22
+ });
23
+ siteDocumentsAdminRoutes.put('/:kind', async (c) => {
24
+ const kind = parseKind(c.req.param('kind'));
25
+ const input = parseOrThrow(siteDocumentUpdateInputSchema, await c.req.json());
26
+ const document = await upsertDocument(c.env.DB, kind, input.content, input.answerSummary);
27
+ return c.json(ok({ document }));
28
+ });
29
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../../src/modules/site-documents/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAClG,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3D;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,IAAI,EAA0B,CAAC;AAE3E,SAAS,SAAS,CAAC,GAAW;IAC5B,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,wBAAwB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACjD,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACnD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,wBAAwB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACjD,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,YAAY,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC1F,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { Hono } from 'hono';
2
+ import type { Bindings } from '../../env.js';
3
+ export declare const siteDocumentsPublicRoutes: Hono<{
4
+ Bindings: Bindings;
5
+ }, import("hono/types").BlankSchema, "/">;
6
+ //# sourceMappingURL=routes.public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.public.d.ts","sourceRoot":"","sources":["../../../src/modules/site-documents/routes.public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,eAAO,MAAM,yBAAyB;cAAwB,QAAQ;yCAAK,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { ok, siteDocumentKindSchema } from '../../shared/index.js';
2
+ import { Hono } from 'hono';
3
+ import { HttpError } from '../../lib/errors.js';
4
+ import { corsMiddleware } from '../../middleware/cors.js';
5
+ import { getDocument } from './service.js';
6
+ export const siteDocumentsPublicRoutes = new Hono();
7
+ siteDocumentsPublicRoutes.use('/:kind', corsMiddleware('publicRead'));
8
+ siteDocumentsPublicRoutes.get('/:kind', async (c) => {
9
+ const result = siteDocumentKindSchema.safeParse(c.req.param('kind'));
10
+ if (!result.success) {
11
+ throw new HttpError('not_found', 'unknown document kind');
12
+ }
13
+ const document = await getDocument(c.env.DB, result.data);
14
+ return c.json(ok({ document }));
15
+ });
16
+ //# sourceMappingURL=routes.public.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.public.js","sourceRoot":"","sources":["../../../src/modules/site-documents/routes.public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,IAAI,EAA0B,CAAC;AAE5E,yBAAyB,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;AAEtE,yBAAyB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAClD,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { SiteDocumentKind, SiteDocumentRecord } from '../../shared/index.js';
2
+ /**
3
+ * A missing row means "never written", which is the same meaning as empty
4
+ * content, not a not-found error: a site whose owner has never opened the
5
+ * Documents screen has written nothing, and that is a normal state.
6
+ */
7
+ export declare function getDocument(db: D1Database, kind: SiteDocumentKind): Promise<SiteDocumentRecord>;
8
+ /** Every document at once, for the AEO check and the generated llms.txt. */
9
+ export declare function listDocuments(db: D1Database): Promise<SiteDocumentRecord[]>;
10
+ export declare function upsertDocument(db: D1Database, kind: SiteDocumentKind, content: string, answerSummary: string | undefined): Promise<SiteDocumentRecord>;
11
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/site-documents/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAkBlF;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAM7B;AAED,4EAA4E;AAC5E,wBAAsB,aAAa,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAKjF;AAED,wBAAsB,cAAc,CAClC,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAe7B"}
@@ -0,0 +1,42 @@
1
+ function toRecord(row, kind) {
2
+ return {
3
+ kind,
4
+ content: row?.content ?? '',
5
+ answerSummary: row?.answer_summary ?? null,
6
+ updatedAt: row?.updated_at ?? null,
7
+ };
8
+ }
9
+ /**
10
+ * A missing row means "never written", which is the same meaning as empty
11
+ * content, not a not-found error: a site whose owner has never opened the
12
+ * Documents screen has written nothing, and that is a normal state.
13
+ */
14
+ export async function getDocument(db, kind) {
15
+ const row = await db
16
+ .prepare('SELECT kind, content, answer_summary, updated_at FROM site_documents WHERE kind = ?1')
17
+ .bind(kind)
18
+ .first();
19
+ return toRecord(row, kind);
20
+ }
21
+ /** Every document at once, for the AEO check and the generated llms.txt. */
22
+ export async function listDocuments(db) {
23
+ const { results } = await db
24
+ .prepare('SELECT kind, content, answer_summary, updated_at FROM site_documents')
25
+ .all();
26
+ return results.map((row) => toRecord(row, row.kind));
27
+ }
28
+ export async function upsertDocument(db, kind, content, answerSummary) {
29
+ await db
30
+ .prepare(`INSERT INTO site_documents (kind, content, answer_summary, updated_at)
31
+ VALUES (?1, ?2, ?3, datetime('now'))
32
+ ON CONFLICT (kind) DO UPDATE SET
33
+ content = excluded.content,
34
+ answer_summary = excluded.answer_summary,
35
+ updated_at = excluded.updated_at`)
36
+ // "" clears, the same wire protocol every other optional string uses, so a
37
+ // row never stores an empty string where null is what is meant.
38
+ .bind(kind, content, answerSummary || null)
39
+ .run();
40
+ return getDocument(db, kind);
41
+ }
42
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/modules/site-documents/service.ts"],"names":[],"mappings":"AASA,SAAS,QAAQ,CAAC,GAA2B,EAAE,IAAsB;IACnE,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,EAAE;QAC3B,aAAa,EAAE,GAAG,EAAE,cAAc,IAAI,IAAI;QAC1C,SAAS,EAAE,GAAG,EAAE,UAAU,IAAI,IAAI;KACnC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAc,EACd,IAAsB;IAEtB,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,OAAO,CAAC,sFAAsF,CAAC;SAC/F,IAAI,CAAC,IAAI,CAAC;SACV,KAAK,EAAmB,CAAC;IAC5B,OAAO,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAc;IAChD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE;SACzB,OAAO,CAAC,sEAAsE,CAAC;SAC/E,GAAG,EAAmB,CAAC;IAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,IAAwB,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,EAAc,EACd,IAAsB,EACtB,OAAe,EACf,aAAiC;IAEjC,MAAM,EAAE;SACL,OAAO,CACN;;;;;0CAKoC,CACrC;QACD,2EAA2E;QAC3E,gEAAgE;SAC/D,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,IAAI,IAAI,CAAC;SAC1C,GAAG,EAAE,CAAC;IACT,OAAO,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { WebhookEvent } from '../../shared/index.js';
2
+ import type { ExecutionContext } from 'hono';
3
+ /**
4
+ * Fires an event to every configured webhook subscribed to it. Each delivery,
5
+ * with its own retry schedule, runs inside ctx.waitUntil so the triggering
6
+ * request (an admin publish, a form submission) returns immediately instead of
7
+ * waiting on somebody else's endpoint.
8
+ */
9
+ export declare function dispatchWebhook(db: D1Database, ctx: ExecutionContext, siteUrl: string, event: WebhookEvent, data: Record<string, unknown>,
10
+ /** Test-only: override the real 0s/30s schedule so retry-exhaustion tests don't take 30+ real seconds. */
11
+ retryDelaysMs?: number[],
12
+ /** Test-only: override the real 10s per-attempt timeout for hang-simulation tests. */
13
+ attemptTimeoutMs?: number): Promise<void>;
14
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/modules/webhooks/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAC;AA6H7C;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,UAAU,EACd,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;AAC7B,0GAA0G;AAC1G,aAAa,GAAE,MAAM,EAAoB;AACzC,sFAAsF;AACtF,gBAAgB,GAAE,MAA2B,GAC5C,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,84 @@
1
+ import { getSettings } from '../../env.js';
2
+ /** Delays BETWEEN attempts. Three attempts total, so two gaps: 0s then 30s. */
3
+ const RETRY_DELAYS_MS = [0, 30_000];
4
+ /**
5
+ * Bounds a single delivery attempt. Load-bearing, and proven so against a real
6
+ * deploy: a receiver URL that never responds left `fetch()` hanging with no
7
+ * timeout, so the delivery never reached its retry schedule at all and the row
8
+ * stayed "pending" forever. Unit tests mock fetch as an immediate resolve or
9
+ * reject, so it takes a genuinely non-resolving host to surface.
10
+ */
11
+ const ATTEMPT_TIMEOUT_MS = 10_000;
12
+ async function fetchWithTimeout(url, init, timeoutMs) {
13
+ const controller = new AbortController();
14
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
15
+ try {
16
+ return await fetch(url, { ...init, signal: controller.signal });
17
+ }
18
+ finally {
19
+ clearTimeout(timeout);
20
+ }
21
+ }
22
+ async function signWebhookBody(secret, body) {
23
+ const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
24
+ const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));
25
+ return Array.from(new Uint8Array(signature))
26
+ .map((byte) => byte.toString(16).padStart(2, '0'))
27
+ .join('');
28
+ }
29
+ async function deliverWithRetries(url, body, secret, retryDelaysMs, attemptTimeoutMs) {
30
+ const signature = await signWebhookBody(secret, body);
31
+ let lastStatus = null;
32
+ for (let attempt = 0; attempt < 3; attempt++) {
33
+ if (attempt > 0) {
34
+ const delay = retryDelaysMs[attempt - 1] ?? 0;
35
+ await new Promise((resolve) => setTimeout(resolve, delay));
36
+ }
37
+ try {
38
+ const res = await fetchWithTimeout(url, {
39
+ method: 'POST',
40
+ headers: { 'Content-Type': 'application/json', 'X-We8-Signature': signature },
41
+ body,
42
+ }, attemptTimeoutMs);
43
+ lastStatus = res.status;
44
+ if (res.ok)
45
+ return { success: true, lastStatus, attempts: attempt + 1 };
46
+ }
47
+ catch {
48
+ lastStatus = null;
49
+ }
50
+ }
51
+ return { success: false, lastStatus, attempts: 3 };
52
+ }
53
+ async function deliverAndLog(db, webhook, event, data, siteUrl, retryDelaysMs, attemptTimeoutMs) {
54
+ const envelope = { event, site: siteUrl, data, timestamp: new Date().toISOString() };
55
+ const body = JSON.stringify(envelope);
56
+ const inserted = await db
57
+ .prepare(`INSERT INTO webhook_deliveries (event, url, status, attempts) VALUES (?1, ?2, 'pending', 0)`)
58
+ .bind(event, webhook.url)
59
+ .run();
60
+ const deliveryId = inserted.meta.last_row_id;
61
+ const outcome = await deliverWithRetries(webhook.url, body, webhook.secret, retryDelaysMs, attemptTimeoutMs);
62
+ await db
63
+ .prepare(`UPDATE webhook_deliveries SET status=?2, response_code=?3, attempts=?4, delivered_at=?5 WHERE id=?1`)
64
+ .bind(deliveryId, outcome.success ? 'delivered' : 'failed', outcome.lastStatus, outcome.attempts, outcome.success ? new Date().toISOString() : null)
65
+ .run();
66
+ }
67
+ /**
68
+ * Fires an event to every configured webhook subscribed to it. Each delivery,
69
+ * with its own retry schedule, runs inside ctx.waitUntil so the triggering
70
+ * request (an admin publish, a form submission) returns immediately instead of
71
+ * waiting on somebody else's endpoint.
72
+ */
73
+ export async function dispatchWebhook(db, ctx, siteUrl, event, data,
74
+ /** Test-only: override the real 0s/30s schedule so retry-exhaustion tests don't take 30+ real seconds. */
75
+ retryDelaysMs = RETRY_DELAYS_MS,
76
+ /** Test-only: override the real 10s per-attempt timeout for hang-simulation tests. */
77
+ attemptTimeoutMs = ATTEMPT_TIMEOUT_MS) {
78
+ const settings = await getSettings(db);
79
+ const matching = settings.webhooks.filter((webhook) => webhook.events.includes(event));
80
+ for (const webhook of matching) {
81
+ ctx.waitUntil(deliverAndLog(db, webhook, event, data, siteUrl, retryDelaysMs, attemptTimeoutMs));
82
+ }
83
+ }
84
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../../src/modules/webhooks/dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The cross-cutting vocabularies of the we8 API dialect. Every list here is
3
+ * fixed on purpose: types drive routing, sitemap sections, JSON-LD, and
4
+ * template switching, and an open vocabulary breaks all four.
5
+ */
6
+ export declare const RATE_LIMIT_BUCKETS: {
7
+ readonly formsBurst: {
8
+ readonly windowSeconds: 60;
9
+ readonly max: 1;
10
+ };
11
+ readonly formsHourly: {
12
+ readonly windowSeconds: 3600;
13
+ readonly max: 5;
14
+ };
15
+ readonly engagement: {
16
+ readonly windowSeconds: 60;
17
+ readonly max: 30;
18
+ };
19
+ };
20
+ export type RateLimitBucket = keyof typeof RATE_LIMIT_BUCKETS;
21
+ export declare const DEFAULT_RETENTION_DAYS: {
22
+ readonly visits: 90;
23
+ readonly consent: 365;
24
+ readonly rateEvents: 2;
25
+ readonly webhookDeliveries: 30;
26
+ };
27
+ export declare const POST_STATUSES: readonly ["draft", "published", "archived"];
28
+ export type PostStatus = (typeof POST_STATUSES)[number];
29
+ export declare const POST_TYPES: readonly ["blog", "article", "news", "research", "whitepaper", "case-study"];
30
+ export type PostType = (typeof POST_TYPES)[number];
31
+ export declare const SUBMISSION_STATUSES: readonly ["new", "reviewing", "resolved", "spam", "archived"];
32
+ export type SubmissionStatus = (typeof SUBMISSION_STATUSES)[number];
33
+ export declare const FORM_TYPES: readonly ["contact", "newsletter", "custom"];
34
+ export type FormType = (typeof FORM_TYPES)[number];
35
+ export declare const AUTHOR_TYPES: readonly ["team", "guest", "advisor", "partner"];
36
+ export type AuthorType = (typeof AUTHOR_TYPES)[number];
37
+ export declare const CONSENT_DECISIONS: readonly ["accepted", "partial", "declined"];
38
+ export type ConsentDecision = (typeof CONSENT_DECISIONS)[number];
39
+ export declare const WEBHOOK_EVENTS: readonly ["post.published", "post.unpublished", "form.submission.created"];
40
+ export type WebhookEvent = (typeof WEBHOOK_EVENTS)[number];
41
+ /**
42
+ * Public API key kinds, the same dialect managed we8 speaks: `pk_` is the
43
+ * publishable key a browser may carry, `sk_` the secret key a server holds.
44
+ * In this package both are minted, stored as SHA-256 hashes, and validated
45
+ * INSIDE this worker rather than in a control plane.
46
+ */
47
+ export declare const API_KEY_KINDS: readonly ["pk", "sk"];
48
+ export type ApiKeyKind = (typeof API_KEY_KINDS)[number];
49
+ /**
50
+ * The two roles of the embedded auth model. `owner` is the first user created
51
+ * and holds the settings mutation and the form-submission inbox; `member` is
52
+ * everyone else and holds the rest of the admin API.
53
+ */
54
+ export declare const USER_ROLES: readonly ["owner", "member"];
55
+ export type UserRole = (typeof USER_ROLES)[number];
56
+ /**
57
+ * Site documents: small author-written texts served at the site root, kept out
58
+ * of `site_settings` (multi-KB docs would tax every hot-path settings read) and
59
+ * out of R2 (small transactional text, not a binary asset). One row per kind.
60
+ * `llms-txt` doubles as the manual override for the generated /llms.txt.
61
+ */
62
+ export declare const SITE_DOCUMENT_KINDS: readonly ["design-md", "llms-txt"];
63
+ export type SiteDocumentKind = (typeof SITE_DOCUMENT_KINDS)[number];
64
+ /**
65
+ * How a visit's user agent is classified when the beacon records it (0.1 AEO).
66
+ * Crawler attribution is the point: an answer engine reading the site is the
67
+ * signal AEO work is judged on, and lumping every non-human into one "bot"
68
+ * bucket throws that signal away. `human` is anything no pattern matched.
69
+ */
70
+ export declare const USER_AGENT_CLASSES: readonly ["human", "ai-crawler", "search-crawler", "social-crawler", "seo-crawler", "other-bot"];
71
+ export type UserAgentClass = (typeof USER_AGENT_CLASSES)[number];
72
+ /**
73
+ * The AEO findings the admin check endpoints report per piece of content. Each
74
+ * is a concrete, locally verifiable absence, never a score or a guess: no
75
+ * external service is called and nothing is inferred from ranking data.
76
+ */
77
+ export declare const AEO_FINDINGS: readonly ["missing_description", "missing_answer_summary", "missing_question_heading", "missing_json_ld", "not_in_sitemap"];
78
+ export type AeoFinding = (typeof AEO_FINDINGS)[number];
79
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;CAIrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,kBAAkB,CAAC;AAE9D,eAAO,MAAM,sBAAsB;;;;;CAKzB,CAAC;AAEX,eAAO,MAAM,aAAa,6CAA8C,CAAC;AACzE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAMxD,eAAO,MAAM,UAAU,8EAOb,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,eAAO,MAAM,mBAAmB,+DAAgE,CAAC;AACjG,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAMpE,eAAO,MAAM,UAAU,8CAA+C,CAAC;AACvE,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,eAAO,MAAM,YAAY,kDAAmD,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,eAAO,MAAM,iBAAiB,8CAA+C,CAAC;AAC9E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,eAAO,MAAM,cAAc,4EAIjB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,aAAa,uBAAwB,CAAC;AACnD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,UAAU,8BAA+B,CAAC;AACvD,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,oCAAqC,CAAC;AACtE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,kGAOrB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,YAAY,6HAMf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The cross-cutting vocabularies of the we8 API dialect. Every list here is
3
+ * fixed on purpose: types drive routing, sitemap sections, JSON-LD, and
4
+ * template switching, and an open vocabulary breaks all four.
5
+ */
6
+ export const RATE_LIMIT_BUCKETS = {
7
+ formsBurst: { windowSeconds: 60, max: 1 },
8
+ formsHourly: { windowSeconds: 3600, max: 5 },
9
+ engagement: { windowSeconds: 60, max: 30 },
10
+ };
11
+ export const DEFAULT_RETENTION_DAYS = {
12
+ visits: 90,
13
+ consent: 365,
14
+ rateEvents: 2,
15
+ webhookDeliveries: 30,
16
+ };
17
+ export const POST_STATUSES = ['draft', 'published', 'archived'];
18
+ // Content types: a FIXED vocabulary, deliberately not site-extensible, because
19
+ // type drives routing (contentPaths), the sitemap, the JSON-LD @type, template
20
+ // switching, and filters; open values break all five. Adding a member later is
21
+ // a cheap additive migration.
22
+ export const POST_TYPES = [
23
+ 'blog',
24
+ 'article',
25
+ 'news',
26
+ 'research',
27
+ 'whitepaper',
28
+ 'case-study',
29
+ ];
30
+ export const SUBMISSION_STATUSES = ['new', 'reviewing', 'resolved', 'spam', 'archived'];
31
+ // A form's declared purpose. Not a hard schema on the submission (every form
32
+ // accepts arbitrary fields); it labels the form and tailors the embed example
33
+ // the admin shows. "contact" is the classic name/email/message form,
34
+ // "newsletter" an email-only signup, "custom" anything else.
35
+ export const FORM_TYPES = ['contact', 'newsletter', 'custom'];
36
+ export const AUTHOR_TYPES = ['team', 'guest', 'advisor', 'partner'];
37
+ export const CONSENT_DECISIONS = ['accepted', 'partial', 'declined'];
38
+ export const WEBHOOK_EVENTS = [
39
+ 'post.published',
40
+ 'post.unpublished',
41
+ 'form.submission.created',
42
+ ];
43
+ /**
44
+ * Public API key kinds, the same dialect managed we8 speaks: `pk_` is the
45
+ * publishable key a browser may carry, `sk_` the secret key a server holds.
46
+ * In this package both are minted, stored as SHA-256 hashes, and validated
47
+ * INSIDE this worker rather than in a control plane.
48
+ */
49
+ export const API_KEY_KINDS = ['pk', 'sk'];
50
+ /**
51
+ * The two roles of the embedded auth model. `owner` is the first user created
52
+ * and holds the settings mutation and the form-submission inbox; `member` is
53
+ * everyone else and holds the rest of the admin API.
54
+ */
55
+ export const USER_ROLES = ['owner', 'member'];
56
+ /**
57
+ * Site documents: small author-written texts served at the site root, kept out
58
+ * of `site_settings` (multi-KB docs would tax every hot-path settings read) and
59
+ * out of R2 (small transactional text, not a binary asset). One row per kind.
60
+ * `llms-txt` doubles as the manual override for the generated /llms.txt.
61
+ */
62
+ export const SITE_DOCUMENT_KINDS = ['design-md', 'llms-txt'];
63
+ /**
64
+ * How a visit's user agent is classified when the beacon records it (0.1 AEO).
65
+ * Crawler attribution is the point: an answer engine reading the site is the
66
+ * signal AEO work is judged on, and lumping every non-human into one "bot"
67
+ * bucket throws that signal away. `human` is anything no pattern matched.
68
+ */
69
+ export const USER_AGENT_CLASSES = [
70
+ 'human',
71
+ 'ai-crawler',
72
+ 'search-crawler',
73
+ 'social-crawler',
74
+ 'seo-crawler',
75
+ 'other-bot',
76
+ ];
77
+ /**
78
+ * The AEO findings the admin check endpoints report per piece of content. Each
79
+ * is a concrete, locally verifiable absence, never a score or a guess: no
80
+ * external service is called and nothing is inferred from ranking data.
81
+ */
82
+ export const AEO_FINDINGS = [
83
+ 'missing_description',
84
+ 'missing_answer_summary',
85
+ 'missing_question_heading',
86
+ 'missing_json_ld',
87
+ 'not_in_sitemap',
88
+ ];
89
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE;IACzC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE;IAC5C,UAAU,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;CAClC,CAAC;AAIX,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,GAAG;IACZ,UAAU,EAAE,CAAC;IACb,iBAAiB,EAAE,EAAE;CACb,CAAC;AAEX,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,CAAU,CAAC;AAGzE,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,8BAA8B;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,MAAM;IACN,SAAS;IACT,MAAM;IACN,UAAU;IACV,YAAY;IACZ,YAAY;CACJ,CAAC;AAGX,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,CAAU,CAAC;AAGjG,6EAA6E;AAC7E,8EAA8E;AAC9E,qEAAqE;AACrE,6DAA6D;AAC7D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAU,CAAC;AAGvE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAU,CAAC;AAG7E,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC;AAG9E,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,gBAAgB;IAChB,kBAAkB;IAClB,yBAAyB;CACjB,CAAC;AAGX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,CAAU,CAAC;AAGnD;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAU,CAAC;AAGvD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,WAAW,EAAE,UAAU,CAAU,CAAC;AAGtE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,OAAO;IACP,YAAY;IACZ,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,WAAW;CACH,CAAC;AAGX;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,qBAAqB;IACrB,wBAAwB;IACxB,0BAA0B;IAC1B,iBAAiB;IACjB,gBAAgB;CACR,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The response envelope every we8 API speaks: a discriminated union on `ok`,
3
+ * with a fixed error vocabulary mapped to HTTP status codes.
4
+ */
5
+ export declare const ERROR_CODES: readonly ["validation_error", "unauthorized", "forbidden", "not_found", "conflict", "rate_limited", "payload_too_large", "internal"];
6
+ export type ErrorCode = (typeof ERROR_CODES)[number];
7
+ /**
8
+ * `as const satisfies` (rather than `: Record<ErrorCode, number>`) keeps the
9
+ * literal number types instead of widening to `number`, so statusForCode()
10
+ * below returns a literal union assignable directly to frameworks' typed
11
+ * status-code parameters (e.g. Hono's ContentfulStatusCode) with no cast.
12
+ */
13
+ export declare const ERROR_STATUS: {
14
+ readonly validation_error: 400;
15
+ readonly unauthorized: 401;
16
+ readonly forbidden: 403;
17
+ readonly not_found: 404;
18
+ readonly conflict: 409;
19
+ readonly rate_limited: 429;
20
+ readonly payload_too_large: 413;
21
+ readonly internal: 500;
22
+ };
23
+ export type Ok<T extends object = object> = {
24
+ ok: true;
25
+ } & T;
26
+ export type Err = {
27
+ ok: false;
28
+ error: string;
29
+ code: ErrorCode;
30
+ };
31
+ export type Envelope<T extends object = object> = Ok<T> | Err;
32
+ export declare function ok<T extends object>(data: T): Ok<T>;
33
+ export declare function err(code: ErrorCode, error: string): Err;
34
+ export declare function isOk<T extends object>(envelope: Envelope<T>): envelope is Ok<T>;
35
+ export declare function isErr<T extends object>(envelope: Envelope<T>): envelope is Err;
36
+ export declare function statusForCode(code: ErrorCode): (typeof ERROR_STATUS)[ErrorCode];
37
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/shared/envelope.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,WAAW,sIASd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;;;;CASqB,CAAC;AAE/C,MAAM,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG,CAAC,CAAC;AAE7D,MAAM,MAAM,GAAG,GAAG;IAChB,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;AAE9D,wBAAgB,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAEnD;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,CAEvD;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAE/E;AAED,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,GAAG,CAE9E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,SAAS,CAAC,CAE/E"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The response envelope every we8 API speaks: a discriminated union on `ok`,
3
+ * with a fixed error vocabulary mapped to HTTP status codes.
4
+ */
5
+ export const ERROR_CODES = [
6
+ 'validation_error',
7
+ 'unauthorized',
8
+ 'forbidden',
9
+ 'not_found',
10
+ 'conflict',
11
+ 'rate_limited',
12
+ 'payload_too_large',
13
+ 'internal',
14
+ ];
15
+ /**
16
+ * `as const satisfies` (rather than `: Record<ErrorCode, number>`) keeps the
17
+ * literal number types instead of widening to `number`, so statusForCode()
18
+ * below returns a literal union assignable directly to frameworks' typed
19
+ * status-code parameters (e.g. Hono's ContentfulStatusCode) with no cast.
20
+ */
21
+ export const ERROR_STATUS = {
22
+ validation_error: 400,
23
+ unauthorized: 401,
24
+ forbidden: 403,
25
+ not_found: 404,
26
+ conflict: 409,
27
+ rate_limited: 429,
28
+ payload_too_large: 413,
29
+ internal: 500,
30
+ };
31
+ export function ok(data) {
32
+ return { ok: true, ...data };
33
+ }
34
+ export function err(code, error) {
35
+ return { ok: false, error, code };
36
+ }
37
+ export function isOk(envelope) {
38
+ return envelope.ok === true;
39
+ }
40
+ export function isErr(envelope) {
41
+ return envelope.ok === false;
42
+ }
43
+ export function statusForCode(code) {
44
+ return ERROR_STATUS[code];
45
+ }
46
+ //# sourceMappingURL=envelope.js.map