@mark1russell7/coeval 0.12.0 → 0.13.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.12.0",
4
+ "version": "0.13.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/conform.ts CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  type Runtime,
24
24
  type Steps,
25
25
  capKeys,
26
+ intersectCaps,
26
27
  makeContext,
27
28
  permits,
28
29
  rootContext,
@@ -371,8 +372,8 @@ class Shot implements Runtime {
371
372
  /** This function answers an effect as the kernel does: it checks the capability, and it asks the mock. */
372
373
  private answer(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result {
373
374
  const keys = capKeys(kind);
374
- // The kernel keeps only the capabilities that the document permits (H-GRANT).
375
- const caps = ctx.caps.filter((cap) => permits(this.context.caps, cap));
375
+ // The kernel keeps the capabilities that both the context and the document permit (H-GRANT, CAP.MEET).
376
+ const caps = intersectCaps(ctx.caps, this.context.caps);
376
377
  const allowed = this.machine.permits === undefined ? permits(caps, keys) : this.machine.permits(caps, keys, { ...ctx, caps });
377
378
  if (!allowed) return fail("#CAP!", `The context has no capability for the effect ${keys.join(".")}.`, { place });
378
379
  if (this.perform === undefined) return ok(payload);
package/src/evaluate.ts CHANGED
@@ -68,8 +68,9 @@ export function permits(caps: readonly Cap[], kind: Cap): boolean {
68
68
  }
69
69
 
70
70
  /**
71
- * This function narrows a context for a part: the part keeps only the capabilities that the context of its parent
72
- * permits. Thus an evaluator cannot give a part more effects than it has.
71
+ * This function narrows a context for a part. The part keeps the meet `intersectCaps` of its list and the list of its
72
+ * parent (CAP.MEET). Thus an evaluator cannot give a part more effects than it has. A context whose list the parent
73
+ * permits stays the same object.
73
74
  *
74
75
  * @param parent - The context of the parent.
75
76
  * @param next - The context that the evaluator gives the part.
@@ -77,8 +78,8 @@ export function permits(caps: readonly Cap[], kind: Cap): boolean {
77
78
  */
78
79
  export function narrow(parent: Context, next: Context): Context {
79
80
  if (next === parent || next.caps === parent.caps) return next;
80
- const caps = next.caps.filter((cap) => permits(parent.caps, cap));
81
- return caps.length === next.caps.length ? next : { ...next, caps };
81
+ const caps = intersectCaps(next.caps, parent.caps);
82
+ return caps === next.caps ? next : { ...next, caps };
82
83
  }
83
84
 
84
85
  /** No capabilities. One list serves each masked part. */
@@ -144,30 +145,36 @@ const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
144
145
  type Granted = Context & { readonly [GRANT]?: readonly Cap[] };
145
146
 
146
147
  /**
147
- * This function gives a context whose capabilities its grant permits (CAP.NARROW-CODE). A context whose grant is its
148
- * own list of capabilities stays the same object. Each other context keeps only the capabilities that its grant
149
- * permits, and a context without a grant keeps none. The result has the grant of its capabilities.
148
+ * This function gives the capabilities that a context and its grant both permit, the meet `intersectCaps` (CAP.MEET).
149
+ * A context whose grant is its own list gives that list after one comparison. A context without a grant gives none.
150
150
  */
151
- function granted(ctx: Context): Context {
151
+ function grantedCaps(ctx: Context): readonly Cap[] {
152
152
  const grant = (ctx as Granted)[GRANT];
153
- if (grant === ctx.caps) return ctx;
154
- const caps = grant === undefined ? NO_CAPS : ctx.caps.every((cap) => permits(grant, cap)) ? ctx.caps : ctx.caps.filter((cap) => permits(grant, cap));
153
+ if (grant === ctx.caps) return grant;
154
+ return grant === undefined ? NO_CAPS : intersectCaps(ctx.caps, grant);
155
+ }
156
+
157
+ /**
158
+ * This function gives a context whose capabilities its grant permits (CAP.NARROW-CODE, CAP.MEET). A context whose grant
159
+ * is its own list of capabilities stays the same object. Each other context keeps the meet of its list and its grant,
160
+ * and a context without a grant keeps none. The result has the grant of its capabilities.
161
+ */
162
+ function granted(ctx: Context): Context {
163
+ if ((ctx as Granted)[GRANT] === ctx.caps) return ctx;
164
+ const caps = grantedCaps(ctx);
155
165
  const out: Granted = { ...ctx, caps, [GRANT]: caps };
156
166
  return out;
157
167
  }
158
168
 
159
169
  /**
160
- * This function gives the context in which the runtime resolves a path of compiled code (CAP.CAPS-GRANT). A path from
161
- * the root `caps` reads the capabilities that both the context and its grant permit, and a context without a grant
162
- * reads none. Thus a context that compiled code widened gives the capabilities of its document, and a context that it
163
- * narrowed keeps its own list. Each other path, and each context whose grant is its own list, keeps the same context.
170
+ * This function gives the context in which the runtime resolves a path of compiled code (CAP.CAPS-GRANT, CAP.MEET).
171
+ * The context has the meet of its list and its grant, and a context without a grant has none. Thus a read of the root
172
+ * `caps` and a walker of a step document get the capabilities of the document from a widened context.
173
+ * A context that the code narrowed keeps its own list. A context whose list does not change stays the same object.
164
174
  */
165
- function readable(ctx: Context, path: readonly unknown[]): Context {
166
- if (path[0] !== "caps") return ctx;
167
- const grant = (ctx as Granted)[GRANT];
168
- if (grant === ctx.caps) return ctx;
169
- const caps = grant === undefined ? NO_CAPS : intersectCaps(ctx.caps, grant);
170
- if (caps === ctx.caps) return ctx;
175
+ function readable(ctx: Context): Context {
176
+ const caps = grantedCaps(ctx);
177
+ if (caps === ctx.caps || (caps.length === 0 && ctx.caps.length === 0)) return ctx;
171
178
  const out: Granted = { ...ctx, caps, [GRANT]: caps };
172
179
  return out;
173
180
  }
@@ -912,18 +919,21 @@ class Session implements Runtime {
912
919
  return this.reading !== undefined;
913
920
  }
914
921
 
915
- /** This function resolves a path. A path from the root `caps` reads the capabilities through the grant (CAP.CAPS-GRANT). */
922
+ /**
923
+ * This function resolves a path in the context that the grant permits (CAP.CAPS-GRANT, CAP.MEET). Thus the root
924
+ * `caps` and a walker of a step document get the capabilities of the document.
925
+ */
916
926
  resolve(ctx: Context, path: readonly unknown[], options?: ResolveOptions): Resolution {
917
- return this.read(readable(ctx, path), path, undefined, options);
927
+ return this.read(readable(ctx), path, undefined, options);
918
928
  }
919
929
 
920
930
  /**
921
931
  * This function resolves a compiled path (PLAN.PATH). Without a list of reads and without a trace, the compiled
922
- * path answers at once. Each other resolution records its read, as `resolve` does. A path from the root `caps` reads
923
- * the capabilities through the grant (CAP.CAPS-GRANT).
932
+ * path answers at once. Each other resolution records its read, as `resolve` does. The path resolves in the context
933
+ * that the grant permits (CAP.CAPS-GRANT, CAP.MEET).
924
934
  */
925
935
  resolvePath(given: Context, compiled: CompiledPath): Resolution {
926
- const ctx = readable(given, compiled.path);
936
+ const ctx = readable(given);
927
937
  if (this.reading === undefined && !this.traced && compiled.nav === this.machine) return compiled.resolve(ctx);
928
938
  return this.read(ctx, compiled.path, compiled, undefined);
929
939
  }
package/src/reference.ts CHANGED
@@ -171,11 +171,13 @@ function capsOfPart(rules: readonly CapsRule[] | undefined, path: readonly Key[]
171
171
  }
172
172
 
173
173
  /**
174
- * This function gives the context of a part. A context that the evaluator gives keeps only the capabilities of the
175
- * parent (CAP.NARROW). Then the rules of the kind narrow it (CAP.CONTRACT).
174
+ * This function gives the context of a part. A context that the evaluator gives keeps the effects that both it and
175
+ * the parent permit (CAP.NARROW, CAP.MEET). A list that the parent permits stays as it is. Then the rules of the kind
176
+ * narrow it (CAP.CONTRACT).
176
177
  */
177
178
  function contextOfPart(seen: Seen, slot: PartSlot, parent: Context, given: Context | undefined): Context {
178
- const base = given === undefined || given === parent ? parent : { ...given, caps: given.caps.filter((cap) => permits(parent.caps, cap)) };
179
+ const base =
180
+ given === undefined || given === parent ? parent : given.caps.every((cap) => permits(parent.caps, cap)) ? given : { ...given, caps: bothPermit(parent.caps, given.caps) };
179
181
  const caps = capsOfPart(seen.kind?.caps, slot.path, base.caps);
180
182
  return caps === base.caps ? base : { ...base, caps };
181
183
  }