@eleven-am/pondsocket 0.1.216 → 0.1.218

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.
@@ -1,7 +1,7 @@
1
1
  import { IncomingHttpHeaders } from 'http';
2
2
  import { IncomingMessage } from 'node:http';
3
3
  import internal from 'node:stream';
4
- import { ChannelEvent, EventParams, JoinParams, PondAssigns, PondMessage, ServerActions, SystemSender, Unsubscribe, UserData } from '@eleven-am/pondsocket-common';
4
+ import { ChannelEvent, AnyPondSchema, AssignsOf, EventParams, EventsOf, JoinParamsOf, PondAssigns, PondEventMap, PondMessage, PondPresence, ServerActions, SystemSender, Unsubscribe, UserData } from '@eleven-am/pondsocket-common';
5
5
  import { WebSocket, WebSocketServer } from 'ws';
6
6
  import { ConnectionContext } from '../contexts/connectionContext';
7
7
  import { EventContext } from '../contexts/eventContext';
@@ -17,10 +17,13 @@ export interface SocketRequest {
17
17
  headers: IncomingHttpHeaders;
18
18
  address: string;
19
19
  }
20
- export type ConnectionHandler<Path extends string> = (ctx: ConnectionContext<Path>, next: NextFunction) => void | Promise<void>;
21
- export type AuthorizationHandler<Path extends string> = (ctx: JoinContext<Path>, next: NextFunction) => void | Promise<void>;
22
- export type EventHandler<Path extends string> = (ctx: EventContext<Path>, next: NextFunction) => void | Promise<void>;
23
- export type OutgoingEventHandler<Event extends string> = (event: OutgoingContext<Event>, next: NextFunction) => PondMessage | Promise<PondMessage> | void | Promise<void>;
20
+ export type EventPayload<EventMap extends PondEventMap, Event extends keyof EventMap> = EventMap[Event] extends [PondMessage, PondMessage] ? EventMap[Event][0] : EventMap[Event];
21
+ export type EventResponse<EventMap extends PondEventMap, Event extends keyof EventMap> = EventMap[Event] extends [PondMessage, PondMessage] ? EventMap[Event][1] : PondMessage;
22
+ export type EventKey<Schema extends AnyPondSchema> = Extract<keyof EventsOf<Schema>, string>;
23
+ export type ConnectionHandler<Path extends string> = (ctx: ConnectionContext<Path>, next: NextFunction) => unknown | Promise<unknown>;
24
+ export type AuthorizationHandler<Path extends string, Schema extends AnyPondSchema = AnyPondSchema> = (ctx: JoinContext<Path, Schema>, next: NextFunction) => unknown | Promise<unknown>;
25
+ export type EventHandler<Path extends string, Schema extends AnyPondSchema = AnyPondSchema, Event extends EventKey<Schema> = EventKey<Schema>> = (ctx: EventContext<Path, Schema, Event>, next: NextFunction) => unknown | Promise<unknown>;
26
+ export type OutgoingEventHandler<Path extends string, Schema extends AnyPondSchema = AnyPondSchema, Event extends EventKey<Schema> = EventKey<Schema>> = (event: OutgoingContext<Path, Schema, Event>, next: NextFunction) => EventPayload<EventsOf<Schema>, Event> | Promise<EventPayload<EventsOf<Schema>, Event>> | void | Promise<void>;
24
27
  export interface ConnectionParams {
25
28
  head: Buffer;
26
29
  socket: internal.Duplex;
@@ -39,10 +42,10 @@ export interface ConnectionResponseOptions {
39
42
  params: ConnectionParams;
40
43
  webSocketServer: WebSocketServer;
41
44
  }
42
- export interface JoinRequestOptions<Path extends string> {
45
+ export interface JoinRequestOptions<Path extends string, Schema extends AnyPondSchema = AnyPondSchema> {
43
46
  clientId: string;
44
- assigns: PondAssigns;
45
- joinParams: JoinParams;
47
+ assigns: AssignsOf<Schema>;
48
+ joinParams: JoinParamsOf<Schema>;
46
49
  params: EventParams<Path>;
47
50
  }
48
51
  export interface RequestCache extends SocketCache {
@@ -59,7 +62,7 @@ export type BroadcastEvent = Omit<InternalChannelEvent, 'action' | 'payload' | '
59
62
  payload: PondMessage;
60
63
  };
61
64
  export interface LeaveEvent {
62
- user: UserData;
65
+ user: UserData<PondPresence, PondAssigns>;
63
66
  channel: Channel;
64
67
  }
65
68
  export type LeaveCallback = (event: LeaveEvent) => void;
@@ -1,18 +1,23 @@
1
- import { ChannelReceivers, EventParams, PondEvent, PondMessage, PondObject, UserAssigns, UserPresences } from '@eleven-am/pondsocket-common';
1
+ import { ChannelReceivers, AnyPondSchema, AssignsOf, EventParams, EventPayload, EventsOf, PondEvent, PondMessage, PondObject, PresenceOf, UserAssigns, UserPresences } from '@eleven-am/pondsocket-common';
2
2
  import { ChannelEngine } from '../engines/channelEngine';
3
3
  import { Channel } from '../wrappers/channel';
4
- export declare abstract class BaseContext<Path extends string> {
4
+ export declare abstract class BaseContext<Path extends string, Schema extends AnyPondSchema = AnyPondSchema, EventName extends Extract<keyof EventsOf<Schema>, string> = Extract<keyof EventsOf<Schema>, string>, Payload extends PondMessage = EventPayload<EventsOf<Schema>, EventName>> {
5
5
  #private;
6
- constructor(engine: ChannelEngine, params: EventParams<Path>, event: string, payload: PondMessage, sender: string);
7
- get event(): PondEvent<Path>;
6
+ protected constructor(engine: ChannelEngine, params: EventParams<Path>, event: string, payload: Payload, sender: string);
7
+ get event(): PondEvent<Path> & {
8
+ event: EventName;
9
+ payload: Payload;
10
+ };
11
+ get query(): EventParams<Path>['query'];
12
+ get params(): EventParams<Path>['params'];
8
13
  get channelName(): string;
9
- get channel(): Channel;
10
- get presences(): UserPresences;
11
- get assigns(): UserAssigns;
12
- get user(): import("@eleven-am/pondsocket-common").UserData<PondObject, PondObject>;
13
- broadcast(event: string, payload: PondMessage): this;
14
- broadcastFrom(event: string, payload: PondMessage): this;
15
- broadcastTo(event: string, payload: PondMessage, userIds: string | string[]): this;
14
+ get channel(): Channel<Schema>;
15
+ get presences(): UserPresences<PresenceOf<Schema>>;
16
+ get assigns(): UserAssigns<AssignsOf<Schema>>;
17
+ get user(): import("@eleven-am/pondsocket-common").UserData<PresenceOf<Schema>, AssignsOf<Schema>> | null;
18
+ broadcast<Event extends Extract<keyof EventsOf<Schema>, string>>(event: Event, payload: EventPayload<EventsOf<Schema>, Event>): this;
19
+ broadcastFrom<Event extends Extract<keyof EventsOf<Schema>, string>>(event: Event, payload: EventPayload<EventsOf<Schema>, Event>): this;
20
+ broadcastTo<Event extends Extract<keyof EventsOf<Schema>, string>>(event: Event, payload: EventPayload<EventsOf<Schema>, Event>, userIds: string | string[]): this;
16
21
  protected abstract _sendMessageToRecipients(recipient: ChannelReceivers, event: string, payload: PondObject): void;
17
22
  protected get engine(): ChannelEngine;
18
23
  protected get sender(): string;
@@ -36,6 +36,12 @@ class BaseContext {
36
36
  payload: __classPrivateFieldGet(this, _BaseContext_payload, "f"),
37
37
  };
38
38
  }
39
+ get query() {
40
+ return __classPrivateFieldGet(this, _BaseContext_params, "f").query;
41
+ }
42
+ get params() {
43
+ return __classPrivateFieldGet(this, _BaseContext_params, "f").params;
44
+ }
39
45
  get channelName() {
40
46
  return __classPrivateFieldGet(this, _BaseContext_engine, "f").name;
41
47
  }
@@ -54,7 +54,7 @@ export declare class ConnectionContext<Path extends string> {
54
54
  /**
55
55
  * Accepts the connection request
56
56
  */
57
- accept(): ConnectionContext<Path>;
57
+ accept(assigns?: PondAssigns): ConnectionContext<Path>;
58
58
  /**
59
59
  * Declines the connection request
60
60
  */
@@ -115,7 +115,10 @@ class ConnectionContext {
115
115
  /**
116
116
  * Accepts the connection request
117
117
  */
118
- accept() {
118
+ accept(assigns) {
119
+ if (assigns) {
120
+ this.assign(assigns);
121
+ }
119
122
  __classPrivateFieldGet(this, _ConnectionContext_instances, "m", _ConnectionContext_performChecks).call(this);
120
123
  __classPrivateFieldGet(this, _ConnectionContext_webSocketServer, "f").handleUpgrade(__classPrivateFieldGet(this, _ConnectionContext_params, "f").request, __classPrivateFieldGet(this, _ConnectionContext_params, "f").socket, __classPrivateFieldGet(this, _ConnectionContext_params, "f").head, (ws) => {
121
124
  const cache = {
@@ -1,15 +1,16 @@
1
- import { ChannelReceivers, EventParams, PondAssigns, PondMessage, PondObject, PondPresence } from '@eleven-am/pondsocket-common';
1
+ import { ChannelReceivers, AnyPondSchema, AssignsOf, EventParams, EventPayload, EventsOf, PondObject, PresenceOf } from '@eleven-am/pondsocket-common';
2
2
  import { BaseContext } from './baseContext';
3
3
  import { BroadcastEvent } from '../abstracts/types';
4
4
  import { ChannelEngine } from '../engines/channelEngine';
5
- export declare class EventContext<Path extends string> extends BaseContext<Path> {
5
+ export declare class EventContext<Path extends string, Schema extends AnyPondSchema = AnyPondSchema, EventName extends Extract<keyof EventsOf<Schema>, string> = Extract<keyof EventsOf<Schema>, string>> extends BaseContext<Path, Schema, EventName, EventPayload<EventsOf<Schema>, EventName>> {
6
6
  #private;
7
7
  constructor(event: BroadcastEvent, params: EventParams<Path>, engine: ChannelEngine);
8
- assign(assigns: PondAssigns): EventContext<Path>;
9
- reply(event: string, payload: PondMessage): this;
10
- trackPresence(presence: PondPresence, userId?: string): EventContext<Path>;
11
- updatePresence(presence: PondPresence, userId?: string): EventContext<Path>;
12
- evictUser(reason: string, userId?: string): EventContext<Path>;
13
- removePresence(userId?: string): EventContext<Path>;
8
+ get payload(): EventPayload<EventsOf<Schema>, EventName>;
9
+ assign(assigns: Partial<AssignsOf<Schema>>): EventContext<Path, Schema, EventName>;
10
+ reply<Event extends Extract<keyof EventsOf<Schema>, string>>(event: Event, payload: EventPayload<EventsOf<Schema>, Event>): this;
11
+ trackPresence(presence: PresenceOf<Schema>, userId?: string): EventContext<Path, Schema, EventName>;
12
+ updatePresence(presence: Partial<PresenceOf<Schema>>, userId?: string): EventContext<Path, Schema, EventName>;
13
+ evictUser(reason: string, userId?: string): EventContext<Path, Schema, EventName>;
14
+ removePresence(userId?: string): EventContext<Path, Schema, EventName>;
14
15
  protected _sendMessageToRecipients(recipient: ChannelReceivers, event: string, payload: PondObject): void;
15
16
  }
@@ -23,6 +23,9 @@ class EventContext extends baseContext_1.BaseContext {
23
23
  __classPrivateFieldSet(this, _EventContext_event, event, "f");
24
24
  __classPrivateFieldSet(this, _EventContext_requestId, event.requestId, "f");
25
25
  }
26
+ get payload() {
27
+ return __classPrivateFieldGet(this, _EventContext_event, "f").payload;
28
+ }
26
29
  assign(assigns) {
27
30
  this.channel.updateAssigns(__classPrivateFieldGet(this, _EventContext_event, "f").sender, assigns);
28
31
  return this;
@@ -1,17 +1,17 @@
1
- import { ChannelReceivers, JoinParams, PondAssigns, PondMessage, PondObject, PondPresence, UserData } from '@eleven-am/pondsocket-common';
1
+ import { ChannelReceivers, AnyPondSchema, AssignsOf, EventsOf, JoinParamsOf, PondMessage, PondObject, PresenceOf, UserData } from '@eleven-am/pondsocket-common';
2
2
  import { BaseContext } from './baseContext';
3
3
  import { JoinRequestOptions, RequestCache } from '../abstracts/types';
4
4
  import { ChannelEngine } from '../engines/channelEngine';
5
- export declare class JoinContext<Path extends string> extends BaseContext<Path> {
5
+ export declare class JoinContext<Path extends string, Schema extends AnyPondSchema = AnyPondSchema> extends BaseContext<Path, Schema, Extract<keyof EventsOf<Schema>, string>, JoinParamsOf<Schema>> {
6
6
  #private;
7
- constructor(options: JoinRequestOptions<Path>, engine: ChannelEngine, user: RequestCache);
8
- get user(): UserData;
9
- get joinParams(): JoinParams;
7
+ constructor(options: JoinRequestOptions<Path, Schema>, engine: ChannelEngine, user: RequestCache);
8
+ get user(): UserData<PresenceOf<Schema>, AssignsOf<Schema>>;
9
+ get joinParams(): JoinParamsOf<Schema>;
10
10
  get hasResponded(): boolean;
11
- accept(): JoinContext<Path>;
12
- decline(message?: string, errorCode?: number): JoinContext<Path>;
13
- assign(assigns: PondAssigns): JoinContext<Path>;
14
- reply(event: string, payload: PondMessage): JoinContext<Path>;
15
- trackPresence(presence: PondPresence): JoinContext<Path>;
11
+ accept(assigns?: Partial<AssignsOf<Schema>>): JoinContext<Path, Schema>;
12
+ decline(message?: string, errorCode?: number): JoinContext<Path, Schema>;
13
+ assign(assigns: Partial<AssignsOf<Schema>>): JoinContext<Path, Schema>;
14
+ reply(event: string, payload: PondMessage): JoinContext<Path, Schema>;
15
+ trackPresence(presence: PresenceOf<Schema>): JoinContext<Path, Schema>;
16
16
  protected _sendMessageToRecipients(recipient: ChannelReceivers, event: string, payload: PondObject): void;
17
17
  }
@@ -44,8 +44,11 @@ class JoinContext extends baseContext_1.BaseContext {
44
44
  get hasResponded() {
45
45
  return __classPrivateFieldGet(this, _JoinContext_executed, "f");
46
46
  }
47
- accept() {
47
+ accept(assigns) {
48
48
  var _a;
49
+ if (assigns) {
50
+ this.assign(assigns);
51
+ }
49
52
  __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_performChecks).call(this);
50
53
  const onMessage = this.engine.parent.parent.sendMessage.bind(this.engine.parent.parent, __classPrivateFieldGet(this, _JoinContext_user, "f").socket);
51
54
  const subscription = this.engine.addUser(__classPrivateFieldGet(this, _JoinContext_user, "f").clientId, __classPrivateFieldGet(this, _JoinContext_newAssigns, "f"), onMessage);
@@ -1,10 +1,10 @@
1
- import { ChannelReceivers, Event, EventParams, PondMessage, PondObject } from '@eleven-am/pondsocket-common';
1
+ import { ChannelReceivers, AnyPondSchema, Event, EventParams, EventPayload, EventsOf, PondObject } from '@eleven-am/pondsocket-common';
2
2
  import { BaseContext } from './baseContext';
3
3
  import { ChannelEngine } from '../engines/channelEngine';
4
- export declare class OutgoingContext<Path extends string> extends BaseContext<Path> {
4
+ export declare class OutgoingContext<Path extends string, Schema extends AnyPondSchema = AnyPondSchema, EventName extends Extract<keyof EventsOf<Schema>, string> = Extract<keyof EventsOf<Schema>, string>> extends BaseContext<Path, Schema, EventName, EventPayload<EventsOf<Schema>, EventName>> {
5
5
  #private;
6
6
  constructor(event: Event, params: EventParams<Path>, engine: ChannelEngine, userid: string);
7
- get payload(): PondMessage;
7
+ get payload(): EventPayload<EventsOf<Schema>, EventName>;
8
8
  /**
9
9
  * Blocks the outgoing context, preventing further processing of the event.
10
10
  */
@@ -18,7 +18,7 @@ export declare class OutgoingContext<Path extends string> extends BaseContext<Pa
18
18
  * Transforms the outgoing context with a new payload.
19
19
  * @param payload - The new payload to set for the context.
20
20
  */
21
- transform(payload: PondMessage): this;
21
+ transform(payload: EventPayload<EventsOf<Schema>, EventName>): this;
22
22
  /**
23
23
  * Updates the parameters of the outgoing context.
24
24
  * @param params - The new parameters to set for the context.
@@ -17,6 +17,8 @@ export declare class ChannelEngine {
17
17
  upsertPresence(userId: string, presence: PondPresence): void;
18
18
  updateAssigns(userId: string, assigns: PondMessage): void;
19
19
  kickUser(userId: string, reason: string): void;
20
+ requestUserRemoval(userId: string): void;
21
+ getUserAcrossNodes(userId: string, timeoutMs?: number): Promise<UserData | null>;
20
22
  getAssigns(): UserAssigns;
21
23
  getPresence(): UserPresences;
22
24
  destroy(reason?: string): void;
@@ -30,7 +30,7 @@ var __rest = (this && this.__rest) || function (s, e) {
30
30
  }
31
31
  return t;
32
32
  };
33
- var _ChannelEngine_instances, _ChannelEngine_endpointId, _ChannelEngine_backend, _ChannelEngine_nodeId, _ChannelEngine_presenceEngine, _ChannelEngine_assignsCache, _ChannelEngine_userSubscriptions, _ChannelEngine_publisher, _ChannelEngine_distributedSubscription, _ChannelEngine_heartbeatSubscription, _ChannelEngine_nodeLastSeen, _ChannelEngine_nodeUsers, _ChannelEngine_staleNodeTimer, _ChannelEngine_name, _ChannelEngine_closed, _ChannelEngine_distributedReady, _ChannelEngine_safeRemovePresence, _ChannelEngine_buildSubscriber, _ChannelEngine_getOrCreatePresenceEngine, _ChannelEngine_getUsersFromRecipients, _ChannelEngine_setupDistributedSubscription, _ChannelEngine_afterDistributedReady, _ChannelEngine_handleDistributedMessage, _ChannelEngine_requestChannelState, _ChannelEngine_handleStateRequest, _ChannelEngine_handleStateResponse, _ChannelEngine_handleRemoteUserJoined, _ChannelEngine_handleRemoteUserLeft, _ChannelEngine_handleRemoteMessage, _ChannelEngine_handleRemotePresenceUpdate, _ChannelEngine_handleRemotePresenceRemoved, _ChannelEngine_handleRemoteAssignsUpdate, _ChannelEngine_handleRemoteEvictUser, _ChannelEngine_setupHeartbeatTracking, _ChannelEngine_cleanupStaleNodes, _ChannelEngine_trackNodeUser, _ChannelEngine_untrackNodeUser, _ChannelEngine_broadcastToNodes;
33
+ var _ChannelEngine_instances, _ChannelEngine_endpointId, _ChannelEngine_backend, _ChannelEngine_nodeId, _ChannelEngine_presenceEngine, _ChannelEngine_assignsCache, _ChannelEngine_userSubscriptions, _ChannelEngine_publisher, _ChannelEngine_distributedSubscription, _ChannelEngine_heartbeatSubscription, _ChannelEngine_nodeLastSeen, _ChannelEngine_nodeUsers, _ChannelEngine_pendingUserGets, _ChannelEngine_staleNodeTimer, _ChannelEngine_name, _ChannelEngine_closed, _ChannelEngine_distributedReady, _ChannelEngine_safeRemovePresence, _ChannelEngine_buildSubscriber, _ChannelEngine_getOrCreatePresenceEngine, _ChannelEngine_getUsersFromRecipients, _ChannelEngine_setupDistributedSubscription, _ChannelEngine_afterDistributedReady, _ChannelEngine_handleDistributedMessage, _ChannelEngine_requestChannelState, _ChannelEngine_handleStateRequest, _ChannelEngine_handleStateResponse, _ChannelEngine_handleRemoteUserJoined, _ChannelEngine_handleRemoteUserLeft, _ChannelEngine_handleRemoteMessage, _ChannelEngine_handleRemotePresenceUpdate, _ChannelEngine_handleRemotePresenceRemoved, _ChannelEngine_handleRemoteAssignsUpdate, _ChannelEngine_handleRemoteEvictUser, _ChannelEngine_handleRemoteUserRemove, _ChannelEngine_handleRemoteUserGetRequest, _ChannelEngine_handleRemoteUserGetResponse, _ChannelEngine_setupHeartbeatTracking, _ChannelEngine_cleanupStaleNodes, _ChannelEngine_trackNodeUser, _ChannelEngine_untrackNodeUser, _ChannelEngine_broadcastToNodes;
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
35
  exports.ChannelEngine = void 0;
36
36
  const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
@@ -59,6 +59,7 @@ class ChannelEngine {
59
59
  _ChannelEngine_heartbeatSubscription.set(this, null);
60
60
  _ChannelEngine_nodeLastSeen.set(this, new Map());
61
61
  _ChannelEngine_nodeUsers.set(this, new Map());
62
+ _ChannelEngine_pendingUserGets.set(this, new Map());
62
63
  _ChannelEngine_staleNodeTimer.set(this, null);
63
64
  _ChannelEngine_name.set(this, void 0);
64
65
  _ChannelEngine_closed.set(this, false);
@@ -222,6 +223,38 @@ class ChannelEngine {
222
223
  });
223
224
  this.removeUser(userId, true);
224
225
  }
226
+ requestUserRemoval(userId) {
227
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
228
+ type: types_1.DistributedMessageType.USER_REMOVE,
229
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
230
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
231
+ userId,
232
+ });
233
+ }
234
+ getUserAcrossNodes(userId, timeoutMs = 5000) {
235
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
236
+ return Promise.resolve(null);
237
+ }
238
+ const requestId = (0, pondsocket_common_1.uuid)();
239
+ return new Promise((resolve) => {
240
+ const timer = setTimeout(() => {
241
+ __classPrivateFieldGet(this, _ChannelEngine_pendingUserGets, "f").delete(requestId);
242
+ resolve(null);
243
+ }, timeoutMs);
244
+ __classPrivateFieldGet(this, _ChannelEngine_pendingUserGets, "f").set(requestId, {
245
+ resolve,
246
+ timer,
247
+ });
248
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
249
+ type: types_1.DistributedMessageType.USER_GET_REQUEST,
250
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
251
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
252
+ userId,
253
+ requestId,
254
+ fromNode: __classPrivateFieldGet(this, _ChannelEngine_nodeId, "f"),
255
+ });
256
+ });
257
+ }
225
258
  getAssigns() {
226
259
  return mapToObject(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f"));
227
260
  }
@@ -304,6 +337,8 @@ class ChannelEngine {
304
337
  clearInterval(__classPrivateFieldGet(this, _ChannelEngine_staleNodeTimer, "f"));
305
338
  __classPrivateFieldSet(this, _ChannelEngine_staleNodeTimer, null, "f");
306
339
  }
340
+ __classPrivateFieldGet(this, _ChannelEngine_pendingUserGets, "f").forEach((pending) => clearTimeout(pending.timer));
341
+ __classPrivateFieldGet(this, _ChannelEngine_pendingUserGets, "f").clear();
307
342
  __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").clear();
308
343
  __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").clear();
309
344
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").close();
@@ -311,7 +346,7 @@ class ChannelEngine {
311
346
  }
312
347
  }
313
348
  exports.ChannelEngine = ChannelEngine;
314
- _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(), _ChannelEngine_nodeId = new WeakMap(), _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new WeakMap(), _ChannelEngine_userSubscriptions = new WeakMap(), _ChannelEngine_publisher = new WeakMap(), _ChannelEngine_distributedSubscription = new WeakMap(), _ChannelEngine_heartbeatSubscription = new WeakMap(), _ChannelEngine_nodeLastSeen = new WeakMap(), _ChannelEngine_nodeUsers = new WeakMap(), _ChannelEngine_staleNodeTimer = new WeakMap(), _ChannelEngine_name = new WeakMap(), _ChannelEngine_closed = new WeakMap(), _ChannelEngine_distributedReady = new WeakMap(), _ChannelEngine_instances = new WeakSet(), _ChannelEngine_safeRemovePresence = function _ChannelEngine_safeRemovePresence(userId) {
349
+ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(), _ChannelEngine_nodeId = new WeakMap(), _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new WeakMap(), _ChannelEngine_userSubscriptions = new WeakMap(), _ChannelEngine_publisher = new WeakMap(), _ChannelEngine_distributedSubscription = new WeakMap(), _ChannelEngine_heartbeatSubscription = new WeakMap(), _ChannelEngine_nodeLastSeen = new WeakMap(), _ChannelEngine_nodeUsers = new WeakMap(), _ChannelEngine_pendingUserGets = new WeakMap(), _ChannelEngine_staleNodeTimer = new WeakMap(), _ChannelEngine_name = new WeakMap(), _ChannelEngine_closed = new WeakMap(), _ChannelEngine_distributedReady = new WeakMap(), _ChannelEngine_instances = new WeakSet(), _ChannelEngine_safeRemovePresence = function _ChannelEngine_safeRemovePresence(userId) {
315
350
  if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
316
351
  try {
317
352
  __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
@@ -421,6 +456,15 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
421
456
  case types_1.DistributedMessageType.EVICT_USER:
422
457
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteEvictUser).call(this, message);
423
458
  break;
459
+ case types_1.DistributedMessageType.USER_REMOVE:
460
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteUserRemove).call(this, message);
461
+ break;
462
+ case types_1.DistributedMessageType.USER_GET_REQUEST:
463
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteUserGetRequest).call(this, message);
464
+ break;
465
+ case types_1.DistributedMessageType.USER_GET_RESPONSE:
466
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteUserGetResponse).call(this, message);
467
+ break;
424
468
  case types_1.DistributedMessageType.NODE_HEARTBEAT:
425
469
  break;
426
470
  default:
@@ -545,6 +589,40 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
545
589
  unsubscribe();
546
590
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(message.userId);
547
591
  }
592
+ }, _ChannelEngine_handleRemoteUserRemove = function _ChannelEngine_handleRemoteUserRemove(message) {
593
+ if (__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").has(message.userId)) {
594
+ this.removeUser(message.userId);
595
+ return;
596
+ }
597
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(message.userId);
598
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
599
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_untrackNodeUser).call(this, message.sourceNodeId, message.userId);
600
+ }, _ChannelEngine_handleRemoteUserGetRequest = function _ChannelEngine_handleRemoteUserGetRequest(message) {
601
+ var _a;
602
+ if (!__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").has(message.userId)) {
603
+ return;
604
+ }
605
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
606
+ type: types_1.DistributedMessageType.USER_GET_RESPONSE,
607
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
608
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
609
+ userId: message.userId,
610
+ requestId: message.requestId,
611
+ assigns: __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(message.userId) || {},
612
+ presence: ((_a = __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) === null || _a === void 0 ? void 0 : _a.getPresence(message.userId)) || {},
613
+ });
614
+ }, _ChannelEngine_handleRemoteUserGetResponse = function _ChannelEngine_handleRemoteUserGetResponse(message) {
615
+ const pending = __classPrivateFieldGet(this, _ChannelEngine_pendingUserGets, "f").get(message.requestId);
616
+ if (!pending) {
617
+ return;
618
+ }
619
+ clearTimeout(pending.timer);
620
+ __classPrivateFieldGet(this, _ChannelEngine_pendingUserGets, "f").delete(message.requestId);
621
+ pending.resolve({
622
+ id: message.userId,
623
+ assigns: message.assigns || {},
624
+ presence: message.presence || {},
625
+ });
548
626
  }, _ChannelEngine_setupHeartbeatTracking = function _ChannelEngine_setupHeartbeatTracking() {
549
627
  if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
550
628
  return;
@@ -1,4 +1,4 @@
1
- import { ChannelEvent, PondPath } from '@eleven-am/pondsocket-common';
1
+ import { ChannelEvent, AnyPondSchema, PondPath } from '@eleven-am/pondsocket-common';
2
2
  import { WebSocket } from 'ws';
3
3
  import { AuthorizationHandler, SocketCache } from '../abstracts/types';
4
4
  import { IDistributedBackend } from '../types';
@@ -10,7 +10,7 @@ export declare class EndpointEngine {
10
10
  /**
11
11
  * Creates a new channel on a specified path
12
12
  */
13
- createChannel<Path extends string>(path: PondPath<Path>, handler: AuthorizationHandler<Path>): PondChannel;
13
+ createChannel<Path extends string, Schema extends AnyPondSchema = AnyPondSchema>(path: PondPath<Path>, handler: AuthorizationHandler<Path, Schema>): PondChannel<Schema>;
14
14
  /**
15
15
  * Gets all connected clients
16
16
  */
@@ -52,7 +52,7 @@ class EndpointEngine {
52
52
  };
53
53
  const channel = lobbyEngine.getOrCreateChannel(user.channelName);
54
54
  const context = new joinContext_1.JoinContext(options, channel, user);
55
- return handler(context, next);
55
+ return Promise.resolve(handler(context, next)).then(() => undefined);
56
56
  }
57
57
  next();
58
58
  });
@@ -1,4 +1,4 @@
1
- import { Event, PondPath } from '@eleven-am/pondsocket-common';
1
+ import { AnyPondSchema, Event, EventsOf, PondPath } from '@eleven-am/pondsocket-common';
2
2
  import { ChannelEngine } from './channelEngine';
3
3
  import { EndpointEngine } from './endpointEngine';
4
4
  import { Middleware } from '../abstracts/middleware';
@@ -20,11 +20,11 @@ export declare class LobbyEngine {
20
20
  /**
21
21
  * Attaches a handler for a specific event pattern
22
22
  */
23
- onEvent<Event extends string>(event: PondPath<Event>, handler: EventHandler<Event>): void;
23
+ onEvent<Schema extends AnyPondSchema, Path extends Extract<keyof EventsOf<Schema>, string>>(event: PondPath<Path>, handler: EventHandler<Path, Schema, Path>): void;
24
24
  /**
25
25
  * Attaches a handler for outgoing events
26
26
  */
27
- handleOutgoingEvent<Event extends string>(event: PondPath<Event>, handler: OutgoingEventHandler<Event>): void;
27
+ handleOutgoingEvent<Schema extends AnyPondSchema, Path extends Extract<keyof EventsOf<Schema>, string>>(event: PondPath<Path>, handler: OutgoingEventHandler<Path, Schema, Path>): void;
28
28
  /**
29
29
  * Processes an outgoing event, applying middleware and returning a new event
30
30
  * @param event - The channel event to process
@@ -56,7 +56,7 @@ class LobbyEngine {
56
56
  return next();
57
57
  }
58
58
  const context = new eventContext_1.EventContext(requestEvent, params, channel);
59
- return handler(context, next);
59
+ return Promise.resolve(handler(context, next)).then(() => undefined);
60
60
  });
61
61
  }
62
62
  /**
@@ -68,12 +68,13 @@ class LobbyEngine {
68
68
  if (!params || context.isBlocked()) {
69
69
  return next();
70
70
  }
71
- context.updateParams(params);
72
- const payload = yield handler(context, next);
71
+ const typedContext = context;
72
+ typedContext.updateParams(params);
73
+ const payload = yield handler(typedContext, next);
73
74
  if (payload === undefined || payload === null) {
74
75
  return;
75
76
  }
76
- context.transform(payload);
77
+ typedContext.transform(payload);
77
78
  return next();
78
79
  }));
79
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eleven-am/pondsocket",
3
- "version": "0.1.216",
3
+ "version": "0.1.218",
4
4
  "description": "PondSocket is a fast simple socket server",
5
5
  "keywords": [
6
6
  "socket",
@@ -43,7 +43,9 @@
43
43
  "url": "git+https://github.com/Eleven-am/pondSocket.git"
44
44
  },
45
45
  "dependencies": {
46
- "@eleven-am/pondsocket-common": "^0.0.38",
46
+ "@eleven-am/pondsocket-common": "^0.0.40",
47
+ "@types/node": "^25.3.3",
48
+ "@types/ws": "^8.18.1",
47
49
  "redis": "^5.12.1",
48
50
  "ws": "^8.20.1"
49
51
  },
package/server/server.js CHANGED
@@ -93,7 +93,7 @@ class PondSocket {
93
93
  webSocketServer: __classPrivateFieldGet(this, _PondSocket_socketServer, "f"),
94
94
  };
95
95
  const context = new connectionContext_1.ConnectionContext(request, newParams);
96
- return handler(context, next);
96
+ return Promise.resolve(handler(context, next)).then(() => undefined);
97
97
  });
98
98
  return new endpoint_1.Endpoint(endpoint);
99
99
  }
package/types.d.ts CHANGED
@@ -100,20 +100,19 @@ export interface EvictUser extends DistributedMessage {
100
100
  export interface UserRemove extends DistributedMessage {
101
101
  type: DistributedMessageType.USER_REMOVE;
102
102
  userId: string;
103
- reason: string;
104
103
  }
105
104
  export interface UserGetRequest extends DistributedMessage {
106
105
  type: DistributedMessageType.USER_GET_REQUEST;
107
106
  userId: string;
108
- lookupRequestId: string;
107
+ requestId: string;
108
+ fromNode: string;
109
109
  }
110
110
  export interface UserGetResponse extends DistributedMessage {
111
111
  type: DistributedMessageType.USER_GET_RESPONSE;
112
- lookupRequestId: string;
113
112
  userId: string;
114
- found: boolean;
115
- assigns?: PondAssigns;
116
- presence?: PondPresence;
113
+ requestId: string;
114
+ assigns: PondAssigns;
115
+ presence: PondPresence;
117
116
  }
118
117
  export interface NodeHeartbeat extends DistributedMessage {
119
118
  type: DistributedMessageType.NODE_HEARTBEAT;
@@ -1,32 +1,32 @@
1
- import { PondMessage, PondAssigns, PondPresence } from '@eleven-am/pondsocket-common';
1
+ import { AnyPondSchema, AssignsOf, EventsOf, EventPayload, PresenceOf, UserData, UserAssigns, UserPresences } from '@eleven-am/pondsocket-common';
2
2
  import { ChannelEngine } from '../engines/channelEngine';
3
- export declare class Channel {
3
+ export declare class Channel<Schema extends AnyPondSchema = AnyPondSchema> {
4
4
  #private;
5
5
  constructor(engine: ChannelEngine);
6
6
  /**
7
7
  * Gets a user's data
8
8
  */
9
- getUserData(userId: string): import("@eleven-am/pondsocket-common").UserData<import("@eleven-am/pondsocket-common").PondObject, import("@eleven-am/pondsocket-common").PondObject>;
9
+ getUserData(userId: string): UserData<PresenceOf<Schema>, AssignsOf<Schema>> | null;
10
10
  /**
11
11
  * Gets all presence data
12
12
  */
13
- getPresences(): import("@eleven-am/pondsocket-common").UserPresences;
13
+ getPresences(): UserPresences<PresenceOf<Schema>>;
14
14
  /**
15
15
  * Gets all assigns data
16
16
  */
17
- getAssigns(): import("@eleven-am/pondsocket-common").UserAssigns;
17
+ getAssigns(): UserAssigns<AssignsOf<Schema>>;
18
18
  /**
19
19
  * Broadcasts a message to all users
20
20
  */
21
- broadcast(event: string, payload: PondMessage): this;
21
+ broadcast<Event extends Extract<keyof EventsOf<Schema>, string>>(event: Event, payload: EventPayload<EventsOf<Schema>, Event>): this;
22
22
  /**
23
23
  * Broadcasts a message from a specific user to all others
24
24
  */
25
- broadcastFrom(userId: string, event: string, payload: PondMessage): this;
25
+ broadcastFrom<Event extends Extract<keyof EventsOf<Schema>, string>>(userId: string, event: Event, payload: EventPayload<EventsOf<Schema>, Event>): this;
26
26
  /**
27
27
  * Broadcasts a message to specific users
28
28
  */
29
- broadcastTo(userIds: string | string[], event: string, payload: PondMessage): this;
29
+ broadcastTo<Event extends Extract<keyof EventsOf<Schema>, string>>(userIds: string | string[], event: Event, payload: EventPayload<EventsOf<Schema>, Event>): this;
30
30
  /**
31
31
  * Kicks a user from the channel
32
32
  */
@@ -34,11 +34,11 @@ export declare class Channel {
34
34
  /**
35
35
  * Tracks a user's presence
36
36
  */
37
- trackPresence(userId: string, presence: PondPresence): this;
37
+ trackPresence(userId: string, presence: PresenceOf<Schema>): this;
38
38
  /**
39
39
  * Updates a user's presence
40
40
  */
41
- updatePresence(userId: string, presence: PondPresence): this;
41
+ updatePresence(userId: string, presence: Partial<PresenceOf<Schema>>): this;
42
42
  /**
43
43
  * Removes a user's presence
44
44
  */
@@ -46,9 +46,17 @@ export declare class Channel {
46
46
  /**
47
47
  * Adds or updates a user's presence
48
48
  */
49
- upsertPresence(userId: string, presence: PondPresence): this;
49
+ upsertPresence(userId: string, presence: PresenceOf<Schema>): this;
50
50
  /**
51
51
  * Updates a user's assigns
52
52
  */
53
- updateAssigns(userId: string, assigns: PondAssigns): this;
53
+ updateAssigns(userId: string, assigns: Partial<AssignsOf<Schema>>): this;
54
+ /**
55
+ * Resolves a user's data from any node in the distributed cluster
56
+ */
57
+ getUserAcrossNodes(userId: string, timeoutMs?: number): Promise<UserData<PresenceOf<Schema>, AssignsOf<Schema>> | null>;
58
+ /**
59
+ * Requests removal of a user across all nodes in the distributed cluster
60
+ */
61
+ requestUserRemoval(userId: string): this;
54
62
  }
@@ -101,6 +101,19 @@ class Channel {
101
101
  __classPrivateFieldGet(this, _Channel_engine, "f").updateAssigns(userId, assigns);
102
102
  return this;
103
103
  }
104
+ /**
105
+ * Resolves a user's data from any node in the distributed cluster
106
+ */
107
+ getUserAcrossNodes(userId, timeoutMs) {
108
+ return __classPrivateFieldGet(this, _Channel_engine, "f").getUserAcrossNodes(userId, timeoutMs);
109
+ }
110
+ /**
111
+ * Requests removal of a user across all nodes in the distributed cluster
112
+ */
113
+ requestUserRemoval(userId) {
114
+ __classPrivateFieldGet(this, _Channel_engine, "f").requestUserRemoval(userId);
115
+ return this;
116
+ }
104
117
  }
105
118
  exports.Channel = Channel;
106
119
  _Channel_engine = new WeakMap();
@@ -1,10 +1,10 @@
1
- import { PondPath } from '@eleven-am/pondsocket-common';
1
+ import { AnyPondSchema, PondPath } from '@eleven-am/pondsocket-common';
2
2
  import { AuthorizationHandler } from '../abstracts/types';
3
3
  import { EndpointEngine } from '../engines/endpointEngine';
4
4
  export declare class Endpoint {
5
5
  #private;
6
6
  constructor(engine: EndpointEngine);
7
- createChannel<Path extends string>(path: PondPath<Path>, handler: AuthorizationHandler<Path>): import("./pondChannel").PondChannel;
7
+ createChannel<Path extends string, Schema extends AnyPondSchema = AnyPondSchema>(path: PondPath<Path>, handler: AuthorizationHandler<Path, Schema>): import("./pondChannel").PondChannel<Schema>;
8
8
  closeConnection(clientIds: string | string[]): void;
9
9
  getClients(): import("ws").WebSocket[];
10
10
  }
@@ -1,15 +1,15 @@
1
- import { PondMessage, PondPath } from '@eleven-am/pondsocket-common';
1
+ import { AnyPondSchema, EventPayload, EventsOf, PondPath } from '@eleven-am/pondsocket-common';
2
2
  import { Channel } from './channel';
3
3
  import { EventHandler, LeaveCallback, OutgoingEventHandler } from '../abstracts/types';
4
4
  import { LobbyEngine } from '../engines/lobbyEngine';
5
- export declare class PondChannel {
5
+ export declare class PondChannel<Schema extends AnyPondSchema = AnyPondSchema> {
6
6
  #private;
7
7
  constructor(lobby: LobbyEngine);
8
- onEvent<Event extends string>(event: PondPath<Event>, handler: EventHandler<Event>): PondChannel;
9
- onLeave(callback: LeaveCallback): PondChannel;
10
- handleOutgoingEvent<Event extends string>(event: PondPath<Event>, handler: OutgoingEventHandler<Event>): this;
11
- getChannel(channelName: string): Channel | null;
12
- broadcast(channelName: string, event: string, payload: PondMessage): PondChannel;
13
- broadcastFrom(channelName: string, userId: string, event: string, payload: PondMessage): PondChannel;
14
- broadcastTo(channelName: string, userIds: string | string[], event: string, payload: PondMessage): PondChannel;
8
+ onEvent<Event extends Extract<keyof EventsOf<Schema>, string>>(event: PondPath<Event>, handler: EventHandler<Event, Schema, Event>): PondChannel<Schema>;
9
+ onLeave(callback: LeaveCallback): PondChannel<Schema>;
10
+ handleOutgoingEvent<Event extends Extract<keyof EventsOf<Schema>, string>>(event: PondPath<Event>, handler: OutgoingEventHandler<Event, Schema, Event>): this;
11
+ getChannel(channelName: string): Channel<Schema> | null;
12
+ broadcast<Event extends Extract<keyof EventsOf<Schema>, string>>(channelName: string, event: Event, payload: EventPayload<EventsOf<Schema>, Event>): PondChannel<Schema>;
13
+ broadcastFrom<Event extends Extract<keyof EventsOf<Schema>, string>>(channelName: string, userId: string, event: Event, payload: EventPayload<EventsOf<Schema>, Event>): PondChannel<Schema>;
14
+ broadcastTo<Event extends Extract<keyof EventsOf<Schema>, string>>(channelName: string, userIds: string | string[], event: Event, payload: EventPayload<EventsOf<Schema>, Event>): PondChannel<Schema>;
15
15
  }