@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.
- package/dist/commonjs/index.js +6 -7
- package/dist/esm/index.js +3 -3
- package/eslint.config.mjs +3 -1
- package/package.json +2 -2
- package/src/helpers/__snapshots__/cachePump.spec.ts.snap +31 -0
- package/src/helpers/__snapshots__/cacheReader.spec.ts.snap +8 -0
- package/src/helpers/aggregateQueue.ts +20 -0
- package/src/helpers/batchedPool.ts +5 -9
- package/src/helpers/batchingQueue.ts +21 -13
- package/src/helpers/cachePump.disposal.spec.ts +49 -0
- package/src/helpers/cachePump.spec.ts +103 -0
- package/src/helpers/cachePump.ts +99 -0
- package/src/helpers/cacheReader.spec.ts +35 -0
- package/src/helpers/cacheReader.ts +64 -0
- package/src/helpers/defermentManager.disposal.spec.ts +28 -0
- package/src/helpers/defermentManager.spec.ts +25 -1
- package/src/helpers/defermentManager.ts +128 -12
- package/src/helpers/deferredBase.ts +44 -6
- package/src/helpers/keyedQueue.ts +45 -0
- package/src/helpers/memoryPump.ts +40 -0
- package/src/helpers/pump.ts +8 -0
- package/src/index.ts +3 -4
- package/src/operations/__snapshots__/objectLoader2.spec.ts.snap +16 -16
- package/src/operations/{__snapshots__ → databases/__snapshots__}/indexedDatabase.spec.ts.snap +0 -21
- package/src/operations/{indexedDatabase.spec.ts → databases/indexedDatabase.spec.ts} +2 -28
- package/src/operations/databases/indexedDatabase.ts +150 -0
- package/src/operations/databases/memoryDatabase.ts +43 -0
- package/src/operations/{__snapshots__ → downloaders/__snapshots__}/serverDownloader.spec.ts.snap +34 -0
- package/src/operations/{memoryDownloader.ts → downloaders/memoryDownloader.ts} +15 -14
- package/src/operations/{serverDownloader.spec.ts → downloaders/serverDownloader.spec.ts} +68 -43
- package/src/operations/{serverDownloader.ts → downloaders/serverDownloader.ts} +92 -38
- package/src/operations/interfaces.ts +11 -12
- package/src/operations/objectLoader2.spec.ts +76 -144
- package/src/operations/objectLoader2.ts +57 -79
- package/src/operations/objectLoader2Factory.ts +56 -0
- package/src/operations/options.ts +18 -37
- package/src/operations/traverser.spec.ts +1 -1
- package/src/operations/traverser.ts +1 -1
- package/src/test/e2e.spec.ts +4 -4
- package/src/types/types.ts +11 -0
- package/src/operations/indexedDatabase.ts +0 -167
- 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
|
-
}
|