@colyseus/schema 3.0.0-alpha.2 → 3.0.0-alpha.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -61
- package/build/cjs/index.js +476 -150
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +475 -149
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +476 -150
- package/lib/Metadata.d.ts +2 -0
- package/lib/Metadata.js +39 -0
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +1 -2
- package/lib/Reflection.js +28 -22
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +1 -1
- package/lib/annotations.js +12 -10
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +93 -0
- package/lib/bench_encode.js.map +1 -0
- package/lib/codegen/api.js +1 -2
- package/lib/codegen/api.js.map +1 -1
- package/lib/codegen/languages/cpp.js +1 -2
- package/lib/codegen/languages/cpp.js.map +1 -1
- package/lib/codegen/languages/csharp.js +1 -2
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +1 -2
- package/lib/codegen/languages/haxe.js.map +1 -1
- package/lib/codegen/languages/java.js +1 -2
- package/lib/codegen/languages/java.js.map +1 -1
- package/lib/codegen/languages/js.js +1 -2
- package/lib/codegen/languages/js.js.map +1 -1
- package/lib/codegen/languages/lua.js +1 -2
- package/lib/codegen/languages/lua.js.map +1 -1
- package/lib/codegen/languages/ts.js +1 -2
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +2 -3
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +3 -3
- package/lib/codegen/types.js.map +1 -1
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +52 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/DecodeOperation.d.ts +0 -1
- package/lib/decoder/DecodeOperation.js +22 -7
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +4 -5
- package/lib/decoder/Decoder.js +4 -4
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/decoder/strategy/RawChanges.js +1 -2
- package/lib/decoder/strategy/RawChanges.js.map +1 -1
- package/lib/decoder/strategy/StateCallbacks.d.ts +36 -7
- package/lib/decoder/strategy/StateCallbacks.js +39 -46
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.js +2 -5
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +0 -1
- package/lib/encoder/EncodeOperation.js +29 -13
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +5 -4
- package/lib/encoder/Encoder.js +60 -38
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/StateView.js +11 -6
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/assert.js +3 -3
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.d.ts +21 -19
- package/lib/encoding/decode.js +24 -25
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +2 -2
- package/lib/encoding/encode.js +40 -39
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +2 -1
- package/lib/encoding/spec.js +1 -0
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +0 -8
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/registry.js +3 -4
- package/lib/types/registry.js.map +1 -1
- package/lib/types/utils.js +1 -2
- package/lib/types/utils.js.map +1 -1
- package/lib/utils.js +3 -4
- package/lib/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/Metadata.ts +47 -0
- package/src/Reflection.ts +31 -22
- package/src/annotations.ts +6 -2
- package/src/bench_encode.ts +66 -0
- package/src/debug.ts +56 -0
- package/src/decoder/DecodeOperation.ts +26 -6
- package/src/decoder/Decoder.ts +8 -8
- package/src/decoder/strategy/StateCallbacks.ts +93 -53
- package/src/encoder/ChangeTree.ts +2 -5
- package/src/encoder/EncodeOperation.ts +29 -12
- package/src/encoder/Encoder.ts +71 -42
- package/src/encoder/StateView.ts +10 -7
- package/src/encoding/decode.ts +24 -25
- package/src/encoding/encode.ts +25 -22
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +5 -0
- package/src/types/custom/ArraySchema.ts +2 -1
package/src/debug.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import { Reflection, Decoder } from "./index";
|
|
3
|
+
|
|
4
|
+
const contents = fs.readFileSync("/Users/endel/Projects/colyseus/clients/bubbits/project/@bubbits/backend/schema-debug.txt", { encoding: "utf8" }).toString();
|
|
5
|
+
|
|
6
|
+
let isCommentBlock = false;
|
|
7
|
+
let lastComment = "";
|
|
8
|
+
|
|
9
|
+
let decoder: Decoder;
|
|
10
|
+
|
|
11
|
+
function getBuffer(line: string) {
|
|
12
|
+
const start = line.lastIndexOf(":");
|
|
13
|
+
const buffer = Buffer.from(new Uint8Array(line.substring(start + 1).split(",").map(n => Number(n))));
|
|
14
|
+
console.log(`(${buffer.byteLength}) ${Array.from(buffer).join(",")}`)
|
|
15
|
+
// console.log("");
|
|
16
|
+
// console.log("");
|
|
17
|
+
// console.log("> ", line);
|
|
18
|
+
// console.log("> substring:", line.substring(start + 1))
|
|
19
|
+
return buffer;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function decode(buffer: Buffer) {
|
|
23
|
+
try {
|
|
24
|
+
decoder.decode(buffer);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error(e);
|
|
27
|
+
console.log("Last log:\n\n")
|
|
28
|
+
console.log(lastComment);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
contents.split("\n").forEach((line) => {
|
|
33
|
+
if (line.startsWith("#")) {
|
|
34
|
+
// reset last comment.
|
|
35
|
+
if (isCommentBlock === false) { lastComment = ""; }
|
|
36
|
+
|
|
37
|
+
isCommentBlock = true;
|
|
38
|
+
lastComment += line.substring(line.indexOf(":") + 1) + "\n";
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
isCommentBlock = false;
|
|
43
|
+
|
|
44
|
+
if (line.startsWith("handshake:") && !decoder) {
|
|
45
|
+
const state = Reflection.decode(getBuffer(line));
|
|
46
|
+
decoder = new Decoder(state);
|
|
47
|
+
|
|
48
|
+
} else if (line.startsWith("state:")) {
|
|
49
|
+
decode(getBuffer(line));
|
|
50
|
+
|
|
51
|
+
} else if (line.startsWith("patch:")) {
|
|
52
|
+
decode(getBuffer(line));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
console.log(decoder.state.toJSON());
|
|
@@ -4,7 +4,7 @@ import { Schema } from "../Schema";
|
|
|
4
4
|
import type { Ref } from "../encoder/ChangeTree";
|
|
5
5
|
import type { Decoder } from "./Decoder";
|
|
6
6
|
import * as decode from "../encoding/decode";
|
|
7
|
-
import { $
|
|
7
|
+
import { $childType, $deleteByIndex, $getByIndex } from "../types/symbols";
|
|
8
8
|
|
|
9
9
|
import type { MapSchema } from "../types/custom/MapSchema";
|
|
10
10
|
import type { ArraySchema } from "../types/custom/ArraySchema";
|
|
@@ -43,7 +43,7 @@ export function decodeValue(
|
|
|
43
43
|
it: decode.Iterator,
|
|
44
44
|
allChanges: DataChange[],
|
|
45
45
|
) {
|
|
46
|
-
const $root = decoder
|
|
46
|
+
const $root = decoder.root;
|
|
47
47
|
const previousValue = ref[$getByIndex](index);
|
|
48
48
|
|
|
49
49
|
let value: any;
|
|
@@ -251,6 +251,7 @@ export const decodeKeyValueOperation: DecodeOperation = function (
|
|
|
251
251
|
dynamicIndex = ref['getIndex'](index);
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
|
|
254
255
|
const { value, previousValue } = decodeValue(
|
|
255
256
|
decoder,
|
|
256
257
|
operation,
|
|
@@ -303,7 +304,10 @@ export const decodeArray: DecodeOperation = function (
|
|
|
303
304
|
allChanges: DataChange[]
|
|
304
305
|
) {
|
|
305
306
|
// "uncompressed" index + operation (array/map items)
|
|
306
|
-
|
|
307
|
+
let operation = bytes[it.offset++];
|
|
308
|
+
|
|
309
|
+
let isSchemaChild: boolean;
|
|
310
|
+
let index: number;
|
|
307
311
|
|
|
308
312
|
if (operation === OPERATION.CLEAR) {
|
|
309
313
|
//
|
|
@@ -318,8 +322,8 @@ export const decodeArray: DecodeOperation = function (
|
|
|
318
322
|
} else if (operation === OPERATION.DELETE_BY_REFID) {
|
|
319
323
|
// TODO: refactor here, try to follow same flow as below
|
|
320
324
|
const refId = decode.number(bytes, it);
|
|
321
|
-
const previousValue = decoder
|
|
322
|
-
|
|
325
|
+
const previousValue = decoder.root.refs.get(refId);
|
|
326
|
+
index = ref.findIndex((value) => value === previousValue);
|
|
323
327
|
ref[$deleteByIndex](index);
|
|
324
328
|
allChanges.push({
|
|
325
329
|
ref,
|
|
@@ -330,10 +334,26 @@ export const decodeArray: DecodeOperation = function (
|
|
|
330
334
|
value: undefined,
|
|
331
335
|
previousValue,
|
|
332
336
|
});
|
|
337
|
+
|
|
333
338
|
return;
|
|
339
|
+
|
|
340
|
+
} else if (operation === OPERATION.ADD_BY_REFID) {
|
|
341
|
+
isSchemaChild = true;
|
|
342
|
+
// operation = OPERATION.ADD;
|
|
343
|
+
|
|
344
|
+
const refId = decode.number(bytes, it);
|
|
345
|
+
const itemByRefId = decoder.root.refs.get(refId);
|
|
346
|
+
|
|
347
|
+
// use existing index, or push new value
|
|
348
|
+
index = (itemByRefId)
|
|
349
|
+
? ref.findIndex((value) => value === itemByRefId)
|
|
350
|
+
: ref.length;
|
|
351
|
+
|
|
352
|
+
} else {
|
|
353
|
+
isSchemaChild = false;
|
|
354
|
+
index = decode.number(bytes, it);
|
|
334
355
|
}
|
|
335
356
|
|
|
336
|
-
const index = decode.number(bytes, it);
|
|
337
357
|
const type = ref[$childType];
|
|
338
358
|
|
|
339
359
|
let dynamicIndex: number | string = index;
|
package/src/decoder/Decoder.ts
CHANGED
|
@@ -4,17 +4,17 @@ import { Schema } from "../Schema";
|
|
|
4
4
|
|
|
5
5
|
import * as decode from "../encoding/decode";
|
|
6
6
|
import { OPERATION, SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';
|
|
7
|
-
import { Ref } from "../encoder/ChangeTree";
|
|
8
|
-
import { Iterator } from "../encoding/decode";
|
|
7
|
+
import type { Ref } from "../encoder/ChangeTree";
|
|
8
|
+
import type { Iterator } from "../encoding/decode";
|
|
9
9
|
import { ReferenceTracker } from "./ReferenceTracker";
|
|
10
|
-
import { DEFINITION_MISMATCH, DataChange, DecodeOperation } from "./DecodeOperation";
|
|
10
|
+
import { DEFINITION_MISMATCH, type DataChange, type DecodeOperation } from "./DecodeOperation";
|
|
11
11
|
import { Collection } from "../types/HelperTypes";
|
|
12
12
|
|
|
13
13
|
export class Decoder<T extends Schema = any> {
|
|
14
14
|
context: TypeContext;
|
|
15
15
|
|
|
16
16
|
state: T;
|
|
17
|
-
|
|
17
|
+
root: ReferenceTracker;
|
|
18
18
|
|
|
19
19
|
currentRefId: number = 0;
|
|
20
20
|
|
|
@@ -32,8 +32,8 @@ export class Decoder<T extends Schema = any> {
|
|
|
32
32
|
|
|
33
33
|
protected setRoot(root: T) {
|
|
34
34
|
this.state = root;
|
|
35
|
-
this
|
|
36
|
-
this
|
|
35
|
+
this.root = new ReferenceTracker();
|
|
36
|
+
this.root.addRef(0, root);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
decode(
|
|
@@ -43,7 +43,7 @@ export class Decoder<T extends Schema = any> {
|
|
|
43
43
|
) {
|
|
44
44
|
const allChanges: DataChange[] = [];
|
|
45
45
|
|
|
46
|
-
const $root = this
|
|
46
|
+
const $root = this.root;
|
|
47
47
|
const totalBytes = bytes.byteLength;
|
|
48
48
|
|
|
49
49
|
let decoder: DecodeOperation = ref['constructor'][$decoder];
|
|
@@ -146,7 +146,7 @@ export class Decoder<T extends Schema = any> {
|
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
if (needRemoveRef) {
|
|
149
|
-
this
|
|
149
|
+
this.root.removeRef(this.root.refIds.get(value));
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
152
|
}
|
|
@@ -17,27 +17,69 @@ import type { ArraySchema } from "../../types/custom/ArraySchema";
|
|
|
17
17
|
// - Avoid closures by allowing to pass a context. (https://github.com/colyseus/schema/issues/155#issuecomment-1804694081)
|
|
18
18
|
//
|
|
19
19
|
|
|
20
|
-
type
|
|
20
|
+
export type GetCallbackProxy = (<T extends Schema>(instance: T) => CallbackProxy<T>);
|
|
21
|
+
|
|
22
|
+
export type CallbackProxy<T> = unknown extends T // is "any"?
|
|
21
23
|
? InstanceCallback<T> & CollectionCallback<any, any>
|
|
22
24
|
: T extends Collection<infer K, infer V, infer _>
|
|
23
25
|
? CollectionCallback<K, V>
|
|
24
|
-
: InstanceCallback<T
|
|
26
|
+
: InstanceCallback<T>;
|
|
25
27
|
|
|
26
28
|
type InstanceCallback<T> = {
|
|
29
|
+
/**
|
|
30
|
+
* Trigger callback when value of a property changes.
|
|
31
|
+
*
|
|
32
|
+
* @param prop name of the property
|
|
33
|
+
* @param callback callback to be triggered on property change
|
|
34
|
+
* @param immediate trigger immediatelly if property has been already set.
|
|
35
|
+
*/
|
|
27
36
|
listen<K extends NonFunctionPropNames<T>>(
|
|
28
37
|
prop: K,
|
|
29
38
|
callback: (value: T[K], previousValue: T[K]) => void,
|
|
30
39
|
immediate?: boolean,
|
|
31
40
|
)
|
|
41
|
+
/**
|
|
42
|
+
* Trigger callback whenever any property changed within this instance.
|
|
43
|
+
*
|
|
44
|
+
* @param prop name of the property
|
|
45
|
+
* @param callback callback to be triggered on property change
|
|
46
|
+
* @param immediate trigger immediatelly if property has been already set.
|
|
47
|
+
*/
|
|
32
48
|
onChange(callback: () => void): void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Bind properties to another object. Changes on the properties will be reflected on the target object.
|
|
52
|
+
*
|
|
53
|
+
* @param targetObject object to bind properties to
|
|
54
|
+
* @param properties list of properties to bind. If not provided, all properties will be bound.
|
|
55
|
+
*/
|
|
33
56
|
bindTo(targetObject: any, properties?: Array<NonFunctionPropNames<T>>): void;
|
|
34
57
|
} & {
|
|
35
|
-
[K in NonFunctionNonPrimitivePropNames<T>]:
|
|
58
|
+
[K in NonFunctionNonPrimitivePropNames<T>]: CallbackProxy<T[K]>;
|
|
36
59
|
}
|
|
37
60
|
|
|
38
61
|
type CollectionCallback<K, V> = {
|
|
62
|
+
/**
|
|
63
|
+
* Trigger callback when an item has been added to the collection.
|
|
64
|
+
*
|
|
65
|
+
* @param callback
|
|
66
|
+
* @param immediate
|
|
67
|
+
*/
|
|
39
68
|
onAdd(callback: (item: V, index: K) => void, immediate?: boolean): void;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Trigger callback when an item has been removed to the collection.
|
|
72
|
+
*
|
|
73
|
+
* @param callback
|
|
74
|
+
*/
|
|
40
75
|
onRemove(callback: (item: V, index: K) => void): void;
|
|
76
|
+
|
|
77
|
+
// /**
|
|
78
|
+
// * Trigger callback when an item has been removed to the collection.
|
|
79
|
+
// *
|
|
80
|
+
// * @param callback
|
|
81
|
+
// */
|
|
82
|
+
// onChange(callback: (item: V, index: K) => void): void;
|
|
41
83
|
};
|
|
42
84
|
|
|
43
85
|
type OnInstanceAvailableCallback = (callback: (ref: Ref, existing: boolean) => void) => void;
|
|
@@ -48,8 +90,9 @@ type CallContext = {
|
|
|
48
90
|
onInstanceAvailable?: OnInstanceAvailableCallback,
|
|
49
91
|
}
|
|
50
92
|
|
|
51
|
-
|
|
52
|
-
|
|
93
|
+
|
|
94
|
+
export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>): GetCallbackProxy {
|
|
95
|
+
const $root = decoder.root;
|
|
53
96
|
const callbacks = $root.callbacks;
|
|
54
97
|
|
|
55
98
|
let isTriggeringOnAdd = false;
|
|
@@ -76,8 +119,6 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
76
119
|
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
77
120
|
deleteCallbacks[i]();
|
|
78
121
|
}
|
|
79
|
-
// callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE]?.forEach(callback =>
|
|
80
|
-
// callback());
|
|
81
122
|
}
|
|
82
123
|
|
|
83
124
|
if (ref instanceof Schema) {
|
|
@@ -86,47 +127,35 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
86
127
|
//
|
|
87
128
|
|
|
88
129
|
if (!uniqueRefIds.has(refId)) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
console.error(e);
|
|
130
|
+
// trigger onChange
|
|
131
|
+
const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
|
|
132
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
133
|
+
replaceCallbacks[i]();
|
|
134
|
+
// try {
|
|
135
|
+
// } catch (e) {
|
|
136
|
+
// console.error(e);
|
|
137
|
+
// }
|
|
98
138
|
}
|
|
99
139
|
}
|
|
100
140
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
141
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
142
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
143
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
144
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
145
|
+
// try {
|
|
146
|
+
// } catch (e) {
|
|
147
|
+
// console.error(e);
|
|
148
|
+
// }
|
|
107
149
|
}
|
|
108
|
-
|
|
109
|
-
} catch (e) {
|
|
110
|
-
//
|
|
111
|
-
console.error(e);
|
|
112
150
|
}
|
|
113
151
|
|
|
152
|
+
|
|
114
153
|
} else {
|
|
115
154
|
//
|
|
116
155
|
// Handle collection of items
|
|
117
156
|
//
|
|
118
157
|
|
|
119
|
-
if (change.op
|
|
120
|
-
// triger onAdd
|
|
121
|
-
|
|
122
|
-
isTriggeringOnAdd = true;
|
|
123
|
-
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
124
|
-
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
125
|
-
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
126
|
-
}
|
|
127
|
-
isTriggeringOnAdd = false;
|
|
128
|
-
|
|
129
|
-
} else if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
158
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
130
159
|
//
|
|
131
160
|
// FIXME: `previousValue` should always be available.
|
|
132
161
|
//
|
|
@@ -146,6 +175,16 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
146
175
|
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
147
176
|
}
|
|
148
177
|
}
|
|
178
|
+
|
|
179
|
+
} else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
|
|
180
|
+
// triger onAdd
|
|
181
|
+
|
|
182
|
+
isTriggeringOnAdd = true;
|
|
183
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
184
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
185
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
186
|
+
}
|
|
187
|
+
isTriggeringOnAdd = false;
|
|
149
188
|
}
|
|
150
189
|
|
|
151
190
|
// trigger onChange
|
|
@@ -206,10 +245,22 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
206
245
|
OPERATION.REPLACE,
|
|
207
246
|
callback
|
|
208
247
|
);
|
|
209
|
-
|
|
210
248
|
},
|
|
211
249
|
bindTo: function bindTo(targetObject: any, properties?: string[]) {
|
|
212
|
-
|
|
250
|
+
//
|
|
251
|
+
// TODO: refactor this implementation. There is room for improvement here.
|
|
252
|
+
//
|
|
253
|
+
if (!properties) {
|
|
254
|
+
properties = Object.keys(metadata);
|
|
255
|
+
}
|
|
256
|
+
return $root.addCallback(
|
|
257
|
+
$root.refIds.get(context.instance),
|
|
258
|
+
OPERATION.REPLACE,
|
|
259
|
+
() => {
|
|
260
|
+
properties.forEach((prop) =>
|
|
261
|
+
targetObject[prop] = context.instance[prop])
|
|
262
|
+
}
|
|
263
|
+
);
|
|
213
264
|
}
|
|
214
265
|
}, {
|
|
215
266
|
get(target, prop: string) {
|
|
@@ -307,20 +358,9 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
307
358
|
}
|
|
308
359
|
}
|
|
309
360
|
|
|
310
|
-
function $<T>(instance: T):
|
|
311
|
-
return getProxy(undefined, { instance }) as
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return {
|
|
315
|
-
$,
|
|
316
|
-
trigger: function trigger(changes: DataChange[]) {
|
|
317
|
-
for (let i = 0, l = changes.length; i < l; i++) {
|
|
318
|
-
const change = changes[i];
|
|
319
|
-
|
|
320
|
-
change.op
|
|
321
|
-
change.ref
|
|
322
|
-
}
|
|
323
|
-
}
|
|
361
|
+
function $<T>(instance: T): CallbackProxy<T> {
|
|
362
|
+
return getProxy(undefined, { instance }) as CallbackProxy<T>;
|
|
324
363
|
}
|
|
325
364
|
|
|
365
|
+
return $;
|
|
326
366
|
}
|
|
@@ -10,7 +10,6 @@ import type { SetSchema } from "../types/custom/SetSchema";
|
|
|
10
10
|
import { Metadata } from "../Metadata";
|
|
11
11
|
import type { EncodeOperation } from "./EncodeOperation";
|
|
12
12
|
import type { DecodeOperation } from "../decoder/DecodeOperation";
|
|
13
|
-
import type { StateView } from "./StateView";
|
|
14
13
|
|
|
15
14
|
declare global {
|
|
16
15
|
interface Object {
|
|
@@ -170,13 +169,12 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
170
169
|
|
|
171
170
|
if (!this.isFiltered) {
|
|
172
171
|
this.root.changes.set(this, this.changes);
|
|
172
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
176
176
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
177
177
|
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
178
|
-
} else {
|
|
179
|
-
this.root.allChanges.set(this, this.allChanges);
|
|
180
178
|
}
|
|
181
179
|
|
|
182
180
|
this.ensureRefId();
|
|
@@ -285,7 +283,6 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
285
283
|
|
|
286
284
|
private _shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0, allChangeSet: Map<number, OPERATION>) {
|
|
287
285
|
Array.from(allChangeSet.entries()).forEach(([index, op]) => {
|
|
288
|
-
// console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
|
|
289
286
|
if (index >= startIndex) {
|
|
290
287
|
allChangeSet.delete(index);
|
|
291
288
|
allChangeSet.set(index + shiftIndex, op);
|
|
@@ -452,7 +449,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
452
449
|
|
|
453
450
|
protected checkIsFiltered(parent: Ref, parentIndex: number) {
|
|
454
451
|
// Detect if current structure has "filters" declared
|
|
455
|
-
this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
|
|
452
|
+
this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
|
|
456
453
|
|
|
457
454
|
// TODO: support "partially filtered", where the instance is visible, but only a field is not.
|
|
458
455
|
|
|
@@ -172,6 +172,19 @@ export const encodeKeyValueOperation: EncodeOperation = function (
|
|
|
172
172
|
const type = changeTree.getType(field);
|
|
173
173
|
const value = changeTree.getValue(field);
|
|
174
174
|
|
|
175
|
+
// try { throw new Error(); } catch (e) {
|
|
176
|
+
// // only print if not coming from Reflection.ts
|
|
177
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
178
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
179
|
+
// ref: changeTree.ref.constructor.name,
|
|
180
|
+
// field,
|
|
181
|
+
// operation: OPERATION[operation],
|
|
182
|
+
// value: value?.toJSON(),
|
|
183
|
+
// items: ref.toJSON(),
|
|
184
|
+
// });
|
|
185
|
+
// }
|
|
186
|
+
// }
|
|
187
|
+
|
|
175
188
|
// TODO: inline this function call small performance gain
|
|
176
189
|
encodeValue(encoder, bytes, ref, type, value, field, operation, it);
|
|
177
190
|
}
|
|
@@ -191,18 +204,22 @@ export const encodeArray: EncodeOperation = function (
|
|
|
191
204
|
hasView: boolean,
|
|
192
205
|
) {
|
|
193
206
|
const ref = changeTree.ref;
|
|
207
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
|
|
194
208
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
209
|
+
let refOrIndex: number;
|
|
210
|
+
|
|
211
|
+
if (useOperationByRefId) {
|
|
212
|
+
refOrIndex = ref['tmpItems'][field][$changes].refId;
|
|
213
|
+
|
|
214
|
+
if (operation === OPERATION.DELETE) {
|
|
215
|
+
operation = OPERATION.DELETE_BY_REFID;
|
|
216
|
+
|
|
217
|
+
} else if (operation === OPERATION.ADD) {
|
|
218
|
+
operation = OPERATION.ADD_BY_REFID;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
} else {
|
|
222
|
+
refOrIndex = field;
|
|
206
223
|
}
|
|
207
224
|
|
|
208
225
|
// encode operation
|
|
@@ -214,7 +231,7 @@ export const encodeArray: EncodeOperation = function (
|
|
|
214
231
|
}
|
|
215
232
|
|
|
216
233
|
// encode index
|
|
217
|
-
encode.number(bytes,
|
|
234
|
+
encode.number(bytes, refOrIndex, it);
|
|
218
235
|
|
|
219
236
|
// Do not encode value for DELETE operations
|
|
220
237
|
if (operation === OPERATION.DELETE) {
|