@milaboratories/pl-middle-layer 1.45.5 → 1.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +58 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/js_render/computable_context.cjs +37 -7
- package/dist/js_render/computable_context.cjs.map +1 -1
- package/dist/js_render/computable_context.d.ts.map +1 -1
- package/dist/js_render/computable_context.js +37 -7
- package/dist/js_render/computable_context.js.map +1 -1
- package/dist/js_render/context.cjs +12 -4
- package/dist/js_render/context.cjs.map +1 -1
- package/dist/js_render/context.d.ts +9 -0
- package/dist/js_render/context.d.ts.map +1 -1
- package/dist/js_render/context.js +12 -4
- package/dist/js_render/context.js.map +1 -1
- package/dist/js_render/index.cjs +1 -1
- package/dist/js_render/index.cjs.map +1 -1
- package/dist/js_render/index.js +1 -1
- package/dist/js_render/index.js.map +1 -1
- package/dist/middle_layer/block.cjs +7 -8
- package/dist/middle_layer/block.cjs.map +1 -1
- package/dist/middle_layer/block.d.ts +4 -4
- package/dist/middle_layer/block.d.ts.map +1 -1
- package/dist/middle_layer/block.js +7 -8
- package/dist/middle_layer/block.js.map +1 -1
- package/dist/middle_layer/block_ctx.cjs +67 -13
- package/dist/middle_layer/block_ctx.cjs.map +1 -1
- package/dist/middle_layer/block_ctx.d.ts +4 -7
- package/dist/middle_layer/block_ctx.d.ts.map +1 -1
- package/dist/middle_layer/block_ctx.js +68 -14
- package/dist/middle_layer/block_ctx.js.map +1 -1
- package/dist/middle_layer/block_ctx_unsafe.cjs +10 -3
- package/dist/middle_layer/block_ctx_unsafe.cjs.map +1 -1
- package/dist/middle_layer/block_ctx_unsafe.d.ts +1 -1
- package/dist/middle_layer/block_ctx_unsafe.d.ts.map +1 -1
- package/dist/middle_layer/block_ctx_unsafe.js +10 -3
- package/dist/middle_layer/block_ctx_unsafe.js.map +1 -1
- package/dist/middle_layer/frontend_path.cjs +1 -0
- package/dist/middle_layer/frontend_path.cjs.map +1 -1
- package/dist/middle_layer/frontend_path.js +1 -0
- package/dist/middle_layer/frontend_path.js.map +1 -1
- package/dist/middle_layer/middle_layer.cjs +1 -0
- package/dist/middle_layer/middle_layer.cjs.map +1 -1
- package/dist/middle_layer/middle_layer.d.ts +1 -1
- package/dist/middle_layer/middle_layer.d.ts.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 +75 -28
- package/dist/middle_layer/project.cjs.map +1 -1
- package/dist/middle_layer/project.d.ts +34 -7
- package/dist/middle_layer/project.d.ts.map +1 -1
- package/dist/middle_layer/project.js +76 -29
- package/dist/middle_layer/project.js.map +1 -1
- package/dist/middle_layer/project_overview.cjs +32 -11
- package/dist/middle_layer/project_overview.cjs.map +1 -1
- package/dist/middle_layer/project_overview.d.ts.map +1 -1
- package/dist/middle_layer/project_overview.js +32 -11
- package/dist/middle_layer/project_overview.js.map +1 -1
- package/dist/middle_layer/render.cjs +1 -1
- package/dist/middle_layer/render.cjs.map +1 -1
- package/dist/middle_layer/render.js +1 -1
- package/dist/middle_layer/render.js.map +1 -1
- package/dist/middle_layer/render.test.d.ts.map +1 -1
- package/dist/model/block_storage_helper.cjs +210 -0
- package/dist/model/block_storage_helper.cjs.map +1 -0
- package/dist/model/block_storage_helper.d.ts +98 -0
- package/dist/model/block_storage_helper.d.ts.map +1 -0
- package/dist/model/block_storage_helper.js +153 -0
- package/dist/model/block_storage_helper.js.map +1 -0
- package/dist/model/index.d.ts +2 -1
- package/dist/model/index.d.ts.map +1 -1
- package/dist/model/project_helper.cjs +177 -0
- package/dist/model/project_helper.cjs.map +1 -1
- package/dist/model/project_helper.d.ts +110 -1
- package/dist/model/project_helper.d.ts.map +1 -1
- package/dist/model/project_helper.js +178 -1
- package/dist/model/project_helper.js.map +1 -1
- package/dist/model/project_model.cjs +6 -3
- package/dist/model/project_model.cjs.map +1 -1
- package/dist/model/project_model.d.ts +3 -2
- package/dist/model/project_model.d.ts.map +1 -1
- package/dist/model/project_model.js +6 -4
- package/dist/model/project_model.js.map +1 -1
- package/dist/mutator/block-pack/block_pack.cjs +1 -2
- package/dist/mutator/block-pack/block_pack.cjs.map +1 -1
- package/dist/mutator/block-pack/block_pack.d.ts.map +1 -1
- package/dist/mutator/block-pack/block_pack.js +1 -2
- package/dist/mutator/block-pack/block_pack.js.map +1 -1
- package/dist/mutator/block-pack/frontend.cjs +1 -0
- package/dist/mutator/block-pack/frontend.cjs.map +1 -1
- package/dist/mutator/block-pack/frontend.js +1 -0
- package/dist/mutator/block-pack/frontend.js.map +1 -1
- package/dist/mutator/migration.cjs +64 -3
- package/dist/mutator/migration.cjs.map +1 -1
- package/dist/mutator/migration.d.ts.map +1 -1
- package/dist/mutator/migration.js +66 -5
- package/dist/mutator/migration.js.map +1 -1
- package/dist/mutator/project-v3.test.d.ts +2 -0
- package/dist/mutator/project-v3.test.d.ts.map +1 -0
- package/dist/mutator/project.cjs +282 -41
- package/dist/mutator/project.cjs.map +1 -1
- package/dist/mutator/project.d.ts +77 -12
- package/dist/mutator/project.d.ts.map +1 -1
- package/dist/mutator/project.js +283 -42
- package/dist/mutator/project.js.map +1 -1
- package/dist/pool/result_pool.cjs +9 -6
- package/dist/pool/result_pool.cjs.map +1 -1
- package/dist/pool/result_pool.d.ts.map +1 -1
- package/dist/pool/result_pool.js +9 -6
- package/dist/pool/result_pool.js.map +1 -1
- package/package.json +15 -15
- package/src/js_render/computable_context.ts +37 -7
- package/src/js_render/context.ts +12 -5
- package/src/js_render/index.ts +1 -1
- package/src/middle_layer/block.ts +13 -14
- package/src/middle_layer/block_ctx.ts +70 -23
- package/src/middle_layer/block_ctx_unsafe.ts +11 -4
- package/src/middle_layer/middle_layer.ts +2 -1
- package/src/middle_layer/project.ts +86 -40
- package/src/middle_layer/project_overview.ts +44 -20
- package/src/middle_layer/render.test.ts +1 -1
- package/src/middle_layer/render.ts +1 -1
- package/src/model/block_storage_helper.ts +213 -0
- package/src/model/index.ts +2 -1
- package/src/model/project_helper.ts +249 -1
- package/src/model/project_model.ts +9 -5
- package/src/mutator/block-pack/block_pack.ts +1 -2
- package/src/mutator/migration.ts +79 -6
- package/src/mutator/project-v3.test.ts +280 -0
- package/src/mutator/project.test.ts +27 -27
- package/src/mutator/project.ts +351 -68
- package/src/pool/result_pool.ts +11 -4
package/src/mutator/project.ts
CHANGED
|
@@ -5,7 +5,8 @@ import type {
|
|
|
5
5
|
PlTransaction,
|
|
6
6
|
ResourceData,
|
|
7
7
|
ResourceId,
|
|
8
|
-
TxOps
|
|
8
|
+
TxOps,
|
|
9
|
+
} from '@milaboratories/pl-client';
|
|
9
10
|
import {
|
|
10
11
|
ensureResourceIdNotNull,
|
|
11
12
|
field,
|
|
@@ -65,7 +66,7 @@ import {
|
|
|
65
66
|
import Denque from 'denque';
|
|
66
67
|
import { exportContext, getPreparedExportTemplateEnvelope } from './context_export';
|
|
67
68
|
import { loadTemplate } from './template/template_loading';
|
|
68
|
-
import { cachedDeserialize, notEmpty,
|
|
69
|
+
import { cachedDeserialize, notEmpty, canonicalJsonBytes, cachedDecode } from '@milaboratories/ts-helpers';
|
|
69
70
|
import type { ProjectHelper } from '../model/project_helper';
|
|
70
71
|
import { extractConfig, UiError, type BlockConfig } from '@platforma-sdk/model';
|
|
71
72
|
import { getDebugFlags } from '../debug';
|
|
@@ -141,12 +142,34 @@ class BlockInfo {
|
|
|
141
142
|
|
|
142
143
|
if (this.fields.blockPack === undefined) throw new Error('no block pack field');
|
|
143
144
|
|
|
144
|
-
if (this.fields.
|
|
145
|
+
if (this.fields.blockStorage === undefined) throw new Error('no block storage field');
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
private readonly currentArgsC = cached(
|
|
148
|
-
() => this.fields.currentArgs
|
|
149
|
-
() =>
|
|
149
|
+
() => this.fields.currentArgs?.modCount,
|
|
150
|
+
() => {
|
|
151
|
+
const bin = this.fields.currentArgs?.value;
|
|
152
|
+
if (bin === undefined) return undefined;
|
|
153
|
+
return cachedDeserialize(bin);
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
private readonly blockStorageC = cached(
|
|
158
|
+
() => this.fields.blockStorage!.modCount,
|
|
159
|
+
() => {
|
|
160
|
+
const bin = this.fields.blockStorage?.value;
|
|
161
|
+
if (bin === undefined) return undefined;
|
|
162
|
+
return cachedDeserialize<Record<string, unknown>>(bin);
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
private readonly blockStorageJ = cached(
|
|
167
|
+
() => this.fields.blockStorage!.modCount,
|
|
168
|
+
() => {
|
|
169
|
+
const bin = this.fields.blockStorage?.value;
|
|
170
|
+
if (bin === undefined) return undefined;
|
|
171
|
+
return cachedDecode(bin);
|
|
172
|
+
},
|
|
150
173
|
);
|
|
151
174
|
|
|
152
175
|
private readonly prodArgsC = cached(
|
|
@@ -158,10 +181,36 @@ class BlockInfo {
|
|
|
158
181
|
},
|
|
159
182
|
);
|
|
160
183
|
|
|
184
|
+
private readonly currentPrerunArgsC = cached(
|
|
185
|
+
() => this.fields.currentPrerunArgs?.modCount,
|
|
186
|
+
() => {
|
|
187
|
+
const bin = this.fields.currentPrerunArgs?.value;
|
|
188
|
+
if (bin === undefined) return undefined;
|
|
189
|
+
return cachedDeserialize(bin);
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
|
|
161
193
|
get currentArgs(): unknown {
|
|
162
194
|
return this.currentArgsC();
|
|
163
195
|
}
|
|
164
196
|
|
|
197
|
+
get blockStorage(): unknown {
|
|
198
|
+
try {
|
|
199
|
+
return this.blockStorageC();
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.error('Error getting blockStorage:', e);
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get blockStorageJson() {
|
|
207
|
+
return this.blockStorageJ();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
get currentPrerunArgs(): unknown {
|
|
211
|
+
return this.currentPrerunArgsC();
|
|
212
|
+
}
|
|
213
|
+
|
|
165
214
|
get stagingRendered(): boolean {
|
|
166
215
|
return this.fields.stagingCtx !== undefined;
|
|
167
216
|
}
|
|
@@ -181,14 +230,17 @@ class BlockInfo {
|
|
|
181
230
|
|| Buffer.compare(this.fields.currentArgs!.value!, this.fields.prodArgs.value!) !== 0,
|
|
182
231
|
);
|
|
183
232
|
|
|
184
|
-
// get productionStale(): boolean {
|
|
185
|
-
// return this.productionRendered && this.productionStaleC() && ;
|
|
186
|
-
// }
|
|
187
|
-
|
|
188
233
|
get requireProductionRendering(): boolean {
|
|
189
234
|
return !this.productionRendered || this.productionStaleC() || this.productionHasErrors;
|
|
190
235
|
}
|
|
191
236
|
|
|
237
|
+
/** Returns true if staging should be re-rendered (stagingCtx is not set) */
|
|
238
|
+
get requireStagingRendering(): boolean {
|
|
239
|
+
// No staging needed if currentPrerunArgs is undefined (args derivation failed)
|
|
240
|
+
if (this.fields.currentPrerunArgs === undefined) return false;
|
|
241
|
+
return !this.stagingRendered;
|
|
242
|
+
}
|
|
243
|
+
|
|
192
244
|
get prodArgs(): unknown {
|
|
193
245
|
return this.prodArgsC();
|
|
194
246
|
}
|
|
@@ -202,33 +254,38 @@ class BlockInfo {
|
|
|
202
254
|
}
|
|
203
255
|
}
|
|
204
256
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Specification for creating a new block.
|
|
259
|
+
* Discriminated union based on `storageMode`.
|
|
260
|
+
*/
|
|
261
|
+
/** Specification for creating a new block. Discriminated union based on `storageMode`. */
|
|
262
|
+
export type NewBlockSpec =
|
|
263
|
+
| { storageMode: 'fromModel'; blockPack: BlockPackSpecPrepared }
|
|
264
|
+
| { storageMode: 'legacy'; blockPack: BlockPackSpecPrepared; legacyState: string };
|
|
210
265
|
|
|
211
266
|
const NoNewBlocks = (blockId: string) => {
|
|
212
267
|
throw new Error(`No new block info for ${blockId}`);
|
|
213
268
|
};
|
|
214
269
|
|
|
215
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Request to set block state using unified state format.
|
|
272
|
+
* For v3 blocks: state is the block's state
|
|
273
|
+
* For v1/v2 blocks: state should be { args, uiState } format
|
|
274
|
+
*/
|
|
275
|
+
export type SetStatesRequest = {
|
|
216
276
|
blockId: string;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
277
|
+
/** The unified state to set */
|
|
278
|
+
state: unknown;
|
|
279
|
+
modelAPIVersion: 1;
|
|
280
|
+
} | {
|
|
281
|
+
blockId: string;
|
|
282
|
+
/** Storage operation payload - middle layer is agnostic to specific operations */
|
|
283
|
+
payload: { operation: string; value: unknown };
|
|
284
|
+
modelAPIVersion: 2;
|
|
285
|
+
};
|
|
220
286
|
|
|
221
|
-
type
|
|
222
|
-
|
|
223
|
-
| 'stagingDownstream'
|
|
224
|
-
| 'futureProductionUpstream'
|
|
225
|
-
| 'futureProductionDownstream'
|
|
226
|
-
| 'actualProductionUpstream'
|
|
227
|
-
| 'actualProductionDownstream';
|
|
228
|
-
|
|
229
|
-
export type ArgsAndUiState = {
|
|
230
|
-
args: unknown;
|
|
231
|
-
uiState: unknown;
|
|
287
|
+
export type ClearState = {
|
|
288
|
+
state: unknown;
|
|
232
289
|
};
|
|
233
290
|
|
|
234
291
|
export class ProjectMutator {
|
|
@@ -295,7 +352,6 @@ export class ProjectMutator {
|
|
|
295
352
|
}
|
|
296
353
|
|
|
297
354
|
get structure(): ProjectStructure {
|
|
298
|
-
// clone
|
|
299
355
|
return JSON.parse(JSON.stringify(this.struct)) as ProjectStructure;
|
|
300
356
|
}
|
|
301
357
|
|
|
@@ -315,7 +371,7 @@ export class ProjectMutator {
|
|
|
315
371
|
private getProductionGraphBlockInfo(blockId: string, prod: boolean): ProductionGraphBlockInfo | undefined {
|
|
316
372
|
const bInfo = this.getBlockInfo(blockId);
|
|
317
373
|
|
|
318
|
-
let argsField: BlockFieldState;
|
|
374
|
+
let argsField: BlockFieldState | undefined;
|
|
319
375
|
let args: unknown;
|
|
320
376
|
|
|
321
377
|
if (prod) {
|
|
@@ -323,10 +379,15 @@ export class ProjectMutator {
|
|
|
323
379
|
argsField = bInfo.fields.prodArgs;
|
|
324
380
|
args = bInfo.prodArgs;
|
|
325
381
|
} else {
|
|
326
|
-
argsField =
|
|
382
|
+
argsField = bInfo.fields.currentArgs;
|
|
327
383
|
args = bInfo.currentArgs;
|
|
328
384
|
}
|
|
329
385
|
|
|
386
|
+
// Can't compute enrichment targets without args
|
|
387
|
+
if (argsField === undefined) {
|
|
388
|
+
return { args, enrichmentTargets: undefined };
|
|
389
|
+
}
|
|
390
|
+
|
|
330
391
|
const blockPackField = notEmpty(bInfo.fields.blockPack);
|
|
331
392
|
|
|
332
393
|
if (isResourceId(argsField.ref!) && isResourceId(blockPackField.ref!))
|
|
@@ -371,6 +432,7 @@ export class ProjectMutator {
|
|
|
371
432
|
}
|
|
372
433
|
|
|
373
434
|
private createJsonFieldValueByContent(content: string): BlockFieldStateValue {
|
|
435
|
+
if (content === undefined) throw new Error('content is undefined');
|
|
374
436
|
const value = Buffer.from(content);
|
|
375
437
|
const ref = this.tx.createValue(Pl.JsonObject, value);
|
|
376
438
|
return { ref, value, status: 'Ready' };
|
|
@@ -508,6 +570,70 @@ export class ProjectMutator {
|
|
|
508
570
|
}
|
|
509
571
|
}
|
|
510
572
|
|
|
573
|
+
/**
|
|
574
|
+
* Gets current block state and merges with partial updates.
|
|
575
|
+
* Used by legacy v1/v2 methods like setBlockArgs and setUiState.
|
|
576
|
+
*
|
|
577
|
+
* @param blockId The block to get state for
|
|
578
|
+
* @param partialUpdate Partial state to merge (e.g. { args } or { uiState })
|
|
579
|
+
* @returns Merged state in unified format { args, uiState }
|
|
580
|
+
*/
|
|
581
|
+
public mergeBlockState(
|
|
582
|
+
blockId: string,
|
|
583
|
+
partialUpdate: { args?: unknown; uiState?: unknown },
|
|
584
|
+
): { args?: unknown; uiState?: unknown } {
|
|
585
|
+
const info = this.getBlockInfo(blockId);
|
|
586
|
+
const currentState = info.blockStorage as { args?: unknown; uiState?: unknown } | undefined;
|
|
587
|
+
return { ...currentState, ...partialUpdate };
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Sets raw block storage content directly (for testing purposes).
|
|
592
|
+
* This bypasses all normalization and VM transformations.
|
|
593
|
+
*
|
|
594
|
+
* @param blockId The block to set storage for
|
|
595
|
+
* @param rawStorageJson Raw storage as JSON string
|
|
596
|
+
*/
|
|
597
|
+
public setBlockStorageRaw(blockId: string, rawStorageJson: string): void {
|
|
598
|
+
this.setBlockFieldObj(blockId, 'blockStorage', this.createJsonFieldValueByContent(rawStorageJson));
|
|
599
|
+
this.blocksWithChangedInputs.add(blockId);
|
|
600
|
+
this.updateLastModified();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Resets a v2+ block to its initial storage state.
|
|
605
|
+
* Gets initial storage from VM and derives args from it.
|
|
606
|
+
*
|
|
607
|
+
* For v1 blocks, use setStates() instead.
|
|
608
|
+
*
|
|
609
|
+
* @param blockId The block to reset
|
|
610
|
+
*/
|
|
611
|
+
public resetToInitialStorage(blockId: string): void {
|
|
612
|
+
const info = this.getBlockInfo(blockId);
|
|
613
|
+
const blockConfig = info.config;
|
|
614
|
+
|
|
615
|
+
if (blockConfig.modelAPIVersion !== 2) {
|
|
616
|
+
throw new Error('resetToInitialStorage is only supported for model API version 2');
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Get initial storage from VM
|
|
620
|
+
const initialStorageJson = this.projectHelper.getInitialStorageInVM(blockConfig);
|
|
621
|
+
this.setBlockStorageRaw(blockId, initialStorageJson);
|
|
622
|
+
|
|
623
|
+
// Derive args from storage - set or clear currentArgs based on derivation result
|
|
624
|
+
const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(blockConfig, initialStorageJson);
|
|
625
|
+
if (!deriveArgsResult.error) {
|
|
626
|
+
this.setBlockFieldObj(blockId, 'currentArgs', this.createJsonFieldValue(deriveArgsResult.value));
|
|
627
|
+
// Derive prerunArgs from storage
|
|
628
|
+
const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(blockConfig, initialStorageJson);
|
|
629
|
+
if (prerunArgs !== undefined) {
|
|
630
|
+
this.setBlockFieldObj(blockId, 'currentPrerunArgs', this.createJsonFieldValue(prerunArgs));
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
this.deleteBlockFields(blockId, 'currentArgs');
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
511
637
|
/** Optimally sets inputs for multiple blocks in one go */
|
|
512
638
|
public setStates(requests: SetStatesRequest[]) {
|
|
513
639
|
const changedArgs: string[] = [];
|
|
@@ -515,34 +641,87 @@ export class ProjectMutator {
|
|
|
515
641
|
for (const req of requests) {
|
|
516
642
|
const info = this.getBlockInfo(req.blockId);
|
|
517
643
|
let blockChanged = false;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
644
|
+
|
|
645
|
+
const blockConfig = info.config;
|
|
646
|
+
// modelAPIVersion === 2 means BlockModelV3 with .args() lambda for deriving args
|
|
647
|
+
|
|
648
|
+
if (req.modelAPIVersion !== blockConfig.modelAPIVersion) {
|
|
649
|
+
throw new Error(`Model API version mismatch for block ${req.blockId}: ${req.modelAPIVersion} !== ${blockConfig.modelAPIVersion}`);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Derive args from storage using the block's config.args() callback
|
|
653
|
+
let args: unknown;
|
|
654
|
+
let prerunArgs: unknown;
|
|
655
|
+
|
|
656
|
+
if (req.modelAPIVersion === 2) {
|
|
657
|
+
const currentStorageJson = info.blockStorageJson;
|
|
658
|
+
if (currentStorageJson === undefined) {
|
|
659
|
+
throw new Error(`Block ${req.blockId} has no blockStorage - this should not happen`);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Apply the state update to storage
|
|
663
|
+
const updatedStorageJson = this.projectHelper.applyStorageUpdateInVM(
|
|
664
|
+
blockConfig,
|
|
665
|
+
currentStorageJson,
|
|
666
|
+
req.payload,
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
this.setBlockFieldObj(req.blockId, 'blockStorage', this.createJsonFieldValueByContent(updatedStorageJson));
|
|
670
|
+
|
|
671
|
+
// Derive args directly from storage (VM extracts data internally)
|
|
672
|
+
const derivedArgsResult = this.projectHelper.deriveArgsFromStorage(blockConfig, updatedStorageJson);
|
|
673
|
+
if (derivedArgsResult.error) {
|
|
674
|
+
args = undefined;
|
|
675
|
+
prerunArgs = undefined;
|
|
533
676
|
} else {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
677
|
+
args = derivedArgsResult.value;
|
|
678
|
+
// Derive prerunArgs from storage, or fall back to args
|
|
679
|
+
prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(blockConfig, updatedStorageJson);
|
|
680
|
+
}
|
|
681
|
+
} else {
|
|
682
|
+
this.setBlockFieldObj(req.blockId, 'blockStorage', this.createJsonFieldValue(req.state));
|
|
683
|
+
if (req.state !== null && typeof req.state === 'object' && 'args' in req.state) {
|
|
684
|
+
args = (req.state as { args: unknown }).args;
|
|
685
|
+
} else {
|
|
686
|
+
args = req.state;
|
|
687
|
+
}
|
|
688
|
+
// For the legacy blocks, prerunArgs = args (same as production args)
|
|
689
|
+
prerunArgs = args;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Set or clear currentArgs based on derivation result
|
|
693
|
+
if (args !== undefined) {
|
|
694
|
+
const currentArgsData = canonicalJsonBytes(args);
|
|
695
|
+
const argsPartRef = this.tx.createValue(Pl.JsonObject, currentArgsData);
|
|
696
|
+
this.setBlockField(req.blockId, 'currentArgs', argsPartRef, 'Ready', currentArgsData);
|
|
697
|
+
} else {
|
|
698
|
+
this.deleteBlockFields(req.blockId, 'currentArgs');
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Set currentPrerunArgs field and check if it actually changed
|
|
702
|
+
let prerunArgsChanged = false;
|
|
703
|
+
if (prerunArgs !== undefined) {
|
|
704
|
+
const prerunArgsData = canonicalJsonBytes(prerunArgs);
|
|
705
|
+
const oldPrerunArgsData = info.fields.currentPrerunArgs?.value;
|
|
706
|
+
// Check if prerunArgs actually changed
|
|
707
|
+
if (oldPrerunArgsData === undefined || Buffer.compare(oldPrerunArgsData, prerunArgsData) !== 0) {
|
|
708
|
+
prerunArgsChanged = true;
|
|
709
|
+
}
|
|
710
|
+
const prerunArgsRef = this.tx.createValue(Pl.JsonObject, prerunArgsData);
|
|
711
|
+
this.setBlockField(req.blockId, 'currentPrerunArgs', prerunArgsRef, 'Ready', prerunArgsData);
|
|
712
|
+
} else {
|
|
713
|
+
// prerunArgs is undefined - check if we previously had one
|
|
714
|
+
if (info.fields.currentPrerunArgs !== undefined) {
|
|
715
|
+
prerunArgsChanged = true;
|
|
537
716
|
}
|
|
538
|
-
|
|
539
|
-
// console.log('setting', fieldName, gzipped, data.length);
|
|
540
|
-
const statePartRef = this.tx.createValue(gzipped ? Pl.JsonGzObject : Pl.JsonObject, data);
|
|
541
|
-
this.setBlockField(req.blockId, fieldName, statePartRef, 'Ready', data);
|
|
717
|
+
}
|
|
542
718
|
|
|
543
|
-
|
|
544
|
-
|
|
719
|
+
blockChanged = true;
|
|
720
|
+
// Only add to changedArgs if prerunArgs changed - this controls staging reset
|
|
721
|
+
if (prerunArgsChanged) {
|
|
722
|
+
changedArgs.push(req.blockId);
|
|
545
723
|
}
|
|
724
|
+
|
|
546
725
|
if (blockChanged) {
|
|
547
726
|
// will be assigned our author marker
|
|
548
727
|
this.blocksWithChangedInputs.add(req.blockId);
|
|
@@ -589,19 +768,30 @@ export class ProjectMutator {
|
|
|
589
768
|
return exportContext(this.tx, Pl.unwrapHolder(this.tx, this.ctxExportTplHolder), ctx);
|
|
590
769
|
}
|
|
591
770
|
|
|
771
|
+
/**
|
|
772
|
+
* Renders staging for a block using currentPrerunArgs.
|
|
773
|
+
* If currentPrerunArgs is not set (prerunArgs returned undefined), skips staging for this block.
|
|
774
|
+
*/
|
|
592
775
|
private renderStagingFor(blockId: string) {
|
|
593
776
|
this.resetStaging(blockId);
|
|
594
777
|
|
|
595
778
|
const info = this.getBlockInfo(blockId);
|
|
596
779
|
|
|
780
|
+
// If currentPrerunArgs is not set (prerunArgs returned undefined), skip staging for this block
|
|
781
|
+
const prerunArgsRef = info.fields.currentPrerunArgs?.ref;
|
|
782
|
+
if (prerunArgsRef === undefined) {
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
|
|
597
786
|
const ctx = this.createStagingCtx(this.getStagingGraph().nodes.get(blockId)!.upstream);
|
|
598
787
|
|
|
599
788
|
if (this.getBlock(blockId).renderingMode !== 'Heavy') throw new Error('not supported yet');
|
|
600
789
|
|
|
601
790
|
const tpl = info.getTemplate(this.tx);
|
|
602
791
|
|
|
792
|
+
// Use currentPrerunArgs for staging rendering
|
|
603
793
|
const results = createRenderHeavyBlock(this.tx, tpl, {
|
|
604
|
-
args:
|
|
794
|
+
args: prerunArgsRef,
|
|
605
795
|
blockId: this.tx.createValue(Pl.JsonString, JSON.stringify(blockId)),
|
|
606
796
|
isProduction: this.tx.createValue(Pl.JsonBool, JSON.stringify(false)),
|
|
607
797
|
context: ctx,
|
|
@@ -628,6 +818,11 @@ export class ProjectMutator {
|
|
|
628
818
|
|
|
629
819
|
const info = this.getBlockInfo(blockId);
|
|
630
820
|
|
|
821
|
+
// Can't render production if currentArgs is not set
|
|
822
|
+
if (info.fields.currentArgs === undefined) {
|
|
823
|
+
throw new Error(`Can't render production for block ${blockId}: currentArgs not set`);
|
|
824
|
+
}
|
|
825
|
+
|
|
631
826
|
const ctx = this.createProdCtx(this.getPendingProductionGraph().nodes.get(blockId)!.upstream);
|
|
632
827
|
|
|
633
828
|
if (this.getBlock(blockId).renderingMode === 'Light')
|
|
@@ -636,7 +831,7 @@ export class ProjectMutator {
|
|
|
636
831
|
const tpl = info.getTemplate(this.tx);
|
|
637
832
|
|
|
638
833
|
const results = createRenderHeavyBlock(this.tx, tpl, {
|
|
639
|
-
args: info.fields.currentArgs
|
|
834
|
+
args: info.fields.currentArgs.ref!,
|
|
640
835
|
blockId: this.tx.createValue(Pl.JsonString, JSON.stringify(blockId)),
|
|
641
836
|
isProduction: this.tx.createValue(Pl.JsonBool, JSON.stringify(true)),
|
|
642
837
|
context: ctx,
|
|
@@ -651,7 +846,7 @@ export class ProjectMutator {
|
|
|
651
846
|
this.setBlockField(blockId, 'prodOutput', results.result, 'NotReady');
|
|
652
847
|
|
|
653
848
|
// saving inputs for which we rendered the production
|
|
654
|
-
this.setBlockFieldObj(blockId, 'prodArgs', info.fields.currentArgs
|
|
849
|
+
this.setBlockFieldObj(blockId, 'prodArgs', info.fields.currentArgs);
|
|
655
850
|
|
|
656
851
|
// removing block from limbo as we juts rendered fresh production for it
|
|
657
852
|
if (this.blocksInLimbo.delete(blockId)) this.renderingStateChanged = true;
|
|
@@ -676,11 +871,50 @@ export class ProjectMutator {
|
|
|
676
871
|
this.createJsonFieldValue(InitialBlockSettings),
|
|
677
872
|
);
|
|
678
873
|
|
|
679
|
-
|
|
680
|
-
|
|
874
|
+
const blockConfig = info.config;
|
|
875
|
+
|
|
876
|
+
let args: unknown;
|
|
877
|
+
let prerunArgs: unknown;
|
|
878
|
+
let storageToWrite: string;
|
|
879
|
+
|
|
880
|
+
if (spec.storageMode === 'fromModel') {
|
|
881
|
+
// Model API v2+: get initial storage and derive args from it
|
|
882
|
+
storageToWrite = this.projectHelper.getInitialStorageInVM(blockConfig);
|
|
883
|
+
|
|
884
|
+
// Derive args directly from storage (VM extracts data internally)
|
|
885
|
+
const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(blockConfig, storageToWrite);
|
|
886
|
+
if (deriveArgsResult.error) {
|
|
887
|
+
args = undefined;
|
|
888
|
+
prerunArgs = undefined;
|
|
889
|
+
} else {
|
|
890
|
+
args = deriveArgsResult.value;
|
|
891
|
+
prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(blockConfig, storageToWrite);
|
|
892
|
+
}
|
|
893
|
+
} else if (spec.storageMode === 'legacy') {
|
|
894
|
+
// Model API v1: use legacyState from spec
|
|
895
|
+
const parsedState = JSON.parse(spec.legacyState);
|
|
896
|
+
args = parsedState.args;
|
|
897
|
+
if (args === undefined) {
|
|
898
|
+
throw new Error('args is undefined in legacyState');
|
|
899
|
+
}
|
|
900
|
+
prerunArgs = args;
|
|
901
|
+
storageToWrite = spec.legacyState;
|
|
902
|
+
} else {
|
|
903
|
+
throw new Error(`Unknown storageMode: ${(spec as NewBlockSpec).storageMode}`);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// currentArgs
|
|
907
|
+
if (args !== undefined) {
|
|
908
|
+
this.setBlockFieldObj(blockId, 'currentArgs', this.createJsonFieldValue(args));
|
|
909
|
+
}
|
|
681
910
|
|
|
682
|
-
//
|
|
683
|
-
|
|
911
|
+
// currentPrerunArgs
|
|
912
|
+
if (prerunArgs !== undefined) {
|
|
913
|
+
this.setBlockFieldObj(blockId, 'currentPrerunArgs', this.createJsonFieldValue(prerunArgs));
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// blockStorage
|
|
917
|
+
this.setBlockFieldObj(blockId, 'blockStorage', this.createJsonFieldValueByContent(storageToWrite));
|
|
684
918
|
|
|
685
919
|
// checking structure
|
|
686
920
|
info.check();
|
|
@@ -888,8 +1122,9 @@ export class ProjectMutator {
|
|
|
888
1122
|
// Block-pack migration
|
|
889
1123
|
//
|
|
890
1124
|
|
|
891
|
-
public migrateBlockPack(blockId: string, spec: BlockPackSpecPrepared,
|
|
1125
|
+
public migrateBlockPack(blockId: string, spec: BlockPackSpecPrepared, newClearState?: ClearState): void {
|
|
892
1126
|
const info = this.getBlockInfo(blockId);
|
|
1127
|
+
const newConfig = extractConfig(spec.config);
|
|
893
1128
|
|
|
894
1129
|
this.setBlockField(
|
|
895
1130
|
blockId,
|
|
@@ -898,10 +1133,52 @@ export class ProjectMutator {
|
|
|
898
1133
|
'NotReady',
|
|
899
1134
|
);
|
|
900
1135
|
|
|
901
|
-
if (
|
|
902
|
-
//
|
|
903
|
-
|
|
1136
|
+
if (newClearState !== undefined) {
|
|
1137
|
+
// State is being reset - no migration needed
|
|
1138
|
+
const supportsStorageFromVM = newConfig.modelAPIVersion === 2;
|
|
1139
|
+
|
|
1140
|
+
if (supportsStorageFromVM) {
|
|
1141
|
+
// V2+: Get initial storage directly from VM and derive args from it
|
|
1142
|
+
const initialStorageJson = this.projectHelper.getInitialStorageInVM(newConfig);
|
|
1143
|
+
this.setBlockStorageRaw(blockId, initialStorageJson);
|
|
1144
|
+
|
|
1145
|
+
// Derive args from storage - only set currentArgs if derivation succeeds
|
|
1146
|
+
const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(newConfig, initialStorageJson);
|
|
1147
|
+
if (!deriveArgsResult.error) {
|
|
1148
|
+
this.setBlockFieldObj(blockId, 'currentArgs', this.createJsonFieldValue(deriveArgsResult.value));
|
|
1149
|
+
// Derive prerunArgs from storage
|
|
1150
|
+
const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(newConfig, initialStorageJson);
|
|
1151
|
+
if (prerunArgs !== undefined) {
|
|
1152
|
+
this.setBlockFieldObj(blockId, 'currentPrerunArgs', this.createJsonFieldValue(prerunArgs));
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
this.blocksWithChangedInputs.add(blockId);
|
|
1156
|
+
this.updateLastModified();
|
|
1157
|
+
} else {
|
|
1158
|
+
// V1: Use setStates with legacy state format
|
|
1159
|
+
this.setStates([{ modelAPIVersion: 1, blockId, state: newClearState.state }]);
|
|
1160
|
+
}
|
|
904
1161
|
} else {
|
|
1162
|
+
// State is being preserved - run migrations if needed via VM
|
|
1163
|
+
// Only Model API v2 blocks support migrations
|
|
1164
|
+
const supportsStateMigrations = newConfig.modelAPIVersion === 2;
|
|
1165
|
+
|
|
1166
|
+
if (supportsStateMigrations) {
|
|
1167
|
+
const currentStorageJson = info.blockStorageJson;
|
|
1168
|
+
|
|
1169
|
+
const migrationResult = this.projectHelper.migrateStorageInVM(newConfig, currentStorageJson);
|
|
1170
|
+
|
|
1171
|
+
if (migrationResult.error !== undefined) {
|
|
1172
|
+
console.error(`[migrateBlockPack] Block ${blockId} migration error: ${migrationResult.error}`);
|
|
1173
|
+
} else {
|
|
1174
|
+
console.log(`[migrateBlockPack] Block ${blockId}: ${migrationResult.info}`);
|
|
1175
|
+
if (migrationResult.warn) {
|
|
1176
|
+
console.warn(`[migrateBlockPack] Block ${blockId} migration warning: ${migrationResult.warn}`);
|
|
1177
|
+
}
|
|
1178
|
+
this.setBlockStorageRaw(blockId, migrationResult.newStorageJson);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
905
1182
|
// resetting staging outputs for all downstream blocks
|
|
906
1183
|
this.getStagingGraph().traverse('downstream', [blockId], ({ id }) => this.resetStaging(id));
|
|
907
1184
|
}
|
|
@@ -1023,12 +1300,17 @@ export class ProjectMutator {
|
|
|
1023
1300
|
const stagingGraph = this.getStagingGraph();
|
|
1024
1301
|
stagingGraph.nodes.forEach((node) => {
|
|
1025
1302
|
const info = this.getBlockInfo(node.id);
|
|
1026
|
-
|
|
1303
|
+
// Use requireStagingRendering to check both: staging exists AND prerunArgs hasn't changed
|
|
1304
|
+
const requiresRendering = info.requireStagingRendering;
|
|
1305
|
+
let lag = requiresRendering ? 1 : 0;
|
|
1027
1306
|
node.upstream.forEach((upstream) => {
|
|
1028
1307
|
const upstreamLag = lags.get(upstream)!;
|
|
1029
1308
|
if (upstreamLag === 0) return;
|
|
1030
1309
|
lag = Math.max(upstreamLag + 1, lag);
|
|
1031
1310
|
});
|
|
1311
|
+
if (!requiresRendering && info.stagingRendered) {
|
|
1312
|
+
// console.log(`[traverseWithStagingLag] SKIP staging for ${node.id} - prerunArgs unchanged`);
|
|
1313
|
+
}
|
|
1032
1314
|
cb(node.id, lag);
|
|
1033
1315
|
lags.set(node.id, lag);
|
|
1034
1316
|
});
|
|
@@ -1047,6 +1329,7 @@ export class ProjectMutator {
|
|
|
1047
1329
|
// meaning staging already rendered
|
|
1048
1330
|
return;
|
|
1049
1331
|
if (lagThreshold === undefined || lag <= lagThreshold) {
|
|
1332
|
+
// console.log(`[refreshStagings] RENDER staging for ${blockId} (lag=${lag})`);
|
|
1050
1333
|
this.renderStagingFor(blockId);
|
|
1051
1334
|
rendered++;
|
|
1052
1335
|
}
|
package/src/pool/result_pool.ts
CHANGED
|
@@ -85,11 +85,13 @@ export class ResultPool {
|
|
|
85
85
|
if (result !== undefined) return result;
|
|
86
86
|
result = block.staging?.results?.get(exportName)?.spec;
|
|
87
87
|
if (result !== undefined) return result;
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// Note: Don't mark unstable when staging is absent - it may be intentionally skipped
|
|
89
|
+
// for v3 blocks with undefined prerunArgs.
|
|
90
|
+
if (block.staging !== undefined && !block.staging.locked)
|
|
91
|
+
this.ctx.markUnstable(`staging_not_locked:${blockId}`);
|
|
90
92
|
else if (block.prod !== undefined && !block.prod.locked)
|
|
91
93
|
this.ctx.markUnstable(`prod_not_locked:${blockId}`);
|
|
92
|
-
// if prod is
|
|
94
|
+
// if neither prod nor staging is present, returned undefined value is considered stable
|
|
93
95
|
return undefined;
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -224,7 +226,10 @@ export class ResultPool {
|
|
|
224
226
|
});
|
|
225
227
|
exportsProcessed.add(exportName);
|
|
226
228
|
}
|
|
227
|
-
}
|
|
229
|
+
}
|
|
230
|
+
// Note: Don't mark unstable when staging is absent - it may be intentionally skipped
|
|
231
|
+
// for v3 blocks with undefined prerunArgs. If prod exists, use it; if neither exists,
|
|
232
|
+
// the block simply has no specs to contribute.
|
|
228
233
|
|
|
229
234
|
if (block.prod !== undefined) {
|
|
230
235
|
if (!block.prod.locked) markUnstable(`prod_not_locked:${blockId}`);
|
|
@@ -296,9 +301,11 @@ export class ResultPool {
|
|
|
296
301
|
field: projectFieldName(blockInfo.id, 'stagingCtx'),
|
|
297
302
|
ignoreError: true,
|
|
298
303
|
pureFieldErrorToUndefined: true,
|
|
304
|
+
stableIfNotFound: true,
|
|
299
305
|
}) !== undefined,
|
|
300
306
|
prj.traverseOrError({
|
|
301
307
|
field: projectFieldName(blockInfo.id, 'stagingUiCtx'),
|
|
308
|
+
stableIfNotFound: true,
|
|
302
309
|
}),
|
|
303
310
|
);
|
|
304
311
|
|