@softure-ai/blog 0.0.0-stage → 0.1.6

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 (419) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +604 -2
  4. package/dist/cli/bin.d.ts +3 -0
  5. package/dist/cli/bin.d.ts.map +1 -0
  6. package/dist/cli/bin.js +5 -0
  7. package/dist/cli/bin.js.map +1 -0
  8. package/dist/cli/command.d.ts +9 -0
  9. package/dist/cli/command.d.ts.map +1 -0
  10. package/dist/cli/command.js +40 -0
  11. package/dist/cli/command.js.map +1 -0
  12. package/dist/cli/index.d.ts +4 -0
  13. package/dist/cli/index.d.ts.map +1 -0
  14. package/dist/cli/index.js +5 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/cli/run.d.ts +62 -0
  17. package/dist/cli/run.d.ts.map +1 -0
  18. package/dist/cli/run.js +555 -0
  19. package/dist/cli/run.js.map +1 -0
  20. package/dist/cli/skill.d.ts +47 -0
  21. package/dist/cli/skill.d.ts.map +1 -0
  22. package/dist/cli/skill.js +221 -0
  23. package/dist/cli/skill.js.map +1 -0
  24. package/dist/content/article-file.d.ts +27 -0
  25. package/dist/content/article-file.d.ts.map +1 -0
  26. package/dist/content/article-file.js +179 -0
  27. package/dist/content/article-file.js.map +1 -0
  28. package/dist/contract.d.ts +77 -0
  29. package/dist/contract.d.ts.map +1 -0
  30. package/dist/contract.js +5 -0
  31. package/dist/contract.js.map +1 -0
  32. package/dist/db/articles.d.ts +30 -0
  33. package/dist/db/articles.d.ts.map +1 -0
  34. package/dist/db/articles.js +138 -0
  35. package/dist/db/articles.js.map +1 -0
  36. package/dist/db/publish-run.d.ts +50 -0
  37. package/dist/db/publish-run.d.ts.map +1 -0
  38. package/dist/db/publish-run.js +168 -0
  39. package/dist/db/publish-run.js.map +1 -0
  40. package/dist/db/schema.d.ts +403 -0
  41. package/dist/db/schema.d.ts.map +1 -0
  42. package/dist/db/schema.js +33 -0
  43. package/dist/db/schema.js.map +1 -0
  44. package/dist/discovery/dates.d.ts +9 -0
  45. package/dist/discovery/dates.d.ts.map +1 -0
  46. package/dist/discovery/dates.js +19 -0
  47. package/dist/discovery/dates.js.map +1 -0
  48. package/dist/discovery/index.d.ts +8 -0
  49. package/dist/discovery/index.d.ts.map +1 -0
  50. package/dist/discovery/index.js +11 -0
  51. package/dist/discovery/index.js.map +1 -0
  52. package/dist/discovery/indexnow.d.ts +11 -0
  53. package/dist/discovery/indexnow.d.ts.map +1 -0
  54. package/dist/discovery/indexnow.js +23 -0
  55. package/dist/discovery/indexnow.js.map +1 -0
  56. package/dist/discovery/refresh.d.ts +49 -0
  57. package/dist/discovery/refresh.d.ts.map +1 -0
  58. package/dist/discovery/refresh.js +55 -0
  59. package/dist/discovery/refresh.js.map +1 -0
  60. package/dist/discovery/related.d.ts +17 -0
  61. package/dist/discovery/related.d.ts.map +1 -0
  62. package/dist/discovery/related.js +28 -0
  63. package/dist/discovery/related.js.map +1 -0
  64. package/dist/discovery/rss.d.ts +30 -0
  65. package/dist/discovery/rss.d.ts.map +1 -0
  66. package/dist/discovery/rss.js +48 -0
  67. package/dist/discovery/rss.js.map +1 -0
  68. package/dist/discovery/sitemap.d.ts +25 -0
  69. package/dist/discovery/sitemap.d.ts.map +1 -0
  70. package/dist/discovery/sitemap.js +31 -0
  71. package/dist/discovery/sitemap.js.map +1 -0
  72. package/dist/discovery/submit.d.ts +44 -0
  73. package/dist/discovery/submit.d.ts.map +1 -0
  74. package/dist/discovery/submit.js +46 -0
  75. package/dist/discovery/submit.js.map +1 -0
  76. package/dist/index.d.ts +371 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +69 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/messages/en.d.ts +83 -0
  81. package/dist/messages/en.d.ts.map +1 -0
  82. package/dist/messages/en.js +83 -0
  83. package/dist/messages/en.js.map +1 -0
  84. package/dist/messages/index.d.ts +158 -0
  85. package/dist/messages/index.d.ts.map +1 -0
  86. package/dist/messages/index.js +5 -0
  87. package/dist/messages/index.js.map +1 -0
  88. package/dist/messages/pl.d.ts +78 -0
  89. package/dist/messages/pl.d.ts.map +1 -0
  90. package/dist/messages/pl.js +78 -0
  91. package/dist/messages/pl.js.map +1 -0
  92. package/dist/next/context.d.ts +6 -0
  93. package/dist/next/context.d.ts.map +1 -0
  94. package/dist/next/context.js +27 -0
  95. package/dist/next/context.js.map +1 -0
  96. package/dist/next/data.d.ts +11 -0
  97. package/dist/next/data.d.ts.map +1 -0
  98. package/dist/next/data.js +40 -0
  99. package/dist/next/data.js.map +1 -0
  100. package/dist/next/discovery.d.ts +6 -0
  101. package/dist/next/discovery.d.ts.map +1 -0
  102. package/dist/next/discovery.js +49 -0
  103. package/dist/next/discovery.js.map +1 -0
  104. package/dist/next/index.d.ts +9 -0
  105. package/dist/next/index.d.ts.map +1 -0
  106. package/dist/next/index.js +10 -0
  107. package/dist/next/index.js.map +1 -0
  108. package/dist/next/og-fonts.d.ts +6 -0
  109. package/dist/next/og-fonts.d.ts.map +1 -0
  110. package/dist/next/og-fonts.js +8 -0
  111. package/dist/next/og-fonts.js.map +1 -0
  112. package/dist/next/og-image.d.ts +36 -0
  113. package/dist/next/og-image.d.ts.map +1 -0
  114. package/dist/next/og-image.js +77 -0
  115. package/dist/next/og-image.js.map +1 -0
  116. package/dist/next/pages.d.ts +45 -0
  117. package/dist/next/pages.d.ts.map +1 -0
  118. package/dist/next/pages.js +189 -0
  119. package/dist/next/pages.js.map +1 -0
  120. package/dist/next/refresh.d.ts +9 -0
  121. package/dist/next/refresh.d.ts.map +1 -0
  122. package/dist/next/refresh.js +81 -0
  123. package/dist/next/refresh.js.map +1 -0
  124. package/dist/options.d.ts +320 -0
  125. package/dist/options.d.ts.map +1 -0
  126. package/dist/options.js +184 -0
  127. package/dist/options.js.map +1 -0
  128. package/dist/pages/body.d.ts +18 -0
  129. package/dist/pages/body.d.ts.map +1 -0
  130. package/dist/pages/body.js +21 -0
  131. package/dist/pages/body.js.map +1 -0
  132. package/dist/pages/dates.d.ts +21 -0
  133. package/dist/pages/dates.d.ts.map +1 -0
  134. package/dist/pages/dates.js +24 -0
  135. package/dist/pages/dates.js.map +1 -0
  136. package/dist/pages/index.d.ts +7 -0
  137. package/dist/pages/index.d.ts.map +1 -0
  138. package/dist/pages/index.js +9 -0
  139. package/dist/pages/index.js.map +1 -0
  140. package/dist/pages/json-ld.d.ts +33 -0
  141. package/dist/pages/json-ld.d.ts.map +1 -0
  142. package/dist/pages/json-ld.js +95 -0
  143. package/dist/pages/json-ld.js.map +1 -0
  144. package/dist/pages/listing.d.ts +46 -0
  145. package/dist/pages/listing.d.ts.map +1 -0
  146. package/dist/pages/listing.js +57 -0
  147. package/dist/pages/listing.js.map +1 -0
  148. package/dist/pages/paths.d.ts +37 -0
  149. package/dist/pages/paths.d.ts.map +1 -0
  150. package/dist/pages/paths.js +61 -0
  151. package/dist/pages/paths.js.map +1 -0
  152. package/dist/pages/redirects.d.ts +57 -0
  153. package/dist/pages/redirects.d.ts.map +1 -0
  154. package/dist/pages/redirects.js +78 -0
  155. package/dist/pages/redirects.js.map +1 -0
  156. package/dist/proxy/index.d.ts +15 -0
  157. package/dist/proxy/index.d.ts.map +1 -0
  158. package/dist/proxy/index.js +60 -0
  159. package/dist/proxy/index.js.map +1 -0
  160. package/dist/quality/blocks.d.ts +21 -0
  161. package/dist/quality/blocks.d.ts.map +1 -0
  162. package/dist/quality/blocks.js +113 -0
  163. package/dist/quality/blocks.js.map +1 -0
  164. package/dist/quality/catalog.d.ts +10 -0
  165. package/dist/quality/catalog.d.ts.map +1 -0
  166. package/dist/quality/catalog.js +71 -0
  167. package/dist/quality/catalog.js.map +1 -0
  168. package/dist/quality/check-article.d.ts +34 -0
  169. package/dist/quality/check-article.d.ts.map +1 -0
  170. package/dist/quality/check-article.js +74 -0
  171. package/dist/quality/check-article.js.map +1 -0
  172. package/dist/quality/check-files.d.ts +23 -0
  173. package/dist/quality/check-files.d.ts.map +1 -0
  174. package/dist/quality/check-files.js +44 -0
  175. package/dist/quality/check-files.js.map +1 -0
  176. package/dist/quality/external-links.d.ts +19 -0
  177. package/dist/quality/external-links.d.ts.map +1 -0
  178. package/dist/quality/external-links.js +30 -0
  179. package/dist/quality/external-links.js.map +1 -0
  180. package/dist/quality/finding.d.ts +17 -0
  181. package/dist/quality/finding.d.ts.map +1 -0
  182. package/dist/quality/finding.js +15 -0
  183. package/dist/quality/finding.js.map +1 -0
  184. package/dist/quality/gate.d.ts +5 -0
  185. package/dist/quality/gate.d.ts.map +1 -0
  186. package/dist/quality/gate.js +10 -0
  187. package/dist/quality/gate.js.map +1 -0
  188. package/dist/quality/index.d.ts +15 -0
  189. package/dist/quality/index.d.ts.map +1 -0
  190. package/dist/quality/index.js +16 -0
  191. package/dist/quality/index.js.map +1 -0
  192. package/dist/quality/link-targets.d.ts +44 -0
  193. package/dist/quality/link-targets.d.ts.map +1 -0
  194. package/dist/quality/link-targets.js +91 -0
  195. package/dist/quality/link-targets.js.map +1 -0
  196. package/dist/quality/options.d.ts +113 -0
  197. package/dist/quality/options.d.ts.map +1 -0
  198. package/dist/quality/options.js +93 -0
  199. package/dist/quality/options.js.map +1 -0
  200. package/dist/quality/plugin.d.ts +34 -0
  201. package/dist/quality/plugin.d.ts.map +1 -0
  202. package/dist/quality/plugin.js +7 -0
  203. package/dist/quality/plugin.js.map +1 -0
  204. package/dist/quality/rules/blocks.d.ts +5 -0
  205. package/dist/quality/rules/blocks.d.ts.map +1 -0
  206. package/dist/quality/rules/blocks.js +35 -0
  207. package/dist/quality/rules/blocks.js.map +1 -0
  208. package/dist/quality/rules/images.d.ts +4 -0
  209. package/dist/quality/rules/images.d.ts.map +1 -0
  210. package/dist/quality/rules/images.js +33 -0
  211. package/dist/quality/rules/images.js.map +1 -0
  212. package/dist/quality/rules/input.d.ts +11 -0
  213. package/dist/quality/rules/input.d.ts.map +1 -0
  214. package/dist/quality/rules/input.js +2 -0
  215. package/dist/quality/rules/input.js.map +1 -0
  216. package/dist/quality/rules/links.d.ts +17 -0
  217. package/dist/quality/rules/links.d.ts.map +1 -0
  218. package/dist/quality/rules/links.js +46 -0
  219. package/dist/quality/rules/links.js.map +1 -0
  220. package/dist/quality/rules/structure.d.ts +13 -0
  221. package/dist/quality/rules/structure.d.ts.map +1 -0
  222. package/dist/quality/rules/structure.js +139 -0
  223. package/dist/quality/rules/structure.js.map +1 -0
  224. package/dist/quality/rules/style.d.ts +11 -0
  225. package/dist/quality/rules/style.d.ts.map +1 -0
  226. package/dist/quality/rules/style.js +129 -0
  227. package/dist/quality/rules/style.js.map +1 -0
  228. package/dist/quality/rules/ymyl.d.ts +5 -0
  229. package/dist/quality/rules/ymyl.d.ts.map +1 -0
  230. package/dist/quality/rules/ymyl.js +51 -0
  231. package/dist/quality/rules/ymyl.js.map +1 -0
  232. package/dist/quality/rulesets/en/ruleset.d.ts +3 -0
  233. package/dist/quality/rulesets/en/ruleset.d.ts.map +1 -0
  234. package/dist/quality/rulesets/en/ruleset.js +106 -0
  235. package/dist/quality/rulesets/en/ruleset.js.map +1 -0
  236. package/dist/quality/rulesets/index.d.ts +7 -0
  237. package/dist/quality/rulesets/index.d.ts.map +1 -0
  238. package/dist/quality/rulesets/index.js +7 -0
  239. package/dist/quality/rulesets/index.js.map +1 -0
  240. package/dist/quality/rulesets/pl/ruleset.d.ts +3 -0
  241. package/dist/quality/rulesets/pl/ruleset.d.ts.map +1 -0
  242. package/dist/quality/rulesets/pl/ruleset.js +114 -0
  243. package/dist/quality/rulesets/pl/ruleset.js.map +1 -0
  244. package/dist/quality/rulesets/types.d.ts +28 -0
  245. package/dist/quality/rulesets/types.d.ts.map +1 -0
  246. package/dist/quality/rulesets/types.js +2 -0
  247. package/dist/quality/rulesets/types.js.map +1 -0
  248. package/dist/quality/settings.d.ts +26 -0
  249. package/dist/quality/settings.d.ts.map +1 -0
  250. package/dist/quality/settings.js +32 -0
  251. package/dist/quality/settings.js.map +1 -0
  252. package/dist/quality/text.d.ts +43 -0
  253. package/dist/quality/text.d.ts.map +1 -0
  254. package/dist/quality/text.js +85 -0
  255. package/dist/quality/text.js.map +1 -0
  256. package/dist/render/glossary.d.ts +27 -0
  257. package/dist/render/glossary.d.ts.map +1 -0
  258. package/dist/render/glossary.js +71 -0
  259. package/dist/render/glossary.js.map +1 -0
  260. package/dist/render/images.d.ts +42 -0
  261. package/dist/render/images.d.ts.map +1 -0
  262. package/dist/render/images.js +84 -0
  263. package/dist/render/images.js.map +1 -0
  264. package/dist/render/index.d.ts +6 -0
  265. package/dist/render/index.d.ts.map +1 -0
  266. package/dist/render/index.js +8 -0
  267. package/dist/render/index.js.map +1 -0
  268. package/dist/render/reading-time.d.ts +8 -0
  269. package/dist/render/reading-time.d.ts.map +1 -0
  270. package/dist/render/reading-time.js +17 -0
  271. package/dist/render/reading-time.js.map +1 -0
  272. package/dist/render/render-article.d.ts +97 -0
  273. package/dist/render/render-article.d.ts.map +1 -0
  274. package/dist/render/render-article.js +373 -0
  275. package/dist/render/render-article.js.map +1 -0
  276. package/dist/render/slugify-heading.d.ts +2 -0
  277. package/dist/render/slugify-heading.d.ts.map +1 -0
  278. package/dist/render/slugify-heading.js +21 -0
  279. package/dist/render/slugify-heading.js.map +1 -0
  280. package/dist/server/health.d.ts +3 -0
  281. package/dist/server/health.d.ts.map +1 -0
  282. package/dist/server/health.js +11 -0
  283. package/dist/server/health.js.map +1 -0
  284. package/dist/server/index.d.ts +10 -0
  285. package/dist/server/index.d.ts.map +1 -0
  286. package/dist/server/index.js +13 -0
  287. package/dist/server/index.js.map +1 -0
  288. package/dist/server/og-fonts.d.ts +24 -0
  289. package/dist/server/og-fonts.d.ts.map +1 -0
  290. package/dist/server/og-fonts.js +85 -0
  291. package/dist/server/og-fonts.js.map +1 -0
  292. package/dist/server/options.d.ts +17 -0
  293. package/dist/server/options.d.ts.map +1 -0
  294. package/dist/server/options.js +56 -0
  295. package/dist/server/options.js.map +1 -0
  296. package/dist/sitemap.d.ts +11 -0
  297. package/dist/sitemap.d.ts.map +1 -0
  298. package/dist/sitemap.js +35 -0
  299. package/dist/sitemap.js.map +1 -0
  300. package/dist/ui/blog-article.d.ts +49 -0
  301. package/dist/ui/blog-article.d.ts.map +1 -0
  302. package/dist/ui/blog-article.js +45 -0
  303. package/dist/ui/blog-article.js.map +1 -0
  304. package/dist/ui/blog-glossary.d.ts +27 -0
  305. package/dist/ui/blog-glossary.d.ts.map +1 -0
  306. package/dist/ui/blog-glossary.js +20 -0
  307. package/dist/ui/blog-glossary.js.map +1 -0
  308. package/dist/ui/blog-layout.d.ts +31 -0
  309. package/dist/ui/blog-layout.d.ts.map +1 -0
  310. package/dist/ui/blog-layout.js +25 -0
  311. package/dist/ui/blog-layout.js.map +1 -0
  312. package/dist/ui/blog-listing.d.ts +15 -0
  313. package/dist/ui/blog-listing.d.ts.map +1 -0
  314. package/dist/ui/blog-listing.js +29 -0
  315. package/dist/ui/blog-listing.js.map +1 -0
  316. package/dist/ui/blog-method.d.ts +5 -0
  317. package/dist/ui/blog-method.d.ts.map +1 -0
  318. package/dist/ui/blog-method.js +21 -0
  319. package/dist/ui/blog-method.js.map +1 -0
  320. package/dist/ui/index.d.ts +7 -0
  321. package/dist/ui/index.d.ts.map +1 -0
  322. package/dist/ui/index.js +8 -0
  323. package/dist/ui/index.js.map +1 -0
  324. package/dist/ui/page-context.d.ts +15 -0
  325. package/dist/ui/page-context.d.ts.map +1 -0
  326. package/dist/ui/page-context.js +2 -0
  327. package/dist/ui/page-context.js.map +1 -0
  328. package/migrations/0001_create_articles.sql +67 -0
  329. package/migrations/README.md +8 -0
  330. package/module.json +28 -0
  331. package/package.json +102 -4
  332. package/skill/SKILL.md +72 -0
  333. package/skill/references/reviewer.md +39 -0
  334. package/skill/references/rules.md +122 -0
  335. package/skill/references/structure.md +69 -0
  336. package/skill/references/template.md +86 -0
  337. package/src/cli/bin.ts +5 -0
  338. package/src/cli/command.ts +49 -0
  339. package/src/cli/index.ts +20 -0
  340. package/src/cli/run.ts +591 -0
  341. package/src/cli/skill.ts +242 -0
  342. package/src/content/article-file.ts +184 -0
  343. package/src/contract.ts +88 -0
  344. package/src/db/articles.ts +160 -0
  345. package/src/db/publish-run.ts +224 -0
  346. package/src/db/schema.ts +36 -0
  347. package/src/discovery/dates.ts +27 -0
  348. package/src/discovery/index.ts +18 -0
  349. package/src/discovery/indexnow.ts +25 -0
  350. package/src/discovery/refresh.ts +79 -0
  351. package/src/discovery/related.ts +35 -0
  352. package/src/discovery/rss.ts +77 -0
  353. package/src/discovery/sitemap.ts +59 -0
  354. package/src/discovery/submit.ts +69 -0
  355. package/src/index.ts +99 -0
  356. package/src/messages/en.ts +82 -0
  357. package/src/messages/index.ts +7 -0
  358. package/src/messages/pl.ts +77 -0
  359. package/src/next/context.ts +30 -0
  360. package/src/next/data.ts +55 -0
  361. package/src/next/discovery.ts +49 -0
  362. package/src/next/index.ts +25 -0
  363. package/src/next/next-modules.d.ts +15 -0
  364. package/src/next/og-fonts.ts +14 -0
  365. package/src/next/og-image.tsx +108 -0
  366. package/src/next/pages.tsx +251 -0
  367. package/src/next/refresh.ts +85 -0
  368. package/src/options.ts +224 -0
  369. package/src/pages/body.ts +38 -0
  370. package/src/pages/dates.ts +39 -0
  371. package/src/pages/index.ts +37 -0
  372. package/src/pages/json-ld.ts +120 -0
  373. package/src/pages/listing.ts +95 -0
  374. package/src/pages/paths.ts +84 -0
  375. package/src/pages/redirects.ts +120 -0
  376. package/src/proxy/index.ts +66 -0
  377. package/src/quality/blocks.ts +130 -0
  378. package/src/quality/catalog.ts +86 -0
  379. package/src/quality/check-article.ts +112 -0
  380. package/src/quality/check-files.ts +63 -0
  381. package/src/quality/external-links.ts +37 -0
  382. package/src/quality/finding.ts +29 -0
  383. package/src/quality/gate.ts +15 -0
  384. package/src/quality/index.ts +28 -0
  385. package/src/quality/link-targets.ts +107 -0
  386. package/src/quality/options.ts +105 -0
  387. package/src/quality/plugin.ts +43 -0
  388. package/src/quality/rules/blocks.ts +43 -0
  389. package/src/quality/rules/images.ts +34 -0
  390. package/src/quality/rules/input.ts +12 -0
  391. package/src/quality/rules/links.ts +56 -0
  392. package/src/quality/rules/structure.ts +137 -0
  393. package/src/quality/rules/style.ts +137 -0
  394. package/src/quality/rules/ymyl.ts +57 -0
  395. package/src/quality/rulesets/en/ruleset.ts +117 -0
  396. package/src/quality/rulesets/index.ts +9 -0
  397. package/src/quality/rulesets/pl/ruleset.ts +126 -0
  398. package/src/quality/rulesets/types.ts +31 -0
  399. package/src/quality/settings.ts +60 -0
  400. package/src/quality/text.ts +113 -0
  401. package/src/render/glossary.ts +103 -0
  402. package/src/render/images.ts +110 -0
  403. package/src/render/index.ts +29 -0
  404. package/src/render/reading-time.ts +18 -0
  405. package/src/render/render-article.ts +487 -0
  406. package/src/render/slugify-heading.ts +22 -0
  407. package/src/server/health.ts +12 -0
  408. package/src/server/index.ts +28 -0
  409. package/src/server/og-fonts.ts +102 -0
  410. package/src/server/options.ts +62 -0
  411. package/src/sitemap.ts +36 -0
  412. package/src/ui/blog-article.tsx +186 -0
  413. package/src/ui/blog-glossary.tsx +104 -0
  414. package/src/ui/blog-layout.tsx +89 -0
  415. package/src/ui/blog-listing.tsx +95 -0
  416. package/src/ui/blog-method.tsx +34 -0
  417. package/src/ui/index.ts +8 -0
  418. package/src/ui/page-context.ts +17 -0
  419. package/styles.css +498 -0
@@ -0,0 +1,15 @@
1
+ export { listQualityRules, QUALITY_RULE_GROUPS, type QualityCatalogRule, type QualityRuleGroup } from "./catalog.js";
2
+ export { checkArticle, checkArticleText, type CheckArticleInput, type CheckArticleTextInput, type QualityCheckResult } from "./check-article.js";
3
+ export { checkArticleFiles, type CheckArticleFilesOptions, type FileCheckResult } from "./check-files.js";
4
+ export { getProseBlocks, splitArticleBody, splitBlocks, type Block, type BlockKind } from "./blocks.js";
5
+ export { checkExternalLinks, checkExternalUrl, type FetchLike } from "./external-links.js";
6
+ export { formatFinding, hasQualityErrors, QUALITY_SEVERITIES, sortFindings, type QualityFinding, type QualitySeverity } from "./finding.js";
7
+ export { createQualityGate } from "./gate.js";
8
+ export { collectAppRoutes, createInternalLinkResolver, findAppDir, readContentFolder, readPublishedContent, type AppRoute, type InternalLinkResolverOptions } from "./link-targets.js";
9
+ export { qualityOptionsSchema, type QualityLimits, type QualityOptions, type QualityOptionsInput } from "./options.js";
10
+ export { isQualityPlugin, type QualityPlugin, type QualityPluginContext, type QualityRuleInfo } from "./plugin.js";
11
+ export { collectLinks, type InternalLinkResolver, type LinkSummary } from "./rules/links.js";
12
+ export { enRuleset, plRuleset, QUALITY_LANGUAGES, QUALITY_RULESETS, type LanguageRuleset, type QualityLanguage, type StylePattern } from "./rulesets/index.js";
13
+ export { getLocalDate, resolveQualitySettings, type QualitySettings } from "./settings.js";
14
+ export { countWords, findBareUrls, findFootnoteRefs, findLinks, findSignificantNumbers, normalizeNumber, parseNumber, splitSentences, toProse, wordPattern, type MarkdownLink, type NumberNotation, } from "./text.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/quality/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjJ,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACxG,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAC5I,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,UAAU,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,KAAK,QAAQ,EAAE,KAAK,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AACvL,OAAO,EAAE,oBAAoB,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACvH,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACnH,OAAO,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC/J,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,sBAAsB,EACtB,eAAe,EACf,WAAW,EACX,cAAc,EACd,OAAO,EACP,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,16 @@
1
+ // The quality gate's public surface, re-exported from `@softure-ai/blog/server`.
2
+ export { listQualityRules, QUALITY_RULE_GROUPS } from "./catalog.js";
3
+ export { checkArticle, checkArticleText } from "./check-article.js";
4
+ export { checkArticleFiles } from "./check-files.js";
5
+ export { getProseBlocks, splitArticleBody, splitBlocks } from "./blocks.js";
6
+ export { checkExternalLinks, checkExternalUrl } from "./external-links.js";
7
+ export { formatFinding, hasQualityErrors, QUALITY_SEVERITIES, sortFindings } from "./finding.js";
8
+ export { createQualityGate } from "./gate.js";
9
+ export { collectAppRoutes, createInternalLinkResolver, findAppDir, readContentFolder, readPublishedContent } from "./link-targets.js";
10
+ export { qualityOptionsSchema } from "./options.js";
11
+ export { isQualityPlugin } from "./plugin.js";
12
+ export { collectLinks } from "./rules/links.js";
13
+ export { enRuleset, plRuleset, QUALITY_LANGUAGES, QUALITY_RULESETS } from "./rulesets/index.js";
14
+ export { getLocalDate, resolveQualitySettings } from "./settings.js";
15
+ export { countWords, findBareUrls, findFootnoteRefs, findLinks, findSignificantNumbers, normalizeNumber, parseNumber, splitSentences, toProse, wordPattern, } from "./text.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/quality/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAkD,MAAM,cAAc,CAAC;AACrH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAA+E,MAAM,oBAAoB,CAAC;AACjJ,OAAO,EAAE,iBAAiB,EAAuD,MAAM,kBAAkB,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAA8B,MAAM,aAAa,CAAC;AACxG,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAkB,MAAM,qBAAqB,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAA6C,MAAM,cAAc,CAAC;AAC5I,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,UAAU,EAAE,iBAAiB,EAAE,oBAAoB,EAAmD,MAAM,mBAAmB,CAAC;AACvL,OAAO,EAAE,oBAAoB,EAAqE,MAAM,cAAc,CAAC;AACvH,OAAO,EAAE,eAAe,EAAuE,MAAM,aAAa,CAAC;AACnH,OAAO,EAAE,YAAY,EAA+C,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAiE,MAAM,qBAAqB,CAAC;AAC/J,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAwB,MAAM,eAAe,CAAC;AAC3F,OAAO,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,sBAAsB,EACtB,eAAe,EACf,WAAW,EACX,cAAc,EACd,OAAO,EACP,WAAW,GAGZ,MAAM,WAAW,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { type ParseArticleFileOptions } from "../content/article-file.js";
2
+ import type { BlogArticleKind } from "../contract.js";
3
+ import type { GlossaryTerm } from "../render/glossary.js";
4
+ import type { InternalLinkResolver } from "./rules/links.js";
5
+ export interface AppRoute {
6
+ readonly pattern: RegExp;
7
+ /** Has a `[param]` segment. */
8
+ readonly isDynamic: boolean;
9
+ }
10
+ export interface InternalLinkResolverOptions {
11
+ /** The Next.js app folder, or `null` when the app has none (only content links resolve). */
12
+ readonly appDir: string | null;
13
+ readonly privateRouteSegments: readonly string[];
14
+ readonly paths: {
15
+ readonly articles: string;
16
+ readonly terms: string;
17
+ };
18
+ /** Published texts by slug. */
19
+ readonly content: ReadonlyMap<string, BlogArticleKind>;
20
+ }
21
+ /** The app folder to read: the given one, else `src/app`, else `app`, else none. */
22
+ export declare function findAppDir(cwd: string, appDir: string | undefined): string | null;
23
+ export declare function collectAppRoutes(appDir: string, privateSegments: readonly string[]): AppRoute[];
24
+ /** Published texts in the content folders by slug: the file name is the slug (BL-2), the kind and status come from the frontmatter. */
25
+ export declare function readPublishedContent(files: readonly {
26
+ readonly name: string;
27
+ readonly text: string;
28
+ }[]): Map<string, BlogArticleKind>;
29
+ /**
30
+ * The published glossary terms of the content folders, for the form conflict check: a later file
31
+ * with the same slug replaces an earlier one (a checked file over its copy in the folder). A file that
32
+ * does not parse is skipped; its own check reports it.
33
+ */
34
+ export declare function readGlossaryTerms(files: readonly {
35
+ readonly name: string;
36
+ readonly text: string;
37
+ }[], parse?: ParseArticleFileOptions): GlossaryTerm[];
38
+ export declare function createInternalLinkResolver(options: InternalLinkResolverOptions): InternalLinkResolver;
39
+ /** Reads `*.md` files of a folder except README.md. */
40
+ export declare function readContentFolder(dir: string): {
41
+ name: string;
42
+ text: string;
43
+ }[];
44
+ //# sourceMappingURL=link-targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-targets.d.ts","sourceRoot":"","sources":["../../src/quality/link-targets.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAI7D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,2BAA2B;IAC1C,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACxD;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGjF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,EAAE,CAe/F;AAED,uIAAuI;AACvI,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAQrI;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,KAAK,GAAE,uBAA4B,GAAG,YAAY,EAAE,CAUzJ;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,oBAAoB,CAarG;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAM/E"}
@@ -0,0 +1,91 @@
1
+ // Internal link targets from the file system (FIRE_TRACKER `src/lib/blog/quality/link-targets.ts`),
2
+ // for `softure-blog check`. A route exists when the Next.js app folder has a `page.*` or `route.*` for
3
+ // it (route groups vanish from the path; private segments and `_folders` are skipped). An article or
4
+ // a glossary term exists when the content folder has its published file of that kind, under
5
+ // `quality.paths`; a static page under the same path wins over the dynamic article route.
6
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { parseArticleFile } from "../content/article-file.js";
9
+ const ROUTE_FILES = /^(?:page|route)\.(?:tsx?|jsx?|mdx?)$/;
10
+ /** The app folder to read: the given one, else `src/app`, else `app`, else none. */
11
+ export function findAppDir(cwd, appDir) {
12
+ if (appDir !== undefined)
13
+ return join(cwd, appDir);
14
+ return [join(cwd, "src/app"), join(cwd, "app")].find((dir) => existsSync(dir)) ?? null;
15
+ }
16
+ export function collectAppRoutes(appDir, privateSegments) {
17
+ const routes = [];
18
+ const walk = (dir, segments) => {
19
+ const entries = readdirSync(dir, { withFileTypes: true });
20
+ if (entries.some((entry) => entry.isFile() && ROUTE_FILES.test(entry.name))) {
21
+ const visible = segments.filter((segment) => !/^\(.*\)$/.test(segment));
22
+ const pattern = visible.map((segment) => (/^\[.*\]$/.test(segment) ? "[^/]+" : escapeRegExp(segment))).join("/");
23
+ routes.push({ pattern: new RegExp(`^/${pattern}$`), isDynamic: visible.some((segment) => /^\[.*\]$/.test(segment)) });
24
+ }
25
+ for (const entry of entries) {
26
+ if (entry.isDirectory() && !privateSegments.includes(entry.name) && !entry.name.startsWith("_"))
27
+ walk(join(dir, entry.name), [...segments, entry.name]);
28
+ }
29
+ };
30
+ walk(appDir, []);
31
+ return routes;
32
+ }
33
+ /** Published texts in the content folders by slug: the file name is the slug (BL-2), the kind and status come from the frontmatter. */
34
+ export function readPublishedContent(files) {
35
+ const content = new Map();
36
+ for (const file of files) {
37
+ const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(file.text)?.[1] ?? "";
38
+ if (!/^status:\s*(["']?)published\1\s*(?:#.*)?$/m.test(frontmatter))
39
+ continue;
40
+ content.set(file.name.replace(/\.md$/, ""), /^kind:\s*(["']?)term\1\s*(?:#.*)?$/m.test(frontmatter) ? "term" : "article");
41
+ }
42
+ return content;
43
+ }
44
+ /**
45
+ * The published glossary terms of the content folders, for the form conflict check: a later file
46
+ * with the same slug replaces an earlier one (a checked file over its copy in the folder). A file that
47
+ * does not parse is skipped; its own check reports it.
48
+ */
49
+ export function readGlossaryTerms(files, parse = {}) {
50
+ const terms = new Map();
51
+ for (const file of files) {
52
+ const parsed = parseArticleFile(file.text, file.name, parse);
53
+ if (!parsed.ok)
54
+ continue;
55
+ const { slug, kind, status, termForms } = parsed.article;
56
+ if (kind === "term" && status === "published")
57
+ terms.set(slug, { slug, forms: termForms });
58
+ else
59
+ terms.delete(slug);
60
+ }
61
+ return [...terms.values()];
62
+ }
63
+ export function createInternalLinkResolver(options) {
64
+ const routes = options.appDir === null || !existsSync(options.appDir) ? [] : collectAppRoutes(options.appDir, options.privateRouteSegments);
65
+ const term = new RegExp(`^${escapeRegExp(options.paths.terms)}/([^/]+)$`);
66
+ const article = new RegExp(`^${escapeRegExp(options.paths.articles === "/" ? "" : options.paths.articles)}/([^/]+)$`);
67
+ const isStaticRoute = (pathname) => routes.some((route) => !route.isDynamic && route.pattern.test(pathname));
68
+ return (rawPathname) => {
69
+ const pathname = rawPathname.length > 1 ? rawPathname.replace(/\/$/, "") : rawPathname;
70
+ const termSlug = term.exec(pathname)?.[1];
71
+ if (termSlug !== undefined)
72
+ return options.content.get(termSlug) === "term" || isStaticRoute(pathname);
73
+ const articleSlug = article.exec(pathname)?.[1];
74
+ if (articleSlug !== undefined)
75
+ return options.content.get(articleSlug) === "article" || isStaticRoute(pathname);
76
+ return routes.some((route) => route.pattern.test(pathname));
77
+ };
78
+ }
79
+ /** Reads `*.md` files of a folder except README.md. */
80
+ export function readContentFolder(dir) {
81
+ if (!existsSync(dir))
82
+ return [];
83
+ return readdirSync(dir)
84
+ .filter((name) => name.endsWith(".md") && name !== "README.md")
85
+ .sort()
86
+ .map((name) => ({ name, text: readFileSync(join(dir, name), "utf8") }));
87
+ }
88
+ function escapeRegExp(text) {
89
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
90
+ }
91
+ //# sourceMappingURL=link-targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-targets.js","sourceRoot":"","sources":["../../src/quality/link-targets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ import type { BlockPlugin } from "../render/render-article.js";
3
+ import { type QualityPlugin } from "./plugin.js";
4
+ /** Thresholds; the defaults are FIRE's, chosen for answer-first texts that AI assistants quote. */
5
+ export declare const qualityLimitsSchema: z.ZodPrefault<z.ZodObject<{
6
+ words: z.ZodPrefault<z.ZodObject<{
7
+ article: z.ZodPrefault<z.ZodObject<{
8
+ min: z.ZodDefault<z.ZodNumber>;
9
+ max: z.ZodDefault<z.ZodNumber>;
10
+ }, z.core.$strict>>;
11
+ term: z.ZodPrefault<z.ZodObject<{
12
+ min: z.ZodDefault<z.ZodNumber>;
13
+ max: z.ZodDefault<z.ZodNumber>;
14
+ }, z.core.$strict>>;
15
+ }, z.core.$strict>>;
16
+ leadWords: z.ZodPrefault<z.ZodObject<{
17
+ article: z.ZodDefault<z.ZodNumber>;
18
+ term: z.ZodDefault<z.ZodNumber>;
19
+ }, z.core.$strict>>;
20
+ answerWords: z.ZodDefault<z.ZodNumber>;
21
+ internalLinks: z.ZodPrefault<z.ZodObject<{
22
+ article: z.ZodDefault<z.ZodNumber>;
23
+ term: z.ZodDefault<z.ZodNumber>;
24
+ }, z.core.$strict>>;
25
+ staleAfterDays: z.ZodDefault<z.ZodNumber>;
26
+ titleChars: z.ZodDefault<z.ZodNumber>;
27
+ descriptionChars: z.ZodPrefault<z.ZodObject<{
28
+ min: z.ZodDefault<z.ZodNumber>;
29
+ max: z.ZodDefault<z.ZodNumber>;
30
+ }, z.core.$strict>>;
31
+ wordsPerDash: z.ZodDefault<z.ZodNumber>;
32
+ wordsPerBold: z.ZodDefault<z.ZodNumber>;
33
+ sentenceWords: z.ZodDefault<z.ZodNumber>;
34
+ }, z.core.$strict>>;
35
+ export declare const qualityOptionsSchema: z.ZodObject<{
36
+ language: z.ZodDefault<z.ZodEnum<{
37
+ en: "en";
38
+ pl: "pl";
39
+ }>>;
40
+ ymyl: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
41
+ ownCalculationMark: z.ZodOptional<z.ZodString>;
42
+ }, z.core.$strict>]>>, z.ZodTransform<{
43
+ ownCalculationMark: string | null;
44
+ } | null, boolean | {
45
+ ownCalculationMark?: string | undefined;
46
+ }>>;
47
+ voice: z.ZodPrefault<z.ZodObject<{
48
+ forbidFirstPersonSingular: z.ZodDefault<z.ZodBoolean>;
49
+ phrases: z.ZodDefault<z.ZodArray<z.ZodObject<{
50
+ id: z.ZodString;
51
+ pattern: z.ZodInstanceOf<RegExp>;
52
+ message: z.ZodString;
53
+ severity: z.ZodDefault<z.ZodEnum<{
54
+ error: "error";
55
+ warning: "warning";
56
+ }>>;
57
+ }, z.core.$strict>>>;
58
+ }, z.core.$strict>>;
59
+ limits: z.ZodPrefault<z.ZodObject<{
60
+ words: z.ZodPrefault<z.ZodObject<{
61
+ article: z.ZodPrefault<z.ZodObject<{
62
+ min: z.ZodDefault<z.ZodNumber>;
63
+ max: z.ZodDefault<z.ZodNumber>;
64
+ }, z.core.$strict>>;
65
+ term: z.ZodPrefault<z.ZodObject<{
66
+ min: z.ZodDefault<z.ZodNumber>;
67
+ max: z.ZodDefault<z.ZodNumber>;
68
+ }, z.core.$strict>>;
69
+ }, z.core.$strict>>;
70
+ leadWords: z.ZodPrefault<z.ZodObject<{
71
+ article: z.ZodDefault<z.ZodNumber>;
72
+ term: z.ZodDefault<z.ZodNumber>;
73
+ }, z.core.$strict>>;
74
+ answerWords: z.ZodDefault<z.ZodNumber>;
75
+ internalLinks: z.ZodPrefault<z.ZodObject<{
76
+ article: z.ZodDefault<z.ZodNumber>;
77
+ term: z.ZodDefault<z.ZodNumber>;
78
+ }, z.core.$strict>>;
79
+ staleAfterDays: z.ZodDefault<z.ZodNumber>;
80
+ titleChars: z.ZodDefault<z.ZodNumber>;
81
+ descriptionChars: z.ZodPrefault<z.ZodObject<{
82
+ min: z.ZodDefault<z.ZodNumber>;
83
+ max: z.ZodDefault<z.ZodNumber>;
84
+ }, z.core.$strict>>;
85
+ wordsPerDash: z.ZodDefault<z.ZodNumber>;
86
+ wordsPerBold: z.ZodDefault<z.ZodNumber>;
87
+ sentenceWords: z.ZodDefault<z.ZodNumber>;
88
+ }, z.core.$strict>>;
89
+ severity: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<{
90
+ error: "error";
91
+ warning: "warning";
92
+ off: "off";
93
+ }>>>;
94
+ paths: z.ZodPrefault<z.ZodObject<{
95
+ articles: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
96
+ terms: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
97
+ }, z.core.$strict>>;
98
+ ownOrigins: z.ZodDefault<z.ZodArray<z.ZodURL>>;
99
+ appDir: z.ZodOptional<z.ZodString>;
100
+ privateRouteSegments: z.ZodDefault<z.ZodArray<z.ZodString>>;
101
+ plugins: z.ZodDefault<z.ZodArray<z.ZodCustom<QualityPlugin, QualityPlugin>>>;
102
+ blocks: z.ZodDefault<z.ZodArray<z.ZodCustom<BlockPlugin<unknown>, BlockPlugin<unknown>>>>;
103
+ }, z.core.$strict>;
104
+ export type QualityOptionsInput = z.input<typeof qualityOptionsSchema>;
105
+ export type QualityOptions = z.output<typeof qualityOptionsSchema>;
106
+ export type QualityLimits = QualityOptions["limits"];
107
+ /**
108
+ * `blog({ quality })`: the options, or `false` to turn the gate off. Not a zod union, whose error
109
+ * would read "Invalid input" and hide which option is wrong: the options' own issues are reported.
110
+ * The cast gives it the types of a defaulted option: optional in, never undefined out.
111
+ */
112
+ export declare const qualitySettingSchema: z.ZodDefault<z.ZodType<QualityOptions | false, QualityOptionsInput | false>>;
113
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/quality/options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAoBlE,mGAAmG;AACnG,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAajB,CAAC;AAIhB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0C/B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACvE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACnE,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EASd,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,GAAG,KAAK,EAAE,mBAAmB,GAAG,KAAK,CAAC,CAAC,CAAC"}
@@ -0,0 +1,93 @@
1
+ // `blog({ quality })`: the gate's options, parsed at startup with the rest of the blog's options.
2
+ import { z } from "zod";
3
+ import { QUALITY_SEVERITIES } from "./finding.js";
4
+ import { isQualityPlugin } from "./plugin.js";
5
+ import { QUALITY_LANGUAGES } from "./rulesets/types.js";
6
+ const KEBAB = /^[a-z0-9]+(-[a-z0-9]+)*$/;
7
+ function isBlockPlugin(value) {
8
+ if (typeof value !== "object" || value === null)
9
+ return false;
10
+ const candidate = value;
11
+ return typeof candidate.type === "string" && typeof candidate.render === "function";
12
+ }
13
+ const range = (min, max) => z
14
+ .strictObject({ min: z.number().int().min(0).default(min), max: z.number().int().positive().default(max) })
15
+ .prefault({})
16
+ .refine((value) => value.min <= value.max, "min must not exceed max");
17
+ const byKind = (article, term) => z.strictObject({ article: z.number().int().min(0).default(article), term: z.number().int().min(0).default(term) }).prefault({});
18
+ /** Thresholds; the defaults are FIRE's, chosen for answer-first texts that AI assistants quote. */
19
+ export const qualityLimitsSchema = z
20
+ .strictObject({
21
+ words: z.strictObject({ article: range(600, 4000), term: range(60, 700) }).prefault({}),
22
+ leadWords: byKind(90, 60),
23
+ answerWords: z.number().int().positive().default(70),
24
+ internalLinks: byKind(2, 1),
25
+ staleAfterDays: z.number().int().positive().default(365),
26
+ titleChars: z.number().int().positive().default(70),
27
+ descriptionChars: range(50, 160),
28
+ wordsPerDash: z.number().int().positive().default(150),
29
+ wordsPerBold: z.number().int().positive().default(200),
30
+ sentenceWords: z.number().int().positive().default(35),
31
+ })
32
+ .prefault({});
33
+ const sitePath = z.string().regex(/^\/[a-z0-9/_-]*$/, "must be a path from the site root, e.g. /blog").transform((path) => (path.length > 1 ? path.replace(/\/$/, "") : path));
34
+ export const qualityOptionsSchema = z.strictObject({
35
+ /** The ruleset of the blog's language. */
36
+ language: z.enum(QUALITY_LANGUAGES).default("en"),
37
+ /** Your money or your life: sources required, every significant number footnoted, no profit promises. */
38
+ ymyl: z
39
+ .union([z.boolean(), z.strictObject({ ownCalculationMark: z.string().trim().min(1).optional() })])
40
+ .default(false)
41
+ .transform((value) => (value === false ? null : { ownCalculationMark: value === true ? null : (value.ownCalculationMark ?? null) })),
42
+ voice: z
43
+ .strictObject({
44
+ /** Texts signed by an editorial team: no "I", "my", "in my opinion". */
45
+ forbidFirstPersonSingular: z.boolean().default(false),
46
+ /**
47
+ * The brand's banned phrases: a global regular expression, matched as given. `wordPattern("...")`
48
+ * builds one that is case-insensitive and knows word edges in any alphabet.
49
+ */
50
+ phrases: z
51
+ .array(z.strictObject({
52
+ id: z.string().max(60).regex(KEBAB, "kebab-case, e.g. finance-cliche"),
53
+ pattern: z.instanceof(RegExp, { error: "must be a regular expression" }).refine((pattern) => pattern.global, "must be global (flag g), so every hit counts"),
54
+ message: z.string().trim().min(1),
55
+ severity: z.enum(QUALITY_SEVERITIES).default("error"),
56
+ }))
57
+ .default([]),
58
+ })
59
+ .prefault({}),
60
+ limits: qualityLimitsSchema,
61
+ /** Per rule: another severity, or "off". */
62
+ severity: z.record(z.string().regex(KEBAB), z.enum(["error", "warning", "off"])).default({}),
63
+ /** Where the pages live; BL-4 serves them there. */
64
+ paths: z.strictObject({ articles: sitePath.default("/blog"), terms: sitePath.default("/blog/glossary") }).prefault({}),
65
+ /** Absolute origins whose links count as internal, besides the config's `appOrigin` and the canonical site origin (`getSiteUrls`). */
66
+ ownOrigins: z.array(z.url({ protocol: /^https?$/ })).default([]),
67
+ /** The Next.js app folder `softure-blog check` reads routes from. Default: `src/app`, else `app`. */
68
+ appDir: z.string().trim().min(1).optional(),
69
+ /** Route folders that are no link target (API, pages behind a login). Route groups count too, e.g. "(app)". */
70
+ privateRouteSegments: z.array(z.string().min(1)).default(["api"]),
71
+ plugins: z.array(z.custom(isQualityPlugin, "must be a plugin: { name, rules, check }")).default([]),
72
+ /** The block plugins the app renders with (`renderArticle({ blocks })`): their `requires` keys must be in the frontmatter. */
73
+ blocks: z.array(z.custom(isBlockPlugin, "must be a block plugin: { type, render }")).default([]),
74
+ });
75
+ /**
76
+ * `blog({ quality })`: the options, or `false` to turn the gate off. Not a zod union, whose error
77
+ * would read "Invalid input" and hide which option is wrong: the options' own issues are reported.
78
+ * The cast gives it the types of a defaulted option: optional in, never undefined out.
79
+ */
80
+ export const qualitySettingSchema = z
81
+ .unknown()
82
+ .optional()
83
+ .transform((value, ctx) => {
84
+ if (value === false)
85
+ return false;
86
+ const parsed = qualityOptionsSchema.safeParse(value ?? {});
87
+ if (parsed.success)
88
+ return parsed.data;
89
+ for (const issue of parsed.error.issues)
90
+ ctx.addIssue({ code: "custom", message: issue.message, path: issue.path });
91
+ return z.NEVER;
92
+ });
93
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/quality/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { BlogArticleInput } from "../contract.js";
2
+ import type { FoundBlock } from "../render/render-article.js";
3
+ import type { Block } from "./blocks.js";
4
+ import type { QualityFinding, QualitySeverity } from "./finding.js";
5
+ import type { LanguageRuleset } from "./rulesets/types.js";
6
+ /** A rule as the catalog lists it: what the writing skill names and the gate enforces. */
7
+ export interface QualityRuleInfo {
8
+ readonly id: string;
9
+ /** The default severity; `quality.severity` overrides it. */
10
+ readonly severity: QualitySeverity;
11
+ readonly description: string;
12
+ }
13
+ export interface QualityPluginContext {
14
+ /** The parsed file: frontmatter values, the app's `fields` and the Markdown body. */
15
+ readonly article: BlogArticleInput;
16
+ /** The body cut into blocks with file lines; fenced code is skipped, `::name{…}` directives are blocks of their own. */
17
+ readonly blocks: readonly Block[];
18
+ /** The fenced blocks of the app's block plugins (`quality.blocks`), with the file line of the opening fence. */
19
+ readonly pluginBlocks: readonly FoundBlock[];
20
+ /** `YYYY-MM-DD` in the app's time zone. */
21
+ readonly today: string;
22
+ /** The app's language ruleset, for its number notation. */
23
+ readonly ruleset: LanguageRuleset;
24
+ }
25
+ export interface QualityPlugin {
26
+ /** Names the plugin in a failure message. */
27
+ readonly name: string;
28
+ /** Every rule id the plugin may report. A finding with another id is refused as `plugin-rule-undeclared`. */
29
+ readonly rules: readonly QualityRuleInfo[];
30
+ /** Pure: no network, no file system. A throw becomes a `plugin-failed` error. */
31
+ readonly check: (context: QualityPluginContext) => readonly QualityFinding[];
32
+ }
33
+ export declare function isQualityPlugin(value: unknown): value is QualityPlugin;
34
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/quality/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,qFAAqF;IACrF,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,wHAAwH;IACxH,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,gHAAgH;IAChH,QAAQ,CAAC,YAAY,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7C,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6GAA6G;IAC7G,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAC3C,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,SAAS,cAAc,EAAE,CAAC;CAC9E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAItE"}
@@ -0,0 +1,7 @@
1
+ export function isQualityPlugin(value) {
2
+ if (typeof value !== "object" || value === null)
3
+ return false;
4
+ const candidate = value;
5
+ return typeof candidate.name === "string" && Array.isArray(candidate.rules) && typeof candidate.check === "function";
6
+ }
7
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/quality/plugin.ts"],"names":[],"mappings":"AAsCA,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,SAAS,GAAG,KAA6D,CAAC;IAChF,OAAO,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,UAAU,CAAC;AACvH,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { BlogArticleInput } from "../../contract.js";
2
+ import type { FoundBlock } from "../../render/render-article.js";
3
+ import type { QualityFinding } from "../finding.js";
4
+ export declare function checkBlockRequires(article: BlogArticleInput, pluginBlocks: readonly FoundBlock[]): QualityFinding[];
5
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../../src/quality/rules/blocks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,UAAU,EAAE,GAAG,cAAc,EAAE,CAMnH"}
@@ -0,0 +1,35 @@
1
+ export function checkBlockRequires(article, pluginBlocks) {
2
+ return pluginBlocks.flatMap((block) => block.requires
3
+ .filter((key) => !hasFrontmatterKey(article, key))
4
+ .map((key) => ({ rule: "block-requires", severity: "error", message: `the ${block.type} block needs ${key} in the frontmatter`, line: block.line })));
5
+ }
6
+ /** Whether the file set a frontmatter key: a module key with a value, or a key of the app's fields. */
7
+ function hasFrontmatterKey(article, key) {
8
+ switch (key) {
9
+ case "id":
10
+ case "slug":
11
+ case "kind":
12
+ case "title":
13
+ case "description":
14
+ case "status":
15
+ case "current_as_of":
16
+ return true;
17
+ case "cluster":
18
+ return article.cluster !== null;
19
+ case "pillar":
20
+ return article.isPillar;
21
+ case "summary":
22
+ return article.summary !== null;
23
+ case "published_at":
24
+ return article.publishedAt !== null;
25
+ case "sources":
26
+ return article.sources.length > 0;
27
+ case "faq":
28
+ return article.faq.length > 0;
29
+ case "forms":
30
+ return article.termForms.length > 0;
31
+ default:
32
+ return article.fields[key] !== undefined;
33
+ }
34
+ }
35
+ //# sourceMappingURL=blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.js","sourceRoot":"","sources":["../../../src/quality/rules/blocks.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,kBAAkB,CAAC,OAAyB,EAAE,YAAmC;IAC/F,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACpC,KAAK,CAAC,QAAQ;SACX,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;SACjD,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,OAAgB,EAAE,OAAO,EAAE,OAAO,KAAK,CAAC,IAAI,gBAAgB,GAAG,qBAAqB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAChK,CAAC;AACJ,CAAC;AAED,uGAAuG;AACvG,SAAS,iBAAiB,CAAC,OAAyB,EAAE,GAAW;IAC/D,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,IAAI,CAAC;QACV,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,aAAa,CAAC;QACnB,KAAK,QAAQ,CAAC;QACd,KAAK,eAAe;YAClB,OAAO,IAAI,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC;QAClC,KAAK,QAAQ;YACX,OAAO,OAAO,CAAC,QAAQ,CAAC;QAC1B,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC;QAClC,KAAK,cAAc;YACjB,OAAO,OAAO,CAAC,WAAW,KAAK,IAAI,CAAC;QACtC,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACpC,KAAK,KAAK;YACR,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QAChC,KAAK,OAAO;YACV,OAAO,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QACtC;YACE,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type ArticleImagePolicy, type FoundImage } from "../../render/images.js";
2
+ import type { QualityFinding } from "../finding.js";
3
+ export declare function checkImages(images: readonly FoundImage[], policy: ArticleImagePolicy | null): QualityFinding[];
4
+ //# sourceMappingURL=images.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../../src/quality/rules/images.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,kBAAkB,EAAE,KAAK,UAAU,EAAwC,MAAM,wBAAwB,CAAC;AAC3I,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAiBpD,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,GAAG,cAAc,EAAE,CAa9G"}
@@ -0,0 +1,33 @@
1
+ // Image rules (BF-3): every image in a body follows the app's image policy (`blog({ images })`), the
2
+ // same check the renderer applies, so a text whose image the page would drop never goes public.
3
+ import { checkArticleImage } from "../../render/images.js";
4
+ const RULE_BY_PROBLEM = { source: "image-source", alt: "image-alt", dimensions: "image-dimensions" };
5
+ function describeProblem(problem, src, policy) {
6
+ switch (problem) {
7
+ case "source":
8
+ return policy === null
9
+ ? `images are off until the app sets blog({ images }): ${src}`
10
+ : `an image from outside the site and the allowed hosts (blog({ images: { hosts } })): ${src}`;
11
+ case "alt":
12
+ return `an image without alt text; describe what it shows: ${src}`;
13
+ case "dimensions":
14
+ return `the app does not know the width and height of an image (blog({ images: { dimensions } })): ${src}`;
15
+ }
16
+ }
17
+ export function checkImages(images, policy) {
18
+ return images.flatMap((image) => {
19
+ let verdict;
20
+ try {
21
+ verdict = checkArticleImage(image, policy ?? undefined);
22
+ }
23
+ catch (error) {
24
+ // The resolver is the app's code; its bug refuses the file instead of crashing a publish.
25
+ const detail = error instanceof Error ? error.message : String(error);
26
+ return [{ rule: "image-dimensions", severity: "error", message: `the image dimensions resolver failed (${detail}): ${image.src}`, line: image.line }];
27
+ }
28
+ if (verdict.ok)
29
+ return [];
30
+ return verdict.problems.map((problem) => ({ rule: RULE_BY_PROBLEM[problem], severity: "error", message: describeProblem(problem, image.src, policy), line: image.line }));
31
+ });
32
+ }
33
+ //# sourceMappingURL=images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.js","sourceRoot":"","sources":["../../../src/quality/rules/images.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,gGAAgG;AAChG,OAAO,EAAE,iBAAiB,EAAkF,MAAM,wBAAwB,CAAC;AAG3I,MAAM,eAAe,GAAiC,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC;AAEnI,SAAS,eAAe,CAAC,OAAqB,EAAE,GAAW,EAAE,MAAiC;IAC5F,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,MAAM,KAAK,IAAI;gBACpB,CAAC,CAAC,uDAAuD,GAAG,EAAE;gBAC9D,CAAC,CAAC,uFAAuF,GAAG,EAAE,CAAC;QACnG,KAAK,KAAK;YACR,OAAO,sDAAsD,GAAG,EAAE,CAAC;QACrE,KAAK,YAAY;YACf,OAAO,8FAA8F,GAAG,EAAE,CAAC;IAC/G,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAA6B,EAAE,MAAiC;IAC1F,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,IAAI,OAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,OAAO,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,0FAA0F;YAC1F,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,OAAO,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,OAAgB,EAAE,OAAO,EAAE,yCAAyC,MAAM,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACjK,CAAC;QACD,IAAI,OAAO,CAAC,EAAE;YAAE,OAAO,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAgB,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACrL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { BlogArticleInput } from "../../contract.js";
2
+ import type { Block } from "../blocks.js";
3
+ import type { QualitySettings } from "../settings.js";
4
+ export interface RuleInput {
5
+ readonly article: BlogArticleInput;
6
+ readonly blocks: readonly Block[];
7
+ readonly settings: QualitySettings;
8
+ /** `YYYY-MM-DD` in the app's time zone. */
9
+ readonly today: string;
10
+ }
11
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/quality/rules/input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../../../src/quality/rules/input.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import type { QualityFinding } from "../finding.js";
2
+ import type { RuleInput } from "./input.js";
3
+ export interface LinkSummary {
4
+ readonly internal: readonly {
5
+ readonly pathname: string;
6
+ readonly line: number;
7
+ }[];
8
+ readonly external: readonly {
9
+ readonly url: string;
10
+ readonly line: number;
11
+ }[];
12
+ }
13
+ /** Whether a site path (`/blog/x`, `/calculator`) leads to a page. */
14
+ export type InternalLinkResolver = (pathname: string) => boolean;
15
+ export declare function collectLinks({ blocks, settings }: Pick<RuleInput, "blocks" | "settings">): LinkSummary;
16
+ export declare function checkLinks(input: RuleInput, links: LinkSummary, resolveInternalLink: InternalLinkResolver): QualityFinding[];
17
+ //# sourceMappingURL=links.d.ts.map