oxedyne/daimond/dev/verify_typstdriver.mjs
6.3 KiB, 1 run
created by r2519314175:771, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | // verify_typstdriver.mjs — the Typst tool can be reached, and what it writes lands in the right root. |
| 2 | // |
| 3 | // The compiler was wired to a human's Compile button and to nothing else. `window.DaimondTypst` is |
| 4 | // the ONE object the Rust `typst_compile` tool looks for, and it is installed by a MODULE -- so |
| 5 | // until something imported that module the object did not exist, the tool was in the belt and |
| 6 | // could never work, and a model asked to produce a PDF correctly reported that it could not. |
| 7 | // |
| 8 | // Three claims: |
| 9 | // |
| 10 | // 1. The driver is there at boot, with nothing having opened a .typ file. That is the whole of |
| 11 | // what the tool needs, and it costs only the ~4 KB module: the 30 MB compiler wasm is still |
| 12 | // built lazily, on the first actual compile. |
| 13 | // 2. It really compiles. A driver that installs and then fails is worse than none, because the |
| 14 | // tool is now advertised. (This one is slow -- it builds the compiler -- and it is the check |
| 15 | // that would have caught the local MIME bug that made every compile fail as "compiler failed |
| 16 | // to load".) |
| 17 | // 3. The bytes go to the workspace through the WASM write, which is what applies the path jail, |
| 18 | // the real-folder override and the per-account namespace. Asserted by setting a namespace and |
| 19 | // finding the PDF inside it rather than at the origin root. |
| 20 | // |
| 21 | // WHAT CHANGED WHEN TYPESETTING WAS SOLD. Claims 2 and 3 used to prove the compile worked FOR |
| 22 | // EVERYONE, which is no longer what the app claims: typesetting is a pack now, and an account that |
| 23 | // has not bought it is refused at the driver. Read literally, this file would have gone red on a |
| 24 | // correct build -- and the tempting fix, deleting the compiles, would have thrown away the only |
| 25 | // check that the compiler works at all. |
| 26 | // |
| 27 | // So the three claims stand, and the account they are made about is stated instead of assumed: |
| 28 | // each compile below first makes sure the pack is HELD, which is the condition under which "it |
| 29 | // compiles" was ever true. The refusal is not tested here. It has a file of its own, |
| 30 | // `dev/verify_typstpack.mjs`, which drives all three doors into the compiler; this one goes on |
| 31 | // answering the question it was written for -- is the compiler reachable and does it write to the |
| 32 | // right place -- for the customer who is entitled to an answer. |
| 33 | import { open, shot } from './harness.mjs'; |
| 34 | |
| 35 | const ok = [], bad = []; |
| 36 | const check = (name, pass, detail) => { |
| 37 | (pass ? ok : bad).push(name + (detail ? ' — ' + detail : '')); |
| 38 | console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : '')); |
| 39 | }; |
| 40 | |
| 41 | const NS = 'd~typstdriver'; |
| 42 | const SRC = 'paper.typ'; |
| 43 | const PDF = 'paper.pdf'; |
| 44 | |
| 45 | const s = await open({ name: 'typstdriver' }); |
| 46 | const p = s.page; |
| 47 | await p.waitForTimeout(1500); |
| 48 | |
| 49 | const atBoot = await p.evaluate(() => ({ |
| 50 | installed: !!(window.DaimondTypst && typeof window.DaimondTypst.compile === 'function'), |
| 51 | // Nothing has opened a .typ file, so if the object is here it was put here at boot. |
| 52 | opened: !!document.querySelector('.files-view'), |
| 53 | })); |
| 54 | check('the Typst driver is installed at boot, with no file opened', |
| 55 | atBoot.installed === true && atBoot.opened === false, |
| 56 | `installed: ${atBoot.installed}, a file view open: ${atBoot.opened}`); |
| 57 | |
| 58 | // The compiles below are the ENTITLED account's, so say so rather than relying on a fresh profile |
| 59 | // happening to have been told nothing. Stated once, and asserted, so this file cannot quietly |
| 60 | // become a test of the refusal and go on calling itself a test of the compiler. |
| 61 | const held = await p.evaluate(async () => { |
| 62 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 63 | mod.set_locked_packs(''); |
| 64 | return mod.tool_locked('typst_compile'); |
| 65 | }); |
| 66 | check('the account these claims are made about holds the typesetting pack', |
| 67 | held === false, `typst_compile locked: ${held}`); |
| 68 | |
| 69 | const built = await p.evaluate(async () => { |
| 70 | const r = await window.DaimondTypst.compile('= A heading\n\nA paragraph, and $x^2 + y^2$.\n'); |
| 71 | return { |
| 72 | error: r.error || '', |
| 73 | bytes: r.pdf ? r.pdf.length : 0, |
| 74 | magic: r.pdf ? String.fromCharCode.apply(null, Array.from(r.pdf.slice(0, 5))) : '', |
| 75 | }; |
| 76 | }); |
| 77 | check('and it compiles a real document to real PDF bytes', |
| 78 | built.error === '' && built.magic === '%PDF-' && built.bytes > 1000, |
| 79 | built.error || `${built.bytes} bytes, magic "${built.magic}"`); |
| 80 | |
| 81 | // The tool's own round trip: source in the workspace, compiled, written back beside it -- under an |
| 82 | // account namespace, which is the thing a hand-rolled OPFS walk in the page could never honour. |
| 83 | await p.evaluate(async ({ ns, src }) => { |
| 84 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 85 | mod.set_account_ns(ns); |
| 86 | const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true); |
| 87 | await app.run_tool('file_write', JSON.stringify({ path: src, content: '= Written by the tool\n' })); |
| 88 | }, { ns: NS, src: SRC }); |
| 89 | |
| 90 | const wrote = await p.evaluate(async ({ ns, src, pdf }) => { |
| 91 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 92 | const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true); |
| 93 | const text = await app.run_tool('file_read', JSON.stringify({ path: src })); |
| 94 | const out = await window.DaimondTypst.compile(text); |
| 95 | if (out.error) return { error: out.error }; |
| 96 | await app.write_bytes(pdf, out.pdf); |
| 97 | const root = await navigator.storage.getDirectory(); |
| 98 | const at = async (d, n) => { try { await d.getFileHandle(n); return true; } catch (e) { return false; } }; |
| 99 | let sub = null; |
| 100 | try { sub = await root.getDirectoryHandle(ns); } catch (e) { sub = null; } |
| 101 | return { |
| 102 | error: '', |
| 103 | inNamespace: sub ? await at(sub, pdf) : false, |
| 104 | atRoot: await at(root, pdf), |
| 105 | }; |
| 106 | }, { ns: NS, src: SRC, pdf: PDF }); |
| 107 | |
| 108 | check('a compiled PDF is written into the workspace through the wasm', |
| 109 | wrote.error === '' && wrote.inNamespace === true, |
| 110 | wrote.error || `in ${NS}/: ${wrote.inNamespace}`); |
| 111 | check("and never into another account's root", |
| 112 | wrote.atRoot === false, `at the OPFS root: ${wrote.atRoot}`); |
| 113 | |
| 114 | await p.evaluate(async () => { |
| 115 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 116 | mod.set_account_ns(''); |
| 117 | }); |
| 118 | |
| 119 | await shot(s, 'typstdriver'); |
| 120 | const errs = s.errs.filter(e => !/favicon|404|401|net::ERR/.test(e)); |
| 121 | console.log('\nconsole errors:', errs.slice(0, 4)); |
| 122 | await s.close(); |
| 123 | |
| 124 | console.log(`\n${ok.length} passed, ${bad.length} failed`); |
| 125 | if (bad.length) console.log('FAILED:\n ' + bad.join('\n ')); |
| 126 | process.exit(bad.length ? 1 : 0); |