@owlmeans/server-socket 0.1.18-rc.5 → 0.1.18-rc.50

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
@@ -1,69 +1,205 @@
1
1
  # @owlmeans/server-socket
2
2
 
3
- Server-side WebSocket connection handler and service for OwlMeans backends.
4
-
5
- ## Overview
6
-
7
- - `handleConnection()` wraps WebSocket business logic — analogous to `handleBody` but for WS connections
8
- - `appendSocketService()` + `createSocketMiddleware()` wire the WebSocket service into a server context
9
- - Used in viable for real-time thinking journal updates and file watching
10
- - The `Connection` interface (from `@owlmeans/socket`) is the runtime object passed to your handler
3
+ The server WebSocket carrier for OwlMeans backends: binds socket protocol declarations to connection
4
+ handlers on the Fastify API server. An application uses `connection(protocol, callback)` for push
5
+ and streaming channels — live notifications, file or job progress, RPC over one long-lived
6
+ connection. `@owlmeans/server-app`'s `makeContext` already registers the socket service and its
7
+ middleware. Request/response HTTP routes use `handlers<Context>()` from `@owlmeans/server-api`, the
8
+ browser side is `@owlmeans/client-socket`, and background work that must survive a restart belongs
9
+ in a queue (`@owlmeans/server-job`).
11
10
 
12
11
  ## Installation
13
12
 
14
13
  ```bash
15
- bun add @owlmeans/server-socket
14
+ bun add @owlmeans/server-socket@^0.1.18-rc.50
16
15
  ```
17
16
 
17
+ ## Concepts
18
+
19
+ - **Socket protocol** — a declaration whose route is built with `socket()` from `@owlmeans/route`.
20
+ The HTTP server skips it; the socket service mounts it as a `GET` WebSocket route.
21
+ - **Connection** — the `@owlmeans/socket` `Connection` model. This package assigns its `send`,
22
+ `close`, `authenticate` and `prepare`; the handler uses `notify`, `listen`, `observe`, `perform`,
23
+ `call` and `close`.
24
+ - **Upgrade-time guards** — the protocol's guards and gates run before the upgrade with the same
25
+ pipeline as HTTP, including organization-entity resolution, so a guarded socket arrives with
26
+ `request.auth` and `request.entity` set.
27
+ - **In-band authentication** — a socket that receives its token after the connection is open
28
+ assigns `conn.authenticate`; that code path establishes authentication and must resolve the entity
29
+ itself.
30
+ - **System frames** — on close, listeners receive `{ type: MessageType.System, event: 'close',
31
+ payload: { code } }`. A thrown callback closes the socket with code 1011; server shutdown closes
32
+ open sockets with 1001.
33
+
18
34
  ## Usage
19
35
 
20
- Handle a WebSocket connection on a module route:
36
+ ### Declare and bind
21
37
 
22
- ```typescript
23
- import { handleConnection } from '@owlmeans/server-socket'
24
- import type { Connection } from '@owlmeans/socket'
38
+ ```ts
39
+ // my-app-common
40
+ import { contract, protocol, typed } from '@owlmeans/entrypoint'
41
+ import { route, socket } from '@owlmeans/route'
42
+
43
+ export const projectProtocols = {
44
+ // ...base, list, create
45
+ events: protocol(
46
+ route('my-app:api:project:events', '/:id/events', socket({ parent: projectBase })),
47
+ contract.request({ params: typed<ProjectParams>(ProjectParamsSchema) }, typed<void>()),
48
+ ),
49
+ }
50
+ ```
25
51
 
26
- export const watch = handleConnection<Connection>(async (conn, context, req) => {
27
- const ctx = context as Context
28
- const projectId = req.params.id as string
52
+ ```ts
53
+ // my-app-api
54
+ import { bind } from '@owlmeans/server-app'
55
+ import { connection } from '@owlmeans/server-socket'
56
+ import { projectProtocols } from 'my-app-common'
57
+ import type { Context } from './context.js'
29
58
 
30
- // Observe events sent by the client
31
- const unsubscribe = conn.observe<FileEvent>('file-change', async (event) => {
32
- await ctx.fileStore().save({ projectId, ...event.payload })
59
+ const events = connection<typeof projectProtocols.events, Context>(projectProtocols.events,
60
+ async (conn, context, request) => {
61
+ await conn.notify('project:ready', { id: request.params.id })
33
62
  })
34
63
 
35
- // Push updates to the client
36
- const listener = ctx.fileStore().listen(record => {
37
- conn.notify('file-update', record)
64
+ export const appEntrypoints = [
65
+ bind(projectProtocols.events, events),
66
+ ]
67
+ ```
68
+
69
+ ### Push organization-scoped events and clean up
70
+
71
+ The parent declares `guards`, so authentication and the entity are resolved before the upgrade.
72
+
73
+ ```ts
74
+ import { requireEntityKey } from '@owlmeans/auth-common'
75
+ import { MessageType } from '@owlmeans/socket'
76
+ import type { EventMessage } from '@owlmeans/socket'
77
+
78
+ export const events = connection<typeof projectProtocols.events, Context>(projectProtocols.events,
79
+ async (conn, context, request) => {
80
+ const entityId = requireEntityKey(request)
81
+
82
+ // app-local pub/sub resource, one channel per organization
83
+ const unsubscribe = await context.projectEvents().subscribe(async event => {
84
+ if (event.projectId === request.params.id) {
85
+ await conn.notify('project:event', event)
86
+ }
87
+ }, { channel: entityId })
88
+
89
+ conn.listen(async raw => {
90
+ if (typeof raw !== 'object') return
91
+ const message = raw as EventMessage<void>
92
+ if (message.type === MessageType.System && message.event === 'close') {
93
+ await unsubscribe()
94
+ }
95
+ })
38
96
  })
39
- })
40
97
  ```
41
98
 
42
- ## API
99
+ ### Authenticate in-band
100
+
101
+ When the client cannot send a bearer on the upgrade request, it sends the token as the first auth
102
+ frame. The handler runs the guard itself and must call `attachEntity`.
103
+
104
+ ```ts
105
+ import { AUTH_HEADER, AuthenFailed, AuthenticationStage, AuthUnknown } from '@owlmeans/auth'
106
+ import type { Auth, AuthToken } from '@owlmeans/auth'
107
+ import { attachEntity, DEFAULT_GUARD, requireEntityKey } from '@owlmeans/auth-common'
108
+ import { provideResponse } from '@owlmeans/entrypoint'
109
+ import type { AbstractResponse, GuardService } from '@owlmeans/entrypoint'
110
+ import type { AuthenticateMethod } from '@owlmeans/socket'
43
111
 
44
- ### `handleConnection<T>(handler): RefedModuleHandler`
112
+ export const watch = connection<typeof projectProtocols.watch, Context>(projectProtocols.watch,
113
+ async (conn, context, request) => {
114
+ const timeout = setTimeout(() => void conn.close(), 5_000)
45
115
 
46
- Wraps a WebSocket handler. The handler receives `(conn: T, ctx, req, res)`.
116
+ conn.authenticate = (async (stage: AuthenticationStage, token: AuthToken) => {
117
+ if (stage !== AuthenticationStage.Authenticate) {
118
+ throw new AuthUnknown('stage')
119
+ }
47
120
 
48
- ```typescript
49
- handler: (conn: T, ctx: Context, req: AbstractRequest, res: AbstractResponse) => Promise<void>
121
+ request.headers[AUTH_HEADER] ??= token.token
122
+ const response: AbstractResponse<Auth> = provideResponse({ header: () => void 0 })
123
+ const guard = context.service<GuardService>(DEFAULT_GUARD)
124
+ if (!await guard.handle(request, response) || response.value == null) {
125
+ throw new AuthenFailed('guard')
126
+ }
127
+
128
+ request.auth = response.value
129
+ // This path establishes authentication, so it resolves the organization entity itself.
130
+ await attachEntity(context, request)
131
+ clearTimeout(timeout)
132
+
133
+ await startWatching(conn, context, requireEntityKey(request))
134
+
135
+ return [AuthenticationStage.Authenticated, true]
136
+ }) as AuthenticateMethod
137
+ })
50
138
  ```
51
139
 
52
- The `conn` object is a server-side `Connection` (from `@owlmeans/socket`).
140
+ ### RPC over a connection
141
+
142
+ ```ts
143
+ export const terminal = connection<typeof projectProtocols.terminal, Context>(projectProtocols.terminal,
144
+ async (conn, context, request) => {
145
+ const entityId = requireEntityKey(request)
53
146
 
54
- ### `appendSocketService<C, T>(context): void`
147
+ conn.perform('project:rename', async (id: string, name: string) => {
148
+ const project = await context.project().load({ id, entityId })
149
+ return project == null ? null : context.project().update({ ...project, name })
150
+ })
151
+ })
152
+ ```
55
153
 
56
- Registers the WebSocket service into the context. Called automatically by `makeContext()`.
154
+ The browser side calls it with `conn.call('project:rename', id, name)` on a
155
+ `@owlmeans/client-socket` connection.
57
156
 
58
- ### `createSocketMiddleware(): Middleware`
157
+ ### A context without `@owlmeans/server-app`
59
158
 
60
- Creates the middleware that initializes the socket service during context init.
159
+ ```ts
160
+ import { appendApiServer } from '@owlmeans/server-api'
161
+ import { appendSocketService, createSocketMiddleware } from '@owlmeans/server-socket'
61
162
 
62
- ## Related Packages
163
+ appendApiServer(context)
164
+ appendSocketService(context)
165
+ context.registerMiddleware(createSocketMiddleware())
166
+ ```
167
+
168
+ ## API
63
169
 
64
- - [`@owlmeans/socket`](../socket) — `Connection`, `MessageType`, `EventMessage` types
170
+ | Symbol | Kind | Purpose |
171
+ |---|---|---|
172
+ | `connection<Protocol, SocketContext, SocketConnection>(protocol, handler)` | function | Bind a socket protocol to `(connection, context, request, response) => Promise<void>`; returns `BoundEntrypointHandler<Protocol>` |
173
+ | `handleConnection(handler)` | function | Unbound compatibility wrapper returning a `RefedEntrypointHandler` |
174
+ | `appendSocketService(ctx, alias = DEFAULT_ALIAS)` | function | Register the socket service on a context that already has the API server |
175
+ | `createSocketService(alias?)` | function | Create the socket service; `update(apiServer)` registers `@fastify/websocket` and the socket routes |
176
+ | `createSocketMiddleware(web = 'api-server', socket = DEFAULT_ALIAS)` | function | Loading-stage context middleware that attaches the socket service to the API server once |
177
+ | `DEFAULT_ALIAS` | const | `'socket-server'` |
178
+ | `SocketService` | type | `InitializedService` with `update(api: ApiServer)` |
179
+ | `Config`, `Context` | type | `ServerConfig` and `ServerContext & ApiServerAppend` |
180
+
181
+ ## Common pitfalls
182
+
183
+ - Declare the route with `socket()`; without it the declaration is mounted as a plain HTTP `GET`.
184
+ - Bind with the same protocol object on both sides: `bind(p, connection(p, callback))`.
185
+ - Guards and gates are inherited from the protocol tree and enforced before the handler runs; do not
186
+ re-implement them inside the callback.
187
+ - A socket that authenticates on its own must call `attachEntity(context, request)`, or
188
+ `request.entity` stays empty and entity-keyed lookups silently find nothing.
189
+ - The token query parameter is used only to stamp `sender` / `recipient` on frames. It is not
190
+ verified — never treat it as authentication.
191
+ - Release subscriptions and watchers on the System `close` frame. Listeners also receive heartbeat
192
+ frames, so check `type` and `event` before acting.
193
+ - Do not expose Fastify request or socket objects through a shared contract.
194
+
195
+ ## Related packages
196
+
197
+ - [`@owlmeans/socket`](../socket) — `Connection`, `MessageType`, `EventMessage`
198
+ - [`@owlmeans/client-socket`](../client-socket) — the browser and native carrier
199
+ - [`@owlmeans/server-api`](../server-api) — the API server this service attaches to
200
+ - [`@owlmeans/server-entrypoint`](../server-entrypoint) — `bind()`
65
201
  - [`@owlmeans/server-app`](../server-app) — calls `appendSocketService` in `makeContext`
66
- - [`@owlmeans/client-socket`](../client-socket) — client-side counterpart
202
+ - [`@owlmeans/auth-common`](../auth-common) — `attachEntity`, `requireEntityKey`, `DEFAULT_GUARD`
67
203
 
68
204
  <!-- owlmeans:agent-guidance:start -->
69
205
  ## Agent guidance
@@ -73,7 +209,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
73
209
  your project's skill store (`.agents/skills/`):
74
210
 
75
211
  ```sh
76
- npx @owlmeans/agent-skills
212
+ npx @owlmeans/agent-skills@^0.1.18-rc.45
77
213
  ```
78
214
 
79
215
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/server-socket",
4
- "version": "0.1.18-rc.0",
5
- "generatedAt": "2026-08-16T22:20:50.511Z",
4
+ "version": "0.1.18-rc.50",
5
+ "generatedAt": "2026-10-01T23:27:48.745Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,32 +1,29 @@
1
1
  ---
2
2
  name: server-socket
3
- description: How to use @owlmeans/server-socket — Fastify WebSocket integration providing a socket service registered on the server context. Auto-invoked when importing server socket primitives or wiring WebSocket support.
4
- user-invocable: false
3
+ description: Bind OwlMeans socket protocol declarations to Fastify WebSocket handlers.
5
4
  ---
6
5
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
6
 
8
- # @owlmeans/server-socket
7
+ # Socket protocol handlers
9
8
 
10
- **Layer:** Server
11
- **Install:** `"@owlmeans/server-socket": "^0.1.18-rc.0"` in `dependencies`
9
+ **Install:** `bun add @owlmeans/server-socket@^0.1.18-rc.50`
12
10
 
13
- ## Key Exports
11
+ Declare a socket route and its contract in the shared protocol package. Bind it on the server with
12
+ `connection()` and `bind()`.
14
13
 
15
- | Export | Description |
16
- |--------|-------------|
17
- | `makeSocketService()` | Factory for the WebSocket server service |
18
- | Middleware | Socket middleware for Fastify |
19
- | Helpers | Channel subscription / broadcast helpers |
20
- | Constants | Default channel names |
14
+ ```ts
15
+ import { bind } from '@owlmeans/server-entrypoint'
16
+ import { connection } from '@owlmeans/server-socket'
21
17
 
22
- ## Usage
23
-
24
- ```typescript
25
- import { makeSocketService } from '@owlmeans/server-socket'
26
- context.registerService(makeSocketService())
18
+ export const serverBindings = [
19
+ bind(streamProtocols.watch, connection(streamProtocols.watch,
20
+ async (connection, context, request) => {
21
+ await connection.send({ type: 'ready' })
22
+ }
23
+ )),
24
+ ]
27
25
  ```
28
26
 
29
- ## Depends On
30
-
31
- - `@owlmeans/socket`, `@owlmeans/server-context`, `@owlmeans/server-api`
32
- - `@fastify/websocket` (runtime)
27
+ The handler receives a typed connection, context, request and response boundary. Guards and gates
28
+ are inherited from the protocol tree and enforced before the WebSocket handler runs. Do not expose
29
+ Fastify request objects through a shared contract.
package/build/helper.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import type { AbstractRequest, AbstractResponse } from '@owlmeans/entrypoint';
1
+ import type { AbstractRequest, AbstractResponse, EntrypointProtocolDeclaration, HandlerRequest, RequestOf, ResponseOf } from '@owlmeans/entrypoint';
2
2
  import type { Context } from '@owlmeans/server-api';
3
- import type { RefedEntrypointHandler } from '@owlmeans/server-entrypoint';
3
+ import type { BoundEntrypointHandler, RefedEntrypointHandler } from '@owlmeans/server-entrypoint';
4
4
  import type { Connection } from '@owlmeans/socket';
5
5
  import type { WebSocket } from '@fastify/websocket';
6
+ /** Bind a socket protocol to its connection lifecycle without an alias lookup. */
7
+ export declare const connection: <Protocol extends EntrypointProtocolDeclaration, SocketContext extends Context = Context, SocketConnection extends Connection = Connection>(protocol: Protocol, handler: (connection: SocketConnection, context: SocketContext, request: HandlerRequest<RequestOf<Protocol>>, response: AbstractResponse<ResponseOf<Protocol>>) => Promise<void>) => BoundEntrypointHandler<Protocol>;
6
8
  export declare const handleConnection: <T extends Connection = Connection>(handler: (conn: T, ctx: Context, req: AbstractRequest<WebSocket>, res: AbstractResponse<any>) => Promise<void>) => RefedEntrypointHandler<AbstractResponse<any>>;
7
9
  //# sourceMappingURL=helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGnD,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAC/D,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,gBAAgB,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,KAC3G,sBAAsB,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAUhD,CAAA"}
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EACxG,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACjG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGnD,kFAAkF;AAClF,eAAO,MAAM,UAAU,GACrB,QAAQ,SAAS,6BAA6B,EAC9C,aAAa,SAAS,OAAO,GAAG,OAAO,EACvC,gBAAgB,SAAS,UAAU,GAAG,UAAU,YAEtC,QAAQ,WACT,CACP,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAC5C,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,KAC7C,OAAO,CAAC,IAAI,CAAC,KACjB,sBAAsB,CAAC,QAAQ,CAkBhC,CAAA;AAEF,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAC/D,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,gBAAgB,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,KAC3G,sBAAsB,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAUhD,CAAA"}
package/build/helper.js CHANGED
@@ -1,5 +1,20 @@
1
1
  import { assertContext } from '@owlmeans/context';
2
2
  import { makeConnection } from './utils/connection.js';
3
+ /** Bind a socket protocol to its connection lifecycle without an alias lookup. */
4
+ export const connection = (protocol, handler) => ({
5
+ protocol,
6
+ bind: ref => async (request, response) => {
7
+ const context = assertContext(ref.ref?.ctx, protocol.alias);
8
+ try {
9
+ await handler(makeConnection(request, context), context, request, response);
10
+ response.resolve(undefined);
11
+ }
12
+ catch (error) {
13
+ response.reject(error);
14
+ }
15
+ return response.value;
16
+ },
17
+ });
3
18
  export const handleConnection = handler => ref => async (req, res) => {
4
19
  const ctx = assertContext(ref.ref?.ctx);
5
20
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAMjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,MAAM,CAAC,MAAM,gBAAgB,GAEwB,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACxF,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAY,CAAA;IAClD,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;IAC/D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,GAAG,CAAC,MAAM,CAAC,CAAU,CAAC,CAAA;IACxB,CAAC;IAED,yDAAyD;IACzD,OAAO,GAAG,CAAC,KAAK,CAAA;AAClB,CAAC,CAAA"}
1
+ {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAQjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,kFAAkF;AAClF,MAAM,CAAC,MAAM,UAAU,GAAG,CAKxB,QAAkB,EAClB,OAKkB,EACgB,EAAE,CAAC,CAAC;IACtC,QAAQ;IACR,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;QACvC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAkB,CAAA;QAC5E,IAAI,CAAC;YACH,MAAM,OAAO,CACX,cAAc,CAAC,OAAqC,EAAE,OAAO,CAAqB,EAClF,OAAO,EACP,OAAyD,EACzD,QAAkD,CACnD,CAAA;YACD,QAAQ,CAAC,OAAO,CAAC,SAAiC,CAAC,CAAA;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,MAAM,CAAC,KAAc,CAAC,CAAA;QACjC,CAAC;QAED,OAAO,QAAQ,CAAC,KAAK,CAAA;IACvB,CAAC;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAEwB,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACxF,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAY,CAAA;IAClD,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;IAC/D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,GAAG,CAAC,MAAM,CAAC,CAAU,CAAC,CAAA;IACxB,CAAC;IAED,yDAAyD;IACzD,OAAO,GAAG,CAAC,KAAK,CAAA;AAClB,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAW,MAAM,sBAAsB,CAAA;AAWpE,eAAO,MAAM,mBAAmB,WAAW,MAAM,KAAmB,aA4GnE,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,OACnE,CAAC,UAAS,MAAM,KACpB,CAOF,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAW,MAAM,sBAAsB,CAAA;AAapE,eAAO,MAAM,mBAAmB,WAAW,MAAM,KAAmB,aAyGnE,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,OACnE,CAAC,UAAS,MAAM,KACpB,CAOF,CAAA"}
package/build/service.js CHANGED
@@ -2,7 +2,7 @@ import { assertContext, createService } from '@owlmeans/context';
2
2
  import { DEFAULT_ALIAS } from './consts.js';
3
3
  import { canServerModule } from './utils/server.js';
4
4
  import { fastifyWebsocket } from '@fastify/websocket';
5
- import { authorize, executeResponse, extractContext, handleError, populateContext, provideRequest } from '@owlmeans/server-api/utils';
5
+ import { authorize, errorExposure, executeResponse, extractContext, handleError, populateContext, provideRequest } from '@owlmeans/server-api/utils';
6
6
  import { EntrypointOutcome, provideResponse } from '@owlmeans/entrypoint';
7
7
  import { ResilientError } from '@owlmeans/error';
8
8
  export const createSocketService = (alias = DEFAULT_ALIAS) => {
@@ -20,8 +20,7 @@ export const createSocketService = (alias = DEFAULT_ALIAS) => {
20
20
  .filter(module => canServerModule(context, module) && !module.route.isIntermediate())
21
21
  .reduce(async (ctx, module) => {
22
22
  let context = await ctx;
23
- await module.resolve();
24
- if (!module.route.match(req)) {
23
+ if (!module.route.match(req, module.mount())) {
25
24
  return context;
26
25
  }
27
26
  if (reply.sent) {
@@ -43,24 +42,22 @@ export const createSocketService = (alias = DEFAULT_ALIAS) => {
43
42
  }
44
43
  }
45
44
  catch (error) {
46
- console.error(error);
47
45
  if (module.fixer != null) {
48
46
  const fixer = context.service(module.fixer);
49
47
  fixer.handle(reply, ResilientError.ensure(error));
50
48
  }
51
49
  else {
52
- handleError(error, reply);
50
+ handleError(error, reply, errorExposure(context.cfg));
53
51
  }
54
52
  }
55
53
  return context;
56
54
  }, Promise.resolve(context));
57
55
  });
58
- await Promise.all(ctx.entrypoints().filter(module => canServerModule(ctx, module) && !module.route.isIntermediate()).map(async (module) => {
59
- await module.resolve();
56
+ ctx.entrypoints().filter(module => canServerModule(ctx, module) && !module.route.isIntermediate()).forEach(module => {
60
57
  if (module.handle == null) {
61
58
  return;
62
59
  }
63
- server.get(module.getPath(), {
60
+ server.get(module.mount(), {
64
61
  schema: {
65
62
  querystring: module.filter?.query ?? {},
66
63
  params: module.filter?.params ?? {},
@@ -92,7 +89,7 @@ export const createSocketService = (alias = DEFAULT_ALIAS) => {
92
89
  }
93
90
  });
94
91
  });
95
- }));
92
+ });
96
93
  });
97
94
  }
98
95
  }, service => async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAI3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAOtD,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,WACvE,eAAe,CAAC,SAAS,CAAC,WAAW,CAAC,KAC9C,UA0IF,CAAA"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAI3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAOtD,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,WACvE,eAAe,CAAC,SAAS,CAAC,WAAW,CAAC,KAC9C,UAoKF,CAAA"}
@@ -6,9 +6,15 @@ export const makeConnection = (request, context) => {
6
6
  if (request.body == null) {
7
7
  throw new SocketInitializationError('request');
8
8
  }
9
- let auth = undefined;
9
+ let auth = request.auth;
10
10
  const conn = request.body;
11
11
  const model = createBasicConnection();
12
+ model.requiresAuthentication = true;
13
+ if (request.auth != null) {
14
+ // The HTTP guard already authenticated this upgrade. In-band authentication remains required
15
+ // for unguarded socket routes before any call, request, event, or message can be dispatched.
16
+ model.stage = AuthenticationStage.Authenticated;
17
+ }
12
18
  model.close = async () => {
13
19
  await conn.close();
14
20
  // @TODO check if on close event is triggered
@@ -79,26 +85,33 @@ export const makeConnection = (request, context) => {
79
85
  }
80
86
  return message;
81
87
  };
82
- const messageHandler = async (_message) => {
88
+ const receiveMessage = async (_message) => {
83
89
  _message = Array.isArray(_message) ? _message : [_message];
84
90
  const message = _message.map(msg => msg.toString('utf8')).join('');
85
91
  if (message.startsWith('{') || message.startsWith('[')) {
86
92
  try {
87
93
  const parsed = JSON.parse(message);
88
94
  if (parsed.type === 'ping') {
89
- console.log("Received soft ping, sending pong.");
90
95
  conn.pong();
91
96
  conn.send(JSON.stringify({ type: 'pong' }));
97
+ return;
92
98
  }
93
99
  }
94
- catch (e) {
95
- console.error(e);
100
+ catch {
101
+ // The shared parser below returns the typed malformed-frame error.
96
102
  }
97
103
  }
98
104
  await model.receive(message);
99
105
  };
106
+ // EventEmitter does not consume a returned promise. Keep the registered callback synchronous
107
+ // and terminate only this socket when parsing, staging, or dispatch rejects.
108
+ const messageHandler = (_message) => {
109
+ void receiveMessage(_message).catch(error => {
110
+ console.error('WebSocket message rejected:', error);
111
+ conn.close(1008);
112
+ });
113
+ };
100
114
  conn.on("ping", () => {
101
- console.log("Received hard ping, sending pong.");
102
115
  conn.pong();
103
116
  });
104
117
  conn.on('open', (ws) => {
@@ -111,7 +124,7 @@ export const makeConnection = (request, context) => {
111
124
  clearInterval(heartbeat);
112
125
  });
113
126
  });
114
- const closeHandler = async (code) => {
127
+ const handleClose = async (code) => {
115
128
  const msg = {
116
129
  type: MessageType.System,
117
130
  event: 'close',
@@ -120,10 +133,22 @@ export const makeConnection = (request, context) => {
120
133
  if (model.prepare != null) {
121
134
  model.prepare(msg);
122
135
  }
123
- await Promise.all(model.getListeners().map(async (listener) => listener(msg)));
136
+ await Promise.all(model.getListeners().map(async (listener) => {
137
+ try {
138
+ await listener(msg);
139
+ }
140
+ catch (error) {
141
+ console.error('Socket close listener error:', error);
142
+ }
143
+ }));
124
144
  conn.off('message', messageHandler);
125
145
  conn.off('close', closeHandler);
126
146
  };
147
+ const closeHandler = (code) => {
148
+ void handleClose(code).catch(error => {
149
+ console.error('WebSocket close handling failed:', error);
150
+ });
151
+ };
127
152
  conn.on('message', messageHandler);
128
153
  conn.on('close', closeHandler);
129
154
  return model;
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-socket",
3
- "version": "0.1.18-rc.5",
3
+ "version": "0.1.18-rc.50",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -31,15 +31,15 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@fastify/websocket": "^11.0.1",
34
- "@owlmeans/auth": "^0.1.18-rc.5",
35
- "@owlmeans/basic-envelope": "^0.1.18-rc.5",
36
- "@owlmeans/context": "^0.1.18-rc.5",
37
- "@owlmeans/entrypoint": "^0.1.18-rc.5",
38
- "@owlmeans/server-api": "^0.1.18-rc.5",
39
- "@owlmeans/server-auth": "^0.1.18-rc.5",
40
- "@owlmeans/server-context": "^0.1.18-rc.5",
41
- "@owlmeans/server-entrypoint": "^0.1.18-rc.5",
42
- "@owlmeans/socket": "^0.1.18-rc.5"
34
+ "@owlmeans/auth": "^0.1.18-rc.37",
35
+ "@owlmeans/basic-envelope": "^0.1.18-rc.42",
36
+ "@owlmeans/context": "^0.1.18-rc.32",
37
+ "@owlmeans/entrypoint": "^0.1.18-rc.38",
38
+ "@owlmeans/server-api": "^0.1.18-rc.47",
39
+ "@owlmeans/server-auth": "^0.1.18-rc.50",
40
+ "@owlmeans/server-context": "^0.1.18-rc.39",
41
+ "@owlmeans/server-entrypoint": "^0.1.18-rc.37",
42
+ "@owlmeans/socket": "^0.1.18-rc.36"
43
43
  },
44
44
  "publishConfig": {
45
45
  "access": "public"
package/src/helper.ts CHANGED
@@ -1,11 +1,46 @@
1
1
  import { assertContext } from '@owlmeans/context'
2
- import type { AbstractRequest, AbstractResponse } from '@owlmeans/entrypoint'
2
+ import type {
3
+ AbstractRequest, AbstractResponse, EntrypointProtocolDeclaration, HandlerRequest, RequestOf, ResponseOf,
4
+ } from '@owlmeans/entrypoint'
3
5
  import type { Context } from '@owlmeans/server-api'
4
- import type { RefedEntrypointHandler } from '@owlmeans/server-entrypoint'
6
+ import type { BoundEntrypointHandler, RefedEntrypointHandler } from '@owlmeans/server-entrypoint'
5
7
  import type { Connection } from '@owlmeans/socket'
6
8
  import type { WebSocket } from '@fastify/websocket'
7
9
  import { makeConnection } from './utils/connection.js'
8
10
 
11
+ /** Bind a socket protocol to its connection lifecycle without an alias lookup. */
12
+ export const connection = <
13
+ Protocol extends EntrypointProtocolDeclaration,
14
+ SocketContext extends Context = Context,
15
+ SocketConnection extends Connection = Connection,
16
+ >(
17
+ protocol: Protocol,
18
+ handler: (
19
+ connection: SocketConnection,
20
+ context: SocketContext,
21
+ request: HandlerRequest<RequestOf<Protocol>>,
22
+ response: AbstractResponse<ResponseOf<Protocol>>,
23
+ ) => Promise<void>,
24
+ ): BoundEntrypointHandler<Protocol> => ({
25
+ protocol,
26
+ bind: ref => async (request, response) => {
27
+ const context = assertContext(ref.ref?.ctx, protocol.alias) as SocketContext
28
+ try {
29
+ await handler(
30
+ makeConnection(request as AbstractRequest<WebSocket>, context) as SocketConnection,
31
+ context,
32
+ request as unknown as HandlerRequest<RequestOf<Protocol>>,
33
+ response as AbstractResponse<ResponseOf<Protocol>>,
34
+ )
35
+ response.resolve(undefined as ResponseOf<Protocol>)
36
+ } catch (error) {
37
+ response.reject(error as Error)
38
+ }
39
+
40
+ return response.value
41
+ },
42
+ })
43
+
9
44
  export const handleConnection: <T extends Connection = Connection>(
10
45
  handler: (conn: T, ctx: Context, req: AbstractRequest<WebSocket>, res: AbstractResponse<any>) => Promise<void>
11
46
  ) => RefedEntrypointHandler<AbstractResponse<any>> = handler => ref => async (req, res) => {
package/src/service.ts CHANGED
@@ -6,7 +6,9 @@ import type { FixerService, ServerEntrypoint } from '@owlmeans/server-entrypoint
6
6
  import { canServerModule } from './utils/server.js'
7
7
  import { fastifyWebsocket } from '@fastify/websocket'
8
8
  import type { WebSocket } from '@fastify/websocket'
9
- import { authorize, executeResponse, extractContext, handleError, populateContext, provideRequest } from '@owlmeans/server-api/utils'
9
+ import {
10
+ authorize, errorExposure, executeResponse, extractContext, handleError, populateContext, provideRequest
11
+ } from '@owlmeans/server-api/utils'
10
12
  import { EntrypointOutcome, provideResponse } from '@owlmeans/entrypoint'
11
13
  import type { AbstractRequest, GateService } from '@owlmeans/entrypoint'
12
14
  import { ResilientError } from '@owlmeans/error'
@@ -27,9 +29,8 @@ export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketServic
27
29
  .filter(module => canServerModule(context, module) && !module.route.isIntermediate())
28
30
  .reduce<Promise<Context>>(async (ctx, module) => {
29
31
  let context = await ctx
30
- await module.resolve()
31
32
 
32
- if (!module.route.match(req)) {
33
+ if (!module.route.match(req, module.mount())) {
33
34
  return context
34
35
  }
35
36
 
@@ -55,12 +56,11 @@ export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketServic
55
56
  executeResponse(response, reply, true)
56
57
  }
57
58
  } catch (error) {
58
- console.error(error)
59
59
  if (module.fixer != null) {
60
60
  const fixer: FixerService = context.service(module.fixer)
61
61
  fixer.handle(reply, ResilientError.ensure(error as Error))
62
62
  } else {
63
- handleError(error as Error, reply)
63
+ handleError(error as Error, reply, errorExposure(context.cfg))
64
64
  }
65
65
  }
66
66
 
@@ -68,15 +68,14 @@ export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketServic
68
68
  }, Promise.resolve(context))
69
69
  })
70
70
 
71
- await Promise.all(ctx.entrypoints<ServerEntrypoint<Request>>().filter(
71
+ ctx.entrypoints<ServerEntrypoint<Request>>().filter(
72
72
  module => canServerModule(ctx, module) && !module.route.isIntermediate()
73
- ).map(async module => {
74
- await module.resolve()
73
+ ).forEach(module => {
75
74
  if (module.handle == null) {
76
75
  return
77
76
  }
78
77
 
79
- server.get(module.getPath(), {
78
+ server.get(module.mount(), {
80
79
  schema: {
81
80
  querystring: module.filter?.query ?? {},
82
81
  params: module.filter?.params ?? {},
@@ -111,7 +110,7 @@ export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketServic
111
110
  }
112
111
  })
113
112
  })
114
- }))
113
+ })
115
114
  })
116
115
  }
117
116
  }, service => async () => {
@@ -17,10 +17,16 @@ export const makeConnection = <C extends Config, T extends Context<C> = Context<
17
17
  if (request.body == null) {
18
18
  throw new SocketInitializationError('request')
19
19
  }
20
- let auth: Auth | AuthCredentials | null | undefined = undefined
20
+ let auth: Auth | AuthCredentials | null | undefined = request.auth
21
21
  const conn = request.body
22
22
 
23
23
  const model = createBasicConnection()
24
+ model.requiresAuthentication = true
25
+ if (request.auth != null) {
26
+ // The HTTP guard already authenticated this upgrade. In-band authentication remains required
27
+ // for unguarded socket routes before any call, request, event, or message can be dispatched.
28
+ model.stage = AuthenticationStage.Authenticated
29
+ }
24
30
 
25
31
  model.close = async () => {
26
32
  await conn.close()
@@ -97,7 +103,7 @@ export const makeConnection = <C extends Config, T extends Context<C> = Context<
97
103
  return message
98
104
  }
99
105
 
100
- const messageHandler = async (_message: Buffer | Buffer[]) => {
106
+ const receiveMessage = async (_message: Buffer | Buffer[]) => {
101
107
  _message = Array.isArray(_message) ? _message : [_message]
102
108
  const message = _message.map(msg => msg.toString('utf8')).join('')
103
109
 
@@ -105,20 +111,28 @@ export const makeConnection = <C extends Config, T extends Context<C> = Context<
105
111
  try {
106
112
  const parsed = JSON.parse(message)
107
113
  if (parsed.type === 'ping') {
108
- console.log("Received soft ping, sending pong.")
109
114
  conn.pong()
110
115
  conn.send(JSON.stringify({ type: 'pong' }))
116
+ return
111
117
  }
112
- } catch (e) {
113
- console.error(e)
118
+ } catch {
119
+ // The shared parser below returns the typed malformed-frame error.
114
120
  }
115
121
  }
116
122
 
117
123
  await model.receive(message)
118
124
  }
119
125
 
126
+ // EventEmitter does not consume a returned promise. Keep the registered callback synchronous
127
+ // and terminate only this socket when parsing, staging, or dispatch rejects.
128
+ const messageHandler = (_message: Buffer | Buffer[]) => {
129
+ void receiveMessage(_message).catch(error => {
130
+ console.error('WebSocket message rejected:', error)
131
+ conn.close(1008)
132
+ })
133
+ }
134
+
120
135
  conn.on("ping", () => {
121
- console.log("Received hard ping, sending pong.")
122
136
  conn.pong()
123
137
  })
124
138
 
@@ -133,7 +147,7 @@ export const makeConnection = <C extends Config, T extends Context<C> = Context<
133
147
  })
134
148
  })
135
149
 
136
- const closeHandler = async (code: number) => {
150
+ const handleClose = async (code: number) => {
137
151
  const msg: EventMessage<{ code: number }> = {
138
152
  type: MessageType.System,
139
153
  event: 'close',
@@ -142,11 +156,23 @@ export const makeConnection = <C extends Config, T extends Context<C> = Context<
142
156
  if (model.prepare != null) {
143
157
  model.prepare(msg)
144
158
  }
145
- await Promise.all(model.getListeners().map(async listener => listener(msg)))
159
+ await Promise.all(model.getListeners().map(async listener => {
160
+ try {
161
+ await listener(msg)
162
+ } catch (error) {
163
+ console.error('Socket close listener error:', error)
164
+ }
165
+ }))
146
166
  conn.off('message', messageHandler)
147
167
  conn.off('close', closeHandler)
148
168
  }
149
169
 
170
+ const closeHandler = (code: number) => {
171
+ void handleClose(code).catch(error => {
172
+ console.error('WebSocket close handling failed:', error)
173
+ })
174
+ }
175
+
150
176
  conn.on('message', messageHandler)
151
177
  conn.on('close', closeHandler)
152
178
 
package/build/.gitkeep DELETED
File without changes