Revert "fix(runtime): polyfill PDF.js Map APIs for older WebViews"
This reverts commit e84906652e.
This commit is contained in:
@@ -37,8 +37,6 @@ RUN node /usr/local/lib/dsh-mgr/install-office-preview.mjs
|
||||
COPY preview-download /usr/local/lib/dsh-mgr/preview-download
|
||||
COPY install-preview-download.mjs /usr/local/lib/dsh-mgr/install-preview-download.mjs
|
||||
RUN node /usr/local/lib/dsh-mgr/install-preview-download.mjs
|
||||
COPY install-webview-compat.mjs /usr/local/lib/dsh-mgr/install-webview-compat.mjs
|
||||
RUN node /usr/local/lib/dsh-mgr/install-webview-compat.mjs
|
||||
ARG RECIPE_REV=7
|
||||
LABEL dsh-mgr.role=runtime dsh-mgr.version=$DSH_VERSION dsh-mgr.recipe=$RECIPE_REV
|
||||
ENV DSH_HOME=/workspace HOME=/workspace XDG_DATA_HOME=/workspace/.local/share XDG_CONFIG_HOME=/workspace/.config
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
// Executed independently in the page and the embedded PDF worker.
|
||||
export function installMapCompat() {
|
||||
'use strict';
|
||||
const { has, get, set } = Map.prototype;
|
||||
if (typeof Map.prototype.getOrInsertComputed !== 'function') {
|
||||
Object.defineProperty(Map.prototype, 'getOrInsertComputed', {
|
||||
configurable: true, writable: true,
|
||||
value: function getOrInsertComputed(key, callback) {
|
||||
const exists = has.call(this, key);
|
||||
if (typeof callback !== 'function') throw new TypeError('callback must be callable');
|
||||
if (exists) return get.call(this, key);
|
||||
if (key === 0) key = 0; // Canonicalize -0 before invoking callback.
|
||||
const value = callback(key);
|
||||
set.call(this, key, value);
|
||||
return value;
|
||||
},
|
||||
});
|
||||
}
|
||||
if (typeof Map.prototype.getOrInsert !== 'function') {
|
||||
Object.defineProperty(Map.prototype, 'getOrInsert', {
|
||||
configurable: true, writable: true,
|
||||
value: function getOrInsert(key, value) {
|
||||
if (has.call(this, key)) return get.call(this, key);
|
||||
set.call(this, key, value);
|
||||
return value;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const newline = String.fromCharCode(10);
|
||||
export const prelude = '/* dsh-mgr: webview-map-compat */' + newline + '(' + installMapCompat.toString() + ')();' + newline;
|
||||
export const workerPrefix = 'var _dsh_pdf_worker_default = ';
|
||||
|
||||
export function patchPdfClient(source, version, pluginVersion) {
|
||||
if (version !== '0.2.1-rc.2' || pluginVersion !== version) {
|
||||
throw new Error('unsupported DSH/documentpreview version for WebView compatibility patch');
|
||||
}
|
||||
if (source.includes('dsh-mgr: webview-map-compat')) throw new Error('WebView compatibility patch already applied');
|
||||
const lines = source.split(newline);
|
||||
const targets = lines.filter(line => line.trimStart().startsWith(workerPrefix));
|
||||
if (targets.length !== 1 || !source.includes('this.#methodPromises.getOrInsertComputed(')) {
|
||||
throw new Error('expected PDF.js method cache and exactly one embedded worker');
|
||||
}
|
||||
const target = targets[0];
|
||||
// Preserve the JS string literal verbatim (it can contain non-JSON escapes).
|
||||
const worker = target.trim().slice(workerPrefix.length, -1);
|
||||
if (!worker.includes('getOrInsertComputed') || !worker.includes('pdfjsVersion = 6.3.289')) {
|
||||
throw new Error('unexpected embedded PDF.js worker');
|
||||
}
|
||||
// A page-level polyfill cannot cross into the worker realm.
|
||||
return prelude + source.replace(target, () => workerPrefix + JSON.stringify(prelude) + ' + ' + worker + ';');
|
||||
}
|
||||
|
||||
export function install(root = '/usr/local/lib/node_modules/@deepseek-ai/dsh') {
|
||||
const plugin = join(root, 'node_modules/@deepseek-ai/dsh-client-ui-sidebar-documentpreview');
|
||||
const version = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version;
|
||||
const pluginVersion = JSON.parse(readFileSync(join(plugin, 'package.json'), 'utf8')).version;
|
||||
const file = join(plugin, 'lib/client.pdf.js');
|
||||
writeFileSync(file, patchPdfClient(readFileSync(file, 'utf8'), version, pluginVersion));
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) install();
|
||||
@@ -1,83 +0,0 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import vm from 'node:vm';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { patchPdfClient, prelude, workerPrefix } from './install-webview-compat.mjs';
|
||||
|
||||
function realm() {
|
||||
const ctx = vm.createContext({ assert });
|
||||
vm.runInContext('delete Map.prototype.getOrInsert; delete Map.prototype.getOrInsertComputed;', ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
test('reproduces missing API, then caches one Promise per key', async () => {
|
||||
const ctx = realm();
|
||||
assert.throws(() => vm.runInContext('new Map().getOrInsertComputed("x", () => 1)', ctx), /not a function/);
|
||||
vm.runInContext(prelude, ctx);
|
||||
await vm.runInContext(
|
||||
'(async () => { const map = new Map(); let calls = 0; const create = key => { assert.equal(key, "metadata"); calls++; return Promise.resolve(42); }; const a = map.getOrInsertComputed("metadata", create); const b = map.getOrInsertComputed("metadata", create); assert.equal(a, b); assert.equal(await a, 42); assert.equal(calls, 1); })()', ctx);
|
||||
});
|
||||
|
||||
test('preserves falsy values, callback semantics, key identity and exceptions', () => {
|
||||
const ctx = realm();
|
||||
vm.runInContext(prelude, ctx);
|
||||
vm.runInContext(
|
||||
'const map = new Map(); for (const v of [undefined, null, false, 0, ""]) { map.set("x", v); assert.equal(map.getOrInsertComputed("x", () => { throw Error("unexpected"); }), v); assert.equal(map.getOrInsert("x", 99), v); }' +
|
||||
'assert.throws(() => map.getOrInsertComputed("x", null), /callable/); assert.throws(() => Map.prototype.getOrInsert.call({}, "x", 1));' +
|
||||
'const key = {}; assert.equal(map.getOrInsertComputed(key, k => k), key); assert.equal(map.getOrInsertComputed(NaN, () => 3), 3); assert.equal(map.getOrInsertComputed(NaN, () => 4), 3);' +
|
||||
'map.getOrInsertComputed(-0, k => { assert.equal(Object.is(k, -0), false); return 0; });' +
|
||||
'assert.throws(() => map.getOrInsertComputed("error", () => { throw Error("boom"); }), /boom/); assert.equal(map.has("error"), false);' +
|
||||
'assert.equal(map.getOrInsertComputed("reentrant", () => { map.set("reentrant", 1); return 2; }), 2); assert.equal(map.get("reentrant"), 2);' +
|
||||
'map.getOrInsertComputed("this", function () { "use strict"; assert.equal(this, undefined); });' +
|
||||
'assert.equal(Object.getOwnPropertyDescriptor(Map.prototype, "getOrInsertComputed").enumerable, false);', ctx);
|
||||
});
|
||||
|
||||
test('does not replace existing implementations and is safe to run twice', () => {
|
||||
const ctx = realm();
|
||||
vm.runInContext(prelude, ctx);
|
||||
vm.runInContext('const original = Map.prototype.getOrInsertComputed; const direct = Map.prototype.getOrInsert;', ctx);
|
||||
vm.runInContext(prelude, ctx);
|
||||
vm.runInContext('assert.equal(Map.prototype.getOrInsertComputed, original); assert.equal(Map.prototype.getOrInsert, direct);', ctx);
|
||||
});
|
||||
|
||||
const nl = String.fromCharCode(10);
|
||||
const worker = '/* pdfjsVersion = 6.3.289 */' + nl + 'globalThis.result = new Map().getOrInsertComputed("worker", () => 7);';
|
||||
const fixture = '// this.#methodPromises.getOrInsertComputed(' + nl + workerPrefix + JSON.stringify(worker) + ';';
|
||||
const patch = source => patchPdfClient(source, '0.2.1-rc.2', '0.2.1-rc.2');
|
||||
|
||||
test('patch initializes both page and isolated embedded worker before use', () => {
|
||||
const output = patch(fixture);
|
||||
const page = realm();
|
||||
vm.runInContext(output, page);
|
||||
const isolated = realm();
|
||||
vm.runInContext(page._dsh_pdf_worker_default, isolated);
|
||||
assert.equal(isolated.result, 7);
|
||||
assert.ok(output.startsWith(prelude));
|
||||
});
|
||||
|
||||
test('rejects version drift, missing/duplicate worker and repeated patch', () => {
|
||||
assert.throws(() => patchPdfClient(fixture, 'other', 'other'), /unsupported/);
|
||||
assert.throws(() => patchPdfClient(fixture, '0.2.1-rc.2', 'other'), /unsupported/);
|
||||
assert.throws(() => patch(''), /expected/);
|
||||
assert.throws(() => patch(fixture + nl + fixture), /exactly one/);
|
||||
assert.throws(() => patch(fixture.replace('6.3.289', '0.0.0')), /unexpected/);
|
||||
assert.throws(() => patch(patch(fixture)), /already applied/);
|
||||
});
|
||||
|
||||
test('actual installed PDF bundle and embedded worker patch and parse', { skip: !process.env.DSH_PDF_CLIENT }, () => {
|
||||
const source = readFileSync(process.env.DSH_PDF_CLIENT, 'utf8');
|
||||
const output = patch(source);
|
||||
const line = output.split(nl).find(line => line.startsWith(workerPrefix));
|
||||
const embedded = vm.runInNewContext(line.slice(workerPrefix.length, -1), {}, { timeout: 1000 });
|
||||
assert.ok(embedded.startsWith(prelude));
|
||||
for (const code of [output, embedded]) {
|
||||
const result = spawnSync(process.execPath, ['--input-type=module', '--check'], { input: code, encoding: 'utf8' });
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
}
|
||||
// Exercise the exact upstream cache method body with the API removed.
|
||||
const body = source.split('#cacheSimpleMethod(name, data = null) {')[1].split(nl)[1].trim();
|
||||
const ctx = realm();
|
||||
vm.runInContext(prelude, ctx);
|
||||
vm.runInContext('class Transport { #methodPromises = new Map(); calls = 0; messageHandler = { sendWithPromise: () => { this.calls++; return Promise.resolve(1); } }; cached(name, data = null) { ' + body + ' } } const t = new Transport(); assert.equal(t.cached("GetMetadata"), t.cached("GetMetadata")); assert.equal(t.calls, 1);', ctx);
|
||||
});
|
||||
Reference in New Issue
Block a user