@speckle/objectloader2 2.26.0 → 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.
Files changed (89) hide show
  1. package/dist/commonjs/core/objectLoader2.d.ts.map +1 -1
  2. package/dist/commonjs/core/objectLoader2.js +1 -9
  3. package/dist/commonjs/core/objectLoader2.js.map +1 -1
  4. package/dist/commonjs/core/objectLoader2Factory.d.ts +7 -2
  5. package/dist/commonjs/core/objectLoader2Factory.d.ts.map +1 -1
  6. package/dist/commonjs/core/objectLoader2Factory.js +14 -4
  7. package/dist/commonjs/core/objectLoader2Factory.js.map +1 -1
  8. package/dist/commonjs/core/objectLoader2Factory.test.d.ts +2 -0
  9. package/dist/commonjs/core/objectLoader2Factory.test.d.ts.map +1 -0
  10. package/dist/commonjs/core/objectLoader2Factory.test.js +106 -0
  11. package/dist/commonjs/core/objectLoader2Factory.test.js.map +1 -0
  12. package/dist/commonjs/core/options.d.ts +2 -0
  13. package/dist/commonjs/core/options.d.ts.map +1 -1
  14. package/dist/commonjs/core/stages/cacheReader.d.ts +2 -2
  15. package/dist/commonjs/core/stages/cacheReader.d.ts.map +1 -1
  16. package/dist/commonjs/core/stages/cacheReader.js.map +1 -1
  17. package/dist/commonjs/core/stages/cacheWriter.d.ts +2 -2
  18. package/dist/commonjs/core/stages/cacheWriter.d.ts.map +1 -1
  19. package/dist/commonjs/core/stages/cacheWriter.js +4 -4
  20. package/dist/commonjs/core/stages/cacheWriter.js.map +1 -1
  21. package/dist/commonjs/core/stages/serverDownloader.d.ts +2 -1
  22. package/dist/commonjs/core/stages/serverDownloader.d.ts.map +1 -1
  23. package/dist/commonjs/core/stages/serverDownloader.js +3 -2
  24. package/dist/commonjs/core/stages/serverDownloader.js.map +1 -1
  25. package/dist/commonjs/deferment/defermentManager.d.ts +18 -2
  26. package/dist/commonjs/deferment/defermentManager.d.ts.map +1 -1
  27. package/dist/commonjs/deferment/defermentManager.js +30 -3
  28. package/dist/commonjs/deferment/defermentManager.js.map +1 -1
  29. package/dist/commonjs/deferment/defermentManager.test.js +11 -11
  30. package/dist/commonjs/deferment/defermentManager.test.js.map +1 -1
  31. package/dist/commonjs/queues/batchingQueue.d.ts +0 -2
  32. package/dist/commonjs/queues/batchingQueue.d.ts.map +1 -1
  33. package/dist/commonjs/queues/batchingQueue.dispose.test.js +1 -3
  34. package/dist/commonjs/queues/batchingQueue.dispose.test.js.map +1 -1
  35. package/dist/commonjs/queues/batchingQueue.js +4 -3
  36. package/dist/commonjs/queues/batchingQueue.js.map +1 -1
  37. package/dist/commonjs/types/types.d.ts +5 -0
  38. package/dist/commonjs/types/types.d.ts.map +1 -1
  39. package/dist/esm/core/objectLoader2.d.ts.map +1 -1
  40. package/dist/esm/core/objectLoader2.js +1 -9
  41. package/dist/esm/core/objectLoader2.js.map +1 -1
  42. package/dist/esm/core/objectLoader2Factory.d.ts +7 -2
  43. package/dist/esm/core/objectLoader2Factory.d.ts.map +1 -1
  44. package/dist/esm/core/objectLoader2Factory.js +14 -4
  45. package/dist/esm/core/objectLoader2Factory.js.map +1 -1
  46. package/dist/esm/core/objectLoader2Factory.test.d.ts +2 -0
  47. package/dist/esm/core/objectLoader2Factory.test.d.ts.map +1 -0
  48. package/dist/esm/core/objectLoader2Factory.test.js +104 -0
  49. package/dist/esm/core/objectLoader2Factory.test.js.map +1 -0
  50. package/dist/esm/core/options.d.ts +2 -0
  51. package/dist/esm/core/options.d.ts.map +1 -1
  52. package/dist/esm/core/stages/cacheReader.d.ts +2 -2
  53. package/dist/esm/core/stages/cacheReader.d.ts.map +1 -1
  54. package/dist/esm/core/stages/cacheReader.js.map +1 -1
  55. package/dist/esm/core/stages/cacheWriter.d.ts +2 -2
  56. package/dist/esm/core/stages/cacheWriter.d.ts.map +1 -1
  57. package/dist/esm/core/stages/cacheWriter.js +4 -4
  58. package/dist/esm/core/stages/cacheWriter.js.map +1 -1
  59. package/dist/esm/core/stages/serverDownloader.d.ts +2 -1
  60. package/dist/esm/core/stages/serverDownloader.d.ts.map +1 -1
  61. package/dist/esm/core/stages/serverDownloader.js +3 -2
  62. package/dist/esm/core/stages/serverDownloader.js.map +1 -1
  63. package/dist/esm/deferment/defermentManager.d.ts +18 -2
  64. package/dist/esm/deferment/defermentManager.d.ts.map +1 -1
  65. package/dist/esm/deferment/defermentManager.js +28 -2
  66. package/dist/esm/deferment/defermentManager.js.map +1 -1
  67. package/dist/esm/deferment/defermentManager.test.js +11 -11
  68. package/dist/esm/deferment/defermentManager.test.js.map +1 -1
  69. package/dist/esm/queues/batchingQueue.d.ts +0 -2
  70. package/dist/esm/queues/batchingQueue.d.ts.map +1 -1
  71. package/dist/esm/queues/batchingQueue.dispose.test.js +1 -3
  72. package/dist/esm/queues/batchingQueue.dispose.test.js.map +1 -1
  73. package/dist/esm/queues/batchingQueue.js +4 -3
  74. package/dist/esm/queues/batchingQueue.js.map +1 -1
  75. package/dist/esm/types/types.d.ts +5 -0
  76. package/dist/esm/types/types.d.ts.map +1 -1
  77. package/package.json +2 -2
  78. package/src/core/objectLoader2.ts +3 -13
  79. package/src/core/objectLoader2Factory.test.ts +135 -0
  80. package/src/core/objectLoader2Factory.ts +29 -6
  81. package/src/core/options.ts +2 -0
  82. package/src/core/stages/cacheReader.ts +3 -3
  83. package/src/core/stages/cacheWriter.ts +5 -5
  84. package/src/core/stages/serverDownloader.ts +7 -4
  85. package/src/deferment/defermentManager.test.ts +11 -11
  86. package/src/deferment/defermentManager.ts +39 -3
  87. package/src/queues/batchingQueue.dispose.test.ts +1 -3
  88. package/src/queues/batchingQueue.ts +3 -5
  89. 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 { CustomLogger, getFeatureFlag, ObjectLoader2Flags } from '../types/functions.js'
2
- import { Base } from '../types/types.js'
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 (getFeatureFlag(ObjectLoader2Flags.DEBUG) === 'true') {
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
- if (getFeatureFlag(ObjectLoader2Flags.USE_CACHE) === 'true') {
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
- logger: log || ((): void => {})
91
+ fetch: params.options?.fetch,
92
+ attributeMask: params.attributeMask,
93
+ logger
71
94
  }),
72
95
  database,
73
- logger: log
96
+ logger
74
97
  })
75
98
  return loader
76
99
  }
@@ -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 { DefermentManager } from '../../deferment/defermentManager.js'
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: 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: DefermentManager,
20
+ defermentManager: Deferment,
21
21
  logger: CustomLogger,
22
22
  options: CacheOptions
23
23
  ) {
@@ -1,4 +1,4 @@
1
- import { DefermentManager } from '../../deferment/defermentManager.js'
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
- #defermentManager: DefermentManager
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
- defermentManager: DefermentManager,
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.#defermentManager = defermentManager
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.#defermentManager.undefer(item, this.#requestItem)
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/getobjects/${
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({ objects: JSON.stringify(batch) })
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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(mockCache, mockLogger)
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 class DefermentManager {
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(cache: MemoryCache, logger: CustomLogger) {
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).toHaveBeenCalledWith(items)
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
- this.#logger('Batch processing failed:', error)
113
+ console.error('Batch processing failed:', error)
114
+ this.#disposed = true
117
115
  } finally {
118
116
  this.#isProcessing = false
119
117
  }
@@ -19,3 +19,8 @@ export interface Reference {
19
19
  export interface DataChunk extends Base {
20
20
  data?: Base[]
21
21
  }
22
+
23
+ export type ObjectAttributeMask =
24
+ | { include: string[] }
25
+ | { exclude: string[] }
26
+ | undefined