Polyfills for Scriptable

Here are some basic polyfills that replicate browser functionality (on a best effort basis):

Fetch

module.exports = async (
  input,
  { method, headers, body } = {}
) => {
  const request = new Request(input);
  request.method = method ?? 'GET';
  request.body = body;
  request.headers = headers;
  const response = await request.load();
  const responseMetadata = request.response;
  const status = responseMetadata.statusCode;
  const ok = status >= 200 && status <= 299;
  const json = () =>
    JSON.parse(response.toRawString());
  const text = () => response.toRawString();
  const arrayBuffer = () =>
    Uint8Array.from(response.getBytes())
      .buffer;
  return {
    ...responseMetadata,
    text,
    json,
    arrayBuffer,
    ok,
    status,
    response,
  };
};

setTimeout

module.exports = (
  callback,
  interval = 0,
  ...params
) =>
  Timer.schedule(interval, false, () =>
    callback(...params)
  );

setInterval

module.exports = (
  callback,
  interval = 0,
  ...params
) =>
  Timer.schedule(interval, true, () =>
    callback(...params)
  );

clearTimeout / Interval

module.exports = (timer) => timer.invalidate();

Promise.allSettled

module.exports = (promises) =>
  Promise.all(
    promises.map((promise) =>
      Promise.resolve(promise)
        .then((value) => ({
          status: 'fulfilled',
          value,
        }))
        .catch((reason) => ({
          status: 'rejected',
          reason,
        }))
    )
  );

Polyfill loader

module.exports = (global) => {
  global.fetch =
    global.fetch ?? importModule('fetch');
  global.setTimeout =
    global.setTimeout ??
    importModule('setTimeout');
  global.setInterval =
    global.setInterval ??
    importModule('setInterval');
  global.clearTimeout =
    global.clearTimeout ??
    importModule('clearTimeout');
  global.clearInterval =
    global.clearInterval ??
    importModule('clearInterval');
  global.require =
    global.require ?? importModule;
  global.Promise.allSettled =
    global.Promise.allSettled ??
    importModule('promiseAllSettled');
};

Polyfill loader usage

importModule('polyfill')(this);
// your script
2 Likes

I also use an additional polyfill for localStorage which uses a modified version of the store module I posted here a while back, will share it if anyone is interested

2 Likes

i’m interested in how well the time-based one work. For example, do you need to keep it in the foreground?

1 Like

I’d love to see the localStorage polyfill…

Think it might work with any of these storage libraries? IndexedDB API - Web APIs | MDN