@mark1russell7/coeval 0.20.0 → 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`.
@@ -2436,6 +2466,143 @@ function ask(world, doc, verb, options = {}) {
2436
2466
  steps
2437
2467
  };
2438
2468
  }
2469
+ // src/steps.ts
2470
+ /**
2471
+ * This function makes a ladder of steps, and it checks the declaration (STEPS.LADDER). The list of steps must have one
2472
+ * step or more, with no name two times. The step of each construct must be a step of the list.
2473
+ *
2474
+ * @param steps - The names of the steps, from the lowest to the highest.
2475
+ * @param constructs - The step that each construct needs.
2476
+ * @returns The ladder, frozen.
2477
+ * @throws TypeError for a declaration that is not valid, because it is a defect of the language.
2478
+ */
2479
+ function stepLadder(steps, constructs) {
2480
+ if (steps.length === 0) throw new TypeError("a ladder of steps needs one step or more");
2481
+ const seen = /* @__PURE__ */ new Set();
2482
+ for (const step of steps) {
2483
+ if (typeof step !== "string") throw new TypeError(`the step ${String(step)} is not a name`);
2484
+ if (seen.has(step)) throw new TypeError(`the step "${step}" is in the ladder two times`);
2485
+ seen.add(step);
2486
+ }
2487
+ for (const [construct, step] of Object.entries(constructs)) if (!seen.has(step)) throw new TypeError(`the construct "${construct}" needs the step "${String(step)}", which is not a step of the ladder`);
2488
+ return Object.freeze({
2489
+ steps: Object.freeze([...steps]),
2490
+ constructs: Object.freeze({ ...constructs })
2491
+ });
2492
+ }
2493
+ /**
2494
+ * This function tells if a value is a step of a ladder. A host uses it to check its option for the highest step.
2495
+ *
2496
+ * @param ladder - The ladder.
2497
+ * @param value - The value.
2498
+ * @returns `true` when the value is the name of a step of the ladder.
2499
+ */
2500
+ function isStep(ladder, value) {
2501
+ return typeof value === "string" && ladder.steps.includes(value);
2502
+ }
2503
+ /**
2504
+ * This function gives the position of a step in its ladder. The lowest step has the position 0.
2505
+ *
2506
+ * @param ladder - The ladder.
2507
+ * @param step - The step.
2508
+ * @returns The position.
2509
+ * @throws TypeError for a name that is not a step of the ladder.
2510
+ */
2511
+ function stepRank(ladder, step) {
2512
+ const rank = ladder.steps.indexOf(step);
2513
+ if (rank < 0) throw new TypeError(`"${String(step)}" is not a step of the ladder. The steps are ${ladder.steps.join(", ")}`);
2514
+ return rank;
2515
+ }
2516
+ function pathOf(visit) {
2517
+ const path = [];
2518
+ for (let v = visit; v?.key !== void 0; v = v.parent) path.push(v.key);
2519
+ return path.reverse();
2520
+ }
2521
+ /**
2522
+ * This function gives the lowest step that a document needs, with the reason (STEPS.LOWEST, STEPS.REASON). The step is
2523
+ * the highest step of the constructs of its nodes. The reason is the first node in document order with that step. The
2524
+ * walk uses its own stack, thus a deep document does not overflow (STEPS.LINEAR). It visits a shared node one time,
2525
+ * and it stops at the first node with the highest step of the ladder.
2526
+ *
2527
+ * @param ladder - The ladder of the language.
2528
+ * @param walk - The construct and the child nodes of a node.
2529
+ * @param root - The root node of the document.
2530
+ * @returns The step, and the reason when the step is not the lowest step.
2531
+ */
2532
+ function stepOf(ladder, walk, root) {
2533
+ const top = ladder.steps.length - 1;
2534
+ let best = 0;
2535
+ let reason;
2536
+ const seen = /* @__PURE__ */ new Set();
2537
+ const todo = [{
2538
+ node: root,
2539
+ key: void 0,
2540
+ parent: void 0
2541
+ }];
2542
+ for (let visit = todo.pop(); visit !== void 0; visit = todo.pop()) {
2543
+ const { node } = visit;
2544
+ if (typeof node === "object" && node !== null) {
2545
+ if (seen.has(node)) continue;
2546
+ seen.add(node);
2547
+ }
2548
+ const construct = walk.construct(node);
2549
+ const step = construct === void 0 || !Object.hasOwn(ladder.constructs, construct) ? void 0 : ladder.constructs[construct];
2550
+ const rank = step === void 0 ? 0 : ladder.steps.indexOf(step);
2551
+ if (rank > best) {
2552
+ best = rank;
2553
+ reason = {
2554
+ construct,
2555
+ step,
2556
+ path: pathOf(visit),
2557
+ node
2558
+ };
2559
+ if (best === top) break;
2560
+ }
2561
+ const kids = [...walk.children(node)];
2562
+ for (let i = kids.length - 1; i >= 0; i--) {
2563
+ const [key, child] = kids[i];
2564
+ todo.push({
2565
+ node: child,
2566
+ key,
2567
+ parent: visit
2568
+ });
2569
+ }
2570
+ }
2571
+ const step = ladder.steps[best];
2572
+ return reason === void 0 ? { step } : {
2573
+ step,
2574
+ reason
2575
+ };
2576
+ }
2577
+ /**
2578
+ * This function compares the step of a document with the highest step that the host accepts (STEPS.MAX). The limit
2579
+ * changes only which documents the host accepts. The language evaluates an accepted document as before, thus a limit
2580
+ * does not change a value.
2581
+ *
2582
+ * @param ladder - The ladder of the language.
2583
+ * @param need - The step of the document, from `stepOf`.
2584
+ * @param maxStep - The highest step that the host accepts.
2585
+ * @returns `undefined` when the host accepts the document, or the refusal that names the needed step.
2586
+ * @throws TypeError when `maxStep` or the step of `need` is not a step of the ladder.
2587
+ */
2588
+ function checkStep(ladder, need, maxStep) {
2589
+ const limit = stepRank(ladder, maxStep);
2590
+ if (stepRank(ladder, need.step) <= limit) return void 0;
2591
+ const head = `the document needs the step ${need.step}, and the host accepts the steps up to ${maxStep}`;
2592
+ const reason = need.reason;
2593
+ if (reason === void 0) return {
2594
+ needed: need.step,
2595
+ maxStep,
2596
+ message: head
2597
+ };
2598
+ const where = reason.path.length === 0 ? "at the root" : `at the path ${JSON.stringify(reason.path)}`;
2599
+ return {
2600
+ needed: need.step,
2601
+ maxStep,
2602
+ reason,
2603
+ message: `${head}. The construct "${reason.construct}" ${where} needs the step ${reason.step}`
2604
+ };
2605
+ }
2439
2606
  // src/deref.ts
2440
2607
  /**
2441
2608
  * The brand of a host thing: an object of the host that answers the steps of a path itself. The brand is a
@@ -3283,6 +3450,34 @@ function pathMatches(path, pattern) {
3283
3450
  }
3284
3451
  return true;
3285
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
+ }
3286
3481
  function valueAt(value, path) {
3287
3482
  let here = value;
3288
3483
  for (const key of path) if (Array.isArray(here)) here = here[Number(key)];
@@ -4446,6 +4641,7 @@ function pureCode(c, envelope, kind, typePath, name, quick) {
4446
4641
  } catch {
4447
4642
  return false;
4448
4643
  }
4644
+ if (answer?.effects !== void 0 && answer.effects.length > 0) return false;
4449
4645
  const described = c.verb === "eval" && (envelope === void 0 || quick?.rung !== "own" || name === answerOf(c.machine.world, void 0, kind, c.verb)?.name);
4450
4646
  const pure = described ? answer?.pure ?? kind.pure : answer?.pure;
4451
4647
  const volatile = described ? answer?.volatile ?? kind.volatile : answer?.volatile;
@@ -5288,6 +5484,14 @@ const STD_WORLD = {
5288
5484
  pure: true,
5289
5485
  summary: "A step of a path: it follows a key again and again, until it reaches a node of a type."
5290
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
+ } } },
5291
5495
  panel: {
5292
5496
  verbs: {
5293
5497
  eval: "tree.node",
@@ -5527,6 +5731,12 @@ const WORLD_SYNTAX = /* @__PURE__ */ Object.freeze({
5527
5731
  "self"
5528
5732
  ]
5529
5733
  },
5734
+ {
5735
+ name: "tree.named",
5736
+ kind: "tree.named",
5737
+ args: "named",
5738
+ fields: ["name", "in"]
5739
+ },
5530
5740
  {
5531
5741
  name: "add",
5532
5742
  kind: "add"
@@ -6066,6 +6276,58 @@ function plainOf(value, path) {
6066
6276
  }
6067
6277
  }
6068
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
+ /**
6069
6331
  * This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
6070
6332
  * step `type` gives its type. Another step reads an attribute through its ladder (TREE.LADDER). The rungs are a part of
6071
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
@@ -6142,6 +6404,10 @@ function treeThing(input, place, build, outer, host) {
6142
6404
  result: fold(key, declared.fold),
6143
6405
  by: "default"
6144
6406
  };
6407
+ if (declared?.collect !== void 0) return {
6408
+ result: collected(node, key, declared.collect),
6409
+ by: "default"
6410
+ };
6145
6411
  if (declared?.default !== void 0) return {
6146
6412
  result: state.equation(declared.default, NO_NAMES$1),
6147
6413
  by: "default"
@@ -6220,6 +6486,7 @@ function treeThing(input, place, build, outer, host) {
6220
6486
  };
6221
6487
  const state = {
6222
6488
  build,
6489
+ name: input.doc.name,
6223
6490
  type,
6224
6491
  place,
6225
6492
  tree,
@@ -6401,8 +6668,45 @@ function nearestWalker(options = {}) {
6401
6668
  };
6402
6669
  };
6403
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
+ };
6404
6705
  /** The walkers of the standard world, by name. */
6405
- const STD_WALKERS = { "walk.nearest": /* @__PURE__ */ nearestWalker() };
6706
+ const STD_WALKERS = {
6707
+ "walk.nearest": nearest,
6708
+ "walk.named": named$2
6709
+ };
6406
6710
  /** This function evaluates parts one after the other, and stops at the first error. */
6407
6711
  function* inOrder(parts) {
6408
6712
  const values = [];
@@ -7387,7 +7691,7 @@ function factsOf(machine, doc, options = {}) {
7387
7691
  at: tight(place.at)
7388
7692
  });
7389
7693
  }
7390
- const declared = declaredEffects(v, verb);
7694
+ const declared = verb === "eval" && answer?.effects !== void 0 ? effectKeys(v, answer.effects) : declaredEffects(v, verb);
7391
7695
  if (verb === "eval" ? kind.side === "run" : declared.length > 0) {
7392
7696
  if (node === void 0) side = "run";
7393
7697
  else pure = false;
@@ -8108,8 +8412,57 @@ function rungReads(b, id) {
8108
8412
  });
8109
8413
  return;
8110
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
+ }
8111
8420
  if (declared?.default !== void 0) termReads(b, termRefs(b, node.kind, `tree.attributes.${key}.default`, declared.default), node, void 0, later);
8112
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
+ }
8113
8466
  function build(machine, doc, declaredType) {
8114
8467
  const b = {
8115
8468
  machine,
@@ -8595,21 +8948,56 @@ function mapRefs(world, doc, change, options = {}) {
8595
8948
  }
8596
8949
  return out;
8597
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
+ }
8598
8962
  /** The least authority: the capability `[]` permits each effect (CAP.KEYS). */
8599
8963
  const EACH$1 = /* @__PURE__ */ Object.freeze([[]]);
8600
8964
  /** No declared effect. One list serves each visit without effects. */
8601
8965
  const NO_EFFECTS = /* @__PURE__ */ Object.freeze([]);
8602
8966
  /**
8603
- * This function gives the effects that the contract of the kind of a visit declares for a verb, as keys
8604
- * (FACTS.EFFECTS, VERB.EFFECTS). The text `"type"` gives the type of the document after each alias. A visit without a
8605
- * 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.
8606
8974
  *
8607
8975
  * @param v - The visit.
8608
8976
  * @param verb - The verb. The default is `eval`. A kind without a declaration for the verb gives no effect.
8609
- * @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.
8610
8997
  */
8611
- function declaredEffects(v, verb = "eval") {
8612
- 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("."));
8613
9001
  }
8614
9002
  /**
8615
9003
  * This function gives the effects that the contract of the kind of a visit declares for a verb, as keys. The result
@@ -8620,8 +9008,7 @@ function verbDeclared(v, verb) {
8620
9008
  if (v.kind === void 0) return verb === "eval" ? NO_EFFECTS : void 0;
8621
9009
  const effects = effectsOfVerb(v.kind, verb);
8622
9010
  if (effects === void 0) return void 0;
8623
- if (effects.length === 0) return NO_EFFECTS;
8624
- return effects === "type" ? [v.typePath ?? []] : effects.map((kind) => kind.split("."));
9011
+ return effectKeys(v, effects);
8625
9012
  }
8626
9013
  /**
8627
9014
  * This function gives the rule of a machine for an effect, for the static kit (CAP.PERMITS). The rule is the hook
@@ -8770,7 +9157,8 @@ const DOWN_NAMES = /* @__PURE__ */ Object.freeze([
8770
9157
  const FOLD_NAMES = /* @__PURE__ */ Object.freeze(["acc", "item"]);
8771
9158
  /**
8772
9159
  * This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
8773
- * `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.
8774
9162
  */
8775
9163
  function equationsOf(kind) {
8776
9164
  const tree = kind.tree;
@@ -8814,7 +9202,32 @@ function equationsOf(kind) {
8814
9202
  attribute: name,
8815
9203
  down: false
8816
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
+ }
8817
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
+ });
8818
9231
  return out;
8819
9232
  }
8820
9233
  /**
@@ -8936,7 +9349,7 @@ function answered(world, node, name) {
8936
9349
  if (hasEntry(tree?.equations, name)) return true;
8937
9350
  if (node.parent === void 0 ? downNames(world).has(name) : hasEntry(node.parent.kind.tree?.down, name)) return true;
8938
9351
  const declared = tree?.attributes !== void 0 && Object.hasOwn(tree.attributes, name) ? tree.attributes[name] : void 0;
8939
- 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);
8940
9353
  }
8941
9354
  /**
8942
9355
  * This function checks the equations of the tree contract of a kind, each with the names of its rung (TREE.DOWN-NAMES).
@@ -8960,7 +9373,7 @@ function equationIssues(machine, kind, kindText, options, state) {
8960
9373
  own: void 0,
8961
9374
  parent: void 0
8962
9375
  };
8963
- for (const { label, term, names, attribute, down } of equationsOf(kind)) {
9376
+ for (const { label, term, names, attribute, down, contributes } of equationsOf(kind)) {
8964
9377
  const inner = {
8965
9378
  names,
8966
9379
  ...of === void 0 ? {} : { declaredType: of },
@@ -8970,7 +9383,7 @@ function equationIssues(machine, kind, kindText, options, state) {
8970
9383
  needs.push({
8971
9384
  name,
8972
9385
  why: `the equation ${label} reads it`,
8973
- hidden: down ? void 0 : attribute
9386
+ hidden: down || contributes === true ? void 0 : attribute
8974
9387
  });
8975
9388
  };
8976
9389
  for (const issue of checkAll(machine, term, inner, void 0, state, {
@@ -8987,7 +9400,8 @@ function equationIssues(machine, kind, kindText, options, state) {
8987
9400
  /**
8988
9401
  * This function finds the issues of the contract of a tree kind (TREE.CHECK-RESERVED). An attribute with the name of a
8989
9402
  * structural step gets no read. A tree kind of the run side contradicts the evaluation on demand of its attributes. A
8990
- * 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).
8991
9405
  */
8992
9406
  function contractIssues(kind, kindText) {
8993
9407
  const out = [];
@@ -8995,7 +9409,8 @@ function contractIssues(kind, kindText) {
8995
9409
  for (const table of [
8996
9410
  "equations",
8997
9411
  "down",
8998
- "attributes"
9412
+ "attributes",
9413
+ "contributes"
8999
9414
  ]) for (const name of Object.keys(tree?.[table] ?? {})) {
9000
9415
  const why = Object.hasOwn(STRUCTURAL, name) ? STRUCTURAL[name] : void 0;
9001
9416
  if (why !== void 0) out.push({
@@ -9011,6 +9426,12 @@ function contractIssues(kind, kindText) {
9011
9426
  place: [],
9012
9427
  reason: `The kind ${kindText} has a bad declaration tree.attributes.${name}.circular. ${rule.reason}`
9013
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
+ });
9014
9435
  }
9015
9436
  if (kind.side === "run") out.push({
9016
9437
  code: "#CALC!",
@@ -9098,6 +9519,7 @@ function checkAll(machine, doc, options, nodes, state = {
9098
9519
  };
9099
9520
  const allowed = options.caps === void 0 ? void 0 : permitsOf(machine);
9100
9521
  const waits = options.run === true ? /* @__PURE__ */ new Map() : void 0;
9522
+ const driven = /* @__PURE__ */ new Map();
9101
9523
  const drives = [];
9102
9524
  const maxDepth = machine.maxDepth ?? 1e3;
9103
9525
  const anyName = options.names === "any";
@@ -9174,7 +9596,7 @@ function checkAll(machine, doc, options, nodes, state = {
9174
9596
  });
9175
9597
  }
9176
9598
  const caps = v.caps;
9177
- const stated = verbDeclared(v, verb);
9599
+ const stated = hookEffects(v, verb, machine, name) ?? verbDeclared(v, verb);
9178
9600
  const declared = stated ?? NO_EFFECTS;
9179
9601
  if (allowed !== void 0 && caps !== void 0) {
9180
9602
  for (const kind of declared) if (!allowed(caps, kind)) push({
@@ -9185,7 +9607,9 @@ function checkAll(machine, doc, options, nodes, state = {
9185
9607
  });
9186
9608
  }
9187
9609
  if (waits !== void 0 && coded) {
9188
- 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;
9189
9613
  let chain = false;
9190
9614
  if (runner && up === false) {
9191
9615
  chain = {
@@ -9202,6 +9626,10 @@ function checkAll(machine, doc, options, nodes, state = {
9202
9626
  drives.push(chain);
9203
9627
  } else if (runner) chain = up ?? true;
9204
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
+ }
9205
9633
  if (typeof chain === "object" && !chain.wait) {
9206
9634
  const rule = allowed ?? permits;
9207
9635
  chain.wait = stated === void 0 || declared.some((kind) => caps === void 0 || rule(caps, kind));
@@ -9307,7 +9735,7 @@ function reaching(machine, doc, options) {
9307
9735
  const rule = options.caps === void 0 ? permits : permitsOf(machine);
9308
9736
  walk(machine.world, doc, (v) => {
9309
9737
  const caps = v.caps ?? EACH;
9310
- 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({
9311
9739
  kind,
9312
9740
  visit: v
9313
9741
  });
@@ -9328,7 +9756,9 @@ function reaching(machine, doc, options) {
9328
9756
  *
9329
9757
  * The result covers each effect that an evaluation or a run gives the host (CAP.EFFECTS-SOUND). A procedure value keeps
9330
9758
  * the capabilities of the document that made it, thus the effects of its body count at the body (CAP.LATENT). The
9331
- * 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.
9332
9762
  *
9333
9763
  * @param machine - The world and its contracts, and the hook `permits`.
9334
9764
  * @param doc - The document, as JSON.
@@ -9828,6 +10258,30 @@ function paramsOf(value) {
9828
10258
  const params = isRecord(value) ? value["params"] : void 0;
9829
10259
  return Array.isArray(params) ? params.filter((param) => typeof param === "string") : [];
9830
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
+ }
9831
10285
  /**
9832
10286
  * This function makes the type rules of the formula kit for one profile (TYPE.SYNTH, D25). Each evaluator of the kit
9833
10287
  * holds its rule. The rules read the rules of the profile that change a type: `truthy`, `truthType`, `logic`,
@@ -9968,11 +10422,14 @@ function formulaTypes(profile) {
9968
10422
  if (isUnknown(type, domains) || !plain && holdsEnvelope(type, domains)) return "unknown";
9969
10423
  if (index >= path.length) return type;
9970
10424
  const step = path[index];
9971
- if (!isKey(step)) return "unknown";
10425
+ if (!isKey(step)) {
10426
+ type = walkType(input, step, type);
10427
+ continue;
10428
+ }
9972
10429
  const next = stepType(type, step, domains, input.contract);
9973
10430
  if ("missing" in next) {
9974
10431
  const fields = next.missing.length === 0 ? "" : ` Its fields are ${listText(next.missing)}.`;
9975
- 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}` });
9976
10433
  return "unknown";
9977
10434
  }
9978
10435
  type = next.type;
@@ -12501,6 +12958,55 @@ function foldOf(node, name, fold) {
12501
12958
  return acc;
12502
12959
  }
12503
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
+ /**
12504
13010
  * TREE.LADDER: the rungs of the ladder of an attribute, in their order. A rung gives `undefined` when it does not
12505
13011
  * answer, and the next rung then tries.
12506
13012
  */
@@ -12524,6 +13030,7 @@ const REF_RUNGS = [
12524
13030
  ["default", (node, name) => {
12525
13031
  const declared = termIn(node.contract?.attributes, name);
12526
13032
  if (declared?.fold !== void 0) return foldOf(node, name, declared.fold);
13033
+ if (declared?.collect !== void 0) return collectIn(node, name, declared.collect);
12527
13034
  return declared?.default === void 0 ? void 0 : equationOf(node, declared.default, {});
12528
13035
  }]
12529
13036
  ];
@@ -12711,7 +13218,7 @@ function plainValue(value, open) {
12711
13218
  * tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error. The node
12712
13219
  * reads `self` only through `resolve` (C4, TREE.SELF).
12713
13220
  */
12714
- const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolve: walk }) => {
13221
+ const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolve: walk, doc }) => {
12715
13222
  const reached = walk(["self"]);
12716
13223
  const parent = reached.ok ? reached.thing : void 0;
12717
13224
  const outer = refNodeOf(parent);
@@ -12722,6 +13229,7 @@ const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolv
12722
13229
  const node = {
12723
13230
  tree: nested ? outer.tree : { open: true },
12724
13231
  kind,
13232
+ name: doc.name,
12725
13233
  place,
12726
13234
  parts,
12727
13235
  ctx: context,
@@ -15045,6 +15553,7 @@ var Shot = class {
15045
15553
  scenario;
15046
15554
  context;
15047
15555
  keys;
15556
+ driven;
15048
15557
  perform;
15049
15558
  replay;
15050
15559
  calls = [];
@@ -15058,12 +15567,13 @@ var Shot = class {
15058
15567
  result = NO_RESULT;
15059
15568
  afterwards = false;
15060
15569
  running;
15061
- constructor(machine, runner, scenario, context, keys, perform, replay) {
15570
+ constructor(machine, runner, scenario, context, keys, driven, perform, replay) {
15062
15571
  this.machine = machine;
15063
15572
  this.runner = runner;
15064
15573
  this.scenario = scenario;
15065
15574
  this.context = context;
15066
15575
  this.keys = keys;
15576
+ this.driven = driven;
15067
15577
  this.perform = perform;
15068
15578
  this.replay = replay;
15069
15579
  this.running = scenario.running;
@@ -15087,7 +15597,7 @@ var Shot = class {
15087
15597
  });
15088
15598
  const key = this.keys[part] ?? String(part);
15089
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.`);
15090
- 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).`);
15091
15601
  return this.scenario.results[part] ?? NO_RESULT;
15092
15602
  }
15093
15603
  /** This function records a call of a part, and gives the steps of the probe part. In a run, the part can wait. */
@@ -15280,13 +15790,14 @@ function typesNaming(world, name, verb) {
15280
15790
  /** This function gives the facts of the kind of a type. The hook `facts` of the machine wins over the contract. */
15281
15791
  function contractOf(env, kind, typePath, caps) {
15282
15792
  const facts = env.machine.facts?.(typePath, env.name);
15283
- const own = effectsOfVerb(kind, env.verb);
15793
+ const own = (env.verb === "eval" ? facts?.effects : void 0) ?? effectsOfVerb(kind, env.verb);
15284
15794
  const effects = own ?? kind.effects;
15285
15795
  const declared = effects === void 0 ? [] : effects === "type" ? [typePath] : effects;
15286
15796
  return {
15287
15797
  declared,
15288
15798
  pure: (facts?.pure ?? kind.pure) === true,
15289
15799
  lazy: facts?.lazy ?? kind.lazy,
15800
+ drives: env.verb === "eval" ? facts?.drives ?? kind.drives : void 0,
15290
15801
  describes: env.verb === "eval",
15291
15802
  declares: own !== void 0,
15292
15803
  caps: caps ?? [...declared, "probe"]
@@ -15434,6 +15945,7 @@ function samplesOf(type, typePath, kind, contract, extra) {
15434
15945
  },
15435
15946
  otherSplit,
15436
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),
15437
15949
  names: scopesOf(kind.scope, value, split.parts).sees
15438
15950
  });
15439
15951
  }
@@ -15580,7 +16092,7 @@ function instanceOf(env, under, doc, split, sample) {
15580
16092
  /** This function evaluates a probe document one time in a new shot. */
15581
16093
  function shoot(env, instance, sample, scenario, more = {}) {
15582
16094
  const context = more.context ?? rootContext(env.machine, makeContext(env.names, scenario.caps));
15583
- 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);
15584
16096
  instance(shot);
15585
16097
  return shot;
15586
16098
  }
@@ -17052,18 +17564,89 @@ const READ = /* @__PURE__ */ new Set([
17052
17564
  "enum",
17053
17565
  "const",
17054
17566
  "anyOf",
17055
- "oneOf"
17567
+ "oneOf",
17568
+ "allOf",
17569
+ "$ref"
17056
17570
  ]);
17057
17571
  /**
17058
- * The keywords that can permit more values than the other keywords permit. Older drafts ignore each keyword beside
17059
- * `$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`.
17060
17574
  */
17061
17575
  const WIDENING = /* @__PURE__ */ new Set([
17062
- "$ref",
17063
17576
  "$dynamicRef",
17064
17577
  "$recursiveRef",
17065
17578
  "nullable"
17066
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
+ }
17067
17650
  /** The base type of each name of the keyword `type`. */
17068
17651
  const BASES = {
17069
17652
  null: "null",
@@ -17086,26 +17669,57 @@ function setOwn(target, key, value) {
17086
17669
  function unionOf(types) {
17087
17670
  return types.reduce((all, type) => join(all, type), "never");
17088
17671
  }
17089
- /** 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. */
17090
17673
  var SchemaReader = class {
17674
+ root;
17091
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
+ }
17092
17685
  issue(at, reason) {
17093
17686
  this.issues.push({
17094
17687
  at,
17095
17688
  reason
17096
17689
  });
17097
17690
  }
17098
- /** This method reads one schema. */
17691
+ /** This method reads one schema: the meet of the types of its keywords, then its conditions. */
17099
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) {
17100
17711
  if (depth > MAX_SCHEMA_DEPTH) {
17101
17712
  this.issue(at, `The schema is deeper than ${MAX_SCHEMA_DEPTH} levels. The type reads it as unknown.`);
17102
- return "unknown";
17713
+ return;
17714
+ }
17715
+ if (schema === true) return;
17716
+ if (schema === false) {
17717
+ types.push("never");
17718
+ return;
17103
17719
  }
17104
- if (schema === true) return "unknown";
17105
- if (schema === false) return "never";
17106
17720
  if (!isRecord(schema)) {
17107
17721
  this.issue(at, "A schema is a record or a truth value. The type reads this value as unknown.");
17108
- return "unknown";
17722
+ return;
17109
17723
  }
17110
17724
  let widens = false;
17111
17725
  for (const key of Object.keys(schema)) {
@@ -17115,46 +17729,99 @@ var SchemaReader = class {
17115
17729
  widens = true;
17116
17730
  } else this.issue([...at, key], `The type does not read the keyword ${key}. It does not test what the keyword tests.`);
17117
17731
  }
17118
- if (widens) return "unknown";
17119
- const sides = [];
17120
- if (Object.hasOwn(schema, "type")) sides.push({
17121
- key: "type",
17122
- type: this.byType(schema, at, depth)
17123
- });
17124
- else if ([
17125
- "properties",
17126
- "required",
17127
- "additionalProperties",
17128
- "items"
17129
- ].some((key) => Object.hasOwn(schema, key)) && ![
17130
- "enum",
17131
- "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 [
17132
17763
  "anyOf",
17133
- "oneOf"
17134
- ].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.");
17135
- for (const key of ["anyOf", "oneOf"]) {
17764
+ "oneOf",
17765
+ "allOf"
17766
+ ]) {
17136
17767
  if (!Object.hasOwn(schema, key)) continue;
17137
17768
  const items = schema[key];
17138
17769
  if (!Array.isArray(items)) {
17139
17770
  this.issue([...at, key], `The value of ${key} is a list of schemas.`);
17140
17771
  continue;
17141
17772
  }
17142
- sides.push({
17773
+ if (key === "allOf") for (const [index, item] of items.entries()) this.parts(item, [
17774
+ ...at,
17143
17775
  key,
17144
- type: unionOf(items.map((item, index) => this.read(item, [
17145
- ...at,
17146
- key,
17147
- index
17148
- ], depth + 1)))
17149
- });
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))));
17150
17783
  }
17151
17784
  const values = this.values(schema, at);
17152
- if (values !== void 0) return { one: values.filter((value) => sides.every((side) => member(value, side.type))) };
17153
- const known = sides.filter((side) => !isUnknown(side.type, void 0));
17154
- const kept = known[0];
17155
- if (kept === void 0) return "unknown";
17156
- 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}.`);
17157
- 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
+ }
17158
17825
  }
17159
17826
  /** This method reads `enum` and `const`: the values that the schema permits, or `undefined` without them. */
17160
17827
  values(schema, at) {
@@ -17242,17 +17909,20 @@ var SchemaReader = class {
17242
17909
  * - `properties`, `required` and `additionalProperties` give a record. `false` closes it, and a property that is not
17243
17910
  * in `required` is optional.
17244
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.
17245
17916
  *
17246
17917
  * The type holds each value that the schema accepts. Each keyword that the function does not read tests nothing, as
17247
- * `unknown`, and the issues name it. Examples are `minimum`, `pattern` and `allOf`. A schema with `$ref` or `nullable`
17248
- * reads as `unknown`, because these keywords can permit more values. A schema with more than one of `type`, `anyOf`
17249
- * 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.
17250
17920
  *
17251
17921
  * @param schema - The schema, as JSON.
17252
17922
  * @returns The type, and the problems of the reading with their keys in the schema.
17253
17923
  */
17254
17924
  function typeFromSchema(schema) {
17255
- const reader = new SchemaReader();
17925
+ const reader = new SchemaReader(schema);
17256
17926
  try {
17257
17927
  return {
17258
17928
  type: reader.read(schema, [], 0),
@@ -17332,6 +18002,6 @@ function samplesAt(values, closed, depth) {
17332
18002
  function typeFromSamples(values, options = {}) {
17333
18003
  return samplesAt(values, options.closed === true, 0);
17334
18004
  }
17335
- 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, 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, 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, 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 };
17336
18006
 
17337
18007
  //# sourceMappingURL=index.js.map