@speckle/objectloader2 2.24.0 → 2.25.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 (42) hide show
  1. package/dist/commonjs/index.js +6 -7
  2. package/dist/esm/index.js +3 -3
  3. package/eslint.config.mjs +3 -1
  4. package/package.json +2 -2
  5. package/src/helpers/__snapshots__/cachePump.spec.ts.snap +31 -0
  6. package/src/helpers/__snapshots__/cacheReader.spec.ts.snap +8 -0
  7. package/src/helpers/aggregateQueue.ts +20 -0
  8. package/src/helpers/batchedPool.ts +5 -9
  9. package/src/helpers/batchingQueue.ts +21 -13
  10. package/src/helpers/cachePump.disposal.spec.ts +49 -0
  11. package/src/helpers/cachePump.spec.ts +103 -0
  12. package/src/helpers/cachePump.ts +99 -0
  13. package/src/helpers/cacheReader.spec.ts +35 -0
  14. package/src/helpers/cacheReader.ts +64 -0
  15. package/src/helpers/defermentManager.disposal.spec.ts +28 -0
  16. package/src/helpers/defermentManager.spec.ts +25 -1
  17. package/src/helpers/defermentManager.ts +128 -12
  18. package/src/helpers/deferredBase.ts +44 -6
  19. package/src/helpers/keyedQueue.ts +45 -0
  20. package/src/helpers/memoryPump.ts +40 -0
  21. package/src/helpers/pump.ts +8 -0
  22. package/src/index.ts +3 -4
  23. package/src/operations/__snapshots__/objectLoader2.spec.ts.snap +16 -16
  24. package/src/operations/{__snapshots__ → databases/__snapshots__}/indexedDatabase.spec.ts.snap +0 -21
  25. package/src/operations/{indexedDatabase.spec.ts → databases/indexedDatabase.spec.ts} +2 -28
  26. package/src/operations/databases/indexedDatabase.ts +150 -0
  27. package/src/operations/databases/memoryDatabase.ts +43 -0
  28. package/src/operations/{__snapshots__ → downloaders/__snapshots__}/serverDownloader.spec.ts.snap +34 -0
  29. package/src/operations/{memoryDownloader.ts → downloaders/memoryDownloader.ts} +15 -14
  30. package/src/operations/{serverDownloader.spec.ts → downloaders/serverDownloader.spec.ts} +68 -43
  31. package/src/operations/{serverDownloader.ts → downloaders/serverDownloader.ts} +92 -38
  32. package/src/operations/interfaces.ts +11 -12
  33. package/src/operations/objectLoader2.spec.ts +76 -144
  34. package/src/operations/objectLoader2.ts +57 -79
  35. package/src/operations/objectLoader2Factory.ts +56 -0
  36. package/src/operations/options.ts +18 -37
  37. package/src/operations/traverser.spec.ts +1 -1
  38. package/src/operations/traverser.ts +1 -1
  39. package/src/test/e2e.spec.ts +4 -4
  40. package/src/types/types.ts +11 -0
  41. package/src/operations/indexedDatabase.ts +0 -167
  42. package/src/operations/memoryDatabase.ts +0 -42
@@ -1,167 +0,0 @@
1
- import BatchingQueue from '../helpers/batchingQueue.js'
2
- import Queue from '../helpers/queue.js'
3
- import { CustomLogger, Item } from '../types/types.js'
4
- import { isSafari, TIME } from '@speckle/shared'
5
- import { BaseDatabaseOptions } from './options.js'
6
- import { Cache } from './interfaces.js'
7
- import { Dexie, DexieOptions, Table } from 'dexie'
8
-
9
- class ObjectStore extends Dexie {
10
- static #databaseName: string = 'speckle-cache'
11
- objects!: Table<Item, string> // Table type: <entity, primaryKey>
12
-
13
- constructor(options: DexieOptions) {
14
- super(ObjectStore.#databaseName, options)
15
-
16
- this.version(1).stores({
17
- objects: 'baseId, item' // baseId is primary key
18
- })
19
- }
20
- }
21
-
22
- export default class IndexedDatabase implements Cache {
23
- #options: BaseDatabaseOptions
24
- #logger: CustomLogger
25
-
26
- #cacheDB?: ObjectStore
27
-
28
- #writeQueue: BatchingQueue<Item> | undefined
29
-
30
- // #count: number = 0
31
-
32
- constructor(options: BaseDatabaseOptions) {
33
- this.#options = {
34
- ...{
35
- maxCacheReadSize: 10000,
36
- maxCacheBatchWriteWait: 1000
37
- },
38
- ...options
39
- }
40
- this.#logger = options.logger || (() => {})
41
- }
42
-
43
- async add(item: Item): Promise<void> {
44
- if (!this.#writeQueue) {
45
- await this.#setupCacheDb()
46
- this.#writeQueue = new BatchingQueue<Item>({
47
- batchSize: this.#options.maxCacheWriteSize ?? 10000,
48
- maxWaitTime: this.#options.maxCacheBatchWriteWait,
49
- processFunction: (batch: Item[]) =>
50
- this.#cacheSaveBatch({ batch, cacheDB: this.#cacheDB! })
51
- })
52
- }
53
- this.#writeQueue.add(item)
54
- }
55
-
56
- async disposeAsync(): Promise<void> {
57
- await this.#writeQueue?.disposeAsync()
58
- }
59
-
60
- async #openDatabase(): Promise<ObjectStore> {
61
- const db = new ObjectStore({
62
- indexedDB: this.#options.indexedDB ?? globalThis.indexedDB,
63
- IDBKeyRange: this.#options.keyRange ?? IDBKeyRange,
64
- chromeTransactionDurability: 'relaxed'
65
- })
66
- await db.open()
67
- return db
68
- }
69
-
70
- async #setupCacheDb(): Promise<void> {
71
- if (this.#cacheDB !== undefined) {
72
- return
73
- }
74
-
75
- // Initialize
76
- await this.#safariFix()
77
- this.#cacheDB = await this.#openDatabase()
78
- }
79
-
80
- async processItems(params: {
81
- ids: string[]
82
- foundItems: Queue<Item>
83
- notFoundItems: Queue<string>
84
- }): Promise<void> {
85
- const { ids, foundItems, notFoundItems } = params
86
- await this.#setupCacheDb()
87
- const maxCacheReadSize = this.#options.maxCacheReadSize ?? 10000
88
-
89
- for (let i = 0; i < ids.length; ) {
90
- if ((this.#writeQueue?.count() ?? 0) > maxCacheReadSize * 2) {
91
- this.#logger(
92
- 'pausing reads (# in write queue: ' + this.#writeQueue?.count() + ')'
93
- )
94
- await new Promise((resolve) => setTimeout(resolve, TIME.second)) // Pause for 1 second, protects against out of memory
95
- continue
96
- }
97
- const batch = ids.slice(i, i + maxCacheReadSize)
98
- // const x = this.#count
99
- // this.#count++
100
- // const startTime = performance.now()
101
- // this.#logger('Start read ' + x + ' ' + batch.length)
102
-
103
- //faster than BulkGet with dexie
104
- await this.#cacheDB!.transaction('r', this.#cacheDB!.objects, async () => {
105
- const gets = batch.map((key) => this.#cacheDB!.objects.get(key))
106
- const cachedData = await Promise.all(gets)
107
- for (let i = 0; i < cachedData.length; i++) {
108
- if (cachedData[i]) {
109
- foundItems.add(cachedData[i]!)
110
- } else {
111
- notFoundItems.add(batch[i])
112
- }
113
- }
114
- })
115
- // const endTime = performance.now()
116
- // const duration = endTime - startTime
117
- // this.#logger('Read batch ' + x + ' ' + batch.length + ' ' + duration / TIME_MS.second)
118
-
119
- // interate down here to help with pausing
120
- i += maxCacheReadSize
121
- }
122
- }
123
-
124
- async getItem(params: { id: string }): Promise<Item | undefined> {
125
- const { id } = params
126
- await this.#setupCacheDb()
127
-
128
- return this.#cacheDB!.transaction('r', this.#cacheDB!.objects, async () => {
129
- return await this.#cacheDB?.objects.get(id)
130
- })
131
- }
132
-
133
- async #cacheSaveBatch(params: {
134
- batch: Item[]
135
- cacheDB: ObjectStore
136
- }): Promise<void> {
137
- const { batch, cacheDB } = params
138
- //const x = this.#count
139
- //this.#count++
140
-
141
- // const startTime = performance.now()
142
- // this.#logger('Start save ' + x + ' ' + batch.length)
143
- await cacheDB.objects.bulkPut(batch)
144
- // const endTime = performance.now()
145
- // const duration = endTime - startTime
146
- //this.#logger('Saved batch ' + x + ' ' + batch.length + ' ' + duration / TIME_MS.second)
147
- }
148
-
149
- /**
150
- * Fixes a Safari bug where IndexedDB requests get lost and never resolve - invoke before you use IndexedDB
151
- * @link Credits and more info: https://github.com/jakearchibald/safari-14-idb-fix
152
- */
153
- async #safariFix(): Promise<void> {
154
- // No point putting other browsers or older versions of Safari through this mess.
155
- if (!isSafari() || !this.#options.indexedDB?.databases) return Promise.resolve()
156
-
157
- let intervalId: ReturnType<typeof setInterval>
158
-
159
- return new Promise<void>((resolve: () => void) => {
160
- const tryIdb = () => this.#options.indexedDB?.databases().finally(resolve)
161
- intervalId = setInterval(() => {
162
- void tryIdb()
163
- }, 100)
164
- void tryIdb()
165
- }).finally(() => clearInterval(intervalId))
166
- }
167
- }
@@ -1,42 +0,0 @@
1
- import Queue from '../helpers/queue.js'
2
- import { Base, Item } from '../types/types.js'
3
- import { Cache } from './interfaces.js'
4
- import { MemoryDatabaseOptions } from './options.js'
5
-
6
- export class MemoryDatabase implements Cache {
7
- #items: Record<string, Base>
8
- constructor(options?: MemoryDatabaseOptions) {
9
- this.#items = options?.items || {}
10
- }
11
-
12
- getItem(params: { id: string }): Promise<Item | undefined> {
13
- const item = this.#items[params.id]
14
- if (item) {
15
- return Promise.resolve({ baseId: params.id, base: item })
16
- }
17
- return Promise.resolve(undefined)
18
- }
19
- processItems(params: {
20
- ids: string[]
21
- foundItems: Queue<Item>
22
- notFoundItems: Queue<string>
23
- }): Promise<void> {
24
- const { ids, foundItems, notFoundItems } = params
25
- for (const id of ids) {
26
- const item = this.#items[id]
27
- if (item) {
28
- foundItems.add({ baseId: id, base: item })
29
- } else {
30
- notFoundItems.add(id)
31
- }
32
- }
33
- return Promise.resolve()
34
- }
35
- add(item: Item): Promise<void> {
36
- this.#items[item.baseId] = item.base
37
- return Promise.resolve()
38
- }
39
- disposeAsync(): Promise<void> {
40
- return Promise.resolve()
41
- }
42
- }