@tiddlywiki/mws 0.0.12 → 0.0.14

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 (66) hide show
  1. package/.env-cmdrc +5 -0
  2. package/README.md +3 -1
  3. package/dist/server.d.ts +923 -0
  4. package/dist/server.js +1763 -1415
  5. package/dist/server.js.map +1 -1
  6. package/editions/mws-docs/tiddlers/$__StoryList.tid +3 -3
  7. package/editions/mws-docs/tiddlers/MWS and SQLite.md +11 -0
  8. package/editions/mws-docs/tiddlers/Request Server.md +20 -0
  9. package/editions/mws-docs/tiddlers/Request Server.md.meta +5 -0
  10. package/package.json +17 -10
  11. package/prisma/schema.prisma +1 -0
  12. package/react-user-mgmt/package-lock.json +76 -321
  13. package/react-user-mgmt/package.json +2 -5
  14. package/react-user-mgmt/public/chunk-YALWN5OT.js +2 -0
  15. package/react-user-mgmt/public/chunk-YALWN5OT.js.map +7 -0
  16. package/react-user-mgmt/public/esm-QKDFEP3B.js +3 -0
  17. package/react-user-mgmt/public/esm-QKDFEP3B.js.map +7 -0
  18. package/react-user-mgmt/public/index.html +1 -26
  19. package/react-user-mgmt/public/main.js +68 -109
  20. package/react-user-mgmt/public/main.js.map +4 -4
  21. package/react-user-mgmt/public/stats.css +597 -0
  22. package/react-user-mgmt/public/stats.html +59 -0
  23. package/react-user-mgmt/public/stats.js +2013 -0
  24. package/react-user-mgmt/public/stats.json +1 -0
  25. package/react-user-mgmt/public/stats.svg +4 -0
  26. package/react-user-mgmt/tsconfig.json +3 -0
  27. package/dist/server.cjs +0 -16677
  28. package/dist/server.cjs.map +0 -1
  29. package/src/StateObject.ts +0 -316
  30. package/src/commander.ts +0 -417
  31. package/src/commands/divider.ts +0 -20
  32. package/src/commands/index.ts +0 -24
  33. package/src/commands/mws-init-store.ts +0 -77
  34. package/src/commands/mws-listen.ts +0 -136
  35. package/src/commands/mws-load-archive.ts +0 -281
  36. package/src/commands/mws-load-plugin-bags.ts +0 -151
  37. package/src/commands/mws-load-tiddlers.ts +0 -37
  38. package/src/commands/mws-load-wiki-folder.ts +0 -124
  39. package/src/commands/mws-render-tiddlywiki5.ts +0 -55
  40. package/src/commands/mws-save-archive.ts +0 -111
  41. package/src/commands/mws-save-tiddler-text.ts +0 -33
  42. package/src/commands/tests-complete.ts +0 -21
  43. package/src/global.ts +0 -247
  44. package/src/routes/TiddlerStore.ts +0 -722
  45. package/src/routes/bag-file-server.ts +0 -340
  46. package/src/routes/cache.ts +0 -25
  47. package/src/routes/index.ts +0 -138
  48. package/src/routes/managers/index.ts +0 -17
  49. package/src/routes/managers/manager-recipes.ts +0 -438
  50. package/src/routes/managers/manager-users.ts +0 -201
  51. package/src/routes/managers/router-tiddlers.ts +0 -313
  52. package/src/routes/router.ts +0 -429
  53. package/src/routes/services/PasswordService.ts +0 -178
  54. package/src/routes/services/attachments.ts +0 -259
  55. package/src/routes/services/sessions.ts +0 -166
  56. package/src/routes/tw-test.ts +0 -65
  57. package/src/server.ts +0 -151
  58. package/src/setupDevServer.ts +0 -71
  59. package/src/streamer.ts +0 -430
  60. package/src/tiddlywiki.ts +0 -94
  61. package/src/utils/data-checks.ts +0 -172
  62. package/src/utils/index.ts +0 -4
  63. package/src/utils/rootRoute.ts +0 -237
  64. package/src/utils/utils.ts +0 -403
  65. package/src/utils/zodAssert.ts +0 -269
  66. /package/prisma/{migrations/notes.md → notes.md} +0 -0
package/src/commander.ts DELETED
@@ -1,417 +0,0 @@
1
-
2
- import { SiteConfig } from "./routes/router";
3
- import * as path from "node:path";
4
-
5
- import { MWSConfig } from "./server";
6
- import { Prisma, PrismaClient } from "@prisma/client";
7
- import { createClient } from "@libsql/client";
8
- import { PrismaLibSQL } from "@prisma/adapter-libsql";
9
- import * as sessions from "./routes/services/sessions";
10
- import * as attacher from "./routes/services/attachments";
11
- import { PasswordService } from "./routes/services/PasswordService";
12
- import { existsSync, mkdirSync, readFileSync } from "node:fs";
13
- import { ITXClientDenyList } from "@prisma/client/runtime/library";
14
- import { TiddlerFields, TW } from "tiddlywiki";
15
- import { dist_resolve } from "./utils";
16
- import { readdir, readFile } from "node:fs/promises";
17
- import { createHash, randomUUID } from "node:crypto";
18
-
19
- import { commands, mws_listen, divider } from "./commands";
20
-
21
- export interface $TW {
22
- utils: any;
23
- wiki: any;
24
- config: any;
25
- boot: any;
26
- loadTiddlersFromPath: any;
27
- Tiddler: {
28
- // TW5-typed for some reason just types this as a record of modules, which is incorrect
29
- fieldModules: Record<string, {
30
- name: string;
31
- parse?: (value: any) => any;
32
- stringify?: (value: any) => string;
33
- }>;
34
- }
35
- }
36
-
37
- export interface CommandInfo {
38
- name: string;
39
- /**
40
- * @default false
41
- *
42
- * @description
43
- *
44
- * - Regardless of the value of `synchronous`, the execute method is always awaited.
45
- * - Let `result` be any value thrown by the constructor or returned or thrown by the execute method.
46
- * ```
47
- * synchronous: false
48
- * ```
49
- * - `result` is awaited (if result is not then-able, this has no effect).
50
- * - If `result` is truthy, the command is failed without waiting for the callback.
51
- * - If `result` is falsy, the command waits for the callback result.
52
- * - The callback value is NOT awaited.
53
- * - if the callback value is truthy, the command is failed.
54
- * - if the callback value is falsy, the command is completed.
55
- * ```
56
- * synchronous: true
57
- * ```
58
- * - `result` is awaited (if result is not then-able, this has no effect).
59
- * - If `result` is truthy, the command is failed.
60
- * - If `result` is falsy, the command is completed.
61
- *
62
- * - The callback is not available if `synchronous` is true.
63
- *
64
- */
65
- synchronous?: boolean;
66
- namedParameterMode?: boolean;
67
- mandatoryParameters?: string[];
68
- }
69
-
70
- // move the startup logic into a separate class
71
- class StartupCommander {
72
-
73
- constructor(
74
- public config: MWSConfig,
75
- public $tw: TW,
76
- public PasswordService: PasswordService,
77
- ) {
78
-
79
- // console.log(config.wikiPath);
80
- // this is already resolved to the cwd.
81
- this.wikiPath = config.wikiPath;
82
- this.storePath = path.resolve(this.wikiPath, "store");
83
- this.databasePath = path.resolve(this.storePath, "database.sqlite");
84
- this.outputPath = path.resolve($tw.boot.wikiPath, $tw.config.wikiOutputSubDir);
85
- this.cachePath = path.resolve(this.wikiPath, "cache");
86
-
87
- if (!existsSync(this.storePath)) {
88
- mkdirSync(this.storePath, { recursive: true });
89
- this.setupRequired = true;
90
- }
91
-
92
- // using the libsql adapter because for some reason prisma was
93
- // freezing when loading the system bag favicons.
94
- // the libsql adapter has an additional advantage of letting us specify pragma
95
- // and also gives us more control over connections.
96
-
97
- this.libsql = createClient({ url: "file:" + this.databasePath });
98
- // console.log(this.libsql.protocol, this.databasePath);
99
- // this.libsql.execute("pragma synchronous=off");
100
- this.engine = new PrismaClient({
101
- log: ["info", "warn"],
102
- adapter: new PrismaLibSQL(this.libsql),
103
- // datasourceUrl: "file:" + this.databasePath
104
- });
105
-
106
- this.siteConfig = {
107
- wikiPath: this.wikiPath,
108
- allowAnonReads: !!config.config?.allowAnonReads,
109
- allowAnonWrites: !!config.config?.allowAnonWrites,
110
- allowUnreadableBags: !!config.config?.allowUnreadableBags,
111
- attachmentsEnabled: !!config.config?.saveLargeTextToFileSystem,
112
- attachmentSizeLimit: config.config?.saveLargeTextToFileSystem ?? 0,
113
- enableBrowserCache: !!config.config?.enableBrowserCache,
114
- enableGzip: !!config.config?.enableGzip,
115
- contentTypeInfo: $tw.config.contentTypeInfo,
116
- storePath: this.storePath,
117
- saveLargeTextToFileSystem: undefined as never
118
- };
119
-
120
- this.SessionManager = config.SessionManager || sessions.SessionManager;
121
- this.AttachmentService = config.AttachmentService || attacher.AttachmentService;
122
-
123
- }
124
-
125
- async init() {
126
- this.setupRequired = false;
127
-
128
- if (process.env.RUN_OLD_MWS_DB_SETUP_FOR_TESTING) {
129
- await this.libsql.executeMultiple(readFileSync(dist_resolve(
130
- "../prisma/migrations/20250406213424_init/migration.sql"
131
- ), "utf8"));
132
- }
133
-
134
- const tables = await this.libsql.batch([{
135
- sql: `SELECT tbl_name FROM sqlite_master WHERE type='table'`, args: [],
136
- }]).then(e => e[0]?.rows as { tbl_name: string }[] | undefined);
137
-
138
- const hasExisting = !!tables?.length;
139
-
140
-
141
- const hasMigrationsTable = !!tables?.length && !!tables?.some((e) => e.tbl_name === "_prisma_migrations");
142
- if (!hasMigrationsTable) await this.createMigrationsTable();
143
- await this.checkMigrationsTable(hasExisting && !hasMigrationsTable);
144
-
145
-
146
- const users = await this.engine.users.count();
147
- if (!users) { this.setupRequired = true; }
148
- }
149
- async createMigrationsTable() {
150
- await this.libsql.execute({
151
- sql:
152
- 'CREATE TABLE "_prisma_migrations" (\n' +
153
- ' "id" TEXT PRIMARY KEY NOT NULL,\n' +
154
- ' "checksum" TEXT NOT NULL,\n' +
155
- ' "finished_at" DATETIME,\n' +
156
- ' "migration_name" TEXT NOT NULL,\n' +
157
- ' "logs" TEXT,\n' +
158
- ' "rolled_back_at" DATETIME,\n' +
159
- ' "started_at" DATETIME NOT NULL DEFAULT current_timestamp,\n' +
160
- ' "applied_steps_count" INTEGER UNSIGNED NOT NULL DEFAULT 0\n' +
161
- ')',
162
- args: [],
163
- })
164
- }
165
- async checkMigrationsTable(migrateExisting: boolean) {
166
-
167
- const applied_migrations = new Set(
168
- await this.libsql.batch([{
169
- sql: `Select migration_name from _prisma_migrations`, args: []
170
- }]).then(e => e[0]?.rows.map(e => e.migration_name))
171
- );
172
-
173
- const migrations = await readdir(dist_resolve("../prisma/migrations"));
174
- migrations.sort();
175
-
176
- const new_migrations = migrations.filter(m => !applied_migrations.has(m) && m !== "migration_lock.toml");
177
- if (!new_migrations.length) return;
178
-
179
- function generateChecksum(fileContent: string) {
180
- return createHash('sha256').update(fileContent).digest('hex');
181
- }
182
-
183
- console.log("New migrations found", new_migrations);
184
-
185
- for (const migration of new_migrations) {
186
- const migration_path = dist_resolve(`../prisma/migrations/${migration}/migration.sql`);
187
- if (!existsSync(migration_path)) continue;
188
-
189
- const fileContent = await readFile(migration_path, 'utf-8');
190
- // this is the hard-coded name of the first migration.
191
- if (migrateExisting && migration === "20250406213424_init") {
192
- console.log("Existing migration", migration, "is already applied");
193
- } else {
194
- console.log("Applying migration", migration);
195
- await this.libsql.executeMultiple(fileContent);
196
- }
197
-
198
- await this.libsql.execute({
199
- sql: 'INSERT INTO _prisma_migrations (' +
200
- 'id, migration_name, checksum, finished_at, logs, rolled_back_at, started_at, applied_steps_count' +
201
- ') VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
202
- args: [randomUUID(), migration, generateChecksum(fileContent), Date.now(), null, null, Date.now(), 1]
203
- });
204
-
205
- }
206
- console.log("Migrations applied", new_migrations);
207
-
208
- }
209
- wikiPath: string;
210
- storePath: string;
211
- databasePath: string;
212
- cachePath: string;
213
-
214
- libsql;
215
- engine: PrismaClient<Prisma.PrismaClientOptions, never, {
216
- result: {
217
- // this types every output field with PrismaField
218
- [T in Uncapitalize<Prisma.ModelName>]: {
219
- [K in keyof PrismaPayloadScalars<Capitalize<T>>]: () => {
220
- compute: () => PrismaField<Capitalize<T>, K>
221
- }
222
- }
223
- },
224
- client: {},
225
- model: {},
226
- query: {},
227
- }>;
228
-
229
- $transaction<R>(
230
- fn: (prisma: Omit<Commander["engine"], ITXClientDenyList>) => Promise<R>,
231
- options?: {
232
- maxWait?: number,
233
- timeout?: number,
234
- isolationLevel?: Prisma.TransactionIsolationLevel
235
- }
236
- ): Promise<R> {
237
- // $transaction doesn't have the client extensions types,
238
- // but should have them available (were they not just types).
239
- return this.engine.$transaction(fn as (prisma: any) => Promise<any>, options);
240
- }
241
-
242
-
243
- siteConfig: SiteConfig;
244
-
245
-
246
- SessionManager: typeof sessions.SessionManager;
247
- AttachmentService: typeof attacher.AttachmentService;
248
-
249
-
250
-
251
- /** Signals that database setup is required. May be set to false by any qualified setup command. */
252
- setupRequired: boolean = true;
253
- outputPath: string;
254
- }
255
- /**
256
- Parse a sequence of commands
257
- commandTokens: an array of command string tokens
258
- wiki: reference to the wiki store object
259
- streams: {output:, error:}, each of which has a write(string) method
260
- callback: a callback invoked as callback(err) where err is null if there was no error
261
- */
262
- export class Commander extends StartupCommander {
263
-
264
- constructor(
265
- config: MWSConfig,
266
- $tw: TW,
267
- PasswordService: PasswordService,
268
- ) {
269
- super(config, $tw, PasswordService);
270
- this.nextToken = 0;
271
- this.verbose = false;
272
-
273
- const { listeners, onListenersCreated } = config;
274
- config.listeners = [];
275
- config.onListenersCreated = undefined;
276
- // there's nothing that can't be hacked in a Node process,
277
- // but this just makes it a little bit harder for the listeners to be read.
278
- // this can be replaced, but it only recieves the listeners via closure.
279
- this.create_mws_listen = (params: string[]) => {
280
- return new mws_listen.Command(params, this, listeners, onListenersCreated);
281
- }
282
- }
283
-
284
- create_mws_listen;
285
- /**
286
- * The promise of the current execution.
287
- * It is created and returned by execute,
288
- * but also accessible here.
289
- *
290
- * After the promise is resolved, it will
291
- * be available until execute is called again.
292
- */
293
- promise!: Promise<void>;
294
- private callback!: (err?: any) => void;
295
-
296
-
297
- commandTokens!: string[];
298
- nextToken
299
- verbose
300
-
301
- static initCommands(moduleType?: string) {
302
- if (moduleType) throw new Error("moduleType is not implemented");
303
- }
304
- /*
305
- Add a string of tokens to the command queue
306
- */
307
- addCommandTokens(params: string[]) {
308
- if (this.verbose) console.log("Commander addCommandTokens", params);
309
- this.commandTokens.splice(this.nextToken, 0, ...params);
310
- }
311
- /*
312
- Execute the sequence of commands and invoke a callback on completion
313
- */
314
- execute(commandTokens: string[]) {
315
- console.log("Commander", commandTokens);
316
- this.commandTokens = commandTokens;
317
- this.promise = new Promise<void>((resolve, reject) => {
318
- this.callback = (err: any) => {
319
- if (err) this.$tw.utils.error("Error: " + err);
320
- err ? reject(err) : resolve();
321
- };
322
- });
323
- this.executeNextCommand();
324
- return this.promise;
325
- }
326
- /*
327
- Execute the next command in the sequence
328
- */
329
- executeNextCommand() {
330
-
331
- // Invoke the callback if there are no more commands
332
- if (this.nextToken >= this.commandTokens.length) {
333
- this.callback(null);
334
- return;
335
- }
336
-
337
- // Get and check the command token
338
- let commandName = this.commandTokens[this.nextToken++] as string;
339
- if (commandName.slice(0, 2) !== "--") {
340
- console.log(this.commandTokens);
341
- this.callback("Missing command: " + commandName);
342
- return;
343
- }
344
-
345
- commandName = commandName.slice(2); // Trim off the --
346
-
347
- // Get the command info
348
- var command = commands[commandName], c, err;
349
- if (!command) {
350
- this.callback("Unknown command: " + commandName);
351
- return;
352
- }
353
-
354
- // Accumulate the parameters to the command
355
- const nextCommand = this.commandTokens.findIndex((token, i) => i >= this.nextToken && token.startsWith("--"));
356
- const params = this.commandTokens.slice(this.nextToken, nextCommand === -1 ? undefined : nextCommand);
357
- this.nextToken = nextCommand === -1 ? this.commandTokens.length : nextCommand;
358
-
359
- if (commandName !== divider.info.name)
360
- console.log("Commander executing", commandName, params);
361
-
362
-
363
- // Parse named parameters if required
364
- const paramsIfMandetory = !command.info.mandatoryParameters ? params :
365
- this.extractNamedParameters(params, command.info.mandatoryParameters);
366
- if (typeof params === "string") { this.callback(params); return; }
367
-
368
- new Promise<any>(async (resolve) => {
369
- const { Command, info } = command!;
370
- try {
371
- c = info.name === "mws-listen"
372
- ? this.create_mws_listen(paramsIfMandetory)
373
- : new Command(paramsIfMandetory, this, info.synchronous ? undefined : resolve);
374
- err = await c.execute();
375
- if (err || info.synchronous) resolve(err);
376
- } catch (e) {
377
- resolve(e);
378
- }
379
- }).then((err: any) => {
380
- if (err) {
381
- console.log(err);
382
- this.callback(err);
383
- } else {
384
- this.executeNextCommand();
385
- }
386
- });
387
-
388
- }
389
- /*
390
- Given an array of parameter strings `params` in name=value format, and an array of mandatory parameter names in `mandatoryParameters`, returns a hashmap of values or a string if error
391
- */
392
- extractNamedParameters(params: string[], mandatoryParameters?: string[]) {
393
- mandatoryParameters = mandatoryParameters || [];
394
- var errors: any[] = [], paramsByName = Object.create(null);
395
- // Extract the parameters
396
- each(params, function (param: string) {
397
- var index = param.indexOf("=");
398
- if (index < 1) {
399
- errors.push("malformed named parameter: '" + param + "'");
400
- }
401
- paramsByName[param.slice(0, index)] = param.slice(index + 1).trim();
402
- });
403
- // Check the mandatory parameters are present
404
- each(mandatoryParameters, function (mandatoryParameter: string) {
405
- if (!hop(paramsByName, mandatoryParameter)) {
406
- errors.push("missing mandatory parameter: '" + mandatoryParameter + "'");
407
- }
408
- });
409
- // Return any errors
410
- if (errors.length > 0) {
411
- return errors.join(" and\n");
412
- } else {
413
- return paramsByName;
414
- }
415
- }
416
- }
417
-
@@ -1,20 +0,0 @@
1
- import { Commander, CommandInfo } from "../commander";
2
-
3
- export const info: CommandInfo = {
4
- name: "divider",
5
- synchronous: true,
6
- };
7
-
8
-
9
- export class Command {
10
-
11
- constructor(
12
- public params: string[],
13
- public commander: Commander,
14
- ) {
15
- if (this.params.length) throw `${info.name}: No parameters allowed. This is a no-op command.`;
16
- }
17
- execute() {
18
- // Do nothing
19
- }
20
- }
@@ -1,24 +0,0 @@
1
- import * as mws_load_plugin_bags from "./mws-load-plugin-bags";
2
- import * as mws_render_tiddler from "./mws-render-tiddlywiki5";
3
- import * as mws_load_wiki_folder from "./mws-load-wiki-folder";
4
- import * as mws_save_archive from "./mws-save-archive";
5
- import * as mws_load_archive from "./mws-load-archive";
6
- import * as mws_init_store from "./mws-init-store";
7
- import * as mws_listen from "./mws-listen";
8
- import * as divider from "./divider";
9
- import * as tests_complete from "./tests-complete";
10
- import { CommandInfo } from "../commander";
11
-
12
- export { mws_listen, divider };
13
-
14
- export const commands: Record<string, { info: CommandInfo, Command: any }> = {
15
- [mws_load_plugin_bags.info.name]: mws_load_plugin_bags,
16
- [mws_render_tiddler.info.name]: mws_render_tiddler,
17
- [mws_load_wiki_folder.info.name]: mws_load_wiki_folder,
18
- [mws_save_archive.info.name]: mws_save_archive,
19
- [mws_load_archive.info.name]: mws_load_archive,
20
- [mws_init_store.info.name]: mws_init_store,
21
- [mws_listen.info.name]: mws_listen,
22
- [divider.info.name]: divider,
23
- [tests_complete.info.name]: tests_complete,
24
- };
@@ -1,77 +0,0 @@
1
- import { resolve } from "node:path";
2
-
3
- import { Commander, CommandInfo } from "../commander";
4
- import { TiddlerStore } from "../routes/TiddlerStore";
5
- import { readFileSync } from "node:fs";
6
- import { dist_require_resolve, dist_resolve } from "../utils";
7
-
8
- export const info: CommandInfo = {
9
- name: "mws-init-store",
10
- synchronous: true,
11
- };
12
-
13
-
14
- export class Command {
15
-
16
- get $tw() { return this.commander.$tw; }
17
- constructor(
18
- public params: string[],
19
- public commander: Commander,
20
- public callback: (err?: any) => void
21
- ) {
22
-
23
- }
24
- async execute() {
25
-
26
- if (!this.commander.setupRequired) return;
27
-
28
- await this.commander.$transaction(async (prisma) => {
29
- const userCount = await prisma.users.count();
30
-
31
- if (!userCount) {
32
-
33
- await prisma.roles.createMany({
34
- data: [
35
- { role_id: 1, role_name: "ADMIN", description: "System Administrator" },
36
- { role_id: 2, role_name: "USER", description: "Basic User" },
37
- ]
38
- });
39
-
40
- const user = await prisma.users.create({
41
- data: { username: "admin", email: "", password: "", roles: { connect: { role_id: 1 } } },
42
- select: { user_id: true }
43
- });
44
-
45
- const password = await this.commander.PasswordService.PasswordCreation(user.user_id.toString(), "1234");
46
-
47
- await prisma.users.update({
48
- where: { user_id: user.user_id },
49
- data: { password: password }
50
- });
51
-
52
- }
53
-
54
- });
55
- // should give us the path to boot.js
56
- const tweditions = resolve(dist_require_resolve("tiddlywiki"), "../../editions");
57
- this.commander.addCommandTokens([
58
- "--mws-load-plugin-bags",
59
- "--mws-load-wiki-folder", dist_resolve("../editions/mws-docs"),
60
- "mws-docs", "MWS Documentation from https://mws.tiddlywiki.com",
61
- "mws-docs", "MWS Documentation from https://mws.tiddlywiki.com",
62
- "--mws-load-wiki-folder", resolve(tweditions, "tw5.com"),
63
- "docs", "TiddlyWiki Documentation from https://tiddlywiki.com",
64
- "docs", "TiddlyWiki Documentation from https://tiddlywiki.com",
65
- "--mws-load-wiki-folder", resolve(tweditions, "dev"),
66
- "dev", "TiddlyWiki Developer Documentation from https://tiddlywiki.com/dev",
67
- "dev-docs", "TiddlyWiki Developer Documentation from https://tiddlywiki.com/dev",
68
- "--mws-load-wiki-folder", resolve(tweditions, "tour"),
69
- "tour", "TiddlyWiki Interactive Tour from https://tiddlywiki.com",
70
- "tour", "TiddlyWiki Interactive Tour from https://tiddlywiki.com",
71
- "--divider",
72
- ]);
73
-
74
- this.commander.setupRequired = false;
75
- }
76
- }
77
-
@@ -1,136 +0,0 @@
1
-
2
- import { Commander, CommandInfo } from "../commander";
3
- import { ok } from "assert";
4
- import { existsSync, readFileSync } from "fs";
5
- import { Server, IncomingMessage, ServerResponse, createServer } from "http";
6
- import * as http2 from "node:http2";
7
- import { Router } from "../routes/router";
8
- import { MWSConfig } from "../server";
9
- import { Streamer } from "../streamer";
10
-
11
-
12
- export class ListenerBase {
13
- constructor(
14
- public server: http2.Http2SecureServer | Server,
15
- public router: Router,
16
- public bindInfo: string
17
- ) {
18
- this.server.on("request", (
19
- req: IncomingMessage | http2.Http2ServerRequest,
20
- res: ServerResponse | http2.Http2ServerResponse
21
- ) => {
22
- this.handleRequest(req, res);
23
- });
24
- this.server.on('error', (error: NodeJS.ErrnoException) => {
25
-
26
- if (error.syscall !== 'listen') {
27
- throw error;
28
- }
29
-
30
- // handle specific listen errors with friendly messages
31
- switch (error.code) {
32
- case 'EACCES':
33
- console.error(bindInfo + ' requires elevated privileges');
34
- process.exit();
35
- break;
36
- case 'EADDRINUSE':
37
- console.error(bindInfo + ' is already in use');
38
- process.exit();
39
- break;
40
- default:
41
- throw error;
42
- }
43
-
44
- });
45
- this.server.on('listening', () => {
46
- const address = this.server.address();
47
- console.log(`Listening on`, address);
48
- });
49
- }
50
-
51
- handleRequest(
52
- req: IncomingMessage | http2.Http2ServerRequest,
53
- res: ServerResponse | http2.Http2ServerResponse
54
- ) {
55
- const streamer = new Streamer(req, res, this.router);
56
- this.router.handle(streamer).catch(streamer.catcher);
57
- }
58
- }
59
-
60
- export class ListenerHTTPS extends ListenerBase {
61
- constructor(router: Router, config: MWSConfig["listeners"][number]) {
62
- const { port, host } = config;
63
- if (port && typeof port !== "number") throw new Error("If specified, port must be a number");
64
- const bindInfo = host ? `HTTPS ${host} ${port}` : `HTTPS ${port}`;
65
- ok(config.key && existsSync(config.key), "Key file not found");
66
- ok(config.cert && existsSync(config.cert), "Cert file not found");
67
- const key = readFileSync(config.key);
68
- const cert = readFileSync(config.cert);
69
- super(http2.createSecureServer({ key, cert, allowHTTP1: true, }), router, bindInfo);
70
- if (port) this.server.listen(port, host);
71
- }
72
-
73
- }
74
-
75
- export class ListenerHTTP extends ListenerBase {
76
- /** Create an http1 server */
77
- constructor(router: Router, config: MWSConfig["listeners"][number]) {
78
- const { port, host } = config;
79
- if (port && typeof port !== "number") throw new Error("If specified, port must be a number");
80
- const bindInfo = host ? `HTTP ${host} ${port}` : `HTTP ${port}`;
81
- super(createServer(), router, bindInfo);
82
- if (port) this.server.listen(port, host);
83
- }
84
- }
85
-
86
-
87
- export const info: CommandInfo = {
88
- name: "mws-listen",
89
- synchronous: true,
90
- };
91
-
92
-
93
- export class Command {
94
- execute;
95
- get $tw() { return this.commander.$tw; }
96
- constructor(
97
- public params: string[],
98
- public commander: Commander,
99
- listenConfig: MWSConfig["listeners"],
100
- onListenersCreated: MWSConfig["onListenersCreated"]
101
- ) {
102
- if (this.params.length) throw `${info.name}: No parameters allowed. Please put listener options in the config file.`;
103
-
104
- this.execute = async () => {
105
-
106
- this.commander.addCommandTokens([
107
- "--mws-render-tiddlywiki5",
108
- "--divider",
109
- ]);
110
-
111
- const router = await Router.makeRouter(
112
- this.commander,
113
- this.commander.config.enableDevServer,
114
- ).catch(e => {
115
- console.log(e.stack);
116
- throw "Router failed to load";
117
- });
118
-
119
- const listeners = listenConfig.map(e => {
120
- if (!e.key !== !e.cert) {
121
- throw new Error("Both key and cert are required for HTTPS");
122
- }
123
-
124
- const listener = e.key && e.cert
125
- ? new ListenerHTTPS(router, e)
126
- : new ListenerHTTP(router, e);
127
-
128
- return listener;
129
- });
130
-
131
- await onListenersCreated?.(listeners);
132
- };
133
-
134
- }
135
-
136
- }