@geekity/cms 0.31.0 → 0.32.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 (745) hide show
  1. package/admin/components/badge.njk +4 -0
  2. package/admin/components/fields.njk +41 -24
  3. package/admin/components/job-status.njk +45 -0
  4. package/admin/components/setting-fields.njk +38 -0
  5. package/admin/layouts/settings-page.njk +16 -0
  6. package/admin/pages/comments/all.njk +1 -2
  7. package/admin/pages/documents/editor.njk +17 -15
  8. package/admin/pages/documents/syndication.njk +4 -4
  9. package/admin/pages/federation/followers.njk +56 -6
  10. package/admin/pages/federation/settings.njk +1 -1
  11. package/admin/pages/media/library.njk +52 -3
  12. package/admin/pages/navigation/menus.njk +2 -2
  13. package/admin/pages/plugins/screen.njk +5 -46
  14. package/admin/pages/settings/discussion.njk +76 -47
  15. package/admin/pages/settings/email.njk +75 -47
  16. package/admin/pages/settings/general.njk +15 -15
  17. package/admin/pages/settings/permalinks.njk +8 -8
  18. package/admin/pages/settings/privacy.njk +6 -6
  19. package/admin/pages/settings/reading.njk +36 -36
  20. package/admin/pages/tools/dev-mode.njk +29 -3
  21. package/admin/pages/tools/export.njk +89 -0
  22. package/admin/pages/tools/maintenance.njk +51 -0
  23. package/admin/pages/users/apps.njk +2 -2
  24. package/admin/pages/users/edit.njk +3 -3
  25. package/admin/pages/users/new.njk +2 -2
  26. package/admin/static/admin.css +80 -0
  27. package/dist/admin/app-activity.d.ts.map +1 -1
  28. package/dist/admin/app-activity.js +2 -3
  29. package/dist/admin/app-activity.js.map +1 -1
  30. package/dist/admin/appearance.d.ts +1 -5
  31. package/dist/admin/appearance.d.ts.map +1 -1
  32. package/dist/admin/appearance.js +9 -16
  33. package/dist/admin/appearance.js.map +1 -1
  34. package/dist/admin/comments.d.ts.map +1 -1
  35. package/dist/admin/comments.js +2 -3
  36. package/dist/admin/comments.js.map +1 -1
  37. package/dist/admin/confirm-password.d.ts +4 -0
  38. package/dist/admin/confirm-password.d.ts.map +1 -0
  39. package/dist/admin/confirm-password.js +11 -0
  40. package/dist/admin/confirm-password.js.map +1 -0
  41. package/dist/admin/connected-apps.d.ts.map +1 -1
  42. package/dist/admin/connected-apps.js +17 -22
  43. package/dist/admin/connected-apps.js.map +1 -1
  44. package/dist/admin/dev-mode.d.ts.map +1 -1
  45. package/dist/admin/dev-mode.js +35 -10
  46. package/dist/admin/dev-mode.js.map +1 -1
  47. package/dist/admin/documents.d.ts.map +1 -1
  48. package/dist/admin/documents.js +22 -10
  49. package/dist/admin/documents.js.map +1 -1
  50. package/dist/admin/editor-actions.d.ts.map +1 -1
  51. package/dist/admin/editor-actions.js +1 -2
  52. package/dist/admin/editor-actions.js.map +1 -1
  53. package/dist/admin/editor-layout.js +1 -1
  54. package/dist/admin/editor-layout.js.map +1 -1
  55. package/dist/admin/export.d.ts +10 -0
  56. package/dist/admin/export.d.ts.map +1 -0
  57. package/dist/admin/export.js +72 -0
  58. package/dist/admin/export.js.map +1 -0
  59. package/dist/admin/federation.d.ts +1 -12
  60. package/dist/admin/federation.d.ts.map +1 -1
  61. package/dist/admin/federation.js +34 -87
  62. package/dist/admin/federation.js.map +1 -1
  63. package/dist/admin/index.d.ts +2 -5
  64. package/dist/admin/index.d.ts.map +1 -1
  65. package/dist/admin/index.js +2 -4
  66. package/dist/admin/index.js.map +1 -1
  67. package/dist/admin/indieauth-consent.d.ts.map +1 -1
  68. package/dist/admin/indieauth-consent.js +5 -7
  69. package/dist/admin/indieauth-consent.js.map +1 -1
  70. package/dist/admin/job-status.d.ts +25 -0
  71. package/dist/admin/job-status.d.ts.map +1 -0
  72. package/dist/admin/job-status.js +53 -0
  73. package/dist/admin/job-status.js.map +1 -0
  74. package/dist/admin/legacy-migrations.d.ts.map +1 -1
  75. package/dist/admin/legacy-migrations.js +7 -6
  76. package/dist/admin/legacy-migrations.js.map +1 -1
  77. package/dist/admin/maintenance.d.ts +9 -0
  78. package/dist/admin/maintenance.d.ts.map +1 -0
  79. package/dist/admin/maintenance.js +42 -0
  80. package/dist/admin/maintenance.js.map +1 -0
  81. package/dist/admin/media.d.ts +4 -1
  82. package/dist/admin/media.d.ts.map +1 -1
  83. package/dist/admin/media.js +16 -1
  84. package/dist/admin/media.js.map +1 -1
  85. package/dist/admin/menu.d.ts.map +1 -1
  86. package/dist/admin/menu.js +4 -0
  87. package/dist/admin/menu.js.map +1 -1
  88. package/dist/admin/messages.d.ts.map +1 -1
  89. package/dist/admin/messages.js +2 -3
  90. package/dist/admin/messages.js.map +1 -1
  91. package/dist/admin/navigation.d.ts.map +1 -1
  92. package/dist/admin/navigation.js +27 -23
  93. package/dist/admin/navigation.js.map +1 -1
  94. package/dist/admin/plugins.d.ts.map +1 -1
  95. package/dist/admin/plugins.js +19 -66
  96. package/dist/admin/plugins.js.map +1 -1
  97. package/dist/admin/preview.d.ts.map +1 -1
  98. package/dist/admin/preview.js +5 -6
  99. package/dist/admin/preview.js.map +1 -1
  100. package/dist/admin/recovery.js +2 -2
  101. package/dist/admin/recovery.js.map +1 -1
  102. package/dist/admin/routes.d.ts.map +1 -1
  103. package/dist/admin/routes.js +6 -3
  104. package/dist/admin/routes.js.map +1 -1
  105. package/dist/admin/setting-overrides.d.ts +4 -0
  106. package/dist/admin/setting-overrides.d.ts.map +1 -0
  107. package/dist/admin/setting-overrides.js +24 -0
  108. package/dist/admin/setting-overrides.js.map +1 -0
  109. package/dist/admin/settings-discussion.d.ts +6 -4
  110. package/dist/admin/settings-discussion.d.ts.map +1 -1
  111. package/dist/admin/settings-discussion.js +75 -45
  112. package/dist/admin/settings-discussion.js.map +1 -1
  113. package/dist/admin/settings-email.d.ts +11 -8
  114. package/dist/admin/settings-email.d.ts.map +1 -1
  115. package/dist/admin/settings-email.js +88 -77
  116. package/dist/admin/settings-email.js.map +1 -1
  117. package/dist/admin/settings-federation.d.ts +0 -7
  118. package/dist/admin/settings-federation.d.ts.map +1 -1
  119. package/dist/admin/settings-federation.js +0 -36
  120. package/dist/admin/settings-federation.js.map +1 -1
  121. package/dist/admin/settings-fields.d.ts +32 -0
  122. package/dist/admin/settings-fields.d.ts.map +1 -0
  123. package/dist/admin/settings-fields.js +368 -0
  124. package/dist/admin/settings-fields.js.map +1 -0
  125. package/dist/admin/settings-general.d.ts.map +1 -1
  126. package/dist/admin/settings-general.js +10 -19
  127. package/dist/admin/settings-general.js.map +1 -1
  128. package/dist/admin/settings-page.d.ts +3 -29
  129. package/dist/admin/settings-page.d.ts.map +1 -1
  130. package/dist/admin/settings-page.js +38 -53
  131. package/dist/admin/settings-page.js.map +1 -1
  132. package/dist/admin/settings-permalinks.js +2 -2
  133. package/dist/admin/settings-permalinks.js.map +1 -1
  134. package/dist/admin/settings-privacy.js +1 -1
  135. package/dist/admin/settings-privacy.js.map +1 -1
  136. package/dist/admin/settings-reading.js +13 -13
  137. package/dist/admin/settings-reading.js.map +1 -1
  138. package/dist/admin/settings-renderer.d.ts +32 -0
  139. package/dist/admin/settings-renderer.d.ts.map +1 -0
  140. package/dist/admin/settings-renderer.js +79 -0
  141. package/dist/admin/settings-renderer.js.map +1 -0
  142. package/dist/admin/site-setting-edit.d.ts +4 -0
  143. package/dist/admin/site-setting-edit.d.ts.map +1 -0
  144. package/dist/admin/site-setting-edit.js +20 -0
  145. package/dist/admin/site-setting-edit.js.map +1 -0
  146. package/dist/admin/taxonomy.d.ts.map +1 -1
  147. package/dist/admin/taxonomy.js +10 -19
  148. package/dist/admin/taxonomy.js.map +1 -1
  149. package/dist/admin/templates.d.ts +2 -0
  150. package/dist/admin/templates.d.ts.map +1 -1
  151. package/dist/admin/templates.js +2 -0
  152. package/dist/admin/templates.js.map +1 -1
  153. package/dist/admin/users.js +1 -1
  154. package/dist/admin/users.js.map +1 -1
  155. package/dist/avatars/avatars.d.ts +0 -2
  156. package/dist/avatars/avatars.d.ts.map +1 -1
  157. package/dist/avatars/avatars.js +9 -10
  158. package/dist/avatars/avatars.js.map +1 -1
  159. package/dist/avatars/index.d.ts +1 -1
  160. package/dist/avatars/index.d.ts.map +1 -1
  161. package/dist/avatars/index.js +1 -1
  162. package/dist/avatars/index.js.map +1 -1
  163. package/dist/cache.d.ts +0 -2
  164. package/dist/cache.d.ts.map +1 -1
  165. package/dist/cache.js +2 -4
  166. package/dist/cache.js.map +1 -1
  167. package/dist/cli.d.ts +1 -1
  168. package/dist/cli.d.ts.map +1 -1
  169. package/dist/cli.js +500 -86
  170. package/dist/cli.js.map +1 -1
  171. package/dist/cms/app.d.ts +1 -1
  172. package/dist/cms/app.d.ts.map +1 -1
  173. package/dist/cms/app.js +13 -2
  174. package/dist/cms/app.js.map +1 -1
  175. package/dist/cms/boot.d.ts +12 -0
  176. package/dist/cms/boot.d.ts.map +1 -1
  177. package/dist/cms/boot.js +37 -16
  178. package/dist/cms/boot.js.map +1 -1
  179. package/dist/cms/create.d.ts +24 -0
  180. package/dist/cms/create.d.ts.map +1 -1
  181. package/dist/cms/create.js +14 -0
  182. package/dist/cms/create.js.map +1 -1
  183. package/dist/cms/services.d.ts +3 -1
  184. package/dist/cms/services.d.ts.map +1 -1
  185. package/dist/cms/services.js +83 -26
  186. package/dist/cms/services.js.map +1 -1
  187. package/dist/cms/site-records.d.ts.map +1 -1
  188. package/dist/cms/site-records.js +0 -2
  189. package/dist/cms/site-records.js.map +1 -1
  190. package/dist/comments/akismet.d.ts +13 -46
  191. package/dist/comments/akismet.d.ts.map +1 -1
  192. package/dist/comments/akismet.js +29 -68
  193. package/dist/comments/akismet.js.map +1 -1
  194. package/dist/comments/conversation.d.ts +14 -8
  195. package/dist/comments/conversation.d.ts.map +1 -1
  196. package/dist/comments/conversation.js.map +1 -1
  197. package/dist/comments/index.d.ts +4 -4
  198. package/dist/comments/index.d.ts.map +1 -1
  199. package/dist/comments/index.js +3 -3
  200. package/dist/comments/index.js.map +1 -1
  201. package/dist/comments/policy.d.ts +23 -26
  202. package/dist/comments/policy.d.ts.map +1 -1
  203. package/dist/comments/policy.js +13 -20
  204. package/dist/comments/policy.js.map +1 -1
  205. package/dist/comments/records.d.ts +0 -2
  206. package/dist/comments/records.d.ts.map +1 -1
  207. package/dist/comments/records.js +2 -3
  208. package/dist/comments/records.js.map +1 -1
  209. package/dist/comments/reply-notices.d.ts +5 -6
  210. package/dist/comments/reply-notices.d.ts.map +1 -1
  211. package/dist/comments/reply-notices.js +27 -15
  212. package/dist/comments/reply-notices.js.map +1 -1
  213. package/dist/comments/reply-post.d.ts.map +1 -1
  214. package/dist/comments/reply-post.js +2 -3
  215. package/dist/comments/reply-post.js.map +1 -1
  216. package/dist/comments/submission.d.ts +0 -2
  217. package/dist/comments/submission.d.ts.map +1 -1
  218. package/dist/comments/submission.js +1 -3
  219. package/dist/comments/submission.js.map +1 -1
  220. package/dist/config-loader.d.ts.map +1 -1
  221. package/dist/config-loader.js +2 -1
  222. package/dist/config-loader.js.map +1 -1
  223. package/dist/config.d.ts +27 -1
  224. package/dist/config.d.ts.map +1 -1
  225. package/dist/config.js +5 -1
  226. package/dist/config.js.map +1 -1
  227. package/dist/contact/delivery.d.ts +2 -4
  228. package/dist/contact/delivery.d.ts.map +1 -1
  229. package/dist/contact/delivery.js +1 -2
  230. package/dist/contact/delivery.js.map +1 -1
  231. package/dist/contact/index.d.ts +1 -1
  232. package/dist/contact/index.d.ts.map +1 -1
  233. package/dist/contact/index.js +1 -1
  234. package/dist/contact/index.js.map +1 -1
  235. package/dist/contact/records.d.ts +0 -2
  236. package/dist/contact/records.d.ts.map +1 -1
  237. package/dist/contact/records.js +3 -5
  238. package/dist/contact/records.js.map +1 -1
  239. package/dist/content/citation.d.ts +1 -0
  240. package/dist/content/citation.d.ts.map +1 -1
  241. package/dist/content/citation.js.map +1 -1
  242. package/dist/content/document-form.d.ts +3 -1
  243. package/dist/content/document-form.d.ts.map +1 -1
  244. package/dist/content/document-form.js.map +1 -1
  245. package/dist/content/enclosure-form.d.ts.map +1 -1
  246. package/dist/content/enclosure-form.js +5 -6
  247. package/dist/content/enclosure-form.js.map +1 -1
  248. package/dist/content/excerpt.d.ts +1 -0
  249. package/dist/content/excerpt.d.ts.map +1 -1
  250. package/dist/content/excerpt.js +3 -0
  251. package/dist/content/excerpt.js.map +1 -1
  252. package/dist/content/index.d.ts +3 -3
  253. package/dist/content/index.d.ts.map +1 -1
  254. package/dist/content/index.js +2 -2
  255. package/dist/content/index.js.map +1 -1
  256. package/dist/content/kept-properties.d.ts +0 -1
  257. package/dist/content/kept-properties.d.ts.map +1 -1
  258. package/dist/content/kept-properties.js +2 -2
  259. package/dist/content/kept-properties.js.map +1 -1
  260. package/dist/content/locations.d.ts +0 -1
  261. package/dist/content/locations.d.ts.map +1 -1
  262. package/dist/content/locations.js +2 -2
  263. package/dist/content/locations.js.map +1 -1
  264. package/dist/content/media.d.ts +7 -0
  265. package/dist/content/media.d.ts.map +1 -1
  266. package/dist/content/media.js +12 -1
  267. package/dist/content/media.js.map +1 -1
  268. package/dist/content/metadata/strip-job.d.ts +16 -0
  269. package/dist/content/metadata/strip-job.d.ts.map +1 -0
  270. package/dist/content/metadata/strip-job.js +24 -0
  271. package/dist/content/metadata/strip-job.js.map +1 -0
  272. package/dist/content/metadata/sweep.d.ts +13 -1
  273. package/dist/content/metadata/sweep.d.ts.map +1 -1
  274. package/dist/content/metadata/sweep.js +47 -25
  275. package/dist/content/metadata/sweep.js.map +1 -1
  276. package/dist/content/permalink-file.d.ts +2 -1
  277. package/dist/content/permalink-file.d.ts.map +1 -1
  278. package/dist/content/permalink-file.js +3 -3
  279. package/dist/content/permalink-file.js.map +1 -1
  280. package/dist/content/photo-form.d.ts +1 -0
  281. package/dist/content/photo-form.d.ts.map +1 -1
  282. package/dist/content/photo-form.js +8 -5
  283. package/dist/content/photo-form.js.map +1 -1
  284. package/dist/content/photo.d.ts +1 -0
  285. package/dist/content/photo.d.ts.map +1 -1
  286. package/dist/content/photo.js +1 -1
  287. package/dist/content/photo.js.map +1 -1
  288. package/dist/content/post-fields.d.ts.map +1 -1
  289. package/dist/content/post-fields.js +5 -9
  290. package/dist/content/post-fields.js.map +1 -1
  291. package/dist/content/read-form.d.ts +10 -3
  292. package/dist/content/read-form.d.ts.map +1 -1
  293. package/dist/content/read-form.js +67 -9
  294. package/dist/content/read-form.js.map +1 -1
  295. package/dist/content/read.d.ts +9 -2
  296. package/dist/content/read.d.ts.map +1 -1
  297. package/dist/content/read.js +22 -3
  298. package/dist/content/read.js.map +1 -1
  299. package/dist/content/store.d.ts +0 -1
  300. package/dist/content/store.d.ts.map +1 -1
  301. package/dist/content/store.js +0 -1
  302. package/dist/content/store.js.map +1 -1
  303. package/dist/content/uploads.d.ts +1 -1
  304. package/dist/content/uploads.d.ts.map +1 -1
  305. package/dist/content/uploads.js +6 -1
  306. package/dist/content/uploads.js.map +1 -1
  307. package/dist/content/write.d.ts +1 -1
  308. package/dist/content/write.d.ts.map +1 -1
  309. package/dist/content/write.js +7 -3
  310. package/dist/content/write.js.map +1 -1
  311. package/dist/dev-mode.d.ts +4 -26
  312. package/dist/dev-mode.d.ts.map +1 -1
  313. package/dist/dev-mode.js +4 -53
  314. package/dist/dev-mode.js.map +1 -1
  315. package/dist/export/export-archives.d.ts +29 -0
  316. package/dist/export/export-archives.d.ts.map +1 -0
  317. package/dist/export/export-archives.js +88 -0
  318. package/dist/export/export-archives.js.map +1 -0
  319. package/dist/export/export-job.d.ts +21 -0
  320. package/dist/export/export-job.d.ts.map +1 -0
  321. package/dist/export/export-job.js +63 -0
  322. package/dist/export/export-job.js.map +1 -0
  323. package/dist/export/site-export.d.ts +101 -0
  324. package/dist/export/site-export.d.ts.map +1 -0
  325. package/dist/export/site-export.js +243 -0
  326. package/dist/export/site-export.js.map +1 -0
  327. package/dist/export/site-import.d.ts +75 -0
  328. package/dist/export/site-import.d.ts.map +1 -0
  329. package/dist/export/site-import.js +492 -0
  330. package/dist/export/site-import.js.map +1 -0
  331. package/dist/exports.d.ts +9 -4
  332. package/dist/exports.d.ts.map +1 -1
  333. package/dist/exports.js +3 -1
  334. package/dist/exports.js.map +1 -1
  335. package/dist/federation/article.d.ts.map +1 -1
  336. package/dist/federation/article.js +22 -20
  337. package/dist/federation/article.js.map +1 -1
  338. package/dist/federation/backfill.d.ts +3 -1
  339. package/dist/federation/backfill.d.ts.map +1 -1
  340. package/dist/federation/backfill.js +5 -3
  341. package/dist/federation/backfill.js.map +1 -1
  342. package/dist/federation/cited-post.d.ts +1 -1
  343. package/dist/federation/cited-post.d.ts.map +1 -1
  344. package/dist/federation/cited-post.js +1 -1
  345. package/dist/federation/cited-post.js.map +1 -1
  346. package/dist/federation/delivery.d.ts +5 -2
  347. package/dist/federation/delivery.d.ts.map +1 -1
  348. package/dist/federation/delivery.js +16 -12
  349. package/dist/federation/delivery.js.map +1 -1
  350. package/dist/federation/federation.d.ts +7 -7
  351. package/dist/federation/federation.d.ts.map +1 -1
  352. package/dist/federation/federation.js +5 -12
  353. package/dist/federation/federation.js.map +1 -1
  354. package/dist/federation/handles.d.ts +1 -1
  355. package/dist/federation/handles.d.ts.map +1 -1
  356. package/dist/federation/handles.js +1 -1
  357. package/dist/federation/handles.js.map +1 -1
  358. package/dist/federation/inbox.js +1 -1
  359. package/dist/federation/inbox.js.map +1 -1
  360. package/dist/federation/keys.js +3 -3
  361. package/dist/federation/keys.js.map +1 -1
  362. package/dist/federation/profiles.d.ts +1 -1
  363. package/dist/federation/profiles.d.ts.map +1 -1
  364. package/dist/federation/profiles.js +1 -1
  365. package/dist/federation/profiles.js.map +1 -1
  366. package/dist/federation/queue.d.ts +32 -17
  367. package/dist/federation/queue.d.ts.map +1 -1
  368. package/dist/federation/queue.js +173 -39
  369. package/dist/federation/queue.js.map +1 -1
  370. package/dist/federation/relays.d.ts +6 -2
  371. package/dist/federation/relays.d.ts.map +1 -1
  372. package/dist/federation/relays.js +43 -36
  373. package/dist/federation/relays.js.map +1 -1
  374. package/dist/federation/resend-job.d.ts +44 -0
  375. package/dist/federation/resend-job.d.ts.map +1 -0
  376. package/dist/federation/resend-job.js +59 -0
  377. package/dist/federation/resend-job.js.map +1 -0
  378. package/dist/files/atomic.d.ts +9 -0
  379. package/dist/files/atomic.d.ts.map +1 -1
  380. package/dist/files/atomic.js +1 -1
  381. package/dist/files/atomic.js.map +1 -1
  382. package/dist/files/data-files.d.ts +104 -0
  383. package/dist/files/data-files.d.ts.map +1 -0
  384. package/dist/files/data-files.js +330 -0
  385. package/dist/files/data-files.js.map +1 -0
  386. package/dist/files/export-archives.d.ts +12 -0
  387. package/dist/files/export-archives.d.ts.map +1 -0
  388. package/dist/files/export-archives.js +67 -0
  389. package/dist/files/export-archives.js.map +1 -0
  390. package/dist/files/static-files.d.ts.map +1 -1
  391. package/dist/files/static-files.js +7 -22
  392. package/dist/files/static-files.js.map +1 -1
  393. package/dist/files/tar.d.ts +29 -0
  394. package/dist/files/tar.d.ts.map +1 -0
  395. package/dist/files/tar.js +206 -0
  396. package/dist/files/tar.js.map +1 -0
  397. package/dist/files/upload-types.d.ts +81 -0
  398. package/dist/files/upload-types.d.ts.map +1 -0
  399. package/dist/files/upload-types.js +234 -0
  400. package/dist/files/upload-types.js.map +1 -0
  401. package/dist/forms/protection.d.ts +0 -8
  402. package/dist/forms/protection.d.ts.map +1 -1
  403. package/dist/forms/protection.js +2 -10
  404. package/dist/forms/protection.js.map +1 -1
  405. package/dist/images/icons.d.ts.map +1 -1
  406. package/dist/images/icons.js +2 -0
  407. package/dist/images/icons.js.map +1 -1
  408. package/dist/images/index.d.ts +1 -1
  409. package/dist/images/index.d.ts.map +1 -1
  410. package/dist/images/index.js +1 -1
  411. package/dist/images/index.js.map +1 -1
  412. package/dist/images/markup.d.ts +4 -1
  413. package/dist/images/markup.d.ts.map +1 -1
  414. package/dist/images/markup.js +10 -6
  415. package/dist/images/markup.js.map +1 -1
  416. package/dist/images/paths.d.ts +1 -4
  417. package/dist/images/paths.d.ts.map +1 -1
  418. package/dist/images/paths.js +6 -12
  419. package/dist/images/paths.js.map +1 -1
  420. package/dist/images/variants.d.ts +4 -2
  421. package/dist/images/variants.d.ts.map +1 -1
  422. package/dist/images/variants.js +21 -21
  423. package/dist/images/variants.js.map +1 -1
  424. package/dist/index.d.ts +1 -0
  425. package/dist/index.d.ts.map +1 -1
  426. package/dist/index.js +1 -0
  427. package/dist/index.js.map +1 -1
  428. package/dist/indexnow.d.ts +4 -2
  429. package/dist/indexnow.d.ts.map +1 -1
  430. package/dist/indexnow.js +32 -20
  431. package/dist/indexnow.js.map +1 -1
  432. package/dist/indieauth/tokens.d.ts +0 -2
  433. package/dist/indieauth/tokens.d.ts.map +1 -1
  434. package/dist/indieauth/tokens.js +2 -4
  435. package/dist/indieauth/tokens.js.map +1 -1
  436. package/dist/jobs/file-records.d.ts +13 -0
  437. package/dist/jobs/file-records.d.ts.map +1 -0
  438. package/dist/jobs/file-records.js +104 -0
  439. package/dist/jobs/file-records.js.map +1 -0
  440. package/dist/jobs/in-process.d.ts +10 -0
  441. package/dist/jobs/in-process.d.ts.map +1 -0
  442. package/dist/jobs/in-process.js +45 -0
  443. package/dist/jobs/in-process.js.map +1 -0
  444. package/dist/jobs/lock-file.d.ts +13 -0
  445. package/dist/jobs/lock-file.d.ts.map +1 -0
  446. package/dist/jobs/lock-file.js +107 -0
  447. package/dist/jobs/lock-file.js.map +1 -0
  448. package/dist/jobs/processes.d.ts +11 -0
  449. package/dist/jobs/processes.d.ts.map +1 -0
  450. package/dist/jobs/processes.js +50 -0
  451. package/dist/jobs/processes.js.map +1 -0
  452. package/dist/jobs/queue.d.ts +18 -0
  453. package/dist/jobs/queue.d.ts.map +1 -0
  454. package/dist/jobs/queue.js +220 -0
  455. package/dist/jobs/queue.js.map +1 -0
  456. package/dist/jobs/types.d.ts +159 -0
  457. package/dist/jobs/types.d.ts.map +1 -0
  458. package/dist/jobs/types.js +22 -0
  459. package/dist/jobs/types.js.map +1 -0
  460. package/dist/mail/index.d.ts +0 -2
  461. package/dist/mail/index.d.ts.map +1 -1
  462. package/dist/mail/index.js +0 -1
  463. package/dist/mail/index.js.map +1 -1
  464. package/dist/maintenance.d.ts +5 -42
  465. package/dist/maintenance.d.ts.map +1 -1
  466. package/dist/maintenance.js +4 -67
  467. package/dist/maintenance.js.map +1 -1
  468. package/dist/media/library.d.ts +1 -1
  469. package/dist/media/library.d.ts.map +1 -1
  470. package/dist/media/library.js +4 -1
  471. package/dist/media/library.js.map +1 -1
  472. package/dist/media/uploads.d.ts +1 -1
  473. package/dist/media/uploads.d.ts.map +1 -1
  474. package/dist/media/uploads.js +1 -1
  475. package/dist/media/uploads.js.map +1 -1
  476. package/dist/micropub/endpoint.js +10 -10
  477. package/dist/micropub/endpoint.js.map +1 -1
  478. package/dist/micropub/media.d.ts +0 -2
  479. package/dist/micropub/media.d.ts.map +1 -1
  480. package/dist/micropub/media.js +5 -7
  481. package/dist/micropub/media.js.map +1 -1
  482. package/dist/notifications/comments.d.ts +4 -2
  483. package/dist/notifications/comments.d.ts.map +1 -1
  484. package/dist/notifications/comments.js +77 -65
  485. package/dist/notifications/comments.js.map +1 -1
  486. package/dist/notifications/digest.d.ts +2 -4
  487. package/dist/notifications/digest.d.ts.map +1 -1
  488. package/dist/notifications/digest.js +9 -10
  489. package/dist/notifications/digest.js.map +1 -1
  490. package/dist/notifications/index.d.ts +3 -3
  491. package/dist/notifications/index.d.ts.map +1 -1
  492. package/dist/notifications/index.js +3 -3
  493. package/dist/notifications/index.js.map +1 -1
  494. package/dist/notifications/mail-service.d.ts +10 -8
  495. package/dist/notifications/mail-service.d.ts.map +1 -1
  496. package/dist/notifications/mail-service.js +70 -70
  497. package/dist/notifications/mail-service.js.map +1 -1
  498. package/dist/notifications/optouts.d.ts +0 -2
  499. package/dist/notifications/optouts.d.ts.map +1 -1
  500. package/dist/notifications/optouts.js +2 -4
  501. package/dist/notifications/optouts.js.map +1 -1
  502. package/dist/notifications/tokens.d.ts +0 -2
  503. package/dist/notifications/tokens.d.ts.map +1 -1
  504. package/dist/notifications/tokens.js +2 -28
  505. package/dist/notifications/tokens.js.map +1 -1
  506. package/dist/notify.d.ts +4 -2
  507. package/dist/notify.d.ts.map +1 -1
  508. package/dist/notify.js +27 -15
  509. package/dist/notify.js.map +1 -1
  510. package/dist/plugin.d.ts +36 -16
  511. package/dist/plugin.d.ts.map +1 -1
  512. package/dist/plugin.js +1 -1
  513. package/dist/plugin.js.map +1 -1
  514. package/dist/plugins/changes.d.ts +0 -1
  515. package/dist/plugins/changes.d.ts.map +1 -1
  516. package/dist/plugins/changes.js +2 -3
  517. package/dist/plugins/changes.js.map +1 -1
  518. package/dist/plugins/data.d.ts +19 -0
  519. package/dist/plugins/data.d.ts.map +1 -0
  520. package/dist/plugins/data.js +117 -0
  521. package/dist/plugins/data.js.map +1 -0
  522. package/dist/plugins/enabled.d.ts +1 -13
  523. package/dist/plugins/enabled.d.ts.map +1 -1
  524. package/dist/plugins/enabled.js +11 -23
  525. package/dist/plugins/enabled.js.map +1 -1
  526. package/dist/plugins/install.d.ts +1 -1
  527. package/dist/plugins/install.d.ts.map +1 -1
  528. package/dist/plugins/install.js +7 -42
  529. package/dist/plugins/install.js.map +1 -1
  530. package/dist/plugins/migrate-settings.d.ts +7 -0
  531. package/dist/plugins/migrate-settings.d.ts.map +1 -0
  532. package/dist/plugins/migrate-settings.js +41 -0
  533. package/dist/plugins/migrate-settings.js.map +1 -0
  534. package/dist/plugins/registry.d.ts +10 -14
  535. package/dist/plugins/registry.d.ts.map +1 -1
  536. package/dist/plugins/registry.js +56 -36
  537. package/dist/plugins/registry.js.map +1 -1
  538. package/dist/plugins/settings.d.ts +5 -57
  539. package/dist/plugins/settings.d.ts.map +1 -1
  540. package/dist/plugins/settings.js +47 -180
  541. package/dist/plugins/settings.js.map +1 -1
  542. package/dist/plugins/site.d.ts +2 -1
  543. package/dist/plugins/site.d.ts.map +1 -1
  544. package/dist/plugins/site.js +5 -5
  545. package/dist/plugins/site.js.map +1 -1
  546. package/dist/privacy/retention.d.ts +4 -0
  547. package/dist/privacy/retention.d.ts.map +1 -1
  548. package/dist/privacy/retention.js +12 -5
  549. package/dist/privacy/retention.js.map +1 -1
  550. package/dist/settings/akismet.d.ts +9 -0
  551. package/dist/settings/akismet.d.ts.map +1 -0
  552. package/dist/settings/akismet.js +46 -0
  553. package/dist/settings/akismet.js.map +1 -0
  554. package/dist/settings/declare.d.ts +15 -0
  555. package/dist/settings/declare.d.ts.map +1 -0
  556. package/dist/settings/declare.js +20 -0
  557. package/dist/settings/declare.js.map +1 -0
  558. package/dist/settings/dev-mode.d.ts +29 -0
  559. package/dist/settings/dev-mode.d.ts.map +1 -0
  560. package/dist/settings/dev-mode.js +77 -0
  561. package/dist/settings/dev-mode.js.map +1 -0
  562. package/dist/settings/kinds.d.ts +13 -0
  563. package/dist/settings/kinds.d.ts.map +1 -0
  564. package/dist/settings/kinds.js +108 -0
  565. package/dist/settings/kinds.js.map +1 -0
  566. package/dist/settings/mail.d.ts +10 -0
  567. package/dist/settings/mail.d.ts.map +1 -0
  568. package/dist/settings/mail.js +92 -0
  569. package/dist/settings/mail.js.map +1 -0
  570. package/dist/settings/maintenance.d.ts +6 -0
  571. package/dist/settings/maintenance.d.ts.map +1 -0
  572. package/dist/settings/maintenance.js +33 -0
  573. package/dist/settings/maintenance.js.map +1 -0
  574. package/dist/settings/names.d.ts +7 -0
  575. package/dist/settings/names.d.ts.map +1 -0
  576. package/dist/settings/names.js +31 -0
  577. package/dist/settings/names.js.map +1 -0
  578. package/dist/settings/presence.d.ts +2 -0
  579. package/dist/settings/presence.d.ts.map +1 -0
  580. package/dist/settings/presence.js +7 -0
  581. package/dist/settings/presence.js.map +1 -0
  582. package/dist/settings/registry.d.ts +14 -0
  583. package/dist/settings/registry.d.ts.map +1 -0
  584. package/dist/settings/registry.js +369 -0
  585. package/dist/settings/registry.js.map +1 -0
  586. package/dist/settings/types.d.ts +176 -0
  587. package/dist/settings/types.d.ts.map +1 -0
  588. package/dist/settings/types.js +2 -0
  589. package/dist/settings/types.js.map +1 -0
  590. package/dist/site/namespace.d.ts +88 -0
  591. package/dist/site/namespace.d.ts.map +1 -0
  592. package/dist/site/namespace.js +471 -0
  593. package/dist/site/namespace.js.map +1 -0
  594. package/dist/site/setting-rows.d.ts +9 -10
  595. package/dist/site/setting-rows.d.ts.map +1 -1
  596. package/dist/site/setting-rows.js +109 -228
  597. package/dist/site/setting-rows.js.map +1 -1
  598. package/dist/site/settings-form.d.ts.map +1 -1
  599. package/dist/site/settings-form.js +6 -10
  600. package/dist/site/settings-form.js.map +1 -1
  601. package/dist/site/settings.d.ts +6 -322
  602. package/dist/site/settings.d.ts.map +1 -1
  603. package/dist/site/settings.js +13 -76
  604. package/dist/site/settings.js.map +1 -1
  605. package/dist/site/site-json.d.ts +0 -31
  606. package/dist/site/site-json.d.ts.map +1 -1
  607. package/dist/site/site-json.js +5 -49
  608. package/dist/site/site-json.js.map +1 -1
  609. package/dist/site/users.d.ts +0 -9
  610. package/dist/site/users.d.ts.map +1 -1
  611. package/dist/site/users.js +2 -11
  612. package/dist/site/users.js.map +1 -1
  613. package/dist/store/queue-database.d.ts +11 -0
  614. package/dist/store/queue-database.d.ts.map +1 -0
  615. package/dist/store/queue-database.js +39 -0
  616. package/dist/store/queue-database.js.map +1 -0
  617. package/dist/supervisor/control.d.ts +1 -0
  618. package/dist/supervisor/control.d.ts.map +1 -1
  619. package/dist/supervisor/control.js +10 -0
  620. package/dist/supervisor/control.js.map +1 -1
  621. package/dist/supervisor/primary.d.ts.map +1 -1
  622. package/dist/supervisor/primary.js +15 -3
  623. package/dist/supervisor/primary.js.map +1 -1
  624. package/dist/testing.d.ts +1 -1
  625. package/dist/testing.d.ts.map +1 -1
  626. package/dist/testing.js +1 -1
  627. package/dist/testing.js.map +1 -1
  628. package/dist/theme/site-data.d.ts +1 -1
  629. package/dist/theme/site-data.d.ts.map +1 -1
  630. package/dist/theme/site-data.js.map +1 -1
  631. package/dist/web/asset-routes.d.ts +6 -2
  632. package/dist/web/asset-routes.d.ts.map +1 -1
  633. package/dist/web/asset-routes.js +12 -4
  634. package/dist/web/asset-routes.js.map +1 -1
  635. package/dist/web/comment-routes.d.ts.map +1 -1
  636. package/dist/web/comment-routes.js +4 -4
  637. package/dist/web/comment-routes.js.map +1 -1
  638. package/dist/web/contact-routes.js +1 -1
  639. package/dist/web/contact-routes.js.map +1 -1
  640. package/dist/web/context.d.ts +5 -0
  641. package/dist/web/context.d.ts.map +1 -1
  642. package/dist/web/context.js +9 -4
  643. package/dist/web/context.js.map +1 -1
  644. package/dist/web/conversation.d.ts +1 -1
  645. package/dist/web/conversation.d.ts.map +1 -1
  646. package/dist/web/conversation.js +13 -16
  647. package/dist/web/conversation.js.map +1 -1
  648. package/dist/web/document-page.d.ts.map +1 -1
  649. package/dist/web/document-page.js +5 -4
  650. package/dist/web/document-page.js.map +1 -1
  651. package/dist/web/document-site.d.ts +1 -1
  652. package/dist/web/document-site.d.ts.map +1 -1
  653. package/dist/web/document-site.js +2 -1
  654. package/dist/web/document-site.js.map +1 -1
  655. package/dist/web/embed.js +1 -1
  656. package/dist/web/embed.js.map +1 -1
  657. package/dist/web/env.d.ts +9 -0
  658. package/dist/web/env.d.ts.map +1 -1
  659. package/dist/web/federation-routes.d.ts.map +1 -1
  660. package/dist/web/federation-routes.js +3 -1
  661. package/dist/web/federation-routes.js.map +1 -1
  662. package/dist/web/feed-item.js +1 -1
  663. package/dist/web/feed-item.js.map +1 -1
  664. package/dist/web/feed-routes.d.ts +3 -4
  665. package/dist/web/feed-routes.d.ts.map +1 -1
  666. package/dist/web/feed-routes.js +17 -15
  667. package/dist/web/feed-routes.js.map +1 -1
  668. package/dist/web/health.js +1 -1
  669. package/dist/web/health.js.map +1 -1
  670. package/dist/web/index.d.ts +1 -1
  671. package/dist/web/index.d.ts.map +1 -1
  672. package/dist/web/index.js.map +1 -1
  673. package/dist/web/indieauth-activity-log.d.ts +0 -2
  674. package/dist/web/indieauth-activity-log.d.ts.map +1 -1
  675. package/dist/web/indieauth-activity-log.js +2 -4
  676. package/dist/web/indieauth-activity-log.js.map +1 -1
  677. package/dist/web/indieauth-bearer.js +2 -2
  678. package/dist/web/indieauth-bearer.js.map +1 -1
  679. package/dist/web/indieauth-discovery.d.ts +0 -4
  680. package/dist/web/indieauth-discovery.d.ts.map +1 -1
  681. package/dist/web/indieauth-discovery.js +3 -10
  682. package/dist/web/indieauth-discovery.js.map +1 -1
  683. package/dist/web/indieauth-token.d.ts.map +1 -1
  684. package/dist/web/indieauth-token.js +3 -3
  685. package/dist/web/indieauth-token.js.map +1 -1
  686. package/dist/web/listing-pages.d.ts.map +1 -1
  687. package/dist/web/listing-pages.js +1 -2
  688. package/dist/web/listing-pages.js.map +1 -1
  689. package/dist/web/maintenance.js +3 -3
  690. package/dist/web/maintenance.js.map +1 -1
  691. package/dist/web/page-sources.d.ts +1 -2
  692. package/dist/web/page-sources.d.ts.map +1 -1
  693. package/dist/web/page-sources.js +6 -7
  694. package/dist/web/page-sources.js.map +1 -1
  695. package/dist/web/paths.js +1 -1
  696. package/dist/web/paths.js.map +1 -1
  697. package/dist/web/pingback-routes.d.ts.map +1 -1
  698. package/dist/web/pingback-routes.js +3 -3
  699. package/dist/web/pingback-routes.js.map +1 -1
  700. package/dist/web/plugin-federation.js +50 -40
  701. package/dist/web/plugin-federation.js.map +1 -1
  702. package/dist/web/render.d.ts +12 -5
  703. package/dist/web/render.d.ts.map +1 -1
  704. package/dist/web/render.js +31 -21
  705. package/dist/web/render.js.map +1 -1
  706. package/dist/web/request.js +1 -1
  707. package/dist/web/request.js.map +1 -1
  708. package/dist/web/routes.d.ts.map +1 -1
  709. package/dist/web/routes.js +8 -9
  710. package/dist/web/routes.js.map +1 -1
  711. package/dist/web/site-index.d.ts.map +1 -1
  712. package/dist/web/site-index.js +1 -2
  713. package/dist/web/site-index.js.map +1 -1
  714. package/dist/web/site-view.d.ts +0 -2
  715. package/dist/web/site-view.d.ts.map +1 -1
  716. package/dist/web/site-view.js +0 -2
  717. package/dist/web/site-view.js.map +1 -1
  718. package/dist/web/webmention-routes.d.ts.map +1 -1
  719. package/dist/web/webmention-routes.js +2 -2
  720. package/dist/web/webmention-routes.js.map +1 -1
  721. package/dist/web/well-known.d.ts.map +1 -1
  722. package/dist/web/well-known.js +6 -7
  723. package/dist/web/well-known.js.map +1 -1
  724. package/dist/webmention/cited-picture.js +1 -1
  725. package/dist/webmention/cited-picture.js.map +1 -1
  726. package/dist/webmention/pingback.d.ts +1 -8
  727. package/dist/webmention/pingback.d.ts.map +1 -1
  728. package/dist/webmention/pingback.js +2 -11
  729. package/dist/webmention/pingback.js.map +1 -1
  730. package/dist/webmention/receive.d.ts +3 -1
  731. package/dist/webmention/receive.d.ts.map +1 -1
  732. package/dist/webmention/receive.js +5 -3
  733. package/dist/webmention/receive.js.map +1 -1
  734. package/dist/webmention/reply-contexts.d.ts +14 -3
  735. package/dist/webmention/reply-contexts.d.ts.map +1 -1
  736. package/dist/webmention/reply-contexts.js +12 -10
  737. package/dist/webmention/reply-contexts.js.map +1 -1
  738. package/dist/webmention/service.d.ts +3 -1
  739. package/dist/webmention/service.d.ts.map +1 -1
  740. package/dist/webmention/service.js +66 -53
  741. package/dist/webmention/service.js.map +1 -1
  742. package/package.json +13 -11
  743. package/themes/default/partials/post-list.njk +11 -11
  744. package/themes/default/src/style.css +12 -0
  745. package/themes/default/static/style.css +19 -0
package/dist/cli.js CHANGED
@@ -10,22 +10,32 @@ import { credentialProblem, emailProblem } from './admin/credentials.js';
10
10
  import { migrateUsersToFile } from './admin/legacy-migrations.js';
11
11
  import { adminStoreOwning, openAdminDatabase } from './store/admin-store.js';
12
12
  import { databaseFile, discardDatabase } from './cache.js';
13
- import { CONFIG_FILENAMES, loadResolvedConfig } from './config-loader.js';
13
+ import { CONTENT_SCRATCH_FILES, dataPath } from './files/data-files.js';
14
+ import { compactTime, exportFileName, planSiteExport, writeSiteExport, } from './export/site-export.js';
15
+ import { checkImportTargets, discardStaging, finishSiteImport, importNotes, installArchivedPlugins, placeSiteImport, pluginFailureLine, readSiteManifest, restoreEnabledPlugins, stageSiteImport, } from './export/site-import.js';
16
+ import { CONFIG_FILENAMES, loadConfig, loadResolvedConfig } from './config-loader.js';
17
+ import { resolveConfig } from './config.js';
14
18
  import { migrateCommentEmails, migrateCommentKeys } from './comments/migrations.js';
15
- import { stripStoredUploads } from './content/metadata/sweep.js';
19
+ import { STRIP_METADATA_JOB } from './content/metadata/strip-job.js';
20
+ import { RESEND_JOB } from './federation/resend-job.js';
16
21
  import { createUser, DuplicateUsernameError } from './site/users.js';
17
- import { UPLOAD_DIRECTORY } from './content/uploads.js';
18
- import { createCms } from './index.js';
22
+ import { createCms, settleConfig } from './index.js';
19
23
  import { initSite, ownManifest, seedStarterContent } from './init.js';
24
+ import { finishedResult } from './jobs/types.js';
20
25
  import { importPluginFolders, scanPluginFolders } from './plugins/folder.js';
21
26
  import { ADD_WITH_COMMAND, addPlugin, installedFolders, parsePackageSpec, pluginRegistry, removePlugin, requirementNotes, } from './plugins/install.js';
22
27
  import { describeUpgrade, upgradePlugins } from './plugins/upgrade.js';
28
+ import { migratePluginSettings } from './plugins/migrate-settings.js';
29
+ import { siteDataFiles } from './plugins/data.js';
30
+ import { readEnabledPlugins } from './plugins/enabled.js';
23
31
  import { pluginSite, sitePluginRegistry } from './plugins/site.js';
24
- import { askToReload } from './supervisor/control.js';
32
+ import { askToReload, isServing } from './supervisor/control.js';
25
33
  import { superviseCluster } from './supervisor/primary.js';
26
34
  import { processChannel, superviseWorker } from './supervisor/worker.js';
27
- import { enterMaintenance, leaveMaintenance, maintenanceFile, readMaintenance, } from './maintenance.js';
28
- import { devModeOn, enterDevMode, leaveDevMode } from './dev-mode.js';
35
+ import { DEV_MODE, DEV_MODE_LATCHED } from './settings/dev-mode.js';
36
+ import { MAINTENANCE, MAINTENANCE_FILE } from './settings/maintenance.js';
37
+ import { overridden, sourceLabel } from './settings/registry.js';
38
+ import { createSettings } from './site/namespace.js';
29
39
  import { messageOf } from './util/errors.js';
30
40
  const COMMANDS = {
31
41
  serve: {
@@ -106,6 +116,45 @@ const COMMANDS = {
106
116
  ],
107
117
  run: devModeCommand,
108
118
  },
119
+ settings: {
120
+ synopsis: [
121
+ 'settings list [<namespace>] [--config <file>]',
122
+ 'settings get <namespace.key> [--config <file>]',
123
+ 'settings set <namespace.key> (<value> | --unset) [--config <file>]',
124
+ 'settings migrate-plugins [--config <file>]',
125
+ ],
126
+ help: [
127
+ [
128
+ 'settings list',
129
+ [
130
+ 'Print every runtime setting: its value in effect, where',
131
+ 'that value came from (env, config, file or default) and',
132
+ 'what it does. A secret prints as (set) or (not set).',
133
+ "Each running plugin's settings follow the core ones, as",
134
+ '@geekity/plugin-llm.default_model.',
135
+ ],
136
+ ],
137
+ ['settings get', ['Print one value in effect, and what overrides it, if', 'anything.']],
138
+ [
139
+ 'settings set',
140
+ [
141
+ 'Store one value, or remove it with --unset so the default',
142
+ 'applies. A setting the environment or the config overrides',
143
+ 'is refused. A secret set here is left in the shell history.',
144
+ ],
145
+ ],
146
+ [
147
+ 'settings migrate-plugins',
148
+ [
149
+ "Move each plugin's settings out of content/_data/site.json",
150
+ 'into data/plugins/<name>/settings.json, keeping enabled',
151
+ 'where it is. A server does this when it starts; run it',
152
+ 'with the site stopped to do it first. Safe to run twice.',
153
+ ],
154
+ ],
155
+ ],
156
+ run: settingsCommand,
157
+ },
109
158
  'strip-metadata': {
110
159
  synopsis: ['strip-metadata [--config <file>]'],
111
160
  help: [
@@ -122,6 +171,45 @@ const COMMANDS = {
122
171
  ],
123
172
  run: stripMetadataCommand,
124
173
  },
174
+ export: {
175
+ synopsis: [
176
+ 'export [--out <file>] [--with-credentials] [--without-content] [--list] [--config <file>]',
177
+ ],
178
+ help: [
179
+ [
180
+ 'export',
181
+ [
182
+ 'Write the site to a .tar.gz: content/ and every file in',
183
+ 'data/ the site cannot rebuild, with manifest.json first.',
184
+ 'Credentials are left out unless --with-credentials is given.',
185
+ 'It only reads, so it runs while the site serves. It prints',
186
+ 'the deliveries waiting in the queue, which are never',
187
+ 'exported. --list prints what each file in data/ is and',
188
+ 'writes nothing.',
189
+ ],
190
+ ],
191
+ ],
192
+ run: exportCommand,
193
+ },
194
+ import: {
195
+ synopsis: ['import <file> [--replace] [--dev-mode] [--new-base-url] [--config <file>]'],
196
+ help: [
197
+ [
198
+ 'import',
199
+ [
200
+ 'Restore a site from an archive geekity export wrote, with the',
201
+ 'site stopped. Every file is checked against the manifest and',
202
+ 'unpacked beside content/ and data/ before it replaces them.',
203
+ 'Folder plugins are installed from the npm registry at the',
204
+ 'versions the archive records, and the database is rebuilt.',
205
+ 'It exits 1 when a plugin could not be installed, naming it',
206
+ 'and the command to retry.',
207
+ ],
208
+ ],
209
+ ],
210
+ sharedWithPlugins: true,
211
+ run: importCommand,
212
+ },
125
213
  user: {
126
214
  synopsis: ['user add <username> [--password <pw>] [--email <address>] [--config <file>]'],
127
215
  help: [
@@ -175,9 +263,19 @@ const COMMANDS = {
175
263
  * The options one command reads out of {@link ParsedArgs.flags}, each of which
176
264
  * carries a value.
177
265
  */
178
- const VALUE_FLAGS = ['until'];
266
+ const VALUE_FLAGS = ['until', 'out'];
179
267
  /** The options that are simply on or off. */
180
- const SWITCH_FLAGS = ['all', 'check'];
268
+ const SWITCH_FLAGS = [
269
+ 'all',
270
+ 'check',
271
+ 'unset',
272
+ 'with-credentials',
273
+ 'without-content',
274
+ 'list',
275
+ 'replace',
276
+ 'dev-mode',
277
+ 'new-base-url',
278
+ ];
181
279
  const OPTIONS_HELP = `Options:
182
280
  --config <file> Config file to load. Defaults to the first of
183
281
  ${CONFIG_FILENAMES.join(', ')} found in the working directory.
@@ -191,8 +289,24 @@ const OPTIONS_HELP = `Options:
191
289
  ISO 8601 time such as 2026-09-28T14:00:00Z. It is the
192
290
  Retry-After a client is sent, and the maintenance page
193
291
  says it.
292
+ --unset With settings set: remove the stored value.
194
293
  --all With resend: every announced post rather than named ones.
195
294
  --check With plugin upgrade: say what would change, install nothing.
295
+ --out <file> With export: the archive to write. Defaults to
296
+ geekity-<host>-<time>.tar.gz in the working directory.
297
+ --with-credentials
298
+ With export: include the API keys and logins kept in
299
+ data/ (mail.json, akismet.json, each plugin's secrets).
300
+ --without-content
301
+ With export: leave content/ out.
302
+ --list With export: print each file data/ may hold, its class
303
+ and what is lost without it, and write nothing.
304
+ --replace With import: move what content/ and data/ hold into
305
+ <dir>/.geekity-before-import-<time> when they hold a site.
306
+ --dev-mode With import: turn dev mode on, for a staging copy of a
307
+ live site.
308
+ --new-base-url With import: accept a base URL in effect that differs
309
+ from the one the archive was exported from.
196
310
  -h, --help Show this help.
197
311
  -v, --version Show the installed version.
198
312
 
@@ -314,9 +428,12 @@ function isCommand(value) {
314
428
  }
315
429
  /** Run one command. Resolves with the exit code the process should use. */
316
430
  async function main(argv) {
317
- const first = leadingWords(argv)[0];
318
- if (first !== undefined && !isCommand(first) && !argv.some(isHelpOrVersion)) {
319
- return await pluginCommand(argv);
431
+ const words = leadingWords(argv);
432
+ const first = words[0];
433
+ if (first !== undefined && !argv.some(isHelpOrVersion)) {
434
+ if (!isCommand(first) || (await pluginExtends(first, words, configFlag(argv)))) {
435
+ return await pluginCommand(argv);
436
+ }
320
437
  }
321
438
  const parsed = parseArgs(argv);
322
439
  if (parsed.command === 'help') {
@@ -348,34 +465,33 @@ async function initCommand({ args }) {
348
465
  ].join('\n'));
349
466
  return 0;
350
467
  }
351
- /**
352
- * `geekity maintenance on|off|status` (TASK-130).
353
- *
354
- * Only the file under `data/` is touched, so this works the same with the
355
- * site running or stopped: a running site re-reads it within a second.
356
- */
357
468
  async function maintenanceCommand({ args, configPath, flags }) {
358
469
  const action = args[0];
359
470
  const config = await loadResolvedConfig(configPath);
360
- const file = maintenanceFile(config.dataDir);
471
+ const settings = createSettings({ config });
361
472
  if (action === 'on') {
362
473
  const until = untilFlag(text(flags, 'until'));
363
- await enterMaintenance(config.dataDir, { until });
474
+ await setOrThrow(settings, 'maintenance', { on: true, until: until?.toISOString() ?? '' });
475
+ const file = dataPath(config.dataDir, MAINTENANCE_FILE);
364
476
  process.stdout.write(`Maintenance mode is on (${file}).${untilLine(until)}\n`);
365
477
  return 0;
366
478
  }
367
479
  if (action === 'off') {
368
- await leaveMaintenance(config.dataDir);
369
- process.stdout.write(`Maintenance mode is off.${forcedLine(config.maintenance)}\n`);
480
+ if (overridden((await settings.resolve(MAINTENANCE.on)).source)) {
481
+ throw new Error(`Maintenance mode is still on.${forcedLine(true)}`);
482
+ }
483
+ await setOrThrow(settings, 'maintenance', { on: false });
484
+ process.stdout.write('Maintenance mode is off.\n');
370
485
  return 0;
371
486
  }
372
487
  if (action === 'status') {
373
- const window = readMaintenance(config.dataDir);
374
- if (window === undefined && !config.maintenance) {
488
+ const on = await settings.resolve(MAINTENANCE.on);
489
+ if (!on.value) {
375
490
  process.stdout.write('Maintenance mode is off.\n');
376
491
  }
377
492
  else {
378
- process.stdout.write(`Maintenance mode is on.${untilLine(window?.until)}${forcedLine(config.maintenance)}\n`);
493
+ const until = await settings.get(MAINTENANCE.until);
494
+ process.stdout.write(`Maintenance mode is on.${untilLine(until === '' ? undefined : new Date(until))}${forcedLine(overridden(on.source))}\n`);
379
495
  }
380
496
  return 0;
381
497
  }
@@ -384,28 +500,170 @@ async function maintenanceCommand({ args, configPath, flags }) {
384
500
  async function devModeCommand({ args, configPath }) {
385
501
  const action = args[0];
386
502
  const config = await loadResolvedConfig(configPath);
503
+ const settings = createSettings({ config });
387
504
  if (action === 'on') {
388
- enterDevMode(config.dataDir, 'command', config.now());
505
+ await setOrThrow(settings, 'dev_mode', { on: true });
389
506
  process.stdout.write('Dev mode is on: nothing the site does is sent until `geekity dev-mode off`.\n');
390
507
  return 0;
391
508
  }
392
509
  if (action === 'off') {
393
510
  if (config.devMode) {
394
- throw new Error('GEEKITY_DEV_MODE or the devMode setting still asks for dev mode. ' +
395
- 'Remove it, then run geekity dev-mode off again.');
511
+ throw new Error(`${DEV_MODE_LATCHED} Remove it, then run geekity dev-mode off again.`);
396
512
  }
397
- const wasOn = leaveDevMode(config.dataDir, config.now());
513
+ const wasOn = await settings.get(DEV_MODE.on);
514
+ await setOrThrow(settings, 'dev_mode', { on: false });
398
515
  process.stdout.write(wasOn
399
516
  ? 'Dev mode is off: the site is live. Nothing held while it was on will be sent.\n'
400
517
  : 'Dev mode was already off: the site is live.\n');
401
518
  return 0;
402
519
  }
403
520
  if (action === 'status') {
404
- process.stdout.write(devModeOn(config) ? 'Dev mode is on.\n' : 'Dev mode is off.\n');
521
+ process.stdout.write((await settings.get(DEV_MODE.on)) ? 'Dev mode is on.\n' : 'Dev mode is off.\n');
405
522
  return 0;
406
523
  }
407
524
  throw new Error('geekity dev-mode needs on, off or status.');
408
525
  }
526
+ async function settingsCommand({ args, configPath, flags }) {
527
+ const [action, name, value] = args;
528
+ const config = await loadResolvedConfig(configPath);
529
+ if (action === 'migrate-plugins' && name === undefined) {
530
+ const moved = migratePluginSettings({
531
+ contentDir: config.contentDir,
532
+ dataDir: config.dataDir,
533
+ log: (line) => process.stdout.write(`${line}\n`),
534
+ });
535
+ if (moved.length === 0) {
536
+ process.stdout.write('No plugin settings are left in content/_data/site.json.\n');
537
+ }
538
+ return 0;
539
+ }
540
+ const request = settingsRequest(action, name, value, flags['unset'] === true);
541
+ if (request === undefined) {
542
+ throw new Error('geekity settings needs list [<namespace>], get <namespace.key>, set <namespace.key> with a value or --unset, or migrate-plugins.');
543
+ }
544
+ const settings = await settingsWithRunningPlugins(config);
545
+ if (request.action === 'list') {
546
+ const refs = settings.list().filter((ref) => name === undefined || ref.namespace === name);
547
+ if (refs.length === 0)
548
+ throw new Error(`There is no settings namespace ${String(name)}.`);
549
+ const rows = await Promise.all(refs.map((ref) => settingRow(settings, ref)));
550
+ process.stdout.write(table(rows));
551
+ return 0;
552
+ }
553
+ const ref = settingNamed(settings, request.name);
554
+ if (request.action === 'get') {
555
+ const resolved = await settings.resolve(ref);
556
+ const lines = [shown(ref, resolved.value)];
557
+ if (overridden(resolved.source)) {
558
+ const held = resolved.stored === undefined ? 'nothing' : shown(ref, resolved.stored);
559
+ lines.push(`overridden by ${sourceLabel(resolved.source)}; the file holds ${held}`);
560
+ }
561
+ process.stdout.write(`${lines.join('\n')}\n`);
562
+ return 0;
563
+ }
564
+ const { type } = ref.declaration;
565
+ let stored;
566
+ if (request.value !== undefined) {
567
+ const parsed = type.fromText(request.value);
568
+ if (!parsed.ok)
569
+ throw new Error(`${request.name} ${parsed.problem}.`);
570
+ stored = type.toJson(parsed.value);
571
+ }
572
+ await setOrThrow(settings, ref.namespace, { [ref.key]: stored });
573
+ process.stdout.write(table([await settingRow(settings, ref)]));
574
+ return 0;
575
+ }
576
+ function settingsRequest(action, name, value, unset) {
577
+ if (action === 'list')
578
+ return { action };
579
+ if (name === undefined)
580
+ return undefined;
581
+ if (action === 'get')
582
+ return { action, name };
583
+ if (action === 'set' && (value !== undefined) !== unset)
584
+ return { action, name, value };
585
+ return undefined;
586
+ }
587
+ async function settingsWithRunningPlugins(config) {
588
+ migratePluginSettings({
589
+ contentDir: config.contentDir,
590
+ dataDir: config.dataDir,
591
+ log: (line) => process.stderr.write(`${line}\n`),
592
+ });
593
+ const settings = createSettings({ config });
594
+ let registry;
595
+ try {
596
+ registry = await loadSitePlugins(config, settings);
597
+ }
598
+ catch (error) {
599
+ process.stderr.write(`Plugin settings are not listed: ${messageOf(error)}\n`);
600
+ return settings;
601
+ }
602
+ const enabled = readEnabledPlugins(config.contentDir);
603
+ const running = registry.active(enabled);
604
+ for (const entry of registry.plugins) {
605
+ const { name } = entry.plugin;
606
+ const declared = entry.settingsNamespace !== undefined;
607
+ const unremarkable = !declared && !enabled.has(name) && entry.problem === undefined;
608
+ if (running.has(name) || unremarkable)
609
+ continue;
610
+ const unlisted = declared ? ' Its settings are not listed.' : '';
611
+ process.stderr.write(`${name} ${whyNotRunning(entry, enabled)}${unlisted}\n`);
612
+ if (declared)
613
+ settings.unregister(name);
614
+ }
615
+ return settings;
616
+ }
617
+ function whyNotRunning(entry, enabled) {
618
+ if (entry.problem !== undefined)
619
+ return `did not load: ${entry.problem}`;
620
+ if (enabled.has(entry.plugin.name)) {
621
+ return 'is enabled but not running, because a plugin it requires is not running.';
622
+ }
623
+ return 'is installed but not enabled.';
624
+ }
625
+ function settingNamed(settings, name) {
626
+ const dot = name.lastIndexOf('.');
627
+ const found = settings
628
+ .list()
629
+ .find((ref) => ref.namespace === name.slice(0, dot) && ref.key === name.slice(dot + 1));
630
+ if (found === undefined) {
631
+ throw new Error(`There is no setting ${name}. geekity settings list names them.`);
632
+ }
633
+ return found;
634
+ }
635
+ async function setOrThrow(settings, namespace, changes) {
636
+ const result = await settings.set(namespace, new Map(Object.entries(changes)), 'cli');
637
+ if (!result.ok)
638
+ throw new Error(Object.values(result.problems).join(' '));
639
+ }
640
+ function shown(ref, value) {
641
+ if (ref.declaration.secret === true) {
642
+ return value === ref.declaration.default ? '(not set)' : '(set)';
643
+ }
644
+ return ref.declaration.type.toText(value);
645
+ }
646
+ async function settingRow(settings, ref) {
647
+ const resolved = await settings.resolve(ref);
648
+ let source = sourceLabel(resolved.source);
649
+ if (overridden(resolved.source) && resolved.stored !== undefined) {
650
+ source += ` (file: ${shown(ref, resolved.stored)})`;
651
+ }
652
+ if (ref.declaration.env === false)
653
+ source += ', not overridable';
654
+ return [
655
+ `${ref.namespace}.${ref.key}`,
656
+ shown(ref, resolved.value),
657
+ source,
658
+ ref.declaration.description,
659
+ ];
660
+ }
661
+ function table(rows) {
662
+ const widths = rows[0]?.map((_, column) => Math.max(...rows.map((row) => row[column]?.length ?? 0))) ?? [];
663
+ return rows
664
+ .map((row) => `${row.map((cell, column) => (column === row.length - 1 ? cell : cell.padEnd(widths[column] ?? 0))).join(' ')}\n`)
665
+ .join('');
666
+ }
409
667
  function untilFlag(value) {
410
668
  if (value === undefined)
411
669
  return undefined;
@@ -423,28 +681,52 @@ function forcedLine(forced) {
423
681
  ? ' GEEKITY_MAINTENANCE or the maintenance setting keeps it on until the site restarts without it.'
424
682
  : '';
425
683
  }
426
- async function stripMetadataCommand({ configPath }) {
427
- const config = await loadResolvedConfig(configPath);
428
- const results = await stripStoredUploads(path.join(config.contentDir, UPLOAD_DIRECTORY));
429
- let stripped = 0;
430
- let clean = 0;
431
- let unreadable = 0;
432
- for (const result of results) {
433
- if (result.outcome === 'stripped') {
434
- stripped += 1;
435
- process.stdout.write(`${result.path}: removed ${result.removed.join(', ')}\n`);
436
- }
437
- else if (result.outcome === 'clean') {
438
- clean += 1;
439
- }
440
- else {
441
- unreadable += 1;
442
- process.stderr.write(`${result.path}: ${result.problem} Left as it was.\n`);
684
+ /**
685
+ * Run one job in this process and return its result, or say on standard
686
+ * error why there is none. A job of the same name that is already active
687
+ * stops it before it is queued. A queued job counts only while a server is up
688
+ * to run it: one a command left queued when it died runs when a server next
689
+ * starts, and is no reason to refuse while none is.
690
+ */
691
+ async function runJobHere(config, job, payload, what, prepare) {
692
+ const cms = createCms(config);
693
+ let status;
694
+ try {
695
+ await prepare?.(cms);
696
+ const enqueued = await cms.jobs.enqueueUnlessActive(job, payload, {
697
+ countQueued: await isServing(config.dataDir),
698
+ });
699
+ if (!enqueued.queued) {
700
+ process.stderr.write(`A ${job.name} job is already ${enqueued.active.state}, so this one did not start. ` +
701
+ `Run the command again once it finishes.\n`);
702
+ return undefined;
443
703
  }
704
+ status = await cms.jobs.run(enqueued.id);
444
705
  }
445
- process.stdout.write(`Checked ${count(results.length, 'file')}: ${String(stripped)} stripped, ` +
446
- `${String(clean)} already clean, ${String(unreadable)} unreadable\n`);
447
- return unreadable === 0 ? 0 : 1;
706
+ finally {
707
+ await cms.close();
708
+ }
709
+ const result = finishedResult(job, status);
710
+ if (result === undefined) {
711
+ const problem = status.state === 'failed' ? status.error : `The job ended ${status.state}.`;
712
+ process.stderr.write(`The ${what} did not finish: ${problem}\n`);
713
+ }
714
+ return result;
715
+ }
716
+ async function stripMetadataCommand({ configPath }) {
717
+ const config = await loadResolvedConfig(configPath, (loaded) => ({ ...loaded, watch: false }));
718
+ const report = await runJobHere(config, STRIP_METADATA_JOB, null, 'sweep');
719
+ if (report === undefined)
720
+ return 1;
721
+ for (const file of report.stripped) {
722
+ process.stdout.write(`${file.path}: removed ${file.removed.join(', ')}\n`);
723
+ }
724
+ for (const file of report.unreadable) {
725
+ process.stderr.write(`${file.path}: ${file.problem} Left as it was.\n`);
726
+ }
727
+ process.stdout.write(`Checked ${count(report.checked, 'file')}: ${String(report.stripped.length)} stripped, ` +
728
+ `${String(report.clean)} already clean, ${String(report.unreadable.length)} unreadable\n`);
729
+ return report.unreadable.length === 0 ? 0 : 1;
448
730
  }
449
731
  /**
450
732
  * `geekity sync`: bring the index into line with the files once and stop.
@@ -478,43 +760,39 @@ async function syncCommand({ configPath }) {
478
760
  * Mastodon keeps the copy of a post it first fetched, so a property added
479
761
  * later — the quote policy (TASK-125) is the one that made this a command —
480
762
  * reaches a post already out only through an `Update`. This is the resend
481
- * button on the federation screen, for every post at once. Delivery runs with
482
- * no queue, so each activity has been posted before the process exits.
763
+ * button on the federation screen, for every post at once, and it runs the
764
+ * same resend job (TASK-393). Delivery runs with no queue, so each activity
765
+ * has been posted before the process exits.
483
766
  */
484
767
  async function resendCommand({ args, configPath, flags }) {
485
768
  const all = flags['all'] === true;
486
769
  if (all === args.length > 0) {
487
770
  throw new Error('geekity resend needs either --all or the slugs of the posts to resend, and not both.');
488
771
  }
489
- const cms = createCms(await loadResolvedConfig(configPath, (config) => ({
490
- ...config,
772
+ const config = await loadResolvedConfig(configPath, (loaded) => ({
773
+ ...loaded,
491
774
  watch: false,
492
- federation: { ...config.federation, queue: null },
493
- })));
494
- try {
495
- await cms.sync();
496
- const slugs = all ? cms.store.listFederated().map((document) => document.slug) : args;
497
- let failed = 0;
498
- for (const slug of slugs) {
499
- const report = await cms.delivery.resend(slug);
500
- if (report === undefined) {
501
- process.stderr.write(`${slug}: nothing to resend, no announced post has that slug.\n`);
502
- failed += 1;
503
- continue;
504
- }
505
- if (report.held) {
506
- process.stdout.write(`${slug}: ${report.activityType} held by dev mode, nothing sent\n`);
507
- continue;
508
- }
509
- const reached = report.deliveries.filter((delivery) => delivery.status === 'sent').length;
510
- process.stdout.write(`${slug}: ${report.activityType} to ${String(reached)} of ` +
511
- `${String(report.deliveries.length)} inboxes\n`);
775
+ federation: { ...loaded.federation, queue: null },
776
+ }));
777
+ const report = await runJobHere(config, RESEND_JOB, { slugs: all ? 'all' : args, webmentions: false }, 'resend', (cms) => cms.sync());
778
+ if (report === undefined)
779
+ return 1;
780
+ let failed = 0;
781
+ for (const post of report.posts) {
782
+ const activity = post.resent ? post.activity : null;
783
+ if (activity === null) {
784
+ process.stderr.write(`${post.slug}: nothing to resend, no announced post has that slug.\n`);
785
+ failed += 1;
786
+ }
787
+ else if (activity.heldByDevMode) {
788
+ process.stdout.write(`${post.slug}: ${activity.activityType} held by dev mode, nothing sent\n`);
789
+ }
790
+ else {
791
+ process.stdout.write(`${post.slug}: ${activity.activityType} to ${String(activity.reached)} of ` +
792
+ `${String(activity.inboxes)} inboxes\n`);
512
793
  }
513
- return failed === 0 ? 0 : 1;
514
- }
515
- finally {
516
- await cms.close();
517
794
  }
795
+ return failed === 0 ? 0 : 1;
518
796
  }
519
797
  /**
520
798
  * `geekity rebuild`: throw the database away and read it back out of the files.
@@ -544,11 +822,14 @@ async function rebuildCommand({ configPath }) {
544
822
  'writing to a file nothing can find.\n');
545
823
  return 1;
546
824
  }
825
+ return (await rebuildDatabase(config)) > 0 ? 1 : 0;
826
+ }
827
+ async function rebuildDatabase(config) {
547
828
  discardDatabase(config.dataDir);
548
829
  const cms = createCms(config);
549
830
  try {
550
831
  const result = await cms.sync();
551
- process.stdout.write(`Rebuilt ${file} from the files.\n` +
832
+ process.stdout.write(`Rebuilt ${databaseFile(config.dataDir)} from the files.\n` +
552
833
  `Scanned ${String(result.scanned)}: ${String(result.created)} created, ` +
553
834
  `${String(result.updated)} updated, ${String(result.removed)} removed, ` +
554
835
  `${String(result.unchanged)} unchanged, ${String(result.failed)} failed\n` +
@@ -559,14 +840,130 @@ async function rebuildCommand({ configPath }) {
559
840
  process.stderr.write(`${String(result.failed)} file${result.failed === 1 ? '' : 's'} could not be parsed and ` +
560
841
  `${result.failed === 1 ? 'was' : 'were'} left out of the index; the warnings above name ` +
561
842
  `${result.failed === 1 ? 'it' : 'them'}.\n`);
562
- return 1;
563
843
  }
564
- return 0;
844
+ return result.failed;
565
845
  }
566
846
  finally {
567
847
  await cms.close();
568
848
  }
569
849
  }
850
+ async function importCommand({ args, configPath, flags }) {
851
+ const [file, ...extra] = args;
852
+ if (file === undefined) {
853
+ throw new Error('geekity import needs one archive: geekity import <file>.');
854
+ }
855
+ if (extra.length > 0) {
856
+ throw new Error(`geekity import takes one archive, and no plugin on this site adds "import ${file}".`);
857
+ }
858
+ const unsettled = resolveConfig({
859
+ ...(await loadConfig(process.cwd(), configPath)),
860
+ watch: false,
861
+ });
862
+ const { contentDir, dataDir } = unsettled;
863
+ if (databaseInUse(dataDir) || (await isServing(dataDir))) {
864
+ process.stderr.write(`The site is running: ${dataDir} is in use. Stop it and run geekity import again.\n`);
865
+ return 1;
866
+ }
867
+ const archive = path.resolve(file);
868
+ const targets = { contentDir, dataDir };
869
+ const settings = createSettings({ config: unsettled });
870
+ const archived = await readSiteManifest(archive);
871
+ const { manifest } = archived;
872
+ const occupied = checkImportTargets(archived, targets, settings).filter((target) => target.state === 'occupied');
873
+ if (occupied.length > 0 && flags['replace'] !== true) {
874
+ process.stderr.write(`${occupied.map((target) => target.dir).join(' and ')} already ${occupied.length === 1 ? 'holds' : 'hold'} a site. ` +
875
+ 'Import into empty folders, or give --replace to move what they hold into <dir>/.geekity-before-import-<time> first.\n');
876
+ return 1;
877
+ }
878
+ const staged = await stageSiteImport({ archive, archived, targets, settings });
879
+ const inEffect = await settleConfig(Object.freeze({ ...unsettled, contentDir: staged.content ?? contentDir }));
880
+ if (inEffect.baseUrl !== manifest.baseUrl && flags['new-base-url'] !== true) {
881
+ await discardStaging(staged);
882
+ process.stderr.write(`The base URL in effect is ${inEffect.baseUrl}, and the archive was exported from ${manifest.baseUrl}. ` +
883
+ 'Actor ids are built from the base URL, so followers would not follow the site to a new address. ' +
884
+ `Set GEEKITY_BASE_URL, or baseUrl in the config, to ${manifest.baseUrl}, ` +
885
+ `or give --new-base-url to import it at ${inEffect.baseUrl}.\n`);
886
+ return 1;
887
+ }
888
+ for (const skipped of staged.skipped) {
889
+ process.stderr.write(`Left out ${skipped.path}: ${skipped.why}.\n`);
890
+ }
891
+ for (const aside of await placeSiteImport(staged, { stamp: compactTime(unsettled.now()) })) {
892
+ process.stdout.write(`Moved what was there into ${aside}.\n`);
893
+ }
894
+ process.stdout.write(`Restored ${count(manifest.files.length - staged.skipped.length, 'file')} from ${archive}.\n`);
895
+ const config = await settleConfig(unsettled);
896
+ if (flags['dev-mode'] === true) {
897
+ await setOrThrow(createSettings({ config }), 'dev_mode', { on: true });
898
+ process.stdout.write('Dev mode is on: nothing the site does is sent until `geekity dev-mode off`.\n');
899
+ }
900
+ const failures = await installArchivedPlugins(manifest, {
901
+ pluginsDir: config.pluginsDir,
902
+ registry: pluginRegistry(process.env),
903
+ coreVersion: ownManifest().version,
904
+ });
905
+ for (const plugin of manifest.plugins) {
906
+ if (plugin.source === 'folder' && !failures.some((failed) => failed.name === plugin.name)) {
907
+ process.stdout.write(`Installed ${plugin.name} ${plugin.version}.\n`);
908
+ }
909
+ }
910
+ await restoreEnabledPlugins(manifest, config.contentDir);
911
+ await rebuildDatabase(config);
912
+ await finishSiteImport(targets);
913
+ process.stdout.write(importNotes(manifest)
914
+ .map((note) => `${note}\n`)
915
+ .join(''));
916
+ for (const failed of failures)
917
+ process.stderr.write(`${pluginFailureLine(failed)}\n`);
918
+ return failures.length > 0 ? 1 : 0;
919
+ }
920
+ /**
921
+ * It runs beside a serving site: no migration runs and no database is opened
922
+ * but the queue, read-only, to count what waits in it.
923
+ */
924
+ async function exportCommand({ configPath, flags }) {
925
+ const config = await loadResolvedConfig(configPath, (loaded) => ({ ...loaded, watch: false }));
926
+ const settings = createSettings({ config });
927
+ const plugins = await loadSitePlugins(config, settings);
928
+ if (flags['list'] === true) {
929
+ const declarations = siteDataFiles({ dataDir: config.dataDir, settings, plugins });
930
+ process.stdout.write(table([
931
+ ...declarations.map((each) => [`data/${each.pattern}`, each.dataClass.kind, each.lost]),
932
+ ['content/**', 'kept', 'The site: its documents, uploads and _data.'],
933
+ ...CONTENT_SCRATCH_FILES.map((each) => [
934
+ `content/${each.pattern}`,
935
+ each.dataClass.kind,
936
+ each.lost,
937
+ ]),
938
+ ]));
939
+ return 0;
940
+ }
941
+ const plan = await planSiteExport({ config, settings, plugins }, {
942
+ withCredentials: flags['with-credentials'] === true,
943
+ withContent: flags['without-content'] !== true,
944
+ });
945
+ const { deliveries } = plan.site.pending;
946
+ process.stdout.write(`${count(deliveries, 'delivery is', 'deliveries are')} waiting in data/queue.sqlite. ` +
947
+ 'They are not exported: before a move, wait until none are.\n' +
948
+ 'The archive holds actor keys, password hashes and personal data. ' +
949
+ 'Store it as carefully as data/.\n');
950
+ const now = config.now();
951
+ const out = path.resolve(text(flags, 'out') ?? exportFileName(config.baseUrl, now));
952
+ const manifest = await writeSiteExport(plan, { out, now });
953
+ for (const file of manifest.undeclared) {
954
+ process.stderr.write(`Left out ${file}: no declaration says what it is.\n`);
955
+ }
956
+ for (const entry of manifest.notCarried) {
957
+ process.stderr.write(`Left out ${entry.path}: it is ${entry.why}, and an export holds only regular files.\n`);
958
+ }
959
+ if (manifest.credentialsLeftOut.length > 0) {
960
+ process.stdout.write(`Credentials left out: ${manifest.credentialsLeftOut.join(', ')}. ` +
961
+ '--with-credentials includes them.\n');
962
+ }
963
+ const bytes = manifest.files.reduce((total, file) => total + file.size, 0);
964
+ process.stdout.write(`Wrote ${out}: ${count(manifest.files.length, 'file')}, ${count(bytes, 'byte')}.\n`);
965
+ return 0;
966
+ }
570
967
  /** "1 follower", "2 followers". The plural is the singular plus s unless told. */
571
968
  /** How many comments the files hold, whatever a moderator has done with them. */
572
969
  function totalComments(admin) {
@@ -679,7 +1076,7 @@ async function serveWorker(configPath) {
679
1076
  supervision: worker.supervision,
680
1077
  });
681
1078
  const { port } = await cms.serve();
682
- if (devModeOn(cms.config)) {
1079
+ if (await cms.settings.get(DEV_MODE.on)) {
683
1080
  process.stdout.write('Dev mode is on: ActivityPub, webmentions, feed pings, IndexNow and email are held ' +
684
1081
  'and recorded in data/dev-mode.jsonl. `geekity dev-mode off` takes the site live.\n');
685
1082
  }
@@ -876,17 +1273,26 @@ function configFlag(argv) {
876
1273
  }
877
1274
  return undefined;
878
1275
  }
1276
+ async function loadSitePlugins(config, settings) {
1277
+ const folderPlugins = await importPluginFolders(scanPluginFolders(config.pluginsDir));
1278
+ return sitePluginRegistry(config, folderPlugins, settings);
1279
+ }
879
1280
  /**
880
1281
  * Every command the site's available plugins add, from its config and its
881
1282
  * plugins folder, longest words first.
882
1283
  */
883
1284
  async function pluginCommands(config) {
884
- const folderPlugins = await importPluginFolders(scanPluginFolders(config.pluginsDir));
885
- return sitePluginRegistry(config, folderPlugins)
886
- .plugins.filter((entry) => entry.problem === undefined)
1285
+ return (await loadSitePlugins(config, createSettings({ config }))).plugins
1286
+ .filter((entry) => entry.problem === undefined)
887
1287
  .flatMap((entry) => entry.commands.map((command) => ({ plugin: entry.plugin.name, command })))
888
1288
  .sort((a, b) => b.command.words.length - a.command.words.length);
889
1289
  }
1290
+ async function pluginExtends(first, words, configPath) {
1291
+ if (COMMANDS[first].sharedWithPlugins !== true || words.length < 2)
1292
+ return false;
1293
+ const found = await pluginCommands(await loadResolvedConfig(configPath));
1294
+ return found.some(({ command }) => command.words.every((word, index) => words[index] === word));
1295
+ }
890
1296
  /**
891
1297
  * Run a command a plugin added (decision-33). It needs no enabling: a
892
1298
  * migration command runs before the plugin it prepares for is turned on.
@@ -896,12 +1302,20 @@ async function pluginCommands(config) {
896
1302
  * version that kept its accounts in SQLite is migrated first. The comments
897
1303
  * are migrated as a boot would migrate them for the same reason: a command
898
1304
  * writing comments before any server has would otherwise meet ids the site
899
- * refuses to store.
1305
+ * refuses to store. Plugin settings still in `site.json` move to
1306
+ * `data/plugins/<name>/` first, so the command reads what a boot would.
900
1307
  */
901
1308
  async function pluginCommand(argv) {
902
1309
  const configPath = configFlag(argv);
903
1310
  const config = await loadResolvedConfig(configPath);
904
1311
  const words = leadingWords(argv);
1312
+ migratePluginSettings({
1313
+ contentDir: config.contentDir,
1314
+ dataDir: config.dataDir,
1315
+ log: (line) => {
1316
+ console.log(line);
1317
+ },
1318
+ });
905
1319
  const found = (await pluginCommands(config)).find(({ command }) => command.words.every((word, index) => words[index] === word));
906
1320
  if (found === undefined) {
907
1321
  throw new Error(`Unknown command "${words[0] ?? ''}". Run geekity --help to see what is available.`);