@milaboratories/pl-middle-layer 1.74.18 → 1.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/middle_layer/block.cjs.map +1 -1
- package/dist/middle_layer/block.js.map +1 -1
- package/dist/middle_layer/build_stamp.cjs +1 -1
- package/dist/middle_layer/build_stamp.js +1 -1
- package/dist/middle_layer/column_links.cjs +106 -0
- package/dist/middle_layer/column_links.cjs.map +1 -0
- package/dist/middle_layer/column_links.js +104 -0
- package/dist/middle_layer/column_links.js.map +1 -0
- package/dist/middle_layer/middle_layer.cjs +1 -0
- package/dist/middle_layer/middle_layer.cjs.map +1 -1
- package/dist/middle_layer/middle_layer.js +1 -0
- package/dist/middle_layer/middle_layer.js.map +1 -1
- package/dist/middle_layer/project.cjs +45 -19
- package/dist/middle_layer/project.cjs.map +1 -1
- package/dist/middle_layer/project.d.ts.map +1 -1
- package/dist/middle_layer/project.js +46 -20
- package/dist/middle_layer/project.js.map +1 -1
- package/dist/middle_layer/project_overview.cjs +56 -24
- package/dist/middle_layer/project_overview.cjs.map +1 -1
- package/dist/middle_layer/project_overview.js +55 -25
- package/dist/middle_layer/project_overview.js.map +1 -1
- package/dist/model/args_derivation.cjs +153 -0
- package/dist/model/args_derivation.cjs.map +1 -0
- package/dist/model/args_derivation.d.ts +21 -0
- package/dist/model/args_derivation.d.ts.map +1 -0
- package/dist/model/args_derivation.js +148 -0
- package/dist/model/args_derivation.js.map +1 -0
- package/dist/model/project_helper.cjs +34 -8
- package/dist/model/project_helper.cjs.map +1 -1
- package/dist/model/project_helper.d.ts +18 -5
- package/dist/model/project_helper.d.ts.map +1 -1
- package/dist/model/project_helper.js +34 -8
- package/dist/model/project_helper.js.map +1 -1
- package/dist/model/project_model.cjs +1 -0
- package/dist/model/project_model.cjs.map +1 -1
- package/dist/model/project_model.d.ts +1 -1
- package/dist/model/project_model.js +1 -0
- package/dist/model/project_model.js.map +1 -1
- package/dist/mutator/project.cjs +54 -8
- package/dist/mutator/project.cjs.map +1 -1
- package/dist/mutator/project.d.ts +1 -0
- package/dist/mutator/project.d.ts.map +1 -1
- package/dist/mutator/project.js +54 -8
- package/dist/mutator/project.js.map +1 -1
- package/package.json +16 -16
- package/src/middle_layer/block.ts +4 -1
- package/src/middle_layer/column_links.test.ts +350 -0
- package/src/middle_layer/column_links.ts +152 -0
- package/src/middle_layer/middle_layer.ts +2 -0
- package/src/middle_layer/project.ts +39 -5
- package/src/middle_layer/project_overview.test.ts +200 -0
- package/src/middle_layer/project_overview.ts +82 -28
- package/src/model/args.test.ts +39 -1
- package/src/model/args_derivation.test.ts +326 -0
- package/src/model/args_derivation.ts +194 -0
- package/src/model/project_helper.ts +55 -25
- package/src/model/project_model.ts +2 -0
- package/src/mutator/project-v3.test.ts +105 -1
- package/src/mutator/project.ts +81 -24
- package/src/test/fake_project.ts +222 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
const require_args_derivation = require("./args_derivation.cjs");
|
|
1
2
|
const require_index = require("../debug/index.cjs");
|
|
2
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");
|
|
6
|
+
let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
|
|
5
7
|
let lru_cache = require("lru-cache");
|
|
6
8
|
// src/model/project_helper.ts
|
|
7
9
|
var ProjectHelper = class {
|
|
@@ -13,38 +15,47 @@ var ProjectHelper = class {
|
|
|
13
15
|
return { value: this.calculateEnrichmentTargets(context) };
|
|
14
16
|
}
|
|
15
17
|
});
|
|
18
|
+
columnReferencesCache = new lru_cache.LRUCache({ max: 1024 });
|
|
16
19
|
constructor(quickJs, logger = new _milaboratories_ts_helpers.ConsoleLoggerAdapter()) {
|
|
17
20
|
this.quickJs = quickJs;
|
|
18
21
|
this.logger = logger;
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* Derives args directly from storage JSON using VM callback.
|
|
22
|
-
* The VM extracts data from storage
|
|
25
|
+
* The VM extracts data from storage, calls the block's args() function and then its argsIssues().
|
|
23
26
|
*
|
|
24
27
|
* This allows the middle layer to work only with storage JSON,
|
|
25
28
|
* without needing to know the underlying data structure.
|
|
26
29
|
*
|
|
27
30
|
* @param blockConfig The block configuration (provides the model code)
|
|
28
31
|
* @param storageJson Storage as JSON string
|
|
29
|
-
* @returns The derived args
|
|
32
|
+
* @returns The derived args and whether the block may run with them, or why there are none
|
|
30
33
|
*/
|
|
31
34
|
deriveArgsFromStorage(blockConfig, storageJson) {
|
|
32
|
-
if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) return {
|
|
35
|
+
if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) return {
|
|
36
|
+
type: "absent",
|
|
37
|
+
error: /* @__PURE__ */ new Error("deriveArgsFromStorage is only supported for model API version 2")
|
|
38
|
+
};
|
|
33
39
|
try {
|
|
34
|
-
|
|
35
|
-
if (result.error !== void 0) return { error: new Error(result.error) };
|
|
36
|
-
return { value: result.value };
|
|
40
|
+
return require_args_derivation.parseArgsDeriveResult(require_index$1.executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[_platforma_sdk_model.BlockStorageFacadeCallbacks.ArgsDerive], (0, _platforma_sdk_model.extractCodeWithInfo)(blockConfig), storageJson));
|
|
37
41
|
} catch (e) {
|
|
38
|
-
return {
|
|
42
|
+
return {
|
|
43
|
+
type: "absent",
|
|
44
|
+
error: new Error("Args derivation from storage failed", { cause: (0, _platforma_sdk_model.ensureError)(e) })
|
|
45
|
+
};
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
42
49
|
* Derives prerunArgs directly from storage JSON using VM callback.
|
|
43
50
|
* Falls back to args() if prerunArgs is not defined in the block model.
|
|
44
51
|
*
|
|
52
|
+
* Errors the block's `prerunArgsIssues()` finds in its prerun args, or for a block without its
|
|
53
|
+
* own prerunArgs, the errors its `argsIssues()` finds in its args, come back as an `error` and
|
|
54
|
+
* skip staging the same way.
|
|
55
|
+
*
|
|
45
56
|
* @param blockConfig The block configuration (provides the model code)
|
|
46
57
|
* @param storageJson Storage as JSON string
|
|
47
|
-
* @returns The derived prerunArgs, or undefined if derivation fails
|
|
58
|
+
* @returns The derived prerunArgs, or undefined if derivation fails or found errors
|
|
48
59
|
*/
|
|
49
60
|
derivePrerunArgsFromStorage(blockConfig, storageJson) {
|
|
50
61
|
if (blockConfig.modelAPIVersion !== _platforma_sdk_model.BLOCK_STORAGE_FACADE_VERSION) throw new Error("derivePrerunArgsFromStorage is only supported for model API version 2");
|
|
@@ -110,6 +121,21 @@ var ProjectHelper = class {
|
|
|
110
121
|
return this.enrichmentTargetsCache.memo(cacheKey, { context: req }).value;
|
|
111
122
|
}
|
|
112
123
|
/**
|
|
124
|
+
* Columns referenced by a block's args, with the values of them the args picked, read from the
|
|
125
|
+
* args resource `argsRid`. `undefined` while the resource data is not loaded yet: `args`
|
|
126
|
+
* returns `undefined` then, and nothing is remembered for such a read, so the references are
|
|
127
|
+
* found once the data arrives.
|
|
128
|
+
*/
|
|
129
|
+
getColumnReferences(argsRid, args) {
|
|
130
|
+
const cached = this.columnReferencesCache.get(argsRid);
|
|
131
|
+
if (cached !== void 0) return cached;
|
|
132
|
+
const data = args();
|
|
133
|
+
if (data === void 0) return void 0;
|
|
134
|
+
const references = (0, _milaboratories_pl_model_common.collectColumnReferences)(data);
|
|
135
|
+
this.columnReferencesCache.set(argsRid, references);
|
|
136
|
+
return references;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
113
139
|
* Creates initial BlockStorage for a new block using VM-based transformation.
|
|
114
140
|
* This calls the '__pl_storage_initial' callback registered by DataModel which:
|
|
115
141
|
* - Gets initial data from DataModel.getDefaultData()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project_helper.cjs","names":["LRUCache","ConsoleLoggerAdapter","BLOCK_STORAGE_FACADE_VERSION","executeSingleLambda","BlockStorageFacadeCallbacks","extractCodeWithInfo","ensureError","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 { 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\n/**\n * Result of building initial storage from params.\n * Returned by the __pl_storage_initialFromParams VM callback.\n */\ntype ParamsStorageResult =\n | { error: string }\n | { error?: undefined; storageJson: StringifiedJson<BlockStorage> };\n\n/**\n * Result of checking params against their kind.\n * Returned by the __pl_initializationParams_validate VM callback.\n */\ntype InitializationParamsValidateResult = { error: string } | { error?: undefined };\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 /**\n * Derives this block's template-export params from storage JSON using the VM\n * callback (`__pl_initializationParams_derive`).\n *\n * The template-export counterpart of {@link deriveArgsFromStorage}: instead of\n * the args a workflow runs on, it returns the params that would recreate the\n * block — the inverse of the data model's `init`. Params come back exactly as the block\n * projected them, references included: nothing marks or rewrites them on the way out, so\n * nothing here has to know a kind's params shape or how a reference is spelled.\n *\n * A `{ value: undefined }` result means the block declares no `templateParams`, which only\n * a block built against an older SDK can do. It does not reach a document: the export walk\n * requires a mapping and reports anything else as that block's problem, so an entry always\n * carries params.\n *\n * Unlike {@link derivePrerunArgsFromStorage}, a failure here is surfaced rather\n * than swallowed — a prerun that cannot derive args just skips a block in\n * staging, whereas an export that silently drops a block produces a template\n * that does not describe the project.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived params with identifiers wrapped, `undefined` if the block declares\n * no lambda, or an error if derivation failed\n */\n public deriveTemplateParamsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"deriveTemplateParamsFromStorage is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsDerive];\n\n // A model built before this callback existed simply has no entry for it. That is\n // NOT the same as a block declaring no `templateParams`: the block may well have\n // params, we just have no way to ask for them. Reporting it as `undefined` params\n // would export the block stripped of its configuration and quietly rebuild a\n // differently-configured project, so it has to be an error.\n // The message names the one action available to whoever pressed Export. It\n // deliberately says nothing about SDKs or callbacks: the person reading it did\n // not build this block and cannot change how it was built.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be written to a template. Update the block \" +\n \"to a newer version and export again.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\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 const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error is rendered\n // into a per-block export problem and shown to whoever triggered the export,\n // and every layer between here and there carries only `message`.\n return {\n error: new Error(`Template params derivation from storage failed: ${cause.message}`, {\n cause,\n }),\n };\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 * Checks a template entry's params against the block's kind, creating nothing.\n *\n * The pre-flight half of {@link getInitialStorageFromParamsInVM}: run once per entry\n * before a template is applied, so params a kind rejects are reported against the\n * entry that carries them while there is still no project. Skipping it is safe —\n * initialization runs the same check — but then the report arrives after earlier\n * entries have already been created.\n *\n * Every kind declares a parser, so a pass here means the params were checked against\n * the contract — not merely that they were JSON.\n *\n * A block whose model predates the callback passes unchecked rather than failing.\n * Unlike initialization, this method creates nothing, so there is nothing to get wrong\n * by proceeding, and such a block is refused outright at the point it is applied.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params. Reference ids may be placeholders: what is being\n * checked is the shape of the params, not what they point at\n * @returns Nothing, or why the params were rejected\n */\n public validateTemplateParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n ): ResultOrError<undefined> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"validateTemplateParamsInVM is only supported for model API version 2\"),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsValidate];\n if (callback === undefined) return { value: undefined };\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n JSON.stringify(params ?? {}),\n ) as InitializationParamsValidateResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: undefined };\n } catch (e) {\n const cause = ensureError(e);\n return { error: new Error(`Params check failed to run: ${cause.message}`, { cause }) };\n }\n }\n\n /**\n * Creates initial BlockStorage for a block being created from template params.\n *\n * The inverse of {@link deriveTemplateParamsFromStorage}, and the reason a block\n * can be created by anything other than the UI: it hands the params to the\n * block's own init factory inside the model VM, so the resulting storage is\n * whatever that block considers a correctly-initialized state.\n *\n * **Pointing the entry's references at this project happens in the same call**, before the\n * factory sees them — inside the block's bundle, because recognizing a reference means\n * knowing the reference system and a template engine deliberately does not. Params reach\n * here exactly as the file held them.\n *\n * One call and not two: every VM call builds a fresh runtime and re-evaluates the whole\n * model bundle, so asking the block to relocate and then asking it to initialize would parse\n * it twice per entry, for an intermediate value nothing else reads.\n *\n * `blockIds` holds the entries created so far, so an id it does not name is left as it is —\n * a reference to an entry further down the file keeps pointing at nothing, and the applied\n * block reports itself as missing references rather than being wired to a block below it.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params, as the document held them\n * @param blockIds template-local entry id → the block id that entry was given\n * @returns The initial storage as JSON string, or why the params yield none\n */\n public getInitialStorageFromParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n blockIds: ReadonlyMap<string, string>,\n ): ResultOrError<string> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"getInitialStorageFromParamsInVM is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitialFromParams];\n\n // A model built before this callback existed has no entry for it. Falling back to the\n // params-less initializer is not an option: it would produce a default-configured block\n // that looks like a successful apply, so the block the user gets would silently differ\n // from the one the template describes. Nor could its params be used as written — they name\n // the blocks of the project the template came from, and only this callback knows which of\n // the values in there are references at all.\n //\n // The message offers the two actions available to whoever applied the file. The\n // second one is the reason this branch is reachable at all: kind resolution only\n // ever returns a block that declares a kind, and such a block is new enough to\n // support this — but an entry may pin an exact block version instead, bypassing\n // resolution, and that pin can name anything ever published.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be created from a template. Use a newer \" +\n \"version of the block, or remove the pinned block version from the template \" +\n \"entry so a supported one is chosen automatically.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n // Params cross the VM boundary as text, like storage does. `undefined` would\n // stringify to nothing at all, and an entry with no params must go through\n // the params-less initializer rather than reaching this method.\n JSON.stringify(params ?? {}),\n JSON.stringify(Object.fromEntries(blockIds)),\n ) as ParamsStorageResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: result.storageJson };\n } catch (e) {\n const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error becomes a\n // per-entry apply problem shown to whoever triggered the import, and every\n // layer in between carries only `message`.\n return {\n error: new Error(`Initial storage creation from params failed: ${cause.message}`, {\n cause,\n }),\n };\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 // 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":";;;;;;AAuDA,IAAa,gBAAb,MAA2B;CAaN;CACD;CAblB,yBAA0C,IAAIA,UAAAA,SAI5C;EACA,KAAK;EACL,aAAa,MAAM,QAAQ,EAAE,cAAc;GACzC,OAAO,EAAE,OAAO,KAAK,2BAA2B,OAAO,EAAE;EAC3D;CACF,CAAC;CAED,YACE,SACA,SAAmC,IAAIC,2BAAAA,qBAAqB,GAC5D;EAFiB,KAAA,UAAA;EACD,KAAA,SAAA;CACf;;;;;;;;;;;;CAiBH,sBACE,aACA,aACwB;EACxB,IAAI,YAAY,oBAAoBC,qBAAAA,8BAClC,OAAO,EACL,uBAAO,IAAI,MAAM,iEAAiE,EACpF;EAGF,IAAI;GACF,MAAM,SAASC,gBAAAA,oBACb,KAAK,SACL,YAAY,wBAAwBC,qBAAAA,4BAA4B,cAAA,GAChEC,qBAAAA,oBAAAA,CAAoB,WAAW,GAC/B,WACF;GAEA,IAAI,OAAO,UAAU,KAAA,GACnB,OAAO,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK,EAAE;GAE1C,OAAO,EAAE,OAAO,OAAO,MAAM;EAC/B,SAAS,GAAG;GACV,OAAO,EAAE,OAAO,IAAI,MAAM,uCAAuC,EAAE,QAAA,GAAOC,qBAAAA,YAAAA,CAAY,CAAC,EAAE,CAAC,EAAE;EAC9F;CACF;;;;;;;;;CAUA,4BAAmC,aAA0B,aAA8B;EACzF,IAAI,YAAY,oBAAoBJ,qBAAAA,8BAClC,MAAM,IAAI,MAAM,uEAAuE;EAGzF,IAAI;GACF,MAAM,SAASC,gBAAAA,oBACb,KAAK,SACL,YAAY,wBAAwBC,qBAAAA,4BAA4B,oBAAA,GAChEC,qBAAAA,oBAAAA,CAAoB,WAAW,GAC/B,WACF;GAEA,IAAI,OAAO,UAAU,KAAA,GAEnB;GAEF,OAAO,OAAO;EAChB,QAAQ;GAEN;EACF;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BA,gCACE,aACA,aACwB;EACxB,IAAI,YAAY,oBAAoBH,qBAAAA,8BAClC,OAAO,EACL,uBAAO,IAAI,MACT,2EACF,EACF;EAGF,MAAM,WACJ,YAAY,wBAAwBE,qBAAAA,4BAA4B;EAUlE,IAAI,aAAa,KAAA,GACf,OAAO,EACL,uBAAO,IAAI,MACT,kHAEF,EACF;EAGF,IAAI;GACF,MAAM,SAASD,gBAAAA,oBACb,KAAK,SACL,WAAA,GACAE,qBAAAA,oBAAAA,CAAoB,WAAW,GAC/B,WACF;GAEA,IAAI,OAAO,UAAU,KAAA,GACnB,OAAO,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK,EAAE;GAE1C,OAAO,EAAE,OAAO,OAAO,MAAM;EAC/B,SAAS,GAAG;GACV,MAAM,SAAA,GAAQC,qBAAAA,YAAAA,CAAY,CAAC;GAI3B,OAAO,EACL,OAAO,IAAI,MAAM,mDAAmD,MAAM,WAAW,EACnF,MACF,CAAC,EACH;EACF;CACF;CAEA,2BAAmC,KAAoD;EACrF,MAAM,cAAc,IAAI,YAAY;EACpC,IAAI,YAAY,sBAAsB,KAAA,GAAW,OAAO,KAAA;EACxD,MAAM,OAAO,IAAI,KAAK;EAOtB,OANeH,gBAAAA,oBACb,KAAK,SACL,YAAY,oBAAA,GACZE,qBAAAA,oBAAAA,CAAoB,WAAW,GAC/B,IAEU;CACd;CAEA,qBACE,aACA,MACA,KACqB;EACrB,MAAM,MAAM;GAAE;GAAa;EAAK;EAChC,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAK,2BAA2B,GAAG;EACjE,MAAM,WAAW,GAAG,IAAI,QAAQ,GAAG,IAAI;EACvC,OAAO,KAAK,uBAAuB,KAAK,UAAU,EAAE,SAAS,IAAI,CAAC,CAAC,CAAC;CACtE;;;;;;;;;;;CAgBA,sBAA6B,aAAkC;EAC7D,IAAI,YAAY,oBAAoBH,qBAAAA,8BAClC,MAAM,IAAI,MAAM,iEAAiE;EAGnF,IAAI;GAMF,OALeC,gBAAAA,oBACb,KAAK,SACL,YAAY,wBAAwBC,qBAAAA,4BAA4B,kBAAA,GAChEC,qBAAAA,oBAAAA,CAAoB,WAAW,CAErB;EACd,SAAS,GAAG;GACV,KAAK,OAAO,MACV,IAAI,MAAM,yEAAyE,EACjF,OAAO,EACT,CAAC,CACH;GACA,MAAM,IAAI,MAAM,0CAA0C,GAAG;EAC/D;CACF;;;;;;;;;;;;;;;;;;;;;;CAuBA,2BACE,aACA,QAC0B;EAC1B,IAAI,YAAY,oBAAoBH,qBAAAA,8BAClC,OAAO,EACL,uBAAO,IAAI,MAAM,sEAAsE,EACzF;EAGF,MAAM,WACJ,YAAY,wBAAwBE,qBAAAA,4BAA4B;EAClE,IAAI,aAAa,KAAA,GAAW,OAAO,EAAE,OAAO,KAAA,EAAU;EAEtD,IAAI;GACF,MAAM,SAASD,gBAAAA,oBACb,KAAK,SACL,WAAA,GACAE,qBAAAA,oBAAAA,CAAoB,WAAW,GAC/B,KAAK,UAAU,UAAU,CAAC,CAAC,CAC7B;GAEA,IAAI,OAAO,UAAU,KAAA,GAAW,OAAO,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK,EAAE;GACxE,OAAO,EAAE,OAAO,KAAA,EAAU;EAC5B,SAAS,GAAG;GACV,MAAM,SAAA,GAAQC,qBAAAA,YAAAA,CAAY,CAAC;GAC3B,OAAO,EAAE,OAAO,IAAI,MAAM,+BAA+B,MAAM,WAAW,EAAE,MAAM,CAAC,EAAE;EACvF;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BA,gCACE,aACA,QACA,UACuB;EACvB,IAAI,YAAY,oBAAoBJ,qBAAAA,8BAClC,OAAO,EACL,uBAAO,IAAI,MACT,2EACF,EACF;EAGF,MAAM,WACJ,YAAY,wBAAwBE,qBAAAA,4BAA4B;EAclE,IAAI,aAAa,KAAA,GACf,OAAO,EACL,uBAAO,IAAI,MACT,uMAGF,EACF;EAGF,IAAI;GACF,MAAM,SAASD,gBAAAA,oBACb,KAAK,SACL,WAAA,GACAE,qBAAAA,oBAAAA,CAAoB,WAAW,GAI/B,KAAK,UAAU,UAAU,CAAC,CAAC,GAC3B,KAAK,UAAU,OAAO,YAAY,QAAQ,CAAC,CAC7C;GAEA,IAAI,OAAO,UAAU,KAAA,GAAW,OAAO,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK,EAAE;GACxE,OAAO,EAAE,OAAO,OAAO,YAAY;EACrC,SAAS,GAAG;GACV,MAAM,SAAA,GAAQC,qBAAAA,YAAAA,CAAY,CAAC;GAI3B,OAAO,EACL,OAAO,IAAI,MAAM,gDAAgD,MAAM,WAAW,EAChF,MACF,CAAC,EACH;EACF;CACF;;;;;;;;;;;;;;CAeA,uBACE,aACA,oBACA,SACQ;EACR,IAAI,YAAY,oBAAoBJ,qBAAAA,8BAClC,MAAM,IAAI,MAAM,kEAAkE;EAGpF,IAAIK,cAAAA,cAAc,CAAC,CAAC,eAClB,KAAK,OAAO,KACV,6DAA6D,mBAAmB,OAAO,aAAa,KAAK,UAAU,OAAO,CAAC,CAAC,OAAO,eAAe,QAAQ,WAC5J;EAEF,IAAI;GAQF,OAPeJ,gBAAAA,oBACb,KAAK,SACL,YAAY,wBAAwBC,qBAAAA,4BAA4B,sBAAA,GAChEC,qBAAAA,oBAAAA,CAAoB,WAAW,GAC/B,oBACA,OAEU;EACd,SAAS,GAAG;GACV,MAAM,cAAc,KAAK,UAAU,OAAO;GAC1C,KAAK,OAAO,MACV,IAAI,MACF,oFAAoF,mBAAmB,OAAO,aAAa,YAAY,OAAO,eAAe,QAAQ,UAAU,IAC/K,EAAE,OAAO,EAAE,CACb,CACF;GACA,MAAM,IAAI,MAAM,gCAAgC,GAAG;EACrD;CACF;;;;;;;;;;;;;;;;CAqBA,mBACE,aACA,oBACiB;EACjB,IAAI,YAAY,oBAAoBH,qBAAAA,8BAClC,OAAO,EAAE,OAAO,+DAA+D;EAGjF,IAAI;GAOF,OANeC,gBAAAA,oBACb,KAAK,SACL,YAAY,wBAAwBC,qBAAAA,4BAA4B,kBAAA,GAChEC,qBAAAA,oBAAAA,CAAoB,WAAW,GAC/B,kBAEU;EACd,SAAS,GAAG;GACV,KAAK,OAAO,MACV,IAAI,MAAM,uDAAuD,EAAE,OAAO,EAAE,CAAC,CAC/E;GACA,OAAO,EAAE,OAAO,wBAAwB,IAAI;EAC9C;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"project_helper.cjs","names":["LRUCache","ConsoleLoggerAdapter","BLOCK_STORAGE_FACADE_VERSION","parseArgsDeriveResult","executeSingleLambda","BlockStorageFacadeCallbacks","extractCodeWithInfo","ensureError","collectColumnReferences","getDebugFlags"],"sources":["../../src/model/project_helper.ts"],"sourcesContent":["import type { ResultOrError, BlockConfig, BlockStorage, PlRef } from \"@platforma-sdk/model\";\nimport type { ColumnReference, StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport { collectColumnReferences } 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 { getDebugFlags } from \"../debug\";\nimport { parseArgsDeriveResult, type DerivedArgs } from \"./args_derivation\";\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 a derivation from storage that either produced a value or failed.\n * Returned by the __pl_prerunArgs_derive and __pl_initializationParams_derive VM callbacks; a\n * newer model adds the issues found in the prerun args, which are not read here.\n */\ntype DeriveResult = { error: string } | { error?: undefined; value: unknown };\n\n/**\n * Result of building initial storage from params.\n * Returned by the __pl_storage_initialFromParams VM callback.\n */\ntype ParamsStorageResult =\n | { error: string }\n | { error?: undefined; storageJson: StringifiedJson<BlockStorage> };\n\n/**\n * Result of checking params against their kind.\n * Returned by the __pl_initializationParams_validate VM callback.\n */\ntype InitializationParamsValidateResult = { error: string } | { error?: undefined };\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 // An args resource never changes, so its id alone identifies the columns found in it.\n private readonly columnReferencesCache = new LRUCache<\n SignedResourceId,\n readonly ColumnReference[]\n >({ max: 1024 });\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, calls the block's args() function and then its argsIssues().\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 and whether the block may run with them, or why there are none\n */\n public deriveArgsFromStorage(blockConfig: BlockConfig, storageJson: string): DerivedArgs {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n type: \"absent\",\n error: new Error(\"deriveArgsFromStorage is only supported for model API version 2\"),\n };\n }\n\n try {\n return parseArgsDeriveResult(\n executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.ArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ),\n );\n } catch (e) {\n return {\n type: \"absent\",\n error: new Error(\"Args derivation from storage failed\", { cause: ensureError(e) }),\n };\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 * Errors the block's `prerunArgsIssues()` finds in its prerun args, or for a block without its\n * own prerunArgs, the errors its `argsIssues()` finds in its args, come back as an `error` and\n * skip staging the same way.\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 or found errors\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 DeriveResult;\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 /**\n * Derives this block's template-export params from storage JSON using the VM\n * callback (`__pl_initializationParams_derive`).\n *\n * The template-export counterpart of {@link deriveArgsFromStorage}: instead of\n * the args a workflow runs on, it returns the params that would recreate the\n * block — the inverse of the data model's `init`. Params come back exactly as the block\n * projected them, references included: nothing marks or rewrites them on the way out, so\n * nothing here has to know a kind's params shape or how a reference is spelled.\n *\n * A `{ value: undefined }` result means the block declares no `templateParams`, which only\n * a block built against an older SDK can do. It does not reach a document: the export walk\n * requires a mapping and reports anything else as that block's problem, so an entry always\n * carries params.\n *\n * Unlike {@link derivePrerunArgsFromStorage}, a failure here is surfaced rather\n * than swallowed — a prerun that cannot derive args just skips a block in\n * staging, whereas an export that silently drops a block produces a template\n * that does not describe the project.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived params with identifiers wrapped, `undefined` if the block declares\n * no lambda, or an error if derivation failed\n */\n public deriveTemplateParamsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"deriveTemplateParamsFromStorage is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsDerive];\n\n // A model built before this callback existed simply has no entry for it. That is\n // NOT the same as a block declaring no `templateParams`: the block may well have\n // params, we just have no way to ask for them. Reporting it as `undefined` params\n // would export the block stripped of its configuration and quietly rebuild a\n // differently-configured project, so it has to be an error.\n // The message names the one action available to whoever pressed Export. It\n // deliberately says nothing about SDKs or callbacks: the person reading it did\n // not build this block and cannot change how it was built.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be written to a template. Update the block \" +\n \"to a newer version and export again.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as DeriveResult;\n\n if (result.error !== undefined) {\n return { error: new Error(result.error) };\n }\n return { value: result.value };\n } catch (e) {\n const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error is rendered\n // into a per-block export problem and shown to whoever triggered the export,\n // and every layer between here and there carries only `message`.\n return {\n error: new Error(`Template params derivation from storage failed: ${cause.message}`, {\n cause,\n }),\n };\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 * Columns referenced by a block's args, with the values of them the args picked, read from the\n * args resource `argsRid`. `undefined` while the resource data is not loaded yet: `args`\n * returns `undefined` then, and nothing is remembered for such a read, so the references are\n * found once the data arrives.\n */\n public getColumnReferences(\n argsRid: SignedResourceId,\n args: () => unknown,\n ): undefined | readonly ColumnReference[] {\n const cached = this.columnReferencesCache.get(argsRid);\n if (cached !== undefined) return cached;\n const data = args();\n if (data === undefined) return undefined;\n const references = collectColumnReferences(data);\n this.columnReferencesCache.set(argsRid, references);\n return references;\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 * Checks a template entry's params against the block's kind, creating nothing.\n *\n * The pre-flight half of {@link getInitialStorageFromParamsInVM}: run once per entry\n * before a template is applied, so params a kind rejects are reported against the\n * entry that carries them while there is still no project. Skipping it is safe —\n * initialization runs the same check — but then the report arrives after earlier\n * entries have already been created.\n *\n * Every kind declares a parser, so a pass here means the params were checked against\n * the contract — not merely that they were JSON.\n *\n * A block whose model predates the callback passes unchecked rather than failing.\n * Unlike initialization, this method creates nothing, so there is nothing to get wrong\n * by proceeding, and such a block is refused outright at the point it is applied.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params. Reference ids may be placeholders: what is being\n * checked is the shape of the params, not what they point at\n * @returns Nothing, or why the params were rejected\n */\n public validateTemplateParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n ): ResultOrError<undefined> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"validateTemplateParamsInVM is only supported for model API version 2\"),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsValidate];\n if (callback === undefined) return { value: undefined };\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n JSON.stringify(params ?? {}),\n ) as InitializationParamsValidateResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: undefined };\n } catch (e) {\n const cause = ensureError(e);\n return { error: new Error(`Params check failed to run: ${cause.message}`, { cause }) };\n }\n }\n\n /**\n * Creates initial BlockStorage for a block being created from template params.\n *\n * The inverse of {@link deriveTemplateParamsFromStorage}, and the reason a block\n * can be created by anything other than the UI: it hands the params to the\n * block's own init factory inside the model VM, so the resulting storage is\n * whatever that block considers a correctly-initialized state.\n *\n * **Pointing the entry's references at this project happens in the same call**, before the\n * factory sees them — inside the block's bundle, because recognizing a reference means\n * knowing the reference system and a template engine deliberately does not. Params reach\n * here exactly as the file held them.\n *\n * One call and not two: every VM call builds a fresh runtime and re-evaluates the whole\n * model bundle, so asking the block to relocate and then asking it to initialize would parse\n * it twice per entry, for an intermediate value nothing else reads.\n *\n * `blockIds` holds the entries created so far, so an id it does not name is left as it is —\n * a reference to an entry further down the file keeps pointing at nothing, and the applied\n * block reports itself as missing references rather than being wired to a block below it.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params, as the document held them\n * @param blockIds template-local entry id → the block id that entry was given\n * @returns The initial storage as JSON string, or why the params yield none\n */\n public getInitialStorageFromParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n blockIds: ReadonlyMap<string, string>,\n ): ResultOrError<string> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"getInitialStorageFromParamsInVM is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitialFromParams];\n\n // A model built before this callback existed has no entry for it. Falling back to the\n // params-less initializer is not an option: it would produce a default-configured block\n // that looks like a successful apply, so the block the user gets would silently differ\n // from the one the template describes. Nor could its params be used as written — they name\n // the blocks of the project the template came from, and only this callback knows which of\n // the values in there are references at all.\n //\n // The message offers the two actions available to whoever applied the file. The\n // second one is the reason this branch is reachable at all: kind resolution only\n // ever returns a block that declares a kind, and such a block is new enough to\n // support this — but an entry may pin an exact block version instead, bypassing\n // resolution, and that pin can name anything ever published.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be created from a template. Use a newer \" +\n \"version of the block, or remove the pinned block version from the template \" +\n \"entry so a supported one is chosen automatically.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n // Params cross the VM boundary as text, like storage does. `undefined` would\n // stringify to nothing at all, and an entry with no params must go through\n // the params-less initializer rather than reaching this method.\n JSON.stringify(params ?? {}),\n JSON.stringify(Object.fromEntries(blockIds)),\n ) as ParamsStorageResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: result.storageJson };\n } catch (e) {\n const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error becomes a\n // per-entry apply problem shown to whoever triggered the import, and every\n // layer in between carries only `message`.\n return {\n error: new Error(`Initial storage creation from params failed: ${cause.message}`, {\n cause,\n }),\n };\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 // 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,6 +1,7 @@
|
|
|
1
|
+
import { DerivedArgs } from "./args_derivation.js";
|
|
1
2
|
import { BlockConfig, BlockStorage, PlRef, ResultOrError } from "@platforma-sdk/model";
|
|
2
3
|
import { MiLogger } from "@milaboratories/ts-helpers";
|
|
3
|
-
import { StringifiedJson } from "@milaboratories/pl-model-common";
|
|
4
|
+
import { ColumnReference, StringifiedJson } from "@milaboratories/pl-model-common";
|
|
4
5
|
import { SignedResourceId } from "@milaboratories/pl-client";
|
|
5
6
|
import { QuickJSWASMModule } from "quickjs-emscripten";
|
|
6
7
|
/**
|
|
@@ -21,26 +22,31 @@ export declare class ProjectHelper {
|
|
|
21
22
|
private readonly quickJs;
|
|
22
23
|
readonly logger: MiLogger;
|
|
23
24
|
private readonly enrichmentTargetsCache;
|
|
25
|
+
private readonly columnReferencesCache;
|
|
24
26
|
constructor(quickJs: QuickJSWASMModule, logger?: MiLogger);
|
|
25
27
|
/**
|
|
26
28
|
* Derives args directly from storage JSON using VM callback.
|
|
27
|
-
* The VM extracts data from storage
|
|
29
|
+
* The VM extracts data from storage, calls the block's args() function and then its argsIssues().
|
|
28
30
|
*
|
|
29
31
|
* This allows the middle layer to work only with storage JSON,
|
|
30
32
|
* without needing to know the underlying data structure.
|
|
31
33
|
*
|
|
32
34
|
* @param blockConfig The block configuration (provides the model code)
|
|
33
35
|
* @param storageJson Storage as JSON string
|
|
34
|
-
* @returns The derived args
|
|
36
|
+
* @returns The derived args and whether the block may run with them, or why there are none
|
|
35
37
|
*/
|
|
36
|
-
deriveArgsFromStorage(blockConfig: BlockConfig, storageJson: string):
|
|
38
|
+
deriveArgsFromStorage(blockConfig: BlockConfig, storageJson: string): DerivedArgs;
|
|
37
39
|
/**
|
|
38
40
|
* Derives prerunArgs directly from storage JSON using VM callback.
|
|
39
41
|
* Falls back to args() if prerunArgs is not defined in the block model.
|
|
40
42
|
*
|
|
43
|
+
* Errors the block's `prerunArgsIssues()` finds in its prerun args, or for a block without its
|
|
44
|
+
* own prerunArgs, the errors its `argsIssues()` finds in its args, come back as an `error` and
|
|
45
|
+
* skip staging the same way.
|
|
46
|
+
*
|
|
41
47
|
* @param blockConfig The block configuration (provides the model code)
|
|
42
48
|
* @param storageJson Storage as JSON string
|
|
43
|
-
* @returns The derived prerunArgs, or undefined if derivation fails
|
|
49
|
+
* @returns The derived prerunArgs, or undefined if derivation fails or found errors
|
|
44
50
|
*/
|
|
45
51
|
derivePrerunArgsFromStorage(blockConfig: BlockConfig, storageJson: string): unknown;
|
|
46
52
|
/**
|
|
@@ -74,6 +80,13 @@ export declare class ProjectHelper {
|
|
|
74
80
|
argsRid: SignedResourceId;
|
|
75
81
|
blockPackRid: SignedResourceId;
|
|
76
82
|
}): PlRef[] | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Columns referenced by a block's args, with the values of them the args picked, read from the
|
|
85
|
+
* args resource `argsRid`. `undefined` while the resource data is not loaded yet: `args`
|
|
86
|
+
* returns `undefined` then, and nothing is remembered for such a read, so the references are
|
|
87
|
+
* found once the data arrives.
|
|
88
|
+
*/
|
|
89
|
+
getColumnReferences(argsRid: SignedResourceId, args: () => unknown): undefined | readonly ColumnReference[];
|
|
77
90
|
/**
|
|
78
91
|
* Creates initial BlockStorage for a new block using VM-based transformation.
|
|
79
92
|
* This calls the '__pl_storage_initial' callback registered by DataModel which:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project_helper.d.ts","names":[],"sources":["../../src/model/project_helper.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"project_helper.d.ts","names":[],"sources":["../../src/model/project_helper.ts"],"mappings":";;;;;;;;;;;;;YAiCY;EACN;;EACA;EAAmB,gBAAgB,gBAAgB;EAAe;;qBAuB3D;mBAmBQ;WACD,QAAQ;mBAnBT;mBAYA;EAKjB,YACmB,SAAS,mBACV,SAAQ;;;;;;;;;;;;EAkBnB,sBAAsB,aAAa,aAAa,sBAAsB;;;;;;;;;;;;;EAqCtE,4BAA4B,aAAa,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDtD,gCACL,aAAa,aACb,sBACC;UAsDK;EAaD,qBACL,mBAAmB,aACnB,qBACA;IAAQ,SAAS;IAAkB,cAAc;MAChD;;;;;;;EAaI,oBACL,SAAS,kBACT,2CACsB;;;;;;;;;;;EAwBjB,sBAAsB,aAAa;;;;;;;;;;;;;;;;;;;;;;EA2CnC,2BACL,aAAa,aACb,kBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqDI,gCACL,aAAa,aACb,iBACA,UAAU,8BACT;;;;;;;;;;;;;;EA0EI,uBACL,aAAa,aACb,4BACA;IAAW;IAAmB;;;;;;;;;;;;;;;;;EAmDzB,mBACL,aAAa,aACb,yCACC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { parseArgsDeriveResult } from "./args_derivation.js";
|
|
1
2
|
import { getDebugFlags } from "../debug/index.js";
|
|
2
3
|
import { executeSingleLambda } from "../js_render/index.js";
|
|
3
4
|
import { BLOCK_STORAGE_FACADE_VERSION, BlockStorageFacadeCallbacks, ensureError, extractCodeWithInfo } from "@platforma-sdk/model";
|
|
4
5
|
import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
|
|
6
|
+
import { collectColumnReferences } from "@milaboratories/pl-model-common";
|
|
5
7
|
import { LRUCache } from "lru-cache";
|
|
6
8
|
// src/model/project_helper.ts
|
|
7
9
|
var ProjectHelper = class {
|
|
@@ -13,38 +15,47 @@ var ProjectHelper = class {
|
|
|
13
15
|
return { value: this.calculateEnrichmentTargets(context) };
|
|
14
16
|
}
|
|
15
17
|
});
|
|
18
|
+
columnReferencesCache = new LRUCache({ max: 1024 });
|
|
16
19
|
constructor(quickJs, logger = new ConsoleLoggerAdapter()) {
|
|
17
20
|
this.quickJs = quickJs;
|
|
18
21
|
this.logger = logger;
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* Derives args directly from storage JSON using VM callback.
|
|
22
|
-
* The VM extracts data from storage
|
|
25
|
+
* The VM extracts data from storage, calls the block's args() function and then its argsIssues().
|
|
23
26
|
*
|
|
24
27
|
* This allows the middle layer to work only with storage JSON,
|
|
25
28
|
* without needing to know the underlying data structure.
|
|
26
29
|
*
|
|
27
30
|
* @param blockConfig The block configuration (provides the model code)
|
|
28
31
|
* @param storageJson Storage as JSON string
|
|
29
|
-
* @returns The derived args
|
|
32
|
+
* @returns The derived args and whether the block may run with them, or why there are none
|
|
30
33
|
*/
|
|
31
34
|
deriveArgsFromStorage(blockConfig, storageJson) {
|
|
32
|
-
if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) return {
|
|
35
|
+
if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) return {
|
|
36
|
+
type: "absent",
|
|
37
|
+
error: /* @__PURE__ */ new Error("deriveArgsFromStorage is only supported for model API version 2")
|
|
38
|
+
};
|
|
33
39
|
try {
|
|
34
|
-
|
|
35
|
-
if (result.error !== void 0) return { error: new Error(result.error) };
|
|
36
|
-
return { value: result.value };
|
|
40
|
+
return parseArgsDeriveResult(executeSingleLambda(this.quickJs, blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.ArgsDerive], extractCodeWithInfo(blockConfig), storageJson));
|
|
37
41
|
} catch (e) {
|
|
38
|
-
return {
|
|
42
|
+
return {
|
|
43
|
+
type: "absent",
|
|
44
|
+
error: new Error("Args derivation from storage failed", { cause: ensureError(e) })
|
|
45
|
+
};
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
42
49
|
* Derives prerunArgs directly from storage JSON using VM callback.
|
|
43
50
|
* Falls back to args() if prerunArgs is not defined in the block model.
|
|
44
51
|
*
|
|
52
|
+
* Errors the block's `prerunArgsIssues()` finds in its prerun args, or for a block without its
|
|
53
|
+
* own prerunArgs, the errors its `argsIssues()` finds in its args, come back as an `error` and
|
|
54
|
+
* skip staging the same way.
|
|
55
|
+
*
|
|
45
56
|
* @param blockConfig The block configuration (provides the model code)
|
|
46
57
|
* @param storageJson Storage as JSON string
|
|
47
|
-
* @returns The derived prerunArgs, or undefined if derivation fails
|
|
58
|
+
* @returns The derived prerunArgs, or undefined if derivation fails or found errors
|
|
48
59
|
*/
|
|
49
60
|
derivePrerunArgsFromStorage(blockConfig, storageJson) {
|
|
50
61
|
if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) throw new Error("derivePrerunArgsFromStorage is only supported for model API version 2");
|
|
@@ -110,6 +121,21 @@ var ProjectHelper = class {
|
|
|
110
121
|
return this.enrichmentTargetsCache.memo(cacheKey, { context: req }).value;
|
|
111
122
|
}
|
|
112
123
|
/**
|
|
124
|
+
* Columns referenced by a block's args, with the values of them the args picked, read from the
|
|
125
|
+
* args resource `argsRid`. `undefined` while the resource data is not loaded yet: `args`
|
|
126
|
+
* returns `undefined` then, and nothing is remembered for such a read, so the references are
|
|
127
|
+
* found once the data arrives.
|
|
128
|
+
*/
|
|
129
|
+
getColumnReferences(argsRid, args) {
|
|
130
|
+
const cached = this.columnReferencesCache.get(argsRid);
|
|
131
|
+
if (cached !== void 0) return cached;
|
|
132
|
+
const data = args();
|
|
133
|
+
if (data === void 0) return void 0;
|
|
134
|
+
const references = collectColumnReferences(data);
|
|
135
|
+
this.columnReferencesCache.set(argsRid, references);
|
|
136
|
+
return references;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
113
139
|
* Creates initial BlockStorage for a new block using VM-based transformation.
|
|
114
140
|
* This calls the '__pl_storage_initial' callback registered by DataModel which:
|
|
115
141
|
* - Gets initial data from DataModel.getDefaultData()
|
|
@@ -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 { 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\n/**\n * Result of building initial storage from params.\n * Returned by the __pl_storage_initialFromParams VM callback.\n */\ntype ParamsStorageResult =\n | { error: string }\n | { error?: undefined; storageJson: StringifiedJson<BlockStorage> };\n\n/**\n * Result of checking params against their kind.\n * Returned by the __pl_initializationParams_validate VM callback.\n */\ntype InitializationParamsValidateResult = { error: string } | { error?: undefined };\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 /**\n * Derives this block's template-export params from storage JSON using the VM\n * callback (`__pl_initializationParams_derive`).\n *\n * The template-export counterpart of {@link deriveArgsFromStorage}: instead of\n * the args a workflow runs on, it returns the params that would recreate the\n * block — the inverse of the data model's `init`. Params come back exactly as the block\n * projected them, references included: nothing marks or rewrites them on the way out, so\n * nothing here has to know a kind's params shape or how a reference is spelled.\n *\n * A `{ value: undefined }` result means the block declares no `templateParams`, which only\n * a block built against an older SDK can do. It does not reach a document: the export walk\n * requires a mapping and reports anything else as that block's problem, so an entry always\n * carries params.\n *\n * Unlike {@link derivePrerunArgsFromStorage}, a failure here is surfaced rather\n * than swallowed — a prerun that cannot derive args just skips a block in\n * staging, whereas an export that silently drops a block produces a template\n * that does not describe the project.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived params with identifiers wrapped, `undefined` if the block declares\n * no lambda, or an error if derivation failed\n */\n public deriveTemplateParamsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"deriveTemplateParamsFromStorage is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsDerive];\n\n // A model built before this callback existed simply has no entry for it. That is\n // NOT the same as a block declaring no `templateParams`: the block may well have\n // params, we just have no way to ask for them. Reporting it as `undefined` params\n // would export the block stripped of its configuration and quietly rebuild a\n // differently-configured project, so it has to be an error.\n // The message names the one action available to whoever pressed Export. It\n // deliberately says nothing about SDKs or callbacks: the person reading it did\n // not build this block and cannot change how it was built.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be written to a template. Update the block \" +\n \"to a newer version and export again.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\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 const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error is rendered\n // into a per-block export problem and shown to whoever triggered the export,\n // and every layer between here and there carries only `message`.\n return {\n error: new Error(`Template params derivation from storage failed: ${cause.message}`, {\n cause,\n }),\n };\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 * Checks a template entry's params against the block's kind, creating nothing.\n *\n * The pre-flight half of {@link getInitialStorageFromParamsInVM}: run once per entry\n * before a template is applied, so params a kind rejects are reported against the\n * entry that carries them while there is still no project. Skipping it is safe —\n * initialization runs the same check — but then the report arrives after earlier\n * entries have already been created.\n *\n * Every kind declares a parser, so a pass here means the params were checked against\n * the contract — not merely that they were JSON.\n *\n * A block whose model predates the callback passes unchecked rather than failing.\n * Unlike initialization, this method creates nothing, so there is nothing to get wrong\n * by proceeding, and such a block is refused outright at the point it is applied.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params. Reference ids may be placeholders: what is being\n * checked is the shape of the params, not what they point at\n * @returns Nothing, or why the params were rejected\n */\n public validateTemplateParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n ): ResultOrError<undefined> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"validateTemplateParamsInVM is only supported for model API version 2\"),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsValidate];\n if (callback === undefined) return { value: undefined };\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n JSON.stringify(params ?? {}),\n ) as InitializationParamsValidateResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: undefined };\n } catch (e) {\n const cause = ensureError(e);\n return { error: new Error(`Params check failed to run: ${cause.message}`, { cause }) };\n }\n }\n\n /**\n * Creates initial BlockStorage for a block being created from template params.\n *\n * The inverse of {@link deriveTemplateParamsFromStorage}, and the reason a block\n * can be created by anything other than the UI: it hands the params to the\n * block's own init factory inside the model VM, so the resulting storage is\n * whatever that block considers a correctly-initialized state.\n *\n * **Pointing the entry's references at this project happens in the same call**, before the\n * factory sees them — inside the block's bundle, because recognizing a reference means\n * knowing the reference system and a template engine deliberately does not. Params reach\n * here exactly as the file held them.\n *\n * One call and not two: every VM call builds a fresh runtime and re-evaluates the whole\n * model bundle, so asking the block to relocate and then asking it to initialize would parse\n * it twice per entry, for an intermediate value nothing else reads.\n *\n * `blockIds` holds the entries created so far, so an id it does not name is left as it is —\n * a reference to an entry further down the file keeps pointing at nothing, and the applied\n * block reports itself as missing references rather than being wired to a block below it.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params, as the document held them\n * @param blockIds template-local entry id → the block id that entry was given\n * @returns The initial storage as JSON string, or why the params yield none\n */\n public getInitialStorageFromParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n blockIds: ReadonlyMap<string, string>,\n ): ResultOrError<string> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"getInitialStorageFromParamsInVM is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitialFromParams];\n\n // A model built before this callback existed has no entry for it. Falling back to the\n // params-less initializer is not an option: it would produce a default-configured block\n // that looks like a successful apply, so the block the user gets would silently differ\n // from the one the template describes. Nor could its params be used as written — they name\n // the blocks of the project the template came from, and only this callback knows which of\n // the values in there are references at all.\n //\n // The message offers the two actions available to whoever applied the file. The\n // second one is the reason this branch is reachable at all: kind resolution only\n // ever returns a block that declares a kind, and such a block is new enough to\n // support this — but an entry may pin an exact block version instead, bypassing\n // resolution, and that pin can name anything ever published.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be created from a template. Use a newer \" +\n \"version of the block, or remove the pinned block version from the template \" +\n \"entry so a supported one is chosen automatically.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n // Params cross the VM boundary as text, like storage does. `undefined` would\n // stringify to nothing at all, and an entry with no params must go through\n // the params-less initializer rather than reaching this method.\n JSON.stringify(params ?? {}),\n JSON.stringify(Object.fromEntries(blockIds)),\n ) as ParamsStorageResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: result.storageJson };\n } catch (e) {\n const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error becomes a\n // per-entry apply problem shown to whoever triggered the import, and every\n // layer in between carries only `message`.\n return {\n error: new Error(`Initial storage creation from params failed: ${cause.message}`, {\n cause,\n }),\n };\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 // 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 { ColumnReference, StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport { collectColumnReferences } 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 { getDebugFlags } from \"../debug\";\nimport { parseArgsDeriveResult, type DerivedArgs } from \"./args_derivation\";\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 a derivation from storage that either produced a value or failed.\n * Returned by the __pl_prerunArgs_derive and __pl_initializationParams_derive VM callbacks; a\n * newer model adds the issues found in the prerun args, which are not read here.\n */\ntype DeriveResult = { error: string } | { error?: undefined; value: unknown };\n\n/**\n * Result of building initial storage from params.\n * Returned by the __pl_storage_initialFromParams VM callback.\n */\ntype ParamsStorageResult =\n | { error: string }\n | { error?: undefined; storageJson: StringifiedJson<BlockStorage> };\n\n/**\n * Result of checking params against their kind.\n * Returned by the __pl_initializationParams_validate VM callback.\n */\ntype InitializationParamsValidateResult = { error: string } | { error?: undefined };\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 // An args resource never changes, so its id alone identifies the columns found in it.\n private readonly columnReferencesCache = new LRUCache<\n SignedResourceId,\n readonly ColumnReference[]\n >({ max: 1024 });\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, calls the block's args() function and then its argsIssues().\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 and whether the block may run with them, or why there are none\n */\n public deriveArgsFromStorage(blockConfig: BlockConfig, storageJson: string): DerivedArgs {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n type: \"absent\",\n error: new Error(\"deriveArgsFromStorage is only supported for model API version 2\"),\n };\n }\n\n try {\n return parseArgsDeriveResult(\n executeSingleLambda(\n this.quickJs,\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.ArgsDerive],\n extractCodeWithInfo(blockConfig),\n storageJson,\n ),\n );\n } catch (e) {\n return {\n type: \"absent\",\n error: new Error(\"Args derivation from storage failed\", { cause: ensureError(e) }),\n };\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 * Errors the block's `prerunArgsIssues()` finds in its prerun args, or for a block without its\n * own prerunArgs, the errors its `argsIssues()` finds in its args, come back as an `error` and\n * skip staging the same way.\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 or found errors\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 DeriveResult;\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 /**\n * Derives this block's template-export params from storage JSON using the VM\n * callback (`__pl_initializationParams_derive`).\n *\n * The template-export counterpart of {@link deriveArgsFromStorage}: instead of\n * the args a workflow runs on, it returns the params that would recreate the\n * block — the inverse of the data model's `init`. Params come back exactly as the block\n * projected them, references included: nothing marks or rewrites them on the way out, so\n * nothing here has to know a kind's params shape or how a reference is spelled.\n *\n * A `{ value: undefined }` result means the block declares no `templateParams`, which only\n * a block built against an older SDK can do. It does not reach a document: the export walk\n * requires a mapping and reports anything else as that block's problem, so an entry always\n * carries params.\n *\n * Unlike {@link derivePrerunArgsFromStorage}, a failure here is surfaced rather\n * than swallowed — a prerun that cannot derive args just skips a block in\n * staging, whereas an export that silently drops a block produces a template\n * that does not describe the project.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param storageJson Storage as JSON string\n * @returns The derived params with identifiers wrapped, `undefined` if the block declares\n * no lambda, or an error if derivation failed\n */\n public deriveTemplateParamsFromStorage(\n blockConfig: BlockConfig,\n storageJson: string,\n ): ResultOrError<unknown> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"deriveTemplateParamsFromStorage is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsDerive];\n\n // A model built before this callback existed simply has no entry for it. That is\n // NOT the same as a block declaring no `templateParams`: the block may well have\n // params, we just have no way to ask for them. Reporting it as `undefined` params\n // would export the block stripped of its configuration and quietly rebuild a\n // differently-configured project, so it has to be an error.\n // The message names the one action available to whoever pressed Export. It\n // deliberately says nothing about SDKs or callbacks: the person reading it did\n // not build this block and cannot change how it was built.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be written to a template. Update the block \" +\n \"to a newer version and export again.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n storageJson,\n ) as DeriveResult;\n\n if (result.error !== undefined) {\n return { error: new Error(result.error) };\n }\n return { value: result.value };\n } catch (e) {\n const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error is rendered\n // into a per-block export problem and shown to whoever triggered the export,\n // and every layer between here and there carries only `message`.\n return {\n error: new Error(`Template params derivation from storage failed: ${cause.message}`, {\n cause,\n }),\n };\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 * Columns referenced by a block's args, with the values of them the args picked, read from the\n * args resource `argsRid`. `undefined` while the resource data is not loaded yet: `args`\n * returns `undefined` then, and nothing is remembered for such a read, so the references are\n * found once the data arrives.\n */\n public getColumnReferences(\n argsRid: SignedResourceId,\n args: () => unknown,\n ): undefined | readonly ColumnReference[] {\n const cached = this.columnReferencesCache.get(argsRid);\n if (cached !== undefined) return cached;\n const data = args();\n if (data === undefined) return undefined;\n const references = collectColumnReferences(data);\n this.columnReferencesCache.set(argsRid, references);\n return references;\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 * Checks a template entry's params against the block's kind, creating nothing.\n *\n * The pre-flight half of {@link getInitialStorageFromParamsInVM}: run once per entry\n * before a template is applied, so params a kind rejects are reported against the\n * entry that carries them while there is still no project. Skipping it is safe —\n * initialization runs the same check — but then the report arrives after earlier\n * entries have already been created.\n *\n * Every kind declares a parser, so a pass here means the params were checked against\n * the contract — not merely that they were JSON.\n *\n * A block whose model predates the callback passes unchecked rather than failing.\n * Unlike initialization, this method creates nothing, so there is nothing to get wrong\n * by proceeding, and such a block is refused outright at the point it is applied.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params. Reference ids may be placeholders: what is being\n * checked is the shape of the params, not what they point at\n * @returns Nothing, or why the params were rejected\n */\n public validateTemplateParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n ): ResultOrError<undefined> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\"validateTemplateParamsInVM is only supported for model API version 2\"),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.InitializationParamsValidate];\n if (callback === undefined) return { value: undefined };\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n JSON.stringify(params ?? {}),\n ) as InitializationParamsValidateResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: undefined };\n } catch (e) {\n const cause = ensureError(e);\n return { error: new Error(`Params check failed to run: ${cause.message}`, { cause }) };\n }\n }\n\n /**\n * Creates initial BlockStorage for a block being created from template params.\n *\n * The inverse of {@link deriveTemplateParamsFromStorage}, and the reason a block\n * can be created by anything other than the UI: it hands the params to the\n * block's own init factory inside the model VM, so the resulting storage is\n * whatever that block considers a correctly-initialized state.\n *\n * **Pointing the entry's references at this project happens in the same call**, before the\n * factory sees them — inside the block's bundle, because recognizing a reference means\n * knowing the reference system and a template engine deliberately does not. Params reach\n * here exactly as the file held them.\n *\n * One call and not two: every VM call builds a fresh runtime and re-evaluates the whole\n * model bundle, so asking the block to relocate and then asking it to initialize would parse\n * it twice per entry, for an intermediate value nothing else reads.\n *\n * `blockIds` holds the entries created so far, so an id it does not name is left as it is —\n * a reference to an entry further down the file keeps pointing at nothing, and the applied\n * block reports itself as missing references rather than being wired to a block below it.\n *\n * @param blockConfig The block configuration (provides the model code)\n * @param params The entry's params, as the document held them\n * @param blockIds template-local entry id → the block id that entry was given\n * @returns The initial storage as JSON string, or why the params yield none\n */\n public getInitialStorageFromParamsInVM(\n blockConfig: BlockConfig,\n params: unknown,\n blockIds: ReadonlyMap<string, string>,\n ): ResultOrError<string> {\n if (blockConfig.modelAPIVersion !== BLOCK_STORAGE_FACADE_VERSION) {\n return {\n error: new Error(\n \"getInitialStorageFromParamsInVM is only supported for model API version 2\",\n ),\n };\n }\n\n const callback =\n blockConfig.blockLifecycleCallbacks[BlockStorageFacadeCallbacks.StorageInitialFromParams];\n\n // A model built before this callback existed has no entry for it. Falling back to the\n // params-less initializer is not an option: it would produce a default-configured block\n // that looks like a successful apply, so the block the user gets would silently differ\n // from the one the template describes. Nor could its params be used as written — they name\n // the blocks of the project the template came from, and only this callback knows which of\n // the values in there are references at all.\n //\n // The message offers the two actions available to whoever applied the file. The\n // second one is the reason this branch is reachable at all: kind resolution only\n // ever returns a block that declares a kind, and such a block is new enough to\n // support this — but an entry may pin an exact block version instead, bypassing\n // resolution, and that pin can name anything ever published.\n if (callback === undefined) {\n return {\n error: new Error(\n \"This version of the block cannot be created from a template. Use a newer \" +\n \"version of the block, or remove the pinned block version from the template \" +\n \"entry so a supported one is chosen automatically.\",\n ),\n };\n }\n\n try {\n const result = executeSingleLambda(\n this.quickJs,\n callback,\n extractCodeWithInfo(blockConfig),\n // Params cross the VM boundary as text, like storage does. `undefined` would\n // stringify to nothing at all, and an entry with no params must go through\n // the params-less initializer rather than reaching this method.\n JSON.stringify(params ?? {}),\n JSON.stringify(Object.fromEntries(blockIds)),\n ) as ParamsStorageResult;\n\n if (result.error !== undefined) return { error: new Error(result.error) };\n return { value: result.storageJson };\n } catch (e) {\n const cause = ensureError(e);\n // The reason goes in the message, not only in `cause`: this error becomes a\n // per-entry apply problem shown to whoever triggered the import, and every\n // layer in between carries only `message`.\n return {\n error: new Error(`Initial storage creation from params failed: ${cause.message}`, {\n cause,\n }),\n };\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 // 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_model.cjs","names":[],"sources":["../../src/model/project_model.ts"],"sourcesContent":["import type { ResourceType } from \"@milaboratories/pl-client\";\nimport type { ProjectId } from \"@milaboratories/pl-model-common\";\nimport type {\n ProjectListEntry as ProjectListEntryFromModel,\n ProjectMeta,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockRenderingMode } from \"@platforma-sdk/model\";\n\nexport type { ProjectId };\n\nexport interface ProjectListEntry extends Omit<ProjectListEntryFromModel, \"id\"> {\n /** Unique project identifier in middle layer. Use to operate with given project. */\n id: ProjectId;\n}\n\n/** Entry representing a single block in block structure */\nexport interface Block {\n /** Unique block id */\n readonly id: string;\n\n /**\n * Label shown to the user\n * @deprecated\n * */\n label: string;\n\n /** How to approach block rendering */\n renderingMode: BlockRenderingMode;\n}\n\n/** Block group in block structure */\nexport interface BlockGroup {\n /** Unique group id */\n readonly id: string;\n\n /** Label shown to the user */\n label: string;\n\n /** Blocks */\n blocks: Block[];\n}\n\n/** Root of block structure value */\nexport interface ProjectStructure {\n /** The structure */\n readonly groups: BlockGroup[];\n}\n\nexport const InitialBlockStructure: ProjectStructure = {\n groups: [{ id: \"default\", label: \"Default\", blocks: [] }],\n};\n\n/** Root of project rendering state */\nexport interface ProjectRenderingState {\n /** Timestamp of last staging rendering event */\n stagingRefreshTimestamp: number;\n\n /** Blocks with detached production state, i.e. some of the dependencies of\n * such blocks were re-rendered, but their state was left \"as is\". */\n blocksInLimbo: string[];\n}\n\nexport const InitialProjectRenderingState: ProjectRenderingState = {\n stagingRefreshTimestamp: 0,\n blocksInLimbo: [],\n};\n\nexport const InitialBlockMeta: ProjectMeta = {\n label: \"New Project\",\n};\n\n//\n// Pl Model\n//\n\nexport const ProjectResourceType: ResourceType = { name: \"UserProject\", version: \"2\" };\n\nexport const SchemaVersionKey = \"SchemaVersion\";\nexport const SchemaVersionV2 = \"2\";\nexport const SchemaVersionV3 = \"3\";\nexport const SchemaVersionCurrent = \"4\";\n\nexport const ProjectCreatedTimestamp = \"ProjectCreated\";\nexport const ProjectLastModifiedTimestamp = \"ProjectLastModified\";\nexport const ProjectMetaKey = \"ProjectMeta\";\nexport const ProjectStructureKey = \"ProjectStructure\";\nexport const BlockRenderingStateKey = \"BlockRenderingState\";\n\nexport const BlockArgsAuthorKeyPrefix = \"BlockArgsAuthor/\";\nexport const BlockArgsAuthorKeyPattern = /^BlockArgsAuthor\\/(?<blockid>.*)$/;\n\nexport function blockArgsAuthorKey(blockId: string): string {\n return `${BlockArgsAuthorKeyPrefix}${blockId}`;\n}\n\nexport const ProjectStructureAuthorKey = \"ProjectStructureAuthor\";\n\nexport const ServiceTemplateCacheFieldPrefix = \"__serviceTemplate_\";\n\nexport function getServiceTemplateField(hash: string): string {\n return `${ServiceTemplateCacheFieldPrefix}${hash}`;\n}\n\nexport interface ProjectField {\n blockId: string;\n fieldName:\n | \"blockPack\"\n | \"blockSettings\"\n | \"blockStorage\" // Persistent storage for v3 blocks (state, plugins data, etc.)\n | \"prodArgs\"\n | \"currentArgs\"\n | \"currentPrerunArgs\" // Derived args for staging/prerun rendering (from prerunArgs() or args())\n | \"prodChainCtx\" // Pre-built production context chain node (accumulated prodCtx from all blocks above)\n | \"prodCtx\"\n | \"prodUiCtx\"\n | \"prodOutput\"\n | \"prodCtxPrevious\"\n | \"prodUiCtxPrevious\"\n | \"prodOutputPrevious\"\n | \"stagingCtx\"\n | \"stagingUiCtx\"\n | \"stagingOutput\"\n | \"stagingCtxPrevious\"\n | \"stagingUiCtxPrevious\"\n | \"stagingOutputPrevious\";\n}\n\nexport const FieldsToDuplicate: Set<ProjectField[\"fieldName\"]> = new Set([\n \"blockPack\",\n \"blockSettings\",\n \"blockStorage\",\n \"prodArgs\",\n \"currentArgs\",\n \"currentPrerunArgs\",\n \"prodCtx\",\n \"prodUiCtx\",\n \"prodOutput\",\n]);\n\nexport function projectFieldName(blockId: string, fieldName: ProjectField[\"fieldName\"]): string {\n return `${blockId}-${fieldName}`;\n}\n\nconst projectFieldPattern =\n /^(?<blockId>.*)-(?<fieldName>blockPack|blockSettings|blockStorage|inputsValid|prodArgs|currentArgs|currentPrerunArgs|prodChainCtx|prodCtx|prodUiCtx|prodOutput|prodCtxPrevious|prodUiCtxPrevious|prodOutputPrevious|stagingCtx|stagingUiCtx|stagingOutput|stagingCtxPrevious|stagingUiCtxPrevious|stagingOutputPrevious)$/;\n\nexport function parseProjectField(name: string): ProjectField | undefined {\n const match = name.match(projectFieldPattern);\n if (match === null) return undefined;\n const { blockId, fieldName } = match.groups!;\n return { blockId, fieldName } as ProjectField;\n}\n"],"mappings":";AAgDA,MAAa,wBAA0C,EACrD,QAAQ,CAAC;CAAE,IAAI;CAAW,OAAO;CAAW,QAAQ,CAAC;AAAE,CAAC,EAC1D;AAYA,MAAa,+BAAsD;CACjE,yBAAyB;CACzB,eAAe,CAAC;AAClB;AAEA,MAAa,mBAAgC,EAC3C,OAAO,cACT;AAMA,MAAa,sBAAoC;CAAE,MAAM;CAAe,SAAS;AAAI;AAErF,MAAa,mBAAmB;AAGhC,MAAa,uBAAuB;AAEpC,MAAa,0BAA0B;AACvC,MAAa,+BAA+B;AAC5C,MAAa,iBAAiB;AAC9B,MAAa,sBAAsB;AACnC,MAAa,yBAAyB;AAEtC,MAAa,2BAA2B;AAGxC,SAAgB,mBAAmB,SAAyB;CAC1D,OAAO,GAAG,2BAA2B;AACvC;AAEA,MAAa,4BAA4B;AAEzC,MAAa,kCAAkC;AAE/C,SAAgB,wBAAwB,MAAsB;CAC5D,OAAO,GAAG,kCAAkC;AAC9C;
|
|
1
|
+
{"version":3,"file":"project_model.cjs","names":[],"sources":["../../src/model/project_model.ts"],"sourcesContent":["import type { ResourceType } from \"@milaboratories/pl-client\";\nimport type { ProjectId } from \"@milaboratories/pl-model-common\";\nimport type {\n ProjectListEntry as ProjectListEntryFromModel,\n ProjectMeta,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockRenderingMode } from \"@platforma-sdk/model\";\n\nexport type { ProjectId };\n\nexport interface ProjectListEntry extends Omit<ProjectListEntryFromModel, \"id\"> {\n /** Unique project identifier in middle layer. Use to operate with given project. */\n id: ProjectId;\n}\n\n/** Entry representing a single block in block structure */\nexport interface Block {\n /** Unique block id */\n readonly id: string;\n\n /**\n * Label shown to the user\n * @deprecated\n * */\n label: string;\n\n /** How to approach block rendering */\n renderingMode: BlockRenderingMode;\n}\n\n/** Block group in block structure */\nexport interface BlockGroup {\n /** Unique group id */\n readonly id: string;\n\n /** Label shown to the user */\n label: string;\n\n /** Blocks */\n blocks: Block[];\n}\n\n/** Root of block structure value */\nexport interface ProjectStructure {\n /** The structure */\n readonly groups: BlockGroup[];\n}\n\nexport const InitialBlockStructure: ProjectStructure = {\n groups: [{ id: \"default\", label: \"Default\", blocks: [] }],\n};\n\n/** Root of project rendering state */\nexport interface ProjectRenderingState {\n /** Timestamp of last staging rendering event */\n stagingRefreshTimestamp: number;\n\n /** Blocks with detached production state, i.e. some of the dependencies of\n * such blocks were re-rendered, but their state was left \"as is\". */\n blocksInLimbo: string[];\n}\n\nexport const InitialProjectRenderingState: ProjectRenderingState = {\n stagingRefreshTimestamp: 0,\n blocksInLimbo: [],\n};\n\nexport const InitialBlockMeta: ProjectMeta = {\n label: \"New Project\",\n};\n\n//\n// Pl Model\n//\n\nexport const ProjectResourceType: ResourceType = { name: \"UserProject\", version: \"2\" };\n\nexport const SchemaVersionKey = \"SchemaVersion\";\nexport const SchemaVersionV2 = \"2\";\nexport const SchemaVersionV3 = \"3\";\nexport const SchemaVersionCurrent = \"4\";\n\nexport const ProjectCreatedTimestamp = \"ProjectCreated\";\nexport const ProjectLastModifiedTimestamp = \"ProjectLastModified\";\nexport const ProjectMetaKey = \"ProjectMeta\";\nexport const ProjectStructureKey = \"ProjectStructure\";\nexport const BlockRenderingStateKey = \"BlockRenderingState\";\n\nexport const BlockArgsAuthorKeyPrefix = \"BlockArgsAuthor/\";\nexport const BlockArgsAuthorKeyPattern = /^BlockArgsAuthor\\/(?<blockid>.*)$/;\n\nexport function blockArgsAuthorKey(blockId: string): string {\n return `${BlockArgsAuthorKeyPrefix}${blockId}`;\n}\n\nexport const ProjectStructureAuthorKey = \"ProjectStructureAuthor\";\n\nexport const ServiceTemplateCacheFieldPrefix = \"__serviceTemplate_\";\n\nexport function getServiceTemplateField(hash: string): string {\n return `${ServiceTemplateCacheFieldPrefix}${hash}`;\n}\n\nexport interface ProjectField {\n blockId: string;\n fieldName:\n | \"blockPack\"\n | \"blockSettings\"\n | \"blockStorage\" // Persistent storage for v3 blocks (state, plugins data, etc.)\n | \"prodArgs\"\n | \"currentArgs\"\n | \"currentPrerunArgs\" // Derived args for staging/prerun rendering (from prerunArgs() or args())\n | \"inputsValid\" // Model API v2: errors found in currentArgs, none when the block may run, with the fingerprint of those args; present while currentArgs are\n | \"prodChainCtx\" // Pre-built production context chain node (accumulated prodCtx from all blocks above)\n | \"prodCtx\"\n | \"prodUiCtx\"\n | \"prodOutput\"\n | \"prodCtxPrevious\"\n | \"prodUiCtxPrevious\"\n | \"prodOutputPrevious\"\n | \"stagingCtx\"\n | \"stagingUiCtx\"\n | \"stagingOutput\"\n | \"stagingCtxPrevious\"\n | \"stagingUiCtxPrevious\"\n | \"stagingOutputPrevious\";\n}\n\nexport const FieldsToDuplicate: Set<ProjectField[\"fieldName\"]> = new Set([\n \"blockPack\",\n \"blockSettings\",\n \"blockStorage\",\n \"prodArgs\",\n \"currentArgs\",\n \"currentPrerunArgs\",\n \"inputsValid\",\n \"prodCtx\",\n \"prodUiCtx\",\n \"prodOutput\",\n]);\n\nexport function projectFieldName(blockId: string, fieldName: ProjectField[\"fieldName\"]): string {\n return `${blockId}-${fieldName}`;\n}\n\nconst projectFieldPattern =\n /^(?<blockId>.*)-(?<fieldName>blockPack|blockSettings|blockStorage|inputsValid|prodArgs|currentArgs|currentPrerunArgs|prodChainCtx|prodCtx|prodUiCtx|prodOutput|prodCtxPrevious|prodUiCtxPrevious|prodOutputPrevious|stagingCtx|stagingUiCtx|stagingOutput|stagingCtxPrevious|stagingUiCtxPrevious|stagingOutputPrevious)$/;\n\nexport function parseProjectField(name: string): ProjectField | undefined {\n const match = name.match(projectFieldPattern);\n if (match === null) return undefined;\n const { blockId, fieldName } = match.groups!;\n return { blockId, fieldName } as ProjectField;\n}\n"],"mappings":";AAgDA,MAAa,wBAA0C,EACrD,QAAQ,CAAC;CAAE,IAAI;CAAW,OAAO;CAAW,QAAQ,CAAC;AAAE,CAAC,EAC1D;AAYA,MAAa,+BAAsD;CACjE,yBAAyB;CACzB,eAAe,CAAC;AAClB;AAEA,MAAa,mBAAgC,EAC3C,OAAO,cACT;AAMA,MAAa,sBAAoC;CAAE,MAAM;CAAe,SAAS;AAAI;AAErF,MAAa,mBAAmB;AAGhC,MAAa,uBAAuB;AAEpC,MAAa,0BAA0B;AACvC,MAAa,+BAA+B;AAC5C,MAAa,iBAAiB;AAC9B,MAAa,sBAAsB;AACnC,MAAa,yBAAyB;AAEtC,MAAa,2BAA2B;AAGxC,SAAgB,mBAAmB,SAAyB;CAC1D,OAAO,GAAG,2BAA2B;AACvC;AAEA,MAAa,4BAA4B;AAEzC,MAAa,kCAAkC;AAE/C,SAAgB,wBAAwB,MAAsB;CAC5D,OAAO,GAAG,kCAAkC;AAC9C;AA2BA,MAAa,oCAAoD,IAAI,IAAI;CACvE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,iBAAiB,SAAiB,WAA8C;CAC9F,OAAO,GAAG,QAAQ,GAAG;AACvB;AAEA,MAAM,sBACJ;AAEF,SAAgB,kBAAkB,MAAwC;CACxE,MAAM,QAAQ,KAAK,MAAM,mBAAmB;CAC5C,IAAI,UAAU,MAAM,OAAO,KAAA;CAC3B,MAAM,EAAE,SAAS,cAAc,MAAM;CACrC,OAAO;EAAE;EAAS;CAAU;AAC9B"}
|
|
@@ -17,7 +17,7 @@ export declare const BlockArgsAuthorKeyPrefix = "BlockArgsAuthor/";
|
|
|
17
17
|
export declare const ProjectStructureAuthorKey = "ProjectStructureAuthor";
|
|
18
18
|
export interface ProjectField {
|
|
19
19
|
blockId: string;
|
|
20
|
-
fieldName: "blockPack" | "blockSettings" | "blockStorage" | "prodArgs" | "currentArgs" | "currentPrerunArgs" | "prodChainCtx" | "prodCtx" | "prodUiCtx" | "prodOutput" | "prodCtxPrevious" | "prodUiCtxPrevious" | "prodOutputPrevious" | "stagingCtx" | "stagingUiCtx" | "stagingOutput" | "stagingCtxPrevious" | "stagingUiCtxPrevious" | "stagingOutputPrevious";
|
|
20
|
+
fieldName: "blockPack" | "blockSettings" | "blockStorage" | "prodArgs" | "currentArgs" | "currentPrerunArgs" | "inputsValid" | "prodChainCtx" | "prodCtx" | "prodUiCtx" | "prodOutput" | "prodCtxPrevious" | "prodUiCtxPrevious" | "prodOutputPrevious" | "stagingCtx" | "stagingUiCtx" | "stagingOutput" | "stagingCtxPrevious" | "stagingUiCtxPrevious" | "stagingOutputPrevious";
|
|
21
21
|
}
|
|
22
22
|
export { type ProjectId$1 as ProjectId, ProjectListEntry$1 as ProjectListEntry };
|
|
23
23
|
//# sourceMappingURL=project_model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project_model.js","names":[],"sources":["../../src/model/project_model.ts"],"sourcesContent":["import type { ResourceType } from \"@milaboratories/pl-client\";\nimport type { ProjectId } from \"@milaboratories/pl-model-common\";\nimport type {\n ProjectListEntry as ProjectListEntryFromModel,\n ProjectMeta,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockRenderingMode } from \"@platforma-sdk/model\";\n\nexport type { ProjectId };\n\nexport interface ProjectListEntry extends Omit<ProjectListEntryFromModel, \"id\"> {\n /** Unique project identifier in middle layer. Use to operate with given project. */\n id: ProjectId;\n}\n\n/** Entry representing a single block in block structure */\nexport interface Block {\n /** Unique block id */\n readonly id: string;\n\n /**\n * Label shown to the user\n * @deprecated\n * */\n label: string;\n\n /** How to approach block rendering */\n renderingMode: BlockRenderingMode;\n}\n\n/** Block group in block structure */\nexport interface BlockGroup {\n /** Unique group id */\n readonly id: string;\n\n /** Label shown to the user */\n label: string;\n\n /** Blocks */\n blocks: Block[];\n}\n\n/** Root of block structure value */\nexport interface ProjectStructure {\n /** The structure */\n readonly groups: BlockGroup[];\n}\n\nexport const InitialBlockStructure: ProjectStructure = {\n groups: [{ id: \"default\", label: \"Default\", blocks: [] }],\n};\n\n/** Root of project rendering state */\nexport interface ProjectRenderingState {\n /** Timestamp of last staging rendering event */\n stagingRefreshTimestamp: number;\n\n /** Blocks with detached production state, i.e. some of the dependencies of\n * such blocks were re-rendered, but their state was left \"as is\". */\n blocksInLimbo: string[];\n}\n\nexport const InitialProjectRenderingState: ProjectRenderingState = {\n stagingRefreshTimestamp: 0,\n blocksInLimbo: [],\n};\n\nexport const InitialBlockMeta: ProjectMeta = {\n label: \"New Project\",\n};\n\n//\n// Pl Model\n//\n\nexport const ProjectResourceType: ResourceType = { name: \"UserProject\", version: \"2\" };\n\nexport const SchemaVersionKey = \"SchemaVersion\";\nexport const SchemaVersionV2 = \"2\";\nexport const SchemaVersionV3 = \"3\";\nexport const SchemaVersionCurrent = \"4\";\n\nexport const ProjectCreatedTimestamp = \"ProjectCreated\";\nexport const ProjectLastModifiedTimestamp = \"ProjectLastModified\";\nexport const ProjectMetaKey = \"ProjectMeta\";\nexport const ProjectStructureKey = \"ProjectStructure\";\nexport const BlockRenderingStateKey = \"BlockRenderingState\";\n\nexport const BlockArgsAuthorKeyPrefix = \"BlockArgsAuthor/\";\nexport const BlockArgsAuthorKeyPattern = /^BlockArgsAuthor\\/(?<blockid>.*)$/;\n\nexport function blockArgsAuthorKey(blockId: string): string {\n return `${BlockArgsAuthorKeyPrefix}${blockId}`;\n}\n\nexport const ProjectStructureAuthorKey = \"ProjectStructureAuthor\";\n\nexport const ServiceTemplateCacheFieldPrefix = \"__serviceTemplate_\";\n\nexport function getServiceTemplateField(hash: string): string {\n return `${ServiceTemplateCacheFieldPrefix}${hash}`;\n}\n\nexport interface ProjectField {\n blockId: string;\n fieldName:\n | \"blockPack\"\n | \"blockSettings\"\n | \"blockStorage\" // Persistent storage for v3 blocks (state, plugins data, etc.)\n | \"prodArgs\"\n | \"currentArgs\"\n | \"currentPrerunArgs\" // Derived args for staging/prerun rendering (from prerunArgs() or args())\n | \"prodChainCtx\" // Pre-built production context chain node (accumulated prodCtx from all blocks above)\n | \"prodCtx\"\n | \"prodUiCtx\"\n | \"prodOutput\"\n | \"prodCtxPrevious\"\n | \"prodUiCtxPrevious\"\n | \"prodOutputPrevious\"\n | \"stagingCtx\"\n | \"stagingUiCtx\"\n | \"stagingOutput\"\n | \"stagingCtxPrevious\"\n | \"stagingUiCtxPrevious\"\n | \"stagingOutputPrevious\";\n}\n\nexport const FieldsToDuplicate: Set<ProjectField[\"fieldName\"]> = new Set([\n \"blockPack\",\n \"blockSettings\",\n \"blockStorage\",\n \"prodArgs\",\n \"currentArgs\",\n \"currentPrerunArgs\",\n \"prodCtx\",\n \"prodUiCtx\",\n \"prodOutput\",\n]);\n\nexport function projectFieldName(blockId: string, fieldName: ProjectField[\"fieldName\"]): string {\n return `${blockId}-${fieldName}`;\n}\n\nconst projectFieldPattern =\n /^(?<blockId>.*)-(?<fieldName>blockPack|blockSettings|blockStorage|inputsValid|prodArgs|currentArgs|currentPrerunArgs|prodChainCtx|prodCtx|prodUiCtx|prodOutput|prodCtxPrevious|prodUiCtxPrevious|prodOutputPrevious|stagingCtx|stagingUiCtx|stagingOutput|stagingCtxPrevious|stagingUiCtxPrevious|stagingOutputPrevious)$/;\n\nexport function parseProjectField(name: string): ProjectField | undefined {\n const match = name.match(projectFieldPattern);\n if (match === null) return undefined;\n const { blockId, fieldName } = match.groups!;\n return { blockId, fieldName } as ProjectField;\n}\n"],"mappings":";AAgDA,MAAa,wBAA0C,EACrD,QAAQ,CAAC;CAAE,IAAI;CAAW,OAAO;CAAW,QAAQ,CAAC;AAAE,CAAC,EAC1D;AAYA,MAAa,+BAAsD;CACjE,yBAAyB;CACzB,eAAe,CAAC;AAClB;AAEA,MAAa,mBAAgC,EAC3C,OAAO,cACT;AAMA,MAAa,sBAAoC;CAAE,MAAM;CAAe,SAAS;AAAI;AAErF,MAAa,mBAAmB;AAGhC,MAAa,uBAAuB;AAEpC,MAAa,0BAA0B;AACvC,MAAa,+BAA+B;AAC5C,MAAa,iBAAiB;AAC9B,MAAa,sBAAsB;AACnC,MAAa,yBAAyB;AAEtC,MAAa,2BAA2B;AAGxC,SAAgB,mBAAmB,SAAyB;CAC1D,OAAO,GAAG,2BAA2B;AACvC;AAEA,MAAa,4BAA4B;AAEzC,MAAa,kCAAkC;AAE/C,SAAgB,wBAAwB,MAAsB;CAC5D,OAAO,GAAG,kCAAkC;AAC9C;
|
|
1
|
+
{"version":3,"file":"project_model.js","names":[],"sources":["../../src/model/project_model.ts"],"sourcesContent":["import type { ResourceType } from \"@milaboratories/pl-client\";\nimport type { ProjectId } from \"@milaboratories/pl-model-common\";\nimport type {\n ProjectListEntry as ProjectListEntryFromModel,\n ProjectMeta,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockRenderingMode } from \"@platforma-sdk/model\";\n\nexport type { ProjectId };\n\nexport interface ProjectListEntry extends Omit<ProjectListEntryFromModel, \"id\"> {\n /** Unique project identifier in middle layer. Use to operate with given project. */\n id: ProjectId;\n}\n\n/** Entry representing a single block in block structure */\nexport interface Block {\n /** Unique block id */\n readonly id: string;\n\n /**\n * Label shown to the user\n * @deprecated\n * */\n label: string;\n\n /** How to approach block rendering */\n renderingMode: BlockRenderingMode;\n}\n\n/** Block group in block structure */\nexport interface BlockGroup {\n /** Unique group id */\n readonly id: string;\n\n /** Label shown to the user */\n label: string;\n\n /** Blocks */\n blocks: Block[];\n}\n\n/** Root of block structure value */\nexport interface ProjectStructure {\n /** The structure */\n readonly groups: BlockGroup[];\n}\n\nexport const InitialBlockStructure: ProjectStructure = {\n groups: [{ id: \"default\", label: \"Default\", blocks: [] }],\n};\n\n/** Root of project rendering state */\nexport interface ProjectRenderingState {\n /** Timestamp of last staging rendering event */\n stagingRefreshTimestamp: number;\n\n /** Blocks with detached production state, i.e. some of the dependencies of\n * such blocks were re-rendered, but their state was left \"as is\". */\n blocksInLimbo: string[];\n}\n\nexport const InitialProjectRenderingState: ProjectRenderingState = {\n stagingRefreshTimestamp: 0,\n blocksInLimbo: [],\n};\n\nexport const InitialBlockMeta: ProjectMeta = {\n label: \"New Project\",\n};\n\n//\n// Pl Model\n//\n\nexport const ProjectResourceType: ResourceType = { name: \"UserProject\", version: \"2\" };\n\nexport const SchemaVersionKey = \"SchemaVersion\";\nexport const SchemaVersionV2 = \"2\";\nexport const SchemaVersionV3 = \"3\";\nexport const SchemaVersionCurrent = \"4\";\n\nexport const ProjectCreatedTimestamp = \"ProjectCreated\";\nexport const ProjectLastModifiedTimestamp = \"ProjectLastModified\";\nexport const ProjectMetaKey = \"ProjectMeta\";\nexport const ProjectStructureKey = \"ProjectStructure\";\nexport const BlockRenderingStateKey = \"BlockRenderingState\";\n\nexport const BlockArgsAuthorKeyPrefix = \"BlockArgsAuthor/\";\nexport const BlockArgsAuthorKeyPattern = /^BlockArgsAuthor\\/(?<blockid>.*)$/;\n\nexport function blockArgsAuthorKey(blockId: string): string {\n return `${BlockArgsAuthorKeyPrefix}${blockId}`;\n}\n\nexport const ProjectStructureAuthorKey = \"ProjectStructureAuthor\";\n\nexport const ServiceTemplateCacheFieldPrefix = \"__serviceTemplate_\";\n\nexport function getServiceTemplateField(hash: string): string {\n return `${ServiceTemplateCacheFieldPrefix}${hash}`;\n}\n\nexport interface ProjectField {\n blockId: string;\n fieldName:\n | \"blockPack\"\n | \"blockSettings\"\n | \"blockStorage\" // Persistent storage for v3 blocks (state, plugins data, etc.)\n | \"prodArgs\"\n | \"currentArgs\"\n | \"currentPrerunArgs\" // Derived args for staging/prerun rendering (from prerunArgs() or args())\n | \"inputsValid\" // Model API v2: errors found in currentArgs, none when the block may run, with the fingerprint of those args; present while currentArgs are\n | \"prodChainCtx\" // Pre-built production context chain node (accumulated prodCtx from all blocks above)\n | \"prodCtx\"\n | \"prodUiCtx\"\n | \"prodOutput\"\n | \"prodCtxPrevious\"\n | \"prodUiCtxPrevious\"\n | \"prodOutputPrevious\"\n | \"stagingCtx\"\n | \"stagingUiCtx\"\n | \"stagingOutput\"\n | \"stagingCtxPrevious\"\n | \"stagingUiCtxPrevious\"\n | \"stagingOutputPrevious\";\n}\n\nexport const FieldsToDuplicate: Set<ProjectField[\"fieldName\"]> = new Set([\n \"blockPack\",\n \"blockSettings\",\n \"blockStorage\",\n \"prodArgs\",\n \"currentArgs\",\n \"currentPrerunArgs\",\n \"inputsValid\",\n \"prodCtx\",\n \"prodUiCtx\",\n \"prodOutput\",\n]);\n\nexport function projectFieldName(blockId: string, fieldName: ProjectField[\"fieldName\"]): string {\n return `${blockId}-${fieldName}`;\n}\n\nconst projectFieldPattern =\n /^(?<blockId>.*)-(?<fieldName>blockPack|blockSettings|blockStorage|inputsValid|prodArgs|currentArgs|currentPrerunArgs|prodChainCtx|prodCtx|prodUiCtx|prodOutput|prodCtxPrevious|prodUiCtxPrevious|prodOutputPrevious|stagingCtx|stagingUiCtx|stagingOutput|stagingCtxPrevious|stagingUiCtxPrevious|stagingOutputPrevious)$/;\n\nexport function parseProjectField(name: string): ProjectField | undefined {\n const match = name.match(projectFieldPattern);\n if (match === null) return undefined;\n const { blockId, fieldName } = match.groups!;\n return { blockId, fieldName } as ProjectField;\n}\n"],"mappings":";AAgDA,MAAa,wBAA0C,EACrD,QAAQ,CAAC;CAAE,IAAI;CAAW,OAAO;CAAW,QAAQ,CAAC;AAAE,CAAC,EAC1D;AAYA,MAAa,+BAAsD;CACjE,yBAAyB;CACzB,eAAe,CAAC;AAClB;AAEA,MAAa,mBAAgC,EAC3C,OAAO,cACT;AAMA,MAAa,sBAAoC;CAAE,MAAM;CAAe,SAAS;AAAI;AAErF,MAAa,mBAAmB;AAGhC,MAAa,uBAAuB;AAEpC,MAAa,0BAA0B;AACvC,MAAa,+BAA+B;AAC5C,MAAa,iBAAiB;AAC9B,MAAa,sBAAsB;AACnC,MAAa,yBAAyB;AAEtC,MAAa,2BAA2B;AAGxC,SAAgB,mBAAmB,SAAyB;CAC1D,OAAO,GAAG,2BAA2B;AACvC;AAEA,MAAa,4BAA4B;AAEzC,MAAa,kCAAkC;AAE/C,SAAgB,wBAAwB,MAAsB;CAC5D,OAAO,GAAG,kCAAkC;AAC9C;AA2BA,MAAa,oCAAoD,IAAI,IAAI;CACvE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,iBAAiB,SAAiB,WAA8C;CAC9F,OAAO,GAAG,QAAQ,GAAG;AACvB;AAEA,MAAM,sBACJ;AAEF,SAAgB,kBAAkB,MAAwC;CACxE,MAAM,QAAQ,KAAK,MAAM,mBAAmB;CAC5C,IAAI,UAAU,MAAM,OAAO,KAAA;CAC3B,MAAM,EAAE,SAAS,cAAc,MAAM;CACrC,OAAO;EAAE;EAAS;CAAU;AAC9B"}
|