Oregami
Repositories/oxedyne/daimond

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.
33import { open, shot } from './harness.mjs';
34
35const ok = [], bad = [];
36const check = (name, pass, detail) => {
37 (pass ? ok : bad).push(name + (detail ? ' — ' + detail : ''));
38 console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : ''));
39};
40
41const NS = 'd~typstdriver';
42const SRC = 'paper.typ';
43const PDF = 'paper.pdf';
44
45const s = await open({ name: 'typstdriver' });
46const p = s.page;
47await p.waitForTimeout(1500);
48
49const 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}));
54check('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.
61const 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});
66check('the account these claims are made about holds the typesetting pack',
67 held === false, `typst_compile locked: ${held}`);
68
69const 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});
77check('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.
83await 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
90const 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
108check('a compiled PDF is written into the workspace through the wasm',
109 wrote.error === '' && wrote.inNamespace === true,
110 wrote.error || `in ${NS}/: ${wrote.inNamespace}`);
111check("and never into another account's root",
112 wrote.atRoot === false, `at the OPFS root: ${wrote.atRoot}`);
113
114await p.evaluate(async () => {
115 const mod = await import('../pkg/oxedyne_daimond.js');
116 mod.set_account_ns('');
117});
118
119await shot(s, 'typstdriver');
120const errs = s.errs.filter(e => !/favicon|404|401|net::ERR/.test(e));
121console.log('\nconsole errors:', errs.slice(0, 4));
122await s.close();
123
124console.log(`\n${ok.length} passed, ${bad.length} failed`);
125if (bad.length) console.log('FAILED:\n ' + bad.join('\n '));
126process.exit(bad.length ? 1 : 0);