@milaboratories/pl-middle-layer 1.74.19 → 1.76.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/dist/index.cjs +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/middle_layer/block.cjs.map +1 -1
- package/dist/middle_layer/block.js.map +1 -1
- package/dist/middle_layer/block_action_context.cjs +57 -0
- package/dist/middle_layer/block_action_context.cjs.map +1 -0
- package/dist/middle_layer/block_action_context.d.ts +47 -0
- package/dist/middle_layer/block_action_context.d.ts.map +1 -0
- package/dist/middle_layer/block_action_context.js +57 -0
- package/dist/middle_layer/block_action_context.js.map +1 -0
- package/dist/middle_layer/build_stamp.cjs +1 -1
- package/dist/middle_layer/build_stamp.js +1 -1
- package/dist/middle_layer/column_links.cjs +106 -0
- package/dist/middle_layer/column_links.cjs.map +1 -0
- package/dist/middle_layer/column_links.js +104 -0
- package/dist/middle_layer/column_links.js.map +1 -0
- package/dist/middle_layer/index.d.ts +2 -1
- package/dist/middle_layer/middle_layer.cjs +1 -0
- package/dist/middle_layer/middle_layer.cjs.map +1 -1
- package/dist/middle_layer/middle_layer.js +1 -0
- package/dist/middle_layer/middle_layer.js.map +1 -1
- package/dist/middle_layer/project.cjs +53 -19
- package/dist/middle_layer/project.cjs.map +1 -1
- package/dist/middle_layer/project.d.ts +6 -0
- package/dist/middle_layer/project.d.ts.map +1 -1
- package/dist/middle_layer/project.js +54 -20
- package/dist/middle_layer/project.js.map +1 -1
- package/dist/middle_layer/project_overview.cjs +56 -24
- package/dist/middle_layer/project_overview.cjs.map +1 -1
- package/dist/middle_layer/project_overview.js +55 -25
- package/dist/middle_layer/project_overview.js.map +1 -1
- package/dist/model/args_derivation.cjs +153 -0
- package/dist/model/args_derivation.cjs.map +1 -0
- package/dist/model/args_derivation.d.ts +21 -0
- package/dist/model/args_derivation.d.ts.map +1 -0
- package/dist/model/args_derivation.js +148 -0
- package/dist/model/args_derivation.js.map +1 -0
- package/dist/model/project_helper.cjs +34 -8
- package/dist/model/project_helper.cjs.map +1 -1
- package/dist/model/project_helper.d.ts +18 -5
- package/dist/model/project_helper.d.ts.map +1 -1
- package/dist/model/project_helper.js +34 -8
- package/dist/model/project_helper.js.map +1 -1
- package/dist/model/project_model.cjs +2 -1
- package/dist/model/project_model.cjs.map +1 -1
- package/dist/model/project_model.d.ts +1 -1
- package/dist/model/project_model.js +2 -1
- package/dist/model/project_model.js.map +1 -1
- package/dist/mutator/project.cjs +73 -12
- package/dist/mutator/project.cjs.map +1 -1
- package/dist/mutator/project.d.ts +1 -0
- package/dist/mutator/project.d.ts.map +1 -1
- package/dist/mutator/project.js +73 -12
- package/dist/mutator/project.js.map +1 -1
- package/dist/mutator/template/render_block.cjs +35 -2
- package/dist/mutator/template/render_block.cjs.map +1 -1
- package/dist/mutator/template/render_block.js +35 -4
- package/dist/mutator/template/render_block.js.map +1 -1
- package/dist/mutator/template/render_template.cjs +16 -1
- package/dist/mutator/template/render_template.cjs.map +1 -1
- package/dist/mutator/template/render_template.d.ts +12 -1
- package/dist/mutator/template/render_template.d.ts.map +1 -1
- package/dist/mutator/template/render_template.js +16 -2
- package/dist/mutator/template/render_template.js.map +1 -1
- package/package.json +16 -16
- package/src/middle_layer/block.ts +4 -1
- package/src/middle_layer/block_action_context.test.ts +148 -0
- package/src/middle_layer/block_action_context.ts +162 -0
- package/src/middle_layer/column_links.test.ts +350 -0
- package/src/middle_layer/column_links.ts +152 -0
- package/src/middle_layer/index.ts +6 -0
- package/src/middle_layer/middle_layer.ts +2 -0
- package/src/middle_layer/project.ts +52 -5
- package/src/middle_layer/project_overview.test.ts +200 -0
- package/src/middle_layer/project_overview.ts +82 -28
- package/src/model/args.test.ts +39 -1
- package/src/model/args_derivation.test.ts +326 -0
- package/src/model/args_derivation.ts +194 -0
- package/src/model/project_helper.ts +55 -25
- package/src/model/project_model.ts +7 -2
- package/src/mutator/project-v3.test.ts +105 -1
- package/src/mutator/project.ts +128 -37
- package/src/mutator/project_action_context.test.ts +178 -0
- package/src/mutator/template/render_block.ts +70 -5
- package/src/mutator/template/render_template.ts +22 -2
- package/src/test/fake_project.ts +222 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AnyRef, PlTransaction, ResourceRef, ResourceType } from "@milaboratories/pl-client";
|
|
2
2
|
import { field, Pl } from "@milaboratories/pl-client";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import {
|
|
4
|
+
import { createRenderTemplateWithResource } from "./render_template";
|
|
5
5
|
|
|
6
6
|
export const BContextEnd: ResourceType = { name: "BContextEnd", version: "1" };
|
|
7
7
|
export const BContext: ResourceType = { name: "BContext", version: "1" };
|
|
@@ -30,12 +30,63 @@ export type HeavyBlockOutputs = {
|
|
|
30
30
|
|
|
31
31
|
export const HeavyBlockOutputNames: (keyof HeavyBlockOutputs)[] = ["context", "result"];
|
|
32
32
|
|
|
33
|
+
/** Baggage of the root action context of a block render. See the block action context spec. */
|
|
34
|
+
export type BlockActionContextBaggage = {
|
|
35
|
+
/** Display name of the block. */
|
|
36
|
+
name: string;
|
|
37
|
+
/** JSON of the block pack spec. */
|
|
38
|
+
blockPack: string;
|
|
39
|
+
blockId: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Resource type of a block action context. See ActionContextV1Type in the backend. */
|
|
43
|
+
export const ActionContextType: ResourceType = { name: "ActionContext", version: "1" };
|
|
44
|
+
|
|
45
|
+
/** The capability under which the backend accepts the "actionContext" link on a render. */
|
|
46
|
+
export const ActionContextLinkCapability = "optionalFieldsInSchemas:v1";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates the root action context of a block render: an ActionContext resource carrying the
|
|
50
|
+
* baggage, with a new random `run-id`, since each render is one run of the block and the contexts
|
|
51
|
+
* the templates create copy it. Undefined without baggage, or on a backend that cannot take the link.
|
|
52
|
+
*/
|
|
53
|
+
function createBlockActionContext(
|
|
54
|
+
tx: PlTransaction,
|
|
55
|
+
baggage: BlockActionContextBaggage | undefined,
|
|
56
|
+
): ResourceRef | undefined {
|
|
57
|
+
if (baggage === undefined || !tx.hasCapability(ActionContextLinkCapability)) return undefined;
|
|
58
|
+
return tx.createEphemeral(
|
|
59
|
+
ActionContextType,
|
|
60
|
+
JSON.stringify({
|
|
61
|
+
name: baggage.name,
|
|
62
|
+
"block-pack": baggage.blockPack,
|
|
63
|
+
"block-id": baggage.blockId,
|
|
64
|
+
"run-id": randomUUID(),
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Creates the heavy block render, and with `baggage` its root action context in the same
|
|
71
|
+
* transaction. Returns `actionContext`: the render is garbage collected after its outputs resolve,
|
|
72
|
+
* so the caller must hold the context to keep it.
|
|
73
|
+
*/
|
|
33
74
|
export function createRenderHeavyBlock(
|
|
34
75
|
tx: PlTransaction,
|
|
35
76
|
tpl: AnyRef,
|
|
36
77
|
inputs: HeavyBlockInputs,
|
|
37
|
-
|
|
38
|
-
|
|
78
|
+
baggage?: BlockActionContextBaggage,
|
|
79
|
+
): HeavyBlockOutputs & { actionContext?: ResourceRef } {
|
|
80
|
+
const actionContext = createBlockActionContext(tx, baggage);
|
|
81
|
+
const { outputs } = createRenderTemplateWithResource(
|
|
82
|
+
tx,
|
|
83
|
+
tpl,
|
|
84
|
+
true,
|
|
85
|
+
inputs,
|
|
86
|
+
HeavyBlockOutputNames,
|
|
87
|
+
actionContext,
|
|
88
|
+
);
|
|
89
|
+
return actionContext === undefined ? outputs : { ...outputs, actionContext };
|
|
39
90
|
}
|
|
40
91
|
|
|
41
92
|
export type LightBlockInputs = {
|
|
@@ -51,12 +102,26 @@ export type LightBlockOutput = {
|
|
|
51
102
|
|
|
52
103
|
export const LightBlockOutputNames: (keyof LightBlockOutput)[] = ["result"];
|
|
53
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Creates the light block render, with the same action context as a heavy one, so template code
|
|
107
|
+
* of either kind knows its block and run.
|
|
108
|
+
*/
|
|
54
109
|
export function createRenderLightBlock(
|
|
55
110
|
tx: PlTransaction,
|
|
56
111
|
tpl: ResourceRef,
|
|
57
112
|
inputs: LightBlockInputs,
|
|
58
|
-
|
|
59
|
-
|
|
113
|
+
baggage?: BlockActionContextBaggage,
|
|
114
|
+
): LightBlockOutput & { actionContext?: ResourceRef } {
|
|
115
|
+
const actionContext = createBlockActionContext(tx, baggage);
|
|
116
|
+
const { outputs } = createRenderTemplateWithResource(
|
|
117
|
+
tx,
|
|
118
|
+
tpl,
|
|
119
|
+
true,
|
|
120
|
+
inputs,
|
|
121
|
+
LightBlockOutputNames,
|
|
122
|
+
actionContext,
|
|
123
|
+
);
|
|
124
|
+
return actionContext === undefined ? outputs : { ...outputs, actionContext };
|
|
60
125
|
}
|
|
61
126
|
|
|
62
127
|
export function createBContextEnd(tx: PlTransaction): ResourceRef {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyRef, FieldRef, PlTransaction } from "@milaboratories/pl-client";
|
|
1
|
+
import type { AnyRef, FieldRef, PlTransaction, ResourceRef } from "@milaboratories/pl-client";
|
|
2
2
|
import { field, Pl } from "@milaboratories/pl-client";
|
|
3
3
|
import type { ResourceType } from "@platforma-sdk/model";
|
|
4
4
|
|
|
@@ -27,6 +27,24 @@ export function createRenderTemplate<O extends string>(
|
|
|
27
27
|
inputs: Pl.PlRecord,
|
|
28
28
|
outputNames: O[],
|
|
29
29
|
): Record<O, FieldRef> {
|
|
30
|
+
return createRenderTemplateWithResource(tx, tpl, ephemeral, inputs, outputNames).outputs;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Same as {@link createRenderTemplate}, and also returns the render resource itself.
|
|
35
|
+
*
|
|
36
|
+
* With `actionContext`, the render's "actionContext" service field points to it. The link is created
|
|
37
|
+
* before the inputs are locked: the backend accepts it only in the creating transaction, ahead of
|
|
38
|
+
* any explicit input lock.
|
|
39
|
+
*/
|
|
40
|
+
export function createRenderTemplateWithResource<O extends string>(
|
|
41
|
+
tx: PlTransaction,
|
|
42
|
+
tpl: AnyRef,
|
|
43
|
+
ephemeral: boolean,
|
|
44
|
+
inputs: Pl.PlRecord,
|
|
45
|
+
outputNames: O[],
|
|
46
|
+
actionContext?: AnyRef,
|
|
47
|
+
): { render: ResourceRef; outputs: Record<O, FieldRef> } {
|
|
30
48
|
if (outputNames.length === 0) throw new Error("Zero output names provided");
|
|
31
49
|
const rId = ephemeral ? tx.createEphemeral(EphRenderTemplate) : tx.createStruct(RenderTemplate);
|
|
32
50
|
|
|
@@ -35,7 +53,9 @@ export function createRenderTemplate<O extends string>(
|
|
|
35
53
|
|
|
36
54
|
tx.createField(tplField, "Input", tpl);
|
|
37
55
|
tx.createField(inputsField, "Input", Pl.createPlMap(tx, inputs, ephemeral));
|
|
56
|
+
if (actionContext !== undefined)
|
|
57
|
+
tx.createField(field(rId, "actionContext"), "Service", actionContext);
|
|
38
58
|
tx.lockInputs(rId);
|
|
39
59
|
|
|
40
|
-
return Pl.futureRecord(tx, rId, outputNames, "Output", "outputs/");
|
|
60
|
+
return { render: rId, outputs: Pl.futureRecord(tx, rId, outputNames, "Output", "outputs/") };
|
|
41
61
|
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSignedResourceId,
|
|
3
|
+
DefaultFinalResourceDataPredicate,
|
|
4
|
+
NullSignedResourceId,
|
|
5
|
+
type FieldData,
|
|
6
|
+
type FieldType,
|
|
7
|
+
type SignedResourceId,
|
|
8
|
+
} from "@milaboratories/pl-client";
|
|
9
|
+
import type { PColumnSpec, ArgsIssue } from "@milaboratories/pl-model-common";
|
|
10
|
+
import { PlTreeState, type ExtendedResourceData } from "@milaboratories/pl-tree";
|
|
11
|
+
import { argsFingerprint, type ArgsErrorsRecord } from "../model/args_derivation";
|
|
12
|
+
import {
|
|
13
|
+
projectFieldName,
|
|
14
|
+
ProjectStructureKey,
|
|
15
|
+
type ProjectField,
|
|
16
|
+
type ProjectStructure,
|
|
17
|
+
} from "../model/project_model";
|
|
18
|
+
|
|
19
|
+
/** What a project field holds: a resource, an error, or nothing yet. */
|
|
20
|
+
export type FieldContent = SignedResourceId | { error: SignedResourceId } | "unresolved";
|
|
21
|
+
|
|
22
|
+
/** Columns of a result list by name: the spec of each and the cells of its data. */
|
|
23
|
+
export type Columns = Record<string, { spec: PColumnSpec; cells: string[] }>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* State of a result list: `locked` — complete; `open` — more columns may come; `failed` — it has
|
|
27
|
+
* an error and will never lock.
|
|
28
|
+
*/
|
|
29
|
+
export type FrameState = { state: "locked" | "open" | "failed" };
|
|
30
|
+
|
|
31
|
+
/** The block fields a {@link FakeProject} holds. */
|
|
32
|
+
export type FakeProjectField = Extract<
|
|
33
|
+
ProjectField["fieldName"],
|
|
34
|
+
| "currentArgs"
|
|
35
|
+
| "inputsValid"
|
|
36
|
+
| "prodArgs"
|
|
37
|
+
| "prodCtx"
|
|
38
|
+
| "prodUiCtx"
|
|
39
|
+
| "stagingCtx"
|
|
40
|
+
| "stagingUiCtx"
|
|
41
|
+
>;
|
|
42
|
+
|
|
43
|
+
/** A project resource tree built in memory: its blocks, in project order, and their fields. */
|
|
44
|
+
export class FakeProject {
|
|
45
|
+
private readonly nodes: ExtendedResourceData[] = [];
|
|
46
|
+
private sent = 0;
|
|
47
|
+
private readonly blocks = new Map<string, Partial<Record<FakeProjectField, FieldContent>>>();
|
|
48
|
+
private readonly frameFields = new Map<SignedResourceId, FieldData[]>();
|
|
49
|
+
|
|
50
|
+
/** Adds a block below the others, or replaces the fields of one already added. */
|
|
51
|
+
block(blockId: string, fields: Partial<Record<FakeProjectField, FieldContent>>): void {
|
|
52
|
+
this.blocks.set(blockId, fields);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Args of a block. */
|
|
56
|
+
args(args: unknown): SignedResourceId {
|
|
57
|
+
return this.value("json/object", args);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Args errors of a block found in the args `forArgs`, as its `inputsValid` field holds them. */
|
|
61
|
+
argsErrors(errors: readonly ArgsIssue[], forArgs: unknown): SignedResourceId {
|
|
62
|
+
const record: ArgsErrorsRecord = { argsFingerprint: argsFingerprint(forArgs), errors };
|
|
63
|
+
return this.value("json/object", record);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A ready value resource holding `json`. */
|
|
67
|
+
value(type: string, json: unknown): SignedResourceId {
|
|
68
|
+
const id = this.newId();
|
|
69
|
+
this.nodes.push({
|
|
70
|
+
...READY,
|
|
71
|
+
id,
|
|
72
|
+
kind: "Value",
|
|
73
|
+
type: { name: type, version: "1" },
|
|
74
|
+
data: Buffer.from(JSON.stringify(json)),
|
|
75
|
+
fields: [],
|
|
76
|
+
});
|
|
77
|
+
return id;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** An error a resource or field can hold. */
|
|
81
|
+
error(message: string): SignedResourceId {
|
|
82
|
+
return this.value("json/resourceError", { message });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** A result list exporting the given columns, each with JSON data holding `cells`. */
|
|
86
|
+
frame(columns: Columns, state: FrameState = { state: "locked" }): SignedResourceId {
|
|
87
|
+
const id = this.newId();
|
|
88
|
+
this.frameFields.set(id, []);
|
|
89
|
+
this.extend(id, columns, state);
|
|
90
|
+
return id;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Adds columns to a result list made by {@link frame}, and moves it to `state`. */
|
|
94
|
+
extend(id: SignedResourceId, columns: Columns, { state }: FrameState): void {
|
|
95
|
+
const fields = this.frameFields.get(id);
|
|
96
|
+
if (fields === undefined) throw new Error("Not a result list of this project");
|
|
97
|
+
for (const [name, { spec, cells }] of Object.entries(columns)) {
|
|
98
|
+
const data = Object.fromEntries(cells.map((cell, i) => [`["s${i}"]`, cell]));
|
|
99
|
+
fields.push(field("Input", `${name}.spec`, this.value("PColumnSpec", spec)));
|
|
100
|
+
fields.push(
|
|
101
|
+
field("Input", `${name}.data`, this.value("PColumnData/Json", { keyLength: 1, data })),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const error = state === "failed" ? this.error("render failed") : NullSignedResourceId;
|
|
105
|
+
this.nodes.push({
|
|
106
|
+
...READY,
|
|
107
|
+
id,
|
|
108
|
+
kind: "Structural",
|
|
109
|
+
type: { name: "PFrame", version: "1" },
|
|
110
|
+
inputsLocked: state === "locked",
|
|
111
|
+
resourceReady: state === "locked",
|
|
112
|
+
final: state === "locked",
|
|
113
|
+
error,
|
|
114
|
+
fields: [...fields],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The tree of the project as it is now. */
|
|
119
|
+
tree(): PlTreeState {
|
|
120
|
+
const tree = new PlTreeState(ROOT_ID, DefaultFinalResourceDataPredicate);
|
|
121
|
+
tree.updateFromResourceData(this.changes());
|
|
122
|
+
return tree;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The project resource and the resources added or changed since the last call: a final
|
|
127
|
+
* resource already in the tree is not sent again.
|
|
128
|
+
*/
|
|
129
|
+
changes(): ExtendedResourceData[] {
|
|
130
|
+
const structure: ProjectStructure = {
|
|
131
|
+
groups: [
|
|
132
|
+
{
|
|
133
|
+
id: "main",
|
|
134
|
+
label: "Main",
|
|
135
|
+
blocks: [...this.blocks.keys()].map((id) => ({ id, label: id, renderingMode: "Heavy" })),
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
const fields = [...this.blocks].flatMap(([blockId, contents]) =>
|
|
140
|
+
PROJECT_FIELDS.flatMap((name) => {
|
|
141
|
+
const content = contents[name];
|
|
142
|
+
return content === undefined
|
|
143
|
+
? []
|
|
144
|
+
: [projectField(projectFieldName(blockId, name), content)];
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
const root: ExtendedResourceData = {
|
|
148
|
+
...READY,
|
|
149
|
+
final: false,
|
|
150
|
+
id: ROOT_ID,
|
|
151
|
+
kind: "Structural",
|
|
152
|
+
type: { name: "UserProject", version: "2" },
|
|
153
|
+
fields,
|
|
154
|
+
kv: [{ key: ProjectStructureKey, value: Buffer.from(JSON.stringify(structure)) }],
|
|
155
|
+
};
|
|
156
|
+
const added = this.nodes.slice(this.sent);
|
|
157
|
+
this.sent = this.nodes.length;
|
|
158
|
+
return [root, ...added];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private newId(): SignedResourceId {
|
|
162
|
+
return createSignedResourceId(nextResourceId++);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Internals
|
|
167
|
+
|
|
168
|
+
const ROOT_ID = createSignedResourceId(1_000_001n);
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Resource ids stay unique across tests, as they are on a backend: the middle layer caches the
|
|
172
|
+
* columns an args resource references by its id.
|
|
173
|
+
*/
|
|
174
|
+
let nextResourceId = 100n;
|
|
175
|
+
|
|
176
|
+
const READY: Omit<ExtendedResourceData, "id" | "kind" | "type" | "fields"> = {
|
|
177
|
+
originalResourceId: NullSignedResourceId,
|
|
178
|
+
error: NullSignedResourceId,
|
|
179
|
+
inputsLocked: true,
|
|
180
|
+
outputsLocked: true,
|
|
181
|
+
resourceReady: true,
|
|
182
|
+
final: true,
|
|
183
|
+
kv: [],
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const PROJECT_FIELDS: readonly FakeProjectField[] = [
|
|
187
|
+
"currentArgs",
|
|
188
|
+
"inputsValid",
|
|
189
|
+
"prodArgs",
|
|
190
|
+
"prodCtx",
|
|
191
|
+
"prodUiCtx",
|
|
192
|
+
"stagingCtx",
|
|
193
|
+
"stagingUiCtx",
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
function field(type: FieldType, name: string, value: SignedResourceId): FieldData {
|
|
197
|
+
return { name, type, value, error: NullSignedResourceId, status: "Resolved", valueIsFinal: true };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function projectField(name: string, content: FieldContent): FieldData {
|
|
201
|
+
if (content === "unresolved") {
|
|
202
|
+
return {
|
|
203
|
+
name,
|
|
204
|
+
type: "Dynamic",
|
|
205
|
+
value: NullSignedResourceId,
|
|
206
|
+
error: NullSignedResourceId,
|
|
207
|
+
status: "Empty",
|
|
208
|
+
valueIsFinal: false,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (typeof content === "object" && "error" in content) {
|
|
212
|
+
return {
|
|
213
|
+
name,
|
|
214
|
+
type: "Dynamic",
|
|
215
|
+
value: NullSignedResourceId,
|
|
216
|
+
error: content.error,
|
|
217
|
+
status: "Resolved",
|
|
218
|
+
valueIsFinal: true,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return field("Dynamic", name, content);
|
|
222
|
+
}
|