@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.
Files changed (65) hide show
  1. package/README.md +3 -1
  2. package/dist/server.d.ts +923 -0
  3. package/dist/server.js +1763 -1415
  4. package/dist/server.js.map +1 -1
  5. package/editions/mws-docs/tiddlers/$__StoryList.tid +3 -3
  6. package/editions/mws-docs/tiddlers/MWS and SQLite.md +11 -0
  7. package/editions/mws-docs/tiddlers/Request Server.md +20 -0
  8. package/editions/mws-docs/tiddlers/Request Server.md.meta +5 -0
  9. package/package.json +16 -10
  10. package/prisma/schema.prisma +1 -0
  11. package/react-user-mgmt/package-lock.json +76 -321
  12. package/react-user-mgmt/package.json +2 -5
  13. package/react-user-mgmt/public/chunk-YALWN5OT.js +2 -0
  14. package/react-user-mgmt/public/chunk-YALWN5OT.js.map +7 -0
  15. package/react-user-mgmt/public/esm-QKDFEP3B.js +3 -0
  16. package/react-user-mgmt/public/esm-QKDFEP3B.js.map +7 -0
  17. package/react-user-mgmt/public/index.html +1 -26
  18. package/react-user-mgmt/public/main.js +68 -109
  19. package/react-user-mgmt/public/main.js.map +4 -4
  20. package/react-user-mgmt/public/stats.css +597 -0
  21. package/react-user-mgmt/public/stats.html +59 -0
  22. package/react-user-mgmt/public/stats.js +2013 -0
  23. package/react-user-mgmt/public/stats.json +1 -0
  24. package/react-user-mgmt/public/stats.svg +4 -0
  25. package/react-user-mgmt/tsconfig.json +3 -0
  26. package/dist/server.cjs +0 -16677
  27. package/dist/server.cjs.map +0 -1
  28. package/src/StateObject.ts +0 -316
  29. package/src/commander.ts +0 -417
  30. package/src/commands/divider.ts +0 -20
  31. package/src/commands/index.ts +0 -24
  32. package/src/commands/mws-init-store.ts +0 -77
  33. package/src/commands/mws-listen.ts +0 -136
  34. package/src/commands/mws-load-archive.ts +0 -281
  35. package/src/commands/mws-load-plugin-bags.ts +0 -151
  36. package/src/commands/mws-load-tiddlers.ts +0 -37
  37. package/src/commands/mws-load-wiki-folder.ts +0 -124
  38. package/src/commands/mws-render-tiddlywiki5.ts +0 -55
  39. package/src/commands/mws-save-archive.ts +0 -111
  40. package/src/commands/mws-save-tiddler-text.ts +0 -33
  41. package/src/commands/tests-complete.ts +0 -21
  42. package/src/global.ts +0 -247
  43. package/src/routes/TiddlerStore.ts +0 -722
  44. package/src/routes/bag-file-server.ts +0 -340
  45. package/src/routes/cache.ts +0 -25
  46. package/src/routes/index.ts +0 -138
  47. package/src/routes/managers/index.ts +0 -17
  48. package/src/routes/managers/manager-recipes.ts +0 -438
  49. package/src/routes/managers/manager-users.ts +0 -201
  50. package/src/routes/managers/router-tiddlers.ts +0 -313
  51. package/src/routes/router.ts +0 -429
  52. package/src/routes/services/PasswordService.ts +0 -178
  53. package/src/routes/services/attachments.ts +0 -259
  54. package/src/routes/services/sessions.ts +0 -166
  55. package/src/routes/tw-test.ts +0 -65
  56. package/src/server.ts +0 -151
  57. package/src/setupDevServer.ts +0 -71
  58. package/src/streamer.ts +0 -430
  59. package/src/tiddlywiki.ts +0 -94
  60. package/src/utils/data-checks.ts +0 -172
  61. package/src/utils/index.ts +0 -4
  62. package/src/utils/rootRoute.ts +0 -237
  63. package/src/utils/utils.ts +0 -403
  64. package/src/utils/zodAssert.ts +0 -269
  65. /package/prisma/{migrations/notes.md → notes.md} +0 -0
@@ -1,403 +0,0 @@
1
- import { IncomingHttpHeaders, OutgoingHttpHeaders } from "node:http";
2
- import { Streamer } from "../streamer";
3
- import { StateObject } from '../StateObject';
4
- import { createHash } from "node:crypto";
5
- import * as zlib from "node:zlib";
6
- import { ok } from "node:assert";
7
- import { promisify } from "node:util";
8
- import { SiteConfig } from "../routes/router";
9
- import * as path from "node:path";
10
- import { createRequire } from "node:module";
11
- import { fileURLToPath } from "node:url";
12
- /**
13
- Options include:
14
- - `cbPartStart(headers,name,filename)` - invoked when a file starts being received
15
- - `cbPartChunk(chunk)` - invoked when a chunk of a file is received
16
- - `cbPartEnd()` - invoked when a file finishes being received
17
- - `cbFinished(err)` - invoked when the all the form data has been processed
18
- */
19
- export function readMultipartData(this: StateObject<any, any>, options: {
20
- cbPartStart: (headers: IncomingHttpHeaders, name: string | null, filename: string | null) => void,
21
- cbPartChunk: (chunk: Buffer) => void,
22
- cbPartEnd: () => void,
23
- cbFinished: (err: Error | string | null) => void
24
- }) {
25
- return new Promise<void>((resolve, reject) => {
26
- // Check that the Content-Type is multipart/form-data
27
- const contentType = this.headers['content-type'];
28
- if (!contentType?.startsWith("multipart/form-data")) {
29
- return reject("Expected multipart/form-data content type");
30
- }
31
- // Extract the boundary string from the Content-Type header
32
- const boundaryMatch = contentType.match(/boundary=(.+)$/);
33
- if (!boundaryMatch) {
34
- return reject("Missing boundary in multipart/form-data");
35
- }
36
- const boundary = boundaryMatch[1];
37
- const boundaryBuffer = Buffer.from("--" + boundary);
38
- // Initialise
39
- let buffer = Buffer.alloc(0);
40
- let processingPart = false;
41
- // Process incoming chunks
42
- this.reader.on("data", (chunk) => {
43
- // Accumulate the incoming data
44
- buffer = Buffer.concat([buffer, chunk]);
45
- // Loop through any parts within the current buffer
46
- while (true) {
47
- if (!processingPart) {
48
- // If we're not processing a part then we try to find a boundary marker
49
- const boundaryIndex = buffer.indexOf(boundaryBuffer);
50
- if (boundaryIndex === -1) {
51
- // Haven't reached the boundary marker yet, so we should wait for more data
52
- break;
53
- }
54
- // Look for the end of the headers
55
- const endOfHeaders = buffer.indexOf("\r\n\r\n", boundaryIndex + boundaryBuffer.length);
56
- if (endOfHeaders === -1) {
57
- // Haven't reached the end of the headers, so we should wait for more data
58
- break;
59
- }
60
- // Extract and parse headers
61
- const headersPart = Uint8Array.prototype.slice.call(buffer, boundaryIndex + boundaryBuffer.length, endOfHeaders).toString();
62
- const currentHeaders: IncomingHttpHeaders = {};
63
- headersPart.split("\r\n").forEach(headerLine => {
64
- const [key, value] = headerLine.split(": ");
65
- ok(typeof key === "string");
66
- currentHeaders[key.toLowerCase()] = value;
67
- });
68
- // Parse the content disposition header
69
- const contentDisposition: {
70
- name: string | null,
71
- filename: string | null
72
- } = {
73
- name: null,
74
- filename: null
75
- };
76
- if (currentHeaders["content-disposition"]) {
77
- // Split the content-disposition header into semicolon-delimited parts
78
- const parts = currentHeaders["content-disposition"].split(";").map(part => part.trim());
79
- // Iterate over each part to extract name and filename if they exist
80
- parts.forEach(part => {
81
- if (part.startsWith("name=")) {
82
- // Remove "name=" and trim quotes
83
- contentDisposition.name = part.substring(6, part.length - 1);
84
- } else if (part.startsWith("filename=")) {
85
- // Remove "filename=" and trim quotes
86
- contentDisposition.filename = part.substring(10, part.length - 1);
87
- }
88
- });
89
- }
90
- processingPart = true;
91
- options.cbPartStart(currentHeaders, contentDisposition.name, contentDisposition.filename);
92
- // Slice the buffer to the next part
93
- buffer = Buffer.from(buffer, endOfHeaders + 4);
94
- } else {
95
- const boundaryIndex = buffer.indexOf(boundaryBuffer);
96
- if (boundaryIndex >= 0) {
97
- // Return the part up to the boundary minus the terminating LF CR
98
- options.cbPartChunk(Buffer.from(buffer, 0, boundaryIndex - 2));
99
- options.cbPartEnd();
100
- processingPart = false;
101
- // this starts at the boundary marker, which gets picked up on the next loop
102
- buffer = Buffer.from(buffer, boundaryIndex);
103
- } else {
104
- // Return the rest of the buffer
105
- options.cbPartChunk(buffer);
106
- // Reset the buffer and wait for more data
107
- buffer = Buffer.alloc(0);
108
- break;
109
- }
110
- }
111
- }
112
- });
113
-
114
- // All done
115
- this.reader.on("end", () => {
116
- resolve();
117
- });
118
- }).then(() => {
119
- options.cbFinished(null);
120
- }, (err) => {
121
- options.cbFinished(err);
122
- throw err;
123
- });
124
-
125
- }
126
-
127
-
128
-
129
- /*
130
- Send a response to the client. This method checks if the response must be sent
131
- or if the client alrady has the data cached. If that's the case only a 304
132
- response will be transmitted and the browser will use the cached data.
133
- Only requests with status code 200 are considdered for caching.
134
- request: request instance passed to the handler
135
- response: response instance passed to the handler
136
- statusCode: stauts code to send to the browser
137
- headers: response headers (they will be augmented with an `Etag` header)
138
- data: the data to send (passed to the end method of the response instance)
139
- encoding: the encoding of the data to send (passed to the end method of the response instance)
140
- */
141
- export async function sendResponse(config: SiteConfig, state: StateObject<any, any>, statusCode: number, headers: OutgoingHttpHeaders, data: string | Buffer, encoding?: NodeJS.BufferEncoding) {
142
- // console.log("sendResponse");
143
- if (config.enableBrowserCache && (statusCode == 200)) {
144
- // console.log("enableBrowserCache");
145
- var hash = createHash('md5');
146
- // Put everything into the hash that could change and invalidate the data that
147
- // the browser already stored. The headers the data and the encoding.
148
- hash.update(data);
149
- hash.update(JSON.stringify(headers));
150
- if (encoding) {
151
- hash.update(encoding);
152
- }
153
- var contentDigest = hash.digest("hex");
154
- // RFC 7232 section 2.3 mandates for the etag to be enclosed in quotes
155
- headers["Etag"] = '"' + contentDigest + '"';
156
- headers["Cache-Control"] = "max-age=0, must-revalidate";
157
- // Check if any of the hashes contained within the if-none-match header
158
- // matches the current hash.
159
- // If one matches, do not send the data but tell the browser to use the
160
- // cached data.
161
- // We do not implement "*" as it makes no sense here.
162
- var ifNoneMatch = state.headers["if-none-match"];
163
- if (ifNoneMatch) {
164
- var matchParts = ifNoneMatch.split(",").map(function (etag) {
165
- return etag.replace(/^[ "]+|[ "]+$/g, "");
166
- });
167
- if (matchParts.indexOf(contentDigest) != -1) {
168
- return state.sendEmpty(304, headers);
169
- }
170
- }
171
- }
172
- /*
173
- If the gzip=yes is set, check if the user agent permits compression. If so,
174
- compress our response if the raw data is bigger than 2k. Compressing less
175
- data is inefficient.
176
- */
177
- if (config.enableGzip && (data.length > 2048)) {
178
- var acceptEncoding = state.headers["accept-encoding"] || "";
179
- if (/\bdeflate\b/.test(acceptEncoding)) {
180
- headers["Content-Encoding"] = "deflate";
181
- data = await promisify(zlib.deflate)(data);
182
- } else if (/\bgzip\b/.test(acceptEncoding)) {
183
- headers["Content-Encoding"] = "gzip";
184
- data = await promisify(zlib.gzip)(data);
185
- }
186
- }
187
- if (typeof data === "string") {
188
- ok(encoding, "encoding must be set for string data");
189
- return state.sendString(statusCode, headers, data, encoding);
190
- } else {
191
- return state.sendBuffer(statusCode, headers, data);
192
- }
193
- }
194
-
195
-
196
- interface Route {
197
- useACL: any;
198
- method: string;
199
- entityName: any;
200
- csrfDisable: any;
201
- bodyFormat: string;
202
- path: { source: string; };
203
- handler: (streamer: Streamer) => void;
204
- };
205
-
206
- interface State {
207
- wiki: any;
208
- boot: any;
209
- server: any;
210
- urlInfo: any;
211
- queryParameters: any;
212
- pathPrefix: string;
213
- sendResponse: any;
214
- redirect: any;
215
- streamMultipartData: any;
216
- authenticatedUser: any;
217
- authenticatedUsername: any;
218
- authorizationType: string;
219
- allowAnon: boolean;
220
- anonAccessConfigured: boolean;
221
- allowAnonReads: boolean;
222
- allowAnonWrites: boolean;
223
- showAnonConfig: boolean;
224
- firstGuestUser: boolean;
225
- data?: any;
226
- }
227
-
228
- interface Server {
229
- enableBrowserCache: boolean;
230
- enableGzip: boolean;
231
- get: (key: string) => any;
232
- methodMappings: { [key: string]: string };
233
- isAuthorized: (authorizationType: string, username: string) => boolean;
234
- getAnonymousAccessConfig: () => { allowReads: boolean; allowWrites: boolean; isEnabled: boolean; showAnonConfig: boolean };
235
- sqlTiddlerDatabase: any;
236
- servername: string;
237
- findMatchingRoute: (request: Streamer, state: State) => Route;
238
- authenticateUser: (request: Streamer, response: Streamer) => any;
239
- methodACLPermMappings: { [key: string]: string };
240
- csrfDisable: boolean;
241
- wiki: any;
242
- boot: any;
243
- }
244
-
245
-
246
- export function is<T>(a: any, b: boolean): a is T { return b; }
247
-
248
- /** Initiates a timer that must get cancelled before the current turn of the event loop ends */
249
- export class SyncCheck {
250
- done;
251
- constructor() {
252
- let cancelled = false;
253
- const error = new Error("SyncCheck was not completed before the current turn of the event loop ended");
254
- process.nextTick(() => {
255
- if (cancelled) return;
256
- console.log(error);
257
- process.exit(1);
258
- });
259
- this.done = () => { cancelled = true };
260
- }
261
- }
262
-
263
-
264
- /**
265
- * If any property returns a function, and that function returns a promise,
266
- * the proxy will throw an error if the promise is not awaited before the next property access.
267
- *
268
- * The stack trace of the error will point to the line where the promise was returned.
269
- */
270
- export function createStrictAwaitProxy<T extends object>(instance: T): T {
271
- let outstandingPromiseError: Error | null = null;
272
-
273
- function wrappedFunction(value: Function) {
274
- return function (this: any, ...args: any[]) {
275
- // Protect against calling any methods while an outstanding promise exists.
276
-
277
- // Call the original function preserving the correct "this" context.
278
- const result = Reflect.apply(value, this, args);
279
- // If the result is a promise, wrap it to clear the outstanding lock on await.
280
- if (result instanceof Promise) {
281
- return new Proxy(result, {
282
- get(promiseTarget, promiseProp, promiseReceiver) {
283
- // When the promise's then property is accessed (i.e. when it's awaited),
284
- // clear the outstanding promise so that subsequent method calls are allowed.
285
- if (promiseProp === "then") {
286
- outstandingPromiseError = null;
287
- }
288
- const inner = Reflect.get(promiseTarget, promiseProp, promiseReceiver).bind(promiseTarget);
289
- if (promiseProp === "catch") {
290
- return wrappedFunction(inner);
291
- }
292
- return inner;
293
- }
294
- });
295
- }
296
- return result;
297
- }
298
- }
299
-
300
- return new Proxy(instance, {
301
- get(target, prop, receiver) {
302
- // throw an error if an outstanding promise exists.
303
- if (outstandingPromiseError !== null) {
304
- throw outstandingPromiseError;
305
- }
306
- // Use Reflect.get to get the property value.
307
- const value = Reflect.get(target, prop, receiver);
308
-
309
- // If the property is a function, return a wrapped function.
310
- if (typeof value === "function") {
311
- return wrappedFunction(value);
312
- }
313
- // For non-function properties, just return the value.
314
- return value;
315
- }
316
- });
317
- }
318
-
319
- export function tryParseJSON<T>(json: string): T | undefined {
320
- try {
321
- return JSON.parse(json);
322
- } catch {
323
- return undefined;
324
- }
325
- }
326
-
327
-
328
- export class TypedGenerator<T extends [any, any][]> {
329
- static checker<T extends [any, any][]>() {
330
- return {
331
- /** `const value1 = asV(1, yield asY(0, value0))` */
332
- asV<I extends number>(index: I, args: T[I][0]): T[I][0] { return args; },
333
- /** `const value1 = asV(1, yield asY(0, value0))` */
334
- asY<I extends number>(index: I, ret: T[I][1]): T[I][1] { return ret; },
335
- }
336
- }
337
- static wrapper<T extends [any, any][]>() {
338
- return <A extends any[]>(factory: (...args: A) => Generator<any, any, any>) => {
339
- return (...args: A) => new TypedGenerator<T>(factory(...args));
340
- };
341
- }
342
- constructor(private inner: Generator<any, any, any>, private index = 0) { }
343
- async next<I extends number>(index: I, ...args: T[I][0] extends void ? [] : [T[I][0]]): Promise<(
344
- T extends [...any[], T[I]] ? IteratorReturnResult<T[I][1]> : IteratorYieldResult<T[I][1]>
345
- )> {
346
- if (index !== this.index) throw new Error("Invalid index");
347
- this.index++;
348
- return this.inner.next(...args) as any;
349
- }
350
- return(value: any): any { this.inner.return(value); }
351
- throw(e: any): any { this.inner.throw(e); }
352
- }
353
-
354
- export async function mapAsync<T, U, V>(array: T[], callback: (this: V, value: T, index: number, array: T[]) => Promise<U>, thisArg?: any): Promise<U[]> {
355
- const results = new Array(array.length);
356
- for (let index = 0; index < array.length; index++) {
357
- results[index] = await callback.call(thisArg, array[index] as T, index, array);
358
- }
359
- return results;
360
- };
361
-
362
- export async function filterAsync<T, V>(array: T[], callback: (this: V, value: T, index: number, array: T[]) => Promise<boolean>, thisArg?: any): Promise<T[]> {
363
- const results = [];
364
- for (let index = 0; index < array.length; index++) {
365
- if (await callback.call(thisArg, array[index] as T, index, array)) {
366
- results.push(array[index]);
367
- }
368
- }
369
- return results as any;
370
- }
371
-
372
-
373
- export function truthy<T>(
374
- obj: T
375
- ): obj is Exclude<T, false | null | undefined | 0 | '' | void> {
376
- return !!obj;
377
- }
378
-
379
-
380
- export class UserError extends Error {
381
- constructor(message: string) {
382
- super(message);
383
- this.name = "UserError";
384
- }
385
- }
386
- /**
387
- * This returns the resolved path relative to the executing code file,
388
- * however it uses path.resolve, not require.resolve.
389
- */
390
-
391
- export function dist_resolve(filepath: string) {
392
- const filename = typeof module === "undefined" ? fileURLToPath(import.meta.url) : module.filename;
393
- return path.resolve(path.dirname(filename), filepath);
394
- }
395
- export function dist_require_resolve(filepath: string) {
396
- const filename = typeof module === "undefined" ? fileURLToPath(import.meta.url) : module.filename;
397
- return createRequire(filename).resolve(filepath);
398
- }
399
-
400
-
401
- export declare interface JsonArray extends Array<JsonValue> { }
402
- export declare type JsonObject = { [Key in string]?: JsonValue; };
403
- export declare type JsonValue = string | number | boolean | JsonObject | JsonArray | null | Date;
@@ -1,269 +0,0 @@
1
- import { z, ZodEffects } from "zod";
2
- import { StateObject } from "../StateObject";
3
- import { Prisma } from "@prisma/client";
4
- import { STREAM_ENDED } from "../streamer";
5
-
6
-
7
-
8
- export namespace ZodAssert {
9
- type Exactly<T, X> = T & Record<Exclude<keyof X, keyof T>, never>
10
- /**
11
- * assert zod on the request body (`state.data`).
12
- *
13
- * You cannot assert data on a stream or ignore body format.
14
- *
15
- * Attempting to do so will cause the schema argument to expect a string
16
- * containing an error message informing the developer of this.
17
- */
18
- export function data<T extends
19
- S extends StateObject<"ignore" | "stream"> ? z.ZodUnknown :
20
- S extends StateObject<"www-form-urlencoded"> ? z.ZodObject<any> :
21
- z.ZodTypeAny, S extends StateObject
22
- >(
23
- state: S,
24
- schema:
25
- S extends StateObject<"ignore" | "stream">
26
- ? "it is invalid to assert data for this body format"
27
- : (zod: S extends StateObject<"www-form-urlencoded"> ? Z2<"STRING"> : Z2<"JSON">) => T,
28
- /**
29
- * If the data does not match the schema, this function is called sync'ly with the zod error.
30
- * Whatever it returns is sent as a utf8 string response body.
31
- * @param error The zod error.
32
- * @returns A string to send as the response body, or void to use the default error message.
33
- */
34
- onError?: (error: z.ZodError<any>) => string | void
35
- ): asserts state is S & ({ data: z.infer<T> }) {
36
- _zodAssert("data", state, schema as any, onError);
37
- }
38
-
39
- /**
40
- * assert zod on the route path match groups (`state.pathParams`).
41
- *
42
- * Sends a 404 response instead of 400.
43
- */
44
- export function pathParams<
45
- T extends Exactly<{ [k in keyof S["pathParams"]]: z.ZodTypeAny; }, T>,
46
- S extends StateObject,
47
- >(
48
- state: S, schemaShape: (zod: Z2<"STRING">) => T,
49
- /**
50
- * If the data does not match the schema, this function is called sync'ly with the zod error.
51
- * Whatever it returns is sent as a utf8 string response body.
52
- * @param error The zod error.
53
- * @returns A string to send as the response body, or void to use the default error message.
54
- */
55
- onError?: (error: z.ZodError<any>) => string | void
56
- ): asserts state is S & ({ pathParams: z.infer<z.ZodObject<T>> }) {
57
- _zodAssert("pathParams", state, schemaShape, onError);
58
- }
59
-
60
- /**
61
- * assert zod on the url query parameters (`state.queryParams`).
62
- *
63
- * This is the only place that the query parameters are declared, so the keys are not checked.
64
- *
65
- * If no value is specified, it defaults to an empty string
66
- * (`?key&key=&key` becomes `key: ["", "", ""]`).
67
- *
68
- * In zod: `z.object({ key: z.array(z.string()).optional() })`
69
- */
70
- export function queryParams<T extends Record<string, z.ZodTypeAny>, S extends StateObject>(
71
- state: S, schemaShape: (zod: Z2<"STRING">) => T,
72
- /**
73
- * If the data does not match the schema, this function is called sync'ly with the zod error.
74
- * Whatever it returns is sent as a utf8 string response body.
75
- * @param error The zod error.
76
- * @returns A string to send as the response body, or void to use the default error message.
77
- */
78
- onError?: (error: z.ZodError<any>) => string | void
79
- ): asserts state is S & ({ queryParams: z.infer<z.ZodObject<T>> }) {
80
- _zodAssert("queryParams", state, schemaShape, onError);
81
- }
82
- /**
83
- * assert zod on the data passed to this function and returns the cleaned data.
84
- * If onError is not specified, or does not return STREAM_ENDED, an error 500 is sent.
85
- * Either way, zodAssert will throw STREAM_ENDED immediately.
86
- *
87
- * If onError is not specified, the error will be printed with console.log.
88
- */
89
- export function any<T extends z.ZodTypeAny, S extends StateObject>(
90
- state: S, schemaShape: (zod: Z2<"JSON">) => T, data: z.input<T>,
91
- /**
92
- * If the data does not match the schema, this function is called sync'ly with the zod error.
93
- * If it does not return the STREAM_ENDED symbol indicating an appropriate error response was sent,
94
- * zodAssert will throw with a 500 error.
95
- *
96
- * It is perfectly valid to throw an error from this function,
97
- * as zodAssert will throw STREAM_ENDED immediately if it returns.
98
- *
99
- * @param error The zod error.
100
- * @returns STREAM_ENDED to indicate an appropriate error response was sent, or undefined.
101
- */
102
- onError?: (error: z.ZodError<any>) => void | symbol
103
- ): z.infer<T> { return _zodAssertAny("any", state, schemaShape, data, onError); }
104
- /**
105
- * assert zod on the data passed to this function and returns the cleaned data.
106
- * If onError is not specified, or does not return STREAM_ENDED, an error 500 is sent.
107
- * Either way, zodAssert will throw STREAM_ENDED immediately.
108
- *
109
- * If onError is not specified, the error will be printed with console.log.
110
- */
111
- export function response<T extends z.ZodTypeAny, S extends StateObject>(
112
- state: S, schemaShape: (zod: Z2<"JSON">) => T, data: z.input<T>,
113
- /**
114
- * If the data does not match the schema, this function is called sync'ly with the zod error.
115
- * If it does not return the STREAM_ENDED symbol indicating an appropriate error response was sent,
116
- * zodAssert will throw with a 500 error.
117
- *
118
- * It is perfectly valid to throw an error from this function,
119
- * as zodAssert will throw STREAM_ENDED immediately if it returns.
120
- *
121
- * @param error The zod error.
122
- * @returns STREAM_ENDED to indicate an appropriate error response was sent, or undefined.
123
- */
124
- onError?: (error: z.ZodError<any>) => void | symbol
125
- ): z.infer<T> { return _zodAssertAny("response", state, schemaShape, data, onError); }
126
-
127
-
128
- }
129
- // type t = number extends PrismaPayloadScalars<"Roles">["role_id"] ? FieldTypeNumberSelector<"JSON"> : false;
130
- export interface Z2<T extends FieldTypeGroups> extends _zod {
131
- /**
132
- * Tags the resulting value as being from the specified table field.
133
- *
134
- * The third argument is a string literal that specifies the field type based on the selected field.
135
- *
136
- * "string" and "parse-number"
137
- * - will use decodeURIComponent on the value.
138
- * - Require a string length at least one
139
- *
140
- * You can use .optional(), .nullable(), .nulish() after this to make the field optional.
141
- */
142
- prismaField<Table extends Prisma.ModelName, Field extends keyof PrismaPayloadScalars<Table>>(
143
- table: Table, field: Field,
144
- fieldtype:
145
- string extends (PrismaPayloadScalars<Table>[Field]) ? FieldTypeStringSelector<T> :
146
- number extends (PrismaPayloadScalars<Table>[Field]) ? FieldTypeNumberSelector<T> :
147
- boolean extends (PrismaPayloadScalars<Table>[Field]) ? FieldTypeBooleanSelector<T> :
148
- never,
149
- nullable?: null extends PrismaPayloadScalars<Table>[Field] ? true : false
150
- ): ZodEffects<any, PrismaField<Table, Field>, PrismaPayloadScalars<Table>[Field]>;
151
-
152
-
153
- authUser(): z.ZodNullable<z.ZodObject<{
154
- user_id: z.ZodNumber;
155
- isAdmin: z.ZodBoolean;
156
- username: z.ZodString;
157
- sessionId: z.ZodNumber;
158
- }>>;
159
-
160
- }
161
-
162
- export const _zodAssertAny = (
163
- input: string,
164
- state: StateObject,
165
- schema: (z: Z2<any>) => z.ZodTypeAny | Record<string, z.ZodTypeAny>,
166
- inputdata: any,
167
- onError?: (error: z.ZodError<any>) => symbol | void,
168
- ) => {
169
- const schema2: any = schema(makeZ2(input));
170
- const { success, data, error } = z.any().pipe(schema2).safeParse(inputdata);
171
- if (!success) {
172
- if (!onError) error.issues.forEach(e => console.log(input, e.message, e.path));
173
- if (onError?.(error) === STREAM_ENDED) throw STREAM_ENDED;
174
- throw state.sendEmpty(500, { "x-reason": input });
175
- }
176
- return data;
177
- }
178
-
179
- const _zodAssert = (
180
- input: "data" | "pathParams" | "queryParams",
181
- state: StateObject,
182
- schema: (z: Z2<any>) => z.ZodTypeAny | Record<string, z.ZodTypeAny>,
183
- onError?: (error: z.ZodError<any>) => string | void,
184
- ) => {
185
-
186
- // this checks if the schema is a string, since that is used to indicate invalid use cases of zodAssert,
187
- // and if it's correctly typed, then this is the best way to find it.
188
- if (typeof schema === "string") {
189
- console.log(new Error(`schema is a string: ${schema}`));
190
- throw state.sendEmpty(500);
191
- }
192
- const schema2: any = schema(makeZ2(input));
193
-
194
- const { success, data, error } = z.any().pipe(
195
- input === "data" ? schema2 : z.object(schema2)
196
- ).safeParse(state[input]);
197
- if (!success) {
198
- if (!onError) error.issues.forEach(e => console.log(input, e.message, e.path));
199
- const status = input === "pathParams" ? 404 : 400;
200
- const message = onError?.(error);
201
- if (typeof message === "string")
202
- throw state.sendString(status, { "x-reason": input }, message ?? "", "utf8");
203
- else
204
- throw state.sendEmpty(status, { "x-reason": input });
205
- }
206
- state[input] = data;
207
- };
208
-
209
- type _zod = typeof z;
210
- type ExtraFieldType = "string" | "number" | "parse-number" | "boolean" | "parse-boolean";
211
- type FieldTypeGroups = "STRING" | "JSON";
212
- type FieldTypeStringSelector<T extends FieldTypeGroups> = T extends "STRING" ? "string" : "string";
213
- type FieldTypeNumberSelector<T extends FieldTypeGroups> = T extends "STRING" ? "parse-number" : "number";
214
- type FieldTypeBooleanSelector<T extends FieldTypeGroups> = T extends "STRING" ? "parse-boolean" : "boolean";
215
-
216
- export const Z2 = makeZ2("any");
217
-
218
-
219
- function makeZ2<T extends FieldTypeGroups>(input: "data" | "pathParams" | "queryParams" | "any" | "response" | string): Z2<T> {
220
- const z2 = Object.create(z);
221
- z2.prismaField = prismaField;
222
- z2.authUser = () => z.object({
223
- user_id: z.number(),
224
- isAdmin: z.boolean(),
225
- username: z.string(),
226
- sessionId: z.number(),
227
- }).nullable();
228
- return z2;
229
- }
230
-
231
-
232
- function prismaField(table: any, field: any, fieldtype: ExtraFieldType, nullable?: boolean): any {
233
- const check = (() => {
234
- switch (fieldtype) {
235
- case "string":
236
- return z.string()
237
- .refine(x => x.length, { message: "String must have length" });
238
- case "parse-number":
239
- return z.string().min(1)
240
- .pipe(z.bigint({ coerce: true }))
241
- .pipe(z.number({ coerce: true }).finite())
242
- .refine(x => !isNaN(x), { message: "Invalid number" });
243
- case "parse-boolean":
244
- return z.enum(["true", "false", "null"]).transform(x => x === "null" ? null : x === "true");
245
- case "boolean":
246
- return z.boolean();
247
- case "number":
248
- return z.number().finite().int();
249
- default:
250
- throw new Error("Invalid field type");
251
- }
252
- })();
253
- return nullable ? check.nullable() : check;
254
-
255
- }
256
-
257
-
258
- // const zodAssertGlobal = {
259
- // data: _zodAssert.bind(null, "data"),
260
- // pathParams: _zodAssert.bind(null, "pathParams"),
261
- // queryParams: _zodAssert.bind(null, "queryParams"),
262
- // response: _zodAssertAny.bind(null, "response"),
263
- // any: _zodAssertAny.bind(null, "any"),
264
- // zod: makeZ2("any"),
265
- // };
266
-
267
- // declare global { type ZodAssert = typeof zodAssertGlobal; }
268
-
269
- // type t = ZodAssert;
File without changes