payload-socket-plugin 1.1.4 → 2.0.0

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
@@ -6,6 +6,9 @@
6
6
 
7
7
  Real-time event broadcasting plugin for Payload CMS using Socket.IO with Redis support for multi-instance deployments.
8
8
 
9
+ > **Version 2.0.0+** supports Payload CMS v3.72.0+
10
+ > For Payload v2, use [v1.x](https://github.com/beewhoo/payload-socket-plugin/tree/v1.1.5)
11
+
9
12
  ## Features
10
13
 
11
14
  - ✅ **Real-time Events**: Broadcast collection changes (create, update, delete) to connected clients
@@ -18,17 +21,21 @@ Real-time event broadcasting plugin for Payload CMS using Socket.IO with Redis s
18
21
  ## Prerequisites
19
22
 
20
23
  - **Node.js**: >= 20.0.0
21
- - **Payload CMS**: ^2.0.0 || ^3.0.0
24
+ - **Payload CMS**: ^3.72.0 (for v2.x of this plugin)
22
25
  - **Redis** (optional): Required for multi-instance deployments
23
26
 
24
27
  ## Installation
25
28
 
29
+ ### For Payload v3 (Current)
30
+
31
+ ```bash
32
+ npm install payload-socket-plugin@^2.0.0
33
+ ```
34
+
35
+ ### For Payload v2 (Legacy)
36
+
26
37
  ```bash
27
- npm install payload-socket-plugin
28
- # or
29
- yarn add payload-socket-plugin
30
- # or
31
- pnpm add payload-socket-plugin
38
+ npm install payload-socket-plugin@^1.0.0
32
39
  ```
33
40
 
34
41
  ### Install Socket.IO Client (for frontend)
@@ -37,13 +44,13 @@ pnpm add payload-socket-plugin
37
44
  npm install socket.io-client
38
45
  ```
39
46
 
40
- ## Quick Start
47
+ ## Quick Start (Payload v3)
41
48
 
42
49
  ### 1. Configure the Plugin
43
50
 
44
51
  ```typescript
45
52
  // payload.config.ts
46
- import { buildConfig } from "payload/config";
53
+ import { buildConfig } from "payload";
47
54
  import { socketPlugin } from "payload-socket-plugin";
48
55
 
49
56
  export default buildConfig({
@@ -79,29 +86,42 @@ export default buildConfig({
79
86
  });
80
87
  ```
81
88
 
82
- ### 2. Initialize Socket.IO Server
89
+ ### 2. Initialize Socket.IO Server (Next.js Custom Server)
83
90
 
84
91
  ```typescript
85
92
  // server.ts
86
93
  import express from "express";
87
- import payload from "payload";
94
+ import { createServer } from "http";
95
+ import next from "next";
96
+ import { getPayload } from "payload";
88
97
  import { initSocketIO } from "payload-socket-plugin";
98
+ import config from "@payload-config";
89
99
 
90
- const app = express();
100
+ const port = parseInt(process.env.PORT || "3000", 10);
101
+ const dev = process.env.NODE_ENV !== "production";
102
+ const app = next({ dev });
103
+ const handle = app.getRequestHandler();
91
104
 
92
- // Initialize Payload
93
- await payload.init({
94
- secret: process.env.PAYLOAD_SECRET,
95
- express: app,
96
- });
105
+ app.prepare().then(async () => {
106
+ const server = express();
97
107
 
98
- // Start HTTP server
99
- const server = app.listen(3000, () => {
100
- console.log("Server running on port 3000");
101
- });
108
+ // Let Next.js handle all routes
109
+ server.all("*", (req, res) => handle(req, res));
102
110
 
103
- // Initialize Socket.IO
104
- await initSocketIO(server);
111
+ // Create HTTP server
112
+ const httpServer = createServer(server);
113
+
114
+ // Get Payload instance
115
+ const payload = await getPayload({ config });
116
+
117
+ // Initialize Socket.IO with the payload instance
118
+ await initSocketIO(httpServer, payload);
119
+
120
+ // Start server
121
+ httpServer.listen(port, () => {
122
+ console.log(`Server listening on port ${port}`);
123
+ });
124
+ });
105
125
  ```
106
126
 
107
127
  ### 3. Connect from Client
@@ -434,6 +454,43 @@ import type {
434
454
  - Check that your `tsconfig.json` includes the plugin's types
435
455
  - Verify Payload CMS version compatibility (>= 2.0.0)
436
456
 
457
+ ## Multi-Instance Deployments with Redis
458
+
459
+ When using Redis adapter for multi-instance deployments, user data is automatically synchronized across all server instances:
460
+
461
+ - **`socket.data.user`**: Automatically synchronized across servers via Redis adapter
462
+ - **`socket.user`**: Only available on the local server where the socket connected (backward compatibility)
463
+
464
+ ### Accessing User Data in Custom Handlers
465
+
466
+ ```typescript
467
+ socketPlugin({
468
+ onSocketConnection: (socket, io, payload) => {
469
+ socket.on("get-active-users", async (roomName) => {
470
+ const sockets = await io.in(roomName).fetchSockets();
471
+
472
+ const users = sockets.map((s) => {
473
+ // Use socket.data.user for Redis compatibility (works across all servers)
474
+ // Fallback to socket.user for local connections
475
+ const user = s.data.user || (s as any).user;
476
+ return {
477
+ id: user?.id,
478
+ email: user?.email,
479
+ };
480
+ });
481
+
482
+ socket.emit("active-users", users);
483
+ });
484
+ },
485
+ });
486
+ ```
487
+
488
+ **Important**: When using `io.in(room).fetchSockets()` with Redis adapter:
489
+
490
+ - Remote sockets (from other servers) will have `socket.data.user` populated
491
+ - Local sockets will have both `socket.data.user` and `socket.user` populated
492
+ - Always check `socket.data.user` first for Redis compatibility
493
+
437
494
  ## Performance Considerations
438
495
 
439
496
  - **Redis**: Highly recommended for production multi-instance deployments
@@ -454,6 +511,99 @@ import type {
454
511
  - No built-in event replay or history mechanism
455
512
  - Redis is required for multi-instance deployments
456
513
 
514
+ ## Migration Guide
515
+
516
+ ### Migrating from v1.x (Payload v2) to v2.x (Payload v3)
517
+
518
+ Version 2.0.0 introduces breaking changes to support Payload CMS v3:
519
+
520
+ #### 1. Update Dependencies
521
+
522
+ ```bash
523
+ npm install payload@^3.72.0 payload-socket-plugin@^2.0.0
524
+ ```
525
+
526
+ #### 2. Update Import Paths
527
+
528
+ ```typescript
529
+ // ❌ Old (v1.x)
530
+ import { buildConfig } from "payload/config";
531
+
532
+ // ✅ New (v2.x)
533
+ import { buildConfig } from "payload";
534
+ ```
535
+
536
+ #### 3. Update Server Initialization
537
+
538
+ The `initSocketIO` function now requires the Payload instance as a parameter:
539
+
540
+ ```typescript
541
+ // ❌ Old (v1.x)
542
+ import payload from "payload";
543
+ import { initSocketIO } from "payload-socket-plugin";
544
+
545
+ await payload.init({ secret, express: app });
546
+ const server = app.listen(3000);
547
+ await initSocketIO(server);
548
+ ```
549
+
550
+ ```typescript
551
+ // ✅ New (v2.x)
552
+ import { getPayload } from "payload";
553
+ import { initSocketIO } from "payload-socket-plugin";
554
+ import config from "@payload-config";
555
+
556
+ const payload = await getPayload({ config });
557
+ const httpServer = createServer(app);
558
+ await initSocketIO(httpServer, payload);
559
+ ```
560
+
561
+ #### 4. Update Next.js Integration
562
+
563
+ Payload v3 uses Next.js by default. Update your server setup:
564
+
565
+ ```typescript
566
+ // server.ts (Payload v3 with Next.js)
567
+ import express from "express";
568
+ import { createServer } from "http";
569
+ import next from "next";
570
+ import { getPayload } from "payload";
571
+ import { initSocketIO } from "payload-socket-plugin";
572
+ import config from "@payload-config";
573
+
574
+ const app = next({ dev: process.env.NODE_ENV !== "production" });
575
+ const handle = app.getRequestHandler();
576
+
577
+ app.prepare().then(async () => {
578
+ const server = express();
579
+ server.all("*", (req, res) => handle(req, res));
580
+
581
+ const httpServer = createServer(server);
582
+ const payload = await getPayload({ config });
583
+
584
+ await initSocketIO(httpServer, payload);
585
+
586
+ httpServer.listen(3000);
587
+ });
588
+ ```
589
+
590
+ #### 5. Environment Variables
591
+
592
+ Make sure to set `SOCKET_ENABLED=true` in your `.env` file if you're using the `enabled` option:
593
+
594
+ ```bash
595
+ # .env
596
+ SOCKET_ENABLED=true
597
+ REDIS_URL=redis://localhost:6379
598
+ ```
599
+
600
+ #### Breaking Changes Summary
601
+
602
+ - **Minimum Payload version**: Now requires Payload v3.72.0+
603
+ - **`initSocketIO` signature**: Now requires `(httpServer, payloadInstance)` instead of just `(httpServer)`
604
+ - **Import paths**: Changed from `payload/config` to `payload`
605
+ - **Payload initialization**: Use `getPayload({ config })` instead of `payload.init()`
606
+
457
607
  ## Changelog
458
608
 
459
609
  See [CHANGELOG.md](./CHANGELOG.md) for version history.
package/dist/index.js CHANGED
@@ -53,6 +53,7 @@ const socketManager_1 = require("./socketManager");
53
53
  */
54
54
  const socketPlugin = (pluginOptions = {}) => {
55
55
  return (incomingConfig) => {
56
+ var _a;
56
57
  // Default options
57
58
  const options = {
58
59
  enabled: true,
@@ -79,12 +80,13 @@ const socketPlugin = (pluginOptions = {}) => {
79
80
  * Create event payload from hook arguments
80
81
  */
81
82
  const createEventPayload = (type, collection, args) => {
83
+ var _a, _b;
82
84
  return {
83
85
  type,
84
86
  collection,
85
- id: args.doc?.id || args.id,
87
+ id: ((_a = args.doc) === null || _a === void 0 ? void 0 : _a.id) || args.id,
86
88
  doc: type === "delete" ? undefined : args.doc,
87
- user: args.req?.user
89
+ user: ((_b = args.req) === null || _b === void 0 ? void 0 : _b.user)
88
90
  ? {
89
91
  id: args.req.user.id,
90
92
  email: args.req.user.email,
@@ -97,7 +99,8 @@ const socketPlugin = (pluginOptions = {}) => {
97
99
  /**
98
100
  * Add hooks to collections
99
101
  */
100
- const collectionsWithHooks = incomingConfig.collections?.map((collection) => {
102
+ const collectionsWithHooks = ((_a = incomingConfig.collections) === null || _a === void 0 ? void 0 : _a.map((collection) => {
103
+ var _a, _b;
101
104
  // Skip if events should not be emitted for this collection
102
105
  if (!shouldEmitForCollection(collection.slug)) {
103
106
  return collection;
@@ -108,7 +111,7 @@ const socketPlugin = (pluginOptions = {}) => {
108
111
  ...collection.hooks,
109
112
  // After change hook - only emit for updates
110
113
  afterChange: [
111
- ...(collection.hooks?.afterChange || []),
114
+ ...(((_a = collection.hooks) === null || _a === void 0 ? void 0 : _a.afterChange) || []),
112
115
  async (args) => {
113
116
  try {
114
117
  // Only emit events for updates, not creates
@@ -125,7 +128,7 @@ const socketPlugin = (pluginOptions = {}) => {
125
128
  ],
126
129
  // After delete hook
127
130
  afterDelete: [
128
- ...(collection.hooks?.afterDelete || []),
131
+ ...(((_b = collection.hooks) === null || _b === void 0 ? void 0 : _b.afterDelete) || []),
129
132
  async (args) => {
130
133
  try {
131
134
  const event = createEventPayload("delete", collection.slug, args);
@@ -138,7 +141,7 @@ const socketPlugin = (pluginOptions = {}) => {
138
141
  ],
139
142
  },
140
143
  };
141
- }) || [];
144
+ })) || [];
142
145
  /**
143
146
  * Add onInit hook to initialize Socket.IO server
144
147
  */
@@ -147,13 +150,10 @@ const socketPlugin = (pluginOptions = {}) => {
147
150
  if (incomingConfig.onInit) {
148
151
  await incomingConfig.onInit(payload);
149
152
  }
150
- // Initialize Socket.IO server
151
- // The server instance is available after Payload initializes with Express
152
- if (payload.express) {
153
- // Store the socket manager for later initialization
154
- // The HTTP server will be initialized in server.ts using initSocketIO()
155
- payload.__socketManager = socketManager;
156
- }
153
+ // Store the socket manager for later initialization
154
+ // The HTTP server will be initialized in server.ts using initSocketIO()
155
+ // This works for both standard Payload server and custom Express servers
156
+ payload.__socketManager = socketManager;
157
157
  };
158
158
  return {
159
159
  ...incomingConfig,
@@ -1,4 +1,5 @@
1
1
  import { Server as HTTPServer } from "http";
2
+ import type { Payload } from "payload";
2
3
  /**
3
4
  * Initialize Socket.IO server with the HTTP server instance
4
5
  * This should be called after the HTTP server is created
@@ -6,13 +7,17 @@ import { Server as HTTPServer } from "http";
6
7
  * @example
7
8
  * ```ts
8
9
  * import { initSocketIO } from 'payload-socket-plugin';
10
+ * import { getPayload } from 'payload';
11
+ * import config from '@payload-config';
12
+ *
13
+ * const payload = await getPayload({ config });
9
14
  *
10
15
  * const server = app.listen(PORT, () => {
11
16
  * console.log(`Server is running on port ${PORT}`);
12
17
  * });
13
18
  *
14
19
  * // Initialize Socket.IO
15
- * await initSocketIO(server);
20
+ * await initSocketIO(server, payload);
16
21
  * ```
17
22
  */
18
- export declare function initSocketIO(httpServer: HTTPServer): Promise<void>;
23
+ export declare function initSocketIO(httpServer: HTTPServer, payloadInstance: Payload): Promise<void>;
@@ -1,10 +1,6 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.initSocketIO = initSocketIO;
7
- const payload_1 = __importDefault(require("payload"));
8
4
  /**
9
5
  * Initialize Socket.IO server with the HTTP server instance
10
6
  * This should be called after the HTTP server is created
@@ -12,29 +8,34 @@ const payload_1 = __importDefault(require("payload"));
12
8
  * @example
13
9
  * ```ts
14
10
  * import { initSocketIO } from 'payload-socket-plugin';
11
+ * import { getPayload } from 'payload';
12
+ * import config from '@payload-config';
13
+ *
14
+ * const payload = await getPayload({ config });
15
15
  *
16
16
  * const server = app.listen(PORT, () => {
17
17
  * console.log(`Server is running on port ${PORT}`);
18
18
  * });
19
19
  *
20
20
  * // Initialize Socket.IO
21
- * await initSocketIO(server);
21
+ * await initSocketIO(server, payload);
22
22
  * ```
23
23
  */
24
- async function initSocketIO(httpServer) {
24
+ async function initSocketIO(httpServer, payloadInstance) {
25
25
  try {
26
26
  // Get the socket manager from payload instance
27
- const socketManager = payload_1.default.__socketManager;
27
+ const socketManager = payloadInstance
28
+ .__socketManager;
28
29
  if (!socketManager) {
29
- payload_1.default.logger.warn("Socket.IO manager not found. Make sure socketPlugin is configured.");
30
+ payloadInstance.logger.warn("Socket.IO manager not found. Make sure socketPlugin is configured.");
30
31
  return;
31
32
  }
32
33
  // Initialize Socket.IO with the HTTP server
33
- await socketManager.init(httpServer);
34
- payload_1.default.logger.info("Socket.IO initialized successfully");
34
+ await socketManager.init(httpServer, payloadInstance);
35
+ payloadInstance.logger.info("Socket.IO initialized successfully");
35
36
  }
36
37
  catch (error) {
37
- payload_1.default.logger.error("Failed to initialize Socket.IO:", error);
38
+ payloadInstance.logger.error("Failed to initialize Socket.IO:", error);
38
39
  throw error;
39
40
  }
40
41
  }
@@ -1,6 +1,7 @@
1
1
  import { Server as SocketIOServer } from "socket.io";
2
2
  import { Server as HTTPServer } from "http";
3
3
  import { RealtimeEventsPluginOptions, RealtimeEventPayload } from "./types";
4
+ import type { Payload } from "payload";
4
5
  /**
5
6
  * Socket.IO Manager for handling real-time events with Redis adapter
6
7
  * Supports multiple Payload instances for production environments
@@ -10,11 +11,12 @@ export declare class SocketIOManager {
10
11
  private pubClient;
11
12
  private subClient;
12
13
  private options;
14
+ private payload;
13
15
  constructor(options: RealtimeEventsPluginOptions);
14
16
  /**
15
17
  * Initialize Socket.IO server with Redis adapter
16
18
  */
17
- init(server: HTTPServer): Promise<SocketIOServer>;
19
+ init(server: HTTPServer, payloadInstance: Payload): Promise<SocketIOServer>;
18
20
  /**
19
21
  * Setup Redis adapter for multi-instance synchronization
20
22
  */
@@ -8,7 +8,6 @@ const socket_io_1 = require("socket.io");
8
8
  const redis_adapter_1 = require("@socket.io/redis-adapter");
9
9
  const ioredis_1 = __importDefault(require("ioredis"));
10
10
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
11
- const payload_1 = __importDefault(require("payload"));
12
11
  /**
13
12
  * Socket.IO Manager for handling real-time events with Redis adapter
14
13
  * Supports multiple Payload instances for production environments
@@ -18,12 +17,14 @@ class SocketIOManager {
18
17
  this.io = null;
19
18
  this.pubClient = null;
20
19
  this.subClient = null;
20
+ this.payload = null;
21
21
  this.options = options;
22
22
  }
23
23
  /**
24
24
  * Initialize Socket.IO server with Redis adapter
25
25
  */
26
- async init(server) {
26
+ async init(server, payloadInstance) {
27
+ this.payload = payloadInstance;
27
28
  const { redis, socketIO = {} } = this.options;
28
29
  // Create Socket.IO server
29
30
  this.io = new socket_io_1.Server(server, {
@@ -42,16 +43,17 @@ class SocketIOManager {
42
43
  this.setupAuthentication();
43
44
  // Setup connection handlers
44
45
  this.setupConnectionHandlers();
45
- payload_1.default.logger.info("Socket.IO server initialized with real-time events plugin");
46
+ this.payload.logger.info("Socket.IO server initialized with real-time events plugin");
46
47
  return this.io;
47
48
  }
48
49
  /**
49
50
  * Setup Redis adapter for multi-instance synchronization
50
51
  */
51
52
  async setupRedisAdapter() {
52
- const redisUrl = this.options.redis?.url;
53
+ var _a;
54
+ const redisUrl = (_a = this.options.redis) === null || _a === void 0 ? void 0 : _a.url;
53
55
  if (!redisUrl) {
54
- payload_1.default.logger.warn("Redis URL not configured. Skipping Redis adapter setup. Set redis.url in plugin options.");
56
+ this.payload.logger.warn("Redis URL not configured. Skipping Redis adapter setup. Set redis.url in plugin options.");
55
57
  return;
56
58
  }
57
59
  try {
@@ -68,10 +70,10 @@ class SocketIOManager {
68
70
  new Promise((resolve) => this.subClient.once("ready", resolve)),
69
71
  ]);
70
72
  this.io.adapter((0, redis_adapter_1.createAdapter)(this.pubClient, this.subClient));
71
- payload_1.default.logger.info("Redis adapter configured for Socket.IO multi-instance support");
73
+ this.payload.logger.info("Redis adapter configured for Socket.IO multi-instance support");
72
74
  }
73
75
  catch (error) {
74
- payload_1.default.logger.error("Failed to setup Redis adapter:", error);
76
+ this.payload.logger.error("Failed to setup Redis adapter:", error);
75
77
  throw error;
76
78
  }
77
79
  }
@@ -91,22 +93,26 @@ class SocketIOManager {
91
93
  return next(new Error("Authentication token required"));
92
94
  }
93
95
  try {
94
- const decoded = jsonwebtoken_1.default.verify(token, payload_1.default.secret);
96
+ const decoded = jsonwebtoken_1.default.verify(token, this.payload.secret);
95
97
  // Fetch full user document from Payload
96
- const userDoc = await payload_1.default.findByID({
98
+ const userDoc = await this.payload.findByID({
97
99
  collection: decoded.collection || "users",
98
100
  id: decoded.id,
99
101
  });
100
102
  if (!userDoc) {
101
103
  return next(new Error("User not found"));
102
104
  }
103
- // Attach user info
104
- socket.user = {
105
+ const userInfo = {
105
106
  id: userDoc.id,
106
107
  email: userDoc.email,
107
108
  collection: decoded.collection || "users",
108
109
  role: userDoc.role,
109
110
  };
111
+ // Store in socket.data for Redis adapter compatibility
112
+ // socket.data is automatically synchronized across servers via Redis
113
+ socket.data.user = userInfo;
114
+ // Also attach to socket.user for backward compatibility
115
+ socket.user = userInfo;
110
116
  next();
111
117
  }
112
118
  catch (jwtError) {
@@ -114,7 +120,7 @@ class SocketIOManager {
114
120
  }
115
121
  }
116
122
  catch (error) {
117
- payload_1.default.logger.error("Socket authentication error:", error);
123
+ this.payload.logger.error("Socket authentication error:", error);
118
124
  next(new Error("Authentication failed"));
119
125
  }
120
126
  });
@@ -124,7 +130,8 @@ class SocketIOManager {
124
130
  */
125
131
  setupConnectionHandlers() {
126
132
  this.io.on("connection", async (socket) => {
127
- payload_1.default.logger.info(`Client connected: ${socket.id}, User: ${socket.user?.email || socket.user?.id}`);
133
+ var _a, _b;
134
+ this.payload.logger.info(`Client connected: ${socket.id}, User: ${((_a = socket.user) === null || _a === void 0 ? void 0 : _a.email) || ((_b = socket.user) === null || _b === void 0 ? void 0 : _b.id)}`);
128
135
  // Allow clients to subscribe to specific collections
129
136
  socket.on("subscribe", (collections) => {
130
137
  const collectionList = Array.isArray(collections)
@@ -132,7 +139,7 @@ class SocketIOManager {
132
139
  : [collections];
133
140
  collectionList.forEach((collection) => {
134
141
  socket.join(`collection:${collection}`);
135
- payload_1.default.logger.info(`Client ${socket.id} subscribed to collection: ${collection}`);
142
+ this.payload.logger.info(`Client ${socket.id} subscribed to collection: ${collection}`);
136
143
  });
137
144
  });
138
145
  // Allow clients to unsubscribe from collections
@@ -142,25 +149,27 @@ class SocketIOManager {
142
149
  : [collections];
143
150
  collectionList.forEach((collection) => {
144
151
  socket.leave(`collection:${collection}`);
145
- payload_1.default.logger.info(`Client ${socket.id} unsubscribed from collection: ${collection}`);
152
+ this.payload.logger.info(`Client ${socket.id} unsubscribed from collection: ${collection}`);
146
153
  });
147
154
  });
148
155
  // Allow clients to join collection rooms (alias for subscribe)
149
156
  socket.on("join-collection", (collection) => {
157
+ var _a;
150
158
  const roomName = `collection:${collection}`;
151
159
  socket.join(roomName);
152
- payload_1.default.logger.info(`Client ${socket.id} (${socket.user?.email}) joined collection room: ${roomName}`);
160
+ this.payload.logger.info(`Client ${socket.id} (${(_a = socket.user) === null || _a === void 0 ? void 0 : _a.email}) joined collection room: ${roomName}`);
153
161
  });
154
162
  // Handle disconnection
155
163
  socket.on("disconnect", () => {
156
- payload_1.default.logger.info(`Client disconnected: ${socket.id}, User: ${socket.user?.email || socket.user?.id}`);
164
+ var _a, _b;
165
+ this.payload.logger.info(`Client disconnected: ${socket.id}, User: ${((_a = socket.user) === null || _a === void 0 ? void 0 : _a.email) || ((_b = socket.user) === null || _b === void 0 ? void 0 : _b.id)}`);
157
166
  });
158
167
  if (this.options.onSocketConnection) {
159
168
  try {
160
- await this.options.onSocketConnection(socket, this.io, payload_1.default);
169
+ await this.options.onSocketConnection(socket, this.io, this.payload);
161
170
  }
162
171
  catch (error) {
163
- payload_1.default.logger.error(`Error in custom socket connection handler: ${error}`);
172
+ this.payload.logger.error(`Error in custom socket connection handler: ${error}`);
164
173
  }
165
174
  }
166
175
  });
@@ -170,7 +179,7 @@ class SocketIOManager {
170
179
  */
171
180
  async emitEvent(event) {
172
181
  if (!this.io) {
173
- payload_1.default.logger.warn("Socket.IO server not initialized, cannot emit event");
182
+ this.payload.logger.warn("Socket.IO server not initialized, cannot emit event");
174
183
  return;
175
184
  }
176
185
  const { authorize, shouldEmit, transformEvent } = this.options;
@@ -190,8 +199,10 @@ class SocketIOManager {
190
199
  const sockets = await this.io.in(room).fetchSockets();
191
200
  for (const socket of sockets) {
192
201
  const authSocket = socket;
193
- if (authSocket.user) {
194
- const isAuthorized = await collectionHandler(authSocket.user, finalEvent);
202
+ // Use socket.data.user for remote sockets (Redis adapter), fallback to socket.user for local
203
+ const user = socket.data.user || authSocket.user;
204
+ if (user) {
205
+ const isAuthorized = await collectionHandler(user, finalEvent);
195
206
  if (isAuthorized) {
196
207
  socket.emit("payload:event", finalEvent);
197
208
  }
@@ -226,7 +237,9 @@ class SocketIOManager {
226
237
  if (this.subClient) {
227
238
  await this.subClient.quit();
228
239
  }
229
- payload_1.default.logger.info("Socket.IO server closed");
240
+ if (this.payload) {
241
+ this.payload.logger.info("Socket.IO server closed");
242
+ }
230
243
  }
231
244
  }
232
245
  exports.SocketIOManager = SocketIOManager;
package/package.json CHANGED
@@ -1,10 +1,21 @@
1
1
  {
2
2
  "name": "payload-socket-plugin",
3
- "version": "1.1.4",
4
- "description": "Real-time Socket.IO plugin for Payload CMS with Redis support",
3
+ "version": "2.0.0",
4
+ "description": "Real-time Socket.IO plugin for Payload CMS v3 with Redis support",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
- "browser": "dist/browser.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./browser": {
14
+ "import": "./dist/browser.js",
15
+ "require": "./dist/browser.js",
16
+ "types": "./dist/browser.d.ts"
17
+ }
18
+ },
8
19
  "files": [
9
20
  "dist",
10
21
  "README.md"
@@ -30,6 +41,9 @@
30
41
  "engines": {
31
42
  "node": ">=20.0.0"
32
43
  },
44
+ "peerDependencies": {
45
+ "payload": "^3.72.0"
46
+ },
33
47
  "dependencies": {
34
48
  "@socket.io/redis-adapter": "^8.0.0",
35
49
  "ioredis": "^5.3.0",