@mark1russell7/coeval 0.7.1 → 0.8.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",
@@ -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
  }
@@ -3416,6 +3500,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
3416
3500
  let meta = meta0;
3417
3501
  let name;
3418
3502
  let unknown;
3503
+ let quick;
3419
3504
  if (c.traced) {
3420
3505
  const answer = ask(world, envelope ?? {}, c.verb, declared === void 0 ? {} : { declaredType: declared });
3421
3506
  meta = {
@@ -3426,7 +3511,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
3426
3511
  name = answer.evaluator;
3427
3512
  unknown = answer.error === "unknown-kind";
3428
3513
  } else {
3429
- const quick = answerOf(world, envelope?.own, kind ?? null, c.verb);
3514
+ quick = answerOf(world, envelope?.own, kind ?? null, c.verb);
3430
3515
  name = quick?.name;
3431
3516
  unknown = quick === void 0 && kind === void 0;
3432
3517
  }
@@ -3437,9 +3522,11 @@ function compileNode(c, value, place, declared, depth, address = false) {
3437
3522
  const evaluator = name !== void 0 && Object.hasOwn(c.machine.evaluators, name) ? c.machine.evaluators[name] : void 0;
3438
3523
  if (name === void 0 || runner === void 0 && evaluator === void 0) return failing(c, meta, place, "#NOEVAL", `No rung gives an evaluator for ${kindText}.`);
3439
3524
  if (!split.ok) return failing(c, meta, place, "#VALUE!", split.reason);
3525
+ const frees = c.fold ? [] : void 0;
3440
3526
  const parts = split.parts.map((slot) => {
3441
3527
  const item = slot.item;
3442
3528
  const own = compileNode(c, item, place.concat(slot.path), slot.of, depth + 1, slot.address === true);
3529
+ frees?.push(own.free);
3443
3530
  const rules = rulesFor(kind?.caps, slot.path);
3444
3531
  const compiled = rules.length === 0 ? own : gated(rules, own);
3445
3532
  const header = typeof item?.name === "string" && isEnvelope(item) ? item.name : void 0;
@@ -3457,9 +3544,10 @@ function compileNode(c, value, place, declared, depth, address = false) {
3457
3544
  run
3458
3545
  };
3459
3546
  });
3547
+ const typePath = read.typePath ?? [];
3460
3548
  const input = {
3461
3549
  kind: kindText,
3462
- typePath: read.typePath ?? [],
3550
+ typePath,
3463
3551
  doc,
3464
3552
  value: split.parts.length === 0 ? doc.value : split.fill(parts),
3465
3553
  data: split.data,
@@ -3485,7 +3573,238 @@ function compileNode(c, value, place, declared, depth, address = false) {
3485
3573
  } catch (error) {
3486
3574
  return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
3487
3575
  }
3488
- return finish$1(c, meta, placedCode(inner, place, name), void 0);
3576
+ const done = finish$1(c, meta, placedCode(inner, place, name), void 0);
3577
+ if (frees === void 0 || kind === void 0 || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
3578
+ const free = freeOf(doc, kind, split.parts, frees);
3579
+ if (free === void 0) return done;
3580
+ return free.length === 0 ? folded(c, done) : {
3581
+ code: done.code,
3582
+ run: done.run,
3583
+ free
3584
+ };
3585
+ }
3586
+ /** The free names of a closed part. */
3587
+ const CLOSED = /* @__PURE__ */ Object.freeze([]);
3588
+ /**
3589
+ * This function tells if the code of a document is pure, by the facts of D24 (FACTS.PURE). It reads the facts as
3590
+ * `factsOf` reads them. A kind with a run side, with declared effects or with volatile code is not pure here.
3591
+ */
3592
+ function pureCode(c, envelope, kind, typePath, name, quick) {
3593
+ if (kind.side === "run" || kind.effects !== void 0 && kind.effects.length > 0) return false;
3594
+ let answer;
3595
+ try {
3596
+ answer = c.machine.facts?.(typePath, name);
3597
+ } catch {
3598
+ return false;
3599
+ }
3600
+ const described = c.verb === "eval" && (envelope === void 0 || quick?.rung !== "own" || name === answerOf(c.machine.world, void 0, kind, c.verb)?.name);
3601
+ const pure = described ? answer?.pure ?? kind.pure : answer?.pure;
3602
+ const volatile = described ? answer?.volatile ?? kind.volatile : answer?.volatile;
3603
+ return pure === true && volatile !== true;
3604
+ }
3605
+ /**
3606
+ * This function gives the names that a document reads and does not bind, or `undefined` when the document can read
3607
+ * other roots. A reference reads the name of its path. Each other document reads the free names of its parts, less
3608
+ * the names that its scope rules give each part (STATIC.REFS).
3609
+ */
3610
+ function freeOf(doc, kind, slots, frees) {
3611
+ if (isRef(doc)) {
3612
+ const path = doc.value;
3613
+ if (path.length < 2 || path[0] !== "names" || !path.every((step, index) => index === 0 || isKey(step))) return void 0;
3614
+ return [String(path[1])];
3615
+ }
3616
+ let sees;
3617
+ let out;
3618
+ for (let index = 0; index < frees.length; index += 1) {
3619
+ const free = frees[index];
3620
+ if (free === void 0) return void 0;
3621
+ if (free.length === 0) continue;
3622
+ if (sees === void 0) sees = kind.scope === void 0 || kind.scope.length === 0 ? [] : scopesOf(kind.scope, doc.value, slots).sees;
3623
+ const bound = sees[index] ?? CLOSED;
3624
+ for (const name of free) {
3625
+ if (bound.includes(name)) continue;
3626
+ out ??= [];
3627
+ if (!out.includes(name)) out.push(name);
3628
+ }
3629
+ }
3630
+ return out ?? CLOSED;
3631
+ }
3632
+ /** This class is the runtime of a fold. It reads only the names that the folded part binds, and it causes no effect. */
3633
+ var Probe = class {
3634
+ machine;
3635
+ running = false;
3636
+ /** `true` after the part asked for something that a fold cannot give. */
3637
+ refused = false;
3638
+ /** The count of the names that the part read. */
3639
+ reads = 0;
3640
+ /**
3641
+ * The context of the folded part. Its roots `self`, `event` and `names` are traps: a direct read of a field
3642
+ * refuses the fold, as a resolution of these roots does. A copy of the record of the names reads no field.
3643
+ */
3644
+ root;
3645
+ constructor(machine) {
3646
+ this.machine = machine;
3647
+ const refuse = () => {
3648
+ this.refused = true;
3649
+ };
3650
+ const root = {
3651
+ self: trap(refuse, false),
3652
+ event: trap(refuse, false),
3653
+ names: trap(refuse, true),
3654
+ world: worldThing(machine.world),
3655
+ caps: NO_CAPS$2,
3656
+ [GRANT]: NO_CAPS$2
3657
+ };
3658
+ this.root = root;
3659
+ }
3660
+ resolve(ctx, path) {
3661
+ const name = path[1];
3662
+ if (path[0] === "names" && isKey(name) && Object.hasOwn(ctx.names, String(name))) {
3663
+ this.reads += 1;
3664
+ const found = resolve(this.machine, ctx, path, WITH_HOPS);
3665
+ if (found.hops.some((hop) => hop.by === "collapse" || isRef(hop.thing))) this.refused = true;
3666
+ return found;
3667
+ }
3668
+ this.refused = true;
3669
+ return {
3670
+ ok: false,
3671
+ code: "#REF!",
3672
+ reason: "A folded part reads only the names that it binds.",
3673
+ hops: []
3674
+ };
3675
+ }
3676
+ effect() {
3677
+ this.refused = true;
3678
+ return fail("#CAP!", "A folded part causes no effect.");
3679
+ }
3680
+ *runEffect() {
3681
+ return this.effect();
3682
+ }
3683
+ *all() {
3684
+ this.refused = true;
3685
+ return [];
3686
+ }
3687
+ keepsContext() {
3688
+ this.refused = true;
3689
+ }
3690
+ };
3691
+ /**
3692
+ * 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
3693
+ * names gives no keys, thus a copy of the names reads no field.
3694
+ */
3695
+ function trap(refuse, copies) {
3696
+ const no = () => {
3697
+ refuse();
3698
+ };
3699
+ return new Proxy({}, {
3700
+ get: no,
3701
+ getOwnPropertyDescriptor: no,
3702
+ has: () => (refuse(), false),
3703
+ ownKeys: () => {
3704
+ if (!copies) refuse();
3705
+ return [];
3706
+ },
3707
+ getPrototypeOf: () => (refuse(), null),
3708
+ set: () => (refuse(), false),
3709
+ defineProperty: () => (refuse(), false),
3710
+ deleteProperty: () => (refuse(), false)
3711
+ });
3712
+ }
3713
+ /**
3714
+ * This function folds the code of a closed pure part. The part evaluates one time with the probe. Its result becomes
3715
+ * one deeply frozen copy, and the code gives that copy at each evaluation (PLAN.SHARED). A fold that the probe cancels
3716
+ * gives the code without free names, thus no parent tries the fold again.
3717
+ */
3718
+ function folded(c, done) {
3719
+ const probe = c.probe ??= new Probe(c.machine);
3720
+ probe.refused = false;
3721
+ probe.reads = 0;
3722
+ let constant;
3723
+ try {
3724
+ const result = done.code(probe.root, probe);
3725
+ if (!probe.refused && (result.ok || result.error.code !== "#DEPTH!")) constant = sharedCopy(result);
3726
+ } catch {
3727
+ constant = void 0;
3728
+ }
3729
+ if (constant === void 0) return {
3730
+ code: done.code,
3731
+ run: done.run
3732
+ };
3733
+ const value = constant;
3734
+ if (probe.reads === 0) return {
3735
+ code: () => value,
3736
+ run: void 0,
3737
+ free: CLOSED
3738
+ };
3739
+ const original = done.code;
3740
+ return {
3741
+ code: (ctx, rt) => rt instanceof Session && !rt.traced && !rt.recording ? value : original(ctx, rt),
3742
+ run: void 0,
3743
+ free: CLOSED
3744
+ };
3745
+ }
3746
+ /** The objects that a fold made. Each one is deeply frozen, thus a later fold uses it again and does not copy it. */
3747
+ const SHARED = /* @__PURE__ */ new WeakSet();
3748
+ /** The mark of a value that is not plain data. */
3749
+ const NOT_PLAIN = /* @__PURE__ */ Symbol("not plain");
3750
+ /**
3751
+ * This function gives a deeply frozen copy of plain data, or `undefined` for other values. Plain data is a primitive or
3752
+ * an array without holes and extra keys. It is also a record of enumerable data fields with the prototype of an object
3753
+ * or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change.
3754
+ */
3755
+ function sharedCopy(value) {
3756
+ try {
3757
+ const out = copyPlain(value, /* @__PURE__ */ new Map());
3758
+ return out === NOT_PLAIN ? void 0 : out;
3759
+ } catch {
3760
+ return;
3761
+ }
3762
+ }
3763
+ /** This function copies one value for `sharedCopy`. The map holds the copy of each object that the copy saw. */
3764
+ function copyPlain(value, memo) {
3765
+ if (typeof value === "function") return NOT_PLAIN;
3766
+ if (typeof value !== "object" || value === null) return value;
3767
+ if (SHARED.has(value)) return value;
3768
+ const seen = memo.get(value);
3769
+ if (seen !== void 0) return seen;
3770
+ const proto = Object.getPrototypeOf(value);
3771
+ const keys = Reflect.ownKeys(value);
3772
+ let out;
3773
+ if (Array.isArray(value)) {
3774
+ if (proto !== Array.prototype || keys.length !== value.length + 1) return NOT_PLAIN;
3775
+ const list = [];
3776
+ memo.set(value, list);
3777
+ for (let index = 0; index < value.length; index += 1) {
3778
+ const field = Object.getOwnPropertyDescriptor(value, index);
3779
+ if (field === void 0 || !("value" in field) || field.enumerable !== true) return NOT_PLAIN;
3780
+ const item = copyPlain(field.value, memo);
3781
+ if (item === NOT_PLAIN) return NOT_PLAIN;
3782
+ list.push(item);
3783
+ }
3784
+ out = list;
3785
+ } else {
3786
+ if (proto !== Object.prototype && proto !== null) return NOT_PLAIN;
3787
+ const record = proto === null ? Object.create(null) : {};
3788
+ memo.set(value, record);
3789
+ for (const key of keys) {
3790
+ if (typeof key !== "string") return NOT_PLAIN;
3791
+ const field = Object.getOwnPropertyDescriptor(value, key);
3792
+ if (field === void 0 || !("value" in field) || field.enumerable !== true) return NOT_PLAIN;
3793
+ const item = copyPlain(field.value, memo);
3794
+ if (item === NOT_PLAIN) return NOT_PLAIN;
3795
+ if (key === "__proto__") Object.defineProperty(record, key, {
3796
+ value: item,
3797
+ enumerable: true,
3798
+ writable: true,
3799
+ configurable: true
3800
+ });
3801
+ else record[key] = item;
3802
+ }
3803
+ out = record;
3804
+ }
3805
+ Object.freeze(out);
3806
+ SHARED.add(out);
3807
+ return out;
3489
3808
  }
3490
3809
  /**
3491
3810
  * This function gives the code of a document: the code of its evaluator, with the place of each error and of each
@@ -3650,12 +3969,14 @@ function prepare(machine, doc, options = {}) {
3650
3969
  error: "no-answer",
3651
3970
  steps: []
3652
3971
  };
3972
+ const traced = options.trace === true;
3653
3973
  const c = {
3654
3974
  machine,
3655
- traced: options.trace === true,
3975
+ traced,
3656
3976
  maxDepth: machine.maxDepth ?? 1e3,
3657
3977
  verb,
3658
- noAnswer
3978
+ noAnswer,
3979
+ fold: !traced && options.fold !== false
3659
3980
  };
3660
3981
  let root;
3661
3982
  try {
@@ -3753,8 +4074,8 @@ function record(s, result) {
3753
4074
  * tells where its parts are. The evaluator gets each part sealed: it can evaluate the part, and it cannot read its
3754
4075
  * value.
3755
4076
  *
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).
4077
+ * A failure gives an error value, and the function does not throw. The function records each effect in its order,
4078
+ * and the host does not do it. A mock can answer each effect at once (RUN.MOCK).
3758
4079
  *
3759
4080
  * @param machine - The world and the code of the evaluators.
3760
4081
  * @param doc - The document, as JSON.
@@ -4112,6 +4433,56 @@ const STD_EVALUATORS = {
4112
4433
  };
4113
4434
  /** The brand of a node of the decorated tree. Its value is the state that the kit reads. */
4114
4435
  const TREE_NODE = /* @__PURE__ */ Symbol.for("coeval.tree.node");
4436
+ /** The rungs of the ladder of an attribute, in their order (TREE.LADDER, D42). */
4437
+ const ATTRIBUTE_RUNGS = /* @__PURE__ */ Object.freeze([
4438
+ "own",
4439
+ "kind",
4440
+ "parent",
4441
+ "copy",
4442
+ "default"
4443
+ ]);
4444
+ /** The capabilities of an equation: none (CAP.PURE). */
4445
+ const NO_CAPS$1 = /* @__PURE__ */ Object.freeze([]);
4446
+ /** The names of an equation of `equations`, of a `unit` and of a `default`. */
4447
+ const NO_NAMES$1 = /* @__PURE__ */ Object.freeze({});
4448
+ /** The planners of equations, for each machine and each type of a bare equation (TREE.PLAN). */
4449
+ const equationPlanners = /* @__PURE__ */ new WeakMap();
4450
+ /**
4451
+ * This function gives the plan of an equation. Each equation compiles one time for each machine (PLAN.CACHE). The
4452
+ * plan of the document does not keep it (PLAN.LEAN).
4453
+ */
4454
+ function equationPlan(machine, of, term) {
4455
+ let byType = equationPlanners.get(machine);
4456
+ if (byType === void 0) {
4457
+ byType = /* @__PURE__ */ new Map();
4458
+ equationPlanners.set(machine, byType);
4459
+ }
4460
+ const key = of ?? "";
4461
+ let plans = byType.get(key);
4462
+ if (plans === void 0) {
4463
+ plans = planner(machine, of === void 0 ? {} : { declaredType: of });
4464
+ byType.set(key, plans);
4465
+ }
4466
+ return plans(term);
4467
+ }
4468
+ /** This function gives the own entry of a table of a tree contract, or `undefined`. */
4469
+ function entryOf(table, key) {
4470
+ return table !== void 0 && Object.hasOwn(table, key) ? table[key] : void 0;
4471
+ }
4472
+ const NO_CONTRACT = /* @__PURE__ */ Object.freeze({
4473
+ tree: void 0,
4474
+ of: void 0
4475
+ });
4476
+ /** This function finds the tree contract of the kind of a node in the world of the machine. */
4477
+ function contractOf$1(machine, typePath) {
4478
+ if (machine === void 0 || typePath.length === 0) return NO_CONTRACT;
4479
+ const found = lookupKind(machine.world, typePath);
4480
+ if (!found.ok || found.kind.tree === void 0) return NO_CONTRACT;
4481
+ return {
4482
+ tree: found.kind.tree,
4483
+ of: equationType(found.kind)
4484
+ };
4485
+ }
4115
4486
  const BUSY = /* @__PURE__ */ Object.freeze({ busy: true });
4116
4487
  /** The attributes in evaluation, in the order of their demands. An evaluation does not wait, thus one list serves. */
4117
4488
  const demands = [];
@@ -4179,28 +4550,92 @@ function plainOf(value, path) {
4179
4550
  }
4180
4551
  /**
4181
4552
  * 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.
4553
+ * step `type` gives its type. Another step reads an attribute through its ladder (TREE.LADDER). The rungs are a part of
4554
+ * the node, an equation of its kind and an equation `down` of the kind of its parent. A copy from the parent, then a
4555
+ * fold or a default, come last. A failed attribute throws `Failure` with its own error, thus a reference to it gives that error.
4184
4556
  */
4185
- function treeThing(parts, context, type, place, build) {
4557
+ function treeThing(input, build) {
4558
+ const { parts, context, kind: type, place, machine } = input;
4559
+ const { tree, of } = contractOf$1(machine, input.typePath);
4560
+ const above = stateOf(context.self);
4561
+ const up = above !== void 0 && above.build === build ? above : void 0;
4186
4562
  const cells = /* @__PURE__ */ new Map();
4563
+ /** The positions of the items of the list `children`, for the names of the children. */
4564
+ let positions;
4565
+ const fold = (key, declared) => {
4566
+ let acc = state.equation(declared.unit, NO_NAMES$1);
4567
+ if (!acc.ok) return acc;
4568
+ const children = read("children");
4569
+ if (children === void 0) return acc;
4570
+ if (!children.ok) return children;
4571
+ if (!Array.isArray(children.value)) return fail("#VALUE!", `The fold of ${key} needs a list in children.`);
4572
+ for (const item of children.value) {
4573
+ const child = stateOf(item);
4574
+ if (child === void 0 || child.build !== build) continue;
4575
+ const value = child.read(key);
4576
+ if (value === void 0) return fail("#N/A", `No rung gives ${attributeText(key, child.type, child.place)}.`);
4577
+ if (!value.ok) return value;
4578
+ acc = state.equation(declared.combine, {
4579
+ acc: acc.value,
4580
+ item: value.value
4581
+ });
4582
+ if (!acc.ok) return acc;
4583
+ }
4584
+ return acc;
4585
+ };
4586
+ /** This function visits the rungs of the ladder of an attribute in their order, until one answers. */
4587
+ const climb = (key) => {
4588
+ const part = parts.find((item) => item.key === key);
4589
+ if (part !== void 0) return {
4590
+ result: part.evaluate({
4591
+ ...context,
4592
+ self: node
4593
+ }),
4594
+ by: "own"
4595
+ };
4596
+ const equation = entryOf(tree?.equations, key);
4597
+ if (equation !== void 0) return {
4598
+ result: state.equation(equation, NO_NAMES$1),
4599
+ by: "kind"
4600
+ };
4601
+ const down = up === void 0 ? void 0 : entryOf(up.tree?.down, key);
4602
+ if (up !== void 0 && down !== void 0) {
4603
+ const names = up.childNames(node);
4604
+ return {
4605
+ result: names.ok ? up.equation(down, names.value) : names,
4606
+ by: "parent"
4607
+ };
4608
+ }
4609
+ const declared = entryOf(tree?.attributes, key);
4610
+ if (declared?.copy === true && up !== void 0) {
4611
+ const copied = up.read(key);
4612
+ if (copied !== void 0) return {
4613
+ result: copied,
4614
+ by: "copy"
4615
+ };
4616
+ }
4617
+ if (declared?.fold !== void 0) return {
4618
+ result: fold(key, declared.fold),
4619
+ by: "default"
4620
+ };
4621
+ if (declared?.default !== void 0) return {
4622
+ result: state.equation(declared.default, NO_NAMES$1),
4623
+ by: "default"
4624
+ };
4625
+ };
4187
4626
  const read = (key) => {
4188
4627
  const cell = cells.get(key);
4189
4628
  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;
4629
+ if (tree === void 0 && up?.tree === void 0 && !parts.some((item) => item.key === key)) return void 0;
4192
4630
  cells.set(key, BUSY);
4193
4631
  demands.push({
4194
4632
  cells,
4195
4633
  key,
4196
4634
  text: attributeText(key, type, place)
4197
4635
  });
4198
- let result;
4636
+ let answer;
4199
4637
  try {
4200
- result = part.evaluate({
4201
- ...context,
4202
- self: node
4203
- });
4638
+ answer = climb(key);
4204
4639
  } catch (error) {
4205
4640
  cells.delete(key);
4206
4641
  throw error;
@@ -4209,14 +4644,60 @@ function treeThing(parts, context, type, place, build) {
4209
4644
  }
4210
4645
  cells.set(key, {
4211
4646
  busy: false,
4212
- result
4647
+ result: answer?.result,
4648
+ by: answer?.by
4213
4649
  });
4214
- return result;
4650
+ return answer?.result;
4215
4651
  };
4216
4652
  const state = {
4217
4653
  build,
4218
4654
  type,
4219
4655
  place,
4656
+ tree,
4657
+ read,
4658
+ equation(term, names) {
4659
+ if (machine === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
4660
+ return equationPlan(machine, of, term).evaluate({
4661
+ ...context,
4662
+ names,
4663
+ caps: NO_CAPS$1,
4664
+ self: node
4665
+ });
4666
+ },
4667
+ childNames(child) {
4668
+ const children = read("children");
4669
+ if (children !== void 0 && !children.ok) return children;
4670
+ const list = children !== void 0 && Array.isArray(children.value) ? children.value : [];
4671
+ if (positions?.list !== list) {
4672
+ const at = /* @__PURE__ */ new Map();
4673
+ list.forEach((item, index) => {
4674
+ if (!at.has(item)) at.set(item, index);
4675
+ });
4676
+ positions = {
4677
+ list,
4678
+ at
4679
+ };
4680
+ }
4681
+ const index = positions.at.get(child);
4682
+ return ok({
4683
+ child,
4684
+ index: index ?? null,
4685
+ prev: index === void 0 || index === 0 ? null : list[index - 1]
4686
+ });
4687
+ },
4688
+ trace(key) {
4689
+ const cell = cells.get(key);
4690
+ if (cell === void 0 || cell.busy) return void 0;
4691
+ const rungs = cell.by === void 0 ? ATTRIBUTE_RUNGS : ATTRIBUTE_RUNGS.slice(0, ATTRIBUTE_RUNGS.indexOf(cell.by) + 1);
4692
+ return cell.by === void 0 ? {
4693
+ attribute: key,
4694
+ rungs
4695
+ } : {
4696
+ attribute: key,
4697
+ rungs,
4698
+ by: cell.by
4699
+ };
4700
+ },
4220
4701
  json(path) {
4221
4702
  path.add(node);
4222
4703
  try {
@@ -4248,7 +4729,9 @@ function treeThing(parts, context, type, place, build) {
4248
4729
  return result.value;
4249
4730
  },
4250
4731
  has(key) {
4251
- return key === "parent" || key === "type" || parts.some((part) => part.key === String(key));
4732
+ if (key === "parent" || key === "type" || parts.some((part) => part.key === String(key))) return true;
4733
+ const cell = cells.get(String(key));
4734
+ return cell !== void 0 && !cell.busy && cell.result !== void 0;
4252
4735
  },
4253
4736
  toJson() {
4254
4737
  const result = state.json(/* @__PURE__ */ new Set());
@@ -4259,14 +4742,25 @@ function treeThing(parts, context, type, place, build) {
4259
4742
  return node;
4260
4743
  }
4261
4744
  /**
4745
+ * This function gives the record of the demand of an attribute of a node of the decorated tree (TREE.RUNGS). The
4746
+ * record holds the rungs that the demand visited, and the rung that answered. An explorer draws the ladder with it.
4747
+ *
4748
+ * @param node - The thing of a node.
4749
+ * @param attribute - The name of the attribute.
4750
+ * @returns The record, or `undefined` for a thing that is not a node or an attribute that no demand reached.
4751
+ */
4752
+ function attributeTrace(node, attribute) {
4753
+ return stateOf(node)?.trace(attribute);
4754
+ }
4755
+ /**
4262
4756
  * This function evaluates a node of the decorated tree. Inside an attribute of an open tree, the node gives its host
4263
4757
  * thing, and no attribute evaluates. Else the node is the root of a new tree, and it gives the JSON of the tree.
4264
4758
  */
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));
4759
+ function treeNode(input) {
4760
+ const above = stateOf(input.context.self);
4761
+ if (above !== void 0 && above.build.open) return ok(treeThing(input, above.build));
4268
4762
  const build = { open: true };
4269
- const root = treeThing(parts, context, kind, place, build);
4763
+ const root = treeThing(input, build);
4270
4764
  try {
4271
4765
  return stateOf(root).json(/* @__PURE__ */ new Set());
4272
4766
  } finally {
@@ -5009,75 +5503,6 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
5009
5503
  }
5010
5504
  //#endregion
5011
5505
  //#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
5506
  /** 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
5507
  const NO_NAMES = /* @__PURE__ */ new Set();
5083
5508
  /** This function gives the options of a walk without the names of the host. */
@@ -5422,11 +5847,81 @@ function typeCheck(machine, doc, options = {}) {
5422
5847
  places: pass.places
5423
5848
  };
5424
5849
  }
5850
+ /** The names that an equation of `down` sees (TREE.DOWN). */
5851
+ const DOWN_NAMES = /* @__PURE__ */ Object.freeze([
5852
+ "child",
5853
+ "index",
5854
+ "prev"
5855
+ ]);
5856
+ /** The names that the term `combine` of a fold sees (TREE.FOLD). */
5857
+ const FOLD_NAMES = /* @__PURE__ */ Object.freeze(["acc", "item"]);
5858
+ /**
5859
+ * This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
5860
+ * `equations`, a `unit` and a `default` see no names.
5861
+ */
5862
+ function equationsOf(kind) {
5863
+ const tree = kind.tree;
5864
+ if (tree === void 0) return [];
5865
+ const out = [];
5866
+ for (const [name, term] of Object.entries(tree.equations ?? {})) out.push({
5867
+ label: `tree.equations.${name}`,
5868
+ term,
5869
+ names: []
5870
+ });
5871
+ for (const [name, term] of Object.entries(tree.down ?? {})) out.push({
5872
+ label: `tree.down.${name}`,
5873
+ term,
5874
+ names: DOWN_NAMES
5875
+ });
5876
+ for (const [name, declared] of Object.entries(tree.attributes ?? {})) {
5877
+ if (declared.fold !== void 0) {
5878
+ out.push({
5879
+ label: `tree.attributes.${name}.fold.unit`,
5880
+ term: declared.fold.unit,
5881
+ names: []
5882
+ });
5883
+ out.push({
5884
+ label: `tree.attributes.${name}.fold.combine`,
5885
+ term: declared.fold.combine,
5886
+ names: FOLD_NAMES
5887
+ });
5888
+ }
5889
+ if (declared.default !== void 0) out.push({
5890
+ label: `tree.attributes.${name}.default`,
5891
+ term: declared.default,
5892
+ names: []
5893
+ });
5894
+ }
5895
+ return out;
5896
+ }
5897
+ /**
5898
+ * This function checks the equations of the tree contract of a kind, each with the names of its rung (TREE.DOWN-NAMES).
5899
+ * An equation evaluates without capabilities, thus a check with capabilities gives it none. Each issue gets the place
5900
+ * of the document of the kind, and its reason names the equation.
5901
+ */
5902
+ function equationIssues(machine, kind, kindText, options, seen) {
5903
+ const of = equationType(kind);
5904
+ const out = [];
5905
+ for (const { label, term, names } of equationsOf(kind)) {
5906
+ const inner = {
5907
+ names,
5908
+ ...of === void 0 ? {} : { declaredType: of },
5909
+ ...options.caps === void 0 ? {} : { caps: [] }
5910
+ };
5911
+ for (const issue of checkAll(machine, term, inner, void 0, seen)) out.push({
5912
+ code: issue.code,
5913
+ place: [],
5914
+ reason: `In the equation ${label} of the kind ${kindText}: ${issue.reason}`
5915
+ });
5916
+ }
5917
+ return out;
5918
+ }
5425
5919
  /**
5426
5920
  * 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.
5921
+ * the pass of types. It marks each document with an issue. The set `seen` holds the kinds whose equations the check
5922
+ * walked, thus it walks the equations of each kind one time.
5428
5923
  */
5429
- function checkAll(machine, doc, options, nodes) {
5924
+ function checkAll(machine, doc, options, nodes, seen = /* @__PURE__ */ new Set()) {
5430
5925
  const issues = [];
5431
5926
  const verb = options.verb ?? "eval";
5432
5927
  const indices = nodes === void 0 ? void 0 : /* @__PURE__ */ new Map();
@@ -5478,6 +5973,14 @@ function checkAll(machine, doc, options, nodes) {
5478
5973
  });
5479
5974
  return "skip";
5480
5975
  }
5976
+ if (v.kind?.tree !== void 0 && !seen.has(v.kind)) {
5977
+ seen.add(v.kind);
5978
+ for (const issue of equationIssues(machine, v.kind, typeName(v.doc.type), options, seen)) push({
5979
+ ...issue,
5980
+ place: v.place,
5981
+ at: v.at
5982
+ });
5983
+ }
5481
5984
  const name = answer.evaluator;
5482
5985
  if (current !== void 0) current.evaluator = name;
5483
5986
  const runner = name !== void 0 && has(machine.runners, name);
@@ -7857,13 +8360,22 @@ function examine$1(env, at) {
7857
8360
  split
7858
8361
  };
7859
8362
  }
8363
+ /**
8364
+ * This function gives the effects that two lists of capabilities both permit (CAP.ONLY). The reference reads the rule
8365
+ * from SPEC directly, and not from the intersection of the plan. Thus `agree` finds a bug in either one (D15).
8366
+ */
8367
+ function bothPermit(a, b) {
8368
+ const out = [];
8369
+ for (const cap of [...a, ...b]) if (permits(a, cap) && permits(b, cap) && !permits(out, cap)) out.push(cap);
8370
+ return out;
8371
+ }
7860
8372
  /** This function gives the capabilities that the rules of a kind give one part (D14, CAP.CONTRACT, CAP.ONLY). */
7861
8373
  function capsOfPart(rules, path, caps) {
7862
8374
  let out = caps;
7863
8375
  for (const rule of rules ?? []) {
7864
8376
  if (rule.in !== void 0 && !rule.in.some((pattern) => pathMatches(path, pattern))) continue;
7865
8377
  if (rule.rule === "mask") return [];
7866
- if (rule.rule === "only") out = intersectCaps(out, rule.caps);
8378
+ if (rule.rule === "only") out = bothPermit(out, rule.caps);
7867
8379
  }
7868
8380
  return out;
7869
8381
  }
@@ -8135,6 +8647,7 @@ function reference(machine, doc, context = makeContext(), options = {}) {
8135
8647
  ...context,
8136
8648
  world: worldThing(machine.world)
8137
8649
  } : context;
8650
+ activeEnvs.push(env);
8138
8651
  try {
8139
8652
  return evaluateAt(env, {
8140
8653
  value: doc,
@@ -8146,8 +8659,12 @@ function reference(machine, doc, context = makeContext(), options = {}) {
8146
8659
  });
8147
8660
  } catch (error) {
8148
8661
  return placed(thrown(error, "the reference"), []);
8662
+ } finally {
8663
+ activeEnvs.pop();
8149
8664
  }
8150
8665
  }
8666
+ /** The evaluations of the reference that run at this time, the innermost last. */
8667
+ const activeEnvs = [];
8151
8668
  const refNodes = /* @__PURE__ */ new WeakMap();
8152
8669
  /** The attributes in evaluation in the reference, in the order of their demands (TREE.CYCLE). */
8153
8670
  const inEvaluation = [];
@@ -8157,28 +8674,109 @@ function nameOfAttribute(node, name) {
8157
8674
  function refNodeOf(thing) {
8158
8675
  return typeof thing === "object" && thing !== null ? refNodes.get(thing) : void 0;
8159
8676
  }
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) {
8677
+ /**
8678
+ * TREE.LADDER: an equation of the kind of a node evaluates in the reference. It gets the node as `self`, the names of
8679
+ * its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind.
8680
+ */
8681
+ function equationOf(node, term, names) {
8682
+ const env = node.env;
8683
+ if (env === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
8684
+ const ctx = {
8685
+ ...node.ctx,
8686
+ names,
8687
+ caps: [],
8688
+ self: node.thing
8689
+ };
8690
+ return reference(env.machine, term, ctx, {
8691
+ definitions: env.definitions,
8692
+ ...node.of === void 0 ? {} : { declaredType: node.of }
8693
+ });
8694
+ }
8695
+ function termIn(table, name) {
8696
+ return table === void 0 || !Object.hasOwn(table, name) ? void 0 : table[name];
8697
+ }
8698
+ /** TREE.DOWN: the names of a child for an equation of `down` of its parent: `child`, `index` and `prev`. */
8699
+ function namesOfChild(up, child) {
8700
+ const children = attributeOf(up, "children");
8701
+ if (children !== void 0 && !children.ok) return children;
8702
+ const list = children !== void 0 && Array.isArray(children.value) ? children.value : [];
8703
+ const index = list.indexOf(child.thing);
8704
+ return ok({
8705
+ child: child.thing,
8706
+ index: index < 0 ? null : index,
8707
+ prev: index > 0 ? list[index - 1] : null
8708
+ });
8709
+ }
8710
+ /** TREE.FOLD: the unit, then `combine` with the attribute of each node of this tree in the list `children`. */
8711
+ function foldOf(node, name, fold) {
8712
+ let acc = equationOf(node, fold.unit, {});
8713
+ if (!acc.ok) return acc;
8714
+ const children = attributeOf(node, "children");
8715
+ if (children === void 0) return acc;
8716
+ if (!children.ok) return children;
8717
+ if (!Array.isArray(children.value)) return fail("#VALUE!", `The fold of ${name} needs a list in children.`);
8718
+ for (const item of children.value) {
8719
+ const child = refNodeOf(item);
8720
+ if (child === void 0 || child.tree !== node.tree) continue;
8721
+ const value = attributeOf(child, name);
8722
+ if (value === void 0) return fail("#N/A", `No rung gives ${nameOfAttribute(child, name)}.`);
8723
+ if (!value.ok) return value;
8724
+ acc = equationOf(node, fold.combine, {
8725
+ acc: acc.value,
8726
+ item: value.value
8727
+ });
8728
+ if (!acc.ok) return acc;
8729
+ }
8730
+ return acc;
8731
+ }
8732
+ /**
8733
+ * TREE.LADDER: the rungs of the ladder of an attribute, in their order. A rung gives `undefined` when it does not
8734
+ * answer, and the next rung then tries.
8735
+ */
8736
+ const REF_RUNGS = [
8737
+ ["own", (node, name) => node.parts.find((item) => item.key === name)?.evaluate({
8738
+ ...node.ctx,
8739
+ self: node.thing
8740
+ })],
8741
+ ["kind", (node, name) => {
8742
+ const term = termIn(node.contract?.equations, name);
8743
+ return term === void 0 ? void 0 : equationOf(node, term, {});
8744
+ }],
8745
+ ["parent", (node, name) => {
8746
+ const up = node.up;
8747
+ const term = up === void 0 ? void 0 : termIn(up.contract?.down, name);
8748
+ if (up === void 0 || term === void 0) return void 0;
8749
+ const names = namesOfChild(up, node);
8750
+ return names.ok ? equationOf(up, term, names.value) : names;
8751
+ }],
8752
+ ["copy", (node, name) => node.up !== void 0 && termIn(node.contract?.attributes, name)?.copy === true ? attributeOf(node.up, name) : void 0],
8753
+ ["default", (node, name) => {
8754
+ const declared = termIn(node.contract?.attributes, name);
8755
+ if (declared?.fold !== void 0) return foldOf(node, name, declared.fold);
8756
+ return declared?.default === void 0 ? void 0 : equationOf(node, declared.default, {});
8757
+ }]
8758
+ ];
8759
+ /** TREE.ONCE, TREE.MEMO and TREE.CYCLE: the result of one attribute, or `undefined` when no rung of its ladder answers. */
8760
+ function attributeOf(node, name) {
8162
8761
  const done = node.values.get(name);
8163
8762
  if (done !== void 0) return done;
8164
8763
  if (node.pending.has(name)) {
8165
8764
  const from = inEvaluation.findIndex((entry) => entry.node === node && entry.name === name);
8166
8765
  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
8766
  }
8168
- const part = node.parts.find((item) => item.key === name);
8169
- if (part === void 0) return void 0;
8170
8767
  node.pending.add(name);
8171
8768
  inEvaluation.push({
8172
8769
  node,
8173
8770
  name
8174
8771
  });
8175
8772
  try {
8176
- const result = part.evaluate({
8177
- ...node.ctx,
8178
- self
8179
- });
8180
- node.values.set(name, result);
8181
- return result;
8773
+ for (const [, rung] of REF_RUNGS) {
8774
+ const result = rung(node, name);
8775
+ if (result === void 0) continue;
8776
+ node.values.set(name, result);
8777
+ return result;
8778
+ }
8779
+ return;
8182
8780
  } finally {
8183
8781
  node.pending.delete(name);
8184
8782
  inEvaluation.pop();
@@ -8188,7 +8786,7 @@ function attributeOf(node, self, name) {
8188
8786
  function jsonOfNode(node, self, open) {
8189
8787
  const record = {};
8190
8788
  for (const part of node.parts) {
8191
- const result = attributeOf(node, self, part.key);
8789
+ const result = attributeOf(node, part.key);
8192
8790
  if (!result.ok) return result;
8193
8791
  const value = plainValue(result.value, [...open, self]);
8194
8792
  if (!value.ok) return value;
@@ -8226,15 +8824,23 @@ function plainValue(value, open) {
8226
8824
  * 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
8825
  * tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error.
8228
8826
  */
8229
- const treeDefinition = ({ parts, context, kind, place }) => {
8827
+ const treeDefinition = ({ parts, context, kind, place, machine, typePath }) => {
8230
8828
  const outer = refNodeOf(context.self);
8231
8829
  const nested = outer !== void 0 && outer.tree.open;
8830
+ const found = machine === void 0 || typePath.length === 0 ? void 0 : lookupKind(machine.world, typePath);
8831
+ const contract = found?.ok === true ? found.kind.tree : void 0;
8832
+ const of = contract === void 0 || found?.ok !== true ? void 0 : equationType(found.kind);
8232
8833
  const node = {
8233
8834
  tree: nested ? outer.tree : { open: true },
8234
8835
  kind,
8235
8836
  place,
8236
8837
  parts,
8237
8838
  ctx: context,
8839
+ contract,
8840
+ of,
8841
+ up: nested ? outer : void 0,
8842
+ env: activeEnvs[activeEnvs.length - 1],
8843
+ thing: null,
8238
8844
  values: /* @__PURE__ */ new Map(),
8239
8845
  pending: /* @__PURE__ */ new Set()
8240
8846
  };
@@ -8245,17 +8851,18 @@ const treeDefinition = ({ parts, context, kind, place }) => {
8245
8851
  deref: (key) => {
8246
8852
  if (key === "parent") return context.self;
8247
8853
  if (key === "type") return kind;
8248
- const result = attributeOf(node, self, String(key));
8854
+ const result = attributeOf(node, String(key));
8249
8855
  if (result === void 0 || result.ok) return result?.ok === true ? result.value : void 0;
8250
8856
  throw new Failure(result.error);
8251
8857
  },
8252
- has: (key) => key === "parent" || key === "type" || parts.some((part) => part.key === String(key)),
8858
+ has: (key) => key === "parent" || key === "type" || parts.some((part) => part.key === String(key)) || node.values.has(String(key)),
8253
8859
  toJson: () => {
8254
8860
  const result = jsonOfNode(node, self, []);
8255
8861
  if (result.ok) return result.value;
8256
8862
  throw new Failure(result.error);
8257
8863
  }
8258
8864
  };
8865
+ node.thing = self;
8259
8866
  refNodes.set(self, node);
8260
8867
  if (nested) return ok(self);
8261
8868
  try {
@@ -8487,6 +9094,13 @@ function valuesOfStrict(parts) {
8487
9094
  }
8488
9095
  //#endregion
8489
9096
  //#region src/agree.ts
9097
+ /** The tiers that `agree` compares when its options name none. */
9098
+ const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
9099
+ "reference",
9100
+ "traced",
9101
+ "again",
9102
+ "folds"
9103
+ ]);
8490
9104
  /**
8491
9105
  * This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
8492
9106
  * by field, and the order of the fields does not matter. Each other leaf compares with `Object.is`, thus `-0` is not
@@ -8576,7 +9190,11 @@ function agree(machine, doc, context = makeContext(), options = {}) {
8576
9190
  };
8577
9191
  const observe = options.observe;
8578
9192
  observe?.();
8579
- const plan = prepare(machine, doc, declared);
9193
+ const folds = observe === void 0;
9194
+ const plan = prepare(machine, doc, {
9195
+ ...declared,
9196
+ fold: folds
9197
+ });
8580
9198
  const effects = [];
8581
9199
  const result = plan.evaluate(context, {
8582
9200
  effects,
@@ -8589,11 +9207,7 @@ function agree(machine, doc, context = makeContext(), options = {}) {
8589
9207
  if (why !== void 0 || observe === void 0) return why;
8590
9208
  return sameValue(saw, observe(), options.equal) ? void 0 : "The host saw other calls in the tier.";
8591
9209
  };
8592
- for (const tier of options.tiers ?? [
8593
- "reference",
8594
- "traced",
8595
- "again"
8596
- ]) {
9210
+ for (const tier of options.tiers ?? DEFAULT_TIERS) {
8597
9211
  let other;
8598
9212
  let otherEffects = [];
8599
9213
  if (tier === "reference") other = reference(machine, doc, context, {
@@ -8609,6 +9223,27 @@ function agree(machine, doc, context = makeContext(), options = {}) {
8609
9223
  });
8610
9224
  other = traced.result;
8611
9225
  otherEffects = [...traced.effects];
9226
+ } else if (tier === "folds") {
9227
+ other = prepare(machine, doc, {
9228
+ ...declared,
9229
+ fold: !folds
9230
+ }).evaluate(context, {
9231
+ effects: otherEffects,
9232
+ ...mock
9233
+ });
9234
+ if (observe !== void 0) {
9235
+ observe();
9236
+ if (overflowed(result) || overflowed(other)) continue;
9237
+ const why = resultDiffers(result, other, options) ?? effectsDiffer(effects, otherEffects, options);
9238
+ if (why !== void 0) return {
9239
+ ok: false,
9240
+ tier,
9241
+ plan: result,
9242
+ other,
9243
+ reason: why
9244
+ };
9245
+ continue;
9246
+ }
8612
9247
  } else other = plan.evaluate(context, {
8613
9248
  effects: otherEffects,
8614
9249
  ...mock
@@ -11255,6 +11890,6 @@ function typeFromSamples(values, options = {}) {
11255
11890
  return samplesAt(values, options.closed === true, 0);
11256
11891
  }
11257
11892
  //#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 };
11893
+ 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, 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
11894
 
11260
11895
  //# sourceMappingURL=index.js.map