cabloy 5.1.58 → 5.1.60
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/.claude/skills/cabloy-contract-loop/SKILL.md +16 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +26 -0
- package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +144 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +18 -0
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +267 -0
- package/.claude/skills/cabloy-resource-field-update/evals/evals.json +53 -0
- package/.claude/skills/cabloy-resource-field-update/references/custom-renderer-demo-checklist.md +102 -0
- package/.claude/skills/cabloy-resource-field-update/references/field-update-decision-tree.md +120 -0
- package/.claude/skills/cabloy-resource-field-update/references/follow-up-checklist.md +80 -0
- package/.claude/skills/cabloy-resource-field-update/references/verification-checklist.md +97 -0
- package/.github/workflows/docs-pages.yml +2 -0
- package/.github/workflows/vona-cov-pg.yml +2 -0
- package/.github/workflows/vona-test-crud.yml +4 -2
- package/.github/workflows/vona-test-mysql.yml +2 -0
- package/.github/workflows/vona-test-pg.yml +2 -0
- package/.github/workflows/vona-test-sqlite3.yml +2 -0
- package/.github/workflows/vona-tsc.yml +2 -0
- package/.github/workflows/zova-ui.yml +2 -0
- package/.gitignore +0 -4
- package/CHANGELOG.md +41 -0
- package/CLAUDE.md +2 -0
- package/README.md +15 -0
- package/cabloy-docs/.vitepress/config.mjs +43 -0
- package/cabloy-docs/ai/class-placement-rule.md +2 -0
- package/cabloy-docs/ai/cli-to-skill-map.md +7 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +17 -2
- package/cabloy-docs/backend/bean-scene-authoring.md +350 -0
- package/cabloy-docs/backend/cli.md +26 -1
- package/cabloy-docs/backend/foundation.md +28 -3
- package/cabloy-docs/backend/introduction.md +8 -0
- package/cabloy-docs/backend/service-guide.md +2 -0
- package/cabloy-docs/backend/websocket-call-flow.md +435 -0
- package/cabloy-docs/backend/websocket-guide.md +455 -0
- package/cabloy-docs/backend/websocket-protocol-guide.md +381 -0
- package/cabloy-docs/backend/websocket-usage-guide.md +356 -0
- package/cabloy-docs/frontend/bean-scene-authoring.md +372 -0
- package/cabloy-docs/frontend/behavior-guide.md +449 -0
- package/cabloy-docs/frontend/cli.md +12 -0
- package/cabloy-docs/frontend/introduction.md +5 -0
- package/cabloy-docs/frontend/ioc-and-beans.md +10 -9
- package/cabloy-docs/frontend/router-tabs-admin-web-comparison.md +206 -0
- package/cabloy-docs/frontend/router-tabs-introduction.md +106 -0
- package/cabloy-docs/frontend/router-tabs-mechanism.md +469 -0
- package/cabloy-docs/frontend/router-tabs-overview.md +227 -0
- package/cabloy-docs/frontend/router-tabs-route-meta-cookbook.md +343 -0
- package/cabloy-docs/frontend/ssr-architecture-overview.md +211 -0
- package/cabloy-docs/frontend/ssr-build-deploy-guide.md +308 -0
- package/cabloy-docs/frontend/ssr-review-checklist.md +184 -0
- package/cabloy-docs/frontend/ssr-troubleshooting-guide.md +301 -0
- package/cabloy-docs/fullstack/framework-performance.md +3 -3
- package/cabloy-docs/fullstack/introduction.md +29 -0
- package/cabloy-docs/fullstack/quickstart.md +7 -1
- package/cabloy-docs/fullstack/tutorial-1-first-module.md +111 -0
- package/cabloy-docs/fullstack/tutorial-2-first-crud.md +122 -0
- package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +131 -0
- package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +119 -0
- package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +144 -0
- package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +168 -0
- package/cabloy-docs/fullstack/tutorials-overview.md +179 -0
- package/cabloy-docs/index.md +4 -3
- package/cabloy-docs/reference/bean-scene-boilerplates.md +73 -0
- package/cabloy-docs/reference/cli-reference.md +2 -0
- package/package.json +6 -2
- package/scripts/init.ts +18 -2
- package/scripts/upgrade.ts +6 -0
- package/vona/packages-cli/cabloy-cli/package.json +2 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.module.ts +4 -0
- package/vona/packages-utils/zod-query/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +133 -1088
- package/vona/pnpm-workspace.yaml +0 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/assets/static/img/vona.svg +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/package.original.json +1 -1
- package/zova/packages-cli/cli/package.json +3 -3
- package/zova/packages-cli/cli-set-front/cli/templates/init/icon/boilerplate/icons/default/zova.svg +1 -1
- package/zova/packages-cli/cli-set-front/package.json +3 -3
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.module.ts +4 -0
- package/zova/packages-cli/cli-set-front/src/lib/command/create.bean.ts +5 -1
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-utils/zova-vite/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +2 -2
- package/zova/pnpm-lock.yaml +284 -1313
- package/zova/pnpm-workspace.yaml +0 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/cabloy.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/vona.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/zova.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/src/.metadata/icons/groups/social.svg +3 -3
- package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/formFieldSelect/controller.tsx +9 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +66 -16
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts +60 -18
- package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableActionRow/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableCell/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-zod/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +3 -3
- package/zova/src/suite-vendor/a-zova/package.json +5 -5
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# Web Socket Usage Guide
|
|
2
|
+
|
|
3
|
+
This guide provides practical server-side authoring patterns for Web Socket features in Vona within the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
Read this together with:
|
|
6
|
+
|
|
7
|
+
- [Web Socket Guide](/backend/websocket-guide)
|
|
8
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide)
|
|
9
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow)
|
|
10
|
+
- [Service Guide](/backend/service-guide)
|
|
11
|
+
- [Broadcast Guide](/backend/broadcast-guide)
|
|
12
|
+
|
|
13
|
+
Use the practical split:
|
|
14
|
+
|
|
15
|
+
- [Web Socket Guide](/backend/websocket-guide) for architecture
|
|
16
|
+
- this page for server-side authoring patterns
|
|
17
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide) for the client-visible wire format
|
|
18
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow) for source tracing
|
|
19
|
+
|
|
20
|
+
## Why this usage guide exists
|
|
21
|
+
|
|
22
|
+
The main Web Socket guide explains the architecture.
|
|
23
|
+
|
|
24
|
+
This page answers a more practical question:
|
|
25
|
+
|
|
26
|
+
- how should a normal backend module define a namespace, design events, and call `send(...)` or `broadcast(...)` in day-to-day code?
|
|
27
|
+
|
|
28
|
+
In practice, most application authors do not need to modify the built-in `a-socket` transport. They usually need to:
|
|
29
|
+
|
|
30
|
+
- define a namespace
|
|
31
|
+
- define namespace event types
|
|
32
|
+
- trigger point-to-point delivery or namespace-wide delivery
|
|
33
|
+
|
|
34
|
+
## Mental model before writing code
|
|
35
|
+
|
|
36
|
+
Use this split first:
|
|
37
|
+
|
|
38
|
+
- **socket namespace** is the normal API surface for application-level delivery
|
|
39
|
+
- **socket connection** is for connection lifecycle rules such as enter or exit behavior
|
|
40
|
+
- **socket packet** is for inbound message handling and packet transformation
|
|
41
|
+
|
|
42
|
+
A practical rule is:
|
|
43
|
+
|
|
44
|
+
- start with a namespace bean when your goal is server push or namespace-scoped signaling
|
|
45
|
+
- add a connection onion only when the behavior belongs to connection setup or teardown
|
|
46
|
+
- add a packet onion only when the behavior belongs to inbound packet handling
|
|
47
|
+
|
|
48
|
+
For most feature work, a namespace bean is the first and best fit.
|
|
49
|
+
|
|
50
|
+
## Recipe 1: create a socket namespace bean
|
|
51
|
+
|
|
52
|
+
Use the Vona CLI to create a namespace bean shell.
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm run vona :create:bean socketNamespace chat -- --module=demo-student
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The generated shape follows the `a-socket` boilerplate pattern.
|
|
61
|
+
|
|
62
|
+
Representative structure:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import type { IDecoratorSocketNamespaceOptions } from 'vona-module-a-socket';
|
|
66
|
+
import { BeanSocketNamespaceBase, SocketNamespace } from 'vona-module-a-socket';
|
|
67
|
+
|
|
68
|
+
declare module 'vona-module-a-socket' {
|
|
69
|
+
export interface ISocketNamespaceRecord {
|
|
70
|
+
'/chat': never;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ISocketNamespaceOptionsChatEvents {
|
|
75
|
+
messageCreated: {
|
|
76
|
+
roomId: string;
|
|
77
|
+
messageId: string;
|
|
78
|
+
text: string;
|
|
79
|
+
};
|
|
80
|
+
typing: {
|
|
81
|
+
roomId: string;
|
|
82
|
+
userId: number;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ISocketNamespaceOptionsChat
|
|
87
|
+
extends IDecoratorSocketNamespaceOptions<ISocketNamespaceOptionsChatEvents> {}
|
|
88
|
+
|
|
89
|
+
@SocketNamespace<ISocketNamespaceOptionsChat>({
|
|
90
|
+
namespace: '/chat',
|
|
91
|
+
})
|
|
92
|
+
export class SocketNamespaceChat extends BeanSocketNamespaceBase<ISocketNamespaceOptionsChatEvents> {}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The key ideas are:
|
|
96
|
+
|
|
97
|
+
- extend `ISocketNamespaceRecord` so the namespace becomes part of the typed socket surface
|
|
98
|
+
- define one event interface for the namespace payloads
|
|
99
|
+
- declare the namespace path explicitly
|
|
100
|
+
- inherit `BeanSocketNamespaceBase` instead of reimplementing send and broadcast logic yourself
|
|
101
|
+
|
|
102
|
+
## Recipe 2: choose a stable namespace path
|
|
103
|
+
|
|
104
|
+
Namespace paths participate directly in runtime routing.
|
|
105
|
+
|
|
106
|
+
A practical rule is:
|
|
107
|
+
|
|
108
|
+
- if the connection URL is `/ws/chat`, the namespace should be `/chat`
|
|
109
|
+
- if the connection URL is `/ws/notifications`, the namespace should be `/notifications`
|
|
110
|
+
- the root path `/ws` maps to namespace `/`
|
|
111
|
+
|
|
112
|
+
That means namespace naming should be deliberate and stable.
|
|
113
|
+
|
|
114
|
+
Use a namespace when it represents a durable channel identity, such as:
|
|
115
|
+
|
|
116
|
+
- chat messages
|
|
117
|
+
- notifications
|
|
118
|
+
- presence
|
|
119
|
+
- build reload signals
|
|
120
|
+
|
|
121
|
+
Avoid treating namespace names as temporary one-off action names. Individual actions normally belong in namespace event names, not in the namespace path itself.
|
|
122
|
+
|
|
123
|
+
## Recipe 3: broadcast to everyone in a namespace
|
|
124
|
+
|
|
125
|
+
Once the namespace bean exists, ordinary backend code can trigger namespace-wide delivery through scope.
|
|
126
|
+
|
|
127
|
+
Representative pattern:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { BeanBase } from 'vona';
|
|
131
|
+
import { Bean } from 'vona-module-a-bean';
|
|
132
|
+
|
|
133
|
+
@Bean()
|
|
134
|
+
export class BeanChat extends BeanBase {
|
|
135
|
+
messageCreated(roomId: string, messageId: string, text: string) {
|
|
136
|
+
this.scope.socketNamespace.chat.broadcast('messageCreated', {
|
|
137
|
+
roomId,
|
|
138
|
+
messageId,
|
|
139
|
+
text,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
What this means in practice:
|
|
146
|
+
|
|
147
|
+
- the current worker delivers immediately to local clients in `/chat`
|
|
148
|
+
- `a-socket` also emits the corresponding broadcast bean so other workers can fan out to their own local `/chat` clients
|
|
149
|
+
|
|
150
|
+
Use `broadcast(...)` when every connected client in one namespace should receive the event.
|
|
151
|
+
|
|
152
|
+
## Recipe 4: send to one known client id
|
|
153
|
+
|
|
154
|
+
`BeanSocketNamespaceBase` also exposes:
|
|
155
|
+
|
|
156
|
+
- `send(id, eventName, data, options)`
|
|
157
|
+
|
|
158
|
+
Representative pattern:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
@Bean()
|
|
162
|
+
export class BeanChat extends BeanBase {
|
|
163
|
+
typingToOneClient(targetSocketId: string, roomId: string, userId: number) {
|
|
164
|
+
this.scope.socketNamespace.chat.send(targetSocketId, 'typing', {
|
|
165
|
+
roomId,
|
|
166
|
+
userId,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Use `send(...)` when one specific connected client should receive the event.
|
|
173
|
+
|
|
174
|
+
A practical constraint is:
|
|
175
|
+
|
|
176
|
+
- your application must already know the target socket id
|
|
177
|
+
|
|
178
|
+
The socket transport assigns `ws.id` during connection setup. How your business logic remembers or maps that id is application-specific.
|
|
179
|
+
|
|
180
|
+
That is why `send(...)` is the right delivery primitive, but socket-id ownership is still part of your own feature design.
|
|
181
|
+
|
|
182
|
+
## Recipe 5: decide between `broadcast(...)` and `send(...)`
|
|
183
|
+
|
|
184
|
+
Use this split:
|
|
185
|
+
|
|
186
|
+
- use `broadcast(...)` when every client in the namespace should receive the same signal
|
|
187
|
+
- use `send(...)` when one specific client should receive the signal
|
|
188
|
+
|
|
189
|
+
Practical examples:
|
|
190
|
+
|
|
191
|
+
- new chat message in one shared room channel -> `broadcast(...)`
|
|
192
|
+
- private acknowledgement to one connected client -> `send(...)`
|
|
193
|
+
- HMR reload signal to all clients in `/ssrhmr` -> `broadcast(...)`
|
|
194
|
+
- one-user delivery after a server-side state transition -> `send(...)`
|
|
195
|
+
|
|
196
|
+
If you find yourself inventing many one-off namespaces just to avoid choosing between send and broadcast, that is usually a sign that the namespace boundary is too granular.
|
|
197
|
+
|
|
198
|
+
## Recipe 6: keep ordinary business logic outside the namespace bean
|
|
199
|
+
|
|
200
|
+
A useful default is:
|
|
201
|
+
|
|
202
|
+
- keep the namespace bean small and transport-focused
|
|
203
|
+
- place business orchestration in an ordinary bean or service
|
|
204
|
+
- trigger namespace delivery through `this.scope.socketNamespace...`
|
|
205
|
+
|
|
206
|
+
Representative shape:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { BeanBase } from 'vona';
|
|
210
|
+
import { Service } from 'vona-module-a-bean';
|
|
211
|
+
|
|
212
|
+
@Service()
|
|
213
|
+
export class ServiceNotification extends BeanBase {
|
|
214
|
+
async publishOrderCreated(orderId: number, userId: number) {
|
|
215
|
+
// business logic first
|
|
216
|
+
|
|
217
|
+
this.scope.socketNamespace.notifications.broadcast('orderCreated', {
|
|
218
|
+
orderId,
|
|
219
|
+
userId,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
This keeps the transport surface reusable while leaving business rules in the ordinary service layer.
|
|
226
|
+
|
|
227
|
+
## Recipe 7: add a connection onion only for connection-time behavior
|
|
228
|
+
|
|
229
|
+
If the behavior belongs to connect or disconnect time, create a `socketConnection` bean.
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npm run vona :create:bean socketConnection audit -- --module=demo-student
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Representative generated shape:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import type { Next } from 'vona';
|
|
241
|
+
import type { IDecoratorSocketConnectionOptions, ISocketConnectionExecute } from 'vona-module-a-socket';
|
|
242
|
+
import type { WebSocket } from 'ws';
|
|
243
|
+
import { BeanBase } from 'vona';
|
|
244
|
+
import { SocketConnection } from 'vona-module-a-socket';
|
|
245
|
+
|
|
246
|
+
export interface ISocketConnectionOptionsAudit extends IDecoratorSocketConnectionOptions {}
|
|
247
|
+
|
|
248
|
+
@SocketConnection<ISocketConnectionOptionsAudit>()
|
|
249
|
+
export class SocketConnectionAudit extends BeanBase implements ISocketConnectionExecute {
|
|
250
|
+
async enter(_ws: WebSocket, _options: ISocketConnectionOptionsAudit, next: Next): Promise<void> {
|
|
251
|
+
return next();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async exit(_ws: WebSocket, _options: ISocketConnectionOptionsAudit, next: Next): Promise<void> {
|
|
255
|
+
return next();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Use this when you need behavior such as:
|
|
261
|
+
|
|
262
|
+
- connect-time auditing
|
|
263
|
+
- per-connection setup
|
|
264
|
+
- disconnect cleanup
|
|
265
|
+
- rules that must run before normal packet traffic begins
|
|
266
|
+
|
|
267
|
+
Do not use a connection onion merely to trigger business events that could have been ordinary namespace sends or broadcasts.
|
|
268
|
+
|
|
269
|
+
## Recipe 8: add a packet onion only for inbound packet handling
|
|
270
|
+
|
|
271
|
+
If the behavior belongs to inbound socket messages, create a `socketPacket` bean.
|
|
272
|
+
|
|
273
|
+
Example:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
npm run vona :create:bean socketPacket chat -- --module=demo-student
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Representative generated shape:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import type { Next } from 'vona';
|
|
283
|
+
import type { IDecoratorSocketPacketOptions, ISocketPacketExecute } from 'vona-module-a-socket';
|
|
284
|
+
import type { WebSocket } from 'ws';
|
|
285
|
+
import { BeanBase } from 'vona';
|
|
286
|
+
import { SocketPacket } from 'vona-module-a-socket';
|
|
287
|
+
|
|
288
|
+
export interface ISocketPacketOptionsChat extends IDecoratorSocketPacketOptions {}
|
|
289
|
+
|
|
290
|
+
@SocketPacket<ISocketPacketOptionsChat>()
|
|
291
|
+
export class SocketPacketChat extends BeanBase implements ISocketPacketExecute {
|
|
292
|
+
async execute(_data: any, _ws: WebSocket, _options: ISocketPacketOptionsChat, next: Next): Promise<void> {
|
|
293
|
+
return next();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Use this when you need behavior such as:
|
|
299
|
+
|
|
300
|
+
- parsing a custom packet format
|
|
301
|
+
- recognizing application-specific event names
|
|
302
|
+
- validating or transforming inbound packet data before later handlers run
|
|
303
|
+
|
|
304
|
+
Do not use a packet onion when the real need is simply to push data from the server to connected clients.
|
|
305
|
+
|
|
306
|
+
## Recipe 9: reuse the `a-ssrhmr` pattern as a minimal reference
|
|
307
|
+
|
|
308
|
+
The built-in `a-ssrhmr` module is a compact example of the intended namespace usage pattern.
|
|
309
|
+
|
|
310
|
+
It does two things:
|
|
311
|
+
|
|
312
|
+
1. declares a namespace bean for `/ssrhmr`
|
|
313
|
+
2. calls `this.scope.socketNamespace.ssrHmr.broadcast('reload')` from ordinary backend code
|
|
314
|
+
|
|
315
|
+
That is the most important practical pattern to copy first:
|
|
316
|
+
|
|
317
|
+
- define namespace identity once
|
|
318
|
+
- define event names and payload shapes
|
|
319
|
+
- trigger delivery through scope from normal backend beans
|
|
320
|
+
|
|
321
|
+
## Common authoring mistakes to avoid
|
|
322
|
+
|
|
323
|
+
Avoid these mistakes:
|
|
324
|
+
|
|
325
|
+
- putting business orchestration into the namespace bean instead of a normal service or bean
|
|
326
|
+
- using a connection onion when the behavior is really a server push event
|
|
327
|
+
- using a packet onion when no inbound packet handling is needed
|
|
328
|
+
- inventing unstable namespace paths for one-off actions
|
|
329
|
+
- treating socket ids as globally meaningful business identifiers instead of transport identifiers
|
|
330
|
+
|
|
331
|
+
A useful rule is:
|
|
332
|
+
|
|
333
|
+
- namespace path identifies the channel
|
|
334
|
+
- event name identifies the action or signal
|
|
335
|
+
- payload identifies the data
|
|
336
|
+
- business services decide when delivery should happen
|
|
337
|
+
|
|
338
|
+
## Practical checklist before implementation
|
|
339
|
+
|
|
340
|
+
Before writing a Web Socket feature, ask:
|
|
341
|
+
|
|
342
|
+
1. what stable namespace should this feature use?
|
|
343
|
+
2. what event names and payload types belong in that namespace?
|
|
344
|
+
3. should the delivery go to one client or all clients in the namespace?
|
|
345
|
+
4. does the feature need only namespace delivery, or also custom connection or packet behavior?
|
|
346
|
+
5. where will the application remember any socket ids needed for targeted send?
|
|
347
|
+
|
|
348
|
+
If those answers are clear, the implementation usually stays small and aligned with the existing `a-socket` model.
|
|
349
|
+
|
|
350
|
+
## Related guides
|
|
351
|
+
|
|
352
|
+
If you need the broader context next, read:
|
|
353
|
+
|
|
354
|
+
- [Web Socket Guide](/backend/websocket-guide) for architecture
|
|
355
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide) for the client-visible wire format
|
|
356
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow) for source tracing and debugging
|