@geekity/cms 0.24.0 → 0.26.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 (182) hide show
  1. package/README.md +96 -160
  2. package/admin/pages/documents/editor.njk +48 -0
  3. package/admin/pages/federation/settings.njk +1 -59
  4. package/admin/pages/plugins/installed.njk +113 -0
  5. package/admin/pages/plugins/screen.njk +129 -0
  6. package/admin/static/admin.css +144 -4
  7. package/admin/static/editor-actions.js +202 -0
  8. package/dist/admin/accounts.d.ts +18 -39
  9. package/dist/admin/accounts.d.ts.map +1 -1
  10. package/dist/admin/accounts.js +19 -48
  11. package/dist/admin/accounts.js.map +1 -1
  12. package/dist/admin/documents.d.ts +5 -0
  13. package/dist/admin/documents.d.ts.map +1 -1
  14. package/dist/admin/documents.js +17 -8
  15. package/dist/admin/documents.js.map +1 -1
  16. package/dist/admin/editor-actions.d.ts +35 -0
  17. package/dist/admin/editor-actions.d.ts.map +1 -0
  18. package/dist/admin/editor-actions.js +187 -0
  19. package/dist/admin/editor-actions.js.map +1 -0
  20. package/dist/admin/federation.d.ts +4 -5
  21. package/dist/admin/federation.d.ts.map +1 -1
  22. package/dist/admin/federation.js +4 -5
  23. package/dist/admin/federation.js.map +1 -1
  24. package/dist/admin/index.d.ts +1 -1
  25. package/dist/admin/index.d.ts.map +1 -1
  26. package/dist/admin/index.js +1 -1
  27. package/dist/admin/index.js.map +1 -1
  28. package/dist/admin/menu.d.ts +1 -1
  29. package/dist/admin/menu.d.ts.map +1 -1
  30. package/dist/admin/menu.js +10 -3
  31. package/dist/admin/menu.js.map +1 -1
  32. package/dist/admin/plugins.d.ts +24 -0
  33. package/dist/admin/plugins.d.ts.map +1 -0
  34. package/dist/admin/plugins.js +312 -0
  35. package/dist/admin/plugins.js.map +1 -0
  36. package/dist/admin/routes.d.ts.map +1 -1
  37. package/dist/admin/routes.js +5 -1
  38. package/dist/admin/routes.js.map +1 -1
  39. package/dist/admin/settings-federation.d.ts +0 -25
  40. package/dist/admin/settings-federation.d.ts.map +1 -1
  41. package/dist/admin/settings-federation.js +1 -54
  42. package/dist/admin/settings-federation.js.map +1 -1
  43. package/dist/admin/settings.d.ts +2 -14
  44. package/dist/admin/settings.d.ts.map +1 -1
  45. package/dist/admin/settings.js +1 -21
  46. package/dist/admin/settings.js.map +1 -1
  47. package/dist/admin/store.d.ts +8 -1
  48. package/dist/admin/store.d.ts.map +1 -1
  49. package/dist/admin/store.js +7 -3
  50. package/dist/admin/store.js.map +1 -1
  51. package/dist/admin/templates.d.ts +2 -0
  52. package/dist/admin/templates.d.ts.map +1 -1
  53. package/dist/admin/templates.js +2 -0
  54. package/dist/admin/templates.js.map +1 -1
  55. package/dist/cli.d.ts +2 -3
  56. package/dist/cli.d.ts.map +1 -1
  57. package/dist/cli.js +278 -212
  58. package/dist/cli.js.map +1 -1
  59. package/dist/config.d.ts +20 -0
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +6 -0
  62. package/dist/config.js.map +1 -1
  63. package/dist/content/store.d.ts +6 -0
  64. package/dist/content/store.d.ts.map +1 -1
  65. package/dist/content/store.js +3 -0
  66. package/dist/content/store.js.map +1 -1
  67. package/dist/drain.d.ts +28 -0
  68. package/dist/drain.d.ts.map +1 -0
  69. package/dist/drain.js +106 -0
  70. package/dist/drain.js.map +1 -0
  71. package/dist/env.d.ts +11 -0
  72. package/dist/env.d.ts.map +1 -1
  73. package/dist/federation/federation.d.ts +4 -2
  74. package/dist/federation/federation.d.ts.map +1 -1
  75. package/dist/federation/federation.js +16 -21
  76. package/dist/federation/federation.js.map +1 -1
  77. package/dist/federation/inbox.d.ts +23 -3
  78. package/dist/federation/inbox.d.ts.map +1 -1
  79. package/dist/federation/inbox.js +63 -1
  80. package/dist/federation/inbox.js.map +1 -1
  81. package/dist/federation/index.d.ts +0 -4
  82. package/dist/federation/index.d.ts.map +1 -1
  83. package/dist/federation/index.js +0 -2
  84. package/dist/federation/index.js.map +1 -1
  85. package/dist/federation/mount.d.ts +6 -12
  86. package/dist/federation/mount.d.ts.map +1 -1
  87. package/dist/federation/mount.js +3 -50
  88. package/dist/federation/mount.js.map +1 -1
  89. package/dist/federation/queue.d.ts +22 -0
  90. package/dist/federation/queue.d.ts.map +1 -0
  91. package/dist/federation/queue.js +58 -0
  92. package/dist/federation/queue.js.map +1 -0
  93. package/dist/files/atomic.d.ts +13 -0
  94. package/dist/files/atomic.d.ts.map +1 -1
  95. package/dist/files/atomic.js +45 -0
  96. package/dist/files/atomic.js.map +1 -1
  97. package/dist/index.d.ts +30 -3
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +156 -79
  100. package/dist/index.js.map +1 -1
  101. package/dist/plugin.d.ts +524 -0
  102. package/dist/plugin.d.ts.map +1 -0
  103. package/dist/plugin.js +23 -0
  104. package/dist/plugin.js.map +1 -0
  105. package/dist/plugins/enabled.d.ts +23 -0
  106. package/dist/plugins/enabled.d.ts.map +1 -0
  107. package/dist/plugins/enabled.js +49 -0
  108. package/dist/plugins/enabled.js.map +1 -0
  109. package/dist/plugins/federation.d.ts +26 -0
  110. package/dist/plugins/federation.d.ts.map +1 -0
  111. package/dist/plugins/federation.js +114 -0
  112. package/dist/plugins/federation.js.map +1 -0
  113. package/dist/plugins/fetch.d.ts +15 -0
  114. package/dist/plugins/fetch.d.ts.map +1 -0
  115. package/dist/plugins/fetch.js +33 -0
  116. package/dist/plugins/fetch.js.map +1 -0
  117. package/dist/plugins/folder.d.ts +50 -0
  118. package/dist/plugins/folder.d.ts.map +1 -0
  119. package/dist/plugins/folder.js +171 -0
  120. package/dist/plugins/folder.js.map +1 -0
  121. package/dist/plugins/install.d.ts +45 -0
  122. package/dist/plugins/install.d.ts.map +1 -0
  123. package/dist/plugins/install.js +275 -0
  124. package/dist/plugins/install.js.map +1 -0
  125. package/dist/plugins/mount.d.ts +17 -0
  126. package/dist/plugins/mount.d.ts.map +1 -0
  127. package/dist/plugins/mount.js +44 -0
  128. package/dist/plugins/mount.js.map +1 -0
  129. package/dist/plugins/registry.d.ts +110 -0
  130. package/dist/plugins/registry.d.ts.map +1 -0
  131. package/dist/plugins/registry.js +447 -0
  132. package/dist/plugins/registry.js.map +1 -0
  133. package/dist/plugins/settings.d.ts +64 -0
  134. package/dist/plugins/settings.d.ts.map +1 -0
  135. package/dist/plugins/settings.js +219 -0
  136. package/dist/plugins/settings.js.map +1 -0
  137. package/dist/plugins/site.d.ts +17 -0
  138. package/dist/plugins/site.d.ts.map +1 -0
  139. package/dist/plugins/site.js +67 -0
  140. package/dist/plugins/site.js.map +1 -0
  141. package/dist/supervisor/primary.d.ts +47 -0
  142. package/dist/supervisor/primary.d.ts.map +1 -0
  143. package/dist/supervisor/primary.js +208 -0
  144. package/dist/supervisor/primary.js.map +1 -0
  145. package/dist/supervisor/protocol.d.ts +37 -0
  146. package/dist/supervisor/protocol.d.ts.map +1 -0
  147. package/dist/supervisor/protocol.js +29 -0
  148. package/dist/supervisor/protocol.js.map +1 -0
  149. package/dist/supervisor/supervision.d.ts +25 -0
  150. package/dist/supervisor/supervision.d.ts.map +1 -0
  151. package/dist/supervisor/supervision.js +7 -0
  152. package/dist/supervisor/supervision.js.map +1 -0
  153. package/dist/supervisor/worker.d.ts +29 -0
  154. package/dist/supervisor/worker.d.ts.map +1 -0
  155. package/dist/supervisor/worker.js +97 -0
  156. package/dist/supervisor/worker.js.map +1 -0
  157. package/dist/web/maintenance.d.ts +2 -2
  158. package/dist/web/maintenance.js +2 -2
  159. package/dist/web/negotiate.d.ts +4 -3
  160. package/dist/web/negotiate.d.ts.map +1 -1
  161. package/dist/web/negotiate.js +4 -3
  162. package/dist/web/negotiate.js.map +1 -1
  163. package/dist/web/routes.d.ts.map +1 -1
  164. package/dist/web/routes.js +33 -14
  165. package/dist/web/routes.js.map +1 -1
  166. package/package.json +10 -1
  167. package/dist/comments/conversation.d.ts +0 -29
  168. package/dist/comments/conversation.d.ts.map +0 -1
  169. package/dist/comments/conversation.js +0 -60
  170. package/dist/comments/conversation.js.map +0 -1
  171. package/dist/federation/import-wordpress.d.ts +0 -128
  172. package/dist/federation/import-wordpress.d.ts.map +0 -1
  173. package/dist/federation/import-wordpress.js +0 -426
  174. package/dist/federation/import-wordpress.js.map +0 -1
  175. package/dist/federation/wordpress.d.ts +0 -131
  176. package/dist/federation/wordpress.d.ts.map +0 -1
  177. package/dist/federation/wordpress.js +0 -342
  178. package/dist/federation/wordpress.js.map +0 -1
  179. package/dist/web/comments.d.ts +0 -60
  180. package/dist/web/comments.d.ts.map +0 -1
  181. package/dist/web/comments.js +0 -207
  182. package/dist/web/comments.js.map +0 -1
@@ -0,0 +1,219 @@
1
+ /**
2
+ * A plugin's settings (decision-33): the fields it declares, where each value
3
+ * lives, and what a save writes.
4
+ *
5
+ * Public values sit under the plugin's key in `content/_data/site.json`,
6
+ * beside `enabled`. Secrets sit in `secrets.json` in the plugin's data folder,
7
+ * mode 0600, and an environment variable named by the package wins over the
8
+ * file, so a Docker operator can keep a key in `.env`. Every value is read
9
+ * from the files and the environment when it is asked for, so a save or a
10
+ * hand edit takes effect on the next request.
11
+ */
12
+ import path from 'node:path';
13
+ import { readFileIfPresentSync, updateFileAtomically } from '../files/atomic.js';
14
+ import { readPluginEntry, updatePluginEntry } from './enabled.js';
15
+ export const SECRETS_FILE = 'secrets.json';
16
+ /** `data/plugins/<package name>/`. */
17
+ export function pluginFolderPath(dataDir, name) {
18
+ return path.join(dataDir, 'plugins', ...name.split('/'));
19
+ }
20
+ function upperSnake(text) {
21
+ return text
22
+ .toUpperCase()
23
+ .replace(/[^A-Z0-9]+/g, '_')
24
+ .replace(/^_+|_+$/g, '');
25
+ }
26
+ /**
27
+ * What every environment variable of a package starts with, before `__`:
28
+ * `GEEKITY_PLUGIN_LLM` for `@geekity/plugin-llm`. It never contains `__`, so
29
+ * no plugin variable can shadow a core one.
30
+ */
31
+ export function pluginEnvPrefix(name) {
32
+ return upperSnake(name);
33
+ }
34
+ /** The environment variable that sets one setting, such as `GEEKITY_PLUGIN_LLM__API_KEY`. */
35
+ export function pluginVariable(name, key) {
36
+ return `${pluginEnvPrefix(name)}__${upperSnake(key)}`;
37
+ }
38
+ const SETTING_KEY = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;
39
+ /** Why these fields cannot be stored, or `undefined` when they can. */
40
+ export function settingFieldsProblem(fields) {
41
+ const seen = new Set();
42
+ for (const field of fields) {
43
+ const key = JSON.stringify(field.key);
44
+ if (!SETTING_KEY.test(field.key)) {
45
+ return `The setting ${key} is not lower case words joined by "_".`;
46
+ }
47
+ if (field.key === 'enabled')
48
+ return `The setting ${key} is the key that enables the plugin.`;
49
+ if (seen.has(field.key))
50
+ return `The setting ${key} is declared twice.`;
51
+ seen.add(field.key);
52
+ if (field.type === 'select' &&
53
+ !field.options.some((option) => option.value === field.default)) {
54
+ return `The setting ${key} has a default that is not one of its options.`;
55
+ }
56
+ if (field.type === 'url' && field.default !== undefined && !isWebUrl(field.default)) {
57
+ return `The setting ${key} has a default that is not an http or https URL.`;
58
+ }
59
+ }
60
+ return undefined;
61
+ }
62
+ /** Every field with its value now, in the order declared. */
63
+ export function resolvePluginSettings(place) {
64
+ const stored = readPluginEntry(place.contentDir, place.name);
65
+ const secrets = readSecrets(place.dataDir, place.name);
66
+ return place.fields.map((field) => {
67
+ if (field.type === 'secret') {
68
+ const variable = pluginVariable(place.name, field.key);
69
+ const fromEnv = place.env[variable];
70
+ if (fromEnv !== undefined && fromEnv !== '') {
71
+ return { field, value: fromEnv, source: 'environment', problem: undefined, variable };
72
+ }
73
+ const fromFile = secrets[field.key];
74
+ return typeof fromFile === 'string' && fromFile !== ''
75
+ ? { field, value: fromFile, source: 'file', problem: undefined, variable }
76
+ : { field, value: undefined, source: 'default', problem: undefined, variable };
77
+ }
78
+ const fallback = { field, value: defaultOf(field), variable: undefined };
79
+ const raw = stored[field.key];
80
+ if (raw === undefined)
81
+ return { ...fallback, source: 'default', problem: undefined };
82
+ if (accepts(field, raw)) {
83
+ return { field, value: raw, source: 'site', problem: undefined, variable: undefined };
84
+ }
85
+ return {
86
+ ...fallback,
87
+ source: 'default',
88
+ problem: `The value in site.json is not ${EXPECTED[field.type]}, so the default is in use.`,
89
+ };
90
+ });
91
+ }
92
+ /** The values a plugin is handed, keyed by field key. */
93
+ export function settingValues(resolved) {
94
+ return Object.fromEntries(resolved.map((entry) => [entry.field.key, entry.value]));
95
+ }
96
+ /**
97
+ * Save a submitted form. A field with a value it does not accept refuses the
98
+ * whole save, named in `problems`, and nothing is written. An empty public
99
+ * box removes the key, so the default applies; an empty secret box keeps the
100
+ * stored secret; a secret the environment sets is never written.
101
+ */
102
+ export async function savePluginSettings(options) {
103
+ const { fields, values, forget } = options;
104
+ const problems = {};
105
+ for (const field of fields) {
106
+ const submitted = values[field.key]?.trim();
107
+ if (submitted === undefined || submitted === '')
108
+ continue;
109
+ if (field.type === 'url' && !isWebUrl(submitted)) {
110
+ problems[field.key] = `${field.label} must be an http:// or https:// URL.`;
111
+ }
112
+ if (field.type === 'select' && !field.options.some((option) => option.value === submitted)) {
113
+ problems[field.key] = `${field.label} must be one of its choices.`;
114
+ }
115
+ }
116
+ if (Object.keys(problems).length > 0)
117
+ return { problems };
118
+ const publicFields = fields.filter((field) => field.type !== 'secret');
119
+ if (publicFields.length > 0) {
120
+ await updatePluginEntry(options.contentDir, options.name, (entry) => {
121
+ const next = { ...entry };
122
+ for (const field of publicFields) {
123
+ const submitted = values[field.key];
124
+ if (field.type === 'checkbox') {
125
+ next[field.key] = submitted !== undefined;
126
+ continue;
127
+ }
128
+ if (submitted === undefined)
129
+ continue;
130
+ const trimmed = submitted.trim();
131
+ if (trimmed === '')
132
+ delete next[field.key];
133
+ else
134
+ next[field.key] = trimmed;
135
+ }
136
+ return next;
137
+ });
138
+ }
139
+ const changes = new Map();
140
+ for (const field of fields) {
141
+ if (field.type !== 'secret')
142
+ continue;
143
+ if (options.env[pluginVariable(options.name, field.key)])
144
+ continue;
145
+ const submitted = values[field.key]?.trim() ?? '';
146
+ if (forget.has(field.key))
147
+ changes.set(field.key, undefined);
148
+ else if (submitted !== '')
149
+ changes.set(field.key, submitted);
150
+ }
151
+ if (changes.size > 0) {
152
+ await updateFileAtomically(secretsPath(options.dataDir, options.name), (current) => {
153
+ const next = parseRecord(current);
154
+ for (const [key, value] of changes) {
155
+ if (value === undefined)
156
+ delete next[key];
157
+ else
158
+ next[key] = value;
159
+ }
160
+ return `${JSON.stringify(next, null, 2)}\n`;
161
+ }, { mode: 0o600 });
162
+ }
163
+ return { problems };
164
+ }
165
+ const EXPECTED = {
166
+ text: 'text',
167
+ url: 'an http or https URL',
168
+ select: 'one of its choices',
169
+ checkbox: 'true or false',
170
+ };
171
+ function defaultOf(field) {
172
+ switch (field.type) {
173
+ case 'checkbox':
174
+ return field.default ?? false;
175
+ case 'select':
176
+ return field.default;
177
+ case 'text':
178
+ case 'url':
179
+ return field.default ?? '';
180
+ }
181
+ }
182
+ function accepts(field, raw) {
183
+ switch (field.type) {
184
+ case 'checkbox':
185
+ return typeof raw === 'boolean';
186
+ case 'select':
187
+ return field.options.some((option) => option.value === raw);
188
+ case 'text':
189
+ return typeof raw === 'string';
190
+ case 'url':
191
+ return typeof raw === 'string' && isWebUrl(raw);
192
+ }
193
+ }
194
+ function isWebUrl(text) {
195
+ if (!URL.canParse(text))
196
+ return false;
197
+ const { protocol } = new URL(text);
198
+ return protocol === 'http:' || protocol === 'https:';
199
+ }
200
+ function secretsPath(dataDir, name) {
201
+ return path.join(pluginFolderPath(dataDir, name), SECRETS_FILE);
202
+ }
203
+ function readSecrets(dataDir, name) {
204
+ return parseRecord(readFileIfPresentSync(secretsPath(dataDir, name)));
205
+ }
206
+ function parseRecord(text) {
207
+ if (text === undefined)
208
+ return {};
209
+ try {
210
+ const parsed = JSON.parse(text);
211
+ return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
212
+ ? parsed
213
+ : {};
214
+ }
215
+ catch {
216
+ return {};
217
+ }
218
+ }
219
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/plugins/settings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The site as a plugin reaches it (decision-33): the accounts, keys and
3
+ * followers behind {@link PluginSite}, and the registry a site's config
4
+ * installs. Shared by requests and the command line, so a plugin's command
5
+ * and its federation read the same files the same way.
6
+ */
7
+ import type { AdminStore } from '../admin/store.ts';
8
+ import type { ResolvedConfig } from '../config.ts';
9
+ import type { PluginSite } from '../plugin.ts';
10
+ import type { InstalledPlugin, PluginRegistry } from './registry.ts';
11
+ /** Every plugin the site config installs, and those from the plugins folder, registered. */
12
+ export declare function sitePluginRegistry(config: ResolvedConfig, folderPlugins?: readonly InstalledPlugin[]): PluginRegistry;
13
+ export declare function pluginSite(options: {
14
+ admin: AdminStore;
15
+ config: ResolvedConfig;
16
+ }): PluginSite;
17
+ //# sourceMappingURL=site.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../../src/plugins/site.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAInD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAErE,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,EACtB,aAAa,GAAE,SAAS,eAAe,EAAO,GAC7C,cAAc,CAqBhB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,GAAG,UAAU,CA2C7F"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The site as a plugin reaches it (decision-33): the accounts, keys and
3
+ * followers behind {@link PluginSite}, and the registry a site's config
4
+ * installs. Shared by requests and the command line, so a plugin's command
5
+ * and its federation read the same files the same way.
6
+ */
7
+ import { listUsers, setUserActorId } from '../admin/accounts.js';
8
+ import { readSiteSettings } from '../admin/settings.js';
9
+ import { readFileIfPresentSync } from '../files/atomic.js';
10
+ import { actorKeyFile, loadActorKeyPairs, writeActorKeyFile } from '../federation/keys.js';
11
+ import { addFollower, readFollowers } from '../federation/records.js';
12
+ import { createPluginRegistry } from './registry.js';
13
+ /** Every plugin the site config installs, and those from the plugins folder, registered. */
14
+ export function sitePluginRegistry(config, folderPlugins = []) {
15
+ return createPluginRegistry([
16
+ ...config.plugins.map((plugin, index) => ({
17
+ plugin,
18
+ source: `the site config, plugins[${String(index)}]`,
19
+ })),
20
+ ...folderPlugins,
21
+ ], {
22
+ dataDir: config.dataDir,
23
+ contentDir: config.contentDir,
24
+ env: process.env,
25
+ allowPrivateAddress: config.federation.allowPrivateAddress ?? false,
26
+ lookup: config.hostLookup,
27
+ siteInfo: () => ({
28
+ baseUrl: config.baseUrl,
29
+ title: readSiteSettings(config.contentDir).title,
30
+ }),
31
+ });
32
+ }
33
+ export function pluginSite(options) {
34
+ const { admin, config } = options;
35
+ const { contentDir, dataDir } = config;
36
+ return {
37
+ baseUrl: config.baseUrl,
38
+ users: () => listUsers(dataDir).map((user) => ({
39
+ username: user.username,
40
+ ...(user.actorId === undefined ? {} : { actorId: user.actorId }),
41
+ })),
42
+ setActorId: (username, actorId) => setUserActorId({ dataDir, username, actorId }),
43
+ actorKey(username, algorithm) {
44
+ const file = actorKeyFile(dataDir, username, algorithm);
45
+ return { file, jwk: readFileIfPresentSync(file) };
46
+ },
47
+ writeActorKey(username, algorithm, jwk) {
48
+ writeActorKeyFile(dataDir, username, algorithm, jwk);
49
+ },
50
+ async loadActorKeys(username) {
51
+ await loadActorKeyPairs(dataDir, username);
52
+ },
53
+ followers: (username) => readFollowers(contentDir, username).map((follower) => ({
54
+ actorId: follower.actorId,
55
+ inboxId: follower.inboxId,
56
+ sharedInboxId: follower.sharedInboxId,
57
+ handle: follower.handle,
58
+ name: follower.name,
59
+ iconUrl: follower.iconUrl,
60
+ url: follower.url,
61
+ })),
62
+ async addFollower(username, follower) {
63
+ await addFollower({ admin, contentDir }, username, follower);
64
+ },
65
+ };
66
+ }
67
+ //# sourceMappingURL=site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site.js","sourceRoot":"","sources":["../../src/plugins/site.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `geekity serve`'s supervisor (TASK-288, decision-33): the primary process
3
+ * that owns the port and runs the CMS in one worker, replacing it on a reload
4
+ * and respawning it when it crashes.
5
+ *
6
+ * The port stays open only while some worker listens on it, so a reload
7
+ * starts the new worker before it retires the old one. And only one worker
8
+ * writes at a time: the old one drains (refuses writes, stops its timers,
9
+ * empties its queues, goes read-only) before the new one is forked, so the
10
+ * new worker's boot migrations run alone.
11
+ *
12
+ * This module is the state machine alone. The workers, the clock and the
13
+ * process are handed in, so the order of a reload is tested without
14
+ * starting a process; {@link superviseCluster} binds it to `node:cluster`.
15
+ */
16
+ import type { SupervisorMessage, WorkerMessage } from './protocol.ts';
17
+ /** A running worker, as the supervisor reaches it. */
18
+ export interface WorkerHandle {
19
+ readonly id: number;
20
+ send(message: SupervisorMessage): void;
21
+ kill(): void;
22
+ }
23
+ export interface Cancel {
24
+ cancel(): void;
25
+ }
26
+ export interface SupervisorOptions {
27
+ fork(): WorkerHandle;
28
+ schedule(task: () => void, ms: number): Cancel;
29
+ now(): number;
30
+ log(line: string): void;
31
+ exit(code: number): void;
32
+ }
33
+ export interface Supervisor {
34
+ start(): void;
35
+ message(worker: WorkerHandle, message: WorkerMessage): void;
36
+ exited(worker: WorkerHandle, code: number | null): void;
37
+ /** SIGTERM or SIGINT: shut every worker down, then exit. */
38
+ stop(): void;
39
+ }
40
+ export declare function createSupervisor(options: SupervisorOptions): Supervisor;
41
+ /**
42
+ * Run the supervisor over `node:cluster` workers, each of which runs this
43
+ * same command. SIGTERM and SIGINT reach the supervisor, which is the process
44
+ * an image's init signals.
45
+ */
46
+ export declare function superviseCluster(): void;
47
+ //# sourceMappingURL=primary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primary.d.ts","sourceRoot":"","sources":["../../src/supervisor/primary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEtE,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACvC,IAAI,IAAI,IAAI,CAAC;CACd;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,IAAI,YAAY,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,GAAG,IAAI,MAAM,CAAC;IACd,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5D,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACxD,4DAA4D;IAC5D,IAAI,IAAI,IAAI,CAAC;CACd;AA6BD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAiJvE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAkCvC"}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * `geekity serve`'s supervisor (TASK-288, decision-33): the primary process
3
+ * that owns the port and runs the CMS in one worker, replacing it on a reload
4
+ * and respawning it when it crashes.
5
+ *
6
+ * The port stays open only while some worker listens on it, so a reload
7
+ * starts the new worker before it retires the old one. And only one worker
8
+ * writes at a time: the old one drains (refuses writes, stops its timers,
9
+ * empties its queues, goes read-only) before the new one is forked, so the
10
+ * new worker's boot migrations run alone.
11
+ *
12
+ * This module is the state machine alone. The workers, the clock and the
13
+ * process are handed in, so the order of a reload is tested without
14
+ * starting a process; {@link superviseCluster} binds it to `node:cluster`.
15
+ */
16
+ import cluster from 'node:cluster';
17
+ import { isWorkerMessage } from './protocol.js';
18
+ /** The first respawn waits this long, doubling with each crash after it. */
19
+ const RESPAWN_BASE_MS = 500;
20
+ const RESPAWN_MAX_MS = 30_000;
21
+ /** A worker that served this long resets the backoff. */
22
+ const STABLE_MS = 60_000;
23
+ /** How long a new worker may take to boot during a reload: a large site's scan, at most. */
24
+ const RELOAD_BOOT_MS = 5 * 60_000;
25
+ /** How long the workers may take to close on a signal before they are killed. */
26
+ const SHUTDOWN_MS = 60_000;
27
+ export function createSupervisor(options) {
28
+ const live = new Set();
29
+ const bootErrors = new Map();
30
+ let state = { phase: 'stopping' };
31
+ let everServed = false;
32
+ let crashes = 0;
33
+ function fork() {
34
+ const worker = options.fork();
35
+ live.add(worker);
36
+ return worker;
37
+ }
38
+ function respawnLater(reason) {
39
+ const delay = Math.min(RESPAWN_BASE_MS * 2 ** crashes, RESPAWN_MAX_MS);
40
+ crashes += 1;
41
+ options.log(`${reason}; starting another in ${(delay / 1000).toFixed(1)} s.`);
42
+ state = {
43
+ phase: 'waiting',
44
+ timer: options.schedule(() => {
45
+ state = { phase: 'booting', worker: fork() };
46
+ }, delay),
47
+ };
48
+ }
49
+ /** The new worker did not boot: the old one carries on, and is told why. */
50
+ function keepOld(old, next, error) {
51
+ if (state.phase === 'replacing')
52
+ state.deadline.cancel();
53
+ options.log(`Reload failed, so the running server carries on: ${error}`);
54
+ old.send({ type: 'resume', error });
55
+ if (live.has(next))
56
+ next.kill();
57
+ state = { phase: 'serving', worker: old, since: options.now() };
58
+ }
59
+ function bootError(worker, code) {
60
+ return (bootErrors.get(worker) ??
61
+ `The new server exited${code === null ? '' : ` with code ${String(code)}`} before it was ready.`);
62
+ }
63
+ return {
64
+ start() {
65
+ state = { phase: 'booting', worker: fork() };
66
+ },
67
+ message(worker, message) {
68
+ switch (message.type) {
69
+ case 'ready':
70
+ if (state.phase === 'booting' && state.worker === worker) {
71
+ everServed = true;
72
+ state = { phase: 'serving', worker, since: options.now() };
73
+ }
74
+ else if (state.phase === 'replacing' && state.next === worker) {
75
+ state.deadline.cancel();
76
+ state.old.send({ type: 'retire' });
77
+ options.log('Reloaded: the new server is serving and the old one is retiring.');
78
+ state = { phase: 'serving', worker, since: options.now() };
79
+ }
80
+ return;
81
+ case 'boot-failed':
82
+ bootErrors.set(worker, message.error);
83
+ if (state.phase === 'replacing' && state.next === worker) {
84
+ keepOld(state.old, worker, message.error);
85
+ }
86
+ return;
87
+ case 'reload':
88
+ if (state.phase === 'serving' && state.worker === worker) {
89
+ options.log('Reloading: the running server is draining.');
90
+ worker.send({ type: 'drain' });
91
+ state = { phase: 'draining', old: worker };
92
+ }
93
+ return;
94
+ case 'drained':
95
+ if (state.phase === 'draining' && state.old === worker) {
96
+ const next = fork();
97
+ const deadline = options.schedule(() => {
98
+ keepOld(worker, next, 'The new server did not start in time.');
99
+ }, RELOAD_BOOT_MS);
100
+ state = { phase: 'replacing', old: worker, next, deadline };
101
+ }
102
+ return;
103
+ }
104
+ },
105
+ exited(worker, code) {
106
+ live.delete(worker);
107
+ const error = bootError(worker, code);
108
+ bootErrors.delete(worker);
109
+ switch (state.phase) {
110
+ case 'stopping':
111
+ if (live.size === 0)
112
+ options.exit(0);
113
+ return;
114
+ case 'booting':
115
+ if (state.worker !== worker)
116
+ return;
117
+ if (!everServed) {
118
+ options.log(error);
119
+ options.exit(1);
120
+ return;
121
+ }
122
+ respawnLater(`The server did not start: ${error}`);
123
+ return;
124
+ case 'serving':
125
+ if (state.worker !== worker)
126
+ return;
127
+ if (options.now() - state.since >= STABLE_MS)
128
+ crashes = 0;
129
+ respawnLater(`The server exited${code === null ? '' : ` with code ${String(code)}`}`);
130
+ return;
131
+ case 'draining':
132
+ if (state.old === worker)
133
+ respawnLater('The server exited while it drained');
134
+ return;
135
+ case 'replacing':
136
+ if (state.next === worker) {
137
+ keepOld(state.old, worker, error);
138
+ }
139
+ else if (state.old === worker) {
140
+ state.deadline.cancel();
141
+ state = { phase: 'booting', worker: state.next };
142
+ }
143
+ return;
144
+ case 'waiting':
145
+ return;
146
+ }
147
+ },
148
+ stop() {
149
+ if (state.phase === 'waiting')
150
+ state.timer.cancel();
151
+ if (state.phase === 'replacing')
152
+ state.deadline.cancel();
153
+ state = { phase: 'stopping' };
154
+ if (live.size === 0) {
155
+ options.exit(0);
156
+ return;
157
+ }
158
+ for (const worker of live)
159
+ worker.send({ type: 'shutdown' });
160
+ options.schedule(() => {
161
+ for (const worker of live)
162
+ worker.kill();
163
+ }, SHUTDOWN_MS);
164
+ },
165
+ };
166
+ }
167
+ /**
168
+ * Run the supervisor over `node:cluster` workers, each of which runs this
169
+ * same command. SIGTERM and SIGINT reach the supervisor, which is the process
170
+ * an image's init signals.
171
+ */
172
+ export function superviseCluster() {
173
+ const supervisor = createSupervisor({
174
+ fork() {
175
+ const worker = cluster.fork();
176
+ const handle = {
177
+ id: worker.id,
178
+ send(message) {
179
+ if (worker.isConnected())
180
+ worker.send(message);
181
+ },
182
+ kill() {
183
+ worker.process.kill('SIGKILL');
184
+ },
185
+ };
186
+ worker.on('message', (message) => {
187
+ if (isWorkerMessage(message))
188
+ supervisor.message(handle, message);
189
+ });
190
+ worker.on('exit', (code) => {
191
+ supervisor.exited(handle, code);
192
+ });
193
+ return handle;
194
+ },
195
+ schedule(task, ms) {
196
+ const timer = setTimeout(task, ms);
197
+ return { cancel: () => clearTimeout(timer) };
198
+ },
199
+ now: () => Date.now(),
200
+ log: (line) => process.stderr.write(`${line}\n`),
201
+ exit: (code) => process.exit(code),
202
+ });
203
+ for (const signal of ['SIGINT', 'SIGTERM']) {
204
+ process.on(signal, () => supervisor.stop());
205
+ }
206
+ supervisor.start();
207
+ }
208
+ //# sourceMappingURL=primary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primary.js","sourceRoot":"","sources":["../../src/supervisor/primary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The messages between `geekity serve`'s supervisor and the worker that runs
3
+ * the CMS (TASK-288). A reload is: the serving worker asks (`reload`); the
4
+ * supervisor tells it to `drain`; it answers `drained` once it writes
5
+ * nothing more; a new worker boots and says `ready`; the old one is told to
6
+ * `retire`. A new worker that does not boot says `boot-failed`, and the old
7
+ * one is told to `resume`, with the reason.
8
+ */
9
+ /**
10
+ * How a worker that cannot carry on gives up: it exits with this code, and the
11
+ * supervisor boots a replacement, as it does for any serving or draining
12
+ * worker that exits without being told to.
13
+ */
14
+ export declare const EXIT_FOR_REPLACEMENT = 1;
15
+ export type WorkerMessage = {
16
+ readonly type: 'ready';
17
+ } | {
18
+ readonly type: 'boot-failed';
19
+ readonly error: string;
20
+ } | {
21
+ readonly type: 'reload';
22
+ } | {
23
+ readonly type: 'drained';
24
+ };
25
+ export type SupervisorMessage = {
26
+ readonly type: 'drain';
27
+ } | {
28
+ readonly type: 'resume';
29
+ readonly error: string;
30
+ } | {
31
+ readonly type: 'retire';
32
+ } | {
33
+ readonly type: 'shutdown';
34
+ };
35
+ export declare function isWorkerMessage(value: unknown): value is WorkerMessage;
36
+ export declare function isSupervisorMessage(value: unknown): value is SupervisorMessage;
37
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/supervisor/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEjC,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC;AAKlC,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAEtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E"}