@jbrowse/core 4.0.4 → 4.1.1
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/esm/PluginLoader.js +11 -9
- package/esm/pluggableElementTypes/RpcMethodType.d.ts +1 -0
- package/esm/pluggableElementTypes/RpcMethodType.js +49 -5
- package/esm/pluggableElementTypes/RpcMethodTypeWithFiltersAndRenameRegions.js +1 -0
- package/esm/pluggableElementTypes/models/saveTrackFileTypes/bed.js +5 -2
- package/esm/pluggableElementTypes/models/saveTrackFileTypes/genbank.js +57 -26
- package/esm/pluggableElementTypes/models/saveTrackFileTypes/gff3.js +35 -16
- package/esm/pluggableElementTypes/renderers/LayoutSession.js +6 -3
- package/esm/pluggableElementTypes/renderers/ServerSideRendererType.js +4 -1
- package/esm/pluggableElementTypes/renderers/util/serializableFilterChain.d.ts +2 -1
- package/esm/pluggableElementTypes/renderers/util/serializableFilterChain.js +2 -2
- package/esm/rpc/methods/CoreRender.js +7 -2
- package/esm/ui/FileSelector/FileSelector.d.ts +0 -1
- package/esm/ui/FileSelector/FileSelector.js +19 -31
- package/esm/ui/FileSelector/LocalFileChooser.js +90 -26
- package/esm/ui/FileSelector/SourceTypeSelector.js +18 -33
- package/esm/ui/FileSelector/util.d.ts +8 -0
- package/esm/ui/FileSelector/util.js +34 -0
- package/esm/ui/SnackbarContents.js +4 -4
- package/esm/ui/SnackbarModel.d.ts +4 -4
- package/esm/ui/SnackbarModel.js +18 -7
- package/esm/ui/index.d.ts +1 -1
- package/esm/ui/index.js +1 -1
- package/esm/util/IntervalTree.d.ts +42 -0
- package/esm/util/IntervalTree.js +257 -0
- package/esm/util/colord.d.ts +22 -8
- package/esm/util/colord.js +227 -10
- package/esm/util/crypto.d.ts +7 -0
- package/esm/util/crypto.js +536 -0
- package/esm/util/fileHandleStore.d.ts +6 -0
- package/esm/util/fileHandleStore.js +68 -0
- package/esm/util/idMaker.js +9 -1
- package/esm/util/index.d.ts +3 -0
- package/esm/util/index.js +3 -0
- package/esm/util/io/index.js +11 -1
- package/esm/util/jexl.js +1 -0
- package/esm/util/tracks.d.ts +41 -7
- package/esm/util/tracks.js +141 -9
- package/esm/util/types/index.d.ts +10 -4
- package/esm/util/types/index.js +6 -0
- package/esm/util/types/mst.d.ts +17 -0
- package/esm/util/types/mst.js +10 -2
- package/package.json +350 -15
- package/esm/ui/FileSelector/index.d.ts +0 -1
- package/esm/ui/FileSelector/index.js +0 -1
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
const SALT_PREFIX = 'Salted__';
|
|
2
|
+
function isWebCryptoAvailable() {
|
|
3
|
+
return typeof crypto !== 'undefined' && crypto.subtle !== undefined;
|
|
4
|
+
}
|
|
5
|
+
const SBOX = new Uint8Array([
|
|
6
|
+
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe,
|
|
7
|
+
0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4,
|
|
8
|
+
0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7,
|
|
9
|
+
0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3,
|
|
10
|
+
0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09,
|
|
11
|
+
0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3,
|
|
12
|
+
0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe,
|
|
13
|
+
0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
|
14
|
+
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92,
|
|
15
|
+
0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c,
|
|
16
|
+
0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
|
|
17
|
+
0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
|
|
18
|
+
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2,
|
|
19
|
+
0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5,
|
|
20
|
+
0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
|
|
21
|
+
0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
|
22
|
+
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86,
|
|
23
|
+
0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e,
|
|
24
|
+
0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42,
|
|
25
|
+
0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
|
26
|
+
]);
|
|
27
|
+
const SBOX_INV = new Uint8Array([
|
|
28
|
+
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81,
|
|
29
|
+
0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e,
|
|
30
|
+
0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23,
|
|
31
|
+
0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66,
|
|
32
|
+
0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72,
|
|
33
|
+
0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65,
|
|
34
|
+
0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46,
|
|
35
|
+
0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
|
|
36
|
+
0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca,
|
|
37
|
+
0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91,
|
|
38
|
+
0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
|
|
39
|
+
0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
|
|
40
|
+
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f,
|
|
41
|
+
0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2,
|
|
42
|
+
0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8,
|
|
43
|
+
0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
|
44
|
+
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93,
|
|
45
|
+
0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb,
|
|
46
|
+
0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6,
|
|
47
|
+
0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
|
|
48
|
+
]);
|
|
49
|
+
const RCON = new Uint8Array([
|
|
50
|
+
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
|
|
51
|
+
]);
|
|
52
|
+
function gmul(a, b) {
|
|
53
|
+
let p = 0;
|
|
54
|
+
for (let i = 0; i < 8; i++) {
|
|
55
|
+
if (b & 1) {
|
|
56
|
+
p ^= a;
|
|
57
|
+
}
|
|
58
|
+
const hiBit = a & 0x80;
|
|
59
|
+
a = (a << 1) & 0xff;
|
|
60
|
+
if (hiBit) {
|
|
61
|
+
a ^= 0x1b;
|
|
62
|
+
}
|
|
63
|
+
b >>= 1;
|
|
64
|
+
}
|
|
65
|
+
return p;
|
|
66
|
+
}
|
|
67
|
+
function aesKeyExpansion(key) {
|
|
68
|
+
const Nk = 8;
|
|
69
|
+
const Nr = 14;
|
|
70
|
+
const expandedKey = new Uint8Array(16 * (Nr + 1));
|
|
71
|
+
expandedKey.set(key);
|
|
72
|
+
for (let i = Nk; i < 4 * (Nr + 1); i++) {
|
|
73
|
+
const temp = expandedKey.slice((i - 1) * 4, i * 4);
|
|
74
|
+
if (i % Nk === 0) {
|
|
75
|
+
const t = temp[0];
|
|
76
|
+
temp[0] = SBOX[temp[1]] ^ RCON[i / Nk - 1];
|
|
77
|
+
temp[1] = SBOX[temp[2]];
|
|
78
|
+
temp[2] = SBOX[temp[3]];
|
|
79
|
+
temp[3] = SBOX[t];
|
|
80
|
+
}
|
|
81
|
+
else if (Nk > 6 && i % Nk === 4) {
|
|
82
|
+
for (let j = 0; j < 4; j++) {
|
|
83
|
+
temp[j] = SBOX[temp[j]];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (let j = 0; j < 4; j++) {
|
|
87
|
+
expandedKey[i * 4 + j] = expandedKey[(i - Nk) * 4 + j] ^ temp[j];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return expandedKey;
|
|
91
|
+
}
|
|
92
|
+
function aesEncryptBlock(block, expandedKey) {
|
|
93
|
+
const Nr = 14;
|
|
94
|
+
const state = new Uint8Array(16);
|
|
95
|
+
state.set(block);
|
|
96
|
+
for (let i = 0; i < 16; i++) {
|
|
97
|
+
state[i] ^= expandedKey[i];
|
|
98
|
+
}
|
|
99
|
+
for (let round = 1; round <= Nr; round++) {
|
|
100
|
+
for (let i = 0; i < 16; i++) {
|
|
101
|
+
state[i] = SBOX[state[i]];
|
|
102
|
+
}
|
|
103
|
+
const t1 = state[1];
|
|
104
|
+
state[1] = state[5];
|
|
105
|
+
state[5] = state[9];
|
|
106
|
+
state[9] = state[13];
|
|
107
|
+
state[13] = t1;
|
|
108
|
+
const t2 = state[2];
|
|
109
|
+
const t6 = state[6];
|
|
110
|
+
state[2] = state[10];
|
|
111
|
+
state[6] = state[14];
|
|
112
|
+
state[10] = t2;
|
|
113
|
+
state[14] = t6;
|
|
114
|
+
const t3 = state[15];
|
|
115
|
+
state[15] = state[11];
|
|
116
|
+
state[11] = state[7];
|
|
117
|
+
state[7] = state[3];
|
|
118
|
+
state[3] = t3;
|
|
119
|
+
if (round < Nr) {
|
|
120
|
+
for (let c = 0; c < 4; c++) {
|
|
121
|
+
const i = c * 4;
|
|
122
|
+
const s0 = state[i];
|
|
123
|
+
const s1 = state[i + 1];
|
|
124
|
+
const s2 = state[i + 2];
|
|
125
|
+
const s3 = state[i + 3];
|
|
126
|
+
state[i] = gmul(s0, 2) ^ gmul(s1, 3) ^ s2 ^ s3;
|
|
127
|
+
state[i + 1] = s0 ^ gmul(s1, 2) ^ gmul(s2, 3) ^ s3;
|
|
128
|
+
state[i + 2] = s0 ^ s1 ^ gmul(s2, 2) ^ gmul(s3, 3);
|
|
129
|
+
state[i + 3] = gmul(s0, 3) ^ s1 ^ s2 ^ gmul(s3, 2);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const roundKey = expandedKey.subarray(round * 16, (round + 1) * 16);
|
|
133
|
+
for (let i = 0; i < 16; i++) {
|
|
134
|
+
state[i] ^= roundKey[i];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return state;
|
|
138
|
+
}
|
|
139
|
+
function aesDecryptBlock(block, expandedKey) {
|
|
140
|
+
const Nr = 14;
|
|
141
|
+
const state = new Uint8Array(16);
|
|
142
|
+
state.set(block);
|
|
143
|
+
for (let i = 0; i < 16; i++) {
|
|
144
|
+
state[i] ^= expandedKey[Nr * 16 + i];
|
|
145
|
+
}
|
|
146
|
+
for (let round = Nr - 1; round >= 0; round--) {
|
|
147
|
+
const t1 = state[13];
|
|
148
|
+
state[13] = state[9];
|
|
149
|
+
state[9] = state[5];
|
|
150
|
+
state[5] = state[1];
|
|
151
|
+
state[1] = t1;
|
|
152
|
+
const t2 = state[10];
|
|
153
|
+
const t6 = state[14];
|
|
154
|
+
state[10] = state[2];
|
|
155
|
+
state[14] = state[6];
|
|
156
|
+
state[2] = t2;
|
|
157
|
+
state[6] = t6;
|
|
158
|
+
const t3 = state[3];
|
|
159
|
+
state[3] = state[7];
|
|
160
|
+
state[7] = state[11];
|
|
161
|
+
state[11] = state[15];
|
|
162
|
+
state[15] = t3;
|
|
163
|
+
for (let i = 0; i < 16; i++) {
|
|
164
|
+
state[i] = SBOX_INV[state[i]];
|
|
165
|
+
}
|
|
166
|
+
const roundKey = expandedKey.subarray(round * 16, (round + 1) * 16);
|
|
167
|
+
for (let i = 0; i < 16; i++) {
|
|
168
|
+
state[i] ^= roundKey[i];
|
|
169
|
+
}
|
|
170
|
+
if (round > 0) {
|
|
171
|
+
for (let c = 0; c < 4; c++) {
|
|
172
|
+
const i = c * 4;
|
|
173
|
+
const s0 = state[i];
|
|
174
|
+
const s1 = state[i + 1];
|
|
175
|
+
const s2 = state[i + 2];
|
|
176
|
+
const s3 = state[i + 3];
|
|
177
|
+
state[i] = gmul(s0, 14) ^ gmul(s1, 11) ^ gmul(s2, 13) ^ gmul(s3, 9);
|
|
178
|
+
state[i + 1] = gmul(s0, 9) ^ gmul(s1, 14) ^ gmul(s2, 11) ^ gmul(s3, 13);
|
|
179
|
+
state[i + 2] = gmul(s0, 13) ^ gmul(s1, 9) ^ gmul(s2, 14) ^ gmul(s3, 11);
|
|
180
|
+
state[i + 3] = gmul(s0, 11) ^ gmul(s1, 13) ^ gmul(s2, 9) ^ gmul(s3, 14);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return state;
|
|
185
|
+
}
|
|
186
|
+
function pkcs7Pad(data, blockSize) {
|
|
187
|
+
const padding = blockSize - (data.length % blockSize);
|
|
188
|
+
const padded = new Uint8Array(data.length + padding);
|
|
189
|
+
padded.set(data);
|
|
190
|
+
padded.fill(padding, data.length);
|
|
191
|
+
return padded;
|
|
192
|
+
}
|
|
193
|
+
function pkcs7Unpad(data) {
|
|
194
|
+
const padding = data[data.length - 1];
|
|
195
|
+
if (padding > 16 || padding === 0) {
|
|
196
|
+
throw new Error('Invalid padding');
|
|
197
|
+
}
|
|
198
|
+
for (let i = data.length - padding; i < data.length; i++) {
|
|
199
|
+
if (data[i] !== padding) {
|
|
200
|
+
throw new Error('Invalid padding');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return data.subarray(0, data.length - padding);
|
|
204
|
+
}
|
|
205
|
+
function aesCbcEncryptFallback(plaintext, key, iv) {
|
|
206
|
+
const expandedKey = aesKeyExpansion(key);
|
|
207
|
+
const padded = pkcs7Pad(plaintext, 16);
|
|
208
|
+
const ciphertext = new Uint8Array(padded.length);
|
|
209
|
+
let prevBlock = iv;
|
|
210
|
+
for (let i = 0; i < padded.length; i += 16) {
|
|
211
|
+
const block = padded.slice(i, i + 16);
|
|
212
|
+
for (let j = 0; j < 16; j++) {
|
|
213
|
+
block[j] ^= prevBlock[j];
|
|
214
|
+
}
|
|
215
|
+
const encrypted = aesEncryptBlock(block, expandedKey);
|
|
216
|
+
ciphertext.set(encrypted, i);
|
|
217
|
+
prevBlock = encrypted;
|
|
218
|
+
}
|
|
219
|
+
return ciphertext;
|
|
220
|
+
}
|
|
221
|
+
function aesCbcDecryptFallback(ciphertext, key, iv) {
|
|
222
|
+
const expandedKey = aesKeyExpansion(key);
|
|
223
|
+
const plaintext = new Uint8Array(ciphertext.length);
|
|
224
|
+
let prevBlock = iv;
|
|
225
|
+
for (let i = 0; i < ciphertext.length; i += 16) {
|
|
226
|
+
const block = ciphertext.slice(i, i + 16);
|
|
227
|
+
const decrypted = aesDecryptBlock(block, expandedKey);
|
|
228
|
+
for (let j = 0; j < 16; j++) {
|
|
229
|
+
decrypted[j] ^= prevBlock[j];
|
|
230
|
+
}
|
|
231
|
+
plaintext.set(decrypted, i);
|
|
232
|
+
prevBlock = block;
|
|
233
|
+
}
|
|
234
|
+
return pkcs7Unpad(plaintext);
|
|
235
|
+
}
|
|
236
|
+
function getRandomBytes(length) {
|
|
237
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
238
|
+
return crypto.getRandomValues(new Uint8Array(length));
|
|
239
|
+
}
|
|
240
|
+
const bytes = new Uint8Array(length);
|
|
241
|
+
for (let i = 0; i < length; i++) {
|
|
242
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
243
|
+
}
|
|
244
|
+
return bytes;
|
|
245
|
+
}
|
|
246
|
+
const SHA256_K = new Uint32Array([
|
|
247
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
248
|
+
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
249
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
250
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
251
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
|
252
|
+
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
253
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
|
254
|
+
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
255
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
256
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
257
|
+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
258
|
+
]);
|
|
259
|
+
function sha256Fallback(data) {
|
|
260
|
+
function rotr(x, n) {
|
|
261
|
+
return (x >>> n) | (x << (32 - n));
|
|
262
|
+
}
|
|
263
|
+
function ch(x, y, z) {
|
|
264
|
+
return (x & y) ^ (~x & z);
|
|
265
|
+
}
|
|
266
|
+
function maj(x, y, z) {
|
|
267
|
+
return (x & y) ^ (x & z) ^ (y & z);
|
|
268
|
+
}
|
|
269
|
+
function sigma0(x) {
|
|
270
|
+
return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
|
|
271
|
+
}
|
|
272
|
+
function sigma1(x) {
|
|
273
|
+
return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
|
|
274
|
+
}
|
|
275
|
+
function gamma0(x) {
|
|
276
|
+
return rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3);
|
|
277
|
+
}
|
|
278
|
+
function gamma1(x) {
|
|
279
|
+
return rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10);
|
|
280
|
+
}
|
|
281
|
+
const originalLength = data.length;
|
|
282
|
+
const bitLength = originalLength * 8;
|
|
283
|
+
const paddingLength = (64 - ((originalLength + 9) % 64)) % 64;
|
|
284
|
+
const padded = new Uint8Array(originalLength + 1 + paddingLength + 8);
|
|
285
|
+
padded.set(data);
|
|
286
|
+
padded[originalLength] = 0x80;
|
|
287
|
+
const view = new DataView(padded.buffer);
|
|
288
|
+
view.setUint32(padded.length - 4, bitLength >>> 0, false);
|
|
289
|
+
view.setUint32(padded.length - 8, Math.floor(bitLength / 0x100000000), false);
|
|
290
|
+
let h0 = 0x6a09e667;
|
|
291
|
+
let h1 = 0xbb67ae85;
|
|
292
|
+
let h2 = 0x3c6ef372;
|
|
293
|
+
let h3 = 0xa54ff53a;
|
|
294
|
+
let h4 = 0x510e527f;
|
|
295
|
+
let h5 = 0x9b05688c;
|
|
296
|
+
let h6 = 0x1f83d9ab;
|
|
297
|
+
let h7 = 0x5be0cd19;
|
|
298
|
+
const W = new Uint32Array(64);
|
|
299
|
+
for (let i = 0; i < padded.length; i += 64) {
|
|
300
|
+
for (let t = 0; t < 16; t++) {
|
|
301
|
+
W[t] = view.getUint32(i + t * 4, false);
|
|
302
|
+
}
|
|
303
|
+
for (let t = 16; t < 64; t++) {
|
|
304
|
+
W[t] =
|
|
305
|
+
(gamma1(W[t - 2]) + W[t - 7] + gamma0(W[t - 15]) + W[t - 16]) >>> 0;
|
|
306
|
+
}
|
|
307
|
+
let a = h0;
|
|
308
|
+
let b = h1;
|
|
309
|
+
let c = h2;
|
|
310
|
+
let d = h3;
|
|
311
|
+
let e = h4;
|
|
312
|
+
let f = h5;
|
|
313
|
+
let g = h6;
|
|
314
|
+
let h = h7;
|
|
315
|
+
for (let t = 0; t < 64; t++) {
|
|
316
|
+
const T1 = (h + sigma1(e) + ch(e, f, g) + SHA256_K[t] + W[t]) >>> 0;
|
|
317
|
+
const T2 = (sigma0(a) + maj(a, b, c)) >>> 0;
|
|
318
|
+
h = g;
|
|
319
|
+
g = f;
|
|
320
|
+
f = e;
|
|
321
|
+
e = (d + T1) >>> 0;
|
|
322
|
+
d = c;
|
|
323
|
+
c = b;
|
|
324
|
+
b = a;
|
|
325
|
+
a = (T1 + T2) >>> 0;
|
|
326
|
+
}
|
|
327
|
+
h0 = (h0 + a) >>> 0;
|
|
328
|
+
h1 = (h1 + b) >>> 0;
|
|
329
|
+
h2 = (h2 + c) >>> 0;
|
|
330
|
+
h3 = (h3 + d) >>> 0;
|
|
331
|
+
h4 = (h4 + e) >>> 0;
|
|
332
|
+
h5 = (h5 + f) >>> 0;
|
|
333
|
+
h6 = (h6 + g) >>> 0;
|
|
334
|
+
h7 = (h7 + h) >>> 0;
|
|
335
|
+
}
|
|
336
|
+
const result = new Uint8Array(32);
|
|
337
|
+
const resultView = new DataView(result.buffer);
|
|
338
|
+
resultView.setUint32(0, h0, false);
|
|
339
|
+
resultView.setUint32(4, h1, false);
|
|
340
|
+
resultView.setUint32(8, h2, false);
|
|
341
|
+
resultView.setUint32(12, h3, false);
|
|
342
|
+
resultView.setUint32(16, h4, false);
|
|
343
|
+
resultView.setUint32(20, h5, false);
|
|
344
|
+
resultView.setUint32(24, h6, false);
|
|
345
|
+
resultView.setUint32(28, h7, false);
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
function md5(data) {
|
|
349
|
+
const S = [
|
|
350
|
+
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5,
|
|
351
|
+
9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11,
|
|
352
|
+
16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10,
|
|
353
|
+
15, 21,
|
|
354
|
+
];
|
|
355
|
+
const K = [
|
|
356
|
+
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
|
|
357
|
+
0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
|
358
|
+
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
|
|
359
|
+
0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
|
360
|
+
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
|
|
361
|
+
0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
|
362
|
+
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
|
|
363
|
+
0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
|
364
|
+
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
|
|
365
|
+
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
|
366
|
+
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
|
|
367
|
+
];
|
|
368
|
+
function leftRotate(x, c) {
|
|
369
|
+
return (x << c) | (x >>> (32 - c));
|
|
370
|
+
}
|
|
371
|
+
const originalLength = data.length;
|
|
372
|
+
const bitLength = originalLength * 8;
|
|
373
|
+
const paddingLength = (56 - ((originalLength + 1) % 64) + 64) % 64;
|
|
374
|
+
const padded = new Uint8Array(originalLength + 1 + paddingLength + 8);
|
|
375
|
+
padded.set(data);
|
|
376
|
+
padded[originalLength] = 0x80;
|
|
377
|
+
const view = new DataView(padded.buffer);
|
|
378
|
+
view.setUint32(padded.length - 8, bitLength >>> 0, true);
|
|
379
|
+
view.setUint32(padded.length - 4, Math.floor(bitLength / 0x100000000), true);
|
|
380
|
+
let a0 = 0x67452301;
|
|
381
|
+
let b0 = 0xefcdab89;
|
|
382
|
+
let c0 = 0x98badcfe;
|
|
383
|
+
let d0 = 0x10325476;
|
|
384
|
+
for (let i = 0; i < padded.length; i += 64) {
|
|
385
|
+
const M = new Uint32Array(16);
|
|
386
|
+
for (let j = 0; j < 16; j++) {
|
|
387
|
+
M[j] = view.getUint32(i + j * 4, true);
|
|
388
|
+
}
|
|
389
|
+
let A = a0;
|
|
390
|
+
let B = b0;
|
|
391
|
+
let C = c0;
|
|
392
|
+
let D = d0;
|
|
393
|
+
for (let j = 0; j < 64; j++) {
|
|
394
|
+
let F;
|
|
395
|
+
let g;
|
|
396
|
+
if (j < 16) {
|
|
397
|
+
F = (B & C) | (~B & D);
|
|
398
|
+
g = j;
|
|
399
|
+
}
|
|
400
|
+
else if (j < 32) {
|
|
401
|
+
F = (D & B) | (~D & C);
|
|
402
|
+
g = (5 * j + 1) % 16;
|
|
403
|
+
}
|
|
404
|
+
else if (j < 48) {
|
|
405
|
+
F = B ^ C ^ D;
|
|
406
|
+
g = (3 * j + 5) % 16;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
F = C ^ (B | ~D);
|
|
410
|
+
g = (7 * j) % 16;
|
|
411
|
+
}
|
|
412
|
+
F = (F + A + K[j] + M[g]) >>> 0;
|
|
413
|
+
A = D;
|
|
414
|
+
D = C;
|
|
415
|
+
C = B;
|
|
416
|
+
B = (B + leftRotate(F, S[j])) >>> 0;
|
|
417
|
+
}
|
|
418
|
+
a0 = (a0 + A) >>> 0;
|
|
419
|
+
b0 = (b0 + B) >>> 0;
|
|
420
|
+
c0 = (c0 + C) >>> 0;
|
|
421
|
+
d0 = (d0 + D) >>> 0;
|
|
422
|
+
}
|
|
423
|
+
const result = new Uint8Array(16);
|
|
424
|
+
const resultView = new DataView(result.buffer);
|
|
425
|
+
resultView.setUint32(0, a0, true);
|
|
426
|
+
resultView.setUint32(4, b0, true);
|
|
427
|
+
resultView.setUint32(8, c0, true);
|
|
428
|
+
resultView.setUint32(12, d0, true);
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
function evpBytesToKey(password, salt, keyLen, ivLen) {
|
|
432
|
+
const result = new Uint8Array(keyLen + ivLen);
|
|
433
|
+
let offset = 0;
|
|
434
|
+
let prevHash = new Uint8Array(0);
|
|
435
|
+
while (offset < keyLen + ivLen) {
|
|
436
|
+
const data = new Uint8Array(prevHash.length + password.length + salt.length);
|
|
437
|
+
data.set(prevHash);
|
|
438
|
+
data.set(password, prevHash.length);
|
|
439
|
+
data.set(salt, prevHash.length + password.length);
|
|
440
|
+
prevHash = md5(data);
|
|
441
|
+
result.set(prevHash.subarray(0, Math.min(16, keyLen + ivLen - offset)), offset);
|
|
442
|
+
offset += 16;
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
key: result.slice(0, keyLen),
|
|
446
|
+
iv: result.slice(keyLen, keyLen + ivLen),
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function stringToBytes(str) {
|
|
450
|
+
const encoded = new TextEncoder().encode(str);
|
|
451
|
+
return encoded.slice();
|
|
452
|
+
}
|
|
453
|
+
function bytesToString(bytes) {
|
|
454
|
+
return new TextDecoder().decode(bytes);
|
|
455
|
+
}
|
|
456
|
+
function base64Encode(data) {
|
|
457
|
+
let binary = '';
|
|
458
|
+
for (const byte of data) {
|
|
459
|
+
binary += String.fromCharCode(byte);
|
|
460
|
+
}
|
|
461
|
+
return btoa(binary);
|
|
462
|
+
}
|
|
463
|
+
function base64Decode(str) {
|
|
464
|
+
const binary = atob(str);
|
|
465
|
+
const bytes = new Uint8Array(binary.length);
|
|
466
|
+
for (let i = 0; i < binary.length; i++) {
|
|
467
|
+
bytes[i] = binary.charCodeAt(i);
|
|
468
|
+
}
|
|
469
|
+
return bytes;
|
|
470
|
+
}
|
|
471
|
+
export async function aesEncrypt(plaintext, password) {
|
|
472
|
+
const salt = getRandomBytes(8);
|
|
473
|
+
const { key, iv } = evpBytesToKey(stringToBytes(password), salt, 32, 16);
|
|
474
|
+
let encrypted;
|
|
475
|
+
if (isWebCryptoAvailable()) {
|
|
476
|
+
const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['encrypt']);
|
|
477
|
+
const encryptedBuffer = await crypto.subtle.encrypt({ name: 'AES-CBC', iv }, cryptoKey, stringToBytes(plaintext));
|
|
478
|
+
encrypted = new Uint8Array(encryptedBuffer);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
encrypted = aesCbcEncryptFallback(stringToBytes(plaintext), key, iv);
|
|
482
|
+
}
|
|
483
|
+
const saltPrefix = stringToBytes(SALT_PREFIX);
|
|
484
|
+
const result = new Uint8Array(saltPrefix.length + salt.length + encrypted.byteLength);
|
|
485
|
+
result.set(saltPrefix);
|
|
486
|
+
result.set(salt, saltPrefix.length);
|
|
487
|
+
result.set(encrypted, saltPrefix.length + salt.length);
|
|
488
|
+
return base64Encode(result);
|
|
489
|
+
}
|
|
490
|
+
export async function aesDecrypt(ciphertext, password) {
|
|
491
|
+
const data = base64Decode(ciphertext);
|
|
492
|
+
const saltPrefix = stringToBytes(SALT_PREFIX);
|
|
493
|
+
const hasSalt = data.length > saltPrefix.length &&
|
|
494
|
+
bytesToString(data.subarray(0, saltPrefix.length)) === SALT_PREFIX;
|
|
495
|
+
if (!hasSalt) {
|
|
496
|
+
throw new Error('Invalid encrypted data format');
|
|
497
|
+
}
|
|
498
|
+
const salt = data.slice(saltPrefix.length, saltPrefix.length + 8);
|
|
499
|
+
const encrypted = data.slice(saltPrefix.length + 8);
|
|
500
|
+
const { key, iv } = evpBytesToKey(stringToBytes(password), salt, 32, 16);
|
|
501
|
+
let decrypted;
|
|
502
|
+
if (isWebCryptoAvailable()) {
|
|
503
|
+
const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['decrypt']);
|
|
504
|
+
const decryptedBuffer = await crypto.subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, encrypted);
|
|
505
|
+
decrypted = new Uint8Array(decryptedBuffer);
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
decrypted = aesCbcDecryptFallback(encrypted, key, iv);
|
|
509
|
+
}
|
|
510
|
+
return bytesToString(decrypted);
|
|
511
|
+
}
|
|
512
|
+
export async function sha256(data) {
|
|
513
|
+
const encoded = stringToBytes(data);
|
|
514
|
+
if (isWebCryptoAvailable()) {
|
|
515
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', encoded);
|
|
516
|
+
return new Uint8Array(hashBuffer);
|
|
517
|
+
}
|
|
518
|
+
return sha256Fallback(encoded);
|
|
519
|
+
}
|
|
520
|
+
export function toBase64(data) {
|
|
521
|
+
return base64Encode(data);
|
|
522
|
+
}
|
|
523
|
+
export function toBase64Url(data) {
|
|
524
|
+
return base64Encode(data)
|
|
525
|
+
.replaceAll('+', '-')
|
|
526
|
+
.replaceAll('/', '_')
|
|
527
|
+
.replaceAll('=', '');
|
|
528
|
+
}
|
|
529
|
+
export async function sha256Base64(data) {
|
|
530
|
+
const hash = await sha256(data);
|
|
531
|
+
return toBase64(hash);
|
|
532
|
+
}
|
|
533
|
+
export async function sha256Base64Url(data) {
|
|
534
|
+
const hash = await sha256(data);
|
|
535
|
+
return toBase64Url(hash);
|
|
536
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function isFileSystemAccessSupported(): boolean;
|
|
2
|
+
export declare function storeFileHandle(handle: FileSystemFileHandle): Promise<string>;
|
|
3
|
+
export declare function getFileHandle(handleId: string): Promise<FileSystemFileHandle | undefined>;
|
|
4
|
+
export declare function removeFileHandle(handleId: string): Promise<void>;
|
|
5
|
+
export declare function verifyPermission(handle: FileSystemFileHandle, requestPermission?: boolean): Promise<boolean>;
|
|
6
|
+
export declare function cleanupStaleHandles(maxAgeMs: number): Promise<void>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { openDB } from 'idb';
|
|
2
|
+
const DB_NAME = 'jbrowse-file-handles';
|
|
3
|
+
const DB_VERSION = 1;
|
|
4
|
+
const STORE_NAME = 'handles';
|
|
5
|
+
let dbPromise;
|
|
6
|
+
function getDB() {
|
|
7
|
+
if (!dbPromise) {
|
|
8
|
+
dbPromise = openDB(DB_NAME, DB_VERSION, {
|
|
9
|
+
upgrade(db) {
|
|
10
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
11
|
+
db.createObjectStore(STORE_NAME);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return dbPromise;
|
|
17
|
+
}
|
|
18
|
+
export function isFileSystemAccessSupported() {
|
|
19
|
+
return typeof window !== 'undefined' && 'showOpenFilePicker' in window;
|
|
20
|
+
}
|
|
21
|
+
let counter = 0;
|
|
22
|
+
export async function storeFileHandle(handle) {
|
|
23
|
+
const handleId = `fh${Date.now()}-${counter++}`;
|
|
24
|
+
const db = await getDB();
|
|
25
|
+
await db.put(STORE_NAME, {
|
|
26
|
+
handle,
|
|
27
|
+
name: handle.name,
|
|
28
|
+
createdAt: Date.now(),
|
|
29
|
+
}, handleId);
|
|
30
|
+
return handleId;
|
|
31
|
+
}
|
|
32
|
+
export async function getFileHandle(handleId) {
|
|
33
|
+
const db = await getDB();
|
|
34
|
+
const entry = await db.get(STORE_NAME, handleId);
|
|
35
|
+
return entry?.handle;
|
|
36
|
+
}
|
|
37
|
+
export async function removeFileHandle(handleId) {
|
|
38
|
+
const db = await getDB();
|
|
39
|
+
await db.delete(STORE_NAME, handleId);
|
|
40
|
+
}
|
|
41
|
+
export async function verifyPermission(handle, requestPermission = false) {
|
|
42
|
+
const options = { mode: 'read' };
|
|
43
|
+
const currentPermission = await handle.queryPermission(options);
|
|
44
|
+
if (currentPermission === 'granted') {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (requestPermission) {
|
|
48
|
+
const newPermission = await handle.requestPermission(options);
|
|
49
|
+
if (newPermission === 'granted') {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
export async function cleanupStaleHandles(maxAgeMs) {
|
|
56
|
+
const db = await getDB();
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
59
|
+
const store = tx.objectStore(STORE_NAME);
|
|
60
|
+
let cursor = await store.openCursor();
|
|
61
|
+
while (cursor) {
|
|
62
|
+
if (now - cursor.value.createdAt > maxAgeMs) {
|
|
63
|
+
await cursor.delete();
|
|
64
|
+
}
|
|
65
|
+
cursor = await cursor.continue();
|
|
66
|
+
}
|
|
67
|
+
await tx.done;
|
|
68
|
+
}
|
package/esm/util/idMaker.js
CHANGED
|
@@ -12,7 +12,15 @@ export default function idMaker(args, id = '', len = 5000) {
|
|
|
12
12
|
stack.push(val);
|
|
13
13
|
}
|
|
14
14
|
else {
|
|
15
|
-
|
|
15
|
+
if (key === 'locationType' && val === 'FileHandleLocation') {
|
|
16
|
+
id += `${key}-BlobLocation`;
|
|
17
|
+
}
|
|
18
|
+
else if (key === 'handleId') {
|
|
19
|
+
id += `blobId-fh-blob-${val}`;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
id += `${key}-${val}`;
|
|
23
|
+
}
|
|
16
24
|
}
|
|
17
25
|
}
|
|
18
26
|
}
|
package/esm/util/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from './coarseStripHTML.ts';
|
|
|
17
17
|
export * from './offscreenCanvasPonyfill.tsx';
|
|
18
18
|
export * from './offscreenCanvasUtils.tsx';
|
|
19
19
|
export * from './rpc.ts';
|
|
20
|
+
export * from './crypto.ts';
|
|
20
21
|
export declare function useDebounce<T>(value: T, delay: number): T;
|
|
21
22
|
export declare function useWidthSetter(view: {
|
|
22
23
|
setWidth: (arg: number) => void;
|
|
@@ -269,3 +270,5 @@ export * from './linkify.ts';
|
|
|
269
270
|
export * from './locString.ts';
|
|
270
271
|
export * from './stopToken.ts';
|
|
271
272
|
export * from './tracks.ts';
|
|
273
|
+
export * from './fileHandleStore.ts';
|
|
274
|
+
export { IntervalTree } from './IntervalTree.ts';
|
package/esm/util/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export * from "./coarseStripHTML.js";
|
|
|
17
17
|
export * from "./offscreenCanvasPonyfill.js";
|
|
18
18
|
export * from "./offscreenCanvasUtils.js";
|
|
19
19
|
export * from "./rpc.js";
|
|
20
|
+
export * from "./crypto.js";
|
|
20
21
|
const containingDisplayCache = new WeakMap();
|
|
21
22
|
const containingTrackCache = new WeakMap();
|
|
22
23
|
const containingViewCache = new WeakMap();
|
|
@@ -871,3 +872,5 @@ export * from "./linkify.js";
|
|
|
871
872
|
export * from "./locString.js";
|
|
872
873
|
export * from "./stopToken.js";
|
|
873
874
|
export * from "./tracks.js";
|
|
875
|
+
export * from "./fileHandleStore.js";
|
|
876
|
+
export { IntervalTree } from "./IntervalTree.js";
|
package/esm/util/io/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import isNode from 'detect-node';
|
|
|
2
2
|
import { BlobFile, LocalFile } from 'generic-filehandle2';
|
|
3
3
|
import { RemoteFileWithRangeCache } from "./RemoteFileWithRangeCache.js";
|
|
4
4
|
import { isElectron } from "../index.js";
|
|
5
|
-
import { getBlob } from "../tracks.js";
|
|
5
|
+
import { getBlob, getFileFromCache } from "../tracks.js";
|
|
6
6
|
import { AuthNeededError, isRootModelWithInternetAccounts, isUriLocation, } from "../types/index.js";
|
|
7
7
|
function isLocalPathLocation(location) {
|
|
8
8
|
return 'localPath' in location;
|
|
@@ -10,6 +10,9 @@ function isLocalPathLocation(location) {
|
|
|
10
10
|
function isBlobLocation(location) {
|
|
11
11
|
return 'blobId' in location;
|
|
12
12
|
}
|
|
13
|
+
function isFileHandleLocationLocal(location) {
|
|
14
|
+
return 'handleId' in location;
|
|
15
|
+
}
|
|
13
16
|
export function resolveUriLocation(location) {
|
|
14
17
|
return location.baseUri
|
|
15
18
|
? { ...location, uri: new URL(location.uri, location.baseUri).href }
|
|
@@ -34,6 +37,13 @@ export function openLocation(location, pluginManager) {
|
|
|
34
37
|
}
|
|
35
38
|
return new BlobFile(blob);
|
|
36
39
|
}
|
|
40
|
+
if (isFileHandleLocationLocal(location)) {
|
|
41
|
+
const file = getFileFromCache(location.handleId);
|
|
42
|
+
if (!file) {
|
|
43
|
+
throw new Error(`file ("${location.name}") requires permission. Please reopen the file from track settings`);
|
|
44
|
+
}
|
|
45
|
+
return new BlobFile(file);
|
|
46
|
+
}
|
|
37
47
|
if (isUriLocation(location)) {
|
|
38
48
|
if (!location.uri) {
|
|
39
49
|
throw new Error('No URI provided');
|