@nodefony/realtime 10.0.0-beta.2 → 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.
Files changed (63) hide show
  1. package/README.md +2 -2
  2. package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
  3. package/dist/index.js +18 -13
  4. package/dist/nodefony/config/config.js +7 -5
  5. package/dist/nodefony/config/configSchema.js +18 -0
  6. package/dist/nodefony/config/defineModuleConfig.js +43 -17
  7. package/dist/nodefony/decorators/realtimeDecorators.js +69 -19
  8. package/dist/nodefony/interfaces/IRealtimeConnectionState.js +1 -0
  9. package/dist/nodefony/src/backplane/ClusterBackplane.js +1 -2
  10. package/dist/nodefony/src/backplane/LoopbackBackplane.js +1 -2
  11. package/dist/nodefony/src/backplane/RedisBackplane.js +22 -6
  12. package/dist/nodefony/src/backplane/backplaneRegistry.js +1 -2
  13. package/dist/nodefony/src/backplane/bootNotices.js +42 -0
  14. package/dist/nodefony/src/backplane/envelope.js +1 -2
  15. package/dist/nodefony/src/backplane/originId.js +1 -2
  16. package/dist/nodefony/src/backplane/publishQueue.js +1 -5
  17. package/dist/nodefony/src/cluster/ClusterProbeClient.js +16 -6
  18. package/dist/nodefony/src/guards.js +40 -0
  19. package/dist/nodefony/src/server/AnonymousRealtimeToken.js +2 -6
  20. package/dist/nodefony/src/server/RealtimeAdminApi.js +1 -2
  21. package/dist/nodefony/src/server/RealtimeController.js +226 -82
  22. package/dist/nodefony/src/server/RealtimeHub.js +32 -54
  23. package/dist/nodefony/src/server/ServerRealtimeSocket.js +30 -13
  24. package/dist/nodefony/src/server/deniedDetail.js +1 -2
  25. package/dist/nodefony/src/server/subscribedAck.js +29 -0
  26. package/dist/nodefony/src/server/syslogUplink.js +5 -4
  27. package/dist/nodefony/src/server/welcomeEnv.js +1 -2
  28. package/dist/nodefony/src/service/RealtimeService.js +4 -4
  29. package/dist/nodefony/src/transport/WsConnectionTransport.js +42 -2
  30. package/dist/nodefony/testing/index.js +118 -5
  31. package/dist/types/index.d.ts +1 -0
  32. package/dist/types/nodefony/config/configSchema.d.ts +12 -0
  33. package/dist/types/nodefony/config/defineModuleConfig.d.ts +29 -6
  34. package/dist/types/nodefony/decorators/realtimeDecorators.d.ts +19 -2
  35. package/dist/types/nodefony/interfaces/IRealtimeConnectionState.d.ts +40 -0
  36. package/dist/types/nodefony/interfaces/IRealtimeController.d.ts +1 -1
  37. package/dist/types/nodefony/interfaces/IRealtimeHandshake.d.ts +1 -1
  38. package/dist/types/nodefony/interfaces/IRealtimeToken.d.ts +13 -0
  39. package/dist/types/nodefony/interfaces/index.d.ts +1 -0
  40. package/dist/types/nodefony/src/backplane/RedisBackplane.d.ts +7 -0
  41. package/dist/types/nodefony/src/backplane/bootNotices.d.ts +25 -0
  42. package/dist/types/nodefony/src/guards.d.ts +32 -0
  43. package/dist/types/nodefony/src/server/AnonymousRealtimeToken.d.ts +13 -0
  44. package/dist/types/nodefony/src/server/RealtimeController.d.ts +82 -4
  45. package/dist/types/nodefony/src/server/RealtimeHub.d.ts +22 -16
  46. package/dist/types/nodefony/src/server/ServerRealtimeSocket.d.ts +18 -8
  47. package/dist/types/nodefony/src/server/subscribedAck.d.ts +23 -0
  48. package/dist/types/nodefony/src/service/RealtimeService.d.ts +1 -1
  49. package/dist/types/nodefony/src/transport/WsConnectionTransport.d.ts +26 -1
  50. package/dist/types/nodefony/testing/index.d.ts +8 -3
  51. package/docs/actions.md +129 -45
  52. package/docs/architecture.md +143 -135
  53. package/docs/cadence-et-contre-pression.md +643 -0
  54. package/docs/configuration.md +126 -97
  55. package/docs/cookbook-chat.md +50 -43
  56. package/docs/index.md +63 -38
  57. package/docs/observabilite.md +58 -56
  58. package/docs/protocole.md +110 -76
  59. package/docs/securite.md +128 -117
  60. package/docs/vocabulaire.md +133 -129
  61. package/package.json +17 -13
  62. package/dist/_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js +0 -6
  63. package/docs/couverture-bancs.md +0 -139
@@ -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:122`) décrit la
116
- prise **côté navigateur** (`RealtimeClient`, `RealtimeClient.ts:194`) **et côté serveur**
117
- (`ServerRealtimeSocket`, `ServerRealtimeSocket.ts:44`). Un service back publie exactement comme une
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:87`).
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:144`) et porte une route WebSocket : HTTP et WebSocket
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:276`) de rejouer sur la socket
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
@@ -179,10 +179,10 @@ export class ChatController extends RealtimeController {
179
179
  Côté client — la même **socket**, les quatre verbes :
180
180
 
181
181
  ```ts
182
- import { RealtimeClient } from "nodefony/client";
182
+ import { NodefonySocket } from "nodefony/client";
183
183
 
184
184
  export async function joinChat(): Promise<void> {
185
- const socket = new RealtimeClient({ url: "wss://127.0.0.1:5152/chat" });
185
+ const socket = new NodefonySocket({ url: "wss://127.0.0.1:5152/chat" });
186
186
  await socket.connect();
187
187
 
188
188
  socket.subscribe("chat:room-42"); // DEMANDE le flux au pair
@@ -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:122`), implémenté côté navigateur par
221
- `RealtimeClient` (`RealtimeClient.ts:194`) et côté serveur par `ServerRealtimeSocket`
222
- (`ServerRealtimeSocket.ts:44`). ⚠️ La socket **n'est pas** le [transport](#transport--la-couche-octets).
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:462`) l'indexe ; `IRealtimeSocket.subscribe()`
233
- (`IRealtimeSocket.ts:132`) le demande. Plusieurs canaux cohabitent sur **une** connexion : c'est le
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:132`). Le plafond par connexion est réglable
242
- (`limits.maxChannelsPerConnection`, `config.ts:142`) — au-delà, le canal n'est pas ouvert.
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:141`).
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:160`), `RealtimeHub.publish()` (`RealtimeHub.ts:604`).
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:101`), appelée
264
- côté client par `request()` (`IRealtimeSocket.ts:166`).
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:70`) choisit ce qu'elle expose au pair.
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:231`) ou
273
- par l'override `realtimeInbound()` (`RealtimeController.ts:262`).
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:16`).
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:87`), `channel()` (`IRealtimeSocket.ts:178`).
287
+ `IRealtimeChannel` (`IRealtimeSocket.ts#IRealtimeChannel.name`), `channel()` (`IRealtimeSocket.ts#IRealtimeSocket.channel`).
285
288
 
286
289
  ### `client` — la socket côté navigateur
287
290
 
288
- `RealtimeClient` (`RealtimeClient.ts:161`) : reconnexion automatique, réémission des abonnements,
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:44`), obtenue par `serverSocket()`
297
- (`ServerRealtimeSocket.ts:223`) : un service métier tient un handle et publie **comme une page
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:329`).
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:231`).
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:212`).
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:1451`). Il ne connaît **ni** les
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:213`). ⚠️ « hub » désigne **toujours** le serveur ; ce que tient le
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:604`) pour le chemin complet, `publishLocal()`
350
- (`RealtimeHub.ts:623`) pour la diffusion **strictement locale** (voie d'entrée du backplane).
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:155`). Le nombre de sinks d'un canal est son nombre d'abonnés locaux.
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:170`). ⚠️ Un provider est **partagé** : il survit à la connexion qui
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:462`).
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:455`) pour l'écoute, `isChannelOwned()`
389
- (`RealtimeHub.ts:550`) pour lire l'état. ⚠️ Un canal passif **n'est pas** un canal fermé : il n'a
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:1297`).
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:668`), déclaré côté contrôleur par
407
- `@RealtimeBroadcast` (`realtimeDecorators.ts:342`).
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:393`).
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:144`), point d'entrée `handleRealtime()`
423
- (`RealtimeController.ts:299`). C'est un **contrôleur** au sens habituel de Nodefony : la même classe
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:48`).
435
+ `RealtimeService` (`RealtimeService.ts#RealtimeService`).
433
436
 
434
437
  ### `pont API` — la même action, sur la socket
435
438
 
@@ -437,31 +440,32 @@ 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:276`), mise en œuvre `invokeApiRequest()`
441
- (`RealtimeController.ts:949`). Désactivé par défaut.
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
 
445
- | Terme | En une ligne |
446
- | -------------- | ---------------------------------------------------------- |
447
- | `frame` | l'unité atomique qui passe sur le fil |
448
- | `pair` | le moteur de protocole, identique client et serveur |
449
- | `requête` | frame avec `method` **et** `id` — appelle une réponse |
450
- | `notification` | frame avec `method` seul — sans réponse |
451
- | `réponse` | frame avec `id` seul — résout une requête en attente |
452
- | `dispatch` | classer une frame entrante puis la router vers son handler |
453
- | `transport` | la couche octets, interchangeable |
454
- | `enveloppe` | joindre des métadonnées serveur à côté du résultat |
455
- | `cadence` | la fréquence d'un canal d'état, portée par son nom |
456
- | `AIMD` | l'auto-ajustement de cadence face à un client lent |
448
+ | Terme | En une ligne |
449
+ | -------------- | ----------------------------------------------------------- |
450
+ | `frame` | l'unité atomique qui passe sur le fil |
451
+ | `pair` | le moteur de protocole, identique client et serveur |
452
+ | `requête` | frame avec `method` **et** `id` — appelle une réponse |
453
+ | `notification` | frame avec `method` seul — sans réponse |
454
+ | `réponse` | `id` + `result` ou `error`, sans `method` — résout un appel |
455
+ | `dispatch` | classer une frame entrante puis la router vers son handler |
456
+ | `transport` | la couche octets, interchangeable |
457
+ | `enveloppe` | joindre des métadonnées serveur à côté du résultat |
458
+ | `cadence` | la fréquence d'un canal d'état, portée par son nom |
459
+ | `AIMD` | l'auto-ajustement de cadence face à un client lent |
457
460
 
458
461
  ### `frame` — l'unité qui passe sur le fil
459
462
 
460
463
  Une enveloppe JSON-RPC 2.0. Sa **nature se lit sur `method`**, pas sur `id` : `method` + `id` =
461
- requête, `method` seul = notification, `id` seul = réponse. Tout le reste est invalide.
464
+ requête, `method` seul = notification, `id` + `result` ou `error` = réponse. Tout le reste est
465
+ invalide.
462
466
 
463
- `JsonRpcFrameKind` (`JsonRpcPeer.ts:133`). Le classement est fait par `JsonRpcPeer.receive()`
464
- (`JsonRpcPeer.ts:390`), qui **ne lève jamais**.
467
+ `JsonRpcFrameKind` (`jsonrpc/index.ts#JsonRpcFrameKind`). Le classement est fait par `JsonRpcPeer.receive()`
468
+ (`JsonRpcPeer.ts#JsonRpcPeer.receive`), qui **ne lève jamais**.
465
469
 
466
470
  ### `pair` (peer) — le moteur de protocole
467
471
 
@@ -469,22 +473,22 @@ Client et serveur sont des **pairs** : classer une frame, router, corréler les
469
473
  travail des deux côtés. Nodefony l'écrit **une seule fois** ; chaque côté l'entoure de son transport
470
474
  et de ses handlers. Aucune dépendance Node — le même fichier tourne dans le navigateur.
471
475
 
472
- `JsonRpcPeer` (`JsonRpcPeer.ts:280`), contrat `IRealtimePeer` (`JsonRpcPeer.ts:213`).
476
+ `JsonRpcPeer` (`JsonRpcPeer.ts#JsonRpcPeer`), contrat `IRealtimePeer` (`JsonRpcPeer.ts#IRealtimePeer`).
473
477
 
474
478
  ### `dispatch` — classer puis router
475
479
 
476
480
  Le geste du pair sur une frame entrante : décider de sa nature, trouver le handler, l'appeler,
477
481
  renvoyer la réponse s'il en faut une. Ce n'est pas une méthode publique — c'est un **moment**, et
478
482
  c'est précisément là que se greffe le [verrou de frame](#verrou-de-frame--la-décision-par-frame),
479
- par le crochet `beforeDispatch` (`JsonRpcPeer.ts:192`).
483
+ par le crochet `beforeDispatch` (`JsonRpcPeer.ts#JsonRpcPeerOptions.beforeDispatch`).
480
484
 
481
485
  ### `transport` — la couche octets
482
486
 
483
487
  La techno réseau qui porte les frames, **interchangeable** : `WsConnectionTransport`
484
- (`WsConnectionTransport.ts:46`) côté serveur, `BrowserWsTransport` (`BrowserWsTransport.ts:12`) côté
488
+ (`WsConnectionTransport.ts#WsConnectionTransport`) côté serveur, `BrowserWsTransport` (`BrowserWsTransport.ts#BrowserWsTransport`) côté
485
489
  navigateur. Le pair ignore laquelle est branchée.
486
490
 
487
- Contrat `IRealtimeTransport` (`IRealtimeTransport.ts:34`). ⚠️ C'est **le seul** endroit du lexique où
491
+ Contrat `IRealtimeTransport` (`IRealtimeTransport.ts#IRealtimeTransport`). ⚠️ C'est **le seul** endroit du lexique où
488
492
  « socket » pourrait désigner la connexion réseau : ici, ce mot est réservé à la
489
493
  [prise applicative](#socket--la-prise-que-tient-ton-code).
490
494
 
@@ -494,7 +498,7 @@ Quand un handler veut joindre des métadonnées serveur à sa réponse, il rend
494
498
  `result` reste **exactement** ce que rendrait la même route en REST, et la méta voyage dans un champ
495
499
  frère. Un pair qui n'en connaît pas les clés les ignore.
496
500
 
497
- `RpcEnvelope` (`JsonRpcPeer.ts:104`), `RpcMeta` (`JsonRpcPeer.ts:90`).
501
+ `RpcEnvelope` (`JsonRpcPeer.ts#RpcEnvelope`), `RpcMeta` (`JsonRpcPeer.ts#RpcMeta`).
498
502
 
499
503
  ### `cadence` — la fréquence portée par le nom
500
504
 
@@ -502,7 +506,7 @@ La fréquence d'un canal d'état vit **dans son nom** : `nodefony:orm:health` =
502
506
  `nodefony:orm:health:2000` = un rythme explicite. Conséquence voulue : **un canal, une cadence, un
503
507
  comptage** — deux cadences sont deux canaux, jamais réconciliés.
504
508
 
505
- `rateChannel()` (`channelRate.ts:44`) côté client, `parseRate()` (`channelRate.ts:63`) côté serveur —
509
+ `rateChannel()` (`channelRate.ts#rateChannel`) côté client, `parseRate()` (`channelRate.ts#parseRate`) côté serveur —
506
510
  même fichier, donc même règle des deux côtés.
507
511
 
508
512
  ### `AIMD` — s'ajuster au client lent
@@ -511,8 +515,8 @@ _Additive Increase, Multiplicative Decrease_ : accélérer par petits pas quand
511
515
  d'un coup à la moindre congestion. Le principe de régulation de TCP, appliqué **par canal** à partir
512
516
  de ce qui reste en attente dans la connexion.
513
517
 
514
- `AdaptiveRate` (`AdaptiveRate.ts:78`), liaison sur un canal par `bindAdaptiveChannel()`
515
- (`AdaptiveRate.ts:239`).
518
+ `AdaptiveRate` (`AdaptiveRate.ts#AdaptiveRate`), liaison sur un canal par `bindAdaptiveChannel()`
519
+ (`AdaptiveRate.ts#bindAdaptiveChannel`).
516
520
 
517
521
  ## 🧩 Le cluster — franchir la frontière du process
518
522
 
@@ -534,8 +538,8 @@ Le **fond de panier** d'un rack : la carte qui relie toutes les autres. Ici, le
534
538
  publications d'un process aux autres. Un contrat volontairement minuscule — `publish`, `onMessage`,
535
539
  `start`, `stop`, `describe` — parce que tout ce qui est riche appartient au hub.
536
540
 
537
- `IBackplane` (`IBackplane.ts:107`), message `IBackplaneMessage` (`IBackplane.ts:51`), branchement
538
- `RealtimeHub.setBackplane()` (`RealtimeHub.ts:714`).
541
+ `IBackplane` (`IBackplane.ts#IBackplane`), message `IBackplaneMessage` (`IBackplane.ts#IBackplaneMessage`), branchement
542
+ `RealtimeHub.setBackplane()` (`RealtimeHub.ts#RealtimeHub.setBackplane`).
539
543
 
540
544
  > [!TIP]
541
545
  > C'est la pièce qui tient la promesse « une ligne de configuration change tout » : le hub **ne sait
@@ -544,11 +548,11 @@ publications d'un process aux autres. Un contrat volontairement minuscule — `p
544
548
  ### `driver` — l'implémentation du transport
545
549
 
546
550
  Le backplane est un **contrat** ; un driver en est une réalisation, désignée par son **nom** dans la
547
- configuration (`backplane.driver`, `config.ts:47`). Un registre associe nom → fabrique : aucune
551
+ configuration (`backplane.driver`, `config.ts#backplaneSchema`). Un registre associe nom → fabrique : aucune
548
552
  cascade de conditions, et n'importe qui peut enregistrer le sien.
549
553
 
550
- `registerBackplaneDriver()` (`backplaneRegistry.ts:55`), `listBackplaneDrivers()`
551
- (`backplaneRegistry.ts:68`). Un nom inconnu au démarrage n'arrête rien : le hub reste local, avec un
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
552
556
  avertissement.
553
557
 
554
558
  ### `loopback` — un seul process
@@ -556,14 +560,14 @@ avertissement.
556
560
  Le driver du cas mono-process : il n'y a **rien à transporter**, le hub fait tout localement. C'est
557
561
  le défaut, et il ne coûte rien.
558
562
 
559
- `LoopbackBackplane` (`LoopbackBackplane.ts:24`).
563
+ `LoopbackBackplane` (`LoopbackBackplane.ts#LoopbackBackplane`).
560
564
 
561
565
  ### `cluster` — entre workers d'une machine
562
566
 
563
567
  Le driver qui relie les workers d'un même hôte par le canal de communication inter-process de Node.
564
568
  Multi-process sans aucune infrastructure à installer.
565
569
 
566
- `ClusterBackplane` (`ClusterBackplane.ts:89`).
570
+ `ClusterBackplane` (`ClusterBackplane.ts#ClusterBackplane`).
567
571
 
568
572
  ### `redis` — entre machines
569
573
 
@@ -571,7 +575,7 @@ Le driver qui propage les publications en pub/sub, donc entre pods de machines d
571
575
  consomme les connexions du module Redis — jamais une dépendance directe. Absent ou injoignable, le
572
576
  hub reste local et le démarrage continue.
573
577
 
574
- `RedisBackplane` (`RedisBackplane.ts:193`), canal `resolveRedisChannel()` (`RedisBackplane.ts:31`).
578
+ `RedisBackplane` (`RedisBackplane.ts#RedisBackplane`), canal `resolveRedisChannel()` (`RedisBackplane.ts#resolveRedisChannel`).
575
579
 
576
580
  ### `origine` (originId) — qui a émis
577
581
 
@@ -579,7 +583,7 @@ L'identifiant **unique dans le cluster** du process émetteur, joint à chaque m
579
583
  du nom du pod ou de l'hôte, jamais du seul identifiant de processus : deux conteneurs peuvent tous
580
584
  deux être le processus n° 1.
581
585
 
582
- `resolveBackplaneOriginId()` (`originId.ts:24`), champ `IBackplane.originId` (`IBackplane.ts:77`).
586
+ `resolveBackplaneOriginId()` (`originId.ts#resolveBackplaneOriginId`), champ `IBackplane.originId` (`IBackplane.ts#IBackplaneInfo.kind`).
583
587
 
584
588
  ### `anti-écho` — ne pas rejouer sa propre voix
585
589
 
@@ -587,7 +591,7 @@ La règle qui empêche les tempêtes : un message **reçu** du backplane est ré
587
591
  **locale seulement**, jamais renvoyé sur le bus ; et un message qui porte sa propre origine est
588
592
  ignoré à l'arrivée. Sans l'origine, l'anti-écho ferait taire le voisin au lieu de soi-même.
589
593
 
590
- `RealtimeHub.publishLocal()` (`RealtimeHub.ts:623`).
594
+ `RealtimeHub.publishLocal()` (`RealtimeHub.ts#RealtimeHub.publishLocal`).
591
595
 
592
596
  ### `cloison` (namespace) — deux applications, un même bus
593
597
 
@@ -595,8 +599,8 @@ Le suffixe qui isole les canaux d'une application sur un transport mutualisé. N
595
599
  numéro de base d'un serveur Redis **ne cloisonne pas** le pub/sub : sans cloison, deux déploiements
596
600
  partageant le même serveur se parleraient.
597
601
 
598
- `backplane.namespace` (`config.ts:59`), constante de base `REDIS_RT_CHANNEL`
599
- (`RedisBackplane.ts:20`).
602
+ `backplane.namespace` (`config.ts#backplaneSchema`), constante de base `REDIS_RT_CHANNEL`
603
+ (`RedisBackplane.ts#REDIS_RT_CHANNEL`).
600
604
 
601
605
  ### `cross-pod` — la propriété qui compte
602
606
 
@@ -604,7 +608,7 @@ L'information que chaque driver déclare sur lui-même : sait-il franchir la mac
604
608
  dans la carte d'identité du backplane, journalisée au démarrage et lisible dans la sonde — c'est ce
605
609
  qui permet de vérifier en une ligne qu'un déploiement diffuse bien au-delà d'un pod.
606
610
 
607
- `IBackplaneInfo` (`IBackplane.ts:74`).
611
+ `IBackplaneInfo` (`IBackplane.ts#IBackplaneInfo`).
608
612
 
609
613
  ## 🔐 La sécurité — qui parle, qui a le droit
610
614
 
@@ -629,8 +633,8 @@ Le moment de l'ouverture : c'est **là**, et une seule fois, que l'origine est c
629
633
  l'authenticator tourne et que l'identité est figée. Tout ce qui coûte cher se paie ici, jamais par
630
634
  frame.
631
635
 
632
- DTO neutre `IRealtimeHandshake` (`IRealtimeHandshake.ts:14`) — en-têtes, cookies, URL, origine,
633
- sous-protocoles. Traitement dans `onHandshake()` (`RealtimeController.ts:374`).
636
+ DTO neutre `IRealtimeHandshake` (`IRealtimeHandshake.ts#IRealtimeHandshake`) — en-têtes, cookies, URL, origine,
637
+ sous-protocoles. Traitement dans `onHandshake()` (`RealtimeController.ts#RealtimeController.onHandshake`).
634
638
 
635
639
  ### `authenticator` — du handshake à l'identité
636
640
 
@@ -638,24 +642,24 @@ La stratégie qui transforme une poignée de main en identité : `supports` (est
638
642
  `authenticate` (voici le jeton), `onSuccess` / `onFailure`. Exactement le motif des authenticators
639
643
  HTTP — un seul modèle mental pour les deux transports.
640
644
 
641
- `IRealtimeAuthenticator` (`IRealtimeAuthenticator.ts:24`), enregistrement
642
- `RealtimeHub.useAuthenticator()` (`RealtimeHub.ts:943`).
645
+ `IRealtimeAuthenticator` (`IRealtimeAuthenticator.ts#IRealtimeAuthenticator`), enregistrement
646
+ `RealtimeHub.useAuthenticator()` (`RealtimeHub.ts#RealtimeHub.useAuthenticator`).
643
647
 
644
648
  ### `matcher` — qui capture quoi
645
649
 
646
650
  Le sélecteur (motif d'URL, hôte optionnel) qui décide **quel** authenticator prend la connexion. Le
647
651
  **premier** qui capture gagne : on enregistre donc du plus spécifique au plus général.
648
652
 
649
- `IRealtimeAuthenticatorMatcher` (`IRealtimeAuthenticatorMatcher.ts:25`), résolution
650
- `resolveAuthenticator()` (`RealtimeHub.ts:961`).
653
+ `IRealtimeAuthenticatorMatcher` (`IRealtimeAuthenticatorMatcher.ts#IRealtimeAuthenticatorMatcher`), résolution
654
+ `resolveAuthenticator()` (`RealtimeHub.ts#RealtimeHub.resolveAuthenticator`).
651
655
 
652
656
  ### `jeton` (token) — l'identité figée
653
657
 
654
658
  Ce que l'authenticator produit : type, identifiant, rôles, scopes, et parfois une méthode de
655
659
  re-validation. Il est mis en cache sur la connexion et **jamais** recalculé par frame.
656
660
 
657
- `IRealtimeToken` (`IRealtimeToken.ts:18`). La lecture ne rend **jamais** `null` : sans authenticator,
658
- c'est le jeton anonyme gelé `ANONYMOUS_REALTIME_TOKEN` (`AnonymousRealtimeToken.ts:18`) qui répond —
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 —
659
663
  un refus a donc toujours un auteur identifiable.
660
664
 
661
665
  ### `politique de canal` — ce qu'un canal exige
@@ -663,8 +667,8 @@ un refus a donc toujours un auteur identifiable.
663
667
  Les exigences déclarées d'un canal : authentifié, rôles, scopes. Elles s'attachent au **nom** du
664
668
  canal, pas à la méthode qui l'implémente — parce que c'est le nom que le pare-feu résout.
665
669
 
666
- `IChannelPolicy` (`IChannelPolicy.ts:20`), déclaration par `@RealtimeChannel`
667
- (`realtimeDecorators.ts:192`), résolution `resolveChannelPolicy()` (`RealtimeHub.ts:1228`).
670
+ `IChannelPolicy` (`IChannelPolicy.ts#IChannelPolicy`), déclaration par `@RealtimeChannel`
671
+ (`realtimeDecorators.ts#RealtimeChannel`), résolution `resolveChannelPolicy()` (`RealtimeHub.ts#RealtimeHub.resolveChannelPolicy`).
668
672
 
669
673
  ### `verrou de frame` — la décision par frame
670
674
 
@@ -672,8 +676,8 @@ La fonction qui répond « cette frame passe-t-elle ? » à partir du jeton **d
672
676
  **Synchrone par doctrine** : attendre une réponse distante à chaque frame sérialiserait le flux d'une
673
677
  connexion. Un contrôle qui doit interroger le réseau se fait au handshake, pas ici.
674
678
 
675
- `FrameAuthorizer` (`RealtimeHub.ts:39`), pose `setFrameAuthorizer()` (`RealtimeHub.ts:1039`), appel
676
- `runAuthorizer()` (`RealtimeHub.ts:1328`).
679
+ `FrameAuthorizer` (`RealtimeHub.ts#FrameAuthorizer`), pose `setFrameAuthorizer()` (`RealtimeHub.ts#RealtimeHub.setFrameAuthorizer`), appel
680
+ `runAuthorizer()` (`RealtimeHub.ts#RealtimeHub.runAuthorizer`).
677
681
 
678
682
  ### `contrôle d'origine` — la défense d'ouverture
679
683
 
@@ -681,7 +685,7 @@ Le contrôle de l'en-tête `Origin` à l'ouverture (RFC 6455 §10.2) : un site t
681
685
  ouvrir une connexion authentifiée dans le dos de l'utilisateur. Correspondance **exacte**, aucun
682
686
  caractère générique.
683
687
 
684
- `OriginGuard` (`RealtimeHub.ts:33`), configuration `csrf.checkOrigin` (`config.ts:255`).
688
+ `OriginGuard` (`RealtimeHub.ts#OriginGuard`), configuration `csrf.checkOrigin` (`config.ts#csrfSchema`).
685
689
 
686
690
  ### `révocation` — la connexion qui survit à sa session
687
691
 
@@ -689,8 +693,8 @@ Le problème que ce mot nomme : une connexion ouverte pourrait survivre à la se
689
693
  autorisée, puisque le verrou de frame ne relit rien. Un contrôle périodique referme l'écart en
690
694
  re-validant les identités révocables et en fermant celles qui ne valent plus.
691
695
 
692
- `registerRevocable()` (`RealtimeHub.ts:794`), `revalidateRevocable()` (`RealtimeHub.ts:826`), période
693
- `REVOCATION_REVALIDATE_MS` (`RealtimeHub.ts:111`). Une re-validation en erreur **ferme** — jamais
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
694
698
  l'inverse.
695
699
 
696
700
  ### `refus` (denied) — rendre le « non » visible
@@ -699,8 +703,8 @@ Une notification refusée serait abandonnée en silence, laissant le client aveu
699
703
  donc `realtime:denied`. Le motif est **générique** : jamais le rôle ou le scope manquant, qui
700
704
  transformerait le refus en oracle d'autorisation.
701
705
 
702
- `IRealtimeDenied` (`RealtimeEventMap.ts:269`), conversion en message d'interface `deniedToNotice()`
703
- (`notice.ts:45`).
706
+ `IRealtimeDenied` (`RealtimeEventMap.ts#IRealtimeDenied`), conversion en message d'interface `deniedToNotice()`
707
+ (`notice.ts#deniedToNotice`).
704
708
 
705
709
  ### `audit de frame` — tracer le notable
706
710
 
@@ -708,7 +712,7 @@ Le signal émis sur les événements protocolaires qui méritent une trace : fra
708
712
  refusée, méthode inconnue, erreur interne. Émis sans attente, avec le pair — ce qui permet de
709
713
  retrouver **qui** a été refusé, pas seulement d'où venait le paquet.
710
714
 
711
- `FrameAuditReason` (`JsonRpcPeer.ts:155`), crochet `onFrameAudit` (`JsonRpcPeer.ts:210`).
715
+ `FrameAuditReason` (`JsonRpcPeer.ts#FrameAuditReason`), crochet `onFrameAudit` (`JsonRpcPeer.ts#JsonRpcPeerOptions.onFrameAudit`).
712
716
 
713
717
  ### `seam` — le point de greffe
714
718
 
@@ -716,8 +720,8 @@ Littéralement une **couture** : un point prévu dans une couche basse pour qu'u
716
720
  greffe du comportement **sans modifier la couche basse**. Le module en expose cinq, et c'est ce qui
717
721
  permet à la couche sécurité de se brancher sans qu'aucune ligne de realtime ne la connaisse.
718
722
 
719
- Les deux du protocole sont `beforeDispatch` (`JsonRpcPeer.ts:192`) et `onFrameAudit`
720
- (`JsonRpcPeer.ts:210`) ; les trois du hub sont l'authenticator, le matcher et le contrôle d'origine.
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.
721
725
 
722
726
  → [Sécurité](./securite.md) pour le détail de chacun.
723
727
 
@@ -727,8 +731,8 @@ Le nombre maximal de canaux qu'**une** connexion peut ouvrir. Chaque canal ouver
727
731
  ressources ; sans borne, une seule connexion peut les épuiser. Au-delà, l'abonnement est refusé et le
728
732
  client en est informé.
729
733
 
730
- `limits.maxChannelsPerConnection` (`config.ts:142`), pose `setMaxChannelsPerConnection()`
731
- (`RealtimeHub.ts:995`).
734
+ `limits.maxChannelsPerConnection` (`config.ts#limitsSchema`), pose `setMaxChannelsPerConnection()`
735
+ (`RealtimeHub.ts#RealtimeHub.setMaxChannelsPerConnection`).
732
736
 
733
737
  ## 📡 L'observabilité — ce que la socket dit d'elle-même
734
738
 
@@ -747,7 +751,7 @@ L'oscilloscope du module : « voici mon état, maintenant ». Canaux et abonnés
747
751
  livraisons, connexions, octets, back-pressure, carte d'identité du backplane. Lecture **pure**, sans
748
752
  allocation sur le chemin chaud.
749
753
 
750
- `RealtimeHub.probe()` (`RealtimeHub.ts:865`) rend un `IRealtimeProbe` (`IRealtimeProbe.ts:61`).
754
+ `RealtimeHub.probe()` (`RealtimeHub.ts#RealtimeHub.probe`) rend un `IRealtimeProbe` (`IRealtimeProbe.ts#IRealtimeProbe`).
751
755
 
752
756
  > [!WARNING]
753
757
  > `IRealtimeProbe` est la **forme des données**, pas une interface à implémenter avec une méthode
@@ -759,34 +763,34 @@ La quantité d'octets déjà remise au transport mais pas encore partie. C'est l
759
763
  n° 1 : il monte quand le client n'absorbe plus. Deux seuils distincts existent — un pour **compter**
760
764
  (la sonde), un pour **agir** (abandonner des frames, puis fermer).
761
765
 
762
- Seuil de comptage `SLOW_CONSUMER_BYTES` (`RealtimeHub.ts:63`) — une constante, car il ne
766
+ Seuil de comptage `SLOW_CONSUMER_BYTES` (`RealtimeHub.ts#SLOW_CONSUMER_BYTES`) — une constante, car il ne
763
767
  décide de rien. Les seuils d'ACTION, eux, sont des réglages : `websocket.maxBackpressure`,
764
768
  `websocket.backpressurePolicy` et `websocket.backpressureCloseAfterDrops` (`http`,
765
- `http/nodefony/config/config.ts:1048`).
769
+ `http/nodefony/config/config.ts#httpConfigSchema`).
766
770
 
767
771
  ### `consommateur lent` — la connexion qui décroche
768
772
 
769
773
  Une connexion dont le back-pressure dépasse le seuil de comptage. C'est une **métrique**, pas une
770
774
  sanction : le seuil de comptage se règle indépendamment des seuils d'action.
771
775
 
772
- `slowConsumer.bytes` (`config.ts:121`), champ `backpressure.slowConsumers`
773
- (`IRealtimeProbe.ts:102`).
776
+ `slowConsumer.bytes` (`config.ts#slowConsumerSchema`), champ `backpressure.slowConsumers`
777
+ (`IRealtimeProbe.ts#IRealtimeProbe.backpressure`).
774
778
 
775
779
  ### `compteurs de canal` — mesurés à l'arrivée
776
780
 
777
781
  Nombre de messages, date du dernier, débit instantané, historique glissant — par canal, calculés au
778
782
  point d'arrivée des frames, donc réutilisables par n'importe quelle application.
779
783
 
780
- `IChannelStats` (`IRealtimeSocket.ts:64`), côté serveur `IRealtimeChannelStat`
781
- (`IRealtimeProbe.ts:47`).
784
+ `IChannelStats` (`IRealtimeSocket.ts#IChannelStats.method`), côté serveur `IRealtimeChannelStat`
785
+ (`IRealtimeProbe.ts#IRealtimeChannelStat`).
782
786
 
783
787
  ### `santé agrégée` — la vue du pod entier
784
788
 
785
789
  En multi-worker, chaque process a **sa** sonde. La vue du pod est leur fusion : cumuls additionnés,
786
790
  maxima repris au plus haut, instances listées une à une.
787
791
 
788
- `mergeClusterHealth()` (`ClusterProbeClient.ts:46`), forme `IRealtimeClusterHealth`
789
- (`IRealtimeProbe.ts:178`).
792
+ `mergeClusterHealth()` (`ClusterProbeClient.ts#mergeClusterHealth`), forme `IRealtimeClusterHealth`
793
+ (`IRealtimeProbe.ts#IRealtimeClusterHealth`).
790
794
 
791
795
  ### `plan de données admin` — la sonde servie à Studio
792
796
 
@@ -794,23 +798,23 @@ Le producteur qui expose la sonde sous `/nodefony/realtime/api/health`, selon la
794
798
  routage des administrations de modules. C'est de l'**auto-observabilité** : la socket se regarde
795
799
  elle-même par le même chemin que les autres modules.
796
800
 
797
- `createRealtimeAdminApi()` (`RealtimeAdminApi.ts:98`), construction `buildOwnHealth()`
798
- (`RealtimeAdminApi.ts:52`).
801
+ `createRealtimeAdminApi()` (`RealtimeAdminApi.ts#createRealtimeAdminApi`), construction `buildOwnHealth()`
802
+ (`RealtimeAdminApi.ts#buildOwnHealth`).
799
803
 
800
804
  ## ⚠️ Pièges — les faux-amis du vocabulaire
801
805
 
802
- | Symptôme | Cause — le mot pris pour un autre | Correction |
803
- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
804
- | « 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:131`) |
805
- | « Ç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:342`) |
806
- | « 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:231`) |
807
- | 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:59`) |
808
- | 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:24`) dérive du pod ou de l'hôte |
809
- | 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:44`) des deux côtés |
810
- | 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:1117`) |
811
- | 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:433`) |
812
- | « 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:170`) |
813
- | « 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 |
814
818
 
815
819
  > [!CAUTION]
816
820
  > Deux mots à ne jamais intervertir en réunion : le **hub** tombe et **un** pod perd sa diffusion ;