@telestack/db-sdk 1.0.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/README.md +103 -0
- package/dist/index.d.ts +214 -0
- package/dist/index.js +848 -0
- package/dist/telestack-sdk.js +4306 -0
- package/package.json +28 -0
|
@@ -0,0 +1,4306 @@
|
|
|
1
|
+
// node_modules/centrifuge/build/index.mjs
|
|
2
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) {
|
|
4
|
+
return value instanceof P ? value : new P(function(resolve) {
|
|
5
|
+
resolve(value);
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
return new (P || (P = Promise))(function(resolve, reject) {
|
|
9
|
+
function fulfilled(value) {
|
|
10
|
+
try {
|
|
11
|
+
step(generator.next(value));
|
|
12
|
+
} catch (e) {
|
|
13
|
+
reject(e);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function rejected(value) {
|
|
17
|
+
try {
|
|
18
|
+
step(generator["throw"](value));
|
|
19
|
+
} catch (e) {
|
|
20
|
+
reject(e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function step(result) {
|
|
24
|
+
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
25
|
+
}
|
|
26
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function getDefaultExportFromCjs(x) {
|
|
30
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
31
|
+
}
|
|
32
|
+
var events = { exports: {} };
|
|
33
|
+
var R = typeof Reflect === "object" ? Reflect : null;
|
|
34
|
+
var ReflectApply = R && typeof R.apply === "function" ? R.apply : function ReflectApply2(target, receiver, args) {
|
|
35
|
+
return Function.prototype.apply.call(target, receiver, args);
|
|
36
|
+
};
|
|
37
|
+
var ReflectOwnKeys;
|
|
38
|
+
if (R && typeof R.ownKeys === "function") {
|
|
39
|
+
ReflectOwnKeys = R.ownKeys;
|
|
40
|
+
} else if (Object.getOwnPropertySymbols) {
|
|
41
|
+
ReflectOwnKeys = function ReflectOwnKeys2(target) {
|
|
42
|
+
return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target));
|
|
43
|
+
};
|
|
44
|
+
} else {
|
|
45
|
+
ReflectOwnKeys = function ReflectOwnKeys2(target) {
|
|
46
|
+
return Object.getOwnPropertyNames(target);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function ProcessEmitWarning(warning) {
|
|
50
|
+
if (console && console.warn) console.warn(warning);
|
|
51
|
+
}
|
|
52
|
+
var NumberIsNaN = Number.isNaN || function NumberIsNaN2(value) {
|
|
53
|
+
return value !== value;
|
|
54
|
+
};
|
|
55
|
+
function EventEmitter() {
|
|
56
|
+
EventEmitter.init.call(this);
|
|
57
|
+
}
|
|
58
|
+
events.exports = EventEmitter;
|
|
59
|
+
events.exports.once = once2;
|
|
60
|
+
EventEmitter.EventEmitter = EventEmitter;
|
|
61
|
+
EventEmitter.prototype._events = void 0;
|
|
62
|
+
EventEmitter.prototype._eventsCount = 0;
|
|
63
|
+
EventEmitter.prototype._maxListeners = void 0;
|
|
64
|
+
var defaultMaxListeners = 10;
|
|
65
|
+
function checkListener(listener) {
|
|
66
|
+
if (typeof listener !== "function") {
|
|
67
|
+
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
Object.defineProperty(EventEmitter, "defaultMaxListeners", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
get: function() {
|
|
73
|
+
return defaultMaxListeners;
|
|
74
|
+
},
|
|
75
|
+
set: function(arg) {
|
|
76
|
+
if (typeof arg !== "number" || arg < 0 || NumberIsNaN(arg)) {
|
|
77
|
+
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + ".");
|
|
78
|
+
}
|
|
79
|
+
defaultMaxListeners = arg;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
EventEmitter.init = function() {
|
|
83
|
+
if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) {
|
|
84
|
+
this._events = /* @__PURE__ */ Object.create(null);
|
|
85
|
+
this._eventsCount = 0;
|
|
86
|
+
}
|
|
87
|
+
this._maxListeners = this._maxListeners || void 0;
|
|
88
|
+
};
|
|
89
|
+
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
|
|
90
|
+
if (typeof n !== "number" || n < 0 || NumberIsNaN(n)) {
|
|
91
|
+
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + ".");
|
|
92
|
+
}
|
|
93
|
+
this._maxListeners = n;
|
|
94
|
+
return this;
|
|
95
|
+
};
|
|
96
|
+
function _getMaxListeners(that) {
|
|
97
|
+
if (that._maxListeners === void 0)
|
|
98
|
+
return EventEmitter.defaultMaxListeners;
|
|
99
|
+
return that._maxListeners;
|
|
100
|
+
}
|
|
101
|
+
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
|
|
102
|
+
return _getMaxListeners(this);
|
|
103
|
+
};
|
|
104
|
+
EventEmitter.prototype.emit = function emit(type) {
|
|
105
|
+
var args = [];
|
|
106
|
+
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
|
|
107
|
+
var doError = type === "error";
|
|
108
|
+
var events2 = this._events;
|
|
109
|
+
if (events2 !== void 0)
|
|
110
|
+
doError = doError && events2.error === void 0;
|
|
111
|
+
else if (!doError)
|
|
112
|
+
return false;
|
|
113
|
+
if (doError) {
|
|
114
|
+
var er;
|
|
115
|
+
if (args.length > 0)
|
|
116
|
+
er = args[0];
|
|
117
|
+
if (er instanceof Error) {
|
|
118
|
+
throw er;
|
|
119
|
+
}
|
|
120
|
+
var err = new Error("Unhandled error." + (er ? " (" + er.message + ")" : ""));
|
|
121
|
+
err.context = er;
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
var handler = events2[type];
|
|
125
|
+
if (handler === void 0)
|
|
126
|
+
return false;
|
|
127
|
+
if (typeof handler === "function") {
|
|
128
|
+
ReflectApply(handler, this, args);
|
|
129
|
+
} else {
|
|
130
|
+
var len = handler.length;
|
|
131
|
+
var listeners2 = arrayClone(handler, len);
|
|
132
|
+
for (var i = 0; i < len; ++i)
|
|
133
|
+
ReflectApply(listeners2[i], this, args);
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
};
|
|
137
|
+
function _addListener(target, type, listener, prepend) {
|
|
138
|
+
var m;
|
|
139
|
+
var events2;
|
|
140
|
+
var existing;
|
|
141
|
+
checkListener(listener);
|
|
142
|
+
events2 = target._events;
|
|
143
|
+
if (events2 === void 0) {
|
|
144
|
+
events2 = target._events = /* @__PURE__ */ Object.create(null);
|
|
145
|
+
target._eventsCount = 0;
|
|
146
|
+
} else {
|
|
147
|
+
if (events2.newListener !== void 0) {
|
|
148
|
+
target.emit(
|
|
149
|
+
"newListener",
|
|
150
|
+
type,
|
|
151
|
+
listener.listener ? listener.listener : listener
|
|
152
|
+
);
|
|
153
|
+
events2 = target._events;
|
|
154
|
+
}
|
|
155
|
+
existing = events2[type];
|
|
156
|
+
}
|
|
157
|
+
if (existing === void 0) {
|
|
158
|
+
existing = events2[type] = listener;
|
|
159
|
+
++target._eventsCount;
|
|
160
|
+
} else {
|
|
161
|
+
if (typeof existing === "function") {
|
|
162
|
+
existing = events2[type] = prepend ? [listener, existing] : [existing, listener];
|
|
163
|
+
} else if (prepend) {
|
|
164
|
+
existing.unshift(listener);
|
|
165
|
+
} else {
|
|
166
|
+
existing.push(listener);
|
|
167
|
+
}
|
|
168
|
+
m = _getMaxListeners(target);
|
|
169
|
+
if (m > 0 && existing.length > m && !existing.warned) {
|
|
170
|
+
existing.warned = true;
|
|
171
|
+
var w = new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type) + " listeners added. Use emitter.setMaxListeners() to increase limit");
|
|
172
|
+
w.name = "MaxListenersExceededWarning";
|
|
173
|
+
w.emitter = target;
|
|
174
|
+
w.type = type;
|
|
175
|
+
w.count = existing.length;
|
|
176
|
+
ProcessEmitWarning(w);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return target;
|
|
180
|
+
}
|
|
181
|
+
EventEmitter.prototype.addListener = function addListener(type, listener) {
|
|
182
|
+
return _addListener(this, type, listener, false);
|
|
183
|
+
};
|
|
184
|
+
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
185
|
+
EventEmitter.prototype.prependListener = function prependListener(type, listener) {
|
|
186
|
+
return _addListener(this, type, listener, true);
|
|
187
|
+
};
|
|
188
|
+
function onceWrapper() {
|
|
189
|
+
if (!this.fired) {
|
|
190
|
+
this.target.removeListener(this.type, this.wrapFn);
|
|
191
|
+
this.fired = true;
|
|
192
|
+
if (arguments.length === 0)
|
|
193
|
+
return this.listener.call(this.target);
|
|
194
|
+
return this.listener.apply(this.target, arguments);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function _onceWrap(target, type, listener) {
|
|
198
|
+
var state = { fired: false, wrapFn: void 0, target, type, listener };
|
|
199
|
+
var wrapped = onceWrapper.bind(state);
|
|
200
|
+
wrapped.listener = listener;
|
|
201
|
+
state.wrapFn = wrapped;
|
|
202
|
+
return wrapped;
|
|
203
|
+
}
|
|
204
|
+
EventEmitter.prototype.once = function once(type, listener) {
|
|
205
|
+
checkListener(listener);
|
|
206
|
+
this.on(type, _onceWrap(this, type, listener));
|
|
207
|
+
return this;
|
|
208
|
+
};
|
|
209
|
+
EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) {
|
|
210
|
+
checkListener(listener);
|
|
211
|
+
this.prependListener(type, _onceWrap(this, type, listener));
|
|
212
|
+
return this;
|
|
213
|
+
};
|
|
214
|
+
EventEmitter.prototype.removeListener = function removeListener(type, listener) {
|
|
215
|
+
var list, events2, position, i, originalListener;
|
|
216
|
+
checkListener(listener);
|
|
217
|
+
events2 = this._events;
|
|
218
|
+
if (events2 === void 0)
|
|
219
|
+
return this;
|
|
220
|
+
list = events2[type];
|
|
221
|
+
if (list === void 0)
|
|
222
|
+
return this;
|
|
223
|
+
if (list === listener || list.listener === listener) {
|
|
224
|
+
if (--this._eventsCount === 0)
|
|
225
|
+
this._events = /* @__PURE__ */ Object.create(null);
|
|
226
|
+
else {
|
|
227
|
+
delete events2[type];
|
|
228
|
+
if (events2.removeListener)
|
|
229
|
+
this.emit("removeListener", type, list.listener || listener);
|
|
230
|
+
}
|
|
231
|
+
} else if (typeof list !== "function") {
|
|
232
|
+
position = -1;
|
|
233
|
+
for (i = list.length - 1; i >= 0; i--) {
|
|
234
|
+
if (list[i] === listener || list[i].listener === listener) {
|
|
235
|
+
originalListener = list[i].listener;
|
|
236
|
+
position = i;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (position < 0)
|
|
241
|
+
return this;
|
|
242
|
+
if (position === 0)
|
|
243
|
+
list.shift();
|
|
244
|
+
else {
|
|
245
|
+
spliceOne(list, position);
|
|
246
|
+
}
|
|
247
|
+
if (list.length === 1)
|
|
248
|
+
events2[type] = list[0];
|
|
249
|
+
if (events2.removeListener !== void 0)
|
|
250
|
+
this.emit("removeListener", type, originalListener || listener);
|
|
251
|
+
}
|
|
252
|
+
return this;
|
|
253
|
+
};
|
|
254
|
+
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
|
255
|
+
EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
|
|
256
|
+
var listeners2, events2, i;
|
|
257
|
+
events2 = this._events;
|
|
258
|
+
if (events2 === void 0)
|
|
259
|
+
return this;
|
|
260
|
+
if (events2.removeListener === void 0) {
|
|
261
|
+
if (arguments.length === 0) {
|
|
262
|
+
this._events = /* @__PURE__ */ Object.create(null);
|
|
263
|
+
this._eventsCount = 0;
|
|
264
|
+
} else if (events2[type] !== void 0) {
|
|
265
|
+
if (--this._eventsCount === 0)
|
|
266
|
+
this._events = /* @__PURE__ */ Object.create(null);
|
|
267
|
+
else
|
|
268
|
+
delete events2[type];
|
|
269
|
+
}
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
if (arguments.length === 0) {
|
|
273
|
+
var keys = Object.keys(events2);
|
|
274
|
+
var key;
|
|
275
|
+
for (i = 0; i < keys.length; ++i) {
|
|
276
|
+
key = keys[i];
|
|
277
|
+
if (key === "removeListener") continue;
|
|
278
|
+
this.removeAllListeners(key);
|
|
279
|
+
}
|
|
280
|
+
this.removeAllListeners("removeListener");
|
|
281
|
+
this._events = /* @__PURE__ */ Object.create(null);
|
|
282
|
+
this._eventsCount = 0;
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
listeners2 = events2[type];
|
|
286
|
+
if (typeof listeners2 === "function") {
|
|
287
|
+
this.removeListener(type, listeners2);
|
|
288
|
+
} else if (listeners2 !== void 0) {
|
|
289
|
+
for (i = listeners2.length - 1; i >= 0; i--) {
|
|
290
|
+
this.removeListener(type, listeners2[i]);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return this;
|
|
294
|
+
};
|
|
295
|
+
function _listeners(target, type, unwrap) {
|
|
296
|
+
var events2 = target._events;
|
|
297
|
+
if (events2 === void 0)
|
|
298
|
+
return [];
|
|
299
|
+
var evlistener = events2[type];
|
|
300
|
+
if (evlistener === void 0)
|
|
301
|
+
return [];
|
|
302
|
+
if (typeof evlistener === "function")
|
|
303
|
+
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
|
|
304
|
+
return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
|
|
305
|
+
}
|
|
306
|
+
EventEmitter.prototype.listeners = function listeners(type) {
|
|
307
|
+
return _listeners(this, type, true);
|
|
308
|
+
};
|
|
309
|
+
EventEmitter.prototype.rawListeners = function rawListeners(type) {
|
|
310
|
+
return _listeners(this, type, false);
|
|
311
|
+
};
|
|
312
|
+
EventEmitter.listenerCount = function(emitter, type) {
|
|
313
|
+
if (typeof emitter.listenerCount === "function") {
|
|
314
|
+
return emitter.listenerCount(type);
|
|
315
|
+
} else {
|
|
316
|
+
return listenerCount.call(emitter, type);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
EventEmitter.prototype.listenerCount = listenerCount;
|
|
320
|
+
function listenerCount(type) {
|
|
321
|
+
var events2 = this._events;
|
|
322
|
+
if (events2 !== void 0) {
|
|
323
|
+
var evlistener = events2[type];
|
|
324
|
+
if (typeof evlistener === "function") {
|
|
325
|
+
return 1;
|
|
326
|
+
} else if (evlistener !== void 0) {
|
|
327
|
+
return evlistener.length;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
EventEmitter.prototype.eventNames = function eventNames() {
|
|
333
|
+
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
|
|
334
|
+
};
|
|
335
|
+
function arrayClone(arr, n) {
|
|
336
|
+
var copy = new Array(n);
|
|
337
|
+
for (var i = 0; i < n; ++i)
|
|
338
|
+
copy[i] = arr[i];
|
|
339
|
+
return copy;
|
|
340
|
+
}
|
|
341
|
+
function spliceOne(list, index) {
|
|
342
|
+
for (; index + 1 < list.length; index++)
|
|
343
|
+
list[index] = list[index + 1];
|
|
344
|
+
list.pop();
|
|
345
|
+
}
|
|
346
|
+
function unwrapListeners(arr) {
|
|
347
|
+
var ret = new Array(arr.length);
|
|
348
|
+
for (var i = 0; i < ret.length; ++i) {
|
|
349
|
+
ret[i] = arr[i].listener || arr[i];
|
|
350
|
+
}
|
|
351
|
+
return ret;
|
|
352
|
+
}
|
|
353
|
+
function once2(emitter, name) {
|
|
354
|
+
return new Promise(function(resolve, reject) {
|
|
355
|
+
function errorListener(err) {
|
|
356
|
+
emitter.removeListener(name, resolver);
|
|
357
|
+
reject(err);
|
|
358
|
+
}
|
|
359
|
+
function resolver() {
|
|
360
|
+
if (typeof emitter.removeListener === "function") {
|
|
361
|
+
emitter.removeListener("error", errorListener);
|
|
362
|
+
}
|
|
363
|
+
resolve([].slice.call(arguments));
|
|
364
|
+
}
|
|
365
|
+
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
|
|
366
|
+
if (name !== "error") {
|
|
367
|
+
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
|
|
372
|
+
if (typeof emitter.on === "function") {
|
|
373
|
+
eventTargetAgnosticAddListener(emitter, "error", handler, flags);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
|
|
377
|
+
if (typeof emitter.on === "function") {
|
|
378
|
+
if (flags.once) {
|
|
379
|
+
emitter.once(name, listener);
|
|
380
|
+
} else {
|
|
381
|
+
emitter.on(name, listener);
|
|
382
|
+
}
|
|
383
|
+
} else if (typeof emitter.addEventListener === "function") {
|
|
384
|
+
emitter.addEventListener(name, function wrapListener(arg) {
|
|
385
|
+
if (flags.once) {
|
|
386
|
+
emitter.removeEventListener(name, wrapListener);
|
|
387
|
+
}
|
|
388
|
+
listener(arg);
|
|
389
|
+
});
|
|
390
|
+
} else {
|
|
391
|
+
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
var eventsExports = events.exports;
|
|
395
|
+
var EventEmitter$1 = /* @__PURE__ */ getDefaultExportFromCjs(eventsExports);
|
|
396
|
+
var errorCodes;
|
|
397
|
+
(function(errorCodes2) {
|
|
398
|
+
errorCodes2[errorCodes2["timeout"] = 1] = "timeout";
|
|
399
|
+
errorCodes2[errorCodes2["transportClosed"] = 2] = "transportClosed";
|
|
400
|
+
errorCodes2[errorCodes2["clientDisconnected"] = 3] = "clientDisconnected";
|
|
401
|
+
errorCodes2[errorCodes2["clientClosed"] = 4] = "clientClosed";
|
|
402
|
+
errorCodes2[errorCodes2["clientConnectToken"] = 5] = "clientConnectToken";
|
|
403
|
+
errorCodes2[errorCodes2["clientRefreshToken"] = 6] = "clientRefreshToken";
|
|
404
|
+
errorCodes2[errorCodes2["subscriptionUnsubscribed"] = 7] = "subscriptionUnsubscribed";
|
|
405
|
+
errorCodes2[errorCodes2["subscriptionSubscribeToken"] = 8] = "subscriptionSubscribeToken";
|
|
406
|
+
errorCodes2[errorCodes2["subscriptionRefreshToken"] = 9] = "subscriptionRefreshToken";
|
|
407
|
+
errorCodes2[errorCodes2["transportWriteError"] = 10] = "transportWriteError";
|
|
408
|
+
errorCodes2[errorCodes2["connectionClosed"] = 11] = "connectionClosed";
|
|
409
|
+
errorCodes2[errorCodes2["badConfiguration"] = 12] = "badConfiguration";
|
|
410
|
+
})(errorCodes || (errorCodes = {}));
|
|
411
|
+
var connectingCodes;
|
|
412
|
+
(function(connectingCodes2) {
|
|
413
|
+
connectingCodes2[connectingCodes2["connectCalled"] = 0] = "connectCalled";
|
|
414
|
+
connectingCodes2[connectingCodes2["transportClosed"] = 1] = "transportClosed";
|
|
415
|
+
connectingCodes2[connectingCodes2["noPing"] = 2] = "noPing";
|
|
416
|
+
connectingCodes2[connectingCodes2["subscribeTimeout"] = 3] = "subscribeTimeout";
|
|
417
|
+
connectingCodes2[connectingCodes2["unsubscribeError"] = 4] = "unsubscribeError";
|
|
418
|
+
})(connectingCodes || (connectingCodes = {}));
|
|
419
|
+
var disconnectedCodes;
|
|
420
|
+
(function(disconnectedCodes2) {
|
|
421
|
+
disconnectedCodes2[disconnectedCodes2["disconnectCalled"] = 0] = "disconnectCalled";
|
|
422
|
+
disconnectedCodes2[disconnectedCodes2["unauthorized"] = 1] = "unauthorized";
|
|
423
|
+
disconnectedCodes2[disconnectedCodes2["badProtocol"] = 2] = "badProtocol";
|
|
424
|
+
disconnectedCodes2[disconnectedCodes2["messageSizeLimit"] = 3] = "messageSizeLimit";
|
|
425
|
+
})(disconnectedCodes || (disconnectedCodes = {}));
|
|
426
|
+
var subscribingCodes;
|
|
427
|
+
(function(subscribingCodes2) {
|
|
428
|
+
subscribingCodes2[subscribingCodes2["subscribeCalled"] = 0] = "subscribeCalled";
|
|
429
|
+
subscribingCodes2[subscribingCodes2["transportClosed"] = 1] = "transportClosed";
|
|
430
|
+
})(subscribingCodes || (subscribingCodes = {}));
|
|
431
|
+
var unsubscribedCodes;
|
|
432
|
+
(function(unsubscribedCodes2) {
|
|
433
|
+
unsubscribedCodes2[unsubscribedCodes2["unsubscribeCalled"] = 0] = "unsubscribeCalled";
|
|
434
|
+
unsubscribedCodes2[unsubscribedCodes2["unauthorized"] = 1] = "unauthorized";
|
|
435
|
+
unsubscribedCodes2[unsubscribedCodes2["clientClosed"] = 2] = "clientClosed";
|
|
436
|
+
})(unsubscribedCodes || (unsubscribedCodes = {}));
|
|
437
|
+
var subscriptionFlags;
|
|
438
|
+
(function(subscriptionFlags2) {
|
|
439
|
+
subscriptionFlags2[subscriptionFlags2["channelCompaction"] = 1] = "channelCompaction";
|
|
440
|
+
})(subscriptionFlags || (subscriptionFlags = {}));
|
|
441
|
+
var State;
|
|
442
|
+
(function(State2) {
|
|
443
|
+
State2["Disconnected"] = "disconnected";
|
|
444
|
+
State2["Connecting"] = "connecting";
|
|
445
|
+
State2["Connected"] = "connected";
|
|
446
|
+
})(State || (State = {}));
|
|
447
|
+
var SubscriptionState;
|
|
448
|
+
(function(SubscriptionState2) {
|
|
449
|
+
SubscriptionState2["Unsubscribed"] = "unsubscribed";
|
|
450
|
+
SubscriptionState2["Subscribing"] = "subscribing";
|
|
451
|
+
SubscriptionState2["Subscribed"] = "subscribed";
|
|
452
|
+
})(SubscriptionState || (SubscriptionState = {}));
|
|
453
|
+
function startsWith(value, prefix) {
|
|
454
|
+
return value.lastIndexOf(prefix, 0) === 0;
|
|
455
|
+
}
|
|
456
|
+
function isFunction(value) {
|
|
457
|
+
if (value === void 0 || value === null) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
return typeof value === "function";
|
|
461
|
+
}
|
|
462
|
+
function log(level, args) {
|
|
463
|
+
if (globalThis.console) {
|
|
464
|
+
const logger = globalThis.console[level];
|
|
465
|
+
if (isFunction(logger)) {
|
|
466
|
+
logger.apply(globalThis.console, args);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function randomInt(min, max) {
|
|
471
|
+
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
472
|
+
}
|
|
473
|
+
function backoff(step, min, max) {
|
|
474
|
+
if (step > 31) {
|
|
475
|
+
step = 31;
|
|
476
|
+
}
|
|
477
|
+
const interval = randomInt(0, Math.min(max, min * Math.pow(2, step)));
|
|
478
|
+
return Math.min(max, min + interval);
|
|
479
|
+
}
|
|
480
|
+
function errorExists(data) {
|
|
481
|
+
return "error" in data && data.error !== null;
|
|
482
|
+
}
|
|
483
|
+
function ttlMilliseconds(ttl) {
|
|
484
|
+
return Math.min(ttl * 1e3, 2147483647);
|
|
485
|
+
}
|
|
486
|
+
var Subscription = class extends EventEmitter$1 {
|
|
487
|
+
/** Subscription constructor should not be used directly, create subscriptions using Client method. */
|
|
488
|
+
constructor(centrifuge, channel, options) {
|
|
489
|
+
super();
|
|
490
|
+
this._resubscribeTimeout = null;
|
|
491
|
+
this._refreshTimeout = null;
|
|
492
|
+
this.channel = channel;
|
|
493
|
+
this.state = SubscriptionState.Unsubscribed;
|
|
494
|
+
this._centrifuge = centrifuge;
|
|
495
|
+
this._token = "";
|
|
496
|
+
this._getToken = null;
|
|
497
|
+
this._data = null;
|
|
498
|
+
this._getData = null;
|
|
499
|
+
this._recover = false;
|
|
500
|
+
this._offset = null;
|
|
501
|
+
this._epoch = null;
|
|
502
|
+
this._id = 0;
|
|
503
|
+
this._recoverable = false;
|
|
504
|
+
this._positioned = false;
|
|
505
|
+
this._joinLeave = false;
|
|
506
|
+
this._minResubscribeDelay = 500;
|
|
507
|
+
this._maxResubscribeDelay = 2e4;
|
|
508
|
+
this._resubscribeTimeout = null;
|
|
509
|
+
this._resubscribeAttempts = 0;
|
|
510
|
+
this._promises = {};
|
|
511
|
+
this._promiseId = 0;
|
|
512
|
+
this._inflight = false;
|
|
513
|
+
this._refreshTimeout = null;
|
|
514
|
+
this._delta = "";
|
|
515
|
+
this._delta_negotiated = false;
|
|
516
|
+
this._tagsFilter = null;
|
|
517
|
+
this._prevValue = null;
|
|
518
|
+
this._unsubPromise = Promise.resolve();
|
|
519
|
+
this._setOptions(options);
|
|
520
|
+
if (this._centrifuge._debugEnabled) {
|
|
521
|
+
this.on("state", (ctx) => {
|
|
522
|
+
this._debug("subscription state", channel, ctx.oldState, "->", ctx.newState);
|
|
523
|
+
});
|
|
524
|
+
this.on("error", (ctx) => {
|
|
525
|
+
this._debug("subscription error", channel, ctx);
|
|
526
|
+
});
|
|
527
|
+
} else {
|
|
528
|
+
this.on("error", function() {
|
|
529
|
+
Function.prototype();
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/** ready returns a Promise which resolves upon subscription goes to Subscribed
|
|
534
|
+
* state and rejects in case of subscription goes to Unsubscribed state.
|
|
535
|
+
* Optional timeout can be passed.*/
|
|
536
|
+
ready(timeout) {
|
|
537
|
+
if (this.state === SubscriptionState.Unsubscribed) {
|
|
538
|
+
return Promise.reject({ code: errorCodes.subscriptionUnsubscribed, message: this.state });
|
|
539
|
+
}
|
|
540
|
+
if (this.state === SubscriptionState.Subscribed) {
|
|
541
|
+
return Promise.resolve();
|
|
542
|
+
}
|
|
543
|
+
return new Promise((res, rej) => {
|
|
544
|
+
const ctx = {
|
|
545
|
+
resolve: res,
|
|
546
|
+
reject: rej
|
|
547
|
+
};
|
|
548
|
+
if (timeout) {
|
|
549
|
+
ctx.timeout = setTimeout(function() {
|
|
550
|
+
rej({ code: errorCodes.timeout, message: "timeout" });
|
|
551
|
+
}, timeout);
|
|
552
|
+
}
|
|
553
|
+
this._promises[this._nextPromiseId()] = ctx;
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
/** subscribe to a channel.*/
|
|
557
|
+
subscribe() {
|
|
558
|
+
if (this._isSubscribed()) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
this._resubscribeAttempts = 0;
|
|
562
|
+
this._setSubscribing(subscribingCodes.subscribeCalled, "subscribe called");
|
|
563
|
+
}
|
|
564
|
+
/** unsubscribe from a channel, keeping position state.*/
|
|
565
|
+
unsubscribe() {
|
|
566
|
+
this._unsubPromise = this._setUnsubscribed(unsubscribedCodes.unsubscribeCalled, "unsubscribe called", true);
|
|
567
|
+
}
|
|
568
|
+
/** publish data to a channel.*/
|
|
569
|
+
publish(data) {
|
|
570
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
571
|
+
yield this._methodCall();
|
|
572
|
+
return this._centrifuge.publish(this.channel, data);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
/** get online presence for a channel.*/
|
|
576
|
+
presence() {
|
|
577
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
578
|
+
yield this._methodCall();
|
|
579
|
+
return this._centrifuge.presence(this.channel);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
/** presence stats for a channel (num clients and unique users).*/
|
|
583
|
+
presenceStats() {
|
|
584
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
585
|
+
yield this._methodCall();
|
|
586
|
+
return this._centrifuge.presenceStats(this.channel);
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
/** history for a channel. By default it does not return publications (only current
|
|
590
|
+
* StreamPosition data) – provide an explicit limit > 0 to load publications.*/
|
|
591
|
+
history(opts) {
|
|
592
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
593
|
+
yield this._methodCall();
|
|
594
|
+
return this._centrifuge.history(this.channel, opts);
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Sets server-side tags filter for the subscription.
|
|
599
|
+
* This only applies on the next subscription attempt, not the current one.
|
|
600
|
+
* Cannot be used together with delta option.
|
|
601
|
+
*
|
|
602
|
+
* @param tagsFilter - Filter configuration object or null to remove filter
|
|
603
|
+
* @throws {Error} If both delta and tagsFilter are configured
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```typescript
|
|
607
|
+
* // Simple equality filter
|
|
608
|
+
* sub.setTagsFilter({
|
|
609
|
+
* key: 'ticker',
|
|
610
|
+
* cmp: 'eq',
|
|
611
|
+
* val: 'BTC'
|
|
612
|
+
* });
|
|
613
|
+
* ```
|
|
614
|
+
*
|
|
615
|
+
* @example
|
|
616
|
+
* ```typescript
|
|
617
|
+
* // Complex filter with logical operators
|
|
618
|
+
* sub.setTagsFilter({
|
|
619
|
+
* op: 'and',
|
|
620
|
+
* nodes: [
|
|
621
|
+
* { key: 'ticker', cmp: 'eq', val: 'BTC' },
|
|
622
|
+
* { key: 'price', cmp: 'gt', val: '50000' }
|
|
623
|
+
* ]
|
|
624
|
+
* });
|
|
625
|
+
* ```
|
|
626
|
+
*
|
|
627
|
+
* @example
|
|
628
|
+
* ```typescript
|
|
629
|
+
* // Filter with IN operator
|
|
630
|
+
* sub.setTagsFilter({
|
|
631
|
+
* key: 'ticker',
|
|
632
|
+
* cmp: 'in',
|
|
633
|
+
* vals: ['BTC', 'ETH', 'SOL']
|
|
634
|
+
* });
|
|
635
|
+
* ```
|
|
636
|
+
*/
|
|
637
|
+
setTagsFilter(tagsFilter) {
|
|
638
|
+
if (tagsFilter && this._delta) {
|
|
639
|
+
throw new Error("cannot use delta and tagsFilter together");
|
|
640
|
+
}
|
|
641
|
+
this._tagsFilter = tagsFilter;
|
|
642
|
+
}
|
|
643
|
+
/** setData allows setting subscription data. This only applied on the next subscription attempt,
|
|
644
|
+
* Note that if getData callback is configured, it will override this value during resubscriptions. */
|
|
645
|
+
setData(data) {
|
|
646
|
+
this._data = data;
|
|
647
|
+
}
|
|
648
|
+
_methodCall() {
|
|
649
|
+
if (this._isSubscribed()) {
|
|
650
|
+
return Promise.resolve();
|
|
651
|
+
}
|
|
652
|
+
if (this._isUnsubscribed()) {
|
|
653
|
+
return Promise.reject({
|
|
654
|
+
code: errorCodes.subscriptionUnsubscribed,
|
|
655
|
+
message: this.state
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
return new Promise((resolve, reject) => {
|
|
659
|
+
const timeoutDuration = this._centrifuge._config.timeout;
|
|
660
|
+
const timeout = setTimeout(() => {
|
|
661
|
+
reject({ code: errorCodes.timeout, message: "timeout" });
|
|
662
|
+
}, timeoutDuration);
|
|
663
|
+
this._promises[this._nextPromiseId()] = {
|
|
664
|
+
timeout,
|
|
665
|
+
resolve,
|
|
666
|
+
reject
|
|
667
|
+
};
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
_nextPromiseId() {
|
|
671
|
+
return ++this._promiseId;
|
|
672
|
+
}
|
|
673
|
+
_needRecover() {
|
|
674
|
+
return this._recover === true;
|
|
675
|
+
}
|
|
676
|
+
_isUnsubscribed() {
|
|
677
|
+
return this.state === SubscriptionState.Unsubscribed;
|
|
678
|
+
}
|
|
679
|
+
_isSubscribing() {
|
|
680
|
+
return this.state === SubscriptionState.Subscribing;
|
|
681
|
+
}
|
|
682
|
+
_isSubscribed() {
|
|
683
|
+
return this.state === SubscriptionState.Subscribed;
|
|
684
|
+
}
|
|
685
|
+
_setState(newState) {
|
|
686
|
+
if (this.state !== newState) {
|
|
687
|
+
const oldState = this.state;
|
|
688
|
+
this.state = newState;
|
|
689
|
+
this.emit("state", { newState, oldState, channel: this.channel });
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
_usesToken() {
|
|
695
|
+
return this._token !== "" || this._getToken !== null;
|
|
696
|
+
}
|
|
697
|
+
_clearSubscribingState() {
|
|
698
|
+
this._resubscribeAttempts = 0;
|
|
699
|
+
this._clearResubscribeTimeout();
|
|
700
|
+
}
|
|
701
|
+
_clearSubscribedState() {
|
|
702
|
+
this._clearRefreshTimeout();
|
|
703
|
+
}
|
|
704
|
+
_setSubscribed(result) {
|
|
705
|
+
if (!this._isSubscribing()) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
this._clearSubscribingState();
|
|
709
|
+
if (result.id) {
|
|
710
|
+
this._id = result.id;
|
|
711
|
+
}
|
|
712
|
+
if (result.recoverable) {
|
|
713
|
+
this._recover = true;
|
|
714
|
+
this._offset = result.offset || 0;
|
|
715
|
+
this._epoch = result.epoch || "";
|
|
716
|
+
}
|
|
717
|
+
if (result.delta) {
|
|
718
|
+
this._delta_negotiated = true;
|
|
719
|
+
} else {
|
|
720
|
+
this._delta_negotiated = false;
|
|
721
|
+
}
|
|
722
|
+
this._setState(SubscriptionState.Subscribed);
|
|
723
|
+
const ctx = this._centrifuge._getSubscribeContext(this.channel, result);
|
|
724
|
+
this.emit("subscribed", ctx);
|
|
725
|
+
this._resolvePromises();
|
|
726
|
+
const pubs = result.publications;
|
|
727
|
+
if (pubs && pubs.length > 0) {
|
|
728
|
+
for (const i in pubs) {
|
|
729
|
+
if (!pubs.hasOwnProperty(i)) {
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
this._handlePublication(pubs[i]);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (result.expires === true) {
|
|
736
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), ttlMilliseconds(result.ttl));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
_setSubscribing(code, reason) {
|
|
740
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
741
|
+
if (this._isSubscribing()) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (this._isSubscribed()) {
|
|
745
|
+
this._clearSubscribedState();
|
|
746
|
+
}
|
|
747
|
+
if (this._setState(SubscriptionState.Subscribing)) {
|
|
748
|
+
this.emit("subscribing", { channel: this.channel, code, reason });
|
|
749
|
+
}
|
|
750
|
+
if (this._centrifuge._transport && this._centrifuge._transport.emulation()) {
|
|
751
|
+
yield this._unsubPromise;
|
|
752
|
+
}
|
|
753
|
+
if (!this._isSubscribing()) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
this._subscribe();
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
_subscribe() {
|
|
760
|
+
this._debug("subscribing on", this.channel);
|
|
761
|
+
if (!this._isTransportOpen()) {
|
|
762
|
+
this._debug("delay subscribe on", this.channel, "till connected");
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
if (this._inflight) {
|
|
766
|
+
return null;
|
|
767
|
+
}
|
|
768
|
+
this._inflight = true;
|
|
769
|
+
if (this._canSubscribeWithoutGettingToken()) {
|
|
770
|
+
return this._subscribeWithoutToken();
|
|
771
|
+
}
|
|
772
|
+
this._getSubscriptionToken().then((token) => this._handleTokenResponse(token)).catch((e) => this._handleTokenError(e));
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
_isTransportOpen() {
|
|
776
|
+
return this._centrifuge._transportIsOpen;
|
|
777
|
+
}
|
|
778
|
+
_canSubscribeWithoutGettingToken() {
|
|
779
|
+
return !this._usesToken() || !!this._token;
|
|
780
|
+
}
|
|
781
|
+
_subscribeWithoutToken() {
|
|
782
|
+
if (this._getData) {
|
|
783
|
+
this._getDataAndSubscribe(this._token);
|
|
784
|
+
return null;
|
|
785
|
+
} else {
|
|
786
|
+
return this._sendSubscribe(this._token);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
_getDataAndSubscribe(token) {
|
|
790
|
+
if (!this._getData) {
|
|
791
|
+
this._inflight = false;
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
this._getData({ channel: this.channel }).then((data) => {
|
|
795
|
+
if (!this._isSubscribing()) {
|
|
796
|
+
this._inflight = false;
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
this._data = data;
|
|
800
|
+
this._sendSubscribe(token);
|
|
801
|
+
}).catch((e) => this._handleGetDataError(e));
|
|
802
|
+
}
|
|
803
|
+
_handleGetDataError(error) {
|
|
804
|
+
if (!this._isSubscribing()) {
|
|
805
|
+
this._inflight = false;
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (error instanceof UnauthorizedError) {
|
|
809
|
+
this._inflight = false;
|
|
810
|
+
this._failUnauthorized();
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
this.emit("error", {
|
|
814
|
+
type: "subscribeData",
|
|
815
|
+
channel: this.channel,
|
|
816
|
+
error: {
|
|
817
|
+
code: errorCodes.badConfiguration,
|
|
818
|
+
message: (error === null || error === void 0 ? void 0 : error.toString()) || ""
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
this._inflight = false;
|
|
822
|
+
this._scheduleResubscribe();
|
|
823
|
+
}
|
|
824
|
+
_handleTokenResponse(token) {
|
|
825
|
+
if (!this._isSubscribing()) {
|
|
826
|
+
this._inflight = false;
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (!token) {
|
|
830
|
+
this._inflight = false;
|
|
831
|
+
this._failUnauthorized();
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
this._token = token;
|
|
835
|
+
if (this._getData) {
|
|
836
|
+
this._getDataAndSubscribe(token);
|
|
837
|
+
} else {
|
|
838
|
+
this._sendSubscribe(token);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
_handleTokenError(error) {
|
|
842
|
+
if (!this._isSubscribing()) {
|
|
843
|
+
this._inflight = false;
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (error instanceof UnauthorizedError) {
|
|
847
|
+
this._inflight = false;
|
|
848
|
+
this._failUnauthorized();
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
this.emit("error", {
|
|
852
|
+
type: "subscribeToken",
|
|
853
|
+
channel: this.channel,
|
|
854
|
+
error: {
|
|
855
|
+
code: errorCodes.subscriptionSubscribeToken,
|
|
856
|
+
message: (error === null || error === void 0 ? void 0 : error.toString()) || ""
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
this._inflight = false;
|
|
860
|
+
this._scheduleResubscribe();
|
|
861
|
+
}
|
|
862
|
+
_sendSubscribe(token) {
|
|
863
|
+
if (!this._isTransportOpen()) {
|
|
864
|
+
this._inflight = false;
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
const cmd = this._buildSubscribeCommand(token);
|
|
868
|
+
this._centrifuge._call(cmd).then((resolveCtx) => {
|
|
869
|
+
this._inflight = false;
|
|
870
|
+
const result = resolveCtx.reply.subscribe;
|
|
871
|
+
this._handleSubscribeResponse(result);
|
|
872
|
+
if (resolveCtx.next) {
|
|
873
|
+
resolveCtx.next();
|
|
874
|
+
}
|
|
875
|
+
}, (rejectCtx) => {
|
|
876
|
+
this._inflight = false;
|
|
877
|
+
this._handleSubscribeError(rejectCtx.error);
|
|
878
|
+
if (rejectCtx.next) {
|
|
879
|
+
rejectCtx.next();
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
return cmd;
|
|
883
|
+
}
|
|
884
|
+
_buildSubscribeCommand(token) {
|
|
885
|
+
const req = { channel: this.channel };
|
|
886
|
+
if (token)
|
|
887
|
+
req.token = token;
|
|
888
|
+
if (this._data)
|
|
889
|
+
req.data = this._data;
|
|
890
|
+
if (this._positioned)
|
|
891
|
+
req.positioned = true;
|
|
892
|
+
if (this._recoverable)
|
|
893
|
+
req.recoverable = true;
|
|
894
|
+
if (this._joinLeave)
|
|
895
|
+
req.join_leave = true;
|
|
896
|
+
req.flag = subscriptionFlags.channelCompaction;
|
|
897
|
+
if (this._needRecover()) {
|
|
898
|
+
req.recover = true;
|
|
899
|
+
const offset = this._getOffset();
|
|
900
|
+
if (offset)
|
|
901
|
+
req.offset = offset;
|
|
902
|
+
const epoch = this._getEpoch();
|
|
903
|
+
if (epoch)
|
|
904
|
+
req.epoch = epoch;
|
|
905
|
+
}
|
|
906
|
+
if (this._delta)
|
|
907
|
+
req.delta = this._delta;
|
|
908
|
+
if (this._tagsFilter)
|
|
909
|
+
req.tf = this._tagsFilter;
|
|
910
|
+
return { subscribe: req };
|
|
911
|
+
}
|
|
912
|
+
_debug(...args) {
|
|
913
|
+
this._centrifuge._debug(...args);
|
|
914
|
+
}
|
|
915
|
+
_handleSubscribeError(error) {
|
|
916
|
+
if (!this._isSubscribing()) {
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (error.code === errorCodes.timeout) {
|
|
920
|
+
this._centrifuge._disconnect(connectingCodes.subscribeTimeout, "subscribe timeout", true);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
this._subscribeError(error);
|
|
924
|
+
}
|
|
925
|
+
_handleSubscribeResponse(result) {
|
|
926
|
+
if (!this._isSubscribing()) {
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
this._setSubscribed(result);
|
|
930
|
+
}
|
|
931
|
+
_setUnsubscribed(code, reason, sendUnsubscribe) {
|
|
932
|
+
if (this._isUnsubscribed()) {
|
|
933
|
+
return Promise.resolve();
|
|
934
|
+
}
|
|
935
|
+
let promise = Promise.resolve();
|
|
936
|
+
if (this._isSubscribed()) {
|
|
937
|
+
if (sendUnsubscribe) {
|
|
938
|
+
promise = this._centrifuge._unsubscribe(this);
|
|
939
|
+
}
|
|
940
|
+
this._clearSubscribedState();
|
|
941
|
+
} else if (this._isSubscribing()) {
|
|
942
|
+
if (this._inflight && sendUnsubscribe) {
|
|
943
|
+
promise = this._centrifuge._unsubscribe(this);
|
|
944
|
+
}
|
|
945
|
+
this._clearSubscribingState();
|
|
946
|
+
}
|
|
947
|
+
this._inflight = false;
|
|
948
|
+
if (this._setState(SubscriptionState.Unsubscribed)) {
|
|
949
|
+
this.emit("unsubscribed", { channel: this.channel, code, reason });
|
|
950
|
+
}
|
|
951
|
+
this._rejectPromises({ code: errorCodes.subscriptionUnsubscribed, message: this.state });
|
|
952
|
+
return promise;
|
|
953
|
+
}
|
|
954
|
+
_handlePublication(pub) {
|
|
955
|
+
if (this._delta && this._delta_negotiated) {
|
|
956
|
+
const { newData, newPrevValue } = this._centrifuge._codec.applyDeltaIfNeeded(pub, this._prevValue);
|
|
957
|
+
pub.data = newData;
|
|
958
|
+
this._prevValue = newPrevValue;
|
|
959
|
+
}
|
|
960
|
+
const ctx = this._centrifuge._getPublicationContext(this.channel, pub);
|
|
961
|
+
this.emit("publication", ctx);
|
|
962
|
+
if (pub.offset) {
|
|
963
|
+
this._offset = pub.offset;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
_handleJoin(join) {
|
|
967
|
+
const info = this._centrifuge._getJoinLeaveContext(join.info);
|
|
968
|
+
this.emit("join", { channel: this.channel, info });
|
|
969
|
+
}
|
|
970
|
+
_handleLeave(leave) {
|
|
971
|
+
const info = this._centrifuge._getJoinLeaveContext(leave.info);
|
|
972
|
+
this.emit("leave", { channel: this.channel, info });
|
|
973
|
+
}
|
|
974
|
+
_resolvePromises() {
|
|
975
|
+
for (const id in this._promises) {
|
|
976
|
+
if (!this._promises.hasOwnProperty(id)) {
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (this._promises[id].timeout) {
|
|
980
|
+
clearTimeout(this._promises[id].timeout);
|
|
981
|
+
}
|
|
982
|
+
this._promises[id].resolve();
|
|
983
|
+
delete this._promises[id];
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
_rejectPromises(err) {
|
|
987
|
+
for (const id in this._promises) {
|
|
988
|
+
if (!this._promises.hasOwnProperty(id)) {
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
if (this._promises[id].timeout) {
|
|
992
|
+
clearTimeout(this._promises[id].timeout);
|
|
993
|
+
}
|
|
994
|
+
this._promises[id].reject(err);
|
|
995
|
+
delete this._promises[id];
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
_scheduleResubscribe() {
|
|
999
|
+
if (!this._isSubscribing()) {
|
|
1000
|
+
this._debug("not in subscribing state, skip resubscribe scheduling", this.channel);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const self = this;
|
|
1004
|
+
const delay = this._getResubscribeDelay();
|
|
1005
|
+
this._resubscribeTimeout = setTimeout(function() {
|
|
1006
|
+
if (self._isSubscribing()) {
|
|
1007
|
+
self._subscribe();
|
|
1008
|
+
}
|
|
1009
|
+
}, delay);
|
|
1010
|
+
this._debug("resubscribe scheduled after " + delay, this.channel);
|
|
1011
|
+
}
|
|
1012
|
+
_subscribeError(err) {
|
|
1013
|
+
if (!this._isSubscribing()) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
if (err.code < 100 || err.code === 109 || err.temporary === true) {
|
|
1017
|
+
if (err.code === 109) {
|
|
1018
|
+
this._token = "";
|
|
1019
|
+
}
|
|
1020
|
+
const errContext = {
|
|
1021
|
+
channel: this.channel,
|
|
1022
|
+
type: "subscribe",
|
|
1023
|
+
error: err
|
|
1024
|
+
};
|
|
1025
|
+
if (this._centrifuge.state === State.Connected) {
|
|
1026
|
+
this.emit("error", errContext);
|
|
1027
|
+
}
|
|
1028
|
+
this._scheduleResubscribe();
|
|
1029
|
+
} else {
|
|
1030
|
+
this._setUnsubscribed(err.code, err.message, false);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
_getResubscribeDelay() {
|
|
1034
|
+
const delay = backoff(this._resubscribeAttempts, this._minResubscribeDelay, this._maxResubscribeDelay);
|
|
1035
|
+
this._resubscribeAttempts++;
|
|
1036
|
+
return delay;
|
|
1037
|
+
}
|
|
1038
|
+
_setOptions(options) {
|
|
1039
|
+
if (!options) {
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (options.since) {
|
|
1043
|
+
this._offset = options.since.offset || 0;
|
|
1044
|
+
this._epoch = options.since.epoch || "";
|
|
1045
|
+
this._recover = true;
|
|
1046
|
+
}
|
|
1047
|
+
if (options.data) {
|
|
1048
|
+
this._data = options.data;
|
|
1049
|
+
}
|
|
1050
|
+
if (options.getData) {
|
|
1051
|
+
this._getData = options.getData;
|
|
1052
|
+
}
|
|
1053
|
+
if (options.minResubscribeDelay !== void 0) {
|
|
1054
|
+
this._minResubscribeDelay = options.minResubscribeDelay;
|
|
1055
|
+
}
|
|
1056
|
+
if (options.maxResubscribeDelay !== void 0) {
|
|
1057
|
+
this._maxResubscribeDelay = options.maxResubscribeDelay;
|
|
1058
|
+
}
|
|
1059
|
+
if (options.token) {
|
|
1060
|
+
this._token = options.token;
|
|
1061
|
+
}
|
|
1062
|
+
if (options.getToken) {
|
|
1063
|
+
this._getToken = options.getToken;
|
|
1064
|
+
}
|
|
1065
|
+
if (options.positioned === true) {
|
|
1066
|
+
this._positioned = true;
|
|
1067
|
+
}
|
|
1068
|
+
if (options.recoverable === true) {
|
|
1069
|
+
this._recoverable = true;
|
|
1070
|
+
}
|
|
1071
|
+
if (options.joinLeave === true) {
|
|
1072
|
+
this._joinLeave = true;
|
|
1073
|
+
}
|
|
1074
|
+
if (options.delta) {
|
|
1075
|
+
if (options.delta !== "fossil") {
|
|
1076
|
+
throw new Error("unsupported delta format");
|
|
1077
|
+
}
|
|
1078
|
+
this._delta = options.delta;
|
|
1079
|
+
}
|
|
1080
|
+
if (options.tagsFilter) {
|
|
1081
|
+
this._tagsFilter = options.tagsFilter;
|
|
1082
|
+
}
|
|
1083
|
+
if (this._tagsFilter && this._delta) {
|
|
1084
|
+
throw new Error("cannot use delta and tagsFilter together");
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
_getOffset() {
|
|
1088
|
+
const offset = this._offset;
|
|
1089
|
+
if (offset !== null) {
|
|
1090
|
+
return offset;
|
|
1091
|
+
}
|
|
1092
|
+
return 0;
|
|
1093
|
+
}
|
|
1094
|
+
_getEpoch() {
|
|
1095
|
+
const epoch = this._epoch;
|
|
1096
|
+
if (epoch !== null) {
|
|
1097
|
+
return epoch;
|
|
1098
|
+
}
|
|
1099
|
+
return "";
|
|
1100
|
+
}
|
|
1101
|
+
_clearRefreshTimeout() {
|
|
1102
|
+
if (this._refreshTimeout !== null) {
|
|
1103
|
+
clearTimeout(this._refreshTimeout);
|
|
1104
|
+
this._refreshTimeout = null;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
_clearResubscribeTimeout() {
|
|
1108
|
+
if (this._resubscribeTimeout !== null) {
|
|
1109
|
+
clearTimeout(this._resubscribeTimeout);
|
|
1110
|
+
this._resubscribeTimeout = null;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
_getSubscriptionToken() {
|
|
1114
|
+
this._debug("get subscription token for channel", this.channel);
|
|
1115
|
+
const ctx = {
|
|
1116
|
+
channel: this.channel
|
|
1117
|
+
};
|
|
1118
|
+
const getToken = this._getToken;
|
|
1119
|
+
if (getToken === null) {
|
|
1120
|
+
this.emit("error", {
|
|
1121
|
+
type: "configuration",
|
|
1122
|
+
channel: this.channel,
|
|
1123
|
+
error: {
|
|
1124
|
+
code: errorCodes.badConfiguration,
|
|
1125
|
+
message: "provide a function to get channel subscription token"
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
return Promise.reject(new UnauthorizedError(""));
|
|
1129
|
+
}
|
|
1130
|
+
return getToken(ctx);
|
|
1131
|
+
}
|
|
1132
|
+
_refresh() {
|
|
1133
|
+
this._clearRefreshTimeout();
|
|
1134
|
+
const self = this;
|
|
1135
|
+
this._getSubscriptionToken().then(function(token) {
|
|
1136
|
+
if (!self._isSubscribed()) {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
if (!token) {
|
|
1140
|
+
self._failUnauthorized();
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
self._token = token;
|
|
1144
|
+
const req = {
|
|
1145
|
+
channel: self.channel,
|
|
1146
|
+
token
|
|
1147
|
+
};
|
|
1148
|
+
const msg = {
|
|
1149
|
+
"sub_refresh": req
|
|
1150
|
+
};
|
|
1151
|
+
self._centrifuge._call(msg).then((resolveCtx) => {
|
|
1152
|
+
const result = resolveCtx.reply.sub_refresh;
|
|
1153
|
+
self._refreshResponse(result);
|
|
1154
|
+
if (resolveCtx.next) {
|
|
1155
|
+
resolveCtx.next();
|
|
1156
|
+
}
|
|
1157
|
+
}, (rejectCtx) => {
|
|
1158
|
+
self._refreshError(rejectCtx.error);
|
|
1159
|
+
if (rejectCtx.next) {
|
|
1160
|
+
rejectCtx.next();
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
}).catch(function(e) {
|
|
1164
|
+
if (e instanceof UnauthorizedError) {
|
|
1165
|
+
self._failUnauthorized();
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
self.emit("error", {
|
|
1169
|
+
type: "refreshToken",
|
|
1170
|
+
channel: self.channel,
|
|
1171
|
+
error: {
|
|
1172
|
+
code: errorCodes.subscriptionRefreshToken,
|
|
1173
|
+
message: e !== void 0 ? e.toString() : ""
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
self._refreshTimeout = setTimeout(() => self._refresh(), self._getRefreshRetryDelay());
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
_refreshResponse(result) {
|
|
1180
|
+
if (!this._isSubscribed()) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
this._debug("subscription token refreshed, channel", this.channel);
|
|
1184
|
+
this._clearRefreshTimeout();
|
|
1185
|
+
if (result.expires === true) {
|
|
1186
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), ttlMilliseconds(result.ttl));
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
_refreshError(err) {
|
|
1190
|
+
if (!this._isSubscribed()) {
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
if (err.code < 100 || err.temporary === true) {
|
|
1194
|
+
this.emit("error", {
|
|
1195
|
+
type: "refresh",
|
|
1196
|
+
channel: this.channel,
|
|
1197
|
+
error: err
|
|
1198
|
+
});
|
|
1199
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), this._getRefreshRetryDelay());
|
|
1200
|
+
} else {
|
|
1201
|
+
this._setUnsubscribed(err.code, err.message, true);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
_getRefreshRetryDelay() {
|
|
1205
|
+
return backoff(0, 1e4, 2e4);
|
|
1206
|
+
}
|
|
1207
|
+
_failUnauthorized() {
|
|
1208
|
+
this._setUnsubscribed(unsubscribedCodes.unauthorized, "unauthorized", true);
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
var SockjsTransport = class {
|
|
1212
|
+
constructor(endpoint, options) {
|
|
1213
|
+
this.endpoint = endpoint;
|
|
1214
|
+
this.options = options;
|
|
1215
|
+
this._transport = null;
|
|
1216
|
+
}
|
|
1217
|
+
name() {
|
|
1218
|
+
return "sockjs";
|
|
1219
|
+
}
|
|
1220
|
+
subName() {
|
|
1221
|
+
return "sockjs-" + this._transport.transport;
|
|
1222
|
+
}
|
|
1223
|
+
emulation() {
|
|
1224
|
+
return false;
|
|
1225
|
+
}
|
|
1226
|
+
supported() {
|
|
1227
|
+
return this.options.sockjs !== null;
|
|
1228
|
+
}
|
|
1229
|
+
initialize(_protocol, callbacks) {
|
|
1230
|
+
this._transport = new this.options.sockjs(this.endpoint, null, this.options.sockjsOptions);
|
|
1231
|
+
this._transport.onopen = () => {
|
|
1232
|
+
callbacks.onOpen();
|
|
1233
|
+
};
|
|
1234
|
+
this._transport.onerror = (e) => {
|
|
1235
|
+
callbacks.onError(e);
|
|
1236
|
+
};
|
|
1237
|
+
this._transport.onclose = (closeEvent) => {
|
|
1238
|
+
callbacks.onClose(closeEvent);
|
|
1239
|
+
};
|
|
1240
|
+
this._transport.onmessage = (event) => {
|
|
1241
|
+
callbacks.onMessage(event.data);
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
close() {
|
|
1245
|
+
this._transport.close();
|
|
1246
|
+
}
|
|
1247
|
+
send(data) {
|
|
1248
|
+
this._transport.send(data);
|
|
1249
|
+
}
|
|
1250
|
+
};
|
|
1251
|
+
var WebsocketTransport = class {
|
|
1252
|
+
constructor(endpoint, options) {
|
|
1253
|
+
this.endpoint = endpoint;
|
|
1254
|
+
this.options = options;
|
|
1255
|
+
this._transport = null;
|
|
1256
|
+
}
|
|
1257
|
+
name() {
|
|
1258
|
+
return "websocket";
|
|
1259
|
+
}
|
|
1260
|
+
subName() {
|
|
1261
|
+
return "websocket";
|
|
1262
|
+
}
|
|
1263
|
+
emulation() {
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
supported() {
|
|
1267
|
+
return this.options.websocket !== void 0 && this.options.websocket !== null;
|
|
1268
|
+
}
|
|
1269
|
+
initialize(protocol, callbacks) {
|
|
1270
|
+
let subProtocol = "";
|
|
1271
|
+
if (protocol === "protobuf") {
|
|
1272
|
+
subProtocol = "centrifuge-protobuf";
|
|
1273
|
+
}
|
|
1274
|
+
if (subProtocol !== "") {
|
|
1275
|
+
this._transport = new this.options.websocket(this.endpoint, subProtocol);
|
|
1276
|
+
} else {
|
|
1277
|
+
this._transport = new this.options.websocket(this.endpoint);
|
|
1278
|
+
}
|
|
1279
|
+
if (protocol === "protobuf") {
|
|
1280
|
+
this._transport.binaryType = "arraybuffer";
|
|
1281
|
+
}
|
|
1282
|
+
this._transport.onopen = () => {
|
|
1283
|
+
callbacks.onOpen();
|
|
1284
|
+
};
|
|
1285
|
+
this._transport.onerror = (e) => {
|
|
1286
|
+
callbacks.onError(e);
|
|
1287
|
+
};
|
|
1288
|
+
this._transport.onclose = (closeEvent) => {
|
|
1289
|
+
callbacks.onClose(closeEvent);
|
|
1290
|
+
};
|
|
1291
|
+
this._transport.onmessage = (event) => {
|
|
1292
|
+
callbacks.onMessage(event.data);
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
close() {
|
|
1296
|
+
this._transport.close();
|
|
1297
|
+
}
|
|
1298
|
+
send(data) {
|
|
1299
|
+
this._transport.send(data);
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
var HttpStreamTransport = class {
|
|
1303
|
+
constructor(endpoint, options) {
|
|
1304
|
+
this.endpoint = endpoint;
|
|
1305
|
+
this.options = options;
|
|
1306
|
+
this._abortController = null;
|
|
1307
|
+
this._utf8decoder = new TextDecoder();
|
|
1308
|
+
this._protocol = "json";
|
|
1309
|
+
}
|
|
1310
|
+
name() {
|
|
1311
|
+
return "http_stream";
|
|
1312
|
+
}
|
|
1313
|
+
subName() {
|
|
1314
|
+
return "http_stream";
|
|
1315
|
+
}
|
|
1316
|
+
emulation() {
|
|
1317
|
+
return true;
|
|
1318
|
+
}
|
|
1319
|
+
_handleErrors(response) {
|
|
1320
|
+
if (!response.ok)
|
|
1321
|
+
throw new Error(response.status);
|
|
1322
|
+
return response;
|
|
1323
|
+
}
|
|
1324
|
+
_fetchEventTarget(self, endpoint, options) {
|
|
1325
|
+
const eventTarget = new EventTarget();
|
|
1326
|
+
const fetchFunc = self.options.fetch;
|
|
1327
|
+
fetchFunc(endpoint, options).then(self._handleErrors).then((response) => {
|
|
1328
|
+
eventTarget.dispatchEvent(new Event("open"));
|
|
1329
|
+
let jsonStreamBuf = "";
|
|
1330
|
+
let jsonStreamPos = 0;
|
|
1331
|
+
let protoStreamBuf = new Uint8Array();
|
|
1332
|
+
const reader = response.body.getReader();
|
|
1333
|
+
return new self.options.readableStream({
|
|
1334
|
+
start(controller) {
|
|
1335
|
+
function pump() {
|
|
1336
|
+
return reader.read().then(({ done, value }) => {
|
|
1337
|
+
if (done) {
|
|
1338
|
+
eventTarget.dispatchEvent(new Event("close"));
|
|
1339
|
+
controller.close();
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
try {
|
|
1343
|
+
if (self._protocol === "json") {
|
|
1344
|
+
jsonStreamBuf += self._utf8decoder.decode(value);
|
|
1345
|
+
while (jsonStreamPos < jsonStreamBuf.length) {
|
|
1346
|
+
if (jsonStreamBuf[jsonStreamPos] === "\n") {
|
|
1347
|
+
const line = jsonStreamBuf.substring(0, jsonStreamPos);
|
|
1348
|
+
eventTarget.dispatchEvent(new MessageEvent("message", { data: line }));
|
|
1349
|
+
jsonStreamBuf = jsonStreamBuf.substring(jsonStreamPos + 1);
|
|
1350
|
+
jsonStreamPos = 0;
|
|
1351
|
+
} else {
|
|
1352
|
+
++jsonStreamPos;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
} else {
|
|
1356
|
+
const mergedArray = new Uint8Array(protoStreamBuf.length + value.length);
|
|
1357
|
+
mergedArray.set(protoStreamBuf);
|
|
1358
|
+
mergedArray.set(value, protoStreamBuf.length);
|
|
1359
|
+
protoStreamBuf = mergedArray;
|
|
1360
|
+
while (true) {
|
|
1361
|
+
const result = self.options.decoder.decodeReply(protoStreamBuf);
|
|
1362
|
+
if (result.ok) {
|
|
1363
|
+
const data = protoStreamBuf.slice(0, result.pos);
|
|
1364
|
+
eventTarget.dispatchEvent(new MessageEvent("message", { data }));
|
|
1365
|
+
protoStreamBuf = protoStreamBuf.slice(result.pos);
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
break;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
} catch (error) {
|
|
1372
|
+
eventTarget.dispatchEvent(new Event("error", { detail: error }));
|
|
1373
|
+
eventTarget.dispatchEvent(new Event("close"));
|
|
1374
|
+
controller.close();
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
pump();
|
|
1378
|
+
}).catch(function(e) {
|
|
1379
|
+
eventTarget.dispatchEvent(new Event("error", { detail: e }));
|
|
1380
|
+
eventTarget.dispatchEvent(new Event("close"));
|
|
1381
|
+
controller.close();
|
|
1382
|
+
return;
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
return pump();
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
}).catch((error) => {
|
|
1389
|
+
eventTarget.dispatchEvent(new Event("error", { detail: error }));
|
|
1390
|
+
eventTarget.dispatchEvent(new Event("close"));
|
|
1391
|
+
});
|
|
1392
|
+
return eventTarget;
|
|
1393
|
+
}
|
|
1394
|
+
supported() {
|
|
1395
|
+
return this.options.fetch !== null && this.options.readableStream !== null && typeof TextDecoder !== "undefined" && typeof AbortController !== "undefined" && typeof EventTarget !== "undefined" && typeof Event !== "undefined" && typeof MessageEvent !== "undefined" && typeof Error !== "undefined";
|
|
1396
|
+
}
|
|
1397
|
+
initialize(protocol, callbacks, initialData) {
|
|
1398
|
+
this._protocol = protocol;
|
|
1399
|
+
this._abortController = new AbortController();
|
|
1400
|
+
let headers;
|
|
1401
|
+
let body;
|
|
1402
|
+
if (protocol === "json") {
|
|
1403
|
+
headers = {
|
|
1404
|
+
"Accept": "application/json",
|
|
1405
|
+
"Content-Type": "application/json"
|
|
1406
|
+
};
|
|
1407
|
+
body = initialData;
|
|
1408
|
+
} else {
|
|
1409
|
+
headers = {
|
|
1410
|
+
"Accept": "application/octet-stream",
|
|
1411
|
+
"Content-Type": "application/octet-stream"
|
|
1412
|
+
};
|
|
1413
|
+
body = initialData;
|
|
1414
|
+
}
|
|
1415
|
+
const fetchOptions = {
|
|
1416
|
+
method: "POST",
|
|
1417
|
+
headers,
|
|
1418
|
+
body,
|
|
1419
|
+
mode: "cors",
|
|
1420
|
+
credentials: "same-origin",
|
|
1421
|
+
signal: this._abortController.signal
|
|
1422
|
+
};
|
|
1423
|
+
const eventTarget = this._fetchEventTarget(this, this.endpoint, fetchOptions);
|
|
1424
|
+
eventTarget.addEventListener("open", () => {
|
|
1425
|
+
callbacks.onOpen();
|
|
1426
|
+
});
|
|
1427
|
+
eventTarget.addEventListener("error", (e) => {
|
|
1428
|
+
this._abortController.abort();
|
|
1429
|
+
callbacks.onError(e);
|
|
1430
|
+
});
|
|
1431
|
+
eventTarget.addEventListener("close", () => {
|
|
1432
|
+
this._abortController.abort();
|
|
1433
|
+
callbacks.onClose({
|
|
1434
|
+
code: 4,
|
|
1435
|
+
reason: "connection closed"
|
|
1436
|
+
});
|
|
1437
|
+
});
|
|
1438
|
+
eventTarget.addEventListener("message", (e) => {
|
|
1439
|
+
callbacks.onMessage(e.data);
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
close() {
|
|
1443
|
+
this._abortController.abort();
|
|
1444
|
+
}
|
|
1445
|
+
send(data, session, node) {
|
|
1446
|
+
let headers;
|
|
1447
|
+
let body;
|
|
1448
|
+
const req = {
|
|
1449
|
+
session,
|
|
1450
|
+
node,
|
|
1451
|
+
data
|
|
1452
|
+
};
|
|
1453
|
+
if (this._protocol === "json") {
|
|
1454
|
+
headers = {
|
|
1455
|
+
"Content-Type": "application/json"
|
|
1456
|
+
};
|
|
1457
|
+
body = JSON.stringify(req);
|
|
1458
|
+
} else {
|
|
1459
|
+
headers = {
|
|
1460
|
+
"Content-Type": "application/octet-stream"
|
|
1461
|
+
};
|
|
1462
|
+
body = this.options.encoder.encodeEmulationRequest(req);
|
|
1463
|
+
}
|
|
1464
|
+
const fetchFunc = this.options.fetch;
|
|
1465
|
+
const fetchOptions = {
|
|
1466
|
+
method: "POST",
|
|
1467
|
+
headers,
|
|
1468
|
+
body,
|
|
1469
|
+
mode: "cors",
|
|
1470
|
+
credentials: "same-origin"
|
|
1471
|
+
};
|
|
1472
|
+
fetchFunc(this.options.emulationEndpoint, fetchOptions);
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
var SseTransport = class {
|
|
1476
|
+
constructor(endpoint, options) {
|
|
1477
|
+
this.endpoint = endpoint;
|
|
1478
|
+
this.options = options;
|
|
1479
|
+
this._protocol = "json";
|
|
1480
|
+
this._transport = null;
|
|
1481
|
+
this._onClose = null;
|
|
1482
|
+
}
|
|
1483
|
+
name() {
|
|
1484
|
+
return "sse";
|
|
1485
|
+
}
|
|
1486
|
+
subName() {
|
|
1487
|
+
return "sse";
|
|
1488
|
+
}
|
|
1489
|
+
emulation() {
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1492
|
+
supported() {
|
|
1493
|
+
return this.options.eventsource !== null && this.options.fetch !== null;
|
|
1494
|
+
}
|
|
1495
|
+
initialize(_protocol, callbacks, initialData) {
|
|
1496
|
+
let url;
|
|
1497
|
+
if (globalThis && globalThis.document && globalThis.document.baseURI) {
|
|
1498
|
+
url = new URL(this.endpoint, globalThis.document.baseURI);
|
|
1499
|
+
} else {
|
|
1500
|
+
url = new URL(this.endpoint);
|
|
1501
|
+
}
|
|
1502
|
+
url.searchParams.append("cf_connect", initialData);
|
|
1503
|
+
const eventsourceOptions = {};
|
|
1504
|
+
const eventSource = new this.options.eventsource(url.toString(), eventsourceOptions);
|
|
1505
|
+
this._transport = eventSource;
|
|
1506
|
+
const self = this;
|
|
1507
|
+
eventSource.onopen = function() {
|
|
1508
|
+
callbacks.onOpen();
|
|
1509
|
+
};
|
|
1510
|
+
eventSource.onerror = function(e) {
|
|
1511
|
+
eventSource.close();
|
|
1512
|
+
callbacks.onError(e);
|
|
1513
|
+
callbacks.onClose({
|
|
1514
|
+
code: 4,
|
|
1515
|
+
reason: "connection closed"
|
|
1516
|
+
});
|
|
1517
|
+
};
|
|
1518
|
+
eventSource.onmessage = function(e) {
|
|
1519
|
+
callbacks.onMessage(e.data);
|
|
1520
|
+
};
|
|
1521
|
+
self._onClose = function() {
|
|
1522
|
+
callbacks.onClose({
|
|
1523
|
+
code: 4,
|
|
1524
|
+
reason: "connection closed"
|
|
1525
|
+
});
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
close() {
|
|
1529
|
+
this._transport.close();
|
|
1530
|
+
if (this._onClose !== null) {
|
|
1531
|
+
this._onClose();
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
send(data, session, node) {
|
|
1535
|
+
const req = {
|
|
1536
|
+
session,
|
|
1537
|
+
node,
|
|
1538
|
+
data
|
|
1539
|
+
};
|
|
1540
|
+
const headers = {
|
|
1541
|
+
"Content-Type": "application/json"
|
|
1542
|
+
};
|
|
1543
|
+
const body = JSON.stringify(req);
|
|
1544
|
+
const fetchFunc = this.options.fetch;
|
|
1545
|
+
const fetchOptions = {
|
|
1546
|
+
method: "POST",
|
|
1547
|
+
headers,
|
|
1548
|
+
body,
|
|
1549
|
+
mode: "cors",
|
|
1550
|
+
credentials: "same-origin"
|
|
1551
|
+
};
|
|
1552
|
+
fetchFunc(this.options.emulationEndpoint, fetchOptions);
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
var WebtransportTransport = class {
|
|
1556
|
+
constructor(endpoint, options) {
|
|
1557
|
+
this.endpoint = endpoint;
|
|
1558
|
+
this.options = options;
|
|
1559
|
+
this._transport = null;
|
|
1560
|
+
this._stream = null;
|
|
1561
|
+
this._writer = null;
|
|
1562
|
+
this._utf8decoder = new TextDecoder();
|
|
1563
|
+
this._protocol = "json";
|
|
1564
|
+
}
|
|
1565
|
+
name() {
|
|
1566
|
+
return "webtransport";
|
|
1567
|
+
}
|
|
1568
|
+
subName() {
|
|
1569
|
+
return "webtransport";
|
|
1570
|
+
}
|
|
1571
|
+
emulation() {
|
|
1572
|
+
return false;
|
|
1573
|
+
}
|
|
1574
|
+
supported() {
|
|
1575
|
+
return this.options.webtransport !== void 0 && this.options.webtransport !== null;
|
|
1576
|
+
}
|
|
1577
|
+
initialize(protocol, callbacks) {
|
|
1578
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1579
|
+
let url;
|
|
1580
|
+
if (globalThis && globalThis.document && globalThis.document.baseURI) {
|
|
1581
|
+
url = new URL(this.endpoint, globalThis.document.baseURI);
|
|
1582
|
+
} else {
|
|
1583
|
+
url = new URL(this.endpoint);
|
|
1584
|
+
}
|
|
1585
|
+
if (protocol === "protobuf") {
|
|
1586
|
+
url.searchParams.append("cf_protocol", "protobuf");
|
|
1587
|
+
}
|
|
1588
|
+
this._protocol = protocol;
|
|
1589
|
+
const eventTarget = new EventTarget();
|
|
1590
|
+
this._transport = new this.options.webtransport(url.toString());
|
|
1591
|
+
this._transport.closed.then(() => {
|
|
1592
|
+
callbacks.onClose({
|
|
1593
|
+
code: 4,
|
|
1594
|
+
reason: "connection closed"
|
|
1595
|
+
});
|
|
1596
|
+
}).catch(() => {
|
|
1597
|
+
callbacks.onClose({
|
|
1598
|
+
code: 4,
|
|
1599
|
+
reason: "connection closed"
|
|
1600
|
+
});
|
|
1601
|
+
});
|
|
1602
|
+
try {
|
|
1603
|
+
yield this._transport.ready;
|
|
1604
|
+
} catch (_a) {
|
|
1605
|
+
this.close();
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
let stream;
|
|
1609
|
+
try {
|
|
1610
|
+
stream = yield this._transport.createBidirectionalStream();
|
|
1611
|
+
} catch (_b) {
|
|
1612
|
+
this.close();
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
this._stream = stream;
|
|
1616
|
+
this._writer = this._stream.writable.getWriter();
|
|
1617
|
+
eventTarget.addEventListener("close", () => {
|
|
1618
|
+
callbacks.onClose({
|
|
1619
|
+
code: 4,
|
|
1620
|
+
reason: "connection closed"
|
|
1621
|
+
});
|
|
1622
|
+
});
|
|
1623
|
+
eventTarget.addEventListener("message", (e) => {
|
|
1624
|
+
callbacks.onMessage(e.data);
|
|
1625
|
+
});
|
|
1626
|
+
this._startReading(eventTarget);
|
|
1627
|
+
callbacks.onOpen();
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
_startReading(eventTarget) {
|
|
1631
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1632
|
+
const reader = this._stream.readable.getReader();
|
|
1633
|
+
let jsonStreamBuf = "";
|
|
1634
|
+
let jsonStreamPos = 0;
|
|
1635
|
+
let protoStreamBuf = new Uint8Array();
|
|
1636
|
+
try {
|
|
1637
|
+
while (true) {
|
|
1638
|
+
const { done, value } = yield reader.read();
|
|
1639
|
+
if (value.length > 0) {
|
|
1640
|
+
if (this._protocol === "json") {
|
|
1641
|
+
jsonStreamBuf += this._utf8decoder.decode(value);
|
|
1642
|
+
while (jsonStreamPos < jsonStreamBuf.length) {
|
|
1643
|
+
if (jsonStreamBuf[jsonStreamPos] === "\n") {
|
|
1644
|
+
const line = jsonStreamBuf.substring(0, jsonStreamPos);
|
|
1645
|
+
eventTarget.dispatchEvent(new MessageEvent("message", { data: line }));
|
|
1646
|
+
jsonStreamBuf = jsonStreamBuf.substring(jsonStreamPos + 1);
|
|
1647
|
+
jsonStreamPos = 0;
|
|
1648
|
+
} else {
|
|
1649
|
+
++jsonStreamPos;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
} else {
|
|
1653
|
+
const mergedArray = new Uint8Array(protoStreamBuf.length + value.length);
|
|
1654
|
+
mergedArray.set(protoStreamBuf);
|
|
1655
|
+
mergedArray.set(value, protoStreamBuf.length);
|
|
1656
|
+
protoStreamBuf = mergedArray;
|
|
1657
|
+
while (true) {
|
|
1658
|
+
const result = this.options.decoder.decodeReply(protoStreamBuf);
|
|
1659
|
+
if (result.ok) {
|
|
1660
|
+
const data = protoStreamBuf.slice(0, result.pos);
|
|
1661
|
+
eventTarget.dispatchEvent(new MessageEvent("message", { data }));
|
|
1662
|
+
protoStreamBuf = protoStreamBuf.slice(result.pos);
|
|
1663
|
+
continue;
|
|
1664
|
+
}
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
if (done) {
|
|
1670
|
+
break;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
} catch (_a) {
|
|
1674
|
+
eventTarget.dispatchEvent(new Event("close"));
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
close() {
|
|
1679
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1680
|
+
try {
|
|
1681
|
+
if (this._writer) {
|
|
1682
|
+
yield this._writer.close();
|
|
1683
|
+
}
|
|
1684
|
+
this._transport.close();
|
|
1685
|
+
} catch (e) {
|
|
1686
|
+
}
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
send(data) {
|
|
1690
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1691
|
+
let binary;
|
|
1692
|
+
if (this._protocol === "json") {
|
|
1693
|
+
binary = new TextEncoder().encode(data + "\n");
|
|
1694
|
+
} else {
|
|
1695
|
+
binary = data;
|
|
1696
|
+
}
|
|
1697
|
+
try {
|
|
1698
|
+
yield this._writer.write(binary);
|
|
1699
|
+
} catch (e) {
|
|
1700
|
+
this.close();
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
var zValue = [
|
|
1706
|
+
-1,
|
|
1707
|
+
-1,
|
|
1708
|
+
-1,
|
|
1709
|
+
-1,
|
|
1710
|
+
-1,
|
|
1711
|
+
-1,
|
|
1712
|
+
-1,
|
|
1713
|
+
-1,
|
|
1714
|
+
-1,
|
|
1715
|
+
-1,
|
|
1716
|
+
-1,
|
|
1717
|
+
-1,
|
|
1718
|
+
-1,
|
|
1719
|
+
-1,
|
|
1720
|
+
-1,
|
|
1721
|
+
-1,
|
|
1722
|
+
-1,
|
|
1723
|
+
-1,
|
|
1724
|
+
-1,
|
|
1725
|
+
-1,
|
|
1726
|
+
-1,
|
|
1727
|
+
-1,
|
|
1728
|
+
-1,
|
|
1729
|
+
-1,
|
|
1730
|
+
-1,
|
|
1731
|
+
-1,
|
|
1732
|
+
-1,
|
|
1733
|
+
-1,
|
|
1734
|
+
-1,
|
|
1735
|
+
-1,
|
|
1736
|
+
-1,
|
|
1737
|
+
-1,
|
|
1738
|
+
-1,
|
|
1739
|
+
-1,
|
|
1740
|
+
-1,
|
|
1741
|
+
-1,
|
|
1742
|
+
-1,
|
|
1743
|
+
-1,
|
|
1744
|
+
-1,
|
|
1745
|
+
-1,
|
|
1746
|
+
-1,
|
|
1747
|
+
-1,
|
|
1748
|
+
-1,
|
|
1749
|
+
-1,
|
|
1750
|
+
-1,
|
|
1751
|
+
-1,
|
|
1752
|
+
-1,
|
|
1753
|
+
-1,
|
|
1754
|
+
0,
|
|
1755
|
+
1,
|
|
1756
|
+
2,
|
|
1757
|
+
3,
|
|
1758
|
+
4,
|
|
1759
|
+
5,
|
|
1760
|
+
6,
|
|
1761
|
+
7,
|
|
1762
|
+
8,
|
|
1763
|
+
9,
|
|
1764
|
+
-1,
|
|
1765
|
+
-1,
|
|
1766
|
+
-1,
|
|
1767
|
+
-1,
|
|
1768
|
+
-1,
|
|
1769
|
+
-1,
|
|
1770
|
+
-1,
|
|
1771
|
+
10,
|
|
1772
|
+
11,
|
|
1773
|
+
12,
|
|
1774
|
+
13,
|
|
1775
|
+
14,
|
|
1776
|
+
15,
|
|
1777
|
+
16,
|
|
1778
|
+
17,
|
|
1779
|
+
18,
|
|
1780
|
+
19,
|
|
1781
|
+
20,
|
|
1782
|
+
21,
|
|
1783
|
+
22,
|
|
1784
|
+
23,
|
|
1785
|
+
24,
|
|
1786
|
+
25,
|
|
1787
|
+
26,
|
|
1788
|
+
27,
|
|
1789
|
+
28,
|
|
1790
|
+
29,
|
|
1791
|
+
30,
|
|
1792
|
+
31,
|
|
1793
|
+
32,
|
|
1794
|
+
33,
|
|
1795
|
+
34,
|
|
1796
|
+
35,
|
|
1797
|
+
-1,
|
|
1798
|
+
-1,
|
|
1799
|
+
-1,
|
|
1800
|
+
-1,
|
|
1801
|
+
36,
|
|
1802
|
+
-1,
|
|
1803
|
+
37,
|
|
1804
|
+
38,
|
|
1805
|
+
39,
|
|
1806
|
+
40,
|
|
1807
|
+
41,
|
|
1808
|
+
42,
|
|
1809
|
+
43,
|
|
1810
|
+
44,
|
|
1811
|
+
45,
|
|
1812
|
+
46,
|
|
1813
|
+
47,
|
|
1814
|
+
48,
|
|
1815
|
+
49,
|
|
1816
|
+
50,
|
|
1817
|
+
51,
|
|
1818
|
+
52,
|
|
1819
|
+
53,
|
|
1820
|
+
54,
|
|
1821
|
+
55,
|
|
1822
|
+
56,
|
|
1823
|
+
57,
|
|
1824
|
+
58,
|
|
1825
|
+
59,
|
|
1826
|
+
60,
|
|
1827
|
+
61,
|
|
1828
|
+
62,
|
|
1829
|
+
-1,
|
|
1830
|
+
-1,
|
|
1831
|
+
-1,
|
|
1832
|
+
63,
|
|
1833
|
+
-1
|
|
1834
|
+
];
|
|
1835
|
+
var Reader = class {
|
|
1836
|
+
constructor(array) {
|
|
1837
|
+
this.a = array;
|
|
1838
|
+
this.pos = 0;
|
|
1839
|
+
}
|
|
1840
|
+
haveBytes() {
|
|
1841
|
+
return this.pos < this.a.length;
|
|
1842
|
+
}
|
|
1843
|
+
getByte() {
|
|
1844
|
+
const b = this.a[this.pos];
|
|
1845
|
+
this.pos++;
|
|
1846
|
+
if (this.pos > this.a.length)
|
|
1847
|
+
throw new RangeError("out of bounds");
|
|
1848
|
+
return b;
|
|
1849
|
+
}
|
|
1850
|
+
getChar() {
|
|
1851
|
+
return String.fromCharCode(this.getByte());
|
|
1852
|
+
}
|
|
1853
|
+
// Read base64-encoded unsigned integer.
|
|
1854
|
+
getInt() {
|
|
1855
|
+
let v = 0;
|
|
1856
|
+
let c;
|
|
1857
|
+
while (this.haveBytes() && (c = zValue[127 & this.getByte()]) >= 0) {
|
|
1858
|
+
v = (v << 6) + c;
|
|
1859
|
+
}
|
|
1860
|
+
this.pos--;
|
|
1861
|
+
return v >>> 0;
|
|
1862
|
+
}
|
|
1863
|
+
};
|
|
1864
|
+
var Writer = class {
|
|
1865
|
+
constructor() {
|
|
1866
|
+
this.a = [];
|
|
1867
|
+
}
|
|
1868
|
+
toByteArray(sourceType) {
|
|
1869
|
+
if (Array.isArray(sourceType)) {
|
|
1870
|
+
return this.a;
|
|
1871
|
+
}
|
|
1872
|
+
return new Uint8Array(this.a);
|
|
1873
|
+
}
|
|
1874
|
+
// Copy from array at start to end.
|
|
1875
|
+
putArray(a, start, end) {
|
|
1876
|
+
for (let i = start; i < end; i++)
|
|
1877
|
+
this.a.push(a[i]);
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
function checksum(arr) {
|
|
1881
|
+
let sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0, z = 0, N = arr.length;
|
|
1882
|
+
while (N >= 16) {
|
|
1883
|
+
sum0 = sum0 + arr[z + 0] | 0;
|
|
1884
|
+
sum1 = sum1 + arr[z + 1] | 0;
|
|
1885
|
+
sum2 = sum2 + arr[z + 2] | 0;
|
|
1886
|
+
sum3 = sum3 + arr[z + 3] | 0;
|
|
1887
|
+
sum0 = sum0 + arr[z + 4] | 0;
|
|
1888
|
+
sum1 = sum1 + arr[z + 5] | 0;
|
|
1889
|
+
sum2 = sum2 + arr[z + 6] | 0;
|
|
1890
|
+
sum3 = sum3 + arr[z + 7] | 0;
|
|
1891
|
+
sum0 = sum0 + arr[z + 8] | 0;
|
|
1892
|
+
sum1 = sum1 + arr[z + 9] | 0;
|
|
1893
|
+
sum2 = sum2 + arr[z + 10] | 0;
|
|
1894
|
+
sum3 = sum3 + arr[z + 11] | 0;
|
|
1895
|
+
sum0 = sum0 + arr[z + 12] | 0;
|
|
1896
|
+
sum1 = sum1 + arr[z + 13] | 0;
|
|
1897
|
+
sum2 = sum2 + arr[z + 14] | 0;
|
|
1898
|
+
sum3 = sum3 + arr[z + 15] | 0;
|
|
1899
|
+
z += 16;
|
|
1900
|
+
N -= 16;
|
|
1901
|
+
}
|
|
1902
|
+
while (N >= 4) {
|
|
1903
|
+
sum0 = sum0 + arr[z + 0] | 0;
|
|
1904
|
+
sum1 = sum1 + arr[z + 1] | 0;
|
|
1905
|
+
sum2 = sum2 + arr[z + 2] | 0;
|
|
1906
|
+
sum3 = sum3 + arr[z + 3] | 0;
|
|
1907
|
+
z += 4;
|
|
1908
|
+
N -= 4;
|
|
1909
|
+
}
|
|
1910
|
+
sum3 = ((sum3 + (sum2 << 8) | 0) + (sum1 << 16) | 0) + (sum0 << 24) | 0;
|
|
1911
|
+
switch (N) {
|
|
1912
|
+
//@ts-ignore fallthrough is needed.
|
|
1913
|
+
case 3:
|
|
1914
|
+
sum3 = sum3 + (arr[z + 2] << 8) | 0;
|
|
1915
|
+
/* falls through */
|
|
1916
|
+
//@ts-ignore fallthrough is needed.
|
|
1917
|
+
case 2:
|
|
1918
|
+
sum3 = sum3 + (arr[z + 1] << 16) | 0;
|
|
1919
|
+
/* falls through */
|
|
1920
|
+
case 1:
|
|
1921
|
+
sum3 = sum3 + (arr[z + 0] << 24) | 0;
|
|
1922
|
+
}
|
|
1923
|
+
return sum3 >>> 0;
|
|
1924
|
+
}
|
|
1925
|
+
function applyDelta(source, delta) {
|
|
1926
|
+
let total = 0;
|
|
1927
|
+
const zDelta = new Reader(delta);
|
|
1928
|
+
const lenSrc = source.length;
|
|
1929
|
+
const lenDelta = delta.length;
|
|
1930
|
+
const limit = zDelta.getInt();
|
|
1931
|
+
if (zDelta.getChar() !== "\n")
|
|
1932
|
+
throw new Error("size integer not terminated by '\\n'");
|
|
1933
|
+
const zOut = new Writer();
|
|
1934
|
+
while (zDelta.haveBytes()) {
|
|
1935
|
+
const cnt = zDelta.getInt();
|
|
1936
|
+
let ofst;
|
|
1937
|
+
switch (zDelta.getChar()) {
|
|
1938
|
+
case "@":
|
|
1939
|
+
ofst = zDelta.getInt();
|
|
1940
|
+
if (zDelta.haveBytes() && zDelta.getChar() !== ",")
|
|
1941
|
+
throw new Error("copy command not terminated by ','");
|
|
1942
|
+
total += cnt;
|
|
1943
|
+
if (total > limit)
|
|
1944
|
+
throw new Error("copy exceeds output file size");
|
|
1945
|
+
if (ofst + cnt > lenSrc)
|
|
1946
|
+
throw new Error("copy extends past end of input");
|
|
1947
|
+
zOut.putArray(source, ofst, ofst + cnt);
|
|
1948
|
+
break;
|
|
1949
|
+
case ":":
|
|
1950
|
+
total += cnt;
|
|
1951
|
+
if (total > limit)
|
|
1952
|
+
throw new Error("insert command gives an output larger than predicted");
|
|
1953
|
+
if (cnt > lenDelta)
|
|
1954
|
+
throw new Error("insert count exceeds size of delta");
|
|
1955
|
+
zOut.putArray(zDelta.a, zDelta.pos, zDelta.pos + cnt);
|
|
1956
|
+
zDelta.pos += cnt;
|
|
1957
|
+
break;
|
|
1958
|
+
case ";": {
|
|
1959
|
+
const out = zOut.toByteArray(source);
|
|
1960
|
+
if (cnt !== checksum(out))
|
|
1961
|
+
throw new Error("bad checksum");
|
|
1962
|
+
if (total !== limit)
|
|
1963
|
+
throw new Error("generated size does not match predicted size");
|
|
1964
|
+
return out;
|
|
1965
|
+
}
|
|
1966
|
+
default:
|
|
1967
|
+
throw new Error("unknown delta operator");
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
throw new Error("unterminated delta");
|
|
1971
|
+
}
|
|
1972
|
+
var JsonCodec = class {
|
|
1973
|
+
name() {
|
|
1974
|
+
return "json";
|
|
1975
|
+
}
|
|
1976
|
+
encodeCommands(commands) {
|
|
1977
|
+
return commands.map((c) => JSON.stringify(c)).join("\n");
|
|
1978
|
+
}
|
|
1979
|
+
decodeReplies(data) {
|
|
1980
|
+
return data.trim().split("\n").map((r) => JSON.parse(r));
|
|
1981
|
+
}
|
|
1982
|
+
applyDeltaIfNeeded(pub, prevValue) {
|
|
1983
|
+
let newData, newPrevValue;
|
|
1984
|
+
if (pub.delta) {
|
|
1985
|
+
const valueArray = applyDelta(prevValue, new TextEncoder().encode(pub.data));
|
|
1986
|
+
newData = JSON.parse(new TextDecoder().decode(valueArray));
|
|
1987
|
+
newPrevValue = valueArray;
|
|
1988
|
+
} else {
|
|
1989
|
+
newData = JSON.parse(pub.data);
|
|
1990
|
+
newPrevValue = new TextEncoder().encode(pub.data);
|
|
1991
|
+
}
|
|
1992
|
+
return { newData, newPrevValue };
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
var defaults = {
|
|
1996
|
+
headers: {},
|
|
1997
|
+
token: "",
|
|
1998
|
+
getToken: null,
|
|
1999
|
+
data: null,
|
|
2000
|
+
getData: null,
|
|
2001
|
+
debug: false,
|
|
2002
|
+
name: "js",
|
|
2003
|
+
version: "",
|
|
2004
|
+
fetch: null,
|
|
2005
|
+
readableStream: null,
|
|
2006
|
+
websocket: null,
|
|
2007
|
+
eventsource: null,
|
|
2008
|
+
sockjs: null,
|
|
2009
|
+
sockjsOptions: {},
|
|
2010
|
+
emulationEndpoint: "/emulation",
|
|
2011
|
+
minReconnectDelay: 500,
|
|
2012
|
+
maxReconnectDelay: 2e4,
|
|
2013
|
+
timeout: 5e3,
|
|
2014
|
+
maxServerPingDelay: 1e4,
|
|
2015
|
+
networkEventTarget: null
|
|
2016
|
+
};
|
|
2017
|
+
var UnauthorizedError = class extends Error {
|
|
2018
|
+
constructor(message) {
|
|
2019
|
+
super(message);
|
|
2020
|
+
this.name = this.constructor.name;
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
var Centrifuge = class extends EventEmitter$1 {
|
|
2024
|
+
/** Constructs Centrifuge client. Call connect() method to start connecting. */
|
|
2025
|
+
constructor(endpoint, options) {
|
|
2026
|
+
super();
|
|
2027
|
+
this._reconnectTimeout = null;
|
|
2028
|
+
this._refreshTimeout = null;
|
|
2029
|
+
this._serverPingTimeout = null;
|
|
2030
|
+
this.state = State.Disconnected;
|
|
2031
|
+
this._transportIsOpen = false;
|
|
2032
|
+
this._endpoint = endpoint;
|
|
2033
|
+
this._emulation = false;
|
|
2034
|
+
this._transports = [];
|
|
2035
|
+
this._currentTransportIndex = 0;
|
|
2036
|
+
this._triedAllTransports = false;
|
|
2037
|
+
this._transportWasOpen = false;
|
|
2038
|
+
this._transport = null;
|
|
2039
|
+
this._transportId = 0;
|
|
2040
|
+
this._deviceWentOffline = false;
|
|
2041
|
+
this._transportClosed = true;
|
|
2042
|
+
this._codec = new JsonCodec();
|
|
2043
|
+
this._reconnecting = false;
|
|
2044
|
+
this._reconnectTimeout = null;
|
|
2045
|
+
this._reconnectAttempts = 0;
|
|
2046
|
+
this._client = null;
|
|
2047
|
+
this._session = "";
|
|
2048
|
+
this._node = "";
|
|
2049
|
+
this._subs = {};
|
|
2050
|
+
this._serverSubs = {};
|
|
2051
|
+
this._commandId = 0;
|
|
2052
|
+
this._commands = [];
|
|
2053
|
+
this._batching = false;
|
|
2054
|
+
this._refreshRequired = false;
|
|
2055
|
+
this._refreshTimeout = null;
|
|
2056
|
+
this._callbacks = {};
|
|
2057
|
+
this._token = "";
|
|
2058
|
+
this._data = null;
|
|
2059
|
+
this._dispatchPromise = Promise.resolve();
|
|
2060
|
+
this._serverPing = 0;
|
|
2061
|
+
this._serverPingTimeout = null;
|
|
2062
|
+
this._sendPong = false;
|
|
2063
|
+
this._promises = {};
|
|
2064
|
+
this._promiseId = 0;
|
|
2065
|
+
this._debugEnabled = false;
|
|
2066
|
+
this._networkEventsSet = false;
|
|
2067
|
+
this._config = Object.assign(Object.assign({}, defaults), options);
|
|
2068
|
+
this._configure();
|
|
2069
|
+
if (this._debugEnabled) {
|
|
2070
|
+
this.on("state", (ctx) => {
|
|
2071
|
+
this._debug("client state", ctx.oldState, "->", ctx.newState);
|
|
2072
|
+
});
|
|
2073
|
+
this.on("error", (ctx) => {
|
|
2074
|
+
this._debug("client error", ctx);
|
|
2075
|
+
});
|
|
2076
|
+
} else {
|
|
2077
|
+
this.on("error", function() {
|
|
2078
|
+
Function.prototype();
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
/** newSubscription allocates new Subscription to a channel. Since server only allows
|
|
2083
|
+
* one subscription per channel per client this method throws if client already has
|
|
2084
|
+
* channel subscription in internal registry.
|
|
2085
|
+
* */
|
|
2086
|
+
newSubscription(channel, options) {
|
|
2087
|
+
if (this.getSubscription(channel) !== null) {
|
|
2088
|
+
throw new Error("Subscription to the channel " + channel + " already exists");
|
|
2089
|
+
}
|
|
2090
|
+
const sub = new Subscription(this, channel, options);
|
|
2091
|
+
this._subs[channel] = sub;
|
|
2092
|
+
return sub;
|
|
2093
|
+
}
|
|
2094
|
+
/** getSubscription returns Subscription if it's registered in the internal
|
|
2095
|
+
* registry or null. */
|
|
2096
|
+
getSubscription(channel) {
|
|
2097
|
+
return this._getSub(channel);
|
|
2098
|
+
}
|
|
2099
|
+
/** removeSubscription allows removing Subcription from the internal registry. */
|
|
2100
|
+
removeSubscription(sub) {
|
|
2101
|
+
if (!sub) {
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
if (sub.state !== SubscriptionState.Unsubscribed) {
|
|
2105
|
+
sub.unsubscribe();
|
|
2106
|
+
}
|
|
2107
|
+
this._removeSubscription(sub);
|
|
2108
|
+
}
|
|
2109
|
+
/** Get a map with all current client-side subscriptions. */
|
|
2110
|
+
subscriptions() {
|
|
2111
|
+
return this._subs;
|
|
2112
|
+
}
|
|
2113
|
+
/** ready returns a Promise which resolves upon client goes to Connected
|
|
2114
|
+
* state and rejects in case of client goes to Disconnected or Failed state.
|
|
2115
|
+
* Users can provide optional timeout in milliseconds. */
|
|
2116
|
+
ready(timeout) {
|
|
2117
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2118
|
+
switch (this.state) {
|
|
2119
|
+
case State.Disconnected:
|
|
2120
|
+
throw { code: errorCodes.clientDisconnected, message: "client disconnected" };
|
|
2121
|
+
case State.Connected:
|
|
2122
|
+
return;
|
|
2123
|
+
default:
|
|
2124
|
+
return new Promise((resolve, reject) => {
|
|
2125
|
+
const ctx = { resolve, reject };
|
|
2126
|
+
if (timeout) {
|
|
2127
|
+
ctx.timeout = setTimeout(() => {
|
|
2128
|
+
reject({ code: errorCodes.timeout, message: "timeout" });
|
|
2129
|
+
}, timeout);
|
|
2130
|
+
}
|
|
2131
|
+
this._promises[this._nextPromiseId()] = ctx;
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
/** connect to a server. */
|
|
2137
|
+
connect() {
|
|
2138
|
+
if (this._isConnected()) {
|
|
2139
|
+
this._debug("connect called when already connected");
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
if (this._isConnecting()) {
|
|
2143
|
+
this._debug("connect called when already connecting");
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
this._debug("connect called");
|
|
2147
|
+
this._reconnectAttempts = 0;
|
|
2148
|
+
this._startConnecting();
|
|
2149
|
+
}
|
|
2150
|
+
/** disconnect from a server. */
|
|
2151
|
+
disconnect() {
|
|
2152
|
+
this._disconnect(disconnectedCodes.disconnectCalled, "disconnect called", false);
|
|
2153
|
+
}
|
|
2154
|
+
/** setToken allows setting connection token. Or resetting used token to be empty. */
|
|
2155
|
+
setToken(token) {
|
|
2156
|
+
this._token = token;
|
|
2157
|
+
}
|
|
2158
|
+
/** setData allows setting connection data. This only affects the next connection attempt,
|
|
2159
|
+
* not the current one. Note that if getData callback is configured, it will override
|
|
2160
|
+
* this value during reconnects. */
|
|
2161
|
+
setData(data) {
|
|
2162
|
+
this._data = data;
|
|
2163
|
+
}
|
|
2164
|
+
/** setHeaders allows setting connection emulated headers. */
|
|
2165
|
+
setHeaders(headers) {
|
|
2166
|
+
this._config.headers = headers;
|
|
2167
|
+
}
|
|
2168
|
+
/** send asynchronous data to a server (without any response from a server
|
|
2169
|
+
* expected, see rpc method if you need response). */
|
|
2170
|
+
send(data) {
|
|
2171
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2172
|
+
const cmd = {
|
|
2173
|
+
send: {
|
|
2174
|
+
data
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
yield this._methodCall();
|
|
2178
|
+
const sent = this._transportSendCommands([cmd]);
|
|
2179
|
+
if (!sent) {
|
|
2180
|
+
throw this._createErrorObject(errorCodes.transportWriteError, "transport write error");
|
|
2181
|
+
}
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
/** rpc to a server - i.e. a call which waits for a response with data. */
|
|
2185
|
+
rpc(method, data) {
|
|
2186
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2187
|
+
const cmd = {
|
|
2188
|
+
rpc: {
|
|
2189
|
+
method,
|
|
2190
|
+
data
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
yield this._methodCall();
|
|
2194
|
+
const result = yield this._callPromise(cmd, (reply) => reply.rpc);
|
|
2195
|
+
return {
|
|
2196
|
+
data: result.data
|
|
2197
|
+
};
|
|
2198
|
+
});
|
|
2199
|
+
}
|
|
2200
|
+
/** publish data to a channel. */
|
|
2201
|
+
publish(channel, data) {
|
|
2202
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2203
|
+
const cmd = {
|
|
2204
|
+
publish: {
|
|
2205
|
+
channel,
|
|
2206
|
+
data
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
yield this._methodCall();
|
|
2210
|
+
yield this._callPromise(cmd, () => ({}));
|
|
2211
|
+
return {};
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
/** history for a channel. By default it does not return publications (only current
|
|
2215
|
+
* StreamPosition data) – provide an explicit limit > 0 to load publications.*/
|
|
2216
|
+
history(channel, options) {
|
|
2217
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2218
|
+
const cmd = {
|
|
2219
|
+
history: this._getHistoryRequest(channel, options)
|
|
2220
|
+
};
|
|
2221
|
+
yield this._methodCall();
|
|
2222
|
+
const result = yield this._callPromise(cmd, (reply) => reply.history);
|
|
2223
|
+
const publications = [];
|
|
2224
|
+
if (result.publications) {
|
|
2225
|
+
for (let i = 0; i < result.publications.length; i++) {
|
|
2226
|
+
publications.push(this._getPublicationContext(channel, result.publications[i]));
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
return {
|
|
2230
|
+
publications,
|
|
2231
|
+
epoch: result.epoch || "",
|
|
2232
|
+
offset: result.offset || 0
|
|
2233
|
+
};
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
/** presence for a channel. */
|
|
2237
|
+
presence(channel) {
|
|
2238
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2239
|
+
const cmd = {
|
|
2240
|
+
presence: {
|
|
2241
|
+
channel
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
2244
|
+
yield this._methodCall();
|
|
2245
|
+
const result = yield this._callPromise(cmd, (reply) => reply.presence);
|
|
2246
|
+
const clients = result.presence;
|
|
2247
|
+
for (const clientId in clients) {
|
|
2248
|
+
if (Object.prototype.hasOwnProperty.call(clients, clientId)) {
|
|
2249
|
+
const rawClient = clients[clientId];
|
|
2250
|
+
const connInfo = rawClient["conn_info"];
|
|
2251
|
+
const chanInfo = rawClient["chan_info"];
|
|
2252
|
+
if (connInfo) {
|
|
2253
|
+
rawClient.connInfo = connInfo;
|
|
2254
|
+
}
|
|
2255
|
+
if (chanInfo) {
|
|
2256
|
+
rawClient.chanInfo = chanInfo;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
return { clients };
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
presenceStats(channel) {
|
|
2264
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2265
|
+
const cmd = {
|
|
2266
|
+
"presence_stats": {
|
|
2267
|
+
channel
|
|
2268
|
+
}
|
|
2269
|
+
};
|
|
2270
|
+
yield this._methodCall();
|
|
2271
|
+
const result = yield this._callPromise(cmd, (reply) => {
|
|
2272
|
+
return reply.presence_stats;
|
|
2273
|
+
});
|
|
2274
|
+
return {
|
|
2275
|
+
numUsers: result.num_users,
|
|
2276
|
+
numClients: result.num_clients
|
|
2277
|
+
};
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
/** start command batching (collect into temporary buffer without sending to a server)
|
|
2281
|
+
* until stopBatching called.*/
|
|
2282
|
+
startBatching() {
|
|
2283
|
+
this._batching = true;
|
|
2284
|
+
}
|
|
2285
|
+
/** stop batching commands and flush collected commands to the
|
|
2286
|
+
* network (all in one request/frame).*/
|
|
2287
|
+
stopBatching() {
|
|
2288
|
+
const self = this;
|
|
2289
|
+
Promise.resolve().then(function() {
|
|
2290
|
+
Promise.resolve().then(function() {
|
|
2291
|
+
self._batching = false;
|
|
2292
|
+
self._flush();
|
|
2293
|
+
});
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
_debug(...args) {
|
|
2297
|
+
if (!this._debugEnabled) {
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
log("debug", args);
|
|
2301
|
+
}
|
|
2302
|
+
_codecName() {
|
|
2303
|
+
return this._codec.name();
|
|
2304
|
+
}
|
|
2305
|
+
/** @internal */
|
|
2306
|
+
_formatOverride() {
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
_configure() {
|
|
2310
|
+
if (!("Promise" in globalThis)) {
|
|
2311
|
+
throw new Error("Promise polyfill required");
|
|
2312
|
+
}
|
|
2313
|
+
if (!this._endpoint) {
|
|
2314
|
+
throw new Error("endpoint configuration required");
|
|
2315
|
+
}
|
|
2316
|
+
if (this._config.token !== null) {
|
|
2317
|
+
this._token = this._config.token;
|
|
2318
|
+
}
|
|
2319
|
+
if (this._config.data !== null) {
|
|
2320
|
+
this._data = this._config.data;
|
|
2321
|
+
}
|
|
2322
|
+
this._codec = new JsonCodec();
|
|
2323
|
+
this._formatOverride();
|
|
2324
|
+
if (this._config.debug === true || typeof localStorage !== "undefined" && typeof localStorage.getItem === "function" && localStorage.getItem("centrifuge.debug")) {
|
|
2325
|
+
this._debugEnabled = true;
|
|
2326
|
+
}
|
|
2327
|
+
this._debug("config", this._config);
|
|
2328
|
+
if (typeof this._endpoint === "string") ;
|
|
2329
|
+
else if (Array.isArray(this._endpoint)) {
|
|
2330
|
+
this._transports = this._endpoint;
|
|
2331
|
+
this._emulation = true;
|
|
2332
|
+
for (const i in this._transports) {
|
|
2333
|
+
if (this._transports.hasOwnProperty(i)) {
|
|
2334
|
+
const transportConfig = this._transports[i];
|
|
2335
|
+
if (!transportConfig.endpoint || !transportConfig.transport) {
|
|
2336
|
+
throw new Error("malformed transport configuration");
|
|
2337
|
+
}
|
|
2338
|
+
const transportName = transportConfig.transport;
|
|
2339
|
+
if (["websocket", "http_stream", "sse", "sockjs", "webtransport"].indexOf(transportName) < 0) {
|
|
2340
|
+
throw new Error("unsupported transport name: " + transportName);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
} else {
|
|
2345
|
+
throw new Error("unsupported url configuration type: only string or array of objects are supported");
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
_setState(newState) {
|
|
2349
|
+
if (this.state !== newState) {
|
|
2350
|
+
this._reconnecting = false;
|
|
2351
|
+
const oldState = this.state;
|
|
2352
|
+
this.state = newState;
|
|
2353
|
+
this.emit("state", { newState, oldState });
|
|
2354
|
+
return true;
|
|
2355
|
+
}
|
|
2356
|
+
return false;
|
|
2357
|
+
}
|
|
2358
|
+
_isDisconnected() {
|
|
2359
|
+
return this.state === State.Disconnected;
|
|
2360
|
+
}
|
|
2361
|
+
_isConnecting() {
|
|
2362
|
+
return this.state === State.Connecting;
|
|
2363
|
+
}
|
|
2364
|
+
_isConnected() {
|
|
2365
|
+
return this.state === State.Connected;
|
|
2366
|
+
}
|
|
2367
|
+
_nextCommandId() {
|
|
2368
|
+
return ++this._commandId;
|
|
2369
|
+
}
|
|
2370
|
+
_setNetworkEvents() {
|
|
2371
|
+
if (this._networkEventsSet) {
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
let eventTarget = null;
|
|
2375
|
+
if (this._config.networkEventTarget !== null) {
|
|
2376
|
+
eventTarget = this._config.networkEventTarget;
|
|
2377
|
+
} else if (typeof globalThis.addEventListener !== "undefined") {
|
|
2378
|
+
eventTarget = globalThis;
|
|
2379
|
+
}
|
|
2380
|
+
if (eventTarget) {
|
|
2381
|
+
eventTarget.addEventListener("offline", () => {
|
|
2382
|
+
this._debug("offline event triggered");
|
|
2383
|
+
if (this.state === State.Connected || this.state === State.Connecting) {
|
|
2384
|
+
this._disconnect(connectingCodes.transportClosed, "transport closed", true);
|
|
2385
|
+
this._deviceWentOffline = true;
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
eventTarget.addEventListener("online", () => {
|
|
2389
|
+
this._debug("online event triggered");
|
|
2390
|
+
if (this.state !== State.Connecting) {
|
|
2391
|
+
return;
|
|
2392
|
+
}
|
|
2393
|
+
if (this._deviceWentOffline && !this._transportClosed) {
|
|
2394
|
+
this._deviceWentOffline = false;
|
|
2395
|
+
this._transportClosed = true;
|
|
2396
|
+
}
|
|
2397
|
+
this._clearReconnectTimeout();
|
|
2398
|
+
this._startReconnecting();
|
|
2399
|
+
});
|
|
2400
|
+
this._networkEventsSet = true;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
_getReconnectDelay() {
|
|
2404
|
+
const delay = backoff(this._reconnectAttempts, this._config.minReconnectDelay, this._config.maxReconnectDelay);
|
|
2405
|
+
this._reconnectAttempts += 1;
|
|
2406
|
+
return delay;
|
|
2407
|
+
}
|
|
2408
|
+
_clearOutgoingRequests() {
|
|
2409
|
+
for (const id in this._callbacks) {
|
|
2410
|
+
if (this._callbacks.hasOwnProperty(id)) {
|
|
2411
|
+
const callbacks = this._callbacks[id];
|
|
2412
|
+
clearTimeout(callbacks.timeout);
|
|
2413
|
+
const errback = callbacks.errback;
|
|
2414
|
+
if (!errback) {
|
|
2415
|
+
continue;
|
|
2416
|
+
}
|
|
2417
|
+
errback({ error: this._createErrorObject(errorCodes.connectionClosed, "connection closed") });
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
this._callbacks = {};
|
|
2421
|
+
}
|
|
2422
|
+
_clearConnectedState() {
|
|
2423
|
+
this._client = null;
|
|
2424
|
+
this._clearServerPingTimeout();
|
|
2425
|
+
this._clearRefreshTimeout();
|
|
2426
|
+
for (const channel in this._subs) {
|
|
2427
|
+
if (!this._subs.hasOwnProperty(channel)) {
|
|
2428
|
+
continue;
|
|
2429
|
+
}
|
|
2430
|
+
const sub = this._subs[channel];
|
|
2431
|
+
if (sub.state === SubscriptionState.Subscribed) {
|
|
2432
|
+
sub._setSubscribing(subscribingCodes.transportClosed, "transport closed");
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
for (const channel in this._serverSubs) {
|
|
2436
|
+
if (this._serverSubs.hasOwnProperty(channel)) {
|
|
2437
|
+
this.emit("subscribing", { channel });
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
_handleWriteError(commands) {
|
|
2442
|
+
for (const command of commands) {
|
|
2443
|
+
const id = command.id;
|
|
2444
|
+
if (!(id in this._callbacks)) {
|
|
2445
|
+
continue;
|
|
2446
|
+
}
|
|
2447
|
+
const callbacks = this._callbacks[id];
|
|
2448
|
+
clearTimeout(this._callbacks[id].timeout);
|
|
2449
|
+
delete this._callbacks[id];
|
|
2450
|
+
const errback = callbacks.errback;
|
|
2451
|
+
errback({ error: this._createErrorObject(errorCodes.transportWriteError, "transport write error") });
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
_transportSendCommands(commands) {
|
|
2455
|
+
if (!commands.length) {
|
|
2456
|
+
return true;
|
|
2457
|
+
}
|
|
2458
|
+
if (!this._transport) {
|
|
2459
|
+
return false;
|
|
2460
|
+
}
|
|
2461
|
+
try {
|
|
2462
|
+
this._transport.send(this._codec.encodeCommands(commands), this._session, this._node);
|
|
2463
|
+
} catch (e) {
|
|
2464
|
+
this._debug("error writing commands", e);
|
|
2465
|
+
this._handleWriteError(commands);
|
|
2466
|
+
return false;
|
|
2467
|
+
}
|
|
2468
|
+
return true;
|
|
2469
|
+
}
|
|
2470
|
+
_initializeTransport() {
|
|
2471
|
+
let websocket;
|
|
2472
|
+
if (this._config.websocket !== null) {
|
|
2473
|
+
websocket = this._config.websocket;
|
|
2474
|
+
} else {
|
|
2475
|
+
if (!(typeof globalThis.WebSocket !== "function" && typeof globalThis.WebSocket !== "object")) {
|
|
2476
|
+
websocket = globalThis.WebSocket;
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
let sockjs = null;
|
|
2480
|
+
if (this._config.sockjs !== null) {
|
|
2481
|
+
sockjs = this._config.sockjs;
|
|
2482
|
+
} else {
|
|
2483
|
+
if (typeof globalThis.SockJS !== "undefined") {
|
|
2484
|
+
sockjs = globalThis.SockJS;
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
let eventsource = null;
|
|
2488
|
+
if (this._config.eventsource !== null) {
|
|
2489
|
+
eventsource = this._config.eventsource;
|
|
2490
|
+
} else {
|
|
2491
|
+
if (typeof globalThis.EventSource !== "undefined") {
|
|
2492
|
+
eventsource = globalThis.EventSource;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
let fetchFunc = null;
|
|
2496
|
+
if (this._config.fetch !== null) {
|
|
2497
|
+
fetchFunc = this._config.fetch;
|
|
2498
|
+
} else {
|
|
2499
|
+
if (typeof globalThis.fetch !== "undefined") {
|
|
2500
|
+
fetchFunc = globalThis.fetch;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
let readableStream = null;
|
|
2504
|
+
if (this._config.readableStream !== null) {
|
|
2505
|
+
readableStream = this._config.readableStream;
|
|
2506
|
+
} else {
|
|
2507
|
+
if (typeof globalThis.ReadableStream !== "undefined") {
|
|
2508
|
+
readableStream = globalThis.ReadableStream;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
if (!this._emulation) {
|
|
2512
|
+
if (startsWith(this._endpoint, "http")) {
|
|
2513
|
+
throw new Error("Provide explicit transport endpoints configuration in case of using HTTP (i.e. using array of TransportEndpoint instead of a single string), or use ws(s):// scheme in an endpoint if you aimed using WebSocket transport");
|
|
2514
|
+
} else {
|
|
2515
|
+
this._debug("client will use websocket");
|
|
2516
|
+
this._transport = new WebsocketTransport(this._endpoint, {
|
|
2517
|
+
websocket
|
|
2518
|
+
});
|
|
2519
|
+
if (!this._transport.supported()) {
|
|
2520
|
+
throw new Error("WebSocket constructor not found, make sure it is available globally or passed as a dependency in Centrifuge options");
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
} else {
|
|
2524
|
+
if (this._currentTransportIndex >= this._transports.length) {
|
|
2525
|
+
this._triedAllTransports = true;
|
|
2526
|
+
this._currentTransportIndex = 0;
|
|
2527
|
+
}
|
|
2528
|
+
let count = 0;
|
|
2529
|
+
while (true) {
|
|
2530
|
+
if (count >= this._transports.length) {
|
|
2531
|
+
throw new Error("no supported transport found");
|
|
2532
|
+
}
|
|
2533
|
+
const transportConfig = this._transports[this._currentTransportIndex];
|
|
2534
|
+
const transportName = transportConfig.transport;
|
|
2535
|
+
const transportEndpoint = transportConfig.endpoint;
|
|
2536
|
+
if (transportName === "websocket") {
|
|
2537
|
+
this._debug("trying websocket transport");
|
|
2538
|
+
this._transport = new WebsocketTransport(transportEndpoint, {
|
|
2539
|
+
websocket
|
|
2540
|
+
});
|
|
2541
|
+
if (!this._transport.supported()) {
|
|
2542
|
+
this._debug("websocket transport not available");
|
|
2543
|
+
this._currentTransportIndex++;
|
|
2544
|
+
count++;
|
|
2545
|
+
continue;
|
|
2546
|
+
}
|
|
2547
|
+
} else if (transportName === "webtransport") {
|
|
2548
|
+
this._debug("trying webtransport transport");
|
|
2549
|
+
this._transport = new WebtransportTransport(transportEndpoint, {
|
|
2550
|
+
webtransport: globalThis.WebTransport,
|
|
2551
|
+
decoder: this._codec,
|
|
2552
|
+
encoder: this._codec
|
|
2553
|
+
});
|
|
2554
|
+
if (!this._transport.supported()) {
|
|
2555
|
+
this._debug("webtransport transport not available");
|
|
2556
|
+
this._currentTransportIndex++;
|
|
2557
|
+
count++;
|
|
2558
|
+
continue;
|
|
2559
|
+
}
|
|
2560
|
+
} else if (transportName === "http_stream") {
|
|
2561
|
+
this._debug("trying http_stream transport");
|
|
2562
|
+
this._transport = new HttpStreamTransport(transportEndpoint, {
|
|
2563
|
+
fetch: fetchFunc,
|
|
2564
|
+
readableStream,
|
|
2565
|
+
emulationEndpoint: this._config.emulationEndpoint,
|
|
2566
|
+
decoder: this._codec,
|
|
2567
|
+
encoder: this._codec
|
|
2568
|
+
});
|
|
2569
|
+
if (!this._transport.supported()) {
|
|
2570
|
+
this._debug("http_stream transport not available");
|
|
2571
|
+
this._currentTransportIndex++;
|
|
2572
|
+
count++;
|
|
2573
|
+
continue;
|
|
2574
|
+
}
|
|
2575
|
+
} else if (transportName === "sse") {
|
|
2576
|
+
this._debug("trying sse transport");
|
|
2577
|
+
this._transport = new SseTransport(transportEndpoint, {
|
|
2578
|
+
eventsource,
|
|
2579
|
+
fetch: fetchFunc,
|
|
2580
|
+
emulationEndpoint: this._config.emulationEndpoint
|
|
2581
|
+
});
|
|
2582
|
+
if (!this._transport.supported()) {
|
|
2583
|
+
this._debug("sse transport not available");
|
|
2584
|
+
this._currentTransportIndex++;
|
|
2585
|
+
count++;
|
|
2586
|
+
continue;
|
|
2587
|
+
}
|
|
2588
|
+
} else if (transportName === "sockjs") {
|
|
2589
|
+
this._debug("trying sockjs");
|
|
2590
|
+
this._transport = new SockjsTransport(transportEndpoint, {
|
|
2591
|
+
sockjs,
|
|
2592
|
+
sockjsOptions: this._config.sockjsOptions
|
|
2593
|
+
});
|
|
2594
|
+
if (!this._transport.supported()) {
|
|
2595
|
+
this._debug("sockjs transport not available");
|
|
2596
|
+
this._currentTransportIndex++;
|
|
2597
|
+
count++;
|
|
2598
|
+
continue;
|
|
2599
|
+
}
|
|
2600
|
+
} else {
|
|
2601
|
+
throw new Error("unknown transport " + transportName);
|
|
2602
|
+
}
|
|
2603
|
+
break;
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
const self = this;
|
|
2607
|
+
const transport = this._transport;
|
|
2608
|
+
const transportId = this._nextTransportId();
|
|
2609
|
+
self._debug("id of transport", transportId);
|
|
2610
|
+
let wasOpen = false;
|
|
2611
|
+
const initialCommands = [];
|
|
2612
|
+
if (this._transport.emulation()) {
|
|
2613
|
+
const connectCommand = self._sendConnect(true);
|
|
2614
|
+
initialCommands.push(connectCommand);
|
|
2615
|
+
}
|
|
2616
|
+
this._setNetworkEvents();
|
|
2617
|
+
const initialData = this._codec.encodeCommands(initialCommands);
|
|
2618
|
+
this._transportClosed = false;
|
|
2619
|
+
let connectTimeout;
|
|
2620
|
+
connectTimeout = setTimeout(function() {
|
|
2621
|
+
transport.close();
|
|
2622
|
+
}, this._config.timeout);
|
|
2623
|
+
this._transport.initialize(this._codecName(), {
|
|
2624
|
+
onOpen: function() {
|
|
2625
|
+
if (connectTimeout) {
|
|
2626
|
+
clearTimeout(connectTimeout);
|
|
2627
|
+
connectTimeout = null;
|
|
2628
|
+
}
|
|
2629
|
+
if (self._transportId != transportId) {
|
|
2630
|
+
self._debug("open callback from non-actual transport");
|
|
2631
|
+
transport.close();
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
wasOpen = true;
|
|
2635
|
+
self._debug(transport.subName(), "transport open");
|
|
2636
|
+
if (transport.emulation()) {
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
self._transportIsOpen = true;
|
|
2640
|
+
self._transportWasOpen = true;
|
|
2641
|
+
self.startBatching();
|
|
2642
|
+
self._sendConnect(false);
|
|
2643
|
+
self._sendSubscribeCommands();
|
|
2644
|
+
self.stopBatching();
|
|
2645
|
+
self.emit("__centrifuge_debug:connect_frame_sent", {});
|
|
2646
|
+
},
|
|
2647
|
+
onError: function(e) {
|
|
2648
|
+
if (self._transportId != transportId) {
|
|
2649
|
+
self._debug("error callback from non-actual transport");
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
self._debug("transport level error", e);
|
|
2653
|
+
},
|
|
2654
|
+
onClose: function(closeEvent) {
|
|
2655
|
+
if (connectTimeout) {
|
|
2656
|
+
clearTimeout(connectTimeout);
|
|
2657
|
+
connectTimeout = null;
|
|
2658
|
+
}
|
|
2659
|
+
if (self._transportId != transportId) {
|
|
2660
|
+
self._debug("close callback from non-actual transport");
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
self._debug(transport.subName(), "transport closed");
|
|
2664
|
+
self._transportClosed = true;
|
|
2665
|
+
self._transportIsOpen = false;
|
|
2666
|
+
let reason = "connection closed";
|
|
2667
|
+
let needReconnect = true;
|
|
2668
|
+
let code = 0;
|
|
2669
|
+
if (closeEvent && "code" in closeEvent && closeEvent.code) {
|
|
2670
|
+
code = closeEvent.code;
|
|
2671
|
+
}
|
|
2672
|
+
if (closeEvent && closeEvent.reason) {
|
|
2673
|
+
try {
|
|
2674
|
+
const advice = JSON.parse(closeEvent.reason);
|
|
2675
|
+
reason = advice.reason;
|
|
2676
|
+
needReconnect = advice.reconnect;
|
|
2677
|
+
} catch (e) {
|
|
2678
|
+
reason = closeEvent.reason;
|
|
2679
|
+
if (code >= 3500 && code < 4e3 || code >= 4500 && code < 5e3) {
|
|
2680
|
+
needReconnect = false;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
if (code < 3e3) {
|
|
2685
|
+
if (code === 1009) {
|
|
2686
|
+
code = disconnectedCodes.messageSizeLimit;
|
|
2687
|
+
reason = "message size limit exceeded";
|
|
2688
|
+
needReconnect = false;
|
|
2689
|
+
} else {
|
|
2690
|
+
code = connectingCodes.transportClosed;
|
|
2691
|
+
reason = "transport closed";
|
|
2692
|
+
}
|
|
2693
|
+
if (self._emulation && !self._transportWasOpen) {
|
|
2694
|
+
self._currentTransportIndex++;
|
|
2695
|
+
if (self._currentTransportIndex >= self._transports.length) {
|
|
2696
|
+
self._triedAllTransports = true;
|
|
2697
|
+
self._currentTransportIndex = 0;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
} else {
|
|
2701
|
+
self._transportWasOpen = true;
|
|
2702
|
+
}
|
|
2703
|
+
if (self._isConnecting() && !wasOpen) {
|
|
2704
|
+
self.emit("error", {
|
|
2705
|
+
type: "transport",
|
|
2706
|
+
error: {
|
|
2707
|
+
code: errorCodes.transportClosed,
|
|
2708
|
+
message: "transport closed"
|
|
2709
|
+
},
|
|
2710
|
+
transport: transport.name()
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
self._reconnecting = false;
|
|
2714
|
+
self._disconnect(code, reason, needReconnect);
|
|
2715
|
+
},
|
|
2716
|
+
onMessage: function(data) {
|
|
2717
|
+
self._dataReceived(data);
|
|
2718
|
+
}
|
|
2719
|
+
}, initialData);
|
|
2720
|
+
self.emit("__centrifuge_debug:transport_initialized", {});
|
|
2721
|
+
}
|
|
2722
|
+
_sendConnect(skipSending) {
|
|
2723
|
+
const connectCommand = this._constructConnectCommand();
|
|
2724
|
+
const self = this;
|
|
2725
|
+
this._call(connectCommand, skipSending).then((resolveCtx) => {
|
|
2726
|
+
const result = resolveCtx.reply.connect;
|
|
2727
|
+
self._connectResponse(result);
|
|
2728
|
+
if (resolveCtx.next) {
|
|
2729
|
+
resolveCtx.next();
|
|
2730
|
+
}
|
|
2731
|
+
}, (rejectCtx) => {
|
|
2732
|
+
self._connectError(rejectCtx.error);
|
|
2733
|
+
if (rejectCtx.next) {
|
|
2734
|
+
rejectCtx.next();
|
|
2735
|
+
}
|
|
2736
|
+
});
|
|
2737
|
+
return connectCommand;
|
|
2738
|
+
}
|
|
2739
|
+
_startReconnecting() {
|
|
2740
|
+
this._debug("start reconnecting");
|
|
2741
|
+
if (!this._isConnecting()) {
|
|
2742
|
+
this._debug("stop reconnecting: client not in connecting state");
|
|
2743
|
+
return;
|
|
2744
|
+
}
|
|
2745
|
+
if (this._reconnecting) {
|
|
2746
|
+
this._debug("reconnect already in progress, return from reconnect routine");
|
|
2747
|
+
return;
|
|
2748
|
+
}
|
|
2749
|
+
if (this._transportClosed === false) {
|
|
2750
|
+
this._debug("waiting for transport close");
|
|
2751
|
+
return;
|
|
2752
|
+
}
|
|
2753
|
+
this._reconnecting = true;
|
|
2754
|
+
const emptyToken = this._token === "";
|
|
2755
|
+
const needTokenRefresh = this._refreshRequired || emptyToken && this._config.getToken !== null;
|
|
2756
|
+
if (!needTokenRefresh) {
|
|
2757
|
+
if (this._config.getData) {
|
|
2758
|
+
this._config.getData().then((data) => {
|
|
2759
|
+
if (!this._isConnecting()) {
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
this._data = data;
|
|
2763
|
+
this._initializeTransport();
|
|
2764
|
+
}).catch((e) => this._handleGetDataError(e));
|
|
2765
|
+
} else {
|
|
2766
|
+
this._initializeTransport();
|
|
2767
|
+
}
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
const self = this;
|
|
2771
|
+
this._getToken().then(function(token) {
|
|
2772
|
+
if (!self._isConnecting()) {
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
if (token == null || token == void 0) {
|
|
2776
|
+
self._failUnauthorized();
|
|
2777
|
+
return;
|
|
2778
|
+
}
|
|
2779
|
+
self._token = token;
|
|
2780
|
+
self._debug("connection token refreshed");
|
|
2781
|
+
if (self._config.getData) {
|
|
2782
|
+
self._config.getData().then(function(data) {
|
|
2783
|
+
if (!self._isConnecting()) {
|
|
2784
|
+
return;
|
|
2785
|
+
}
|
|
2786
|
+
self._data = data;
|
|
2787
|
+
self._initializeTransport();
|
|
2788
|
+
}).catch((e) => self._handleGetDataError(e));
|
|
2789
|
+
} else {
|
|
2790
|
+
self._initializeTransport();
|
|
2791
|
+
}
|
|
2792
|
+
}).catch(function(e) {
|
|
2793
|
+
if (!self._isConnecting()) {
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
if (e instanceof UnauthorizedError) {
|
|
2797
|
+
self._failUnauthorized();
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
self.emit("error", {
|
|
2801
|
+
"type": "connectToken",
|
|
2802
|
+
"error": {
|
|
2803
|
+
code: errorCodes.clientConnectToken,
|
|
2804
|
+
message: e !== void 0 ? e.toString() : ""
|
|
2805
|
+
}
|
|
2806
|
+
});
|
|
2807
|
+
const delay = self._getReconnectDelay();
|
|
2808
|
+
self._debug("error on getting connection token, reconnect after " + delay + " milliseconds", e);
|
|
2809
|
+
self._reconnecting = false;
|
|
2810
|
+
self._reconnectTimeout = setTimeout(() => {
|
|
2811
|
+
self._startReconnecting();
|
|
2812
|
+
}, delay);
|
|
2813
|
+
});
|
|
2814
|
+
}
|
|
2815
|
+
_handleGetDataError(e) {
|
|
2816
|
+
if (e instanceof UnauthorizedError) {
|
|
2817
|
+
this._failUnauthorized();
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
this.emit("error", {
|
|
2821
|
+
type: "connectData",
|
|
2822
|
+
error: {
|
|
2823
|
+
code: errorCodes.badConfiguration,
|
|
2824
|
+
message: (e === null || e === void 0 ? void 0 : e.toString()) || ""
|
|
2825
|
+
}
|
|
2826
|
+
});
|
|
2827
|
+
const delay = this._getReconnectDelay();
|
|
2828
|
+
this._debug("error on getting connect data, reconnect after " + delay + " milliseconds", e);
|
|
2829
|
+
this._reconnecting = false;
|
|
2830
|
+
this._reconnectTimeout = setTimeout(() => {
|
|
2831
|
+
this._startReconnecting();
|
|
2832
|
+
}, delay);
|
|
2833
|
+
}
|
|
2834
|
+
_connectError(err) {
|
|
2835
|
+
if (this.state !== State.Connecting) {
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
if (err.code === 109) {
|
|
2839
|
+
this._refreshRequired = true;
|
|
2840
|
+
}
|
|
2841
|
+
if (err.code < 100 || err.temporary === true || err.code === 109) {
|
|
2842
|
+
this.emit("error", {
|
|
2843
|
+
"type": "connect",
|
|
2844
|
+
"error": err
|
|
2845
|
+
});
|
|
2846
|
+
this._debug("closing transport due to connect error");
|
|
2847
|
+
this._disconnect(err.code, err.message, true);
|
|
2848
|
+
} else {
|
|
2849
|
+
this._disconnect(err.code, err.message, false);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
_scheduleReconnect() {
|
|
2853
|
+
if (!this._isConnecting()) {
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
let isInitialHandshake = false;
|
|
2857
|
+
if (this._emulation && !this._transportWasOpen && !this._triedAllTransports) {
|
|
2858
|
+
isInitialHandshake = true;
|
|
2859
|
+
}
|
|
2860
|
+
let delay = this._getReconnectDelay();
|
|
2861
|
+
if (isInitialHandshake) {
|
|
2862
|
+
delay = 0;
|
|
2863
|
+
}
|
|
2864
|
+
this._debug("reconnect after " + delay + " milliseconds");
|
|
2865
|
+
this._clearReconnectTimeout();
|
|
2866
|
+
this._reconnectTimeout = setTimeout(() => {
|
|
2867
|
+
this._startReconnecting();
|
|
2868
|
+
}, delay);
|
|
2869
|
+
}
|
|
2870
|
+
_constructConnectCommand() {
|
|
2871
|
+
const req = {};
|
|
2872
|
+
if (this._token) {
|
|
2873
|
+
req.token = this._token;
|
|
2874
|
+
}
|
|
2875
|
+
if (this._data) {
|
|
2876
|
+
req.data = this._data;
|
|
2877
|
+
}
|
|
2878
|
+
if (this._config.name) {
|
|
2879
|
+
req.name = this._config.name;
|
|
2880
|
+
}
|
|
2881
|
+
if (this._config.version) {
|
|
2882
|
+
req.version = this._config.version;
|
|
2883
|
+
}
|
|
2884
|
+
if (Object.keys(this._config.headers).length > 0) {
|
|
2885
|
+
req.headers = this._config.headers;
|
|
2886
|
+
}
|
|
2887
|
+
const subs = {};
|
|
2888
|
+
let hasSubs = false;
|
|
2889
|
+
for (const channel in this._serverSubs) {
|
|
2890
|
+
if (this._serverSubs.hasOwnProperty(channel) && this._serverSubs[channel].recoverable) {
|
|
2891
|
+
hasSubs = true;
|
|
2892
|
+
const sub = {
|
|
2893
|
+
"recover": true
|
|
2894
|
+
};
|
|
2895
|
+
if (this._serverSubs[channel].offset) {
|
|
2896
|
+
sub["offset"] = this._serverSubs[channel].offset;
|
|
2897
|
+
}
|
|
2898
|
+
if (this._serverSubs[channel].epoch) {
|
|
2899
|
+
sub["epoch"] = this._serverSubs[channel].epoch;
|
|
2900
|
+
}
|
|
2901
|
+
subs[channel] = sub;
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
if (hasSubs) {
|
|
2905
|
+
req.subs = subs;
|
|
2906
|
+
}
|
|
2907
|
+
return {
|
|
2908
|
+
connect: req
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
_getHistoryRequest(channel, options) {
|
|
2912
|
+
const req = {
|
|
2913
|
+
channel
|
|
2914
|
+
};
|
|
2915
|
+
if (options !== void 0) {
|
|
2916
|
+
if (options.since) {
|
|
2917
|
+
req.since = {
|
|
2918
|
+
offset: options.since.offset
|
|
2919
|
+
};
|
|
2920
|
+
if (options.since.epoch) {
|
|
2921
|
+
req.since.epoch = options.since.epoch;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
if (options.limit !== void 0) {
|
|
2925
|
+
req.limit = options.limit;
|
|
2926
|
+
}
|
|
2927
|
+
if (options.reverse === true) {
|
|
2928
|
+
req.reverse = true;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
return req;
|
|
2932
|
+
}
|
|
2933
|
+
_methodCall() {
|
|
2934
|
+
if (this._isConnected()) {
|
|
2935
|
+
return Promise.resolve();
|
|
2936
|
+
}
|
|
2937
|
+
return new Promise((res, rej) => {
|
|
2938
|
+
const timeout = setTimeout(function() {
|
|
2939
|
+
rej({ code: errorCodes.timeout, message: "timeout" });
|
|
2940
|
+
}, this._config.timeout);
|
|
2941
|
+
this._promises[this._nextPromiseId()] = {
|
|
2942
|
+
timeout,
|
|
2943
|
+
resolve: res,
|
|
2944
|
+
reject: rej
|
|
2945
|
+
};
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
_callPromise(cmd, resultCB) {
|
|
2949
|
+
return new Promise((resolve, reject) => {
|
|
2950
|
+
this._call(cmd, false).then((resolveCtx) => {
|
|
2951
|
+
var _a;
|
|
2952
|
+
const result = resultCB(resolveCtx.reply);
|
|
2953
|
+
resolve(result);
|
|
2954
|
+
(_a = resolveCtx.next) === null || _a === void 0 ? void 0 : _a.call(resolveCtx);
|
|
2955
|
+
}, (rejectCtx) => {
|
|
2956
|
+
var _a;
|
|
2957
|
+
reject(rejectCtx.error);
|
|
2958
|
+
(_a = rejectCtx.next) === null || _a === void 0 ? void 0 : _a.call(rejectCtx);
|
|
2959
|
+
});
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
_dataReceived(data) {
|
|
2963
|
+
if (this._serverPing > 0) {
|
|
2964
|
+
this._waitServerPing();
|
|
2965
|
+
}
|
|
2966
|
+
const replies = this._codec.decodeReplies(data);
|
|
2967
|
+
this._dispatchPromise = this._dispatchPromise.then(() => {
|
|
2968
|
+
let finishDispatch;
|
|
2969
|
+
this._dispatchPromise = new Promise((resolve) => {
|
|
2970
|
+
finishDispatch = resolve;
|
|
2971
|
+
});
|
|
2972
|
+
this._dispatchSynchronized(replies, finishDispatch);
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
_dispatchSynchronized(replies, finishDispatch) {
|
|
2976
|
+
let p = Promise.resolve();
|
|
2977
|
+
for (const i in replies) {
|
|
2978
|
+
if (replies.hasOwnProperty(i)) {
|
|
2979
|
+
p = p.then(() => {
|
|
2980
|
+
return this._dispatchReply(replies[i]);
|
|
2981
|
+
});
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
p = p.then(() => {
|
|
2985
|
+
finishDispatch();
|
|
2986
|
+
});
|
|
2987
|
+
}
|
|
2988
|
+
_dispatchReply(reply) {
|
|
2989
|
+
let next;
|
|
2990
|
+
const p = new Promise((resolve) => {
|
|
2991
|
+
next = resolve;
|
|
2992
|
+
});
|
|
2993
|
+
if (reply === void 0 || reply === null) {
|
|
2994
|
+
this._debug("dispatch: got undefined or null reply");
|
|
2995
|
+
next();
|
|
2996
|
+
return p;
|
|
2997
|
+
}
|
|
2998
|
+
const id = reply.id;
|
|
2999
|
+
if (id && id > 0) {
|
|
3000
|
+
this._handleReply(reply, next);
|
|
3001
|
+
} else {
|
|
3002
|
+
if (!reply.push) {
|
|
3003
|
+
this._handleServerPing(next);
|
|
3004
|
+
} else {
|
|
3005
|
+
this._handlePush(reply.push, next);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
return p;
|
|
3009
|
+
}
|
|
3010
|
+
_call(cmd, skipSending) {
|
|
3011
|
+
return new Promise((resolve, reject) => {
|
|
3012
|
+
cmd.id = this._nextCommandId();
|
|
3013
|
+
this._registerCall(cmd.id, resolve, reject);
|
|
3014
|
+
if (!skipSending) {
|
|
3015
|
+
this._addCommand(cmd);
|
|
3016
|
+
}
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
_startConnecting() {
|
|
3020
|
+
this._debug("start connecting");
|
|
3021
|
+
if (this._setState(State.Connecting)) {
|
|
3022
|
+
this.emit("connecting", { code: connectingCodes.connectCalled, reason: "connect called" });
|
|
3023
|
+
}
|
|
3024
|
+
this._client = null;
|
|
3025
|
+
this._startReconnecting();
|
|
3026
|
+
}
|
|
3027
|
+
_disconnect(code, reason, reconnect) {
|
|
3028
|
+
if (this._isDisconnected()) {
|
|
3029
|
+
return;
|
|
3030
|
+
}
|
|
3031
|
+
this._transportIsOpen = false;
|
|
3032
|
+
const previousState = this.state;
|
|
3033
|
+
this._reconnecting = false;
|
|
3034
|
+
const ctx = {
|
|
3035
|
+
code,
|
|
3036
|
+
reason
|
|
3037
|
+
};
|
|
3038
|
+
let needEvent = false;
|
|
3039
|
+
if (reconnect) {
|
|
3040
|
+
needEvent = this._setState(State.Connecting);
|
|
3041
|
+
} else {
|
|
3042
|
+
needEvent = this._setState(State.Disconnected);
|
|
3043
|
+
this._rejectPromises({ code: errorCodes.clientDisconnected, message: "disconnected" });
|
|
3044
|
+
}
|
|
3045
|
+
this._clearOutgoingRequests();
|
|
3046
|
+
if (previousState === State.Connecting) {
|
|
3047
|
+
this._clearReconnectTimeout();
|
|
3048
|
+
}
|
|
3049
|
+
if (previousState === State.Connected) {
|
|
3050
|
+
this._clearConnectedState();
|
|
3051
|
+
}
|
|
3052
|
+
if (needEvent) {
|
|
3053
|
+
if (this._isConnecting()) {
|
|
3054
|
+
this.emit("connecting", ctx);
|
|
3055
|
+
} else {
|
|
3056
|
+
this.emit("disconnected", ctx);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
if (this._transport) {
|
|
3060
|
+
this._debug("closing existing transport");
|
|
3061
|
+
const transport = this._transport;
|
|
3062
|
+
this._transport = null;
|
|
3063
|
+
transport.close();
|
|
3064
|
+
this._transportClosed = true;
|
|
3065
|
+
this._nextTransportId();
|
|
3066
|
+
} else {
|
|
3067
|
+
this._debug("no transport to close");
|
|
3068
|
+
}
|
|
3069
|
+
this._scheduleReconnect();
|
|
3070
|
+
}
|
|
3071
|
+
_failUnauthorized() {
|
|
3072
|
+
this._disconnect(disconnectedCodes.unauthorized, "unauthorized", false);
|
|
3073
|
+
}
|
|
3074
|
+
_getToken() {
|
|
3075
|
+
this._debug("get connection token");
|
|
3076
|
+
if (!this._config.getToken) {
|
|
3077
|
+
this.emit("error", {
|
|
3078
|
+
type: "configuration",
|
|
3079
|
+
error: {
|
|
3080
|
+
code: errorCodes.badConfiguration,
|
|
3081
|
+
message: "token expired but no getToken function set in the configuration"
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
return Promise.reject(new UnauthorizedError(""));
|
|
3085
|
+
}
|
|
3086
|
+
return this._config.getToken({});
|
|
3087
|
+
}
|
|
3088
|
+
_refresh() {
|
|
3089
|
+
const clientId = this._client;
|
|
3090
|
+
const self = this;
|
|
3091
|
+
this._getToken().then(function(token) {
|
|
3092
|
+
if (clientId !== self._client) {
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
if (!token) {
|
|
3096
|
+
self._failUnauthorized();
|
|
3097
|
+
return;
|
|
3098
|
+
}
|
|
3099
|
+
self._token = token;
|
|
3100
|
+
self._debug("connection token refreshed");
|
|
3101
|
+
if (!self._isConnected()) {
|
|
3102
|
+
return;
|
|
3103
|
+
}
|
|
3104
|
+
const cmd = {
|
|
3105
|
+
refresh: { token: self._token }
|
|
3106
|
+
};
|
|
3107
|
+
self._call(cmd, false).then((resolveCtx) => {
|
|
3108
|
+
const result = resolveCtx.reply.refresh;
|
|
3109
|
+
self._refreshResponse(result);
|
|
3110
|
+
if (resolveCtx.next) {
|
|
3111
|
+
resolveCtx.next();
|
|
3112
|
+
}
|
|
3113
|
+
}, (rejectCtx) => {
|
|
3114
|
+
self._refreshError(rejectCtx.error);
|
|
3115
|
+
if (rejectCtx.next) {
|
|
3116
|
+
rejectCtx.next();
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
}).catch(function(e) {
|
|
3120
|
+
if (!self._isConnected()) {
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
if (e instanceof UnauthorizedError) {
|
|
3124
|
+
self._failUnauthorized();
|
|
3125
|
+
return;
|
|
3126
|
+
}
|
|
3127
|
+
self.emit("error", {
|
|
3128
|
+
type: "refreshToken",
|
|
3129
|
+
error: {
|
|
3130
|
+
code: errorCodes.clientRefreshToken,
|
|
3131
|
+
message: e !== void 0 ? e.toString() : ""
|
|
3132
|
+
}
|
|
3133
|
+
});
|
|
3134
|
+
self._refreshTimeout = setTimeout(() => self._refresh(), self._getRefreshRetryDelay());
|
|
3135
|
+
});
|
|
3136
|
+
}
|
|
3137
|
+
_refreshError(err) {
|
|
3138
|
+
if (err.code < 100 || err.temporary === true) {
|
|
3139
|
+
this.emit("error", {
|
|
3140
|
+
type: "refresh",
|
|
3141
|
+
error: err
|
|
3142
|
+
});
|
|
3143
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), this._getRefreshRetryDelay());
|
|
3144
|
+
} else {
|
|
3145
|
+
this._disconnect(err.code, err.message, false);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
_getRefreshRetryDelay() {
|
|
3149
|
+
return backoff(0, 5e3, 1e4);
|
|
3150
|
+
}
|
|
3151
|
+
_refreshResponse(result) {
|
|
3152
|
+
if (this._refreshTimeout) {
|
|
3153
|
+
clearTimeout(this._refreshTimeout);
|
|
3154
|
+
this._refreshTimeout = null;
|
|
3155
|
+
}
|
|
3156
|
+
if (result.expires) {
|
|
3157
|
+
this._client = result.client;
|
|
3158
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), ttlMilliseconds(result.ttl));
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
_removeSubscription(sub) {
|
|
3162
|
+
if (sub === null) {
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
3165
|
+
delete this._subs[sub.channel];
|
|
3166
|
+
}
|
|
3167
|
+
_unsubscribe(sub) {
|
|
3168
|
+
if (!this._transportIsOpen) {
|
|
3169
|
+
return Promise.resolve();
|
|
3170
|
+
}
|
|
3171
|
+
const req = {
|
|
3172
|
+
channel: sub.channel
|
|
3173
|
+
};
|
|
3174
|
+
const cmd = { unsubscribe: req };
|
|
3175
|
+
const self = this;
|
|
3176
|
+
const unsubscribePromise = new Promise((resolve, _) => {
|
|
3177
|
+
this._call(cmd, false).then((resolveCtx) => {
|
|
3178
|
+
resolve();
|
|
3179
|
+
if (resolveCtx.next) {
|
|
3180
|
+
resolveCtx.next();
|
|
3181
|
+
}
|
|
3182
|
+
}, (rejectCtx) => {
|
|
3183
|
+
resolve();
|
|
3184
|
+
if (rejectCtx.next) {
|
|
3185
|
+
rejectCtx.next();
|
|
3186
|
+
}
|
|
3187
|
+
self._disconnect(connectingCodes.unsubscribeError, "unsubscribe error", true);
|
|
3188
|
+
});
|
|
3189
|
+
});
|
|
3190
|
+
return unsubscribePromise;
|
|
3191
|
+
}
|
|
3192
|
+
_getSub(channel, id) {
|
|
3193
|
+
if (id && id > 0) {
|
|
3194
|
+
for (const ch in this._subs) {
|
|
3195
|
+
if (this._subs.hasOwnProperty(ch)) {
|
|
3196
|
+
const sub2 = this._subs[ch];
|
|
3197
|
+
if (sub2._id === id) {
|
|
3198
|
+
return sub2;
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
return null;
|
|
3203
|
+
}
|
|
3204
|
+
const sub = this._subs[channel];
|
|
3205
|
+
if (!sub) {
|
|
3206
|
+
return null;
|
|
3207
|
+
}
|
|
3208
|
+
return sub;
|
|
3209
|
+
}
|
|
3210
|
+
_isServerSub(channel) {
|
|
3211
|
+
return this._serverSubs[channel] !== void 0;
|
|
3212
|
+
}
|
|
3213
|
+
_sendSubscribeCommands() {
|
|
3214
|
+
const commands = [];
|
|
3215
|
+
for (const channel in this._subs) {
|
|
3216
|
+
if (!this._subs.hasOwnProperty(channel)) {
|
|
3217
|
+
continue;
|
|
3218
|
+
}
|
|
3219
|
+
const sub = this._subs[channel];
|
|
3220
|
+
if (sub._inflight === true) {
|
|
3221
|
+
continue;
|
|
3222
|
+
}
|
|
3223
|
+
if (sub.state === SubscriptionState.Subscribing) {
|
|
3224
|
+
const cmd = sub._subscribe();
|
|
3225
|
+
if (cmd) {
|
|
3226
|
+
commands.push(cmd);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
return commands;
|
|
3231
|
+
}
|
|
3232
|
+
_connectResponse(result) {
|
|
3233
|
+
this._transportIsOpen = true;
|
|
3234
|
+
this._transportWasOpen = true;
|
|
3235
|
+
this._reconnectAttempts = 0;
|
|
3236
|
+
this._refreshRequired = false;
|
|
3237
|
+
if (this._isConnected()) {
|
|
3238
|
+
return;
|
|
3239
|
+
}
|
|
3240
|
+
this._client = result.client;
|
|
3241
|
+
this._setState(State.Connected);
|
|
3242
|
+
if (this._refreshTimeout) {
|
|
3243
|
+
clearTimeout(this._refreshTimeout);
|
|
3244
|
+
}
|
|
3245
|
+
if (result.expires) {
|
|
3246
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), ttlMilliseconds(result.ttl));
|
|
3247
|
+
}
|
|
3248
|
+
this._session = result.session;
|
|
3249
|
+
this._node = result.node;
|
|
3250
|
+
this.startBatching();
|
|
3251
|
+
this._sendSubscribeCommands();
|
|
3252
|
+
this.stopBatching();
|
|
3253
|
+
const ctx = {
|
|
3254
|
+
client: result.client,
|
|
3255
|
+
transport: this._transport.subName()
|
|
3256
|
+
};
|
|
3257
|
+
if (result.data) {
|
|
3258
|
+
ctx.data = result.data;
|
|
3259
|
+
}
|
|
3260
|
+
this.emit("connected", ctx);
|
|
3261
|
+
this._resolvePromises();
|
|
3262
|
+
this._processServerSubs(result.subs || {});
|
|
3263
|
+
if (result.ping && result.ping > 0) {
|
|
3264
|
+
this._serverPing = result.ping * 1e3;
|
|
3265
|
+
this._sendPong = result.pong === true;
|
|
3266
|
+
this._waitServerPing();
|
|
3267
|
+
} else {
|
|
3268
|
+
this._serverPing = 0;
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
_processServerSubs(subs) {
|
|
3272
|
+
for (const channel in subs) {
|
|
3273
|
+
if (!subs.hasOwnProperty(channel)) {
|
|
3274
|
+
continue;
|
|
3275
|
+
}
|
|
3276
|
+
const sub = subs[channel];
|
|
3277
|
+
this._serverSubs[channel] = {
|
|
3278
|
+
"offset": sub.offset,
|
|
3279
|
+
"epoch": sub.epoch,
|
|
3280
|
+
"recoverable": sub.recoverable || false
|
|
3281
|
+
};
|
|
3282
|
+
const subCtx = this._getSubscribeContext(channel, sub);
|
|
3283
|
+
this.emit("subscribed", subCtx);
|
|
3284
|
+
}
|
|
3285
|
+
for (const channel in subs) {
|
|
3286
|
+
if (!subs.hasOwnProperty(channel)) {
|
|
3287
|
+
continue;
|
|
3288
|
+
}
|
|
3289
|
+
const sub = subs[channel];
|
|
3290
|
+
if (sub.recovered) {
|
|
3291
|
+
const pubs = sub.publications;
|
|
3292
|
+
if (pubs && pubs.length > 0) {
|
|
3293
|
+
for (const i in pubs) {
|
|
3294
|
+
if (pubs.hasOwnProperty(i)) {
|
|
3295
|
+
this._handlePublication(channel, pubs[i]);
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
for (const channel in this._serverSubs) {
|
|
3302
|
+
if (!this._serverSubs.hasOwnProperty(channel)) {
|
|
3303
|
+
continue;
|
|
3304
|
+
}
|
|
3305
|
+
if (!subs[channel]) {
|
|
3306
|
+
this.emit("unsubscribed", { channel });
|
|
3307
|
+
delete this._serverSubs[channel];
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
_clearRefreshTimeout() {
|
|
3312
|
+
if (this._refreshTimeout !== null) {
|
|
3313
|
+
clearTimeout(this._refreshTimeout);
|
|
3314
|
+
this._refreshTimeout = null;
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
_clearReconnectTimeout() {
|
|
3318
|
+
if (this._reconnectTimeout !== null) {
|
|
3319
|
+
clearTimeout(this._reconnectTimeout);
|
|
3320
|
+
this._reconnectTimeout = null;
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
_clearServerPingTimeout() {
|
|
3324
|
+
if (this._serverPingTimeout !== null) {
|
|
3325
|
+
clearTimeout(this._serverPingTimeout);
|
|
3326
|
+
this._serverPingTimeout = null;
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
_waitServerPing() {
|
|
3330
|
+
if (this._config.maxServerPingDelay === 0) {
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
if (!this._isConnected()) {
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
this._clearServerPingTimeout();
|
|
3337
|
+
this._serverPingTimeout = setTimeout(() => {
|
|
3338
|
+
if (!this._isConnected()) {
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
this._disconnect(connectingCodes.noPing, "no ping", true);
|
|
3342
|
+
}, this._serverPing + this._config.maxServerPingDelay);
|
|
3343
|
+
}
|
|
3344
|
+
_getSubscribeContext(channel, result) {
|
|
3345
|
+
const ctx = {
|
|
3346
|
+
channel,
|
|
3347
|
+
positioned: false,
|
|
3348
|
+
recoverable: false,
|
|
3349
|
+
wasRecovering: false,
|
|
3350
|
+
recovered: false,
|
|
3351
|
+
hasRecoveredPublications: false
|
|
3352
|
+
};
|
|
3353
|
+
if (result.recovered) {
|
|
3354
|
+
ctx.recovered = true;
|
|
3355
|
+
}
|
|
3356
|
+
if (result.positioned) {
|
|
3357
|
+
ctx.positioned = true;
|
|
3358
|
+
}
|
|
3359
|
+
if (result.recoverable) {
|
|
3360
|
+
ctx.recoverable = true;
|
|
3361
|
+
}
|
|
3362
|
+
if (result.was_recovering) {
|
|
3363
|
+
ctx.wasRecovering = true;
|
|
3364
|
+
}
|
|
3365
|
+
let epoch = "";
|
|
3366
|
+
if ("epoch" in result) {
|
|
3367
|
+
epoch = result.epoch;
|
|
3368
|
+
}
|
|
3369
|
+
let offset = 0;
|
|
3370
|
+
if ("offset" in result) {
|
|
3371
|
+
offset = result.offset;
|
|
3372
|
+
}
|
|
3373
|
+
if (ctx.positioned || ctx.recoverable) {
|
|
3374
|
+
ctx.streamPosition = {
|
|
3375
|
+
"offset": offset,
|
|
3376
|
+
"epoch": epoch
|
|
3377
|
+
};
|
|
3378
|
+
}
|
|
3379
|
+
if (Array.isArray(result.publications) && result.publications.length > 0) {
|
|
3380
|
+
ctx.hasRecoveredPublications = true;
|
|
3381
|
+
}
|
|
3382
|
+
if (result.data) {
|
|
3383
|
+
ctx.data = result.data;
|
|
3384
|
+
}
|
|
3385
|
+
return ctx;
|
|
3386
|
+
}
|
|
3387
|
+
_handleReply(reply, next) {
|
|
3388
|
+
const id = reply.id;
|
|
3389
|
+
if (!(id in this._callbacks)) {
|
|
3390
|
+
next();
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
const callbacks = this._callbacks[id];
|
|
3394
|
+
clearTimeout(this._callbacks[id].timeout);
|
|
3395
|
+
delete this._callbacks[id];
|
|
3396
|
+
if (!errorExists(reply)) {
|
|
3397
|
+
const callback = callbacks.callback;
|
|
3398
|
+
if (!callback) {
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
callback({ reply, next });
|
|
3402
|
+
} else {
|
|
3403
|
+
const errback = callbacks.errback;
|
|
3404
|
+
if (!errback) {
|
|
3405
|
+
next();
|
|
3406
|
+
return;
|
|
3407
|
+
}
|
|
3408
|
+
const error = { code: reply.error.code, message: reply.error.message || "", temporary: reply.error.temporary || false };
|
|
3409
|
+
errback({ error, next });
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
_handleJoin(channel, join, id) {
|
|
3413
|
+
const sub = this._getSub(channel, id);
|
|
3414
|
+
if (!sub && channel) {
|
|
3415
|
+
if (this._isServerSub(channel)) {
|
|
3416
|
+
const ctx = { channel, info: this._getJoinLeaveContext(join.info) };
|
|
3417
|
+
this.emit("join", ctx);
|
|
3418
|
+
}
|
|
3419
|
+
return;
|
|
3420
|
+
}
|
|
3421
|
+
sub._handleJoin(join);
|
|
3422
|
+
}
|
|
3423
|
+
_handleLeave(channel, leave, id) {
|
|
3424
|
+
const sub = this._getSub(channel, id);
|
|
3425
|
+
if (!sub && channel) {
|
|
3426
|
+
if (this._isServerSub(channel)) {
|
|
3427
|
+
const ctx = { channel, info: this._getJoinLeaveContext(leave.info) };
|
|
3428
|
+
this.emit("leave", ctx);
|
|
3429
|
+
}
|
|
3430
|
+
return;
|
|
3431
|
+
}
|
|
3432
|
+
sub._handleLeave(leave);
|
|
3433
|
+
}
|
|
3434
|
+
_handleUnsubscribe(channel, unsubscribe) {
|
|
3435
|
+
const sub = this._getSub(channel, 0);
|
|
3436
|
+
if (!sub && channel) {
|
|
3437
|
+
if (this._isServerSub(channel)) {
|
|
3438
|
+
delete this._serverSubs[channel];
|
|
3439
|
+
this.emit("unsubscribed", { channel });
|
|
3440
|
+
}
|
|
3441
|
+
return;
|
|
3442
|
+
}
|
|
3443
|
+
if (unsubscribe.code < 2500) {
|
|
3444
|
+
sub._setUnsubscribed(unsubscribe.code, unsubscribe.reason, false);
|
|
3445
|
+
} else {
|
|
3446
|
+
sub._setSubscribing(unsubscribe.code, unsubscribe.reason);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
_handleSubscribe(channel, sub) {
|
|
3450
|
+
this._serverSubs[channel] = {
|
|
3451
|
+
"offset": sub.offset,
|
|
3452
|
+
"epoch": sub.epoch,
|
|
3453
|
+
"recoverable": sub.recoverable || false
|
|
3454
|
+
};
|
|
3455
|
+
this.emit("subscribed", this._getSubscribeContext(channel, sub));
|
|
3456
|
+
}
|
|
3457
|
+
_handleDisconnect(disconnect) {
|
|
3458
|
+
const code = disconnect.code;
|
|
3459
|
+
let reconnect = true;
|
|
3460
|
+
if (code >= 3500 && code < 4e3 || code >= 4500 && code < 5e3) {
|
|
3461
|
+
reconnect = false;
|
|
3462
|
+
}
|
|
3463
|
+
this._disconnect(code, disconnect.reason, reconnect);
|
|
3464
|
+
}
|
|
3465
|
+
_getPublicationContext(channel, pub) {
|
|
3466
|
+
const ctx = {
|
|
3467
|
+
channel,
|
|
3468
|
+
data: pub.data
|
|
3469
|
+
};
|
|
3470
|
+
if (pub.offset) {
|
|
3471
|
+
ctx.offset = pub.offset;
|
|
3472
|
+
}
|
|
3473
|
+
if (pub.info) {
|
|
3474
|
+
ctx.info = this._getJoinLeaveContext(pub.info);
|
|
3475
|
+
}
|
|
3476
|
+
if (pub.tags) {
|
|
3477
|
+
ctx.tags = pub.tags;
|
|
3478
|
+
}
|
|
3479
|
+
return ctx;
|
|
3480
|
+
}
|
|
3481
|
+
_getJoinLeaveContext(clientInfo) {
|
|
3482
|
+
const info = {
|
|
3483
|
+
client: clientInfo.client,
|
|
3484
|
+
user: clientInfo.user
|
|
3485
|
+
};
|
|
3486
|
+
const connInfo = clientInfo["conn_info"];
|
|
3487
|
+
if (connInfo) {
|
|
3488
|
+
info.connInfo = connInfo;
|
|
3489
|
+
}
|
|
3490
|
+
const chanInfo = clientInfo["chan_info"];
|
|
3491
|
+
if (chanInfo) {
|
|
3492
|
+
info.chanInfo = chanInfo;
|
|
3493
|
+
}
|
|
3494
|
+
return info;
|
|
3495
|
+
}
|
|
3496
|
+
_handlePublication(channel, pub, id) {
|
|
3497
|
+
const sub = this._getSub(channel, id);
|
|
3498
|
+
if (!sub && channel) {
|
|
3499
|
+
if (this._isServerSub(channel)) {
|
|
3500
|
+
const ctx = this._getPublicationContext(channel, pub);
|
|
3501
|
+
this.emit("publication", ctx);
|
|
3502
|
+
if (pub.offset !== void 0) {
|
|
3503
|
+
this._serverSubs[channel].offset = pub.offset;
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
return;
|
|
3507
|
+
}
|
|
3508
|
+
sub._handlePublication(pub);
|
|
3509
|
+
}
|
|
3510
|
+
_handleMessage(message) {
|
|
3511
|
+
this.emit("message", { data: message.data });
|
|
3512
|
+
}
|
|
3513
|
+
_handleServerPing(next) {
|
|
3514
|
+
if (this._sendPong) {
|
|
3515
|
+
const cmd = {};
|
|
3516
|
+
this._transportSendCommands([cmd]);
|
|
3517
|
+
}
|
|
3518
|
+
next();
|
|
3519
|
+
}
|
|
3520
|
+
_handlePush(data, next) {
|
|
3521
|
+
const channel = data.channel;
|
|
3522
|
+
const id = data.id;
|
|
3523
|
+
if (data.pub) {
|
|
3524
|
+
this._handlePublication(channel, data.pub, id);
|
|
3525
|
+
} else if (data.message) {
|
|
3526
|
+
this._handleMessage(data.message);
|
|
3527
|
+
} else if (data.join) {
|
|
3528
|
+
this._handleJoin(channel, data.join, id);
|
|
3529
|
+
} else if (data.leave) {
|
|
3530
|
+
this._handleLeave(channel, data.leave, id);
|
|
3531
|
+
} else if (data.unsubscribe) {
|
|
3532
|
+
this._handleUnsubscribe(channel, data.unsubscribe);
|
|
3533
|
+
} else if (data.subscribe) {
|
|
3534
|
+
this._handleSubscribe(channel, data.subscribe);
|
|
3535
|
+
} else if (data.disconnect) {
|
|
3536
|
+
this._handleDisconnect(data.disconnect);
|
|
3537
|
+
}
|
|
3538
|
+
next();
|
|
3539
|
+
}
|
|
3540
|
+
_flush() {
|
|
3541
|
+
const commands = this._commands.slice(0);
|
|
3542
|
+
this._commands = [];
|
|
3543
|
+
this._transportSendCommands(commands);
|
|
3544
|
+
}
|
|
3545
|
+
_createErrorObject(code, message, temporary) {
|
|
3546
|
+
const errObject = {
|
|
3547
|
+
code,
|
|
3548
|
+
message
|
|
3549
|
+
};
|
|
3550
|
+
if (temporary) {
|
|
3551
|
+
errObject.temporary = true;
|
|
3552
|
+
}
|
|
3553
|
+
return errObject;
|
|
3554
|
+
}
|
|
3555
|
+
_registerCall(id, callback, errback) {
|
|
3556
|
+
this._callbacks[id] = {
|
|
3557
|
+
callback,
|
|
3558
|
+
errback,
|
|
3559
|
+
timeout: null
|
|
3560
|
+
};
|
|
3561
|
+
this._callbacks[id].timeout = setTimeout(() => {
|
|
3562
|
+
delete this._callbacks[id];
|
|
3563
|
+
if (isFunction(errback)) {
|
|
3564
|
+
errback({ error: this._createErrorObject(errorCodes.timeout, "timeout") });
|
|
3565
|
+
}
|
|
3566
|
+
}, this._config.timeout);
|
|
3567
|
+
}
|
|
3568
|
+
_addCommand(command) {
|
|
3569
|
+
if (this._batching) {
|
|
3570
|
+
this._commands.push(command);
|
|
3571
|
+
} else {
|
|
3572
|
+
this._transportSendCommands([command]);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
_nextPromiseId() {
|
|
3576
|
+
return ++this._promiseId;
|
|
3577
|
+
}
|
|
3578
|
+
_nextTransportId() {
|
|
3579
|
+
return ++this._transportId;
|
|
3580
|
+
}
|
|
3581
|
+
_resolvePromises() {
|
|
3582
|
+
for (const id in this._promises) {
|
|
3583
|
+
if (!this._promises.hasOwnProperty(id)) {
|
|
3584
|
+
continue;
|
|
3585
|
+
}
|
|
3586
|
+
if (this._promises[id].timeout) {
|
|
3587
|
+
clearTimeout(this._promises[id].timeout);
|
|
3588
|
+
}
|
|
3589
|
+
this._promises[id].resolve();
|
|
3590
|
+
delete this._promises[id];
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
_rejectPromises(err) {
|
|
3594
|
+
for (const id in this._promises) {
|
|
3595
|
+
if (!this._promises.hasOwnProperty(id)) {
|
|
3596
|
+
continue;
|
|
3597
|
+
}
|
|
3598
|
+
if (this._promises[id].timeout) {
|
|
3599
|
+
clearTimeout(this._promises[id].timeout);
|
|
3600
|
+
}
|
|
3601
|
+
this._promises[id].reject(err);
|
|
3602
|
+
delete this._promises[id];
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
};
|
|
3606
|
+
Centrifuge.SubscriptionState = SubscriptionState;
|
|
3607
|
+
Centrifuge.State = State;
|
|
3608
|
+
Centrifuge.UnauthorizedError = UnauthorizedError;
|
|
3609
|
+
|
|
3610
|
+
// src/index.ts
|
|
3611
|
+
var IndexedDBPersistence = class {
|
|
3612
|
+
db = null;
|
|
3613
|
+
dbName = "TelestackDB_Cache";
|
|
3614
|
+
async init() {
|
|
3615
|
+
return new Promise((resolve, reject) => {
|
|
3616
|
+
const request = indexedDB.open(this.dbName, 1);
|
|
3617
|
+
request.onupgradeneeded = () => {
|
|
3618
|
+
const db = request.result;
|
|
3619
|
+
if (!db.objectStoreNames.contains("documents")) db.createObjectStore("documents", { keyPath: "path" });
|
|
3620
|
+
if (!db.objectStoreNames.contains("queue")) db.createObjectStore("queue", { keyPath: "id", autoIncrement: true });
|
|
3621
|
+
};
|
|
3622
|
+
request.onsuccess = () => {
|
|
3623
|
+
this.db = request.result;
|
|
3624
|
+
resolve();
|
|
3625
|
+
};
|
|
3626
|
+
request.onerror = () => reject(request.error);
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3629
|
+
async get(table, path) {
|
|
3630
|
+
if (!this.db) await this.init();
|
|
3631
|
+
return new Promise((resolve, reject) => {
|
|
3632
|
+
const tx = this.db.transaction(table, "readonly");
|
|
3633
|
+
const store = tx.objectStore(table);
|
|
3634
|
+
const req = store.get(path);
|
|
3635
|
+
req.onsuccess = () => resolve(req.result);
|
|
3636
|
+
req.onerror = () => reject(req.error);
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
async put(table, id, data) {
|
|
3640
|
+
if (!this.db) await this.init();
|
|
3641
|
+
return new Promise((resolve, reject) => {
|
|
3642
|
+
const tx = this.db.transaction(table, "readwrite");
|
|
3643
|
+
const store = tx.objectStore(table);
|
|
3644
|
+
const req = store.put({ ...data, path: id });
|
|
3645
|
+
req.onsuccess = () => resolve();
|
|
3646
|
+
req.onerror = () => reject(req.error);
|
|
3647
|
+
});
|
|
3648
|
+
}
|
|
3649
|
+
async delete(table, id) {
|
|
3650
|
+
if (!this.db) await this.init();
|
|
3651
|
+
return new Promise((resolve, reject) => {
|
|
3652
|
+
const tx = this.db.transaction(table, "readwrite");
|
|
3653
|
+
const store = tx.objectStore(table);
|
|
3654
|
+
const req = store.delete(id);
|
|
3655
|
+
req.onsuccess = () => resolve();
|
|
3656
|
+
req.onerror = () => reject(req.error);
|
|
3657
|
+
});
|
|
3658
|
+
}
|
|
3659
|
+
async getAll(table) {
|
|
3660
|
+
if (!this.db) await this.init();
|
|
3661
|
+
return new Promise((resolve, reject) => {
|
|
3662
|
+
const tx = this.db.transaction(table, "readonly");
|
|
3663
|
+
const store = tx.objectStore(table);
|
|
3664
|
+
const req = store.getAll();
|
|
3665
|
+
req.onsuccess = () => resolve(req.result);
|
|
3666
|
+
req.onerror = () => reject(req.error);
|
|
3667
|
+
});
|
|
3668
|
+
}
|
|
3669
|
+
async clear(table) {
|
|
3670
|
+
if (!this.db) await this.init();
|
|
3671
|
+
return new Promise((resolve, reject) => {
|
|
3672
|
+
const tx = this.db.transaction(table, "readwrite");
|
|
3673
|
+
tx.objectStore(table).clear();
|
|
3674
|
+
tx.oncomplete = () => resolve();
|
|
3675
|
+
tx.onerror = () => reject(tx.error);
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
};
|
|
3679
|
+
var TelestackError = class extends Error {
|
|
3680
|
+
constructor(message, code) {
|
|
3681
|
+
super(message);
|
|
3682
|
+
this.message = message;
|
|
3683
|
+
this.code = code;
|
|
3684
|
+
this.name = "TelestackError";
|
|
3685
|
+
}
|
|
3686
|
+
};
|
|
3687
|
+
var TelestackClient = class {
|
|
3688
|
+
constructor(config) {
|
|
3689
|
+
this.config = config;
|
|
3690
|
+
this.workspaceId = config.workspaceId || "default";
|
|
3691
|
+
if (config.enablePersistence) {
|
|
3692
|
+
this.persistence = new IndexedDBPersistence();
|
|
3693
|
+
}
|
|
3694
|
+
if (config.centrifugoUrl) {
|
|
3695
|
+
this.centrifuge = new Centrifuge(config.centrifugoUrl, {
|
|
3696
|
+
getToken: () => this.getToken()
|
|
3697
|
+
});
|
|
3698
|
+
this.centrifuge.on("connected", () => {
|
|
3699
|
+
console.log("Telestack: Connected to real-time via JWT. Syncing...");
|
|
3700
|
+
this.sync();
|
|
3701
|
+
this.processQueue();
|
|
3702
|
+
});
|
|
3703
|
+
this.centrifuge.on("error", (ctx) => {
|
|
3704
|
+
console.error("Telestack: Centrifuge error:", ctx);
|
|
3705
|
+
});
|
|
3706
|
+
this.centrifuge.on("disconnected", (ctx) => {
|
|
3707
|
+
console.warn("Telestack: Centrifuge disconnected:", ctx);
|
|
3708
|
+
});
|
|
3709
|
+
this.centrifuge.connect();
|
|
3710
|
+
}
|
|
3711
|
+
this.startBackgroundWorkers();
|
|
3712
|
+
}
|
|
3713
|
+
centrifuge = null;
|
|
3714
|
+
workspaceId;
|
|
3715
|
+
lastVersion = 0;
|
|
3716
|
+
token = null;
|
|
3717
|
+
persistence = null;
|
|
3718
|
+
/** Ensure we have a valid JWT token */
|
|
3719
|
+
async getToken() {
|
|
3720
|
+
if (this.token) return this.token;
|
|
3721
|
+
const res = await fetch(`${this.config.endpoint}/documents/auth/token`, {
|
|
3722
|
+
method: "POST",
|
|
3723
|
+
headers: { "Content-Type": "application/json" },
|
|
3724
|
+
body: JSON.stringify({ userId: this.config.userId })
|
|
3725
|
+
});
|
|
3726
|
+
if (!res.ok) throw new TelestackError("Auth failed: " + await res.text());
|
|
3727
|
+
const { token } = await res.json();
|
|
3728
|
+
this.token = token;
|
|
3729
|
+
return token;
|
|
3730
|
+
}
|
|
3731
|
+
/** Helper for authenticated fetch */
|
|
3732
|
+
async authFetch(url, options = {}) {
|
|
3733
|
+
const token = await this.getToken();
|
|
3734
|
+
const headers = {
|
|
3735
|
+
...options.headers,
|
|
3736
|
+
"Authorization": `Bearer ${token}`,
|
|
3737
|
+
"workspaceId": this.workspaceId
|
|
3738
|
+
};
|
|
3739
|
+
return fetch(url.toString(), { ...options, headers });
|
|
3740
|
+
}
|
|
3741
|
+
/** Incremental sync to fetch changes since last version */
|
|
3742
|
+
async sync() {
|
|
3743
|
+
try {
|
|
3744
|
+
const url = new URL(`${this.config.endpoint}/documents/sync`);
|
|
3745
|
+
url.searchParams.set("workspaceId", this.workspaceId);
|
|
3746
|
+
url.searchParams.set("since", this.lastVersion.toString());
|
|
3747
|
+
const res = await this.authFetch(url);
|
|
3748
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
3749
|
+
const { changes } = await res.json();
|
|
3750
|
+
if (changes && changes.length > 0) {
|
|
3751
|
+
this.lastVersion = Math.max(this.lastVersion, ...changes.map((c) => c.version));
|
|
3752
|
+
return changes;
|
|
3753
|
+
}
|
|
3754
|
+
return [];
|
|
3755
|
+
} catch (e) {
|
|
3756
|
+
console.error("Telestack: Sync failed", e);
|
|
3757
|
+
return [];
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
/** Get a reference to a collection */
|
|
3761
|
+
collection(path) {
|
|
3762
|
+
return new CollectionReference(this, path);
|
|
3763
|
+
}
|
|
3764
|
+
/** Get a reference to a document */
|
|
3765
|
+
doc(path) {
|
|
3766
|
+
const parts = path.split("/");
|
|
3767
|
+
if (parts.length % 2 !== 0) {
|
|
3768
|
+
throw new TelestackError("Invalid document path. Must have an even number of segments.");
|
|
3769
|
+
}
|
|
3770
|
+
const collectionPath = parts.slice(0, -1).join("/");
|
|
3771
|
+
const id = parts[parts.length - 1];
|
|
3772
|
+
return new DocumentReference(this, collectionPath, id);
|
|
3773
|
+
}
|
|
3774
|
+
/** Get a new write batch */
|
|
3775
|
+
batch() {
|
|
3776
|
+
return new WriteBatch(this);
|
|
3777
|
+
}
|
|
3778
|
+
/** Run an atomic transaction with automatic retries (OCC) */
|
|
3779
|
+
async runTransaction(updateFunction, maxRetries = 5) {
|
|
3780
|
+
let retries = 0;
|
|
3781
|
+
while (retries < maxRetries) {
|
|
3782
|
+
const transaction = new Transaction(this);
|
|
3783
|
+
try {
|
|
3784
|
+
const result = await updateFunction(transaction);
|
|
3785
|
+
await transaction.commit();
|
|
3786
|
+
return result;
|
|
3787
|
+
} catch (e) {
|
|
3788
|
+
if (e.message.includes("Conflict") || e.status === 409) {
|
|
3789
|
+
retries++;
|
|
3790
|
+
console.warn(`Telestack: Transaction conflict, retrying (${retries}/${maxRetries})...`);
|
|
3791
|
+
continue;
|
|
3792
|
+
}
|
|
3793
|
+
throw e;
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
throw new TelestackError("Transaction failed after max retries due to persistent conflicts.");
|
|
3797
|
+
}
|
|
3798
|
+
getCentrifuge() {
|
|
3799
|
+
return this.centrifuge;
|
|
3800
|
+
}
|
|
3801
|
+
getPersistence() {
|
|
3802
|
+
return this.persistence;
|
|
3803
|
+
}
|
|
3804
|
+
getLastVersion() {
|
|
3805
|
+
return this.lastVersion;
|
|
3806
|
+
}
|
|
3807
|
+
updateLastVersion(v) {
|
|
3808
|
+
this.lastVersion = Math.max(this.lastVersion, v);
|
|
3809
|
+
}
|
|
3810
|
+
/** Background process to sync offline writes */
|
|
3811
|
+
async processQueue() {
|
|
3812
|
+
if (!this.persistence) return;
|
|
3813
|
+
const queue = await this.persistence.getAll("queue");
|
|
3814
|
+
if (queue.length === 0) return;
|
|
3815
|
+
console.log(`Telestack: Processing ${queue.length} queued offline writes...`);
|
|
3816
|
+
for (const item of queue) {
|
|
3817
|
+
try {
|
|
3818
|
+
const docRef = this.doc(item.path);
|
|
3819
|
+
let result;
|
|
3820
|
+
if (item.type === "SET") {
|
|
3821
|
+
result = await docRef.set(item.data);
|
|
3822
|
+
} else if (item.type === "UPDATE") {
|
|
3823
|
+
result = await docRef.update(item.data);
|
|
3824
|
+
} else if (item.type === "DELETE") {
|
|
3825
|
+
await docRef.delete();
|
|
3826
|
+
}
|
|
3827
|
+
await this.persistence.delete("queue", item.path);
|
|
3828
|
+
console.log(`\u2713 Synced ${item.path}`);
|
|
3829
|
+
} catch (e) {
|
|
3830
|
+
console.warn(`\u2717 Failed to sync ${item.path}, will retry later.`, e.message);
|
|
3831
|
+
break;
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
/** Start periodic sync and queue processing */
|
|
3836
|
+
startBackgroundWorkers() {
|
|
3837
|
+
setInterval(() => this.sync(), 3e4);
|
|
3838
|
+
setInterval(() => this.processQueue(), 5e3);
|
|
3839
|
+
}
|
|
3840
|
+
};
|
|
3841
|
+
var Query = class {
|
|
3842
|
+
constructor(client, path) {
|
|
3843
|
+
this.client = client;
|
|
3844
|
+
this.path = path;
|
|
3845
|
+
}
|
|
3846
|
+
filters = [];
|
|
3847
|
+
limitCount;
|
|
3848
|
+
orderByField;
|
|
3849
|
+
orderDirection = "asc";
|
|
3850
|
+
docsCache = [];
|
|
3851
|
+
debounceTimer = null;
|
|
3852
|
+
/** Add a filter to the query */
|
|
3853
|
+
where(field, op, value) {
|
|
3854
|
+
this.filters.push({ field, op, value });
|
|
3855
|
+
return this;
|
|
3856
|
+
}
|
|
3857
|
+
/** Limit the number of documents */
|
|
3858
|
+
limit(n) {
|
|
3859
|
+
this.limitCount = n;
|
|
3860
|
+
return this;
|
|
3861
|
+
}
|
|
3862
|
+
/** Order the documents */
|
|
3863
|
+
orderBy(field, direction = "asc") {
|
|
3864
|
+
this.orderByField = field;
|
|
3865
|
+
this.orderDirection = direction;
|
|
3866
|
+
return this;
|
|
3867
|
+
}
|
|
3868
|
+
/** Convert filters to SQL-like where clause for backend query */
|
|
3869
|
+
buildWhereClause() {
|
|
3870
|
+
if (this.filters.length === 0) return "1=1";
|
|
3871
|
+
return this.filters.map((f) => {
|
|
3872
|
+
let val = f.value;
|
|
3873
|
+
let sqlOp = f.op === "==" ? "=" : f.op;
|
|
3874
|
+
if (f.op === "array-contains") {
|
|
3875
|
+
return `EXISTS (SELECT 1 FROM json_each(json_extract(data, '$.${f.field}')) WHERE json_each.value = ${typeof val === "string" ? `'${val}'` : val})`;
|
|
3876
|
+
}
|
|
3877
|
+
const fieldExpr = `json_extract(data, '$.${f.field}')`;
|
|
3878
|
+
if (f.op === "in") {
|
|
3879
|
+
const list = val.map((v) => typeof v === "string" ? `'${v}'` : v).join(", ");
|
|
3880
|
+
return `${fieldExpr} IN (${list})`;
|
|
3881
|
+
}
|
|
3882
|
+
const formattedVal = typeof val === "string" ? `'${val}'` : val;
|
|
3883
|
+
return `${fieldExpr} ${sqlOp} ${formattedVal}`;
|
|
3884
|
+
}).join(" AND ");
|
|
3885
|
+
}
|
|
3886
|
+
/** Fetch documents matching the query */
|
|
3887
|
+
async get() {
|
|
3888
|
+
const persistence = this.client.getPersistence();
|
|
3889
|
+
const url = new URL(`${this.client.config.endpoint}/documents/query`);
|
|
3890
|
+
url.searchParams.set("workspaceId", this.client.workspaceId);
|
|
3891
|
+
url.searchParams.set("filters", JSON.stringify(this.filters));
|
|
3892
|
+
if (this.orderByField) {
|
|
3893
|
+
url.searchParams.set("orderByField", this.orderByField);
|
|
3894
|
+
url.searchParams.set("orderDirection", this.orderDirection);
|
|
3895
|
+
}
|
|
3896
|
+
if (this.limitCount) url.searchParams.set("limit", this.limitCount.toString());
|
|
3897
|
+
try {
|
|
3898
|
+
const res = await this.client.authFetch(url.toString());
|
|
3899
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
3900
|
+
const data = await res.json();
|
|
3901
|
+
const docs = data.map((d) => ({ id: d.id, ...d.data }));
|
|
3902
|
+
if (persistence) {
|
|
3903
|
+
for (const d of data) {
|
|
3904
|
+
await persistence.put("documents", `${this.path}/${d.id}`, { data: d.data, version: d.version });
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
return docs;
|
|
3908
|
+
} catch (e) {
|
|
3909
|
+
if (persistence) {
|
|
3910
|
+
console.warn(`Telestack: Offline query for ${this.path}, serving from cache.`);
|
|
3911
|
+
const allDocs = await persistence.getAll("documents");
|
|
3912
|
+
const filtered = allDocs.filter((d) => d.path.startsWith(this.path) && this.matches(d.data)).map((d) => ({ id: d.path.split("/").pop(), ...d.data }));
|
|
3913
|
+
if (this.orderByField) {
|
|
3914
|
+
filtered.sort((a, b) => {
|
|
3915
|
+
const valA = a[this.orderByField];
|
|
3916
|
+
const valB = b[this.orderByField];
|
|
3917
|
+
if (this.orderDirection === "asc") return valA > valB ? 1 : -1;
|
|
3918
|
+
return valA < valB ? 1 : -1;
|
|
3919
|
+
});
|
|
3920
|
+
}
|
|
3921
|
+
if (this.limitCount) return filtered.slice(0, this.limitCount);
|
|
3922
|
+
return filtered;
|
|
3923
|
+
}
|
|
3924
|
+
throw e;
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
/** Check if a document matches the local filters */
|
|
3928
|
+
matches(doc) {
|
|
3929
|
+
for (const filter of this.filters) {
|
|
3930
|
+
const docValue = doc[filter.field];
|
|
3931
|
+
switch (filter.op) {
|
|
3932
|
+
case "==":
|
|
3933
|
+
if (docValue !== filter.value) return false;
|
|
3934
|
+
break;
|
|
3935
|
+
case "!=":
|
|
3936
|
+
if (docValue === filter.value) return false;
|
|
3937
|
+
break;
|
|
3938
|
+
case ">":
|
|
3939
|
+
if (!(docValue > filter.value)) return false;
|
|
3940
|
+
break;
|
|
3941
|
+
case "<":
|
|
3942
|
+
if (!(docValue < filter.value)) return false;
|
|
3943
|
+
break;
|
|
3944
|
+
case ">=":
|
|
3945
|
+
if (!(docValue >= filter.value)) return false;
|
|
3946
|
+
break;
|
|
3947
|
+
case "<=":
|
|
3948
|
+
if (!(docValue <= filter.value)) return false;
|
|
3949
|
+
break;
|
|
3950
|
+
case "in":
|
|
3951
|
+
if (!Array.isArray(filter.value) || !filter.value.includes(docValue)) return false;
|
|
3952
|
+
break;
|
|
3953
|
+
case "array-contains":
|
|
3954
|
+
if (!Array.isArray(docValue) || !docValue.includes(filter.value)) return false;
|
|
3955
|
+
break;
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
return true;
|
|
3959
|
+
}
|
|
3960
|
+
/** Subscribe to realtime updates for this query */
|
|
3961
|
+
onSnapshot(callback) {
|
|
3962
|
+
const centrifuge = this.client.getCentrifuge();
|
|
3963
|
+
if (!centrifuge) return () => {
|
|
3964
|
+
};
|
|
3965
|
+
const channel = `collection_${this.path.replace(/\//g, "_")}`;
|
|
3966
|
+
const sub = centrifuge.newSubscription(channel);
|
|
3967
|
+
const debouncedCallback = () => {
|
|
3968
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
3969
|
+
this.debounceTimer = setTimeout(() => callback([...this.docsCache]), 50);
|
|
3970
|
+
};
|
|
3971
|
+
sub.on("publication", async (ctx) => {
|
|
3972
|
+
const event = ctx.data;
|
|
3973
|
+
console.log(`\u{1F4E1} Received publication on ${channel}:`, event.type, event.id || event.doc && event.doc.id);
|
|
3974
|
+
if (event.version) this.client.updateLastVersion(event.version);
|
|
3975
|
+
let changed = false;
|
|
3976
|
+
const docId = event.id || event.doc && event.doc.id;
|
|
3977
|
+
if (event.type === "CREATED" || event.type === "SET") {
|
|
3978
|
+
if (this.matches(event.doc.data)) {
|
|
3979
|
+
if (this.limitCount || this.orderByField) {
|
|
3980
|
+
const docs = await this.get();
|
|
3981
|
+
this.docsCache = docs;
|
|
3982
|
+
callback(docs);
|
|
3983
|
+
} else {
|
|
3984
|
+
this.docsCache = [...this.docsCache.filter((d) => d.id !== docId), { id: docId, ...event.doc.data }];
|
|
3985
|
+
changed = true;
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
} else if (event.type === "UPDATED") {
|
|
3989
|
+
const docData = event.doc ? event.doc.data : event.patch ? event.patch : {};
|
|
3990
|
+
const matches = this.matches(docData);
|
|
3991
|
+
if (this.limitCount || this.orderByField) {
|
|
3992
|
+
const docs = await this.get();
|
|
3993
|
+
this.docsCache = docs;
|
|
3994
|
+
callback(docs);
|
|
3995
|
+
} else if (matches) {
|
|
3996
|
+
this.docsCache = this.docsCache.map((d) => d.id === docId ? { ...d, ...docData } : d);
|
|
3997
|
+
changed = true;
|
|
3998
|
+
} else {
|
|
3999
|
+
this.docsCache = this.docsCache.filter((d) => d.id !== docId);
|
|
4000
|
+
changed = true;
|
|
4001
|
+
}
|
|
4002
|
+
} else if (event.type === "DELETED") {
|
|
4003
|
+
this.docsCache = this.docsCache.filter((d) => d.id !== docId);
|
|
4004
|
+
changed = true;
|
|
4005
|
+
}
|
|
4006
|
+
if (changed && !(this.limitCount || this.orderByField)) {
|
|
4007
|
+
debouncedCallback();
|
|
4008
|
+
}
|
|
4009
|
+
});
|
|
4010
|
+
sub.subscribe();
|
|
4011
|
+
this.get().then((docs) => {
|
|
4012
|
+
this.docsCache = docs;
|
|
4013
|
+
callback(docs);
|
|
4014
|
+
});
|
|
4015
|
+
return () => {
|
|
4016
|
+
sub.unsubscribe();
|
|
4017
|
+
sub.removeAllListeners();
|
|
4018
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
4019
|
+
};
|
|
4020
|
+
}
|
|
4021
|
+
};
|
|
4022
|
+
var CollectionReference = class extends Query {
|
|
4023
|
+
doc(id) {
|
|
4024
|
+
return new DocumentReference(this.client, this.path, id);
|
|
4025
|
+
}
|
|
4026
|
+
/** Add a new document to this collection */
|
|
4027
|
+
async add(data) {
|
|
4028
|
+
const collectionName = this.path.split("/").pop();
|
|
4029
|
+
const parentPath = this.path.includes("/") ? this.path.split("/").slice(0, -1).join("/") : void 0;
|
|
4030
|
+
const res = await this.client.authFetch(`${this.client.config.endpoint}/documents/${collectionName}`, {
|
|
4031
|
+
method: "POST",
|
|
4032
|
+
body: JSON.stringify({
|
|
4033
|
+
data,
|
|
4034
|
+
userId: this.client.config.userId,
|
|
4035
|
+
workspaceId: this.client.workspaceId,
|
|
4036
|
+
parentPath
|
|
4037
|
+
})
|
|
4038
|
+
});
|
|
4039
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
4040
|
+
const result = await res.json();
|
|
4041
|
+
if (result.version) this.client.updateLastVersion(result.version);
|
|
4042
|
+
return result;
|
|
4043
|
+
}
|
|
4044
|
+
};
|
|
4045
|
+
var DocumentReference = class {
|
|
4046
|
+
constructor(client, collectionPath, id) {
|
|
4047
|
+
this.client = client;
|
|
4048
|
+
this.collectionPath = collectionPath;
|
|
4049
|
+
this.id = id;
|
|
4050
|
+
}
|
|
4051
|
+
get path() {
|
|
4052
|
+
return `${this.collectionPath}/${this.id}`;
|
|
4053
|
+
}
|
|
4054
|
+
/** Access a nested collection */
|
|
4055
|
+
collection(name) {
|
|
4056
|
+
return new CollectionReference(this.client, `${this.path}/${name}`);
|
|
4057
|
+
}
|
|
4058
|
+
/** Fetch this document */
|
|
4059
|
+
async get() {
|
|
4060
|
+
const snap = await this.getSnapshot();
|
|
4061
|
+
return snap.exists() ? snap.data() : null;
|
|
4062
|
+
}
|
|
4063
|
+
/** Fetch document snapshot (includes version for transactions) */
|
|
4064
|
+
async asyncGetSnapshot() {
|
|
4065
|
+
const persistence = this.client.getPersistence();
|
|
4066
|
+
const collectionName = this.collectionPath.split("/").pop();
|
|
4067
|
+
try {
|
|
4068
|
+
const res = await this.client.authFetch(`${this.client.config.endpoint}/documents/${collectionName}/${this.id}`);
|
|
4069
|
+
if (res.status === 404) return new DocumentSnapshot(this.id, this.path, null, 0);
|
|
4070
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
4071
|
+
const data = await res.json();
|
|
4072
|
+
if (data.version) {
|
|
4073
|
+
this.client.updateLastVersion(data.version);
|
|
4074
|
+
if (persistence) await persistence.put("documents", this.path, { data: data.data, version: data.version });
|
|
4075
|
+
}
|
|
4076
|
+
return new DocumentSnapshot(this.id, this.path, data.data, data.version);
|
|
4077
|
+
} catch (e) {
|
|
4078
|
+
if (persistence) {
|
|
4079
|
+
const cached = await persistence.get("documents", this.path);
|
|
4080
|
+
if (cached) return new DocumentSnapshot(this.id, this.path, cached.data, cached.version);
|
|
4081
|
+
}
|
|
4082
|
+
throw e;
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
/** Compatibility alias for existing codebase */
|
|
4086
|
+
async getSnapshot() {
|
|
4087
|
+
return this.asyncGetSnapshot();
|
|
4088
|
+
}
|
|
4089
|
+
/** Replace or create this document */
|
|
4090
|
+
async set(data) {
|
|
4091
|
+
const persistence = this.client.getPersistence();
|
|
4092
|
+
const collectionName = this.collectionPath.split("/").pop();
|
|
4093
|
+
const parentPath = this.collectionPath.includes("/") ? this.collectionPath.split("/").slice(0, -1).join("/") : void 0;
|
|
4094
|
+
if (persistence) await persistence.put("documents", this.path, { data, version: -1 });
|
|
4095
|
+
try {
|
|
4096
|
+
const res = await this.client.authFetch(`${this.client.config.endpoint}/documents/${collectionName}/${this.id}`, {
|
|
4097
|
+
method: "PUT",
|
|
4098
|
+
body: JSON.stringify({
|
|
4099
|
+
data,
|
|
4100
|
+
userId: this.client.config.userId,
|
|
4101
|
+
workspaceId: this.client.workspaceId,
|
|
4102
|
+
parentPath
|
|
4103
|
+
})
|
|
4104
|
+
});
|
|
4105
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
4106
|
+
const result = await res.json();
|
|
4107
|
+
if (result.version) {
|
|
4108
|
+
this.client.updateLastVersion(result.version);
|
|
4109
|
+
if (persistence) await persistence.put("documents", this.path, { data, version: result.version });
|
|
4110
|
+
}
|
|
4111
|
+
return result;
|
|
4112
|
+
} catch (e) {
|
|
4113
|
+
if (persistence) {
|
|
4114
|
+
console.warn(`Telestack: Offline, queueing SET for ${this.path}`);
|
|
4115
|
+
await persistence.put("queue", this.path, { type: "SET", path: this.path, data, collectionName, parentPath });
|
|
4116
|
+
return { version: -1 };
|
|
4117
|
+
}
|
|
4118
|
+
throw e;
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
/** Update part of this document */
|
|
4122
|
+
async update(data) {
|
|
4123
|
+
const persistence = this.client.getPersistence();
|
|
4124
|
+
const collectionName = this.collectionPath.split("/").pop();
|
|
4125
|
+
const parentPath = this.collectionPath.includes("/") ? this.collectionPath.split("/").slice(0, -1).join("/") : void 0;
|
|
4126
|
+
if (persistence) {
|
|
4127
|
+
const cached = await persistence.get("documents", this.path);
|
|
4128
|
+
const newData = cached ? { ...cached.data, ...data } : data;
|
|
4129
|
+
await persistence.put("documents", this.path, { data: newData, version: cached ? cached.version : -1 });
|
|
4130
|
+
}
|
|
4131
|
+
try {
|
|
4132
|
+
const res = await this.client.authFetch(`${this.client.config.endpoint}/documents/${collectionName}/${this.id}`, {
|
|
4133
|
+
method: "PATCH",
|
|
4134
|
+
body: JSON.stringify({
|
|
4135
|
+
data,
|
|
4136
|
+
userId: this.client.config.userId,
|
|
4137
|
+
workspaceId: this.client.workspaceId,
|
|
4138
|
+
parentPath
|
|
4139
|
+
})
|
|
4140
|
+
});
|
|
4141
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
4142
|
+
const result = await res.json();
|
|
4143
|
+
if (result.version) {
|
|
4144
|
+
this.client.updateLastVersion(result.version);
|
|
4145
|
+
if (persistence) {
|
|
4146
|
+
const snap = await this.getSnapshot();
|
|
4147
|
+
await persistence.put("documents", this.path, { data: snap.data(), version: result.version });
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
return result;
|
|
4151
|
+
} catch (e) {
|
|
4152
|
+
if (persistence) {
|
|
4153
|
+
console.warn(`Telestack: Offline, queueing UPDATE for ${this.path}`);
|
|
4154
|
+
await persistence.put("queue", this.path, { type: "UPDATE", path: this.path, data, collectionName, parentPath });
|
|
4155
|
+
return { version: -1 };
|
|
4156
|
+
}
|
|
4157
|
+
throw e;
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
/** Delete this document */
|
|
4161
|
+
async delete() {
|
|
4162
|
+
const persistence = this.client.getPersistence();
|
|
4163
|
+
const collectionName = this.collectionPath.split("/").pop();
|
|
4164
|
+
if (persistence) await persistence.delete("documents", this.path);
|
|
4165
|
+
try {
|
|
4166
|
+
const res = await this.client.authFetch(`${this.client.config.endpoint}/documents/${collectionName}/${this.id}`, {
|
|
4167
|
+
method: "DELETE"
|
|
4168
|
+
});
|
|
4169
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
4170
|
+
} catch (e) {
|
|
4171
|
+
if (persistence) {
|
|
4172
|
+
console.warn(`Telestack: Offline, queueing DELETE for ${this.path}`);
|
|
4173
|
+
await persistence.put("queue", this.path, { type: "DELETE", path: this.path, collectionName });
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
throw e;
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
/** Subscribe to realtime changes on this document */
|
|
4180
|
+
onSnapshot(callback) {
|
|
4181
|
+
const centrifuge = this.client.getCentrifuge();
|
|
4182
|
+
if (!centrifuge) return () => {
|
|
4183
|
+
};
|
|
4184
|
+
const channel = `path_${this.path.replace(/\//g, "_")}`;
|
|
4185
|
+
const sub = centrifuge.newSubscription(channel);
|
|
4186
|
+
sub.on("publication", (ctx) => {
|
|
4187
|
+
const event = ctx.data;
|
|
4188
|
+
if (event.version) this.client.updateLastVersion(event.version);
|
|
4189
|
+
if (event.type === "DELETED") callback(null);
|
|
4190
|
+
else {
|
|
4191
|
+
this.get().then(callback);
|
|
4192
|
+
}
|
|
4193
|
+
});
|
|
4194
|
+
sub.on("subscribed", (ctx) => console.log(`Telestack: Subscribed to document channel ${channel}`, ctx));
|
|
4195
|
+
sub.on("error", (ctx) => console.error(`Telestack: Document subscription error on ${channel}`, ctx));
|
|
4196
|
+
sub.subscribe();
|
|
4197
|
+
this.get().then(callback);
|
|
4198
|
+
return () => {
|
|
4199
|
+
sub.unsubscribe();
|
|
4200
|
+
sub.removeAllListeners();
|
|
4201
|
+
};
|
|
4202
|
+
}
|
|
4203
|
+
};
|
|
4204
|
+
var WriteBatch = class {
|
|
4205
|
+
constructor(client) {
|
|
4206
|
+
this.client = client;
|
|
4207
|
+
}
|
|
4208
|
+
operations = [];
|
|
4209
|
+
set(docRef, data) {
|
|
4210
|
+
this.operations.push({ type: "SET", path: docRef.path, data });
|
|
4211
|
+
return this;
|
|
4212
|
+
}
|
|
4213
|
+
update(docRef, data) {
|
|
4214
|
+
this.operations.push({ type: "UPDATE", path: docRef.path, data });
|
|
4215
|
+
return this;
|
|
4216
|
+
}
|
|
4217
|
+
delete(docRef) {
|
|
4218
|
+
this.operations.push({ type: "DELETE", path: docRef.path });
|
|
4219
|
+
return this;
|
|
4220
|
+
}
|
|
4221
|
+
async commit() {
|
|
4222
|
+
if (this.operations.length === 0) return;
|
|
4223
|
+
const res = await this.client.authFetch(`${this.client.config.endpoint}/documents/batch`, {
|
|
4224
|
+
method: "POST",
|
|
4225
|
+
body: JSON.stringify({ operations: this.operations })
|
|
4226
|
+
});
|
|
4227
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
4228
|
+
const result = await res.json();
|
|
4229
|
+
if (result.version) this.client.updateLastVersion(result.version);
|
|
4230
|
+
}
|
|
4231
|
+
};
|
|
4232
|
+
var DocumentSnapshot = class {
|
|
4233
|
+
constructor(id, path, _data, version) {
|
|
4234
|
+
this.id = id;
|
|
4235
|
+
this.path = path;
|
|
4236
|
+
this._data = _data;
|
|
4237
|
+
this.version = version;
|
|
4238
|
+
}
|
|
4239
|
+
exists() {
|
|
4240
|
+
return this._data !== null;
|
|
4241
|
+
}
|
|
4242
|
+
data() {
|
|
4243
|
+
return this._data;
|
|
4244
|
+
}
|
|
4245
|
+
};
|
|
4246
|
+
var Transaction = class {
|
|
4247
|
+
constructor(client) {
|
|
4248
|
+
this.client = client;
|
|
4249
|
+
}
|
|
4250
|
+
operations = [];
|
|
4251
|
+
async get(docRef) {
|
|
4252
|
+
return docRef.getSnapshot();
|
|
4253
|
+
}
|
|
4254
|
+
set(docRef, data, snapshot) {
|
|
4255
|
+
this.operations.push({
|
|
4256
|
+
type: "SET",
|
|
4257
|
+
path: docRef.path,
|
|
4258
|
+
data,
|
|
4259
|
+
expectedVersion: snapshot?.version
|
|
4260
|
+
});
|
|
4261
|
+
return this;
|
|
4262
|
+
}
|
|
4263
|
+
update(docRef, data, snapshot) {
|
|
4264
|
+
this.operations.push({
|
|
4265
|
+
type: "UPDATE",
|
|
4266
|
+
path: docRef.path,
|
|
4267
|
+
data,
|
|
4268
|
+
expectedVersion: snapshot?.version
|
|
4269
|
+
});
|
|
4270
|
+
return this;
|
|
4271
|
+
}
|
|
4272
|
+
delete(docRef, snapshot) {
|
|
4273
|
+
this.operations.push({
|
|
4274
|
+
type: "DELETE",
|
|
4275
|
+
path: docRef.path,
|
|
4276
|
+
expectedVersion: snapshot?.version
|
|
4277
|
+
});
|
|
4278
|
+
return this;
|
|
4279
|
+
}
|
|
4280
|
+
async commit() {
|
|
4281
|
+
if (this.operations.length === 0) return;
|
|
4282
|
+
const res = await this.client.authFetch(`${this.client.config.endpoint}/documents/batch`, {
|
|
4283
|
+
method: "POST",
|
|
4284
|
+
body: JSON.stringify({ operations: this.operations })
|
|
4285
|
+
});
|
|
4286
|
+
if (res.status === 409) {
|
|
4287
|
+
const err = new TelestackError("Conflict");
|
|
4288
|
+
err.status = 409;
|
|
4289
|
+
throw err;
|
|
4290
|
+
}
|
|
4291
|
+
if (!res.ok) throw new TelestackError(await res.text());
|
|
4292
|
+
const result = await res.json();
|
|
4293
|
+
if (result.version) this.client.updateLastVersion(result.version);
|
|
4294
|
+
}
|
|
4295
|
+
};
|
|
4296
|
+
export {
|
|
4297
|
+
CollectionReference,
|
|
4298
|
+
DocumentReference,
|
|
4299
|
+
DocumentSnapshot,
|
|
4300
|
+
IndexedDBPersistence,
|
|
4301
|
+
Query,
|
|
4302
|
+
TelestackClient,
|
|
4303
|
+
TelestackError,
|
|
4304
|
+
Transaction,
|
|
4305
|
+
WriteBatch
|
|
4306
|
+
};
|