reverb-ws-client 1.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/dist/index.d.mts +55 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +251 -0
- package/dist/index.mjs +224 -0
- package/package.json +35 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
type ReverbScheme = "ws" | "wss";
|
|
2
|
+
type EventCallback<T = unknown> = (data: T) => void;
|
|
3
|
+
type ReverbClientOptions = {
|
|
4
|
+
appKey: string;
|
|
5
|
+
host: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
scheme?: ReverbScheme;
|
|
8
|
+
authorizer?: Authorizer;
|
|
9
|
+
};
|
|
10
|
+
type PusherOutgoingMessage = {
|
|
11
|
+
event: string;
|
|
12
|
+
data?: Record<string, unknown>;
|
|
13
|
+
channel?: string;
|
|
14
|
+
};
|
|
15
|
+
type AuthResponse = {
|
|
16
|
+
auth: string;
|
|
17
|
+
channel_data?: string;
|
|
18
|
+
};
|
|
19
|
+
type Authorizer = (params: {
|
|
20
|
+
socketId: string;
|
|
21
|
+
channelName: string;
|
|
22
|
+
}) => Promise<AuthResponse>;
|
|
23
|
+
|
|
24
|
+
declare class Channel {
|
|
25
|
+
private name;
|
|
26
|
+
private client;
|
|
27
|
+
private listeners;
|
|
28
|
+
private subscribed;
|
|
29
|
+
constructor(name: string, client: ReverbClient);
|
|
30
|
+
subscribe(): Promise<void>;
|
|
31
|
+
listen(event: string, callback: EventCallback): this;
|
|
32
|
+
stopListening(event: string, callback?: EventCallback): this;
|
|
33
|
+
handleEvent(event: string, data: unknown): void;
|
|
34
|
+
getName(): string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare class ReverbClient {
|
|
38
|
+
private options;
|
|
39
|
+
private connection;
|
|
40
|
+
private channels;
|
|
41
|
+
private socketId;
|
|
42
|
+
constructor(options: ReverbClientOptions);
|
|
43
|
+
connect(): void;
|
|
44
|
+
disconnect(): void;
|
|
45
|
+
channel(name: string): Channel;
|
|
46
|
+
private(name: string): Channel;
|
|
47
|
+
presence(name: string): Channel;
|
|
48
|
+
send(data: PusherOutgoingMessage | object): void;
|
|
49
|
+
getSocketId(): string | null;
|
|
50
|
+
authorize(socketId: string, channelName: string): Promise<AuthResponse>;
|
|
51
|
+
private getChannel;
|
|
52
|
+
private handleMessage;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { ReverbClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
type ReverbScheme = "ws" | "wss";
|
|
2
|
+
type EventCallback<T = unknown> = (data: T) => void;
|
|
3
|
+
type ReverbClientOptions = {
|
|
4
|
+
appKey: string;
|
|
5
|
+
host: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
scheme?: ReverbScheme;
|
|
8
|
+
authorizer?: Authorizer;
|
|
9
|
+
};
|
|
10
|
+
type PusherOutgoingMessage = {
|
|
11
|
+
event: string;
|
|
12
|
+
data?: Record<string, unknown>;
|
|
13
|
+
channel?: string;
|
|
14
|
+
};
|
|
15
|
+
type AuthResponse = {
|
|
16
|
+
auth: string;
|
|
17
|
+
channel_data?: string;
|
|
18
|
+
};
|
|
19
|
+
type Authorizer = (params: {
|
|
20
|
+
socketId: string;
|
|
21
|
+
channelName: string;
|
|
22
|
+
}) => Promise<AuthResponse>;
|
|
23
|
+
|
|
24
|
+
declare class Channel {
|
|
25
|
+
private name;
|
|
26
|
+
private client;
|
|
27
|
+
private listeners;
|
|
28
|
+
private subscribed;
|
|
29
|
+
constructor(name: string, client: ReverbClient);
|
|
30
|
+
subscribe(): Promise<void>;
|
|
31
|
+
listen(event: string, callback: EventCallback): this;
|
|
32
|
+
stopListening(event: string, callback?: EventCallback): this;
|
|
33
|
+
handleEvent(event: string, data: unknown): void;
|
|
34
|
+
getName(): string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare class ReverbClient {
|
|
38
|
+
private options;
|
|
39
|
+
private connection;
|
|
40
|
+
private channels;
|
|
41
|
+
private socketId;
|
|
42
|
+
constructor(options: ReverbClientOptions);
|
|
43
|
+
connect(): void;
|
|
44
|
+
disconnect(): void;
|
|
45
|
+
channel(name: string): Channel;
|
|
46
|
+
private(name: string): Channel;
|
|
47
|
+
presence(name: string): Channel;
|
|
48
|
+
send(data: PusherOutgoingMessage | object): void;
|
|
49
|
+
getSocketId(): string | null;
|
|
50
|
+
authorize(socketId: string, channelName: string): Promise<AuthResponse>;
|
|
51
|
+
private getChannel;
|
|
52
|
+
private handleMessage;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { ReverbClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ReverbClient: () => ReverbClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/Channel.ts
|
|
28
|
+
var Channel = class {
|
|
29
|
+
constructor(name, client) {
|
|
30
|
+
this.name = name;
|
|
31
|
+
this.client = client;
|
|
32
|
+
}
|
|
33
|
+
name;
|
|
34
|
+
client;
|
|
35
|
+
listeners = /* @__PURE__ */ new Map();
|
|
36
|
+
subscribed = false;
|
|
37
|
+
async subscribe() {
|
|
38
|
+
if (this.subscribed) return;
|
|
39
|
+
const isPrivate = this.name.startsWith("private-");
|
|
40
|
+
const isPresence = this.name.startsWith("presence-");
|
|
41
|
+
let authPayload = {};
|
|
42
|
+
if (isPrivate || isPresence) {
|
|
43
|
+
const socketId = this.client.getSocketId();
|
|
44
|
+
if (!socketId) {
|
|
45
|
+
throw new Error("[Reverb] socket_id not ready yet");
|
|
46
|
+
}
|
|
47
|
+
const auth = await this.client.authorize(socketId, this.name);
|
|
48
|
+
authPayload = {
|
|
49
|
+
auth: auth.auth,
|
|
50
|
+
...auth.channel_data ? { channel_data: auth.channel_data } : {}
|
|
51
|
+
};
|
|
52
|
+
this.client.send({
|
|
53
|
+
event: "pusher:subscribe",
|
|
54
|
+
data: {
|
|
55
|
+
channel: this.name,
|
|
56
|
+
...authPayload
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
listen(event, callback) {
|
|
62
|
+
if (!this.listeners.has(event)) {
|
|
63
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
64
|
+
}
|
|
65
|
+
this.listeners.get(event).add(callback);
|
|
66
|
+
this.subscribe().catch((err) => {
|
|
67
|
+
console.error("[Reverb] subscribe failed", err);
|
|
68
|
+
});
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
stopListening(event, callback) {
|
|
72
|
+
if (!this.listeners.has(event)) return this;
|
|
73
|
+
if (callback) {
|
|
74
|
+
this.listeners.get(event).delete(callback);
|
|
75
|
+
} else {
|
|
76
|
+
this.listeners.delete(event);
|
|
77
|
+
}
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
handleEvent(event, data) {
|
|
81
|
+
const callbacks = this.listeners.get(event);
|
|
82
|
+
if (!callbacks) return;
|
|
83
|
+
let parsedData = data;
|
|
84
|
+
if (typeof data === "string") {
|
|
85
|
+
try {
|
|
86
|
+
parsedData = JSON.parse(data);
|
|
87
|
+
} catch {
|
|
88
|
+
parsedData = data;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
callbacks.forEach((cb) => cb(parsedData));
|
|
92
|
+
}
|
|
93
|
+
getName() {
|
|
94
|
+
return this.name;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/ConnectionManager.ts
|
|
99
|
+
var ConnectionManager = class {
|
|
100
|
+
constructor(options, onMessage) {
|
|
101
|
+
this.options = options;
|
|
102
|
+
this.onMessage = onMessage;
|
|
103
|
+
}
|
|
104
|
+
options;
|
|
105
|
+
onMessage;
|
|
106
|
+
ws = null;
|
|
107
|
+
state = "disconnected";
|
|
108
|
+
reconnectAttempts = 0;
|
|
109
|
+
maxReconnectAttempts = 5;
|
|
110
|
+
reconnectDelay = 2e3;
|
|
111
|
+
manuallyDisconnected = false;
|
|
112
|
+
connect() {
|
|
113
|
+
if (this.state === "connecting" || this.state === "connected") return;
|
|
114
|
+
this.manuallyDisconnected = false;
|
|
115
|
+
this.state = "connecting";
|
|
116
|
+
const url = this.buildUrl();
|
|
117
|
+
this.ws = new WebSocket(url);
|
|
118
|
+
this.ws.onopen = () => {
|
|
119
|
+
this.state = "connected";
|
|
120
|
+
this.reconnectAttempts = 0;
|
|
121
|
+
console.log("[Reverb] Connected");
|
|
122
|
+
};
|
|
123
|
+
this.ws.onmessage = (event) => {
|
|
124
|
+
try {
|
|
125
|
+
const parsed = JSON.parse(event.data);
|
|
126
|
+
this.onMessage(parsed);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.warn("[Reverb] Failed to parse message", err);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
this.ws.onerror = (err) => {
|
|
132
|
+
console.warn("[Reverb] WebSocket error", err);
|
|
133
|
+
};
|
|
134
|
+
this.ws.onclose = () => {
|
|
135
|
+
this.ws = null;
|
|
136
|
+
this.state = "disconnected";
|
|
137
|
+
console.log("[Reverb] Disconnected");
|
|
138
|
+
if (!this.manuallyDisconnected) {
|
|
139
|
+
this.reconnect();
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
disconnect() {
|
|
144
|
+
this.manuallyDisconnected = true;
|
|
145
|
+
if (this.ws) {
|
|
146
|
+
this.ws.close();
|
|
147
|
+
this.ws = null;
|
|
148
|
+
}
|
|
149
|
+
this.state = "disconnected";
|
|
150
|
+
}
|
|
151
|
+
send(data) {
|
|
152
|
+
if (!this.ws || this.state !== "connected") {
|
|
153
|
+
console.warn("[Reverb] Cannot send, socket not connected");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
this.ws.send(JSON.stringify(data));
|
|
157
|
+
}
|
|
158
|
+
getState() {
|
|
159
|
+
return this.state;
|
|
160
|
+
}
|
|
161
|
+
buildUrl() {
|
|
162
|
+
const { scheme = "ws", host, port, appKey } = this.options;
|
|
163
|
+
const portPart = port ? `:${port}` : "";
|
|
164
|
+
return `${scheme}://${host}${portPart}/app/${appKey}?protocol=7&client=js&version=1.0&flash=false`;
|
|
165
|
+
}
|
|
166
|
+
reconnect() {
|
|
167
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
168
|
+
console.warn("[Reverb] Max reconnect attempts reached");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
this.state = "reconnecting";
|
|
172
|
+
this.reconnectAttempts++;
|
|
173
|
+
setTimeout(() => {
|
|
174
|
+
console.log("[Reverb] Reconnecting...");
|
|
175
|
+
this.connect();
|
|
176
|
+
}, this.reconnectDelay);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// src/ReverbClient.ts
|
|
181
|
+
var ReverbClient = class {
|
|
182
|
+
constructor(options) {
|
|
183
|
+
this.options = options;
|
|
184
|
+
this.connection = new ConnectionManager(
|
|
185
|
+
options,
|
|
186
|
+
this.handleMessage.bind(this)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
options;
|
|
190
|
+
connection;
|
|
191
|
+
channels = /* @__PURE__ */ new Map();
|
|
192
|
+
socketId = null;
|
|
193
|
+
connect() {
|
|
194
|
+
this.connection.connect();
|
|
195
|
+
}
|
|
196
|
+
disconnect() {
|
|
197
|
+
this.connection.disconnect();
|
|
198
|
+
}
|
|
199
|
+
channel(name) {
|
|
200
|
+
return this.getChannel(name);
|
|
201
|
+
}
|
|
202
|
+
private(name) {
|
|
203
|
+
return this.getChannel(`private-${name}`);
|
|
204
|
+
}
|
|
205
|
+
presence(name) {
|
|
206
|
+
return this.getChannel(`presence-${name}`);
|
|
207
|
+
}
|
|
208
|
+
send(data) {
|
|
209
|
+
this.connection.send(data);
|
|
210
|
+
}
|
|
211
|
+
getSocketId() {
|
|
212
|
+
return this.socketId;
|
|
213
|
+
}
|
|
214
|
+
authorize(socketId, channelName) {
|
|
215
|
+
if (!this.options.authorizer) {
|
|
216
|
+
throw new Error("[Reverb] authorizer is required for private channels");
|
|
217
|
+
}
|
|
218
|
+
return this.options.authorizer({
|
|
219
|
+
socketId,
|
|
220
|
+
channelName
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
getChannel(name) {
|
|
224
|
+
if (!this.channels.has(name)) {
|
|
225
|
+
this.channels.set(name, new Channel(name, this));
|
|
226
|
+
}
|
|
227
|
+
return this.channels.get(name);
|
|
228
|
+
}
|
|
229
|
+
handleMessage(message) {
|
|
230
|
+
if (message.event === "pusher:connection_established") {
|
|
231
|
+
const data = typeof message.data === "string" ? JSON.parse(message.data) : message.data;
|
|
232
|
+
this.socketId = data?.socket_id;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (message.event === "pusher:ping") {
|
|
236
|
+
this.send({
|
|
237
|
+
event: "pusher:pong",
|
|
238
|
+
data: {}
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (message.channel) {
|
|
243
|
+
const channel = this.channels.get(message.channel);
|
|
244
|
+
channel?.handleEvent(message.event, message.data);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
249
|
+
0 && (module.exports = {
|
|
250
|
+
ReverbClient
|
|
251
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// src/Channel.ts
|
|
2
|
+
var Channel = class {
|
|
3
|
+
constructor(name, client) {
|
|
4
|
+
this.name = name;
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
name;
|
|
8
|
+
client;
|
|
9
|
+
listeners = /* @__PURE__ */ new Map();
|
|
10
|
+
subscribed = false;
|
|
11
|
+
async subscribe() {
|
|
12
|
+
if (this.subscribed) return;
|
|
13
|
+
const isPrivate = this.name.startsWith("private-");
|
|
14
|
+
const isPresence = this.name.startsWith("presence-");
|
|
15
|
+
let authPayload = {};
|
|
16
|
+
if (isPrivate || isPresence) {
|
|
17
|
+
const socketId = this.client.getSocketId();
|
|
18
|
+
if (!socketId) {
|
|
19
|
+
throw new Error("[Reverb] socket_id not ready yet");
|
|
20
|
+
}
|
|
21
|
+
const auth = await this.client.authorize(socketId, this.name);
|
|
22
|
+
authPayload = {
|
|
23
|
+
auth: auth.auth,
|
|
24
|
+
...auth.channel_data ? { channel_data: auth.channel_data } : {}
|
|
25
|
+
};
|
|
26
|
+
this.client.send({
|
|
27
|
+
event: "pusher:subscribe",
|
|
28
|
+
data: {
|
|
29
|
+
channel: this.name,
|
|
30
|
+
...authPayload
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
listen(event, callback) {
|
|
36
|
+
if (!this.listeners.has(event)) {
|
|
37
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
38
|
+
}
|
|
39
|
+
this.listeners.get(event).add(callback);
|
|
40
|
+
this.subscribe().catch((err) => {
|
|
41
|
+
console.error("[Reverb] subscribe failed", err);
|
|
42
|
+
});
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
stopListening(event, callback) {
|
|
46
|
+
if (!this.listeners.has(event)) return this;
|
|
47
|
+
if (callback) {
|
|
48
|
+
this.listeners.get(event).delete(callback);
|
|
49
|
+
} else {
|
|
50
|
+
this.listeners.delete(event);
|
|
51
|
+
}
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
handleEvent(event, data) {
|
|
55
|
+
const callbacks = this.listeners.get(event);
|
|
56
|
+
if (!callbacks) return;
|
|
57
|
+
let parsedData = data;
|
|
58
|
+
if (typeof data === "string") {
|
|
59
|
+
try {
|
|
60
|
+
parsedData = JSON.parse(data);
|
|
61
|
+
} catch {
|
|
62
|
+
parsedData = data;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
callbacks.forEach((cb) => cb(parsedData));
|
|
66
|
+
}
|
|
67
|
+
getName() {
|
|
68
|
+
return this.name;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/ConnectionManager.ts
|
|
73
|
+
var ConnectionManager = class {
|
|
74
|
+
constructor(options, onMessage) {
|
|
75
|
+
this.options = options;
|
|
76
|
+
this.onMessage = onMessage;
|
|
77
|
+
}
|
|
78
|
+
options;
|
|
79
|
+
onMessage;
|
|
80
|
+
ws = null;
|
|
81
|
+
state = "disconnected";
|
|
82
|
+
reconnectAttempts = 0;
|
|
83
|
+
maxReconnectAttempts = 5;
|
|
84
|
+
reconnectDelay = 2e3;
|
|
85
|
+
manuallyDisconnected = false;
|
|
86
|
+
connect() {
|
|
87
|
+
if (this.state === "connecting" || this.state === "connected") return;
|
|
88
|
+
this.manuallyDisconnected = false;
|
|
89
|
+
this.state = "connecting";
|
|
90
|
+
const url = this.buildUrl();
|
|
91
|
+
this.ws = new WebSocket(url);
|
|
92
|
+
this.ws.onopen = () => {
|
|
93
|
+
this.state = "connected";
|
|
94
|
+
this.reconnectAttempts = 0;
|
|
95
|
+
console.log("[Reverb] Connected");
|
|
96
|
+
};
|
|
97
|
+
this.ws.onmessage = (event) => {
|
|
98
|
+
try {
|
|
99
|
+
const parsed = JSON.parse(event.data);
|
|
100
|
+
this.onMessage(parsed);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.warn("[Reverb] Failed to parse message", err);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
this.ws.onerror = (err) => {
|
|
106
|
+
console.warn("[Reverb] WebSocket error", err);
|
|
107
|
+
};
|
|
108
|
+
this.ws.onclose = () => {
|
|
109
|
+
this.ws = null;
|
|
110
|
+
this.state = "disconnected";
|
|
111
|
+
console.log("[Reverb] Disconnected");
|
|
112
|
+
if (!this.manuallyDisconnected) {
|
|
113
|
+
this.reconnect();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
disconnect() {
|
|
118
|
+
this.manuallyDisconnected = true;
|
|
119
|
+
if (this.ws) {
|
|
120
|
+
this.ws.close();
|
|
121
|
+
this.ws = null;
|
|
122
|
+
}
|
|
123
|
+
this.state = "disconnected";
|
|
124
|
+
}
|
|
125
|
+
send(data) {
|
|
126
|
+
if (!this.ws || this.state !== "connected") {
|
|
127
|
+
console.warn("[Reverb] Cannot send, socket not connected");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
this.ws.send(JSON.stringify(data));
|
|
131
|
+
}
|
|
132
|
+
getState() {
|
|
133
|
+
return this.state;
|
|
134
|
+
}
|
|
135
|
+
buildUrl() {
|
|
136
|
+
const { scheme = "ws", host, port, appKey } = this.options;
|
|
137
|
+
const portPart = port ? `:${port}` : "";
|
|
138
|
+
return `${scheme}://${host}${portPart}/app/${appKey}?protocol=7&client=js&version=1.0&flash=false`;
|
|
139
|
+
}
|
|
140
|
+
reconnect() {
|
|
141
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
142
|
+
console.warn("[Reverb] Max reconnect attempts reached");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this.state = "reconnecting";
|
|
146
|
+
this.reconnectAttempts++;
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
console.log("[Reverb] Reconnecting...");
|
|
149
|
+
this.connect();
|
|
150
|
+
}, this.reconnectDelay);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/ReverbClient.ts
|
|
155
|
+
var ReverbClient = class {
|
|
156
|
+
constructor(options) {
|
|
157
|
+
this.options = options;
|
|
158
|
+
this.connection = new ConnectionManager(
|
|
159
|
+
options,
|
|
160
|
+
this.handleMessage.bind(this)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
options;
|
|
164
|
+
connection;
|
|
165
|
+
channels = /* @__PURE__ */ new Map();
|
|
166
|
+
socketId = null;
|
|
167
|
+
connect() {
|
|
168
|
+
this.connection.connect();
|
|
169
|
+
}
|
|
170
|
+
disconnect() {
|
|
171
|
+
this.connection.disconnect();
|
|
172
|
+
}
|
|
173
|
+
channel(name) {
|
|
174
|
+
return this.getChannel(name);
|
|
175
|
+
}
|
|
176
|
+
private(name) {
|
|
177
|
+
return this.getChannel(`private-${name}`);
|
|
178
|
+
}
|
|
179
|
+
presence(name) {
|
|
180
|
+
return this.getChannel(`presence-${name}`);
|
|
181
|
+
}
|
|
182
|
+
send(data) {
|
|
183
|
+
this.connection.send(data);
|
|
184
|
+
}
|
|
185
|
+
getSocketId() {
|
|
186
|
+
return this.socketId;
|
|
187
|
+
}
|
|
188
|
+
authorize(socketId, channelName) {
|
|
189
|
+
if (!this.options.authorizer) {
|
|
190
|
+
throw new Error("[Reverb] authorizer is required for private channels");
|
|
191
|
+
}
|
|
192
|
+
return this.options.authorizer({
|
|
193
|
+
socketId,
|
|
194
|
+
channelName
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
getChannel(name) {
|
|
198
|
+
if (!this.channels.has(name)) {
|
|
199
|
+
this.channels.set(name, new Channel(name, this));
|
|
200
|
+
}
|
|
201
|
+
return this.channels.get(name);
|
|
202
|
+
}
|
|
203
|
+
handleMessage(message) {
|
|
204
|
+
if (message.event === "pusher:connection_established") {
|
|
205
|
+
const data = typeof message.data === "string" ? JSON.parse(message.data) : message.data;
|
|
206
|
+
this.socketId = data?.socket_id;
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (message.event === "pusher:ping") {
|
|
210
|
+
this.send({
|
|
211
|
+
event: "pusher:pong",
|
|
212
|
+
data: {}
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (message.channel) {
|
|
217
|
+
const channel = this.channels.get(message.channel);
|
|
218
|
+
channel?.handleEvent(message.event, message.data);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
export {
|
|
223
|
+
ReverbClient
|
|
224
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "reverb-ws-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight Laravel Reverb client for React Native using the Pusher protocol.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
14
|
+
"test": "vitest",
|
|
15
|
+
"test:integration": "vitest tests/integration --run",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"laravel",
|
|
20
|
+
"reverb",
|
|
21
|
+
"react-native",
|
|
22
|
+
"expo",
|
|
23
|
+
"websocket",
|
|
24
|
+
"pusher"
|
|
25
|
+
],
|
|
26
|
+
"author": "Almant",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.6.0",
|
|
30
|
+
"dotenv": "^17.4.2",
|
|
31
|
+
"tsup": "^8.5.1",
|
|
32
|
+
"typescript": "^6.0.3",
|
|
33
|
+
"vitest": "^4.1.5"
|
|
34
|
+
}
|
|
35
|
+
}
|