wasm-host-sw.mjs raw

   1  // sw-build: 20260611-v0.6.38
   2  import * as sw from '../$runtime/sw.mjs';
   3  import * as idb from '../$runtime/idb.mjs';
   4  import { makeCoreHelpers, makeCommonBridge } from '../bridge-common.mjs';
   5  
   6  let mem;
   7  let xp;
   8  const enc = new TextEncoder();
   9  const dec = new TextDecoder();
  10  
  11  // Spawn-channel state. Every moxie wasm module imports bridge.channel_send /
  12  // channel_recv / channel_close (the spawn-channel ABI), so the import object
  13  // must provide them or instantiation dies with "import object field
  14  // 'channel_close' is not a Function". The other workers get these from
  15  // makeCommonBridge; this host predated it and had none.
  16  const _sabTable = new Map();
  17  const _sabSeqRef = { value: 0 };
  18  const _wasmUrlRef = { value: null };
  19  const _buildHashRef = { value: null };
  20  const _spawnedWorkerChans = new Map();
  21  
  22  function readStr(ptr, len) {
  23    if (len <= 0) return '';
  24    return dec.decode(new Uint8Array(mem.buffer, ptr, len));
  25  }
  26  
  27  function writeStr(s) {
  28    const bytes = enc.encode('' + s);
  29    const ptr = xp.__alloc(bytes.length);
  30    new Uint8Array(mem.buffer, ptr, bytes.length).set(bytes);
  31    return [ptr, bytes.length];
  32  }
  33  
  34  function writeStrOut(s, rPtrAddr, rLenAddr) {
  35    const [ptr, len] = writeStr(s);
  36    const dv = new DataView(mem.buffer);
  37    dv.setInt32(rPtrAddr, ptr, true);
  38    dv.setInt32(rLenAddr, len, true);
  39  }
  40  
  41  function cb0(id) { xp.__cb0(id); }
  42  function cbs(id, s) { const [ptr, len] = writeStr(s); xp.__cbs(id, ptr, len); }
  43  function cbb(id, v) { xp.__cbb(id, v ? 1 : 0); }
  44  function cbi(id, v) { xp.__cbi(id, v); }
  45  function cbii(id, a, b) { xp.__cbii(id, a, b); }
  46  
  47  function readNullSep(ptr, len) {
  48    if (len <= 0) return [];
  49    return readStr(ptr, len).split('\0');
  50  }
  51  
  52  const bridge = {
  53    // --- sw lifecycle ---
  54    sw_on_install(cbID) { sw.OnInstall(function(id) { cbi(cbID, id); }); },
  55    sw_on_activate(cbID) { sw.OnActivate(function(id) { cbi(cbID, id); }); },
  56    sw_on_fetch(cbID) { sw.OnFetch(function(id) { cbi(cbID, id); }); },
  57    sw_on_message(cbID) { sw.OnMessage(function(id) { cbi(cbID, id); }); },
  58  
  59    // --- event methods ---
  60    sw_wait_until(event, cbID) {
  61      sw.WaitUntil(event, function(done) { cb0(cbID); done(); });
  62    },
  63    sw_respond_with_cache_first(event) { sw.RespondWithCacheFirst(event); },
  64    sw_respond_with_network_first(event) { sw.RespondWithNetworkFirst(event); },
  65    sw_get_request_url(event, rPtrAddr, rLenAddr) {
  66      writeStrOut(sw.GetRequestURL(event), rPtrAddr, rLenAddr);
  67    },
  68    sw_get_request_path(event, rPtrAddr, rLenAddr) {
  69      writeStrOut(sw.GetRequestPath(event), rPtrAddr, rLenAddr);
  70    },
  71    sw_get_message_data(event, rPtrAddr, rLenAddr) {
  72      writeStrOut(sw.GetMessageData(event), rPtrAddr, rLenAddr);
  73    },
  74    sw_get_message_client_id(event, rPtrAddr, rLenAddr) {
  75      writeStrOut(sw.GetMessageClientID(event), rPtrAddr, rLenAddr);
  76    },
  77  
  78    // --- sw globals ---
  79    sw_skip_waiting() { sw.SkipWaiting(); },
  80    sw_claim_clients(cbID) { sw.ClaimClients(function() { cb0(cbID); }); },
  81    sw_match_clients(cbID) { sw.MatchClients(function(c) { cbi(cbID, c); }); },
  82    sw_post_message(client, ptr, len, cap) { sw.PostMessage(client, readStr(ptr, len)); },
  83    sw_post_message_json(client, ptr, len, cap) { sw.PostMessageJSON(client, readStr(ptr, len)); },
  84    sw_get_client_by_id(ptr, len, cap, cbID) {
  85      sw.GetClientByID(readStr(ptr, len), function(c, found) { cbii(cbID, c, found ? 1 : 0); });
  86    },
  87    sw_origin(rPtrAddr, rLenAddr) { writeStrOut(sw.Origin(), rPtrAddr, rLenAddr); },
  88    sw_log(ptr, len, cap) { sw.Log(readStr(ptr, len)); },
  89  
  90    // --- cache ---
  91    sw_cache_open(ptr, len, cap, cbID) {
  92      sw.CacheOpen(readStr(ptr, len), function(c) { cbi(cbID, c); });
  93    },
  94    sw_cache_from_manifests(cache, filesPtr, filesLen, filesCap, count, cbID) {
  95      sw.CacheFromManifests(cache, readNullSep(filesPtr, filesLen), function() { cb0(cbID); });
  96    },
  97    sw_cache_delete(ptr, len, cap, cbID) {
  98      sw.CacheDelete(readStr(ptr, len), function() { cb0(cbID); });
  99    },
 100  
 101    // --- fetch ---
 102    sw_fetch(ptr, len, cap, cbID) {
 103      sw.Fetch(readStr(ptr, len), function(resp, ok) { cbii(cbID, resp, ok ? 1 : 0); });
 104    },
 105  
 106    // --- sse ---
 107    sw_sse_connect(ptr, len, cap, cbID) {
 108      return sw.SSEConnect(readStr(ptr, len), function(data) { cbs(cbID, data); });
 109    },
 110  
 111    // --- timers ---
 112    sw_set_timeout(ms, cbID) { return sw.SetTimeout(ms, function() { cb0(cbID); }); },
 113    sw_clear_timeout(t) { sw.ClearTimeout(t); },
 114    sw_now_seconds() { return sw.NowSeconds(); },
 115  
 116    // --- idb ---
 117    idb_set_enc_key(ptr, len, cap) { idb.SetEncKey(readStr(ptr, len)); },
 118    idb_open(cbID) { idb.Open(function() { cb0(cbID); }); },
 119    idb_save_event(ptr, len, cap, cbID) {
 120      idb.SaveEvent(readStr(ptr, len), function(ok) { cbb(cbID, ok); });
 121    },
 122    idb_query_events(ptr, len, cap, cbID) {
 123      idb.QueryEvents(readStr(ptr, len), function(json) { cbs(cbID, json); });
 124    },
 125    idb_save_dm(ptr, len, cap, cbID) {
 126      idb.SaveDM(readStr(ptr, len), function(status) { cbs(cbID, status); });
 127    },
 128    idb_query_dms(peerPtr, peerLen, peerCap, limit, until, cbID) {
 129      idb.QueryDMs(readStr(peerPtr, peerLen), limit, Number(until), function(json) { cbs(cbID, json); });
 130    },
 131    idb_get_conversation_list(cbID) {
 132      idb.GetConversationList(function(json) { cbs(cbID, json); });
 133    },
 134    idb_clear_dms_by_peer(ptr, len, cap, cbID) {
 135      idb.ClearDMsByPeer(readStr(ptr, len), function() { cb0(cbID); });
 136    },
 137  
 138    // --- common (channel ops, spawn, subtle_random_bytes) ---
 139    ...makeCommonBridge(
 140      makeCoreHelpers(() => mem, () => xp),
 141      _sabTable, _sabSeqRef, _wasmUrlRef, _buildHashRef, _spawnedWorkerChans,
 142    ),
 143  };
 144  
 145  const wasi = {
 146    fd_write(fd, iovs, iovs_len, nwritten_ptr) {
 147      const dv = new DataView(mem.buffer);
 148      let total = 0;
 149      for (let i = 0; i < iovs_len; i++) {
 150        const ptr = dv.getUint32(iovs + i * 8, true);
 151        const len = dv.getUint32(iovs + i * 8 + 4, true);
 152        const s = dec.decode(new Uint8Array(mem.buffer, ptr, len));
 153        if (fd === 1) console.log(s);
 154        else if (fd === 2) console.error(s);
 155        total += len;
 156      }
 157      dv.setUint32(nwritten_ptr, total, true);
 158      return 0;
 159    },
 160    clock_time_get(clock_id, precision, time_ptr) {
 161      const dv = new DataView(mem.buffer);
 162      const ms = (clock_id === 1) ? performance.now() : Date.now();
 163      const ns = BigInt(Math.trunc(ms * 1e6));
 164      dv.setBigUint64(time_ptr >>> 0, ns, true);
 165      return 0;
 166    },
 167  };
 168  
 169  
 170  
 171  async function boot() {
 172    const { instance } = await WebAssembly.instantiateStreaming(
 173      fetch('/$sw/sw.wasm'),
 174      { bridge, wasi_snapshot_preview1: wasi }
 175    );
 176    mem = instance.exports.memory;
 177    xp = instance.exports;
 178    instance.exports._start();
 179  }
 180  
 181  boot().catch(e => console.error('SW WASM boot failed:', e));
 182