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
@@ -0,0 +1,828 @@
1
+ import { Persistent, persistent, persistentReference, persistentReferenceAt, registerPersistentClass, persistentPureReferenceWithPersistentProps, persistentReferenceWithPersistentProps, registerLegacyClassName, searchableArray, required, requiredWithValidator } from './persistent'
2
+
3
+ interface InnerObject {
4
+ nonPersistedReferences: PersistentClass[]
5
+ }
6
+ const beforeSerialize = vi.fn()
7
+ const afterDeserialize = vi.fn()
8
+
9
+ @registerLegacyClassName( 'LegacyClassName' )
10
+ @registerPersistentClass( 'PersistentClass' )
11
+ class PersistentClass extends Persistent {
12
+ protected override beforeSerialize(): void { beforeSerialize() }
13
+ protected override afterDeserialize(): void { afterDeserialize() }
14
+ set persistentProp( val: number | undefined ) { this._persistentProp = val }
15
+ get persistentProp() { return this._persistentProp }
16
+ set personPureRef( value: Person | undefined ) { this._personPureRef = value }
17
+ get personPureRef() { return this._personPureRef }
18
+ set persistentArray( value: PersistentClass[] | undefined ) { this._persistentArray = value }
19
+ get persistentArray() { return this._persistentArray }
20
+ @persistent _persistentProp: number | undefined
21
+ @persistent @searchableArray _persistentArray: PersistentClass[] | undefined
22
+ @persistentPureReferenceWithPersistentProps<Person>([ 'name', 'salary' ]) _personPureRef: Person | undefined
23
+ _nonPersistentProp: number | undefined
24
+ }
25
+
26
+ class NotRegistered extends Persistent {}
27
+
28
+ @registerPersistentClass( 'Person' )
29
+ class Person extends Persistent {
30
+ protected override beforeSerialize(): void {
31
+ beforeSerialize()
32
+ }
33
+
34
+ protected override afterDeserialize(): void {
35
+ afterDeserialize()
36
+ }
37
+
38
+ set name( value: string | undefined ) {
39
+ this._name = value
40
+ }
41
+
42
+ get name() {
43
+ return this._name
44
+ }
45
+
46
+ set salary( value: number | undefined ) {
47
+ this._salary = value
48
+ }
49
+
50
+ get salary() {
51
+ return this._salary
52
+ }
53
+
54
+ set doNotPersist( value: number | undefined ) {
55
+ this._doNotPersist = value
56
+ }
57
+
58
+ get doNotPersist() {
59
+ return this._doNotPersist
60
+ }
61
+
62
+ set anObjectProperty( value: PersistentClass ) {
63
+ this._anObjectProperty = value
64
+ }
65
+
66
+ get anObjectProperty() {
67
+ return this._anObjectProperty
68
+ }
69
+
70
+ set skills( value: string[] | undefined ) {
71
+ this._skills = value
72
+ }
73
+
74
+ get skills() {
75
+ return this._skills
76
+ }
77
+
78
+ set arrayOfPersistent( value: PersistentClass[] | undefined ) {
79
+ this._arrayOfPersistent = value
80
+ }
81
+
82
+ get arrayOfPersistent() {
83
+ return this._arrayOfPersistent
84
+ }
85
+
86
+ set document( value: PersistentClass | undefined ) {
87
+ this._document = value
88
+ }
89
+
90
+ get document() {
91
+ return this._document
92
+ }
93
+
94
+ set arrayOfRefs(value: PersistentClass[]) {
95
+ this._arrayOfRefs = value
96
+ }
97
+
98
+ get arrayOfRefs(): PersistentClass[] {
99
+ return this._arrayOfRefs
100
+ }
101
+
102
+ set persistentObject( value: InnerObject | undefined ) {
103
+ this._persistentObject = value
104
+ }
105
+
106
+ get persistentObject() {
107
+ return this._persistentObject
108
+ }
109
+
110
+ @required @persistent private _name?: string
111
+ @persistent private _salary?: number
112
+ @requiredWithValidator(
113
+ ( val, propInfo, instace ) => !!val && val.length > 0 && propInfo.name === 'skills' && instace.id === 'person9'
114
+ ) @persistent private _skills: string[] | undefined
115
+ @persistent private _anObjectProperty: PersistentClass = new PersistentClass()
116
+ @persistent private _arrayOfPersistent: PersistentClass[] | undefined
117
+ @persistent _notRegistered: NotRegistered | undefined
118
+ @persistent _arrayOfArray: number[][] | undefined
119
+ @persistent _arrayOfPersistentArray: PersistentClass[][] | undefined
120
+ @persistent _plainObject: { [ key: string ]: unknown } | undefined
121
+ @persistentReference _document: PersistentClass | undefined
122
+ @persistentReferenceAt('ArbitraryCollectionName') _docAtArbitraryCollection: PersistentClass | undefined
123
+ @persistentReferenceAt(( value, prop ) => `ArbitraryCollectionName/${ value.className }/${ prop.name }` ) _docAtArbitraryCollectionRefFunc: PersistentClass | undefined
124
+ @persistentReference private _arrayOfRefs: PersistentClass[] = []
125
+ @persistent private _persistentObject: InnerObject | undefined
126
+ @persistentReferenceWithPersistentProps<PersistentClass>([ 'persistentProp' ], value => `ArbitraryCollectionName/${ value.className }` ) _referenceWithStoredValues: PersistentClass | undefined
127
+ private _doNotPersist: number | undefined
128
+ }
129
+
130
+
131
+ interface CustomAnnotation {
132
+ menu: string
133
+ subType: string
134
+ showInDashboard: boolean
135
+ }
136
+
137
+ @registerPersistentClass( 'WithAnnotations', {
138
+ menu: 'main',
139
+ subType: 'NiceClass',
140
+ showInDashboard: false
141
+ } as CustomAnnotation )
142
+ export class WithAnnotations extends PersistentClass {}
143
+
144
+ @registerPersistentClass( 'WithAnnotations2', {
145
+ menu: 'main',
146
+ subType: 'UglyClass',
147
+ showInDashboard: false
148
+ }as CustomAnnotation)
149
+ export class WithAnnotations2 extends PersistentClass {}
150
+
151
+ @registerPersistentClass( 'WithAnnotations3', {
152
+ menu: 'subMenu',
153
+ showInDashboard: true
154
+ }as CustomAnnotation)
155
+ export class WithAnnotations3 extends PersistentClass {}
156
+
157
+
158
+ abstract class AbstractClass extends Persistent {
159
+ abstract pp(): any
160
+ }
161
+
162
+ @registerPersistentClass( 'ConcreteClass' )
163
+ class ConcreteClass extends AbstractClass {
164
+ pp(){}
165
+ }
166
+
167
+ describe( 'Persistent', ()=>{
168
+ let person: Person
169
+ let newPerson: Person
170
+ let obj = {
171
+ __className: 'Person',
172
+ name: 'Lisa',
173
+ salary: 2500,
174
+ skills: [ 'lazy', 'messy' ],
175
+ arrayOfArray: [ [ 5, 6 ], [ 7, 8 ] ],
176
+ plainObject: {
177
+ prop1: 'prop1',
178
+ prop2: 3
179
+ }
180
+ }
181
+
182
+ beforeEach(()=>{
183
+ person = new Person( 'person1' )
184
+ person.name = 'Maria'
185
+ person.salary = 3000
186
+ person.skills = [ 'diligent', 'smart' ]
187
+ person._arrayOfArray = [ [ 1, 2 ], [ 3, 4 ] ]
188
+ person._plainObject = {
189
+ prop1: 'aProp1',
190
+ prop2: 1034
191
+ }
192
+ newPerson = Persistent.createInstance<Person>( obj )
193
+ })
194
+
195
+ it( 'should keep a persistent properties list for each class', ()=>{
196
+ const a = new Person( 'person2' )
197
+ const b = new PersistentClass()
198
+
199
+ // we are testing a decorator that manipulates the class definition.
200
+ // Therefore we need to access the decorator created private properties
201
+ // in order to test the behaviour of the decorator
202
+ expect( a[ '_persistentProperties' ] ).toEqual( expect.arrayContaining([
203
+ { name: '_name', validator: expect.any( Function ) },
204
+ { name: '_skills', validator: expect.any( Function ) }
205
+ ]))
206
+ expect( b[ '_persistentProperties' ] ).not.toEqual( expect.arrayContaining( [ expect.objectContaining({
207
+ name: '_name'
208
+ })]))
209
+ expect( a[ '_persistentProperties' ] ).not.toEqual( expect.arrayContaining([ expect.objectContaining({
210
+ name: '_persistentProp'
211
+ })]))
212
+ expect( b[ '_persistentProperties' ] ).toEqual( expect.arrayContaining( [ {
213
+ name: '_persistentProp'
214
+ } ] ) )
215
+ })
216
+
217
+ it( 'should provide a flat object with persistent properties', ()=>{
218
+ expect( person.name ).toEqual( 'Maria' )
219
+ expect( person.toObject().name ).toEqual( 'Maria' )
220
+ })
221
+
222
+ it( 'should set persistent properties from a flat object', ()=>{
223
+ person.fromObject( obj )
224
+
225
+ expect( person.name ).toEqual( 'Lisa' )
226
+ expect( person.salary ).toBe( 2500 )
227
+ })
228
+
229
+ it( 'should process number object even when undefined', ()=>{
230
+ person.salary = undefined
231
+ expect( person.salary ).toBeUndefined()
232
+
233
+ person.fromObject( obj )
234
+ expect( person.salary ).toBe( 2500 )
235
+ })
236
+
237
+ it( 'should fill properties from arbitrary object', ()=>{
238
+ person.name = 'meganito'
239
+ person.salary = 32
240
+ person.fromObject({
241
+ name: 'fulanito changed',
242
+ salary: 4839,
243
+ foo: 'bar'
244
+ })
245
+
246
+ expect( person.name ).toBe( 'fulanito changed' )
247
+ expect( person.salary ).toBe( 4839 )
248
+ expect( (person as any).foo ).not.toBeDefined()
249
+ })
250
+
251
+ it( 'should persist number with value of 0', ()=>{
252
+ person.salary = 0
253
+ const object = person.toObject()
254
+
255
+ expect( object.salary ).toBe( 0 )
256
+
257
+ const newPerson = new Person( 'person3' )
258
+ newPerson.fromObject( object )
259
+ expect( newPerson.salary ).toBe( 0 )
260
+ })
261
+
262
+ it( 'should deal with undefined strings', ()=>{
263
+ person.name = undefined
264
+ expect( person.name ).toBeUndefined()
265
+
266
+ expect( person.toObject().name ).toBeUndefined()
267
+
268
+ delete (obj as any).name
269
+ person.fromObject( obj )
270
+ expect( person.name ).toBeUndefined()
271
+ })
272
+
273
+ it( 'should persist array properties', ()=>{
274
+ expect( person.toObject().skills ).toEqual( [ 'diligent', 'smart' ] )
275
+ })
276
+
277
+ it( 'should read arrays from stream', ()=>{
278
+ expect( newPerson.skills ).toEqual( [ 'lazy', 'messy' ] )
279
+ })
280
+
281
+ it( 'should persist array of array properties', ()=>{
282
+ const obj: any = person.toObject()
283
+ expect( obj.arrayOfArray ).toEqual( [ [ 1, 2 ], [ 3, 4 ] ] )
284
+ })
285
+
286
+ it( 'should read arrays of array from stream', ()=>{
287
+ expect( newPerson._arrayOfArray ).toEqual( [ [ 5, 6 ], [ 7, 8 ] ] )
288
+ })
289
+
290
+ it( 'should persist plain object properties', ()=>{
291
+ const obj: any = person.toObject()
292
+ expect( obj.plainObject ).toEqual( {
293
+ prop1: 'aProp1',
294
+ prop2: 1034
295
+ })
296
+ })
297
+
298
+ it( 'should read plain objects from stream', ()=>{
299
+ expect( newPerson._plainObject ).toEqual( {
300
+ prop1: 'prop1',
301
+ prop2: 3
302
+ })
303
+ })
304
+
305
+ it( 'should deal with undefined values', ()=>{
306
+ obj.plainObject.prop1 = undefined as any
307
+ obj.plainObject.prop2 = null as any
308
+ let aPerson: Person
309
+
310
+ expect( ()=>{ aPerson = new Person( 'person4' ).fromObject( obj ) } ).not.toThrow()
311
+ aPerson = new Person( 'person4' ).fromObject( obj )
312
+ expect( aPerson._plainObject!.prop1 ).toBeUndefined()
313
+ expect( aPerson._plainObject!.prop2 ).toBeNull()
314
+ })
315
+
316
+ it( 'should not throw on null property when toObject', ()=>{
317
+ newPerson._plainObject!.prop1 = null
318
+
319
+ expect( ()=>{
320
+ newPerson.toObject()
321
+ }).not.toThrow()
322
+ })
323
+
324
+ it( 'should not throw on null property when toObject', ()=>{
325
+ newPerson._plainObject!.prop1 = undefined
326
+
327
+ expect( ()=>{
328
+ newPerson.toObject()
329
+ }).not.toThrow()
330
+ })
331
+
332
+ it( 'should throw with object info if not available on creating a persistent instance', ()=>{
333
+ expect(()=>{
334
+ Persistent.createInstance({ id: 'id' } as any )
335
+ }).toThrow( 'You should provide a class name.' )
336
+ })
337
+
338
+ it( 'should clone persistent properties of an instance into this instance', ()=>{
339
+ const instace = new Person( 'personId' )
340
+ instace.clone( person )
341
+
342
+ expect( instace.id ).toBe( 'personId' )
343
+ expect( instace.name ).toBe( 'Maria')
344
+ })
345
+
346
+
347
+ describe( 'Properties instance of Persistent type', ()=>{
348
+ beforeEach(()=>{
349
+ const subObject = new PersistentClass()
350
+ subObject._persistentProp = 23
351
+ const subObject2 = new PersistentClass()
352
+ subObject._persistentProp = 103
353
+
354
+ const a = new PersistentClass(), b = new PersistentClass()
355
+ a._persistentProp = 205
356
+ b._persistentProp = 206
357
+ subObject._persistentArray = [ a, b ]
358
+
359
+ person.anObjectProperty = subObject
360
+ person.arrayOfPersistent = [ subObject, subObject2 ]
361
+ person._arrayOfPersistentArray = [ [ subObject, subObject2 ], [ subObject2, subObject ] ]
362
+ person._plainObject = {
363
+ prop1: subObject,
364
+ prop2: subObject2
365
+ }
366
+
367
+ const obj = JSON.stringify( person.toObject() )
368
+ newPerson = Persistent.createInstance<Person>( JSON.parse( obj ) )
369
+ })
370
+
371
+ it( 'should return compound objects as instance of object class', ()=>{
372
+ expect( newPerson.anObjectProperty ).toBeInstanceOf( PersistentClass )
373
+ })
374
+
375
+ it( 'should throw if class not registered on writing to a stream', ()=>{
376
+ person._notRegistered = new NotRegistered()
377
+
378
+ expect(()=>{
379
+ person.toObject()
380
+ }).toThrow( 'You should register this class prior to streaming it.' )
381
+ })
382
+
383
+ it( 'should throw if class not registered on reading from stream', ()=>{
384
+ const obj: any = {
385
+ id: 'id',
386
+ anObjectProperty: { __className: 'NotRegistered' }
387
+ }
388
+
389
+ expect(()=>{
390
+ new Person( 'person5' ).fromObject( obj )
391
+ }).toThrow( 'You should register class NotRegistered prior to use.' )
392
+ })
393
+
394
+ it( 'should persist array of Persistent type properties', ()=>{
395
+ expect( newPerson.arrayOfPersistent?.[ 0 ] ).toBeInstanceOf( PersistentClass )
396
+ })
397
+
398
+ it( 'should persist persistent array of array of Persistent type properties', ()=>{
399
+ expect( newPerson.arrayOfPersistent?.[ 0 ]?._persistentArray?.[ 0 ] ).toBeInstanceOf( PersistentClass )
400
+ })
401
+
402
+ it( 'should persist array of array of Persistent type properties', ()=>{
403
+ expect( newPerson._arrayOfPersistentArray?.[ 0 ]?.[ 0 ] ).toBeInstanceOf( PersistentClass )
404
+ })
405
+
406
+ it( 'should persist properties of type Persistent in plain object', ()=>{
407
+ expect( newPerson._plainObject?.prop1 ).toBeInstanceOf( PersistentClass )
408
+ })
409
+
410
+ it( 'should return registered properties', ()=>{
411
+ expect( new PersistentClass().getPersistentProperties() ).toEqual([
412
+ { name: 'id' },
413
+ { name: 'persistentProp' },
414
+ { name: 'persistentArray', searchableArray: true },
415
+ { name: 'personPureRef', isReference: true, isPureReference: true, forcedPersistentProps: [ 'name', 'salary' ] }
416
+ ])
417
+
418
+ expect( new Person( 'person6' ).getPersistentProperties() ).toEqual( expect.arrayContaining([
419
+ { name: 'name', validator: expect.any( Function ) },
420
+ { name: 'document', isReference: true },
421
+ { name: 'docAtArbitraryCollection', isReference: true, storeInCollection: 'ArbitraryCollectionName' }
422
+ ]))
423
+ })
424
+
425
+ })
426
+
427
+ describe( 'Document as reference', ()=>{
428
+ let ref1: PersistentClass, ref2: PersistentClass
429
+
430
+ beforeEach(()=>{
431
+ person.document = new PersistentClass()
432
+ person.document._persistentProp = 345
433
+ person._docAtArbitraryCollection = new PersistentClass()
434
+ person._docAtArbitraryCollection._persistentProp = 3989
435
+ person._docAtArbitraryCollectionRefFunc = person._docAtArbitraryCollection
436
+ ref1 = new PersistentClass(); ref1._persistentProp = 2091
437
+ ref2 = new PersistentClass(); ref2._persistentProp = 2092
438
+ person.arrayOfRefs.push( ref1 )
439
+ person.arrayOfRefs.push( ref2 )
440
+ person._referenceWithStoredValues = new PersistentClass(); person._referenceWithStoredValues._persistentProp = 2093
441
+ const obj = JSON.stringify( person.toObject() )
442
+ newPerson = Persistent.createInstance<Person>( JSON.parse( obj ) )
443
+ })
444
+
445
+ it( 'should create an object at root level', ()=>{
446
+ const persistentClassDocs = person.toObject().__rootCollections?.[ 'PersistentClass' ]
447
+
448
+ expect( persistentClassDocs ).toEqual( expect.arrayContaining([
449
+ expect.objectContaining({ persistentProp: 345 })
450
+ ]))
451
+ expect( newPerson.document ).toBeInstanceOf( PersistentClass )
452
+ expect( newPerson.document?.persistentProp ).toBeUndefined()
453
+ })
454
+
455
+ it( 'should create root reference collection with arbitrary name', ()=>{
456
+ const collectionDocs = person.toObject().__rootCollections?.[ 'ArbitraryCollectionName' ]
457
+
458
+ expect( collectionDocs ).toEqual( expect.arrayContaining([
459
+ expect.objectContaining({ persistentProp: 3989 })
460
+ ]))
461
+ expect( newPerson._docAtArbitraryCollection ).toBeInstanceOf( PersistentClass )
462
+ expect( newPerson._docAtArbitraryCollection?.persistentProp ).toBeUndefined()
463
+ })
464
+
465
+ it( 'should create root reference collection with arbitrary name based on function call', ()=>{
466
+ const collectionDocs = person.toObject().__rootCollections?.[ 'ArbitraryCollectionName/PersistentClass/_docAtArbitraryCollectionRefFunc' ]
467
+ expect( collectionDocs ).toBeDefined()
468
+
469
+ expect( collectionDocs ).toEqual( expect.arrayContaining([
470
+ expect.objectContaining({ persistentProp: 3989 })
471
+ ]))
472
+ expect( newPerson._docAtArbitraryCollection ).toBeInstanceOf( PersistentClass )
473
+ expect( newPerson._docAtArbitraryCollection?.persistentProp ).toBeUndefined()
474
+ })
475
+
476
+
477
+ it( 'should not create root reference for existing references', ()=>{
478
+ expect( Object.values(
479
+ newPerson.toObject().__rootCollections as any
480
+ )).toHaveLength( 1 )
481
+ })
482
+
483
+ it( 'should read swallow object document as reference', ()=>{
484
+ expect( newPerson.document ).toBeInstanceOf( PersistentClass )
485
+ expect( newPerson.document?.id ).toEqual( person.document?.id )
486
+ expect( newPerson.document?.['__documentReference'] ).toEqual({
487
+ storedInCollection: 'PersistentClass'
488
+ })
489
+ expect( newPerson.document?.persistentProp ).toBeUndefined()
490
+ })
491
+
492
+ it( 'should save existing refs properly', ()=>{
493
+ const res = newPerson.toObject()
494
+
495
+ expect( res.document ).not.toBeInstanceOf( PersistentClass )
496
+ expect( res.document ).toEqual({
497
+ __className: 'PersistentClass',
498
+ id: newPerson.document?.id,
499
+ __documentReference: { storedInCollection: 'PersistentClass' },
500
+ })
501
+ })
502
+
503
+ it( 'should not store pure references', ()=>{
504
+ person.anObjectProperty.personPureRef = new Person( 'person7' )
505
+ const obj = person.toObject()
506
+
507
+ expect( obj.__rootCollections?.['Person'] ).toHaveLength( 1 )
508
+ expect( obj.__rootCollections?.['Person']?.[0]?.id ).not.toEqual( 'person7' )
509
+ })
510
+
511
+ it( 'should fill reference object when store pure reference', ()=>{
512
+ const personPureRef =new Person( 'person7' )
513
+ personPureRef.name = 'James'
514
+ personPureRef.salary = 5643
515
+ person.anObjectProperty.personPureRef = personPureRef
516
+ const obj = person.toObject()
517
+
518
+ expect( obj.anObjectProperty?.personPureRef ).toEqual({
519
+ __className: 'Person',
520
+ id: personPureRef.id,
521
+ __documentReference: { storedInCollection: 'Person' },
522
+ name: 'James',
523
+ salary: 5643
524
+ })
525
+
526
+ expect( obj.__rootCollections?.Person?.[ 0 ]?.['anObjectProperty'].personPureRef ).toEqual({
527
+ __className: 'Person',
528
+ id: personPureRef.id,
529
+ __documentReference: { storedInCollection: 'Person' },
530
+ name: 'James',
531
+ salary: 5643
532
+ })
533
+ })
534
+
535
+ it( 'should NOT fill reference object when store pure reference and value is undefined', ()=>{
536
+ const personPureRef =new Person( 'person7' )
537
+ personPureRef.name = undefined
538
+ personPureRef.salary = 5643
539
+ person.anObjectProperty.personPureRef = personPureRef
540
+ const obj = person.toObject()
541
+
542
+ expect( obj.anObjectProperty?.personPureRef ).toEqual({
543
+ __className: 'Person',
544
+ id: personPureRef.id,
545
+ __documentReference: { storedInCollection: 'Person' },
546
+ salary: 5643
547
+ })
548
+ expect( obj.anObjectProperty?.personPureRef ).not.toHaveProperty( 'name' )
549
+ })
550
+
551
+ it( 'should create an instance from pure reference with the referenced persistent props', ()=>{
552
+ const personPureRef =new Person( 'person7' )
553
+ personPureRef.name = 'James'
554
+ personPureRef.salary = 5643
555
+ person.anObjectProperty.personPureRef = personPureRef
556
+ const obj = person.toObject()
557
+ const instance = Persistent.createInstance( obj )
558
+
559
+ expect( instance.anObjectProperty.personPureRef ).toBeInstanceOf( Person )
560
+ expect( instance.anObjectProperty.personPureRef?.name ).toEqual( 'James' )
561
+ expect( instance.anObjectProperty.personPureRef?.salary ).toBe( 5643 )
562
+ })
563
+
564
+
565
+ it( 'should store values of persistentReferenceWithPersistentProps', ()=>{
566
+ const obj = person.toObject()
567
+
568
+ expect( obj.__rootCollections?.Person?.[0]?.id ).toEqual( person.id )
569
+ expect( obj['referenceWithStoredValues'] ).toEqual({
570
+ __className: 'PersistentClass',
571
+ id: person._referenceWithStoredValues?.id,
572
+ __documentReference: { storedInCollection: 'ArbitraryCollectionName/PersistentClass' },
573
+ persistentProp: 2093
574
+ })
575
+
576
+ const referenceWithValuesInRootCollections = obj.__rootCollections?.['ArbitraryCollectionName/PersistentClass']?.find(
577
+ persistent => persistent.id === person._referenceWithStoredValues?.id
578
+ )
579
+
580
+ expect( referenceWithValuesInRootCollections ).toEqual({
581
+ __className: 'PersistentClass',
582
+ id: person._referenceWithStoredValues?.id,
583
+ persistentProp: 2093
584
+ })
585
+ })
586
+
587
+ it( 'should not store values of persistentReferenceWithPersistentProps if value is undefined', ()=>{
588
+ person._referenceWithStoredValues!.persistentProp = undefined as any
589
+ const obj = person.toObject()
590
+
591
+ expect( obj['referenceWithStoredValues'] ).toEqual({
592
+ __className: 'PersistentClass',
593
+ id: person._referenceWithStoredValues?.id,
594
+ __documentReference: { storedInCollection: 'ArbitraryCollectionName/PersistentClass' },
595
+ })
596
+ expect( obj['referenceWithStoredValues'] ).not.toHaveProperty( 'persistentProp' )
597
+ })
598
+
599
+ it( 'should create an instance from reference with the referenced persistent props', ()=>{
600
+ const obj = person.toObject()
601
+ const instance = Persistent.createInstance( obj )
602
+
603
+ expect( instance._referenceWithStoredValues ).toBeInstanceOf( PersistentClass )
604
+ expect( instance._referenceWithStoredValues?.persistentProp ).toBe( 2093 )
605
+ })
606
+
607
+ describe( 'Array of references', ()=>{
608
+
609
+ it( 'should create an object with array of refs', ()=>{
610
+ const obj = person.toObject()
611
+
612
+ expect( obj.arrayOfRefs ).toHaveLength( 2 )
613
+ expect( obj.arrayOfRefs?.[0] ).toEqual({
614
+ id: ref1.id,
615
+ __className: 'PersistentClass',
616
+ __documentReference: {
617
+ storedInCollection: 'PersistentClass'
618
+ }
619
+ })
620
+ })
621
+
622
+ it( 'should create root reference collection with references in array of references', ()=>{
623
+ const persistentClassDocs = person.toObject().__rootCollections?.[ 'PersistentClass' ]
624
+
625
+ expect( persistentClassDocs ).toEqual( expect.arrayContaining([
626
+ expect.objectContaining({ id: ref1.id }),
627
+ expect.objectContaining({ id: ref2.id }),
628
+ ]))
629
+ })
630
+
631
+ it( 'should deal with arrays of refs', ()=>{
632
+ expect( newPerson.arrayOfRefs ).toHaveLength( 2 )
633
+ expect( newPerson.arrayOfRefs[0]?.persistentProp ).toBeUndefined()
634
+ expect( newPerson.arrayOfRefs ).toEqual( expect.arrayContaining([
635
+ expect.objectContaining({
636
+ id: ref1.id,
637
+ __className: 'PersistentClass',
638
+ __documentReference: {
639
+ storedInCollection: 'PersistentClass'
640
+ }
641
+ }),
642
+ expect.objectContaining({
643
+ id: ref2.id,
644
+ __className: 'PersistentClass',
645
+ __documentReference: {
646
+ storedInCollection: 'PersistentClass'
647
+ }
648
+ })
649
+ ]))
650
+ })
651
+
652
+ it( 'should produce proper formed references from object', ()=>{
653
+ const personObj = person.toObject()
654
+
655
+ personObj.persistentObject = {
656
+ nonPersistedReferences: [{
657
+ id: ref1.id,
658
+ __className: 'PersistentClass',
659
+ __documentReference: {
660
+ storedInCollection: 'PersistentClass'
661
+ }
662
+ } as any ]
663
+ }
664
+
665
+ const newPerson = Persistent.createInstance<Person>( personObj )
666
+
667
+ const obj = newPerson.toObject()
668
+ expect( obj.persistentObject?.nonPersistedReferences ).toHaveLength( 1 )
669
+ expect( obj.persistentObject?.nonPersistedReferences[0]?.id ).toBeDefined()
670
+ expect( obj.persistentObject?.nonPersistedReferences[0]?.['__className'] ).toBeDefined()
671
+ expect( obj.persistentObject?.nonPersistedReferences[0]?.['__documentReference'] ).toBeDefined()
672
+ expect( obj.persistentObject?.nonPersistedReferences[0]?.persistentProp ).not.toBeDefined()
673
+ })
674
+ })
675
+
676
+ })
677
+
678
+ describe( 'Annotations', ()=>{
679
+
680
+ it( 'should allow register persistent class with arbitrary annotations', ()=>{
681
+ expect( Persistent.annotations( WithAnnotations ) ).toEqual({
682
+ menu: 'main',
683
+ subType: 'NiceClass',
684
+ showInDashboard: false
685
+ })
686
+ })
687
+
688
+ it( 'should check if a property is required', ()=>{
689
+ const person = new Person( 'person8' )
690
+ expect( person.isRequired( 'name' ) ).toBe( true )
691
+ expect( person.isRequired( 'salary' ) ).toBe( false )
692
+ })
693
+
694
+ it( 'should validate non required properties', ()=>{
695
+ const person = new Person( 'person9' )
696
+ expect( person.isPropValueValid('salary') ).toBeTruthy()
697
+ })
698
+
699
+ it( 'should validate a required properties', ()=>{
700
+ const person = new Person( 'person9' )
701
+ person.name = 'A test name'
702
+ expect( person.isPropValueValid('name') ).toBeTruthy()
703
+ })
704
+
705
+ it( 'should fail validation of an empty required properties', ()=>{
706
+ const person = new Person( 'person9' )
707
+ expect( person.isPropValueValid('name') ).toBeFalsy()
708
+ })
709
+
710
+ it( 'should fail validation of an empty required with validator properties', ()=>{
711
+ const person = new Person( 'person9' )
712
+ expect( person.isPropValueValid('skills') ).toBeFalsy()
713
+ })
714
+
715
+ it( 'should fail validation of an non passing validator properties', ()=>{
716
+ const person = new Person( 'person9' )
717
+ person.skills = []
718
+ expect( person.isPropValueValid('skills') ).toBeFalsy()
719
+ })
720
+
721
+ it( 'should pass validation of a filled required with validator properties', ()=>{
722
+ const person = new Person( 'person9' )
723
+ person.skills = [ 'a skill' ]
724
+ expect( person.isPropValueValid('skills') ).toBeTruthy()
725
+ })
726
+ })
727
+
728
+ describe( 'Persistent Class collection retrieval', ()=>{
729
+
730
+ it( 'should retrieve all registered classes by class name', ()=>{
731
+ expect( Persistent.registeredClasses() ).toHaveLength( 7 )
732
+ expect( Persistent.registeredClasses() ).toContain( 'Person' )
733
+ expect( Persistent.registeredClasses() ).toContain( 'PersistentClass' )
734
+ expect( Persistent.registeredClasses() ).toContain( 'LegacyClassName' )
735
+ })
736
+
737
+ it( 'should retrieve classes by type', ()=>{
738
+ expect( Persistent.classesExtending( PersistentClass ) ).toHaveLength( 5 )
739
+ expect( Persistent.classesExtending( PersistentClass ) ).toContain( 'PersistentClass' )
740
+ expect( Persistent.classesExtending( PersistentClass ) ).toContain( 'LegacyClassName' )
741
+ expect( Persistent.classesExtending( PersistentClass ) ).toContain( 'WithAnnotations' )
742
+ expect( Persistent.classesExtending( PersistentClass ) ).toContain( 'WithAnnotations3' )
743
+ })
744
+
745
+ it( 'should retrieve classes by abstract class', ()=>{
746
+ expect( Persistent.classesExtending( AbstractClass ) ).toHaveLength( 1 )
747
+ expect( Persistent.classesExtending( AbstractClass ) ).toContain( 'ConcreteClass' )
748
+ })
749
+
750
+ it( 'should retrieve property info', ()=>{
751
+ expect( Persistent.propInfo<Person>( 'Person', 'name' ) ).toEqual({
752
+ name: 'name', validator: expect.any( Function )
753
+ })
754
+ })
755
+ })
756
+
757
+ describe( 'Persistent instantation', ()=>{
758
+
759
+ it( 'should create an instance of a registered class', ()=>{
760
+ const instance = Persistent.createInstance({ __className: 'PersistentClass', id: 'testPersistent' } )
761
+ expect( instance ).toBeInstanceOf( PersistentClass )
762
+ expect( instance.id ).toEqual( 'testPersistent' )
763
+ })
764
+
765
+ it( 'should create an instance of a registered legacy class', ()=>{
766
+ const instance = Persistent.createInstance({ __className: 'LegacyClassName', id: 'testPersistent' } )
767
+ expect( instance ).toBeInstanceOf( PersistentClass )
768
+ expect( instance.id ).toEqual( 'testPersistent' )
769
+ })
770
+
771
+ it( 'should not create persistent objects with legacy name', ()=>{
772
+ const instance = new PersistentClass( 'testPersistent' )
773
+
774
+ expect( instance.toObject().__className ).toBe( 'PersistentClass' )
775
+ })
776
+
777
+ it( 'should create a reference from a plain object', ()=>{
778
+ const instance = Persistent.createReference({ __className: 'PersistentClass', id: 'testPersistent', __documentReference:{ storedInCollection: 'PersistentClass' } } )
779
+ expect( instance ).toBeInstanceOf( PersistentClass )
780
+ expect( instance.id ).toEqual( 'testPersistent' )
781
+ expect( instance['__documentReference'] ).toEqual({ storedInCollection: 'PersistentClass' })
782
+ })
783
+
784
+ it( 'should create a reference from a plain object without explicit __documentReference object', ()=>{
785
+ const instance = Persistent.createReference({ __className: 'PersistentClass', id: 'testPersistent' } )
786
+ expect( instance ).toBeInstanceOf( PersistentClass )
787
+ expect( instance.id ).toEqual( 'testPersistent' )
788
+ expect( instance['__documentReference'] ).toEqual({ storedInCollection: 'PersistentClass' })
789
+ })
790
+
791
+ })
792
+
793
+ describe( 'Before and after serialize hooks', ()=>{
794
+ beforeEach(()=>{
795
+ beforeSerialize.mockReset()
796
+ afterDeserialize.mockReset()
797
+ })
798
+
799
+ it( 'should call beforeSerialize hook', ()=>{
800
+ person.toObject()
801
+ expect( beforeSerialize ).toHaveBeenCalledTimes( 2 )
802
+ })
803
+
804
+ it( 'should call afterDeserialize hook', ()=>{
805
+ const obj = person.toObject()
806
+ Persistent.createInstance<Person>( obj )
807
+ expect( afterDeserialize ).toHaveBeenCalledTimes( 2 )
808
+ })
809
+ })
810
+
811
+ describe( 'Make array field searchable by property', ()=>{
812
+ it( 'should create a searchable array field', ()=>{
813
+ const instance = new PersistentClass( 'testPersistent' )
814
+ instance.persistentArray = [
815
+ new PersistentClass( 'testPersistent0' ),
816
+ new PersistentClass( 'testPersistent1' ),
817
+ new PersistentClass( 'testPersistent2' ),
818
+ ]
819
+ const obj = instance.toObject()
820
+
821
+ expect( obj[Persistent.searchableArrayNameFor( 'persistentArray' )] ).toEqual([
822
+ 'testPersistent0',
823
+ 'testPersistent1',
824
+ 'testPersistent2',
825
+ ])
826
+ })
827
+ })
828
+ })