mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Embed the files Cesium's workers load in the panel's workers script
The ground-polyline worker loads approximateTerrainHeights.json itself, and a worker's requests go to the panel page's own site, where they failed with 404 and stopped rendering once ground-clamped lines such as military installations appeared. The panel build now embeds that file in the workers script, which answers the worker's request from memory.
This commit is contained in:
1 parent
d4311a9d54
commit
9e5ef78f91
3 files changed
+118
-1
No files matched your search
@@ -101,3 +101,66 @@ export function panelBuildPlugin({ outDir = PANEL_OUT_DIR } = {}) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cesium files its workers load themselves, as paths under Cesium's base.
|
||||||
|
* Workers request them from the panel page's own site, which has none of
|
||||||
|
* the app's files, so the panel build embeds them in the workers script.
|
||||||
|
*/
|
||||||
|
export const PANEL_WORKER_FILES = Object.freeze([
|
||||||
|
'Assets/approximateTerrainHeights.json',
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A script, run in each worker before Cesium's, that answers the worker's
|
||||||
|
* requests for `files` (Cesium-relative path → text) from memory and lets
|
||||||
|
* every other request through.
|
||||||
|
*/
|
||||||
|
export function workerFilesPrelude(files) {
|
||||||
|
const answer = (FILES) => {
|
||||||
|
const find = (url) => {
|
||||||
|
const path = String(url).split(/[?#]/)[0];
|
||||||
|
for (const name of Object.keys(FILES))
|
||||||
|
if (path.endsWith(`/${name}`)) return FILES[name];
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
const xhr = XMLHttpRequest.prototype;
|
||||||
|
const { open, send, setRequestHeader } = xhr;
|
||||||
|
xhr.open = function (method, url, ...rest) {
|
||||||
|
this.__gevFile = find(url);
|
||||||
|
if (this.__gevFile === null) return open.call(this, method, url, ...rest);
|
||||||
|
};
|
||||||
|
xhr.setRequestHeader = function (...args) {
|
||||||
|
if (this.__gevFile === null) return setRequestHeader.apply(this, args);
|
||||||
|
};
|
||||||
|
xhr.send = function (body) {
|
||||||
|
const text = this.__gevFile;
|
||||||
|
if (text === null || text === undefined) return send.call(this, body);
|
||||||
|
const type = this.responseType;
|
||||||
|
const response =
|
||||||
|
type === 'json'
|
||||||
|
? JSON.parse(text)
|
||||||
|
: type === 'arraybuffer'
|
||||||
|
? new TextEncoder().encode(text).buffer
|
||||||
|
: text;
|
||||||
|
const state = {
|
||||||
|
readyState: 4,
|
||||||
|
status: 200,
|
||||||
|
statusText: 'OK',
|
||||||
|
response,
|
||||||
|
responseText: typeof response === 'string' ? response : '',
|
||||||
|
getAllResponseHeaders: () => 'content-type: application/json\r\n',
|
||||||
|
getResponseHeader: (name) =>
|
||||||
|
name.toLowerCase() === 'content-type' ? 'application/json' : null,
|
||||||
|
};
|
||||||
|
for (const [name, value] of Object.entries(state))
|
||||||
|
Object.defineProperty(this, name, { configurable: true, value });
|
||||||
|
setTimeout(() => {
|
||||||
|
this.dispatchEvent(new Event('readystatechange'));
|
||||||
|
this.dispatchEvent(new ProgressEvent('load'));
|
||||||
|
this.dispatchEvent(new ProgressEvent('loadend'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return `(${answer})(${JSON.stringify(files)});\n`;
|
||||||
|
}
|
||||||
+11
-1
@@ -4,7 +4,7 @@
|
|||||||
* See build/panel.js.
|
* See build/panel.js.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { readdir, rename, rm, writeFile } from 'node:fs/promises';
|
import { readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { build as esbuild } from 'esbuild';
|
import { build as esbuild } from 'esbuild';
|
||||||
@@ -13,8 +13,10 @@ import standaloneConfig from '../server/standalone/vite.config.js';
|
|||||||
import {
|
import {
|
||||||
PANEL_BASE,
|
PANEL_BASE,
|
||||||
PANEL_OUT_DIR,
|
PANEL_OUT_DIR,
|
||||||
|
PANEL_WORKER_FILES,
|
||||||
PANEL_WORKERS_PATH,
|
PANEL_WORKERS_PATH,
|
||||||
panelBuildConfig,
|
panelBuildConfig,
|
||||||
|
workerFilesPrelude,
|
||||||
workersEntrySource,
|
workersEntrySource,
|
||||||
} from '../build/panel.js';
|
} from '../build/panel.js';
|
||||||
|
|
||||||
@@ -49,4 +51,12 @@ await esbuild({
|
|||||||
logLevel: 'warning',
|
logLevel: 'warning',
|
||||||
});
|
});
|
||||||
await rm(entry);
|
await rm(entry);
|
||||||
|
const files = {};
|
||||||
|
for (const name of PANEL_WORKER_FILES)
|
||||||
|
files[name] = await readFile(join(outDir, 'cesium', name), 'utf8');
|
||||||
|
const workers = join(outDir, PANEL_WORKERS_PATH);
|
||||||
|
await writeFile(
|
||||||
|
workers,
|
||||||
|
workerFilesPrelude(files) + (await readFile(workers, 'utf8')),
|
||||||
|
);
|
||||||
console.log(`Panel build written to ${PANEL_OUT_DIR}`);
|
console.log(`Panel build written to ${PANEL_OUT_DIR}`);
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
PANEL_WORKERS_PATH,
|
PANEL_WORKERS_PATH,
|
||||||
panelBuildConfig,
|
panelBuildConfig,
|
||||||
panelBuildPlugin,
|
panelBuildPlugin,
|
||||||
|
workerFilesPrelude,
|
||||||
workersEntrySource,
|
workersEntrySource,
|
||||||
} from '../../build/panel.js';
|
} from '../../build/panel.js';
|
||||||
|
|
||||||
@@ -81,3 +82,46 @@ test('the dev server serves the panel build and nothing outside it', async () =>
|
|||||||
await rm(root, { recursive: true, force: true });
|
await rm(root, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Cesium's workers get embedded files from memory and other requests from the network", async () => {
|
||||||
|
const sent = [];
|
||||||
|
class FakeRequest extends EventTarget {
|
||||||
|
open(method, url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
setRequestHeader() {}
|
||||||
|
send() {
|
||||||
|
sent.push(this.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const saved = {
|
||||||
|
XMLHttpRequest: globalThis.XMLHttpRequest,
|
||||||
|
ProgressEvent: globalThis.ProgressEvent,
|
||||||
|
};
|
||||||
|
globalThis.XMLHttpRequest = FakeRequest;
|
||||||
|
globalThis.ProgressEvent ??= class extends Event {};
|
||||||
|
try {
|
||||||
|
new Function(workerFilesPrelude({ 'Assets/heights.json': '{"a":1}' }))();
|
||||||
|
const embedded = new XMLHttpRequest();
|
||||||
|
embedded.open(
|
||||||
|
'GET',
|
||||||
|
'https://panel.example/panel/cesium/Assets/heights.json?v=1',
|
||||||
|
);
|
||||||
|
embedded.setRequestHeader('Accept', 'application/json');
|
||||||
|
embedded.responseType = 'text';
|
||||||
|
const loaded = new Promise((resolve) =>
|
||||||
|
embedded.addEventListener('load', resolve),
|
||||||
|
);
|
||||||
|
embedded.send();
|
||||||
|
await loaded;
|
||||||
|
assert.equal(embedded.status, 200);
|
||||||
|
assert.equal(embedded.response, '{"a":1}');
|
||||||
|
const other = new XMLHttpRequest();
|
||||||
|
other.open('GET', 'https://tiles.example/1.png');
|
||||||
|
other.send();
|
||||||
|
assert.deepEqual(sent, ['https://tiles.example/1.png']);
|
||||||
|
} finally {
|
||||||
|
globalThis.XMLHttpRequest = saved.XMLHttpRequest;
|
||||||
|
globalThis.ProgressEvent = saved.ProgressEvent;
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in new issue
Block a user