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.
- package/.github/workflows/release.yml +26 -0
- package/CHANGELOG.md +1151 -0
- package/docs/.nojekyll +1 -0
- package/docs/README.md +94 -0
- package/docs/classes/Auth.md +391 -0
- package/docs/classes/AuthMock.md +278 -0
- package/docs/classes/AuthService.md +188 -0
- package/docs/classes/CloudFunctions.md +123 -0
- package/docs/classes/CloudFunctionsMock.md +97 -0
- package/docs/classes/CloudStorage.md +215 -0
- package/docs/classes/DataSource.md +248 -0
- package/docs/classes/EntropicComponent.md +666 -0
- package/docs/classes/JsonDataSource.md +328 -0
- package/docs/classes/MockCloudStorage.md +279 -0
- package/docs/classes/Model.md +274 -0
- package/docs/classes/Observable.md +120 -0
- package/docs/classes/Persistent.md +420 -0
- package/docs/classes/ServerAuth.md +211 -0
- package/docs/classes/ServerAuthMock.md +176 -0
- package/docs/classes/ServerAuthService.md +130 -0
- package/docs/classes/Store.md +218 -0
- package/docs/classes/StoredFile.md +636 -0
- package/docs/enums/StoredFileEvent.md +41 -0
- package/docs/interfaces/AuthError.md +30 -0
- package/docs/interfaces/CloudFunctionsService.md +69 -0
- package/docs/interfaces/Collection.md +13 -0
- package/docs/interfaces/CustomCredentials.md +7 -0
- package/docs/interfaces/DocumentReference.md +49 -0
- package/docs/interfaces/JsonRawData.md +7 -0
- package/docs/interfaces/SignData.md +63 -0
- package/docs/interfaces/StoreParams.md +52 -0
- package/docs/interfaces/StoredFileChange.md +41 -0
- package/docs/interfaces/UploadControl.md +90 -0
- package/docs/interfaces/UserCredentials.md +113 -0
- package/docs/interfaces/Values.md +17 -0
- package/docs/modules.md +1273 -0
- package/package.json +23 -19
- package/src/auth/auth-mock.spec.ts +168 -0
- package/src/auth/auth-mock.ts +129 -0
- package/src/auth/auth.ts +185 -0
- package/src/auth/user-auth-types.ts +21 -0
- package/src/cloud-functions/cloud-functions-mock.spec.ts +136 -0
- package/src/cloud-functions/cloud-functions-mock.ts +23 -0
- package/src/cloud-functions/cloud-functions.ts +83 -0
- package/src/cloud-storage/cloud-storage.spec.ts +207 -0
- package/src/cloud-storage/cloud-storage.ts +60 -0
- package/src/cloud-storage/mock-cloud-storage.ts +72 -0
- package/src/cloud-storage/stored-file.ts +102 -0
- package/src/index.ts +19 -0
- package/src/observable/observable.spec.ts +105 -0
- package/src/observable/observable.ts +67 -0
- package/src/persistent/entropic-component.spec.ts +143 -0
- package/src/persistent/entropic-component.ts +135 -0
- package/src/persistent/persistent.spec.ts +828 -0
- package/src/persistent/persistent.ts +650 -0
- package/src/server-auth/server-auth-mock.spec.ts +53 -0
- package/src/server-auth/server-auth-mock.ts +45 -0
- package/src/server-auth/server-auth.ts +49 -0
- package/src/store/data-source.ts +186 -0
- package/src/store/json-data-source.spec.ts +100 -0
- package/src/store/json-data-source.ts +256 -0
- package/src/store/mocks/mock-data.json +155 -0
- package/src/store/mocks/test-user.ts +122 -0
- package/src/store/model.spec.ts +659 -0
- package/src/store/model.ts +462 -0
- package/src/store/store.spec.ts +30 -0
- package/src/store/store.ts +113 -0
- package/src/types/utility-types.spec.ts +117 -0
- package/src/types/utility-types.ts +116 -0
- package/src/utils/test-utils/test-person.ts +44 -0
- package/src/utils/utils.spec.ts +95 -0
- package/{lib/utils/utils.d.ts → src/utils/utils.ts} +34 -10
- package/tsconfig-build.json +7 -0
- package/tsconfig-cjs.json +9 -0
- package/tsconfig.json +33 -0
- package/vite.config.ts +22 -0
- package/lib/auth/auth-mock.d.ts +0 -21
- package/lib/auth/auth-mock.js +0 -108
- package/lib/auth/auth-mock.js.map +0 -1
- package/lib/auth/auth.d.ts +0 -129
- package/lib/auth/auth.js +0 -146
- package/lib/auth/auth.js.map +0 -1
- package/lib/auth/user-auth-types.d.ts +0 -19
- package/lib/auth/user-auth-types.js +0 -3
- package/lib/auth/user-auth-types.js.map +0 -1
- package/lib/cloud-functions/cloud-functions-mock.d.ts +0 -11
- package/lib/cloud-functions/cloud-functions-mock.js +0 -19
- package/lib/cloud-functions/cloud-functions-mock.js.map +0 -1
- package/lib/cloud-functions/cloud-functions.d.ts +0 -19
- package/lib/cloud-functions/cloud-functions.js +0 -64
- package/lib/cloud-functions/cloud-functions.js.map +0 -1
- package/lib/cloud-storage/cloud-storage.d.ts +0 -24
- package/lib/cloud-storage/cloud-storage.js +0 -37
- package/lib/cloud-storage/cloud-storage.js.map +0 -1
- package/lib/cloud-storage/mock-cloud-storage.d.ts +0 -20
- package/lib/cloud-storage/mock-cloud-storage.js +0 -68
- package/lib/cloud-storage/mock-cloud-storage.js.map +0 -1
- package/lib/cloud-storage/stored-file.d.ts +0 -39
- package/lib/cloud-storage/stored-file.js +0 -106
- package/lib/cloud-storage/stored-file.js.map +0 -1
- package/lib/index.d.ts +0 -19
- package/lib/index.js +0 -36
- package/lib/index.js.map +0 -1
- package/lib/observable/observable.d.ts +0 -52
- package/lib/observable/observable.js +0 -66
- package/lib/observable/observable.js.map +0 -1
- package/lib/persistent/entropic-component.d.ts +0 -76
- package/lib/persistent/entropic-component.js +0 -109
- package/lib/persistent/entropic-component.js.map +0 -1
- package/lib/persistent/persistent.d.ts +0 -281
- package/lib/persistent/persistent.js +0 -539
- package/lib/persistent/persistent.js.map +0 -1
- package/lib/server-auth/server-auth-mock.d.ts +0 -12
- package/lib/server-auth/server-auth-mock.js +0 -39
- package/lib/server-auth/server-auth-mock.js.map +0 -1
- package/lib/server-auth/server-auth.d.ts +0 -24
- package/lib/server-auth/server-auth.js +0 -36
- package/lib/server-auth/server-auth.js.map +0 -1
- package/lib/store/data-source.d.ts +0 -137
- package/lib/store/data-source.js +0 -62
- package/lib/store/data-source.js.map +0 -1
- package/lib/store/json-data-source.d.ts +0 -68
- package/lib/store/json-data-source.js +0 -199
- package/lib/store/json-data-source.js.map +0 -1
- package/lib/store/mocks/test-user.d.ts +0 -49
- package/lib/store/mocks/test-user.js +0 -135
- package/lib/store/mocks/test-user.js.map +0 -1
- package/lib/store/model.d.ts +0 -238
- package/lib/store/model.js +0 -417
- package/lib/store/model.js.map +0 -1
- package/lib/store/store.d.ts +0 -62
- package/lib/store/store.js +0 -102
- package/lib/store/store.js.map +0 -1
- package/lib/types/utility-types.d.ts +0 -45
- package/lib/types/utility-types.js +0 -3
- package/lib/types/utility-types.js.map +0 -1
- package/lib/utils/test-utils/test-person.d.ts +0 -33
- package/lib/utils/test-utils/test-person.js +0 -25
- package/lib/utils/test-utils/test-person.js.map +0 -1
- package/lib/utils/utils.js +0 -76
- 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
|
+
}
|