@mark1russell7/coeval 0.13.0 → 0.14.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.13.0",
4
+ "version": "0.14.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/evaluate.ts CHANGED
@@ -141,8 +141,15 @@ export function partCaps(rules: readonly CapsRule[] | undefined, path: readonly
141
141
  */
142
142
  const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
143
143
 
144
- /** A context with the grant of the kernel. */
145
- type Granted = Context & { readonly [GRANT]?: readonly Cap[] };
144
+ /**
145
+ * The key of the epoch of a grant (CAP.NARROW-KEPT). The epoch is the list of capabilities that the host gave the root
146
+ * of the evaluation of the grant. A spread of a context keeps its epoch, as it keeps its grant. An
147
+ * effect and a resolution read a context of another epoch as a context without a grant.
148
+ */
149
+ const EPOCH = /* @__PURE__ */ Symbol("coeval.epoch");
150
+
151
+ /** A context with the grant of the kernel and the epoch of that grant. */
152
+ type Granted = Context & { readonly [GRANT]?: readonly Cap[]; readonly [EPOCH]?: readonly Cap[] | undefined };
146
153
 
147
154
  /**
148
155
  * This function gives the capabilities that a context and its grant both permit, the meet `intersectCaps` (CAP.MEET).
@@ -166,13 +173,25 @@ function granted(ctx: Context): Context {
166
173
  return out;
167
174
  }
168
175
 
176
+ /**
177
+ * This function gives a context of another epoch the epoch of the evaluation, without capabilities (CAP.NARROW-KEPT).
178
+ * Thus the context reads as a context without a grant. The kernel cannot know the document of the context in this
179
+ * evaluation.
180
+ */
181
+ function ungranted(ctx: Context, epoch: readonly Cap[]): Context {
182
+ const out: Granted = { ...ctx, caps: NO_CAPS, [GRANT]: NO_CAPS, [EPOCH]: epoch };
183
+ return out;
184
+ }
185
+
169
186
  /**
170
187
  * This function gives the context in which the runtime resolves a path of compiled code (CAP.CAPS-GRANT, CAP.MEET).
171
188
  * 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
189
  * `caps` and a walker of a step document get the capabilities of the document from a widened context.
173
190
  * A context that the code narrowed keeps its own list. A context whose list does not change stays the same object.
191
+ * A context of another epoch has no capabilities (CAP.NARROW-KEPT).
174
192
  */
175
- function readable(ctx: Context): Context {
193
+ function readable(ctx: Context, epoch: readonly Cap[]): Context {
194
+ if ((ctx as Granted)[EPOCH] !== epoch) return ctx.caps.length === 0 ? ctx : ungranted(ctx, epoch);
176
195
  const caps = grantedCaps(ctx);
177
196
  if (caps === ctx.caps || (caps.length === 0 && ctx.caps.length === 0)) return ctx;
178
197
  const out: Granted = { ...ctx, caps, [GRANT]: caps };
@@ -181,12 +200,14 @@ function readable(ctx: Context): Context {
181
200
 
182
201
  /**
183
202
  * This function gives the context of a part of a function evaluator. It is the context that the evaluator gives,
184
- * narrowed by the context of the document (CAP.NARROW), with the grant of its capabilities.
203
+ * narrowed by the context of the document (CAP.NARROW), with the grant of its capabilities. The grant has the epoch
204
+ * of the document, because the document permits each of its capabilities (CAP.NARROW-KEPT).
185
205
  */
186
206
  function handed(parent: Context, next: Context): Context {
187
207
  const ctx = narrow(parent, next);
188
- if ((ctx as Granted)[GRANT] === ctx.caps) return ctx;
189
- const out: Granted = { ...ctx, [GRANT]: ctx.caps };
208
+ const epoch = (parent as Granted)[EPOCH];
209
+ if ((ctx as Granted)[GRANT] === ctx.caps && (ctx as Granted)[EPOCH] === epoch) return ctx;
210
+ const out: Granted = { ...ctx, [GRANT]: ctx.caps, [EPOCH]: epoch };
190
211
  return out;
191
212
  }
192
213
 
@@ -896,6 +917,11 @@ class Session implements Runtime {
896
917
 
897
918
  constructor(
898
919
  readonly machine: Machine,
920
+ /**
921
+ * The epoch of the evaluation: the list of capabilities that the host gave the root (CAP.NARROW-KEPT). A context
922
+ * keeps its grant only in an evaluation of the same epoch.
923
+ */
924
+ readonly epoch: readonly Cap[],
899
925
  readonly running: boolean,
900
926
  readonly traced: boolean,
901
927
  readonly perform: Perform | undefined,
@@ -924,7 +950,7 @@ class Session implements Runtime {
924
950
  * `caps` and a walker of a step document get the capabilities of the document.
925
951
  */
926
952
  resolve(ctx: Context, path: readonly unknown[], options?: ResolveOptions): Resolution {
927
- return this.read(readable(ctx), path, undefined, options);
953
+ return this.read(readable(ctx, this.epoch), path, undefined, options);
928
954
  }
929
955
 
930
956
  /**
@@ -933,7 +959,7 @@ class Session implements Runtime {
933
959
  * that the grant permits (CAP.CAPS-GRANT, CAP.MEET).
934
960
  */
935
961
  resolvePath(given: Context, compiled: CompiledPath): Resolution {
936
- const ctx = readable(given);
962
+ const ctx = readable(given, this.epoch);
937
963
  if (this.reading === undefined && !this.traced && compiled.nav === this.machine) return compiled.resolve(ctx);
938
964
  return this.read(ctx, compiled.path, compiled, undefined);
939
965
  }
@@ -995,10 +1021,11 @@ class Session implements Runtime {
995
1021
  /**
996
1022
  * This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). The
997
1023
  * context keeps only the capabilities of its grant, thus compiled code cannot widen the context of its own effect
998
- * (CAP.NARROW-CODE).
1024
+ * (CAP.NARROW-CODE). A context of another epoch has no capabilities, thus code cannot keep a context for a later
1025
+ * evaluation with fewer capabilities (CAP.NARROW-KEPT).
999
1026
  */
1000
1027
  private record(given: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result | Recorded {
1001
- const ctx = granted(given);
1028
+ const ctx = (given as Granted)[EPOCH] === this.epoch ? granted(given) : ungranted(given, this.epoch);
1002
1029
  const keys = typeof kind === "string" ? kind.split(".") : kind;
1003
1030
  const text = keys.join(".");
1004
1031
  if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
@@ -1921,15 +1948,16 @@ function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonl
1921
1948
  /**
1922
1949
  * This function gives the context at the root of an evaluation. It is the context of the host, with the world of
1923
1950
  * the machine when the context has none. The host gives the capabilities, thus the root has the grant of each one
1924
- * (CAP.NARROW-CODE). An evaluation and `verifyReads` share it.
1951
+ * (CAP.NARROW-CODE). The list of the host is also the epoch of the grant (CAP.NARROW-KEPT). An evaluation and
1952
+ * `verifyReads` share it.
1925
1953
  *
1926
1954
  * @param machine - The machine.
1927
1955
  * @param ctx - The context of the host.
1928
1956
  * @returns The context at the root.
1929
1957
  */
1930
1958
  export function rootContext(machine: Machine, ctx: Context): Context {
1931
- if (ctx.world !== null && (ctx as Granted)[GRANT] === ctx.caps) return ctx;
1932
- const root: Granted = { ...ctx, world: ctx.world === null ? worldThing(machine.world) : ctx.world, [GRANT]: ctx.caps };
1959
+ if (ctx.world !== null && (ctx as Granted)[GRANT] === ctx.caps && (ctx as Granted)[EPOCH] === ctx.caps) return ctx;
1960
+ const root: Granted = { ...ctx, world: ctx.world === null ? worldThing(machine.world) : ctx.world, [GRANT]: ctx.caps, [EPOCH]: ctx.caps };
1933
1961
  return root;
1934
1962
  }
1935
1963
 
@@ -2000,14 +2028,14 @@ class CompiledPlan implements Plan {
2000
2028
  evaluate(context: Context = makeContext(), evalOptions?: EvalOptions): Result {
2001
2029
  const machine = this.machine;
2002
2030
  const ctx = rootContext(machine, context);
2003
- const s = new Session(machine, false, this.traced, evalOptions?.perform, undefined, evalOptions?.effects, readingOf(evalOptions?.reads, ctx));
2031
+ const s = new Session(machine, ctx.caps, false, this.traced, evalOptions?.perform, undefined, evalOptions?.effects, readingOf(evalOptions?.reads, ctx));
2004
2032
  return this.code(ctx, s);
2005
2033
  }
2006
2034
 
2007
2035
  run(context: Context = makeContext(), runOptions: PlanRunOptions = {}): Promise<Result> {
2008
2036
  const machine = this.machine;
2009
2037
  const ctx = rootContext(machine, context);
2010
- const s = new Session(machine, true, this.traced, runOptions.perform, runOptions.signal, undefined, readingOf(runOptions.reads, ctx));
2038
+ const s = new Session(machine, ctx.caps, true, this.traced, runOptions.perform, runOptions.signal, undefined, readingOf(runOptions.reads, ctx));
2011
2039
  return driveWaiting(this.runCode()(ctx, s), runOptions.signal);
2012
2040
  }
2013
2041
  }
@@ -2075,7 +2103,7 @@ export function evaluate(machine: Machine, doc: unknown, context: Context = make
2075
2103
  ...(given.verb === undefined ? {} : { verb: given.verb }),
2076
2104
  });
2077
2105
  const ctx = rootContext(machine, context);
2078
- const s = new Session(machine, false, true, given.perform, undefined, undefined, readingOf(given.reads, ctx));
2106
+ const s = new Session(machine, ctx.caps, false, true, given.perform, undefined, undefined, readingOf(given.reads, ctx));
2079
2107
  const result = (plan as CompiledPlan).code(ctx, s);
2080
2108
  return record(s, result);
2081
2109
  }
@@ -2098,7 +2126,7 @@ export async function run(machine: Machine, doc: unknown, context: Context = mak
2098
2126
  ...(options.verb === undefined ? {} : { verb: options.verb }),
2099
2127
  });
2100
2128
  const ctx = rootContext(machine, context);
2101
- const s = new Session(machine, true, true, options.perform, options.signal, undefined, readingOf(options.reads, ctx));
2129
+ const s = new Session(machine, ctx.caps, true, true, options.perform, options.signal, undefined, readingOf(options.reads, ctx));
2102
2130
  const result = await driveWaiting((plan as CompiledPlan).runCode()(ctx, s), options.signal);
2103
2131
  return record(s, result);
2104
2132
  }