spooder 4.2.6 → 4.2.8
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 +90 -54
- package/package.json +1 -1
- package/src/api.d.ts +4 -2
- package/src/api.ts +34 -25
package/README.md
CHANGED
|
@@ -8,10 +8,68 @@
|
|
|
8
8
|
- It provides streamlined APIs for common server tasks in a minimalistic way, without the overhead of a full-featured web framework.
|
|
9
9
|
- It is opinionated in its design to reduce complexity and overhead.
|
|
10
10
|
|
|
11
|
+
The design goal behind `spooder` is not to provide a full-featured web server, but to expand the Bun runtime with a set of APIs and utilities that make it easy to develop servers with minimal overhead.
|
|
12
|
+
|
|
13
|
+
> [!NOTE]
|
|
14
|
+
> If you think a is missing a feature, consider opening an issue with your use-case. The goal behind `spooder` is to provide APIs that are useful for a wide range of use-cases, not to provide bespoke features better suited for userland.
|
|
15
|
+
|
|
11
16
|
It consists of two components, the `CLI` and the `API`.
|
|
12
17
|
- The `CLI` is responsible for keeping the server process running, applying updates in response to source control changes, and automatically raising issues on GitHub via the canary feature.
|
|
13
18
|
- The `API` provides a minimal building-block style API for developing servers, with a focus on simplicity and performance.
|
|
14
19
|
|
|
20
|
+
> [!WARNING]
|
|
21
|
+
> `spooder` is stable but still in active development. Backwards compatibility between versions is not guaranteed and breaking changes may be introduced. Consider pinning a specific version in your `package.json`.
|
|
22
|
+
|
|
23
|
+
# CLI
|
|
24
|
+
|
|
25
|
+
The `CLI` component of `spooder` is a global command-line tool for running server processes.
|
|
26
|
+
|
|
27
|
+
- [CLI > Usage](#cli-usage)
|
|
28
|
+
- [CLI > Dev Mode](#cli-dev-mode)
|
|
29
|
+
- [CLI > Auto Restart](#cli-auto-restart)
|
|
30
|
+
- [CLI > Auto Update](#cli-auto-update)
|
|
31
|
+
- [CLI > Canary](#cli-canary)
|
|
32
|
+
- [CLI > Canary > Crash](#cli-canary-crash)
|
|
33
|
+
- [CLI > Canary > Sanitization](#cli-canary-sanitization)
|
|
34
|
+
- [CLI > Canary > System Information](#cli-canary-system-information)
|
|
35
|
+
|
|
36
|
+
# API
|
|
37
|
+
|
|
38
|
+
`spooder` exposes a simple yet powerful API for developing servers. The API is designed to be minimal to leave control in the hands of the developer and not add overhead for features you may not need.
|
|
39
|
+
|
|
40
|
+
- [API > Serving](#api-serving)
|
|
41
|
+
- [`serve(port: number): Server`](#api-serving-serve)
|
|
42
|
+
- [API > Routing](#api-routing)
|
|
43
|
+
- [`server.route(path: string, handler: RequestHandler, method: HTTP_METHODS)`](#api-routing-server-route)
|
|
44
|
+
- [HTTP Methods](#api-routing-methods)
|
|
45
|
+
- [Redirection Routes](#api-routing-redirection-routes)
|
|
46
|
+
- [Status Code Text](#api-routing-status-code-text)
|
|
47
|
+
- [API > Routing > RequestHandler](#api-routing-request-handler)
|
|
48
|
+
- [API > Routing > Fallback Handling](#api-routing-fallback-handlers)
|
|
49
|
+
- [`server.handle(status_code: number, handler: RequestHandler)`](#api-routing-server-handle)
|
|
50
|
+
- [`server.default(handler: DefaultHandler)`](#api-routing-server-default)
|
|
51
|
+
- [`server.error(handler: ErrorHandler)`](#api-routing-server-error)
|
|
52
|
+
- [API > Routing > Directory Serving](#api-routing-directory-serving)
|
|
53
|
+
- [`server.dir(path: string, dir: string, handler?: DirHandler, method: HTTP_METHODS)`](#api-routing-server-dir)
|
|
54
|
+
- [API > Routing > Server-Sent Events](#api-routing-server-sent-events)
|
|
55
|
+
- [`server.sse(path: string, handler: ServerSentEventHandler)`](#api-routing-server-sse)
|
|
56
|
+
- [API > Routing > Webhooks](#api-routing-webhooks)
|
|
57
|
+
- [`server.webhook(secret: string, path: string, handler: WebhookHandler)`](#api-routing-server-webhook)
|
|
58
|
+
- [API > Server Control](#api-server-control)
|
|
59
|
+
- [`server.stop(immediate: boolean)`](#api-server-control-server-stop)
|
|
60
|
+
- [API > Error Handling](#api-error-handling)
|
|
61
|
+
- [`ErrorWithMetadata(message: string, metadata: object)`](#api-error-handling-error-with-metadata)
|
|
62
|
+
- [`caution(err_message_or_obj: string | object, ...err: object[]): Promise<void>`](#api-error-handling-caution)
|
|
63
|
+
- [`panic(err_message_or_obj: string | object, ...err: object[]): Promise<void>`](#api-error-handling-panic)
|
|
64
|
+
- [`safe(fn: Callable): Promise<void>`](#api-error-handling-safe)
|
|
65
|
+
- [API > Content](#api-content)
|
|
66
|
+
- [`parse_template(template: string, replacements: Record<string, string>, drop_missing: boolean): string`](#api-content-parse-template)
|
|
67
|
+
- [`generate_hash_subs(length: number, prefix: string): Promise<Record<string, string>>`](#api-content-generate-hash-subs)
|
|
68
|
+
- [`apply_range(file: BunFile, request: Request): HandlerReturnType`](#api-content-apply-range)
|
|
69
|
+
- [API > State Management](#api-state-management)
|
|
70
|
+
- [`set_cookie(res: Response, name: string, value: string, options?: CookieOptions)`](#api-state-management-set-cookie)
|
|
71
|
+
- [`get_cookies(source: Request | Response): Record<string, string>`](#api-state-management-get-cookies)
|
|
72
|
+
|
|
15
73
|
# Installation
|
|
16
74
|
|
|
17
75
|
```bash
|
|
@@ -51,20 +109,6 @@ If there are any issues with the provided configuration, a warning will be print
|
|
|
51
109
|
> [!NOTE]
|
|
52
110
|
> Configuration warnings **do not** raise `caution` events with the `spooder` canary functionality.
|
|
53
111
|
|
|
54
|
-
# CLI
|
|
55
|
-
|
|
56
|
-
The `CLI` component of `spooder` is a global command-line tool for running server processes.
|
|
57
|
-
|
|
58
|
-
- [CLI > Usage](#cli-usage)
|
|
59
|
-
- [CLI > Dev Mode](#cli-dev-mode)
|
|
60
|
-
- [CLI > Auto Restart](#cli-auto-restart)
|
|
61
|
-
- [CLI > Auto Update](#cli-auto-update)
|
|
62
|
-
- [CLI > Canary](#cli-canary)
|
|
63
|
-
- [CLI > Canary > Crash](#cli-canary-crash)
|
|
64
|
-
- [CLI > Canary > Sanitization](#cli-canary-sanitization)
|
|
65
|
-
- [CLI > Canary > System Information](#cli-canary-system-information)
|
|
66
|
-
|
|
67
|
-
|
|
68
112
|
<a id="cli-usage"></a>
|
|
69
113
|
## CLI > Usage
|
|
70
114
|
|
|
@@ -183,7 +227,7 @@ server.webhook(process.env.WEBHOOK_SECRET, '/webhook', payload => {
|
|
|
183
227
|
|
|
184
228
|
`canary` is a feature in `spooder` which allows server problems to be raised as issues in your repository on GitHub.
|
|
185
229
|
|
|
186
|
-
To enable this feature, you will need to
|
|
230
|
+
To enable this feature, you will need to create a GitHub App and configure it:
|
|
187
231
|
|
|
188
232
|
### 1. Create a GitHub App
|
|
189
233
|
|
|
@@ -404,41 +448,6 @@ In addition to the information provided by the developer, `spooder` also include
|
|
|
404
448
|
}
|
|
405
449
|
```
|
|
406
450
|
|
|
407
|
-
# API
|
|
408
|
-
|
|
409
|
-
`spooder` exposes a simple yet powerful API for developing servers. The API is designed to be minimal to leave control in the hands of the developer and not add overhead for features you may not need.
|
|
410
|
-
|
|
411
|
-
- [API > Serving](#api-serving)
|
|
412
|
-
- [`serve(port: number): Server`](#api-serving-serve)
|
|
413
|
-
- [API > Routing](#api-routing)
|
|
414
|
-
- [`server.route(path: string, handler: RequestHandler)`](#api-routing-server-route)
|
|
415
|
-
- [Redirection Routes](#api-routing-redirection-routes)
|
|
416
|
-
- [API > Routing > RequestHandler](#api-routing-request-handler)
|
|
417
|
-
- [API > Routing > Fallback Handling](#api-routing-fallback-handlers)
|
|
418
|
-
- [`server.handle(status_code: number, handler: RequestHandler)`](#api-routing-server-handle)
|
|
419
|
-
- [`server.default(handler: DefaultHandler)`](#api-routing-server-default)
|
|
420
|
-
- [`server.error(handler: ErrorHandler)`](#api-routing-server-error)
|
|
421
|
-
- [API > Routing > Directory Serving](#api-routing-directory-serving)
|
|
422
|
-
- [`server.dir(path: string, dir: string, handler?: DirHandler)`](#api-routing-server-dir)
|
|
423
|
-
- [API > Routing > Server-Sent Events](#api-routing-server-sent-events)
|
|
424
|
-
- [`server.sse(path: string, handler: ServerSentEventHandler)`](#api-routing-server-sse)
|
|
425
|
-
- [API > Routing > Webhooks](#api-routing-webhooks)
|
|
426
|
-
- [`server.webhook(secret: string, path: string, handler: WebhookHandler)`](#api-routing-server-webhook)
|
|
427
|
-
- [API > Server Control](#api-server-control)
|
|
428
|
-
- [`server.stop(immediate: boolean)`](#api-server-control-server-stop)
|
|
429
|
-
- [API > Error Handling](#api-error-handling)
|
|
430
|
-
- [`ErrorWithMetadata(message: string, metadata: object)`](#api-error-handling-error-with-metadata)
|
|
431
|
-
- [`caution(err_message_or_obj: string | object, ...err: object[]): Promise<void>`](#api-error-handling-caution)
|
|
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)
|
|
434
|
-
- [API > Content](#api-content)
|
|
435
|
-
- [`parse_template(template: string, replacements: Record<string, string>, drop_missing: boolean): string`](#api-content-parse-template)
|
|
436
|
-
- [`generate_hash_subs(length: number, prefix: string): Promise<Record<string, string>>`](#api-content-generate-hash-subs)
|
|
437
|
-
- [`apply_range(file: BunFile, request: Request): HandlerReturnType`](#api-content-apply-range)
|
|
438
|
-
- [API > State Management](#api-state-management)
|
|
439
|
-
- [`set_cookie(res: Response, name: string, value: string, options?: CookieOptions)`](#api-state-management-set-cookie)
|
|
440
|
-
- [`get_cookies(source: Request | Response): Record<string, string>`](#api-state-management-get-cookies)
|
|
441
|
-
|
|
442
451
|
<a id="api-serving"></a>
|
|
443
452
|
## API > Serving
|
|
444
453
|
|
|
@@ -477,6 +486,32 @@ server.route('/test/route', (req, url) => {
|
|
|
477
486
|
});
|
|
478
487
|
```
|
|
479
488
|
|
|
489
|
+
<a id="api-routing-methods"></a>
|
|
490
|
+
### HTTP Methods
|
|
491
|
+
|
|
492
|
+
By default, `spooder` will register routes defined with `server.route()` and `server.dir()` as `GET` routes. Requests to these routes with other methods will return `405 Method Not Allowed`.
|
|
493
|
+
|
|
494
|
+
> [!NOTE]
|
|
495
|
+
> spooder does not automatically handle HEAD requests natively.
|
|
496
|
+
|
|
497
|
+
This can be controlled by providing the `method` parameter with a string or array defining one or more of the following methods.
|
|
498
|
+
|
|
499
|
+
```
|
|
500
|
+
GET | HEAD | POST | PUT | DELETE | CONNECT | OPTIONS | TRACE | PATCH
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
```ts
|
|
504
|
+
server.route('/test/route', (req, url) => {
|
|
505
|
+
if (req.method === 'GET')
|
|
506
|
+
// Handle GET request.
|
|
507
|
+
else if (req.method === 'POST')
|
|
508
|
+
// Handle POST request.
|
|
509
|
+
}, ['GET', 'POST']);
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
> [!NOTE]
|
|
513
|
+
> Routes defined with .sse() or .webhook() are always registered as 'GET' and 'POST' respectively and cannot be configured.
|
|
514
|
+
|
|
480
515
|
<a id="api-routing-redirection-routes"></a>
|
|
481
516
|
### Redirection Routes
|
|
482
517
|
|
|
@@ -486,6 +521,7 @@ server.route('/test/route', (req, url) => {
|
|
|
486
521
|
server.route('/redirect', () => Response.redirect('/redirected', 301));
|
|
487
522
|
```
|
|
488
523
|
|
|
524
|
+
<a id="api-routing-status-code-text"></a>
|
|
489
525
|
### Status Code Text
|
|
490
526
|
|
|
491
527
|
`spooder` exposes `HTTP_STATUS_CODE` to convieniently access status code text.
|
|
@@ -546,7 +582,7 @@ bar
|
|
|
546
582
|
|
|
547
583
|
Named parameters can be used in paths by prefixing a path segment with a colon.
|
|
548
584
|
|
|
549
|
-
> [!
|
|
585
|
+
> [!IMPORTANT]
|
|
550
586
|
> Named parameters will overwrite existing query parameters with the same name.
|
|
551
587
|
|
|
552
588
|
```ts
|
|
@@ -878,7 +914,7 @@ try {
|
|
|
878
914
|
|
|
879
915
|
`safe()` is a utility function that wraps a "callable" and calls `caution()` if it throws an error.
|
|
880
916
|
|
|
881
|
-
> !
|
|
917
|
+
> [!NOTE]
|
|
882
918
|
> 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
919
|
|
|
884
920
|
```ts
|
|
@@ -962,12 +998,12 @@ parse_template(template, replacements, true);
|
|
|
962
998
|
`parse_template` supports looping arrays with the following syntax.
|
|
963
999
|
|
|
964
1000
|
```html
|
|
965
|
-
{$for:foo}My colour is {
|
|
1001
|
+
{$for:foo}My colour is %s{/for}
|
|
966
1002
|
```
|
|
967
1003
|
```ts
|
|
968
1004
|
const template = `
|
|
969
1005
|
<ul>
|
|
970
|
-
{$for:foo}<li
|
|
1006
|
+
{$for:foo}<li>%s</li>{/for}
|
|
971
1007
|
</ul>
|
|
972
1008
|
`;
|
|
973
1009
|
|
package/package.json
CHANGED
package/src/api.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare const HTTP_STATUS_CODE: {
|
|
|
8
8
|
[errorCode: number]: string | undefined;
|
|
9
9
|
[errorCode: string]: string | undefined;
|
|
10
10
|
};
|
|
11
|
+
type HTTP_METHOD = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
|
|
12
|
+
type HTTP_METHODS = HTTP_METHOD | HTTP_METHOD[];
|
|
11
13
|
export declare class ErrorWithMetadata extends Error {
|
|
12
14
|
metadata: Record<string, unknown>;
|
|
13
15
|
constructor(message: string, metadata: Record<string, unknown>);
|
|
@@ -60,9 +62,9 @@ type DirStat = PromiseType<ReturnType<typeof fs.stat>>;
|
|
|
60
62
|
type DirHandler = (file_path: string, file: BunFile, stat: DirStat, request: Request, url: URL) => HandlerReturnType;
|
|
61
63
|
export declare function serve(port: number): {
|
|
62
64
|
/** Register a handler for a specific route. */
|
|
63
|
-
route: (path: string, handler: RequestHandler) => void;
|
|
65
|
+
route: (path: string, handler: RequestHandler, method?: HTTP_METHODS) => void;
|
|
64
66
|
/** Serve a directory for a specific route. */
|
|
65
|
-
dir: (path: string, dir: string, handler?: DirHandler) => void;
|
|
67
|
+
dir: (path: string, dir: string, handler?: DirHandler, method?: HTTP_METHODS) => void;
|
|
66
68
|
webhook: (secret: string, path: string, handler: WebhookHandler) => void;
|
|
67
69
|
/** Register a default handler for all status codes. */
|
|
68
70
|
default: (handler: DefaultHandler) => void;
|
package/src/api.ts
CHANGED
|
@@ -8,6 +8,10 @@ import { Blob } from 'node:buffer';
|
|
|
8
8
|
|
|
9
9
|
export const HTTP_STATUS_CODE = http.STATUS_CODES;
|
|
10
10
|
|
|
11
|
+
// Create enum containing HTTP methods
|
|
12
|
+
type HTTP_METHOD = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
|
|
13
|
+
type HTTP_METHODS = HTTP_METHOD|HTTP_METHOD[];
|
|
14
|
+
|
|
11
15
|
export class ErrorWithMetadata extends Error {
|
|
12
16
|
constructor(message: string, public metadata: Record<string, unknown>) {
|
|
13
17
|
super(message);
|
|
@@ -24,7 +28,7 @@ export class ErrorWithMetadata extends Error {
|
|
|
24
28
|
if (value instanceof Promise)
|
|
25
29
|
resolved_value = await value;
|
|
26
30
|
else if (typeof value === 'function')
|
|
27
|
-
resolved_value = value();
|
|
31
|
+
resolved_value = await value();
|
|
28
32
|
else if (value instanceof ReadableStream)
|
|
29
33
|
resolved_value = await Bun.readableStreamToText(value);
|
|
30
34
|
|
|
@@ -132,14 +136,9 @@ export function parse_template(template: string, replacements: Record<string, st
|
|
|
132
136
|
} else {
|
|
133
137
|
const loop_content = template.substring(loop_content_start_index, loop_close_index);
|
|
134
138
|
if (loop_entries !== undefined) {
|
|
135
|
-
const inner_replacements = {
|
|
136
|
-
...replacements,
|
|
137
|
-
entry: ''
|
|
138
|
-
};
|
|
139
|
-
|
|
140
139
|
for (const loop_entry of loop_entries) {
|
|
141
|
-
|
|
142
|
-
result += parse_template(
|
|
140
|
+
const inner_content = loop_content.replaceAll('%s', loop_entry);
|
|
141
|
+
result += parse_template(inner_content, replacements, drop_missing);
|
|
143
142
|
}
|
|
144
143
|
} else {
|
|
145
144
|
if (!drop_missing)
|
|
@@ -354,8 +353,15 @@ function print_request_info(req: Request, res: Response, url: URL, request_start
|
|
|
354
353
|
return res;
|
|
355
354
|
}
|
|
356
355
|
|
|
356
|
+
function is_valid_method(method: HTTP_METHODS, req: Request): boolean {
|
|
357
|
+
if (Array.isArray(method))
|
|
358
|
+
return method.includes(req.method as HTTP_METHOD);
|
|
359
|
+
|
|
360
|
+
return req.method === method;
|
|
361
|
+
}
|
|
362
|
+
|
|
357
363
|
export function serve(port: number) {
|
|
358
|
-
const routes = new Array<[string[], RequestHandler]>();
|
|
364
|
+
const routes = new Array<[string[], RequestHandler, HTTP_METHODS]>();
|
|
359
365
|
const handlers = new Map<number, StatusCodeHandler>();
|
|
360
366
|
|
|
361
367
|
let error_handler: ErrorHandler | undefined;
|
|
@@ -393,8 +399,9 @@ export function serve(port: number) {
|
|
|
393
399
|
try {
|
|
394
400
|
const route_array = url.pathname.split('/').filter(e => !(e === '..' || e === '.'));
|
|
395
401
|
let handler: RequestHandler | undefined;
|
|
402
|
+
let methods: HTTP_METHODS | undefined;
|
|
396
403
|
|
|
397
|
-
for (const [path, route_handler] of routes) {
|
|
404
|
+
for (const [path, route_handler, route_methods] of routes) {
|
|
398
405
|
const is_trailing_wildcard = path[path.length - 1] === '*';
|
|
399
406
|
if (!is_trailing_wildcard && path.length !== route_array.length)
|
|
400
407
|
continue;
|
|
@@ -419,20 +426,25 @@ export function serve(port: number) {
|
|
|
419
426
|
|
|
420
427
|
if (match) {
|
|
421
428
|
handler = route_handler;
|
|
429
|
+
methods = route_methods;
|
|
422
430
|
break;
|
|
423
431
|
}
|
|
424
432
|
}
|
|
425
433
|
|
|
426
434
|
// Check for a handler for the route.
|
|
427
435
|
if (handler !== undefined) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
436
|
+
if (is_valid_method(methods!, req)) {
|
|
437
|
+
const response = await resolve_handler(handler(req, url), status_code, true);
|
|
438
|
+
if (response instanceof Response)
|
|
439
|
+
return response;
|
|
431
440
|
|
|
432
|
-
|
|
433
|
-
|
|
441
|
+
// If the handler returned a status code, use that instead.
|
|
442
|
+
status_code = response;
|
|
443
|
+
} else {
|
|
444
|
+
status_code = 405; // Method Not Allowed
|
|
445
|
+
}
|
|
434
446
|
} else {
|
|
435
|
-
status_code = 404;
|
|
447
|
+
status_code = 404; // Not Found
|
|
436
448
|
}
|
|
437
449
|
|
|
438
450
|
// Fallback to checking for a handler for the status code.
|
|
@@ -477,23 +489,20 @@ export function serve(port: number) {
|
|
|
477
489
|
|
|
478
490
|
return {
|
|
479
491
|
/** Register a handler for a specific route. */
|
|
480
|
-
route: (path: string, handler: RequestHandler): void => {
|
|
481
|
-
routes.push([path.split('/'), handler]);
|
|
492
|
+
route: (path: string, handler: RequestHandler, method: HTTP_METHODS = 'GET'): void => {
|
|
493
|
+
routes.push([path.split('/'), handler, method]);
|
|
482
494
|
},
|
|
483
495
|
|
|
484
496
|
/** Serve a directory for a specific route. */
|
|
485
|
-
dir: (path: string, dir: string, handler?: DirHandler): void => {
|
|
497
|
+
dir: (path: string, dir: string, handler?: DirHandler, method: HTTP_METHODS = 'GET'): void => {
|
|
486
498
|
if (path.endsWith('/'))
|
|
487
499
|
path = path.slice(0, -1);
|
|
488
500
|
|
|
489
|
-
routes.push([[...path.split('/'), '*'], route_directory(path, dir, handler ?? default_directory_handler)]);
|
|
501
|
+
routes.push([[...path.split('/'), '*'], route_directory(path, dir, handler ?? default_directory_handler), method]);
|
|
490
502
|
},
|
|
491
503
|
|
|
492
504
|
webhook: (secret: string, path: string, handler: WebhookHandler): void => {
|
|
493
505
|
routes.push([path.split('/'), async (req: Request, url: URL) => {
|
|
494
|
-
if (req.method !== 'POST')
|
|
495
|
-
return 405; // Method Not Allowed
|
|
496
|
-
|
|
497
506
|
if (req.headers.get('Content-Type') !== 'application/json')
|
|
498
507
|
return 400; // Bad Request
|
|
499
508
|
|
|
@@ -509,7 +518,7 @@ export function serve(port: number) {
|
|
|
509
518
|
return 401; // Unauthorized
|
|
510
519
|
|
|
511
520
|
return handler(body);
|
|
512
|
-
}]);
|
|
521
|
+
}, 'POST']);
|
|
513
522
|
},
|
|
514
523
|
|
|
515
524
|
/** Register a default handler for all status codes. */
|
|
@@ -581,7 +590,7 @@ export function serve(port: number) {
|
|
|
581
590
|
'Cache-Control': 'no-cache',
|
|
582
591
|
'Connection': 'keep-alive'
|
|
583
592
|
}});
|
|
584
|
-
}]);
|
|
593
|
+
}, 'GET']);
|
|
585
594
|
}
|
|
586
595
|
}
|
|
587
596
|
}
|