@nodefony/realtime 10.0.0-beta.4 → 10.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -10
- package/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.157.0}/helpers/esm/decorate.js +1 -1
- package/dist/index.js +79 -12
- package/dist/nodefony/config/config.js +7 -0
- package/dist/nodefony/decorators/realtimeDecorators.js +125 -19
- package/dist/nodefony/interfaces/IRealtimeConnection.js +1 -0
- package/dist/nodefony/src/backplane/ClusterBackplane.js +32 -4
- package/dist/nodefony/src/backplane/RedisBackplane.js +143 -31
- package/dist/nodefony/src/backplane/controlOrder.js +57 -0
- package/dist/nodefony/src/backplane/envelope.js +113 -12
- package/dist/nodefony/src/cluster/ClusterProbeClient.js +2 -0
- package/dist/nodefony/src/server/AnonymousRealtimeToken.js +23 -2
- package/dist/nodefony/src/server/ContractRealtimeController.js +75 -0
- package/dist/nodefony/src/server/RealtimeConnection.js +110 -0
- package/dist/nodefony/src/server/RealtimeController.js +344 -85
- package/dist/nodefony/src/server/RealtimeHub.js +604 -61
- package/dist/nodefony/src/server/ServerRealtimeSocket.js +2 -0
- package/dist/nodefony/src/server/admissionBudget.js +176 -0
- package/dist/nodefony/src/server/channelPolicyRegistry.js +73 -0
- package/dist/nodefony/src/server/connectionAdmission.js +242 -0
- package/dist/nodefony/src/server/contractRoutes.js +85 -0
- package/dist/nodefony/src/server/deniedDetail.js +10 -3
- package/dist/nodefony/src/server/frameAdmission.js +168 -0
- package/dist/nodefony/src/server/subscribedAck.js +10 -1
- package/dist/nodefony/src/server/syslogUplink.js +3 -4
- package/dist/nodefony/src/server/welcomeIdentity.js +37 -0
- package/dist/nodefony/src/service/RealtimeService.js +42 -1
- package/dist/nodefony/src/service/publicOrigin.js +40 -0
- package/dist/nodefony/src/transport/WsConnectionTransport.js +27 -4
- package/dist/nodefony/testing/backplaneConformance.js +271 -0
- package/dist/nodefony/testing/index.js +21 -4
- package/dist/types/index.d.ts +34 -7
- package/dist/types/nodefony/config/config.d.ts +9 -0
- package/dist/types/nodefony/decorators/realtimeDecorators.d.ts +86 -6
- package/dist/types/nodefony/interfaces/IBackplane.d.ts +34 -50
- package/dist/types/nodefony/interfaces/IChannelPolicy.d.ts +5 -25
- package/dist/types/nodefony/interfaces/IRealtimeConnection.d.ts +22 -0
- package/dist/types/nodefony/interfaces/IRealtimeConnectionState.d.ts +41 -2
- package/dist/types/nodefony/interfaces/IRealtimeController.d.ts +64 -3
- package/dist/types/nodefony/interfaces/IRealtimeProbe.d.ts +7 -166
- package/dist/types/nodefony/interfaces/IRealtimeToken.d.ts +21 -0
- package/dist/types/nodefony/interfaces/index.d.ts +2 -1
- package/dist/types/nodefony/src/backplane/ClusterBackplane.d.ts +22 -4
- package/dist/types/nodefony/src/backplane/RedisBackplane.d.ts +79 -16
- package/dist/types/nodefony/src/backplane/controlOrder.d.ts +52 -0
- package/dist/types/nodefony/src/backplane/envelope.d.ts +34 -0
- package/dist/types/nodefony/src/server/AnonymousRealtimeToken.d.ts +10 -0
- package/dist/types/nodefony/src/server/ContractRealtimeController.d.ts +138 -0
- package/dist/types/nodefony/src/server/RealtimeConnection.d.ts +17 -0
- package/dist/types/nodefony/src/server/RealtimeController.d.ts +93 -13
- package/dist/types/nodefony/src/server/RealtimeHub.d.ts +321 -24
- package/dist/types/nodefony/src/server/ServerRealtimeSocket.d.ts +1 -1
- package/dist/types/nodefony/src/server/admissionBudget.d.ts +71 -0
- package/dist/types/nodefony/src/server/channelPolicyRegistry.d.ts +34 -0
- package/dist/types/nodefony/src/server/connectionAdmission.d.ts +68 -0
- package/dist/types/nodefony/src/server/contractRoutes.d.ts +42 -0
- package/dist/types/nodefony/src/server/deniedDetail.d.ts +6 -0
- package/dist/types/nodefony/src/server/frameAdmission.d.ts +70 -0
- package/dist/types/nodefony/src/server/subscribedAck.d.ts +7 -0
- package/dist/types/nodefony/src/server/welcomeIdentity.d.ts +19 -0
- package/dist/types/nodefony/src/service/RealtimeService.d.ts +10 -2
- package/dist/types/nodefony/src/service/publicOrigin.d.ts +19 -0
- package/dist/types/nodefony/src/transport/WsConnectionTransport.d.ts +16 -30
- package/dist/types/nodefony/testing/backplaneConformance.d.ts +66 -0
- package/dist/types/nodefony/testing/index.d.ts +17 -4
- package/docs/actions.md +442 -19
- package/docs/architecture.md +83 -8
- package/docs/cadence-et-contre-pression.md +87 -11
- package/docs/canaux-a-membres.md +267 -0
- package/docs/cas-d-usage.md +1702 -0
- package/docs/configuration.md +137 -17
- package/docs/cookbook-chat.md +333 -85
- package/docs/index.md +11 -1
- package/docs/observabilite.md +71 -14
- package/docs/protocole.md +133 -24
- package/docs/securite.md +294 -33
- package/docs/vocabulaire.md +13 -12
- package/package.json +14 -11
|
@@ -25,22 +25,9 @@
|
|
|
25
25
|
*
|
|
26
26
|
* Cf vision « la socket Nodefony » + mémoire `project_cluster_backplane_vision`.
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* client réseau) grossit sans limite sous rafale — d'où le seuil et ses compteurs,
|
|
32
|
-
* exposés jusqu'à la sonde et Studio. Cf `BackplanePublishQueue`.
|
|
33
|
-
*/
|
|
34
|
-
export interface IBackplaneQueueInfo {
|
|
35
|
-
/** Octets publiés en attente d'acquittement par le transport (instantané). */
|
|
36
|
-
bytes: number;
|
|
37
|
-
/** Seuil au-dessus duquel une publication est jetée ; `0` = illimité. */
|
|
38
|
-
maxBytes: number;
|
|
39
|
-
/** Publications JETÉES faute de place (monotone). `0` en régime sain. */
|
|
40
|
-
droppedTotal: number;
|
|
41
|
-
/** Publications REFUSÉES par le transport (bus coupé…) (monotone). */
|
|
42
|
-
failedTotal: number;
|
|
43
|
-
}
|
|
28
|
+
import type { IFrameCodec } from "nodefony";
|
|
29
|
+
import type { IBackplaneInfo, IBackplaneQueueInfo } from "nodefony";
|
|
30
|
+
export type { IBackplaneInfo, IBackplaneQueueInfo };
|
|
44
31
|
/**
|
|
45
32
|
* Message transporté entre pairs par le backplane. Sérialisé une fois à l'émission
|
|
46
33
|
* (IPC `structuredClone` / Redis JSON) ; le `payload` doit donc être structurellement
|
|
@@ -60,44 +47,28 @@ export interface IBackplaneMessage {
|
|
|
60
47
|
* condition (mais en fan-out **local only**).
|
|
61
48
|
*/
|
|
62
49
|
export type BackplaneHandler = (msg: IBackplaneMessage) => void;
|
|
63
|
-
/**
|
|
64
|
-
* **Carte d'identité** d'un backplane — descripteur de DONNÉE (pas un log) décrivant
|
|
65
|
-
* l'état du fond de panier. Source unique alimentant trois sorties cohérentes : la
|
|
66
|
-
* ligne stdout au boot, le champ `backplane` de la sonde (`/api/health`) et la vue
|
|
67
|
-
* Studio. Réutilisable par les tests / un agent IA sans dupliquer la connaissance
|
|
68
|
-
* « c'est quoi ce backplane ».
|
|
69
|
-
*/
|
|
70
|
-
export interface IBackplaneInfo {
|
|
71
|
-
/** Nom du driver (clé du registre). Ex. `redis`, `cluster`, `loopback`. */
|
|
72
|
-
driver: string;
|
|
73
|
-
/** Nature du transport. Ex. `redis-pubsub`, `ipc`, `local`. */
|
|
74
|
-
kind: string;
|
|
75
|
-
/** Identité de CE pair (process/pod). Ex. `String(process.pid)`. */
|
|
76
|
-
originId: string;
|
|
77
|
-
/** `true` si le fan-out franchit les frontières de pod (multi-host). */
|
|
78
|
-
crossPod: boolean;
|
|
79
|
-
/** Canal/topic de transport, si applicable (canal Redis, topic Kafka). */
|
|
80
|
-
channel?: string;
|
|
81
|
-
/**
|
|
82
|
-
* Les messages sont-ils **scellés** (authenticité de l'émetteur vérifiée) ?
|
|
83
|
-
* Renseigné par les drivers à transport PARTAGÉ, où un tiers peut écrire :
|
|
84
|
-
* `false` y signale un bus ouvert. Absent quand la question n'a pas de sens
|
|
85
|
-
* (mono-process, IPC master↔workers — authentifié par construction).
|
|
86
|
-
*/
|
|
87
|
-
sealed?: boolean;
|
|
88
|
-
/**
|
|
89
|
-
* État de la **file d'envoi** vers le bus (octets en vol, seuil, jetés). Renseigné
|
|
90
|
-
* par les drivers à transport RÉSEAU, dont les publications sont acquittées de
|
|
91
|
-
* façon asynchrone : c'est là qu'un bus lent fait grossir la mémoire du pod.
|
|
92
|
-
* `droppedTotal > 0` = du fan-out cross-pod a été sacrifié pour tenir la mémoire.
|
|
93
|
-
* Absent quand le transport n'a pas de file (mono-process, IPC synchrone).
|
|
94
|
-
*/
|
|
95
|
-
queue?: IBackplaneQueueInfo;
|
|
96
|
-
}
|
|
97
50
|
/**
|
|
98
51
|
* Contrat d'un backplane realtime. Implémentation **stateless du point de vue métier**
|
|
99
52
|
* (l'état des canaux vit dans le hub) ; le backplane ne porte que le transport vers les
|
|
100
53
|
* pairs + son identité.
|
|
54
|
+
*
|
|
55
|
+
* Exigences d'un driver (ADR-0022 §6, décision 4) — aucune ne se voit à la
|
|
56
|
+
* compilation, toutes se perdent en silence :
|
|
57
|
+
*
|
|
58
|
+
* - **chaque pair reçoit TOUT** : sur un journal partitionné, un groupe de lecteurs
|
|
59
|
+
* PAR pair — un groupe partagé répartirait les messages entre serveurs ;
|
|
60
|
+
* - **l'ordre par émetteur** est tenu : clé de partition = `originId`, jamais le
|
|
61
|
+
* canal (ADR-0021 S5) ;
|
|
62
|
+
* - **aucun rejeu au démarrage** : lire à partir du dernier message, jamais depuis le
|
|
63
|
+
* début — un ordre de fin rejoué couperait une partie rouverte ;
|
|
64
|
+
* - **aucune attente de regroupement** à l'émission : `publish` part tout de suite ;
|
|
65
|
+
* - **le sceau appliqué** dès que le bus quitte la machine (`crossPod`) :
|
|
66
|
+
* `sealBackplaneEnvelope` à l'émission, `openBackplaneEnvelope` à la réception ;
|
|
67
|
+
* - `start` après `stop` remet la réception en route sans doubler les messages ;
|
|
68
|
+
* - la dépendance npm du transport vit dans le driver, jamais au cœur.
|
|
69
|
+
*
|
|
70
|
+
* Un driver s'y prouve par la suite de conformité, la même que jouent les drivers
|
|
71
|
+
* livrés : `runBackplaneConformance` de `@nodefony/realtime/testing`.
|
|
101
72
|
*/
|
|
102
73
|
export interface IBackplane {
|
|
103
74
|
/** Identité stable de CE pair (process/pod). Discrimine l'echo. Ex. `String(process.pid)`. */
|
|
@@ -117,6 +88,19 @@ export interface IBackplane {
|
|
|
117
88
|
* Un seul handler à la fois — un appel ultérieur remplace le précédent.
|
|
118
89
|
*/
|
|
119
90
|
onMessage(handler: BackplaneHandler): void;
|
|
91
|
+
/**
|
|
92
|
+
* Arme le transport des **octets** (ADR-0021 S6) : après un `true`, une charge qui
|
|
93
|
+
* est ou contient un `Uint8Array` traverse {@link publish} intacte jusqu'aux autres
|
|
94
|
+
* pairs. Appelé par le hub, une fois, quand le serveur a un codec binaire.
|
|
95
|
+
*
|
|
96
|
+
* Optionnel : un driver qui ne l'implémente pas — ou qui rend `false` — garde ses
|
|
97
|
+
* messages texte inchangés, et le hub retient les octets sur ce serveur en
|
|
98
|
+
* prévenant l'exploitant une fois, plutôt que de les laisser se déformer.
|
|
99
|
+
*
|
|
100
|
+
* @param codec - le codec binaire du serveur (`CBOR_FRAME_CODEC` de `nodefony/cbor`).
|
|
101
|
+
* @returns `true` si les octets traverseront le bus intacts.
|
|
102
|
+
*/
|
|
103
|
+
enableBytes?(codec: IFrameCodec): boolean;
|
|
120
104
|
/** Arrête le transport et libère les ressources (connexions, listeners). Idempotent. */
|
|
121
105
|
stop(): void | Promise<void>;
|
|
122
106
|
/**
|
|
@@ -1,28 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Politique d'autorisation d'un
|
|
3
|
-
* pour s'y abonner (`subscribe`) ou y pousser (canal inbound full-duplex).
|
|
2
|
+
* Politique d'autorisation d'un canal — déclaration UNIQUE, au cœur (`nodefony`).
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* frame via le seam `resolveChannelPolicy`. Le module realtime NE décide PAS de
|
|
9
|
-
* l'autorisation (il ne connaît ni la hiérarchie de rôles ni l'identité réelle) :
|
|
10
|
-
* il ne fait que **transporter la déclaration** jusqu'au décideur (le firewall).
|
|
11
|
-
*
|
|
12
|
-
* ⚠️ Cette interface est un **miroir structurel** de `IChannelPolicy` côté
|
|
13
|
-
* `@nodefony/security` (`realtimeContracts.ts`) — les deux modules ne s'importent
|
|
14
|
-
* pas (`package.json` disjoints). Le typage structurel de TS fait le pont à
|
|
15
|
-
* l'exécution. Toute dérive de forme casserait le seam silencieusement.
|
|
16
|
-
*
|
|
17
|
-
* Champs cumulatifs (ET) ; un champ absent = pas de contrainte sur cet axe ;
|
|
18
|
-
* objet entièrement vide = canal libre (équivaut à « pas de politique »).
|
|
4
|
+
* Réexportée ici pour que `import type { IChannelPolicy } from "@nodefony/realtime"`
|
|
5
|
+
* continue de compiler. Elle n'est plus recopiée : `@nodefony/security`, qui la
|
|
6
|
+
* juge, lit la même déclaration — deux copies dérivaient en silence.
|
|
19
7
|
*/
|
|
20
|
-
export
|
|
21
|
-
/** Exige une connexion authentifiée (token non anonyme). */
|
|
22
|
-
readonly authenticated?: boolean | undefined;
|
|
23
|
-
/** Un de ces rôles suffit (évalué AVEC la hiérarchie de rôles, côté security). */
|
|
24
|
-
readonly roles?: readonly string[] | undefined;
|
|
25
|
-
/** Un de ces scopes suffit (axe API : JWT/clé API ; session BFF n'en porte pas). */
|
|
26
|
-
readonly scopes?: readonly string[] | undefined;
|
|
27
|
-
}
|
|
28
|
-
export default IChannelPolicy;
|
|
8
|
+
export type { IChannelPolicy, IChannelPolicy as default } from "nodefony";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Une connexion temps réel servie par CE processus — la poignée qu'un
|
|
3
|
+
* gestionnaire `@RealtimeInbound` reçoit, et que les gestes du hub sur les
|
|
4
|
+
* membres d'un canal désignent (`RealtimeHub.subscribeConnection`,
|
|
5
|
+
* `RealtimeHub.evictConnection`, `RealtimeHub.channelMembers`).
|
|
6
|
+
*
|
|
7
|
+
* Elle n'existe que dans la mémoire du processus qui a accepté la socket : elle
|
|
8
|
+
* ne traverse pas le backplane, et un autre exemplaire (pod, `nodefony cluster
|
|
9
|
+
* -w N`) ne la connaît pas. Une poignée fermée, ou venue d'ailleurs, fait rendre
|
|
10
|
+
* `false` aux gestes du hub — jamais un geste muet.
|
|
11
|
+
*/
|
|
12
|
+
export interface IRealtimeConnection {
|
|
13
|
+
/**
|
|
14
|
+
* Identifiant du compte connecté (`RealtimeIdentity.id`) — la clé des canaux
|
|
15
|
+
* personnels et de `RealtimeHub.evictUser`, qui l'atteint sur tous les
|
|
16
|
+
* exemplaires ; `null` pour l'anonyme (qu'aucun ordre par compte n'atteint),
|
|
17
|
+
* et pour un authentificateur qui ne produit pas d'identité d'admission.
|
|
18
|
+
*/
|
|
19
|
+
readonly userId: string | null;
|
|
20
|
+
/** `false` une fois la socket fermée : la poignée ne désigne plus rien. */
|
|
21
|
+
readonly open: boolean;
|
|
22
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { JsonRpcPeer } from "nodefony";
|
|
1
|
+
import type { IFrameCodec, JsonRpcPeer } from "nodefony";
|
|
2
2
|
import type { WsConnectionTransport } from "../src/transport/WsConnectionTransport.js";
|
|
3
3
|
import type { ChannelSink } from "../src/server/RealtimeHub.js";
|
|
4
|
+
import type { RealtimeConnection } from "../src/server/RealtimeConnection.js";
|
|
5
|
+
import type { ConnectionAdmission } from "../src/server/connectionAdmission.js";
|
|
4
6
|
import type { RealtimeInboundHandler } from "./IRealtimeController.js";
|
|
5
7
|
/**
|
|
6
8
|
* État du protocole temps réel d'UNE connexion WebSocket — posé au welcome, et
|
|
@@ -22,10 +24,47 @@ export interface IRealtimeConnectionState {
|
|
|
22
24
|
peer: JsonRpcPeer;
|
|
23
25
|
/** Le transport qui relie le pair à la socket (contre-pression, sonde). */
|
|
24
26
|
transport: WsConnectionTransport;
|
|
27
|
+
/**
|
|
28
|
+
* Le codec BINAIRE accordé à cette connexion au handshake (ADR-0022 §2), `null`
|
|
29
|
+
* pour une connexion restée en texte. Non nul, toute trame binaire reçue est une
|
|
30
|
+
* frame du codec, et le flux brut (`onRealtimeBinary`, `sendBinaryToClient`) est
|
|
31
|
+
* refusé : les octets passent par les canaux, et leurs portes.
|
|
32
|
+
*/
|
|
33
|
+
binaryCodec: IFrameCodec | null;
|
|
25
34
|
/** Canal → sink de CETTE connexion auprès du hub partagé, pour s'en désabonner. */
|
|
26
35
|
channels: Map<string, ChannelSink>;
|
|
27
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Canaux full-duplex qui acceptent une entrée client ; `null` s'il n'y en a aucun.
|
|
38
|
+
* Objet ordinaire, de même forme pour toutes les connexions d'un contrôleur :
|
|
39
|
+
* un nom hérité de son prototype (`__proto__`, `toString`) est refusé par le
|
|
40
|
+
* verrou des trames avant d'atteindre la distribution.
|
|
41
|
+
*/
|
|
28
42
|
inbound: Record<string, RealtimeInboundHandler> | null;
|
|
43
|
+
/**
|
|
44
|
+
* File d'admission aux canaux à `allow` — `null` tant que la connexion n'a
|
|
45
|
+
* demandé aucun de ces canaux (la plupart ne le font jamais).
|
|
46
|
+
*/
|
|
47
|
+
admission: ConnectionAdmission | null;
|
|
48
|
+
/**
|
|
49
|
+
* Canaux entrants dont les trames passent sans le verrou : ouverts par `allow`,
|
|
50
|
+
* ou dont le verrou a déjà dit oui (ADR-0022 §6, décision 1 ter). Le coût d'une
|
|
51
|
+
* trame chaude tient dans cette lecture (`admitted[method] === true`). Mêmes
|
|
52
|
+
* clés que {@link inbound}, à `false` tant que non admises : une seule forme
|
|
53
|
+
* d'objet pour toutes les connexions d'un contrôleur. `null` jusqu'à la
|
|
54
|
+
* première admission.
|
|
55
|
+
*/
|
|
56
|
+
admitted: Record<string, boolean> | null;
|
|
57
|
+
/**
|
|
58
|
+
* Ceux de {@link admitted} que le VERROU a admis — au plus 64, vidés quand une
|
|
59
|
+
* politique arrive après coup. `null` s'il n'y en a aucun.
|
|
60
|
+
*/
|
|
61
|
+
retained: string[] | null;
|
|
62
|
+
/**
|
|
63
|
+
* Poignée publique de la connexion (membre d'un canal, argument des
|
|
64
|
+
* gestionnaires `@RealtimeInbound`) — `null` jusqu'au premier abonnement ou à
|
|
65
|
+
* la première trame entrante qui la demande.
|
|
66
|
+
*/
|
|
67
|
+
connection: RealtimeConnection | null;
|
|
29
68
|
}
|
|
30
69
|
declare module "@nodefony/http" {
|
|
31
70
|
interface IWebsocketConnectionState {
|
|
@@ -1,5 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { ContractServerEmit, EventNames, EventPayload, EventsMap, IRealtimeContract } from "nodefony";
|
|
2
|
+
import type { IRealtimeConnection } from "./IRealtimeConnection.js";
|
|
3
|
+
/**
|
|
4
|
+
* Publie une charge sur un canal (notification serveur→client) — forme LIBRE :
|
|
5
|
+
* tout nom, toute charge objet. Sous un contrat, préférer {@link ContractRealtimePublish}.
|
|
6
|
+
*/
|
|
7
|
+
export type RealtimePublish = (channel: string, payload?: object) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Publie sur un canal DÉCLARÉ par le contrat `C` : le nom est l'un des canaux de
|
|
10
|
+
* `serverToClient`, la charge est celle qu'il déclare. Une faute de frappe dans
|
|
11
|
+
* le nom ou dans la charge ne compile pas.
|
|
12
|
+
*
|
|
13
|
+
* C'est le `publish` qu'une fabrique de canal déclare sous un contrat — méthode
|
|
14
|
+
* `@RealtimeChannel` ou surcharge `createRealtimeChannel`. La fonction reçue à
|
|
15
|
+
* l'exécution est la même qu'en forme libre ({@link RealtimePublish}) : seul le
|
|
16
|
+
* compilateur voit la différence. La charge est un objet : le hub refuse un
|
|
17
|
+
* scalaire à l'exécution (`isRealtimePayload`), le type le dit avant.
|
|
18
|
+
*
|
|
19
|
+
* @typeParam C - le contrat de l'application.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* @RealtimeChannel("chat:presence", { authenticated: true })
|
|
24
|
+
* presence(_channel: string, publish: ContractRealtimePublish<ChatContract>) {
|
|
25
|
+
* publish("chat:presence", { online: 3 });
|
|
26
|
+
* return () => undefined;
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export type ContractRealtimePublish<C extends IRealtimeContract> = <K extends EventNames<ContractServerEmit<C>>>(channel: K, payload: EventPayload<ContractServerEmit<C>, K> & object) => void;
|
|
3
31
|
/**
|
|
4
32
|
* Handler d'un message ENTRANT sur un canal full-duplex (client → serveur). C'est le
|
|
5
33
|
* seam des backings entrants (SIP, bridge) : le client `publish(channel, payload)` ; si
|
|
@@ -9,10 +37,43 @@ export type RealtimePublish = (channel: string, payload: unknown) => void;
|
|
|
9
37
|
* valider (origine non authentifiée au niveau transport).
|
|
10
38
|
* - `reply` = pousse une charge serveur→client sur le MÊME canal, vers CETTE connexion
|
|
11
39
|
* uniquement (ex. réponse SIP). Per-connexion (≠ fan-out).
|
|
40
|
+
* - `connection` = la connexion émettrice, locale à ce processus : de quoi l'abonner
|
|
41
|
+
* d'office à un canal (`RealtimeHub.subscribeConnection`) ou l'en éjecter. Un
|
|
42
|
+
* gestionnaire qui n'en a pas l'usage l'omet.
|
|
12
43
|
*
|
|
13
44
|
* Par défaut aucun canal n'accepte d'entrée (sûr) : un contrôleur opte explicitement.
|
|
14
45
|
*/
|
|
15
|
-
export type RealtimeInboundHandler = (params: unknown, reply: (payload: unknown) => void) => void;
|
|
46
|
+
export type RealtimeInboundHandler = (params: unknown, reply: (payload: unknown) => void, connection: IRealtimeConnection) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Handler d'entrée TYPÉ par le contrat : `params` est ce que le client a émis sur
|
|
49
|
+
* ce canal, lu par `Jsonify` ; `reply` pousse la charge que le serveur émet sur le
|
|
50
|
+
* même canal. Les paramètres restent NON FIABLES : le type n'est pas une validation.
|
|
51
|
+
*
|
|
52
|
+
* Écrit en signature de MÉTHODE pour que ses paramètres se comparent de façon
|
|
53
|
+
* bivariante : c'est ce qui permet à une table typée de remplacer la table
|
|
54
|
+
* permissive de la base, que le contrôleur lit en `unknown` à l'exécution.
|
|
55
|
+
*/
|
|
56
|
+
export type TypedRealtimeInboundHandler<P, R> = {
|
|
57
|
+
handle(params: P, reply: (payload: R) => void, connection: IRealtimeConnection): void;
|
|
58
|
+
}["handle"];
|
|
59
|
+
/**
|
|
60
|
+
* Table des entrées client d'un contrôleur : un handler par canal de `Listen`,
|
|
61
|
+
* chacun facultatif — accepter une entrée reste un choix explicite, canal par
|
|
62
|
+
* canal (par défaut, un client ne peut rien pousser). `Listen` non déclaré :
|
|
63
|
+
* n'importe quel nom, `params: unknown`.
|
|
64
|
+
*/
|
|
65
|
+
export type RealtimeInboundHandlers<Listen extends EventsMap, Emit extends EventsMap> = {
|
|
66
|
+
[K in EventNames<Listen>]?: TypedRealtimeInboundHandler<EventPayload<Listen, K>, K extends keyof Emit ? Emit[K] : unknown>;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Handler d'action RPC tel que la table de `realtimeActions()` le range. Signature
|
|
70
|
+
* de MÉTHODE (paramètre comparé de façon bivariante) : une table typée par le
|
|
71
|
+
* contrat, dont chaque handler attend sa forme, remplace ainsi la table
|
|
72
|
+
* permissive de la base.
|
|
73
|
+
*/
|
|
74
|
+
export type RealtimeActionHandler = {
|
|
75
|
+
handle(params: unknown): unknown;
|
|
76
|
+
}["handle"];
|
|
16
77
|
/**
|
|
17
78
|
* IRealtimeController — contrat d'un contrôleur temps réel SERVEUR (endpoint WS
|
|
18
79
|
* JSON-RPC 2.0). Le protocole (discrimination, dispatch, actions, pending, pub/sub,
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
* seuil de drop). Tous les cumuls sont **monotones** → le débit/s se dérive côté
|
|
14
14
|
* lecteur (delta `total`/`ts`, comme le CPU% ou le flux ORM) : 0 état de lecture.
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
import type { IBackplaneInfo } from "./IBackplane.js";
|
|
16
|
+
export type { IRealtimeChannelStat, IRealtimeProbe, IRealtimeHealth, IRealtimeClusterHealth, RealtimeHealthReport, } from "nodefony";
|
|
18
17
|
/** Vue d'UNE connexion realtime pour la sonde (backpressure = risque #1). */
|
|
19
18
|
export interface IRealtimeConnProbe {
|
|
20
19
|
/** État du transport (0..3, aligné `WebSocket.readyState`). */
|
|
@@ -35,172 +34,14 @@ export interface IRealtimeConnProbe {
|
|
|
35
34
|
* coupe un slow-consumer (`close 1013`). `> 0` = ce client n'absorbe pas le flux.
|
|
36
35
|
*/
|
|
37
36
|
readonly dropped: number;
|
|
38
|
-
}
|
|
39
|
-
/** Stat per-canal du hub (fan-out). */
|
|
40
|
-
export interface IRealtimeChannelStat {
|
|
41
|
-
/** Nom du canal (suffixe `:<ms>` de granularité inclus). */
|
|
42
|
-
channel: string;
|
|
43
|
-
/** Abonnés locaux vivants (sinks) — instantané. */
|
|
44
|
-
subscribers: number;
|
|
45
|
-
/** Publications cumulées sur ce canal (monotone) → débit dérivé côté lecteur. */
|
|
46
|
-
messages: number;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Snapshot PUR du hub (sans identité process) — ce que renvoie
|
|
50
|
-
* {@link RealtimeHub.probe}. Per-instance/pod (cloud-native) ; l'agrégat multi-pod
|
|
51
|
-
* = backplane Redis / Prometheus (P13).
|
|
52
|
-
*/
|
|
53
|
-
export interface IRealtimeProbe {
|
|
54
|
-
/** Horodatage du snapshot (ms epoch) — base du calcul de débit côté lecteur. */
|
|
55
|
-
ts: number;
|
|
56
|
-
/** Canaux actifs (≥ 1 abonné) avec abonnés + publications cumulées. */
|
|
57
|
-
channels: IRealtimeChannelStat[];
|
|
58
|
-
/** Nombre de canaux actifs (= 1 provider/canal/pod). */
|
|
59
|
-
channelCount: number;
|
|
60
|
-
/** Appels `publish` cumulés (monotone). */
|
|
61
|
-
publishTotal: number;
|
|
62
|
-
/** Livraisons cumulées (`publish` × abonnés) — vrai coût de fan-out (monotone). */
|
|
63
|
-
fanoutTotal: number;
|
|
64
|
-
/** Frames entrantes full-duplex cumulées (canaux gated SIP/bridge) (monotone). */
|
|
65
|
-
inboundTotal: number;
|
|
66
|
-
/**
|
|
67
|
-
* Messages d'ingress backplane REFUSÉS (canal non déclaré broadcast) — monotone.
|
|
68
|
-
* Reste à 0 en fonctionnement normal : un pair légitime n'émet que des canaux
|
|
69
|
-
* broadcast. Un compteur qui décolle = pair mal configuré, ou écriture tierce
|
|
70
|
-
* dans un bus partagé (Redis mutualisé). Cf `RealtimeHub` (contrôle d'admission).
|
|
71
|
-
*/
|
|
72
|
-
ingressRejectedTotal: number;
|
|
73
37
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* module de sécurité n'est chargé : sans identité vérifiable, le hub ferme.
|
|
77
|
-
* `> 0` signale une configuration incomplète (sécurité absente) autant qu'une
|
|
78
|
-
* tentative d'accès — dans les deux cas, quelque chose est à regarder.
|
|
38
|
+
* Cumul de trames BINAIRES reçues et écartées faute de lecteur (monotone) :
|
|
39
|
+
* connexion texte sans `onRealtimeBinary`. Absent = la connexion ne compte pas.
|
|
79
40
|
*/
|
|
80
|
-
|
|
81
|
-
/** Connexions realtime vivantes. */
|
|
82
|
-
connectionCount: number;
|
|
83
|
-
/** Somme cumulée des octets envoyés, toutes connexions (monotone). */
|
|
84
|
-
bytesSentTotal: number;
|
|
85
|
-
/** Somme cumulée des frames envoyées, toutes connexions (monotone). */
|
|
86
|
-
messagesSentTotal: number;
|
|
87
|
-
/** Backpressure agrégée (instantanée) — risque #1. */
|
|
88
|
-
backpressure: {
|
|
89
|
-
/** Pire `bufferedAmount` parmi les connexions (octets). */
|
|
90
|
-
maxBufferedAmount: number;
|
|
91
|
-
/** Somme des `bufferedAmount` (octets en attente process-wide). */
|
|
92
|
-
totalBufferedAmount: number;
|
|
93
|
-
/** Connexions au-dessus du seuil d'alerte slow-consumer. */
|
|
94
|
-
slowConsumers: number;
|
|
95
|
-
/**
|
|
96
|
-
* Cumul de frames jetées par back-pressure (drop latest-wins + close
|
|
97
|
-
* slow-consumer), toutes connexions (monotone) → taux de perte dérivé côté
|
|
98
|
-
* lecteur. `0` en régime sain ; croît = des clients lents sont contenus pour
|
|
99
|
-
* protéger la mémoire process (sinon file `ws` non bornée → OOM).
|
|
100
|
-
*/
|
|
101
|
-
drops: number;
|
|
102
|
-
};
|
|
41
|
+
readonly binaryDiscarded?: number;
|
|
103
42
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* fallback fail-soft), sinon le descripteur du driver branché. Optionnel pour
|
|
107
|
-
* rétrocompat des consommateurs qui construisent un probe partiel.
|
|
43
|
+
* Cumul de trames reçues et écartées parce qu'illisibles par le codec de la
|
|
44
|
+
* connexion (monotone). Absent = la connexion ne compte pas.
|
|
108
45
|
*/
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
/** Snapshot enrichi de l'identité d'instance — santé per-instance d'un worker/pod. */
|
|
112
|
-
export interface IRealtimeHealth extends IRealtimeProbe {
|
|
113
|
-
/** Identifiant de CE process/pod (per-instance). */
|
|
114
|
-
instanceId: string;
|
|
115
|
-
/**
|
|
116
|
-
* Santé PROCESS du worker (CPU/mém/event-loop) — additif : la grille « salle des
|
|
117
|
-
* machines » de la vue pod lit `instances[].process` par worker. Optionnel (absent si
|
|
118
|
-
* sonde process coupée) → les consommateurs realtime existants l'ignorent (non-breaking).
|
|
119
|
-
*/
|
|
120
|
-
process?: IProcessHealth;
|
|
121
|
-
/**
|
|
122
|
-
* Sonde process **riche** (GC/heap-spaces/handles/ELU active-idle/ctx-switches) —
|
|
123
|
-
* présente UNIQUEMENT pendant un drill-down de CE worker (Phase 2 : le master a envoyé
|
|
124
|
-
* `nf:probe:enrich` à ce pid). Absente sinon → « on paie ce qu'on regarde » (0 surcoût
|
|
125
|
-
* hors drill). Fusionnée côté front avec `process` pour la vue Supervision complète.
|
|
126
|
-
*/
|
|
127
|
-
rich?: IProcessRich;
|
|
128
|
-
/**
|
|
129
|
-
* Santé ORM **lean** du worker (connecteurs/requêtes/erreurs/latence EWMA) — additif,
|
|
130
|
-
* comme `process`. Présent si un driver ORM a branché sa sonde (`setOrmHealthProvider`),
|
|
131
|
-
* absent sinon → les consommateurs realtime l'ignorent (non-breaking). Agrégée pod dans
|
|
132
|
-
* {@link IRealtimeClusterHealth.totals}.`orm`.
|
|
133
|
-
*/
|
|
134
|
-
orm?: IOrmLeanHealth;
|
|
135
|
-
/**
|
|
136
|
-
* Compteurs d'erreurs Syslog du worker (ERROR/CRITIC cumulés) — additif. Permet une
|
|
137
|
-
* carte « erreurs par worker » + un taux d'erreur pod (delta côté lecteur). Absent si la
|
|
138
|
-
* sonde n'a pu lire le syslog du kernel.
|
|
139
|
-
*/
|
|
140
|
-
errors?: IInstanceErrorHealth;
|
|
141
|
-
/**
|
|
142
|
-
* Drivers de backplane **enregistrés dans ce process** (clés du registre ouvert :
|
|
143
|
-
* natifs + ceux qu'une application a ajoutés par `registerBackplaneDriver`). Sert la
|
|
144
|
-
* même question que la liste des backends côté stores : « qu'est-ce que je POURRAIS
|
|
145
|
-
* brancher ? », à laquelle `backplane.driver` (l'actif) ne répond pas. Absent si le
|
|
146
|
-
* registre n'est pas joignable.
|
|
147
|
-
*/
|
|
148
|
-
backplaneDrivers?: string[];
|
|
149
|
-
/**
|
|
150
|
-
* Diagnostic ORM **riche** du worker (par connecteur : ping/latence/stockage/pool +
|
|
151
|
-
* flux requêtes) — blob OPAQUE `{ health, flow }` produit par le driver, présent
|
|
152
|
-
* UNIQUEMENT pendant un drill ORM de CE worker (facette `"orm"` : le master a envoyé
|
|
153
|
-
* `nf:probe:enrich {facet:"orm"}` à ce pid). Absent sinon → « on paie ce qu'on regarde »
|
|
154
|
-
* (0 ping ORM hors drill). Consommé par la page drill `/nodefony/orm/<pid>` (canal
|
|
155
|
-
* `nodefony:orm:rich@<pid>`) → diagnostic riche du pid EXACT (≠ round-robin).
|
|
156
|
-
*/
|
|
157
|
-
ormRich?: unknown;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Vue **POD agrégée** de la socket (Phase 4c, mode cluster) — la santé de TOUS les workers
|
|
161
|
-
* du pod consolidée. Servie par n'importe quel worker via l'endpoint santé quand le master
|
|
162
|
-
* pousse le snapshot agrégé (sinon on retombe sur {@link IRealtimeHealth} per-instance).
|
|
163
|
-
*
|
|
164
|
-
* `cluster: true` est le discriminant : un consommateur (Studio) distingue la vue pod de la
|
|
165
|
-
* vue per-instance. `instances` garde le détail par worker (drill-down) ; `totals` somme les
|
|
166
|
-
* scalaires (la backpressure `maxBufferedAmount` est un MAX, pas une somme — c'est le pire pod).
|
|
167
|
-
*/
|
|
168
|
-
export interface IRealtimeClusterHealth {
|
|
169
|
-
/** Discriminant : vue agrégée multi-worker (≠ {@link IRealtimeHealth} per-instance). */
|
|
170
|
-
cluster: true;
|
|
171
|
-
/** Horodatage du snapshot agrégé (ms epoch). */
|
|
172
|
-
ts: number;
|
|
173
|
-
/** Nombre de workers présents dans l'agrégat. */
|
|
174
|
-
instanceCount: number;
|
|
175
|
-
/** Santé par worker (détail per-instance pour le drill-down). */
|
|
176
|
-
instances: IRealtimeHealth[];
|
|
177
|
-
/** Totaux pod (sommes des scalaires ; backpressure max = pire worker). */
|
|
178
|
-
totals: {
|
|
179
|
-
channelCount: number;
|
|
180
|
-
publishTotal: number;
|
|
181
|
-
fanoutTotal: number;
|
|
182
|
-
inboundTotal: number;
|
|
183
|
-
/** Ingress backplane refusés, tous workers (signal de sécurité pod-wide). */
|
|
184
|
-
ingressRejectedTotal: number;
|
|
185
|
-
connectionCount: number;
|
|
186
|
-
bytesSentTotal: number;
|
|
187
|
-
messagesSentTotal: number;
|
|
188
|
-
backpressure: {
|
|
189
|
-
/** Pire `maxBufferedAmount` parmi les workers (octets) — pas une somme. */
|
|
190
|
-
maxBufferedAmount: number;
|
|
191
|
-
/** Somme des octets en attente, tous workers. */
|
|
192
|
-
totalBufferedAmount: number;
|
|
193
|
-
/** Total des slow-consumers du pod. */
|
|
194
|
-
slowConsumers: number;
|
|
195
|
-
/** Total des frames jetées par back-pressure (drop + close), tous workers. */
|
|
196
|
-
drops: number;
|
|
197
|
-
};
|
|
198
|
-
/**
|
|
199
|
-
* Santé ORM agrégée du pod (sommes ; `maxEwmaMs` = pire worker). Présent si ≥ 1 worker
|
|
200
|
-
* a remonté sa sonde ORM, absent sinon.
|
|
201
|
-
*/
|
|
202
|
-
orm?: IOrmLeanHealth;
|
|
203
|
-
/** Erreurs Syslog agrégées du pod (sommes). Présent si ≥ 1 worker a remonté ses compteurs. */
|
|
204
|
-
errors?: IInstanceErrorHealth;
|
|
205
|
-
};
|
|
46
|
+
readonly malformedDiscarded?: number;
|
|
206
47
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ISubscriberIdentity, TokenStatus } from "nodefony";
|
|
1
2
|
/**
|
|
2
3
|
* Jeton realtime — surface MINIMALE de l'identité d'une connexion WebSocket.
|
|
3
4
|
*
|
|
@@ -45,6 +46,15 @@ export interface IRealtimeToken {
|
|
|
45
46
|
* (publish/subscribe), payé seulement sur `api.request`.
|
|
46
47
|
*/
|
|
47
48
|
isValid?(nowMs?: number): boolean | Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* La CAUSE de l'état de l'identité, quand elle est connaissable : `active`,
|
|
51
|
+
* `expired` (jeton porteur arrivé à `exp`) ou `revoked` (révoqué l'emporte sur
|
|
52
|
+
* expiré). Le hub en tire le code de fermeture d'une socket ouverte : `expired` →
|
|
53
|
+
* `TOKEN_EXPIRED_CLOSE_CODE` (4006), sinon `IDENTITY_DEAD_CLOSE_CODE` (4001) —
|
|
54
|
+
* ADR-0021 §S11. Optionnel : absent, `isValid` décide et son `false` vaut 4001
|
|
55
|
+
* (une session ne sait pas dire si elle a expiré ou été détruite).
|
|
56
|
+
*/
|
|
57
|
+
status?(nowMs?: number): TokenStatus | Promise<TokenStatus>;
|
|
48
58
|
/**
|
|
49
59
|
* Le COMPTE qui a ouvert la socket existe-t-il toujours, actif, et est-ce
|
|
50
60
|
* bien le même ? `false` si le compte a disparu, a été désactivé ou
|
|
@@ -58,4 +68,15 @@ export interface IRealtimeToken {
|
|
|
58
68
|
* qui le paierait pour chaque socket ouverte, à chaque tour.
|
|
59
69
|
*/
|
|
60
70
|
confirmAccount?(): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Identité d'ADMISSION de la connexion — la donnée plate que reçoit la règle `allow` d'un canal, construite UNE fois et gelée.
|
|
73
|
+
*
|
|
74
|
+
* Rend toujours la MÊME référence : elle est lue au `welcome` puis à chaque
|
|
75
|
+
* admission, jamais recalculée. Optionnelle pour un authentificateur tiers ;
|
|
76
|
+
* absente, la connexion n'entre sur AUCUN canal qui déclare `allow` (refus
|
|
77
|
+
* `forbidden` et une ligne de journal) — jamais un `id` déduit de
|
|
78
|
+
* {@link getUserIdentifier}, qui est un login et se réattribue
|
|
79
|
+
* (ADR-0021 §S10).
|
|
80
|
+
*/
|
|
81
|
+
getIdentity?(): ISubscriberIdentity;
|
|
61
82
|
}
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* `@nodefony/security` (P6) les implémente sans coupler ce module au security.
|
|
10
10
|
*/
|
|
11
11
|
export type { IBackplane, IBackplaneMessage, BackplaneHandler, } from "./IBackplane.js";
|
|
12
|
-
export type { IRealtimeController, RealtimePublish, RealtimeInboundHandler, } from "./IRealtimeController.js";
|
|
12
|
+
export type { IRealtimeController, RealtimePublish, ContractRealtimePublish, RealtimeInboundHandler, } from "./IRealtimeController.js";
|
|
13
13
|
export type { IChannelPolicy } from "./IChannelPolicy.js";
|
|
14
14
|
export type { IRealtimeConnectionState } from "./IRealtimeConnectionState.js";
|
|
15
|
+
export type { IRealtimeConnection } from "./IRealtimeConnection.js";
|
|
15
16
|
export type { IRealtimeProbe, IRealtimeHealth, IRealtimeClusterHealth, IRealtimeChannelStat, IRealtimeConnProbe, } from "./IRealtimeProbe.js";
|
|
16
17
|
export type { IRealtimeToken } from "./IRealtimeToken.js";
|
|
17
18
|
export type { IRealtimeHandshake } from "./IRealtimeHandshake.js";
|
|
@@ -2,8 +2,11 @@ import type { IBackplane, BackplaneHandler, IBackplaneMessage, IBackplaneInfo }
|
|
|
2
2
|
import { CLUSTER_RT_KIND } from "nodefony";
|
|
3
3
|
/**
|
|
4
4
|
* Enveloppe IPC d'une publication realtime = {@link IBackplaneMessage} + discriminant.
|
|
5
|
-
* Sérialisée une fois par `process.send`/`worker.send
|
|
6
|
-
*
|
|
5
|
+
* Sérialisée une fois par `process.send`/`worker.send`. Le master de Nodefony forke
|
|
6
|
+
* ses workers en sérialisation `advanced` (clonage structuré,
|
|
7
|
+
* `src/nodefony/src/service/cluster/clusterMaster.ts#startClusterMaster`) : un
|
|
8
|
+
* `Uint8Array` y traverse intact, sans codec — l'IPC ne quitte pas la machine, il
|
|
9
|
+
* n'a ni sceau ni second sujet à porter.
|
|
7
10
|
*/
|
|
8
11
|
export interface ClusterBackplaneEnvelope extends IBackplaneMessage {
|
|
9
12
|
readonly kind: typeof CLUSTER_RT_KIND;
|
|
@@ -26,6 +29,11 @@ export interface IClusterBackplaneTransport {
|
|
|
26
29
|
* Appelé une seule fois, au `start()`.
|
|
27
30
|
*/
|
|
28
31
|
onReceive(cb: (msg: unknown) => void): void;
|
|
32
|
+
/**
|
|
33
|
+
* Le canal porte-t-il un `Uint8Array` intact (sérialisation `advanced`) ?
|
|
34
|
+
* Optionnel : absent, les octets restent sur ce process.
|
|
35
|
+
*/
|
|
36
|
+
carriesBytes?(): boolean;
|
|
29
37
|
}
|
|
30
38
|
/**
|
|
31
39
|
* Transport de production : `process.send` (émission) + `process.on("message")`
|
|
@@ -60,7 +68,7 @@ export declare class ClusterBackplane implements IBackplane {
|
|
|
60
68
|
static readonly driver = "cluster";
|
|
61
69
|
readonly originId: string;
|
|
62
70
|
constructor(transport?: IClusterBackplaneTransport, originId?: string);
|
|
63
|
-
/** Branche la réception IPC. Idempotent. */
|
|
71
|
+
/** Branche la réception IPC. Idempotent, et redémarrable après {@link stop}. */
|
|
64
72
|
start(): void;
|
|
65
73
|
/**
|
|
66
74
|
* Propage une publication locale aux autres pairs via le master. NE refait PAS le
|
|
@@ -69,7 +77,17 @@ export declare class ClusterBackplane implements IBackplane {
|
|
|
69
77
|
publish(channel: string, payload: unknown): void;
|
|
70
78
|
/** Un seul handler d'ingress ; un appel ultérieur remplace le précédent. */
|
|
71
79
|
onMessage(handler: BackplaneHandler): void;
|
|
72
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Les octets traversent l'IPC tels quels quand le canal est en clonage
|
|
82
|
+
* structuré : aucun codec n'est appliqué, l'argument est ignoré.
|
|
83
|
+
*
|
|
84
|
+
* @returns `true` si le transport constate un canal `advanced`.
|
|
85
|
+
*/
|
|
86
|
+
enableBytes(): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Coupe la réception et détache le handler. Le récepteur du transport survit
|
|
89
|
+
* (`process` ne le retire pas) mais ne livre plus rien jusqu'au prochain {@link start}.
|
|
90
|
+
*/
|
|
73
91
|
stop(): void;
|
|
74
92
|
describe(): IBackplaneInfo;
|
|
75
93
|
}
|