@mark1russell7/coeval 0.2.0 → 0.2.1

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.2.0",
4
+ "version": "0.2.1",
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/deref.ts CHANGED
@@ -112,6 +112,8 @@ export type Walker = (input: WalkInput) => WalkResult;
112
112
  * - `plain`: `true` when the values are data. Then a step does not ask the kind of an envelope, and a reference
113
113
  * that a step reaches stays a value (no collapse).
114
114
  * - `roots`: the roots that a path can start at. The default is each root.
115
+ * - `adopt`: the thing of each value that a path reaches. A host gives its own values a host thing with this
116
+ * function, for example a value whose fields read through signals. A host thing is not followed as a reference.
115
117
  */
116
118
  export interface Navigator {
117
119
  readonly world: World;
@@ -119,6 +121,7 @@ export interface Navigator {
119
121
  readonly walkers?: Readonly<Record<string, Walker>>;
120
122
  readonly plain?: boolean;
121
123
  readonly roots?: readonly Root[];
124
+ readonly adopt?: (value: unknown) => Thing;
122
125
  }
123
126
 
124
127
  /** The way that one hop of a path was answered. */
@@ -265,6 +268,7 @@ function resolveQuick(nav: Navigator, ctx: Context, path: readonly unknown[]): R
265
268
  const root = path[0];
266
269
  if (root !== "names" && root !== "self" && root !== "event") return undefined;
267
270
  if (nav.roots !== undefined && !(nav.roots as readonly string[]).includes(root)) return undefined;
271
+ if (nav.adopt !== undefined) return undefined;
268
272
  let here: unknown = ctx[root];
269
273
  const plain = nav.plain === true;
270
274
  for (let index = 1; index < path.length; index += 1) {
@@ -316,6 +320,7 @@ export function resolve(nav: Navigator, ctx: Context, path: readonly unknown[],
316
320
  const done = hops ?? NO_HOPS;
317
321
  const failed = (code: ResolveCode, reason: string): Resolution => ({ ok: false, code, reason, hops: done });
318
322
  let collapses = 0;
323
+ const adopt = nav.adopt ?? ((value: unknown): Thing => value);
319
324
 
320
325
  const collapse = (thing: Thing): Resolution => {
321
326
  let here = thing;
@@ -325,7 +330,7 @@ export function resolve(nav: Navigator, ctx: Context, path: readonly unknown[],
325
330
  if (collapses > MAX_COLLAPSE) return failed("#CYCLE!", "The references form a cycle.");
326
331
  const inner = walk(here.value);
327
332
  if (!inner.ok) return inner;
328
- here = inner.thing;
333
+ here = adopt(inner.thing);
329
334
  hops?.push({ step: "collapse", thing: here, by: "collapse" });
330
335
  }
331
336
  return { ok: true, thing: here, hops: done };
@@ -339,7 +344,7 @@ export function resolve(nav: Navigator, ctx: Context, path: readonly unknown[],
339
344
  if (nav.roots !== undefined && !(nav.roots as readonly string[]).includes(root)) {
340
345
  return failed("#REF!", `The root ${root} is not open here. The open roots are ${nav.roots.join(", ")}.`);
341
346
  }
342
- let here: Thing = ctx[root as Root];
347
+ let here: Thing = adopt(ctx[root as Root]);
343
348
  hops?.push({ step: root, thing: here, by: "root" });
344
349
  const first = collapse(here);
345
350
  if (!first.ok) return first;
@@ -350,8 +355,9 @@ export function resolve(nav: Navigator, ctx: Context, path: readonly unknown[],
350
355
  const keys = steps.slice(index) as readonly Key[];
351
356
  const reached = here.resolve(keys);
352
357
  if (reached === undefined) return failed("#N/A", `The path ${keys.join(".")} reaches nothing.`);
353
- hops?.push({ step: keys as readonly Json[], thing: reached.thing, by: "own" });
354
- return collapse(reached.thing);
358
+ const thing = adopt(reached.thing);
359
+ hops?.push({ step: keys as readonly Json[], thing, by: "own" });
360
+ return collapse(thing);
355
361
  }
356
362
  if (isKey(step)) {
357
363
  const reached = derefStep(nav, here, step);
@@ -359,8 +365,8 @@ export function resolve(nav: Navigator, ctx: Context, path: readonly unknown[],
359
365
  if (root === "names" && index === 1) return failed("#NAME?", `The context has no name ${String(step)}.`);
360
366
  return failed("#N/A", `The step ${String(step)} reaches nothing.`);
361
367
  }
362
- hops?.push({ step, thing: reached.thing, by: reached.by });
363
- here = reached.thing;
368
+ here = adopt(reached.thing);
369
+ hops?.push({ step, thing: here, by: reached.by });
364
370
  } else {
365
371
  const envelope = isEnvelope(step) ? step : undefined;
366
372
  const found = envelope === undefined ? undefined : lookupKind(nav.world, envelope.type);
@@ -373,11 +379,14 @@ export function resolve(nav: Navigator, ctx: Context, path: readonly unknown[],
373
379
  data: envelope.value,
374
380
  here,
375
381
  context: ctx,
376
- deref: (thing, key) => derefStep(nav, thing, key)?.thing,
382
+ deref: (thing, key) => {
383
+ const reached = derefStep(nav, thing, key);
384
+ return reached === undefined ? undefined : adopt(reached.thing);
385
+ },
377
386
  });
378
387
  if (!result.ok) return failed("#N/A", result.reason);
379
- hops?.push({ step: envelope as Json, thing: result.thing, by: "step" });
380
- here = result.thing;
388
+ here = adopt(result.thing);
389
+ hops?.push({ step: envelope as Json, thing: here, by: "step" });
381
390
  }
382
391
  const next = collapse(here);
383
392
  if (!next.ok) return next;