@softure-ai/blog 0.0.0-stage → 0.1.5

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 (418) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +602 -2
  3. package/dist/cli/bin.d.ts +3 -0
  4. package/dist/cli/bin.d.ts.map +1 -0
  5. package/dist/cli/bin.js +5 -0
  6. package/dist/cli/bin.js.map +1 -0
  7. package/dist/cli/command.d.ts +9 -0
  8. package/dist/cli/command.d.ts.map +1 -0
  9. package/dist/cli/command.js +40 -0
  10. package/dist/cli/command.js.map +1 -0
  11. package/dist/cli/index.d.ts +4 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +5 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/run.d.ts +62 -0
  16. package/dist/cli/run.d.ts.map +1 -0
  17. package/dist/cli/run.js +552 -0
  18. package/dist/cli/run.js.map +1 -0
  19. package/dist/cli/skill.d.ts +47 -0
  20. package/dist/cli/skill.d.ts.map +1 -0
  21. package/dist/cli/skill.js +221 -0
  22. package/dist/cli/skill.js.map +1 -0
  23. package/dist/content/article-file.d.ts +27 -0
  24. package/dist/content/article-file.d.ts.map +1 -0
  25. package/dist/content/article-file.js +179 -0
  26. package/dist/content/article-file.js.map +1 -0
  27. package/dist/contract.d.ts +77 -0
  28. package/dist/contract.d.ts.map +1 -0
  29. package/dist/contract.js +5 -0
  30. package/dist/contract.js.map +1 -0
  31. package/dist/db/articles.d.ts +30 -0
  32. package/dist/db/articles.d.ts.map +1 -0
  33. package/dist/db/articles.js +138 -0
  34. package/dist/db/articles.js.map +1 -0
  35. package/dist/db/publish-run.d.ts +50 -0
  36. package/dist/db/publish-run.d.ts.map +1 -0
  37. package/dist/db/publish-run.js +168 -0
  38. package/dist/db/publish-run.js.map +1 -0
  39. package/dist/db/schema.d.ts +403 -0
  40. package/dist/db/schema.d.ts.map +1 -0
  41. package/dist/db/schema.js +33 -0
  42. package/dist/db/schema.js.map +1 -0
  43. package/dist/discovery/dates.d.ts +9 -0
  44. package/dist/discovery/dates.d.ts.map +1 -0
  45. package/dist/discovery/dates.js +19 -0
  46. package/dist/discovery/dates.js.map +1 -0
  47. package/dist/discovery/index.d.ts +8 -0
  48. package/dist/discovery/index.d.ts.map +1 -0
  49. package/dist/discovery/index.js +11 -0
  50. package/dist/discovery/index.js.map +1 -0
  51. package/dist/discovery/indexnow.d.ts +11 -0
  52. package/dist/discovery/indexnow.d.ts.map +1 -0
  53. package/dist/discovery/indexnow.js +23 -0
  54. package/dist/discovery/indexnow.js.map +1 -0
  55. package/dist/discovery/refresh.d.ts +49 -0
  56. package/dist/discovery/refresh.d.ts.map +1 -0
  57. package/dist/discovery/refresh.js +55 -0
  58. package/dist/discovery/refresh.js.map +1 -0
  59. package/dist/discovery/related.d.ts +17 -0
  60. package/dist/discovery/related.d.ts.map +1 -0
  61. package/dist/discovery/related.js +28 -0
  62. package/dist/discovery/related.js.map +1 -0
  63. package/dist/discovery/rss.d.ts +30 -0
  64. package/dist/discovery/rss.d.ts.map +1 -0
  65. package/dist/discovery/rss.js +48 -0
  66. package/dist/discovery/rss.js.map +1 -0
  67. package/dist/discovery/sitemap.d.ts +25 -0
  68. package/dist/discovery/sitemap.d.ts.map +1 -0
  69. package/dist/discovery/sitemap.js +31 -0
  70. package/dist/discovery/sitemap.js.map +1 -0
  71. package/dist/discovery/submit.d.ts +44 -0
  72. package/dist/discovery/submit.d.ts.map +1 -0
  73. package/dist/discovery/submit.js +46 -0
  74. package/dist/discovery/submit.js.map +1 -0
  75. package/dist/index.d.ts +371 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +69 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/messages/en.d.ts +83 -0
  80. package/dist/messages/en.d.ts.map +1 -0
  81. package/dist/messages/en.js +83 -0
  82. package/dist/messages/en.js.map +1 -0
  83. package/dist/messages/index.d.ts +158 -0
  84. package/dist/messages/index.d.ts.map +1 -0
  85. package/dist/messages/index.js +5 -0
  86. package/dist/messages/index.js.map +1 -0
  87. package/dist/messages/pl.d.ts +78 -0
  88. package/dist/messages/pl.d.ts.map +1 -0
  89. package/dist/messages/pl.js +78 -0
  90. package/dist/messages/pl.js.map +1 -0
  91. package/dist/next/context.d.ts +6 -0
  92. package/dist/next/context.d.ts.map +1 -0
  93. package/dist/next/context.js +27 -0
  94. package/dist/next/context.js.map +1 -0
  95. package/dist/next/data.d.ts +11 -0
  96. package/dist/next/data.d.ts.map +1 -0
  97. package/dist/next/data.js +40 -0
  98. package/dist/next/data.js.map +1 -0
  99. package/dist/next/discovery.d.ts +6 -0
  100. package/dist/next/discovery.d.ts.map +1 -0
  101. package/dist/next/discovery.js +49 -0
  102. package/dist/next/discovery.js.map +1 -0
  103. package/dist/next/index.d.ts +9 -0
  104. package/dist/next/index.d.ts.map +1 -0
  105. package/dist/next/index.js +10 -0
  106. package/dist/next/index.js.map +1 -0
  107. package/dist/next/og-fonts.d.ts +6 -0
  108. package/dist/next/og-fonts.d.ts.map +1 -0
  109. package/dist/next/og-fonts.js +8 -0
  110. package/dist/next/og-fonts.js.map +1 -0
  111. package/dist/next/og-image.d.ts +36 -0
  112. package/dist/next/og-image.d.ts.map +1 -0
  113. package/dist/next/og-image.js +77 -0
  114. package/dist/next/og-image.js.map +1 -0
  115. package/dist/next/pages.d.ts +45 -0
  116. package/dist/next/pages.d.ts.map +1 -0
  117. package/dist/next/pages.js +189 -0
  118. package/dist/next/pages.js.map +1 -0
  119. package/dist/next/refresh.d.ts +9 -0
  120. package/dist/next/refresh.d.ts.map +1 -0
  121. package/dist/next/refresh.js +81 -0
  122. package/dist/next/refresh.js.map +1 -0
  123. package/dist/options.d.ts +320 -0
  124. package/dist/options.d.ts.map +1 -0
  125. package/dist/options.js +184 -0
  126. package/dist/options.js.map +1 -0
  127. package/dist/pages/body.d.ts +18 -0
  128. package/dist/pages/body.d.ts.map +1 -0
  129. package/dist/pages/body.js +21 -0
  130. package/dist/pages/body.js.map +1 -0
  131. package/dist/pages/dates.d.ts +21 -0
  132. package/dist/pages/dates.d.ts.map +1 -0
  133. package/dist/pages/dates.js +24 -0
  134. package/dist/pages/dates.js.map +1 -0
  135. package/dist/pages/index.d.ts +7 -0
  136. package/dist/pages/index.d.ts.map +1 -0
  137. package/dist/pages/index.js +9 -0
  138. package/dist/pages/index.js.map +1 -0
  139. package/dist/pages/json-ld.d.ts +33 -0
  140. package/dist/pages/json-ld.d.ts.map +1 -0
  141. package/dist/pages/json-ld.js +95 -0
  142. package/dist/pages/json-ld.js.map +1 -0
  143. package/dist/pages/listing.d.ts +46 -0
  144. package/dist/pages/listing.d.ts.map +1 -0
  145. package/dist/pages/listing.js +57 -0
  146. package/dist/pages/listing.js.map +1 -0
  147. package/dist/pages/paths.d.ts +37 -0
  148. package/dist/pages/paths.d.ts.map +1 -0
  149. package/dist/pages/paths.js +61 -0
  150. package/dist/pages/paths.js.map +1 -0
  151. package/dist/pages/redirects.d.ts +57 -0
  152. package/dist/pages/redirects.d.ts.map +1 -0
  153. package/dist/pages/redirects.js +78 -0
  154. package/dist/pages/redirects.js.map +1 -0
  155. package/dist/proxy/index.d.ts +15 -0
  156. package/dist/proxy/index.d.ts.map +1 -0
  157. package/dist/proxy/index.js +60 -0
  158. package/dist/proxy/index.js.map +1 -0
  159. package/dist/quality/blocks.d.ts +21 -0
  160. package/dist/quality/blocks.d.ts.map +1 -0
  161. package/dist/quality/blocks.js +113 -0
  162. package/dist/quality/blocks.js.map +1 -0
  163. package/dist/quality/catalog.d.ts +10 -0
  164. package/dist/quality/catalog.d.ts.map +1 -0
  165. package/dist/quality/catalog.js +71 -0
  166. package/dist/quality/catalog.js.map +1 -0
  167. package/dist/quality/check-article.d.ts +34 -0
  168. package/dist/quality/check-article.d.ts.map +1 -0
  169. package/dist/quality/check-article.js +74 -0
  170. package/dist/quality/check-article.js.map +1 -0
  171. package/dist/quality/check-files.d.ts +23 -0
  172. package/dist/quality/check-files.d.ts.map +1 -0
  173. package/dist/quality/check-files.js +44 -0
  174. package/dist/quality/check-files.js.map +1 -0
  175. package/dist/quality/external-links.d.ts +19 -0
  176. package/dist/quality/external-links.d.ts.map +1 -0
  177. package/dist/quality/external-links.js +30 -0
  178. package/dist/quality/external-links.js.map +1 -0
  179. package/dist/quality/finding.d.ts +17 -0
  180. package/dist/quality/finding.d.ts.map +1 -0
  181. package/dist/quality/finding.js +15 -0
  182. package/dist/quality/finding.js.map +1 -0
  183. package/dist/quality/gate.d.ts +5 -0
  184. package/dist/quality/gate.d.ts.map +1 -0
  185. package/dist/quality/gate.js +10 -0
  186. package/dist/quality/gate.js.map +1 -0
  187. package/dist/quality/index.d.ts +15 -0
  188. package/dist/quality/index.d.ts.map +1 -0
  189. package/dist/quality/index.js +16 -0
  190. package/dist/quality/index.js.map +1 -0
  191. package/dist/quality/link-targets.d.ts +44 -0
  192. package/dist/quality/link-targets.d.ts.map +1 -0
  193. package/dist/quality/link-targets.js +91 -0
  194. package/dist/quality/link-targets.js.map +1 -0
  195. package/dist/quality/options.d.ts +113 -0
  196. package/dist/quality/options.d.ts.map +1 -0
  197. package/dist/quality/options.js +93 -0
  198. package/dist/quality/options.js.map +1 -0
  199. package/dist/quality/plugin.d.ts +34 -0
  200. package/dist/quality/plugin.d.ts.map +1 -0
  201. package/dist/quality/plugin.js +7 -0
  202. package/dist/quality/plugin.js.map +1 -0
  203. package/dist/quality/rules/blocks.d.ts +5 -0
  204. package/dist/quality/rules/blocks.d.ts.map +1 -0
  205. package/dist/quality/rules/blocks.js +35 -0
  206. package/dist/quality/rules/blocks.js.map +1 -0
  207. package/dist/quality/rules/images.d.ts +4 -0
  208. package/dist/quality/rules/images.d.ts.map +1 -0
  209. package/dist/quality/rules/images.js +33 -0
  210. package/dist/quality/rules/images.js.map +1 -0
  211. package/dist/quality/rules/input.d.ts +11 -0
  212. package/dist/quality/rules/input.d.ts.map +1 -0
  213. package/dist/quality/rules/input.js +2 -0
  214. package/dist/quality/rules/input.js.map +1 -0
  215. package/dist/quality/rules/links.d.ts +17 -0
  216. package/dist/quality/rules/links.d.ts.map +1 -0
  217. package/dist/quality/rules/links.js +46 -0
  218. package/dist/quality/rules/links.js.map +1 -0
  219. package/dist/quality/rules/structure.d.ts +13 -0
  220. package/dist/quality/rules/structure.d.ts.map +1 -0
  221. package/dist/quality/rules/structure.js +139 -0
  222. package/dist/quality/rules/structure.js.map +1 -0
  223. package/dist/quality/rules/style.d.ts +11 -0
  224. package/dist/quality/rules/style.d.ts.map +1 -0
  225. package/dist/quality/rules/style.js +129 -0
  226. package/dist/quality/rules/style.js.map +1 -0
  227. package/dist/quality/rules/ymyl.d.ts +5 -0
  228. package/dist/quality/rules/ymyl.d.ts.map +1 -0
  229. package/dist/quality/rules/ymyl.js +51 -0
  230. package/dist/quality/rules/ymyl.js.map +1 -0
  231. package/dist/quality/rulesets/en/ruleset.d.ts +3 -0
  232. package/dist/quality/rulesets/en/ruleset.d.ts.map +1 -0
  233. package/dist/quality/rulesets/en/ruleset.js +106 -0
  234. package/dist/quality/rulesets/en/ruleset.js.map +1 -0
  235. package/dist/quality/rulesets/index.d.ts +7 -0
  236. package/dist/quality/rulesets/index.d.ts.map +1 -0
  237. package/dist/quality/rulesets/index.js +7 -0
  238. package/dist/quality/rulesets/index.js.map +1 -0
  239. package/dist/quality/rulesets/pl/ruleset.d.ts +3 -0
  240. package/dist/quality/rulesets/pl/ruleset.d.ts.map +1 -0
  241. package/dist/quality/rulesets/pl/ruleset.js +114 -0
  242. package/dist/quality/rulesets/pl/ruleset.js.map +1 -0
  243. package/dist/quality/rulesets/types.d.ts +28 -0
  244. package/dist/quality/rulesets/types.d.ts.map +1 -0
  245. package/dist/quality/rulesets/types.js +2 -0
  246. package/dist/quality/rulesets/types.js.map +1 -0
  247. package/dist/quality/settings.d.ts +26 -0
  248. package/dist/quality/settings.d.ts.map +1 -0
  249. package/dist/quality/settings.js +32 -0
  250. package/dist/quality/settings.js.map +1 -0
  251. package/dist/quality/text.d.ts +43 -0
  252. package/dist/quality/text.d.ts.map +1 -0
  253. package/dist/quality/text.js +85 -0
  254. package/dist/quality/text.js.map +1 -0
  255. package/dist/render/glossary.d.ts +27 -0
  256. package/dist/render/glossary.d.ts.map +1 -0
  257. package/dist/render/glossary.js +71 -0
  258. package/dist/render/glossary.js.map +1 -0
  259. package/dist/render/images.d.ts +42 -0
  260. package/dist/render/images.d.ts.map +1 -0
  261. package/dist/render/images.js +84 -0
  262. package/dist/render/images.js.map +1 -0
  263. package/dist/render/index.d.ts +6 -0
  264. package/dist/render/index.d.ts.map +1 -0
  265. package/dist/render/index.js +8 -0
  266. package/dist/render/index.js.map +1 -0
  267. package/dist/render/reading-time.d.ts +8 -0
  268. package/dist/render/reading-time.d.ts.map +1 -0
  269. package/dist/render/reading-time.js +17 -0
  270. package/dist/render/reading-time.js.map +1 -0
  271. package/dist/render/render-article.d.ts +97 -0
  272. package/dist/render/render-article.d.ts.map +1 -0
  273. package/dist/render/render-article.js +373 -0
  274. package/dist/render/render-article.js.map +1 -0
  275. package/dist/render/slugify-heading.d.ts +2 -0
  276. package/dist/render/slugify-heading.d.ts.map +1 -0
  277. package/dist/render/slugify-heading.js +21 -0
  278. package/dist/render/slugify-heading.js.map +1 -0
  279. package/dist/server/health.d.ts +3 -0
  280. package/dist/server/health.d.ts.map +1 -0
  281. package/dist/server/health.js +11 -0
  282. package/dist/server/health.js.map +1 -0
  283. package/dist/server/index.d.ts +10 -0
  284. package/dist/server/index.d.ts.map +1 -0
  285. package/dist/server/index.js +13 -0
  286. package/dist/server/index.js.map +1 -0
  287. package/dist/server/og-fonts.d.ts +24 -0
  288. package/dist/server/og-fonts.d.ts.map +1 -0
  289. package/dist/server/og-fonts.js +85 -0
  290. package/dist/server/og-fonts.js.map +1 -0
  291. package/dist/server/options.d.ts +17 -0
  292. package/dist/server/options.d.ts.map +1 -0
  293. package/dist/server/options.js +56 -0
  294. package/dist/server/options.js.map +1 -0
  295. package/dist/sitemap.d.ts +11 -0
  296. package/dist/sitemap.d.ts.map +1 -0
  297. package/dist/sitemap.js +35 -0
  298. package/dist/sitemap.js.map +1 -0
  299. package/dist/ui/blog-article.d.ts +49 -0
  300. package/dist/ui/blog-article.d.ts.map +1 -0
  301. package/dist/ui/blog-article.js +45 -0
  302. package/dist/ui/blog-article.js.map +1 -0
  303. package/dist/ui/blog-glossary.d.ts +27 -0
  304. package/dist/ui/blog-glossary.d.ts.map +1 -0
  305. package/dist/ui/blog-glossary.js +20 -0
  306. package/dist/ui/blog-glossary.js.map +1 -0
  307. package/dist/ui/blog-layout.d.ts +31 -0
  308. package/dist/ui/blog-layout.d.ts.map +1 -0
  309. package/dist/ui/blog-layout.js +25 -0
  310. package/dist/ui/blog-layout.js.map +1 -0
  311. package/dist/ui/blog-listing.d.ts +15 -0
  312. package/dist/ui/blog-listing.d.ts.map +1 -0
  313. package/dist/ui/blog-listing.js +29 -0
  314. package/dist/ui/blog-listing.js.map +1 -0
  315. package/dist/ui/blog-method.d.ts +5 -0
  316. package/dist/ui/blog-method.d.ts.map +1 -0
  317. package/dist/ui/blog-method.js +21 -0
  318. package/dist/ui/blog-method.js.map +1 -0
  319. package/dist/ui/index.d.ts +7 -0
  320. package/dist/ui/index.d.ts.map +1 -0
  321. package/dist/ui/index.js +8 -0
  322. package/dist/ui/index.js.map +1 -0
  323. package/dist/ui/page-context.d.ts +15 -0
  324. package/dist/ui/page-context.d.ts.map +1 -0
  325. package/dist/ui/page-context.js +2 -0
  326. package/dist/ui/page-context.js.map +1 -0
  327. package/migrations/0001_create_articles.sql +67 -0
  328. package/migrations/README.md +8 -0
  329. package/module.json +28 -0
  330. package/package.json +98 -4
  331. package/skill/SKILL.md +72 -0
  332. package/skill/references/reviewer.md +39 -0
  333. package/skill/references/rules.md +122 -0
  334. package/skill/references/structure.md +69 -0
  335. package/skill/references/template.md +86 -0
  336. package/src/cli/bin.ts +5 -0
  337. package/src/cli/command.ts +49 -0
  338. package/src/cli/index.ts +20 -0
  339. package/src/cli/run.ts +589 -0
  340. package/src/cli/skill.ts +242 -0
  341. package/src/content/article-file.ts +184 -0
  342. package/src/contract.ts +88 -0
  343. package/src/db/articles.ts +160 -0
  344. package/src/db/publish-run.ts +224 -0
  345. package/src/db/schema.ts +36 -0
  346. package/src/discovery/dates.ts +27 -0
  347. package/src/discovery/index.ts +18 -0
  348. package/src/discovery/indexnow.ts +25 -0
  349. package/src/discovery/refresh.ts +79 -0
  350. package/src/discovery/related.ts +35 -0
  351. package/src/discovery/rss.ts +77 -0
  352. package/src/discovery/sitemap.ts +59 -0
  353. package/src/discovery/submit.ts +69 -0
  354. package/src/index.ts +99 -0
  355. package/src/messages/en.ts +82 -0
  356. package/src/messages/index.ts +7 -0
  357. package/src/messages/pl.ts +77 -0
  358. package/src/next/context.ts +30 -0
  359. package/src/next/data.ts +55 -0
  360. package/src/next/discovery.ts +49 -0
  361. package/src/next/index.ts +25 -0
  362. package/src/next/next-modules.d.ts +15 -0
  363. package/src/next/og-fonts.ts +14 -0
  364. package/src/next/og-image.tsx +108 -0
  365. package/src/next/pages.tsx +251 -0
  366. package/src/next/refresh.ts +85 -0
  367. package/src/options.ts +224 -0
  368. package/src/pages/body.ts +38 -0
  369. package/src/pages/dates.ts +39 -0
  370. package/src/pages/index.ts +37 -0
  371. package/src/pages/json-ld.ts +120 -0
  372. package/src/pages/listing.ts +95 -0
  373. package/src/pages/paths.ts +84 -0
  374. package/src/pages/redirects.ts +120 -0
  375. package/src/proxy/index.ts +66 -0
  376. package/src/quality/blocks.ts +130 -0
  377. package/src/quality/catalog.ts +86 -0
  378. package/src/quality/check-article.ts +112 -0
  379. package/src/quality/check-files.ts +63 -0
  380. package/src/quality/external-links.ts +37 -0
  381. package/src/quality/finding.ts +29 -0
  382. package/src/quality/gate.ts +15 -0
  383. package/src/quality/index.ts +28 -0
  384. package/src/quality/link-targets.ts +107 -0
  385. package/src/quality/options.ts +105 -0
  386. package/src/quality/plugin.ts +43 -0
  387. package/src/quality/rules/blocks.ts +43 -0
  388. package/src/quality/rules/images.ts +34 -0
  389. package/src/quality/rules/input.ts +12 -0
  390. package/src/quality/rules/links.ts +56 -0
  391. package/src/quality/rules/structure.ts +137 -0
  392. package/src/quality/rules/style.ts +137 -0
  393. package/src/quality/rules/ymyl.ts +57 -0
  394. package/src/quality/rulesets/en/ruleset.ts +117 -0
  395. package/src/quality/rulesets/index.ts +9 -0
  396. package/src/quality/rulesets/pl/ruleset.ts +126 -0
  397. package/src/quality/rulesets/types.ts +31 -0
  398. package/src/quality/settings.ts +60 -0
  399. package/src/quality/text.ts +113 -0
  400. package/src/render/glossary.ts +103 -0
  401. package/src/render/images.ts +110 -0
  402. package/src/render/index.ts +29 -0
  403. package/src/render/reading-time.ts +18 -0
  404. package/src/render/render-article.ts +487 -0
  405. package/src/render/slugify-heading.ts +22 -0
  406. package/src/server/health.ts +12 -0
  407. package/src/server/index.ts +28 -0
  408. package/src/server/og-fonts.ts +102 -0
  409. package/src/server/options.ts +62 -0
  410. package/src/sitemap.ts +36 -0
  411. package/src/ui/blog-article.tsx +186 -0
  412. package/src/ui/blog-glossary.tsx +104 -0
  413. package/src/ui/blog-layout.tsx +89 -0
  414. package/src/ui/blog-listing.tsx +95 -0
  415. package/src/ui/blog-method.tsx +34 -0
  416. package/src/ui/index.ts +8 -0
  417. package/src/ui/page-context.ts +17 -0
  418. package/styles.css +498 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/server/health.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,6DAA6D;AAC7D,OAAO,EAAE,EAAE,EAAoB,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,MAAM,CAAC,MAAM,kBAAkB,GAAgB,KAAK,EAAE,OAAO,EAAE,EAAE;IAC/D,iFAAiF;IACjF,MAAM,EAAE,GAAG,OAAO,CAAC,EAAe,CAAC;IACnC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAA,qCAAqC,CAAC,CAAC;IAC3D,OAAO,EAAE,EAAE,CAAC;AACd,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { computeContentHash, parseArticleFile, type ArticleFileResult, type ParseArticleFileOptions } from "../content/article-file.js";
2
+ export { findArticleBySlug, findSlugRedirect, getPublishedArticle, listArticles, publishArticle, type BlogContext, type ListArticlesFilter, } from "../db/articles.js";
3
+ export { runBlogPublish, type ArticleFile, type BlogPublishRun, type PublishedChange, type PublishGate, type PublishProblem, type RunBlogPublishOptions, } from "../db/publish-run.js";
4
+ export { checkArticlesTable } from "./health.js";
5
+ export { getBlogMessages, getBlogOptions, getBlogRefreshPath, getBlogReservedSlugs, getBlogRoutes, getQualitySettings } from "./options.js";
6
+ export * from "../discovery/index.js";
7
+ export * from "../pages/index.js";
8
+ export * from "../quality/index.js";
9
+ export * from "../render/index.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,KAAK,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACxI,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5I,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,13 @@
1
+ // Server-only API of @softure-ai/blog. Store functions receive the module context
2
+ // (`{ db, clock, config }`) and never read request scope; the renderer is pure. `next/*` imports are
3
+ // not allowed here.
4
+ export { computeContentHash, parseArticleFile } from "../content/article-file.js";
5
+ export { findArticleBySlug, findSlugRedirect, getPublishedArticle, listArticles, publishArticle, } from "../db/articles.js";
6
+ export { runBlogPublish, } from "../db/publish-run.js";
7
+ export { checkArticlesTable } from "./health.js";
8
+ export { getBlogMessages, getBlogOptions, getBlogRefreshPath, getBlogReservedSlugs, getBlogRoutes, getQualitySettings } from "./options.js";
9
+ export * from "../discovery/index.js";
10
+ export * from "../pages/index.js";
11
+ export * from "../quality/index.js";
12
+ export * from "../render/index.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,qGAAqG;AACrG,oBAAoB;AACpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAwD,MAAM,4BAA4B,CAAC;AACxI,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,cAAc,GAOf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5I,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { OgFontSource, OgFontWeight } from "../options.js";
2
+ /** A font for the card, as `ImageResponse` takes it. */
3
+ export interface OgFont {
4
+ readonly name: string;
5
+ readonly data: ArrayBuffer;
6
+ readonly weight?: OgFontWeight;
7
+ readonly style?: "normal" | "italic";
8
+ }
9
+ export type OgFontsResult = {
10
+ readonly ok: true;
11
+ readonly fonts: readonly OgFont[];
12
+ } | {
13
+ readonly ok: false;
14
+ readonly error: string;
15
+ };
16
+ export interface OgFontLoaderOptions {
17
+ /** Where a relative path starts: the app's root. */
18
+ readonly root: string;
19
+ readonly readFile?: (path: string) => Promise<Uint8Array>;
20
+ readonly fetchImpl?: typeof fetch;
21
+ }
22
+ /** A loader with its own cache, keyed by the resolved path or URL. */
23
+ export declare function createOgFontLoader(options: OgFontLoaderOptions): (fonts: readonly OgFontSource[]) => Promise<OgFontsResult>;
24
+ //# sourceMappingURL=og-fonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"og-fonts.d.ts","sourceRoot":"","sources":["../../src/server/og-fonts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEhE,wDAAwD;AACxD,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACtC;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAItI,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAwBD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,KAAK,OAAO,CAAC,aAAa,CAAC,CAmD3H"}
@@ -0,0 +1,85 @@
1
+ // The loader of the brand's fonts for the article OG card (`blog({ brand: { fonts } })`): each source
2
+ // is read once, checked to be a font Satori draws (.ttf, .otf, .woff; not .woff2), and kept for the
3
+ // life of the loader. A failed read is not kept, so a fixed file is picked up by the next call. It
4
+ // imports no Next code: the card's route (`@softure-ai/blog/next`) and `softure-blog check` share it.
5
+ import { readFile } from "node:fs/promises";
6
+ import { isAbsolute, resolve } from "node:path";
7
+ /** The first four bytes of each font format Satori parses. */
8
+ const FONT_SIGNATURES = ["wOFF", "OTTO", "\u0000\u0001\u0000\u0000", "true"];
9
+ const WOFF2_SIGNATURE = "wOF2";
10
+ function toArrayBuffer(bytes) {
11
+ const copy = new Uint8Array(bytes.byteLength);
12
+ copy.set(bytes);
13
+ return copy.buffer;
14
+ }
15
+ function checkFontBytes(bytes) {
16
+ const signature = String.fromCharCode(...bytes.subarray(0, 4));
17
+ if (signature === WOFF2_SIGNATURE)
18
+ return { ok: false, reason: "a .woff2 font, which the card cannot read; use the .woff or .ttf file" };
19
+ if (!FONT_SIGNATURES.includes(signature))
20
+ return { ok: false, reason: "not a .ttf, .otf or .woff font" };
21
+ return { ok: true, data: toArrayBuffer(bytes) };
22
+ }
23
+ function getErrorCode(error) {
24
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string")
25
+ return error.code;
26
+ return error instanceof Error ? error.message : String(error);
27
+ }
28
+ /** A loader with its own cache, keyed by the resolved path or URL. */
29
+ export function createOgFontLoader(options) {
30
+ const read = options.readFile ?? readFile;
31
+ const fetchImpl = options.fetchImpl ?? fetch;
32
+ const cache = new Map();
33
+ async function readSource(location, isUrl) {
34
+ if (!isUrl) {
35
+ try {
36
+ return checkFontBytes(await read(location));
37
+ }
38
+ catch (error) {
39
+ return { ok: false, reason: `the file cannot be read (${getErrorCode(error)})` };
40
+ }
41
+ }
42
+ let response;
43
+ try {
44
+ response = await fetchImpl(location);
45
+ }
46
+ catch (error) {
47
+ return { ok: false, reason: `the request failed (${getErrorCode(error)})` };
48
+ }
49
+ if (!response.ok)
50
+ return { ok: false, reason: `the server answered ${response.status}` };
51
+ try {
52
+ return checkFontBytes(new Uint8Array(await response.arrayBuffer()));
53
+ }
54
+ catch (error) {
55
+ return { ok: false, reason: `the answer could not be read (${getErrorCode(error)})` };
56
+ }
57
+ }
58
+ function getBytes(location, isUrl) {
59
+ const cached = cache.get(location);
60
+ if (cached !== undefined)
61
+ return cached;
62
+ const pending = readSource(location, isUrl).then((result) => {
63
+ if (!result.ok)
64
+ cache.delete(location);
65
+ return result;
66
+ });
67
+ cache.set(location, pending);
68
+ return pending;
69
+ }
70
+ return async (fonts) => {
71
+ const located = fonts.map((font) => {
72
+ const isUrl = font.src.startsWith("https://");
73
+ return { font, isUrl, location: isUrl || isAbsolute(font.src) ? font.src : resolve(options.root, font.src) };
74
+ });
75
+ const results = await Promise.all(located.map(async (entry) => ({ ...entry, result: await getBytes(entry.location, entry.isUrl) })));
76
+ const loaded = [];
77
+ for (const [index, { font, location, result }] of results.entries()) {
78
+ if (!result.ok)
79
+ return { ok: false, error: `Blog OG card: brand.fonts[${index}] "${font.src}": ${result.reason} (${location}).` };
80
+ loaded.push({ name: font.name, data: result.data, weight: font.weight, style: font.style });
81
+ }
82
+ return { ok: true, fonts: loaded };
83
+ };
84
+ }
85
+ //# sourceMappingURL=og-fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"og-fonts.js","sourceRoot":"","sources":["../../src/server/og-fonts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type SoftureConfig } from "@softure-ai/core";
2
+ import type { BlogMessages } from "../messages/index.js";
3
+ import type { BlogOptions } from "../options.js";
4
+ import { type BlogRoutes } from "../pages/paths.js";
5
+ import { type QualitySettings } from "../quality/settings.js";
6
+ export declare function getBlogOptions(config: SoftureConfig): BlogOptions;
7
+ /** The module's copy in the app's locale, with the app's overrides applied. */
8
+ export declare function getBlogMessages(config: SoftureConfig): BlogMessages;
9
+ /** The module's routes with the app's overrides applied, without trailing slashes. */
10
+ export declare function getBlogRoutes(config: SoftureConfig): BlogRoutes;
11
+ /** The path of the cache refresh route (`refreshBlogCache`), outside the pages' routes. */
12
+ export declare function getBlogRefreshPath(config: SoftureConfig): string;
13
+ /** The slugs no article may take: the app's `reservedSlugs` and the static pages under the listing. */
14
+ export declare function getBlogReservedSlugs(config: SoftureConfig): string[];
15
+ /** The quality gate's settings, or `null` when the app turned the gate off (`quality: false`). */
16
+ export declare function getQualitySettings(config: SoftureConfig): QualitySettings | null;
17
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"AACA,OAAO,EAAiD,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAatF,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAGjE;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,CAGnE;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,CAS/D;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAKhE;AAED,uGAAuG;AACvG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAGpE;AAED,kGAAkG;AAClG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG,IAAI,CAKhF"}
@@ -0,0 +1,56 @@
1
+ // The blog options, routes and copy of the running app, read from the configuration.
2
+ import { getModule, getSiteUrls } from "@softure-ai/core";
3
+ import { getReservedSlugs, normalizeRoute } from "../pages/paths.js";
4
+ import { resolveQualitySettings } from "../quality/settings.js";
5
+ const MODULE_ID = "blog";
6
+ /** The enabled module. Throws when the app did not enable it: calling its functions then is a bug. */
7
+ function getBlogModule(config) {
8
+ const module = getModule(config, MODULE_ID);
9
+ if (module === undefined) {
10
+ throw new Error("@softure-ai/blog: the module is not enabled; add blog() to modules in softure.config.ts");
11
+ }
12
+ return module;
13
+ }
14
+ export function getBlogOptions(config) {
15
+ // The module factory parsed these options with blogOptionsSchema.
16
+ return getBlogModule(config).options;
17
+ }
18
+ /** The module's copy in the app's locale, with the app's overrides applied. */
19
+ export function getBlogMessages(config) {
20
+ // The module factory merged the dictionaries; their shape is the module's own.
21
+ return getBlogModule(config).messages[config.locale];
22
+ }
23
+ /** The module's routes with the app's overrides applied, without trailing slashes. */
24
+ export function getBlogRoutes(config) {
25
+ const routes = getBlogModule(config).routes;
26
+ const read = (name) => {
27
+ const path = routes[name];
28
+ // The manifest declares every route, so a missing one means a broken module definition.
29
+ if (path === undefined)
30
+ throw new Error(`@softure-ai/blog: route "${name}" is missing from the module manifest`);
31
+ return normalizeRoute(path);
32
+ };
33
+ return { index: read("index"), glossary: read("glossary"), method: read("method"), rss: read("rss") };
34
+ }
35
+ /** The path of the cache refresh route (`refreshBlogCache`), outside the pages' routes. */
36
+ export function getBlogRefreshPath(config) {
37
+ const path = getBlogModule(config).routes.refresh;
38
+ // The manifest declares the route, so a missing one means a broken module definition.
39
+ if (path === undefined)
40
+ throw new Error('@softure-ai/blog: route "refresh" is missing from the module manifest');
41
+ return normalizeRoute(path);
42
+ }
43
+ /** The slugs no article may take: the app's `reservedSlugs` and the static pages under the listing. */
44
+ export function getBlogReservedSlugs(config) {
45
+ const options = getBlogOptions(config);
46
+ return getReservedSlugs(getBlogRoutes(config), options);
47
+ }
48
+ /** The quality gate's settings, or `null` when the app turned the gate off (`quality: false`). */
49
+ export function getQualitySettings(config) {
50
+ const { quality, images } = getBlogOptions(config);
51
+ if (quality === false)
52
+ return null;
53
+ const site = { appOrigin: config.appOrigin, siteOrigin: getSiteUrls(config).origin, timezone: config.timezone };
54
+ return resolveQualitySettings(quality, site, images ?? null);
55
+ }
56
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,OAAO,EAAE,SAAS,EAAE,WAAW,EAA6C,MAAM,kBAAkB,CAAC;AAGrG,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAmB,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAwB,MAAM,wBAAwB,CAAC;AAEtF,MAAM,SAAS,GAAG,MAAM,CAAC;AAEzB,sGAAsG;AACtG,SAAS,aAAa,CAAC,MAAqB;IAC1C,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC5C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAqB;IAClD,kEAAkE;IAClE,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,OAAsB,CAAC;AACtD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,MAAqB;IACnD,+EAA+E;IAC/E,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAiB,CAAC;AACvE,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,MAAqB;IACjD,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,IAAI,GAAG,CAAC,IAAsB,EAAU,EAAE;QAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,wFAAwF;QACxF,IAAI,IAAI,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,uCAAuC,CAAC,CAAC;QACjH,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;AACxG,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,kBAAkB,CAAC,MAAqB;IACtD,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAClD,sFAAsF;IACtF,IAAI,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;IACjH,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,oBAAoB,CAAC,MAAqB;IACxD,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,gBAAgB,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;AAC1D,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,kBAAkB,CAAC,MAAqB;IACtD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChH,OAAO,sBAAsB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type SoftureConfig } from "@softure-ai/core";
2
+ import { type BlogSitemapEntry } from "./discovery/sitemap.js";
3
+ /** The listing, the articles, the glossary, its terms and the method page, each dated by its last content change. */
4
+ export declare function readBlogSitemap(config: SoftureConfig): Promise<BlogSitemapEntry[]>;
5
+ /**
6
+ * `seo({ sitemap: { contributors: [blogSitemap()] } })`. The config is read when the sitemap is
7
+ * requested (it is still being defined when this is called); pass one to read another. A failed read
8
+ * throws; seo logs it and serves the rest. Mount `app/sitemap.ts` with `dynamic = "force-dynamic"`.
9
+ */
10
+ export declare function blogSitemap(config?: SoftureConfig): () => Promise<BlogSitemapEntry[]>;
11
+ //# sourceMappingURL=sitemap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAInE,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAGtF,qHAAqH;AACrH,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAcxF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAErF"}
@@ -0,0 +1,35 @@
1
+ // The sitemap contributor for `@softure-ai/seo`, in the root entry because `softure.config.ts` imports
2
+ // it. That file also loads in plain Node and in bundles made outside Next (`softure migrate`, the blog
3
+ // command, a container's migrate script), so nothing here may reach `next/*`: the entries come from
4
+ // one query on the shared database, not from the pages' Next cache. Crawlers read a sitemap rarely.
5
+ import { systemClock } from "@softure-ai/core";
6
+ import { getSoftureConfig } from "@softure-ai/core/next";
7
+ import { getSharedDatabase } from "@softure-ai/db";
8
+ import { listArticles } from "./db/articles.js";
9
+ import { getBlogSitemapEntries } from "./discovery/sitemap.js";
10
+ import { getBlogOptions, getBlogRoutes } from "./server/options.js";
11
+ /** The listing, the articles, the glossary, its terms and the method page, each dated by its last content change. */
12
+ export async function readBlogSitemap(config) {
13
+ if (config.database === null) {
14
+ // Unreachable for a validated config: the module has a database schema.
15
+ throw new Error("@softure-ai/blog: softure.config.ts has no database; the blog sitemap needs one");
16
+ }
17
+ const { db } = await getSharedDatabase(config.database.url);
18
+ const texts = await listArticles({ db, clock: systemClock, config });
19
+ const routes = getBlogRoutes(config);
20
+ return getBlogSitemapEntries({
21
+ articles: texts.filter((text) => text.kind === "article"),
22
+ terms: texts.filter((text) => text.kind === "term"),
23
+ routes,
24
+ methodPath: getBlogOptions(config).methodPage ? routes.method : null,
25
+ });
26
+ }
27
+ /**
28
+ * `seo({ sitemap: { contributors: [blogSitemap()] } })`. The config is read when the sitemap is
29
+ * requested (it is still being defined when this is called); pass one to read another. A failed read
30
+ * throws; seo logs it and serves the rest. Mount `app/sitemap.ts` with `dynamic = "force-dynamic"`.
31
+ */
32
+ export function blogSitemap(config) {
33
+ return () => readBlogSitemap(config ?? getSoftureConfig());
34
+ }
35
+ //# sourceMappingURL=sitemap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,uGAAuG;AACvG,oGAAoG;AACpG,oGAAoG;AACpG,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAyB,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpE,qHAAqH;AACrH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAqB;IACzD,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7B,wEAAwE;QACxE,MAAM,IAAI,KAAK,CAAC,iFAAiF,CAAC,CAAC;IACrG,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,qBAAqB,CAAC;QAC3B,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;QACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;QACnD,MAAM;QACN,UAAU,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;KACrE,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAAsB;IAChD,OAAO,GAAG,EAAE,CAAC,eAAe,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { ReactNode } from "react";
2
+ import type { BlogArticle, BlogSource } from "../contract.js";
3
+ import { type ArticleDates } from "../pages/dates.js";
4
+ import type { Crumb } from "../pages/listing.js";
5
+ import type { ArticleHeading, ArticleSegment } from "../render/render-article.js";
6
+ import type { BlogPageContext } from "./page-context.js";
7
+ export interface RenderedBody {
8
+ readonly segments: readonly ArticleSegment<ReactNode>[];
9
+ readonly headings: readonly ArticleHeading[];
10
+ readonly readingMinutes: number;
11
+ }
12
+ export interface BlogArticleViewProps {
13
+ readonly context: BlogPageContext;
14
+ readonly article: BlogArticle;
15
+ readonly dates: ArticleDates;
16
+ readonly body: RenderedBody;
17
+ readonly crumbs: readonly Crumb[];
18
+ /** From `serializeJsonLd(getArticleJsonLd(…))`. */
19
+ readonly jsonLd: string;
20
+ /** The app's call to action, right after the text. */
21
+ readonly cta?: ReactNode;
22
+ /** The app's block under the article, e.g. `<Waitlist placement="blog" />`. */
23
+ readonly afterArticle?: ReactNode;
24
+ /** "Read next" (`getRelatedArticles`); none or empty: no section. */
25
+ readonly related?: readonly Pick<BlogArticle, "id" | "slug" | "title" | "description">[];
26
+ }
27
+ /** The body in order: HTML segments from the renderer, node segments (the app's block plugins) as they are. */
28
+ export declare function ArticleBody({ segments }: {
29
+ readonly segments: readonly ArticleSegment<ReactNode>[];
30
+ }): import("react").JSX.Element;
31
+ export declare function ArticleDatesList({ dates, readingMinutes, context }: {
32
+ readonly dates: ArticleDates;
33
+ readonly readingMinutes?: number;
34
+ readonly context: BlogPageContext;
35
+ }): import("react").JSX.Element;
36
+ export declare function SourceList({ sources, heading }: {
37
+ readonly sources: readonly BlogSource[];
38
+ readonly heading: string;
39
+ }): import("react").JSX.Element | null;
40
+ /**
41
+ * "Read next": texts of the same cluster, then of others, chosen without manual lists. After the app's
42
+ * call to action: the text has just made its point, so the app's next step comes first, this second.
43
+ */
44
+ export declare function RelatedList({ articles, context }: {
45
+ readonly articles: readonly Pick<BlogArticle, "id" | "slug" | "title" | "description">[];
46
+ readonly context: BlogPageContext;
47
+ }): import("react").JSX.Element | null;
48
+ export declare function BlogArticleView({ context, article, dates, body, crumbs, jsonLd, cta, afterArticle, related }: BlogArticleViewProps): import("react").JSX.Element;
49
+ //# sourceMappingURL=blog-article.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-article.d.ts","sourceRoot":"","sources":["../../src/ui/blog-article.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;IACxD,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAClC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC,EAAE,CAAC;CAC1F;AAED,+GAA+G;AAC/G,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,CAAC,SAAS,CAAC,EAAE,CAAA;CAAE,+BAepG;AAaD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;CAAE,+BAezK;AAED,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,sCAgBrH;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;CAAE,sCAiBjL;AAqBD,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,OAAY,EAAE,EAAE,oBAAoB,+BA2CvI"}
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // The article page, answer first: crumbs, title, description and dates; then the "in short" box, the
3
+ // contents (from two level-2 headings up), the body, questions and answers, sources, the signature
4
+ // and the disclaimer; then the app's slots. Everything is server HTML: a crawler without JavaScript
5
+ // gets the whole text.
6
+ import { formatMessage } from "@softure-ai/core";
7
+ import { formatDay } from "../pages/dates.js";
8
+ import { getArticlePath } from "../pages/paths.js";
9
+ import { BlogFooterNote, BlogLayout, JsonLdScript } from "./blog-layout.js";
10
+ /** The body in order: HTML segments from the renderer, node segments (the app's block plugins) as they are. */
11
+ export function ArticleBody({ segments }) {
12
+ return (_jsx("div", { className: "blog-body", children: segments.map((segment, index) => segment.kind === "html" ? (_jsx("div", { className: "blog-segment", dangerouslySetInnerHTML: { __html: segment.html } }, index)) : (_jsx("div", { className: "blog-segment", "data-block": segment.type, children: segment.node }, index))) }));
13
+ }
14
+ function DateItem({ label, day, context }) {
15
+ return (_jsxs("div", { children: [_jsx("dt", { children: label }), _jsx("dd", { children: _jsx("time", { dateTime: day, children: formatDay(day, context.locale) }) })] }));
16
+ }
17
+ export function ArticleDatesList({ dates, readingMinutes, context }) {
18
+ const copy = context.messages.pages;
19
+ return (_jsxs("dl", { className: "blog-dates", children: [_jsx(DateItem, { label: copy.published, day: dates.published, context: context }), dates.updated === null ? null : _jsx(DateItem, { label: copy.updated, day: dates.updated, context: context }), _jsx(DateItem, { label: copy.currentAsOf, day: dates.currentAsOf, context: context }), readingMinutes === undefined ? null : (_jsxs("div", { children: [_jsx("dt", { className: "blog-visually-hidden", children: copy.readingTimeLabel }), _jsx("dd", { children: formatMessage(copy.readingTime, { minutes: readingMinutes }) })] }))] }));
20
+ }
21
+ export function SourceList({ sources, heading }) {
22
+ if (sources.length === 0)
23
+ return null;
24
+ return (_jsxs("section", { "aria-labelledby": "blog-sources", className: "blog-section blog-sources", children: [_jsx("h2", { id: "blog-sources", children: heading }), _jsx("ul", { children: sources.map((source) => (_jsx("li", { children: _jsx("a", { href: source.url, rel: "noopener noreferrer", target: "_blank", children: source.name }) }, source.url))) })] }));
25
+ }
26
+ /**
27
+ * "Read next": texts of the same cluster, then of others, chosen without manual lists. After the app's
28
+ * call to action: the text has just made its point, so the app's next step comes first, this second.
29
+ */
30
+ export function RelatedList({ articles, context }) {
31
+ if (articles.length === 0)
32
+ return null;
33
+ return (_jsxs("section", { "aria-labelledby": "blog-related", className: "blog-section blog-related", children: [_jsx("h2", { id: "blog-related", children: context.messages.pages.readNext }), _jsx("ul", { children: articles.map((article) => (_jsxs("li", { children: [_jsx("h3", { children: _jsx("a", { href: getArticlePath(context.routes, article.slug), children: article.title }) }), _jsx("p", { children: article.description })] }, article.id))) })] }));
34
+ }
35
+ function Contents({ headings, label }) {
36
+ const sections = headings.filter((heading) => heading.level === 2);
37
+ if (sections.length < 2)
38
+ return null;
39
+ return (_jsxs("nav", { "aria-labelledby": "blog-contents", className: "blog-contents", children: [_jsx("p", { id: "blog-contents", className: "blog-contents-title", children: label }), _jsx("ol", { children: sections.map((heading) => (_jsx("li", { children: _jsx("a", { href: `#${heading.id}`, children: heading.text }) }, heading.id))) })] }));
40
+ }
41
+ export function BlogArticleView({ context, article, dates, body, crumbs, jsonLd, cta, afterArticle, related = [] }) {
42
+ const copy = context.messages.pages;
43
+ return (_jsxs(BlogLayout, { context: context, title: article.title, lead: article.description, crumbs: crumbs, meta: _jsx(ArticleDatesList, { dates: dates, readingMinutes: body.readingMinutes, context: context }), children: [_jsx(JsonLdScript, { json: jsonLd }), _jsxs("div", { className: "blog-article-layout", children: [_jsx(Contents, { headings: body.headings, label: copy.contents }), _jsxs("article", { className: "blog-article", children: [article.summary === null ? null : (_jsxs("aside", { "aria-labelledby": "blog-summary", className: "blog-summary", children: [_jsx("h2", { id: "blog-summary", children: copy.summary }), _jsx("p", { children: article.summary })] })), _jsx(ArticleBody, { segments: body.segments }), article.faq.length === 0 ? null : (_jsxs("section", { "aria-labelledby": "blog-faq", className: "blog-section blog-faq", children: [_jsx("h2", { id: "blog-faq", children: copy.faq }), _jsx("dl", { children: article.faq.map((entry) => (_jsxs("div", { children: [_jsx("dt", { children: entry.question }), _jsx("dd", { children: entry.answer })] }, entry.question))) })] })), _jsx(SourceList, { sources: article.sources, heading: copy.sources }), _jsx(BlogFooterNote, { context: context })] })] }), cta === undefined ? null : _jsx("div", { className: "blog-slot", children: cta }), _jsx(RelatedList, { articles: related, context: context }), afterArticle === undefined ? null : _jsx("div", { className: "blog-slot", children: afterArticle })] }));
44
+ }
45
+ //# sourceMappingURL=blog-article.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-article.js","sourceRoot":"","sources":["../../src/ui/blog-article.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { ReactNode } from "react";
2
+ import type { BlogArticle } from "../contract.js";
3
+ import { type ArticleDates } from "../pages/dates.js";
4
+ import type { Crumb } from "../pages/listing.js";
5
+ import { type RenderedBody } from "./blog-article.js";
6
+ import type { BlogPageContext } from "./page-context.js";
7
+ export interface GlossaryIndexViewProps {
8
+ readonly context: BlogPageContext;
9
+ /** Already sorted (`sortTerms`). */
10
+ readonly terms: readonly BlogArticle[];
11
+ /** `null` with no terms: an empty set says nothing. */
12
+ readonly jsonLd: string | null;
13
+ }
14
+ export declare function GlossaryIndexView({ context, terms, jsonLd }: GlossaryIndexViewProps): import("react").JSX.Element;
15
+ export interface GlossaryTermViewProps {
16
+ readonly context: BlogPageContext;
17
+ readonly term: BlogArticle;
18
+ readonly dates: ArticleDates;
19
+ readonly body: RenderedBody;
20
+ readonly crumbs: readonly Crumb[];
21
+ readonly jsonLd: string;
22
+ /** Published articles whose text links this term. */
23
+ readonly articles: readonly BlogArticle[];
24
+ readonly cta?: ReactNode;
25
+ }
26
+ export declare function GlossaryTermView({ context, term, dates, body, crumbs, jsonLd, articles, cta }: GlossaryTermViewProps): import("react").JSX.Element;
27
+ //# sourceMappingURL=blog-glossary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-glossary.d.ts","sourceRoot":"","sources":["../../src/ui/blog-glossary.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,oCAAoC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,iBAAiB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,sBAAsB,+BA0BnF;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;CAC1B;AAED,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,qBAAqB,+BA4CpH"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { formatDay } from "../pages/dates.js";
3
+ import { getArticlePath, getTermPath } from "../pages/paths.js";
4
+ import { ArticleBody, SourceList } from "./blog-article.js";
5
+ import { BlogFooterNote, BlogLayout, JsonLdScript } from "./blog-layout.js";
6
+ export function GlossaryIndexView({ context, terms, jsonLd }) {
7
+ const copy = context.messages.glossary;
8
+ const crumbs = [
9
+ { name: context.messages.pages.blogTitle, path: context.routes.index },
10
+ { name: copy.title, path: context.routes.glossary },
11
+ ];
12
+ return (_jsxs(BlogLayout, { context: context, title: copy.title, lead: copy.description, crumbs: crumbs, children: [jsonLd === null ? null : _jsx(JsonLdScript, { json: jsonLd }), terms.length === 0 ? (_jsx("p", { className: "blog-empty", children: copy.empty })) : (_jsx("dl", { className: "blog-terms", children: terms.map((term) => (_jsxs("div", { children: [_jsx("dt", { children: _jsx("a", { href: getTermPath(context.routes, term.slug), children: term.title }) }), _jsx("dd", { children: term.description })] }, term.id))) })), _jsx(BlogFooterNote, { context: context })] }));
13
+ }
14
+ export function GlossaryTermView({ context, term, dates, body, crumbs, jsonLd, articles, cta }) {
15
+ const pages = context.messages.pages;
16
+ const copy = context.messages.glossary;
17
+ const meta = (_jsxs("dl", { className: "blog-dates", children: [_jsxs("div", { children: [_jsx("dt", { children: pages.currentAsOf }), _jsx("dd", { children: _jsx("time", { dateTime: dates.currentAsOf, children: formatDay(dates.currentAsOf, context.locale) }) })] }), dates.updated === null ? null : (_jsxs("div", { children: [_jsx("dt", { children: pages.updated }), _jsx("dd", { children: _jsx("time", { dateTime: dates.updated, children: formatDay(dates.updated, context.locale) }) })] }))] }));
18
+ return (_jsxs(BlogLayout, { context: context, title: term.title, lead: term.description, crumbs: crumbs, meta: meta, children: [_jsx(JsonLdScript, { json: jsonLd }), _jsxs("article", { className: "blog-article", children: [_jsx(ArticleBody, { segments: body.segments }), _jsx(SourceList, { sources: term.sources, heading: pages.sources }), articles.length === 0 ? null : (_jsxs("section", { "aria-labelledby": "blog-explained-in", className: "blog-section blog-explained-in", children: [_jsx("h2", { id: "blog-explained-in", children: copy.explainedIn }), _jsx("ul", { children: articles.map((article) => (_jsx("li", { children: _jsx("a", { href: getArticlePath(context.routes, article.slug), children: article.title }) }, article.id))) })] })), _jsx(BlogFooterNote, { context: context, extraLink: { href: context.routes.glossary, label: copy.allTerms } })] }), cta === undefined ? null : _jsx("div", { className: "blog-slot", children: cta })] }));
19
+ }
20
+ //# sourceMappingURL=blog-glossary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-glossary.js","sourceRoot":"","sources":["../../src/ui/blog-glossary.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from "react";
2
+ import type { Crumb } from "../pages/listing.js";
3
+ import type { BlogPageContext } from "./page-context.js";
4
+ export interface BlogLayoutProps {
5
+ readonly context: BlogPageContext;
6
+ readonly title: string;
7
+ readonly lead?: string;
8
+ /** Crumbs up to the page itself; the last one is announced, not shown. */
9
+ readonly crumbs?: readonly Crumb[];
10
+ /** Under the title: dates, reading time. */
11
+ readonly meta?: ReactNode;
12
+ readonly children: ReactNode;
13
+ }
14
+ export declare function Breadcrumbs({ crumbs, label }: {
15
+ readonly crumbs: readonly Crumb[];
16
+ readonly label: string;
17
+ }): import("react").JSX.Element;
18
+ export declare function BlogLayout({ context, title, lead, crumbs, meta, children }: BlogLayoutProps): import("react").JSX.Element;
19
+ /** The signature and the method link under a text, then the disclaimer. */
20
+ export declare function BlogFooterNote({ context, extraLink }: {
21
+ readonly context: BlogPageContext;
22
+ readonly extraLink?: {
23
+ readonly href: string;
24
+ readonly label: string;
25
+ };
26
+ }): import("react").JSX.Element;
27
+ /** Structured data for search engines and assistants; `json` comes from `serializeJsonLd`. */
28
+ export declare function JsonLdScript({ json }: {
29
+ readonly json: string;
30
+ }): import("react").JSX.Element;
31
+ //# sourceMappingURL=blog-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-layout.d.ts","sourceRoot":"","sources":["../../src/ui/blog-layout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;IACnC,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,+BAqB3G;AAED,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,eAAe,+BAY3F;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,+BA2BnK;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,+BAE/D"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // The frame of every blog page: crumbs, the title with its lead, an optional line under it (dates),
3
+ // then the content. Links are plain anchors: the pages render on the server and need no client router.
4
+ import { formatMessage } from "@softure-ai/core";
5
+ export function Breadcrumbs({ crumbs, label }) {
6
+ return (_jsx("nav", { "aria-label": label, className: "blog-crumbs", children: _jsx("ol", { children: crumbs.map((crumb, index) => index === crumbs.length - 1 ? (_jsx("li", { className: "blog-visually-hidden", "aria-current": "page", children: crumb.name }, crumb.path)) : (_jsxs("li", { children: [_jsx("a", { href: crumb.path, children: crumb.name }), _jsx("span", { "aria-hidden": "true", className: "blog-crumbs-separator", children: "›" })] }, crumb.path))) }) }));
7
+ }
8
+ export function BlogLayout({ context, title, lead, crumbs, meta, children }) {
9
+ return (_jsxs("main", { className: "blog-page", children: [_jsxs("header", { className: "blog-header", children: [crumbs !== undefined && crumbs.length > 1 ? _jsx(Breadcrumbs, { crumbs: crumbs, label: context.messages.pages.breadcrumbs }) : null, _jsx("h1", { className: "blog-title", children: title }), lead === undefined ? null : _jsx("p", { className: "blog-lead", children: lead }), meta] }), children] }));
10
+ }
11
+ /** The signature and the method link under a text, then the disclaimer. */
12
+ export function BlogFooterNote({ context, extraLink }) {
13
+ const copy = context.messages.pages;
14
+ const links = [
15
+ ...(extraLink === undefined ? [] : [extraLink]),
16
+ ...(context.methodPath === null ? [] : [{ href: context.methodPath, label: copy.methodLink }]),
17
+ ];
18
+ const hasSignature = context.brand !== null;
19
+ return (_jsxs("footer", { className: "blog-footer-note", children: [hasSignature || links.length > 0 ? (_jsxs("p", { className: "blog-signature", children: [context.brand === null ? null : _jsx("strong", { children: formatMessage(copy.signature, { brand: context.brand }) }), links.map((link, index) => (_jsxs("span", { children: [hasSignature || index > 0 ? _jsx("span", { "aria-hidden": "true", children: " · " }) : null, _jsx("a", { href: link.href, children: link.label })] }, link.href)))] })) : null, context.disclaimer === null ? null : (_jsx("aside", { "aria-label": copy.disclaimerLabel, className: "blog-disclaimer", children: _jsx("p", { children: context.disclaimer }) }))] }));
20
+ }
21
+ /** Structured data for search engines and assistants; `json` comes from `serializeJsonLd`. */
22
+ export function JsonLdScript({ json }) {
23
+ return _jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: json } });
24
+ }
25
+ //# sourceMappingURL=blog-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-layout.js","sourceRoot":"","sources":["../../src/ui/blog-layout.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from "react";
2
+ import type { BlogArticle } from "../contract.js";
3
+ import { type ClusterGroup } from "../pages/listing.js";
4
+ import type { BlogPageContext } from "./page-context.js";
5
+ export interface BlogListingViewProps {
6
+ readonly context: BlogPageContext;
7
+ readonly groups: readonly ClusterGroup<BlogArticle>[];
8
+ /** Published glossary terms; the glossary link shows when there is at least one. */
9
+ readonly termCount: number;
10
+ readonly timezone: string;
11
+ /** The app's call to action under the cards. */
12
+ readonly cta?: ReactNode;
13
+ }
14
+ export declare function BlogListingView({ context, groups, termCount, timezone, cta }: BlogListingViewProps): import("react").JSX.Element;
15
+ //# sourceMappingURL=blog-listing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-listing.d.ts","sourceRoot":"","sources":["../../src/ui/blog-listing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAI1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;IACtD,oFAAoF;IACpF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;CAC1B;AA+BD,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,oBAAoB,+BA0ClG"}