larasopp 1.2.23 → 1.2.25

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Sergey Serov
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Sergey Serov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,70 +1,443 @@
1
- # Laravel websocket client
2
-
3
- #### Server
4
- ```
5
- https://www.npmjs.com/package/larasopp-server
6
- ```
7
- #### Laravel package
8
- ```
9
- composer require larasopp/larasopp
10
- ```
11
-
12
- ### Connect app to websocket
13
-
14
- ```js
15
- ...
16
- import Larasopp from "Larasopp";
17
-
18
- const larasopp = new Larasopp({
19
- host: 'ws://127.0.0.1:3001',
20
- token: 'token'
21
- });
22
-
23
- larasopp.connect();
24
- //or
25
- larasopp.connect('token');
26
-
27
- ```
28
-
29
- ### Update user token
30
-
31
- ```js
32
- larasopp.setToken('new token');
33
- ```
34
-
35
- ### Subscribe on channel and listen event
36
-
37
- ```js
38
- const listener = larasopp.subscribe('chat').listen('message',(data) => {
39
- console.log(data.text); // Hello World
40
- });
41
-
42
- // Unsubscribe event
43
- listener.remove();
44
- ```
45
-
46
- ### Trigger event on subscribe channel
47
-
48
- ```js
49
- larasopp.trigger('chat','message',{
50
- text: 'Hello World'
51
- },'public');
52
- ```
53
-
54
- ### Unsubscribe channel
55
-
56
- ```js
57
- larasopp.unsubscribe('chat');
58
- ```
59
-
60
- ### Disconnect
61
-
62
- ```js
63
- larasopp.disconnect();
64
- ```
65
-
66
- ### Permissions
67
-
68
- ```js
69
- 'public' | 'protected' | 'private'
70
- ```
1
+ # Larasopp Client
2
+
3
+ WebSocket client for Laravel with full TypeScript support and type safety.
4
+
5
+ ## Features
6
+
7
+ - 🔌 **WebSocket Connection** - Real-time bidirectional communication
8
+ - 🎯 **Full TypeScript Support** - Complete type safety for channels, events, and users
9
+ - 📡 **Channel Subscriptions** - Subscribe to multiple channels
10
+ - 🎧 **Event Listeners** - Listen to specific events with typed callbacks
11
+ - 👥 **Presence Channels** - Track users joining/leaving channels
12
+ - 🔐 **Authentication** - Token-based authentication
13
+ - 🔄 **Auto Reconnect** - Automatic reconnection with configurable delays
14
+ - ✅ **Type Safety** - Compile-time type checking for all operations
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install larasopp
20
+ # or
21
+ yarn add larasopp
22
+ # or
23
+ pnpm add larasopp
24
+ ```
25
+
26
+ ## Related Packages
27
+
28
+ - **Server**: [larasopp-server](https://www.npmjs.com/package/larasopp-server)
29
+ - **Laravel Package**: `composer require larasopp/larasopp`
30
+
31
+ ## Quick Start
32
+
33
+ ```typescript
34
+ import Larasopp from 'larasopp';
35
+
36
+ const larasopp = new Larasopp({
37
+ host: 'ws://127.0.0.1:3001',
38
+ token: 'your-auth-token'
39
+ });
40
+
41
+ larasopp.connect();
42
+ ```
43
+
44
+ ## TypeScript Support
45
+
46
+ Larasopp provides full TypeScript support with generic types for channels, events, and users.
47
+
48
+ ### Defining Types
49
+
50
+ ```typescript
51
+ // Define your channel events structure
52
+ type ChannelsEvents = {
53
+ chat: {
54
+ message: { text: string; author: string; timestamp: number };
55
+ typing: { user: string };
56
+ userJoined: { user: string };
57
+ };
58
+ notifications: {
59
+ alert: { title: string; body: string; type: 'info' | 'warning' | 'error' };
60
+ update: { version: string };
61
+ };
62
+ };
63
+
64
+ // Define your user type
65
+ interface MyUser {
66
+ id: number;
67
+ name: string;
68
+ email: string;
69
+ avatar?: string;
70
+ }
71
+
72
+ // Create typed instance
73
+ const larasopp = new Larasopp<ChannelsEvents, MyUser>({
74
+ host: 'ws://127.0.0.1:3001',
75
+ token: 'your-token'
76
+ });
77
+ ```
78
+
79
+ ## API Reference
80
+
81
+ ### Configuration
82
+
83
+ ```typescript
84
+ interface IConfig {
85
+ host: string; // WebSocket server URL
86
+ token?: string; // Authentication token
87
+ reviver?: (key: string, value: any) => any; // JSON reviver function
88
+ dataReviver?: { [key: string]: (value: any) => any }; // Per-key revivers
89
+ reconnect?: number; // Max reconnection attempts
90
+ reconnectDelay?: number; // Delay between reconnections (ms)
91
+ }
92
+ ```
93
+
94
+ ### Connection
95
+
96
+ #### `connect(token?: string)`
97
+
98
+ Connect to the WebSocket server.
99
+
100
+ ```typescript
101
+ larasopp.connect();
102
+ // or with token
103
+ larasopp.connect('new-token');
104
+ ```
105
+
106
+ #### `disconnect()`
107
+
108
+ Disconnect from the WebSocket server.
109
+
110
+ ```typescript
111
+ larasopp.disconnect();
112
+ ```
113
+
114
+ #### `setToken(token: string)`
115
+
116
+ Update authentication token.
117
+
118
+ ```typescript
119
+ larasopp.setToken('new-token');
120
+ ```
121
+
122
+ ### Channels
123
+
124
+ #### `subscribe<K>(channel: K): Listener`
125
+
126
+ Subscribe to a channel. Returns a `Listener` instance for the channel.
127
+
128
+ ```typescript
129
+ // TypeScript will enforce that 'chat' exists in ChannelsEvents
130
+ const listener = larasopp.subscribe('chat');
131
+ ```
132
+
133
+ #### `unsubscribe<K>(channel: K)`
134
+
135
+ Unsubscribe from a channel.
136
+
137
+ ```typescript
138
+ larasopp.unsubscribe('chat');
139
+ ```
140
+
141
+ #### `countListeners<K>(channel: K): number`
142
+
143
+ Get the number of listeners for a channel.
144
+
145
+ ```typescript
146
+ const count = larasopp.countListeners('chat');
147
+ ```
148
+
149
+ ### Events
150
+
151
+ #### `listen<K>(event: K, callback, withCache?: boolean): this`
152
+
153
+ Listen to a specific event on the subscribed channel.
154
+
155
+ ```typescript
156
+ const listener = larasopp.subscribe('chat');
157
+
158
+ // TypeScript knows 'message' exists and its data type
159
+ listener.listen('message', (data) => {
160
+ // data is typed as { text: string; author: string; timestamp: number }
161
+ console.log(data.text, data.author);
162
+ });
163
+
164
+ // withCache: true will call callback immediately if cached data exists
165
+ listener.listen('typing', (data) => {
166
+ console.log(data.user, 'is typing');
167
+ }, true);
168
+ ```
169
+
170
+ #### `trigger<K, E>(channel: K, event: E, message, permission?, waitSubscribe?): void`
171
+
172
+ Trigger an event on a channel.
173
+
174
+ **Permission Levels:**
175
+
176
+ - **`'public'`** - Event will be sent to all channel subscribers AND to the API application
177
+ - **`'protected'`** - Event will be sent to all channel subscribers but NOT to the API application
178
+ - **`'private'`** - Event will be sent ONLY to the API application, not to channel subscribers
179
+
180
+ ```typescript
181
+ // TypeScript ensures 'chat' channel and 'message' event exist
182
+ // and that the message matches the event type
183
+
184
+ // Public: sent to all subscribers and API
185
+ larasopp.trigger('chat', 'message', {
186
+ text: 'Hello World',
187
+ author: 'John Doe',
188
+ timestamp: Date.now()
189
+ }, 'public');
190
+
191
+ // Protected: sent to subscribers only, not to API
192
+ larasopp.trigger('chat', 'message', {
193
+ text: 'User message',
194
+ author: 'Jane Doe',
195
+ timestamp: Date.now()
196
+ }, 'protected');
197
+
198
+ // Private: sent to API only, not to subscribers
199
+ larasopp.trigger('chat', 'message', {
200
+ text: 'Admin notification',
201
+ author: 'System',
202
+ timestamp: Date.now()
203
+ }, 'private', true); // waitSubscribe: true waits for subscription
204
+ ```
205
+
206
+ ### Event Permissions
207
+
208
+ When triggering events, you can specify one of three permission levels:
209
+
210
+ | Permission | Channel Subscribers | API Application | Use Case |
211
+ |------------|---------------------|-----------------|----------|
212
+ | `'public'` | ✅ Yes | ✅ Yes | Broadcast to everyone including server-side processing |
213
+ | `'protected'` | ✅ Yes | ❌ No | Client-to-client communication without server handling |
214
+ | `'private'` | ❌ No | ✅ Yes | Server-side only events (notifications, admin actions) |
215
+
216
+ **Examples:**
217
+
218
+ ```typescript
219
+ // Public: Broadcast message to all users and process on server
220
+ larasopp.trigger('chat', 'message', {
221
+ text: 'Hello everyone!',
222
+ author: 'John'
223
+ }, 'public');
224
+
225
+ // Protected: Send to users but don't trigger server-side handlers
226
+ larasopp.trigger('chat', 'typing', {
227
+ user: 'John'
228
+ }, 'protected');
229
+
230
+ // Private: Server-side only (e.g., admin notifications, system events)
231
+ larasopp.trigger('notifications', 'alert', {
232
+ title: 'System Maintenance',
233
+ body: 'Scheduled maintenance in 5 minutes'
234
+ }, 'private');
235
+ ```
236
+
237
+ ### Presence Channels
238
+
239
+ #### `here(callback, withCache?: boolean): this`
240
+
241
+ Get list of users currently in the channel.
242
+
243
+ ```typescript
244
+ listener.here((users) => {
245
+ // users is typed as MyUser[]
246
+ users.forEach(user => {
247
+ console.log(user.name, 'is here');
248
+ });
249
+ });
250
+ ```
251
+
252
+ #### `joining(callback, withCache?: boolean): this`
253
+
254
+ Listen for users joining the channel.
255
+
256
+ ```typescript
257
+ listener.joining((user) => {
258
+ // user is typed as MyUser
259
+ console.log(user.name, 'joined');
260
+ });
261
+ ```
262
+
263
+ #### `leaving(callback, withCache?: boolean): this`
264
+
265
+ Listen for users leaving the channel.
266
+
267
+ ```typescript
268
+ listener.leaving((user) => {
269
+ // user is typed as MyUser
270
+ console.log(user.name, 'left');
271
+ });
272
+ ```
273
+
274
+ ### User Management
275
+
276
+ #### `user(callback): void`
277
+
278
+ Get current authenticated user.
279
+
280
+ ```typescript
281
+ larasopp.user((user) => {
282
+ // user is typed as MyUser
283
+ console.log('Current user:', user.name);
284
+ });
285
+ ```
286
+
287
+ #### `userRefresh(callback): void`
288
+
289
+ Refresh current user data.
290
+
291
+ ```typescript
292
+ larasopp.userRefresh((user) => {
293
+ // user is typed as MyUser
294
+ console.log('Refreshed user:', user);
295
+ });
296
+ ```
297
+
298
+ ### Socket Events
299
+
300
+ #### `addListener(event, callback): EventListener | undefined`
301
+
302
+ Listen to socket-level events: `'open'`, `'close'`, `'error'`.
303
+
304
+ ```typescript
305
+ larasopp.addListener('open', () => {
306
+ console.log('Connected to WebSocket');
307
+ });
308
+
309
+ larasopp.addListener('close', () => {
310
+ console.log('Disconnected from WebSocket');
311
+ });
312
+
313
+ larasopp.addListener('error', (error) => {
314
+ console.error('WebSocket error:', error);
315
+ });
316
+ ```
317
+
318
+ ### Listener Management
319
+
320
+ #### `remove()`
321
+
322
+ Remove all event listeners from a channel subscription.
323
+
324
+ ```typescript
325
+ const listener = larasopp.subscribe('chat');
326
+ listener.listen('message', handleMessage);
327
+
328
+ // Later, remove all listeners
329
+ listener.remove();
330
+ ```
331
+
332
+ #### `unsubscribe()`
333
+
334
+ Unsubscribe from the channel and remove all listeners.
335
+
336
+ ```typescript
337
+ listener.unsubscribe();
338
+ ```
339
+
340
+ ## Complete Example
341
+
342
+ ```typescript
343
+ import Larasopp from 'larasopp';
344
+
345
+ // Define types
346
+ type AppChannels = {
347
+ chat: {
348
+ message: { text: string; author: string };
349
+ typing: { user: string };
350
+ };
351
+ notifications: {
352
+ alert: { title: string; body: string };
353
+ };
354
+ };
355
+
356
+ interface AppUser {
357
+ id: number;
358
+ name: string;
359
+ email: string;
360
+ }
361
+
362
+ // Create instance
363
+ const larasopp = new Larasopp<AppChannels, AppUser>({
364
+ host: 'ws://127.0.0.1:3001',
365
+ token: 'auth-token',
366
+ reconnect: 5,
367
+ reconnectDelay: 1000
368
+ });
369
+
370
+ // Connect
371
+ larasopp.connect();
372
+
373
+ // Listen to connection events
374
+ larasopp.addListener('open', () => {
375
+ console.log('Connected');
376
+ });
377
+
378
+ // Subscribe to channel
379
+ const chatListener = larasopp.subscribe('chat');
380
+
381
+ // Listen to events with full type safety
382
+ chatListener.listen('message', (data) => {
383
+ // TypeScript knows data structure
384
+ console.log(`${data.author}: ${data.text}`);
385
+ });
386
+
387
+ chatListener.listen('typing', (data) => {
388
+ console.log(`${data.user} is typing...`);
389
+ });
390
+
391
+ // Presence channel features
392
+ chatListener.here((users) => {
393
+ console.log('Users in channel:', users.map(u => u.name));
394
+ });
395
+
396
+ chatListener.joining((user) => {
397
+ console.log(`${user.name} joined`);
398
+ });
399
+
400
+ chatListener.leaving((user) => {
401
+ console.log(`${user.name} left`);
402
+ });
403
+
404
+ // Trigger events
405
+ larasopp.trigger('chat', 'message', {
406
+ text: 'Hello!',
407
+ author: 'John'
408
+ }, 'public');
409
+
410
+ // Get current user
411
+ larasopp.user((user) => {
412
+ console.log('Logged in as:', user.name);
413
+ });
414
+
415
+ // Cleanup
416
+ chatListener.remove();
417
+ larasopp.unsubscribe('chat');
418
+ larasopp.disconnect();
419
+ ```
420
+
421
+ ## Type Safety Benefits
422
+
423
+ With TypeScript support, you get:
424
+
425
+ - ✅ **Compile-time checks** - Catch errors before runtime
426
+ - ✅ **Autocomplete** - IDE suggests available channels and events
427
+ - ✅ **Type inference** - Automatic type detection for callbacks
428
+ - ✅ **Refactoring safety** - Rename channels/events with confidence
429
+ - ✅ **Documentation** - Types serve as inline documentation
430
+
431
+ ## License
432
+
433
+ MIT
434
+
435
+ ## Author
436
+
437
+ Sergey Serov
438
+
439
+ ## Links
440
+
441
+ - [GitHub Repository](https://github.com/SergoMorello/larasopp.client)
442
+ - [NPM Package](https://www.npmjs.com/package/larasopp)
443
+ - [Server Package](https://www.npmjs.com/package/larasopp-server)
package/lib/Core.d.ts CHANGED
@@ -9,7 +9,9 @@ declare abstract class Core {
9
9
  private reconnectTimer?;
10
10
  constructor(config: IConfig);
11
11
  setConfig(config: IConfig): this;
12
+ getConfig(): IConfig;
12
13
  setToken(token: string): this;
14
+ getToken(): string | undefined;
13
15
  /**
14
16
  * Connect to websocket
15
17
  * @returns {this}
package/lib/Core.js CHANGED
@@ -56,6 +56,9 @@ class Core {
56
56
  this.config = config;
57
57
  return this;
58
58
  }
59
+ getConfig() {
60
+ return this.config;
61
+ }
59
62
  setToken(token) {
60
63
  this.config = Object.assign(Object.assign({}, this.config), { token });
61
64
  if (this.status) {
@@ -65,6 +68,9 @@ class Core {
65
68
  }
66
69
  return this;
67
70
  }
71
+ getToken() {
72
+ return this.config.token;
73
+ }
68
74
  /**
69
75
  * Connect to websocket
70
76
  * @returns {this}
@@ -72,6 +78,9 @@ class Core {
72
78
  connect(token) {
73
79
  if (this.status)
74
80
  return this;
81
+ if (this.config.debug) {
82
+ console.log('ws_connect');
83
+ }
75
84
  try {
76
85
  if (token)
77
86
  this.setToken(token);
@@ -95,6 +104,9 @@ class Core {
95
104
  */
96
105
  disconnect() {
97
106
  var _a;
107
+ if (this.config.debug) {
108
+ console.log('ws_disconnect');
109
+ }
98
110
  if (this.status) {
99
111
  (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
100
112
  }
@@ -111,6 +123,9 @@ class Core {
111
123
  }
112
124
  tryReconnect() {
113
125
  var _a;
126
+ if (this.config.debug) {
127
+ console.log('ws_try_reconnect', this.reconnectTimer);
128
+ }
114
129
  if (this.reconnectTimer)
115
130
  clearTimeout(this.reconnectTimer);
116
131
  this.reconnectTimer = setTimeout(() => {
@@ -127,13 +142,22 @@ class Core {
127
142
  }, (_a = this.config.reconnectDelay) !== null && _a !== void 0 ? _a : 1000);
128
143
  }
129
144
  handleOpen(e) {
145
+ if (this.config.debug) {
146
+ console.log('ws_open', e);
147
+ }
130
148
  this.events.emit("open", e);
131
149
  }
132
150
  handleClose(e) {
151
+ if (this.config.debug) {
152
+ console.log('ws_close', e);
153
+ }
133
154
  this.events.emit("close", e);
134
155
  this.tryReconnect();
135
156
  }
136
157
  handleError(e) {
158
+ if (this.config.debug) {
159
+ console.log('ws_error', e);
160
+ }
137
161
  this.events.emit("error", e);
138
162
  }
139
163
  jsonParse(str) {
@@ -157,6 +181,9 @@ class Core {
157
181
  return JSON.parse(str);
158
182
  }
159
183
  handleMessage(e) {
184
+ if (this.config.debug) {
185
+ console.log('ws_message', e.data);
186
+ }
160
187
  if (this.isJson(e.data)) {
161
188
  const json = this.jsonParse(e.data);
162
189
  if (json.socket_id) {
package/lib/Listener.d.ts CHANGED
@@ -1,16 +1,18 @@
1
1
  import EventEmmiter from "easy-event-emitter";
2
2
  import type Larasopp from ".";
3
- declare class Listener extends EventEmmiter.Stack {
3
+ import type { TListenCallback, TJoiningCallback, TLeavingCallback, THereCallback, TUser } from "./types";
4
+ declare class Listener<TEvents = Record<string, unknown>, TUserType extends TUser = TUser> extends EventEmmiter.Stack {
4
5
  private readonly context;
5
6
  private channel;
6
7
  private listener?;
7
8
  private cacheEvents;
8
9
  private hereMap;
9
- constructor(channel: string, constext: Larasopp);
10
- listen(event: string, callback: (data: any) => void, withCache?: boolean): this;
11
- here(callback: (data: any) => void, withCache?: boolean): this;
12
- joining(callback: (data: any) => void, withCache?: boolean): this;
13
- leaving(callback: (data: any) => void, withCache?: boolean): this;
10
+ constructor(channel: string, constext: Larasopp<any, TUserType>);
11
+ listen<K extends keyof TEvents>(event: K, callback: TListenCallback<TEvents[K]>, withCache?: boolean): this;
12
+ private listenInternal;
13
+ here(callback: THereCallback<TUserType>, withCache?: boolean): this;
14
+ joining(callback: TJoiningCallback<TUserType>, withCache?: boolean): this;
15
+ leaving(callback: TLeavingCallback<TUserType>, withCache?: boolean): this;
14
16
  unsubscribe(): void;
15
17
  private hasCache;
16
18
  private getCache;
package/lib/Listener.js CHANGED
@@ -43,8 +43,22 @@ class Listener extends easy_event_emitter_1.default.Stack {
43
43
  this.here(() => { }, true);
44
44
  }
45
45
  listen(event, callback, withCache = false) {
46
- if (withCache && this.hasCache(event))
46
+ const eventString = String(event);
47
+ if (withCache && this.hasCache(eventString)) {
48
+ callback(this.getCache(eventString));
49
+ }
50
+ const listener = this.context.events.addListener(`${this.channel}:${eventString}`, (data) => {
51
+ callback(data);
52
+ if (withCache)
53
+ this.pushCache(eventString, data);
54
+ });
55
+ this.push(listener);
56
+ return this;
57
+ }
58
+ listenInternal(event, callback, withCache = false) {
59
+ if (withCache && this.hasCache(event)) {
47
60
  callback(this.getCache(event));
61
+ }
48
62
  const listener = this.context.events.addListener(`${this.channel}:${event}`, (data) => {
49
63
  callback(data);
50
64
  if (withCache)
@@ -63,17 +77,17 @@ class Listener extends easy_event_emitter_1.default.Stack {
63
77
  this.hereMap.delete(leave.id);
64
78
  callback([...this.hereMap.values()]);
65
79
  }));
66
- const hereListener = this.listen('__HERE', (here) => {
80
+ const hereListener = this.listenInternal('__HERE', (here) => {
67
81
  this.hereMap = new Map(here.map((u) => [u.id, u]));
68
82
  callback([...this.hereMap.values()]);
69
83
  }, withCache);
70
84
  return hereListener;
71
85
  }
72
86
  joining(callback, withCache = false) {
73
- return this.listen('__JOIN', callback, withCache);
87
+ return this.listenInternal('__JOIN', callback, withCache);
74
88
  }
75
89
  leaving(callback, withCache = false) {
76
- return this.listen('__LEAVE', callback, withCache);
90
+ return this.listenInternal('__LEAVE', callback, withCache);
77
91
  }
78
92
  unsubscribe() {
79
93
  this.context.unsubscribe(this.channel);
package/lib/index.d.ts CHANGED
@@ -1,18 +1,19 @@
1
1
  import { type EventListener } from "easy-event-emitter";
2
2
  import Core from "./Core";
3
3
  import Listener from "./Listener";
4
- import type { IConfig, TPermissions, TSocketEvents, TListenerCallback } from "./types";
5
- declare class Larasopp extends Core {
4
+ import type { IConfig, TPermissions, TSocketEvents, TListenerCallback, TUser } from "./types";
5
+ declare class Larasopp<TChannelsEvents = Record<string, Record<string, unknown>>, TUserType extends TUser = TUser> extends Core {
6
6
  private readonly channels;
7
7
  constructor(config: IConfig);
8
8
  private listenResumeSubscribes;
9
- user(callback: (user: any) => void): void;
10
- userRefresh(callback: (user: any) => void): void;
11
- subscribe(channel: string): Listener;
12
- unsubscribe(channel: string): void;
13
- trigger<T>(channel: string, event: string, message: T, permission?: TPermissions, waitSubscribe?: boolean): void;
9
+ user(callback: (user: TUserType) => void): void;
10
+ userRefresh(callback: (user: TUserType) => void): void;
11
+ subscribe<K extends keyof TChannelsEvents>(channel: K): Listener<TChannelsEvents[K], TUserType>;
12
+ unsubscribe<K extends keyof TChannelsEvents>(channel: K): void;
13
+ trigger<K extends keyof TChannelsEvents, E extends keyof TChannelsEvents[K]>(channel: K, event: E, message: TChannelsEvents[K][E], permission?: TPermissions, waitSubscribe?: boolean): void;
14
+ trigger(channel: string, event: string, message: unknown, permission?: TPermissions, waitSubscribe?: boolean): void;
14
15
  private pushListener;
15
- countListeners(channel: string): number;
16
+ countListeners<K extends keyof TChannelsEvents>(channel: K): number;
16
17
  addListener(event: TSocketEvents, callback: TListenerCallback): EventListener | undefined;
17
18
  }
18
19
  export type { Listener, EventListener };
package/lib/index.js CHANGED
@@ -29,19 +29,13 @@ class Larasopp extends Core_1.default {
29
29
  if (!this.status)
30
30
  return;
31
31
  this.send({ me: true });
32
- const listener = this.events.addListener(`__SYSTEM:user`, (e) => {
33
- callback(e);
34
- listener.remove();
35
- });
32
+ this.events.once(`__SYSTEM:user`, callback);
36
33
  }
37
34
  userRefresh(callback) {
38
35
  if (!this.status)
39
36
  return;
40
37
  this.send({ me: 'refresh' });
41
- const listener = this.events.addListener(`__SYSTEM:user-refresh`, (e) => {
42
- callback(e);
43
- listener.remove();
44
- });
38
+ this.events.once(`__SYSTEM:user-refresh`, callback);
45
39
  }
46
40
  subscribe(channel) {
47
41
  const listener = new Listener_1.default(channel, this);
package/lib/types.d.ts CHANGED
@@ -26,7 +26,18 @@ export interface IConfig {
26
26
  dataReviver?: TConfigDataReviver;
27
27
  reconnect?: number;
28
28
  reconnectDelay?: number;
29
+ debug?: boolean;
29
30
  }
30
- export type TChannels = {
31
- [channel: string]: Listener[];
31
+ export type TUser = {
32
+ id: string | number;
33
+ [key: string]: unknown;
32
34
  };
35
+ export type TChannels<TChannelsEvents = Record<string, Record<string, unknown>>, TUserType extends TUser = TUser> = {
36
+ [K in keyof TChannelsEvents]?: Listener<TChannelsEvents[K], TUserType>[];
37
+ };
38
+ export type TJoinData<T extends TUser = TUser> = T;
39
+ export type TLeaveData<T extends TUser = TUser> = T;
40
+ export type THereCallback<T extends TUser = TUser> = (users: T[]) => void;
41
+ export type TListenCallback<T = unknown> = (data: T) => void;
42
+ export type TJoiningCallback<T extends TUser = TUser> = (data: TJoinData<T>) => void;
43
+ export type TLeavingCallback<T extends TUser = TUser> = (data: TLeaveData<T>) => void;
package/package.json CHANGED
@@ -1,46 +1,67 @@
1
- {
2
- "name": "larasopp",
3
- "version": "1.2.23",
4
- "description": "Websocket client for laravel",
5
- "main": "lib/index.js",
6
- "types": "lib/index.d.ts",
7
- "scripts": {
8
- "test": "jest",
9
- "build": "tsc",
10
- "prepublishOnly": "npm run test && npm run build"
11
- },
12
- "files": [
13
- "lib"
14
- ],
15
- "jest": {
16
- "transform": {
17
- "^.+\\.[t|j]sx?$": "babel-jest"
18
- }
19
- },
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/SergoMorello/larasopp.client.git"
23
- },
24
- "keywords": [
25
- "websocket",
26
- "laravel",
27
- "broadcast",
28
- "events"
29
- ],
30
- "author": "Sergey Serov",
31
- "license": "MIT",
32
- "bugs": {
33
- "url": "https://github.com/SergoMorello/larasopp.client/issues"
34
- },
35
- "homepage": "https://github.com/SergoMorello/larasopp.client#readme",
36
- "devDependencies": {
37
- "@babel/preset-env": "^7.22.9",
38
- "@types/jest": "^29.5.3",
39
- "babel-jest": "^29.4.3",
40
- "jest": "^29.4.3",
41
- "typescript": "^5.1.6"
42
- },
43
- "dependencies": {
44
- "easy-event-emitter": "^1.1.5"
45
- }
46
- }
1
+ {
2
+ "name": "larasopp",
3
+ "version": "1.2.25",
4
+ "description": "Websocket client for laravel",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "scripts": {
8
+ "test": "jest",
9
+ "build": "tsc",
10
+ "prepublishOnly": "npm run test && npm run build"
11
+ },
12
+ "files": [
13
+ "lib"
14
+ ],
15
+ "jest": {
16
+ "transform": {
17
+ "^.+\\.[t|j]sx?$": "babel-jest"
18
+ }
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/SergoMorello/larasopp.client.git"
23
+ },
24
+ "keywords": [
25
+ "websocket",
26
+ "websocket-client",
27
+ "laravel",
28
+ "laravel-broadcast",
29
+ "broadcast",
30
+ "events",
31
+ "typescript",
32
+ "types",
33
+ "type-safe",
34
+ "type-safety",
35
+ "typing",
36
+ "real-time",
37
+ "realtime",
38
+ "presence",
39
+ "channels",
40
+ "client",
41
+ "socket",
42
+ "ws",
43
+ "event-emitter",
44
+ "subscription",
45
+ "reconnect",
46
+ "authentication",
47
+ "token",
48
+ "pubsub",
49
+ "publish-subscribe"
50
+ ],
51
+ "author": "Sergey Serov",
52
+ "license": "MIT",
53
+ "bugs": {
54
+ "url": "https://github.com/SergoMorello/larasopp.client/issues"
55
+ },
56
+ "homepage": "https://github.com/SergoMorello/larasopp.client#readme",
57
+ "devDependencies": {
58
+ "@babel/preset-env": "^7.22.9",
59
+ "@types/jest": "^29.5.3",
60
+ "babel-jest": "^29.4.3",
61
+ "jest": "^29.4.3",
62
+ "typescript": "^5.1.6"
63
+ },
64
+ "dependencies": {
65
+ "easy-event-emitter": "^1.2.3"
66
+ }
67
+ }