@nodefony/realtime 10.0.0-beta.5 → 10.0.0-beta.7
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 +55 -0
- package/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.158.0}/helpers/esm/decorate.js +1 -1
- package/dist/index.js +91 -12
- package/dist/nodefony/config/config.js +7 -0
- package/dist/nodefony/decorators/realtimeDecorators.js +341 -43
- package/dist/nodefony/interfaces/IRealtimeConnection.js +1 -0
- package/dist/nodefony/src/backplane/ClusterBackplane.js +32 -4
- package/dist/nodefony/src/backplane/RedisBackplane.js +146 -41
- package/dist/nodefony/src/backplane/controlOrder.js +86 -0
- package/dist/nodefony/src/backplane/envelope.js +505 -27
- package/dist/nodefony/src/backplane/ingress.js +93 -0
- package/dist/nodefony/src/backplane/presenceMessage.js +205 -0
- package/dist/nodefony/src/backplane/publishQueue.js +97 -3
- 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 +135 -0
- package/dist/nodefony/src/server/RealtimeController.js +409 -107
- package/dist/nodefony/src/server/RealtimeHub.js +1158 -111
- 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 +95 -0
- package/dist/nodefony/src/server/channelPolicyResolution.js +191 -0
- package/dist/nodefony/src/server/channelPresence.js +488 -0
- package/dist/nodefony/src/server/connectionAdmission.js +242 -0
- package/dist/nodefony/src/server/contractRoutes.js +75 -0
- package/dist/nodefony/src/server/deniedDetail.js +10 -3
- package/dist/nodefony/src/server/frameAdmission.js +160 -0
- package/dist/nodefony/src/server/presenceBus.js +486 -0
- package/dist/nodefony/src/server/realtimeShutdown.js +31 -0
- package/dist/nodefony/src/server/remotePresence.js +541 -0
- package/dist/nodefony/src/server/subscribedAck.js +13 -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 +51 -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 +347 -0
- package/dist/nodefony/testing/index.js +43 -12
- package/dist/nodefony/testing/presenceClient.js +54 -0
- package/dist/types/index.d.ts +39 -7
- package/dist/types/nodefony/config/config.d.ts +9 -0
- package/dist/types/nodefony/decorators/realtimeDecorators.d.ts +198 -17
- package/dist/types/nodefony/interfaces/IBackplane.d.ts +46 -51
- 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 +89 -18
- package/dist/types/nodefony/src/backplane/controlOrder.d.ts +75 -0
- package/dist/types/nodefony/src/backplane/envelope.d.ts +77 -0
- package/dist/types/nodefony/src/backplane/ingress.d.ts +48 -0
- package/dist/types/nodefony/src/backplane/presenceMessage.d.ts +150 -0
- package/dist/types/nodefony/src/backplane/publishQueue.d.ts +25 -1
- 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 +20 -0
- package/dist/types/nodefony/src/server/RealtimeController.d.ts +110 -12
- package/dist/types/nodefony/src/server/RealtimeHub.d.ts +448 -42
- 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 +41 -0
- package/dist/types/nodefony/src/server/channelPolicyResolution.d.ts +112 -0
- package/dist/types/nodefony/src/server/channelPresence.d.ts +155 -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 +66 -0
- package/dist/types/nodefony/src/server/presenceBus.d.ts +112 -0
- package/dist/types/nodefony/src/server/realtimeShutdown.d.ts +21 -0
- package/dist/types/nodefony/src/server/remotePresence.d.ts +151 -0
- package/dist/types/nodefony/src/server/subscribedAck.d.ts +10 -0
- package/dist/types/nodefony/src/server/welcomeIdentity.d.ts +19 -0
- package/dist/types/nodefony/src/service/RealtimeService.d.ts +17 -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 +78 -0
- package/dist/types/nodefony/testing/index.d.ts +13 -2
- package/dist/types/nodefony/testing/presenceClient.d.ts +20 -0
- package/docs/actions.md +420 -18
- package/docs/architecture.md +101 -11
- package/docs/cadence-et-contre-pression.md +87 -11
- package/docs/canaux-a-membres.md +277 -0
- package/docs/cas-d-usage.md +1703 -0
- package/docs/configuration.md +175 -19
- package/docs/cookbook-chat.md +701 -224
- package/docs/index.md +11 -1
- package/docs/observabilite.md +97 -21
- package/docs/protocole.md +142 -33
- package/docs/securite.md +347 -41
- package/docs/vocabulaire.md +13 -12
- package/package.json +16 -13
|
@@ -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
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IBackplane, BackplaneHandler, IBackplaneInfo } from "../../interfaces/IBackplane.js";
|
|
2
|
+
import { type IFrameCodec } from "nodefony";
|
|
2
3
|
import { type BackplaneNotice } from "./publishQueue.js";
|
|
3
4
|
/**
|
|
4
5
|
* BASE du canal Redis **dédié** transportant les enveloppes realtime entre pods.
|
|
@@ -22,6 +23,17 @@ export declare const REDIS_RT_CHANNEL = "nodefony:realtime";
|
|
|
22
23
|
* @returns le nom du canal Redis à `SUBSCRIBE`/`PUBLISH`.
|
|
23
24
|
*/
|
|
24
25
|
export declare function resolveRedisChannel(namespace?: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Construit le **sujet binaire** du bus (ADR-0021 S6) : le canal texte suffixé du
|
|
28
|
+
* protocole du codec. Un second sujet, versionné par son nom, que les exemplaires
|
|
29
|
+
* plus anciens n'écoutent pas — le message texte n'est pas touché, et un codec
|
|
30
|
+
* binaire d'un autre format aurait son propre sujet.
|
|
31
|
+
*
|
|
32
|
+
* @param redisChannel - le canal texte effectif ({@link resolveRedisChannel}).
|
|
33
|
+
* @param codec - le codec binaire du serveur.
|
|
34
|
+
* @returns le nom du canal Redis qui porte les enveloppes binaires.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveRedisBytesChannel(redisChannel: string, codec: IFrameCodec): string;
|
|
25
37
|
/**
|
|
26
38
|
* Transport pub/sub du backplane Redis — abstrait `PUBLISH` / `SUBSCRIBE` derrière
|
|
27
39
|
* un seam **injectable** (même pattern que `IClusterBackplaneTransport`). Permet de
|
|
@@ -39,12 +51,18 @@ export interface IRedisBackplaneTransport {
|
|
|
39
51
|
* {@link BackplanePublishQueue}). Un transport synchrone (bus mémoire de test) rend
|
|
40
52
|
* `void` — il n'a pas de file, la borne reste alors inerte.
|
|
41
53
|
*/
|
|
42
|
-
publish(channel: string, message: string): void | Promise<unknown>;
|
|
54
|
+
publish(channel: string, message: string | Uint8Array): void | Promise<unknown>;
|
|
43
55
|
/**
|
|
44
56
|
* Abonne au canal Redis. Le listener reçoit le message brut (string) ; le tri
|
|
45
57
|
* (parse + anti-echo) est fait par le backplane. Async (Redis) ou sync (fake).
|
|
46
58
|
*/
|
|
47
59
|
subscribe(channel: string, onMessage: (message: string) => void): void | Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Abonne au canal Redis en mode **octets** : le listener reçoit le message tel
|
|
62
|
+
* que publié, sans décodage UTF-8 (sujet binaire, ADR-0021 S6). Optionnel : un
|
|
63
|
+
* transport qui ne l'offre pas laisse les octets sur le serveur qui les publie.
|
|
64
|
+
*/
|
|
65
|
+
subscribeBytes?(channel: string, onMessage: (message: Uint8Array) => void): void | Promise<void>;
|
|
48
66
|
/** Désabonne du canal Redis et libère le listener associé. Idempotent. */
|
|
49
67
|
unsubscribe(channel: string): void | Promise<void>;
|
|
50
68
|
}
|
|
@@ -54,18 +72,53 @@ export interface IRedisBackplaneTransport {
|
|
|
54
72
|
* en dépendance de `@nodefony/realtime`.
|
|
55
73
|
*/
|
|
56
74
|
export interface IRedisPublisher {
|
|
57
|
-
publish(channel: string, message: string): unknown;
|
|
75
|
+
publish(channel: string, message: string | Buffer): unknown;
|
|
58
76
|
}
|
|
77
|
+
/** Écouteur texte d'un client Redis abonné. */
|
|
78
|
+
type RedisTextListener = (message: string, channel: string) => unknown;
|
|
79
|
+
/** Écouteur octets d'un client Redis abonné (`bufferMode`). */
|
|
80
|
+
type RedisBytesListener = (message: Buffer, channel: Buffer) => unknown;
|
|
59
81
|
/**
|
|
60
82
|
* Client Redis **subscriber** dédié — surface structurelle minimale (compatible
|
|
61
83
|
* `RedisClientType` v5). Un client abonné ne peut plus émettre de commandes
|
|
62
84
|
* normales (protocole Redis) → publisher et subscriber sont 2 connexions distinctes
|
|
63
85
|
* (cf `@nodefony/redis` : connexions `publish` / `subscribe`).
|
|
86
|
+
*
|
|
64
87
|
*/
|
|
65
88
|
export interface IRedisSubscriber {
|
|
66
|
-
subscribe(channel: string, listener:
|
|
67
|
-
unsubscribe(channel: string, listener?:
|
|
89
|
+
subscribe(channel: string, listener: RedisTextListener): unknown;
|
|
90
|
+
unsubscribe(channel: string, listener?: RedisTextListener): unknown;
|
|
68
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Client Redis abonné en mode **octets** — le troisième argument `true`
|
|
94
|
+
* (`bufferMode` de node-redis) rend les messages en `Buffer`, sans décodage
|
|
95
|
+
* UTF-8 : c'est lui qui porte le sujet binaire intact (ADR-0021 S6).
|
|
96
|
+
*
|
|
97
|
+
* Interface SÉPARÉE de {@link IRedisSubscriber}, et non une surcharge de
|
|
98
|
+
* celle-ci : les deux formes réunies dans une même interface ne sont plus
|
|
99
|
+
* satisfaites ni par le type de node-redis (générique sur le mode), ni par une
|
|
100
|
+
* doublure de test qui ne connaît que le texte. Séparées, chacune l'est.
|
|
101
|
+
*/
|
|
102
|
+
export interface IRedisBytesSubscriber {
|
|
103
|
+
subscribe(channel: string, listener: RedisBytesListener, bufferMode: true): unknown;
|
|
104
|
+
unsubscribe(channel: string, listener: RedisBytesListener, bufferMode: true): unknown;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Vrai si la valeur expose la surface d'un client Redis publisher — vérifié au
|
|
108
|
+
* démarrage, sur le client que rend le service `redis`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function isRedisPublisher(value: unknown): value is IRedisPublisher;
|
|
111
|
+
/** Vrai si la valeur expose la surface d'un client Redis subscriber dédié. */
|
|
112
|
+
export declare function isRedisSubscriber(value: unknown): value is IRedisSubscriber;
|
|
113
|
+
/**
|
|
114
|
+
* Vrai si la valeur expose la surface d'un client abonné capable du mode octets.
|
|
115
|
+
*
|
|
116
|
+
* La forme ne dit pas si le troisième argument est compris : ce garde vaut pour
|
|
117
|
+
* le client que rend le service `redis` (node-redis, qui l'accepte). Un client qui
|
|
118
|
+
* l'ignorerait livrerait du texte au sujet binaire — illisible pour le codec,
|
|
119
|
+
* donc jeté, compté sous un secret (`sealRejectedTotal`), jamais réinjecté.
|
|
120
|
+
*/
|
|
121
|
+
export declare function isRedisBytesSubscriber(value: unknown): value is IRedisBytesSubscriber;
|
|
69
122
|
/**
|
|
70
123
|
* Adapte deux clients Redis (publisher + subscriber dédié) au seam
|
|
71
124
|
* {@link IRedisBackplaneTransport}. SEUL point de contact avec la lib `redis` —
|
|
@@ -80,17 +133,17 @@ export interface IRedisSubscriber {
|
|
|
80
133
|
* defineRealtimeConfig({ backplane: { driver: "redis" } }, { backplane: bp });
|
|
81
134
|
* ```
|
|
82
135
|
*
|
|
83
|
-
*
|
|
84
|
-
* (n'arrache pas d'éventuels autres abonnés du même client).
|
|
136
|
+
* Chaque listener Redis est conservé, par canal, pour un `unsubscribe(channel,
|
|
137
|
+
* listener)` ciblé (n'arrache pas d'éventuels autres abonnés du même client).
|
|
138
|
+
*
|
|
139
|
+
* @param publisher - connexion de publication.
|
|
140
|
+
* @param subscriber - connexion d'abonnement dédiée.
|
|
141
|
+
* @param bytesSubscriber - la MÊME connexion d'abonnement, vue en mode octets
|
|
142
|
+
* ({@link IRedisBytesSubscriber}) ; absente, le transport n'offre pas de sujet
|
|
143
|
+
* binaire et les octets restent sur le serveur qui les publie.
|
|
144
|
+
* @returns le transport du {@link RedisBackplane}.
|
|
85
145
|
*/
|
|
86
|
-
|
|
87
|
-
* Vrai si la valeur expose la surface d'un client Redis publisher — vérifié au
|
|
88
|
-
* démarrage, sur le client que rend le service `redis`.
|
|
89
|
-
*/
|
|
90
|
-
export declare function isRedisPublisher(value: unknown): value is IRedisPublisher;
|
|
91
|
-
/** Vrai si la valeur expose la surface d'un client Redis subscriber dédié. */
|
|
92
|
-
export declare function isRedisSubscriber(value: unknown): value is IRedisSubscriber;
|
|
93
|
-
export declare function createRedisServiceTransport(publisher: IRedisPublisher, subscriber: IRedisSubscriber): IRedisBackplaneTransport;
|
|
146
|
+
export declare function createRedisServiceTransport(publisher: IRedisPublisher, subscriber: IRedisSubscriber, bytesSubscriber?: IRedisBytesSubscriber): IRedisBackplaneTransport;
|
|
94
147
|
/**
|
|
95
148
|
* Réglages optionnels du driver — regroupés en objet pour ne pas allonger une
|
|
96
149
|
* liste de paramètres positionnels déjà à quatre entrées.
|
|
@@ -103,8 +156,8 @@ export interface IRedisBackplaneOptions {
|
|
|
103
156
|
*/
|
|
104
157
|
maxQueueBytes?: number;
|
|
105
158
|
/**
|
|
106
|
-
* Annonce des transitions de la file (saturation / retour à la normale)
|
|
107
|
-
* wiring y branche le syslog du module. Omis = silencieux : acceptable en test,
|
|
159
|
+
* Annonce des transitions de la file (saturation / retour à la normale) et des
|
|
160
|
+
* sceaux refusés à l'entrée — le wiring y branche le syslog du module. Omis = silencieux : acceptable en test,
|
|
108
161
|
* jamais en production (une dégradation doit être annoncée).
|
|
109
162
|
*/
|
|
110
163
|
onNotice?: BackplaneNotice;
|
|
@@ -152,6 +205,16 @@ export declare class RedisBackplane implements IBackplane {
|
|
|
152
205
|
* un 2ᵉ appel concurrent ne ré-abonne pas).
|
|
153
206
|
*/
|
|
154
207
|
start(): Promise<void>;
|
|
208
|
+
/**
|
|
209
|
+
* Arme le sujet binaire (ADR-0021 S6) : les charges qui contiennent des octets
|
|
210
|
+
* partent en enveloppe encodée par `codec` sur un second canal Redis, et ce
|
|
211
|
+
* canal est écouté. Les charges sans octets gardent le sujet texte, intact pour
|
|
212
|
+
* les exemplaires plus anciens. Idempotent.
|
|
213
|
+
*
|
|
214
|
+
* @param codec - le codec binaire du serveur.
|
|
215
|
+
* @returns `false` si le transport ne sait pas s'abonner en octets.
|
|
216
|
+
*/
|
|
217
|
+
enableBytes(codec: IFrameCodec): boolean;
|
|
155
218
|
/**
|
|
156
219
|
* Propage une publication locale aux autres pods via Redis. NE refait PAS le
|
|
157
220
|
* fan-out local (déjà fait par le hub). Sérialisé une fois ici.
|
|
@@ -159,12 +222,20 @@ export declare class RedisBackplane implements IBackplane {
|
|
|
159
222
|
* L'envoi passe par la **file bornée** : si le bus n'acquitte plus, les
|
|
160
223
|
* publications suivantes sont jetées et comptées plutôt que d'empiler des
|
|
161
224
|
* mégaoctets dans le client Redis (sémantique at-most-once déjà assumée par le
|
|
162
|
-
* port ; le client realtime re-synchronise).
|
|
225
|
+
* port ; le client realtime re-synchronise). Un ordre de contrôle, lui, part
|
|
226
|
+
* toujours (cf {@link BackplanePublishQueue.send}).
|
|
163
227
|
*/
|
|
164
228
|
publish(channel: string, payload: unknown): void;
|
|
165
229
|
/** Un seul handler d'ingress ; un appel ultérieur remplace le précédent. */
|
|
166
230
|
onMessage(handler: BackplaneHandler): void;
|
|
167
|
-
/**
|
|
231
|
+
/**
|
|
232
|
+
* Laisse partir les publications déjà émises (acquittées ou refusées par Redis), puis désabonne du canal et détache le handler. Idempotent.
|
|
233
|
+
*
|
|
234
|
+
* Sans l'attente, un arrêt suivi de la sortie du processus perdait ce qui
|
|
235
|
+
* était encore dans la file — la preuve de vie FINALE de la présence comprise,
|
|
236
|
+
* et les pairs gardaient alors ce pod jusqu'au délai d'oubli. Un Redis qui ne
|
|
237
|
+
* répond plus fait attendre : l'appelant borne (l'arrêt du module le fait).
|
|
238
|
+
*/
|
|
168
239
|
stop(): Promise<void>;
|
|
169
240
|
describe(): IBackplaneInfo;
|
|
170
241
|
}
|