@geekity/cms 0.3.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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +870 -0
  3. package/admin/layouts/_flash.njk +5 -0
  4. package/admin/layouts/base.njk +27 -0
  5. package/admin/layouts/dashboard.njk +78 -0
  6. package/admin/layouts/document-conflict.njk +49 -0
  7. package/admin/layouts/document-editor.njk +167 -0
  8. package/admin/layouts/document-list.njk +92 -0
  9. package/admin/layouts/federation.njk +145 -0
  10. package/admin/layouts/login.njk +23 -0
  11. package/admin/layouts/placeholder.njk +10 -0
  12. package/admin/layouts/settings.njk +116 -0
  13. package/admin/layouts/setup.njk +27 -0
  14. package/admin/layouts/shell.njk +37 -0
  15. package/admin/layouts/users.njk +91 -0
  16. package/admin/static/admin.css +726 -0
  17. package/admin/static/editor.js +30 -0
  18. package/dist/admin/assets.d.ts +26 -0
  19. package/dist/admin/assets.d.ts.map +1 -0
  20. package/dist/admin/assets.js +38 -0
  21. package/dist/admin/assets.js.map +1 -0
  22. package/dist/admin/credentials.d.ts +28 -0
  23. package/dist/admin/credentials.d.ts.map +1 -0
  24. package/dist/admin/credentials.js +39 -0
  25. package/dist/admin/credentials.js.map +1 -0
  26. package/dist/admin/documents.d.ts +134 -0
  27. package/dist/admin/documents.d.ts.map +1 -0
  28. package/dist/admin/documents.js +613 -0
  29. package/dist/admin/documents.js.map +1 -0
  30. package/dist/admin/federation.d.ts +194 -0
  31. package/dist/admin/federation.d.ts.map +1 -0
  32. package/dist/admin/federation.js +299 -0
  33. package/dist/admin/federation.js.map +1 -0
  34. package/dist/admin/flash.d.ts +24 -0
  35. package/dist/admin/flash.d.ts.map +1 -0
  36. package/dist/admin/flash.js +31 -0
  37. package/dist/admin/flash.js.map +1 -0
  38. package/dist/admin/index.d.ts +23 -0
  39. package/dist/admin/index.d.ts.map +1 -0
  40. package/dist/admin/index.js +15 -0
  41. package/dist/admin/index.js.map +1 -0
  42. package/dist/admin/passwords.d.ts +49 -0
  43. package/dist/admin/passwords.d.ts.map +1 -0
  44. package/dist/admin/passwords.js +122 -0
  45. package/dist/admin/passwords.js.map +1 -0
  46. package/dist/admin/preview.d.ts +18 -0
  47. package/dist/admin/preview.d.ts.map +1 -0
  48. package/dist/admin/preview.js +83 -0
  49. package/dist/admin/preview.js.map +1 -0
  50. package/dist/admin/routes.d.ts +54 -0
  51. package/dist/admin/routes.d.ts.map +1 -0
  52. package/dist/admin/routes.js +383 -0
  53. package/dist/admin/routes.js.map +1 -0
  54. package/dist/admin/session.d.ts +44 -0
  55. package/dist/admin/session.d.ts.map +1 -0
  56. package/dist/admin/session.js +69 -0
  57. package/dist/admin/session.js.map +1 -0
  58. package/dist/admin/settings.d.ts +194 -0
  59. package/dist/admin/settings.d.ts.map +1 -0
  60. package/dist/admin/settings.js +470 -0
  61. package/dist/admin/settings.js.map +1 -0
  62. package/dist/admin/store.d.ts +407 -0
  63. package/dist/admin/store.d.ts.map +1 -0
  64. package/dist/admin/store.js +704 -0
  65. package/dist/admin/store.js.map +1 -0
  66. package/dist/admin/templates.d.ts +36 -0
  67. package/dist/admin/templates.d.ts.map +1 -0
  68. package/dist/admin/templates.js +43 -0
  69. package/dist/admin/templates.js.map +1 -0
  70. package/dist/admin/uploads.d.ts +96 -0
  71. package/dist/admin/uploads.d.ts.map +1 -0
  72. package/dist/admin/uploads.js +179 -0
  73. package/dist/admin/uploads.js.map +1 -0
  74. package/dist/admin/users.d.ts +95 -0
  75. package/dist/admin/users.d.ts.map +1 -0
  76. package/dist/admin/users.js +233 -0
  77. package/dist/admin/users.js.map +1 -0
  78. package/dist/cli.d.ts +85 -0
  79. package/dist/cli.d.ts.map +1 -0
  80. package/dist/cli.js +424 -0
  81. package/dist/cli.js.map +1 -0
  82. package/dist/config.d.ts +172 -0
  83. package/dist/config.d.ts.map +1 -0
  84. package/dist/config.js +184 -0
  85. package/dist/config.js.map +1 -0
  86. package/dist/content/document.d.ts +66 -0
  87. package/dist/content/document.d.ts.map +1 -0
  88. package/dist/content/document.js +13 -0
  89. package/dist/content/document.js.map +1 -0
  90. package/dist/content/index.d.ts +17 -0
  91. package/dist/content/index.d.ts.map +1 -0
  92. package/dist/content/index.js +10 -0
  93. package/dist/content/index.js.map +1 -0
  94. package/dist/content/markdown.d.ts +3 -0
  95. package/dist/content/markdown.d.ts.map +1 -0
  96. package/dist/content/markdown.js +39 -0
  97. package/dist/content/markdown.js.map +1 -0
  98. package/dist/content/media.d.ts +65 -0
  99. package/dist/content/media.d.ts.map +1 -0
  100. package/dist/content/media.js +143 -0
  101. package/dist/content/media.js.map +1 -0
  102. package/dist/content/parser.d.ts +24 -0
  103. package/dist/content/parser.d.ts.map +1 -0
  104. package/dist/content/parser.js +165 -0
  105. package/dist/content/parser.js.map +1 -0
  106. package/dist/content/save.d.ts +60 -0
  107. package/dist/content/save.d.ts.map +1 -0
  108. package/dist/content/save.js +103 -0
  109. package/dist/content/save.js.map +1 -0
  110. package/dist/content/slug.d.ts +25 -0
  111. package/dist/content/slug.d.ts.map +1 -0
  112. package/dist/content/slug.js +75 -0
  113. package/dist/content/slug.js.map +1 -0
  114. package/dist/content/store.d.ts +133 -0
  115. package/dist/content/store.d.ts.map +1 -0
  116. package/dist/content/store.js +411 -0
  117. package/dist/content/store.js.map +1 -0
  118. package/dist/content/sync.d.ts +146 -0
  119. package/dist/content/sync.d.ts.map +1 -0
  120. package/dist/content/sync.js +421 -0
  121. package/dist/content/sync.js.map +1 -0
  122. package/dist/content/writer.d.ts +37 -0
  123. package/dist/content/writer.d.ts.map +1 -0
  124. package/dist/content/writer.js +97 -0
  125. package/dist/content/writer.js.map +1 -0
  126. package/dist/env.d.ts +48 -0
  127. package/dist/env.d.ts.map +1 -0
  128. package/dist/env.js +2 -0
  129. package/dist/env.js.map +1 -0
  130. package/dist/federation/actor.d.ts +55 -0
  131. package/dist/federation/actor.d.ts.map +1 -0
  132. package/dist/federation/actor.js +101 -0
  133. package/dist/federation/actor.js.map +1 -0
  134. package/dist/federation/article.d.ts +83 -0
  135. package/dist/federation/article.d.ts.map +1 -0
  136. package/dist/federation/article.js +169 -0
  137. package/dist/federation/article.js.map +1 -0
  138. package/dist/federation/delivery.d.ts +90 -0
  139. package/dist/federation/delivery.d.ts.map +1 -0
  140. package/dist/federation/delivery.js +289 -0
  141. package/dist/federation/delivery.js.map +1 -0
  142. package/dist/federation/federation.d.ts +75 -0
  143. package/dist/federation/federation.d.ts.map +1 -0
  144. package/dist/federation/federation.js +190 -0
  145. package/dist/federation/federation.js.map +1 -0
  146. package/dist/federation/followers.d.ts +43 -0
  147. package/dist/federation/followers.d.ts.map +1 -0
  148. package/dist/federation/followers.js +69 -0
  149. package/dist/federation/followers.js.map +1 -0
  150. package/dist/federation/inbox.d.ts +68 -0
  151. package/dist/federation/inbox.d.ts.map +1 -0
  152. package/dist/federation/inbox.js +171 -0
  153. package/dist/federation/inbox.js.map +1 -0
  154. package/dist/federation/index.d.ts +14 -0
  155. package/dist/federation/index.d.ts.map +1 -0
  156. package/dist/federation/index.js +10 -0
  157. package/dist/federation/index.js.map +1 -0
  158. package/dist/federation/keys.d.ts +27 -0
  159. package/dist/federation/keys.d.ts.map +1 -0
  160. package/dist/federation/keys.js +60 -0
  161. package/dist/federation/keys.js.map +1 -0
  162. package/dist/federation/mount.d.ts +23 -0
  163. package/dist/federation/mount.d.ts.map +1 -0
  164. package/dist/federation/mount.js +71 -0
  165. package/dist/federation/mount.js.map +1 -0
  166. package/dist/federation/paths.d.ts +85 -0
  167. package/dist/federation/paths.d.ts.map +1 -0
  168. package/dist/federation/paths.js +97 -0
  169. package/dist/federation/paths.js.map +1 -0
  170. package/dist/index.d.ts +106 -0
  171. package/dist/index.d.ts.map +1 -0
  172. package/dist/index.js +148 -0
  173. package/dist/index.js.map +1 -0
  174. package/dist/init.d.ts +71 -0
  175. package/dist/init.d.ts.map +1 -0
  176. package/dist/init.js +125 -0
  177. package/dist/init.js.map +1 -0
  178. package/dist/web/assets.d.ts +65 -0
  179. package/dist/web/assets.d.ts.map +1 -0
  180. package/dist/web/assets.js +152 -0
  181. package/dist/web/assets.js.map +1 -0
  182. package/dist/web/context.d.ts +121 -0
  183. package/dist/web/context.d.ts.map +1 -0
  184. package/dist/web/context.js +112 -0
  185. package/dist/web/context.js.map +1 -0
  186. package/dist/web/documents.d.ts +28 -0
  187. package/dist/web/documents.d.ts.map +1 -0
  188. package/dist/web/documents.js +39 -0
  189. package/dist/web/documents.js.map +1 -0
  190. package/dist/web/feeds.d.ts +122 -0
  191. package/dist/web/feeds.d.ts.map +1 -0
  192. package/dist/web/feeds.js +229 -0
  193. package/dist/web/feeds.js.map +1 -0
  194. package/dist/web/index.d.ts +17 -0
  195. package/dist/web/index.d.ts.map +1 -0
  196. package/dist/web/index.js +10 -0
  197. package/dist/web/index.js.map +1 -0
  198. package/dist/web/negotiate.d.ts +219 -0
  199. package/dist/web/negotiate.d.ts.map +1 -0
  200. package/dist/web/negotiate.js +409 -0
  201. package/dist/web/negotiate.js.map +1 -0
  202. package/dist/web/pagination.d.ts +48 -0
  203. package/dist/web/pagination.d.ts.map +1 -0
  204. package/dist/web/pagination.js +30 -0
  205. package/dist/web/pagination.js.map +1 -0
  206. package/dist/web/render.d.ts +72 -0
  207. package/dist/web/render.d.ts.map +1 -0
  208. package/dist/web/render.js +73 -0
  209. package/dist/web/render.js.map +1 -0
  210. package/dist/web/routes.d.ts +31 -0
  211. package/dist/web/routes.d.ts.map +1 -0
  212. package/dist/web/routes.js +366 -0
  213. package/dist/web/routes.js.map +1 -0
  214. package/dist/web/templates.d.ts +34 -0
  215. package/dist/web/templates.d.ts.map +1 -0
  216. package/dist/web/templates.js +106 -0
  217. package/dist/web/templates.js.map +1 -0
  218. package/package.json +68 -0
  219. package/templates/site/content/_data/site.json +8 -0
  220. package/templates/site/content/pages/about.md +11 -0
  221. package/templates/site/content/pages/pages.json +3 -0
  222. package/templates/site/content/posts/2026-01-01-hello-world.md +34 -0
  223. package/templates/site/content/posts/posts.json +4 -0
  224. package/templates/site/geekity.config.ts +11 -0
  225. package/templates/site/gitignore +3 -0
  226. package/templates/site/server.ts +20 -0
  227. package/templates/site/tsconfig.json +18 -0
  228. package/themes/default/README.md +148 -0
  229. package/themes/default/layouts/404.njk +9 -0
  230. package/themes/default/layouts/base.njk +59 -0
  231. package/themes/default/layouts/home.njk +9 -0
  232. package/themes/default/layouts/page.njk +13 -0
  233. package/themes/default/layouts/post.njk +30 -0
  234. package/themes/default/layouts/tag.njk +19 -0
  235. package/themes/default/partials/pagination.njk +14 -0
  236. package/themes/default/partials/post-list.njk +24 -0
  237. package/themes/default/partials/tags.njk +12 -0
  238. package/themes/default/static/style.css +327 -0
@@ -0,0 +1,233 @@
1
+ import { randomInt } from 'node:crypto';
2
+ import { passwordProblem, usernameProblem } from './credentials.js';
3
+ import { flash } from './flash.js';
4
+ import { ADMIN_PREFIX } from './session.js';
5
+ import { DuplicateUsernameError } from './store.js';
6
+ import { ADMIN_TEMPLATES } from './templates.js';
7
+ /** Where the users screen lives. The add form posts here too. */
8
+ export const USERS_PATH = `${ADMIN_PREFIX}/users`;
9
+ /** Where the signed-in admin's change-password form posts. */
10
+ export const CHANGE_PASSWORD_PATH = `${USERS_PATH}/password`;
11
+ /** Where a row's delete button posts. */
12
+ export const DELETE_USER_PATH = `${USERS_PATH}/delete`;
13
+ /** The fields the three forms on the screen submit. */
14
+ export const USER_FIELDS = {
15
+ username: 'username',
16
+ password: 'password',
17
+ generate: 'generate',
18
+ currentPassword: 'current_password',
19
+ newPassword: 'new_password',
20
+ newPasswordConfirmation: 'new_password_confirmation',
21
+ userId: 'user_id',
22
+ };
23
+ /**
24
+ * Register the users screen: the list, the add form, and the change-password
25
+ * form for whoever is signed in.
26
+ *
27
+ * There is one role, so there is nothing to edit about somebody else: an admin
28
+ * can add a user, delete one, and change their own password. Changing
29
+ * somebody else's is deliberately not offered — resetting a colleague's
30
+ * password from under them is what `geekity user add` and a fresh account are
31
+ * for, and it keeps the current-password check honest.
32
+ */
33
+ export function mountUsers(app, options) {
34
+ const { render } = options;
35
+ app.get(USERS_PATH, (c) => render(c, ADMIN_TEMPLATES.users, screen(c)));
36
+ app.post(USERS_PATH, async (c) => {
37
+ const body = await c.req.parseBody();
38
+ const username = field(body[USER_FIELDS.username]).trim();
39
+ const supplied = field(body[USER_FIELDS.password]);
40
+ const generate = field(body[USER_FIELDS.generate]) !== '';
41
+ const password = generate ? generatePassword() : supplied;
42
+ const problems = addUserProblems(username, password, generate);
43
+ if (Object.keys(problems).length === 0) {
44
+ try {
45
+ c.var.admin.createUser({ username, password });
46
+ }
47
+ catch (error) {
48
+ if (!(error instanceof DuplicateUsernameError))
49
+ throw error;
50
+ problems.username = error.message;
51
+ }
52
+ }
53
+ if (Object.keys(problems).length > 0) {
54
+ c.status(400);
55
+ return render(c, ADMIN_TEMPLATES.users, screen(c, { addForm: { username, generate }, addProblems: problems }));
56
+ }
57
+ // A generated password is shown once, here, and never stored in the clear.
58
+ // The flash lives on the session row, so it is not in the URL, in a cookie
59
+ // or in anybody's history.
60
+ flash(c, 'notice', generate
61
+ ? `Added ${username}. Their password is ${password} — copy it now, it is not shown again.`
62
+ : `Added ${username}.`);
63
+ return c.redirect(USERS_PATH, 303);
64
+ });
65
+ app.post(CHANGE_PASSWORD_PATH, async (c) => {
66
+ // The guard has already established that there is a live session with a
67
+ // user behind it, so both of these are present on every path that gets here.
68
+ const session = c.var.session;
69
+ const user = session?.userId === null || session === undefined
70
+ ? undefined
71
+ : c.var.admin.getUserById(session.userId);
72
+ if (session === undefined || user === undefined)
73
+ return c.redirect(USERS_PATH, 303);
74
+ const body = await c.req.parseBody();
75
+ const current = field(body[USER_FIELDS.currentPassword]);
76
+ const next = field(body[USER_FIELDS.newPassword]);
77
+ const confirmation = field(body[USER_FIELDS.newPasswordConfirmation]);
78
+ const problems = changePasswordProblems({
79
+ correct: c.var.admin.verifyPassword(user.username, current) !== undefined,
80
+ next,
81
+ confirmation,
82
+ });
83
+ if (Object.keys(problems).length > 0) {
84
+ c.status(400);
85
+ // Nothing that was typed is echoed back: every field on this form is a
86
+ // password, and a password does not belong in rendered HTML.
87
+ return render(c, ADMIN_TEMPLATES.users, screen(c, { passwordProblems: problems }));
88
+ }
89
+ c.var.admin.setPassword(user.id, next);
90
+ // Every browser that was signed in as this user is signed out, because the
91
+ // usual reason to change a password is that somebody else may have it. The
92
+ // one doing the changing is spared, so the admin is not thrown out of the
93
+ // form they just submitted.
94
+ const ended = c.var.admin.deleteSessionsForUser(user.id, { except: session.id });
95
+ flash(c, 'notice', ended === 0
96
+ ? 'Your password was changed.'
97
+ : `Your password was changed, and ${signedOut(ended)} signed out.`);
98
+ return c.redirect(USERS_PATH, 303);
99
+ });
100
+ app.post(DELETE_USER_PATH, async (c) => {
101
+ const admin = c.var.admin;
102
+ const body = await c.req.parseBody();
103
+ const id = Number(field(body[USER_FIELDS.userId]));
104
+ const target = Number.isInteger(id) ? admin.getUserById(id) : undefined;
105
+ const refusal = deleteUserRefusal({
106
+ target,
107
+ signedInAs: c.var.session?.userId ?? null,
108
+ total: admin.countUsers(),
109
+ });
110
+ if (refusal !== undefined) {
111
+ flash(c, 'error', refusal);
112
+ return c.redirect(USERS_PATH, 303);
113
+ }
114
+ // Their sessions go with them: the foreign key cascades, and the
115
+ // connection runs with `PRAGMA foreign_keys = ON`.
116
+ admin.deleteUser(target.id);
117
+ flash(c, 'notice', `Deleted ${target.username}.`);
118
+ return c.redirect(USERS_PATH, 303);
119
+ });
120
+ }
121
+ /**
122
+ * Delete a user.
123
+ *
124
+ * Two refusals, in this order. The last remaining user cannot go, because a
125
+ * site with no users falls back into first-run setup and anybody who reaches
126
+ * it becomes its admin. And nobody may delete their own account: it would end
127
+ * the session doing the deleting, and there is no undo. Both are a flash and a
128
+ * redirect rather than a 400, because nothing was typed to send back.
129
+ */
130
+ export function deleteUserRefusal(input) {
131
+ if (input.target === undefined)
132
+ return 'That user is already gone.';
133
+ if (input.total <= 1) {
134
+ return `${input.target.username} is the only user. Add another before deleting this one.`;
135
+ }
136
+ if (input.target.id === input.signedInAs) {
137
+ return 'You cannot delete your own account. Another admin can do it for you.';
138
+ }
139
+ return undefined;
140
+ }
141
+ /**
142
+ * What is wrong with a proposed password change, one message per field.
143
+ *
144
+ * The current password is taken as already checked, rather than checked here,
145
+ * so this stays a pure function over what the store said.
146
+ */
147
+ export function changePasswordProblems(input) {
148
+ const problems = {};
149
+ if (!input.correct)
150
+ problems.currentPassword = 'That is not your current password.';
151
+ const secret = passwordProblem(input.next);
152
+ if (secret !== undefined)
153
+ problems.newPassword = secret;
154
+ else if (input.next !== input.confirmation) {
155
+ problems.newPassword = 'The two new passwords do not match.';
156
+ }
157
+ return problems;
158
+ }
159
+ /** "1 other session was" or "3 other sessions were", for the flash. */
160
+ function signedOut(count) {
161
+ return count === 1 ? '1 other session was' : `${String(count)} other sessions were`;
162
+ }
163
+ /**
164
+ * What is wrong with a proposed user, one message per field.
165
+ *
166
+ * The rules are the shared ones, so an account made here is one
167
+ * `/admin/setup` and `geekity user add` would both have accepted. A generated
168
+ * password is not checked against them — it is made to satisfy them — and the
169
+ * password field is not read at all when the box is ticked, so a stale value
170
+ * left in it cannot be what is refused.
171
+ */
172
+ export function addUserProblems(username, password, generate = false) {
173
+ const problems = {};
174
+ const name = usernameProblem(username);
175
+ if (name !== undefined)
176
+ problems.username = name;
177
+ if (!generate) {
178
+ const secret = passwordProblem(password);
179
+ if (secret !== undefined)
180
+ problems.password = secret;
181
+ }
182
+ return problems;
183
+ }
184
+ /**
185
+ * The alphabet a generated password is drawn from: no `0`/`O` and no `1`/`l`,
186
+ * because a generated password is read off a screen and typed somewhere else.
187
+ */
188
+ export const GENERATED_PASSWORD_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
189
+ /** How many characters a generated password has. 20 is about 116 bits here. */
190
+ export const GENERATED_PASSWORD_LENGTH = 20;
191
+ /** A password nobody chose, from `randomInt` rather than `Math.random`. */
192
+ export function generatePassword() {
193
+ let password = '';
194
+ for (let index = 0; index < GENERATED_PASSWORD_LENGTH; index += 1) {
195
+ password += GENERATED_PASSWORD_ALPHABET[randomInt(GENERATED_PASSWORD_ALPHABET.length)];
196
+ }
197
+ return password;
198
+ }
199
+ /** Everything the users template renders. */
200
+ function screen(c, extra = {}) {
201
+ const signedInAs = c.var.session?.userId ?? null;
202
+ const users = c.var.admin.listUsers();
203
+ return {
204
+ section: 'users',
205
+ usersUrl: USERS_PATH,
206
+ changePasswordUrl: CHANGE_PASSWORD_PATH,
207
+ deleteUserUrl: DELETE_USER_PATH,
208
+ fields: USER_FIELDS,
209
+ users: users.map((user) => row(user, { signedInAs, total: users.length })),
210
+ addForm: { username: '', generate: false },
211
+ addProblems: {},
212
+ passwordProblems: {},
213
+ ...extra,
214
+ };
215
+ }
216
+ /** A form field as a string. A file upload, or a missing field, is the empty one. */
217
+ function field(value) {
218
+ return typeof value === 'string' ? value : '';
219
+ }
220
+ /** One user as the table renders it. */
221
+ function row(user, context) {
222
+ const you = user.id === context.signedInAs;
223
+ return {
224
+ id: user.id,
225
+ username: user.username,
226
+ createdAt: user.createdAt,
227
+ you,
228
+ // The button is rendered for everybody the signed-in admin may actually
229
+ // delete, so the screen never offers an action it is about to refuse.
230
+ deletable: !you && context.total > 1,
231
+ };
232
+ }
233
+ //# sourceMappingURL=users.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/admin/users.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ import type { GeekityConfig } from './config.ts';
3
+ export type Command = 'serve' | 'init' | 'sync' | 'user' | 'help' | 'version';
4
+ export interface ParsedArgs {
5
+ command: Command;
6
+ /** Path to the config file, relative to the working directory, if the user named one. */
7
+ configPath: string | undefined;
8
+ /**
9
+ * The password `user add` was given on the command line, if any. `undefined`
10
+ * means "ask"; the empty string means the user passed `--password=` and is
11
+ * refused by the password rules rather than by the parser.
12
+ */
13
+ password: string | undefined;
14
+ /**
15
+ * Positional arguments after the command: the directory for `init`, the
16
+ * subcommand and its arguments for `user`. Flags are never in here.
17
+ */
18
+ args: readonly string[];
19
+ }
20
+ /** Turn `process.argv.slice(2)` into a command, its options and its arguments. */
21
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs;
22
+ /**
23
+ * Load a config module, returning `{}` when the site has no config file.
24
+ *
25
+ * A TypeScript config is imported directly first. That is all it takes on Node
26
+ * 24, which strips types without a flag, and inside this repository, where
27
+ * the CLI already runs under tsx. A Node started with stripping disabled — and
28
+ * any file carrying TypeScript syntax that stripping cannot erase — falls back to
29
+ * registering tsx from the *site's* own dependencies, which is where a site
30
+ * scaffolded by `geekity init` has it. A site with neither writes
31
+ * `geekity.config.js` instead; it is in the candidate list for exactly that.
32
+ */
33
+ export declare function loadConfig(cwd: string, configPath: string | undefined): Promise<GeekityConfig>;
34
+ /**
35
+ * The site's own tsx, as the directory the package was installed into, or
36
+ * `undefined` when the site does not have one.
37
+ *
38
+ * Node's resolver is deliberately not asked: this has to be the tsx the *site*
39
+ * installed, and a resolver anchored anywhere else — or patched by a tsx that
40
+ * is already loaded, which is what happens inside this repository — would
41
+ * happily answer with somebody else's copy.
42
+ */
43
+ export declare function findSiteTsx(cwd: string): string | undefined;
44
+ /**
45
+ * Teach this process to import TypeScript, using the tsx installed alongside
46
+ * the site.
47
+ *
48
+ * tsx is the site's rather than this package's because the CMS does not depend
49
+ * on it at runtime: a site that runs `server.ts` already has it, and a site
50
+ * that does not want it writes its config in JavaScript instead.
51
+ */
52
+ export declare function registerTypeScriptLoader(cwd: string, configFile: string): Promise<void>;
53
+ /** A terminal, as much of one as {@link readPassword} needs. */
54
+ type PasswordInput = NodeJS.ReadableStream & {
55
+ isTTY?: boolean | undefined;
56
+ isRaw?: boolean | undefined;
57
+ setRawMode?: (mode: boolean) => unknown;
58
+ };
59
+ /** Where {@link readPassword} reads from, writes to, and what it says. */
60
+ export interface ReadPasswordOptions {
61
+ /** Stream to read the password from. Defaults to standard input. */
62
+ input?: PasswordInput;
63
+ /** Stream the prompt goes to. Defaults to standard error, so `>` keeps it out of a file. */
64
+ output?: NodeJS.WritableStream;
65
+ /** The prompt, shown only on a terminal. */
66
+ prompt?: string;
67
+ }
68
+ /**
69
+ * Ask for a password once, without putting it on the screen.
70
+ *
71
+ * Two things would otherwise echo it, and both are turned off: the terminal
72
+ * driver, silenced by putting the input into raw mode, and readline, whose
73
+ * output goes to a writable that keeps nothing. Readline happens to set raw
74
+ * mode itself, but it is set here as well rather than relied on: the day that
75
+ * changes, a password would be printed. Raw mode also means readline sees
76
+ * Ctrl-C as a byte rather than a signal, so it is turned back into a
77
+ * cancellation here.
78
+ *
79
+ * Off a terminal — a pipe, a heredoc, a CI job — nothing is printed and the
80
+ * first line is the password, which is what makes
81
+ * `printf '%s\n' "$PW" | geekity user add ada` work.
82
+ */
83
+ export declare function readPassword(options?: ReadPasswordOptions): Promise<string>;
84
+ export {};
85
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAK9E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,yFAAyF;IACzF,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B;;;OAGG;IACH,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB;AAkCD,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAiE7D;AASD;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,aAAa,CAAC,CAyBxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW3D;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe7F;AA8KD,gEAAgE;AAChE,KAAK,aAAa,GAAG,MAAM,CAAC,cAAc,GAAG;IAC3C,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;CACzC,CAAC;AAEF,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4CrF"}