@colyseus/schema 3.0.76 → 4.0.1

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 (160) hide show
  1. package/build/cjs/index.js +781 -434
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/index.mjs +779 -435
  4. package/build/esm/index.mjs.map +1 -1
  5. package/build/umd/index.js +781 -434
  6. package/lib/Metadata.js +1 -5
  7. package/lib/Metadata.js.map +1 -1
  8. package/lib/Reflection.d.ts +50 -17
  9. package/lib/Reflection.js +151 -202
  10. package/lib/Reflection.js.map +1 -1
  11. package/lib/Schema.d.ts +13 -1
  12. package/lib/Schema.js +73 -9
  13. package/lib/Schema.js.map +1 -1
  14. package/lib/annotations.d.ts +6 -1
  15. package/lib/annotations.js +8 -34
  16. package/lib/annotations.js.map +1 -1
  17. package/lib/bench_encode.js +34 -1
  18. package/lib/bench_encode.js.map +1 -1
  19. package/lib/codegen/api.js +35 -2
  20. package/lib/codegen/api.js.map +1 -1
  21. package/lib/codegen/cli.js +4 -1
  22. package/lib/codegen/cli.js.map +1 -1
  23. package/lib/codegen/parser.js +35 -2
  24. package/lib/codegen/parser.js.map +1 -1
  25. package/lib/codegen/types.js +34 -1
  26. package/lib/codegen/types.js.map +1 -1
  27. package/lib/decoder/DecodeOperation.d.ts +2 -2
  28. package/lib/decoder/DecodeOperation.js +3 -3
  29. package/lib/decoder/DecodeOperation.js.map +1 -1
  30. package/lib/decoder/Decoder.d.ts +3 -3
  31. package/lib/decoder/Decoder.js +2 -2
  32. package/lib/decoder/Decoder.js.map +1 -1
  33. package/lib/decoder/ReferenceTracker.d.ts +0 -1
  34. package/lib/decoder/ReferenceTracker.js +9 -7
  35. package/lib/decoder/ReferenceTracker.js.map +1 -1
  36. package/lib/decoder/strategy/Callbacks.d.ts +154 -0
  37. package/lib/decoder/strategy/Callbacks.js +340 -0
  38. package/lib/decoder/strategy/Callbacks.js.map +1 -0
  39. package/lib/decoder/strategy/{StateCallbacks.d.ts → getDecoderStateCallbacks.d.ts} +6 -0
  40. package/lib/decoder/strategy/{StateCallbacks.js → getDecoderStateCallbacks.js} +17 -10
  41. package/lib/decoder/strategy/getDecoderStateCallbacks.js.map +1 -0
  42. package/lib/encoder/ChangeTree.d.ts +2 -2
  43. package/lib/encoder/ChangeTree.js.map +1 -1
  44. package/lib/encoder/EncodeOperation.d.ts +2 -2
  45. package/lib/encoder/EncodeOperation.js +3 -3
  46. package/lib/encoder/EncodeOperation.js.map +1 -1
  47. package/lib/encoder/Encoder.d.ts +6 -6
  48. package/lib/encoder/Encoder.js +19 -18
  49. package/lib/encoder/Encoder.js.map +1 -1
  50. package/lib/encoder/Root.js +17 -14
  51. package/lib/encoder/Root.js.map +1 -1
  52. package/lib/encoder/StateView.js +13 -12
  53. package/lib/encoder/StateView.js.map +1 -1
  54. package/lib/encoding/decode.d.ts +2 -2
  55. package/lib/encoding/encode.d.ts +3 -1
  56. package/lib/encoding/encode.js.map +1 -1
  57. package/lib/index.d.ts +3 -2
  58. package/lib/index.js +7 -3
  59. package/lib/index.js.map +1 -1
  60. package/lib/types/HelperTypes.d.ts +7 -14
  61. package/lib/types/HelperTypes.js.map +1 -1
  62. package/lib/types/custom/ArraySchema.d.ts +2 -1
  63. package/lib/types/custom/ArraySchema.js.map +1 -1
  64. package/lib/types/custom/CollectionSchema.d.ts +2 -1
  65. package/lib/types/custom/CollectionSchema.js.map +1 -1
  66. package/lib/types/custom/MapSchema.d.ts +3 -2
  67. package/lib/types/custom/MapSchema.js.map +1 -1
  68. package/lib/types/custom/SetSchema.d.ts +2 -1
  69. package/lib/types/custom/SetSchema.js.map +1 -1
  70. package/lib/types/symbols.d.ts +1 -0
  71. package/lib/types/symbols.js +2 -1
  72. package/lib/types/symbols.js.map +1 -1
  73. package/lib/utils.js +1 -1
  74. package/lib/utils.js.map +1 -1
  75. package/package.json +12 -16
  76. package/src/Metadata.ts +1 -5
  77. package/src/Reflection.ts +185 -174
  78. package/src/Schema.ts +81 -13
  79. package/src/annotations.ts +14 -40
  80. package/src/codegen/parser.ts +1 -1
  81. package/src/decoder/DecodeOperation.ts +9 -9
  82. package/src/decoder/Decoder.ts +6 -6
  83. package/src/decoder/ReferenceTracker.ts +10 -8
  84. package/src/decoder/strategy/Callbacks.ts +547 -0
  85. package/src/decoder/strategy/{StateCallbacks.ts → getDecoderStateCallbacks.ts} +17 -11
  86. package/src/encoder/ChangeTree.ts +4 -7
  87. package/src/encoder/EncodeOperation.ts +9 -9
  88. package/src/encoder/Encoder.ts +47 -22
  89. package/src/encoder/Root.ts +20 -15
  90. package/src/encoder/StateView.ts +15 -13
  91. package/src/encoding/encode.ts +1 -1
  92. package/src/index.ts +3 -2
  93. package/src/types/HelperTypes.ts +13 -11
  94. package/src/types/custom/ArraySchema.ts +2 -1
  95. package/src/types/custom/CollectionSchema.ts +4 -2
  96. package/src/types/custom/MapSchema.ts +4 -2
  97. package/src/types/custom/SetSchema.ts +3 -1
  98. package/src/types/symbols.ts +1 -0
  99. package/src/utils.ts +2 -2
  100. package/lib/Decoder.d.ts +0 -16
  101. package/lib/Decoder.js +0 -182
  102. package/lib/Decoder.js.map +0 -1
  103. package/lib/Encoder.d.ts +0 -13
  104. package/lib/Encoder.js +0 -79
  105. package/lib/Encoder.js.map +0 -1
  106. package/lib/changes/ChangeSet.d.ts +0 -12
  107. package/lib/changes/ChangeSet.js +0 -35
  108. package/lib/changes/ChangeSet.js.map +0 -1
  109. package/lib/changes/ChangeTree.d.ts +0 -53
  110. package/lib/changes/ChangeTree.js +0 -202
  111. package/lib/changes/ChangeTree.js.map +0 -1
  112. package/lib/changes/DecodeOperation.d.ts +0 -15
  113. package/lib/changes/DecodeOperation.js +0 -186
  114. package/lib/changes/DecodeOperation.js.map +0 -1
  115. package/lib/changes/EncodeOperation.d.ts +0 -18
  116. package/lib/changes/EncodeOperation.js +0 -130
  117. package/lib/changes/EncodeOperation.js.map +0 -1
  118. package/lib/changes/ReferenceTracker.d.ts +0 -14
  119. package/lib/changes/ReferenceTracker.js +0 -83
  120. package/lib/changes/ReferenceTracker.js.map +0 -1
  121. package/lib/changes/consts.d.ts +0 -14
  122. package/lib/changes/consts.js +0 -18
  123. package/lib/changes/consts.js.map +0 -1
  124. package/lib/decoder/strategy/StateCallbacks.js.map +0 -1
  125. package/lib/decoding/decode.d.ts +0 -48
  126. package/lib/decoding/decode.js +0 -267
  127. package/lib/decoding/decode.js.map +0 -1
  128. package/lib/ecs.d.ts +0 -11
  129. package/lib/ecs.js +0 -160
  130. package/lib/ecs.js.map +0 -1
  131. package/lib/filters/index.d.ts +0 -8
  132. package/lib/filters/index.js +0 -24
  133. package/lib/filters/index.js.map +0 -1
  134. package/lib/spec.d.ts +0 -13
  135. package/lib/spec.js +0 -42
  136. package/lib/spec.js.map +0 -1
  137. package/lib/types/ArraySchema.d.ts +0 -238
  138. package/lib/types/ArraySchema.js +0 -555
  139. package/lib/types/ArraySchema.js.map +0 -1
  140. package/lib/types/CollectionSchema.d.ts +0 -35
  141. package/lib/types/CollectionSchema.js +0 -150
  142. package/lib/types/CollectionSchema.js.map +0 -1
  143. package/lib/types/MapSchema.d.ts +0 -38
  144. package/lib/types/MapSchema.js +0 -215
  145. package/lib/types/MapSchema.js.map +0 -1
  146. package/lib/types/SetSchema.d.ts +0 -32
  147. package/lib/types/SetSchema.js +0 -162
  148. package/lib/types/SetSchema.js.map +0 -1
  149. package/lib/types/typeRegistry.d.ts +0 -5
  150. package/lib/types/typeRegistry.js +0 -13
  151. package/lib/types/typeRegistry.js.map +0 -1
  152. package/lib/usage.d.ts +0 -1
  153. package/lib/usage.js +0 -22
  154. package/lib/usage.js.map +0 -1
  155. package/lib/v3.d.ts +0 -1
  156. package/lib/v3.js +0 -427
  157. package/lib/v3.js.map +0 -1
  158. package/lib/v3_experiment.d.ts +0 -1
  159. package/lib/v3_experiment.js +0 -407
  160. package/lib/v3_experiment.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { OPERATION } from "../encoding/spec";
2
- import { $changes, $childType, $getByIndex } from "../types/symbols";
2
+ import { $changes, $childType, $getByIndex, $refId } from "../types/symbols";
3
3
 
4
4
  import { encode } from "../encoding/encode";
5
5
 
@@ -13,7 +13,7 @@ import type { Metadata } from "../Metadata";
13
13
 
14
14
  export type EncodeOperation<T extends Ref = any> = (
15
15
  encoder: Encoder,
16
- bytes: Buffer,
16
+ bytes: Uint8Array,
17
17
  changeTree: ChangeTree<T>,
18
18
  index: number,
19
19
  operation: OPERATION,
@@ -25,7 +25,7 @@ export type EncodeOperation<T extends Ref = any> = (
25
25
 
26
26
  export function encodeValue(
27
27
  encoder: Encoder,
28
- bytes: Buffer,
28
+ bytes: Uint8Array,
29
29
  type: any,
30
30
  value: any,
31
31
  operation: OPERATION,
@@ -39,7 +39,7 @@ export function encodeValue(
39
39
  // Encode refId for this instance.
40
40
  // The actual instance is going to be encoded on next `changeTree` iteration.
41
41
  //
42
- encode.number(bytes, value[$changes].refId, it);
42
+ encode.number(bytes, value[$refId], it);
43
43
 
44
44
  // Try to encode inherited TYPE_ID if it's an ADD operation.
45
45
  if ((operation & OPERATION.ADD) === OPERATION.ADD) {
@@ -51,7 +51,7 @@ export function encodeValue(
51
51
  // Encode refId for this instance.
52
52
  // The actual instance is going to be encoded on next `changeTree` iteration.
53
53
  //
54
- encode.number(bytes, value[$changes].refId, it);
54
+ encode.number(bytes, value[$refId], it);
55
55
  }
56
56
  }
57
57
 
@@ -61,7 +61,7 @@ export function encodeValue(
61
61
  */
62
62
  export const encodeSchemaOperation: EncodeOperation = function <T extends Schema> (
63
63
  encoder: Encoder,
64
- bytes: Buffer,
64
+ bytes: Uint8Array,
65
65
  changeTree: ChangeTree<T>,
66
66
  index: number,
67
67
  operation: OPERATION,
@@ -98,7 +98,7 @@ export const encodeSchemaOperation: EncodeOperation = function <T extends Schema
98
98
  */
99
99
  export const encodeKeyValueOperation: EncodeOperation = function (
100
100
  encoder: Encoder,
101
- bytes: Buffer,
101
+ bytes: Uint8Array,
102
102
  changeTree: ChangeTree,
103
103
  index: number,
104
104
  operation: OPERATION,
@@ -163,7 +163,7 @@ export const encodeKeyValueOperation: EncodeOperation = function (
163
163
  */
164
164
  export const encodeArray: EncodeOperation = function (
165
165
  encoder: Encoder,
166
- bytes: Buffer,
166
+ bytes: Uint8Array,
167
167
  changeTree: ChangeTree<ArraySchema>,
168
168
  field: number,
169
169
  operation: OPERATION,
@@ -182,7 +182,7 @@ export const encodeArray: EncodeOperation = function (
182
182
  // Skip encoding if item is undefined (e.g. when clear() is called)
183
183
  if (!item) { return; }
184
184
 
185
- refOrIndex = item[$changes].refId;
185
+ refOrIndex = item[$refId];
186
186
 
187
187
  if (operation === OPERATION.DELETE) {
188
188
  operation = OPERATION.DELETE_BY_REFID;
@@ -1,6 +1,6 @@
1
1
  import type { Schema } from "../Schema";
2
2
  import { TypeContext } from "../types/TypeContext";
3
- import { $changes, $encoder, $filter, $getByIndex } from "../types/symbols";
3
+ import { $changes, $encoder, $filter, $getByIndex, $refId } from "../types/symbols";
4
4
 
5
5
  import { encode } from "../encoding/encode";
6
6
  import type { Iterator } from "../encoding/decode";
@@ -9,13 +9,19 @@ import { OPERATION, SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';
9
9
  import { Root } from "./Root";
10
10
 
11
11
  import type { StateView } from "./StateView";
12
- import type { Metadata } from "../Metadata";
13
12
  import type { ChangeSetName, ChangeTree, ChangeTreeList, ChangeTreeNode } from "./ChangeTree";
14
13
  import { createChangeTreeList } from "./ChangeTree";
15
14
 
15
+ function concatBytes(a: Uint8Array, b: Uint8Array): Uint8Array {
16
+ const result = new Uint8Array(a.length + b.length);
17
+ result.set(a, 0);
18
+ result.set(b, a.length);
19
+ return result;
20
+ }
21
+
16
22
  export class Encoder<T extends Schema = any> {
17
- static BUFFER_SIZE = (typeof(Buffer) !== "undefined") && Buffer.poolSize || 8 * 1024; // 8KB
18
- sharedBuffer = Buffer.allocUnsafe(Encoder.BUFFER_SIZE);
23
+ static BUFFER_SIZE = 8 * 1024; // 8KB
24
+ sharedBuffer: Uint8Array = new Uint8Array(Encoder.BUFFER_SIZE);
19
25
 
20
26
  context: TypeContext;
21
27
  state: T;
@@ -48,11 +54,11 @@ export class Encoder<T extends Schema = any> {
48
54
  encode(
49
55
  it: Iterator = { offset: 0 },
50
56
  view?: StateView,
51
- buffer = this.sharedBuffer,
57
+ buffer: Uint8Array = this.sharedBuffer,
52
58
  changeSetName: ChangeSetName = "changes",
53
59
  isEncodeAll = changeSetName === "allChanges",
54
60
  initialOffset = it.offset // cache current offset in case we need to resize the buffer
55
- ): Buffer {
61
+ ): Uint8Array {
56
62
  const hasView = (view !== undefined);
57
63
  const rootChangeTree = this.state[$changes];
58
64
 
@@ -63,7 +69,7 @@ export class Encoder<T extends Schema = any> {
63
69
 
64
70
  if (hasView) {
65
71
  if (!view.isChangeTreeVisible(changeTree)) {
66
- // console.log("MARK AS INVISIBLE:", { ref: changeTree.ref.constructor.name, refId: changeTree.refId, raw: changeTree.ref.toJSON() });
72
+ // console.log("MARK AS INVISIBLE:", { ref: changeTree.ref.constructor.name, refId: changeTree.ref[$refId], raw: changeTree.ref.toJSON() });
67
73
  view.invisible.add(changeTree);
68
74
  continue; // skip this change tree
69
75
  }
@@ -86,7 +92,7 @@ export class Encoder<T extends Schema = any> {
86
92
  // (unless it "hasView", which will need to revisit the root)
87
93
  if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
88
94
  buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
89
- encode.number(buffer, changeTree.refId, it);
95
+ encode.number(buffer, ref[$refId], it);
90
96
  }
91
97
 
92
98
  for (let j = 0; j < numChanges; j++) {
@@ -121,10 +127,9 @@ export class Encoder<T extends Schema = any> {
121
127
  }
122
128
 
123
129
  if (it.offset > buffer.byteLength) {
124
- // we can assume that n + 1 poolSize will suffice given that we are likely done with encoding at this point
125
- // multiples of poolSize are faster to allocate than arbitrary sizes
126
- // if we are on an older platform that doesn't implement pooling use 8kb as poolSize (that's the default for node)
127
- const newSize = Math.ceil(it.offset / (Buffer.poolSize ?? 8 * 1024)) * (Buffer.poolSize ?? 8 * 1024);
130
+ // we can assume that n + 1 BUFFER_SIZE will suffice given that we are likely done with encoding at this point
131
+ // multiples of BUFFER_SIZE are faster to allocate than arbitrary sizes
132
+ const newSize = Math.ceil(it.offset / Encoder.BUFFER_SIZE) * Encoder.BUFFER_SIZE;
128
133
 
129
134
  console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
130
135
 
@@ -136,7 +141,9 @@ export class Encoder<T extends Schema = any> {
136
141
  // resize buffer and re-encode (TODO: can we avoid re-encoding here?)
137
142
  // -> No we probably can't unless we catch the need for resize before encoding which is likely more computationally expensive than resizing on demand
138
143
  //
139
- buffer = Buffer.alloc(newSize, buffer); // fill with buffer here to memcpy previous encoding steps beyond the initialOffset
144
+ const newBuffer = new Uint8Array(newSize);
145
+ newBuffer.set(buffer); // copy previous encoding steps beyond the initialOffset
146
+ buffer = newBuffer;
140
147
 
141
148
  // assign resized buffer to local sharedBuffer
142
149
  if (buffer === this.sharedBuffer) {
@@ -151,23 +158,36 @@ export class Encoder<T extends Schema = any> {
151
158
  }
152
159
  }
153
160
 
154
- encodeAll(it: Iterator = { offset: 0 }, buffer: Buffer = this.sharedBuffer) {
161
+ encodeAll(
162
+ it: Iterator = { offset: 0 },
163
+ buffer: Uint8Array = this.sharedBuffer
164
+ ) {
155
165
  return this.encode(it, undefined, buffer, "allChanges", true);
156
166
  }
157
167
 
158
- encodeAllView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {
168
+ encodeAllView(
169
+ view: StateView,
170
+ sharedOffset: number,
171
+ it: Iterator,
172
+ bytes: Uint8Array = this.sharedBuffer
173
+ ) {
159
174
  const viewOffset = it.offset;
160
175
 
161
176
  // try to encode "filtered" changes
162
177
  this.encode(it, view, bytes, "allFilteredChanges", true, viewOffset);
163
178
 
164
- return Buffer.concat([
179
+ return concatBytes(
165
180
  bytes.subarray(0, sharedOffset),
166
181
  bytes.subarray(viewOffset, it.offset)
167
- ]);
182
+ );
168
183
  }
169
184
 
170
- encodeView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {
185
+ encodeView(
186
+ view: StateView,
187
+ sharedOffset: number,
188
+ it: Iterator,
189
+ bytes: Uint8Array = this.sharedBuffer
190
+ ) {
171
191
  const viewOffset = it.offset;
172
192
 
173
193
  // encode visibility changes (add/remove for this view)
@@ -195,7 +215,7 @@ export class Encoder<T extends Schema = any> {
195
215
  const metadata = ctor[Symbol.metadata];
196
216
 
197
217
  bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
198
- encode.number(bytes, changeTree.refId, it);
218
+ encode.number(bytes, ref[$refId], it);
199
219
 
200
220
  for (let i = 0, numChanges = keys.length; i < numChanges; i++) {
201
221
  const index = Number(keys[i]);
@@ -219,10 +239,10 @@ export class Encoder<T extends Schema = any> {
219
239
  // try to encode "filtered" changes
220
240
  this.encode(it, view, bytes, "filteredChanges", false, viewOffset);
221
241
 
222
- return Buffer.concat([
242
+ return concatBytes(
223
243
  bytes.subarray(0, sharedOffset),
224
244
  bytes.subarray(viewOffset, it.offset)
225
- ]);
245
+ );
226
246
  }
227
247
 
228
248
  discardChanges() {
@@ -243,7 +263,12 @@ export class Encoder<T extends Schema = any> {
243
263
  this.root.filteredChanges = createChangeTreeList();
244
264
  }
245
265
 
246
- tryEncodeTypeId (bytes: Buffer, baseType: typeof Schema, targetType: typeof Schema, it: Iterator) {
266
+ tryEncodeTypeId(
267
+ bytes: Uint8Array,
268
+ baseType: typeof Schema,
269
+ targetType: typeof Schema,
270
+ it: Iterator
271
+ ) {
247
272
  const baseTypeId = this.context.getTypeId(baseType);
248
273
  const targetTypeId = this.context.getTypeId(targetType);
249
274
 
@@ -1,7 +1,7 @@
1
1
  import { OPERATION } from "../encoding/spec";
2
2
  import { TypeContext } from "../types/TypeContext";
3
3
  import { ChangeTree, setOperationAtIndex, ChangeTreeList, createChangeTreeList, ChangeSetName, type ChangeTreeNode } from "./ChangeTree";
4
- import { $changes } from "../types/symbols";
4
+ import { $changes, $refId } from "../types/symbols";
5
5
 
6
6
  export class Root {
7
7
  protected nextUniqueId: number = 0;
@@ -24,15 +24,19 @@ export class Root {
24
24
  }
25
25
 
26
26
  add(changeTree: ChangeTree) {
27
- // Assign unique `refId` to changeTree if it doesn't have one yet.
28
- if (changeTree.refId === undefined) {
29
- changeTree.refId = this.getNextUniqueId();
27
+ const ref = changeTree.ref;
28
+
29
+ // Assign unique `refId` to ref if it doesn't have one yet.
30
+ if (ref[$refId] === undefined) {
31
+ ref[$refId] = this.getNextUniqueId();
30
32
  }
31
33
 
32
- const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);
33
- if (isNewChangeTree) { this.changeTrees[changeTree.refId] = changeTree; }
34
+ const refId = ref[$refId];
35
+
36
+ const isNewChangeTree = (this.changeTrees[refId] === undefined);
37
+ if (isNewChangeTree) { this.changeTrees[refId] = changeTree; }
34
38
 
35
- const previousRefCount = this.refCount[changeTree.refId];
39
+ const previousRefCount = this.refCount[refId];
36
40
  if (previousRefCount === 0) {
37
41
  //
38
42
  // When a ChangeTree is re-added, it means that it was previously removed.
@@ -46,24 +50,25 @@ export class Root {
46
50
  }
47
51
  }
48
52
 
49
- this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;
53
+ this.refCount[refId] = (previousRefCount || 0) + 1;
50
54
 
51
- // console.log("ADD", { refId: changeTree.refId, ref: changeTree.ref.constructor.name, refCount: this.refCount[changeTree.refId], isNewChangeTree });
55
+ // console.log("ADD", { refId, ref: ref.constructor.name, refCount: this.refCount[refId], isNewChangeTree });
52
56
 
53
57
  return isNewChangeTree;
54
58
  }
55
59
 
56
60
  remove(changeTree: ChangeTree) {
57
- const refCount = (this.refCount[changeTree.refId]) - 1;
61
+ const refId = changeTree.ref[$refId];
62
+ const refCount = (this.refCount[refId]) - 1;
58
63
 
59
- // console.log("REMOVE", { refId: changeTree.refId, ref: changeTree.ref.constructor.name, refCount, needRemove: refCount <= 0 });
64
+ // console.log("REMOVE", { refId, ref: changeTree.ref.constructor.name, refCount, needRemove: refCount <= 0 });
60
65
 
61
66
  if (refCount <= 0) {
62
67
  //
63
68
  // Only remove "root" reference if it's the last reference
64
69
  //
65
70
  changeTree.root = undefined;
66
- delete this.changeTrees[changeTree.refId];
71
+ delete this.changeTrees[refId];
67
72
 
68
73
  this.removeChangeFromChangeSet("allChanges", changeTree);
69
74
  this.removeChangeFromChangeSet("changes", changeTree);
@@ -73,13 +78,13 @@ export class Root {
73
78
  this.removeChangeFromChangeSet("filteredChanges", changeTree);
74
79
  }
75
80
 
76
- this.refCount[changeTree.refId] = 0;
81
+ this.refCount[refId] = 0;
77
82
 
78
83
  changeTree.forEachChild((child, _) => {
79
84
  if (child.removeParent(changeTree.ref)) {
80
85
  if ((
81
86
  child.parentChain === undefined || // no parent, remove it
82
- (child.parentChain && this.refCount[child.refId] > 0) // parent is still in use, but has more than one reference, remove it
87
+ (child.parentChain && this.refCount[child.ref[$refId]] > 0) // parent is still in use, but has more than one reference, remove it
83
88
  )) {
84
89
  this.remove(child);
85
90
 
@@ -91,7 +96,7 @@ export class Root {
91
96
  });
92
97
 
93
98
  } else {
94
- this.refCount[changeTree.refId] = refCount;
99
+ this.refCount[refId] = refCount;
95
100
 
96
101
  //
97
102
  // When losing a reference to an instance, it is best to move the
@@ -1,5 +1,5 @@
1
1
  import { ChangeTree, IndexedOperations, Ref } from "./ChangeTree";
2
- import { $changes, $fieldIndexesByViewTag, $viewFieldIndexes } from "../types/symbols";
2
+ import { $changes, $fieldIndexesByViewTag, $refId, $viewFieldIndexes } from "../types/symbols";
3
3
  import { DEFAULT_VIEW_TAG } from "../annotations";
4
4
  import { OPERATION } from "../encoding/spec";
5
5
  import { Metadata } from "../Metadata";
@@ -52,7 +52,7 @@ export class StateView {
52
52
 
53
53
  } else if (
54
54
  !parentChangeTree &&
55
- changeTree.refId !== 0 // allow root object
55
+ obj[$refId] !== 0 // allow root object
56
56
  ) {
57
57
  /**
58
58
  * TODO: can we avoid this?
@@ -82,11 +82,11 @@ export class StateView {
82
82
  this.addParentOf(changeTree, tag);
83
83
  }
84
84
 
85
- let changes = this.changes.get(changeTree.refId);
85
+ let changes = this.changes.get(obj[$refId]);
86
86
  if (changes === undefined) {
87
87
  changes = {};
88
88
  // FIXME / OPTIMIZE: do not add if no changes are needed
89
- this.changes.set(changeTree.refId, changes);
89
+ this.changes.set(obj[$refId], changes);
90
90
  }
91
91
 
92
92
  let isChildAdded = false;
@@ -182,10 +182,10 @@ export class StateView {
182
182
 
183
183
  // add parent's tag properties
184
184
  if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
185
- let changes = this.changes.get(changeTree.refId);
185
+ let changes = this.changes.get(changeTree.ref[$refId]);
186
186
  if (changes === undefined) {
187
187
  changes = {};
188
- this.changes.set(changeTree.refId, changes);
188
+ this.changes.set(changeTree.ref[$refId], changes);
189
189
  }
190
190
 
191
191
  if (!this.tags) {
@@ -227,28 +227,30 @@ export class StateView {
227
227
  const ref = changeTree.ref;
228
228
  const metadata: Metadata = ref.constructor[Symbol.metadata]; // ArraySchema/MapSchema do not have metadata
229
229
 
230
- let changes = this.changes.get(changeTree.refId);
230
+ const refId = ref[$refId];
231
+
232
+ let changes = this.changes.get(refId);
231
233
  if (changes === undefined) {
232
234
  changes = {};
233
- this.changes.set(changeTree.refId, changes);
235
+ this.changes.set(refId, changes);
234
236
  }
235
237
 
236
238
  if (tag === DEFAULT_VIEW_TAG) {
237
239
  // parent is collection (Map/Array)
238
240
  const parent = changeTree.parent;
239
241
  if (parent && !Metadata.isValidInstance(parent) && changeTree.isFiltered) {
240
- const parentChangeTree = parent[$changes];
241
- let changes = this.changes.get(parentChangeTree.refId);
242
+ const parentRefId = parent[$refId];
243
+ let changes = this.changes.get(parentRefId);
242
244
  if (changes === undefined) {
243
245
  changes = {};
244
- this.changes.set(parentChangeTree.refId, changes);
246
+ this.changes.set(parentRefId, changes);
245
247
 
246
248
  } else if (changes[changeTree.parentIndex] === OPERATION.ADD) {
247
249
  //
248
250
  // SAME PATCH ADD + REMOVE:
249
251
  // The 'changes' of deleted structure should be ignored.
250
252
  //
251
- this.changes.delete(changeTree.refId);
253
+ this.changes.delete(refId);
252
254
  }
253
255
 
254
256
  // DELETE / DELETE BY REF ID
@@ -322,7 +324,7 @@ export class StateView {
322
324
 
323
325
  // console.log("CHECK AGAINST PARENT...", {
324
326
  // ref: changeTree.ref.constructor.name,
325
- // refId: changeTree.refId,
327
+ // refId: changeTree.ref[$refId],
326
328
  // parent: changeTree.parent.constructor.name,
327
329
  // });
328
330
 
@@ -26,7 +26,7 @@
26
26
  import type { TextEncoder } from "util";
27
27
  import type { Iterator } from "./decode";
28
28
 
29
- export type BufferLike = number[] | ArrayBufferLike | Buffer;
29
+ export type BufferLike = ArrayLike<number> & { [index: number]: number };
30
30
 
31
31
  /**
32
32
  * msgpack implementation highly based on notepack.io
package/src/index.ts CHANGED
@@ -26,7 +26,7 @@ registerType("collection", { constructor: CollectionSchema, });
26
26
  export { dumpChanges } from "./utils";
27
27
 
28
28
  // Encoder / Decoder
29
- export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType } from "./types/symbols";
29
+ export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType, $refId } from "./types/symbols";
30
30
  export { encode } from "./encoding/encode";
31
31
  export { decode, type Iterator } from "./encoding/decode";
32
32
 
@@ -62,7 +62,8 @@ export { TypeContext } from "./types/TypeContext";
62
62
  // Helper types for type inference
63
63
  export type { InferValueType, InferSchemaInstanceType, AssignableProps } from "./types/HelperTypes";
64
64
 
65
- export { getDecoderStateCallbacks, CallbackProxy, SchemaCallback, CollectionCallback, SchemaCallbackProxy } from "./decoder/strategy/StateCallbacks";
65
+ export { getDecoderStateCallbacks, CallbackProxy, SchemaCallback, CollectionCallback, SchemaCallbackProxy } from "./decoder/strategy/getDecoderStateCallbacks";
66
+ export { Callbacks, StateCallbackStrategy } from "./decoder/strategy/Callbacks";
66
67
  export { getRawChangesCallback } from "./decoder/strategy/RawChanges";
67
68
 
68
69
  export { Encoder } from "./encoder/Encoder";
@@ -7,6 +7,13 @@ import type { SetSchema } from "./custom/SetSchema";
7
7
 
8
8
  export type Constructor<T = {}> = new (...args: any[]) => T;
9
9
 
10
+ // Helper to convert primitive type literals to actual runtime types
11
+ type PrimitiveStringToType<T> =
12
+ T extends "string" ? string
13
+ : T extends "number" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" ? number
14
+ : T extends "boolean" ? boolean
15
+ : T;
16
+
10
17
  export interface Collection<K = any, V = any, IT = V> {
11
18
  [Symbol.iterator](): IterableIterator<IT>;
12
19
  forEach(callback: Function): void;
@@ -38,25 +45,20 @@ export type InferValueType<T extends DefinitionType> =
38
45
  : T extends { type: infer ChildType } ? (ChildType extends Record<string | number, string | number> ? ChildType[keyof ChildType] : ChildType) // TS ENUM
39
46
 
40
47
  // Handle direct array patterns
41
- : T extends Array<infer ChildType extends Constructor> ? InstanceType<ChildType>[]
42
- : T extends Array<infer ChildType extends RawPrimitiveType> ? InferValueType<ChildType>[] // primitive types
43
- : T extends Array<infer ChildType> ? (ChildType extends Record<string | number, string | number> ? ChildType[keyof ChildType][] : ChildType[]) // TS ENUM
48
+ : T extends Array<infer ChildType extends Constructor> ? ArraySchema<InstanceType<ChildType>>
49
+ : T extends Array<infer ChildType> ? (ChildType extends Record<string | number, string | number> ? ArraySchema<ChildType[keyof ChildType]> : ArraySchema<PrimitiveStringToType<ChildType>>) // TS ENUM
44
50
 
45
51
  // Handle collection object patterns
46
- : T extends { array: infer ChildType extends Constructor } ? InstanceType<ChildType>[]
47
- : T extends { array: infer ChildType extends RawPrimitiveType } ? InferValueType<ChildType>[] // primitive types
48
- : T extends { array: infer ChildType } ? (ChildType extends Record<string | number, string | number> ? ChildType[keyof ChildType][] : ChildType[]) // TS ENUM
52
+ : T extends { array: infer ChildType extends Constructor } ? ArraySchema<InstanceType<ChildType>>
53
+ : T extends { array: infer ChildType } ? (ChildType extends Record<string | number, string | number> ? ArraySchema<ChildType[keyof ChildType]> : ArraySchema<PrimitiveStringToType<ChildType>>) // TS ENUM
49
54
 
50
55
  : T extends { map: infer ChildType extends Constructor } ? MapSchema<InstanceType<ChildType>>
51
- : T extends { map: infer ChildType extends RawPrimitiveType } ? MapSchema<InferValueType<ChildType>> // primitive types
52
- : T extends { map: infer ChildType } ? (ChildType extends Record<string | number, string | number> ? MapSchema<ChildType[keyof ChildType]> : MapSchema<ChildType>) // TS ENUM
56
+ : T extends { map: infer ChildType } ? (ChildType extends Record<string | number, string | number> ? MapSchema<ChildType[keyof ChildType]> : MapSchema<PrimitiveStringToType<ChildType>>) // TS ENUM
53
57
 
54
58
  : T extends { set: infer ChildType extends Constructor } ? SetSchema<InstanceType<ChildType>>
55
- : T extends { set: infer ChildType extends RawPrimitiveType } ? SetSchema<InferValueType<ChildType>> // primitive types
56
59
  : T extends { set: infer ChildType } ? (ChildType extends Record<string | number, string | number> ? SetSchema<ChildType[keyof ChildType]> : SetSchema<ChildType>) // TS ENUM
57
60
 
58
61
  : T extends { collection: infer ChildType extends Constructor } ? CollectionSchema<InstanceType<ChildType>>
59
- : T extends { collection: infer ChildType extends RawPrimitiveType } ? CollectionSchema<InferValueType<ChildType>> // primitive types
60
62
  : T extends { collection: infer ChildType } ? (ChildType extends Record<string | number, string | number> ? CollectionSchema<ChildType[keyof ChildType]> : CollectionSchema<ChildType>) // TS ENUM
61
63
 
62
64
  // Handle direct types
@@ -89,7 +91,7 @@ export type NonFunctionNonPrimitivePropNames<T> = {
89
91
  }[keyof T];
90
92
 
91
93
  // Helper to recursively convert Schema instances to their JSON representation
92
- type ToJSONValue<U> = U extends Schema ? ToJSON<U> : U;
94
+ type ToJSONValue<U> = U extends Schema ? ToJSON<U> : PrimitiveStringToType<U>;
93
95
 
94
96
  export type ToJSON<T> = NonFunctionProps<{
95
97
  [K in keyof T]:
@@ -1,4 +1,4 @@
1
- import { $changes, $childType, $decoder, $deleteByIndex, $onEncodeEnd, $encoder, $filter, $getByIndex, $onDecodeEnd } from "../symbols";
1
+ import { $changes, $childType, $decoder, $deleteByIndex, $onEncodeEnd, $encoder, $filter, $getByIndex, $onDecodeEnd, $refId } from "../symbols";
2
2
  import type { Schema } from "../../Schema";
3
3
  import { type IRef, ChangeTree, setOperationAtIndex } from "../../encoder/ChangeTree";
4
4
  import { OPERATION } from "../../encoding/spec";
@@ -21,6 +21,7 @@ const DEFAULT_SORT = (a: any, b: any) => {
21
21
  export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IRef {
22
22
  [n: number]: V;
23
23
  [$changes]: ChangeTree;
24
+ [$refId]?: number;
24
25
 
25
26
  protected [$childType]: string | typeof Schema;
26
27
 
@@ -1,4 +1,4 @@
1
- import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd } from "../symbols";
1
+ import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd, $refId } from "../symbols";
2
2
  import { ChangeTree, type IRef } from "../../encoder/ChangeTree";
3
3
  import { OPERATION } from "../../encoding/spec";
4
4
  import { registerType } from "../registry";
@@ -11,7 +11,9 @@ import type { Schema } from "../../Schema";
11
11
  type K = number; // TODO: allow to specify K generic on MapSchema.
12
12
 
13
13
  export class CollectionSchema<V=any> implements Collection<K, V>, IRef {
14
- public [$changes]: ChangeTree;
14
+ [$changes]: ChangeTree;
15
+ [$refId]?: number;
16
+
15
17
  protected [$childType]: string | typeof Schema;
16
18
 
17
19
  protected $items: Map<number, V> = new Map<number, V>();
@@ -1,4 +1,4 @@
1
- import { $changes, $childType, $decoder, $deleteByIndex, $onEncodeEnd, $encoder, $filter, $getByIndex, $numFields } from "../symbols";
1
+ import { $changes, $childType, $decoder, $deleteByIndex, $onEncodeEnd, $encoder, $filter, $getByIndex, $numFields, $refId } from "../symbols";
2
2
  import { ChangeTree, IRef } from "../../encoder/ChangeTree";
3
3
  import { OPERATION } from "../../encoding/spec";
4
4
  import { registerType } from "../registry";
@@ -10,8 +10,10 @@ import type { Schema } from "../../Schema";
10
10
  import { assertInstanceType } from "../../encoding/assert";
11
11
 
12
12
  export class MapSchema<V=any, K extends string = string> implements Map<K, V>, Collection<K, V, [K, V]>, IRef {
13
- protected childType: new () => V;
14
13
  [$changes]: ChangeTree;
14
+ [$refId]?: number;
15
+
16
+ protected childType: new () => V;
15
17
  protected [$childType]: string | typeof Schema;
16
18
 
17
19
  protected $items: Map<K, V> = new Map<K, V>();
@@ -1,6 +1,6 @@
1
1
  import { OPERATION } from "../../encoding/spec";
2
2
  import { registerType } from "../registry";
3
- import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd } from "../symbols";
3
+ import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd, $refId } from "../symbols";
4
4
  import { Collection } from "../HelperTypes";
5
5
  import { ChangeTree, type IRef } from "../../encoder/ChangeTree";
6
6
  import { encodeKeyValueOperation } from "../../encoder/EncodeOperation";
@@ -10,6 +10,8 @@ import type { Schema } from "../../Schema";
10
10
 
11
11
  export class SetSchema<V=any> implements Collection<number, V>, IRef {
12
12
  [$changes]: ChangeTree;
13
+ [$refId]?: number;
14
+
13
15
  protected [$childType]: string | typeof Schema;
14
16
 
15
17
  protected $items: Map<number, V> = new Map<number, V>();
@@ -1,3 +1,4 @@
1
+ export const $refId = "~refId";
1
2
  export const $track = "~track";
2
3
  export const $encoder = "~encoder";
3
4
  export const $decoder = "~decoder";
package/src/utils.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Schema } from "./Schema";
2
2
  import { OPERATION } from "./encoding/spec";
3
- import { $changes } from "./types/symbols";
3
+ import { $changes, $refId } from "./types/symbols";
4
4
 
5
5
  interface ChangeDump {
6
6
  ops: {
@@ -37,7 +37,7 @@ export function dumpChanges(schema: Schema) {
37
37
 
38
38
  const changes = changeTree.indexedOperations;
39
39
 
40
- dump.refs.push(`refId#${changeTree.refId}`);
40
+ dump.refs.push(`refId#${changeTree.ref[$refId]}`);
41
41
  for (const index in changes) {
42
42
  const op = changes[index];
43
43
  const opName = OPERATION[op];
package/lib/Decoder.d.ts DELETED
@@ -1,16 +0,0 @@
1
- import { TypeContext } from "./annotations";
2
- import { DataChange, Schema } from "./Schema";
3
- import { Ref } from "./changes/ChangeTree";
4
- import { Iterator } from "./encoding/decode";
5
- import { ReferenceTracker } from "./changes/ReferenceTracker";
6
- export declare class Decoder<T extends Schema = any> {
7
- context: TypeContext;
8
- root: T;
9
- refs: ReferenceTracker;
10
- currentRefId: number;
11
- constructor(root: T, context?: TypeContext);
12
- protected setRoot(root: T): void;
13
- decode(bytes: number[], it?: Iterator, ref?: Ref): DataChange<any, string>[];
14
- getInstanceType(bytes: number[], it: Iterator, defaultType: typeof Schema): typeof Schema;
15
- createInstanceOfType(type: typeof Schema): Schema;
16
- }