@azure/web-pubsub-chat-client 1.0.0-beta.1
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/CHANGELOG.md +5 -0
- package/README.md +175 -0
- package/dist/browser/index.js +4754 -0
- package/dist/browser/index.js.map +7 -0
- package/dist/chatClient.d.ts +253 -0
- package/dist/chatClient.d.ts.map +1 -0
- package/dist/constant.d.ts +21 -0
- package/dist/constant.d.ts.map +1 -0
- package/dist/events.d.ts +70 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/generatedTypes.d.ts +406 -0
- package/dist/generatedTypes.d.ts.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2620 -0
- package/dist/index.js.map +7 -0
- package/dist/logger.d.ts +5 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/modelGuards.d.ts +2 -0
- package/dist/modelGuards.d.ts.map +1 -0
- package/dist/models.d.ts +71 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/options.d.ts +69 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +71 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2620 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// node_modules/@typespec/ts-http-runtime/dist/esm/env.js
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
function getEnvironmentVariable(name) {
|
|
8
|
+
return process.env[name];
|
|
9
|
+
}
|
|
10
|
+
var isDeno = typeof process.versions.deno === "string" && process.versions.deno.length > 0;
|
|
11
|
+
var isBun = typeof process.versions.bun === "string" && process.versions.bun.length > 0;
|
|
12
|
+
|
|
13
|
+
// node_modules/@azure/abort-controller/dist/esm/AbortError.js
|
|
14
|
+
var AbortError = class extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "AbortError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// node_modules/@azure/core-util/dist/esm/createAbortablePromise.js
|
|
22
|
+
function createAbortablePromise(buildPromise, options) {
|
|
23
|
+
const { cleanupBeforeAbort, abortSignal, abortErrorMsg } = options ?? {};
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
function rejectOnAbort() {
|
|
26
|
+
reject(new AbortError(abortErrorMsg ?? "The operation was aborted."));
|
|
27
|
+
}
|
|
28
|
+
function removeListeners() {
|
|
29
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
30
|
+
}
|
|
31
|
+
function onAbort() {
|
|
32
|
+
cleanupBeforeAbort?.();
|
|
33
|
+
removeListeners();
|
|
34
|
+
rejectOnAbort();
|
|
35
|
+
}
|
|
36
|
+
if (abortSignal?.aborted) {
|
|
37
|
+
return rejectOnAbort();
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
buildPromise((x) => {
|
|
41
|
+
removeListeners();
|
|
42
|
+
resolve(x);
|
|
43
|
+
}, (x) => {
|
|
44
|
+
removeListeners();
|
|
45
|
+
reject(x);
|
|
46
|
+
});
|
|
47
|
+
} catch (err) {
|
|
48
|
+
reject(err);
|
|
49
|
+
}
|
|
50
|
+
abortSignal?.addEventListener("abort", onAbort);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// node_modules/@azure/core-util/dist/esm/delay.js
|
|
55
|
+
var StandardAbortMessage = "The delay was aborted.";
|
|
56
|
+
function delay(timeInMs, options) {
|
|
57
|
+
let token;
|
|
58
|
+
const { abortSignal, abortErrorMsg } = options ?? {};
|
|
59
|
+
return createAbortablePromise((resolve) => {
|
|
60
|
+
token = setTimeout(resolve, timeInMs);
|
|
61
|
+
}, {
|
|
62
|
+
cleanupBeforeAbort: () => clearTimeout(token),
|
|
63
|
+
abortSignal,
|
|
64
|
+
abortErrorMsg: abortErrorMsg ?? StandardAbortMessage
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/webPubSubClient.js
|
|
69
|
+
import EventEmitter from "events";
|
|
70
|
+
|
|
71
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/errors/index.js
|
|
72
|
+
var SendMessageError = class extends Error {
|
|
73
|
+
/**
|
|
74
|
+
* Initialize a SendMessageError
|
|
75
|
+
* @param message - The error message
|
|
76
|
+
* @param ackMessage - The ack message
|
|
77
|
+
*/
|
|
78
|
+
constructor(message, options) {
|
|
79
|
+
super(message);
|
|
80
|
+
/**
|
|
81
|
+
* Error name
|
|
82
|
+
*/
|
|
83
|
+
__publicField(this, "name");
|
|
84
|
+
/**
|
|
85
|
+
* The ack id of the message
|
|
86
|
+
*/
|
|
87
|
+
__publicField(this, "ackId");
|
|
88
|
+
/**
|
|
89
|
+
* The error details from the service
|
|
90
|
+
*/
|
|
91
|
+
__publicField(this, "errorDetail");
|
|
92
|
+
this.name = "SendMessageError";
|
|
93
|
+
this.ackId = options.ackId;
|
|
94
|
+
this.errorDetail = options.errorDetail;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var InvocationError = class extends Error {
|
|
98
|
+
constructor(message, options) {
|
|
99
|
+
super(message);
|
|
100
|
+
/**
|
|
101
|
+
* The invocation id of the request.
|
|
102
|
+
*/
|
|
103
|
+
__publicField(this, "invocationId");
|
|
104
|
+
/**
|
|
105
|
+
* Error details from the service if available.
|
|
106
|
+
*/
|
|
107
|
+
__publicField(this, "errorDetail");
|
|
108
|
+
this.name = "InvocationError";
|
|
109
|
+
this.invocationId = options.invocationId;
|
|
110
|
+
this.errorDetail = options.errorDetail;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// node_modules/@typespec/ts-http-runtime/dist/esm/logger/log.js
|
|
115
|
+
import { EOL } from "node:os";
|
|
116
|
+
import util from "node:util";
|
|
117
|
+
import process2 from "node:process";
|
|
118
|
+
function log(message, ...args) {
|
|
119
|
+
process2.stderr.write(`${util.format(message, ...args)}${EOL}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// node_modules/@typespec/ts-http-runtime/dist/esm/logger/debug.js
|
|
123
|
+
var debugEnvVariable = getEnvironmentVariable("DEBUG");
|
|
124
|
+
var enabledString;
|
|
125
|
+
var enabledNamespaces = [];
|
|
126
|
+
var skippedNamespaces = [];
|
|
127
|
+
var debuggers = [];
|
|
128
|
+
if (debugEnvVariable) {
|
|
129
|
+
enable(debugEnvVariable);
|
|
130
|
+
}
|
|
131
|
+
var debugObj = Object.assign((namespace) => {
|
|
132
|
+
return createDebugger(namespace);
|
|
133
|
+
}, {
|
|
134
|
+
enable,
|
|
135
|
+
enabled,
|
|
136
|
+
disable,
|
|
137
|
+
log
|
|
138
|
+
});
|
|
139
|
+
function enable(namespaces) {
|
|
140
|
+
enabledString = namespaces;
|
|
141
|
+
enabledNamespaces = [];
|
|
142
|
+
skippedNamespaces = [];
|
|
143
|
+
const namespaceList = namespaces.split(",").map((ns) => ns.trim());
|
|
144
|
+
for (const ns of namespaceList) {
|
|
145
|
+
if (ns.startsWith("-")) {
|
|
146
|
+
skippedNamespaces.push(ns.substring(1));
|
|
147
|
+
} else {
|
|
148
|
+
enabledNamespaces.push(ns);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
for (const instance of debuggers) {
|
|
152
|
+
instance.enabled = enabled(instance.namespace);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function enabled(namespace) {
|
|
156
|
+
if (namespace.endsWith("*")) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
for (const skipped of skippedNamespaces) {
|
|
160
|
+
if (namespaceMatches(namespace, skipped)) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
for (const enabledNamespace of enabledNamespaces) {
|
|
165
|
+
if (namespaceMatches(namespace, enabledNamespace)) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
function namespaceMatches(namespace, patternToMatch) {
|
|
172
|
+
if (patternToMatch.indexOf("*") === -1) {
|
|
173
|
+
return namespace === patternToMatch;
|
|
174
|
+
}
|
|
175
|
+
let pattern = patternToMatch;
|
|
176
|
+
if (patternToMatch.indexOf("**") !== -1) {
|
|
177
|
+
const patternParts = [];
|
|
178
|
+
let lastCharacter = "";
|
|
179
|
+
for (const character of patternToMatch) {
|
|
180
|
+
if (character === "*" && lastCharacter === "*") {
|
|
181
|
+
continue;
|
|
182
|
+
} else {
|
|
183
|
+
lastCharacter = character;
|
|
184
|
+
patternParts.push(character);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
pattern = patternParts.join("");
|
|
188
|
+
}
|
|
189
|
+
let namespaceIndex = 0;
|
|
190
|
+
let patternIndex = 0;
|
|
191
|
+
const patternLength = pattern.length;
|
|
192
|
+
const namespaceLength = namespace.length;
|
|
193
|
+
let lastWildcard = -1;
|
|
194
|
+
let lastWildcardNamespace = -1;
|
|
195
|
+
while (namespaceIndex < namespaceLength && patternIndex < patternLength) {
|
|
196
|
+
if (pattern[patternIndex] === "*") {
|
|
197
|
+
lastWildcard = patternIndex;
|
|
198
|
+
patternIndex++;
|
|
199
|
+
if (patternIndex === patternLength) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
while (namespace[namespaceIndex] !== pattern[patternIndex]) {
|
|
203
|
+
namespaceIndex++;
|
|
204
|
+
if (namespaceIndex === namespaceLength) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
lastWildcardNamespace = namespaceIndex;
|
|
209
|
+
namespaceIndex++;
|
|
210
|
+
patternIndex++;
|
|
211
|
+
continue;
|
|
212
|
+
} else if (pattern[patternIndex] === namespace[namespaceIndex]) {
|
|
213
|
+
patternIndex++;
|
|
214
|
+
namespaceIndex++;
|
|
215
|
+
} else if (lastWildcard >= 0) {
|
|
216
|
+
patternIndex = lastWildcard + 1;
|
|
217
|
+
namespaceIndex = lastWildcardNamespace + 1;
|
|
218
|
+
if (namespaceIndex === namespaceLength) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
while (namespace[namespaceIndex] !== pattern[patternIndex]) {
|
|
222
|
+
namespaceIndex++;
|
|
223
|
+
if (namespaceIndex === namespaceLength) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
lastWildcardNamespace = namespaceIndex;
|
|
228
|
+
namespaceIndex++;
|
|
229
|
+
patternIndex++;
|
|
230
|
+
continue;
|
|
231
|
+
} else {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const namespaceDone = namespaceIndex === namespace.length;
|
|
236
|
+
const patternDone = patternIndex === pattern.length;
|
|
237
|
+
const trailingWildCard = patternIndex === pattern.length - 1 && pattern[patternIndex] === "*";
|
|
238
|
+
return namespaceDone && (patternDone || trailingWildCard);
|
|
239
|
+
}
|
|
240
|
+
function disable() {
|
|
241
|
+
const result = enabledString || "";
|
|
242
|
+
enable("");
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
function createDebugger(namespace) {
|
|
246
|
+
const newDebugger = Object.assign(debug, {
|
|
247
|
+
enabled: enabled(namespace),
|
|
248
|
+
destroy,
|
|
249
|
+
log: debugObj.log,
|
|
250
|
+
namespace,
|
|
251
|
+
extend
|
|
252
|
+
});
|
|
253
|
+
function debug(...args) {
|
|
254
|
+
if (!newDebugger.enabled) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (args.length > 0) {
|
|
258
|
+
args[0] = `${namespace} ${args[0]}`;
|
|
259
|
+
}
|
|
260
|
+
newDebugger.log(...args);
|
|
261
|
+
}
|
|
262
|
+
debuggers.push(newDebugger);
|
|
263
|
+
return newDebugger;
|
|
264
|
+
}
|
|
265
|
+
function destroy() {
|
|
266
|
+
const index = debuggers.indexOf(this);
|
|
267
|
+
if (index >= 0) {
|
|
268
|
+
debuggers.splice(index, 1);
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
function extend(namespace) {
|
|
274
|
+
const newDebugger = createDebugger(`${this.namespace}:${namespace}`);
|
|
275
|
+
newDebugger.log = this.log;
|
|
276
|
+
return newDebugger;
|
|
277
|
+
}
|
|
278
|
+
var debug_default = debugObj;
|
|
279
|
+
|
|
280
|
+
// node_modules/@typespec/ts-http-runtime/dist/esm/logger/logger.js
|
|
281
|
+
var TYPESPEC_RUNTIME_LOG_LEVELS = ["verbose", "info", "warning", "error"];
|
|
282
|
+
var levelMap = {
|
|
283
|
+
verbose: 400,
|
|
284
|
+
info: 300,
|
|
285
|
+
warning: 200,
|
|
286
|
+
error: 100
|
|
287
|
+
};
|
|
288
|
+
function patchLogMethod(parent, child) {
|
|
289
|
+
child.log = (...args) => {
|
|
290
|
+
parent.log(...args);
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function isTypeSpecRuntimeLogLevel(level) {
|
|
294
|
+
return TYPESPEC_RUNTIME_LOG_LEVELS.includes(level);
|
|
295
|
+
}
|
|
296
|
+
function createLoggerContext(options) {
|
|
297
|
+
const registeredLoggers = /* @__PURE__ */ new Set();
|
|
298
|
+
const logLevelFromEnv = getEnvironmentVariable(options.logLevelEnvVarName);
|
|
299
|
+
let logLevel;
|
|
300
|
+
const clientLogger = debug_default(options.namespace);
|
|
301
|
+
clientLogger.log = (...args) => {
|
|
302
|
+
debug_default.log(...args);
|
|
303
|
+
};
|
|
304
|
+
function contextSetLogLevel(level) {
|
|
305
|
+
if (level && !isTypeSpecRuntimeLogLevel(level)) {
|
|
306
|
+
throw new Error(`Unknown log level '${level}'. Acceptable values: ${TYPESPEC_RUNTIME_LOG_LEVELS.join(",")}`);
|
|
307
|
+
}
|
|
308
|
+
logLevel = level;
|
|
309
|
+
const enabledNamespaces2 = [];
|
|
310
|
+
for (const logger3 of registeredLoggers) {
|
|
311
|
+
if (shouldEnable(logger3)) {
|
|
312
|
+
enabledNamespaces2.push(logger3.namespace);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
debug_default.enable(enabledNamespaces2.join(","));
|
|
316
|
+
}
|
|
317
|
+
if (logLevelFromEnv) {
|
|
318
|
+
if (isTypeSpecRuntimeLogLevel(logLevelFromEnv)) {
|
|
319
|
+
contextSetLogLevel(logLevelFromEnv);
|
|
320
|
+
} else {
|
|
321
|
+
console.error(`${options.logLevelEnvVarName} set to unknown log level '${logLevelFromEnv}'; logging is not enabled. Acceptable values: ${TYPESPEC_RUNTIME_LOG_LEVELS.join(", ")}.`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function shouldEnable(logger3) {
|
|
325
|
+
return Boolean(logLevel && levelMap[logger3.level] <= levelMap[logLevel]);
|
|
326
|
+
}
|
|
327
|
+
function createLogger(parent, level) {
|
|
328
|
+
const logger3 = Object.assign(parent.extend(level), {
|
|
329
|
+
level
|
|
330
|
+
});
|
|
331
|
+
patchLogMethod(parent, logger3);
|
|
332
|
+
if (shouldEnable(logger3)) {
|
|
333
|
+
const enabledNamespaces2 = debug_default.disable();
|
|
334
|
+
debug_default.enable(enabledNamespaces2 + "," + logger3.namespace);
|
|
335
|
+
}
|
|
336
|
+
registeredLoggers.add(logger3);
|
|
337
|
+
return logger3;
|
|
338
|
+
}
|
|
339
|
+
function contextGetLogLevel() {
|
|
340
|
+
return logLevel;
|
|
341
|
+
}
|
|
342
|
+
function contextCreateClientLogger(namespace) {
|
|
343
|
+
const clientRootLogger = clientLogger.extend(namespace);
|
|
344
|
+
patchLogMethod(clientLogger, clientRootLogger);
|
|
345
|
+
return {
|
|
346
|
+
error: createLogger(clientRootLogger, "error"),
|
|
347
|
+
warning: createLogger(clientRootLogger, "warning"),
|
|
348
|
+
info: createLogger(clientRootLogger, "info"),
|
|
349
|
+
verbose: createLogger(clientRootLogger, "verbose")
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
setLogLevel: contextSetLogLevel,
|
|
354
|
+
getLogLevel: contextGetLogLevel,
|
|
355
|
+
createClientLogger: contextCreateClientLogger,
|
|
356
|
+
logger: clientLogger
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
var context = createLoggerContext({
|
|
360
|
+
logLevelEnvVarName: "TYPESPEC_RUNTIME_LOG_LEVEL",
|
|
361
|
+
namespace: "typeSpecRuntime"
|
|
362
|
+
});
|
|
363
|
+
var TypeSpecRuntimeLogger = context.logger;
|
|
364
|
+
|
|
365
|
+
// node_modules/@azure/logger/dist/esm/index.js
|
|
366
|
+
var context2 = createLoggerContext({
|
|
367
|
+
logLevelEnvVarName: "AZURE_LOG_LEVEL",
|
|
368
|
+
namespace: "azure"
|
|
369
|
+
});
|
|
370
|
+
var AzureLogger = context2.logger;
|
|
371
|
+
function createClientLogger(namespace) {
|
|
372
|
+
return context2.createClientLogger(namespace);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/logger.js
|
|
376
|
+
var logger = createClientLogger("web-pubsub-client");
|
|
377
|
+
|
|
378
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/protocols/jsonProtocolBase.js
|
|
379
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
380
|
+
function parseMessages(input) {
|
|
381
|
+
if (typeof input !== "string") {
|
|
382
|
+
throw new Error("Invalid input for JSON hub protocol. Expected a string.");
|
|
383
|
+
}
|
|
384
|
+
if (!input) {
|
|
385
|
+
throw new Error("No input");
|
|
386
|
+
}
|
|
387
|
+
const parsedMessage = JSON.parse(input);
|
|
388
|
+
const typedMessage = parsedMessage;
|
|
389
|
+
let returnMessage;
|
|
390
|
+
if (typedMessage.type === "system") {
|
|
391
|
+
if (typedMessage.event === "connected") {
|
|
392
|
+
returnMessage = { ...parsedMessage, kind: "connected" };
|
|
393
|
+
} else if (typedMessage.event === "disconnected") {
|
|
394
|
+
returnMessage = { ...parsedMessage, kind: "disconnected" };
|
|
395
|
+
} else {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
} else if (typedMessage.type === "message") {
|
|
399
|
+
if (typedMessage.from === "group") {
|
|
400
|
+
const data = parsePayload(parsedMessage.data, parsedMessage.dataType);
|
|
401
|
+
if (data === null) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
returnMessage = { ...parsedMessage, data, kind: "groupData" };
|
|
405
|
+
} else if (typedMessage.from === "server") {
|
|
406
|
+
const data = parsePayload(parsedMessage.data, parsedMessage.dataType);
|
|
407
|
+
if (data === null) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
returnMessage = {
|
|
411
|
+
...parsedMessage,
|
|
412
|
+
data,
|
|
413
|
+
kind: "serverData"
|
|
414
|
+
};
|
|
415
|
+
} else {
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
} else if (typedMessage.type === "ack") {
|
|
419
|
+
returnMessage = { ...parsedMessage, kind: "ack" };
|
|
420
|
+
} else if (typedMessage.type === "invokeResponse") {
|
|
421
|
+
let data;
|
|
422
|
+
if (parsedMessage.dataType != null) {
|
|
423
|
+
const parsedData = parsePayload(parsedMessage.data, parsedMessage.dataType);
|
|
424
|
+
if (parsedData === null) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
data = parsedData;
|
|
428
|
+
}
|
|
429
|
+
returnMessage = {
|
|
430
|
+
kind: "invokeResponse",
|
|
431
|
+
invocationId: parsedMessage.invocationId,
|
|
432
|
+
success: parsedMessage.success,
|
|
433
|
+
dataType: parsedMessage.dataType,
|
|
434
|
+
data,
|
|
435
|
+
error: parsedMessage.error
|
|
436
|
+
};
|
|
437
|
+
} else if (typedMessage.type === "cancelInvocation") {
|
|
438
|
+
returnMessage = {
|
|
439
|
+
...parsedMessage,
|
|
440
|
+
kind: "cancelInvocation"
|
|
441
|
+
};
|
|
442
|
+
} else if (typedMessage.type === "pong") {
|
|
443
|
+
returnMessage = { ...parsedMessage, kind: "pong" };
|
|
444
|
+
} else {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
return returnMessage;
|
|
448
|
+
}
|
|
449
|
+
function writeMessage(message) {
|
|
450
|
+
let data;
|
|
451
|
+
switch (message.kind) {
|
|
452
|
+
case "joinGroup": {
|
|
453
|
+
data = { type: "joinGroup", group: message.group, ackId: message.ackId };
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
case "leaveGroup": {
|
|
457
|
+
data = { type: "leaveGroup", group: message.group, ackId: message.ackId };
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
case "sendEvent": {
|
|
461
|
+
data = {
|
|
462
|
+
type: "event",
|
|
463
|
+
event: message.event,
|
|
464
|
+
ackId: message.ackId,
|
|
465
|
+
dataType: message.dataType,
|
|
466
|
+
data: getPayload(message.data, message.dataType)
|
|
467
|
+
};
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
case "sendToGroup": {
|
|
471
|
+
data = {
|
|
472
|
+
type: "sendToGroup",
|
|
473
|
+
group: message.group,
|
|
474
|
+
ackId: message.ackId,
|
|
475
|
+
dataType: message.dataType,
|
|
476
|
+
data: getPayload(message.data, message.dataType),
|
|
477
|
+
noEcho: message.noEcho
|
|
478
|
+
};
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
case "sequenceAck": {
|
|
482
|
+
data = { type: "sequenceAck", sequenceId: message.sequenceId };
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
case "invoke": {
|
|
486
|
+
const invokePayload = {
|
|
487
|
+
type: "invoke",
|
|
488
|
+
invocationId: message.invocationId,
|
|
489
|
+
target: message.target,
|
|
490
|
+
event: message.event
|
|
491
|
+
};
|
|
492
|
+
if (message.dataType != null && message.data != null) {
|
|
493
|
+
invokePayload.dataType = message.dataType;
|
|
494
|
+
invokePayload.data = getPayload(message.data, message.dataType);
|
|
495
|
+
}
|
|
496
|
+
data = invokePayload;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
case "invokeResponse": {
|
|
500
|
+
const invokeResponse = {
|
|
501
|
+
type: "invokeResponse",
|
|
502
|
+
invocationId: message.invocationId,
|
|
503
|
+
success: message.success,
|
|
504
|
+
error: message.error
|
|
505
|
+
};
|
|
506
|
+
if (message.dataType != null && message.data != null) {
|
|
507
|
+
invokeResponse.dataType = message.dataType;
|
|
508
|
+
invokeResponse.data = getPayload(message.data, message.dataType);
|
|
509
|
+
}
|
|
510
|
+
data = invokeResponse;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
case "cancelInvocation": {
|
|
514
|
+
data = {
|
|
515
|
+
type: "cancelInvocation",
|
|
516
|
+
invocationId: message.invocationId
|
|
517
|
+
};
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
case "ping": {
|
|
521
|
+
data = { type: "ping" };
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
default: {
|
|
525
|
+
throw new Error(`Unsupported type: ${message.kind}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return JSON.stringify(data);
|
|
529
|
+
}
|
|
530
|
+
function getPayload(data, dataType) {
|
|
531
|
+
switch (dataType) {
|
|
532
|
+
case "text": {
|
|
533
|
+
if (typeof data !== "string") {
|
|
534
|
+
throw new TypeError("Message must be a string.");
|
|
535
|
+
}
|
|
536
|
+
return data;
|
|
537
|
+
}
|
|
538
|
+
case "json": {
|
|
539
|
+
return data;
|
|
540
|
+
}
|
|
541
|
+
case "binary":
|
|
542
|
+
case "protobuf": {
|
|
543
|
+
if (data instanceof ArrayBuffer) {
|
|
544
|
+
return Buffer2.from(data).toString("base64");
|
|
545
|
+
}
|
|
546
|
+
throw new TypeError("Message must be a ArrayBuffer");
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function parsePayload(data, dataType) {
|
|
551
|
+
if (dataType === "text") {
|
|
552
|
+
if (typeof data !== "string") {
|
|
553
|
+
throw new TypeError("Message must be a string when dataType is text");
|
|
554
|
+
}
|
|
555
|
+
return data;
|
|
556
|
+
} else if (dataType === "json") {
|
|
557
|
+
return data;
|
|
558
|
+
} else if (dataType === "binary" || dataType === "protobuf") {
|
|
559
|
+
const buf = Buffer2.from(data, "base64");
|
|
560
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
561
|
+
} else {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/protocols/webPubSubJsonReliableProtocol.js
|
|
567
|
+
var WebPubSubJsonReliableProtocolImpl = class {
|
|
568
|
+
constructor() {
|
|
569
|
+
/**
|
|
570
|
+
* True if the protocol supports reliable features
|
|
571
|
+
*/
|
|
572
|
+
__publicField(this, "isReliableSubProtocol", true);
|
|
573
|
+
/**
|
|
574
|
+
* The name of subprotocol. Name will be used in websocket subprotocol
|
|
575
|
+
*/
|
|
576
|
+
__publicField(this, "name", "json.reliable.webpubsub.azure.v1");
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Creates WebPubSubMessage objects from the specified serialized representation.
|
|
580
|
+
* @param input - The serialized representation
|
|
581
|
+
*/
|
|
582
|
+
parseMessages(input) {
|
|
583
|
+
return parseMessages(input);
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Write WebPubSubMessage to string
|
|
587
|
+
* @param message - The message to be written
|
|
588
|
+
*/
|
|
589
|
+
writeMessage(message) {
|
|
590
|
+
return writeMessage(message);
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/protocols/index.js
|
|
595
|
+
var WebPubSubJsonReliableProtocol = () => {
|
|
596
|
+
return new WebPubSubJsonReliableProtocolImpl();
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/websocket/websocketClient.js
|
|
600
|
+
import WebSocket from "ws";
|
|
601
|
+
var WebSocketClient = class {
|
|
602
|
+
constructor(uri, protocolName) {
|
|
603
|
+
__publicField(this, "_socket");
|
|
604
|
+
this._socket = new WebSocket(uri, protocolName);
|
|
605
|
+
this._socket.binaryType = "arraybuffer";
|
|
606
|
+
}
|
|
607
|
+
onopen(fn) {
|
|
608
|
+
this._socket.onopen = fn;
|
|
609
|
+
}
|
|
610
|
+
onclose(fn) {
|
|
611
|
+
this._socket.onclose = (event) => fn(event.code, event.reason);
|
|
612
|
+
}
|
|
613
|
+
onerror(fn) {
|
|
614
|
+
this._socket.onerror = (event) => fn(event.error);
|
|
615
|
+
}
|
|
616
|
+
onmessage(fn) {
|
|
617
|
+
this._socket.onmessage = (event) => fn(event.data);
|
|
618
|
+
}
|
|
619
|
+
close(code, reason) {
|
|
620
|
+
this._socket.close(code, reason);
|
|
621
|
+
}
|
|
622
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
623
|
+
send(data, _) {
|
|
624
|
+
return new Promise((resolve, reject) => {
|
|
625
|
+
this._socket.send(data, (err) => {
|
|
626
|
+
if (err) {
|
|
627
|
+
reject(err);
|
|
628
|
+
} else {
|
|
629
|
+
resolve();
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
isOpen() {
|
|
635
|
+
return this._socket.readyState === WebSocket.OPEN;
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
var WebSocketClientFactory = class {
|
|
639
|
+
create(uri, protocolName) {
|
|
640
|
+
return new WebSocketClient(uri, protocolName);
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/utils/abortablePromise.js
|
|
645
|
+
async function abortablePromise(promise, signal) {
|
|
646
|
+
if (signal.aborted) {
|
|
647
|
+
throw new AbortError("The operation was aborted.");
|
|
648
|
+
}
|
|
649
|
+
let onAbort;
|
|
650
|
+
const p = new Promise((_, reject) => {
|
|
651
|
+
onAbort = () => {
|
|
652
|
+
reject(new AbortError("The operation was aborted."));
|
|
653
|
+
};
|
|
654
|
+
signal.addEventListener("abort", onAbort);
|
|
655
|
+
});
|
|
656
|
+
try {
|
|
657
|
+
return await Promise.race([promise, p]);
|
|
658
|
+
} finally {
|
|
659
|
+
signal.removeEventListener("abort", onAbort);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/ackManager.js
|
|
664
|
+
var AckManager = class {
|
|
665
|
+
constructor(initialAckId = 0) {
|
|
666
|
+
__publicField(this, "_ackEntries", /* @__PURE__ */ new Map());
|
|
667
|
+
__publicField(this, "_ackId");
|
|
668
|
+
this._ackId = initialAckId;
|
|
669
|
+
}
|
|
670
|
+
registerAck(ackId) {
|
|
671
|
+
const resolvedAckId = ackId ?? this._generateAckId();
|
|
672
|
+
let entry = this._ackEntries.get(resolvedAckId);
|
|
673
|
+
if (!entry) {
|
|
674
|
+
entry = new AckEntity(resolvedAckId);
|
|
675
|
+
this._ackEntries.set(resolvedAckId, entry);
|
|
676
|
+
}
|
|
677
|
+
const ackEntry = entry;
|
|
678
|
+
return {
|
|
679
|
+
ackId: resolvedAckId,
|
|
680
|
+
wait: (abortSignal) => this._waitForEntry(ackEntry, abortSignal)
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
resolveAck(ackId, result) {
|
|
684
|
+
const entry = this._ackEntries.get(ackId);
|
|
685
|
+
if (!entry) {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
this._ackEntries.delete(ackId);
|
|
689
|
+
entry.resolve(result);
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
rejectAck(ackId, reason) {
|
|
693
|
+
const entry = this._ackEntries.get(ackId);
|
|
694
|
+
if (!entry) {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
this._ackEntries.delete(ackId);
|
|
698
|
+
entry.reject(reason);
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
discard(ackId) {
|
|
702
|
+
this._ackEntries.delete(ackId);
|
|
703
|
+
}
|
|
704
|
+
rejectAll(createReason) {
|
|
705
|
+
this._ackEntries.forEach((entry, ackId) => {
|
|
706
|
+
if (this._ackEntries.delete(ackId)) {
|
|
707
|
+
entry.reject(createReason(ackId));
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
_waitForEntry(entry, abortSignal) {
|
|
712
|
+
if (!abortSignal) {
|
|
713
|
+
return entry.promise();
|
|
714
|
+
}
|
|
715
|
+
return abortablePromise(entry.promise(), abortSignal).catch((err) => {
|
|
716
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
717
|
+
throw new SendMessageError("Cancelled by abortSignal", { ackId: entry.ackId });
|
|
718
|
+
}
|
|
719
|
+
throw err;
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
_generateAckId() {
|
|
723
|
+
this._ackId += 1;
|
|
724
|
+
return this._ackId;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
var AckEntity = class {
|
|
728
|
+
constructor(ackId) {
|
|
729
|
+
__publicField(this, "_promise");
|
|
730
|
+
__publicField(this, "_resolve");
|
|
731
|
+
__publicField(this, "_reject");
|
|
732
|
+
this.ackId = ackId;
|
|
733
|
+
this._promise = new Promise((resolve, reject) => {
|
|
734
|
+
this._resolve = resolve;
|
|
735
|
+
this._reject = reject;
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
promise() {
|
|
739
|
+
return this._promise;
|
|
740
|
+
}
|
|
741
|
+
resolve(value) {
|
|
742
|
+
const callback = this._resolve;
|
|
743
|
+
if (!callback) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
this._resolve = void 0;
|
|
747
|
+
this._reject = void 0;
|
|
748
|
+
callback(value);
|
|
749
|
+
}
|
|
750
|
+
reject(reason) {
|
|
751
|
+
const callback = this._reject;
|
|
752
|
+
if (!callback) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
this._resolve = void 0;
|
|
756
|
+
this._reject = void 0;
|
|
757
|
+
callback(reason);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/invocationManager.js
|
|
762
|
+
var InvocationManager = class {
|
|
763
|
+
constructor() {
|
|
764
|
+
__publicField(this, "_entries", /* @__PURE__ */ new Map());
|
|
765
|
+
__publicField(this, "_nextId", 0);
|
|
766
|
+
}
|
|
767
|
+
registerInvocation(invocationId) {
|
|
768
|
+
const resolvedId = invocationId ?? this._generateInvocationId();
|
|
769
|
+
if (this._entries.has(resolvedId)) {
|
|
770
|
+
throw new InvocationError("Invocation id is already registered.", {
|
|
771
|
+
invocationId: resolvedId
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
const entity = new InvocationEntity(resolvedId);
|
|
775
|
+
this._entries.set(resolvedId, entity);
|
|
776
|
+
return {
|
|
777
|
+
invocationId: resolvedId,
|
|
778
|
+
wait: (options) => this._waitForEntry(entity, options)
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
resolveInvocation(message) {
|
|
782
|
+
const entry = this._entries.get(message.invocationId);
|
|
783
|
+
if (!entry) {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
this._entries.delete(message.invocationId);
|
|
787
|
+
entry.resolve(message);
|
|
788
|
+
return true;
|
|
789
|
+
}
|
|
790
|
+
rejectInvocation(invocationId, reason) {
|
|
791
|
+
const entry = this._entries.get(invocationId);
|
|
792
|
+
if (!entry) {
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
this._entries.delete(invocationId);
|
|
796
|
+
entry.reject(reason);
|
|
797
|
+
return true;
|
|
798
|
+
}
|
|
799
|
+
discard(invocationId) {
|
|
800
|
+
this._entries.delete(invocationId);
|
|
801
|
+
}
|
|
802
|
+
rejectAll(createReason) {
|
|
803
|
+
this._entries.forEach((entry, invocationId) => {
|
|
804
|
+
if (this._entries.delete(invocationId)) {
|
|
805
|
+
entry.reject(createReason(invocationId));
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
_waitForEntry(entry, options) {
|
|
810
|
+
const waitPromise = entry.promise();
|
|
811
|
+
const abortSignal = options?.abortSignal;
|
|
812
|
+
if (!abortSignal) {
|
|
813
|
+
return waitPromise;
|
|
814
|
+
}
|
|
815
|
+
if (abortSignal.aborted) {
|
|
816
|
+
if (this._entries.delete(entry.invocationId)) {
|
|
817
|
+
entry.reject(this._createAbortError(entry.invocationId));
|
|
818
|
+
}
|
|
819
|
+
return waitPromise;
|
|
820
|
+
}
|
|
821
|
+
return new Promise((resolve, reject) => {
|
|
822
|
+
const onAbort = () => {
|
|
823
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
824
|
+
if (this._entries.delete(entry.invocationId)) {
|
|
825
|
+
entry.reject(this._createAbortError(entry.invocationId));
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
abortSignal.addEventListener("abort", onAbort);
|
|
829
|
+
waitPromise.then((result) => {
|
|
830
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
831
|
+
return resolve(result);
|
|
832
|
+
}).catch((err) => {
|
|
833
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
834
|
+
return reject(err);
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
_generateInvocationId() {
|
|
839
|
+
this._nextId += 1;
|
|
840
|
+
return this._nextId.toString();
|
|
841
|
+
}
|
|
842
|
+
_createAbortError(invocationId) {
|
|
843
|
+
return new InvocationError("Invocation cancelled by abortSignal.", {
|
|
844
|
+
invocationId
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
var InvocationEntity = class {
|
|
849
|
+
constructor(invocationId) {
|
|
850
|
+
__publicField(this, "_promise");
|
|
851
|
+
__publicField(this, "_resolve");
|
|
852
|
+
__publicField(this, "_reject");
|
|
853
|
+
this.invocationId = invocationId;
|
|
854
|
+
this._promise = new Promise((resolve, reject) => {
|
|
855
|
+
this._resolve = resolve;
|
|
856
|
+
this._reject = reject;
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
promise() {
|
|
860
|
+
return this._promise;
|
|
861
|
+
}
|
|
862
|
+
resolve(value) {
|
|
863
|
+
const callback = this._resolve;
|
|
864
|
+
if (!callback) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
this._resolve = void 0;
|
|
868
|
+
this._reject = void 0;
|
|
869
|
+
callback(value);
|
|
870
|
+
}
|
|
871
|
+
reject(reason) {
|
|
872
|
+
const callback = this._reject;
|
|
873
|
+
if (!callback) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
this._resolve = void 0;
|
|
877
|
+
this._reject = void 0;
|
|
878
|
+
callback(reason);
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
// node_modules/@azure/web-pubsub-client/dist/esm/webPubSubClient.js
|
|
883
|
+
var WebPubSubClient = class {
|
|
884
|
+
constructor(credential, options) {
|
|
885
|
+
__publicField(this, "_protocol");
|
|
886
|
+
__publicField(this, "_credential");
|
|
887
|
+
__publicField(this, "_options");
|
|
888
|
+
__publicField(this, "_groupMap");
|
|
889
|
+
__publicField(this, "_ackManager");
|
|
890
|
+
__publicField(this, "_invocationManager");
|
|
891
|
+
__publicField(this, "_sequenceId");
|
|
892
|
+
__publicField(this, "_messageRetryPolicy");
|
|
893
|
+
__publicField(this, "_reconnectRetryPolicy");
|
|
894
|
+
__publicField(this, "_quickSequenceAckDiff", 300);
|
|
895
|
+
// The timeout for keep alive
|
|
896
|
+
__publicField(this, "_keepAliveTimeoutInMs");
|
|
897
|
+
// The interval at which to send keep-alive ping messages to the runtime
|
|
898
|
+
__publicField(this, "_keepAliveIntervalInMs");
|
|
899
|
+
__publicField(this, "_emitter", new EventEmitter());
|
|
900
|
+
__publicField(this, "_state");
|
|
901
|
+
__publicField(this, "_isStopping", false);
|
|
902
|
+
__publicField(this, "_pingKeepaliveTask");
|
|
903
|
+
__publicField(this, "_timeoutMonitorTask");
|
|
904
|
+
// connection lifetime
|
|
905
|
+
__publicField(this, "_wsClient");
|
|
906
|
+
__publicField(this, "_uri");
|
|
907
|
+
__publicField(this, "_lastCloseEvent");
|
|
908
|
+
__publicField(this, "_lastDisconnectedMessage");
|
|
909
|
+
__publicField(this, "_connectionId");
|
|
910
|
+
__publicField(this, "_reconnectionToken");
|
|
911
|
+
__publicField(this, "_isInitialConnected", false);
|
|
912
|
+
__publicField(this, "_sequenceAckTask");
|
|
913
|
+
__publicField(this, "_lastMessageReceived", Date.now());
|
|
914
|
+
if (typeof credential === "string") {
|
|
915
|
+
this._credential = { getClientAccessUrl: credential };
|
|
916
|
+
} else {
|
|
917
|
+
this._credential = credential;
|
|
918
|
+
}
|
|
919
|
+
if (options == null) {
|
|
920
|
+
options = {};
|
|
921
|
+
}
|
|
922
|
+
this._buildDefaultOptions(options);
|
|
923
|
+
this._options = options;
|
|
924
|
+
this._messageRetryPolicy = new RetryPolicy(this._options.messageRetryOptions);
|
|
925
|
+
this._reconnectRetryPolicy = new RetryPolicy(this._options.reconnectRetryOptions);
|
|
926
|
+
this._protocol = this._options.protocol;
|
|
927
|
+
this._groupMap = /* @__PURE__ */ new Map();
|
|
928
|
+
this._ackManager = new AckManager();
|
|
929
|
+
this._invocationManager = new InvocationManager();
|
|
930
|
+
this._sequenceId = new SequenceId();
|
|
931
|
+
this._keepAliveTimeoutInMs = this._options.keepAliveTimeoutInMs ?? 12e4;
|
|
932
|
+
this._keepAliveIntervalInMs = this._options.keepAliveIntervalInMs ?? 2e4;
|
|
933
|
+
this._state = "Stopped";
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Start to start to the service.
|
|
937
|
+
* @param abortSignal - The abort signal
|
|
938
|
+
*/
|
|
939
|
+
async start(options) {
|
|
940
|
+
if (this._isStopping) {
|
|
941
|
+
throw new Error("Can't start a client during stopping");
|
|
942
|
+
}
|
|
943
|
+
if (this._state !== "Stopped") {
|
|
944
|
+
throw new Error("Client can be only started when it's Stopped");
|
|
945
|
+
}
|
|
946
|
+
let abortSignal;
|
|
947
|
+
if (options) {
|
|
948
|
+
abortSignal = options.abortSignal;
|
|
949
|
+
}
|
|
950
|
+
if (!this._pingKeepaliveTask && this._keepAliveIntervalInMs > 0) {
|
|
951
|
+
this._pingKeepaliveTask = this._getPingKeepaliveTask();
|
|
952
|
+
}
|
|
953
|
+
if (!this._timeoutMonitorTask && this._keepAliveTimeoutInMs > 0) {
|
|
954
|
+
this._timeoutMonitorTask = this._getTimeoutMonitorTask();
|
|
955
|
+
}
|
|
956
|
+
try {
|
|
957
|
+
await this._startCore(abortSignal);
|
|
958
|
+
} catch (err) {
|
|
959
|
+
this._changeState(
|
|
960
|
+
"Stopped"
|
|
961
|
+
/* Stopped */
|
|
962
|
+
);
|
|
963
|
+
this._disposeKeepaliveTasks();
|
|
964
|
+
this._isStopping = false;
|
|
965
|
+
throw err;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
async _startFromRestarting(abortSignal) {
|
|
969
|
+
if (this._state !== "Disconnected") {
|
|
970
|
+
throw new Error("Client can be only restarted when it's Disconnected");
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
logger.verbose("Staring reconnecting.");
|
|
974
|
+
await this._startCore(abortSignal);
|
|
975
|
+
} catch (err) {
|
|
976
|
+
this._changeState(
|
|
977
|
+
"Disconnected"
|
|
978
|
+
/* Disconnected */
|
|
979
|
+
);
|
|
980
|
+
throw err;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
async _startCore(abortSignal) {
|
|
984
|
+
this._changeState(
|
|
985
|
+
"Connecting"
|
|
986
|
+
/* Connecting */
|
|
987
|
+
);
|
|
988
|
+
logger.info("Staring a new connection");
|
|
989
|
+
this._sequenceId.reset();
|
|
990
|
+
this._isInitialConnected = false;
|
|
991
|
+
this._lastCloseEvent = void 0;
|
|
992
|
+
this._lastDisconnectedMessage = void 0;
|
|
993
|
+
this._connectionId = void 0;
|
|
994
|
+
this._reconnectionToken = void 0;
|
|
995
|
+
this._uri = void 0;
|
|
996
|
+
if (typeof this._credential.getClientAccessUrl === "string") {
|
|
997
|
+
this._uri = this._credential.getClientAccessUrl;
|
|
998
|
+
} else {
|
|
999
|
+
this._uri = await this._credential.getClientAccessUrl({
|
|
1000
|
+
abortSignal
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
if (typeof this._uri !== "string") {
|
|
1004
|
+
throw new Error(
|
|
1005
|
+
`The clientAccessUrl must be a string but currently it's ${typeof this._uri}`
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
await this._connectCore(this._uri);
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Stop the client.
|
|
1012
|
+
*/
|
|
1013
|
+
stop() {
|
|
1014
|
+
if (this._state === "Stopped" || this._isStopping) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
this._isStopping = true;
|
|
1018
|
+
if (this._wsClient && this._wsClient.isOpen()) {
|
|
1019
|
+
this._wsClient.close();
|
|
1020
|
+
} else {
|
|
1021
|
+
this._isStopping = false;
|
|
1022
|
+
}
|
|
1023
|
+
this._disposeKeepaliveTasks();
|
|
1024
|
+
}
|
|
1025
|
+
_disposeKeepaliveTasks() {
|
|
1026
|
+
if (this._pingKeepaliveTask) {
|
|
1027
|
+
this._pingKeepaliveTask.abort();
|
|
1028
|
+
this._pingKeepaliveTask = void 0;
|
|
1029
|
+
}
|
|
1030
|
+
if (this._timeoutMonitorTask) {
|
|
1031
|
+
this._timeoutMonitorTask.abort();
|
|
1032
|
+
this._timeoutMonitorTask = void 0;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
on(event, listener) {
|
|
1036
|
+
this._emitter.on(event, listener);
|
|
1037
|
+
}
|
|
1038
|
+
off(event, listener) {
|
|
1039
|
+
this._emitter.removeListener(event, listener);
|
|
1040
|
+
}
|
|
1041
|
+
_emitEvent(event, args) {
|
|
1042
|
+
this._emitter.emit(event, args);
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Send custom event to server.
|
|
1046
|
+
* @param eventName - The event name
|
|
1047
|
+
* @param content - The data content
|
|
1048
|
+
* @param dataType - The data type
|
|
1049
|
+
* @param options - The options
|
|
1050
|
+
* @param abortSignal - The abort signal
|
|
1051
|
+
*/
|
|
1052
|
+
async sendEvent(eventName, content, dataType, options) {
|
|
1053
|
+
return this._operationExecuteWithRetry(
|
|
1054
|
+
() => this._sendEventAttempt(eventName, content, dataType, options),
|
|
1055
|
+
options?.abortSignal
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
async _sendEventAttempt(eventName, content, dataType, options) {
|
|
1059
|
+
const fireAndForget = options?.fireAndForget ?? false;
|
|
1060
|
+
if (!fireAndForget) {
|
|
1061
|
+
return this._sendMessageWithAckId(
|
|
1062
|
+
(id) => {
|
|
1063
|
+
return {
|
|
1064
|
+
kind: "sendEvent",
|
|
1065
|
+
dataType,
|
|
1066
|
+
data: content,
|
|
1067
|
+
ackId: id,
|
|
1068
|
+
event: eventName
|
|
1069
|
+
};
|
|
1070
|
+
},
|
|
1071
|
+
options?.ackId,
|
|
1072
|
+
options?.abortSignal
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
const message = {
|
|
1076
|
+
kind: "sendEvent",
|
|
1077
|
+
dataType,
|
|
1078
|
+
data: content,
|
|
1079
|
+
event: eventName
|
|
1080
|
+
};
|
|
1081
|
+
await this._sendMessage(message, options?.abortSignal);
|
|
1082
|
+
return { isDuplicated: false };
|
|
1083
|
+
}
|
|
1084
|
+
async _invokeEventAttempt(eventName, content, dataType, options) {
|
|
1085
|
+
const invokeOptions = options ?? {};
|
|
1086
|
+
const { invocationId, wait } = this._invocationManager.registerInvocation(
|
|
1087
|
+
invokeOptions.invocationId
|
|
1088
|
+
);
|
|
1089
|
+
const invokeMessage = {
|
|
1090
|
+
kind: "invoke",
|
|
1091
|
+
invocationId,
|
|
1092
|
+
target: "event",
|
|
1093
|
+
event: eventName,
|
|
1094
|
+
dataType,
|
|
1095
|
+
data: content
|
|
1096
|
+
};
|
|
1097
|
+
const responsePromise = wait({
|
|
1098
|
+
abortSignal: invokeOptions.abortSignal
|
|
1099
|
+
});
|
|
1100
|
+
try {
|
|
1101
|
+
await this._sendMessage(invokeMessage, invokeOptions.abortSignal);
|
|
1102
|
+
} catch (err) {
|
|
1103
|
+
const invocationError = err instanceof InvocationError ? err : new InvocationError(
|
|
1104
|
+
err instanceof Error ? err.message : "Failed to send invocation message.",
|
|
1105
|
+
{
|
|
1106
|
+
invocationId
|
|
1107
|
+
}
|
|
1108
|
+
);
|
|
1109
|
+
this._invocationManager.rejectInvocation(invocationId, invocationError);
|
|
1110
|
+
void responsePromise.catch(() => {
|
|
1111
|
+
});
|
|
1112
|
+
throw invocationError;
|
|
1113
|
+
}
|
|
1114
|
+
try {
|
|
1115
|
+
const response = await responsePromise;
|
|
1116
|
+
return this._mapInvokeResponse(response);
|
|
1117
|
+
} catch (err) {
|
|
1118
|
+
const shouldCancel = err instanceof InvocationError && err.errorDetail == null || invokeOptions.abortSignal?.aborted === true;
|
|
1119
|
+
if (shouldCancel) {
|
|
1120
|
+
await this._sendCancelInvocation(invocationId).catch(() => {
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
throw err;
|
|
1124
|
+
} finally {
|
|
1125
|
+
this._invocationManager.discard(invocationId);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Invoke an upstream event and wait for the correlated response.
|
|
1130
|
+
* @param eventName - The event name
|
|
1131
|
+
* @param content - The payload
|
|
1132
|
+
* @param dataType - The payload type
|
|
1133
|
+
* @param options - Invoke options
|
|
1134
|
+
*/
|
|
1135
|
+
async invokeEvent(eventName, content, dataType, options) {
|
|
1136
|
+
return this._operationExecuteWithRetry(
|
|
1137
|
+
() => this._invokeEventAttempt(eventName, content, dataType, options),
|
|
1138
|
+
options?.abortSignal
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Join the client to group
|
|
1143
|
+
* @param groupName - The group name
|
|
1144
|
+
* @param options - The join group options
|
|
1145
|
+
*/
|
|
1146
|
+
async joinGroup(groupName, options) {
|
|
1147
|
+
return this._operationExecuteWithRetry(
|
|
1148
|
+
() => this._joinGroupAttempt(groupName, options),
|
|
1149
|
+
options?.abortSignal
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
async _joinGroupAttempt(groupName, options) {
|
|
1153
|
+
const group = this._getOrAddGroup(groupName);
|
|
1154
|
+
const result = await this._joinGroupCore(groupName, options);
|
|
1155
|
+
group.isJoined = true;
|
|
1156
|
+
return result;
|
|
1157
|
+
}
|
|
1158
|
+
async _joinGroupCore(groupName, options) {
|
|
1159
|
+
return this._sendMessageWithAckId(
|
|
1160
|
+
(id) => {
|
|
1161
|
+
return {
|
|
1162
|
+
group: groupName,
|
|
1163
|
+
ackId: id,
|
|
1164
|
+
kind: "joinGroup"
|
|
1165
|
+
};
|
|
1166
|
+
},
|
|
1167
|
+
options?.ackId,
|
|
1168
|
+
options?.abortSignal
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Leave the client from group
|
|
1173
|
+
* @param groupName - The group name
|
|
1174
|
+
* @param ackId - The optional ackId. If not specified, client will generate one.
|
|
1175
|
+
* @param abortSignal - The abort signal
|
|
1176
|
+
*/
|
|
1177
|
+
async leaveGroup(groupName, options) {
|
|
1178
|
+
return this._operationExecuteWithRetry(
|
|
1179
|
+
() => this._leaveGroupAttempt(groupName, options),
|
|
1180
|
+
options?.abortSignal
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
async _leaveGroupAttempt(groupName, options) {
|
|
1184
|
+
const group = this._getOrAddGroup(groupName);
|
|
1185
|
+
const result = await this._sendMessageWithAckId(
|
|
1186
|
+
(id) => {
|
|
1187
|
+
return {
|
|
1188
|
+
group: groupName,
|
|
1189
|
+
ackId: id,
|
|
1190
|
+
kind: "leaveGroup"
|
|
1191
|
+
};
|
|
1192
|
+
},
|
|
1193
|
+
options?.ackId,
|
|
1194
|
+
options?.abortSignal
|
|
1195
|
+
);
|
|
1196
|
+
group.isJoined = false;
|
|
1197
|
+
return result;
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Send message to group.
|
|
1201
|
+
* @param groupName - The group name
|
|
1202
|
+
* @param content - The data content
|
|
1203
|
+
* @param dataType - The data type
|
|
1204
|
+
* @param options - The options
|
|
1205
|
+
* @param abortSignal - The abort signal
|
|
1206
|
+
*/
|
|
1207
|
+
async sendToGroup(groupName, content, dataType, options) {
|
|
1208
|
+
return this._operationExecuteWithRetry(
|
|
1209
|
+
() => this._sendToGroupAttempt(groupName, content, dataType, options),
|
|
1210
|
+
options?.abortSignal
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
async _sendToGroupAttempt(groupName, content, dataType, options) {
|
|
1214
|
+
const fireAndForget = options?.fireAndForget ?? false;
|
|
1215
|
+
const noEcho = options?.noEcho ?? false;
|
|
1216
|
+
if (!fireAndForget) {
|
|
1217
|
+
return this._sendMessageWithAckId(
|
|
1218
|
+
(id) => {
|
|
1219
|
+
return {
|
|
1220
|
+
kind: "sendToGroup",
|
|
1221
|
+
group: groupName,
|
|
1222
|
+
dataType,
|
|
1223
|
+
data: content,
|
|
1224
|
+
ackId: id,
|
|
1225
|
+
noEcho
|
|
1226
|
+
};
|
|
1227
|
+
},
|
|
1228
|
+
options?.ackId,
|
|
1229
|
+
options?.abortSignal
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
const message = {
|
|
1233
|
+
kind: "sendToGroup",
|
|
1234
|
+
group: groupName,
|
|
1235
|
+
dataType,
|
|
1236
|
+
data: content,
|
|
1237
|
+
noEcho
|
|
1238
|
+
};
|
|
1239
|
+
await this._sendMessage(message, options?.abortSignal);
|
|
1240
|
+
return { isDuplicated: false };
|
|
1241
|
+
}
|
|
1242
|
+
_getWebSocketClientFactory() {
|
|
1243
|
+
return new WebSocketClientFactory();
|
|
1244
|
+
}
|
|
1245
|
+
async _trySendSequenceAck() {
|
|
1246
|
+
if (!this._protocol.isReliableSubProtocol) {
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
const [isUpdated, seqId] = this._sequenceId.tryGetSequenceId();
|
|
1250
|
+
if (isUpdated && seqId !== null && seqId !== void 0) {
|
|
1251
|
+
const message = {
|
|
1252
|
+
kind: "sequenceAck",
|
|
1253
|
+
sequenceId: seqId
|
|
1254
|
+
};
|
|
1255
|
+
try {
|
|
1256
|
+
await this._sendMessage(message);
|
|
1257
|
+
} catch {
|
|
1258
|
+
this._sequenceId.tryUpdate(seqId);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
_connectCore(uri) {
|
|
1263
|
+
if (this._isStopping) {
|
|
1264
|
+
throw new Error("Can't start a client during stopping");
|
|
1265
|
+
}
|
|
1266
|
+
return new Promise((resolve, reject) => {
|
|
1267
|
+
const client = this._wsClient = this._getWebSocketClientFactory().create(
|
|
1268
|
+
uri,
|
|
1269
|
+
this._protocol.name
|
|
1270
|
+
);
|
|
1271
|
+
client.onopen(() => {
|
|
1272
|
+
if (this._isStopping) {
|
|
1273
|
+
try {
|
|
1274
|
+
client.close();
|
|
1275
|
+
} catch {
|
|
1276
|
+
}
|
|
1277
|
+
reject(new Error(`The client is stopped`));
|
|
1278
|
+
}
|
|
1279
|
+
logger.verbose("WebSocket connection has opened");
|
|
1280
|
+
this._lastMessageReceived = Date.now();
|
|
1281
|
+
this._changeState(
|
|
1282
|
+
"Connected"
|
|
1283
|
+
/* Connected */
|
|
1284
|
+
);
|
|
1285
|
+
if (this._protocol.isReliableSubProtocol) {
|
|
1286
|
+
if (this._sequenceAckTask != null) {
|
|
1287
|
+
this._sequenceAckTask.abort();
|
|
1288
|
+
}
|
|
1289
|
+
this._sequenceAckTask = new AbortableTask(async () => {
|
|
1290
|
+
await this._trySendSequenceAck();
|
|
1291
|
+
}, 1e3);
|
|
1292
|
+
}
|
|
1293
|
+
resolve();
|
|
1294
|
+
});
|
|
1295
|
+
client.onerror((e) => {
|
|
1296
|
+
if (this._sequenceAckTask != null) {
|
|
1297
|
+
this._sequenceAckTask.abort();
|
|
1298
|
+
}
|
|
1299
|
+
reject(e);
|
|
1300
|
+
});
|
|
1301
|
+
client.onclose((code, reason) => {
|
|
1302
|
+
if (this._state === "Connected") {
|
|
1303
|
+
logger.verbose("WebSocket closed after open");
|
|
1304
|
+
if (this._sequenceAckTask != null) {
|
|
1305
|
+
this._sequenceAckTask.abort();
|
|
1306
|
+
}
|
|
1307
|
+
logger.info(`WebSocket connection closed. Code: ${code}, Reason: ${reason}`);
|
|
1308
|
+
this._lastCloseEvent = { code, reason };
|
|
1309
|
+
this._handleConnectionClose.call(this);
|
|
1310
|
+
} else {
|
|
1311
|
+
logger.verbose("WebSocket closed before open");
|
|
1312
|
+
reject(new Error(`Failed to start WebSocket: ${code}`));
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
client.onmessage((data) => {
|
|
1316
|
+
const handleAckMessage = (message) => {
|
|
1317
|
+
const isDuplicated = message.error != null && message.error.name === "Duplicate";
|
|
1318
|
+
if (message.success || isDuplicated) {
|
|
1319
|
+
this._ackManager.resolveAck(message.ackId, {
|
|
1320
|
+
ackId: message.ackId,
|
|
1321
|
+
isDuplicated
|
|
1322
|
+
});
|
|
1323
|
+
} else {
|
|
1324
|
+
this._ackManager.rejectAck(
|
|
1325
|
+
message.ackId,
|
|
1326
|
+
new SendMessageError("Failed to send message.", {
|
|
1327
|
+
ackId: message.ackId,
|
|
1328
|
+
errorDetail: message.error
|
|
1329
|
+
})
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
const handleConnectedMessage = async (message) => {
|
|
1334
|
+
this._connectionId = message.connectionId;
|
|
1335
|
+
this._reconnectionToken = message.reconnectionToken;
|
|
1336
|
+
if (!this._isInitialConnected) {
|
|
1337
|
+
this._isInitialConnected = true;
|
|
1338
|
+
if (this._options.autoRejoinGroups) {
|
|
1339
|
+
const groupPromises = [];
|
|
1340
|
+
this._groupMap.forEach((g) => {
|
|
1341
|
+
if (g.isJoined) {
|
|
1342
|
+
groupPromises.push(
|
|
1343
|
+
(async () => {
|
|
1344
|
+
try {
|
|
1345
|
+
await this._joinGroupCore(g.name);
|
|
1346
|
+
} catch (err) {
|
|
1347
|
+
this._safeEmitRejoinGroupFailed(g.name, err);
|
|
1348
|
+
}
|
|
1349
|
+
})()
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
await Promise.all(groupPromises).catch(() => {
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
this._safeEmitConnected(message.connectionId, message.userId);
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1359
|
+
const handleDisconnectedMessage = (message) => {
|
|
1360
|
+
this._lastDisconnectedMessage = message;
|
|
1361
|
+
};
|
|
1362
|
+
const handleGroupDataMessage = (message) => {
|
|
1363
|
+
if (message.sequenceId != null) {
|
|
1364
|
+
const diff = this._sequenceId.tryUpdate(message.sequenceId);
|
|
1365
|
+
if (diff === 0) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
if (diff > this._quickSequenceAckDiff) {
|
|
1369
|
+
this._trySendSequenceAck();
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
this._safeEmitGroupMessage(message);
|
|
1373
|
+
};
|
|
1374
|
+
const handleServerDataMessage = (message) => {
|
|
1375
|
+
if (message.sequenceId != null) {
|
|
1376
|
+
const diff = this._sequenceId.tryUpdate(message.sequenceId);
|
|
1377
|
+
if (diff === 0) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
if (diff > this._quickSequenceAckDiff) {
|
|
1381
|
+
this._trySendSequenceAck();
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
this._safeEmitServerMessage(message);
|
|
1385
|
+
};
|
|
1386
|
+
const handleInvokeResponseMessage = (message) => {
|
|
1387
|
+
const resolved = this._invocationManager.resolveInvocation(message);
|
|
1388
|
+
if (!resolved) {
|
|
1389
|
+
logger.verbose(
|
|
1390
|
+
`Received invokeResponse for unknown invocationId: ${message.invocationId}`
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
this._lastMessageReceived = Date.now();
|
|
1395
|
+
let messages;
|
|
1396
|
+
try {
|
|
1397
|
+
let convertedData;
|
|
1398
|
+
if (Array.isArray(data)) {
|
|
1399
|
+
convertedData = Buffer.concat(data);
|
|
1400
|
+
} else {
|
|
1401
|
+
convertedData = data;
|
|
1402
|
+
}
|
|
1403
|
+
messages = this._protocol.parseMessages(convertedData);
|
|
1404
|
+
if (messages === null) {
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
} catch (err) {
|
|
1408
|
+
logger.warning("An error occurred while parsing the message from service", err);
|
|
1409
|
+
throw err;
|
|
1410
|
+
}
|
|
1411
|
+
if (!Array.isArray(messages)) {
|
|
1412
|
+
messages = [messages];
|
|
1413
|
+
}
|
|
1414
|
+
messages.forEach((message) => {
|
|
1415
|
+
try {
|
|
1416
|
+
switch (message.kind) {
|
|
1417
|
+
case "pong": {
|
|
1418
|
+
break;
|
|
1419
|
+
}
|
|
1420
|
+
case "ack": {
|
|
1421
|
+
handleAckMessage(message);
|
|
1422
|
+
break;
|
|
1423
|
+
}
|
|
1424
|
+
case "connected": {
|
|
1425
|
+
handleConnectedMessage(message);
|
|
1426
|
+
break;
|
|
1427
|
+
}
|
|
1428
|
+
case "disconnected": {
|
|
1429
|
+
handleDisconnectedMessage(message);
|
|
1430
|
+
break;
|
|
1431
|
+
}
|
|
1432
|
+
case "groupData": {
|
|
1433
|
+
handleGroupDataMessage(message);
|
|
1434
|
+
break;
|
|
1435
|
+
}
|
|
1436
|
+
case "serverData": {
|
|
1437
|
+
handleServerDataMessage(message);
|
|
1438
|
+
break;
|
|
1439
|
+
}
|
|
1440
|
+
case "invokeResponse": {
|
|
1441
|
+
handleInvokeResponseMessage(message);
|
|
1442
|
+
break;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
} catch (err) {
|
|
1446
|
+
logger.warning(
|
|
1447
|
+
`An error occurred while handling the message with kind: ${message.kind} from service`,
|
|
1448
|
+
err
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
});
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
async _handleConnectionCloseAndNoRecovery() {
|
|
1456
|
+
this._state = "Disconnected";
|
|
1457
|
+
this._safeEmitDisconnected(this._connectionId, this._lastDisconnectedMessage);
|
|
1458
|
+
if (this._options.autoReconnect) {
|
|
1459
|
+
await this._autoReconnect();
|
|
1460
|
+
} else {
|
|
1461
|
+
await this._handleConnectionStopped();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
async _autoReconnect() {
|
|
1465
|
+
let isSuccess = false;
|
|
1466
|
+
let attempt = 0;
|
|
1467
|
+
try {
|
|
1468
|
+
while (!this._isStopping) {
|
|
1469
|
+
try {
|
|
1470
|
+
await this._startFromRestarting();
|
|
1471
|
+
isSuccess = true;
|
|
1472
|
+
break;
|
|
1473
|
+
} catch (err) {
|
|
1474
|
+
logger.warning("An attempt to reconnect connection failed.", err);
|
|
1475
|
+
attempt++;
|
|
1476
|
+
const delayInMs = this._reconnectRetryPolicy.nextRetryDelayInMs(attempt);
|
|
1477
|
+
if (delayInMs == null) {
|
|
1478
|
+
break;
|
|
1479
|
+
}
|
|
1480
|
+
logger.verbose(`Delay time for reconnect attempt ${attempt}: ${delayInMs}`);
|
|
1481
|
+
await delay(delayInMs).catch(() => {
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
} finally {
|
|
1486
|
+
if (!isSuccess) {
|
|
1487
|
+
this._handleConnectionStopped();
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
_handleConnectionStopped() {
|
|
1492
|
+
this._isStopping = false;
|
|
1493
|
+
this._state = "Stopped";
|
|
1494
|
+
this._disposeKeepaliveTasks();
|
|
1495
|
+
this._safeEmitStopped();
|
|
1496
|
+
}
|
|
1497
|
+
async _trySendPing() {
|
|
1498
|
+
if (this._state !== "Connected" || !this._wsClient?.isOpen()) {
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
const message = {
|
|
1502
|
+
kind: "ping"
|
|
1503
|
+
};
|
|
1504
|
+
try {
|
|
1505
|
+
await this._sendMessage(message);
|
|
1506
|
+
} catch {
|
|
1507
|
+
logger.warning("Failed to send keepalive message to the service");
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
async _checkKeepAliveTimeout() {
|
|
1511
|
+
if (this._state !== "Connected" || !this._wsClient?.isOpen()) {
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
const now = Date.now();
|
|
1515
|
+
if (now - this._lastMessageReceived > this._keepAliveTimeoutInMs) {
|
|
1516
|
+
logger.warning(
|
|
1517
|
+
`No messages received for ${now - this._lastMessageReceived} ms. Closing. The keep alive timeout is set to ${this._keepAliveTimeoutInMs} ms.`
|
|
1518
|
+
);
|
|
1519
|
+
this._wsClient?.close();
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
_getPingKeepaliveTask() {
|
|
1523
|
+
return new AbortableTask(async () => {
|
|
1524
|
+
await this._trySendPing();
|
|
1525
|
+
}, this._keepAliveIntervalInMs);
|
|
1526
|
+
}
|
|
1527
|
+
_getTimeoutMonitorTask() {
|
|
1528
|
+
const timeout = this._keepAliveTimeoutInMs;
|
|
1529
|
+
const checkInterval = Math.floor(timeout / 3);
|
|
1530
|
+
return new AbortableTask(async () => {
|
|
1531
|
+
await this._checkKeepAliveTimeout();
|
|
1532
|
+
}, checkInterval);
|
|
1533
|
+
}
|
|
1534
|
+
async _sendMessage(message, abortSignal) {
|
|
1535
|
+
if (!this._wsClient || !this._wsClient.isOpen()) {
|
|
1536
|
+
throw new Error("The connection is not connected.");
|
|
1537
|
+
}
|
|
1538
|
+
const payload = this._protocol.writeMessage(message);
|
|
1539
|
+
await this._wsClient.send(payload, abortSignal);
|
|
1540
|
+
}
|
|
1541
|
+
async _sendMessageWithAckId(messageProvider, ackId, abortSignal) {
|
|
1542
|
+
const { ackId: resolvedAckId, wait } = this._ackManager.registerAck(ackId);
|
|
1543
|
+
const message = messageProvider(resolvedAckId);
|
|
1544
|
+
try {
|
|
1545
|
+
await this._sendMessage(message, abortSignal);
|
|
1546
|
+
} catch (error) {
|
|
1547
|
+
this._ackManager.discard(resolvedAckId);
|
|
1548
|
+
let errorMessage = "";
|
|
1549
|
+
if (error instanceof Error) {
|
|
1550
|
+
errorMessage = error.message;
|
|
1551
|
+
}
|
|
1552
|
+
throw new SendMessageError(errorMessage, { ackId: resolvedAckId });
|
|
1553
|
+
}
|
|
1554
|
+
return wait(abortSignal);
|
|
1555
|
+
}
|
|
1556
|
+
async _handleConnectionClose() {
|
|
1557
|
+
this._ackManager.rejectAll((ackId) => {
|
|
1558
|
+
return new SendMessageError(
|
|
1559
|
+
"Connection is disconnected before receive ack from the service",
|
|
1560
|
+
{
|
|
1561
|
+
ackId
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1564
|
+
});
|
|
1565
|
+
this._invocationManager.rejectAll((invocationId) => {
|
|
1566
|
+
return new InvocationError(
|
|
1567
|
+
"Connection is disconnected before receiving invoke response from the service",
|
|
1568
|
+
{
|
|
1569
|
+
invocationId
|
|
1570
|
+
}
|
|
1571
|
+
);
|
|
1572
|
+
});
|
|
1573
|
+
if (this._isStopping) {
|
|
1574
|
+
logger.warning("The client is stopping state. Stop recovery.");
|
|
1575
|
+
this._handleConnectionCloseAndNoRecovery();
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
if (this._lastCloseEvent && this._lastCloseEvent.code === 1008) {
|
|
1579
|
+
logger.warning("The websocket close with status code 1008. Stop recovery.");
|
|
1580
|
+
this._handleConnectionCloseAndNoRecovery();
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
if (!this._protocol.isReliableSubProtocol) {
|
|
1584
|
+
logger.warning("The protocol is not reliable, recovery is not applicable");
|
|
1585
|
+
this._handleConnectionCloseAndNoRecovery();
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
const recoveryUri = this._buildRecoveryUri();
|
|
1589
|
+
if (!recoveryUri) {
|
|
1590
|
+
logger.warning("Connection id or reconnection token is not available");
|
|
1591
|
+
this._handleConnectionCloseAndNoRecovery();
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
let recovered = false;
|
|
1595
|
+
this._state = "Recovering";
|
|
1596
|
+
const abortSignal = AbortSignal.timeout(30 * 1e3);
|
|
1597
|
+
try {
|
|
1598
|
+
while (!abortSignal.aborted || this._isStopping) {
|
|
1599
|
+
try {
|
|
1600
|
+
await this._connectCore.call(this, recoveryUri);
|
|
1601
|
+
recovered = true;
|
|
1602
|
+
return;
|
|
1603
|
+
} catch {
|
|
1604
|
+
await delay(1e3);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
} finally {
|
|
1608
|
+
if (!recovered) {
|
|
1609
|
+
logger.warning("Recovery attempts failed more then 30 seconds or the client is stopping");
|
|
1610
|
+
this._handleConnectionCloseAndNoRecovery();
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
_safeEmitConnected(connectionId, userId) {
|
|
1615
|
+
this._emitEvent("connected", {
|
|
1616
|
+
connectionId,
|
|
1617
|
+
userId
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
_safeEmitDisconnected(connectionId, lastDisconnectedMessage) {
|
|
1621
|
+
this._emitEvent("disconnected", {
|
|
1622
|
+
connectionId,
|
|
1623
|
+
message: lastDisconnectedMessage
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
_safeEmitGroupMessage(message) {
|
|
1627
|
+
this._emitEvent("group-message", {
|
|
1628
|
+
message
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
_safeEmitServerMessage(message) {
|
|
1632
|
+
this._emitEvent("server-message", {
|
|
1633
|
+
message
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
_safeEmitStopped() {
|
|
1637
|
+
this._emitEvent("stopped", {});
|
|
1638
|
+
}
|
|
1639
|
+
_safeEmitRejoinGroupFailed(groupName, err) {
|
|
1640
|
+
this._emitEvent("rejoin-group-failed", {
|
|
1641
|
+
group: groupName,
|
|
1642
|
+
error: err
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
_mapInvokeResponse(message) {
|
|
1646
|
+
if (message.success !== true) {
|
|
1647
|
+
if (message.success === false) {
|
|
1648
|
+
throw new InvocationError(message.error?.message ?? "Invocation failed.", {
|
|
1649
|
+
invocationId: message.invocationId,
|
|
1650
|
+
errorDetail: message.error
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
throw new InvocationError("Unsupported invoke response frame.", {
|
|
1654
|
+
invocationId: message.invocationId
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
return {
|
|
1658
|
+
invocationId: message.invocationId,
|
|
1659
|
+
dataType: message.dataType,
|
|
1660
|
+
data: message.data
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
async _sendCancelInvocation(invocationId) {
|
|
1664
|
+
const message = {
|
|
1665
|
+
kind: "cancelInvocation",
|
|
1666
|
+
invocationId
|
|
1667
|
+
};
|
|
1668
|
+
try {
|
|
1669
|
+
await this._sendMessage(message);
|
|
1670
|
+
} catch (err) {
|
|
1671
|
+
logger.verbose(`Failed to send cancelInvocation for ${invocationId}`, err);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
_buildDefaultOptions(clientOptions) {
|
|
1675
|
+
if (clientOptions.autoReconnect == null) {
|
|
1676
|
+
clientOptions.autoReconnect = true;
|
|
1677
|
+
}
|
|
1678
|
+
if (clientOptions.autoRejoinGroups == null) {
|
|
1679
|
+
clientOptions.autoRejoinGroups = true;
|
|
1680
|
+
}
|
|
1681
|
+
if (clientOptions.protocol == null) {
|
|
1682
|
+
clientOptions.protocol = WebPubSubJsonReliableProtocol();
|
|
1683
|
+
}
|
|
1684
|
+
if (clientOptions.keepAliveTimeoutInMs == null) {
|
|
1685
|
+
clientOptions.keepAliveTimeoutInMs = 12e4;
|
|
1686
|
+
}
|
|
1687
|
+
if (clientOptions.keepAliveTimeoutInMs < 0) {
|
|
1688
|
+
throw new RangeError("keepAliveTimeoutInMs must be greater than or equal to 0.");
|
|
1689
|
+
}
|
|
1690
|
+
if (clientOptions.keepAliveIntervalInMs == null) {
|
|
1691
|
+
clientOptions.keepAliveIntervalInMs = 2e4;
|
|
1692
|
+
}
|
|
1693
|
+
if (clientOptions.keepAliveIntervalInMs < 0) {
|
|
1694
|
+
throw new RangeError("keepAliveIntervalInMs must be greater than or equal to 0.");
|
|
1695
|
+
}
|
|
1696
|
+
this._buildMessageRetryOptions(clientOptions);
|
|
1697
|
+
this._buildReconnectRetryOptions(clientOptions);
|
|
1698
|
+
return clientOptions;
|
|
1699
|
+
}
|
|
1700
|
+
_buildMessageRetryOptions(clientOptions) {
|
|
1701
|
+
if (!clientOptions.messageRetryOptions) {
|
|
1702
|
+
clientOptions.messageRetryOptions = {};
|
|
1703
|
+
}
|
|
1704
|
+
if (clientOptions.messageRetryOptions.maxRetries == null || clientOptions.messageRetryOptions.maxRetries < 0) {
|
|
1705
|
+
clientOptions.messageRetryOptions.maxRetries = 3;
|
|
1706
|
+
}
|
|
1707
|
+
if (clientOptions.messageRetryOptions.retryDelayInMs == null || clientOptions.messageRetryOptions.retryDelayInMs < 0) {
|
|
1708
|
+
clientOptions.messageRetryOptions.retryDelayInMs = 1e3;
|
|
1709
|
+
}
|
|
1710
|
+
if (clientOptions.messageRetryOptions.maxRetryDelayInMs == null || clientOptions.messageRetryOptions.maxRetryDelayInMs < 0) {
|
|
1711
|
+
clientOptions.messageRetryOptions.maxRetryDelayInMs = 3e4;
|
|
1712
|
+
}
|
|
1713
|
+
if (clientOptions.messageRetryOptions.mode == null) {
|
|
1714
|
+
clientOptions.messageRetryOptions.mode = "Fixed";
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
_buildReconnectRetryOptions(clientOptions) {
|
|
1718
|
+
if (!clientOptions.reconnectRetryOptions) {
|
|
1719
|
+
clientOptions.reconnectRetryOptions = {};
|
|
1720
|
+
}
|
|
1721
|
+
if (clientOptions.reconnectRetryOptions.maxRetries == null || clientOptions.reconnectRetryOptions.maxRetries < 0) {
|
|
1722
|
+
clientOptions.reconnectRetryOptions.maxRetries = Number.MAX_VALUE;
|
|
1723
|
+
}
|
|
1724
|
+
if (clientOptions.reconnectRetryOptions.retryDelayInMs == null || clientOptions.reconnectRetryOptions.retryDelayInMs < 0) {
|
|
1725
|
+
clientOptions.reconnectRetryOptions.retryDelayInMs = 1e3;
|
|
1726
|
+
}
|
|
1727
|
+
if (clientOptions.reconnectRetryOptions.maxRetryDelayInMs == null || clientOptions.reconnectRetryOptions.maxRetryDelayInMs < 0) {
|
|
1728
|
+
clientOptions.reconnectRetryOptions.maxRetryDelayInMs = 3e4;
|
|
1729
|
+
}
|
|
1730
|
+
if (clientOptions.reconnectRetryOptions.mode == null) {
|
|
1731
|
+
clientOptions.reconnectRetryOptions.mode = "Fixed";
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
_buildRecoveryUri() {
|
|
1735
|
+
if (this._connectionId && this._reconnectionToken && this._uri) {
|
|
1736
|
+
const url = new URL(this._uri);
|
|
1737
|
+
url.searchParams.append("awps_connection_id", this._connectionId);
|
|
1738
|
+
url.searchParams.append("awps_reconnection_token", this._reconnectionToken);
|
|
1739
|
+
return url.toString();
|
|
1740
|
+
}
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1743
|
+
_getOrAddGroup(name) {
|
|
1744
|
+
if (!this._groupMap.has(name)) {
|
|
1745
|
+
this._groupMap.set(name, new WebPubSubGroup(name));
|
|
1746
|
+
}
|
|
1747
|
+
return this._groupMap.get(name);
|
|
1748
|
+
}
|
|
1749
|
+
_changeState(newState) {
|
|
1750
|
+
logger.verbose(
|
|
1751
|
+
`The client state transfer from ${this._state.toString()} to ${newState.toString()}`
|
|
1752
|
+
);
|
|
1753
|
+
this._state = newState;
|
|
1754
|
+
}
|
|
1755
|
+
async _operationExecuteWithRetry(inner, signal) {
|
|
1756
|
+
let retryAttempt = 0;
|
|
1757
|
+
while (true) {
|
|
1758
|
+
try {
|
|
1759
|
+
return await inner.call(this);
|
|
1760
|
+
} catch (err) {
|
|
1761
|
+
if (err instanceof InvocationError) {
|
|
1762
|
+
throw err;
|
|
1763
|
+
}
|
|
1764
|
+
retryAttempt++;
|
|
1765
|
+
const delayInMs = this._messageRetryPolicy.nextRetryDelayInMs(retryAttempt);
|
|
1766
|
+
if (delayInMs == null) {
|
|
1767
|
+
throw err;
|
|
1768
|
+
}
|
|
1769
|
+
await delay(delayInMs);
|
|
1770
|
+
if (signal?.aborted) {
|
|
1771
|
+
throw err;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
var RetryPolicy = class {
|
|
1778
|
+
constructor(retryOptions) {
|
|
1779
|
+
__publicField(this, "_retryOptions");
|
|
1780
|
+
__publicField(this, "_maxRetriesToGetMaxDelay");
|
|
1781
|
+
this._retryOptions = retryOptions;
|
|
1782
|
+
this._maxRetriesToGetMaxDelay = Math.ceil(
|
|
1783
|
+
Math.log2(this._retryOptions.maxRetryDelayInMs) - Math.log2(this._retryOptions.retryDelayInMs) + 1
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
nextRetryDelayInMs(retryAttempt) {
|
|
1787
|
+
if (retryAttempt > this._retryOptions.maxRetries) {
|
|
1788
|
+
return null;
|
|
1789
|
+
} else {
|
|
1790
|
+
if (this._retryOptions.mode === "Fixed") {
|
|
1791
|
+
return this._retryOptions.retryDelayInMs;
|
|
1792
|
+
} else {
|
|
1793
|
+
return this._calculateExponentialDelay(retryAttempt);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
_calculateExponentialDelay(attempt) {
|
|
1798
|
+
if (attempt >= this._maxRetriesToGetMaxDelay) {
|
|
1799
|
+
return this._retryOptions.maxRetryDelayInMs;
|
|
1800
|
+
} else {
|
|
1801
|
+
return (1 << attempt - 1) * this._retryOptions.retryDelayInMs;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
var WebPubSubGroup = class {
|
|
1806
|
+
constructor(name) {
|
|
1807
|
+
__publicField(this, "name");
|
|
1808
|
+
__publicField(this, "isJoined", false);
|
|
1809
|
+
this.name = name;
|
|
1810
|
+
}
|
|
1811
|
+
};
|
|
1812
|
+
var SequenceId = class {
|
|
1813
|
+
constructor() {
|
|
1814
|
+
__publicField(this, "_sequenceId");
|
|
1815
|
+
__publicField(this, "_isUpdate");
|
|
1816
|
+
this._sequenceId = 0;
|
|
1817
|
+
this._isUpdate = false;
|
|
1818
|
+
}
|
|
1819
|
+
tryUpdate(sequenceId) {
|
|
1820
|
+
this._isUpdate = true;
|
|
1821
|
+
if (sequenceId > this._sequenceId) {
|
|
1822
|
+
const diff = sequenceId - this._sequenceId;
|
|
1823
|
+
this._sequenceId = sequenceId;
|
|
1824
|
+
return diff;
|
|
1825
|
+
}
|
|
1826
|
+
return 0;
|
|
1827
|
+
}
|
|
1828
|
+
tryGetSequenceId() {
|
|
1829
|
+
if (this._isUpdate) {
|
|
1830
|
+
this._isUpdate = false;
|
|
1831
|
+
return [true, this._sequenceId];
|
|
1832
|
+
}
|
|
1833
|
+
return [false, null];
|
|
1834
|
+
}
|
|
1835
|
+
reset() {
|
|
1836
|
+
this._sequenceId = 0;
|
|
1837
|
+
this._isUpdate = false;
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
var AbortableTask = class {
|
|
1841
|
+
constructor(func, interval, obj) {
|
|
1842
|
+
__publicField(this, "_func");
|
|
1843
|
+
__publicField(this, "_abortController");
|
|
1844
|
+
__publicField(this, "_interval");
|
|
1845
|
+
__publicField(this, "_obj");
|
|
1846
|
+
this._func = func;
|
|
1847
|
+
this._abortController = new AbortController();
|
|
1848
|
+
this._interval = interval;
|
|
1849
|
+
this._obj = obj;
|
|
1850
|
+
this._start();
|
|
1851
|
+
}
|
|
1852
|
+
abort() {
|
|
1853
|
+
try {
|
|
1854
|
+
this._abortController.abort();
|
|
1855
|
+
} catch {
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
async _start() {
|
|
1859
|
+
const signal = this._abortController.signal;
|
|
1860
|
+
while (!signal.aborted) {
|
|
1861
|
+
try {
|
|
1862
|
+
await this._func(this._obj);
|
|
1863
|
+
} catch {
|
|
1864
|
+
} finally {
|
|
1865
|
+
await delay(this._interval);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
|
|
1871
|
+
// node_modules/tslib/tslib.es6.mjs
|
|
1872
|
+
function __values(o) {
|
|
1873
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
1874
|
+
if (m) return m.call(o);
|
|
1875
|
+
if (o && typeof o.length === "number") return {
|
|
1876
|
+
next: function() {
|
|
1877
|
+
if (o && i >= o.length) o = void 0;
|
|
1878
|
+
return { value: o && o[i++], done: !o };
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
1882
|
+
}
|
|
1883
|
+
function __await(v) {
|
|
1884
|
+
return this instanceof __await ? (this.v = v, this) : new __await(v);
|
|
1885
|
+
}
|
|
1886
|
+
function __asyncGenerator(thisArg, _arguments, generator) {
|
|
1887
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
1888
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
1889
|
+
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function() {
|
|
1890
|
+
return this;
|
|
1891
|
+
}, i;
|
|
1892
|
+
function awaitReturn(f) {
|
|
1893
|
+
return function(v) {
|
|
1894
|
+
return Promise.resolve(v).then(f, reject);
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
function verb(n, f) {
|
|
1898
|
+
if (g[n]) {
|
|
1899
|
+
i[n] = function(v) {
|
|
1900
|
+
return new Promise(function(a, b) {
|
|
1901
|
+
q.push([n, v, a, b]) > 1 || resume(n, v);
|
|
1902
|
+
});
|
|
1903
|
+
};
|
|
1904
|
+
if (f) i[n] = f(i[n]);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
function resume(n, v) {
|
|
1908
|
+
try {
|
|
1909
|
+
step(g[n](v));
|
|
1910
|
+
} catch (e) {
|
|
1911
|
+
settle(q[0][3], e);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
function step(r) {
|
|
1915
|
+
r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
|
|
1916
|
+
}
|
|
1917
|
+
function fulfill(value) {
|
|
1918
|
+
resume("next", value);
|
|
1919
|
+
}
|
|
1920
|
+
function reject(value) {
|
|
1921
|
+
resume("throw", value);
|
|
1922
|
+
}
|
|
1923
|
+
function settle(f, v) {
|
|
1924
|
+
if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
function __asyncDelegator(o) {
|
|
1928
|
+
var i, p;
|
|
1929
|
+
return i = {}, verb("next"), verb("throw", function(e) {
|
|
1930
|
+
throw e;
|
|
1931
|
+
}), verb("return"), i[Symbol.iterator] = function() {
|
|
1932
|
+
return this;
|
|
1933
|
+
}, i;
|
|
1934
|
+
function verb(n, f) {
|
|
1935
|
+
i[n] = o[n] ? function(v) {
|
|
1936
|
+
return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v;
|
|
1937
|
+
} : f;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
function __asyncValues(o) {
|
|
1941
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
1942
|
+
var m = o[Symbol.asyncIterator], i;
|
|
1943
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
|
|
1944
|
+
return this;
|
|
1945
|
+
}, i);
|
|
1946
|
+
function verb(n) {
|
|
1947
|
+
i[n] = o[n] && function(v) {
|
|
1948
|
+
return new Promise(function(resolve, reject) {
|
|
1949
|
+
v = o[n](v), settle(resolve, reject, v.done, v.value);
|
|
1950
|
+
});
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
function settle(resolve, reject, d, v) {
|
|
1954
|
+
Promise.resolve(v).then(function(v2) {
|
|
1955
|
+
resolve({ value: v2, done: d });
|
|
1956
|
+
}, reject);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
// node_modules/@azure/core-paging/dist/esm/getPagedAsyncIterator.js
|
|
1961
|
+
function getPagedAsyncIterator(pagedResult) {
|
|
1962
|
+
var _a;
|
|
1963
|
+
const iter = getItemAsyncIterator(pagedResult);
|
|
1964
|
+
return {
|
|
1965
|
+
next() {
|
|
1966
|
+
return iter.next();
|
|
1967
|
+
},
|
|
1968
|
+
[Symbol.asyncIterator]() {
|
|
1969
|
+
return this;
|
|
1970
|
+
},
|
|
1971
|
+
byPage: (_a = pagedResult === null || pagedResult === void 0 ? void 0 : pagedResult.byPage) !== null && _a !== void 0 ? _a : ((settings) => {
|
|
1972
|
+
const { continuationToken, maxPageSize } = settings !== null && settings !== void 0 ? settings : {};
|
|
1973
|
+
return getPageAsyncIterator(pagedResult, {
|
|
1974
|
+
pageLink: continuationToken,
|
|
1975
|
+
maxPageSize
|
|
1976
|
+
});
|
|
1977
|
+
})
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
function getItemAsyncIterator(pagedResult) {
|
|
1981
|
+
return __asyncGenerator(this, arguments, function* getItemAsyncIterator_1() {
|
|
1982
|
+
var _a, e_1, _b, _c, _d, e_2, _e, _f;
|
|
1983
|
+
const pages = getPageAsyncIterator(pagedResult);
|
|
1984
|
+
const firstVal = yield __await(pages.next());
|
|
1985
|
+
if (!Array.isArray(firstVal.value)) {
|
|
1986
|
+
const { toElements } = pagedResult;
|
|
1987
|
+
if (toElements) {
|
|
1988
|
+
yield __await(yield* __asyncDelegator(__asyncValues(toElements(firstVal.value))));
|
|
1989
|
+
try {
|
|
1990
|
+
for (var _g = true, pages_1 = __asyncValues(pages), pages_1_1; pages_1_1 = yield __await(pages_1.next()), _a = pages_1_1.done, !_a; _g = true) {
|
|
1991
|
+
_c = pages_1_1.value;
|
|
1992
|
+
_g = false;
|
|
1993
|
+
const page = _c;
|
|
1994
|
+
yield __await(yield* __asyncDelegator(__asyncValues(toElements(page))));
|
|
1995
|
+
}
|
|
1996
|
+
} catch (e_1_1) {
|
|
1997
|
+
e_1 = { error: e_1_1 };
|
|
1998
|
+
} finally {
|
|
1999
|
+
try {
|
|
2000
|
+
if (!_g && !_a && (_b = pages_1.return)) yield __await(_b.call(pages_1));
|
|
2001
|
+
} finally {
|
|
2002
|
+
if (e_1) throw e_1.error;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
} else {
|
|
2006
|
+
yield yield __await(firstVal.value);
|
|
2007
|
+
yield __await(yield* __asyncDelegator(__asyncValues(pages)));
|
|
2008
|
+
}
|
|
2009
|
+
} else {
|
|
2010
|
+
yield __await(yield* __asyncDelegator(__asyncValues(firstVal.value)));
|
|
2011
|
+
try {
|
|
2012
|
+
for (var _h = true, pages_2 = __asyncValues(pages), pages_2_1; pages_2_1 = yield __await(pages_2.next()), _d = pages_2_1.done, !_d; _h = true) {
|
|
2013
|
+
_f = pages_2_1.value;
|
|
2014
|
+
_h = false;
|
|
2015
|
+
const page = _f;
|
|
2016
|
+
yield __await(yield* __asyncDelegator(__asyncValues(page)));
|
|
2017
|
+
}
|
|
2018
|
+
} catch (e_2_1) {
|
|
2019
|
+
e_2 = { error: e_2_1 };
|
|
2020
|
+
} finally {
|
|
2021
|
+
try {
|
|
2022
|
+
if (!_h && !_d && (_e = pages_2.return)) yield __await(_e.call(pages_2));
|
|
2023
|
+
} finally {
|
|
2024
|
+
if (e_2) throw e_2.error;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
function getPageAsyncIterator(pagedResult, options = {}) {
|
|
2031
|
+
return __asyncGenerator(this, arguments, function* getPageAsyncIterator_1() {
|
|
2032
|
+
const { pageLink, maxPageSize } = options;
|
|
2033
|
+
let response = yield __await(pagedResult.getPage(pageLink !== null && pageLink !== void 0 ? pageLink : pagedResult.firstPageLink, maxPageSize));
|
|
2034
|
+
if (!response) {
|
|
2035
|
+
return yield __await(void 0);
|
|
2036
|
+
}
|
|
2037
|
+
yield yield __await(response.page);
|
|
2038
|
+
while (response.nextPageLink) {
|
|
2039
|
+
response = yield __await(pagedResult.getPage(response.nextPageLink, maxPageSize));
|
|
2040
|
+
if (!response) {
|
|
2041
|
+
return yield __await(void 0);
|
|
2042
|
+
}
|
|
2043
|
+
yield yield __await(response.page);
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// src/chatClient.ts
|
|
2049
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
2050
|
+
|
|
2051
|
+
// src/constant.ts
|
|
2052
|
+
var INVOCATION_NAME = {
|
|
2053
|
+
LOGIN: "chat.login",
|
|
2054
|
+
LIST_USER_CONVERSATION: "chat.listUserConversation",
|
|
2055
|
+
GET_USER_PROPERTIES: "chat.getUserProperties",
|
|
2056
|
+
GET_ROOM: "chat.getRoom",
|
|
2057
|
+
LIST_MESSAGES: "chat.queryMessageHistory",
|
|
2058
|
+
SEND_TEXT_MESSAGE: "chat.sendTextMessage",
|
|
2059
|
+
CREATE_ROOM: "chat.createRoom",
|
|
2060
|
+
JOIN_ROOM: "chat.joinRoom",
|
|
2061
|
+
MANAGE_ROOM_MEMBER: "chat.manageRoomMember"
|
|
2062
|
+
};
|
|
2063
|
+
var ERRORS = {
|
|
2064
|
+
RoomAlreadyExists: "RoomAlreadyExists",
|
|
2065
|
+
UserAlreadyInRoom: "UserAlreadyInRoom",
|
|
2066
|
+
NoPermissionInRoom: "NoPermissionInRoom",
|
|
2067
|
+
NotStarted: "NotStarted",
|
|
2068
|
+
UnknownRoom: "UnknownRoom",
|
|
2069
|
+
InvalidServerResponse: "InvalidServerResponse"
|
|
2070
|
+
};
|
|
2071
|
+
|
|
2072
|
+
// src/logger.ts
|
|
2073
|
+
var logger2 = createClientLogger("web-pubsub-chat-client:*");
|
|
2074
|
+
|
|
2075
|
+
// src/utils.ts
|
|
2076
|
+
function isWebPubSubClient(obj) {
|
|
2077
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
2078
|
+
const anyObj = obj;
|
|
2079
|
+
return typeof anyObj === "object" && (typeof anyObj.start === "function" || typeof anyObj.stop === "function" || typeof anyObj.on === "function");
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
// src/chatClient.ts
|
|
2083
|
+
var ChatError = class extends Error {
|
|
2084
|
+
constructor(message, code) {
|
|
2085
|
+
super(message);
|
|
2086
|
+
/** Stable, machine-readable error code. Compare against {@link KnownChatErrorCode}. */
|
|
2087
|
+
__publicField(this, "code");
|
|
2088
|
+
this.name = "ChatError";
|
|
2089
|
+
this.code = code;
|
|
2090
|
+
}
|
|
2091
|
+
};
|
|
2092
|
+
var PromiseCompletionSource = class {
|
|
2093
|
+
constructor() {
|
|
2094
|
+
__publicField(this, "promise");
|
|
2095
|
+
__publicField(this, "resolvePromise");
|
|
2096
|
+
this.promise = new Promise((resolve) => {
|
|
2097
|
+
this.resolvePromise = resolve;
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
setResult() {
|
|
2101
|
+
this.resolvePromise();
|
|
2102
|
+
}
|
|
2103
|
+
wait() {
|
|
2104
|
+
return this.promise;
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
var ChatClient = class _ChatClient {
|
|
2108
|
+
// Implementation also accepts a pre-built connection (test seam); this is
|
|
2109
|
+
// not a public overload, so it does not appear in the public API surface.
|
|
2110
|
+
constructor(credentialOrConnection) {
|
|
2111
|
+
/** The underlying transport. Private — `ChatClient` builds and owns it. */
|
|
2112
|
+
__publicField(this, "_connection");
|
|
2113
|
+
__publicField(this, "_emitter", new EventEmitter2());
|
|
2114
|
+
__publicField(this, "_rooms", /* @__PURE__ */ new Map());
|
|
2115
|
+
__publicField(this, "_conversationIds", /* @__PURE__ */ new Set());
|
|
2116
|
+
__publicField(this, "_userId");
|
|
2117
|
+
__publicField(this, "_isStarted", false);
|
|
2118
|
+
__publicField(this, "_startPromise");
|
|
2119
|
+
// Created after the underlying connection starts so stop() can wait for the single "stopped" event.
|
|
2120
|
+
__publicField(this, "_connectionStoppedTCS");
|
|
2121
|
+
__publicField(this, "_isConnectionStopping", false);
|
|
2122
|
+
this._connection = isWebPubSubClient(credentialOrConnection) ? credentialOrConnection : new WebPubSubClient(credentialOrConnection);
|
|
2123
|
+
this._connection.on("group-message", (e) => {
|
|
2124
|
+
this._handleNotification(e.message.data);
|
|
2125
|
+
});
|
|
2126
|
+
this._connection.on("server-message", (e) => {
|
|
2127
|
+
this._handleNotification(e.message.data);
|
|
2128
|
+
});
|
|
2129
|
+
this._connection.on("stopped", () => {
|
|
2130
|
+
this._connectionStoppedTCS?.setResult();
|
|
2131
|
+
this._connectionStoppedTCS = void 0;
|
|
2132
|
+
this._isConnectionStopping = false;
|
|
2133
|
+
this.resetState();
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
async _handleNotification(data) {
|
|
2137
|
+
if (!this._isStarted && !this._startPromise) {
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
logger2.info("Received notification:", data);
|
|
2141
|
+
try {
|
|
2142
|
+
const type = data.notificationType;
|
|
2143
|
+
switch (type) {
|
|
2144
|
+
case "MessageCreated": {
|
|
2145
|
+
const body = data.body;
|
|
2146
|
+
if (!body.conversation.roomId) {
|
|
2147
|
+
logger2.warning(
|
|
2148
|
+
`MessageCreated notification missing roomId; skipping emit. conversationId=${body.conversation.conversationId}`
|
|
2149
|
+
);
|
|
2150
|
+
break;
|
|
2151
|
+
}
|
|
2152
|
+
const event = {
|
|
2153
|
+
roomId: body.conversation.roomId,
|
|
2154
|
+
message: body.message
|
|
2155
|
+
};
|
|
2156
|
+
this._emitter.emit("message", event);
|
|
2157
|
+
break;
|
|
2158
|
+
}
|
|
2159
|
+
case "RoomJoined": {
|
|
2160
|
+
const roomInfo = data.body;
|
|
2161
|
+
this._rooms.set(roomInfo.roomId, roomInfo);
|
|
2162
|
+
const event = { room: roomInfo };
|
|
2163
|
+
this._emitter.emit("room-joined", event);
|
|
2164
|
+
break;
|
|
2165
|
+
}
|
|
2166
|
+
case "RoomMemberJoined": {
|
|
2167
|
+
const body = data.body;
|
|
2168
|
+
const event = { roomId: body.roomId, title: body.title, userId: body.userId };
|
|
2169
|
+
this._emitter.emit("member-joined", event);
|
|
2170
|
+
break;
|
|
2171
|
+
}
|
|
2172
|
+
// someone (not self) left a specific room
|
|
2173
|
+
case "RoomMemberLeft": {
|
|
2174
|
+
const body = data.body;
|
|
2175
|
+
const event = { roomId: body.roomId, title: body.title, userId: body.userId };
|
|
2176
|
+
this._emitter.emit("member-left", event);
|
|
2177
|
+
break;
|
|
2178
|
+
}
|
|
2179
|
+
// self left a specific room
|
|
2180
|
+
case "RoomLeft": {
|
|
2181
|
+
const body = data.body;
|
|
2182
|
+
if (!this._rooms.has(body.roomId)) {
|
|
2183
|
+
break;
|
|
2184
|
+
}
|
|
2185
|
+
const event = { roomId: body.roomId, title: body.title };
|
|
2186
|
+
this._emitter.emit("room-left", event);
|
|
2187
|
+
this._rooms.delete(body.roomId);
|
|
2188
|
+
break;
|
|
2189
|
+
}
|
|
2190
|
+
case "MessageUpdated":
|
|
2191
|
+
case "MessageDeleted":
|
|
2192
|
+
case "RoomClosed":
|
|
2193
|
+
case "AddContact":
|
|
2194
|
+
logger2.warning(`Known notification type ${type} received but not implemented yet.`);
|
|
2195
|
+
break;
|
|
2196
|
+
default:
|
|
2197
|
+
logger2.warning(`Unknown notification type received: ${type}`);
|
|
2198
|
+
}
|
|
2199
|
+
} catch (err) {
|
|
2200
|
+
logger2.error(`Error processing notification, error = ${err}, data: `, data);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
/** Invoke server event and return typed data */
|
|
2204
|
+
async invokeWithReturnType(eventName, payload, dataType, options) {
|
|
2205
|
+
logger2.verbose(`invoke event: '${eventName}', dataType: ${dataType}, payload:`, payload);
|
|
2206
|
+
try {
|
|
2207
|
+
const rawResponse = await this._connection.invokeEvent(eventName, payload, dataType, {
|
|
2208
|
+
abortSignal: options?.abortSignal
|
|
2209
|
+
});
|
|
2210
|
+
logger2.verbose(`invoke response for '${eventName}':`, rawResponse);
|
|
2211
|
+
const data = rawResponse.data;
|
|
2212
|
+
if (data && typeof data === "object" && typeof data.code === "string") {
|
|
2213
|
+
throw new ChatError(`Invocation of event "${eventName}" failed: ${data.code}`, data.code);
|
|
2214
|
+
}
|
|
2215
|
+
return data;
|
|
2216
|
+
} catch (e) {
|
|
2217
|
+
if (e instanceof ChatError) throw e;
|
|
2218
|
+
if (e instanceof InvocationError && e.errorDetail?.name) {
|
|
2219
|
+
throw new ChatError(e.message, e.errorDetail.name);
|
|
2220
|
+
}
|
|
2221
|
+
throw e;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
static async start(clientAccessUrlOrCredential, options) {
|
|
2225
|
+
const credential = typeof clientAccessUrlOrCredential === "string" ? { getClientAccessUrl: async () => clientAccessUrlOrCredential } : clientAccessUrlOrCredential;
|
|
2226
|
+
const chatClient = new _ChatClient(credential);
|
|
2227
|
+
await chatClient.start({ abortSignal: options?.abortSignal });
|
|
2228
|
+
return chatClient;
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Connect the underlying transport and authenticate with the chat
|
|
2232
|
+
* service.
|
|
2233
|
+
*
|
|
2234
|
+
* Idempotent: concurrent calls share a single in-flight promise, and
|
|
2235
|
+
* calls made on an already-started client resolve immediately. After
|
|
2236
|
+
* `stop()` the client can be started again; state from the previous
|
|
2237
|
+
* session is reset.
|
|
2238
|
+
*
|
|
2239
|
+
* @param options - Cancellation token for the start operation. Aborting
|
|
2240
|
+
* leaves the client in its initial (not-started) state.
|
|
2241
|
+
*/
|
|
2242
|
+
async start(options) {
|
|
2243
|
+
if (this._startPromise) return this._startPromise;
|
|
2244
|
+
if (this._isStarted) return;
|
|
2245
|
+
if (this._connectionStoppedTCS && this._isConnectionStopping) {
|
|
2246
|
+
await this._connectionStoppedTCS.wait();
|
|
2247
|
+
if (this._startPromise) return this._startPromise;
|
|
2248
|
+
if (this._isStarted) return;
|
|
2249
|
+
}
|
|
2250
|
+
const startPromise = this.startCore(options);
|
|
2251
|
+
this._startPromise = startPromise;
|
|
2252
|
+
try {
|
|
2253
|
+
await startPromise;
|
|
2254
|
+
} finally {
|
|
2255
|
+
if (this._startPromise === startPromise) {
|
|
2256
|
+
this._startPromise = void 0;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
async startCore(options) {
|
|
2261
|
+
this.resetState();
|
|
2262
|
+
try {
|
|
2263
|
+
await this._connection.start({ abortSignal: options?.abortSignal });
|
|
2264
|
+
this._connectionStoppedTCS = new PromiseCompletionSource();
|
|
2265
|
+
this._isConnectionStopping = false;
|
|
2266
|
+
const loginResponse = await this.invokeWithReturnType(
|
|
2267
|
+
INVOCATION_NAME.LOGIN,
|
|
2268
|
+
"",
|
|
2269
|
+
"text",
|
|
2270
|
+
options
|
|
2271
|
+
);
|
|
2272
|
+
logger2.info("loginResponse", loginResponse);
|
|
2273
|
+
const conversationIds = new Set(loginResponse.conversationIds || []);
|
|
2274
|
+
const roomInfos = await Promise.all(
|
|
2275
|
+
(loginResponse.roomIds || []).map(async (roomId) => {
|
|
2276
|
+
const roomInfo = await this.fetchRoomDetail(roomId, {
|
|
2277
|
+
abortSignal: options?.abortSignal,
|
|
2278
|
+
withMembers: false
|
|
2279
|
+
});
|
|
2280
|
+
return { roomId, roomInfo };
|
|
2281
|
+
})
|
|
2282
|
+
);
|
|
2283
|
+
this._userId = loginResponse.userId;
|
|
2284
|
+
this._conversationIds = conversationIds;
|
|
2285
|
+
roomInfos.forEach(({ roomId, roomInfo }) => {
|
|
2286
|
+
this._rooms.set(roomId, roomInfo);
|
|
2287
|
+
});
|
|
2288
|
+
this._isStarted = true;
|
|
2289
|
+
const startedEvent = { userId: loginResponse.userId };
|
|
2290
|
+
this._emitter.emit("started", startedEvent);
|
|
2291
|
+
} catch (err) {
|
|
2292
|
+
this.resetState();
|
|
2293
|
+
await this.stopConnection();
|
|
2294
|
+
throw err;
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
ensureStarted() {
|
|
2298
|
+
if (!this._isStarted) {
|
|
2299
|
+
throw new ChatError("Not started. Please call start() first.", ERRORS.NotStarted);
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Fetch a user's profile.
|
|
2304
|
+
*
|
|
2305
|
+
* @param userId - Id of the user to look up.
|
|
2306
|
+
* @param options - Optional `{ abortSignal }`.
|
|
2307
|
+
*/
|
|
2308
|
+
async getUserProfile(userId, options) {
|
|
2309
|
+
this.ensureStarted();
|
|
2310
|
+
return this.invokeWithReturnType(
|
|
2311
|
+
INVOCATION_NAME.GET_USER_PROPERTIES,
|
|
2312
|
+
{ userId },
|
|
2313
|
+
"json",
|
|
2314
|
+
options
|
|
2315
|
+
);
|
|
2316
|
+
}
|
|
2317
|
+
async sendToConversation(conversationId, message, options) {
|
|
2318
|
+
this.ensureStarted();
|
|
2319
|
+
const payload = {
|
|
2320
|
+
conversation: { conversationId },
|
|
2321
|
+
content: message
|
|
2322
|
+
};
|
|
2323
|
+
const resp = await this.invokeWithReturnType(
|
|
2324
|
+
INVOCATION_NAME.SEND_TEXT_MESSAGE,
|
|
2325
|
+
payload,
|
|
2326
|
+
"json",
|
|
2327
|
+
options
|
|
2328
|
+
);
|
|
2329
|
+
if (!resp || !resp.id) {
|
|
2330
|
+
throw new ChatError(
|
|
2331
|
+
`Failed to send message to conversation ${conversationId}, got invalid invoke response: ${JSON.stringify(resp)}`,
|
|
2332
|
+
ERRORS.InvalidServerResponse
|
|
2333
|
+
);
|
|
2334
|
+
}
|
|
2335
|
+
const msgId = resp.id;
|
|
2336
|
+
const roomId = Array.from(this._rooms.values()).find((r) => r.defaultConversationId === conversationId)?.roomId;
|
|
2337
|
+
if (!roomId) {
|
|
2338
|
+
logger2.warning(
|
|
2339
|
+
`Failed to find roomId for conversationId ${conversationId} when sending message; skipping local sender-echo emit.`
|
|
2340
|
+
);
|
|
2341
|
+
return { messageId: msgId };
|
|
2342
|
+
}
|
|
2343
|
+
const event = {
|
|
2344
|
+
roomId,
|
|
2345
|
+
message: {
|
|
2346
|
+
messageId: msgId,
|
|
2347
|
+
createdBy: this.userId,
|
|
2348
|
+
messageBodyType: "Inline",
|
|
2349
|
+
content: {
|
|
2350
|
+
text: message,
|
|
2351
|
+
binary: null
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
};
|
|
2355
|
+
this._emitter.emit("message", event);
|
|
2356
|
+
return { messageId: msgId };
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Send a text message to a room and return the service-assigned message id.
|
|
2360
|
+
*
|
|
2361
|
+
* The room must be one this client has created or joined. The sender also
|
|
2362
|
+
* observes the message through the `"message"` event.
|
|
2363
|
+
*
|
|
2364
|
+
* @param roomId - Target room.
|
|
2365
|
+
* @param message - Message text to send.
|
|
2366
|
+
* @param options - Optional `{ abortSignal }`.
|
|
2367
|
+
* @returns A {@link SendMessageResult} with the service-assigned `messageId`.
|
|
2368
|
+
*/
|
|
2369
|
+
async sendToRoom(roomId, message, options) {
|
|
2370
|
+
this.ensureStarted();
|
|
2371
|
+
const conversationId = this._rooms.get(roomId)?.defaultConversationId;
|
|
2372
|
+
if (!conversationId) {
|
|
2373
|
+
throw new ChatError(`Failed to sendToRoom, not found roomId ${roomId}`, ERRORS.UnknownRoom);
|
|
2374
|
+
}
|
|
2375
|
+
return await this.sendToConversation(conversationId, message, options);
|
|
2376
|
+
}
|
|
2377
|
+
// Internal: fetch a room's full wire shape (including its conversation id)
|
|
2378
|
+
// to populate the local cache. Public callers go through getRoomDetail().
|
|
2379
|
+
async fetchRoomDetail(roomId, options) {
|
|
2380
|
+
return this.invokeWithReturnType(
|
|
2381
|
+
INVOCATION_NAME.GET_ROOM,
|
|
2382
|
+
{ id: roomId, withMembers: options?.withMembers ?? false },
|
|
2383
|
+
"json",
|
|
2384
|
+
options
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2388
|
+
* Fetch the detailed view of a room.
|
|
2389
|
+
*
|
|
2390
|
+
* @param roomId - Room to query.
|
|
2391
|
+
* @param options - Optional `{ withMembers, abortSignal }`. Pass
|
|
2392
|
+
* `withMembers: true` to populate the returned `members` list; it is
|
|
2393
|
+
* left undefined otherwise.
|
|
2394
|
+
*/
|
|
2395
|
+
async getRoomDetail(roomId, options) {
|
|
2396
|
+
this.ensureStarted();
|
|
2397
|
+
return this.fetchRoomDetail(roomId, options);
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Create a room and its initial members. The current user is always
|
|
2401
|
+
* included in the resulting member list.
|
|
2402
|
+
*
|
|
2403
|
+
* @param title - Display title for the room.
|
|
2404
|
+
* @param members - Other user ids to invite. The caller is added
|
|
2405
|
+
* automatically; duplicates are de-duplicated.
|
|
2406
|
+
* @param options - Optional `{ roomId, abortSignal }`. Pass `roomId`
|
|
2407
|
+
* to choose an id explicitly; omit to let the service assign one.
|
|
2408
|
+
*/
|
|
2409
|
+
async createRoom(title, members, options) {
|
|
2410
|
+
this.ensureStarted();
|
|
2411
|
+
let roomDetails = {
|
|
2412
|
+
title,
|
|
2413
|
+
members: [.../* @__PURE__ */ new Set([...members, this.userId])]
|
|
2414
|
+
// deduplicate and add self
|
|
2415
|
+
};
|
|
2416
|
+
if (options?.roomId) {
|
|
2417
|
+
roomDetails = { ...roomDetails, roomId: options.roomId };
|
|
2418
|
+
}
|
|
2419
|
+
const roomInfo = await this.invokeWithReturnType(
|
|
2420
|
+
INVOCATION_NAME.CREATE_ROOM,
|
|
2421
|
+
roomDetails,
|
|
2422
|
+
"json",
|
|
2423
|
+
options
|
|
2424
|
+
);
|
|
2425
|
+
this._rooms.set(roomInfo.roomId, roomInfo);
|
|
2426
|
+
const event = { room: roomInfo };
|
|
2427
|
+
this._emitter.emit("room-joined", event);
|
|
2428
|
+
return roomInfo;
|
|
2429
|
+
}
|
|
2430
|
+
async manageRoomMember(request, options) {
|
|
2431
|
+
await this.invokeWithReturnType(INVOCATION_NAME.MANAGE_ROOM_MEMBER, request, "json", options);
|
|
2432
|
+
}
|
|
2433
|
+
async ensureRoomCached(roomId, options) {
|
|
2434
|
+
if (this._rooms.has(roomId)) {
|
|
2435
|
+
return;
|
|
2436
|
+
}
|
|
2437
|
+
const roomInfo = await this.fetchRoomDetail(roomId, options);
|
|
2438
|
+
this._rooms.set(roomId, roomInfo);
|
|
2439
|
+
}
|
|
2440
|
+
/** Add a user to a room. This is an admin operation where one user adds another user to a room. */
|
|
2441
|
+
async addUserToRoom(roomId, userId, options) {
|
|
2442
|
+
this.ensureStarted();
|
|
2443
|
+
const payload = { roomId, operation: "Add", userId };
|
|
2444
|
+
const isSelf = userId === this.userId;
|
|
2445
|
+
const shouldCacheRoomAfterSelfAdd = isSelf && !this._rooms.has(roomId);
|
|
2446
|
+
try {
|
|
2447
|
+
await this.manageRoomMember(payload, options);
|
|
2448
|
+
} catch (error) {
|
|
2449
|
+
if (!isSelf || !(error instanceof ChatError && error.code === ERRORS.UserAlreadyInRoom)) {
|
|
2450
|
+
throw error;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
if (shouldCacheRoomAfterSelfAdd) {
|
|
2454
|
+
await this.ensureRoomCached(roomId, options);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
/** Remove a user from a room. This is an admin operation where one user removes another user from a room. */
|
|
2458
|
+
async removeUserFromRoom(roomId, userId, options) {
|
|
2459
|
+
this.ensureStarted();
|
|
2460
|
+
const payload = { roomId, operation: "Delete", userId };
|
|
2461
|
+
await this.manageRoomMember(payload, options);
|
|
2462
|
+
if (userId === this.userId) {
|
|
2463
|
+
const roomInfo = this._rooms.get(roomId);
|
|
2464
|
+
if (roomInfo) {
|
|
2465
|
+
this._rooms.delete(roomId);
|
|
2466
|
+
const event = { roomId, title: roomInfo.title };
|
|
2467
|
+
this._emitter.emit("room-left", event);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* List messages in a room as a paged async iterator.
|
|
2473
|
+
*
|
|
2474
|
+
* The iterator transparently fetches additional pages from the
|
|
2475
|
+
* service as you iterate. For Teams-style infinite scrolling, drive
|
|
2476
|
+
* the iterator one page at a time via `byPage(...)`:
|
|
2477
|
+
*
|
|
2478
|
+
* @example Stream every message (e.g. for export or full sync):
|
|
2479
|
+
* ```ts
|
|
2480
|
+
* for await (const msg of client.listRoomMessages(roomId)) {
|
|
2481
|
+
* console.log(msg.content.text);
|
|
2482
|
+
* }
|
|
2483
|
+
* ```
|
|
2484
|
+
*
|
|
2485
|
+
* @example Load history one page at a time (Teams-style scroll-back):
|
|
2486
|
+
* ```ts
|
|
2487
|
+
* // Load up to 50 messages per page.
|
|
2488
|
+
* const pages = client.listRoomMessages(roomId).byPage({ maxPageSize: 50 });
|
|
2489
|
+
* const first = await pages.next();
|
|
2490
|
+
* displayMessages(first.value);
|
|
2491
|
+
* // later, when the user scrolls up:
|
|
2492
|
+
* const more = await pages.next();
|
|
2493
|
+
* displayMessages(more.value);
|
|
2494
|
+
* ```
|
|
2495
|
+
*
|
|
2496
|
+
* The room must be one this client has created or joined.
|
|
2497
|
+
*/
|
|
2498
|
+
listRoomMessages(roomId, options) {
|
|
2499
|
+
this.ensureStarted();
|
|
2500
|
+
const conversationId = this._rooms.get(roomId)?.defaultConversationId;
|
|
2501
|
+
if (!conversationId) {
|
|
2502
|
+
throw new ChatError(`Failed to listRoomMessages, not found roomId ${roomId}`, ERRORS.UnknownRoom);
|
|
2503
|
+
}
|
|
2504
|
+
const defaultPageSize = options?.maxPageSize ?? 100;
|
|
2505
|
+
const firstPageLink = {
|
|
2506
|
+
conversation: { conversationId },
|
|
2507
|
+
start: options?.startId ?? null,
|
|
2508
|
+
end: options?.endId ?? null,
|
|
2509
|
+
maxCount: defaultPageSize
|
|
2510
|
+
};
|
|
2511
|
+
const fetchPage = async (link, maxPageSize) => {
|
|
2512
|
+
const query = {
|
|
2513
|
+
...link,
|
|
2514
|
+
maxCount: maxPageSize ?? link.maxCount ?? defaultPageSize
|
|
2515
|
+
};
|
|
2516
|
+
const result = await this.invokeWithReturnType(
|
|
2517
|
+
INVOCATION_NAME.LIST_MESSAGES,
|
|
2518
|
+
query,
|
|
2519
|
+
"json",
|
|
2520
|
+
{ abortSignal: options?.abortSignal }
|
|
2521
|
+
);
|
|
2522
|
+
if (result.messages.length === 0) {
|
|
2523
|
+
return void 0;
|
|
2524
|
+
}
|
|
2525
|
+
return { page: result.messages, nextPageLink: result.nextQuery ?? void 0 };
|
|
2526
|
+
};
|
|
2527
|
+
return getPagedAsyncIterator({
|
|
2528
|
+
firstPageLink,
|
|
2529
|
+
getPage: (link, maxPageSize) => fetchPage(link, maxPageSize)
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
/** Cached rooms known to the client. */
|
|
2533
|
+
get rooms() {
|
|
2534
|
+
return Array.from(this._rooms.values());
|
|
2535
|
+
}
|
|
2536
|
+
/** Whether the current client has the room in its local joined-room cache. */
|
|
2537
|
+
hasJoinedRoom(roomId) {
|
|
2538
|
+
return this._rooms.has(roomId);
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* The chat-domain identity of this client, established by `start()`.
|
|
2542
|
+
*
|
|
2543
|
+
* @throws `ChatError` with code `NotStarted` if the client is not started.
|
|
2544
|
+
*/
|
|
2545
|
+
get userId() {
|
|
2546
|
+
if (!this._userId) {
|
|
2547
|
+
throw new ChatError("User ID is not set. Please call start() first.", ERRORS.NotStarted);
|
|
2548
|
+
}
|
|
2549
|
+
return this._userId;
|
|
2550
|
+
}
|
|
2551
|
+
on(event, listener) {
|
|
2552
|
+
this._emitter.on(event, listener);
|
|
2553
|
+
}
|
|
2554
|
+
off(event, listener) {
|
|
2555
|
+
this._emitter.off(event, listener);
|
|
2556
|
+
}
|
|
2557
|
+
/**
|
|
2558
|
+
* Stop the underlying connection and reset client state. Idempotent.
|
|
2559
|
+
*
|
|
2560
|
+
* After resolution the client returns to its initial state and may
|
|
2561
|
+
* be started again via `start()`. Callers that want the same identity
|
|
2562
|
+
* should keep their authentication source (URL or credential)
|
|
2563
|
+
* constant.
|
|
2564
|
+
*
|
|
2565
|
+
* Stopping is not cancellable: it tears down the transport and clears
|
|
2566
|
+
* local state, mirroring the underlying `WebPubSubClient.stop()`, which
|
|
2567
|
+
* takes no options.
|
|
2568
|
+
*/
|
|
2569
|
+
async stop() {
|
|
2570
|
+
const startPromise = this._startPromise;
|
|
2571
|
+
if (startPromise) {
|
|
2572
|
+
await startPromise.catch(() => void 0);
|
|
2573
|
+
}
|
|
2574
|
+
this._startPromise = void 0;
|
|
2575
|
+
this.resetState();
|
|
2576
|
+
await this.stopConnection();
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* Reset chat-domain state. Emits `"stopped"` exactly once per
|
|
2580
|
+
* started → not-started transition: if `_isStarted` was already
|
|
2581
|
+
* false on entry (e.g. the pre-start guard inside `startCore()` or
|
|
2582
|
+
* the post-failure rollback), no event fires.
|
|
2583
|
+
*/
|
|
2584
|
+
resetState() {
|
|
2585
|
+
const wasStarted = this._isStarted;
|
|
2586
|
+
this._isStarted = false;
|
|
2587
|
+
this._userId = void 0;
|
|
2588
|
+
this._rooms.clear();
|
|
2589
|
+
this._conversationIds.clear();
|
|
2590
|
+
if (wasStarted) {
|
|
2591
|
+
const stoppedEvent = {};
|
|
2592
|
+
this._emitter.emit("stopped", stoppedEvent);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
async stopConnection() {
|
|
2596
|
+
const connectionStoppedTCS = this._connectionStoppedTCS;
|
|
2597
|
+
if (!connectionStoppedTCS) {
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
if (!this._isConnectionStopping) {
|
|
2601
|
+
this._isConnectionStopping = true;
|
|
2602
|
+
try {
|
|
2603
|
+
this._connection.stop();
|
|
2604
|
+
} catch (err) {
|
|
2605
|
+
this._isConnectionStopping = false;
|
|
2606
|
+
throw err;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
await connectionStoppedTCS.wait();
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
|
|
2613
|
+
// src/index.ts
|
|
2614
|
+
var KnownChatErrorCode = ERRORS;
|
|
2615
|
+
export {
|
|
2616
|
+
ChatClient,
|
|
2617
|
+
ChatError,
|
|
2618
|
+
KnownChatErrorCode
|
|
2619
|
+
};
|
|
2620
|
+
//# sourceMappingURL=index.js.map
|