@platforma-sdk/model 1.78.0 → 1.78.2

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/package.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.78.0";
2
+ var version = "1.78.2";
3
3
  //#endregion
4
4
  Object.defineProperty(exports, "version", {
5
5
  enumerable: true,
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.78.0";
2
+ var version = "1.78.2";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -6,6 +6,19 @@ let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common")
6
6
  function ifDef(value, cb) {
7
7
  return value === void 0 ? void 0 : cb(value);
8
8
  }
9
+ /**
10
+ * Decode an error node's content into a display message. The backend serializes
11
+ * a resource error as `{"message": "..."}` (`ResourceError`); unwrap that to the
12
+ * human-readable message. Falls back to the raw string when the content is not
13
+ * that envelope (e.g. plain text, or an unexpected shape).
14
+ */
15
+ function decodeErrorMessage(raw) {
16
+ try {
17
+ const parsed = JSON.parse(raw);
18
+ if (typeof parsed?.message === "string") return parsed.message;
19
+ } catch {}
20
+ return raw;
21
+ }
9
22
  /** Represent resource tree node accessor */
10
23
  var TreeNodeAccessor = class TreeNodeAccessor {
11
24
  constructor(handle, resolvePath) {
@@ -91,21 +104,28 @@ var TreeNodeAccessor = class TreeNodeAccessor {
91
104
  return JSON.parse(content);
92
105
  }
93
106
  /**
94
- * Like {@link getDataAsJson}, but returns `undefined` while the resource is
95
- * still computing instead of throwing.
107
+ * Like {@link getDataAsJson}, but does not throw while the resource is still
108
+ * computing. Three states:
96
109
  *
97
- * Prefer this in reactive output lambdas. A field that resolves before its
98
- * resource is ready (routine on remote backends) makes {@link getDataAsJson}
99
- * throw "Resource has no content." mid-calculation, surfacing a transient
100
- * "Some outputs have errors" banner that clears once the resource finishes
101
- * (MILAB-6318).
110
+ * - **Not ready** → returns `undefined` (loading).
111
+ * - **Errored** → throws the resource's error.
112
+ * - **Ready** → returns the parsed content.
102
113
  *
103
- * A resource that has reached a terminal state (ready or error) but still
104
- * carries no content is a genuine error and throws, matching
105
- * {@link getDataAsJson}.
114
+ * Prefer this over {@link getDataAsJson} in reactive output lambdas: a field
115
+ * that resolves before its resource is ready (routine on remote backends)
116
+ * makes {@link getDataAsJson} throw "Resource has no content." mid-calculation,
117
+ * flashing a transient "Some outputs have errors" banner that clears when the
118
+ * resource finishes (MILAB-6318).
106
119
  */
107
120
  getDataAsJsonOrUndefined() {
108
121
  if (!this.getIsReadyOrError()) return void 0;
122
+ const error = this.getError();
123
+ if (error !== void 0) {
124
+ const raw = error.getDataAsString();
125
+ const message = raw === void 0 ? "Resource computation failed." : decodeErrorMessage(raw);
126
+ const path = this.resolvePath.join(".");
127
+ throw new Error(path ? `${message} (at ${path})` : message);
128
+ }
109
129
  return this.getDataAsJson();
110
130
  }
111
131
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"accessor.cjs","names":["getCfgRenderCtx","FutureRef"],"sources":["../../src/render/accessor.ts"],"sourcesContent":["import type {\n AnyLogHandle,\n ImportProgress,\n LocalBlobHandleAndSize,\n PColumn,\n PObject,\n RemoteBlobHandleAndSize,\n FolderURL,\n ArchiveFormat,\n ProgressLogWithInfo,\n RangeBytes,\n} from \"@milaboratories/pl-model-common\";\nimport { isPColumn, mapPObjectData } from \"@milaboratories/pl-model-common\";\nimport { getCfgRenderCtx } from \"../internal\";\nimport { FutureRef } from \"./future\";\nimport type { AccessorHandle } from \"./internal\";\nimport type { CommonFieldTraverseOps, FieldTraversalStep, ResourceType } from \"./traversal_ops\";\n\nexport function ifDef<T, R>(value: T | undefined, cb: (value: T) => R): R | undefined {\n return value === undefined ? undefined : cb(value);\n}\n\ntype FieldMapOps = {\n /**\n * Type of fields to iterate over.\n * (default 'Input')\n * */\n readonly fieldType?: \"Input\" | \"Output\" | \"Dynamic\";\n /**\n * If not locked, `undefined` value will be returned. Do nothing if mapping `Dynamic` fields.\n * (default true)\n * */\n readonly requireLocked?: boolean;\n /**\n * Will skip unresolved fields.\n * (default false)\n * */\n readonly skipUnresolved?: boolean;\n};\n\n/** Represent resource tree node accessor */\nexport class TreeNodeAccessor {\n constructor(\n public readonly handle: AccessorHandle,\n public readonly resolvePath: string[],\n ) {}\n\n /** Shortcut for {@link resolveInput} */\n public resolve(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** Shortcut for {@link resolveInput} */\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Output\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n public resolveAny(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n return this.resolveWithCommon({}, ...steps);\n }\n\n public resolveWithCommon(\n commonOptions: CommonFieldTraverseOps,\n ...steps: (FieldTraversalStep | string)[]\n ): TreeNodeAccessor | undefined {\n const resolvePath = [\n ...this.resolvePath,\n ...steps.map((step) => (typeof step === \"string\" ? step : step.field)),\n ];\n return ifDef(\n getCfgRenderCtx().resolveWithCommon(this.handle, commonOptions, ...steps),\n (accessor) => new TreeNodeAccessor(accessor, resolvePath),\n );\n }\n\n public get resourceType(): ResourceType {\n return getCfgRenderCtx().getResourceType(this.handle);\n }\n\n public getInputsLocked(): boolean {\n return getCfgRenderCtx().getInputsLocked(this.handle);\n }\n\n public getOutputsLocked(): boolean {\n return getCfgRenderCtx().getOutputsLocked(this.handle);\n }\n\n public getIsReadyOrError(): boolean {\n return getCfgRenderCtx().getIsReadyOrError(this.handle);\n }\n\n public getIsFinal(): boolean {\n return getCfgRenderCtx().getIsFinal(this.handle);\n }\n\n public getError(): TreeNodeAccessor | undefined {\n const resolvePath = [...this.resolvePath, \"error\"];\n return ifDef(\n getCfgRenderCtx().getError(this.handle),\n (accsessor) => new TreeNodeAccessor(accsessor, resolvePath),\n );\n }\n\n public listInputFields(): string[] {\n return getCfgRenderCtx().listInputFields(this.handle);\n }\n\n public listOutputFields(): string[] {\n return getCfgRenderCtx().listOutputFields(this.handle);\n }\n\n public listDynamicFields(): string[] {\n return getCfgRenderCtx().listDynamicFields(this.handle);\n }\n\n public getKeyValueBase64(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueBase64(this.handle, key);\n }\n\n public getKeyValueAsString(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueAsString(this.handle, key);\n }\n\n public getKeyValueAsJson<T>(key: string): T {\n const content = this.getKeyValueAsString(key);\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n public getDataBase64(): string | undefined {\n return getCfgRenderCtx().getDataBase64(this.handle);\n }\n\n public getDataAsString(): string | undefined {\n return getCfgRenderCtx().getDataAsString(this.handle);\n }\n\n public getDataAsJson<T>(): T {\n const content = this.getDataAsString();\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n /**\n * Like {@link getDataAsJson}, but returns `undefined` while the resource is\n * still computing instead of throwing.\n *\n * Prefer this in reactive output lambdas. A field that resolves before its\n * resource is ready (routine on remote backends) makes {@link getDataAsJson}\n * throw \"Resource has no content.\" mid-calculation, surfacing a transient\n * \"Some outputs have errors\" banner that clears once the resource finishes\n * (MILAB-6318).\n *\n * A resource that has reached a terminal state (ready or error) but still\n * carries no content is a genuine error and throws, matching\n * {@link getDataAsJson}.\n */\n public getDataAsJsonOrUndefined<T>(): T | undefined {\n // getIsReadyOrError() marks the computable unstable while not ready, which\n // schedules recomputation once the resource finishes. Load-bearing, not a\n // redundant guard over getDataAsJson — keep it.\n if (!this.getIsReadyOrError()) return undefined;\n return this.getDataAsJson<T>();\n }\n\n /**\n *\n */\n public getPColumns(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): PColumn<TreeNodeAccessor>[] | undefined {\n const result = this.parsePObjectCollection(errorOnUnknownField, prefix);\n if (result === undefined) return undefined;\n\n const pf = Object.entries(result).map(([, obj]) => {\n if (!isPColumn(obj)) throw new Error(`not a PColumn (kind = ${obj.spec.kind})`);\n return obj;\n });\n\n return pf;\n }\n\n /**\n *\n */\n public parsePObjectCollection(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): Record<string, PObject<TreeNodeAccessor>> | undefined {\n const pObjects = getCfgRenderCtx().parsePObjectCollection(\n this.handle,\n errorOnUnknownField,\n prefix,\n ...this.resolvePath,\n );\n if (pObjects === undefined) return undefined;\n const result: Record<string, PObject<TreeNodeAccessor>> = {};\n for (const [key, value] of Object.entries(pObjects)) {\n const resolvePath = [...this.resolvePath, key];\n result[key] = mapPObjectData(value, (c) => new TreeNodeAccessor(c, resolvePath));\n }\n return result;\n }\n\n public getFileContentAsBase64(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsBase64(this.handle, range));\n }\n\n public getFileContentAsString(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range));\n }\n\n public getFileContentAsJson<T>(range?: RangeBytes): FutureRef<T | undefined> {\n return new FutureRef<string | undefined>(\n getCfgRenderCtx().getBlobContentAsString(this.handle, range),\n ).mapDefined((v) => JSON.parse(v) as T);\n }\n\n /**\n * @deprecated use getFileContentAsBase64\n */\n public getBlobContentAsBase64(): FutureRef<string | undefined> {\n return this.getFileContentAsBase64();\n }\n\n /**\n * @deprecated use getFileContentAsString\n */\n public getBlobContentAsString(): FutureRef<string | undefined> {\n return this.getFileContentAsString();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getFileHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getDownloadedBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getFileHandle\n */\n public getDownloadedBlobHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return this.getFileHandle();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getRemoteFileHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getOnDemandBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getRemoteFileHandle\n */\n public getOnDemandBlobHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return this.getRemoteFileHandle();\n }\n\n /**\n * @returns the url to the extracted folder\n */\n public extractArchiveAndGetURL(format: ArchiveFormat): FutureRef<FolderURL | undefined> {\n return new FutureRef(getCfgRenderCtx().extractArchiveAndGetURL(this.handle, format));\n }\n\n public getImportProgress(): FutureRef<ImportProgress> {\n return new FutureRef(getCfgRenderCtx().getImportProgress(this.handle));\n }\n\n public getLastLogs(nLines: number): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getLastLogs(this.handle, nLines));\n }\n\n public getProgressLog(patternToSearch: string): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLog(this.handle, patternToSearch));\n }\n\n public getProgressLogWithInfo(\n patternToSearch: string,\n ): FutureRef<ProgressLogWithInfo | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLogWithInfo(this.handle, patternToSearch));\n }\n\n public getLogHandle(): FutureRef<AnyLogHandle | undefined> {\n return new FutureRef(getCfgRenderCtx().getLogHandle(this.handle));\n }\n\n public allFieldsResolved(fieldType: \"Input\" | \"Output\" = \"Input\"): boolean {\n switch (fieldType) {\n case \"Input\":\n return (\n this.getInputsLocked() &&\n this.listInputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Input\" }) !== undefined,\n )\n );\n case \"Output\":\n return (\n this.getOutputsLocked() &&\n this.listOutputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Output\" }) !== undefined,\n )\n );\n }\n }\n\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops: FieldMapOps & { skipUnresolved: true },\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor | undefined) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined {\n const { fieldType, requireLocked, skipUnresolved } = {\n fieldType: \"Input\" as const,\n requireLocked: true,\n skipUnresolved: false,\n ..._ops,\n };\n const mapping = _mapping as (name: string, value: TreeNodeAccessor | undefined) => T;\n if (requireLocked) {\n if (fieldType === \"Input\" && !this.getInputsLocked()) return undefined;\n if (fieldType === \"Output\" && !this.getOutputsLocked()) return undefined;\n }\n const fieldList =\n fieldType === \"Input\"\n ? this.listInputFields()\n : fieldType === \"Output\"\n ? this.listOutputFields()\n : this.listDynamicFields();\n let fieldEntries = fieldList.map(\n (field) => [field, this.resolve({ field, assertFieldType: fieldType })] as const,\n );\n if (skipUnresolved) fieldEntries = fieldEntries.filter((e) => e[1] !== undefined);\n return fieldEntries.map(([name, value]) => mapping(name, value));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"accessor.cjs","names":["getCfgRenderCtx","FutureRef"],"sources":["../../src/render/accessor.ts"],"sourcesContent":["import type {\n AnyLogHandle,\n ImportProgress,\n LocalBlobHandleAndSize,\n PColumn,\n PObject,\n RemoteBlobHandleAndSize,\n FolderURL,\n ArchiveFormat,\n ProgressLogWithInfo,\n RangeBytes,\n} from \"@milaboratories/pl-model-common\";\nimport { isPColumn, mapPObjectData } from \"@milaboratories/pl-model-common\";\nimport { getCfgRenderCtx } from \"../internal\";\nimport { FutureRef } from \"./future\";\nimport type { AccessorHandle } from \"./internal\";\nimport type { CommonFieldTraverseOps, FieldTraversalStep, ResourceType } from \"./traversal_ops\";\n\nexport function ifDef<T, R>(value: T | undefined, cb: (value: T) => R): R | undefined {\n return value === undefined ? undefined : cb(value);\n}\n\n/**\n * Decode an error node's content into a display message. The backend serializes\n * a resource error as `{\"message\": \"...\"}` (`ResourceError`); unwrap that to the\n * human-readable message. Falls back to the raw string when the content is not\n * that envelope (e.g. plain text, or an unexpected shape).\n */\nfunction decodeErrorMessage(raw: string): string {\n try {\n const parsed = JSON.parse(raw) as { message?: unknown };\n if (typeof parsed?.message === \"string\") return parsed.message;\n } catch {\n // Not JSON — surface the raw content.\n }\n return raw;\n}\n\ntype FieldMapOps = {\n /**\n * Type of fields to iterate over.\n * (default 'Input')\n * */\n readonly fieldType?: \"Input\" | \"Output\" | \"Dynamic\";\n /**\n * If not locked, `undefined` value will be returned. Do nothing if mapping `Dynamic` fields.\n * (default true)\n * */\n readonly requireLocked?: boolean;\n /**\n * Will skip unresolved fields.\n * (default false)\n * */\n readonly skipUnresolved?: boolean;\n};\n\n/** Represent resource tree node accessor */\nexport class TreeNodeAccessor {\n constructor(\n public readonly handle: AccessorHandle,\n public readonly resolvePath: string[],\n ) {}\n\n /** Shortcut for {@link resolveInput} */\n public resolve(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** Shortcut for {@link resolveInput} */\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Output\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n public resolveAny(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n return this.resolveWithCommon({}, ...steps);\n }\n\n public resolveWithCommon(\n commonOptions: CommonFieldTraverseOps,\n ...steps: (FieldTraversalStep | string)[]\n ): TreeNodeAccessor | undefined {\n const resolvePath = [\n ...this.resolvePath,\n ...steps.map((step) => (typeof step === \"string\" ? step : step.field)),\n ];\n return ifDef(\n getCfgRenderCtx().resolveWithCommon(this.handle, commonOptions, ...steps),\n (accessor) => new TreeNodeAccessor(accessor, resolvePath),\n );\n }\n\n public get resourceType(): ResourceType {\n return getCfgRenderCtx().getResourceType(this.handle);\n }\n\n public getInputsLocked(): boolean {\n return getCfgRenderCtx().getInputsLocked(this.handle);\n }\n\n public getOutputsLocked(): boolean {\n return getCfgRenderCtx().getOutputsLocked(this.handle);\n }\n\n public getIsReadyOrError(): boolean {\n return getCfgRenderCtx().getIsReadyOrError(this.handle);\n }\n\n public getIsFinal(): boolean {\n return getCfgRenderCtx().getIsFinal(this.handle);\n }\n\n public getError(): TreeNodeAccessor | undefined {\n const resolvePath = [...this.resolvePath, \"error\"];\n return ifDef(\n getCfgRenderCtx().getError(this.handle),\n (accsessor) => new TreeNodeAccessor(accsessor, resolvePath),\n );\n }\n\n public listInputFields(): string[] {\n return getCfgRenderCtx().listInputFields(this.handle);\n }\n\n public listOutputFields(): string[] {\n return getCfgRenderCtx().listOutputFields(this.handle);\n }\n\n public listDynamicFields(): string[] {\n return getCfgRenderCtx().listDynamicFields(this.handle);\n }\n\n public getKeyValueBase64(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueBase64(this.handle, key);\n }\n\n public getKeyValueAsString(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueAsString(this.handle, key);\n }\n\n public getKeyValueAsJson<T>(key: string): T {\n const content = this.getKeyValueAsString(key);\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n public getDataBase64(): string | undefined {\n return getCfgRenderCtx().getDataBase64(this.handle);\n }\n\n public getDataAsString(): string | undefined {\n return getCfgRenderCtx().getDataAsString(this.handle);\n }\n\n public getDataAsJson<T>(): T {\n const content = this.getDataAsString();\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n /**\n * Like {@link getDataAsJson}, but does not throw while the resource is still\n * computing. Three states:\n *\n * - **Not ready** → returns `undefined` (loading).\n * - **Errored** → throws the resource's error.\n * - **Ready** → returns the parsed content.\n *\n * Prefer this over {@link getDataAsJson} in reactive output lambdas: a field\n * that resolves before its resource is ready (routine on remote backends)\n * makes {@link getDataAsJson} throw \"Resource has no content.\" mid-calculation,\n * flashing a transient \"Some outputs have errors\" banner that clears when the\n * resource finishes (MILAB-6318).\n */\n public getDataAsJsonOrUndefined<T>(): T | undefined {\n // Not ready → undefined (loading). getIsReadyOrError() registers the\n // readiness dependency, so the lambda re-runs once the resource finishes.\n if (!this.getIsReadyOrError()) return undefined;\n // Errored → throw the actual error, decoded from the error node and tagged\n // with the resolve path for debugging context. Beats getDataAsJson's\n // generic \"Resource has no content.\"\n const error = this.getError();\n if (error !== undefined) {\n const raw = error.getDataAsString();\n const message = raw === undefined ? \"Resource computation failed.\" : decodeErrorMessage(raw);\n const path = this.resolvePath.join(\".\");\n throw new Error(path ? `${message} (at ${path})` : message);\n }\n // Ready → parse the content.\n return this.getDataAsJson<T>();\n }\n\n /**\n *\n */\n public getPColumns(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): PColumn<TreeNodeAccessor>[] | undefined {\n const result = this.parsePObjectCollection(errorOnUnknownField, prefix);\n if (result === undefined) return undefined;\n\n const pf = Object.entries(result).map(([, obj]) => {\n if (!isPColumn(obj)) throw new Error(`not a PColumn (kind = ${obj.spec.kind})`);\n return obj;\n });\n\n return pf;\n }\n\n /**\n *\n */\n public parsePObjectCollection(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): Record<string, PObject<TreeNodeAccessor>> | undefined {\n const pObjects = getCfgRenderCtx().parsePObjectCollection(\n this.handle,\n errorOnUnknownField,\n prefix,\n ...this.resolvePath,\n );\n if (pObjects === undefined) return undefined;\n const result: Record<string, PObject<TreeNodeAccessor>> = {};\n for (const [key, value] of Object.entries(pObjects)) {\n const resolvePath = [...this.resolvePath, key];\n result[key] = mapPObjectData(value, (c) => new TreeNodeAccessor(c, resolvePath));\n }\n return result;\n }\n\n public getFileContentAsBase64(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsBase64(this.handle, range));\n }\n\n public getFileContentAsString(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range));\n }\n\n public getFileContentAsJson<T>(range?: RangeBytes): FutureRef<T | undefined> {\n return new FutureRef<string | undefined>(\n getCfgRenderCtx().getBlobContentAsString(this.handle, range),\n ).mapDefined((v) => JSON.parse(v) as T);\n }\n\n /**\n * @deprecated use getFileContentAsBase64\n */\n public getBlobContentAsBase64(): FutureRef<string | undefined> {\n return this.getFileContentAsBase64();\n }\n\n /**\n * @deprecated use getFileContentAsString\n */\n public getBlobContentAsString(): FutureRef<string | undefined> {\n return this.getFileContentAsString();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getFileHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getDownloadedBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getFileHandle\n */\n public getDownloadedBlobHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return this.getFileHandle();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getRemoteFileHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getOnDemandBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getRemoteFileHandle\n */\n public getOnDemandBlobHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return this.getRemoteFileHandle();\n }\n\n /**\n * @returns the url to the extracted folder\n */\n public extractArchiveAndGetURL(format: ArchiveFormat): FutureRef<FolderURL | undefined> {\n return new FutureRef(getCfgRenderCtx().extractArchiveAndGetURL(this.handle, format));\n }\n\n public getImportProgress(): FutureRef<ImportProgress> {\n return new FutureRef(getCfgRenderCtx().getImportProgress(this.handle));\n }\n\n public getLastLogs(nLines: number): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getLastLogs(this.handle, nLines));\n }\n\n public getProgressLog(patternToSearch: string): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLog(this.handle, patternToSearch));\n }\n\n public getProgressLogWithInfo(\n patternToSearch: string,\n ): FutureRef<ProgressLogWithInfo | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLogWithInfo(this.handle, patternToSearch));\n }\n\n public getLogHandle(): FutureRef<AnyLogHandle | undefined> {\n return new FutureRef(getCfgRenderCtx().getLogHandle(this.handle));\n }\n\n public allFieldsResolved(fieldType: \"Input\" | \"Output\" = \"Input\"): boolean {\n switch (fieldType) {\n case \"Input\":\n return (\n this.getInputsLocked() &&\n this.listInputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Input\" }) !== undefined,\n )\n );\n case \"Output\":\n return (\n this.getOutputsLocked() &&\n this.listOutputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Output\" }) !== undefined,\n )\n );\n }\n }\n\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops: FieldMapOps & { skipUnresolved: true },\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor | undefined) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined {\n const { fieldType, requireLocked, skipUnresolved } = {\n fieldType: \"Input\" as const,\n requireLocked: true,\n skipUnresolved: false,\n ..._ops,\n };\n const mapping = _mapping as (name: string, value: TreeNodeAccessor | undefined) => T;\n if (requireLocked) {\n if (fieldType === \"Input\" && !this.getInputsLocked()) return undefined;\n if (fieldType === \"Output\" && !this.getOutputsLocked()) return undefined;\n }\n const fieldList =\n fieldType === \"Input\"\n ? this.listInputFields()\n : fieldType === \"Output\"\n ? this.listOutputFields()\n : this.listDynamicFields();\n let fieldEntries = fieldList.map(\n (field) => [field, this.resolve({ field, assertFieldType: fieldType })] as const,\n );\n if (skipUnresolved) fieldEntries = fieldEntries.filter((e) => e[1] !== undefined);\n return fieldEntries.map(([name, value]) => mapping(name, value));\n }\n}\n"],"mappings":"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"}
@@ -66,18 +66,18 @@ declare class TreeNodeAccessor {
66
66
  getDataAsString(): string | undefined;
67
67
  getDataAsJson<T>(): T;
68
68
  /**
69
- * Like {@link getDataAsJson}, but returns `undefined` while the resource is
70
- * still computing instead of throwing.
69
+ * Like {@link getDataAsJson}, but does not throw while the resource is still
70
+ * computing. Three states:
71
71
  *
72
- * Prefer this in reactive output lambdas. A field that resolves before its
73
- * resource is ready (routine on remote backends) makes {@link getDataAsJson}
74
- * throw "Resource has no content." mid-calculation, surfacing a transient
75
- * "Some outputs have errors" banner that clears once the resource finishes
76
- * (MILAB-6318).
72
+ * - **Not ready** → returns `undefined` (loading).
73
+ * - **Errored** → throws the resource's error.
74
+ * - **Ready** → returns the parsed content.
77
75
  *
78
- * A resource that has reached a terminal state (ready or error) but still
79
- * carries no content is a genuine error and throws, matching
80
- * {@link getDataAsJson}.
76
+ * Prefer this over {@link getDataAsJson} in reactive output lambdas: a field
77
+ * that resolves before its resource is ready (routine on remote backends)
78
+ * makes {@link getDataAsJson} throw "Resource has no content." mid-calculation,
79
+ * flashing a transient "Some outputs have errors" banner that clears when the
80
+ * resource finishes (MILAB-6318).
81
81
  */
82
82
  getDataAsJsonOrUndefined<T>(): T | undefined;
83
83
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"accessor.d.ts","names":[],"sources":["../../src/render/accessor.ts"],"mappings":"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"}
1
+ {"version":3,"file":"accessor.d.ts","names":[],"sources":["../../src/render/accessor.ts"],"mappings":"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"}
@@ -5,6 +5,19 @@ import { isPColumn, mapPObjectData } from "@milaboratories/pl-model-common";
5
5
  function ifDef(value, cb) {
6
6
  return value === void 0 ? void 0 : cb(value);
7
7
  }
8
+ /**
9
+ * Decode an error node's content into a display message. The backend serializes
10
+ * a resource error as `{"message": "..."}` (`ResourceError`); unwrap that to the
11
+ * human-readable message. Falls back to the raw string when the content is not
12
+ * that envelope (e.g. plain text, or an unexpected shape).
13
+ */
14
+ function decodeErrorMessage(raw) {
15
+ try {
16
+ const parsed = JSON.parse(raw);
17
+ if (typeof parsed?.message === "string") return parsed.message;
18
+ } catch {}
19
+ return raw;
20
+ }
8
21
  /** Represent resource tree node accessor */
9
22
  var TreeNodeAccessor = class TreeNodeAccessor {
10
23
  constructor(handle, resolvePath) {
@@ -90,21 +103,28 @@ var TreeNodeAccessor = class TreeNodeAccessor {
90
103
  return JSON.parse(content);
91
104
  }
92
105
  /**
93
- * Like {@link getDataAsJson}, but returns `undefined` while the resource is
94
- * still computing instead of throwing.
106
+ * Like {@link getDataAsJson}, but does not throw while the resource is still
107
+ * computing. Three states:
95
108
  *
96
- * Prefer this in reactive output lambdas. A field that resolves before its
97
- * resource is ready (routine on remote backends) makes {@link getDataAsJson}
98
- * throw "Resource has no content." mid-calculation, surfacing a transient
99
- * "Some outputs have errors" banner that clears once the resource finishes
100
- * (MILAB-6318).
109
+ * - **Not ready** → returns `undefined` (loading).
110
+ * - **Errored** → throws the resource's error.
111
+ * - **Ready** → returns the parsed content.
101
112
  *
102
- * A resource that has reached a terminal state (ready or error) but still
103
- * carries no content is a genuine error and throws, matching
104
- * {@link getDataAsJson}.
113
+ * Prefer this over {@link getDataAsJson} in reactive output lambdas: a field
114
+ * that resolves before its resource is ready (routine on remote backends)
115
+ * makes {@link getDataAsJson} throw "Resource has no content." mid-calculation,
116
+ * flashing a transient "Some outputs have errors" banner that clears when the
117
+ * resource finishes (MILAB-6318).
105
118
  */
106
119
  getDataAsJsonOrUndefined() {
107
120
  if (!this.getIsReadyOrError()) return void 0;
121
+ const error = this.getError();
122
+ if (error !== void 0) {
123
+ const raw = error.getDataAsString();
124
+ const message = raw === void 0 ? "Resource computation failed." : decodeErrorMessage(raw);
125
+ const path = this.resolvePath.join(".");
126
+ throw new Error(path ? `${message} (at ${path})` : message);
127
+ }
108
128
  return this.getDataAsJson();
109
129
  }
110
130
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"accessor.js","names":[],"sources":["../../src/render/accessor.ts"],"sourcesContent":["import type {\n AnyLogHandle,\n ImportProgress,\n LocalBlobHandleAndSize,\n PColumn,\n PObject,\n RemoteBlobHandleAndSize,\n FolderURL,\n ArchiveFormat,\n ProgressLogWithInfo,\n RangeBytes,\n} from \"@milaboratories/pl-model-common\";\nimport { isPColumn, mapPObjectData } from \"@milaboratories/pl-model-common\";\nimport { getCfgRenderCtx } from \"../internal\";\nimport { FutureRef } from \"./future\";\nimport type { AccessorHandle } from \"./internal\";\nimport type { CommonFieldTraverseOps, FieldTraversalStep, ResourceType } from \"./traversal_ops\";\n\nexport function ifDef<T, R>(value: T | undefined, cb: (value: T) => R): R | undefined {\n return value === undefined ? undefined : cb(value);\n}\n\ntype FieldMapOps = {\n /**\n * Type of fields to iterate over.\n * (default 'Input')\n * */\n readonly fieldType?: \"Input\" | \"Output\" | \"Dynamic\";\n /**\n * If not locked, `undefined` value will be returned. Do nothing if mapping `Dynamic` fields.\n * (default true)\n * */\n readonly requireLocked?: boolean;\n /**\n * Will skip unresolved fields.\n * (default false)\n * */\n readonly skipUnresolved?: boolean;\n};\n\n/** Represent resource tree node accessor */\nexport class TreeNodeAccessor {\n constructor(\n public readonly handle: AccessorHandle,\n public readonly resolvePath: string[],\n ) {}\n\n /** Shortcut for {@link resolveInput} */\n public resolve(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** Shortcut for {@link resolveInput} */\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Output\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n public resolveAny(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n return this.resolveWithCommon({}, ...steps);\n }\n\n public resolveWithCommon(\n commonOptions: CommonFieldTraverseOps,\n ...steps: (FieldTraversalStep | string)[]\n ): TreeNodeAccessor | undefined {\n const resolvePath = [\n ...this.resolvePath,\n ...steps.map((step) => (typeof step === \"string\" ? step : step.field)),\n ];\n return ifDef(\n getCfgRenderCtx().resolveWithCommon(this.handle, commonOptions, ...steps),\n (accessor) => new TreeNodeAccessor(accessor, resolvePath),\n );\n }\n\n public get resourceType(): ResourceType {\n return getCfgRenderCtx().getResourceType(this.handle);\n }\n\n public getInputsLocked(): boolean {\n return getCfgRenderCtx().getInputsLocked(this.handle);\n }\n\n public getOutputsLocked(): boolean {\n return getCfgRenderCtx().getOutputsLocked(this.handle);\n }\n\n public getIsReadyOrError(): boolean {\n return getCfgRenderCtx().getIsReadyOrError(this.handle);\n }\n\n public getIsFinal(): boolean {\n return getCfgRenderCtx().getIsFinal(this.handle);\n }\n\n public getError(): TreeNodeAccessor | undefined {\n const resolvePath = [...this.resolvePath, \"error\"];\n return ifDef(\n getCfgRenderCtx().getError(this.handle),\n (accsessor) => new TreeNodeAccessor(accsessor, resolvePath),\n );\n }\n\n public listInputFields(): string[] {\n return getCfgRenderCtx().listInputFields(this.handle);\n }\n\n public listOutputFields(): string[] {\n return getCfgRenderCtx().listOutputFields(this.handle);\n }\n\n public listDynamicFields(): string[] {\n return getCfgRenderCtx().listDynamicFields(this.handle);\n }\n\n public getKeyValueBase64(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueBase64(this.handle, key);\n }\n\n public getKeyValueAsString(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueAsString(this.handle, key);\n }\n\n public getKeyValueAsJson<T>(key: string): T {\n const content = this.getKeyValueAsString(key);\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n public getDataBase64(): string | undefined {\n return getCfgRenderCtx().getDataBase64(this.handle);\n }\n\n public getDataAsString(): string | undefined {\n return getCfgRenderCtx().getDataAsString(this.handle);\n }\n\n public getDataAsJson<T>(): T {\n const content = this.getDataAsString();\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n /**\n * Like {@link getDataAsJson}, but returns `undefined` while the resource is\n * still computing instead of throwing.\n *\n * Prefer this in reactive output lambdas. A field that resolves before its\n * resource is ready (routine on remote backends) makes {@link getDataAsJson}\n * throw \"Resource has no content.\" mid-calculation, surfacing a transient\n * \"Some outputs have errors\" banner that clears once the resource finishes\n * (MILAB-6318).\n *\n * A resource that has reached a terminal state (ready or error) but still\n * carries no content is a genuine error and throws, matching\n * {@link getDataAsJson}.\n */\n public getDataAsJsonOrUndefined<T>(): T | undefined {\n // getIsReadyOrError() marks the computable unstable while not ready, which\n // schedules recomputation once the resource finishes. Load-bearing, not a\n // redundant guard over getDataAsJson — keep it.\n if (!this.getIsReadyOrError()) return undefined;\n return this.getDataAsJson<T>();\n }\n\n /**\n *\n */\n public getPColumns(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): PColumn<TreeNodeAccessor>[] | undefined {\n const result = this.parsePObjectCollection(errorOnUnknownField, prefix);\n if (result === undefined) return undefined;\n\n const pf = Object.entries(result).map(([, obj]) => {\n if (!isPColumn(obj)) throw new Error(`not a PColumn (kind = ${obj.spec.kind})`);\n return obj;\n });\n\n return pf;\n }\n\n /**\n *\n */\n public parsePObjectCollection(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): Record<string, PObject<TreeNodeAccessor>> | undefined {\n const pObjects = getCfgRenderCtx().parsePObjectCollection(\n this.handle,\n errorOnUnknownField,\n prefix,\n ...this.resolvePath,\n );\n if (pObjects === undefined) return undefined;\n const result: Record<string, PObject<TreeNodeAccessor>> = {};\n for (const [key, value] of Object.entries(pObjects)) {\n const resolvePath = [...this.resolvePath, key];\n result[key] = mapPObjectData(value, (c) => new TreeNodeAccessor(c, resolvePath));\n }\n return result;\n }\n\n public getFileContentAsBase64(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsBase64(this.handle, range));\n }\n\n public getFileContentAsString(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range));\n }\n\n public getFileContentAsJson<T>(range?: RangeBytes): FutureRef<T | undefined> {\n return new FutureRef<string | undefined>(\n getCfgRenderCtx().getBlobContentAsString(this.handle, range),\n ).mapDefined((v) => JSON.parse(v) as T);\n }\n\n /**\n * @deprecated use getFileContentAsBase64\n */\n public getBlobContentAsBase64(): FutureRef<string | undefined> {\n return this.getFileContentAsBase64();\n }\n\n /**\n * @deprecated use getFileContentAsString\n */\n public getBlobContentAsString(): FutureRef<string | undefined> {\n return this.getFileContentAsString();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getFileHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getDownloadedBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getFileHandle\n */\n public getDownloadedBlobHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return this.getFileHandle();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getRemoteFileHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getOnDemandBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getRemoteFileHandle\n */\n public getOnDemandBlobHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return this.getRemoteFileHandle();\n }\n\n /**\n * @returns the url to the extracted folder\n */\n public extractArchiveAndGetURL(format: ArchiveFormat): FutureRef<FolderURL | undefined> {\n return new FutureRef(getCfgRenderCtx().extractArchiveAndGetURL(this.handle, format));\n }\n\n public getImportProgress(): FutureRef<ImportProgress> {\n return new FutureRef(getCfgRenderCtx().getImportProgress(this.handle));\n }\n\n public getLastLogs(nLines: number): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getLastLogs(this.handle, nLines));\n }\n\n public getProgressLog(patternToSearch: string): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLog(this.handle, patternToSearch));\n }\n\n public getProgressLogWithInfo(\n patternToSearch: string,\n ): FutureRef<ProgressLogWithInfo | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLogWithInfo(this.handle, patternToSearch));\n }\n\n public getLogHandle(): FutureRef<AnyLogHandle | undefined> {\n return new FutureRef(getCfgRenderCtx().getLogHandle(this.handle));\n }\n\n public allFieldsResolved(fieldType: \"Input\" | \"Output\" = \"Input\"): boolean {\n switch (fieldType) {\n case \"Input\":\n return (\n this.getInputsLocked() &&\n this.listInputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Input\" }) !== undefined,\n )\n );\n case \"Output\":\n return (\n this.getOutputsLocked() &&\n this.listOutputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Output\" }) !== undefined,\n )\n );\n }\n }\n\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops: FieldMapOps & { skipUnresolved: true },\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor | undefined) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined {\n const { fieldType, requireLocked, skipUnresolved } = {\n fieldType: \"Input\" as const,\n requireLocked: true,\n skipUnresolved: false,\n ..._ops,\n };\n const mapping = _mapping as (name: string, value: TreeNodeAccessor | undefined) => T;\n if (requireLocked) {\n if (fieldType === \"Input\" && !this.getInputsLocked()) return undefined;\n if (fieldType === \"Output\" && !this.getOutputsLocked()) return undefined;\n }\n const fieldList =\n fieldType === \"Input\"\n ? this.listInputFields()\n : fieldType === \"Output\"\n ? this.listOutputFields()\n : this.listDynamicFields();\n let fieldEntries = fieldList.map(\n (field) => [field, this.resolve({ field, assertFieldType: fieldType })] as const,\n );\n if (skipUnresolved) fieldEntries = fieldEntries.filter((e) => e[1] !== undefined);\n return fieldEntries.map(([name, value]) => mapping(name, value));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"accessor.js","names":[],"sources":["../../src/render/accessor.ts"],"sourcesContent":["import type {\n AnyLogHandle,\n ImportProgress,\n LocalBlobHandleAndSize,\n PColumn,\n PObject,\n RemoteBlobHandleAndSize,\n FolderURL,\n ArchiveFormat,\n ProgressLogWithInfo,\n RangeBytes,\n} from \"@milaboratories/pl-model-common\";\nimport { isPColumn, mapPObjectData } from \"@milaboratories/pl-model-common\";\nimport { getCfgRenderCtx } from \"../internal\";\nimport { FutureRef } from \"./future\";\nimport type { AccessorHandle } from \"./internal\";\nimport type { CommonFieldTraverseOps, FieldTraversalStep, ResourceType } from \"./traversal_ops\";\n\nexport function ifDef<T, R>(value: T | undefined, cb: (value: T) => R): R | undefined {\n return value === undefined ? undefined : cb(value);\n}\n\n/**\n * Decode an error node's content into a display message. The backend serializes\n * a resource error as `{\"message\": \"...\"}` (`ResourceError`); unwrap that to the\n * human-readable message. Falls back to the raw string when the content is not\n * that envelope (e.g. plain text, or an unexpected shape).\n */\nfunction decodeErrorMessage(raw: string): string {\n try {\n const parsed = JSON.parse(raw) as { message?: unknown };\n if (typeof parsed?.message === \"string\") return parsed.message;\n } catch {\n // Not JSON — surface the raw content.\n }\n return raw;\n}\n\ntype FieldMapOps = {\n /**\n * Type of fields to iterate over.\n * (default 'Input')\n * */\n readonly fieldType?: \"Input\" | \"Output\" | \"Dynamic\";\n /**\n * If not locked, `undefined` value will be returned. Do nothing if mapping `Dynamic` fields.\n * (default true)\n * */\n readonly requireLocked?: boolean;\n /**\n * Will skip unresolved fields.\n * (default false)\n * */\n readonly skipUnresolved?: boolean;\n};\n\n/** Represent resource tree node accessor */\nexport class TreeNodeAccessor {\n constructor(\n public readonly handle: AccessorHandle,\n public readonly resolvePath: string[],\n ) {}\n\n /** Shortcut for {@link resolveInput} */\n public resolve(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** Shortcut for {@link resolveInput} */\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolve(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Output. */\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveOutput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Output\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n /** If field type assertion is not specified for the step, default is Input. */\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveInput(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n const transformedSteps = steps.map(\n (s) =>\n ({\n assertFieldType: \"Input\",\n ...(typeof s === \"string\" ? { field: s } : s),\n }) satisfies FieldTraversalStep,\n );\n return this.resolveWithCommon({}, ...transformedSteps);\n }\n\n public resolveAny(\n ...steps: [\n Omit<FieldTraversalStep, \"errorIfFieldNotSet\"> & {\n errorIfFieldNotAssigned: true;\n },\n ]\n ): TreeNodeAccessor;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined;\n public resolveAny(...steps: (FieldTraversalStep | string)[]): TreeNodeAccessor | undefined {\n return this.resolveWithCommon({}, ...steps);\n }\n\n public resolveWithCommon(\n commonOptions: CommonFieldTraverseOps,\n ...steps: (FieldTraversalStep | string)[]\n ): TreeNodeAccessor | undefined {\n const resolvePath = [\n ...this.resolvePath,\n ...steps.map((step) => (typeof step === \"string\" ? step : step.field)),\n ];\n return ifDef(\n getCfgRenderCtx().resolveWithCommon(this.handle, commonOptions, ...steps),\n (accessor) => new TreeNodeAccessor(accessor, resolvePath),\n );\n }\n\n public get resourceType(): ResourceType {\n return getCfgRenderCtx().getResourceType(this.handle);\n }\n\n public getInputsLocked(): boolean {\n return getCfgRenderCtx().getInputsLocked(this.handle);\n }\n\n public getOutputsLocked(): boolean {\n return getCfgRenderCtx().getOutputsLocked(this.handle);\n }\n\n public getIsReadyOrError(): boolean {\n return getCfgRenderCtx().getIsReadyOrError(this.handle);\n }\n\n public getIsFinal(): boolean {\n return getCfgRenderCtx().getIsFinal(this.handle);\n }\n\n public getError(): TreeNodeAccessor | undefined {\n const resolvePath = [...this.resolvePath, \"error\"];\n return ifDef(\n getCfgRenderCtx().getError(this.handle),\n (accsessor) => new TreeNodeAccessor(accsessor, resolvePath),\n );\n }\n\n public listInputFields(): string[] {\n return getCfgRenderCtx().listInputFields(this.handle);\n }\n\n public listOutputFields(): string[] {\n return getCfgRenderCtx().listOutputFields(this.handle);\n }\n\n public listDynamicFields(): string[] {\n return getCfgRenderCtx().listDynamicFields(this.handle);\n }\n\n public getKeyValueBase64(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueBase64(this.handle, key);\n }\n\n public getKeyValueAsString(key: string): string | undefined {\n return getCfgRenderCtx().getKeyValueAsString(this.handle, key);\n }\n\n public getKeyValueAsJson<T>(key: string): T {\n const content = this.getKeyValueAsString(key);\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n public getDataBase64(): string | undefined {\n return getCfgRenderCtx().getDataBase64(this.handle);\n }\n\n public getDataAsString(): string | undefined {\n return getCfgRenderCtx().getDataAsString(this.handle);\n }\n\n public getDataAsJson<T>(): T {\n const content = this.getDataAsString();\n if (content == undefined) throw new Error(\"Resource has no content.\");\n return JSON.parse(content);\n }\n\n /**\n * Like {@link getDataAsJson}, but does not throw while the resource is still\n * computing. Three states:\n *\n * - **Not ready** → returns `undefined` (loading).\n * - **Errored** → throws the resource's error.\n * - **Ready** → returns the parsed content.\n *\n * Prefer this over {@link getDataAsJson} in reactive output lambdas: a field\n * that resolves before its resource is ready (routine on remote backends)\n * makes {@link getDataAsJson} throw \"Resource has no content.\" mid-calculation,\n * flashing a transient \"Some outputs have errors\" banner that clears when the\n * resource finishes (MILAB-6318).\n */\n public getDataAsJsonOrUndefined<T>(): T | undefined {\n // Not ready → undefined (loading). getIsReadyOrError() registers the\n // readiness dependency, so the lambda re-runs once the resource finishes.\n if (!this.getIsReadyOrError()) return undefined;\n // Errored → throw the actual error, decoded from the error node and tagged\n // with the resolve path for debugging context. Beats getDataAsJson's\n // generic \"Resource has no content.\"\n const error = this.getError();\n if (error !== undefined) {\n const raw = error.getDataAsString();\n const message = raw === undefined ? \"Resource computation failed.\" : decodeErrorMessage(raw);\n const path = this.resolvePath.join(\".\");\n throw new Error(path ? `${message} (at ${path})` : message);\n }\n // Ready → parse the content.\n return this.getDataAsJson<T>();\n }\n\n /**\n *\n */\n public getPColumns(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): PColumn<TreeNodeAccessor>[] | undefined {\n const result = this.parsePObjectCollection(errorOnUnknownField, prefix);\n if (result === undefined) return undefined;\n\n const pf = Object.entries(result).map(([, obj]) => {\n if (!isPColumn(obj)) throw new Error(`not a PColumn (kind = ${obj.spec.kind})`);\n return obj;\n });\n\n return pf;\n }\n\n /**\n *\n */\n public parsePObjectCollection(\n errorOnUnknownField: boolean = false,\n prefix: string = \"\",\n ): Record<string, PObject<TreeNodeAccessor>> | undefined {\n const pObjects = getCfgRenderCtx().parsePObjectCollection(\n this.handle,\n errorOnUnknownField,\n prefix,\n ...this.resolvePath,\n );\n if (pObjects === undefined) return undefined;\n const result: Record<string, PObject<TreeNodeAccessor>> = {};\n for (const [key, value] of Object.entries(pObjects)) {\n const resolvePath = [...this.resolvePath, key];\n result[key] = mapPObjectData(value, (c) => new TreeNodeAccessor(c, resolvePath));\n }\n return result;\n }\n\n public getFileContentAsBase64(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsBase64(this.handle, range));\n }\n\n public getFileContentAsString(range?: RangeBytes): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range));\n }\n\n public getFileContentAsJson<T>(range?: RangeBytes): FutureRef<T | undefined> {\n return new FutureRef<string | undefined>(\n getCfgRenderCtx().getBlobContentAsString(this.handle, range),\n ).mapDefined((v) => JSON.parse(v) as T);\n }\n\n /**\n * @deprecated use getFileContentAsBase64\n */\n public getBlobContentAsBase64(): FutureRef<string | undefined> {\n return this.getFileContentAsBase64();\n }\n\n /**\n * @deprecated use getFileContentAsString\n */\n public getBlobContentAsString(): FutureRef<string | undefined> {\n return this.getFileContentAsString();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getFileHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getDownloadedBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getFileHandle\n */\n public getDownloadedBlobHandle(): FutureRef<LocalBlobHandleAndSize | undefined> {\n return this.getFileHandle();\n }\n\n /**\n * @returns downloaded file handle\n */\n public getRemoteFileHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return new FutureRef(getCfgRenderCtx().getOnDemandBlobContentHandle(this.handle));\n }\n\n /**\n * @deprecated use getRemoteFileHandle\n */\n public getOnDemandBlobHandle(): FutureRef<RemoteBlobHandleAndSize | undefined> {\n return this.getRemoteFileHandle();\n }\n\n /**\n * @returns the url to the extracted folder\n */\n public extractArchiveAndGetURL(format: ArchiveFormat): FutureRef<FolderURL | undefined> {\n return new FutureRef(getCfgRenderCtx().extractArchiveAndGetURL(this.handle, format));\n }\n\n public getImportProgress(): FutureRef<ImportProgress> {\n return new FutureRef(getCfgRenderCtx().getImportProgress(this.handle));\n }\n\n public getLastLogs(nLines: number): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getLastLogs(this.handle, nLines));\n }\n\n public getProgressLog(patternToSearch: string): FutureRef<string | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLog(this.handle, patternToSearch));\n }\n\n public getProgressLogWithInfo(\n patternToSearch: string,\n ): FutureRef<ProgressLogWithInfo | undefined> {\n return new FutureRef(getCfgRenderCtx().getProgressLogWithInfo(this.handle, patternToSearch));\n }\n\n public getLogHandle(): FutureRef<AnyLogHandle | undefined> {\n return new FutureRef(getCfgRenderCtx().getLogHandle(this.handle));\n }\n\n public allFieldsResolved(fieldType: \"Input\" | \"Output\" = \"Input\"): boolean {\n switch (fieldType) {\n case \"Input\":\n return (\n this.getInputsLocked() &&\n this.listInputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Input\" }) !== undefined,\n )\n );\n case \"Output\":\n return (\n this.getOutputsLocked() &&\n this.listOutputFields().every(\n (field) => this.resolve({ field, assertFieldType: \"Output\" }) !== undefined,\n )\n );\n }\n }\n\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops: FieldMapOps & { skipUnresolved: true },\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor | undefined) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined;\n public mapFields<T>(\n _mapping: (name: string, value: TreeNodeAccessor) => T,\n _ops?: FieldMapOps,\n ): T[] | undefined {\n const { fieldType, requireLocked, skipUnresolved } = {\n fieldType: \"Input\" as const,\n requireLocked: true,\n skipUnresolved: false,\n ..._ops,\n };\n const mapping = _mapping as (name: string, value: TreeNodeAccessor | undefined) => T;\n if (requireLocked) {\n if (fieldType === \"Input\" && !this.getInputsLocked()) return undefined;\n if (fieldType === \"Output\" && !this.getOutputsLocked()) return undefined;\n }\n const fieldList =\n fieldType === \"Input\"\n ? this.listInputFields()\n : fieldType === \"Output\"\n ? this.listOutputFields()\n : this.listDynamicFields();\n let fieldEntries = fieldList.map(\n (field) => [field, this.resolve({ field, assertFieldType: fieldType })] as const,\n );\n if (skipUnresolved) fieldEntries = fieldEntries.filter((e) => e[1] !== undefined);\n return fieldEntries.map(([name, value]) => mapping(name, value));\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/model",
3
- "version": "1.78.0",
3
+ "version": "1.78.2",
4
4
  "description": "Platforma.bio SDK / Block Model",
5
5
  "files": [
6
6
  "./dist/**/*",
@@ -30,11 +30,11 @@
30
30
  "fast-json-patch": "^3.1.1",
31
31
  "utility-types": "^3.11.0",
32
32
  "zod": "~3.25.76",
33
- "@milaboratories/pl-model-common": "1.43.0",
34
33
  "@milaboratories/helpers": "1.14.2",
35
- "@milaboratories/ptabler-expression-js": "1.2.26",
36
- "@milaboratories/pl-model-middle-layer": "1.27.0",
37
- "@milaboratories/pl-error-like": "1.12.10"
34
+ "@milaboratories/pl-model-common": "1.44.0",
35
+ "@milaboratories/pl-error-like": "1.12.10",
36
+ "@milaboratories/pl-model-middle-layer": "1.28.0",
37
+ "@milaboratories/ptabler-expression-js": "1.2.27"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@vitest/coverage-istanbul": "^4.1.3",
@@ -42,8 +42,8 @@
42
42
  "typescript": "~5.9.3",
43
43
  "vitest": "^4.1.3",
44
44
  "@milaboratories/build-configs": "2.0.0",
45
- "@milaboratories/pf-driver": "1.5.2",
46
- "@milaboratories/pf-spec-driver": "1.4.1",
45
+ "@milaboratories/pf-driver": "1.6.0",
46
+ "@milaboratories/pf-spec-driver": "1.4.2",
47
47
  "@milaboratories/ts-configs": "1.2.3",
48
48
  "@milaboratories/ts-builder": "1.5.0"
49
49
  },
@@ -4,12 +4,13 @@ import * as internal from "../internal";
4
4
  import type { AccessorHandle } from "./internal";
5
5
 
6
6
  const HANDLE = "test-handle" as AccessorHandle;
7
+ const ERROR_HANDLE = "error-handle" as AccessorHandle;
7
8
 
8
9
  type RenderCtx = ReturnType<typeof internal.getCfgRenderCtx>;
9
10
 
10
- function accessorWithCtx(ctx: Partial<RenderCtx>): TreeNodeAccessor {
11
+ function accessorWithCtx(ctx: Partial<RenderCtx>, resolvePath: string[] = []): TreeNodeAccessor {
11
12
  vi.spyOn(internal, "getCfgRenderCtx").mockReturnValue(ctx as RenderCtx);
12
- return new TreeNodeAccessor(HANDLE, []);
13
+ return new TreeNodeAccessor(HANDLE, resolvePath);
13
14
  }
14
15
 
15
16
  afterEach(() => {
@@ -17,24 +18,88 @@ afterEach(() => {
17
18
  });
18
19
 
19
20
  test("getDataAsJsonOrUndefined returns undefined while the resource is still computing", () => {
20
- const getDataAsString = vi.fn<() => string | undefined>(() => undefined);
21
- const acc = accessorWithCtx({ getIsReadyOrError: () => false, getDataAsString });
21
+ // Not ready → undefined, and crucially no throw (toBeUndefined fails on a throw).
22
+ // That is the MILAB-6318 fix.
23
+ const acc = accessorWithCtx({
24
+ getIsReadyOrError: () => false,
25
+ getError: () => undefined,
26
+ getDataAsString: () => undefined,
27
+ });
22
28
  expect(acc.getDataAsJsonOrUndefined()).toBeUndefined();
23
- // Must short-circuit before reading content — that is the MILAB-6318 fix.
24
- expect(getDataAsString).not.toHaveBeenCalled();
25
29
  });
26
30
 
27
31
  test("getDataAsJsonOrUndefined parses content once the resource is ready", () => {
28
32
  const acc = accessorWithCtx({
29
33
  getIsReadyOrError: () => true,
34
+ getError: () => undefined,
30
35
  getDataAsString: () => JSON.stringify({ min_len: 7 }),
31
36
  });
32
37
  expect(acc.getDataAsJsonOrUndefined<{ min_len: number }>()).toEqual({ min_len: 7 });
33
38
  });
34
39
 
35
- test("getDataAsJsonOrUndefined throws when a ready resource has no content", () => {
40
+ test("getDataAsJsonOrUndefined throws the error node's message when the resource errored", () => {
41
+ // The backend serializes a resource error as {"message": "..."}; the thrown
42
+ // string is the unwrapped message, not the raw JSON envelope.
43
+ const resourceError = JSON.stringify({ message: "upstream blew up" });
44
+ const acc = accessorWithCtx({
45
+ getIsReadyOrError: () => true,
46
+ getError: () => ERROR_HANDLE,
47
+ getDataAsString: (handle: AccessorHandle) =>
48
+ handle === ERROR_HANDLE ? resourceError : undefined,
49
+ });
50
+ expect(() => acc.getDataAsJsonOrUndefined()).toThrow(/^upstream blew up$/);
51
+ });
52
+
53
+ test("getDataAsJsonOrUndefined tags the thrown error with the resolve path", () => {
54
+ // The path identifies which resolve failed when a lambda reads several fields.
55
+ const resourceError = JSON.stringify({ message: "boom" });
56
+ const acc = accessorWithCtx(
57
+ {
58
+ getIsReadyOrError: () => true,
59
+ getError: () => ERROR_HANDLE,
60
+ getDataAsString: (handle: AccessorHandle) =>
61
+ handle === ERROR_HANDLE ? resourceError : undefined,
62
+ },
63
+ ["outputs", "isEmpty"],
64
+ );
65
+ expect(() => acc.getDataAsJsonOrUndefined()).toThrow(/^boom \(at outputs\.isEmpty\)$/);
66
+ });
67
+
68
+ test("getDataAsJsonOrUndefined falls back to the raw content when it is not a message envelope", () => {
69
+ // Plain text (not JSON) → surfaced verbatim.
70
+ const acc = accessorWithCtx({
71
+ getIsReadyOrError: () => true,
72
+ getError: () => ERROR_HANDLE,
73
+ getDataAsString: (handle: AccessorHandle) => (handle === ERROR_HANDLE ? "not json" : undefined),
74
+ });
75
+ expect(() => acc.getDataAsJsonOrUndefined()).toThrow(/^not json$/);
76
+ });
77
+
78
+ test("getDataAsJsonOrUndefined falls back to the raw content when JSON has no string message", () => {
79
+ // Valid JSON but not the {message} envelope → surfaced verbatim.
80
+ const raw = JSON.stringify({ code: 42 });
81
+ const acc = accessorWithCtx({
82
+ getIsReadyOrError: () => true,
83
+ getError: () => ERROR_HANDLE,
84
+ getDataAsString: (handle: AccessorHandle) => (handle === ERROR_HANDLE ? raw : undefined),
85
+ });
86
+ expect(() => acc.getDataAsJsonOrUndefined()).toThrow(raw);
87
+ });
88
+
89
+ test("getDataAsJsonOrUndefined throws a generic message when the errored node has no content", () => {
90
+ // Errored, but the error node itself has nothing readable — generic fallback.
91
+ const acc = accessorWithCtx({
92
+ getIsReadyOrError: () => true,
93
+ getError: () => ERROR_HANDLE,
94
+ getDataAsString: () => undefined,
95
+ });
96
+ expect(() => acc.getDataAsJsonOrUndefined()).toThrow(/^Resource computation failed\.$/);
97
+ });
98
+
99
+ test("getDataAsJsonOrUndefined throws when a ready resource has no error and no content", () => {
36
100
  const acc = accessorWithCtx({
37
101
  getIsReadyOrError: () => true,
102
+ getError: () => undefined,
38
103
  getDataAsString: () => undefined,
39
104
  });
40
105
  expect(() => acc.getDataAsJsonOrUndefined()).toThrow("Resource has no content.");
@@ -20,6 +20,22 @@ export function ifDef<T, R>(value: T | undefined, cb: (value: T) => R): R | unde
20
20
  return value === undefined ? undefined : cb(value);
21
21
  }
22
22
 
23
+ /**
24
+ * Decode an error node's content into a display message. The backend serializes
25
+ * a resource error as `{"message": "..."}` (`ResourceError`); unwrap that to the
26
+ * human-readable message. Falls back to the raw string when the content is not
27
+ * that envelope (e.g. plain text, or an unexpected shape).
28
+ */
29
+ function decodeErrorMessage(raw: string): string {
30
+ try {
31
+ const parsed = JSON.parse(raw) as { message?: unknown };
32
+ if (typeof parsed?.message === "string") return parsed.message;
33
+ } catch {
34
+ // Not JSON — surface the raw content.
35
+ }
36
+ return raw;
37
+ }
38
+
23
39
  type FieldMapOps = {
24
40
  /**
25
41
  * Type of fields to iterate over.
@@ -203,24 +219,34 @@ export class TreeNodeAccessor {
203
219
  }
204
220
 
205
221
  /**
206
- * Like {@link getDataAsJson}, but returns `undefined` while the resource is
207
- * still computing instead of throwing.
222
+ * Like {@link getDataAsJson}, but does not throw while the resource is still
223
+ * computing. Three states:
208
224
  *
209
- * Prefer this in reactive output lambdas. A field that resolves before its
210
- * resource is ready (routine on remote backends) makes {@link getDataAsJson}
211
- * throw "Resource has no content." mid-calculation, surfacing a transient
212
- * "Some outputs have errors" banner that clears once the resource finishes
213
- * (MILAB-6318).
225
+ * - **Not ready** → returns `undefined` (loading).
226
+ * - **Errored** → throws the resource's error.
227
+ * - **Ready** → returns the parsed content.
214
228
  *
215
- * A resource that has reached a terminal state (ready or error) but still
216
- * carries no content is a genuine error and throws, matching
217
- * {@link getDataAsJson}.
229
+ * Prefer this over {@link getDataAsJson} in reactive output lambdas: a field
230
+ * that resolves before its resource is ready (routine on remote backends)
231
+ * makes {@link getDataAsJson} throw "Resource has no content." mid-calculation,
232
+ * flashing a transient "Some outputs have errors" banner that clears when the
233
+ * resource finishes (MILAB-6318).
218
234
  */
219
235
  public getDataAsJsonOrUndefined<T>(): T | undefined {
220
- // getIsReadyOrError() marks the computable unstable while not ready, which
221
- // schedules recomputation once the resource finishes. Load-bearing, not a
222
- // redundant guard over getDataAsJson — keep it.
236
+ // Not ready → undefined (loading). getIsReadyOrError() registers the
237
+ // readiness dependency, so the lambda re-runs once the resource finishes.
223
238
  if (!this.getIsReadyOrError()) return undefined;
239
+ // Errored → throw the actual error, decoded from the error node and tagged
240
+ // with the resolve path for debugging context. Beats getDataAsJson's
241
+ // generic "Resource has no content."
242
+ const error = this.getError();
243
+ if (error !== undefined) {
244
+ const raw = error.getDataAsString();
245
+ const message = raw === undefined ? "Resource computation failed." : decodeErrorMessage(raw);
246
+ const path = this.resolvePath.join(".");
247
+ throw new Error(path ? `${message} (at ${path})` : message);
248
+ }
249
+ // Ready → parse the content.
224
250
  return this.getDataAsJson<T>();
225
251
  }
226
252