node-entry.mjs raw

   1  // Node.js entry wrapper for the moxiejs-compiled mlsinterop binary.
   2  //
   3  // The moxiejs runtime index re-exports sw.mjs, which has top-level
   4  // `self.addEventListener(...)` calls designed for a Service Worker scope.
   5  // Under Node, those globals don't exist. We polyfill a minimum viable
   6  // ServiceWorkerGlobalScope before importing the entry module so those
   7  // registrations are no-ops.
   8  //
   9  // Usage:
  10  //   node web/mlsinterop/node-entry.mjs
  11  //
  12  // Expects the compiled output at ./web/mlsinterop/static/$entry.mjs
  13  // (build command in scripts/mls-interop-test.sh).
  14  
  15  const g = globalThis;
  16  
  17  if (typeof g.self === 'undefined') {
  18    g.self = g;
  19  }
  20  
  21  if (typeof g.self.addEventListener !== 'function') {
  22    g.self.addEventListener = () => {};
  23    g.self.removeEventListener = () => {};
  24  }
  25  
  26  if (typeof g.self.skipWaiting !== 'function') {
  27    g.self.skipWaiting = () => Promise.resolve();
  28  }
  29  
  30  if (!g.self.clients) {
  31    g.self.clients = {
  32      claim: () => Promise.resolve(),
  33      matchAll: () => Promise.resolve([]),
  34      get: () => Promise.resolve(null),
  35    };
  36  }
  37  
  38  if (!g.self.registration) {
  39    g.self.registration = {
  40      scope: 'node://mlsinterop',
  41      active: null,
  42      waiting: null,
  43      installing: null,
  44      update: () => Promise.resolve(),
  45      unregister: () => Promise.resolve(true),
  46      showNotification: () => Promise.resolve(),
  47    };
  48  }
  49  
  50  if (!g.self.location) {
  51    g.self.location = { origin: 'node://mlsinterop', href: 'node://mlsinterop/' };
  52  }
  53  
  54  if (typeof g.caches === 'undefined') {
  55    g.caches = {
  56      open: () => Promise.resolve({
  57        match: () => Promise.resolve(undefined),
  58        put: () => Promise.resolve(),
  59        delete: () => Promise.resolve(true),
  60        keys: () => Promise.resolve([]),
  61      }),
  62      match: () => Promise.resolve(undefined),
  63      has: () => Promise.resolve(false),
  64      delete: () => Promise.resolve(true),
  65      keys: () => Promise.resolve([]),
  66    };
  67  }
  68  
  69  if (typeof g.IDBDatabase === 'undefined') {
  70    // IndexedDB is not provided; the mlsinterop binary does not use it, but
  71    // idb.mjs may reference `indexedDB` at call time. Leave it undefined so
  72    // attempted use surfaces loudly rather than silently no-op.
  73  }
  74  
  75  // Surface unhandled rejections on stderr so a misrouted Promise from
  76  // inside a stdin callback is visible instead of swallowed.
  77  process.on('unhandledRejection', (reason) => {
  78    process.stderr.write('[mlsinterop] unhandled rejection: ' + (reason && reason.stack || reason) + '\n');
  79  });
  80  
  81  await import('./static/$entry.mjs');
  82