Import the 2026-10-09 source release, Fastify/SQLite backend, restored source assets, and analytics-enabled server build. Replace files from the previous release while retaining the existing repository history. Make builds portable across Windows and Linux, configure local startup, normalize Windows static asset paths, and fix test fixture cleanup and file URL handling. Exclude local environment settings, databases, dependencies, validation output, and TypeScript build cache from version control. Validation: npm ci, static and server builds, 106 arrival tests, and actual local HTTP checks passed. The full suite passed 467 of 470 tests; three tests still require QA fixtures omitted from the supplied release, documented in README-서버실행.md.
54 lines
3.7 KiB
JavaScript
54 lines
3.7 KiB
JavaScript
/** Production worker + real ORT-Web WASM under Node. Canvas supplies retained
|
|
* local RGB fixture pixels; this is not browser, GPU, CDN or mobile validation. */
|
|
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { readFile } from 'node:fs/promises';
|
|
import vm from 'node:vm';
|
|
import ts from 'typescript';
|
|
import * as ort from 'onnxruntime-web/webgpu';
|
|
import { webcrypto } from 'node:crypto';
|
|
import { loadTypescript } from './support/load-typescript.mjs';
|
|
|
|
test('production worker executes the retained local model and releases its input', { timeout: 30000 }, async () => {
|
|
const mask = await loadTypescript(new URL('../src/cutout/CutoutMask.ts', import.meta.url));
|
|
const preprocess = await loadTypescript(new URL('../src/cutout/U2NetPreprocess.ts', import.meta.url));
|
|
const model = await readFile(new URL('../public/models/u2netp/model.onnx', import.meta.url));
|
|
const rgba = await readFile(new URL('../docs/qa-countryside-cutout-2026-10-07/runtime/sample-320.rgba', import.meta.url));
|
|
// Only the host boundary is changed. The model and all preprocess, inference,
|
|
// normalization and worker cleanup logic are the actual production code.
|
|
ort.env.wasm.wasmPaths = new URL('../node_modules/onnxruntime-web/dist/', import.meta.url).href;
|
|
ort.env.wasm.numThreads = 1;
|
|
const wasm = new Proxy(ort.env.wasm, { set(target, key, value) { if (key !== 'wasmPaths') target[key] = value; return true; } });
|
|
const runtime = { ...ort, env: { ...ort.env, wasm } };
|
|
let listener, complete;
|
|
const terminal = new Promise(resolve => { complete = resolve; });
|
|
const messages = [], requested = [];
|
|
const image = { width: 415, height: 739, closes: 0, close() { this.closes++; } };
|
|
const source = await readFile(new URL('../src/cutout/cutout.worker.ts', import.meta.url), 'utf8');
|
|
vm.runInNewContext(ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS } }).outputText, {
|
|
exports: {}, require: name => name === 'onnxruntime-web/webgpu' ? runtime : name === './U2NetPreprocess' ? preprocess : mask,
|
|
performance, crypto: webcrypto, navigator: {},
|
|
fetch: async (url, options) => { requested.push({ url, options }); return { ok: true, arrayBuffer: async () => model.buffer.slice(model.byteOffset, model.byteOffset + model.byteLength) }; },
|
|
OffscreenCanvas: class { constructor(width, height) { assert.equal(width, 320); assert.equal(height, 320); } getContext() { return { drawImage(input) { assert.equal(input, image); }, getImageData() { return { data: new Uint8ClampedArray(rgba) }; } }; } },
|
|
self: { addEventListener(_type, fn) { listener = fn; }, postMessage(message) { messages.push(message); if (['result', 'error', 'fallback'].includes(message.type)) complete(message); } },
|
|
});
|
|
listener({ data: { type: 'infer', id: 17, image, forceWasm: true } });
|
|
const result = await terminal;
|
|
await new Promise(resolve => setImmediate(resolve));
|
|
assert.equal(result.type, 'result', result.message);
|
|
assert.equal(result.id, 17);
|
|
assert.equal(result.width, 415);
|
|
assert.equal(result.height, 739);
|
|
assert.equal(result.run.backend, 'wasm');
|
|
assert.equal(result.run.downloadBytes, 4574861);
|
|
const alpha = new Uint8Array(result.alpha), analysis = mask.analyzeCutout(alpha, result.width, result.height);
|
|
assert.equal(alpha.length, 415 * 739);
|
|
assert.ok(alpha.some(value => value > 0 && value < 255), 'soft fringe is retained');
|
|
assert.ok(analysis.bounds.y0 > 0 && analysis.bounds.y1 < result.height);
|
|
assert.equal(image.closes, 1);
|
|
assert.equal(requested.length, 1);
|
|
assert.equal(requested[0].url, '/models/u2netp/model.onnx');
|
|
assert.equal(requested[0].options.body, undefined);
|
|
assert.equal(messages.filter(message => message.type === 'status').length, 2);
|
|
});
|