@eleven-am/pondsocket-nest 0.0.21 → 0.0.23

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 CHANGED
@@ -187,7 +187,7 @@ const endpoint = pond.createEndpoint('/api/socket', (req, res) => {
187
187
  const role = getRoleFromToken(token);
188
188
 
189
189
  // Handle socket connection and authentication for valid users
190
- res.accept({ role }); // Assign the user's role to the socket
190
+ res.accept({role}); // Assign the user's role to the socket
191
191
  } else {
192
192
  // Reject the connection for invalid users or without a token
193
193
  res.reject('Invalid token', 401);
@@ -198,9 +198,9 @@ const endpoint = pond.createEndpoint('/api/socket', (req, res) => {
198
198
  const profanityChannel = endpoint.createChannel('/channel/:id', async (req, res) => {
199
199
  // When joining the channel, any joinParams passed from the client will be available in the request payload
200
200
  // Also any previous assigns on the socket will be available in the request payload as well
201
- const { role } = req.user.assigns;
202
- const { username } = req.joinParams;
203
- const { id } = req.event.params;
201
+ const {role} = req.user.assigns;
202
+ const {username} = req.joinParams;
203
+ const {id} = req.event.params;
204
204
 
205
205
  // maybe retrieve the previous messages from the database
206
206
  const messages = await getMessagesFromDatabase(id);
@@ -208,7 +208,7 @@ const profanityChannel = endpoint.createChannel('/channel/:id', async (req, res)
208
208
  // Check if the user has the required role to join the channel
209
209
  if (role === 'admin') {
210
210
  // Accept the join request
211
- res.accept({ username, profanityCount: 0 })
211
+ res.accept({username, profanityCount: 0})
212
212
  // optionally you can track the presence of the user in the channel
213
213
  .trackPresence({
214
214
  username,
@@ -217,7 +217,7 @@ const profanityChannel = endpoint.createChannel('/channel/:id', async (req, res)
217
217
  onlineSince: Date.now(),
218
218
  })
219
219
  // and send the user the channel history
220
- .sendToUsers('history', { messages }, [req.user.id]);
220
+ .sendToUsers('history', {messages}, [req.user.id]);
221
221
 
222
222
  // Alternatively, you can also send messages to the user, NOTE that the user would be automatically subscribed to the channel.
223
223
  // res.send('history', { messages }, { username, profanityCount: 0 })
@@ -229,19 +229,19 @@ const profanityChannel = endpoint.createChannel('/channel/:id', async (req, res)
229
229
  // });
230
230
  } else {
231
231
  // Reject the join request
232
- res.reject('You do not have the required role to join this channel', 403);
232
+ res.decline('You do not have the required role to join this channel', 403);
233
233
  }
234
234
  });
235
235
 
236
236
  // Attach message event listener to the profanityChannel
237
237
  profanityChannel.onEvent('message', (req, res) => {
238
- const { text } = req.event.payload;
238
+ const {text} = req.event.payload;
239
239
 
240
240
  // Check for profanity
241
241
  if (isTextProfane(text)) {
242
242
  // Reject the message if it contains profanity
243
- res.reject('Profanity is not allowed', 400, {
244
- profanityCount: req.user.assigns.profanityCount + 1
243
+ res.decline('Profanity is not allowed', 400, {
244
+ profanityCount: req.user.assigns.profanityCount + 1
245
245
  });
246
246
 
247
247
  // note that profanityCount is updated so req.user.assigns.profanityCount will be updated
@@ -250,9 +250,9 @@ profanityChannel.onEvent('message', (req, res) => {
250
250
  res.evictUser('You have been kicked from the channel for using profanity');
251
251
  } else {
252
252
  // you can broadcast a message to all users or In the channel that profanity is not allowed
253
- res.broadcast('profanity-warning', { message: 'Profanity is not allowed' })
253
+ res.broadcast('profanity-warning', {message: 'Profanity is not allowed'})
254
254
  // or you can send a message to the user that profanity is not allowed
255
- .sendToUsers('profanity-warning', { message: `You have used profanity ${profanityCount} times. You will be kicked from the channel if you use profanity more than 3 times.` }, [req.user.id]);
255
+ .sendToUsers('profanity-warning', {message: `You have used profanity ${profanityCount} times. You will be kicked from the channel if you use profanity more than 3 times.`}, [req.user.id]);
256
256
  }
257
257
  } else {
258
258
  // Accept the message to allow broadcasting to other clients in the channel
@@ -264,8 +264,8 @@ profanityChannel.onEvent('message', (req, res) => {
264
264
  });
265
265
 
266
266
  profanityChannel.onEvent('presence/:presence', (req, res) => {
267
- const { presence } = req.event.params;
268
- const { username } = req.user.assigns;
267
+ const {presence} = req.event.params;
268
+ const {username} = req.user.assigns;
269
269
 
270
270
  // Handle presence events
271
271
  res.updatePresence({
@@ -277,7 +277,7 @@ profanityChannel.onEvent('presence/:presence', (req, res) => {
277
277
  });
278
278
 
279
279
  profanityChannel.onLeave((event) => {
280
- const { username } = event.assigns;
280
+ const {username} = event.assigns;
281
281
 
282
282
  // When a user leaves the channel, PondSocket will automatically remove the user from the presence list and inform other users in the channel
283
283
 
package/index.d.ts CHANGED
@@ -1,13 +1,18 @@
1
1
  import type {
2
- UserData,
3
- JoinRequest,
2
+ Channel as PondChannel,
3
+ ConnectionResponse,
4
4
  EventRequest,
5
+ EventResponse,
5
6
  IncomingConnection,
6
- Channel as PondChannel,
7
+ JoinRequest,
8
+ JoinResponse,
7
9
  LeaveEvent,
8
- JoinResponse, EventResponse,
9
- ConnectionResponse,
10
- PondEvenType, PondEvent, PondMessage, PondPresence, PondAssigns,
10
+ PondAssigns,
11
+ PondEvent,
12
+ PondEvenType,
13
+ PondMessage,
14
+ PondPresence,
15
+ UserData,
11
16
  } from '@eleven-am/pondsocket/types';
12
17
  import type { DynamicModule, ModuleMetadata } from '@nestjs/common';
13
18
 
@@ -35,9 +40,11 @@ type NestFuncType<Event extends string, Payload extends PondMessage, Presence ex
35
40
  broadcast?: Event;
36
41
  assigns?: Assigns;
37
42
  presence?: Presence;
43
+ subscribeTo?: string[];
44
+ unsubscribeFrom?: string[];
38
45
  } & Payload;
39
46
 
40
- type NestReturnType<EventType extends PondEvenType, Event extends keyof EventType, Presence extends PondPresence = PondPresence, Assigns extends PondAssigns = PondAssigns> =
47
+ type PondResponse<EventType extends PondEvenType, Event extends keyof EventType, Presence extends PondPresence = PondPresence, Assigns extends PondAssigns = PondAssigns> =
41
48
  Event extends string ?
42
49
  EventType[Event] extends [PondMessage, PondMessage] ? NestFuncType<Event, EventType[Event][1], Presence, Assigns> :
43
50
  EventType[Event] extends PondMessage ? NestFuncType<Event, EventType[Event], Presence, Assigns> :
@@ -1,23 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PondSocketModule = void 0;
4
+ const nestjs_discovery_1 = require("@golevelup/nestjs-discovery");
4
5
  // eslint-disable-next-line import/no-unresolved
5
6
  const core_1 = require("@nestjs/core");
6
- // eslint-disable-next-line import/no-unresolved
7
- const modules_container_1 = require("@nestjs/core/injector/modules-container");
8
- const discovery_1 = require("../services/discovery");
9
7
  const pondSocket_1 = require("../services/pondSocket");
10
8
  class PondSocketModule {
11
- static forRoot({ guards = [], providers = [], imports = [], exports = [], isGlobal = false, appModuleName = 'AppModule', }) {
12
- const discoveryServiceProvider = {
13
- provide: discovery_1.DiscoveryService,
14
- useFactory: (moduleRef) => new discovery_1.DiscoveryService(moduleRef, appModuleName),
15
- inject: [modules_container_1.ModulesContainer, core_1.HttpAdapterHost],
16
- };
9
+ static forRoot({ guards = [], providers = [], imports = [], exports = [], isGlobal = false, }) {
17
10
  const pondSocketProvider = {
18
11
  provide: pondSocket_1.PondSocketService,
19
12
  useFactory: (moduleRef, adapterHost, discovery) => new pondSocket_1.PondSocketService(moduleRef, discovery, adapterHost, guards),
20
- inject: [core_1.ModuleRef, core_1.HttpAdapterHost, discovery_1.DiscoveryService],
13
+ inject: [core_1.ModuleRef, core_1.HttpAdapterHost, nestjs_discovery_1.DiscoveryModule],
21
14
  };
22
15
  return {
23
16
  imports,
@@ -25,7 +18,6 @@ class PondSocketModule {
25
18
  global: isGlobal,
26
19
  module: PondSocketModule,
27
20
  providers: [
28
- discoveryServiceProvider,
29
21
  pondSocketProvider,
30
22
  ...providers,
31
23
  ...guards,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eleven-am/pondsocket-nest",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "PondSocket is a fast simple socket server",
5
5
  "keywords": [
6
6
  "socket",
@@ -28,18 +28,19 @@
28
28
  "pipeline": "npm run lint && npm run build && npm run push"
29
29
  },
30
30
  "dependencies": {
31
- "@eleven-am/pondsocket": "^0.1.141"
31
+ "@eleven-am/pondsocket": "^0.1.145",
32
+ "@golevelup/nestjs-discovery": "^4.0.0"
32
33
  },
33
34
  "devDependencies": {
34
- "@nestjs/common": "^10.3.0",
35
- "@nestjs/core": "^10.3.0",
36
- "@types/jest": "^29.5.11",
37
- "@typescript-eslint/eslint-plugin": "^6.19.0",
35
+ "@nestjs/common": "^10.3.3",
36
+ "@nestjs/core": "^10.3.3",
37
+ "@types/jest": "^29.5.12",
38
+ "@typescript-eslint/eslint-plugin": "^7.0.2",
38
39
  "eslint-plugin-file-progress": "^1.3.0",
39
40
  "eslint-plugin-import": "^2.29.1",
40
41
  "jest": "^29.7.0",
41
- "prettier": "^3.2.4",
42
- "ts-jest": "^29.1.1",
42
+ "prettier": "^3.2.5",
43
+ "ts-jest": "^29.1.2",
43
44
  "ts-loader": "^9.5.1",
44
45
  "ts-node": "^10.9.2",
45
46
  "typescript": "^5.3.3"
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.performAction = void 0;
13
13
  const guards_1 = require("./guards");
14
+ const narrow_1 = require("./narrow");
14
15
  const response_1 = require("./response");
15
16
  const context_1 = require("../context/context");
16
17
  const parametres_1 = require("../managers/parametres");
@@ -31,15 +32,15 @@ function performAction(instance, moduleRef, originalMethod, propertyKey, leaveEv
31
32
  const req = {};
32
33
  const res = {};
33
34
  if (request && response) {
34
- if ('joinParams' in request && 'broadcast' in response && !('sendOnly' in response)) {
35
+ if ((0, narrow_1.isJoinRequest)(request) && (0, narrow_1.isJoinResponse)(response)) {
35
36
  req.joinRequest = request;
36
37
  res.joinResponse = response;
37
38
  }
38
- else if ('channel' in request && 'sendOnly' in response) {
39
+ else if ((0, narrow_1.isEventRequest)(request) && (0, narrow_1.isEventResponse)(response)) {
39
40
  req.eventRequest = request;
40
41
  res.eventResponse = response;
41
42
  }
42
- else if ('headers' in request) {
43
+ else if ((0, narrow_1.isConnectionRequest)(request) && (0, narrow_1.isConnectionResponse)(response)) {
43
44
  req.connection = request;
44
45
  res.connection = response;
45
46
  }
@@ -53,10 +54,10 @@ function performAction(instance, moduleRef, originalMethod, propertyKey, leaveEv
53
54
  const canProceed = yield (0, guards_1.performGuards)(moduleRef, context);
54
55
  if (canProceed) {
55
56
  const data = yield originalMethod.apply(instance, retrieveParameters(context));
56
- (0, response_1.performResponse)(data, socketId, channel, response);
57
+ (0, response_1.performResponse)(socketId, channel, data, response);
57
58
  }
58
- else if (response) {
59
- response.reject('Unauthorized', 401);
59
+ else if (response && ((0, narrow_1.isJoinResponse)(response) || (0, narrow_1.isConnectionResponse)(response))) {
60
+ response.decline('Unauthorized', 403);
60
61
  }
61
62
  });
62
63
  }
@@ -3,15 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.performErrors = void 0;
4
4
  // eslint-disable-next-line import/no-unresolved
5
5
  const common_1 = require("@nestjs/common");
6
+ const narrow_1 = require("./narrow");
6
7
  function performErrors(error, response) {
8
+ let message;
9
+ let status;
10
+ if (error instanceof common_1.HttpException) {
11
+ message = error.message;
12
+ status = error.getStatus();
13
+ }
14
+ else if (error instanceof Error) {
15
+ message = error.message;
16
+ status = 500;
17
+ }
18
+ else {
19
+ message = 'An unknown error occurred';
20
+ status = 500;
21
+ }
7
22
  if (response.hasResponded) {
8
23
  return response;
9
24
  }
10
- if (error instanceof common_1.HttpException) {
11
- return response.reject(error.message, error.getStatus());
12
- }
13
- if (error instanceof Error) {
14
- return response.reject(error.message, 500);
25
+ if ((0, narrow_1.isEventResponse)(response)) {
26
+ return response.reply('UNKNOWN_ERROR', {
27
+ message,
28
+ status,
29
+ });
15
30
  }
31
+ return response.decline(message, status);
16
32
  }
17
33
  exports.performErrors = performErrors;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isConnectionResponse = exports.isJoinResponse = exports.isEventResponse = exports.isConnectionRequest = exports.isJoinRequest = exports.isEventRequest = void 0;
4
+ function isEventRequest(request) {
5
+ return 'channel' in request && !('joinParams' in request);
6
+ }
7
+ exports.isEventRequest = isEventRequest;
8
+ function isJoinRequest(request) {
9
+ return 'joinParams' in request;
10
+ }
11
+ exports.isJoinRequest = isJoinRequest;
12
+ function isConnectionRequest(request) {
13
+ return 'headers' in request;
14
+ }
15
+ exports.isConnectionRequest = isConnectionRequest;
16
+ function isEventResponse(response) {
17
+ return !('accept' in response);
18
+ }
19
+ exports.isEventResponse = isEventResponse;
20
+ function isJoinResponse(response) {
21
+ return 'accept' in response && 'broadcast' in response;
22
+ }
23
+ exports.isJoinResponse = isJoinResponse;
24
+ function isConnectionResponse(response) {
25
+ return 'accept' in response && !('broadcast' in response);
26
+ }
27
+ exports.isConnectionResponse = isConnectionResponse;
@@ -12,43 +12,59 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.performResponse = void 0;
15
+ const narrow_1 = require("./narrow");
15
16
  function isNotEmpty(value) {
16
17
  return value !== null &&
17
18
  value !== undefined &&
18
19
  value !== '' &&
19
20
  Object.keys(value).length !== 0;
20
21
  }
21
- function performResponse(data, socketId, channel, response) {
22
+ function performResponse(socketId, channel, data, response) {
22
23
  if (response && response.hasResponded || !isNotEmpty(data)) {
23
24
  return;
24
25
  }
25
- const { event, presence, assigns, broadcast } = data, rest = __rest(data, ["event", "presence", "assigns", "broadcast"]);
26
+ const { event, presence, assigns, broadcast, subscribeTo, unsubscribeFrom } = data, rest = __rest(data, ["event", "presence", "assigns", "broadcast", "subscribeTo", "unsubscribeFrom"]);
26
27
  if (response) {
27
- if (event && typeof event === 'string' && isNotEmpty(rest)) {
28
- if ('sendOnly' in response) {
29
- response.sendOnly(event, rest, assigns);
28
+ if ((0, narrow_1.isConnectionResponse)(response) || (0, narrow_1.isJoinResponse)(response)) {
29
+ response
30
+ .assign(typeof assigns === 'object' ? assigns : {})
31
+ .accept();
32
+ }
33
+ else {
34
+ response
35
+ .assign(typeof assigns === 'object' ? assigns : {});
36
+ }
37
+ if (isNotEmpty(rest)) {
38
+ if (event) {
39
+ response.reply(event, rest);
30
40
  }
31
- else {
32
- response.send(event, rest, assigns);
41
+ if (broadcast && ((0, narrow_1.isJoinResponse)(response) || (0, narrow_1.isEventResponse)(response))) {
42
+ response.broadcast(broadcast, rest);
33
43
  }
34
44
  }
35
- else if (isNotEmpty(assigns)) {
36
- response.accept(assigns);
45
+ if (subscribeTo) {
46
+ subscribeTo.forEach((channelName) => {
47
+ response.subscribeTo(channelName);
48
+ });
37
49
  }
38
- if (broadcast && typeof broadcast === 'string' && isNotEmpty(rest) && 'broadcast' in response) {
39
- response.broadcast(broadcast, rest);
50
+ if (unsubscribeFrom) {
51
+ unsubscribeFrom.forEach((channelName) => {
52
+ response.unsubscribeFrom(channelName);
53
+ });
40
54
  }
41
55
  }
42
- else if (channel && broadcast && typeof broadcast === 'string' && isNotEmpty(rest)) {
43
- channel.broadcastMessage(broadcast, rest);
44
- }
45
- if (presence && isNotEmpty(presence) && channel) {
46
- const existingPresence = channel.getPresences()[socketId];
47
- if (existingPresence) {
48
- channel.updatePresence(socketId, presence);
56
+ else if (channel) {
57
+ if (isNotEmpty(rest) && broadcast) {
58
+ channel.broadcast(broadcast, rest);
49
59
  }
50
- else if (response) {
51
- channel.trackPresence(socketId, presence);
60
+ if (isNotEmpty(presence)) {
61
+ const existingPresence = channel.getPresences()[socketId];
62
+ if (existingPresence) {
63
+ channel.updatePresence(socketId, presence);
64
+ }
65
+ else {
66
+ channel.trackPresence(socketId, presence);
67
+ }
52
68
  }
53
69
  }
54
70
  }
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.PondSocketService = void 0;
16
16
  const http_1 = require("http");
17
17
  const pondsocket_1 = __importDefault(require("@eleven-am/pondsocket"));
18
+ const constants_1 = require("../constants");
18
19
  const channel_1 = require("../managers/channel");
19
20
  const channelInstance_1 = require("../managers/channelInstance");
20
21
  const connection_1 = require("../managers/connection");
@@ -38,7 +39,7 @@ class PondSocketService {
38
39
  }
39
40
  init(httpAdapter) {
40
41
  return __awaiter(this, void 0, void 0, function* () {
41
- const groupedInstances = yield this.buildInstances();
42
+ const groupedInstances = yield this.getGroupedInstances();
42
43
  const app = httpAdapter.getInstance();
43
44
  const server = (0, http_1.createServer)(app);
44
45
  const socket = new pondsocket_1.default(server);
@@ -48,31 +49,6 @@ class PondSocketService {
48
49
  return socket;
49
50
  });
50
51
  }
51
- buildInstances() {
52
- return __awaiter(this, void 0, void 0, function* () {
53
- const modules = yield this.discovery.groupToModules();
54
- const values = [...modules.values()];
55
- const channelsWithNoEndpoint = values.filter((value) => value.endpoints.length === 0 && value.channels.length > 0);
56
- const channelsWithEndpoint = values.filter((value) => value.endpoints.length > 0 && value.channels.length > 0);
57
- const groupedInstances = channelsWithEndpoint.map((group) => group.endpoints.map((endpoint) => ({
58
- endpoint,
59
- channels: group.channels,
60
- }))).flat();
61
- if (channelsWithNoEndpoint.length > 0) {
62
- const appModule = yield this.discovery.getAppModule();
63
- const value = modules.get(appModule.dependencyType);
64
- if (value && value.endpoints.length > 0) {
65
- value.endpoints.forEach((endpoint) => {
66
- groupedInstances.push({
67
- endpoint,
68
- channels: channelsWithNoEndpoint.map((group) => group.channels).flat(),
69
- });
70
- });
71
- }
72
- }
73
- return groupedInstances;
74
- });
75
- }
76
52
  manageEndpoint(socket, groupedInstance) {
77
53
  const instance = groupedInstance.endpoint.instance;
78
54
  const constructor = instance.constructor;
@@ -134,5 +110,49 @@ class PondSocketService {
134
110
  }));
135
111
  }
136
112
  }
113
+ getGroupedInstances() {
114
+ return __awaiter(this, void 0, void 0, function* () {
115
+ const endpoints = yield this.discovery.providersWithMetaAtKey(constants_1.endpointKey);
116
+ const channels = yield this.discovery.providersWithMetaAtKey(constants_1.channelKey);
117
+ const modules = new Map();
118
+ endpoints.forEach((endpoint) => {
119
+ const module = endpoint.discoveredClass.parentModule.injectType;
120
+ if (!modules.has(module)) {
121
+ modules.set(module, {
122
+ endpoints: [],
123
+ channels: [],
124
+ });
125
+ }
126
+ modules.get(module).endpoints.push(endpoint.discoveredClass.instance);
127
+ });
128
+ channels.forEach((channel) => {
129
+ const module = channel.discoveredClass.parentModule.injectType;
130
+ if (!modules.has(module)) {
131
+ modules.set(module, {
132
+ endpoints: [],
133
+ channels: [],
134
+ });
135
+ }
136
+ modules.get(module).channels.push(channel.discoveredClass.instance);
137
+ });
138
+ const instances = [...modules.values()];
139
+ const channelsWithNoEndpoints = instances.filter((instance) => instance.channels.length > 0 && instance.endpoints.length === 0);
140
+ const channelsWithEndpoints = instances.filter((instance) => instance.channels.length > 0 && instance.endpoints.length > 0);
141
+ const groupedInstances = channelsWithEndpoints.map((instance) => instance.endpoints.map((endpoint) => ({
142
+ endpoint,
143
+ channels: instance.channels,
144
+ }))).flat();
145
+ const baseEndpoint = endpoints.find((endpoint) => endpoint.discoveredClass.parentModule.name === 'AppModule');
146
+ const baseEndpointInstance = groupedInstances.find((groupedInstance) => groupedInstance.endpoint.instance === (baseEndpoint === null || baseEndpoint === void 0 ? void 0 : baseEndpoint.discoveredClass.instance));
147
+ if (channelsWithNoEndpoints.length > 0 && baseEndpointInstance) {
148
+ const channels = channelsWithNoEndpoints.map((instance) => instance.channels).flat();
149
+ baseEndpointInstance.channels.push(...channels);
150
+ }
151
+ else if (channelsWithNoEndpoints.length > 0) {
152
+ throw new Error('No base endpoint found');
153
+ }
154
+ return groupedInstances;
155
+ });
156
+ }
137
157
  }
138
158
  exports.PondSocketService = PondSocketService;