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