@hcikn/colibri 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +226 -88
- package/dist/index.cjs +1513 -979
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +239 -49
- package/dist/index.d.ts +239 -49
- package/dist/index.js +1478 -890
- package/dist/index.js.map +1 -1
- package/package.json +98 -41
package/dist/index.cjs
CHANGED
|
@@ -1,1072 +1,1606 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
if (len == null || len > arr.length) len = arr.length;
|
|
4
|
-
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
5
|
-
return arr2;
|
|
6
|
-
}
|
|
7
|
-
function _array_without_holes(arr) {
|
|
8
|
-
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
9
|
-
}
|
|
10
|
-
function _assert_this_initialized(self) {
|
|
11
|
-
if (self === void 0) {
|
|
12
|
-
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
13
|
-
}
|
|
14
|
-
return self;
|
|
15
|
-
}
|
|
16
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
17
|
-
try {
|
|
18
|
-
var info = gen[key](arg);
|
|
19
|
-
var value = info.value;
|
|
20
|
-
} catch (error) {
|
|
21
|
-
reject(error);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (info.done) {
|
|
25
|
-
resolve(value);
|
|
26
|
-
} else {
|
|
27
|
-
Promise.resolve(value).then(_next, _throw);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function _async_to_generator(fn) {
|
|
31
|
-
return function() {
|
|
32
|
-
var self = this, args = arguments;
|
|
33
|
-
return new Promise(function(resolve, reject) {
|
|
34
|
-
var gen = fn.apply(self, args);
|
|
35
|
-
function _next(value) {
|
|
36
|
-
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
37
|
-
}
|
|
38
|
-
function _throw(err) {
|
|
39
|
-
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
40
|
-
}
|
|
41
|
-
_next(undefined);
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
function _class_call_check(instance, Constructor) {
|
|
46
|
-
if (!(instance instanceof Constructor)) {
|
|
47
|
-
throw new TypeError("Cannot call a class as a function");
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function _defineProperties(target, props) {
|
|
51
|
-
for(var i = 0; i < props.length; i++){
|
|
52
|
-
var descriptor = props[i];
|
|
53
|
-
descriptor.enumerable = descriptor.enumerable || false;
|
|
54
|
-
descriptor.configurable = true;
|
|
55
|
-
if ("value" in descriptor) descriptor.writable = true;
|
|
56
|
-
Object.defineProperty(target, descriptor.key, descriptor);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function _create_class(Constructor, protoProps, staticProps) {
|
|
60
|
-
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
61
|
-
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
62
|
-
return Constructor;
|
|
63
|
-
}
|
|
64
|
-
function _get_prototype_of(o) {
|
|
65
|
-
_get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) {
|
|
66
|
-
return o.__proto__ || Object.getPrototypeOf(o);
|
|
67
|
-
};
|
|
68
|
-
return _get_prototype_of(o);
|
|
69
|
-
}
|
|
70
|
-
function _inherits(subClass, superClass) {
|
|
71
|
-
if (typeof superClass !== "function" && superClass !== null) {
|
|
72
|
-
throw new TypeError("Super expression must either be null or a function");
|
|
73
|
-
}
|
|
74
|
-
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
|
75
|
-
constructor: {
|
|
76
|
-
value: subClass,
|
|
77
|
-
writable: true,
|
|
78
|
-
configurable: true
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
if (superClass) _set_prototype_of(subClass, superClass);
|
|
82
|
-
}
|
|
83
|
-
function _instanceof(left, right) {
|
|
84
|
-
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
85
|
-
return !!right[Symbol.hasInstance](left);
|
|
86
|
-
} else {
|
|
87
|
-
return left instanceof right;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
function _iterable_to_array(iter) {
|
|
91
|
-
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
92
|
-
}
|
|
93
|
-
function _non_iterable_spread() {
|
|
94
|
-
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
95
|
-
}
|
|
96
|
-
function _possible_constructor_return(self, call) {
|
|
97
|
-
if (call && (_type_of(call) === "object" || typeof call === "function")) {
|
|
98
|
-
return call;
|
|
99
|
-
}
|
|
100
|
-
return _assert_this_initialized(self);
|
|
101
|
-
}
|
|
102
|
-
function _set_prototype_of(o, p) {
|
|
103
|
-
_set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
|
|
104
|
-
o.__proto__ = p;
|
|
105
|
-
return o;
|
|
106
|
-
};
|
|
107
|
-
return _set_prototype_of(o, p);
|
|
108
|
-
}
|
|
109
|
-
function _to_consumable_array(arr) {
|
|
110
|
-
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
111
|
-
}
|
|
112
|
-
function _type_of(obj) {
|
|
113
|
-
"@swc/helpers - typeof";
|
|
114
|
-
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
115
|
-
}
|
|
116
|
-
function _unsupported_iterable_to_array(o, minLen) {
|
|
117
|
-
if (!o) return;
|
|
118
|
-
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
119
|
-
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
120
|
-
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
121
|
-
if (n === "Map" || n === "Set") return Array.from(n);
|
|
122
|
-
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
123
|
-
}
|
|
124
|
-
function _is_native_reflect_construct() {
|
|
125
|
-
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
126
|
-
if (Reflect.construct.sham) return false;
|
|
127
|
-
if (typeof Proxy === "function") return true;
|
|
128
|
-
try {
|
|
129
|
-
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
|
|
130
|
-
return true;
|
|
131
|
-
} catch (e) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
function _create_super(Derived) {
|
|
136
|
-
var hasNativeReflectConstruct = _is_native_reflect_construct();
|
|
137
|
-
return function _createSuperInternal() {
|
|
138
|
-
var Super = _get_prototype_of(Derived), result;
|
|
139
|
-
if (hasNativeReflectConstruct) {
|
|
140
|
-
var NewTarget = _get_prototype_of(this).constructor;
|
|
141
|
-
result = Reflect.construct(Super, arguments, NewTarget);
|
|
142
|
-
} else {
|
|
143
|
-
result = Super.apply(this, arguments);
|
|
144
|
-
}
|
|
145
|
-
return _possible_constructor_return(this, result);
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
function _ts_generator(thisArg, body) {
|
|
149
|
-
var f, y, t, g, _ = {
|
|
150
|
-
label: 0,
|
|
151
|
-
sent: function() {
|
|
152
|
-
if (t[0] & 1) throw t[1];
|
|
153
|
-
return t[1];
|
|
154
|
-
},
|
|
155
|
-
trys: [],
|
|
156
|
-
ops: []
|
|
157
|
-
};
|
|
158
|
-
return g = {
|
|
159
|
-
next: verb(0),
|
|
160
|
-
"throw": verb(1),
|
|
161
|
-
"return": verb(2)
|
|
162
|
-
}, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
163
|
-
return this;
|
|
164
|
-
}), g;
|
|
165
|
-
function verb(n) {
|
|
166
|
-
return function(v) {
|
|
167
|
-
return step([
|
|
168
|
-
n,
|
|
169
|
-
v
|
|
170
|
-
]);
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function step(op) {
|
|
174
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
175
|
-
while(_)try {
|
|
176
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
177
|
-
if (y = 0, t) op = [
|
|
178
|
-
op[0] & 2,
|
|
179
|
-
t.value
|
|
180
|
-
];
|
|
181
|
-
switch(op[0]){
|
|
182
|
-
case 0:
|
|
183
|
-
case 1:
|
|
184
|
-
t = op;
|
|
185
|
-
break;
|
|
186
|
-
case 4:
|
|
187
|
-
_.label++;
|
|
188
|
-
return {
|
|
189
|
-
value: op[1],
|
|
190
|
-
done: false
|
|
191
|
-
};
|
|
192
|
-
case 5:
|
|
193
|
-
_.label++;
|
|
194
|
-
y = op[1];
|
|
195
|
-
op = [
|
|
196
|
-
0
|
|
197
|
-
];
|
|
198
|
-
continue;
|
|
199
|
-
case 7:
|
|
200
|
-
op = _.ops.pop();
|
|
201
|
-
_.trys.pop();
|
|
202
|
-
continue;
|
|
203
|
-
default:
|
|
204
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
205
|
-
_ = 0;
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
209
|
-
_.label = op[1];
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
if (op[0] === 6 && _.label < t[1]) {
|
|
213
|
-
_.label = t[1];
|
|
214
|
-
t = op;
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
if (t && _.label < t[2]) {
|
|
218
|
-
_.label = t[2];
|
|
219
|
-
_.ops.push(op);
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
if (t[2]) _.ops.pop();
|
|
223
|
-
_.trys.pop();
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
op = body.call(thisArg, _);
|
|
227
|
-
} catch (e) {
|
|
228
|
-
op = [
|
|
229
|
-
6,
|
|
230
|
-
e
|
|
231
|
-
];
|
|
232
|
-
y = 0;
|
|
233
|
-
} finally{
|
|
234
|
-
f = t = 0;
|
|
235
|
-
}
|
|
236
|
-
if (op[0] & 5) throw op[1];
|
|
237
|
-
return {
|
|
238
|
-
value: op[0] ? op[1] : void 0,
|
|
239
|
-
done: true
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
}
|
|
2
|
+
var __create = Object.create;
|
|
243
3
|
var __defProp = Object.defineProperty;
|
|
244
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
245
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
246
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
247
|
-
var
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
enumerable: true
|
|
251
|
-
});
|
|
7
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
8
|
+
var __typeError = (msg) => {
|
|
9
|
+
throw TypeError(msg);
|
|
252
10
|
};
|
|
253
|
-
var
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
var key = _step.value;
|
|
259
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
260
|
-
get: function() {
|
|
261
|
-
return from[key];
|
|
262
|
-
},
|
|
263
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
264
|
-
});
|
|
265
|
-
};
|
|
266
|
-
for(var _iterator = __getOwnPropNames(from)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
|
|
267
|
-
} catch (err) {
|
|
268
|
-
_didIteratorError = true;
|
|
269
|
-
_iteratorError = err;
|
|
270
|
-
} finally{
|
|
271
|
-
try {
|
|
272
|
-
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
273
|
-
_iterator.return();
|
|
274
|
-
}
|
|
275
|
-
} finally{
|
|
276
|
-
if (_didIteratorError) {
|
|
277
|
-
throw _iteratorError;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return to;
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
283
16
|
};
|
|
284
|
-
var
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
288
24
|
};
|
|
289
|
-
|
|
290
|
-
var
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
},
|
|
316
|
-
Sync: function() {
|
|
317
|
-
return Sync;
|
|
318
|
-
},
|
|
319
|
-
SyncModel: function() {
|
|
320
|
-
return SyncModel;
|
|
321
|
-
},
|
|
322
|
-
Synced: function() {
|
|
323
|
-
return Synced;
|
|
324
|
-
},
|
|
325
|
-
UnregisterChannel: function() {
|
|
326
|
-
return UnregisterChannel;
|
|
25
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
26
|
+
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
27
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
28
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
29
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
30
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
31
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
32
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
36
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
37
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
38
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
39
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
40
|
+
return __privateGet(this, extra);
|
|
41
|
+
}, set [name](x) {
|
|
42
|
+
return __privateSet(this, extra, x);
|
|
43
|
+
} }, name));
|
|
44
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
45
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
46
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
47
|
+
if (k) {
|
|
48
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
49
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
50
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
327
51
|
}
|
|
52
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
53
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
54
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
55
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
56
|
+
}
|
|
57
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
58
|
+
};
|
|
59
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
60
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
61
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
62
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
63
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
64
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
65
|
+
|
|
66
|
+
// src/index.ts
|
|
67
|
+
var index_exports = {};
|
|
68
|
+
__export(index_exports, {
|
|
69
|
+
Colibri: () => Colibri,
|
|
70
|
+
ColibriError: () => ColibriError,
|
|
71
|
+
DeleteRestApi: () => DeleteRestApi,
|
|
72
|
+
GetRestApi: () => GetRestApi,
|
|
73
|
+
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
74
|
+
ProtocolMismatchError: () => ProtocolMismatchError,
|
|
75
|
+
PutRestApi: () => PutRestApi,
|
|
76
|
+
RegisterChannel: () => RegisterChannel,
|
|
77
|
+
RegisterModelSync: () => RegisterModelSync,
|
|
78
|
+
RegisterOnce: () => RegisterOnce,
|
|
79
|
+
RegisterSyncTransforms: () => RegisterSyncTransforms,
|
|
80
|
+
RemoteLogger: () => RemoteLogger,
|
|
81
|
+
SendMessage: () => SendMessage,
|
|
82
|
+
Sync: () => Sync,
|
|
83
|
+
SyncModel: () => SyncModel,
|
|
84
|
+
SyncTransform: () => SyncTransform,
|
|
85
|
+
Synced: () => Synced,
|
|
86
|
+
UnregisterChannel: () => UnregisterChannel,
|
|
87
|
+
synced: () => synced,
|
|
88
|
+
toHexColor: () => toHexColor,
|
|
89
|
+
toRgbaColor: () => toRgbaColor
|
|
328
90
|
});
|
|
329
|
-
module.exports = __toCommonJS(
|
|
91
|
+
module.exports = __toCommonJS(index_exports);
|
|
92
|
+
|
|
330
93
|
// src/Colibri.ts
|
|
94
|
+
var import_rxjs = require("rxjs");
|
|
331
95
|
var import_socket = require("socket.io-client");
|
|
96
|
+
|
|
332
97
|
// src/ColibriError.ts
|
|
333
98
|
var import_ts_custom_error = require("ts-custom-error");
|
|
334
|
-
var ColibriError =
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
99
|
+
var ColibriError = class extends import_ts_custom_error.CustomError {
|
|
100
|
+
};
|
|
101
|
+
var ProtocolMismatchError = class extends ColibriError {
|
|
102
|
+
constructor(message, serverVersion, clientVersion, fatal = true) {
|
|
103
|
+
super(message);
|
|
104
|
+
this.serverVersion = serverVersion;
|
|
105
|
+
this.clientVersion = clientVersion;
|
|
106
|
+
this.fatal = fatal;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/lifecycle.ts
|
|
111
|
+
var awaitingInstance = [];
|
|
112
|
+
var whenColibriCreated = (action) => {
|
|
113
|
+
awaitingInstance.push(action);
|
|
114
|
+
return () => {
|
|
115
|
+
const index = awaitingInstance.indexOf(action);
|
|
116
|
+
if (index >= 0) awaitingInstance.splice(index, 1);
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
var colibriCreated = (colibri) => {
|
|
120
|
+
for (const action of awaitingInstance.splice(0)) {
|
|
121
|
+
try {
|
|
122
|
+
action(colibri);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error("Colibri: a registration made before new Colibri() failed to attach.", error);
|
|
340
125
|
}
|
|
341
|
-
|
|
342
|
-
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
var reconnectActions = /* @__PURE__ */ new WeakMap();
|
|
129
|
+
var disconnectActions = /* @__PURE__ */ new WeakMap();
|
|
130
|
+
var addAction = (actions, colibri, action) => {
|
|
131
|
+
const forInstance = actions.get(colibri);
|
|
132
|
+
if (forInstance) forInstance.push(action);
|
|
133
|
+
else actions.set(colibri, [action]);
|
|
134
|
+
};
|
|
135
|
+
var runActions = (actions, colibri, failure) => {
|
|
136
|
+
for (const action of actions.get(colibri) ?? []) {
|
|
137
|
+
try {
|
|
138
|
+
action(colibri);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(failure, error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var onColibriReconnected = (colibri, action) => {
|
|
145
|
+
addAction(reconnectActions, colibri, action);
|
|
146
|
+
};
|
|
147
|
+
var colibriReconnected = (colibri) => {
|
|
148
|
+
runActions(reconnectActions, colibri, "Colibri: catching up after a reconnect failed.");
|
|
149
|
+
};
|
|
150
|
+
var onColibriDisconnected = (colibri, action) => {
|
|
151
|
+
addAction(disconnectActions, colibri, action);
|
|
152
|
+
};
|
|
153
|
+
var colibriDisconnected = (colibri) => {
|
|
154
|
+
runActions(disconnectActions, colibri, "Colibri: handling a disconnect failed.");
|
|
155
|
+
};
|
|
156
|
+
var lastHeard = /* @__PURE__ */ new WeakMap();
|
|
157
|
+
var colibriHeardFrom = (colibri) => {
|
|
158
|
+
lastHeard.set(colibri, Date.now());
|
|
159
|
+
};
|
|
160
|
+
var lastHeardFrom = (colibri) => lastHeard.get(colibri);
|
|
161
|
+
|
|
343
162
|
// src/Colibri.ts
|
|
344
|
-
var
|
|
345
|
-
var
|
|
346
|
-
var
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
163
|
+
var PROTOCOL_VERSION = "2";
|
|
164
|
+
var REST_MEDIA_TYPE = `application/vnd.colibri.v${PROTOCOL_VERSION}+json`;
|
|
165
|
+
var COLIBRI_CHANNEL = "colibri";
|
|
166
|
+
var ADMIN_APP = "colibri";
|
|
167
|
+
var PROTOCOL_REJECTED_COMMAND = "protocol::rejected";
|
|
168
|
+
var PROTOCOL_ACCEPTED_COMMAND = "protocol::accepted";
|
|
169
|
+
var LATENCY_COMMAND = "latency";
|
|
170
|
+
var OLD_SERVER_TIMEOUT_MS = 5e3;
|
|
171
|
+
var OLD_SERVER_PROTOCOL_VERSION = "1";
|
|
172
|
+
var pageHostname = () => typeof window !== "undefined" ? window.location?.hostname ?? "" : "";
|
|
173
|
+
var SCHEMES = {
|
|
174
|
+
ws: { socket: "ws", rest: "http" },
|
|
175
|
+
http: { socket: "ws", rest: "http" },
|
|
176
|
+
wss: { socket: "wss", rest: "https" },
|
|
177
|
+
https: { socket: "wss", rest: "https" }
|
|
178
|
+
};
|
|
179
|
+
var parseServerAddress = (server) => {
|
|
180
|
+
const address = server.trim();
|
|
181
|
+
const match = /^([a-z][a-z0-9+.-]*):\/\/(.*)$/i.exec(address);
|
|
182
|
+
const schemes = match ? SCHEMES[match[1].toLowerCase()] : SCHEMES.ws;
|
|
183
|
+
if (!schemes) {
|
|
184
|
+
throw new ColibriError(
|
|
185
|
+
`Unsupported scheme in server address '${server}' - use ws://, wss://, http://, https:// or none.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const authority = (match ? match[2] : address).replace(/\/+$/, "");
|
|
189
|
+
if (authority.length === 0) {
|
|
190
|
+
throw new ColibriError("Server Address missing or empty!");
|
|
191
|
+
}
|
|
192
|
+
if (/[/?#]/.test(authority)) {
|
|
193
|
+
throw new ColibriError(
|
|
194
|
+
`Server address '${server}' has a path or query after the host - pass only the host and, optionally, the port, e.g. 'http://example.com:9211'.`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const hostAndPort = /^(\[[^\]]+\]|[^:]+)(?::(\d+))?$/.exec(authority);
|
|
198
|
+
if (!hostAndPort) {
|
|
199
|
+
throw new ColibriError(
|
|
200
|
+
`Server address '${server}' is not a host, or a host and a numeric port - an IPv6 address goes in brackets.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const portText = hostAndPort.at(2);
|
|
204
|
+
return { ...schemes, host: hostAndPort[1], port: portText === void 0 ? void 0 : Number(portText) };
|
|
205
|
+
};
|
|
206
|
+
var DEFAULT_PORT = 9211;
|
|
207
|
+
var checkPort = (port) => {
|
|
208
|
+
if (!Number.isInteger(port)) {
|
|
209
|
+
throw new ColibriError(`Port must be a whole number (1 - 65535): ${port}`);
|
|
210
|
+
}
|
|
211
|
+
if (port < 1 || port > 65535) {
|
|
212
|
+
throw new ColibriError(`Port out of allowed range (1 - 65535): ${port}`);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
var coercePort = (port) => typeof port === "string" && /^\s*\d+\s*$/.test(port) ? Number(port) : port;
|
|
216
|
+
var resolvePort = (server, fromAddress, given) => {
|
|
217
|
+
if (given !== void 0) checkPort(given);
|
|
218
|
+
if (fromAddress === void 0) return given ?? DEFAULT_PORT;
|
|
219
|
+
checkPort(fromAddress);
|
|
220
|
+
if (given !== void 0 && given !== fromAddress) {
|
|
221
|
+
throw new ColibriError(
|
|
222
|
+
`Server address '${server}' has port ${fromAddress}, but port ${given} was passed as well - give the port once.`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return fromAddress;
|
|
226
|
+
};
|
|
227
|
+
var pendingRegistrations = [];
|
|
228
|
+
var Colibri = class _Colibri {
|
|
229
|
+
/**
|
|
230
|
+
* Connects to a Colibri server. Only one instance may exist.
|
|
231
|
+
* @param app the application name; clients only see each other's messages within one app
|
|
232
|
+
* @param server the host, optionally after `ws://`, `wss://`, `http://` or `https://` (the
|
|
233
|
+
* secure ones mean `wss` for the socket and `https` for REST) and optionally with a port,
|
|
234
|
+
* as in `'http://example.com:9211'`; no path. Defaults to the host that served the page,
|
|
235
|
+
* and is required outside a browser.
|
|
236
|
+
* @param port the server port, if the address has none; 9211 if neither does. When both have
|
|
237
|
+
* one, they must agree.
|
|
238
|
+
* @throws ColibriError for an address or port this client cannot connect to, or when an
|
|
239
|
+
* instance already exists
|
|
240
|
+
*/
|
|
241
|
+
constructor(app, server = pageHostname(), port) {
|
|
242
|
+
this.app = app;
|
|
243
|
+
this.server = server;
|
|
244
|
+
if (server.trim().length <= 0) {
|
|
245
|
+
throw new ColibriError(
|
|
246
|
+
typeof window === "undefined" ? "Server Address missing or empty! Outside a browser there is no page to take it from, so pass it as the second argument." : "Server Address missing or empty!"
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const address = parseServerAddress(server);
|
|
250
|
+
this.port = resolvePort(server, address.port, coercePort(port));
|
|
251
|
+
this.uri = `${address.socket}://${address.host}:${this.port}`;
|
|
252
|
+
this.uriRestApi = `${address.rest}://${address.host}:${this.port}/api/store/${encodeURIComponent(app)}/`;
|
|
253
|
+
if (_Colibri.instance) throw new ColibriError("A Colibri instance already exists!");
|
|
254
|
+
else _Colibri.instance = this;
|
|
255
|
+
if (app === ADMIN_APP) {
|
|
256
|
+
console.warn(
|
|
257
|
+
`Colibri: the app name '${ADMIN_APP}' is reserved for the server's admin UI, and the server treats this client as one: it sends it the server's log as it is written and every client's connects and disconnects, shares its messages with every open admin UI, and skips the protocol version check. Give your app a name of its own.`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
this.socket = (0, import_socket.connect)(this.uri, {
|
|
261
|
+
query: { app, version: PROTOCOL_VERSION },
|
|
262
|
+
transports: ["websocket"]
|
|
263
|
+
});
|
|
264
|
+
this.socket.on("connect", this.onSocketConnect.bind(this));
|
|
265
|
+
this.socket.on("disconnect", this.onSocketDisconnect.bind(this));
|
|
266
|
+
this.socket.on("connect_error", this.onSocketConnectError.bind(this));
|
|
267
|
+
this.socket.onAny(this.onSocketAny.bind(this));
|
|
268
|
+
this.registerChannel(COLIBRI_CHANNEL, (msg) => {
|
|
269
|
+
if (msg.command === LATENCY_COMMAND) {
|
|
270
|
+
SendMessage(COLIBRI_CHANNEL, LATENCY_COMMAND, msg.payload);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
for (const { channel, handler, once } of pendingRegistrations.splice(0)) {
|
|
274
|
+
if (once) this.registerOnce(channel, handler);
|
|
275
|
+
else this.registerChannel(channel, handler);
|
|
276
|
+
}
|
|
277
|
+
colibriCreated(this);
|
|
278
|
+
}
|
|
279
|
+
static instance = null;
|
|
280
|
+
socket;
|
|
281
|
+
messageSubject = new import_rxjs.Subject();
|
|
282
|
+
messages = this.messageSubject.asObservable();
|
|
283
|
+
protocolMismatchSubject = new import_rxjs.Subject();
|
|
284
|
+
/**
|
|
285
|
+
* Reports that this client and the server do not speak the same wire protocol. Two kinds,
|
|
286
|
+
* told apart by {@link ProtocolMismatchError.fatal `fatal`}, each emitted at most once per
|
|
287
|
+
* instance:
|
|
288
|
+
*
|
|
289
|
+
* - `fatal: true`: the server refused this client. The connection is closed and not retried.
|
|
290
|
+
* - `fatal: false`: the server did not announce itself within 5s of connecting, so it is
|
|
291
|
+
* suspected to predate colibri-server 2.0.0. The connection stays up.
|
|
292
|
+
*
|
|
293
|
+
* A suspicion can be followed by a refusal, never the other way round. Nothing is replayed to
|
|
294
|
+
* a late subscriber, so subscribe right after construction. Both kinds are also logged.
|
|
295
|
+
*/
|
|
296
|
+
protocolMismatch = this.protocolMismatchSubject.asObservable();
|
|
297
|
+
uri;
|
|
298
|
+
uriRestApi;
|
|
299
|
+
/** The port connected to: the one in the server address, else the one passed, else 9211. */
|
|
300
|
+
port;
|
|
301
|
+
oldServerTimer;
|
|
302
|
+
// Once per instance, not per connection, so a reconnect does not repeat the warning.
|
|
303
|
+
hasReportedOldServer = false;
|
|
304
|
+
// Once per instance, not once per `protocol::rejected` received.
|
|
305
|
+
hasReportedRefusal = false;
|
|
306
|
+
hasConnected = false;
|
|
307
|
+
// An outage runs from a failed connection attempt to the next connect.
|
|
308
|
+
hasReportedOutage = false;
|
|
309
|
+
onSocketConnect() {
|
|
310
|
+
console.debug(`Connected to colibri server on ${this.server}`);
|
|
311
|
+
this.hasReportedOutage = false;
|
|
312
|
+
this.waitForServerHello();
|
|
313
|
+
if (this.hasConnected) colibriReconnected(this);
|
|
314
|
+
this.hasConnected = true;
|
|
315
|
+
}
|
|
316
|
+
// Until the next connect, sends wait in Socket.IO's buffer and go out ahead of anything a
|
|
317
|
+
// catch-up sends, which is the wrong order for some of them.
|
|
318
|
+
onSocketDisconnect() {
|
|
319
|
+
colibriDisconnected(this);
|
|
320
|
+
}
|
|
321
|
+
// Socket.IO retries silently, so a mistyped address, a server that is down and a firewall all
|
|
322
|
+
// look like a slow connection. Reported once per outage, on its first failed attempt, not on
|
|
323
|
+
// every retry.
|
|
324
|
+
onSocketConnectError(error) {
|
|
325
|
+
if (this.hasReportedOutage) return;
|
|
326
|
+
this.hasReportedOutage = true;
|
|
327
|
+
const cause = error.description?.message;
|
|
328
|
+
const reason = typeof cause === "string" && cause.length > 0 ? `${error.message}: ${cause}` : error.message;
|
|
329
|
+
console.warn(
|
|
330
|
+
`Colibri: could not connect to ${this.uri} (${reason}). ` + (this.socket.active ? "Retrying until it answers - check the server address and that the server is running." : "Not retrying.")
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
/*
|
|
334
|
+
* Detecting a server that predates the protocol version check.
|
|
335
|
+
*
|
|
336
|
+
* Such a server neither refuses this client nor says what it speaks, so a current one
|
|
337
|
+
* announces itself unprompted and the missing announcement is the signal. The timer only
|
|
338
|
+
* observes; it does not gate message delivery.
|
|
339
|
+
*/
|
|
340
|
+
waitForServerHello() {
|
|
341
|
+
if (this.hasReportedOldServer) return;
|
|
342
|
+
clearTimeout(this.oldServerTimer);
|
|
343
|
+
this.oldServerTimer = setTimeout(() => {
|
|
344
|
+
this.onServerHelloMissing();
|
|
345
|
+
}, OLD_SERVER_TIMEOUT_MS);
|
|
346
|
+
}
|
|
347
|
+
stopWaitingForServerHello() {
|
|
348
|
+
clearTimeout(this.oldServerTimer);
|
|
349
|
+
this.oldServerTimer = void 0;
|
|
350
|
+
}
|
|
351
|
+
onServerHelloMissing() {
|
|
352
|
+
if (!this.socket.connected) return;
|
|
353
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
354
|
+
this.waitForServerHello();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
this.hasReportedOldServer = true;
|
|
358
|
+
this.stopWaitingForServerHello();
|
|
359
|
+
const error = new ProtocolMismatchError(
|
|
360
|
+
`The server did not identify itself within ${OLD_SERVER_TIMEOUT_MS / 1e3}s, so it predates colibri-server 2.0.0 and speaks protocol v1; this client speaks v${PROTOCOL_VERSION}.`,
|
|
361
|
+
// A protocol version, as in the refusal. Inferred, not received: every 2.0.0+ server
|
|
362
|
+
// announces itself, and everything older speaks v1.
|
|
363
|
+
OLD_SERVER_PROTOCOL_VERSION,
|
|
364
|
+
PROTOCOL_VERSION,
|
|
365
|
+
false
|
|
366
|
+
);
|
|
367
|
+
console.warn(`Colibri: ${error.message} The connection still works; upgrade the server when you can.`);
|
|
368
|
+
this.protocolMismatchSubject.next(error);
|
|
369
|
+
}
|
|
370
|
+
onSocketAny(channel, msg) {
|
|
371
|
+
colibriHeardFrom(this);
|
|
372
|
+
const { command, payload } = msg;
|
|
373
|
+
if (channel === COLIBRI_CHANNEL && command === PROTOCOL_REJECTED_COMMAND) {
|
|
374
|
+
this.onProtocolRejected(payload);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (channel === COLIBRI_CHANNEL && command === PROTOCOL_ACCEPTED_COMMAND) {
|
|
378
|
+
this.stopWaitingForServerHello();
|
|
379
|
+
return;
|
|
387
380
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
381
|
+
this.messageSubject.next({ channel, command, payload });
|
|
382
|
+
}
|
|
383
|
+
// `rejection` is optional because it comes off the wire, and a bare `protocol::rejected` must
|
|
384
|
+
// still give a usable error rather than a TypeError.
|
|
385
|
+
onProtocolRejected(rejection) {
|
|
386
|
+
this.stopWaitingForServerHello();
|
|
387
|
+
this.hasReportedOldServer = true;
|
|
388
|
+
this.socket.io.reconnection(false);
|
|
389
|
+
this.socket.disconnect();
|
|
390
|
+
if (this.hasReportedRefusal) return;
|
|
391
|
+
this.hasReportedRefusal = true;
|
|
392
|
+
const serverVersion = rejection?.serverVersion ?? "unknown";
|
|
393
|
+
const error = new ProtocolMismatchError(
|
|
394
|
+
rejection?.reason ?? `Server refused the connection: it speaks protocol v${serverVersion}, this client speaks v${PROTOCOL_VERSION}.`,
|
|
395
|
+
serverVersion,
|
|
396
|
+
rejection?.clientVersion ?? PROTOCOL_VERSION
|
|
397
|
+
);
|
|
398
|
+
console.error(
|
|
399
|
+
`Colibri: ${error.message} Update colibri-web and colibri-server to matching versions. Not reconnecting.`
|
|
400
|
+
);
|
|
401
|
+
this.protocolMismatchSubject.next(error);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Returns the existing Colibri instance or null if there's none yet
|
|
405
|
+
* @param warnIfNotInitialized true => print Log message if Colibri has not been initialized yet
|
|
406
|
+
* @returns
|
|
407
|
+
*/
|
|
408
|
+
static getInstance(warnIfNotInitialized = true) {
|
|
409
|
+
if (warnIfNotInitialized && !_Colibri.instance) {
|
|
410
|
+
console.warn("Colibri not initialized yet! (Instance is null)");
|
|
411
|
+
}
|
|
412
|
+
return _Colibri.instance;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
408
415
|
* Sends a single message in the given `channel`.
|
|
409
416
|
* @param channel message channel
|
|
410
417
|
* @param command message command
|
|
411
418
|
* @param payload message payload
|
|
412
|
-
*/
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
{
|
|
422
|
-
//#region Socket Events
|
|
423
|
-
/**
|
|
419
|
+
*/
|
|
420
|
+
sendMessage(channel, command, payload = {}) {
|
|
421
|
+
this.socket.emit(channel, {
|
|
422
|
+
command,
|
|
423
|
+
payload
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
// #region Socket Events
|
|
427
|
+
/**
|
|
424
428
|
* Adds a `handler` function listening for messages in `channel`.
|
|
425
429
|
* @param channel message channel
|
|
426
430
|
* @param handler handler to be executed when a message is received
|
|
427
|
-
*/
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
{
|
|
433
|
-
/**
|
|
431
|
+
*/
|
|
432
|
+
registerChannel(channel, handler) {
|
|
433
|
+
this.socket.on(channel, handler);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
434
436
|
* Removes the `handler` function listening for messages in `channel`.
|
|
435
437
|
* @param channel message channel
|
|
436
438
|
* @param handler handler to be executed when a message is received
|
|
437
|
-
*/
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
{
|
|
443
|
-
/**
|
|
439
|
+
*/
|
|
440
|
+
unregisterChannel(channel, handler) {
|
|
441
|
+
this.socket.off(channel, handler);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
444
|
* Adds a one-time `handler` function listening for the next message in `channel`.
|
|
445
445
|
* @param channel message channel
|
|
446
446
|
* @param handler handler to be executed when a message is received
|
|
447
|
-
*/
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
*
|
|
447
|
+
*/
|
|
448
|
+
registerOnce(channel, handler) {
|
|
449
|
+
this.socket.once(channel, handler);
|
|
450
|
+
}
|
|
451
|
+
// #endregion
|
|
452
|
+
// #region Rest API
|
|
453
|
+
/**
|
|
454
|
+
* Returns the REST API Endpoint for a given `key`, or null if the key is empty, `.` or `..`.
|
|
455
|
+
* Any other key is stored under exactly that name (`#`, `?`, `/`, `%` and spaces included)
|
|
456
|
+
* after surrounding whitespace and leading slashes are stripped.
|
|
457
457
|
* @param key REST API storage key
|
|
458
458
|
* @returns
|
|
459
|
-
*/
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
key: "getRestObject",
|
|
468
|
-
value: /**
|
|
459
|
+
*/
|
|
460
|
+
getRestUri(key) {
|
|
461
|
+
key = key.trim();
|
|
462
|
+
while (key.startsWith("/")) key = key.substring(1);
|
|
463
|
+
if (key.length === 0 || key === "." || key === "..") return null;
|
|
464
|
+
return this.uriRestApi + encodeURIComponent(key);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
469
467
|
* Queries an object from the REST API identified by `key`.
|
|
468
|
+
*
|
|
469
|
+
* Rejects when the server cannot be reached (wrong address, server down, no network) or an
|
|
470
|
+
* answer with a success status is not JSON.
|
|
470
471
|
* @param key REST API storage key
|
|
471
|
-
* @returns
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
})
|
|
493
|
-
];
|
|
494
|
-
case 1:
|
|
495
|
-
response = _state.sent();
|
|
496
|
-
if (response.status >= 400) return [
|
|
497
|
-
2,
|
|
498
|
-
null
|
|
499
|
-
];
|
|
500
|
-
else return [
|
|
501
|
-
2,
|
|
502
|
-
response.json()
|
|
503
|
-
];
|
|
504
|
-
return [
|
|
505
|
-
2
|
|
506
|
-
];
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
})();
|
|
510
|
-
}
|
|
511
|
-
},
|
|
512
|
-
{
|
|
513
|
-
key: "setRestObject",
|
|
514
|
-
value: /**
|
|
515
|
-
* Sets or updatees an object in the REST API identified by `key`.
|
|
472
|
+
* @returns the stored value; or null if `key` is empty or the server answered with an error
|
|
473
|
+
* status, such as 404 for a key that was never stored. A stored `null` looks the same.
|
|
474
|
+
*/
|
|
475
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
476
|
+
async getRestObject(key) {
|
|
477
|
+
const uri = this.getRestUri(key);
|
|
478
|
+
if (!uri) return null;
|
|
479
|
+
const response = await fetch(uri, {
|
|
480
|
+
method: "GET",
|
|
481
|
+
headers: {
|
|
482
|
+
Accept: REST_MEDIA_TYPE
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
if (response.status >= 400) return null;
|
|
486
|
+
else return response.json();
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Sets or updates an object in the REST API identified by `key`.
|
|
490
|
+
*
|
|
491
|
+
* Rejects when the server cannot be reached (wrong address, server down, no network) or
|
|
492
|
+
* `data` cannot be serialized to JSON (circular structure, BigInt).
|
|
516
493
|
* @param key REST API storage key
|
|
517
494
|
* @param data JSON data to write
|
|
518
|
-
* @returns true if data
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}();
|
|
570
|
-
_Colibri.instance = null;
|
|
571
|
-
var Colibri = _Colibri;
|
|
572
|
-
var SendMessage = function(channel, command) {
|
|
573
|
-
var payload = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
|
|
574
|
-
var _Colibri_getInstance;
|
|
575
|
-
return (_Colibri_getInstance = Colibri.getInstance()) === null || _Colibri_getInstance === void 0 ? void 0 : _Colibri_getInstance.sendMessage(channel, command, payload);
|
|
576
|
-
};
|
|
577
|
-
var RegisterChannel = function(channel, handler) {
|
|
578
|
-
var _Colibri_getInstance;
|
|
579
|
-
return (_Colibri_getInstance = Colibri.getInstance()) === null || _Colibri_getInstance === void 0 ? void 0 : _Colibri_getInstance.registerChannel(channel, handler);
|
|
580
|
-
};
|
|
581
|
-
var UnregisterChannel = function(channel, handler) {
|
|
582
|
-
var _Colibri_getInstance;
|
|
583
|
-
return (_Colibri_getInstance = Colibri.getInstance()) === null || _Colibri_getInstance === void 0 ? void 0 : _Colibri_getInstance.unregisterChannel(channel, handler);
|
|
584
|
-
};
|
|
585
|
-
var RegisterOnce = function(channel, handler) {
|
|
586
|
-
var _Colibri_getInstance;
|
|
587
|
-
return (_Colibri_getInstance = Colibri.getInstance()) === null || _Colibri_getInstance === void 0 ? void 0 : _Colibri_getInstance.registerOnce(channel, handler);
|
|
588
|
-
};
|
|
589
|
-
var GetRestApi = function(key) {
|
|
590
|
-
var _Colibri_getInstance;
|
|
591
|
-
return (_Colibri_getInstance = Colibri.getInstance()) === null || _Colibri_getInstance === void 0 ? void 0 : _Colibri_getInstance.getRestObject(key);
|
|
592
|
-
};
|
|
593
|
-
var PutRestApi = function(key, data) {
|
|
594
|
-
var _Colibri_getInstance;
|
|
595
|
-
return (_Colibri_getInstance = Colibri.getInstance()) === null || _Colibri_getInstance === void 0 ? void 0 : _Colibri_getInstance.setRestObject(key, data);
|
|
495
|
+
* @returns true if the server stored the data; false if `key` is empty or the server answered
|
|
496
|
+
* with any other status
|
|
497
|
+
*/
|
|
498
|
+
async setRestObject(key, data) {
|
|
499
|
+
const uri = this.getRestUri(key);
|
|
500
|
+
if (!uri) return false;
|
|
501
|
+
const response = await fetch(uri, {
|
|
502
|
+
method: "PUT",
|
|
503
|
+
headers: {
|
|
504
|
+
Accept: REST_MEDIA_TYPE,
|
|
505
|
+
"Content-Type": REST_MEDIA_TYPE
|
|
506
|
+
},
|
|
507
|
+
body: JSON.stringify(data)
|
|
508
|
+
});
|
|
509
|
+
return response.status >= 200 && response.status < 300;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Deletes the object identified by `key` from the REST API.
|
|
513
|
+
*
|
|
514
|
+
* Rejects when the server cannot be reached (wrong address, server down, no network).
|
|
515
|
+
* @param key REST API storage key
|
|
516
|
+
* @returns true if the server deleted the value; false if `key` is empty or the server answered
|
|
517
|
+
* with any other status, such as 404 for a key that holds no value
|
|
518
|
+
*/
|
|
519
|
+
async deleteRestObject(key) {
|
|
520
|
+
const uri = this.getRestUri(key);
|
|
521
|
+
if (!uri) return false;
|
|
522
|
+
const response = await fetch(uri, {
|
|
523
|
+
method: "DELETE",
|
|
524
|
+
headers: {
|
|
525
|
+
Accept: REST_MEDIA_TYPE
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
return response.status >= 200 && response.status < 300;
|
|
529
|
+
}
|
|
530
|
+
// #endregion
|
|
531
|
+
};
|
|
532
|
+
var SendMessage = (channel, command, payload = {}) => Colibri.getInstance()?.sendMessage(channel, command, payload);
|
|
533
|
+
var RegisterChannel = (channel, handler) => {
|
|
534
|
+
const colibri = Colibri.getInstance(false);
|
|
535
|
+
if (colibri) colibri.registerChannel(channel, handler);
|
|
536
|
+
else pendingRegistrations.push({ channel, handler, once: false });
|
|
537
|
+
};
|
|
538
|
+
var UnregisterChannel = (channel, handler) => {
|
|
539
|
+
const colibri = Colibri.getInstance(false);
|
|
540
|
+
if (colibri) {
|
|
541
|
+
colibri.unregisterChannel(channel, handler);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const index = pendingRegistrations.findIndex((p) => p.channel === channel && p.handler === handler);
|
|
545
|
+
if (index >= 0) pendingRegistrations.splice(index, 1);
|
|
596
546
|
};
|
|
547
|
+
var RegisterOnce = (channel, handler) => {
|
|
548
|
+
const colibri = Colibri.getInstance(false);
|
|
549
|
+
if (colibri) colibri.registerOnce(channel, handler);
|
|
550
|
+
else pendingRegistrations.push({ channel, handler, once: true });
|
|
551
|
+
};
|
|
552
|
+
var GetRestApi = (key) => Colibri.getInstance()?.getRestObject(key);
|
|
553
|
+
var PutRestApi = (key, data) => Colibri.getInstance()?.setRestObject(key, data);
|
|
554
|
+
var DeleteRestApi = (key) => Colibri.getInstance()?.deleteRestObject(key);
|
|
555
|
+
|
|
597
556
|
// src/Broadcasting.ts
|
|
598
|
-
var sendBool =
|
|
599
|
-
|
|
557
|
+
var sendBool = (channel, val) => {
|
|
558
|
+
SendMessage(channel, "broadcast::bool", val);
|
|
559
|
+
};
|
|
560
|
+
var sendBoolArray = (channel, val) => {
|
|
561
|
+
SendMessage(channel, "broadcast::bool[]", val);
|
|
562
|
+
};
|
|
563
|
+
var sendNumber = (channel, val) => {
|
|
564
|
+
SendMessage(channel, "broadcast::float", val);
|
|
600
565
|
};
|
|
601
|
-
var
|
|
602
|
-
|
|
566
|
+
var sendNumberArray = (channel, val) => {
|
|
567
|
+
SendMessage(channel, "broadcast::float[]", val);
|
|
603
568
|
};
|
|
604
|
-
var
|
|
605
|
-
|
|
569
|
+
var sendString = (channel, val) => {
|
|
570
|
+
SendMessage(channel, "broadcast::string", val);
|
|
606
571
|
};
|
|
607
|
-
var
|
|
608
|
-
|
|
572
|
+
var sendStringArray = (channel, val) => {
|
|
573
|
+
SendMessage(channel, "broadcast::string[]", val);
|
|
609
574
|
};
|
|
610
|
-
var
|
|
611
|
-
|
|
575
|
+
var sendVector2 = (channel, val) => {
|
|
576
|
+
SendMessage(channel, "broadcast::vector2", val);
|
|
612
577
|
};
|
|
613
|
-
var
|
|
614
|
-
|
|
578
|
+
var sendVector2Array = (channel, val) => {
|
|
579
|
+
SendMessage(channel, "broadcast::vector2[]", val);
|
|
615
580
|
};
|
|
616
|
-
var sendVector3 =
|
|
617
|
-
|
|
581
|
+
var sendVector3 = (channel, val) => {
|
|
582
|
+
SendMessage(channel, "broadcast::vector3", val);
|
|
618
583
|
};
|
|
619
|
-
var sendVector3Array =
|
|
620
|
-
|
|
584
|
+
var sendVector3Array = (channel, val) => {
|
|
585
|
+
SendMessage(channel, "broadcast::vector3[]", val);
|
|
621
586
|
};
|
|
622
|
-
var sendQuaternion =
|
|
623
|
-
|
|
587
|
+
var sendQuaternion = (channel, val) => {
|
|
588
|
+
SendMessage(channel, "broadcast::quaternion", val);
|
|
624
589
|
};
|
|
625
|
-
var sendQuaternionArray =
|
|
626
|
-
|
|
590
|
+
var sendQuaternionArray = (channel, val) => {
|
|
591
|
+
SendMessage(channel, "broadcast::quaternion[]", val);
|
|
627
592
|
};
|
|
628
|
-
var sendColor =
|
|
629
|
-
|
|
593
|
+
var sendColor = (channel, val) => {
|
|
594
|
+
SendMessage(channel, "broadcast::color", val);
|
|
630
595
|
};
|
|
631
|
-
var sendColorArray =
|
|
632
|
-
|
|
596
|
+
var sendColorArray = (channel, val) => {
|
|
597
|
+
SendMessage(channel, "broadcast::color[]", val);
|
|
633
598
|
};
|
|
634
|
-
var sendJson =
|
|
635
|
-
|
|
599
|
+
var sendJson = (channel, val) => {
|
|
600
|
+
SendMessage(channel, "broadcast::json", val);
|
|
636
601
|
};
|
|
637
602
|
var listeners = {};
|
|
638
|
-
var
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
603
|
+
var channelHandlers = {};
|
|
604
|
+
var registerListener = (channel, type, callback) => {
|
|
605
|
+
let channelListeners = listeners[channel];
|
|
606
|
+
if (channelListeners === void 0) {
|
|
607
|
+
const newChannelListeners = {};
|
|
608
|
+
channelListeners = newChannelListeners;
|
|
609
|
+
listeners[channel] = newChannelListeners;
|
|
610
|
+
const handler = (msg) => {
|
|
611
|
+
const commandListeners = newChannelListeners[msg.command];
|
|
612
|
+
if (commandListeners !== void 0) {
|
|
613
|
+
commandListeners.forEach((cb) => {
|
|
614
|
+
cb(msg.payload);
|
|
647
615
|
});
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
channelHandlers[channel] = handler;
|
|
619
|
+
RegisterChannel(channel, handler);
|
|
620
|
+
}
|
|
621
|
+
let typeListeners = channelListeners[type];
|
|
622
|
+
if (typeListeners === void 0) {
|
|
623
|
+
typeListeners = [];
|
|
624
|
+
channelListeners[type] = typeListeners;
|
|
625
|
+
}
|
|
626
|
+
typeListeners.push(callback);
|
|
653
627
|
};
|
|
654
|
-
var receiveBool =
|
|
655
|
-
|
|
628
|
+
var receiveBool = (channel, callback) => {
|
|
629
|
+
registerListener(channel, "broadcast::bool", callback);
|
|
656
630
|
};
|
|
657
|
-
var receiveBoolArray =
|
|
658
|
-
|
|
631
|
+
var receiveBoolArray = (channel, callback) => {
|
|
632
|
+
registerListener(channel, "broadcast::bool[]", callback);
|
|
659
633
|
};
|
|
660
|
-
var receiveNumber =
|
|
661
|
-
|
|
634
|
+
var receiveNumber = (channel, callback) => {
|
|
635
|
+
registerListener(channel, "broadcast::float", callback);
|
|
636
|
+
registerListener(channel, "broadcast::int", callback);
|
|
662
637
|
};
|
|
663
|
-
var receiveNumberArray =
|
|
664
|
-
|
|
638
|
+
var receiveNumberArray = (channel, callback) => {
|
|
639
|
+
registerListener(channel, "broadcast::float[]", callback);
|
|
640
|
+
registerListener(channel, "broadcast::int[]", callback);
|
|
665
641
|
};
|
|
666
|
-
var receiveString =
|
|
667
|
-
|
|
642
|
+
var receiveString = (channel, callback) => {
|
|
643
|
+
registerListener(channel, "broadcast::string", callback);
|
|
668
644
|
};
|
|
669
|
-
var receiveStringArray =
|
|
670
|
-
|
|
645
|
+
var receiveStringArray = (channel, callback) => {
|
|
646
|
+
registerListener(channel, "broadcast::string[]", callback);
|
|
671
647
|
};
|
|
672
|
-
var
|
|
673
|
-
|
|
648
|
+
var receiveVector2 = (channel, callback) => {
|
|
649
|
+
registerListener(channel, "broadcast::vector2", callback);
|
|
674
650
|
};
|
|
675
|
-
var
|
|
676
|
-
|
|
651
|
+
var receiveVector2Array = (channel, callback) => {
|
|
652
|
+
registerListener(channel, "broadcast::vector2[]", callback);
|
|
677
653
|
};
|
|
678
|
-
var
|
|
679
|
-
|
|
654
|
+
var receiveVector3 = (channel, callback) => {
|
|
655
|
+
registerListener(channel, "broadcast::vector3", callback);
|
|
680
656
|
};
|
|
681
|
-
var
|
|
682
|
-
|
|
657
|
+
var receiveVector3Array = (channel, callback) => {
|
|
658
|
+
registerListener(channel, "broadcast::vector3[]", callback);
|
|
683
659
|
};
|
|
684
|
-
var
|
|
685
|
-
|
|
660
|
+
var receiveQuaternion = (channel, callback) => {
|
|
661
|
+
registerListener(channel, "broadcast::quaternion", callback);
|
|
686
662
|
};
|
|
687
|
-
var
|
|
688
|
-
|
|
663
|
+
var receiveQuaternionArray = (channel, callback) => {
|
|
664
|
+
registerListener(channel, "broadcast::quaternion[]", callback);
|
|
689
665
|
};
|
|
690
|
-
var
|
|
691
|
-
|
|
666
|
+
var receiveColor = (channel, callback) => {
|
|
667
|
+
registerListener(channel, "broadcast::color", callback);
|
|
692
668
|
};
|
|
693
|
-
var
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
669
|
+
var receiveColorArray = (channel, callback) => {
|
|
670
|
+
registerListener(channel, "broadcast::color[]", callback);
|
|
671
|
+
};
|
|
672
|
+
var receiveJson = (channel, callback) => {
|
|
673
|
+
registerListener(channel, "broadcast::json", callback);
|
|
674
|
+
};
|
|
675
|
+
var clamp01 = (n) => Math.min(1, Math.max(0, n));
|
|
676
|
+
var hexPair = (n) => Math.round(clamp01(n) * 255).toString(16).padStart(2, "0");
|
|
677
|
+
var warnNotAColor = (val) => {
|
|
678
|
+
console.warn(`Colibri: '${JSON.stringify(val)}' is not a colour - expected "#RRGGBBAA" or [r,g,b,a]. Using black.`);
|
|
679
|
+
};
|
|
680
|
+
var asComponents = (val) => {
|
|
681
|
+
if (!Array.isArray(val) || val.length < 3) return null;
|
|
682
|
+
const numbers = val;
|
|
683
|
+
if (!numbers.every((c) => typeof c === "number" && !Number.isNaN(c))) return null;
|
|
684
|
+
return numbers;
|
|
685
|
+
};
|
|
686
|
+
var toHexColor = (val) => {
|
|
687
|
+
const rgba = toRgbaColor(val);
|
|
688
|
+
return `#${hexPair(rgba[0])}${hexPair(rgba[1])}${hexPair(rgba[2])}${hexPair(rgba[3])}`;
|
|
689
|
+
};
|
|
690
|
+
var toRgbaColor = (val) => {
|
|
691
|
+
const components = asComponents(val);
|
|
692
|
+
if (components !== null) {
|
|
693
|
+
return [
|
|
694
|
+
clamp01(components[0]),
|
|
695
|
+
clamp01(components[1]),
|
|
696
|
+
clamp01(components[2]),
|
|
697
|
+
components.length > 3 ? clamp01(components[3]) : 1
|
|
698
|
+
];
|
|
699
|
+
}
|
|
700
|
+
if (typeof val === "string") {
|
|
701
|
+
const hex = val.startsWith("#") ? val.slice(1) : val;
|
|
702
|
+
const digits = hex.length === 3 || hex.length === 4 ? hex.replace(/./g, (c) => c + c) : hex;
|
|
703
|
+
if ((digits.length === 6 || digits.length === 8) && /^[0-9a-f]+$/i.test(digits)) {
|
|
704
|
+
const byteAt = (i) => parseInt(digits.slice(i, i + 2), 16) / 255;
|
|
705
|
+
return [byteAt(0), byteAt(2), byteAt(4), digits.length === 8 ? byteAt(6) : 1];
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
warnNotAColor(val);
|
|
709
|
+
return [0, 0, 0, 1];
|
|
710
|
+
};
|
|
711
|
+
var unregister = (channel, callback) => {
|
|
712
|
+
const channelListeners = listeners[channel];
|
|
713
|
+
if (channelListeners === void 0) return;
|
|
714
|
+
let hasRemainingListeners = false;
|
|
715
|
+
for (const command in channelListeners) {
|
|
716
|
+
const commandListeners = channelListeners[command];
|
|
717
|
+
const index = commandListeners?.indexOf(callback) ?? -1;
|
|
718
|
+
if (index >= 0) {
|
|
719
|
+
commandListeners?.splice(index, 1);
|
|
720
|
+
}
|
|
721
|
+
if ((commandListeners?.length ?? 0) > 0) {
|
|
722
|
+
hasRemainingListeners = true;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (!hasRemainingListeners) {
|
|
726
|
+
const handler = channelHandlers[channel];
|
|
727
|
+
if (handler !== void 0) {
|
|
728
|
+
UnregisterChannel(channel, handler);
|
|
699
729
|
}
|
|
730
|
+
Reflect.deleteProperty(listeners, channel);
|
|
731
|
+
Reflect.deleteProperty(channelHandlers, channel);
|
|
732
|
+
}
|
|
700
733
|
};
|
|
701
734
|
var Sync = {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
735
|
+
sendBool,
|
|
736
|
+
sendBoolArray,
|
|
737
|
+
sendNumber,
|
|
738
|
+
sendNumberArray,
|
|
739
|
+
sendString,
|
|
740
|
+
sendStringArray,
|
|
741
|
+
sendVector2,
|
|
742
|
+
sendVector2Array,
|
|
743
|
+
sendVector3,
|
|
744
|
+
sendVector3Array,
|
|
745
|
+
sendQuaternion,
|
|
746
|
+
sendQuaternionArray,
|
|
747
|
+
sendColor,
|
|
748
|
+
sendColorArray,
|
|
749
|
+
sendJson,
|
|
750
|
+
receiveBool,
|
|
751
|
+
receiveBoolArray,
|
|
752
|
+
receiveNumber,
|
|
753
|
+
receiveNumberArray,
|
|
754
|
+
receiveString,
|
|
755
|
+
receiveStringArray,
|
|
756
|
+
receiveVector2,
|
|
757
|
+
receiveVector2Array,
|
|
758
|
+
receiveVector3,
|
|
759
|
+
receiveVector3Array,
|
|
760
|
+
receiveQuaternion,
|
|
761
|
+
receiveQuaternionArray,
|
|
762
|
+
receiveColor,
|
|
763
|
+
receiveColorArray,
|
|
764
|
+
receiveJson,
|
|
765
|
+
unregister,
|
|
766
|
+
// for better compatibility with Unity Colibri
|
|
767
|
+
// sendInt and sendIntArray still send the float commands (JS cannot tell 5 from 5.0), so a
|
|
768
|
+
// Unity client has to listen for float, not int.
|
|
769
|
+
sendFloat: sendNumber,
|
|
770
|
+
sendInt: sendNumber,
|
|
771
|
+
sendFloatArray: sendNumberArray,
|
|
772
|
+
sendIntArray: sendNumberArray
|
|
734
773
|
};
|
|
774
|
+
|
|
735
775
|
// src/RemoteLogger.ts
|
|
736
|
-
var
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
_this.consoleLog.apply(_this, _to_consumable_array(args));
|
|
759
|
-
_this.sendMessage("info", args);
|
|
760
|
-
};
|
|
761
|
-
console.info = function() {
|
|
762
|
-
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
763
|
-
args[_key] = arguments[_key];
|
|
764
|
-
}
|
|
765
|
-
_this.consoleInfo.apply(_this, _to_consumable_array(args));
|
|
766
|
-
_this.sendMessage("info", args);
|
|
767
|
-
};
|
|
768
|
-
console.warn = function() {
|
|
769
|
-
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
770
|
-
args[_key] = arguments[_key];
|
|
771
|
-
}
|
|
772
|
-
_this.consoleWarn.apply(_this, _to_consumable_array(args));
|
|
773
|
-
_this.sendMessage("warn", args);
|
|
774
|
-
};
|
|
775
|
-
console.error = function() {
|
|
776
|
-
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
777
|
-
args[_key] = arguments[_key];
|
|
778
|
-
}
|
|
779
|
-
_this.consoleError.apply(_this, _to_consumable_array(args));
|
|
780
|
-
_this.sendMessage("error", args);
|
|
781
|
-
};
|
|
776
|
+
var MAX_EARLY_LINES = 100;
|
|
777
|
+
var RemoteLogger = class _RemoteLogger {
|
|
778
|
+
/**
|
|
779
|
+
* Forwards everything logged through `console` to the server's log. Create one, at startup.
|
|
780
|
+
* A further instance warns once and does not patch again; its `enabled` argument, `enable()` and
|
|
781
|
+
* `disable()` act on the first one.
|
|
782
|
+
* @param enabled whether to forward from the start
|
|
783
|
+
*/
|
|
784
|
+
constructor(enabled = true) {
|
|
785
|
+
this.enabled = enabled;
|
|
786
|
+
const patched = _RemoteLogger.patched;
|
|
787
|
+
if (patched) {
|
|
788
|
+
this.forwarder = patched;
|
|
789
|
+
patched.enabled = enabled;
|
|
790
|
+
if (!patched.hasWarnedOfAnotherInstance) {
|
|
791
|
+
patched.hasWarnedOfAnotherInstance = true;
|
|
792
|
+
console.warn(
|
|
793
|
+
"RemoteLogger: the console is already forwarded by an earlier new RemoteLogger(), so this one controls that instead of forwarding every line a second time. Create one, at startup."
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
return;
|
|
782
797
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
798
|
+
_RemoteLogger.patched = this;
|
|
799
|
+
this.forwarder = this;
|
|
800
|
+
console.debug = (...args) => {
|
|
801
|
+
this.consoleDebug(...args);
|
|
802
|
+
this.sendMessage("debug", args);
|
|
803
|
+
};
|
|
804
|
+
console.log = (...args) => {
|
|
805
|
+
this.consoleLog(...args);
|
|
806
|
+
this.sendMessage("info", args);
|
|
807
|
+
};
|
|
808
|
+
console.info = (...args) => {
|
|
809
|
+
this.consoleInfo(...args);
|
|
810
|
+
this.sendMessage("info", args);
|
|
811
|
+
};
|
|
812
|
+
console.warn = (...args) => {
|
|
813
|
+
this.consoleWarn(...args);
|
|
814
|
+
this.sendMessage("warn", args);
|
|
815
|
+
};
|
|
816
|
+
console.error = (...args) => {
|
|
817
|
+
this.consoleError(...args);
|
|
818
|
+
this.sendMessage("error", args);
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
// The instance that patched the console. Later instances delegate to it, since patching again
|
|
822
|
+
// would forward every line once per instance (StrictMode and hot reload create extras).
|
|
823
|
+
static patched = null;
|
|
824
|
+
// Itself, or the instance that patched the console.
|
|
825
|
+
forwarder;
|
|
826
|
+
hasWarnedOfAnotherInstance = false;
|
|
827
|
+
consoleDebug = console.debug;
|
|
828
|
+
consoleLog = console.log;
|
|
829
|
+
consoleInfo = console.info;
|
|
830
|
+
consoleWarn = console.warn;
|
|
831
|
+
consoleError = console.error;
|
|
832
|
+
// Recursion guard: lines logged while forwarding (by Colibri, or by a getter that JSON.stringify
|
|
833
|
+
// runs) go to the console only.
|
|
834
|
+
forwarding = false;
|
|
835
|
+
// Lines logged before `new Colibri()`.
|
|
836
|
+
earlyLines = [];
|
|
837
|
+
droppedEarlyLines = 0;
|
|
838
|
+
waitingForColibri = false;
|
|
839
|
+
/**
|
|
807
840
|
* @deprecated use new class constructor directly
|
|
808
|
-
*/
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
841
|
+
*/
|
|
842
|
+
static init(enabled = true) {
|
|
843
|
+
return new _RemoteLogger(enabled);
|
|
844
|
+
}
|
|
845
|
+
sendMessage(level, args) {
|
|
846
|
+
if (!this.enabled || this.forwarding) return;
|
|
847
|
+
this.forwarding = true;
|
|
848
|
+
try {
|
|
849
|
+
const message = [...args].map(stringify).join().trim();
|
|
850
|
+
const colibri = Colibri.getInstance(false);
|
|
851
|
+
if (colibri) colibri.sendMessage("log", level, message);
|
|
852
|
+
else this.keepUntilColibriExists(level, message);
|
|
853
|
+
} catch (error) {
|
|
854
|
+
this.consoleError("RemoteLogger: could not forward a log line.", error);
|
|
855
|
+
} finally {
|
|
856
|
+
this.forwarding = false;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
keepUntilColibriExists(level, message) {
|
|
860
|
+
if (this.earlyLines.length >= MAX_EARLY_LINES) {
|
|
861
|
+
this.droppedEarlyLines++;
|
|
862
|
+
return;
|
|
819
863
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
864
|
+
this.earlyLines.push({ level, message });
|
|
865
|
+
if (!this.waitingForColibri) {
|
|
866
|
+
this.waitingForColibri = true;
|
|
867
|
+
whenColibriCreated((colibri) => {
|
|
868
|
+
this.sendEarlyLines(colibri);
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
// Socket.IO buffers emits until connected, so there is no need to wait for the connection.
|
|
873
|
+
sendEarlyLines(colibri) {
|
|
874
|
+
this.waitingForColibri = false;
|
|
875
|
+
for (const { level, message } of this.earlyLines.splice(0)) {
|
|
876
|
+
colibri.sendMessage("log", level, message);
|
|
877
|
+
}
|
|
878
|
+
if (this.droppedEarlyLines > 0) {
|
|
879
|
+
colibri.sendMessage(
|
|
880
|
+
"log",
|
|
881
|
+
"warn",
|
|
882
|
+
`RemoteLogger: ${this.droppedEarlyLines} more line(s) logged before new Colibri() were not kept, only the first ${MAX_EARLY_LINES}.`
|
|
883
|
+
);
|
|
884
|
+
this.droppedEarlyLines = 0;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
/** Starts forwarding console output to the server's log. */
|
|
888
|
+
enable() {
|
|
889
|
+
this.forwarder.enabled = true;
|
|
890
|
+
}
|
|
891
|
+
/** Stops forwarding to the server's log; the console still prints. */
|
|
892
|
+
disable() {
|
|
893
|
+
this.forwarder.enabled = false;
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
var stringify = (obj) => {
|
|
897
|
+
if (obj instanceof Error) {
|
|
898
|
+
return obj.message + "\n" + (obj.stack ?? "");
|
|
899
|
+
}
|
|
900
|
+
if (typeof obj === "string") return obj;
|
|
901
|
+
const cache = [];
|
|
902
|
+
let str;
|
|
903
|
+
try {
|
|
904
|
+
str = JSON.stringify(
|
|
905
|
+
obj,
|
|
906
|
+
(_, value) => {
|
|
907
|
+
if (typeof value === "bigint") return value.toString();
|
|
823
908
|
if (typeof value === "object" && value !== null) {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
909
|
+
if (cache.indexOf(value) !== -1) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
cache.push(value);
|
|
828
913
|
}
|
|
829
914
|
return value;
|
|
830
|
-
|
|
831
|
-
|
|
915
|
+
},
|
|
916
|
+
2
|
|
917
|
+
);
|
|
918
|
+
} catch {
|
|
919
|
+
str = String(obj);
|
|
920
|
+
}
|
|
921
|
+
return str;
|
|
832
922
|
};
|
|
923
|
+
|
|
833
924
|
// src/ModelSynchronization.ts
|
|
925
|
+
var import_rxjs3 = require("rxjs");
|
|
926
|
+
|
|
927
|
+
// src/SyncModel.ts
|
|
834
928
|
var import_rxjs2 = require("rxjs");
|
|
835
|
-
var
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
929
|
+
var SyncModel = class _SyncModel {
|
|
930
|
+
id;
|
|
931
|
+
// True while update() applies a remote update, so the setters do not echo it.
|
|
932
|
+
applyingRemoteUpdate = false;
|
|
933
|
+
modelChanges = new import_rxjs2.Subject();
|
|
934
|
+
modelChanges$ = this.modelChanges.pipe(
|
|
935
|
+
// auditTime arms its timer on the first change only; bufferTime keeps an interval running while idle.
|
|
936
|
+
(0, import_rxjs2.buffer)(this.modelChanges.pipe((0, import_rxjs2.auditTime)(1))),
|
|
937
|
+
(0, import_rxjs2.map)((changes) => changes.filter(this.uniq)),
|
|
938
|
+
(0, import_rxjs2.filter)((changes) => changes.length > 0),
|
|
939
|
+
(0, import_rxjs2.share)()
|
|
940
|
+
);
|
|
941
|
+
// Emits once on delete(), which RegisterModelSync sends on as model::delete. Public only for RegisterModelSync.
|
|
942
|
+
deletions = new import_rxjs2.Subject();
|
|
943
|
+
// ES private: a subclass may have a `deleted` of its own.
|
|
944
|
+
#deleted = false;
|
|
945
|
+
constructor(id) {
|
|
946
|
+
this.id = id;
|
|
947
|
+
}
|
|
948
|
+
uniq(value, index, array) {
|
|
949
|
+
return array.indexOf(value) === index;
|
|
950
|
+
}
|
|
951
|
+
onModelChanges(prop) {
|
|
952
|
+
this.modelChanges.next(prop);
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Deletes the model for every client of the app (Unity destroys its GameObject) and removes it
|
|
956
|
+
* from this client's list. The model stops syncing.
|
|
957
|
+
*/
|
|
958
|
+
delete() {
|
|
959
|
+
if (this.#deleted) return;
|
|
960
|
+
this.#deleted = true;
|
|
961
|
+
if (!this.deletions.observed) {
|
|
962
|
+
console.warn(
|
|
963
|
+
`Colibri: delete() of '${this.id}', which no RegisterModelSync lists. Register it, or take it from the list RegisterModelSync gives, to delete it for every client.`
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
_SyncModel.prototype.detach.call(this);
|
|
967
|
+
this.deletions.next();
|
|
968
|
+
this.deletions.complete();
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Stops syncing the model on this client only; every other client keeps it. RegisterModelSync
|
|
972
|
+
* calls it on a remote delete and when registerModel replaces a listed copy.
|
|
973
|
+
*/
|
|
974
|
+
detach() {
|
|
975
|
+
this.modelChanges.complete();
|
|
976
|
+
}
|
|
977
|
+
registerSyncedProperty(name, prop) {
|
|
978
|
+
let syncedProperties = this.__syncedProperties;
|
|
979
|
+
if (syncedProperties === void 0) {
|
|
980
|
+
syncedProperties = {};
|
|
981
|
+
Object.defineProperty(this, "__syncedProperties", {
|
|
982
|
+
value: syncedProperties,
|
|
983
|
+
enumerable: false,
|
|
984
|
+
writable: true,
|
|
985
|
+
configurable: true
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
syncedProperties[name] = prop;
|
|
989
|
+
}
|
|
990
|
+
getSyncedProperties() {
|
|
991
|
+
return this.__syncedProperties ?? {};
|
|
992
|
+
}
|
|
993
|
+
update(updates) {
|
|
994
|
+
const syncedProps = this.getSyncedProperties();
|
|
995
|
+
this.applyingRemoteUpdate = true;
|
|
996
|
+
try {
|
|
997
|
+
for (const key of Object.keys(updates)) {
|
|
998
|
+
if (key !== "id") {
|
|
999
|
+
const localKey = syncedProps[key];
|
|
1000
|
+
if (localKey) {
|
|
1001
|
+
this[localKey] = updates[key];
|
|
1002
|
+
} else {
|
|
1003
|
+
console.warn(`Unknown property ${key} in ${this.constructor.name}`);
|
|
1004
|
+
}
|
|
870
1005
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
});
|
|
888
|
-
SendMessage("".concat(name), "model::update", model.toJson());
|
|
889
|
-
models.next(_to_consumable_array(models.value).concat([
|
|
890
|
-
model
|
|
891
|
-
]));
|
|
892
|
-
};
|
|
893
|
-
return [
|
|
894
|
-
models.asObservable(),
|
|
895
|
-
registerModel
|
|
896
|
-
];
|
|
1006
|
+
}
|
|
1007
|
+
} finally {
|
|
1008
|
+
this.applyingRemoteUpdate = false;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
toJson(attributes = []) {
|
|
1012
|
+
const json = { id: this.id };
|
|
1013
|
+
const syncedProps = this.getSyncedProperties();
|
|
1014
|
+
for (const key of Object.keys(syncedProps)) {
|
|
1015
|
+
const localKey = syncedProps[key];
|
|
1016
|
+
if (attributes.length === 0 || attributes.includes(localKey)) {
|
|
1017
|
+
json[key] = this[localKey];
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
return json;
|
|
1021
|
+
}
|
|
897
1022
|
};
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
var
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1023
|
+
|
|
1024
|
+
// src/ModelSynchronization.ts
|
|
1025
|
+
var toJsonOf = (model, attributes) => SyncModel.prototype.toJson.call(model, attributes);
|
|
1026
|
+
var updateOf = (model, updates) => {
|
|
1027
|
+
SyncModel.prototype.update.call(model, updates);
|
|
1028
|
+
};
|
|
1029
|
+
var detachOf = (model) => {
|
|
1030
|
+
if (model) SyncModel.prototype.detach.call(model);
|
|
1031
|
+
};
|
|
1032
|
+
var withColibri = (action) => {
|
|
1033
|
+
const colibri = Colibri.getInstance(false);
|
|
1034
|
+
if (colibri) action(colibri);
|
|
1035
|
+
else whenColibriCreated(action);
|
|
1036
|
+
};
|
|
1037
|
+
var warnedMinifiedNames = /* @__PURE__ */ new Set();
|
|
1038
|
+
var warnIfMinified = (className, channel) => {
|
|
1039
|
+
if (channel.length > 2 && !channel.includes("$") || warnedMinifiedNames.has(channel)) return;
|
|
1040
|
+
warnedMinifiedNames.add(channel);
|
|
1041
|
+
console.warn(
|
|
1042
|
+
`Colibri: RegisterModelSync is syncing on channel '${channel}', derived from the class name '${className}', which looks minified. A minified build renames classes, so this will not sync with another build or with Unity. Pass the channel explicitly: RegisterModelSync({ name: '...', type: ${className || "YourModel"} }).`
|
|
1043
|
+
);
|
|
1044
|
+
};
|
|
1045
|
+
var isBare = (modelData) => Object.keys(modelData).every((key) => key === "id");
|
|
1046
|
+
var ownOnChannel = /* @__PURE__ */ new Map();
|
|
1047
|
+
var guardsOnChannel = /* @__PURE__ */ new Map();
|
|
1048
|
+
var END_CHANNEL = "colibri::reconnect";
|
|
1049
|
+
var endRequests = 0;
|
|
1050
|
+
var askForEnd = (colibri) => {
|
|
1051
|
+
const id = `colibri-web-${++endRequests}`;
|
|
1052
|
+
colibri.sendMessage(END_CHANNEL, "model::request", { id, again: true });
|
|
1053
|
+
return id;
|
|
1054
|
+
};
|
|
1055
|
+
var KNOWN_VALUES_KEPT = 8;
|
|
1056
|
+
var KNOWN_VALUES_MS = 1e4;
|
|
1057
|
+
var DELETED_HERE_MS = 6e4;
|
|
1058
|
+
var AFRESH_ANSWER_MS = 1e4;
|
|
1059
|
+
var RegisterModelSync = (registration) => {
|
|
1060
|
+
const name = registration.name || registration.type.name.toLowerCase();
|
|
1061
|
+
if (!registration.name) warnIfMinified(registration.type.name, name);
|
|
1062
|
+
const models = new import_rxjs3.BehaviorSubject([]);
|
|
1063
|
+
const deletedHere = /* @__PURE__ */ new Map();
|
|
1064
|
+
const rememberDeletedHere = (id) => {
|
|
1065
|
+
const now = Date.now();
|
|
1066
|
+
for (const [old, at] of deletedHere) {
|
|
1067
|
+
if (now - at < DELETED_HERE_MS) break;
|
|
1068
|
+
deletedHere.delete(old);
|
|
1069
|
+
}
|
|
1070
|
+
deletedHere.delete(id);
|
|
1071
|
+
deletedHere.set(id, now);
|
|
1072
|
+
};
|
|
1073
|
+
const afresh = /* @__PURE__ */ new Map();
|
|
1074
|
+
const recentlyDeletedHere = (id) => {
|
|
1075
|
+
const at = deletedHere.get(id);
|
|
1076
|
+
if (at === void 0) return false;
|
|
1077
|
+
if (Date.now() - at < DELETED_HERE_MS) return true;
|
|
1078
|
+
deletedHere.delete(id);
|
|
1079
|
+
return false;
|
|
1080
|
+
};
|
|
1081
|
+
const ownModels = /* @__PURE__ */ new WeakSet();
|
|
1082
|
+
const awaitingAnswer = /* @__PURE__ */ new Map();
|
|
1083
|
+
const answered = /* @__PURE__ */ new WeakSet();
|
|
1084
|
+
let catchingUpThrough;
|
|
1085
|
+
const deletedWhileAwaited = /* @__PURE__ */ new Set();
|
|
1086
|
+
const heldChanges = /* @__PURE__ */ new WeakMap();
|
|
1087
|
+
const changesToSend = /* @__PURE__ */ new WeakMap();
|
|
1088
|
+
const noteChangeToSend = (model, change) => {
|
|
1089
|
+
const toSend = changesToSend.get(model);
|
|
1090
|
+
if (toSend) toSend.add(change);
|
|
1091
|
+
else changesToSend.set(model, /* @__PURE__ */ new Set([change]));
|
|
1092
|
+
};
|
|
1093
|
+
const confirming = /* @__PURE__ */ new Map();
|
|
1094
|
+
const endConfirmation = (id) => {
|
|
1095
|
+
const confirmation = confirming.get(id);
|
|
1096
|
+
confirming.delete(id);
|
|
1097
|
+
return confirmation;
|
|
1098
|
+
};
|
|
1099
|
+
const knownValues = /* @__PURE__ */ new WeakMap();
|
|
1100
|
+
const askedAfterOutage = /* @__PURE__ */ new Map();
|
|
1101
|
+
let roundEnd;
|
|
1102
|
+
let guardEnds = [];
|
|
1103
|
+
let keptOut = /* @__PURE__ */ new WeakMap();
|
|
1104
|
+
const answerMayFollow = /* @__PURE__ */ new Map();
|
|
1105
|
+
let lastHeardBeforeOutage = 0;
|
|
1106
|
+
let disconnected = false;
|
|
1107
|
+
const hasOwn = (id) => models.value.some((m) => m.id === id && ownModels.has(m));
|
|
1108
|
+
const onChannel = ownOnChannel.get(name);
|
|
1109
|
+
if (onChannel) onChannel.add(hasOwn);
|
|
1110
|
+
else ownOnChannel.set(name, /* @__PURE__ */ new Set([hasOwn]));
|
|
1111
|
+
const guard = (end) => {
|
|
1112
|
+
guardEnds.push(end);
|
|
1113
|
+
};
|
|
1114
|
+
const guards = guardsOnChannel.get(name);
|
|
1115
|
+
if (guards) guards.add(guard);
|
|
1116
|
+
else guardsOnChannel.set(name, /* @__PURE__ */ new Set([guard]));
|
|
1117
|
+
const askFor = (colibri, model) => {
|
|
1118
|
+
awaitingAnswer.set(model.id, colibri);
|
|
1119
|
+
colibri.sendMessage(
|
|
1120
|
+
name,
|
|
1121
|
+
"model::request",
|
|
1122
|
+
answered.has(model) ? { id: model.id, again: true } : { id: model.id }
|
|
1123
|
+
);
|
|
1124
|
+
};
|
|
1125
|
+
const sendAfresh = (colibri, model) => {
|
|
1126
|
+
colibri.sendMessage(name, "model::request", { id: model.id });
|
|
1127
|
+
afresh.set(model.id, Date.now());
|
|
1128
|
+
answered.add(model);
|
|
1129
|
+
releaseHeldChanges(model);
|
|
1130
|
+
sendUpdate(colibri, model, toJsonOf(model));
|
|
1131
|
+
};
|
|
1132
|
+
const catchUpOnceAnswered = () => {
|
|
1133
|
+
if (!catchingUpThrough || awaitingAnswer.size > 0) return false;
|
|
1134
|
+
const colibri = catchingUpThrough;
|
|
1135
|
+
catchingUpThrough = void 0;
|
|
1136
|
+
colibri.sendMessage(name, "model::request");
|
|
1137
|
+
guardFrom(colibri);
|
|
1138
|
+
return true;
|
|
1139
|
+
};
|
|
1140
|
+
const guardFrom = (colibri) => {
|
|
1141
|
+
const end = askForEnd(colibri);
|
|
1142
|
+
for (const guardOnChannel of guardsOnChannel.get(name) ?? []) guardOnChannel(end);
|
|
1143
|
+
};
|
|
1144
|
+
const keepOut = (model, update) => {
|
|
1145
|
+
const end = guardEnds.at(-1);
|
|
1146
|
+
if (end === void 0) return;
|
|
1147
|
+
let kept = keptOut.get(model);
|
|
1148
|
+
if (!kept) keptOut.set(model, kept = /* @__PURE__ */ new Map());
|
|
1149
|
+
for (const key of Object.keys(update)) if (key !== "id") kept.set(key, end);
|
|
1150
|
+
};
|
|
1151
|
+
const keptOutOf = (model) => [...keptOut.get(model) ?? []].filter(([, end]) => guardEnds.includes(end)).map(([key]) => key);
|
|
1152
|
+
const endGuard = (end) => {
|
|
1153
|
+
guardEnds = end === void 0 ? [] : guardEnds.slice(guardEnds.indexOf(end) + 1);
|
|
1154
|
+
if (guardEnds.length > 0) return;
|
|
1155
|
+
keptOut = /* @__PURE__ */ new WeakMap();
|
|
1156
|
+
answerMayFollow.clear();
|
|
1157
|
+
};
|
|
1158
|
+
withColibri((colibri) => {
|
|
1159
|
+
catchingUpThrough = colibri;
|
|
1160
|
+
setTimeout(catchUpOnceAnswered, 0);
|
|
1161
|
+
onColibriDisconnected(colibri, () => {
|
|
1162
|
+
disconnected = true;
|
|
1163
|
+
endGuard();
|
|
1164
|
+
if (askedAfterOutage.size === 0) lastHeardBeforeOutage = lastHeardFrom(colibri) ?? Date.now();
|
|
1165
|
+
const since = (lastHeardFrom(colibri) ?? Date.now()) - 1e3;
|
|
1166
|
+
for (const [id, at] of deletedHere) {
|
|
1167
|
+
if (at >= since) colibri.sendMessage(name, "model::delete", { id });
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
onColibriReconnected(colibri, () => {
|
|
1171
|
+
disconnected = false;
|
|
1172
|
+
awaitingAnswer.clear();
|
|
1173
|
+
deletedWhileAwaited.clear();
|
|
1174
|
+
askedAfterOutage.clear();
|
|
1175
|
+
afresh.clear();
|
|
1176
|
+
for (const id of [...confirming.keys()]) {
|
|
1177
|
+
const confirmation = endConfirmation(id);
|
|
1178
|
+
const model = models.value.find((m) => m.id === id);
|
|
1179
|
+
if (confirmation && model && ownModels.has(model)) holdChanges(model, confirmation.props);
|
|
1180
|
+
}
|
|
1181
|
+
catchingUpThrough = colibri;
|
|
1182
|
+
for (const model of models.value) {
|
|
1183
|
+
if (!ownModels.has(model)) continue;
|
|
1184
|
+
if (answered.has(model)) askedAfterOutage.set(model.id, { shown: /* @__PURE__ */ new Map(), lost: /* @__PURE__ */ new Set() });
|
|
1185
|
+
askFor(colibri, model);
|
|
1186
|
+
}
|
|
1187
|
+
roundEnd = askedAfterOutage.size > 0 ? askForEnd(colibri) : void 0;
|
|
1188
|
+
catchUpOnceAnswered();
|
|
1189
|
+
});
|
|
1190
|
+
});
|
|
1191
|
+
RegisterChannel(name, (payload) => {
|
|
1192
|
+
if (payload.command === "model::update") {
|
|
1193
|
+
onUpdate(payload.payload);
|
|
1194
|
+
} else if (payload.command === "model::delete") {
|
|
1195
|
+
onDelete(payload.payload.id);
|
|
1196
|
+
} else {
|
|
1197
|
+
console.error(`Unknown model command: ${payload.command}`);
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
RegisterChannel(END_CHANNEL, (message) => {
|
|
1201
|
+
if (message.command !== "model::update") return;
|
|
1202
|
+
const id = message.payload?.id;
|
|
1203
|
+
if (typeof id !== "string") return;
|
|
1204
|
+
if (id === roundEnd) endRound();
|
|
1205
|
+
if (guardEnds.includes(id)) endGuard(id);
|
|
1206
|
+
const confirmed = [...confirming].find(([, confirmation]) => confirmation.end === id);
|
|
1207
|
+
if (confirmed) endWait(confirmed[0]);
|
|
1208
|
+
});
|
|
1209
|
+
const onUpdate = (modelData) => {
|
|
1210
|
+
const model = models.value.find((m) => m.id === modelData.id);
|
|
1211
|
+
const id = modelData.id;
|
|
1212
|
+
const afreshSince = id === void 0 ? void 0 : afresh.get(id);
|
|
1213
|
+
if (id !== void 0 && afreshSince !== void 0) {
|
|
1214
|
+
const waiting = Date.now() - afreshSince < AFRESH_ANSWER_MS;
|
|
1215
|
+
if (isBare(modelData) || !waiting) afresh.delete(id);
|
|
1216
|
+
if (isBare(modelData) || waiting) return;
|
|
1217
|
+
}
|
|
1218
|
+
if (id !== void 0 && deletedWhileAwaited.delete(id) && !model && isBare(modelData)) return;
|
|
1219
|
+
const asker = id === void 0 ? void 0 : awaitingAnswer.get(id);
|
|
1220
|
+
if (id !== void 0 && asker) {
|
|
1221
|
+
takeAnswer(id, asker, model, modelData);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (!model && id !== void 0 && isBare(modelData) && ownElsewhere(id)) return;
|
|
1225
|
+
applyUpdate(model, modelData);
|
|
1226
|
+
};
|
|
1227
|
+
const ownElsewhere = (id) => [...ownOnChannel.get(name) ?? []].some((has) => has !== hasOwn && has(id));
|
|
1228
|
+
const takeAnswer = (id, asker, model, modelData) => {
|
|
1229
|
+
if (model) answered.add(model);
|
|
1230
|
+
if (model && !ownModels.has(model)) {
|
|
1231
|
+
askedAfterOutage.delete(id);
|
|
1232
|
+
awaitingAnswer.delete(id);
|
|
1233
|
+
endConfirmation(id);
|
|
1234
|
+
if (!isBare(modelData)) applyUpdate(model, modelData);
|
|
1235
|
+
catchUpOnceAnswered();
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (!model || isBare(modelData)) {
|
|
1239
|
+
askedAfterOutage.delete(id);
|
|
1240
|
+
awaitingAnswer.delete(id);
|
|
1241
|
+
endConfirmation(id);
|
|
1242
|
+
if (model) {
|
|
1243
|
+
heldChanges.delete(model);
|
|
1244
|
+
sendUpdate(asker, model, toJsonOf(model));
|
|
1245
|
+
} else if (!isBare(modelData)) {
|
|
1246
|
+
applyUpdate(model, modelData);
|
|
1247
|
+
}
|
|
1248
|
+
catchUpOnceAnswered();
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
const afterOutage = askedAfterOutage.get(id);
|
|
1252
|
+
if (afterOutage) {
|
|
1253
|
+
const held2 = [...heldChanges.get(model) ?? []];
|
|
1254
|
+
for (const key of lostChanges(model, modelData, held2)) afterOutage.lost.add(key);
|
|
1255
|
+
for (const [key, value] of Object.entries(modelData)) afterOutage.shown.set(key, value);
|
|
1256
|
+
applyUpdate(model, withoutKeys(withoutChanges(modelData, model, held2), afterOutage.lost));
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
const confirmation = confirming.get(id);
|
|
1260
|
+
if (confirmation) {
|
|
1261
|
+
for (const key of confirmation.sent.keys()) {
|
|
1262
|
+
if (key in modelData) confirmation.shown.set(key, modelData[key]);
|
|
1263
|
+
}
|
|
1264
|
+
const held2 = [...heldChanges.get(model) ?? []];
|
|
1265
|
+
applyUpdate(model, withoutKeys(withoutChanges(modelData, model, held2), confirmation.sent.keys()));
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
awaitingAnswer.delete(id);
|
|
1269
|
+
const held = releaseHeldChanges(model);
|
|
1270
|
+
applyUpdate(model, withoutChanges(modelData, model, held));
|
|
1271
|
+
if (!models.value.includes(model)) {
|
|
1272
|
+
catchUpOnceAnswered();
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
if (sendOnTop(id, asker, model, modelData, held, [])) return;
|
|
1276
|
+
if (!catchUpOnceAnswered()) guardFrom(asker);
|
|
1277
|
+
const end = guardEnds.at(-1);
|
|
1278
|
+
if (end !== void 0) answerMayFollow.set(id, end);
|
|
1279
|
+
};
|
|
1280
|
+
const endRound = () => {
|
|
1281
|
+
roundEnd = void 0;
|
|
1282
|
+
const asked = [...askedAfterOutage];
|
|
1283
|
+
askedAfterOutage.clear();
|
|
1284
|
+
for (const [id, { shown, lost }] of asked) {
|
|
1285
|
+
const asker = awaitingAnswer.get(id);
|
|
1286
|
+
const model = models.value.find((m) => m.id === id);
|
|
1287
|
+
if (!asker || !model) continue;
|
|
1288
|
+
awaitingAnswer.delete(id);
|
|
1289
|
+
if (!ownModels.has(model)) continue;
|
|
1290
|
+
const held = releaseHeldChanges(model);
|
|
1291
|
+
if (lost.size > 0)
|
|
1292
|
+
remember(model, Object.fromEntries([...lost].map((key) => [key, shown.get(key)])));
|
|
1293
|
+
sendOnTop(id, asker, model, Object.fromEntries(shown), held, [...lost]);
|
|
1294
|
+
}
|
|
1295
|
+
catchUpOnceAnswered();
|
|
1296
|
+
};
|
|
1297
|
+
const sendOnTop = (id, asker, model, shown, held, lost) => {
|
|
1298
|
+
if (held.length === 0 && lost.length === 0) return false;
|
|
1299
|
+
const current = toJsonOf(model);
|
|
1300
|
+
const sent = held.length > 0 ? toJsonOf(model, held) : { id };
|
|
1301
|
+
for (const key of lost) sent[key] = current[key];
|
|
1302
|
+
sendUpdate(asker, model, sent);
|
|
1303
|
+
const differs = /* @__PURE__ */ new Map();
|
|
1304
|
+
for (const [key, value] of Object.entries(sent)) {
|
|
1305
|
+
const json = JSON.stringify(value);
|
|
1306
|
+
if (key === "id" || json === void 0) continue;
|
|
1307
|
+
if (!(key in shown) || JSON.stringify(shown[key]) !== json) differs.set(key, json);
|
|
913
1308
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1309
|
+
if (differs.size === 0) return false;
|
|
1310
|
+
const replaced = /* @__PURE__ */ new Map();
|
|
1311
|
+
for (const key of differs.keys()) {
|
|
1312
|
+
replaced.set(key, key in shown ? JSON.stringify(shown[key]) : void 0);
|
|
1313
|
+
}
|
|
1314
|
+
askFor(asker, model);
|
|
1315
|
+
confirming.set(id, { sent: differs, replaced, props: held, shown: /* @__PURE__ */ new Map(), end: askForEnd(asker) });
|
|
1316
|
+
return true;
|
|
1317
|
+
};
|
|
1318
|
+
const endWait = (id) => {
|
|
1319
|
+
const confirmation = endConfirmation(id);
|
|
1320
|
+
const asker = awaitingAnswer.get(id);
|
|
1321
|
+
const model = models.value.find((m) => m.id === id);
|
|
1322
|
+
if (!confirmation || !asker || !model) return;
|
|
1323
|
+
awaitingAnswer.delete(id);
|
|
1324
|
+
const held = releaseHeldChanges(model);
|
|
1325
|
+
const theirs = { id };
|
|
1326
|
+
const again = [];
|
|
1327
|
+
for (const [key, json] of confirmation.sent) {
|
|
1328
|
+
const shown = confirmation.shown.has(key) ? JSON.stringify(confirmation.shown.get(key)) : void 0;
|
|
1329
|
+
if (shown === json) continue;
|
|
1330
|
+
if (shown === void 0 || shown === confirmation.replaced.get(key)) again.push(key);
|
|
1331
|
+
else theirs[key] = confirmation.shown.get(key);
|
|
1332
|
+
}
|
|
1333
|
+
applyUpdate(model, withoutChanges(theirs, model, held));
|
|
1334
|
+
const current = toJsonOf(model);
|
|
1335
|
+
const update = held.length > 0 ? toJsonOf(model, held) : { id };
|
|
1336
|
+
for (const key of again) update[key] = current[key];
|
|
1337
|
+
if (ownModels.has(model) && Object.keys(update).length > 1) sendUpdate(asker, model, update);
|
|
1338
|
+
catchUpOnceAnswered();
|
|
1339
|
+
};
|
|
1340
|
+
const sendUpdate = (colibri, model, update) => {
|
|
1341
|
+
colibri.sendMessage(name, "model::update", update);
|
|
1342
|
+
remember(model, update, lastHeardFrom(colibri) ?? Number.POSITIVE_INFINITY);
|
|
1343
|
+
keepOut(model, update);
|
|
1344
|
+
};
|
|
1345
|
+
const remember = (model, fields, heardAt) => {
|
|
1346
|
+
let known = knownValues.get(model);
|
|
1347
|
+
if (!known) knownValues.set(model, known = /* @__PURE__ */ new Map());
|
|
1348
|
+
const at = Date.now();
|
|
1349
|
+
for (const [key, field] of Object.entries(fields)) {
|
|
1350
|
+
const json = JSON.stringify(field);
|
|
1351
|
+
if (key === "id" || json === void 0) continue;
|
|
1352
|
+
const value = { json, at, sent: heardAt !== void 0 };
|
|
1353
|
+
known.set(key, heardAt === void 0 ? [value] : keepKnown([...known.get(key) ?? [], value], heardAt));
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
const keepKnown = (values, heardAt) => {
|
|
1357
|
+
const split = values.findIndex((v) => v.at > heardAt);
|
|
1358
|
+
if (split < 0) return values.slice(-KNOWN_VALUES_KEPT);
|
|
1359
|
+
const after = values.slice(split);
|
|
1360
|
+
const newest = after.slice(KNOWN_VALUES_KEPT).slice(-1);
|
|
1361
|
+
return [...values.slice(0, split).slice(-KNOWN_VALUES_KEPT), ...after.slice(0, KNOWN_VALUES_KEPT), ...newest];
|
|
1362
|
+
};
|
|
1363
|
+
const lostChanges = (model, modelData, held) => {
|
|
1364
|
+
const known = knownValues.get(model);
|
|
1365
|
+
if (!known) return [];
|
|
1366
|
+
const since = lastHeardBeforeOutage - KNOWN_VALUES_MS;
|
|
1367
|
+
const heldKeys = new Set(held.length > 0 ? Object.keys(toJsonOf(model, held)) : []);
|
|
1368
|
+
const current = toJsonOf(model);
|
|
1369
|
+
return Object.entries(modelData).filter(([key, value]) => {
|
|
1370
|
+
const values = known.get(key) ?? [];
|
|
1371
|
+
const last = values.at(-1);
|
|
1372
|
+
if (!last?.sent || heldKeys.has(key) || !(key in current)) return false;
|
|
1373
|
+
const json = JSON.stringify(value);
|
|
1374
|
+
const earlier = values.filter((_, i) => i + 1 < values.length && values[i + 1].at >= since);
|
|
1375
|
+
return json !== last.json && earlier.some((v) => v.json === json) && // Nothing to send when this client has what the server has anyway.
|
|
1376
|
+
JSON.stringify(current[key]) !== json;
|
|
1377
|
+
}).map(([key]) => key);
|
|
1378
|
+
};
|
|
1379
|
+
const withoutKeys = (modelData, keys) => {
|
|
1380
|
+
const drop = new Set(keys);
|
|
1381
|
+
return Object.fromEntries(Object.entries(modelData).filter(([key]) => !drop.has(key)));
|
|
1382
|
+
};
|
|
1383
|
+
const applyUpdate = (model, modelData) => {
|
|
1384
|
+
if (model) {
|
|
1385
|
+
const unsent = [...changesToSend.get(model) ?? []];
|
|
1386
|
+
const update = withoutKeys(withoutChanges(modelData, model, unsent), keptOutOf(model));
|
|
1387
|
+
if (ownModels.has(model)) remember(model, update);
|
|
1388
|
+
updateOf(model, update);
|
|
1389
|
+
models.next([...models.value]);
|
|
1390
|
+
} else if (modelData.id) {
|
|
1391
|
+
if (recentlyDeletedHere(modelData.id)) return;
|
|
1392
|
+
const newModel = new registration.type(modelData.id);
|
|
1393
|
+
newModel.deletions.subscribe(() => {
|
|
1394
|
+
deleteHere(newModel);
|
|
1395
|
+
});
|
|
1396
|
+
newModel.modelChanges.subscribe((change) => {
|
|
1397
|
+
noteChangeToSend(newModel, change);
|
|
1398
|
+
});
|
|
1399
|
+
newModel.modelChanges$.subscribe((changes) => {
|
|
1400
|
+
changesToSend.delete(newModel);
|
|
1401
|
+
const update = toJsonOf(newModel, changes);
|
|
1402
|
+
SendMessage(name, "model::update", update);
|
|
1403
|
+
keepOut(newModel, update);
|
|
1404
|
+
models.next([...models.value]);
|
|
1405
|
+
});
|
|
1406
|
+
updateOf(newModel, modelData);
|
|
1407
|
+
models.next([...models.value, newModel]);
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
const holdChanges = (model, changes) => {
|
|
1411
|
+
const held = heldChanges.get(model);
|
|
1412
|
+
if (held) for (const change of changes) held.add(change);
|
|
1413
|
+
else heldChanges.set(model, new Set(changes));
|
|
1414
|
+
};
|
|
1415
|
+
const releaseHeldChanges = (model) => {
|
|
1416
|
+
const held = [...heldChanges.get(model) ?? []];
|
|
1417
|
+
heldChanges.delete(model);
|
|
1418
|
+
return held;
|
|
1419
|
+
};
|
|
1420
|
+
const withoutChanges = (modelData, model, changes) => {
|
|
1421
|
+
if (changes.length === 0) return modelData;
|
|
1422
|
+
const changed = new Set(Object.keys(toJsonOf(model, changes)).filter((key) => key !== "id"));
|
|
1423
|
+
return Object.fromEntries(Object.entries(modelData).filter(([key]) => !changed.has(key)));
|
|
1424
|
+
};
|
|
1425
|
+
const onDelete = (id) => {
|
|
1426
|
+
const model = models.value.find((m) => m.id === id);
|
|
1427
|
+
detachOf(model);
|
|
1428
|
+
forget(id, model);
|
|
1429
|
+
};
|
|
1430
|
+
const deleteHere = (model) => {
|
|
1431
|
+
if (models.value.find((m) => m.id === model.id) !== model) return;
|
|
1432
|
+
rememberDeletedHere(model.id);
|
|
1433
|
+
forget(model.id, model);
|
|
1434
|
+
withColibri((colibri) => {
|
|
1435
|
+
colibri.sendMessage(name, "model::delete", { id: model.id });
|
|
1436
|
+
});
|
|
1437
|
+
};
|
|
1438
|
+
const forget = (id, model) => {
|
|
1439
|
+
if (model) heldChanges.delete(model);
|
|
1440
|
+
askedAfterOutage.delete(id);
|
|
1441
|
+
models.next(models.value.filter((m) => m.id !== id));
|
|
1442
|
+
endConfirmation(id);
|
|
1443
|
+
const answerEnd = answerMayFollow.get(id);
|
|
1444
|
+
answerMayFollow.delete(id);
|
|
1445
|
+
if (awaitingAnswer.delete(id)) {
|
|
1446
|
+
deletedWhileAwaited.add(id);
|
|
1447
|
+
catchUpOnceAnswered();
|
|
1448
|
+
} else if (answerEnd !== void 0 && guardEnds.includes(answerEnd)) {
|
|
1449
|
+
deletedWhileAwaited.add(id);
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
const registerModel = (model) => {
|
|
1453
|
+
const listed = models.value.find((m) => m.id === model.id);
|
|
1454
|
+
if (listed === model) return;
|
|
1455
|
+
if (listed && ownModels.has(listed)) {
|
|
1456
|
+
console.warn(
|
|
1457
|
+
`Colibri: registerModel was given a second model with the id '${model.id}' on channel '${name}'. It replaces the first one, which no longer syncs.`
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
const mustHold = () => !Colibri.getInstance(false) || disconnected || awaitingAnswer.has(model.id);
|
|
1461
|
+
model.modelChanges.subscribe((change) => {
|
|
1462
|
+
if (mustHold()) holdChanges(model, [change]);
|
|
1463
|
+
else noteChangeToSend(model, change);
|
|
1464
|
+
});
|
|
1465
|
+
model.modelChanges$.subscribe({
|
|
1466
|
+
next: () => {
|
|
1467
|
+
const changes = [...changesToSend.get(model) ?? []];
|
|
1468
|
+
changesToSend.delete(model);
|
|
1469
|
+
if (changes.length > 0) {
|
|
1470
|
+
const colibri = Colibri.getInstance(false);
|
|
1471
|
+
if (!colibri || mustHold()) holdChanges(model, changes);
|
|
1472
|
+
else sendUpdate(colibri, model, toJsonOf(model, changes));
|
|
1009
1473
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1474
|
+
if (models.value.includes(model)) models.next([...models.value]);
|
|
1475
|
+
},
|
|
1476
|
+
// delete() and detach() end the stream: nothing more is sent for the model, neither held changes
|
|
1477
|
+
// nor the whole model after a reconnect.
|
|
1478
|
+
complete: () => {
|
|
1479
|
+
ownModels.delete(model);
|
|
1480
|
+
heldChanges.delete(model);
|
|
1481
|
+
changesToSend.delete(model);
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
model.deletions.subscribe(() => {
|
|
1485
|
+
deleteHere(model);
|
|
1486
|
+
});
|
|
1487
|
+
const reused = recentlyDeletedHere(model.id);
|
|
1488
|
+
deletedHere.delete(model.id);
|
|
1489
|
+
ownModels.add(model);
|
|
1490
|
+
if (listed) {
|
|
1491
|
+
endConfirmation(model.id);
|
|
1492
|
+
askedAfterOutage.delete(model.id);
|
|
1493
|
+
detachOf(listed);
|
|
1494
|
+
models.next(models.value.map((m) => m === listed ? model : m));
|
|
1495
|
+
} else {
|
|
1496
|
+
models.next([...models.value, model]);
|
|
1497
|
+
}
|
|
1498
|
+
withColibri((colibri) => {
|
|
1499
|
+
if (disconnected || !ownModels.has(model)) return;
|
|
1500
|
+
if (reused) sendAfresh(colibri, model);
|
|
1501
|
+
else askFor(colibri, model);
|
|
1502
|
+
});
|
|
1503
|
+
};
|
|
1504
|
+
return [models.asObservable(), registerModel];
|
|
1505
|
+
};
|
|
1506
|
+
|
|
1013
1507
|
// src/Synced.ts
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
console.log("Set ".concat(key.toString(), " to ").concat(newVal));
|
|
1033
|
-
original = newVal;
|
|
1034
|
-
},
|
|
1035
|
-
enumerable: true,
|
|
1036
|
-
configurable: true
|
|
1037
|
-
});
|
|
1038
|
-
} else {
|
|
1039
|
-
var originalAccessors = {
|
|
1040
|
-
get: descriptor.get,
|
|
1041
|
-
set: descriptor.set
|
|
1042
|
-
};
|
|
1043
|
-
descriptor.get = function() {
|
|
1044
|
-
var ret = originalAccessors.get.call(this);
|
|
1045
|
-
return ret;
|
|
1046
|
-
};
|
|
1047
|
-
descriptor.set = function(newval) {
|
|
1048
|
-
var model = this;
|
|
1049
|
-
model.modelChanges.next(String(key));
|
|
1050
|
-
if (originalAccessors.set) {
|
|
1051
|
-
originalAccessors.set.call(this, newval);
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1508
|
+
function Synced(syncedName = "") {
|
|
1509
|
+
return function(value, context) {
|
|
1510
|
+
if (context.kind !== "accessor") {
|
|
1511
|
+
throw new Error("@Synced() must decorate an `accessor` member");
|
|
1512
|
+
}
|
|
1513
|
+
const key = String(context.name);
|
|
1514
|
+
const name = (syncedName || key).toLowerCase();
|
|
1515
|
+
context.addInitializer(function() {
|
|
1516
|
+
this.registerSyncedProperty(name, key);
|
|
1517
|
+
});
|
|
1518
|
+
return {
|
|
1519
|
+
get() {
|
|
1520
|
+
return value.get.call(this);
|
|
1521
|
+
},
|
|
1522
|
+
set(newVal) {
|
|
1523
|
+
value.set.call(this, newVal);
|
|
1524
|
+
if (!this.applyingRemoteUpdate) {
|
|
1525
|
+
this.modelChanges.next(key);
|
|
1054
1526
|
}
|
|
1527
|
+
}
|
|
1055
1528
|
};
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
function synced(model, values, names = {}) {
|
|
1532
|
+
for (const [key, initial] of Object.entries(values)) {
|
|
1533
|
+
if (initial === void 0) {
|
|
1534
|
+
throw new Error(`synced(): '${key}' has no start value. undefined is not JSON: use null for none.`);
|
|
1535
|
+
}
|
|
1536
|
+
if (key in model) {
|
|
1537
|
+
throw new Error(
|
|
1538
|
+
`synced(): '${key}' is already a member of ${model.constructor.name}, a synced property or one of SyncModel's. Give it another name.`
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
let value = initial;
|
|
1542
|
+
model.registerSyncedProperty((names[key] || key).toLowerCase(), key);
|
|
1543
|
+
Object.defineProperty(model, key, {
|
|
1544
|
+
enumerable: true,
|
|
1545
|
+
get: () => value,
|
|
1546
|
+
set: (newValue) => {
|
|
1547
|
+
value = newValue;
|
|
1548
|
+
if (!model.applyingRemoteUpdate) model.modelChanges.next(key);
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// src/SyncTransform.ts
|
|
1555
|
+
var _physicsId_dec, _scale_dec, _rotation_dec, _position_dec, _active_dec, _a, _init, _active, _position, _rotation, _scale, _physicsId;
|
|
1556
|
+
var SyncTransform = class extends (_a = SyncModel, _active_dec = [Synced()], _position_dec = [Synced()], _rotation_dec = [Synced()], _scale_dec = [Synced()], _physicsId_dec = [Synced()], _a) {
|
|
1557
|
+
constructor() {
|
|
1558
|
+
super(...arguments);
|
|
1559
|
+
__privateAdd(this, _active, __runInitializers(_init, 8, this, true)), __runInitializers(_init, 11, this);
|
|
1560
|
+
__privateAdd(this, _position, __runInitializers(_init, 12, this, [0, 0, 0])), __runInitializers(_init, 15, this);
|
|
1561
|
+
__privateAdd(this, _rotation, __runInitializers(_init, 16, this, [0, 0, 0, 1])), __runInitializers(_init, 19, this);
|
|
1562
|
+
__privateAdd(this, _scale, __runInitializers(_init, 20, this, [1, 1, 1])), __runInitializers(_init, 23, this);
|
|
1563
|
+
__privateAdd(this, _physicsId, __runInitializers(_init, 24, this, "")), __runInitializers(_init, 27, this);
|
|
1564
|
+
}
|
|
1056
1565
|
};
|
|
1566
|
+
_init = __decoratorStart(_a);
|
|
1567
|
+
_active = new WeakMap();
|
|
1568
|
+
_position = new WeakMap();
|
|
1569
|
+
_rotation = new WeakMap();
|
|
1570
|
+
_scale = new WeakMap();
|
|
1571
|
+
_physicsId = new WeakMap();
|
|
1572
|
+
__decorateElement(_init, 4, "active", _active_dec, SyncTransform, _active);
|
|
1573
|
+
__decorateElement(_init, 4, "position", _position_dec, SyncTransform, _position);
|
|
1574
|
+
__decorateElement(_init, 4, "rotation", _rotation_dec, SyncTransform, _rotation);
|
|
1575
|
+
__decorateElement(_init, 4, "scale", _scale_dec, SyncTransform, _scale);
|
|
1576
|
+
__decorateElement(_init, 4, "physicsId", _physicsId_dec, SyncTransform, _physicsId);
|
|
1577
|
+
__decoratorMetadata(_init, SyncTransform);
|
|
1578
|
+
var RegisterSyncTransforms = (modelId = "") => RegisterModelSync({
|
|
1579
|
+
name: modelId ? `synctransform_${modelId}` : "synctransform",
|
|
1580
|
+
type: SyncTransform
|
|
1581
|
+
});
|
|
1057
1582
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1058
1583
|
0 && (module.exports = {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1584
|
+
Colibri,
|
|
1585
|
+
ColibriError,
|
|
1586
|
+
DeleteRestApi,
|
|
1587
|
+
GetRestApi,
|
|
1588
|
+
PROTOCOL_VERSION,
|
|
1589
|
+
ProtocolMismatchError,
|
|
1590
|
+
PutRestApi,
|
|
1591
|
+
RegisterChannel,
|
|
1592
|
+
RegisterModelSync,
|
|
1593
|
+
RegisterOnce,
|
|
1594
|
+
RegisterSyncTransforms,
|
|
1595
|
+
RemoteLogger,
|
|
1596
|
+
SendMessage,
|
|
1597
|
+
Sync,
|
|
1598
|
+
SyncModel,
|
|
1599
|
+
SyncTransform,
|
|
1600
|
+
Synced,
|
|
1601
|
+
UnregisterChannel,
|
|
1602
|
+
synced,
|
|
1603
|
+
toHexColor,
|
|
1604
|
+
toRgbaColor
|
|
1071
1605
|
});
|
|
1072
1606
|
//# sourceMappingURL=index.cjs.map
|