@milaboratories/pl-middle-layer 1.60.0 → 1.60.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -209,7 +209,12 @@ var ErrorRepository = class {
209
209
  return quickJSError;
210
210
  }
211
211
  const causeName = cause.name;
212
- const errorId = causeName.slice(causeName.indexOf("/uuid:") + 6);
212
+ const uuidIdx = causeName.indexOf("/uuid:");
213
+ if (uuidIdx === -1) {
214
+ const causeMsg = "message" in cause && typeof cause.message === "string" ? cause.message : "";
215
+ throw new Error(`QuickJS native error: ${causeName}: ${causeMsg}, ${(0, _milaboratories_pl_client.stringifyWithResourceId)(quickJSError)}`, { cause: quickJSError });
216
+ }
217
+ const errorId = causeName.slice(uuidIdx + 6);
213
218
  if (!errorId) throw new Error(`ErrorRepo: quickJSError.cause.name does not contain errorId: ${causeName}, ${(0, _milaboratories_pl_client.stringifyWithResourceId)(quickJSError)}`);
214
219
  const error = this.errorIdToError.get(errorId);
215
220
  if (error === void 0) throw new Error(`ErrorRepo: errorId not found: ${errorId}, ${(0, _milaboratories_pl_client.stringifyWithResourceId)(quickJSError)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"context.cjs","names":["ComputableContextHelper","Scope","JsRenderInternal","errors","PlQuickJSError"],"sources":["../../src/js_render/context.ts"],"sourcesContent":["import type { ComputableCtx } from \"@milaboratories/computable\";\nimport type { BlockCodeKnownFeatureFlags } from \"@platforma-sdk/model\";\nimport { JsRenderInternal } from \"@platforma-sdk/model\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport { randomUUID } from \"node:crypto\";\nimport type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { Scope, errors } from \"quickjs-emscripten\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport { stringifyWithResourceId } from \"@milaboratories/pl-client\";\nimport { PlQuickJSError } from \"@milaboratories/pl-errors\";\nimport { ComputableContextHelper } from \"./computable_context\";\n\nexport type DeadlineSettings = {\n currentExecutionTarget: string;\n deadline: number;\n};\n\n/**\n * Communicates a deadline to the quickjs runtime, that if passed, will interrupt the execution.\n * Undefined can be used to reset the deadline.\n * */\nexport type DeadlineSetter = (settings: DeadlineSettings | undefined) => void;\n\nfunction isArrayBufferOrView(obj: unknown): obj is ArrayBufferLike {\n return obj instanceof ArrayBuffer || ArrayBuffer.isView(obj);\n}\n\n/**\n * Contains references to objects needed to execute lambda within computable,\n * providing access to:\n * - block context\n * - computable context\n * - middle layer environment\n * */\nexport type ComputableEnv = {\n readonly blockCtx: BlockContextAny;\n readonly mlEnv: MiddleLayerEnvironment;\n computableCtx: ComputableCtx;\n};\n\n/** Execution stats accumulated during the lifetime of a JsExecutionContext. */\nexport type JsExecStats = {\n bundleEvalMs: number;\n bundleBytes: number;\n\n callbackMs: number;\n callbackCount: number;\n\n serInMs: number;\n serInBytes: number;\n serInCalls: number;\n\n serOutMs: number;\n serOutBytes: number;\n serOutCalls: number;\n\n ctxMethodCalls: number;\n ctxMethodMs: number;\n};\n\nexport class JsExecutionContext {\n private readonly callbackRegistry: QuickJSHandle;\n private readonly fnJSONStringify: QuickJSHandle;\n private readonly fnJSONParse: QuickJSHandle;\n\n public readonly errorRepo = new ErrorRepository();\n\n public readonly computableHelper: ComputableContextHelper | undefined;\n\n public readonly stats: JsExecStats = {\n bundleEvalMs: 0,\n bundleBytes: 0,\n callbackMs: 0,\n callbackCount: 0,\n serInMs: 0,\n serInBytes: 0,\n serInCalls: 0,\n serOutMs: 0,\n serOutBytes: 0,\n serOutCalls: 0,\n ctxMethodCalls: 0,\n ctxMethodMs: 0,\n };\n\n /**\n * Creates a new JS execution context.\n *\n * @param scope - QuickJS scope for memory management\n * @param vm - QuickJS VM context\n * @param deadlineSetter - Function to set execution deadline\n * @param featureFlags - Block feature flags\n * @param computableEnv - Optional reactive computable environment (for outputs, inputsValid, etc.)\n */\n constructor(\n public readonly scope: Scope,\n public readonly vm: QuickJSContext,\n private readonly deadlineSetter: DeadlineSetter,\n featureFlags: BlockCodeKnownFeatureFlags | undefined,\n computableEnv?: ComputableEnv,\n ) {\n this.callbackRegistry = this.scope.manage(this.vm.newObject());\n\n this.fnJSONStringify = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"stringify\")),\n );\n if (vm.typeof(this.fnJSONStringify) !== \"function\")\n throw new Error(`JSON.stringify() not found.`);\n\n this.fnJSONParse = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"parse\")),\n );\n if (vm.typeof(this.fnJSONParse) !== \"function\") throw new Error(`JSON.parse() not found.`);\n\n if (computableEnv !== undefined)\n this.computableHelper = new ComputableContextHelper(\n this,\n computableEnv.blockCtx,\n computableEnv.mlEnv,\n featureFlags,\n computableEnv.computableCtx,\n );\n\n this.injectCtx();\n }\n\n public resetComputableCtx() {\n notEmpty(\n this.computableHelper,\n \"Computable context helper is not initialized\",\n ).resetComputableCtx();\n }\n\n private static cleanErrorContext(error: unknown): void {\n if (typeof error === \"object\" && error !== null && \"context\" in error) delete error[\"context\"];\n }\n\n // private static cleanError(error: unknown): unknown {\n // if (error instanceof errors.QuickJSUnwrapError) {\n // const { cause, context: _, name, message, stack, ...rest } = error;\n // const clean = new errors.QuickJSUnwrapError(cause);\n // Object.assign(clean, { ...rest, name, message, stack });\n // return clean;\n // }\n // return error;\n // }\n\n public evaluateBundle(code: string) {\n const t0 = performance.now();\n try {\n this.deadlineSetter({\n currentExecutionTarget: \"evaluateBundle\",\n deadline: Date.now() + 10000,\n });\n this.vm.unwrapResult(this.vm.evalCode(code, \"bundle.js\", { type: \"global\" })).dispose();\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n throw err;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.bundleEvalMs += performance.now() - t0;\n this.stats.bundleBytes += code.length;\n }\n }\n\n public runCallback(cbName: string, ...args: unknown[]): QuickJSHandle {\n const t0 = performance.now();\n try {\n this.deadlineSetter({ currentExecutionTarget: cbName, deadline: Date.now() + 10000 });\n return Scope.withScope((localScope) => {\n const targetCallback = localScope.manage(this.vm.getProp(this.callbackRegistry, cbName));\n\n if (this.vm.typeof(targetCallback) !== \"function\")\n throw new Error(`No such callback: ${cbName}`);\n\n return this.scope.manage(\n this.vm.unwrapResult(\n this.vm.callFunction(\n targetCallback,\n this.vm.undefined,\n ...args.map((arg) => this.exportObjectUniversal(arg, localScope)),\n ),\n ),\n );\n });\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n const original = this.errorRepo.getOriginal(err);\n throw original;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.callbackMs += performance.now() - t0;\n this.stats.callbackCount++;\n }\n }\n\n //\n // QuickJS Helpers\n //\n\n public exportSingleValue(\n obj: boolean | number | string | null | ArrayBuffer | undefined,\n scope?: Scope,\n ): QuickJSHandle {\n const result = this.tryExportSingleValue(obj, scope);\n if (result === undefined) {\n throw new Error(\n `Can't export value: ${obj === undefined ? \"undefined\" : JSON.stringify(obj)}`,\n );\n }\n return result;\n }\n\n public tryExportSingleValue(obj: unknown, scope?: Scope): QuickJSHandle | undefined {\n let handle: QuickJSHandle;\n let manage = false;\n switch (typeof obj) {\n case \"string\":\n handle = this.vm.newString(obj);\n manage = true;\n break;\n case \"number\":\n handle = this.vm.newNumber(obj);\n manage = true;\n break;\n case \"undefined\":\n handle = this.vm.undefined;\n break;\n case \"boolean\":\n handle = obj ? this.vm.true : this.vm.false;\n break;\n default:\n if (obj === null) {\n handle = this.vm.null;\n break;\n }\n if (isArrayBufferOrView(obj)) {\n handle = this.vm.newArrayBuffer(obj);\n manage = true;\n break;\n }\n return undefined;\n }\n return manage && scope != undefined ? scope.manage(handle) : handle;\n }\n\n public exportObjectUniversal(obj: unknown, scope?: Scope): QuickJSHandle {\n const simpleHandle = this.tryExportSingleValue(obj, scope);\n if (simpleHandle !== undefined) return simpleHandle;\n return this.exportObjectViaJson(obj, scope);\n }\n\n public exportObjectViaJson(obj: unknown, scope?: Scope): QuickJSHandle {\n const t0 = performance.now();\n const json = JSON.stringify(obj);\n this.stats.serInBytes += json.length;\n this.stats.serInCalls++;\n const result = this.vm\n .newString(json)\n .consume((jsonHandle) =>\n this.vm.unwrapResult(this.vm.callFunction(this.fnJSONParse, this.vm.undefined, jsonHandle)),\n );\n this.stats.serInMs += performance.now() - t0;\n return scope !== undefined ? scope.manage(result) : result;\n }\n\n public importObjectUniversal(handle: QuickJSHandle | undefined): unknown {\n if (handle === undefined) return undefined;\n switch (this.vm.typeof(handle)) {\n case \"undefined\":\n return undefined;\n case \"boolean\":\n case \"number\":\n case \"string\":\n return this.vm.dump(handle);\n default:\n return this.importObjectViaJson(handle);\n }\n }\n\n public importObjectViaJson(handle: QuickJSHandle): unknown {\n const t0 = performance.now();\n const text = this.vm\n .unwrapResult(this.vm.callFunction(this.fnJSONStringify, this.vm.undefined, handle))\n .consume((strHandle) => this.vm.getString(strHandle));\n this.stats.serOutBytes += text.length;\n this.stats.serOutCalls++;\n if (text === \"undefined\") {\n // special case with futures\n this.stats.serOutMs += performance.now() - t0;\n return undefined;\n }\n const result = JSON.parse(text);\n this.stats.serOutMs += performance.now() - t0;\n return result;\n }\n\n private injectCtx() {\n Scope.withScope((localScope) => {\n const configCtx = localScope.manage(this.vm.newObject());\n\n //\n // Core props\n //\n\n this.vm.setProp(configCtx, \"callbackRegistry\", this.callbackRegistry);\n this.vm.setProp(\n configCtx,\n \"featureFlags\",\n this.exportObjectUniversal(JsRenderInternal.GlobalCfgRenderCtxFeatureFlags, localScope),\n );\n\n // Inject context values from computableHelper (reactive context for outputs, inputsValid, etc.)\n if (this.computableHelper !== undefined) {\n this.computableHelper.injectCtx(configCtx);\n }\n\n //\n // Creating global variable inside the vm\n //\n\n this.vm.setProp(this.vm.global, \"cfgRenderCtx\", configCtx);\n });\n }\n}\n\n/** Holds errors that happened in the host code (like in middle-layer's drivers)\n * and then throws it where the error from quick JS is needed.\n * QuickJS couldn't throw custom errors, so we store them here, and rethrow them when we exit QuickJS side. */\nexport class ErrorRepository {\n private readonly errorIdToError = new Map<string, unknown>();\n\n /** Sets the error to the repository and returns a mimicrated error that also has uuid key of the original error. */\n public setAndRecreateForQuickJS(error: unknown): {\n name: string;\n message: string;\n } {\n const errorId = randomUUID();\n this.errorIdToError.set(errorId, error);\n\n if (error instanceof Error) {\n return {\n name: `${error.name}/uuid:${errorId}`,\n message: error.message,\n };\n }\n\n return {\n name: `UnknownErrorQuickJS/uuid:${errorId}`,\n message: `${error as any}`,\n };\n }\n\n /** Returns the original error that was stored by parsing uuid of mimicrated error. */\n public getOriginal(quickJSError: unknown): unknown {\n if (!(quickJSError instanceof errors.QuickJSUnwrapError)) {\n console.warn(\n \"ErrorRepo: quickJSError is not a QuickJSUnwrapError\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const cause = quickJSError.cause;\n if (\n !(\n typeof cause === \"object\" &&\n cause !== null &&\n \"name\" in cause &&\n typeof cause.name === \"string\"\n )\n ) {\n console.warn(\n \"ErrorRepo: quickJSError.cause is not an Error (can be stack limit exceeded)\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const causeName = cause.name;\n const errorId = causeName.slice(causeName.indexOf(\"/uuid:\") + \"/uuid:\".length);\n if (!errorId) {\n throw new Error(\n `ErrorRepo: quickJSError.cause.name does not contain errorId: ${causeName}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n const error = this.errorIdToError.get(errorId);\n if (error === undefined) {\n throw new Error(\n `ErrorRepo: errorId not found: ${errorId}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n return new PlQuickJSError(quickJSError, error as Error);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"context.cjs","names":["ComputableContextHelper","Scope","JsRenderInternal","errors","PlQuickJSError"],"sources":["../../src/js_render/context.ts"],"sourcesContent":["import type { ComputableCtx } from \"@milaboratories/computable\";\nimport type { BlockCodeKnownFeatureFlags } from \"@platforma-sdk/model\";\nimport { JsRenderInternal } from \"@platforma-sdk/model\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport { randomUUID } from \"node:crypto\";\nimport type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { Scope, errors } from \"quickjs-emscripten\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport { stringifyWithResourceId } from \"@milaboratories/pl-client\";\nimport { PlQuickJSError } from \"@milaboratories/pl-errors\";\nimport { ComputableContextHelper } from \"./computable_context\";\n\nexport type DeadlineSettings = {\n currentExecutionTarget: string;\n deadline: number;\n};\n\n/**\n * Communicates a deadline to the quickjs runtime, that if passed, will interrupt the execution.\n * Undefined can be used to reset the deadline.\n * */\nexport type DeadlineSetter = (settings: DeadlineSettings | undefined) => void;\n\nfunction isArrayBufferOrView(obj: unknown): obj is ArrayBufferLike {\n return obj instanceof ArrayBuffer || ArrayBuffer.isView(obj);\n}\n\n/**\n * Contains references to objects needed to execute lambda within computable,\n * providing access to:\n * - block context\n * - computable context\n * - middle layer environment\n * */\nexport type ComputableEnv = {\n readonly blockCtx: BlockContextAny;\n readonly mlEnv: MiddleLayerEnvironment;\n computableCtx: ComputableCtx;\n};\n\n/** Execution stats accumulated during the lifetime of a JsExecutionContext. */\nexport type JsExecStats = {\n bundleEvalMs: number;\n bundleBytes: number;\n\n callbackMs: number;\n callbackCount: number;\n\n serInMs: number;\n serInBytes: number;\n serInCalls: number;\n\n serOutMs: number;\n serOutBytes: number;\n serOutCalls: number;\n\n ctxMethodCalls: number;\n ctxMethodMs: number;\n};\n\nexport class JsExecutionContext {\n private readonly callbackRegistry: QuickJSHandle;\n private readonly fnJSONStringify: QuickJSHandle;\n private readonly fnJSONParse: QuickJSHandle;\n\n public readonly errorRepo = new ErrorRepository();\n\n public readonly computableHelper: ComputableContextHelper | undefined;\n\n public readonly stats: JsExecStats = {\n bundleEvalMs: 0,\n bundleBytes: 0,\n callbackMs: 0,\n callbackCount: 0,\n serInMs: 0,\n serInBytes: 0,\n serInCalls: 0,\n serOutMs: 0,\n serOutBytes: 0,\n serOutCalls: 0,\n ctxMethodCalls: 0,\n ctxMethodMs: 0,\n };\n\n /**\n * Creates a new JS execution context.\n *\n * @param scope - QuickJS scope for memory management\n * @param vm - QuickJS VM context\n * @param deadlineSetter - Function to set execution deadline\n * @param featureFlags - Block feature flags\n * @param computableEnv - Optional reactive computable environment (for outputs, inputsValid, etc.)\n */\n constructor(\n public readonly scope: Scope,\n public readonly vm: QuickJSContext,\n private readonly deadlineSetter: DeadlineSetter,\n featureFlags: BlockCodeKnownFeatureFlags | undefined,\n computableEnv?: ComputableEnv,\n ) {\n this.callbackRegistry = this.scope.manage(this.vm.newObject());\n\n this.fnJSONStringify = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"stringify\")),\n );\n if (vm.typeof(this.fnJSONStringify) !== \"function\")\n throw new Error(`JSON.stringify() not found.`);\n\n this.fnJSONParse = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"parse\")),\n );\n if (vm.typeof(this.fnJSONParse) !== \"function\") throw new Error(`JSON.parse() not found.`);\n\n if (computableEnv !== undefined)\n this.computableHelper = new ComputableContextHelper(\n this,\n computableEnv.blockCtx,\n computableEnv.mlEnv,\n featureFlags,\n computableEnv.computableCtx,\n );\n\n this.injectCtx();\n }\n\n public resetComputableCtx() {\n notEmpty(\n this.computableHelper,\n \"Computable context helper is not initialized\",\n ).resetComputableCtx();\n }\n\n private static cleanErrorContext(error: unknown): void {\n if (typeof error === \"object\" && error !== null && \"context\" in error) delete error[\"context\"];\n }\n\n // private static cleanError(error: unknown): unknown {\n // if (error instanceof errors.QuickJSUnwrapError) {\n // const { cause, context: _, name, message, stack, ...rest } = error;\n // const clean = new errors.QuickJSUnwrapError(cause);\n // Object.assign(clean, { ...rest, name, message, stack });\n // return clean;\n // }\n // return error;\n // }\n\n public evaluateBundle(code: string) {\n const t0 = performance.now();\n try {\n this.deadlineSetter({\n currentExecutionTarget: \"evaluateBundle\",\n deadline: Date.now() + 10000,\n });\n this.vm.unwrapResult(this.vm.evalCode(code, \"bundle.js\", { type: \"global\" })).dispose();\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n throw err;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.bundleEvalMs += performance.now() - t0;\n this.stats.bundleBytes += code.length;\n }\n }\n\n public runCallback(cbName: string, ...args: unknown[]): QuickJSHandle {\n const t0 = performance.now();\n try {\n this.deadlineSetter({ currentExecutionTarget: cbName, deadline: Date.now() + 10000 });\n return Scope.withScope((localScope) => {\n const targetCallback = localScope.manage(this.vm.getProp(this.callbackRegistry, cbName));\n\n if (this.vm.typeof(targetCallback) !== \"function\")\n throw new Error(`No such callback: ${cbName}`);\n\n return this.scope.manage(\n this.vm.unwrapResult(\n this.vm.callFunction(\n targetCallback,\n this.vm.undefined,\n ...args.map((arg) => this.exportObjectUniversal(arg, localScope)),\n ),\n ),\n );\n });\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n const original = this.errorRepo.getOriginal(err);\n throw original;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.callbackMs += performance.now() - t0;\n this.stats.callbackCount++;\n }\n }\n\n //\n // QuickJS Helpers\n //\n\n public exportSingleValue(\n obj: boolean | number | string | null | ArrayBuffer | undefined,\n scope?: Scope,\n ): QuickJSHandle {\n const result = this.tryExportSingleValue(obj, scope);\n if (result === undefined) {\n throw new Error(\n `Can't export value: ${obj === undefined ? \"undefined\" : JSON.stringify(obj)}`,\n );\n }\n return result;\n }\n\n public tryExportSingleValue(obj: unknown, scope?: Scope): QuickJSHandle | undefined {\n let handle: QuickJSHandle;\n let manage = false;\n switch (typeof obj) {\n case \"string\":\n handle = this.vm.newString(obj);\n manage = true;\n break;\n case \"number\":\n handle = this.vm.newNumber(obj);\n manage = true;\n break;\n case \"undefined\":\n handle = this.vm.undefined;\n break;\n case \"boolean\":\n handle = obj ? this.vm.true : this.vm.false;\n break;\n default:\n if (obj === null) {\n handle = this.vm.null;\n break;\n }\n if (isArrayBufferOrView(obj)) {\n handle = this.vm.newArrayBuffer(obj);\n manage = true;\n break;\n }\n return undefined;\n }\n return manage && scope != undefined ? scope.manage(handle) : handle;\n }\n\n public exportObjectUniversal(obj: unknown, scope?: Scope): QuickJSHandle {\n const simpleHandle = this.tryExportSingleValue(obj, scope);\n if (simpleHandle !== undefined) return simpleHandle;\n return this.exportObjectViaJson(obj, scope);\n }\n\n public exportObjectViaJson(obj: unknown, scope?: Scope): QuickJSHandle {\n const t0 = performance.now();\n const json = JSON.stringify(obj);\n this.stats.serInBytes += json.length;\n this.stats.serInCalls++;\n const result = this.vm\n .newString(json)\n .consume((jsonHandle) =>\n this.vm.unwrapResult(this.vm.callFunction(this.fnJSONParse, this.vm.undefined, jsonHandle)),\n );\n this.stats.serInMs += performance.now() - t0;\n return scope !== undefined ? scope.manage(result) : result;\n }\n\n public importObjectUniversal(handle: QuickJSHandle | undefined): unknown {\n if (handle === undefined) return undefined;\n switch (this.vm.typeof(handle)) {\n case \"undefined\":\n return undefined;\n case \"boolean\":\n case \"number\":\n case \"string\":\n return this.vm.dump(handle);\n default:\n return this.importObjectViaJson(handle);\n }\n }\n\n public importObjectViaJson(handle: QuickJSHandle): unknown {\n const t0 = performance.now();\n const text = this.vm\n .unwrapResult(this.vm.callFunction(this.fnJSONStringify, this.vm.undefined, handle))\n .consume((strHandle) => this.vm.getString(strHandle));\n this.stats.serOutBytes += text.length;\n this.stats.serOutCalls++;\n if (text === \"undefined\") {\n // special case with futures\n this.stats.serOutMs += performance.now() - t0;\n return undefined;\n }\n const result = JSON.parse(text);\n this.stats.serOutMs += performance.now() - t0;\n return result;\n }\n\n private injectCtx() {\n Scope.withScope((localScope) => {\n const configCtx = localScope.manage(this.vm.newObject());\n\n //\n // Core props\n //\n\n this.vm.setProp(configCtx, \"callbackRegistry\", this.callbackRegistry);\n this.vm.setProp(\n configCtx,\n \"featureFlags\",\n this.exportObjectUniversal(JsRenderInternal.GlobalCfgRenderCtxFeatureFlags, localScope),\n );\n\n // Inject context values from computableHelper (reactive context for outputs, inputsValid, etc.)\n if (this.computableHelper !== undefined) {\n this.computableHelper.injectCtx(configCtx);\n }\n\n //\n // Creating global variable inside the vm\n //\n\n this.vm.setProp(this.vm.global, \"cfgRenderCtx\", configCtx);\n });\n }\n}\n\n/** Holds errors that happened in the host code (like in middle-layer's drivers)\n * and then throws it where the error from quick JS is needed.\n * QuickJS couldn't throw custom errors, so we store them here, and rethrow them when we exit QuickJS side. */\nexport class ErrorRepository {\n private readonly errorIdToError = new Map<string, unknown>();\n\n /** Sets the error to the repository and returns a mimicrated error that also has uuid key of the original error. */\n public setAndRecreateForQuickJS(error: unknown): {\n name: string;\n message: string;\n } {\n const errorId = randomUUID();\n this.errorIdToError.set(errorId, error);\n\n if (error instanceof Error) {\n return {\n name: `${error.name}/uuid:${errorId}`,\n message: error.message,\n };\n }\n\n return {\n name: `UnknownErrorQuickJS/uuid:${errorId}`,\n message: `${error as any}`,\n };\n }\n\n /** Returns the original error that was stored by parsing uuid of mimicrated error. */\n public getOriginal(quickJSError: unknown): unknown {\n if (!(quickJSError instanceof errors.QuickJSUnwrapError)) {\n console.warn(\n \"ErrorRepo: quickJSError is not a QuickJSUnwrapError\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const cause = quickJSError.cause;\n if (\n !(\n typeof cause === \"object\" &&\n cause !== null &&\n \"name\" in cause &&\n typeof cause.name === \"string\"\n )\n ) {\n console.warn(\n \"ErrorRepo: quickJSError.cause is not an Error (can be stack limit exceeded)\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const causeName = cause.name;\n const uuidIdx = causeName.indexOf(\"/uuid:\");\n if (uuidIdx === -1) {\n const causeMsg = \"message\" in cause && typeof cause.message === \"string\" ? cause.message : \"\";\n throw new Error(\n `QuickJS native error: ${causeName}: ${causeMsg}, ${stringifyWithResourceId(quickJSError)}`,\n { cause: quickJSError },\n );\n }\n const errorId = causeName.slice(uuidIdx + \"/uuid:\".length);\n if (!errorId) {\n throw new Error(\n `ErrorRepo: quickJSError.cause.name does not contain errorId: ${causeName}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n const error = this.errorIdToError.get(errorId);\n if (error === undefined) {\n throw new Error(\n `ErrorRepo: errorId not found: ${errorId}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n return new PlQuickJSError(quickJSError, error as Error);\n }\n}\n"],"mappings":"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"}
@@ -208,7 +208,12 @@ var ErrorRepository = class {
208
208
  return quickJSError;
209
209
  }
210
210
  const causeName = cause.name;
211
- const errorId = causeName.slice(causeName.indexOf("/uuid:") + 6);
211
+ const uuidIdx = causeName.indexOf("/uuid:");
212
+ if (uuidIdx === -1) {
213
+ const causeMsg = "message" in cause && typeof cause.message === "string" ? cause.message : "";
214
+ throw new Error(`QuickJS native error: ${causeName}: ${causeMsg}, ${stringifyWithResourceId(quickJSError)}`, { cause: quickJSError });
215
+ }
216
+ const errorId = causeName.slice(uuidIdx + 6);
212
217
  if (!errorId) throw new Error(`ErrorRepo: quickJSError.cause.name does not contain errorId: ${causeName}, ${stringifyWithResourceId(quickJSError)}`);
213
218
  const error = this.errorIdToError.get(errorId);
214
219
  if (error === void 0) throw new Error(`ErrorRepo: errorId not found: ${errorId}, ${stringifyWithResourceId(quickJSError)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../src/js_render/context.ts"],"sourcesContent":["import type { ComputableCtx } from \"@milaboratories/computable\";\nimport type { BlockCodeKnownFeatureFlags } from \"@platforma-sdk/model\";\nimport { JsRenderInternal } from \"@platforma-sdk/model\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport { randomUUID } from \"node:crypto\";\nimport type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { Scope, errors } from \"quickjs-emscripten\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport { stringifyWithResourceId } from \"@milaboratories/pl-client\";\nimport { PlQuickJSError } from \"@milaboratories/pl-errors\";\nimport { ComputableContextHelper } from \"./computable_context\";\n\nexport type DeadlineSettings = {\n currentExecutionTarget: string;\n deadline: number;\n};\n\n/**\n * Communicates a deadline to the quickjs runtime, that if passed, will interrupt the execution.\n * Undefined can be used to reset the deadline.\n * */\nexport type DeadlineSetter = (settings: DeadlineSettings | undefined) => void;\n\nfunction isArrayBufferOrView(obj: unknown): obj is ArrayBufferLike {\n return obj instanceof ArrayBuffer || ArrayBuffer.isView(obj);\n}\n\n/**\n * Contains references to objects needed to execute lambda within computable,\n * providing access to:\n * - block context\n * - computable context\n * - middle layer environment\n * */\nexport type ComputableEnv = {\n readonly blockCtx: BlockContextAny;\n readonly mlEnv: MiddleLayerEnvironment;\n computableCtx: ComputableCtx;\n};\n\n/** Execution stats accumulated during the lifetime of a JsExecutionContext. */\nexport type JsExecStats = {\n bundleEvalMs: number;\n bundleBytes: number;\n\n callbackMs: number;\n callbackCount: number;\n\n serInMs: number;\n serInBytes: number;\n serInCalls: number;\n\n serOutMs: number;\n serOutBytes: number;\n serOutCalls: number;\n\n ctxMethodCalls: number;\n ctxMethodMs: number;\n};\n\nexport class JsExecutionContext {\n private readonly callbackRegistry: QuickJSHandle;\n private readonly fnJSONStringify: QuickJSHandle;\n private readonly fnJSONParse: QuickJSHandle;\n\n public readonly errorRepo = new ErrorRepository();\n\n public readonly computableHelper: ComputableContextHelper | undefined;\n\n public readonly stats: JsExecStats = {\n bundleEvalMs: 0,\n bundleBytes: 0,\n callbackMs: 0,\n callbackCount: 0,\n serInMs: 0,\n serInBytes: 0,\n serInCalls: 0,\n serOutMs: 0,\n serOutBytes: 0,\n serOutCalls: 0,\n ctxMethodCalls: 0,\n ctxMethodMs: 0,\n };\n\n /**\n * Creates a new JS execution context.\n *\n * @param scope - QuickJS scope for memory management\n * @param vm - QuickJS VM context\n * @param deadlineSetter - Function to set execution deadline\n * @param featureFlags - Block feature flags\n * @param computableEnv - Optional reactive computable environment (for outputs, inputsValid, etc.)\n */\n constructor(\n public readonly scope: Scope,\n public readonly vm: QuickJSContext,\n private readonly deadlineSetter: DeadlineSetter,\n featureFlags: BlockCodeKnownFeatureFlags | undefined,\n computableEnv?: ComputableEnv,\n ) {\n this.callbackRegistry = this.scope.manage(this.vm.newObject());\n\n this.fnJSONStringify = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"stringify\")),\n );\n if (vm.typeof(this.fnJSONStringify) !== \"function\")\n throw new Error(`JSON.stringify() not found.`);\n\n this.fnJSONParse = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"parse\")),\n );\n if (vm.typeof(this.fnJSONParse) !== \"function\") throw new Error(`JSON.parse() not found.`);\n\n if (computableEnv !== undefined)\n this.computableHelper = new ComputableContextHelper(\n this,\n computableEnv.blockCtx,\n computableEnv.mlEnv,\n featureFlags,\n computableEnv.computableCtx,\n );\n\n this.injectCtx();\n }\n\n public resetComputableCtx() {\n notEmpty(\n this.computableHelper,\n \"Computable context helper is not initialized\",\n ).resetComputableCtx();\n }\n\n private static cleanErrorContext(error: unknown): void {\n if (typeof error === \"object\" && error !== null && \"context\" in error) delete error[\"context\"];\n }\n\n // private static cleanError(error: unknown): unknown {\n // if (error instanceof errors.QuickJSUnwrapError) {\n // const { cause, context: _, name, message, stack, ...rest } = error;\n // const clean = new errors.QuickJSUnwrapError(cause);\n // Object.assign(clean, { ...rest, name, message, stack });\n // return clean;\n // }\n // return error;\n // }\n\n public evaluateBundle(code: string) {\n const t0 = performance.now();\n try {\n this.deadlineSetter({\n currentExecutionTarget: \"evaluateBundle\",\n deadline: Date.now() + 10000,\n });\n this.vm.unwrapResult(this.vm.evalCode(code, \"bundle.js\", { type: \"global\" })).dispose();\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n throw err;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.bundleEvalMs += performance.now() - t0;\n this.stats.bundleBytes += code.length;\n }\n }\n\n public runCallback(cbName: string, ...args: unknown[]): QuickJSHandle {\n const t0 = performance.now();\n try {\n this.deadlineSetter({ currentExecutionTarget: cbName, deadline: Date.now() + 10000 });\n return Scope.withScope((localScope) => {\n const targetCallback = localScope.manage(this.vm.getProp(this.callbackRegistry, cbName));\n\n if (this.vm.typeof(targetCallback) !== \"function\")\n throw new Error(`No such callback: ${cbName}`);\n\n return this.scope.manage(\n this.vm.unwrapResult(\n this.vm.callFunction(\n targetCallback,\n this.vm.undefined,\n ...args.map((arg) => this.exportObjectUniversal(arg, localScope)),\n ),\n ),\n );\n });\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n const original = this.errorRepo.getOriginal(err);\n throw original;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.callbackMs += performance.now() - t0;\n this.stats.callbackCount++;\n }\n }\n\n //\n // QuickJS Helpers\n //\n\n public exportSingleValue(\n obj: boolean | number | string | null | ArrayBuffer | undefined,\n scope?: Scope,\n ): QuickJSHandle {\n const result = this.tryExportSingleValue(obj, scope);\n if (result === undefined) {\n throw new Error(\n `Can't export value: ${obj === undefined ? \"undefined\" : JSON.stringify(obj)}`,\n );\n }\n return result;\n }\n\n public tryExportSingleValue(obj: unknown, scope?: Scope): QuickJSHandle | undefined {\n let handle: QuickJSHandle;\n let manage = false;\n switch (typeof obj) {\n case \"string\":\n handle = this.vm.newString(obj);\n manage = true;\n break;\n case \"number\":\n handle = this.vm.newNumber(obj);\n manage = true;\n break;\n case \"undefined\":\n handle = this.vm.undefined;\n break;\n case \"boolean\":\n handle = obj ? this.vm.true : this.vm.false;\n break;\n default:\n if (obj === null) {\n handle = this.vm.null;\n break;\n }\n if (isArrayBufferOrView(obj)) {\n handle = this.vm.newArrayBuffer(obj);\n manage = true;\n break;\n }\n return undefined;\n }\n return manage && scope != undefined ? scope.manage(handle) : handle;\n }\n\n public exportObjectUniversal(obj: unknown, scope?: Scope): QuickJSHandle {\n const simpleHandle = this.tryExportSingleValue(obj, scope);\n if (simpleHandle !== undefined) return simpleHandle;\n return this.exportObjectViaJson(obj, scope);\n }\n\n public exportObjectViaJson(obj: unknown, scope?: Scope): QuickJSHandle {\n const t0 = performance.now();\n const json = JSON.stringify(obj);\n this.stats.serInBytes += json.length;\n this.stats.serInCalls++;\n const result = this.vm\n .newString(json)\n .consume((jsonHandle) =>\n this.vm.unwrapResult(this.vm.callFunction(this.fnJSONParse, this.vm.undefined, jsonHandle)),\n );\n this.stats.serInMs += performance.now() - t0;\n return scope !== undefined ? scope.manage(result) : result;\n }\n\n public importObjectUniversal(handle: QuickJSHandle | undefined): unknown {\n if (handle === undefined) return undefined;\n switch (this.vm.typeof(handle)) {\n case \"undefined\":\n return undefined;\n case \"boolean\":\n case \"number\":\n case \"string\":\n return this.vm.dump(handle);\n default:\n return this.importObjectViaJson(handle);\n }\n }\n\n public importObjectViaJson(handle: QuickJSHandle): unknown {\n const t0 = performance.now();\n const text = this.vm\n .unwrapResult(this.vm.callFunction(this.fnJSONStringify, this.vm.undefined, handle))\n .consume((strHandle) => this.vm.getString(strHandle));\n this.stats.serOutBytes += text.length;\n this.stats.serOutCalls++;\n if (text === \"undefined\") {\n // special case with futures\n this.stats.serOutMs += performance.now() - t0;\n return undefined;\n }\n const result = JSON.parse(text);\n this.stats.serOutMs += performance.now() - t0;\n return result;\n }\n\n private injectCtx() {\n Scope.withScope((localScope) => {\n const configCtx = localScope.manage(this.vm.newObject());\n\n //\n // Core props\n //\n\n this.vm.setProp(configCtx, \"callbackRegistry\", this.callbackRegistry);\n this.vm.setProp(\n configCtx,\n \"featureFlags\",\n this.exportObjectUniversal(JsRenderInternal.GlobalCfgRenderCtxFeatureFlags, localScope),\n );\n\n // Inject context values from computableHelper (reactive context for outputs, inputsValid, etc.)\n if (this.computableHelper !== undefined) {\n this.computableHelper.injectCtx(configCtx);\n }\n\n //\n // Creating global variable inside the vm\n //\n\n this.vm.setProp(this.vm.global, \"cfgRenderCtx\", configCtx);\n });\n }\n}\n\n/** Holds errors that happened in the host code (like in middle-layer's drivers)\n * and then throws it where the error from quick JS is needed.\n * QuickJS couldn't throw custom errors, so we store them here, and rethrow them when we exit QuickJS side. */\nexport class ErrorRepository {\n private readonly errorIdToError = new Map<string, unknown>();\n\n /** Sets the error to the repository and returns a mimicrated error that also has uuid key of the original error. */\n public setAndRecreateForQuickJS(error: unknown): {\n name: string;\n message: string;\n } {\n const errorId = randomUUID();\n this.errorIdToError.set(errorId, error);\n\n if (error instanceof Error) {\n return {\n name: `${error.name}/uuid:${errorId}`,\n message: error.message,\n };\n }\n\n return {\n name: `UnknownErrorQuickJS/uuid:${errorId}`,\n message: `${error as any}`,\n };\n }\n\n /** Returns the original error that was stored by parsing uuid of mimicrated error. */\n public getOriginal(quickJSError: unknown): unknown {\n if (!(quickJSError instanceof errors.QuickJSUnwrapError)) {\n console.warn(\n \"ErrorRepo: quickJSError is not a QuickJSUnwrapError\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const cause = quickJSError.cause;\n if (\n !(\n typeof cause === \"object\" &&\n cause !== null &&\n \"name\" in cause &&\n typeof cause.name === \"string\"\n )\n ) {\n console.warn(\n \"ErrorRepo: quickJSError.cause is not an Error (can be stack limit exceeded)\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const causeName = cause.name;\n const errorId = causeName.slice(causeName.indexOf(\"/uuid:\") + \"/uuid:\".length);\n if (!errorId) {\n throw new Error(\n `ErrorRepo: quickJSError.cause.name does not contain errorId: ${causeName}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n const error = this.errorIdToError.get(errorId);\n if (error === undefined) {\n throw new Error(\n `ErrorRepo: errorId not found: ${errorId}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n return new PlQuickJSError(quickJSError, error as Error);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"context.js","names":[],"sources":["../../src/js_render/context.ts"],"sourcesContent":["import type { ComputableCtx } from \"@milaboratories/computable\";\nimport type { BlockCodeKnownFeatureFlags } from \"@platforma-sdk/model\";\nimport { JsRenderInternal } from \"@platforma-sdk/model\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport { randomUUID } from \"node:crypto\";\nimport type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { Scope, errors } from \"quickjs-emscripten\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport { stringifyWithResourceId } from \"@milaboratories/pl-client\";\nimport { PlQuickJSError } from \"@milaboratories/pl-errors\";\nimport { ComputableContextHelper } from \"./computable_context\";\n\nexport type DeadlineSettings = {\n currentExecutionTarget: string;\n deadline: number;\n};\n\n/**\n * Communicates a deadline to the quickjs runtime, that if passed, will interrupt the execution.\n * Undefined can be used to reset the deadline.\n * */\nexport type DeadlineSetter = (settings: DeadlineSettings | undefined) => void;\n\nfunction isArrayBufferOrView(obj: unknown): obj is ArrayBufferLike {\n return obj instanceof ArrayBuffer || ArrayBuffer.isView(obj);\n}\n\n/**\n * Contains references to objects needed to execute lambda within computable,\n * providing access to:\n * - block context\n * - computable context\n * - middle layer environment\n * */\nexport type ComputableEnv = {\n readonly blockCtx: BlockContextAny;\n readonly mlEnv: MiddleLayerEnvironment;\n computableCtx: ComputableCtx;\n};\n\n/** Execution stats accumulated during the lifetime of a JsExecutionContext. */\nexport type JsExecStats = {\n bundleEvalMs: number;\n bundleBytes: number;\n\n callbackMs: number;\n callbackCount: number;\n\n serInMs: number;\n serInBytes: number;\n serInCalls: number;\n\n serOutMs: number;\n serOutBytes: number;\n serOutCalls: number;\n\n ctxMethodCalls: number;\n ctxMethodMs: number;\n};\n\nexport class JsExecutionContext {\n private readonly callbackRegistry: QuickJSHandle;\n private readonly fnJSONStringify: QuickJSHandle;\n private readonly fnJSONParse: QuickJSHandle;\n\n public readonly errorRepo = new ErrorRepository();\n\n public readonly computableHelper: ComputableContextHelper | undefined;\n\n public readonly stats: JsExecStats = {\n bundleEvalMs: 0,\n bundleBytes: 0,\n callbackMs: 0,\n callbackCount: 0,\n serInMs: 0,\n serInBytes: 0,\n serInCalls: 0,\n serOutMs: 0,\n serOutBytes: 0,\n serOutCalls: 0,\n ctxMethodCalls: 0,\n ctxMethodMs: 0,\n };\n\n /**\n * Creates a new JS execution context.\n *\n * @param scope - QuickJS scope for memory management\n * @param vm - QuickJS VM context\n * @param deadlineSetter - Function to set execution deadline\n * @param featureFlags - Block feature flags\n * @param computableEnv - Optional reactive computable environment (for outputs, inputsValid, etc.)\n */\n constructor(\n public readonly scope: Scope,\n public readonly vm: QuickJSContext,\n private readonly deadlineSetter: DeadlineSetter,\n featureFlags: BlockCodeKnownFeatureFlags | undefined,\n computableEnv?: ComputableEnv,\n ) {\n this.callbackRegistry = this.scope.manage(this.vm.newObject());\n\n this.fnJSONStringify = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"stringify\")),\n );\n if (vm.typeof(this.fnJSONStringify) !== \"function\")\n throw new Error(`JSON.stringify() not found.`);\n\n this.fnJSONParse = scope.manage(\n vm.getProp(vm.global, \"JSON\").consume((json) => vm.getProp(json, \"parse\")),\n );\n if (vm.typeof(this.fnJSONParse) !== \"function\") throw new Error(`JSON.parse() not found.`);\n\n if (computableEnv !== undefined)\n this.computableHelper = new ComputableContextHelper(\n this,\n computableEnv.blockCtx,\n computableEnv.mlEnv,\n featureFlags,\n computableEnv.computableCtx,\n );\n\n this.injectCtx();\n }\n\n public resetComputableCtx() {\n notEmpty(\n this.computableHelper,\n \"Computable context helper is not initialized\",\n ).resetComputableCtx();\n }\n\n private static cleanErrorContext(error: unknown): void {\n if (typeof error === \"object\" && error !== null && \"context\" in error) delete error[\"context\"];\n }\n\n // private static cleanError(error: unknown): unknown {\n // if (error instanceof errors.QuickJSUnwrapError) {\n // const { cause, context: _, name, message, stack, ...rest } = error;\n // const clean = new errors.QuickJSUnwrapError(cause);\n // Object.assign(clean, { ...rest, name, message, stack });\n // return clean;\n // }\n // return error;\n // }\n\n public evaluateBundle(code: string) {\n const t0 = performance.now();\n try {\n this.deadlineSetter({\n currentExecutionTarget: \"evaluateBundle\",\n deadline: Date.now() + 10000,\n });\n this.vm.unwrapResult(this.vm.evalCode(code, \"bundle.js\", { type: \"global\" })).dispose();\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n throw err;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.bundleEvalMs += performance.now() - t0;\n this.stats.bundleBytes += code.length;\n }\n }\n\n public runCallback(cbName: string, ...args: unknown[]): QuickJSHandle {\n const t0 = performance.now();\n try {\n this.deadlineSetter({ currentExecutionTarget: cbName, deadline: Date.now() + 10000 });\n return Scope.withScope((localScope) => {\n const targetCallback = localScope.manage(this.vm.getProp(this.callbackRegistry, cbName));\n\n if (this.vm.typeof(targetCallback) !== \"function\")\n throw new Error(`No such callback: ${cbName}`);\n\n return this.scope.manage(\n this.vm.unwrapResult(\n this.vm.callFunction(\n targetCallback,\n this.vm.undefined,\n ...args.map((arg) => this.exportObjectUniversal(arg, localScope)),\n ),\n ),\n );\n });\n } catch (err: unknown) {\n JsExecutionContext.cleanErrorContext(err);\n const original = this.errorRepo.getOriginal(err);\n throw original;\n } finally {\n this.deadlineSetter(undefined);\n this.stats.callbackMs += performance.now() - t0;\n this.stats.callbackCount++;\n }\n }\n\n //\n // QuickJS Helpers\n //\n\n public exportSingleValue(\n obj: boolean | number | string | null | ArrayBuffer | undefined,\n scope?: Scope,\n ): QuickJSHandle {\n const result = this.tryExportSingleValue(obj, scope);\n if (result === undefined) {\n throw new Error(\n `Can't export value: ${obj === undefined ? \"undefined\" : JSON.stringify(obj)}`,\n );\n }\n return result;\n }\n\n public tryExportSingleValue(obj: unknown, scope?: Scope): QuickJSHandle | undefined {\n let handle: QuickJSHandle;\n let manage = false;\n switch (typeof obj) {\n case \"string\":\n handle = this.vm.newString(obj);\n manage = true;\n break;\n case \"number\":\n handle = this.vm.newNumber(obj);\n manage = true;\n break;\n case \"undefined\":\n handle = this.vm.undefined;\n break;\n case \"boolean\":\n handle = obj ? this.vm.true : this.vm.false;\n break;\n default:\n if (obj === null) {\n handle = this.vm.null;\n break;\n }\n if (isArrayBufferOrView(obj)) {\n handle = this.vm.newArrayBuffer(obj);\n manage = true;\n break;\n }\n return undefined;\n }\n return manage && scope != undefined ? scope.manage(handle) : handle;\n }\n\n public exportObjectUniversal(obj: unknown, scope?: Scope): QuickJSHandle {\n const simpleHandle = this.tryExportSingleValue(obj, scope);\n if (simpleHandle !== undefined) return simpleHandle;\n return this.exportObjectViaJson(obj, scope);\n }\n\n public exportObjectViaJson(obj: unknown, scope?: Scope): QuickJSHandle {\n const t0 = performance.now();\n const json = JSON.stringify(obj);\n this.stats.serInBytes += json.length;\n this.stats.serInCalls++;\n const result = this.vm\n .newString(json)\n .consume((jsonHandle) =>\n this.vm.unwrapResult(this.vm.callFunction(this.fnJSONParse, this.vm.undefined, jsonHandle)),\n );\n this.stats.serInMs += performance.now() - t0;\n return scope !== undefined ? scope.manage(result) : result;\n }\n\n public importObjectUniversal(handle: QuickJSHandle | undefined): unknown {\n if (handle === undefined) return undefined;\n switch (this.vm.typeof(handle)) {\n case \"undefined\":\n return undefined;\n case \"boolean\":\n case \"number\":\n case \"string\":\n return this.vm.dump(handle);\n default:\n return this.importObjectViaJson(handle);\n }\n }\n\n public importObjectViaJson(handle: QuickJSHandle): unknown {\n const t0 = performance.now();\n const text = this.vm\n .unwrapResult(this.vm.callFunction(this.fnJSONStringify, this.vm.undefined, handle))\n .consume((strHandle) => this.vm.getString(strHandle));\n this.stats.serOutBytes += text.length;\n this.stats.serOutCalls++;\n if (text === \"undefined\") {\n // special case with futures\n this.stats.serOutMs += performance.now() - t0;\n return undefined;\n }\n const result = JSON.parse(text);\n this.stats.serOutMs += performance.now() - t0;\n return result;\n }\n\n private injectCtx() {\n Scope.withScope((localScope) => {\n const configCtx = localScope.manage(this.vm.newObject());\n\n //\n // Core props\n //\n\n this.vm.setProp(configCtx, \"callbackRegistry\", this.callbackRegistry);\n this.vm.setProp(\n configCtx,\n \"featureFlags\",\n this.exportObjectUniversal(JsRenderInternal.GlobalCfgRenderCtxFeatureFlags, localScope),\n );\n\n // Inject context values from computableHelper (reactive context for outputs, inputsValid, etc.)\n if (this.computableHelper !== undefined) {\n this.computableHelper.injectCtx(configCtx);\n }\n\n //\n // Creating global variable inside the vm\n //\n\n this.vm.setProp(this.vm.global, \"cfgRenderCtx\", configCtx);\n });\n }\n}\n\n/** Holds errors that happened in the host code (like in middle-layer's drivers)\n * and then throws it where the error from quick JS is needed.\n * QuickJS couldn't throw custom errors, so we store them here, and rethrow them when we exit QuickJS side. */\nexport class ErrorRepository {\n private readonly errorIdToError = new Map<string, unknown>();\n\n /** Sets the error to the repository and returns a mimicrated error that also has uuid key of the original error. */\n public setAndRecreateForQuickJS(error: unknown): {\n name: string;\n message: string;\n } {\n const errorId = randomUUID();\n this.errorIdToError.set(errorId, error);\n\n if (error instanceof Error) {\n return {\n name: `${error.name}/uuid:${errorId}`,\n message: error.message,\n };\n }\n\n return {\n name: `UnknownErrorQuickJS/uuid:${errorId}`,\n message: `${error as any}`,\n };\n }\n\n /** Returns the original error that was stored by parsing uuid of mimicrated error. */\n public getOriginal(quickJSError: unknown): unknown {\n if (!(quickJSError instanceof errors.QuickJSUnwrapError)) {\n console.warn(\n \"ErrorRepo: quickJSError is not a QuickJSUnwrapError\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const cause = quickJSError.cause;\n if (\n !(\n typeof cause === \"object\" &&\n cause !== null &&\n \"name\" in cause &&\n typeof cause.name === \"string\"\n )\n ) {\n console.warn(\n \"ErrorRepo: quickJSError.cause is not an Error (can be stack limit exceeded)\",\n stringifyWithResourceId(quickJSError),\n );\n return quickJSError;\n }\n\n const causeName = cause.name;\n const uuidIdx = causeName.indexOf(\"/uuid:\");\n if (uuidIdx === -1) {\n const causeMsg = \"message\" in cause && typeof cause.message === \"string\" ? cause.message : \"\";\n throw new Error(\n `QuickJS native error: ${causeName}: ${causeMsg}, ${stringifyWithResourceId(quickJSError)}`,\n { cause: quickJSError },\n );\n }\n const errorId = causeName.slice(uuidIdx + \"/uuid:\".length);\n if (!errorId) {\n throw new Error(\n `ErrorRepo: quickJSError.cause.name does not contain errorId: ${causeName}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n const error = this.errorIdToError.get(errorId);\n if (error === undefined) {\n throw new Error(\n `ErrorRepo: errorId not found: ${errorId}, ${stringifyWithResourceId(quickJSError)}`,\n );\n }\n\n return new PlQuickJSError(quickJSError, error as Error);\n }\n}\n"],"mappings":";;;;;;;;AAwBA,SAAS,oBAAoB,KAAsC;AACjE,QAAO,eAAe,eAAe,YAAY,OAAO,IAAI;;AAoC9D,IAAa,qBAAb,MAAa,mBAAmB;CAC9B;CACA;CACA;CAEA,YAA4B,IAAI,iBAAiB;CAEjD;CAEA,QAAqC;EACnC,cAAc;EACd,aAAa;EACb,YAAY;EACZ,eAAe;EACf,SAAS;EACT,YAAY;EACZ,YAAY;EACZ,UAAU;EACV,aAAa;EACb,aAAa;EACb,gBAAgB;EAChB,aAAa;EACd;;;;;;;;;;CAWD,YACE,OACA,IACA,gBACA,cACA,eACA;AALgB,OAAA,QAAA;AACA,OAAA,KAAA;AACC,OAAA,iBAAA;AAIjB,OAAK,mBAAmB,KAAK,MAAM,OAAO,KAAK,GAAG,WAAW,CAAC;AAE9D,OAAK,kBAAkB,MAAM,OAC3B,GAAG,QAAQ,GAAG,QAAQ,OAAO,CAAC,SAAS,SAAS,GAAG,QAAQ,MAAM,YAAY,CAAC,CAC/E;AACD,MAAI,GAAG,OAAO,KAAK,gBAAgB,KAAK,WACtC,OAAM,IAAI,MAAM,8BAA8B;AAEhD,OAAK,cAAc,MAAM,OACvB,GAAG,QAAQ,GAAG,QAAQ,OAAO,CAAC,SAAS,SAAS,GAAG,QAAQ,MAAM,QAAQ,CAAC,CAC3E;AACD,MAAI,GAAG,OAAO,KAAK,YAAY,KAAK,WAAY,OAAM,IAAI,MAAM,0BAA0B;AAE1F,MAAI,kBAAkB,KAAA,EACpB,MAAK,mBAAmB,IAAI,wBAC1B,MACA,cAAc,UACd,cAAc,OACd,cACA,cAAc,cACf;AAEH,OAAK,WAAW;;CAGlB,qBAA4B;AAC1B,WACE,KAAK,kBACL,+CACD,CAAC,oBAAoB;;CAGxB,OAAe,kBAAkB,OAAsB;AACrD,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,aAAa,MAAO,QAAO,MAAM;;CAatF,eAAsB,MAAc;EAClC,MAAM,KAAK,YAAY,KAAK;AAC5B,MAAI;AACF,QAAK,eAAe;IAClB,wBAAwB;IACxB,UAAU,KAAK,KAAK,GAAG;IACxB,CAAC;AACF,QAAK,GAAG,aAAa,KAAK,GAAG,SAAS,MAAM,aAAa,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,SAAS;WAChF,KAAc;AACrB,sBAAmB,kBAAkB,IAAI;AACzC,SAAM;YACE;AACR,QAAK,eAAe,KAAA,EAAU;AAC9B,QAAK,MAAM,gBAAgB,YAAY,KAAK,GAAG;AAC/C,QAAK,MAAM,eAAe,KAAK;;;CAInC,YAAmB,QAAgB,GAAG,MAAgC;EACpE,MAAM,KAAK,YAAY,KAAK;AAC5B,MAAI;AACF,QAAK,eAAe;IAAE,wBAAwB;IAAQ,UAAU,KAAK,KAAK,GAAG;IAAO,CAAC;AACrF,UAAO,MAAM,WAAW,eAAe;IACrC,MAAM,iBAAiB,WAAW,OAAO,KAAK,GAAG,QAAQ,KAAK,kBAAkB,OAAO,CAAC;AAExF,QAAI,KAAK,GAAG,OAAO,eAAe,KAAK,WACrC,OAAM,IAAI,MAAM,qBAAqB,SAAS;AAEhD,WAAO,KAAK,MAAM,OAChB,KAAK,GAAG,aACN,KAAK,GAAG,aACN,gBACA,KAAK,GAAG,WACR,GAAG,KAAK,KAAK,QAAQ,KAAK,sBAAsB,KAAK,WAAW,CAAC,CAClE,CACF,CACF;KACD;WACK,KAAc;AACrB,sBAAmB,kBAAkB,IAAI;AAEzC,SADiB,KAAK,UAAU,YAAY,IAAI;YAExC;AACR,QAAK,eAAe,KAAA,EAAU;AAC9B,QAAK,MAAM,cAAc,YAAY,KAAK,GAAG;AAC7C,QAAK,MAAM;;;CAQf,kBACE,KACA,OACe;EACf,MAAM,SAAS,KAAK,qBAAqB,KAAK,MAAM;AACpD,MAAI,WAAW,KAAA,EACb,OAAM,IAAI,MACR,uBAAuB,QAAQ,KAAA,IAAY,cAAc,KAAK,UAAU,IAAI,GAC7E;AAEH,SAAO;;CAGT,qBAA4B,KAAc,OAA0C;EAClF,IAAI;EACJ,IAAI,SAAS;AACb,UAAQ,OAAO,KAAf;GACE,KAAK;AACH,aAAS,KAAK,GAAG,UAAU,IAAI;AAC/B,aAAS;AACT;GACF,KAAK;AACH,aAAS,KAAK,GAAG,UAAU,IAAI;AAC/B,aAAS;AACT;GACF,KAAK;AACH,aAAS,KAAK,GAAG;AACjB;GACF,KAAK;AACH,aAAS,MAAM,KAAK,GAAG,OAAO,KAAK,GAAG;AACtC;GACF;AACE,QAAI,QAAQ,MAAM;AAChB,cAAS,KAAK,GAAG;AACjB;;AAEF,QAAI,oBAAoB,IAAI,EAAE;AAC5B,cAAS,KAAK,GAAG,eAAe,IAAI;AACpC,cAAS;AACT;;AAEF;;AAEJ,SAAO,UAAU,SAAS,KAAA,IAAY,MAAM,OAAO,OAAO,GAAG;;CAG/D,sBAA6B,KAAc,OAA8B;EACvE,MAAM,eAAe,KAAK,qBAAqB,KAAK,MAAM;AAC1D,MAAI,iBAAiB,KAAA,EAAW,QAAO;AACvC,SAAO,KAAK,oBAAoB,KAAK,MAAM;;CAG7C,oBAA2B,KAAc,OAA8B;EACrE,MAAM,KAAK,YAAY,KAAK;EAC5B,MAAM,OAAO,KAAK,UAAU,IAAI;AAChC,OAAK,MAAM,cAAc,KAAK;AAC9B,OAAK,MAAM;EACX,MAAM,SAAS,KAAK,GACjB,UAAU,KAAK,CACf,SAAS,eACR,KAAK,GAAG,aAAa,KAAK,GAAG,aAAa,KAAK,aAAa,KAAK,GAAG,WAAW,WAAW,CAAC,CAC5F;AACH,OAAK,MAAM,WAAW,YAAY,KAAK,GAAG;AAC1C,SAAO,UAAU,KAAA,IAAY,MAAM,OAAO,OAAO,GAAG;;CAGtD,sBAA6B,QAA4C;AACvE,MAAI,WAAW,KAAA,EAAW,QAAO,KAAA;AACjC,UAAQ,KAAK,GAAG,OAAO,OAAO,EAA9B;GACE,KAAK,YACH;GACF,KAAK;GACL,KAAK;GACL,KAAK,SACH,QAAO,KAAK,GAAG,KAAK,OAAO;GAC7B,QACE,QAAO,KAAK,oBAAoB,OAAO;;;CAI7C,oBAA2B,QAAgC;EACzD,MAAM,KAAK,YAAY,KAAK;EAC5B,MAAM,OAAO,KAAK,GACf,aAAa,KAAK,GAAG,aAAa,KAAK,iBAAiB,KAAK,GAAG,WAAW,OAAO,CAAC,CACnF,SAAS,cAAc,KAAK,GAAG,UAAU,UAAU,CAAC;AACvD,OAAK,MAAM,eAAe,KAAK;AAC/B,OAAK,MAAM;AACX,MAAI,SAAS,aAAa;AAExB,QAAK,MAAM,YAAY,YAAY,KAAK,GAAG;AAC3C;;EAEF,MAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,OAAK,MAAM,YAAY,YAAY,KAAK,GAAG;AAC3C,SAAO;;CAGT,YAAoB;AAClB,QAAM,WAAW,eAAe;GAC9B,MAAM,YAAY,WAAW,OAAO,KAAK,GAAG,WAAW,CAAC;AAMxD,QAAK,GAAG,QAAQ,WAAW,oBAAoB,KAAK,iBAAiB;AACrE,QAAK,GAAG,QACN,WACA,gBACA,KAAK,sBAAsB,iBAAiB,gCAAgC,WAAW,CACxF;AAGD,OAAI,KAAK,qBAAqB,KAAA,EAC5B,MAAK,iBAAiB,UAAU,UAAU;AAO5C,QAAK,GAAG,QAAQ,KAAK,GAAG,QAAQ,gBAAgB,UAAU;IAC1D;;;;;;AAON,IAAa,kBAAb,MAA6B;CAC3B,iCAAkC,IAAI,KAAsB;;CAG5D,yBAAgC,OAG9B;EACA,MAAM,UAAU,YAAY;AAC5B,OAAK,eAAe,IAAI,SAAS,MAAM;AAEvC,MAAI,iBAAiB,MACnB,QAAO;GACL,MAAM,GAAG,MAAM,KAAK,QAAQ;GAC5B,SAAS,MAAM;GAChB;AAGH,SAAO;GACL,MAAM,4BAA4B;GAClC,SAAS,GAAG;GACb;;;CAIH,YAAmB,cAAgC;AACjD,MAAI,EAAE,wBAAwB,OAAO,qBAAqB;AACxD,WAAQ,KACN,uDACA,wBAAwB,aAAa,CACtC;AACD,UAAO;;EAGT,MAAM,QAAQ,aAAa;AAC3B,MACE,EACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,OAAO,MAAM,SAAS,WAExB;AACA,WAAQ,KACN,+EACA,wBAAwB,aAAa,CACtC;AACD,UAAO;;EAGT,MAAM,YAAY,MAAM;EACxB,MAAM,UAAU,UAAU,QAAQ,SAAS;AAC3C,MAAI,YAAY,IAAI;GAClB,MAAM,WAAW,aAAa,SAAS,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU;AAC3F,SAAM,IAAI,MACR,yBAAyB,UAAU,IAAI,SAAS,IAAI,wBAAwB,aAAa,IACzF,EAAE,OAAO,cAAc,CACxB;;EAEH,MAAM,UAAU,UAAU,MAAM,UAAU,EAAgB;AAC1D,MAAI,CAAC,QACH,OAAM,IAAI,MACR,gEAAgE,UAAU,IAAI,wBAAwB,aAAa,GACpH;EAGH,MAAM,QAAQ,KAAK,eAAe,IAAI,QAAQ;AAC9C,MAAI,UAAU,KAAA,EACZ,OAAM,IAAI,MACR,iCAAiC,QAAQ,IAAI,wBAAwB,aAAa,GACnF;AAGH,SAAO,IAAI,eAAe,cAAc,MAAe"}
@@ -1,5 +1,6 @@
1
1
  require("../_virtual/_rolldown/runtime.cjs");
2
- const require_index = require("../js_render/index.cjs");
2
+ const require_index = require("../debug/index.cjs");
3
+ const require_index$1 = require("../js_render/index.cjs");
3
4
  let _platforma_sdk_model = require("@platforma-sdk/model");
4
5
  let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
5
6
  let lru_cache = require("lru-cache");
@@ -29,7 +30,7 @@ var ProjectHelper = class {
29
30
  deriveArgsFromStorage(blockConfig, storageJson) {
30
31
  if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) return { error: /* @__PURE__ */ new Error("deriveArgsFromStorage is only supported for model API version 2") };
31
32
  try {
32
- const result = require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.ArgsDerive], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), storageJson);
33
+ const result = require_index$1.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.ArgsDerive], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), storageJson);
33
34
  if (result.error !== void 0) return { error: new Error(result.error) };
34
35
  return { value: result.value };
35
36
  } catch (e) {
@@ -47,7 +48,7 @@ var ProjectHelper = class {
47
48
  derivePrerunArgsFromStorage(blockConfig, storageJson) {
48
49
  if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) throw new Error("derivePrerunArgsFromStorage is only supported for model API version 2");
49
50
  try {
50
- const result = require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.PrerunArgsDerive], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), storageJson);
51
+ const result = require_index$1.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.PrerunArgsDerive], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), storageJson);
51
52
  if (result.error !== void 0) return;
52
53
  return result.value;
53
54
  } catch {
@@ -58,7 +59,7 @@ var ProjectHelper = class {
58
59
  const blockConfig = req.blockConfig();
59
60
  if (blockConfig.enrichmentTargets === void 0) return void 0;
60
61
  const args = req.args();
61
- return require_index.executeSingleLambda(this.quickJs, blockConfig.enrichmentTargets, (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), args);
62
+ return require_index$1.executeSingleLambda(this.quickJs, blockConfig.enrichmentTargets, (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), args);
62
63
  }
63
64
  getEnrichmentTargets(blockConfig, args, key) {
64
65
  const req = {
@@ -82,7 +83,7 @@ var ProjectHelper = class {
82
83
  getInitialStorageInVM(blockConfig) {
83
84
  if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) throw new Error("getInitialStorageInVM is only supported for model API version 2");
84
85
  try {
85
- return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageInitial], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig));
86
+ return require_index$1.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageInitial], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig));
86
87
  } catch (e) {
87
88
  this.logger.error(new Error("[ProjectHelper.getInitialStorageInVM] Initial storage creation failed", { cause: e }));
88
89
  throw new Error(`Block initial storage creation failed: ${e}`);
@@ -103,10 +104,12 @@ var ProjectHelper = class {
103
104
  */
104
105
  applyStorageUpdateInVM(blockConfig, currentStorageJson, payload) {
105
106
  if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) throw new Error("applyStorageUpdateInVM is only supported for model API version 2");
107
+ if (require_index.getDebugFlags().logJsExecStat) this.logger.info(`[ProjectHelper.applyStorageUpdateInVM] currentStorageJson=${currentStorageJson.length}B, payload=${JSON.stringify(payload).length}B, operation=${payload.operation}`);
106
108
  try {
107
- return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageApplyUpdate], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), currentStorageJson, payload);
109
+ return require_index$1.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageApplyUpdate], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), currentStorageJson, payload);
108
110
  } catch (e) {
109
- this.logger.error(new Error("[ProjectHelper.applyStorageUpdateInVM] Storage update failed", { cause: e }));
111
+ const payloadJson = JSON.stringify(payload);
112
+ this.logger.error(new Error(`[ProjectHelper.applyStorageUpdateInVM] Storage update failed (currentStorageJson=${currentStorageJson.length}B, payload=${payloadJson.length}B, operation=${payload.operation})`, { cause: e }));
110
113
  throw new Error(`Block storage update failed: ${e}`);
111
114
  }
112
115
  }
@@ -121,7 +124,7 @@ var ProjectHelper = class {
121
124
  getStorageDebugViewInVM(blockConfig, rawStorageJson) {
122
125
  if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) throw new Error("getStorageDebugViewInVM is only supported for model API version 2");
123
126
  try {
124
- return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageDebugView], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), rawStorageJson);
127
+ return require_index$1.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageDebugView], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), rawStorageJson);
125
128
  } catch (e) {
126
129
  this.logger.error(new Error("[ProjectHelper.getStorageDebugViewInVM] Get storage debug view failed", { cause: e }));
127
130
  return;
@@ -145,7 +148,7 @@ var ProjectHelper = class {
145
148
  migrateStorageInVM(blockConfig, currentStorageJson) {
146
149
  if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) return { error: "migrateStorageInVM is only supported for model API version 2" };
147
150
  try {
148
- return require_index.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageMigrate], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), currentStorageJson);
151
+ return require_index$1.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.StorageMigrate], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), currentStorageJson);
149
152
  } catch (e) {
150
153
  this.logger.error(new Error("[ProjectHelper.migrateStorageInVM] Migration failed", { cause: e }));
151
154
  return { error: `VM execution failed: ${e}` };
@@ -1 +1 @@
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 { ResultOrError, BlockConfig, BlockStorage, PlRef } 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 { SignedResourceId } from \"@milaboratories/pl-client\";\nimport { ConsoleLoggerAdapter, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { StorageDebugView } from \"@milaboratories/pl-model-middle-layer\";\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: SignedResourceId; blockPackRid: SignedResourceId },\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
+ {"version":3,"file":"project_helper.cjs","names":["LRUCache","ConsoleLoggerAdapter","BLOCK_STORAGE_FACADE_VERSION","executeSingleLambda","BlockStorageFacadeCallbacks","getDebugFlags"],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type { ResultOrError, BlockConfig, BlockStorage, PlRef } 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 { SignedResourceId } from \"@milaboratories/pl-client\";\nimport { ConsoleLoggerAdapter, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { StorageDebugView } from \"@milaboratories/pl-model-middle-layer\";\nimport { getDebugFlags } from \"../debug\";\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: SignedResourceId; blockPackRid: SignedResourceId },\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 if (getDebugFlags().logJsExecStat) {\n this.logger.info(\n `[ProjectHelper.applyStorageUpdateInVM] currentStorageJson=${currentStorageJson.length}B, payload=${JSON.stringify(payload).length}B, operation=${payload.operation}`,\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 const payloadJson = JSON.stringify(payload);\n this.logger.error(\n new Error(\n `[ProjectHelper.applyStorageUpdateInVM] Storage update failed (currentStorageJson=${currentStorageJson.length}B, payload=${payloadJson.length}B, operation=${payload.operation})`,\n { cause: e },\n ),\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 +1 @@
1
- {"version":3,"file":"project_helper.d.ts","names":[],"sources":["../../src/model/project_helper.ts"],"mappings":";;;;;;;;;;AA+BA;;;;;KAAY,eAAA;EACN,KAAA;AAAA;EACA,KAAA;EAAmB,cAAA,EAAgB,eAAA,CAAgB,YAAA;EAAe,IAAA;AAAA;AAAA,cAQ3D,aAAA;EAAA,iBAaQ,OAAA;EAAA,SACD,MAAA,EAAQ,QAAA;EAAA,iBAbT,sBAAA;cAYE,OAAA,EAAS,iBAAA,EACV,MAAA,GAAQ,QAAA;EAAA;;;;;;;;;;;EAkBnB,qBAAA,CACL,WAAA,EAAa,WAAA,EACb,WAAA,WACC,aAAA;EAsKA;;;;;;;;EAtII,2BAAA,CAA4B,WAAA,EAAa,WAAA,EAAa,WAAA;EAAA,QAwBrD,0BAAA;EAaD,oBAAA,CACL,WAAA,QAAmB,WAAA,EACnB,IAAA,iBACA,GAAA;IAAQ,OAAA,EAAS,gBAAA;IAAkB,YAAA,EAAc,gBAAA;EAAA,IAChD,KAAA;EA5EI;;;;;;;;;;EAiGA,qBAAA,CAAsB,WAAA,EAAa,WAAA;EAxBrB;;;;;;;;;;;;;EA2Dd,sBAAA,CACL,WAAA,EAAa,WAAA,EACb,kBAAA,UACA,OAAA;IAAW,SAAA;IAAmB,KAAA;EAAA;EAAA;;;;;;;;EA+BzB,uBAAA,CACL,WAAA,EAAa,WAAA,EACb,cAAA,uBACC,eAAA,CAAgB,gBAAA;EA2CJ;;;;;;;;;;;;;;;EADR,kBAAA,CACL,WAAA,EAAa,WAAA,EACb,kBAAA,uBACC,eAAA;AAAA"}
1
+ {"version":3,"file":"project_helper.d.ts","names":[],"sources":["../../src/model/project_helper.ts"],"mappings":";;;;;;;;;;AAgCA;;;;;KAAY,eAAA;EACN,KAAA;AAAA;EACA,KAAA;EAAmB,cAAA,EAAgB,eAAA,CAAgB,YAAA;EAAe,IAAA;AAAA;AAAA,cAQ3D,aAAA;EAAA,iBAaQ,OAAA;EAAA,SACD,MAAA,EAAQ,QAAA;EAAA,iBAbT,sBAAA;cAYE,OAAA,EAAS,iBAAA,EACV,MAAA,GAAQ,QAAA;EAAA;;;;;;;;;;;EAkBnB,qBAAA,CACL,WAAA,EAAa,WAAA,EACb,WAAA,WACC,aAAA;EA+KA;;;;;;;;EA/II,2BAAA,CAA4B,WAAA,EAAa,WAAA,EAAa,WAAA;EAAA,QAwBrD,0BAAA;EAaD,oBAAA,CACL,WAAA,QAAmB,WAAA,EACnB,IAAA,iBACA,GAAA;IAAQ,OAAA,EAAS,gBAAA;IAAkB,YAAA,EAAc,gBAAA;EAAA,IAChD,KAAA;EA5EI;;;;;;;;;;EAiGA,qBAAA,CAAsB,WAAA,EAAa,WAAA;EAxBrB;;;;;;;;;;;;;EA2Dd,sBAAA,CACL,WAAA,EAAa,WAAA,EACb,kBAAA,UACA,OAAA;IAAW,SAAA;IAAmB,KAAA;EAAA;EAAA;;;;;;;;EAwCzB,uBAAA,CACL,WAAA,EAAa,WAAA,EACb,cAAA,uBACC,eAAA,CAAgB,gBAAA;EA2CJ;;;;;;;;;;;;;;;EADR,kBAAA,CACL,WAAA,EAAa,WAAA,EACb,kBAAA,uBACC,eAAA;AAAA"}
@@ -1,3 +1,4 @@
1
+ import { getDebugFlags } from "../debug/index.js";
1
2
  import { executeSingleLambda } from "../js_render/index.js";
2
3
  import { BLOCK_STORAGE_FACADE_VERSION, BlockStorageFacadeCallbacks, ensureError, extractCodeWithInfo } from "@platforma-sdk/model";
3
4
  import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
@@ -102,10 +103,12 @@ var ProjectHelper = class {
102
103
  */
103
104
  applyStorageUpdateInVM(blockConfig, currentStorageJson, payload) {
104
105
  if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) throw new Error("applyStorageUpdateInVM is only supported for model API version 2");
106
+ if (getDebugFlags().logJsExecStat) this.logger.info(`[ProjectHelper.applyStorageUpdateInVM] currentStorageJson=${currentStorageJson.length}B, payload=${JSON.stringify(payload).length}B, operation=${payload.operation}`);
105
107
  try {
106
108
  return executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageApplyUpdate], extractCodeWithInfo(blockConfig), currentStorageJson, payload);
107
109
  } catch (e) {
108
- this.logger.error(new Error("[ProjectHelper.applyStorageUpdateInVM] Storage update failed", { cause: e }));
110
+ const payloadJson = JSON.stringify(payload);
111
+ this.logger.error(new Error(`[ProjectHelper.applyStorageUpdateInVM] Storage update failed (currentStorageJson=${currentStorageJson.length}B, payload=${payloadJson.length}B, operation=${payload.operation})`, { cause: e }));
109
112
  throw new Error(`Block storage update failed: ${e}`);
110
113
  }
111
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"project_helper.js","names":[],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type { ResultOrError, BlockConfig, BlockStorage, PlRef } 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 { SignedResourceId } from \"@milaboratories/pl-client\";\nimport { ConsoleLoggerAdapter, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { StorageDebugView } from \"@milaboratories/pl-model-middle-layer\";\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: SignedResourceId; blockPackRid: SignedResourceId },\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
+ {"version":3,"file":"project_helper.js","names":[],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type { ResultOrError, BlockConfig, BlockStorage, PlRef } 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 { SignedResourceId } from \"@milaboratories/pl-client\";\nimport { ConsoleLoggerAdapter, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { StorageDebugView } from \"@milaboratories/pl-model-middle-layer\";\nimport { getDebugFlags } from \"../debug\";\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: SignedResourceId; blockPackRid: SignedResourceId },\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 if (getDebugFlags().logJsExecStat) {\n this.logger.info(\n `[ProjectHelper.applyStorageUpdateInVM] currentStorageJson=${currentStorageJson.length}B, payload=${JSON.stringify(payload).length}B, operation=${payload.operation}`,\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 const payloadJson = JSON.stringify(payload);\n this.logger.error(\n new Error(\n `[ProjectHelper.applyStorageUpdateInVM] Storage update failed (currentStorageJson=${currentStorageJson.length}B, payload=${payloadJson.length}B, operation=${payload.operation})`,\n { cause: e },\n ),\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-middle-layer",
3
- "version": "1.60.0",
3
+ "version": "1.60.2",
4
4
  "description": "Pl Middle Layer",
5
5
  "keywords": [],
6
6
  "license": "UNLICENSED",
@@ -30,24 +30,24 @@
30
30
  "utility-types": "^3.11.0",
31
31
  "yaml": "^2.8.0",
32
32
  "zod": "~3.25.76",
33
+ "@milaboratories/computable": "2.9.4",
33
34
  "@milaboratories/pf-spec-driver": "1.3.16",
34
35
  "@milaboratories/helpers": "1.14.2",
35
- "@milaboratories/computable": "2.9.4",
36
36
  "@milaboratories/pf-driver": "1.4.11",
37
37
  "@milaboratories/pl-client": "3.5.0",
38
- "@milaboratories/pl-deployments": "2.17.17",
39
- "@milaboratories/pl-drivers": "1.14.7",
38
+ "@milaboratories/pl-deployments": "2.17.18",
39
+ "@milaboratories/pl-http": "1.2.4",
40
40
  "@milaboratories/pl-errors": "1.4.7",
41
+ "@milaboratories/pl-model-common": "1.42.0",
42
+ "@milaboratories/pl-drivers": "1.14.7",
43
+ "@milaboratories/resolve-helper": "1.1.3",
41
44
  "@milaboratories/pl-model-backend": "1.2.29",
42
- "@milaboratories/pl-http": "1.2.4",
43
45
  "@milaboratories/pl-model-middle-layer": "1.19.4",
44
- "@milaboratories/pl-model-common": "1.42.0",
45
46
  "@milaboratories/pl-tree": "1.11.0",
46
- "@milaboratories/resolve-helper": "1.1.3",
47
47
  "@milaboratories/ts-helpers": "1.8.2",
48
48
  "@platforma-sdk/block-tools": "2.7.25",
49
- "@platforma-sdk/workflow-tengo": "5.23.0",
50
- "@platforma-sdk/model": "1.76.5"
49
+ "@platforma-sdk/model": "1.77.0",
50
+ "@platforma-sdk/workflow-tengo": "5.23.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "~24.5.2",
@@ -378,7 +378,15 @@ export class ErrorRepository {
378
378
  }
379
379
 
380
380
  const causeName = cause.name;
381
- const errorId = causeName.slice(causeName.indexOf("/uuid:") + "/uuid:".length);
381
+ const uuidIdx = causeName.indexOf("/uuid:");
382
+ if (uuidIdx === -1) {
383
+ const causeMsg = "message" in cause && typeof cause.message === "string" ? cause.message : "";
384
+ throw new Error(
385
+ `QuickJS native error: ${causeName}: ${causeMsg}, ${stringifyWithResourceId(quickJSError)}`,
386
+ { cause: quickJSError },
387
+ );
388
+ }
389
+ const errorId = causeName.slice(uuidIdx + "/uuid:".length);
382
390
  if (!errorId) {
383
391
  throw new Error(
384
392
  `ErrorRepo: quickJSError.cause.name does not contain errorId: ${causeName}, ${stringifyWithResourceId(quickJSError)}`,
@@ -12,6 +12,7 @@ import { executeSingleLambda } from "../js_render";
12
12
  import type { SignedResourceId } from "@milaboratories/pl-client";
13
13
  import { ConsoleLoggerAdapter, type MiLogger } from "@milaboratories/ts-helpers";
14
14
  import type { StorageDebugView } from "@milaboratories/pl-model-middle-layer";
15
+ import { getDebugFlags } from "../debug";
15
16
 
16
17
  type EnrichmentTargetsRequest = {
17
18
  blockConfig: () => BlockConfig;
@@ -212,6 +213,11 @@ export class ProjectHelper {
212
213
  throw new Error("applyStorageUpdateInVM is only supported for model API version 2");
213
214
  }
214
215
 
216
+ if (getDebugFlags().logJsExecStat) {
217
+ this.logger.info(
218
+ `[ProjectHelper.applyStorageUpdateInVM] currentStorageJson=${currentStorageJson.length}B, payload=${JSON.stringify(payload).length}B, operation=${payload.operation}`,
219
+ );
220
+ }
215
221
  try {
216
222
  const result = executeSingleLambda(
217
223
  this.quickJs,
@@ -222,8 +228,12 @@ export class ProjectHelper {
222
228
  ) as string;
223
229
  return result;
224
230
  } catch (e) {
231
+ const payloadJson = JSON.stringify(payload);
225
232
  this.logger.error(
226
- new Error("[ProjectHelper.applyStorageUpdateInVM] Storage update failed", { cause: e }),
233
+ new Error(
234
+ `[ProjectHelper.applyStorageUpdateInVM] Storage update failed (currentStorageJson=${currentStorageJson.length}B, payload=${payloadJson.length}B, operation=${payload.operation})`,
235
+ { cause: e },
236
+ ),
227
237
  );
228
238
  throw new Error(`Block storage update failed: ${e}`);
229
239
  }