@nodefony/realtime 10.0.0-beta.3 → 10.0.0-beta.4
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/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/index.js +4 -6
- package/dist/nodefony/config/config.js +1 -2
- package/dist/nodefony/config/configSchema.js +1 -2
- package/dist/nodefony/config/defineModuleConfig.js +1 -2
- package/dist/nodefony/decorators/realtimeDecorators.js +1 -2
- package/dist/nodefony/interfaces/IRealtimeConnectionState.js +1 -0
- package/dist/nodefony/src/backplane/ClusterBackplane.js +1 -2
- package/dist/nodefony/src/backplane/LoopbackBackplane.js +1 -2
- package/dist/nodefony/src/backplane/RedisBackplane.js +1 -2
- package/dist/nodefony/src/backplane/backplaneRegistry.js +1 -2
- package/dist/nodefony/src/backplane/bootNotices.js +1 -2
- package/dist/nodefony/src/backplane/envelope.js +1 -2
- package/dist/nodefony/src/backplane/originId.js +1 -2
- package/dist/nodefony/src/backplane/publishQueue.js +0 -1
- package/dist/nodefony/src/cluster/ClusterProbeClient.js +1 -2
- package/dist/nodefony/src/guards.js +1 -2
- package/dist/nodefony/src/server/AnonymousRealtimeToken.js +1 -2
- package/dist/nodefony/src/server/RealtimeAdminApi.js +1 -2
- package/dist/nodefony/src/server/RealtimeController.js +154 -80
- package/dist/nodefony/src/server/RealtimeHub.js +3 -3
- package/dist/nodefony/src/server/ServerRealtimeSocket.js +18 -9
- package/dist/nodefony/src/server/deniedDetail.js +1 -2
- package/dist/nodefony/src/server/subscribedAck.js +29 -0
- package/dist/nodefony/src/server/syslogUplink.js +1 -2
- package/dist/nodefony/src/server/welcomeEnv.js +1 -2
- package/dist/nodefony/src/service/RealtimeService.js +1 -2
- package/dist/nodefony/src/transport/WsConnectionTransport.js +42 -2
- package/dist/nodefony/testing/index.js +77 -4
- package/dist/types/index.d.ts +1 -0
- package/dist/types/nodefony/interfaces/IRealtimeConnectionState.d.ts +40 -0
- package/dist/types/nodefony/interfaces/IRealtimeToken.d.ts +13 -0
- package/dist/types/nodefony/interfaces/index.d.ts +1 -0
- package/dist/types/nodefony/src/server/RealtimeController.d.ts +58 -0
- package/dist/types/nodefony/src/server/ServerRealtimeSocket.d.ts +18 -8
- package/dist/types/nodefony/src/server/subscribedAck.d.ts +23 -0
- package/dist/types/nodefony/src/transport/WsConnectionTransport.d.ts +26 -1
- package/dist/types/nodefony/testing/index.d.ts +6 -1
- package/docs/actions.md +44 -43
- package/docs/architecture.md +125 -119
- package/docs/cadence-et-contre-pression.md +31 -22
- package/docs/configuration.md +106 -82
- package/docs/cookbook-chat.md +37 -34
- package/docs/index.md +35 -24
- package/docs/observabilite.md +49 -49
- package/docs/protocole.md +91 -68
- package/docs/securite.md +125 -114
- package/docs/vocabulaire.md +117 -114
- package/package.json +10 -10
- package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.js +0 -6
package/docs/vocabulaire.md
CHANGED
|
@@ -112,9 +112,9 @@ Nodefony **fige** donc trois séparations, et tout le reste en découle :
|
|
|
112
112
|
Trois mots du lexique ne sont pas des synonymes de ce que proposent les autres bibliothèques
|
|
113
113
|
temps réel. Ce sont les différenciateurs du framework.
|
|
114
114
|
|
|
115
|
-
**`socket` est isomorphe.** Le même contrat `IRealtimeSocket` (`IRealtimeSocket.ts
|
|
116
|
-
prise **côté navigateur** (`NodefonySocket`, `NodefonySocket.ts
|
|
117
|
-
(`ServerRealtimeSocket`, `ServerRealtimeSocket.ts
|
|
115
|
+
**`socket` est isomorphe.** Le même contrat `IRealtimeSocket` (`IRealtimeSocket.ts#IRealtimeSocket`) décrit la
|
|
116
|
+
prise **côté navigateur** (`NodefonySocket`, `NodefonySocket.ts#NodefonySocket`) **et côté serveur**
|
|
117
|
+
(`ServerRealtimeSocket`, `ServerRealtimeSocket.ts#ServerRealtimeSocket`). Un service back publie exactement comme une
|
|
118
118
|
page front : `publish("chat:room-42", payload)`. Il n'y a pas une API cliente et une API serveur à
|
|
119
119
|
apprendre, il y en a **une**.
|
|
120
120
|
|
|
@@ -122,12 +122,12 @@ apprendre, il y en a **une**.
|
|
|
122
122
|
bidirectionnel, chacun avec son cycle de vie et son compteur d'abonnés. Le backing d'un canal est
|
|
123
123
|
**pluggable côté serveur** — pub/sub, encapsulation d'un autre protocole, pont vers TCP/UDP, relais
|
|
124
124
|
vers un autre service — et le consommateur ne voit jamais la différence
|
|
125
|
-
(`IRealtimeChannel`, `IRealtimeSocket.ts
|
|
125
|
+
(`IRealtimeChannel`, `IRealtimeSocket.ts#IRealtimeChannel.name`).
|
|
126
126
|
|
|
127
127
|
**`contrôleur` est le même mot qu'en HTTP.** Un endpoint temps réel est un contrôleur qui étend
|
|
128
|
-
`RealtimeController` (`RealtimeController.ts
|
|
128
|
+
`RealtimeController` (`RealtimeController.ts#RealtimeController`) et porte une route WebSocket : HTTP et WebSocket
|
|
129
129
|
vivent dans le **même contexte de contrôleur**. C'est ce qui permet au pont `api.request`
|
|
130
|
-
(`RealtimeController.realtimeApiRequest()`, `RealtimeController.ts
|
|
130
|
+
(`RealtimeController.realtimeApiRequest()`, `RealtimeController.ts#RealtimeController.realtimeApiRequest`) de rejouer sur la socket
|
|
131
131
|
**la même action** que celle servie en REST.
|
|
132
132
|
|
|
133
133
|
## 🚀 Démarrage rapide — les mots en situation
|
|
@@ -217,9 +217,9 @@ export async function joinChat(): Promise<void> {
|
|
|
217
217
|
Le **handle unique** manipulé par le code applicatif : quatre verbes (`subscribe`, `on`, `publish`,
|
|
218
218
|
`request`) et une vue par canal. Le mot vient de la prise murale : on branche, on ignore le câblage.
|
|
219
219
|
|
|
220
|
-
Contrat isomorphe `IRealtimeSocket` (`IRealtimeSocket.ts
|
|
221
|
-
`NodefonySocket` (`NodefonySocket.ts
|
|
222
|
-
(`ServerRealtimeSocket.ts
|
|
220
|
+
Contrat isomorphe `IRealtimeSocket` (`IRealtimeSocket.ts#IRealtimeSocket`), implémenté côté navigateur par
|
|
221
|
+
`NodefonySocket` (`NodefonySocket.ts#NodefonySocket`) et côté serveur par `ServerRealtimeSocket`
|
|
222
|
+
(`ServerRealtimeSocket.ts#ServerRealtimeSocket`). ⚠️ La socket **n'est pas** le [transport](#transport--la-couche-octets).
|
|
223
223
|
|
|
224
224
|
→ [Architecture](./architecture.md) pour la pile complète.
|
|
225
225
|
|
|
@@ -229,8 +229,8 @@ Un **tuyau bidirectionnel nommé**, multiplexé sur la connexion : `"chat:room-4
|
|
|
229
229
|
`"nodefony:orm:health"`, `"nodefony:socket"`. Convention de nommage par `:` (espace de noms, puis
|
|
230
230
|
précision). Ce n'est pas une classe — c'est une **chaîne de caractères indexée par le hub**.
|
|
231
231
|
|
|
232
|
-
`RealtimeHub.subscribe()` (`RealtimeHub.ts
|
|
233
|
-
(`IRealtimeSocket.ts
|
|
232
|
+
`RealtimeHub.subscribe()` (`RealtimeHub.ts#RealtimeHub.subscribe`) l'indexe ; `IRealtimeSocket.subscribe()`
|
|
233
|
+
(`IRealtimeSocket.ts#IRealtimeSocket.subscribe`) le demande. Plusieurs canaux cohabitent sur **une** connexion : c'est le
|
|
234
234
|
multiplexage.
|
|
235
235
|
|
|
236
236
|
### `abonnement` (subscribe) — demander le flux
|
|
@@ -238,8 +238,8 @@ multiplexage.
|
|
|
238
238
|
**Ref-compté** : la demande part vers le pair au **premier** consommateur seulement, et le flux se
|
|
239
239
|
coupe au **dernier** désabonnement. Réémise automatiquement à chaque reconnexion.
|
|
240
240
|
|
|
241
|
-
`IRealtimeSocket.subscribe()` (`IRealtimeSocket.ts
|
|
242
|
-
(`limits.maxChannelsPerConnection`, `config.ts
|
|
241
|
+
`IRealtimeSocket.subscribe()` (`IRealtimeSocket.ts#IRealtimeSocket.subscribe`). Le plafond par connexion est réglable
|
|
242
|
+
(`limits.maxChannelsPerConnection`, `config.ts#limitsSchema`) — au-delà, le canal n'est pas ouvert.
|
|
243
243
|
|
|
244
244
|
### `réception` (on) — brancher un handler
|
|
245
245
|
|
|
@@ -247,7 +247,7 @@ coupe au **dernier** désabonnement. Réémise automatiquement à chaque reconne
|
|
|
247
247
|
module : `on` ne demande rien au serveur, `subscribe` si. S'abonner sans écouter reçoit des frames
|
|
248
248
|
que personne ne lit ; écouter sans s'abonner n'en reçoit aucune.
|
|
249
249
|
|
|
250
|
-
`IRealtimeSocket.on(channel, handler)` (`IRealtimeSocket.ts
|
|
250
|
+
`IRealtimeSocket.on(channel, handler)` (`IRealtimeSocket.ts#IRealtimeSocket.on`).
|
|
251
251
|
|
|
252
252
|
### `publication` (publish) — émettre sans réponse
|
|
253
253
|
|
|
@@ -255,25 +255,28 @@ que personne ne lit ; écouter sans s'abonner n'en reçoit aucune.
|
|
|
255
255
|
client, la charge part vers le serveur (un seul pair) ; depuis le serveur, elle part en
|
|
256
256
|
[fan-out](#fan-out--une-publication-n-livraisons) vers tous les abonnés.
|
|
257
257
|
|
|
258
|
-
`IRealtimeSocket.publish()` (`IRealtimeSocket.ts
|
|
258
|
+
`IRealtimeSocket.publish()` (`IRealtimeSocket.ts#IRealtimeSocket.publish`), `RealtimeHub.publish()` (`RealtimeHub.ts#RealtimeHub.publish`).
|
|
259
259
|
|
|
260
260
|
### `action RPC` (request) — l'appel corrélé
|
|
261
261
|
|
|
262
262
|
L'appel qui **attend une réponse** : une `Promise` résolue avec le `result`, rejetée sur `error` ou
|
|
263
|
-
sur expiration. Déclarée côté serveur par `@RealtimeAction` (`realtimeDecorators.ts
|
|
264
|
-
côté client par `request()` (`
|
|
263
|
+
sur expiration. Déclarée côté serveur par `@RealtimeAction` (`realtimeDecorators.ts#RealtimeAction`), appelée
|
|
264
|
+
côté client par `request()` (`NodefonySocket.ts#NodefonySocket.request`), côté serveur vers UN
|
|
265
|
+
client par `requestClient()` (`RealtimeController.ts#RealtimeController.requestClient`). Elle
|
|
266
|
+
n'appartient pas au contrat commun `IRealtimeSocket` : une requête attend UN pair, et un handle
|
|
267
|
+
serveur au-dessus du hub en a N.
|
|
265
268
|
|
|
266
269
|
Un handler qui lève une erreur rend un `-32603` **générique** ; seule une `RpcError`
|
|
267
|
-
(`JsonRpcPeer.ts
|
|
270
|
+
(`JsonRpcPeer.ts#RpcError`) choisit ce qu'elle expose au pair.
|
|
268
271
|
|
|
269
272
|
### `canal entrant` (inbound) — le client pousse
|
|
270
273
|
|
|
271
274
|
Un canal où le **client a le droit d'émettre** vers le serveur. Défaut sûr : **aucun**. Un canal
|
|
272
|
-
n'accepte d'entrée que déclaré explicitement, par `@RealtimeInbound` (`realtimeDecorators.ts
|
|
273
|
-
par l'override `realtimeInbound()` (`RealtimeController.ts
|
|
275
|
+
n'accepte d'entrée que déclaré explicitement, par `@RealtimeInbound` (`realtimeDecorators.ts#RealtimeInbound`) ou
|
|
276
|
+
par l'override `realtimeInbound()` (`RealtimeController.ts#RealtimeController.realtimeInbound`).
|
|
274
277
|
|
|
275
278
|
Le handler reçoit `(params, reply)` — `params` vient du réseau, donc **jamais fiable** :
|
|
276
|
-
`RealtimeInboundHandler` (`IRealtimeController.ts
|
|
279
|
+
`RealtimeInboundHandler` (`IRealtimeController.ts#RealtimeInboundHandler`).
|
|
277
280
|
|
|
278
281
|
### `handle de canal` — la vue objet d'un canal
|
|
279
282
|
|
|
@@ -281,11 +284,11 @@ Le handler reçoit `(params, reply)` — `params` vient du réseau, donc **jamai
|
|
|
281
284
|
`pubsub`, `protocol`, `bridge`, `proxy`) et son cycle de vie (`open`/`close`/`send`/`on`). C'est le
|
|
282
285
|
point d'accroche des canaux à état — un appel SIP, une connexion pontée.
|
|
283
286
|
|
|
284
|
-
`IRealtimeChannel` (`IRealtimeSocket.ts
|
|
287
|
+
`IRealtimeChannel` (`IRealtimeSocket.ts#IRealtimeChannel.name`), `channel()` (`IRealtimeSocket.ts#IRealtimeSocket.channel`).
|
|
285
288
|
|
|
286
289
|
### `client` — la socket côté navigateur
|
|
287
290
|
|
|
288
|
-
`NodefonySocket` (`NodefonySocket.ts
|
|
291
|
+
`NodefonySocket` (`NodefonySocket.ts#NodefonySocket`) : reconnexion automatique, réémission des abonnements,
|
|
289
292
|
compteurs par canal, heartbeat. Publié dans le sous-chemin `nodefony/client` du cœur — donc
|
|
290
293
|
importable **sans** aucune dépendance serveur, ce qui est la condition de l'isomorphisme.
|
|
291
294
|
|
|
@@ -293,11 +296,11 @@ importable **sans** aucune dépendance serveur, ce qui est la condition de l'iso
|
|
|
293
296
|
|
|
294
297
|
### `façade serveur` — la même socket, côté back
|
|
295
298
|
|
|
296
|
-
`ServerRealtimeSocket` (`ServerRealtimeSocket.ts
|
|
297
|
-
(`ServerRealtimeSocket.ts
|
|
299
|
+
`ServerRealtimeSocket` (`ServerRealtimeSocket.ts#ServerRealtimeSocket`), obtenue par `serverSocket()`
|
|
300
|
+
(`ServerRealtimeSocket.ts#serverSocket`) : un service métier tient un handle et publie **comme une page
|
|
298
301
|
front**. Une différence assumée : `request()` n'y est pas supporté — au-dessus du hub il n'y a pas
|
|
299
302
|
**un** pair mais N clients. Pour un appel serveur → un client précis, c'est `requestClient()`
|
|
300
|
-
(`RealtimeController.ts
|
|
303
|
+
(`RealtimeController.ts#RealtimeController.requestClient`).
|
|
301
304
|
|
|
302
305
|
### `accueil` (welcome) — la première frame
|
|
303
306
|
|
|
@@ -305,7 +308,7 @@ La notification `realtime:welcome`, poussée par le serveur juste après le hand
|
|
|
305
308
|
protocole, les canaux que ce visiteur peut obtenir, les actions de l'endpoint, et l'identité résolue. Un client
|
|
306
309
|
attend cette frame avant de pousser quoi que ce soit.
|
|
307
310
|
|
|
308
|
-
`IRealtimeWelcome` (`RealtimeEventMap.ts
|
|
311
|
+
`IRealtimeWelcome` (`RealtimeEventMap.ts#IRealtimeWelcome`).
|
|
309
312
|
|
|
310
313
|
### `identité` — qui est cette connexion
|
|
311
314
|
|
|
@@ -313,7 +316,7 @@ La vue « sur soi » d'une connexion : type de jeton, authentifié ou non, ident
|
|
|
313
316
|
Aucun secret — seulement ce que le porteur sait déjà de lui-même. Elle est **résolue une fois** au
|
|
314
317
|
handshake, jamais renégociée par frame.
|
|
315
318
|
|
|
316
|
-
`RealtimeIdentity` (`RealtimeEventMap.ts
|
|
319
|
+
`RealtimeIdentity` (`RealtimeEventMap.ts#RealtimeIdentity`).
|
|
317
320
|
|
|
318
321
|
## 🏗️ Le serveur — ce qui aiguille derrière la prise
|
|
319
322
|
|
|
@@ -335,10 +338,10 @@ handshake, jamais renégociée par frame.
|
|
|
335
338
|
### `hub` — le broker du process
|
|
336
339
|
|
|
337
340
|
Le **standard téléphonique** du pod : il tient la table « canal → abonnés locaux » et diffuse. Un
|
|
338
|
-
process = un hub, obtenu par `getRealtimeHub()` (`RealtimeHub.ts
|
|
341
|
+
process = un hub, obtenu par `getRealtimeHub()` (`RealtimeHub.ts#getRealtimeHub`). Il ne connaît **ni** les
|
|
339
342
|
contrôleurs, **ni** le métier : ce sont les providers qui portent les dépendances.
|
|
340
343
|
|
|
341
|
-
`RealtimeHub` (`RealtimeHub.ts
|
|
344
|
+
`RealtimeHub` (`RealtimeHub.ts#RealtimeHub`). ⚠️ « hub » désigne **toujours** le serveur ; ce que tient le
|
|
342
345
|
code applicatif s'appelle une [socket](#socket--la-prise-que-tient-ton-code).
|
|
343
346
|
|
|
344
347
|
### `fan-out` — une publication, N livraisons
|
|
@@ -346,15 +349,15 @@ code applicatif s'appelle une [socket](#socket--la-prise-que-tient-ton-code).
|
|
|
346
349
|
L'action de diffuser : une charge publiée sur un canal part vers **tous** les sinks abonnés
|
|
347
350
|
localement. Une connexion fautive ne casse pas la diffusion aux autres — chaque livraison est isolée.
|
|
348
351
|
|
|
349
|
-
`RealtimeHub.publish()` (`RealtimeHub.ts
|
|
350
|
-
(`RealtimeHub.ts
|
|
352
|
+
`RealtimeHub.publish()` (`RealtimeHub.ts#RealtimeHub.publish`) pour le chemin complet, `publishLocal()`
|
|
353
|
+
(`RealtimeHub.ts#RealtimeHub.publishLocal`) pour la diffusion **strictement locale** (voie d'entrée du backplane).
|
|
351
354
|
|
|
352
355
|
### `sink` — le point de livraison d'une connexion
|
|
353
356
|
|
|
354
357
|
Un `sink` = **une** connexion sur **un** canal : la fonction qui pousse la charge vers son pair. Le
|
|
355
358
|
mot vient du couple source/puits : le provider est la source, le sink le puits.
|
|
356
359
|
|
|
357
|
-
`ChannelSink` (`RealtimeHub.ts
|
|
360
|
+
`ChannelSink` (`RealtimeHub.ts#ChannelSink`). Le nombre de sinks d'un canal est son nombre d'abonnés locaux.
|
|
358
361
|
|
|
359
362
|
### `provider` — ce qui produit un canal
|
|
360
363
|
|
|
@@ -362,7 +365,7 @@ La fabrique appelée **au premier abonné** d'un canal, qui démarre ce qui prod
|
|
|
362
365
|
ticker, un écouteur) et rend son `dispose`, appelé **au dernier désabonné**. Zéro abonné = zéro
|
|
363
366
|
timer.
|
|
364
367
|
|
|
365
|
-
`ChannelFactory` (`RealtimeHub.ts
|
|
368
|
+
`ChannelFactory` (`RealtimeHub.ts#ChannelFactory`). ⚠️ Un provider est **partagé** : il survit à la connexion qui
|
|
366
369
|
l'a créé — n'y capturer que des dépendances à longue vie, jamais le contexte d'une connexion.
|
|
367
370
|
|
|
368
371
|
### `canal partagé` — un provider par canal et par pod
|
|
@@ -371,7 +374,7 @@ Le modèle de coût du module : là où une implémentation naïve crée un tick
|
|
|
371
374
|
Nodefony en crée **un par canal et par pod** et se contente d'ajouter un sink par abonné. Mille
|
|
372
375
|
spectateurs d'un tableau de bord coûtent un timer, pas mille.
|
|
373
376
|
|
|
374
|
-
`RealtimeHub.subscribe()` (`RealtimeHub.ts
|
|
377
|
+
`RealtimeHub.subscribe()` (`RealtimeHub.ts#RealtimeHub.subscribe`).
|
|
375
378
|
|
|
376
379
|
### `canal passif` — écouter n'est pas posséder
|
|
377
380
|
|
|
@@ -385,8 +388,8 @@ reçoit du vrai producteur ; sinon l'abonnement est refusé, et l'écoute en pla
|
|
|
385
388
|
Sans cette distinction, le premier service qui prononce un nom de canal l'ouvrirait pour tout le
|
|
386
389
|
monde — et les clients suivants s'abonneraient à un flux que rien ne produit, sans jamais l'apprendre.
|
|
387
390
|
|
|
388
|
-
`RealtimeHub.listen()` (`RealtimeHub.ts
|
|
389
|
-
(`RealtimeHub.ts
|
|
391
|
+
`RealtimeHub.listen()` (`RealtimeHub.ts#RealtimeHub.listen`) pour l'écoute, `isChannelOwned()`
|
|
392
|
+
(`RealtimeHub.ts#RealtimeHub.isChannelOwned`) pour lire l'état. ⚠️ Un canal passif **n'est pas** un canal fermé : il n'a
|
|
390
393
|
simplement pas encore de propriétaire.
|
|
391
394
|
|
|
392
395
|
### `canal système` — servi par la plateforme
|
|
@@ -395,7 +398,7 @@ Un canal dont la fabrique est déclarée par un module bas niveau (le journal d'
|
|
|
395
398
|
qui devient servable par **n'importe quel** endpoint, présent ou futur, sans qu'aucun contrôleur ne
|
|
396
399
|
le connaisse. Consulté seulement quand la fabrique du contrôleur a dit « inconnu ».
|
|
397
400
|
|
|
398
|
-
`RealtimeHub.registerSystemChannel()` (`RealtimeHub.ts
|
|
401
|
+
`RealtimeHub.registerSystemChannel()` (`RealtimeHub.ts#RealtimeHub.registerSystemChannel`).
|
|
399
402
|
|
|
400
403
|
### `canal broadcast` — celui qui traverse le backplane
|
|
401
404
|
|
|
@@ -403,8 +406,8 @@ Un canal **déclaré** comme franchissant la frontière du process : chat, prés
|
|
|
403
406
|
déclaration se fait par **préfixe**, ce qui couvre les variantes de cadence (`chat:` couvre
|
|
404
407
|
`chat:room-42:1000`).
|
|
405
408
|
|
|
406
|
-
`RealtimeHub.markBroadcastChannel()` (`RealtimeHub.ts
|
|
407
|
-
`@RealtimeBroadcast` (`realtimeDecorators.ts
|
|
409
|
+
`RealtimeHub.markBroadcastChannel()` (`RealtimeHub.ts#RealtimeHub.markBroadcastChannel`), déclaré côté contrôleur par
|
|
410
|
+
`@RealtimeBroadcast` (`realtimeDecorators.ts#RealtimeBroadcast`).
|
|
408
411
|
|
|
409
412
|
### `canal instance-local` — le défaut
|
|
410
413
|
|
|
@@ -412,15 +415,15 @@ Tout canal **non déclaré** broadcast reste dans son process. C'est voulu : les
|
|
|
412
415
|
d'observabilité (journaux, sondes, état interne) décrivent **ce pod**, et les agréger silencieusement
|
|
413
416
|
serait à la fois faux et une fuite. Traverser le process est une capacité qu'un canal **demande**.
|
|
414
417
|
|
|
415
|
-
Politique de forward du hub — `#broadcastPrefixes` (`RealtimeHub.ts
|
|
418
|
+
Politique de forward du hub — `#broadcastPrefixes` (`RealtimeHub.ts#RealtimeHub.#broadcastPrefixes`).
|
|
416
419
|
|
|
417
420
|
### `contrôleur temps réel` — l'endpoint WebSocket
|
|
418
421
|
|
|
419
422
|
La classe de base d'un endpoint : elle porte tout le protocole (handshake, accueil, discrimination
|
|
420
423
|
des frames, cycle de vie des canaux) et ne laisse au métier que ses canaux et ses actions.
|
|
421
424
|
|
|
422
|
-
`RealtimeController` (`RealtimeController.ts
|
|
423
|
-
(`RealtimeController.ts
|
|
425
|
+
`RealtimeController` (`RealtimeController.ts#RealtimeController`), point d'entrée `handleRealtime()`
|
|
426
|
+
(`RealtimeController.ts#RealtimeController.handleRealtime`). C'est un **contrôleur** au sens habituel de Nodefony : la même classe
|
|
424
427
|
peut porter des routes HTTP.
|
|
425
428
|
|
|
426
429
|
### `service realtime` — la façade d'injection
|
|
@@ -429,7 +432,7 @@ Le service exposé au conteneur sous le nom `realtimeService` : `publish`, `subs
|
|
|
429
432
|
config gelée. C'est l'API stable pour le code utilisateur, quand `getRealtimeHub()` est l'accès
|
|
430
433
|
interne.
|
|
431
434
|
|
|
432
|
-
`RealtimeService` (`RealtimeService.ts
|
|
435
|
+
`RealtimeService` (`RealtimeService.ts#RealtimeService`).
|
|
433
436
|
|
|
434
437
|
### `pont API` — la même action, sur la socket
|
|
435
438
|
|
|
@@ -437,8 +440,8 @@ L'option qui expose la méthode `api.request { path }` : la connexion rejoue **l
|
|
|
437
440
|
contrôleur** que celle servie en REST, avec **la même garde**. Le pont n'atteint que les routes qui
|
|
438
441
|
déclarent explicitement le transport WebSocket — aucun contournement possible.
|
|
439
442
|
|
|
440
|
-
`realtimeApiRequest()` (`RealtimeController.ts
|
|
441
|
-
(`RealtimeController.ts
|
|
443
|
+
`realtimeApiRequest()` (`RealtimeController.ts#RealtimeController.realtimeApiRequest`), mise en œuvre `invokeApiRequest()`
|
|
444
|
+
(`RealtimeController.ts#RealtimeController.invokeApiRequest`). Désactivé par défaut.
|
|
442
445
|
|
|
443
446
|
## 🔌 Le protocole et le transport — ce qui passe sur le fil
|
|
444
447
|
|
|
@@ -461,8 +464,8 @@ Une enveloppe JSON-RPC 2.0. Sa **nature se lit sur `method`**, pas sur `id` : `m
|
|
|
461
464
|
requête, `method` seul = notification, `id` + `result` ou `error` = réponse. Tout le reste est
|
|
462
465
|
invalide.
|
|
463
466
|
|
|
464
|
-
`JsonRpcFrameKind` (`jsonrpc/index.ts
|
|
465
|
-
(`JsonRpcPeer.ts
|
|
467
|
+
`JsonRpcFrameKind` (`jsonrpc/index.ts#JsonRpcFrameKind`). Le classement est fait par `JsonRpcPeer.receive()`
|
|
468
|
+
(`JsonRpcPeer.ts#JsonRpcPeer.receive`), qui **ne lève jamais**.
|
|
466
469
|
|
|
467
470
|
### `pair` (peer) — le moteur de protocole
|
|
468
471
|
|
|
@@ -470,22 +473,22 @@ Client et serveur sont des **pairs** : classer une frame, router, corréler les
|
|
|
470
473
|
travail des deux côtés. Nodefony l'écrit **une seule fois** ; chaque côté l'entoure de son transport
|
|
471
474
|
et de ses handlers. Aucune dépendance Node — le même fichier tourne dans le navigateur.
|
|
472
475
|
|
|
473
|
-
`JsonRpcPeer` (`JsonRpcPeer.ts
|
|
476
|
+
`JsonRpcPeer` (`JsonRpcPeer.ts#JsonRpcPeer`), contrat `IRealtimePeer` (`JsonRpcPeer.ts#IRealtimePeer`).
|
|
474
477
|
|
|
475
478
|
### `dispatch` — classer puis router
|
|
476
479
|
|
|
477
480
|
Le geste du pair sur une frame entrante : décider de sa nature, trouver le handler, l'appeler,
|
|
478
481
|
renvoyer la réponse s'il en faut une. Ce n'est pas une méthode publique — c'est un **moment**, et
|
|
479
482
|
c'est précisément là que se greffe le [verrou de frame](#verrou-de-frame--la-décision-par-frame),
|
|
480
|
-
par le crochet `beforeDispatch` (`JsonRpcPeer.ts
|
|
483
|
+
par le crochet `beforeDispatch` (`JsonRpcPeer.ts#JsonRpcPeerOptions.beforeDispatch`).
|
|
481
484
|
|
|
482
485
|
### `transport` — la couche octets
|
|
483
486
|
|
|
484
487
|
La techno réseau qui porte les frames, **interchangeable** : `WsConnectionTransport`
|
|
485
|
-
(`WsConnectionTransport.ts
|
|
488
|
+
(`WsConnectionTransport.ts#WsConnectionTransport`) côté serveur, `BrowserWsTransport` (`BrowserWsTransport.ts#BrowserWsTransport`) côté
|
|
486
489
|
navigateur. Le pair ignore laquelle est branchée.
|
|
487
490
|
|
|
488
|
-
Contrat `IRealtimeTransport` (`IRealtimeTransport.ts
|
|
491
|
+
Contrat `IRealtimeTransport` (`IRealtimeTransport.ts#IRealtimeTransport`). ⚠️ C'est **le seul** endroit du lexique où
|
|
489
492
|
« socket » pourrait désigner la connexion réseau : ici, ce mot est réservé à la
|
|
490
493
|
[prise applicative](#socket--la-prise-que-tient-ton-code).
|
|
491
494
|
|
|
@@ -495,7 +498,7 @@ Quand un handler veut joindre des métadonnées serveur à sa réponse, il rend
|
|
|
495
498
|
`result` reste **exactement** ce que rendrait la même route en REST, et la méta voyage dans un champ
|
|
496
499
|
frère. Un pair qui n'en connaît pas les clés les ignore.
|
|
497
500
|
|
|
498
|
-
`RpcEnvelope` (`JsonRpcPeer.ts
|
|
501
|
+
`RpcEnvelope` (`JsonRpcPeer.ts#RpcEnvelope`), `RpcMeta` (`JsonRpcPeer.ts#RpcMeta`).
|
|
499
502
|
|
|
500
503
|
### `cadence` — la fréquence portée par le nom
|
|
501
504
|
|
|
@@ -503,7 +506,7 @@ La fréquence d'un canal d'état vit **dans son nom** : `nodefony:orm:health` =
|
|
|
503
506
|
`nodefony:orm:health:2000` = un rythme explicite. Conséquence voulue : **un canal, une cadence, un
|
|
504
507
|
comptage** — deux cadences sont deux canaux, jamais réconciliés.
|
|
505
508
|
|
|
506
|
-
`rateChannel()` (`channelRate.ts
|
|
509
|
+
`rateChannel()` (`channelRate.ts#rateChannel`) côté client, `parseRate()` (`channelRate.ts#parseRate`) côté serveur —
|
|
507
510
|
même fichier, donc même règle des deux côtés.
|
|
508
511
|
|
|
509
512
|
### `AIMD` — s'ajuster au client lent
|
|
@@ -512,8 +515,8 @@ _Additive Increase, Multiplicative Decrease_ : accélérer par petits pas quand
|
|
|
512
515
|
d'un coup à la moindre congestion. Le principe de régulation de TCP, appliqué **par canal** à partir
|
|
513
516
|
de ce qui reste en attente dans la connexion.
|
|
514
517
|
|
|
515
|
-
`AdaptiveRate` (`AdaptiveRate.ts
|
|
516
|
-
(`AdaptiveRate.ts
|
|
518
|
+
`AdaptiveRate` (`AdaptiveRate.ts#AdaptiveRate`), liaison sur un canal par `bindAdaptiveChannel()`
|
|
519
|
+
(`AdaptiveRate.ts#bindAdaptiveChannel`).
|
|
517
520
|
|
|
518
521
|
## 🧩 Le cluster — franchir la frontière du process
|
|
519
522
|
|
|
@@ -535,8 +538,8 @@ Le **fond de panier** d'un rack : la carte qui relie toutes les autres. Ici, le
|
|
|
535
538
|
publications d'un process aux autres. Un contrat volontairement minuscule — `publish`, `onMessage`,
|
|
536
539
|
`start`, `stop`, `describe` — parce que tout ce qui est riche appartient au hub.
|
|
537
540
|
|
|
538
|
-
`IBackplane` (`IBackplane.ts
|
|
539
|
-
`RealtimeHub.setBackplane()` (`RealtimeHub.ts
|
|
541
|
+
`IBackplane` (`IBackplane.ts#IBackplane`), message `IBackplaneMessage` (`IBackplane.ts#IBackplaneMessage`), branchement
|
|
542
|
+
`RealtimeHub.setBackplane()` (`RealtimeHub.ts#RealtimeHub.setBackplane`).
|
|
540
543
|
|
|
541
544
|
> [!TIP]
|
|
542
545
|
> C'est la pièce qui tient la promesse « une ligne de configuration change tout » : le hub **ne sait
|
|
@@ -545,11 +548,11 @@ publications d'un process aux autres. Un contrat volontairement minuscule — `p
|
|
|
545
548
|
### `driver` — l'implémentation du transport
|
|
546
549
|
|
|
547
550
|
Le backplane est un **contrat** ; un driver en est une réalisation, désignée par son **nom** dans la
|
|
548
|
-
configuration (`backplane.driver`, `config.ts
|
|
551
|
+
configuration (`backplane.driver`, `config.ts#backplaneSchema`). Un registre associe nom → fabrique : aucune
|
|
549
552
|
cascade de conditions, et n'importe qui peut enregistrer le sien.
|
|
550
553
|
|
|
551
|
-
`registerBackplaneDriver()` (`backplaneRegistry.ts
|
|
552
|
-
(`backplaneRegistry.ts
|
|
554
|
+
`registerBackplaneDriver()` (`backplaneRegistry.ts#registerBackplaneDriver`), `listBackplaneDrivers()`
|
|
555
|
+
(`backplaneRegistry.ts#listBackplaneDrivers`). Un nom inconnu au démarrage n'arrête rien : le hub reste local, avec un
|
|
553
556
|
avertissement.
|
|
554
557
|
|
|
555
558
|
### `loopback` — un seul process
|
|
@@ -557,14 +560,14 @@ avertissement.
|
|
|
557
560
|
Le driver du cas mono-process : il n'y a **rien à transporter**, le hub fait tout localement. C'est
|
|
558
561
|
le défaut, et il ne coûte rien.
|
|
559
562
|
|
|
560
|
-
`LoopbackBackplane` (`LoopbackBackplane.ts
|
|
563
|
+
`LoopbackBackplane` (`LoopbackBackplane.ts#LoopbackBackplane`).
|
|
561
564
|
|
|
562
565
|
### `cluster` — entre workers d'une machine
|
|
563
566
|
|
|
564
567
|
Le driver qui relie les workers d'un même hôte par le canal de communication inter-process de Node.
|
|
565
568
|
Multi-process sans aucune infrastructure à installer.
|
|
566
569
|
|
|
567
|
-
`ClusterBackplane` (`ClusterBackplane.ts
|
|
570
|
+
`ClusterBackplane` (`ClusterBackplane.ts#ClusterBackplane`).
|
|
568
571
|
|
|
569
572
|
### `redis` — entre machines
|
|
570
573
|
|
|
@@ -572,7 +575,7 @@ Le driver qui propage les publications en pub/sub, donc entre pods de machines d
|
|
|
572
575
|
consomme les connexions du module Redis — jamais une dépendance directe. Absent ou injoignable, le
|
|
573
576
|
hub reste local et le démarrage continue.
|
|
574
577
|
|
|
575
|
-
`RedisBackplane` (`RedisBackplane.ts
|
|
578
|
+
`RedisBackplane` (`RedisBackplane.ts#RedisBackplane`), canal `resolveRedisChannel()` (`RedisBackplane.ts#resolveRedisChannel`).
|
|
576
579
|
|
|
577
580
|
### `origine` (originId) — qui a émis
|
|
578
581
|
|
|
@@ -580,7 +583,7 @@ L'identifiant **unique dans le cluster** du process émetteur, joint à chaque m
|
|
|
580
583
|
du nom du pod ou de l'hôte, jamais du seul identifiant de processus : deux conteneurs peuvent tous
|
|
581
584
|
deux être le processus n° 1.
|
|
582
585
|
|
|
583
|
-
`resolveBackplaneOriginId()` (`originId.ts
|
|
586
|
+
`resolveBackplaneOriginId()` (`originId.ts#resolveBackplaneOriginId`), champ `IBackplane.originId` (`IBackplane.ts#IBackplaneInfo.kind`).
|
|
584
587
|
|
|
585
588
|
### `anti-écho` — ne pas rejouer sa propre voix
|
|
586
589
|
|
|
@@ -588,7 +591,7 @@ La règle qui empêche les tempêtes : un message **reçu** du backplane est ré
|
|
|
588
591
|
**locale seulement**, jamais renvoyé sur le bus ; et un message qui porte sa propre origine est
|
|
589
592
|
ignoré à l'arrivée. Sans l'origine, l'anti-écho ferait taire le voisin au lieu de soi-même.
|
|
590
593
|
|
|
591
|
-
`RealtimeHub.publishLocal()` (`RealtimeHub.ts
|
|
594
|
+
`RealtimeHub.publishLocal()` (`RealtimeHub.ts#RealtimeHub.publishLocal`).
|
|
592
595
|
|
|
593
596
|
### `cloison` (namespace) — deux applications, un même bus
|
|
594
597
|
|
|
@@ -596,8 +599,8 @@ Le suffixe qui isole les canaux d'une application sur un transport mutualisé. N
|
|
|
596
599
|
numéro de base d'un serveur Redis **ne cloisonne pas** le pub/sub : sans cloison, deux déploiements
|
|
597
600
|
partageant le même serveur se parleraient.
|
|
598
601
|
|
|
599
|
-
`backplane.namespace` (`config.ts
|
|
600
|
-
(`RedisBackplane.ts
|
|
602
|
+
`backplane.namespace` (`config.ts#backplaneSchema`), constante de base `REDIS_RT_CHANNEL`
|
|
603
|
+
(`RedisBackplane.ts#REDIS_RT_CHANNEL`).
|
|
601
604
|
|
|
602
605
|
### `cross-pod` — la propriété qui compte
|
|
603
606
|
|
|
@@ -605,7 +608,7 @@ L'information que chaque driver déclare sur lui-même : sait-il franchir la mac
|
|
|
605
608
|
dans la carte d'identité du backplane, journalisée au démarrage et lisible dans la sonde — c'est ce
|
|
606
609
|
qui permet de vérifier en une ligne qu'un déploiement diffuse bien au-delà d'un pod.
|
|
607
610
|
|
|
608
|
-
`IBackplaneInfo` (`IBackplane.ts
|
|
611
|
+
`IBackplaneInfo` (`IBackplane.ts#IBackplaneInfo`).
|
|
609
612
|
|
|
610
613
|
## 🔐 La sécurité — qui parle, qui a le droit
|
|
611
614
|
|
|
@@ -630,8 +633,8 @@ Le moment de l'ouverture : c'est **là**, et une seule fois, que l'origine est c
|
|
|
630
633
|
l'authenticator tourne et que l'identité est figée. Tout ce qui coûte cher se paie ici, jamais par
|
|
631
634
|
frame.
|
|
632
635
|
|
|
633
|
-
DTO neutre `IRealtimeHandshake` (`IRealtimeHandshake.ts
|
|
634
|
-
sous-protocoles. Traitement dans `onHandshake()` (`RealtimeController.ts
|
|
636
|
+
DTO neutre `IRealtimeHandshake` (`IRealtimeHandshake.ts#IRealtimeHandshake`) — en-têtes, cookies, URL, origine,
|
|
637
|
+
sous-protocoles. Traitement dans `onHandshake()` (`RealtimeController.ts#RealtimeController.onHandshake`).
|
|
635
638
|
|
|
636
639
|
### `authenticator` — du handshake à l'identité
|
|
637
640
|
|
|
@@ -639,24 +642,24 @@ La stratégie qui transforme une poignée de main en identité : `supports` (est
|
|
|
639
642
|
`authenticate` (voici le jeton), `onSuccess` / `onFailure`. Exactement le motif des authenticators
|
|
640
643
|
HTTP — un seul modèle mental pour les deux transports.
|
|
641
644
|
|
|
642
|
-
`IRealtimeAuthenticator` (`IRealtimeAuthenticator.ts
|
|
643
|
-
`RealtimeHub.useAuthenticator()` (`RealtimeHub.ts
|
|
645
|
+
`IRealtimeAuthenticator` (`IRealtimeAuthenticator.ts#IRealtimeAuthenticator`), enregistrement
|
|
646
|
+
`RealtimeHub.useAuthenticator()` (`RealtimeHub.ts#RealtimeHub.useAuthenticator`).
|
|
644
647
|
|
|
645
648
|
### `matcher` — qui capture quoi
|
|
646
649
|
|
|
647
650
|
Le sélecteur (motif d'URL, hôte optionnel) qui décide **quel** authenticator prend la connexion. Le
|
|
648
651
|
**premier** qui capture gagne : on enregistre donc du plus spécifique au plus général.
|
|
649
652
|
|
|
650
|
-
`IRealtimeAuthenticatorMatcher` (`IRealtimeAuthenticatorMatcher.ts
|
|
651
|
-
`resolveAuthenticator()` (`RealtimeHub.ts
|
|
653
|
+
`IRealtimeAuthenticatorMatcher` (`IRealtimeAuthenticatorMatcher.ts#IRealtimeAuthenticatorMatcher`), résolution
|
|
654
|
+
`resolveAuthenticator()` (`RealtimeHub.ts#RealtimeHub.resolveAuthenticator`).
|
|
652
655
|
|
|
653
656
|
### `jeton` (token) — l'identité figée
|
|
654
657
|
|
|
655
658
|
Ce que l'authenticator produit : type, identifiant, rôles, scopes, et parfois une méthode de
|
|
656
659
|
re-validation. Il est mis en cache sur la connexion et **jamais** recalculé par frame.
|
|
657
660
|
|
|
658
|
-
`IRealtimeToken` (`IRealtimeToken.ts
|
|
659
|
-
c'est le jeton anonyme gelé `ANONYMOUS_REALTIME_TOKEN` (`AnonymousRealtimeToken.ts
|
|
661
|
+
`IRealtimeToken` (`IRealtimeToken.ts#IRealtimeToken`). La lecture ne rend **jamais** `null` : sans authenticator,
|
|
662
|
+
c'est le jeton anonyme gelé `ANONYMOUS_REALTIME_TOKEN` (`AnonymousRealtimeToken.ts#ANONYMOUS_REALTIME_TOKEN`) qui répond —
|
|
660
663
|
un refus a donc toujours un auteur identifiable.
|
|
661
664
|
|
|
662
665
|
### `politique de canal` — ce qu'un canal exige
|
|
@@ -664,8 +667,8 @@ un refus a donc toujours un auteur identifiable.
|
|
|
664
667
|
Les exigences déclarées d'un canal : authentifié, rôles, scopes. Elles s'attachent au **nom** du
|
|
665
668
|
canal, pas à la méthode qui l'implémente — parce que c'est le nom que le pare-feu résout.
|
|
666
669
|
|
|
667
|
-
`IChannelPolicy` (`IChannelPolicy.ts
|
|
668
|
-
(`realtimeDecorators.ts
|
|
670
|
+
`IChannelPolicy` (`IChannelPolicy.ts#IChannelPolicy`), déclaration par `@RealtimeChannel`
|
|
671
|
+
(`realtimeDecorators.ts#RealtimeChannel`), résolution `resolveChannelPolicy()` (`RealtimeHub.ts#RealtimeHub.resolveChannelPolicy`).
|
|
669
672
|
|
|
670
673
|
### `verrou de frame` — la décision par frame
|
|
671
674
|
|
|
@@ -673,8 +676,8 @@ La fonction qui répond « cette frame passe-t-elle ? » à partir du jeton **d
|
|
|
673
676
|
**Synchrone par doctrine** : attendre une réponse distante à chaque frame sérialiserait le flux d'une
|
|
674
677
|
connexion. Un contrôle qui doit interroger le réseau se fait au handshake, pas ici.
|
|
675
678
|
|
|
676
|
-
`FrameAuthorizer` (`RealtimeHub.ts
|
|
677
|
-
`runAuthorizer()` (`RealtimeHub.ts
|
|
679
|
+
`FrameAuthorizer` (`RealtimeHub.ts#FrameAuthorizer`), pose `setFrameAuthorizer()` (`RealtimeHub.ts#RealtimeHub.setFrameAuthorizer`), appel
|
|
680
|
+
`runAuthorizer()` (`RealtimeHub.ts#RealtimeHub.runAuthorizer`).
|
|
678
681
|
|
|
679
682
|
### `contrôle d'origine` — la défense d'ouverture
|
|
680
683
|
|
|
@@ -682,7 +685,7 @@ Le contrôle de l'en-tête `Origin` à l'ouverture (RFC 6455 §10.2) : un site t
|
|
|
682
685
|
ouvrir une connexion authentifiée dans le dos de l'utilisateur. Correspondance **exacte**, aucun
|
|
683
686
|
caractère générique.
|
|
684
687
|
|
|
685
|
-
`OriginGuard` (`RealtimeHub.ts
|
|
688
|
+
`OriginGuard` (`RealtimeHub.ts#OriginGuard`), configuration `csrf.checkOrigin` (`config.ts#csrfSchema`).
|
|
686
689
|
|
|
687
690
|
### `révocation` — la connexion qui survit à sa session
|
|
688
691
|
|
|
@@ -690,8 +693,8 @@ Le problème que ce mot nomme : une connexion ouverte pourrait survivre à la se
|
|
|
690
693
|
autorisée, puisque le verrou de frame ne relit rien. Un contrôle périodique referme l'écart en
|
|
691
694
|
re-validant les identités révocables et en fermant celles qui ne valent plus.
|
|
692
695
|
|
|
693
|
-
`registerRevocable()` (`RealtimeHub.ts
|
|
694
|
-
`REVOCATION_REVALIDATE_MS` (`RealtimeHub.ts
|
|
696
|
+
`registerRevocable()` (`RealtimeHub.ts#RealtimeHub.registerRevocable`), `revalidateRevocable()` (`RealtimeHub.ts#RealtimeHub.revalidateRevocable`), période
|
|
697
|
+
`REVOCATION_REVALIDATE_MS` (`RealtimeHub.ts#channelFamily`). Une re-validation en erreur **ferme** — jamais
|
|
695
698
|
l'inverse.
|
|
696
699
|
|
|
697
700
|
### `refus` (denied) — rendre le « non » visible
|
|
@@ -700,8 +703,8 @@ Une notification refusée serait abandonnée en silence, laissant le client aveu
|
|
|
700
703
|
donc `realtime:denied`. Le motif est **générique** : jamais le rôle ou le scope manquant, qui
|
|
701
704
|
transformerait le refus en oracle d'autorisation.
|
|
702
705
|
|
|
703
|
-
`IRealtimeDenied` (`RealtimeEventMap.ts
|
|
704
|
-
(`notice.ts
|
|
706
|
+
`IRealtimeDenied` (`RealtimeEventMap.ts#IRealtimeDenied`), conversion en message d'interface `deniedToNotice()`
|
|
707
|
+
(`notice.ts#deniedToNotice`).
|
|
705
708
|
|
|
706
709
|
### `audit de frame` — tracer le notable
|
|
707
710
|
|
|
@@ -709,7 +712,7 @@ Le signal émis sur les événements protocolaires qui méritent une trace : fra
|
|
|
709
712
|
refusée, méthode inconnue, erreur interne. Émis sans attente, avec le pair — ce qui permet de
|
|
710
713
|
retrouver **qui** a été refusé, pas seulement d'où venait le paquet.
|
|
711
714
|
|
|
712
|
-
`FrameAuditReason` (`JsonRpcPeer.ts
|
|
715
|
+
`FrameAuditReason` (`JsonRpcPeer.ts#FrameAuditReason`), crochet `onFrameAudit` (`JsonRpcPeer.ts#JsonRpcPeerOptions.onFrameAudit`).
|
|
713
716
|
|
|
714
717
|
### `seam` — le point de greffe
|
|
715
718
|
|
|
@@ -717,8 +720,8 @@ Littéralement une **couture** : un point prévu dans une couche basse pour qu'u
|
|
|
717
720
|
greffe du comportement **sans modifier la couche basse**. Le module en expose cinq, et c'est ce qui
|
|
718
721
|
permet à la couche sécurité de se brancher sans qu'aucune ligne de realtime ne la connaisse.
|
|
719
722
|
|
|
720
|
-
Les deux du protocole sont `beforeDispatch` (`JsonRpcPeer.ts
|
|
721
|
-
(`JsonRpcPeer.ts
|
|
723
|
+
Les deux du protocole sont `beforeDispatch` (`JsonRpcPeer.ts#JsonRpcPeerOptions.beforeDispatch`) et `onFrameAudit`
|
|
724
|
+
(`JsonRpcPeer.ts#JsonRpcPeerOptions.onFrameAudit`) ; les trois du hub sont l'authenticator, le matcher et le contrôle d'origine.
|
|
722
725
|
|
|
723
726
|
→ [Sécurité](./securite.md) pour le détail de chacun.
|
|
724
727
|
|
|
@@ -728,8 +731,8 @@ Le nombre maximal de canaux qu'**une** connexion peut ouvrir. Chaque canal ouver
|
|
|
728
731
|
ressources ; sans borne, une seule connexion peut les épuiser. Au-delà, l'abonnement est refusé et le
|
|
729
732
|
client en est informé.
|
|
730
733
|
|
|
731
|
-
`limits.maxChannelsPerConnection` (`config.ts
|
|
732
|
-
(`RealtimeHub.ts
|
|
734
|
+
`limits.maxChannelsPerConnection` (`config.ts#limitsSchema`), pose `setMaxChannelsPerConnection()`
|
|
735
|
+
(`RealtimeHub.ts#RealtimeHub.setMaxChannelsPerConnection`).
|
|
733
736
|
|
|
734
737
|
## 📡 L'observabilité — ce que la socket dit d'elle-même
|
|
735
738
|
|
|
@@ -748,7 +751,7 @@ L'oscilloscope du module : « voici mon état, maintenant ». Canaux et abonnés
|
|
|
748
751
|
livraisons, connexions, octets, back-pressure, carte d'identité du backplane. Lecture **pure**, sans
|
|
749
752
|
allocation sur le chemin chaud.
|
|
750
753
|
|
|
751
|
-
`RealtimeHub.probe()` (`RealtimeHub.ts
|
|
754
|
+
`RealtimeHub.probe()` (`RealtimeHub.ts#RealtimeHub.probe`) rend un `IRealtimeProbe` (`IRealtimeProbe.ts#IRealtimeProbe`).
|
|
752
755
|
|
|
753
756
|
> [!WARNING]
|
|
754
757
|
> `IRealtimeProbe` est la **forme des données**, pas une interface à implémenter avec une méthode
|
|
@@ -760,34 +763,34 @@ La quantité d'octets déjà remise au transport mais pas encore partie. C'est l
|
|
|
760
763
|
n° 1 : il monte quand le client n'absorbe plus. Deux seuils distincts existent — un pour **compter**
|
|
761
764
|
(la sonde), un pour **agir** (abandonner des frames, puis fermer).
|
|
762
765
|
|
|
763
|
-
Seuil de comptage `SLOW_CONSUMER_BYTES` (`RealtimeHub.ts
|
|
766
|
+
Seuil de comptage `SLOW_CONSUMER_BYTES` (`RealtimeHub.ts#SLOW_CONSUMER_BYTES`) — une constante, car il ne
|
|
764
767
|
décide de rien. Les seuils d'ACTION, eux, sont des réglages : `websocket.maxBackpressure`,
|
|
765
768
|
`websocket.backpressurePolicy` et `websocket.backpressureCloseAfterDrops` (`http`,
|
|
766
|
-
`http/nodefony/config/config.ts
|
|
769
|
+
`http/nodefony/config/config.ts#httpConfigSchema`).
|
|
767
770
|
|
|
768
771
|
### `consommateur lent` — la connexion qui décroche
|
|
769
772
|
|
|
770
773
|
Une connexion dont le back-pressure dépasse le seuil de comptage. C'est une **métrique**, pas une
|
|
771
774
|
sanction : le seuil de comptage se règle indépendamment des seuils d'action.
|
|
772
775
|
|
|
773
|
-
`slowConsumer.bytes` (`config.ts
|
|
774
|
-
(`IRealtimeProbe.ts
|
|
776
|
+
`slowConsumer.bytes` (`config.ts#slowConsumerSchema`), champ `backpressure.slowConsumers`
|
|
777
|
+
(`IRealtimeProbe.ts#IRealtimeProbe.backpressure`).
|
|
775
778
|
|
|
776
779
|
### `compteurs de canal` — mesurés à l'arrivée
|
|
777
780
|
|
|
778
781
|
Nombre de messages, date du dernier, débit instantané, historique glissant — par canal, calculés au
|
|
779
782
|
point d'arrivée des frames, donc réutilisables par n'importe quelle application.
|
|
780
783
|
|
|
781
|
-
`IChannelStats` (`IRealtimeSocket.ts
|
|
782
|
-
(`IRealtimeProbe.ts
|
|
784
|
+
`IChannelStats` (`IRealtimeSocket.ts#IChannelStats.method`), côté serveur `IRealtimeChannelStat`
|
|
785
|
+
(`IRealtimeProbe.ts#IRealtimeChannelStat`).
|
|
783
786
|
|
|
784
787
|
### `santé agrégée` — la vue du pod entier
|
|
785
788
|
|
|
786
789
|
En multi-worker, chaque process a **sa** sonde. La vue du pod est leur fusion : cumuls additionnés,
|
|
787
790
|
maxima repris au plus haut, instances listées une à une.
|
|
788
791
|
|
|
789
|
-
`mergeClusterHealth()` (`ClusterProbeClient.ts
|
|
790
|
-
(`IRealtimeProbe.ts
|
|
792
|
+
`mergeClusterHealth()` (`ClusterProbeClient.ts#mergeClusterHealth`), forme `IRealtimeClusterHealth`
|
|
793
|
+
(`IRealtimeProbe.ts#IRealtimeClusterHealth`).
|
|
791
794
|
|
|
792
795
|
### `plan de données admin` — la sonde servie à Studio
|
|
793
796
|
|
|
@@ -795,23 +798,23 @@ Le producteur qui expose la sonde sous `/nodefony/realtime/api/health`, selon la
|
|
|
795
798
|
routage des administrations de modules. C'est de l'**auto-observabilité** : la socket se regarde
|
|
796
799
|
elle-même par le même chemin que les autres modules.
|
|
797
800
|
|
|
798
|
-
`createRealtimeAdminApi()` (`RealtimeAdminApi.ts
|
|
799
|
-
(`RealtimeAdminApi.ts
|
|
801
|
+
`createRealtimeAdminApi()` (`RealtimeAdminApi.ts#createRealtimeAdminApi`), construction `buildOwnHealth()`
|
|
802
|
+
(`RealtimeAdminApi.ts#buildOwnHealth`).
|
|
800
803
|
|
|
801
804
|
## ⚠️ Pièges — les faux-amis du vocabulaire
|
|
802
805
|
|
|
803
|
-
| Symptôme | Cause — le mot pris pour un autre | Correction
|
|
804
|
-
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
|
|
805
|
-
| « Je suis abonné mais je ne reçois rien » | `subscribe` confondu avec `on` : le flux est demandé, aucun écouteur ne le lit | Les deux : `subscribe(canal)` **et** `on(canal, handler)` (`IRealtimeSocket.ts
|
|
806
|
-
| « Ça marche en local, plus rien dès qu'on passe à plusieurs pods » | canal resté **instance-local** — le défaut. Traverser le process est une capacité qu'on **demande** | Déclarer le préfixe dans `@RealtimeBroadcast` (`realtimeDecorators.ts
|
|
807
|
-
| « Le client publie, le serveur ignore » | canal non déclaré **entrant**. Un client ne peut rien pousser tant qu'aucun handler n'existe | `@RealtimeInbound("mon:canal")` (`realtimeDecorators.ts
|
|
808
|
-
| Deux déploiements se mélangent sur un même serveur Redis | pas de **cloison** — le numéro de base ne cloisonne pas le pub/sub | Poser `backplane.namespace` (`config.ts
|
|
809
|
-
| Le fan-out disparaît entre conteneurs identiques | **origine** dérivée du seul identifiant de processus : deux conteneurs sont tous deux le n° 1, l'anti-écho avale tout | `resolveBackplaneOriginId()` (`originId.ts
|
|
810
|
-
| Deux tickers pour le même tableau de bord | **cadence** différente = **canal** différent, jamais réconcilié | Fabriquer le nom via `rateChannel()` (`channelRate.ts
|
|
811
|
-
| Un canal se croit protégé mais laisse tout passer | **politique** déclarée sans **verrou de frame** posé pour la faire respecter | Le hub le détecte et avertit : `hasUnenforcedChannelPolicies()` (`RealtimeHub.ts
|
|
812
|
-
| Une connexion garde ses flux après une déconnexion applicative | le **verrou de frame** ne relit rien : par construction, il ne voit pas la session mourir | Inscrire l'identité au registre de **révocation** (`RealtimeHub.ts
|
|
813
|
-
| « Le provider a planté après la fermeture d'un onglet » | **provider** confondu avec **sink** : le provider est partagé, il survit à la connexion créatrice | N'y capturer que des dépendances à longue vie (`ChannelFactory`, `RealtimeHub.ts
|
|
814
|
-
| « Notre store realtime est indisponible » | **store** employé pour **driver** : le backplane ne conserve rien, il fait passer | Dire « le driver de backplane » — les données ont des stores, les flux ont des drivers
|
|
806
|
+
| Symptôme | Cause — le mot pris pour un autre | Correction |
|
|
807
|
+
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
808
|
+
| « Je suis abonné mais je ne reçois rien » | `subscribe` confondu avec `on` : le flux est demandé, aucun écouteur ne le lit | Les deux : `subscribe(canal)` **et** `on(canal, handler)` (`IRealtimeSocket.ts#IRealtimeSocket.subscribe`) |
|
|
809
|
+
| « Ça marche en local, plus rien dès qu'on passe à plusieurs pods » | canal resté **instance-local** — le défaut. Traverser le process est une capacité qu'on **demande** | Déclarer le préfixe dans `@RealtimeBroadcast` (`realtimeDecorators.ts#RealtimeBroadcast`) |
|
|
810
|
+
| « Le client publie, le serveur ignore » | canal non déclaré **entrant**. Un client ne peut rien pousser tant qu'aucun handler n'existe | `@RealtimeInbound("mon:canal")` (`realtimeDecorators.ts#RealtimeInbound`) |
|
|
811
|
+
| Deux déploiements se mélangent sur un même serveur Redis | pas de **cloison** — le numéro de base ne cloisonne pas le pub/sub | Poser `backplane.namespace` (`config.ts#backplaneSchema`) |
|
|
812
|
+
| Le fan-out disparaît entre conteneurs identiques | **origine** dérivée du seul identifiant de processus : deux conteneurs sont tous deux le n° 1, l'anti-écho avale tout | `resolveBackplaneOriginId()` (`originId.ts#resolveBackplaneOriginId`) dérive du pod ou de l'hôte |
|
|
813
|
+
| Deux tickers pour le même tableau de bord | **cadence** différente = **canal** différent, jamais réconcilié | Fabriquer le nom via `rateChannel()` (`channelRate.ts#rateChannel`) des deux côtés |
|
|
814
|
+
| Un canal se croit protégé mais laisse tout passer | **politique** déclarée sans **verrou de frame** posé pour la faire respecter | Le hub le détecte et avertit : `hasUnenforcedChannelPolicies()` (`RealtimeHub.ts#RealtimeHub.hasUnenforcedChannelPolicies`) |
|
|
815
|
+
| Une connexion garde ses flux après une déconnexion applicative | le **verrou de frame** ne relit rien : par construction, il ne voit pas la session mourir | Inscrire l'identité au registre de **révocation** (`RealtimeHub.ts#RealtimeHub.subscribeClient`) |
|
|
816
|
+
| « Le provider a planté après la fermeture d'un onglet » | **provider** confondu avec **sink** : le provider est partagé, il survit à la connexion créatrice | N'y capturer que des dépendances à longue vie (`ChannelFactory`, `RealtimeHub.ts#ChannelFactory`) |
|
|
817
|
+
| « Notre store realtime est indisponible » | **store** employé pour **driver** : le backplane ne conserve rien, il fait passer | Dire « le driver de backplane » — les données ont des stores, les flux ont des drivers |
|
|
815
818
|
|
|
816
819
|
> [!CAUTION]
|
|
817
820
|
> Deux mots à ne jamais intervertir en réunion : le **hub** tombe et **un** pod perd sa diffusion ;
|