spooder 3.2.2 → 3.2.4

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
@@ -623,6 +623,22 @@ The `stop` function allows you to stop the server. `method` is one of `ServerSto
623
623
 
624
624
  ---
625
625
 
626
+ #### `ErrorWithMetadata(message: string, metadata: object)`
627
+
628
+ The `ErrorWithMetadata` class is a thin wrapper around the built-in `Error` class that allows you to attach metadata to the error.
629
+
630
+ Providing additional information to errors can be used for debugging purposes when errors are dispatched to the canary.
631
+
632
+ ```ts
633
+ throw new ErrorWithMetadata('Something went wrong', { foo: 'bar' });
634
+ ```
635
+
636
+ For convinience, if any of the values in the `metadata` are functions, they will be called and the return value will be used instead.
637
+
638
+ Additionally, promises will be resolved and readable streams will be converted to strings.
639
+
640
+ ---
641
+
626
642
  #### `route_location(redirect_url: string)`
627
643
 
628
644
  The `route_location` is a built-in request handler that redirects the client to a specified URL with the status code `301 Moved Permanently`.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spooder",
3
3
  "type": "module",
4
- "version": "3.2.2",
4
+ "version": "3.2.4",
5
5
  "exports": {
6
6
  ".": {
7
7
  "bun": "./src/api.ts",
package/src/api.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  /// <reference types="bun-types" />
2
2
  /// <reference types="node" />
3
+ export declare class ErrorWithMetadata extends Error {
4
+ metadata: Record<string, unknown>;
5
+ constructor(message: string, metadata: Record<string, unknown>);
6
+ resolve_metadata(): Promise<object>;
7
+ }
3
8
  export declare function panic(err_message_or_obj: string | object, ...err: object[]): Promise<void>;
4
9
  export declare function caution(err_message_or_obj: string | object, ...err: object[]): Promise<void>;
5
10
  type HandlerReturnType = any;
package/src/api.ts CHANGED
@@ -3,6 +3,26 @@ import http from 'node:http';
3
3
  import path from 'node:path';
4
4
  import fs from 'node:fs/promises';
5
5
 
6
+ export class ErrorWithMetadata extends Error {
7
+ constructor(message: string, public metadata: Record<string, unknown>) {
8
+ super(message);
9
+ }
10
+
11
+ async resolve_metadata(): Promise<object> {
12
+ const metadata = Object.assign({}, this.metadata);
13
+ for (const [key, value] of Object.entries(metadata)) {
14
+ if (value instanceof Promise)
15
+ metadata[key] = await value;
16
+ else if (typeof value === 'function')
17
+ metadata[key] = value();
18
+ else if (value instanceof ReadableStream)
19
+ metadata[key] = await new Response(value).text();
20
+ }
21
+
22
+ return metadata;
23
+ }
24
+ }
25
+
6
26
  async function handle_error(prefix: string, err_message_or_obj: string | object, ...err: unknown[]): Promise<void> {
7
27
  let error_message = 'unknown error';
8
28
 
@@ -16,20 +36,25 @@ async function handle_error(prefix: string, err_message_or_obj: string | object,
16
36
  err.push(err_message_or_obj);
17
37
  }
18
38
 
19
- // Serialize error objects.
20
- err = err.map(e => {
39
+ const final_err = Array(err.length);
40
+ for (let i = 0; i < err.length; i++) {
41
+ const e = err[i];
42
+
21
43
  if (e instanceof Error) {
22
- return {
44
+ const report = {
23
45
  name: e.name,
24
46
  message: e.message,
25
47
  stack: e.stack?.split('\n') ?? []
26
- }
27
- }
48
+ } as Record<string, unknown>;
28
49
 
29
- return e;
30
- })
50
+ if (e instanceof ErrorWithMetadata)
51
+ report.metadata = await e.resolve_metadata();
52
+
53
+ final_err[i] = report;
54
+ }
55
+ }
31
56
 
32
- await dispatch_report(prefix + error_message, err);
57
+ await dispatch_report(prefix + error_message, final_err);
33
58
  }
34
59
 
35
60
  export async function panic(err_message_or_obj: string | object, ...err: object[]): Promise<void> {
package/src/cli.ts CHANGED
@@ -36,38 +36,39 @@ async function start_server() {
36
36
  }
37
37
  }
38
38
 
39
- Bun.spawn(parse_command_line(config.run), {
39
+ const proc = Bun.spawn(parse_command_line(config.run), {
40
40
  cwd: process.cwd(),
41
41
  stdout: 'inherit',
42
- stderr: 'pipe',
43
-
44
- onExit: (proc, exitCode, signal) => {
45
- log('server exited with code %d', exitCode);
46
-
47
- if (exitCode !== null && exitCode > 0) {
48
- if (proc.stderr !== undefined) {
49
- const res = new Response(proc.stderr as ReadableStream);
50
-
51
- res.text().then(async stderr => {
52
- await dispatch_report('crash: server exited unexpectedly', [{
53
- exitCode,
54
- stderr: strip_color_codes(stderr).split(/\r?\n/)
55
- }]);
56
- });
57
- } else {
58
- dispatch_report('crash: service exited unexpectedly', [{
59
- exitCode
60
- }]);
61
- }
62
- }
42
+ stderr: 'pipe'
43
+ });
63
44
 
64
- const auto_restart_ms = config.autoRestart;
65
- if (auto_restart_ms > -1) {
66
- log('restarting server in %dms', auto_restart_ms);
67
- setTimeout(start_server, auto_restart_ms);
68
- }
45
+ await proc.exited;
46
+
47
+ const proc_exit_code = proc.exitCode;
48
+ log('server exited with code %s', proc_exit_code);
49
+
50
+ if (proc_exit_code !== 0) {
51
+ if (proc.stderr !== undefined) {
52
+ const res = new Response(proc.stderr as ReadableStream);
53
+
54
+ res.text().then(async stderr => {
55
+ await dispatch_report('crash: server exited unexpectedly', [{
56
+ proc_exit_code,
57
+ stderr: strip_color_codes(stderr).split(/\r?\n/)
58
+ }]);
59
+ });
60
+ } else {
61
+ dispatch_report('crash: service exited unexpectedly', [{
62
+ proc_exit_code
63
+ }]);
69
64
  }
70
- });
65
+ }
66
+
67
+ const auto_restart_ms = config.autoRestart;
68
+ if (auto_restart_ms > -1) {
69
+ log('restarting server in %dms', auto_restart_ms);
70
+ setTimeout(start_server, auto_restart_ms);
71
+ }
71
72
  }
72
73
 
73
74
  await start_server();