@milaboratories/pl-middle-layer 1.49.0 → 1.50.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 +16 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -1
- package/dist/middle_layer/index.cjs +1 -0
- package/dist/middle_layer/index.d.ts +2 -1
- package/dist/middle_layer/index.js +1 -0
- package/dist/middle_layer/ops.cjs +1 -2
- package/dist/middle_layer/ops.cjs.map +1 -1
- package/dist/middle_layer/ops.d.ts +3 -6
- package/dist/middle_layer/ops.js +1 -2
- package/dist/middle_layer/ops.js.map +1 -1
- package/dist/middle_layer/project.cjs +1 -1
- package/dist/middle_layer/project.cjs.map +1 -1
- package/dist/middle_layer/project.js +1 -1
- package/dist/middle_layer/project.js.map +1 -1
- package/dist/middle_layer/project_list.d.ts +12 -0
- package/dist/model/index.d.ts +1 -1
- package/dist/model/project_model.cjs +4 -2
- package/dist/model/project_model.cjs.map +1 -1
- package/dist/model/project_model.d.ts +11 -2
- package/dist/model/project_model.js +4 -3
- package/dist/model/project_model.js.map +1 -1
- package/dist/mutator/block-pack/block_pack.d.ts +1 -1
- package/dist/mutator/migration.cjs +6 -2
- package/dist/mutator/migration.cjs.map +1 -1
- package/dist/mutator/migration.js +7 -3
- package/dist/mutator/migration.js.map +1 -1
- package/dist/mutator/project.cjs +94 -46
- package/dist/mutator/project.cjs.map +1 -1
- package/dist/mutator/project.d.ts +27 -0
- package/dist/mutator/project.js +96 -48
- package/dist/mutator/project.js.map +1 -1
- package/dist/mutator/template/render_block.cjs +1 -0
- package/dist/mutator/template/render_block.js +1 -1
- package/package.json +7 -7
- package/src/index.ts +3 -0
- package/src/middle_layer/index.ts +1 -0
- package/src/middle_layer/ops.ts +2 -8
- package/src/middle_layer/project.ts +1 -1
- package/src/model/index.ts +12 -0
- package/src/model/project_model.ts +4 -2
- package/src/mutator/migration.ts +13 -2
- package/src/mutator/project.ts +168 -74
|
@@ -74,7 +74,8 @@ export const ProjectResourceType: ResourceType = { name: "UserProject", version:
|
|
|
74
74
|
|
|
75
75
|
export const SchemaVersionKey = "SchemaVersion";
|
|
76
76
|
export const SchemaVersionV2 = "2";
|
|
77
|
-
export const
|
|
77
|
+
export const SchemaVersionV3 = "3";
|
|
78
|
+
export const SchemaVersionCurrent = "4";
|
|
78
79
|
|
|
79
80
|
export const ProjectCreatedTimestamp = "ProjectCreated";
|
|
80
81
|
export const ProjectLastModifiedTimestamp = "ProjectLastModified";
|
|
@@ -106,6 +107,7 @@ export interface ProjectField {
|
|
|
106
107
|
| "prodArgs"
|
|
107
108
|
| "currentArgs"
|
|
108
109
|
| "currentPrerunArgs" // Derived args for staging/prerun rendering (from prerunArgs() or args())
|
|
110
|
+
| "prodChainCtx" // Pre-built production context chain node (accumulated prodCtx from all blocks above)
|
|
109
111
|
| "prodCtx"
|
|
110
112
|
| "prodUiCtx"
|
|
111
113
|
| "prodOutput"
|
|
@@ -137,7 +139,7 @@ export function projectFieldName(blockId: string, fieldName: ProjectField["field
|
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
const projectFieldPattern =
|
|
140
|
-
/^(?<blockId>.*)-(?<fieldName>blockPack|blockSettings|blockStorage|inputsValid|prodArgs|currentArgs|currentPrerunArgs|prodCtx|prodUiCtx|prodOutput|prodCtxPrevious|prodUiCtxPrevious|prodOutputPrevious|stagingCtx|stagingUiCtx|stagingOutput|stagingCtxPrevious|stagingUiCtxPrevious|stagingOutputPrevious)$/;
|
|
142
|
+
/^(?<blockId>.*)-(?<fieldName>blockPack|blockSettings|blockStorage|inputsValid|prodArgs|currentArgs|currentPrerunArgs|prodChainCtx|prodCtx|prodUiCtx|prodOutput|prodCtxPrevious|prodUiCtxPrevious|prodOutputPrevious|stagingCtx|stagingUiCtx|stagingOutput|stagingCtxPrevious|stagingUiCtxPrevious|stagingOutputPrevious)$/;
|
|
141
143
|
|
|
142
144
|
export function parseProjectField(name: string): ProjectField | undefined {
|
|
143
145
|
const match = name.match(projectFieldPattern);
|
package/src/mutator/migration.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SchemaVersionCurrent,
|
|
7
7
|
SchemaVersionKey,
|
|
8
8
|
SchemaVersionV2,
|
|
9
|
+
SchemaVersionV3,
|
|
9
10
|
} from "../model/project_model";
|
|
10
11
|
import { BlockFrontendStateKeyPrefixV1, SchemaVersionV1 } from "../model/project_model_v1";
|
|
11
12
|
import { field, isNullResourceId } from "@milaboratories/pl-client";
|
|
@@ -31,8 +32,18 @@ export async function applyProjectMigrations(pl: PlClient, rid: ResourceId) {
|
|
|
31
32
|
|
|
32
33
|
if (schemaVersion === SchemaVersionV2) {
|
|
33
34
|
await migrateV2ToV3(tx, rid);
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
schemaVersion = SchemaVersionV3;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (schemaVersion === SchemaVersionV3) {
|
|
39
|
+
// V3 → V4: production context chain + staging re-render.
|
|
40
|
+
// The actual chain building and staging reset happens in fixProblemsAndMigrate()
|
|
41
|
+
// (called from ProjectMutator.load). This migration step just bumps the schema
|
|
42
|
+
// to prevent older clients from operating on the new project structure.
|
|
43
|
+
schemaVersion = SchemaVersionCurrent;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (schemaVersion !== SchemaVersionCurrent) {
|
|
36
47
|
throw new Error(`Unknown project schema version: ${schemaVersion}`);
|
|
37
48
|
}
|
|
38
49
|
|
package/src/mutator/project.ts
CHANGED
|
@@ -18,7 +18,11 @@ import {
|
|
|
18
18
|
Pl,
|
|
19
19
|
PlClient,
|
|
20
20
|
} from "@milaboratories/pl-client";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
createRenderHeavyBlock,
|
|
23
|
+
createBContextFromUpstreams,
|
|
24
|
+
createBContextEnd,
|
|
25
|
+
} from "./template/render_block";
|
|
22
26
|
import type {
|
|
23
27
|
Block,
|
|
24
28
|
ProjectStructure,
|
|
@@ -32,6 +36,7 @@ import {
|
|
|
32
36
|
projectFieldName,
|
|
33
37
|
SchemaVersionCurrent,
|
|
34
38
|
SchemaVersionKey,
|
|
39
|
+
SchemaVersionV3,
|
|
35
40
|
ProjectResourceType,
|
|
36
41
|
InitialBlockStructure,
|
|
37
42
|
InitialProjectRenderingState,
|
|
@@ -298,6 +303,9 @@ export type ClearState = {
|
|
|
298
303
|
};
|
|
299
304
|
|
|
300
305
|
export class ProjectMutator {
|
|
306
|
+
/** Max number of blocks to render staging for in a single background refresh pass. */
|
|
307
|
+
private static readonly STAGING_REFRESH_MAX_BATCH = 10;
|
|
308
|
+
|
|
301
309
|
private globalModCount = 0;
|
|
302
310
|
private fieldsChanged: boolean = false;
|
|
303
311
|
|
|
@@ -379,6 +387,18 @@ export class ProjectMutator {
|
|
|
379
387
|
}
|
|
380
388
|
});
|
|
381
389
|
|
|
390
|
+
// Migration: build production context chain if not present,
|
|
391
|
+
// and reset all stagings so they re-render using the new chain
|
|
392
|
+
const needsChainBuild = [...this.blockInfos.values()].some(
|
|
393
|
+
(info) => info.fields.prodChainCtx === undefined,
|
|
394
|
+
);
|
|
395
|
+
if (needsChainBuild && this.blockInfos.size > 0) {
|
|
396
|
+
this.rebuildProdChain(0);
|
|
397
|
+
this.blockInfos.forEach((blockInfo) => {
|
|
398
|
+
this.resetStaging(blockInfo.id);
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
382
402
|
// Validate after fixes
|
|
383
403
|
this.blockInfos.forEach((info) => info.check());
|
|
384
404
|
}
|
|
@@ -848,8 +868,18 @@ export class ProjectMutator {
|
|
|
848
868
|
}
|
|
849
869
|
}
|
|
850
870
|
|
|
851
|
-
//
|
|
852
|
-
|
|
871
|
+
// Render staging inline for blocks with changed prerunArgs — no downstream cascade.
|
|
872
|
+
// Each block's staging uses only the pre-built production context chain (prodChainCtx),
|
|
873
|
+
// which doesn't change on arg edits, so downstream blocks are unaffected.
|
|
874
|
+
for (const blockId of changedArgs) {
|
|
875
|
+
try {
|
|
876
|
+
this.renderStagingFor(blockId);
|
|
877
|
+
} catch (e) {
|
|
878
|
+
this.projectHelper.logger.error(
|
|
879
|
+
new Error(`[setStates] inline staging render failed for ${blockId}`, { cause: e }),
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
853
883
|
|
|
854
884
|
if (somethingChanged) this.updateLastModified();
|
|
855
885
|
}
|
|
@@ -870,22 +900,67 @@ export class ProjectMutator {
|
|
|
870
900
|
return createBContextFromUpstreams(this.tx, upstreamContexts);
|
|
871
901
|
}
|
|
872
902
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
903
|
+
/**
|
|
904
|
+
* Rebuilds the production context chain from `fromBlockIndex` to the end.
|
|
905
|
+
* Each block gets a `prodChainCtx` field containing the accumulated production
|
|
906
|
+
* contexts from all blocks above it in project order.
|
|
907
|
+
*
|
|
908
|
+
* Construction rule for block at position K:
|
|
909
|
+
* - If block K-1 has prodCtx: chainNode[K] = BContext(chainNode[K-1], K-1.prodCtx)
|
|
910
|
+
* - If block K-1 has no prodCtx: chainNode[K] = chainNode[K-1] (passthrough)
|
|
911
|
+
* - Block 0: chainNode[0] = BContextEnd
|
|
912
|
+
*/
|
|
913
|
+
private rebuildProdChain(fromBlockIndex: number = 0): void {
|
|
914
|
+
const blocks = [...allBlocks(this.struct)];
|
|
915
|
+
if (fromBlockIndex >= blocks.length) return;
|
|
916
|
+
|
|
917
|
+
// Get the inner chain context ref of the block before fromBlockIndex
|
|
918
|
+
let prevChainCtx: AnyRef | undefined;
|
|
919
|
+
if (fromBlockIndex > 0) {
|
|
920
|
+
const prevHolder = this.getBlockInfo(blocks[fromBlockIndex - 1].id).fields.prodChainCtx?.ref;
|
|
921
|
+
if (prevHolder === undefined) {
|
|
922
|
+
throw new Error(
|
|
923
|
+
`rebuildProdChain(${fromBlockIndex}): block ${blocks[fromBlockIndex - 1].id} at position ${fromBlockIndex - 1} has no prodChainCtx — chain must be built from 0 first`,
|
|
924
|
+
);
|
|
882
925
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
926
|
+
prevChainCtx = Pl.unwrapHolder(this.tx, prevHolder);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
for (let i = fromBlockIndex; i < blocks.length; i++) {
|
|
930
|
+
const blockId = blocks[i].id;
|
|
931
|
+
|
|
932
|
+
let newChainCtx: AnyRef;
|
|
933
|
+
|
|
934
|
+
if (i === 0) {
|
|
935
|
+
// First block: nothing above
|
|
936
|
+
newChainCtx = createBContextEnd(this.tx);
|
|
937
|
+
} else {
|
|
938
|
+
const prevBlockId = blocks[i - 1].id;
|
|
939
|
+
const prevInfo = this.getBlockInfo(prevBlockId);
|
|
940
|
+
const prevProdCtxHolder = prevInfo.fields.prodCtx?.ref;
|
|
941
|
+
|
|
942
|
+
if (prevProdCtxHolder !== undefined) {
|
|
943
|
+
// Block above has production: accumulate into chain
|
|
944
|
+
const upstreams: AnyRef[] = [];
|
|
945
|
+
upstreams.push(prevChainCtx!);
|
|
946
|
+
upstreams.push(Pl.unwrapHolder(this.tx, prevProdCtxHolder));
|
|
947
|
+
newChainCtx = createBContextFromUpstreams(this.tx, upstreams);
|
|
948
|
+
} else {
|
|
949
|
+
// Passthrough: reuse inner context from previous block
|
|
950
|
+
newChainCtx = prevChainCtx!;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// Store chain node (wrapped in holder)
|
|
955
|
+
this.setBlockField(
|
|
956
|
+
blockId,
|
|
957
|
+
"prodChainCtx",
|
|
958
|
+
Pl.wrapInEphHolder(this.tx, newChainCtx),
|
|
959
|
+
"NotReady",
|
|
960
|
+
);
|
|
961
|
+
|
|
962
|
+
prevChainCtx = newChainCtx;
|
|
963
|
+
}
|
|
889
964
|
}
|
|
890
965
|
|
|
891
966
|
private exportCtx(ctx: AnyRef): AnyRef {
|
|
@@ -899,10 +974,7 @@ export class ProjectMutator {
|
|
|
899
974
|
private renderStagingFor(blockId: string) {
|
|
900
975
|
const info = this.getBlockInfo(blockId);
|
|
901
976
|
|
|
902
|
-
//
|
|
903
|
-
// because inputs aren't ready, or args derivation failed), skip without clearing existing staging.
|
|
904
|
-
// Otherwise resetStaging would delete stagingCtx, and downstream blocks that reference this block
|
|
905
|
-
// as an upstream would fail in createStagingCtx.
|
|
977
|
+
// Skip if currentPrerunArgs is not set (prerunArgs() returned undefined or args derivation failed)
|
|
906
978
|
const prerunArgsRef = info.fields.currentPrerunArgs?.ref;
|
|
907
979
|
if (prerunArgsRef === undefined) {
|
|
908
980
|
return;
|
|
@@ -910,7 +982,14 @@ export class ProjectMutator {
|
|
|
910
982
|
|
|
911
983
|
this.resetStaging(blockId);
|
|
912
984
|
|
|
913
|
-
|
|
985
|
+
// Use the pre-built production context chain node
|
|
986
|
+
const chainCtxHolder = info.fields.prodChainCtx?.ref;
|
|
987
|
+
if (chainCtxHolder === undefined) {
|
|
988
|
+
throw new Error(
|
|
989
|
+
`[renderStagingFor] block ${blockId} has no prodChainCtx — chain must be built before staging render`,
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
const ctx = Pl.unwrapHolder(this.tx, chainCtxHolder);
|
|
914
993
|
|
|
915
994
|
if (this.getBlock(blockId).renderingMode !== "Heavy") throw new Error("not supported yet");
|
|
916
995
|
|
|
@@ -1174,6 +1253,19 @@ export class ProjectMutator {
|
|
|
1174
1253
|
this.pendingProductionGraph = undefined;
|
|
1175
1254
|
this.actualProductionGraph = undefined;
|
|
1176
1255
|
|
|
1256
|
+
// Rebuild production chain — structure (and thus block order) may have changed.
|
|
1257
|
+
// Find the first position that changed to avoid rebuilding the entire chain.
|
|
1258
|
+
const newBlocks = [...allBlocks(newStructure)];
|
|
1259
|
+
const oldBlocks = [...currentStagingGraph.nodes.keys()];
|
|
1260
|
+
const n = Math.min(newBlocks.length, oldBlocks.length);
|
|
1261
|
+
let firstChangedIdx = 0;
|
|
1262
|
+
while (firstChangedIdx < n && newBlocks[firstChangedIdx].id === oldBlocks[firstChangedIdx]) {
|
|
1263
|
+
firstChangedIdx++;
|
|
1264
|
+
}
|
|
1265
|
+
if (firstChangedIdx < newBlocks.length) {
|
|
1266
|
+
this.rebuildProdChain(firstChangedIdx);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1177
1269
|
this.updateLastModified();
|
|
1178
1270
|
}
|
|
1179
1271
|
|
|
@@ -1356,9 +1448,6 @@ export class ProjectMutator {
|
|
|
1356
1448
|
}
|
|
1357
1449
|
|
|
1358
1450
|
this.blocksWithChangedInputs.add(blockId);
|
|
1359
|
-
|
|
1360
|
-
// resetting staging outputs for all downstream blocks
|
|
1361
|
-
this.getStagingGraph().traverse("downstream", [blockId], ({ id }) => this.resetStaging(id));
|
|
1362
1451
|
}
|
|
1363
1452
|
|
|
1364
1453
|
// also reset or limbo all downstream productions
|
|
@@ -1367,6 +1456,17 @@ export class ProjectMutator {
|
|
|
1367
1456
|
this.resetOrLimboProduction(id),
|
|
1368
1457
|
);
|
|
1369
1458
|
|
|
1459
|
+
// Rebuild chain from this block onward (production may have been reset/limboed)
|
|
1460
|
+
// and reset staging for the migrated block + everything below
|
|
1461
|
+
const blocksList = [...allBlocks(this.struct)];
|
|
1462
|
+
const blockIdx = blocksList.findIndex((b) => b.id === blockId);
|
|
1463
|
+
if (blockIdx >= 0) {
|
|
1464
|
+
this.rebuildProdChain(blockIdx + 1);
|
|
1465
|
+
for (let i = blockIdx; i < blocksList.length; i++) {
|
|
1466
|
+
this.resetStaging(blocksList[i].id);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1370
1470
|
this.updateLastModified();
|
|
1371
1471
|
}
|
|
1372
1472
|
|
|
@@ -1424,11 +1524,20 @@ export class ProjectMutator {
|
|
|
1424
1524
|
this.resetOrLimboProduction(node.id);
|
|
1425
1525
|
});
|
|
1426
1526
|
|
|
1427
|
-
//
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1527
|
+
// Rebuild production chain and reset downstream staging
|
|
1528
|
+
if (rendered.size > 0) {
|
|
1529
|
+
const blocksList = [...allBlocks(this.struct)];
|
|
1530
|
+
const firstRenderedIdx = blocksList.findIndex((b) => rendered.has(b.id));
|
|
1531
|
+
if (firstRenderedIdx >= 0) {
|
|
1532
|
+
// Chain from firstRenderedIdx+1 onward changes (rendered blocks have new prodCtx)
|
|
1533
|
+
this.rebuildProdChain(firstRenderedIdx + 1);
|
|
1534
|
+
// Reset staging for all blocks after the first rendered block
|
|
1535
|
+
// (their chain node changed; the first rendered block's chain is unaffected)
|
|
1536
|
+
for (let i = firstRenderedIdx + 1; i < blocksList.length; i++) {
|
|
1537
|
+
this.resetStaging(blocksList[i].id);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1432
1541
|
|
|
1433
1542
|
if (rendered.size > 0) this.updateLastModified();
|
|
1434
1543
|
|
|
@@ -1470,54 +1579,39 @@ export class ProjectMutator {
|
|
|
1470
1579
|
for (const blockId of activeProdGraph.traverseIdsExcludingRoots("downstream", ...stopped))
|
|
1471
1580
|
this.resetOrLimboProduction(blockId);
|
|
1472
1581
|
|
|
1473
|
-
// reset staging
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
let lag = requiresRendering ? 1 : 0;
|
|
1485
|
-
node.upstream.forEach((upstream) => {
|
|
1486
|
-
const upstreamLag = lags.get(upstream)!;
|
|
1487
|
-
if (upstreamLag === 0) return;
|
|
1488
|
-
lag = Math.max(upstreamLag + 1, lag);
|
|
1489
|
-
});
|
|
1490
|
-
if (!requiresRendering && info.stagingRendered) {
|
|
1491
|
-
// console.log(`[traverseWithStagingLag] SKIP staging for ${node.id} - prerunArgs unchanged`);
|
|
1582
|
+
// Rebuild chain and reset staging for stopped blocks and everything below
|
|
1583
|
+
if (stopped.length > 0) {
|
|
1584
|
+
const blocksList = [...allBlocks(this.struct)];
|
|
1585
|
+
const stoppedSet = new Set(stopped);
|
|
1586
|
+
const firstStoppedIdx = blocksList.findIndex((b) => stoppedSet.has(b.id));
|
|
1587
|
+
if (firstStoppedIdx >= 0) {
|
|
1588
|
+
// Stopped blocks lost prodCtx — chain below them changes
|
|
1589
|
+
this.rebuildProdChain(firstStoppedIdx + 1);
|
|
1590
|
+
for (let i = firstStoppedIdx; i < blocksList.length; i++) {
|
|
1591
|
+
this.resetStaging(blocksList[i].id);
|
|
1592
|
+
}
|
|
1492
1593
|
}
|
|
1493
|
-
|
|
1494
|
-
lags.set(node.id, lag);
|
|
1495
|
-
});
|
|
1594
|
+
}
|
|
1496
1595
|
}
|
|
1497
1596
|
|
|
1498
|
-
/**
|
|
1499
|
-
private refreshStagings(
|
|
1500
|
-
const
|
|
1501
|
-
const lagThreshold =
|
|
1502
|
-
stagingRenderingRate === undefined
|
|
1503
|
-
? undefined
|
|
1504
|
-
: 1 + Math.max(0, (elapsed * stagingRenderingRate) / 1000);
|
|
1597
|
+
/** Renders staging for blocks that need it (have currentPrerunArgs but no staging). */
|
|
1598
|
+
private refreshStagings() {
|
|
1599
|
+
const maxBatch = ProjectMutator.STAGING_REFRESH_MAX_BATCH;
|
|
1505
1600
|
let rendered = 0;
|
|
1506
|
-
this.
|
|
1507
|
-
if (
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if (lagThreshold === undefined || lag <= lagThreshold) {
|
|
1601
|
+
for (const block of allBlocks(this.struct)) {
|
|
1602
|
+
if (rendered >= maxBatch) break;
|
|
1603
|
+
const info = this.getBlockInfo(block.id);
|
|
1604
|
+
if (info.requireStagingRendering) {
|
|
1511
1605
|
try {
|
|
1512
|
-
this.renderStagingFor(
|
|
1606
|
+
this.renderStagingFor(block.id);
|
|
1513
1607
|
rendered++;
|
|
1514
1608
|
} catch (e) {
|
|
1515
1609
|
this.projectHelper.logger.error(
|
|
1516
|
-
new Error(`[refreshStagings] renderStagingFor failed for ${
|
|
1610
|
+
new Error(`[refreshStagings] renderStagingFor failed for ${block.id}`, { cause: e }),
|
|
1517
1611
|
);
|
|
1518
1612
|
}
|
|
1519
1613
|
}
|
|
1520
|
-
}
|
|
1614
|
+
}
|
|
1521
1615
|
if (rendered > 0) this.resetStagingRefreshTimestamp();
|
|
1522
1616
|
}
|
|
1523
1617
|
|
|
@@ -1536,9 +1630,9 @@ export class ProjectMutator {
|
|
|
1536
1630
|
// Maintenance
|
|
1537
1631
|
//
|
|
1538
1632
|
|
|
1539
|
-
/**
|
|
1540
|
-
public doRefresh(
|
|
1541
|
-
this.refreshStagings(
|
|
1633
|
+
/** Background maintenance: render pending stagings + GC of Previous fields. */
|
|
1634
|
+
public doRefresh() {
|
|
1635
|
+
this.refreshStagings();
|
|
1542
1636
|
this.blockInfos.forEach((blockInfo) => {
|
|
1543
1637
|
if (
|
|
1544
1638
|
blockInfo.fields.prodCtx?.status === "Ready" &&
|
|
@@ -1847,13 +1941,13 @@ export async function duplicateProject(
|
|
|
1847
1941
|
const sourceData = await sourceDataP;
|
|
1848
1942
|
const sourceKVs = await sourceKVsP;
|
|
1849
1943
|
|
|
1850
|
-
// Validate schema version
|
|
1944
|
+
// Validate schema version (accept current and previous version that can be migrated on open)
|
|
1851
1945
|
const schemaKV = sourceKVs.find((kv) => kv.key === SchemaVersionKey);
|
|
1852
1946
|
const schema = schemaKV ? JSON.parse(schemaKV.value) : undefined;
|
|
1853
|
-
if (schema !== SchemaVersionCurrent) {
|
|
1947
|
+
if (schema !== SchemaVersionCurrent && schema !== SchemaVersionV3) {
|
|
1854
1948
|
throw new UiError(
|
|
1855
1949
|
`Cannot duplicate project with schema version ${schema ?? "unknown"}. ` +
|
|
1856
|
-
`Only schema
|
|
1950
|
+
`Only schema versions ${SchemaVersionV3} and ${SchemaVersionCurrent} are supported. ` +
|
|
1857
1951
|
`Try opening the project first to trigger migration.`,
|
|
1858
1952
|
);
|
|
1859
1953
|
}
|