@procedures/client 0.24.1 → 0.26.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/CHANGELOG.md +93 -0
- package/README.md +15 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/server/handler.js +7 -2
- package/dist/adapters/http/server/handler.js.map +1 -1
- package/dist/adapters/http/server/ndjson.d.ts +10 -13
- package/dist/adapters/http/server/ndjson.d.ts.map +1 -1
- package/dist/adapters/http/server/ndjson.js +12 -13
- package/dist/adapters/http/server/ndjson.js.map +1 -1
- package/dist/adapters/websocket/shared/rpc.d.ts +5 -8
- package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.js +11 -14
- package/dist/adapters/websocket/shared/rpc.js.map +1 -1
- package/dist/browser.d.ts +3 -3
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -1
- package/dist/browser.js.map +1 -1
- package/dist/client/client.d.ts +7 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +30 -19
- package/dist/client/client.js.map +1 -1
- package/dist/client/middleware/retry.d.ts.map +1 -1
- package/dist/client/middleware/retry.js +3 -1
- package/dist/client/middleware/retry.js.map +1 -1
- package/dist/procedures/core/index.d.ts +19 -3
- package/dist/procedures/core/index.d.ts.map +1 -1
- package/dist/procedures/core/index.js +53 -178
- package/dist/procedures/core/index.js.map +1 -1
- package/dist/procedures/core/operands.d.ts +12 -13
- package/dist/procedures/core/operands.d.ts.map +1 -1
- package/dist/procedures/core/operands.js +22 -38
- package/dist/procedures/core/operands.js.map +1 -1
- package/dist/procedures/define-procedure.d.ts.map +1 -1
- package/dist/procedures/define-procedure.js +6 -6
- package/dist/procedures/define-procedure.js.map +1 -1
- package/dist/procedures/envelope.d.ts +24 -8
- package/dist/procedures/envelope.d.ts.map +1 -1
- package/dist/procedures/envelope.js +51 -20
- package/dist/procedures/envelope.js.map +1 -1
- package/dist/procedures/idempotency.d.ts +5 -0
- package/dist/procedures/idempotency.d.ts.map +1 -1
- package/dist/procedures/idempotency.js +8 -0
- package/dist/procedures/idempotency.js.map +1 -1
- package/dist/procedures/index.d.ts +3 -1
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +3 -1
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/program.d.ts +251 -0
- package/dist/procedures/program.d.ts.map +1 -0
- package/dist/procedures/program.js +1120 -0
- package/dist/procedures/program.js.map +1 -0
- package/dist/procedures/ref.d.ts +8 -91
- package/dist/procedures/ref.d.ts.map +1 -1
- package/dist/procedures/ref.js +16 -279
- package/dist/procedures/ref.js.map +1 -1
- package/dist/protocol.d.ts +12 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +20 -0
- package/dist/protocol.js.map +1 -1
- package/dist/server/procedure-server.d.ts +3 -6
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +33 -14
- package/dist/server/procedure-server.js.map +1 -1
- package/package.json +2 -1
- package/src/adapters/http/server/handler.ts +8 -2
- package/src/adapters/http/server/ndjson.ts +17 -18
- package/src/adapters/websocket/shared/rpc.ts +11 -14
- package/src/browser.ts +25 -1
- package/src/client/client.ts +35 -23
- package/src/client/middleware/retry.ts +3 -1
- package/src/procedures/core/index.ts +68 -212
- package/src/procedures/core/operands.ts +24 -44
- package/src/procedures/define-procedure.ts +5 -7
- package/src/procedures/envelope.ts +64 -21
- package/src/procedures/idempotency.ts +8 -0
- package/src/procedures/index.ts +34 -4
- package/src/procedures/program.ts +1371 -0
- package/src/procedures/ref.ts +20 -354
- package/src/protocol.ts +19 -0
- package/src/server/procedure-server.ts +32 -13
|
@@ -7,25 +7,22 @@ import type { ProcedureContext, ProcedurePath } from "../types.js";
|
|
|
7
7
|
import { PROCEDURE_REGISTRY } from "../registry.js";
|
|
8
8
|
import {
|
|
9
9
|
isAnyProcedureRef,
|
|
10
|
-
getRefWhen,
|
|
11
|
-
WHEN_NEVER,
|
|
12
|
-
normalizeRef,
|
|
13
|
-
hydrateInput,
|
|
14
|
-
createRefScope,
|
|
15
10
|
isOutputRef,
|
|
16
11
|
lookupOutputRef,
|
|
17
12
|
rawInputRule,
|
|
18
|
-
|
|
13
|
+
scopeOf,
|
|
19
14
|
RUNS_REFS_TAG,
|
|
20
15
|
type AnyProcedureRef,
|
|
21
16
|
type RefScope,
|
|
22
17
|
} from "../ref.js";
|
|
18
|
+
import { hydrateInput, runControl, runOperand } from "../program.js";
|
|
23
19
|
|
|
24
20
|
/**
|
|
25
|
-
* The operands of a control-flow procedure arrive raw: the procedure runs them itself
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* chain, `map` or `reduce` (deep dive
|
|
21
|
+
* The operands of a control-flow procedure arrive raw: the procedure runs them itself. Its logic
|
|
22
|
+
* is a runner of the coeval kernel (`program.ts`): the handler gives its input to the kernel as a
|
|
23
|
+
* document of its kind. The input of a control-flow procedure carries the scope of its caller
|
|
24
|
+
* (`withScope`), so an operand can read the names of an outer chain, `map` or `reduce` (deep dive
|
|
25
|
+
* CORE-13).
|
|
29
26
|
*/
|
|
30
27
|
|
|
31
28
|
/** The raw-input rule of the caller's registry: the procedure at a path decides (deep dive CORE-5). */
|
|
@@ -33,6 +30,19 @@ export function rawRule(ctx: ProcedureContext): (path: ProcedurePath) => boolean
|
|
|
33
30
|
return rawInputRule(ctx.registry ?? PROCEDURE_REGISTRY);
|
|
34
31
|
}
|
|
35
32
|
|
|
33
|
+
/** The call function of a context, as the executor of a run. */
|
|
34
|
+
function executorOf(ctx: ProcedureContext): <TIn, TOut>(path: ProcedurePath, input: TIn) => Promise<TOut> {
|
|
35
|
+
return <TIn, TOut>(path: ProcedurePath, input: TIn): Promise<TOut> => ctx.client.call<TIn, TOut>(path, input);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* This function runs a control-flow procedure on the kernel: the runner of its kind runs the
|
|
40
|
+
* operands of the input, in the scope of the caller.
|
|
41
|
+
*/
|
|
42
|
+
export function runControlFlow(name: string, input: unknown, ctx: ProcedureContext): Promise<unknown> {
|
|
43
|
+
return runControl(name, input, { executor: executorOf(ctx), rawInput: rawRule(ctx), onRef: ctx.onRef, scope: scopeOf(input) });
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
/**
|
|
37
47
|
* Without a client, a control-flow procedure cannot run refs. This function gives a value with
|
|
38
48
|
* its known `$ref` values resolved. A name that the scope does not have stays as it is.
|
|
@@ -59,38 +69,23 @@ export function substituteRefs(value: unknown, scope: RefScope): unknown {
|
|
|
59
69
|
* in `scope` and runs its nested refs, as `exec()` does for an input.
|
|
60
70
|
*/
|
|
61
71
|
export async function hydrateOperand(value: unknown, ctx: ProcedureContext, scope?: RefScope): Promise<unknown> {
|
|
62
|
-
return hydrateInput(value, (
|
|
72
|
+
return hydrateInput(value, executorOf(ctx), { scope, rawInput: rawRule(ctx), onRef: ctx.onRef });
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
/**
|
|
66
|
-
* This function runs an operand ref of a control-flow procedure.
|
|
76
|
+
* This function runs an operand ref of a control-flow procedure (`runOperand()`).
|
|
67
77
|
*
|
|
68
78
|
* It resolves the `$ref` values of the ref's input in `scope`, runs the nested refs of the
|
|
69
79
|
* input and adds `extra` (the parent context, for example `cwd`). Then it calls the procedure.
|
|
70
80
|
* A procedure that takes its input raw (control flow) gets the input unchanged, with the scope:
|
|
71
|
-
* that procedure runs its own operands.
|
|
81
|
+
* that procedure runs its own operands. A `$never` operand is data with its full input.
|
|
72
82
|
*/
|
|
73
83
|
export async function callOperand(
|
|
74
84
|
ref: AnyProcedureRef,
|
|
75
85
|
ctx: ProcedureContext,
|
|
76
86
|
options: { scope?: RefScope | undefined; extra?: Record<string, unknown> | undefined } = {},
|
|
77
87
|
): Promise<unknown> {
|
|
78
|
-
|
|
79
|
-
// only a chain step obeyed it, and a $never branch, task or fn started)
|
|
80
|
-
if (getRefWhen(ref) === WHEN_NEVER) {
|
|
81
|
-
return ref;
|
|
82
|
-
}
|
|
83
|
-
const normalized = normalizeRef(ref);
|
|
84
|
-
const raw = rawRule(ctx)(normalized.path);
|
|
85
|
-
let input: unknown = raw ? normalized.input : await hydrateOperand(normalized.input, ctx, options.scope);
|
|
86
|
-
const extra = options.extra ?? {};
|
|
87
|
-
if (Object.keys(extra).length > 0) {
|
|
88
|
-
input = { ...(typeof input === "object" && input !== null && !Array.isArray(input) ? input : {}), ...extra };
|
|
89
|
-
}
|
|
90
|
-
if (raw) {
|
|
91
|
-
input = withScope(input, options.scope);
|
|
92
|
-
}
|
|
93
|
-
return ctx.client.call(normalized.path, input);
|
|
88
|
+
return runOperand(ref, { executor: executorOf(ctx), rawInput: rawRule(ctx), onRef: ctx.onRef, scope: options.scope, extra: options.extra });
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
/** This function gives the value of any operand: a ref runs, another value is hydrated. */
|
|
@@ -105,21 +100,6 @@ export async function evaluateOperand(
|
|
|
105
100
|
return hydrateOperand(value, ctx, options.scope);
|
|
106
101
|
}
|
|
107
102
|
|
|
108
|
-
/** The parent context that the control-flow procedures give to their operands. */
|
|
109
|
-
export function parentContext(input: { cwd?: string | undefined; node?: unknown }): Record<string, unknown> {
|
|
110
|
-
return {
|
|
111
|
-
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
112
|
-
...(input.node ? { node: input.node } : {}),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** A child scope of `parent` with the given `$ref` names, for one call of `fn`. */
|
|
117
|
-
export function scopeWith(values: Record<string, unknown>, parent?: RefScope): RefScope {
|
|
118
|
-
const scope = createRefScope(parent);
|
|
119
|
-
for (const [name, value] of Object.entries(values)) scope.outputs.set(name, value);
|
|
120
|
-
return scope;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
103
|
/** The tags of a control-flow procedure: hydration gives it its input raw. */
|
|
124
104
|
export function controlFlowTags(...tags: string[]): string[] {
|
|
125
105
|
return ["core", ...tags, RUNS_REFS_TAG];
|
|
@@ -41,13 +41,12 @@ import { anySchema, zodAdapter } from "./core/schemas.js";
|
|
|
41
41
|
import { keyToPath, pathToKey } from "./types.js";
|
|
42
42
|
import {
|
|
43
43
|
createRefScope,
|
|
44
|
-
hydrateInput,
|
|
45
44
|
isAnyProcedureRef,
|
|
46
45
|
markDataDriven,
|
|
47
|
-
prepareRootCall,
|
|
48
46
|
rawInputRule,
|
|
49
47
|
RUNS_REFS_TAG,
|
|
50
48
|
} from "./ref.js";
|
|
49
|
+
import { hydrateInput, runProgram } from "./program.js";
|
|
51
50
|
import { INFERRED_EFFECTS_METADATA_KEY, PURE_EFFECTS, REGISTRY_READ_EFFECTS, effectsValue, withEffects } from "./effects.js";
|
|
52
51
|
import { MANIFEST_FORMAT, describeProcedure as describeEntry, describeRegistry, type ProcedureManifest } from "./manifest.js";
|
|
53
52
|
import type { JsonSchema } from "./schema.js";
|
|
@@ -192,9 +191,9 @@ const DeleteOutputSchema = z.looseObject({
|
|
|
192
191
|
/**
|
|
193
192
|
* Create a handler that executes an aggregation definition.
|
|
194
193
|
*
|
|
195
|
-
* The body runs exactly as `exec()` runs the same program: `
|
|
196
|
-
*
|
|
197
|
-
* the root input is an implicit chain)
|
|
194
|
+
* The body runs exactly as `exec()` runs the same program: one run of the kernel (`runProgram()`),
|
|
195
|
+
* whose root document is the root call (a `$when` on the root call does not apply, a list of calls
|
|
196
|
+
* in the root input is an implicit chain). The scope of the
|
|
198
197
|
* body has one name, `input`: the input of the procedure. So `{ $ref: "input.dir" }` reads a
|
|
199
198
|
* field of the input, and `$last`, the step names, `item`, `acc` and `index` keep their meaning
|
|
200
199
|
* inside the body. (Deep dive CORE-4: before, a first pass replaced every `$ref` with a field of
|
|
@@ -213,8 +212,7 @@ function createAggregationHandler(
|
|
|
213
212
|
const executor = <TIn, TOut>(path: ProcedurePath, value: TIn): Promise<TOut> => ctx.client.call<TIn, TOut>(path, value);
|
|
214
213
|
const options = { scope, rawInput: rawInputRule(ctx.registry ?? PROCEDURE_REGISTRY), onRef: ctx.onRef };
|
|
215
214
|
if (isAnyProcedureRef(aggregation)) {
|
|
216
|
-
|
|
217
|
-
return ctx.client.call(root.path, root.input);
|
|
215
|
+
return runProgram(aggregation, { ...options, executor });
|
|
218
216
|
}
|
|
219
217
|
// A body that is not one call (an older stored program): hydrate it as a root value
|
|
220
218
|
return hydrateInput(aggregation, executor, options);
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The envelope form of a program (coeval, decisions D6, D9 and
|
|
2
|
+
* The envelope form of a program (coeval, decisions D6, D9, D10 and D12, roadmap item M7).
|
|
3
3
|
*
|
|
4
4
|
* A document of coeval is an envelope: `{ type, value }`. The header can also have `name` and
|
|
5
5
|
* `own`. In the envelope form of a program:
|
|
6
6
|
*
|
|
7
7
|
* - A call is `{ type: path, value: input }`. The type is the path of a procedure, as an array of
|
|
8
8
|
* keys. A ref into the world, `{ type: "ref", value: ["world", ...path] }`, is also a type.
|
|
9
|
+
* The arrays `["ref"]` and `["lit"]` are kinds of the kernel, not paths. A procedure with the
|
|
10
|
+
* path `["ref"]` or `["lit"]` has a ref into the world as its type.
|
|
9
11
|
* - A ref is `{ type: "ref", value: ["names", name, ...fields] }`. The name `last` reads the result
|
|
10
12
|
* of the step before.
|
|
11
|
-
* - A literal is `{ type: "literal", value }`. Nothing in its value runs.
|
|
13
|
+
* - A literal is `{ type: "literal", value }`. Nothing in its value runs. The type `"lit"` (the kind
|
|
14
|
+
* of the kernel, coeval D12) is an alias: `{ type: "lit", value }` is also a literal.
|
|
12
15
|
*
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* The functions of this module read the envelopes with the helpers of `@mark1russell7/coeval`
|
|
17
|
+
* (`isTypeRef`, `typePath`, `ref`). `fromEnvelopeProgram()` changes the envelope form into the
|
|
18
|
+
* `$proc` form, and `client.exec()` then runs the program on the kernel (`program.ts`). In the
|
|
19
|
+
* input of a `$proc` program, an envelope is data. Thus the root of a program decides its form,
|
|
20
|
+
* and the old programs do not change.
|
|
16
21
|
*/
|
|
17
22
|
|
|
23
|
+
import { ENVELOPE_KEYS, isTypeRef, ref as refDocument, typePath } from "@mark1russell7/coeval";
|
|
18
24
|
import {
|
|
19
25
|
LITERAL_KEY,
|
|
20
26
|
MAX_DATA_DEPTH,
|
|
@@ -50,7 +56,10 @@ export interface EnvelopeRef {
|
|
|
50
56
|
readonly value: EnvelopePath;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* This document gives its value as data. Nothing in the value runs. The functions of this module
|
|
61
|
+
* write the type `"literal"`, and they also read the alias `"lit"` (coeval D12: the kind `lit`).
|
|
62
|
+
*/
|
|
54
63
|
export interface EnvelopeLiteral {
|
|
55
64
|
readonly type: "literal";
|
|
56
65
|
readonly value: unknown;
|
|
@@ -61,7 +70,14 @@ export interface EnvelopeLiteral {
|
|
|
61
70
|
* procedure, or a ref into the world. The value is the input. A value of `null` gives no input.
|
|
62
71
|
*/
|
|
63
72
|
export interface EnvelopeProgram {
|
|
64
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* The path of the procedure, or a ref into the world: `{ type: "ref", value: ["world", ...path] }`.
|
|
75
|
+
*
|
|
76
|
+
* The types `["ref"]` and `["lit"]` (arrays of one key) are the kinds `ref` and `lit` of the
|
|
77
|
+
* kernel (D26). Before 0.26.0, they were calls of the procedures with these paths. A ref into
|
|
78
|
+
* the world is always a call: it is the type of these procedures, for example
|
|
79
|
+
* `{ type: { type: "ref", value: ["world", "ref"] }, value }`.
|
|
80
|
+
*/
|
|
65
81
|
readonly type: readonly string[] | EnvelopeRef;
|
|
66
82
|
/** The input of the call. `null` gives a call without input. */
|
|
67
83
|
readonly value: unknown;
|
|
@@ -76,7 +92,7 @@ export interface EnvelopeProgram {
|
|
|
76
92
|
// =============================================================================
|
|
77
93
|
|
|
78
94
|
/** The keys of an envelope (coeval D6). */
|
|
79
|
-
const HEADER_KEYS: ReadonlySet<string> = new Set(
|
|
95
|
+
const HEADER_KEYS: ReadonlySet<string> = new Set<string>(ENVELOPE_KEYS);
|
|
80
96
|
|
|
81
97
|
/** The keys that make an object a construct of the `$proc` form. */
|
|
82
98
|
const NATIVE_KEYS: readonly string[] = [PROCEDURE_JSON_KEY, OUTPUT_REF_KEY, LITERAL_KEY];
|
|
@@ -88,7 +104,10 @@ const CALL_KEYS: ReadonlySet<string> = new Set([PROCEDURE_JSON_KEY, "input", PRO
|
|
|
88
104
|
const RUNTIME_CALL_KEYS: ReadonlySet<string> = new Set(["path", "input", PROCEDURE_NAME_KEY, PROCEDURE_WHEN_KEY]);
|
|
89
105
|
|
|
90
106
|
const REF_TYPE = "ref";
|
|
107
|
+
/** The type of a literal that this module writes (the form of 0.23.0). */
|
|
91
108
|
const LITERAL_TYPE = "literal";
|
|
109
|
+
/** The alias of the literal type: the kind `lit` of the kernel (coeval D12). */
|
|
110
|
+
const LIT_ALIAS = "lit";
|
|
92
111
|
const NAMES_ROOT = "names";
|
|
93
112
|
const WORLD_ROOT = "world";
|
|
94
113
|
/** The name of the result of the step before, in the envelope form. */
|
|
@@ -102,7 +121,10 @@ type EnvelopeShape = Record<string, unknown> & { type: unknown; value: unknown;
|
|
|
102
121
|
/** The kind of an envelope. `bad-type` is an envelope whose type is a ref that is not into the world. */
|
|
103
122
|
type EnvelopeKind = "call" | "ref" | "literal" | "bad-type";
|
|
104
123
|
|
|
105
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* This function gives true if a value is a plain object with `type`, `value` and only header keys.
|
|
126
|
+
* The test is looser than `isEnvelope()` of coeval: a bad header gives an error, not data.
|
|
127
|
+
*/
|
|
106
128
|
function isEnvelopeShape(value: unknown): value is EnvelopeShape {
|
|
107
129
|
return (
|
|
108
130
|
isPlainObject(value) &&
|
|
@@ -117,22 +139,39 @@ function isProcedurePath(value: unknown): value is readonly string[] {
|
|
|
117
139
|
return Array.isArray(value) && value.length > 0 && value.every((key) => typeof key === "string");
|
|
118
140
|
}
|
|
119
141
|
|
|
120
|
-
/**
|
|
142
|
+
/** The keys of the type of an envelope (coeval `typePath()`), or undefined for a value that is not a type. */
|
|
143
|
+
function keysOfType(type: unknown): readonly (string | number)[] | undefined {
|
|
144
|
+
// A ref into the world as a type has only the keys `type` and `value`
|
|
145
|
+
if (isPlainObject(type) && Object.keys(type).length !== 2) return undefined;
|
|
146
|
+
return isTypeRef(type) ? typePath(type) : undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** This function gives true if a procedure path is also a kind of the kernel as an array: `["ref"]` or `["lit"]`. */
|
|
150
|
+
function isKindPath(keys: readonly unknown[]): boolean {
|
|
151
|
+
return keys.length === 1 && (keys[0] === REF_TYPE || keys[0] === LIT_ALIAS);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* This function gives the procedure path of the type of a call, or undefined for another type. The
|
|
156
|
+
* type of a call is a list of keys, or a ref into the world. A text is a kind of the kernel or data.
|
|
157
|
+
* The arrays `["ref"]` and `["lit"]` are kinds. A ref into the world is always a call, so it is the
|
|
158
|
+
* type of the procedures `["ref"]` and `["lit"]`.
|
|
159
|
+
*/
|
|
121
160
|
function callPath(type: unknown): ProcedurePath | undefined {
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const steps: unknown[] = path.slice(1);
|
|
127
|
-
return isProcedurePath(steps) ? [...steps] : undefined;
|
|
161
|
+
if (typeof type === "string") return undefined;
|
|
162
|
+
const keys = keysOfType(type);
|
|
163
|
+
if (!isProcedurePath(keys) || (Array.isArray(type) && isKindPath(keys))) return undefined;
|
|
164
|
+
return [...keys];
|
|
128
165
|
}
|
|
129
166
|
|
|
130
167
|
/** This function gives the kind of an envelope, or undefined for data. */
|
|
131
168
|
function envelopeKind(value: unknown): EnvelopeKind | undefined {
|
|
132
169
|
if (!isEnvelopeShape(value)) return undefined;
|
|
133
170
|
if (callPath(value.type)) return "call";
|
|
134
|
-
|
|
135
|
-
if (
|
|
171
|
+
const keys = keysOfType(value.type);
|
|
172
|
+
if (keys?.length === 1 && keys[0] === REF_TYPE) return "ref";
|
|
173
|
+
// The text "literal" (an array ["literal"] is a call, as in 0.23.0), or the alias "lit"
|
|
174
|
+
if (value.type === LITERAL_TYPE || (keys?.length === 1 && keys[0] === LIT_ALIAS)) return "literal";
|
|
136
175
|
if (isPlainObject(value.type) && value.type["type"] === REF_TYPE) return "bad-type";
|
|
137
176
|
return undefined;
|
|
138
177
|
}
|
|
@@ -332,7 +371,8 @@ function fromValue(value: unknown, path: Set<object>, depth: number): unknown {
|
|
|
332
371
|
*
|
|
333
372
|
* The conversion:
|
|
334
373
|
* - A call gives `{ type: path, value: input }`. A call without input gives the value `null`.
|
|
335
|
-
* `$name` gives the header `name`.
|
|
374
|
+
* `$name` gives the header `name`. The path `["ref"]` or `["lit"]` gives a ref into the world as
|
|
375
|
+
* the type, because these arrays are kinds of the kernel.
|
|
336
376
|
* - `{ $ref: "$last" }` gives `["names", "last"]`. `{ $ref: "a.b" }` gives `["names", "a", "b"]`.
|
|
337
377
|
* - `{ $literal: value }` gives a literal.
|
|
338
378
|
* - Data that the envelope form reads in a different way gives a literal. This data is an object
|
|
@@ -396,7 +436,10 @@ function toCall(call: AnyProcedureRef, path: Set<object>, depth: number): Envelo
|
|
|
396
436
|
: input === null
|
|
397
437
|
? { type: LITERAL_TYPE, value: null }
|
|
398
438
|
: toValue(input, path, depth + 1);
|
|
399
|
-
|
|
439
|
+
const type: EnvelopeProgram["type"] = isKindPath(procedurePath)
|
|
440
|
+
? (refDocument(WORLD_ROOT, ...procedurePath) as unknown as EnvelopeRef)
|
|
441
|
+
: [...procedurePath];
|
|
442
|
+
return { type, value, ...($name !== undefined ? { name: $name } : {}) };
|
|
400
443
|
}
|
|
401
444
|
|
|
402
445
|
/** This function changes a `$ref` object into a ref envelope. */
|
|
@@ -414,7 +457,7 @@ function toRef(ref: OutputRef): EnvelopeRef {
|
|
|
414
457
|
`["names", "last"] reads the result of the step before: give the step a different name`
|
|
415
458
|
);
|
|
416
459
|
}
|
|
417
|
-
return
|
|
460
|
+
return refDocument(NAMES_ROOT, name === NATIVE_LAST ? LAST_NAME : name!, ...fields) as unknown as EnvelopeRef;
|
|
418
461
|
}
|
|
419
462
|
|
|
420
463
|
/** This function gives true if a value of the `$proc` form is a call, a `$ref` or a `$literal`. */
|
|
@@ -136,6 +136,14 @@ export class IdempotencyCache<T> {
|
|
|
136
136
|
return { result: entry.done, first: true };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
/**
|
|
140
|
+
* This method removes the result of a key, so the next call with the key runs again. It removes
|
|
141
|
+
* the entry only when `result` is its result: an entry of a newer call with the key stays.
|
|
142
|
+
*/
|
|
143
|
+
forget(key: string, result: Promise<T>): void {
|
|
144
|
+
if (this.entries.get(key)?.done === result) this.entries.delete(key);
|
|
145
|
+
}
|
|
146
|
+
|
|
139
147
|
/** This function removes the expired results, and the oldest finished results above the limit. */
|
|
140
148
|
private evict(): void {
|
|
141
149
|
const now = this.now();
|
package/src/procedures/index.ts
CHANGED
|
@@ -247,9 +247,6 @@ export {
|
|
|
247
247
|
toJson,
|
|
248
248
|
normalizeRef,
|
|
249
249
|
// Hydration
|
|
250
|
-
hydrateInput,
|
|
251
|
-
prepareRootCall,
|
|
252
|
-
observeHydration,
|
|
253
250
|
isControlFlowPath,
|
|
254
251
|
isDataDriven,
|
|
255
252
|
markDataDriven,
|
|
@@ -266,7 +263,6 @@ export {
|
|
|
266
263
|
resolveOutputRef,
|
|
267
264
|
withScope,
|
|
268
265
|
scopeOf,
|
|
269
|
-
executeRef,
|
|
270
266
|
// Templates
|
|
271
267
|
extractTemplate,
|
|
272
268
|
parseProcedureJson,
|
|
@@ -287,6 +283,40 @@ export type {
|
|
|
287
283
|
RefScope,
|
|
288
284
|
} from "./ref.js";
|
|
289
285
|
|
|
286
|
+
// Programs on the coeval kernel (coeval D13, roadmap item M14): the run documents and their machine
|
|
287
|
+
export {
|
|
288
|
+
hydrateInput,
|
|
289
|
+
prepareRootCall,
|
|
290
|
+
observeHydration,
|
|
291
|
+
executeRef,
|
|
292
|
+
runProgram,
|
|
293
|
+
runDocument,
|
|
294
|
+
runControl,
|
|
295
|
+
runOperand,
|
|
296
|
+
programMachine,
|
|
297
|
+
PROGRAM_WORLD,
|
|
298
|
+
desugarProgram,
|
|
299
|
+
desugarInput,
|
|
300
|
+
desugarControlValue,
|
|
301
|
+
originOf,
|
|
302
|
+
namesOfScope,
|
|
303
|
+
scopeOfNames,
|
|
304
|
+
nameKey,
|
|
305
|
+
nameOfKey,
|
|
306
|
+
documentAt,
|
|
307
|
+
} from "./program.js";
|
|
308
|
+
export type {
|
|
309
|
+
ProgramDocument,
|
|
310
|
+
DocumentOrigin,
|
|
311
|
+
DesugarOptions,
|
|
312
|
+
ProgramHost,
|
|
313
|
+
CallEffect,
|
|
314
|
+
CallPosition,
|
|
315
|
+
RunDocumentOptions,
|
|
316
|
+
RunProgramOptions,
|
|
317
|
+
RunOperandOptions,
|
|
318
|
+
} from "./program.js";
|
|
319
|
+
|
|
290
320
|
// The envelope form of a program (coeval D6, D9 and D10, roadmap item M7)
|
|
291
321
|
export { isEnvelopeProgram, fromEnvelopeProgram, toEnvelopeProgram } from "./envelope.js";
|
|
292
322
|
export type { EnvelopeProgram, EnvelopeRef, EnvelopeLiteral, EnvelopePath } from "./envelope.js";
|