dyo-tools 0.1.0-rc1

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 (78) hide show
  1. package/.c8rc.json +4 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc.json +41 -0
  4. package/Makefile +34 -0
  5. package/README.md +0 -0
  6. package/TODO.md +18 -0
  7. package/babel.config.js +1 -0
  8. package/dist/core/DTBunch.d.ts +32 -0
  9. package/dist/core/DTBunch.js +283 -0
  10. package/dist/core/DTBunch.js.map +1 -0
  11. package/dist/core/DTComponent.d.ts +20 -0
  12. package/dist/core/DTComponent.js +41 -0
  13. package/dist/core/DTComponent.js.map +1 -0
  14. package/dist/core/DTComponentWithMeta.d.ts +9 -0
  15. package/dist/core/DTComponentWithMeta.js +32 -0
  16. package/dist/core/DTComponentWithMeta.js.map +1 -0
  17. package/dist/core/DTElement.d.ts +13 -0
  18. package/dist/core/DTElement.js +46 -0
  19. package/dist/core/DTElement.js.map +1 -0
  20. package/dist/core/DTError.d.ts +13 -0
  21. package/dist/core/DTError.js +28 -0
  22. package/dist/core/DTError.js.map +1 -0
  23. package/dist/core/DTPlayer.d.ts +8 -0
  24. package/dist/core/DTPlayer.js +30 -0
  25. package/dist/core/DTPlayer.js.map +1 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/index.js +16 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/tsconfig.tsbuildinfo +1321 -0
  30. package/dist/types/index.d.ts +58 -0
  31. package/dist/types/index.js +15 -0
  32. package/dist/types/index.js.map +1 -0
  33. package/dist/utils/filters.d.ts +6 -0
  34. package/dist/utils/filters.js +39 -0
  35. package/dist/utils/filters.js.map +1 -0
  36. package/docs/.nojekyll +1 -0
  37. package/docs/assets/highlight.css +22 -0
  38. package/docs/assets/icons.css +1043 -0
  39. package/docs/assets/icons.png +0 -0
  40. package/docs/assets/icons@2x.png +0 -0
  41. package/docs/assets/main.js +52 -0
  42. package/docs/assets/search.js +1 -0
  43. package/docs/assets/style.css +1388 -0
  44. package/docs/assets/widgets.png +0 -0
  45. package/docs/assets/widgets@2x.png +0 -0
  46. package/docs/classes/DTBunch.html +265 -0
  47. package/docs/classes/DTComponent.html +49 -0
  48. package/docs/classes/DTComponentWithMeta.html +73 -0
  49. package/docs/classes/DTElement.html +95 -0
  50. package/docs/classes/DTError.html +32 -0
  51. package/docs/classes/DTPlayer.html +86 -0
  52. package/docs/index.html +1 -0
  53. package/docs/modules.html +1 -0
  54. package/jest.config.js +6 -0
  55. package/package.json +41 -0
  56. package/src/core/DTBunch.ts +600 -0
  57. package/src/core/DTComponent.ts +135 -0
  58. package/src/core/DTComponentWithMeta.ts +62 -0
  59. package/src/core/DTElement.ts +96 -0
  60. package/src/core/DTError.ts +78 -0
  61. package/src/core/DTPlayer.ts +57 -0
  62. package/src/index.ts +7 -0
  63. package/src/types/index.ts +76 -0
  64. package/src/utils/filters.ts +64 -0
  65. package/test/core/DTBunch.double.ts +150 -0
  66. package/test/core/DTBunch.spec.ts +1374 -0
  67. package/test/core/DTComponent.double.ts +69 -0
  68. package/test/core/DTComponent.spec.ts +182 -0
  69. package/test/core/DTComponentWithMeta.double.ts +88 -0
  70. package/test/core/DTComponentWithMeta.spec.ts +112 -0
  71. package/test/core/DTElement.double.ts +27 -0
  72. package/test/core/DTElement.spec.ts +181 -0
  73. package/test/core/DTError.double.ts +43 -0
  74. package/test/core/DTError.spec.ts +106 -0
  75. package/test/core/DTPlayer.double.ts +49 -0
  76. package/test/core/DTPlayer.spec.ts +102 -0
  77. package/test/utils/filters.spec.ts +109 -0
  78. package/tsconfig.json +21 -0
@@ -0,0 +1,600 @@
1
+ import DYOToolsComponentWithMeta from './DTComponentWithMeta';
2
+ import DYOToolsElement from './DTElement';
3
+ import {
4
+ DTAcceptedMetaData,
5
+ DTBunchFilters, DTBunchFilterWithBaseOperator, DTBunchFilterWithMetaOperator,
6
+ DTBunchOptionsConstructor,
7
+ DTBunchOptionsEditable,
8
+ DTBunchToObject,
9
+ FilterOperatorType, StandardPrimitiveType,
10
+ } from '../types';
11
+ import DYOToolsPlayer from './DTPlayer';
12
+ import DYOToolsError from './DTError';
13
+ import { validFiltersForItem } from '../utils/filters';
14
+
15
+ export default class DYOToolsBunch<
16
+ IBunchItem extends DYOToolsElement<DTAcceptedMetaData>,
17
+ IComponentMeta extends DTAcceptedMetaData,
18
+ > extends DYOToolsComponentWithMeta<IComponentMeta> {
19
+ /**
20
+ * Defining component type to "bunch".
21
+ */
22
+ protected _componentType = 'bunch';
23
+
24
+ /**
25
+ * DTPlayer instance who owns the current bunch.
26
+ */
27
+ protected _owner?: DYOToolsPlayer<DTAcceptedMetaData>;
28
+
29
+ /**
30
+ * Ordered Array of DTElement instance managed by the bunch.
31
+ */
32
+ protected _items: IBunchItem[];
33
+
34
+ /**
35
+ * All global option configuration for the current bunch.
36
+ *
37
+ * Options can be :
38
+ * * **errors** : Default *false*. If *true*, no exception is thrown when an error occurred, a new DTError instance is
39
+ * added to the _errors property array instead. If *false*, throw the exception with a DTError instance.
40
+ * * **uniqueKey** : Default *false*. If *true*, an error occurred when adding a new DTElement with the same key of an
41
+ * existing element into the bunch.
42
+ * * **inheritOwner** : Default *false*. If *true*, when a new DTElement is added, the owner of this element becomes
43
+ * automatically the current bunch owner.
44
+ * * **virtualContext** : Default *false*. If *true*, the context is not changed when a new DTElement is added.
45
+ * If *false*, when a new DTElement is added, the context of this element becomes automatically the current bunch instance
46
+ * and the element is removed from the old context Component (if defined).
47
+ * * **replaceIndex** : Default *false*. If *true*, when a new DTElement is added at existing index (using **addAtIndex**
48
+ * or **addManyAtIndex** method), this component replaces the old one. If *false*, this component is added at the specified
49
+ * index and other existing component are reindexed with the following index.
50
+ *
51
+ */
52
+ protected _globalOptions: DTBunchOptionsConstructor;
53
+
54
+ /**
55
+ * Array of DTError occurred during bunch instance execution, ordered by time.
56
+ * Only set if **errors** option is true.
57
+ */
58
+ protected _errors: DYOToolsError[];
59
+
60
+ /**
61
+ * Applying the parent constructor, and execute following process steps :
62
+ * * Add **items** to the bunch instance (using adding specifications).
63
+ * * Merge specific **options** configuration with default in _globalOptions.
64
+ *
65
+ * @see [addAtIndex](#addAtIndex) method for adding specifications.
66
+ * @param key
67
+ * @param items Array of DTElement instance to add. Default empty array.
68
+ * @param options Specific options configuration for the instance. Default empty object.
69
+ */
70
+ constructor(key?: string, items: IBunchItem[] = [], options: Partial<DTBunchOptionsConstructor> = {}) {
71
+ super(key);
72
+
73
+ const defaultOptions: DTBunchOptionsConstructor = {
74
+ errors: false,
75
+ uniqueKey: false,
76
+ inheritOwner: false,
77
+ replaceIndex: false,
78
+ virtualContext: false,
79
+ };
80
+ this._globalOptions = { ...defaultOptions, ...options };
81
+ this._errors = [];
82
+
83
+ this._items = [];
84
+ if (items && items.length > 0) {
85
+ this.addMany(items);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Getter for _owner property.
91
+ */
92
+ getOwner(): DYOToolsPlayer<DTAcceptedMetaData> {
93
+ return this._owner;
94
+ }
95
+
96
+ /**
97
+ * Setter for _owner property.
98
+ */
99
+ setOwner(value: DYOToolsPlayer<DTAcceptedMetaData>): void {
100
+ this._owner = value;
101
+
102
+ // Update owner elements
103
+ const { inheritOwner } = this._globalOptions;
104
+ if (inheritOwner) {
105
+ this._items.forEach((item) => { item.setOwner(this.getOwner()); });
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Remove the current owner of bunch.
111
+ */
112
+ removeOwner(): void {
113
+ this._owner = undefined;
114
+
115
+ // Update owner elements
116
+ const { inheritOwner } = this._globalOptions;
117
+ if (inheritOwner) {
118
+ this._items.forEach((item) => { item.removeOwner(); });
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Getter for _errors property.
124
+ */
125
+ getErrors(): DYOToolsError[] {
126
+ return this._errors;
127
+ }
128
+
129
+ /**
130
+ * Return the last error (most recent) of the current bunch. Undefined if _errors is empty.
131
+ */
132
+ getLastError(): DYOToolsError | undefined {
133
+ return this._errors.length > 0 ? this._errors[this._errors.length - 1] : undefined;
134
+ }
135
+
136
+ /**
137
+ * Add an element **item** as the last element into _items property array.
138
+ *
139
+ * @see [addAtIndex](#addAtIndex) method for adding specifications.
140
+ * @param item A DTElement instance to add into the bunch.
141
+ * @param options Optional Bunch option configuration object to apply only for this method execution. Options are not
142
+ * saved in current _globalOptions property. Available Options are : **uniqueKey**, **inheritOwner**, **replaceIndex**
143
+ * and **errors**.
144
+ */
145
+ add(item: IBunchItem, options: Partial<DTBunchOptionsEditable> = {}): void {
146
+ this.addAtIndex(item, this._items.length, options);
147
+ }
148
+
149
+ /**
150
+ * Add an element **item** at specified **index** into _items property array.
151
+ *
152
+ * The adding process has following specifications :
153
+ * * If the added item has the same _id than existing item, an error occurred (depending on **errors** option).
154
+ * * Option **uniqueKey** = *true*. If the added item has the same _key than existing item,
155
+ * an error occurred (depending on **errors** option).
156
+ * * Option **inheritOwner** = *true*. When the new item is added, its owner is replaced by the current bunch owner.
157
+ * * Option **virtualContext** = *false*. When the new item is added, its context is replaced by the current bunch
158
+ * instance. The item is removed from the old context.
159
+ * * If an item already exists at the specified index, the new item is added at the index, and following items are
160
+ * automatically affected at next indexes. If **replaceIndex** option is *true*, the new item replaces the former one
161
+ * at the index instead.
162
+ *
163
+ * @param item A DTElement instance to add into the bunch.
164
+ * @param index Index value where the item might be added. Must be a number between 0 and the current _items length.
165
+ * If not, the provided argument is automatically changed to 0 or current _items length.
166
+ * @param options Optional Bunch option configuration object to apply only for this method execution. Options are not
167
+ * saved in current _globalOptions property. Available Options are : **uniqueKey**, **inheritOwner**, **replaceIndex**
168
+ * and **errors**.
169
+ */
170
+ addAtIndex(item: IBunchItem, index: number, options: Partial<DTBunchOptionsEditable> = {}): void {
171
+ const {
172
+ errors, uniqueKey, replaceIndex, inheritOwner,
173
+ } = { ...this._globalOptions, ...options };
174
+ let hasError = false;
175
+ let finalIndex = index;
176
+
177
+ // Handle ID conflicts
178
+ const existingItem = this.get(item.getId());
179
+ if (existingItem) {
180
+ hasError = true;
181
+ if (errors) {
182
+ this._errors.push(new DYOToolsError(
183
+ 'id_conflict',
184
+ 'Element with same id already exists in the bunch',
185
+ this,
186
+ item,
187
+ ));
188
+ } else {
189
+ throw new DYOToolsError(
190
+ 'id_conflict',
191
+ 'Element with same id already exists in the bunch',
192
+ this,
193
+ item,
194
+ );
195
+ }
196
+ }
197
+
198
+ // Handle Key conflicts
199
+ if (uniqueKey && !hasError) {
200
+ const existingItemByKey = this.find({ key: { $eq: item.getKey() } });
201
+ if (existingItemByKey) {
202
+ hasError = true;
203
+ if (errors) {
204
+ this._errors.push(new DYOToolsError(
205
+ 'key_conflict',
206
+ 'Element with same key already exists in the bunch',
207
+ this,
208
+ item,
209
+ ));
210
+ } else {
211
+ throw new DYOToolsError(
212
+ 'key_conflict',
213
+ 'Element with same key already exists in the bunch',
214
+ this,
215
+ item,
216
+ );
217
+ }
218
+ }
219
+ }
220
+
221
+ if (!hasError) {
222
+ // Update indexes if out of limits
223
+ if (index < 0) {
224
+ finalIndex = 0;
225
+ }
226
+ if (index > this._items.length) {
227
+ finalIndex = this._items.length;
228
+ }
229
+
230
+ // Update Context
231
+ if (!this._globalOptions.virtualContext) {
232
+ const oldContext = item.getContext();
233
+ if (oldContext && oldContext.getComponentType() === 'bunch') {
234
+ (oldContext as DYOToolsBunch<IBunchItem, DTAcceptedMetaData>).remove(item.getId());
235
+ }
236
+ item.setContext(this);
237
+ }
238
+
239
+ // Update Owner
240
+ if (inheritOwner) {
241
+ item.setOwner(this._owner);
242
+ }
243
+
244
+ // Add the new Item
245
+ if (replaceIndex) {
246
+ this._items[finalIndex] = item;
247
+ } else {
248
+ const arrayPart1 = this._items.slice(0, finalIndex);
249
+ const arrayPart2 = this._items.slice(finalIndex);
250
+
251
+ this._items = [...arrayPart1, item, ...arrayPart2];
252
+ }
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Add each element of an array **items** at the end of the _items property array.
258
+ *
259
+ * @see [addAtIndex](#addAtIndex) method for adding specifications.
260
+ * @param items An array of DTElement instances to add into the bunch.
261
+ * @param options Optional Bunch option configuration object to apply only for this method execution. Options are not
262
+ * saved in current _globalOptions property. Available Options are : **uniqueKey**, **inheritOwner**, **replaceIndex**
263
+ * and **errors**.
264
+ */
265
+ addMany(items: IBunchItem[], options: Partial<DTBunchOptionsEditable> = {}): void {
266
+ this.addManyAtIndex(items, this._items.length, options);
267
+ }
268
+
269
+ /**
270
+ * Add each element of an array **items** at specified **index** into _items property array.
271
+ * The first element is added at provided **index** argument, and each next element at next indexes, following adding
272
+ * specifications.
273
+ *
274
+ * @see [addAtIndex](#addAtIndex) method for adding specifications.
275
+ * @param items An array of DTElement instances to add into the bunch.
276
+ * @param index Index value where the item might be added. Must be a number between 0 and the current _items length.
277
+ * If not, the provided argument is automatically changed to 0 or current _items length.
278
+ * @param options Optional Bunch option configuration object to apply only for this method execution. Options are not
279
+ * saved in current _globalOptions property. Available Options are : **uniqueKey**, **inheritOwner**, **replaceIndex**
280
+ * and **errors**.
281
+ */
282
+ addManyAtIndex(items: IBunchItem[], index: number, options: Partial<DTBunchOptionsEditable> = {}): void {
283
+ const previousItems = this._items;
284
+ const { errors } = { ...this._globalOptions, ...options };
285
+ let currentIndex = index;
286
+
287
+ if (index < 0) {
288
+ currentIndex = 0;
289
+ }
290
+
291
+ try {
292
+ for (const item of items) {
293
+ this.addAtIndex(item, currentIndex, options);
294
+ currentIndex += 1;
295
+ }
296
+ } catch (exception) {
297
+ if (!errors) {
298
+ this._items = previousItems;
299
+ throw exception;
300
+ }
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Return one DTElement instance included in the _items property by index or id.
306
+ *
307
+ * * If a Number is provided, return the DTElement instance at the corresponding index into _items.
308
+ * * If a String is provided, return the DTElement instance with the corresponding _id property into _items.
309
+ *
310
+ * @param index Number index value or String _id value.
311
+ * @returns DTElement instance that corresponds to index or id provided, or undefined if not found.
312
+ */
313
+ get(index: string | number): IBunchItem | undefined {
314
+ if (typeof index === 'number') {
315
+ return this._items[index];
316
+ }
317
+ const itemFiltered = this._items.filter((item: IBunchItem) => item.getId() === index);
318
+ return itemFiltered.length > 0 ? itemFiltered[0] : undefined;
319
+ }
320
+
321
+ /**
322
+ * Return all DTElement instance managed by the Bunch.
323
+ *
324
+ * @returns DTElement array corresponding to current _items property.
325
+ */
326
+ getAll(): IBunchItem[] {
327
+ return this._items;
328
+ }
329
+
330
+ /**
331
+ * Return current index of a DTElement instance into _items property by _id.
332
+ *
333
+ * @param id String _id value of the DTElement instance.
334
+ * @returns Current index number into _items, or -1 if not found.
335
+ */
336
+ indexOf(id: string): number {
337
+ let indexOfItem = -1;
338
+ for (let i = 0; i < this._items.length; i += 1) {
339
+ if (this._items[i].getId() === id) {
340
+ indexOfItem = i;
341
+ break;
342
+ }
343
+ }
344
+ return indexOfItem;
345
+ }
346
+
347
+ /**
348
+ * Remove a DTElement instance into the _items property by index or id.
349
+ *
350
+ * * If a Number is provided, remove the DTElement instance at the corresponding index into _items.
351
+ * * If a String is provided, remove the DTElement instance with the corresponding _id property into _items.
352
+ *
353
+ * Note : Remove also the current context of the removed item (only if Option **virtualContext** is *false*).
354
+ *
355
+ * @param index Number index value or String _id value.
356
+ */
357
+ remove(index: string | number): void {
358
+ if (typeof index === 'number') {
359
+ this.removeMany([index as number]);
360
+ } else {
361
+ this.removeMany([index as string]);
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Remove multiple DTElement instances into the _items property by index or id. An array of indexes or ids to remove
367
+ * must be provided.
368
+ *
369
+ * * If a Number Array is provided, remove DTElement instances at corresponding indexes into _items.
370
+ * * If a String Array is provided, remove DTElement instances with corresponding _id properties into _items.
371
+ *
372
+ * Note : Remove also the current context of removed items (only if Option **virtualContext** is *false*).
373
+ *
374
+ * @param indexes Number Array index values or String Array _id values.
375
+ */
376
+ removeMany(indexes: string[] | number[]): void {
377
+ const { virtualContext } = this._globalOptions;
378
+ const newItems = [];
379
+ for (let i = 0; i < this._items.length; i += 1) {
380
+ if (typeof indexes[0] === 'number') {
381
+ if (!(indexes as number[]).includes(i)) {
382
+ newItems.push(this._items[i]);
383
+ } else if (!virtualContext) {
384
+ this._items[i].removeContext();
385
+ }
386
+ } else if (!(indexes as string[]).includes(this._items[i].getId())) {
387
+ newItems.push(this._items[i]);
388
+ } else if (!virtualContext) {
389
+ this._items[i].removeContext();
390
+ }
391
+ }
392
+
393
+ this._items = newItems;
394
+ }
395
+
396
+ /**
397
+ * Remove all DTElement instances into the _items property.
398
+ *
399
+ * Note : Remove also the current context of removed items (only if Option **virtualContext** is *false*).
400
+ */
401
+ removeAll(): void {
402
+ const keysToRemove: number[] = this._items.map((item, index) => index);
403
+ this.removeMany(keysToRemove);
404
+ }
405
+
406
+ /**
407
+ * Return an array of DTElement from _items property filtered with a **filters** argument.
408
+ *
409
+ * Search filters can be apply on following DTElement properties :
410
+ * * **id** : property _id. Basic operators only.
411
+ * * **key** : property _key. Basic operators only.
412
+ * * **context** : property _id of current _context instance. Basic operators only.
413
+ * * **owner** : property _id of current _owner instance. Basic operators only.
414
+ * * **meta** : each meta Key of _meta property. Extended operators can be used.
415
+ *
416
+ * For each search filter provided, an object of specific operators is applied :
417
+ * * **BASIC OPERATORS**
418
+ * * **$eq** : The property must be strict equal to the filter value.
419
+ * * **$in** : The property must be included into the filter array.
420
+ * * **$nin** : The property must not be included into the filter array.
421
+ * * **$ne** : The property must be different to the filter value.
422
+ * * **EXTENDED OPERATORS** (meta only)
423
+ * * **$lte** : Number property only. The property must be lower or equal than the filter value.
424
+ * * **$gte** : Number property only. The property must be higher or equal than the filter array.
425
+ * * **$contains** : Array property only. The property must contain the filter value.
426
+ * * **$ncontains** : Array property only. The property must not contain the filter value.
427
+ *
428
+ * If many operators and / or many properties are passed into the **filters** argument, the logic operator applied is
429
+ * **AND**. For **owner** and **context** properties, you can pass *null* to filter elements with no owner or context
430
+ * defined.
431
+ *
432
+ * Examples of **filters** argument :
433
+ * * { key: { $eq: "key_1" } } : Return all DTElement instance into _items with *key_1* as _key property.
434
+ * * { context: { $in: [null, "bunch_1"] } } : Return all DTElement instance into _items having no context or a
435
+ * bunch context with *bunch_1* as _id property.
436
+ * * { key: { $ne: "key_1" }, meta: { score: { $gte: 50, $lte: 50 } } } : Return all DTElement instance into _items
437
+ * with _key property different than *key_1*, and meta key *score* value from _meta property between 50 and 100.
438
+ *
439
+ * @param filters Filters Object. The format is :
440
+ * { [property_1] : { [operator_1] : filter_value, [operator_2] : filter_value_2, ... }, [property_2] : { ... }, ... }
441
+ *
442
+ * For **meta**, you have to pass the meta key before the operator :
443
+ * { meta: { [meta_key1] : { [operator_1] : filter_value_1, ... }, [meta_key2] : { ... }, ... }, ... }
444
+ * @returns Array of DTElement instance corresponding to the filters. Empty if no filter or invalid ones are passed.
445
+ */
446
+ find(filters: Partial<DTBunchFilters>): IBunchItem[] {
447
+ const filteredItems : IBunchItem[] = [];
448
+ const validOperatorsBase: FilterOperatorType[] = [
449
+ FilterOperatorType.EQ,
450
+ FilterOperatorType.IN,
451
+ FilterOperatorType.NIN,
452
+ FilterOperatorType.NE,
453
+ ];
454
+ const validOperatorsMeta: FilterOperatorType[] = [
455
+ FilterOperatorType.EQ,
456
+ FilterOperatorType.IN,
457
+ FilterOperatorType.NIN,
458
+ FilterOperatorType.NE,
459
+ FilterOperatorType.LTE,
460
+ FilterOperatorType.GTE,
461
+ FilterOperatorType.CONTAINS,
462
+ FilterOperatorType.NCONTAINS,
463
+ ];
464
+ const checkAllValidFiltersForProp = (
465
+ itemProp: StandardPrimitiveType,
466
+ operators: Partial<DTBunchFilterWithBaseOperator>,
467
+ validOperators: FilterOperatorType[],
468
+ ) => {
469
+ if (Object.keys(operators).length) {
470
+ for (const operator of Object.keys(operators)) {
471
+ if (!validOperators.includes(operator as FilterOperatorType)
472
+ || !validFiltersForItem(itemProp, operators[operator], operator as FilterOperatorType.EQ)) {
473
+ return false;
474
+ }
475
+ }
476
+ return true;
477
+ }
478
+ return false;
479
+ };
480
+
481
+ for (const item of this._items) {
482
+ let validItem = !!(Object.keys(filters).length);
483
+
484
+ // id Filter
485
+ if (filters.id && !checkAllValidFiltersForProp(item.getId(), filters.id, validOperatorsBase)) {
486
+ validItem = false;
487
+ }
488
+
489
+ // key Filter
490
+ if (filters.key && !checkAllValidFiltersForProp(item.getKey(), filters.key, validOperatorsBase)) {
491
+ validItem = false;
492
+ }
493
+
494
+ // context Filter
495
+ const itemContext = item.getContext() ? item.getContext().getId() : null;
496
+ if (filters.context && !checkAllValidFiltersForProp(itemContext, filters.context, validOperatorsBase)) {
497
+ validItem = false;
498
+ }
499
+
500
+ // owner Filter
501
+ const itemOwner = item.getOwner() ? item.getOwner().getId() : null;
502
+ if (filters.owner && !checkAllValidFiltersForProp(itemOwner, filters.owner, validOperatorsBase)) {
503
+ validItem = false;
504
+ }
505
+
506
+ // meta Filter
507
+ if (filters.meta) {
508
+ if (Object.keys(filters.meta).length) {
509
+ const itemMeta = item.getManyMeta();
510
+ for (const [meta, filter] of Object.entries(filters.meta as Record<string, Partial<DTBunchFilterWithMetaOperator>>)) {
511
+ const metaValue = Object.prototype.hasOwnProperty.call(itemMeta, meta) ? itemMeta[meta] : null;
512
+ if (!checkAllValidFiltersForProp(metaValue as StandardPrimitiveType, filter, validOperatorsMeta)) {
513
+ validItem = false;
514
+ break;
515
+ }
516
+ }
517
+ }
518
+ }
519
+
520
+ if (validItem) {
521
+ filteredItems.push(item);
522
+ }
523
+ }
524
+
525
+ return filteredItems;
526
+ }
527
+
528
+ /**
529
+ * Create and return a new DTBunch instance by applying from current instance :
530
+ * - Copy _key property
531
+ * - Copy _meta property
532
+ * - Copy _globalOptions property
533
+ * - Make a copy of each element in _items, and add it into _items of the copied Bunch.
534
+ *
535
+ * @returns New DTBunch instance copied.
536
+ */
537
+ copy(): DYOToolsBunch<IBunchItem, IComponentMeta> {
538
+ let copyItems;
539
+ if (this._globalOptions.virtualContext) {
540
+ copyItems = this._items;
541
+ } else {
542
+ copyItems = this._items.length === 0 ? [] : this._items.map((item) => item.copy() as IBunchItem);
543
+ }
544
+
545
+ const copyBunch = new DYOToolsBunch<IBunchItem, IComponentMeta>(this._key, copyItems, this._globalOptions);
546
+ copyBunch.setManyMeta({ ...this.getManyMeta() });
547
+
548
+ return copyBunch;
549
+ }
550
+
551
+ /**
552
+ * Return JSON Object representation of the Bunch instance.
553
+ *
554
+ * JSON Object returned has the following structure :
555
+ * * **id** : _id property of the Bunch.
556
+ * * **key** : _key property of the Bunch.
557
+ * * **type** : _componentType property of the Bunch.
558
+ * * **items** : Array of JSON Object representation for each DTElement instance in _items property of the Bunch.
559
+ * * **owner** : String representation of the current _owner property of the Bunch (only if defined).
560
+ * * **meta** : JSON Object of all current metadata in _meta property of the Bunch (only if not empty).
561
+ *
562
+ * @returns JSON Object representation of the Bunch.
563
+ */
564
+ toObject(): DTBunchToObject<IComponentMeta> {
565
+ const objectBunch: DTBunchToObject<IComponentMeta> = {
566
+ id: this._id,
567
+ key: this._key,
568
+ type: this._componentType,
569
+ items: [],
570
+ };
571
+
572
+ if (this._items.length) {
573
+ objectBunch.items = this._items.map((item) => item.toObject());
574
+ }
575
+
576
+ if (this._owner) {
577
+ objectBunch.owner = this._owner.toString();
578
+ }
579
+
580
+ if (this._meta && Object.keys(this._meta).length > 0) {
581
+ objectBunch.meta = { ...this.getManyMeta() };
582
+ }
583
+
584
+ return objectBunch;
585
+ }
586
+
587
+ /**
588
+ * Return String representation of the Bunch instance.
589
+ *
590
+ * @returns String representation of the Bunch.
591
+ */
592
+ toString(): string {
593
+ let ownerKey = '';
594
+ if (this._owner) {
595
+ ownerKey = ` - Owner: ${this._owner.getKey()}`;
596
+ }
597
+
598
+ return `Component ${this._key} - Type: Bunch${ownerKey} - Items: ${this._items.length}`;
599
+ }
600
+ }