@colyseus/schema 3.0.0-alpha.40 → 3.0.0-alpha.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/build/cjs/index.js +1216 -1221
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +1216 -1222
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +1216 -1221
- package/lib/annotations.d.ts +1 -1
- package/lib/annotations.js.map +1 -1
- package/lib/codegen/languages/csharp.js +1 -44
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/decoder/DecodeOperation.d.ts +3 -3
- package/lib/decoder/DecodeOperation.js +9 -9
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.js +5 -5
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/encoder/EncodeOperation.js +7 -7
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.js +4 -4
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoding/assert.d.ts +5 -5
- package/lib/encoding/assert.js +8 -0
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.d.ts +36 -19
- package/lib/encoding/decode.js +53 -82
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +36 -17
- package/lib/encoding/encode.js +49 -39
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +2 -4
- package/lib/encoding/spec.js +0 -2
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +4 -6
- package/lib/index.js +6 -5
- package/lib/index.js.map +1 -1
- package/lib/types/registry.d.ts +8 -1
- package/lib/types/registry.js +20 -2
- package/lib/types/registry.js.map +1 -1
- package/package.json +1 -1
- package/src/annotations.ts +3 -1
- package/src/codegen/languages/csharp.ts +1 -47
- package/src/decoder/DecodeOperation.ts +6 -6
- package/src/decoder/Decoder.ts +3 -3
- package/src/encoder/EncodeOperation.ts +1 -1
- package/src/encoder/Encoder.ts +1 -1
- package/src/encoding/assert.ts +13 -5
- package/src/encoding/decode.ts +73 -93
- package/src/encoding/encode.ts +64 -36
- package/src/encoding/spec.ts +2 -5
- package/src/index.ts +4 -6
- package/src/types/registry.ts +22 -3
package/build/cjs/index.js
CHANGED
|
@@ -20,8 +20,6 @@ exports.OPERATION = void 0;
|
|
|
20
20
|
/**
|
|
21
21
|
* ArraySchema operations
|
|
22
22
|
*/
|
|
23
|
-
OPERATION[OPERATION["PUSH"] = 11] = "PUSH";
|
|
24
|
-
OPERATION[OPERATION["UNSHIFT"] = 12] = "UNSHIFT";
|
|
25
23
|
OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
|
|
26
24
|
OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
|
|
27
25
|
OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
|
|
@@ -63,1055 +61,1296 @@ const $refTypeFieldIndexes = "$__refTypeFieldIndexes";
|
|
|
63
61
|
const $viewFieldIndexes = "$__viewFieldIndexes";
|
|
64
62
|
const $fieldIndexesByViewTag = "$__fieldIndexesByViewTag";
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Copyright (c) 2018 Endel Dreyer
|
|
66
|
+
* Copyright (c) 2014 Ion Drive Software Ltd.
|
|
67
|
+
*
|
|
68
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
69
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
70
|
+
* in the Software without restriction, including without limitation the rights
|
|
71
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
72
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
73
|
+
* furnished to do so, subject to the following conditions:
|
|
74
|
+
*
|
|
75
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
76
|
+
* copies or substantial portions of the Software.
|
|
77
|
+
*
|
|
78
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
79
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
80
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
81
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
82
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
83
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
84
|
+
* SOFTWARE
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* msgpack implementation highly based on notepack.io
|
|
88
|
+
* https://github.com/darrachequesne/notepack
|
|
89
|
+
*/
|
|
90
|
+
let textEncoder;
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
try {
|
|
93
|
+
textEncoder = new TextEncoder();
|
|
74
94
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
inherits.add(target);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
constructor(rootClass) {
|
|
94
|
-
this.types = {};
|
|
95
|
-
this.schemas = new Map();
|
|
96
|
-
this.hasFilters = false;
|
|
97
|
-
this.parentFiltered = {};
|
|
98
|
-
if (rootClass) {
|
|
99
|
-
this.discoverTypes(rootClass);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
has(schema) {
|
|
103
|
-
return this.schemas.has(schema);
|
|
104
|
-
}
|
|
105
|
-
get(typeid) {
|
|
106
|
-
return this.types[typeid];
|
|
107
|
-
}
|
|
108
|
-
add(schema, typeid = this.schemas.size) {
|
|
109
|
-
// skip if already registered
|
|
110
|
-
if (this.schemas.has(schema)) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
this.types[typeid] = schema;
|
|
114
|
-
//
|
|
115
|
-
// Workaround to allow using an empty Schema (with no `@type()` fields)
|
|
116
|
-
//
|
|
117
|
-
if (schema[Symbol.metadata] === undefined) {
|
|
118
|
-
Metadata.initialize(schema);
|
|
119
|
-
}
|
|
120
|
-
this.schemas.set(schema, typeid);
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
getTypeId(klass) {
|
|
124
|
-
return this.schemas.get(klass);
|
|
125
|
-
}
|
|
126
|
-
discoverTypes(klass, parentIndex, parentFieldViewTag) {
|
|
127
|
-
if (!this.add(klass)) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
// add classes inherited from this base class
|
|
131
|
-
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
132
|
-
this.discoverTypes(child, parentIndex, parentFieldViewTag);
|
|
133
|
-
});
|
|
134
|
-
// add parent classes
|
|
135
|
-
let parent = klass;
|
|
136
|
-
while ((parent = Object.getPrototypeOf(parent)) &&
|
|
137
|
-
parent !== Schema && // stop at root (Schema)
|
|
138
|
-
parent !== Function.prototype // stop at root (non-Schema)
|
|
139
|
-
) {
|
|
140
|
-
this.discoverTypes(parent);
|
|
141
|
-
}
|
|
142
|
-
const metadata = (klass[Symbol.metadata] ??= {});
|
|
143
|
-
// if any schema/field has filters, mark "context" as having filters.
|
|
144
|
-
if (metadata[$viewFieldIndexes]) {
|
|
145
|
-
this.hasFilters = true;
|
|
146
|
-
}
|
|
147
|
-
if (parentFieldViewTag !== undefined) {
|
|
148
|
-
this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
|
|
149
|
-
}
|
|
150
|
-
for (const fieldIndex in metadata) {
|
|
151
|
-
const index = fieldIndex;
|
|
152
|
-
const fieldType = metadata[index].type;
|
|
153
|
-
const viewTag = metadata[index].tag;
|
|
154
|
-
if (typeof (fieldType) === "string") {
|
|
155
|
-
continue;
|
|
95
|
+
catch (e) { }
|
|
96
|
+
const _convoBuffer$1 = new ArrayBuffer(8);
|
|
97
|
+
const _int32$1 = new Int32Array(_convoBuffer$1);
|
|
98
|
+
const _float32$1 = new Float32Array(_convoBuffer$1);
|
|
99
|
+
const _float64$1 = new Float64Array(_convoBuffer$1);
|
|
100
|
+
const _int64$1 = new BigInt64Array(_convoBuffer$1);
|
|
101
|
+
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
|
|
102
|
+
const utf8Length = (hasBufferByteLength)
|
|
103
|
+
? Buffer.byteLength // node
|
|
104
|
+
: function (str, _) {
|
|
105
|
+
var c = 0, length = 0;
|
|
106
|
+
for (var i = 0, l = str.length; i < l; i++) {
|
|
107
|
+
c = str.charCodeAt(i);
|
|
108
|
+
if (c < 0x80) {
|
|
109
|
+
length += 1;
|
|
156
110
|
}
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
// skip primitive types
|
|
160
|
-
if (type === "string") {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
this.discoverTypes(type, index, viewTag);
|
|
111
|
+
else if (c < 0x800) {
|
|
112
|
+
length += 2;
|
|
164
113
|
}
|
|
165
|
-
else if (
|
|
166
|
-
|
|
114
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
115
|
+
length += 3;
|
|
167
116
|
}
|
|
168
117
|
else {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (typeof (type) === "string") {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
this.discoverTypes(type, index, viewTag);
|
|
118
|
+
i++;
|
|
119
|
+
length += 4;
|
|
175
120
|
}
|
|
176
121
|
}
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
: type;
|
|
186
|
-
}
|
|
187
|
-
const Metadata = {
|
|
188
|
-
addField(metadata, index, name, type, descriptor) {
|
|
189
|
-
if (index > 64) {
|
|
190
|
-
throw new Error(`Can't define field '${name}'.\nSchema instances may only have up to 64 fields.`);
|
|
122
|
+
return length;
|
|
123
|
+
};
|
|
124
|
+
function utf8Write(view, str, it) {
|
|
125
|
+
var c = 0;
|
|
126
|
+
for (var i = 0, l = str.length; i < l; i++) {
|
|
127
|
+
c = str.charCodeAt(i);
|
|
128
|
+
if (c < 0x80) {
|
|
129
|
+
view[it.offset++] = c;
|
|
191
130
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
name,
|
|
197
|
-
});
|
|
198
|
-
// create "descriptors" map
|
|
199
|
-
Object.defineProperty(metadata, $descriptors, {
|
|
200
|
-
value: metadata[$descriptors] || {},
|
|
201
|
-
enumerable: false,
|
|
202
|
-
configurable: true,
|
|
203
|
-
});
|
|
204
|
-
if (descriptor) {
|
|
205
|
-
// for encoder
|
|
206
|
-
metadata[$descriptors][name] = descriptor;
|
|
207
|
-
metadata[$descriptors][`_${name}`] = {
|
|
208
|
-
value: undefined,
|
|
209
|
-
writable: true,
|
|
210
|
-
enumerable: false,
|
|
211
|
-
configurable: true,
|
|
212
|
-
};
|
|
131
|
+
else if (c < 0x800) {
|
|
132
|
+
view[it.offset] = 0xc0 | (c >> 6);
|
|
133
|
+
view[it.offset + 1] = 0x80 | (c & 0x3f);
|
|
134
|
+
it.offset += 2;
|
|
213
135
|
}
|
|
214
|
-
else {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
enumerable: true,
|
|
220
|
-
configurable: true,
|
|
221
|
-
};
|
|
136
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
137
|
+
view[it.offset] = 0xe0 | (c >> 12);
|
|
138
|
+
view[it.offset + 1] = 0x80 | (c >> 6 & 0x3f);
|
|
139
|
+
view[it.offset + 2] = 0x80 | (c & 0x3f);
|
|
140
|
+
it.offset += 3;
|
|
222
141
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
value: index,
|
|
232
|
-
enumerable: false,
|
|
233
|
-
configurable: true,
|
|
234
|
-
});
|
|
235
|
-
// if child Ref/complex type, add to -4
|
|
236
|
-
if (typeof (metadata[index].type) !== "string") {
|
|
237
|
-
if (metadata[$refTypeFieldIndexes] === undefined) {
|
|
238
|
-
Object.defineProperty(metadata, $refTypeFieldIndexes, {
|
|
239
|
-
value: [],
|
|
240
|
-
enumerable: false,
|
|
241
|
-
configurable: true,
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
metadata[$refTypeFieldIndexes].push(index);
|
|
142
|
+
else {
|
|
143
|
+
i++;
|
|
144
|
+
c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
|
|
145
|
+
view[it.offset] = 0xf0 | (c >> 18);
|
|
146
|
+
view[it.offset + 1] = 0x80 | (c >> 12 & 0x3f);
|
|
147
|
+
view[it.offset + 2] = 0x80 | (c >> 6 & 0x3f);
|
|
148
|
+
view[it.offset + 3] = 0x80 | (c & 0x3f);
|
|
149
|
+
it.offset += 4;
|
|
245
150
|
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function int8$1(bytes, value, it) {
|
|
154
|
+
bytes[it.offset++] = value & 255;
|
|
155
|
+
}
|
|
156
|
+
function uint8$1(bytes, value, it) {
|
|
157
|
+
bytes[it.offset++] = value & 255;
|
|
158
|
+
}
|
|
159
|
+
function int16$1(bytes, value, it) {
|
|
160
|
+
bytes[it.offset++] = value & 255;
|
|
161
|
+
bytes[it.offset++] = (value >> 8) & 255;
|
|
162
|
+
}
|
|
163
|
+
function uint16$1(bytes, value, it) {
|
|
164
|
+
bytes[it.offset++] = value & 255;
|
|
165
|
+
bytes[it.offset++] = (value >> 8) & 255;
|
|
166
|
+
}
|
|
167
|
+
function int32$1(bytes, value, it) {
|
|
168
|
+
bytes[it.offset++] = value & 255;
|
|
169
|
+
bytes[it.offset++] = (value >> 8) & 255;
|
|
170
|
+
bytes[it.offset++] = (value >> 16) & 255;
|
|
171
|
+
bytes[it.offset++] = (value >> 24) & 255;
|
|
172
|
+
}
|
|
173
|
+
function uint32$1(bytes, value, it) {
|
|
174
|
+
const b4 = value >> 24;
|
|
175
|
+
const b3 = value >> 16;
|
|
176
|
+
const b2 = value >> 8;
|
|
177
|
+
const b1 = value;
|
|
178
|
+
bytes[it.offset++] = b1 & 255;
|
|
179
|
+
bytes[it.offset++] = b2 & 255;
|
|
180
|
+
bytes[it.offset++] = b3 & 255;
|
|
181
|
+
bytes[it.offset++] = b4 & 255;
|
|
182
|
+
}
|
|
183
|
+
function int64$1(bytes, value, it) {
|
|
184
|
+
const high = Math.floor(value / Math.pow(2, 32));
|
|
185
|
+
const low = value >>> 0;
|
|
186
|
+
uint32$1(bytes, low, it);
|
|
187
|
+
uint32$1(bytes, high, it);
|
|
188
|
+
}
|
|
189
|
+
function uint64$1(bytes, value, it) {
|
|
190
|
+
const high = (value / Math.pow(2, 32)) >> 0;
|
|
191
|
+
const low = value >>> 0;
|
|
192
|
+
uint32$1(bytes, low, it);
|
|
193
|
+
uint32$1(bytes, high, it);
|
|
194
|
+
}
|
|
195
|
+
function bigint64$1(bytes, value, it) {
|
|
196
|
+
_int64$1[0] = BigInt.asIntN(64, value);
|
|
197
|
+
int32$1(bytes, _int32$1[0], it);
|
|
198
|
+
int32$1(bytes, _int32$1[1], it);
|
|
199
|
+
}
|
|
200
|
+
function biguint64$1(bytes, value, it) {
|
|
201
|
+
_int64$1[0] = BigInt.asIntN(64, value);
|
|
202
|
+
int32$1(bytes, _int32$1[0], it);
|
|
203
|
+
int32$1(bytes, _int32$1[1], it);
|
|
204
|
+
}
|
|
205
|
+
function float32$1(bytes, value, it) {
|
|
206
|
+
_float32$1[0] = value;
|
|
207
|
+
int32$1(bytes, _int32$1[0], it);
|
|
208
|
+
}
|
|
209
|
+
function float64$1(bytes, value, it) {
|
|
210
|
+
_float64$1[0] = value;
|
|
211
|
+
int32$1(bytes, _int32$1[0 ], it);
|
|
212
|
+
int32$1(bytes, _int32$1[1 ], it);
|
|
213
|
+
}
|
|
214
|
+
function boolean$1(bytes, value, it) {
|
|
215
|
+
bytes[it.offset++] = value ? 1 : 0; // uint8
|
|
216
|
+
}
|
|
217
|
+
function string$1(bytes, value, it) {
|
|
218
|
+
// encode `null` strings as empty.
|
|
219
|
+
if (!value) {
|
|
220
|
+
value = "";
|
|
221
|
+
}
|
|
222
|
+
let length = utf8Length(value, "utf8");
|
|
223
|
+
let size = 0;
|
|
224
|
+
// fixstr
|
|
225
|
+
if (length < 0x20) {
|
|
226
|
+
bytes[it.offset++] = length | 0xa0;
|
|
227
|
+
size = 1;
|
|
228
|
+
}
|
|
229
|
+
// str 8
|
|
230
|
+
else if (length < 0x100) {
|
|
231
|
+
bytes[it.offset++] = 0xd9;
|
|
232
|
+
bytes[it.offset++] = length % 255;
|
|
233
|
+
size = 2;
|
|
234
|
+
}
|
|
235
|
+
// str 16
|
|
236
|
+
else if (length < 0x10000) {
|
|
237
|
+
bytes[it.offset++] = 0xda;
|
|
238
|
+
uint16$1(bytes, length, it);
|
|
239
|
+
size = 3;
|
|
240
|
+
}
|
|
241
|
+
// str 32
|
|
242
|
+
else if (length < 0x100000000) {
|
|
243
|
+
bytes[it.offset++] = 0xdb;
|
|
244
|
+
uint32$1(bytes, length, it);
|
|
245
|
+
size = 5;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
throw new Error('String too long');
|
|
249
|
+
}
|
|
250
|
+
utf8Write(bytes, value, it);
|
|
251
|
+
return size + length;
|
|
252
|
+
}
|
|
253
|
+
function number$1(bytes, value, it) {
|
|
254
|
+
if (isNaN(value)) {
|
|
255
|
+
return number$1(bytes, 0, it);
|
|
256
|
+
}
|
|
257
|
+
else if (!isFinite(value)) {
|
|
258
|
+
return number$1(bytes, (value > 0) ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER, it);
|
|
259
|
+
}
|
|
260
|
+
else if (value !== (value | 0)) {
|
|
261
|
+
if (Math.abs(value) <= 3.4028235e+38) { // range check
|
|
262
|
+
_float32$1[0] = value;
|
|
263
|
+
if (Math.abs(Math.abs(_float32$1[0]) - Math.abs(value)) < 1e-4) { // precision check; adjust 1e-n (n = precision) to in-/decrease acceptable precision loss
|
|
264
|
+
// now we know value is in range for f32 and has acceptable precision for f32
|
|
265
|
+
bytes[it.offset++] = 0xca;
|
|
266
|
+
float32$1(bytes, value, it);
|
|
267
|
+
return 5;
|
|
268
|
+
}
|
|
265
269
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
270
|
+
bytes[it.offset++] = 0xcb;
|
|
271
|
+
float64$1(bytes, value, it);
|
|
272
|
+
return 9;
|
|
273
|
+
}
|
|
274
|
+
if (value >= 0) {
|
|
275
|
+
// positive fixnum
|
|
276
|
+
if (value < 0x80) {
|
|
277
|
+
bytes[it.offset++] = value & 255; // uint8
|
|
278
|
+
return 1;
|
|
269
279
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
TypeContext.register(constructor);
|
|
276
|
-
const parentClass = Object.getPrototypeOf(constructor);
|
|
277
|
-
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
278
|
-
const metadata = Metadata.initialize(constructor);
|
|
279
|
-
// Use Schema's methods if not defined in the class
|
|
280
|
-
if (!constructor[$track]) {
|
|
281
|
-
constructor[$track] = Schema[$track];
|
|
280
|
+
// uint 8
|
|
281
|
+
if (value < 0x100) {
|
|
282
|
+
bytes[it.offset++] = 0xcc;
|
|
283
|
+
bytes[it.offset++] = value & 255; // uint8
|
|
284
|
+
return 2;
|
|
282
285
|
}
|
|
283
|
-
|
|
284
|
-
|
|
286
|
+
// uint 16
|
|
287
|
+
if (value < 0x10000) {
|
|
288
|
+
bytes[it.offset++] = 0xcd;
|
|
289
|
+
uint16$1(bytes, value, it);
|
|
290
|
+
return 3;
|
|
285
291
|
}
|
|
286
|
-
|
|
287
|
-
|
|
292
|
+
// uint 32
|
|
293
|
+
if (value < 0x100000000) {
|
|
294
|
+
bytes[it.offset++] = 0xce;
|
|
295
|
+
uint32$1(bytes, value, it);
|
|
296
|
+
return 5;
|
|
288
297
|
}
|
|
289
|
-
|
|
290
|
-
|
|
298
|
+
// uint 64
|
|
299
|
+
bytes[it.offset++] = 0xcf;
|
|
300
|
+
uint64$1(bytes, value, it);
|
|
301
|
+
return 9;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// negative fixnum
|
|
305
|
+
if (value >= -0x20) {
|
|
306
|
+
bytes[it.offset++] = 0xe0 | (value + 0x20);
|
|
307
|
+
return 1;
|
|
291
308
|
}
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
?? -1; // no fields defined
|
|
298
|
-
fieldIndex++;
|
|
299
|
-
for (const field in fields) {
|
|
300
|
-
const type = fields[field];
|
|
301
|
-
const normalizedType = getNormalizedType(type);
|
|
302
|
-
// FIXME: this code is duplicated from @type() annotation
|
|
303
|
-
const complexTypeKlass = (Array.isArray(type))
|
|
304
|
-
? getType("array")
|
|
305
|
-
: (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
|
|
306
|
-
const childType = (complexTypeKlass)
|
|
307
|
-
? Object.values(type)[0]
|
|
308
|
-
: normalizedType;
|
|
309
|
-
Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass));
|
|
310
|
-
fieldIndex++;
|
|
309
|
+
// int 8
|
|
310
|
+
if (value >= -0x80) {
|
|
311
|
+
bytes[it.offset++] = 0xd0;
|
|
312
|
+
int8$1(bytes, value, it);
|
|
313
|
+
return 2;
|
|
311
314
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
init(klass) {
|
|
318
|
-
//
|
|
319
|
-
// Used only to initialize an empty Schema (Encoder#constructor)
|
|
320
|
-
// TODO: remove/refactor this...
|
|
321
|
-
//
|
|
322
|
-
const metadata = {};
|
|
323
|
-
klass[Symbol.metadata] = metadata;
|
|
324
|
-
Object.defineProperty(metadata, $numFields, {
|
|
325
|
-
value: 0,
|
|
326
|
-
enumerable: false,
|
|
327
|
-
configurable: true,
|
|
328
|
-
});
|
|
329
|
-
},
|
|
330
|
-
initialize(constructor) {
|
|
331
|
-
const parentClass = Object.getPrototypeOf(constructor);
|
|
332
|
-
const parentMetadata = parentClass[Symbol.metadata];
|
|
333
|
-
let metadata = constructor[Symbol.metadata] ?? Object.create(null);
|
|
334
|
-
// make sure inherited classes have their own metadata object.
|
|
335
|
-
if (parentClass !== Schema && metadata === parentMetadata) {
|
|
336
|
-
metadata = Object.create(null);
|
|
337
|
-
if (parentMetadata) {
|
|
338
|
-
//
|
|
339
|
-
// assign parent metadata to current
|
|
340
|
-
//
|
|
341
|
-
Object.setPrototypeOf(metadata, parentMetadata);
|
|
342
|
-
// $numFields
|
|
343
|
-
Object.defineProperty(metadata, $numFields, {
|
|
344
|
-
value: parentMetadata[$numFields],
|
|
345
|
-
enumerable: false,
|
|
346
|
-
configurable: true,
|
|
347
|
-
writable: true,
|
|
348
|
-
});
|
|
349
|
-
// $viewFieldIndexes / $fieldIndexesByViewTag
|
|
350
|
-
if (parentMetadata[$viewFieldIndexes] !== undefined) {
|
|
351
|
-
Object.defineProperty(metadata, $viewFieldIndexes, {
|
|
352
|
-
value: [...parentMetadata[$viewFieldIndexes]],
|
|
353
|
-
enumerable: false,
|
|
354
|
-
configurable: true,
|
|
355
|
-
writable: true,
|
|
356
|
-
});
|
|
357
|
-
Object.defineProperty(metadata, $fieldIndexesByViewTag, {
|
|
358
|
-
value: { ...parentMetadata[$fieldIndexesByViewTag] },
|
|
359
|
-
enumerable: false,
|
|
360
|
-
configurable: true,
|
|
361
|
-
writable: true,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
// $refTypeFieldIndexes
|
|
365
|
-
if (parentMetadata[$refTypeFieldIndexes] !== undefined) {
|
|
366
|
-
Object.defineProperty(metadata, $refTypeFieldIndexes, {
|
|
367
|
-
value: [...parentMetadata[$refTypeFieldIndexes]],
|
|
368
|
-
enumerable: false,
|
|
369
|
-
configurable: true,
|
|
370
|
-
writable: true,
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
// $descriptors
|
|
374
|
-
Object.defineProperty(metadata, $descriptors, {
|
|
375
|
-
value: { ...parentMetadata[$descriptors] },
|
|
376
|
-
enumerable: false,
|
|
377
|
-
configurable: true,
|
|
378
|
-
writable: true,
|
|
379
|
-
});
|
|
380
|
-
}
|
|
315
|
+
// int 16
|
|
316
|
+
if (value >= -0x8000) {
|
|
317
|
+
bytes[it.offset++] = 0xd1;
|
|
318
|
+
int16$1(bytes, value, it);
|
|
319
|
+
return 3;
|
|
381
320
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], $numFields));
|
|
388
|
-
},
|
|
389
|
-
getFields(klass) {
|
|
390
|
-
const metadata = klass[Symbol.metadata];
|
|
391
|
-
const fields = {};
|
|
392
|
-
for (let i = 0; i <= metadata[$numFields]; i++) {
|
|
393
|
-
fields[metadata[i].name] = metadata[i].type;
|
|
321
|
+
// int 32
|
|
322
|
+
if (value >= -0x80000000) {
|
|
323
|
+
bytes[it.offset++] = 0xd2;
|
|
324
|
+
int32$1(bytes, value, it);
|
|
325
|
+
return 5;
|
|
394
326
|
}
|
|
395
|
-
|
|
327
|
+
// int 64
|
|
328
|
+
bytes[it.offset++] = 0xd3;
|
|
329
|
+
int64$1(bytes, value, it);
|
|
330
|
+
return 9;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const encode = {
|
|
334
|
+
int8: int8$1,
|
|
335
|
+
uint8: uint8$1,
|
|
336
|
+
int16: int16$1,
|
|
337
|
+
uint16: uint16$1,
|
|
338
|
+
int32: int32$1,
|
|
339
|
+
uint32: uint32$1,
|
|
340
|
+
int64: int64$1,
|
|
341
|
+
uint64: uint64$1,
|
|
342
|
+
bigint64: bigint64$1,
|
|
343
|
+
biguint64: biguint64$1,
|
|
344
|
+
float32: float32$1,
|
|
345
|
+
float64: float64$1,
|
|
346
|
+
boolean: boolean$1,
|
|
347
|
+
string: string$1,
|
|
348
|
+
number: number$1,
|
|
349
|
+
utf8Write,
|
|
350
|
+
utf8Length,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Copyright (c) 2018 Endel Dreyer
|
|
355
|
+
* Copyright (c) 2014 Ion Drive Software Ltd.
|
|
356
|
+
*
|
|
357
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
358
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
359
|
+
* in the Software without restriction, including without limitation the rights
|
|
360
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
361
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
362
|
+
* furnished to do so, subject to the following conditions:
|
|
363
|
+
*
|
|
364
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
365
|
+
* copies or substantial portions of the Software.
|
|
366
|
+
*
|
|
367
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
368
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
369
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
370
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
371
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
372
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
373
|
+
* SOFTWARE
|
|
374
|
+
*/
|
|
375
|
+
// force little endian to facilitate decoding on multiple implementations
|
|
376
|
+
const _convoBuffer = new ArrayBuffer(8);
|
|
377
|
+
const _int32 = new Int32Array(_convoBuffer);
|
|
378
|
+
const _float32 = new Float32Array(_convoBuffer);
|
|
379
|
+
const _float64 = new Float64Array(_convoBuffer);
|
|
380
|
+
const _uint64 = new BigUint64Array(_convoBuffer);
|
|
381
|
+
const _int64 = new BigInt64Array(_convoBuffer);
|
|
382
|
+
function utf8Read(bytes, it, length) {
|
|
383
|
+
var string = '', chr = 0;
|
|
384
|
+
for (var i = it.offset, end = it.offset + length; i < end; i++) {
|
|
385
|
+
var byte = bytes[i];
|
|
386
|
+
if ((byte & 0x80) === 0x00) {
|
|
387
|
+
string += String.fromCharCode(byte);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if ((byte & 0xe0) === 0xc0) {
|
|
391
|
+
string += String.fromCharCode(((byte & 0x1f) << 6) |
|
|
392
|
+
(bytes[++i] & 0x3f));
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if ((byte & 0xf0) === 0xe0) {
|
|
396
|
+
string += String.fromCharCode(((byte & 0x0f) << 12) |
|
|
397
|
+
((bytes[++i] & 0x3f) << 6) |
|
|
398
|
+
((bytes[++i] & 0x3f) << 0));
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if ((byte & 0xf8) === 0xf0) {
|
|
402
|
+
chr = ((byte & 0x07) << 18) |
|
|
403
|
+
((bytes[++i] & 0x3f) << 12) |
|
|
404
|
+
((bytes[++i] & 0x3f) << 6) |
|
|
405
|
+
((bytes[++i] & 0x3f) << 0);
|
|
406
|
+
if (chr >= 0x010000) { // surrogate pair
|
|
407
|
+
chr -= 0x010000;
|
|
408
|
+
string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
string += String.fromCharCode(chr);
|
|
412
|
+
}
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
console.error('Invalid byte ' + byte.toString(16));
|
|
416
|
+
// (do not throw error to avoid server/client from crashing due to hack attemps)
|
|
417
|
+
// throw new Error('Invalid byte ' + byte.toString(16));
|
|
418
|
+
}
|
|
419
|
+
it.offset += length;
|
|
420
|
+
return string;
|
|
421
|
+
}
|
|
422
|
+
function int8(bytes, it) {
|
|
423
|
+
return uint8(bytes, it) << 24 >> 24;
|
|
424
|
+
}
|
|
425
|
+
function uint8(bytes, it) {
|
|
426
|
+
return bytes[it.offset++];
|
|
427
|
+
}
|
|
428
|
+
function int16(bytes, it) {
|
|
429
|
+
return uint16(bytes, it) << 16 >> 16;
|
|
430
|
+
}
|
|
431
|
+
function uint16(bytes, it) {
|
|
432
|
+
return bytes[it.offset++] | bytes[it.offset++] << 8;
|
|
433
|
+
}
|
|
434
|
+
function int32(bytes, it) {
|
|
435
|
+
return bytes[it.offset++] | bytes[it.offset++] << 8 | bytes[it.offset++] << 16 | bytes[it.offset++] << 24;
|
|
436
|
+
}
|
|
437
|
+
function uint32(bytes, it) {
|
|
438
|
+
return int32(bytes, it) >>> 0;
|
|
439
|
+
}
|
|
440
|
+
function float32(bytes, it) {
|
|
441
|
+
_int32[0] = int32(bytes, it);
|
|
442
|
+
return _float32[0];
|
|
443
|
+
}
|
|
444
|
+
function float64(bytes, it) {
|
|
445
|
+
_int32[0 ] = int32(bytes, it);
|
|
446
|
+
_int32[1 ] = int32(bytes, it);
|
|
447
|
+
return _float64[0];
|
|
448
|
+
}
|
|
449
|
+
function int64(bytes, it) {
|
|
450
|
+
const low = uint32(bytes, it);
|
|
451
|
+
const high = int32(bytes, it) * Math.pow(2, 32);
|
|
452
|
+
return high + low;
|
|
453
|
+
}
|
|
454
|
+
function uint64(bytes, it) {
|
|
455
|
+
const low = uint32(bytes, it);
|
|
456
|
+
const high = uint32(bytes, it) * Math.pow(2, 32);
|
|
457
|
+
return high + low;
|
|
458
|
+
}
|
|
459
|
+
function bigint64(bytes, it) {
|
|
460
|
+
_int32[0] = int32(bytes, it);
|
|
461
|
+
_int32[1] = int32(bytes, it);
|
|
462
|
+
return _int64[0];
|
|
463
|
+
}
|
|
464
|
+
function biguint64(bytes, it) {
|
|
465
|
+
_int32[0] = int32(bytes, it);
|
|
466
|
+
_int32[1] = int32(bytes, it);
|
|
467
|
+
return _uint64[0];
|
|
468
|
+
}
|
|
469
|
+
function boolean(bytes, it) {
|
|
470
|
+
return uint8(bytes, it) > 0;
|
|
471
|
+
}
|
|
472
|
+
function string(bytes, it) {
|
|
473
|
+
const prefix = bytes[it.offset++];
|
|
474
|
+
let length;
|
|
475
|
+
if (prefix < 0xc0) {
|
|
476
|
+
// fixstr
|
|
477
|
+
length = prefix & 0x1f;
|
|
478
|
+
}
|
|
479
|
+
else if (prefix === 0xd9) {
|
|
480
|
+
length = uint8(bytes, it);
|
|
481
|
+
}
|
|
482
|
+
else if (prefix === 0xda) {
|
|
483
|
+
length = uint16(bytes, it);
|
|
484
|
+
}
|
|
485
|
+
else if (prefix === 0xdb) {
|
|
486
|
+
length = uint32(bytes, it);
|
|
487
|
+
}
|
|
488
|
+
return utf8Read(bytes, it, length);
|
|
489
|
+
}
|
|
490
|
+
function number(bytes, it) {
|
|
491
|
+
const prefix = bytes[it.offset++];
|
|
492
|
+
if (prefix < 0x80) {
|
|
493
|
+
// positive fixint
|
|
494
|
+
return prefix;
|
|
495
|
+
}
|
|
496
|
+
else if (prefix === 0xca) {
|
|
497
|
+
// float 32
|
|
498
|
+
return float32(bytes, it);
|
|
499
|
+
}
|
|
500
|
+
else if (prefix === 0xcb) {
|
|
501
|
+
// float 64
|
|
502
|
+
return float64(bytes, it);
|
|
503
|
+
}
|
|
504
|
+
else if (prefix === 0xcc) {
|
|
505
|
+
// uint 8
|
|
506
|
+
return uint8(bytes, it);
|
|
507
|
+
}
|
|
508
|
+
else if (prefix === 0xcd) {
|
|
509
|
+
// uint 16
|
|
510
|
+
return uint16(bytes, it);
|
|
511
|
+
}
|
|
512
|
+
else if (prefix === 0xce) {
|
|
513
|
+
// uint 32
|
|
514
|
+
return uint32(bytes, it);
|
|
515
|
+
}
|
|
516
|
+
else if (prefix === 0xcf) {
|
|
517
|
+
// uint 64
|
|
518
|
+
return uint64(bytes, it);
|
|
519
|
+
}
|
|
520
|
+
else if (prefix === 0xd0) {
|
|
521
|
+
// int 8
|
|
522
|
+
return int8(bytes, it);
|
|
523
|
+
}
|
|
524
|
+
else if (prefix === 0xd1) {
|
|
525
|
+
// int 16
|
|
526
|
+
return int16(bytes, it);
|
|
527
|
+
}
|
|
528
|
+
else if (prefix === 0xd2) {
|
|
529
|
+
// int 32
|
|
530
|
+
return int32(bytes, it);
|
|
531
|
+
}
|
|
532
|
+
else if (prefix === 0xd3) {
|
|
533
|
+
// int 64
|
|
534
|
+
return int64(bytes, it);
|
|
535
|
+
}
|
|
536
|
+
else if (prefix > 0xdf) {
|
|
537
|
+
// negative fixint
|
|
538
|
+
return (0xff - prefix + 1) * -1;
|
|
396
539
|
}
|
|
540
|
+
}
|
|
541
|
+
function stringCheck(bytes, it) {
|
|
542
|
+
const prefix = bytes[it.offset];
|
|
543
|
+
return (
|
|
544
|
+
// fixstr
|
|
545
|
+
(prefix < 0xc0 && prefix > 0xa0) ||
|
|
546
|
+
// str 8
|
|
547
|
+
prefix === 0xd9 ||
|
|
548
|
+
// str 16
|
|
549
|
+
prefix === 0xda ||
|
|
550
|
+
// str 32
|
|
551
|
+
prefix === 0xdb);
|
|
552
|
+
}
|
|
553
|
+
const decode = {
|
|
554
|
+
utf8Read,
|
|
555
|
+
int8,
|
|
556
|
+
uint8,
|
|
557
|
+
int16,
|
|
558
|
+
uint16,
|
|
559
|
+
int32,
|
|
560
|
+
uint32,
|
|
561
|
+
float32,
|
|
562
|
+
float64,
|
|
563
|
+
int64,
|
|
564
|
+
uint64,
|
|
565
|
+
bigint64,
|
|
566
|
+
biguint64,
|
|
567
|
+
boolean,
|
|
568
|
+
string,
|
|
569
|
+
number,
|
|
570
|
+
stringCheck,
|
|
397
571
|
};
|
|
398
572
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
573
|
+
const registeredTypes = {};
|
|
574
|
+
const identifiers = new Map();
|
|
575
|
+
function registerType(identifier, definition) {
|
|
576
|
+
if (definition.constructor) {
|
|
577
|
+
identifiers.set(definition.constructor, identifier);
|
|
578
|
+
registeredTypes[identifier] = definition;
|
|
403
579
|
}
|
|
404
|
-
|
|
405
|
-
|
|
580
|
+
if (definition.encode) {
|
|
581
|
+
encode[identifier] = definition.encode;
|
|
406
582
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const operationsIndex = changeSet.indexes[index];
|
|
410
|
-
if (operationsIndex !== undefined) {
|
|
411
|
-
changeSet.operations[operationsIndex] = undefined;
|
|
583
|
+
if (definition.decode) {
|
|
584
|
+
decode[identifier] = definition.decode;
|
|
412
585
|
}
|
|
413
|
-
delete changeSet.indexes[index];
|
|
414
586
|
}
|
|
415
|
-
function
|
|
416
|
-
|
|
417
|
-
changeTree[changeSet].queueRootIndex = root[changeSet].push(changeTree) - 1;
|
|
418
|
-
}
|
|
587
|
+
function getType(identifier) {
|
|
588
|
+
return registeredTypes[identifier];
|
|
419
589
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
this.isPartiallyFiltered = false;
|
|
424
|
-
this.indexedOperations = {};
|
|
425
|
-
//
|
|
426
|
-
// TODO:
|
|
427
|
-
// try storing the index + operation per item.
|
|
428
|
-
// example: 1024 & 1025 => ADD, 1026 => DELETE
|
|
429
|
-
//
|
|
430
|
-
// => https://chatgpt.com/share/67107d0c-bc20-8004-8583-83b17dd7c196
|
|
431
|
-
//
|
|
432
|
-
this.changes = { indexes: {}, operations: [] };
|
|
433
|
-
this.allChanges = { indexes: {}, operations: [] };
|
|
434
|
-
/**
|
|
435
|
-
* Is this a new instance? Used on ArraySchema to determine OPERATION.MOVE_AND_ADD operation.
|
|
436
|
-
*/
|
|
437
|
-
this.isNew = true;
|
|
438
|
-
this.ref = ref;
|
|
439
|
-
//
|
|
440
|
-
// Does this structure have "filters" declared?
|
|
441
|
-
//
|
|
442
|
-
if (ref.constructor[Symbol.metadata]?.[$viewFieldIndexes]) {
|
|
443
|
-
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
444
|
-
this.filteredChanges = { indexes: {}, operations: [] };
|
|
445
|
-
}
|
|
590
|
+
function defineCustomTypes(types) {
|
|
591
|
+
for (const identifier in types) {
|
|
592
|
+
registerType(identifier, types[identifier]);
|
|
446
593
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (!this.isFiltered) {
|
|
467
|
-
enqueueChangeTree(root, this, 'changes');
|
|
468
|
-
if (isNewChangeTree) {
|
|
469
|
-
this.root.allChanges.push(this);
|
|
594
|
+
return (t) => type(t);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
class TypeContext {
|
|
598
|
+
/**
|
|
599
|
+
* For inheritance support
|
|
600
|
+
* Keeps track of which classes extends which. (parent -> children)
|
|
601
|
+
*/
|
|
602
|
+
static { this.inheritedTypes = new Map(); }
|
|
603
|
+
static register(target) {
|
|
604
|
+
const parent = Object.getPrototypeOf(target);
|
|
605
|
+
if (parent !== Schema) {
|
|
606
|
+
let inherits = TypeContext.inheritedTypes.get(parent);
|
|
607
|
+
if (!inherits) {
|
|
608
|
+
inherits = new Set();
|
|
609
|
+
TypeContext.inheritedTypes.set(parent, inherits);
|
|
470
610
|
}
|
|
471
|
-
|
|
472
|
-
// Recursively set root on child structures
|
|
473
|
-
if (metadata) {
|
|
474
|
-
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
475
|
-
const field = metadata[index];
|
|
476
|
-
const value = this.ref[field.name];
|
|
477
|
-
value?.[$changes].setRoot(root);
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
481
|
-
// MapSchema / ArraySchema, etc.
|
|
482
|
-
this.ref.forEach((value, key) => {
|
|
483
|
-
value[$changes].setRoot(root);
|
|
484
|
-
});
|
|
611
|
+
inherits.add(target);
|
|
485
612
|
}
|
|
486
613
|
}
|
|
487
|
-
|
|
488
|
-
this.
|
|
489
|
-
this.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
const metadata = this.ref.constructor[Symbol.metadata];
|
|
495
|
-
// skip if parent is already set
|
|
496
|
-
if (root !== this.root) {
|
|
497
|
-
this.root = root;
|
|
498
|
-
const isNewChangeTree = root.add(this);
|
|
499
|
-
if (root.types.hasFilters) {
|
|
500
|
-
this.checkIsFiltered(metadata, parent, parentIndex);
|
|
501
|
-
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
502
|
-
enqueueChangeTree(root, this, 'filteredChanges');
|
|
503
|
-
if (isNewChangeTree) {
|
|
504
|
-
this.root.allFilteredChanges.push(this);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
if (!this.isFiltered) {
|
|
509
|
-
enqueueChangeTree(root, this, 'changes');
|
|
510
|
-
if (isNewChangeTree) {
|
|
511
|
-
this.root.allChanges.push(this);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
root.add(this);
|
|
517
|
-
}
|
|
518
|
-
// assign same parent on child structures
|
|
519
|
-
if (metadata) {
|
|
520
|
-
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
521
|
-
const field = metadata[index];
|
|
522
|
-
const value = this.ref[field.name];
|
|
523
|
-
value?.[$changes].setParent(this.ref, root, index);
|
|
524
|
-
// try { throw new Error(); } catch (e) {
|
|
525
|
-
// console.log(e.stack);
|
|
526
|
-
// }
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
530
|
-
// MapSchema / ArraySchema, etc.
|
|
531
|
-
this.ref.forEach((value, key) => {
|
|
532
|
-
value[$changes].setParent(this.ref, root, this.indexes[key] ?? key);
|
|
533
|
-
});
|
|
614
|
+
constructor(rootClass) {
|
|
615
|
+
this.types = {};
|
|
616
|
+
this.schemas = new Map();
|
|
617
|
+
this.hasFilters = false;
|
|
618
|
+
this.parentFiltered = {};
|
|
619
|
+
if (rootClass) {
|
|
620
|
+
this.discoverTypes(rootClass);
|
|
534
621
|
}
|
|
535
622
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
// assign same parent on child structures
|
|
539
|
-
//
|
|
540
|
-
const metadata = this.ref.constructor[Symbol.metadata];
|
|
541
|
-
if (metadata) {
|
|
542
|
-
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
543
|
-
const field = metadata[index];
|
|
544
|
-
const value = this.ref[field.name];
|
|
545
|
-
if (value) {
|
|
546
|
-
callback(value[$changes], index);
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
551
|
-
// MapSchema / ArraySchema, etc.
|
|
552
|
-
this.ref.forEach((value, key) => {
|
|
553
|
-
callback(value[$changes], this.indexes[key] ?? key);
|
|
554
|
-
});
|
|
555
|
-
}
|
|
623
|
+
has(schema) {
|
|
624
|
+
return this.schemas.has(schema);
|
|
556
625
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
// this is checked during .encode() time.
|
|
560
|
-
this.changes.operations.push(-op);
|
|
561
|
-
enqueueChangeTree(this.root, this, 'changes');
|
|
626
|
+
get(typeid) {
|
|
627
|
+
return this.types[typeid];
|
|
562
628
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
? this.filteredChanges
|
|
568
|
-
: this.changes;
|
|
569
|
-
const previousOperation = this.indexedOperations[index];
|
|
570
|
-
if (!previousOperation || previousOperation === exports.OPERATION.DELETE) {
|
|
571
|
-
const op = (!previousOperation)
|
|
572
|
-
? operation
|
|
573
|
-
: (previousOperation === exports.OPERATION.DELETE)
|
|
574
|
-
? exports.OPERATION.DELETE_AND_ADD
|
|
575
|
-
: operation;
|
|
576
|
-
//
|
|
577
|
-
// TODO: are DELETE operations being encoded as ADD here ??
|
|
578
|
-
//
|
|
579
|
-
this.indexedOperations[index] = op;
|
|
580
|
-
}
|
|
581
|
-
setOperationAtIndex(changeSet, index);
|
|
582
|
-
if (isFiltered) {
|
|
583
|
-
setOperationAtIndex(this.allFilteredChanges, index);
|
|
584
|
-
if (this.root) {
|
|
585
|
-
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
586
|
-
enqueueChangeTree(this.root, this, 'allFilteredChanges');
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
setOperationAtIndex(this.allChanges, index);
|
|
591
|
-
enqueueChangeTree(this.root, this, 'changes');
|
|
629
|
+
add(schema, typeid = this.schemas.size) {
|
|
630
|
+
// skip if already registered
|
|
631
|
+
if (this.schemas.has(schema)) {
|
|
632
|
+
return false;
|
|
592
633
|
}
|
|
593
|
-
|
|
594
|
-
shiftChangeIndexes(shiftIndex) {
|
|
595
|
-
//
|
|
596
|
-
// Used only during:
|
|
634
|
+
this.types[typeid] = schema;
|
|
597
635
|
//
|
|
598
|
-
//
|
|
636
|
+
// Workaround to allow using an empty Schema (with no `@type()` fields)
|
|
599
637
|
//
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
: this.changes;
|
|
603
|
-
const newIndexedOperations = {};
|
|
604
|
-
const newIndexes = {};
|
|
605
|
-
for (const index in this.indexedOperations) {
|
|
606
|
-
newIndexedOperations[Number(index) + shiftIndex] = this.indexedOperations[index];
|
|
607
|
-
newIndexes[Number(index) + shiftIndex] = changeSet[index];
|
|
638
|
+
if (schema[Symbol.metadata] === undefined) {
|
|
639
|
+
Metadata.initialize(schema);
|
|
608
640
|
}
|
|
609
|
-
this.
|
|
610
|
-
|
|
611
|
-
changeSet.operations = changeSet.operations.map((index) => index + shiftIndex);
|
|
641
|
+
this.schemas.set(schema, typeid);
|
|
642
|
+
return true;
|
|
612
643
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
620
|
-
this._shiftAllChangeIndexes(shiftIndex, startIndex, this.allFilteredChanges);
|
|
621
|
-
this._shiftAllChangeIndexes(shiftIndex, startIndex, this.allChanges);
|
|
644
|
+
getTypeId(klass) {
|
|
645
|
+
return this.schemas.get(klass);
|
|
646
|
+
}
|
|
647
|
+
discoverTypes(klass, parentIndex, parentFieldViewTag) {
|
|
648
|
+
if (!this.add(klass)) {
|
|
649
|
+
return;
|
|
622
650
|
}
|
|
623
|
-
|
|
624
|
-
|
|
651
|
+
// add classes inherited from this base class
|
|
652
|
+
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
653
|
+
this.discoverTypes(child, parentIndex, parentFieldViewTag);
|
|
654
|
+
});
|
|
655
|
+
// add parent classes
|
|
656
|
+
let parent = klass;
|
|
657
|
+
while ((parent = Object.getPrototypeOf(parent)) &&
|
|
658
|
+
parent !== Schema && // stop at root (Schema)
|
|
659
|
+
parent !== Function.prototype // stop at root (non-Schema)
|
|
660
|
+
) {
|
|
661
|
+
this.discoverTypes(parent);
|
|
625
662
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
663
|
+
const metadata = (klass[Symbol.metadata] ??= {});
|
|
664
|
+
// if any schema/field has filters, mark "context" as having filters.
|
|
665
|
+
if (metadata[$viewFieldIndexes]) {
|
|
666
|
+
this.hasFilters = true;
|
|
667
|
+
}
|
|
668
|
+
if (parentFieldViewTag !== undefined) {
|
|
669
|
+
this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
|
|
670
|
+
}
|
|
671
|
+
for (const fieldIndex in metadata) {
|
|
672
|
+
const index = fieldIndex;
|
|
673
|
+
const fieldType = metadata[index].type;
|
|
674
|
+
const viewTag = metadata[index].tag;
|
|
675
|
+
if (typeof (fieldType) === "string") {
|
|
676
|
+
continue;
|
|
633
677
|
}
|
|
634
|
-
|
|
635
|
-
|
|
678
|
+
if (Array.isArray(fieldType)) {
|
|
679
|
+
const type = fieldType[0];
|
|
680
|
+
// skip primitive types
|
|
681
|
+
if (type === "string") {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
this.discoverTypes(type, index, viewTag);
|
|
636
685
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
686
|
+
else if (typeof (fieldType) === "function") {
|
|
687
|
+
this.discoverTypes(fieldType, viewTag);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
const type = Object.values(fieldType)[0];
|
|
691
|
+
// skip primitive types
|
|
692
|
+
if (typeof (type) === "string") {
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
this.discoverTypes(type, index, viewTag);
|
|
643
696
|
}
|
|
644
697
|
}
|
|
645
698
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function getNormalizedType(type) {
|
|
702
|
+
return (Array.isArray(type))
|
|
703
|
+
? { array: type[0] }
|
|
704
|
+
: (typeof (type['type']) !== "undefined")
|
|
705
|
+
? type['type']
|
|
706
|
+
: type;
|
|
707
|
+
}
|
|
708
|
+
const Metadata = {
|
|
709
|
+
addField(metadata, index, name, type, descriptor) {
|
|
710
|
+
if (index > 64) {
|
|
711
|
+
throw new Error(`Can't define field '${name}'.\nSchema instances may only have up to 64 fields.`);
|
|
657
712
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
713
|
+
metadata[index] = Object.assign(metadata[index] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
|
|
714
|
+
{
|
|
715
|
+
type: getNormalizedType(type),
|
|
716
|
+
index,
|
|
717
|
+
name,
|
|
718
|
+
});
|
|
719
|
+
// create "descriptors" map
|
|
720
|
+
Object.defineProperty(metadata, $descriptors, {
|
|
721
|
+
value: metadata[$descriptors] || {},
|
|
722
|
+
enumerable: false,
|
|
723
|
+
configurable: true,
|
|
724
|
+
});
|
|
725
|
+
if (descriptor) {
|
|
726
|
+
// for encoder
|
|
727
|
+
metadata[$descriptors][name] = descriptor;
|
|
728
|
+
metadata[$descriptors][`_${name}`] = {
|
|
729
|
+
value: undefined,
|
|
730
|
+
writable: true,
|
|
731
|
+
enumerable: false,
|
|
732
|
+
configurable: true,
|
|
733
|
+
};
|
|
663
734
|
}
|
|
664
735
|
else {
|
|
665
|
-
//
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
736
|
+
// for decoder
|
|
737
|
+
metadata[$descriptors][name] = {
|
|
738
|
+
value: undefined,
|
|
739
|
+
writable: true,
|
|
740
|
+
enumerable: true,
|
|
741
|
+
configurable: true,
|
|
742
|
+
};
|
|
672
743
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
744
|
+
// map -1 as last field index
|
|
745
|
+
Object.defineProperty(metadata, $numFields, {
|
|
746
|
+
value: index,
|
|
747
|
+
enumerable: false,
|
|
748
|
+
configurable: true
|
|
749
|
+
});
|
|
750
|
+
// map field name => index (non enumerable)
|
|
751
|
+
Object.defineProperty(metadata, name, {
|
|
752
|
+
value: index,
|
|
753
|
+
enumerable: false,
|
|
754
|
+
configurable: true,
|
|
755
|
+
});
|
|
756
|
+
// if child Ref/complex type, add to -4
|
|
757
|
+
if (typeof (metadata[index].type) !== "string") {
|
|
758
|
+
if (metadata[$refTypeFieldIndexes] === undefined) {
|
|
759
|
+
Object.defineProperty(metadata, $refTypeFieldIndexes, {
|
|
760
|
+
value: [],
|
|
761
|
+
enumerable: false,
|
|
762
|
+
configurable: true,
|
|
763
|
+
});
|
|
693
764
|
}
|
|
694
|
-
|
|
765
|
+
metadata[$refTypeFieldIndexes].push(index);
|
|
695
766
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
// -
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
767
|
+
},
|
|
768
|
+
setTag(metadata, fieldName, tag) {
|
|
769
|
+
const index = metadata[fieldName];
|
|
770
|
+
const field = metadata[index];
|
|
771
|
+
// add 'tag' to the field
|
|
772
|
+
field.tag = tag;
|
|
773
|
+
if (!metadata[$viewFieldIndexes]) {
|
|
774
|
+
// -2: all field indexes with "view" tag
|
|
775
|
+
Object.defineProperty(metadata, $viewFieldIndexes, {
|
|
776
|
+
value: [],
|
|
777
|
+
enumerable: false,
|
|
778
|
+
configurable: true
|
|
779
|
+
});
|
|
780
|
+
// -3: field indexes by "view" tag
|
|
781
|
+
Object.defineProperty(metadata, $fieldIndexesByViewTag, {
|
|
782
|
+
value: {},
|
|
783
|
+
enumerable: false,
|
|
784
|
+
configurable: true
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
metadata[$viewFieldIndexes].push(index);
|
|
788
|
+
if (!metadata[$fieldIndexesByViewTag][tag]) {
|
|
789
|
+
metadata[$fieldIndexesByViewTag][tag] = [];
|
|
790
|
+
}
|
|
791
|
+
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
792
|
+
},
|
|
793
|
+
setFields(target, fields) {
|
|
794
|
+
// for inheritance support
|
|
795
|
+
const constructor = target.prototype.constructor;
|
|
796
|
+
TypeContext.register(constructor);
|
|
797
|
+
const parentClass = Object.getPrototypeOf(constructor);
|
|
798
|
+
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
799
|
+
const metadata = Metadata.initialize(constructor);
|
|
800
|
+
// Use Schema's methods if not defined in the class
|
|
801
|
+
if (!constructor[$track]) {
|
|
802
|
+
constructor[$track] = Schema[$track];
|
|
803
|
+
}
|
|
804
|
+
if (!constructor[$encoder]) {
|
|
805
|
+
constructor[$encoder] = Schema[$encoder];
|
|
715
806
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
//
|
|
719
|
-
if (this.filteredChanges) {
|
|
720
|
-
deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
|
|
721
|
-
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
807
|
+
if (!constructor[$decoder]) {
|
|
808
|
+
constructor[$decoder] = Schema[$decoder];
|
|
722
809
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
enqueueChangeTree(this.root, this, 'changes');
|
|
810
|
+
if (!constructor.prototype.toJSON) {
|
|
811
|
+
constructor.prototype.toJSON = Schema.prototype.toJSON;
|
|
726
812
|
}
|
|
727
|
-
}
|
|
728
|
-
endEncode() {
|
|
729
|
-
this.indexedOperations = {};
|
|
730
|
-
// // clear changes
|
|
731
|
-
// this.changes.indexes = {};
|
|
732
|
-
// this.changes.operations.length = 0;
|
|
733
|
-
// ArraySchema and MapSchema have a custom "encode end" method
|
|
734
|
-
this.ref[$onEncodeEnd]?.();
|
|
735
|
-
// Not a new instance anymore
|
|
736
|
-
this.isNew = false;
|
|
737
|
-
}
|
|
738
|
-
discard(discardAll = false) {
|
|
739
813
|
//
|
|
740
|
-
//
|
|
741
|
-
// Remove cached key to ensure ADD operations is unsed instead of
|
|
742
|
-
// REPLACE in case same key is used on next patches.
|
|
814
|
+
// detect index for this field, considering inheritance
|
|
743
815
|
//
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
this
|
|
752
|
-
|
|
816
|
+
let fieldIndex = metadata[$numFields] // current structure already has fields defined
|
|
817
|
+
?? (parentMetadata && parentMetadata[$numFields]) // parent structure has fields defined
|
|
818
|
+
?? -1; // no fields defined
|
|
819
|
+
fieldIndex++;
|
|
820
|
+
for (const field in fields) {
|
|
821
|
+
const type = fields[field];
|
|
822
|
+
const normalizedType = getNormalizedType(type);
|
|
823
|
+
// FIXME: this code is duplicated from @type() annotation
|
|
824
|
+
const complexTypeKlass = (Array.isArray(type))
|
|
825
|
+
? getType("array")
|
|
826
|
+
: (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
|
|
827
|
+
const childType = (complexTypeKlass)
|
|
828
|
+
? Object.values(type)[0]
|
|
829
|
+
: normalizedType;
|
|
830
|
+
Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass));
|
|
831
|
+
fieldIndex++;
|
|
753
832
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
833
|
+
return target;
|
|
834
|
+
},
|
|
835
|
+
isDeprecated(metadata, field) {
|
|
836
|
+
return metadata[field].deprecated === true;
|
|
837
|
+
},
|
|
838
|
+
init(klass) {
|
|
839
|
+
//
|
|
840
|
+
// Used only to initialize an empty Schema (Encoder#constructor)
|
|
841
|
+
// TODO: remove/refactor this...
|
|
842
|
+
//
|
|
843
|
+
const metadata = {};
|
|
844
|
+
klass[Symbol.metadata] = metadata;
|
|
845
|
+
Object.defineProperty(metadata, $numFields, {
|
|
846
|
+
value: 0,
|
|
847
|
+
enumerable: false,
|
|
848
|
+
configurable: true,
|
|
849
|
+
});
|
|
850
|
+
},
|
|
851
|
+
initialize(constructor) {
|
|
852
|
+
const parentClass = Object.getPrototypeOf(constructor);
|
|
853
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
854
|
+
let metadata = constructor[Symbol.metadata] ?? Object.create(null);
|
|
855
|
+
// make sure inherited classes have their own metadata object.
|
|
856
|
+
if (parentClass !== Schema && metadata === parentMetadata) {
|
|
857
|
+
metadata = Object.create(null);
|
|
858
|
+
if (parentMetadata) {
|
|
859
|
+
//
|
|
860
|
+
// assign parent metadata to current
|
|
861
|
+
//
|
|
862
|
+
Object.setPrototypeOf(metadata, parentMetadata);
|
|
863
|
+
// $numFields
|
|
864
|
+
Object.defineProperty(metadata, $numFields, {
|
|
865
|
+
value: parentMetadata[$numFields],
|
|
866
|
+
enumerable: false,
|
|
867
|
+
configurable: true,
|
|
868
|
+
writable: true,
|
|
869
|
+
});
|
|
870
|
+
// $viewFieldIndexes / $fieldIndexesByViewTag
|
|
871
|
+
if (parentMetadata[$viewFieldIndexes] !== undefined) {
|
|
872
|
+
Object.defineProperty(metadata, $viewFieldIndexes, {
|
|
873
|
+
value: [...parentMetadata[$viewFieldIndexes]],
|
|
874
|
+
enumerable: false,
|
|
875
|
+
configurable: true,
|
|
876
|
+
writable: true,
|
|
877
|
+
});
|
|
878
|
+
Object.defineProperty(metadata, $fieldIndexesByViewTag, {
|
|
879
|
+
value: { ...parentMetadata[$fieldIndexesByViewTag] },
|
|
880
|
+
enumerable: false,
|
|
881
|
+
configurable: true,
|
|
882
|
+
writable: true,
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
// $refTypeFieldIndexes
|
|
886
|
+
if (parentMetadata[$refTypeFieldIndexes] !== undefined) {
|
|
887
|
+
Object.defineProperty(metadata, $refTypeFieldIndexes, {
|
|
888
|
+
value: [...parentMetadata[$refTypeFieldIndexes]],
|
|
889
|
+
enumerable: false,
|
|
890
|
+
configurable: true,
|
|
891
|
+
writable: true,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
// $descriptors
|
|
895
|
+
Object.defineProperty(metadata, $descriptors, {
|
|
896
|
+
value: { ...parentMetadata[$descriptors] },
|
|
897
|
+
enumerable: false,
|
|
898
|
+
configurable: true,
|
|
899
|
+
writable: true,
|
|
900
|
+
});
|
|
760
901
|
}
|
|
761
|
-
// remove children references
|
|
762
|
-
this.forEachChild((changeTree, _) => this.root?.remove(changeTree));
|
|
763
902
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
903
|
+
constructor[Symbol.metadata] = metadata;
|
|
904
|
+
return metadata;
|
|
905
|
+
},
|
|
906
|
+
isValidInstance(klass) {
|
|
907
|
+
return (klass.constructor[Symbol.metadata] &&
|
|
908
|
+
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], $numFields));
|
|
909
|
+
},
|
|
910
|
+
getFields(klass) {
|
|
911
|
+
const metadata = klass[Symbol.metadata];
|
|
912
|
+
const fields = {};
|
|
913
|
+
for (let i = 0; i <= metadata[$numFields]; i++) {
|
|
914
|
+
fields[metadata[i].name] = metadata[i].type;
|
|
775
915
|
}
|
|
776
|
-
|
|
916
|
+
return fields;
|
|
777
917
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
function setOperationAtIndex(changeSet, index) {
|
|
921
|
+
const operationsIndex = changeSet.indexes[index];
|
|
922
|
+
if (operationsIndex === undefined) {
|
|
923
|
+
changeSet.indexes[index] = changeSet.operations.push(index) - 1;
|
|
784
924
|
}
|
|
785
|
-
|
|
786
|
-
|
|
925
|
+
else {
|
|
926
|
+
changeSet.operations[operationsIndex] = index;
|
|
787
927
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
this.
|
|
928
|
+
}
|
|
929
|
+
function deleteOperationAtIndex(changeSet, index) {
|
|
930
|
+
const operationsIndex = changeSet.indexes[index];
|
|
931
|
+
if (operationsIndex !== undefined) {
|
|
932
|
+
changeSet.operations[operationsIndex] = undefined;
|
|
933
|
+
}
|
|
934
|
+
delete changeSet.indexes[index];
|
|
935
|
+
}
|
|
936
|
+
function enqueueChangeTree(root, changeTree, changeSet, queueRootIndex = changeTree[changeSet].queueRootIndex) {
|
|
937
|
+
if (root && root[changeSet][queueRootIndex] !== changeTree) {
|
|
938
|
+
changeTree[changeSet].queueRootIndex = root[changeSet].push(changeTree) - 1;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
class ChangeTree {
|
|
942
|
+
constructor(ref) {
|
|
943
|
+
this.isFiltered = false;
|
|
944
|
+
this.isPartiallyFiltered = false;
|
|
945
|
+
this.indexedOperations = {};
|
|
806
946
|
//
|
|
807
|
-
// TODO:
|
|
947
|
+
// TODO:
|
|
948
|
+
// try storing the index + operation per item.
|
|
949
|
+
// example: 1024 & 1025 => ADD, 1026 => DELETE
|
|
808
950
|
//
|
|
809
|
-
//
|
|
810
|
-
// because "isFiltered" may not be imedialely available on `change()`
|
|
951
|
+
// => https://chatgpt.com/share/67107d0c-bc20-8004-8583-83b17dd7c196
|
|
811
952
|
//
|
|
812
|
-
|
|
813
|
-
|
|
953
|
+
this.changes = { indexes: {}, operations: [] };
|
|
954
|
+
this.allChanges = { indexes: {}, operations: [] };
|
|
955
|
+
/**
|
|
956
|
+
* Is this a new instance? Used on ArraySchema to determine OPERATION.MOVE_AND_ADD operation.
|
|
957
|
+
*/
|
|
958
|
+
this.isNew = true;
|
|
959
|
+
this.ref = ref;
|
|
960
|
+
//
|
|
961
|
+
// Does this structure have "filters" declared?
|
|
962
|
+
//
|
|
963
|
+
if (ref.constructor[Symbol.metadata]?.[$viewFieldIndexes]) {
|
|
814
964
|
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
965
|
+
this.filteredChanges = { indexes: {}, operations: [] };
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
setRoot(root) {
|
|
969
|
+
this.root = root;
|
|
970
|
+
const isNewChangeTree = this.root.add(this);
|
|
971
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
972
|
+
if (this.root.types.hasFilters) {
|
|
973
|
+
//
|
|
974
|
+
// At Schema initialization, the "root" structure might not be available
|
|
975
|
+
// yet, as it only does once the "Encoder" has been set up.
|
|
976
|
+
//
|
|
977
|
+
// So the "parent" may be already set without a "root".
|
|
978
|
+
//
|
|
979
|
+
this.checkIsFiltered(metadata, this.parent, this.parentIndex);
|
|
980
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
981
|
+
enqueueChangeTree(root, this, 'filteredChanges');
|
|
982
|
+
if (isNewChangeTree) {
|
|
983
|
+
this.root.allFilteredChanges.push(this);
|
|
984
|
+
}
|
|
828
985
|
}
|
|
829
986
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
* Copyright (c) 2018 Endel Dreyer
|
|
835
|
-
* Copyright (c) 2014 Ion Drive Software Ltd.
|
|
836
|
-
*
|
|
837
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
838
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
839
|
-
* in the Software without restriction, including without limitation the rights
|
|
840
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
841
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
842
|
-
* furnished to do so, subject to the following conditions:
|
|
843
|
-
*
|
|
844
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
845
|
-
* copies or substantial portions of the Software.
|
|
846
|
-
*
|
|
847
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
848
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
849
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
850
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
851
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
852
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
853
|
-
* SOFTWARE
|
|
854
|
-
*/
|
|
855
|
-
/**
|
|
856
|
-
* msgpack implementation highly based on notepack.io
|
|
857
|
-
* https://github.com/darrachequesne/notepack
|
|
858
|
-
*/
|
|
859
|
-
let textEncoder;
|
|
860
|
-
// @ts-ignore
|
|
861
|
-
try {
|
|
862
|
-
textEncoder = new TextEncoder();
|
|
863
|
-
}
|
|
864
|
-
catch (e) { }
|
|
865
|
-
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
|
|
866
|
-
const utf8Length = (hasBufferByteLength)
|
|
867
|
-
? Buffer.byteLength // node
|
|
868
|
-
: function (str, _) {
|
|
869
|
-
var c = 0, length = 0;
|
|
870
|
-
for (var i = 0, l = str.length; i < l; i++) {
|
|
871
|
-
c = str.charCodeAt(i);
|
|
872
|
-
if (c < 0x80) {
|
|
873
|
-
length += 1;
|
|
874
|
-
}
|
|
875
|
-
else if (c < 0x800) {
|
|
876
|
-
length += 2;
|
|
987
|
+
if (!this.isFiltered) {
|
|
988
|
+
enqueueChangeTree(root, this, 'changes');
|
|
989
|
+
if (isNewChangeTree) {
|
|
990
|
+
this.root.allChanges.push(this);
|
|
877
991
|
}
|
|
878
|
-
|
|
879
|
-
|
|
992
|
+
}
|
|
993
|
+
// Recursively set root on child structures
|
|
994
|
+
if (metadata) {
|
|
995
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
996
|
+
const field = metadata[index];
|
|
997
|
+
const value = this.ref[field.name];
|
|
998
|
+
value?.[$changes].setRoot(root);
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
1002
|
+
// MapSchema / ArraySchema, etc.
|
|
1003
|
+
this.ref.forEach((value, key) => {
|
|
1004
|
+
value[$changes].setRoot(root);
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
setParent(parent, root, parentIndex) {
|
|
1009
|
+
this.parent = parent;
|
|
1010
|
+
this.parentIndex = parentIndex;
|
|
1011
|
+
// avoid setting parents with empty `root`
|
|
1012
|
+
if (!root) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
1016
|
+
// skip if parent is already set
|
|
1017
|
+
if (root !== this.root) {
|
|
1018
|
+
this.root = root;
|
|
1019
|
+
const isNewChangeTree = root.add(this);
|
|
1020
|
+
if (root.types.hasFilters) {
|
|
1021
|
+
this.checkIsFiltered(metadata, parent, parentIndex);
|
|
1022
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
1023
|
+
enqueueChangeTree(root, this, 'filteredChanges');
|
|
1024
|
+
if (isNewChangeTree) {
|
|
1025
|
+
this.root.allFilteredChanges.push(this);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
880
1028
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1029
|
+
if (!this.isFiltered) {
|
|
1030
|
+
enqueueChangeTree(root, this, 'changes');
|
|
1031
|
+
if (isNewChangeTree) {
|
|
1032
|
+
this.root.allChanges.push(this);
|
|
1033
|
+
}
|
|
884
1034
|
}
|
|
885
1035
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
function utf8Write(view, str, it) {
|
|
889
|
-
var c = 0;
|
|
890
|
-
for (var i = 0, l = str.length; i < l; i++) {
|
|
891
|
-
c = str.charCodeAt(i);
|
|
892
|
-
if (c < 0x80) {
|
|
893
|
-
view[it.offset++] = c;
|
|
1036
|
+
else {
|
|
1037
|
+
root.add(this);
|
|
894
1038
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1039
|
+
// assign same parent on child structures
|
|
1040
|
+
if (metadata) {
|
|
1041
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
1042
|
+
const field = metadata[index];
|
|
1043
|
+
const value = this.ref[field.name];
|
|
1044
|
+
value?.[$changes].setParent(this.ref, root, index);
|
|
1045
|
+
// try { throw new Error(); } catch (e) {
|
|
1046
|
+
// console.log(e.stack);
|
|
1047
|
+
// }
|
|
1048
|
+
});
|
|
899
1049
|
}
|
|
900
|
-
else if (
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1050
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
1051
|
+
// MapSchema / ArraySchema, etc.
|
|
1052
|
+
this.ref.forEach((value, key) => {
|
|
1053
|
+
value[$changes].setParent(this.ref, root, this.indexes[key] ?? key);
|
|
1054
|
+
});
|
|
905
1055
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1056
|
+
}
|
|
1057
|
+
forEachChild(callback) {
|
|
1058
|
+
//
|
|
1059
|
+
// assign same parent on child structures
|
|
1060
|
+
//
|
|
1061
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
1062
|
+
if (metadata) {
|
|
1063
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
1064
|
+
const field = metadata[index];
|
|
1065
|
+
const value = this.ref[field.name];
|
|
1066
|
+
if (value) {
|
|
1067
|
+
callback(value[$changes], index);
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
1072
|
+
// MapSchema / ArraySchema, etc.
|
|
1073
|
+
this.ref.forEach((value, key) => {
|
|
1074
|
+
callback(value[$changes], this.indexes[key] ?? key);
|
|
1075
|
+
});
|
|
914
1076
|
}
|
|
915
1077
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
bytes[it.offset++] = value & 255;
|
|
922
|
-
}
|
|
923
|
-
function int16$1(bytes, value, it) {
|
|
924
|
-
bytes[it.offset++] = value & 255;
|
|
925
|
-
bytes[it.offset++] = (value >> 8) & 255;
|
|
926
|
-
}
|
|
927
|
-
function uint16$1(bytes, value, it) {
|
|
928
|
-
bytes[it.offset++] = value & 255;
|
|
929
|
-
bytes[it.offset++] = (value >> 8) & 255;
|
|
930
|
-
}
|
|
931
|
-
function int32$1(bytes, value, it) {
|
|
932
|
-
bytes[it.offset++] = value & 255;
|
|
933
|
-
bytes[it.offset++] = (value >> 8) & 255;
|
|
934
|
-
bytes[it.offset++] = (value >> 16) & 255;
|
|
935
|
-
bytes[it.offset++] = (value >> 24) & 255;
|
|
936
|
-
}
|
|
937
|
-
function uint32$1(bytes, value, it) {
|
|
938
|
-
const b4 = value >> 24;
|
|
939
|
-
const b3 = value >> 16;
|
|
940
|
-
const b2 = value >> 8;
|
|
941
|
-
const b1 = value;
|
|
942
|
-
bytes[it.offset++] = b1 & 255;
|
|
943
|
-
bytes[it.offset++] = b2 & 255;
|
|
944
|
-
bytes[it.offset++] = b3 & 255;
|
|
945
|
-
bytes[it.offset++] = b4 & 255;
|
|
946
|
-
}
|
|
947
|
-
function int64$1(bytes, value, it) {
|
|
948
|
-
const high = Math.floor(value / Math.pow(2, 32));
|
|
949
|
-
const low = value >>> 0;
|
|
950
|
-
uint32$1(bytes, low, it);
|
|
951
|
-
uint32$1(bytes, high, it);
|
|
952
|
-
}
|
|
953
|
-
function uint64$1(bytes, value, it) {
|
|
954
|
-
const high = (value / Math.pow(2, 32)) >> 0;
|
|
955
|
-
const low = value >>> 0;
|
|
956
|
-
uint32$1(bytes, low, it);
|
|
957
|
-
uint32$1(bytes, high, it);
|
|
958
|
-
}
|
|
959
|
-
function float32$1(bytes, value, it) {
|
|
960
|
-
writeFloat32(bytes, value, it);
|
|
961
|
-
}
|
|
962
|
-
function float64$1(bytes, value, it) {
|
|
963
|
-
writeFloat64(bytes, value, it);
|
|
964
|
-
}
|
|
965
|
-
const _int32$1 = new Int32Array(2);
|
|
966
|
-
const _float32$1 = new Float32Array(_int32$1.buffer);
|
|
967
|
-
const _float64$1 = new Float64Array(_int32$1.buffer);
|
|
968
|
-
function writeFloat32(bytes, value, it) {
|
|
969
|
-
_float32$1[0] = value;
|
|
970
|
-
int32$1(bytes, _int32$1[0], it);
|
|
971
|
-
}
|
|
972
|
-
function writeFloat64(bytes, value, it) {
|
|
973
|
-
_float64$1[0] = value;
|
|
974
|
-
int32$1(bytes, _int32$1[0 ], it);
|
|
975
|
-
int32$1(bytes, _int32$1[1 ], it);
|
|
976
|
-
}
|
|
977
|
-
function boolean$1(bytes, value, it) {
|
|
978
|
-
bytes[it.offset++] = value ? 1 : 0; // uint8
|
|
979
|
-
}
|
|
980
|
-
function string$1(bytes, value, it) {
|
|
981
|
-
// encode `null` strings as empty.
|
|
982
|
-
if (!value) {
|
|
983
|
-
value = "";
|
|
1078
|
+
operation(op) {
|
|
1079
|
+
// operations without index use negative values to represent them
|
|
1080
|
+
// this is checked during .encode() time.
|
|
1081
|
+
this.changes.operations.push(-op);
|
|
1082
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
984
1083
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1084
|
+
change(index, operation = exports.OPERATION.ADD) {
|
|
1085
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
1086
|
+
const isFiltered = this.isFiltered || (metadata?.[index]?.tag !== undefined);
|
|
1087
|
+
const changeSet = (isFiltered)
|
|
1088
|
+
? this.filteredChanges
|
|
1089
|
+
: this.changes;
|
|
1090
|
+
const previousOperation = this.indexedOperations[index];
|
|
1091
|
+
if (!previousOperation || previousOperation === exports.OPERATION.DELETE) {
|
|
1092
|
+
const op = (!previousOperation)
|
|
1093
|
+
? operation
|
|
1094
|
+
: (previousOperation === exports.OPERATION.DELETE)
|
|
1095
|
+
? exports.OPERATION.DELETE_AND_ADD
|
|
1096
|
+
: operation;
|
|
1097
|
+
//
|
|
1098
|
+
// TODO: are DELETE operations being encoded as ADD here ??
|
|
1099
|
+
//
|
|
1100
|
+
this.indexedOperations[index] = op;
|
|
1101
|
+
}
|
|
1102
|
+
setOperationAtIndex(changeSet, index);
|
|
1103
|
+
if (isFiltered) {
|
|
1104
|
+
setOperationAtIndex(this.allFilteredChanges, index);
|
|
1105
|
+
if (this.root) {
|
|
1106
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
1107
|
+
enqueueChangeTree(this.root, this, 'allFilteredChanges');
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
setOperationAtIndex(this.allChanges, index);
|
|
1112
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
1113
|
+
}
|
|
991
1114
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1115
|
+
shiftChangeIndexes(shiftIndex) {
|
|
1116
|
+
//
|
|
1117
|
+
// Used only during:
|
|
1118
|
+
//
|
|
1119
|
+
// - ArraySchema#unshift()
|
|
1120
|
+
//
|
|
1121
|
+
const changeSet = (this.isFiltered)
|
|
1122
|
+
? this.filteredChanges
|
|
1123
|
+
: this.changes;
|
|
1124
|
+
const newIndexedOperations = {};
|
|
1125
|
+
const newIndexes = {};
|
|
1126
|
+
for (const index in this.indexedOperations) {
|
|
1127
|
+
newIndexedOperations[Number(index) + shiftIndex] = this.indexedOperations[index];
|
|
1128
|
+
newIndexes[Number(index) + shiftIndex] = changeSet[index];
|
|
1129
|
+
}
|
|
1130
|
+
this.indexedOperations = newIndexedOperations;
|
|
1131
|
+
changeSet.indexes = newIndexes;
|
|
1132
|
+
changeSet.operations = changeSet.operations.map((index) => index + shiftIndex);
|
|
997
1133
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1134
|
+
shiftAllChangeIndexes(shiftIndex, startIndex = 0) {
|
|
1135
|
+
//
|
|
1136
|
+
// Used only during:
|
|
1137
|
+
//
|
|
1138
|
+
// - ArraySchema#splice()
|
|
1139
|
+
//
|
|
1140
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
1141
|
+
this._shiftAllChangeIndexes(shiftIndex, startIndex, this.allFilteredChanges);
|
|
1142
|
+
this._shiftAllChangeIndexes(shiftIndex, startIndex, this.allChanges);
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
this._shiftAllChangeIndexes(shiftIndex, startIndex, this.allChanges);
|
|
1146
|
+
}
|
|
1003
1147
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1148
|
+
_shiftAllChangeIndexes(shiftIndex, startIndex = 0, changeSet) {
|
|
1149
|
+
const newIndexes = {};
|
|
1150
|
+
for (const key in changeSet.indexes) {
|
|
1151
|
+
const index = changeSet.indexes[key];
|
|
1152
|
+
if (index > startIndex) {
|
|
1153
|
+
newIndexes[Number(key) + shiftIndex] = index;
|
|
1154
|
+
}
|
|
1155
|
+
else {
|
|
1156
|
+
newIndexes[key] = index;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
changeSet.indexes = newIndexes;
|
|
1160
|
+
for (let i = 0; i < changeSet.operations.length; i++) {
|
|
1161
|
+
const index = changeSet.operations[i];
|
|
1162
|
+
if (index > startIndex) {
|
|
1163
|
+
changeSet.operations[i] = index + shiftIndex;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1009
1166
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1167
|
+
indexedOperation(index, operation, allChangesIndex = index) {
|
|
1168
|
+
this.indexedOperations[index] = operation;
|
|
1169
|
+
if (this.filteredChanges) {
|
|
1170
|
+
setOperationAtIndex(this.allFilteredChanges, allChangesIndex);
|
|
1171
|
+
setOperationAtIndex(this.filteredChanges, index);
|
|
1172
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
setOperationAtIndex(this.allChanges, allChangesIndex);
|
|
1176
|
+
setOperationAtIndex(this.changes, index);
|
|
1177
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
1178
|
+
}
|
|
1012
1179
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1180
|
+
getType(index) {
|
|
1181
|
+
if (Metadata.isValidInstance(this.ref)) {
|
|
1182
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
1183
|
+
return metadata[index].type;
|
|
1184
|
+
}
|
|
1185
|
+
else {
|
|
1186
|
+
//
|
|
1187
|
+
// Get the child type from parent structure.
|
|
1188
|
+
// - ["string"] => "string"
|
|
1189
|
+
// - { map: "string" } => "string"
|
|
1190
|
+
// - { set: "string" } => "string"
|
|
1191
|
+
//
|
|
1192
|
+
return this.ref[$childType];
|
|
1193
|
+
}
|
|
1019
1194
|
}
|
|
1020
|
-
|
|
1021
|
-
return
|
|
1195
|
+
getChange(index) {
|
|
1196
|
+
return this.indexedOperations[index];
|
|
1022
1197
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
//
|
|
1028
|
-
//
|
|
1029
|
-
//
|
|
1030
|
-
|
|
1031
|
-
// writeFloat32(bytes, value);
|
|
1032
|
-
// return 5;
|
|
1198
|
+
//
|
|
1199
|
+
// used during `.encode()`
|
|
1200
|
+
//
|
|
1201
|
+
getValue(index, isEncodeAll = false) {
|
|
1202
|
+
//
|
|
1203
|
+
// `isEncodeAll` param is only used by ArraySchema
|
|
1204
|
+
//
|
|
1205
|
+
return this.ref[$getByIndex](index, isEncodeAll);
|
|
1033
1206
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1207
|
+
delete(index, operation, allChangesIndex = index) {
|
|
1208
|
+
if (index === undefined) {
|
|
1209
|
+
try {
|
|
1210
|
+
throw new Error(`@colyseus/schema ${this.ref.constructor.name}: trying to delete non-existing index '${index}'`);
|
|
1211
|
+
}
|
|
1212
|
+
catch (e) {
|
|
1213
|
+
console.warn(e);
|
|
1214
|
+
}
|
|
1215
|
+
return;
|
|
1039
1216
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1217
|
+
const changeSet = (this.filteredChanges)
|
|
1218
|
+
? this.filteredChanges
|
|
1219
|
+
: this.changes;
|
|
1220
|
+
this.indexedOperations[index] = operation ?? exports.OPERATION.DELETE;
|
|
1221
|
+
setOperationAtIndex(changeSet, index);
|
|
1222
|
+
const previousValue = this.getValue(index);
|
|
1223
|
+
// remove `root` reference
|
|
1224
|
+
if (previousValue && previousValue[$changes]) {
|
|
1225
|
+
//
|
|
1226
|
+
// FIXME: this.root is "undefined"
|
|
1227
|
+
//
|
|
1228
|
+
// This method is being called at decoding time when a DELETE operation is found.
|
|
1229
|
+
//
|
|
1230
|
+
// - This is due to using the concrete Schema class at decoding time.
|
|
1231
|
+
// - "Reflected" structures do not have this problem.
|
|
1232
|
+
//
|
|
1233
|
+
// (the property descriptors should NOT be used at decoding time. only at encoding time.)
|
|
1234
|
+
//
|
|
1235
|
+
this.root?.remove(previousValue[$changes]);
|
|
1045
1236
|
}
|
|
1046
|
-
//
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1237
|
+
//
|
|
1238
|
+
// FIXME: this is looking a ugly and repeated
|
|
1239
|
+
//
|
|
1240
|
+
if (this.filteredChanges) {
|
|
1241
|
+
deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
|
|
1242
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
1051
1243
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
uint32$1(bytes, value, it);
|
|
1056
|
-
return 5;
|
|
1244
|
+
else {
|
|
1245
|
+
deleteOperationAtIndex(this.allChanges, allChangesIndex);
|
|
1246
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
1057
1247
|
}
|
|
1058
|
-
// uint 64
|
|
1059
|
-
bytes[it.offset++] = 0xcf;
|
|
1060
|
-
uint64$1(bytes, value, it);
|
|
1061
|
-
return 9;
|
|
1062
1248
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1249
|
+
endEncode() {
|
|
1250
|
+
this.indexedOperations = {};
|
|
1251
|
+
// // clear changes
|
|
1252
|
+
// this.changes.indexes = {};
|
|
1253
|
+
// this.changes.operations.length = 0;
|
|
1254
|
+
// ArraySchema and MapSchema have a custom "encode end" method
|
|
1255
|
+
this.ref[$onEncodeEnd]?.();
|
|
1256
|
+
// Not a new instance anymore
|
|
1257
|
+
this.isNew = false;
|
|
1258
|
+
}
|
|
1259
|
+
discard(discardAll = false) {
|
|
1260
|
+
//
|
|
1261
|
+
// > MapSchema:
|
|
1262
|
+
// Remove cached key to ensure ADD operations is unsed instead of
|
|
1263
|
+
// REPLACE in case same key is used on next patches.
|
|
1264
|
+
//
|
|
1265
|
+
this.ref[$onEncodeEnd]?.();
|
|
1266
|
+
this.indexedOperations = {};
|
|
1267
|
+
this.changes.indexes = {};
|
|
1268
|
+
this.changes.operations.length = 0;
|
|
1269
|
+
this.changes.queueRootIndex = undefined;
|
|
1270
|
+
if (this.filteredChanges !== undefined) {
|
|
1271
|
+
this.filteredChanges.indexes = {};
|
|
1272
|
+
this.filteredChanges.operations.length = 0;
|
|
1273
|
+
this.filteredChanges.queueRootIndex = undefined;
|
|
1068
1274
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1275
|
+
if (discardAll) {
|
|
1276
|
+
this.allChanges.indexes = {};
|
|
1277
|
+
this.allChanges.operations.length = 0;
|
|
1278
|
+
if (this.allFilteredChanges !== undefined) {
|
|
1279
|
+
this.allFilteredChanges.indexes = {};
|
|
1280
|
+
this.allFilteredChanges.operations.length = 0;
|
|
1281
|
+
}
|
|
1282
|
+
// remove children references
|
|
1283
|
+
this.forEachChild((changeTree, _) => this.root?.remove(changeTree));
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Recursively discard all changes from this, and child structures.
|
|
1288
|
+
*/
|
|
1289
|
+
discardAll() {
|
|
1290
|
+
const keys = Object.keys(this.indexedOperations);
|
|
1291
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
1292
|
+
const value = this.getValue(Number(keys[i]));
|
|
1293
|
+
if (value && value[$changes]) {
|
|
1294
|
+
value[$changes].discardAll();
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
this.discard();
|
|
1298
|
+
}
|
|
1299
|
+
ensureRefId() {
|
|
1300
|
+
// skip if refId is already set.
|
|
1301
|
+
if (this.refId !== undefined) {
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
this.refId = this.root.getNextUniqueId();
|
|
1305
|
+
}
|
|
1306
|
+
get changed() {
|
|
1307
|
+
return (Object.entries(this.indexedOperations).length > 0);
|
|
1308
|
+
}
|
|
1309
|
+
checkIsFiltered(metadata, parent, parentIndex) {
|
|
1310
|
+
// Detect if current structure has "filters" declared
|
|
1311
|
+
this.isPartiallyFiltered = metadata?.[$viewFieldIndexes] !== undefined;
|
|
1312
|
+
if (this.isPartiallyFiltered) {
|
|
1313
|
+
this.filteredChanges = this.filteredChanges || { indexes: {}, operations: [] };
|
|
1314
|
+
this.allFilteredChanges = this.allFilteredChanges || { indexes: {}, operations: [] };
|
|
1074
1315
|
}
|
|
1075
|
-
//
|
|
1076
|
-
if (
|
|
1077
|
-
|
|
1078
|
-
int16$1(bytes, value, it);
|
|
1079
|
-
return 3;
|
|
1316
|
+
// skip if parent is not set
|
|
1317
|
+
if (!parent) {
|
|
1318
|
+
return;
|
|
1080
1319
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1320
|
+
if (!Metadata.isValidInstance(parent)) {
|
|
1321
|
+
const parentChangeTree = parent[$changes];
|
|
1322
|
+
parent = parentChangeTree.parent;
|
|
1323
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
1324
|
+
}
|
|
1325
|
+
const parentMetadata = parent.constructor?.[Symbol.metadata];
|
|
1326
|
+
this.isFiltered = parentMetadata?.[$viewFieldIndexes]?.includes(parentIndex);
|
|
1327
|
+
//
|
|
1328
|
+
// TODO: refactor this!
|
|
1329
|
+
//
|
|
1330
|
+
// swapping `changes` and `filteredChanges` is required here
|
|
1331
|
+
// because "isFiltered" may not be imedialely available on `change()`
|
|
1332
|
+
//
|
|
1333
|
+
if (this.isFiltered) {
|
|
1334
|
+
this.filteredChanges = { indexes: {}, operations: [] };
|
|
1335
|
+
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
1336
|
+
if (this.changes.operations.length > 0) {
|
|
1337
|
+
// swap changes reference
|
|
1338
|
+
const changes = this.changes;
|
|
1339
|
+
this.changes = this.filteredChanges;
|
|
1340
|
+
this.filteredChanges = changes;
|
|
1341
|
+
// swap "all changes" reference
|
|
1342
|
+
const allFilteredChanges = this.allFilteredChanges;
|
|
1343
|
+
this.allFilteredChanges = this.allChanges;
|
|
1344
|
+
this.allChanges = allFilteredChanges;
|
|
1345
|
+
// console.log("SWAP =>", {
|
|
1346
|
+
// "this.allFilteredChanges": this.allFilteredChanges,
|
|
1347
|
+
// "this.allChanges": this.allChanges
|
|
1348
|
+
// })
|
|
1349
|
+
}
|
|
1086
1350
|
}
|
|
1087
|
-
// int 64
|
|
1088
|
-
bytes[it.offset++] = 0xd3;
|
|
1089
|
-
int64$1(bytes, value, it);
|
|
1090
|
-
return 9;
|
|
1091
1351
|
}
|
|
1092
1352
|
}
|
|
1093
1353
|
|
|
1094
|
-
var encode = /*#__PURE__*/Object.freeze({
|
|
1095
|
-
__proto__: null,
|
|
1096
|
-
boolean: boolean$1,
|
|
1097
|
-
float32: float32$1,
|
|
1098
|
-
float64: float64$1,
|
|
1099
|
-
int16: int16$1,
|
|
1100
|
-
int32: int32$1,
|
|
1101
|
-
int64: int64$1,
|
|
1102
|
-
int8: int8$1,
|
|
1103
|
-
number: number$1,
|
|
1104
|
-
string: string$1,
|
|
1105
|
-
uint16: uint16$1,
|
|
1106
|
-
uint32: uint32$1,
|
|
1107
|
-
uint64: uint64$1,
|
|
1108
|
-
uint8: uint8$1,
|
|
1109
|
-
utf8Length: utf8Length,
|
|
1110
|
-
utf8Write: utf8Write,
|
|
1111
|
-
writeFloat32: writeFloat32,
|
|
1112
|
-
writeFloat64: writeFloat64
|
|
1113
|
-
});
|
|
1114
|
-
|
|
1115
1354
|
function encodeValue(encoder, bytes, type, value, operation, it) {
|
|
1116
1355
|
if (typeof (type) === "string") {
|
|
1117
1356
|
encode[type]?.(bytes, value, it);
|
|
@@ -1121,7 +1360,7 @@ function encodeValue(encoder, bytes, type, value, operation, it) {
|
|
|
1121
1360
|
// Encode refId for this instance.
|
|
1122
1361
|
// The actual instance is going to be encoded on next `changeTree` iteration.
|
|
1123
1362
|
//
|
|
1124
|
-
number
|
|
1363
|
+
encode.number(bytes, value[$changes].refId, it);
|
|
1125
1364
|
// Try to encode inherited TYPE_ID if it's an ADD operation.
|
|
1126
1365
|
if ((operation & exports.OPERATION.ADD) === exports.OPERATION.ADD) {
|
|
1127
1366
|
encoder.tryEncodeTypeId(bytes, type, value.constructor, it);
|
|
@@ -1132,7 +1371,7 @@ function encodeValue(encoder, bytes, type, value, operation, it) {
|
|
|
1132
1371
|
// Encode refId for this instance.
|
|
1133
1372
|
// The actual instance is going to be encoded on next `changeTree` iteration.
|
|
1134
1373
|
//
|
|
1135
|
-
number
|
|
1374
|
+
encode.number(bytes, value[$changes].refId, it);
|
|
1136
1375
|
}
|
|
1137
1376
|
}
|
|
1138
1377
|
/**
|
|
@@ -1163,7 +1402,7 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, index, ope
|
|
|
1163
1402
|
return;
|
|
1164
1403
|
}
|
|
1165
1404
|
// encode index
|
|
1166
|
-
number
|
|
1405
|
+
encode.number(bytes, index, it);
|
|
1167
1406
|
// Do not encode value for DELETE operations
|
|
1168
1407
|
if (operation === exports.OPERATION.DELETE) {
|
|
1169
1408
|
return;
|
|
@@ -1178,7 +1417,7 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, index, ope
|
|
|
1178
1417
|
// MapSchema dynamic key
|
|
1179
1418
|
//
|
|
1180
1419
|
const dynamicIndex = changeTree.ref['$indexes'].get(index);
|
|
1181
|
-
string
|
|
1420
|
+
encode.string(bytes, dynamicIndex, it);
|
|
1182
1421
|
}
|
|
1183
1422
|
}
|
|
1184
1423
|
const type = ref[$childType];
|
|
@@ -1226,7 +1465,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
1226
1465
|
return;
|
|
1227
1466
|
}
|
|
1228
1467
|
// encode index
|
|
1229
|
-
number
|
|
1468
|
+
encode.number(bytes, refOrIndex, it);
|
|
1230
1469
|
// Do not encode value for DELETE operations
|
|
1231
1470
|
if (operation === exports.OPERATION.DELETE) {
|
|
1232
1471
|
return;
|
|
@@ -1244,259 +1483,6 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
1244
1483
|
encodeValue(encoder, bytes, type, value, operation, it);
|
|
1245
1484
|
};
|
|
1246
1485
|
|
|
1247
|
-
/**
|
|
1248
|
-
* Copyright (c) 2018 Endel Dreyer
|
|
1249
|
-
* Copyright (c) 2014 Ion Drive Software Ltd.
|
|
1250
|
-
*
|
|
1251
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1252
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
1253
|
-
* in the Software without restriction, including without limitation the rights
|
|
1254
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1255
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
1256
|
-
* furnished to do so, subject to the following conditions:
|
|
1257
|
-
*
|
|
1258
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
1259
|
-
* copies or substantial portions of the Software.
|
|
1260
|
-
*
|
|
1261
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1262
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1263
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1264
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1265
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1266
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1267
|
-
* SOFTWARE
|
|
1268
|
-
*/
|
|
1269
|
-
function utf8Read(bytes, it, length) {
|
|
1270
|
-
var string = '', chr = 0;
|
|
1271
|
-
for (var i = it.offset, end = it.offset + length; i < end; i++) {
|
|
1272
|
-
var byte = bytes[i];
|
|
1273
|
-
if ((byte & 0x80) === 0x00) {
|
|
1274
|
-
string += String.fromCharCode(byte);
|
|
1275
|
-
continue;
|
|
1276
|
-
}
|
|
1277
|
-
if ((byte & 0xe0) === 0xc0) {
|
|
1278
|
-
string += String.fromCharCode(((byte & 0x1f) << 6) |
|
|
1279
|
-
(bytes[++i] & 0x3f));
|
|
1280
|
-
continue;
|
|
1281
|
-
}
|
|
1282
|
-
if ((byte & 0xf0) === 0xe0) {
|
|
1283
|
-
string += String.fromCharCode(((byte & 0x0f) << 12) |
|
|
1284
|
-
((bytes[++i] & 0x3f) << 6) |
|
|
1285
|
-
((bytes[++i] & 0x3f) << 0));
|
|
1286
|
-
continue;
|
|
1287
|
-
}
|
|
1288
|
-
if ((byte & 0xf8) === 0xf0) {
|
|
1289
|
-
chr = ((byte & 0x07) << 18) |
|
|
1290
|
-
((bytes[++i] & 0x3f) << 12) |
|
|
1291
|
-
((bytes[++i] & 0x3f) << 6) |
|
|
1292
|
-
((bytes[++i] & 0x3f) << 0);
|
|
1293
|
-
if (chr >= 0x010000) { // surrogate pair
|
|
1294
|
-
chr -= 0x010000;
|
|
1295
|
-
string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
|
|
1296
|
-
}
|
|
1297
|
-
else {
|
|
1298
|
-
string += String.fromCharCode(chr);
|
|
1299
|
-
}
|
|
1300
|
-
continue;
|
|
1301
|
-
}
|
|
1302
|
-
console.error('Invalid byte ' + byte.toString(16));
|
|
1303
|
-
// (do not throw error to avoid server/client from crashing due to hack attemps)
|
|
1304
|
-
// throw new Error('Invalid byte ' + byte.toString(16));
|
|
1305
|
-
}
|
|
1306
|
-
it.offset += length;
|
|
1307
|
-
return string;
|
|
1308
|
-
}
|
|
1309
|
-
function int8(bytes, it) {
|
|
1310
|
-
return uint8(bytes, it) << 24 >> 24;
|
|
1311
|
-
}
|
|
1312
|
-
function uint8(bytes, it) {
|
|
1313
|
-
return bytes[it.offset++];
|
|
1314
|
-
}
|
|
1315
|
-
function int16(bytes, it) {
|
|
1316
|
-
return uint16(bytes, it) << 16 >> 16;
|
|
1317
|
-
}
|
|
1318
|
-
function uint16(bytes, it) {
|
|
1319
|
-
return bytes[it.offset++] | bytes[it.offset++] << 8;
|
|
1320
|
-
}
|
|
1321
|
-
function int32(bytes, it) {
|
|
1322
|
-
return bytes[it.offset++] | bytes[it.offset++] << 8 | bytes[it.offset++] << 16 | bytes[it.offset++] << 24;
|
|
1323
|
-
}
|
|
1324
|
-
function uint32(bytes, it) {
|
|
1325
|
-
return int32(bytes, it) >>> 0;
|
|
1326
|
-
}
|
|
1327
|
-
function float32(bytes, it) {
|
|
1328
|
-
return readFloat32(bytes, it);
|
|
1329
|
-
}
|
|
1330
|
-
function float64(bytes, it) {
|
|
1331
|
-
return readFloat64(bytes, it);
|
|
1332
|
-
}
|
|
1333
|
-
function int64(bytes, it) {
|
|
1334
|
-
const low = uint32(bytes, it);
|
|
1335
|
-
const high = int32(bytes, it) * Math.pow(2, 32);
|
|
1336
|
-
return high + low;
|
|
1337
|
-
}
|
|
1338
|
-
function uint64(bytes, it) {
|
|
1339
|
-
const low = uint32(bytes, it);
|
|
1340
|
-
const high = uint32(bytes, it) * Math.pow(2, 32);
|
|
1341
|
-
return high + low;
|
|
1342
|
-
}
|
|
1343
|
-
const _int32 = new Int32Array(2);
|
|
1344
|
-
const _float32 = new Float32Array(_int32.buffer);
|
|
1345
|
-
const _float64 = new Float64Array(_int32.buffer);
|
|
1346
|
-
function readFloat32(bytes, it) {
|
|
1347
|
-
_int32[0] = int32(bytes, it);
|
|
1348
|
-
return _float32[0];
|
|
1349
|
-
}
|
|
1350
|
-
function readFloat64(bytes, it) {
|
|
1351
|
-
_int32[0 ] = int32(bytes, it);
|
|
1352
|
-
_int32[1 ] = int32(bytes, it);
|
|
1353
|
-
return _float64[0];
|
|
1354
|
-
}
|
|
1355
|
-
function boolean(bytes, it) {
|
|
1356
|
-
return uint8(bytes, it) > 0;
|
|
1357
|
-
}
|
|
1358
|
-
function string(bytes, it) {
|
|
1359
|
-
const prefix = bytes[it.offset++];
|
|
1360
|
-
let length;
|
|
1361
|
-
if (prefix < 0xc0) {
|
|
1362
|
-
// fixstr
|
|
1363
|
-
length = prefix & 0x1f;
|
|
1364
|
-
}
|
|
1365
|
-
else if (prefix === 0xd9) {
|
|
1366
|
-
length = uint8(bytes, it);
|
|
1367
|
-
}
|
|
1368
|
-
else if (prefix === 0xda) {
|
|
1369
|
-
length = uint16(bytes, it);
|
|
1370
|
-
}
|
|
1371
|
-
else if (prefix === 0xdb) {
|
|
1372
|
-
length = uint32(bytes, it);
|
|
1373
|
-
}
|
|
1374
|
-
return utf8Read(bytes, it, length);
|
|
1375
|
-
}
|
|
1376
|
-
function stringCheck(bytes, it) {
|
|
1377
|
-
const prefix = bytes[it.offset];
|
|
1378
|
-
return (
|
|
1379
|
-
// fixstr
|
|
1380
|
-
(prefix < 0xc0 && prefix > 0xa0) ||
|
|
1381
|
-
// str 8
|
|
1382
|
-
prefix === 0xd9 ||
|
|
1383
|
-
// str 16
|
|
1384
|
-
prefix === 0xda ||
|
|
1385
|
-
// str 32
|
|
1386
|
-
prefix === 0xdb);
|
|
1387
|
-
}
|
|
1388
|
-
function number(bytes, it) {
|
|
1389
|
-
const prefix = bytes[it.offset++];
|
|
1390
|
-
if (prefix < 0x80) {
|
|
1391
|
-
// positive fixint
|
|
1392
|
-
return prefix;
|
|
1393
|
-
}
|
|
1394
|
-
else if (prefix === 0xca) {
|
|
1395
|
-
// float 32
|
|
1396
|
-
return readFloat32(bytes, it);
|
|
1397
|
-
}
|
|
1398
|
-
else if (prefix === 0xcb) {
|
|
1399
|
-
// float 64
|
|
1400
|
-
return readFloat64(bytes, it);
|
|
1401
|
-
}
|
|
1402
|
-
else if (prefix === 0xcc) {
|
|
1403
|
-
// uint 8
|
|
1404
|
-
return uint8(bytes, it);
|
|
1405
|
-
}
|
|
1406
|
-
else if (prefix === 0xcd) {
|
|
1407
|
-
// uint 16
|
|
1408
|
-
return uint16(bytes, it);
|
|
1409
|
-
}
|
|
1410
|
-
else if (prefix === 0xce) {
|
|
1411
|
-
// uint 32
|
|
1412
|
-
return uint32(bytes, it);
|
|
1413
|
-
}
|
|
1414
|
-
else if (prefix === 0xcf) {
|
|
1415
|
-
// uint 64
|
|
1416
|
-
return uint64(bytes, it);
|
|
1417
|
-
}
|
|
1418
|
-
else if (prefix === 0xd0) {
|
|
1419
|
-
// int 8
|
|
1420
|
-
return int8(bytes, it);
|
|
1421
|
-
}
|
|
1422
|
-
else if (prefix === 0xd1) {
|
|
1423
|
-
// int 16
|
|
1424
|
-
return int16(bytes, it);
|
|
1425
|
-
}
|
|
1426
|
-
else if (prefix === 0xd2) {
|
|
1427
|
-
// int 32
|
|
1428
|
-
return int32(bytes, it);
|
|
1429
|
-
}
|
|
1430
|
-
else if (prefix === 0xd3) {
|
|
1431
|
-
// int 64
|
|
1432
|
-
return int64(bytes, it);
|
|
1433
|
-
}
|
|
1434
|
-
else if (prefix > 0xdf) {
|
|
1435
|
-
// negative fixint
|
|
1436
|
-
return (0xff - prefix + 1) * -1;
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
function numberCheck(bytes, it) {
|
|
1440
|
-
const prefix = bytes[it.offset];
|
|
1441
|
-
// positive fixint - 0x00 - 0x7f
|
|
1442
|
-
// float 32 - 0xca
|
|
1443
|
-
// float 64 - 0xcb
|
|
1444
|
-
// uint 8 - 0xcc
|
|
1445
|
-
// uint 16 - 0xcd
|
|
1446
|
-
// uint 32 - 0xce
|
|
1447
|
-
// uint 64 - 0xcf
|
|
1448
|
-
// int 8 - 0xd0
|
|
1449
|
-
// int 16 - 0xd1
|
|
1450
|
-
// int 32 - 0xd2
|
|
1451
|
-
// int 64 - 0xd3
|
|
1452
|
-
return (prefix < 0x80 ||
|
|
1453
|
-
(prefix >= 0xca && prefix <= 0xd3));
|
|
1454
|
-
}
|
|
1455
|
-
function arrayCheck(bytes, it) {
|
|
1456
|
-
return bytes[it.offset] < 0xa0;
|
|
1457
|
-
// const prefix = bytes[it.offset] ;
|
|
1458
|
-
// if (prefix < 0xa0) {
|
|
1459
|
-
// return prefix;
|
|
1460
|
-
// // array
|
|
1461
|
-
// } else if (prefix === 0xdc) {
|
|
1462
|
-
// it.offset += 2;
|
|
1463
|
-
// } else if (0xdd) {
|
|
1464
|
-
// it.offset += 4;
|
|
1465
|
-
// }
|
|
1466
|
-
// return prefix;
|
|
1467
|
-
}
|
|
1468
|
-
function switchStructureCheck(bytes, it) {
|
|
1469
|
-
return (
|
|
1470
|
-
// previous byte should be `SWITCH_TO_STRUCTURE`
|
|
1471
|
-
bytes[it.offset - 1] === SWITCH_TO_STRUCTURE &&
|
|
1472
|
-
// next byte should be a number
|
|
1473
|
-
(bytes[it.offset] < 0x80 || (bytes[it.offset] >= 0xca && bytes[it.offset] <= 0xd3)));
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
var decode = /*#__PURE__*/Object.freeze({
|
|
1477
|
-
__proto__: null,
|
|
1478
|
-
arrayCheck: arrayCheck,
|
|
1479
|
-
boolean: boolean,
|
|
1480
|
-
float32: float32,
|
|
1481
|
-
float64: float64,
|
|
1482
|
-
int16: int16,
|
|
1483
|
-
int32: int32,
|
|
1484
|
-
int64: int64,
|
|
1485
|
-
int8: int8,
|
|
1486
|
-
number: number,
|
|
1487
|
-
numberCheck: numberCheck,
|
|
1488
|
-
readFloat32: readFloat32,
|
|
1489
|
-
readFloat64: readFloat64,
|
|
1490
|
-
string: string,
|
|
1491
|
-
stringCheck: stringCheck,
|
|
1492
|
-
switchStructureCheck: switchStructureCheck,
|
|
1493
|
-
uint16: uint16,
|
|
1494
|
-
uint32: uint32,
|
|
1495
|
-
uint64: uint64,
|
|
1496
|
-
uint8: uint8,
|
|
1497
|
-
utf8Read: utf8Read
|
|
1498
|
-
});
|
|
1499
|
-
|
|
1500
1486
|
const DEFINITION_MISMATCH = -1;
|
|
1501
1487
|
function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
|
|
1502
1488
|
const $root = decoder.root;
|
|
@@ -1531,7 +1517,7 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1531
1517
|
}
|
|
1532
1518
|
if (operation === exports.OPERATION.DELETE) ;
|
|
1533
1519
|
else if (Schema.is(type)) {
|
|
1534
|
-
const refId = number(bytes, it);
|
|
1520
|
+
const refId = decode.number(bytes, it);
|
|
1535
1521
|
value = $root.refs.get(refId);
|
|
1536
1522
|
if (previousValue) {
|
|
1537
1523
|
const previousRefId = $root.refIds.get(previousValue);
|
|
@@ -1560,7 +1546,7 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1560
1546
|
}
|
|
1561
1547
|
else {
|
|
1562
1548
|
const typeDef = getType(Object.keys(type)[0]);
|
|
1563
|
-
const refId = number(bytes, it);
|
|
1549
|
+
const refId = decode.number(bytes, it);
|
|
1564
1550
|
const valueRef = ($root.refs.has(refId))
|
|
1565
1551
|
? previousValue || $root.refs.get(refId)
|
|
1566
1552
|
: new typeDef.constructor();
|
|
@@ -1639,12 +1625,12 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1639
1625
|
ref.clear();
|
|
1640
1626
|
return;
|
|
1641
1627
|
}
|
|
1642
|
-
const index = number(bytes, it);
|
|
1628
|
+
const index = decode.number(bytes, it);
|
|
1643
1629
|
const type = ref[$childType];
|
|
1644
1630
|
let dynamicIndex;
|
|
1645
1631
|
if ((operation & exports.OPERATION.ADD) === exports.OPERATION.ADD) { // ADD or DELETE_AND_ADD
|
|
1646
1632
|
if (typeof (ref['set']) === "function") {
|
|
1647
|
-
dynamicIndex = string(bytes, it); // MapSchema
|
|
1633
|
+
dynamicIndex = decode.string(bytes, it); // MapSchema
|
|
1648
1634
|
ref['setIndex'](index, dynamicIndex);
|
|
1649
1635
|
}
|
|
1650
1636
|
else {
|
|
@@ -1706,7 +1692,7 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1706
1692
|
}
|
|
1707
1693
|
else if (operation === exports.OPERATION.DELETE_BY_REFID) {
|
|
1708
1694
|
// TODO: refactor here, try to follow same flow as below
|
|
1709
|
-
const refId = number(bytes, it);
|
|
1695
|
+
const refId = decode.number(bytes, it);
|
|
1710
1696
|
const previousValue = decoder.root.refs.get(refId);
|
|
1711
1697
|
index = ref.findIndex((value) => value === previousValue);
|
|
1712
1698
|
ref[$deleteByIndex](index);
|
|
@@ -1722,7 +1708,7 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1722
1708
|
return;
|
|
1723
1709
|
}
|
|
1724
1710
|
else if (operation === exports.OPERATION.ADD_BY_REFID) {
|
|
1725
|
-
const refId = number(bytes, it);
|
|
1711
|
+
const refId = decode.number(bytes, it);
|
|
1726
1712
|
const itemByRefId = decoder.root.refs.get(refId);
|
|
1727
1713
|
// use existing index, or push new value
|
|
1728
1714
|
index = (itemByRefId)
|
|
@@ -1730,7 +1716,7 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1730
1716
|
: ref.length;
|
|
1731
1717
|
}
|
|
1732
1718
|
else {
|
|
1733
|
-
index = number(bytes, it);
|
|
1719
|
+
index = decode.number(bytes, it);
|
|
1734
1720
|
}
|
|
1735
1721
|
const type = ref[$childType];
|
|
1736
1722
|
let dynamicIndex = index;
|
|
@@ -1777,6 +1763,10 @@ function assertType(value, type, klass, field) {
|
|
|
1777
1763
|
console.log(`trying to encode "NaN" in ${klass.constructor.name}#${field}`);
|
|
1778
1764
|
}
|
|
1779
1765
|
break;
|
|
1766
|
+
case "bigint64":
|
|
1767
|
+
case "biguint64":
|
|
1768
|
+
typeofTarget = "bigint";
|
|
1769
|
+
break;
|
|
1780
1770
|
case "string":
|
|
1781
1771
|
typeofTarget = "string";
|
|
1782
1772
|
allowNull = true;
|
|
@@ -1784,6 +1774,10 @@ function assertType(value, type, klass, field) {
|
|
|
1784
1774
|
case "boolean":
|
|
1785
1775
|
// boolean is always encoded as true/false based on truthiness
|
|
1786
1776
|
return;
|
|
1777
|
+
default:
|
|
1778
|
+
// skip assertion for custom types
|
|
1779
|
+
// TODO: allow custom types to define their own assertions
|
|
1780
|
+
return;
|
|
1787
1781
|
}
|
|
1788
1782
|
if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) {
|
|
1789
1783
|
let foundValue = `'${JSON.stringify(value)}'${(value && value.constructor && ` (${value.constructor.name})`) || ''}`;
|
|
@@ -3788,7 +3782,7 @@ class Encoder {
|
|
|
3788
3782
|
// (unless it "hasView", which will need to revisit the root)
|
|
3789
3783
|
if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
|
|
3790
3784
|
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3791
|
-
number
|
|
3785
|
+
encode.number(buffer, changeTree.refId, it);
|
|
3792
3786
|
}
|
|
3793
3787
|
for (let j = 0, numChanges = operations.operations.length; j < numChanges; j++) {
|
|
3794
3788
|
const fieldIndex = operations.operations[j];
|
|
@@ -3917,7 +3911,7 @@ class Encoder {
|
|
|
3917
3911
|
const encoder = ctor[$encoder];
|
|
3918
3912
|
const metadata = ctor[Symbol.metadata];
|
|
3919
3913
|
bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3920
|
-
number
|
|
3914
|
+
encode.number(bytes, changeTree.refId, it);
|
|
3921
3915
|
const keys = Object.keys(changes);
|
|
3922
3916
|
for (let i = 0, numChanges = keys.length; i < numChanges; i++) {
|
|
3923
3917
|
const key = keys[i];
|
|
@@ -3983,7 +3977,7 @@ class Encoder {
|
|
|
3983
3977
|
}
|
|
3984
3978
|
if (baseTypeId !== targetTypeId) {
|
|
3985
3979
|
bytes[it.offset++] = TYPE_ID & 255;
|
|
3986
|
-
number
|
|
3980
|
+
encode.number(bytes, targetTypeId, it);
|
|
3987
3981
|
}
|
|
3988
3982
|
}
|
|
3989
3983
|
get hasChanges() {
|
|
@@ -4143,7 +4137,7 @@ class Decoder {
|
|
|
4143
4137
|
//
|
|
4144
4138
|
if (bytes[it.offset] == SWITCH_TO_STRUCTURE) {
|
|
4145
4139
|
it.offset++;
|
|
4146
|
-
this.currentRefId = number(bytes, it);
|
|
4140
|
+
this.currentRefId = decode.number(bytes, it);
|
|
4147
4141
|
const nextRef = $root.refs.get(this.currentRefId);
|
|
4148
4142
|
//
|
|
4149
4143
|
// Trying to access a reference that haven't been decoded yet.
|
|
@@ -4165,9 +4159,9 @@ class Decoder {
|
|
|
4165
4159
|
//
|
|
4166
4160
|
const nextIterator = { offset: it.offset };
|
|
4167
4161
|
while (it.offset < totalBytes) {
|
|
4168
|
-
if (
|
|
4162
|
+
if (bytes[it.offset] === SWITCH_TO_STRUCTURE) {
|
|
4169
4163
|
nextIterator.offset = it.offset + 1;
|
|
4170
|
-
if ($root.refs.has(number(bytes, nextIterator))) {
|
|
4164
|
+
if ($root.refs.has(decode.number(bytes, nextIterator))) {
|
|
4171
4165
|
break;
|
|
4172
4166
|
}
|
|
4173
4167
|
}
|
|
@@ -4188,7 +4182,7 @@ class Decoder {
|
|
|
4188
4182
|
let type;
|
|
4189
4183
|
if (bytes[it.offset] === TYPE_ID) {
|
|
4190
4184
|
it.offset++;
|
|
4191
|
-
const type_id = number(bytes, it);
|
|
4185
|
+
const type_id = decode.number(bytes, it);
|
|
4192
4186
|
type = this.context.get(type_id);
|
|
4193
4187
|
}
|
|
4194
4188
|
return type || defaultType;
|
|
@@ -4867,6 +4861,7 @@ exports.TypeContext = TypeContext;
|
|
|
4867
4861
|
exports.decode = decode;
|
|
4868
4862
|
exports.decodeKeyValueOperation = decodeKeyValueOperation;
|
|
4869
4863
|
exports.decodeSchemaOperation = decodeSchemaOperation;
|
|
4864
|
+
exports.defineCustomTypes = defineCustomTypes;
|
|
4870
4865
|
exports.defineTypes = defineTypes;
|
|
4871
4866
|
exports.deprecated = deprecated;
|
|
4872
4867
|
exports.dumpChanges = dumpChanges;
|