@milaboratories/pl-middle-layer 1.66.1 → 1.66.3

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.
@@ -553,3 +553,70 @@ test("v3 blocks: migrateBlockPack assigns author marker", async () => {
553
553
  });
554
554
  });
555
555
  });
556
+
557
+ // Regression: MILAB-6621. Updating a block whose args() returns undefined (the
558
+ // standard "inputs incomplete/invalid" contract, e.g. `if (!Valid.safeParse(data).success)
559
+ // return undefined`) must not crash. Previously applyStorageAndDeriveArgs passed the
560
+ // undefined value straight to createJsonFieldValue -> Buffer.from(undefined) -> ERR_INVALID_ARG_TYPE,
561
+ // aborting the update-block-pack task (reported by AstraZeneca on mixcr-clonotyping 2.14 -> 2.20).
562
+ // The sentinel input [777] makes the enter-numbers-v3 model's args() return undefined without throwing.
563
+ test("v3 blocks: migrateBlockPack does not crash when args() returns undefined", async () => {
564
+ const quickJs = await getQuickJS();
565
+
566
+ await TestHelpers.withTempRoot(async (pl) => {
567
+ const prj = await pl.withWriteTx("CreatingProject", async (tx) => {
568
+ const prjRef = await createProject(tx);
569
+ tx.createField(field(tx.clientRoot, "prj"), "Dynamic", prjRef);
570
+ await tx.commit();
571
+ return await toGlobalResourceId(prjRef);
572
+ });
573
+
574
+ // Add block and set the sentinel state: args() returns undefined (no error).
575
+ await pl.withWriteTx("AddBlock", async (tx) => {
576
+ const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
577
+ mut.addBlock(
578
+ { id: "enter1", label: "Enter Numbers V3", renderingMode: "Heavy" },
579
+ {
580
+ storageMode: "fromModel",
581
+ blockPack: await TestBPPreparer.prepare(BPSpecEnterV3),
582
+ },
583
+ );
584
+ mut.setStates([
585
+ {
586
+ modelAPIVersion: 2,
587
+ blockId: "enter1",
588
+ payload: { operation: "update-block-data", value: { numbers: [777] } },
589
+ },
590
+ ]);
591
+ mut.save();
592
+ await tx.commit();
593
+ });
594
+
595
+ // Update the block pack — re-derives args from storage. Must NOT throw even though
596
+ // args() returns undefined (previously crashed inside createJsonFieldValue).
597
+ await pl.withWriteTx("MigrateBlockPack", async (tx) => {
598
+ const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
599
+ mut.migrateBlockPack("enter1", await TestBPPreparer.prepare(BPSpecEnterV3));
600
+ mut.save();
601
+ await tx.commit();
602
+ });
603
+
604
+ // Verify: currentArgs cleared (args() undefined), storage preserved, prerunArgs still derived.
605
+ await poll(pl, async (tx) => {
606
+ const prjR = await tx.get(prj);
607
+
608
+ const currentArgsField = prjR.data.fields.find(
609
+ (f) => f.name === projectFieldName("enter1", "currentArgs"),
610
+ );
611
+ expect(currentArgsField).toBeUndefined();
612
+
613
+ const blockStorage = await prjR.get(projectFieldName("enter1", "blockStorage"));
614
+ const storageData = JSON.parse(Buffer.from(blockStorage.data.data!).toString());
615
+ expect(storageData.__data).toStrictEqual({ numbers: [777] });
616
+
617
+ const currentPrerunArgs = await prjR.get(projectFieldName("enter1", "currentPrerunArgs"));
618
+ const prerunData = JSON.parse(Buffer.from(currentPrerunArgs.data.data!).toString());
619
+ expect(prerunData).toStrictEqual({ evenNumbers: [] });
620
+ });
621
+ });
622
+ });
@@ -769,7 +769,9 @@ export class ProjectMutator {
769
769
  blockConfig,
770
770
  initialStorageJson,
771
771
  );
772
- if (!deriveArgsResult.error) {
772
+ // args() legitimately returns undefined when inputs are incomplete/invalid;
773
+ // treat that like a derivation failure and clear the field (never JSON-encode undefined).
774
+ if (!deriveArgsResult.error && deriveArgsResult.value !== undefined) {
773
775
  this.setBlockFieldObj(
774
776
  blockId,
775
777
  "currentArgs",
@@ -1408,7 +1410,9 @@ export class ProjectMutator {
1408
1410
  }
1409
1411
 
1410
1412
  const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(newConfig, storageJson);
1411
- if (!deriveArgsResult.error) {
1413
+ // args() legitimately returns undefined when inputs are incomplete/invalid;
1414
+ // treat that like a derivation failure and clear the field (never JSON-encode undefined).
1415
+ if (!deriveArgsResult.error && deriveArgsResult.value !== undefined) {
1412
1416
  this.setBlockFieldObj(
1413
1417
  blockId,
1414
1418
  "currentArgs",