@milaboratories/pl-middle-layer 1.48.16 → 1.48.17

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.
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","names":[],"sources":["../../src/middle_layer/project.ts"],"sourcesContent":["import type { MiddleLayerEnvironment } from \"./middle_layer\";\nimport type { FieldData, OptionalAnyResourceId, ResourceId } from \"@milaboratories/pl-client\";\nimport {\n DefaultRetryOptions,\n ensureResourceIdNotNull,\n field,\n isNotFoundError,\n isTimeoutOrCancelError,\n Pl,\n resourceIdToString,\n} from \"@milaboratories/pl-client\";\nimport type { ComputableStableDefined, ComputableValueOrErrors } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport { projectOverview } from \"./project_overview\";\nimport type { BlockPackSpecAny } from \"../model\";\nimport { randomUUID } from \"node:crypto\";\nimport { withProject, withProjectAuthored } from \"../mutator/project\";\nimport type { ExtendedResourceData, PruningFunction } from \"@milaboratories/pl-tree\";\nimport { SynchronizedTreeState, treeDumpStats } from \"@milaboratories/pl-tree\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { frontendData } from \"./frontend_path\";\nimport type { NavigationState } from \"@milaboratories/pl-model-common\";\nimport { getBlockParameters, blockOutputs } from \"./block\";\nimport type { FrontendData } from \"../model/frontend\";\nimport type { ProjectStructure } from \"../model/project_model\";\nimport { projectFieldName } from \"../model/project_model\";\nimport { cachedDeserialize, notEmpty, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { BlockPackInfo } from \"../model/block_pack\";\nimport type {\n ProjectOverview,\n AuthorMarker,\n BlockSettings,\n BlockStateInternalV3,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { activeConfigs } from \"./active_cfg\";\nimport { NavigationStates } from \"./navigation_states\";\nimport { extractConfig, BLOCK_STORAGE_FACADE_VERSION } from \"@platforma-sdk/model\";\nimport fs from \"node:fs/promises\";\nimport canonicalize from \"canonicalize\";\nimport type { ProjectOverviewLight } from \"./project_overview_light\";\nimport { projectOverviewLight } from \"./project_overview_light\";\nimport { applyProjectMigrations } from \"../mutator/migration\";\n\ntype BlockStateComputables = {\n readonly fullState: Computable<BlockStateInternalV3>;\n};\n\nfunction stringifyForDump(object: unknown): string {\n return JSON.stringify(object, (key, value) => {\n if (typeof value === \"bigint\") return resourceIdToString(value as OptionalAnyResourceId);\n else if (\n ArrayBuffer.isView(value) ||\n value instanceof Int8Array ||\n value instanceof Uint8Array ||\n value instanceof Uint8ClampedArray ||\n value instanceof Int16Array ||\n value instanceof Uint16Array ||\n value instanceof Int32Array ||\n value instanceof Uint32Array ||\n value instanceof Float32Array ||\n value instanceof Float64Array ||\n value instanceof BigInt64Array ||\n value instanceof BigUint64Array\n )\n return Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString(\"base64\");\n else if (Buffer.isBuffer(value)) return value.toString(\"base64\");\n\n return value;\n });\n}\n\n/** Data access object, to manipulate and read single opened (!) project data. */\nexport class Project {\n /** Underlying pl resource id */\n public readonly rid: ResourceId;\n\n /** Data for the left panel, contain basic information about block status. */\n public readonly overview: ComputableStableDefined<ProjectOverview>;\n private readonly overviewLight: Computable<ProjectOverviewLight>;\n\n private readonly navigationStates = new NavigationStates();\n // null is set for deleted blocks\n private readonly blockComputables = new Map<string, BlockStateComputables | null>();\n\n private readonly blockFrontends = new Map<string, ComputableStableDefined<FrontendData>>();\n private readonly activeConfigs: Computable<unknown[]>;\n private readonly refreshLoopResult: Promise<void>;\n\n private readonly abortController = new AbortController();\n\n private get destroyed() {\n return this.abortController.signal.aborted;\n }\n\n constructor(\n private readonly env: MiddleLayerEnvironment,\n rid: ResourceId,\n private readonly projectTree: SynchronizedTreeState,\n ) {\n this.overview = projectOverview(\n projectTree.entry(),\n this.navigationStates,\n env,\n ).withPreCalculatedValueTree();\n this.overviewLight = projectOverviewLight(projectTree.entry()).withPreCalculatedValueTree();\n this.rid = rid;\n this.refreshLoopResult = this.refreshLoop();\n this.refreshLoopResult.catch((err) => {\n env.logger.warn(new Error(\"Error during refresh loop\", { cause: err })); // TODO (safe voiding for now)\n });\n this.activeConfigs = activeConfigs(projectTree.entry(), env);\n }\n\n get projectLockId(): string {\n return \"project:\" + this.rid.toString();\n }\n\n private async refreshLoop(): Promise<void> {\n while (!this.destroyed) {\n try {\n await withProject(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n (prj) => {\n prj.doRefresh(this.env.ops.stagingRenderingRate);\n },\n { name: \"doRefresh\", lockId: this.projectLockId },\n );\n await this.activeConfigs.getValue();\n await setTimeout(this.env.ops.projectRefreshInterval, this.abortController.signal);\n\n // Block computables housekeeping\n const overviewLight = await this.overviewLight.getValue();\n const existingBlocks = new Set(overviewLight.listOfBlocks);\n // Doing cleanup for deleted blocks\n for (const blockId of this.blockComputables.keys()) {\n if (!existingBlocks.has(blockId)) {\n const computable = this.blockComputables.get(blockId);\n if (computable !== undefined && computable !== null) computable.fullState.resetState();\n this.blockComputables.set(blockId, null);\n }\n }\n } catch (e: unknown) {\n // If we're destroyed, exit gracefully regardless of error type\n if (this.destroyed) {\n // Log just in case, to help with debugging if something unexpected happens during shutdown\n this.env.logger.warn(new Error(\"Error during refresh loop shutdown\", { cause: e }));\n break;\n }\n\n if (isNotFoundError(e)) {\n console.warn(\n \"project refresh routine terminated, because project was externally deleted\",\n );\n break;\n } else if (isTimeoutOrCancelError(e)) {\n // Timeout during normal operation, continue the loop\n } else {\n // TODO: This stops the refresh loop permanently, leaving the project broken.\n // Need to decide how to handle this case.\n throw new Error(\"Unexpected exception\", { cause: e });\n }\n }\n }\n }\n\n /**\n * Adds new block to the project.\n *\n * @param blockLabel block label / title visible to the user\n * @param blockPackSpec object describing the \"block type\", read more in the type docs\n * @param before id of the block to insert new block before\n * @param blockId internal id to be assigned for the block, this arg can be omitted\n * then, randomly generated UUID will be assigned automatically\n *\n * @return returns newly created block id\n * */\n public async addBlock(\n blockLabel: string,\n blockPackSpec: BlockPackSpecAny,\n before?: string,\n author: AuthorMarker | undefined = undefined,\n blockId: string = randomUUID(),\n ): Promise<string> {\n const preparedBp = await this.env.bpPreparer.prepare(blockPackSpec);\n const blockCfgContainer = await this.env.bpPreparer.getBlockConfigContainer(blockPackSpec);\n const blockCfg = extractConfig(blockCfgContainer); // full content of this var should never be persisted\n\n this.env.runtimeCapabilities.throwIfIncompatible(blockCfg.featureFlags);\n\n // Build NewBlockSpec based on model API version\n const newBlockSpec =\n blockCfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION\n ? { storageMode: \"fromModel\" as const, blockPack: preparedBp }\n : {\n storageMode: \"legacy\" as const,\n blockPack: preparedBp,\n legacyState: canonicalize({\n args: blockCfg.initialArgs,\n uiState: blockCfg.initialUiState,\n })!,\n };\n\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n return mut.addBlock(\n {\n id: blockId,\n label: blockLabel,\n renderingMode: blockCfg.renderingMode,\n },\n newBlockSpec,\n before,\n );\n },\n {\n retryOptions: {\n ...DefaultRetryOptions,\n backoffMultiplier: DefaultRetryOptions.backoffMultiplier * 1.1,\n },\n name: \"addBlock\",\n lockId: this.projectLockId,\n },\n );\n\n await this.projectTree.refreshState();\n\n return blockId;\n }\n\n /**\n * Duplicates an existing block by copying all its fields and state.\n * This method works at the mutator level for efficient block copying.\n *\n * @param originalBlockId id of the block to duplicate\n * @param before id of the block to insert new block before\n * @param author author marker for the duplication operation\n * @param newBlockId internal id to be assigned for the duplicated block,\n * if omitted, a randomly generated UUID will be assigned\n *\n * @return returns newly created block id\n * */\n public async duplicateBlock(\n originalBlockId: string,\n before?: string,\n author: AuthorMarker | undefined = undefined,\n newBlockId: string = randomUUID(),\n ): Promise<string> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.duplicateBlock(originalBlockId, newBlockId, before),\n { name: \"duplicateBlock\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n\n return newBlockId;\n }\n\n /**\n * Update block to new block pack, optionally resetting args and ui state to\n * initial values\n * */\n public async updateBlockPack(\n blockId: string,\n blockPackSpec: BlockPackSpecAny,\n resetArgs: boolean = false,\n author?: AuthorMarker,\n ): Promise<void> {\n const preparedBp = await this.env.bpPreparer.prepare(blockPackSpec);\n const blockCfg = extractConfig(\n await this.env.bpPreparer.getBlockConfigContainer(blockPackSpec),\n );\n\n this.env.runtimeCapabilities.throwIfIncompatible(blockCfg.featureFlags);\n\n // resetState signals to mutator to reset storage\n // For v2+ blocks: mutator gets initial storage directly via getInitialStorageInVM\n // For v1 blocks: we pass the legacy state format\n const resetState = resetArgs\n ? {\n state:\n blockCfg.modelAPIVersion === 1\n ? { args: blockCfg.initialArgs, uiState: blockCfg.initialUiState }\n : {},\n }\n : undefined;\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.migrateBlockPack(blockId, preparedBp, resetState),\n { name: \"updateBlockPack\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /** Deletes a block with all associated data. */\n public async deleteBlock(blockId: string, author?: AuthorMarker): Promise<void> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.deleteBlock(blockId),\n { name: \"deleteBlock\", lockId: this.projectLockId },\n );\n this.navigationStates.deleteBlock(blockId);\n await this.projectTree.refreshState();\n }\n\n /**\n * Updates block order according to the given array of block ids.\n *\n * Provided array must contain exactly the same set of ids current project cosists of,\n * an error will be thrown instead.\n */\n public async reorderBlocks(blocks: string[], author?: AuthorMarker): Promise<void> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n const currentStructure = mut.structure;\n if (currentStructure.groups.length !== 1)\n throw new Error(\"Unexpected project structure, non-singular block group\");\n const currentGroup = currentStructure.groups[0];\n if (currentGroup.blocks.length !== blocks.length)\n throw new Error(`Length mismatch: ${currentGroup.blocks.length} !== ${blocks.length}`);\n if (new Set<string>(blocks).size !== blocks.length) throw new Error(`Repeated block ids`);\n const newStructure: ProjectStructure = {\n groups: [\n {\n id: currentGroup.id,\n label: currentGroup.label,\n blocks: blocks.map((blockId) => {\n const block = currentGroup.blocks.find((b) => b.id === blockId);\n if (block === undefined) throw new Error(`Can't find block: ${blockId}`);\n return block;\n }),\n },\n ],\n };\n mut.updateStructure(newStructure);\n },\n { name: \"reorderBlocks\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Renders production part of the block starting all connected heavy computations.\n * Upstream blocks of the specified block will be started automatically if in\n * stale state.\n * */\n public async runBlock(blockId: string): Promise<void> {\n await withProject(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n (mut) => mut.renderProduction([blockId], true),\n { name: \"runBlock\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Stops the block if it is running by destroying its production state. All\n * its downstreams will also be destroyed or moved to limbo if already\n * calculated.\n * */\n public async stopBlock(blockId: string): Promise<void> {\n await withProject(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n (mut) => mut.stopProduction(blockId),\n { name: \"stopBlock\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * @deprecated Use mutateBlockStorage() for V3 blocks.\n * Sets block args, and changes whole project state accordingly.\n * Along with setting arguments one can specify author marker, that will be\n * transactionally associated with the block, to facilitate conflict resolution\n * in collaborative editing scenario.\n * */\n public async setBlockArgs(blockId: string, args: unknown, author?: AuthorMarker) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n const state = mut.mergeBlockState(blockId, { args });\n mut.setStates([{ modelAPIVersion: 1, blockId, state }]);\n },\n { name: \"setBlockArgs\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * @deprecated Use mutateBlockStorage() for V3 blocks.\n * Sets ui block state associated with the block.\n * Along with setting arguments one can specify author marker, that will be\n * transactionally associated with the block, to facilitate conflict resolution\n * in collaborative editing scenario.\n * */\n public async setUiState(blockId: string, uiState: unknown, author?: AuthorMarker) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n const state = mut.mergeBlockState(blockId, { uiState });\n mut.setStates([{ modelAPIVersion: 1, blockId, state }]);\n },\n { name: \"setUiState\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * @deprecated Use mutateBlockStorage() for V3 blocks.\n * Sets block args and ui state, and changes the whole project state accordingly.\n * Along with setting arguments one can specify author marker, that will be\n * transactionally associated with the block, to facilitate conflict resolution\n * in collaborative editing scenario.\n * */\n public async setBlockArgsAndUiState(\n blockId: string,\n args: unknown, // keep for v1/v2 compatibility\n uiState: unknown, // keep for v1/v2 compatibility\n author?: AuthorMarker,\n ) {\n // Normalize to unified state format { args, uiState } for v1/v2 blocks\n const state = { args, uiState };\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n mut.setStates([{ modelAPIVersion: 1, blockId, state }]);\n },\n { name: \"setBlockArgsAndUiState\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Sets navigation state.\n * */\n //\n public async setNavigationState(blockId: string, state: NavigationState): Promise<void> {\n this.navigationStates.setState(blockId, state);\n }\n\n /**\n * Mutates block storage for Model API v3 blocks.\n * Applies a storage operation (e.g., 'update-data') which triggers\n * args derivation (args(data) and prerunArgs(data)).\n * The derived args are stored atomically with the data.\n *\n * @param blockId - The block ID\n * @param payload - Storage mutation payload with operation and value\n * @param author - Optional author marker for collaborative editing\n */\n public async mutateBlockStorage(\n blockId: string,\n payload: { operation: string; value: unknown },\n author?: AuthorMarker,\n ) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.setStates([{ modelAPIVersion: 2, blockId, payload }]),\n { name: \"mutateBlockStorage\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /** Update block settings */\n public async setBlockSettings(blockId: string, newValue: BlockSettings) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n undefined,\n (mut) => {\n mut.setBlockSettings(blockId, newValue);\n },\n { name: \"setBlockSettings\" },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Sets raw block storage content directly.\n * This bypasses all normalization and VM transformations.\n *\n * @param blockId The block to set storage for\n * @param rawStorageJson Raw storage as JSON string\n */\n public async setBlockStorageRaw(blockId: string, rawStorageJson: string): Promise<void> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n undefined,\n (mut) => {\n mut.setBlockStorageRaw(blockId, rawStorageJson);\n },\n { name: \"setBlockStorageRaw\" },\n );\n await this.projectTree.refreshState();\n }\n\n /** Resets arguments and ui state of the block to initial state */\n public async resetBlockArgsAndUiState(blockId: string, author?: AuthorMarker): Promise<void> {\n await this.env.pl.withWriteTx(\"BlockInputsReset\", async (tx) => {\n // reading default arg values from block pack\n const bpHolderRid = ensureResourceIdNotNull(\n (await tx.getField(field(this.rid, projectFieldName(blockId, \"blockPack\")))).value,\n );\n const bpRid = ensureResourceIdNotNull(\n (await tx.getField(field(bpHolderRid, Pl.HolderRefField))).value,\n );\n const bpData = await tx.getResourceData(bpRid, false);\n const config = extractConfig(cachedDeserialize<BlockPackInfo>(notEmpty(bpData.data)).config);\n\n await withProjectAuthored(\n this.env.projectHelper,\n tx,\n this.rid,\n author,\n (prj) => {\n if (config.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION) {\n // V2+: Reset to initial storage via VM\n prj.resetToInitialStorage(blockId);\n } else {\n // V1: Use legacy state format\n const initialState = { args: config.initialArgs, uiState: config.initialUiState };\n prj.setStates([{ modelAPIVersion: 1, blockId, state: initialState }]);\n }\n },\n { name: \"resetBlockArgsAndUiState\", lockId: this.projectLockId },\n );\n await tx.commit();\n });\n await this.projectTree.refreshState();\n }\n\n private getBlockComputables(blockId: string): BlockStateComputables {\n const cached = this.blockComputables.get(blockId);\n if (cached === null) throw new Error(`Block ${blockId} is deleted`);\n if (cached === undefined) {\n // state consists of inputs (args + ui state) and outputs\n const outputs = blockOutputs(this.projectTree.entry(), blockId, this.env);\n const fullState = Computable.make(\n (ctx) => {\n return {\n parameters: getBlockParameters(this.projectTree.entry(), blockId, ctx),\n outputs,\n navigationState: this.navigationStates.getState(blockId),\n overview: this.overview,\n };\n },\n {\n postprocessValue: (v) => {\n const blockOverview = v.overview?.blocks?.find((b) => b.id == blockId);\n const sdkVersion = blockOverview?.sdkVersion;\n const storageDebugView = blockOverview?.storageDebugView;\n const toString = sdkVersion && shouldStillUseStringErrors(sdkVersion);\n const newOutputs =\n toString && v.outputs !== undefined ? convertErrorsToStrings(v.outputs) : v.outputs;\n\n return {\n ...v.parameters,\n outputs: newOutputs,\n navigationState: v.navigationState,\n storageDebugView,\n } as BlockStateInternalV3;\n },\n },\n );\n\n const computables: BlockStateComputables = {\n fullState: fullState.withPreCalculatedValueTree(),\n };\n\n this.blockComputables.set(blockId, computables);\n\n return computables;\n }\n return cached;\n }\n\n /**\n * Returns a computable, that can be used to retrieve and watch full block state,\n * including outputs, arguments, ui state.\n * */\n public getBlockState(blockId: string): Computable<BlockStateInternalV3> {\n return this.getBlockComputables(blockId).fullState;\n }\n\n /**\n * Returns a computable, that can be used to retrieve and watch path of the\n * folder containing frontend code.\n * */\n public getBlockFrontend(blockId: string): ComputableStableDefined<FrontendData> {\n const cached = this.blockFrontends.get(blockId);\n if (cached === undefined) {\n const fd = frontendData(\n this.projectTree.entry(),\n blockId,\n this.env,\n ).withPreCalculatedValueTree();\n this.blockFrontends.set(blockId, fd);\n return fd;\n }\n return cached;\n }\n\n /** Called by middle layer on close */\n public async destroy(): Promise<void> {\n // terminating the project service loop\n this.abortController.abort();\n try {\n await this.refreshLoopResult;\n } catch (e: unknown) {\n // Error was already logged in the constructor's catch handler, but log again for context\n this.env.logger.warn(\n new Error(\"Refresh loop had terminated with error before destroy\", { cause: e }),\n );\n }\n\n // terminating the synchronized project tree\n try {\n await this.projectTree.terminate();\n } catch (e: unknown) {\n // TODO: SynchronizedTreeState.terminate() can throw if mainLoop had an error before termination\n // Log error but continue cleanup - we must clean up remaining resources\n this.env.logger.warn(new Error(\"Project tree termination failed\", { cause: e }));\n }\n\n // the following will deregister all external resource holders, like\n // downloaded files, running uploads and alike\n this.overview.resetState();\n this.blockFrontends.forEach((c) => c.resetState());\n this.blockComputables.forEach((c) => {\n if (c !== null) c.fullState.resetState();\n });\n this.activeConfigs.resetState();\n }\n\n /** @deprecated */\n public async destroyAndAwaitTermination(): Promise<void> {\n await this.destroy();\n }\n\n public dumpState(): ExtendedResourceData[] {\n return this.projectTree.dumpState();\n }\n\n public static async init(env: MiddleLayerEnvironment, rid: ResourceId): Promise<Project> {\n // Applying migrations to the project resource, if needed\n await applyProjectMigrations(env.pl, rid);\n\n // Doing a no-op mutation to apply all migration and schema fixes\n await withProject(env.projectHelper, env.pl, rid, (_) => {}, { name: \"init\" });\n\n // Loading project tree\n const projectTree = await SynchronizedTreeState.init(\n env.pl,\n rid,\n {\n ...env.ops.defaultTreeOptions,\n pruning: projectTreePruning(env.logger),\n },\n env.logger,\n );\n\n if (env.ops.debugOps.dumpInitialTreeState) {\n const state = projectTree.dumpState();\n state.sort((a, b) => (b.data?.byteLength ?? 0) - (a.data?.byteLength ?? 0));\n const stats = treeDumpStats(state);\n await fs.writeFile(`${resourceIdToString(rid)}.json`, stringifyForDump(state));\n await fs.writeFile(`${resourceIdToString(rid)}.stats.json`, stringifyForDump(stats));\n }\n\n return new Project(env, rid, projectTree);\n }\n}\n\nfunction projectTreePruning(logger: MiLogger): PruningFunction {\n return (r: ExtendedResourceData): FieldData[] => {\n if (r.fields.length > 1000)\n logger.warn(\n `resource with excessive field count: type=${r.type.name} id=${r.id} fields=${r.fields.length}` +\n ` names=[${r.fields\n .slice(0, 10)\n .map((f) => f.name)\n .join(\", \")}, ...]`,\n );\n if (r.type.name.startsWith(\"StreamWorkdir/\")) return [];\n switch (r.type.name) {\n case \"BlockPackCustom\":\n return r.fields.filter((f) => f.name !== \"template\");\n case \"UserProject\":\n return r.fields.filter((f) => !f.name.startsWith(\"__serviceTemplate\"));\n case \"Blob\":\n return [];\n default:\n return r.fields;\n }\n };\n}\n\n/** Returns true if sdk version of the block is old and we need to convert\n * ErrorLike errors to strings like it was.\n * We need it for keeping old blocks and new UI compatibility. */\nfunction shouldStillUseStringErrors(sdkVersion: string): boolean {\n return !isVersionGreater(sdkVersion, \"1.26.0\");\n}\n\n/** Checks if sdk version is greater that a target version. */\nfunction isVersionGreater(sdkVersion: string, targetVersion: string): boolean {\n const version = sdkVersion.split(\".\").map(Number);\n const target = targetVersion.split(\".\").map(Number);\n\n return (\n version[0] > target[0] ||\n (version[0] === target[0] && version[1] > target[1]) ||\n (version[0] === target[0] && version[1] === target[1] && version[2] > target[2])\n );\n}\n\n/** Converts ErrorLike errors to strings in the outputs like it was in old ML versions. */\nfunction convertErrorsToStrings(\n outputs: Record<string, ComputableValueOrErrors<unknown>>,\n): Record<string, ComputableValueOrErrors<unknown>> {\n const result: Record<string, ComputableValueOrErrors<unknown>> = {};\n for (const [key, val] of Object.entries(outputs)) {\n if (val.ok) {\n result[key] = val;\n continue;\n }\n\n result[key] = {\n ok: false,\n errors: val.errors.map((e) => {\n if (typeof e === \"string\") {\n return e;\n } else if (e.type == \"PlError\" && e.fullMessage !== undefined) {\n return e.fullMessage;\n }\n return e.message;\n }),\n moreErrors: val.moreErrors,\n };\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA+CA,SAAS,iBAAiB,QAAyB;AACjD,QAAO,KAAK,UAAU,SAAS,KAAK,UAAU;AAC5C,MAAI,OAAO,UAAU,SAAU,QAAO,mBAAmB,MAA+B;WAEtF,YAAY,OAAO,MAAM,IACzB,iBAAiB,aACjB,iBAAiB,cACjB,iBAAiB,qBACjB,iBAAiB,cACjB,iBAAiB,eACjB,iBAAiB,cACjB,iBAAiB,eACjB,iBAAiB,gBACjB,iBAAiB,gBACjB,iBAAiB,iBACjB,iBAAiB,eAEjB,QAAO,OAAO,KAAK,MAAM,QAAQ,MAAM,YAAY,MAAM,WAAW,CAAC,SAAS,SAAS;WAChF,OAAO,SAAS,MAAM,CAAE,QAAO,MAAM,SAAS,SAAS;AAEhE,SAAO;GACP;;;AAIJ,IAAa,UAAb,MAAa,QAAQ;;CAEnB,AAAgB;;CAGhB,AAAgB;CAChB,AAAiB;CAEjB,AAAiB,mBAAmB,IAAI,kBAAkB;CAE1D,AAAiB,mCAAmB,IAAI,KAA2C;CAEnF,AAAiB,iCAAiB,IAAI,KAAoD;CAC1F,AAAiB;CACjB,AAAiB;CAEjB,AAAiB,kBAAkB,IAAI,iBAAiB;CAExD,IAAY,YAAY;AACtB,SAAO,KAAK,gBAAgB,OAAO;;CAGrC,YACE,AAAiB,KACjB,KACA,AAAiB,aACjB;EAHiB;EAEA;AAEjB,OAAK,WAAW,gBACd,YAAY,OAAO,EACnB,KAAK,kBACL,IACD,CAAC,4BAA4B;AAC9B,OAAK,gBAAgB,qBAAqB,YAAY,OAAO,CAAC,CAAC,4BAA4B;AAC3F,OAAK,MAAM;AACX,OAAK,oBAAoB,KAAK,aAAa;AAC3C,OAAK,kBAAkB,OAAO,QAAQ;AACpC,OAAI,OAAO,KAAK,IAAI,MAAM,6BAA6B,EAAE,OAAO,KAAK,CAAC,CAAC;IACvE;AACF,OAAK,gBAAgB,cAAc,YAAY,OAAO,EAAE,IAAI;;CAG9D,IAAI,gBAAwB;AAC1B,SAAO,aAAa,KAAK,IAAI,UAAU;;CAGzC,MAAc,cAA6B;AACzC,SAAO,CAAC,KAAK,UACX,KAAI;AACF,SAAM,YACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,MACJ,QAAQ;AACP,QAAI,UAAU,KAAK,IAAI,IAAI,qBAAqB;MAElD;IAAE,MAAM;IAAa,QAAQ,KAAK;IAAe,CAClD;AACD,SAAM,KAAK,cAAc,UAAU;AACnC,SAAM,WAAW,KAAK,IAAI,IAAI,wBAAwB,KAAK,gBAAgB,OAAO;GAGlF,MAAM,gBAAgB,MAAM,KAAK,cAAc,UAAU;GACzD,MAAM,iBAAiB,IAAI,IAAI,cAAc,aAAa;AAE1D,QAAK,MAAM,WAAW,KAAK,iBAAiB,MAAM,CAChD,KAAI,CAAC,eAAe,IAAI,QAAQ,EAAE;IAChC,MAAM,aAAa,KAAK,iBAAiB,IAAI,QAAQ;AACrD,QAAI,eAAe,UAAa,eAAe,KAAM,YAAW,UAAU,YAAY;AACtF,SAAK,iBAAiB,IAAI,SAAS,KAAK;;WAGrC,GAAY;AAEnB,OAAI,KAAK,WAAW;AAElB,SAAK,IAAI,OAAO,KAAK,IAAI,MAAM,sCAAsC,EAAE,OAAO,GAAG,CAAC,CAAC;AACnF;;AAGF,OAAI,gBAAgB,EAAE,EAAE;AACtB,YAAQ,KACN,6EACD;AACD;cACS,uBAAuB,EAAE,EAAE,OAKpC,OAAM,IAAI,MAAM,wBAAwB,EAAE,OAAO,GAAG,CAAC;;;;;;;;;;;;;;CAiB7D,MAAa,SACX,YACA,eACA,QACA,SAAmC,QACnC,UAAkB,YAAY,EACb;EACjB,MAAM,aAAa,MAAM,KAAK,IAAI,WAAW,QAAQ,cAAc;EAEnE,MAAM,WAAW,cADS,MAAM,KAAK,IAAI,WAAW,wBAAwB,cAAc,CACzC;AAEjD,OAAK,IAAI,oBAAoB,oBAAoB,SAAS,aAAa;EAGvE,MAAM,eACJ,SAAS,oBAAoB,+BACzB;GAAE,aAAa;GAAsB,WAAW;GAAY,GAC5D;GACE,aAAa;GACb,WAAW;GACX,aAAa,aAAa;IACxB,MAAM,SAAS;IACf,SAAS,SAAS;IACnB,CAAC;GACH;AAEP,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ;AACP,UAAO,IAAI,SACT;IACE,IAAI;IACJ,OAAO;IACP,eAAe,SAAS;IACzB,EACD,cACA,OACD;KAEH;GACE,cAAc;IACZ,GAAG;IACH,mBAAmB,oBAAoB,oBAAoB;IAC5D;GACD,MAAM;GACN,QAAQ,KAAK;GACd,CACF;AAED,QAAM,KAAK,YAAY,cAAc;AAErC,SAAO;;;;;;;;;;;;;;CAeT,MAAa,eACX,iBACA,QACA,SAAmC,QACnC,aAAqB,YAAY,EAChB;AACjB,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ,IAAI,eAAe,iBAAiB,YAAY,OAAO,EAChE;GAAE,MAAM;GAAkB,QAAQ,KAAK;GAAe,CACvD;AACD,QAAM,KAAK,YAAY,cAAc;AAErC,SAAO;;;;;;CAOT,MAAa,gBACX,SACA,eACA,YAAqB,OACrB,QACe;EACf,MAAM,aAAa,MAAM,KAAK,IAAI,WAAW,QAAQ,cAAc;EACnE,MAAM,WAAW,cACf,MAAM,KAAK,IAAI,WAAW,wBAAwB,cAAc,CACjE;AAED,OAAK,IAAI,oBAAoB,oBAAoB,SAAS,aAAa;EAKvE,MAAM,aAAa,YACf,EACE,OACE,SAAS,oBAAoB,IACzB;GAAE,MAAM,SAAS;GAAa,SAAS,SAAS;GAAgB,GAChE,EAAE,EACT,GACD;AACJ,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ,IAAI,iBAAiB,SAAS,YAAY,WAAW,EAC9D;GAAE,MAAM;GAAmB,QAAQ,KAAK;GAAe,CACxD;AACD,QAAM,KAAK,YAAY,cAAc;;;CAIvC,MAAa,YAAY,SAAiB,QAAsC;AAC9E,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ,IAAI,YAAY,QAAQ,EACjC;GAAE,MAAM;GAAe,QAAQ,KAAK;GAAe,CACpD;AACD,OAAK,iBAAiB,YAAY,QAAQ;AAC1C,QAAM,KAAK,YAAY,cAAc;;;;;;;;CASvC,MAAa,cAAc,QAAkB,QAAsC;AACjF,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ;GACP,MAAM,mBAAmB,IAAI;AAC7B,OAAI,iBAAiB,OAAO,WAAW,EACrC,OAAM,IAAI,MAAM,yDAAyD;GAC3E,MAAM,eAAe,iBAAiB,OAAO;AAC7C,OAAI,aAAa,OAAO,WAAW,OAAO,OACxC,OAAM,IAAI,MAAM,oBAAoB,aAAa,OAAO,OAAO,OAAO,OAAO,SAAS;AACxF,OAAI,IAAI,IAAY,OAAO,CAAC,SAAS,OAAO,OAAQ,OAAM,IAAI,MAAM,qBAAqB;GACzF,MAAM,eAAiC,EACrC,QAAQ,CACN;IACE,IAAI,aAAa;IACjB,OAAO,aAAa;IACpB,QAAQ,OAAO,KAAK,YAAY;KAC9B,MAAM,QAAQ,aAAa,OAAO,MAAM,MAAM,EAAE,OAAO,QAAQ;AAC/D,SAAI,UAAU,OAAW,OAAM,IAAI,MAAM,qBAAqB,UAAU;AACxE,YAAO;MACP;IACH,CACF,EACF;AACD,OAAI,gBAAgB,aAAa;KAEnC;GAAE,MAAM;GAAiB,QAAQ,KAAK;GAAe,CACtD;AACD,QAAM,KAAK,YAAY,cAAc;;;;;;;CAQvC,MAAa,SAAS,SAAgC;AACpD,QAAM,YACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,MACJ,QAAQ,IAAI,iBAAiB,CAAC,QAAQ,EAAE,KAAK,EAC9C;GAAE,MAAM;GAAY,QAAQ,KAAK;GAAe,CACjD;AACD,QAAM,KAAK,YAAY,cAAc;;;;;;;CAQvC,MAAa,UAAU,SAAgC;AACrD,QAAM,YACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,MACJ,QAAQ,IAAI,eAAe,QAAQ,EACpC;GAAE,MAAM;GAAa,QAAQ,KAAK;GAAe,CAClD;AACD,QAAM,KAAK,YAAY,cAAc;;;;;;;;;CAUvC,MAAa,aAAa,SAAiB,MAAe,QAAuB;AAC/E,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ;GACP,MAAM,QAAQ,IAAI,gBAAgB,SAAS,EAAE,MAAM,CAAC;AACpD,OAAI,UAAU,CAAC;IAAE,iBAAiB;IAAG;IAAS;IAAO,CAAC,CAAC;KAEzD;GAAE,MAAM;GAAgB,QAAQ,KAAK;GAAe,CACrD;AACD,QAAM,KAAK,YAAY,cAAc;;;;;;;;;CAUvC,MAAa,WAAW,SAAiB,SAAkB,QAAuB;AAChF,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ;GACP,MAAM,QAAQ,IAAI,gBAAgB,SAAS,EAAE,SAAS,CAAC;AACvD,OAAI,UAAU,CAAC;IAAE,iBAAiB;IAAG;IAAS;IAAO,CAAC,CAAC;KAEzD;GAAE,MAAM;GAAc,QAAQ,KAAK;GAAe,CACnD;AACD,QAAM,KAAK,YAAY,cAAc;;;;;;;;;CAUvC,MAAa,uBACX,SACA,MACA,SACA,QACA;EAEA,MAAM,QAAQ;GAAE;GAAM;GAAS;AAC/B,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ;AACP,OAAI,UAAU,CAAC;IAAE,iBAAiB;IAAG;IAAS;IAAO,CAAC,CAAC;KAEzD;GAAE,MAAM;GAA0B,QAAQ,KAAK;GAAe,CAC/D;AACD,QAAM,KAAK,YAAY,cAAc;;;;;CAOvC,MAAa,mBAAmB,SAAiB,OAAuC;AACtF,OAAK,iBAAiB,SAAS,SAAS,MAAM;;;;;;;;;;;;CAahD,MAAa,mBACX,SACA,SACA,QACA;AACA,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ,IAAI,UAAU,CAAC;GAAE,iBAAiB;GAAG;GAAS;GAAS,CAAC,CAAC,EAClE;GAAE,MAAM;GAAsB,QAAQ,KAAK;GAAe,CAC3D;AACD,QAAM,KAAK,YAAY,cAAc;;;CAIvC,MAAa,iBAAiB,SAAiB,UAAyB;AACtE,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ;AACP,OAAI,iBAAiB,SAAS,SAAS;KAEzC,EAAE,MAAM,oBAAoB,CAC7B;AACD,QAAM,KAAK,YAAY,cAAc;;;;;;;;;CAUvC,MAAa,mBAAmB,SAAiB,gBAAuC;AACtF,QAAM,oBACJ,KAAK,IAAI,eACT,KAAK,IAAI,IACT,KAAK,KACL,SACC,QAAQ;AACP,OAAI,mBAAmB,SAAS,eAAe;KAEjD,EAAE,MAAM,sBAAsB,CAC/B;AACD,QAAM,KAAK,YAAY,cAAc;;;CAIvC,MAAa,yBAAyB,SAAiB,QAAsC;AAC3F,QAAM,KAAK,IAAI,GAAG,YAAY,oBAAoB,OAAO,OAAO;GAE9D,MAAM,cAAc,yBACjB,MAAM,GAAG,SAAS,MAAM,KAAK,KAAK,iBAAiB,SAAS,YAAY,CAAC,CAAC,EAAE,MAC9E;GACD,MAAM,QAAQ,yBACX,MAAM,GAAG,SAAS,MAAM,aAAa,GAAG,eAAe,CAAC,EAAE,MAC5D;GAED,MAAM,SAAS,cAAc,kBAAiC,UAD/C,MAAM,GAAG,gBAAgB,OAAO,MAAM,EACyB,KAAK,CAAC,CAAC,OAAO;AAE5F,SAAM,oBACJ,KAAK,IAAI,eACT,IACA,KAAK,KACL,SACC,QAAQ;AACP,QAAI,OAAO,oBAAoB,6BAE7B,KAAI,sBAAsB,QAAQ;SAC7B;KAEL,MAAM,eAAe;MAAE,MAAM,OAAO;MAAa,SAAS,OAAO;MAAgB;AACjF,SAAI,UAAU,CAAC;MAAE,iBAAiB;MAAG;MAAS,OAAO;MAAc,CAAC,CAAC;;MAGzE;IAAE,MAAM;IAA4B,QAAQ,KAAK;IAAe,CACjE;AACD,SAAM,GAAG,QAAQ;IACjB;AACF,QAAM,KAAK,YAAY,cAAc;;CAGvC,AAAQ,oBAAoB,SAAwC;EAClE,MAAM,SAAS,KAAK,iBAAiB,IAAI,QAAQ;AACjD,MAAI,WAAW,KAAM,OAAM,IAAI,MAAM,SAAS,QAAQ,aAAa;AACnE,MAAI,WAAW,QAAW;GAExB,MAAM,UAAU,aAAa,KAAK,YAAY,OAAO,EAAE,SAAS,KAAK,IAAI;GA6BzE,MAAM,cAAqC,EACzC,WA7BgB,WAAW,MAC1B,QAAQ;AACP,WAAO;KACL,YAAY,mBAAmB,KAAK,YAAY,OAAO,EAAE,SAAS,IAAI;KACtE;KACA,iBAAiB,KAAK,iBAAiB,SAAS,QAAQ;KACxD,UAAU,KAAK;KAChB;MAEH,EACE,mBAAmB,MAAM;IACvB,MAAM,gBAAgB,EAAE,UAAU,QAAQ,MAAM,MAAM,EAAE,MAAM,QAAQ;IACtE,MAAM,aAAa,eAAe;IAClC,MAAM,mBAAmB,eAAe;IAExC,MAAM,aADW,cAAc,2BAA2B,WAAW,IAEvD,EAAE,YAAY,SAAY,uBAAuB,EAAE,QAAQ,GAAG,EAAE;AAE9E,WAAO;KACL,GAAG,EAAE;KACL,SAAS;KACT,iBAAiB,EAAE;KACnB;KACD;MAEJ,CACF,CAGsB,4BAA4B,EAClD;AAED,QAAK,iBAAiB,IAAI,SAAS,YAAY;AAE/C,UAAO;;AAET,SAAO;;;;;;CAOT,AAAO,cAAc,SAAmD;AACtE,SAAO,KAAK,oBAAoB,QAAQ,CAAC;;;;;;CAO3C,AAAO,iBAAiB,SAAwD;EAC9E,MAAM,SAAS,KAAK,eAAe,IAAI,QAAQ;AAC/C,MAAI,WAAW,QAAW;GACxB,MAAM,KAAK,aACT,KAAK,YAAY,OAAO,EACxB,SACA,KAAK,IACN,CAAC,4BAA4B;AAC9B,QAAK,eAAe,IAAI,SAAS,GAAG;AACpC,UAAO;;AAET,SAAO;;;CAIT,MAAa,UAAyB;AAEpC,OAAK,gBAAgB,OAAO;AAC5B,MAAI;AACF,SAAM,KAAK;WACJ,GAAY;AAEnB,QAAK,IAAI,OAAO,KACd,IAAI,MAAM,yDAAyD,EAAE,OAAO,GAAG,CAAC,CACjF;;AAIH,MAAI;AACF,SAAM,KAAK,YAAY,WAAW;WAC3B,GAAY;AAGnB,QAAK,IAAI,OAAO,KAAK,IAAI,MAAM,mCAAmC,EAAE,OAAO,GAAG,CAAC,CAAC;;AAKlF,OAAK,SAAS,YAAY;AAC1B,OAAK,eAAe,SAAS,MAAM,EAAE,YAAY,CAAC;AAClD,OAAK,iBAAiB,SAAS,MAAM;AACnC,OAAI,MAAM,KAAM,GAAE,UAAU,YAAY;IACxC;AACF,OAAK,cAAc,YAAY;;;CAIjC,MAAa,6BAA4C;AACvD,QAAM,KAAK,SAAS;;CAGtB,AAAO,YAAoC;AACzC,SAAO,KAAK,YAAY,WAAW;;CAGrC,aAAoB,KAAK,KAA6B,KAAmC;AAEvF,QAAM,uBAAuB,IAAI,IAAI,IAAI;AAGzC,QAAM,YAAY,IAAI,eAAe,IAAI,IAAI,MAAM,MAAM,IAAI,EAAE,MAAM,QAAQ,CAAC;EAG9E,MAAM,cAAc,MAAM,sBAAsB,KAC9C,IAAI,IACJ,KACA;GACE,GAAG,IAAI,IAAI;GACX,SAAS,mBAAmB,IAAI,OAAO;GACxC,EACD,IAAI,OACL;AAED,MAAI,IAAI,IAAI,SAAS,sBAAsB;GACzC,MAAM,QAAQ,YAAY,WAAW;AACrC,SAAM,MAAM,GAAG,OAAO,EAAE,MAAM,cAAc,MAAM,EAAE,MAAM,cAAc,GAAG;GAC3E,MAAM,QAAQ,cAAc,MAAM;AAClC,SAAM,GAAG,UAAU,GAAG,mBAAmB,IAAI,CAAC,QAAQ,iBAAiB,MAAM,CAAC;AAC9E,SAAM,GAAG,UAAU,GAAG,mBAAmB,IAAI,CAAC,cAAc,iBAAiB,MAAM,CAAC;;AAGtF,SAAO,IAAI,QAAQ,KAAK,KAAK,YAAY;;;AAI7C,SAAS,mBAAmB,QAAmC;AAC7D,SAAQ,MAAyC;AAC/C,MAAI,EAAE,OAAO,SAAS,IACpB,QAAO,KACL,6CAA6C,EAAE,KAAK,KAAK,MAAM,EAAE,GAAG,UAAU,EAAE,OAAO,iBAC1E,EAAE,OACV,MAAM,GAAG,GAAG,CACZ,KAAK,MAAM,EAAE,KAAK,CAClB,KAAK,KAAK,CAAC,QACjB;AACH,MAAI,EAAE,KAAK,KAAK,WAAW,iBAAiB,CAAE,QAAO,EAAE;AACvD,UAAQ,EAAE,KAAK,MAAf;GACE,KAAK,kBACH,QAAO,EAAE,OAAO,QAAQ,MAAM,EAAE,SAAS,WAAW;GACtD,KAAK,cACH,QAAO,EAAE,OAAO,QAAQ,MAAM,CAAC,EAAE,KAAK,WAAW,oBAAoB,CAAC;GACxE,KAAK,OACH,QAAO,EAAE;GACX,QACE,QAAO,EAAE;;;;;;;AAQjB,SAAS,2BAA2B,YAA6B;AAC/D,QAAO,CAAC,iBAAiB,YAAY,SAAS;;;AAIhD,SAAS,iBAAiB,YAAoB,eAAgC;CAC5E,MAAM,UAAU,WAAW,MAAM,IAAI,CAAC,IAAI,OAAO;CACjD,MAAM,SAAS,cAAc,MAAM,IAAI,CAAC,IAAI,OAAO;AAEnD,QACE,QAAQ,KAAK,OAAO,MACnB,QAAQ,OAAO,OAAO,MAAM,QAAQ,KAAK,OAAO,MAChD,QAAQ,OAAO,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,QAAQ,KAAK,OAAO;;;AAKjF,SAAS,uBACP,SACkD;CAClD,MAAM,SAA2D,EAAE;AACnE,MAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,EAAE;AAChD,MAAI,IAAI,IAAI;AACV,UAAO,OAAO;AACd;;AAGF,SAAO,OAAO;GACZ,IAAI;GACJ,QAAQ,IAAI,OAAO,KAAK,MAAM;AAC5B,QAAI,OAAO,MAAM,SACf,QAAO;aACE,EAAE,QAAQ,aAAa,EAAE,gBAAgB,OAClD,QAAO,EAAE;AAEX,WAAO,EAAE;KACT;GACF,YAAY,IAAI;GACjB;;AAGH,QAAO"}
1
+ {"version":3,"file":"project.js","names":[],"sources":["../../src/middle_layer/project.ts"],"sourcesContent":["import type { MiddleLayerEnvironment } from \"./middle_layer\";\nimport type { FieldData, OptionalAnyResourceId, ResourceId } from \"@milaboratories/pl-client\";\nimport {\n DefaultRetryOptions,\n ensureResourceIdNotNull,\n field,\n isNotFoundError,\n isTimeoutOrCancelError,\n Pl,\n resourceIdToString,\n} from \"@milaboratories/pl-client\";\nimport type { ComputableStableDefined, ComputableValueOrErrors } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport { projectOverview } from \"./project_overview\";\nimport type { BlockPackSpecAny } from \"../model\";\nimport { randomUUID } from \"node:crypto\";\nimport { withProject, withProjectAuthored } from \"../mutator/project\";\nimport type { ExtendedResourceData, PruningFunction } from \"@milaboratories/pl-tree\";\nimport { SynchronizedTreeState, treeDumpStats } from \"@milaboratories/pl-tree\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { frontendData } from \"./frontend_path\";\nimport type { NavigationState } from \"@milaboratories/pl-model-common\";\nimport { getBlockParameters, blockOutputs } from \"./block\";\nimport type { FrontendData } from \"../model/frontend\";\nimport type { ProjectStructure } from \"../model/project_model\";\nimport { projectFieldName } from \"../model/project_model\";\nimport {\n cachedDeserialize,\n notEmpty,\n type MiLogger,\n createInfiniteRetryState,\n nextInfiniteRetryState,\n type InfiniteRetryState,\n} from \"@milaboratories/ts-helpers\";\nimport type { BlockPackInfo } from \"../model/block_pack\";\nimport type {\n ProjectOverview,\n AuthorMarker,\n BlockSettings,\n BlockStateInternalV3,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { activeConfigs } from \"./active_cfg\";\nimport { NavigationStates } from \"./navigation_states\";\nimport { extractConfig, BLOCK_STORAGE_FACADE_VERSION } from \"@platforma-sdk/model\";\nimport fs from \"node:fs/promises\";\nimport canonicalize from \"canonicalize\";\nimport type { ProjectOverviewLight } from \"./project_overview_light\";\nimport { projectOverviewLight } from \"./project_overview_light\";\nimport { applyProjectMigrations } from \"../mutator/migration\";\n\ntype BlockStateComputables = {\n readonly fullState: Computable<BlockStateInternalV3>;\n};\n\nfunction stringifyForDump(object: unknown): string {\n return JSON.stringify(object, (key, value) => {\n if (typeof value === \"bigint\") return resourceIdToString(value as OptionalAnyResourceId);\n else if (\n ArrayBuffer.isView(value) ||\n value instanceof Int8Array ||\n value instanceof Uint8Array ||\n value instanceof Uint8ClampedArray ||\n value instanceof Int16Array ||\n value instanceof Uint16Array ||\n value instanceof Int32Array ||\n value instanceof Uint32Array ||\n value instanceof Float32Array ||\n value instanceof Float64Array ||\n value instanceof BigInt64Array ||\n value instanceof BigUint64Array\n )\n return Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString(\"base64\");\n else if (Buffer.isBuffer(value)) return value.toString(\"base64\");\n\n return value;\n });\n}\n\n/** Data access object, to manipulate and read single opened (!) project data. */\nexport class Project {\n /** Underlying pl resource id */\n public readonly rid: ResourceId;\n\n /** Data for the left panel, contain basic information about block status. */\n public readonly overview: ComputableStableDefined<ProjectOverview>;\n private readonly overviewLight: Computable<ProjectOverviewLight>;\n\n private readonly navigationStates = new NavigationStates();\n // null is set for deleted blocks\n private readonly blockComputables = new Map<string, BlockStateComputables | null>();\n\n private readonly blockFrontends = new Map<string, ComputableStableDefined<FrontendData>>();\n private readonly activeConfigs: Computable<unknown[]>;\n private readonly refreshLoopResult: Promise<void>;\n\n private readonly abortController = new AbortController();\n\n private get destroyed() {\n return this.abortController.signal.aborted;\n }\n\n constructor(\n private readonly env: MiddleLayerEnvironment,\n rid: ResourceId,\n private readonly projectTree: SynchronizedTreeState,\n ) {\n this.overview = projectOverview(\n projectTree.entry(),\n this.navigationStates,\n env,\n ).withPreCalculatedValueTree();\n this.overviewLight = projectOverviewLight(projectTree.entry()).withPreCalculatedValueTree();\n this.rid = rid;\n this.refreshLoopResult = this.refreshLoop();\n this.refreshLoopResult.catch((err) => {\n env.logger.warn(new Error(\"Error during refresh loop\", { cause: err })); // TODO (safe voiding for now)\n });\n this.activeConfigs = activeConfigs(projectTree.entry(), env);\n }\n\n get projectLockId(): string {\n return \"project:\" + this.rid.toString();\n }\n\n private async refreshLoop(): Promise<void> {\n let retryState: InfiniteRetryState | undefined;\n while (!this.destroyed) {\n try {\n await withProject(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n (prj) => {\n prj.doRefresh(this.env.ops.stagingRenderingRate);\n },\n { name: \"doRefresh\", lockId: this.projectLockId },\n );\n await this.activeConfigs.getValue();\n await setTimeout(this.env.ops.projectRefreshInterval, undefined, {\n signal: this.abortController.signal,\n });\n\n // Block computables housekeeping\n const overviewLight = await this.overviewLight.getValue();\n const existingBlocks = new Set(overviewLight.listOfBlocks);\n // Doing cleanup for deleted blocks\n for (const blockId of this.blockComputables.keys()) {\n if (!existingBlocks.has(blockId)) {\n const computable = this.blockComputables.get(blockId);\n if (computable !== undefined && computable !== null) computable.fullState.resetState();\n this.blockComputables.set(blockId, null);\n }\n }\n retryState = undefined;\n } catch (e: unknown) {\n // If we're destroyed, exit gracefully regardless of error type\n if (this.destroyed) break;\n\n if (isNotFoundError(e)) {\n this.env.logger.warn(\n \"project refresh routine terminated, because project was externally deleted\",\n );\n break;\n } else if (isTimeoutOrCancelError(e)) {\n // Timeout during normal operation, continue the loop\n } else {\n retryState = retryState\n ? nextInfiniteRetryState(retryState)\n : createInfiniteRetryState({\n type: \"exponentialWithMaxDelayBackoff\",\n initialDelay: 1000,\n maxDelay: 60_000,\n backoffMultiplier: 2,\n jitter: 0,\n });\n this.env.logger.error(\n new Error(`[refreshLoop] unexpected exception, retrying in ${retryState.nextDelay}ms`, {\n cause: e,\n }),\n );\n try {\n await setTimeout(retryState.nextDelay, undefined, {\n signal: this.abortController.signal,\n });\n } catch {\n // Aborted during retry delay, will exit via while condition or destroyed check\n break;\n }\n }\n }\n }\n }\n\n /**\n * Adds new block to the project.\n *\n * @param blockLabel block label / title visible to the user\n * @param blockPackSpec object describing the \"block type\", read more in the type docs\n * @param before id of the block to insert new block before\n * @param blockId internal id to be assigned for the block, this arg can be omitted\n * then, randomly generated UUID will be assigned automatically\n *\n * @return returns newly created block id\n * */\n public async addBlock(\n blockLabel: string,\n blockPackSpec: BlockPackSpecAny,\n before?: string,\n author: AuthorMarker | undefined = undefined,\n blockId: string = randomUUID(),\n ): Promise<string> {\n const preparedBp = await this.env.bpPreparer.prepare(blockPackSpec);\n const blockCfgContainer = await this.env.bpPreparer.getBlockConfigContainer(blockPackSpec);\n const blockCfg = extractConfig(blockCfgContainer); // full content of this var should never be persisted\n\n this.env.runtimeCapabilities.throwIfIncompatible(blockCfg.featureFlags);\n\n // Build NewBlockSpec based on model API version\n const newBlockSpec =\n blockCfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION\n ? { storageMode: \"fromModel\" as const, blockPack: preparedBp }\n : {\n storageMode: \"legacy\" as const,\n blockPack: preparedBp,\n legacyState: canonicalize({\n args: blockCfg.initialArgs,\n uiState: blockCfg.initialUiState,\n })!,\n };\n\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n return mut.addBlock(\n {\n id: blockId,\n label: blockLabel,\n renderingMode: blockCfg.renderingMode,\n },\n newBlockSpec,\n before,\n );\n },\n {\n retryOptions: {\n ...DefaultRetryOptions,\n backoffMultiplier: DefaultRetryOptions.backoffMultiplier * 1.1,\n },\n name: \"addBlock\",\n lockId: this.projectLockId,\n },\n );\n\n await this.projectTree.refreshState();\n\n return blockId;\n }\n\n /**\n * Duplicates an existing block by copying all its fields and state.\n * This method works at the mutator level for efficient block copying.\n *\n * @param originalBlockId id of the block to duplicate\n * @param before id of the block to insert new block before\n * @param author author marker for the duplication operation\n * @param newBlockId internal id to be assigned for the duplicated block,\n * if omitted, a randomly generated UUID will be assigned\n *\n * @return returns newly created block id\n * */\n public async duplicateBlock(\n originalBlockId: string,\n before?: string,\n author: AuthorMarker | undefined = undefined,\n newBlockId: string = randomUUID(),\n ): Promise<string> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.duplicateBlock(originalBlockId, newBlockId, before),\n { name: \"duplicateBlock\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n\n return newBlockId;\n }\n\n /**\n * Update block to new block pack, optionally resetting args and ui state to\n * initial values\n * */\n public async updateBlockPack(\n blockId: string,\n blockPackSpec: BlockPackSpecAny,\n resetArgs: boolean = false,\n author?: AuthorMarker,\n ): Promise<void> {\n const preparedBp = await this.env.bpPreparer.prepare(blockPackSpec);\n const blockCfg = extractConfig(\n await this.env.bpPreparer.getBlockConfigContainer(blockPackSpec),\n );\n\n this.env.runtimeCapabilities.throwIfIncompatible(blockCfg.featureFlags);\n\n // resetState signals to mutator to reset storage\n // For v2+ blocks: mutator gets initial storage directly via getInitialStorageInVM\n // For v1 blocks: we pass the legacy state format\n const resetState = resetArgs\n ? {\n state:\n blockCfg.modelAPIVersion === 1\n ? { args: blockCfg.initialArgs, uiState: blockCfg.initialUiState }\n : {},\n }\n : undefined;\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.migrateBlockPack(blockId, preparedBp, resetState),\n { name: \"updateBlockPack\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /** Deletes a block with all associated data. */\n public async deleteBlock(blockId: string, author?: AuthorMarker): Promise<void> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.deleteBlock(blockId),\n { name: \"deleteBlock\", lockId: this.projectLockId },\n );\n this.navigationStates.deleteBlock(blockId);\n await this.projectTree.refreshState();\n }\n\n /**\n * Updates block order according to the given array of block ids.\n *\n * Provided array must contain exactly the same set of ids current project cosists of,\n * an error will be thrown instead.\n */\n public async reorderBlocks(blocks: string[], author?: AuthorMarker): Promise<void> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n const currentStructure = mut.structure;\n if (currentStructure.groups.length !== 1)\n throw new Error(\"Unexpected project structure, non-singular block group\");\n const currentGroup = currentStructure.groups[0];\n if (currentGroup.blocks.length !== blocks.length)\n throw new Error(`Length mismatch: ${currentGroup.blocks.length} !== ${blocks.length}`);\n if (new Set<string>(blocks).size !== blocks.length) throw new Error(`Repeated block ids`);\n const newStructure: ProjectStructure = {\n groups: [\n {\n id: currentGroup.id,\n label: currentGroup.label,\n blocks: blocks.map((blockId) => {\n const block = currentGroup.blocks.find((b) => b.id === blockId);\n if (block === undefined) throw new Error(`Can't find block: ${blockId}`);\n return block;\n }),\n },\n ],\n };\n mut.updateStructure(newStructure);\n },\n { name: \"reorderBlocks\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Renders production part of the block starting all connected heavy computations.\n * Upstream blocks of the specified block will be started automatically if in\n * stale state.\n * */\n public async runBlock(blockId: string): Promise<void> {\n await withProject(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n (mut) => mut.renderProduction([blockId], true),\n { name: \"runBlock\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Stops the block if it is running by destroying its production state. All\n * its downstreams will also be destroyed or moved to limbo if already\n * calculated.\n * */\n public async stopBlock(blockId: string): Promise<void> {\n await withProject(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n (mut) => mut.stopProduction(blockId),\n { name: \"stopBlock\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * @deprecated Use mutateBlockStorage() for V3 blocks.\n * Sets block args, and changes whole project state accordingly.\n * Along with setting arguments one can specify author marker, that will be\n * transactionally associated with the block, to facilitate conflict resolution\n * in collaborative editing scenario.\n * */\n public async setBlockArgs(blockId: string, args: unknown, author?: AuthorMarker) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n const state = mut.mergeBlockState(blockId, { args });\n mut.setStates([{ modelAPIVersion: 1, blockId, state }]);\n },\n { name: \"setBlockArgs\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * @deprecated Use mutateBlockStorage() for V3 blocks.\n * Sets ui block state associated with the block.\n * Along with setting arguments one can specify author marker, that will be\n * transactionally associated with the block, to facilitate conflict resolution\n * in collaborative editing scenario.\n * */\n public async setUiState(blockId: string, uiState: unknown, author?: AuthorMarker) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n const state = mut.mergeBlockState(blockId, { uiState });\n mut.setStates([{ modelAPIVersion: 1, blockId, state }]);\n },\n { name: \"setUiState\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * @deprecated Use mutateBlockStorage() for V3 blocks.\n * Sets block args and ui state, and changes the whole project state accordingly.\n * Along with setting arguments one can specify author marker, that will be\n * transactionally associated with the block, to facilitate conflict resolution\n * in collaborative editing scenario.\n * */\n public async setBlockArgsAndUiState(\n blockId: string,\n args: unknown, // keep for v1/v2 compatibility\n uiState: unknown, // keep for v1/v2 compatibility\n author?: AuthorMarker,\n ) {\n // Normalize to unified state format { args, uiState } for v1/v2 blocks\n const state = { args, uiState };\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => {\n mut.setStates([{ modelAPIVersion: 1, blockId, state }]);\n },\n { name: \"setBlockArgsAndUiState\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Sets navigation state.\n * */\n //\n public async setNavigationState(blockId: string, state: NavigationState): Promise<void> {\n this.navigationStates.setState(blockId, state);\n }\n\n /**\n * Mutates block storage for Model API v3 blocks.\n * Applies a storage operation (e.g., 'update-data') which triggers\n * args derivation (args(data) and prerunArgs(data)).\n * The derived args are stored atomically with the data.\n *\n * @param blockId - The block ID\n * @param payload - Storage mutation payload with operation and value\n * @param author - Optional author marker for collaborative editing\n */\n public async mutateBlockStorage(\n blockId: string,\n payload: { operation: string; value: unknown },\n author?: AuthorMarker,\n ) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n author,\n (mut) => mut.setStates([{ modelAPIVersion: 2, blockId, payload }]),\n { name: \"mutateBlockStorage\", lockId: this.projectLockId },\n );\n await this.projectTree.refreshState();\n }\n\n /** Update block settings */\n public async setBlockSettings(blockId: string, newValue: BlockSettings) {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n undefined,\n (mut) => {\n mut.setBlockSettings(blockId, newValue);\n },\n { name: \"setBlockSettings\" },\n );\n await this.projectTree.refreshState();\n }\n\n /**\n * Sets raw block storage content directly.\n * This bypasses all normalization and VM transformations.\n *\n * @param blockId The block to set storage for\n * @param rawStorageJson Raw storage as JSON string\n */\n public async setBlockStorageRaw(blockId: string, rawStorageJson: string): Promise<void> {\n await withProjectAuthored(\n this.env.projectHelper,\n this.env.pl,\n this.rid,\n undefined,\n (mut) => {\n mut.setBlockStorageRaw(blockId, rawStorageJson);\n },\n { name: \"setBlockStorageRaw\" },\n );\n await this.projectTree.refreshState();\n }\n\n /** Resets arguments and ui state of the block to initial state */\n public async resetBlockArgsAndUiState(blockId: string, author?: AuthorMarker): Promise<void> {\n await this.env.pl.withWriteTx(\"BlockInputsReset\", async (tx) => {\n // reading default arg values from block pack\n const bpHolderRid = ensureResourceIdNotNull(\n (await tx.getField(field(this.rid, projectFieldName(blockId, \"blockPack\")))).value,\n );\n const bpRid = ensureResourceIdNotNull(\n (await tx.getField(field(bpHolderRid, Pl.HolderRefField))).value,\n );\n const bpData = await tx.getResourceData(bpRid, false);\n const config = extractConfig(cachedDeserialize<BlockPackInfo>(notEmpty(bpData.data)).config);\n\n await withProjectAuthored(\n this.env.projectHelper,\n tx,\n this.rid,\n author,\n (prj) => {\n if (config.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION) {\n // V2+: Reset to initial storage via VM\n prj.resetToInitialStorage(blockId);\n } else {\n // V1: Use legacy state format\n const initialState = { args: config.initialArgs, uiState: config.initialUiState };\n prj.setStates([{ modelAPIVersion: 1, blockId, state: initialState }]);\n }\n },\n { name: \"resetBlockArgsAndUiState\", lockId: this.projectLockId },\n );\n await tx.commit();\n });\n await this.projectTree.refreshState();\n }\n\n private getBlockComputables(blockId: string): BlockStateComputables {\n const cached = this.blockComputables.get(blockId);\n if (cached === null) throw new Error(`Block ${blockId} is deleted`);\n if (cached === undefined) {\n // state consists of inputs (args + ui state) and outputs\n const outputs = blockOutputs(this.projectTree.entry(), blockId, this.env);\n const fullState = Computable.make(\n (ctx) => {\n return {\n parameters: getBlockParameters(this.projectTree.entry(), blockId, ctx),\n outputs,\n navigationState: this.navigationStates.getState(blockId),\n overview: this.overview,\n };\n },\n {\n postprocessValue: (v) => {\n const blockOverview = v.overview?.blocks?.find((b) => b.id == blockId);\n const sdkVersion = blockOverview?.sdkVersion;\n const storageDebugView = blockOverview?.storageDebugView;\n const toString = sdkVersion && shouldStillUseStringErrors(sdkVersion);\n const newOutputs =\n toString && v.outputs !== undefined ? convertErrorsToStrings(v.outputs) : v.outputs;\n\n return {\n ...v.parameters,\n outputs: newOutputs,\n navigationState: v.navigationState,\n storageDebugView,\n } as BlockStateInternalV3;\n },\n },\n );\n\n const computables: BlockStateComputables = {\n fullState: fullState.withPreCalculatedValueTree(),\n };\n\n this.blockComputables.set(blockId, computables);\n\n return computables;\n }\n return cached;\n }\n\n /**\n * Returns a computable, that can be used to retrieve and watch full block state,\n * including outputs, arguments, ui state.\n * */\n public getBlockState(blockId: string): Computable<BlockStateInternalV3> {\n return this.getBlockComputables(blockId).fullState;\n }\n\n /**\n * Returns a computable, that can be used to retrieve and watch path of the\n * folder containing frontend code.\n * */\n public getBlockFrontend(blockId: string): ComputableStableDefined<FrontendData> {\n const cached = this.blockFrontends.get(blockId);\n if (cached === undefined) {\n const fd = frontendData(\n this.projectTree.entry(),\n blockId,\n this.env,\n ).withPreCalculatedValueTree();\n this.blockFrontends.set(blockId, fd);\n return fd;\n }\n return cached;\n }\n\n /** Called by middle layer on close */\n public async destroy(): Promise<void> {\n // terminating the project service loop\n this.abortController.abort();\n try {\n await this.refreshLoopResult;\n } catch (e: unknown) {\n // Error was already logged in the constructor's catch handler, but log again for context\n this.env.logger.warn(\n new Error(\"Refresh loop had terminated with error before destroy\", { cause: e }),\n );\n }\n\n // terminating the synchronized project tree\n try {\n await this.projectTree.terminate();\n } catch (e: unknown) {\n // TODO: SynchronizedTreeState.terminate() can throw if mainLoop had an error before termination\n // Log error but continue cleanup - we must clean up remaining resources\n this.env.logger.warn(new Error(\"Project tree termination failed\", { cause: e }));\n }\n\n // the following will deregister all external resource holders, like\n // downloaded files, running uploads and alike\n this.overview.resetState();\n this.blockFrontends.forEach((c) => c.resetState());\n this.blockComputables.forEach((c) => {\n if (c !== null) c.fullState.resetState();\n });\n this.activeConfigs.resetState();\n }\n\n /** @deprecated */\n public async destroyAndAwaitTermination(): Promise<void> {\n await this.destroy();\n }\n\n public dumpState(): ExtendedResourceData[] {\n return this.projectTree.dumpState();\n }\n\n public static async init(env: MiddleLayerEnvironment, rid: ResourceId): Promise<Project> {\n // Applying migrations to the project resource, if needed\n await applyProjectMigrations(env.pl, rid);\n\n // Doing a no-op mutation to apply all migration and schema fixes\n await withProject(env.projectHelper, env.pl, rid, (_) => {}, { name: \"init\" });\n\n // Loading project tree\n const projectTree = await SynchronizedTreeState.init(\n env.pl,\n rid,\n {\n ...env.ops.defaultTreeOptions,\n pruning: projectTreePruning(env.logger),\n },\n env.logger,\n );\n\n if (env.ops.debugOps.dumpInitialTreeState) {\n const state = projectTree.dumpState();\n state.sort((a, b) => (b.data?.byteLength ?? 0) - (a.data?.byteLength ?? 0));\n const stats = treeDumpStats(state);\n await fs.writeFile(`${resourceIdToString(rid)}.json`, stringifyForDump(state));\n await fs.writeFile(`${resourceIdToString(rid)}.stats.json`, stringifyForDump(stats));\n }\n\n return new Project(env, rid, projectTree);\n }\n}\n\nfunction projectTreePruning(logger: MiLogger): PruningFunction {\n return (r: ExtendedResourceData): FieldData[] => {\n if (r.fields.length > 1000)\n logger.warn(\n `resource with excessive field count: type=${r.type.name} id=${r.id} fields=${r.fields.length}` +\n ` names=[${r.fields\n .slice(0, 10)\n .map((f) => f.name)\n .join(\", \")}, ...]`,\n );\n if (r.type.name.startsWith(\"StreamWorkdir/\")) return [];\n switch (r.type.name) {\n case \"BlockPackCustom\":\n return r.fields.filter((f) => f.name !== \"template\");\n case \"UserProject\":\n return r.fields.filter((f) => !f.name.startsWith(\"__serviceTemplate\"));\n case \"Blob\":\n return [];\n default:\n return r.fields;\n }\n };\n}\n\n/** Returns true if sdk version of the block is old and we need to convert\n * ErrorLike errors to strings like it was.\n * We need it for keeping old blocks and new UI compatibility. */\nfunction shouldStillUseStringErrors(sdkVersion: string): boolean {\n return !isVersionGreater(sdkVersion, \"1.26.0\");\n}\n\n/** Checks if sdk version is greater that a target version. */\nfunction isVersionGreater(sdkVersion: string, targetVersion: string): boolean {\n const version = sdkVersion.split(\".\").map(Number);\n const target = targetVersion.split(\".\").map(Number);\n\n return (\n version[0] > target[0] ||\n (version[0] === target[0] && version[1] > target[1]) ||\n (version[0] === target[0] && version[1] === target[1] && version[2] > target[2])\n );\n}\n\n/** Converts ErrorLike errors to strings in the outputs like it was in old ML versions. */\nfunction convertErrorsToStrings(\n outputs: Record<string, ComputableValueOrErrors<unknown>>,\n): Record<string, ComputableValueOrErrors<unknown>> {\n const result: Record<string, ComputableValueOrErrors<unknown>> = {};\n for (const [key, val] of Object.entries(outputs)) {\n if (val.ok) {\n result[key] = val;\n continue;\n }\n\n result[key] = {\n ok: false,\n errors: val.errors.map((e) => {\n if (typeof e === \"string\") {\n return e;\n } else if (e.type == \"PlError\" && e.fullMessage !== undefined) {\n return e.fullMessage;\n }\n return e.message;\n }),\n moreErrors: val.moreErrors,\n };\n }\n\n return result;\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
2
2
  const require_index = require('../js_render/index.cjs');
3
3
  let _platforma_sdk_model = require("@platforma-sdk/model");
4
+ let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
4
5
  let lru_cache = require("lru-cache");
5
6
 
6
7
  //#region src/model/project_helper.ts
@@ -11,8 +12,9 @@ var ProjectHelper = class {
11
12
  return { value: this.calculateEnrichmentTargets(context) };
12
13
  }
13
14
  });
14
- constructor(quickJs) {
15
+ constructor(quickJs, logger = new _milaboratories_ts_helpers.ConsoleLoggerAdapter()) {
15
16
  this.quickJs = quickJs;
17
+ this.logger = logger;
16
18
  }
17
19
  /**
18
20
  * Derives args directly from storage JSON using VM callback.
@@ -83,7 +85,7 @@ var ProjectHelper = class {
83
85
  try {
84
86
  return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageInitial], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig));
85
87
  } catch (e) {
86
- console.error("[ProjectHelper.getInitialStorageInVM] Initial storage creation failed:", e);
88
+ this.logger.error(new Error("[ProjectHelper.getInitialStorageInVM] Initial storage creation failed", { cause: e }));
87
89
  throw new Error(`Block initial storage creation failed: ${e}`);
88
90
  }
89
91
  }
@@ -105,7 +107,7 @@ var ProjectHelper = class {
105
107
  try {
106
108
  return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageApplyUpdate], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), currentStorageJson, payload);
107
109
  } catch (e) {
108
- console.error("[ProjectHelper.applyStorageUpdateInVM] Storage update failed:", e);
110
+ this.logger.error(new Error("[ProjectHelper.applyStorageUpdateInVM] Storage update failed", { cause: e }));
109
111
  throw new Error(`Block storage update failed: ${e}`);
110
112
  }
111
113
  }
@@ -122,7 +124,7 @@ var ProjectHelper = class {
122
124
  try {
123
125
  return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageDebugView], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), rawStorageJson);
124
126
  } catch (e) {
125
- console.error("[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed:", e);
127
+ this.logger.error(new Error("[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed", { cause: e }));
126
128
  return;
127
129
  }
128
130
  }
@@ -146,7 +148,7 @@ var ProjectHelper = class {
146
148
  try {
147
149
  return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageMigrate], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), currentStorageJson);
148
150
  } catch (e) {
149
- console.error("[ProjectHelper.migrateStorageInVM] Migration failed:", e);
151
+ this.logger.error(new Error("[ProjectHelper.migrateStorageInVM] Migration failed", { cause: e }));
150
152
  return { error: `VM execution failed: ${e}` };
151
153
  }
152
154
  }
@@ -1 +1 @@
1
- {"version":3,"file":"project_helper.cjs","names":["LRUCache","BLOCK_STORAGE_FACADE_VERSION","executeSingleLambda","BlockStorageFacadeCallbacks"],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type {\n ResultOrError,\n BlockConfig,\n BlockStorage,\n PlRef,\n StorageDebugView,\n} from \"@platforma-sdk/model\";\nimport type { StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport {\n extractCodeWithInfo,\n ensureError,\n BlockStorageFacadeCallbacks,\n BLOCK_STORAGE_FACADE_VERSION,\n} from \"@platforma-sdk/model\";\nimport { LRUCache } from \"lru-cache\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { executeSingleLambda } from \"../js_render\";\nimport type { ResourceId } from \"@milaboratories/pl-client\";\n\ntype EnrichmentTargetsRequest = {\n blockConfig: () => BlockConfig;\n args: () => unknown;\n};\n\ntype EnrichmentTargetsValue = {\n value: PlRef[] | undefined;\n};\n\n/**\n * Result of VM-based storage migration.\n * Returned by migrateStorageInVM().\n *\n * - Error result: { error: string } - serious failure (no context, etc.)\n * - Success result: { newStorageJson: StringifiedJson<BlockStorage>, info: string } - migration succeeded\n */\nexport type MigrationResult =\n | { error: string }\n | { error?: undefined; newStorageJson: StringifiedJson<BlockStorage>; info: string };\n\n/**\n * Result of args derivation from storage.\n * Returned by __pl_args_derive and __pl_prerunArgs_derive VM callbacks.\n */\ntype ArgsDeriveResult = { error: string } | { error?: undefined; value: unknown };\n\nexport class ProjectHelper {\n private readonly enrichmentTargetsCache = new LRUCache<\n string,\n EnrichmentTargetsValue,\n EnrichmentTargetsRequest\n >({\n max: 256,\n memoMethod: (_key, _value, { context }) => {\n return { value: this.calculateEnrichmentTargets(context) };\n },\n });\n\n constructor(private readonly quickJs: QuickJSWASMModule) {}\n\n // =============================================================================\n // Args Derivation from Storage (V3+)\n // =============================================================================\n\n /**\n * Derives args directly from storage JSON using VM callback.\n * The VM extracts data from storage and calls the block's args() function.\n *\n * This allows the middle layer to work only with storage JSON,\n * without needing to know the underlying data structure.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived args object, or error if derivation fails\n */\n public deriveArgsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"deriveArgsFromStorage is only supported for model API version 2\"),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.ArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n return { error: new Error(result.error) };\n }\n return { value: result.value };\n } catch (e) {\n return { error: new Error(\"Args derivation from storage failed\", { cause: ensureError(e) }) };\n }\n }\n\n /**\n * Derives prerunArgs directly from storage JSON using VM callback.\n * Falls back to args() if prerunArgs is not defined in the block model.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived prerunArgs, or undefined if derivation fails\n */\n public derivePrerunArgsFromStorage(blockConfig: BlockConfig, storageJson: string): unknown {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"derivePrerunArgsFromStorage is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.PrerunArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n return result.value;\n } catch {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n }\n\n private calculateEnrichmentTargets(req: EnrichmentTargetsRequest): PlRef[] | undefined {\n const blockConfig = req.blockConfig();\n if (blockConfig.enrichmentTargets === undefined) return undefined;\n const args = req.args();\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.enrichmentTargets,\n extractCodeWithInfo(blockConfig),\n args,\n ) as PlRef[];\n return result;\n }\n\n public getEnrichmentTargets(\n blockConfig: () => BlockConfig,\n args: () => unknown,\n key?: { argsRid: ResourceId; blockPackRid: ResourceId },\n ): PlRef[] | undefined {\n const req = { blockConfig, args };\n if (key === undefined) return this.calculateEnrichmentTargets(req);\n const cacheKey = `${key.argsRid}:${key.blockPackRid}`;\n return this.enrichmentTargetsCache.memo(cacheKey, { context: req }).value;\n }\n\n // =============================================================================\n // VM-based Storage Operations\n // =============================================================================\n\n /**\n * Creates initial BlockStorage for a new block using VM-based transformation.\n * This calls the '__pl_storage_initial' callback registered by DataModel which:\n * - Gets initial data from DataModel.getDefaultData()\n * - Creates BlockStorage with correct version\n *\n * @param blockConfig The block configuration (provides the model code)\n * @returns Initial storage as JSON string\n * @throws Error if storage creation fails\n */\n public getInitialStorageInVM(blockConfig: BlockConfig): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getInitialStorageInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitial],\n extractCodeWithInfo(blockConfig),\n ) as string;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.getInitialStorageInVM] Initial storage creation failed:\", e);\n throw new Error(`Block initial storage creation failed: ${e}`);\n }\n }\n\n /**\n * Applies a state update using VM-based transformation.\n * This calls the model's `__pl_storage_applyUpdate` callback which:\n * - Normalizes current storage\n * - Updates state while preserving other fields (version, plugins)\n * - Returns the updated storage as JSON string\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param currentStorageJson Current storage as JSON string (must be defined)\n * @param newState New state from developer\n * @returns Updated storage as JSON string\n * @throws Error if storage update fails\n */\n public applyStorageUpdateInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string,\n payload: { operation: string; value: unknown },\n ): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"applyStorageUpdateInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageApplyUpdate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n payload,\n ) as string;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.applyStorageUpdateInVM] Storage update failed:\", e);\n throw new Error(`Block storage update failed: ${e}`);\n }\n }\n\n /**\n * Gets storage debug view from raw storage data by calling the VM's __pl_storage_debugView callback.\n * Returns structured debug info about the storage (e.g., dataVersion).\n *\n * @param blockConfig Block configuration\n * @param rawStorageJson Raw storage as JSON string (or undefined)\n * @returns Storage debug view as JSON string (e.g., '{\"dataVersion\": \"v1\"}')\n */\n public getStorageDebugViewInVM(\n blockConfig: BlockConfig,\n rawStorageJson: string | undefined,\n ): StringifiedJson<StorageDebugView> | undefined {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getStorageDebugViewInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageDebugView],\n extractCodeWithInfo(blockConfig),\n rawStorageJson,\n ) as StringifiedJson<StorageDebugView>;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed:\", e);\n return undefined;\n }\n }\n\n // =============================================================================\n // Block State Migrations\n // =============================================================================\n\n /**\n * Runs block state migrations via VM-based transformation.\n * This calls the model's `__pl_storage_migrate` callback which:\n * - Normalizes current storage to get state and version\n * - Applies DataModel upgrade to reach target version key\n * - Runs all necessary migrations sequentially\n * - Returns new storage with updated state and version\n *\n * The middle layer doesn't need to know about dataVersion or storage internals.\n * All migration logic is encapsulated in the model.\n *\n * @param blockConfig The NEW block configuration (provides the model code with migrations)\n * @param currentStorageJson Current storage as JSON string (or undefined)\n * @returns MigrationResult with new storage or skip/error info\n */\n public migrateStorageInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string | undefined,\n ): MigrationResult {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return { error: \"migrateStorageInVM is only supported for model API version 2\" };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageMigrate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n ) as MigrationResult;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.migrateStorageInVM] Migration failed:\", e);\n return { error: `VM execution failed: ${e}` };\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"project_helper.cjs","names":["LRUCache","ConsoleLoggerAdapter","BLOCK_STORAGE_FACADE_VERSION","executeSingleLambda","BlockStorageFacadeCallbacks"],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type {\n ResultOrError,\n BlockConfig,\n BlockStorage,\n PlRef,\n StorageDebugView,\n} from \"@platforma-sdk/model\";\nimport type { StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport {\n extractCodeWithInfo,\n ensureError,\n BlockStorageFacadeCallbacks,\n BLOCK_STORAGE_FACADE_VERSION,\n} from \"@platforma-sdk/model\";\nimport { LRUCache } from \"lru-cache\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { executeSingleLambda } from \"../js_render\";\nimport type { ResourceId } from \"@milaboratories/pl-client\";\nimport { ConsoleLoggerAdapter, type MiLogger } from \"@milaboratories/ts-helpers\";\n\ntype EnrichmentTargetsRequest = {\n blockConfig: () => BlockConfig;\n args: () => unknown;\n};\n\ntype EnrichmentTargetsValue = {\n value: PlRef[] | undefined;\n};\n\n/**\n * Result of VM-based storage migration.\n * Returned by migrateStorageInVM().\n *\n * - Error result: { error: string } - serious failure (no context, etc.)\n * - Success result: { newStorageJson: StringifiedJson<BlockStorage>, info: string } - migration succeeded\n */\nexport type MigrationResult =\n | { error: string }\n | { error?: undefined; newStorageJson: StringifiedJson<BlockStorage>; info: string };\n\n/**\n * Result of args derivation from storage.\n * Returned by __pl_args_derive and __pl_prerunArgs_derive VM callbacks.\n */\ntype ArgsDeriveResult = { error: string } | { error?: undefined; value: unknown };\n\nexport class ProjectHelper {\n private readonly enrichmentTargetsCache = new LRUCache<\n string,\n EnrichmentTargetsValue,\n EnrichmentTargetsRequest\n >({\n max: 256,\n memoMethod: (_key, _value, { context }) => {\n return { value: this.calculateEnrichmentTargets(context) };\n },\n });\n\n constructor(\n private readonly quickJs: QuickJSWASMModule,\n public readonly logger: MiLogger = new ConsoleLoggerAdapter(),\n ) {}\n\n // =============================================================================\n // Args Derivation from Storage (V3+)\n // =============================================================================\n\n /**\n * Derives args directly from storage JSON using VM callback.\n * The VM extracts data from storage and calls the block's args() function.\n *\n * This allows the middle layer to work only with storage JSON,\n * without needing to know the underlying data structure.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived args object, or error if derivation fails\n */\n public deriveArgsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"deriveArgsFromStorage is only supported for model API version 2\"),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.ArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n return { error: new Error(result.error) };\n }\n return { value: result.value };\n } catch (e) {\n return { error: new Error(\"Args derivation from storage failed\", { cause: ensureError(e) }) };\n }\n }\n\n /**\n * Derives prerunArgs directly from storage JSON using VM callback.\n * Falls back to args() if prerunArgs is not defined in the block model.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived prerunArgs, or undefined if derivation fails\n */\n public derivePrerunArgsFromStorage(blockConfig: BlockConfig, storageJson: string): unknown {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"derivePrerunArgsFromStorage is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.PrerunArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n return result.value;\n } catch {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n }\n\n private calculateEnrichmentTargets(req: EnrichmentTargetsRequest): PlRef[] | undefined {\n const blockConfig = req.blockConfig();\n if (blockConfig.enrichmentTargets === undefined) return undefined;\n const args = req.args();\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.enrichmentTargets,\n extractCodeWithInfo(blockConfig),\n args,\n ) as PlRef[];\n return result;\n }\n\n public getEnrichmentTargets(\n blockConfig: () => BlockConfig,\n args: () => unknown,\n key?: { argsRid: ResourceId; blockPackRid: ResourceId },\n ): PlRef[] | undefined {\n const req = { blockConfig, args };\n if (key === undefined) return this.calculateEnrichmentTargets(req);\n const cacheKey = `${key.argsRid}:${key.blockPackRid}`;\n return this.enrichmentTargetsCache.memo(cacheKey, { context: req }).value;\n }\n\n // =============================================================================\n // VM-based Storage Operations\n // =============================================================================\n\n /**\n * Creates initial BlockStorage for a new block using VM-based transformation.\n * This calls the '__pl_storage_initial' callback registered by DataModel which:\n * - Gets initial data from DataModel.getDefaultData()\n * - Creates BlockStorage with correct version\n *\n * @param blockConfig The block configuration (provides the model code)\n * @returns Initial storage as JSON string\n * @throws Error if storage creation fails\n */\n public getInitialStorageInVM(blockConfig: BlockConfig): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getInitialStorageInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitial],\n extractCodeWithInfo(blockConfig),\n ) as string;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.getInitialStorageInVM] Initial storage creation failed\", {\n cause: e,\n }),\n );\n throw new Error(`Block initial storage creation failed: ${e}`);\n }\n }\n\n /**\n * Applies a state update using VM-based transformation.\n * This calls the model's `__pl_storage_applyUpdate` callback which:\n * - Normalizes current storage\n * - Updates state while preserving other fields (version, plugins)\n * - Returns the updated storage as JSON string\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param currentStorageJson Current storage as JSON string (must be defined)\n * @param newState New state from developer\n * @returns Updated storage as JSON string\n * @throws Error if storage update fails\n */\n public applyStorageUpdateInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string,\n payload: { operation: string; value: unknown },\n ): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"applyStorageUpdateInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageApplyUpdate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n payload,\n ) as string;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.applyStorageUpdateInVM] Storage update failed\", { cause: e }),\n );\n throw new Error(`Block storage update failed: ${e}`);\n }\n }\n\n /**\n * Gets storage debug view from raw storage data by calling the VM's __pl_storage_debugView callback.\n * Returns structured debug info about the storage (e.g., dataVersion).\n *\n * @param blockConfig Block configuration\n * @param rawStorageJson Raw storage as JSON string (or undefined)\n * @returns Storage debug view as JSON string (e.g., '{\"dataVersion\": \"v1\"}')\n */\n public getStorageDebugViewInVM(\n blockConfig: BlockConfig,\n rawStorageJson: string | undefined,\n ): StringifiedJson<StorageDebugView> | undefined {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getStorageDebugViewInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageDebugView],\n extractCodeWithInfo(blockConfig),\n rawStorageJson,\n ) as StringifiedJson<StorageDebugView>;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed\", {\n cause: e,\n }),\n );\n return undefined;\n }\n }\n\n // =============================================================================\n // Block State Migrations\n // =============================================================================\n\n /**\n * Runs block state migrations via VM-based transformation.\n * This calls the model's `__pl_storage_migrate` callback which:\n * - Normalizes current storage to get state and version\n * - Applies DataModel upgrade to reach target version key\n * - Runs all necessary migrations sequentially\n * - Returns new storage with updated state and version\n *\n * The middle layer doesn't need to know about dataVersion or storage internals.\n * All migration logic is encapsulated in the model.\n *\n * @param blockConfig The NEW block configuration (provides the model code with migrations)\n * @param currentStorageJson Current storage as JSON string (or undefined)\n * @returns MigrationResult with new storage or skip/error info\n */\n public migrateStorageInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string | undefined,\n ): MigrationResult {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return { error: \"migrateStorageInVM is only supported for model API version 2\" };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageMigrate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n ) as MigrationResult;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.migrateStorageInVM] Migration failed\", { cause: e }),\n );\n return { error: `VM execution failed: ${e}` };\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { BlockConfig, BlockStorage, PlRef, ResultOrError, StorageDebugView } from "@platforma-sdk/model";
2
+ import { MiLogger } from "@milaboratories/ts-helpers";
2
3
  import { ResourceId } from "@milaboratories/pl-client";
3
4
  import { QuickJSWASMModule } from "quickjs-emscripten";
4
5
  import { StringifiedJson } from "@milaboratories/pl-model-common";
@@ -20,8 +21,9 @@ type MigrationResult = {
20
21
  };
21
22
  declare class ProjectHelper {
22
23
  private readonly quickJs;
24
+ readonly logger: MiLogger;
23
25
  private readonly enrichmentTargetsCache;
24
- constructor(quickJs: QuickJSWASMModule);
26
+ constructor(quickJs: QuickJSWASMModule, logger?: MiLogger);
25
27
  /**
26
28
  * Derives args directly from storage JSON using VM callback.
27
29
  * The VM extracts data from storage and calls the block's args() function.
@@ -1,5 +1,6 @@
1
1
  import { executeSingleLambda } from "../js_render/index.js";
2
2
  import { BLOCK_STORAGE_FACADE_VERSION, BlockStorageFacadeCallbacks, ensureError, extractCodeWithInfo } from "@platforma-sdk/model";
3
+ import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
3
4
  import { LRUCache } from "lru-cache";
4
5
 
5
6
  //#region src/model/project_helper.ts
@@ -10,8 +11,9 @@ var ProjectHelper = class {
10
11
  return { value: this.calculateEnrichmentTargets(context) };
11
12
  }
12
13
  });
13
- constructor(quickJs) {
14
+ constructor(quickJs, logger = new ConsoleLoggerAdapter()) {
14
15
  this.quickJs = quickJs;
16
+ this.logger = logger;
15
17
  }
16
18
  /**
17
19
  * Derives args directly from storage JSON using VM callback.
@@ -82,7 +84,7 @@ var ProjectHelper = class {
82
84
  try {
83
85
  return executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitial], extractCodeWithInfo(blockConfig));
84
86
  } catch (e) {
85
- console.error("[ProjectHelper.getInitialStorageInVM] Initial storage creation failed:", e);
87
+ this.logger.error(new Error("[ProjectHelper.getInitialStorageInVM] Initial storage creation failed", { cause: e }));
86
88
  throw new Error(`Block initial storage creation failed: ${e}`);
87
89
  }
88
90
  }
@@ -104,7 +106,7 @@ var ProjectHelper = class {
104
106
  try {
105
107
  return executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageApplyUpdate], extractCodeWithInfo(blockConfig), currentStorageJson, payload);
106
108
  } catch (e) {
107
- console.error("[ProjectHelper.applyStorageUpdateInVM] Storage update failed:", e);
109
+ this.logger.error(new Error("[ProjectHelper.applyStorageUpdateInVM] Storage update failed", { cause: e }));
108
110
  throw new Error(`Block storage update failed: ${e}`);
109
111
  }
110
112
  }
@@ -121,7 +123,7 @@ var ProjectHelper = class {
121
123
  try {
122
124
  return executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageDebugView], extractCodeWithInfo(blockConfig), rawStorageJson);
123
125
  } catch (e) {
124
- console.error("[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed:", e);
126
+ this.logger.error(new Error("[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed", { cause: e }));
125
127
  return;
126
128
  }
127
129
  }
@@ -145,7 +147,7 @@ var ProjectHelper = class {
145
147
  try {
146
148
  return executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageMigrate], extractCodeWithInfo(blockConfig), currentStorageJson);
147
149
  } catch (e) {
148
- console.error("[ProjectHelper.migrateStorageInVM] Migration failed:", e);
150
+ this.logger.error(new Error("[ProjectHelper.migrateStorageInVM] Migration failed", { cause: e }));
149
151
  return { error: `VM execution failed: ${e}` };
150
152
  }
151
153
  }
@@ -1 +1 @@
1
- {"version":3,"file":"project_helper.js","names":[],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type {\n ResultOrError,\n BlockConfig,\n BlockStorage,\n PlRef,\n StorageDebugView,\n} from \"@platforma-sdk/model\";\nimport type { StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport {\n extractCodeWithInfo,\n ensureError,\n BlockStorageFacadeCallbacks,\n BLOCK_STORAGE_FACADE_VERSION,\n} from \"@platforma-sdk/model\";\nimport { LRUCache } from \"lru-cache\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { executeSingleLambda } from \"../js_render\";\nimport type { ResourceId } from \"@milaboratories/pl-client\";\n\ntype EnrichmentTargetsRequest = {\n blockConfig: () => BlockConfig;\n args: () => unknown;\n};\n\ntype EnrichmentTargetsValue = {\n value: PlRef[] | undefined;\n};\n\n/**\n * Result of VM-based storage migration.\n * Returned by migrateStorageInVM().\n *\n * - Error result: { error: string } - serious failure (no context, etc.)\n * - Success result: { newStorageJson: StringifiedJson<BlockStorage>, info: string } - migration succeeded\n */\nexport type MigrationResult =\n | { error: string }\n | { error?: undefined; newStorageJson: StringifiedJson<BlockStorage>; info: string };\n\n/**\n * Result of args derivation from storage.\n * Returned by __pl_args_derive and __pl_prerunArgs_derive VM callbacks.\n */\ntype ArgsDeriveResult = { error: string } | { error?: undefined; value: unknown };\n\nexport class ProjectHelper {\n private readonly enrichmentTargetsCache = new LRUCache<\n string,\n EnrichmentTargetsValue,\n EnrichmentTargetsRequest\n >({\n max: 256,\n memoMethod: (_key, _value, { context }) => {\n return { value: this.calculateEnrichmentTargets(context) };\n },\n });\n\n constructor(private readonly quickJs: QuickJSWASMModule) {}\n\n // =============================================================================\n // Args Derivation from Storage (V3+)\n // =============================================================================\n\n /**\n * Derives args directly from storage JSON using VM callback.\n * The VM extracts data from storage and calls the block's args() function.\n *\n * This allows the middle layer to work only with storage JSON,\n * without needing to know the underlying data structure.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived args object, or error if derivation fails\n */\n public deriveArgsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"deriveArgsFromStorage is only supported for model API version 2\"),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.ArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n return { error: new Error(result.error) };\n }\n return { value: result.value };\n } catch (e) {\n return { error: new Error(\"Args derivation from storage failed\", { cause: ensureError(e) }) };\n }\n }\n\n /**\n * Derives prerunArgs directly from storage JSON using VM callback.\n * Falls back to args() if prerunArgs is not defined in the block model.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived prerunArgs, or undefined if derivation fails\n */\n public derivePrerunArgsFromStorage(blockConfig: BlockConfig, storageJson: string): unknown {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"derivePrerunArgsFromStorage is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.PrerunArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n return result.value;\n } catch {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n }\n\n private calculateEnrichmentTargets(req: EnrichmentTargetsRequest): PlRef[] | undefined {\n const blockConfig = req.blockConfig();\n if (blockConfig.enrichmentTargets === undefined) return undefined;\n const args = req.args();\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.enrichmentTargets,\n extractCodeWithInfo(blockConfig),\n args,\n ) as PlRef[];\n return result;\n }\n\n public getEnrichmentTargets(\n blockConfig: () => BlockConfig,\n args: () => unknown,\n key?: { argsRid: ResourceId; blockPackRid: ResourceId },\n ): PlRef[] | undefined {\n const req = { blockConfig, args };\n if (key === undefined) return this.calculateEnrichmentTargets(req);\n const cacheKey = `${key.argsRid}:${key.blockPackRid}`;\n return this.enrichmentTargetsCache.memo(cacheKey, { context: req }).value;\n }\n\n // =============================================================================\n // VM-based Storage Operations\n // =============================================================================\n\n /**\n * Creates initial BlockStorage for a new block using VM-based transformation.\n * This calls the '__pl_storage_initial' callback registered by DataModel which:\n * - Gets initial data from DataModel.getDefaultData()\n * - Creates BlockStorage with correct version\n *\n * @param blockConfig The block configuration (provides the model code)\n * @returns Initial storage as JSON string\n * @throws Error if storage creation fails\n */\n public getInitialStorageInVM(blockConfig: BlockConfig): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getInitialStorageInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitial],\n extractCodeWithInfo(blockConfig),\n ) as string;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.getInitialStorageInVM] Initial storage creation failed:\", e);\n throw new Error(`Block initial storage creation failed: ${e}`);\n }\n }\n\n /**\n * Applies a state update using VM-based transformation.\n * This calls the model's `__pl_storage_applyUpdate` callback which:\n * - Normalizes current storage\n * - Updates state while preserving other fields (version, plugins)\n * - Returns the updated storage as JSON string\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param currentStorageJson Current storage as JSON string (must be defined)\n * @param newState New state from developer\n * @returns Updated storage as JSON string\n * @throws Error if storage update fails\n */\n public applyStorageUpdateInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string,\n payload: { operation: string; value: unknown },\n ): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"applyStorageUpdateInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageApplyUpdate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n payload,\n ) as string;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.applyStorageUpdateInVM] Storage update failed:\", e);\n throw new Error(`Block storage update failed: ${e}`);\n }\n }\n\n /**\n * Gets storage debug view from raw storage data by calling the VM's __pl_storage_debugView callback.\n * Returns structured debug info about the storage (e.g., dataVersion).\n *\n * @param blockConfig Block configuration\n * @param rawStorageJson Raw storage as JSON string (or undefined)\n * @returns Storage debug view as JSON string (e.g., '{\"dataVersion\": \"v1\"}')\n */\n public getStorageDebugViewInVM(\n blockConfig: BlockConfig,\n rawStorageJson: string | undefined,\n ): StringifiedJson<StorageDebugView> | undefined {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getStorageDebugViewInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageDebugView],\n extractCodeWithInfo(blockConfig),\n rawStorageJson,\n ) as StringifiedJson<StorageDebugView>;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed:\", e);\n return undefined;\n }\n }\n\n // =============================================================================\n // Block State Migrations\n // =============================================================================\n\n /**\n * Runs block state migrations via VM-based transformation.\n * This calls the model's `__pl_storage_migrate` callback which:\n * - Normalizes current storage to get state and version\n * - Applies DataModel upgrade to reach target version key\n * - Runs all necessary migrations sequentially\n * - Returns new storage with updated state and version\n *\n * The middle layer doesn't need to know about dataVersion or storage internals.\n * All migration logic is encapsulated in the model.\n *\n * @param blockConfig The NEW block configuration (provides the model code with migrations)\n * @param currentStorageJson Current storage as JSON string (or undefined)\n * @returns MigrationResult with new storage or skip/error info\n */\n public migrateStorageInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string | undefined,\n ): MigrationResult {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return { error: \"migrateStorageInVM is only supported for model API version 2\" };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageMigrate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n ) as MigrationResult;\n return result;\n } catch (e) {\n console.error(\"[ProjectHelper.migrateStorageInVM] Migration failed:\", e);\n return { error: `VM execution failed: ${e}` };\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"project_helper.js","names":[],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type {\n ResultOrError,\n BlockConfig,\n BlockStorage,\n PlRef,\n StorageDebugView,\n} from \"@platforma-sdk/model\";\nimport type { StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport {\n extractCodeWithInfo,\n ensureError,\n BlockStorageFacadeCallbacks,\n BLOCK_STORAGE_FACADE_VERSION,\n} from \"@platforma-sdk/model\";\nimport { LRUCache } from \"lru-cache\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { executeSingleLambda } from \"../js_render\";\nimport type { ResourceId } from \"@milaboratories/pl-client\";\nimport { ConsoleLoggerAdapter, type MiLogger } from \"@milaboratories/ts-helpers\";\n\ntype EnrichmentTargetsRequest = {\n blockConfig: () => BlockConfig;\n args: () => unknown;\n};\n\ntype EnrichmentTargetsValue = {\n value: PlRef[] | undefined;\n};\n\n/**\n * Result of VM-based storage migration.\n * Returned by migrateStorageInVM().\n *\n * - Error result: { error: string } - serious failure (no context, etc.)\n * - Success result: { newStorageJson: StringifiedJson<BlockStorage>, info: string } - migration succeeded\n */\nexport type MigrationResult =\n | { error: string }\n | { error?: undefined; newStorageJson: StringifiedJson<BlockStorage>; info: string };\n\n/**\n * Result of args derivation from storage.\n * Returned by __pl_args_derive and __pl_prerunArgs_derive VM callbacks.\n */\ntype ArgsDeriveResult = { error: string } | { error?: undefined; value: unknown };\n\nexport class ProjectHelper {\n private readonly enrichmentTargetsCache = new LRUCache<\n string,\n EnrichmentTargetsValue,\n EnrichmentTargetsRequest\n >({\n max: 256,\n memoMethod: (_key, _value, { context }) => {\n return { value: this.calculateEnrichmentTargets(context) };\n },\n });\n\n constructor(\n private readonly quickJs: QuickJSWASMModule,\n public readonly logger: MiLogger = new ConsoleLoggerAdapter(),\n ) {}\n\n // =============================================================================\n // Args Derivation from Storage (V3+)\n // =============================================================================\n\n /**\n * Derives args directly from storage JSON using VM callback.\n * The VM extracts data from storage and calls the block's args() function.\n *\n * This allows the middle layer to work only with storage JSON,\n * without needing to know the underlying data structure.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived args object, or error if derivation fails\n */\n public deriveArgsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"deriveArgsFromStorage is only supported for model API version 2\"),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.ArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n return { error: new Error(result.error) };\n }\n return { value: result.value };\n } catch (e) {\n return { error: new Error(\"Args derivation from storage failed\", { cause: ensureError(e) }) };\n }\n }\n\n /**\n * Derives prerunArgs directly from storage JSON using VM callback.\n * Falls back to args() if prerunArgs is not defined in the block model.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived prerunArgs, or undefined if derivation fails\n */\n public derivePrerunArgsFromStorage(blockConfig: BlockConfig, storageJson: string): unknown {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"derivePrerunArgsFromStorage is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.PrerunArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as ArgsDeriveResult;\n\n if (result.error !== undefined) {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n return result.value;\n } catch {\n // Return undefined if derivation fails (skip block in staging)\n return undefined;\n }\n }\n\n private calculateEnrichmentTargets(req: EnrichmentTargetsRequest): PlRef[] | undefined {\n const blockConfig = req.blockConfig();\n if (blockConfig.enrichmentTargets === undefined) return undefined;\n const args = req.args();\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.enrichmentTargets,\n extractCodeWithInfo(blockConfig),\n args,\n ) as PlRef[];\n return result;\n }\n\n public getEnrichmentTargets(\n blockConfig: () => BlockConfig,\n args: () => unknown,\n key?: { argsRid: ResourceId; blockPackRid: ResourceId },\n ): PlRef[] | undefined {\n const req = { blockConfig, args };\n if (key === undefined) return this.calculateEnrichmentTargets(req);\n const cacheKey = `${key.argsRid}:${key.blockPackRid}`;\n return this.enrichmentTargetsCache.memo(cacheKey, { context: req }).value;\n }\n\n // =============================================================================\n // VM-based Storage Operations\n // =============================================================================\n\n /**\n * Creates initial BlockStorage for a new block using VM-based transformation.\n * This calls the '__pl_storage_initial' callback registered by DataModel which:\n * - Gets initial data from DataModel.getDefaultData()\n * - Creates BlockStorage with correct version\n *\n * @param blockConfig The block configuration (provides the model code)\n * @returns Initial storage as JSON string\n * @throws Error if storage creation fails\n */\n public getInitialStorageInVM(blockConfig: BlockConfig): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getInitialStorageInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitial],\n extractCodeWithInfo(blockConfig),\n ) as string;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.getInitialStorageInVM] Initial storage creation failed\", {\n cause: e,\n }),\n );\n throw new Error(`Block initial storage creation failed: ${e}`);\n }\n }\n\n /**\n * Applies a state update using VM-based transformation.\n * This calls the model's `__pl_storage_applyUpdate` callback which:\n * - Normalizes current storage\n * - Updates state while preserving other fields (version, plugins)\n * - Returns the updated storage as JSON string\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param currentStorageJson Current storage as JSON string (must be defined)\n * @param newState New state from developer\n * @returns Updated storage as JSON string\n * @throws Error if storage update fails\n */\n public applyStorageUpdateInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string,\n payload: { operation: string; value: unknown },\n ): string {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"applyStorageUpdateInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageApplyUpdate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n payload,\n ) as string;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.applyStorageUpdateInVM] Storage update failed\", { cause: e }),\n );\n throw new Error(`Block storage update failed: ${e}`);\n }\n }\n\n /**\n * Gets storage debug view from raw storage data by calling the VM's __pl_storage_debugView callback.\n * Returns structured debug info about the storage (e.g., dataVersion).\n *\n * @param blockConfig Block configuration\n * @param rawStorageJson Raw storage as JSON string (or undefined)\n * @returns Storage debug view as JSON string (e.g., '{\"dataVersion\": \"v1\"}')\n */\n public getStorageDebugViewInVM(\n blockConfig: BlockConfig,\n rawStorageJson: string | undefined,\n ): StringifiedJson<StorageDebugView> | undefined {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n throw new Error(\"getStorageDebugViewInVM is only supported for model API version 2\");\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageDebugView],\n extractCodeWithInfo(blockConfig),\n rawStorageJson,\n ) as StringifiedJson<StorageDebugView>;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed\", {\n cause: e,\n }),\n );\n return undefined;\n }\n }\n\n // =============================================================================\n // Block State Migrations\n // =============================================================================\n\n /**\n * Runs block state migrations via VM-based transformation.\n * This calls the model's `__pl_storage_migrate` callback which:\n * - Normalizes current storage to get state and version\n * - Applies DataModel upgrade to reach target version key\n * - Runs all necessary migrations sequentially\n * - Returns new storage with updated state and version\n *\n * The middle layer doesn't need to know about dataVersion or storage internals.\n * All migration logic is encapsulated in the model.\n *\n * @param blockConfig The NEW block configuration (provides the model code with migrations)\n * @param currentStorageJson Current storage as JSON string (or undefined)\n * @returns MigrationResult with new storage or skip/error info\n */\n public migrateStorageInVM(\n blockConfig: BlockConfig,\n currentStorageJson: string | undefined,\n ): MigrationResult {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return { error: \"migrateStorageInVM is only supported for model API version 2\" };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageMigrate],\n extractCodeWithInfo(blockConfig),\n currentStorageJson,\n ) as MigrationResult;\n return result;\n } catch (e) {\n this.logger.error(\n new Error(\"[ProjectHelper.migrateStorageInVM] Migration failed\", { cause: e }),\n );\n return { error: `VM execution failed: ${e}` };\n }\n }\n}\n"],"mappings":"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"}
@@ -34,11 +34,12 @@ function cached(modIdCb, valueCb) {
34
34
  };
35
35
  }
36
36
  var BlockInfo = class {
37
- constructor(id, fields, config, source) {
37
+ constructor(id, fields, config, source, logger = new _milaboratories_ts_helpers.ConsoleLoggerAdapter()) {
38
38
  this.id = id;
39
39
  this.fields = fields;
40
40
  this.config = config;
41
41
  this.source = source;
42
+ this.logger = logger;
42
43
  }
43
44
  check() {
44
45
  if (this.fields.prodOutput === void 0 !== (this.fields.prodCtx === void 0)) throw new Error("inconsistent prod fields");
@@ -80,7 +81,7 @@ var BlockInfo = class {
80
81
  try {
81
82
  return this.blockStorageC();
82
83
  } catch (e) {
83
- console.error("Error getting blockStorage:", e);
84
+ this.logger.error(new Error(`Error getting blockStorage for ${this.id}`, { cause: e }));
84
85
  return;
85
86
  }
86
87
  }
@@ -303,8 +304,10 @@ var ProjectMutator = class ProjectMutator {
303
304
  * @returns Merged state in unified format { args, uiState }
304
305
  */
305
306
  mergeBlockState(blockId, partialUpdate) {
307
+ const currentState = this.getBlockInfo(blockId).blockStorage;
308
+ if (currentState === void 0) throw new Error(`Cannot merge block state for ${blockId}: blockStorage is unavailable`);
306
309
  return {
307
- ...this.getBlockInfo(blockId).blockStorage,
310
+ ...currentState,
308
311
  ...partialUpdate
309
312
  };
310
313
  }
@@ -329,7 +332,8 @@ var ProjectMutator = class ProjectMutator {
329
332
  * @param blockId The block to reset
330
333
  */
331
334
  resetToInitialStorage(blockId) {
332
- const blockConfig = this.getBlockInfo(blockId).config;
335
+ const info = this.getBlockInfo(blockId);
336
+ const blockConfig = info.config;
333
337
  if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) throw new Error("resetToInitialStorage is only supported for model API version 2");
334
338
  const initialStorageJson = this.projectHelper.getInitialStorageInVM(blockConfig);
335
339
  this.setBlockStorageRaw(blockId, initialStorageJson);
@@ -338,7 +342,12 @@ var ProjectMutator = class ProjectMutator {
338
342
  this.setBlockFieldObj(blockId, "currentArgs", this.createJsonFieldValue(deriveArgsResult.value));
339
343
  const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(blockConfig, initialStorageJson);
340
344
  if (prerunArgs !== void 0) this.setBlockFieldObj(blockId, "currentPrerunArgs", this.createJsonFieldValue(prerunArgs));
341
- } else this.deleteBlockFields(blockId, "currentArgs");
345
+ else this.deleteBlockFields(blockId, "currentPrerunArgs");
346
+ } else {
347
+ if (info.fields.currentPrerunArgs !== void 0) this.projectHelper.logger.warn(`[staging] ${blockId}: currentPrerunArgs cleared (args derivation failed)`);
348
+ this.deleteBlockFields(blockId, "currentArgs");
349
+ this.deleteBlockFields(blockId, "currentPrerunArgs");
350
+ }
342
351
  }
343
352
  /** Optimally sets inputs for multiple blocks in one go */
344
353
  setStates(requests) {
@@ -382,7 +391,7 @@ var ProjectMutator = class ProjectMutator {
382
391
  if (oldPrerunArgsData === void 0 || Buffer.compare(oldPrerunArgsData, prerunArgsData) !== 0) prerunArgsChanged = true;
383
392
  const prerunArgsRef = this.tx.createValue(_milaboratories_pl_client.Pl.JsonObject, prerunArgsData);
384
393
  this.setBlockField(req.blockId, "currentPrerunArgs", prerunArgsRef, "Ready", prerunArgsData);
385
- } else if (info.fields.currentPrerunArgs !== void 0) prerunArgsChanged = true;
394
+ } else this.deleteBlockFields(req.blockId, "currentPrerunArgs");
386
395
  blockChanged = true;
387
396
  if (prerunArgsChanged) changedArgs.push(req.blockId);
388
397
  if (blockChanged) {
@@ -461,7 +470,7 @@ var ProjectMutator = class ProjectMutator {
461
470
  if (this.blocksInLimbo.delete(blockId)) this.renderingStateChanged = true;
462
471
  }
463
472
  initializeNewBlock(blockId, spec) {
464
- const info = new BlockInfo(blockId, {}, (0, _platforma_sdk_model.extractConfig)(spec.blockPack.config), spec.blockPack.source);
473
+ const info = new BlockInfo(blockId, {}, (0, _platforma_sdk_model.extractConfig)(spec.blockPack.config), spec.blockPack.source, this.projectHelper.logger);
465
474
  this.blockInfos.set(blockId, info);
466
475
  const bp = require_block_pack.createBlockPack(this.tx, spec.blockPack);
467
476
  this.setBlockField(blockId, "blockPack", _milaboratories_pl_client.Pl.wrapInHolder(this.tx, bp), "NotReady");
@@ -512,7 +521,7 @@ var ProjectMutator = class ProjectMutator {
512
521
  ]));
513
522
  }
514
523
  initializeBlockDuplicate(blockId, originalBlockInfo) {
515
- const info = new BlockInfo(blockId, {}, originalBlockInfo.config, originalBlockInfo.source);
524
+ const info = new BlockInfo(blockId, {}, originalBlockInfo.config, originalBlockInfo.source, this.projectHelper.logger);
516
525
  this.blockInfos.set(blockId, info);
517
526
  const fieldNamesToDuplicate = this.getFieldNamesToDuplicate(blockId);
518
527
  for (const [fieldName, fieldState] of Object.entries(originalBlockInfo.fields)) if (fieldNamesToDuplicate.has(fieldName) && fieldState && fieldState.ref) this.setBlockFieldObj(blockId, fieldName, {
@@ -628,6 +637,10 @@ var ProjectMutator = class ProjectMutator {
628
637
  this.setBlockFieldObj(blockId, "currentArgs", this.createJsonFieldValue(deriveArgsResult.value));
629
638
  const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(newConfig, storageJson);
630
639
  if (prerunArgs !== void 0) this.setBlockFieldObj(blockId, "currentPrerunArgs", this.createJsonFieldValue(prerunArgs));
640
+ else this.deleteBlockFields(blockId, "currentPrerunArgs");
641
+ } else {
642
+ this.deleteBlockFields(blockId, "currentArgs");
643
+ this.deleteBlockFields(blockId, "currentPrerunArgs");
631
644
  }
632
645
  };
633
646
  if (newClearState !== void 0) if (newConfig.modelAPIVersion === _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) {
@@ -647,7 +660,7 @@ var ProjectMutator = class ProjectMutator {
647
660
  const currentStorageJson = info.blockStorageJson;
648
661
  const migrationResult = this.projectHelper.migrateStorageInVM(newConfig, currentStorageJson);
649
662
  if (migrationResult.error !== void 0) throw new Error(`[migrateBlockPack] Block ${blockId} migration failed: ${migrationResult.error}`);
650
- console.log(`[migrateBlockPack] Block ${blockId}: ${migrationResult.info}`);
663
+ this.projectHelper.logger.info(`[migrateBlockPack] Block ${blockId}: ${migrationResult.info}`);
651
664
  applyStorageAndDeriveArgs(migrationResult.newStorageJson);
652
665
  } else {
653
666
  persistBlockPack();
@@ -742,9 +755,11 @@ var ProjectMutator = class ProjectMutator {
742
755
  let rendered = 0;
743
756
  this.traverseWithStagingLag((blockId, lag) => {
744
757
  if (lag === 0) return;
745
- if (lagThreshold === void 0 || lag <= lagThreshold) {
758
+ if (lagThreshold === void 0 || lag <= lagThreshold) try {
746
759
  this.renderStagingFor(blockId);
747
760
  rendered++;
761
+ } catch (e) {
762
+ this.projectHelper.logger.error(new Error(`[refreshStagings] renderStagingFor failed for ${blockId}`, { cause: e }));
748
763
  }
749
764
  });
750
765
  if (rendered > 0) this.resetStagingRefreshTimestamp();
@@ -860,7 +875,7 @@ var ProjectMutator = class ProjectMutator {
860
875
  const renderingState = { stagingRefreshTimestamp };
861
876
  const blocksInLimboSet = new Set(blocksInLimbo);
862
877
  const blockInfos = /* @__PURE__ */ new Map();
863
- blockInfoStates.forEach(({ id, fields, blockConfig, blockPack }) => blockInfos.set(id, new BlockInfo(id, fields, (0, _milaboratories_ts_helpers.notEmpty)(blockConfig), (0, _milaboratories_ts_helpers.notEmpty)(blockPack))));
878
+ blockInfoStates.forEach(({ id, fields, blockConfig, blockPack }) => blockInfos.set(id, new BlockInfo(id, fields, (0, _milaboratories_ts_helpers.notEmpty)(blockConfig), (0, _milaboratories_ts_helpers.notEmpty)(blockPack), projectHelper.logger)));
864
879
  const blockInStruct = /* @__PURE__ */ new Set();
865
880
  for (const b of require_project_model_util.allBlocks(structure)) {
866
881
  if (!blockInfos.has(b.id)) throw new Error(`Inconsistent project structure: no inputs for ${b.id}`);