defineExoClass = (
  tag,
  interfaceGuard,
  init,
  methods,
  options = {},
) => {
  harden(methods);
  const {
    finish = undefined,
    receiveAmplifier = undefined,
    receiveInstanceTester = undefined,
  } = options;
  receiveAmplifier === undefined ||
    Fail`Only facets of an exo class kit can be amplified ${q(tag)}`;

  /** @type {WeakMap<M, import('./types.js').ClassContext<ReturnType<I>, M>>} */
  const contextMap = new WeakMap();
  const proto = defendPrototype(
    tag,
    self => /** @type {any} */ (contextMap.get(self)),
    methods,
    true,
    interfaceGuard,
  );
  let instanceCount = 0;
  /**
   * @param  {Parameters<I>} args
   */
  const makeInstance = (...args) => {
    // Be careful not to freeze the state record
    const state = seal(init(...args));
    instanceCount += 1;
    const self = makeSelf(proto, instanceCount);

    // Be careful not to freeze the state record
    /** @type {import('./types.js').ClassContext<ReturnType<I>,M>} */
    const context = freeze({ state, self });
    contextMap.set(self, context);
    if (finish) {
      finish(context);
    }
    return self;
  };

  if (receiveInstanceTester) {
    /** @type {IsInstance} */
    const isInstance = (exo, facetName = undefined) => {
      facetName === undefined ||
        Fail`facetName can only be used with an exo class kit: ${q(
          tag,
        )} has no facet ${q(facetName)}`;
      return contextMap.has(exo);
    };
    harden(isInstance);
    receiveInstanceTester(isInstance);
  }

  return harden(makeInstance);
}
@template {(...args: any[]) => any} I init function
@template {Methods} M methods
@param {string} tag
@param {ExoClassInterfaceGuard<M> | undefined} interfaceGuard
@param {I} init
@param {ExoClassMethods<M, I>} methods
@param {FarClassOptions<import('./types.js').ClassContext<ReturnType<I>, M>>} [options]
@returns {(...args: Parameters<I>) => Guarded<M>}