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 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 active sessions. The filter function receives `(clientId, data, index)` and returns a boolean. Each entry in the returned array is a `SessionEntry` with `clientId` and `app` (the application context). To list all sessions: `app.ws.query(() => true)`.
575
- - `send(event, body, fn)`: push a notification to clients matching a filter. The filter function receives `(clientId, data, index)` and returns a boolean. To target one client: `app.ws.send('ping', body, id => id === targetId)`.
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 `(clientId, data, index)` and returns a boolean. To drop one client: `app.ws.drop({ code: 4000, reason: 'kicked' }, id => id === targetId)`.
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' }, id => id === targetId)
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
- - `InternalCloseSignal`: internal close signals: `Ok` (`{ code: 1000, reason: 'ok' }`), `Reaped` (`{ code: 4998, reason: 'reaped' }`), `Superseded` (`{ code: 4999, reason: 'superseded' }`)
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
- - `SessionEntry`: the shape returned by `query()`: `{ clientId: string, app: unknown }`
602
- - `FilterFn`: the filter callback type: `(clientId: string, data: unknown, index: number) => boolean`
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`
@@ -1,10 +1,33 @@
1
- import type { RouteConfig, SocketCommands, AppOptions, Server } from './utils';
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 { StatusCode, HttpMethod, InternalCloseSignal, } from './utils';
5
+ export { ServerCloseSignals } from './socket';
6
+ export { StatusCode, HttpMethod, SessionType, SessionFilter, } from './utils';
4
7
  export { parseJsonBody, setValidationFormats, validateSchemas, } from './middleware';
5
- export type { FilterFn, SessionEntry, SocketCommands } from './utils';
6
- export type { AppOptions, AsyncHandlerResult, BaseRequest, CloseSignal, EndpointRequest, FormattedError, Handler, MetaEntry, Middleware, MiddlewareChain, NextFn, HandlerResult, Request, RouteConfig, RouteDefinition, Server, SocketConnection, SocketOptions, WebSocketRequest, } from './utils';
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 = {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAOV,WAAW,EAEX,cAAc,EAEd,UAAU,EACV,MAAM,EACP,MAAM,SAAS,CAAA;AAOhB,cAAc,UAAU,CAAA;AAExB,OAAO,EACL,UAAU,EACV,UAAU,EACV,mBAAmB,GACpB,MAAM,SAAS,CAAA;AAEhB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAA;AAErB,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAErE,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,eAAe,EACf,cAAc,EACd,OAAO,EACP,SAAS,EACT,UAAU,EACV,eAAe,EACf,MAAM,EACN,aAAa,EACb,OAAO,EACP,WAAW,EACX,eAAe,EACf,MAAM,EACN,gBAAgB,EAChB,aAAa,EACb,gBAAgB,GACjB,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,cAAc,CAAA;AAErB,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"}
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"}
@@ -1,11 +1,38 @@
1
1
  import type { WebSocketHandler } from 'bun';
2
- import type { AsyncHandlerResult, CloseSignal, HttpMethod, Request, MiddlewareChain, SocketCommands, SocketData, ActiveSession, InactiveSession, SocketOptions } from './utils';
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":"AA6BA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAE3C,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,OAAO,EAEP,eAAe,EACf,cAAc,EAGd,UAAU,EAEV,aAAa,EACb,eAAe,EAEf,aAAa,EACd,MAAM,SAAS,CAAA;AAShB,KAAK,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,kBAAkB,CAAA;AAYvE,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,aAAa,CAAA;CACvB,CAAA;AAqBD,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IAC1C,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC9C,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IAC3C,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;CAClE,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,KAAK,EAAE,eAAe,CAAA;CACvB,CAAA;AA+QD,wBAAgB,gBAAgB,CAAE,IAAI,GAAE,aAAkB,GAAG,WAAW,CAavE;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EAAE,EACrB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,cAAc,GACvB,gBAAgB,CAAC,UAAU,CAAC,CA8J9B;AAED,wBAAgB,mBAAmB,CAAE,KAAK,EAAE,WAAW,GAAG,cAAc,EAAE,CAwIzE;AAED,wBAAgB,2BAA2B,IAAK,cAAc,CAS7D;AAED,wBAAgB,mBAAmB,CAAE,KAAK,EAAE,WAAW,GAAG,cAAc,CAwEvE"}
1
+ {"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/core/socket.ts"],"names":[],"mappings":"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"}
@@ -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 ActiveSession = {
89
- token: string;
90
- ws: SocketConnection;
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 InactiveSession = {
93
- token: string;
94
- expiresAt: number;
95
- app: unknown;
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 ActiveSessions = ReadonlyMap<string, ActiveSession>;
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
- app: unknown;
113
+ type: SessionType;
114
+ data: unknown;
102
115
  };
103
- export type FilterFn = (clientId: string, data: unknown, index: number) => boolean;
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;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/core/utils.ts"],"names":[],"mappings":"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"}
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
@@ -1,4 +1,4 @@
1
1
  export * from './core';
2
- import type { App, AppOptions } from './core';
2
+ import type { AppOptions, App } from './core';
3
3
  export declare function createApp(port: number, opts?: AppOptions): App;
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AAEtB,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAE7C,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,UAAU,GAChB,GAAG,CAAA"}
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
@@ -3,7 +3,7 @@
3
3
  "description": "A more opinionated web server for building REST-ful applications",
4
4
  "author": "Travis J True",
5
5
  "license": "MIT",
6
- "version": "0.23.0",
6
+ "version": "0.24.0",
7
7
  "type": "module",
8
8
  "bin": {
9
9
  "sleepy": "./src/cli/index.ts"
package/src/core/index.ts CHANGED
@@ -31,26 +31,28 @@ import type {
31
31
  Middleware,
32
32
  MiddlewareChain,
33
33
  EndpointRequest,
34
- MetaEntry,
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
- InternalCloseSignal,
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 { FilterFn, SessionEntry, SocketCommands } from './utils'
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
- MetaEntry,
80
+ HandlerResult,
73
81
  Middleware,
74
82
  MiddlewareChain,
75
83
  NextFn,
76
- HandlerResult,
77
84
  Request,
78
- RouteConfig,
79
- RouteDefinition,
80
85
  Server,
81
- SocketConnection,
82
- SocketOptions,
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
 
@@ -10,7 +10,8 @@ import {
10
10
 
11
11
  import {
12
12
  StatusCode,
13
- InternalCloseSignal,
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
- InternalCloseSignal.Reaped.code,
429
- InternalCloseSignal.Reaped.reason,
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
- InternalCloseSignal.Superseded.code,
466
- InternalCloseSignal.Superseded.reason,
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 !== InternalCloseSignal.Ok.code || ws.data.reaped) {
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
- app: ws.data.app,
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.app = ticket.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.app : session.app
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
- if (fn(clientId, session.ws.data, index)) {
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
- if (fn(clientId, session.ws.data, index)) {
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 (fn: FilterFn) {
828
+ query (
829
+ fn: FilterFn,
830
+ filter: SessionFilter = SessionFilter.Active,
831
+ ) {
772
832
  const results: SessionEntry[] = []
773
- let index = 0
774
833
 
775
- for (const [clientId, session] of state.activeSessions) {
776
- if (fn(clientId, session.ws.data, index)) {
777
- results.push({
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
- app: session.ws.data.app,
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 += 1
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 ActiveSession = {
111
- token: string
112
- ws: SocketConnection
112
+ export type CloseSignal = {
113
+ code: number
114
+ reason: string
113
115
  }
114
116
 
115
- export type InactiveSession = {
116
- token: string
117
- expiresAt: number
118
- app: unknown
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 type ActiveSessions = ReadonlyMap<string, ActiveSession>
122
- export type Session = ActiveSession | InactiveSession
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
- app: unknown
142
+ type: SessionType
143
+ data: unknown
127
144
  }
128
145
 
129
146
  export type FilterFn = (
130
- clientId: string,
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('/')
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from './core'
2
2
 
3
- import type { App, AppOptions } from './core'
3
+ import type { AppOptions, App } from './core'
4
4
 
5
5
  export declare function createApp (
6
6
  port: number,