@polytric/openws-spec 0.0.1 → 0.0.2
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 +399 -71
- package/dist/builder.cjs +380 -0
- package/dist/builder.cjs.map +1 -0
- package/dist/builder.d.cts +77 -0
- package/dist/builder.d.ts +77 -0
- package/dist/builder.js +347 -0
- package/dist/builder.js.map +1 -0
- package/dist/chunk-7D4SUZUM.js +38 -0
- package/dist/chunk-7D4SUZUM.js.map +1 -0
- package/dist/index.cjs +1110 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +139 -0
- package/dist/index.d.ts +139 -0
- package/dist/index.js +1074 -0
- package/dist/index.js.map +1 -0
- package/dist/types.cjs +19 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +42 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -14
- package/index.js +0 -11
- package/spec.js +0 -62
- package/spec.json +0 -1
package/README.md
CHANGED
|
@@ -1,125 +1,453 @@
|
|
|
1
1
|
# OpenWS Specification <!-- omit in toc -->
|
|
2
2
|
|
|
3
|
-
OpenWS is a specification for describing
|
|
3
|
+
OpenWS is a specification for describing **WebSocket-based systems** in the same way OpenAPI describes HTTP APIs. It models WebSocket communication as an **asymmetrical 2-way request-response mesh**: multiple components exchange named messages, and "responses" are simply messages sent back through the same network.
|
|
4
4
|
|
|
5
5
|
An OpenWS document describes:
|
|
6
6
|
|
|
7
|
-
- Networks
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- Payload
|
|
11
|
-
-
|
|
7
|
+
- **Networks**: logical WebSocket systems where `message`s are exchanged
|
|
8
|
+
- **Roles**: `role`-based API surfaces with statically defined `message` contracts
|
|
9
|
+
- **Messages**: named `message` definitions that contain a `payload`
|
|
10
|
+
- **Payload**: the shape of application data transmitted through WebSocket messages, defined with JSON Schema
|
|
11
|
+
- **Metadata & connection hints**: document metadata (title, version, description) and optional connection hints (endpoints)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Conceptually (at runtime), we also use the following terms throughout this document:
|
|
14
14
|
|
|
15
|
+
- **Participants**: runtime instances/connections that assume a `role` on a `network`
|
|
16
|
+
- **Handlers**: runtime message-processing functions invoked when a participant receives a `message`
|
|
17
|
+
|
|
18
|
+
Participants and handlers are **not serialized** in the OpenWS JSON document. They are explanatory terms used to describe how role/message contracts are typically implemented.
|
|
19
|
+
|
|
20
|
+
This spec intentionally does **not** describe behavior or execution.
|
|
21
|
+
|
|
22
|
+
- [Conventions](#conventions)
|
|
23
|
+
- [Document Structure](#document-structure)
|
|
24
|
+
- [Ecosystem](#ecosystem)
|
|
15
25
|
- [Core Concepts](#core-concepts)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
26
|
+
- [Networks](#networks)
|
|
27
|
+
- [Roles](#roles)
|
|
28
|
+
- [Messages](#messages)
|
|
29
|
+
- [Request/response modeling](#requestresponse-modeling)
|
|
30
|
+
- [Payload](#payload)
|
|
31
|
+
- [Metadata \& Connection Hints](#metadata--connection-hints)
|
|
32
|
+
- [Document metadata](#document-metadata)
|
|
33
|
+
- [Connection hints](#connection-hints)
|
|
34
|
+
- [Custom metadata and extensions](#custom-metadata-and-extensions)
|
|
35
|
+
- [Complete Example](#complete-example)
|
|
36
|
+
|
|
37
|
+
# Conventions
|
|
38
|
+
|
|
39
|
+
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this document are to be interpreted as described in RFC 2119.
|
|
40
|
+
|
|
41
|
+
Unless otherwise noted:
|
|
42
|
+
|
|
43
|
+
- OpenWS documents are JSON objects.
|
|
44
|
+
- Examples are written as valid JSON (no comments, no trailing commas).
|
|
45
|
+
- `payload` schemas use **JSON Schema**. A specific JSON Schema dialect may be chosen by downstream tooling; OpenWS itself describes shapes rather than enforcing a single validator implementation.
|
|
46
|
+
|
|
47
|
+
> Note: The OpenWS document describes _contracts_. Runtime details such as routing, correlation, authentication, and connection lifecycle are intentionally left to runtime layers.
|
|
48
|
+
|
|
49
|
+
# Document Structure
|
|
50
|
+
|
|
51
|
+
An OpenWS document is a JSON object with the following top-level fields:
|
|
52
|
+
|
|
53
|
+
- `openws` (**REQUIRED**): the OpenWS specification version as a string.
|
|
54
|
+
- `title` (**OPTIONAL**): a human-readable name for the document.
|
|
55
|
+
- `version` (**OPTIONAL**): the version of the described system/API (not the OpenWS spec version).
|
|
56
|
+
- `description` (**OPTIONAL**): a longer explanation of the document.
|
|
57
|
+
- `networks` (**REQUIRED**): a map of network names to network definitions.
|
|
58
|
+
|
|
59
|
+
An implementation:
|
|
60
|
+
|
|
61
|
+
- **MUST** ignore unknown fields (to allow forward compatibility and extensions).
|
|
62
|
+
- **SHOULD** preserve unknown fields when round-tripping documents (when feasible).
|
|
63
|
+
- **MAY** support custom extension fields anywhere in the document (see [Metadata & Connection Hints](#metadata--connection-hints)).
|
|
64
|
+
|
|
65
|
+
Minimal skeleton:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"openws": "0.0.2",
|
|
70
|
+
"title": "My WebSocket System",
|
|
71
|
+
"version": "1.0.0",
|
|
72
|
+
"description": "Optional, human-readable description.",
|
|
73
|
+
"networks": {
|
|
74
|
+
"example": {
|
|
75
|
+
"roles": {
|
|
76
|
+
"server": {
|
|
77
|
+
"messages": {}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
# Ecosystem
|
|
86
|
+
|
|
87
|
+
OpenWS is designed as a **tooling ecosystem**, similar in spirit to OpenAPI: a spec, plus runtimes, adapters, generators, and framework integrations.
|
|
88
|
+
|
|
89
|
+
This repository/spec is intended to support a full "fleet" of packages, including (but not limited to):
|
|
90
|
+
|
|
91
|
+
- **Spec & validation**
|
|
92
|
+
- [`@polytric/openws-spec`](https://www.npmjs.com/package/@polytric/openws-spec) - This library
|
|
93
|
+
- [`@polytric/fastify-openws-spec`](https://www.npmjs.com/package/@polytric/fastify-openws-spec) -- Fastify plugin to generate OpenWS spec from code
|
|
94
|
+
|
|
95
|
+
- **Runtime framework**
|
|
96
|
+
- [`@polytric/ws`](https://www.npmjs.com/package/@polytric/ws) -- Polytric WS framework for JavaScript.
|
|
97
|
+
- [`@polytric/fastify-openws`](https://www.npmjs.com/package/@polytric/fastify-openws) -- Fastify OpenWS SDK for JavaScript
|
|
24
98
|
|
|
99
|
+
- **SDK generation**
|
|
100
|
+
- [`@polytric/openws-sdkgen`](https://www.npmjs.com/package/@polytric/openws-sdkgen) -- OpenWS code generation tool with multi-language support
|
|
101
|
+
|
|
102
|
+
> The spec intentionally stays framework-agnostic; framework integrations and SDKs belong to the ecosystem layer.
|
|
25
103
|
|
|
26
104
|
# Core Concepts
|
|
27
105
|
|
|
28
|
-
##
|
|
106
|
+
## Networks
|
|
29
107
|
|
|
30
|
-
A network is a logical WebSocket system,
|
|
108
|
+
A network is a logical WebSocket system, with multiple participants on the network forming a mesh. It is common to run multiple isolated features over WebSocket on a single host. For example, a chess game client may have a chat room, a tournament entry, and the actual chess game. They are logically separated like this in the spec:
|
|
31
109
|
|
|
32
110
|
```json
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
111
|
+
{
|
|
112
|
+
"networks": {
|
|
113
|
+
"chat": {},
|
|
114
|
+
"tournament": {},
|
|
115
|
+
"chess": {}
|
|
116
|
+
}
|
|
37
117
|
}
|
|
38
118
|
```
|
|
39
119
|
|
|
40
|
-
|
|
120
|
+
> [!NOTE]
|
|
121
|
+
> Examples in the core concepts sections have omitted details for brevity; they are not valid standalone.
|
|
122
|
+
> Consult the complete example towards the bottom of the doc for a fully working example.
|
|
123
|
+
|
|
124
|
+
On the backend side, there may be a dedicated service for each of these components, so each service would typically define just one network:
|
|
41
125
|
|
|
42
|
-
|
|
126
|
+
Chat service:
|
|
43
127
|
|
|
44
128
|
```json
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"admin": { ... }
|
|
129
|
+
{
|
|
130
|
+
"networks": {
|
|
131
|
+
"chat": {}
|
|
132
|
+
}
|
|
50
133
|
}
|
|
51
134
|
```
|
|
52
135
|
|
|
53
|
-
|
|
54
|
-
- Multiple instances of a participant may exist at runtime
|
|
55
|
-
- Participants are purely declarative
|
|
136
|
+
Tournament service:
|
|
56
137
|
|
|
57
|
-
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"networks": {
|
|
141
|
+
"tournament": {}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
58
145
|
|
|
59
|
-
|
|
146
|
+
Chess service:
|
|
60
147
|
|
|
61
148
|
```json
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
149
|
+
{
|
|
150
|
+
"networks": {
|
|
151
|
+
"chess": {}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
65
154
|
```
|
|
66
155
|
|
|
67
|
-
|
|
156
|
+
A network definition:
|
|
157
|
+
|
|
158
|
+
- **MUST** define `roles`.
|
|
159
|
+
- **MAY** include metadata such as `description`.
|
|
160
|
+
- **MAY** include custom extension fields.
|
|
68
161
|
|
|
69
|
-
|
|
162
|
+
## Roles
|
|
163
|
+
|
|
164
|
+
In the OpenWS document, we define **roles**. At runtime, a **participant** is a connected instance that assumes a `role` on a `network`.
|
|
165
|
+
|
|
166
|
+
Multiple participants of the same role may exist on the same network. For example, in a chat service, a `server` is normally connected to many `client`s. OpenWS defers modeling one-to-one, one-to-many, or many-to-many relationships to the application/runtime.
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"networks": {
|
|
171
|
+
"chat": {
|
|
172
|
+
"roles": {
|
|
173
|
+
"server": {},
|
|
174
|
+
"client": {}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
It is common for different components of a system to expose different APIs and provide different behaviors. This is naturally modeled by participants on the same network having different roles. Continuing the chat example, imagine we now have a mobile client, a customer web portal, and an admin console. To serve both customer and admin needs, the server is split into two separate roles, while a single backend may implement both roles. Distinct roles can be set up like this:
|
|
70
182
|
|
|
71
183
|
```json
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
184
|
+
{
|
|
185
|
+
"roles": {
|
|
186
|
+
"adminServer": {},
|
|
187
|
+
"customerServer": {},
|
|
188
|
+
"mobile": {},
|
|
189
|
+
"portal": {},
|
|
190
|
+
"console": {}
|
|
191
|
+
}
|
|
75
192
|
}
|
|
76
193
|
```
|
|
77
194
|
|
|
78
|
-
|
|
195
|
+
IMPORTANT: this is not a role-based access control system, but the clean API boundary is intended to make layering an auth system trivial.
|
|
196
|
+
|
|
197
|
+
A role definition:
|
|
79
198
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
199
|
+
- **MUST** define `messages` (it MAY be empty).
|
|
200
|
+
- **MAY** include metadata such as `description` or connection hints such as `endpoints`.
|
|
201
|
+
- **MAY** include custom extension fields.
|
|
83
202
|
|
|
84
|
-
##
|
|
203
|
+
## Messages
|
|
85
204
|
|
|
86
|
-
|
|
205
|
+
In the OpenWS document, roles declare the data shapes they accept as messages. Messages are indexed by name.
|
|
206
|
+
|
|
207
|
+
Message definitions are **scoped to the receiving role**. At runtime:
|
|
208
|
+
|
|
209
|
+
- A participant receives a message by name, and dispatches it to a handler.
|
|
210
|
+
- A participant sends a message by targeting some other participant(s) whose role defines that message name and payload shape.
|
|
211
|
+
|
|
212
|
+
OpenWS intentionally describes **contracts**, not delivery semantics. Routing (broadcast, directed, room-based, etc.) is runtime-specific.
|
|
213
|
+
|
|
214
|
+
For example, a chat server may accept `message`, `join`, `createRoom`, while the customer portal may accept `channelStats`. The meaning of messages differs depending on perspective: the chat server accepts `join` as an incoming message and handles it to update its state, while the portal can send `join` to the server. Similarly, the portal accepts `channelStats` as an incoming message, while the server can send `channelStats` to the portal.
|
|
87
215
|
|
|
88
216
|
```json
|
|
89
|
-
|
|
90
|
-
|
|
217
|
+
{
|
|
218
|
+
"roles": {
|
|
219
|
+
"server": {
|
|
220
|
+
"messages": {
|
|
221
|
+
"message": {},
|
|
222
|
+
"join": {},
|
|
223
|
+
"createRoom": {}
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
"portal": {
|
|
227
|
+
"messages": {
|
|
228
|
+
"channelStats": {}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
91
232
|
}
|
|
92
233
|
```
|
|
93
234
|
|
|
94
|
-
|
|
235
|
+
A message definition:
|
|
95
236
|
|
|
96
|
-
-
|
|
97
|
-
-
|
|
237
|
+
- **MUST** define `payload`.
|
|
238
|
+
- **MAY** include metadata such as `description`.
|
|
239
|
+
- **MUST** be uniquely named within its role (message names under the same role **MUST NOT** collide).
|
|
240
|
+
- **MAY** include custom extension fields.
|
|
98
241
|
|
|
99
|
-
|
|
242
|
+
### Request/response modeling
|
|
100
243
|
|
|
101
|
-
|
|
244
|
+
OpenWS supports request/response patterns, but does not mandate a single correlation mechanism. If your system uses request/response semantics, runtimes **SHOULD** define a correlation convention (for example, an ID field inside the payload or an envelope-level correlation ID).
|
|
102
245
|
|
|
103
|
-
|
|
104
|
-
- Connection state
|
|
105
|
-
- Lifecycle hooks
|
|
106
|
-
- Transport implementation details
|
|
246
|
+
## Payload
|
|
107
247
|
|
|
108
|
-
|
|
248
|
+
Each message declares its payload shape using JSON Schema:
|
|
109
249
|
|
|
110
|
-
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"message": {
|
|
253
|
+
"payload": {
|
|
254
|
+
"type": "string",
|
|
255
|
+
"minLength": 1,
|
|
256
|
+
"maxLength": 2048
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
111
261
|
|
|
112
|
-
|
|
113
|
-
- SDK / client generation
|
|
114
|
-
- Documentation
|
|
115
|
-
- Cross-language interoperability
|
|
116
|
-
- Static analysis
|
|
262
|
+
Payload schemas:
|
|
117
263
|
|
|
118
|
-
|
|
264
|
+
- **MUST** be valid JSON Schema objects (as interpreted by the runtime/tooling).
|
|
265
|
+
- **SHOULD** be compatible with your chosen JSON Schema dialect across toolchains.
|
|
266
|
+
- **MAY** use common JSON Schema keywords such as `type`, `properties`, `required`, `enum`, and `format`.
|
|
119
267
|
|
|
120
|
-
|
|
268
|
+
A role's handlers are the runtime functions that process incoming messages whose payloads match the message definitions in the spec.
|
|
121
269
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
-
|
|
125
|
-
|
|
270
|
+
## Metadata & Connection Hints
|
|
271
|
+
|
|
272
|
+
OpenWS includes a small set of **well-known metadata fields** for product-grade tooling, while still allowing custom metadata anywhere in the document.
|
|
273
|
+
|
|
274
|
+
### Document metadata
|
|
275
|
+
|
|
276
|
+
OpenWS supports a small set of well-known metadata fields at the root of the document:
|
|
277
|
+
|
|
278
|
+
- `title` (**SHOULD**): a human-readable name for the document.
|
|
279
|
+
- `version` (**SHOULD**): the version of the described system/API (not the OpenWS spec version).
|
|
280
|
+
- `description` (**MAY**): a longer explanation of the document.
|
|
281
|
+
|
|
282
|
+
Tooling **MAY** use these fields for documentation, generation, and display.
|
|
283
|
+
|
|
284
|
+
### Connection hints
|
|
285
|
+
|
|
286
|
+
Endpoints are **optional connection hints** that help participants determine how to establish connections. They do not prescribe deployment topology, discovery, authentication, or load-balancing.
|
|
287
|
+
|
|
288
|
+
An endpoint is typically attached to a role definition:
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"networks": {
|
|
293
|
+
"chat": {
|
|
294
|
+
"roles": {
|
|
295
|
+
"server": {
|
|
296
|
+
"endpoints": [
|
|
297
|
+
{ "scheme": "wss", "host": "localhost", "port": 8082, "path": "/abc" }
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Endpoint objects:
|
|
307
|
+
|
|
308
|
+
- **MAY** include `scheme` (`ws` or `wss`), `host`, `port`, and `path`.
|
|
309
|
+
- **SHOULD** be treated as hints. Implementations **MAY** connect using other configuration (service discovery, environment variables, user choice, etc.).
|
|
310
|
+
- Dynamic endpoint resolution is outside the scope of this spec.
|
|
311
|
+
|
|
312
|
+
### Custom metadata and extensions
|
|
313
|
+
|
|
314
|
+
Custom metadata fields **MAY** be added anywhere in the JSON structure. Implementations:
|
|
315
|
+
|
|
316
|
+
- **MUST** ignore fields they do not understand.
|
|
317
|
+
- **SHOULD** encourage a consistent prefix such as `x-...` for extension fields to reduce the chance of collisions with future spec fields.
|
|
318
|
+
|
|
319
|
+
## Complete Example
|
|
320
|
+
|
|
321
|
+
The following example is a minimal but complete OpenWS document modeling a chat system. It uses:
|
|
322
|
+
|
|
323
|
+
- a single `chat` network
|
|
324
|
+
- three roles (`server`, `client`, `portal`)
|
|
325
|
+
- message contracts scoped to the receiving role
|
|
326
|
+
- connection hints via `endpoints`
|
|
327
|
+
- one extension field (`x-routingNotes`) to document intended flows
|
|
328
|
+
|
|
329
|
+
```<!-- embed:./test/chat-spec.json:scope:{ -->
|
|
330
|
+
{
|
|
331
|
+
"openws": "0.0.2",
|
|
332
|
+
"title": "Example Chat Service",
|
|
333
|
+
"version": "1.0.0",
|
|
334
|
+
"description": "A minimal OpenWS document modeling a chat network.",
|
|
335
|
+
"networks": {
|
|
336
|
+
"chat": {
|
|
337
|
+
"description": "Realtime chat network.",
|
|
338
|
+
"roles": {
|
|
339
|
+
"server": {
|
|
340
|
+
"description": "Backend that hosts rooms and broadcasts messages.",
|
|
341
|
+
"endpoints": [
|
|
342
|
+
{
|
|
343
|
+
"scheme": "wss",
|
|
344
|
+
"host": "chat.example.com",
|
|
345
|
+
"port": 443,
|
|
346
|
+
"path": "/ws/chat"
|
|
347
|
+
}
|
|
348
|
+
],
|
|
349
|
+
"messages": {
|
|
350
|
+
"join": {
|
|
351
|
+
"description": "Request to join a room.",
|
|
352
|
+
"payload": {
|
|
353
|
+
"type": "object",
|
|
354
|
+
"additionalProperties": false,
|
|
355
|
+
"required": ["userId", "roomId"],
|
|
356
|
+
"properties": {
|
|
357
|
+
"userId": { "type": "string", "minLength": 1 },
|
|
358
|
+
"roomId": { "type": "string", "minLength": 1 }
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
"message": {
|
|
363
|
+
"description": "Send a chat message to a room.",
|
|
364
|
+
"payload": {
|
|
365
|
+
"type": "object",
|
|
366
|
+
"additionalProperties": false,
|
|
367
|
+
"required": ["userId", "roomId", "text"],
|
|
368
|
+
"properties": {
|
|
369
|
+
"userId": { "type": "string", "minLength": 1 },
|
|
370
|
+
"roomId": { "type": "string", "minLength": 1 },
|
|
371
|
+
"text": { "type": "string", "minLength": 1, "maxLength": 2048 }
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
"createRoom": {
|
|
376
|
+
"description": "Create a new room.",
|
|
377
|
+
"payload": {
|
|
378
|
+
"type": "object",
|
|
379
|
+
"additionalProperties": false,
|
|
380
|
+
"required": ["userId", "name"],
|
|
381
|
+
"properties": {
|
|
382
|
+
"userId": { "type": "string", "minLength": 1 },
|
|
383
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 128 }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
"requestStats": {
|
|
388
|
+
"description": "Request channel statistics.",
|
|
389
|
+
"payload": {
|
|
390
|
+
"type": "object",
|
|
391
|
+
"additionalProperties": false,
|
|
392
|
+
"required": ["roomId"],
|
|
393
|
+
"properties": {
|
|
394
|
+
"roomId": { "type": "string", "minLength": 1 }
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
"client": {
|
|
401
|
+
"description": "End-user client that receives events from the server.",
|
|
402
|
+
"messages": {
|
|
403
|
+
"roomJoined": {
|
|
404
|
+
"description": "Emitted after a join succeeds.",
|
|
405
|
+
"payload": {
|
|
406
|
+
"type": "object",
|
|
407
|
+
"additionalProperties": false,
|
|
408
|
+
"required": ["roomId"],
|
|
409
|
+
"properties": {
|
|
410
|
+
"roomId": { "type": "string", "minLength": 1 }
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
"messageReceived": {
|
|
415
|
+
"description": "Broadcast of a message to participants in a room.",
|
|
416
|
+
"payload": {
|
|
417
|
+
"type": "object",
|
|
418
|
+
"additionalProperties": false,
|
|
419
|
+
"required": ["roomId", "text", "senderId", "sentAt"],
|
|
420
|
+
"properties": {
|
|
421
|
+
"roomId": { "type": "string", "minLength": 1 },
|
|
422
|
+
"text": { "type": "string", "minLength": 1, "maxLength": 2048 },
|
|
423
|
+
"senderId": { "type": "string", "minLength": 1 },
|
|
424
|
+
"sentAt": { "type": "integer" }
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
"portal": {
|
|
431
|
+
"description": "Internal web portal that receives aggregated stats.",
|
|
432
|
+
"messages": {
|
|
433
|
+
"channelStats": {
|
|
434
|
+
"description": "Room-level metrics snapshot.",
|
|
435
|
+
"payload": {
|
|
436
|
+
"type": "object",
|
|
437
|
+
"additionalProperties": false,
|
|
438
|
+
"required": ["roomId", "members", "messagesLastMinute"],
|
|
439
|
+
"properties": {
|
|
440
|
+
"roomId": { "type": "string", "minLength": 1 },
|
|
441
|
+
"members": { "type": "integer", "minimum": 0 },
|
|
442
|
+
"messagesLastMinute": { "type": "integer", "minimum": 0 }
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
"x-routingNotes": "Clients send server.join/server.message. Server emits client.roomJoined/client.messageReceived and portal.channelStats."
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
```
|