spooder 4.1.1 → 4.2.1
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 +59 -23
- package/package.json +1 -1
- package/src/api.d.ts +4 -2
- package/src/api.ts +43 -9
package/README.md
CHANGED
|
@@ -430,9 +430,9 @@ In addition to the information provided by the developer, `spooder` also include
|
|
|
430
430
|
- [`ErrorWithMetadata(message: string, metadata: object)`](#api-error-handling-error-with-metadata)
|
|
431
431
|
- [`caution(err_message_or_obj: string | object, ...err: object[]): Promise<void>`](#api-error-handling-caution)
|
|
432
432
|
- [`panic(err_message_or_obj: string | object, ...err: object[]): Promise<void>`](#api-error-handling-panic)
|
|
433
|
+
- [`safe(fn: Callable): Promise<void>`](#api-error-handling-safe)
|
|
433
434
|
- [API > Content](#api-content)
|
|
434
|
-
- [`
|
|
435
|
-
- [`template_sub_file(template_file: string, replacements: Record<string, string>): Promise<string>`](#api-content-template-sub-file)
|
|
435
|
+
- [`parse_template(template: string, replacements: Record<string, string>): string`](#api-content-parse-template)
|
|
436
436
|
- [`generate_hash_subs(length: number, prefix: string): Promise<Record<string, string>>`](#api-content-generate-hash-subs)
|
|
437
437
|
- [`apply_range(file: BunFile, request: Request): HandlerReturnType`](#api-content-apply-range)
|
|
438
438
|
- [API > State Management](#api-state-management)
|
|
@@ -873,11 +873,35 @@ try {
|
|
|
873
873
|
}
|
|
874
874
|
```
|
|
875
875
|
|
|
876
|
+
<a id="api-error-handling-safe"></a>
|
|
877
|
+
### 🔧 `safe(fn: Callable): Promise<void>`
|
|
878
|
+
|
|
879
|
+
`safe()` is a utility function that wraps a "callable" and calls `caution()` if it throws an error.
|
|
880
|
+
|
|
881
|
+
> ![NOTE]
|
|
882
|
+
> This utility is primarily intended to be used to reduce boilerplate for fire-and-forget functions that you want to be notified about if they fail.
|
|
883
|
+
|
|
884
|
+
```ts
|
|
885
|
+
safe(async (() => {
|
|
886
|
+
// This code will run async and any errors will invoke caution().
|
|
887
|
+
});
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
`safe()` supports both async and sync callables, as well as Promise objects. `safe()` can also used with `await`.
|
|
891
|
+
|
|
892
|
+
```ts
|
|
893
|
+
await safe(() => {
|
|
894
|
+
return new Promise((resolve, reject) => {
|
|
895
|
+
// Do stuff.
|
|
896
|
+
});
|
|
897
|
+
});
|
|
898
|
+
```
|
|
899
|
+
|
|
876
900
|
<a id="api-content"></a>
|
|
877
901
|
## API > Content
|
|
878
902
|
|
|
879
|
-
<a id="api-content-template
|
|
880
|
-
### 🔧 `
|
|
903
|
+
<a id="api-content-parse-template"></a>
|
|
904
|
+
### 🔧 `parse_template(template: string, replacements: Record<string, string>): string`
|
|
881
905
|
|
|
882
906
|
Replace placeholders in a template string with values from a replacement object.
|
|
883
907
|
|
|
@@ -888,12 +912,12 @@ Replace placeholders in a template string with values from a replacement object.
|
|
|
888
912
|
const template = `
|
|
889
913
|
<html>
|
|
890
914
|
<head>
|
|
891
|
-
<title>{title}</title>
|
|
915
|
+
<title>{$title}</title>
|
|
892
916
|
</head>
|
|
893
917
|
<body>
|
|
894
|
-
<h1>{title}</h1>
|
|
895
|
-
<p>{content}</p>
|
|
896
|
-
<p>{ignored}</p>
|
|
918
|
+
<h1>{$title}</h1>
|
|
919
|
+
<p>{$content}</p>
|
|
920
|
+
<p>{$ignored}</p>
|
|
897
921
|
</body>
|
|
898
922
|
</html>
|
|
899
923
|
`;
|
|
@@ -903,7 +927,7 @@ const replacements = {
|
|
|
903
927
|
content: 'This is a test.'
|
|
904
928
|
};
|
|
905
929
|
|
|
906
|
-
const html =
|
|
930
|
+
const html = parse_template(template, replacements);
|
|
907
931
|
```
|
|
908
932
|
|
|
909
933
|
```html
|
|
@@ -914,28 +938,40 @@ const html = template_sub(template, replacements);
|
|
|
914
938
|
<body>
|
|
915
939
|
<h1>Hello, world!</h1>
|
|
916
940
|
<p>This is a test.</p>
|
|
917
|
-
<p>{ignored}</p>
|
|
941
|
+
<p>{$ignored}</p>
|
|
918
942
|
</body>
|
|
919
943
|
</html>
|
|
920
944
|
```
|
|
921
945
|
|
|
922
|
-
|
|
923
|
-
### 🔧 `template_sub_file(template_file: string, replacements: Record<string, string>): Promise<string>`
|
|
946
|
+
`parse_template` supports looping arrays with the following syntax.
|
|
924
947
|
|
|
925
|
-
|
|
948
|
+
```html
|
|
949
|
+
{$for:foo}My colour is %s{/for}
|
|
950
|
+
```
|
|
951
|
+
```ts
|
|
952
|
+
const template = `
|
|
953
|
+
<ul>
|
|
954
|
+
{$for:foo}<li>%s</li>{/for}
|
|
955
|
+
</ul>
|
|
956
|
+
`;
|
|
926
957
|
|
|
927
|
-
|
|
928
|
-
|
|
958
|
+
const replacements = {
|
|
959
|
+
foo: ['red', 'green', 'blue']
|
|
960
|
+
};
|
|
929
961
|
|
|
930
|
-
|
|
931
|
-
|
|
962
|
+
const html = parse_template(template, replacements);
|
|
963
|
+
```
|
|
932
964
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
965
|
+
```html
|
|
966
|
+
<ul>
|
|
967
|
+
<li>red</li>
|
|
968
|
+
<li>green</li>
|
|
969
|
+
<li>blue</li>
|
|
970
|
+
</ul>
|
|
937
971
|
```
|
|
938
972
|
|
|
973
|
+
> [!WARNING]
|
|
974
|
+
> Nested loops are not supported.
|
|
939
975
|
|
|
940
976
|
<a id="api-content-generate-hash-subs"></a>
|
|
941
977
|
### 🔧 `generate_hash_subs(prefix: string): Promise<Record<string, string>>`
|
|
@@ -951,7 +987,7 @@ let hash_sub_table = {};
|
|
|
951
987
|
generate_hash_subs().then(subs => hash_sub_table = subs).catch(caution);
|
|
952
988
|
|
|
953
989
|
server.route('/test', (req, url) => {
|
|
954
|
-
return
|
|
990
|
+
return parse_template('Hello world {hash=docs/project-logo.png}', hash_sub_table);
|
|
955
991
|
});
|
|
956
992
|
```
|
|
957
993
|
|
|
@@ -978,7 +1014,7 @@ Use a different prefix other than `hash=` by passing it as the first parameter.
|
|
|
978
1014
|
generate_hash_subs(7, '#').then(subs => hash_sub_table = subs).catch(caution);
|
|
979
1015
|
|
|
980
1016
|
server.route('/test', (req, url) => {
|
|
981
|
-
return
|
|
1017
|
+
return parse_template('Hello world {#docs/project-logo.png}', hash_sub_table);
|
|
982
1018
|
});
|
|
983
1019
|
```
|
|
984
1020
|
|
package/package.json
CHANGED
package/src/api.d.ts
CHANGED
|
@@ -15,8 +15,10 @@ export declare class ErrorWithMetadata extends Error {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function panic(err_message_or_obj: string | object, ...err: object[]): Promise<void>;
|
|
17
17
|
export declare function caution(err_message_or_obj: string | object, ...err: object[]): Promise<void>;
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
type CallableFunction = (...args: any[]) => any;
|
|
19
|
+
type Callable = Promise<any> | CallableFunction;
|
|
20
|
+
export declare function safe(target_fn: Callable): Promise<void>;
|
|
21
|
+
export declare function parse_template(template: string, replacements: Record<string, string | Array<string>>): string;
|
|
20
22
|
export declare function generate_hash_subs(length?: number, prefix?: string): Promise<Record<string, string>>;
|
|
21
23
|
type CookieOptions = {
|
|
22
24
|
same_site?: 'Strict' | 'Lax' | 'None';
|
package/src/api.ts
CHANGED
|
@@ -89,14 +89,21 @@ export async function caution(err_message_or_obj: string | object, ...err: objec
|
|
|
89
89
|
await handle_error('caution: ', err_message_or_obj, ...err);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
type CallableFunction = (...args: any[]) => any;
|
|
93
|
+
type Callable = Promise<any> | CallableFunction;
|
|
94
|
+
|
|
95
|
+
export async function safe(target_fn: Callable) {
|
|
96
|
+
try {
|
|
97
|
+
if (target_fn instanceof Promise)
|
|
98
|
+
await target_fn;
|
|
99
|
+
else
|
|
100
|
+
await target_fn();
|
|
101
|
+
} catch (e) {
|
|
102
|
+
caution(e as Error);
|
|
103
|
+
}
|
|
97
104
|
}
|
|
98
105
|
|
|
99
|
-
export function
|
|
106
|
+
export function parse_template(template: string, replacements: Record<string, string | Array<string>>): string {
|
|
100
107
|
let result = '';
|
|
101
108
|
let buffer = '';
|
|
102
109
|
let buffer_active = false;
|
|
@@ -105,14 +112,41 @@ export function template_sub(template: string, replacements: Record<string, stri
|
|
|
105
112
|
for (let i = 0; i < template_length; i++) {
|
|
106
113
|
const char = template[i];
|
|
107
114
|
|
|
108
|
-
if (char === '{') {
|
|
115
|
+
if (char === '{' && template[i + 1] === '$') {
|
|
116
|
+
i++;
|
|
109
117
|
buffer_active = true;
|
|
110
118
|
buffer = '';
|
|
111
|
-
} else if (char === '}') {
|
|
119
|
+
} else if (char === '}' && buffer_active) {
|
|
112
120
|
buffer_active = false;
|
|
113
121
|
|
|
114
|
-
|
|
122
|
+
if (buffer.startsWith('for:')) {
|
|
123
|
+
const loop_key = buffer.substring(4);
|
|
124
|
+
|
|
125
|
+
const loop_entries = replacements[loop_key];
|
|
126
|
+
if (loop_entries !== undefined) {
|
|
127
|
+
const loop_content_start_index = i + 1;
|
|
128
|
+
const loop_close_index = template.indexOf('{/for}', loop_content_start_index);
|
|
129
|
+
const loop_content = template.substring(loop_content_start_index, loop_close_index);
|
|
130
|
+
|
|
131
|
+
// More performat than replaceAll on larger arrays (and equal on tiny arrays).
|
|
132
|
+
const content_parts = loop_content.split('%s');
|
|
133
|
+
const indicies = [] as Array<number>;
|
|
134
|
+
|
|
135
|
+
for (let j = 0; j < content_parts.length; j++)
|
|
136
|
+
if (content_parts[j] === '%s')
|
|
137
|
+
indicies.push(j);
|
|
115
138
|
|
|
139
|
+
for (const loop_entry of loop_entries)
|
|
140
|
+
for (const index of indicies)
|
|
141
|
+
content_parts[index] = loop_entry;
|
|
142
|
+
|
|
143
|
+
i += loop_content.length + 6;
|
|
144
|
+
} else {
|
|
145
|
+
result += '{$' + buffer + '}';
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
result += replacements[buffer] ?? '{$' + buffer + '}';
|
|
149
|
+
}
|
|
116
150
|
buffer = '';
|
|
117
151
|
} else if (buffer_active) {
|
|
118
152
|
buffer += char;
|