@debros/network-ts-sdk 0.3.2 → 0.4.2
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 +49 -0
- package/dist/index.d.ts +109 -13
- package/dist/index.js +137 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/http.ts +46 -6
- package/src/core/ws.ts +14 -5
- package/src/functions/client.ts +62 -0
- package/src/functions/types.ts +21 -0
- package/src/index.ts +17 -7
- package/src/pubsub/client.ts +104 -31
- package/src/pubsub/types.ts +46 -0
package/src/core/ws.ts
CHANGED
|
@@ -15,10 +15,11 @@ export type WSOpenHandler = () => void;
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Simple WebSocket client with minimal abstractions
|
|
18
|
-
* No complex reconnection, no
|
|
18
|
+
* No complex reconnection, no failover - keep it simple
|
|
19
|
+
* Gateway failover is handled at the application layer
|
|
19
20
|
*/
|
|
20
21
|
export class WSClient {
|
|
21
|
-
private
|
|
22
|
+
private wsURL: string;
|
|
22
23
|
private timeout: number;
|
|
23
24
|
private authToken?: string;
|
|
24
25
|
private WebSocketClass: typeof WebSocket;
|
|
@@ -31,12 +32,19 @@ export class WSClient {
|
|
|
31
32
|
private isClosed = false;
|
|
32
33
|
|
|
33
34
|
constructor(config: WSClientConfig) {
|
|
34
|
-
this.
|
|
35
|
+
this.wsURL = config.wsURL;
|
|
35
36
|
this.timeout = config.timeout ?? 30000;
|
|
36
37
|
this.authToken = config.authToken;
|
|
37
38
|
this.WebSocketClass = config.WebSocket ?? WebSocket;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Get the current WebSocket URL
|
|
43
|
+
*/
|
|
44
|
+
get url(): string {
|
|
45
|
+
return this.wsURL;
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
/**
|
|
41
49
|
* Connect to WebSocket server
|
|
42
50
|
*/
|
|
@@ -56,7 +64,7 @@ export class WSClient {
|
|
|
56
64
|
|
|
57
65
|
this.ws.addEventListener("open", () => {
|
|
58
66
|
clearTimeout(timeout);
|
|
59
|
-
console.log("[WSClient] Connected to", this.
|
|
67
|
+
console.log("[WSClient] Connected to", this.wsURL);
|
|
60
68
|
this.openHandlers.forEach((handler) => handler());
|
|
61
69
|
resolve();
|
|
62
70
|
});
|
|
@@ -71,6 +79,7 @@ export class WSClient {
|
|
|
71
79
|
clearTimeout(timeout);
|
|
72
80
|
const error = new SDKError("WebSocket error", 500, "WS_ERROR", event);
|
|
73
81
|
this.errorHandlers.forEach((handler) => handler(error));
|
|
82
|
+
reject(error);
|
|
74
83
|
});
|
|
75
84
|
|
|
76
85
|
this.ws.addEventListener("close", () => {
|
|
@@ -88,7 +97,7 @@ export class WSClient {
|
|
|
88
97
|
* Build WebSocket URL with auth token
|
|
89
98
|
*/
|
|
90
99
|
private buildWSUrl(): string {
|
|
91
|
-
let url = this.
|
|
100
|
+
let url = this.wsURL;
|
|
92
101
|
|
|
93
102
|
if (this.authToken) {
|
|
94
103
|
const separator = url.includes("?") ? "&" : "?";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions Client
|
|
3
|
+
* Client for calling serverless functions on the Orama Network
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { HttpClient } from "../core/http";
|
|
7
|
+
import { SDKError } from "../errors";
|
|
8
|
+
|
|
9
|
+
export interface FunctionsClientConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Base URL for the functions gateway
|
|
12
|
+
* Defaults to using the same baseURL as the HTTP client
|
|
13
|
+
*/
|
|
14
|
+
gatewayURL?: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Namespace for the functions
|
|
18
|
+
*/
|
|
19
|
+
namespace: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class FunctionsClient {
|
|
23
|
+
private httpClient: HttpClient;
|
|
24
|
+
private gatewayURL?: string;
|
|
25
|
+
private namespace: string;
|
|
26
|
+
|
|
27
|
+
constructor(httpClient: HttpClient, config?: FunctionsClientConfig) {
|
|
28
|
+
this.httpClient = httpClient;
|
|
29
|
+
this.gatewayURL = config?.gatewayURL;
|
|
30
|
+
this.namespace = config?.namespace ?? "default";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Invoke a serverless function by name
|
|
35
|
+
*
|
|
36
|
+
* @param functionName - Name of the function to invoke
|
|
37
|
+
* @param input - Input payload for the function
|
|
38
|
+
* @returns The function response
|
|
39
|
+
*/
|
|
40
|
+
async invoke<TInput = any, TOutput = any>(
|
|
41
|
+
functionName: string,
|
|
42
|
+
input: TInput
|
|
43
|
+
): Promise<TOutput> {
|
|
44
|
+
const url = this.gatewayURL
|
|
45
|
+
? `${this.gatewayURL}/v1/invoke/${this.namespace}/${functionName}`
|
|
46
|
+
: `/v1/invoke/${this.namespace}/${functionName}`;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const response = await this.httpClient.post<TOutput>(url, input);
|
|
50
|
+
return response;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof SDKError) {
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
throw new SDKError(
|
|
56
|
+
`Function ${functionName} failed`,
|
|
57
|
+
500,
|
|
58
|
+
error instanceof Error ? error.message : String(error)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serverless Functions Types
|
|
3
|
+
* Type definitions for calling serverless functions on the Orama Network
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generic response from a serverless function
|
|
8
|
+
*/
|
|
9
|
+
export interface FunctionResponse<T = unknown> {
|
|
10
|
+
success: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
data?: T;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Standard success/error response used by many functions
|
|
17
|
+
*/
|
|
18
|
+
export interface SuccessResponse {
|
|
19
|
+
success: boolean;
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { PubSubClient } from "./pubsub/client";
|
|
|
5
5
|
import { NetworkClient } from "./network/client";
|
|
6
6
|
import { CacheClient } from "./cache/client";
|
|
7
7
|
import { StorageClient } from "./storage/client";
|
|
8
|
+
import { FunctionsClient, FunctionsClientConfig } from "./functions/client";
|
|
8
9
|
import { WSClientConfig } from "./core/ws";
|
|
9
10
|
import {
|
|
10
11
|
StorageAdapter,
|
|
@@ -16,7 +17,8 @@ export interface ClientConfig extends Omit<HttpClientConfig, "fetch"> {
|
|
|
16
17
|
apiKey?: string;
|
|
17
18
|
jwt?: string;
|
|
18
19
|
storage?: StorageAdapter;
|
|
19
|
-
wsConfig?: Partial<WSClientConfig
|
|
20
|
+
wsConfig?: Partial<Omit<WSClientConfig, "wsURL">>;
|
|
21
|
+
functionsConfig?: FunctionsClientConfig;
|
|
20
22
|
fetch?: typeof fetch;
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -27,6 +29,7 @@ export interface Client {
|
|
|
27
29
|
network: NetworkClient;
|
|
28
30
|
cache: CacheClient;
|
|
29
31
|
storage: StorageClient;
|
|
32
|
+
functions: FunctionsClient;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
export function createClient(config: ClientConfig): Client {
|
|
@@ -45,10 +48,8 @@ export function createClient(config: ClientConfig): Client {
|
|
|
45
48
|
jwt: config.jwt,
|
|
46
49
|
});
|
|
47
50
|
|
|
48
|
-
// Derive WebSocket URL from baseURL
|
|
49
|
-
const wsURL =
|
|
50
|
-
config.wsConfig?.wsURL ??
|
|
51
|
-
config.baseURL.replace(/^http/, "ws").replace(/\/$/, "");
|
|
51
|
+
// Derive WebSocket URL from baseURL
|
|
52
|
+
const wsURL = config.baseURL.replace(/^http/, "ws").replace(/\/$/, "");
|
|
52
53
|
|
|
53
54
|
const db = new DBClient(httpClient);
|
|
54
55
|
const pubsub = new PubSubClient(httpClient, {
|
|
@@ -58,6 +59,7 @@ export function createClient(config: ClientConfig): Client {
|
|
|
58
59
|
const network = new NetworkClient(httpClient);
|
|
59
60
|
const cache = new CacheClient(httpClient);
|
|
60
61
|
const storage = new StorageClient(httpClient);
|
|
62
|
+
const functions = new FunctionsClient(httpClient, config.functionsConfig);
|
|
61
63
|
|
|
62
64
|
return {
|
|
63
65
|
auth,
|
|
@@ -66,6 +68,7 @@ export function createClient(config: ClientConfig): Client {
|
|
|
66
68
|
network,
|
|
67
69
|
cache,
|
|
68
70
|
storage,
|
|
71
|
+
functions,
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
|
|
@@ -79,16 +82,21 @@ export { PubSubClient, Subscription } from "./pubsub/client";
|
|
|
79
82
|
export { NetworkClient } from "./network/client";
|
|
80
83
|
export { CacheClient } from "./cache/client";
|
|
81
84
|
export { StorageClient } from "./storage/client";
|
|
85
|
+
export { FunctionsClient } from "./functions/client";
|
|
82
86
|
export { SDKError } from "./errors";
|
|
83
87
|
export { MemoryStorage, LocalStorageAdapter } from "./auth/types";
|
|
84
88
|
export type { StorageAdapter, AuthConfig, WhoAmI } from "./auth/types";
|
|
85
89
|
export type * from "./db/types";
|
|
86
90
|
export type {
|
|
87
|
-
Message,
|
|
88
91
|
MessageHandler,
|
|
89
92
|
ErrorHandler,
|
|
90
93
|
CloseHandler,
|
|
91
|
-
|
|
94
|
+
PresenceMember,
|
|
95
|
+
PresenceResponse,
|
|
96
|
+
PresenceOptions,
|
|
97
|
+
SubscribeOptions,
|
|
98
|
+
} from "./pubsub/types";
|
|
99
|
+
export { type PubSubMessage } from "./pubsub/types";
|
|
92
100
|
export type {
|
|
93
101
|
PeerInfo,
|
|
94
102
|
NetworkStatus,
|
|
@@ -114,3 +122,5 @@ export type {
|
|
|
114
122
|
StoragePinResponse,
|
|
115
123
|
StorageStatus,
|
|
116
124
|
} from "./storage/client";
|
|
125
|
+
export type { FunctionsClientConfig } from "./functions/client";
|
|
126
|
+
export type * from "./functions/types";
|
package/src/pubsub/client.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { HttpClient } from "../core/http";
|
|
2
2
|
import { WSClient, WSClientConfig } from "../core/ws";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
3
|
+
import {
|
|
4
|
+
PubSubMessage,
|
|
5
|
+
RawEnvelope,
|
|
6
|
+
MessageHandler,
|
|
7
|
+
ErrorHandler,
|
|
8
|
+
CloseHandler,
|
|
9
|
+
SubscribeOptions,
|
|
10
|
+
PresenceResponse,
|
|
11
|
+
PresenceMember,
|
|
12
|
+
PresenceOptions,
|
|
13
|
+
} from "./types";
|
|
15
14
|
|
|
16
15
|
// Cross-platform base64 encoding/decoding utilities
|
|
17
16
|
function base64Encode(str: string): string {
|
|
@@ -54,13 +53,9 @@ function base64Decode(b64: string): string {
|
|
|
54
53
|
throw new Error("No base64 decoding method available");
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
export type MessageHandler = (message: Message) => void;
|
|
58
|
-
export type ErrorHandler = (error: Error) => void;
|
|
59
|
-
export type CloseHandler = () => void;
|
|
60
|
-
|
|
61
56
|
/**
|
|
62
57
|
* Simple PubSub client - one WebSocket connection per topic
|
|
63
|
-
*
|
|
58
|
+
* Gateway failover is handled at the application layer
|
|
64
59
|
*/
|
|
65
60
|
export class PubSubClient {
|
|
66
61
|
private httpClient: HttpClient;
|
|
@@ -104,23 +99,40 @@ export class PubSubClient {
|
|
|
104
99
|
return response.topics || [];
|
|
105
100
|
}
|
|
106
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Get current presence for a topic without subscribing
|
|
104
|
+
*/
|
|
105
|
+
async getPresence(topic: string): Promise<PresenceResponse> {
|
|
106
|
+
const response = await this.httpClient.get<PresenceResponse>(
|
|
107
|
+
`/v1/pubsub/presence?topic=${encodeURIComponent(topic)}`
|
|
108
|
+
);
|
|
109
|
+
return response;
|
|
110
|
+
}
|
|
111
|
+
|
|
107
112
|
/**
|
|
108
113
|
* Subscribe to a topic via WebSocket
|
|
109
114
|
* Creates one WebSocket connection per topic
|
|
110
115
|
*/
|
|
111
116
|
async subscribe(
|
|
112
117
|
topic: string,
|
|
113
|
-
|
|
114
|
-
onMessage?: MessageHandler;
|
|
115
|
-
onError?: ErrorHandler;
|
|
116
|
-
onClose?: CloseHandler;
|
|
117
|
-
} = {}
|
|
118
|
+
options: SubscribeOptions = {}
|
|
118
119
|
): Promise<Subscription> {
|
|
119
120
|
// Build WebSocket URL for this topic
|
|
120
121
|
const wsUrl = new URL(this.wsConfig.wsURL || "ws://127.0.0.1:6001");
|
|
121
122
|
wsUrl.pathname = "/v1/pubsub/ws";
|
|
122
123
|
wsUrl.searchParams.set("topic", topic);
|
|
123
124
|
|
|
125
|
+
// Handle presence options
|
|
126
|
+
let presence: PresenceOptions | undefined;
|
|
127
|
+
if (options.presence?.enabled) {
|
|
128
|
+
presence = options.presence;
|
|
129
|
+
wsUrl.searchParams.set("presence", "true");
|
|
130
|
+
wsUrl.searchParams.set("member_id", presence.memberId);
|
|
131
|
+
if (presence.meta) {
|
|
132
|
+
wsUrl.searchParams.set("member_meta", JSON.stringify(presence.meta));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
124
136
|
const authToken = this.httpClient.getApiKey() ?? this.httpClient.getToken();
|
|
125
137
|
|
|
126
138
|
// Create WebSocket client
|
|
@@ -133,16 +145,18 @@ export class PubSubClient {
|
|
|
133
145
|
await wsClient.connect();
|
|
134
146
|
|
|
135
147
|
// Create subscription wrapper
|
|
136
|
-
const subscription = new Subscription(wsClient, topic)
|
|
148
|
+
const subscription = new Subscription(wsClient, topic, presence, () =>
|
|
149
|
+
this.getPresence(topic)
|
|
150
|
+
);
|
|
137
151
|
|
|
138
|
-
if (
|
|
139
|
-
subscription.onMessage(
|
|
152
|
+
if (options.onMessage) {
|
|
153
|
+
subscription.onMessage(options.onMessage);
|
|
140
154
|
}
|
|
141
|
-
if (
|
|
142
|
-
subscription.onError(
|
|
155
|
+
if (options.onError) {
|
|
156
|
+
subscription.onError(options.onError);
|
|
143
157
|
}
|
|
144
|
-
if (
|
|
145
|
-
subscription.onClose(
|
|
158
|
+
if (options.onClose) {
|
|
159
|
+
subscription.onClose(options.onClose);
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
return subscription;
|
|
@@ -155,6 +169,7 @@ export class PubSubClient {
|
|
|
155
169
|
export class Subscription {
|
|
156
170
|
private wsClient: WSClient;
|
|
157
171
|
private topic: string;
|
|
172
|
+
private presenceOptions?: PresenceOptions;
|
|
158
173
|
private messageHandlers: Set<MessageHandler> = new Set();
|
|
159
174
|
private errorHandlers: Set<ErrorHandler> = new Set();
|
|
160
175
|
private closeHandlers: Set<CloseHandler> = new Set();
|
|
@@ -162,10 +177,18 @@ export class Subscription {
|
|
|
162
177
|
private wsMessageHandler: ((data: string) => void) | null = null;
|
|
163
178
|
private wsErrorHandler: ((error: Error) => void) | null = null;
|
|
164
179
|
private wsCloseHandler: (() => void) | null = null;
|
|
180
|
+
private getPresenceFn: () => Promise<PresenceResponse>;
|
|
165
181
|
|
|
166
|
-
constructor(
|
|
182
|
+
constructor(
|
|
183
|
+
wsClient: WSClient,
|
|
184
|
+
topic: string,
|
|
185
|
+
presenceOptions: PresenceOptions | undefined,
|
|
186
|
+
getPresenceFn: () => Promise<PresenceResponse>
|
|
187
|
+
) {
|
|
167
188
|
this.wsClient = wsClient;
|
|
168
189
|
this.topic = topic;
|
|
190
|
+
this.presenceOptions = presenceOptions;
|
|
191
|
+
this.getPresenceFn = getPresenceFn;
|
|
169
192
|
|
|
170
193
|
// Register message handler
|
|
171
194
|
this.wsMessageHandler = (data) => {
|
|
@@ -177,6 +200,37 @@ export class Subscription {
|
|
|
177
200
|
if (!envelope || typeof envelope !== "object") {
|
|
178
201
|
throw new Error("Invalid envelope: not an object");
|
|
179
202
|
}
|
|
203
|
+
|
|
204
|
+
// Handle presence events
|
|
205
|
+
if (
|
|
206
|
+
envelope.type === "presence.join" ||
|
|
207
|
+
envelope.type === "presence.leave"
|
|
208
|
+
) {
|
|
209
|
+
if (!envelope.member_id) {
|
|
210
|
+
console.warn("[Subscription] Presence event missing member_id");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const presenceMember: PresenceMember = {
|
|
215
|
+
memberId: envelope.member_id,
|
|
216
|
+
joinedAt: envelope.timestamp,
|
|
217
|
+
meta: envelope.meta,
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
if (
|
|
221
|
+
envelope.type === "presence.join" &&
|
|
222
|
+
this.presenceOptions?.onJoin
|
|
223
|
+
) {
|
|
224
|
+
this.presenceOptions.onJoin(presenceMember);
|
|
225
|
+
} else if (
|
|
226
|
+
envelope.type === "presence.leave" &&
|
|
227
|
+
this.presenceOptions?.onLeave
|
|
228
|
+
) {
|
|
229
|
+
this.presenceOptions.onLeave(presenceMember);
|
|
230
|
+
}
|
|
231
|
+
return; // Don't call regular onMessage for presence events
|
|
232
|
+
}
|
|
233
|
+
|
|
180
234
|
if (!envelope.data || typeof envelope.data !== "string") {
|
|
181
235
|
throw new Error("Invalid envelope: missing or invalid data field");
|
|
182
236
|
}
|
|
@@ -192,7 +246,7 @@ export class Subscription {
|
|
|
192
246
|
// Decode base64 data
|
|
193
247
|
const messageData = base64Decode(envelope.data);
|
|
194
248
|
|
|
195
|
-
const message:
|
|
249
|
+
const message: PubSubMessage = {
|
|
196
250
|
topic: envelope.topic,
|
|
197
251
|
data: messageData,
|
|
198
252
|
timestamp: envelope.timestamp,
|
|
@@ -223,6 +277,25 @@ export class Subscription {
|
|
|
223
277
|
this.wsClient.onClose(this.wsCloseHandler);
|
|
224
278
|
}
|
|
225
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Get current presence (requires presence.enabled on subscribe)
|
|
282
|
+
*/
|
|
283
|
+
async getPresence(): Promise<PresenceMember[]> {
|
|
284
|
+
if (!this.presenceOptions?.enabled) {
|
|
285
|
+
throw new Error("Presence is not enabled for this subscription");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const response = await this.getPresenceFn();
|
|
289
|
+
return response.members;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Check if presence is enabled for this subscription
|
|
294
|
+
*/
|
|
295
|
+
hasPresence(): boolean {
|
|
296
|
+
return !!this.presenceOptions?.enabled;
|
|
297
|
+
}
|
|
298
|
+
|
|
226
299
|
/**
|
|
227
300
|
* Register message handler
|
|
228
301
|
*/
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface PubSubMessage {
|
|
2
|
+
data: string;
|
|
3
|
+
topic: string;
|
|
4
|
+
timestamp: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RawEnvelope {
|
|
8
|
+
type?: string;
|
|
9
|
+
data: string; // base64-encoded
|
|
10
|
+
timestamp: number;
|
|
11
|
+
topic: string;
|
|
12
|
+
member_id?: string;
|
|
13
|
+
meta?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PresenceMember {
|
|
17
|
+
memberId: string;
|
|
18
|
+
joinedAt: number;
|
|
19
|
+
meta?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PresenceResponse {
|
|
23
|
+
topic: string;
|
|
24
|
+
members: PresenceMember[];
|
|
25
|
+
count: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PresenceOptions {
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
memberId: string;
|
|
31
|
+
meta?: Record<string, unknown>;
|
|
32
|
+
onJoin?: (member: PresenceMember) => void;
|
|
33
|
+
onLeave?: (member: PresenceMember) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SubscribeOptions {
|
|
37
|
+
onMessage?: MessageHandler;
|
|
38
|
+
onError?: ErrorHandler;
|
|
39
|
+
onClose?: CloseHandler;
|
|
40
|
+
presence?: PresenceOptions;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type MessageHandler = (message: PubSubMessage) => void;
|
|
44
|
+
export type ErrorHandler = (error: Error) => void;
|
|
45
|
+
export type CloseHandler = () => void;
|
|
46
|
+
|