spooder 4.2.0 → 4.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/README.md CHANGED
@@ -430,6 +430,7 @@ 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
  - [`parse_template(template: string, replacements: Record<string, string>): string`](#api-content-parse-template)
435
436
  - [`generate_hash_subs(length: number, prefix: string): Promise<Record<string, string>>`](#api-content-generate-hash-subs)
@@ -872,6 +873,30 @@ try {
872
873
  }
873
874
  ```
874
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
+
875
900
  <a id="api-content"></a>
876
901
  ## API > Content
877
902
 
@@ -962,7 +987,7 @@ let hash_sub_table = {};
962
987
  generate_hash_subs().then(subs => hash_sub_table = subs).catch(caution);
963
988
 
964
989
  server.route('/test', (req, url) => {
965
- return parse_template('Hello world {hash=docs/project-logo.png}', hash_sub_table);
990
+ return parse_template('Hello world {$hash=docs/project-logo.png}', hash_sub_table);
966
991
  });
967
992
  ```
968
993
 
@@ -983,13 +1008,13 @@ generate_hash_subs(40).then(...);
983
1008
  > [!NOTE]
984
1009
  > SHA-1 hashes are `40` characters. Git is transitioning to SHA-256, which are `64` characters. Short hashes of `7` are generally sufficient for cache-busting.
985
1010
 
986
- Use a different prefix other than `hash=` by passing it as the first parameter.
1011
+ Use a different prefix other than `$hash=` by passing it as the first parameter.
987
1012
 
988
1013
  ```ts
989
- generate_hash_subs(7, '#').then(subs => hash_sub_table = subs).catch(caution);
1014
+ generate_hash_subs(7, '$#').then(subs => hash_sub_table = subs).catch(caution);
990
1015
 
991
1016
  server.route('/test', (req, url) => {
992
- return parse_template('Hello world {#docs/project-logo.png}', hash_sub_table);
1017
+ return parse_template('Hello world {$#docs/project-logo.png}', hash_sub_table);
993
1018
  });
994
1019
  ```
995
1020
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spooder",
3
3
  "type": "module",
4
- "version": "4.2.0",
4
+ "version": "4.2.2",
5
5
  "exports": {
6
6
  ".": {
7
7
  "bun": "./src/api.ts",
package/src/api.d.ts CHANGED
@@ -15,6 +15,9 @@ 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
+ type CallableFunction = (...args: any[]) => any;
19
+ type Callable = Promise<any> | CallableFunction;
20
+ export declare function safe(target_fn: Callable): Promise<void>;
18
21
  export declare function parse_template(template: string, replacements: Record<string, string | Array<string>>): string;
19
22
  export declare function generate_hash_subs(length?: number, prefix?: string): Promise<Record<string, string>>;
20
23
  type CookieOptions = {
package/src/api.ts CHANGED
@@ -89,6 +89,20 @@ 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
+ 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
+ }
104
+ }
105
+
92
106
  export function parse_template(template: string, replacements: Record<string, string | Array<string>>): string {
93
107
  let result = '';
94
108
  let buffer = '';
@@ -109,26 +123,30 @@ export function parse_template(template: string, replacements: Record<string, st
109
123
  const loop_key = buffer.substring(4);
110
124
 
111
125
  const loop_entries = replacements[loop_key];
112
- if (loop_entries !== undefined) {
113
- const loop_content_start_index = i + 1;
114
- const loop_close_index = template.indexOf('{/for}', loop_content_start_index);
115
- const loop_content = template.substring(loop_content_start_index, loop_close_index);
116
-
117
- // More performat than replaceAll on larger arrays (and equal on tiny arrays).
118
- const content_parts = loop_content.split('%s');
119
- const indicies = [] as Array<number>;
120
-
121
- for (let j = 0; j < content_parts.length; j++)
122
- if (content_parts[j] === '%s')
123
- indicies.push(j);
124
-
125
- for (const loop_entry of loop_entries)
126
- for (const index of indicies)
127
- content_parts[index] = loop_entry;
128
-
129
- i += loop_content.length + 6;
130
- } else {
126
+ const loop_content_start_index = i + 1;
127
+ const loop_close_index = template.indexOf('{/for}', loop_content_start_index);
128
+
129
+ if (loop_close_index === -1) {
131
130
  result += '{$' + buffer + '}';
131
+ } else {
132
+ const loop_content = template.substring(loop_content_start_index, loop_close_index);
133
+ if (loop_entries !== undefined) {
134
+ // More performat than replaceAll on larger arrays (and equal on tiny arrays).
135
+ const content_parts = loop_content.split('%s');
136
+ const indicies = [] as Array<number>;
137
+
138
+ for (let j = 0; j < content_parts.length; j++)
139
+ if (content_parts[j] === '%s')
140
+ indicies.push(j);
141
+
142
+ for (const loop_entry of loop_entries)
143
+ for (const index of indicies)
144
+ content_parts[index] = loop_entry;
145
+
146
+ i += loop_content.length + 6;
147
+ } else {
148
+ result += '{$' + buffer + '}' + loop_content + '{/for}';
149
+ }
132
150
  }
133
151
  } else {
134
152
  result += replacements[buffer] ?? '{$' + buffer + '}';
@@ -144,7 +162,7 @@ export function parse_template(template: string, replacements: Record<string, st
144
162
  return result;
145
163
  }
146
164
 
147
- export async function generate_hash_subs(length = 7, prefix = 'hash='): Promise<Record<string, string>> {
165
+ export async function generate_hash_subs(length = 7, prefix = '$hash='): Promise<Record<string, string>> {
148
166
  const cmd = ['git', 'ls-tree', '-r', 'HEAD'];
149
167
  const process = Bun.spawn(cmd, {
150
168
  stdout: 'pipe',