@speckle/objectloader2 2.26.1 → 2.26.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.
- package/dist/commonjs/core/objectLoader2.d.ts.map +1 -1
- package/dist/commonjs/core/objectLoader2.js +1 -9
- package/dist/commonjs/core/objectLoader2.js.map +1 -1
- package/dist/commonjs/core/objectLoader2Factory.d.ts +7 -2
- package/dist/commonjs/core/objectLoader2Factory.d.ts.map +1 -1
- package/dist/commonjs/core/objectLoader2Factory.js +14 -4
- package/dist/commonjs/core/objectLoader2Factory.js.map +1 -1
- package/dist/commonjs/core/objectLoader2Factory.test.d.ts +2 -0
- package/dist/commonjs/core/objectLoader2Factory.test.d.ts.map +1 -0
- package/dist/commonjs/core/objectLoader2Factory.test.js +106 -0
- package/dist/commonjs/core/objectLoader2Factory.test.js.map +1 -0
- package/dist/commonjs/core/options.d.ts +2 -0
- package/dist/commonjs/core/options.d.ts.map +1 -1
- package/dist/commonjs/core/stages/cacheReader.d.ts +2 -2
- package/dist/commonjs/core/stages/cacheReader.d.ts.map +1 -1
- package/dist/commonjs/core/stages/cacheReader.js.map +1 -1
- package/dist/commonjs/core/stages/cacheWriter.d.ts +2 -2
- package/dist/commonjs/core/stages/cacheWriter.d.ts.map +1 -1
- package/dist/commonjs/core/stages/cacheWriter.js +4 -4
- package/dist/commonjs/core/stages/cacheWriter.js.map +1 -1
- package/dist/commonjs/core/stages/serverDownloader.d.ts +2 -1
- package/dist/commonjs/core/stages/serverDownloader.d.ts.map +1 -1
- package/dist/commonjs/core/stages/serverDownloader.js +3 -2
- package/dist/commonjs/core/stages/serverDownloader.js.map +1 -1
- package/dist/commonjs/deferment/defermentManager.d.ts +18 -2
- package/dist/commonjs/deferment/defermentManager.d.ts.map +1 -1
- package/dist/commonjs/deferment/defermentManager.js +30 -3
- package/dist/commonjs/deferment/defermentManager.js.map +1 -1
- package/dist/commonjs/deferment/defermentManager.test.js +11 -11
- package/dist/commonjs/deferment/defermentManager.test.js.map +1 -1
- package/dist/commonjs/queues/batchingQueue.d.ts +0 -2
- package/dist/commonjs/queues/batchingQueue.d.ts.map +1 -1
- package/dist/commonjs/queues/batchingQueue.dispose.test.js +1 -3
- package/dist/commonjs/queues/batchingQueue.dispose.test.js.map +1 -1
- package/dist/commonjs/queues/batchingQueue.js +4 -3
- package/dist/commonjs/queues/batchingQueue.js.map +1 -1
- package/dist/commonjs/types/types.d.ts +5 -0
- package/dist/commonjs/types/types.d.ts.map +1 -1
- package/dist/esm/core/objectLoader2.d.ts.map +1 -1
- package/dist/esm/core/objectLoader2.js +1 -9
- package/dist/esm/core/objectLoader2.js.map +1 -1
- package/dist/esm/core/objectLoader2Factory.d.ts +7 -2
- package/dist/esm/core/objectLoader2Factory.d.ts.map +1 -1
- package/dist/esm/core/objectLoader2Factory.js +14 -4
- package/dist/esm/core/objectLoader2Factory.js.map +1 -1
- package/dist/esm/core/objectLoader2Factory.test.d.ts +2 -0
- package/dist/esm/core/objectLoader2Factory.test.d.ts.map +1 -0
- package/dist/esm/core/objectLoader2Factory.test.js +104 -0
- package/dist/esm/core/objectLoader2Factory.test.js.map +1 -0
- package/dist/esm/core/options.d.ts +2 -0
- package/dist/esm/core/options.d.ts.map +1 -1
- package/dist/esm/core/stages/cacheReader.d.ts +2 -2
- package/dist/esm/core/stages/cacheReader.d.ts.map +1 -1
- package/dist/esm/core/stages/cacheReader.js.map +1 -1
- package/dist/esm/core/stages/cacheWriter.d.ts +2 -2
- package/dist/esm/core/stages/cacheWriter.d.ts.map +1 -1
- package/dist/esm/core/stages/cacheWriter.js +4 -4
- package/dist/esm/core/stages/cacheWriter.js.map +1 -1
- package/dist/esm/core/stages/serverDownloader.d.ts +2 -1
- package/dist/esm/core/stages/serverDownloader.d.ts.map +1 -1
- package/dist/esm/core/stages/serverDownloader.js +3 -2
- package/dist/esm/core/stages/serverDownloader.js.map +1 -1
- package/dist/esm/deferment/defermentManager.d.ts +18 -2
- package/dist/esm/deferment/defermentManager.d.ts.map +1 -1
- package/dist/esm/deferment/defermentManager.js +28 -2
- package/dist/esm/deferment/defermentManager.js.map +1 -1
- package/dist/esm/deferment/defermentManager.test.js +11 -11
- package/dist/esm/deferment/defermentManager.test.js.map +1 -1
- package/dist/esm/queues/batchingQueue.d.ts +0 -2
- package/dist/esm/queues/batchingQueue.d.ts.map +1 -1
- package/dist/esm/queues/batchingQueue.dispose.test.js +1 -3
- package/dist/esm/queues/batchingQueue.dispose.test.js.map +1 -1
- package/dist/esm/queues/batchingQueue.js +4 -3
- package/dist/esm/queues/batchingQueue.js.map +1 -1
- package/dist/esm/types/types.d.ts +5 -0
- package/dist/esm/types/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/core/objectLoader2.ts +3 -13
- package/src/core/objectLoader2Factory.test.ts +135 -0
- package/src/core/objectLoader2Factory.ts +29 -6
- package/src/core/options.ts +2 -0
- package/src/core/stages/cacheReader.ts +3 -3
- package/src/core/stages/cacheWriter.ts +5 -5
- package/src/core/stages/serverDownloader.ts +7 -4
- package/src/deferment/defermentManager.test.ts +11 -11
- package/src/deferment/defermentManager.ts +39 -3
- package/src/queues/batchingQueue.dispose.test.ts +1 -3
- package/src/queues/batchingQueue.ts +3 -5
- package/src/types/types.ts +5 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
3
|
+
import { ObjectLoader2Factory } from './objectLoader2Factory.js'
|
|
4
|
+
import { Base } from '../types/types.js'
|
|
5
|
+
|
|
6
|
+
describe('ObjectLoader2Factory', () => {
|
|
7
|
+
let testObjects: Base[]
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
testObjects = [
|
|
11
|
+
{
|
|
12
|
+
id: 'root-id',
|
|
13
|
+
speckle_type: 'Base',
|
|
14
|
+
__closure: {
|
|
15
|
+
'child-1': 1,
|
|
16
|
+
'child-2': 2
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'child-1',
|
|
21
|
+
speckle_type: 'Base'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'child-2',
|
|
25
|
+
speckle_type: 'Base'
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('createFromObjects', () => {
|
|
31
|
+
it('should create ObjectLoader2 from array of objects', async () => {
|
|
32
|
+
const loader = ObjectLoader2Factory.createFromObjects(testObjects)
|
|
33
|
+
|
|
34
|
+
expect(loader).toBeDefined()
|
|
35
|
+
|
|
36
|
+
// Test that we can get the root object
|
|
37
|
+
const rootObject = await loader.getRootObject()
|
|
38
|
+
expect(rootObject?.baseId).toBe('root-id')
|
|
39
|
+
expect(rootObject?.base?.speckle_type).toBe('Base')
|
|
40
|
+
|
|
41
|
+
await loader.disposeAsync()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('should use first object as root', async () => {
|
|
45
|
+
const loader = ObjectLoader2Factory.createFromObjects(testObjects)
|
|
46
|
+
|
|
47
|
+
const rootObject = await loader.getRootObject()
|
|
48
|
+
expect(rootObject?.baseId).toBe('root-id')
|
|
49
|
+
expect(rootObject?.base?.__closure).toEqual({
|
|
50
|
+
'child-1': 1,
|
|
51
|
+
'child-2': 2
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
await loader.disposeAsync()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should allow iteration over all objects', async () => {
|
|
58
|
+
const loader = ObjectLoader2Factory.createFromObjects(testObjects)
|
|
59
|
+
|
|
60
|
+
const objects: Base[] = []
|
|
61
|
+
for await (const obj of loader.getObjectIterator()) {
|
|
62
|
+
objects.push(obj)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
expect(objects).toHaveLength(3)
|
|
66
|
+
expect(objects[0].id).toBe('root-id')
|
|
67
|
+
|
|
68
|
+
await loader.disposeAsync()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should get total object count correctly', async () => {
|
|
72
|
+
const loader = ObjectLoader2Factory.createFromObjects(testObjects)
|
|
73
|
+
|
|
74
|
+
const totalCount = await loader.getTotalObjectCount()
|
|
75
|
+
expect(totalCount).toBe(3) // root + 2 children
|
|
76
|
+
|
|
77
|
+
await loader.disposeAsync()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should handle empty objects array', () => {
|
|
81
|
+
expect(() => {
|
|
82
|
+
ObjectLoader2Factory.createFromObjects([])
|
|
83
|
+
}).toThrow()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should get individual objects by id', async () => {
|
|
87
|
+
const loader = ObjectLoader2Factory.createFromObjects(testObjects)
|
|
88
|
+
|
|
89
|
+
const rootObj = await loader.getObject({ id: 'root-id' })
|
|
90
|
+
expect(rootObj.id).toBe('root-id')
|
|
91
|
+
expect(rootObj.speckle_type).toBe('Base')
|
|
92
|
+
|
|
93
|
+
const child1 = await loader.getObject({ id: 'child-1' })
|
|
94
|
+
expect(child1.id).toBe('child-1')
|
|
95
|
+
|
|
96
|
+
const child2 = await loader.getObject({ id: 'child-2' })
|
|
97
|
+
expect(child2.id).toBe('child-2')
|
|
98
|
+
|
|
99
|
+
await loader.disposeAsync()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should get individual objects by id that does not exist', async () => {
|
|
103
|
+
const loader = ObjectLoader2Factory.createFromObjects(testObjects)
|
|
104
|
+
|
|
105
|
+
const rootObj = await loader.getObject({ id: 'root-id' })
|
|
106
|
+
expect(rootObj.id).toBe('root-id')
|
|
107
|
+
expect(rootObj.speckle_type).toBe('Base')
|
|
108
|
+
|
|
109
|
+
const getObjectPromise = loader.getObject({ id: 'child-11111' })
|
|
110
|
+
await expect(getObjectPromise).rejects.toThrow()
|
|
111
|
+
|
|
112
|
+
await loader.disposeAsync()
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('createFromJSON', () => {
|
|
117
|
+
it('should create ObjectLoader2 from JSON string', async () => {
|
|
118
|
+
const json = JSON.stringify(testObjects)
|
|
119
|
+
const loader = ObjectLoader2Factory.createFromJSON(json)
|
|
120
|
+
|
|
121
|
+
expect(loader).toBeDefined()
|
|
122
|
+
|
|
123
|
+
const rootObject = await loader.getRootObject()
|
|
124
|
+
expect(rootObject?.baseId).toBe('root-id')
|
|
125
|
+
|
|
126
|
+
await loader.disposeAsync()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should handle invalid JSON', () => {
|
|
130
|
+
expect(() => {
|
|
131
|
+
ObjectLoader2Factory.createFromJSON('invalid json')
|
|
132
|
+
}).toThrow()
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
})
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DefermentManager, MemoryOnlyDeferment } from '../deferment/defermentManager.js'
|
|
2
|
+
import {
|
|
3
|
+
CustomLogger,
|
|
4
|
+
Fetcher,
|
|
5
|
+
getFeatureFlag,
|
|
6
|
+
ObjectLoader2Flags
|
|
7
|
+
} from '../types/functions.js'
|
|
8
|
+
import { Base, ObjectAttributeMask } from '../types/types.js'
|
|
3
9
|
import { ObjectLoader2 } from './objectLoader2.js'
|
|
4
10
|
import { IndexedDatabase } from './stages/indexedDatabase.js'
|
|
5
11
|
import { MemoryDatabase } from './stages/memory/memoryDatabase.js'
|
|
@@ -10,6 +16,10 @@ export interface ObjectLoader2FactoryOptions {
|
|
|
10
16
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
11
17
|
keyRange?: { bound: Function; lowerBound: Function; upperBound: Function }
|
|
12
18
|
indexedDB?: IDBFactory
|
|
19
|
+
fetch?: Fetcher
|
|
20
|
+
attributeMask?: ObjectAttributeMask
|
|
21
|
+
useCache?: boolean
|
|
22
|
+
debug?: boolean
|
|
13
23
|
logger?: CustomLogger
|
|
14
24
|
}
|
|
15
25
|
|
|
@@ -22,6 +32,7 @@ export class ObjectLoader2Factory {
|
|
|
22
32
|
})
|
|
23
33
|
const loader = new ObjectLoader2({
|
|
24
34
|
rootId: root.id,
|
|
35
|
+
deferments: new MemoryOnlyDeferment(records),
|
|
25
36
|
database: new MemoryDatabase({ items: records }),
|
|
26
37
|
downloader: new MemoryDownloader(root.id, records)
|
|
27
38
|
})
|
|
@@ -40,13 +51,21 @@ export class ObjectLoader2Factory {
|
|
|
40
51
|
token?: string
|
|
41
52
|
headers?: Headers
|
|
42
53
|
options?: ObjectLoader2FactoryOptions
|
|
54
|
+
attributeMask?: ObjectAttributeMask
|
|
43
55
|
}): ObjectLoader2 {
|
|
44
56
|
const log = ObjectLoader2Factory.getLogger(params.options?.logger)
|
|
45
57
|
let database
|
|
46
|
-
if (
|
|
58
|
+
if (
|
|
59
|
+
params.options?.debug === true ||
|
|
60
|
+
getFeatureFlag(ObjectLoader2Flags.DEBUG) === 'true'
|
|
61
|
+
) {
|
|
47
62
|
this.logger('Using DEBUG mode for ObjectLoader2Factory')
|
|
48
63
|
}
|
|
49
|
-
|
|
64
|
+
const useCache = params.options?.useCache ?? true
|
|
65
|
+
const flag = getFeatureFlag(ObjectLoader2Flags.USE_CACHE)
|
|
66
|
+
const flagAllowsCache = flag !== 'false'
|
|
67
|
+
|
|
68
|
+
if (useCache && flagAllowsCache) {
|
|
50
69
|
database = new IndexedDatabase({
|
|
51
70
|
indexedDB: params.options?.indexedDB,
|
|
52
71
|
keyRange: params.options?.keyRange
|
|
@@ -59,18 +78,22 @@ export class ObjectLoader2Factory {
|
|
|
59
78
|
'Disabled persistent caching for ObjectLoader2. Using MemoryDatabase'
|
|
60
79
|
)
|
|
61
80
|
}
|
|
81
|
+
const logger = log || (((): void => {}) as CustomLogger)
|
|
62
82
|
const loader = new ObjectLoader2({
|
|
63
83
|
rootId: params.objectId,
|
|
84
|
+
deferments: new DefermentManager(logger),
|
|
64
85
|
downloader: new ServerDownloader({
|
|
65
86
|
serverUrl: params.serverUrl,
|
|
66
87
|
streamId: params.streamId,
|
|
67
88
|
objectId: params.objectId,
|
|
68
89
|
token: params.token,
|
|
69
90
|
headers: params.headers,
|
|
70
|
-
|
|
91
|
+
fetch: params.options?.fetch,
|
|
92
|
+
attributeMask: params.attributeMask,
|
|
93
|
+
logger
|
|
71
94
|
}),
|
|
72
95
|
database,
|
|
73
|
-
logger
|
|
96
|
+
logger
|
|
74
97
|
})
|
|
75
98
|
return loader
|
|
76
99
|
}
|
package/src/core/options.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { Deferment } from '../deferment/defermentManager.js'
|
|
1
2
|
import { CustomLogger } from '../types/functions.js'
|
|
2
3
|
import { Base } from '../types/types.js'
|
|
3
4
|
import { Downloader, Database } from './interfaces.js'
|
|
4
5
|
|
|
5
6
|
export interface ObjectLoader2Options {
|
|
6
7
|
rootId: string
|
|
8
|
+
deferments: Deferment
|
|
7
9
|
downloader: Downloader
|
|
8
10
|
database: Database
|
|
9
11
|
logger?: CustomLogger
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Deferment } from '../../deferment/defermentManager.js'
|
|
2
2
|
import BatchingQueue from '../../queues/batchingQueue.js'
|
|
3
3
|
import Queue from '../../queues/queue.js'
|
|
4
4
|
import { CustomLogger } from '../../types/functions.js'
|
|
@@ -8,7 +8,7 @@ import { CacheOptions } from '../options.js'
|
|
|
8
8
|
|
|
9
9
|
export class CacheReader {
|
|
10
10
|
#database: Database
|
|
11
|
-
#defermentManager:
|
|
11
|
+
#defermentManager: Deferment
|
|
12
12
|
#logger: CustomLogger
|
|
13
13
|
#options: CacheOptions
|
|
14
14
|
#readQueue: BatchingQueue<string> | undefined
|
|
@@ -17,7 +17,7 @@ export class CacheReader {
|
|
|
17
17
|
|
|
18
18
|
constructor(
|
|
19
19
|
database: Database,
|
|
20
|
-
defermentManager:
|
|
20
|
+
defermentManager: Deferment,
|
|
21
21
|
logger: CustomLogger,
|
|
22
22
|
options: CacheOptions
|
|
23
23
|
) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Deferment } from '../../deferment/defermentManager.js'
|
|
2
2
|
import BatchingQueue from '../../queues/batchingQueue.js'
|
|
3
3
|
import Queue from '../../queues/queue.js'
|
|
4
4
|
import { CustomLogger } from '../../types/functions.js'
|
|
@@ -9,7 +9,7 @@ import { CacheOptions } from '../options.js'
|
|
|
9
9
|
export class CacheWriter implements Queue<Item> {
|
|
10
10
|
#writeQueue: BatchingQueue<Item> | undefined
|
|
11
11
|
#database: Database
|
|
12
|
-
#
|
|
12
|
+
#deferment: Deferment
|
|
13
13
|
#requestItem: (id: string) => void
|
|
14
14
|
#logger: CustomLogger
|
|
15
15
|
#options: CacheOptions
|
|
@@ -18,14 +18,14 @@ export class CacheWriter implements Queue<Item> {
|
|
|
18
18
|
constructor(
|
|
19
19
|
database: Database,
|
|
20
20
|
logger: CustomLogger,
|
|
21
|
-
|
|
21
|
+
deferment: Deferment,
|
|
22
22
|
options: CacheOptions,
|
|
23
23
|
requestItem: (id: string) => void
|
|
24
24
|
) {
|
|
25
25
|
this.#database = database
|
|
26
26
|
this.#options = options
|
|
27
27
|
this.#logger = logger
|
|
28
|
-
this.#
|
|
28
|
+
this.#deferment = deferment
|
|
29
29
|
this.#requestItem = requestItem
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -40,7 +40,7 @@ export class CacheWriter implements Queue<Item> {
|
|
|
40
40
|
})
|
|
41
41
|
}
|
|
42
42
|
this.#writeQueue.add(item.baseId, item)
|
|
43
|
-
this.#
|
|
43
|
+
this.#deferment.undefer(item, this.#requestItem)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
async writeAll(items: Item[]): Promise<void> {
|
|
@@ -2,7 +2,7 @@ import BatchingQueue from '../../queues/batchingQueue.js'
|
|
|
2
2
|
import Queue from '../../queues/queue.js'
|
|
3
3
|
import { ObjectLoaderRuntimeError } from '../../types/errors.js'
|
|
4
4
|
import { CustomLogger, Fetcher, indexOf, isBase, take } from '../../types/functions.js'
|
|
5
|
-
import { Item } from '../../types/types.js'
|
|
5
|
+
import { Item, ObjectAttributeMask } from '../../types/types.js'
|
|
6
6
|
import { Downloader } from '../interfaces.js'
|
|
7
7
|
|
|
8
8
|
export interface ServerDownloaderOptions {
|
|
@@ -13,6 +13,7 @@ export interface ServerDownloaderOptions {
|
|
|
13
13
|
headers?: Headers
|
|
14
14
|
logger: CustomLogger
|
|
15
15
|
fetch?: Fetcher
|
|
16
|
+
attributeMask?: ObjectAttributeMask
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const MAX_SAFARI_DECODE_BYTES = 2 * 1024 * 1024 * 1024 - 1024 * 1024 // 2GB minus a margin
|
|
@@ -51,9 +52,10 @@ export default class ServerDownloader implements Downloader {
|
|
|
51
52
|
if (this.#options.token) {
|
|
52
53
|
this.#headers['Authorization'] = `Bearer ${this.#options.token}`
|
|
53
54
|
}
|
|
54
|
-
this.#requestUrlChildren = `${this.#options.serverUrl}/api/
|
|
55
|
+
this.#requestUrlChildren = `${this.#options.serverUrl}/api/v2/projects/${
|
|
55
56
|
this.#options.streamId
|
|
56
|
-
}
|
|
57
|
+
}/object-stream/`
|
|
58
|
+
|
|
57
59
|
this.#requestUrlRootObj = `${this.#options.serverUrl}/objects/${
|
|
58
60
|
this.#options.streamId
|
|
59
61
|
}/${this.#options.objectId}/single`
|
|
@@ -117,11 +119,12 @@ Chrome's behavior: Chrome generally handles larger data sizes without this speci
|
|
|
117
119
|
|
|
118
120
|
const start = performance.now()
|
|
119
121
|
this.#logger(`Downloading batch of ${batch.length} items...`)
|
|
122
|
+
const attributeMask = this.#options.attributeMask
|
|
120
123
|
const keys = new Set<string>(batch)
|
|
121
124
|
const response = await this.#fetch(url, {
|
|
122
125
|
method: 'POST',
|
|
123
126
|
headers: { ...headers, 'Content-Type': 'application/json' },
|
|
124
|
-
body: JSON.stringify({
|
|
127
|
+
body: JSON.stringify({ objectIds: batch, attributeMask })
|
|
125
128
|
})
|
|
126
129
|
|
|
127
130
|
this.#validateResponse(response)
|
|
@@ -11,7 +11,7 @@ describe('DefermentManager', () => {
|
|
|
11
11
|
get: vi.fn(),
|
|
12
12
|
add: vi.fn()
|
|
13
13
|
} as unknown as MemoryCache
|
|
14
|
-
const defermentManager = new DefermentManager(
|
|
14
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
15
15
|
expect(defermentManager).toBeDefined()
|
|
16
16
|
})
|
|
17
17
|
|
|
@@ -24,7 +24,7 @@ describe('DefermentManager', () => {
|
|
|
24
24
|
get,
|
|
25
25
|
add
|
|
26
26
|
} as unknown as MemoryCache
|
|
27
|
-
const defermentManager = new DefermentManager(
|
|
27
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
28
28
|
|
|
29
29
|
const item: Item = {
|
|
30
30
|
// eslint-disable-next-line camelcase
|
|
@@ -49,7 +49,7 @@ describe('DefermentManager', () => {
|
|
|
49
49
|
get,
|
|
50
50
|
add
|
|
51
51
|
} as unknown as MemoryCache
|
|
52
|
-
const defermentManager = new DefermentManager(
|
|
52
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
53
53
|
|
|
54
54
|
const [promise1, wasInCache1] = defermentManager.defer({ id: 'testId' })
|
|
55
55
|
const [promise2, wasInCache2] = defermentManager.defer({ id: 'testId' })
|
|
@@ -67,7 +67,7 @@ describe('DefermentManager', () => {
|
|
|
67
67
|
get,
|
|
68
68
|
add
|
|
69
69
|
} as unknown as MemoryCache
|
|
70
|
-
const defermentManager = new DefermentManager(
|
|
70
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
71
71
|
|
|
72
72
|
const [promise, wasInCache] = defermentManager.defer({ id: 'testId' })
|
|
73
73
|
|
|
@@ -83,7 +83,7 @@ describe('DefermentManager', () => {
|
|
|
83
83
|
get,
|
|
84
84
|
add
|
|
85
85
|
} as unknown as MemoryCache
|
|
86
|
-
const defermentManager = new DefermentManager(
|
|
86
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
87
87
|
|
|
88
88
|
defermentManager.dispose()
|
|
89
89
|
expect(() => defermentManager.defer({ id: 'testId' })).toThrow(
|
|
@@ -101,7 +101,7 @@ describe('DefermentManager', () => {
|
|
|
101
101
|
get,
|
|
102
102
|
add
|
|
103
103
|
} as unknown as MemoryCache
|
|
104
|
-
const defermentManager = new DefermentManager(
|
|
104
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
105
105
|
const requestItem = vi.fn()
|
|
106
106
|
|
|
107
107
|
const [promise] = defermentManager.defer({ id: 'testId' })
|
|
@@ -125,7 +125,7 @@ describe('DefermentManager', () => {
|
|
|
125
125
|
get,
|
|
126
126
|
add
|
|
127
127
|
} as unknown as MemoryCache
|
|
128
|
-
const defermentManager = new DefermentManager(
|
|
128
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
129
129
|
const requestItem = vi.fn()
|
|
130
130
|
|
|
131
131
|
const item: Item = { baseId: 'testId' }
|
|
@@ -141,7 +141,7 @@ describe('DefermentManager', () => {
|
|
|
141
141
|
get,
|
|
142
142
|
add
|
|
143
143
|
} as unknown as MemoryCache
|
|
144
|
-
const defermentManager = new DefermentManager(
|
|
144
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
145
145
|
const requestItem = vi.fn()
|
|
146
146
|
|
|
147
147
|
const item: Item = {
|
|
@@ -167,7 +167,7 @@ describe('DefermentManager', () => {
|
|
|
167
167
|
get,
|
|
168
168
|
add
|
|
169
169
|
} as unknown as MemoryCache
|
|
170
|
-
const defermentManager = new DefermentManager(
|
|
170
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
171
171
|
const requestItem = vi.fn()
|
|
172
172
|
|
|
173
173
|
defermentManager.dispose()
|
|
@@ -191,7 +191,7 @@ describe('DefermentManager', () => {
|
|
|
191
191
|
get,
|
|
192
192
|
add
|
|
193
193
|
} as unknown as MemoryCache
|
|
194
|
-
const defermentManager = new DefermentManager(
|
|
194
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
195
195
|
|
|
196
196
|
void defermentManager.defer({ id: 'testId' })
|
|
197
197
|
defermentManager.dispose()
|
|
@@ -207,7 +207,7 @@ describe('DefermentManager', () => {
|
|
|
207
207
|
get,
|
|
208
208
|
add
|
|
209
209
|
} as unknown as MemoryCache
|
|
210
|
-
const defermentManager = new DefermentManager(
|
|
210
|
+
const defermentManager = new DefermentManager(mockLogger, mockCache)
|
|
211
211
|
|
|
212
212
|
defermentManager.dispose()
|
|
213
213
|
// @ts-expect-error - accessing private property for testing
|
|
@@ -3,15 +3,50 @@ import { CustomLogger } from '../types/functions.js'
|
|
|
3
3
|
import { Item, Base } from '../types/types.js'
|
|
4
4
|
import { MemoryCache } from './MemoryCache.js'
|
|
5
5
|
|
|
6
|
-
export
|
|
6
|
+
export interface Deferment {
|
|
7
|
+
defer(params: { id: string }): [Promise<Base>, boolean]
|
|
8
|
+
undefer(item: Item, requestItem: (id: string) => void): void
|
|
9
|
+
dispose(): void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class MemoryOnlyDeferment implements Deferment {
|
|
13
|
+
private items: Map<string, Base>
|
|
14
|
+
|
|
15
|
+
constructor(items: Map<string, Base>) {
|
|
16
|
+
this.items = items
|
|
17
|
+
}
|
|
18
|
+
defer(params: { id: string }): [Promise<Base>, boolean] {
|
|
19
|
+
const item = this.items.get(params.id)
|
|
20
|
+
if (item) {
|
|
21
|
+
return [Promise.resolve(item), true]
|
|
22
|
+
}
|
|
23
|
+
return [Promise.reject(new Error('Not found in cache: ' + params.id)), false]
|
|
24
|
+
}
|
|
25
|
+
undefer(): void {
|
|
26
|
+
//no-op
|
|
27
|
+
}
|
|
28
|
+
dispose(): void {
|
|
29
|
+
//no-op
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class DefermentManager implements Deferment {
|
|
7
34
|
private outstanding: Map<string, DeferredBase> = new Map()
|
|
8
35
|
private logger: CustomLogger
|
|
9
36
|
private disposed = false
|
|
10
37
|
private cache: MemoryCache
|
|
11
38
|
|
|
12
|
-
constructor(
|
|
13
|
-
this.cache = cache
|
|
39
|
+
constructor(logger: CustomLogger, cache?: MemoryCache) {
|
|
14
40
|
this.logger = logger
|
|
41
|
+
this.cache =
|
|
42
|
+
cache ||
|
|
43
|
+
new MemoryCache(
|
|
44
|
+
{
|
|
45
|
+
maxSizeInMb: 500, // 500 MB
|
|
46
|
+
ttlms: 5_000 // 5 seconds
|
|
47
|
+
},
|
|
48
|
+
logger
|
|
49
|
+
)
|
|
15
50
|
}
|
|
16
51
|
|
|
17
52
|
defer(params: { id: string }): [Promise<Base>, boolean] {
|
|
@@ -55,5 +90,6 @@ export class DefermentManager {
|
|
|
55
90
|
this.disposed = true
|
|
56
91
|
this.logger('cleared deferments, left', this.outstanding.size)
|
|
57
92
|
this.outstanding.clear()
|
|
93
|
+
this.cache.dispose()
|
|
58
94
|
}
|
|
59
95
|
}
|
|
@@ -17,7 +17,7 @@ describe('BatchingQueue disposal', () => {
|
|
|
17
17
|
|
|
18
18
|
await queue.disposeAsync()
|
|
19
19
|
|
|
20
|
-
expect(processFunction).
|
|
20
|
+
expect(processFunction).not.toHaveBeenCalled()
|
|
21
21
|
expect(queue.count()).toBe(0)
|
|
22
22
|
expect(queue.isDisposed()).toBe(true)
|
|
23
23
|
})
|
|
@@ -52,8 +52,6 @@ describe('BatchingQueue disposal', () => {
|
|
|
52
52
|
resolveProcess()
|
|
53
53
|
await disposePromise
|
|
54
54
|
|
|
55
|
-
expect(processFunction).toHaveBeenCalledTimes(2)
|
|
56
|
-
expect(processFunction).toHaveBeenCalledWith(items2)
|
|
57
55
|
expect(queue.count()).toBe(0)
|
|
58
56
|
expect(queue.isDisposed()).toBe(true)
|
|
59
57
|
})
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { CustomLogger } from '../types/functions.js'
|
|
2
1
|
import KeyedQueue from './keyedQueue.js'
|
|
3
2
|
|
|
4
3
|
/**
|
|
@@ -13,7 +12,6 @@ export default class BatchingQueue<T> {
|
|
|
13
12
|
#processFunction: (batch: T[]) => Promise<void>
|
|
14
13
|
#timeoutId: ReturnType<typeof setTimeout> | null = null
|
|
15
14
|
#isProcessing = false
|
|
16
|
-
#logger: CustomLogger
|
|
17
15
|
|
|
18
16
|
#disposed = false
|
|
19
17
|
#batchTimeout: number
|
|
@@ -41,12 +39,10 @@ export default class BatchingQueue<T> {
|
|
|
41
39
|
batchSize: number
|
|
42
40
|
maxWaitTime: number
|
|
43
41
|
processFunction: (batch: T[]) => Promise<void>
|
|
44
|
-
logger?: CustomLogger
|
|
45
42
|
}) {
|
|
46
43
|
this.#batchSize = params.batchSize
|
|
47
44
|
this.#processFunction = params.processFunction
|
|
48
45
|
this.#batchTimeout = params.maxWaitTime
|
|
49
|
-
this.#logger = params.logger || ((): void => {})
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
async disposeAsync(): Promise<void> {
|
|
@@ -109,11 +105,13 @@ export default class BatchingQueue<T> {
|
|
|
109
105
|
this.#isProcessing = true
|
|
110
106
|
|
|
111
107
|
const batchToProcess = this.#getBatch(this.#batchSize)
|
|
108
|
+
if (this.#disposed) return
|
|
112
109
|
|
|
113
110
|
try {
|
|
114
111
|
await this.#processFunction(batchToProcess)
|
|
115
112
|
} catch (error) {
|
|
116
|
-
|
|
113
|
+
console.error('Batch processing failed:', error)
|
|
114
|
+
this.#disposed = true
|
|
117
115
|
} finally {
|
|
118
116
|
this.#isProcessing = false
|
|
119
117
|
}
|