@tiddlywiki/mws 0.0.12 → 0.0.13
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.
- package/README.md +3 -1
- package/dist/server.d.ts +923 -0
- package/dist/server.js +1763 -1415
- package/dist/server.js.map +1 -1
- package/editions/mws-docs/tiddlers/$__StoryList.tid +3 -3
- package/editions/mws-docs/tiddlers/MWS and SQLite.md +11 -0
- package/editions/mws-docs/tiddlers/Request Server.md +20 -0
- package/editions/mws-docs/tiddlers/Request Server.md.meta +5 -0
- package/package.json +16 -10
- package/prisma/schema.prisma +1 -0
- package/react-user-mgmt/package-lock.json +76 -321
- package/react-user-mgmt/package.json +2 -5
- package/react-user-mgmt/public/chunk-YALWN5OT.js +2 -0
- package/react-user-mgmt/public/chunk-YALWN5OT.js.map +7 -0
- package/react-user-mgmt/public/esm-QKDFEP3B.js +3 -0
- package/react-user-mgmt/public/esm-QKDFEP3B.js.map +7 -0
- package/react-user-mgmt/public/index.html +1 -26
- package/react-user-mgmt/public/main.js +68 -109
- package/react-user-mgmt/public/main.js.map +4 -4
- package/react-user-mgmt/public/stats.css +597 -0
- package/react-user-mgmt/public/stats.html +59 -0
- package/react-user-mgmt/public/stats.js +2013 -0
- package/react-user-mgmt/public/stats.json +1 -0
- package/react-user-mgmt/public/stats.svg +4 -0
- package/react-user-mgmt/tsconfig.json +3 -0
- package/dist/server.cjs +0 -16677
- package/dist/server.cjs.map +0 -1
- package/src/StateObject.ts +0 -316
- package/src/commander.ts +0 -417
- package/src/commands/divider.ts +0 -20
- package/src/commands/index.ts +0 -24
- package/src/commands/mws-init-store.ts +0 -77
- package/src/commands/mws-listen.ts +0 -136
- package/src/commands/mws-load-archive.ts +0 -281
- package/src/commands/mws-load-plugin-bags.ts +0 -151
- package/src/commands/mws-load-tiddlers.ts +0 -37
- package/src/commands/mws-load-wiki-folder.ts +0 -124
- package/src/commands/mws-render-tiddlywiki5.ts +0 -55
- package/src/commands/mws-save-archive.ts +0 -111
- package/src/commands/mws-save-tiddler-text.ts +0 -33
- package/src/commands/tests-complete.ts +0 -21
- package/src/global.ts +0 -247
- package/src/routes/TiddlerStore.ts +0 -722
- package/src/routes/bag-file-server.ts +0 -340
- package/src/routes/cache.ts +0 -25
- package/src/routes/index.ts +0 -138
- package/src/routes/managers/index.ts +0 -17
- package/src/routes/managers/manager-recipes.ts +0 -438
- package/src/routes/managers/manager-users.ts +0 -201
- package/src/routes/managers/router-tiddlers.ts +0 -313
- package/src/routes/router.ts +0 -429
- package/src/routes/services/PasswordService.ts +0 -178
- package/src/routes/services/attachments.ts +0 -259
- package/src/routes/services/sessions.ts +0 -166
- package/src/routes/tw-test.ts +0 -65
- package/src/server.ts +0 -151
- package/src/setupDevServer.ts +0 -71
- package/src/streamer.ts +0 -430
- package/src/tiddlywiki.ts +0 -94
- package/src/utils/data-checks.ts +0 -172
- package/src/utils/index.ts +0 -4
- package/src/utils/rootRoute.ts +0 -237
- package/src/utils/utils.ts +0 -403
- package/src/utils/zodAssert.ts +0 -269
- /package/prisma/{migrations/notes.md → notes.md} +0 -0
package/src/setupDevServer.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { request } from "http";
|
|
3
|
-
import { resolve } from "path";
|
|
4
|
-
import { StateObject } from "./StateObject";
|
|
5
|
-
import { dist_resolve } from "./utils";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export async function setupDevServer<T extends StateObject>(enableDevServer: string | undefined) {
|
|
10
|
-
|
|
11
|
-
if (!enableDevServer) {
|
|
12
|
-
return async function sendProdServer(state: T) {
|
|
13
|
-
|
|
14
|
-
const rootdir = dist_resolve('../react-user-mgmt/public');
|
|
15
|
-
// use sendFile directly instead of having the dev server send it
|
|
16
|
-
return state.sendFile(200, {}, {
|
|
17
|
-
root: rootdir,
|
|
18
|
-
reqpath: state.url === "/" ? "/index.html" : state.url,
|
|
19
|
-
on404: () => state.sendFile(200, {}, { reqpath: "/index.html", root: rootdir, })
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
} else {
|
|
23
|
-
const esbuild = await import("esbuild");
|
|
24
|
-
|
|
25
|
-
const rootdir = resolve(enableDevServer, 'react-user-mgmt');
|
|
26
|
-
|
|
27
|
-
let ctx = await esbuild.context({
|
|
28
|
-
entryPoints: [resolve(rootdir, 'src/main.tsx')],
|
|
29
|
-
bundle: true,
|
|
30
|
-
target: 'es2020',
|
|
31
|
-
platform: 'browser',
|
|
32
|
-
jsx: 'automatic',
|
|
33
|
-
outdir: resolve(rootdir, 'public'),
|
|
34
|
-
minify: true,
|
|
35
|
-
sourcemap: true,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const { port } = await ctx.serve({
|
|
39
|
-
servedir: resolve(rootdir, 'public'),
|
|
40
|
-
fallback: resolve(rootdir, 'public/index.html'),
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return async function sendDevServer(state: T) {
|
|
44
|
-
const proxyRes = await new Promise<import("http").IncomingMessage>((resolve, reject) => {
|
|
45
|
-
const headers = { ...state.headers };
|
|
46
|
-
delete headers[":method"];
|
|
47
|
-
delete headers[":path"];
|
|
48
|
-
delete headers[":authority"];
|
|
49
|
-
delete headers[":scheme"];
|
|
50
|
-
headers.host = "localhost";
|
|
51
|
-
const proxyReq = request({
|
|
52
|
-
hostname: "localhost",
|
|
53
|
-
port: port,
|
|
54
|
-
path: state.url,
|
|
55
|
-
method: state.method,
|
|
56
|
-
headers,
|
|
57
|
-
}, resolve);
|
|
58
|
-
state.reader.pipe(proxyReq, { end: true });
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const { statusCode, headers } = proxyRes;
|
|
62
|
-
if (statusCode === 404 || !statusCode) {
|
|
63
|
-
proxyRes.resume();
|
|
64
|
-
return state.sendEmpty(404, { 'Content-Type': 'text/html' });
|
|
65
|
-
} else {
|
|
66
|
-
return state.sendStream(statusCode as number, headers, proxyRes);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
}
|
package/src/streamer.ts
DELETED
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
import "./global";
|
|
2
|
-
import * as http2 from 'node:http2';
|
|
3
|
-
import send from 'send';
|
|
4
|
-
import { Readable } from 'stream';
|
|
5
|
-
import { IncomingMessage, ServerResponse, IncomingHttpHeaders as NodeIncomingHeaders, OutgoingHttpHeaders } from 'node:http';
|
|
6
|
-
import { is } from './utils';
|
|
7
|
-
import { createReadStream } from 'node:fs';
|
|
8
|
-
import { Writable } from 'node:stream';
|
|
9
|
-
|
|
10
|
-
declare module 'node:net' {
|
|
11
|
-
interface Socket {
|
|
12
|
-
// this comment gets prepended to the other comment for this property, thus the hanging sentance.
|
|
13
|
-
/** Not defined on net.Socket instances.
|
|
14
|
-
*
|
|
15
|
-
* On tls.Socket instances, */
|
|
16
|
-
encrypted?: boolean;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export interface IncomingHttpHeaders extends NodeIncomingHeaders {
|
|
22
|
-
"accept-encoding"?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const SYMBOL_IGNORE_ERROR: unique symbol = Symbol("IGNORE_ERROR");
|
|
26
|
-
export const STREAM_ENDED: unique symbol = Symbol("STREAM_ENDED");
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export type StreamerChunk = { data: string, encoding: NodeJS.BufferEncoding } | NodeJS.ReadableStream | Readable | Buffer;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The HTTP2 shims used in the request handler are only used for HTTP2 requests.
|
|
33
|
-
* The NodeJS HTTP2 server actually calls the HTTP1 parser for all HTTP1 requests.
|
|
34
|
-
*/
|
|
35
|
-
export class Streamer {
|
|
36
|
-
host: string;
|
|
37
|
-
method: AllowedMethod;
|
|
38
|
-
urlInfo: URL;
|
|
39
|
-
url: string;
|
|
40
|
-
headers: IncomingHttpHeaders;
|
|
41
|
-
isSecure: boolean;
|
|
42
|
-
cookies: Record<string, string | undefined>;
|
|
43
|
-
constructor(
|
|
44
|
-
private req: IncomingMessage | http2.Http2ServerRequest,
|
|
45
|
-
private res: ServerResponse | http2.Http2ServerResponse,
|
|
46
|
-
private router: Router
|
|
47
|
-
) {
|
|
48
|
-
|
|
49
|
-
this.headers = req.headers;
|
|
50
|
-
this.url = req.url as string;
|
|
51
|
-
if (is<http2.Http2ServerRequest>(req, req.httpVersionMajor > 1)) {
|
|
52
|
-
this.req.headers.host = req.headers[":authority"];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (!req.headers.host) throw new Error("This should never happen");
|
|
56
|
-
if (!req.method) throw new Error("This should never happen");
|
|
57
|
-
if (!req.url?.startsWith("/")) throw new Error("This should never happen");
|
|
58
|
-
//https://httpwg.org/specs/rfc9110.html#status.501
|
|
59
|
-
if (!is<AllowedMethod>(req.method, AllowedMethods.includes(req.method as any)))
|
|
60
|
-
throw this.sendString(501, {}, "Method not supported", "utf8");
|
|
61
|
-
this.host = req.headers.host;
|
|
62
|
-
this.method = req.method;
|
|
63
|
-
this.isSecure = !!req.socket.encrypted;
|
|
64
|
-
this.urlInfo = new URL(`https://${req.headers.host}${req.url}`);
|
|
65
|
-
|
|
66
|
-
this.cookies = this.parseCookieString(req.headers.cookie || "");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// RIP Push Stream. it was a great idea.
|
|
71
|
-
async pushStream(path: string) {
|
|
72
|
-
return new Promise<Streamer>((resolve, reject) => {
|
|
73
|
-
const req2: http2.Http2ServerRequest = this.req as any;
|
|
74
|
-
if (!req2.stream || !req2.stream.pushAllowed) return reject();
|
|
75
|
-
req2.stream.write
|
|
76
|
-
const newRawHeaders = this.req.rawHeaders.slice();
|
|
77
|
-
for (let i = 0; i < newRawHeaders.length; i += 2) {
|
|
78
|
-
if (newRawHeaders[i] === ":method") newRawHeaders[i + 1] = "GET";
|
|
79
|
-
if (newRawHeaders[i] === ":path") newRawHeaders[i + 1] = path;
|
|
80
|
-
}
|
|
81
|
-
req2.stream.pushStream({ ":method": "GET", ":path": path }, (err, pushStream, headers) => {
|
|
82
|
-
if (err) return reject(err);
|
|
83
|
-
const preq = new http2.Http2ServerRequest(pushStream, req2.headers, {}, newRawHeaders);
|
|
84
|
-
const pres = new http2.Http2ServerResponse(pushStream);
|
|
85
|
-
const pushStreamer = new Streamer(preq, pres, this.router);
|
|
86
|
-
resolve(pushStreamer);
|
|
87
|
-
});
|
|
88
|
-
}).then(async streamer => {
|
|
89
|
-
await this.router.handle(streamer);
|
|
90
|
-
return streamer;
|
|
91
|
-
}, (err) => { if (err) throw err; });
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
parseCookieString(cookieString: string) {
|
|
96
|
-
const cookies: any = {};
|
|
97
|
-
if (typeof cookieString !== 'string') throw new Error('cookieString must be a string');
|
|
98
|
-
cookieString.split(';').forEach(cookie => {
|
|
99
|
-
const parts = cookie.split('=');
|
|
100
|
-
if (parts.length >= 2) {
|
|
101
|
-
const key = parts[0]!.trim();
|
|
102
|
-
const value = parts.slice(1).join('=').trim();
|
|
103
|
-
cookies[key] = decodeURIComponent(value);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
return cookies;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
get reader(): Readable { return this.req; }
|
|
110
|
-
get writer(): Writable {
|
|
111
|
-
// don't overwrite it here if it's already set because this could just be for sending the body.
|
|
112
|
-
if (!this.headersSent && !this.headersSentBy)
|
|
113
|
-
this.headersSentBy = new Error("Possible culprit was given access to the response object here.");
|
|
114
|
-
return this.res;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
throw(statusCode: number) {
|
|
118
|
-
this.sendEmpty(statusCode);
|
|
119
|
-
throw SYMBOL_IGNORE_ERROR;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
catcher = (error: unknown) => {
|
|
123
|
-
if (error === SYMBOL_IGNORE_ERROR) return;
|
|
124
|
-
if (error === STREAM_ENDED) return;
|
|
125
|
-
const tag = this.urlInfo.href;
|
|
126
|
-
console.error(tag, error);
|
|
127
|
-
if (!this.headersSent)
|
|
128
|
-
this.sendEmpty(500, { "x-reason": "Internal Server Error (catcher)" });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
checkHeadersSentBy(setError: boolean) {
|
|
132
|
-
if (this.headersSent && this.headersSentBy) {
|
|
133
|
-
console.log(this.headersSentBy);
|
|
134
|
-
console.log(new Error("This is the second attempt to send headers. Was it supposed to happen?"));
|
|
135
|
-
}
|
|
136
|
-
else if (this.res.headersSent) console.log("Headers were sent by an unknown source.");
|
|
137
|
-
// queue up the error in case there is a second attempt.
|
|
138
|
-
else this.headersSentBy = new Error("You appear to be sending headers more than once. The first attempt was here. Does it need to throw or return?")
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
toHeadersMap(headers: { [x: string]: string | string[] | number | undefined }) {
|
|
144
|
-
return new Map(Object.entries(headers).map(([k, v]) =>
|
|
145
|
-
[k.toLowerCase(), Array.isArray(v) ? v : v === undefined ? [] : [v.toString()]]
|
|
146
|
-
));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
readBody = () => new Promise<Buffer>((resolve: (chunk: Buffer) => void) => {
|
|
150
|
-
const chunks: Buffer[] = [];
|
|
151
|
-
|
|
152
|
-
this.reader.on('data', chunk => chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk));
|
|
153
|
-
this.reader.on('end', () => resolve(Buffer.concat(chunks)));
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
sendEmpty(status: number, headers: OutgoingHttpHeaders = {}): typeof STREAM_ENDED {
|
|
157
|
-
if (process.env.DEBUG?.split(",").includes("send")) {
|
|
158
|
-
console.error("sendEmpty", status, headers);
|
|
159
|
-
}
|
|
160
|
-
this.checkHeadersSentBy(true);
|
|
161
|
-
this.res.writeHead(status, headers);
|
|
162
|
-
this.res.end();
|
|
163
|
-
return STREAM_ENDED;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
sendString(status: number, headers: OutgoingHttpHeaders, data: string, encoding: NodeJS.BufferEncoding): typeof STREAM_ENDED {
|
|
167
|
-
if (process.env.DEBUG?.split(",").includes("send")) {
|
|
168
|
-
console.error("sendString", status, headers);
|
|
169
|
-
}
|
|
170
|
-
this.checkHeadersSentBy(true);
|
|
171
|
-
headers['content-length'] = Buffer.byteLength(data, encoding);
|
|
172
|
-
this.res.writeHead(status, headers);
|
|
173
|
-
|
|
174
|
-
if (this.method === "HEAD")
|
|
175
|
-
this.res.end();
|
|
176
|
-
else
|
|
177
|
-
this.res.end(data, encoding);
|
|
178
|
-
|
|
179
|
-
return STREAM_ENDED;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
sendBuffer(status: number, headers: OutgoingHttpHeaders, data: Buffer): typeof STREAM_ENDED {
|
|
183
|
-
if (process.env.DEBUG?.split(",").includes("send")) {
|
|
184
|
-
console.error("sendBuffer", status, headers);
|
|
185
|
-
}
|
|
186
|
-
this.checkHeadersSentBy(true);
|
|
187
|
-
headers['content-length'] = data.length;
|
|
188
|
-
this.res.writeHead(status, headers);
|
|
189
|
-
if (this.method === "HEAD")
|
|
190
|
-
this.res.end();
|
|
191
|
-
else
|
|
192
|
-
this.res.end(data);
|
|
193
|
-
return STREAM_ENDED;
|
|
194
|
-
}
|
|
195
|
-
/** If this is a HEAD request, the stream will be ignored AND LEFT OPEN. */
|
|
196
|
-
sendStream(status: number, headers: OutgoingHttpHeaders, stream: Readable): typeof STREAM_ENDED {
|
|
197
|
-
if (process.env.DEBUG?.split(",").includes("send")) {
|
|
198
|
-
console.error("sendStream", status, headers);
|
|
199
|
-
}
|
|
200
|
-
this.checkHeadersSentBy(true);
|
|
201
|
-
this.res.writeHead(status, headers);
|
|
202
|
-
if (this.method === "HEAD")
|
|
203
|
-
this.res.end();
|
|
204
|
-
else
|
|
205
|
-
stream.pipe(this.res);
|
|
206
|
-
return STREAM_ENDED;
|
|
207
|
-
}
|
|
208
|
-
// I'm not sure if there's a use case for this
|
|
209
|
-
private sendFD(status: number, headers: OutgoingHttpHeaders, options: {
|
|
210
|
-
fd: number;
|
|
211
|
-
offset?: number;
|
|
212
|
-
length?: number;
|
|
213
|
-
}): typeof STREAM_ENDED {
|
|
214
|
-
this.checkHeadersSentBy(true);
|
|
215
|
-
this.res.writeHead(status, headers);
|
|
216
|
-
const { fd, offset, length } = options;
|
|
217
|
-
const stream = createReadStream("", {
|
|
218
|
-
fd,
|
|
219
|
-
start: offset,
|
|
220
|
-
end: length && length - 1,
|
|
221
|
-
autoClose: false,
|
|
222
|
-
});
|
|
223
|
-
stream.pipe(this.res);
|
|
224
|
-
return STREAM_ENDED;
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Sends a file with the appropriate cache headers, using the `send` npm module.
|
|
228
|
-
*
|
|
229
|
-
* Think of it like a static file server where you are serving files from a directory.
|
|
230
|
-
*
|
|
231
|
-
* @param options.root The directory to serve files from.
|
|
232
|
-
* @param options.reqpath The path to the file relative to the `root` directory.
|
|
233
|
-
* @param options.offset The offset in bytes to start reading the file from.
|
|
234
|
-
* @param options.length The number of bytes to read from the file.
|
|
235
|
-
* @param options.index "index.html" by default, to disable this set false or
|
|
236
|
-
* to supply a new index pass a string or an array in preferred order.
|
|
237
|
-
*
|
|
238
|
-
* If an index.html file is not found, `send` will NOT generate a directory listing.
|
|
239
|
-
*
|
|
240
|
-
* The `send` method will automatically set the `Content-Type` header based on the file extension.
|
|
241
|
-
*
|
|
242
|
-
* If the file is not found, the `send` method will automatically send a 404 response.
|
|
243
|
-
*
|
|
244
|
-
|
|
245
|
-
* @returns STREAM_ENDED
|
|
246
|
-
*/
|
|
247
|
-
sendFile(status: number, headers: OutgoingHttpHeaders, options: {
|
|
248
|
-
root: string;
|
|
249
|
-
reqpath: string;
|
|
250
|
-
offset?: number;
|
|
251
|
-
length?: number;
|
|
252
|
-
index?: string | boolean | string[] | undefined,
|
|
253
|
-
on404?: () => Promise<typeof STREAM_ENDED>;
|
|
254
|
-
onDir?: () => Promise<typeof STREAM_ENDED>;
|
|
255
|
-
}) {
|
|
256
|
-
|
|
257
|
-
// the headers and status have to be set on the response object before piping the stream
|
|
258
|
-
this.res.statusCode = status;
|
|
259
|
-
this.toHeadersMap(headers).forEach((v, k) => { this.res.appendHeader(k, v); });
|
|
260
|
-
|
|
261
|
-
const { root, reqpath, offset, length } = options;
|
|
262
|
-
|
|
263
|
-
if (process.env.DEBUG?.split(",").includes("send")) {
|
|
264
|
-
console.error("sendFile", root, reqpath, status, headers);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const stream = send(this.req, reqpath, {
|
|
268
|
-
dotfiles: "ignore",
|
|
269
|
-
index: false,
|
|
270
|
-
root,
|
|
271
|
-
start: offset,
|
|
272
|
-
end: length && length - 1,
|
|
273
|
-
});
|
|
274
|
-
return new Promise<typeof STREAM_ENDED>((resolve, reject) => {
|
|
275
|
-
|
|
276
|
-
stream.on("error", (err) => Promise.resolve().then(async (): Promise<typeof STREAM_ENDED> => {
|
|
277
|
-
if (err === 404 || err?.statusCode === 404) {
|
|
278
|
-
return (await options.on404?.()) ?? this.sendEmpty(404);
|
|
279
|
-
} else {
|
|
280
|
-
console.log(err);
|
|
281
|
-
throw this.sendEmpty(500);
|
|
282
|
-
}
|
|
283
|
-
}).then(resolve, reject));
|
|
284
|
-
|
|
285
|
-
stream.on("directory", () => Promise.resolve().then(async (): Promise<typeof STREAM_ENDED> => {
|
|
286
|
-
return (await options.onDir?.())
|
|
287
|
-
?? this.sendEmpty(404, { "x-reason": "Directory listing not allowed" })
|
|
288
|
-
}).then(resolve, reject));
|
|
289
|
-
|
|
290
|
-
stream.on("end", () => {
|
|
291
|
-
resolve(STREAM_ENDED);
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
this.checkHeadersSentBy(true);
|
|
295
|
-
stream.pipe(this.res);
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
setCookie(name: string, value: string, options: {
|
|
300
|
-
domain?: string;
|
|
301
|
-
path?: string;
|
|
302
|
-
expires?: Date;
|
|
303
|
-
maxAge?: number;
|
|
304
|
-
secure?: boolean;
|
|
305
|
-
httpOnly?: boolean;
|
|
306
|
-
sameSite?: "Strict" | "Lax" | "None";
|
|
307
|
-
}) {
|
|
308
|
-
var cookie = `${name}=${encodeURIComponent(value)}`;
|
|
309
|
-
if (options.domain) cookie += `; Domain=${options.domain}`;
|
|
310
|
-
if (options.path) cookie += `; Path=${options.path}`;
|
|
311
|
-
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
|
|
312
|
-
if (options.maxAge) cookie += `; Max-Age=${options.maxAge}`;
|
|
313
|
-
if (options.secure) cookie += `; Secure`;
|
|
314
|
-
if (options.httpOnly) cookie += `; HttpOnly`;
|
|
315
|
-
if (options.sameSite) cookie += `; SameSite=${options.sameSite}`;
|
|
316
|
-
this.appendHeader("Set-Cookie", cookie);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
appendHeader(name: string, value: string): void {
|
|
320
|
-
const current = this.res.getHeader(name);
|
|
321
|
-
this.res.setHeader(name,
|
|
322
|
-
current
|
|
323
|
-
? Array.isArray(current)
|
|
324
|
-
? [...current as string[], value]
|
|
325
|
-
: [current as string, value]
|
|
326
|
-
: value
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
setHeader(name: string, value: string): void {
|
|
331
|
-
this.res.setHeader(name, value);
|
|
332
|
-
}
|
|
333
|
-
writeHead(status: number, headers: OutgoingHttpHeaders = {}): void {
|
|
334
|
-
if (process.env.DEBUG?.split(",").includes("send")) {
|
|
335
|
-
console.error("writeHead", status, headers);
|
|
336
|
-
}
|
|
337
|
-
this.checkHeadersSentBy(true);
|
|
338
|
-
this.res.writeHead(status, headers);
|
|
339
|
-
}
|
|
340
|
-
write(chunk: Buffer | string, encoding?: NodeJS.BufferEncoding): void {
|
|
341
|
-
// Between http1 and http2, the types are slightly different, but the runtime effect seems to be the same.
|
|
342
|
-
encoding ? (this.res as ServerResponse).write(chunk, encoding) : (this.res as ServerResponse).write(chunk)
|
|
343
|
-
}
|
|
344
|
-
end(): typeof STREAM_ENDED {
|
|
345
|
-
this.res.end();
|
|
346
|
-
return STREAM_ENDED;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
get headersSent() {
|
|
350
|
-
return this.res.headersSent;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
headersSentBy: Error | undefined;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
export class StreamerState {
|
|
357
|
-
|
|
358
|
-
readBody
|
|
359
|
-
sendEmpty
|
|
360
|
-
sendString
|
|
361
|
-
sendBuffer
|
|
362
|
-
sendStream
|
|
363
|
-
sendFile
|
|
364
|
-
setCookie
|
|
365
|
-
/**
|
|
366
|
-
*
|
|
367
|
-
* Sets a single header value. If the header already exists in the to-be-sent
|
|
368
|
-
* headers, its value will be replaced. Use an array of strings to send multiple
|
|
369
|
-
* headers with the same name
|
|
370
|
-
*
|
|
371
|
-
* When headers have been set with `response.setHeader()`, they will be merged
|
|
372
|
-
* with any headers passed to `response.writeHead()`, with the headers passed
|
|
373
|
-
* to `response.writeHead()` given precedence.
|
|
374
|
-
*
|
|
375
|
-
*/
|
|
376
|
-
setHeader
|
|
377
|
-
writeHead
|
|
378
|
-
write
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
/** sends a status and plain text string body */
|
|
382
|
-
sendSimple(status: number, msg: string): typeof STREAM_ENDED {
|
|
383
|
-
return this.sendString(status, {
|
|
384
|
-
"content-type": "text/plain"
|
|
385
|
-
}, msg, "utf8");
|
|
386
|
-
}
|
|
387
|
-
/** Stringify the value (unconditionally) and send it with content-type `application/json` */
|
|
388
|
-
sendJSON<T>(status: number, obj: T): typeof STREAM_ENDED {
|
|
389
|
-
return this.sendString(status, {
|
|
390
|
-
"content-type": "application/json"
|
|
391
|
-
}, JSON.stringify(obj), "utf8");
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
STREAM_ENDED: typeof STREAM_ENDED = STREAM_ENDED;
|
|
396
|
-
|
|
397
|
-
constructor(private streamer: Streamer) {
|
|
398
|
-
this.readBody = this.streamer.readBody.bind(this.streamer);
|
|
399
|
-
this.sendEmpty = this.streamer.sendEmpty.bind(this.streamer);
|
|
400
|
-
this.sendString = this.streamer.sendString.bind(this.streamer);
|
|
401
|
-
this.sendBuffer = this.streamer.sendBuffer.bind(this.streamer);
|
|
402
|
-
this.sendStream = this.streamer.sendStream.bind(this.streamer);
|
|
403
|
-
this.sendFile = this.streamer.sendFile.bind(this.streamer);
|
|
404
|
-
this.setHeader = this.streamer.setHeader.bind(this.streamer);
|
|
405
|
-
this.writeHead = this.streamer.writeHead.bind(this.streamer);
|
|
406
|
-
this.write = this.streamer.write.bind(this.streamer);
|
|
407
|
-
this.end = this.streamer.end.bind(this.streamer);
|
|
408
|
-
this.setCookie = this.streamer.setCookie.bind(this.streamer);
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
get url() { return this.streamer.url; }
|
|
414
|
-
get method(): AllowedMethod { return this.streamer.method; }
|
|
415
|
-
get headers() { return this.streamer.headers; }
|
|
416
|
-
get host() { return this.streamer.host; }
|
|
417
|
-
get urlInfo() { return this.streamer.urlInfo; }
|
|
418
|
-
get headersSent() { return this.streamer.headersSent; }
|
|
419
|
-
|
|
420
|
-
get reader() { return this.streamer.reader; }
|
|
421
|
-
|
|
422
|
-
/** Currently this is based on whether the socket is secure, so a proxy will cause problems. */
|
|
423
|
-
get isSecure() { return this.streamer.isSecure; }
|
|
424
|
-
get cookies() { return this.streamer.cookies; }
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
import { AllowedMethod, AllowedMethods, Router } from './routes/router';
|
|
430
|
-
|
package/src/tiddlywiki.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { TiddlyWiki, TWBoot, TWBootBrowser, TWBootNode } from "tiddlywiki";
|
|
2
|
-
import { } from "tiddlywiki/boot/bootprefix";
|
|
3
|
-
import { $TW } from "./commander";
|
|
4
|
-
import { resolve } from "node:path";
|
|
5
|
-
import { dist_resolve, is } from "./utils";
|
|
6
|
-
import { ok } from "node:assert";
|
|
7
|
-
|
|
8
|
-
declare module "tiddlywiki" {
|
|
9
|
-
interface TWUtils {
|
|
10
|
-
eachAsync: (array: any[], callback: (item: any, index: number) => Promise<void>) => Promise<void>;
|
|
11
|
-
}
|
|
12
|
-
interface TWBoot {
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function bootTiddlyWiki(wikiPath: string) {
|
|
18
|
-
|
|
19
|
-
const $tw = TiddlyWiki()
|
|
20
|
-
|
|
21
|
-
$tw.utils.eachAsync = eachAsync;
|
|
22
|
-
|
|
23
|
-
$tw.boot.boot = async (callback) => {
|
|
24
|
-
// Initialise crypto object
|
|
25
|
-
$tw.crypto = new $tw.utils.Crypto();
|
|
26
|
-
// Initialise password prompter
|
|
27
|
-
if ($tw.browser && !$tw.node) {
|
|
28
|
-
$tw.passwordPrompt = new $tw.utils.PasswordPrompt();
|
|
29
|
-
}
|
|
30
|
-
// Preload any encrypted tiddlers
|
|
31
|
-
await new Promise<void>(resolve =>
|
|
32
|
-
$tw.boot.decryptEncryptedTiddlers(resolve)
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// $tw.boot.startup();
|
|
36
|
-
await new Promise<void>(callback => {
|
|
37
|
-
const options = { callback };
|
|
38
|
-
$tw.boot.initStartup(options);
|
|
39
|
-
$tw.boot.loadStartup(options);
|
|
40
|
-
$tw.boot.execStartup(options);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
callback?.();
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// creating a new tiddler with the same title as a shadow tiddler
|
|
48
|
-
// prevents the shadow tiddler from defining modules
|
|
49
|
-
|
|
50
|
-
$tw.preloadTiddler({ title: "$:/core/modules/commander.js" })
|
|
51
|
-
$tw.modules.define("$:/core/modules/commander.js", "global", {
|
|
52
|
-
Commander: { initCommands: function () { } },
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// disable the default commander. We'll use our own version of it.
|
|
56
|
-
$tw.preloadTiddler({ title: "$:/core/modules/startup/commands.js" })
|
|
57
|
-
$tw.modules.define("$:/core/modules/startup/commands.js", "startup", {
|
|
58
|
-
name: "commands",
|
|
59
|
-
platforms: ["node"],
|
|
60
|
-
after: ["story"],
|
|
61
|
-
synchronous: true,
|
|
62
|
-
startup: () => { },
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
$tw.preloadTiddler({ title: "$:/plugins/tiddlywiki/multiwikiserver/startup.js" })
|
|
68
|
-
$tw.modules.define("$:/plugins/tiddlywiki/multiwikiserver/startup.js", "startup", {
|
|
69
|
-
name: "multiwikiserver",
|
|
70
|
-
platforms: ["node"],
|
|
71
|
-
after: ["load-modules"],
|
|
72
|
-
before: ["story", "commands"],
|
|
73
|
-
synchronous: true,
|
|
74
|
-
startup: () => { },
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// tiddlywiki [+<pluginname> | ++<pluginpath>] [<wikipath>] ...[--command ...args]
|
|
78
|
-
$tw.boot.argv = [
|
|
79
|
-
"++" + dist_resolve("../plugins/client"),
|
|
80
|
-
"+plugins/tiddlywiki/tiddlyweb",
|
|
81
|
-
"+themes/tiddlywiki/vanilla",
|
|
82
|
-
"+themes/tiddlywiki/snowwhite",
|
|
83
|
-
wikiPath,
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
// use callback to match the type signature
|
|
87
|
-
await new Promise<void>(resolve => $tw.boot.boot(resolve));
|
|
88
|
-
|
|
89
|
-
// this makes sure boot followed the node path
|
|
90
|
-
ok(!$tw.boot.tasks.readBrowserTiddlers);
|
|
91
|
-
|
|
92
|
-
return $tw;
|
|
93
|
-
|
|
94
|
-
}
|