@mark1russell7/coeval 0.4.2 → 0.4.3

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
@@ -175,9 +175,17 @@ function fromJson(value) {
175
175
  });
176
176
  }
177
177
  /**
178
- * This function tells if two JSON values are equal. Equal values have the same texts, numbers and truth values,
179
- * the same items in the same order, and the same fields. The order of the fields does not matter. It does not use the stack for the
180
- * depth, thus a deep document cannot overflow it.
178
+ * This function tells if two values are equal. Plain JSON compares by its structure. Equal values have the same
179
+ * texts, numbers and truth values, the same items in the same order, and the same fields. The order of the fields
180
+ * does not matter. Each leaf compares with `Object.is`, thus `-0` is not `0`, and `NaN` is `NaN`.
181
+ *
182
+ * Only lists and plain records (`isRecord`) compare by their items and fields. Each other object compares by
183
+ * identity: a date, a map, a function and an instance of a class are equal only to themselves. Thus an early cutoff
184
+ * with this function does not keep a stale host value.
185
+ *
186
+ * The function does not use the stack for the depth, thus a deep document cannot overflow it. A cycle of lists or
187
+ * records does not make it loop. `sameValue` of `agree` uses the same rules, and it adds rules for closures and for
188
+ * a rule of the caller.
181
189
  *
182
190
  * @param a - The first value.
183
191
  * @param b - The second value.
@@ -185,15 +193,23 @@ function fromJson(value) {
185
193
  */
186
194
  function docEquals(a, b) {
187
195
  const stack = [[a, b]];
196
+ let seen;
188
197
  while (stack.length > 0) {
189
198
  const [x, y] = stack.pop();
190
199
  if (Object.is(x, y)) continue;
191
- if (typeof x !== "object" || typeof y !== "object" || x === null || y === null) return false;
192
- if (Array.isArray(x) !== Array.isArray(y)) return false;
193
- if (Array.isArray(x)) {
200
+ const list = Array.isArray(x);
201
+ if (list !== Array.isArray(y)) return false;
202
+ if (!list && (!isRecord(x) || !isRecord(y))) return false;
203
+ seen ??= /* @__PURE__ */ new Map();
204
+ const pairs = seen.get(x);
205
+ if (pairs?.has(y) === true) continue;
206
+ if (pairs === void 0) seen.set(x, /* @__PURE__ */ new Set([y]));
207
+ else pairs.add(y);
208
+ if (list) {
209
+ const xs = x;
194
210
  const ys = y;
195
- if (x.length !== ys.length) return false;
196
- for (let index = 0; index < x.length; index += 1) stack.push([x[index], ys[index]]);
211
+ if (xs.length !== ys.length) return false;
212
+ for (let index = 0; index < xs.length; index += 1) stack.push([xs[index], ys[index]]);
197
213
  continue;
198
214
  }
199
215
  const xr = x;
@@ -1040,6 +1056,10 @@ const NO_SLOTS = Object.freeze([]);
1040
1056
  function hasSlots(template) {
1041
1057
  return template.t !== "data";
1042
1058
  }
1059
+ /** This function tells if a shape holds only data: no value of the shape can hold a part. */
1060
+ function isData(shape) {
1061
+ return shape.shape === "none" || shape.shape === "text" || shape.shape === "number" || shape.shape === "one";
1062
+ }
1043
1063
  /** This function gives the reason why a value is not data of a shape, or `undefined` when it is. */
1044
1064
  function dataFault(spec, item) {
1045
1065
  if (spec.shape === "text") return typeof item === "string" ? void 0 : "The value must be a text.";
@@ -1141,9 +1161,13 @@ function splitParts(shape, value) {
1141
1161
  shape: "term",
1142
1162
  ...spec.of === void 0 ? {} : { of: spec.of }
1143
1163
  };
1144
- return {
1164
+ const items = list.map((child, index) => walk(each, child, [...path, index]));
1165
+ return isData(each) ? {
1166
+ t: "data",
1167
+ value: item
1168
+ } : {
1145
1169
  t: "list",
1146
- items: list.map((child, index) => walk(each, child, [...path, index]))
1170
+ items
1147
1171
  };
1148
1172
  }
1149
1173
  if (!isRecord(item)) throw new Bad("The value must be a record.", path);
@@ -1211,6 +1235,54 @@ function splitParts(shape, value) {
1211
1235
  };
1212
1236
  }
1213
1237
  /**
1238
+ * This function reads one document by the contracts of the world. It finds the kind of the type. A bare value gets
1239
+ * the type that its position declares. Then the function splits the value by the shape of the kind. The plan, the
1240
+ * reference interpreter and the static kit read each document with this function. Thus they see the same parts.
1241
+ *
1242
+ * @param world - The world of the kinds.
1243
+ * @param value - The document, or a bare value.
1244
+ * @param declaredType - The type that the position of a bare value declares.
1245
+ * @returns The document, its kind and its parts, or the reason why the value is not a document.
1246
+ */
1247
+ function partsOf(world, value, declaredType) {
1248
+ const envelope = isEnvelope(value) ? value : void 0;
1249
+ if (envelope === void 0 && declaredType === void 0) return {
1250
+ ok: false,
1251
+ code: "#PART?",
1252
+ reason: "The value has no envelope, and its position declares no type."
1253
+ };
1254
+ const doc = envelope ?? {
1255
+ type: declaredType,
1256
+ value
1257
+ };
1258
+ const found = lookupKind(world, doc.type);
1259
+ const kind = found.ok ? found.kind : void 0;
1260
+ const split = splitParts(kind?.parts, doc.value);
1261
+ const typePath = found.ok ? found.path : void 0;
1262
+ if (split.ok) return {
1263
+ ok: true,
1264
+ doc,
1265
+ declared: envelope === void 0,
1266
+ kind,
1267
+ typePath,
1268
+ split
1269
+ };
1270
+ const where = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
1271
+ const reason = `${split.reason}${where} The kind is ${typeName(doc.type)}.`;
1272
+ return {
1273
+ ok: true,
1274
+ doc,
1275
+ declared: envelope === void 0,
1276
+ kind,
1277
+ typePath,
1278
+ split: {
1279
+ ok: false,
1280
+ reason,
1281
+ at: split.at
1282
+ }
1283
+ };
1284
+ }
1285
+ /**
1214
1286
  * This function tells if the path of a part matches a pattern of a scope rule. The key `"*"` of a pattern matches
1215
1287
  * each key.
1216
1288
  *
@@ -1655,15 +1727,15 @@ function finish(c, meta, code, run) {
1655
1727
  * This function finds the evaluator of the verb `eval` without the record of the rungs, for a compilation without a
1656
1728
  * trace. It gives the same evaluator as `ask`: the own entry, then the kind, then the default of the world.
1657
1729
  */
1658
- function evaluatorOf(world, envelope, found) {
1730
+ function evaluatorOf(world, envelope, kind) {
1659
1731
  const own = envelope?.own;
1660
1732
  const ownName = own !== void 0 && Object.hasOwn(own, "eval") ? own["eval"] : void 0;
1661
1733
  if (ownName !== void 0) return {
1662
1734
  name: ownName,
1663
1735
  unknown: false
1664
1736
  };
1665
- if (!found.ok) return { unknown: true };
1666
- const verbs = found.kind.verbs;
1737
+ if (kind === void 0) return { unknown: true };
1738
+ const verbs = kind.verbs;
1667
1739
  const name = Object.hasOwn(verbs, "eval") ? verbs["eval"] : Object.hasOwn(world.defaults, "eval") ? world.defaults["eval"] : void 0;
1668
1740
  return name === void 0 ? { unknown: false } : {
1669
1741
  name,
@@ -1682,23 +1754,28 @@ const NO_META = Object.freeze({
1682
1754
  side: "derive"
1683
1755
  });
1684
1756
  function compileNode(c, value, place, declared, depth, address = false) {
1685
- const envelope = isEnvelope(value) ? value : void 0;
1686
- const doc = envelope ?? {
1687
- type: declared ?? "?",
1688
- value
1689
- };
1757
+ const world = c.machine.world;
1758
+ const read = partsOf(world, value, declared);
1759
+ if (!read.ok) return failing(c, c.traced ? {
1760
+ path: place.map(String),
1761
+ doc: {
1762
+ type: "?",
1763
+ value
1764
+ },
1765
+ declared: true,
1766
+ answer: NO_ANSWER,
1767
+ side: "derive"
1768
+ } : NO_META, place, read.code, read.reason);
1769
+ const { doc, kind, split } = read;
1770
+ const envelope = read.declared ? void 0 : doc;
1690
1771
  const meta0 = c.traced ? {
1691
1772
  path: place.map(String),
1692
1773
  doc,
1693
- declared: envelope === void 0,
1774
+ declared: read.declared,
1694
1775
  answer: NO_ANSWER,
1695
1776
  side: "derive"
1696
1777
  } : NO_META;
1697
- if (envelope === void 0 && declared === void 0) return failing(c, meta0, place, "#PART?", "The value has no envelope, and its position declares no type.");
1698
1778
  if (depth > c.maxDepth) return failing(c, meta0, place, "#DEPTH!", `The nesting is deeper than ${c.maxDepth} documents.`);
1699
- const world = c.machine.world;
1700
- const found = lookupKind(world, doc.type);
1701
- const kind = found.ok ? found.kind : void 0;
1702
1779
  let meta = meta0;
1703
1780
  let name;
1704
1781
  let unknown;
@@ -1712,7 +1789,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
1712
1789
  name = answer.evaluator;
1713
1790
  unknown = answer.error === "unknown-kind";
1714
1791
  } else {
1715
- const quick = evaluatorOf(world, envelope, found);
1792
+ const quick = evaluatorOf(world, envelope, kind);
1716
1793
  name = quick.name;
1717
1794
  unknown = quick.unknown;
1718
1795
  }
@@ -1722,11 +1799,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
1722
1799
  const runner = name !== void 0 && Object.hasOwn(runners, name) ? runners[name] : void 0;
1723
1800
  const evaluator = name !== void 0 && Object.hasOwn(c.machine.evaluators, name) ? c.machine.evaluators[name] : void 0;
1724
1801
  if (name === void 0 || runner === void 0 && evaluator === void 0) return failing(c, meta, place, "#NOEVAL", `No rung gives an evaluator for ${kindText}.`);
1725
- const split = splitParts(kind?.parts, doc.value);
1726
- if (!split.ok) {
1727
- const at = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
1728
- return failing(c, meta, place, "#VALUE!", `${split.reason}${at} The kind is ${kindText}.`);
1729
- }
1802
+ if (!split.ok) return failing(c, meta, place, "#VALUE!", split.reason);
1730
1803
  const parts = split.parts.map((slot) => {
1731
1804
  const item = slot.item;
1732
1805
  const own = compileNode(c, item, place.concat(slot.path), slot.of, depth + 1, slot.address === true);
@@ -1748,7 +1821,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
1748
1821
  });
1749
1822
  const input = {
1750
1823
  kind: kindText,
1751
- typePath: found.ok ? found.path : [],
1824
+ typePath: read.typePath ?? [],
1752
1825
  doc,
1753
1826
  value: split.parts.length === 0 ? doc.value : split.fill(parts),
1754
1827
  data: split.data,
@@ -3020,40 +3093,32 @@ function walk(world, doc, visit, options = {}) {
3020
3093
  }];
3021
3094
  while (stack.length > 0) {
3022
3095
  const item = stack.pop();
3023
- const envelope = isEnvelope(item.value) ? item.value : void 0;
3024
- if (envelope === void 0 && item.declared === void 0) {
3096
+ const read = partsOf(world, item.value, item.declared);
3097
+ if (!read.ok) {
3025
3098
  options.problem?.({
3026
3099
  place: keysOf(item.place),
3027
3100
  at: keysOf(item.at),
3028
- code: "#PART?",
3029
- reason: "The value has no envelope, and its position declares no type."
3101
+ code: read.code,
3102
+ reason: read.reason
3030
3103
  });
3031
3104
  continue;
3032
3105
  }
3033
- const doc = envelope ?? {
3034
- type: item.declared ?? "?",
3035
- value: item.value
3036
- };
3037
- const found = lookupKind(world, doc.type);
3038
- const kind = found.ok ? found.kind : void 0;
3039
- const here = new Visit(item.place, item.at, doc, envelope === void 0, item.depth, item.names, item.later, item.slot, item.parent, kind, found.ok ? found.path : void 0, item.address ? true : void 0, item.caps);
3106
+ const { doc, kind, split } = read;
3107
+ const here = new Visit(item.place, item.at, doc, read.declared, item.depth, item.names, item.later, item.slot, item.parent, kind, read.typePath, item.address ? true : void 0, item.caps);
3040
3108
  if (visit(here) === "skip" || kind === void 0) continue;
3041
- const split = splitParts(kind.parts, doc.value);
3042
- const base = envelope === void 0 ? item.at : {
3109
+ const base = read.declared ? item.at : {
3043
3110
  up: item.at,
3044
3111
  keys: ["value"]
3045
3112
  };
3046
3113
  if (!split.ok) {
3047
- const at = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
3048
- const where = keysOf({
3049
- up: base,
3050
- keys: split.at
3051
- });
3052
3114
  options.problem?.({
3053
3115
  place: keysOf(item.place),
3054
- at: where,
3116
+ at: keysOf({
3117
+ up: base,
3118
+ keys: split.at
3119
+ }),
3055
3120
  code: "#VALUE!",
3056
- reason: `${split.reason}${at} The kind is ${typeName(doc.type)}.`
3121
+ reason: split.reason
3057
3122
  });
3058
3123
  continue;
3059
3124
  }
@@ -3185,7 +3250,8 @@ function capReason(v, kind) {
3185
3250
  * - `#PART?`: a value without a type.
3186
3251
  * - `#DEPTH!`: too much nesting.
3187
3252
  * - `#REF!` and `#STEP?`: a bad path.
3188
- * - `#NAME?`: a name that nothing binds.
3253
+ * - `#NAME?`: a name that nothing binds. With the hook `known` of the machine, also a name that the language does not
3254
+ * have, for example an operation.
3189
3255
  * - `#CAP!`: an effect that the capabilities of its document do not permit. A kind around the document can narrow them.
3190
3256
  *
3191
3257
  * @param machine - The world and the code of the evaluators.
@@ -3227,6 +3293,15 @@ function check(machine, doc, options = {}) {
3227
3293
  reason: `No rung gives an evaluator for ${typeName(v.doc.type)}.`,
3228
3294
  at: v.at
3229
3295
  });
3296
+ else if (machine.known !== void 0) {
3297
+ const known = machine.known(v.typePath ?? [], name);
3298
+ if (known !== true) issues.push({
3299
+ code: "#NAME?",
3300
+ place: v.place,
3301
+ reason: known,
3302
+ at: v.at
3303
+ });
3304
+ }
3230
3305
  const caps = v.caps;
3231
3306
  if (caps !== void 0 && v.kind?.effects !== void 0) {
3232
3307
  const kinds = v.kind.effects === "type" ? [v.typePath ?? []] : v.kind.effects.map((kind) => kind.split("."));
@@ -3259,7 +3334,7 @@ function check(machine, doc, options = {}) {
3259
3334
  reason: `A path starts with a root: ${ROOTS.join(", ")}.`,
3260
3335
  at: stepAt(0)
3261
3336
  });
3262
- return "skip";
3337
+ return;
3263
3338
  }
3264
3339
  if (!roots.includes(root)) issues.push({
3265
3340
  code: "#REF!",
@@ -3366,7 +3441,7 @@ function valuesOf(parts, ctx, rt) {
3366
3441
  }
3367
3442
  return values;
3368
3443
  }
3369
- function partsOf(value) {
3444
+ function partsOf$1(value) {
3370
3445
  return isRecord(value) ? value : {};
3371
3446
  }
3372
3447
  const LIT_SUMMARY = "A literal: its value is the result.";
@@ -3430,7 +3505,14 @@ function formulaKinds() {
3430
3505
  verbs: { eval: "formula.fn" },
3431
3506
  parts: {
3432
3507
  shape: "record",
3433
- fields: { body: term },
3508
+ fields: {
3509
+ params: {
3510
+ shape: "list",
3511
+ item: { shape: "text" },
3512
+ distinct: true
3513
+ },
3514
+ body: term
3515
+ },
3434
3516
  required: ["params", "body"]
3435
3517
  },
3436
3518
  scope: [{
@@ -3582,7 +3664,7 @@ function formulaEvaluators(profile = {}) {
3582
3664
  };
3583
3665
  }),
3584
3666
  "formula.record": compiled(({ value }) => {
3585
- const fields = partsOf(value);
3667
+ const fields = partsOf$1(value);
3586
3668
  const keys = Object.keys(fields);
3587
3669
  const codes = keys.map((key) => fields[key].code);
3588
3670
  if (combine !== void 0) {
@@ -3615,8 +3697,8 @@ function formulaEvaluators(profile = {}) {
3615
3697
  return Array.isArray(values) ? ok(values) : values;
3616
3698
  }),
3617
3699
  "formula.let": compiled(({ value }) => {
3618
- const record = partsOf(value);
3619
- const bindings = Object.entries(partsOf(record["bind"]));
3700
+ const record = partsOf$1(value);
3701
+ const bindings = Object.entries(partsOf$1(record["bind"]));
3620
3702
  const body = record["body"];
3621
3703
  return (ctx, rt) => {
3622
3704
  const names = { ...ctx.names };
@@ -3637,7 +3719,7 @@ function formulaEvaluators(profile = {}) {
3637
3719
  return () => bad;
3638
3720
  }
3639
3721
  const names = params;
3640
- const body = partsOf(value)["body"];
3722
+ const body = partsOf$1(value)["body"];
3641
3723
  return (ctx, rt) => {
3642
3724
  const closure = (...args) => {
3643
3725
  const scope = { ...ctx.names };
@@ -3654,7 +3736,7 @@ function formulaEvaluators(profile = {}) {
3654
3736
  };
3655
3737
  }),
3656
3738
  "formula.call": compiled(({ value }) => {
3657
- const record = partsOf(value);
3739
+ const record = partsOf$1(value);
3658
3740
  const fnPart = record["fn"];
3659
3741
  const args = Array.isArray(record["args"]) ? record["args"] : [];
3660
3742
  return (ctx, rt) => {
@@ -3675,7 +3757,7 @@ function formulaEvaluators(profile = {}) {
3675
3757
  };
3676
3758
  }),
3677
3759
  "formula.if": compiled(({ value }) => {
3678
- const record = partsOf(value);
3760
+ const record = partsOf$1(value);
3679
3761
  const test = record["test"];
3680
3762
  const then = record["then"];
3681
3763
  const other = record["else"];
@@ -3794,14 +3876,52 @@ function formulaEvaluators(profile = {}) {
3794
3876
  };
3795
3877
  }
3796
3878
  /**
3879
+ * This function makes the hook `known` of the formula kit for one profile (STATIC.KNOWN). The evaluator
3880
+ * `formula.op` knows an operation of `lazy` or of `ops`. A profile whose `ops` is a function answers through a call
3881
+ * of that function. A function that throws counts as an answer, because the run then gives `#CALC!`. The evaluator
3882
+ * `formula.ext` knows an extension of `ext` and each standard extension. The hook knows each name of the other
3883
+ * evaluators.
3884
+ *
3885
+ * @param profile - The operations and the extensions of the language.
3886
+ * @returns The hook: `true`, or the reason of the `#NAME?` that the evaluator gives in a run.
3887
+ */
3888
+ function formulaKnown(profile = {}) {
3889
+ const hasOp = (name) => {
3890
+ if (profile.lazy !== void 0 && Object.hasOwn(profile.lazy, name)) return true;
3891
+ const ops = profile.ops;
3892
+ if (ops === void 0) return false;
3893
+ if (typeof ops !== "function") return Object.hasOwn(ops, name);
3894
+ try {
3895
+ return ops(name) !== void 0;
3896
+ } catch {
3897
+ return true;
3898
+ }
3899
+ };
3900
+ const hasExt = (name) => profile.ext !== void 0 && Object.hasOwn(profile.ext, name) || Object.hasOwn(STD_EXT, name);
3901
+ return (path, evaluator) => {
3902
+ if (evaluator !== "formula.op" && evaluator !== "formula.ext") return true;
3903
+ const name = path.slice(1).join(".");
3904
+ if (evaluator === "formula.op") return hasOp(name) ? true : `The language has no operation ${name}.`;
3905
+ return hasExt(name) ? true : `The language has no extension ${name}.`;
3906
+ };
3907
+ }
3908
+ /**
3797
3909
  * This function makes a machine for a formula language. The machine has the kinds and the evaluators of the
3798
- * formula kit. It also has the kinds, the evaluators and the walkers that the language adds.
3910
+ * formula kit. It also has the kinds, the evaluators and the walkers that the language adds. Its hook `known` comes
3911
+ * from the profile (`formulaKnown`). A hook `known` of the language answers each name that the kit knows.
3799
3912
  *
3800
3913
  * @param profile - The operations and the rules of the language.
3801
3914
  * @param more - The parts of a machine that the language adds. Its kinds win over the kinds of the kit.
3802
3915
  * @returns The machine.
3803
3916
  */
3804
3917
  function formulaMachine(profile = {}, more = {}) {
3918
+ const kit = formulaKnown(profile);
3919
+ const replaced = (name) => more.evaluators !== void 0 && Object.hasOwn(more.evaluators, name) || more.runners !== void 0 && Object.hasOwn(more.runners, name);
3920
+ const theirs = more.known;
3921
+ const known = (path, evaluator) => {
3922
+ const answer = replaced(evaluator) ? true : kit(path, evaluator);
3923
+ return answer === true && theirs !== void 0 ? theirs(path, evaluator) : answer;
3924
+ };
3805
3925
  return {
3806
3926
  ...more,
3807
3927
  world: {
@@ -3814,7 +3934,8 @@ function formulaMachine(profile = {}, more = {}) {
3814
3934
  evaluators: {
3815
3935
  ...formulaEvaluators(profile),
3816
3936
  ...more.evaluators
3817
- }
3937
+ },
3938
+ known
3818
3939
  };
3819
3940
  }
3820
3941
  //#endregion
@@ -3843,31 +3964,22 @@ function choose(env, name) {
3843
3964
  */
3844
3965
  function examine(env, at) {
3845
3966
  const { place, declared } = at;
3846
- const envelope = isEnvelope(at.value) ? at.value : void 0;
3847
- if (envelope === void 0 && declared === void 0) return fail("#PART?", "The value has no envelope, and its position declares no type.", { place });
3967
+ const world = env.machine.world;
3968
+ const read = partsOf(world, at.value, declared);
3969
+ if (!read.ok) return fail(read.code, read.reason, { place });
3848
3970
  if (at.depth > env.maxDepth) return fail("#DEPTH!", `The nesting is deeper than ${env.maxDepth} documents.`, { place });
3849
- const doc = envelope ?? {
3850
- type: declared,
3851
- value: at.value
3852
- };
3971
+ const { doc, kind, split } = read;
3853
3972
  const kindText = typeName(doc.type);
3854
- const world = env.machine.world;
3855
- const answer = ask(world, envelope ?? {}, "eval", declared === void 0 ? {} : { declaredType: declared });
3973
+ const answer = ask(world, read.declared ? {} : doc, "eval", declared === void 0 ? {} : { declaredType: declared });
3856
3974
  if (answer.error === "unknown-kind") return fail("#KIND?", `The world has no kind ${kindText}.`, { place });
3857
3975
  const name = answer.evaluator;
3858
3976
  const chosen = name === void 0 ? void 0 : choose(env, name);
3859
3977
  if (name === void 0 || chosen === void 0) return fail("#NOEVAL", `No rung gives an evaluator for ${kindText}.`, { place });
3860
- const found = lookupKind(world, doc.type);
3861
- const kind = found.ok ? found.kind : void 0;
3862
- const split = splitParts(kind?.parts, doc.value);
3863
- if (!split.ok) {
3864
- const where = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
3865
- return fail("#VALUE!", `${split.reason}${where} The kind is ${kindText}.`, { place });
3866
- }
3978
+ if (!split.ok) return fail("#VALUE!", split.reason, { place });
3867
3979
  return {
3868
3980
  doc,
3869
3981
  kindText,
3870
- typePath: found.ok ? found.path : [],
3982
+ typePath: read.typePath ?? [],
3871
3983
  kind,
3872
3984
  name,
3873
3985
  chosen,
@@ -4327,8 +4439,14 @@ function valuesOfStrict(parts) {
4327
4439
  /**
4328
4440
  * This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
4329
4441
  * by field, and the order of the fields does not matter. Each other leaf compares with `Object.is`, thus `-0` is not
4330
- * `0`, and `NaN` is `NaN`. Two closures of `fn` are the same when they have the same parameters. Two `Held` values
4331
- * are the same when their errors have the same code and place. Identity is not compared.
4442
+ * `0`, and `NaN` is `NaN`. The identity of a list or of a plain record is not compared.
4443
+ *
4444
+ * Two closures of `fn` are the same when they have the same parameters. Two `Held` values are the same when their
4445
+ * errors have the same code and place. Each other object is the same only as itself, unless the rule of the caller
4446
+ * tells otherwise.
4447
+ *
4448
+ * `docEquals` uses the same rules for lists, plain records and the other objects. It has no rules for closures, for
4449
+ * `Held` values and for a rule of the caller. Thus two closures are equal there only when they are the same function.
4332
4450
  *
4333
4451
  * @param a - The first value.
4334
4452
  * @param b - The second value.
@@ -4564,8 +4682,9 @@ function leafKind(kind) {
4564
4682
  /**
4565
4683
  * This function builds a random document for a world from the contracts of its kinds. It reads the shapes of the
4566
4684
  * parts, the declared types, the permitted kinds and the names that the scope rules bind. Some choices are malformed
4567
- * on purpose. Examples are an unknown kind, a value without its shape, a bare value without a type and a name that
4568
- * nothing binds. The depth and the size have limits.
4685
+ * on purpose, for example an unknown kind or an unknown name under a namespace with a fallback. Other examples are a
4686
+ * value without its shape, a bare value without a type and a name that nothing binds. The depth and the size have
4687
+ * limits.
4569
4688
  *
4570
4689
  * @param world - The world.
4571
4690
  * @param options - The seed, the types, the limits and the data of the leaves.
@@ -4582,6 +4701,10 @@ function generate(world, options) {
4582
4701
  const found = lookupKind(world, type);
4583
4702
  return found.ok && Object.hasOwn(found.kind.verbs, "step");
4584
4703
  });
4704
+ const unknownNames = Object.keys(world.kinds).filter((key) => {
4705
+ const entry = world.kinds[key];
4706
+ return isNamespace(entry) && entry.fallback !== void 0;
4707
+ }).map((key) => [key, "nope"]);
4585
4708
  let size = 0;
4586
4709
  const kindOf = (type) => {
4587
4710
  const found = lookupKind(world, type);
@@ -4804,9 +4927,17 @@ function generate(world, options) {
4804
4927
  case "one": return rng.chance(malformed) ? "other" : rng.pick(shape.values);
4805
4928
  }
4806
4929
  };
4807
- /** A malformed document: an unknown kind, a bad shape, a bare value without a type, or a bad reference. */
4930
+ /**
4931
+ * A malformed document: an unknown kind, or an unknown name under a namespace with a fallback. It can also be a
4932
+ * bad shape, a bare value without a type, or a bad reference.
4933
+ */
4808
4934
  const brokenAt = (pos) => {
4809
4935
  const roll = rng.int(5);
4936
+ if (roll === 0 && unknownNames.length > 0 && rng.chance(.5)) {
4937
+ const type = rng.pick(unknownNames);
4938
+ const kind = kindOf(type);
4939
+ return envelope(type, kind?.parts === void 0 ? dataOf(type) : valueOf(kind.parts, [], pos, scopeOf(kind), type));
4940
+ }
4810
4941
  if (roll === 0) return {
4811
4942
  type: rng.pick(["nothing", ["no", "such"]]),
4812
4943
  value: 1
@@ -4844,7 +4975,10 @@ function generate(world, options) {
4844
4975
  size += 1;
4845
4976
  const leaf = pos.depth >= maxDepth || size >= maxSize;
4846
4977
  if (rng.chance(malformed)) return brokenAt(pos);
4847
- if (pos.declared !== void 0 && rng.chance(leaf ? .7 : .3)) return dataOf([pos.declared]);
4978
+ if (pos.declared !== void 0 && rng.chance(leaf ? .7 : .3)) {
4979
+ const bare = dataOf([pos.declared]);
4980
+ if (!isEnvelope(bare) || permitted(typePath(bare.type) ?? [], pos.kinds)) return bare;
4981
+ }
4848
4982
  let candidates = types.filter((type) => permitted(type, pos.kinds) && (!leaf || leafKind(kindOf(type))));
4849
4983
  if (pos.hint !== void 0 && rng.chance(.6)) {
4850
4984
  const hinted = candidates.filter((type) => pos.hint?.some((hint) => hint.length === type.length && hint.every((key, index) => key === type[index])));
@@ -4866,6 +5000,6 @@ function generate(world, options) {
4866
5000
  });
4867
5001
  }
4868
5002
  //#endregion
4869
- export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkSteps, classify, context, dataEdges, derefStep, describe, docEquals, effectEdges, entriesAsJson, evaluate, fail, failures, field, flatten, formulaDefinitions, formulaEvaluators, formulaKinds, formulaMachine, fromJson, generate, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, narrow, nearestWalker, nodeAt, ok, partCaps, pathMatches, permits, placed, planner, prepare, random, ref, reference, refsOf, resolve, run, sameValue, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, typesOf, valueOfThing, walk, worldThing };
5003
+ export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkSteps, classify, context, dataEdges, derefStep, describe, docEquals, effectEdges, entriesAsJson, evaluate, fail, failures, field, flatten, formulaDefinitions, formulaEvaluators, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, narrow, nearestWalker, nodeAt, ok, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, random, ref, reference, refsOf, resolve, run, sameValue, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, typesOf, valueOfThing, walk, worldThing };
4870
5004
 
4871
5005
  //# sourceMappingURL=index.js.map