@speckle/objectloader2 2.25.9 → 2.26.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/core/interfaces.d.ts +4 -6
- package/dist/commonjs/core/interfaces.d.ts.map +1 -1
- package/dist/commonjs/core/objectLoader2.js +2 -2
- package/dist/commonjs/core/objectLoader2.js.map +1 -1
- package/dist/commonjs/core/objectLoader2Factory.d.ts.map +1 -1
- package/dist/commonjs/core/objectLoader2Factory.js +4 -3
- package/dist/commonjs/core/objectLoader2Factory.js.map +1 -1
- package/dist/commonjs/core/stages/cacheWriter.js +1 -1
- package/dist/commonjs/core/stages/cacheWriter.js.map +1 -1
- package/dist/commonjs/core/stages/indexedDatabase.d.ts +20 -6
- package/dist/commonjs/core/stages/indexedDatabase.d.ts.map +1 -1
- package/dist/commonjs/core/stages/indexedDatabase.js +138 -19
- package/dist/commonjs/core/stages/indexedDatabase.js.map +1 -1
- package/dist/commonjs/core/stages/memory/memoryDatabase.d.ts +2 -4
- package/dist/commonjs/core/stages/memory/memoryDatabase.d.ts.map +1 -1
- package/dist/commonjs/core/stages/memory/memoryDatabase.js +3 -3
- package/dist/commonjs/core/stages/memory/memoryDatabase.js.map +1 -1
- package/dist/commonjs/core/stages/memory/memoryDownloader.d.ts +1 -1
- package/dist/commonjs/core/stages/memory/memoryDownloader.d.ts.map +1 -1
- package/dist/commonjs/core/stages/memory/memoryDownloader.js +1 -1
- package/dist/commonjs/core/stages/memory/memoryDownloader.js.map +1 -1
- package/dist/commonjs/core/stages/serverDownloader.d.ts +3 -2
- package/dist/commonjs/core/stages/serverDownloader.d.ts.map +1 -1
- package/dist/commonjs/core/stages/serverDownloader.js +12 -19
- package/dist/commonjs/core/stages/serverDownloader.js.map +1 -1
- package/dist/commonjs/queues/keyedQueue.test.d.ts +2 -0
- package/dist/commonjs/queues/keyedQueue.test.d.ts.map +1 -0
- package/dist/commonjs/queues/keyedQueue.test.js +118 -0
- package/dist/commonjs/queues/keyedQueue.test.js.map +1 -0
- package/dist/esm/core/interfaces.d.ts +4 -6
- package/dist/esm/core/interfaces.d.ts.map +1 -1
- package/dist/esm/core/objectLoader2.js +2 -2
- package/dist/esm/core/objectLoader2.js.map +1 -1
- package/dist/esm/core/objectLoader2Factory.d.ts.map +1 -1
- package/dist/esm/core/objectLoader2Factory.js +3 -2
- package/dist/esm/core/objectLoader2Factory.js.map +1 -1
- package/dist/esm/core/stages/cacheWriter.js +1 -1
- package/dist/esm/core/stages/cacheWriter.js.map +1 -1
- package/dist/esm/core/stages/indexedDatabase.d.ts +20 -6
- package/dist/esm/core/stages/indexedDatabase.d.ts.map +1 -1
- package/dist/esm/core/stages/indexedDatabase.js +140 -22
- package/dist/esm/core/stages/indexedDatabase.js.map +1 -1
- package/dist/esm/core/stages/memory/memoryDatabase.d.ts +2 -4
- package/dist/esm/core/stages/memory/memoryDatabase.d.ts.map +1 -1
- package/dist/esm/core/stages/memory/memoryDatabase.js +3 -3
- package/dist/esm/core/stages/memory/memoryDatabase.js.map +1 -1
- package/dist/esm/core/stages/memory/memoryDownloader.d.ts +1 -1
- package/dist/esm/core/stages/memory/memoryDownloader.d.ts.map +1 -1
- package/dist/esm/core/stages/memory/memoryDownloader.js +1 -1
- package/dist/esm/core/stages/memory/memoryDownloader.js.map +1 -1
- package/dist/esm/core/stages/serverDownloader.d.ts +3 -2
- package/dist/esm/core/stages/serverDownloader.d.ts.map +1 -1
- package/dist/esm/core/stages/serverDownloader.js +12 -19
- package/dist/esm/core/stages/serverDownloader.js.map +1 -1
- package/dist/esm/queues/keyedQueue.test.d.ts +2 -0
- package/dist/esm/queues/keyedQueue.test.d.ts.map +1 -0
- package/dist/esm/queues/keyedQueue.test.js +113 -0
- package/dist/esm/queues/keyedQueue.test.js.map +1 -0
- package/package.json +2 -2
- package/src/core/interfaces.ts +4 -4
- package/src/core/objectLoader2.spec.ts +1 -1
- package/src/core/objectLoader2.ts +2 -2
- package/src/core/objectLoader2Factory.ts +3 -2
- package/src/core/stages/cacheWriter.spec.ts +4 -4
- package/src/core/stages/cacheWriter.ts +1 -1
- package/src/core/stages/indexedDatabase.spec.ts +6 -6
- package/src/core/stages/indexedDatabase.ts +154 -25
- package/src/core/stages/memory/memoryDatabase.spec.ts +6 -6
- package/src/core/stages/memory/memoryDatabase.ts +3 -3
- package/src/core/stages/memory/memoryDownloader.spec.ts +2 -2
- package/src/core/stages/memory/memoryDownloader.ts +1 -1
- package/src/core/stages/serverDownloader.spec.ts +20 -12
- package/src/core/stages/serverDownloader.ts +18 -24
- package/src/queues/keyedQueue.test.ts +146 -0
- package/dist/commonjs/core/stages/ItemStore.d.ts +0 -37
- package/dist/commonjs/core/stages/ItemStore.d.ts.map +0 -1
- package/dist/commonjs/core/stages/ItemStore.js +0 -167
- package/dist/commonjs/core/stages/ItemStore.js.map +0 -1
- package/dist/commonjs/queues/batchedPool.d.ts +0 -13
- package/dist/commonjs/queues/batchedPool.d.ts.map +0 -1
- package/dist/commonjs/queues/batchedPool.js +0 -45
- package/dist/commonjs/queues/batchedPool.js.map +0 -1
- package/dist/esm/core/stages/ItemStore.d.ts +0 -37
- package/dist/esm/core/stages/ItemStore.d.ts.map +0 -1
- package/dist/esm/core/stages/ItemStore.js +0 -163
- package/dist/esm/core/stages/ItemStore.js.map +0 -1
- package/dist/esm/queues/batchedPool.d.ts +0 -13
- package/dist/esm/queues/batchedPool.d.ts.map +0 -1
- package/dist/esm/queues/batchedPool.js +0 -42
- package/dist/esm/queues/batchedPool.js.map +0 -1
- package/src/core/stages/ItemStore.ts +0 -196
- package/src/queues/batchedPool.ts +0 -58
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import BatchingQueue from '../../queues/batchingQueue.js';
|
|
2
2
|
import { ObjectLoaderRuntimeError } from '../../types/errors.js';
|
|
3
3
|
import { indexOf, isBase, take } from '../../types/functions.js';
|
|
4
4
|
const MAX_SAFARI_DECODE_BYTES = 2 * 1024 * 1024 * 1024 - 1024 * 1024; // 2GB minus a margin
|
|
@@ -10,6 +10,7 @@ export default class ServerDownloader {
|
|
|
10
10
|
#fetch;
|
|
11
11
|
#results;
|
|
12
12
|
#total;
|
|
13
|
+
#logger;
|
|
13
14
|
#downloadQueue;
|
|
14
15
|
#decoder = new TextDecoder('utf-8', { fatal: true });
|
|
15
16
|
#decodedBytesCount = 0;
|
|
@@ -17,6 +18,7 @@ export default class ServerDownloader {
|
|
|
17
18
|
#rawEncoding;
|
|
18
19
|
constructor(options) {
|
|
19
20
|
this.#options = options;
|
|
21
|
+
this.#logger = options.logger;
|
|
20
22
|
this.#fetch =
|
|
21
23
|
options.fetch ?? ((...args) => globalThis.fetch(...args));
|
|
22
24
|
this.#headers = {};
|
|
@@ -34,19 +36,13 @@ export default class ServerDownloader {
|
|
|
34
36
|
const encoder = new TextEncoder();
|
|
35
37
|
this.#rawEncoding = encoder.encode(this.#rawString);
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return [total];
|
|
40
|
-
}
|
|
41
|
-
return [10000, 25000, 10000, 1000];
|
|
42
|
-
}
|
|
43
|
-
initializePool(params) {
|
|
44
|
-
const { results, total } = params;
|
|
39
|
+
initialize(params) {
|
|
40
|
+
const { results, total, maxDownloadBatchWait } = params;
|
|
45
41
|
this.#results = results;
|
|
46
42
|
this.#total = total;
|
|
47
|
-
this.#downloadQueue = new
|
|
48
|
-
|
|
49
|
-
maxWaitTime:
|
|
43
|
+
this.#downloadQueue = new BatchingQueue({
|
|
44
|
+
batchSize: 15000, // 15k is a good number for most cases
|
|
45
|
+
maxWaitTime: maxDownloadBatchWait ?? 1000, // 1 second
|
|
50
46
|
processFunction: (batch) => this.downloadBatch({
|
|
51
47
|
batch,
|
|
52
48
|
url: this.#requestUrlChildren,
|
|
@@ -54,14 +50,8 @@ export default class ServerDownloader {
|
|
|
54
50
|
})
|
|
55
51
|
});
|
|
56
52
|
}
|
|
57
|
-
#getPool() {
|
|
58
|
-
if (this.#downloadQueue) {
|
|
59
|
-
return this.#downloadQueue;
|
|
60
|
-
}
|
|
61
|
-
throw new Error('Download pool is not initialized');
|
|
62
|
-
}
|
|
63
53
|
add(id) {
|
|
64
|
-
this.#
|
|
54
|
+
this.#downloadQueue?.add(id, id);
|
|
65
55
|
}
|
|
66
56
|
/*
|
|
67
57
|
This is the most frequently reported and confirmed reason for this error in Safari. There's a known bug in WebKit (Safari's rendering engine) where TextDecoder can fail or throw a RangeError after decoding around 2GB of data. Chrome and other browsers handle much larger amounts of data without this specific limitation.
|
|
@@ -85,6 +75,8 @@ export default class ServerDownloader {
|
|
|
85
75
|
}
|
|
86
76
|
async downloadBatch(params) {
|
|
87
77
|
const { batch, url, headers } = params;
|
|
78
|
+
const start = performance.now();
|
|
79
|
+
this.#logger(`Downloading batch of ${batch.length} items...`);
|
|
88
80
|
const keys = new Set(batch);
|
|
89
81
|
const response = await this.#fetch(url, {
|
|
90
82
|
method: 'POST',
|
|
@@ -116,6 +108,7 @@ export default class ServerDownloader {
|
|
|
116
108
|
if (count >= this.#total) {
|
|
117
109
|
await this.#results?.disposeAsync(); // mark the queue as done
|
|
118
110
|
}
|
|
111
|
+
this.#logger(`Downloaded batch of ${batch.length} items in ${performance.now() - start}ms`);
|
|
119
112
|
}
|
|
120
113
|
async #processArray(leftover, value, keys, callback) {
|
|
121
114
|
//this concat will allocate a new array
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serverDownloader.js","sourceRoot":"","sources":["../../../../src/core/stages/serverDownloader.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"serverDownloader.js","sourceRoot":"","sources":["../../../../src/core/stages/serverDownloader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyedQueue.test.d.ts","sourceRoot":"","sources":["../../../src/queues/keyedQueue.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import KeyedQueue from './keyedQueue.js';
|
|
3
|
+
describe('KeyedQueue', () => {
|
|
4
|
+
let queue;
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
queue = new KeyedQueue();
|
|
7
|
+
});
|
|
8
|
+
describe('enqueue', () => {
|
|
9
|
+
it('should add a key-value pair to the queue', () => {
|
|
10
|
+
const result = queue.enqueue('key1', 1);
|
|
11
|
+
expect(result).toBe(true);
|
|
12
|
+
expect(queue.size).toBe(1);
|
|
13
|
+
expect(queue.get('key1')).toBe(1);
|
|
14
|
+
});
|
|
15
|
+
it('should return false when trying to add a key that already exists', () => {
|
|
16
|
+
queue.enqueue('key1', 1);
|
|
17
|
+
const result = queue.enqueue('key1', 2);
|
|
18
|
+
expect(result).toBe(false);
|
|
19
|
+
expect(queue.size).toBe(1);
|
|
20
|
+
expect(queue.get('key1')).toBe(1); // Value should not be updated
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('enqueueAll', () => {
|
|
24
|
+
it('should add multiple key-value pairs to the queue', () => {
|
|
25
|
+
const keys = ['key1', 'key2', 'key3'];
|
|
26
|
+
const values = [1, 2, 3];
|
|
27
|
+
const count = queue.enqueueAll(keys, values);
|
|
28
|
+
expect(count).toBe(3);
|
|
29
|
+
expect(queue.size).toBe(3);
|
|
30
|
+
expect(queue.get('key1')).toBe(1);
|
|
31
|
+
expect(queue.get('key2')).toBe(2);
|
|
32
|
+
expect(queue.get('key3')).toBe(3);
|
|
33
|
+
});
|
|
34
|
+
it('should skip keys that already exist and return the count of added items', () => {
|
|
35
|
+
queue.enqueue('key1', 1);
|
|
36
|
+
const keys = ['key1', 'key2', 'key3'];
|
|
37
|
+
const values = [10, 2, 3];
|
|
38
|
+
const count = queue.enqueueAll(keys, values);
|
|
39
|
+
expect(count).toBe(2);
|
|
40
|
+
expect(queue.size).toBe(3);
|
|
41
|
+
expect(queue.get('key1')).toBe(1); // Original value preserved
|
|
42
|
+
expect(queue.get('key2')).toBe(2);
|
|
43
|
+
expect(queue.get('key3')).toBe(3);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('get', () => {
|
|
47
|
+
it('should return the value for a given key', () => {
|
|
48
|
+
queue.enqueue('key1', 1);
|
|
49
|
+
expect(queue.get('key1')).toBe(1);
|
|
50
|
+
});
|
|
51
|
+
it('should return undefined for a non-existent key', () => {
|
|
52
|
+
expect(queue.get('nonexistent')).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('has', () => {
|
|
56
|
+
it('should return true if the key exists', () => {
|
|
57
|
+
queue.enqueue('key1', 1);
|
|
58
|
+
expect(queue.has('key1')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('should return false if the key does not exist', () => {
|
|
61
|
+
expect(queue.has('nonexistent')).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('size', () => {
|
|
65
|
+
it('should return the number of items in the queue', () => {
|
|
66
|
+
expect(queue.size).toBe(0);
|
|
67
|
+
queue.enqueue('key1', 1);
|
|
68
|
+
expect(queue.size).toBe(1);
|
|
69
|
+
queue.enqueue('key2', 2);
|
|
70
|
+
expect(queue.size).toBe(2);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('spliceValues', () => {
|
|
74
|
+
it('should remove and return values from the queue', () => {
|
|
75
|
+
queue.enqueue('key1', 1);
|
|
76
|
+
queue.enqueue('key2', 2);
|
|
77
|
+
queue.enqueue('key3', 3);
|
|
78
|
+
queue.enqueue('key4', 4);
|
|
79
|
+
const result = queue.spliceValues(1, 2);
|
|
80
|
+
expect(result).toEqual([2, 3]);
|
|
81
|
+
expect(queue.size).toBe(2);
|
|
82
|
+
expect(queue.has('key1')).toBe(true);
|
|
83
|
+
expect(queue.has('key2')).toBe(false);
|
|
84
|
+
expect(queue.has('key3')).toBe(false);
|
|
85
|
+
expect(queue.has('key4')).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
it('should handle splicing at the beginning of the queue', () => {
|
|
88
|
+
queue.enqueue('key1', 1);
|
|
89
|
+
queue.enqueue('key2', 2);
|
|
90
|
+
const result = queue.spliceValues(0, 1);
|
|
91
|
+
expect(result).toEqual([1]);
|
|
92
|
+
expect(queue.size).toBe(1);
|
|
93
|
+
expect(queue.has('key1')).toBe(false);
|
|
94
|
+
expect(queue.has('key2')).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
it('should handle splicing at the end of the queue', () => {
|
|
97
|
+
queue.enqueue('key1', 1);
|
|
98
|
+
queue.enqueue('key2', 2);
|
|
99
|
+
const result = queue.spliceValues(1, 1);
|
|
100
|
+
expect(result).toEqual([2]);
|
|
101
|
+
expect(queue.size).toBe(1);
|
|
102
|
+
expect(queue.has('key1')).toBe(true);
|
|
103
|
+
expect(queue.has('key2')).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
it('should return an empty array when deleting zero elements', () => {
|
|
106
|
+
queue.enqueue('key1', 1);
|
|
107
|
+
const result = queue.spliceValues(0, 0);
|
|
108
|
+
expect(result).toEqual([]);
|
|
109
|
+
expect(queue.size).toBe(1);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=keyedQueue.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyedQueue.test.js","sourceRoot":"","sources":["../../../src/queues/keyedQueue.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speckle/objectloader2",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "This is an updated objectloader for the Speckle viewer written in typescript",
|
|
5
5
|
"main": "./dist/commonjs/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"author": "AEC Systems",
|
|
34
34
|
"license": "Apache-2.0",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@speckle/shared": "^2.
|
|
36
|
+
"@speckle/shared": "^2.26.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/lodash": "^4.17.5",
|
package/src/core/interfaces.ts
CHANGED
|
@@ -2,7 +2,7 @@ import Queue from '../queues/queue.js'
|
|
|
2
2
|
import { Item } from '../types/types.js'
|
|
3
3
|
|
|
4
4
|
export interface Downloader extends Queue<string> {
|
|
5
|
-
|
|
5
|
+
initialize(params: {
|
|
6
6
|
results: Queue<Item>
|
|
7
7
|
total: number
|
|
8
8
|
maxDownloadBatchWait?: number
|
|
@@ -12,7 +12,7 @@ export interface Downloader extends Queue<string> {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export interface Database {
|
|
15
|
-
getAll(
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
getAll(ids: string[]): Promise<(Item | undefined)[]>
|
|
16
|
+
putAll(batch: Item[]): Promise<void>
|
|
17
|
+
dispose(): void
|
|
18
18
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, test, expect } from 'vitest'
|
|
2
2
|
import { Base, Item } from '../types/types.js'
|
|
3
3
|
import { ObjectLoader2 } from './objectLoader2.js'
|
|
4
|
-
import IndexedDatabase from './stages/indexedDatabase.js'
|
|
4
|
+
import { IndexedDatabase } from './stages/indexedDatabase.js'
|
|
5
5
|
import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
|
|
6
6
|
import { MemoryDatabase } from './stages/memory/memoryDatabase.js'
|
|
7
7
|
import { MemoryDownloader } from './stages/memory/memoryDownloader.js'
|
|
@@ -131,7 +131,7 @@ export class ObjectLoader2 {
|
|
|
131
131
|
)
|
|
132
132
|
const children = sortedClosures.map((x) => x[0])
|
|
133
133
|
const total = children.length + 1 // +1 for the root object
|
|
134
|
-
this.#downloader.
|
|
134
|
+
this.#downloader.initialize({
|
|
135
135
|
results: new AggregateQueue(this.#gathered, this.#cacheWriter),
|
|
136
136
|
total
|
|
137
137
|
})
|
|
@@ -147,7 +147,7 @@ export class ObjectLoader2 {
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
if (!this.#isRootStored) {
|
|
150
|
-
await this.#database.
|
|
150
|
+
await this.#database.putAll([rootItem])
|
|
151
151
|
this.#isRootStored = true
|
|
152
152
|
}
|
|
153
153
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CustomLogger, getFeatureFlag, ObjectLoader2Flags } from '../types/functions.js'
|
|
2
2
|
import { Base } from '../types/types.js'
|
|
3
3
|
import { ObjectLoader2 } from './objectLoader2.js'
|
|
4
|
-
import IndexedDatabase from './stages/indexedDatabase.js'
|
|
4
|
+
import { IndexedDatabase } from './stages/indexedDatabase.js'
|
|
5
5
|
import { MemoryDatabase } from './stages/memory/memoryDatabase.js'
|
|
6
6
|
import { MemoryDownloader } from './stages/memory/memoryDownloader.js'
|
|
7
7
|
import ServerDownloader from './stages/serverDownloader.js'
|
|
@@ -66,7 +66,8 @@ export class ObjectLoader2Factory {
|
|
|
66
66
|
streamId: params.streamId,
|
|
67
67
|
objectId: params.objectId,
|
|
68
68
|
token: params.token,
|
|
69
|
-
headers: params.headers
|
|
69
|
+
headers: params.headers,
|
|
70
|
+
logger: log || ((): void => {})
|
|
70
71
|
}),
|
|
71
72
|
database,
|
|
72
73
|
logger: log
|
|
@@ -15,13 +15,13 @@ class MockDatabase implements Database {
|
|
|
15
15
|
return Promise.resolve([])
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
putAll(batch: Item[]): Promise<void> {
|
|
19
19
|
this.savedItems.push(...batch)
|
|
20
20
|
return Promise.resolve()
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
dispose(): void {
|
|
24
|
+
this.savedItems = []
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -150,7 +150,7 @@ describe('CacheWriter', () => {
|
|
|
150
150
|
})
|
|
151
151
|
|
|
152
152
|
it('should process items in batches according to maxCacheWriteSize', async () => {
|
|
153
|
-
const spy = vi.spyOn(database, '
|
|
153
|
+
const spy = vi.spyOn(database, 'putAll')
|
|
154
154
|
const smallBatchOptions: CacheOptions = {
|
|
155
155
|
...options,
|
|
156
156
|
maxCacheWriteSize: 2, // Set small batch size
|
|
@@ -45,7 +45,7 @@ export class CacheWriter implements Queue<Item> {
|
|
|
45
45
|
|
|
46
46
|
async writeAll(items: Item[]): Promise<void> {
|
|
47
47
|
const start = performance.now()
|
|
48
|
-
await this.#database.
|
|
48
|
+
await this.#database.putAll(items)
|
|
49
49
|
this.#logger(
|
|
50
50
|
`writeBatch: wrote ${items.length}, time ${
|
|
51
51
|
performance.now() - start
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
|
2
2
|
import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
|
|
3
3
|
|
|
4
4
|
import { Base, Item } from '../../types/types.js'
|
|
5
|
-
import IndexedDatabase,
|
|
5
|
+
import { IndexedDatabase, IndexedDatabaseOptions } from './indexedDatabase.js'
|
|
6
6
|
|
|
7
7
|
// Mock Item
|
|
8
8
|
const defaultItem = (id: string): Item => ({
|
|
@@ -19,19 +19,19 @@ describe('IndexedDatabase', () => {
|
|
|
19
19
|
db = new IndexedDatabase(options)
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
afterEach(
|
|
23
|
-
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
db.dispose()
|
|
24
24
|
})
|
|
25
25
|
|
|
26
26
|
it('should add and get multiple items', async () => {
|
|
27
27
|
const items = [defaultItem('id1'), defaultItem('id2')]
|
|
28
|
-
await db.
|
|
28
|
+
await db.putAll(items)
|
|
29
29
|
const result = await db.getAll(['id1', 'id2'])
|
|
30
30
|
expect(result).toMatchSnapshot()
|
|
31
31
|
expect(result).toEqual(items)
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
it('should dispose without error',
|
|
35
|
-
|
|
34
|
+
it('should dispose without error', () => {
|
|
35
|
+
expect(() => db.dispose()).not.toThrow()
|
|
36
36
|
})
|
|
37
37
|
})
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-base-to-string */
|
|
2
|
+
/* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
4
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
5
|
+
import { isSafari } from '@speckle/shared'
|
|
2
6
|
import { Item } from '../../types/types.js'
|
|
3
7
|
import { Database } from '../interfaces.js'
|
|
4
|
-
import BatchingQueue from '../../queues/batchingQueue.js'
|
|
5
|
-
import { ItemStore } from './ItemStore.js'
|
|
6
8
|
|
|
9
|
+
/**
|
|
10
|
+
* A wrapper class for IndexedDB to simplify common database operations.
|
|
11
|
+
*/
|
|
7
12
|
export interface IndexedDatabaseOptions {
|
|
8
13
|
indexedDB?: IDBFactory
|
|
9
14
|
keyRange?: {
|
|
@@ -12,38 +17,162 @@ export interface IndexedDatabaseOptions {
|
|
|
12
17
|
upperBound: Function
|
|
13
18
|
}
|
|
14
19
|
}
|
|
15
|
-
|
|
16
|
-
export default class IndexedDatabase implements Database {
|
|
20
|
+
export class IndexedDatabase implements Database {
|
|
17
21
|
#options: IndexedDatabaseOptions
|
|
18
|
-
|
|
19
|
-
#
|
|
22
|
+
|
|
23
|
+
#db: IDBDatabase | undefined = undefined
|
|
24
|
+
readonly #dbName: string = 'speckle-cache'
|
|
25
|
+
readonly #storeName: string = 'cache'
|
|
20
26
|
|
|
21
27
|
constructor(options: IndexedDatabaseOptions) {
|
|
22
28
|
this.#options = options
|
|
23
|
-
this.#cacheDB = new ItemStore(
|
|
24
|
-
{
|
|
25
|
-
indexedDB: this.#options.indexedDB,
|
|
26
|
-
keyRange: this.#options.keyRange
|
|
27
|
-
},
|
|
28
|
-
'speckle-cache',
|
|
29
|
-
'cache'
|
|
30
|
-
)
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Initializes the database connection and creates the object store if needed.
|
|
33
|
+
* This must be called before any other database operations.
|
|
34
|
+
*/
|
|
35
|
+
async init(): Promise<void> {
|
|
36
|
+
if (this.#db) return
|
|
37
|
+
await this.#safariFix()
|
|
38
|
+
return this.#openDatabase()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Opens the database, and if there's an error, deletes the database and tries again.
|
|
43
|
+
*/
|
|
44
|
+
async #openDatabase(): Promise<void> {
|
|
45
|
+
const idb = this.#options.indexedDB ?? indexedDB
|
|
46
|
+
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const request = idb.open(this.#dbName, 1)
|
|
49
|
+
|
|
50
|
+
request.onerror = (): any => {
|
|
51
|
+
console.warn(
|
|
52
|
+
`Failed to open database: ${this.#dbName}, deleting and trying again`
|
|
53
|
+
)
|
|
54
|
+
// Delete the database and try again
|
|
55
|
+
const deleteRequest = idb.deleteDatabase(this.#dbName)
|
|
56
|
+
deleteRequest.onsuccess = (): any => {
|
|
57
|
+
// Try opening again after deletion
|
|
58
|
+
void this.#openDatabase().then(resolve).catch(reject)
|
|
59
|
+
}
|
|
60
|
+
deleteRequest.onerror = (): any => {
|
|
61
|
+
reject(`Failed to delete and reopen database: ${this.#dbName}`)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
request.onupgradeneeded = (event): any => {
|
|
66
|
+
const db = (event.target as IDBOpenDBRequest).result
|
|
67
|
+
if (db.objectStoreNames.contains(this.#storeName)) {
|
|
68
|
+
db.deleteObjectStore(this.#storeName)
|
|
69
|
+
}
|
|
70
|
+
db.createObjectStore(this.#storeName, { keyPath: 'baseId' })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
request.onsuccess = (event): any => {
|
|
74
|
+
this.#db = (event.target as IDBOpenDBRequest).result
|
|
75
|
+
resolve()
|
|
76
|
+
}
|
|
77
|
+
})
|
|
36
78
|
}
|
|
37
79
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
80
|
+
#getDB(): IDBDatabase {
|
|
81
|
+
if (!this.#db) {
|
|
82
|
+
throw new Error('Database not initialized. Call init() first.')
|
|
83
|
+
}
|
|
84
|
+
return this.#db
|
|
42
85
|
}
|
|
43
86
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Fixes a Safari bug where IndexedDB requests get lost and never resolve - invoke before you use IndexedDB
|
|
89
|
+
* @link Credits and more info: https://github.com/jakearchibald/safari-14-idb-fix
|
|
90
|
+
*/
|
|
91
|
+
async #safariFix(): Promise<void> {
|
|
92
|
+
// No point putting other browsers or older versions of Safari through this mess.
|
|
93
|
+
if (!isSafari() || !this.#options.indexedDB?.databases) return Promise.resolve()
|
|
94
|
+
|
|
95
|
+
let intervalId: ReturnType<typeof setInterval>
|
|
96
|
+
|
|
97
|
+
return new Promise<void>((resolve: () => void) => {
|
|
98
|
+
const tryIdb = (): Promise<IDBDatabaseInfo[]> | undefined =>
|
|
99
|
+
this.#options.indexedDB?.databases().finally(resolve)
|
|
100
|
+
intervalId = setInterval(() => {
|
|
101
|
+
void tryIdb()
|
|
102
|
+
}, 100)
|
|
103
|
+
void tryIdb()
|
|
104
|
+
}).finally(() => clearInterval(intervalId))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Inserts or updates an array of items in a single transaction.
|
|
109
|
+
* @param data The array of items to insert.
|
|
110
|
+
*/
|
|
111
|
+
async putAll(data: Item[]): Promise<void> {
|
|
112
|
+
await this.init() // Ensure the database is initialized
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
try {
|
|
115
|
+
const transaction = this.#getDB().transaction(this.#storeName, 'readwrite', {
|
|
116
|
+
durability: 'relaxed'
|
|
117
|
+
})
|
|
118
|
+
const store = transaction.objectStore(this.#storeName)
|
|
119
|
+
|
|
120
|
+
transaction.onerror = (): any => {
|
|
121
|
+
reject(`Transaction error: ${transaction.error}`)
|
|
122
|
+
}
|
|
123
|
+
transaction.oncomplete = (): any => {
|
|
124
|
+
resolve()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
data.forEach((item) => store.put(item))
|
|
128
|
+
} catch (error) {
|
|
129
|
+
reject(error)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Retrieves an array of items from the object store based on their IDs.
|
|
136
|
+
* @param ids The array of IDs to retrieve.
|
|
137
|
+
*/
|
|
138
|
+
async getAll(ids: string[]): Promise<(Item | undefined)[]> {
|
|
139
|
+
await this.init() // Ensure the database is initialized
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
if (ids.length === 0) {
|
|
142
|
+
return resolve([])
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const transaction = this.#getDB().transaction(this.#storeName, 'readonly', {
|
|
146
|
+
durability: 'relaxed'
|
|
147
|
+
})
|
|
148
|
+
const store = transaction.objectStore(this.#storeName)
|
|
149
|
+
const promises: Promise<Item | undefined>[] = []
|
|
150
|
+
|
|
151
|
+
for (const id of ids) {
|
|
152
|
+
promises.push(
|
|
153
|
+
new Promise((resolveGet, rejectGet) => {
|
|
154
|
+
const request = store.get(id)
|
|
155
|
+
request.onerror = (): void =>
|
|
156
|
+
rejectGet(`Request error for id ${id}: ${request.error}`)
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
158
|
+
request.onsuccess = (): void => resolveGet(request.result)
|
|
159
|
+
})
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Promise.all(promises)
|
|
164
|
+
.then((results) => {
|
|
165
|
+
resolve(results)
|
|
166
|
+
})
|
|
167
|
+
.catch(reject)
|
|
168
|
+
} catch (error) {
|
|
169
|
+
reject(error)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
dispose(): void {
|
|
174
|
+
if (!this.#db) return
|
|
175
|
+
this.#db.close()
|
|
176
|
+
this.#db = undefined
|
|
48
177
|
}
|
|
49
178
|
}
|
|
@@ -21,14 +21,14 @@ describe('MemoryDatabase', () => {
|
|
|
21
21
|
|
|
22
22
|
it('should add and retrieve a single item', async () => {
|
|
23
23
|
const item = makeItem('id1')
|
|
24
|
-
await db.
|
|
24
|
+
await db.putAll([item])
|
|
25
25
|
const result = await db.getAll(['id1'])
|
|
26
26
|
expect(result).toEqual([item])
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
it('should add and retrieve multiple items', async () => {
|
|
30
30
|
const items = [makeItem('id1'), makeItem('id2', 'baz')]
|
|
31
|
-
await db.
|
|
31
|
+
await db.putAll(items)
|
|
32
32
|
const result = await db.getAll(['id1', 'id2'])
|
|
33
33
|
expect(result).toEqual(items)
|
|
34
34
|
})
|
|
@@ -36,13 +36,13 @@ describe('MemoryDatabase', () => {
|
|
|
36
36
|
it('should overwrite items with the same key', async () => {
|
|
37
37
|
const item1 = makeItem('id1', 'foo')
|
|
38
38
|
const item2 = makeItem('id1', 'bar')
|
|
39
|
-
await db.
|
|
40
|
-
await db.
|
|
39
|
+
await db.putAll([item1])
|
|
40
|
+
await db.putAll([item2])
|
|
41
41
|
const result = await db.getAll(['id1'])
|
|
42
42
|
expect(result).toEqual([item2])
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
it('
|
|
46
|
-
|
|
45
|
+
it('dispose should not throw', () => {
|
|
46
|
+
db.dispose()
|
|
47
47
|
})
|
|
48
48
|
})
|