entropic-bond 1.48.1 → 1.50.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 (141) hide show
  1. package/.github/workflows/release.yml +26 -0
  2. package/CHANGELOG.md +1151 -0
  3. package/docs/.nojekyll +1 -0
  4. package/docs/README.md +94 -0
  5. package/docs/classes/Auth.md +391 -0
  6. package/docs/classes/AuthMock.md +278 -0
  7. package/docs/classes/AuthService.md +188 -0
  8. package/docs/classes/CloudFunctions.md +123 -0
  9. package/docs/classes/CloudFunctionsMock.md +97 -0
  10. package/docs/classes/CloudStorage.md +215 -0
  11. package/docs/classes/DataSource.md +248 -0
  12. package/docs/classes/EntropicComponent.md +666 -0
  13. package/docs/classes/JsonDataSource.md +328 -0
  14. package/docs/classes/MockCloudStorage.md +279 -0
  15. package/docs/classes/Model.md +274 -0
  16. package/docs/classes/Observable.md +120 -0
  17. package/docs/classes/Persistent.md +420 -0
  18. package/docs/classes/ServerAuth.md +211 -0
  19. package/docs/classes/ServerAuthMock.md +176 -0
  20. package/docs/classes/ServerAuthService.md +130 -0
  21. package/docs/classes/Store.md +218 -0
  22. package/docs/classes/StoredFile.md +636 -0
  23. package/docs/enums/StoredFileEvent.md +41 -0
  24. package/docs/interfaces/AuthError.md +30 -0
  25. package/docs/interfaces/CloudFunctionsService.md +69 -0
  26. package/docs/interfaces/Collection.md +13 -0
  27. package/docs/interfaces/CustomCredentials.md +7 -0
  28. package/docs/interfaces/DocumentReference.md +49 -0
  29. package/docs/interfaces/JsonRawData.md +7 -0
  30. package/docs/interfaces/SignData.md +63 -0
  31. package/docs/interfaces/StoreParams.md +52 -0
  32. package/docs/interfaces/StoredFileChange.md +41 -0
  33. package/docs/interfaces/UploadControl.md +90 -0
  34. package/docs/interfaces/UserCredentials.md +113 -0
  35. package/docs/interfaces/Values.md +17 -0
  36. package/docs/modules.md +1273 -0
  37. package/package.json +23 -19
  38. package/src/auth/auth-mock.spec.ts +168 -0
  39. package/src/auth/auth-mock.ts +129 -0
  40. package/src/auth/auth.ts +185 -0
  41. package/src/auth/user-auth-types.ts +21 -0
  42. package/src/cloud-functions/cloud-functions-mock.spec.ts +136 -0
  43. package/src/cloud-functions/cloud-functions-mock.ts +23 -0
  44. package/src/cloud-functions/cloud-functions.ts +83 -0
  45. package/src/cloud-storage/cloud-storage.spec.ts +207 -0
  46. package/src/cloud-storage/cloud-storage.ts +60 -0
  47. package/src/cloud-storage/mock-cloud-storage.ts +72 -0
  48. package/src/cloud-storage/stored-file.ts +102 -0
  49. package/src/index.ts +19 -0
  50. package/src/observable/observable.spec.ts +105 -0
  51. package/src/observable/observable.ts +67 -0
  52. package/src/persistent/entropic-component.spec.ts +143 -0
  53. package/src/persistent/entropic-component.ts +135 -0
  54. package/src/persistent/persistent.spec.ts +828 -0
  55. package/src/persistent/persistent.ts +650 -0
  56. package/src/server-auth/server-auth-mock.spec.ts +53 -0
  57. package/src/server-auth/server-auth-mock.ts +45 -0
  58. package/src/server-auth/server-auth.ts +49 -0
  59. package/src/store/data-source.ts +186 -0
  60. package/src/store/json-data-source.spec.ts +100 -0
  61. package/src/store/json-data-source.ts +256 -0
  62. package/src/store/mocks/mock-data.json +155 -0
  63. package/src/store/mocks/test-user.ts +122 -0
  64. package/src/store/model.spec.ts +659 -0
  65. package/src/store/model.ts +462 -0
  66. package/src/store/store.spec.ts +30 -0
  67. package/src/store/store.ts +113 -0
  68. package/src/types/utility-types.spec.ts +117 -0
  69. package/src/types/utility-types.ts +116 -0
  70. package/src/utils/test-utils/test-person.ts +44 -0
  71. package/src/utils/utils.spec.ts +95 -0
  72. package/{lib/utils/utils.d.ts → src/utils/utils.ts} +34 -10
  73. package/tsconfig-build.json +7 -0
  74. package/tsconfig-cjs.json +9 -0
  75. package/tsconfig.json +33 -0
  76. package/vite.config.ts +22 -0
  77. package/lib/auth/auth-mock.d.ts +0 -21
  78. package/lib/auth/auth-mock.js +0 -108
  79. package/lib/auth/auth-mock.js.map +0 -1
  80. package/lib/auth/auth.d.ts +0 -129
  81. package/lib/auth/auth.js +0 -146
  82. package/lib/auth/auth.js.map +0 -1
  83. package/lib/auth/user-auth-types.d.ts +0 -19
  84. package/lib/auth/user-auth-types.js +0 -3
  85. package/lib/auth/user-auth-types.js.map +0 -1
  86. package/lib/cloud-functions/cloud-functions-mock.d.ts +0 -11
  87. package/lib/cloud-functions/cloud-functions-mock.js +0 -19
  88. package/lib/cloud-functions/cloud-functions-mock.js.map +0 -1
  89. package/lib/cloud-functions/cloud-functions.d.ts +0 -19
  90. package/lib/cloud-functions/cloud-functions.js +0 -64
  91. package/lib/cloud-functions/cloud-functions.js.map +0 -1
  92. package/lib/cloud-storage/cloud-storage.d.ts +0 -24
  93. package/lib/cloud-storage/cloud-storage.js +0 -37
  94. package/lib/cloud-storage/cloud-storage.js.map +0 -1
  95. package/lib/cloud-storage/mock-cloud-storage.d.ts +0 -20
  96. package/lib/cloud-storage/mock-cloud-storage.js +0 -68
  97. package/lib/cloud-storage/mock-cloud-storage.js.map +0 -1
  98. package/lib/cloud-storage/stored-file.d.ts +0 -39
  99. package/lib/cloud-storage/stored-file.js +0 -106
  100. package/lib/cloud-storage/stored-file.js.map +0 -1
  101. package/lib/index.d.ts +0 -19
  102. package/lib/index.js +0 -36
  103. package/lib/index.js.map +0 -1
  104. package/lib/observable/observable.d.ts +0 -52
  105. package/lib/observable/observable.js +0 -66
  106. package/lib/observable/observable.js.map +0 -1
  107. package/lib/persistent/entropic-component.d.ts +0 -76
  108. package/lib/persistent/entropic-component.js +0 -109
  109. package/lib/persistent/entropic-component.js.map +0 -1
  110. package/lib/persistent/persistent.d.ts +0 -281
  111. package/lib/persistent/persistent.js +0 -539
  112. package/lib/persistent/persistent.js.map +0 -1
  113. package/lib/server-auth/server-auth-mock.d.ts +0 -12
  114. package/lib/server-auth/server-auth-mock.js +0 -39
  115. package/lib/server-auth/server-auth-mock.js.map +0 -1
  116. package/lib/server-auth/server-auth.d.ts +0 -24
  117. package/lib/server-auth/server-auth.js +0 -36
  118. package/lib/server-auth/server-auth.js.map +0 -1
  119. package/lib/store/data-source.d.ts +0 -137
  120. package/lib/store/data-source.js +0 -62
  121. package/lib/store/data-source.js.map +0 -1
  122. package/lib/store/json-data-source.d.ts +0 -68
  123. package/lib/store/json-data-source.js +0 -199
  124. package/lib/store/json-data-source.js.map +0 -1
  125. package/lib/store/mocks/test-user.d.ts +0 -49
  126. package/lib/store/mocks/test-user.js +0 -135
  127. package/lib/store/mocks/test-user.js.map +0 -1
  128. package/lib/store/model.d.ts +0 -238
  129. package/lib/store/model.js +0 -417
  130. package/lib/store/model.js.map +0 -1
  131. package/lib/store/store.d.ts +0 -62
  132. package/lib/store/store.js +0 -102
  133. package/lib/store/store.js.map +0 -1
  134. package/lib/types/utility-types.d.ts +0 -45
  135. package/lib/types/utility-types.js +0 -3
  136. package/lib/types/utility-types.js.map +0 -1
  137. package/lib/utils/test-utils/test-person.d.ts +0 -33
  138. package/lib/utils/test-utils/test-person.js +0 -25
  139. package/lib/utils/test-utils/test-person.js.map +0 -1
  140. package/lib/utils/utils.js +0 -76
  141. package/lib/utils/utils.js.map +0 -1
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export * from './observable/observable'
2
+ export * from './persistent/entropic-component'
3
+ export * from './persistent/persistent'
4
+ export * from './store/data-source'
5
+ export * from './store/json-data-source'
6
+ export * from './store/store'
7
+ export * from './store/model'
8
+ export * from './types/utility-types'
9
+ export * from './cloud-storage/cloud-storage'
10
+ export * from './cloud-storage/mock-cloud-storage'
11
+ export * from './cloud-storage/stored-file'
12
+ export * from './auth/auth'
13
+ export * from './auth/user-auth-types'
14
+ export * from './auth/auth-mock'
15
+ export * from './cloud-functions/cloud-functions'
16
+ export * from './cloud-functions/cloud-functions-mock'
17
+ export * from './server-auth/server-auth'
18
+ export * from './server-auth/server-auth-mock'
19
+ export * from './utils/utils'
@@ -0,0 +1,105 @@
1
+ import {Observable} from './observable'
2
+
3
+ class ObserverTest {
4
+ public changed: boolean = true
5
+ userChanged() {
6
+ this.changed = true
7
+ }
8
+ }
9
+
10
+ interface MockEvent {
11
+ [ idx: string ]: any
12
+ }
13
+
14
+ describe('observer', () => {
15
+ let observable: Observable<MockEvent | void>
16
+
17
+ beforeEach(()=>{
18
+ observable = new Observable<MockEvent | void>()
19
+ })
20
+
21
+ it('should subscribe ', () => {
22
+ const mockObserver = new ObserverTest();
23
+ const changedSpy = vi.spyOn( mockObserver, 'userChanged' )
24
+
25
+ observable.subscribe( ()=>mockObserver.userChanged() )
26
+ observable.notify()
27
+
28
+ expect( changedSpy ).toBeCalledTimes(1)
29
+ })
30
+
31
+ it('should notify', ()=>{
32
+ const myCallback = vi.fn()
33
+
34
+ observable.subscribe( myCallback )
35
+ observable.notify()
36
+
37
+ expect( myCallback ).toHaveBeenCalledTimes(1)
38
+ })
39
+
40
+ it('should notify all subscribers', () => {
41
+ const callbackOne = vi.fn()
42
+ const callbackTwo = vi.fn()
43
+ const callbackThree = vi.fn()
44
+ observable.subscribe(callbackOne)
45
+ observable.subscribe(callbackTwo)
46
+ observable.subscribe(callbackThree)
47
+
48
+ observable.notify()
49
+
50
+ expect(callbackOne).toHaveBeenCalledTimes(1)
51
+ expect(callbackTwo).toHaveBeenCalledTimes(1)
52
+ expect(callbackThree).toHaveBeenCalledTimes(1)
53
+ })
54
+
55
+ it( 'should notify with event', ()=>{
56
+ const myCallback = vi.fn()
57
+ const obj: MockEvent = {name: 'Juan'};
58
+
59
+ observable.subscribe( myCallback )
60
+ observable.notify(obj)
61
+
62
+ expect( myCallback ).toHaveBeenCalledWith( obj )
63
+ })
64
+
65
+ it('should not notify removed listeners', ()=>{
66
+ const callbackOne = vi.fn()
67
+ const callbackTwo = vi.fn()
68
+ const callbackThree = vi.fn()
69
+ observable.subscribe(callbackOne)
70
+ observable.subscribe(callbackTwo)
71
+ observable.subscribe(callbackThree)
72
+
73
+ observable.notify()
74
+
75
+ expect(callbackOne).toHaveBeenCalledTimes(1)
76
+ expect(callbackTwo).toHaveBeenCalledTimes(1)
77
+ expect(callbackThree).toHaveBeenCalledTimes(1)
78
+
79
+ observable.unsubscribe( callbackThree )
80
+
81
+ observable.notify()
82
+
83
+ expect(callbackOne).toHaveBeenCalledTimes(2)
84
+ expect(callbackTwo).toHaveBeenCalledTimes(2)
85
+ expect(callbackThree).toHaveBeenCalledTimes(1)
86
+ })
87
+
88
+ it( 'should return an unsubscribe function on subscribe', ()=>{
89
+ const cb = vi.fn()
90
+ const unsubscriber = observable.subscribe( cb )
91
+ unsubscriber()
92
+
93
+ observable.notify()
94
+ expect( cb ).not.toHaveBeenCalled()
95
+ })
96
+
97
+ it( 'should return the number of subscribers', ()=>{
98
+ const cb = vi.fn()
99
+ observable.subscribe( cb )
100
+ observable.subscribe( cb )
101
+ observable.subscribe( vi.fn() )
102
+
103
+ expect( observable.subscribersCount ).toBe( 2 )
104
+ })
105
+ })
@@ -0,0 +1,67 @@
1
+ export type Callback<T> = ( event: T ) => void
2
+ export type Unsubscriber = ()=>void
3
+
4
+ /**
5
+ * Implements the Observer pattern.
6
+ * The Observable class is used to notify a list of subscribers when an event occurs.
7
+ * The subscribers are callback functions that are called when the event occurs.
8
+ * The event is passed as a parameter to the callback function.
9
+ * @example
10
+ * // Create an observable
11
+ * const observable = new Observable<number>()
12
+ * // Subscribe a listener
13
+ * const unsubscribe = observable.subscribe( event => console.log( event ) )
14
+ * // Notify the subscribers
15
+ * observable.notify( 1 )
16
+ * // Unsubscribe the listener
17
+ * unsubscribe()
18
+ */
19
+ export class Observable<T> {
20
+
21
+ /**
22
+ * Subscribes a listener callback function. On every notification,
23
+ * the listener callback will be called with an event as a parameter if sent.
24
+ *
25
+ * @param callback the listener callback
26
+ * @returns a function to unsubscribe the listener from further notifications
27
+ */
28
+ subscribe( callback: Callback<T> ): Unsubscriber {
29
+ this.subscribers.add(callback)
30
+ return ()=>this.unsubscribe( callback )
31
+ }
32
+
33
+ /**
34
+ * Removes the callback from the notification list.
35
+ *
36
+ * @param listenerCallback the listener callback to remove
37
+ */
38
+ unsubscribe( callback: Callback<T> ) {
39
+ this.subscribers.delete( callback )
40
+ }
41
+
42
+ /**
43
+ * Notifies all the subscribers with the event passed as parameter.
44
+ *
45
+ * @param event the event passed to all subscribers.
46
+ */
47
+ notify( event?: T ) {
48
+ this.subscribers.forEach(subs => subs(event!))
49
+ }
50
+
51
+ /**
52
+ * Returns the number of subscribers.
53
+ *
54
+ * @returns the number of subscribers
55
+ * @example
56
+ * const observable = new Observable<number>()
57
+ * observable.subscribe( event => console.log( event ) )
58
+ * observable.subscribe( event => console.log( event ) )
59
+ * observable.subscribe( event => console.log( event ) )
60
+ * console.log( observable.subscribersCount ) // 3
61
+ */
62
+ get subscribersCount() {
63
+ return this.subscribers.size
64
+ }
65
+
66
+ private subscribers: Set<Callback<T>> = new Set()
67
+ }
@@ -0,0 +1,143 @@
1
+ import { CompareFunction, EntropicComponent } from './entropic-component'
2
+
3
+ class Character extends EntropicComponent {
4
+ setName( value: string ) {
5
+ this.changeProp( 'name', value )
6
+ return this
7
+ }
8
+
9
+ get name() {
10
+ return this._name
11
+ }
12
+
13
+ pushFriend( name: string, isUnique?: CompareFunction<Character> ) {
14
+ return this.pushAndNotify<Character>( 'friends', name, isUnique )
15
+ }
16
+
17
+ removeFriend( name: string, isEqual: CompareFunction<Character> ) {
18
+ return this.removeAndNotify<Character>( 'friends', name, isEqual )
19
+ }
20
+
21
+ get friends(): readonly string[] {
22
+ return this._friends
23
+ }
24
+
25
+ private _friends: string[] = []
26
+ private _name: string = ''
27
+ }
28
+
29
+ describe('Observable Persistent', ()=>{
30
+ let hero: Character
31
+
32
+ beforeEach(()=>{
33
+ hero = new Character()
34
+ })
35
+
36
+ it( 'should change properties', ()=>{
37
+ hero.setName( 'Rogue I' )
38
+
39
+ expect( hero.name ).toEqual( 'Rogue I' )
40
+ })
41
+
42
+ it( 'should notify on change', ()=>{
43
+ const changed = vi.fn()
44
+ hero.onChange( changed )
45
+
46
+ hero.setName( 'Rogue I' )
47
+
48
+ expect( changed ).toHaveBeenCalledWith({ name: 'Rogue I' })
49
+ })
50
+
51
+ it( 'should return a unsubscribe function on subscription', ()=>{
52
+ const changed = vi.fn()
53
+
54
+ const unsubscribe = hero.onChange( changed )
55
+ unsubscribe()
56
+ hero.setName( 'Rogue II' )
57
+
58
+ expect( changed ).not.toHaveBeenCalled()
59
+ })
60
+
61
+ describe( 'Array Props', ()=>{
62
+ let spy = vi.fn()
63
+
64
+ beforeEach(()=>{
65
+ hero.onChange( spy )
66
+ })
67
+
68
+ afterEach(()=>{
69
+ hero.removeOnChange( spy )
70
+ spy.mockClear()
71
+ })
72
+
73
+ it( 'should insert element in array member and notify', ()=>{
74
+ const res = hero.pushFriend( 'John' )
75
+
76
+ expect( res ).toEqual( 'John' )
77
+ expect( hero.friends ).toContain( 'John' )
78
+ expect( spy ).toHaveBeenCalledTimes(1)
79
+ expect( spy ).toHaveBeenCalledWith({ friends: ['John'] })
80
+ })
81
+
82
+ it( 'should insert different elements in array member', ()=>{
83
+ expect( hero.pushFriend( 'John' ) ).toBeDefined()
84
+ expect( hero.pushFriend( 'Jaume' ) ).toBeDefined()
85
+
86
+ expect( hero.friends ).toContain( 'John' )
87
+ expect( hero.friends ).toContain( 'Jaume' )
88
+ expect( spy ).toHaveBeenCalledTimes(2)
89
+ })
90
+
91
+ it( 'should insert different elements in array member with check function', ()=>{
92
+ const isUnique = ( a: string, b: string ) => a!==b
93
+
94
+ expect( hero.pushFriend( 'John', isUnique ) ).toBeDefined()
95
+ expect( hero.pushFriend( 'Jaume', isUnique ) ).toBeDefined()
96
+
97
+ expect( hero.friends ).toContain( 'John' )
98
+ expect( hero.friends ).toContain( 'Jaume' )
99
+ expect( spy ).toHaveBeenCalledTimes(2)
100
+ })
101
+
102
+ it( 'should not insert repeated elements in array member with check function', ()=>{
103
+ const isUnique = ( a: string, b: string ) => a!==b
104
+
105
+ expect( hero.pushFriend( 'John', isUnique ) ).toBeDefined()
106
+ expect( hero.pushFriend( 'John', isUnique ) ).not.toBeDefined()
107
+
108
+ expect( hero.friends ).toContain( 'John' )
109
+ expect( hero.friends ).toHaveLength( 1 )
110
+ expect( spy ).toHaveBeenCalledTimes( 1 )
111
+ })
112
+
113
+ it( 'should remove an element from array member and notify', ()=>{
114
+ const isEqual = ( a: string, b: string ) => a===b
115
+ hero.pushFriend( 'John' )
116
+ hero.pushFriend( 'Jaume' )
117
+ spy.mockReset()
118
+ expect( hero.friends ).toHaveLength( 2 )
119
+
120
+ const res = hero.removeFriend( 'John', isEqual )
121
+ expect( res ).toEqual( 'John' )
122
+ expect( hero.friends ).not.toContain( 'John' )
123
+ expect( hero.friends ).toContain( 'Jaume' )
124
+ expect( spy ).toHaveBeenCalledTimes(1)
125
+ expect( spy ).toHaveBeenCalledWith({ friends: ['Jaume'] })
126
+ })
127
+
128
+ it( 'should not notify on element not removed', ()=>{
129
+ const isEqual = ( a: string, b: string ) => a===b
130
+ hero.pushFriend( 'John' )
131
+ hero.pushFriend( 'Jaume' )
132
+ spy.mockReset()
133
+ expect( hero.friends ).toHaveLength( 2 )
134
+
135
+ const res = hero.removeFriend( 'Nobody', isEqual )
136
+ expect( res ).toBeUndefined()
137
+ expect( hero.friends ).toContain( 'John' )
138
+ expect( hero.friends ).toContain( 'Jaume' )
139
+ expect( spy ).not.toHaveBeenCalled()
140
+ })
141
+
142
+ })
143
+ })
@@ -0,0 +1,135 @@
1
+ import { Callback, Observable, Unsubscriber } from '../observable/observable';
2
+ import { ClassArrayPropNames, ClassProps, Elements } from '../types/utility-types';
3
+ import { Persistent } from './persistent';
4
+
5
+ export type PropChangeEvent<T> = Partial<ClassProps<T>>
6
+ export type PropChangeCallback<T> = Callback<PropChangeEvent<T>>
7
+ type ArrayPropsElem<T> = Elements<T[ClassArrayPropNames<T>]>
8
+ export type CompareFunction<T> = ( a: ArrayPropsElem<T>, b: ArrayPropsElem<T> )=>boolean
9
+
10
+ /**
11
+ * Derived classes from EntropicComponent will have the ability to notify
12
+ * property changes by calling one of the provided notification methods.
13
+ * It extends Persistent class therefore EntropicComponent children will have
14
+ * persistance through the Entropic Bond persistence mechanism
15
+ */
16
+ export class EntropicComponent extends Persistent {
17
+
18
+ /**
19
+ * Subscribes a listener callback function. Every time a property is changed,
20
+ * the listener callback will be called with the property change event.
21
+ *
22
+ * @param listenerCallback the listener callback
23
+ * @returns a function to unsubscribe the listener from further notifications
24
+ */
25
+ onChange( listenerCallback: PropChangeCallback<this> ): Unsubscriber {
26
+ return this._onChange.subscribe( listenerCallback )
27
+ }
28
+
29
+ /**
30
+ * Removes the listener callback subscrition from the notifications.
31
+ *
32
+ * @param listenerCallback the listener callback to remove
33
+ */
34
+ removeOnChange( listenerCallback: PropChangeCallback<this> ) {
35
+ this._onChange.unsubscribe( listenerCallback )
36
+ }
37
+
38
+ /**
39
+ * Changes the value of the property and notifies the subscribers about the change.
40
+ * This is a helper method that can be used in the property setter.
41
+ *
42
+ * @param propName the name of the property to be changed
43
+ * @param value the new value for the property
44
+ * @returns true in case the property has been effectively changed, false otherwise
45
+ */
46
+ protected changeProp<P extends keyof this>( propName: P, value: this[ P ] ): boolean {
47
+ const pName = '_' + String( propName );
48
+
49
+ if ( this[ pName ] !== value ) {
50
+ this[ pName ] = value;
51
+ this._onChange.notify({ [ propName ]: value });
52
+ return true;
53
+ }
54
+
55
+ return false;
56
+ }
57
+
58
+ /**
59
+ * Notifies the subscribers a property or group of properties change.
60
+ * This is a helper function to be used when you want to notify property changes.
61
+ *
62
+ * @param event the event with the changed properties
63
+ */
64
+ protected notify<T extends EntropicComponent>( event: PropChangeEvent<T> ) {
65
+ this._onChange.notify(event)
66
+ }
67
+
68
+ /**
69
+ * Inserts a new element in an arbitrary array property of this class and
70
+ * fires a change event if successfully inserted. To avoid repeated elements
71
+ * to be inserted, you can pass a function that checks for inequity.
72
+ *
73
+ * @param arrayPropName the name of the array property of this class where you
74
+ * want to insert the new element.
75
+ * @param element the element to be inserted
76
+ * @param isUnique a function that checks for inequity of the two elements
77
+ * passed as parameter. If the returned value is true, the
78
+ * value will be pushed into the array. When the function is
79
+ * not provided, the element will be inserted regardless it is
80
+ * already in the array.
81
+ * @returns the inserted element or undefined if the element was not inserted.
82
+ */
83
+ protected pushAndNotify<T extends EntropicComponent>(
84
+ arrayPropName: ClassArrayPropNames<T>,
85
+ element: ArrayPropsElem<T>,
86
+ isUnique?: CompareFunction<T>
87
+ ): ArrayPropsElem<T> | undefined {
88
+
89
+ const pName = '_' + String( arrayPropName );
90
+ const alreadyIn = isUnique && this[ pName ].find(
91
+ ( item: ArrayPropsElem<T> ) => !isUnique( item, element )
92
+ )
93
+ if ( alreadyIn ) return undefined
94
+
95
+ this[ pName ].push( element )
96
+ this.notify({ [arrayPropName]: this[ arrayPropName as string ] })
97
+ return element
98
+ }
99
+
100
+ /**
101
+ * Removes an element from an arbitrary array property of this class and fires
102
+ * a change event on operation success.
103
+ *
104
+ * @param arrayPropName the name of the array property of this class where you
105
+ * want to insert the new element.
106
+ * @param element the element to be inserted
107
+ * @param isEqual a function that checks for equity of the two elements
108
+ * passed as parameter. If the returned value is true, the
109
+ * value will be removed from the array.
110
+ * @returns the removed element or undefined if the element was not removed.
111
+ */
112
+ protected removeAndNotify<T extends EntropicComponent>(
113
+ arrayPropName: ClassArrayPropNames<T>,
114
+ element: ArrayPropsElem<T>,
115
+ isEqual: CompareFunction<T>
116
+ ): ArrayPropsElem<T> | undefined {
117
+
118
+ const pName = '_' + String( arrayPropName );
119
+
120
+ const originalLength = this[ pName ].length
121
+
122
+ this[ pName ] = this[ pName ].filter(
123
+ ( item: ArrayPropsElem<T> ) => !isEqual( item, element )
124
+ )
125
+
126
+ if ( originalLength === this[ pName ].length ) {
127
+ return undefined
128
+ }
129
+
130
+ this.notify({ [arrayPropName]: this[ arrayPropName as string ] })
131
+ return element
132
+ }
133
+
134
+ private _onChange: Observable<PropChangeEvent<EntropicComponent>> = new Observable<PropChangeEvent<EntropicComponent>>()
135
+ }