@milaboratories/pl-middle-layer 1.49.0 → 1.50.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.
Files changed (43) hide show
  1. package/dist/index.cjs +16 -2
  2. package/dist/index.d.ts +4 -2
  3. package/dist/index.js +4 -1
  4. package/dist/middle_layer/index.cjs +1 -0
  5. package/dist/middle_layer/index.d.ts +2 -1
  6. package/dist/middle_layer/index.js +1 -0
  7. package/dist/middle_layer/ops.cjs +1 -2
  8. package/dist/middle_layer/ops.cjs.map +1 -1
  9. package/dist/middle_layer/ops.d.ts +3 -6
  10. package/dist/middle_layer/ops.js +1 -2
  11. package/dist/middle_layer/ops.js.map +1 -1
  12. package/dist/middle_layer/project.cjs +1 -1
  13. package/dist/middle_layer/project.cjs.map +1 -1
  14. package/dist/middle_layer/project.js +1 -1
  15. package/dist/middle_layer/project.js.map +1 -1
  16. package/dist/middle_layer/project_list.d.ts +12 -0
  17. package/dist/model/index.d.ts +1 -1
  18. package/dist/model/project_model.cjs +4 -2
  19. package/dist/model/project_model.cjs.map +1 -1
  20. package/dist/model/project_model.d.ts +11 -2
  21. package/dist/model/project_model.js +4 -3
  22. package/dist/model/project_model.js.map +1 -1
  23. package/dist/mutator/block-pack/block_pack.d.ts +1 -1
  24. package/dist/mutator/migration.cjs +6 -2
  25. package/dist/mutator/migration.cjs.map +1 -1
  26. package/dist/mutator/migration.js +7 -3
  27. package/dist/mutator/migration.js.map +1 -1
  28. package/dist/mutator/project.cjs +94 -46
  29. package/dist/mutator/project.cjs.map +1 -1
  30. package/dist/mutator/project.d.ts +27 -0
  31. package/dist/mutator/project.js +96 -48
  32. package/dist/mutator/project.js.map +1 -1
  33. package/dist/mutator/template/render_block.cjs +1 -0
  34. package/dist/mutator/template/render_block.js +1 -1
  35. package/package.json +8 -8
  36. package/src/index.ts +3 -0
  37. package/src/middle_layer/index.ts +1 -0
  38. package/src/middle_layer/ops.ts +2 -8
  39. package/src/middle_layer/project.ts +1 -1
  40. package/src/model/index.ts +12 -0
  41. package/src/model/project_model.ts +4 -2
  42. package/src/mutator/migration.ts +13 -2
  43. package/src/mutator/project.ts +168 -74
@@ -121,6 +121,8 @@ const NoNewBlocks = (blockId) => {
121
121
  throw new Error(`No new block info for ${blockId}`);
122
122
  };
123
123
  var ProjectMutator = class ProjectMutator {
124
+ /** Max number of blocks to render staging for in a single background refresh pass. */
125
+ static STAGING_REFRESH_MAX_BATCH = 10;
124
126
  globalModCount = 0;
125
127
  fieldsChanged = false;
126
128
  lastModifiedChanged = false;
@@ -161,6 +163,12 @@ var ProjectMutator = class ProjectMutator {
161
163
  this.setBlockFieldObj(blockInfo.id, "blockSettings", initialBlockSettings);
162
164
  }
163
165
  });
166
+ if ([...this.blockInfos.values()].some((info) => info.fields.prodChainCtx === void 0) && this.blockInfos.size > 0) {
167
+ this.rebuildProdChain(0);
168
+ this.blockInfos.forEach((blockInfo) => {
169
+ this.resetStaging(blockInfo.id);
170
+ });
171
+ }
164
172
  this.blockInfos.forEach((info) => info.check());
165
173
  }
166
174
  get wasModified() {
@@ -438,7 +446,11 @@ var ProjectMutator = class ProjectMutator {
438
446
  somethingChanged = true;
439
447
  }
440
448
  }
441
- this.getStagingGraph().traverse("downstream", changedArgs, ({ id }) => this.resetStaging(id));
449
+ for (const blockId of changedArgs) try {
450
+ this.renderStagingFor(blockId);
451
+ } catch (e) {
452
+ this.projectHelper.logger.error(new Error(`[setStates] inline staging render failed for ${blockId}`, { cause: e }));
453
+ }
442
454
  if (somethingChanged) this.updateLastModified();
443
455
  }
444
456
  setBlockSettings(blockId, newValue) {
@@ -454,15 +466,42 @@ var ProjectMutator = class ProjectMutator {
454
466
  });
455
467
  return require_render_block.createBContextFromUpstreams(this.tx, upstreamContexts);
456
468
  }
457
- createStagingCtx(upstream) {
458
- const upstreamContexts = [];
459
- upstream.forEach((id) => {
460
- const info = this.getBlockInfo(id);
461
- if (info.fields["stagingCtx"]?.ref !== void 0) upstreamContexts.push(_milaboratories_pl_client.Pl.unwrapHolder(this.tx, info.fields["stagingCtx"].ref));
462
- else if (info.fields.currentPrerunArgs !== void 0) throw new Error(`Upstream ${id} staging is not rendered but has currentPrerunArgs set.`);
463
- if (info.fields["prodCtx"]?.ref !== void 0) upstreamContexts.push(_milaboratories_pl_client.Pl.unwrapHolder(this.tx, info.fields["prodCtx"].ref));
464
- });
465
- return require_render_block.createBContextFromUpstreams(this.tx, upstreamContexts);
469
+ /**
470
+ * Rebuilds the production context chain from `fromBlockIndex` to the end.
471
+ * Each block gets a `prodChainCtx` field containing the accumulated production
472
+ * contexts from all blocks above it in project order.
473
+ *
474
+ * Construction rule for block at position K:
475
+ * - If block K-1 has prodCtx: chainNode[K] = BContext(chainNode[K-1], K-1.prodCtx)
476
+ * - If block K-1 has no prodCtx: chainNode[K] = chainNode[K-1] (passthrough)
477
+ * - Block 0: chainNode[0] = BContextEnd
478
+ */
479
+ rebuildProdChain(fromBlockIndex = 0) {
480
+ const blocks = [...require_project_model_util.allBlocks(this.struct)];
481
+ if (fromBlockIndex >= blocks.length) return;
482
+ let prevChainCtx;
483
+ if (fromBlockIndex > 0) {
484
+ const prevHolder = this.getBlockInfo(blocks[fromBlockIndex - 1].id).fields.prodChainCtx?.ref;
485
+ if (prevHolder === void 0) throw new Error(`rebuildProdChain(${fromBlockIndex}): block ${blocks[fromBlockIndex - 1].id} at position ${fromBlockIndex - 1} has no prodChainCtx — chain must be built from 0 first`);
486
+ prevChainCtx = _milaboratories_pl_client.Pl.unwrapHolder(this.tx, prevHolder);
487
+ }
488
+ for (let i = fromBlockIndex; i < blocks.length; i++) {
489
+ const blockId = blocks[i].id;
490
+ let newChainCtx;
491
+ if (i === 0) newChainCtx = require_render_block.createBContextEnd(this.tx);
492
+ else {
493
+ const prevBlockId = blocks[i - 1].id;
494
+ const prevProdCtxHolder = this.getBlockInfo(prevBlockId).fields.prodCtx?.ref;
495
+ if (prevProdCtxHolder !== void 0) {
496
+ const upstreams = [];
497
+ upstreams.push(prevChainCtx);
498
+ upstreams.push(_milaboratories_pl_client.Pl.unwrapHolder(this.tx, prevProdCtxHolder));
499
+ newChainCtx = require_render_block.createBContextFromUpstreams(this.tx, upstreams);
500
+ } else newChainCtx = prevChainCtx;
501
+ }
502
+ this.setBlockField(blockId, "prodChainCtx", _milaboratories_pl_client.Pl.wrapInEphHolder(this.tx, newChainCtx), "NotReady");
503
+ prevChainCtx = newChainCtx;
504
+ }
466
505
  }
467
506
  exportCtx(ctx) {
468
507
  return require_context_export.exportContext(this.tx, _milaboratories_pl_client.Pl.unwrapHolder(this.tx, this.ctxExportTplHolder), ctx);
@@ -476,7 +515,9 @@ var ProjectMutator = class ProjectMutator {
476
515
  const prerunArgsRef = info.fields.currentPrerunArgs?.ref;
477
516
  if (prerunArgsRef === void 0) return;
478
517
  this.resetStaging(blockId);
479
- const ctx = this.createStagingCtx(this.getStagingGraph().nodes.get(blockId).upstream);
518
+ const chainCtxHolder = info.fields.prodChainCtx?.ref;
519
+ if (chainCtxHolder === void 0) throw new Error(`[renderStagingFor] block ${blockId} has no prodChainCtx — chain must be built before staging render`);
520
+ const ctx = _milaboratories_pl_client.Pl.unwrapHolder(this.tx, chainCtxHolder);
480
521
  if (this.getBlock(blockId).renderingMode !== "Heavy") throw new Error("not supported yet");
481
522
  const tpl = info.getTemplate(this.tx);
482
523
  const results = require_render_block.createRenderHeavyBlock(this.tx, tpl, {
@@ -594,6 +635,12 @@ var ProjectMutator = class ProjectMutator {
594
635
  this.stagingGraph = void 0;
595
636
  this.pendingProductionGraph = void 0;
596
637
  this.actualProductionGraph = void 0;
638
+ const newBlocks = [...require_project_model_util.allBlocks(newStructure)];
639
+ const oldBlocks = [...currentStagingGraph.nodes.keys()];
640
+ const n = Math.min(newBlocks.length, oldBlocks.length);
641
+ let firstChangedIdx = 0;
642
+ while (firstChangedIdx < n && newBlocks[firstChangedIdx].id === oldBlocks[firstChangedIdx]) firstChangedIdx++;
643
+ if (firstChangedIdx < newBlocks.length) this.rebuildProdChain(firstChangedIdx);
597
644
  this.updateLastModified();
598
645
  }
599
646
  addBlock(block, spec, before) {
@@ -706,9 +753,14 @@ var ProjectMutator = class ProjectMutator {
706
753
  if (info.fields.currentArgs !== void 0) this.setBlockFieldObj(blockId, "currentPrerunArgs", info.fields.currentArgs);
707
754
  }
708
755
  this.blocksWithChangedInputs.add(blockId);
709
- this.getStagingGraph().traverse("downstream", [blockId], ({ id }) => this.resetStaging(id));
710
756
  }
711
757
  if (info.productionRendered) this.getActualProductionGraph().traverse("downstream", [blockId], ({ id }) => this.resetOrLimboProduction(id));
758
+ const blocksList = [...require_project_model_util.allBlocks(this.struct)];
759
+ const blockIdx = blocksList.findIndex((b) => b.id === blockId);
760
+ if (blockIdx >= 0) {
761
+ this.rebuildProdChain(blockIdx + 1);
762
+ for (let i = blockIdx; i < blocksList.length; i++) this.resetStaging(blocksList[i].id);
763
+ }
712
764
  this.updateLastModified();
713
765
  }
714
766
  renderProduction(blockIds, addUpstreams = false) {
@@ -742,9 +794,14 @@ var ProjectMutator = class ProjectMutator {
742
794
  if (rendered.has(node.id)) return;
743
795
  this.resetOrLimboProduction(node.id);
744
796
  });
745
- this.getStagingGraph().traverse("downstream", renderedArray, ({ id }) => {
746
- if (renderedArray[0] !== id) this.resetStaging(id);
747
- });
797
+ if (rendered.size > 0) {
798
+ const blocksList = [...require_project_model_util.allBlocks(this.struct)];
799
+ const firstRenderedIdx = blocksList.findIndex((b) => rendered.has(b.id));
800
+ if (firstRenderedIdx >= 0) {
801
+ this.rebuildProdChain(firstRenderedIdx + 1);
802
+ for (let i = firstRenderedIdx + 1; i < blocksList.length; i++) this.resetStaging(blocksList[i].id);
803
+ }
804
+ }
748
805
  if (rendered.size > 0) this.updateLastModified();
749
806
  return rendered;
750
807
  }
@@ -769,38 +826,29 @@ var ProjectMutator = class ProjectMutator {
769
826
  }
770
827
  }
771
828
  for (const blockId of activeProdGraph.traverseIdsExcludingRoots("downstream", ...stopped)) this.resetOrLimboProduction(blockId);
772
- this.getStagingGraph().traverse("downstream", stopped, ({ id }) => this.resetStaging(id));
773
- }
774
- traverseWithStagingLag(cb) {
775
- const lags = /* @__PURE__ */ new Map();
776
- this.getStagingGraph().nodes.forEach((node) => {
777
- const info = this.getBlockInfo(node.id);
778
- const requiresRendering = info.requireStagingRendering;
779
- let lag = requiresRendering ? 1 : 0;
780
- node.upstream.forEach((upstream) => {
781
- const upstreamLag = lags.get(upstream);
782
- if (upstreamLag === 0) return;
783
- lag = Math.max(upstreamLag + 1, lag);
784
- });
785
- if (!requiresRendering && info.stagingRendered) {}
786
- cb(node.id, lag);
787
- lags.set(node.id, lag);
788
- });
829
+ if (stopped.length > 0) {
830
+ const blocksList = [...require_project_model_util.allBlocks(this.struct)];
831
+ const stoppedSet = new Set(stopped);
832
+ const firstStoppedIdx = blocksList.findIndex((b) => stoppedSet.has(b.id));
833
+ if (firstStoppedIdx >= 0) {
834
+ this.rebuildProdChain(firstStoppedIdx + 1);
835
+ for (let i = firstStoppedIdx; i < blocksList.length; i++) this.resetStaging(blocksList[i].id);
836
+ }
837
+ }
789
838
  }
790
- /** @param stagingRenderingRate rate in blocks per second */
791
- refreshStagings(stagingRenderingRate) {
792
- const elapsed = Date.now() - this.renderingState.stagingRefreshTimestamp;
793
- const lagThreshold = stagingRenderingRate === void 0 ? void 0 : 1 + Math.max(0, elapsed * stagingRenderingRate / 1e3);
839
+ /** Renders staging for blocks that need it (have currentPrerunArgs but no staging). */
840
+ refreshStagings() {
841
+ const maxBatch = ProjectMutator.STAGING_REFRESH_MAX_BATCH;
794
842
  let rendered = 0;
795
- this.traverseWithStagingLag((blockId, lag) => {
796
- if (lag === 0) return;
797
- if (lagThreshold === void 0 || lag <= lagThreshold) try {
798
- this.renderStagingFor(blockId);
843
+ for (const block of require_project_model_util.allBlocks(this.struct)) {
844
+ if (rendered >= maxBatch) break;
845
+ if (this.getBlockInfo(block.id).requireStagingRendering) try {
846
+ this.renderStagingFor(block.id);
799
847
  rendered++;
800
848
  } catch (e) {
801
- this.projectHelper.logger.error(new Error(`[refreshStagings] renderStagingFor failed for ${blockId}`, { cause: e }));
849
+ this.projectHelper.logger.error(new Error(`[refreshStagings] renderStagingFor failed for ${block.id}`, { cause: e }));
802
850
  }
803
- });
851
+ }
804
852
  if (rendered > 0) this.resetStagingRefreshTimestamp();
805
853
  }
806
854
  /** Updates project metadata */
@@ -809,9 +857,9 @@ var ProjectMutator = class ProjectMutator {
809
857
  this.metaChanged = true;
810
858
  this.updateLastModified();
811
859
  }
812
- /** @param stagingRenderingRate rate in blocks per second */
813
- doRefresh(stagingRenderingRate) {
814
- this.refreshStagings(stagingRenderingRate);
860
+ /** Background maintenance: render pending stagings + GC of Previous fields. */
861
+ doRefresh() {
862
+ this.refreshStagings();
815
863
  this.blockInfos.forEach((blockInfo) => {
816
864
  if (blockInfo.fields.prodCtx?.status === "Ready" && blockInfo.fields.prodOutput?.status === "Ready") this.deleteBlockFields(blockInfo.id, "prodOutputPrevious", "prodCtxPrevious", "prodUiCtxPrevious");
817
865
  if (blockInfo.fields.stagingCtx?.status === "Ready" && blockInfo.fields.stagingOutput?.status === "Ready") this.deleteBlockFields(blockInfo.id, "stagingOutputPrevious", "stagingCtxPrevious", "stagingUiCtxPrevious");
@@ -961,7 +1009,7 @@ async function duplicateProject(tx, sourceRid, options) {
961
1009
  const sourceKVs = await sourceKVsP;
962
1010
  const schemaKV = sourceKVs.find((kv) => kv.key === require_project_model.SchemaVersionKey);
963
1011
  const schema = schemaKV ? JSON.parse(schemaKV.value) : void 0;
964
- if (schema !== require_project_model.SchemaVersionCurrent) throw new _platforma_sdk_model.UiError(`Cannot duplicate project with schema version ${schema ?? "unknown"}. Only schema version ${require_project_model.SchemaVersionCurrent} is supported. Try opening the project first to trigger migration.`);
1012
+ if (schema !== require_project_model.SchemaVersionCurrent && schema !== require_project_model.SchemaVersionV3) throw new _platforma_sdk_model.UiError(`Cannot duplicate project with schema version ${schema ?? "unknown"}. Only schema versions ${require_project_model.SchemaVersionV3} and ${require_project_model.SchemaVersionCurrent} are supported. Try opening the project first to trigger migration.`);
965
1013
  const newPrj = tx.createEphemeral(require_project_model.ProjectResourceType);
966
1014
  tx.lock(newPrj);
967
1015
  const ts = String(Date.now());