@mark1russell7/coeval 0.18.0 → 0.19.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/coeval",
4
- "version": "0.18.0",
4
+ "version": "0.19.0",
5
5
  "description": "coeval: the kernel of declarative languages. Documents in envelopes, a ladder that finds the evaluator, kind contracts, compiled plans, a static kit, a formula kit and a run side with capabilities.",
6
6
  "type": "module",
7
7
  "main": "./build/index.js",
package/src/codec.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type Json, isEnvelope, isRecord } from "./doc.ts";
2
2
  import { type Result, describe, fail, ok } from "./errors.ts";
3
3
  import { type Machine, makeContext, prepare } from "./evaluate.ts";
4
- import { type Domain, type DomainCodec, domainOf, member } from "./types.ts";
4
+ import { type Domain, type DomainCodec, domainOf, lineOf, member } from "./types.ts";
5
5
 
6
6
  /*
7
7
  * The codec of the values that JSON cannot write (K140, D40). A document holds such a value as a standard extension:
@@ -71,7 +71,7 @@ function setOwn(target: Record<string, Json>, key: string, value: Json): void {
71
71
  /** The state of one call of `encodeValue`. */
72
72
  interface Encoder {
73
73
  readonly domains: Domains;
74
- /** The domains with a codec, in the order of their names. */
74
+ /** The domains with a codec, in the order of `codecOrder`. */
75
75
  readonly codecs: readonly (readonly [string, DomainCodec])[];
76
76
  /** The answer of `isJson` for each container that it examined. */
77
77
  readonly json: WeakMap<object, boolean>;
@@ -151,7 +151,8 @@ function encodeTerm(value: unknown, encoder: Encoder): Json {
151
151
  *
152
152
  * - A value that JSON writes exactly stays as it is. A literal with the shape of an envelope goes into `lit`.
153
153
  * - `NaN`, an infinity and `-0` become `ext.number`, and `undefined` becomes `ext.undefined`.
154
- * - A member of a domain with a codec becomes `ext.domain`. The domains go in the order of their names.
154
+ * - A member of a domain with a codec becomes `ext.domain`. The codec of a domain under another domain comes first
155
+ * (EXT.SPECIFIC, D109). The order of the names decides between unrelated domains.
155
156
  * - A list or a plain record with such a value becomes the construct `list` or `record`.
156
157
  *
157
158
  * @param machine - The machine, or only its domains.
@@ -161,15 +162,34 @@ function encodeTerm(value: unknown, encoder: Encoder): Json {
161
162
  */
162
163
  export function encodeValue(machine: Pick<Machine, "domains">, value: unknown): Json {
163
164
  const domains = machine.domains;
165
+ return encodeTerm(value, { domains, codecs: codecOrder(domains), json: new WeakMap(), open: new Set() });
166
+ }
167
+
168
+ /**
169
+ * This function gives the domains with a codec in the order of `encodeValue` (EXT.SPECIFIC, D109). The depth of a
170
+ * domain is the count of the other domains with a codec in its line. A deeper domain comes first, and the order of the
171
+ * names decides between domains of the same depth (EXT.TIE). Thus a domain comes before each domain with a codec above it.
172
+ */
173
+ function codecOrder(domains: Domains): readonly (readonly [string, DomainCodec])[] {
164
174
  const codecs: (readonly [string, DomainCodec])[] = [];
165
- if (domains !== undefined) {
166
- for (const name of Object.keys(domains).sort()) {
167
- const domain = domainOf(name, domains);
168
- const codec = domain === undefined ? undefined : codecOf(domain);
169
- if (codec !== undefined) codecs.push([name, codec]);
170
- }
175
+ if (domains === undefined) return codecs;
176
+ let parents = false;
177
+ for (const name of Object.keys(domains).sort()) {
178
+ const domain = domainOf(name, domains);
179
+ const codec = domain === undefined ? undefined : codecOf(domain);
180
+ if (domain === undefined || codec === undefined) continue;
181
+ codecs.push([name, codec]);
182
+ if (Array.isArray(domain.under)) parents = true;
183
+ }
184
+ // Without a parent of a domain with a codec, each depth is zero, thus the order of the names stays.
185
+ if (!parents) return codecs;
186
+ const depth = new Map<string, number>();
187
+ for (const [name] of codecs) {
188
+ const line = lineOf(name, domains);
189
+ depth.set(name, codecs.filter(([other]) => other !== name && line.has(other)).length);
171
190
  }
172
- return encodeTerm(value, { domains, codecs, json: new WeakMap(), open: new Set() });
191
+ // The sort is stable, thus the order of the names stays between domains of the same depth.
192
+ return codecs.sort(([a], [b]) => (depth.get(b) as number) - (depth.get(a) as number));
173
193
  }
174
194
 
175
195
  /**
package/src/durable.ts CHANGED
@@ -2,7 +2,7 @@ import type { Context } from "./deref.ts";
2
2
  import type { Result } from "./errors.ts";
3
3
  import { type Machine, type Plan, type PrepareOptions, type Read, prepare } from "./evaluate.ts";
4
4
  import { type DocFacts, type FactsOptions, factsOf } from "./facts.ts";
5
- import { type VerifyOptions, verifyReads } from "./reads.ts";
5
+ import { type PrintOptions, type ReadPrints, type VerifyOptions, printReads, verifyReads } from "./reads.ts";
6
6
 
7
7
  /*
8
8
  * Two levels of durability (R4 P7, D30). The plan and the facts of a document depend only on the structure: the
@@ -42,6 +42,11 @@ export interface DurableOptions {
42
42
  readonly volatile?: FactsOptions["volatile"];
43
43
  /** The rule of the host for two things, as in `verifyReads`. */
44
44
  readonly equal?: VerifyOptions["equal"];
45
+ /**
46
+ * The check of changes in place (READS.IN-PLACE, D112). With it, the cache keeps the prints of the reads of each
47
+ * pure document, and a verification compares them. `true` gives the default limit. Without it, the cache makes no prints.
48
+ */
49
+ readonly inPlace?: true | PrintOptions;
45
50
  }
46
51
 
47
52
  /**
@@ -154,10 +159,11 @@ function byDocument<T>(make: (doc: unknown) => T): (doc: unknown) => T {
154
159
  * and each result (DURABLE.STRUCTURE).
155
160
  *
156
161
  * The host keeps two promises. In one data epoch, the context of a key gives the same data. The values of a
157
- * context and the documents do not change in place, as PLAN.CACHE and `verifyReads` say.
162
+ * context and the documents do not change in place, as PLAN.CACHE and `verifyReads` say. The option `inPlace`
163
+ * finds a change in place of a value that a pure document read (READS.IN-PLACE).
158
164
  *
159
165
  * @param machine - The machine of the first structure epoch.
160
- * @param options - The type of a bare root value, the verb, the volatile reads and the rule of the host for two things.
166
+ * @param options - The type of a bare root value, the verb, the volatile reads, the rule of the host and the check of changes in place.
161
167
  * @returns The cache.
162
168
  */
163
169
  export function durable(machine: Machine, options: DurableOptions = {}): Durable {
@@ -167,6 +173,9 @@ export function durable(machine: Machine, options: DurableOptions = {}): Durable
167
173
  };
168
174
  const factsOptions: FactsOptions = { ...prepareOptions, ...(options.volatile === undefined ? {} : { volatile: options.volatile }) };
169
175
  const verifyOptions: VerifyOptions = options.equal === undefined ? {} : { equal: options.equal };
176
+ const printOptions: PrintOptions | undefined = options.inPlace === true ? {} : options.inPlace;
177
+ // The prints of the reads of each entry, only with the option `inPlace`. Thus an entry keeps its shape (PLAN.LEAN).
178
+ const printsOf = printOptions === undefined ? undefined : new WeakMap<Entry, ReadPrints>();
170
179
  const counts = { compiles: 0, facts: 0, evaluations: 0, verifications: 0, reuses: 0 };
171
180
  const results = new Map<unknown, Entry>();
172
181
  let current = machine;
@@ -205,7 +214,8 @@ export function durable(machine: Machine, options: DurableOptions = {}): Durable
205
214
  }
206
215
  if (entry.reads !== undefined) {
207
216
  counts.verifications += 1;
208
- if (verifyReads(plan, entry.reads, context, verifyOptions).same) {
217
+ const prints = printsOf?.get(entry);
218
+ if (verifyReads(plan, entry.reads, context, prints === undefined ? verifyOptions : { ...verifyOptions, prints }).same) {
209
219
  entry.data = epochs.data;
210
220
  counts.reuses += 1;
211
221
  return entry.result;
@@ -217,7 +227,9 @@ export function durable(machine: Machine, options: DurableOptions = {}): Durable
217
227
  counts.evaluations += 1;
218
228
  const result = plan.evaluate(context, reads === undefined ? undefined : { reads });
219
229
  // A list that grows by push keeps room for more reads. A copy of a short list is smaller (PLAN.LEAN).
220
- results.set(key, { plan, result, reads: reads === undefined ? undefined : reads.length === 0 ? NO_READS : reads.slice(), data: epochs.data });
230
+ const made: Entry = { plan, result, reads: reads === undefined ? undefined : reads.length === 0 ? NO_READS : reads.slice(), data: epochs.data };
231
+ results.set(key, made);
232
+ if (printsOf !== undefined && made.reads !== undefined) printsOf.set(made, printReads(made.reads, printOptions));
221
233
  return result;
222
234
  },
223
235
  changeData() {
package/src/facts.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Json, type Key, isEnvelope, isKey } from "./doc.ts";
2
- import { type Kind, type LazyRule, type Side, type World, equationType, lookupKind } from "./world.ts";
2
+ import { type Kind, type KindFacts, type LazyRule, type Side, type World, equationType, lookupKind } from "./world.ts";
3
3
  import { answerOf } from "./ladder.ts";
4
4
  import { pathMatches, splitParts } from "./parts.ts";
5
5
  import { type Root, ROOTS, isRef } from "./deref.ts";
@@ -43,9 +43,12 @@ export interface DocFacts {
43
43
  * here. The roots that a kind declares in `reads` are here too, each one at the place of its document.
44
44
  */
45
45
  readonly reads: readonly StaticRead[];
46
- /** `"run"` when one kind of the document is on the run side (FACTS.SIDE). */
46
+ /** `"run"` when one kind of the document is on the run side for the verb of the facts (FACTS.SIDE, FACTS.VERB-SIDE). */
47
47
  readonly side: Side;
48
- /** The kinds of the effects that the contracts declare, as texts with dots, each one time (FACTS.EFFECTS). */
48
+ /**
49
+ * The kinds of the effects that the contracts declare for the verb of the facts, as texts with dots, each one time
50
+ * (FACTS.EFFECTS, FACTS.VERB-EFFECTS).
51
+ */
49
52
  readonly effects: readonly string[];
50
53
  /**
51
54
  * `true` when each kind of the document declares `pure`, and the document has no effect and is not volatile. Then
@@ -63,8 +66,10 @@ export interface FactsOptions {
63
66
  /** The type of a bare root value. */
64
67
  readonly declaredType?: string;
65
68
  /**
66
- * The verb whose evaluators the facts describe, as in `prepare`. The default is `eval`. The contract of a kind
67
- * describes the code of `eval` only. For another verb, only the hook `facts` describes the code (FACTS.VERB).
69
+ * The verb whose evaluators the facts describe, as in `prepare`. The default is `eval`. The marks `pure`,
70
+ * `volatile` and `lazy` of a contract describe the code of `eval` only. For another verb, only the hook `facts`
71
+ * describes the code (FACTS.VERB). The side and the effects come from the entry of the verb in `verbEffects`
72
+ * (FACTS.VERB-EFFECTS, D111).
68
73
  */
69
74
  readonly verb?: string;
70
75
  /**
@@ -92,7 +97,7 @@ function tight<T>(list: readonly T[]): readonly T[] {
92
97
  }
93
98
 
94
99
  /** What a document gives to its parts: its own lazy mark, and the lazy rules of its kind for the parts. */
95
- interface Above {
100
+ export interface Above {
96
101
  readonly lazy: boolean;
97
102
  readonly rules: readonly LazyRule[] | typeof EACH | undefined;
98
103
  /** The position of each part among the parts that a rule `after` covers. It is made at the first need. */
@@ -136,7 +141,7 @@ function positionOf(above: Above, parent: DocVisit, rule: LazyRule, slot: readon
136
141
  }
137
142
 
138
143
  /** This function tells if the lazy rules of a document cover one of its parts. */
139
- function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean {
144
+ export function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean {
140
145
  const rules = above.rules;
141
146
  if (rules === undefined) return false;
142
147
  if (rules === EACH) return true;
@@ -148,6 +153,32 @@ function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean
148
153
  return false;
149
154
  }
150
155
 
156
+ /** The code of a document for the facts: the answer of the hook, and the lazy rules that the code gives its parts. */
157
+ export interface CodeFacts {
158
+ /** `true` when the contract of the kind describes the code. */
159
+ readonly described: boolean;
160
+ /** The answer of the hook `facts` of the machine for the code. */
161
+ readonly answer: KindFacts | undefined;
162
+ /** The lazy rules of the parts (FACTS.LAZY). Code that the contract and the hook do not describe makes each part lazy. */
163
+ readonly rules: Above["rules"];
164
+ }
165
+
166
+ /**
167
+ * This function gives the code of a document of a kind for the facts (FACTS.LAZY). `factsOf` and the graph of
168
+ * attribute instances read the lazy rules with this one function (D107).
169
+ *
170
+ * @param machine - The world and the hook `facts`.
171
+ * @param v - The visit of the document.
172
+ * @param kind - The kind of the document.
173
+ * @param verb - The verb whose code the facts describe.
174
+ * @returns The answer of the hook and the lazy rules.
175
+ */
176
+ export function codeFactsOf(machine: Machine, v: DocVisit, kind: Kind, verb: string): CodeFacts {
177
+ const code = codeOf(machine.world, v.declared ? undefined : v.doc.own, kind, verb, "eval");
178
+ const answer = code.name === undefined ? undefined : machine.facts?.(v.typePath ?? [], code.name);
179
+ return { described: code.described, answer, rules: code.described ? (answer?.lazy ?? kind.lazy) : (answer?.lazy ?? EACH) };
180
+ }
181
+
151
182
  /**
152
183
  * This function gives the static facts of a document, for a schedule (R4 P2, D24). It walks the document one time,
153
184
  * by the contracts of its kinds:
@@ -157,7 +188,7 @@ function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean
157
188
  * declares `reads` adds a read of each of its roots at each document of the kind. The first document of a tree
158
189
  * kind also gets the lazy reads of the equations of its kind (FACTS.EQUATIONS). A read from `self` or from a name
159
190
  * of a rung is not one of them, because it is not free (READS.EQUATION).
160
- * - `side` and `effects`: from the contract of each kind.
191
+ * - `side` and `effects`: from the contract of each kind, for the verb of the facts.
161
192
  * - `pure`: each kind declares `pure`, also in an equation, and the document has no effect and is not volatile.
162
193
  * - `volatile`: a kind, an operation or a step document declares `volatile`, or the host marks a read.
163
194
  *
@@ -166,9 +197,11 @@ function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean
166
197
  * entry. Thus a document with an own entry for another evaluator is not pure, and each of its parts is lazy. The
167
198
  * hook can still answer for that evaluator.
168
199
  *
169
- * The option `verb` names the verb whose code the facts describe (FACTS.VERB). A contract describes the code of
170
- * `eval`, thus the rule of an own entry applies to each document for another verb. The side and the effects come
171
- * from the contracts for each verb.
200
+ * The option `verb` names the verb whose code the facts describe (FACTS.VERB). The marks of a contract describe the
201
+ * code of `eval`, thus the rule of an own entry applies to each document for another verb. For another verb, the
202
+ * effects are the effects that the entry of the verb in `verbEffects` declares (FACTS.VERB-EFFECTS, D111). A kind is
203
+ * on the run side for that verb when its entry declares an effect (FACTS.VERB-SIDE). A kind without an entry for the
204
+ * verb adds no effect, as in `check` (FACTS.VERB-UNDECLARED). Its code is still not pure without an answer of the hook.
172
205
  *
173
206
  * A read is one resolution of its path. A reference that the resolution reaches and follows is a part of that read
174
207
  * (DEREF.COLLAPSE), not a separate read. The facts depend only on the document and the machine. Thus a host computes
@@ -220,17 +253,15 @@ export function factsOf(machine: Machine, doc: unknown, options: FactsOptions =
220
253
  above.set(v, { lazy, rules: undefined });
221
254
  return undefined;
222
255
  }
223
- const code = codeOf(world, v.declared ? undefined : v.doc.own, kind, verb, "eval");
224
- const answer = code.name === undefined ? undefined : hook?.(v.typePath ?? [], code.name);
256
+ const { described, answer, rules } = codeFactsOf(machine, v, kind, verb);
257
+ above.set(v, { lazy, rules });
225
258
  let roots: readonly Root[] | undefined;
226
- if (code.described) {
259
+ if (described) {
227
260
  note(answer?.pure ?? kind.pure, answer?.volatile ?? kind.volatile);
228
- above.set(v, { lazy, rules: answer?.lazy ?? kind.lazy });
229
261
  roots = answer?.reads ?? kind.reads;
230
262
  } else {
231
263
  // The contract of the kind does not describe this code. Only the hook can.
232
264
  note(answer?.pure, answer?.volatile);
233
- above.set(v, { lazy, rules: answer?.lazy ?? EACH });
234
265
  roots = answer?.reads;
235
266
  }
236
267
  // The code of the kind resolves each of these roots as a whole, at the place of its document (FACTS.KIND-READS).
@@ -239,13 +270,18 @@ export function factsOf(machine: Machine, doc: unknown, options: FactsOptions =
239
270
  if (node !== undefined && root === "self") continue;
240
271
  add({ root, keys: NONE, exact: true, lazy, address: false, path: [root], place: tight(place.place), at: tight(place.at) });
241
272
  }
273
+ // check and effectsOf read the declared effects with the same function (D27). The verb of the facts selects the
274
+ // declaration: effects for eval, and the entry of verbEffects for another verb (D49, D111).
275
+ const declared = declaredEffects(v, verb);
276
+ // The field side gives the side of eval. For another verb, a declared effect needs a runner, as in check, thus
277
+ // the run side (FACTS.VERB-SIDE).
278
+ const runs = verb === "eval" ? kind.side === "run" : declared.length > 0;
242
279
  // An equation evaluates without capabilities, thus an effect in it never reaches the host. It is still not pure.
243
- if (kind.side === "run") {
280
+ if (runs) {
244
281
  if (node === undefined) side = "run";
245
282
  else pure = false;
246
283
  }
247
- // check and effectsOf read the declared effects with the same function (D27).
248
- for (const keys of declaredEffects(v)) {
284
+ for (const keys of declared) {
249
285
  if (node !== undefined) {
250
286
  pure = false;
251
287
  continue;
package/src/formula.ts CHANGED
@@ -5,7 +5,7 @@ import { type EvalError, type Failed, type Result, Failure, fail, ok, thrown } f
5
5
  import { type Code, type CompileInput, type CompiledEvaluator, type CompiledPart, type Evaluator, type Machine, type Runtime, resolveCompiled, totalCode } from "./evaluate.ts";
6
6
  import { type MachinePart, mergeMachines } from "./merge.ts";
7
7
  import { type Domain, type ValueType, HEADER_KEYS, STD_DOMAINS, callable, fieldOf, fits, fnOf, holdsEnvelope, isPrecise, isUnknown, itemOf, join, member, stepType, typeIssues, typeText } from "./types.ts";
8
- import type { PartType, TypeInput, TypeRule } from "./typing.ts";
8
+ import type { Declared, PartType, TypeInput, TypeRule } from "./typing.ts";
9
9
  import { decodeDomain, domainTypeOf } from "./codec.ts";
10
10
  import { type StaticRead, factsOf } from "./facts.ts";
11
11
 
@@ -235,6 +235,12 @@ function isSignature(value: unknown): value is Signature {
235
235
  return typeof value === "object" && value !== null && Array.isArray((value as { readonly params?: unknown }).params) && Object.hasOwn(value, "to");
236
236
  }
237
237
 
238
+ /** This function tells if two signatures have the same parameters and the same rest parameter. */
239
+ function sameParams(a: Signature, b: Signature): boolean {
240
+ if (!docEquals(a.params, b.params)) return false;
241
+ return a.rest === undefined ? b.rest === undefined : b.rest !== undefined && docEquals(a.rest, b.rest);
242
+ }
243
+
238
244
  /** This function gives the text of a signature, for the reason of an issue. */
239
245
  function signatureText(sig: Signature): string {
240
246
  const params = sig.params.map(typeText);
@@ -325,6 +331,32 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
325
331
  }
326
332
  return { sigs, bad: sigs.length !== raw.length };
327
333
  };
334
+ /**
335
+ * The declarations of the signatures, for `checkWorld` (TYPE.WORLD, D110). Only a record of signatures lists them.
336
+ * An overload with the parameters of an earlier overload is a problem: the earlier one always fits first.
337
+ */
338
+ const sigsDeclared = (): readonly Declared[] => {
339
+ const sigs = profile.sigs;
340
+ if (sigs === undefined || typeof sigs === "function") return [];
341
+ const out: Declared[] = [];
342
+ for (const name of Object.keys(sigs)) {
343
+ const raw: unknown = sigs[name];
344
+ const list: readonly unknown[] = Array.isArray(raw) ? raw : [raw];
345
+ list.forEach((sig, index) => {
346
+ const at: readonly Key[] = Array.isArray(raw) ? ["sigs", name, index] : ["sigs", name];
347
+ if (!isSignature(sig)) {
348
+ out.push({ at, problem: `A signature of ops.${name} is not a record with params and to. The check does not read it.` });
349
+ return;
350
+ }
351
+ sig.params.forEach((param, position) => out.push({ at: [...at, "params", position], type: param, what: `parameter ${position + 1} of a signature of ops.${name}` }));
352
+ if (sig.rest !== undefined) out.push({ at: [...at, "rest"], type: sig.rest, what: `rest parameter of a signature of ops.${name}` });
353
+ out.push({ at: [...at, "to"], type: sig.to, what: `result of a signature of ops.${name}` });
354
+ const earlier = list.slice(0, index).findIndex((other) => isSignature(other) && sameParams(other, sig));
355
+ if (earlier >= 0) out.push({ at, problem: `The overload ${index + 1} of ops.${name} has the parameters of the overload ${earlier + 1}. Thus the check never selects it.` });
356
+ });
357
+ }
358
+ return out;
359
+ };
328
360
  const logic = (word: "and" | "or"): TypeRule => ({
329
361
  part: (input, path) => truthPart(input, `term ${Number(path[0]) + 1} of ${word}`),
330
362
  synth: (input) => {
@@ -456,6 +488,8 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
456
488
  },
457
489
 
458
490
  "formula.if": {
491
+ // The rule of if declares the truth type of the profile, thus one bad truth type gives one issue in checkWorld.
492
+ declared: () => (declared ? [{ at: ["truthType"], type: truth, what: "truth type of the profile" }] : []),
459
493
  part: (input, path) => {
460
494
  if (path[0] === "test") return truthPart(input, "test of if");
461
495
  return isUnknown(input.expected, input.machine.domains) ? {} : { expect: input.expected, what: `${String(path[0])} branch of if` };
@@ -471,6 +505,7 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
471
505
  "formula.or": logic("or"),
472
506
 
473
507
  "formula.op": {
508
+ declared: sigsDeclared,
474
509
  part: (input, path) => {
475
510
  const read = signatures(input);
476
511
  // Only one signature gives the types of the arguments before their check. Overloads select after it.
@@ -510,6 +545,15 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
510
545
  },
511
546
 
512
547
  "formula.ext": {
548
+ declared: () => {
549
+ const types = profile.extTypes;
550
+ const own = profile.ext;
551
+ if (types === undefined || own === undefined) return [];
552
+ // Only the type of an own extension counts. An entry for a standard extension has no effect (D103).
553
+ return Object.keys(types)
554
+ .filter((name) => Object.hasOwn(own, name))
555
+ .map((name) => ({ at: ["extTypes", name], type: types[name], what: `type of ext.${name} in extTypes` }));
556
+ },
513
557
  synth: (input) => {
514
558
  const name = nameOf(input);
515
559
  const own = profile.ext !== undefined && Object.hasOwn(profile.ext, name);
package/src/index.ts CHANGED
@@ -164,9 +164,12 @@ export {
164
164
  run,
165
165
  } from "./evaluate.ts";
166
166
  export {
167
+ type AttributeAnswer,
168
+ type AttributeCell,
167
169
  type AttributeRung,
168
170
  type AttributeTrace,
169
171
  type NearestOptions,
172
+ type TreeOptions,
170
173
  ATTRIBUTE_RUNGS,
171
174
  STD_CAPS,
172
175
  STD_EVALUATORS,
@@ -178,6 +181,7 @@ export {
178
181
  attributeTrace,
179
182
  nearestWalker,
180
183
  placeOf,
184
+ treeNodeEvaluator,
181
185
  } from "./stdlib.ts";
182
186
  export {
183
187
  type Edge,
@@ -208,6 +212,7 @@ export {
208
212
  type WalkOptions,
209
213
  type WalkProblem,
210
214
  type WorldIssue,
215
+ type WorldOptions,
211
216
  check,
212
217
  checkWorld,
213
218
  mapRefs,
@@ -259,7 +264,7 @@ export { type Definitions, type ReferenceOptions, type ReferenceRunOptions, STD_
259
264
  export { type AgreeOptions, type AgreeRunOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, agreeRun, sameValue } from "./agree.ts";
260
265
  export { type ConformOptions, type ConformProbe, type Finding, type Obligation, conforms } from "./conform.ts";
261
266
  export { sameResult } from "./same.ts";
262
- export { type ReadsCheck, type VerifyOptions, verifyReads } from "./reads.ts";
267
+ export { type PrintOptions, type ReadPrints, type ReadsCheck, type VerifyOptions, printReads, verifyReads } from "./reads.ts";
263
268
  export { type Durable, type DurableCounts, type DurableOptions, type Epochs, durable } from "./durable.ts";
264
269
  export {
265
270
  type NodeEdge,
@@ -281,6 +286,15 @@ export {
281
286
  agreeSchedule,
282
287
  } from "./meaning.ts";
283
288
  export { type GraphCycle, type GraphEdge, type GraphNode, type GraphOptions, type NodeGraph, components, cycles, heights, necessary, nodeLayers } from "./graph.ts";
289
+ export {
290
+ type AttributeCycle,
291
+ type AttributeEdge,
292
+ type AttributeGraph,
293
+ type AttributeGraphOptions,
294
+ type AttributeVertex,
295
+ attributeGraph,
296
+ attributeVertexId,
297
+ } from "./instances.ts";
284
298
  export {
285
299
  type Recorder,
286
300
  type ReplayCausePartsJson,
@@ -302,5 +316,5 @@ export {
302
316
  } from "./replay.ts";
303
317
  export { type GenerateOptions, type Random, generate, random, typesOf } from "./generate.ts";
304
318
  export { type Domain, type DomainCodec, type TypeIssue, type ValueType, STD_DOMAINS, fits, join, member, typeIssues, typeText } from "./types.ts";
305
- export { type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE } from "./typing.ts";
319
+ export { type Declared, type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE } from "./typing.ts";
306
320
  export { type SampleOptions, type TypeReading, typeFromSamples, typeFromSchema } from "./providers.ts";