-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathstorage.js
168 lines (145 loc) · 4.09 KB
/
storage.js
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* Browser-agnostic abstraction of key-value web storage.
*
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
*/
export const IMPL_HUB_STORAGE = 0;
export const IMPL_INDEXED_DB = 1;
const INDEXED_DB_VERSION = 1;
const INDEXED_DB_NAME = 'para-tgui';
const INDEXED_DB_STORE_NAME = 'storage-v1';
const READ_ONLY = 'readonly';
const READ_WRITE = 'readwrite';
const testGeneric = (testFn) => () => {
try {
return Boolean(testFn());
} catch {
return false;
}
};
const testHubStorage = testGeneric(() => window.hubStorage && window.hubStorage.getItem);
// TODO: Remove with 516
// prettier-ignore
const testIndexedDb = testGeneric(() => (
(window.indexedDB || window.msIndexedDB)
&& (window.IDBTransaction || window.msIDBTransaction)
));
class HubStorageBackend {
constructor() {
this.impl = IMPL_HUB_STORAGE;
}
get(key) {
const value = window.hubStorage.getItem('paradise-' + key);
if (typeof value === 'string') {
return JSON.parse(value);
}
}
set(key, value) {
window.hubStorage.setItem('paradise-' + key, JSON.stringify(value));
}
remove(key) {
window.hubStorage.removeItem('paradise-' + key);
}
clear() {
window.hubStorage.clear();
}
}
class IndexedDbBackend {
// TODO: Remove with 516
constructor() {
this.impl = IMPL_INDEXED_DB;
/** @type {Promise<IDBDatabase>} */
this.dbPromise = new Promise((resolve, reject) => {
const indexedDB = window.indexedDB || window.msIndexedDB;
const req = indexedDB.open(INDEXED_DB_NAME, INDEXED_DB_VERSION);
req.onupgradeneeded = () => {
try {
req.result.createObjectStore(INDEXED_DB_STORE_NAME);
} catch (err) {
reject(new Error('Failed to upgrade IDB: ' + req.error));
}
};
req.onsuccess = () => resolve(req.result);
req.onerror = () => {
reject(new Error('Failed to open IDB: ' + req.error));
};
});
}
getStore(mode) {
// prettier-ignore
return this.dbPromise.then((db) => db
.transaction(INDEXED_DB_STORE_NAME, mode)
.objectStore(INDEXED_DB_STORE_NAME));
}
async get(key) {
const store = await this.getStore(READ_ONLY);
return new Promise((resolve, reject) => {
const req = store.get(key);
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
}
async set(key, value) {
// The reason we don't _save_ null is because IE 10 does
// not support saving the `null` type in IndexedDB. How
// ironic, given the bug below!
// See: https://github.com/mozilla/localForage/issues/161
if (value === null) {
value = undefined;
}
// NOTE: We deliberately make this operation transactionless
const store = await this.getStore(READ_WRITE);
store.put(value, key);
}
async remove(key) {
// NOTE: We deliberately make this operation transactionless
const store = await this.getStore(READ_WRITE);
store.delete(key);
}
async clear() {
// NOTE: We deliberately make this operation transactionless
const store = await this.getStore(READ_WRITE);
store.clear();
}
}
/**
* Web Storage Proxy object, which selects the best backend available
* depending on the environment.
*/
class StorageProxy {
constructor() {
this.backendPromise = (async () => {
if (Byond.TRIDENT) {
// fuckin IE users, TODO: Remove with 516
if (testIndexedDb()) {
try {
const backend = new IndexedDbBackend();
await backend.dbPromise;
return backend;
} catch {}
}
} else if (testHubStorage()) {
return new HubStorageBackend();
}
})();
}
async get(key) {
const backend = await this.backendPromise;
return backend.get(key);
}
async set(key, value) {
const backend = await this.backendPromise;
return backend.set(key, value);
}
async remove(key) {
const backend = await this.backendPromise;
return backend.remove(key);
}
async clear() {
const backend = await this.backendPromise;
return backend.clear();
}
}
export const storage = new StorageProxy();