Skip to content

Commit 00e5e5c

Browse files
committed
v0.73.1: refactored cache
1 parent 46c75a0 commit 00e5e5c

File tree

8 files changed

+206
-73
lines changed

8 files changed

+206
-73
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "emulators-ui",
3-
"version": "0.73.0",
3+
"version": "0.73.1",
44
"description": "Emulators UI",
55
"main": "dist/emulators-ui.js",
66
"types": "dist/types/emulators-ui.d.ts",
@@ -50,7 +50,7 @@
5050
"core-js": "^3.19.1",
5151
"del": "^5.1.0",
5252
"element-resize-detector": "^1.2.1",
53-
"emulators": "^0.73.0",
53+
"emulators": "^0.73.1",
5454
"eslint": "^7.30.0",
5555
"eslint-config-google": "^0.14.0",
5656
"git-repo-info": "^2.1.1",

src/build.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// gulpfile.ts/wasm.ts --> generateBuildInfo
55

66
export const Build = {
7-
short: "0.72.9",
8-
version: "0.72.9 (abdf90a98a4f9ed382c5b722a42cb87d)",
9-
buildSeed: 1646465611403,
7+
short: "0.73.0",
8+
version: "0.73.0 (520ced180cb105d8c82fcbe04e3fc430)",
9+
buildSeed: 1646900945967,
1010
};

src/emulators-ui.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
2+
import { Build } from "./build";
13
import { layers } from "./dom/layers";
24
import { lifecycle } from "./dom/lifecycle";
35
import { resolveBundle } from "./network/xhr";
@@ -19,6 +21,8 @@ import { LStorage } from "./dom/storage";
1921
import { DosInstance, DosFactoryType, DosOptions } from "./js-dos";
2022

2123
export class EmulatorsUi {
24+
build = Build;
25+
2226
dom = {
2327
layers, // DOM components that used by js-dos player
2428
lifecycle, // compnent that manges liefcycle events

src/js-dos.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Emulators, CommandInterface } from "emulators";
22
import { TransportLayer } from "emulators/dist/types/protocol/protocol";
33
import { EmulatorsUi } from "./emulators-ui";
44
import { Layers, LayersOptions } from "./dom/layers";
5-
import { Build } from "./build";
65

76
import { extractLayersConfig, LegacyLayersConfig, LayersConfig } from "./controls/layers-config";
87

@@ -25,8 +24,6 @@ export interface DosOptions {
2524
}
2625

2726
export class DosInstance {
28-
static initialRun = true;
29-
3027
emulatorsUi: EmulatorsUi;
3128
emulatorFunction: EmulatorFunction;
3229
createTransportLayer?: () => TransportLayer;
@@ -50,11 +47,6 @@ export class DosInstance {
5047
private onMobileControlsChanged: (visible: boolean) => void;
5148

5249
constructor(root: HTMLDivElement, emulatorsUi: EmulatorsUi, options: DosOptions) {
53-
if (DosInstance.initialRun) {
54-
emulators.cacheSeed += " ui (" + Build.short + ")";
55-
DosInstance.initialRun = false;
56-
}
57-
5850
this.options = options;
5951
this.emulatorsUi = emulatorsUi;
6052
this.storage = emulatorsUi.dom.storage;

src/network/xhr.ts

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,47 @@
1-
import { Emulators } from "emulators";
2-
import { Cache } from "emulators/dist/types/cache";
3-
4-
declare const emulators: Emulators;
5-
61
export async function resolveBundle(url: string,
72
options?: {
8-
cache?: Cache | null,
93
httpCache?: boolean,
104
onprogress?: (progress: number) => void
115
}): Promise<Uint8Array> {
12-
const cache = options?.cache || null;
136
const onprogress = options?.onprogress;
147
const httpCache = !(options?.httpCache === false);
158

16-
try {
17-
if (cache === null) {
18-
throw new Error("no-cache");
19-
}
20-
const cacheImpl = cache || await emulators.cache();
21-
const buffer = await cacheImpl.get(url) as ArrayBuffer;
22-
if (onprogress !== undefined) {
23-
onprogress(100);
24-
}
25-
return new Uint8Array(buffer);
26-
} catch {
27-
return new Promise<Uint8Array>((resolve, reject) => {
28-
const request = new XMLHttpRequest();
29-
request.open("GET", url, true);
30-
request.overrideMimeType("text/plain; charset=x-user-defined");
31-
request.addEventListener("error", () => {
32-
reject(new Error("Network error, can't download " + url));
33-
});
34-
request.addEventListener("abort", () => {
35-
reject(new Error("Request canceled for url " + url));
36-
}, false);
37-
request.responseType = "arraybuffer";
38-
request.onreadystatechange = () => {
39-
if (request.readyState === 4) {
40-
if (request.status === 200) {
41-
if (onprogress !== undefined) {
42-
onprogress(100);
43-
}
44-
resolve(new Uint8Array(request.response));
45-
} else {
46-
reject(new Error("Network error, can't download " + url));
9+
return new Promise<Uint8Array>((resolve, reject) => {
10+
const request = new XMLHttpRequest();
11+
request.open("GET", url, true);
12+
request.overrideMimeType("text/plain; charset=x-user-defined");
13+
request.addEventListener("error", () => {
14+
reject(new Error("Network error, can't download " + url));
15+
});
16+
request.addEventListener("abort", () => {
17+
reject(new Error("Request canceled for url " + url));
18+
}, false);
19+
request.responseType = "arraybuffer";
20+
request.onreadystatechange = () => {
21+
if (request.readyState === 4) {
22+
if (request.status === 200) {
23+
if (onprogress !== undefined) {
24+
onprogress(100);
4725
}
26+
resolve(new Uint8Array(request.response));
27+
} else {
28+
reject(new Error("Network error, can't download " + url));
4829
}
49-
};
50-
if (onprogress !== undefined) {
51-
request.onprogress = (event) => {
52-
if (event.total && event.total > 0) {
53-
const porgress = Math.round(event.loaded * 10000 / event.total) / 100;
54-
onprogress(porgress);
55-
}
56-
};
5730
}
58-
if (httpCache === false) {
59-
request.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");
60-
request.setRequestHeader("Expires", "Tue, 01 Jan 1980 1:00:00 GMT");
61-
request.setRequestHeader("Pragma", "no-cache");
62-
}
63-
request.send();
64-
});
65-
}
31+
};
32+
if (onprogress !== undefined) {
33+
request.onprogress = (event) => {
34+
if (event.total && event.total > 0) {
35+
const porgress = Math.round(event.loaded * 10000 / event.total) / 100;
36+
onprogress(porgress);
37+
}
38+
};
39+
}
40+
if (httpCache === false) {
41+
request.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");
42+
request.setRequestHeader("Expires", "Tue, 01 Jan 1980 1:00:00 GMT");
43+
request.setRequestHeader("Pragma", "no-cache");
44+
}
45+
request.send();
46+
});
6647
}

src/persist/cache.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/* eslint @typescript-eslint/no-unused-vars: 0 */
2+
3+
export interface Cache {
4+
put: (key: string, data: string | ArrayBuffer) => Promise<void>;
5+
get: (key: string, defaultValue?: string | ArrayBuffer) => Promise<string | ArrayBuffer>;
6+
forEach: (each: (key: string, value: any) => void, onend: () => void) => void;
7+
close: () => void;
8+
}
9+
10+
class CacheNoop implements Cache {
11+
// eslint-disable-next-line
12+
public close() {
13+
}
14+
15+
public put(key: string, data: string | ArrayBuffer): Promise<void> {
16+
return Promise.resolve();
17+
}
18+
19+
public get(key: string, defaultValue?: string | ArrayBuffer): Promise<string | ArrayBuffer> {
20+
if (defaultValue !== undefined) {
21+
return Promise.resolve(defaultValue);
22+
}
23+
return Promise.reject(new Error("Cache is not supported on this host"));
24+
}
25+
26+
public forEach(each: (key: string, value: any) => void, onend: () => void) {
27+
onend();
28+
}
29+
}
30+
31+
export function makeCache(version: string, logger: { onErr(...args: any[]): any }): Promise<Cache> {
32+
return new Promise((resolve) => {
33+
new CacheDbImpl(version, resolve, (msg: string) => {
34+
logger.onErr(msg);
35+
resolve(new CacheNoop());
36+
});
37+
});
38+
}
39+
40+
class CacheDbImpl implements Cache {
41+
public version: string;
42+
private storeName = "files";
43+
private indexedDB: IDBFactory;
44+
private db: IDBDatabase | null = null;
45+
46+
constructor(version: string, onready: (cache: Cache) => void, onerror: (msg: string) => void) {
47+
this.version = version;
48+
this.indexedDB = (typeof window === "undefined" ? undefined : window.indexedDB ||
49+
(window as any).mozIndexedDB ||
50+
(window as any).webkitIndexedDB || (window as any).msIndexedDB) as any;
51+
52+
if (!this.indexedDB) {
53+
onerror("Indexed db is not supported on this host");
54+
return;
55+
}
56+
57+
try {
58+
const openRequest = this.indexedDB.open("js-dos-cache (" + version + ")", 1);
59+
openRequest.onerror = (event) => {
60+
onerror("Can't open cache database: " + openRequest.error?.message);
61+
};
62+
openRequest.onsuccess = (event) => {
63+
this.db = openRequest.result;
64+
onready(this);
65+
};
66+
openRequest.onupgradeneeded = (event) => {
67+
try {
68+
this.db = openRequest.result;
69+
this.db.onerror = (event) => {
70+
onerror("Can't upgrade cache database");
71+
};
72+
73+
this.db.createObjectStore(this.storeName);
74+
} catch (e) {
75+
onerror("Can't upgrade cache database");
76+
}
77+
};
78+
} catch (e: any) {
79+
onerror("Can't open cache database: " + e.message);
80+
}
81+
}
82+
83+
public close() {
84+
if (this.db !== null) {
85+
this.db.close();
86+
this.db = null;
87+
}
88+
}
89+
90+
public put(key: string, data: string | ArrayBuffer): Promise<void> {
91+
return new Promise((resolve) => {
92+
if (this.db === null) {
93+
resolve();
94+
return;
95+
}
96+
97+
const transaction = this.db.transaction(this.storeName, "readwrite");
98+
transaction.oncomplete = () => resolve();
99+
transaction.objectStore(this.storeName).put(data, key);
100+
});
101+
}
102+
103+
public get(key: string, defaultValue?: string | ArrayBuffer): Promise<string | ArrayBuffer> {
104+
return new Promise((resolve, reject) => {
105+
function rejectOrResolve(message: string) {
106+
if (defaultValue === undefined) {
107+
reject(new Error(message));
108+
} else {
109+
resolve(defaultValue);
110+
}
111+
}
112+
113+
114+
if (this.db === null) {
115+
rejectOrResolve("db is not initalized");
116+
return;
117+
}
118+
119+
const transaction = this.db.transaction(this.storeName, "readonly");
120+
const request = transaction.objectStore(this.storeName).get(key);
121+
request.onerror = () => reject(new Error("Can't read value for key '" + key + "'"));
122+
request.onsuccess = () => {
123+
if (request.result) {
124+
resolve(request.result);
125+
} else {
126+
rejectOrResolve("Result is empty for key '" + key + "', result: " + request.result);
127+
}
128+
};
129+
});
130+
}
131+
132+
public forEach(each: (key: string, value: any) => void, onend: () => void) {
133+
if (this.db === null) {
134+
onend();
135+
return;
136+
}
137+
138+
const transaction = this.db.transaction(this.storeName, "readonly");
139+
const request = transaction.objectStore(this.storeName).openCursor();
140+
request.onerror = () => onend();
141+
request.onsuccess = (event) => {
142+
const cursor = (event.target as any).result as IDBCursorWithValue;
143+
if (cursor) {
144+
each(cursor.key.toString(), cursor.value);
145+
cursor.continue();
146+
} else {
147+
onend();
148+
}
149+
};
150+
}
151+
}

src/persist/save-load.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
import { CommandInterface, Emulators } from "emulators";
22
import { Layers } from "../dom/layers";
3+
import { makeCache } from "./cache";
34

45
const cacheName = "emulators-ui-saves";
6+
const cachePromise = makeCache(cacheName, {
7+
onErr: console.error,
8+
});
9+
510

611
export function save(key: string,
712
layers: Layers,
813
ci: CommandInterface,
914
emulators: Emulators) {
1015
layers.setOnSave(async () => {
11-
const cache = await emulators.cache(cacheName);
16+
const cache = await cachePromise;
1217
const updated = await ci.persist();
1318
return cache.put(key, updated.buffer);
1419
});
1520
}
1621

1722
export async function load(key: string,
1823
emulators: Emulators) {
19-
const cache = await emulators.cache(cacheName);
24+
const cache = await cachePromise;
2025
return cache.get(key).then((buffer) => new Uint8Array(buffer as ArrayBuffer));
2126
}

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,10 +2457,10 @@ emoji-regex@^8.0.0:
24572457
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
24582458
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
24592459

2460-
emulators@^0.73.0:
2461-
version "0.73.0"
2462-
resolved "https://registry.yarnpkg.com/emulators/-/emulators-0.73.0.tgz#e3cef51700aaac1495a7656ab02009749c84bf17"
2463-
integrity sha512-gxUtw1KkgNZOXoPdt5dVDQZMakxC+iBZ2pgrGjZYa10kph08Yn4A7+6IdB69HNn2UxiPwr1JZ516viUIdCjGUw==
2460+
emulators@^0.73.1:
2461+
version "0.73.1"
2462+
resolved "https://registry.yarnpkg.com/emulators/-/emulators-0.73.1.tgz#5a4a0c681263664187a8b54301fa390006fef307"
2463+
integrity sha512-f8O13Iq2/Hy7qmHQ5m2bAZrjdcuQlRjMez0F/i8rqQqrqwyc35rqxakioWKfmIiXB6NnsbuTLsbws9icCc1+1A==
24642464

24652465
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
24662466
version "1.4.4"

0 commit comments

Comments
 (0)