sleepy-serv 0.23.0 → 0.24.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 +9 -7
- package/dist/core/index.d.ts +27 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/socket.d.ts +28 -1
- package/dist/core/socket.d.ts.map +1 -1
- package/dist/core/utils.d.ts +25 -71
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/index.ts +45 -15
- package/src/core/socket.ts +114 -25
- package/src/core/utils.ts +29 -81
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -571,10 +571,10 @@ and the server does not accept WebSocket upgrades. Calling `app.ws` or
|
|
|
571
571
|
|
|
572
572
|
The `app.ws` object exposes four methods for interacting with connected clients:
|
|
573
573
|
|
|
574
|
-
- `query(fn)`: return a filtered list of
|
|
575
|
-
- `send(event, body, fn)`: push a notification to clients matching a filter. The filter function receives `(
|
|
574
|
+
- `query(fn, filter?)`: return a filtered list of sessions. The filter function receives `(session, index)` where `session` is a `SessionEntry` (`{ clientId, type, data }`), and returns a boolean. The optional `filter` parameter controls which sessions are iterated: `'active'` (default), `'inactive'`, or `'all'`. To list all active sessions: `app.ws.query(() => true)`. To include inactive sessions: `app.ws.query(() => true, 'all')`.
|
|
575
|
+
- `send(event, body, fn)`: push a notification to clients matching a filter. The filter function receives `(session, index)` and returns a boolean. To target one client: `app.ws.send('ping', body, s => s.clientId === targetId)`.
|
|
576
576
|
- `broadcast(event, body)`: push a notification to all connected clients.
|
|
577
|
-
- `drop(signal, fn)`: close connections matching a filter. `signal` is a `CloseSignal` (`{ code, reason }`) with `code` validated in the range [4000-4099]. The filter function receives `(
|
|
577
|
+
- `drop(signal, fn)`: close connections matching a filter. `signal` is a `CloseSignal` (`{ code, reason }`) with `code` validated in the range [4000-4099]. The filter function receives `(session, index)` and returns a boolean. To drop one client: `app.ws.drop({ code: 4000, reason: 'kicked' }, s => s.clientId === targetId)`.
|
|
578
578
|
|
|
579
579
|
The same commands are available inside endpoint handlers via `req.ws`:
|
|
580
580
|
|
|
@@ -582,7 +582,7 @@ The same commands are available inside endpoint handlers via `req.ws`:
|
|
|
582
582
|
export default function (req) {
|
|
583
583
|
const targetId = req.query.targetId
|
|
584
584
|
|
|
585
|
-
req.ws.send('ping', { from: 'handler' },
|
|
585
|
+
req.ws.send('ping', { from: 'handler' }, s => s.clientId === targetId)
|
|
586
586
|
|
|
587
587
|
return Response.json({ ok: true })
|
|
588
588
|
}
|
|
@@ -594,12 +594,14 @@ This works from both HTTP and WebSocket transports.
|
|
|
594
594
|
|
|
595
595
|
`sleepy-serv` exports several runtime constants and types:
|
|
596
596
|
|
|
597
|
-
- `
|
|
597
|
+
- `ServerCloseSignals`: server close signals: `Ok` (`{ code: 1000, reason: 'ok' }`), `Reaped` (`{ code: 4998, reason: 'reaped' }`), `Superseded` (`{ code: 4999, reason: 'superseded' }`)
|
|
598
598
|
- `CloseSignal`: the type for close signals: `{ code: number, reason: string }`
|
|
599
599
|
- `StatusCode`: the full range of HTTP status codes (1xx through 5xx)
|
|
600
600
|
- `HttpMethod`: HTTP verbs: `Head`, `Get`, `Post`, `Put`, `Patch`, `Delete`
|
|
601
|
-
- `
|
|
602
|
-
- `
|
|
601
|
+
- `SessionType`: session state: `Active` (`'active'`), `Inactive` (`'inactive'`)
|
|
602
|
+
- `SessionFilter`: session filter: `Active` (`'active'`), `Inactive` (`'inactive'`), `All` (`'all'`)
|
|
603
|
+
- `SessionEntry`: the shape returned by `query()` and received by filter callbacks: `{ clientId: string, type: SessionType, data: unknown }`
|
|
604
|
+
- `FilterFn`: the filter callback type: `(session: SessionEntry, index: number) => boolean`
|
|
603
605
|
- `SocketCommands`: the type for `app.ws` and `req.ws`
|
|
604
606
|
- Error classes for every 4xx and 5xx status (e.g. `NotFoundError`, `UnauthorizedError`, `InternalServerError`)
|
|
605
607
|
- Middleware helpers: `parseJsonBody`, `validateSchemas`, `setValidationFormats`
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HttpMethod, Middleware, MiddlewareChain, Handler, Server } from './utils';
|
|
2
|
+
import type { SocketCommands } from './utils';
|
|
3
|
+
import type { SocketOptions } from './socket';
|
|
2
4
|
export * from './errors';
|
|
3
|
-
export {
|
|
5
|
+
export { ServerCloseSignals } from './socket';
|
|
6
|
+
export { StatusCode, HttpMethod, SessionType, SessionFilter, } from './utils';
|
|
4
7
|
export { parseJsonBody, setValidationFormats, validateSchemas, } from './middleware';
|
|
5
|
-
export type {
|
|
6
|
-
export type {
|
|
8
|
+
export type { ActiveSession, ActiveSessions, InactiveSession, SocketConnection, SocketOptions, } from './socket';
|
|
9
|
+
export type { AsyncHandlerResult, BaseRequest, CloseSignal, EndpointRequest, FilterFn, FormattedError, Handler, HandlerResult, Middleware, MiddlewareChain, NextFn, Request, Server, SessionEntry, SocketCommands, SocketData, WebSocketRequest, } from './utils';
|
|
7
10
|
export type { FormatterField, FormatterSchema, ValidationSchemas, } from './middleware';
|
|
11
|
+
export type RouteDefinition = {
|
|
12
|
+
method: HttpMethod;
|
|
13
|
+
path: string;
|
|
14
|
+
chain: Handler | MiddlewareChain;
|
|
15
|
+
};
|
|
16
|
+
export type MetaEntry = {
|
|
17
|
+
path: string;
|
|
18
|
+
middleware: Middleware[];
|
|
19
|
+
};
|
|
20
|
+
export type RouteConfig = {
|
|
21
|
+
routes: RouteDefinition[];
|
|
22
|
+
meta?: MetaEntry[];
|
|
23
|
+
};
|
|
24
|
+
export type AppOptions = {
|
|
25
|
+
hostname?: string;
|
|
26
|
+
mountPath?: string;
|
|
27
|
+
middleware?: Middleware[];
|
|
28
|
+
ws?: boolean | SocketOptions;
|
|
29
|
+
onClose?: () => Promise<void> | void;
|
|
30
|
+
};
|
|
8
31
|
type OutputRoutes = Record<string, string[]>;
|
|
9
32
|
type CloseFn = (force?: boolean) => Promise<void>;
|
|
10
33
|
export type App = {
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAEV,UAAU,EACV,UAAU,EACV,eAAe,EAEf,OAAO,EACP,MAAM,EACP,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EACV,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EACV,aAAa,EAGd,MAAM,UAAU,CAAA;AAEjB,cAAc,UAAU,CAAA;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAE7C,OAAO,EACL,UAAU,EACV,UAAU,EACV,WAAW,EACX,aAAa,GACd,MAAM,SAAS,CAAA;AAEhB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAA;AAErB,YAAY,EACV,aAAa,EACb,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,MAAM,UAAU,CAAA;AAEjB,YAAY,EACV,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,eAAe,EACf,QAAQ,EACR,cAAc,EACd,OAAO,EACP,aAAa,EACb,UAAU,EACV,eAAe,EACf,MAAM,EACN,OAAO,EACP,MAAM,EACN,YAAY,EACZ,cAAc,EACd,UAAU,EACV,gBAAgB,GACjB,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,cAAc,CAAA;AAErB,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,GAAG,eAAe,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,UAAU,EAAE,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,IAAI,CAAC,EAAE,SAAS,EAAE,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAA;IACzB,EAAE,CAAC,EAAE,OAAO,GAAG,aAAa,CAAA;IAC5B,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CACrC,CAAA;AAED,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AA0B5C,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,MAAM,EAAE,YAAY,CAAA;IACpB,EAAE,EAAE,cAAc,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAsRD,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE,UAAe,GACpB,GAAG,CAqBL"}
|
package/dist/core/socket.d.ts
CHANGED
|
@@ -1,11 +1,38 @@
|
|
|
1
1
|
import type { WebSocketHandler } from 'bun';
|
|
2
|
-
import type { AsyncHandlerResult, CloseSignal, HttpMethod, Request, MiddlewareChain, SocketCommands, SocketData
|
|
2
|
+
import type { AsyncHandlerResult, CloseSignal, HttpMethod, Request, MiddlewareChain, SocketCommands, SocketData } from './utils';
|
|
3
3
|
type SocketHandler = (req: Request, res: unknown) => AsyncHandlerResult;
|
|
4
4
|
type SocketEndpoint = {
|
|
5
5
|
method: HttpMethod;
|
|
6
6
|
path: string;
|
|
7
7
|
handler: SocketHandler;
|
|
8
8
|
};
|
|
9
|
+
export type SocketConnection = {
|
|
10
|
+
data: SocketData;
|
|
11
|
+
send: (data: string) => unknown;
|
|
12
|
+
close: (code?: number, reason?: string) => void;
|
|
13
|
+
};
|
|
14
|
+
export type ActiveSession = {
|
|
15
|
+
token: string;
|
|
16
|
+
ws: SocketConnection;
|
|
17
|
+
};
|
|
18
|
+
export type InactiveSession = {
|
|
19
|
+
token: string;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
data: unknown;
|
|
22
|
+
};
|
|
23
|
+
export type ActiveSessions = ReadonlyMap<string, ActiveSession>;
|
|
24
|
+
export declare const ServerCloseSignals: Record<string, CloseSignal>;
|
|
25
|
+
export type ServerCloseSignals = typeof ServerCloseSignals[keyof typeof ServerCloseSignals];
|
|
26
|
+
export type Session = ActiveSession | InactiveSession;
|
|
27
|
+
export type SocketOptions = {
|
|
28
|
+
dropThreshold?: number;
|
|
29
|
+
heartbeatInterval?: number;
|
|
30
|
+
maxTickets?: number;
|
|
31
|
+
reclaimTtl?: number;
|
|
32
|
+
ticketTtl?: number;
|
|
33
|
+
onOpen?: (clientId: string) => void;
|
|
34
|
+
onClose?: (clientId: string, signal: CloseSignal) => void;
|
|
35
|
+
};
|
|
9
36
|
export type Ticket = {
|
|
10
37
|
clientId: string;
|
|
11
38
|
expiresAt: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/core/socket.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/core/socket.ts"],"names":[],"mappings":"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"}
|
package/dist/core/utils.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ErrorObject } from 'ajv';
|
|
2
2
|
import type { BunRequest, Server as BunServer } from 'bun';
|
|
3
|
+
export type TimeoutHandle = ReturnType<typeof setTimeout>;
|
|
3
4
|
export declare const HttpMethod: {
|
|
4
5
|
readonly Head: "HEAD";
|
|
5
6
|
readonly Get: "GET";
|
|
@@ -85,27 +86,39 @@ export type NextFn = (data?: unknown) => HandlerResult;
|
|
|
85
86
|
export type Middleware = (req: Request, res: unknown, next: NextFn) => HandlerResult;
|
|
86
87
|
export type Handler = (req: Request, res: unknown) => HandlerResult;
|
|
87
88
|
export type MiddlewareChain = (Middleware | Handler)[];
|
|
88
|
-
export type
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
export type CloseSignal = {
|
|
90
|
+
code: number;
|
|
91
|
+
reason: string;
|
|
92
|
+
};
|
|
93
|
+
export type SocketData = {
|
|
94
|
+
clientId: string;
|
|
95
|
+
superseded: boolean;
|
|
96
|
+
reaped: boolean;
|
|
97
|
+
reaperHandle: TimeoutHandle | null;
|
|
98
|
+
data: unknown;
|
|
99
|
+
};
|
|
100
|
+
export declare const SessionType: {
|
|
101
|
+
readonly Active: "active";
|
|
102
|
+
readonly Inactive: "inactive";
|
|
91
103
|
};
|
|
92
|
-
export type
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
export type SessionType = typeof SessionType[keyof typeof SessionType];
|
|
105
|
+
export declare const SessionFilter: {
|
|
106
|
+
readonly Active: "active";
|
|
107
|
+
readonly Inactive: "inactive";
|
|
108
|
+
readonly All: "all";
|
|
96
109
|
};
|
|
97
|
-
export type
|
|
98
|
-
export type Session = ActiveSession | InactiveSession;
|
|
110
|
+
export type SessionFilter = typeof SessionFilter[keyof typeof SessionFilter];
|
|
99
111
|
export type SessionEntry = {
|
|
100
112
|
clientId: string;
|
|
101
|
-
|
|
113
|
+
type: SessionType;
|
|
114
|
+
data: unknown;
|
|
102
115
|
};
|
|
103
|
-
export type FilterFn = (
|
|
116
|
+
export type FilterFn = (session: SessionEntry, index: number) => boolean;
|
|
104
117
|
export type SocketCommands = {
|
|
105
118
|
broadcast: (event: string, body: unknown) => void;
|
|
106
119
|
send: (event: string, body: unknown, fn: FilterFn) => void;
|
|
107
120
|
drop: (signal: CloseSignal, fn: FilterFn) => void;
|
|
108
|
-
query: (fn: FilterFn) => SessionEntry[];
|
|
121
|
+
query: (fn: FilterFn, filter?: SessionFilter) => SessionEntry[];
|
|
109
122
|
};
|
|
110
123
|
export type BaseRequest = {
|
|
111
124
|
method: HttpMethod;
|
|
@@ -125,66 +138,7 @@ export type WebSocketRequest = BaseRequest & {
|
|
|
125
138
|
clientId: string;
|
|
126
139
|
};
|
|
127
140
|
export type Request = EndpointRequest | WebSocketRequest;
|
|
128
|
-
export type CloseSignal = {
|
|
129
|
-
code: number;
|
|
130
|
-
reason: string;
|
|
131
|
-
};
|
|
132
|
-
export declare const InternalCloseSignal: {
|
|
133
|
-
readonly Ok: {
|
|
134
|
-
readonly code: 1000;
|
|
135
|
-
readonly reason: "ok";
|
|
136
|
-
};
|
|
137
|
-
readonly Reaped: {
|
|
138
|
-
readonly code: 4998;
|
|
139
|
-
readonly reason: "reaped";
|
|
140
|
-
};
|
|
141
|
-
readonly Superseded: {
|
|
142
|
-
readonly code: 4999;
|
|
143
|
-
readonly reason: "superseded";
|
|
144
|
-
};
|
|
145
|
-
};
|
|
146
|
-
export type SocketOptions = {
|
|
147
|
-
dropThreshold?: number;
|
|
148
|
-
heartbeatInterval?: number;
|
|
149
|
-
maxTickets?: number;
|
|
150
|
-
reclaimTtl?: number;
|
|
151
|
-
ticketTtl?: number;
|
|
152
|
-
onOpen?: (clientId: string) => void;
|
|
153
|
-
onClose?: (clientId: string, signal: CloseSignal) => void;
|
|
154
|
-
};
|
|
155
|
-
export type SocketData = {
|
|
156
|
-
clientId: string;
|
|
157
|
-
superseded: boolean;
|
|
158
|
-
reaped: boolean;
|
|
159
|
-
reaperHandle: ReturnType<typeof setTimeout> | null;
|
|
160
|
-
app: unknown;
|
|
161
|
-
};
|
|
162
|
-
export type SocketConnection = {
|
|
163
|
-
data: SocketData;
|
|
164
|
-
send: (data: string) => unknown;
|
|
165
|
-
close: (code?: number, reason?: string) => void;
|
|
166
|
-
};
|
|
167
141
|
export type Server = BunServer<SocketData>;
|
|
168
|
-
export type RouteDefinition = {
|
|
169
|
-
method: HttpMethod;
|
|
170
|
-
path: string;
|
|
171
|
-
chain: Handler | MiddlewareChain;
|
|
172
|
-
};
|
|
173
|
-
export type MetaEntry = {
|
|
174
|
-
path: string;
|
|
175
|
-
middleware: Middleware[];
|
|
176
|
-
};
|
|
177
|
-
export type RouteConfig = {
|
|
178
|
-
routes: RouteDefinition[];
|
|
179
|
-
meta?: MetaEntry[];
|
|
180
|
-
};
|
|
181
|
-
export type AppOptions = {
|
|
182
|
-
hostname?: string;
|
|
183
|
-
mountPath?: string;
|
|
184
|
-
middleware?: Middleware[];
|
|
185
|
-
ws?: boolean | SocketOptions;
|
|
186
|
-
onClose?: () => Promise<void> | void;
|
|
187
|
-
};
|
|
188
142
|
export declare function toSegments(pathString: string): string[];
|
|
189
143
|
export declare function formatError(prefix: string, input: ValidationError): FormattedError;
|
|
190
144
|
export declare function executeMiddlewareChain(req: Request, chain: MiddlewareChain): AsyncHandlerResult;
|
package/dist/core/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/core/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,KAAK,CAAA;AAE1D,eAAO,MAAM,UAAU;;;;;;;CAOb,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,MAAM,OAAO,UAAU,CAAC,CAAA;AAEnE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmEb,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,MAAM,OAAO,UAAU,CAAC,CAAA;AAEnE,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;AAElD,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAClD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,kBAAkB,CAAA;AACzD,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,aAAa,CAAA;AAEtD,MAAM,MAAM,UAAU,GAAG,CACvB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,KACT,aAAa,CAAA;AAElB,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,OAAO,KACT,aAAa,CAAA;AAElB,MAAM,MAAM,eAAe,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,EAAE,CAAA;AAEtD,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/core/utils.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AAEtB,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AAEtB,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAE7C,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,UAAU,GAChB,GAAG,CAAA"}
|
package/package.json
CHANGED
package/src/core/index.ts
CHANGED
|
@@ -31,26 +31,28 @@ import type {
|
|
|
31
31
|
Middleware,
|
|
32
32
|
MiddlewareChain,
|
|
33
33
|
EndpointRequest,
|
|
34
|
-
|
|
35
|
-
RouteConfig,
|
|
36
|
-
RouteDefinition,
|
|
37
|
-
SocketCommands,
|
|
38
|
-
SocketOptions,
|
|
39
|
-
AppOptions,
|
|
34
|
+
Handler,
|
|
40
35
|
Server,
|
|
41
36
|
} from './utils'
|
|
42
37
|
|
|
43
38
|
import type {
|
|
39
|
+
SocketCommands,
|
|
40
|
+
} from './utils'
|
|
41
|
+
|
|
42
|
+
import type {
|
|
43
|
+
SocketOptions,
|
|
44
44
|
SocketRoute,
|
|
45
45
|
SocketState,
|
|
46
46
|
} from './socket'
|
|
47
47
|
|
|
48
48
|
export * from './errors'
|
|
49
|
+
export { ServerCloseSignals } from './socket'
|
|
49
50
|
|
|
50
51
|
export {
|
|
51
52
|
StatusCode,
|
|
52
53
|
HttpMethod,
|
|
53
|
-
|
|
54
|
+
SessionType,
|
|
55
|
+
SessionFilter,
|
|
54
56
|
} from './utils'
|
|
55
57
|
|
|
56
58
|
export {
|
|
@@ -59,27 +61,31 @@ export {
|
|
|
59
61
|
validateSchemas,
|
|
60
62
|
} from './middleware'
|
|
61
63
|
|
|
62
|
-
export type {
|
|
64
|
+
export type {
|
|
65
|
+
ActiveSession,
|
|
66
|
+
ActiveSessions,
|
|
67
|
+
InactiveSession,
|
|
68
|
+
SocketConnection,
|
|
69
|
+
SocketOptions,
|
|
70
|
+
} from './socket'
|
|
63
71
|
|
|
64
72
|
export type {
|
|
65
|
-
AppOptions,
|
|
66
73
|
AsyncHandlerResult,
|
|
67
74
|
BaseRequest,
|
|
68
75
|
CloseSignal,
|
|
69
76
|
EndpointRequest,
|
|
77
|
+
FilterFn,
|
|
70
78
|
FormattedError,
|
|
71
79
|
Handler,
|
|
72
|
-
|
|
80
|
+
HandlerResult,
|
|
73
81
|
Middleware,
|
|
74
82
|
MiddlewareChain,
|
|
75
83
|
NextFn,
|
|
76
|
-
HandlerResult,
|
|
77
84
|
Request,
|
|
78
|
-
RouteConfig,
|
|
79
|
-
RouteDefinition,
|
|
80
85
|
Server,
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
SessionEntry,
|
|
87
|
+
SocketCommands,
|
|
88
|
+
SocketData,
|
|
83
89
|
WebSocketRequest,
|
|
84
90
|
} from './utils'
|
|
85
91
|
|
|
@@ -89,6 +95,30 @@ export type {
|
|
|
89
95
|
ValidationSchemas,
|
|
90
96
|
} from './middleware'
|
|
91
97
|
|
|
98
|
+
export type RouteDefinition = {
|
|
99
|
+
method: HttpMethod
|
|
100
|
+
path: string
|
|
101
|
+
chain: Handler | MiddlewareChain
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type MetaEntry = {
|
|
105
|
+
path: string
|
|
106
|
+
middleware: Middleware[]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type RouteConfig = {
|
|
110
|
+
routes: RouteDefinition[]
|
|
111
|
+
meta?: MetaEntry[]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type AppOptions = {
|
|
115
|
+
hostname?: string
|
|
116
|
+
mountPath?: string
|
|
117
|
+
middleware?: Middleware[]
|
|
118
|
+
ws?: boolean | SocketOptions
|
|
119
|
+
onClose?: () => Promise<void> | void
|
|
120
|
+
}
|
|
121
|
+
|
|
92
122
|
type OutputRoutes = Record<string, string[]>
|
|
93
123
|
type ServerRoutes = Record<string, Record<string, EndpointHandler>>
|
|
94
124
|
|
package/src/core/socket.ts
CHANGED
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
StatusCode,
|
|
13
|
-
|
|
13
|
+
SessionType,
|
|
14
|
+
SessionFilter,
|
|
14
15
|
toSegments,
|
|
15
16
|
formatError,
|
|
16
17
|
executeMiddlewareChain,
|
|
@@ -37,14 +38,9 @@ import type {
|
|
|
37
38
|
FilterFn,
|
|
38
39
|
MiddlewareChain,
|
|
39
40
|
SocketCommands,
|
|
41
|
+
SocketData,
|
|
40
42
|
SessionEntry,
|
|
41
43
|
WebSocketRequest,
|
|
42
|
-
SocketData,
|
|
43
|
-
SocketConnection,
|
|
44
|
-
ActiveSession,
|
|
45
|
-
InactiveSession,
|
|
46
|
-
Session,
|
|
47
|
-
SocketOptions,
|
|
48
44
|
} from './utils'
|
|
49
45
|
|
|
50
46
|
import type {
|
|
@@ -91,6 +87,55 @@ type UpdateTicketRequest = {
|
|
|
91
87
|
}
|
|
92
88
|
}
|
|
93
89
|
|
|
90
|
+
export type SocketConnection = {
|
|
91
|
+
data: SocketData
|
|
92
|
+
send: (data: string) => unknown
|
|
93
|
+
close: (code?: number, reason?: string) => void
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type ActiveSession = {
|
|
97
|
+
token: string
|
|
98
|
+
ws: SocketConnection
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type InactiveSession = {
|
|
102
|
+
token: string
|
|
103
|
+
expiresAt: number
|
|
104
|
+
data: unknown
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type ActiveSessions = ReadonlyMap<string, ActiveSession>
|
|
108
|
+
|
|
109
|
+
export const ServerCloseSignals: Record<string, CloseSignal> = {
|
|
110
|
+
Ok: {
|
|
111
|
+
code: 1000,
|
|
112
|
+
reason: 'ok',
|
|
113
|
+
},
|
|
114
|
+
Reaped: {
|
|
115
|
+
code: 4998,
|
|
116
|
+
reason: 'reaped',
|
|
117
|
+
},
|
|
118
|
+
Superseded: {
|
|
119
|
+
code: 4999,
|
|
120
|
+
reason: 'superseded',
|
|
121
|
+
},
|
|
122
|
+
} as const
|
|
123
|
+
|
|
124
|
+
export type ServerCloseSignals =
|
|
125
|
+
typeof ServerCloseSignals[keyof typeof ServerCloseSignals]
|
|
126
|
+
|
|
127
|
+
export type Session = ActiveSession | InactiveSession
|
|
128
|
+
|
|
129
|
+
export type SocketOptions = {
|
|
130
|
+
dropThreshold?: number
|
|
131
|
+
heartbeatInterval?: number
|
|
132
|
+
maxTickets?: number
|
|
133
|
+
reclaimTtl?: number
|
|
134
|
+
ticketTtl?: number
|
|
135
|
+
onOpen?: (clientId: string) => void
|
|
136
|
+
onClose?: (clientId: string, signal: CloseSignal) => void
|
|
137
|
+
}
|
|
138
|
+
|
|
94
139
|
export type Ticket = {
|
|
95
140
|
clientId: string
|
|
96
141
|
expiresAt: number
|
|
@@ -425,8 +470,8 @@ export function buildSocketServer (
|
|
|
425
470
|
ws.data.reaped = true
|
|
426
471
|
|
|
427
472
|
ws.close(
|
|
428
|
-
|
|
429
|
-
|
|
473
|
+
ServerCloseSignals.Reaped.code,
|
|
474
|
+
ServerCloseSignals.Reaped.reason,
|
|
430
475
|
)
|
|
431
476
|
}, dropThreshold)
|
|
432
477
|
}
|
|
@@ -462,8 +507,8 @@ export function buildSocketServer (
|
|
|
462
507
|
existingSession.ws.data.superseded = true
|
|
463
508
|
|
|
464
509
|
existingSession.ws.close(
|
|
465
|
-
|
|
466
|
-
|
|
510
|
+
ServerCloseSignals.Superseded.code,
|
|
511
|
+
ServerCloseSignals.Superseded.reason,
|
|
467
512
|
)
|
|
468
513
|
}
|
|
469
514
|
|
|
@@ -515,11 +560,11 @@ export function buildSocketServer (
|
|
|
515
560
|
|
|
516
561
|
activeSessions.delete(ws.data.clientId)
|
|
517
562
|
|
|
518
|
-
if (code !==
|
|
563
|
+
if (code !== ServerCloseSignals.Ok.code || ws.data.reaped) {
|
|
519
564
|
inactiveSessions.set(ws.data.clientId, {
|
|
520
565
|
token: exists.token,
|
|
521
566
|
expiresAt: Date.now() + reclaimTtl,
|
|
522
|
-
|
|
567
|
+
data: ws.data.data,
|
|
523
568
|
})
|
|
524
569
|
}
|
|
525
570
|
|
|
@@ -635,7 +680,7 @@ export function buildSocketHandlers (state: SocketState): SocketEndpoint[] {
|
|
|
635
680
|
ctx.data.superseded = false
|
|
636
681
|
ctx.data.reaped = false
|
|
637
682
|
ctx.data.reaperHandle = null
|
|
638
|
-
ctx.data.
|
|
683
|
+
ctx.data.data = ticket.data
|
|
639
684
|
|
|
640
685
|
const useSocket = validReq.server.upgrade(validReq.raw, ctx)
|
|
641
686
|
|
|
@@ -691,7 +736,7 @@ export function buildSocketHandlers (state: SocketState): SocketEndpoint[] {
|
|
|
691
736
|
throw new UnauthorizedError('Invalid token')
|
|
692
737
|
}
|
|
693
738
|
|
|
694
|
-
const appData = 'ws' in session ? session.ws.data.
|
|
739
|
+
const appData = 'ws' in session ? session.ws.data.data : session.data
|
|
695
740
|
|
|
696
741
|
return Response.json({
|
|
697
742
|
clientId: validReq.params.clientId,
|
|
@@ -742,7 +787,13 @@ export function buildSocketCommands (state: SocketState): SocketCommands {
|
|
|
742
787
|
|
|
743
788
|
/* TODO: look into concurrency at some point */
|
|
744
789
|
for (const [clientId, session] of state.activeSessions) {
|
|
745
|
-
|
|
790
|
+
const entry: SessionEntry = {
|
|
791
|
+
clientId,
|
|
792
|
+
type: SessionType.Active,
|
|
793
|
+
data: session.ws.data.data,
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (fn(entry, index)) {
|
|
746
797
|
sendToClient(clientId, event, body)
|
|
747
798
|
}
|
|
748
799
|
|
|
@@ -761,28 +812,66 @@ export function buildSocketCommands (state: SocketState): SocketCommands {
|
|
|
761
812
|
}
|
|
762
813
|
|
|
763
814
|
for (const [clientId, session] of state.activeSessions) {
|
|
764
|
-
|
|
815
|
+
const entry: SessionEntry = {
|
|
816
|
+
clientId,
|
|
817
|
+
type: SessionType.Active,
|
|
818
|
+
data: session.ws.data.data,
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (fn(entry, index)) {
|
|
765
822
|
session.ws.close(code, reason)
|
|
766
823
|
}
|
|
767
824
|
|
|
768
825
|
index += 1
|
|
769
826
|
}
|
|
770
827
|
},
|
|
771
|
-
query (
|
|
828
|
+
query (
|
|
829
|
+
fn: FilterFn,
|
|
830
|
+
filter: SessionFilter = SessionFilter.Active,
|
|
831
|
+
) {
|
|
772
832
|
const results: SessionEntry[] = []
|
|
773
|
-
let index = 0
|
|
774
833
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
834
|
+
function processSessions (
|
|
835
|
+
type: SessionType,
|
|
836
|
+
filter: SessionFilter,
|
|
837
|
+
startIndex: number,
|
|
838
|
+
): number {
|
|
839
|
+
let index = startIndex
|
|
840
|
+
|
|
841
|
+
const sessions = type === SessionType.Active
|
|
842
|
+
? state.activeSessions
|
|
843
|
+
: state.inactiveSessions
|
|
844
|
+
|
|
845
|
+
if (type !== filter && filter !== SessionFilter.All) {
|
|
846
|
+
return index
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
for (const [clientId, session] of sessions) {
|
|
850
|
+
const data = 'ws' in session ? session.ws.data.data : session.data
|
|
851
|
+
|
|
852
|
+
const entry: SessionEntry = {
|
|
778
853
|
clientId,
|
|
779
|
-
|
|
780
|
-
|
|
854
|
+
type,
|
|
855
|
+
data,
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (fn(entry, index)) {
|
|
859
|
+
results.push(entry)
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
index += 1
|
|
781
863
|
}
|
|
782
864
|
|
|
783
|
-
index
|
|
865
|
+
return index
|
|
784
866
|
}
|
|
785
867
|
|
|
868
|
+
let index = 0
|
|
869
|
+
|
|
870
|
+
sweepInactiveSessions(state)
|
|
871
|
+
|
|
872
|
+
index = processSessions(SessionType.Active, filter, index)
|
|
873
|
+
index = processSessions(SessionType.Inactive, filter, index)
|
|
874
|
+
|
|
786
875
|
return results
|
|
787
876
|
},
|
|
788
877
|
}
|
package/src/core/utils.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ErrorObject } from 'ajv'
|
|
2
2
|
import type { BunRequest, Server as BunServer } from 'bun'
|
|
3
3
|
|
|
4
|
+
export type TimeoutHandle = ReturnType<typeof setTimeout>
|
|
5
|
+
|
|
4
6
|
export const HttpMethod = {
|
|
5
7
|
Head: 'HEAD',
|
|
6
8
|
Get: 'GET',
|
|
@@ -107,28 +109,42 @@ export type Handler = (
|
|
|
107
109
|
|
|
108
110
|
export type MiddlewareChain = (Middleware | Handler)[]
|
|
109
111
|
|
|
110
|
-
export type
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
export type CloseSignal = {
|
|
113
|
+
code: number
|
|
114
|
+
reason: string
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
export type
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
export type SocketData = {
|
|
118
|
+
clientId: string
|
|
119
|
+
superseded: boolean
|
|
120
|
+
reaped: boolean
|
|
121
|
+
reaperHandle: TimeoutHandle | null
|
|
122
|
+
data: unknown
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
export
|
|
122
|
-
|
|
125
|
+
export const SessionType = {
|
|
126
|
+
Active: 'active',
|
|
127
|
+
Inactive: 'inactive',
|
|
128
|
+
} as const
|
|
129
|
+
|
|
130
|
+
export type SessionType = typeof SessionType[keyof typeof SessionType]
|
|
131
|
+
|
|
132
|
+
export const SessionFilter = {
|
|
133
|
+
Active: 'active',
|
|
134
|
+
Inactive: 'inactive',
|
|
135
|
+
All: 'all',
|
|
136
|
+
} as const
|
|
137
|
+
|
|
138
|
+
export type SessionFilter = typeof SessionFilter[keyof typeof SessionFilter]
|
|
123
139
|
|
|
124
140
|
export type SessionEntry = {
|
|
125
141
|
clientId: string
|
|
126
|
-
|
|
142
|
+
type: SessionType
|
|
143
|
+
data: unknown
|
|
127
144
|
}
|
|
128
145
|
|
|
129
146
|
export type FilterFn = (
|
|
130
|
-
|
|
131
|
-
data: unknown,
|
|
147
|
+
session: SessionEntry,
|
|
132
148
|
index: number,
|
|
133
149
|
) => boolean
|
|
134
150
|
|
|
@@ -136,7 +152,7 @@ export type SocketCommands = {
|
|
|
136
152
|
broadcast: (event: string, body: unknown) => void
|
|
137
153
|
send: (event: string, body: unknown, fn: FilterFn) => void
|
|
138
154
|
drop: (signal: CloseSignal, fn: FilterFn) => void
|
|
139
|
-
query: (fn: FilterFn) => SessionEntry[]
|
|
155
|
+
query: (fn: FilterFn, filter?: SessionFilter) => SessionEntry[]
|
|
140
156
|
}
|
|
141
157
|
|
|
142
158
|
export type BaseRequest = {
|
|
@@ -161,76 +177,8 @@ export type WebSocketRequest = BaseRequest & {
|
|
|
161
177
|
|
|
162
178
|
export type Request = EndpointRequest | WebSocketRequest
|
|
163
179
|
|
|
164
|
-
export type CloseSignal = {
|
|
165
|
-
code: number
|
|
166
|
-
reason: string
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export const InternalCloseSignal = {
|
|
170
|
-
Ok: {
|
|
171
|
-
code: 1000,
|
|
172
|
-
reason: 'ok',
|
|
173
|
-
},
|
|
174
|
-
Reaped: {
|
|
175
|
-
code: 4998,
|
|
176
|
-
reason: 'reaped',
|
|
177
|
-
},
|
|
178
|
-
Superseded: {
|
|
179
|
-
code: 4999,
|
|
180
|
-
reason: 'superseded',
|
|
181
|
-
},
|
|
182
|
-
} as const
|
|
183
|
-
|
|
184
|
-
export type SocketOptions = {
|
|
185
|
-
dropThreshold?: number
|
|
186
|
-
heartbeatInterval?: number
|
|
187
|
-
maxTickets?: number
|
|
188
|
-
reclaimTtl?: number
|
|
189
|
-
ticketTtl?: number
|
|
190
|
-
onOpen?: (clientId: string) => void
|
|
191
|
-
onClose?: (clientId: string, signal: CloseSignal) => void
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export type SocketData = {
|
|
195
|
-
clientId: string
|
|
196
|
-
superseded: boolean
|
|
197
|
-
reaped: boolean
|
|
198
|
-
reaperHandle: ReturnType<typeof setTimeout> | null
|
|
199
|
-
app: unknown
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export type SocketConnection = {
|
|
203
|
-
data: SocketData
|
|
204
|
-
send: (data: string) => unknown
|
|
205
|
-
close: (code?: number, reason?: string) => void
|
|
206
|
-
}
|
|
207
|
-
|
|
208
180
|
export type Server = BunServer<SocketData>
|
|
209
181
|
|
|
210
|
-
export type RouteDefinition = {
|
|
211
|
-
method: HttpMethod
|
|
212
|
-
path: string
|
|
213
|
-
chain: Handler | MiddlewareChain
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export type MetaEntry = {
|
|
217
|
-
path: string
|
|
218
|
-
middleware: Middleware[]
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export type RouteConfig = {
|
|
222
|
-
routes: RouteDefinition[]
|
|
223
|
-
meta?: MetaEntry[]
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export type AppOptions = {
|
|
227
|
-
hostname?: string
|
|
228
|
-
mountPath?: string
|
|
229
|
-
middleware?: Middleware[]
|
|
230
|
-
ws?: boolean | SocketOptions
|
|
231
|
-
onClose?: () => Promise<void> | void
|
|
232
|
-
}
|
|
233
|
-
|
|
234
182
|
export function toSegments (pathString: string): string[] {
|
|
235
183
|
const [pathname] = String(pathString).split('?')
|
|
236
184
|
const segments = pathname.split('/')
|