loader.mjs raw
1 // WASM Loader — instantiates a Moxie WASM binary with all bridge imports.
2 // Reuses existing jsruntime modules for browser API surface.
3
4 import * as dom from './dom.mjs';
5 import * as webgl from './webgl.mjs';
6 import * as app from './app.mjs';
7 import * as text from './text.mjs';
8 import * as localstorage from './localstorage.mjs';
9 import * as ws from './ws.mjs';
10 import * as schnorr from './schnorr.mjs';
11 import * as ed25519 from './ed25519.mjs';
12 import * as x25519 from './x25519.mjs';
13 import * as p256 from './p256.mjs';
14 import * as poly1305 from './poly1305.mjs';
15 import * as crypto from './crypto.mjs';
16 import * as subtle from './subtle.mjs';
17 import * as signer from './signer.mjs';
18 import * as ext from './ext.mjs';
19 import * as idb from './idb.mjs';
20 import * as sw from './sw.mjs';
21 import * as markdown from './markdown.mjs';
22 import * as node from './node.mjs';
23
24 let memory;
25 let instance;
26 const encoder = new TextEncoder();
27 const decoder = new TextDecoder();
28
29 function readStr(ptr, len) {
30 if (len <= 0) return '';
31 return decoder.decode(new Uint8Array(memory.buffer, ptr, len));
32 }
33
34 function readBytes(ptr, len) {
35 if (len <= 0) return new Uint8Array(0);
36 return new Uint8Array(memory.buffer, ptr, len).slice();
37 }
38
39 function writeStr(s) {
40 const buf = encoder.encode(s);
41 const ptr = instance.exports.__alloc(buf.length);
42 new Uint8Array(memory.buffer, ptr, buf.length).set(buf);
43 return { ptr, len: buf.length };
44 }
45
46 function writeBytes(b) {
47 if (!b || b.length === 0) return { ptr: 0, len: 0 };
48 const ptr = instance.exports.__alloc(b.length);
49 new Uint8Array(memory.buffer, ptr, b.length).set(b instanceof Uint8Array ? b : new Uint8Array(b));
50 return { ptr, len: b.length };
51 }
52
53 function setOutStr(outPtr, outLen, s) {
54 const { ptr, len } = writeStr(s);
55 const dv = new DataView(memory.buffer);
56 dv.setInt32(outPtr, ptr, true);
57 dv.setInt32(outLen, len, true);
58 }
59
60 function setOutBytes(outPtr, outLen, b) {
61 const { ptr, len } = writeBytes(b);
62 const dv = new DataView(memory.buffer);
63 dv.setInt32(outPtr, ptr, true);
64 dv.setInt32(outLen, len, true);
65 }
66
67 function setOutI32(addr, val) {
68 new DataView(memory.buffer).setInt32(addr, val, true);
69 }
70
71 // Callback dispatch — always deferred via queueMicrotask.
72 function cb0(id) { queueMicrotask(() => instance.exports.__cb0(id)); }
73 function cbs(id, s) {
74 queueMicrotask(() => {
75 const { ptr, len } = writeStr(s);
76 instance.exports.__cbs(id, ptr, len);
77 });
78 }
79 function cbb(id, v) { queueMicrotask(() => instance.exports.__cbb(id, v ? 1 : 0)); }
80 function cbi(id, v) { queueMicrotask(() => instance.exports.__cbi(id, v | 0)); }
81 function cbis(id, i, s) {
82 queueMicrotask(() => {
83 const { ptr, len } = writeStr(s);
84 instance.exports.__cbis(id, i | 0, ptr, len);
85 });
86 }
87 function cbiis(id, a, b, s) {
88 queueMicrotask(() => {
89 const { ptr, len } = writeStr(s);
90 instance.exports.__cbiis(id, a | 0, b | 0, ptr, len);
91 });
92 }
93 function cbss(id, s1, s2) {
94 queueMicrotask(() => {
95 const a = writeStr(s1);
96 const b = writeStr(s2);
97 instance.exports.__cbss(id, a.ptr, a.len, b.ptr, b.len);
98 });
99 }
100 function cb6i(id, a, b, c, d, e, f) {
101 queueMicrotask(() => instance.exports.__cb6i(id, a|0, b|0, c|0, d|0, e|0, f|0));
102 }
103 function cbii(id, a, b) { queueMicrotask(() => instance.exports.__cbii(id, a|0, b|0)); }
104 function cbiii(id, a, b, c) { queueMicrotask(() => instance.exports.__cbiii(id, a|0, b|0, c|0)); }
105 function cbdata(id, bytes) {
106 queueMicrotask(() => {
107 const { ptr, len } = writeBytes(bytes);
108 instance.exports.__cbdata(id, ptr, len);
109 });
110 }
111
112 // Parse null-separated string list.
113 function splitNull(ptr, len, count) {
114 if (count <= 0) return [];
115 const s = readStr(ptr, len);
116 return s.split('\0');
117 }
118
119 // Track JS wrappers for WASM event listeners so RemoveEventListener works.
120 const jsListeners = new Map();
121 function listenerKey(elId, event, cbID) { return `${elId}:${event}:${cbID}`; }
122
123 // Hook response tracking for nested callbacks.
124 const hookResponses = new Map();
125 let hookNextReq = 1;
126
127 function buildBridge() {
128 return {
129 // --- localstorage ---
130 localstorage_get_item(kp, kl, _kc, op, ol) {
131 setOutStr(op, ol, localstorage.GetItem(readStr(kp, kl)));
132 },
133 localstorage_set_item(kp, kl, _kc, vp, vl, _vc) {
134 localstorage.SetItem(readStr(kp, kl), readStr(vp, vl));
135 },
136 localstorage_remove_item(kp, kl, _kc) {
137 localstorage.RemoveItem(readStr(kp, kl));
138 },
139
140 // --- crypto ---
141 crypto_pubkey_from_seckey(p, l, _c, op, ol) {
142 setOutBytes(op, ol, crypto.PubKeyFromSecKey(readBytes(p, l)));
143 },
144 crypto_sign_schnorr(skp, skl, _skc, mp, ml, _mc, ap, al, _ac, op, ol) {
145 setOutBytes(op, ol, crypto.SignSchnorr(readBytes(skp, skl), readBytes(mp, ml), readBytes(ap, al)));
146 },
147 crypto_verify_schnorr(pkp, pkl, _pkc, mp, ml, _mc, sp, sl, _sc) {
148 return crypto.VerifySchnorr(readBytes(pkp, pkl), readBytes(mp, ml), readBytes(sp, sl)) ? 1 : 0;
149 },
150
151 // --- schnorr ---
152 schnorr_pubkey_from_seckey(p, l, _c, op, ol, ok) {
153 try {
154 const r = schnorr.PubKeyFromSecKey(readBytes(p, l));
155 setOutBytes(op, ol, r); setOutI32(ok, r ? 1 : 0);
156 } catch { setOutI32(ok, 0); }
157 },
158 schnorr_sign(skp, skl, _skc, mp, ml, _mc, ap, al, _ac, op, ol, ok) {
159 try {
160 const r = schnorr.SignSchnorr(readBytes(skp, skl), readBytes(mp, ml), readBytes(ap, al));
161 setOutBytes(op, ol, r); setOutI32(ok, r ? 1 : 0);
162 } catch { setOutI32(ok, 0); }
163 },
164 schnorr_verify(pkp, pkl, _pkc, mp, ml, _mc, sp, sl, _sc) {
165 return schnorr.VerifySchnorr(readBytes(pkp, pkl), readBytes(mp, ml), readBytes(sp, sl)) ? 1 : 0;
166 },
167 schnorr_ecdh(skp, skl, _skc, pkp, pkl, _pkc, op, ol, ok) {
168 try {
169 const r = schnorr.ECDH(readBytes(skp, skl), readBytes(pkp, pkl));
170 setOutBytes(op, ol, r); setOutI32(ok, r ? 1 : 0);
171 } catch { setOutI32(ok, 0); }
172 },
173 schnorr_sha256sum(p, l, _c, op, ol) {
174 setOutBytes(op, ol, schnorr.SHA256Sum(readBytes(p, l)));
175 },
176 schnorr_scalar_add_mod_n(ap, al, _ac, bp, bl, _bc, op, ol, ok) {
177 try {
178 const r = schnorr.ScalarAddModN(readBytes(ap, al), readBytes(bp, bl));
179 setOutBytes(op, ol, r); setOutI32(ok, r ? 1 : 0);
180 } catch { setOutI32(ok, 0); }
181 },
182 schnorr_compressed_pubkey(p, l, _c, op, ol, ok) {
183 try {
184 const r = schnorr.CompressedPubKey(readBytes(p, l));
185 setOutBytes(op, ol, r); setOutI32(ok, r ? 1 : 0);
186 } catch { setOutI32(ok, 0); }
187 },
188
189 // --- ed25519 ---
190 ed25519_new_key_from_seed(p, l, _c, op, ol) {
191 setOutBytes(op, ol, ed25519.NewKeyFromSeed(readBytes(p, l)));
192 },
193 ed25519_sign(sp, sl, _sc, mp, ml, _mc, op, ol) {
194 setOutBytes(op, ol, ed25519.Sign(readBytes(sp, sl), readBytes(mp, ml)));
195 },
196 ed25519_verify(pkp, pkl, _pkc, mp, ml, _mc, sp, sl, _sc) {
197 return ed25519.Verify(readBytes(pkp, pkl), readBytes(mp, ml), readBytes(sp, sl)) ? 1 : 0;
198 },
199
200 // --- x25519 ---
201 x25519_scalar_mult(sp, sl, _sc, pp, pl, _pc, op, ol) {
202 setOutBytes(op, ol, x25519.ScalarMult(readBytes(sp, sl), readBytes(pp, pl)));
203 },
204 x25519_scalar_base_mult(sp, sl, _sc, op, ol) {
205 setOutBytes(op, ol, x25519.ScalarBaseMult(readBytes(sp, sl)));
206 },
207
208 // --- p256 ---
209 p256_scalar_base_mult(p, l, _c, op, ol) {
210 setOutBytes(op, ol, p256.ScalarBaseMult(readBytes(p, l)));
211 },
212 p256_scalar_mult(sp, sl, _sc, pp, pl, _pc, op, ol) {
213 setOutBytes(op, ol, p256.ScalarMult(readBytes(sp, sl), readBytes(pp, pl)));
214 },
215 p256_sign(sp, sl, _sc, mp, ml, _mc, op, ol) {
216 setOutBytes(op, ol, p256.Sign(readBytes(sp, sl), readBytes(mp, ml)));
217 },
218 p256_verify(pkp, pkl, _pkc, mp, ml, _mc, sp, sl, _sc) {
219 return p256.Verify(readBytes(pkp, pkl), readBytes(mp, ml), readBytes(sp, sl)) ? 1 : 0;
220 },
221 p256_reduce_scalar(p, l, _c, op, ol) {
222 setOutBytes(op, ol, p256.ReduceScalar(readBytes(p, l)));
223 },
224 p256_valid_scalar(p, l, _c) {
225 return p256.ValidScalar(readBytes(p, l)) ? 1 : 0;
226 },
227
228 // --- poly1305 ---
229 poly1305_mac(kp, kl, _kc, dp, dl, _dc, op, ol) {
230 setOutBytes(op, ol, poly1305.MAC(readBytes(kp, kl), readBytes(dp, dl)));
231 },
232 poly1305_verify(kp, kl, _kc, dp, dl, _dc, tp, tl, _tc) {
233 return poly1305.Verify(readBytes(kp, kl), readBytes(dp, dl), readBytes(tp, tl)) ? 1 : 0;
234 },
235
236 // --- markdown ---
237 markdown_render(p, l, _c, op, ol) {
238 setOutStr(op, ol, markdown.Render(readStr(p, l)));
239 },
240
241 // --- dom ---
242 dom_body() { return dom.Body(); },
243 dom_create_element(p, l, _c) { return dom.CreateElement(readStr(p, l)); },
244 dom_create_text_node(p, l, _c) { return dom.CreateTextNode(readStr(p, l)); },
245 dom_get_element_by_id(p, l, _c) { return dom.GetElementById(readStr(p, l)); },
246 dom_query_selector(p, l, _c) { return dom.QuerySelector(readStr(p, l)); },
247 dom_query_selector_from(root, p, l, _c) { return dom.QuerySelectorFrom(root, readStr(p, l)); },
248 dom_append_child(parent, child) { dom.AppendChild(parent, child); },
249 dom_remove_child(parent, child) { dom.RemoveChild(parent, child); },
250 dom_remove(el) { dom.Remove(el); },
251 dom_insert_before(parent, nc, rc) { dom.InsertBefore(parent, nc, rc); },
252 dom_first_child(el) { return dom.FirstChild(el); },
253 dom_first_element_child(el) { return dom.FirstElementChild(el); },
254 dom_next_sibling(el) { return dom.NextSibling(el); },
255 dom_release_element(el) { dom.ReleaseElement(el); },
256 dom_set_attribute(el, np, nl, _nc, vp, vl, _vc) {
257 dom.SetAttribute(el, readStr(np, nl), readStr(vp, vl));
258 },
259 dom_remove_attribute(el, np, nl, _nc) { dom.RemoveAttribute(el, readStr(np, nl)); },
260 dom_get_attribute(el, np, nl, _nc, op, ol) { setOutStr(op, ol, dom.GetAttribute(el, readStr(np, nl))); },
261 dom_set_text_content(el, p, l, _c) { dom.SetTextContent(el, readStr(p, l)); },
262 dom_set_inner_html(el, p, l, _c) { dom.SetInnerHTML(el, readStr(p, l)); },
263 dom_set_style(el, pp, pl, _pc, vp, vl, _vc) {
264 dom.SetStyle(el, readStr(pp, pl), readStr(vp, vl));
265 },
266 dom_add_class(el, p, l, _c) { dom.AddClass(el, readStr(p, l)); },
267 dom_remove_class(el, p, l, _c) { dom.RemoveClass(el, readStr(p, l)); },
268 dom_focus(el) { dom.Focus(el); },
269 dom_set_property(el, pp, pl, _pc, vp, vl, _vc) {
270 dom.SetProperty(el, readStr(pp, pl), readStr(vp, vl));
271 },
272 dom_get_property(el, pp, pl, _pc, op, ol) {
273 setOutStr(op, ol, dom.GetProperty(el, readStr(pp, pl)));
274 },
275 dom_bounding_client_left(el) { return dom.BoundingClientLeft(el); },
276 dom_get_bounding_rect(el, cbID) {
277 const r = dom.GetBoundingRect_sync ? dom.GetBoundingRect_sync(el) : null;
278 if (r) { cb6i(cbID, r.left|0, r.top|0, r.right|0, r.bottom|0, r.width|0, r.height|0); }
279 else { dom.GetBoundingRect(el, (l,t,r,b,w,h) => cb6i(cbID, l,t,r,b,w,h)); }
280 },
281 dom_get_viewport_height() { return dom.GetViewportHeight(); },
282 dom_get_viewport_width() { return dom.GetViewportWidth(); },
283 dom_add_event_listener(elId, ep, el2, _ec, cbID) {
284 const el = dom.getElement(elId);
285 if (!el) return;
286 const event = readStr(ep, el2);
287 const fn = () => cb0(cbID);
288 el.addEventListener(event, fn);
289 jsListeners.set(listenerKey(elId, event, cbID), fn);
290 },
291 dom_add_self_event_listener(elId, ep, el2, _ec, cbID) {
292 const el = dom.getElement(elId);
293 if (!el) return;
294 const event = readStr(ep, el2);
295 const fn = (e) => { if (e.target === e.currentTarget) cb0(cbID); };
296 el.addEventListener(event, fn);
297 jsListeners.set(listenerKey(elId, event, cbID), fn);
298 },
299 dom_add_enter_key_listener(elId, cbID) {
300 const el = dom.getElement(elId);
301 if (!el) return;
302 const fn = (e) => { if (e.key === 'Enter') { e.preventDefault(); cb0(cbID); } };
303 el.addEventListener('keydown', fn);
304 jsListeners.set(listenerKey(elId, 'keydown:enter', cbID), fn);
305 },
306 dom_remove_event_listener(elId, ep, el2, _ec, cbID) {
307 const el = dom.getElement(elId);
308 if (!el) return;
309 const event = readStr(ep, el2);
310 const key = listenerKey(elId, event, cbID);
311 const fn = jsListeners.get(key);
312 if (fn) { el.removeEventListener(event, fn); jsListeners.delete(key); }
313 },
314 dom_request_animation_frame(cbID) { requestAnimationFrame(() => cb0(cbID)); },
315 dom_observe_resize(elId, cbID) { dom.ObserveResize(elId, cbID); },
316 dom_unobserve_resize(elId) { dom.UnobserveResize(elId); },
317 dom_on_keydown(el, handlerID) {
318 const element = dom.getElement(el);
319 if (!element) return;
320 element.addEventListener('keydown', (e) => {
321 const { ptr, len } = writeStr(e.key);
322 const prevented = instance.exports.__hook_keydown(handlerID, ptr, len);
323 if (prevented) e.preventDefault();
324 });
325 },
326 dom_insert_mention_chip(el, charCount, np, nl, _nc, nmp, nml, _nmc, pp, pl, _pc) {
327 dom.InsertMentionChip(el, charCount, readStr(np, nl), readStr(nmp, nml), readStr(pp, pl));
328 },
329 dom_set_timeout(cbID, ms) {
330 return setTimeout(() => cb0(cbID), ms);
331 },
332 dom_clear_timeout(id) { clearTimeout(id); },
333 dom_fetch_text(p, l, _c, cbID) {
334 dom.FetchText(readStr(p, l), (s) => cbs(cbID, s));
335 },
336 dom_fetch_relay_info(p, l, _c, cbID) {
337 dom.FetchRelayInfo(readStr(p, l), (s) => cbs(cbID, s));
338 },
339 dom_fetch_put_blob_base64(up, ul, _uc, dp, dl, _dc, cp, cl, _cc, ap, al, _ac, cbID) {
340 dom.FetchPutBlobBase64(readStr(up, ul), readStr(dp, dl), readStr(cp, cl), readStr(ap, al), (s) => cbs(cbID, s));
341 },
342 dom_idb_set_enc_key(p, l, _c) { dom.IDBSetEncKey(readStr(p, l)); },
343 dom_idb_get(sp, sl, _sc, kp, kl, _kc, cbID) {
344 dom.IDBGet(readStr(sp, sl), readStr(kp, kl), (s) => cbs(cbID, s));
345 },
346 dom_idb_put(sp, sl, _sc, kp, kl, _kc, vp, vl, _vc) {
347 dom.IDBPut(readStr(sp, sl), readStr(kp, kl), readStr(vp, vl));
348 },
349 dom_idb_get_all(sp, sl, _sc, fnID, doneID) {
350 dom.IDBGetAll(readStr(sp, sl), (k, v) => cbss(fnID, k, v), () => cb0(doneID));
351 },
352 dom_prefers_dark() { return dom.PrefersDark() ? 1 : 0; },
353 dom_console_log(p, l, _c) { console.log(readStr(p, l)); },
354 dom_confirm(p, l, _c) { return confirm(readStr(p, l)) ? 1 : 0; },
355 dom_post_to_sw(p, l, _c) { dom.PostToSW(readStr(p, l)); },
356 dom_on_sw_message(cbID) { dom.OnSWMessage((s) => cbs(cbID, s)); },
357 dom_push_state(p, l, _c) { dom.PushState(readStr(p, l)); },
358 dom_replace_state(p, l, _c) { dom.ReplaceState(readStr(p, l)); },
359 dom_back() { history.back(); },
360 dom_location_reload() { location.reload(); },
361 dom_hard_refresh() { dom.HardRefresh(); },
362 dom_clear_storage_prefix(p, l, _c) { dom.ClearStoragePrefix(readStr(p, l)); },
363 dom_timezone_offset_seconds() { return -(new Date().getTimezoneOffset()) * 60; },
364 dom_read_clipboard(cbID) { dom.ReadClipboard((s) => cbs(cbID, s)); },
365 dom_write_clipboard(p, l, _c, cbID) { dom.WriteClipboard(readStr(p, l), (ok) => cbb(cbID, ok)); },
366 dom_get_path(op, ol) { setOutStr(op, ol, location.pathname); },
367 dom_get_hash(op, ol) { setOutStr(op, ol, location.hash); },
368 dom_hostname(op, ol) { setOutStr(op, ol, location.hostname); },
369 dom_port(op, ol) { setOutStr(op, ol, location.port); },
370 dom_user_agent(op, ol) { setOutStr(op, ol, navigator.userAgent); },
371 dom_on_pop_state(cbID) {
372 window.addEventListener('popstate', () => cbs(cbID, location.pathname));
373 },
374 dom_pick_file_text(p, l, _c, cbID) { dom.PickFileText(readStr(p, l), (s) => cbs(cbID, s)); },
375 dom_download_text(fp, fl, _fc, cp, cl, _cc, mp, ml, _mc) {
376 dom.DownloadText(readStr(fp, fl), readStr(cp, cl), readStr(mp, ml));
377 },
378 dom_on_pull_refresh(el, ind, cbID) { dom.OnPullRefresh(el, ind, () => cb0(cbID)); },
379 dom_now_seconds() { return BigInt(Math.floor(Date.now() / 1000)); },
380 dom_pick_file_base64(p, l, _c, cbID) {
381 dom.PickFileBase64(readStr(p, l), (d, m) => cbss(cbID, d, m));
382 },
383 dom_on_paste_image(el, cbID) { dom.OnPasteImage(el, (d, m) => cbss(cbID, d, m)); },
384 dom_on_drop_image(el, cbID) { dom.OnDropImage(el, (d, m) => cbss(cbID, d, m)); },
385
386 // --- ws ---
387 ws_dial(up, ul, _uc, msgID, openID, closeID, errID) {
388 return ws.Dial(readStr(up, ul),
389 (conn, s) => cbis(msgID, conn, s),
390 (conn) => cbi(openID, conn),
391 (conn, code, reason) => cbiis(closeID, conn, code, reason),
392 (conn) => cbi(errID, conn),
393 );
394 },
395 ws_send(conn, mp, ml, _mc) { return ws.Send(conn, readStr(mp, ml)) ? 1 : 0; },
396 ws_close(conn) { ws.Close(conn); },
397 ws_ready_state(conn) { return ws.ReadyState(conn); },
398
399 // --- bc ---
400 bc_open(np, nl, _nc, cbID) {
401 return (typeof BroadcastChannel !== 'undefined')
402 ? (() => { const bc = new BroadcastChannel(readStr(np, nl)); bc.onmessage = (e) => cbs(cbID, String(e.data)); return 1; })()
403 : 0;
404 },
405 bc_send(handle, mp, ml, _mc) { /* placeholder - needs handle tracking */ },
406 bc_close(handle) { /* placeholder */ },
407
408 // --- wasm ---
409 wasm_load_go_wasm(ep, el, _ec, wp, wl, _wc, cbID) {
410 // Placeholder: loads another Go WASM binary
411 cb0(cbID);
412 },
413
414 // --- node ---
415 node_on_line(cbID) { if (node.OnLine) node.OnLine((s) => cbs(cbID, s)); },
416 node_on_close(cbID) { if (node.OnClose) node.OnClose(() => cb0(cbID)); },
417 node_write_line(p, l, _c) { if (node.WriteLine) node.WriteLine(readStr(p, l)); },
418 node_write_err(p, l, _c) { if (node.WriteErr) node.WriteErr(readStr(p, l)); },
419 node_exit(code) { if (node.Exit) node.Exit(code); },
420 node_now_seconds() { return BigInt(Math.floor(Date.now() / 1000)); },
421
422 // --- subtle ---
423 subtle_random_bytes(p, l, _c) {
424 const buf = new Uint8Array(memory.buffer, p, l);
425 globalThis.crypto.getRandomValues(buf);
426 },
427 subtle_aes_cbc_encrypt(kp, kl, _kc, ip, il, _ic, pp, pl, _pc, cbID) {
428 subtle.AESCBCEncrypt(readBytes(kp, kl), readBytes(ip, il), readBytes(pp, pl), (r) => cbdata(cbID, r));
429 },
430 subtle_aes_cbc_decrypt(kp, kl, _kc, ip, il, _ic, cp, cl, _cc, cbID) {
431 subtle.AESCBCDecrypt(readBytes(kp, kl), readBytes(ip, il), readBytes(cp, cl), (r) => cbdata(cbID, r));
432 },
433 subtle_aes_gcm_encrypt(kp, kl, _kc, ip, il, _ic, pp, pl, _pc, cbID) {
434 subtle.AESGCMEncrypt(readBytes(kp, kl), readBytes(ip, il), readBytes(pp, pl), (r) => cbdata(cbID, r));
435 },
436 subtle_aes_gcm_decrypt(kp, kl, _kc, ip, il, _ic, cp, cl, _cc, cbID) {
437 subtle.AESGCMDecrypt(readBytes(kp, kl), readBytes(ip, il), readBytes(cp, cl), (r) => cbdata(cbID, r));
438 },
439 subtle_pbkdf2_derive_key(pp, pl, _pc, sp, sl, _sc, iterations, cbID) {
440 subtle.PBKDF2DeriveKey(readStr(pp, pl), readBytes(sp, sl), iterations, (r) => cbdata(cbID, r));
441 },
442 subtle_argon2id_derive_key(pp, pl, _pc, sp, sl, _sc, t, m, p, dkLen, cbID) {
443 subtle.Argon2idDeriveKey(readStr(pp, pl), readBytes(sp, sl), t, m, p, dkLen, (r) => cbdata(cbID, r));
444 },
445 subtle_sha256_hex(p, l, _c, cbID) {
446 subtle.SHA256Hex(readBytes(p, l), (s) => cbs(cbID, s));
447 },
448 subtle_hmac_sha512(kp, kl, _kc, dp, dl, _dc, cbID) {
449 subtle.HMACSHA512(readBytes(kp, kl), readBytes(dp, dl), (r) => cbdata(cbID, r));
450 },
451 subtle_pbkdf2_sha512(pp, pl, _pc, sp, sl, _sc, iterations, dkLen, cbID) {
452 subtle.PBKDF2SHA512(readStr(pp, pl), readBytes(sp, sl), iterations, dkLen, (r) => cbdata(cbID, r));
453 },
454
455 // --- signer ---
456 signer_has_signer() { return signer.HasSigner() ? 1 : 0; },
457 signer_has_mls() { return signer.HasMLS() ? 1 : 0; },
458 signer_get_public_key(cbID) { signer.GetPublicKey((s) => cbs(cbID, s)); },
459 signer_sign_event(p, l, _c, cbID) { signer.SignEvent(readStr(p, l), (s) => cbs(cbID, s)); },
460 signer_get_shared_secret(p, l, _c, cbID) { signer.GetSharedSecret(readStr(p, l), (s) => cbs(cbID, s)); },
461 signer_nip04_decrypt(pkp, pkl, _pkc, cp, cl, _cc, cbID) {
462 signer.Nip04Decrypt(readStr(pkp, pkl), readStr(cp, cl), (s) => cbs(cbID, s));
463 },
464 signer_nip04_encrypt(pkp, pkl, _pkc, pp, pl, _pc, cbID) {
465 signer.Nip04Encrypt(readStr(pkp, pkl), readStr(pp, pl), (s) => cbs(cbID, s));
466 },
467 signer_nip44_decrypt(pkp, pkl, _pkc, cp, cl, _cc, cbID) {
468 signer.Nip44Decrypt(readStr(pkp, pkl), readStr(cp, cl), (s) => cbs(cbID, s));
469 },
470 signer_nip44_encrypt(pkp, pkl, _pkc, pp, pl, _pc, cbID) {
471 signer.Nip44Encrypt(readStr(pkp, pkl), readStr(pp, pl), (s) => cbs(cbID, s));
472 },
473 signer_is_installed(cbID) { signer.IsInstalled((v) => cbb(cbID, v)); },
474 signer_get_vault_status(cbID) { signer.GetVaultStatus((s) => cbs(cbID, s)); },
475 signer_create_vault(p, l, _c, cbID) { signer.CreateVault(readStr(p, l), (v) => cbb(cbID, v)); },
476 signer_unlock_vault(p, l, _c, cbID) { signer.UnlockVault(readStr(p, l), (v) => cbb(cbID, v)); },
477 signer_lock_vault(cbID) { signer.LockVault(() => cb0(cbID)); },
478 signer_list_identities(cbID) { signer.ListIdentities((s) => cbs(cbID, s)); },
479 signer_add_identity(p, l, _c, cbID) { signer.AddIdentity(readStr(p, l), (v) => cbb(cbID, v)); },
480 signer_nsec_login(p, l, _c, cbID) { signer.NsecLogin(readStr(p, l), (v) => cbb(cbID, v)); },
481 signer_switch_identity(p, l, _c, cbID) { signer.SwitchIdentity(readStr(p, l), (v) => cbb(cbID, v)); },
482 signer_export_vault(p, l, _c, cbID) { signer.ExportVault(readStr(p, l), (s) => cbs(cbID, s)); },
483 signer_import_vault(p, l, _c, cbID) { signer.ImportVault(readStr(p, l), (v) => cbb(cbID, v)); },
484 signer_remove_identity(p, l, _c, cbID) { signer.RemoveIdentity(readStr(p, l), (v) => cbb(cbID, v)); },
485 signer_generate_mnemonic(cbID) { signer.GenerateMnemonic((s) => cbs(cbID, s)); },
486 signer_validate_mnemonic(p, l, _c, cbID) { signer.ValidateMnemonic(readStr(p, l), (v) => cbb(cbID, v)); },
487 signer_create_hd_vault(pp, pl, _pc, np, nl, _nc, cbID) {
488 signer.CreateHDVault(readStr(pp, pl), readStr(np, nl), (s) => cbs(cbID, s));
489 },
490 signer_restore_hd_vault(pp, pl, _pc, mp, ml, _mc, np, nl, _nc, cbID) {
491 signer.RestoreHDVault(readStr(pp, pl), readStr(mp, ml), readStr(np, nl), (v) => cbb(cbID, v));
492 },
493 signer_derive_identity(p, l, _c, cbID) { signer.DeriveIdentity(readStr(p, l), (s) => cbs(cbID, s)); },
494 signer_get_mnemonic(cbID) { signer.GetMnemonic((s) => cbs(cbID, s)); },
495 signer_probe_account(index, cbID) { signer.ProbeAccount(index, (s) => cbs(cbID, s)); },
496 signer_is_hd(cbID) { signer.IsHD((v) => cbb(cbID, v)); },
497 signer_reset_extension(cbID) { signer.ResetExtension((v) => cbb(cbID, v)); },
498 signer_nwc_list(cbID) { signer.NwcList((s) => cbs(cbID, s)); },
499 signer_nwc_add(up, ul, _uc, ap, al, _ac, createdAt, cbID) {
500 signer.NwcAdd(readStr(up, ul), readStr(ap, al), Number(createdAt), (s) => cbs(cbID, s));
501 },
502 signer_nwc_remove(p, l, _c, cbID) { signer.NwcRemove(readStr(p, l), (v) => cbb(cbID, v)); },
503 signer_nwc_build_request(ip, il, _ic, mp, ml, _mc, pp, pl, _pc, ep, el2, _ec, createdAt, cbID) {
504 signer.NwcBuildRequest(readStr(ip, il), readStr(mp, ml), readStr(pp, pl), readStr(ep, el2), Number(createdAt), (s) => cbs(cbID, s));
505 },
506 signer_nwc_parse_response(ip, il, _ic, cp, cl, _cc, ep, el2, _ec, cbID) {
507 signer.NwcParseResponse(readStr(ip, il), readStr(cp, cl), readStr(ep, el2), (s) => cbs(cbID, s));
508 },
509
510 // --- idb ---
511 idb_set_enc_key(p, l, _c) { idb.SetEncKey(readStr(p, l)); },
512 idb_open(cbID) { idb.Open(() => cb0(cbID)); },
513 idb_save_event(p, l, _c, cbID) { idb.SaveEvent(readStr(p, l), (v) => cbb(cbID, v)); },
514 idb_query_events(p, l, _c, cbID) { idb.QueryEvents(readStr(p, l), (s) => cbs(cbID, s)); },
515 idb_save_dm(p, l, _c, cbID) { idb.SaveDM(readStr(p, l), (s) => cbs(cbID, s)); },
516 idb_query_dms(pp, pl, _pc, limit, until, cbID) {
517 idb.QueryDMs(readStr(pp, pl), limit, Number(until), (s) => cbs(cbID, s));
518 },
519 idb_get_conversation_list(cbID) { idb.GetConversationList((s) => cbs(cbID, s)); },
520 idb_clear_dms_by_peer(p, l, _c, cbID) { idb.ClearDMsByPeer(readStr(p, l), () => cb0(cbID)); },
521 idb_set_version(p, l, _c) { idb.SetVersion(readStr(p, l)); },
522 idb_mls_save_group(gp, gl, _gc, sp, sl, _sc, cbID) {
523 idb.MlsSaveGroup(readStr(gp, gl), readStr(sp, sl), () => cb0(cbID));
524 },
525 idb_mls_load_group(gp, gl, _gc, cbID) {
526 idb.MlsLoadGroup(readStr(gp, gl), (s) => cbs(cbID, s));
527 },
528 idb_mls_list_groups(cbID) { idb.MlsListGroups((s) => cbs(cbID, s)); },
529 idb_mls_save_kpp(p, l, _c, cbID) { idb.MlsSaveKPP(readStr(p, l), () => cb0(cbID)); },
530 idb_mls_load_kpp(cbID) { idb.MlsLoadKPP((s) => cbs(cbID, s)); },
531
532 // --- ext ---
533 ext_storage_get(kp, kl, _kc, cbID) { ext.StorageGet(readStr(kp, kl), (s) => cbs(cbID, s)); },
534 ext_storage_set(kp, kl, _kc, vp, vl, _vc) { ext.StorageSet(readStr(kp, kl), readStr(vp, vl)); },
535 ext_storage_remove(kp, kl, _kc) { ext.StorageRemove(readStr(kp, kl)); },
536 ext_on_message(_unused) {
537 ext.OnMessage((method, params, tabID, respond) => {
538 const reqID = hookNextReq++;
539 hookResponses.set(reqID, respond);
540 const m = writeStr(method);
541 const p = writeStr(params);
542 queueMicrotask(() => instance.exports.__hook_ext_on_message(reqID, m.ptr, m.len, p.ptr, p.len, tabID));
543 });
544 },
545 ext_on_message_respond(reqID, p, l, _c) {
546 const respond = hookResponses.get(reqID);
547 if (respond) { hookResponses.delete(reqID); respond(readStr(p, l)); }
548 },
549 ext_send_message_to_tab(tabID, mp, ml, _mc) { ext.SendMessageToTab(tabID, readStr(mp, ml)); },
550 ext_get_active_tab(cbID) { ext.GetActiveTab((id, url) => cbis(cbID, id, url)); },
551 ext_console_log(p, l, _c) { console.log(readStr(p, l)); },
552 ext_session_get(kp, kl, _kc, cbID) { ext.SessionGet(readStr(kp, kl), (s) => cbs(cbID, s)); },
553 ext_session_set(kp, kl, _kc, vp, vl, _vc) { ext.SessionSet(readStr(kp, kl), readStr(vp, vl)); },
554 ext_is_in_page() { return ext.IsInPage() ? 1 : 0; },
555
556 // --- registry ---
557 registry_set_seckey(p, l, _c) { self.$hooks = self.$hooks || {}; self.$hooks.seckey = readStr(p, l); },
558 registry_seckey(op, ol) { setOutStr(op, ol, (self.$hooks && self.$hooks.seckey) || ''); },
559 registry_set_pubkey(p, l, _c) { self.$hooks = self.$hooks || {}; self.$hooks.pubkey = readStr(p, l); },
560 registry_pubkey(op, ol) { setOutStr(op, ol, (self.$hooks && self.$hooks.pubkey) || ''); },
561 registry_set_has_key(v) { self.$hooks = self.$hooks || {}; self.$hooks.hasKey = v !== 0; },
562 registry_has_key() { return (self.$hooks && self.$hooks.hasKey) ? 1 : 0; },
563
564 registry_on_marmot_init(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.marmotInit = (s) => cbs(cbID, s); },
565 registry_on_marmot_send(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.marmotSend = (r, c) => cbss(cbID, r, c); },
566 registry_on_marmot_subscribe(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.marmotSubscribe = () => cb0(cbID); },
567 registry_on_marmot_publish_kp(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.marmotPublishKP = (s) => cbs(cbID, s); },
568 registry_on_marmot_list_groups(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.marmotListGroups = (s) => cbs(cbID, s); },
569 registry_on_save_dm_record(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.saveDMRecord = (s) => cbs(cbID, s); },
570 registry_on_broadcast_to_clients(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.broadcastToClients = (s) => cbs(cbID, s); },
571 registry_on_send_to_client(cbID) { self.$hooks = self.$hooks || {}; self.$hooks.sendToClient = (id, m) => cbss(cbID, id, m); },
572
573 registry_on_encrypt_nip04() {
574 self.$hooks = self.$hooks || {};
575 self.$hooks.encryptNip04 = (pk, ct) => new Promise(resolve => {
576 const reqID = hookNextReq++;
577 hookResponses.set(reqID, resolve);
578 const p = writeStr(pk); const c = writeStr(ct);
579 queueMicrotask(() => instance.exports.__hook_encrypt_nip04(reqID, p.ptr, p.len, c.ptr, c.len));
580 });
581 },
582 registry_on_encrypt_nip17() {
583 self.$hooks = self.$hooks || {};
584 self.$hooks.encryptNip17 = (pk, ct) => new Promise(resolve => {
585 const reqID = hookNextReq++;
586 hookResponses.set(reqID, resolve);
587 const p = writeStr(pk); const c = writeStr(ct);
588 queueMicrotask(() => instance.exports.__hook_encrypt_nip17(reqID, p.ptr, p.len, c.ptr, c.len));
589 });
590 },
591 registry_on_decrypt_dm() {
592 self.$hooks = self.$hooks || {};
593 self.$hooks.decryptDM = (ev) => new Promise(resolve => {
594 const reqID = hookNextReq++;
595 hookResponses.set(reqID, resolve);
596 const e = writeStr(ev);
597 queueMicrotask(() => instance.exports.__hook_decrypt_dm(reqID, e.ptr, e.len));
598 });
599 },
600 registry_hook_respond_s(reqID, p, l, _c) {
601 const resolve = hookResponses.get(reqID);
602 if (resolve) { hookResponses.delete(reqID); resolve(readStr(p, l)); }
603 },
604 registry_hook_respond_ss(reqID, p1, l1, _c1, p2, l2, _c2) {
605 const resolve = hookResponses.get(reqID);
606 if (resolve) { hookResponses.delete(reqID); resolve([readStr(p1, l1), readStr(p2, l2)]); }
607 },
608
609 registry_encrypt_nip04(pkp, pkl, _pkc, cp, cl, _cc, cbID) {
610 if (self.$hooks && self.$hooks.encryptNip04) {
611 self.$hooks.encryptNip04(readStr(pkp, pkl), readStr(cp, cl)).then((s) => cbs(cbID, s));
612 }
613 },
614 registry_encrypt_nip17(pkp, pkl, _pkc, cp, cl, _cc, cbID) {
615 if (self.$hooks && self.$hooks.encryptNip17) {
616 self.$hooks.encryptNip17(readStr(pkp, pkl), readStr(cp, cl)).then(([a, b]) => cbss(cbID, a, b));
617 }
618 },
619 registry_decrypt_dm(p, l, _c, cbID) {
620 if (self.$hooks && self.$hooks.decryptDM) {
621 self.$hooks.decryptDM(readStr(p, l)).then((s) => cbs(cbID, s));
622 }
623 },
624 registry_make_dm_record(pp, pl, _pc, fp, fl, _fc, cp, cl, _cc, ts, prp, prl, _prc, ep, el2, _ec, op, ol) {
625 // Build DM record JSON directly
626 const peer = readStr(pp, pl), from = readStr(fp, fl), content = readStr(cp, cl);
627 const proto = readStr(prp, prl), eid = readStr(ep, el2);
628 const r = JSON.stringify({peer, from, content, ts: Number(ts), proto, eid});
629 setOutStr(op, ol, r);
630 },
631 registry_marmot_init(p, l, _c) { if (self.$hooks && self.$hooks.marmotInit) self.$hooks.marmotInit(readStr(p, l)); },
632 registry_marmot_send(rp, rl, _rc, cp, cl, _cc) { if (self.$hooks && self.$hooks.marmotSend) self.$hooks.marmotSend(readStr(rp, rl), readStr(cp, cl)); },
633 registry_marmot_subscribe() { if (self.$hooks && self.$hooks.marmotSubscribe) self.$hooks.marmotSubscribe(); },
634 registry_marmot_publish_kp(p, l, _c) { if (self.$hooks && self.$hooks.marmotPublishKP) self.$hooks.marmotPublishKP(readStr(p, l)); },
635 registry_marmot_list_groups(p, l, _c) { if (self.$hooks && self.$hooks.marmotListGroups) self.$hooks.marmotListGroups(readStr(p, l)); },
636 registry_save_dm_record(p, l, _c) { if (self.$hooks && self.$hooks.saveDMRecord) self.$hooks.saveDMRecord(readStr(p, l)); },
637 registry_broadcast_to_clients(p, l, _c) { if (self.$hooks && self.$hooks.broadcastToClients) self.$hooks.broadcastToClients(readStr(p, l)); },
638 registry_send_to_client(ip, il, _ic, mp, ml, _mc) { if (self.$hooks && self.$hooks.sendToClient) self.$hooks.sendToClient(readStr(ip, il), readStr(mp, ml)); },
639 registry_has_hook(p, l, _c) { return (self.$hooks && self.$hooks[readStr(p, l)]) ? 1 : 0; },
640 registry_load_module(p, l, _c, cbID) {
641 // Module loading is moxiejs-specific; in WASM all modules are compiled in
642 cb0(cbID);
643 },
644
645 // --- sw ---
646 sw_on_install(cbID) { self.addEventListener('install', (e) => cbi(cbID, 0)); },
647 sw_on_activate(cbID) { self.addEventListener('activate', (e) => cbi(cbID, 0)); },
648 sw_on_fetch(cbID) { self.addEventListener('fetch', (e) => cbi(cbID, 0)); },
649 sw_on_message(cbID) { self.addEventListener('message', (e) => cbi(cbID, 0)); },
650 sw_wait_until(event, cbID) { /* simplified - actual impl needs event ref */ cb0(cbID); },
651 sw_respond_with(event, resp) { /* needs event/response tracking */ },
652 sw_respond_with_network(event) { /* pass-through */ },
653 sw_respond_with_cache_first(event) { /* needs event tracking */ },
654 sw_respond_with_network_first(event) { /* needs event tracking */ },
655 sw_get_request_url(event, op, ol) { setOutStr(op, ol, ''); },
656 sw_get_request_path(event, op, ol) { setOutStr(op, ol, ''); },
657 sw_get_message_data(event, op, ol) { setOutStr(op, ol, ''); },
658 sw_get_message_client_id(event, op, ol) { setOutStr(op, ol, ''); },
659 sw_origin(op, ol) { setOutStr(op, ol, self.location ? self.location.origin : ''); },
660 sw_skip_waiting() { if (self.skipWaiting) self.skipWaiting(); },
661 sw_claim_clients(cbID) {
662 if (self.clients && self.clients.claim) self.clients.claim().then(() => cb0(cbID));
663 else cb0(cbID);
664 },
665 sw_match_clients(cbID) { /* simplified */ },
666 sw_post_message(client, p, l, _c) { /* needs client handle tracking */ },
667 sw_post_message_json(client, p, l, _c) { /* needs client handle tracking */ },
668 sw_get_client_by_id(p, l, _c, cbID) { cbii(cbID, 0, 0); },
669 sw_navigate(client, p, l, _c) { /* needs client handle tracking */ },
670 sw_cache_open(p, l, _c, cbID) {
671 caches.open(readStr(p, l)).then(() => cbi(cbID, 1));
672 },
673 sw_cache_add_all(cache, up, ul, _uc, count, cbID) {
674 const urls = splitNull(up, ul, count);
675 caches.open('v1').then(c => c.addAll(urls)).then(() => cb0(cbID));
676 },
677 sw_cache_from_manifests(cache, fp, fl, _fc, count, cbID) { cb0(cbID); },
678 sw_cache_put(cache, up, ul, _uc, resp, cbID) { cb0(cbID); },
679 sw_cache_match(p, l, _c, cbID) { cbi(cbID, 0); },
680 sw_cache_delete(p, l, _c, cbID) {
681 caches.delete(readStr(p, l)).then(() => cb0(cbID));
682 },
683 sw_fetch(p, l, _c, cbID) {
684 fetch(readStr(p, l)).then(r => cbii(cbID, 1, r.ok ? 1 : 0)).catch(() => cbii(cbID, 0, 0));
685 },
686 sw_fetch_all(up, ul, _uc, count, eachID, doneID) {
687 const urls = splitNull(up, ul, count);
688 Promise.all(urls.map((u, i) => fetch(u).then(r => cbiii(eachID, i, 1, r.ok ? 1 : 0)).catch(() => cbiii(eachID, i, 0, 0))))
689 .then(() => cb0(doneID));
690 },
691 sw_response_ok(resp) { return 0; },
692 sw_sse_connect(p, l, _c, cbID) {
693 const es = new EventSource(readStr(p, l));
694 es.onmessage = (e) => cbs(cbID, e.data);
695 return 1;
696 },
697 sw_sse_close(id) { /* needs handle tracking */ },
698 sw_set_timeout(ms, cbID) { return setTimeout(() => cb0(cbID), ms); },
699 sw_clear_timeout(t) { clearTimeout(t); },
700 sw_now_seconds() { return BigInt(Math.floor(Date.now() / 1000)); },
701 sw_now_millis() { return BigInt(Date.now()); },
702 sw_log(p, l, _c) { console.log(readStr(p, l)); },
703
704 // --- webgl ---
705 webgl_CreateContext(canvasId) { return webgl.CreateContext(canvasId); },
706 webgl_CreateContextFromHandle(el) { return webgl.CreateContextFromHandle(el); },
707 webgl_DeleteContext(ctx) { webgl.DeleteContext(ctx); },
708 webgl_Enable(ctx, cap) { webgl.Enable(ctx, cap); },
709 webgl_Disable(ctx, cap) { webgl.Disable(ctx, cap); },
710 webgl_Viewport(ctx, x, y, w, h) { webgl.Viewport(ctx, x, y, w, h); },
711 webgl_Scissor(ctx, x, y, w, h) { webgl.Scissor(ctx, x, y, w, h); },
712 webgl_BlendFunc(ctx, src, dst) { webgl.BlendFunc(ctx, src, dst); },
713 webgl_BlendFuncSeparate(ctx, sr, dr, sa, da) { webgl.BlendFuncSeparate(ctx, sr, dr, sa, da); },
714 webgl_BlendEquation(ctx, mode) { webgl.BlendEquation(ctx, mode); },
715 webgl_DepthFunc(ctx, fn) { webgl.DepthFunc(ctx, fn); },
716 webgl_DepthMask(ctx, flag) { webgl.DepthMask(ctx, flag); },
717 webgl_ClearColor(ctx, r, g, b, a) { webgl.ClearColor(ctx, r, g, b, a); },
718 webgl_ClearDepth(ctx, d) { webgl.ClearDepth(ctx, d); },
719 webgl_Clear(ctx, mask) { webgl.Clear(ctx, mask); },
720 webgl_Flush(ctx) { webgl.Flush(ctx); },
721 webgl_Finish(ctx) { webgl.Finish(ctx); },
722 webgl_GetError(ctx) { return webgl.GetError(ctx); },
723 webgl_GetInteger(ctx, pname) { return webgl.GetInteger(ctx, pname); },
724 webgl_IsEnabled(ctx, cap) { return webgl.IsEnabled(ctx, cap); },
725 webgl_PixelStorei(ctx, pname, param) { webgl.PixelStorei(ctx, pname, param); },
726 webgl_ReadPixels(ctx, x, y, w, h, fmt, typ, bufPtr, bufLen) { webgl.ReadPixels(ctx, x, y, w, h, fmt, typ, bufPtr, bufLen); },
727 webgl_CreateBuffer(ctx) { return webgl.CreateBuffer(ctx); },
728 webgl_DeleteBuffer(ctx, buf) { webgl.DeleteBuffer(ctx, buf); },
729 webgl_BindBuffer(ctx, target, buf) { webgl.BindBuffer(ctx, target, buf); },
730 webgl_BufferData(ctx, target, ptr, len, usage) { webgl.BufferData(ctx, target, ptr, len, usage); },
731 webgl_BufferSubData(ctx, target, off, ptr, len) { webgl.BufferSubData(ctx, target, off, ptr, len); },
732 webgl_CreateTexture(ctx) { return webgl.CreateTexture(ctx); },
733 webgl_DeleteTexture(ctx, tex) { webgl.DeleteTexture(ctx, tex); },
734 webgl_BindTexture(ctx, target, tex) { webgl.BindTexture(ctx, target, tex); },
735 webgl_ActiveTexture(ctx, unit) { webgl.ActiveTexture(ctx, unit); },
736 webgl_TexParameteri(ctx, target, pname, param) { webgl.TexParameteri(ctx, target, pname, param); },
737 webgl_TexImage2D(ctx, target, lvl, ifmt, w, h, border, fmt, typ, ptr, len) { webgl.TexImage2D(ctx, target, lvl, ifmt, w, h, border, fmt, typ, ptr, len); },
738 webgl_TexStorage2D(ctx, target, lvls, ifmt, w, h) { webgl.TexStorage2D(ctx, target, lvls, ifmt, w, h); },
739 webgl_TexSubImage2D(ctx, target, lvl, xo, yo, w, h, fmt, typ, ptr, len) { webgl.TexSubImage2D(ctx, target, lvl, xo, yo, w, h, fmt, typ, ptr, len); },
740 webgl_CopyTexSubImage2D(ctx, target, lvl, xo, yo, x, y, w, h) { webgl.CopyTexSubImage2D(ctx, target, lvl, xo, yo, x, y, w, h); },
741 webgl_GenerateMipmap(ctx, target) { webgl.GenerateMipmap(ctx, target); },
742 webgl_CreateFramebuffer(ctx) { return webgl.CreateFramebuffer(ctx); },
743 webgl_DeleteFramebuffer(ctx, fbo) { webgl.DeleteFramebuffer(ctx, fbo); },
744 webgl_BindFramebuffer(ctx, target, fbo) { webgl.BindFramebuffer(ctx, target, fbo); },
745 webgl_FramebufferTexture2D(ctx, target, att, texTarget, tex, lvl) { webgl.FramebufferTexture2D(ctx, target, att, texTarget, tex, lvl); },
746 webgl_CheckFramebufferStatus(ctx, target) { return webgl.CheckFramebufferStatus(ctx, target); },
747 webgl_InvalidateFramebuffer(ctx, target, att) { webgl.InvalidateFramebuffer(ctx, target, att); },
748 webgl_CreateRenderbuffer(ctx) { return webgl.CreateRenderbuffer(ctx); },
749 webgl_DeleteRenderbuffer(ctx, rbo) { webgl.DeleteRenderbuffer(ctx, rbo); },
750 webgl_BindRenderbuffer(ctx, target, rbo) { webgl.BindRenderbuffer(ctx, target, rbo); },
751 webgl_RenderbufferStorage(ctx, target, ifmt, w, h) { webgl.RenderbufferStorage(ctx, target, ifmt, w, h); },
752 webgl_FramebufferRenderbuffer(ctx, target, att, rbtarget, rbo) { webgl.FramebufferRenderbuffer(ctx, target, att, rbtarget, rbo); },
753 webgl_CreateShader(ctx, typ) { return webgl.CreateShader(ctx, typ); },
754 webgl_DeleteShader(ctx, sh) { webgl.DeleteShader(ctx, sh); },
755 webgl_ShaderSource(ctx, sh, ptr, len) { webgl.ShaderSource(ctx, sh, ptr, len); },
756 webgl_CompileShader(ctx, sh) { webgl.CompileShader(ctx, sh); },
757 webgl_GetShaderParameter(ctx, sh, pname) { return webgl.GetShaderParameter(ctx, sh, pname); },
758 webgl_GetShaderInfoLog(ctx, sh, ptr, len) { return webgl.GetShaderInfoLog(ctx, sh, ptr, len); },
759 webgl_CreateProgram(ctx) { return webgl.CreateProgram(ctx); },
760 webgl_DeleteProgram(ctx, prog) { webgl.DeleteProgram(ctx, prog); },
761 webgl_AttachShader(ctx, prog, sh) { webgl.AttachShader(ctx, prog, sh); },
762 webgl_LinkProgram(ctx, prog) { webgl.LinkProgram(ctx, prog); },
763 webgl_UseProgram(ctx, prog) { webgl.UseProgram(ctx, prog); },
764 webgl_GetProgramParameter(ctx, prog, pname) { return webgl.GetProgramParameter(ctx, prog, pname); },
765 webgl_GetProgramInfoLog(ctx, prog, ptr, len) { return webgl.GetProgramInfoLog(ctx, prog, ptr, len); },
766 webgl_BindAttribLocation(ctx, prog, idx, ptr, len) { webgl.BindAttribLocation(ctx, prog, idx, ptr, len); },
767 webgl_GetUniformLocation(ctx, prog, ptr, len) { return webgl.GetUniformLocation(ctx, prog, ptr, len); },
768 webgl_Uniform1f(ctx, loc, v) { webgl.Uniform1f(ctx, loc, v); },
769 webgl_Uniform2f(ctx, loc, v0, v1) { webgl.Uniform2f(ctx, loc, v0, v1); },
770 webgl_Uniform3f(ctx, loc, v0, v1, v2) { webgl.Uniform3f(ctx, loc, v0, v1, v2); },
771 webgl_Uniform4f(ctx, loc, v0, v1, v2, v3) { webgl.Uniform4f(ctx, loc, v0, v1, v2, v3); },
772 webgl_Uniform1i(ctx, loc, v) { webgl.Uniform1i(ctx, loc, v); },
773 webgl_EnableVertexAttribArray(ctx, idx) { webgl.EnableVertexAttribArray(ctx, idx); },
774 webgl_DisableVertexAttribArray(ctx, idx) { webgl.DisableVertexAttribArray(ctx, idx); },
775 webgl_VertexAttribPointer(ctx, idx, size, typ, norm, stride, off) { webgl.VertexAttribPointer(ctx, idx, size, typ, norm, stride, off); },
776 webgl_DrawArrays(ctx, mode, first, count) { webgl.DrawArrays(ctx, mode, first, count); },
777 webgl_DrawElements(ctx, mode, count, typ, off) { webgl.DrawElements(ctx, mode, count, typ, off); },
778
779 // --- text ---
780 text_Measure(fp, fl, sz, tp, tl, wp, hp) { text.Measure(fp, fl, sz, tp, tl, wp, hp); },
781 text_Render(fp, fl, sz, tp, tl, mw, dp, dc) { return text.Render(fp, fl, sz, tp, tl, mw, dp, dc); },
782
783 // --- app ---
784 app_CreateFullscreenCanvas() { app.CreateFullscreenCanvas(); },
785 app_CreateWebGLContext() { return app.CreateWebGLContext(); },
786 app_GetDevicePixelRatio() { return app.GetDevicePixelRatio(); },
787 app_GetCanvasCSSSize(wPtr, hPtr) { app.GetCanvasCSSSize(wPtr, hPtr); },
788 app_SetCanvasBacking(w, h) { app.SetCanvasBacking(w, h); },
789 app_RequestRAF() { app.RequestRAF(); },
790 app_SetCursor(ptr, len) { app.SetCursor(ptr, len); },
791 app_SetTitle(ptr, len) { app.SetTitle(ptr, len); },
792 app_SetFullscreen(full) { app.SetFullscreen(full); },
793 app_RegisterPointerEvents() { app.RegisterPointerEvents(); },
794 app_RegisterKeyEvents() { app.RegisterKeyEvents(); },
795 app_RegisterResizeEvents() { app.RegisterResizeEvents(); },
796 };
797 }
798
799 export async function load(wasmPath) {
800 const response = await fetch(wasmPath);
801 const bytes = await response.arrayBuffer();
802
803 const bridge = buildBridge();
804
805 const result = await WebAssembly.instantiate(bytes, {
806 wasi_snapshot_preview1: {
807 fd_write(fd, iovs, iovs_len, nwritten) {
808 const dv = new DataView(memory.buffer);
809 let totalWritten = 0;
810 for (let i = 0; i < iovs_len; i++) {
811 const ptr = dv.getUint32(iovs + i * 8, true);
812 const len = dv.getUint32(iovs + i * 8 + 4, true);
813 const s = decoder.decode(new Uint8Array(memory.buffer, ptr, len));
814 if (fd === 1) console.log(s);
815 else if (fd === 2) console.error(s);
816 totalWritten += len;
817 }
818 dv.setUint32(nwritten, totalWritten, true);
819 return 0;
820 },
821 clock_time_get(clockID, precision, time) {
822 const now = clockID === 1
823 ? performance.now() * 1_000_000
824 : Date.now() * 1_000_000;
825 const dv = new DataView(memory.buffer);
826 dv.setBigInt64(time, BigInt(Math.floor(now)), true);
827 return 0;
828 },
829 },
830 bridge,
831 });
832
833 instance = result.instance;
834 memory = instance.exports.memory;
835 // Wire up reverse callbacks for app and text bridges.
836 app.SetWASM(instance);
837 text.SetWASM(instance);
838 globalThis.__moxie_wasm_memory = instance.exports.memory;
839 instance.exports._start();
840 // Mark WASM as ready for event dispatch now that initialization is complete.
841 app.SetWASMReady();
842 return instance;
843 }
844