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