@colyseus/schema 3.0.0-alpha.3 → 3.0.0-alpha.30
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 +644 -283
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +643 -282
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +644 -283
- 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 +2 -3
- package/lib/Reflection.js +31 -26
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +2 -2
- package/lib/Schema.js +2 -2
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +0 -19
- package/lib/annotations.js +15 -101
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +120 -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 +23 -7
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +6 -7
- package/lib/decoder/Decoder.js +8 -8
- 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 +44 -11
- package/lib/decoder/strategy/StateCallbacks.js +72 -63
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +1 -12
- package/lib/encoder/ChangeTree.js +29 -58
- 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 +10 -8
- package/lib/encoder/Encoder.js +78 -53
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +17 -0
- package/lib/encoder/Root.js +44 -0
- package/lib/encoder/Root.js.map +1 -0
- package/lib/encoder/StateView.d.ts +2 -2
- package/lib/encoder/StateView.js +48 -58
- 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.js +21 -22
- 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 +5 -1
- package/lib/index.js +8 -4
- package/lib/index.js.map +1 -1
- package/lib/types/TypeContext.d.ts +23 -0
- package/lib/types/TypeContext.js +109 -0
- package/lib/types/TypeContext.js.map +1 -0
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +0 -9
- 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 +34 -26
- package/src/Schema.ts +2 -2
- package/src/annotations.ts +7 -109
- package/src/bench_encode.ts +97 -0
- package/src/debug.ts +56 -0
- package/src/decoder/DecodeOperation.ts +30 -7
- package/src/decoder/Decoder.ts +13 -11
- package/src/decoder/strategy/StateCallbacks.ts +149 -79
- package/src/encoder/ChangeTree.ts +36 -66
- package/src/encoder/EncodeOperation.ts +29 -12
- package/src/encoder/Encoder.ts +95 -61
- package/src/encoder/Root.ts +51 -0
- package/src/encoder/StateView.ts +51 -67
- package/src/encoding/decode.ts +1 -2
- package/src/encoding/encode.ts +25 -22
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +8 -11
- package/src/types/TypeContext.ts +127 -0
- package/src/types/custom/ArraySchema.ts +2 -2
|
@@ -6,7 +6,7 @@ import { DataChange } from "../DecodeOperation";
|
|
|
6
6
|
import { OPERATION } from "../../encoding/spec";
|
|
7
7
|
import { DefinitionType } from "../../annotations";
|
|
8
8
|
import { Schema } from "../../Schema";
|
|
9
|
-
import type {
|
|
9
|
+
import type { CollectionSchema } from "../../types/custom/CollectionSchema";
|
|
10
10
|
|
|
11
11
|
//
|
|
12
12
|
// Discussion: https://github.com/colyseus/schema/issues/155
|
|
@@ -17,27 +17,74 @@ 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
|
+
* @return callback to detach the listener
|
|
36
|
+
*/
|
|
27
37
|
listen<K extends NonFunctionPropNames<T>>(
|
|
28
38
|
prop: K,
|
|
29
39
|
callback: (value: T[K], previousValue: T[K]) => void,
|
|
30
40
|
immediate?: boolean,
|
|
31
|
-
)
|
|
32
|
-
|
|
41
|
+
): () => void;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Trigger callback whenever any property changed within this instance.
|
|
45
|
+
*
|
|
46
|
+
* @param prop name of the property
|
|
47
|
+
* @param callback callback to be triggered on property change
|
|
48
|
+
* @param immediate trigger immediatelly if property has been already set.
|
|
49
|
+
* @return callback to detach the listener
|
|
50
|
+
*/
|
|
51
|
+
onChange(callback: () => void): () => void;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Bind properties to another object. Changes on the properties will be reflected on the target object.
|
|
55
|
+
*
|
|
56
|
+
* @param targetObject object to bind properties to
|
|
57
|
+
* @param properties list of properties to bind. If not provided, all properties will be bound.
|
|
58
|
+
*/
|
|
33
59
|
bindTo(targetObject: any, properties?: Array<NonFunctionPropNames<T>>): void;
|
|
34
60
|
} & {
|
|
35
|
-
[K in NonFunctionNonPrimitivePropNames<T>]:
|
|
61
|
+
[K in NonFunctionNonPrimitivePropNames<T>]: CallbackProxy<T[K]>;
|
|
36
62
|
}
|
|
37
63
|
|
|
38
64
|
type CollectionCallback<K, V> = {
|
|
39
|
-
|
|
40
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Trigger callback when an item has been added to the collection.
|
|
67
|
+
*
|
|
68
|
+
* @param callback
|
|
69
|
+
* @param immediate
|
|
70
|
+
* @return callback to detach the onAdd listener
|
|
71
|
+
*/
|
|
72
|
+
onAdd(callback: (item: V, index: K) => void, immediate?: boolean): () => void;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Trigger callback when an item has been removed to the collection.
|
|
76
|
+
*
|
|
77
|
+
* @param callback
|
|
78
|
+
* @return callback to detach the onRemove listener
|
|
79
|
+
*/
|
|
80
|
+
onRemove(callback: (item: V, index: K) => void): () => void;
|
|
81
|
+
|
|
82
|
+
// /**
|
|
83
|
+
// * Trigger callback when an item has been removed to the collection.
|
|
84
|
+
// *
|
|
85
|
+
// * @param callback
|
|
86
|
+
// */
|
|
87
|
+
// onChange(callback: (item: V, index: K) => void): void;
|
|
41
88
|
};
|
|
42
89
|
|
|
43
90
|
type OnInstanceAvailableCallback = (callback: (ref: Ref, existing: boolean) => void) => void;
|
|
@@ -48,11 +95,13 @@ type CallContext = {
|
|
|
48
95
|
onInstanceAvailable?: OnInstanceAvailableCallback,
|
|
49
96
|
}
|
|
50
97
|
|
|
51
|
-
|
|
52
|
-
|
|
98
|
+
|
|
99
|
+
export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>): GetCallbackProxy {
|
|
100
|
+
const $root = decoder.root;
|
|
53
101
|
const callbacks = $root.callbacks;
|
|
54
102
|
|
|
55
|
-
|
|
103
|
+
const onAddCalls: WeakMap<Function, boolean> = new WeakMap();
|
|
104
|
+
let currentOnAddCallback: Function | undefined;
|
|
56
105
|
|
|
57
106
|
decoder.triggerChanges = function (allChanges: DataChange[]) {
|
|
58
107
|
const uniqueRefIds = new Set<number>();
|
|
@@ -76,8 +125,6 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
76
125
|
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
77
126
|
deleteCallbacks[i]();
|
|
78
127
|
}
|
|
79
|
-
// callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE]?.forEach(callback =>
|
|
80
|
-
// callback());
|
|
81
128
|
}
|
|
82
129
|
|
|
83
130
|
if (ref instanceof Schema) {
|
|
@@ -86,47 +133,35 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
86
133
|
//
|
|
87
134
|
|
|
88
135
|
if (!uniqueRefIds.has(refId)) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
console.error(e);
|
|
136
|
+
// trigger onChange
|
|
137
|
+
const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
|
|
138
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
139
|
+
replaceCallbacks[i]();
|
|
140
|
+
// try {
|
|
141
|
+
// } catch (e) {
|
|
142
|
+
// console.error(e);
|
|
143
|
+
// }
|
|
98
144
|
}
|
|
99
145
|
}
|
|
100
146
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
147
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
148
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
149
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
150
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
151
|
+
// try {
|
|
152
|
+
// } catch (e) {
|
|
153
|
+
// console.error(e);
|
|
154
|
+
// }
|
|
107
155
|
}
|
|
108
|
-
|
|
109
|
-
} catch (e) {
|
|
110
|
-
//
|
|
111
|
-
console.error(e);
|
|
112
156
|
}
|
|
113
157
|
|
|
158
|
+
|
|
114
159
|
} else {
|
|
115
160
|
//
|
|
116
161
|
// Handle collection of items
|
|
117
162
|
//
|
|
118
163
|
|
|
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) {
|
|
164
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
130
165
|
//
|
|
131
166
|
// FIXME: `previousValue` should always be available.
|
|
132
167
|
//
|
|
@@ -139,13 +174,19 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
139
174
|
}
|
|
140
175
|
|
|
141
176
|
// Handle DELETE_AND_ADD operations
|
|
142
|
-
// FIXME: should we set "isTriggeringOnAdd" here?
|
|
143
177
|
if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
|
|
144
178
|
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
145
179
|
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
146
180
|
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
147
181
|
}
|
|
148
182
|
}
|
|
183
|
+
|
|
184
|
+
} else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
|
|
185
|
+
// triger onAdd
|
|
186
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
187
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
188
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
189
|
+
}
|
|
149
190
|
}
|
|
150
191
|
|
|
151
192
|
// trigger onChange
|
|
@@ -161,7 +202,10 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
161
202
|
}
|
|
162
203
|
};
|
|
163
204
|
|
|
164
|
-
function getProxy(
|
|
205
|
+
function getProxy(
|
|
206
|
+
metadataOrType: Metadata | DefinitionType,
|
|
207
|
+
context: CallContext
|
|
208
|
+
) {
|
|
165
209
|
let metadata: Metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
166
210
|
let isCollection = (
|
|
167
211
|
(context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
@@ -170,7 +214,7 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
170
214
|
|
|
171
215
|
if (metadata && !isCollection) {
|
|
172
216
|
|
|
173
|
-
const
|
|
217
|
+
const onAddListen = function (
|
|
174
218
|
ref: Ref,
|
|
175
219
|
prop: string,
|
|
176
220
|
callback: (value: any, previousValue: any) => void, immediate: boolean
|
|
@@ -179,7 +223,7 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
179
223
|
if (
|
|
180
224
|
immediate &&
|
|
181
225
|
context.instance[prop] !== undefined &&
|
|
182
|
-
!
|
|
226
|
+
!onAddCalls.has(currentOnAddCallback) // Workaround for https://github.com/colyseus/schema/issues/147
|
|
183
227
|
) {
|
|
184
228
|
callback(context.instance[prop], undefined);
|
|
185
229
|
}
|
|
@@ -192,24 +236,44 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
192
236
|
return new Proxy({
|
|
193
237
|
listen: function listen(prop: string, callback: (value: any, previousValue: any) => void, immediate: boolean = true) {
|
|
194
238
|
if (context.instance) {
|
|
195
|
-
return
|
|
239
|
+
return onAddListen(context.instance, prop, callback, immediate);
|
|
196
240
|
|
|
197
241
|
} else {
|
|
198
242
|
// collection instance not received yet
|
|
199
|
-
|
|
200
|
-
|
|
243
|
+
let detachCallback = () => {};
|
|
244
|
+
|
|
245
|
+
context.onInstanceAvailable((ref: Ref, existing: boolean) => {
|
|
246
|
+
detachCallback = onAddListen(ref, prop, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback))
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return () => detachCallback();
|
|
201
250
|
}
|
|
202
251
|
},
|
|
252
|
+
|
|
203
253
|
onChange: function onChange(callback: () => void) {
|
|
204
254
|
return $root.addCallback(
|
|
205
255
|
$root.refIds.get(context.instance),
|
|
206
256
|
OPERATION.REPLACE,
|
|
207
257
|
callback
|
|
208
258
|
);
|
|
209
|
-
|
|
210
259
|
},
|
|
260
|
+
|
|
261
|
+
//
|
|
262
|
+
// TODO: refactor `bindTo()` implementation.
|
|
263
|
+
// There is room for improvement.
|
|
264
|
+
//
|
|
211
265
|
bindTo: function bindTo(targetObject: any, properties?: string[]) {
|
|
212
|
-
|
|
266
|
+
if (!properties) {
|
|
267
|
+
properties = Object.keys(metadata);
|
|
268
|
+
}
|
|
269
|
+
return $root.addCallback(
|
|
270
|
+
$root.refIds.get(context.instance),
|
|
271
|
+
OPERATION.REPLACE,
|
|
272
|
+
() => {
|
|
273
|
+
properties.forEach((prop) =>
|
|
274
|
+
targetObject[prop] = context.instance[prop])
|
|
275
|
+
}
|
|
276
|
+
);
|
|
213
277
|
}
|
|
214
278
|
}, {
|
|
215
279
|
get(target, prop: string) {
|
|
@@ -234,7 +298,8 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
234
298
|
}
|
|
235
299
|
);
|
|
236
300
|
return getProxy(metadata[prop].type, {
|
|
237
|
-
instance
|
|
301
|
+
// make sure refId is available, otherwise need to wait for the instance to be available.
|
|
302
|
+
instance: ($root.refIds.get(instance) && instance),
|
|
238
303
|
parentInstance: context.instance,
|
|
239
304
|
onInstanceAvailable,
|
|
240
305
|
});
|
|
@@ -257,9 +322,16 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
257
322
|
const onAdd = function (ref: Ref, callback: (value: any, key: any) => void, immediate: boolean) {
|
|
258
323
|
// Trigger callback on existing items
|
|
259
324
|
if (immediate) {
|
|
260
|
-
(ref as
|
|
325
|
+
(ref as CollectionSchema).forEach((v, k) => callback(v, k));
|
|
261
326
|
}
|
|
262
|
-
|
|
327
|
+
|
|
328
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, (value, key) => {
|
|
329
|
+
onAddCalls.set(callback, true);
|
|
330
|
+
currentOnAddCallback = callback;
|
|
331
|
+
callback(value, key);
|
|
332
|
+
onAddCalls.delete(callback)
|
|
333
|
+
currentOnAddCallback = undefined;
|
|
334
|
+
});
|
|
263
335
|
};
|
|
264
336
|
|
|
265
337
|
const onRemove = function (ref: Ref, callback: (value: any, key: any) => void) {
|
|
@@ -272,25 +344,34 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
272
344
|
// https://github.com/colyseus/schema/issues/147
|
|
273
345
|
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
274
346
|
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
347
|
+
|
|
348
|
+
if (context.instance) {
|
|
349
|
+
return onAdd(context.instance, callback, immediate && !onAddCalls.has(currentOnAddCallback));
|
|
350
|
+
|
|
351
|
+
} else if (context.onInstanceAvailable) {
|
|
278
352
|
// collection instance not received yet
|
|
279
|
-
|
|
280
|
-
onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
|
|
353
|
+
let detachCallback = () => {};
|
|
281
354
|
|
|
282
|
-
|
|
283
|
-
|
|
355
|
+
context.onInstanceAvailable((ref: Ref, existing: boolean) => {
|
|
356
|
+
detachCallback = onAdd(ref, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
return () => detachCallback();
|
|
284
360
|
}
|
|
285
361
|
},
|
|
286
362
|
onRemove: function(callback: (value, key) => void) {
|
|
287
363
|
if (context.onInstanceAvailable) {
|
|
288
364
|
// collection instance not received yet
|
|
289
|
-
|
|
290
|
-
|
|
365
|
+
let detachCallback = () => {};
|
|
366
|
+
|
|
367
|
+
context.onInstanceAvailable((ref: Ref) => {
|
|
368
|
+
detachCallback = onRemove(ref, callback)
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
return () => detachCallback();
|
|
291
372
|
|
|
292
373
|
} else if (context.instance) {
|
|
293
|
-
onRemove(context.instance, callback);
|
|
374
|
+
return onRemove(context.instance, callback);
|
|
294
375
|
}
|
|
295
376
|
},
|
|
296
377
|
}, {
|
|
@@ -307,20 +388,9 @@ export function getStateCallbacks(decoder: Decoder) {
|
|
|
307
388
|
}
|
|
308
389
|
}
|
|
309
390
|
|
|
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
|
-
}
|
|
391
|
+
function $<T>(instance: T): CallbackProxy<T> {
|
|
392
|
+
return getProxy(undefined, { instance }) as CallbackProxy<T>;
|
|
324
393
|
}
|
|
325
394
|
|
|
395
|
+
return $;
|
|
326
396
|
}
|
|
@@ -7,10 +7,10 @@ import type { ArraySchema } from "../types/custom/ArraySchema";
|
|
|
7
7
|
import type { CollectionSchema } from "../types/custom/CollectionSchema";
|
|
8
8
|
import type { SetSchema } from "../types/custom/SetSchema";
|
|
9
9
|
|
|
10
|
+
import { Root } from "./Root";
|
|
10
11
|
import { Metadata } from "../Metadata";
|
|
11
12
|
import type { EncodeOperation } from "./EncodeOperation";
|
|
12
13
|
import type { DecodeOperation } from "../decoder/DecodeOperation";
|
|
13
|
-
import type { StateView } from "./StateView";
|
|
14
14
|
|
|
15
15
|
declare global {
|
|
16
16
|
interface Object {
|
|
@@ -27,53 +27,6 @@ export type Ref = Schema
|
|
|
27
27
|
| CollectionSchema
|
|
28
28
|
| SetSchema;
|
|
29
29
|
|
|
30
|
-
export class Root {
|
|
31
|
-
protected nextUniqueId: number = 0;
|
|
32
|
-
refCount = new WeakMap<ChangeTree, number>();
|
|
33
|
-
|
|
34
|
-
// all changes
|
|
35
|
-
allChanges = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
36
|
-
allFilteredChanges = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
37
|
-
|
|
38
|
-
// pending changes to be encoded
|
|
39
|
-
changes = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
40
|
-
filteredChanges = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
41
|
-
|
|
42
|
-
getNextUniqueId() {
|
|
43
|
-
return this.nextUniqueId++;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
add (changeTree: ChangeTree) {
|
|
47
|
-
const refCount = this.refCount.get(changeTree) || 0;
|
|
48
|
-
this.refCount.set(changeTree, refCount + 1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
remove(changeTree: ChangeTree) {
|
|
52
|
-
const refCount = this.refCount.get(changeTree);
|
|
53
|
-
if (refCount <= 1) {
|
|
54
|
-
this.allChanges.delete(changeTree);
|
|
55
|
-
this.changes.delete(changeTree);
|
|
56
|
-
|
|
57
|
-
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
58
|
-
this.allFilteredChanges.delete(changeTree);
|
|
59
|
-
this.filteredChanges.delete(changeTree);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
this.refCount.delete(changeTree);
|
|
63
|
-
|
|
64
|
-
} else {
|
|
65
|
-
this.refCount.set(changeTree, refCount - 1);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
changeTree.forEachChild((child, _) =>
|
|
69
|
-
this.remove(child));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
clear() {
|
|
73
|
-
this.changes.clear();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
30
|
export class ChangeTree<T extends Ref=any> {
|
|
78
31
|
ref: T;
|
|
79
32
|
refId: number;
|
|
@@ -123,9 +76,6 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
123
76
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
124
77
|
this.root.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
125
78
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
126
|
-
|
|
127
|
-
// } else {
|
|
128
|
-
// this.root.allChanges.set(this, this.allChanges);
|
|
129
79
|
}
|
|
130
80
|
|
|
131
81
|
if (!this.isFiltered) {
|
|
@@ -170,13 +120,12 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
170
120
|
|
|
171
121
|
if (!this.isFiltered) {
|
|
172
122
|
this.root.changes.set(this, this.changes);
|
|
123
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
173
124
|
}
|
|
174
125
|
|
|
175
126
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
176
127
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
177
128
|
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
178
|
-
} else {
|
|
179
|
-
this.root.allChanges.set(this, this.allChanges);
|
|
180
129
|
}
|
|
181
130
|
|
|
182
131
|
this.ensureRefId();
|
|
@@ -206,7 +155,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
206
155
|
// MapSchema / ArraySchema, etc.
|
|
207
156
|
(this.ref as MapSchema).forEach((value, key) => {
|
|
208
157
|
if (Metadata.isValidInstance(value)) {
|
|
209
|
-
callback(value[$changes], this.ref[$changes].indexes[key]);
|
|
158
|
+
callback(value[$changes], this.ref[$changes].indexes[key] ?? key);
|
|
210
159
|
}
|
|
211
160
|
});
|
|
212
161
|
}
|
|
@@ -240,9 +189,11 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
240
189
|
//
|
|
241
190
|
|
|
242
191
|
if (isFiltered) {
|
|
243
|
-
this.allFilteredChanges.set(index, OPERATION.ADD);
|
|
244
192
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
245
193
|
|
|
194
|
+
this.allFilteredChanges.set(index, OPERATION.ADD);
|
|
195
|
+
this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
196
|
+
|
|
246
197
|
} else {
|
|
247
198
|
this.allChanges.set(index, OPERATION.ADD);
|
|
248
199
|
this.root?.changes.set(this, this.changes);
|
|
@@ -285,7 +236,6 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
285
236
|
|
|
286
237
|
private _shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0, allChangeSet: Map<number, OPERATION>) {
|
|
287
238
|
Array.from(allChangeSet.entries()).forEach(([index, op]) => {
|
|
288
|
-
// console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
|
|
289
239
|
if (index >= startIndex) {
|
|
290
240
|
allChangeSet.delete(index);
|
|
291
241
|
allChangeSet.set(index + shiftIndex, op);
|
|
@@ -452,21 +402,41 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
452
402
|
|
|
453
403
|
protected checkIsFiltered(parent: Ref, parentIndex: number) {
|
|
454
404
|
// Detect if current structure has "filters" declared
|
|
455
|
-
this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
|
|
405
|
+
this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
|
|
406
|
+
|
|
407
|
+
if (parent && !Metadata.isValidInstance(parent)) {
|
|
408
|
+
const parentChangeTree = parent[$changes];
|
|
409
|
+
parent = parentChangeTree.parent;
|
|
410
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const parentMetadata = parent?.['constructor']?.[Symbol.metadata];
|
|
456
414
|
|
|
457
|
-
|
|
415
|
+
this.isFiltered = (
|
|
416
|
+
parent &&
|
|
417
|
+
parentMetadata?.[-2]?.includes(parentIndex)
|
|
418
|
+
);
|
|
458
419
|
|
|
459
|
-
//
|
|
460
|
-
while (parent && !this.isFiltered) {
|
|
461
|
-
const metadata: Metadata = parent['constructor'][Symbol.metadata];
|
|
420
|
+
// this.isFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-4];
|
|
462
421
|
|
|
463
|
-
|
|
464
|
-
|
|
422
|
+
// // Detect if parent has "filters" declared
|
|
423
|
+
// while (parent && !this.isFiltered) {
|
|
424
|
+
// const metadata: Metadata = parent['constructor'][Symbol.metadata];
|
|
425
|
+
// // this.isFiltered = metadata?.[-4];
|
|
465
426
|
|
|
466
|
-
|
|
427
|
+
// const fieldName = metadata?.[parentIndex];
|
|
428
|
+
// const isParentOwned = metadata?.[fieldName]?.tag !== undefined;
|
|
429
|
+
// this.isFiltered = isParentOwned || parent[$changes].isFiltered; // metadata?.[-2]
|
|
467
430
|
|
|
468
|
-
|
|
469
|
-
};
|
|
431
|
+
// parent = parent[$changes].parent;
|
|
432
|
+
// };
|
|
433
|
+
|
|
434
|
+
// console.log("ChangeTree.checkIsFiltered", {
|
|
435
|
+
// parent: parent?.constructor.name,
|
|
436
|
+
// ref: this.ref.constructor.name,
|
|
437
|
+
// isFiltered: this.isFiltered,
|
|
438
|
+
// isPartiallyFiltered: this.isPartiallyFiltered,
|
|
439
|
+
// });
|
|
470
440
|
|
|
471
441
|
//
|
|
472
442
|
// TODO: refactor this!
|
|
@@ -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) {
|