-
-
Notifications
You must be signed in to change notification settings - Fork 626
/
tson.ts
81 lines (75 loc) · 2.72 KB
/
tson.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import Typeson from 'typeson';
import StructuredCloning from 'typeson-registry/dist/presets/structured-cloning';
import { encode as encodeB64, decode as decodeB64 } from 'base64-arraybuffer-es6';
import Dexie from 'dexie';
import { readBlobSync, readBlobAsync } from './helpers';
import typedArray from './tson-typed-array';
import arrayBuffer from './tson-arraybuffer';
export const TSON = new Typeson().register(StructuredCloning);
const readBlobsSynchronously = 'FileReaderSync' in self; // true in workers only.
let blobsToAwait: any[] = [];
let blobsToAwaitPos = 0;
// Need to patch encapsulateAsync as it does not work as of typeson 5.8.2
// Also, current version of typespn-registry-1.0.0-alpha.21 does not
// encapsulate/revive Blobs correctly (fails one of the unit tests in
// this library (test 'export-format'))
TSON.register([
arrayBuffer,
typedArray, {
blob2: {
test(x) { return Typeson.toStringTag(x) === 'Blob'; },
replace(b) {
if (b.isClosed) { // On MDN, but not in https://w3c.github.io/FileAPI/#dfn-Blob
throw new Error('The Blob is closed');
}
if (readBlobsSynchronously) {
const data = readBlobSync(b, 'binary');
const base64 = encodeB64(data, 0, data.byteLength);
return {
type: b.type,
data: base64
}
} else {
blobsToAwait.push(b); // This will also make TSON.mustFinalize() return true.
const result = {
type: b.type,
data: {start: blobsToAwaitPos, end: blobsToAwaitPos + b.size}
}
blobsToAwaitPos += b.size;
return result;
}
},
finalize(b, ba: ArrayBuffer) {
b.data = encodeB64(ba, 0, ba.byteLength);
},
revive ({type, data}) {
return new Blob([decodeB64(data)], {type});
}
}
}
]);
TSON.mustFinalize = ()=>blobsToAwait.length > 0;
TSON.finalize = async (items?: any[]) => {
const allChunks = await readBlobAsync(new Blob(blobsToAwait), 'binary');
if (items) {
for (const item of items) {
// Manually go through all "blob" types in the result
// and lookup the data slice they point at.
if (item.$types) {
let types = item.$types;
const arrayType = types.$;
if (arrayType) types = types.$;
for (let keyPath in types) {
const typeName = types[keyPath];
const typeSpec = TSON.types[typeName];
if (typeSpec && typeSpec.finalize) {
const b = Dexie.getByKeyPath(item, arrayType ? "$." + keyPath : keyPath);
typeSpec.finalize(b, allChunks.slice(b.start, b.end));
}
}
}
}
}
// Free up memory
blobsToAwait = [];
}