worker/registry.js

// Convention: createFooBar is exported from ./create_foo_bar.js (one level up from
// this directory). Only "create*" names are allowed through, since the function name
// is used to build a dynamic import path. FILE_OVERRIDES covers exceptions, e.g. a
// create* function that lives alongside a related one in a differently-named file.
const NAME_PATTERN = /^create[A-Z]\w*$/;
const FILE_OVERRIDES = {
  createMapEnrichment: "create_location",
  createHorse: "create_animal",
  createDog: "create_animal",
};

function toFileName(functionName) {
  if (FILE_OVERRIDES[functionName]) {
    return FILE_OVERRIDES[functionName];
  }
  return functionName.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
}

/**
 * Dynamically imports the module for a create* function and invokes it with args.
 * Runs identically on the main thread or inside a worker.
 *
 * @param {String} functionName e.g. "createLocation"
 * @param {Object} [args] the single named-parameters object the function expects
 * @returns {*} whatever the create* function returns
 */
export async function callCreate(functionName, args) {
  if (!NAME_PATTERN.test(functionName)) {
    console.error(`${functionName} is not a create* function`);
    return null;
  }
  const fileName = toFileName(functionName);
  const module = await import(`../${fileName}.js`);
  const fn = module[functionName];
  if (typeof fn !== "function") {
    console.error(`${fn} is not a function`);
    return null;
  }
  const result = fn(args);
  return result;
}

/**
 * Expands a shimmed location (see createLocation's `shim` option) in place, by guid. Must
 * run in the same worker that originally created the location, since it looks up the live
 * location object (and its LocationTemplate) by guid in that worker's registries.
 *
 * @param {String} guid the guid of a location previously produced by createLocation
 * @returns {Object|null} the same canonical location object, fully expanded, or null if this
 *   worker has no record of that guid
 */
export async function callExpand(guid) {
  const { expandShim } = await import("../create_location.js");
  return expandShim(guid);
}

/**
 * Looks up the canonical location object by guid, without expanding a shim.
 *
 * @param {String} guid the guid of a location previously produced by createLocation
 * @returns {Object|null} the canonical location object, or null if this worker has no
 *   record of that guid
 */
export async function callGetLocation(guid) {
  const { getLocation } = await import("../create_location.js");
  return getLocation(guid);
}

/**
 * Repopulates the worker's location registries from a previously-generated location tree
 * (or array of trees) — see registerLocations' own doc comment for why this is needed.
 *
 * @param {Object|Object[]} locations one location tree, or an array of them
 */
export async function callRegisterLocations(locations) {
  const { registerLocations } = await import("../create_location.js");
  return registerLocations(locations);
}