diff --git a/lib/ts-types/localForage.d.ts b/lib/ts-types/localForage.d.ts index e5d42d6d..e8e6fc16 100644 --- a/lib/ts-types/localForage.d.ts +++ b/lib/ts-types/localForage.d.ts @@ -18,8 +18,8 @@ interface LocalForageOptions { } interface LocalForageDbMethods { - getItem(key: string): Promise; - getItem(key: string, callback: (err: any, value: T) => void): void; + getItem(key: string): Promise; + getItem(key: string, callback: (err: any, value: T | null) => void): void; setItem(key: string, value: T): Promise; setItem(key: string, value: T, callback: (err: any, value: T) => void): void; diff --git a/package-lock.json b/package-lock.json index cfa9ad5e..a2cbc96d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -705,9 +705,9 @@ "integrity": "sha512-81oxVi/OY+LrzgrONX7ciD1wtvq24nb2M9iYixRiQG+1hrDrDRqFVWuQzF1fUexh3Sg/ol6eMAz1MOVYFouzUg==" }, "node_modules/kaitai-struct-compiler": { - "version": "0.11.0-SNAPSHOT20231104.85023.e581718", - "resolved": "https://registry.npmjs.org/kaitai-struct-compiler/-/kaitai-struct-compiler-0.11.0-SNAPSHOT20231104.85023.e581718.tgz", - "integrity": "sha512-1i5jAfwSAxrB6HkItjdZwpOJqkcHEcoQdRhvFp1U3n5kSTtsuX/0yos+5D65GONcZkl3rf21IN4zwkSsjjMrOw==" + "version": "0.11.0-SNAPSHOT20231104.124722.3af7a08", + "resolved": "https://registry.npmjs.org/kaitai-struct-compiler/-/kaitai-struct-compiler-0.11.0-SNAPSHOT20231104.124722.3af7a08.tgz", + "integrity": "sha512-iZFRd7Md3tK8+WlD3WBmrIIPTEfmVWWMSNgfeiHOjDS8XAXZBvbvjdtkuMcUgJ1AMbZfozvKK4ecDy6x/scaig==" }, "node_modules/lie": { "version": "3.1.1", diff --git a/src/v1/KaitaiServices.ts b/src/v1/KaitaiServices.ts index 7261b040..569c6a7e 100644 --- a/src/v1/KaitaiServices.ts +++ b/src/v1/KaitaiServices.ts @@ -57,8 +57,27 @@ class JsImporter implements IYamlImporter { } console.log(`import yaml: ${name}, mode: ${mode}, loadFn: ${loadFn}, root:`, this.rootFsItem); - let ksyContent = await fss[importedFsType].get(`${loadFn}.ksy`); - var ksyModel = YAML.parse(ksyContent); + const fn = `${loadFn}.ksy`; + const sourceAppendix = mode === 'abs' ? 'kaitai.io' : 'local storage'; + let ksyContent; + try { + ksyContent = await fss[importedFsType].get(`${loadFn}.ksy`); + } catch (e) { + const error = new Error(`failed to import spec ${fn} from ${sourceAppendix}${e.message ? ': ' + e.message : ''}`); + + // The default implementation of the Error.prototype.toString() method gives + // "Error: {message}", see + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString#description + // However, the error we're throwing here goes directly into the KSC code, + // which will add its own `error: ` prefix (as the severity of the problem), + // so the resulting message would contain `error: Error: ...`. By overriding + // toString() to omit the `Error: ` part, we can make the message a bit nicer. + error.toString = function () { + return this.message; + }; + throw error; + } + const ksyModel = YAML.parse(ksyContent); return ksyModel; } } diff --git a/src/v1/app.files.ts b/src/v1/app.files.ts index 548d8e16..d50e6852 100644 --- a/src/v1/app.files.ts +++ b/src/v1/app.files.ts @@ -67,7 +67,15 @@ class LocalStorageFs implements IFileSystem { return this.save(); } - get(fn: string): Promise { return localforage.getItem(this.fileKey(fn)); } + get(fn: string): Promise { + return localforage.getItem(this.fileKey(fn)) + .then(content => { + if (content === null) { + throw new Error('file not found'); + } + return content; + }); + } put(fn: string, data: any): Promise { return this.getRootNode().then(root => { @@ -84,7 +92,25 @@ class KaitaiFs implements IFileSystem { get(fn: string): Promise { if (fn.toLowerCase().endsWith(".ksy")) - return Promise.resolve($.ajax({ url: fn })); + return fetch(fn) + .then(response => { + if (!response.ok) { + let msg; + if (response.status === 404) { + msg = 'file not found'; + } else { + const textAppendix = response.statusText ? ` (${response.statusText})` : ''; + msg = `server responded with HTTP status ${response.status}${textAppendix}`; + } + throw new Error(msg); + } + return response.text(); + }, err => { + if (err instanceof TypeError) { + throw new Error(`cannot reach the server (message: ${err.message}), check your internet connection`); + } + throw err; + }); else return downloadFile(fn); } diff --git a/src/v1/app.layout.ts b/src/v1/app.layout.ts index 445e18f3..570f8c9f 100644 --- a/src/v1/app.layout.ts +++ b/src/v1/app.layout.ts @@ -121,7 +121,8 @@ export class UI { this.layout.addEditor("genCodeDebugViewer", "javascript", false); this.layout.addComponent("hexViewer", () => { var hexViewer = new HexViewer("#hexViewer"); - hexViewer.bytesPerLine = parseInt(localStorage.getItem("HexViewer.bytesPerLine")) || 16; + const stored = localStorage.getItem("HexViewer.bytesPerLine"); + hexViewer.bytesPerLine = (stored !== null && parseInt(stored, 10)) || 16; return hexViewer; }); this.layout.addComponent("errorWindow", cont => { cont.getElement().append($("
")); }); @@ -132,4 +133,4 @@ export class UI { } init() { this.layout.layout.init(); } -} \ No newline at end of file +} diff --git a/src/v1/app.ts b/src/v1/app.ts index 6d7669bb..a24122e7 100644 --- a/src/v1/app.ts +++ b/src/v1/app.ts @@ -279,7 +279,8 @@ $(() => { app.formatReady = loadCachedFsItem(app.ksyFsItemName, "kaitai", "formats/archive/zip.ksy"); app.inputReady.then(() => { - var storedSelection = JSON.parse(localStorage.getItem("selection")); + const value = localStorage.getItem("selection"); + const storedSelection = value !== null ? JSON.parse(value) : null; if (storedSelection) app.ui.hexViewer.setSelection(storedSelection.start, storedSelection.end); }); diff --git a/tsconfig.json b/tsconfig.json index a9b4ad52..b4fa3b73 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "alwaysStrict": true, +// "strictNullChecks": true, "sourceMap": false, "target": "es2017", "outDir": "js/",