@geekity/cms 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (660) hide show
  1. package/README.md +2217 -214
  2. package/admin/components/fields.njk +165 -0
  3. package/admin/layouts/base.njk +1 -1
  4. package/admin/layouts/settings-page.njk +26 -0
  5. package/admin/layouts/shell.njk +17 -3
  6. package/admin/pages/account/forgot.njk +47 -0
  7. package/admin/pages/account/login.njk +26 -0
  8. package/admin/pages/account/reset.njk +38 -0
  9. package/admin/pages/account/setup.njk +28 -0
  10. package/admin/pages/appearance/themes.njk +69 -0
  11. package/admin/pages/comments/all.njk +133 -0
  12. package/admin/{layouts/dashboard.njk → pages/dashboard/home.njk} +8 -15
  13. package/admin/{layouts/document-conflict.njk → pages/documents/conflict.njk} +1 -0
  14. package/admin/{layouts/document-editor.njk → pages/documents/editor.njk} +75 -59
  15. package/admin/{layouts/document-list.njk → pages/documents/list.njk} +19 -2
  16. package/admin/pages/documents/taxonomy.njk +95 -0
  17. package/admin/pages/federation/followers.njk +238 -0
  18. package/admin/pages/federation/settings.njk +77 -0
  19. package/admin/pages/media/library.njk +160 -0
  20. package/admin/pages/messages/all.njk +110 -0
  21. package/admin/pages/navigation/menus.njk +129 -0
  22. package/admin/{layouts → pages}/placeholder.njk +5 -1
  23. package/admin/pages/settings/discussion.njk +99 -0
  24. package/admin/pages/settings/email.njk +203 -0
  25. package/admin/pages/settings/general.njk +29 -0
  26. package/admin/pages/settings/permalinks.njk +50 -0
  27. package/admin/pages/settings/reading.njk +64 -0
  28. package/admin/pages/tools/content-index.njk +89 -0
  29. package/admin/pages/users/edit.njk +239 -0
  30. package/admin/pages/users/list.njk +51 -0
  31. package/admin/pages/users/new.njk +41 -0
  32. package/admin/static/admin.css +558 -3
  33. package/admin/static/copy.js +52 -0
  34. package/admin/static/editor.js +16 -16
  35. package/admin/static/slug.js +61 -0
  36. package/dist/access-log.d.ts +44 -0
  37. package/dist/access-log.d.ts.map +1 -0
  38. package/dist/access-log.js +71 -0
  39. package/dist/access-log.js.map +1 -0
  40. package/dist/admin/accounts.d.ts +413 -0
  41. package/dist/admin/accounts.d.ts.map +1 -0
  42. package/dist/admin/accounts.js +680 -0
  43. package/dist/admin/accounts.js.map +1 -0
  44. package/dist/admin/appearance.d.ts +80 -0
  45. package/dist/admin/appearance.d.ts.map +1 -0
  46. package/dist/admin/appearance.js +136 -0
  47. package/dist/admin/appearance.js.map +1 -0
  48. package/dist/admin/comments.d.ts +90 -0
  49. package/dist/admin/comments.d.ts.map +1 -0
  50. package/dist/admin/comments.js +244 -0
  51. package/dist/admin/comments.js.map +1 -0
  52. package/dist/admin/credentials.d.ts +26 -2
  53. package/dist/admin/credentials.d.ts.map +1 -1
  54. package/dist/admin/credentials.js +35 -2
  55. package/dist/admin/credentials.js.map +1 -1
  56. package/dist/admin/documents.d.ts +117 -10
  57. package/dist/admin/documents.d.ts.map +1 -1
  58. package/dist/admin/documents.js +328 -41
  59. package/dist/admin/documents.js.map +1 -1
  60. package/dist/admin/federation.d.ts +161 -50
  61. package/dist/admin/federation.d.ts.map +1 -1
  62. package/dist/admin/federation.js +235 -93
  63. package/dist/admin/federation.js.map +1 -1
  64. package/dist/admin/formatting.d.ts +20 -0
  65. package/dist/admin/formatting.d.ts.map +1 -0
  66. package/dist/admin/formatting.js +72 -0
  67. package/dist/admin/formatting.js.map +1 -0
  68. package/dist/admin/headers.d.ts +65 -0
  69. package/dist/admin/headers.d.ts.map +1 -0
  70. package/dist/admin/headers.js +104 -0
  71. package/dist/admin/headers.js.map +1 -0
  72. package/dist/admin/index.d.ts +38 -12
  73. package/dist/admin/index.d.ts.map +1 -1
  74. package/dist/admin/index.js +25 -7
  75. package/dist/admin/index.js.map +1 -1
  76. package/dist/admin/media.d.ts +168 -0
  77. package/dist/admin/media.d.ts.map +1 -0
  78. package/dist/admin/media.js +295 -0
  79. package/dist/admin/media.js.map +1 -0
  80. package/dist/admin/menu.d.ts +79 -0
  81. package/dist/admin/menu.d.ts.map +1 -0
  82. package/dist/admin/menu.js +143 -0
  83. package/dist/admin/menu.js.map +1 -0
  84. package/dist/admin/messages.d.ts +80 -0
  85. package/dist/admin/messages.d.ts.map +1 -0
  86. package/dist/admin/messages.js +152 -0
  87. package/dist/admin/messages.js.map +1 -0
  88. package/dist/admin/navigation.d.ts +155 -0
  89. package/dist/admin/navigation.d.ts.map +1 -0
  90. package/dist/admin/navigation.js +292 -0
  91. package/dist/admin/navigation.js.map +1 -0
  92. package/dist/admin/preview.d.ts.map +1 -1
  93. package/dist/admin/preview.js +31 -7
  94. package/dist/admin/preview.js.map +1 -1
  95. package/dist/admin/recovery.d.ts +92 -0
  96. package/dist/admin/recovery.d.ts.map +1 -0
  97. package/dist/admin/recovery.js +229 -0
  98. package/dist/admin/recovery.js.map +1 -0
  99. package/dist/admin/routes.d.ts +0 -20
  100. package/dist/admin/routes.d.ts.map +1 -1
  101. package/dist/admin/routes.js +164 -105
  102. package/dist/admin/routes.js.map +1 -1
  103. package/dist/admin/session.d.ts +17 -3
  104. package/dist/admin/session.d.ts.map +1 -1
  105. package/dist/admin/session.js +19 -5
  106. package/dist/admin/session.js.map +1 -1
  107. package/dist/admin/settings-discussion.d.ts +37 -0
  108. package/dist/admin/settings-discussion.d.ts.map +1 -0
  109. package/dist/admin/settings-discussion.js +117 -0
  110. package/dist/admin/settings-discussion.js.map +1 -0
  111. package/dist/admin/settings-email.d.ts +54 -0
  112. package/dist/admin/settings-email.d.ts.map +1 -0
  113. package/dist/admin/settings-email.js +189 -0
  114. package/dist/admin/settings-email.js.map +1 -0
  115. package/dist/admin/settings-federation.d.ts +48 -0
  116. package/dist/admin/settings-federation.d.ts.map +1 -0
  117. package/dist/admin/settings-federation.js +123 -0
  118. package/dist/admin/settings-federation.js.map +1 -0
  119. package/dist/admin/settings-general.d.ts +12 -0
  120. package/dist/admin/settings-general.d.ts.map +1 -0
  121. package/dist/admin/settings-general.js +48 -0
  122. package/dist/admin/settings-general.js.map +1 -0
  123. package/dist/admin/settings-page.d.ts +119 -0
  124. package/dist/admin/settings-page.d.ts.map +1 -0
  125. package/dist/admin/settings-page.js +132 -0
  126. package/dist/admin/settings-page.js.map +1 -0
  127. package/dist/admin/settings-pages.d.ts +30 -0
  128. package/dist/admin/settings-pages.d.ts.map +1 -0
  129. package/dist/admin/settings-pages.js +43 -0
  130. package/dist/admin/settings-pages.js.map +1 -0
  131. package/dist/admin/settings-permalinks.d.ts +16 -0
  132. package/dist/admin/settings-permalinks.d.ts.map +1 -0
  133. package/dist/admin/settings-permalinks.js +32 -0
  134. package/dist/admin/settings-permalinks.js.map +1 -0
  135. package/dist/admin/settings-reading.d.ts +34 -0
  136. package/dist/admin/settings-reading.d.ts.map +1 -0
  137. package/dist/admin/settings-reading.js +49 -0
  138. package/dist/admin/settings-reading.js.map +1 -0
  139. package/dist/admin/settings.d.ts +339 -96
  140. package/dist/admin/settings.d.ts.map +1 -1
  141. package/dist/admin/settings.js +600 -308
  142. package/dist/admin/settings.js.map +1 -1
  143. package/dist/admin/store.d.ts +606 -152
  144. package/dist/admin/store.d.ts.map +1 -1
  145. package/dist/admin/store.js +863 -268
  146. package/dist/admin/store.js.map +1 -1
  147. package/dist/admin/taxonomy.d.ts +114 -0
  148. package/dist/admin/taxonomy.d.ts.map +1 -0
  149. package/dist/admin/taxonomy.js +289 -0
  150. package/dist/admin/taxonomy.js.map +1 -0
  151. package/dist/admin/templates.d.ts +46 -14
  152. package/dist/admin/templates.d.ts.map +1 -1
  153. package/dist/admin/templates.js +46 -14
  154. package/dist/admin/templates.js.map +1 -1
  155. package/dist/admin/throttle.d.ts +84 -0
  156. package/dist/admin/throttle.d.ts.map +1 -0
  157. package/dist/admin/throttle.js +143 -0
  158. package/dist/admin/throttle.js.map +1 -0
  159. package/dist/admin/tools.d.ts +111 -0
  160. package/dist/admin/tools.d.ts.map +1 -0
  161. package/dist/admin/tools.js +182 -0
  162. package/dist/admin/tools.js.map +1 -0
  163. package/dist/admin/uploads.d.ts +28 -2
  164. package/dist/admin/uploads.d.ts.map +1 -1
  165. package/dist/admin/uploads.js +43 -4
  166. package/dist/admin/uploads.js.map +1 -1
  167. package/dist/admin/users.d.ts +100 -13
  168. package/dist/admin/users.d.ts.map +1 -1
  169. package/dist/admin/users.js +412 -39
  170. package/dist/admin/users.js.map +1 -1
  171. package/dist/cache.d.ts +141 -0
  172. package/dist/cache.d.ts.map +1 -0
  173. package/dist/cache.js +207 -0
  174. package/dist/cache.js.map +1 -0
  175. package/dist/cli.d.ts +14 -2
  176. package/dist/cli.d.ts.map +1 -1
  177. package/dist/cli.js +429 -21
  178. package/dist/cli.js.map +1 -1
  179. package/dist/comments/akismet.d.ts +130 -0
  180. package/dist/comments/akismet.d.ts.map +1 -0
  181. package/dist/comments/akismet.js +274 -0
  182. package/dist/comments/akismet.js.map +1 -0
  183. package/dist/comments/conversation.d.ts +29 -0
  184. package/dist/comments/conversation.d.ts.map +1 -0
  185. package/dist/comments/conversation.js +60 -0
  186. package/dist/comments/conversation.js.map +1 -0
  187. package/dist/comments/form.d.ts +123 -0
  188. package/dist/comments/form.d.ts.map +1 -0
  189. package/dist/comments/form.js +79 -0
  190. package/dist/comments/form.js.map +1 -0
  191. package/dist/comments/index.d.ts +16 -0
  192. package/dist/comments/index.d.ts.map +1 -0
  193. package/dist/comments/index.js +10 -0
  194. package/dist/comments/index.js.map +1 -0
  195. package/dist/comments/markdown.d.ts +8 -0
  196. package/dist/comments/markdown.d.ts.map +1 -0
  197. package/dist/comments/markdown.js +85 -0
  198. package/dist/comments/markdown.js.map +1 -0
  199. package/dist/comments/moderate.d.ts +71 -0
  200. package/dist/comments/moderate.d.ts.map +1 -0
  201. package/dist/comments/moderate.js +88 -0
  202. package/dist/comments/moderate.js.map +1 -0
  203. package/dist/comments/policy.d.ts +57 -0
  204. package/dist/comments/policy.d.ts.map +1 -0
  205. package/dist/comments/policy.js +80 -0
  206. package/dist/comments/policy.js.map +1 -0
  207. package/dist/comments/records.d.ts +266 -0
  208. package/dist/comments/records.d.ts.map +1 -0
  209. package/dist/comments/records.js +487 -0
  210. package/dist/comments/records.js.map +1 -0
  211. package/dist/comments/routes.d.ts +60 -0
  212. package/dist/comments/routes.d.ts.map +1 -0
  213. package/dist/comments/routes.js +212 -0
  214. package/dist/comments/routes.js.map +1 -0
  215. package/dist/comments/submission.d.ts +297 -0
  216. package/dist/comments/submission.d.ts.map +1 -0
  217. package/dist/comments/submission.js +280 -0
  218. package/dist/comments/submission.js.map +1 -0
  219. package/dist/comments/viewer.d.ts +21 -0
  220. package/dist/comments/viewer.d.ts.map +1 -0
  221. package/dist/comments/viewer.js +45 -0
  222. package/dist/comments/viewer.js.map +1 -0
  223. package/dist/config.d.ts +225 -5
  224. package/dist/config.d.ts.map +1 -1
  225. package/dist/config.js +121 -6
  226. package/dist/config.js.map +1 -1
  227. package/dist/contact/delivery.d.ts +60 -0
  228. package/dist/contact/delivery.d.ts.map +1 -0
  229. package/dist/contact/delivery.js +87 -0
  230. package/dist/contact/delivery.js.map +1 -0
  231. package/dist/contact/form.d.ts +130 -0
  232. package/dist/contact/form.d.ts.map +1 -0
  233. package/dist/contact/form.js +163 -0
  234. package/dist/contact/form.js.map +1 -0
  235. package/dist/contact/index.d.ts +17 -0
  236. package/dist/contact/index.d.ts.map +1 -0
  237. package/dist/contact/index.js +13 -0
  238. package/dist/contact/index.js.map +1 -0
  239. package/dist/contact/records.d.ts +124 -0
  240. package/dist/contact/records.d.ts.map +1 -0
  241. package/dist/contact/records.js +218 -0
  242. package/dist/contact/records.js.map +1 -0
  243. package/dist/contact/routes.d.ts +17 -0
  244. package/dist/contact/routes.d.ts.map +1 -0
  245. package/dist/contact/routes.js +127 -0
  246. package/dist/contact/routes.js.map +1 -0
  247. package/dist/contact/submission.d.ts +103 -0
  248. package/dist/contact/submission.d.ts.map +1 -0
  249. package/dist/contact/submission.js +166 -0
  250. package/dist/contact/submission.js.map +1 -0
  251. package/dist/content/document.d.ts +7 -2
  252. package/dist/content/document.d.ts.map +1 -1
  253. package/dist/content/document.js +1 -0
  254. package/dist/content/document.js.map +1 -1
  255. package/dist/content/index.d.ts +7 -2
  256. package/dist/content/index.d.ts.map +1 -1
  257. package/dist/content/index.js +4 -1
  258. package/dist/content/index.js.map +1 -1
  259. package/dist/content/markdown.d.ts.map +1 -1
  260. package/dist/content/markdown.js +24 -2
  261. package/dist/content/markdown.js.map +1 -1
  262. package/dist/content/parser.d.ts.map +1 -1
  263. package/dist/content/parser.js +11 -4
  264. package/dist/content/parser.js.map +1 -1
  265. package/dist/content/save.d.ts +5 -0
  266. package/dist/content/save.d.ts.map +1 -1
  267. package/dist/content/save.js +44 -1
  268. package/dist/content/save.js.map +1 -1
  269. package/dist/content/schedule.d.ts +125 -0
  270. package/dist/content/schedule.d.ts.map +1 -0
  271. package/dist/content/schedule.js +207 -0
  272. package/dist/content/schedule.js.map +1 -0
  273. package/dist/content/search.d.ts +64 -0
  274. package/dist/content/search.d.ts.map +1 -0
  275. package/dist/content/search.js +136 -0
  276. package/dist/content/search.js.map +1 -0
  277. package/dist/content/store.d.ts +220 -7
  278. package/dist/content/store.d.ts.map +1 -1
  279. package/dist/content/store.js +392 -67
  280. package/dist/content/store.js.map +1 -1
  281. package/dist/content/sync.d.ts +7 -1
  282. package/dist/content/sync.d.ts.map +1 -1
  283. package/dist/content/sync.js +18 -10
  284. package/dist/content/sync.js.map +1 -1
  285. package/dist/content/time.d.ts +56 -0
  286. package/dist/content/time.d.ts.map +1 -0
  287. package/dist/content/time.js +141 -0
  288. package/dist/content/time.js.map +1 -0
  289. package/dist/content/writer.d.ts.map +1 -1
  290. package/dist/content/writer.js +3 -0
  291. package/dist/content/writer.js.map +1 -1
  292. package/dist/env.d.ts +70 -5
  293. package/dist/env.d.ts.map +1 -1
  294. package/dist/federation/actor.d.ts +95 -41
  295. package/dist/federation/actor.d.ts.map +1 -1
  296. package/dist/federation/actor.js +180 -53
  297. package/dist/federation/actor.js.map +1 -1
  298. package/dist/federation/article.d.ts +44 -17
  299. package/dist/federation/article.d.ts.map +1 -1
  300. package/dist/federation/article.js +98 -46
  301. package/dist/federation/article.js.map +1 -1
  302. package/dist/federation/delivery.d.ts +63 -15
  303. package/dist/federation/delivery.d.ts.map +1 -1
  304. package/dist/federation/delivery.js +154 -80
  305. package/dist/federation/delivery.js.map +1 -1
  306. package/dist/federation/federation.d.ts +22 -17
  307. package/dist/federation/federation.d.ts.map +1 -1
  308. package/dist/federation/federation.js +98 -77
  309. package/dist/federation/federation.js.map +1 -1
  310. package/dist/federation/followers.d.ts +3 -2
  311. package/dist/federation/followers.d.ts.map +1 -1
  312. package/dist/federation/followers.js +6 -5
  313. package/dist/federation/followers.js.map +1 -1
  314. package/dist/federation/import-wordpress.d.ts +128 -0
  315. package/dist/federation/import-wordpress.d.ts.map +1 -0
  316. package/dist/federation/import-wordpress.js +426 -0
  317. package/dist/federation/import-wordpress.js.map +1 -0
  318. package/dist/federation/inbox.d.ts +36 -13
  319. package/dist/federation/inbox.d.ts.map +1 -1
  320. package/dist/federation/inbox.js +120 -49
  321. package/dist/federation/inbox.js.map +1 -1
  322. package/dist/federation/index.d.ts +22 -10
  323. package/dist/federation/index.d.ts.map +1 -1
  324. package/dist/federation/index.js +13 -8
  325. package/dist/federation/index.js.map +1 -1
  326. package/dist/federation/keys.d.ts +98 -20
  327. package/dist/federation/keys.d.ts.map +1 -1
  328. package/dist/federation/keys.js +269 -39
  329. package/dist/federation/keys.js.map +1 -1
  330. package/dist/federation/mount.d.ts +46 -10
  331. package/dist/federation/mount.d.ts.map +1 -1
  332. package/dist/federation/mount.js +300 -28
  333. package/dist/federation/mount.js.map +1 -1
  334. package/dist/federation/paths.d.ts +23 -35
  335. package/dist/federation/paths.d.ts.map +1 -1
  336. package/dist/federation/paths.js +26 -39
  337. package/dist/federation/paths.js.map +1 -1
  338. package/dist/federation/records.d.ts +223 -0
  339. package/dist/federation/records.d.ts.map +1 -0
  340. package/dist/federation/records.js +506 -0
  341. package/dist/federation/records.js.map +1 -0
  342. package/dist/federation/relays.d.ts +114 -0
  343. package/dist/federation/relays.d.ts.map +1 -0
  344. package/dist/federation/relays.js +290 -0
  345. package/dist/federation/relays.js.map +1 -0
  346. package/dist/federation/replies.d.ts +80 -0
  347. package/dist/federation/replies.d.ts.map +1 -0
  348. package/dist/federation/replies.js +166 -0
  349. package/dist/federation/replies.js.map +1 -0
  350. package/dist/federation/wordpress.d.ts +131 -0
  351. package/dist/federation/wordpress.d.ts.map +1 -0
  352. package/dist/federation/wordpress.js +341 -0
  353. package/dist/federation/wordpress.js.map +1 -0
  354. package/dist/files/atomic.d.ts +103 -0
  355. package/dist/files/atomic.d.ts.map +1 -0
  356. package/dist/files/atomic.js +150 -0
  357. package/dist/files/atomic.js.map +1 -0
  358. package/dist/files/index.d.ts +3 -0
  359. package/dist/files/index.d.ts.map +1 -0
  360. package/dist/files/index.js +2 -0
  361. package/dist/files/index.js.map +1 -0
  362. package/dist/forms/protection.d.ts +85 -0
  363. package/dist/forms/protection.d.ts.map +1 -0
  364. package/dist/forms/protection.js +116 -0
  365. package/dist/forms/protection.js.map +1 -0
  366. package/dist/images/icons.d.ts +59 -0
  367. package/dist/images/icons.d.ts.map +1 -0
  368. package/dist/images/icons.js +131 -0
  369. package/dist/images/icons.js.map +1 -0
  370. package/dist/images/index.d.ts +7 -0
  371. package/dist/images/index.d.ts.map +1 -0
  372. package/dist/images/index.js +4 -0
  373. package/dist/images/index.js.map +1 -0
  374. package/dist/images/markup.d.ts +47 -0
  375. package/dist/images/markup.d.ts.map +1 -0
  376. package/dist/images/markup.js +175 -0
  377. package/dist/images/markup.js.map +1 -0
  378. package/dist/images/paths.d.ts +34 -0
  379. package/dist/images/paths.d.ts.map +1 -0
  380. package/dist/images/paths.js +41 -0
  381. package/dist/images/paths.js.map +1 -0
  382. package/dist/images/variants.d.ts +104 -0
  383. package/dist/images/variants.d.ts.map +1 -0
  384. package/dist/images/variants.js +320 -0
  385. package/dist/images/variants.js.map +1 -0
  386. package/dist/index.d.ts +123 -19
  387. package/dist/index.d.ts.map +1 -1
  388. package/dist/index.js +344 -25
  389. package/dist/index.js.map +1 -1
  390. package/dist/init.d.ts +18 -0
  391. package/dist/init.d.ts.map +1 -1
  392. package/dist/init.js +34 -3
  393. package/dist/init.js.map +1 -1
  394. package/dist/mail/brevo.d.ts +35 -0
  395. package/dist/mail/brevo.d.ts.map +1 -0
  396. package/dist/mail/brevo.js +85 -0
  397. package/dist/mail/brevo.js.map +1 -0
  398. package/dist/mail/credentials.d.ts +62 -0
  399. package/dist/mail/credentials.d.ts.map +1 -0
  400. package/dist/mail/credentials.js +86 -0
  401. package/dist/mail/credentials.js.map +1 -0
  402. package/dist/mail/index.d.ts +15 -0
  403. package/dist/mail/index.d.ts.map +1 -0
  404. package/dist/mail/index.js +8 -0
  405. package/dist/mail/index.js.map +1 -0
  406. package/dist/mail/memory.d.ts +35 -0
  407. package/dist/mail/memory.d.ts.map +1 -0
  408. package/dist/mail/memory.js +29 -0
  409. package/dist/mail/memory.js.map +1 -0
  410. package/dist/mail/provider.d.ts +80 -0
  411. package/dist/mail/provider.d.ts.map +1 -0
  412. package/dist/mail/provider.js +23 -0
  413. package/dist/mail/provider.js.map +1 -0
  414. package/dist/mail/service.d.ts +156 -0
  415. package/dist/mail/service.d.ts.map +1 -0
  416. package/dist/mail/service.js +265 -0
  417. package/dist/mail/service.js.map +1 -0
  418. package/dist/mail/smtp.d.ts +39 -0
  419. package/dist/mail/smtp.d.ts.map +1 -0
  420. package/dist/mail/smtp.js +58 -0
  421. package/dist/mail/smtp.js.map +1 -0
  422. package/dist/mail/templates.d.ts +91 -0
  423. package/dist/mail/templates.d.ts.map +1 -0
  424. package/dist/mail/templates.js +69 -0
  425. package/dist/mail/templates.js.map +1 -0
  426. package/dist/notifications/comments.d.ts +66 -0
  427. package/dist/notifications/comments.d.ts.map +1 -0
  428. package/dist/notifications/comments.js +151 -0
  429. package/dist/notifications/comments.js.map +1 -0
  430. package/dist/notifications/digest.d.ts +135 -0
  431. package/dist/notifications/digest.d.ts.map +1 -0
  432. package/dist/notifications/digest.js +287 -0
  433. package/dist/notifications/digest.js.map +1 -0
  434. package/dist/notifications/index.d.ts +13 -0
  435. package/dist/notifications/index.d.ts.map +1 -0
  436. package/dist/notifications/index.js +8 -0
  437. package/dist/notifications/index.js.map +1 -0
  438. package/dist/notifications/links.d.ts +60 -0
  439. package/dist/notifications/links.d.ts.map +1 -0
  440. package/dist/notifications/links.js +63 -0
  441. package/dist/notifications/links.js.map +1 -0
  442. package/dist/notifications/optouts.d.ts +48 -0
  443. package/dist/notifications/optouts.d.ts.map +1 -0
  444. package/dist/notifications/optouts.js +93 -0
  445. package/dist/notifications/optouts.js.map +1 -0
  446. package/dist/notifications/preferences.d.ts +158 -0
  447. package/dist/notifications/preferences.d.ts.map +1 -0
  448. package/dist/notifications/preferences.js +195 -0
  449. package/dist/notifications/preferences.js.map +1 -0
  450. package/dist/notifications/routes.d.ts +28 -0
  451. package/dist/notifications/routes.d.ts.map +1 -0
  452. package/dist/notifications/routes.js +184 -0
  453. package/dist/notifications/routes.js.map +1 -0
  454. package/dist/notifications/tokens.d.ts +60 -0
  455. package/dist/notifications/tokens.d.ts.map +1 -0
  456. package/dist/notifications/tokens.js +125 -0
  457. package/dist/notifications/tokens.js.map +1 -0
  458. package/dist/notify.d.ts +84 -0
  459. package/dist/notify.d.ts.map +1 -0
  460. package/dist/notify.js +149 -0
  461. package/dist/notify.js.map +1 -0
  462. package/dist/web/archive.d.ts +49 -0
  463. package/dist/web/archive.d.ts.map +1 -0
  464. package/dist/web/archive.js +59 -0
  465. package/dist/web/archive.js.map +1 -0
  466. package/dist/web/assets.d.ts +6 -5
  467. package/dist/web/assets.d.ts.map +1 -1
  468. package/dist/web/assets.js +5 -7
  469. package/dist/web/assets.js.map +1 -1
  470. package/dist/web/authors.d.ts +143 -0
  471. package/dist/web/authors.d.ts.map +1 -0
  472. package/dist/web/authors.js +178 -0
  473. package/dist/web/authors.js.map +1 -0
  474. package/dist/web/comments.d.ts +60 -0
  475. package/dist/web/comments.d.ts.map +1 -0
  476. package/dist/web/comments.js +207 -0
  477. package/dist/web/comments.js.map +1 -0
  478. package/dist/web/context.d.ts +162 -28
  479. package/dist/web/context.d.ts.map +1 -1
  480. package/dist/web/context.js +123 -26
  481. package/dist/web/context.js.map +1 -1
  482. package/dist/web/conversation.d.ts +209 -0
  483. package/dist/web/conversation.d.ts.map +1 -0
  484. package/dist/web/conversation.js +557 -0
  485. package/dist/web/conversation.js.map +1 -0
  486. package/dist/web/documents.d.ts +36 -6
  487. package/dist/web/documents.d.ts.map +1 -1
  488. package/dist/web/documents.js +52 -10
  489. package/dist/web/documents.js.map +1 -1
  490. package/dist/web/feed-atom.d.ts +22 -0
  491. package/dist/web/feed-atom.d.ts.map +1 -0
  492. package/dist/web/feed-atom.js +83 -0
  493. package/dist/web/feed-atom.js.map +1 -0
  494. package/dist/web/feed-item.d.ts +145 -0
  495. package/dist/web/feed-item.d.ts.map +1 -0
  496. package/dist/web/feed-item.js +120 -0
  497. package/dist/web/feed-item.js.map +1 -0
  498. package/dist/web/feed-json.d.ts +79 -0
  499. package/dist/web/feed-json.d.ts.map +1 -0
  500. package/dist/web/feed-json.js +53 -0
  501. package/dist/web/feed-json.js.map +1 -0
  502. package/dist/web/feed-rss.d.ts +26 -0
  503. package/dist/web/feed-rss.d.ts.map +1 -0
  504. package/dist/web/feed-rss.js +203 -0
  505. package/dist/web/feed-rss.js.map +1 -0
  506. package/dist/web/feed-source.d.ts +244 -0
  507. package/dist/web/feed-source.d.ts.map +1 -0
  508. package/dist/web/feed-source.js +201 -0
  509. package/dist/web/feed-source.js.map +1 -0
  510. package/dist/web/feed-xml.d.ts +63 -0
  511. package/dist/web/feed-xml.d.ts.map +1 -0
  512. package/dist/web/feed-xml.js +116 -0
  513. package/dist/web/feed-xml.js.map +1 -0
  514. package/dist/web/feeds.d.ts +31 -96
  515. package/dist/web/feeds.d.ts.map +1 -1
  516. package/dist/web/feeds.js +94 -198
  517. package/dist/web/feeds.js.map +1 -1
  518. package/dist/web/health.d.ts +31 -0
  519. package/dist/web/health.d.ts.map +1 -0
  520. package/dist/web/health.js +44 -0
  521. package/dist/web/health.js.map +1 -0
  522. package/dist/web/index.d.ts +30 -10
  523. package/dist/web/index.d.ts.map +1 -1
  524. package/dist/web/index.js +18 -7
  525. package/dist/web/index.js.map +1 -1
  526. package/dist/web/navigation.d.ts +195 -0
  527. package/dist/web/navigation.d.ts.map +1 -0
  528. package/dist/web/navigation.js +300 -0
  529. package/dist/web/navigation.js.map +1 -0
  530. package/dist/web/negotiate.d.ts +31 -0
  531. package/dist/web/negotiate.d.ts.map +1 -1
  532. package/dist/web/negotiate.js +31 -11
  533. package/dist/web/negotiate.js.map +1 -1
  534. package/dist/web/recent.d.ts +43 -0
  535. package/dist/web/recent.d.ts.map +1 -0
  536. package/dist/web/recent.js +37 -0
  537. package/dist/web/recent.js.map +1 -0
  538. package/dist/web/render.d.ts +203 -9
  539. package/dist/web/render.d.ts.map +1 -1
  540. package/dist/web/render.js +341 -17
  541. package/dist/web/render.js.map +1 -1
  542. package/dist/web/routes.d.ts +25 -8
  543. package/dist/web/routes.d.ts.map +1 -1
  544. package/dist/web/routes.js +854 -93
  545. package/dist/web/routes.js.map +1 -1
  546. package/dist/web/sanitize.d.ts +19 -0
  547. package/dist/web/sanitize.d.ts.map +1 -0
  548. package/dist/web/sanitize.js +272 -0
  549. package/dist/web/sanitize.js.map +1 -0
  550. package/dist/web/search.d.ts +79 -0
  551. package/dist/web/search.d.ts.map +1 -0
  552. package/dist/web/search.js +94 -0
  553. package/dist/web/search.js.map +1 -0
  554. package/dist/web/sitemap.d.ts +120 -0
  555. package/dist/web/sitemap.d.ts.map +1 -0
  556. package/dist/web/sitemap.js +199 -0
  557. package/dist/web/sitemap.js.map +1 -0
  558. package/dist/web/taxonomy.d.ts +152 -0
  559. package/dist/web/taxonomy.d.ts.map +1 -0
  560. package/dist/web/taxonomy.js +237 -0
  561. package/dist/web/taxonomy.js.map +1 -0
  562. package/dist/web/templates.d.ts +54 -16
  563. package/dist/web/templates.d.ts.map +1 -1
  564. package/dist/web/templates.js +109 -28
  565. package/dist/web/templates.js.map +1 -1
  566. package/dist/web/themes.d.ts +226 -0
  567. package/dist/web/themes.d.ts.map +1 -0
  568. package/dist/web/themes.js +314 -0
  569. package/dist/web/themes.js.map +1 -0
  570. package/dist/webmention/discovery.d.ts +51 -0
  571. package/dist/webmention/discovery.d.ts.map +1 -0
  572. package/dist/webmention/discovery.js +176 -0
  573. package/dist/webmention/discovery.js.map +1 -0
  574. package/dist/webmention/html.d.ts +71 -0
  575. package/dist/webmention/html.d.ts.map +1 -0
  576. package/dist/webmention/html.js +285 -0
  577. package/dist/webmention/html.js.map +1 -0
  578. package/dist/webmention/index.d.ts +12 -0
  579. package/dist/webmention/index.d.ts.map +1 -0
  580. package/dist/webmention/index.js +8 -0
  581. package/dist/webmention/index.js.map +1 -0
  582. package/dist/webmention/links.d.ts +18 -0
  583. package/dist/webmention/links.d.ts.map +1 -0
  584. package/dist/webmention/links.js +64 -0
  585. package/dist/webmention/links.js.map +1 -0
  586. package/dist/webmention/microformats.d.ts +93 -0
  587. package/dist/webmention/microformats.d.ts.map +1 -0
  588. package/dist/webmention/microformats.js +376 -0
  589. package/dist/webmention/microformats.js.map +1 -0
  590. package/dist/webmention/receive.d.ts +141 -0
  591. package/dist/webmention/receive.d.ts.map +1 -0
  592. package/dist/webmention/receive.js +264 -0
  593. package/dist/webmention/receive.js.map +1 -0
  594. package/dist/webmention/routes.d.ts +37 -0
  595. package/dist/webmention/routes.d.ts.map +1 -0
  596. package/dist/webmention/routes.js +75 -0
  597. package/dist/webmention/routes.js.map +1 -0
  598. package/dist/webmention/service.d.ts +94 -0
  599. package/dist/webmention/service.d.ts.map +1 -0
  600. package/dist/webmention/service.js +207 -0
  601. package/dist/webmention/service.js.map +1 -0
  602. package/package.json +11 -6
  603. package/templates/site/content/_data/site.json +8 -0
  604. package/templates/site/content/pages/about.md +7 -0
  605. package/templates/site/content/posts/2026-01-01-hello-world.md +5 -3
  606. package/templates/site/geekity.config.ts +11 -1
  607. package/templates/site/tsconfig.json +1 -1
  608. package/themes/default/README.md +1216 -50
  609. package/themes/default/layouts/404.njk +2 -3
  610. package/themes/default/layouts/author.njk +36 -0
  611. package/themes/default/layouts/base.njk +161 -31
  612. package/themes/default/layouts/category.njk +27 -0
  613. package/themes/default/layouts/front-page.njk +56 -0
  614. package/themes/default/layouts/home.njk +17 -1
  615. package/themes/default/layouts/page.njk +57 -5
  616. package/themes/default/layouts/post.njk +78 -19
  617. package/themes/default/layouts/search.njk +57 -0
  618. package/themes/default/layouts/tag.njk +3 -6
  619. package/themes/default/mail/comment-digest.html.njk +37 -0
  620. package/themes/default/mail/comment-digest.subject.njk +3 -0
  621. package/themes/default/mail/comment-digest.txt.njk +40 -0
  622. package/themes/default/mail/comment-pending.html.njk +27 -0
  623. package/themes/default/mail/comment-pending.subject.njk +2 -0
  624. package/themes/default/mail/comment-pending.txt.njk +24 -0
  625. package/themes/default/mail/comment-reply.html.njk +21 -0
  626. package/themes/default/mail/comment-reply.subject.njk +2 -0
  627. package/themes/default/mail/comment-reply.txt.njk +22 -0
  628. package/themes/default/mail/contact-message.html.njk +24 -0
  629. package/themes/default/mail/contact-message.subject.njk +5 -0
  630. package/themes/default/mail/contact-message.txt.njk +20 -0
  631. package/themes/default/mail/password-changed.html.njk +18 -0
  632. package/themes/default/mail/password-changed.subject.njk +2 -0
  633. package/themes/default/mail/password-changed.txt.njk +17 -0
  634. package/themes/default/mail/password-reset.html.njk +22 -0
  635. package/themes/default/mail/password-reset.subject.njk +2 -0
  636. package/themes/default/mail/password-reset.txt.njk +18 -0
  637. package/themes/default/mail/test.html.njk +14 -0
  638. package/themes/default/mail/test.subject.njk +2 -0
  639. package/themes/default/mail/test.txt.njk +12 -0
  640. package/themes/default/partials/archive.njk +27 -0
  641. package/themes/default/partials/bio.njk +71 -0
  642. package/themes/default/partials/comment-form.njk +137 -0
  643. package/themes/default/partials/contact-form.njk +88 -0
  644. package/themes/default/partials/conversation.njk +139 -0
  645. package/themes/default/partials/feeds.njk +26 -0
  646. package/themes/default/partials/jsonld.njk +120 -0
  647. package/themes/default/partials/menu.njk +32 -0
  648. package/themes/default/partials/pagination.njk +12 -6
  649. package/themes/default/partials/post-list.njk +51 -18
  650. package/themes/default/partials/search-form.njk +22 -0
  651. package/themes/default/partials/tags.njk +26 -5
  652. package/themes/default/static/highlight.js +4 -0
  653. package/themes/default/static/style.css +1142 -194
  654. package/themes/default/theme.json +9 -0
  655. package/admin/layouts/federation.njk +0 -145
  656. package/admin/layouts/login.njk +0 -23
  657. package/admin/layouts/settings.njk +0 -116
  658. package/admin/layouts/setup.njk +0 -27
  659. package/admin/layouts/users.njk +0 -91
  660. /package/admin/{layouts/_flash.njk → components/flash.njk} +0 -0
@@ -1,10 +1,10 @@
1
- import { mkdirSync } from 'node:fs';
2
- import path from 'node:path';
3
- import { DatabaseSync } from 'node:sqlite';
4
- /** File name of the derived index inside a site's data directory. */
5
- export const DATABASE_FILE = 'geekity.db';
1
+ import { databaseFile, openDatabase } from '../cache.js';
2
+ import { searchExpression, searchText, SNIPPET_CLOSE, SNIPPET_OPEN } from './search.js';
3
+ export { DATABASE_FILE } from '../cache.js';
6
4
  /** Directory name that marks a document as thrown away but not yet deleted. */
7
5
  export const TRASH_DIRECTORY = '_trash';
6
+ /** The clock an index uses when it is not given one. */
7
+ export const systemClock = () => new Date();
8
8
  /**
9
9
  * Thrown when two files claim the same permalink. Two documents at one URL is
10
10
  * a content mistake the site owner has to fix, so the index refuses it rather
@@ -47,18 +47,67 @@ export function dateSortKey(date) {
47
47
  const parsed = new Date(date);
48
48
  return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
49
49
  }
50
+ /**
51
+ * The clause every public query carries: a document is public only once its
52
+ * date has arrived.
53
+ *
54
+ * A document with no date — or one nobody can read — has nothing to wait for
55
+ * and is public straight away, which is what keeps an undated page out of the
56
+ * scheduling rule without a second predicate. The value compared against is a
57
+ * {@link dateSortKey} of the clock, so the comparison is between two UTC ISO
58
+ * strings and lexicographic order is chronological order.
59
+ */
60
+ const DUE_CLAUSE = '(date_sort IS NULL OR date_sort <= ?)';
61
+ /** The reverse: a document whose date is still ahead of the clock. */
62
+ const SCHEDULED_CLAUSE = '(date_sort IS NOT NULL AND date_sort > ?)';
63
+ /**
64
+ * The clause that picks out a document some follower holds a copy of: one
65
+ * whose `activitypub` block records when it was announced.
66
+ *
67
+ * `published` rather than `id`, because after decision-13 a post's id is its
68
+ * permalink and is never written into the file: the announcement date is the
69
+ * only mark a delivery leaves, and it is the one thing that says a follower
70
+ * was ever told. The block is stored as JSON rather than shredded into
71
+ * columns, so the date is read back out of it here. An empty value is no
72
+ * value: the front matter is a file somebody may have typed, and
73
+ * `activitypub: {published: ""}` is what a half-finished hand edit looks like.
74
+ */
75
+ const FEDERATED_CLAUSE = `COALESCE(json_extract(activitypub, '$.published'), '') <> ''`;
76
+ /**
77
+ * The public side of a search: the full-text match joined to the documents it
78
+ * indexes, held to exactly what every public listing is held to.
79
+ *
80
+ * The draft, trash and due clauses are applied to the documents table rather
81
+ * than indexed alongside the words, so a post that is drafted, trashed or
82
+ * pushed into the future drops out of the results the moment the listings drop
83
+ * it, with no second write to keep in step.
84
+ */
85
+ const SEARCH_FROM = `
86
+ FROM documents_fts
87
+ JOIN documents ON documents.path = documents_fts.path
88
+ WHERE documents_fts MATCH ?
89
+ AND documents.draft = 0 AND documents.trashed = 0
90
+ AND (documents.date_sort IS NULL OR documents.date_sort <= ?)
91
+ `;
92
+ /**
93
+ * How much a match in each column of the index counts, in the order the table
94
+ * declares them: the path is not indexed, a title match counts for most, the
95
+ * description and the taxonomy for half as much, and the body for least.
96
+ */
97
+ const SEARCH_WEIGHTS = '0.0, 10.0, 5.0, 5.0, 1.0';
50
98
  /**
51
99
  * Open (and if needed create) the index in `dataDir`, applying every migration
52
100
  * the package ships. Applying them is idempotent, so reopening an up-to-date
53
101
  * database does nothing.
54
102
  */
55
103
  export function openContentStore(options) {
56
- const file = path.join(options.dataDir, DATABASE_FILE);
57
- mkdirSync(options.dataDir, { recursive: true });
58
- const db = new DatabaseSync(file);
59
- db.exec('PRAGMA journal_mode = WAL');
60
- db.exec('PRAGMA foreign_keys = ON');
61
- migrate(db);
104
+ const clock = options.now ?? systemClock;
105
+ /** The clock as the index sorts dates, so the two compare as strings. */
106
+ function nowKey() {
107
+ return clock().toISOString();
108
+ }
109
+ const file = databaseFile(options.dataDir);
110
+ const db = openDatabase({ dataDir: options.dataDir, ledger: LEDGER, migrations: MIGRATIONS });
62
111
  const statements = {
63
112
  insert: db.prepare(`
64
113
  INSERT INTO documents (
@@ -88,19 +137,24 @@ export function openContentStore(options) {
88
137
  `),
89
138
  deleteTags: db.prepare('DELETE FROM document_tags WHERE path = ?'),
90
139
  insertTag: db.prepare('INSERT INTO document_tags (path, tag, position) VALUES (?, ?, ?)'),
140
+ deleteCategories: db.prepare('DELETE FROM document_categories WHERE path = ?'),
141
+ insertCategory: db.prepare('INSERT INTO document_categories (path, category, position) VALUES (?, ?, ?)'),
91
142
  pathForPermalink: db.prepare('SELECT path FROM documents WHERE permalink = ?'),
92
143
  remove: db.prepare('DELETE FROM documents WHERE path = ?'),
93
144
  byPath: db.prepare('SELECT * FROM documents WHERE path = ?'),
94
145
  byPermalink: db.prepare('SELECT * FROM documents WHERE permalink = ?'),
146
+ byStoredObjectId: db.prepare(`SELECT * FROM documents WHERE json_extract(activitypub, '$.id') = ? LIMIT 1`),
95
147
  bySlug: db.prepare(`SELECT * FROM documents WHERE slug = ? ORDER BY date_sort DESC, path DESC LIMIT 1`),
96
148
  tagsFor: db.prepare('SELECT tag FROM document_tags WHERE path = ? ORDER BY position'),
149
+ categoriesFor: db.prepare('SELECT category FROM document_categories WHERE path = ? ORDER BY position'),
97
150
  paths: db.prepare('SELECT path FROM documents ORDER BY path'),
98
151
  counts: db.prepare(`
99
152
  SELECT
100
153
  COUNT(*) AS total,
101
- COALESCE(SUM(type = 'post' AND draft = 0 AND trashed = 0), 0) AS posts,
102
- COALESCE(SUM(type = 'page' AND draft = 0 AND trashed = 0), 0) AS pages,
154
+ COALESCE(SUM(type = 'post' AND draft = 0 AND trashed = 0 AND ${DUE_CLAUSE}), 0) AS posts,
155
+ COALESCE(SUM(type = 'page' AND draft = 0 AND trashed = 0 AND ${DUE_CLAUSE}), 0) AS pages,
103
156
  COALESCE(SUM(draft = 1 AND trashed = 0), 0) AS drafts,
157
+ COALESCE(SUM(draft = 0 AND trashed = 0 AND ${SCHEDULED_CLAUSE}), 0) AS scheduled,
104
158
  COALESCE(SUM(trashed = 1), 0) AS trashed
105
159
  FROM documents
106
160
  `),
@@ -109,21 +163,60 @@ export function openContentStore(options) {
109
163
  FROM document_tags
110
164
  JOIN documents ON documents.path = document_tags.path
111
165
  WHERE documents.draft = 0 AND documents.trashed = 0
166
+ AND (documents.date_sort IS NULL OR documents.date_sort <= ?)
112
167
  GROUP BY document_tags.tag
113
168
  ORDER BY count DESC, tag ASC
169
+ `),
170
+ categoryCounts: db.prepare(`
171
+ SELECT document_categories.category AS category, COUNT(*) AS count
172
+ FROM document_categories
173
+ JOIN documents ON documents.path = document_categories.path
174
+ WHERE documents.draft = 0 AND documents.trashed = 0
175
+ AND (documents.date_sort IS NULL OR documents.date_sort <= ?)
176
+ GROUP BY document_categories.category
177
+ ORDER BY count DESC, category ASC
178
+ `),
179
+ tagUsage: db.prepare(termUsageSql('document_tags', 'tag')),
180
+ categoryUsage: db.prepare(termUsageSql('document_categories', 'category')),
181
+ nextDue: db.prepare(`
182
+ SELECT MIN(date_sort) AS due FROM documents
183
+ WHERE draft = 0 AND trashed = 0 AND ${SCHEDULED_CLAUSE}
184
+ `),
185
+ deleteText: db.prepare('DELETE FROM documents_fts WHERE path = ?'),
186
+ insertText: db.prepare('INSERT INTO documents_fts (path, title, description, terms, body) VALUES (?, ?, ?, ?, ?)'),
187
+ search: db.prepare(`
188
+ SELECT documents.*,
189
+ snippet(documents_fts, -1, '${SNIPPET_OPEN}', '${SNIPPET_CLOSE}', '…', 24) AS snippet
190
+ ${SEARCH_FROM}
191
+ ORDER BY bm25(documents_fts, ${SEARCH_WEIGHTS}), documents.date_sort DESC, documents.path DESC
192
+ LIMIT ? OFFSET ?
193
+ `),
194
+ countSearch: db.prepare(`SELECT COUNT(*) AS count ${SEARCH_FROM}`),
195
+ dueSince: db.prepare(`
196
+ SELECT * FROM documents
197
+ WHERE draft = 0 AND trashed = 0
198
+ AND date_sort IS NOT NULL AND date_sort > ? AND date_sort <= ?
199
+ ORDER BY date_sort ASC, path ASC
114
200
  `),
115
201
  };
116
202
  let open = true;
117
203
  function tagsOf(contentPath) {
118
204
  return statements.tagsFor.all(contentPath).map((row) => String(row['tag']));
119
205
  }
206
+ function categoriesOf(contentPath) {
207
+ return statements.categoriesFor.all(contentPath).map((row) => String(row['category']));
208
+ }
120
209
  function hydrate(row) {
121
210
  if (row === undefined)
122
211
  return undefined;
123
- return toDocument(row, tagsOf(String(row['path'])));
212
+ return hydrateOne(row);
213
+ }
214
+ function hydrateOne(row) {
215
+ const contentPath = String(row['path']);
216
+ return toDocument(row, tagsOf(contentPath), categoriesOf(contentPath));
124
217
  }
125
218
  function hydrateAll(rows) {
126
- return rows.map((row) => toDocument(row, tagsOf(String(row['path']))));
219
+ return rows.map(hydrateOne);
127
220
  }
128
221
  function writeOne(document) {
129
222
  const contentPath = document.path;
@@ -137,6 +230,72 @@ export function openContentStore(options) {
137
230
  document.tags.forEach((tag, position) => {
138
231
  statements.insertTag.run(contentPath, tag, position);
139
232
  });
233
+ statements.deleteCategories.run(contentPath);
234
+ document.categories.forEach((category, position) => {
235
+ statements.insertCategory.run(contentPath, category, position);
236
+ });
237
+ // The words it is found by. Replaced rather than updated, because an FTS5
238
+ // table has no key to conflict on: the path is only a column in it.
239
+ const words = searchText(document);
240
+ statements.deleteText.run(contentPath);
241
+ statements.insertText.run(contentPath, words.title, words.description, words.terms, words.body);
242
+ }
243
+ /**
244
+ * One taxonomy archive: the published, untrashed documents whose term list
245
+ * holds `term`, newest first. Tags and categories are the same query over two
246
+ * tables, so neither can drift from the other.
247
+ */
248
+ function selectByTerm(table, column, term, options) {
249
+ const where = [
250
+ 'draft = 0',
251
+ 'trashed = 0',
252
+ DUE_CLAUSE,
253
+ `path IN (SELECT path FROM ${table} WHERE ${column} = ?)`,
254
+ ];
255
+ const params = [nowKey(), term];
256
+ if (options.type !== undefined) {
257
+ where.unshift('type = ?');
258
+ params.unshift(options.type);
259
+ }
260
+ return select(where, params, options);
261
+ }
262
+ /** {@link selectByTerm}'s total, for the pager and the archive's existence. */
263
+ function countByTerm(table, column, term, options) {
264
+ const where = [
265
+ 'documents.draft = 0',
266
+ 'documents.trashed = 0',
267
+ '(documents.date_sort IS NULL OR documents.date_sort <= ?)',
268
+ `${table}.${column} = ?`,
269
+ ];
270
+ const params = [nowKey(), term];
271
+ if (options.type !== undefined) {
272
+ where.push('documents.type = ?');
273
+ params.push(options.type);
274
+ }
275
+ const row = db
276
+ .prepare(`SELECT COUNT(*) AS count FROM ${table}
277
+ JOIN documents ON documents.path = ${table}.path
278
+ WHERE ${where.join(' AND ')}`)
279
+ .get(...params);
280
+ return Number(row['count']);
281
+ }
282
+ /**
283
+ * The nearest published post on one side of a point in the archive.
284
+ *
285
+ * `direction` is which way to look — `<` for older, `>` for newer — and
286
+ * `order` is the sort that puts the nearest one first, so one query serves
287
+ * both ends of {@link ContentStore.neighbours}.
288
+ */
289
+ function neighbour(direction, order, key, contentPath) {
290
+ const row = db
291
+ .prepare(`SELECT * FROM documents
292
+ WHERE type = 'post' AND draft = 0 AND trashed = 0 AND ${DUE_CLAUSE}
293
+ AND date_sort IS NOT NULL
294
+ AND (date_sort, path) ${direction} (?, ?)
295
+ ORDER BY date_sort ${order}, path ${order}
296
+ LIMIT 1`)
297
+ .get(...[nowKey(), key, contentPath]);
298
+ return hydrate(row);
140
299
  }
141
300
  function select(where, params, options) {
142
301
  const clause = where.length === 0 ? '' : `WHERE ${where.join(' AND ')}`;
@@ -147,6 +306,9 @@ export function openContentStore(options) {
147
306
  }
148
307
  return {
149
308
  file,
309
+ now() {
310
+ return clock();
311
+ },
150
312
  upsert(document) {
151
313
  writeOne(document);
152
314
  },
@@ -163,32 +325,88 @@ export function openContentStore(options) {
163
325
  }
164
326
  },
165
327
  remove(contentPath) {
328
+ // Nothing cascades into a virtual table, so the words go by hand.
329
+ statements.deleteText.run(contentPath);
166
330
  return statements.remove.run(contentPath).changes > 0;
167
331
  },
332
+ clear() {
333
+ db.exec('BEGIN');
334
+ try {
335
+ // All four by name. The two term tables would go with the documents
336
+ // through their foreign keys, but a virtual table has none to cascade
337
+ // through — and a clear that depended on a pragma being on for half of
338
+ // what it empties would be a clear that half worked.
339
+ db.exec('DELETE FROM documents_fts');
340
+ db.exec('DELETE FROM document_tags');
341
+ db.exec('DELETE FROM document_categories');
342
+ db.exec('DELETE FROM documents');
343
+ db.exec('COMMIT');
344
+ }
345
+ catch (error) {
346
+ db.exec('ROLLBACK');
347
+ throw error;
348
+ }
349
+ },
168
350
  getByPath(contentPath) {
169
351
  return hydrate(statements.byPath.get(contentPath));
170
352
  },
171
353
  getByPermalink(permalink) {
172
354
  return hydrate(statements.byPermalink.get(permalink));
173
355
  },
356
+ getByStoredObjectId(objectId) {
357
+ if (objectId === '')
358
+ return undefined;
359
+ return hydrate(statements.byStoredObjectId.get(objectId));
360
+ },
174
361
  getBySlug(slug) {
175
362
  return hydrate(statements.bySlug.get(slug));
176
363
  },
177
364
  listPosts(options = {}) {
178
- return select(["type = 'post'", 'draft = 0', 'trashed = 0'], [], options);
365
+ return select(["type = 'post'", 'draft = 0', 'trashed = 0', DUE_CLAUSE], [nowKey()], options);
366
+ },
367
+ listPostsSince(instant) {
368
+ const since = dateSortKey(instant);
369
+ if (since === null)
370
+ return [];
371
+ return select(["type = 'post'", 'draft = 0', 'trashed = 0', DUE_CLAUSE, 'date_sort >= ?'], [nowKey(), since], {});
372
+ },
373
+ neighbours(document) {
374
+ // A page is nobody's neighbour, and neither is a document the archive
375
+ // does not order: without a date there is no place in the sequence to be
376
+ // either side of.
377
+ const key = dateSortKey(document.date);
378
+ if (document.type !== 'post' || key === null)
379
+ return {};
380
+ // The listings are ordered by `date_sort DESC, path DESC`, so "before"
381
+ // and "after" are that same pair compared as a row: two posts sharing an
382
+ // instant are separated by their paths exactly as the listing separates
383
+ // them, and the document itself is excluded by the comparison being
384
+ // strict.
385
+ const previous = neighbour('<', 'DESC', key, document.path);
386
+ const next = neighbour('>', 'ASC', key, document.path);
387
+ return {
388
+ ...(previous === undefined ? {} : { previous }),
389
+ ...(next === undefined ? {} : { next }),
390
+ };
391
+ },
392
+ nextDue() {
393
+ const row = statements.nextDue.get(nowKey());
394
+ return text(row['due']);
395
+ },
396
+ listDueSince(after) {
397
+ const rows = statements.dueSince.all(after, nowKey());
398
+ return hydrateAll(rows);
179
399
  },
180
400
  listByTag(tag, options = {}) {
181
- const where = [
182
- 'draft = 0',
183
- 'trashed = 0',
184
- 'path IN (SELECT path FROM document_tags WHERE tag = ?)',
185
- ];
186
- const params = [tag];
187
- if (options.type !== undefined) {
188
- where.unshift('type = ?');
189
- params.unshift(options.type);
190
- }
191
- return select(where, params, options);
401
+ return selectByTerm('document_tags', 'tag', tag, options);
402
+ },
403
+ listByCategory(category, options = {}) {
404
+ return selectByTerm('document_categories', 'category', category, options);
405
+ },
406
+ listByAuthor(names, options = {}) {
407
+ if (names.length === 0)
408
+ return [];
409
+ return select(["type = 'post'", 'draft = 0', 'trashed = 0', DUE_CLAUSE, authorClause(names)], [nowKey(), ...names], options);
192
410
  },
193
411
  listAll(options = {}) {
194
412
  const where = [];
@@ -203,45 +421,88 @@ export function openContentStore(options) {
203
421
  }
204
422
  where.push('trashed = ?');
205
423
  params.push(options.trashed === true ? 1 : 0);
424
+ if (options.scheduled !== undefined) {
425
+ where.push(options.scheduled ? SCHEDULED_CLAUSE : DUE_CLAUSE);
426
+ params.push(nowKey());
427
+ }
206
428
  if (options.tag !== undefined) {
207
429
  where.push('path IN (SELECT path FROM document_tags WHERE tag = ?)');
208
430
  params.push(options.tag);
209
431
  }
432
+ if (options.category !== undefined) {
433
+ where.push('path IN (SELECT path FROM document_categories WHERE category = ?)');
434
+ params.push(options.category);
435
+ }
210
436
  return select(where, params, options);
211
437
  },
438
+ listFederated(options = {}) {
439
+ return select([`type = 'post'`, FEDERATED_CLAUSE], [], options);
440
+ },
212
441
  listPaths() {
213
442
  return statements.paths.all().map((row) => String(row['path']));
214
443
  },
215
444
  counts() {
216
- const row = statements.counts.get();
445
+ const now = nowKey();
446
+ const row = statements.counts.get(now, now, now);
217
447
  return {
218
448
  total: Number(row['total']),
219
449
  posts: Number(row['posts']),
220
450
  pages: Number(row['pages']),
221
451
  drafts: Number(row['drafts']),
452
+ scheduled: Number(row['scheduled']),
222
453
  trashed: Number(row['trashed']),
223
454
  };
224
455
  },
225
456
  countByTag(tag, options = {}) {
226
- const where = ['documents.draft = 0', 'documents.trashed = 0', 'document_tags.tag = ?'];
227
- const params = [tag];
228
- if (options.type !== undefined) {
229
- where.push('documents.type = ?');
230
- params.push(options.type);
231
- }
457
+ return countByTerm('document_tags', 'tag', tag, options);
458
+ },
459
+ countByCategory(category, options = {}) {
460
+ return countByTerm('document_categories', 'category', category, options);
461
+ },
462
+ countByAuthor(names) {
463
+ if (names.length === 0)
464
+ return 0;
232
465
  const row = db
233
- .prepare(`SELECT COUNT(*) AS count FROM document_tags
234
- JOIN documents ON documents.path = document_tags.path
235
- WHERE ${where.join(' AND ')}`)
236
- .get(...params);
466
+ .prepare(`SELECT COUNT(*) AS count FROM documents
467
+ WHERE type = 'post' AND draft = 0 AND trashed = 0
468
+ AND ${DUE_CLAUSE} AND ${authorClause(names)}`)
469
+ .get(...[nowKey(), ...names]);
237
470
  return Number(row['count']);
238
471
  },
239
472
  listTags() {
240
- return statements.tagCounts.all().map((row) => ({
473
+ return statements.tagCounts.all(nowKey()).map((row) => ({
241
474
  tag: String(row['tag']),
242
475
  count: Number(row['count']),
243
476
  }));
244
477
  },
478
+ listCategories() {
479
+ return statements.categoryCounts.all(nowKey()).map((row) => ({
480
+ category: String(row['category']),
481
+ count: Number(row['count']),
482
+ }));
483
+ },
484
+ listTermUsage(taxonomy) {
485
+ const query = taxonomy === 'tag' ? statements.tagUsage : statements.categoryUsage;
486
+ return query.all(nowKey()).map((row) => ({
487
+ term: String(row['term']),
488
+ published: Number(row['published']),
489
+ total: Number(row['total']),
490
+ }));
491
+ },
492
+ search(query, options = {}) {
493
+ const expression = searchExpression(query);
494
+ if (expression === undefined)
495
+ return [];
496
+ const rows = statements.search.all(expression, nowKey(), options.limit ?? -1, options.offset ?? 0);
497
+ return rows.map((row) => ({ document: hydrateOne(row), snippet: String(row['snippet']) }));
498
+ },
499
+ countSearch(query) {
500
+ const expression = searchExpression(query);
501
+ if (expression === undefined)
502
+ return 0;
503
+ const row = statements.countSearch.get(expression, nowKey());
504
+ return Number(row['count']);
505
+ },
245
506
  close() {
246
507
  if (!open)
247
508
  return;
@@ -250,6 +511,38 @@ export function openContentStore(options) {
250
511
  },
251
512
  };
252
513
  }
514
+ /**
515
+ * The query behind {@link ContentStore.listTermUsage} for one of the two join
516
+ * tables: every term, the public count and the total, in one pass.
517
+ *
518
+ * The table and column names are the module's own literals rather than
519
+ * anything a caller supplies, so there is nothing here to interpolate from
520
+ * outside; the clock is the one bound parameter.
521
+ */
522
+ function termUsageSql(table, column) {
523
+ return `
524
+ SELECT ${table}.${column} AS term,
525
+ COALESCE(SUM(
526
+ documents.draft = 0 AND documents.trashed = 0
527
+ AND (documents.date_sort IS NULL OR documents.date_sort <= ?)
528
+ ), 0) AS published,
529
+ COUNT(*) AS total
530
+ FROM ${table}
531
+ JOIN documents ON documents.path = ${table}.path
532
+ GROUP BY ${table}.${column}
533
+ ORDER BY term ASC
534
+ `;
535
+ }
536
+ /**
537
+ * `author IN (?, ?, …)`, with one placeholder per name.
538
+ *
539
+ * Only the count of the names is interpolated; every name itself is a bound
540
+ * parameter, so a display name holding a quote is a string to match rather
541
+ * than anything SQLite reads as syntax.
542
+ */
543
+ function authorClause(names) {
544
+ return `author IN (${names.map(() => '?').join(', ')})`;
545
+ }
253
546
  function limitClause(options) {
254
547
  if (options.limit === undefined && options.offset === undefined)
255
548
  return '';
@@ -288,7 +581,7 @@ function toRow(document) {
288
581
  * Optional fields are left off entirely when the column is NULL, so a document
289
582
  * that went through the index deep-equals the one that was parsed from the file.
290
583
  */
291
- function toDocument(row, tags) {
584
+ function toDocument(row, tags, categories) {
292
585
  return {
293
586
  type: String(row['type']),
294
587
  path: String(row['path']),
@@ -298,6 +591,7 @@ function toDocument(row, tags) {
298
591
  ...optional('date', text(row['date'])),
299
592
  ...optional('updated', text(row['updated'])),
300
593
  tags,
594
+ categories,
301
595
  draft: row['draft'] === 1,
302
596
  ...optional('description', text(row['description'])),
303
597
  ...optional('author', text(row['author'])),
@@ -337,6 +631,8 @@ function isUniqueViolation(error, column) {
337
631
  return false;
338
632
  return error.message.includes('UNIQUE constraint failed') && error.message.includes(column);
339
633
  }
634
+ /** The table this store's applied versions are recorded in. */
635
+ const LEDGER = 'migrations';
340
636
  /**
341
637
  * Schema versions, applied in order. Never edit a migration that has shipped;
342
638
  * append a new one, so a site that upgrades lands on the same schema as a site
@@ -380,32 +676,61 @@ const MIGRATIONS = [
380
676
  CREATE INDEX document_tags_tag ON document_tags (tag);
381
677
  `,
382
678
  },
679
+ {
680
+ version: 2,
681
+ sql: `
682
+ CREATE TABLE document_categories (
683
+ path TEXT NOT NULL REFERENCES documents (path) ON DELETE CASCADE,
684
+ category TEXT NOT NULL,
685
+ position INTEGER NOT NULL,
686
+ PRIMARY KEY (path, category)
687
+ );
688
+
689
+ CREATE INDEX document_categories_category ON document_categories (category);
690
+
691
+ -- A file that already carried categories hashes the same as it did
692
+ -- before the key was modelled, so a sync would find every row up to date
693
+ -- and never learn what those files are filed under. Emptying the index
694
+ -- is what makes the next scan read them all again; the files are the
695
+ -- source of truth, so nothing is lost (decision-1).
696
+ DELETE FROM documents;
697
+ `,
698
+ },
699
+ {
700
+ version: 3,
701
+ sql: `
702
+ -- The renderer now puts \`tabindex="0"\` on every \`<pre>\` (TASK-86), so a
703
+ -- wide block of code can be scrolled from the keyboard. The HTML in this
704
+ -- table was rendered by the old one, and the hash covers the file rather
705
+ -- than what was made of it, so a scan would find every row up to date and
706
+ -- serve the old markup for as long as nobody edited the post. Emptying
707
+ -- the index is what makes the next scan render them all again; the files
708
+ -- are the source of truth, so nothing is lost (decision-1).
709
+ DELETE FROM documents;
710
+ `,
711
+ },
712
+ {
713
+ version: 4,
714
+ sql: `
715
+ -- The full-text index (TASK-22): one row per document, keyed by path,
716
+ -- holding the words it is found by. \`porter\` so "running" finds "run",
717
+ -- \`remove_diacritics\` so "cafe" finds "café".
718
+ CREATE VIRTUAL TABLE documents_fts USING fts5 (
719
+ path UNINDEXED,
720
+ title,
721
+ description,
722
+ terms,
723
+ body,
724
+ tokenize = 'porter unicode61 remove_diacritics 2'
725
+ );
726
+
727
+ -- The body is indexed as the text of its rendered HTML, which SQL cannot
728
+ -- work out from the row, and the hash covers the file rather than the
729
+ -- index, so a scan would find every row up to date and never fill the
730
+ -- table. Emptying the index is what makes the next scan index them all;
731
+ -- the files are the source of truth, so nothing is lost (decision-1).
732
+ DELETE FROM documents;
733
+ `,
734
+ },
383
735
  ];
384
- function migrate(db) {
385
- db.exec(`
386
- CREATE TABLE IF NOT EXISTS migrations (
387
- version INTEGER PRIMARY KEY,
388
- applied_at TEXT NOT NULL
389
- )
390
- `);
391
- const applied = new Set(db
392
- .prepare('SELECT version FROM migrations')
393
- .all()
394
- .map((row) => Number(row['version'])));
395
- const record = db.prepare('INSERT INTO migrations (version, applied_at) VALUES (?, ?)');
396
- for (const migration of MIGRATIONS) {
397
- if (applied.has(migration.version))
398
- continue;
399
- db.exec('BEGIN');
400
- try {
401
- db.exec(migration.sql);
402
- record.run(migration.version, new Date().toISOString());
403
- db.exec('COMMIT');
404
- }
405
- catch (error) {
406
- db.exec('ROLLBACK');
407
- throw error;
408
- }
409
- }
410
- }
411
736
  //# sourceMappingURL=store.js.map