@spirobel/mininext 0.7.6 → 0.8.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.
- package/README.md +4 -18
- package/dist/backend/html.d.ts +46 -0
- package/dist/backend/html.js +233 -0
- package/dist/backend/miniresolve.d.ts +8 -0
- package/dist/backend/miniresolve.js +60 -0
- package/dist/frontend/minidom.d.ts +8 -0
- package/dist/frontend/minidom.js +29 -0
- package/dist/frontend/minirender.d.ts +10 -0
- package/dist/frontend/minirender.js +136 -0
- package/dist/frontend/miniresolve.d.ts +9 -0
- package/dist/frontend/miniresolve.js +143 -0
- package/dist/frontend/minirouter.d.ts +18 -0
- package/dist/frontend/minirouter.js +107 -0
- package/dist/minicache.d.ts +26 -0
- package/dist/minicache.js +44 -0
- package/dist/mininext.d.ts +41 -22
- package/dist/mininext.js +127 -229
- package/package.json +2 -2
- package/dist/html.d.ts +0 -73
- package/dist/html.js +0 -288
- package/dist/url.d.ts +0 -282
- package/dist/url.js +0 -533
package/dist/url.js
DELETED
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import { htmlResponder, html, json, dangerjson, HtmlString } from "./html";
|
|
2
|
-
import { BasedHtml, } from "./html";
|
|
3
|
-
/**
|
|
4
|
-
* A helper function that helps narrow unknown objects
|
|
5
|
-
* @param object - the object of type unknown that is to be narrowed
|
|
6
|
-
* @param key - the key that may or may not exist in object
|
|
7
|
-
* @returns true if the key is present and false if not
|
|
8
|
-
* @example
|
|
9
|
-
* ``` js
|
|
10
|
-
* has(this.form.formJson, "formName") &&
|
|
11
|
-
* this.form.formJson.formName === this.form.formName
|
|
12
|
-
* ```
|
|
13
|
-
* https://stackoverflow.com/questions/70028907/narrowing-an-object-of-type-unknown
|
|
14
|
-
*/
|
|
15
|
-
export function has(object, key) {
|
|
16
|
-
return typeof object === "object" && object !== null && key in object;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Mini - the data object can be filled with url.data
|
|
20
|
-
* @example
|
|
21
|
-
* ``` js
|
|
22
|
-
* const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export class Mini {
|
|
26
|
-
html;
|
|
27
|
-
css;
|
|
28
|
-
json;
|
|
29
|
-
dangerjson;
|
|
30
|
-
data;
|
|
31
|
-
req;
|
|
32
|
-
head;
|
|
33
|
-
headers;
|
|
34
|
-
options;
|
|
35
|
-
deliver;
|
|
36
|
-
route;
|
|
37
|
-
params;
|
|
38
|
-
form;
|
|
39
|
-
requrl;
|
|
40
|
-
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirect_static) */
|
|
41
|
-
redirect;
|
|
42
|
-
constructor(mini, data) {
|
|
43
|
-
Object.assign(this, mini);
|
|
44
|
-
this.html = (html);
|
|
45
|
-
this.css = (html);
|
|
46
|
-
this.json = (json);
|
|
47
|
-
this.dangerjson = (dangerjson);
|
|
48
|
-
this.data = data;
|
|
49
|
-
this.deliver = url.deliver;
|
|
50
|
-
this.form.onPostSubmit = (cb) => {
|
|
51
|
-
if (this.form.formName) {
|
|
52
|
-
if (this.form.formData &&
|
|
53
|
-
this.form.formData.get("formName") === this.form.formName) {
|
|
54
|
-
return cb();
|
|
55
|
-
}
|
|
56
|
-
else if (has(this.form.formJson, "formName") &&
|
|
57
|
-
this.form.formJson.formName === this.form.formName) {
|
|
58
|
-
return cb();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (this.form.post) {
|
|
62
|
-
return cb();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
export class url {
|
|
68
|
-
static websocket = undefined;
|
|
69
|
-
static server;
|
|
70
|
-
static routes = {};
|
|
71
|
-
// direct mapping of "url string" -> function leads to Html Response
|
|
72
|
-
static direct_handlers_html = new Map();
|
|
73
|
-
// An array of the uncompiled frontend files, example frontends[0] = "index.tsx" -> frontend/index.tsx (from the project root)
|
|
74
|
-
static frontends = [];
|
|
75
|
-
static svgs = [];
|
|
76
|
-
/**
|
|
77
|
-
* This function takes care of bundling your svg (icons?) into the webapp
|
|
78
|
-
* they will have a hash in the name to break the cache when needed
|
|
79
|
-
* @param svgFilePath first place to look: svgs folder in the same path the calling file, after that path from project root.
|
|
80
|
-
* @param options ResponseInit, default headers for an svg
|
|
81
|
-
* @returns url to the svg
|
|
82
|
-
*/
|
|
83
|
-
static svg(svgFilePath, options = {
|
|
84
|
-
headers: {
|
|
85
|
-
"Content-Type": "image/svg+xml",
|
|
86
|
-
"Content-Disposition": "attachment",
|
|
87
|
-
},
|
|
88
|
-
}) {
|
|
89
|
-
const stack = new Error().stack?.split("\n");
|
|
90
|
-
let callerPath = "";
|
|
91
|
-
if (stack) {
|
|
92
|
-
callerPath = stack[2].slice(stack[2].lastIndexOf("(") + 1, stack[2].lastIndexOf(".") + 3);
|
|
93
|
-
callerPath = callerPath.slice(callerPath.search("at") + 2).trim();
|
|
94
|
-
}
|
|
95
|
-
const position = url.svgs.length;
|
|
96
|
-
//we register the svg for bundleing.
|
|
97
|
-
url.svgs.push({
|
|
98
|
-
svgFilePath,
|
|
99
|
-
callerPath,
|
|
100
|
-
options,
|
|
101
|
-
position: url.svgs.length,
|
|
102
|
-
});
|
|
103
|
-
//this will be filled in by the bundling step.
|
|
104
|
-
var foundSvg = Object.entries(bundledSVGs).find(([key, value]) => value.position === position);
|
|
105
|
-
return foundSvg && foundSvg[0];
|
|
106
|
-
}
|
|
107
|
-
static frontend(frontendFilePath, snippet) {
|
|
108
|
-
const stack = new Error().stack?.split("\n");
|
|
109
|
-
let callerPath = "";
|
|
110
|
-
if (stack) {
|
|
111
|
-
callerPath = stack[2].slice(stack[2].lastIndexOf("(") + 1, stack[2].lastIndexOf(".") + 3);
|
|
112
|
-
callerPath = callerPath.slice(callerPath.search("at") + 2).trim();
|
|
113
|
-
}
|
|
114
|
-
const position = url.frontends.length;
|
|
115
|
-
//we register the frontend for bundleing.
|
|
116
|
-
url.frontends.push({ frontendFilePath, callerPath, position });
|
|
117
|
-
//this will be filled in by the bundling step.
|
|
118
|
-
const bundledFrontend = Object.entries(bundledFrontends).find(([key, value]) => value.position === position);
|
|
119
|
-
if (!bundledFrontend)
|
|
120
|
-
return;
|
|
121
|
-
const scriptUrl = bundledFrontend[0];
|
|
122
|
-
if (snippet instanceof BasedHtml || !snippet) {
|
|
123
|
-
return html ` ${snippet}
|
|
124
|
-
<script type="module" src="${scriptUrl}"></script>`; // return an html script tag with the index hash
|
|
125
|
-
}
|
|
126
|
-
return (mini) => {
|
|
127
|
-
return mini.html `${snippet}
|
|
128
|
-
<script type="module" src="${scriptUrl}"></script>`;
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* This is used by the frontend bundler in order to find all frontends and their corresponding script files.
|
|
133
|
-
*/
|
|
134
|
-
static getFrontends() {
|
|
135
|
-
return url.frontends;
|
|
136
|
-
}
|
|
137
|
-
static getSvgs() {
|
|
138
|
-
return url.svgs;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* tool to expose data to a frontend as a global variable.
|
|
142
|
-
* @param name this will be added as window.name to the window object in the frontend
|
|
143
|
-
* @param value this will be parsed as json in the frontend and asigned as follows: window.name = JSON.parsed(value)
|
|
144
|
-
* @returns the script tag to be embeded in the html response
|
|
145
|
-
*
|
|
146
|
-
* @example
|
|
147
|
-
* ``` js
|
|
148
|
-
* //backend
|
|
149
|
-
* url.deliver("user", userData); // window.user = JSON.parse(userData)
|
|
150
|
-
* //frontend
|
|
151
|
-
* const user = window["user"];
|
|
152
|
-
* ```
|
|
153
|
-
* if you want to use types, declare them like so in your frontend code:
|
|
154
|
-
* ``` ts
|
|
155
|
-
* declare global {
|
|
156
|
-
* var user: string;
|
|
157
|
-
*}
|
|
158
|
-
* ```
|
|
159
|
-
*/
|
|
160
|
-
static deliver(name, value) {
|
|
161
|
-
return html ` <script type="application/json" id="${name}">
|
|
162
|
-
${dangerjson `${value}`}
|
|
163
|
-
</script>
|
|
164
|
-
|
|
165
|
-
<script>
|
|
166
|
-
window["${name}"] = JSON.parse(
|
|
167
|
-
document.getElementById("${name}").innerHTML
|
|
168
|
-
);
|
|
169
|
-
</script>`;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* @param dataHandler the function that prepares the data for the handlers
|
|
173
|
-
* @example const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
|
|
174
|
-
* @returns
|
|
175
|
-
*/
|
|
176
|
-
static data(dataMaker) {
|
|
177
|
-
return {
|
|
178
|
-
/**
|
|
179
|
-
* @param dataHandler the function that prepares the data for the handlers
|
|
180
|
-
* @example const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
|
|
181
|
-
* @returns
|
|
182
|
-
*/
|
|
183
|
-
handler: (dataHandler) => {
|
|
184
|
-
return async (oldmini) => {
|
|
185
|
-
const data = await dataMaker(oldmini);
|
|
186
|
-
const mini = new Mini(oldmini, data);
|
|
187
|
-
const unresolvedDataHandler = await dataHandler(mini); // passing mini
|
|
188
|
-
if (unresolvedDataHandler instanceof HtmlString) {
|
|
189
|
-
return await unresolvedDataHandler.resolve(mini);
|
|
190
|
-
}
|
|
191
|
-
return unresolvedDataHandler;
|
|
192
|
-
};
|
|
193
|
-
},
|
|
194
|
-
dataMaker,
|
|
195
|
-
/**
|
|
196
|
-
* use this to **specify the input type for the functions**,
|
|
197
|
-
*
|
|
198
|
-
* that you want to use in the HtmlHandlers that follow this **data blend!**
|
|
199
|
-
* @example type lol = typeof MaybeLoggedIn.$Mini
|
|
200
|
-
*/
|
|
201
|
-
$Mini: {
|
|
202
|
-
data: "DONT USE THIS DIRECTLY, ya goofball. This is just to infer the Mini type",
|
|
203
|
-
},
|
|
204
|
-
/**
|
|
205
|
-
* use this to **specify the input type for the functions**,
|
|
206
|
-
*
|
|
207
|
-
* that you want to use in the Htmlhandlers that follow this **data blend!**
|
|
208
|
-
* @example type haha = Mini<typeof MaybeLoggedIn.$Data>
|
|
209
|
-
*/
|
|
210
|
-
$Data: {
|
|
211
|
-
data: "DONT USE THIS DIRECTLY, ya goofball. This is just to infer the Mini type",
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
static set(entries, handler) {
|
|
216
|
-
function addUrl(entryUrl, entryHandler) {
|
|
217
|
-
for (const u of url.generateVariations(entryUrl)) {
|
|
218
|
-
url.direct_handlers_html.set(u, entryHandler);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
if (typeof entries === "string" && handler) {
|
|
222
|
-
addUrl(entries, handler);
|
|
223
|
-
}
|
|
224
|
-
if (typeof entries !== "string" && "routes" in entries) {
|
|
225
|
-
url.routes = entries.routes;
|
|
226
|
-
}
|
|
227
|
-
if (typeof entries !== "string" && !("routes" in entries))
|
|
228
|
-
for (const [entryUrl, entryHandler] of entries) {
|
|
229
|
-
addUrl(entryUrl, entryHandler);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* wrap your handlers in this if you mutate something to prevent CSRF issues.
|
|
234
|
-
* @param handler - normal html handler with mini as the argument
|
|
235
|
-
* @returns a wrapped html handler that will only be called when the request is post
|
|
236
|
-
*/
|
|
237
|
-
static post(handler) {
|
|
238
|
-
return (mini) => {
|
|
239
|
-
if (mini.form.post) {
|
|
240
|
-
return handler(mini);
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
return no_post_warning;
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* wrap your handlers in this if you mutate something to prevent CSRF issues.
|
|
249
|
-
* @param handler - normal html handler with mini as the argument
|
|
250
|
-
* @returns a wrapped html handler that will only be called when the request is post and contains a json body
|
|
251
|
-
*/
|
|
252
|
-
static postJson(handler) {
|
|
253
|
-
return (mini) => {
|
|
254
|
-
if (mini.form.formJson) {
|
|
255
|
-
return handler(mini);
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
return no_post_warning;
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* wrap your handlers in this if you mutate something to prevent CSRF issues.
|
|
264
|
-
* @param handler - normal html handler with mini as the argument
|
|
265
|
-
* @returns a wrapped html handler that will only be called when the request is post and contains a FormData body
|
|
266
|
-
*/
|
|
267
|
-
static postFormData(handler) {
|
|
268
|
-
return (mini) => {
|
|
269
|
-
if (mini.form.formData) {
|
|
270
|
-
return handler(mini);
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
return no_post_warning;
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* This is useful to decouple forms from routes.
|
|
279
|
-
* @param name name of the form - mini.form.onPostSubmit() will only be called if a (possibly hidden) field called formName matches this
|
|
280
|
-
* @param handler just like a normal handler (aka you can return the form as a HtmlString), but you can optionally return additional data in formInfo
|
|
281
|
-
* @returns - { formResponse: result of the handler, formInfo?: some info about the form. Totally up to you}
|
|
282
|
-
*/
|
|
283
|
-
static namedForm(name, handler) {
|
|
284
|
-
return async (mini) => {
|
|
285
|
-
mini.form.formName = name;
|
|
286
|
-
mini.form.hiddenField = html `<input
|
|
287
|
-
type="hidden"
|
|
288
|
-
name="formName"
|
|
289
|
-
value="${name}"
|
|
290
|
-
/>`;
|
|
291
|
-
const namedFormResponse = await handler(mini);
|
|
292
|
-
let handlerResult = {};
|
|
293
|
-
if (typeof namedFormResponse !== "string" &&
|
|
294
|
-
namedFormResponse &&
|
|
295
|
-
"formResponse" in namedFormResponse) {
|
|
296
|
-
handlerResult.formResponse = await namedFormResponse.formResponse;
|
|
297
|
-
handlerResult.formInfo = namedFormResponse.formInfo;
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
handlerResult.formResponse = namedFormResponse;
|
|
301
|
-
}
|
|
302
|
-
delete mini.form.formName;
|
|
303
|
-
delete mini.form.hiddenField;
|
|
304
|
-
return handlerResult;
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* pass in all the query string parameter names that you want to preserve in the link
|
|
309
|
-
* @param Url - the url that you want to link to (example: "/login")
|
|
310
|
-
* @param qs - the query string parameters that you want to preserve in the link
|
|
311
|
-
* @param settings - key and string values that you want to set in the link
|
|
312
|
-
* @returns - the link that you can use in your html template
|
|
313
|
-
*/
|
|
314
|
-
static link(Url, qs = "", settings) {
|
|
315
|
-
return (mini) => {
|
|
316
|
-
return url.currylink(Url, qs, mini.req, settings);
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
static currylink(Url, qs, req, settings) {
|
|
320
|
-
if (!Array.isArray(qs)) {
|
|
321
|
-
qs = [qs];
|
|
322
|
-
}
|
|
323
|
-
// Create a new URL object from the current location
|
|
324
|
-
// https://github.com/whatwg/url/issues/531#issuecomment-1337050285
|
|
325
|
-
const GOOFY_HACK = "http://goofyhack.com";
|
|
326
|
-
const updatedUrl = new URL(Url, GOOFY_HACK);
|
|
327
|
-
for (const q of qs) {
|
|
328
|
-
// Use URLSearchParams to set the name query parameter
|
|
329
|
-
const reqParam = new URL(req.url).searchParams.get(q);
|
|
330
|
-
if (reqParam) {
|
|
331
|
-
updatedUrl.searchParams.set(q, reqParam);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
for (const key in settings) {
|
|
335
|
-
const value = settings[key];
|
|
336
|
-
if (value !== undefined && value !== null) {
|
|
337
|
-
updatedUrl.searchParams.set(key, value);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
// Return the updated URL as a string
|
|
341
|
-
return updatedUrl.toString().slice(GOOFY_HACK.length);
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* users expect links to work with or without a trailing slash.
|
|
345
|
-
* Developers expect that that links work with or without a preceding slash.
|
|
346
|
-
* We make sure that these expectations are met when using url.set and url.get.
|
|
347
|
-
* (by adding all the variations to the url.direct_handlers Map)
|
|
348
|
-
* @param {string} inputString - the url
|
|
349
|
-
* @returns {string[]} - returns array of variations (added slash in the beginning, added, removed slash at the end)
|
|
350
|
-
*/
|
|
351
|
-
static generateVariations(inputString) {
|
|
352
|
-
const variations = [];
|
|
353
|
-
// Special case for the index route
|
|
354
|
-
if (inputString === "/") {
|
|
355
|
-
variations.push("/");
|
|
356
|
-
return variations;
|
|
357
|
-
}
|
|
358
|
-
// Check if the string starts with a slash and add/remove variations accordingly
|
|
359
|
-
if (inputString.startsWith("/")) {
|
|
360
|
-
variations.push(inputString); // With leading slash
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
inputString = "/" + inputString;
|
|
364
|
-
variations.push(inputString); // With leading slash
|
|
365
|
-
}
|
|
366
|
-
// Check if the string ends with a slash and add/remove variations accordingly
|
|
367
|
-
if (inputString.endsWith("/")) {
|
|
368
|
-
variations.push(inputString.slice(0, -1)); // Without trailing slash
|
|
369
|
-
}
|
|
370
|
-
else {
|
|
371
|
-
variations.push(inputString + "/"); // With trailing slash
|
|
372
|
-
}
|
|
373
|
-
return variations;
|
|
374
|
-
}
|
|
375
|
-
static async handleWithMini(req, server, handler) {
|
|
376
|
-
if (!url.server)
|
|
377
|
-
url.server = server;
|
|
378
|
-
const miniurl = Object.freeze(new URL(req.url));
|
|
379
|
-
const reqPath = miniurl.pathname;
|
|
380
|
-
let redirectTarget = null;
|
|
381
|
-
let redirectStatus = undefined;
|
|
382
|
-
let handlerHead = undefined;
|
|
383
|
-
let handlerOptions = {
|
|
384
|
-
headers: {
|
|
385
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
386
|
-
},
|
|
387
|
-
};
|
|
388
|
-
const post = req.method === "POST";
|
|
389
|
-
let formJson;
|
|
390
|
-
let formData;
|
|
391
|
-
const urlencoded = (req.headers.get("Content-Type") + "").includes("application/x-www-form-urlencoded");
|
|
392
|
-
const multipart = (req.headers.get("Content-Type") + "").includes("multipart/form-data");
|
|
393
|
-
if (post && !urlencoded && !multipart) {
|
|
394
|
-
const length = Number(req.headers.get("content-length"));
|
|
395
|
-
const bodyNotEmpty = length > 0;
|
|
396
|
-
if (bodyNotEmpty) {
|
|
397
|
-
formJson = await req.json();
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
formJson = {};
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (post && (urlencoded || multipart)) {
|
|
404
|
-
formData = await req.formData();
|
|
405
|
-
}
|
|
406
|
-
//this is the source of mini
|
|
407
|
-
const mini = new Mini({
|
|
408
|
-
requrl: miniurl,
|
|
409
|
-
data: undefined,
|
|
410
|
-
req: req,
|
|
411
|
-
html,
|
|
412
|
-
css: html,
|
|
413
|
-
deliver: url.deliver,
|
|
414
|
-
route: reqPath,
|
|
415
|
-
params: new URL(req.url).searchParams,
|
|
416
|
-
json,
|
|
417
|
-
form: {
|
|
418
|
-
post,
|
|
419
|
-
urlencoded,
|
|
420
|
-
multipart,
|
|
421
|
-
formJson,
|
|
422
|
-
formData,
|
|
423
|
-
onPostSubmit(cb) {
|
|
424
|
-
if (post) {
|
|
425
|
-
return cb();
|
|
426
|
-
}
|
|
427
|
-
},
|
|
428
|
-
actionlink: (qs = "", settings) => url.link(reqPath, qs, settings),
|
|
429
|
-
},
|
|
430
|
-
dangerjson,
|
|
431
|
-
head: (head) => {
|
|
432
|
-
handlerHead = head;
|
|
433
|
-
},
|
|
434
|
-
headers: (headers, overwrite = false) => {
|
|
435
|
-
if (overwrite) {
|
|
436
|
-
handlerOptions.headers = headers;
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
handlerOptions.headers = {
|
|
440
|
-
...handlerOptions.headers,
|
|
441
|
-
...headers,
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
},
|
|
445
|
-
options: (options) => {
|
|
446
|
-
handlerOptions = options;
|
|
447
|
-
},
|
|
448
|
-
redirect: (url, status) => {
|
|
449
|
-
redirectTarget = url;
|
|
450
|
-
redirectStatus = status;
|
|
451
|
-
},
|
|
452
|
-
}, undefined);
|
|
453
|
-
const unresolved = await handler(mini); //passing mini
|
|
454
|
-
if (redirectTarget) {
|
|
455
|
-
return Response.redirect(redirectTarget, redirectStatus);
|
|
456
|
-
}
|
|
457
|
-
return htmlResponder(mini, unresolved, handlerHead, handlerOptions);
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* use this to set the Websocket object. Check out [the bun docs](https://bun.sh/docs/api/websockets) for more details.
|
|
461
|
-
* @param wsObject the websocketsocket object {@link WebSocketHandler}
|
|
462
|
-
*/
|
|
463
|
-
static setWebsocket(wsObject) {
|
|
464
|
-
url.websocket = wsObject;
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Send a message to all connected {@link ServerWebSocket} subscribed to a topic
|
|
468
|
-
* @param topic The topic to publish to
|
|
469
|
-
* @param message The data to send
|
|
470
|
-
* @returns 0 if the message was dropped, -1 if backpressure was applied, or the number of bytes sent.
|
|
471
|
-
*/
|
|
472
|
-
static publishHtml(topic, message) {
|
|
473
|
-
return url.server.publish(topic, message);
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Fetch handler that is called by the server when a request is made to any of the urls.
|
|
477
|
-
* @param {Request} req - The Request object.
|
|
478
|
-
* @return {Promise<Response>} - The Response object.
|
|
479
|
-
*/
|
|
480
|
-
static install() {
|
|
481
|
-
const transformedRouteObject = {};
|
|
482
|
-
for (const route in url.routes) {
|
|
483
|
-
//handle route object split by methods and pull them through mininext
|
|
484
|
-
const handler = url.routes[route];
|
|
485
|
-
if (typeof handler === "function") {
|
|
486
|
-
transformedRouteObject[route] = (req, server) => url.handleWithMini(req, server, handler);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
const newHandlerObject = {};
|
|
490
|
-
for (const HTTPmethod in handler) {
|
|
491
|
-
newHandlerObject[HTTPmethod] = (req, server) => url.handleWithMini(req, server, handler[HTTPmethod]);
|
|
492
|
-
}
|
|
493
|
-
transformedRouteObject[route] = newHandlerObject;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
for (const [route, handler] of url.direct_handlers_html) {
|
|
497
|
-
transformedRouteObject[route] = (req, server) => url.handleWithMini(req, server, handler);
|
|
498
|
-
}
|
|
499
|
-
for (const svgUrl in bundledSVGs) {
|
|
500
|
-
const resolvedSvg = bundledSVGs[svgUrl];
|
|
501
|
-
transformedRouteObject[svgUrl] = (req, server) => new Response(resolvedSvg.svgContent, resolvedSvg.options);
|
|
502
|
-
}
|
|
503
|
-
for (const frontendUrl in bundledFrontends) {
|
|
504
|
-
const resolvedFrontend = bundledFrontends[frontendUrl];
|
|
505
|
-
transformedRouteObject[frontendUrl] = (req, server) => new Response(resolvedFrontend.frontendContent, {
|
|
506
|
-
headers: {
|
|
507
|
-
"Content-Type": "application/javascript; charset=utf-8",
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
function fetchFunction(req, server) {
|
|
512
|
-
return new Response("No matching url found", { status: 404 });
|
|
513
|
-
}
|
|
514
|
-
return {
|
|
515
|
-
fetch: fetchFunction,
|
|
516
|
-
websocket: url.websocket,
|
|
517
|
-
routes: transformedRouteObject,
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
const no_post_warning = html `<div style="color:red;">
|
|
522
|
-
This method is only accessible through the POST method. Remember to make all
|
|
523
|
-
mutations (insert / update data in the database) only accessible via POST and
|
|
524
|
-
implement your session cookies like this:
|
|
525
|
-
<div
|
|
526
|
-
style="color:#0FFF50; width:800px; overflow:wrap; margin-left:30px; margin-top:20px; margin-bottom:20px;"
|
|
527
|
-
>
|
|
528
|
-
"Set-Cookie": sessionId=="some random string made with crypto.randomUUID()"
|
|
529
|
-
expires=Thu, 01 Jan 1970 00:00:00 GMT Secure; HttpOnly; SameSite=Strict;
|
|
530
|
-
path=/,
|
|
531
|
-
</div>
|
|
532
|
-
This is necessary to prevent CSRF issues.
|
|
533
|
-
</div>`;
|