@mark1russell7/coeval 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -3082,6 +3082,28 @@ const NO_ANSWER = {
3082
3082
  /** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
3083
3083
  const WITHOUT_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
3084
3084
  const WITH_HOPS = /* @__PURE__ */ Object.freeze({ hops: true });
3085
+ /**
3086
+ * This function gives the path of a kept read (READS.CLOSURE). The evaluation did not ask for the read. Thus the read
3087
+ * does not step into a host thing, and the path ends at the first host thing. The host gives a new thing for a new state.
3088
+ * A reference in the data reads the context of its reader, thus the function then gives `undefined`.
3089
+ *
3090
+ * @param nav - The world and the code of the evaluators.
3091
+ * @param root - The context at the root of the evaluation.
3092
+ * @param path - The root and the keys of the static read.
3093
+ * @returns The path of the kept read, or `undefined` for a reference on the way.
3094
+ */
3095
+ function keptPath(nav, root, path) {
3096
+ const adopt = nav.adopt ?? ((value) => value);
3097
+ let here = adopt(root[path[0]]);
3098
+ for (let index = 1;; index += 1) {
3099
+ if (isHostThing(here)) return index === path.length ? path : path.slice(0, index);
3100
+ if (nav.plain !== true && isRef(here)) return void 0;
3101
+ if (index >= path.length) return path;
3102
+ const reached = derefStep(nav, here, path[index]);
3103
+ if (reached === void 0) return path;
3104
+ here = adopt(reached.thing);
3105
+ }
3106
+ }
3085
3107
  /** This function makes the record of the reads of an evaluation, when the host gives a list. */
3086
3108
  function readingOf(list, root) {
3087
3109
  return list === void 0 ? void 0 : {
@@ -3188,13 +3210,45 @@ var Session = class {
3188
3210
  if (compiled !== void 0 && !hops && compiled.nav === this.machine) return compiled.resolve(ctx);
3189
3211
  return resolve(this.machine, ctx, path, hops ? WITH_HOPS : WITHOUT_HOPS);
3190
3212
  }
3191
- keepsContext() {
3213
+ keepsContext(ctx, reads) {
3192
3214
  const reading = this.reading;
3193
3215
  if (reading === void 0 || reading.kept) return;
3216
+ const known = ctx === void 0 ? void 0 : reads?.();
3217
+ if (ctx !== void 0 && known !== void 0 && known.every((read) => this.keepRead(reading, ctx, read))) return;
3194
3218
  reading.kept = true;
3195
3219
  this.keep(reading, reading.self ? ROOTS.filter((root) => root !== "self") : ROOTS);
3196
3220
  }
3197
3221
  /**
3222
+ * This function adds the kept read of one static read of a closure (READS.CLOSURE). The read resolves from the
3223
+ * context at the root, one time in an evaluation. A name that a kind binds in the kept context gives no read,
3224
+ * because the reads of its binding are already in the list.
3225
+ *
3226
+ * @returns `false` when the path meets a reference in the data. Then the closure keeps each whole root.
3227
+ */
3228
+ keepRead(reading, ctx, read) {
3229
+ const root = read.root;
3230
+ const exact = [root, ...read.keys];
3231
+ if (!isFree(reading.root, ctx, exact)) {
3232
+ if (root === "self" && !reading.self) this.keep(reading, ["self"]);
3233
+ return true;
3234
+ }
3235
+ if (root === "self" && reading.self) return true;
3236
+ const path = keptPath(this.machine, reading.root, read.exact ? exact : [root]);
3237
+ if (path === void 0) return false;
3238
+ const paths = reading.paths ??= /* @__PURE__ */ new Set();
3239
+ const text = JSON.stringify(path);
3240
+ if (paths.has(text) || paths.has(JSON.stringify([root]))) return true;
3241
+ paths.add(text);
3242
+ if (path.length === 1 && root === "self") reading.self = true;
3243
+ reading.list.push({
3244
+ path,
3245
+ reached: readAgain(this.machine, reading.root, path, false).reached,
3246
+ free: true,
3247
+ kept: true
3248
+ });
3249
+ return true;
3250
+ }
3251
+ /**
3198
3252
  * This function adds a read of each whole root to the list, from the context at the root (READS.KEPT). An exception
3199
3253
  * of the host gives the error value of the read, because the evaluation did not ask for the read.
3200
3254
  */
@@ -7539,7 +7593,7 @@ function formulaCode(profile) {
7539
7593
  }, rt);
7540
7594
  };
7541
7595
  }),
7542
- "formula.fn": compiled(({ value, data }) => {
7596
+ "formula.fn": compiled(({ value, data, doc, machine }) => {
7543
7597
  const params = isRecord(data) ? data["params"] : void 0;
7544
7598
  if (!Array.isArray(params) || !params.every((param) => typeof param === "string") || new Set(params).size !== params.length) {
7545
7599
  const bad = fail("#VALUE!", "The params of fn are a list of different texts.");
@@ -7547,8 +7601,19 @@ function formulaCode(profile) {
7547
7601
  }
7548
7602
  const names = params;
7549
7603
  const body = partsOf$1(value)["body"];
7604
+ let known = null;
7605
+ const reads = () => {
7606
+ if (known !== null) return known;
7607
+ try {
7608
+ const facts = factsOf(machine, doc);
7609
+ known = facts.pure ? facts.reads : void 0;
7610
+ } catch {
7611
+ known = void 0;
7612
+ }
7613
+ return known;
7614
+ };
7550
7615
  return (ctx, rt) => {
7551
- rt.keepsContext?.();
7616
+ rt.keepsContext?.(ctx, reads);
7552
7617
  const closure = (...args) => {
7553
7618
  const scope = { ...ctx.names };
7554
7619
  names.forEach((name, index) => setOwn$1(scope, name, args[index]));
@@ -8762,6 +8827,42 @@ function resolveFully(machine, ctx, path, options) {
8762
8827
  * mock, the effect gives its payload (RUN.EVAL).
8763
8828
  */
8764
8829
  function cause(env, ctx, kind, payload, place) {
8830
+ const effect = recordEffect(env, ctx, kind, payload, place);
8831
+ if ("ok" in effect) return effect;
8832
+ effect.outcome = answer(env.perform, effect, place);
8833
+ return effect.outcome;
8834
+ }
8835
+ /**
8836
+ * This function causes the effect of an evaluator. In a run, the host does each effect, thus only a runner can cause
8837
+ * one. The effect then gives `#CALC!` before the test of its capability (RUN.NEEDS-RUNNER).
8838
+ */
8839
+ function causeFromEvaluator(env, ctx, kind, payload, place) {
8840
+ const text = typeof kind === "string" ? kind : kind.join(".");
8841
+ if (env.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
8842
+ return cause(env, ctx, kind, payload, place);
8843
+ }
8844
+ /**
8845
+ * This function causes the effect of a runner. In an evaluation, the mock answers it at once. In a run, the host does
8846
+ * it, and a promise from the host makes the steps wait (RUN.SEQ). An effect whose wait a stop ends keeps no outcome
8847
+ * (RUN.OUTCOME).
8848
+ */
8849
+ function* causeAndWait(env, ctx, kind, payload, place) {
8850
+ if (!env.running) return cause(env, ctx, kind, payload, place);
8851
+ const effect = recordEffect(env, ctx, kind, payload, place);
8852
+ if ("ok" in effect) return effect;
8853
+ const perform = env.perform;
8854
+ if (perform === void 0) return effect.outcome = ok(payload);
8855
+ let given;
8856
+ try {
8857
+ given = perform(effect);
8858
+ } catch (error) {
8859
+ return effect.outcome = hostError(effect, place, error);
8860
+ }
8861
+ if (!(given instanceof Promise)) return effect.outcome = ok(given);
8862
+ return effect.outcome = yield given.then((value) => ok(value), (error) => hostError(effect, place, error));
8863
+ }
8864
+ /** This function checks the capability of an effect, and records the effect with its address (RUN.CAP, RUN.ADDRESS). */
8865
+ function recordEffect(env, ctx, kind, payload, place) {
8765
8866
  const keys = typeof kind === "string" ? kind.split(".") : kind;
8766
8867
  const text = keys.join(".");
8767
8868
  const machine = env.machine;
@@ -8781,8 +8882,18 @@ function cause(env, ctx, kind, payload, place) {
8781
8882
  address: `${env.scope.prefix}${at}#${count}`
8782
8883
  };
8783
8884
  env.effects.push(effect);
8784
- effect.outcome = answer(env.perform, effect, place);
8785
- return effect.outcome;
8885
+ return effect;
8886
+ }
8887
+ /** This function gives the outcome of an effect that the host failed. A `Failure` gives its own error value (RUN.CAUSE). */
8888
+ function hostError(effect, place, error) {
8889
+ if (error instanceof Failure) return placed({
8890
+ ok: false,
8891
+ error: error.error
8892
+ }, place);
8893
+ return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, {
8894
+ place,
8895
+ cause: error
8896
+ });
8786
8897
  }
8787
8898
  /**
8788
8899
  * This function gives the answer of the mock to an effect. A throw gives `#CALC!` with the cause, and a `Failure`
@@ -8794,14 +8905,7 @@ function answer(perform, effect, place) {
8794
8905
  try {
8795
8906
  given = perform(effect);
8796
8907
  } catch (error) {
8797
- if (error instanceof Failure) return placed({
8798
- ok: false,
8799
- error: error.error
8800
- }, place);
8801
- return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, {
8802
- place,
8803
- cause: error
8804
- });
8908
+ return hostError(effect, place, error);
8805
8909
  }
8806
8910
  if (!(given instanceof Promise)) return ok(given);
8807
8911
  given.catch(() => void 0);
@@ -8872,7 +8976,7 @@ function evalInput(env, seen, at) {
8872
8976
  machine: env.machine,
8873
8977
  ...at.address ? { address: true } : {},
8874
8978
  resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
8875
- effect: (kind, payload) => cause(env, at.ctx, kind, payload, at.place),
8979
+ effect: (kind, payload) => causeFromEvaluator(env, at.ctx, kind, payload, at.place),
8876
8980
  fill
8877
8981
  };
8878
8982
  }
@@ -8899,67 +9003,116 @@ function runInput(env, seen, at) {
8899
9003
  place: at.place,
8900
9004
  machine: env.machine,
8901
9005
  resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
8902
- effect: function* (kind, payload) {
8903
- return cause(env, at.ctx, kind, payload, at.place);
8904
- },
9006
+ effect: (kind, payload) => causeAndWait(env, at.ctx, kind, payload, at.place),
8905
9007
  all: (list, options) => together(env, list, options),
8906
9008
  fill
8907
9009
  };
8908
9010
  }
8909
9011
  /**
8910
9012
  * This function evaluates parts for `all` (RUN.ALL). The parts start in their order, up to the limit, and each part
8911
- * goes on until it ends or waits. In an evaluation, a part that waits does not end, thus it keeps its place in the
8912
- * limit. With `failFast`, the first failure stops the parts that wait, and the later parts do not start
8913
- * (RUN.FAIL-FAST). When no part failed and a part waits, the steps wait, and the drive gives `#CALC!`.
9013
+ * goes on until it ends or waits. A part that waits keeps its place in the limit. With `failFast`, the first failure
9014
+ * stops the parts that wait, and the later parts do not start (RUN.FAIL-FAST).
9015
+ *
9016
+ * When a part waits, the steps wait for the next part that settles. The journal of the host can choose that part
9017
+ * (RUN.REPLAY), and it gets each part that settled (RUN.RECORD). In an evaluation, the drive stops at the first wait
9018
+ * and gives `#CALC!`. In a run, the host settles the wait, and the part goes on in its own scope.
8914
9019
  *
8915
9020
  * Each part has its own scope of addresses. Its prefix names the use of `all` in the scope around it, and the part
8916
9021
  * (RUN.ADDRESS).
8917
9022
  */
8918
9023
  function* together(env, list, options = {}) {
8919
9024
  const limit = Math.max(1, options.concurrency ?? list.length);
9025
+ const failFast = options.failFast === true;
8920
9026
  const results = list.map(() => void 0);
9027
+ const scopes = [];
8921
9028
  const waiting = [];
8922
9029
  const outer = env.scope;
8923
9030
  const address = `${outer.prefix}all${outer.alls}`;
8924
9031
  outer.alls += 1;
8925
9032
  let failed = false;
8926
- for (let index = 0; index < list.length && !failed && waiting.length < limit; index += 1) {
8927
- let result;
8928
- env.scope = {
8929
- prefix: `${address}.${index}/`,
8930
- counts: /* @__PURE__ */ new Map(),
8931
- alls: 0
8932
- };
9033
+ let started = 0;
9034
+ const end = (index, result) => {
9035
+ results[index] = result;
9036
+ if (failFast && !result.ok) failed = true;
9037
+ };
9038
+ /** This function gives a part its input in its own scope, and keeps it in the list when it waits again. */
9039
+ const goOn = (index, steps, input) => {
9040
+ const around = env.scope;
9041
+ env.scope = scopes[index];
9042
+ let step;
8933
9043
  try {
8934
- const steps = list[index].evaluate();
8935
- const step = steps.next();
8936
- if (step.done === true) result = step.value;
8937
- else waiting.push({
8938
- steps,
8939
- promise: step.value
8940
- });
9044
+ step = input.threw ? steps.throw(input.error) : steps.next(input.value);
8941
9045
  } catch (error) {
8942
- result = thrown(error, "a part");
9046
+ end(index, thrown(error, "a part"));
9047
+ return;
8943
9048
  } finally {
8944
- env.scope = outer;
9049
+ env.scope = around;
8945
9050
  }
8946
- results[index] = result;
8947
- if (result !== void 0 && options.failFast === true && !result.ok) failed = true;
8948
- }
8949
- if (failed) {
8950
- const stopped = fail("#ABORT", "The part stopped: a part before it failed, and the parts run with failFast.");
8951
- for (const item of waiting) try {
8952
- item.steps.return(stopped);
8953
- } catch {}
8954
- return results.map((result) => result ?? stopped);
9051
+ if (step.done === true) {
9052
+ end(index, step.value);
9053
+ return;
9054
+ }
9055
+ const waiter = {
9056
+ index,
9057
+ steps,
9058
+ next: Promise.resolve()
9059
+ };
9060
+ waiter.next = step.value.then((value) => ({
9061
+ waiter,
9062
+ value,
9063
+ threw: false
9064
+ }), (error) => ({
9065
+ waiter,
9066
+ error,
9067
+ threw: true
9068
+ }));
9069
+ waiting.push(waiter);
9070
+ };
9071
+ /** This function starts the next parts in their order, while the limit permits it and no part failed. */
9072
+ const startMore = () => {
9073
+ while (!failed && waiting.length < limit && started < list.length) {
9074
+ const index = started;
9075
+ started += 1;
9076
+ scopes[index] = {
9077
+ prefix: `${address}.${index}/`,
9078
+ counts: /* @__PURE__ */ new Map(),
9079
+ alls: 0
9080
+ };
9081
+ const around = env.scope;
9082
+ env.scope = scopes[index];
9083
+ let steps;
9084
+ try {
9085
+ steps = list[index].evaluate();
9086
+ } catch (error) {
9087
+ end(index, thrown(error, "a part"));
9088
+ continue;
9089
+ } finally {
9090
+ env.scope = around;
9091
+ }
9092
+ goOn(index, steps, { threw: false });
9093
+ }
9094
+ };
9095
+ startMore();
9096
+ while (waiting.length > 0 && !failed) {
9097
+ const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index));
9098
+ const pick = chosen === void 0 ? void 0 : waiting.find((waiter) => waiter.index === chosen);
9099
+ const resumed = yield pick === void 0 ? Promise.race(waiting.map((waiter) => waiter.next)) : pick.next;
9100
+ env.journal?.settled?.(address, resumed.waiter.index);
9101
+ waiting.splice(waiting.indexOf(resumed.waiter), 1);
9102
+ goOn(resumed.waiter.index, resumed.waiter.steps, resumed);
9103
+ startMore();
8955
9104
  }
8956
- if (waiting.length > 0) yield Promise.race(waiting.map((item) => item.promise));
8957
- return results;
9105
+ if (!failed) return results;
9106
+ const stopped = fail("#ABORT", "The part stopped: a part before it failed, and the parts run with failFast.");
9107
+ for (const waiter of waiting) try {
9108
+ waiter.steps.return(stopped);
9109
+ } catch {}
9110
+ return results.map((result) => result ?? stopped);
8958
9111
  }
8959
9112
  /**
8960
9113
  * This function evaluates a document by the rules, without a compilation. It is the reference of the plans: a plan,
8961
- * a traced plan and the second tier of a language must give its results. It covers evaluations, not runs: an effect
8962
- * gives the answer of the mock or its payload, and a part that waits gives `#CALC!`.
9114
+ * a traced plan and the second tier of a language must give its results. In an evaluation, an effect gives the answer
9115
+ * of the mock or its payload, and a part that waits gives `#CALC!`. `referenceRun` reads the rules of a run.
8963
9116
  *
8964
9117
  * The verb of the options goes up the ladder at each document, as in `prepare`. Each verb has the same rules: the
8965
9118
  * ladder, the contract of the kind and the sealed parts (VERB.AGREE).
@@ -8971,12 +9124,43 @@ function* together(env, list, options = {}) {
8971
9124
  * @returns The result.
8972
9125
  */
8973
9126
  function reference(machine, doc, context = makeContext(), options = {}) {
8974
- const env = {
9127
+ const env = envOf(machine, options, false);
9128
+ try {
9129
+ return within(env, () => evaluateAt(env, rootAt(machine, doc, context, options)));
9130
+ } catch (error) {
9131
+ return placed(thrown(error, "the reference"), []);
9132
+ }
9133
+ }
9134
+ /**
9135
+ * This function starts a run of a document by the rules, without a compilation (REF.RUN). The host does each effect of a runner,
9136
+ * and a step waits when the host waits (RUN.SEQ). `all` settles the waits in the order of the host, or in the order
9137
+ * of the journal of a replay (RUN.REPLAY). An effect of an evaluator gives `#CALC!` (RUN.NEEDS-RUNNER). A signal that
9138
+ * aborts stops the run with `#ABORT` (RUN.ABORT).
9139
+ *
9140
+ * The part of the run before the first wait happens at once, in the call (RUN.EAGER). `agreeRun` compares the run of
9141
+ * a plan with this run.
9142
+ *
9143
+ * @param machine - The world, the evaluators and the runners.
9144
+ * @param doc - The document.
9145
+ * @param context - The names and the capabilities at the root.
9146
+ * @param options - The options of `reference`, the host and a signal.
9147
+ * @returns The result, when the run ends.
9148
+ */
9149
+ function referenceRun(machine, doc, context = makeContext(), options = {}) {
9150
+ const env = envOf(machine, options, true);
9151
+ return driveRun(env, stepsAt(env, rootAt(machine, doc, context, options)), options.signal).catch((error) => placed(thrown(error, "the reference"), []));
9152
+ }
9153
+ /** This function makes the state of the reference for one evaluation or one run. */
9154
+ function envOf(machine, options, running) {
9155
+ const perform = options.perform;
9156
+ return {
8975
9157
  machine,
8976
9158
  definitions: options.definitions ?? {},
8977
9159
  effects: options.effects ?? [],
8978
9160
  maxDepth: machine.maxDepth ?? 1e3,
8979
- perform: options.perform,
9161
+ perform,
9162
+ journal: perform === void 0 ? void 0 : perform[JOURNAL],
9163
+ running,
8980
9164
  verb: options.verb ?? "eval",
8981
9165
  scope: {
8982
9166
  prefix: "",
@@ -8984,26 +9168,66 @@ function reference(machine, doc, context = makeContext(), options = {}) {
8984
9168
  alls: 0
8985
9169
  }
8986
9170
  };
9171
+ }
9172
+ /** This function gives the root position of a document. A context without a world gets the world of the machine. */
9173
+ function rootAt(machine, doc, context, options) {
8987
9174
  const ctx = context.world === null ? {
8988
9175
  ...context,
8989
9176
  world: worldThing(machine.world)
8990
9177
  } : context;
9178
+ return {
9179
+ value: doc,
9180
+ place: [],
9181
+ declared: options.declaredType,
9182
+ depth: 0,
9183
+ address: false,
9184
+ ctx
9185
+ };
9186
+ }
9187
+ /**
9188
+ * This function does one synchronous step of an evaluation or a run. A node of a tree reads the definitions of the
9189
+ * evaluation for its equations (TREE.LADDER). Thus the state of the evaluation is the innermost one in the step.
9190
+ */
9191
+ function within(env, step) {
8991
9192
  activeEnvs.push(env);
8992
9193
  try {
8993
- return evaluateAt(env, {
8994
- value: doc,
8995
- place: [],
8996
- declared: options.declaredType,
8997
- depth: 0,
8998
- address: false,
8999
- ctx
9000
- });
9001
- } catch (error) {
9002
- return placed(thrown(error, "the reference"), []);
9194
+ return step();
9003
9195
  } finally {
9004
9196
  activeEnvs.pop();
9005
9197
  }
9006
9198
  }
9199
+ /** The mark of a signal that aborted. */
9200
+ const STOPPED$1 = /* @__PURE__ */ Symbol("stopped");
9201
+ /**
9202
+ * This function drives the steps of a run. Each step waits for its promise. A rejection goes into the steps as an
9203
+ * exception. A signal that aborts stops the steps with `#ABORT`, also before the first step (RUN.ABORT).
9204
+ */
9205
+ async function driveRun(env, steps, signal) {
9206
+ const stop = () => {
9207
+ const aborted = fail("#ABORT", `The host stopped the run: ${describe(signal?.reason ?? "no reason")}`, { cause: signal?.reason });
9208
+ try {
9209
+ within(env, () => steps.return(aborted));
9210
+ } catch {}
9211
+ return aborted;
9212
+ };
9213
+ if (signal?.aborted === true) return stop();
9214
+ const stopping = signal === void 0 ? void 0 : new Promise((resolve) => {
9215
+ signal.addEventListener("abort", () => resolve(STOPPED$1), { once: true });
9216
+ });
9217
+ let step = within(env, () => steps.next());
9218
+ while (step.done !== true) {
9219
+ let input;
9220
+ try {
9221
+ input = await (stopping === void 0 ? step.value : Promise.race([step.value, stopping]));
9222
+ } catch (error) {
9223
+ step = within(env, () => steps.throw(error));
9224
+ continue;
9225
+ }
9226
+ if (input === STOPPED$1) return stop();
9227
+ step = within(env, () => steps.next(input));
9228
+ }
9229
+ return step.value;
9230
+ }
9007
9231
  /** The evaluations of the reference that run at this time, the innermost last. */
9008
9232
  const activeEnvs = [];
9009
9233
  const refNodes = /* @__PURE__ */ new WeakMap();
@@ -9437,6 +9661,101 @@ function valuesOfStrict(parts) {
9437
9661
  return { values };
9438
9662
  }
9439
9663
  //#endregion
9664
+ //#region src/replay.ts
9665
+ /**
9666
+ * This function makes a recorder (RUN.RECORD). The recorder gives each effect to the function `perform` of the host.
9667
+ * Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
9668
+ * also keeps the order in which the waits of each `all` settle.
9669
+ *
9670
+ * @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
9671
+ * @returns The function `perform` for the run, and the log.
9672
+ */
9673
+ function recorder(perform) {
9674
+ const seen = [];
9675
+ const settles = [];
9676
+ const journal = { settled: (all, item) => {
9677
+ settles.push({
9678
+ all,
9679
+ item
9680
+ });
9681
+ } };
9682
+ const host = (effect) => {
9683
+ const entry = {
9684
+ effect,
9685
+ waited: false
9686
+ };
9687
+ seen.push(entry);
9688
+ const answer = perform === void 0 ? effect.payload : perform(effect);
9689
+ if (answer instanceof Promise) entry.waited = true;
9690
+ return answer;
9691
+ };
9692
+ return {
9693
+ perform: Object.assign(host, { [JOURNAL]: journal }),
9694
+ log: () => ({
9695
+ effects: seen.map(({ effect, waited }) => ({
9696
+ address: effect.address ?? "",
9697
+ kind: effect.kind,
9698
+ payload: effect.payload,
9699
+ waited,
9700
+ ...effect.outcome === void 0 ? {} : { outcome: effect.outcome }
9701
+ })),
9702
+ settles: [...settles]
9703
+ })
9704
+ };
9705
+ }
9706
+ /**
9707
+ * This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
9708
+ * recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
9709
+ * recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
9710
+ *
9711
+ * A replayer keeps its place in the order of the waits, thus each run needs a new replayer.
9712
+ *
9713
+ * @param log - The log of a recorder.
9714
+ * @param options - `wait: false` for an evaluation.
9715
+ * @returns The function `perform` for the replay.
9716
+ */
9717
+ function replayer(log, options = {}) {
9718
+ const wait = options.wait !== false;
9719
+ const entries = /* @__PURE__ */ new Map();
9720
+ for (const entry of log.effects) entries.set(entry.address, entry);
9721
+ const order = /* @__PURE__ */ new Map();
9722
+ for (const settle of log.settles) {
9723
+ const items = order.get(settle.all);
9724
+ if (items === void 0) order.set(settle.all, [settle.item]);
9725
+ else items.push(settle.item);
9726
+ }
9727
+ const journal = { next: (all, waiting) => {
9728
+ const items = order.get(all);
9729
+ const item = items?.[0];
9730
+ if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
9731
+ items.shift();
9732
+ return item;
9733
+ } };
9734
+ const perform = (effect) => {
9735
+ const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
9736
+ const wrong = mismatch(effect, entry);
9737
+ if (wrong !== void 0 || entry === void 0) throw wrong;
9738
+ const outcome = entry.outcome;
9739
+ const failure = outcome === void 0 ? replayError(effect, `The log has no outcome for the effect ${effect.kind} at ${effect.address ?? ""}: the recorded run stopped before the answer.`) : outcome.ok ? void 0 : new Failure(outcome.error);
9740
+ const value = outcome?.ok === true ? outcome.value : void 0;
9741
+ if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
9742
+ if (failure !== void 0) throw failure;
9743
+ return value;
9744
+ };
9745
+ return Object.assign(perform, { [JOURNAL]: journal });
9746
+ }
9747
+ /** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
9748
+ function mismatch(effect, entry) {
9749
+ const at = effect.address ?? "";
9750
+ if (entry === void 0) return replayError(effect, `The log has no effect at the address ${at}, and the run asks for the effect ${effect.kind}.`);
9751
+ if (entry.kind !== effect.kind) return replayError(effect, `The log has the effect ${entry.kind} at the address ${at}, and the run asks for the effect ${effect.kind}.`);
9752
+ if (!docEquals(entry.payload, effect.payload)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
9753
+ }
9754
+ /** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
9755
+ function replayError(effect, reason) {
9756
+ return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
9757
+ }
9758
+ //#endregion
9440
9759
  //#region src/agree.ts
9441
9760
  /** The tiers that `agree` compares when its options name none. */
9442
9761
  const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
@@ -9624,6 +9943,113 @@ function agree(machine, doc, context = makeContext(), options = {}) {
9624
9943
  effects
9625
9944
  };
9626
9945
  }
9946
+ /**
9947
+ * This function wraps the function `perform` of a run. The wrapper keeps each effect and each wait of `all` that
9948
+ * settles. It gives each effect to the inner function, and each call of its journal to the inner journal.
9949
+ */
9950
+ function watched(perform) {
9951
+ const effects = [];
9952
+ const settles = [];
9953
+ const inner = perform[JOURNAL];
9954
+ const settled = (all, item) => {
9955
+ settles.push({
9956
+ all,
9957
+ item
9958
+ });
9959
+ inner?.settled?.(all, item);
9960
+ };
9961
+ const journal = inner?.next === void 0 ? { settled } : {
9962
+ settled,
9963
+ next: (all, waiting) => inner.next?.(all, waiting)
9964
+ };
9965
+ const host = (effect) => {
9966
+ effects.push(effect);
9967
+ return perform(effect);
9968
+ };
9969
+ return {
9970
+ perform: Object.assign(host, { [JOURNAL]: journal }),
9971
+ effects,
9972
+ settles
9973
+ };
9974
+ }
9975
+ function settleText(settle) {
9976
+ return settle === void 0 ? "no item" : `the item ${settle.item} of ${settle.all}`;
9977
+ }
9978
+ /** This function gives the first difference of two orders of the waits of `all`, or `undefined` when they agree. */
9979
+ function settlesDiffer(a, b) {
9980
+ for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
9981
+ const x = a[index];
9982
+ const y = b[index];
9983
+ if (x?.all !== y?.all || x?.item !== y?.item) return `The wait ${index} settles ${settleText(x)} in the plan, and ${settleText(y)} in the tier.`;
9984
+ }
9985
+ }
9986
+ /**
9987
+ * This function compares the run of a document in a plan with its run in other tiers (RUN.AGREES). First the plan
9988
+ * starts the run with a recorder around the host. Then each tier replays the log of that run. These tiers are the
9989
+ * reference run, the traced run, a second run of the plan and the plan with the other choice of folds.
9990
+ *
9991
+ * The rules of `agree` compare the results and the effects, with their addresses and outcomes. A tier must also
9992
+ * settle the waits of each `all` in the order of the plan, thus a replay keeps the order of the waits.
9993
+ *
9994
+ * @param machine - The world, the evaluators and the runners.
9995
+ * @param doc - The document.
9996
+ * @param context - The names and the capabilities at the root.
9997
+ * @param options - The tiers, the definitions for the reference, the rules of the comparison and the host.
9998
+ * @returns The result of the plan, or the first tier that disagrees and the reason.
9999
+ */
10000
+ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
10001
+ const declared = {
10002
+ ...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
10003
+ ...options.verb === void 0 ? {} : { verb: options.verb }
10004
+ };
10005
+ const observe = options.observe;
10006
+ observe?.();
10007
+ const folds = observe === void 0;
10008
+ const plan = prepare(machine, doc, {
10009
+ ...declared,
10010
+ fold: folds
10011
+ });
10012
+ const recording = recorder(options.perform);
10013
+ const first = watched(recording.perform);
10014
+ const result = await plan.run(context, { perform: first.perform });
10015
+ const saw = observe?.();
10016
+ const log = recording.log();
10017
+ for (const tier of options.tiers ?? DEFAULT_TIERS) {
10018
+ const replay = watched(replayer(log));
10019
+ const host = { perform: replay.perform };
10020
+ let other;
10021
+ if (tier === "reference") other = await referenceRun(machine, doc, context, {
10022
+ ...declared,
10023
+ ...host,
10024
+ ...options.definitions === void 0 ? {} : { definitions: options.definitions }
10025
+ });
10026
+ else if (tier === "traced") other = (await run(machine, doc, context, {
10027
+ ...declared,
10028
+ ...host
10029
+ })).result;
10030
+ else if (tier === "folds") other = await prepare(machine, doc, {
10031
+ ...declared,
10032
+ fold: !folds
10033
+ }).run(context, host);
10034
+ else other = await plan.run(context, host);
10035
+ const compareCalls = observe !== void 0 && tier !== "folds";
10036
+ const calls = observe?.();
10037
+ if (overflowed(result) || overflowed(other)) continue;
10038
+ const why = resultDiffers(result, other, options) ?? effectsDiffer(first.effects, replay.effects, options) ?? settlesDiffer(first.settles, replay.settles) ?? (compareCalls && !sameValue(saw, calls, options.equal) ? "The host saw other calls in the tier." : void 0);
10039
+ if (why !== void 0) return {
10040
+ ok: false,
10041
+ tier,
10042
+ plan: result,
10043
+ other,
10044
+ reason: why
10045
+ };
10046
+ }
10047
+ return {
10048
+ ok: true,
10049
+ result,
10050
+ effects: first.effects
10051
+ };
10052
+ }
9627
10053
  //#endregion
9628
10054
  //#region src/generate.ts
9629
10055
  /**
@@ -11839,101 +12265,6 @@ function schedule(machine, options = {}) {
11839
12265
  };
11840
12266
  }
11841
12267
  //#endregion
11842
- //#region src/replay.ts
11843
- /**
11844
- * This function makes a recorder (RUN.RECORD). The recorder gives each effect to the function `perform` of the host.
11845
- * Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
11846
- * also keeps the order in which the waits of each `all` settle.
11847
- *
11848
- * @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
11849
- * @returns The function `perform` for the run, and the log.
11850
- */
11851
- function recorder(perform) {
11852
- const seen = [];
11853
- const settles = [];
11854
- const journal = { settled: (all, item) => {
11855
- settles.push({
11856
- all,
11857
- item
11858
- });
11859
- } };
11860
- const host = (effect) => {
11861
- const entry = {
11862
- effect,
11863
- waited: false
11864
- };
11865
- seen.push(entry);
11866
- const answer = perform === void 0 ? effect.payload : perform(effect);
11867
- if (answer instanceof Promise) entry.waited = true;
11868
- return answer;
11869
- };
11870
- return {
11871
- perform: Object.assign(host, { [JOURNAL]: journal }),
11872
- log: () => ({
11873
- effects: seen.map(({ effect, waited }) => ({
11874
- address: effect.address ?? "",
11875
- kind: effect.kind,
11876
- payload: effect.payload,
11877
- waited,
11878
- ...effect.outcome === void 0 ? {} : { outcome: effect.outcome }
11879
- })),
11880
- settles: [...settles]
11881
- })
11882
- };
11883
- }
11884
- /**
11885
- * This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
11886
- * recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
11887
- * recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
11888
- *
11889
- * A replayer keeps its place in the order of the waits, thus each run needs a new replayer.
11890
- *
11891
- * @param log - The log of a recorder.
11892
- * @param options - `wait: false` for an evaluation.
11893
- * @returns The function `perform` for the replay.
11894
- */
11895
- function replayer(log, options = {}) {
11896
- const wait = options.wait !== false;
11897
- const entries = /* @__PURE__ */ new Map();
11898
- for (const entry of log.effects) entries.set(entry.address, entry);
11899
- const order = /* @__PURE__ */ new Map();
11900
- for (const settle of log.settles) {
11901
- const items = order.get(settle.all);
11902
- if (items === void 0) order.set(settle.all, [settle.item]);
11903
- else items.push(settle.item);
11904
- }
11905
- const journal = { next: (all, waiting) => {
11906
- const items = order.get(all);
11907
- const item = items?.[0];
11908
- if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
11909
- items.shift();
11910
- return item;
11911
- } };
11912
- const perform = (effect) => {
11913
- const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
11914
- const wrong = mismatch(effect, entry);
11915
- if (wrong !== void 0 || entry === void 0) throw wrong;
11916
- const outcome = entry.outcome;
11917
- const failure = outcome === void 0 ? replayError(effect, `The log has no outcome for the effect ${effect.kind} at ${effect.address ?? ""}: the recorded run stopped before the answer.`) : outcome.ok ? void 0 : new Failure(outcome.error);
11918
- const value = outcome?.ok === true ? outcome.value : void 0;
11919
- if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
11920
- if (failure !== void 0) throw failure;
11921
- return value;
11922
- };
11923
- return Object.assign(perform, { [JOURNAL]: journal });
11924
- }
11925
- /** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
11926
- function mismatch(effect, entry) {
11927
- const at = effect.address ?? "";
11928
- if (entry === void 0) return replayError(effect, `The log has no effect at the address ${at}, and the run asks for the effect ${effect.kind}.`);
11929
- if (entry.kind !== effect.kind) return replayError(effect, `The log has the effect ${entry.kind} at the address ${at}, and the run asks for the effect ${effect.kind}.`);
11930
- if (!docEquals(entry.payload, effect.payload)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
11931
- }
11932
- /** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
11933
- function replayError(effect, reason) {
11934
- return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
11935
- }
11936
- //#endregion
11937
12268
  //#region src/providers.ts
11938
12269
  /** The deepest schema that `typeFromSchema` reads. A deeper schema reads as `unknown`. */
11939
12270
  const MAX_SCHEMA_DEPTH = 256;
@@ -12244,6 +12575,6 @@ function typeFromSamples(values, options = {}) {
12244
12575
  return samplesAt(values, options.closed === true, 0);
12245
12576
  }
12246
12577
  //#endregion
12247
- export { ATTRIBUTE_RUNGS, CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, agree, ask, attributeTrace, capKeys, check, checkLatent, checkSteps, checkWorld, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, print, random, recorder, ref, reference, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, syntaxFormat, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
12578
+ export { ATTRIBUTE_RUNGS, CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, agree, agreeRun, ask, attributeTrace, capKeys, check, checkLatent, checkSteps, checkWorld, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, print, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, syntaxFormat, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
12248
12579
 
12249
12580
  //# sourceMappingURL=index.js.map