entropic-bond 1.50.0 → 1.50.2

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 (111) hide show
  1. package/lib/auth/auth-mock.d.ts +22 -0
  2. package/lib/auth/auth-mock.spec.d.ts +1 -0
  3. package/lib/auth/auth.d.ts +131 -0
  4. package/lib/auth/user-auth-types.d.ts +19 -0
  5. package/lib/cloud-functions/cloud-functions-mock.d.ts +11 -0
  6. package/lib/cloud-functions/cloud-functions-mock.spec.d.ts +1 -0
  7. package/lib/cloud-functions/cloud-functions.d.ts +19 -0
  8. package/lib/cloud-storage/cloud-storage.d.ts +24 -0
  9. package/lib/cloud-storage/cloud-storage.spec.d.ts +1 -0
  10. package/lib/cloud-storage/mock-cloud-storage.d.ts +20 -0
  11. package/lib/cloud-storage/stored-file.d.ts +39 -0
  12. package/lib/entropic-bond.js +1667 -0
  13. package/lib/entropic-bond.umd.cjs +7 -0
  14. package/lib/index.d.ts +19 -0
  15. package/lib/observable/observable.d.ts +52 -0
  16. package/lib/observable/observable.spec.d.ts +1 -0
  17. package/lib/persistent/entropic-component.d.ts +76 -0
  18. package/lib/persistent/entropic-component.spec.d.ts +1 -0
  19. package/lib/persistent/persistent.d.ts +281 -0
  20. package/lib/persistent/persistent.spec.d.ts +67 -0
  21. package/lib/server-auth/server-auth-mock.d.ts +12 -0
  22. package/lib/server-auth/server-auth-mock.spec.d.ts +1 -0
  23. package/lib/server-auth/server-auth.d.ts +24 -0
  24. package/lib/store/data-source.d.ts +137 -0
  25. package/lib/store/json-data-source.d.ts +68 -0
  26. package/lib/store/json-data-source.spec.d.ts +1 -0
  27. package/lib/store/mocks/test-user.d.ts +49 -0
  28. package/lib/store/model.d.ts +238 -0
  29. package/lib/store/model.spec.d.ts +1 -0
  30. package/lib/store/store.d.ts +62 -0
  31. package/lib/store/store.spec.d.ts +1 -0
  32. package/lib/types/utility-types.d.ts +45 -0
  33. package/lib/types/utility-types.spec.d.ts +1 -0
  34. package/lib/utils/test-utils/test-person.d.ts +33 -0
  35. package/{src/utils/utils.ts → lib/utils/utils.d.ts} +10 -34
  36. package/lib/utils/utils.spec.d.ts +1 -0
  37. package/package.json +7 -4
  38. package/.github/workflows/release.yml +0 -26
  39. package/CHANGELOG.md +0 -1151
  40. package/docs/.nojekyll +0 -1
  41. package/docs/README.md +0 -94
  42. package/docs/classes/Auth.md +0 -391
  43. package/docs/classes/AuthMock.md +0 -278
  44. package/docs/classes/AuthService.md +0 -188
  45. package/docs/classes/CloudFunctions.md +0 -123
  46. package/docs/classes/CloudFunctionsMock.md +0 -97
  47. package/docs/classes/CloudStorage.md +0 -215
  48. package/docs/classes/DataSource.md +0 -248
  49. package/docs/classes/EntropicComponent.md +0 -666
  50. package/docs/classes/JsonDataSource.md +0 -328
  51. package/docs/classes/MockCloudStorage.md +0 -279
  52. package/docs/classes/Model.md +0 -274
  53. package/docs/classes/Observable.md +0 -120
  54. package/docs/classes/Persistent.md +0 -420
  55. package/docs/classes/ServerAuth.md +0 -211
  56. package/docs/classes/ServerAuthMock.md +0 -176
  57. package/docs/classes/ServerAuthService.md +0 -130
  58. package/docs/classes/Store.md +0 -218
  59. package/docs/classes/StoredFile.md +0 -636
  60. package/docs/enums/StoredFileEvent.md +0 -41
  61. package/docs/interfaces/AuthError.md +0 -30
  62. package/docs/interfaces/CloudFunctionsService.md +0 -69
  63. package/docs/interfaces/Collection.md +0 -13
  64. package/docs/interfaces/CustomCredentials.md +0 -7
  65. package/docs/interfaces/DocumentReference.md +0 -49
  66. package/docs/interfaces/JsonRawData.md +0 -7
  67. package/docs/interfaces/SignData.md +0 -63
  68. package/docs/interfaces/StoreParams.md +0 -52
  69. package/docs/interfaces/StoredFileChange.md +0 -41
  70. package/docs/interfaces/UploadControl.md +0 -90
  71. package/docs/interfaces/UserCredentials.md +0 -113
  72. package/docs/interfaces/Values.md +0 -17
  73. package/docs/modules.md +0 -1273
  74. package/src/auth/auth-mock.spec.ts +0 -168
  75. package/src/auth/auth-mock.ts +0 -129
  76. package/src/auth/auth.ts +0 -185
  77. package/src/auth/user-auth-types.ts +0 -21
  78. package/src/cloud-functions/cloud-functions-mock.spec.ts +0 -136
  79. package/src/cloud-functions/cloud-functions-mock.ts +0 -23
  80. package/src/cloud-functions/cloud-functions.ts +0 -83
  81. package/src/cloud-storage/cloud-storage.spec.ts +0 -207
  82. package/src/cloud-storage/cloud-storage.ts +0 -60
  83. package/src/cloud-storage/mock-cloud-storage.ts +0 -72
  84. package/src/cloud-storage/stored-file.ts +0 -102
  85. package/src/index.ts +0 -19
  86. package/src/observable/observable.spec.ts +0 -105
  87. package/src/observable/observable.ts +0 -67
  88. package/src/persistent/entropic-component.spec.ts +0 -143
  89. package/src/persistent/entropic-component.ts +0 -135
  90. package/src/persistent/persistent.spec.ts +0 -828
  91. package/src/persistent/persistent.ts +0 -650
  92. package/src/server-auth/server-auth-mock.spec.ts +0 -53
  93. package/src/server-auth/server-auth-mock.ts +0 -45
  94. package/src/server-auth/server-auth.ts +0 -49
  95. package/src/store/data-source.ts +0 -186
  96. package/src/store/json-data-source.spec.ts +0 -100
  97. package/src/store/json-data-source.ts +0 -256
  98. package/src/store/mocks/mock-data.json +0 -155
  99. package/src/store/mocks/test-user.ts +0 -122
  100. package/src/store/model.spec.ts +0 -659
  101. package/src/store/model.ts +0 -462
  102. package/src/store/store.spec.ts +0 -30
  103. package/src/store/store.ts +0 -113
  104. package/src/types/utility-types.spec.ts +0 -117
  105. package/src/types/utility-types.ts +0 -116
  106. package/src/utils/test-utils/test-person.ts +0 -44
  107. package/src/utils/utils.spec.ts +0 -95
  108. package/tsconfig-build.json +0 -7
  109. package/tsconfig-cjs.json +0 -9
  110. package/tsconfig.json +0 -33
  111. package/vite.config.ts +0 -22
@@ -1,45 +0,0 @@
1
- import { UserCredentials } from '../auth/user-auth-types'
2
- import { Collection } from '../types/utility-types'
3
- import { ServerAuthService, CustomCredentials } from './server-auth'
4
-
5
- export class ServerAuthMock extends ServerAuthService {
6
- constructor( userCredentials: Collection<UserCredentials<{}>> ) {
7
- super()
8
- this._userCredentials = userCredentials
9
- }
10
-
11
- getUser<T extends CustomCredentials>( userId: string ): Promise<UserCredentials<T> | undefined> {
12
- if ( !this._userCredentials[ userId ] ) Promise.resolve( undefined )
13
-
14
- return Promise.resolve( this._userCredentials[ userId ] as UserCredentials<T> )
15
- }
16
-
17
- setCustomCredentials<T extends CustomCredentials>( userId: string, customCredentials: T ): Promise<void> {
18
- const userCredentials = this._userCredentials[ userId ]
19
- if ( !userCredentials ) throw new Error( `User ${ userId } not found in the auth system` )
20
- userCredentials.customData = { ...customCredentials }
21
-
22
- return Promise.resolve()
23
- }
24
-
25
- updateUser<T extends CustomCredentials>( userId: string, credentials: Partial<UserCredentials<T>> ): Promise<UserCredentials<T>> {
26
- this._userCredentials[ userId ] = {
27
- ...this._userCredentials,
28
- ...credentials,
29
- id: userId
30
- } as UserCredentials<T>
31
-
32
- return Promise.resolve( this._userCredentials[ userId ] as UserCredentials<T> )
33
- }
34
-
35
- deleteUser( userId: string ): Promise<void> {
36
- delete this._userCredentials[ userId ]
37
- return Promise.resolve()
38
- }
39
-
40
- get userCredentials() {
41
- return this._userCredentials
42
- }
43
-
44
- private _userCredentials: Collection<UserCredentials<{}>>
45
- }
@@ -1,49 +0,0 @@
1
- import { UserCredentials } from '../auth/user-auth-types'
2
-
3
- export interface CustomCredentials {
4
- [ key: string ]: unknown
5
- }
6
-
7
- export abstract class ServerAuthService {
8
- abstract setCustomCredentials<T extends CustomCredentials>( userId: string, customCredentials: T ): Promise<void>
9
- abstract getUser<T extends CustomCredentials>( userId: string ): Promise<UserCredentials<T> | undefined>
10
- abstract updateUser<T extends CustomCredentials>( userId: string, credentials: Partial<UserCredentials<T>> ): Promise<UserCredentials<T>>
11
- abstract deleteUser( userId: string ): Promise<void>
12
- }
13
-
14
- export class ServerAuth extends ServerAuthService {
15
- static error = { shouldBeRegistered: 'You should register a Server Auth service before using the Server Auth.' }
16
-
17
- protected constructor(){ super() }
18
-
19
- static useServerAuthService( authService: ServerAuthService ) {
20
- if ( ServerAuth._authService != authService ) {
21
- ServerAuth._authService = authService
22
- this._instance = undefined
23
- }
24
- }
25
-
26
- static get instance() {
27
- if ( !ServerAuth._authService ) throw new Error( ServerAuth.error.shouldBeRegistered )
28
- return this._instance || (this._instance = new ServerAuth() )
29
- }
30
-
31
- getUser<T extends CustomCredentials>( userId: string ): Promise<UserCredentials<T> | undefined> {
32
- return ServerAuth._authService.getUser( userId )
33
- }
34
-
35
- updateUser<T extends CustomCredentials>( userId: string, credentials: Partial<UserCredentials<T>> ): Promise<UserCredentials<T>> {
36
- return ServerAuth._authService.updateUser( userId, credentials )
37
- }
38
-
39
- setCustomCredentials<T extends CustomCredentials>( userId: string, customCredentials: T ): Promise<void> {
40
- return ServerAuth._authService.setCustomCredentials( userId, customCredentials )
41
- }
42
-
43
- deleteUser( userId: string ) {
44
- return ServerAuth._authService.deleteUser( userId )
45
- }
46
-
47
- private static _instance: ServerAuth | undefined = undefined
48
- private static _authService: ServerAuthService
49
- }
@@ -1,186 +0,0 @@
1
- import { Persistent, PersistentObject, Collections } from '../persistent/persistent'
2
- import { ClassPropNames } from '../types/utility-types'
3
-
4
- export type DocumentObject = PersistentObject<Persistent>
5
-
6
- /**
7
- * The query operators
8
- * @param == equal
9
- * @param != not equal
10
- * @param < less than
11
- * @param <= less than or equal
12
- * @param > greater than
13
- * @param >= greater than or equal
14
- * @param contains array contains
15
- * @param containsAny array contains any
16
- * @param in in
17
- * @param !in not in
18
- */
19
- export type QueryOperator = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'contains' | 'containsAny'// | 'in' | '!in'
20
-
21
- /**
22
- * A representation of a query operation
23
- * @param property the name of the property to be used in the query
24
- * @param operator the operator to be used in the query
25
- * @param value the value to be used in the query
26
- * @param aggregate if true, the query results will be aggregated using an `or` operator
27
- */
28
- export type QueryOperation<T> = {
29
- property: ClassPropNames<T>
30
- operator: QueryOperator
31
- value: Partial<T[ClassPropNames<T>]> | {[key:string]: unknown}
32
- aggregate?: boolean
33
- }
34
-
35
- /**
36
- * The sort order
37
- * @param asc ascending order
38
- * @param desc descending order
39
- */
40
- export type QueryOrder = 'asc' | 'desc'
41
-
42
- /**
43
- * A representation of a full query
44
- * @param operations the query operations to be performed
45
- * @param limit the maximum number of items to be retrieved
46
- * @param sort sort info
47
- * @param sort.order the sort order
48
- * @param sort.propertyName the name of the property to be used for sorting
49
- */
50
- export type QueryObject<T> = {
51
- operations?: QueryOperation<T>[]
52
- limit?: number
53
- sort?: {
54
- order: QueryOrder
55
- propertyName: ClassPropNames<T> | string
56
- }
57
- }
58
-
59
- /**
60
- * The data source interface.
61
- * It defines the methods that must be implemented by a data source
62
- * A data source is able to retrieve and save data i.e: from a database, a file, a RestAPI, etc.
63
- * You can derive from this class to implement your own data source with the
64
- * A data source is used by the store to retrieve and save data.
65
- */
66
- export abstract class DataSource {
67
-
68
- /**
69
- * Retrieves a document by id
70
- * Implement the required logic to retrieve a document by id from your concrete
71
- * the data source
72
- * @param id the id of the document to be retrieved
73
- * @param collectionName the name of the collection where the document is stored
74
- * @returns a promise resolving to the document object. The document object is
75
- * a plain object with the properties of the document class.
76
- */
77
- abstract findById( id: string, collectionName: string ): Promise< DocumentObject >
78
-
79
- /**
80
- * Retrieves all documents matching the query stored in the query object
81
- * Implement the required logic to retrieve the documents that match the
82
- * requirements in the query object from your concrete the data source
83
- * @param queryObject the query object containing the query operations
84
- * @param collectionName the name of the collection where the documents are stored
85
- * @returns a promise resolving to an array of document objects. The document object is
86
- * a plain object with the properties of the document class.
87
- * @see QueryObject
88
- * @see QueryOperation
89
- * @see QueryOperator
90
- * @see QueryOrder
91
- * @see DocumentObject
92
- */
93
- abstract find( queryObject: QueryObject<DocumentObject>, collectionName: string ): Promise< DocumentObject[] >
94
-
95
- /**
96
- * Saves a document
97
- * Implement the required logic to save the document in your concrete the data source
98
- * @param object A collection of documents to be saved
99
- * @returns a promise
100
- */
101
- abstract save( object: Collections ): Promise< void >
102
-
103
- /**
104
- * Deletes a document by id
105
- * Implement the required logic to delete a document by id from your concrete
106
- * data source
107
- * @param id the id of the document to be deleted
108
- * @param collectionName the name of the collection where the document is stored
109
- * @returns a promise
110
- */
111
- abstract delete( id: string, collectionName: string ): Promise<void>
112
-
113
- /**
114
- * Retrieves the next bunch of documents matching the query stored in the query object
115
- * Implement the required logic to retrieve the next bunch of documents that match the
116
- * requirements in the query object from your concrete the data source
117
- * @param limit the maximum number of items to be retrieved
118
- * @returns a promise resolving to an array representing the next bunch of document objects
119
- */
120
- abstract next( limit?: number ): Promise< DocumentObject[] >
121
-
122
- /**
123
- * Retrieves the number of documents matching the query stored in the query object
124
- * Implement the required logic to retrieve the number of documents that match the
125
- * requirements in the query object from your concrete the data source
126
- * @param queryObject the query object containing the query operations
127
- * @param collectionName the name of the collection where the documents are stored
128
- * @returns a promise resolving to the number of documents matching the query
129
- * @see QueryObject
130
- */
131
- abstract count( queryObject: QueryObject<DocumentObject>, collectionName: string ): Promise<number>
132
-
133
- /**
134
- * Utility method to convert a query object to a property path query object
135
- *
136
- * @param queryObject the query object to be converted
137
- * @returns a property path query object
138
- * @example
139
- * const queryObject = {
140
- * operations: [{ property: 'name', operator: '==', value: { ancestorName: { father: 'Felipe' }}]
141
- * }
142
- * const propPathQueryObject = DataSource.toPropertyPathQueryObject( queryObject )
143
- * // returned value: [{ property: 'name.ancestorName.father', operator: '==', value: 'Felipe' }]
144
- */
145
- static toPropertyPathOperations<T extends Persistent>( operations: QueryOperation<T>[] ): QueryOperation<T>[] {
146
- if ( !operations ) return []
147
-
148
- return operations.map( operation => {
149
-
150
- if ( DataSource.isArrayOperator( operation.operator ) && operation.value[0] instanceof Persistent ) {
151
- return {
152
- property: Persistent.searchableArrayNameFor( operation.property as string ),
153
- operator: operation.operator,
154
- value: ( operation.value as unknown as Persistent[] ).map( v => v.id ) as any,
155
- aggregate: operation.aggregate
156
- } as QueryOperation<T>
157
- }
158
-
159
- const [ path, value ] = this.toPropertyPathValue( operation.value )
160
- const propPath = `${ String( operation.property ) }${ path? '.'+path : '' }`
161
-
162
- return {
163
- property: propPath,
164
- operator: operation.operator,
165
- value,
166
- aggregate: operation.aggregate
167
- } as QueryOperation<T>
168
- })
169
- }
170
-
171
- static isArrayOperator( operator: QueryOperator ): boolean {
172
- return operator === 'containsAny' || operator === 'contains' //|| operator === 'in' || operator === '!in'
173
- }
174
-
175
- private static toPropertyPathValue( obj: {} ): [ string | undefined, unknown ] {
176
- if ( typeof obj === 'object' && !Array.isArray( obj ) ) {
177
- const propName = Object.keys( obj )[0]!
178
- const [ propPath, value ] = this.toPropertyPathValue( obj[ propName ] )
179
- return [ `${ propName }${ propPath? '.'+propPath : '' }`, value ]
180
- }
181
- else {
182
- return [ undefined, obj ]
183
- }
184
-
185
- }
186
- }
@@ -1,100 +0,0 @@
1
- import { DocumentObject, JsonDataSource, Model, Store } from '..'
2
- import { TestUser } from './mocks/test-user'
3
-
4
- describe( 'Json DataSource', ()=>{
5
- let datasource: JsonDataSource
6
- const resolveDelay = 50
7
-
8
- describe( 'Delayed promise resolution', ()=>{
9
-
10
- beforeEach(()=>{
11
- datasource = new JsonDataSource({
12
- collection: { a: { id: 'a' }, b: { id: 'b' }, c: { id: 'c' } } as any
13
- }).simulateDelay( resolveDelay )
14
- Store.useDataSource( datasource )
15
- })
16
-
17
- it( 'should fail if no wait to resolve', async ()=>{
18
- let result: DocumentObject | undefined = undefined
19
- datasource.findById( 'a', 'collection' ).then( data => result = data )
20
- expect( result ).not.toBeDefined()
21
- await datasource.wait()
22
- expect( result ).toBeDefined()
23
- })
24
-
25
- it( 'should wait promises to resolve', async ()=>{
26
- let result: DocumentObject | undefined = undefined
27
- datasource.findById( 'a', 'collection' ).then( data => result = data )
28
- await datasource.wait()
29
- expect( result ).toBeDefined()
30
- })
31
-
32
- it( 'should accumulate promises', async ()=>{
33
- datasource.findById( 'a', 'collection' )
34
- datasource.findById( 'b', 'collection' )
35
- datasource.findById( 'c', 'collection' )
36
- const promises = await datasource.wait()
37
- expect( promises ).toHaveLength( 3 )
38
- })
39
-
40
- it( 'should remove resolved promises', ( done )=>{
41
- datasource.findById( 'b', 'collection' )
42
- datasource.findById( 'a', 'collection' )
43
- setTimeout(
44
- async ()=>{
45
- datasource.findById( 'c', 'collection' )
46
- const promises = await datasource.wait()
47
- expect( promises ).toHaveLength( 1 )
48
- done()
49
- },
50
- resolveDelay * 3
51
- )
52
- })
53
-
54
- it( 'should work with save', async ()=>{
55
- datasource.save({ testCollection: [{ id: "id" } as any ]})
56
- await datasource.wait()
57
- expect( datasource.rawData ).toEqual( expect.objectContaining({
58
- testCollection: { id: { id: "id" } }
59
- }))
60
- })
61
-
62
- it( 'should work with model', async ()=>{
63
- const model = Store.getModel( 'TestUser' )
64
- model.save( new TestUser('id456') )
65
- await datasource.wait()
66
- expect( datasource.rawData.TestUser ).toEqual( expect.objectContaining({
67
- id456: expect.anything()
68
- }))
69
- })
70
-
71
- })
72
-
73
- describe( 'Error simulation', ()=>{
74
- let model: Model<TestUser>
75
-
76
- beforeAll(()=>{
77
- datasource = new JsonDataSource({
78
- collection: { a: { id: 'a' }, b: { id: 'b' }, c: { id: 'c' } } as any
79
- }).simulateError( 'Simulated error' )
80
- Store.useDataSource( datasource )
81
- model = Store.getModel<TestUser>( 'TestUser' )
82
- })
83
-
84
- it( 'should simulate error on findById', ()=>{
85
- expect( model.findById( 'a' ) ).rejects.toThrow( 'Simulated error' )
86
- })
87
-
88
- it( 'should simulate error on find', ()=>{
89
- expect( model.find().get() ).rejects.toThrow( 'Simulated error' )
90
- })
91
-
92
- it( 'should simulate error on save', ()=>{
93
- expect( model.save( new TestUser('id') ) ).rejects.toThrow( 'Simulated error' )
94
- })
95
-
96
- it( 'should simulate error on delete', ()=>{
97
- expect( model.delete( 'b' ) ).rejects.toThrow( 'Simulated error' )
98
- })
99
- })
100
- })
@@ -1,256 +0,0 @@
1
- import { Collections, Persistent, PersistentObject } from '../persistent/persistent';
2
- import { DataSource, DocumentObject, QueryObject, QueryOperation } from "./data-source";
3
-
4
- export interface JsonRawData {
5
- [ collection: string ]: {
6
- [ documentId: string ]: PersistentObject<Persistent>
7
- }
8
- }
9
-
10
- export interface ErrorOnOperation {
11
- store: string
12
- find: string
13
- findById: string
14
- delete: string
15
- }
16
-
17
- type QueryProcessors = {
18
- [ P in keyof Required<QueryObject<unknown>> ]: Function
19
- }
20
-
21
- /**
22
- * A concrete implementation of the DataSource interface uses an in-memory data store
23
- * initialized by a JSON object.
24
- * It is useful for testing purposes.
25
- * The data in the JSON object is not persisted.
26
- */
27
- export class JsonDataSource implements DataSource {
28
-
29
- /**
30
- * @param jsonRawData the JSON object to be used as data store
31
- */
32
- constructor( jsonRawData?: JsonRawData ) {
33
- if ( jsonRawData ) this._jsonRawData = jsonRawData;
34
- }
35
-
36
- /**
37
- * Set the JSON object to initialize the data store. Use to set the it after
38
- * the constructor has been called.
39
- * @param jsonRawData the JSON object to be used as data store
40
- */
41
- setDataStore( rawDataStore: JsonRawData ) {
42
- this._jsonRawData = rawDataStore;
43
- return this
44
- }
45
-
46
- /**
47
- * Introduce a delay in the execution of operations to simulate a real data source
48
- * @param miliSeconds the number of milliseconds to delay the execution of operations
49
- * @returns a chainable reference to this object
50
- */
51
- simulateDelay( miliSeconds: number ) {
52
- this._simulateDelay = miliSeconds
53
- return this
54
- }
55
-
56
- findById( id: string, collectionName: string ): Promise< DocumentObject > {
57
- if ( this._simulateError?.findById ) throw new Error( this._simulateError.findById )
58
-
59
- return this.resolveWithDelay( this._jsonRawData[ collectionName ]?.[ id ] )
60
- }
61
-
62
- save( collections: Collections ): Promise< void > {
63
- if ( this._simulateError?.store ) throw new Error( this._simulateError.store )
64
-
65
- Object.entries( collections ).forEach(([ collectionName, collection ]) => {
66
- if ( !this._jsonRawData[ collectionName ] ) this._jsonRawData[ collectionName ] = {}
67
- collection?.forEach( document => {
68
- this._jsonRawData[ collectionName ]![ document.id ] = document
69
- })
70
- })
71
-
72
- return this.resolveWithDelay()
73
- }
74
-
75
- find( queryObject: QueryObject<DocumentObject>, collectionName: string ): Promise< DocumentObject[] > {
76
- if ( this._simulateError?.find ) throw new Error( this._simulateError.find )
77
-
78
- const rawDataArray = Object.values( this._jsonRawData[ collectionName ] || {} )
79
- if ( !queryObject ) return this.resolveWithDelay( rawDataArray )
80
-
81
- this._lastLimit = queryObject.limit || 0
82
- this._cursor = 0
83
-
84
- this._lastMatchingDocs = Object.entries( queryObject ).reduce(
85
- ( prevDocs, [ processMethod, value ]) => {
86
-
87
- return this.queryProcessor( prevDocs, processMethod as any, value )
88
-
89
- }, Object.values( rawDataArray )
90
- )
91
-
92
- return this.resolveWithDelay( this._lastMatchingDocs.slice( 0, queryObject.limit ) )
93
- }
94
-
95
- delete( id: string, collectionName: string ): Promise<void> {
96
- if ( this._simulateError?.delete ) throw new Error( this._simulateError.delete )
97
-
98
- delete this._jsonRawData[ collectionName ]![ id ]
99
- return this.resolveWithDelay()
100
- }
101
-
102
- next( limit?: number ): Promise< DocumentObject[] > {
103
- if ( limit ) this._lastLimit = limit
104
- this.incCursor( this._lastLimit )
105
-
106
- return this.resolveWithDelay( this._lastMatchingDocs.slice( this._cursor, this._cursor + this._lastLimit ) )
107
- }
108
-
109
- count( queryObject: QueryObject<DocumentObject>, collectionName: string ): Promise<number> {
110
- return this.resolveWithDelay(
111
- Object.keys( this._jsonRawData[ collectionName ] ?? {} ).length
112
- )
113
- }
114
-
115
- /**
116
- * @returns the raw data store data as a JSON object
117
- */
118
- get rawData() {
119
- return this._jsonRawData
120
- }
121
-
122
- /**
123
- * Wait for all pending promises to be resolved
124
- * @returns a promise that resolves when all pending promises are resolved
125
- */
126
- wait() {
127
- return Promise.all([ ...this._pendingPromises ])
128
- }
129
-
130
- private incCursor( amount: number ) {
131
- this._cursor += amount
132
- if ( this._cursor > this._lastMatchingDocs.length ) {
133
- this._cursor = this._lastMatchingDocs.length
134
- }
135
- }
136
-
137
- simulateError( error: string | ErrorOnOperation | undefined ): this {
138
- if ( error === undefined ) {
139
- this._simulateError = undefined
140
- return this
141
- }
142
-
143
- if ( typeof error === 'string' ) {
144
- this._simulateError = {
145
- store: error,
146
- find: error,
147
- findById: error,
148
- delete: error
149
- }
150
- }
151
- else this._simulateError = error
152
-
153
- return this
154
- }
155
-
156
- private decCursor( amount: number ) {
157
- this._cursor -= amount
158
- if ( this._cursor < 0 ) {
159
- this._cursor = 0
160
- return true
161
- }
162
- return false
163
- }
164
-
165
- private queryProcessor<T, P extends keyof QueryProcessors>( docs: DocumentObject[], processMethod: P, value: QueryObject<T>[P] ) {
166
-
167
- const processors: QueryProcessors = {
168
-
169
- limit: ( limit: number ) => docs, //.slice( 0, limit ),
170
-
171
- operations: ( operations: QueryOperation<T>[] ) => this.retrieveQueryDocs( docs, operations ),
172
-
173
- sort: ({ order, propertyName }) => docs.sort( ( a, b ) => {
174
- if ( order === 'asc' ) {
175
- return this.deepValue( a, propertyName ) > this.deepValue( b, propertyName )? 1 : -1
176
- }
177
- else {
178
- return this.deepValue( a, propertyName ) < this.deepValue( b, propertyName )? 1 : -1
179
- }
180
- })
181
- }
182
-
183
- return processors[ processMethod ]( value )
184
- }
185
-
186
- private retrieveQueryDocs<T>( docs: DocumentObject[], queryOperations: QueryOperation<T>[] ): DocumentObject[] {
187
- return queryOperations.reduce(( prevDocs, queryOperation, i ) => {
188
- if ( queryOperation.aggregate ) {
189
- const aggregate = docs.filter( doc => this.isQueryMatched( doc, queryOperation ) )
190
- if ( i === 0 ) return aggregate
191
- else return prevDocs.concat( aggregate )
192
- }
193
- else {
194
- return prevDocs.filter( doc => this.isQueryMatched( doc, queryOperation ) )
195
- }
196
- }, docs )
197
- }
198
-
199
- private deepValue( obj: {}, propertyPath: string /*like person.name.firstName*/) {
200
- const propChain = propertyPath.split( '.' )
201
- return propChain.reduce(( value, prop ) => value[ prop ], obj )
202
- }
203
-
204
- private isQueryMatched<T>( doc: DocumentObject, queryOperation: QueryOperation<T> ) {
205
- const queryOperator = {
206
- '==': <U>(a: U, b: U) => a === b,
207
- '!=': <U>(a: U, b: U) => a !== b,
208
- '<': <U>(a: U, b: U) => a < b,
209
- '<=': <U>(a: U, b: U) => a <= b,
210
- '>': <U>(a: U, b: U) => a > b,
211
- '>=': <U>(a: U, b: U) => a >= b,
212
- 'containsAny': <U>(a: U[], b: U[]) => a?.some( v => b?.includes( v ) ),
213
- 'contains': <U>(a: U[], b: U) => a?.includes( b ),
214
- }
215
-
216
- const { property, value, operator } = queryOperation
217
- const [ propValue, v ] = this.retrieveValuesToCompare( doc, property as string, value )
218
-
219
- return queryOperator[ operator ]( propValue, v )
220
- }
221
-
222
- private retrieveValuesToCompare( doc: DocumentObject, propertyName: string, value: unknown ): [ any, any ] {
223
- const propertyValue = doc[ propertyName ]
224
-
225
- if ( propertyValue && typeof value === 'object' && !Array.isArray( value )) {
226
- const propName = Object.keys( value! )[0]!
227
- var [ propVal, val ] = this.retrieveValuesToCompare( propertyValue, propName, value?.[ propName ] )
228
- }
229
-
230
- return [ propVal || propertyValue, val || value ]
231
- }
232
-
233
- private resolveWithDelay<T>( data?: T ): Promise<T> {
234
- if ( this._simulateDelay <=0 ) return Promise.resolve( data! )
235
-
236
- const promise = new Promise<T>( resolve => {
237
- setTimeout(
238
- ()=> resolve( data! ),
239
- this._simulateDelay
240
- )
241
- })
242
- this._pendingPromises.push( promise )
243
- promise.finally(
244
- ()=> this._pendingPromises = this._pendingPromises.filter( p => p === promise )
245
- )
246
- return promise
247
- }
248
-
249
- private _jsonRawData: JsonRawData = {}
250
- private _lastMatchingDocs: DocumentObject[] = []
251
- private _lastLimit: number = 0
252
- private _cursor: number = 0
253
- private _simulateDelay: number = 0
254
- private _pendingPromises: Promise<any>[] = []
255
- private _simulateError: ErrorOnOperation | undefined
256
- }