cabloy 5.1.59 → 5.1.61
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/hooks/contract-loop-gate.ts +296 -0
- package/.claude/settings.json +16 -0
- package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -0
- package/.claude/skills/cabloy-contract-loop/SKILL.md +103 -14
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +126 -12
- package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +148 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +49 -13
- package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +11 -0
- package/.claude/skills/cabloy-frontend-scaffold/references/follow-up-checklist.md +2 -0
- package/.claude/skills/cabloy-module-removal/SKILL.md +144 -0
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +274 -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/.claude/skills/cabloy-zova-source-reading/SKILL.md +221 -0
- package/.claude/skills/cabloy-zova-source-reading/references/analysis-modes.md +91 -0
- package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +117 -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 +52 -0
- package/CLAUDE.md +12 -0
- package/README.md +15 -0
- package/cabloy-docs/.vitepress/config.mjs +89 -0
- package/cabloy-docs/ai/class-placement-rule.md +2 -0
- package/cabloy-docs/ai/cli-to-skill-map.md +14 -0
- package/cabloy-docs/ai/docs-skills-rules-mapping.md +14 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +27 -9
- package/cabloy-docs/ai/introduction.md +1 -0
- package/cabloy-docs/ai/playbook-backend-module.md +6 -0
- package/cabloy-docs/ai/playbook-module-removal.md +164 -0
- package/cabloy-docs/ai/skills.md +11 -0
- package/cabloy-docs/backend/bean-scene-authoring.md +350 -0
- package/cabloy-docs/backend/cli.md +26 -1
- package/cabloy-docs/backend/dto-guide.md +6 -0
- package/cabloy-docs/backend/entity-guide.md +18 -0
- package/cabloy-docs/backend/foundation.md +28 -3
- package/cabloy-docs/backend/introduction.md +10 -0
- package/cabloy-docs/backend/serialization-guide.md +10 -0
- package/cabloy-docs/backend/service-guide.md +2 -0
- package/cabloy-docs/backend/status-guide.md +271 -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/api-guide.md +2 -0
- package/cabloy-docs/frontend/bean-scene-authoring.md +374 -0
- package/cabloy-docs/frontend/behavior-guide.md +449 -0
- package/cabloy-docs/frontend/cli.md +24 -0
- package/cabloy-docs/frontend/command-scene-authoring.md +495 -0
- package/cabloy-docs/frontend/design-principles.md +6 -0
- package/cabloy-docs/frontend/fetch-interceptor-guide.md +440 -0
- package/cabloy-docs/frontend/form-guide.md +795 -0
- package/cabloy-docs/frontend/foundation.md +29 -0
- package/cabloy-docs/frontend/introduction.md +17 -1
- package/cabloy-docs/frontend/ioc-and-beans.md +16 -9
- package/cabloy-docs/frontend/mock-guide.md +1 -0
- package/cabloy-docs/frontend/model-architecture.md +252 -39
- package/cabloy-docs/frontend/model-resource-best-practices.md +379 -0
- package/cabloy-docs/frontend/model-resource-cookbook.md +505 -0
- package/cabloy-docs/frontend/model-resource-owner-pattern.md +382 -0
- package/cabloy-docs/frontend/model-resource-usage-guide.md +318 -0
- package/cabloy-docs/frontend/model-state-guide.md +366 -13
- package/cabloy-docs/frontend/openapi-sdk-guide.md +5 -2
- package/cabloy-docs/frontend/page-guide.md +6 -0
- package/cabloy-docs/frontend/quickstart.md +4 -0
- package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +266 -0
- 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/server-data.md +2 -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/frontend/zova-form-source-reading-map.md +295 -0
- package/cabloy-docs/frontend/zova-form-under-the-hood.md +556 -0
- package/cabloy-docs/frontend/zova-reactivity-under-the-hood.md +320 -0
- package/cabloy-docs/frontend/zova-source-reading-map.md +327 -0
- package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +308 -0
- package/cabloy-docs/fullstack/contract-loop-playbook.md +350 -0
- package/cabloy-docs/fullstack/framework-performance.md +3 -3
- package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +44 -1
- package/cabloy-docs/fullstack/introduction.md +40 -0
- package/cabloy-docs/fullstack/openapi-to-sdk.md +19 -9
- 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 +144 -0
- package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +146 -0
- package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +170 -0
- package/cabloy-docs/fullstack/tutorials-overview.md +192 -0
- package/cabloy-docs/index.md +4 -3
- package/cabloy-docs/reference/bean-scene-boilerplates.md +75 -0
- package/cabloy-docs/reference/cli-reference.md +2 -0
- package/package.json +7 -2
- package/scripts/initTestData.ts +25 -0
- package/scripts/upgrade.ts +17 -2
- 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-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +226 -1091
- 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-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/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/cli/templates/openapi/config/boilerplate/module/openapi.config.ts +6 -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/bean/cli.openapi.generate.ts +34 -4
- package/zova/packages-cli/cli-set-front/src/lib/command/create.bean.ts +5 -1
- package/zova/packages-utils/zova-vite/package.json +2 -2
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +282 -1311
- 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-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Web Socket Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how Web Socket support works in Vona within the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
Read this together with:
|
|
6
|
+
|
|
7
|
+
- [Web Socket Usage Guide](/backend/websocket-usage-guide)
|
|
8
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide)
|
|
9
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow)
|
|
10
|
+
|
|
11
|
+
Use the practical split:
|
|
12
|
+
|
|
13
|
+
- this page for the architecture overview
|
|
14
|
+
- [Web Socket Usage Guide](/backend/websocket-usage-guide) for server-side authoring patterns
|
|
15
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide) for the client-visible wire format
|
|
16
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow) for source tracing and debugging
|
|
17
|
+
|
|
18
|
+
## Why Web Socket support matters
|
|
19
|
+
|
|
20
|
+
Vona provides a framework-native Web Socket runtime so backend modules can keep long-lived connections, push server-side events, and route request-like actions over a persistent channel.
|
|
21
|
+
|
|
22
|
+
That matters because some backend workflows are not well expressed as one HTTP request followed by one HTTP response. They need:
|
|
23
|
+
|
|
24
|
+
- server-initiated delivery
|
|
25
|
+
- namespace-scoped signaling
|
|
26
|
+
- connection-aware authentication and lifecycle control
|
|
27
|
+
- extension hooks that other modules can reuse without replacing the transport layer
|
|
28
|
+
|
|
29
|
+
## The `a-socket` module role
|
|
30
|
+
|
|
31
|
+
The built-in Web Socket runtime lives in `a-socket`.
|
|
32
|
+
|
|
33
|
+
At the module level, `a-socket` contributes three onion families:
|
|
34
|
+
|
|
35
|
+
- `socketNamespace`
|
|
36
|
+
- `socketConnection`
|
|
37
|
+
- `socketPacket`
|
|
38
|
+
|
|
39
|
+
A practical split is:
|
|
40
|
+
|
|
41
|
+
- use **socket namespace** beans to define namespace-scoped send and broadcast APIs
|
|
42
|
+
- use **socket connection** onions to control connection-time lifecycle behavior
|
|
43
|
+
- use **socket packet** onions to process inbound Web Socket messages
|
|
44
|
+
|
|
45
|
+
The module also enables the monkey lifecycle capability so Web Socket startup and shutdown can attach to the backend runtime automatically.
|
|
46
|
+
|
|
47
|
+
## Runtime entry and lifecycle
|
|
48
|
+
|
|
49
|
+
The runtime entry is straightforward:
|
|
50
|
+
|
|
51
|
+
- `src/monkey.ts` forwards `appReady()` and `appClose()` to `service.socket`
|
|
52
|
+
- `src/service/socket.ts` creates the `WebSocketServer` when the app server is ready
|
|
53
|
+
- on shutdown, the Web Socket server is closed and tracked clients are terminated
|
|
54
|
+
|
|
55
|
+
Representative shape:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
export class Monkey extends BeanSimple implements IMonkeyAppReady, IMonkeyAppClose {
|
|
59
|
+
async appReady() {
|
|
60
|
+
await this.app.scope(__ThisModule__).service.socket.appReady();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async appClose() {
|
|
64
|
+
await this.app.scope(__ThisModule__).service.socket.appClose();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This keeps the Web Socket transport aligned with ordinary backend startup instead of requiring a separate manual bootstrap path.
|
|
70
|
+
|
|
71
|
+
## Connection entry flow
|
|
72
|
+
|
|
73
|
+
The main connection flow is implemented in `src/service/socket.ts`.
|
|
74
|
+
|
|
75
|
+
A practical reading order is:
|
|
76
|
+
|
|
77
|
+
1. create `WebSocketServer` from `app.server`
|
|
78
|
+
2. accept a `connection`
|
|
79
|
+
3. parse request URL and query values
|
|
80
|
+
4. create a request-scoped backend context
|
|
81
|
+
5. assign Web Socket metadata such as `id` and `namespace`
|
|
82
|
+
6. register the client in the socket registry
|
|
83
|
+
7. execute the `socketConnection` onion chain with `enter`
|
|
84
|
+
8. install `close`, `message`, and `error` handlers
|
|
85
|
+
9. send the `sysReady` system event
|
|
86
|
+
|
|
87
|
+
The connection context carries useful protocol values from query parameters, including:
|
|
88
|
+
|
|
89
|
+
- passport code
|
|
90
|
+
- instance name
|
|
91
|
+
- locale
|
|
92
|
+
- timezone
|
|
93
|
+
|
|
94
|
+
Those keys come from the module config.
|
|
95
|
+
|
|
96
|
+
Representative config shape:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
return {
|
|
100
|
+
eventPrefix: '_:',
|
|
101
|
+
globalPrefix: '/ws',
|
|
102
|
+
queryKey: {
|
|
103
|
+
passportCode: $protocolKey('x-vona-passport-code'),
|
|
104
|
+
instanceName: $protocolKey('x-vona-instance-name'),
|
|
105
|
+
locale: $protocolKey('x-vona-locale'),
|
|
106
|
+
tz: $protocolKey('x-vona-tz'),
|
|
107
|
+
},
|
|
108
|
+
timeout: {
|
|
109
|
+
ping: 20000,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Namespace routing
|
|
115
|
+
|
|
116
|
+
`a-socket` derives the logical namespace from the request path.
|
|
117
|
+
|
|
118
|
+
The default global prefix is:
|
|
119
|
+
|
|
120
|
+
- `/ws`
|
|
121
|
+
|
|
122
|
+
A useful mental model is:
|
|
123
|
+
|
|
124
|
+
- `/ws` maps to the root namespace `/`
|
|
125
|
+
- `/ws/ssrhmr` maps to the namespace `/ssrhmr`
|
|
126
|
+
|
|
127
|
+
This happens through `ServiceSocket.getNamespace()`, which strips the global prefix from `ctx.path` and falls back to `/` when no additional path segment remains.
|
|
128
|
+
|
|
129
|
+
That means the URL path is not only a transport detail. It becomes the namespace identity used by namespace beans and namespace-scoped broadcasts.
|
|
130
|
+
|
|
131
|
+
## Client registry and connection tracking
|
|
132
|
+
|
|
133
|
+
Connected clients are tracked by `src/bean/bean.socket.ts`.
|
|
134
|
+
|
|
135
|
+
The registry keeps two coordinated views:
|
|
136
|
+
|
|
137
|
+
- client id -> `WebSocket`
|
|
138
|
+
- namespace -> array of client ids
|
|
139
|
+
|
|
140
|
+
This supports:
|
|
141
|
+
|
|
142
|
+
- direct send by client id
|
|
143
|
+
- namespace broadcast
|
|
144
|
+
- cleanup on disconnect
|
|
145
|
+
- full termination on app shutdown or bean disposal
|
|
146
|
+
|
|
147
|
+
That registry is the bridge between transport-level sockets and higher-level namespace delivery.
|
|
148
|
+
|
|
149
|
+
## Built-in connection onion chain
|
|
150
|
+
|
|
151
|
+
The connection pipeline is built from `socketConnection` onions. The built-in chain executes in this order:
|
|
152
|
+
|
|
153
|
+
1. `alive`
|
|
154
|
+
2. `app`
|
|
155
|
+
3. `instance`
|
|
156
|
+
4. `cors`
|
|
157
|
+
5. `event`
|
|
158
|
+
6. `passport`
|
|
159
|
+
7. `ready`
|
|
160
|
+
|
|
161
|
+
Each stage has a focused responsibility.
|
|
162
|
+
|
|
163
|
+
### `alive`
|
|
164
|
+
|
|
165
|
+
The `alive` stage sets up heartbeat tracking.
|
|
166
|
+
|
|
167
|
+
It:
|
|
168
|
+
|
|
169
|
+
- marks each new socket as alive
|
|
170
|
+
- listens for `pong`
|
|
171
|
+
- runs a periodic interval
|
|
172
|
+
- terminates stale sockets that stop responding
|
|
173
|
+
|
|
174
|
+
The interval uses the module `timeout.ping` config value.
|
|
175
|
+
|
|
176
|
+
### `app`
|
|
177
|
+
|
|
178
|
+
The `app` stage checks runtime readiness.
|
|
179
|
+
|
|
180
|
+
For ordinary external access, it prevents new connections when:
|
|
181
|
+
|
|
182
|
+
- the app is already closing, or
|
|
183
|
+
- the instance service reports that the app is not ready yet
|
|
184
|
+
|
|
185
|
+
This keeps Web Socket traffic aligned with the same backend readiness expectations as other runtime entry paths.
|
|
186
|
+
|
|
187
|
+
### `instance`
|
|
188
|
+
|
|
189
|
+
The `instance` stage initializes the effective instance before the connection proceeds.
|
|
190
|
+
|
|
191
|
+
This is important in multi-instance deployments because Web Socket work should resolve against the same backend instance model as the rest of the framework.
|
|
192
|
+
|
|
193
|
+
Read this together with [Multi-Instance and Instance Resolution](/backend/multi-instance-and-instance-resolution).
|
|
194
|
+
|
|
195
|
+
### `cors`
|
|
196
|
+
|
|
197
|
+
The `cors` stage validates the request origin.
|
|
198
|
+
|
|
199
|
+
If origin checking fails, the socket is terminated immediately.
|
|
200
|
+
|
|
201
|
+
That means Web Socket origin validation is part of the built-in connection chain rather than a separate ad hoc check.
|
|
202
|
+
|
|
203
|
+
### `event`
|
|
204
|
+
|
|
205
|
+
The `event` stage adds the transport send helper:
|
|
206
|
+
|
|
207
|
+
- `ws.sendEvent(...)`
|
|
208
|
+
|
|
209
|
+
This method:
|
|
210
|
+
|
|
211
|
+
- maps system event names to short wire codes when available
|
|
212
|
+
- prefixes event payloads with `'_:'`
|
|
213
|
+
- serializes the packet as JSON
|
|
214
|
+
|
|
215
|
+
Representative pattern:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
ws.send(`${this.scope.config.eventPrefix}${JSON.stringify([eventNameInner, data])}`);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
This is the core server-to-client event encoding mechanism.
|
|
222
|
+
|
|
223
|
+
### `passport`
|
|
224
|
+
|
|
225
|
+
The `passport` stage handles authentication.
|
|
226
|
+
|
|
227
|
+
A practical rule is:
|
|
228
|
+
|
|
229
|
+
- if a current passport already exists, use it
|
|
230
|
+
- otherwise read the access token from the configured passport-code query key
|
|
231
|
+
- if no authenticated passport is established, sign in anonymously
|
|
232
|
+
|
|
233
|
+
This gives Web Socket connections the same backend identity model as other Vona runtime flows, while still allowing anonymous connections when that is the intended capability.
|
|
234
|
+
|
|
235
|
+
### `ready`
|
|
236
|
+
|
|
237
|
+
The `ready` stage is the terminal built-in stage in the default chain.
|
|
238
|
+
|
|
239
|
+
It does not add extra behavior by itself, but it completes the ordered dependency chain and marks the point after which the connection is considered ready for normal packet processing.
|
|
240
|
+
|
|
241
|
+
## System ready signal
|
|
242
|
+
|
|
243
|
+
After the connection chain finishes successfully, `a-socket` sends the system event:
|
|
244
|
+
|
|
245
|
+
- `sysReady`
|
|
246
|
+
|
|
247
|
+
This tells the client that:
|
|
248
|
+
|
|
249
|
+
- the connection was accepted
|
|
250
|
+
- the server-side connection pipeline completed
|
|
251
|
+
- the socket can begin ordinary packet traffic
|
|
252
|
+
|
|
253
|
+
## Packet processing flow
|
|
254
|
+
|
|
255
|
+
Inbound Web Socket messages move through the `socketPacket` onion chain.
|
|
256
|
+
|
|
257
|
+
The built-in packet order is:
|
|
258
|
+
|
|
259
|
+
1. `event`
|
|
260
|
+
2. `performAction`
|
|
261
|
+
|
|
262
|
+
### `event`
|
|
263
|
+
|
|
264
|
+
The `event` packet stage normalizes raw inbound data.
|
|
265
|
+
|
|
266
|
+
If a string payload starts with the configured event prefix `'_:'`, the stage:
|
|
267
|
+
|
|
268
|
+
- removes the prefix
|
|
269
|
+
- parses the JSON payload
|
|
270
|
+
- reverse-maps short wire codes such as `_a`, `_b`, and `_c`
|
|
271
|
+
- returns a structured packet `[eventName, data]`
|
|
272
|
+
|
|
273
|
+
If the payload is not an event-formatted string, the stage forwards:
|
|
274
|
+
|
|
275
|
+
- `[undefined, data]`
|
|
276
|
+
|
|
277
|
+
That makes later packet handlers operate on a normalized packet shape instead of raw transport data.
|
|
278
|
+
|
|
279
|
+
### `performAction`
|
|
280
|
+
|
|
281
|
+
The `performAction` stage implements the built-in request-like action channel.
|
|
282
|
+
|
|
283
|
+
It handles the system event:
|
|
284
|
+
|
|
285
|
+
- `sysPerformAction`
|
|
286
|
+
|
|
287
|
+
The payload contains compact request fields such as:
|
|
288
|
+
|
|
289
|
+
- request id
|
|
290
|
+
- method
|
|
291
|
+
- path
|
|
292
|
+
- query
|
|
293
|
+
- body
|
|
294
|
+
- headers
|
|
295
|
+
|
|
296
|
+
The stage forwards those values to:
|
|
297
|
+
|
|
298
|
+
- `this.$scope.executor.service.executor.performActionInner(...)`
|
|
299
|
+
|
|
300
|
+
If execution succeeds, the server sends:
|
|
301
|
+
|
|
302
|
+
- `sysPerformActionBack` with success code `0` and the result payload
|
|
303
|
+
|
|
304
|
+
If execution fails, the server sends:
|
|
305
|
+
|
|
306
|
+
- `sysPerformActionBack` with the backend error code and message
|
|
307
|
+
|
|
308
|
+
This gives Vona a built-in RPC-like path over Web Socket without replacing the ordinary backend action execution model.
|
|
309
|
+
|
|
310
|
+
## System event protocol
|
|
311
|
+
|
|
312
|
+
The canonical built-in system event mapping is:
|
|
313
|
+
|
|
314
|
+
- `sysReady -> _a`
|
|
315
|
+
- `sysPerformAction -> _b`
|
|
316
|
+
- `sysPerformActionBack -> _c`
|
|
317
|
+
|
|
318
|
+
A useful interpretation is:
|
|
319
|
+
|
|
320
|
+
- the human-readable names are the framework event identities
|
|
321
|
+
- the short codes are the compact wire-level transport forms
|
|
322
|
+
|
|
323
|
+
Because the mapping lives in `types/socketEvent.ts`, that file is the source-truth contract for built-in Web Socket system events.
|
|
324
|
+
|
|
325
|
+
## Sending and broadcasting messages
|
|
326
|
+
|
|
327
|
+
The delivery service is implemented in `src/service/socketEvent.ts`.
|
|
328
|
+
|
|
329
|
+
It provides two main patterns:
|
|
330
|
+
|
|
331
|
+
- `send(id, eventName, data, options)`
|
|
332
|
+
- `broadcast(namespace, eventName, data, options)`
|
|
333
|
+
|
|
334
|
+
### Direct send
|
|
335
|
+
|
|
336
|
+
`send(...)` delivers to one known client id.
|
|
337
|
+
|
|
338
|
+
A practical flow is:
|
|
339
|
+
|
|
340
|
+
- send immediately on the current worker through `sendWorker(...)`
|
|
341
|
+
- emit the matching broadcast bean so other workers can perform the same delivery check if needed
|
|
342
|
+
|
|
343
|
+
### Namespace broadcast
|
|
344
|
+
|
|
345
|
+
`broadcast(...)` delivers to all known clients in one namespace.
|
|
346
|
+
|
|
347
|
+
A practical flow is:
|
|
348
|
+
|
|
349
|
+
- send immediately to namespace members on the current worker through `broadcastWorker(...)`
|
|
350
|
+
- emit the matching broadcast bean so other workers can fan out to their local namespace members
|
|
351
|
+
|
|
352
|
+
This is why Web Socket delivery in Vona is closely related to distributed runtime primitives.
|
|
353
|
+
|
|
354
|
+
Read this together with:
|
|
355
|
+
|
|
356
|
+
- [Broadcast Guide](/backend/broadcast-guide)
|
|
357
|
+
- [Worker Guide](/backend/worker-guide)
|
|
358
|
+
|
|
359
|
+
## Defining a socket namespace
|
|
360
|
+
|
|
361
|
+
The main extension pattern for application modules is a socket namespace bean.
|
|
362
|
+
|
|
363
|
+
The pattern has three parts:
|
|
364
|
+
|
|
365
|
+
1. extend `ISocketNamespaceRecord`
|
|
366
|
+
2. declare a bean with `@SocketNamespace(...)`
|
|
367
|
+
3. inherit `BeanSocketNamespaceBase`
|
|
368
|
+
|
|
369
|
+
Representative shape:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
declare module 'vona-module-a-socket' {
|
|
373
|
+
export interface ISocketNamespaceRecord {
|
|
374
|
+
'/ssrhmr': never;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
@SocketNamespace({
|
|
379
|
+
namespace: '/ssrhmr',
|
|
380
|
+
})
|
|
381
|
+
export class SocketNamespaceSsrHmr extends BeanSocketNamespaceBase {}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`BeanSocketNamespaceBase` gives namespace beans two useful helpers:
|
|
385
|
+
|
|
386
|
+
- `send(id, eventName, data, options)`
|
|
387
|
+
- `broadcast(eventName, data, options)`
|
|
388
|
+
|
|
389
|
+
That means downstream modules usually do not need to talk to the low-level socket registry directly.
|
|
390
|
+
|
|
391
|
+
## Example: `a-ssrhmr`
|
|
392
|
+
|
|
393
|
+
The `a-ssrhmr` module is a concrete example of extending `a-socket`.
|
|
394
|
+
|
|
395
|
+
Its namespace bean declares:
|
|
396
|
+
|
|
397
|
+
- namespace: `/ssrhmr`
|
|
398
|
+
- event: `reload`
|
|
399
|
+
|
|
400
|
+
Representative shape:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
@SocketNamespace<ISocketNamespaceOptionsSsrHmr>({
|
|
404
|
+
namespace: '/ssrhmr',
|
|
405
|
+
})
|
|
406
|
+
export class SocketNamespaceSsrHmr extends BeanSocketNamespaceBase<ISocketNamespaceOptionsSsrHmrEvents> {}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Then a regular bean can trigger namespace broadcast through scope:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
@Bean()
|
|
413
|
+
export class BeanSsrHmr extends BeanBase {
|
|
414
|
+
reload() {
|
|
415
|
+
this.scope.socketNamespace.ssrHmr.broadcast('reload');
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
This example shows the intended layering:
|
|
421
|
+
|
|
422
|
+
- `a-socket` owns the transport runtime
|
|
423
|
+
- application or framework modules define namespace semantics
|
|
424
|
+
- ordinary beans trigger namespace delivery through scope
|
|
425
|
+
|
|
426
|
+
## Relationship to other backend guides
|
|
427
|
+
|
|
428
|
+
Read this guide together with:
|
|
429
|
+
|
|
430
|
+
- [Web Socket Usage Guide](/backend/websocket-usage-guide)
|
|
431
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide)
|
|
432
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow)
|
|
433
|
+
- [Runtime and Flavors](/backend/runtime-and-flavors)
|
|
434
|
+
- [Config Guide](/backend/config-guide)
|
|
435
|
+
- [Broadcast Guide](/backend/broadcast-guide)
|
|
436
|
+
- [Worker Guide](/backend/worker-guide)
|
|
437
|
+
- [Event Guide](/backend/event-guide)
|
|
438
|
+
|
|
439
|
+
A practical distinction is:
|
|
440
|
+
|
|
441
|
+
- use **Event** when backend code needs framework-native in-process event composition
|
|
442
|
+
- use **Broadcast** when many workers should all receive the same signal
|
|
443
|
+
- use **Web Socket** when the backend needs long-lived client connections, server push, or packet-based request flow over a persistent transport
|
|
444
|
+
|
|
445
|
+
## Implementation checks for Web Socket-related backend work
|
|
446
|
+
|
|
447
|
+
When extending or reviewing Web Socket behavior, ask:
|
|
448
|
+
|
|
449
|
+
1. does the work belong in a namespace bean, a connection onion, or a packet onion?
|
|
450
|
+
2. is the namespace path consistent with the `/ws/...` routing model?
|
|
451
|
+
3. does the connection flow need identity, instance, or origin checks?
|
|
452
|
+
4. should delivery be point-to-point or namespace-wide?
|
|
453
|
+
5. is the behavior local to one worker, or should it propagate through broadcast?
|
|
454
|
+
|
|
455
|
+
That helps backend extensions stay aligned with the existing `a-socket` architecture instead of bypassing the framework transport model.
|