@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,4 +1,12 @@
|
|
|
1
|
-
import { JsonRpcPeer, isPlatformChannel } from "nodefony";
|
|
1
|
+
import { JsonRpcPeer, isPlatformChannel, type IFrameCodec, type IFrameCodecSet, type RealtimeFrameData } from "nodefony";
|
|
2
|
+
import { RealtimeConnection } from "./RealtimeConnection.js";
|
|
3
|
+
import { type IChannelPresenceView } from "./channelPresence.js";
|
|
4
|
+
import type { IRealtimeConnection } from "../../interfaces/IRealtimeConnection.js";
|
|
5
|
+
import type { IChannelAdmission, IChannelPolicies, UserChannelPayload } from "nodefony";
|
|
6
|
+
import { AdmissionBudget, type IAdmissionLimits } from "./admissionBudget.js";
|
|
7
|
+
import type { IChannelAdmissionRule } from "./connectionAdmission.js";
|
|
8
|
+
import type { IRealtimeConnectionState } from "../../interfaces/IRealtimeConnectionState.js";
|
|
9
|
+
import type { ContractServerEmit, EventNames, EventPayload, IRealtimeContract } from "nodefony";
|
|
2
10
|
import type { RealtimePublish } from "../../interfaces/IRealtimeController.js";
|
|
3
11
|
import type { IClientLogsLimits } from "../../interfaces/IClientLogsLimits.js";
|
|
4
12
|
import type { IChannelPolicy } from "../../interfaces/IChannelPolicy.js";
|
|
@@ -26,8 +34,21 @@ export type OriginGuard = (origin: string | undefined) => boolean;
|
|
|
26
34
|
* `@nodefony/security` au boot via {@link RealtimeService.setFrameAuthorizer}.
|
|
27
35
|
* Doctrine SYNC stricte (cf `JsonRpcPeerOptions.beforeDispatch`) : un `await` par
|
|
28
36
|
* frame coûterait une microtask et sérialiserait le pipeline RPC du peer.
|
|
37
|
+
*
|
|
38
|
+
* `judgesWrite` : le verrou sait juger une politique d'ÉCRITURE distincte de la
|
|
39
|
+
* lecture (ADR-0022 §6, décision 1 quater). Un verrou qui ne le déclare pas ne
|
|
40
|
+
* lirait que la lecture, et laisserait ouverte une écriture plus stricte : le
|
|
41
|
+
* hub refuse alors de démarrer dès qu'une telle écriture est déclarée.
|
|
42
|
+
*
|
|
43
|
+
* `explain` : la phrase qui dit POURQUOI une trame a été refusée, quand le
|
|
44
|
+
* verrou sait la rédiger (`null` sinon). Lu après un refus seulement
|
|
45
|
+
* ({@link RealtimeHub.explainDenial}) — jamais sur le trajet d'une trame
|
|
46
|
+
* acceptée. Elle va au journal du serveur, et au client hors production.
|
|
29
47
|
*/
|
|
30
|
-
export type FrameAuthorizer = (frame: unknown, token: IRealtimeToken) => boolean
|
|
48
|
+
export type FrameAuthorizer = ((frame: unknown, token: IRealtimeToken) => boolean) & {
|
|
49
|
+
readonly judgesWrite?: true;
|
|
50
|
+
readonly explain?: (frame: unknown, token: IRealtimeToken) => string | null;
|
|
51
|
+
};
|
|
31
52
|
/**
|
|
32
53
|
* Seuil d'alerte slow-consumer (octets de `bufferedAmount`). Au-delà, la connexion
|
|
33
54
|
* est comptée comme « lente » par la sonde (PAS encore de drop/close — la sonde MESURE
|
|
@@ -108,18 +129,62 @@ export interface IRevocableConnection {
|
|
|
108
129
|
* Sink d'un canal : pousse une charge vers UNE connexion abonnée (son `peer.notify`).
|
|
109
130
|
* 1 sink = 1 connexion. Le hub fan-out la charge à tous les sinks d'un canal.
|
|
110
131
|
*
|
|
111
|
-
* `
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* sink se débrouille comme avant — un sink
|
|
115
|
-
* simplement l'argument.
|
|
132
|
+
* `frame` n'est fourni que si le canal a déclaré un {@link ChannelFrameBuilder} ET
|
|
133
|
+
* qu'il a plusieurs abonnés : la frame est la MÊME pour tous, le sink en demande
|
|
134
|
+
* l'encodage pour le codec de SA connexion ({@link ChannelFrame.encodeFor}) au lieu
|
|
135
|
+
* de refaire le travail. Absent, le sink se débrouille comme avant — un sink à un
|
|
136
|
+
* seul paramètre ignore simplement l'argument.
|
|
137
|
+
*/
|
|
138
|
+
export type ChannelSink = (payload: unknown, frame?: ChannelFrame) => void;
|
|
139
|
+
/**
|
|
140
|
+
* Fabrique la frame protocolaire d'un canal (l'objet, pas ses octets), fournie par
|
|
141
|
+
* l'abonné : le hub ignore tout du protocole. Appelée **au plus une fois par
|
|
142
|
+
* publication**, jamais par abonné ; l'encodage, lui, se fait une fois par codec.
|
|
116
143
|
*/
|
|
117
|
-
export type
|
|
144
|
+
export type ChannelFrameBuilder = (payload: unknown) => unknown;
|
|
118
145
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
146
|
+
* Frame d'UNE publication diffusée à plusieurs abonnés, encodée **au plus une fois
|
|
147
|
+
* par codec** (ADR-0022 §2) : un canal suivi par des connexions texte et CBOR paie
|
|
148
|
+
* deux encodages, jamais un par connexion. L'encodage est paresseux — un codec
|
|
149
|
+
* qu'aucun abonné n'utilise ne coûte rien.
|
|
150
|
+
*
|
|
151
|
+
* Deux places suffisent : un hub sert au plus son codec texte et son codec
|
|
152
|
+
* binaire. Un troisième codec reste servi, sans mise en cache.
|
|
121
153
|
*/
|
|
122
|
-
export
|
|
154
|
+
export declare class ChannelFrame {
|
|
155
|
+
private readonly message;
|
|
156
|
+
private codecA;
|
|
157
|
+
private dataA;
|
|
158
|
+
private codecB;
|
|
159
|
+
private dataB;
|
|
160
|
+
private claimedA;
|
|
161
|
+
private claimedB;
|
|
162
|
+
/** @param message - la frame protocolaire, telle que rendue par le {@link ChannelFrameBuilder}. */
|
|
163
|
+
constructor(message: unknown);
|
|
164
|
+
/**
|
|
165
|
+
* Rend la frame encodée pour un codec, en l'encodant à la première demande.
|
|
166
|
+
*
|
|
167
|
+
* @param codec - le codec de la connexion qui va l'écrire.
|
|
168
|
+
* @returns la trame à écrire telle quelle, ou `undefined` si la frame n'est pas
|
|
169
|
+
* encodable : l'abonné qui en {@link ChannelFrame.claimFailure | réclame}
|
|
170
|
+
* l'échec reprend son chemin habituel, qui porte le filet (journal).
|
|
171
|
+
*/
|
|
172
|
+
encodeFor(codec: IFrameCodec): RealtimeFrameData | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* Réclame l'échec d'encodage de cette frame pour un codec : `true` au PREMIER
|
|
175
|
+
* abonné qui le demande, `false` ensuite.
|
|
176
|
+
*
|
|
177
|
+
* Un seul abonné reprend alors son chemin habituel, dont le filet journalise
|
|
178
|
+
* l'échec ; les autres sautent la trame, qu'ils ne pourraient pas encoder non
|
|
179
|
+
* plus. Sans cela, une charge non encodable — un `$bytes` forgé par un client,
|
|
180
|
+
* une valeur cyclique — coûtait un encodage et une ligne de journal PAR abonné.
|
|
181
|
+
* Un codec hors des deux places réclame à chaque fois : il n'est pas retenu.
|
|
182
|
+
*
|
|
183
|
+
* @param codec - le codec dont {@link ChannelFrame.encodeFor} a rendu `undefined`.
|
|
184
|
+
* @returns `true` si l'appelant doit dire l'échec.
|
|
185
|
+
*/
|
|
186
|
+
claimFailure(codec: IFrameCodec): boolean;
|
|
187
|
+
}
|
|
123
188
|
/**
|
|
124
189
|
* Fabrique le provider PARTAGÉ d'un canal (listener/ticker qui pousse via `publish`).
|
|
125
190
|
* Appelée UNE fois, au 1ᵉʳ abonné. Renvoie son `dispose` (appelé au dernier
|
|
@@ -128,6 +193,17 @@ export type ChannelSerializer = (payload: unknown) => string;
|
|
|
128
193
|
* a déclenché la création (elle peut fermer alors que le provider partagé survit).
|
|
129
194
|
*/
|
|
130
195
|
export type ChannelFactory = (channel: string, publish: RealtimePublish) => (() => void) | null;
|
|
196
|
+
/**
|
|
197
|
+
* Qui a déclaré une sorte de politique sous un nom, et laquelle — retenu au démarrage pour refuser deux classes en désaccord sur un même nom.
|
|
198
|
+
*/
|
|
199
|
+
export interface IChannelPolicyClaim {
|
|
200
|
+
/** Constructeur du contrôleur qui a déclaré le premier. */
|
|
201
|
+
readonly owner: object;
|
|
202
|
+
/** Son nom, pour le message de refus. */
|
|
203
|
+
readonly ownerName: string;
|
|
204
|
+
/** La politique déclarée. */
|
|
205
|
+
readonly policy: IChannelPolicy;
|
|
206
|
+
}
|
|
131
207
|
/**
|
|
132
208
|
* RealtimeHub — broker temps réel **PAR INSTANCE** (1 pod = 1 process = 1 hub local).
|
|
133
209
|
*
|
|
@@ -157,6 +233,12 @@ export type ChannelFactory = (channel: string, publish: RealtimePublish) => (()
|
|
|
157
233
|
*/
|
|
158
234
|
export declare class RealtimeHub {
|
|
159
235
|
#private;
|
|
236
|
+
/**
|
|
237
|
+
* Budget des admissions de l'exemplaire (appels d'`allow` en vol, coupe-circuit,
|
|
238
|
+
* `allowTimedOutTotal`/`allowSaturatedTotal`) — partagé par les files de toutes
|
|
239
|
+
* les connexions. Bornes posées par {@link setAdmissionLimits} au boot.
|
|
240
|
+
*/
|
|
241
|
+
readonly admissionBudget: AdmissionBudget;
|
|
160
242
|
/**
|
|
161
243
|
* Abonnement demandé par une **connexion cliente** — même chose que
|
|
162
244
|
* {@link subscribe}, plus le plancher des canaux de plateforme.
|
|
@@ -171,9 +253,11 @@ export declare class RealtimeHub {
|
|
|
171
253
|
* sécurité en pose un, c'est lui qui décide (avec les rôles, ce que le hub ne
|
|
172
254
|
* sait pas faire) et le plancher s'efface.
|
|
173
255
|
*
|
|
256
|
+
* @param member - la connexion qui s'abonne : inscrite au registre du canal,
|
|
257
|
+
* elle devient un membre identifiable ({@link channelMembers}).
|
|
174
258
|
* @returns `true` si abonné, `false` si canal inconnu ou fermé par le plancher.
|
|
175
259
|
*/
|
|
176
|
-
subscribeClient(channel: string, sink: ChannelSink, factory: ChannelFactory,
|
|
260
|
+
subscribeClient(channel: string, sink: ChannelSink, factory: ChannelFactory, buildFrame?: ChannelFrameBuilder, member?: RealtimeConnection): boolean;
|
|
177
261
|
/**
|
|
178
262
|
* Abonne un consommateur qui **répond du canal** : il apporte la `factory`, donc
|
|
179
263
|
* la réponse à « ce canal existe-t-il, et qui le produit ? ». Le provider partagé
|
|
@@ -187,7 +271,7 @@ export declare class RealtimeHub {
|
|
|
187
271
|
*
|
|
188
272
|
* @returns `true` si abonné, `false` si canal inconnu (`factory` a renvoyé `null`).
|
|
189
273
|
*/
|
|
190
|
-
subscribe(channel: string, sink: ChannelSink, factory: ChannelFactory,
|
|
274
|
+
subscribe(channel: string, sink: ChannelSink, factory: ChannelFactory, buildFrame?: ChannelFrameBuilder): boolean;
|
|
191
275
|
/**
|
|
192
276
|
* **Écoute passive** — un service du serveur reçoit ce qui passe sur un canal
|
|
193
277
|
* sans prétendre le produire. C'est la porte de {@link ServerRealtimeSocket} :
|
|
@@ -213,6 +297,131 @@ export declare class RealtimeHub {
|
|
|
213
297
|
* et retire le canal (libère timers/listeners). No-op si non abonné.
|
|
214
298
|
*/
|
|
215
299
|
unsubscribe(channel: string, sink: ChannelSink): void;
|
|
300
|
+
/**
|
|
301
|
+
* Présence d'un canal vue de CE pod (ADR-0025 P3) — `count`, `unnamed`, `members` (toujours, quel que soit le seuil de la trame), `partial`.
|
|
302
|
+
*
|
|
303
|
+
* La voie de l'observateur qui lit sans figurer (modérateur, tableau de
|
|
304
|
+
* bord). Chemin froid : une copie à chaque appel.
|
|
305
|
+
*
|
|
306
|
+
* @param channel - nom exact du canal.
|
|
307
|
+
* @returns la vue (vide si personne n'est là), ou `null` si la politique du
|
|
308
|
+
* canal ne déclare pas `presence`.
|
|
309
|
+
*/
|
|
310
|
+
presence(channel: string): IChannelPresenceView | null;
|
|
311
|
+
/**
|
|
312
|
+
* Arrêt propre de la présence répartie (ADR-0025 P5) — le dernier lot part, puis une preuve de vie FINALE : les pairs retirent aussitôt les présents de ce serveur, sans attendre le délai d'oubli. Plus rien ne part ensuite ; idempotent, sans effet sans bus.
|
|
313
|
+
*
|
|
314
|
+
* Appelé par le module à l'arrêt du noyau (`onTerminate`), AVANT que le bus ne
|
|
315
|
+
* se ferme. Les connexions qui se ferment ensuite ne publient plus rien : la
|
|
316
|
+
* preuve de vie finale les a déjà retirées chez les pairs.
|
|
317
|
+
*/
|
|
318
|
+
endPresence(): void;
|
|
319
|
+
/**
|
|
320
|
+
* Un abonnement vient d'être acquitté : sur un canal à présence, la liste complète part vers CETTE connexion, puis les changements en attente partent si la fenêtre le permet.
|
|
321
|
+
*
|
|
322
|
+
* Appelé par le contrôleur APRÈS `realtime:subscribed`, à chaque (re-)`subscribe`
|
|
323
|
+
* — aucune borne : la contre-pression peut avoir jeté la liste d'avant sans
|
|
324
|
+
* le dire, et c'est le re-`subscribe` qui répare (ADR-0025 P2 bis). La liste
|
|
325
|
+
* est encodée une fois par envoi de changements et par codec, puis servie
|
|
326
|
+
* telle quelle.
|
|
327
|
+
*
|
|
328
|
+
* @param channel - nom exact du canal acquitté.
|
|
329
|
+
* @param connection - la connexion abonnée.
|
|
330
|
+
*/
|
|
331
|
+
presenceSubscribed(channel: string, connection: RealtimeConnection): void;
|
|
332
|
+
/**
|
|
333
|
+
* Les connexions abonnées à un canal par un client — **local à ce pod** : les membres servis par un autre exemplaire n'y figurent pas.
|
|
334
|
+
*
|
|
335
|
+
* Les écouteurs et services du serveur (`listen`, `subscribe`) ne sont pas
|
|
336
|
+
* des membres. Chemin froid : une copie à chaque appel.
|
|
337
|
+
*
|
|
338
|
+
* @param channel - nom exact du canal.
|
|
339
|
+
* @returns les membres de ce pod, vide si le canal n'en a aucun ici.
|
|
340
|
+
*/
|
|
341
|
+
channelMembers(channel: string): IRealtimeConnection[];
|
|
342
|
+
/**
|
|
343
|
+
* Abonne d'office une connexion à un canal — **local à ce pod** : seule une connexion acceptée par ce processus peut l'être.
|
|
344
|
+
*
|
|
345
|
+
* Même porte qu'un `subscribe` du client (ADR-0022 §6 décision 2) : la
|
|
346
|
+
* politique fixe du canal (rôles, scopes, plancher de plateforme), puis sa
|
|
347
|
+
* règle `allow` s'il en déclare une. Un refus n'abonne pas, et seul
|
|
348
|
+
* l'appelant l'apprend ; jamais un abonnement qui contourne `allow` — le
|
|
349
|
+
* client rejoue ses abonnements à chaque reconnexion, et ce rejeu passe par
|
|
350
|
+
* `allow` : l'application écrit donc l'appartenance là où `allow` la lit.
|
|
351
|
+
*
|
|
352
|
+
* Le client est notifié par `realtime:subscribed { channel, imposed: true }`
|
|
353
|
+
* et inscrit le canal dans ses abonnements ; il peut le quitter.
|
|
354
|
+
*
|
|
355
|
+
* Plusieurs exemplaires : la requête qui décide doit atteindre le processus
|
|
356
|
+
* qui tient la socket — la passer par la socket elle-même (`@RealtimeInbound`,
|
|
357
|
+
* qui reçoit la connexion), ou faire autoriser l'entrée par `allow` (registre
|
|
358
|
+
* partagé) et laisser le navigateur s'abonner.
|
|
359
|
+
*
|
|
360
|
+
* @param channel - nom exact du canal.
|
|
361
|
+
* @param connection - la connexion à abonner (reçue par un gestionnaire `@RealtimeInbound`, ou lue dans {@link channelMembers}).
|
|
362
|
+
* @returns `true` une fois l'abonnement acquitté ; `false` si refusé, si la
|
|
363
|
+
* connexion est fermée ou inconnue de ce processus, ou si le client s'en
|
|
364
|
+
* désabonne avant le verdict.
|
|
365
|
+
*/
|
|
366
|
+
subscribeConnection(channel: string, connection: IRealtimeConnection): Promise<boolean>;
|
|
367
|
+
/**
|
|
368
|
+
* Retire une connexion d'un canal — **local à ce pod** ; elle reçoit `realtime:closed { reason: "evicted" }`.
|
|
369
|
+
*
|
|
370
|
+
* ⚠️ **Éjecter coupe la connexion du canal, seul `allow` exclut**
|
|
371
|
+
* (ADR-0022 §6 décision 3). Le client ne rejoue pas un canal dont il a été
|
|
372
|
+
* éjecté, mais c'est une politesse : une page rechargée refait `subscribe`.
|
|
373
|
+
* Sur un canal à `allow`, l'éjecté revient si l'application n'a pas retiré
|
|
374
|
+
* l'appartenance là où `allow` la lit ; sur un canal sans `allow`, sa
|
|
375
|
+
* politique fixe le laisse rentrer.
|
|
376
|
+
*
|
|
377
|
+
* @param channel - nom exact du canal.
|
|
378
|
+
* @param connection - la connexion à retirer.
|
|
379
|
+
* @returns `false` si la connexion n'est pas membre du canal, est fermée, ou
|
|
380
|
+
* inconnue de ce processus — jamais un geste muet.
|
|
381
|
+
*/
|
|
382
|
+
evictConnection(channel: string, connection: IRealtimeConnection): boolean;
|
|
383
|
+
/**
|
|
384
|
+
* Termine un canal pour tous ses membres, **sur tous les serveurs** : chacun reçoit `realtime:closed { reason: "ended" }`, et le producteur est libéré.
|
|
385
|
+
*
|
|
386
|
+
* **Traverse les pods** : avec un backplane branché, l'ordre part sur le bus
|
|
387
|
+
* (`PLATFORM_BUS_CHANNELS.control`, scellé comme toute publication) et chaque
|
|
388
|
+
* serveur l'applique à SES membres. Livraison « au plus une fois » du bus :
|
|
389
|
+
* un pod qui manque l'ordre, ou qui démarre après, est rattrapé par la
|
|
390
|
+
* fabrique, qui rend `null` pour une instance terminée. Un canal de la
|
|
391
|
+
* plateforme (`nodefony:`) reste local — il décrit un pod, pas une partie.
|
|
392
|
+
*
|
|
393
|
+
* Les écouteurs du serveur (`listen`) restent inscrits, sur un canal redevenu
|
|
394
|
+
* PASSIF : le prochain `subscribe` d'un client rejoue la fabrique, et c'est
|
|
395
|
+
* elle qui tranche. **Seule la fabrique clôt un canal** (ADR-0021 S4) — le
|
|
396
|
+
* hub ne garde aucune marque « fermé » : une fabrique qui rend encore un
|
|
397
|
+
* producteur pour ce nom le rouvre. D'où la règle d'écriture d'un canal à
|
|
398
|
+
* durée de vie : son nom porte l'identifiant de l'instance
|
|
399
|
+
* (`room:42:<partie>`), et la fabrique rend `null` pour une instance terminée.
|
|
400
|
+
*
|
|
401
|
+
* @param channel - nom exact du canal.
|
|
402
|
+
* @returns le nombre de membres de CE pod à qui la fin a été notifiée — les
|
|
403
|
+
* autres serveurs l'appliquent sans compte rendu.
|
|
404
|
+
*/
|
|
405
|
+
closeChannel(channel: string): number;
|
|
406
|
+
/**
|
|
407
|
+
* Éjecte un compte d'un canal, **sur tous les serveurs** : chacune de ses connexions membres reçoit `realtime:closed { reason: "evicted" }`.
|
|
408
|
+
*
|
|
409
|
+
* Tous les onglets du compte sont visés, où qu'ils soient servis ; l'ordre
|
|
410
|
+
* traverse les pods comme {@link closeChannel}. ⚠️ **Éjecter coupe, seul
|
|
411
|
+
* `allow` exclut** (ADR-0022 §6 décision 3) : pour que le compte ne revienne
|
|
412
|
+
* pas, l'application retire l'appartenance là où `allow` la lit.
|
|
413
|
+
*
|
|
414
|
+
* ⚠️ Sur un bus PARTAGÉ, un tiers qui y écrit peut rejouer un ordre capté :
|
|
415
|
+
* sur un canal durable, il recouperait le compte à chaque réadmission (risque
|
|
416
|
+
* accepté et borné, ADR-0022 §6 d4) — bus privé exigé ; changer le secret du
|
|
417
|
+
* bus rend caducs les ordres déjà captés.
|
|
418
|
+
*
|
|
419
|
+
* @param channel - nom exact du canal.
|
|
420
|
+
* @param user - identifiant du compte (`IRealtimeConnection.userId`).
|
|
421
|
+
* @returns le nombre de connexions de CE pod éjectées. Un nom vide ou de plus
|
|
422
|
+
* de 1 024 caractères n'éjecte rien et ne part pas sur le bus.
|
|
423
|
+
*/
|
|
424
|
+
evictUser(channel: string, user: string): number;
|
|
216
425
|
/**
|
|
217
426
|
* Publie une charge sur un canal : **fan-out local** **+** propagation conditionnelle
|
|
218
427
|
* au {@link IBackplane} (autres workers/pods). C'est le point d'entrée des providers
|
|
@@ -226,8 +435,29 @@ export declare class RealtimeHub {
|
|
|
226
435
|
*
|
|
227
436
|
* Anti-boucle : un message **reçu** du backplane ne repasse PAS par ici (il appelle
|
|
228
437
|
* `publishLocal` directement) → aucune ré-émission cross-process, pas de tempête.
|
|
438
|
+
*
|
|
439
|
+
* @param payload - objet, tableau ou octets ; omise = signal sans charge.
|
|
440
|
+
* @throws TypeError si la charge est un scalaire ou `null` ({@link isRealtimePayload},
|
|
441
|
+
* ADR-0021 S1) — un scalaire se publie dans un objet (`{ value }`).
|
|
442
|
+
*/
|
|
443
|
+
publish(channel: string, payload?: object): void;
|
|
444
|
+
/**
|
|
445
|
+
* Livre une charge à UNE identité — tous ses onglets, tous les serveurs, et personne d'autre (ADR-0022 §6, D1).
|
|
446
|
+
*
|
|
447
|
+
* Publie sur son canal personnel `user:<userId>` : seuls les onglets de cette
|
|
448
|
+
* identité ont pu s'y abonner (politique intégrée, admission par égalité de
|
|
449
|
+
* chaîne), et le préfixe traverse le bus dès qu'un bus est branché. Rien à
|
|
450
|
+
* déclarer côté application. Personne n'écoute ? La charge est perdue, sans
|
|
451
|
+
* erreur : c'est une diffusion, pas une boîte aux lettres.
|
|
452
|
+
*
|
|
453
|
+
* @param userId - l'`id` STABLE du compte (`RealtimeIdentity.id`), jamais le
|
|
454
|
+
* login.
|
|
455
|
+
* @param payload - objet, tableau ou octets ; omise = signal sans charge.
|
|
456
|
+
* @throws TypeError si la charge est un scalaire ou `null` (cf {@link publish}).
|
|
229
457
|
*/
|
|
230
|
-
|
|
458
|
+
publishToUser(userId: string, payload?: object): void;
|
|
459
|
+
/** Préfixes déclarés diffusables entre serveurs, dans l'ordre de déclaration — lu par la ligne de démarrage. */
|
|
460
|
+
broadcastPrefixes(): readonly string[];
|
|
231
461
|
/**
|
|
232
462
|
* Fan-out d'une charge aux **seuls abonnés locaux** d'un canal (process courant).
|
|
233
463
|
* NE propage PAS au backplane → c'est la voie d'**ingress** d'un message venu d'un
|
|
@@ -269,7 +499,11 @@ export declare class RealtimeHub {
|
|
|
269
499
|
* sur le chemin chaud). À équilibrer par {@link unregisterConnection} au close.
|
|
270
500
|
*/
|
|
271
501
|
registerConnection(conn: IRealtimeConnProbe): void;
|
|
272
|
-
/**
|
|
502
|
+
/**
|
|
503
|
+
* Retire une connexion du registre de la sonde (au close) et verse ses cumuls à
|
|
504
|
+
* ceux des connexions fermées : les totaux de {@link probe} restent monotones.
|
|
505
|
+
* No-op si absente — une double sortie ne compte pas deux fois.
|
|
506
|
+
*/
|
|
273
507
|
unregisterConnection(conn: IRealtimeConnProbe): void;
|
|
274
508
|
/**
|
|
275
509
|
* Inscrit une connexion à identité RÉVOCABLE au registre de
|
|
@@ -361,6 +595,30 @@ export declare class RealtimeHub {
|
|
|
361
595
|
* `defineRealtimeConfig().slowConsumer.bytes` (sinon la clé de config serait morte).
|
|
362
596
|
*/
|
|
363
597
|
setSlowConsumerBytes(bytes: number): void;
|
|
598
|
+
/**
|
|
599
|
+
* Les codecs de frame du serveur (ADR-0022 §2) : le texte, et le codec binaire
|
|
600
|
+
* accordé aux clients qui l'offrent — `null` tant que la configuration n'en
|
|
601
|
+
* active aucun. Lu une fois par connexion, au handshake, jamais par trame.
|
|
602
|
+
*/
|
|
603
|
+
get frameCodecs(): IFrameCodecSet;
|
|
604
|
+
/**
|
|
605
|
+
* Pose le codec binaire du serveur — 1× au boot par `RealtimeService.init()`,
|
|
606
|
+
* depuis la clé `codec` de la configuration. Sans argument : texte seul.
|
|
607
|
+
*
|
|
608
|
+
* @param codec - le codec activé (`CBOR_FRAME_CODEC` de `nodefony/cbor`).
|
|
609
|
+
*/
|
|
610
|
+
setFrameCodec(codec?: IFrameCodec): void;
|
|
611
|
+
/**
|
|
612
|
+
* Le codec des connexions texte — celui qu'emprunte aussi le REST du plan
|
|
613
|
+
* d'administration, pour qu'une valeur lue par `GET` soit celle que la socket
|
|
614
|
+
* livre (#618 D9). Avec un codec binaire activé, c'est le JSON à convention
|
|
615
|
+
* d'octets ; sinon le JSON natif.
|
|
616
|
+
*/
|
|
617
|
+
get textFrameCodec(): IFrameCodec;
|
|
618
|
+
/**
|
|
619
|
+
* Pose les bornes des admissions (`authenticateTimeoutMs`, `allowTimeoutMs`, `maxPendingAllowsPerConnection`, `maxPendingAllowsTotal`) — 1× au boot par `RealtimeService.init()`.
|
|
620
|
+
*/
|
|
621
|
+
setAdmissionLimits(limits: IAdmissionLimits): void;
|
|
364
622
|
/**
|
|
365
623
|
* Associe un token au peer (posé par `RealtimeController.onHandshake()` après
|
|
366
624
|
* succès `authenticator.authenticate()` ou fallback anonyme). Lazy alloc de
|
|
@@ -377,6 +635,10 @@ export declare class RealtimeHub {
|
|
|
377
635
|
* **Seam #1 (P13 → P6)** — pose le verrou de frame (cf {@link FrameAuthorizer}).
|
|
378
636
|
* `null` retire la politique (bypass total). Posé 1× au boot par
|
|
379
637
|
* `@nodefony/security` depuis les zones `realtime: true`. Cold path.
|
|
638
|
+
*
|
|
639
|
+
* @throws BootConfigurationError Le verrou ne déclare pas `judgesWrite`
|
|
640
|
+
* alors qu'une écriture distincte de la lecture est inscrite : il la
|
|
641
|
+
* laisserait ouverte.
|
|
380
642
|
*/
|
|
381
643
|
setFrameAuthorizer(authorizer: FrameAuthorizer | null, options?: {
|
|
382
644
|
readonly silentProbe?: FrameAuthorizer | null;
|
|
@@ -420,10 +682,7 @@ export declare class RealtimeHub {
|
|
|
420
682
|
*/
|
|
421
683
|
hasUnenforcedChannelPolicies(): boolean;
|
|
422
684
|
/**
|
|
423
|
-
* **Seam #1b** —
|
|
424
|
-
* (`@RealtimeChannel`/`@RealtimeInbound` avec opts). Appelé au handshake par
|
|
425
|
-
* le controller pour CHAQUE canal décoré (idempotent : même nom = écrase, les
|
|
426
|
-
* controllers d'un même endpoint déclarent la même policy). Cold path.
|
|
685
|
+
* **Seam #1b** — pose la politique d'un canal/méthode en REMPLAÇANT ce que ce nom déclarait (voie programmatique ; les décorateurs passent par {@link RealtimeHub.declareChannelPolicy}). Cold path.
|
|
427
686
|
*
|
|
428
687
|
* Deux formes de nom, et la seconde garde une FAMILLE :
|
|
429
688
|
*
|
|
@@ -433,15 +692,42 @@ export declare class RealtimeHub {
|
|
|
433
692
|
* Le `*` remplace n'importe quelle suite de caractères ; tout le reste du
|
|
434
693
|
* motif est littéral (échappé), y compris `.` et `(`.
|
|
435
694
|
*
|
|
436
|
-
* Précédence
|
|
437
|
-
*
|
|
438
|
-
* **
|
|
695
|
+
* Précédence, SORTE PAR SORTE (ADR-0022 §6, décision 1 quater) : la lecture
|
|
696
|
+
* d'un nom vient de la déclaration la plus spécifique qui POSE une lecture —
|
|
697
|
+
* le nom **exact**, puis le motif au littéral le plus long ; l'écriture de
|
|
698
|
+
* même. C'est ainsi qu'on ouvre une exception dans une famille gardée, sans
|
|
699
|
+
* effacer la sorte que l'exception ne déclare pas.
|
|
439
700
|
*
|
|
440
|
-
* @param name
|
|
701
|
+
* @param name - nom exact du canal, de la méthode entrante ou de l'action, ou
|
|
441
702
|
* MOTIF de famille s'il contient `*`.
|
|
442
|
-
* @param policy - exigences ({@link IChannelPolicy}) lues par
|
|
703
|
+
* @param policy - exigences de LECTURE ({@link IChannelPolicy}) lues par
|
|
704
|
+
* security ; seules exigées au `subscribe`.
|
|
705
|
+
* @param admission - instance de la classe d'admission quand `policy.allow`
|
|
706
|
+
* en est une ; ignorée sinon.
|
|
707
|
+
* @param write - exigences d'ÉCRITURE, exigées EN PLUS de la lecture sur une
|
|
708
|
+
* trame entrante ; `null` : ce nom n'en déclare pas.
|
|
709
|
+
* @throws BootConfigurationError Classe d'admission sans instance ; `allow`
|
|
710
|
+
* sur une écriture qui rencontre une lecture différente ; écriture distincte
|
|
711
|
+
* face à un verrou qui ne sait pas la juger.
|
|
712
|
+
*/
|
|
713
|
+
registerChannelPolicy(name: string, policy: IChannelPolicy, admission?: IChannelAdmission, write?: IChannelPolicy | null): void;
|
|
714
|
+
/**
|
|
715
|
+
* Déclare la lecture et/ou l'écriture d'un nom en les AJOUTANT à ce qu'il déclarait — une sorte posée remplace la sienne, une sorte `null` garde celle d'avant. Voie des décorateurs (`channelPolicyRegistry.ts#registerControllerPolicies`). Cold path.
|
|
716
|
+
*
|
|
717
|
+
* @param name - nom exact ou motif.
|
|
718
|
+
* @param read - lecture déclarée, ou `null`.
|
|
719
|
+
* @param write - écriture déclarée, ou `null`.
|
|
720
|
+
* @param admission - instance de la classe d'admission désignée par l'une ou
|
|
721
|
+
* l'autre ; ignorée sinon.
|
|
722
|
+
* @throws BootConfigurationError Mêmes refus que {@link RealtimeHub.registerChannelPolicy}.
|
|
723
|
+
*/
|
|
724
|
+
declareChannelPolicy(name: string, read: IChannelPolicy | null, write: IChannelPolicy | null, admission?: IChannelAdmission): void;
|
|
725
|
+
/**
|
|
726
|
+
* Pose le défaut FERMÉ d'une action (`{ authenticated: true }`) — son ÉCRITURE tant que rien d'exact ne déclare son nom et qu'aucun motif ne pose d'écriture ; une lecture de motif s'y ajoute sans l'ouvrir. Idempotent : une poignée de main n'en paie qu'une lecture.
|
|
727
|
+
*
|
|
728
|
+
* @param name - nom de l'action.
|
|
443
729
|
*/
|
|
444
|
-
|
|
730
|
+
declareActionDefault(name: string): void;
|
|
445
731
|
/**
|
|
446
732
|
* Nombre maximum de `*` dans un motif.
|
|
447
733
|
*
|
|
@@ -459,6 +745,51 @@ export declare class RealtimeHub {
|
|
|
459
745
|
* la frame `subscribe`/inbound. O(1), 0 alloc quand le registre est vide.
|
|
460
746
|
*/
|
|
461
747
|
resolveChannelPolicy(name: string): IChannelPolicy | null;
|
|
748
|
+
/**
|
|
749
|
+
* Lecture ET écriture qui s'appliquent à ce nom, en UNE résolution — `null` si aucune politique ne le couvre (ADR-0022 §6, décision 1 quater).
|
|
750
|
+
*
|
|
751
|
+
* Même résolution que {@link resolveChannelPolicy} : l'entrée la plus
|
|
752
|
+
* spécifique (nom exact, puis motif) gouverne les deux sortes. Rendue telle
|
|
753
|
+
* quelle : aucune allocation, une seule recherche par trame jugée.
|
|
754
|
+
*
|
|
755
|
+
* @param name - nom du canal, de la méthode entrante ou de l'action.
|
|
756
|
+
*/
|
|
757
|
+
resolveChannelPolicies(name: string): IChannelPolicies | null;
|
|
758
|
+
/**
|
|
759
|
+
* Retient la PREMIÈRE classe qui déclare une sorte de politique sous ce nom, et rend celle qui l'avait déjà déclarée — pour refuser au démarrage deux classes en désaccord.
|
|
760
|
+
*
|
|
761
|
+
* @param name - nom exact ou motif.
|
|
762
|
+
* @param kind - sorte déclarée : lecture ou écriture.
|
|
763
|
+
* @param owner - constructeur du contrôleur qui déclare.
|
|
764
|
+
* @param policy - la politique déclarée.
|
|
765
|
+
* @returns la déclaration antérieure de cette sorte, ou `null`.
|
|
766
|
+
*/
|
|
767
|
+
claimChannelPolicy(name: string, kind: "read" | "write", owner: object, policy: IChannelPolicy): IChannelPolicyClaim | null;
|
|
768
|
+
/**
|
|
769
|
+
* Règle d'admission prête à appeler pour ce canal — `null` s'il ne déclare pas `allow`. Lue au `subscribe`, jamais par trame.
|
|
770
|
+
*
|
|
771
|
+
* Même résolution que {@link resolveChannelPolicy} (nom exact, puis motif le
|
|
772
|
+
* plus spécifique) : la règle suit TOUJOURS la politique qui s'applique.
|
|
773
|
+
*/
|
|
774
|
+
resolveChannelAdmission(name: string): IChannelAdmissionRule | null;
|
|
775
|
+
/**
|
|
776
|
+
* Les politiques de cette classe de contrôleur sont-elles déjà inscrites ? Une poignée de main n'en paie que ce `has`.
|
|
777
|
+
*
|
|
778
|
+
* @param owner - le constructeur du contrôleur.
|
|
779
|
+
*/
|
|
780
|
+
hasPolicyOwner(owner: object): boolean;
|
|
781
|
+
/**
|
|
782
|
+
* Marque les politiques de cette classe comme inscrites — APRÈS l'inscription réussie, jamais avant : une politique refusée doit l'être de nouveau à la poignée de main suivante, pas laisser le canal ouvert.
|
|
783
|
+
*
|
|
784
|
+
* @param owner - le constructeur du contrôleur.
|
|
785
|
+
*/
|
|
786
|
+
addPolicyOwner(owner: object): void;
|
|
787
|
+
/**
|
|
788
|
+
* Ce nom EXACT porte-t-il une déclaration — motifs ignorés ?
|
|
789
|
+
*
|
|
790
|
+
* @param name - nom d'action ou de canal.
|
|
791
|
+
*/
|
|
792
|
+
hasExactChannelPolicy(name: string): boolean;
|
|
462
793
|
/** Des politiques de canal sont-elles déclarées (au moins une) ? */
|
|
463
794
|
hasChannelPolicies(): boolean;
|
|
464
795
|
/**
|
|
@@ -466,14 +797,14 @@ export declare class RealtimeHub {
|
|
|
466
797
|
* DYNAMIQUE n'est couvert par aucune politique déclarée, alors que d'autres
|
|
467
798
|
* canaux du module en portent une.
|
|
468
799
|
*
|
|
469
|
-
* Le registre des politiques
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
* canaux qu'aucun motif ne couvre
|
|
476
|
-
* de l'outil.
|
|
800
|
+
* Le registre des politiques lit le nom **EXACT**, puis les **MOTIFS**
|
|
801
|
+
* déclarés ({@link resolveChannelPolicy}) : `@RealtimeChannel` déclare
|
|
802
|
+
* `"chat:room"`, mais un canal dérivé (`chat:room:1000` — cadence, forage,
|
|
803
|
+
* suffixe) servi par la fabrique dynamique ne matche ce nom que si un motif
|
|
804
|
+
* (`chat:room:*`) le couvre. Sans motif, l'auteur croit son canal gardé ; il
|
|
805
|
+
* ne l'est pas, et rien ne le lui dit. C'est ce silence qu'on ferme ici :
|
|
806
|
+
* l'avertissement ne parle que des canaux qu'aucun motif ne couvre — un vrai
|
|
807
|
+
* trou, pas une limite de l'outil.
|
|
477
808
|
*
|
|
478
809
|
* Appelé par le contrôleur, seul à savoir par quelle voie le canal a été
|
|
479
810
|
* servi. Muet si aucune politique n'est déclarée (module aux canaux libres :
|
|
@@ -495,25 +826,53 @@ export declare class RealtimeHub {
|
|
|
495
826
|
* @param factory - {@link ChannelFactory} qui crée le provider partagé.
|
|
496
827
|
*/
|
|
497
828
|
registerSystemChannel(channel: string, factory: ChannelFactory): void;
|
|
829
|
+
/**
|
|
830
|
+
* Ouvre (ou referme) la réception des journaux du navigateur, avec ses bornes.
|
|
831
|
+
*
|
|
832
|
+
* @param limits - bornes de débit et de taille, ou `null` pour refermer le canal.
|
|
833
|
+
*/
|
|
834
|
+
setClientLogsLimits(limits: IClientLogsLimits | null): void;
|
|
835
|
+
/** Bornes en vigueur, ou `null` si le canal montant est fermé. */
|
|
836
|
+
get clientLogsLimits(): IClientLogsLimits | null;
|
|
837
|
+
/**
|
|
838
|
+
* La raison d'un refus du verrou, quand il sait la dire — `null` sinon, ou si l'explication lève.
|
|
839
|
+
*
|
|
840
|
+
* Chemin FROID : appelé après un refus, jamais par trame acceptée. Le
|
|
841
|
+
* verdict, lui, est déjà rendu : une explication fautive ne change rien à la
|
|
842
|
+
* décision, elle se tait.
|
|
843
|
+
*
|
|
844
|
+
* @param frame - la trame refusée.
|
|
845
|
+
* @param peer - la connexion qui l'a émise.
|
|
846
|
+
* @returns la phrase, ou `null`.
|
|
847
|
+
*/
|
|
848
|
+
explainDenial(frame: unknown, peer: JsonRpcPeer): string | null;
|
|
498
849
|
/**
|
|
499
850
|
* Pont hot-path entre `JsonRpcPeer.beforeDispatch` et le verrou métier : lit
|
|
500
851
|
* le token déjà caché du peer (O(1), 0 lecture base) puis délègue à
|
|
501
|
-
* l'authorizer. `true` (autorisée) si aucun verrou posé
|
|
502
|
-
*
|
|
852
|
+
* l'authorizer. `true` (autorisée) si aucun verrou posé : `beforeDispatch`
|
|
853
|
+
* est armé sur TOUTE connexion, et c'est ici que le bypass se paie — un
|
|
854
|
+
* appel et une comparaison de champ.
|
|
503
855
|
*
|
|
504
856
|
* @param frame - frame JSON-RPC entrante (brute) à autoriser.
|
|
505
857
|
* @param peer - peer émetteur (clé du mapping `peer → token`).
|
|
506
858
|
* @returns `true` si la frame peut être dispatchée, `false` si refusée.
|
|
507
859
|
*/
|
|
860
|
+
runAuthorizer(frame: unknown, peer: JsonRpcPeer): boolean;
|
|
508
861
|
/**
|
|
509
|
-
*
|
|
862
|
+
* Inscrit une connexion qui vient de retenir son premier « oui » du verrou.
|
|
510
863
|
*
|
|
511
|
-
*
|
|
864
|
+
* Appelé par `gateFrame`, jamais par trame chaude ; à équilibrer par
|
|
865
|
+
* {@link untrackRetainedVerdicts} à la fermeture.
|
|
866
|
+
*
|
|
867
|
+
* @param state - état de la connexion.
|
|
512
868
|
*/
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
|
|
516
|
-
|
|
869
|
+
trackRetainedVerdicts(state: IRealtimeConnectionState): void;
|
|
870
|
+
/**
|
|
871
|
+
* Retire une connexion fermée du registre des verdicts retenus.
|
|
872
|
+
*
|
|
873
|
+
* @param state - état de la connexion.
|
|
874
|
+
*/
|
|
875
|
+
untrackRetainedVerdicts(state: IRealtimeConnectionState): void;
|
|
517
876
|
/**
|
|
518
877
|
* Ce peer pourrait-il s'abonner à ce canal ? — même décision que `subscribe`,
|
|
519
878
|
* sans trace ni effet de bord.
|
|
@@ -533,6 +892,53 @@ export declare class RealtimeHub {
|
|
|
533
892
|
/** Dispose tous les providers et vide le hub (arrêt process / reset de test). */
|
|
534
893
|
clear(): void;
|
|
535
894
|
}
|
|
536
|
-
/**
|
|
895
|
+
/**
|
|
896
|
+
* Le hub lu à travers le contrat `C` : mêmes méthodes que {@link RealtimeHub},
|
|
897
|
+
* mais `publish` n'accepte que les canaux de `serverToClient`, chacun avec la
|
|
898
|
+
* charge qu'il déclare. C'est le MÊME objet que le hub libre — une vue de type,
|
|
899
|
+
* sans un octet ni une instruction de plus.
|
|
900
|
+
*
|
|
901
|
+
* @typeParam C - le contrat de l'application.
|
|
902
|
+
*/
|
|
903
|
+
export interface IContractRealtimeHub<C extends IRealtimeContract> extends Omit<RealtimeHub, "publish" | "publishToUser"> {
|
|
904
|
+
/**
|
|
905
|
+
* Livre à UNE identité la charge que le contrat déclare pour le canal
|
|
906
|
+
* personnel (clé gabarit `` [k: `user:${string}`]: … `` de `serverToClient`)
|
|
907
|
+
* — cf {@link RealtimeHub.publishToUser}. Contrat sans cette clé : ne compile
|
|
908
|
+
* pas, la forme du message privé se déclare d'abord.
|
|
909
|
+
*
|
|
910
|
+
* @param userId - l'`id` STABLE du compte, jamais le login.
|
|
911
|
+
*/
|
|
912
|
+
publishToUser(userId: string, payload: UserChannelPayload<ContractServerEmit<C>> & object): void;
|
|
913
|
+
/**
|
|
914
|
+
* Publie sur un canal déclaré par le contrat (fan-out local, puis backplane
|
|
915
|
+
* si le canal est broadcast) — cf {@link RealtimeHub.publish}.
|
|
916
|
+
*
|
|
917
|
+
* @throws TypeError si la charge n'est pas un objet ({@link isRealtimePayload}).
|
|
918
|
+
*/
|
|
919
|
+
publish<K extends EventNames<ContractServerEmit<C>>>(channel: K, payload: EventPayload<ContractServerEmit<C>, K> & object): void;
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Renvoie le hub realtime du process (singleton lazy) — forme LIBRE : tout nom
|
|
923
|
+
* de canal, toute charge objet.
|
|
924
|
+
*/
|
|
537
925
|
export declare function getRealtimeHub(): RealtimeHub;
|
|
926
|
+
/**
|
|
927
|
+
* Renvoie le hub realtime du process lu à travers le contrat `C` : la diffusion
|
|
928
|
+
* d'un service, d'un job ou d'une boucle de jeu est vérifiée par le contrat
|
|
929
|
+
* partagé avec le navigateur. Même objet que la forme libre : 0 octet, 0 ns.
|
|
930
|
+
*
|
|
931
|
+
* Le contrat est choisi par l'appelant — rien n'empêche d'en nommer un autre que
|
|
932
|
+
* celui du contrôleur qui sert le canal ; c'est le fichier partagé qui fait foi.
|
|
933
|
+
*
|
|
934
|
+
* @typeParam C - le contrat de l'application.
|
|
935
|
+
*
|
|
936
|
+
* @example
|
|
937
|
+
* ```ts
|
|
938
|
+
* const chat = getRealtimeHub<ChatContract>();
|
|
939
|
+
* chat.publish("chat:room", { from: "ana", text: "salut" }); // charge vérifiée
|
|
940
|
+
* getRealtimeHub().publish("n'importe:quoi", { x: 1 }); // forme libre
|
|
941
|
+
* ```
|
|
942
|
+
*/
|
|
943
|
+
export declare function getRealtimeHub<C extends IRealtimeContract>(): IContractRealtimeHub<C>;
|
|
538
944
|
export default RealtimeHub;
|
|
@@ -31,7 +31,7 @@ export declare class ServerRealtimeSocket<Emit extends EventsMap = DefaultEvents
|
|
|
31
31
|
private readonly hub;
|
|
32
32
|
constructor(hub?: import("./RealtimeHub.js").RealtimeHub);
|
|
33
33
|
/** Émet sur un canal — fan-out aux abonnés via le hub (+ backplane si broadcast). */
|
|
34
|
-
publish<K extends string>(channel: K, payload?: K extends EventNames<Emit> ? EventPayload<Emit, K> :
|
|
34
|
+
publish<K extends string>(channel: K, payload?: K extends EventNames<Emit> ? EventPayload<Emit, K> : object): void;
|
|
35
35
|
/**
|
|
36
36
|
* S'abonne (ref-compté) à un canal pour le RECEVOIR. Au 1ᵉʳ consommateur, pose un
|
|
37
37
|
* sink d'**écoute passive** auprès du hub : le service reçoit ce qui passe, sans
|