@colyseus/schema 3.0.0-alpha.8 → 3.0.0

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 (150) hide show
  1. package/README.md +148 -62
  2. package/bin/schema-debug +94 -0
  3. package/build/cjs/index.js +2227 -1519
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/esm/index.mjs +2228 -1522
  6. package/build/esm/index.mjs.map +1 -1
  7. package/build/umd/index.js +2230 -1522
  8. package/lib/Metadata.d.ts +21 -9
  9. package/lib/Metadata.js +169 -32
  10. package/lib/Metadata.js.map +1 -1
  11. package/lib/Reflection.d.ts +19 -4
  12. package/lib/Reflection.js +66 -31
  13. package/lib/Reflection.js.map +1 -1
  14. package/lib/Schema.d.ts +12 -5
  15. package/lib/Schema.js +57 -56
  16. package/lib/Schema.js.map +1 -1
  17. package/lib/annotations.d.ts +31 -34
  18. package/lib/annotations.js +110 -160
  19. package/lib/annotations.js.map +1 -1
  20. package/lib/bench_encode.d.ts +1 -0
  21. package/lib/bench_encode.js +130 -0
  22. package/lib/bench_encode.js.map +1 -0
  23. package/lib/codegen/api.js +1 -2
  24. package/lib/codegen/api.js.map +1 -1
  25. package/lib/codegen/languages/cpp.js +1 -2
  26. package/lib/codegen/languages/cpp.js.map +1 -1
  27. package/lib/codegen/languages/csharp.js +9 -46
  28. package/lib/codegen/languages/csharp.js.map +1 -1
  29. package/lib/codegen/languages/haxe.js +4 -2
  30. package/lib/codegen/languages/haxe.js.map +1 -1
  31. package/lib/codegen/languages/java.js +1 -2
  32. package/lib/codegen/languages/java.js.map +1 -1
  33. package/lib/codegen/languages/js.js +1 -2
  34. package/lib/codegen/languages/js.js.map +1 -1
  35. package/lib/codegen/languages/lua.js +23 -25
  36. package/lib/codegen/languages/lua.js.map +1 -1
  37. package/lib/codegen/languages/ts.js +1 -2
  38. package/lib/codegen/languages/ts.js.map +1 -1
  39. package/lib/codegen/parser.js +85 -3
  40. package/lib/codegen/parser.js.map +1 -1
  41. package/lib/codegen/types.js +6 -3
  42. package/lib/codegen/types.js.map +1 -1
  43. package/lib/debug.d.ts +1 -0
  44. package/lib/debug.js +51 -0
  45. package/lib/debug.js.map +1 -0
  46. package/lib/decoder/DecodeOperation.d.ts +3 -4
  47. package/lib/decoder/DecodeOperation.js +35 -17
  48. package/lib/decoder/DecodeOperation.js.map +1 -1
  49. package/lib/decoder/Decoder.d.ts +5 -6
  50. package/lib/decoder/Decoder.js +10 -10
  51. package/lib/decoder/Decoder.js.map +1 -1
  52. package/lib/decoder/ReferenceTracker.js +4 -2
  53. package/lib/decoder/ReferenceTracker.js.map +1 -1
  54. package/lib/decoder/strategy/RawChanges.js +1 -2
  55. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  56. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  57. package/lib/decoder/strategy/StateCallbacks.js +74 -64
  58. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  59. package/lib/encoder/ChangeTree.d.ts +28 -20
  60. package/lib/encoder/ChangeTree.js +242 -188
  61. package/lib/encoder/ChangeTree.js.map +1 -1
  62. package/lib/encoder/EncodeOperation.d.ts +3 -6
  63. package/lib/encoder/EncodeOperation.js +51 -65
  64. package/lib/encoder/EncodeOperation.js.map +1 -1
  65. package/lib/encoder/Encoder.d.ts +8 -7
  66. package/lib/encoder/Encoder.js +133 -85
  67. package/lib/encoder/Encoder.js.map +1 -1
  68. package/lib/encoder/Root.d.ts +22 -0
  69. package/lib/encoder/Root.js +81 -0
  70. package/lib/encoder/Root.js.map +1 -0
  71. package/lib/encoder/StateView.d.ts +7 -7
  72. package/lib/encoder/StateView.js +72 -74
  73. package/lib/encoder/StateView.js.map +1 -1
  74. package/lib/encoding/assert.d.ts +7 -6
  75. package/lib/encoding/assert.js +13 -5
  76. package/lib/encoding/assert.js.map +1 -1
  77. package/lib/encoding/decode.d.ts +36 -19
  78. package/lib/encoding/decode.js +54 -84
  79. package/lib/encoding/decode.js.map +1 -1
  80. package/lib/encoding/encode.d.ts +36 -18
  81. package/lib/encoding/encode.js +61 -48
  82. package/lib/encoding/encode.js.map +1 -1
  83. package/lib/encoding/spec.d.ts +4 -5
  84. package/lib/encoding/spec.js +1 -2
  85. package/lib/encoding/spec.js.map +1 -1
  86. package/lib/index.d.ts +10 -9
  87. package/lib/index.js +24 -17
  88. package/lib/index.js.map +1 -1
  89. package/lib/types/HelperTypes.d.ts +34 -2
  90. package/lib/types/HelperTypes.js.map +1 -1
  91. package/lib/types/TypeContext.d.ts +29 -0
  92. package/lib/types/TypeContext.js +151 -0
  93. package/lib/types/TypeContext.js.map +1 -0
  94. package/lib/types/custom/ArraySchema.d.ts +2 -2
  95. package/lib/types/custom/ArraySchema.js +33 -22
  96. package/lib/types/custom/ArraySchema.js.map +1 -1
  97. package/lib/types/custom/CollectionSchema.d.ts +2 -2
  98. package/lib/types/custom/CollectionSchema.js +1 -0
  99. package/lib/types/custom/CollectionSchema.js.map +1 -1
  100. package/lib/types/custom/MapSchema.d.ts +18 -16
  101. package/lib/types/custom/MapSchema.js +12 -4
  102. package/lib/types/custom/MapSchema.js.map +1 -1
  103. package/lib/types/custom/SetSchema.d.ts +2 -2
  104. package/lib/types/custom/SetSchema.js +1 -0
  105. package/lib/types/custom/SetSchema.js.map +1 -1
  106. package/lib/types/registry.d.ts +8 -1
  107. package/lib/types/registry.js +23 -6
  108. package/lib/types/registry.js.map +1 -1
  109. package/lib/types/symbols.d.ts +8 -5
  110. package/lib/types/symbols.js +9 -6
  111. package/lib/types/symbols.js.map +1 -1
  112. package/lib/types/utils.js +1 -2
  113. package/lib/types/utils.js.map +1 -1
  114. package/lib/utils.js +9 -7
  115. package/lib/utils.js.map +1 -1
  116. package/package.json +19 -18
  117. package/src/Metadata.ts +190 -42
  118. package/src/Reflection.ts +76 -38
  119. package/src/Schema.ts +72 -70
  120. package/src/annotations.ts +156 -202
  121. package/src/bench_encode.ts +108 -0
  122. package/src/codegen/languages/csharp.ts +8 -47
  123. package/src/codegen/languages/haxe.ts +4 -0
  124. package/src/codegen/languages/lua.ts +19 -27
  125. package/src/codegen/parser.ts +107 -0
  126. package/src/codegen/types.ts +1 -0
  127. package/src/debug.ts +55 -0
  128. package/src/decoder/DecodeOperation.ts +43 -15
  129. package/src/decoder/Decoder.ts +12 -10
  130. package/src/decoder/ReferenceTracker.ts +5 -3
  131. package/src/decoder/strategy/StateCallbacks.ts +152 -81
  132. package/src/encoder/ChangeTree.ts +282 -209
  133. package/src/encoder/EncodeOperation.ts +78 -78
  134. package/src/encoder/Encoder.ts +157 -93
  135. package/src/encoder/Root.ts +93 -0
  136. package/src/encoder/StateView.ts +80 -88
  137. package/src/encoding/assert.ts +17 -8
  138. package/src/encoding/decode.ts +73 -93
  139. package/src/encoding/encode.ts +76 -45
  140. package/src/encoding/spec.ts +3 -5
  141. package/src/index.ts +12 -20
  142. package/src/types/HelperTypes.ts +54 -2
  143. package/src/types/TypeContext.ts +175 -0
  144. package/src/types/custom/ArraySchema.ts +49 -19
  145. package/src/types/custom/CollectionSchema.ts +1 -0
  146. package/src/types/custom/MapSchema.ts +30 -17
  147. package/src/types/custom/SetSchema.ts +1 -0
  148. package/src/types/registry.ts +22 -3
  149. package/src/types/symbols.ts +10 -7
  150. package/src/utils.ts +7 -3
@@ -4,9 +4,9 @@ import { Ref } from "../../encoder/ChangeTree";
4
4
  import { Decoder } from "../Decoder";
5
5
  import { DataChange } from "../DecodeOperation";
6
6
  import { OPERATION } from "../../encoding/spec";
7
- import { DefinitionType } from "../../annotations";
8
7
  import { Schema } from "../../Schema";
9
- import type { ArraySchema } from "../../types/custom/ArraySchema";
8
+ import type { DefinitionType } from "../../annotations";
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 GetProxyType<T> = unknown extends T // is "any"?
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
- onChange(callback: () => void): void;
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>]: GetProxyType<T[K]>;
61
+ [K in NonFunctionNonPrimitivePropNames<T>]: CallbackProxy<T[K]>;
36
62
  }
37
63
 
38
64
  type CollectionCallback<K, V> = {
39
- onAdd(callback: (item: V, index: K) => void, immediate?: boolean): void;
40
- onRemove(callback: (item: V, index: K) => void): void;
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
- export function getStateCallbacks(decoder: Decoder) {
98
+
99
+ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>): GetCallbackProxy {
52
100
  const $root = decoder.root;
53
101
  const callbacks = $root.callbacks;
54
102
 
55
- let isTriggeringOnAdd = false;
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
- try {
90
- // trigger onChange
91
- const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
92
- for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
93
- replaceCallbacks[i]();
94
- }
95
-
96
- } catch (e) {
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
- try {
102
- if ($callbacks.hasOwnProperty(change.field)) {
103
- const fieldCallbacks = $callbacks[change.field];
104
- for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
105
- fieldCallbacks[i](change.value, change.previousValue);
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 === OPERATION.ADD && change.previousValue === undefined) {
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(metadataOrType: Metadata | DefinitionType, context: CallContext) {
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 onAdd = function (
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
- !isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
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,28 +236,49 @@ 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 onAdd(context.instance, prop, callback, immediate);
239
+ return onAddListen(context.instance, prop, callback, immediate);
196
240
 
197
241
  } else {
198
242
  // collection instance not received yet
199
- context.onInstanceAvailable((ref: Ref, existing: boolean) =>
200
- onAdd(ref, prop, callback, immediate && existing));
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
- console.log("bindTo", targetObject, properties);
266
+ if (!properties) {
267
+ properties = Object.keys(metadata).map((index) => metadata[index as any as number].name);
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) {
216
- if (metadata[prop]) {
280
+ const metadataField = metadata[metadata[prop]];
281
+ if (metadataField) {
217
282
  const instance = context.instance?.[prop];
218
283
  const onInstanceAvailable: OnInstanceAvailableCallback = (
219
284
  (callback: (ref: Ref, existing: boolean) => void) => {
@@ -233,8 +298,9 @@ export function getStateCallbacks(decoder: Decoder) {
233
298
  }
234
299
  }
235
300
  );
236
- return getProxy(metadata[prop].type, {
237
- instance,
301
+ return getProxy(metadataField.type, {
302
+ // make sure refId is available, otherwise need to wait for the instance to be available.
303
+ instance: ($root.refIds.get(instance) && instance),
238
304
  parentInstance: context.instance,
239
305
  onInstanceAvailable,
240
306
  });
@@ -257,9 +323,16 @@ export function getStateCallbacks(decoder: Decoder) {
257
323
  const onAdd = function (ref: Ref, callback: (value: any, key: any) => void, immediate: boolean) {
258
324
  // Trigger callback on existing items
259
325
  if (immediate) {
260
- (ref as ArraySchema).forEach((v, k) => callback(v, k));
326
+ (ref as CollectionSchema).forEach((v, k) => callback(v, k));
261
327
  }
262
- return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, callback);
328
+
329
+ return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, (value, key) => {
330
+ onAddCalls.set(callback, true);
331
+ currentOnAddCallback = callback;
332
+ callback(value, key);
333
+ onAddCalls.delete(callback)
334
+ currentOnAddCallback = undefined;
335
+ });
263
336
  };
264
337
 
265
338
  const onRemove = function (ref: Ref, callback: (value: any, key: any) => void) {
@@ -272,25 +345,34 @@ export function getStateCallbacks(decoder: Decoder) {
272
345
  // https://github.com/colyseus/schema/issues/147
273
346
  // If parent instance has "onAdd" registered, avoid triggering immediate callback.
274
347
  //
275
- // FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
276
- //
277
- if (context.onInstanceAvailable) {
348
+
349
+ if (context.instance) {
350
+ return onAdd(context.instance, callback, immediate && !onAddCalls.has(currentOnAddCallback));
351
+
352
+ } else if (context.onInstanceAvailable) {
278
353
  // collection instance not received yet
279
- context.onInstanceAvailable((ref: Ref, existing: boolean) =>
280
- onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
354
+ let detachCallback = () => {};
281
355
 
282
- } else if (context.instance) {
283
- onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
356
+ context.onInstanceAvailable((ref: Ref, existing: boolean) => {
357
+ detachCallback = onAdd(ref, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
358
+ });
359
+
360
+ return () => detachCallback();
284
361
  }
285
362
  },
286
363
  onRemove: function(callback: (value, key) => void) {
287
364
  if (context.onInstanceAvailable) {
288
365
  // collection instance not received yet
289
- context.onInstanceAvailable((ref: Ref) =>
290
- onRemove(ref, callback));
366
+ let detachCallback = () => {};
367
+
368
+ context.onInstanceAvailable((ref: Ref) => {
369
+ detachCallback = onRemove(ref, callback)
370
+ });
371
+
372
+ return () => detachCallback();
291
373
 
292
374
  } else if (context.instance) {
293
- onRemove(context.instance, callback);
375
+ return onRemove(context.instance, callback);
294
376
  }
295
377
  },
296
378
  }, {
@@ -307,20 +389,9 @@ export function getStateCallbacks(decoder: Decoder) {
307
389
  }
308
390
  }
309
391
 
310
- function $<T>(instance: T): GetProxyType<T> {
311
- return getProxy(undefined, { instance }) as GetProxyType<T>;
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
- }
392
+ function $<T>(instance: T): CallbackProxy<T> {
393
+ return getProxy(undefined, { instance }) as CallbackProxy<T>;
324
394
  }
325
395
 
396
+ return $;
326
397
  }