sleepy-serv 0.7.0 → 0.8.0
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 +19 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +37 -19
package/README.md
CHANGED
|
@@ -26,9 +26,25 @@ The parameter for `import.meta.dirname` can be any directory you prefer, but it'
|
|
|
26
26
|
|
|
27
27
|
### Return Value
|
|
28
28
|
|
|
29
|
-
`sleepy-serv` was originally built for NodeJS, but it was ported to `bun` recently (before the initial release). The `createApp()` function merely calls `Bun.serve()` under-the-hood, and returns the `app` object that contains
|
|
29
|
+
`sleepy-serv` was originally built for NodeJS, but it was ported to `bun` recently (before the initial release). The `createApp()` function merely calls `Bun.serve()` under-the-hood, and returns the `app` object that contains these properties:
|
|
30
30
|
- `routes`: Contains a list of all of the routes defined by the file structure. This is useful for debugging.
|
|
31
|
-
- `server`: this is the object that's returned from `Bun.serve()`. The `server` object has an `async` `.stop()` method on it, which
|
|
31
|
+
- `server`: this is the object that's returned from `Bun.serve()`. The `server` object has an `async` `.stop()` method on it, but prefer `app.close()` (see [Shutting Down](#shutting-down)), which stops the server and releases everything else the app holds.
|
|
32
|
+
- `commands`: WebSocket helpers for pushing messages out to connected clients: `send(clientId, event, body)` and `broadcast(event, body)`.
|
|
33
|
+
- `close`: an `async` function that shuts the app down. See [Shutting Down](#shutting-down).
|
|
34
|
+
|
|
35
|
+
### Shutting Down
|
|
36
|
+
|
|
37
|
+
`app.close()` shuts the app down and releases everything it holds:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const app = await createApp(PORT, import.meta.dirname)
|
|
41
|
+
|
|
42
|
+
await app.close()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
It stops the server, tears down the Ctrl+D shutdown handler, and calls [`onClose`](#onclose). Pass `close(true)` to force-close active connections rather than waiting for in-flight requests to finish. That argument is handed straight to `server.stop()`, so it behaves exactly as it does in `Bun.serve()`.
|
|
46
|
+
|
|
47
|
+
This matters most for tests that start and stop an app per case. Calling `server.stop()` on its own stops the HTTP listener but leaves the Ctrl+D shutdown handler attached to `stdin`, which holds an open handle and can keep the process from exiting. `app.close()` does not have that problem.
|
|
32
48
|
|
|
33
49
|
### Adding Routes
|
|
34
50
|
|
|
@@ -465,7 +481,7 @@ Yields these routes:
|
|
|
465
481
|
|
|
466
482
|
### `onClose`
|
|
467
483
|
|
|
468
|
-
When the app is started, the app can be shutdown gracefully by pressing Ctrl+D in the terminal. The `onClose` hook will be called during that shutdown if it's defined. `onClose` can also be `async` as well.
|
|
484
|
+
When the app is started, the app can be shutdown gracefully by pressing Ctrl+D in the terminal. That handler is only wired up when `stdin` is a TTY, so it's skipped in CI, in test runners, and anywhere `stdin` is piped. The `onClose` hook will be called during that shutdown if it's defined, and it's also called by [`app.close()`](#shutting-down). `onClose` can also be `async` as well.
|
|
469
485
|
|
|
470
486
|
```js
|
|
471
487
|
const app = await createApp(PORT, import.meta.dirname, {
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,12 @@ export type { AppOptions, EndpointRequest, FormattedError, Middleware, NextFn, R
|
|
|
7
7
|
export type { FormatterField, FormatterSchema, ValidationSchemas, } from './middleware';
|
|
8
8
|
export type { SocketCommands } from './socket';
|
|
9
9
|
type OutputRoutes = Record<string, string[]>;
|
|
10
|
+
type CloseFn = (force?: boolean) => Promise<void>;
|
|
10
11
|
export type App = {
|
|
11
12
|
server: Server;
|
|
12
13
|
commands: SocketCommands;
|
|
13
14
|
routes: OutputRoutes;
|
|
15
|
+
close: CloseFn;
|
|
14
16
|
};
|
|
15
17
|
export declare function createApp(port: number, rootPath: string, opts?: AppOptions): Promise<App>;
|
|
16
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAIV,UAAU,EACV,MAAM,EACP,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,UAAU,CAAA;AAEjB,cAAc,UAAU,CAAA;AAExB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhD,YAAY,EACV,UAAU,EACV,eAAe,EACf,cAAc,EACd,UAAU,EACV,MAAM,EACN,OAAO,EACP,MAAM,EACN,aAAa,EACb,gBAAgB,GACjB,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,cAAc,CAAA;AAErB,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AA4C5C,MAAM,MAAM,GAAG,GAAG;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,cAAc,CAAA;IACxB,MAAM,EAAE,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAIV,UAAU,EACV,MAAM,EACP,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,UAAU,CAAA;AAEjB,cAAc,UAAU,CAAA;AAExB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhD,YAAY,EACV,UAAU,EACV,eAAe,EACf,cAAc,EACd,UAAU,EACV,MAAM,EACN,OAAO,EACP,MAAM,EACN,aAAa,EACb,gBAAgB,GACjB,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,cAAc,CAAA;AAErB,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AA4C5C,KAAK,OAAO,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAEjD,MAAM,MAAM,GAAG,GAAG;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,cAAc,CAAA;IACxB,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AA4ZD,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,GAAG,CAAC,CAad"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -109,10 +109,13 @@ type AppRoutes = {
|
|
|
109
109
|
socket: SocketRoute[]
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
type CloseFn = (force?: boolean) => Promise<void>
|
|
113
|
+
|
|
112
114
|
export type App = {
|
|
113
115
|
server: Server
|
|
114
116
|
commands: SocketCommands
|
|
115
117
|
routes: OutputRoutes
|
|
118
|
+
close: CloseFn
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
const ALLOWED_FILES_META = ['meta.js', 'meta.ts']
|
|
@@ -132,16 +135,6 @@ const ALLOWED_FILES_METHODS = [
|
|
|
132
135
|
'delete.ts',
|
|
133
136
|
]
|
|
134
137
|
|
|
135
|
-
/* istanbul ignore if */
|
|
136
|
-
if (process.stdin.isTTY) {
|
|
137
|
-
process.stdin.setRawMode(true)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const rl = readline.createInterface({
|
|
141
|
-
input: stdin,
|
|
142
|
-
output: stdout,
|
|
143
|
-
})
|
|
144
|
-
|
|
145
138
|
function methodNotAllowedHandler (_req: unknown): never {
|
|
146
139
|
throw new MethodNotAllowedError()
|
|
147
140
|
}
|
|
@@ -382,8 +375,8 @@ function buildSocketRoutes (mergedRoutes: ChainRoute[]): SocketRoute[] {
|
|
|
382
375
|
}))
|
|
383
376
|
}
|
|
384
377
|
|
|
385
|
-
function buildModuleRoutes (
|
|
386
|
-
return
|
|
378
|
+
function buildModuleRoutes (socketRoutes: SocketRoute[]): ModuleRoute[] {
|
|
379
|
+
return socketRoutes.map(route => {
|
|
387
380
|
const handler: EndpointHandler = async (bunReq, server) => {
|
|
388
381
|
const req = buildEndpointRequest(bunReq, server)
|
|
389
382
|
|
|
@@ -494,7 +487,7 @@ function processIO (
|
|
|
494
487
|
port: number,
|
|
495
488
|
server: Server,
|
|
496
489
|
opts: AppOptions,
|
|
497
|
-
):
|
|
490
|
+
): CloseFn {
|
|
498
491
|
const onClose = opts.onClose || (() => {})
|
|
499
492
|
|
|
500
493
|
console.info(`Running on port: ${port}`)
|
|
@@ -502,12 +495,37 @@ function processIO (
|
|
|
502
495
|
console.info('Press Ctrl+D to gracefully shutdown')
|
|
503
496
|
console.info('')
|
|
504
497
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
await server.stop()
|
|
498
|
+
const shutdown = async (force = false): Promise<void> => {
|
|
499
|
+
await server.stop(force)
|
|
508
500
|
await onClose()
|
|
509
|
-
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (!stdin.isTTY) {
|
|
504
|
+
return shutdown
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
stdin.setRawMode(true)
|
|
508
|
+
|
|
509
|
+
const rl = readline.createInterface({
|
|
510
|
+
input: stdin,
|
|
511
|
+
output: stdout,
|
|
510
512
|
})
|
|
513
|
+
|
|
514
|
+
/* istanbul ignore next */
|
|
515
|
+
const handleClose = async (): Promise<void> => {
|
|
516
|
+
await shutdown()
|
|
517
|
+
process.exit(0)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
rl.on('close', handleClose)
|
|
521
|
+
|
|
522
|
+
return async (force = false) => {
|
|
523
|
+
rl.off('close', handleClose)
|
|
524
|
+
rl.close()
|
|
525
|
+
stdin.setRawMode(false)
|
|
526
|
+
|
|
527
|
+
await shutdown(force)
|
|
528
|
+
}
|
|
511
529
|
}
|
|
512
530
|
|
|
513
531
|
export async function createApp (
|
|
@@ -519,12 +537,12 @@ export async function createApp (
|
|
|
519
537
|
const routes = await buildRoutes(rootPath, state, opts)
|
|
520
538
|
const server = buildServer(port, routes, state, opts)
|
|
521
539
|
const commands = buildSocketCommands(state)
|
|
522
|
-
|
|
523
|
-
processIO(port, server, opts)
|
|
540
|
+
const close = processIO(port, server, opts)
|
|
524
541
|
|
|
525
542
|
return {
|
|
526
543
|
routes: routes.output,
|
|
527
544
|
server,
|
|
528
545
|
commands,
|
|
546
|
+
close,
|
|
529
547
|
}
|
|
530
548
|
}
|