@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,5 +1,6 @@
|
|
|
1
1
|
import { BlockRenderingStateKey, ProjectCreatedTimestamp, ProjectLastModifiedTimestamp, ProjectMetaKey, ProjectStructureAuthorKey, ProjectStructureKey, projectFieldName } from "../model/project_model.js";
|
|
2
2
|
import { allBlocks, productionGraph } from "../model/project_model_util.js";
|
|
3
|
+
import { readArgsErrorsField } from "../model/args_derivation.js";
|
|
3
4
|
import { getDebugFlags } from "../debug/index.js";
|
|
4
5
|
import { constructBlockContext, constructBlockContextArgsOnly } from "./block_ctx.js";
|
|
5
6
|
import { ifNotUndef } from "../cfg_render/util.js";
|
|
@@ -90,7 +91,7 @@ function projectOverview(prjEntry, navigationStates, env) {
|
|
|
90
91
|
else calculationStatus = info.prod.finished ? "Done" : "Running";
|
|
91
92
|
}
|
|
92
93
|
const bp = getBlockPackInfo(prj, id);
|
|
93
|
-
const { sections, title, subtitle, tags, inputsValid, sdkVersion, featureFlags, isIncompatibleWithRuntime } = ifNotUndef(bp, ({ bpId, cfg, info: blockPackInfo }) => {
|
|
94
|
+
const { sections, title, subtitle, tags, inputsValid, inputsValidFromArgs, sdkVersion, featureFlags, isIncompatibleWithRuntime } = ifNotUndef(bp, ({ bpId, cfg, info: blockPackInfo }) => {
|
|
94
95
|
if (!env.runtimeCapabilities.checkCompatibility(cfg.featureFlags)) return {
|
|
95
96
|
isIncompatibleWithRuntime: true,
|
|
96
97
|
featureFlags: cfg.featureFlags
|
|
@@ -121,10 +122,11 @@ function projectOverview(prjEntry, navigationStates, env) {
|
|
|
121
122
|
env.logger.error(new Error("Error in block model tags", { cause }));
|
|
122
123
|
return [];
|
|
123
124
|
} })),
|
|
124
|
-
inputsValid: cfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION ? info.currentArguments
|
|
125
|
+
inputsValid: cfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION ? argsAllowRun(prj, id, info.currentArguments) : cfg.inputsValid ? computableFromCfgOrRF(env, blockCtxArgsOnly, cfg.inputsValid, codeWithInfo, bpId).wrap({ recover: (cause) => {
|
|
125
126
|
env.logger.error(new Error("Error in block model inputsValid", { cause }));
|
|
126
127
|
return false;
|
|
127
128
|
} }) : void 0,
|
|
129
|
+
inputsValidFromArgs: cfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION,
|
|
128
130
|
sdkVersion: codeWithInfo?.sdkVersion,
|
|
129
131
|
featureFlags: codeWithInfo?.featureFlags ?? {},
|
|
130
132
|
isIncompatibleWithRuntime: false
|
|
@@ -158,6 +160,7 @@ function projectOverview(prjEntry, navigationStates, env) {
|
|
|
158
160
|
settings,
|
|
159
161
|
sections,
|
|
160
162
|
inputsValid,
|
|
163
|
+
inputsValidFromArgs: inputsValidFromArgs === true,
|
|
161
164
|
updateInfo: {},
|
|
162
165
|
currentBlockPack: bp?.info?.source,
|
|
163
166
|
updates,
|
|
@@ -175,33 +178,60 @@ function projectOverview(prjEntry, navigationStates, env) {
|
|
|
175
178
|
blocks
|
|
176
179
|
};
|
|
177
180
|
}, {
|
|
178
|
-
postprocessValue: (value) => {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const stale = b.stale || b.upstreams.findIndex((u) => staleBlocks.has(u)) !== -1;
|
|
187
|
-
const canRun = (stale || b.outputErrors) && Boolean(b.inputsValid) && !b.missingReference && b.upstreams.findIndex((u) => cantRun.has(u)) === -1;
|
|
188
|
-
const bb = {
|
|
189
|
-
...b,
|
|
190
|
-
canRun,
|
|
191
|
-
stale,
|
|
192
|
-
updateSuggestions: b.updates?.suggestions ?? [],
|
|
193
|
-
updatedBlockPack: b.updates?.mainSuggestion
|
|
194
|
-
};
|
|
195
|
-
delete bb["updates"];
|
|
196
|
-
return bb;
|
|
197
|
-
})
|
|
198
|
-
};
|
|
199
|
-
},
|
|
181
|
+
postprocessValue: (value) => ({
|
|
182
|
+
...value,
|
|
183
|
+
blocks: withRunState(value.blocks).map(({ updates, inputsValidFromArgs: _, ...b }) => ({
|
|
184
|
+
...b,
|
|
185
|
+
updateSuggestions: updates?.suggestions ?? [],
|
|
186
|
+
updatedBlockPack: updates?.mainSuggestion
|
|
187
|
+
}))
|
|
188
|
+
}),
|
|
200
189
|
onRecalculation: getDebugFlags().logProjectOverviewStat ? (stats) => {
|
|
201
190
|
console.log(`[projectOverview] ${JSON.stringify(stats)}`);
|
|
202
191
|
} : void 0
|
|
203
192
|
}).withStableType();
|
|
204
193
|
}
|
|
205
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Whether the args of a block with model API version 2 let it run: the block has current args,
|
|
196
|
+
* and its `inputsValid` field records no errors its `argsIssues()` found in them (see
|
|
197
|
+
* `readArgsErrorsField`).
|
|
198
|
+
*
|
|
199
|
+
* @param currentArgs - The block's current args as decoded from its `currentArgs` field;
|
|
200
|
+
* `undefined` when it has none
|
|
201
|
+
*/
|
|
202
|
+
function argsAllowRun(prj, blockId, currentArgs) {
|
|
203
|
+
if (currentArgs === void 0) return false;
|
|
204
|
+
const argsErrors = prj.getField({
|
|
205
|
+
field: projectFieldName(blockId, "inputsValid"),
|
|
206
|
+
assertFieldType: "Dynamic",
|
|
207
|
+
stableIfNotFound: true
|
|
208
|
+
});
|
|
209
|
+
return readArgsErrorsField(argsErrors?.value?.getData(), currentArgs).length === 0;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Adds the run state to each block of a project, the blocks listed upstream first. A block is
|
|
213
|
+
* stale when its own args changed or an upstream block is stale. It can run when it needs to run
|
|
214
|
+
* (it is stale or its outputs failed), its args allow it, it references no missing block, and no
|
|
215
|
+
* upstream block that needs to run is kept from running. An upstream block whose `inputsValid`
|
|
216
|
+
* comes from its args and whose results are up to date feeds its dependents even when its args
|
|
217
|
+
* could not run again; any other upstream block that is not valid keeps them from running.
|
|
218
|
+
*/
|
|
219
|
+
function withRunState(blocks) {
|
|
220
|
+
const cantRun = /* @__PURE__ */ new Set();
|
|
221
|
+
const staleBlocks = /* @__PURE__ */ new Set();
|
|
222
|
+
return blocks.map((b) => {
|
|
223
|
+
if (b.stale) staleBlocks.add(b.id);
|
|
224
|
+
const stale = b.stale || b.upstreams.some((u) => staleBlocks.has(u));
|
|
225
|
+
const needsRun = stale || b.outputErrors;
|
|
226
|
+
if (!b.inputsValid && (needsRun || !b.inputsValidFromArgs)) cantRun.add(b.id);
|
|
227
|
+
const canRun = needsRun && Boolean(b.inputsValid) && !b.missingReference && !b.upstreams.some((u) => cantRun.has(u));
|
|
228
|
+
return {
|
|
229
|
+
...b,
|
|
230
|
+
stale,
|
|
231
|
+
canRun
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
export { argsAllowRun, projectOverview, withRunState };
|
|
206
236
|
|
|
207
237
|
//# sourceMappingURL=project_overview.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project_overview.js","names":[],"sources":["../../src/middle_layer/project_overview.ts"],"sourcesContent":["import type { PlTreeEntry } from \"@milaboratories/pl-tree\";\nimport type { ComputableStableDefined } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport type { ProjectRenderingState, ProjectStructure } from \"../model/project_model\";\nimport {\n BlockRenderingStateKey,\n ProjectCreatedTimestamp,\n projectFieldName,\n ProjectLastModifiedTimestamp,\n ProjectMetaKey,\n ProjectStructureAuthorKey,\n ProjectStructureKey,\n} from \"../model/project_model\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport { allBlocks, productionGraph } from \"../model/project_model_util\";\nimport type { MiddleLayerEnvironment } from \"./middle_layer\";\nimport type {\n AuthorMarker,\n BlockCalculationStatus,\n BlockSettings,\n ProjectMeta,\n ProjectOverview,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { constructBlockContext, constructBlockContextArgsOnly } from \"./block_ctx\";\nimport { ifNotUndef } from \"../cfg_render/util\";\nimport { type BlockSection, BLOCK_STORAGE_FACADE_VERSION } from \"@platforma-sdk/model\";\nimport { extractCodeWithInfo, wrapCallback } from \"@platforma-sdk/model\";\nimport { computableFromCfgOrRF } from \"./render\";\nimport type { NavigationStates } from \"./navigation_states\";\nimport { getBlockPackInfo } from \"./util\";\nimport { resourceIdToString, type SignedResourceId } from \"@milaboratories/pl-client\";\nimport { omitBy, isEqual } from \"es-toolkit\";\nimport { getDebugFlags } from \"../debug\";\n\ntype BlockInfo = {\n argsRid?: SignedResourceId;\n currentArguments: unknown;\n prod?: ProdState;\n};\n\ntype ProdState = {\n finished: boolean;\n\n outputError: boolean;\n\n outputsError?: string;\n\n exportsError?: string;\n\n stale: boolean;\n\n /** Arguments current production was rendered with. */\n arguments: Record<string, unknown>;\n};\n\nfunction argsEquals(\n a: Record<string, unknown> | undefined,\n b: Record<string, unknown> | undefined,\n): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n const cleanA = omitBy(a, (_, key) => key.startsWith(\"__\"));\n const cleanB = omitBy(b, (_, key) => key.startsWith(\"__\"));\n return isEqual(cleanA, cleanB);\n}\n\n/** Returns derived general project state form the project resource */\nexport function projectOverview(\n prjEntry: PlTreeEntry,\n navigationStates: NavigationStates,\n env: MiddleLayerEnvironment,\n): ComputableStableDefined<ProjectOverview> {\n return Computable.make(\n (ctx) => {\n const prj = ctx.accessor(prjEntry).node();\n\n const created = notEmpty(prj.getKeyValueAsJson<number>(ProjectCreatedTimestamp));\n const lastModified = notEmpty(prj.getKeyValueAsJson<number>(ProjectLastModifiedTimestamp));\n\n const meta = notEmpty(prj.getKeyValueAsJson<ProjectMeta>(ProjectMetaKey));\n const structure = notEmpty(prj.getKeyValueAsJson<ProjectStructure>(ProjectStructureKey));\n const renderingState = notEmpty(\n prj.getKeyValueAsJson<ProjectRenderingState>(BlockRenderingStateKey),\n );\n\n const infos = new Map<string, BlockInfo>();\n for (const { id } of allBlocks(structure)) {\n const cInputs = prj.traverse({\n field: projectFieldName(id, \"currentArgs\"),\n assertFieldType: \"Dynamic\",\n stableIfNotFound: true,\n });\n const currentArguments = cInputs?.getDataAsJson<Record<string, unknown>>();\n\n let prod: ProdState | undefined = undefined;\n\n const rInputs = prj.traverse({\n field: projectFieldName(id, \"prodArgs\"),\n assertFieldType: \"Dynamic\",\n stableIfNotFound: true,\n });\n if (rInputs !== undefined) {\n const prodArgs = rInputs.getDataAsJson() as Record<string, unknown>;\n const result = prj.getField({\n field: projectFieldName(id, \"prodOutput\"),\n assertFieldType: \"Dynamic\",\n errorIfFieldNotFound: true,\n });\n const ctx = prj.getField({\n field: projectFieldName(id, \"prodUiCtx\"),\n assertFieldType: \"Dynamic\",\n errorIfFieldNotFound: true,\n });\n prod = {\n arguments: prodArgs,\n stale: !argsEquals(currentArguments, prodArgs),\n outputError:\n result.error !== undefined ||\n ctx.error !== undefined ||\n result.value?.getError() !== undefined ||\n ctx.value?.getError() !== undefined,\n outputsError:\n result.error?.getDataAsString() ?? result.value?.getError()?.getDataAsString(),\n exportsError: ctx.error?.getDataAsString() ?? ctx.value?.getError()?.getDataAsString(),\n finished:\n ((result.value !== undefined && result.value.getIsReadyOrError()) ||\n (result.error !== undefined && result.error.getIsReadyOrError())) &&\n ((ctx.value !== undefined && ctx.value.getIsReadyOrError()) ||\n (ctx.error !== undefined && ctx.error.getIsReadyOrError())),\n };\n }\n\n infos.set(id, { currentArguments, prod, argsRid: cInputs?.resourceInfo.id });\n }\n\n const currentGraph = productionGraph(structure, (id) => {\n const bpInfo = getBlockPackInfo(prj, id)!;\n const bInfo = infos.get(id)!;\n const args = bInfo.currentArguments;\n return {\n args,\n enrichmentTargets: bInfo.argsRid\n ? env.projectHelper.getEnrichmentTargets(\n () => bpInfo.cfg,\n () => args,\n { argsRid: bInfo.argsRid, blockPackRid: bpInfo.bpResourceId },\n )\n : undefined,\n };\n });\n\n const limbo = new Set(renderingState.blocksInLimbo);\n\n const blocks = [...allBlocks(structure)].map(({ id, label: defaultLabel, renderingMode }) => {\n const info = notEmpty(infos.get(id));\n const gNode = notEmpty(currentGraph.nodes.get(id));\n let calculationStatus: BlockCalculationStatus = \"NotCalculated\";\n if (info.prod !== undefined) {\n if (limbo.has(id)) calculationStatus = \"Limbo\";\n else calculationStatus = info.prod.finished ? \"Done\" : \"Running\";\n }\n\n const bp = getBlockPackInfo(prj, id);\n\n const {\n sections,\n title,\n subtitle,\n tags,\n inputsValid,\n sdkVersion,\n featureFlags,\n isIncompatibleWithRuntime,\n } =\n ifNotUndef(bp, ({ bpId, cfg, info: blockPackInfo }) => {\n if (!env.runtimeCapabilities.checkCompatibility(cfg.featureFlags)) {\n return {\n isIncompatibleWithRuntime: true,\n featureFlags: cfg.featureFlags,\n };\n }\n const blockCtx = constructBlockContext(prjEntry, id);\n const blockCtxArgsOnly = constructBlockContextArgsOnly(prjEntry, id);\n const codeWithInfoOrError = wrapCallback(() => extractCodeWithInfo(cfg));\n if (codeWithInfoOrError.error) {\n return {\n title: codeWithInfoOrError.error.message,\n isIncompatibleWithRuntime: true,\n featureFlags: cfg.featureFlags,\n };\n }\n const codeWithInfo = codeWithInfoOrError.value;\n return {\n sections: computableFromCfgOrRF(\n env,\n blockCtx,\n cfg.sections,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model sections\", { cause }));\n return [];\n },\n }) as ComputableStableDefined<BlockSection[]>,\n title: ifNotUndef(\n cfg.title,\n (title) =>\n computableFromCfgOrRF(\n env,\n blockCtxArgsOnly,\n title,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model title\", { cause }));\n return \"Invalid title\";\n },\n }) as ComputableStableDefined<string>,\n ),\n subtitle: ifNotUndef(\n cfg.subtitle,\n (subtitle) =>\n computableFromCfgOrRF(\n env,\n blockCtxArgsOnly,\n subtitle,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model subtitle\", { cause }));\n return \"Invalid subtitle\";\n },\n }) as ComputableStableDefined<string>,\n ),\n tags: ifNotUndef(\n cfg.tags,\n (tags) =>\n computableFromCfgOrRF(\n env,\n blockCtx,\n tags,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model tags\", { cause }));\n return [];\n },\n }) as ComputableStableDefined<string[]>,\n ),\n // inputsValid: for modelAPIVersion 2, it's true if currentArgs exists (args derivation succeeded)\n // For older blocks, use the inputsValid callback from config\n inputsValid:\n cfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION\n ? info.currentArguments !== undefined\n : cfg.inputsValid\n ? (computableFromCfgOrRF(\n env,\n blockCtxArgsOnly,\n cfg.inputsValid,\n codeWithInfo,\n bpId,\n ).wrap({\n recover: (cause) => {\n env.logger.error(\n new Error(\"Error in block model inputsValid\", { cause }),\n );\n return false;\n },\n }) as ComputableStableDefined<boolean>)\n : undefined,\n sdkVersion: codeWithInfo?.sdkVersion,\n featureFlags: codeWithInfo?.featureFlags ?? {},\n isIncompatibleWithRuntime: false,\n };\n }) || {};\n\n const settings = prj\n .traverse({\n field: projectFieldName(id, \"blockSettings\"),\n assertFieldType: \"Dynamic\",\n errorIfFieldNotSet: true,\n })\n .getDataAsJson() as BlockSettings;\n\n const updates = ifNotUndef(bp, ({ info }) =>\n env.blockUpdateWatcher.get({ currentSpec: info.source, settings }),\n );\n\n return {\n projectResourceId: resourceIdToString(prjEntry.rid),\n id,\n label: title ?? defaultLabel,\n title: title ?? defaultLabel,\n subtitle,\n tags,\n renderingMode,\n stale: info.prod?.stale !== false || calculationStatus === \"Limbo\",\n missingReference: gNode.missingReferences,\n upstreams: [...currentGraph.traverseIdsExcludingRoots(\"upstream\", id)],\n downstreams: [...currentGraph.traverseIdsExcludingRoots(\"downstream\", id)],\n calculationStatus,\n outputErrors: info.prod?.outputError === true,\n outputsError: info.prod?.outputsError,\n exportsError: info.prod?.exportsError,\n settings,\n sections,\n inputsValid,\n updateInfo: {},\n currentBlockPack: bp?.info?.source,\n updates,\n sdkVersion,\n featureFlags,\n isIncompatibleWithRuntime,\n navigationState: navigationStates.getState(id),\n };\n });\n\n return {\n meta,\n created: new Date(created),\n lastModified: new Date(lastModified),\n authorMarker: prj.getKeyValueAsJson<AuthorMarker>(ProjectStructureAuthorKey),\n blocks,\n };\n },\n {\n postprocessValue: (value) => {\n const cantRun = new Set<string>();\n const staleBlocks = new Set<string>();\n return {\n ...value,\n blocks: value.blocks.map((b) => {\n if (!b.inputsValid) cantRun.add(b.id);\n if (b.stale) staleBlocks.add(b.id);\n const stale = b.stale || b.upstreams.findIndex((u) => staleBlocks.has(u)) !== -1;\n const canRun =\n (stale || b.outputErrors) &&\n Boolean(b.inputsValid) &&\n !b.missingReference &&\n b.upstreams.findIndex((u) => cantRun.has(u)) === -1;\n const bb = {\n ...b,\n canRun,\n stale,\n updateSuggestions: b.updates?.suggestions ?? [],\n updatedBlockPack: b.updates?.mainSuggestion,\n };\n delete bb[\"updates\"];\n return bb;\n }),\n };\n },\n onRecalculation: getDebugFlags().logProjectOverviewStat\n ? (stats) => {\n console.log(`[projectOverview] ${JSON.stringify(stats)}`);\n }\n : undefined,\n },\n ).withStableType();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"project_overview.js","names":[],"sources":["../../src/middle_layer/project_overview.ts"],"sourcesContent":["import type { PlTreeEntry, PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport type { ComputableStableDefined } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport type { ProjectRenderingState, ProjectStructure } from \"../model/project_model\";\nimport {\n BlockRenderingStateKey,\n ProjectCreatedTimestamp,\n projectFieldName,\n ProjectLastModifiedTimestamp,\n ProjectMetaKey,\n ProjectStructureAuthorKey,\n ProjectStructureKey,\n} from \"../model/project_model\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport { allBlocks, productionGraph } from \"../model/project_model_util\";\nimport type { MiddleLayerEnvironment } from \"./middle_layer\";\nimport type {\n AuthorMarker,\n BlockCalculationStatus,\n BlockSettings,\n ProjectMeta,\n ProjectOverview,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { constructBlockContext, constructBlockContextArgsOnly } from \"./block_ctx\";\nimport { ifNotUndef } from \"../cfg_render/util\";\nimport { type BlockSection, BLOCK_STORAGE_FACADE_VERSION } from \"@platforma-sdk/model\";\nimport { extractCodeWithInfo, wrapCallback } from \"@platforma-sdk/model\";\nimport { computableFromCfgOrRF } from \"./render\";\nimport type { NavigationStates } from \"./navigation_states\";\nimport { getBlockPackInfo } from \"./util\";\nimport { resourceIdToString, type SignedResourceId } from \"@milaboratories/pl-client\";\nimport { omitBy, isEqual } from \"es-toolkit\";\nimport { getDebugFlags } from \"../debug\";\nimport { readArgsErrorsField } from \"../model/args_derivation\";\n\ntype BlockInfo = {\n argsRid?: SignedResourceId;\n currentArguments: unknown;\n prod?: ProdState;\n};\n\ntype ProdState = {\n finished: boolean;\n\n outputError: boolean;\n\n outputsError?: string;\n\n exportsError?: string;\n\n stale: boolean;\n\n /** Arguments current production was rendered with. */\n arguments: Record<string, unknown>;\n};\n\nfunction argsEquals(\n a: Record<string, unknown> | undefined,\n b: Record<string, unknown> | undefined,\n): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n const cleanA = omitBy(a, (_, key) => key.startsWith(\"__\"));\n const cleanB = omitBy(b, (_, key) => key.startsWith(\"__\"));\n return isEqual(cleanA, cleanB);\n}\n\n/** Returns derived general project state form the project resource */\nexport function projectOverview(\n prjEntry: PlTreeEntry,\n navigationStates: NavigationStates,\n env: MiddleLayerEnvironment,\n): ComputableStableDefined<ProjectOverview> {\n return Computable.make(\n (ctx) => {\n const prj = ctx.accessor(prjEntry).node();\n\n const created = notEmpty(prj.getKeyValueAsJson<number>(ProjectCreatedTimestamp));\n const lastModified = notEmpty(prj.getKeyValueAsJson<number>(ProjectLastModifiedTimestamp));\n\n const meta = notEmpty(prj.getKeyValueAsJson<ProjectMeta>(ProjectMetaKey));\n const structure = notEmpty(prj.getKeyValueAsJson<ProjectStructure>(ProjectStructureKey));\n const renderingState = notEmpty(\n prj.getKeyValueAsJson<ProjectRenderingState>(BlockRenderingStateKey),\n );\n\n const infos = new Map<string, BlockInfo>();\n for (const { id } of allBlocks(structure)) {\n const cInputs = prj.traverse({\n field: projectFieldName(id, \"currentArgs\"),\n assertFieldType: \"Dynamic\",\n stableIfNotFound: true,\n });\n const currentArguments = cInputs?.getDataAsJson<Record<string, unknown>>();\n\n let prod: ProdState | undefined = undefined;\n\n const rInputs = prj.traverse({\n field: projectFieldName(id, \"prodArgs\"),\n assertFieldType: \"Dynamic\",\n stableIfNotFound: true,\n });\n if (rInputs !== undefined) {\n const prodArgs = rInputs.getDataAsJson() as Record<string, unknown>;\n const result = prj.getField({\n field: projectFieldName(id, \"prodOutput\"),\n assertFieldType: \"Dynamic\",\n errorIfFieldNotFound: true,\n });\n const ctx = prj.getField({\n field: projectFieldName(id, \"prodUiCtx\"),\n assertFieldType: \"Dynamic\",\n errorIfFieldNotFound: true,\n });\n prod = {\n arguments: prodArgs,\n stale: !argsEquals(currentArguments, prodArgs),\n outputError:\n result.error !== undefined ||\n ctx.error !== undefined ||\n result.value?.getError() !== undefined ||\n ctx.value?.getError() !== undefined,\n outputsError:\n result.error?.getDataAsString() ?? result.value?.getError()?.getDataAsString(),\n exportsError: ctx.error?.getDataAsString() ?? ctx.value?.getError()?.getDataAsString(),\n finished:\n ((result.value !== undefined && result.value.getIsReadyOrError()) ||\n (result.error !== undefined && result.error.getIsReadyOrError())) &&\n ((ctx.value !== undefined && ctx.value.getIsReadyOrError()) ||\n (ctx.error !== undefined && ctx.error.getIsReadyOrError())),\n };\n }\n\n infos.set(id, { currentArguments, prod, argsRid: cInputs?.resourceInfo.id });\n }\n\n const currentGraph = productionGraph(structure, (id) => {\n const bpInfo = getBlockPackInfo(prj, id)!;\n const bInfo = infos.get(id)!;\n const args = bInfo.currentArguments;\n return {\n args,\n enrichmentTargets: bInfo.argsRid\n ? env.projectHelper.getEnrichmentTargets(\n () => bpInfo.cfg,\n () => args,\n { argsRid: bInfo.argsRid, blockPackRid: bpInfo.bpResourceId },\n )\n : undefined,\n };\n });\n\n const limbo = new Set(renderingState.blocksInLimbo);\n\n const blocks = [...allBlocks(structure)].map(({ id, label: defaultLabel, renderingMode }) => {\n const info = notEmpty(infos.get(id));\n const gNode = notEmpty(currentGraph.nodes.get(id));\n let calculationStatus: BlockCalculationStatus = \"NotCalculated\";\n if (info.prod !== undefined) {\n if (limbo.has(id)) calculationStatus = \"Limbo\";\n else calculationStatus = info.prod.finished ? \"Done\" : \"Running\";\n }\n\n const bp = getBlockPackInfo(prj, id);\n\n const {\n sections,\n title,\n subtitle,\n tags,\n inputsValid,\n inputsValidFromArgs,\n sdkVersion,\n featureFlags,\n isIncompatibleWithRuntime,\n } =\n ifNotUndef(bp, ({ bpId, cfg, info: blockPackInfo }) => {\n if (!env.runtimeCapabilities.checkCompatibility(cfg.featureFlags)) {\n return {\n isIncompatibleWithRuntime: true,\n featureFlags: cfg.featureFlags,\n };\n }\n const blockCtx = constructBlockContext(prjEntry, id);\n const blockCtxArgsOnly = constructBlockContextArgsOnly(prjEntry, id);\n const codeWithInfoOrError = wrapCallback(() => extractCodeWithInfo(cfg));\n if (codeWithInfoOrError.error) {\n return {\n title: codeWithInfoOrError.error.message,\n isIncompatibleWithRuntime: true,\n featureFlags: cfg.featureFlags,\n };\n }\n const codeWithInfo = codeWithInfoOrError.value;\n return {\n sections: computableFromCfgOrRF(\n env,\n blockCtx,\n cfg.sections,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model sections\", { cause }));\n return [];\n },\n }) as ComputableStableDefined<BlockSection[]>,\n title: ifNotUndef(\n cfg.title,\n (title) =>\n computableFromCfgOrRF(\n env,\n blockCtxArgsOnly,\n title,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model title\", { cause }));\n return \"Invalid title\";\n },\n }) as ComputableStableDefined<string>,\n ),\n subtitle: ifNotUndef(\n cfg.subtitle,\n (subtitle) =>\n computableFromCfgOrRF(\n env,\n blockCtxArgsOnly,\n subtitle,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model subtitle\", { cause }));\n return \"Invalid subtitle\";\n },\n }) as ComputableStableDefined<string>,\n ),\n tags: ifNotUndef(\n cfg.tags,\n (tags) =>\n computableFromCfgOrRF(\n env,\n blockCtx,\n tags,\n codeWithInfo,\n bpId,\n {},\n blockPackInfo.source,\n ).wrap({\n recover: (cause) => {\n env.logger.error(new Error(\"Error in block model tags\", { cause }));\n return [];\n },\n }) as ComputableStableDefined<string[]>,\n ),\n // inputsValid: for modelAPIVersion 2, it's true if currentArgs exists (args derivation succeeded)\n // and the block's argsIssues() is known to have found no errors in them.\n // For older blocks, use the inputsValid callback from config\n inputsValid:\n cfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION\n ? argsAllowRun(prj, id, info.currentArguments)\n : cfg.inputsValid\n ? (computableFromCfgOrRF(\n env,\n blockCtxArgsOnly,\n cfg.inputsValid,\n codeWithInfo,\n bpId,\n ).wrap({\n recover: (cause) => {\n env.logger.error(\n new Error(\"Error in block model inputsValid\", { cause }),\n );\n return false;\n },\n }) as ComputableStableDefined<boolean>)\n : undefined,\n inputsValidFromArgs: cfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION,\n sdkVersion: codeWithInfo?.sdkVersion,\n featureFlags: codeWithInfo?.featureFlags ?? {},\n isIncompatibleWithRuntime: false,\n };\n }) || {};\n\n const settings = prj\n .traverse({\n field: projectFieldName(id, \"blockSettings\"),\n assertFieldType: \"Dynamic\",\n errorIfFieldNotSet: true,\n })\n .getDataAsJson() as BlockSettings;\n\n const updates = ifNotUndef(bp, ({ info }) =>\n env.blockUpdateWatcher.get({ currentSpec: info.source, settings }),\n );\n\n return {\n projectResourceId: resourceIdToString(prjEntry.rid),\n id,\n label: title ?? defaultLabel,\n title: title ?? defaultLabel,\n subtitle,\n tags,\n renderingMode,\n stale: info.prod?.stale !== false || calculationStatus === \"Limbo\",\n missingReference: gNode.missingReferences,\n upstreams: [...currentGraph.traverseIdsExcludingRoots(\"upstream\", id)],\n downstreams: [...currentGraph.traverseIdsExcludingRoots(\"downstream\", id)],\n calculationStatus,\n outputErrors: info.prod?.outputError === true,\n outputsError: info.prod?.outputsError,\n exportsError: info.prod?.exportsError,\n settings,\n sections,\n inputsValid,\n inputsValidFromArgs: inputsValidFromArgs === true,\n updateInfo: {},\n currentBlockPack: bp?.info?.source,\n updates,\n sdkVersion,\n featureFlags,\n isIncompatibleWithRuntime,\n navigationState: navigationStates.getState(id),\n };\n });\n\n return {\n meta,\n created: new Date(created),\n lastModified: new Date(lastModified),\n authorMarker: prj.getKeyValueAsJson<AuthorMarker>(ProjectStructureAuthorKey),\n blocks,\n };\n },\n {\n postprocessValue: (value) => ({\n ...value,\n blocks: withRunState(value.blocks).map(({ updates, inputsValidFromArgs: _, ...b }) => ({\n ...b,\n updateSuggestions: updates?.suggestions ?? [],\n updatedBlockPack: updates?.mainSuggestion,\n })),\n }),\n onRecalculation: getDebugFlags().logProjectOverviewStat\n ? (stats) => {\n console.log(`[projectOverview] ${JSON.stringify(stats)}`);\n }\n : undefined,\n },\n ).withStableType();\n}\n\n/**\n * Whether the args of a block with model API version 2 let it run: the block has current args,\n * and its `inputsValid` field records no errors its `argsIssues()` found in them (see\n * `readArgsErrorsField`).\n *\n * @param currentArgs - The block's current args as decoded from its `currentArgs` field;\n * `undefined` when it has none\n */\nexport function argsAllowRun(\n prj: PlTreeNodeAccessor,\n blockId: string,\n currentArgs: unknown,\n): boolean {\n if (currentArgs === undefined) return false;\n const argsErrors = prj.getField({\n field: projectFieldName(blockId, \"inputsValid\"),\n assertFieldType: \"Dynamic\",\n stableIfNotFound: true,\n });\n return readArgsErrorsField(argsErrors?.value?.getData(), currentArgs).length === 0;\n}\n\n/** What the run state of a block is derived from. */\nexport type RunStateSource = {\n id: string;\n /** Whether the block's own args differ from those its results were rendered with. */\n stale: boolean;\n outputErrors: boolean;\n inputsValid: boolean | undefined;\n /**\n * Whether `inputsValid` tells only whether the block's current args may run: a block with model\n * API version 2 this runtime is compatible with. Otherwise it also stands for a block whose code\n * cannot run here, or for checks of a model that predates args issues.\n */\n inputsValidFromArgs: boolean;\n missingReference: boolean;\n /** Every block upstream of this one, direct or not. */\n upstreams: string[];\n};\n\n/**\n * Adds the run state to each block of a project, the blocks listed upstream first. A block is\n * stale when its own args changed or an upstream block is stale. It can run when it needs to run\n * (it is stale or its outputs failed), its args allow it, it references no missing block, and no\n * upstream block that needs to run is kept from running. An upstream block whose `inputsValid`\n * comes from its args and whose results are up to date feeds its dependents even when its args\n * could not run again; any other upstream block that is not valid keeps them from running.\n */\nexport function withRunState<B extends RunStateSource>(\n blocks: B[],\n): (B & { stale: boolean; canRun: boolean })[] {\n const cantRun = new Set<string>();\n const staleBlocks = new Set<string>();\n return blocks.map((b) => {\n if (b.stale) staleBlocks.add(b.id);\n const stale = b.stale || b.upstreams.some((u) => staleBlocks.has(u));\n const needsRun = stale || b.outputErrors;\n if (!b.inputsValid && (needsRun || !b.inputsValidFromArgs)) cantRun.add(b.id);\n const canRun =\n needsRun &&\n Boolean(b.inputsValid) &&\n !b.missingReference &&\n !b.upstreams.some((u) => cantRun.has(u));\n return { ...b, stale, canRun };\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
let _platforma_sdk_model = require("@platforma-sdk/model");
|
|
2
|
+
let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
|
|
3
|
+
let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
|
|
4
|
+
let node_crypto = require("node:crypto");
|
|
5
|
+
let es_toolkit = require("es-toolkit");
|
|
6
|
+
// src/model/args_derivation.ts
|
|
7
|
+
/**
|
|
8
|
+
* Reads the result of the model's `__pl_args_derive` callback.
|
|
9
|
+
*
|
|
10
|
+
* A model built before args issues returns `{ value }` for args and `{ error }` for none; both
|
|
11
|
+
* read as they did then. A newer model adds the args issues, and returns `{ error, args, issues }`
|
|
12
|
+
* when its `argsIssues()` found errors. Args that are `undefined` are no args, whatever
|
|
13
|
+
* `argsIssues()` said.
|
|
14
|
+
*
|
|
15
|
+
* @throws when `value` is not an object, or carries an `error` that is not a string
|
|
16
|
+
*/
|
|
17
|
+
function parseArgsDeriveResult(value) {
|
|
18
|
+
if (typeof value !== "object" || value === null) throw new Error(`Args derivation returned ${value === null ? "null" : typeof value}`);
|
|
19
|
+
const error = "error" in value ? value.error : void 0;
|
|
20
|
+
if (error !== void 0) {
|
|
21
|
+
if (!(0, es_toolkit.isString)(error)) throw new Error(`Args derivation error is ${typeof error}, not a string`);
|
|
22
|
+
const args = "args" in value ? value.args : void 0;
|
|
23
|
+
if (args === void 0) return {
|
|
24
|
+
type: "absent",
|
|
25
|
+
error: new Error(error)
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
type: "blocked",
|
|
29
|
+
args,
|
|
30
|
+
errors: argsErrorsOf("issues" in value ? value.issues : void 0, error)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const args = "value" in value ? value.value : void 0;
|
|
34
|
+
if (args === void 0) return {
|
|
35
|
+
type: "absent",
|
|
36
|
+
error: /* @__PURE__ */ new Error("args() returned no args")
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
type: "runnable",
|
|
40
|
+
args
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** The args an args derivation produced; `undefined` when it produced none. */
|
|
44
|
+
function derivedArgsValue(derived) {
|
|
45
|
+
return derived.type === "absent" ? void 0 : derived.args;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Identifies a value of a block's current args by its content: the SHA-256 of its canonical JSON,
|
|
49
|
+
* in hex. Args that encode to the same JSON, whatever the key order, share a fingerprint.
|
|
50
|
+
*/
|
|
51
|
+
function argsFingerprint(args) {
|
|
52
|
+
return (0, node_crypto.createHash)("sha256").update((0, _milaboratories_ts_helpers.canonicalJsonBytes)(args)).digest("hex");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* What a block's `inputsValid` field must become after an args derivation, given the field as it
|
|
56
|
+
* is now.
|
|
57
|
+
*
|
|
58
|
+
* The field holds a {@link ArgsErrorsRecord} as canonical JSON, and exists while the block has
|
|
59
|
+
* args: with the errors its `argsIssues()` found in them, and with none when it found none. A
|
|
60
|
+
* field already holding the same record is kept as it is, so that readers of the project are not
|
|
61
|
+
* woken by a write that changes nothing.
|
|
62
|
+
*/
|
|
63
|
+
function argsErrorsFieldChange(current, derived) {
|
|
64
|
+
if (derived.type === "absent") return current === void 0 ? { type: "keep" } : { type: "delete" };
|
|
65
|
+
const record = {
|
|
66
|
+
argsFingerprint: argsFingerprint(derived.args),
|
|
67
|
+
errors: derived.type === "blocked" ? derived.errors : []
|
|
68
|
+
};
|
|
69
|
+
const data = (0, _milaboratories_ts_helpers.canonicalJsonBytes)(record);
|
|
70
|
+
if (current?.value !== void 0 && Buffer.compare(current.value, data) === 0) return { type: "keep" };
|
|
71
|
+
return {
|
|
72
|
+
type: "write",
|
|
73
|
+
data
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The errors a block's `inputsValid` field, as {@link argsErrorsFieldChange} writes it, records
|
|
78
|
+
* for the block's current args.
|
|
79
|
+
*
|
|
80
|
+
* A field that is missing, cannot be read, or was written for other args records none: the args
|
|
81
|
+
* were written by a middle layer that does not know the field. Such a middle layer refuses every
|
|
82
|
+
* block whose `argsIssues()` could find errors, so it wrote args only for blocks whose `args()`
|
|
83
|
+
* throws instead, and those args may run, as they always could. No current args hold no errors:
|
|
84
|
+
* without args the block may not run anyway.
|
|
85
|
+
*
|
|
86
|
+
* @param data - Content of the field; `undefined` when there is none, or while it is not loaded
|
|
87
|
+
* @param currentArgs - The block's current args as decoded from its `currentArgs` field;
|
|
88
|
+
* `undefined` when the block has none
|
|
89
|
+
*/
|
|
90
|
+
function readArgsErrorsField(data, currentArgs) {
|
|
91
|
+
if (currentArgs === void 0 || data === void 0) return [];
|
|
92
|
+
let record;
|
|
93
|
+
try {
|
|
94
|
+
record = parseArgsErrorsRecord((0, _milaboratories_ts_helpers.cachedDeserialize)(data));
|
|
95
|
+
} catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
return record.argsFingerprint === argsFingerprint(currentArgs) ? record.errors : [];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The errors found in a block's current args, read from its `inputsValid` field as
|
|
102
|
+
* {@link readArgsErrorsField} reads it.
|
|
103
|
+
*
|
|
104
|
+
* Only a block with model API version 2 has args errors. An earlier model tells whether the block
|
|
105
|
+
* may run by itself, and a field left over from a later model version does not apply to it: such
|
|
106
|
+
* a block has none.
|
|
107
|
+
*/
|
|
108
|
+
function argsErrorsOfBlock(block) {
|
|
109
|
+
const { modelAPIVersion, inputsValid, currentArgs } = block;
|
|
110
|
+
if (modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) return [];
|
|
111
|
+
return readArgsErrorsField(inputsValid?.value, currentArgs);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The errors among the issues of a derivation whose `argsIssues()` found errors. Issues that
|
|
115
|
+
* cannot be read, or that hold no error, give way to `error`, the joined messages the model
|
|
116
|
+
* reported.
|
|
117
|
+
*/
|
|
118
|
+
function argsErrorsOf(issues, error) {
|
|
119
|
+
try {
|
|
120
|
+
const errors = (0, _milaboratories_pl_model_common.parseArgsIssues)(issues).filter(isError);
|
|
121
|
+
if (errors.length > 0) return errors;
|
|
122
|
+
} catch {}
|
|
123
|
+
return [{
|
|
124
|
+
severity: "error",
|
|
125
|
+
message: error
|
|
126
|
+
}];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Reads a {@link ArgsErrorsRecord} from decoded field content. Issues other than errors are
|
|
130
|
+
* dropped.
|
|
131
|
+
*
|
|
132
|
+
* @throws when `value` is not an object carrying a string fingerprint and a list of issues
|
|
133
|
+
*/
|
|
134
|
+
function parseArgsErrorsRecord(value) {
|
|
135
|
+
if (typeof value !== "object" || value === null) throw new Error("Args errors record must be an object");
|
|
136
|
+
const fingerprint = "argsFingerprint" in value ? value.argsFingerprint : void 0;
|
|
137
|
+
if (!(0, es_toolkit.isString)(fingerprint)) throw new Error("Args errors record must carry a fingerprint");
|
|
138
|
+
return {
|
|
139
|
+
argsFingerprint: fingerprint,
|
|
140
|
+
errors: (0, _milaboratories_pl_model_common.parseArgsIssues)("errors" in value ? value.errors : void 0).filter(isError)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function isError(issue) {
|
|
144
|
+
return issue.severity === "error";
|
|
145
|
+
}
|
|
146
|
+
exports.argsErrorsFieldChange = argsErrorsFieldChange;
|
|
147
|
+
exports.argsErrorsOfBlock = argsErrorsOfBlock;
|
|
148
|
+
exports.argsFingerprint = argsFingerprint;
|
|
149
|
+
exports.derivedArgsValue = derivedArgsValue;
|
|
150
|
+
exports.parseArgsDeriveResult = parseArgsDeriveResult;
|
|
151
|
+
exports.readArgsErrorsField = readArgsErrorsField;
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=args_derivation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args_derivation.cjs","names":["isString","createHash","canonicalJsonBytes","cachedDeserialize","BLOCK_STORAGE_FACADE_VERSION","parseArgsIssues"],"sources":["../../src/model/args_derivation.ts"],"sourcesContent":["import { parseArgsIssues, type ArgsIssue } from \"@milaboratories/pl-model-common\";\nimport { cachedDeserialize, canonicalJsonBytes } from \"@milaboratories/ts-helpers\";\nimport { isString } from \"es-toolkit\";\nimport { createHash } from \"node:crypto\";\nimport { BLOCK_STORAGE_FACADE_VERSION } from \"@platforma-sdk/model\";\n\n/**\n * What the args derivation of a block with model API version 2 made of the block's storage.\n *\n * - `runnable`: the block has args and may run with them.\n * - `blocked`: the block has args, but its `argsIssues()` found errors in them, so it must not run.\n * `errors` is never empty.\n * - `absent`: the block has no args, because `args()` threw or returned nothing.\n */\nexport type DerivedArgs =\n | { readonly type: \"runnable\"; readonly args: unknown }\n | { readonly type: \"blocked\"; readonly args: unknown; readonly errors: readonly ArgsIssue[] }\n | { readonly type: \"absent\"; readonly error: Error };\n\n/**\n * What a block's `inputsValid` field holds: the errors found in the block's current args, and the\n * fingerprint of the args they were found in. Every args derivation that produces args writes\n * it, with no errors when the block may run.\n *\n * A middle layer that predates args issues never writes or deletes the field, so after it edits\n * the block, the field can be missing or outlive the args it was computed for. The fingerprint\n * tells such a stale field apart: it no longer matches the current args, and says nothing of them.\n */\nexport type ArgsErrorsRecord = {\n /** {@link argsFingerprint} of the current args the errors were found in. */\n readonly argsFingerprint: string;\n /** The errors; empty when the block may run with the args. */\n readonly errors: readonly ArgsIssue[];\n};\n\n/** How a block's `inputsValid` field must change to hold what an args derivation found. */\nexport type ArgsErrorsFieldChange =\n | { readonly type: \"keep\" }\n | { readonly type: \"write\"; readonly data: Uint8Array }\n | { readonly type: \"delete\" };\n\n/**\n * Reads the result of the model's `__pl_args_derive` callback.\n *\n * A model built before args issues returns `{ value }` for args and `{ error }` for none; both\n * read as they did then. A newer model adds the args issues, and returns `{ error, args, issues }`\n * when its `argsIssues()` found errors. Args that are `undefined` are no args, whatever\n * `argsIssues()` said.\n *\n * @throws when `value` is not an object, or carries an `error` that is not a string\n */\nexport function parseArgsDeriveResult(value: unknown): DerivedArgs {\n if (typeof value !== \"object\" || value === null) {\n throw new Error(`Args derivation returned ${value === null ? \"null\" : typeof value}`);\n }\n\n const error = \"error\" in value ? value.error : undefined;\n if (error !== undefined) {\n if (!isString(error)) throw new Error(`Args derivation error is ${typeof error}, not a string`);\n const args = \"args\" in value ? value.args : undefined;\n if (args === undefined) return { type: \"absent\", error: new Error(error) };\n const issues = \"issues\" in value ? value.issues : undefined;\n return { type: \"blocked\", args, errors: argsErrorsOf(issues, error) };\n }\n\n const args = \"value\" in value ? value.value : undefined;\n if (args === undefined) return { type: \"absent\", error: new Error(\"args() returned no args\") };\n return { type: \"runnable\", args };\n}\n\n/** The args an args derivation produced; `undefined` when it produced none. */\nexport function derivedArgsValue(derived: DerivedArgs): unknown {\n return derived.type === \"absent\" ? undefined : derived.args;\n}\n\n/**\n * Identifies a value of a block's current args by its content: the SHA-256 of its canonical JSON,\n * in hex. Args that encode to the same JSON, whatever the key order, share a fingerprint.\n */\nexport function argsFingerprint(args: unknown): string {\n return createHash(\"sha256\").update(canonicalJsonBytes(args)).digest(\"hex\");\n}\n\n/**\n * What a block's `inputsValid` field must become after an args derivation, given the field as it\n * is now.\n *\n * The field holds a {@link ArgsErrorsRecord} as canonical JSON, and exists while the block has\n * args: with the errors its `argsIssues()` found in them, and with none when it found none. A\n * field already holding the same record is kept as it is, so that readers of the project are not\n * woken by a write that changes nothing.\n */\nexport function argsErrorsFieldChange(\n current: { readonly value?: Uint8Array } | undefined,\n derived: DerivedArgs,\n): ArgsErrorsFieldChange {\n if (derived.type === \"absent\")\n return current === undefined ? { type: \"keep\" } : { type: \"delete\" };\n const record: ArgsErrorsRecord = {\n argsFingerprint: argsFingerprint(derived.args),\n errors: derived.type === \"blocked\" ? derived.errors : [],\n };\n const data = canonicalJsonBytes(record);\n if (current?.value !== undefined && Buffer.compare(current.value, data) === 0) {\n return { type: \"keep\" };\n }\n return { type: \"write\", data };\n}\n\n/**\n * The errors a block's `inputsValid` field, as {@link argsErrorsFieldChange} writes it, records\n * for the block's current args.\n *\n * A field that is missing, cannot be read, or was written for other args records none: the args\n * were written by a middle layer that does not know the field. Such a middle layer refuses every\n * block whose `argsIssues()` could find errors, so it wrote args only for blocks whose `args()`\n * throws instead, and those args may run, as they always could. No current args hold no errors:\n * without args the block may not run anyway.\n *\n * @param data - Content of the field; `undefined` when there is none, or while it is not loaded\n * @param currentArgs - The block's current args as decoded from its `currentArgs` field;\n * `undefined` when the block has none\n */\nexport function readArgsErrorsField(\n data: Uint8Array | undefined,\n currentArgs: unknown,\n): readonly ArgsIssue[] {\n if (currentArgs === undefined || data === undefined) return [];\n let record: ArgsErrorsRecord;\n try {\n record = parseArgsErrorsRecord(cachedDeserialize(data));\n } catch {\n return [];\n }\n return record.argsFingerprint === argsFingerprint(currentArgs) ? record.errors : [];\n}\n\n/**\n * The errors found in a block's current args, read from its `inputsValid` field as\n * {@link readArgsErrorsField} reads it.\n *\n * Only a block with model API version 2 has args errors. An earlier model tells whether the block\n * may run by itself, and a field left over from a later model version does not apply to it: such\n * a block has none.\n */\nexport function argsErrorsOfBlock(block: {\n /** Model API version of the block's current block pack. */\n readonly modelAPIVersion: number | undefined;\n /** The block's `inputsValid` field; `undefined` when there is none. */\n readonly inputsValid: { readonly value?: Uint8Array } | undefined;\n /** The block's current args; `undefined` when it has none. */\n readonly currentArgs: unknown;\n}): readonly ArgsIssue[] {\n const { modelAPIVersion, inputsValid, currentArgs } = block;\n if (modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) return [];\n return readArgsErrorsField(inputsValid?.value, currentArgs);\n}\n\n// Internals\n\n/**\n * The errors among the issues of a derivation whose `argsIssues()` found errors. Issues that\n * cannot be read, or that hold no error, give way to `error`, the joined messages the model\n * reported.\n */\nfunction argsErrorsOf(issues: unknown, error: string): readonly ArgsIssue[] {\n try {\n const errors = parseArgsIssues(issues).filter(isError);\n if (errors.length > 0) return errors;\n } catch {\n // An older or broken model: `error` alone tells why the block must not run.\n }\n return [{ severity: \"error\", message: error }];\n}\n\n/**\n * Reads a {@link ArgsErrorsRecord} from decoded field content. Issues other than errors are\n * dropped.\n *\n * @throws when `value` is not an object carrying a string fingerprint and a list of issues\n */\nfunction parseArgsErrorsRecord(value: unknown): ArgsErrorsRecord {\n if (typeof value !== \"object\" || value === null) {\n throw new Error(\"Args errors record must be an object\");\n }\n const fingerprint = \"argsFingerprint\" in value ? value.argsFingerprint : undefined;\n if (!isString(fingerprint)) throw new Error(\"Args errors record must carry a fingerprint\");\n const errors = parseArgsIssues(\"errors\" in value ? value.errors : undefined).filter(isError);\n return { argsFingerprint: fingerprint, errors };\n}\n\nfunction isError(issue: ArgsIssue): boolean {\n return issue.severity === \"error\";\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ArgsIssue } from "@milaboratories/pl-model-common";
|
|
2
|
+
/**
|
|
3
|
+
* What the args derivation of a block with model API version 2 made of the block's storage.
|
|
4
|
+
*
|
|
5
|
+
* - `runnable`: the block has args and may run with them.
|
|
6
|
+
* - `blocked`: the block has args, but its `argsIssues()` found errors in them, so it must not run.
|
|
7
|
+
* `errors` is never empty.
|
|
8
|
+
* - `absent`: the block has no args, because `args()` threw or returned nothing.
|
|
9
|
+
*/
|
|
10
|
+
export type DerivedArgs = {
|
|
11
|
+
readonly type: "runnable";
|
|
12
|
+
readonly args: unknown;
|
|
13
|
+
} | {
|
|
14
|
+
readonly type: "blocked";
|
|
15
|
+
readonly args: unknown;
|
|
16
|
+
readonly errors: readonly ArgsIssue[];
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "absent";
|
|
19
|
+
readonly error: Error;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=args_derivation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args_derivation.d.ts","names":[],"sources":["../../src/model/args_derivation.ts"],"mappings":";;;;;;;;;YAcY;WACG;WAA2B;;WAC3B;WAA0B;WAAwB,iBAAiB;;WACnE;WAAyB,OAAO"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { BLOCK_STORAGE_FACADE_VERSION } from "@platforma-sdk/model";
|
|
2
|
+
import { cachedDeserialize, canonicalJsonBytes } from "@milaboratories/ts-helpers";
|
|
3
|
+
import { parseArgsIssues } from "@milaboratories/pl-model-common";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { isString } from "es-toolkit";
|
|
6
|
+
// src/model/args_derivation.ts
|
|
7
|
+
/**
|
|
8
|
+
* Reads the result of the model's `__pl_args_derive` callback.
|
|
9
|
+
*
|
|
10
|
+
* A model built before args issues returns `{ value }` for args and `{ error }` for none; both
|
|
11
|
+
* read as they did then. A newer model adds the args issues, and returns `{ error, args, issues }`
|
|
12
|
+
* when its `argsIssues()` found errors. Args that are `undefined` are no args, whatever
|
|
13
|
+
* `argsIssues()` said.
|
|
14
|
+
*
|
|
15
|
+
* @throws when `value` is not an object, or carries an `error` that is not a string
|
|
16
|
+
*/
|
|
17
|
+
function parseArgsDeriveResult(value) {
|
|
18
|
+
if (typeof value !== "object" || value === null) throw new Error(`Args derivation returned ${value === null ? "null" : typeof value}`);
|
|
19
|
+
const error = "error" in value ? value.error : void 0;
|
|
20
|
+
if (error !== void 0) {
|
|
21
|
+
if (!isString(error)) throw new Error(`Args derivation error is ${typeof error}, not a string`);
|
|
22
|
+
const args = "args" in value ? value.args : void 0;
|
|
23
|
+
if (args === void 0) return {
|
|
24
|
+
type: "absent",
|
|
25
|
+
error: new Error(error)
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
type: "blocked",
|
|
29
|
+
args,
|
|
30
|
+
errors: argsErrorsOf("issues" in value ? value.issues : void 0, error)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const args = "value" in value ? value.value : void 0;
|
|
34
|
+
if (args === void 0) return {
|
|
35
|
+
type: "absent",
|
|
36
|
+
error: /* @__PURE__ */ new Error("args() returned no args")
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
type: "runnable",
|
|
40
|
+
args
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** The args an args derivation produced; `undefined` when it produced none. */
|
|
44
|
+
function derivedArgsValue(derived) {
|
|
45
|
+
return derived.type === "absent" ? void 0 : derived.args;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Identifies a value of a block's current args by its content: the SHA-256 of its canonical JSON,
|
|
49
|
+
* in hex. Args that encode to the same JSON, whatever the key order, share a fingerprint.
|
|
50
|
+
*/
|
|
51
|
+
function argsFingerprint(args) {
|
|
52
|
+
return createHash("sha256").update(canonicalJsonBytes(args)).digest("hex");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* What a block's `inputsValid` field must become after an args derivation, given the field as it
|
|
56
|
+
* is now.
|
|
57
|
+
*
|
|
58
|
+
* The field holds a {@link ArgsErrorsRecord} as canonical JSON, and exists while the block has
|
|
59
|
+
* args: with the errors its `argsIssues()` found in them, and with none when it found none. A
|
|
60
|
+
* field already holding the same record is kept as it is, so that readers of the project are not
|
|
61
|
+
* woken by a write that changes nothing.
|
|
62
|
+
*/
|
|
63
|
+
function argsErrorsFieldChange(current, derived) {
|
|
64
|
+
if (derived.type === "absent") return current === void 0 ? { type: "keep" } : { type: "delete" };
|
|
65
|
+
const record = {
|
|
66
|
+
argsFingerprint: argsFingerprint(derived.args),
|
|
67
|
+
errors: derived.type === "blocked" ? derived.errors : []
|
|
68
|
+
};
|
|
69
|
+
const data = canonicalJsonBytes(record);
|
|
70
|
+
if (current?.value !== void 0 && Buffer.compare(current.value, data) === 0) return { type: "keep" };
|
|
71
|
+
return {
|
|
72
|
+
type: "write",
|
|
73
|
+
data
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The errors a block's `inputsValid` field, as {@link argsErrorsFieldChange} writes it, records
|
|
78
|
+
* for the block's current args.
|
|
79
|
+
*
|
|
80
|
+
* A field that is missing, cannot be read, or was written for other args records none: the args
|
|
81
|
+
* were written by a middle layer that does not know the field. Such a middle layer refuses every
|
|
82
|
+
* block whose `argsIssues()` could find errors, so it wrote args only for blocks whose `args()`
|
|
83
|
+
* throws instead, and those args may run, as they always could. No current args hold no errors:
|
|
84
|
+
* without args the block may not run anyway.
|
|
85
|
+
*
|
|
86
|
+
* @param data - Content of the field; `undefined` when there is none, or while it is not loaded
|
|
87
|
+
* @param currentArgs - The block's current args as decoded from its `currentArgs` field;
|
|
88
|
+
* `undefined` when the block has none
|
|
89
|
+
*/
|
|
90
|
+
function readArgsErrorsField(data, currentArgs) {
|
|
91
|
+
if (currentArgs === void 0 || data === void 0) return [];
|
|
92
|
+
let record;
|
|
93
|
+
try {
|
|
94
|
+
record = parseArgsErrorsRecord(cachedDeserialize(data));
|
|
95
|
+
} catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
return record.argsFingerprint === argsFingerprint(currentArgs) ? record.errors : [];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The errors found in a block's current args, read from its `inputsValid` field as
|
|
102
|
+
* {@link readArgsErrorsField} reads it.
|
|
103
|
+
*
|
|
104
|
+
* Only a block with model API version 2 has args errors. An earlier model tells whether the block
|
|
105
|
+
* may run by itself, and a field left over from a later model version does not apply to it: such
|
|
106
|
+
* a block has none.
|
|
107
|
+
*/
|
|
108
|
+
function argsErrorsOfBlock(block) {
|
|
109
|
+
const { modelAPIVersion, inputsValid, currentArgs } = block;
|
|
110
|
+
if (modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) return [];
|
|
111
|
+
return readArgsErrorsField(inputsValid?.value, currentArgs);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The errors among the issues of a derivation whose `argsIssues()` found errors. Issues that
|
|
115
|
+
* cannot be read, or that hold no error, give way to `error`, the joined messages the model
|
|
116
|
+
* reported.
|
|
117
|
+
*/
|
|
118
|
+
function argsErrorsOf(issues, error) {
|
|
119
|
+
try {
|
|
120
|
+
const errors = parseArgsIssues(issues).filter(isError);
|
|
121
|
+
if (errors.length > 0) return errors;
|
|
122
|
+
} catch {}
|
|
123
|
+
return [{
|
|
124
|
+
severity: "error",
|
|
125
|
+
message: error
|
|
126
|
+
}];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Reads a {@link ArgsErrorsRecord} from decoded field content. Issues other than errors are
|
|
130
|
+
* dropped.
|
|
131
|
+
*
|
|
132
|
+
* @throws when `value` is not an object carrying a string fingerprint and a list of issues
|
|
133
|
+
*/
|
|
134
|
+
function parseArgsErrorsRecord(value) {
|
|
135
|
+
if (typeof value !== "object" || value === null) throw new Error("Args errors record must be an object");
|
|
136
|
+
const fingerprint = "argsFingerprint" in value ? value.argsFingerprint : void 0;
|
|
137
|
+
if (!isString(fingerprint)) throw new Error("Args errors record must carry a fingerprint");
|
|
138
|
+
return {
|
|
139
|
+
argsFingerprint: fingerprint,
|
|
140
|
+
errors: parseArgsIssues("errors" in value ? value.errors : void 0).filter(isError)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function isError(issue) {
|
|
144
|
+
return issue.severity === "error";
|
|
145
|
+
}
|
|
146
|
+
export { argsErrorsFieldChange, argsErrorsOfBlock, argsFingerprint, derivedArgsValue, parseArgsDeriveResult, readArgsErrorsField };
|
|
147
|
+
|
|
148
|
+
//# sourceMappingURL=args_derivation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args_derivation.js","names":[],"sources":["../../src/model/args_derivation.ts"],"sourcesContent":["import { parseArgsIssues, type ArgsIssue } from \"@milaboratories/pl-model-common\";\nimport { cachedDeserialize, canonicalJsonBytes } from \"@milaboratories/ts-helpers\";\nimport { isString } from \"es-toolkit\";\nimport { createHash } from \"node:crypto\";\nimport { BLOCK_STORAGE_FACADE_VERSION } from \"@platforma-sdk/model\";\n\n/**\n * What the args derivation of a block with model API version 2 made of the block's storage.\n *\n * - `runnable`: the block has args and may run with them.\n * - `blocked`: the block has args, but its `argsIssues()` found errors in them, so it must not run.\n * `errors` is never empty.\n * - `absent`: the block has no args, because `args()` threw or returned nothing.\n */\nexport type DerivedArgs =\n | { readonly type: \"runnable\"; readonly args: unknown }\n | { readonly type: \"blocked\"; readonly args: unknown; readonly errors: readonly ArgsIssue[] }\n | { readonly type: \"absent\"; readonly error: Error };\n\n/**\n * What a block's `inputsValid` field holds: the errors found in the block's current args, and the\n * fingerprint of the args they were found in. Every args derivation that produces args writes\n * it, with no errors when the block may run.\n *\n * A middle layer that predates args issues never writes or deletes the field, so after it edits\n * the block, the field can be missing or outlive the args it was computed for. The fingerprint\n * tells such a stale field apart: it no longer matches the current args, and says nothing of them.\n */\nexport type ArgsErrorsRecord = {\n /** {@link argsFingerprint} of the current args the errors were found in. */\n readonly argsFingerprint: string;\n /** The errors; empty when the block may run with the args. */\n readonly errors: readonly ArgsIssue[];\n};\n\n/** How a block's `inputsValid` field must change to hold what an args derivation found. */\nexport type ArgsErrorsFieldChange =\n | { readonly type: \"keep\" }\n | { readonly type: \"write\"; readonly data: Uint8Array }\n | { readonly type: \"delete\" };\n\n/**\n * Reads the result of the model's `__pl_args_derive` callback.\n *\n * A model built before args issues returns `{ value }` for args and `{ error }` for none; both\n * read as they did then. A newer model adds the args issues, and returns `{ error, args, issues }`\n * when its `argsIssues()` found errors. Args that are `undefined` are no args, whatever\n * `argsIssues()` said.\n *\n * @throws when `value` is not an object, or carries an `error` that is not a string\n */\nexport function parseArgsDeriveResult(value: unknown): DerivedArgs {\n if (typeof value !== \"object\" || value === null) {\n throw new Error(`Args derivation returned ${value === null ? \"null\" : typeof value}`);\n }\n\n const error = \"error\" in value ? value.error : undefined;\n if (error !== undefined) {\n if (!isString(error)) throw new Error(`Args derivation error is ${typeof error}, not a string`);\n const args = \"args\" in value ? value.args : undefined;\n if (args === undefined) return { type: \"absent\", error: new Error(error) };\n const issues = \"issues\" in value ? value.issues : undefined;\n return { type: \"blocked\", args, errors: argsErrorsOf(issues, error) };\n }\n\n const args = \"value\" in value ? value.value : undefined;\n if (args === undefined) return { type: \"absent\", error: new Error(\"args() returned no args\") };\n return { type: \"runnable\", args };\n}\n\n/** The args an args derivation produced; `undefined` when it produced none. */\nexport function derivedArgsValue(derived: DerivedArgs): unknown {\n return derived.type === \"absent\" ? undefined : derived.args;\n}\n\n/**\n * Identifies a value of a block's current args by its content: the SHA-256 of its canonical JSON,\n * in hex. Args that encode to the same JSON, whatever the key order, share a fingerprint.\n */\nexport function argsFingerprint(args: unknown): string {\n return createHash(\"sha256\").update(canonicalJsonBytes(args)).digest(\"hex\");\n}\n\n/**\n * What a block's `inputsValid` field must become after an args derivation, given the field as it\n * is now.\n *\n * The field holds a {@link ArgsErrorsRecord} as canonical JSON, and exists while the block has\n * args: with the errors its `argsIssues()` found in them, and with none when it found none. A\n * field already holding the same record is kept as it is, so that readers of the project are not\n * woken by a write that changes nothing.\n */\nexport function argsErrorsFieldChange(\n current: { readonly value?: Uint8Array } | undefined,\n derived: DerivedArgs,\n): ArgsErrorsFieldChange {\n if (derived.type === \"absent\")\n return current === undefined ? { type: \"keep\" } : { type: \"delete\" };\n const record: ArgsErrorsRecord = {\n argsFingerprint: argsFingerprint(derived.args),\n errors: derived.type === \"blocked\" ? derived.errors : [],\n };\n const data = canonicalJsonBytes(record);\n if (current?.value !== undefined && Buffer.compare(current.value, data) === 0) {\n return { type: \"keep\" };\n }\n return { type: \"write\", data };\n}\n\n/**\n * The errors a block's `inputsValid` field, as {@link argsErrorsFieldChange} writes it, records\n * for the block's current args.\n *\n * A field that is missing, cannot be read, or was written for other args records none: the args\n * were written by a middle layer that does not know the field. Such a middle layer refuses every\n * block whose `argsIssues()` could find errors, so it wrote args only for blocks whose `args()`\n * throws instead, and those args may run, as they always could. No current args hold no errors:\n * without args the block may not run anyway.\n *\n * @param data - Content of the field; `undefined` when there is none, or while it is not loaded\n * @param currentArgs - The block's current args as decoded from its `currentArgs` field;\n * `undefined` when the block has none\n */\nexport function readArgsErrorsField(\n data: Uint8Array | undefined,\n currentArgs: unknown,\n): readonly ArgsIssue[] {\n if (currentArgs === undefined || data === undefined) return [];\n let record: ArgsErrorsRecord;\n try {\n record = parseArgsErrorsRecord(cachedDeserialize(data));\n } catch {\n return [];\n }\n return record.argsFingerprint === argsFingerprint(currentArgs) ? record.errors : [];\n}\n\n/**\n * The errors found in a block's current args, read from its `inputsValid` field as\n * {@link readArgsErrorsField} reads it.\n *\n * Only a block with model API version 2 has args errors. An earlier model tells whether the block\n * may run by itself, and a field left over from a later model version does not apply to it: such\n * a block has none.\n */\nexport function argsErrorsOfBlock(block: {\n /** Model API version of the block's current block pack. */\n readonly modelAPIVersion: number | undefined;\n /** The block's `inputsValid` field; `undefined` when there is none. */\n readonly inputsValid: { readonly value?: Uint8Array } | undefined;\n /** The block's current args; `undefined` when it has none. */\n readonly currentArgs: unknown;\n}): readonly ArgsIssue[] {\n const { modelAPIVersion, inputsValid, currentArgs } = block;\n if (modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) return [];\n return readArgsErrorsField(inputsValid?.value, currentArgs);\n}\n\n// Internals\n\n/**\n * The errors among the issues of a derivation whose `argsIssues()` found errors. Issues that\n * cannot be read, or that hold no error, give way to `error`, the joined messages the model\n * reported.\n */\nfunction argsErrorsOf(issues: unknown, error: string): readonly ArgsIssue[] {\n try {\n const errors = parseArgsIssues(issues).filter(isError);\n if (errors.length > 0) return errors;\n } catch {\n // An older or broken model: `error` alone tells why the block must not run.\n }\n return [{ severity: \"error\", message: error }];\n}\n\n/**\n * Reads a {@link ArgsErrorsRecord} from decoded field content. Issues other than errors are\n * dropped.\n *\n * @throws when `value` is not an object carrying a string fingerprint and a list of issues\n */\nfunction parseArgsErrorsRecord(value: unknown): ArgsErrorsRecord {\n if (typeof value !== \"object\" || value === null) {\n throw new Error(\"Args errors record must be an object\");\n }\n const fingerprint = \"argsFingerprint\" in value ? value.argsFingerprint : undefined;\n if (!isString(fingerprint)) throw new Error(\"Args errors record must carry a fingerprint\");\n const errors = parseArgsIssues(\"errors\" in value ? value.errors : undefined).filter(isError);\n return { argsFingerprint: fingerprint, errors };\n}\n\nfunction isError(issue: ArgsIssue): boolean {\n return issue.severity === \"error\";\n}\n"],"mappings":"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"}
|