@mark1russell7/coeval 0.7.1 → 0.9.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
@@ -357,6 +357,17 @@ function describe(value) {
357
357
  //#endregion
358
358
  //#region src/world.ts
359
359
  /**
360
+ * This function gives the type of a bare equation of a kind (D42). It is the type `of` of the shape of its parts, or
361
+ * else the type `of` of the shape `rest` of a record. The plan, the reference and `check` read it with this one function.
362
+ *
363
+ * @param kind - The kind.
364
+ * @returns The type, or `undefined` when the shape declares none.
365
+ */
366
+ function equationType(kind) {
367
+ const parts = kind.parts;
368
+ return parts?.of ?? parts?.rest?.of;
369
+ }
370
+ /**
360
371
  * This function tells if an entry of the world is a kind.
361
372
  *
362
373
  * @param entry - The entry.
@@ -1694,7 +1705,7 @@ const RUNGS = [
1694
1705
  "kind",
1695
1706
  "default"
1696
1707
  ];
1697
- function entryOf(verbs, verb) {
1708
+ function entryOf$1(verbs, verb) {
1698
1709
  return verbs !== void 0 && Object.hasOwn(verbs, verb) ? verbs[verb] : void 0;
1699
1710
  }
1700
1711
  /**
@@ -1735,18 +1746,18 @@ const EACH_NAME = () => true;
1735
1746
  * @returns The evaluator name and its rung, or `undefined` when no rung answers.
1736
1747
  */
1737
1748
  function answerOf(world, own, kind, verb, has = EACH_NAME) {
1738
- const ownName = entryOf(own, verb);
1749
+ const ownName = entryOf$1(own, verb);
1739
1750
  if (ownName !== void 0 && has(ownName)) return {
1740
1751
  name: ownName,
1741
1752
  rung: "own"
1742
1753
  };
1743
1754
  if (kind === null) return void 0;
1744
- const kindName = kind === void 0 ? void 0 : entryOf(kind.verbs, verb);
1755
+ const kindName = kind === void 0 ? void 0 : entryOf$1(kind.verbs, verb);
1745
1756
  if (kindName !== void 0 && has(kindName)) return {
1746
1757
  name: kindName,
1747
1758
  rung: "kind"
1748
1759
  };
1749
- const fallback = entryOf(world.defaults, verb);
1760
+ const fallback = entryOf$1(world.defaults, verb);
1750
1761
  return fallback !== void 0 && has(fallback) ? {
1751
1762
  name: fallback,
1752
1763
  rung: "default"
@@ -1768,7 +1779,7 @@ function answerOf(world, own, kind, verb, has = EACH_NAME) {
1768
1779
  */
1769
1780
  function ask(world, doc, verb, options = {}) {
1770
1781
  const steps = [];
1771
- const own = entryOf(doc.own, verb);
1782
+ const own = entryOf$1(doc.own, verb);
1772
1783
  if (own !== void 0) {
1773
1784
  steps.push({
1774
1785
  rung: "own",
@@ -1808,7 +1819,7 @@ function ask(world, doc, verb, options = {}) {
1808
1819
  steps
1809
1820
  };
1810
1821
  }
1811
- const entry = entryOf(kind.verbs, verb);
1822
+ const entry = entryOf$1(kind.verbs, verb);
1812
1823
  if (entry !== void 0) {
1813
1824
  steps.push({
1814
1825
  rung: "kind",
@@ -1831,7 +1842,7 @@ function ask(world, doc, verb, options = {}) {
1831
1842
  source
1832
1843
  });
1833
1844
  }
1834
- const fallback = entryOf(world.defaults, verb);
1845
+ const fallback = entryOf$1(world.defaults, verb);
1835
1846
  if (fallback !== void 0) {
1836
1847
  steps.push({
1837
1848
  rung: "default",
@@ -2060,7 +2071,7 @@ function context(parts = {}) {
2060
2071
  };
2061
2072
  }
2062
2073
  const MAX_COLLAPSE = 64;
2063
- const NO_HOPS = /* @__PURE__ */ Object.freeze([]);
2074
+ const NO_HOPS$1 = /* @__PURE__ */ Object.freeze([]);
2064
2075
  /** A value that can be a reference document: a record with the type `ref`. The full check comes after. */
2065
2076
  function mayBeRef(thing) {
2066
2077
  return typeof thing === "object" && thing !== null && thing.type === "ref";
@@ -2111,13 +2122,13 @@ function walkQuick(start, path, plain, keyed) {
2111
2122
  ok: false,
2112
2123
  code: "#N/A",
2113
2124
  reason: `The path ${keys.join(".")} reaches nothing.`,
2114
- hops: NO_HOPS
2125
+ hops: NO_HOPS$1
2115
2126
  };
2116
2127
  if (!plain && mayBeRef(reached.thing)) return void 0;
2117
2128
  return {
2118
2129
  ok: true,
2119
2130
  thing: reached.thing,
2120
- hops: NO_HOPS
2131
+ hops: NO_HOPS$1
2121
2132
  };
2122
2133
  }
2123
2134
  const step = path[index];
@@ -2134,7 +2145,7 @@ function walkQuick(start, path, plain, keyed) {
2134
2145
  return {
2135
2146
  ok: true,
2136
2147
  thing: here,
2137
- hops: NO_HOPS
2148
+ hops: NO_HOPS$1
2138
2149
  };
2139
2150
  }
2140
2151
  /** The root whose value starts the short path of a compiled path. `FULL` takes the full algorithm at once. */
@@ -2245,7 +2256,7 @@ function resolve(nav, ctx, path, options = {}) {
2245
2256
  * @returns What the path reached and each hop, or an error.
2246
2257
  */
2247
2258
  function resolveFully$1(nav, ctx, path, hops, known) {
2248
- const done = hops ?? NO_HOPS;
2259
+ const done = hops ?? NO_HOPS$1;
2249
2260
  const failed = (code, reason) => ({
2250
2261
  ok: false,
2251
2262
  code,
@@ -2667,6 +2678,75 @@ function pathMatches(path, pattern) {
2667
2678
  }
2668
2679
  return true;
2669
2680
  }
2681
+ function valueAt(value, path) {
2682
+ let here = value;
2683
+ for (const key of path) if (Array.isArray(here)) here = here[Number(key)];
2684
+ else if (isRecord(here) && Object.hasOwn(here, String(key))) here = here[String(key)];
2685
+ else return void 0;
2686
+ return here;
2687
+ }
2688
+ function headerName(item) {
2689
+ return isEnvelope(item) && typeof item.name === "string" ? item.name : void 0;
2690
+ }
2691
+ function namesOf(source, value, covered) {
2692
+ if ("fixed" in source) return source.fixed;
2693
+ if ("keysAt" in source) {
2694
+ const at = valueAt(value, source.keysAt);
2695
+ return isRecord(at) ? Object.keys(at) : [];
2696
+ }
2697
+ if ("listAt" in source) {
2698
+ const at = valueAt(value, source.listAt);
2699
+ return Array.isArray(at) ? at.filter((item) => typeof item === "string") : [];
2700
+ }
2701
+ if ("textAt" in source) {
2702
+ const at = valueAt(value, source.textAt);
2703
+ if (typeof at === "string") return [at];
2704
+ return source.otherwise === void 0 ? [] : [source.otherwise];
2705
+ }
2706
+ return covered.map((slot) => headerName(slot.item)).filter((name) => name !== void 0);
2707
+ }
2708
+ /**
2709
+ * This function gives the names that the scope rules of a kind give each part, and the names that only later parts
2710
+ * see. `check`, `conforms` and the plan read the scope rules with it.
2711
+ *
2712
+ * @param rules - The scope rules of the kind.
2713
+ * @param value - The value of the document.
2714
+ * @param slots - The parts of the value, in order.
2715
+ * @returns The names that each part sees, and the names that only the later parts see, by the index of the part.
2716
+ */
2717
+ function scopesOf(rules, value, slots) {
2718
+ const sees = slots.map(() => []);
2719
+ const later = slots.map(() => []);
2720
+ for (const rule of rules ?? []) {
2721
+ const covered = slots.map((slot, index) => ({
2722
+ slot,
2723
+ index
2724
+ })).filter(({ slot }) => rule.in.some((pattern) => pathMatches(slot.path, pattern)));
2725
+ if (rule.sequential !== true) {
2726
+ const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
2727
+ for (const { index } of covered) sees[index]?.push(...names);
2728
+ continue;
2729
+ }
2730
+ if ("headers" in rule.names) {
2731
+ covered.forEach(({ index }, position) => {
2732
+ const before = covered.slice(0, position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
2733
+ const after = covered.slice(position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
2734
+ sees[index]?.push(...before);
2735
+ later[index]?.push(...after);
2736
+ });
2737
+ continue;
2738
+ }
2739
+ const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
2740
+ covered.forEach(({ index }, position) => {
2741
+ if (position > 0) sees[index]?.push(...names);
2742
+ else later[index]?.push(...names);
2743
+ });
2744
+ }
2745
+ return {
2746
+ sees,
2747
+ later
2748
+ };
2749
+ }
2670
2750
  //#endregion
2671
2751
  //#region src/evaluate.ts
2672
2752
  /**
@@ -2727,7 +2807,7 @@ function narrow(parent, next) {
2727
2807
  };
2728
2808
  }
2729
2809
  /** No capabilities. One list serves each masked part. */
2730
- const NO_CAPS$1 = /* @__PURE__ */ Object.freeze([]);
2810
+ const NO_CAPS$2 = /* @__PURE__ */ Object.freeze([]);
2731
2811
  /** No capability rules. One list serves each part that no rule narrows. */
2732
2812
  const NO_RULES = /* @__PURE__ */ Object.freeze([]);
2733
2813
  /**
@@ -2755,7 +2835,7 @@ function rulesFor(rules, path) {
2755
2835
  function capsUnder(rules, caps) {
2756
2836
  let out = caps;
2757
2837
  for (const rule of rules) {
2758
- if (rule.rule === "mask") return NO_CAPS$1;
2838
+ if (rule.rule === "mask") return NO_CAPS$2;
2759
2839
  if (rule.rule === "only") out = intersectCaps(out, rule.caps);
2760
2840
  }
2761
2841
  return out;
@@ -2786,7 +2866,7 @@ const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
2786
2866
  function granted(ctx) {
2787
2867
  const grant = ctx[GRANT];
2788
2868
  if (grant === ctx.caps) return ctx;
2789
- const caps = grant === void 0 ? NO_CAPS$1 : ctx.caps.every((cap) => permits(grant, cap)) ? ctx.caps : ctx.caps.filter((cap) => permits(grant, cap));
2869
+ const caps = grant === void 0 ? NO_CAPS$2 : ctx.caps.every((cap) => permits(grant, cap)) ? ctx.caps : ctx.caps.filter((cap) => permits(grant, cap));
2790
2870
  return {
2791
2871
  ...ctx,
2792
2872
  caps,
@@ -2952,6 +3032,10 @@ var Session = class {
2952
3032
  get effects() {
2953
3033
  return this.list ??= [];
2954
3034
  }
3035
+ /** `true` when the evaluation records its reads. Then folded code evaluates again, thus each read goes in the list (PLAN.FOLD). */
3036
+ get recording() {
3037
+ return this.reading !== void 0;
3038
+ }
2955
3039
  resolve(ctx, path, options) {
2956
3040
  return this.read(ctx, path, void 0, options);
2957
3041
  }
@@ -2970,6 +3054,7 @@ var Session = class {
2970
3054
  if (reading === void 0) return this.find(ctx, path, compiled, hops);
2971
3055
  const free = isFree(reading.root, ctx, path);
2972
3056
  if (!free && path[0] === "self" && !reading.self) this.keep(reading, ["self"]);
3057
+ else if (free && path.length === 1 && path[0] === "self") reading.self = true;
2973
3058
  let found;
2974
3059
  try {
2975
3060
  found = this.find(ctx, path, compiled, hops);
@@ -3416,6 +3501,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
3416
3501
  let meta = meta0;
3417
3502
  let name;
3418
3503
  let unknown;
3504
+ let quick;
3419
3505
  if (c.traced) {
3420
3506
  const answer = ask(world, envelope ?? {}, c.verb, declared === void 0 ? {} : { declaredType: declared });
3421
3507
  meta = {
@@ -3426,7 +3512,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
3426
3512
  name = answer.evaluator;
3427
3513
  unknown = answer.error === "unknown-kind";
3428
3514
  } else {
3429
- const quick = answerOf(world, envelope?.own, kind ?? null, c.verb);
3515
+ quick = answerOf(world, envelope?.own, kind ?? null, c.verb);
3430
3516
  name = quick?.name;
3431
3517
  unknown = quick === void 0 && kind === void 0;
3432
3518
  }
@@ -3437,9 +3523,11 @@ function compileNode(c, value, place, declared, depth, address = false) {
3437
3523
  const evaluator = name !== void 0 && Object.hasOwn(c.machine.evaluators, name) ? c.machine.evaluators[name] : void 0;
3438
3524
  if (name === void 0 || runner === void 0 && evaluator === void 0) return failing(c, meta, place, "#NOEVAL", `No rung gives an evaluator for ${kindText}.`);
3439
3525
  if (!split.ok) return failing(c, meta, place, "#VALUE!", split.reason);
3526
+ const frees = c.fold ? [] : void 0;
3440
3527
  const parts = split.parts.map((slot) => {
3441
3528
  const item = slot.item;
3442
3529
  const own = compileNode(c, item, place.concat(slot.path), slot.of, depth + 1, slot.address === true);
3530
+ frees?.push(own.free);
3443
3531
  const rules = rulesFor(kind?.caps, slot.path);
3444
3532
  const compiled = rules.length === 0 ? own : gated(rules, own);
3445
3533
  const header = typeof item?.name === "string" && isEnvelope(item) ? item.name : void 0;
@@ -3457,9 +3545,10 @@ function compileNode(c, value, place, declared, depth, address = false) {
3457
3545
  run
3458
3546
  };
3459
3547
  });
3548
+ const typePath = read.typePath ?? [];
3460
3549
  const input = {
3461
3550
  kind: kindText,
3462
- typePath: read.typePath ?? [],
3551
+ typePath,
3463
3552
  doc,
3464
3553
  value: split.parts.length === 0 ? doc.value : split.fill(parts),
3465
3554
  data: split.data,
@@ -3485,7 +3574,238 @@ function compileNode(c, value, place, declared, depth, address = false) {
3485
3574
  } catch (error) {
3486
3575
  return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
3487
3576
  }
3488
- return finish$1(c, meta, placedCode(inner, place, name), void 0);
3577
+ const done = finish$1(c, meta, placedCode(inner, place, name), void 0);
3578
+ if (frees === void 0 || kind === void 0 || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
3579
+ const free = freeOf(doc, kind, split.parts, frees);
3580
+ if (free === void 0) return done;
3581
+ return free.length === 0 ? folded(c, done) : {
3582
+ code: done.code,
3583
+ run: done.run,
3584
+ free
3585
+ };
3586
+ }
3587
+ /** The free names of a closed part. */
3588
+ const CLOSED = /* @__PURE__ */ Object.freeze([]);
3589
+ /**
3590
+ * This function tells if the code of a document is pure, by the facts of D24 (FACTS.PURE). It reads the facts as
3591
+ * `factsOf` reads them. A kind with a run side, with declared effects or with volatile code is not pure here.
3592
+ */
3593
+ function pureCode(c, envelope, kind, typePath, name, quick) {
3594
+ if (kind.side === "run" || kind.effects !== void 0 && kind.effects.length > 0) return false;
3595
+ let answer;
3596
+ try {
3597
+ answer = c.machine.facts?.(typePath, name);
3598
+ } catch {
3599
+ return false;
3600
+ }
3601
+ const described = c.verb === "eval" && (envelope === void 0 || quick?.rung !== "own" || name === answerOf(c.machine.world, void 0, kind, c.verb)?.name);
3602
+ const pure = described ? answer?.pure ?? kind.pure : answer?.pure;
3603
+ const volatile = described ? answer?.volatile ?? kind.volatile : answer?.volatile;
3604
+ return pure === true && volatile !== true;
3605
+ }
3606
+ /**
3607
+ * This function gives the names that a document reads and does not bind, or `undefined` when the document can read
3608
+ * other roots. A reference reads the name of its path. Each other document reads the free names of its parts, less
3609
+ * the names that its scope rules give each part (STATIC.REFS).
3610
+ */
3611
+ function freeOf(doc, kind, slots, frees) {
3612
+ if (isRef(doc)) {
3613
+ const path = doc.value;
3614
+ if (path.length < 2 || path[0] !== "names" || !path.every((step, index) => index === 0 || isKey(step))) return void 0;
3615
+ return [String(path[1])];
3616
+ }
3617
+ let sees;
3618
+ let out;
3619
+ for (let index = 0; index < frees.length; index += 1) {
3620
+ const free = frees[index];
3621
+ if (free === void 0) return void 0;
3622
+ if (free.length === 0) continue;
3623
+ if (sees === void 0) sees = kind.scope === void 0 || kind.scope.length === 0 ? [] : scopesOf(kind.scope, doc.value, slots).sees;
3624
+ const bound = sees[index] ?? CLOSED;
3625
+ for (const name of free) {
3626
+ if (bound.includes(name)) continue;
3627
+ out ??= [];
3628
+ if (!out.includes(name)) out.push(name);
3629
+ }
3630
+ }
3631
+ return out ?? CLOSED;
3632
+ }
3633
+ /** This class is the runtime of a fold. It reads only the names that the folded part binds, and it causes no effect. */
3634
+ var Probe = class {
3635
+ machine;
3636
+ running = false;
3637
+ /** `true` after the part asked for something that a fold cannot give. */
3638
+ refused = false;
3639
+ /** The count of the names that the part read. */
3640
+ reads = 0;
3641
+ /**
3642
+ * The context of the folded part. Its roots `self`, `event` and `names` are traps: a direct read of a field
3643
+ * refuses the fold, as a resolution of these roots does. A copy of the record of the names reads no field.
3644
+ */
3645
+ root;
3646
+ constructor(machine) {
3647
+ this.machine = machine;
3648
+ const refuse = () => {
3649
+ this.refused = true;
3650
+ };
3651
+ const root = {
3652
+ self: trap(refuse, false),
3653
+ event: trap(refuse, false),
3654
+ names: trap(refuse, true),
3655
+ world: worldThing(machine.world),
3656
+ caps: NO_CAPS$2,
3657
+ [GRANT]: NO_CAPS$2
3658
+ };
3659
+ this.root = root;
3660
+ }
3661
+ resolve(ctx, path) {
3662
+ const name = path[1];
3663
+ if (path[0] === "names" && isKey(name) && Object.hasOwn(ctx.names, String(name))) {
3664
+ this.reads += 1;
3665
+ const found = resolve(this.machine, ctx, path, WITH_HOPS);
3666
+ if (found.hops.some((hop) => hop.by === "collapse" || isRef(hop.thing))) this.refused = true;
3667
+ return found;
3668
+ }
3669
+ this.refused = true;
3670
+ return {
3671
+ ok: false,
3672
+ code: "#REF!",
3673
+ reason: "A folded part reads only the names that it binds.",
3674
+ hops: []
3675
+ };
3676
+ }
3677
+ effect() {
3678
+ this.refused = true;
3679
+ return fail("#CAP!", "A folded part causes no effect.");
3680
+ }
3681
+ *runEffect() {
3682
+ return this.effect();
3683
+ }
3684
+ *all() {
3685
+ this.refused = true;
3686
+ return [];
3687
+ }
3688
+ keepsContext() {
3689
+ this.refused = true;
3690
+ }
3691
+ };
3692
+ /**
3693
+ * This function makes a trap for a root of the context of a fold. Each read of a field calls `refuse`. A trap of the
3694
+ * names gives no keys, thus a copy of the names reads no field.
3695
+ */
3696
+ function trap(refuse, copies) {
3697
+ const no = () => {
3698
+ refuse();
3699
+ };
3700
+ return new Proxy({}, {
3701
+ get: no,
3702
+ getOwnPropertyDescriptor: no,
3703
+ has: () => (refuse(), false),
3704
+ ownKeys: () => {
3705
+ if (!copies) refuse();
3706
+ return [];
3707
+ },
3708
+ getPrototypeOf: () => (refuse(), null),
3709
+ set: () => (refuse(), false),
3710
+ defineProperty: () => (refuse(), false),
3711
+ deleteProperty: () => (refuse(), false)
3712
+ });
3713
+ }
3714
+ /**
3715
+ * This function folds the code of a closed pure part. The part evaluates one time with the probe. Its result becomes
3716
+ * one deeply frozen copy, and the code gives that copy at each evaluation (PLAN.SHARED). A fold that the probe cancels
3717
+ * gives the code without free names, thus no parent tries the fold again.
3718
+ */
3719
+ function folded(c, done) {
3720
+ const probe = c.probe ??= new Probe(c.machine);
3721
+ probe.refused = false;
3722
+ probe.reads = 0;
3723
+ let constant;
3724
+ try {
3725
+ const result = done.code(probe.root, probe);
3726
+ if (!probe.refused && (result.ok || result.error.code !== "#DEPTH!")) constant = sharedCopy(result);
3727
+ } catch {
3728
+ constant = void 0;
3729
+ }
3730
+ if (constant === void 0) return {
3731
+ code: done.code,
3732
+ run: done.run
3733
+ };
3734
+ const value = constant;
3735
+ if (probe.reads === 0) return {
3736
+ code: () => value,
3737
+ run: void 0,
3738
+ free: CLOSED
3739
+ };
3740
+ const original = done.code;
3741
+ return {
3742
+ code: (ctx, rt) => rt instanceof Session && !rt.traced && !rt.recording ? value : original(ctx, rt),
3743
+ run: void 0,
3744
+ free: CLOSED
3745
+ };
3746
+ }
3747
+ /** The objects that a fold made. Each one is deeply frozen, thus a later fold uses it again and does not copy it. */
3748
+ const SHARED = /* @__PURE__ */ new WeakSet();
3749
+ /** The mark of a value that is not plain data. */
3750
+ const NOT_PLAIN = /* @__PURE__ */ Symbol("not plain");
3751
+ /**
3752
+ * This function gives a deeply frozen copy of plain data, or `undefined` for other values. Plain data is a primitive or
3753
+ * an array without holes and extra keys. It is also a record of enumerable data fields with the prototype of an object
3754
+ * or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change.
3755
+ */
3756
+ function sharedCopy(value) {
3757
+ try {
3758
+ const out = copyPlain(value, /* @__PURE__ */ new Map());
3759
+ return out === NOT_PLAIN ? void 0 : out;
3760
+ } catch {
3761
+ return;
3762
+ }
3763
+ }
3764
+ /** This function copies one value for `sharedCopy`. The map holds the copy of each object that the copy saw. */
3765
+ function copyPlain(value, memo) {
3766
+ if (typeof value === "function") return NOT_PLAIN;
3767
+ if (typeof value !== "object" || value === null) return value;
3768
+ if (SHARED.has(value)) return value;
3769
+ const seen = memo.get(value);
3770
+ if (seen !== void 0) return seen;
3771
+ const proto = Object.getPrototypeOf(value);
3772
+ const keys = Reflect.ownKeys(value);
3773
+ let out;
3774
+ if (Array.isArray(value)) {
3775
+ if (proto !== Array.prototype || keys.length !== value.length + 1) return NOT_PLAIN;
3776
+ const list = [];
3777
+ memo.set(value, list);
3778
+ for (let index = 0; index < value.length; index += 1) {
3779
+ const field = Object.getOwnPropertyDescriptor(value, index);
3780
+ if (field === void 0 || !("value" in field) || field.enumerable !== true) return NOT_PLAIN;
3781
+ const item = copyPlain(field.value, memo);
3782
+ if (item === NOT_PLAIN) return NOT_PLAIN;
3783
+ list.push(item);
3784
+ }
3785
+ out = list;
3786
+ } else {
3787
+ if (proto !== Object.prototype && proto !== null) return NOT_PLAIN;
3788
+ const record = proto === null ? Object.create(null) : {};
3789
+ memo.set(value, record);
3790
+ for (const key of keys) {
3791
+ if (typeof key !== "string") return NOT_PLAIN;
3792
+ const field = Object.getOwnPropertyDescriptor(value, key);
3793
+ if (field === void 0 || !("value" in field) || field.enumerable !== true) return NOT_PLAIN;
3794
+ const item = copyPlain(field.value, memo);
3795
+ if (item === NOT_PLAIN) return NOT_PLAIN;
3796
+ if (key === "__proto__") Object.defineProperty(record, key, {
3797
+ value: item,
3798
+ enumerable: true,
3799
+ writable: true,
3800
+ configurable: true
3801
+ });
3802
+ else record[key] = item;
3803
+ }
3804
+ out = record;
3805
+ }
3806
+ Object.freeze(out);
3807
+ SHARED.add(out);
3808
+ return out;
3489
3809
  }
3490
3810
  /**
3491
3811
  * This function gives the code of a document: the code of its evaluator, with the place of each error and of each
@@ -3650,12 +3970,14 @@ function prepare(machine, doc, options = {}) {
3650
3970
  error: "no-answer",
3651
3971
  steps: []
3652
3972
  };
3973
+ const traced = options.trace === true;
3653
3974
  const c = {
3654
3975
  machine,
3655
- traced: options.trace === true,
3976
+ traced,
3656
3977
  maxDepth: machine.maxDepth ?? 1e3,
3657
3978
  verb,
3658
- noAnswer
3979
+ noAnswer,
3980
+ fold: !traced && options.fold !== false
3659
3981
  };
3660
3982
  let root;
3661
3983
  try {
@@ -3753,8 +4075,8 @@ function record(s, result) {
3753
4075
  * tells where its parts are. The evaluator gets each part sealed: it can evaluate the part, and it cannot read its
3754
4076
  * value.
3755
4077
  *
3756
- * A failure gives an error value, and the function does not throw. An effect is recorded in its order, and the host
3757
- * does not do it. A mock can answer each effect at once (RUN.MOCK).
4078
+ * A failure gives an error value, and the function does not throw. The function records each effect in its order,
4079
+ * and the host does not do it. A mock can answer each effect at once (RUN.MOCK).
3758
4080
  *
3759
4081
  * @param machine - The world and the code of the evaluators.
3760
4082
  * @param doc - The document, as JSON.
@@ -3969,6 +4291,7 @@ const STD_WORLD = {
3969
4291
  caps: [{ rule: "mask" }],
3970
4292
  pure: true,
3971
4293
  lazy: [{}],
4294
+ reads: ["self"],
3972
4295
  summary: "A node of a tree. Each part is an attribute that evaluates on demand, one time, with the node as self."
3973
4296
  },
3974
4297
  box: {
@@ -3988,6 +4311,7 @@ const STD_WORLD = {
3988
4311
  caps: [{ rule: "mask" }],
3989
4312
  pure: true,
3990
4313
  lazy: [{}],
4314
+ reads: ["self"],
3991
4315
  summary: "A node of a tree, as panel. A formula in a box can read its parent and its siblings."
3992
4316
  },
3993
4317
  math: { kinds: {
@@ -4112,6 +4436,56 @@ const STD_EVALUATORS = {
4112
4436
  };
4113
4437
  /** The brand of a node of the decorated tree. Its value is the state that the kit reads. */
4114
4438
  const TREE_NODE = /* @__PURE__ */ Symbol.for("coeval.tree.node");
4439
+ /** The rungs of the ladder of an attribute, in their order (TREE.LADDER, D42). */
4440
+ const ATTRIBUTE_RUNGS = /* @__PURE__ */ Object.freeze([
4441
+ "own",
4442
+ "kind",
4443
+ "parent",
4444
+ "copy",
4445
+ "default"
4446
+ ]);
4447
+ /** The capabilities of an equation: none (CAP.PURE). */
4448
+ const NO_CAPS$1 = /* @__PURE__ */ Object.freeze([]);
4449
+ /** The names of an equation of `equations`, of a `unit` and of a `default`. */
4450
+ const NO_NAMES$1 = /* @__PURE__ */ Object.freeze({});
4451
+ /** The planners of equations, for each machine and each type of a bare equation (TREE.PLAN). */
4452
+ const equationPlanners = /* @__PURE__ */ new WeakMap();
4453
+ /**
4454
+ * This function gives the plan of an equation. Each equation compiles one time for each machine (PLAN.CACHE). The
4455
+ * plan of the document does not keep it (PLAN.LEAN).
4456
+ */
4457
+ function equationPlan(machine, of, term) {
4458
+ let byType = equationPlanners.get(machine);
4459
+ if (byType === void 0) {
4460
+ byType = /* @__PURE__ */ new Map();
4461
+ equationPlanners.set(machine, byType);
4462
+ }
4463
+ const key = of ?? "";
4464
+ let plans = byType.get(key);
4465
+ if (plans === void 0) {
4466
+ plans = planner(machine, of === void 0 ? {} : { declaredType: of });
4467
+ byType.set(key, plans);
4468
+ }
4469
+ return plans(term);
4470
+ }
4471
+ /** This function gives the own entry of a table of a tree contract, or `undefined`. */
4472
+ function entryOf(table, key) {
4473
+ return table !== void 0 && Object.hasOwn(table, key) ? table[key] : void 0;
4474
+ }
4475
+ const NO_CONTRACT = /* @__PURE__ */ Object.freeze({
4476
+ tree: void 0,
4477
+ of: void 0
4478
+ });
4479
+ /** This function finds the tree contract of the kind of a node in the world of the machine. */
4480
+ function contractOf$1(machine, typePath) {
4481
+ if (machine === void 0 || typePath.length === 0) return NO_CONTRACT;
4482
+ const found = lookupKind(machine.world, typePath);
4483
+ if (!found.ok || found.kind.tree === void 0) return NO_CONTRACT;
4484
+ return {
4485
+ tree: found.kind.tree,
4486
+ of: equationType(found.kind)
4487
+ };
4488
+ }
4115
4489
  const BUSY = /* @__PURE__ */ Object.freeze({ busy: true });
4116
4490
  /** The attributes in evaluation, in the order of their demands. An evaluation does not wait, thus one list serves. */
4117
4491
  const demands = [];
@@ -4179,28 +4553,93 @@ function plainOf(value, path) {
4179
4553
  }
4180
4554
  /**
4181
4555
  * This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
4182
- * step `type` gives its type. A step with the key of a part reads that attribute. A failed attribute throws `Failure`
4183
- * with its own error, thus a reference to it gives that error.
4184
- */
4185
- function treeThing(parts, context, type, place, build) {
4556
+ * step `type` gives its type. Another step reads an attribute through its ladder (TREE.LADDER). The rungs are a part of
4557
+ * the node, an equation of its kind and an equation `down` of the kind of its parent. A copy from the parent, then a
4558
+ * fold or a default, come last. A failed attribute throws `Failure` with its own error, thus a reference to it gives that error.
4559
+ * The argument `outer` is the self of the context, that `treeNode` resolved. The node reads no root of the context directly (C4).
4560
+ */
4561
+ function treeThing(input, build, outer) {
4562
+ const { parts, context, kind: type, place, machine } = input;
4563
+ const { tree, of } = contractOf$1(machine, input.typePath);
4564
+ const above = stateOf(outer);
4565
+ const up = above !== void 0 && above.build === build ? above : void 0;
4186
4566
  const cells = /* @__PURE__ */ new Map();
4567
+ /** The positions of the items of the list `children`, for the names of the children. */
4568
+ let positions;
4569
+ const fold = (key, declared) => {
4570
+ let acc = state.equation(declared.unit, NO_NAMES$1);
4571
+ if (!acc.ok) return acc;
4572
+ const children = read("children");
4573
+ if (children === void 0) return acc;
4574
+ if (!children.ok) return children;
4575
+ if (!Array.isArray(children.value)) return fail("#VALUE!", `The fold of ${key} needs a list in children.`);
4576
+ for (const item of children.value) {
4577
+ const child = stateOf(item);
4578
+ if (child === void 0 || child.build !== build) continue;
4579
+ const value = child.read(key);
4580
+ if (value === void 0) return fail("#N/A", `No rung gives ${attributeText(key, child.type, child.place)}.`);
4581
+ if (!value.ok) return value;
4582
+ acc = state.equation(declared.combine, {
4583
+ acc: acc.value,
4584
+ item: value.value
4585
+ });
4586
+ if (!acc.ok) return acc;
4587
+ }
4588
+ return acc;
4589
+ };
4590
+ /** This function visits the rungs of the ladder of an attribute in their order, until one answers. */
4591
+ const climb = (key) => {
4592
+ const part = parts.find((item) => item.key === key);
4593
+ if (part !== void 0) return {
4594
+ result: part.evaluate({
4595
+ ...context,
4596
+ self: node
4597
+ }),
4598
+ by: "own"
4599
+ };
4600
+ const equation = entryOf(tree?.equations, key);
4601
+ if (equation !== void 0) return {
4602
+ result: state.equation(equation, NO_NAMES$1),
4603
+ by: "kind"
4604
+ };
4605
+ const down = up === void 0 ? void 0 : entryOf(up.tree?.down, key);
4606
+ if (up !== void 0 && down !== void 0) {
4607
+ const names = up.childNames(node);
4608
+ return {
4609
+ result: names.ok ? up.equation(down, names.value) : names,
4610
+ by: "parent"
4611
+ };
4612
+ }
4613
+ const declared = entryOf(tree?.attributes, key);
4614
+ if (declared?.copy === true && up !== void 0) {
4615
+ const copied = up.read(key);
4616
+ if (copied !== void 0) return {
4617
+ result: copied,
4618
+ by: "copy"
4619
+ };
4620
+ }
4621
+ if (declared?.fold !== void 0) return {
4622
+ result: fold(key, declared.fold),
4623
+ by: "default"
4624
+ };
4625
+ if (declared?.default !== void 0) return {
4626
+ result: state.equation(declared.default, NO_NAMES$1),
4627
+ by: "default"
4628
+ };
4629
+ };
4187
4630
  const read = (key) => {
4188
4631
  const cell = cells.get(key);
4189
4632
  if (cell !== void 0) return cell.busy ? cycleAt(cells, key, attributeText(key, type, place)) : cell.result;
4190
- const part = parts.find((item) => item.key === key);
4191
- if (part === void 0) return void 0;
4633
+ if (tree === void 0 && up?.tree === void 0 && !parts.some((item) => item.key === key)) return void 0;
4192
4634
  cells.set(key, BUSY);
4193
4635
  demands.push({
4194
4636
  cells,
4195
4637
  key,
4196
4638
  text: attributeText(key, type, place)
4197
4639
  });
4198
- let result;
4640
+ let answer;
4199
4641
  try {
4200
- result = part.evaluate({
4201
- ...context,
4202
- self: node
4203
- });
4642
+ answer = climb(key);
4204
4643
  } catch (error) {
4205
4644
  cells.delete(key);
4206
4645
  throw error;
@@ -4209,14 +4648,60 @@ function treeThing(parts, context, type, place, build) {
4209
4648
  }
4210
4649
  cells.set(key, {
4211
4650
  busy: false,
4212
- result
4651
+ result: answer?.result,
4652
+ by: answer?.by
4213
4653
  });
4214
- return result;
4654
+ return answer?.result;
4215
4655
  };
4216
4656
  const state = {
4217
4657
  build,
4218
4658
  type,
4219
4659
  place,
4660
+ tree,
4661
+ read,
4662
+ equation(term, names) {
4663
+ if (machine === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
4664
+ return equationPlan(machine, of, term).evaluate({
4665
+ ...context,
4666
+ names,
4667
+ caps: NO_CAPS$1,
4668
+ self: node
4669
+ });
4670
+ },
4671
+ childNames(child) {
4672
+ const children = read("children");
4673
+ if (children !== void 0 && !children.ok) return children;
4674
+ const list = children !== void 0 && Array.isArray(children.value) ? children.value : [];
4675
+ if (positions?.list !== list) {
4676
+ const at = /* @__PURE__ */ new Map();
4677
+ list.forEach((item, index) => {
4678
+ if (!at.has(item)) at.set(item, index);
4679
+ });
4680
+ positions = {
4681
+ list,
4682
+ at
4683
+ };
4684
+ }
4685
+ const index = positions.at.get(child);
4686
+ return ok({
4687
+ child,
4688
+ index: index ?? null,
4689
+ prev: index === void 0 || index === 0 ? null : list[index - 1]
4690
+ });
4691
+ },
4692
+ trace(key) {
4693
+ const cell = cells.get(key);
4694
+ if (cell === void 0 || cell.busy) return void 0;
4695
+ const rungs = cell.by === void 0 ? ATTRIBUTE_RUNGS : ATTRIBUTE_RUNGS.slice(0, ATTRIBUTE_RUNGS.indexOf(cell.by) + 1);
4696
+ return cell.by === void 0 ? {
4697
+ attribute: key,
4698
+ rungs
4699
+ } : {
4700
+ attribute: key,
4701
+ rungs,
4702
+ by: cell.by
4703
+ };
4704
+ },
4220
4705
  json(path) {
4221
4706
  path.add(node);
4222
4707
  try {
@@ -4240,7 +4725,7 @@ function treeThing(parts, context, type, place, build) {
4240
4725
  type,
4241
4726
  label: `node ${type}`,
4242
4727
  deref(key) {
4243
- if (key === "parent") return context.self;
4728
+ if (key === "parent") return outer;
4244
4729
  if (key === "type") return type;
4245
4730
  const result = read(String(key));
4246
4731
  if (result === void 0) return void 0;
@@ -4248,7 +4733,9 @@ function treeThing(parts, context, type, place, build) {
4248
4733
  return result.value;
4249
4734
  },
4250
4735
  has(key) {
4251
- return key === "parent" || key === "type" || parts.some((part) => part.key === String(key));
4736
+ if (key === "parent" || key === "type" || parts.some((part) => part.key === String(key))) return true;
4737
+ const cell = cells.get(String(key));
4738
+ return cell !== void 0 && !cell.busy && cell.result !== void 0;
4252
4739
  },
4253
4740
  toJson() {
4254
4741
  const result = state.json(/* @__PURE__ */ new Set());
@@ -4259,14 +4746,32 @@ function treeThing(parts, context, type, place, build) {
4259
4746
  return node;
4260
4747
  }
4261
4748
  /**
4262
- * This function evaluates a node of the decorated tree. Inside an attribute of an open tree, the node gives its host
4263
- * thing, and no attribute evaluates. Else the node is the root of a new tree, and it gives the JSON of the tree.
4749
+ * This function gives the record of the demand of an attribute of a node of the decorated tree (TREE.RUNGS). The
4750
+ * record holds the rungs that the demand visited, and the rung that answered. An explorer draws the ladder with it.
4751
+ *
4752
+ * @param node - The thing of a node.
4753
+ * @param attribute - The name of the attribute.
4754
+ * @returns The record, or `undefined` for a thing that is not a node or an attribute that no demand reached.
4264
4755
  */
4265
- function treeNode({ parts, context, kind, place }) {
4266
- const above = stateOf(context.self);
4267
- if (above !== void 0 && above.build.open) return ok(treeThing(parts, context, kind, place, above.build));
4756
+ function attributeTrace(node, attribute) {
4757
+ return stateOf(node)?.trace(attribute);
4758
+ }
4759
+ /** The path of the root `self`. A node of the tree resolves it, thus the list of reads holds the read (C4). */
4760
+ const SELF_PATH = /* @__PURE__ */ Object.freeze(["self"]);
4761
+ /** The options of the resolution of `self`: the node needs no hops. */
4762
+ const NO_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
4763
+ /**
4764
+ * This function evaluates a node of the decorated tree. Inside an attribute of an open tree, the node gives its host
4765
+ * thing, and no attribute evaluates. Else the node is the root of a new tree, and it gives the JSON of the tree. The
4766
+ * node reads `self` through `resolve` (C4, D44). A machine that closes the root `self` thus gives no nested trees.
4767
+ */
4768
+ function treeNode(input) {
4769
+ const found = input.resolve(SELF_PATH, NO_HOPS);
4770
+ const outer = found.ok ? found.thing : void 0;
4771
+ const above = stateOf(outer);
4772
+ if (above !== void 0 && above.build.open) return ok(treeThing(input, above.build, outer));
4268
4773
  const build = { open: true };
4269
- const root = treeThing(parts, context, kind, place, build);
4774
+ const root = treeThing(input, build, outer);
4270
4775
  try {
4271
4776
  return stateOf(root).json(/* @__PURE__ */ new Set());
4272
4777
  } finally {
@@ -5009,75 +5514,6 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
5009
5514
  }
5010
5515
  //#endregion
5011
5516
  //#region src/static.ts
5012
- function valueAt(value, path) {
5013
- let here = value;
5014
- for (const key of path) if (Array.isArray(here)) here = here[Number(key)];
5015
- else if (isRecord(here) && Object.hasOwn(here, String(key))) here = here[String(key)];
5016
- else return void 0;
5017
- return here;
5018
- }
5019
- function headerName(item) {
5020
- return isEnvelope(item) && typeof item.name === "string" ? item.name : void 0;
5021
- }
5022
- function namesOf(source, value, covered) {
5023
- if ("fixed" in source) return source.fixed;
5024
- if ("keysAt" in source) {
5025
- const at = valueAt(value, source.keysAt);
5026
- return isRecord(at) ? Object.keys(at) : [];
5027
- }
5028
- if ("listAt" in source) {
5029
- const at = valueAt(value, source.listAt);
5030
- return Array.isArray(at) ? at.filter((item) => typeof item === "string") : [];
5031
- }
5032
- if ("textAt" in source) {
5033
- const at = valueAt(value, source.textAt);
5034
- if (typeof at === "string") return [at];
5035
- return source.otherwise === void 0 ? [] : [source.otherwise];
5036
- }
5037
- return covered.map((slot) => headerName(slot.item)).filter((name) => name !== void 0);
5038
- }
5039
- /**
5040
- * This function gives the names that the scope rules of a kind give each part, and the names that only later parts
5041
- * see. `check` and `conforms` read the scope rules with it.
5042
- *
5043
- * @param rules - The scope rules of the kind.
5044
- * @param value - The value of the document.
5045
- * @param slots - The parts of the value, in order.
5046
- * @returns The names that each part sees, and the names that only the later parts see, by the index of the part.
5047
- */
5048
- function scopesOf(rules, value, slots) {
5049
- const sees = slots.map(() => []);
5050
- const later = slots.map(() => []);
5051
- for (const rule of rules ?? []) {
5052
- const covered = slots.map((slot, index) => ({
5053
- slot,
5054
- index
5055
- })).filter(({ slot }) => rule.in.some((pattern) => pathMatches(slot.path, pattern)));
5056
- if (rule.sequential !== true) {
5057
- const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
5058
- for (const { index } of covered) sees[index]?.push(...names);
5059
- continue;
5060
- }
5061
- if ("headers" in rule.names) {
5062
- covered.forEach(({ index }, position) => {
5063
- const before = covered.slice(0, position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
5064
- const after = covered.slice(position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
5065
- sees[index]?.push(...before);
5066
- later[index]?.push(...after);
5067
- });
5068
- continue;
5069
- }
5070
- const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
5071
- covered.forEach(({ index }, position) => {
5072
- if (position > 0) sees[index]?.push(...names);
5073
- else later[index]?.push(...names);
5074
- });
5075
- }
5076
- return {
5077
- sees,
5078
- later
5079
- };
5080
- }
5081
5517
  /** The names of a walk without names. A walk does not change a set at any time: it makes a new set to add names. */
5082
5518
  const NO_NAMES = /* @__PURE__ */ new Set();
5083
5519
  /** This function gives the options of a walk without the names of the host. */
@@ -5422,11 +5858,141 @@ function typeCheck(machine, doc, options = {}) {
5422
5858
  places: pass.places
5423
5859
  };
5424
5860
  }
5861
+ /** This function tells if a table of verbs has an entry for a verb. */
5862
+ function hasVerb(table, verb) {
5863
+ return table !== void 0 && Object.hasOwn(table, verb);
5864
+ }
5865
+ /**
5866
+ * This function checks the world of a machine before a document exists. It finds each kind that has no entry for a
5867
+ * verb of the list, when the world has no default for that verb (STATIC.COVERAGE). A document of such a kind gets no
5868
+ * evaluator for the verb, thus the check gives the same proof of coverage as a type system.
5869
+ *
5870
+ * The check visits each kind of each namespace, at each depth, and the fallback kind of each namespace and of the
5871
+ * world. It does not follow an alias, because the kind of an alias has its own place. The check reads only the world.
5872
+ *
5873
+ * @param machine - The machine, for its world.
5874
+ * @param verbs - The verbs that each kind of the language must answer, for example `eval`, `view` and `type`.
5875
+ * @returns One issue for each kind and verb without an evaluator. The order is the order of the world, then the
5876
+ * order of the verbs. The fallback kind of a namespace comes after the entries of that namespace.
5877
+ */
5878
+ function checkWorld(machine, verbs) {
5879
+ const world = machine.world;
5880
+ const open = verbs.filter((verb) => !hasVerb(world.defaults, verb));
5881
+ const out = [];
5882
+ if (open.length === 0) return out;
5883
+ const visit = (kind, path, fallback) => {
5884
+ const text = path.join(".");
5885
+ const name = fallback ? path.length === 0 ? "the fallback kind of the world" : `the fallback kind of ${text}` : `the kind ${text}`;
5886
+ for (const verb of open) {
5887
+ if (hasVerb(kind.verbs, verb)) continue;
5888
+ const reason = `${name.charAt(0).toUpperCase()}${name.slice(1)} has no entry for the verb ${verb}, and the world has no default for it.`;
5889
+ out.push(fallback ? {
5890
+ code: "#NOEVAL",
5891
+ kind: path,
5892
+ fallback: true,
5893
+ verb,
5894
+ reason
5895
+ } : {
5896
+ code: "#NOEVAL",
5897
+ kind: path,
5898
+ verb,
5899
+ reason
5900
+ });
5901
+ }
5902
+ };
5903
+ const way = /* @__PURE__ */ new Set();
5904
+ const entries = (kinds, path) => {
5905
+ for (const key of Object.keys(kinds)) {
5906
+ const entry = kinds[key];
5907
+ const here = [...path, key];
5908
+ if (isKind(entry)) visit(entry, here, false);
5909
+ else if (isNamespace(entry) && !way.has(entry)) {
5910
+ way.add(entry);
5911
+ entries(entry.kinds, here);
5912
+ if (entry.fallback !== void 0) visit(entry.fallback, here, true);
5913
+ way.delete(entry);
5914
+ }
5915
+ }
5916
+ };
5917
+ entries(world.kinds, []);
5918
+ if (world.fallback !== void 0) visit(world.fallback, [], true);
5919
+ return out;
5920
+ }
5921
+ /** The names that an equation of `down` sees (TREE.DOWN). */
5922
+ const DOWN_NAMES = /* @__PURE__ */ Object.freeze([
5923
+ "child",
5924
+ "index",
5925
+ "prev"
5926
+ ]);
5927
+ /** The names that the term `combine` of a fold sees (TREE.FOLD). */
5928
+ const FOLD_NAMES = /* @__PURE__ */ Object.freeze(["acc", "item"]);
5929
+ /**
5930
+ * This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
5931
+ * `equations`, a `unit` and a `default` see no names.
5932
+ */
5933
+ function equationsOf(kind) {
5934
+ const tree = kind.tree;
5935
+ if (tree === void 0) return [];
5936
+ const out = [];
5937
+ for (const [name, term] of Object.entries(tree.equations ?? {})) out.push({
5938
+ label: `tree.equations.${name}`,
5939
+ term,
5940
+ names: []
5941
+ });
5942
+ for (const [name, term] of Object.entries(tree.down ?? {})) out.push({
5943
+ label: `tree.down.${name}`,
5944
+ term,
5945
+ names: DOWN_NAMES
5946
+ });
5947
+ for (const [name, declared] of Object.entries(tree.attributes ?? {})) {
5948
+ if (declared.fold !== void 0) {
5949
+ out.push({
5950
+ label: `tree.attributes.${name}.fold.unit`,
5951
+ term: declared.fold.unit,
5952
+ names: []
5953
+ });
5954
+ out.push({
5955
+ label: `tree.attributes.${name}.fold.combine`,
5956
+ term: declared.fold.combine,
5957
+ names: FOLD_NAMES
5958
+ });
5959
+ }
5960
+ if (declared.default !== void 0) out.push({
5961
+ label: `tree.attributes.${name}.default`,
5962
+ term: declared.default,
5963
+ names: []
5964
+ });
5965
+ }
5966
+ return out;
5967
+ }
5968
+ /**
5969
+ * This function checks the equations of the tree contract of a kind, each with the names of its rung (TREE.DOWN-NAMES).
5970
+ * An equation evaluates without capabilities, thus a check with capabilities gives it none. Each issue gets the place
5971
+ * of the document of the kind, and its reason names the equation.
5972
+ */
5973
+ function equationIssues(machine, kind, kindText, options, seen) {
5974
+ const of = equationType(kind);
5975
+ const out = [];
5976
+ for (const { label, term, names } of equationsOf(kind)) {
5977
+ const inner = {
5978
+ names,
5979
+ ...of === void 0 ? {} : { declaredType: of },
5980
+ ...options.caps === void 0 ? {} : { caps: [] }
5981
+ };
5982
+ for (const issue of checkAll(machine, term, inner, void 0, seen)) out.push({
5983
+ code: issue.code,
5984
+ place: [],
5985
+ reason: `In the equation ${label} of the kind ${kindText}: ${issue.reason}`
5986
+ });
5987
+ }
5988
+ return out;
5989
+ }
5425
5990
  /**
5426
5991
  * This function finds the issues of `check`. With a list of nodes, it also records each document that it visits for
5427
- * the pass of types. It marks each document with an issue.
5992
+ * the pass of types. It marks each document with an issue. The set `seen` holds the kinds whose equations the check
5993
+ * walked, thus it walks the equations of each kind one time.
5428
5994
  */
5429
- function checkAll(machine, doc, options, nodes) {
5995
+ function checkAll(machine, doc, options, nodes, seen = /* @__PURE__ */ new Set()) {
5430
5996
  const issues = [];
5431
5997
  const verb = options.verb ?? "eval";
5432
5998
  const indices = nodes === void 0 ? void 0 : /* @__PURE__ */ new Map();
@@ -5478,6 +6044,14 @@ function checkAll(machine, doc, options, nodes) {
5478
6044
  });
5479
6045
  return "skip";
5480
6046
  }
6047
+ if (v.kind?.tree !== void 0 && !seen.has(v.kind)) {
6048
+ seen.add(v.kind);
6049
+ for (const issue of equationIssues(machine, v.kind, typeName(v.doc.type), options, seen)) push({
6050
+ ...issue,
6051
+ place: v.place,
6052
+ at: v.at
6053
+ });
6054
+ }
5481
6055
  const name = answer.evaluator;
5482
6056
  if (current !== void 0) current.evaluator = name;
5483
6057
  const runner = name !== void 0 && has(machine.runners, name);
@@ -5675,7 +6249,8 @@ function partLazy(above, parent, slot) {
5675
6249
  * by the contracts of its kinds:
5676
6250
  *
5677
6251
  * - `reads`: each free reference, with the root and the keys of its path. A read under a lazy part has the mark
5678
- * `lazy`, for example a read in a branch of `if`. A read after a step document has `exact: false`.
6252
+ * `lazy`, for example a read in a branch of `if`. A read after a step document has `exact: false`. A kind that
6253
+ * declares `reads` adds a read of each of its roots at each document of the kind.
5679
6254
  * - `side` and `effects`: from the contract of each kind.
5680
6255
  * - `pure`: each kind declares `pure`, and the document has no effect and is not volatile.
5681
6256
  * - `volatile`: a kind, an operation or a step document declares `volatile`, or the host marks a read.
@@ -5728,18 +6303,35 @@ function factsOf(machine, doc, options = {}) {
5728
6303
  }
5729
6304
  const code = codeOf(world, v.declared ? void 0 : v.doc.own, kind, verb, "eval");
5730
6305
  const answer = code.name === void 0 ? void 0 : hook?.(v.typePath ?? [], code.name);
6306
+ let roots;
5731
6307
  if (code.described) {
5732
6308
  note(answer?.pure ?? kind.pure, answer?.volatile ?? kind.volatile);
5733
6309
  above.set(v, {
5734
6310
  lazy,
5735
6311
  rules: answer?.lazy ?? kind.lazy
5736
6312
  });
6313
+ roots = answer?.reads ?? kind.reads;
5737
6314
  } else {
5738
6315
  note(answer?.pure, answer?.volatile);
5739
6316
  above.set(v, {
5740
6317
  lazy,
5741
6318
  rules: answer?.lazy ?? EACH$1
5742
6319
  });
6320
+ roots = answer?.reads;
6321
+ }
6322
+ for (const root of roots ?? NONE) {
6323
+ const read = {
6324
+ root,
6325
+ keys: NONE,
6326
+ exact: true,
6327
+ lazy,
6328
+ address: false,
6329
+ path: [root],
6330
+ place: tight(v.place),
6331
+ at: tight(v.at)
6332
+ };
6333
+ reads.push(read);
6334
+ if (options.volatile?.(read) === true) volatile = true;
5743
6335
  }
5744
6336
  if (kind.side === "run") side = "run";
5745
6337
  for (const keys of declaredEffects(v)) {
@@ -6557,22 +7149,110 @@ function formulaTypes(profile) {
6557
7149
  * This function makes the evaluators of the formula kit for one language. Each evaluator compiles its document
6558
7150
  * one time into code. Each evaluator also holds its type rule, for a check of types (TYPE.SYNTH).
6559
7151
  *
7152
+ * Equal profiles give the same evaluator objects (COMPOSE.UNIFY, D46). Thus two machines of one profile merge without
7153
+ * a conflict. Each call gives a new record. Each evaluator object is frozen, because many machines can share it.
7154
+ *
6560
7155
  * @param profile - The operations and the rules of the language.
6561
7156
  * @returns The evaluators, by their name.
6562
7157
  */
6563
7158
  function formulaEvaluators(profile = {}) {
7159
+ return { ...kitOf(profile).evaluators };
7160
+ }
7161
+ /** This function makes new evaluators of the formula kit for the fields of one profile. */
7162
+ function makeEvaluators(profile) {
6564
7163
  const evaluators = formulaCode(profile);
6565
7164
  const types = formulaTypes(profile);
6566
7165
  const out = {};
6567
7166
  for (const name of Object.keys(evaluators)) {
6568
7167
  const evaluator = evaluators[name];
6569
7168
  const type = Object.hasOwn(types, name) ? types[name] : void 0;
6570
- out[name] = type === void 0 ? evaluator : {
7169
+ out[name] = Object.freeze(type === void 0 ? evaluator : {
6571
7170
  compile: evaluator.compile,
6572
7171
  type
7172
+ });
7173
+ }
7174
+ return Object.freeze(out);
7175
+ }
7176
+ /** The keys of a profile that the kit reads. The kit ignores each other key. */
7177
+ const PROFILE_KEYS = /* @__PURE__ */ Object.freeze([
7178
+ "ops",
7179
+ "lazy",
7180
+ "ext",
7181
+ "truthy",
7182
+ "otherwise",
7183
+ "logic",
7184
+ "omit",
7185
+ "missing",
7186
+ "check",
7187
+ "combine",
7188
+ "pure",
7189
+ "volatile",
7190
+ "sigs"
7191
+ ]);
7192
+ /** The number of recent kits that a profile in a new object can find by its fields. */
7193
+ const RECENT_KITS = 16;
7194
+ /** The kit of each profile object. A kit goes when its profile object goes. */
7195
+ const kitsByProfile = /* @__PURE__ */ new WeakMap();
7196
+ /** The recent kits, the most recent first. The list holds `RECENT_KITS` kits or fewer. */
7197
+ const recentKits = [];
7198
+ /**
7199
+ * This function gives the canonical form of a profile: a frozen record of the fields that the kit reads. A field
7200
+ * with the value `undefined` is absent, but `otherwise` is present when the profile has it as an own key. A list
7201
+ * is a frozen copy. Each other value stays the same object.
7202
+ */
7203
+ function fieldsOf(profile) {
7204
+ const out = {};
7205
+ for (const key of PROFILE_KEYS) {
7206
+ if (key === "otherwise" ? !Object.hasOwn(profile, key) : profile[key] === void 0) continue;
7207
+ const value = profile[key];
7208
+ out[key] = Array.isArray(value) ? Object.freeze([...value]) : value;
7209
+ }
7210
+ return Object.freeze(out);
7211
+ }
7212
+ /**
7213
+ * This function tells if two canonical forms are equal. They must have the same keys. A function, a record and a host
7214
+ * value are equal only to themselves. Two lists are equal when their items are the same, in order.
7215
+ */
7216
+ function sameFields(a, b) {
7217
+ const left = a;
7218
+ const right = b;
7219
+ const keys = Object.keys(left);
7220
+ if (keys.length !== Object.keys(right).length) return false;
7221
+ return keys.every((key) => {
7222
+ if (!Object.hasOwn(right, key)) return false;
7223
+ const x = left[key];
7224
+ const y = right[key];
7225
+ if (Object.is(x, y)) return true;
7226
+ return Array.isArray(x) && Array.isArray(y) && x.length === y.length && x.every((item, index) => Object.is(item, y[index]));
7227
+ });
7228
+ }
7229
+ /**
7230
+ * This function gives the kit of a profile (D46). A profile object that the kit knows, with the same fields, gives its
7231
+ * kit. Then a recent kit with equal fields serves a new profile object. Else the function makes a new kit from the
7232
+ * canonical form. Thus a later change of a field of the profile object gives a new kit, and an old kit does not change.
7233
+ */
7234
+ function kitOf(profile) {
7235
+ const fields = fieldsOf(profile);
7236
+ const known = kitsByProfile.get(profile);
7237
+ if (known !== void 0 && sameFields(known.fields, fields)) return known;
7238
+ const index = recentKits.findIndex((kit) => sameFields(kit.fields, fields));
7239
+ let kit;
7240
+ if (index >= 0) {
7241
+ kit = recentKits[index];
7242
+ recentKits.splice(index, 1);
7243
+ } else {
7244
+ const hook = makeKnown(fields);
7245
+ kit = {
7246
+ fields,
7247
+ evaluators: makeEvaluators(fields),
7248
+ known: hook,
7249
+ facts: makeFacts(fields, hook)
6573
7250
  };
6574
7251
  }
6575
- return out;
7252
+ recentKits.unshift(kit);
7253
+ if (recentKits.length > RECENT_KITS) recentKits.length = RECENT_KITS;
7254
+ kitsByProfile.set(profile, kit);
7255
+ return kit;
6576
7256
  }
6577
7257
  /** This function makes the code of the evaluators of the formula kit, without their type rules. */
6578
7258
  function formulaCode(profile) {
@@ -6891,10 +7571,16 @@ function formulaCode(profile) {
6891
7571
  * `formula.ext` knows an extension of `ext` and each standard extension. The hook knows each name of the other
6892
7572
  * evaluators.
6893
7573
  *
7574
+ * Equal profiles give the same hook (D46).
7575
+ *
6894
7576
  * @param profile - The operations and the extensions of the language.
6895
7577
  * @returns The hook: `true`, or the reason of the `#NAME?` that the evaluator gives in a run.
6896
7578
  */
6897
7579
  function formulaKnown(profile = {}) {
7580
+ return kitOf(profile).known;
7581
+ }
7582
+ /** This function makes a new hook `known` for the fields of one profile. */
7583
+ function makeKnown(profile) {
6898
7584
  const hasOp = (name) => {
6899
7585
  if (profile.lazy !== void 0 && Object.hasOwn(profile.lazy, name)) return true;
6900
7586
  const ops = profile.ops;
@@ -6928,13 +7614,18 @@ const EACH_PART = /* @__PURE__ */ Object.freeze([/* @__PURE__ */ Object.freeze({
6928
7614
  * - An operation is pure when the profile names it in `pure`, or when `pure` is `true`. A volatile operation is not pure.
6929
7615
  * - A standard extension is pure. An extension of the profile gets the context, thus it is not pure.
6930
7616
  *
7617
+ * Equal profiles give the same hook (D46).
7618
+ *
6931
7619
  * @param profile - The operations, the extensions and their facts.
6932
7620
  * @returns The hook: the facts of a name, or `undefined` for the facts of the contract.
6933
7621
  */
6934
7622
  function formulaFacts(profile = {}) {
7623
+ return kitOf(profile).facts;
7624
+ }
7625
+ /** This function makes a new hook `facts` for the fields of one profile, with the hook `known` of the same fields. */
7626
+ function makeFacts(profile, known) {
6935
7627
  const volatile = new Set(profile.volatile ?? []);
6936
7628
  const pure = profile.pure === true ? void 0 : new Set(profile.pure ?? []);
6937
- const known = formulaKnown(profile);
6938
7629
  return (path, code) => {
6939
7630
  if (code !== "formula.op" && code !== "formula.ext") return void 0;
6940
7631
  const name = path.slice(1).join(".");
@@ -6962,18 +7653,23 @@ function formulaFacts(profile = {}) {
6962
7653
  * The kit gives the domains of the kernel (`STD_DOMAINS`). Thus a domain of the language with the name `number` is a
6963
7654
  * conflict. The language names `["domains", "number"]` in `replaces` to replace it.
6964
7655
  *
7656
+ * Equal profiles give the same evaluators and the same hooks (COMPOSE.UNIFY, D46). Thus two machines of one profile
7657
+ * merge, also when each adds its own language. Two profiles with different fields give different evaluators, and
7658
+ * their machines conflict at each evaluator name of the kit.
7659
+ *
6965
7660
  * @param profile - The operations and the rules of the language.
6966
7661
  * @param more - The parts of a machine that the language adds, with the paths of the entries that it replaces.
6967
7662
  * @returns The machine.
6968
7663
  * @throws {MergeError} The language and the kit have a conflict.
6969
7664
  */
6970
7665
  function formulaMachine(profile = {}, more = {}) {
7666
+ const kit = kitOf(profile);
6971
7667
  return mergeMachines({
6972
7668
  world: { kinds: formulaKinds() },
6973
- evaluators: formulaEvaluators(profile),
7669
+ evaluators: { ...kit.evaluators },
6974
7670
  domains: STD_DOMAINS,
6975
- known: formulaKnown(profile),
6976
- facts: formulaFacts(profile)
7671
+ known: kit.known,
7672
+ facts: kit.facts
6977
7673
  }, more);
6978
7674
  }
6979
7675
  //#endregion
@@ -7857,13 +8553,22 @@ function examine$1(env, at) {
7857
8553
  split
7858
8554
  };
7859
8555
  }
8556
+ /**
8557
+ * This function gives the effects that two lists of capabilities both permit (CAP.ONLY). The reference reads the rule
8558
+ * from SPEC directly, and not from the intersection of the plan. Thus `agree` finds a bug in either one (D15).
8559
+ */
8560
+ function bothPermit(a, b) {
8561
+ const out = [];
8562
+ for (const cap of [...a, ...b]) if (permits(a, cap) && permits(b, cap) && !permits(out, cap)) out.push(cap);
8563
+ return out;
8564
+ }
7860
8565
  /** This function gives the capabilities that the rules of a kind give one part (D14, CAP.CONTRACT, CAP.ONLY). */
7861
8566
  function capsOfPart(rules, path, caps) {
7862
8567
  let out = caps;
7863
8568
  for (const rule of rules ?? []) {
7864
8569
  if (rule.in !== void 0 && !rule.in.some((pattern) => pathMatches(path, pattern))) continue;
7865
8570
  if (rule.rule === "mask") return [];
7866
- if (rule.rule === "only") out = intersectCaps(out, rule.caps);
8571
+ if (rule.rule === "only") out = bothPermit(out, rule.caps);
7867
8572
  }
7868
8573
  return out;
7869
8574
  }
@@ -8135,6 +8840,7 @@ function reference(machine, doc, context = makeContext(), options = {}) {
8135
8840
  ...context,
8136
8841
  world: worldThing(machine.world)
8137
8842
  } : context;
8843
+ activeEnvs.push(env);
8138
8844
  try {
8139
8845
  return evaluateAt(env, {
8140
8846
  value: doc,
@@ -8146,8 +8852,12 @@ function reference(machine, doc, context = makeContext(), options = {}) {
8146
8852
  });
8147
8853
  } catch (error) {
8148
8854
  return placed(thrown(error, "the reference"), []);
8855
+ } finally {
8856
+ activeEnvs.pop();
8149
8857
  }
8150
8858
  }
8859
+ /** The evaluations of the reference that run at this time, the innermost last. */
8860
+ const activeEnvs = [];
8151
8861
  const refNodes = /* @__PURE__ */ new WeakMap();
8152
8862
  /** The attributes in evaluation in the reference, in the order of their demands (TREE.CYCLE). */
8153
8863
  const inEvaluation = [];
@@ -8157,28 +8867,109 @@ function nameOfAttribute(node, name) {
8157
8867
  function refNodeOf(thing) {
8158
8868
  return typeof thing === "object" && thing !== null ? refNodes.get(thing) : void 0;
8159
8869
  }
8160
- /** TREE.ONCE, TREE.MEMO and TREE.CYCLE: the result of one attribute, or `undefined` when the node has no such part. */
8161
- function attributeOf(node, self, name) {
8870
+ /**
8871
+ * TREE.LADDER: an equation of the kind of a node evaluates in the reference. It gets the node as `self`, the names of
8872
+ * its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind.
8873
+ */
8874
+ function equationOf(node, term, names) {
8875
+ const env = node.env;
8876
+ if (env === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
8877
+ const ctx = {
8878
+ ...node.ctx,
8879
+ names,
8880
+ caps: [],
8881
+ self: node.thing
8882
+ };
8883
+ return reference(env.machine, term, ctx, {
8884
+ definitions: env.definitions,
8885
+ ...node.of === void 0 ? {} : { declaredType: node.of }
8886
+ });
8887
+ }
8888
+ function termIn(table, name) {
8889
+ return table === void 0 || !Object.hasOwn(table, name) ? void 0 : table[name];
8890
+ }
8891
+ /** TREE.DOWN: the names of a child for an equation of `down` of its parent: `child`, `index` and `prev`. */
8892
+ function namesOfChild(up, child) {
8893
+ const children = attributeOf(up, "children");
8894
+ if (children !== void 0 && !children.ok) return children;
8895
+ const list = children !== void 0 && Array.isArray(children.value) ? children.value : [];
8896
+ const index = list.indexOf(child.thing);
8897
+ return ok({
8898
+ child: child.thing,
8899
+ index: index < 0 ? null : index,
8900
+ prev: index > 0 ? list[index - 1] : null
8901
+ });
8902
+ }
8903
+ /** TREE.FOLD: the unit, then `combine` with the attribute of each node of this tree in the list `children`. */
8904
+ function foldOf(node, name, fold) {
8905
+ let acc = equationOf(node, fold.unit, {});
8906
+ if (!acc.ok) return acc;
8907
+ const children = attributeOf(node, "children");
8908
+ if (children === void 0) return acc;
8909
+ if (!children.ok) return children;
8910
+ if (!Array.isArray(children.value)) return fail("#VALUE!", `The fold of ${name} needs a list in children.`);
8911
+ for (const item of children.value) {
8912
+ const child = refNodeOf(item);
8913
+ if (child === void 0 || child.tree !== node.tree) continue;
8914
+ const value = attributeOf(child, name);
8915
+ if (value === void 0) return fail("#N/A", `No rung gives ${nameOfAttribute(child, name)}.`);
8916
+ if (!value.ok) return value;
8917
+ acc = equationOf(node, fold.combine, {
8918
+ acc: acc.value,
8919
+ item: value.value
8920
+ });
8921
+ if (!acc.ok) return acc;
8922
+ }
8923
+ return acc;
8924
+ }
8925
+ /**
8926
+ * TREE.LADDER: the rungs of the ladder of an attribute, in their order. A rung gives `undefined` when it does not
8927
+ * answer, and the next rung then tries.
8928
+ */
8929
+ const REF_RUNGS = [
8930
+ ["own", (node, name) => node.parts.find((item) => item.key === name)?.evaluate({
8931
+ ...node.ctx,
8932
+ self: node.thing
8933
+ })],
8934
+ ["kind", (node, name) => {
8935
+ const term = termIn(node.contract?.equations, name);
8936
+ return term === void 0 ? void 0 : equationOf(node, term, {});
8937
+ }],
8938
+ ["parent", (node, name) => {
8939
+ const up = node.up;
8940
+ const term = up === void 0 ? void 0 : termIn(up.contract?.down, name);
8941
+ if (up === void 0 || term === void 0) return void 0;
8942
+ const names = namesOfChild(up, node);
8943
+ return names.ok ? equationOf(up, term, names.value) : names;
8944
+ }],
8945
+ ["copy", (node, name) => node.up !== void 0 && termIn(node.contract?.attributes, name)?.copy === true ? attributeOf(node.up, name) : void 0],
8946
+ ["default", (node, name) => {
8947
+ const declared = termIn(node.contract?.attributes, name);
8948
+ if (declared?.fold !== void 0) return foldOf(node, name, declared.fold);
8949
+ return declared?.default === void 0 ? void 0 : equationOf(node, declared.default, {});
8950
+ }]
8951
+ ];
8952
+ /** TREE.ONCE, TREE.MEMO and TREE.CYCLE: the result of one attribute, or `undefined` when no rung of its ladder answers. */
8953
+ function attributeOf(node, name) {
8162
8954
  const done = node.values.get(name);
8163
8955
  if (done !== void 0) return done;
8164
8956
  if (node.pending.has(name)) {
8165
8957
  const from = inEvaluation.findIndex((entry) => entry.node === node && entry.name === name);
8166
8958
  return fail("#CYCLE!", `The attributes form a cycle: ${[...from < 0 ? [nameOfAttribute(node, name)] : inEvaluation.slice(from).map((entry) => nameOfAttribute(entry.node, entry.name)), nameOfAttribute(node, name)].join(" -> ")}.`);
8167
8959
  }
8168
- const part = node.parts.find((item) => item.key === name);
8169
- if (part === void 0) return void 0;
8170
8960
  node.pending.add(name);
8171
8961
  inEvaluation.push({
8172
8962
  node,
8173
8963
  name
8174
8964
  });
8175
8965
  try {
8176
- const result = part.evaluate({
8177
- ...node.ctx,
8178
- self
8179
- });
8180
- node.values.set(name, result);
8181
- return result;
8966
+ for (const [, rung] of REF_RUNGS) {
8967
+ const result = rung(node, name);
8968
+ if (result === void 0) continue;
8969
+ node.values.set(name, result);
8970
+ return result;
8971
+ }
8972
+ return;
8182
8973
  } finally {
8183
8974
  node.pending.delete(name);
8184
8975
  inEvaluation.pop();
@@ -8188,7 +8979,7 @@ function attributeOf(node, self, name) {
8188
8979
  function jsonOfNode(node, self, open) {
8189
8980
  const record = {};
8190
8981
  for (const part of node.parts) {
8191
- const result = attributeOf(node, self, part.key);
8982
+ const result = attributeOf(node, part.key);
8192
8983
  if (!result.ok) return result;
8193
8984
  const value = plainValue(result.value, [...open, self]);
8194
8985
  if (!value.ok) return value;
@@ -8224,17 +9015,28 @@ function plainValue(value, open) {
8224
9015
  }
8225
9016
  /**
8226
9017
  * TREE.LAZY, TREE.PARENT, TREE.SIBLING and TREE.ERROR: a node of an open tree gives its thing, and the root of a new
8227
- * tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error.
9018
+ * tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error. The node
9019
+ * reads `self` only through `resolve` (C4, TREE.SELF).
8228
9020
  */
8229
- const treeDefinition = ({ parts, context, kind, place }) => {
8230
- const outer = refNodeOf(context.self);
9021
+ const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolve: walk }) => {
9022
+ const reached = walk(["self"]);
9023
+ const parent = reached.ok ? reached.thing : void 0;
9024
+ const outer = refNodeOf(parent);
8231
9025
  const nested = outer !== void 0 && outer.tree.open;
9026
+ const found = machine === void 0 || typePath.length === 0 ? void 0 : lookupKind(machine.world, typePath);
9027
+ const contract = found?.ok === true ? found.kind.tree : void 0;
9028
+ const of = contract === void 0 || found?.ok !== true ? void 0 : equationType(found.kind);
8232
9029
  const node = {
8233
9030
  tree: nested ? outer.tree : { open: true },
8234
9031
  kind,
8235
9032
  place,
8236
9033
  parts,
8237
9034
  ctx: context,
9035
+ contract,
9036
+ of,
9037
+ up: nested ? outer : void 0,
9038
+ env: activeEnvs[activeEnvs.length - 1],
9039
+ thing: null,
8238
9040
  values: /* @__PURE__ */ new Map(),
8239
9041
  pending: /* @__PURE__ */ new Set()
8240
9042
  };
@@ -8243,19 +9045,20 @@ const treeDefinition = ({ parts, context, kind, place }) => {
8243
9045
  type: kind,
8244
9046
  label: `node ${kind}`,
8245
9047
  deref: (key) => {
8246
- if (key === "parent") return context.self;
9048
+ if (key === "parent") return parent;
8247
9049
  if (key === "type") return kind;
8248
- const result = attributeOf(node, self, String(key));
9050
+ const result = attributeOf(node, String(key));
8249
9051
  if (result === void 0 || result.ok) return result?.ok === true ? result.value : void 0;
8250
9052
  throw new Failure(result.error);
8251
9053
  },
8252
- has: (key) => key === "parent" || key === "type" || parts.some((part) => part.key === String(key)),
9054
+ has: (key) => key === "parent" || key === "type" || parts.some((part) => part.key === String(key)) || node.values.has(String(key)),
8253
9055
  toJson: () => {
8254
9056
  const result = jsonOfNode(node, self, []);
8255
9057
  if (result.ok) return result.value;
8256
9058
  throw new Failure(result.error);
8257
9059
  }
8258
9060
  };
9061
+ node.thing = self;
8259
9062
  refNodes.set(self, node);
8260
9063
  if (nested) return ok(self);
8261
9064
  try {
@@ -8487,6 +9290,13 @@ function valuesOfStrict(parts) {
8487
9290
  }
8488
9291
  //#endregion
8489
9292
  //#region src/agree.ts
9293
+ /** The tiers that `agree` compares when its options name none. */
9294
+ const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
9295
+ "reference",
9296
+ "traced",
9297
+ "again",
9298
+ "folds"
9299
+ ]);
8490
9300
  /**
8491
9301
  * This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
8492
9302
  * by field, and the order of the fields does not matter. Each other leaf compares with `Object.is`, thus `-0` is not
@@ -8576,7 +9386,11 @@ function agree(machine, doc, context = makeContext(), options = {}) {
8576
9386
  };
8577
9387
  const observe = options.observe;
8578
9388
  observe?.();
8579
- const plan = prepare(machine, doc, declared);
9389
+ const folds = observe === void 0;
9390
+ const plan = prepare(machine, doc, {
9391
+ ...declared,
9392
+ fold: folds
9393
+ });
8580
9394
  const effects = [];
8581
9395
  const result = plan.evaluate(context, {
8582
9396
  effects,
@@ -8589,11 +9403,7 @@ function agree(machine, doc, context = makeContext(), options = {}) {
8589
9403
  if (why !== void 0 || observe === void 0) return why;
8590
9404
  return sameValue(saw, observe(), options.equal) ? void 0 : "The host saw other calls in the tier.";
8591
9405
  };
8592
- for (const tier of options.tiers ?? [
8593
- "reference",
8594
- "traced",
8595
- "again"
8596
- ]) {
9406
+ for (const tier of options.tiers ?? DEFAULT_TIERS) {
8597
9407
  let other;
8598
9408
  let otherEffects = [];
8599
9409
  if (tier === "reference") other = reference(machine, doc, context, {
@@ -8609,6 +9419,27 @@ function agree(machine, doc, context = makeContext(), options = {}) {
8609
9419
  });
8610
9420
  other = traced.result;
8611
9421
  otherEffects = [...traced.effects];
9422
+ } else if (tier === "folds") {
9423
+ other = prepare(machine, doc, {
9424
+ ...declared,
9425
+ fold: !folds
9426
+ }).evaluate(context, {
9427
+ effects: otherEffects,
9428
+ ...mock
9429
+ });
9430
+ if (observe !== void 0) {
9431
+ observe();
9432
+ if (overflowed(result) || overflowed(other)) continue;
9433
+ const why = resultDiffers(result, other, options) ?? effectsDiffer(effects, otherEffects, options);
9434
+ if (why !== void 0) return {
9435
+ ok: false,
9436
+ tier,
9437
+ plan: result,
9438
+ other,
9439
+ reason: why
9440
+ };
9441
+ continue;
9442
+ }
8612
9443
  } else other = plan.evaluate(context, {
8613
9444
  effects: otherEffects,
8614
9445
  ...mock
@@ -11255,6 +12086,6 @@ function typeFromSamples(values, options = {}) {
11255
12086
  return samplesAt(values, options.closed === true, 0);
11256
12087
  }
11257
12088
  //#endregion
11258
- export { 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, capKeys, check, checkLatent, checkSteps, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, encodeValue, entriesAsJson, 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 };
12089
+ 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, 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 };
11259
12090
 
11260
12091
  //# sourceMappingURL=index.js.map