@spirobel/mininext 0.2.0 → 0.2.2
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/dist/html.d.ts +55 -0
- package/dist/html.js +201 -0
- package/dist/mininext.d.ts +4 -0
- package/dist/mininext.js +115 -0
- package/dist/url.d.ts +191 -0
- package/dist/url.js +415 -0
- package/mininext/html.ts +262 -0
- package/mininext/mininext.ts +125 -0
- package/mininext/url.ts +516 -0
- package/package.json +8 -2
- package/bun.lockb +0 -0
- package/index.ts +0 -1
- package/tsconfig.json +0 -27
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { HandlerReturnType, HtmlHandler, LazyHandlerReturnType, Mini } from "./url";
|
|
2
|
+
export type HtmlStringValues<T = undefined> = HtmlString | HtmlString[] | string | number | HtmlHandler<T> | JsonString | LazyHandlerReturnType | undefined;
|
|
3
|
+
export type JsonStringValues<T = undefined> = HtmlStringValues<T> | {
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
};
|
|
6
|
+
export declare class HtmlString extends Array {
|
|
7
|
+
/**
|
|
8
|
+
* a HtmlString is by default resolved.
|
|
9
|
+
* if we we pass a function as a value to the html`` template string, it will be unresolved.
|
|
10
|
+
*/
|
|
11
|
+
resolved: boolean;
|
|
12
|
+
resolve<T>(mini: Mini<T>): Promise<this>;
|
|
13
|
+
flat(depth?: number): this;
|
|
14
|
+
}
|
|
15
|
+
export declare function html<X = undefined>(strings: TemplateStringsArray, ...values: HtmlStringValues<X>[]): HtmlString;
|
|
16
|
+
export declare class JsonString extends HtmlString {
|
|
17
|
+
}
|
|
18
|
+
export declare class DangerJsonInHtml extends HtmlString {
|
|
19
|
+
}
|
|
20
|
+
export declare const json: <X = undefined>(strings: TemplateStringsArray, ...values: JsonStringValues<X>[]) => JsonString;
|
|
21
|
+
export declare const dangerjson: <X = undefined>(strings: TemplateStringsArray, ...values: JsonStringValues<X>[]) => DangerJsonInHtml;
|
|
22
|
+
/**
|
|
23
|
+
* Set the default head for all pages. Can still be overwritten on a per page basis
|
|
24
|
+
* @param defaultHead - HtmlString
|
|
25
|
+
*
|
|
26
|
+
* @example Here is what a default head might look like:
|
|
27
|
+
* ```ts
|
|
28
|
+
*head(html` <title>hello hello</title> `);
|
|
29
|
+
* url.set([
|
|
30
|
+
* ["/", (mini) => mini.html`<h1>Hello world</h1>`],
|
|
31
|
+
* [
|
|
32
|
+
* "/bye",
|
|
33
|
+
* (mini) =>
|
|
34
|
+
* mini.html`<h1>Goodbye world</h1>${mini.head(
|
|
35
|
+
* mini.html` <title>bye bye</title>`
|
|
36
|
+
* )}`,
|
|
37
|
+
* ],
|
|
38
|
+
* ]);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function head(defaultHead: HtmlString): void;
|
|
42
|
+
export declare function htmlResponder(mini: Mini, maybeUnresolved: HandlerReturnType, head?: HtmlString, options?: ResponseInit): Promise<Response>;
|
|
43
|
+
/**
|
|
44
|
+
* Generic html error type guard
|
|
45
|
+
* @param submissionResult output of some function
|
|
46
|
+
* @returns boolean - true if the given object has a property called "error" and its value is an instance of HtmlString
|
|
47
|
+
*/
|
|
48
|
+
export declare function isError(submissionResult: any | {
|
|
49
|
+
error: HtmlString;
|
|
50
|
+
}): submissionResult is {
|
|
51
|
+
error: HtmlString;
|
|
52
|
+
};
|
|
53
|
+
declare global {
|
|
54
|
+
var Reloader: HtmlString | undefined;
|
|
55
|
+
}
|
package/dist/html.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
export class HtmlString extends Array {
|
|
2
|
+
/**
|
|
3
|
+
* a HtmlString is by default resolved.
|
|
4
|
+
* if we we pass a function as a value to the html`` template string, it will be unresolved.
|
|
5
|
+
*/
|
|
6
|
+
resolved = true;
|
|
7
|
+
async resolve(mini) {
|
|
8
|
+
if (this.resolved)
|
|
9
|
+
return this;
|
|
10
|
+
for (const [index, htmlPiece] of this.entries()) {
|
|
11
|
+
if (typeof htmlPiece === "function") {
|
|
12
|
+
let resolvedHtmlPiece = await htmlPiece(mini); //passing mini
|
|
13
|
+
if (resolvedHtmlPiece instanceof HtmlString) {
|
|
14
|
+
resolvedHtmlPiece = await resolvedHtmlPiece.resolve(mini);
|
|
15
|
+
}
|
|
16
|
+
// Replace the function with the resolved HTML piece in place
|
|
17
|
+
this[index] = resolvedHtmlPiece;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
this.resolved = true;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
flat(depth = 1) {
|
|
24
|
+
const flattened = super.flat(depth);
|
|
25
|
+
const newHtmlString = new this.constructor(...flattened);
|
|
26
|
+
newHtmlString.resolved = this.resolved;
|
|
27
|
+
return newHtmlString;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function html(strings, ...values) {
|
|
31
|
+
const htmlStringArray = new HtmlString();
|
|
32
|
+
htmlStringArray.resolved = true;
|
|
33
|
+
// Iterate over strings and values, alternating between them
|
|
34
|
+
for (const [index, string] of strings.entries()) {
|
|
35
|
+
htmlStringArray.push(string);
|
|
36
|
+
if (index < values.length) {
|
|
37
|
+
const value = values[index];
|
|
38
|
+
// we can pass arrays of HtmlString and they will get flattened automatically
|
|
39
|
+
if (Array.isArray(value) &&
|
|
40
|
+
value.every((val) => val instanceof HtmlString)) {
|
|
41
|
+
// If the value is an array of HtmlString objects, add the whole array as a single value
|
|
42
|
+
values[index] = value;
|
|
43
|
+
}
|
|
44
|
+
else if (typeof value === "function") {
|
|
45
|
+
htmlStringArray.resolved = false;
|
|
46
|
+
values[index] = value;
|
|
47
|
+
}
|
|
48
|
+
else if (value instanceof JsonString) {
|
|
49
|
+
values[index] = html `<div style="color:red;">
|
|
50
|
+
Please use dangerjson to include json in html. Untrusted input needs
|
|
51
|
+
to pass through a html template function to get escaped. You can do
|
|
52
|
+
html -> dangerjson -> html if you want!
|
|
53
|
+
</div>`;
|
|
54
|
+
}
|
|
55
|
+
else if (!(value instanceof HtmlString)) {
|
|
56
|
+
const notEmpty = value || "";
|
|
57
|
+
// values will be escaped by default
|
|
58
|
+
values[index] = Bun.escapeHTML(notEmpty + "");
|
|
59
|
+
}
|
|
60
|
+
htmlStringArray.push(values[index]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Flatten the HtmlString array to ensure all nested arrays are properly included
|
|
64
|
+
return htmlStringArray.flat();
|
|
65
|
+
}
|
|
66
|
+
export class JsonString extends HtmlString {
|
|
67
|
+
}
|
|
68
|
+
export class DangerJsonInHtml extends HtmlString {
|
|
69
|
+
}
|
|
70
|
+
function JsonTemplateProcessor(danger = false) {
|
|
71
|
+
const constructorr = danger
|
|
72
|
+
? () => new DangerJsonInHtml()
|
|
73
|
+
: () => new JsonString();
|
|
74
|
+
return function (strings, ...values) {
|
|
75
|
+
const jsonStringArray = constructorr();
|
|
76
|
+
jsonStringArray.resolved = true;
|
|
77
|
+
// Iterate over strings and values, alternating between them
|
|
78
|
+
for (const [index, string] of strings.entries()) {
|
|
79
|
+
jsonStringArray.push(string);
|
|
80
|
+
if (index < values.length) {
|
|
81
|
+
const value = values[index];
|
|
82
|
+
// we can pass arrays of HtmlString and they will get flattened automatically
|
|
83
|
+
if (Array.isArray(value) &&
|
|
84
|
+
value.every((val) => val instanceof HtmlString)) {
|
|
85
|
+
// If the value is an array of HtmlString objects, add the whole array as a single value
|
|
86
|
+
values[index] = value;
|
|
87
|
+
}
|
|
88
|
+
else if (typeof value === "function") {
|
|
89
|
+
jsonStringArray.resolved = false;
|
|
90
|
+
values[index] = value;
|
|
91
|
+
}
|
|
92
|
+
else if (!(value instanceof JsonString)) {
|
|
93
|
+
const notEmpty = value || "";
|
|
94
|
+
// values will be turned into a JSON string
|
|
95
|
+
values[index] = JSON.stringify(notEmpty);
|
|
96
|
+
}
|
|
97
|
+
jsonStringArray.push(values[index]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Flatten the HtmlString array to ensure all nested arrays are properly included
|
|
101
|
+
return jsonStringArray.flat();
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export const json = JsonTemplateProcessor();
|
|
105
|
+
export const dangerjson = JsonTemplateProcessor(true);
|
|
106
|
+
let default_head = html ` <title>mini-next</title>
|
|
107
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
108
|
+
<style>
|
|
109
|
+
/* CSS Reset */
|
|
110
|
+
* {
|
|
111
|
+
margin: 0;
|
|
112
|
+
padding: 0;
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Set the background color to black */
|
|
117
|
+
html,
|
|
118
|
+
body {
|
|
119
|
+
background-color: #000;
|
|
120
|
+
color: #fff; /* Set the default text color to white for better contrast */
|
|
121
|
+
}
|
|
122
|
+
</style>
|
|
123
|
+
<script>
|
|
124
|
+
/* prevent form resubmission */
|
|
125
|
+
if (window.history.replaceState) {
|
|
126
|
+
window.history.replaceState(null, null, window.location.href);
|
|
127
|
+
}
|
|
128
|
+
</script>`;
|
|
129
|
+
/**
|
|
130
|
+
* Set the default head for all pages. Can still be overwritten on a per page basis
|
|
131
|
+
* @param defaultHead - HtmlString
|
|
132
|
+
*
|
|
133
|
+
* @example Here is what a default head might look like:
|
|
134
|
+
* ```ts
|
|
135
|
+
*head(html` <title>hello hello</title> `);
|
|
136
|
+
* url.set([
|
|
137
|
+
* ["/", (mini) => mini.html`<h1>Hello world</h1>`],
|
|
138
|
+
* [
|
|
139
|
+
* "/bye",
|
|
140
|
+
* (mini) =>
|
|
141
|
+
* mini.html`<h1>Goodbye world</h1>${mini.head(
|
|
142
|
+
* mini.html` <title>bye bye</title>`
|
|
143
|
+
* )}`,
|
|
144
|
+
* ],
|
|
145
|
+
* ]);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export function head(defaultHead) {
|
|
149
|
+
default_head = defaultHead;
|
|
150
|
+
}
|
|
151
|
+
export async function htmlResponder(mini, maybeUnresolved, head = default_head, options = {
|
|
152
|
+
headers: {
|
|
153
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
154
|
+
},
|
|
155
|
+
}) {
|
|
156
|
+
if (!(maybeUnresolved instanceof HtmlString)) {
|
|
157
|
+
maybeUnresolved = html `${maybeUnresolved + ""}`;
|
|
158
|
+
}
|
|
159
|
+
if (maybeUnresolved instanceof DangerJsonInHtml) {
|
|
160
|
+
maybeUnresolved = html `<div style="color:red;">
|
|
161
|
+
Use json and not dangerjson. The purpose of dangerjson is to be explicit
|
|
162
|
+
when you embed unescaped json elements in an html document.
|
|
163
|
+
</div>`;
|
|
164
|
+
}
|
|
165
|
+
const definitelyResolved = await maybeUnresolved.resolve(mini);
|
|
166
|
+
const flattend = definitelyResolved.flat(Infinity);
|
|
167
|
+
if (!(maybeUnresolved instanceof JsonString)) {
|
|
168
|
+
flattend.unshift(/*html*/ `<!DOCTYPE html>
|
|
169
|
+
<html>
|
|
170
|
+
<head>
|
|
171
|
+
${Reloader} ${head}
|
|
172
|
+
</head>
|
|
173
|
+
<body>
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const headers = {
|
|
178
|
+
...options.headers,
|
|
179
|
+
...{ "Content-Type": "application/json; charset=utf-8" },
|
|
180
|
+
};
|
|
181
|
+
options.headers = headers;
|
|
182
|
+
}
|
|
183
|
+
async function* stepGen() {
|
|
184
|
+
let index = 0;
|
|
185
|
+
while (index < flattend.length) {
|
|
186
|
+
yield flattend[index++];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function Stream(a) {
|
|
190
|
+
return a;
|
|
191
|
+
}
|
|
192
|
+
return new Response(Stream(stepGen), options);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Generic html error type guard
|
|
196
|
+
* @param submissionResult output of some function
|
|
197
|
+
* @returns boolean - true if the given object has a property called "error" and its value is an instance of HtmlString
|
|
198
|
+
*/
|
|
199
|
+
export function isError(submissionResult) {
|
|
200
|
+
return ("error" in submissionResult && submissionResult.error instanceof HtmlString);
|
|
201
|
+
}
|
package/dist/mininext.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { url, Mini } from "./url";
|
|
2
|
+
import { html, isError, HtmlString, head } from "./html";
|
|
3
|
+
import { watch } from "fs/promises";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
const PROJECT_ROOT = import.meta.dir + "/../";
|
|
6
|
+
async function build(backendPath = "backend/backend.ts") {
|
|
7
|
+
await buildBackend(backendPath);
|
|
8
|
+
if (Bun.argv[2] === "dev") {
|
|
9
|
+
await devServer();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const myPlugin = {
|
|
13
|
+
name: "node buffer in the frontend",
|
|
14
|
+
setup(build) {
|
|
15
|
+
build.onResolve({ filter: /^buffer$/ }, (args) => {
|
|
16
|
+
const path_to_buffer_lib = path.resolve(PROJECT_ROOT, "node_modules/buffer/index.js");
|
|
17
|
+
if (path_to_buffer_lib)
|
|
18
|
+
return {
|
|
19
|
+
path: path_to_buffer_lib,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
async function buildBackend(backendPath = "backend/backend.ts") {
|
|
25
|
+
global.FrontendScriptUrls = [];
|
|
26
|
+
global.FrontendScripts = [];
|
|
27
|
+
global.bundledSVGs = {};
|
|
28
|
+
const i = await import(path.resolve(PROJECT_ROOT, backendPath));
|
|
29
|
+
for (const frontend of url.getFrontends()) {
|
|
30
|
+
const f = await buildFrontend(frontend);
|
|
31
|
+
FrontendScriptUrls.push("/" + f.url);
|
|
32
|
+
FrontendScripts.push(f.script);
|
|
33
|
+
}
|
|
34
|
+
for (const svgPath of url.getSvgPaths()) {
|
|
35
|
+
const parsedSvgPath = path.parse(svgPath);
|
|
36
|
+
const svgContent = Bun.file(path.join(PROJECT_ROOT + "/backend/", svgPath));
|
|
37
|
+
const svgHash = Bun.hash(await svgContent.arrayBuffer());
|
|
38
|
+
const svgUrl = `/${parsedSvgPath.name}-${svgHash}.svg`;
|
|
39
|
+
bundledSVGs[svgUrl] = {
|
|
40
|
+
svgContent: await svgContent.text(),
|
|
41
|
+
svgPath,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const res = await Bun.build({
|
|
45
|
+
entrypoints: [path.resolve(PROJECT_ROOT, backendPath)],
|
|
46
|
+
outdir: path.resolve(PROJECT_ROOT, "dist"),
|
|
47
|
+
naming: "backend.js",
|
|
48
|
+
minify: Bun.argv[2] === "dev" ? false : true, //production
|
|
49
|
+
target: "node",
|
|
50
|
+
define: {
|
|
51
|
+
FrontendScripts: JSON.stringify(FrontendScripts),
|
|
52
|
+
FrontendScriptUrls: JSON.stringify(FrontendScriptUrls),
|
|
53
|
+
bundledSVGs: JSON.stringify(bundledSVGs),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function buildFrontend(file) {
|
|
58
|
+
const result = await Bun.build({
|
|
59
|
+
entrypoints: [path.resolve(PROJECT_ROOT, `frontend/${file}`)],
|
|
60
|
+
outdir: path.resolve(PROJECT_ROOT, "dist"),
|
|
61
|
+
naming: "[name]-[hash].[ext]",
|
|
62
|
+
minify: Bun.argv[2] === "dev" ? false : true, //production
|
|
63
|
+
target: "browser",
|
|
64
|
+
plugins: [myPlugin],
|
|
65
|
+
});
|
|
66
|
+
if (!result?.outputs[0]?.path)
|
|
67
|
+
console.log(result);
|
|
68
|
+
const url = path.basename(result.outputs[0].path);
|
|
69
|
+
//results.push({ file, p });
|
|
70
|
+
return { url, script: await result.outputs[0].text() };
|
|
71
|
+
}
|
|
72
|
+
async function devServer() {
|
|
73
|
+
//start the reloader and tell browser to refresh once
|
|
74
|
+
await buildBackend();
|
|
75
|
+
let refreshed_once = false;
|
|
76
|
+
const server = Bun.serve({
|
|
77
|
+
port: 3001,
|
|
78
|
+
fetch(request) {
|
|
79
|
+
const success = server.upgrade(request);
|
|
80
|
+
return success
|
|
81
|
+
? new Response("Reloader works!")
|
|
82
|
+
: new Response("Reloader WebSocket upgrade error", { status: 400 });
|
|
83
|
+
},
|
|
84
|
+
websocket: {
|
|
85
|
+
open(ws) {
|
|
86
|
+
ws.subscribe("reloader");
|
|
87
|
+
if (!refreshed_once) {
|
|
88
|
+
ws.send("Reload!");
|
|
89
|
+
refreshed_once = true;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
message(ws, message) { }, // a message is received
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
async function watchAndBuild(dir) {
|
|
96
|
+
try {
|
|
97
|
+
//start the file watcher that will rebuild frontend on save
|
|
98
|
+
const watcher = watch(path.resolve(PROJECT_ROOT, dir), {
|
|
99
|
+
recursive: true,
|
|
100
|
+
});
|
|
101
|
+
for await (const event of watcher) {
|
|
102
|
+
buildBackend().then(() => {
|
|
103
|
+
// tell browser to refresh again because we saw a change
|
|
104
|
+
server.publish("reloader", "Reload!");
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
console.log(`mini-next dev server has trouble watching "./${dir}", does the directory exist?`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
watchAndBuild("frontend");
|
|
113
|
+
watchAndBuild("backend");
|
|
114
|
+
}
|
|
115
|
+
export { url, html, head, build, isError, HtmlString, Mini };
|
package/dist/url.d.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { html, json, dangerjson, HtmlString } from "./html";
|
|
2
|
+
import type { DangerJsonInHtml, JsonString, JsonStringValues } from "./html";
|
|
3
|
+
export type Form = {
|
|
4
|
+
post: boolean;
|
|
5
|
+
urlencoded: boolean;
|
|
6
|
+
multipart: boolean;
|
|
7
|
+
formJson?: any;
|
|
8
|
+
formData?: FormData;
|
|
9
|
+
formName?: string;
|
|
10
|
+
hiddenField?: HtmlString;
|
|
11
|
+
actionlink<Y = undefined>(qs?: string[] | string, settings?: LinkSettings): (mini: Mini<Y>) => string;
|
|
12
|
+
onPostSubmit<F>(cb: () => F): F | undefined;
|
|
13
|
+
};
|
|
14
|
+
export type DataMaker<X> = ((mini: Mini) => DataMakerReturnType<X>) | (() => DataMakerReturnType<X>);
|
|
15
|
+
export type DataMakerReturnType<X> = X | Promise<X>;
|
|
16
|
+
export type HandlerReturnType = JsonString | DangerJsonInHtml | HtmlString | string | void;
|
|
17
|
+
export type LazyHandlerReturnType = HandlerReturnType | Promise<HandlerReturnType>;
|
|
18
|
+
export type NamedForm<Z> = {
|
|
19
|
+
formResponse: LazyHandlerReturnType;
|
|
20
|
+
formInfo?: Z;
|
|
21
|
+
};
|
|
22
|
+
export type NamedFormHandlerReturnType<X> = HandlerReturnType | Promise<HandlerReturnType> | NamedForm<X> | Promise<NamedForm<X>>;
|
|
23
|
+
/**
|
|
24
|
+
* Mini - the data object can be filled with url.data
|
|
25
|
+
* @example
|
|
26
|
+
* ``` js
|
|
27
|
+
* const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare class Mini<X = undefined> {
|
|
31
|
+
html: typeof html<X>;
|
|
32
|
+
css: typeof html<X>;
|
|
33
|
+
json: typeof json<X>;
|
|
34
|
+
dangerjson: typeof dangerjson<X>;
|
|
35
|
+
data: X;
|
|
36
|
+
req: Request;
|
|
37
|
+
head: (head: HtmlString) => undefined;
|
|
38
|
+
headers: (headers: HeadersInit, overwrite?: boolean) => undefined;
|
|
39
|
+
options: (options: ResponseInit) => undefined;
|
|
40
|
+
deliver: typeof url.deliver;
|
|
41
|
+
route: string;
|
|
42
|
+
params: URLSearchParams;
|
|
43
|
+
form: Form;
|
|
44
|
+
constructor(mini: Mini<undefined | any>, data: X);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* HtmlHandler
|
|
48
|
+
* @param mini - the mini object
|
|
49
|
+
* @returns - return a partially resolved html string with mini.html
|
|
50
|
+
* @example
|
|
51
|
+
* ``` js
|
|
52
|
+
* const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export type HtmlHandler<Y = undefined> = ((mini: Mini<Y>) => LazyHandlerReturnType) | (() => LazyHandlerReturnType);
|
|
56
|
+
export type NamedFormHandler<Y = undefined, Z = undefined> = ((mini: Mini<Y>) => NamedFormHandlerReturnType<Z>) | (() => NamedFormHandlerReturnType<Z>);
|
|
57
|
+
declare global {
|
|
58
|
+
var FrontendScripts: Array<string>;
|
|
59
|
+
var FrontendScriptUrls: Array<string>;
|
|
60
|
+
var bundledSVGs: Record<string, {
|
|
61
|
+
svgContent: string;
|
|
62
|
+
svgPath: string;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
export type ScriptTag = (...params: any[]) => Promise<HtmlString>;
|
|
66
|
+
interface LinkSettings {
|
|
67
|
+
[key: string]: string | null | undefined;
|
|
68
|
+
}
|
|
69
|
+
export declare class url {
|
|
70
|
+
static direct_handlers_html: ReadonlyMap<string, HtmlHandler>;
|
|
71
|
+
private static frontends;
|
|
72
|
+
private static svgs;
|
|
73
|
+
static svg(path: string, options?: ResponseInit): string | undefined;
|
|
74
|
+
static frontend(path: string, snippet?: HtmlString): HtmlString;
|
|
75
|
+
/**
|
|
76
|
+
* This is used by the frontend bundler in order to find all frontends and their corresponding script files.
|
|
77
|
+
*/
|
|
78
|
+
static getFrontends(): string[];
|
|
79
|
+
static getSvgPaths(): string[];
|
|
80
|
+
static serveFrontend(req: Request): Response | undefined;
|
|
81
|
+
static serveSvg(req: Request): Response | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* tool to expose data to a frontend as a global variable.
|
|
84
|
+
* @param name this will be added as window.name to the window object in the frontend
|
|
85
|
+
* @param value this will be parsed as json in the frontend and asigned as follows: window.name = JSON.parsed(value)
|
|
86
|
+
* @returns the script tag to be embeded in the html response
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ``` js
|
|
90
|
+
* //backend
|
|
91
|
+
* url.deliver("user", userData); // window.user = JSON.parse(userData)
|
|
92
|
+
* //frontend
|
|
93
|
+
* const user = window["user"];
|
|
94
|
+
* ```
|
|
95
|
+
* if you want to use types, declare them like so in your frontend code:
|
|
96
|
+
* ``` ts
|
|
97
|
+
* declare global {
|
|
98
|
+
* var user: string;
|
|
99
|
+
*}
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
static deliver(name: string, value: JsonStringValues): HtmlString;
|
|
103
|
+
/**
|
|
104
|
+
* @param dataHandler the function that prepares the data for the handlers
|
|
105
|
+
* @example const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
|
|
106
|
+
* @returns
|
|
107
|
+
*/
|
|
108
|
+
static data<T>(dataMaker: DataMaker<T>): {
|
|
109
|
+
/**
|
|
110
|
+
* @param dataHandler the function that prepares the data for the handlers
|
|
111
|
+
* @example const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
|
|
112
|
+
* @returns
|
|
113
|
+
*/
|
|
114
|
+
handler: (dataHandler: HtmlHandler<T>) => (oldmini: Mini) => Promise<string | void | DangerJsonInHtml>;
|
|
115
|
+
/**
|
|
116
|
+
* use this to **specify the input type for the functions**,
|
|
117
|
+
*
|
|
118
|
+
* that you want to use in the HtmlHandlers that follow this **data blend!**
|
|
119
|
+
* @example type lol = typeof MaybeLoggedIn.$Mini
|
|
120
|
+
*/
|
|
121
|
+
$Mini: Mini<T>;
|
|
122
|
+
/**
|
|
123
|
+
* use this to **specify the input type for the functions**,
|
|
124
|
+
*
|
|
125
|
+
* that you want to use in the Htmlhandlers that follow this **data blend!**
|
|
126
|
+
* @example type haha = Mini<typeof MaybeLoggedIn.$Data>
|
|
127
|
+
*/
|
|
128
|
+
$Data: T;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* use this to define your routes.
|
|
132
|
+
* @example
|
|
133
|
+
* ``` js
|
|
134
|
+
* url.set([
|
|
135
|
+
* ["/", (mini) => mini.html`<h1>Hello world</h1>`],
|
|
136
|
+
* ["/apple", (mini) => mini.html`<h1>Hello apple</h1>`],
|
|
137
|
+
* ["/banana", (mini) => mini.html`<h1>Hello banana</h1>`],
|
|
138
|
+
* ]);
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
static set<K extends string>(entries: [K, HtmlHandler][]): void;
|
|
142
|
+
/**
|
|
143
|
+
* wrap your handlers in this if you mutate something to prevent CSRF issues.
|
|
144
|
+
* @param handler - normal html handler with mini as the argument
|
|
145
|
+
* @returns a wrapped html handler that will only be called when the request is post
|
|
146
|
+
*/
|
|
147
|
+
static post(handler: HtmlHandler): (mini: Mini) => LazyHandlerReturnType;
|
|
148
|
+
/**
|
|
149
|
+
* wrap your handlers in this if you mutate something to prevent CSRF issues.
|
|
150
|
+
* @param handler - normal html handler with mini as the argument
|
|
151
|
+
* @returns a wrapped html handler that will only be called when the request is post and contains a json body
|
|
152
|
+
*/
|
|
153
|
+
static postJson(handler: HtmlHandler): (mini: Mini) => LazyHandlerReturnType;
|
|
154
|
+
/**
|
|
155
|
+
* wrap your handlers in this if you mutate something to prevent CSRF issues.
|
|
156
|
+
* @param handler - normal html handler with mini as the argument
|
|
157
|
+
* @returns a wrapped html handler that will only be called when the request is post and contains a FormData body
|
|
158
|
+
*/
|
|
159
|
+
static postFormData(handler: HtmlHandler): (mini: Mini) => LazyHandlerReturnType;
|
|
160
|
+
/**
|
|
161
|
+
* This is useful to decouple forms from routes.
|
|
162
|
+
* @param name name of the form - mini.form.onPostSubmit() will only be called if a (possibly hidden) field called formName matches this
|
|
163
|
+
* @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
|
|
164
|
+
* @returns - { formResponse: result of the handler, formInfo?: some info about the form. Totally up to you}
|
|
165
|
+
*/
|
|
166
|
+
static namedForm<X = undefined, Z = undefined>(name: string, handler: NamedFormHandler<X, Z>): (mini: Mini<X>) => Promise<NamedForm<Z>>;
|
|
167
|
+
/**
|
|
168
|
+
* pass in all the query string parameter names that you want to preserve in the link
|
|
169
|
+
* @param Url - the url that you want to link to (example: "/login")
|
|
170
|
+
* @param qs - the query string parameters that you want to preserve in the link
|
|
171
|
+
* @param settings - key and string values that you want to set in the link
|
|
172
|
+
* @returns - the link that you can use in your html template
|
|
173
|
+
*/
|
|
174
|
+
static link<X>(Url: string, qs?: string[] | string, settings?: LinkSettings): (mini: Mini<X>) => string;
|
|
175
|
+
static currylink(Url: string, qs: string[] | string, req: Request, settings?: LinkSettings): string;
|
|
176
|
+
/**
|
|
177
|
+
* This method retrieves a url from the urls array. If the url does not exist in the urls array, an error will be thrown.
|
|
178
|
+
* @param {string} Url - The url to retrieve.
|
|
179
|
+
* @return {string} - The retrieved url.
|
|
180
|
+
* @throws Will throw an Error if the provided url is not found in the urls array.
|
|
181
|
+
*/
|
|
182
|
+
static get(Url: string): string;
|
|
183
|
+
static match(req: Request, reqPath?: string): Promise<Response | undefined>;
|
|
184
|
+
/**
|
|
185
|
+
* Fetch handler that is called by the server when a request is made to any of the urls.
|
|
186
|
+
* @param {Request} req - The Request object.
|
|
187
|
+
* @return {Promise<Response>} - The Response object.
|
|
188
|
+
*/
|
|
189
|
+
static install(req: Request): Promise<Response>;
|
|
190
|
+
}
|
|
191
|
+
export {};
|