@mark1russell7/coeval 0.20.1 → 0.21.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
@@ -354,6 +354,11 @@ function describe(value) {
354
354
  }
355
355
  }
356
356
  // src/world.ts
357
+ /** The rule of the registry of binding names. */
358
+ const NAMES_RULE = /* @__PURE__ */ Object.freeze({
359
+ ok: true,
360
+ names: true
361
+ });
357
362
  /** The most rounds of a circular attribute without its own `limit` (TREE.CIRCULAR-LIMIT, D113). */
358
363
  const CIRCULAR_LIMIT = 100;
359
364
  /** This function tells if a value is JSON data: `null`, a truth value, a finite number, a text, or a container of them. */
@@ -403,6 +408,26 @@ function circularOf(declared) {
403
408
  };
404
409
  }
405
410
  /**
411
+ * This function reads the declaration `collect` of an attribute (D122). The tree kit, the reference, `check` and the
412
+ * graph of attribute instances read it with this one function.
413
+ *
414
+ * @param declared - The value of the field `collect`.
415
+ * @returns The rule of the collection, or the reason when the declaration has no correct form.
416
+ */
417
+ function collectOf(declared) {
418
+ if (declared === "names") return NAMES_RULE;
419
+ if (!isRecord(declared) || !Object.hasOwn(declared, "unit") || !Object.hasOwn(declared, "combine")) return {
420
+ ok: false,
421
+ reason: "The declaration collect needs the text \"names\" or a record with the fields unit and combine."
422
+ };
423
+ return {
424
+ ok: true,
425
+ names: false,
426
+ unit: field(declared, "unit"),
427
+ combine: field(declared, "combine")
428
+ };
429
+ }
430
+ /**
406
431
  * 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
407
432
  * else the type `of` of the shape `rest` of a record. The plan, the reference and `check` read it with this one function.
408
433
  *
@@ -1584,8 +1609,10 @@ function valueStep(value, key) {
1584
1609
  *
1585
1610
  * - A record gives the type of its field. A closed record without the field has no such step. An open record gives
1586
1611
  * `unknown`.
1587
- * - A list gives the type of its items for an index, and `number` for `length`.
1588
- * - A base type reads the steps of its domain, then of each parent (`Domain.steps`).
1612
+ * - A list gives the type of its items for an index, and `number` for `length`. Another key has no such step.
1613
+ * - A base type reads the steps of its domain, then of each parent (`Domain.steps`). A base type of the kernel
1614
+ * without such a step has no step, because its values are not objects (TYPE.NO-STEP, D120).
1615
+ * - A function type has no step, because a step into a function reaches nothing.
1589
1616
  * - A union joins the steps of its types. `one` gives the values that the step reaches.
1590
1617
  * - Each other step gives `unknown`, and `never` gives `never`.
1591
1618
  *
@@ -1604,10 +1631,11 @@ function stepType(type, key, domains, examine, depth = 0) {
1604
1631
  const found = steps[name];
1605
1632
  return { type: examine === void 0 ? found : examine(found, `type of the step ${name} of the domain ${each}`) };
1606
1633
  }
1607
- return { type: "unknown" };
1634
+ return Object.hasOwn(STD_DOMAINS, form.name) ? NO_STEP : { type: "unknown" };
1635
+ case "fn": return NO_STEP;
1608
1636
  case "list":
1609
1637
  if (key === "length") return { type: "number" };
1610
- return { type: indexOf(key) >= 0 ? form.item : "unknown" };
1638
+ return indexOf(key) >= 0 ? { type: form.item } : NO_STEP;
1611
1639
  case "record":
1612
1640
  if (Object.hasOwn(form.fields, name)) return { type: form.fields[name] };
1613
1641
  return form.closed ? { missing: Object.keys(form.fields) } : { type: "unknown" };
@@ -1634,6 +1662,8 @@ function stepType(type, key, domains, examine, depth = 0) {
1634
1662
  default: return { type: "unknown" };
1635
1663
  }
1636
1664
  }
1665
+ /** The answer of `stepType` for a type whose members have no step with the key, and no fields to name. */
1666
+ const NO_STEP = { missing: [] };
1637
1667
  /**
1638
1668
  * This function lists the problems of a value type (TYPE.ISSUES). A problem is a term that is not a value type, a
1639
1669
  * base type without a domain, or a term that is too deep. `fits` and `member` read each such term as `unknown`.
@@ -3420,6 +3450,34 @@ function pathMatches(path, pattern) {
3420
3450
  }
3421
3451
  return true;
3422
3452
  }
3453
+ /**
3454
+ * This function tells if a list of rules in the form of the lazy rules covers a part (D24, D119). A rule without
3455
+ * `in` covers each part. With `after: n`, a rule covers only the parts after the first `n` parts that it matches.
3456
+ *
3457
+ * @param rules - The rules.
3458
+ * @param shape - The shape of the parts of the parent.
3459
+ * @param value - The value of the parent.
3460
+ * @param slot - The path of the part in the value.
3461
+ * @returns `true` when a rule covers the part.
3462
+ */
3463
+ function rulesCover(rules, shape, value, slot) {
3464
+ for (const rule of rules) {
3465
+ if (rule.in !== void 0 && !rule.in.some((pattern) => pathMatches(slot, pattern))) continue;
3466
+ const after = rule.after ?? 0;
3467
+ if (after <= 0) return true;
3468
+ const split = splitParts(shape, value);
3469
+ let count = 0;
3470
+ for (const part of split.ok ? split.parts : []) {
3471
+ if (rule.in !== void 0 && !rule.in.some((pattern) => pathMatches(part.path, pattern))) continue;
3472
+ if (part.path.length === slot.length && pathMatches(part.path, slot)) {
3473
+ if (count >= after) return true;
3474
+ break;
3475
+ }
3476
+ count += 1;
3477
+ }
3478
+ }
3479
+ return false;
3480
+ }
3423
3481
  function valueAt(value, path) {
3424
3482
  let here = value;
3425
3483
  for (const key of path) if (Array.isArray(here)) here = here[Number(key)];
@@ -4583,6 +4641,7 @@ function pureCode(c, envelope, kind, typePath, name, quick) {
4583
4641
  } catch {
4584
4642
  return false;
4585
4643
  }
4644
+ if (answer?.effects !== void 0 && answer.effects.length > 0) return false;
4586
4645
  const described = c.verb === "eval" && (envelope === void 0 || quick?.rung !== "own" || name === answerOf(c.machine.world, void 0, kind, c.verb)?.name);
4587
4646
  const pure = described ? answer?.pure ?? kind.pure : answer?.pure;
4588
4647
  const volatile = described ? answer?.volatile ?? kind.volatile : answer?.volatile;
@@ -5425,6 +5484,14 @@ const STD_WORLD = {
5425
5484
  pure: true,
5426
5485
  summary: "A step of a path: it follows a key again and again, until it reaches a node of a type."
5427
5486
  },
5487
+ tree: { kinds: { named: {
5488
+ verbs: {
5489
+ step: "walk.named",
5490
+ view: "view.card"
5491
+ },
5492
+ pure: true,
5493
+ summary: "A step of a path: it goes up to the nearest registry of names, and it gives the node with the name (TREE.NAMED)."
5494
+ } } },
5428
5495
  panel: {
5429
5496
  verbs: {
5430
5497
  eval: "tree.node",
@@ -5664,6 +5731,12 @@ const WORLD_SYNTAX = /* @__PURE__ */ Object.freeze({
5664
5731
  "self"
5665
5732
  ]
5666
5733
  },
5734
+ {
5735
+ name: "tree.named",
5736
+ kind: "tree.named",
5737
+ args: "named",
5738
+ fields: ["name", "in"]
5739
+ },
5667
5740
  {
5668
5741
  name: "add",
5669
5742
  kind: "add"
@@ -6203,6 +6276,58 @@ function plainOf(value, path) {
6203
6276
  }
6204
6277
  }
6205
6278
  /**
6279
+ * This function gives the collection of an attribute of a node (TREE.COLLECT, D122). It visits the node, then each node
6280
+ * of the same tree in each list `children`, in the order of the lists. Each node is visited one time.
6281
+ * The registry of names (TREE.NAMED) gives a record from each binding name to its node.
6282
+ */
6283
+ function collected(root, key, declared) {
6284
+ const top = stateOf(root);
6285
+ const rule = collectOf(declared);
6286
+ if (!rule.ok) return fail("#VALUE!", `The attribute ${attributeText(key, top.type, top.place)} has a bad declaration collect. ${rule.reason}`);
6287
+ const registry = {};
6288
+ const owners = /* @__PURE__ */ new Map();
6289
+ let acc = rule.names ? ok(registry) : top.equation(rule.unit, NO_NAMES$1);
6290
+ if (!acc.ok) return acc;
6291
+ const seen = /* @__PURE__ */ new Set();
6292
+ const stack = [root];
6293
+ while (stack.length > 0) {
6294
+ const thing = stack.pop();
6295
+ const at = stateOf(thing);
6296
+ if (at === void 0 || at.build !== top.build || seen.has(at)) continue;
6297
+ seen.add(at);
6298
+ if (rule.names) {
6299
+ const name = at.name;
6300
+ const earlier = name === void 0 ? void 0 : owners.get(name);
6301
+ if (earlier !== void 0) {
6302
+ const both = `${earlier.type} at [${earlier.place.join(", ")}] and ${at.type} at [${at.place.join(", ")}]`;
6303
+ return fail("#VALUE!", `Two nodes have the name ${name} in the registry ${attributeText(key, top.type, top.place)}: ${both}.`);
6304
+ }
6305
+ if (name !== void 0) {
6306
+ owners.set(name, at);
6307
+ putField(registry, name, thing);
6308
+ }
6309
+ } else {
6310
+ const term = entryOf(at.tree?.contributes, key);
6311
+ if (term !== void 0) {
6312
+ const item = at.equation(term, NO_NAMES$1);
6313
+ if (!item.ok) return item;
6314
+ acc = top.equation(rule.combine, {
6315
+ acc: acc.value,
6316
+ item: item.value
6317
+ });
6318
+ if (!acc.ok) return acc;
6319
+ }
6320
+ }
6321
+ const children = at.read("children");
6322
+ if (children === void 0) continue;
6323
+ if (!children.ok) return children;
6324
+ if (!Array.isArray(children.value)) return fail("#VALUE!", `The collection of ${key} needs a list in children of ${at.type} at [${at.place.join(", ")}].`);
6325
+ const list = children.value;
6326
+ for (let index = list.length - 1; index >= 0; index -= 1) stack.push(list[index]);
6327
+ }
6328
+ return acc;
6329
+ }
6330
+ /**
6206
6331
  * This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
6207
6332
  * step `type` gives its type. Another step reads an attribute through its ladder (TREE.LADDER). The rungs are a part of
6208
6333
  * the node, an equation of its kind and an equation `down` of the kind of its parent. A copy from the parent, then a
@@ -6279,6 +6404,10 @@ function treeThing(input, place, build, outer, host) {
6279
6404
  result: fold(key, declared.fold),
6280
6405
  by: "default"
6281
6406
  };
6407
+ if (declared?.collect !== void 0) return {
6408
+ result: collected(node, key, declared.collect),
6409
+ by: "default"
6410
+ };
6282
6411
  if (declared?.default !== void 0) return {
6283
6412
  result: state.equation(declared.default, NO_NAMES$1),
6284
6413
  by: "default"
@@ -6357,6 +6486,7 @@ function treeThing(input, place, build, outer, host) {
6357
6486
  };
6358
6487
  const state = {
6359
6488
  build,
6489
+ name: input.doc.name,
6360
6490
  type,
6361
6491
  place,
6362
6492
  tree,
@@ -6538,8 +6668,45 @@ function nearestWalker(options = {}) {
6538
6668
  };
6539
6669
  };
6540
6670
  }
6671
+ const nearest = /* @__PURE__ */ nearestWalker();
6672
+ /**
6673
+ * The walker of the step `tree.named` (TREE.NAMED, D122). It goes up from the thing before the step through the key
6674
+ * `parent`, to the first thing that answers the registry. The field `in` names the registry, and the default is
6675
+ * `names`. The walker gives the node with the field `name` in that registry.
6676
+ */
6677
+ const named$2 = ({ data, here, deref }) => {
6678
+ const name = isRecord(data) ? field(data, "name") : void 0;
6679
+ if (typeof name !== "string") return {
6680
+ ok: false,
6681
+ code: "#VALUE!",
6682
+ reason: "The step tree.named needs a text name."
6683
+ };
6684
+ const registry = isRecord(data) && typeof field(data, "in") === "string" ? String(field(data, "in")) : "names";
6685
+ let at = here;
6686
+ for (let hops = 0; hops < 256 && at !== void 0 && at !== null; hops += 1) {
6687
+ const found = deref(at, registry);
6688
+ if (found !== void 0) {
6689
+ if (!isRecord(found) || !Object.hasOwn(found, name)) return {
6690
+ ok: false,
6691
+ reason: `The registry ${registry} has no node with the name ${name}.`
6692
+ };
6693
+ return {
6694
+ ok: true,
6695
+ thing: field(found, name)
6696
+ };
6697
+ }
6698
+ at = deref(at, "parent");
6699
+ }
6700
+ return {
6701
+ ok: false,
6702
+ reason: `No registry ${registry} is on the way.`
6703
+ };
6704
+ };
6541
6705
  /** The walkers of the standard world, by name. */
6542
- const STD_WALKERS = { "walk.nearest": /* @__PURE__ */ nearestWalker() };
6706
+ const STD_WALKERS = {
6707
+ "walk.nearest": nearest,
6708
+ "walk.named": named$2
6709
+ };
6543
6710
  /** This function evaluates parts one after the other, and stops at the first error. */
6544
6711
  function* inOrder(parts) {
6545
6712
  const values = [];
@@ -7524,7 +7691,7 @@ function factsOf(machine, doc, options = {}) {
7524
7691
  at: tight(place.at)
7525
7692
  });
7526
7693
  }
7527
- const declared = declaredEffects(v, verb);
7694
+ const declared = verb === "eval" && answer?.effects !== void 0 ? effectKeys(v, answer.effects) : declaredEffects(v, verb);
7528
7695
  if (verb === "eval" ? kind.side === "run" : declared.length > 0) {
7529
7696
  if (node === void 0) side = "run";
7530
7697
  else pure = false;
@@ -8245,8 +8412,57 @@ function rungReads(b, id) {
8245
8412
  });
8246
8413
  return;
8247
8414
  }
8415
+ const collect = declared?.collect === void 0 ? void 0 : collectOf(declared.collect);
8416
+ if (collect?.ok === true) {
8417
+ collectReads(b, node, key, collect, later);
8418
+ return;
8419
+ }
8248
8420
  if (declared?.default !== void 0) termReads(b, termRefs(b, node.kind, `tree.attributes.${key}.default`, declared.default), node, void 0, later);
8249
8421
  }
8422
+ /** This function gives the list children of a node before the run. A node without a rung for it has no children. */
8423
+ function childrenOf(b, at) {
8424
+ const part = at.parts.get("children");
8425
+ if (part !== void 0) return valueOf$1(part);
8426
+ const tree = at.kind.tree;
8427
+ if (hasOwnEntry(tree?.equations, "children") || hasOwnEntry(tree?.attributes, "children")) return "unknown";
8428
+ if (at.up === void 0) return downNames(b.machine.world).has("children") ? "unknown" : "data";
8429
+ return at.up !== null && hasOwnEntry(at.up.kind.tree?.down, "children") ? "unknown" : "data";
8430
+ }
8431
+ /**
8432
+ * This function adds the reads of a collection (TREE.COLLECT, D122), in the order of the run. Each node of the subtree
8433
+ * gives the reads of its contribution and of its list `children`. A list that only the run knows gives an edge with an
8434
+ * unknown source. The registry of names reads only the lists `children`.
8435
+ */
8436
+ function collectReads(b, node, key, rule, later) {
8437
+ if (!rule.names) termReads(b, termRefs(b, node.kind, `tree.attributes.${key}.collect.unit`, rule.unit), node, void 0, later);
8438
+ let combined = false;
8439
+ const done = /* @__PURE__ */ new Set();
8440
+ const stack = [node];
8441
+ while (stack.length > 0) {
8442
+ const at = stack.pop();
8443
+ if (done.has(at)) continue;
8444
+ done.add(at);
8445
+ const term = rule.names || !hasOwnEntry(at.kind.tree?.contributes, key) ? void 0 : at.kind.tree?.contributes?.[key];
8446
+ if (term !== void 0) {
8447
+ termReads(b, termRefs(b, at.kind, `tree.contributes.${key}`, term), at, void 0, later);
8448
+ combined = true;
8449
+ }
8450
+ edge(b, vertexOf(b, at, "children"), later);
8451
+ const children = childrenOf(b, at);
8452
+ if (children === "unknown") edge(b, void 0, later);
8453
+ else if (typeof children === "object" && "list" in children) {
8454
+ const items = itemsOf(children.list);
8455
+ for (let index = items.length - 1; index >= 0; index -= 1) {
8456
+ const child = items[index]?.node;
8457
+ if (child !== void 0 && child.up === at) stack.push(child);
8458
+ }
8459
+ }
8460
+ }
8461
+ if (!rule.names) termReads(b, termRefs(b, node.kind, `tree.attributes.${key}.collect.combine`, rule.combine), node, void 0, combined ? later : {
8462
+ ...later,
8463
+ lazy: true
8464
+ });
8465
+ }
8250
8466
  function build(machine, doc, declaredType) {
8251
8467
  const b = {
8252
8468
  machine,
@@ -8732,21 +8948,56 @@ function mapRefs(world, doc, change, options = {}) {
8732
8948
  }
8733
8949
  return out;
8734
8950
  }
8951
+ /**
8952
+ * This function gives the parts that the code of a runner evaluates itself, without a wait (RUN.DRIVES, D119). The
8953
+ * answer of the hook `facts` for the code wins over the contract. The contract describes only the code of its kind for
8954
+ * `eval`. Without a declaration, the runner passes each wait of its parts through.
8955
+ */
8956
+ function driveRules(machine, v, kind, name, rung, verb) {
8957
+ if (verb !== "eval") return void 0;
8958
+ const hook = machine.facts?.(v.typePath ?? [], name)?.drives;
8959
+ if (hook !== void 0) return hook;
8960
+ return v.declared || rung !== "own" || name === answerOf(machine.world, void 0, kind, verb)?.name ? kind.drives : void 0;
8961
+ }
8735
8962
  /** The least authority: the capability `[]` permits each effect (CAP.KEYS). */
8736
8963
  const EACH$1 = /* @__PURE__ */ Object.freeze([[]]);
8737
8964
  /** No declared effect. One list serves each visit without effects. */
8738
8965
  const NO_EFFECTS = /* @__PURE__ */ Object.freeze([]);
8739
8966
  /**
8740
- * This function gives the effects that the contract of the kind of a visit declares for a verb, as keys
8741
- * (FACTS.EFFECTS, VERB.EFFECTS). The text `"type"` gives the type of the document after each alias. A visit without a
8742
- * kind declares no effect. `check`, `factsOf` and `effectsOf` read the declarations with this one function (D27).
8967
+ * This function gives the effects that the code of a visit declares for a verb, as keys (FACTS.EFFECTS, VERB.EFFECTS).
8968
+ * The text `"type"` gives the type of the document after each alias. A visit without a kind declares no effect.
8969
+ * `check`, `factsOf`, `effectsOf` and `checkLatent` read the declarations with this one function (D27).
8970
+ *
8971
+ * For `eval`, the answer of the hook `facts` of the machine for the code of the visit wins over the contract of the
8972
+ * kind (CAP.OWN-EFFECTS, D118). Thus a language gives the effects of an own entry for another evaluator. Without a
8973
+ * machine, or without the field `effects` in the answer, the contract gives the effects.
8743
8974
  *
8744
8975
  * @param v - The visit.
8745
8976
  * @param verb - The verb. The default is `eval`. A kind without a declaration for the verb gives no effect.
8746
- * @returns The keys of each declared effect, in the order of the contract.
8977
+ * @param machine - The machine with the hook `facts`, when the caller has it.
8978
+ * @param name - The name of the evaluator of the visit, when the caller knows it. Else the ladder gives it.
8979
+ * @returns The keys of each declared effect, in the order of the declaration.
8980
+ */
8981
+ function declaredEffects(v, verb = "eval", machine, name) {
8982
+ return hookEffects(v, verb, machine, name) ?? verbDeclared(v, verb) ?? NO_EFFECTS;
8983
+ }
8984
+ /**
8985
+ * This function gives the effects that the hook `facts` gives for the code of a visit, as keys (D118). The result is
8986
+ * `undefined` for another verb than `eval`, without the hook, or when the answer has no field `effects`.
8987
+ */
8988
+ function hookEffects(v, verb, machine, name) {
8989
+ if (verb !== "eval" || machine?.facts === void 0 || v.kind === void 0) return void 0;
8990
+ const code = name ?? answerOf(machine.world, v.declared ? void 0 : v.doc.own, v.kind, "eval")?.name;
8991
+ const effects = code === void 0 ? void 0 : machine.facts(v.typePath ?? [], code)?.effects;
8992
+ return effects === void 0 ? void 0 : effectKeys(v, effects);
8993
+ }
8994
+ /**
8995
+ * This function gives the keys of a declaration of effects at a visit (D27). The text `"type"` gives the type of the
8996
+ * document after each alias.
8747
8997
  */
8748
- function declaredEffects(v, verb = "eval") {
8749
- return verbDeclared(v, verb) ?? NO_EFFECTS;
8998
+ function effectKeys(v, effects) {
8999
+ if (effects.length === 0) return NO_EFFECTS;
9000
+ return effects === "type" ? [v.typePath ?? []] : effects.map((kind) => kind.split("."));
8750
9001
  }
8751
9002
  /**
8752
9003
  * This function gives the effects that the contract of the kind of a visit declares for a verb, as keys. The result
@@ -8757,8 +9008,7 @@ function verbDeclared(v, verb) {
8757
9008
  if (v.kind === void 0) return verb === "eval" ? NO_EFFECTS : void 0;
8758
9009
  const effects = effectsOfVerb(v.kind, verb);
8759
9010
  if (effects === void 0) return void 0;
8760
- if (effects.length === 0) return NO_EFFECTS;
8761
- return effects === "type" ? [v.typePath ?? []] : effects.map((kind) => kind.split("."));
9011
+ return effectKeys(v, effects);
8762
9012
  }
8763
9013
  /**
8764
9014
  * This function gives the rule of a machine for an effect, for the static kit (CAP.PERMITS). The rule is the hook
@@ -8907,7 +9157,8 @@ const DOWN_NAMES = /* @__PURE__ */ Object.freeze([
8907
9157
  const FOLD_NAMES = /* @__PURE__ */ Object.freeze(["acc", "item"]);
8908
9158
  /**
8909
9159
  * This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
8910
- * `equations`, a `unit` and a `default` see no names. `check` and `factsOf` read the equations with this one function.
9160
+ * `equations`, a `unit`, a `default` and a contribution see no names. `check` and `factsOf` read the equations with
9161
+ * this one function.
8911
9162
  */
8912
9163
  function equationsOf(kind) {
8913
9164
  const tree = kind.tree;
@@ -8951,7 +9202,32 @@ function equationsOf(kind) {
8951
9202
  attribute: name,
8952
9203
  down: false
8953
9204
  });
9205
+ const collect = declared.collect === void 0 ? void 0 : collectOf(declared.collect);
9206
+ if (collect?.ok === true && !collect.names) {
9207
+ out.push({
9208
+ label: `tree.attributes.${name}.collect.unit`,
9209
+ term: collect.unit,
9210
+ names: [],
9211
+ attribute: name,
9212
+ down: false
9213
+ });
9214
+ out.push({
9215
+ label: `tree.attributes.${name}.collect.combine`,
9216
+ term: collect.combine,
9217
+ names: FOLD_NAMES,
9218
+ attribute: name,
9219
+ down: false
9220
+ });
9221
+ }
8954
9222
  }
9223
+ for (const [name, term] of Object.entries(tree.contributes ?? {})) out.push({
9224
+ label: `tree.contributes.${name}`,
9225
+ term,
9226
+ names: [],
9227
+ attribute: name,
9228
+ down: false,
9229
+ contributes: true
9230
+ });
8955
9231
  return out;
8956
9232
  }
8957
9233
  /**
@@ -9073,7 +9349,7 @@ function answered(world, node, name) {
9073
9349
  if (hasEntry(tree?.equations, name)) return true;
9074
9350
  if (node.parent === void 0 ? downNames(world).has(name) : hasEntry(node.parent.kind.tree?.down, name)) return true;
9075
9351
  const declared = tree?.attributes !== void 0 && Object.hasOwn(tree.attributes, name) ? tree.attributes[name] : void 0;
9076
- return declared !== void 0 && (declared.copy === true || declared.fold !== void 0 || declared.default !== void 0);
9352
+ return declared !== void 0 && (declared.copy === true || declared.fold !== void 0 || declared.collect !== void 0 || declared.default !== void 0);
9077
9353
  }
9078
9354
  /**
9079
9355
  * This function checks the equations of the tree contract of a kind, each with the names of its rung (TREE.DOWN-NAMES).
@@ -9097,7 +9373,7 @@ function equationIssues(machine, kind, kindText, options, state) {
9097
9373
  own: void 0,
9098
9374
  parent: void 0
9099
9375
  };
9100
- for (const { label, term, names, attribute, down } of equationsOf(kind)) {
9376
+ for (const { label, term, names, attribute, down, contributes } of equationsOf(kind)) {
9101
9377
  const inner = {
9102
9378
  names,
9103
9379
  ...of === void 0 ? {} : { declaredType: of },
@@ -9107,7 +9383,7 @@ function equationIssues(machine, kind, kindText, options, state) {
9107
9383
  needs.push({
9108
9384
  name,
9109
9385
  why: `the equation ${label} reads it`,
9110
- hidden: down ? void 0 : attribute
9386
+ hidden: down || contributes === true ? void 0 : attribute
9111
9387
  });
9112
9388
  };
9113
9389
  for (const issue of checkAll(machine, term, inner, void 0, state, {
@@ -9124,7 +9400,8 @@ function equationIssues(machine, kind, kindText, options, state) {
9124
9400
  /**
9125
9401
  * This function finds the issues of the contract of a tree kind (TREE.CHECK-RESERVED). An attribute with the name of a
9126
9402
  * structural step gets no read. A tree kind of the run side contradicts the evaluation on demand of its attributes. A
9127
- * declaration `circular` without a start value or with a bad limit gives `#VALUE!` (TREE.CIRCULAR-CHECK).
9403
+ * declaration `circular` without a start value or with a bad limit gives `#VALUE!` (TREE.CIRCULAR-CHECK). A declaration
9404
+ * `collect` without its form gives `#VALUE!` too (TREE.COLLECT).
9128
9405
  */
9129
9406
  function contractIssues(kind, kindText) {
9130
9407
  const out = [];
@@ -9132,7 +9409,8 @@ function contractIssues(kind, kindText) {
9132
9409
  for (const table of [
9133
9410
  "equations",
9134
9411
  "down",
9135
- "attributes"
9412
+ "attributes",
9413
+ "contributes"
9136
9414
  ]) for (const name of Object.keys(tree?.[table] ?? {})) {
9137
9415
  const why = Object.hasOwn(STRUCTURAL, name) ? STRUCTURAL[name] : void 0;
9138
9416
  if (why !== void 0) out.push({
@@ -9148,6 +9426,12 @@ function contractIssues(kind, kindText) {
9148
9426
  place: [],
9149
9427
  reason: `The kind ${kindText} has a bad declaration tree.attributes.${name}.circular. ${rule.reason}`
9150
9428
  });
9429
+ const collect = declared.collect === void 0 ? void 0 : collectOf(declared.collect);
9430
+ if (collect !== void 0 && !collect.ok) out.push({
9431
+ code: "#VALUE!",
9432
+ place: [],
9433
+ reason: `The kind ${kindText} has a bad declaration tree.attributes.${name}.collect. ${collect.reason}`
9434
+ });
9151
9435
  }
9152
9436
  if (kind.side === "run") out.push({
9153
9437
  code: "#CALC!",
@@ -9235,6 +9519,7 @@ function checkAll(machine, doc, options, nodes, state = {
9235
9519
  };
9236
9520
  const allowed = options.caps === void 0 ? void 0 : permitsOf(machine);
9237
9521
  const waits = options.run === true ? /* @__PURE__ */ new Map() : void 0;
9522
+ const driven = /* @__PURE__ */ new Map();
9238
9523
  const drives = [];
9239
9524
  const maxDepth = machine.maxDepth ?? 1e3;
9240
9525
  const anyName = options.names === "any";
@@ -9311,7 +9596,7 @@ function checkAll(machine, doc, options, nodes, state = {
9311
9596
  });
9312
9597
  }
9313
9598
  const caps = v.caps;
9314
- const stated = verbDeclared(v, verb);
9599
+ const stated = hookEffects(v, verb, machine, name) ?? verbDeclared(v, verb);
9315
9600
  const declared = stated ?? NO_EFFECTS;
9316
9601
  if (allowed !== void 0 && caps !== void 0) {
9317
9602
  for (const kind of declared) if (!allowed(caps, kind)) push({
@@ -9322,7 +9607,9 @@ function checkAll(machine, doc, options, nodes, state = {
9322
9607
  });
9323
9608
  }
9324
9609
  if (waits !== void 0 && coded) {
9325
- const up = v.parent === void 0 ? true : waits.get(v.parent);
9610
+ let up = v.parent === void 0 ? true : waits.get(v.parent);
9611
+ const rules = v.parent === void 0 ? void 0 : driven.get(v.parent);
9612
+ if (up !== false && rules !== void 0 && v.parent !== void 0 && rulesCover(rules, v.parent.kind?.parts, v.parent.doc.value, v.slot)) up = false;
9326
9613
  let chain = false;
9327
9614
  if (runner && up === false) {
9328
9615
  chain = {
@@ -9339,6 +9626,10 @@ function checkAll(machine, doc, options, nodes, state = {
9339
9626
  drives.push(chain);
9340
9627
  } else if (runner) chain = up ?? true;
9341
9628
  waits.set(v, chain);
9629
+ if (runner && v.kind !== void 0) {
9630
+ const own = driveRules(machine, v, v.kind, name, answer.rung, verb);
9631
+ if (own !== void 0 && own.length > 0) driven.set(v, own);
9632
+ }
9342
9633
  if (typeof chain === "object" && !chain.wait) {
9343
9634
  const rule = allowed ?? permits;
9344
9635
  chain.wait = stated === void 0 || declared.some((kind) => caps === void 0 || rule(caps, kind));
@@ -9444,7 +9735,7 @@ function reaching(machine, doc, options) {
9444
9735
  const rule = options.caps === void 0 ? permits : permitsOf(machine);
9445
9736
  walk(machine.world, doc, (v) => {
9446
9737
  const caps = v.caps ?? EACH;
9447
- for (const kind of declaredEffects(v)) if (rule(caps, kind)) out.push({
9738
+ for (const kind of declaredEffects(v, "eval", machine)) if (rule(caps, kind)) out.push({
9448
9739
  kind,
9449
9740
  visit: v
9450
9741
  });
@@ -9465,7 +9756,9 @@ function reaching(machine, doc, options) {
9465
9756
  *
9466
9757
  * The result covers each effect that an evaluation or a run gives the host (CAP.EFFECTS-SOUND). A procedure value keeps
9467
9758
  * the capabilities of the document that made it, thus the effects of its body count at the body (CAP.LATENT). The
9468
- * contract describes the code of its kind. The code of an own entry for another evaluator is outside the result.
9759
+ * contract describes the code of its kind. The contract does not describe the code of an own entry for another
9760
+ * evaluator. The answer of the hook `facts` for that code gives its effects (CAP.OWN-EFFECTS, D118). Without an
9761
+ * answer, its effects are outside the result.
9469
9762
  *
9470
9763
  * @param machine - The world and its contracts, and the hook `permits`.
9471
9764
  * @param doc - The document, as JSON.
@@ -9965,6 +10258,30 @@ function paramsOf(value) {
9965
10258
  const params = isRecord(value) ? value["params"] : void 0;
9966
10259
  return Array.isArray(params) ? params.filter((param) => typeof param === "string") : [];
9967
10260
  }
10261
+ /** This function gives the text of a step of a path: a key, or the kind of a step document in parentheses. */
10262
+ function stepText(step) {
10263
+ if (isKey(step)) return String(step);
10264
+ return isRecord(step) ? `(${typeName(step["type"])})` : "(step)";
10265
+ }
10266
+ /**
10267
+ * This function gives the type of what a step document reaches (TYPE.WALKER, D121). The type rule of its walker gives
10268
+ * it. A step document with parts gets its data in the run, thus its type is `unknown`, as for a walker without a rule.
10269
+ *
10270
+ * @param input - The input of the rule of the reference.
10271
+ * @param step - The step document.
10272
+ * @param here - The type before the step.
10273
+ * @returns The type of what the step reaches.
10274
+ */
10275
+ function walkType(input, step, here) {
10276
+ const rule = walkerOf(input.machine, step)?.type;
10277
+ if (rule === void 0) return "unknown";
10278
+ const read = partsOf(input.machine.world, step);
10279
+ if (!read.ok || !read.split.ok || read.split.parts.length > 0) return "unknown";
10280
+ return input.contract(rule({
10281
+ data: read.doc.value,
10282
+ here
10283
+ }), `type of the step ${typeName(read.doc.type)}`);
10284
+ }
9968
10285
  /**
9969
10286
  * This function makes the type rules of the formula kit for one profile (TYPE.SYNTH, D25). Each evaluator of the kit
9970
10287
  * holds its rule. The rules read the rules of the profile that change a type: `truthy`, `truthType`, `logic`,
@@ -10105,11 +10422,14 @@ function formulaTypes(profile) {
10105
10422
  if (isUnknown(type, domains) || !plain && holdsEnvelope(type, domains)) return "unknown";
10106
10423
  if (index >= path.length) return type;
10107
10424
  const step = path[index];
10108
- if (!isKey(step)) return "unknown";
10425
+ if (!isKey(step)) {
10426
+ type = walkType(input, step, type);
10427
+ continue;
10428
+ }
10109
10429
  const next = stepType(type, step, domains, input.contract);
10110
10430
  if ("missing" in next) {
10111
10431
  const fields = next.missing.length === 0 ? "" : ` Its fields are ${listText(next.missing)}.`;
10112
- report({ reason: `The value at ${path.slice(0, index).map(String).join(".")} has no field ${String(step)}.${fields}` });
10432
+ report({ reason: `The value at ${path.slice(0, index).map(stepText).join(".")} has no field ${String(step)}.${fields}` });
10113
10433
  return "unknown";
10114
10434
  }
10115
10435
  type = next.type;
@@ -12638,6 +12958,55 @@ function foldOf(node, name, fold) {
12638
12958
  return acc;
12639
12959
  }
12640
12960
  /**
12961
+ * TREE.COLLECT and TREE.NAMED: the node, then each node of this tree in each list `children`, in the order of the
12962
+ * lists. Each node counts one time. The registry `names` maps each binding name to its node.
12963
+ */
12964
+ function collectIn(node, name, declared) {
12965
+ const rule = collectOf(declared);
12966
+ if (!rule.ok) return fail("#VALUE!", `The attribute ${nameOfAttribute(node, name)} has a bad declaration collect. ${rule.reason}`);
12967
+ const registry = {};
12968
+ const first = /* @__PURE__ */ new Map();
12969
+ let acc = rule.names ? ok(registry) : equationOf(node, rule.unit, {});
12970
+ if (!acc.ok) return acc;
12971
+ const done = [];
12972
+ const visit = (at) => {
12973
+ if (done.includes(at)) return void 0;
12974
+ done.push(at);
12975
+ if (rule.names && at.name !== void 0) {
12976
+ const earlier = first.get(at.name);
12977
+ if (earlier !== void 0) return fail("#VALUE!", `Two nodes have the name ${at.name} in the registry ${nameOfAttribute(node, name)}: ${earlier.kind} at [${earlier.place.join(", ")}] and ${at.kind} at [${at.place.join(", ")}].`);
12978
+ first.set(at.name, at);
12979
+ Object.defineProperty(registry, at.name, {
12980
+ value: at.thing,
12981
+ enumerable: true,
12982
+ writable: true,
12983
+ configurable: true
12984
+ });
12985
+ }
12986
+ const term = rule.names ? void 0 : termIn(at.contract?.contributes, name);
12987
+ if (!rule.names && term !== void 0) {
12988
+ const item = equationOf(at, term, {});
12989
+ if (!item.ok) return item;
12990
+ acc = equationOf(node, rule.combine, {
12991
+ acc: acc.ok ? acc.value : null,
12992
+ item: item.value
12993
+ });
12994
+ if (!acc.ok) return acc;
12995
+ }
12996
+ const children = attributeOf(at, "children");
12997
+ if (children === void 0) return void 0;
12998
+ if (!children.ok) return children;
12999
+ if (!Array.isArray(children.value)) return fail("#VALUE!", `The collection of ${name} needs a list in children of ${at.kind} at [${at.place.join(", ")}].`);
13000
+ for (const item of children.value) {
13001
+ const child = refNodeOf(item);
13002
+ if (child === void 0 || child.tree !== node.tree) continue;
13003
+ const failed = visit(child);
13004
+ if (failed !== void 0) return failed;
13005
+ }
13006
+ };
13007
+ return visit(node) ?? acc;
13008
+ }
13009
+ /**
12641
13010
  * TREE.LADDER: the rungs of the ladder of an attribute, in their order. A rung gives `undefined` when it does not
12642
13011
  * answer, and the next rung then tries.
12643
13012
  */
@@ -12661,6 +13030,7 @@ const REF_RUNGS = [
12661
13030
  ["default", (node, name) => {
12662
13031
  const declared = termIn(node.contract?.attributes, name);
12663
13032
  if (declared?.fold !== void 0) return foldOf(node, name, declared.fold);
13033
+ if (declared?.collect !== void 0) return collectIn(node, name, declared.collect);
12664
13034
  return declared?.default === void 0 ? void 0 : equationOf(node, declared.default, {});
12665
13035
  }]
12666
13036
  ];
@@ -12848,7 +13218,7 @@ function plainValue(value, open) {
12848
13218
  * tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error. The node
12849
13219
  * reads `self` only through `resolve` (C4, TREE.SELF).
12850
13220
  */
12851
- const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolve: walk }) => {
13221
+ const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolve: walk, doc }) => {
12852
13222
  const reached = walk(["self"]);
12853
13223
  const parent = reached.ok ? reached.thing : void 0;
12854
13224
  const outer = refNodeOf(parent);
@@ -12859,6 +13229,7 @@ const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolv
12859
13229
  const node = {
12860
13230
  tree: nested ? outer.tree : { open: true },
12861
13231
  kind,
13232
+ name: doc.name,
12862
13233
  place,
12863
13234
  parts,
12864
13235
  ctx: context,
@@ -15182,6 +15553,7 @@ var Shot = class {
15182
15553
  scenario;
15183
15554
  context;
15184
15555
  keys;
15556
+ driven;
15185
15557
  perform;
15186
15558
  replay;
15187
15559
  calls = [];
@@ -15195,12 +15567,13 @@ var Shot = class {
15195
15567
  result = NO_RESULT;
15196
15568
  afterwards = false;
15197
15569
  running;
15198
- constructor(machine, runner, scenario, context, keys, perform, replay) {
15570
+ constructor(machine, runner, scenario, context, keys, driven, perform, replay) {
15199
15571
  this.machine = machine;
15200
15572
  this.runner = runner;
15201
15573
  this.scenario = scenario;
15202
15574
  this.context = context;
15203
15575
  this.keys = keys;
15576
+ this.driven = driven;
15204
15577
  this.perform = perform;
15205
15578
  this.replay = replay;
15206
15579
  this.running = scenario.running;
@@ -15224,7 +15597,7 @@ var Shot = class {
15224
15597
  });
15225
15598
  const key = this.keys[part] ?? String(part);
15226
15599
  if (via === "run" && !this.runner) this.problems.push(`The code of an evaluator starts the steps of the part ${key}. Only a runner waits.`);
15227
- if (via === "code" && this.runner && this.running) this.problems.push(`The runner evaluates the part ${key} with its code, and that code cannot wait. A runner evaluates a part with its steps (K65).`);
15600
+ if (via === "code" && this.runner && this.running && this.driven[part] !== true) this.problems.push(`The runner evaluates the part ${key} with its code, and that code cannot wait. A runner evaluates a part with its steps, or its contract declares the part in drives (D119).`);
15228
15601
  return this.scenario.results[part] ?? NO_RESULT;
15229
15602
  }
15230
15603
  /** This function records a call of a part, and gives the steps of the probe part. In a run, the part can wait. */
@@ -15417,13 +15790,14 @@ function typesNaming(world, name, verb) {
15417
15790
  /** This function gives the facts of the kind of a type. The hook `facts` of the machine wins over the contract. */
15418
15791
  function contractOf(env, kind, typePath, caps) {
15419
15792
  const facts = env.machine.facts?.(typePath, env.name);
15420
- const own = effectsOfVerb(kind, env.verb);
15793
+ const own = (env.verb === "eval" ? facts?.effects : void 0) ?? effectsOfVerb(kind, env.verb);
15421
15794
  const effects = own ?? kind.effects;
15422
15795
  const declared = effects === void 0 ? [] : effects === "type" ? [typePath] : effects;
15423
15796
  return {
15424
15797
  declared,
15425
15798
  pure: (facts?.pure ?? kind.pure) === true,
15426
15799
  lazy: facts?.lazy ?? kind.lazy,
15800
+ drives: env.verb === "eval" ? facts?.drives ?? kind.drives : void 0,
15427
15801
  describes: env.verb === "eval",
15428
15802
  declares: own !== void 0,
15429
15803
  caps: caps ?? [...declared, "probe"]
@@ -15571,6 +15945,7 @@ function samplesOf(type, typePath, kind, contract, extra) {
15571
15945
  },
15572
15946
  otherSplit,
15573
15947
  lazy: split.parts.map((_slot, index) => contract.lazy?.some((rule) => covers(rule, split.parts, index)) === true),
15948
+ driven: split.parts.map((_slot, index) => contract.drives?.some((rule) => covers(rule, split.parts, index)) === true),
15574
15949
  names: scopesOf(kind.scope, value, split.parts).sees
15575
15950
  });
15576
15951
  }
@@ -15717,7 +16092,7 @@ function instanceOf(env, under, doc, split, sample) {
15717
16092
  /** This function evaluates a probe document one time in a new shot. */
15718
16093
  function shoot(env, instance, sample, scenario, more = {}) {
15719
16094
  const context = more.context ?? rootContext(env.machine, makeContext(env.names, scenario.caps));
15720
- const shot = new Shot(env.machine, env.runner, scenario, context, sample.split.parts.map((slot) => slot.key), env.perform, more.replay);
16095
+ const shot = new Shot(env.machine, env.runner, scenario, context, sample.split.parts.map((slot) => slot.key), sample.driven, env.perform, more.replay);
15721
16096
  instance(shot);
15722
16097
  return shot;
15723
16098
  }
@@ -17189,18 +17564,89 @@ const READ = /* @__PURE__ */ new Set([
17189
17564
  "enum",
17190
17565
  "const",
17191
17566
  "anyOf",
17192
- "oneOf"
17567
+ "oneOf",
17568
+ "allOf",
17569
+ "$ref"
17193
17570
  ]);
17194
17571
  /**
17195
- * The keywords that can permit more values than the other keywords permit. Older drafts ignore each keyword beside
17196
- * `$ref`, and `nullable` of OpenAPI 3.0 permits `null`. Thus a schema with one of them reads as `unknown`.
17572
+ * The keywords that can permit more values than the other keywords permit. A dynamic reference can reach a schema
17573
+ * outside this schema, and `nullable` of OpenAPI 3.0 permits `null`. Thus a schema with one of them reads as `unknown`.
17197
17574
  */
17198
17575
  const WIDENING = /* @__PURE__ */ new Set([
17199
- "$ref",
17200
17576
  "$dynamicRef",
17201
17577
  "$recursiveRef",
17202
17578
  "nullable"
17203
17579
  ]);
17580
+ /** The keywords of an object without the keyword `type`. They test only the values that are records. */
17581
+ const OBJECT_KEYS = [
17582
+ "properties",
17583
+ "required",
17584
+ "additionalProperties"
17585
+ ];
17586
+ /** The form of a local reference: a definition in `$defs` or `definitions` of the root schema. */
17587
+ const LOCAL_REF = /^#\/(\$defs|definitions)\/([^/]*)$/;
17588
+ /** The most pairs of members that the meet of two unions builds. A larger meet keeps the first type, which is sound. */
17589
+ const MAX_MEET_PAIRS = 64;
17590
+ /** This function tells if a field of a record type can be absent. */
17591
+ function isOptional(type, key) {
17592
+ return type.optional?.includes(key) === true;
17593
+ }
17594
+ /**
17595
+ * This function gives the meet of two types of a reading: a type that holds each value of both types. A schema is
17596
+ * the conjunction of its keywords, thus its type is the meet of their types. The meet is exact for the forms that a
17597
+ * reading makes: base types, lists, records, unions and `one`. A meet of two different forms is `never`.
17598
+ */
17599
+ function meet(a, b, depth) {
17600
+ if (depth > MAX_SCHEMA_DEPTH) return a;
17601
+ if (isUnknown(a, void 0)) return b;
17602
+ if (isUnknown(b, void 0)) return a;
17603
+ if (a === "never" || b === "never") return "never";
17604
+ if (typeof a !== "string" && "one" in a) return { one: a.one.filter((value) => member(value, b)) };
17605
+ if (typeof b !== "string" && "one" in b) return { one: b.one.filter((value) => member(value, a)) };
17606
+ if (typeof b !== "string" && "union" in b && !(typeof a !== "string" && "union" in a)) return meet(b, a, depth);
17607
+ if (typeof a !== "string" && "union" in a) {
17608
+ const others = typeof b !== "string" && "union" in b ? b.union : [b];
17609
+ if (a.union.length * others.length > MAX_MEET_PAIRS) return a;
17610
+ return unionOf(a.union.flatMap((item) => others.map((other) => meet(item, other, depth + 1))));
17611
+ }
17612
+ if (typeof a === "string" || typeof b === "string") return a === b ? a : "never";
17613
+ if ("list" in a && "list" in b) return { list: meet(a.list, b.list, depth + 1) };
17614
+ if ("record" in a && "record" in b) return meetRecords(a, b, depth + 1);
17615
+ return ("list" in a || "record" in a) && ("list" in b || "record" in b) ? "never" : a;
17616
+ }
17617
+ /**
17618
+ * This function gives the meet of two record types. A field of both records gets the meet of its types, and it is
17619
+ * optional only when both records permit its absence. A closed record has no other field, thus a field of only the
17620
+ * other record is absent. When the other record needs that field, no value fits both records.
17621
+ */
17622
+ function meetRecords(a, b, depth) {
17623
+ const fields = {};
17624
+ const optional = [];
17625
+ const keys = [...Object.keys(a.record), ...Object.keys(b.record).filter((key) => !Object.hasOwn(a.record, key))];
17626
+ for (const key of keys) {
17627
+ const inA = Object.hasOwn(a.record, key);
17628
+ const inB = Object.hasOwn(b.record, key);
17629
+ if (!inA && a.closed === true || !inB && b.closed === true) {
17630
+ if (inA && !isOptional(a, key) || inB && !isOptional(b, key)) return "never";
17631
+ continue;
17632
+ }
17633
+ setOwn(fields, key, inA && inB ? meet(a.record[key], b.record[key], depth) : inA ? a.record[key] : b.record[key]);
17634
+ if ((!inA || isOptional(a, key)) && (!inB || isOptional(b, key))) optional.push(key);
17635
+ }
17636
+ const closed = a.closed === true || b.closed === true;
17637
+ return {
17638
+ record: fields,
17639
+ ...optional.length === 0 ? {} : { optional },
17640
+ ...closed ? { closed: true } : {}
17641
+ };
17642
+ }
17643
+ /** This function applies a condition to a type: each member of the JSON type of the condition meets its type. */
17644
+ function narrow$1(type, condition) {
17645
+ if (typeof type === "string") return type;
17646
+ if ("union" in type) return unionOf(type.union.map((item) => narrow$1(item, condition)));
17647
+ if ("one" in type) return { one: type.one.filter((value) => !(condition.on === "list" ? Array.isArray(value) : isRecord(value)) || member(value, condition.type)) };
17648
+ return condition.on in type ? meet(type, condition.type, 0) : type;
17649
+ }
17204
17650
  /** The base type of each name of the keyword `type`. */
17205
17651
  const BASES = {
17206
17652
  null: "null",
@@ -17223,26 +17669,57 @@ function setOwn(target, key, value) {
17223
17669
  function unionOf(types) {
17224
17670
  return types.reduce((all, type) => join(all, type), "never");
17225
17671
  }
17226
- /** The state of one reading of a schema: the issues that it found. */
17672
+ /** The state of one reading of a schema: the root, the issues and the definitions that it read. */
17227
17673
  var SchemaReader = class {
17674
+ root;
17228
17675
  issues = [];
17676
+ /** The type of each definition that a reference reached. Each reading of a definition is sound, thus the first stays. */
17677
+ defs = /* @__PURE__ */ new Map();
17678
+ /** The definitions that the reading has not finished. A reference to one of them is a cycle. */
17679
+ open = /* @__PURE__ */ new Set();
17680
+ /** The number of schemas with an own `$id` around the current schema. A reference inside them has another base. */
17681
+ inner = 0;
17682
+ constructor(root) {
17683
+ this.root = root;
17684
+ }
17229
17685
  issue(at, reason) {
17230
17686
  this.issues.push({
17231
17687
  at,
17232
17688
  reason
17233
17689
  });
17234
17690
  }
17235
- /** This method reads one schema. */
17691
+ /** This method reads one schema: the meet of the types of its keywords, then its conditions. */
17236
17692
  read(schema, at, depth) {
17693
+ const types = [];
17694
+ const conditions = [];
17695
+ this.parts(schema, at, depth, types, conditions);
17696
+ let type = types.reduce((all, item) => meet(all, item, 0), "unknown");
17697
+ for (const condition of conditions) {
17698
+ if (isUnknown(type, void 0)) {
17699
+ this.issue(condition.at, "The schema has no keyword type. Thus a value of each JSON type fits it, and the type reads it as unknown.");
17700
+ break;
17701
+ }
17702
+ type = narrow$1(type, condition);
17703
+ }
17704
+ return type;
17705
+ }
17706
+ /**
17707
+ * This method adds the type of each keyword of a schema to `types`, and its conditions to `conditions`. Each type
17708
+ * holds each value that the schema accepts. The parts of `allOf` add their keywords too.
17709
+ */
17710
+ parts(schema, at, depth, types, conditions) {
17237
17711
  if (depth > MAX_SCHEMA_DEPTH) {
17238
17712
  this.issue(at, `The schema is deeper than ${MAX_SCHEMA_DEPTH} levels. The type reads it as unknown.`);
17239
- return "unknown";
17713
+ return;
17714
+ }
17715
+ if (schema === true) return;
17716
+ if (schema === false) {
17717
+ types.push("never");
17718
+ return;
17240
17719
  }
17241
- if (schema === true) return "unknown";
17242
- if (schema === false) return "never";
17243
17720
  if (!isRecord(schema)) {
17244
17721
  this.issue(at, "A schema is a record or a truth value. The type reads this value as unknown.");
17245
- return "unknown";
17722
+ return;
17246
17723
  }
17247
17724
  let widens = false;
17248
17725
  for (const key of Object.keys(schema)) {
@@ -17252,46 +17729,99 @@ var SchemaReader = class {
17252
17729
  widens = true;
17253
17730
  } else this.issue([...at, key], `The type does not read the keyword ${key}. It does not test what the keyword tests.`);
17254
17731
  }
17255
- if (widens) return "unknown";
17256
- const sides = [];
17257
- if (Object.hasOwn(schema, "type")) sides.push({
17258
- key: "type",
17259
- type: this.byType(schema, at, depth)
17260
- });
17261
- else if ([
17262
- "properties",
17263
- "required",
17264
- "additionalProperties",
17265
- "items"
17266
- ].some((key) => Object.hasOwn(schema, key)) && ![
17267
- "enum",
17268
- "const",
17732
+ if (widens) return;
17733
+ const nested = schema !== this.root && Object.hasOwn(schema, "$id");
17734
+ if (nested) this.inner += 1;
17735
+ try {
17736
+ this.keywords(schema, at, depth, types, conditions);
17737
+ } finally {
17738
+ if (nested) this.inner -= 1;
17739
+ }
17740
+ }
17741
+ /** This method reads the keywords of a schema that does not permit more values (WIDENING). */
17742
+ keywords(schema, at, depth, types, conditions) {
17743
+ if (Object.hasOwn(schema, "$ref")) {
17744
+ for (const key of Object.keys(schema)) if (READ.has(key) && key !== "$ref") this.issue([...at, key], `The type reads only the target of $ref. It does not test the keyword ${key} beside it.`);
17745
+ const type = this.target(schema["$ref"], [...at, "$ref"], depth);
17746
+ if (type !== void 0) types.push(type);
17747
+ return;
17748
+ }
17749
+ if (Object.hasOwn(schema, "type")) types.push(this.byType(schema, at, depth));
17750
+ else {
17751
+ if (OBJECT_KEYS.some((key) => Object.hasOwn(schema, key))) conditions.push({
17752
+ on: "record",
17753
+ type: this.object(schema, at, depth),
17754
+ at
17755
+ });
17756
+ if (Object.hasOwn(schema, "items")) conditions.push({
17757
+ on: "list",
17758
+ type: this.array(schema, at, depth),
17759
+ at
17760
+ });
17761
+ }
17762
+ for (const key of [
17269
17763
  "anyOf",
17270
- "oneOf"
17271
- ].some((key) => Object.hasOwn(schema, key))) this.issue(at, "The schema has no keyword type. Thus a value of each JSON type fits it, and the type reads it as unknown.");
17272
- for (const key of ["anyOf", "oneOf"]) {
17764
+ "oneOf",
17765
+ "allOf"
17766
+ ]) {
17273
17767
  if (!Object.hasOwn(schema, key)) continue;
17274
17768
  const items = schema[key];
17275
17769
  if (!Array.isArray(items)) {
17276
17770
  this.issue([...at, key], `The value of ${key} is a list of schemas.`);
17277
17771
  continue;
17278
17772
  }
17279
- sides.push({
17773
+ if (key === "allOf") for (const [index, item] of items.entries()) this.parts(item, [
17774
+ ...at,
17280
17775
  key,
17281
- type: unionOf(items.map((item, index) => this.read(item, [
17282
- ...at,
17283
- key,
17284
- index
17285
- ], depth + 1)))
17286
- });
17776
+ index
17777
+ ], depth + 1, types, conditions);
17778
+ else types.push(unionOf(items.map((item, index) => this.read(item, [
17779
+ ...at,
17780
+ key,
17781
+ index
17782
+ ], depth + 1))));
17287
17783
  }
17288
17784
  const values = this.values(schema, at);
17289
- if (values !== void 0) return { one: values.filter((value) => sides.every((side) => member(value, side.type))) };
17290
- const known = sides.filter((side) => !isUnknown(side.type, void 0));
17291
- const kept = known[0];
17292
- if (kept === void 0) return "unknown";
17293
- for (const side of known.slice(1)) this.issue([...at, side.key], `The type reads the keyword ${kept.key}, and not the keyword ${side.key}.`);
17294
- return kept.type;
17785
+ if (values !== void 0) types.push({ one: values });
17786
+ }
17787
+ /** This method reads the target of a reference: a definition of the root schema, or `undefined` for each value. */
17788
+ target(ref, at, depth) {
17789
+ const match = typeof ref === "string" ? LOCAL_REF.exec(ref) : null;
17790
+ let name;
17791
+ try {
17792
+ name = match === null ? void 0 : decodeURIComponent(match[2] ?? "").replace(/~1/g, "/").replace(/~0/g, "~");
17793
+ } catch {
17794
+ name = void 0;
17795
+ }
17796
+ if (match === null || name === void 0) {
17797
+ this.issue(at, "The type reads only a $ref to #/$defs/<name> or #/definitions/<name> of the same schema. Thus it reads the schema as unknown.");
17798
+ return;
17799
+ }
17800
+ if (this.inner > 0) {
17801
+ this.issue(at, "A $ref inside a schema with its own $id has another base. Thus the type reads the schema as unknown.");
17802
+ return;
17803
+ }
17804
+ const group = match[1];
17805
+ const defs = isRecord(this.root) ? this.root[group] : void 0;
17806
+ if (!isRecord(defs) || !Object.hasOwn(defs, name)) {
17807
+ this.issue(at, `The schema has no definition ${String(ref)}. Thus the type reads the reference as unknown.`);
17808
+ return;
17809
+ }
17810
+ const key = `${group}/${name}`;
17811
+ const known = this.defs.get(key);
17812
+ if (known !== void 0) return known;
17813
+ if (this.open.has(key)) {
17814
+ this.issue(at, `The reference ${String(ref)} is in a cycle. The type reads this reference as unknown.`);
17815
+ return;
17816
+ }
17817
+ this.open.add(key);
17818
+ try {
17819
+ const type = this.read(defs[name], [group, name], depth + 1);
17820
+ this.defs.set(key, type);
17821
+ return type;
17822
+ } finally {
17823
+ this.open.delete(key);
17824
+ }
17295
17825
  }
17296
17826
  /** This method reads `enum` and `const`: the values that the schema permits, or `undefined` without them. */
17297
17827
  values(schema, at) {
@@ -17379,17 +17909,20 @@ var SchemaReader = class {
17379
17909
  * - `properties`, `required` and `additionalProperties` give a record. `false` closes it, and a property that is not
17380
17910
  * in `required` is optional.
17381
17911
  * - `items` gives a list. `enum` and `const` give `one`. `anyOf` and `oneOf` give a union.
17912
+ * - `$ref` reads a definition in `$defs` or `definitions` of the same schema (TYPE.SCHEMA-REF). Another reference, a
17913
+ * missing definition and a reference in a cycle read as `unknown`.
17914
+ * - `allOf` and the other keywords of a schema give the meet of their types (TYPE.SCHEMA-ALLOF). Records merge their
17915
+ * fields, and a closed part keeps the merge closed.
17382
17916
  *
17383
17917
  * The type holds each value that the schema accepts. Each keyword that the function does not read tests nothing, as
17384
- * `unknown`, and the issues name it. Examples are `minimum`, `pattern` and `allOf`. A schema with `$ref` or `nullable`
17385
- * reads as `unknown`, because these keywords can permit more values. A schema with more than one of `type`, `anyOf`
17386
- * and `oneOf` gives the type of the first, and an issue for each other. The function does not throw.
17918
+ * `unknown`, and the issues name it. Examples are `minimum` and `pattern`. A schema with `nullable` or `$dynamicRef`
17919
+ * reads as `unknown`, because these keywords can permit more values. The function does not throw.
17387
17920
  *
17388
17921
  * @param schema - The schema, as JSON.
17389
17922
  * @returns The type, and the problems of the reading with their keys in the schema.
17390
17923
  */
17391
17924
  function typeFromSchema(schema) {
17392
- const reader = new SchemaReader();
17925
+ const reader = new SchemaReader(schema);
17393
17926
  try {
17394
17927
  return {
17395
17928
  type: reader.read(schema, [], 0),
@@ -17469,6 +18002,6 @@ function samplesAt(values, closed, depth) {
17469
18002
  function typeFromSamples(values, options = {}) {
17470
18003
  return samplesAt(values, options.closed === true, 0);
17471
18004
  }
17472
- export { ATTRIBUTE_RUNGS, CAPS_TYPE, CIRCULAR_LIMIT, 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, WORLD_SYNTAX, agree, agreeRun, agreeSchedule, ask, attributeGraph, attributeTrace, attributeVertexId, capKeys, check, checkLatent, checkStep, checkSteps, checkWorld, circularOf, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeLog, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeLog, 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, isStep, 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, placeOf, placed, planner, prepare, print, printReads, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stepLadder, stepOf, stepRank, stored, syntaxFormat, tag, thrown, toJson, treeNodeEvaluator, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
18005
+ export { ATTRIBUTE_RUNGS, CAPS_TYPE, CIRCULAR_LIMIT, 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, WORLD_SYNTAX, agree, agreeRun, agreeSchedule, ask, attributeGraph, attributeTrace, attributeVertexId, capKeys, check, checkLatent, checkStep, checkSteps, checkWorld, circularOf, classify, collectOf, components, conforms, context, cycles, dataEdges, decodeDomain, decodeLog, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeLog, 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, isStep, 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, placeOf, placed, planner, prepare, print, printReads, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stepLadder, stepOf, stepRank, stored, syntaxFormat, tag, thrown, toJson, treeNodeEvaluator, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
17473
18006
 
17474
18007
  //# sourceMappingURL=index.js.map