@milaboratories/pl-middle-layer 1.54.7 → 1.55.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-middle-layer",
3
- "version": "1.54.7",
3
+ "version": "1.55.0",
4
4
  "description": "Pl Middle Layer",
5
5
  "keywords": [],
6
6
  "license": "UNLICENSED",
@@ -31,23 +31,23 @@
31
31
  "yaml": "^2.8.0",
32
32
  "zod": "~3.23.8",
33
33
  "@milaboratories/computable": "2.9.2",
34
- "@milaboratories/pf-spec-driver": "1.2.4",
35
34
  "@milaboratories/helpers": "1.14.1",
36
- "@milaboratories/pf-driver": "1.3.3",
37
- "@milaboratories/pl-client": "3.0.0",
38
35
  "@milaboratories/pl-deployments": "2.16.5",
36
+ "@milaboratories/pl-client": "3.0.0",
37
+ "@milaboratories/pf-driver": "1.3.3",
38
+ "@milaboratories/pf-spec-driver": "1.2.4",
39
+ "@milaboratories/pl-drivers": "1.12.8",
39
40
  "@milaboratories/pl-errors": "1.2.6",
40
41
  "@milaboratories/pl-http": "1.2.4",
41
- "@milaboratories/pl-drivers": "1.12.8",
42
42
  "@milaboratories/pl-model-backend": "1.2.6",
43
43
  "@milaboratories/pl-model-common": "1.31.1",
44
44
  "@milaboratories/pl-model-middle-layer": "1.16.3",
45
- "@milaboratories/resolve-helper": "1.1.3",
46
45
  "@milaboratories/pl-tree": "1.9.7",
47
46
  "@platforma-sdk/block-tools": "2.7.5",
48
- "@platforma-sdk/workflow-tengo": "5.11.0",
47
+ "@milaboratories/resolve-helper": "1.1.3",
49
48
  "@platforma-sdk/model": "1.63.1",
50
- "@milaboratories/ts-helpers": "1.8.1"
49
+ "@milaboratories/ts-helpers": "1.8.1",
50
+ "@platforma-sdk/workflow-tengo": "5.11.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "~24.5.2",
@@ -56,8 +56,8 @@
56
56
  "semver": "^7.7.2",
57
57
  "typescript": "~5.9.3",
58
58
  "vitest": "^4.0.18",
59
- "@milaboratories/build-configs": "1.5.2",
60
59
  "@milaboratories/ts-builder": "1.3.0",
60
+ "@milaboratories/build-configs": "1.5.2",
61
61
  "@milaboratories/ts-configs": "1.2.2"
62
62
  },
63
63
  "engines": {
@@ -433,6 +433,77 @@ test("v3 blocks: migrateBlockPack with storage migration re-derives args and pre
433
433
  });
434
434
  });
435
435
 
436
+ test("v3 blocks: resetToInitialStorage derives prerunArgs even when args() throws", async () => {
437
+ const quickJs = await getQuickJS();
438
+
439
+ await TestHelpers.withTempRoot(async (pl) => {
440
+ const prj = await pl.withWriteTx("CreatingProject", async (tx) => {
441
+ const prjRef = await createProject(tx);
442
+ tx.createField(field(tx.clientRoot, "prj"), "Dynamic", prjRef);
443
+ await tx.commit();
444
+ return await toGlobalResourceId(prjRef);
445
+ });
446
+
447
+ // Add block with valid data so args and prerunArgs are both set
448
+ await pl.withWriteTx("AddBlock", async (tx) => {
449
+ const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
450
+ mut.addBlock(
451
+ { id: "enter1", label: "Enter Numbers V3", renderingMode: "Heavy" },
452
+ {
453
+ storageMode: "fromModel",
454
+ blockPack: await TestBPPreparer.prepare(BPSpecEnterV3),
455
+ },
456
+ );
457
+ mut.setStates([
458
+ {
459
+ modelAPIVersion: 2,
460
+ blockId: "enter1",
461
+ payload: { operation: "update-block-data", value: { numbers: [4, 2, 6] } },
462
+ },
463
+ ]);
464
+ mut.save();
465
+ await tx.commit();
466
+ });
467
+
468
+ // Verify both args and prerunArgs are set
469
+ await poll(pl, async (tx) => {
470
+ const prjR = await tx.get(prj);
471
+ const currentArgs = await prjR.get(projectFieldName("enter1", "currentArgs"));
472
+ const argsData = JSON.parse(Buffer.from(currentArgs.data.data!).toString());
473
+ expect(argsData).toStrictEqual({ numbers: [2, 4, 6] });
474
+
475
+ const currentPrerunArgs = await prjR.get(projectFieldName("enter1", "currentPrerunArgs"));
476
+ const prerunData = JSON.parse(Buffer.from(currentPrerunArgs.data.data!).toString());
477
+ expect(prerunData).toStrictEqual({ evenNumbers: [2, 4, 6] });
478
+ });
479
+
480
+ // Reset to initial storage — init() returns { numbers: [] }
481
+ // args() will throw "Numbers are required!" but prerunArgs() should still succeed
482
+ await pl.withWriteTx("ResetToInitial", async (tx) => {
483
+ const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
484
+ mut.resetToInitialStorage("enter1");
485
+ mut.save();
486
+ await tx.commit();
487
+ });
488
+
489
+ // Verify: currentArgs is cleared (args threw), but currentPrerunArgs is still derived
490
+ await poll(pl, async (tx) => {
491
+ const prjR = await tx.get(prj);
492
+
493
+ // currentArgs should be gone since args() throws for empty numbers
494
+ const currentArgsField = prjR.data.fields.find(
495
+ (f) => f.name === projectFieldName("enter1", "currentArgs"),
496
+ );
497
+ expect(currentArgsField).toBeUndefined();
498
+
499
+ // currentPrerunArgs should still be set with empty even numbers
500
+ const currentPrerunArgs = await prjR.get(projectFieldName("enter1", "currentPrerunArgs"));
501
+ const prerunData = JSON.parse(Buffer.from(currentPrerunArgs.data.data!).toString());
502
+ expect(prerunData).toStrictEqual({ evenNumbers: [] });
503
+ });
504
+ });
505
+ });
506
+
436
507
  test("v3 blocks: migrateBlockPack assigns author marker", async () => {
437
508
  const quickJs = await getQuickJS();
438
509
 
@@ -751,6 +751,17 @@ export class ProjectMutator {
751
751
  const initialStorageJson = this.projectHelper.getInitialStorageInVM(blockConfig);
752
752
  this.setBlockStorageRaw(blockId, initialStorageJson);
753
753
 
754
+ // Derive prerunArgs first — always derived independently of args validation
755
+ const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(
756
+ blockConfig,
757
+ initialStorageJson,
758
+ );
759
+ if (prerunArgs !== undefined) {
760
+ this.setBlockFieldObj(blockId, "currentPrerunArgs", this.createJsonFieldValue(prerunArgs));
761
+ } else {
762
+ this.deleteBlockFields(blockId, "currentPrerunArgs");
763
+ }
764
+
754
765
  // Derive args from storage - set or clear currentArgs based on derivation result
755
766
  const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(
756
767
  blockConfig,
@@ -762,24 +773,8 @@ export class ProjectMutator {
762
773
  "currentArgs",
763
774
  this.createJsonFieldValue(deriveArgsResult.value),
764
775
  );
765
- // Derive prerunArgs from storage
766
- const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(
767
- blockConfig,
768
- initialStorageJson,
769
- );
770
- if (prerunArgs !== undefined) {
771
- this.setBlockFieldObj(blockId, "currentPrerunArgs", this.createJsonFieldValue(prerunArgs));
772
- } else {
773
- this.deleteBlockFields(blockId, "currentPrerunArgs");
774
- }
775
776
  } else {
776
- if (info.fields.currentPrerunArgs !== undefined) {
777
- this.projectHelper.logger.warn(
778
- `[staging] ${blockId}: currentPrerunArgs cleared (args derivation failed)`,
779
- );
780
- }
781
777
  this.deleteBlockFields(blockId, "currentArgs");
782
- this.deleteBlockFields(blockId, "currentPrerunArgs");
783
778
  }
784
779
  }
785
780
 
@@ -823,22 +818,18 @@ export class ProjectMutator {
823
818
  this.createGzJsonFieldValueFromContent(updatedStorageJson),
824
819
  );
825
820
 
826
- // Derive args directly from storage (VM extracts data internally)
821
+ // Derive prerunArgs first always derived independently of args validation
822
+ prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(
823
+ blockConfig,
824
+ updatedStorageJson,
825
+ );
826
+
827
+ // Derive args from storage (VM extracts data internally)
827
828
  const derivedArgsResult = this.projectHelper.deriveArgsFromStorage(
828
829
  blockConfig,
829
830
  updatedStorageJson,
830
831
  );
831
- if (derivedArgsResult.error) {
832
- args = undefined;
833
- prerunArgs = undefined;
834
- } else {
835
- args = derivedArgsResult.value;
836
- // Derive prerunArgs from storage, or fall back to args
837
- prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(
838
- blockConfig,
839
- updatedStorageJson,
840
- );
841
- }
832
+ args = derivedArgsResult.error ? undefined : derivedArgsResult.value;
842
833
  } else {
843
834
  this.setBlockFieldObj(req.blockId, "blockStorage", this.createGzJsonFieldValue(req.state));
844
835
  if (req.state !== null && typeof req.state === "object" && "args" in req.state) {
@@ -1118,18 +1109,15 @@ export class ProjectMutator {
1118
1109
  // Model API v2+: get initial storage and derive args from it
1119
1110
  storageToWrite = this.projectHelper.getInitialStorageInVM(blockConfig);
1120
1111
 
1121
- // Derive args directly from storage (VM extracts data internally)
1112
+ // Derive prerunArgs first always derived independently of args validation
1113
+ prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(blockConfig, storageToWrite);
1114
+
1115
+ // Derive args from storage (VM extracts data internally)
1122
1116
  const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(
1123
1117
  blockConfig,
1124
1118
  storageToWrite,
1125
1119
  );
1126
- if (deriveArgsResult.error) {
1127
- args = undefined;
1128
- prerunArgs = undefined;
1129
- } else {
1130
- args = deriveArgsResult.value;
1131
- prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(blockConfig, storageToWrite);
1132
- }
1120
+ args = deriveArgsResult.error ? undefined : deriveArgsResult.value;
1133
1121
  } else if (spec.storageMode === "legacy") {
1134
1122
  // Model API v1: use legacyState from spec
1135
1123
  const parsedState = JSON.parse(spec.legacyState);
@@ -1406,6 +1394,15 @@ export class ProjectMutator {
1406
1394
  const applyStorageAndDeriveArgs = (storageJson: string) => {
1407
1395
  persistBlockPack();
1408
1396
  this.setBlockStorageRaw(blockId, storageJson);
1397
+
1398
+ // Derive prerunArgs first — always derived independently of args validation
1399
+ const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(newConfig, storageJson);
1400
+ if (prerunArgs !== undefined) {
1401
+ this.setBlockFieldObj(blockId, "currentPrerunArgs", this.createJsonFieldValue(prerunArgs));
1402
+ } else {
1403
+ this.deleteBlockFields(blockId, "currentPrerunArgs");
1404
+ }
1405
+
1409
1406
  const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(newConfig, storageJson);
1410
1407
  if (!deriveArgsResult.error) {
1411
1408
  this.setBlockFieldObj(
@@ -1413,19 +1410,8 @@ export class ProjectMutator {
1413
1410
  "currentArgs",
1414
1411
  this.createJsonFieldValue(deriveArgsResult.value),
1415
1412
  );
1416
- const prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(newConfig, storageJson);
1417
- if (prerunArgs !== undefined) {
1418
- this.setBlockFieldObj(
1419
- blockId,
1420
- "currentPrerunArgs",
1421
- this.createJsonFieldValue(prerunArgs),
1422
- );
1423
- } else {
1424
- this.deleteBlockFields(blockId, "currentPrerunArgs");
1425
- }
1426
1413
  } else {
1427
1414
  this.deleteBlockFields(blockId, "currentArgs");
1428
- this.deleteBlockFields(blockId, "currentPrerunArgs");
1429
1415
  }
1430
1416
  };
1431
1417