spooder 4.2.8 → 4.2.10
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 +35 -2
- package/package.json +2 -3
- package/src/api.d.ts +3 -3
- package/src/api.ts +17 -9
- package/src/cli.ts +1 -1
package/README.md
CHANGED
|
@@ -158,7 +158,7 @@ The following differences will be observed when running in development mode:
|
|
|
158
158
|
It is possible to detect in userland if a server is running in development mode by checking the `SPOODER_ENV` environment variable.
|
|
159
159
|
|
|
160
160
|
```ts
|
|
161
|
-
if (process.env.
|
|
161
|
+
if (process.env.SPOODER_ENV === 'dev') {
|
|
162
162
|
// Server is running in development mode.
|
|
163
163
|
}
|
|
164
164
|
```
|
|
@@ -728,6 +728,16 @@ server.dir('/static', '/static', (file_path, file, stat, request, url) => {
|
|
|
728
728
|
> [!NOTE]
|
|
729
729
|
> The directory handler function is only called for files that exist on disk - including directories.
|
|
730
730
|
|
|
731
|
+
Asynchronous directory handlers are supported and will be awaited.
|
|
732
|
+
|
|
733
|
+
```js
|
|
734
|
+
server.dir('/static', '/static', async (file_path, file) => {
|
|
735
|
+
let file_contents = await file.text();
|
|
736
|
+
// do something with file_contents
|
|
737
|
+
return file_contents;
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
731
741
|
<a id="api-routing-server-sse"></a>
|
|
732
742
|
## API > Routing > Server-Sent Events
|
|
733
743
|
|
|
@@ -937,7 +947,7 @@ await safe(() => {
|
|
|
937
947
|
## API > Content
|
|
938
948
|
|
|
939
949
|
<a id="api-content-parse-template"></a>
|
|
940
|
-
### 🔧 `parse_template(template: string, replacements:
|
|
950
|
+
### 🔧 `parse_template(template: string, replacements: Replacements, drop_missing: boolean): string`
|
|
941
951
|
|
|
942
952
|
Replace placeholders in a template string with values from a replacement object.
|
|
943
953
|
|
|
@@ -995,6 +1005,29 @@ parse_template(template, replacements, true);
|
|
|
995
1005
|
</html>
|
|
996
1006
|
```
|
|
997
1007
|
|
|
1008
|
+
`parse_template` supports passing a function instead of a replacement object. This function will be called for each placeholder and the return value will be used as the replacement.
|
|
1009
|
+
|
|
1010
|
+
```ts
|
|
1011
|
+
const replacer = (placeholder: string) => {
|
|
1012
|
+
return placeholder.toUpperCase();
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
parse_template('Hello {$world}', replacer);
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
```html
|
|
1019
|
+
<html>
|
|
1020
|
+
<head>
|
|
1021
|
+
<title>TITLE</title>
|
|
1022
|
+
</head>
|
|
1023
|
+
<body>
|
|
1024
|
+
<h1>TITLE</h1>
|
|
1025
|
+
<p>CONTENT</p>
|
|
1026
|
+
<p>IGNORED</p>
|
|
1027
|
+
</body>
|
|
1028
|
+
</html>
|
|
1029
|
+
```
|
|
1030
|
+
|
|
998
1031
|
`parse_template` supports looping arrays with the following syntax.
|
|
999
1032
|
|
|
1000
1033
|
```html
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spooder",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.2.
|
|
4
|
+
"version": "4.2.10",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"bun": "./src/api.ts",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"module": "./src/api.ts",
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@types/
|
|
14
|
-
"bun-types": "^0.5.0"
|
|
13
|
+
"@types/bun": "^1.0.5"
|
|
15
14
|
},
|
|
16
15
|
"bin": {
|
|
17
16
|
"spooder": "./src/cli.ts"
|
package/src/api.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
/// <reference types="bun-types" />
|
|
3
|
-
/// <reference types="node" />
|
|
4
2
|
/// <reference types="node" />
|
|
5
3
|
import fs from 'node:fs/promises';
|
|
6
4
|
import { Blob } from 'node:buffer';
|
|
@@ -20,7 +18,9 @@ export declare function caution(err_message_or_obj: string | object, ...err: obj
|
|
|
20
18
|
type CallableFunction = (...args: any[]) => any;
|
|
21
19
|
type Callable = Promise<any> | CallableFunction;
|
|
22
20
|
export declare function safe(target_fn: Callable): Promise<void>;
|
|
23
|
-
|
|
21
|
+
type ReplacerFn = (key: string) => string | Array<string>;
|
|
22
|
+
type Replacements = Record<string, string | Array<string>> | ReplacerFn;
|
|
23
|
+
export declare function parse_template(template: string, replacements: Replacements, drop_missing?: boolean): string;
|
|
24
24
|
export declare function generate_hash_subs(length?: number, prefix?: string): Promise<Record<string, string>>;
|
|
25
25
|
type CookieOptions = {
|
|
26
26
|
same_site?: 'Strict' | 'Lax' | 'None';
|
package/src/api.ts
CHANGED
|
@@ -107,11 +107,16 @@ export async function safe(target_fn: Callable) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
type ReplacerFn = (key: string) => string | Array<string>;
|
|
111
|
+
type Replacements = Record<string, string | Array<string>> | ReplacerFn;
|
|
112
|
+
|
|
113
|
+
export function parse_template(template: string, replacements: Replacements, drop_missing = false): string {
|
|
111
114
|
let result = '';
|
|
112
115
|
let buffer = '';
|
|
113
116
|
let buffer_active = false;
|
|
114
117
|
|
|
118
|
+
const is_replacer_fn = typeof replacements === 'function';
|
|
119
|
+
|
|
115
120
|
const template_length = template.length;
|
|
116
121
|
for (let i = 0; i < template_length; i++) {
|
|
117
122
|
const char = template[i];
|
|
@@ -126,7 +131,7 @@ export function parse_template(template: string, replacements: Record<string, st
|
|
|
126
131
|
if (buffer.startsWith('for:')) {
|
|
127
132
|
const loop_key = buffer.substring(4);
|
|
128
133
|
|
|
129
|
-
const loop_entries = replacements[loop_key];
|
|
134
|
+
const loop_entries = is_replacer_fn ? replacements(loop_key) : replacements[loop_key];
|
|
130
135
|
const loop_content_start_index = i + 1;
|
|
131
136
|
const loop_close_index = template.indexOf('{/for}', loop_content_start_index);
|
|
132
137
|
|
|
@@ -147,7 +152,7 @@ export function parse_template(template: string, replacements: Record<string, st
|
|
|
147
152
|
i += loop_content.length + 6;
|
|
148
153
|
}
|
|
149
154
|
} else {
|
|
150
|
-
const replacement = replacements[buffer];
|
|
155
|
+
const replacement = is_replacer_fn ? replacements(buffer) : replacements[buffer];
|
|
151
156
|
if (replacement !== undefined)
|
|
152
157
|
result += replacement;
|
|
153
158
|
else if (!drop_missing)
|
|
@@ -326,7 +331,7 @@ function route_directory(route_path: string, dir: string, handler: DirHandler):
|
|
|
326
331
|
const file_stat = await fs.stat(file_path);
|
|
327
332
|
const bun_file = Bun.file(file_path);
|
|
328
333
|
|
|
329
|
-
return handler(file_path, bun_file, file_stat, req, url);
|
|
334
|
+
return await handler(file_path, bun_file, file_stat, req, url);
|
|
330
335
|
} catch (e) {
|
|
331
336
|
const err = e as NodeJS.ErrnoException;
|
|
332
337
|
if (err?.code === 'ENOENT')
|
|
@@ -380,7 +385,8 @@ export function serve(port: number) {
|
|
|
380
385
|
|
|
381
386
|
// Content-type/content-length are automatically set for blobs.
|
|
382
387
|
if (response instanceof Blob)
|
|
383
|
-
|
|
388
|
+
// @ts-ignore Response does accept Blob in Bun, typing disagrees.
|
|
389
|
+
return new Response(response, { status: status_code });
|
|
384
390
|
|
|
385
391
|
// Status codes can be returned from some handlers.
|
|
386
392
|
if (return_status_code && typeof response === 'number')
|
|
@@ -554,14 +560,16 @@ export function serve(port: number) {
|
|
|
554
560
|
|
|
555
561
|
const queue = Array<string>();
|
|
556
562
|
const stream = new ReadableStream({
|
|
563
|
+
// @ts-ignore Bun implements a "direct" mode which does not exist in the spec.
|
|
557
564
|
type: 'direct',
|
|
558
565
|
|
|
559
|
-
async pull(controller
|
|
560
|
-
|
|
566
|
+
async pull(controller) {
|
|
567
|
+
// @ts-ignore `controller` in "direct" mode is ReadableStreamDirectController.
|
|
568
|
+
stream_controller = controller as ReadableStreamDirectController;
|
|
561
569
|
while (!req.signal.aborted) {
|
|
562
570
|
if (queue.length > 0) {
|
|
563
|
-
|
|
564
|
-
|
|
571
|
+
stream_controller.write(queue.shift()!);
|
|
572
|
+
stream_controller.flush();
|
|
565
573
|
} else {
|
|
566
574
|
await Bun.sleep(50);
|
|
567
575
|
}
|
package/src/cli.ts
CHANGED
|
@@ -64,7 +64,7 @@ async function start_server() {
|
|
|
64
64
|
function capture_stream(stream: ReadableStream, output: NodeJS.WritableStream) {
|
|
65
65
|
const reader = stream.getReader();
|
|
66
66
|
|
|
67
|
-
reader.read().then(function read_chunk(chunk
|
|
67
|
+
reader.read().then(function read_chunk(chunk) {
|
|
68
68
|
if (chunk.done)
|
|
69
69
|
return;
|
|
70
70
|
|