// Makes occt-import-js 0.0.23's JavaScript glue run under a Content Security // Policy without 'unsafe-eval', and writes it to vendor/local/occt@1/. // // node wasm-src/occt/patch.mjs (from frontend/) // // The glue is Emscripten output. Its embind layer builds each wrapper between // JavaScript and C++ by generating source text and compiling it with // new Function (craftInvokerFunction + createJsInvoker), which the site's CSP // forbids. This replaces craftInvokerFunction with a closure that performs the // same steps in the same order - wire the arguments, call into WebAssembly, // run destructors, convert the result - as Emscripten itself does when built // with -sDYNAMIC_EXECUTION=0. Nothing else changes; the .wasm is used as // published on npm. import { createHash } from 'node:crypto'; import { mkdirSync, writeFileSync, copyFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const here = dirname(fileURLToPath(import.meta.url)); const out = join(here, '..', '..', 'vendor', 'local', 'occt@1'); const SOURCE = 'https://cdn.jsdelivr.net/npm/occt-import-js@0.0.23/dist/occt-import-js.js'; // The same hash vendor.lock.json held for the unmodified file. const SHA384 = 'Z8pO9JEhFZF+mvDKxMuLvPT85VP7nly8CqYjBatl0bMeW9OsaKCes4gEUPL4nihc'; const original = Buffer.from(await (await fetch(SOURCE)).arrayBuffer()); const got = createHash('sha384').update(original).digest('base64'); if (got !== SHA384) throw new Error(`occt-import-js.js is not the expected file (sha384 ${got})`); let js = original.toString('utf8'); const start = js.indexOf('function craftInvokerFunction('); if (start < 0) throw new Error('craftInvokerFunction not found'); let depth = 0; let end = js.indexOf('{', start); for (; end < js.length; end++) { if (js[end] === '{') depth++; else if (js[end] === '}' && --depth === 0) break; } const before = js.slice(start, end + 1); if (!before.includes('newFunc(Function,args)')) throw new Error('craftInvokerFunction is not the code-generating version'); const replacement = `function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc,isAsync){ /* Patched by johnmcm.com: no generated code (see wasm-src/occt/patch.mjs). */ var argCount=argTypes.length; if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")} var isClassMethodFunc=argTypes[1]!==null&&classType!==null; var needsDestructorStack=usesDestructorStack(argTypes); var returns=argTypes[0].name!=="void"; var expected=argCount-2; var first=isClassMethodFunc?2:1; var invokerFn=function(...args){ var destructors=needsDestructorStack?[]:null; var wired=[cppTargetFunc]; if(isClassMethodFunc){wired.push(argTypes[1]["toWireType"](destructors,this))} for(var i=0;i{ /* Patched by johnmcm.com: no generated code (see wasm-src/occt/patch.mjs). */ var types=emval_lookupTypes(argCount,argTypes);var retType=types.shift();argCount--; var offsets=[];var offset=0; for(var i=0;it.name).join(", ")}) => \${retType.name}>\`; return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction)) };`; js = js.slice(0, mcStart) + methodCaller + js.slice(mcEnd); // createJsInvoker and newFunc are left in place but are no longer called. const callers = js.match(/newFunc\(Function|new Function|\beval\(/g); if (callers) throw new Error(`string evaluation left in the glue: ${callers.join(', ')}`); const header = '/* occt-import-js 0.0.23 (LGPL-2.1), patched by johnmcm.com to run without eval: see https://johnmcm.com/source/occt/ */\n'; mkdirSync(out, { recursive: true }); writeFileSync(join(out, 'occt-import-js.js'), header + js); copyFileSync(join(here, 'LICENSE'), join(out, 'LICENSE')); console.log(`wrote ${join(out, 'occt-import-js.js')}`);