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