runtimeSource

tcmts.cli.web.runtimeSource · #45fsqpdj · term · runs anywhere

import { CODEC } from "#codebase/tcmts/cli/web";

export const runtimeSource = `import { encode, decode } from ${JSON.stringify(CODEC)};

const callListeners = new Set();
/** Run \`f\` after each server function call this page makes; returns an unsubscribe function. */
export function onServerCall(f) {
  callListeners.add(f);
  return () => callListeners.delete(f);
}

/** This page's build was retired: reload to get the current version. */
export function reloadOutdated() {
  if (!globalThis.__tcmReloading) {
    globalThis.__tcmReloading = true;
    location.reload();
  }
  return new Promise(() => {});
}

/** A function that runs on the server, called by hash. */
export function remote(hash, name, callable) {
  const fn = async (...args) => {
    if (!callable) throw new Error(name + " runs on the server and this app doesn't call it from the browser");
    let res;
    try {
      res = await fetch((globalThis.__TCM_BASE__ ?? "") + "/__tcm/rpc/" + hash, { method: "POST", headers: { "content-type": "application/json" }, body: encode(args, "arguments to " + name) });
    } catch (e) {
      throw new Error(name + ": the server is unreachable (" + e.message + ")");
    }
    // this page's build was retired: reload to get the current version
    if (res.status === 410) return reloadOutdated();
    const body = decode(await res.text());
    // whether it worked or not, the call may have changed what the server shows
    for (const f of callListeners) f();
    if (!res.ok) {
      const err = new Error(body && body.message ? body.message : res.statusText);
      err.name = body && body.name ? body.name : "RemoteError";
      err.status = res.status;
      throw err;
    }
    return body;
  };
  Object.defineProperty(fn, "name", { value: name });
  return fn;
}

/** A server-side async generator, iterated from the browser: chunks arrive as they're produced. */
export function remoteStream(hash, name) {
  const fn = (...args) => ({
    [Symbol.asyncIterator]() {
      const stop = new AbortController();
      const decoder = new TextDecoder();
      let reader;
      let buf = "";
      const opened = (async () => {
        const res = await fetch((globalThis.__TCM_BASE__ ?? "") + "/__tcm/rpc/" + hash, { method: "POST", headers: { "content-type": "application/json" }, body: encode(args, "arguments to " + name), signal: stop.signal });
        if (res.status === 410 && !globalThis.__tcmReloading) {
          globalThis.__tcmReloading = true;
          location.reload();
          await new Promise(() => {});
        }
        if (!res.ok) {
          const body = decode(await res.text());
          const err = new Error(body && body.message ? body.message : res.statusText);
          err.name = body && body.name ? body.name : "RemoteError";
          err.status = res.status;
          throw err;
        }
        reader = res.body.getReader();
      })();
      return {
        async next() {
          await opened;
          for (;;) {
            const nl = buf.indexOf("\\n");
            if (nl >= 0) {
              const msg = decode(buf.slice(0, nl));
              buf = buf.slice(nl + 1);
              if (msg.e) {
                const err = new Error(msg.e.message);
                err.name = msg.e.name;
                err.status = msg.e.status;
                throw err;
              }
              if (msg.done) return { done: true, value: undefined };
              return { done: false, value: msg.v };
            }
            const { value, done } = await reader.read();
            if (done) return { done: true, value: undefined };
            buf += decoder.decode(value, { stream: true });
          }
        },
        async return() {
          stop.abort();
          return { done: true, value: undefined };
        },
      };
    },
  });
  Object.defineProperty(fn, "name", { value: name });
  return fn;
}
`

uses

used by