@colyseus/schema 2.0.4 → 2.0.6

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 (107) hide show
  1. package/README.md +0 -4
  2. package/build/cjs/index.js +48 -48
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +130 -104
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +50 -50
  7. package/lib/Reflection.js +87 -119
  8. package/lib/Reflection.js.map +1 -1
  9. package/lib/Schema.js +195 -257
  10. package/lib/Schema.js.map +1 -1
  11. package/lib/annotations.d.ts +6 -6
  12. package/lib/annotations.js +64 -92
  13. package/lib/annotations.js.map +1 -1
  14. package/lib/changes/ChangeTree.d.ts +1 -1
  15. package/lib/changes/ChangeTree.js +63 -70
  16. package/lib/changes/ChangeTree.js.map +1 -1
  17. package/lib/changes/ReferenceTracker.js +24 -27
  18. package/lib/changes/ReferenceTracker.js.map +1 -1
  19. package/lib/codegen/api.js +9 -9
  20. package/lib/codegen/api.js.map +1 -1
  21. package/lib/codegen/argv.d.ts +1 -1
  22. package/lib/codegen/argv.js +11 -11
  23. package/lib/codegen/argv.js.map +1 -1
  24. package/lib/codegen/cli.js +21 -10
  25. package/lib/codegen/cli.js.map +1 -1
  26. package/lib/codegen/languages/cpp.js +126 -77
  27. package/lib/codegen/languages/cpp.js.map +1 -1
  28. package/lib/codegen/languages/csharp.js +121 -62
  29. package/lib/codegen/languages/csharp.js.map +1 -1
  30. package/lib/codegen/languages/haxe.js +34 -26
  31. package/lib/codegen/languages/haxe.js.map +1 -1
  32. package/lib/codegen/languages/java.js +39 -27
  33. package/lib/codegen/languages/java.js.map +1 -1
  34. package/lib/codegen/languages/js.js +48 -32
  35. package/lib/codegen/languages/js.js.map +1 -1
  36. package/lib/codegen/languages/lua.js +35 -24
  37. package/lib/codegen/languages/lua.js.map +1 -1
  38. package/lib/codegen/languages/ts.js +63 -68
  39. package/lib/codegen/languages/ts.js.map +1 -1
  40. package/lib/codegen/parser.d.ts +9 -1
  41. package/lib/codegen/parser.js +88 -46
  42. package/lib/codegen/parser.js.map +1 -1
  43. package/lib/codegen/types.d.ts +8 -0
  44. package/lib/codegen/types.js +64 -54
  45. package/lib/codegen/types.js.map +1 -1
  46. package/lib/encoding/decode.js +15 -15
  47. package/lib/encoding/decode.js.map +1 -1
  48. package/lib/encoding/encode.js +14 -14
  49. package/lib/encoding/encode.js.map +1 -1
  50. package/lib/events/EventEmitter.d.ts +1 -1
  51. package/lib/events/EventEmitter.js +16 -47
  52. package/lib/events/EventEmitter.js.map +1 -1
  53. package/lib/filters/index.js +7 -8
  54. package/lib/filters/index.js.map +1 -1
  55. package/lib/index.js +11 -11
  56. package/lib/index.js.map +1 -1
  57. package/lib/types/ArraySchema.d.ts +1 -1
  58. package/lib/types/ArraySchema.js +161 -219
  59. package/lib/types/ArraySchema.js.map +1 -1
  60. package/lib/types/CollectionSchema.d.ts +1 -1
  61. package/lib/types/CollectionSchema.js +63 -71
  62. package/lib/types/CollectionSchema.js.map +1 -1
  63. package/lib/types/HelperTypes.d.ts +9 -9
  64. package/lib/types/MapSchema.d.ts +16 -16
  65. package/lib/types/MapSchema.js +68 -78
  66. package/lib/types/MapSchema.js.map +1 -1
  67. package/lib/types/SetSchema.js +62 -71
  68. package/lib/types/SetSchema.js.map +1 -1
  69. package/lib/types/index.js +1 -1
  70. package/lib/types/index.js.map +1 -1
  71. package/lib/types/typeRegistry.js +1 -1
  72. package/lib/types/typeRegistry.js.map +1 -1
  73. package/lib/types/utils.js +9 -10
  74. package/lib/types/utils.js.map +1 -1
  75. package/lib/utils.js +10 -13
  76. package/lib/utils.js.map +1 -1
  77. package/package.json +18 -15
  78. package/src/Reflection.ts +159 -0
  79. package/src/Schema.ts +1024 -0
  80. package/src/annotations.ts +400 -0
  81. package/src/changes/ChangeTree.ts +295 -0
  82. package/src/changes/ReferenceTracker.ts +81 -0
  83. package/src/codegen/api.ts +46 -0
  84. package/src/codegen/argv.ts +40 -0
  85. package/src/codegen/cli.ts +65 -0
  86. package/src/codegen/languages/cpp.ts +297 -0
  87. package/src/codegen/languages/csharp.ts +208 -0
  88. package/src/codegen/languages/haxe.ts +110 -0
  89. package/src/codegen/languages/java.ts +115 -0
  90. package/src/codegen/languages/js.ts +115 -0
  91. package/src/codegen/languages/lua.ts +125 -0
  92. package/src/codegen/languages/ts.ts +129 -0
  93. package/src/codegen/parser.ts +299 -0
  94. package/src/codegen/types.ts +177 -0
  95. package/src/encoding/decode.ts +278 -0
  96. package/src/encoding/encode.ts +283 -0
  97. package/src/filters/index.ts +23 -0
  98. package/src/index.ts +59 -0
  99. package/src/spec.ts +49 -0
  100. package/src/types/ArraySchema.ts +612 -0
  101. package/src/types/CollectionSchema.ts +199 -0
  102. package/src/types/HelperTypes.ts +34 -0
  103. package/src/types/MapSchema.ts +268 -0
  104. package/src/types/SetSchema.ts +208 -0
  105. package/src/types/typeRegistry.ts +19 -0
  106. package/src/types/utils.ts +62 -0
  107. package/src/utils.ts +28 -0
@@ -0,0 +1,199 @@
1
+ import { ChangeTree } from "../changes/ChangeTree";
2
+ import { OPERATION } from "../spec";
3
+ import { SchemaDecoderCallbacks } from "../Schema";
4
+ import { addCallback, removeChildRefs } from "./utils";
5
+ import { DataChange } from "..";
6
+
7
+ type K = number; // TODO: allow to specify K generic on MapSchema.
8
+
9
+ export class CollectionSchema<V=any> implements SchemaDecoderCallbacks {
10
+ protected $changes: ChangeTree = new ChangeTree(this);
11
+
12
+ protected $items: Map<number, V> = new Map<number, V>();
13
+ protected $indexes: Map<number, number> = new Map<number, number>();
14
+
15
+ protected $refId: number = 0;
16
+
17
+ //
18
+ // Decoding callbacks
19
+ //
20
+ public $callbacks: { [operation: number]: Array<(item: V, key: string) => void> };
21
+ public onAdd(callback: (item: V, key: string) => void, triggerAll: boolean = true) {
22
+ return addCallback(
23
+ (this.$callbacks || (this.$callbacks = [])),
24
+ OPERATION.ADD,
25
+ callback,
26
+ (triggerAll)
27
+ ? this.$items
28
+ : undefined
29
+ );
30
+ }
31
+ public onRemove(callback: (item: V, key: string) => void) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
32
+ public onChange(callback: (item: V, key: string) => void) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
33
+
34
+ static is(type: any) {
35
+ return type['collection'] !== undefined;
36
+ }
37
+
38
+ constructor (initialValues?: Array<V>) {
39
+ if (initialValues) {
40
+ initialValues.forEach((v) => this.add(v));
41
+ }
42
+ }
43
+
44
+ add(value: V) {
45
+ // set "index" for reference.
46
+ const index = this.$refId++;
47
+
48
+ const isRef = (value['$changes']) !== undefined;
49
+ if (isRef) {
50
+ (value['$changes'] as ChangeTree).setParent(this, this.$changes.root, index);
51
+ }
52
+
53
+ this.$changes.indexes[index] = index;
54
+
55
+ this.$indexes.set(index, index);
56
+ this.$items.set(index, value);
57
+
58
+ this.$changes.change(index);
59
+
60
+ return index;
61
+ }
62
+
63
+ at(index: number): V | undefined {
64
+ const key = Array.from(this.$items.keys())[index];
65
+ return this.$items.get(key);
66
+ }
67
+
68
+ entries() {
69
+ return this.$items.entries();
70
+ }
71
+
72
+ delete(item: V) {
73
+ const entries = this.$items.entries();
74
+
75
+ let index: K;
76
+ let entry: IteratorResult<[number, V]>;
77
+ while (entry = entries.next()) {
78
+ if (entry.done) { break; }
79
+
80
+ if (item === entry.value[1]) {
81
+ index = entry.value[0];
82
+ break;
83
+ }
84
+ }
85
+
86
+ if (index === undefined) {
87
+ return false;
88
+ }
89
+
90
+ this.$changes.delete(index);
91
+ this.$indexes.delete(index);
92
+
93
+ return this.$items.delete(index);
94
+ }
95
+
96
+ clear(changes?: DataChange[]) {
97
+ // discard previous operations.
98
+ this.$changes.discard(true, true);
99
+ this.$changes.indexes = {};
100
+
101
+ // clear previous indexes
102
+ this.$indexes.clear();
103
+
104
+ //
105
+ // When decoding:
106
+ // - enqueue items for DELETE callback.
107
+ // - flag child items for garbage collection.
108
+ //
109
+ if (changes) {
110
+ removeChildRefs.call(this, changes);
111
+ }
112
+
113
+ // clear items
114
+ this.$items.clear();
115
+
116
+ this.$changes.operation({ index: 0, op: OPERATION.CLEAR });
117
+
118
+ // touch all structures until reach root
119
+ this.$changes.touchParents();
120
+ }
121
+
122
+ has (value: V): boolean {
123
+ return Array.from(this.$items.values()).some((v) => v === value);
124
+ }
125
+
126
+ forEach(callbackfn: (value: V, key: K, collection: CollectionSchema<V>) => void) {
127
+ this.$items.forEach((value, key, _) => callbackfn(value, key, this));
128
+ }
129
+
130
+ values() {
131
+ return this.$items.values();
132
+ }
133
+
134
+ get size () {
135
+ return this.$items.size;
136
+ }
137
+
138
+ protected setIndex(index: number, key: number) {
139
+ this.$indexes.set(index, key);
140
+ }
141
+
142
+ protected getIndex(index: number) {
143
+ return this.$indexes.get(index);
144
+ }
145
+
146
+ protected getByIndex(index: number) {
147
+ return this.$items.get(this.$indexes.get(index));
148
+ }
149
+
150
+ protected deleteByIndex(index: number) {
151
+ const key = this.$indexes.get(index);
152
+ this.$items.delete(key);
153
+ this.$indexes.delete(index);
154
+ }
155
+
156
+ toArray() {
157
+ return Array.from(this.$items.values());
158
+ }
159
+
160
+ toJSON() {
161
+ const values: V[] = [];
162
+
163
+ this.forEach((value, key) => {
164
+ values.push(
165
+ (typeof (value['toJSON']) === "function")
166
+ ? value['toJSON']()
167
+ : value
168
+ );
169
+ });
170
+
171
+ return values;
172
+ }
173
+
174
+ //
175
+ // Decoding utilities
176
+ //
177
+ clone(isDecoding?: boolean): CollectionSchema<V> {
178
+ let cloned: CollectionSchema;
179
+
180
+ if (isDecoding) {
181
+ // client-side
182
+ cloned = Object.assign(new CollectionSchema(), this);
183
+
184
+ } else {
185
+ // server-side
186
+ cloned = new CollectionSchema();
187
+ this.forEach((value) => {
188
+ if (value['$changes']) {
189
+ cloned.add(value['clone']());
190
+ } else {
191
+ cloned.add(value);
192
+ }
193
+ })
194
+ }
195
+
196
+ return cloned;
197
+ }
198
+
199
+ }
@@ -0,0 +1,34 @@
1
+ type Bool = 'true' | 'false'
2
+ type Key = string | number | symbol;
3
+
4
+ type Not<X extends Bool> = {
5
+ true: 'false',
6
+ false: 'true'
7
+ }[X]
8
+
9
+ type HaveIntersection<S1 extends string, S2 extends string> = (
10
+ { [K in S1]: 'true' } &
11
+ { [key: string]: 'false' }
12
+ )[S2]
13
+
14
+ type IsNeverWorker<S extends Key> = (
15
+ { [K in S]: 'false' } &
16
+ { [key: string]: 'true' }
17
+ )[S]
18
+
19
+ // Worker needed because of https://github.com/Microsoft/TypeScript/issues/18118
20
+ type IsNever<T extends Key> = Not<HaveIntersection<IsNeverWorker<T>, 'false'>>
21
+
22
+ type IsFunction<T> = IsNever<keyof T>
23
+
24
+ export type NonFunctionProps<T> = {
25
+ [K in keyof T]: {
26
+ 'false': K,
27
+ 'true': never
28
+ }[IsFunction<T[K]>]
29
+ }[keyof T];
30
+
31
+ export type NonFunctionPropNames<T> = {
32
+ [K in keyof T]: T[K] extends Function ? never : K
33
+ }[keyof T];
34
+
@@ -0,0 +1,268 @@
1
+ import { SchemaDecoderCallbacks } from "../Schema";
2
+ import { addCallback, removeChildRefs } from "./utils";
3
+ import { DataChange } from "..";
4
+ import { ChangeTree } from "../changes/ChangeTree";
5
+ import { OPERATION } from "../spec";
6
+
7
+ export function getMapProxy(value: MapSchema) {
8
+ value['$proxy'] = true;
9
+
10
+ value = new Proxy(value, {
11
+ get: (obj, prop) => {
12
+ if (
13
+ typeof (prop) !== "symbol" && // accessing properties
14
+ typeof (obj[prop]) === "undefined"
15
+ ) {
16
+ return obj.get(prop as string);
17
+
18
+ } else {
19
+ return obj[prop];
20
+ }
21
+ },
22
+
23
+ set: (obj, prop, setValue) => {
24
+ if (
25
+ typeof (prop) !== "symbol" &&
26
+ (
27
+ (prop as string).indexOf("$") === -1 &&
28
+ prop !== "onAdd" &&
29
+ prop !== "onRemove" &&
30
+ prop !== "onChange"
31
+ )
32
+ ) {
33
+ obj.set(prop as string, setValue);
34
+
35
+ } else {
36
+ obj[prop] = setValue;
37
+ }
38
+ return true;
39
+ },
40
+
41
+ deleteProperty: (obj, prop) => {
42
+ obj.delete(prop as string);
43
+ return true;
44
+ },
45
+ });
46
+
47
+ return value;
48
+ }
49
+
50
+ export class MapSchema<V=any, K extends string = string> implements Map<K, V>, SchemaDecoderCallbacks {
51
+ protected $changes: ChangeTree = new ChangeTree(this);
52
+
53
+ protected $items: Map<K, V> = new Map<K, V>();
54
+ protected $indexes: Map<number, K> = new Map<number, K>();
55
+
56
+ protected $refId: number = 0;
57
+
58
+ //
59
+ // Decoding callbacks
60
+ //
61
+ public $callbacks: { [operation: number]: Array<(item: V, key: string) => void> };
62
+ public onAdd(callback: (item: V, key: string) => void, triggerAll: boolean = true) {
63
+ return addCallback(
64
+ (this.$callbacks || (this.$callbacks = [])),
65
+ OPERATION.ADD,
66
+ callback,
67
+ (triggerAll)
68
+ ? this.$items
69
+ : undefined
70
+ );
71
+ }
72
+ public onRemove(callback: (item: V, key: string) => void) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
73
+ public onChange(callback: (item: V, key: string) => void) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
74
+
75
+ static is(type: any) {
76
+ return type['map'] !== undefined;
77
+ }
78
+
79
+ constructor (initialValues?: Map<K, V> | Record<K, V>) {
80
+ if (initialValues) {
81
+ if (
82
+ initialValues instanceof Map ||
83
+ initialValues instanceof MapSchema
84
+ ) {
85
+ initialValues.forEach((v, k) => this.set(k, v));
86
+
87
+ } else {
88
+ for (const k in initialValues) {
89
+ this.set(k, initialValues[k]);
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ /** Iterator */
96
+ [Symbol.iterator](): IterableIterator<[K, V]> { return this.$items[Symbol.iterator](); }
97
+ get [Symbol.toStringTag]() { return this.$items[Symbol.toStringTag] }
98
+
99
+ set(key: K, value: V) {
100
+ if (value === undefined || value === null) {
101
+ throw new Error(`MapSchema#set('${key}', ${value}): trying to set ${value} value on '${key}'.`);
102
+ }
103
+
104
+ // get "index" for this value.
105
+ const hasIndex = typeof(this.$changes.indexes[key]) !== "undefined";
106
+ const index = (hasIndex)
107
+ ? this.$changes.indexes[key]
108
+ : this.$refId++;
109
+
110
+ let operation: OPERATION = (hasIndex)
111
+ ? OPERATION.REPLACE
112
+ : OPERATION.ADD;
113
+
114
+ const isRef = (value['$changes']) !== undefined;
115
+ if (isRef) {
116
+ (value['$changes'] as ChangeTree).setParent(
117
+ this,
118
+ this.$changes.root,
119
+ index
120
+ );
121
+ }
122
+
123
+ //
124
+ // (encoding)
125
+ // set a unique id to relate directly with this key/value.
126
+ //
127
+ if (!hasIndex) {
128
+ this.$changes.indexes[key] = index;
129
+ this.$indexes.set(index, key);
130
+
131
+ } else if (
132
+ isRef && // if is schema, force ADD operation if value differ from previous one.
133
+ this.$items.get(key) !== value
134
+ ) {
135
+ operation = OPERATION.ADD;
136
+ }
137
+
138
+ this.$items.set(key, value);
139
+
140
+ this.$changes.change(key, operation);
141
+
142
+ return this;
143
+ }
144
+
145
+ get(key: K): V | undefined {
146
+ return this.$items.get(key);
147
+ }
148
+
149
+ delete(key: K) {
150
+ //
151
+ // TODO: add a "purge" method after .encode() runs, to cleanup removed `$indexes`
152
+ //
153
+ // We don't remove $indexes to allow setting the same key in the same patch
154
+ // (See "should allow to remove and set an item in the same place" test)
155
+ //
156
+ // // const index = this.$changes.indexes[key];
157
+ // // this.$indexes.delete(index);
158
+
159
+ this.$changes.delete(key);
160
+ return this.$items.delete(key);
161
+ }
162
+
163
+ clear(changes?: DataChange[]) {
164
+ // discard previous operations.
165
+ this.$changes.discard(true, true);
166
+ this.$changes.indexes = {};
167
+
168
+ // clear previous indexes
169
+ this.$indexes.clear();
170
+
171
+ //
172
+ // When decoding:
173
+ // - enqueue items for DELETE callback.
174
+ // - flag child items for garbage collection.
175
+ //
176
+ if (changes) {
177
+ removeChildRefs.call(this, changes);
178
+ }
179
+
180
+ // clear items
181
+ this.$items.clear();
182
+
183
+ this.$changes.operation({ index: 0, op: OPERATION.CLEAR });
184
+
185
+ // touch all structures until reach root
186
+ this.$changes.touchParents();
187
+ }
188
+
189
+ has (key: K) {
190
+ return this.$items.has(key);
191
+ }
192
+
193
+ forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void) {
194
+ this.$items.forEach(callbackfn);
195
+ }
196
+
197
+ entries () {
198
+ return this.$items.entries();
199
+ }
200
+
201
+ keys () {
202
+ return this.$items.keys();
203
+ }
204
+
205
+ values() {
206
+ return this.$items.values();
207
+ }
208
+
209
+ get size () {
210
+ return this.$items.size;
211
+ }
212
+
213
+ protected setIndex(index: number, key: K) {
214
+ this.$indexes.set(index, key);
215
+ }
216
+
217
+ protected getIndex(index: number) {
218
+ return this.$indexes.get(index);
219
+ }
220
+
221
+ protected getByIndex(index: number) {
222
+ return this.$items.get(this.$indexes.get(index));
223
+ }
224
+
225
+ protected deleteByIndex(index: number) {
226
+ const key = this.$indexes.get(index);
227
+ this.$items.delete(key);
228
+ this.$indexes.delete(index);
229
+ }
230
+
231
+ toJSON() {
232
+ const map: any = {};
233
+
234
+ this.forEach((value, key) => {
235
+ map[key] = (typeof (value['toJSON']) === "function")
236
+ ? value['toJSON']()
237
+ : value;
238
+ });
239
+
240
+ return map;
241
+ }
242
+
243
+ //
244
+ // Decoding utilities
245
+ //
246
+ clone(isDecoding?: boolean): MapSchema<V> {
247
+ let cloned: MapSchema;
248
+
249
+ if (isDecoding) {
250
+ // client-side
251
+ cloned = Object.assign(new MapSchema(), this);
252
+
253
+ } else {
254
+ // server-side
255
+ cloned = new MapSchema();
256
+ this.forEach((value, key) => {
257
+ if (value['$changes']) {
258
+ cloned.set(key, value['clone']());
259
+ } else {
260
+ cloned.set(key, value);
261
+ }
262
+ })
263
+ }
264
+
265
+ return cloned;
266
+ }
267
+
268
+ }
@@ -0,0 +1,208 @@
1
+ import { ChangeTree } from "../changes/ChangeTree";
2
+ import { OPERATION } from "../spec";
3
+ import { SchemaDecoderCallbacks } from "../Schema";
4
+ import { addCallback, removeChildRefs } from "./utils";
5
+ import { DataChange } from "..";
6
+
7
+ export class SetSchema<V=any> implements SchemaDecoderCallbacks {
8
+ protected $changes: ChangeTree = new ChangeTree(this);
9
+
10
+ protected $items: Map<number, V> = new Map<number, V>();
11
+ protected $indexes: Map<number, number> = new Map<number, number>();
12
+
13
+ protected $refId: number = 0;
14
+
15
+ //
16
+ // Decoding callbacks
17
+ //
18
+ public $callbacks: { [operation: number]: Array<(item: V, key: string) => void> };
19
+ public onAdd(callback: (item: V, key: string) => void, triggerAll: boolean = true) {
20
+ return addCallback(
21
+ (this.$callbacks || (this.$callbacks = [])),
22
+ OPERATION.ADD,
23
+ callback,
24
+ (triggerAll)
25
+ ? this.$items
26
+ : undefined
27
+ );
28
+ }
29
+ public onRemove(callback: (item: V, key: string) => void) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
30
+ public onChange(callback: (item: V, key: string) => void) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
31
+
32
+ static is(type: any) {
33
+ return type['set'] !== undefined;
34
+ }
35
+
36
+ constructor (initialValues?: Array<V>) {
37
+ if (initialValues) {
38
+ initialValues.forEach((v) => this.add(v));
39
+ }
40
+ }
41
+
42
+ add(value: V) {
43
+ // immediatelly return false if value already added.
44
+ if (this.has(value)) { return false; }
45
+
46
+ // set "index" for reference.
47
+ const index = this.$refId++;
48
+
49
+ if ((value['$changes']) !== undefined) {
50
+ (value['$changes'] as ChangeTree).setParent(this, this.$changes.root, index);
51
+ }
52
+
53
+ const operation = this.$changes.indexes[index]?.op ?? OPERATION.ADD;
54
+
55
+ this.$changes.indexes[index] = index;
56
+
57
+ this.$indexes.set(index, index);
58
+ this.$items.set(index, value);
59
+
60
+ this.$changes.change(index, operation);
61
+ return index;
62
+ }
63
+
64
+ entries () {
65
+ return this.$items.entries();
66
+ }
67
+
68
+ delete(item: V) {
69
+ const entries = this.$items.entries();
70
+
71
+ let index: number;
72
+ let entry: IteratorResult<[number, V]>;
73
+ while (entry = entries.next()) {
74
+ if (entry.done) { break; }
75
+
76
+ if (item === entry.value[1]) {
77
+ index = entry.value[0];
78
+ break;
79
+ }
80
+ }
81
+
82
+ if (index === undefined) {
83
+ return false;
84
+ }
85
+
86
+ this.$changes.delete(index);
87
+ this.$indexes.delete(index);
88
+
89
+ return this.$items.delete(index);
90
+ }
91
+
92
+ clear(changes?: DataChange[]) {
93
+ // discard previous operations.
94
+ this.$changes.discard(true, true);
95
+ this.$changes.indexes = {};
96
+
97
+ // clear previous indexes
98
+ this.$indexes.clear();
99
+
100
+ //
101
+ // When decoding:
102
+ // - enqueue items for DELETE callback.
103
+ // - flag child items for garbage collection.
104
+ //
105
+ if (changes) {
106
+ removeChildRefs.call(this, changes);
107
+ }
108
+
109
+ // clear items
110
+ this.$items.clear();
111
+
112
+ this.$changes.operation({ index: 0, op: OPERATION.CLEAR });
113
+
114
+ // touch all structures until reach root
115
+ this.$changes.touchParents();
116
+ }
117
+
118
+ has (value: V): boolean {
119
+ const values = this.$items.values();
120
+
121
+ let has = false;
122
+ let entry: IteratorResult<V>;
123
+
124
+ while (entry = values.next()) {
125
+ if (entry.done) { break; }
126
+ if (value === entry.value) {
127
+ has = true;
128
+ break;
129
+ }
130
+ }
131
+
132
+ return has;
133
+ }
134
+
135
+ forEach(callbackfn: (value: V, key: number, collection: SetSchema<V>) => void) {
136
+ this.$items.forEach((value, key, _) => callbackfn(value, key, this));
137
+ }
138
+
139
+ values() {
140
+ return this.$items.values();
141
+ }
142
+
143
+ get size () {
144
+ return this.$items.size;
145
+ }
146
+
147
+ protected setIndex(index: number, key: number) {
148
+ this.$indexes.set(index, key);
149
+ }
150
+
151
+ protected getIndex(index: number) {
152
+ return this.$indexes.get(index);
153
+ }
154
+
155
+ protected getByIndex(index: number) {
156
+ return this.$items.get(this.$indexes.get(index));
157
+ }
158
+
159
+ protected deleteByIndex(index: number) {
160
+ const key = this.$indexes.get(index);
161
+ this.$items.delete(key);
162
+ this.$indexes.delete(index);
163
+ }
164
+
165
+ toArray() {
166
+ return Array.from(this.$items.values());
167
+ }
168
+
169
+ toJSON() {
170
+ const values: V[] = [];
171
+
172
+ this.forEach((value, key) => {
173
+ values.push(
174
+ (typeof (value['toJSON']) === "function")
175
+ ? value['toJSON']()
176
+ : value
177
+ );
178
+ });
179
+
180
+ return values;
181
+ }
182
+
183
+ //
184
+ // Decoding utilities
185
+ //
186
+ clone(isDecoding?: boolean): SetSchema<V> {
187
+ let cloned: SetSchema;
188
+
189
+ if (isDecoding) {
190
+ // client-side
191
+ cloned = Object.assign(new SetSchema(), this);
192
+
193
+ } else {
194
+ // server-side
195
+ cloned = new SetSchema();
196
+ this.forEach((value) => {
197
+ if (value['$changes']) {
198
+ cloned.add(value['clone']());
199
+ } else {
200
+ cloned.add(value);
201
+ }
202
+ })
203
+ }
204
+
205
+ return cloned;
206
+ }
207
+
208
+ }